Skip to content

Keep one-time and cooldown data per character by default - #2

Merged
ryanbarlow97 merged 7 commits into
mainfrom
character-scope
Oct 14, 2026
Merged

ryanbarlow97 merged 7 commits into
mainfrom
character-scope

Conversation

@ryanbarlow97

@ryanbarlow97 ryanbarlow97 commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Once-only flags and cooldowns now belong to the roleplay character being played, unless an event opts back to the account. Stacked on #1.

act1_exp_catchup:
  type: repetitive
  one_time: true
  data_scope: account   # character is the default

Why. Codex discoveries become per character in Codex #2. On Main, 87 Codex unlock events in codex.yml and codex_research.yml are one_time, and today those flags are per account. Without this change, a second character could never earn codex_die, a duchy title or a research entry the account already has.

What the default affects. Only events that keep data, meaning those with one_time or a cooldown, and only when a player is involved. Such an event runs only while a character is in play, so not at join before a character is chosen. Events without that data, and player-less events such as server_start or player_pre_join, have nothing to keep per character and run exactly as upstream. On Main that leaves 90 events affected:

  • the 87 Codex unlocks, which should be per character, with no config change needed;
  • the 10-second meditation cooldown in r_lore.yml, where per character is fine;
  • act1_exp_catchup and act1_exp_catchup_retry, which should get data_scope: account at deployment so the one-off pre-split catch-up doesn't arm again for every character.

How it works.

  • Storage: per-character data lives in the same player file, under characters.<character>.events, and loads with the account at login as before. Upstream files load unchanged.
  • No inheritance: old account-level entries for events now per character stay in the file, unused, so every character starts these events fresh, because the old data doesn't record which character ran an event.
  • Character tracking: a tracker records the character in play from TLibs session events in a thread-safe map, for events that run off the main thread.
  • Delayed actions: a run is tied to the character it started for. If that character leaves before a delayed action (wait_ticks) or the hand-over from an async event, the rest is dropped and the run's one-time flag and cooldown are undone.
  • Thread safety: player data is locked for every access. Saves write a snapshot and never clear changes made during the write.
  • Resets: /ce reset <player> <event> and /ce reset * <event> clear the account and every character.
  • Unrecognised values: an unknown data_scope value logs a warning and counts as character.

Tests. JUnit and Mockito cover scope rules, the data model and snapshots, the file format, the tracker and run abandonment. JaCoCo requires 100% line coverage of net.tfminecraft.conditionalevents.character. mvn clean verify passes (19 tests).

🤖 Generated with Claude Code

@github-actions github-actions Bot added the size:XL 500-999 effective changed lines (tests excluded in mixed PRs). label Oct 14, 2026
@github-actions github-actions Bot added size:L 100-499 effective changed lines (tests excluded in mixed PRs). size:XL 500-999 effective changed lines (tests excluded in mixed PRs). and removed size:XL 500-999 effective changed lines (tests excluded in mixed PRs). size:L 100-499 effective changed lines (tests excluded in mixed PRs). labels Oct 14, 2026
@ryanbarlow97 ryanbarlow97 changed the title Add per-character event data with data_scope: character Keep one-time and cooldown data per character by default Oct 14, 2026
Base automatically changed from tfmc-build to main October 14, 2026 22:14
ryanbarlow97 and others added 7 commits October 14, 2026 22:14
An event can now set data_scope: character to keep its one_time flag and
cooldown per roleplay character instead of per account. Such events run
only while a character is in play, tracked from TLibs session events so
async events can read it. Each account's existing data for those events
moves to its RPCharacters heir when a session starts. Events without the
setting behave as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Match Codex: a reviewed heir missing from a known roster falls through
to the most-played rule. Drop RPCharacters from softdepend, since
RPCharacters loads after SimpleFactions, which loads after
ConditionalEvents.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Old per-account data doesn't say which character ran an event, so no
character inherits it. Per-character events read only character data;
account-level entries for them stay in the file, unused. This removes
the heir lookup and the move at session start.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A per-character event fixes its character when it starts and uses it
  for every check and write. If that character is no longer in play when
  an action is due (after wait_ticks, or after an async event hands over
  to the main thread), the rest is dropped and the one-time flag and
  cooldown it set are undone, so the character can earn it again.
- Events without a player never run per-character.
- PlayerData is locked for every access; saves write a snapshot, clear
  the dirty flag only if nothing changed meanwhile, and never write one
  account's file twice at once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Events with one_time or a cooldown now keep that data per character
unless they set data_scope: account. Events without such data, and
events without a player, have nothing to keep per character and run as
upstream, so join, server-start and plain command events are unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A per-character run's record (character, marks set, abandoned) is
  shared with every action group it calls through execute_action_group,
  so nested delayed actions are checked too and the undo happens once.
- An abandoned run undoes only what it still owns: the one-time flag
  unless a staff reset happened since, and the cooldown only if it is
  still the start it wrote.
- An undo touches only loaded data; it never creates an empty account
  that could overwrite the saved file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resetting one event no longer stops an abandoned run of another event
from undoing its marks. A reset of the same event, or of all events,
still does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ryanbarlow97

Copy link
Copy Markdown
Contributor Author

Reviewed by Astra (GPT-6-Astra) over four read-only rounds (reports kept locally); every finding fixed, the last with tests for its exact scenario. Merging with admin bypass. Not released or deployed.

@ryanbarlow97
ryanbarlow97 merged commit c6af3fd into main Oct 14, 2026
2 checks passed
@ryanbarlow97
ryanbarlow97 deleted the character-scope branch October 14, 2026 22:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL 500-999 effective changed lines (tests excluded in mixed PRs).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant