Skip to content

Turn timer macro functions + minor editor/permissions QoL#5963

Open
magnus-ISU wants to merge 1 commit into
RPTools:developfrom
magnus-ISU:feature/turn-timer-macros
Open

Turn timer macro functions + minor editor/permissions QoL#5963
magnus-ISU wants to merge 1 commit into
RPTools:developfrom
magnus-ISU:feature/turn-timer-macros

Conversation

@magnus-ISU
Copy link
Copy Markdown

@magnus-ISU magnus-ISU commented May 10, 2026

Summary

Three independent changes bundled into one PR for convenience. Happy to split into three PRs if reviewers prefer — particularly because feature 3 is a deliberate security-bypass and may be unwelcome upstream as-is.

1. Turn-timer macro functions (the main change)

Adds TurnTimerFunction, registered in MapToolExpressionParser, exposing:

Function Purpose
startTimer(name, seconds, [callback], [args]) Schedules a macro to run after seconds. Non-blocking — the calling macro returns immediately. Implements the long-missing "sleep" primitive without freezing the parser.
stopTimer(name) Cancel a pending timer.
getTimerRemaining(name) Seconds remaining (BigDecimal).
getTimerStatus(name) running or none.
getTimerNames() JSON array of active names.
setInitiativeTimer(seconds, [callback], [args]) Stores an auto-restart template; the timer re-arms on every initiative pass under reserved name __initiative__. Pass 0 to disable.
clearInitiativeTimer() Drops the template and stops any pending instance.

Backed by a single dedicated ScheduledExecutorService (named threads, daemon — same idiom as DebounceExecutor). Callbacks are bounced onto the EDT via EventQueue.invokeLater and dispatched through MapTool.getParser().runMacro(...) — same pattern CallFunction's deferred path uses. Write operations require trusted-macro context.

Hooked into InitiativeList.handleInitiativeChangeCommitMacroEvent for the auto-restart, and into MapTool.setCampaign to cancel pending timers on campaign change. Timers are non-persistent (deliberate; mirrors how HTMLFrame/HTMLOverlayPanel registries behave).

Covered by TurnTimerFunctionTest (lifecycle: register, cancel, replace, decay-to-zero, invalid-duration, cancelAll).

Example usage:

[h: setInitiativeTimer(60, "turnExpired@Lib:Combat")]

Or layered (warning + expiry) by starting two named timers from an onInitiativeChange lib-token macro:

[h: startTimer("turnWarning", 50, "turnWarning@Lib:Combat")]
[h: startTimer("turnExpired", 60, "timerExpired@Lib:Combat")]

2. # Name macro-editor directive (small QoL)

When saving in MacroEditorDialog, if the command's first line is # something, that line is consumed and something becomes the macro's label. Plain regex match in applyNameDirective(), called from the existing save() entry point. Useful when bulk-pasting macros from disk where the filename has been embedded as a hint at the top. Caveat: people who deliberately output a literal # Text line at the top of a macro will be surprised — in practice, almost all macros start with […].

3. Trust All Players (insecure) preference (likely contentious)

New AppPreferences.trustAllPlayers checkbox in Preferences → Application → Macro Permissions. When on, MapToolLineParser.isMacroTrusted() and LibraryToken macro-trust both short-circuit to true, making every macro trusted regardless of token ownership or per-button "Allow Player Edits". The label includes (insecure) and the tooltip warns it's intended for solo development/testing only.

Why include it: Because at 99% of tables, the DM can trust all the players to not destroy the game if given power to write their own complex macros. It's a massive annoyance for my group that we can't do this.

Test plan

  • ./gradlew testTurnTimerFunctionTest green; nothing else regresses.
  • ./gradlew spotlessCheck — clean.
  • Manual end-to-end: setInitiativeTimer(10, "ping@Lib:Combat") arms timer; Next/Previous re-arm; setting 0 disables; campaign load cancels pending timers; macro-editor directive lifts label and strips line; trust-all preference is opt-in and persists across restart.
  • Reviewer sanity-check on Windows/Linux (developed and tested on macOS).

This change is Reviewable

@Baaaaaz
Copy link
Copy Markdown
Contributor

Baaaaaz commented May 12, 2026

Disclaimer, I'm not a reviewer but... I suspect these should be split into separate PRs each using the PR template and with a reference to the open GitHub issue they are implementing/fixing/etc.

For no. 2, I think this would break existing macro editor command usage, for example URI accessed CSS which may have a # id selector as the first line in the macro command, to perhaps unconventional usage for storing raw markdown with # as headings. Maybe a new Preferences checkbox enabling this would be acceptable (with default being off to preserve existing behavior)?

@FullBleed
Copy link
Copy Markdown

FullBleed commented May 13, 2026

Here is an example of what Aliasmask's lib:rpEdit uses in the header of its macro export/import:

@@ @macroName
@PROPS@ fontColor=black ; autoExecute=true ; fontSize=1.00em ; sortBy= ; color=aqua ; playerEditable=false ; applyToSelected=false ; group= ; tooltip= ; minWidth=

Perhaps having the editor use that "standard" would work to serve the same purpose?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants