Powerup hit events on the game-state websocket #18
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Depends on
Reference
mintopia/blur#18
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What to build
A websocket message every time a racer is hit by a powerup, for every racer (local, AI, remote), in SP, split-screen and LAN races.
Blocked by the runtime spike #17. The field mapping below comes from static analysis and must be replaced by what the spike confirms.
Message shape
A new message type, not a key in the state feed:
attackeris absent when unknown (mines/world).powerupis lower-case:shunt,bolt,mine,shock,barge, plus whatever else the spike shows.race_ms(from #15) only if that ticket has landed; otherwise leave it out. If the spike finds damage or wreck events, adddamageand a separate{"event":"wreck",...}.Why a new type rather than
change: the state feed (src/host/game_state.cpp) is last-value-wins.state::setonly notifies when the value differs, so two identical hits in a row would be dropped, and the connectsnapshotwould replay a stale hit to new clients. Hits are one-shot events, so they need their own path that never goes into the snapshot. Unknowntypevalues are ignorable by existing clients.Where the data comes from
Confirmed by spike #17 (see its results comment and ADR 0016):
0x535770, the game event send (thiscall,this= hub0x111d1e8; stack argschannel, event_id, a2, payload). Handle onlychannel == 9 && event_id == 4; for everything else call the original straight away, since it runs about 250 times a second. Add it toHOOKSintools/recomp/emit.py. This replaces the audio listener0xc8ee40: the send null-checks that sink, so the hook doesn't depend on audio.+0x8u8 attacker (firer),+0x9u8 victim,+0xa= victim for self-applied pickups, else0xff.+0xci32 powerup: 0nitro, 1shock, 2shunt, 3bolt, 4barge, 5mine, 6repair.+0x14u32 hit serial (the same on every LAN instance). Indices are racer+0x314, the same numbering asstandings.levelin the payload:+0x10was always 0. Droplevelfrom the message.+0x3dc+0xbc > 0(damage 0)damage= previous health (from the scan) minus current health. Or use the fixed values: Shock/Bolt 50 (one event per bolt), Shunt/Mine/Barge 100.wreckevent needs the 5 Hzrace_scan.cpppoll: health+0x3dcreaches 0 (+0x38c= 1) for 2.6-4 s.Implementation notes
state::event(const std::string& json)ingame_state.{h,cpp}with its own subscribers.src/host/state_ws.mmsends it to connected clients and nothing else (no snapshot, noBLUR_STATE_LOGkey churn).BLUR_STATE_WSis unset (the hook returns straight to the original).standings(racer), so clients can join with names.Acceptance criteria
tests/state_ws_test.mm: an emitted event reaches every connected client as{"type":"event",...}, is not in the snapshot sent to a client connecting later, and two identical events both arrivedocs/state-websocket.mddocuments theeventmessage type and thehitfieldsBLUR_ENGINE=recompBlocked by
Implemented
{"type":"event","event":"hit",...}per ADR 0016: hook 0x535770 (ch 9/ev 4, added to emit.py HOOKS) -> queue -> worker thread ->state::event(new, never stored, no snapshot, identical events all delivered) -> state_ws broadcast. Fields: victim, attacker (absent if 0xff), powerup, damage (fixed; 0 if shielded), shielded, wrecked (victim health/shield via 5 Hz scan racer registry), race_ms (staterace_ms, #15 landed). Nitro/Repair skipped;leveldropped. Hook only installed with BLUR_STATE_WS and bails to orig when no client connected. Tests: hit_event_test (mapping), state_ws_test (event delivery, not in later snapshot, duplicates). docs/state-websocket.md updated.NOT done: runtime verification on BLUR_ENGINE=recomp (SP/split-screen/LAN) and frame-time check — no game install/inputs in this unattended worktree; needs emit.py regen + manual race. Also no separate
wreckevent (collision wrecks need the 5 Hz poll; follow-up).Runtime verification on BLUR_ENGINE=recomp (build from this branch, emit.py regenerated: only the shard holding 0x535770 changed):
Not done: separate
wreckevent (collision wrecks have no game event; optional per ticket).wrecked/shieldedtrue was not observed in the runs (shield read non-zero once in a debug log).Completed and merged by Harmonic (task 17).