KB: task #1 landings -- update-record channel DONE, movementMode=simulationState identification swept

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-11 11:40:44 -05:00
co-authored by Claude Fable 5
parent c9f0c2a7f1
commit e8b9f71025
4 changed files with 69 additions and 14 deletions
+7
View File
@@ -284,6 +284,13 @@ Full decomp map of the death path. Two tiers:
kill → full chain (blhdead → wreck swap → smoke → sink → burial → INERT). [T2]
- `Mech::IsDestroyed()` (`FUN_0049fb54`) = `movementMode==2||9` (reconstructed as `IsDisabled`, btstubs.cpp);
`FUN_004ab1c8` freezes locomotion (zeros mech+0x298) when destroyed.
**IDENTIFICATION (task #1, 2026-07-11): `movementMode` (mech+0x40) IS
`Simulation::simulationState.currentState`** -- the StateIndicator @0x2c (+0x10 oldState,
+0x14 currentState); every binary "movementMode write" is a `SetLevel(this+0x2c, n)` call.
The port's parallel `int movementMode` member is gone: `MovementMode()/SetMovementMode()`
accessors ride the engine StateIndicator, which is why the death state (9) now REPLICATES --
it travels in every update record's header (+0xC) and the observer's replicant runs its own
wreck sink off it (see [[multiplayer]] "Mech-level update records"). [T1]
- **`MechDeathHandler`** (ctor `FUN_0042a984` + Performance `FUN_0042aa2c`, cached mech+0x850 / `mech[0x214]`) —
the **per-subsystem destroyed-skin + explosion engine**. Each tick walks the damage subsystems
(mech+0x120[mech+0x11c]); as a subsystem's damage crosses a descriptor-table threshold (`FUN_0042a5f4`
+52 -6
View File
@@ -6,7 +6,7 @@ source_sections: "PROGRESS_LOG.md §7 (Phase 7), §8 (P6); docs/HARD_PROBLEMS.md
related_topics: [wintesla-port, combat-damage, locomotion, decomp-reference]
key_terms: [master, replicant, EGG, dead-reckon]
open_questions:
- "pod-LAN config (real IPs); update-record velocity from the body-channel projection; peer SimulationState/DropZoneLocation replication (authentic peer warp spheres); Mech-level update records @0x4a0c2c (in progress)"
- "pod-LAN config (real IPs); update-record velocity from the body-channel projection; peer SimulationState/DropZoneLocation replication (authentic peer warp spheres); stream the model deadband constants (UpdatePositionDiffrence et al) to the update-record senders"
---
# Multiplayer
@@ -188,6 +188,55 @@ from the leg channel); the binary publishes the BODY channel's smoothed projecti
disasm +0x2a0). Swapping the source is a fidelity refinement DEFERRED — it risks a verified working feed
for a smoothing nuance. [T3 on that nuance]
## Mech-level update records (task #1, 2026-07-11) — DONE [T1 transcription / T2 live]
The 9-type Mech record channel is LIVE end-to-end: **writer @004a0c2c** transcribed
instruction-for-instruction from `reference/decomp/mech_writeupdate_004a0c2c.disasm.txt`, **reader
@004a1232** rewritten from part_012.c:9576-9692 with every Wword absorber promoted to the NAMED
engine/port member. Wire sizes verified live (0x14/0x20/0x2c/0x78). Key mechanics:
- **Record type == the `updateModel` BIT INDEX** (`Simulation::WriteSimulationUpdate` walks the
mask, SIMULATE.cpp:302-328; binary twin FUN_0041bd98). Request = `Mech::ForceUpdate(1<<N)`
(@0x4a4c54: `updateModel |= mask`, `&= 0xfe03` filter when disabled). THREE prior mislabels of
the same binary fns are retired: `SetInstanceFlags` and `RequestActionFlags` (both @0x4a4c54
+0x18 is `Simulation::updateModel`, NOT entity flags) and `IsNetworkCopy` (@0x49fb54 =
`IsDisabled`, simulationState 2||9).
- **`movementMode` == `Simulation::simulationState.currentState`** (binary mech+0x40 =
StateIndicator@0x2c +0x14; oldState @+0x3c). The port's parallel `int movementMode` member is
GONE — `MovementMode()/SetMovementMode()` accessors ride the engine StateIndicator, so the state
(death 9 / limbo 2 / airborne 3,4) replicates in EVERY record header (+0xC) and the replicant
tracks the master automatically.
- **Every record tail = the mapper's `speedDemand`** (subsystemArray[0]+0x128) mirrored into
`bodyTargetSpeed@0x6b4` on both sides — the authentic replicant-gait feed (it was misread as a
"sequence number"; the type-2 record exists solely to ship it on change). Task #50's
velocity-derived gait stays as the leg-channel driver meanwhile.
- **Types**: 0 pose (Mover record + tail; orientation SAVED/RESTORED around the base call — it
rides ONLY type 4), 1 damage zones (engine), 2 speedDemand, 3 leg-state+stability (write-through
body dispatch both sides), 4 orientation+angular-velocity resync (euler-encoded, quat
normalized), 5 knockdown (SetBodyAnimation 0x20 both sides), 6 death, 7 impact (fields only),
8 airborne/reverse selector. 5/6/7 share the 0x2c heat/throttle/fall-basis payload.
- **Senders wired byte-exact** (the perf-loop + event map recovered from 27 binary call sites):
gait transitions `Force(8)` (mech2 — already transcribed under the RequestActionFlags mislabel,
masks matched the binary exactly), knockdown `Force(1|0x20)` (mech4), death `Force(1|0x40)`
BEFORE `SetMovementMode(9)` (the disabled filter would mask it after), `Reset` `Force(0x1f)` +
the binary zero-set (state 0, cycle/target speeds, poseSyncLatch), perf-loop deadbands: position
→ type 0, orientation deviation → type 4, speedDemand change → type 2, heat-level change → type
7 (snapshot @0x780). [T3 deadband thresholds — the model record's `UpdatePositionDiffrence` /
`UpdateTurnVelocityDiffrence` / `UpdateTurnDegreeDiffrence` fields (+0x26..0x28) are the
authentic constants, not yet streamed to the runtime members +0x768/+0x76c/+0x770.]
- **VERIFIED 2-node**: types 2/3/4 flow while driving; kill → B emits ONE type-6 (simState=9 in
the header) → A's replicant flips to MovementMode 9 and its own `UpdateDeathState` runs the
wreck-sink loop on the OBSERVER's screen (smoke re-arm logged on A; NO double death transition,
NO replicant VehicleDead) — the death SINK/burial gap (old item 3) is CLOSED. Respawn:
`Force(0x1f)` burst snaps the replicant to the drop zone + un-wrecks it. Walking replicant
un-regressed (run states 10→12, legCycle tracking, with type-3 records flowing). Solo clean.
- Telemetry: `[mrec-tx]/[mrec-rx]` per non-pose record (BT_REPL_LOG). Note the type-1 rx line
prints a bogus simState (the damage record's own layout at +0xC) — cosmetic.
- New by-name members: `poseSyncLatch@0x77c` (dead-reckon re-base latch, consumed by
FUN_004ab1c8), `fallDirection@0x4a8` + `fallScalar@0x4b4` [T4 names], `heatLevelSnapshot@0x780`.
A TU-local stub type forced the `BTMapperSpeedDemandRaw(void*)` bridge (mech.cpp carries a local
`struct MechControlsMapper` recon stub — typed cross-TU signatures mangle differently).
## Remaining (P6 phase 4 / Phase 7)
The pod-LAN config (real IPs, bare-IP pilot entries); update-record velocity sourced from the body-channel
projection (above). See [[open-questions]]. [T3]
@@ -310,11 +359,8 @@ transition, HUD all landed since P6): console egg → mesh → RunningMission on
(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.
**The MECH-level records LANDED (task #1, 2026-07-11)** — see the section below; this
REMAINING note is closed.
5.**2-window driving — RESOLVED (tasks #48/#51).** Keyboard MP turning was restored at the root
(the `pilotArray[1]` shrunk-span overrun fix, task #51); `BT_KEY_NOFOCUS` exists for automated
harnesses. DEATHS scoring lights via the existing BTPostKillScore MP branch. Respawn is
+6 -5
View File
@@ -195,11 +195,12 @@ authentic path scoped.
master, full cross-pod kill verified). ✅ Replicant GAIT animation — DONE (task #50: replicant
derives speedDemand from the replicated velocity, full stand→walk→run lifecycle verified).
See [[multiplayer]] — the entries here were stale (swept 2026-07-11).
- **Mech-level update records (@0x4a0c2c) untranscribed — the biggest remaining MP fidelity gap:**
only the base Mover record replicates, so a peer's death SINK/burial, knockdown, heat, alarm and
movementMode states don't reach the other pod (the wreck SWAP does appear). The writer's disasm is
recovered + 9 record types decoded (`reference/decomp/mech_writeupdate_004a0c2c.disasm.txt`);
the read-side leg-state branches at mech.cpp:1511/:1613 are the dead Wword comparisons.
- **Mech-level update records (@0x4a0c2c) — DONE (task #1, 2026-07-11):** all 9 types
transcribed + 2-node verified; the peer's death SINK/burial, knockdown, heat and state now
replicate (the observer's replicant runs its own wreck loop off the replicated
simulationState). Full mechanics in [[multiplayer]] "Mech-level update records". Residue
[T3]: stream the model's UpdatePositionDiffrence/TurnVelocity/TurnDegree deadband constants
to the senders (stand-in thresholds meanwhile).
- The WinSock2 stack (L4NET.CPP) is 2-node verified end-to-end (cross-pod kills, beams,
replicant gait — tasks #46-#51); remaining = the real pod-LAN config (real IPs, bare-IP
pilot entries).
+4 -3
View File
@@ -53,9 +53,10 @@ and `BTVal` is the recon **absorber** type: `operator=` stores NOTHING, every re
check `mech.hpp`'s offset map first (the slot may already exist under a best-effort
mislabel; 0x111 was mislabeled `ammoExpended`).
- `if (Wword(a) != Wword(b))` and `if (Wword(a) == 2)` are BOTH always-false → the guarded
branch is dead code. Known dead sites: `mech.cpp:1673` + `mech.cpp:1775` (replicant
state sync in ReadUpdateRecord — MP is live; these are being revived by the in-progress
Mech Write/ReadUpdateRecord reconstruction, task #1).
branch is dead code. (The archetype sites — the replicant state sync in Mech::
ReadUpdateRecord — were REVIVED by the task #1 update-record reconstruction, 2026-07-11:
every Wword in that path is now a named engine/port member; the `Wword(0xf)/(0x10)`
comparisons were `simulationState.oldState/currentState`.)
- Any `Wword(N)` used for OBJECT access reads a shared global, not `this+i*4`; e.g.
`(Mech*)Wword(3)` for a zone's owner → garbage; use `GetOwningSimulation()`.