Cross-pod beams: replicate emitter discharge via subsystem update records
User report: lasers only visible on the window firing them. The peer's replicant emitters never learned the master fired. THE AUTHENTIC PIPE (decomp-verified): - FUN_0041c350 (the "beam keepalive" ServiceDischarge/ContinueDischarge call) does TWO things: queue the LOCAL deferred beam-effect callback (@0x4bac0c) on the app+0x34 manager -- our per-weapon render walk already plays that role -- and set the subsystem DIRTY bit, which maps to the 2007 engine's updateModel / ForceUpdate(). - Replication rides SUBSYSTEM UPDATE RECORDS inside the mech's update message: the roster walk already hands the entity's stream to every subsystem's PerformAndWatch; Simulation::WriteSimulationUpdate serializes each requested updateModel bit; Entity::UpdateMessageHandler routes received records by subsystemID to the subsystem's ReadUpdateRecord. All engine machinery -- the missing pieces were the Emitter's serialize/apply pair + the triggers. CORRECTIONS to the dormant task-33-era transcriptions (never exercised -- nothing ever set updateModel -- so the latent misreads never surfaced): - The weapon-family VTABLE SLOT MAP was swapped: slot 6 = ReadUpdateRecord, 7 = WriteUpdateRecord, 9 = TakeDamage (evidence: Mech hierarchy symmetry + body semantics; @004ba568 resolves an EntityID at rec+0x30 through the entity index -- record semantics, not Damage). Renamed across MechWeapon / Emitter / ProjectileWeapon; the real Emitter::TakeDamage @004bafc8 is undecoded (inherits MechWeapon for now). - Emitter/MechWeapon Write: `*record = 0x38/0x18` is the record LENGTH, not recordID; rec+0x30 is the TARGET's EntityID (GetEntityID()), not a colour -- the old `CopyColor(targetEntity+0x184)` was also a databinding trap. - OVERRIDE-SIGNATURE TRAP: the decls used each class's own shadowing UpdateRecord typedef as the param type, silently NOT overriding the engine virtual (the base ran instead; nothing would ever have serialized). Base-typed params (Simulation__UpdateRecord*), casts inside. - Emitter::ReadUpdateRecord reconstructed (@004ba568): target EntityID resolve (drop unknown non-null targets), MechWeapon alarm apply chain, beam fields. - ServiceDischarge/ContinueDischarge: ForceUpdate() per keepalive tick + one final record at beam end (turns the peer's beam off). - Mech::DrawWeaponBeams extracted from the player-only drive block so the walk runs for REPLICANTS (+ per-mech gun-port cache -- the process-wide statics would have served the player's segment pointers as the replicant's muzzles). VERIFIED 2-node: A fires 57 volleys -> 225 emitter records -> B applies all 225 -> B draws 414 beams (PPC blue / laser red, from A's replicant's own gun ports). Solo un-regressed (150 beams, kill chain, no crash). Also preserved: the full Mech::WriteUpdateRecord @0x4a0c2c recovery (reference/decomp/mech_writeupdate_004a0c2c.disasm.txt) with all 9 record types decoded (pose/alarm/leg-state+heat with the body-channel write-through re-sync, knockdown, death, impact, movementMode) -- transcription deferred; it replicates remote knockdown/death/heat and was not needed for beams. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
a110041a70
commit
29035028fd
+22
-4
@@ -181,10 +181,28 @@ transition, HUD all landed since P6): console egg → mesh → RunningMission on
|
||||
3. **Victim visuals on the shooter's screen**: the wreck swap gates on zone/graphic state the
|
||||
REPLICANT copy may never see (zone damage may not replicate — only pose updates confirmed).
|
||||
Check DamageZone/subsystem state in the update stream; else replicate the death as an event.
|
||||
4. **Cross-pod beam visuals**: replicant emitters don't run the local trigger sim, so the peer's
|
||||
beams never draw today. ⭐ The authentic path exists: ServiceDischarge/ContinueDischarge SEND
|
||||
**beam-keepalive messages** (`FUN_0041c350`, templates @0x511e6c/@0x511e78, emitter.cpp already
|
||||
stubs them) — decode + route those to drive replicant beam draws.
|
||||
4. ✅ **Cross-pod beam visuals — DONE (task #51, 2026-07-09).** The keepalive theory needed one
|
||||
correction: `FUN_0041c350` = (a) queue the LOCAL deferred beam-effect callback (@0x4bac0c) on the
|
||||
app+0x34 manager (our per-weapon render walk plays that role) AND (b) set the subsystem DIRTY bit —
|
||||
which maps to the 2007 engine's `updateModel`/`ForceUpdate()`. The REPLICATION rides **subsystem
|
||||
update records inside the mech's update message**: the roster walk passes the entity's stream to
|
||||
every subsystem's `PerformAndWatch`; `Emitter::WriteUpdateRecord` (@004ba65c, corrected: record
|
||||
LENGTH not recordID; rec+0x30 = the TARGET's **EntityID** not a colour) serializes
|
||||
firingActive/beamEndpoint/beamFlag/targetID; the engine's `Entity::UpdateMessageHandler` routes
|
||||
records by `subsystemID` to `Emitter::ReadUpdateRecord` (@004ba568 — was MIStranscribed as
|
||||
TakeDamage; the whole weapon-family slot map was swapped: slot 6=Read, 7=Write, 9=TakeDamage — see
|
||||
mechweap.hpp). `ServiceDischarge`/`ContinueDischarge` call `ForceUpdate()` per keepalive tick +
|
||||
once at beam end. The render walk was extracted to `Mech::DrawWeaponBeams` (per-mech port cache)
|
||||
and runs for REPLICANTS. ⚠ two silent-failure traps fixed: the overrides originally used the
|
||||
class's own shadowing `UpdateRecord` typedef as the param type → never overrode the engine virtual
|
||||
(base ran, nothing serialized); and `recordLength` must be each level's sizeof or the stream
|
||||
framing misparses. VERIFIED 2-node: A fires 57 volleys → 225 records → B applies 225 → draws 414
|
||||
replicant beams (PPC blue / laser red from A's replicant's gun ports). Solo un-regressed.
|
||||
REMAINING (deferred): the MECH-level writer @0x4a0c2c (recovered + preserved:
|
||||
`reference/decomp/mech_writeupdate_004a0c2c.disasm.txt`; 9 record types decoded — 0/1/4 pose
|
||||
variants, 2 alarm, 3 leg-state+heat with body-channel write-through re-sync, 5 knockdown
|
||||
(SetBodyAnimation(0x20) write-through), 6 death, 7 impact, 8 movementMode) — transcribing it
|
||||
would replicate remote KNOCKDOWN/death/heat states; beams did not need it.
|
||||
5. **2-window driving**: input gates on window focus (alternate windows; `BT_KEY_NOFOCUS` for
|
||||
harness). DEATHS scoring should light via the existing BTPostKillScore MP branch. Respawn =
|
||||
the P5 teardown debt (deferred until needed).
|
||||
|
||||
Reference in New Issue
Block a user