Repository navigation
feat(sim): trigger player events from scenario and Damage/Heal - #130
Conversation
Injected events and the events modeled state changes raise share one dispatch. `Damage` raises `Player Took Damage` on the target and `Player Dealt Damage` on a player dealer, a hit that empties the health pool adds `Player Died` then `Player Dealt Final Blow`, and `Heal` raises `Player Received Healing`/`Player Dealt Healing` — in occurrence order, binding `Event Player`, `Attacker`, `Victim` and `Event Damage`/`Event Healing`. Team/player filters apply to a raised event the same as an injected one, a busy instance queues it until idle, and one tick's raised events are bounded at 1024 against a triggering rule's own fan-out. A raised event binds only what its cause knows — an unbound event value is `not_modeled`, never a `missing_input`. One liveness predicate — the `isDead` read `Is Alive`/`Is Dead`/ `Set Health` share — gates `Damage`, `Heal` and `Resurrect`: a dead target's pools do not move and no events raise. A killing `Damage` writes the cell, and a revive refills the pool so the player can die again — `Respawn` unconditionally, `Resurrect` only on an actual revive. Scenario event values `attacker`/`victim` validate as declared player ids or `null`. Bastion (scen3 + declared gameMode/local/isDuplicatingAHero) moves past the player-left rule to tick 91, stopping `not_modeled` on "wait under a 90% simulation rate" — the same next stop as main. With injected `playerLeft`/`playerJoined` the Team 2 rules dispatch to p2; the join rule's condition stops on "player inside a custom string". Fixes #47
The 0.11.1 release bumped crates/athena-sim/Cargo.toml without regenerating the lockfile, so every --locked CI step failed.
|
Fixed the reported finding:
Verified locally on Rust 1.88.0: The PR is ready for follow-up review. |
The EVENT_VALUES doc comment listed hostPlayer among unmodeled event values, but hostPlayer is modeled: player.rs resolves the scenario `host` marker, a rule architecture.md records as decided. The same sentence mislabeled the remaining entries as player- and vector-typed; the catalog gives eventAbility returnType Button. architecture.md still said a non-Ignore wait behavior stops only in a rule triggered by an injected event, but the gate is any bound event — a raised event also sets inst.event. Updated both sites to the file's "injected or raised" terminology.
|
Review findings addressed in 7e20007 (
No findings were contested or deferred. Verified with |
The player-scoped rules paragraph still said a `Slot` filter never fires only when "no matching event is injected". Since this PR, modeled `Damage`/`Heal` also raise player events that can reach the filter, so a raised match fires the rule too. Audit of every "injected" phrase in docs and crate comments found this the last site that named injected events where raised events also apply; the rest already say "injected or raised" or correctly scope "injected" to scenario-declared events.
|
Round-2 review findings addressed. Fixed
A full-repo audit of every "injected" phrase in Verified with |
Summary
Trigger the player events issue #47 scopes, from both sources the issue names:
eventskeep dispatching toEvent::Playerrules as before (injected), now sharing one path with raised events: newevents.rsholds eligibility (kind +team/targetfilters on the event player), queueing, and firing.Damage→Player Took Damage(target) andPlayer Dealt Damage(player dealer;Nulldealer raises none and bindsAttackertonull); a hit that emptieshealthaddsPlayer DiedthenPlayer Dealt Final Blow;Heal→Player Received Healing/Player Dealt Healing.Event Player= the player the event happened to;Attacker/Victim/Event Damage/Event Healingbound to the cause. A raised event carries no scenariovalues, so an event value its cause does not bind (Event Was Critical Hit,Event Was Environment,Event Was Health Pack,Event Ability,Event Direction) isnot_modeled— nevermissing_input.values.attacker/values.victimvalidate as a declared playeridornull; anything else is a load error.dead(), theisDeadreadIs Alive/Is Dead/Set Healthshare, falling back to the health pool only whereisDeadcannot answer — gatesDamage,HealandResurrect. A killingDamagewritesisDeadthroughwrite_cell; a revive refills theworldpool so the player can die again:Respawnunconditionally ("with full health, even if they were already alive"),Resurrectonly on an actual revive.TICK_EVENT_LIMIT(1024) against self-triggering fan-out.Prior-attempt lessons addressed (reviews 47-r1/r2/r3): single authoritative alive/dead read shared by
Damage/Heal/Kill/Respawn/Resurrect;Resurrectdoes not heal living targets whileRespawnmay; raised vs injected event-value miss semantics split correctly; event filter evaluation reads the event player; occurrence-order dispatch between instance visits.Verification
cargo fmt --check— cleancargo clippy --all-targets— cleancargo test— 44 suites, 362 tests, 0 failures (incl. newtests/player_events.rs: 15 tests covering injected dispatch +Event Playerbinding, occurrence order took→dealt→died→finalBlow,Nulldealer, healing events, team/player filters on raised events, raised-unboundnot_modeledvs injectedmissing_input, dead-target suppression, respawn/resurrect split + re-death, busy-instance queueing, the 1024 bound,attacker/victimvalidation)cargo check --locked --target wasm32-unknown-unknown -p athena-sim— cleangit diff --check— clean (docs/simulator-interface.mdtrimmed back under the 10k-token limit)bastion.ow, scen3 + declaredgameMode/local/state.isDuplicatingAHero): moves past the player-left rule to tick 91, stoppingnot_modeledon "action wait under a 90% simulation rate" — the documented athena#120 boundary, identical to main. With injectedplayerLeft/playerJoined/playerTookDamage/playerDiedfor the Team 2 player, the filtered rules dispatch at the due tick witheventPlayerbound (Update targetPlayer when player left,Remove Player from Blacklist,[System] Arm passive regen, …); the next stops are pre-existingnot_modeledconstructs inside rule bodies (player inside a custom string,wait behavior in an event-triggered rule) — precise constructs, as required.Not verified
Damage/Healbefore the tick-91 wall, so the raised-event path is exercised only by the unit tests, not the workload.docs/findings.md).Fixes #47