The retail movement-manager family the R4 MoveToManager port left as do-not-invent seams (decomp §9f/§9g). Faithful C# ports of retail's PositionManager facade + StickyManager + ConstraintManager + the TargetManager voyeur system, with full conformance tests. NO wiring yet — purely additive, no behavior change. Wiring (retiring TS-39 sticky + AP-79 target adapter) is R5-V2/V3. New Core classes (src/AcDream.Core/Physics/Motion/): - StickyManager (0x00555400): follow-a-target steering. adjust_offset's dense x87 mush decoded via ACE (StickyRadius 0.3, StickyTime 1.0, follow speed ×5 / fallback 15) — speed-clamped signed-distance steer + bounded turn-to-face; 1 s watchdog; Ok→initialized / non-Ok→teardown. - ConstraintManager (0x00556090): the server-position rubber-band leash. 90% IsFullyConstrained jump gate + grounded linear brake taper. Structural only — acdream never ARMS it (retail arms from SmartBox::HandleReceivedPosition, which acdream lacks, with two x87 constants BN elided). IsFullyConstrained stays false = TS-35 behavior; leash-arming + the unknown constants are a deferred issue. - PositionManager facade (0x00555160): lazy Sticky/Constraint + fan-out. - TargetManager (0x0051a370) + TargettedVoyeurInfo: the peer-to-peer voyeur subscription system (0.5 s throttle, 10 s staleness, send-on-drift-past-radius, dead-reckon GetInterpolatedPosition). A faithful superset of the AP-79 adapter — SetTarget subscribes ON the target; the target's HandleTargetting pushes updates back. - IPhysicsObjHost: the CPhysicsObj back-pointer seam (position/velocity/ radius/contact/GetObjectA + target-tracking fan-out) the App wires per entity in V2/V3. MotionDeltaFrame: mutable retail-Frame delta accumulator. Supporting: - TargetInfo extended to the full retail 10-field struct (additive defaults keep the R4 4-arg call sites compiling). - MoveToMath: signed CylinderDistanceNoZ, NormalizeCheckSmall, GlobalToLocalVec. - Rename: the misnamed AcDream.Core.Physics.PositionManager (a remote anim+interp per-frame combiner, NOT the retail facade) → RemoteMotion Combiner, freeing the name and removing the ambiguity that breaks every file importing both Physics + Physics.Motion (GameWindow will in V2/V3). Tests: 42 new conformance cases (Sticky/Constraint/Position facade + TargetManager incl. the full cross-entity voyeur round-trip). Full suite 4006 green (+2 skipped), no regressions. Decomp + ACE cross-ref + port plan: docs/research/2026-07-03-r5-managers/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
27 KiB
R5 seam recon — current acdream integration points MovementManager/PositionManager/TargetManager will replace
Repo root: C:/Users/erikn/source/repos/acdream/.claude/worktrees/vigorous-joliot-f0c3ad
Read-only recon. All line numbers verified against files as of this session (2026-07-03).
1. MoveToManager seams — src/AcDream.Core/Physics/Motion/MoveToManager.cs (1605 lines)
Ctor-required seam delegates (lines 78-138, ctor 184-222)
All Action/Func fields, all REQUIRED (non-nullable, thrown in ctor if null):
_stopCompletely(L78) — retailCPhysicsObj::StopCompletely_getPosition(L83) — retailphysics_obj->m_position_getHeading(L87) — retailCPhysicsObj::get_heading_setHeading(L94) — retailCPhysicsObj::set_heading(heading, send)— the ONE heading snap, fired only fromHandleTurnToHeading(L1168)_getOwnRadius(L99),_getOwnHeight(L103) — mover's own cylinder dims_contact(L107) — retailtransient_state & 1(CONTACT bit) —UseTime's tick gate_isInterpolating(L112) — retailCPhysicsObj::IsInterpolating→ PositionManager (R5 body doesn't exist yet)_getVelocity(L116) — feeds Phase-3 TargetManager quantum retune_getSelfId(L120) — self-target detection_setTarget(L126) — retailCPhysicsObj::set_target→ TargetManager::SetTarget (R5, call-shape only)_clearTarget(L130) — retailCPhysicsObj::clear_target→ TargetManager::ClearTarget (R5, call-shape only)_getTargetQuantum(L134) /_setTargetQuantum(L138) — TargetManager quantum (R5, call-shape only)_curTime(L175, optional param, defaults to a per-call 1/30s stub)
AP-79 target seams (setTarget/clearTarget)
_setTarget(0, TopLevelObjectId, 0.5f, 0.0)called fromMoveToObject(L471) andTurnToObject(L592) — always context 0, radius 0.5, quantum 0.0._clearTarget()called fromCleanUp(L1499) whenTopLevelObjectId != 0 && MovementTypeState != Invalid.- Consumer of the callback:
HandleUpdateTarget(TargetInfo info)(L1229-1283) — the P4 TargetTracker feed point. Ignores updates for anyinfo.ObjectId != TopLevelObjectId(L1237). Two paths: deferred-start (Initialized==false, first callback) vs retarget-while-running. - Today's producer of
TargetInfois NOT a real TargetManager — it'sGameWindow.TickRemoteMoveTo(App-layer, §2c below) polling_entitiesByServerGuideach tick and manually diffing againstTrackedTargetRadius.
TS-39 sticky seams (StickTo/Unstick)
public Action? Unstick { get; set; }(L145) — retailCPhysicsObj::unstick_from_object→ PositionManager::UnStick (R5 body). Called unconditionally at the head ofPerformMovement(L414). Optional — null = silent no-op.public Action<uint,float,float>? StickTo { get; set; }(L151) — retailPositionManager::StickTo(object_id, radius, height)(R5 StickyManager body). Called fromBeginNextNode's sticky arrival handoff (L708) with pre-CleanUp values(tlid, radius, height). Optional — null = silent no-op.- Today, NEITHER
UnsticknorStickTois bound to anything in GameWindow.cs — grep ofEnsureRemoteMotionBindingsandEnterPlayerModeNowshows norm.MoveTo.StickTo = ...or.Unstick = ...assignment anywhere. Both are permanently null (silent no-op) in production today. (Separately,MotionInterpreter.UnstickFromObject— a DIFFERENT, unrelated seam on the interp, not the MoveToManager — IS bound in some contexts; see §3.)
setHeading seam
_setHeading: Action<float,bool>— bound per-manager at construction (remote:mtBody.Orientation = MoveToMath.SetHeading(...), GameWindow.cs:4277-4278; player:pcMoveTo.Yaw = MoveToMath.YawFromHeading(h), GameWindow.cs:12996-12997). Thebool send(network-echo flag) param is UNCONSUMED on both sides — register row TS-33 (GameWindow.cs:12983-12988 comment): a stationary heading snap never reaches the wire.
Public surface (entry points + drivers)
PerformMovement(MovementStruct mvs)(L411) — the type-6..9 dispatch entry, called fromRouteServerMoveTo(GameWindow.cs, §5below)MoveToObject/MoveToPosition/TurnToObject/TurnToHeading(L450, 501, 560, 613)UseTime()(L953) — THE TICK GATE:!_contact()/ no pending node / (object-moveInitializedgate) → dispatchesHandleMoveToPosition/HandleTurnToHeading. Called fromGameWindow.TickRemoteMoveTo(L4504) for remotes; not shown called for the player in the same function (player pump is via_playerController/OnUpdateFrame— see §2c).HandleUpdateTarget(TargetInfo info)(L1229) — AP-79 feed point (see above)HitGround()(L1292) — no-op ifMovementTypeState==Invalid, elseBeginNextNode(). Called from GameWindow at L5426 (remote landing) and L10070 (another remote landing site — see §2d).CancelMoveTo(WeenieError error)(L1461) — reentrancy-guarded drain+cleanup+stop; theerrorparam is NEVER read in retail's body (kept for parity/logging only)IsMovingTo()(L397),Destroy()(L387),CleanUp()(L1485),CleanUpAndCallWeenie()(L1516)MoveToComplete(L168) — CLIENT ADDITION, not retail — firesWeenieError.Noneon natural completion only (never on cancel). Bound inEnterPlayerModeNow(GameWindow.cs:13024-13030) to re-fire the deferred AD-27 Use/PickUp action.
2. GameWindow wiring — src/AcDream.App/Rendering/GameWindow.cs (13,587 lines)
2a. EnsureRemoteMotionBindings (GameWindow.cs:4234-4308)
Binds, once per RemoteMotion (guarded by rm.Sink is not null early-return, L4239):
rm.Sink = new MotionTableDispatchSink(ae.Sequencer)withTurnApplied/TurnStoppedcallbacks (L4243-4255) feedingrmForSink.ObservedOmegarm.Motion.DefaultSink = rm.Sink(L4256)rm.Motion.RemoveLinkAnimations = ae.Sequencer.RemoveAllLinkAnimations(L4257)rm.Motion.InitializeMotionTables = () => ae.Sequencer.Manager.InitializeState()(L4258)rm.Motion.CheckForCompletedMotions = ae.Sequencer.Manager.CheckForCompletedMotions(L4261) — R3-W5 per-op zero-tick flushrm.MoveTo = new MoveToManager(...)(L4270-4300) — full seam wiring:stopCompletely,getPosition(world-space,rmT.CellId+mtBody.Position/Orientation),getHeading/setHeadingviaMoveToMath,getOwnRadius/getOwnHeightboth hardcoded0f(comment: "pin P4 note — setup cylsphere radius lands with R5's TargetManager port"),contact: () => mtBody.OnWalkable,isInterpolating: () => rmT.Interp.IsActive,getVelocity,getSelfId: () => serverGuid,setTarget/clearTargetwritingrmT.TrackedTargetGuid/Radius/FedOnce(L4285-4291),getTargetQuantum/setTargetQuantumonrmT.TargetQuantum, real-clockcurTime(L4294-4300, R4-V5 fix — was per-call stub before)rm.Motion.InterruptCurrentMovement = () => rmT.MoveTo?.CancelMoveTo(WeenieError.ActionCancelled)(L4304-4306) — TS-36, retail'sinterrupt_current_movement → MovementManager::CancelMoveTo(0x36)chain- NOT bound here:
rm.MoveTo.StickTo,rm.MoveTo.Unstick,rm.MoveTo.MoveToComplete— all left null for remotes.
2b. EnterPlayerModeNow (GameWindow.cs:12960-13145+)
Player-side construction, mirrors 2a with player-specific deltas (documented at L12970-12988):
- (a) heading via
pcMoveTo.Yawbridge, not a quaternion write directly - (b)
contact: () => pcMoveTo.BodyInContact— real Contact transient bit, not OnWalkable proxy - (c)
isInterpolating: () => false— player has no InterpolationManager playerMoveTo = new MoveToManager(...)(L12990-13013)playerMoveTo.MoveToComplete = err => { ...; if (err==None) OnAutoWalkArrivedSendDeferredAction(); }(L13024-13030) — the ONLY MoveToComplete binding in the codebase (AD-27 reanchor)_playerController.MoveTo = playerMoveTo(L13031)_playerController.Motion.InterruptCurrentMovement = () => playerMoveTo.CancelMoveTo(WeenieError.ActionCancelled)(L13039-13045) — TS-36 player side_playerController.Motion.CheckForCompletedMotions = playerSeq.Manager.CheckForCompletedMotions(L13145-13146), inside theif (_animatedEntities.TryGetValue(...))sequencer-bind block (L13135+) — the R4-V5 "moveto-stall fix #2" comment (L13126-13134) documents this MUST run before the initialSetPositionor the sequencer's pending_motions node orphans.- NOT bound:
playerMoveTo.StickTo,playerMoveTo.Unstick. - Player MoveToManager tick pump: not inside
EnterPlayerModeNow— search shows no directplayerMoveTo.UseTime()call in that function; it's driven per-frame fromOnUpdateFramevia_playerMoveToTarget*fields (see §2c) and theRouteServerMoveTocall at L4772 (§2e).
2c. AP-79 tracker block + player pre-Update feed
- Remote-side tracker:
TickRemoteMoveTo(RemoteMotion rm)(GameWindow.cs:4469-4505). FeedsHandleUpdateTarget(Ok)from_entitiesByServerGuidlookup when the tracked target moved beyondrm.TrackedTargetRadius(voyeur-radius diff, L4479-4491), orExitWorldif the guid vanished from the live table (L4493-4502); then unconditionally callsmtm.UseTime()(L4504). - Player-side fields:
_playerMoveToTargetGuid/_playerMoveToTargetRadius/_playerMoveToTargetFedOnce/_playerMoveToTargetQuantumare declared and seeded inEnterPlayerModeNow(L13006-13016) via the ctor'ssetTarget/clearTarget/getTargetQuantum/setTargetQuantumclosures, but the actual per-tick "feed HandleUpdateTarget + call UseTime()" loop for the player was NOT found insideEnterPlayerModeNow— grep ofTickRemoteMoveTo-equivalent player logic did not surface a distinct function; it likely lives inOnUpdateFrame(13,587-line file — not fully read this pass). Flag for R5 authors: locate/confirm the player pump site before assuming parity withTickRemoteMoveTo.
2d. HitGround / LeaveGround / MotionDone / UseTime / CheckForCompletedMotions call sites
rm.Motion.LeaveGround()— GameWindow.cs:5100 (remote ground departure, retailLeaveGround0x00528b00)rmState.Motion.HitGround()+rmState.MoveTo?.HitGround()— GameWindow.cs:5425-5426 (remote landing; comment cites #161, retailMovementManager::HitGround)rm.Motion.HitGround()+rm.MoveTo?.HitGround()— GameWindow.cs:10062, 10070 (a SECOND remote-landing site, same pattern — the file has two independent landing branches)ae.Sequencer.MotionDoneTarget = (m, ok) => { ...; interp?.MotionDone(m, ok); }— GameWindow.cs:10142-10160 (R3-W2, bound once per sequencer; retailCPhysicsObj::MotionDone0x0050fdb0 chain)ae.Sequencer.Manager.UseTime()— GameWindow.cs:10194 (this is the ANIMATION sequencer's own UseTime, not MoveToManager's — do not conflate)mtm.UseTime()— GameWindow.cs:4504 insideTickRemoteMoveTo(the MoveToManager tick, per remote, per frame)rm.Motion.CheckForCompletedMotions = ...— GameWindow.cs:4261 (bind site, inEnsureRemoteMotionBindings)
2e. RouteServerMoveTo (the type-6..9 funnel entry — see §5 also)
RouteServerMoveTo(MoveToManager mgr, MotionInterpreter interp, uint cellId, EntityMotionUpdate update) — GameWindow.cs:4361-4457. Two call sites:
- Player: GameWindow.cs:4772 —
RouteServerMoveTo(playerMoveTo, _playerController.Motion, _playerController.CellId, update), inside theupdate.Guid == _playerServerGuidbranch (L4716), gated behind_playerController is not null(L4753) and preceded by_playerController.Motion.InterruptCurrentMovement?.Invoke()+.UnstickFromObject?.Invoke()(L4768-4769) — the retail unpack_movement head (@300566). - Remote: GameWindow.cs:4835 —
RouteServerMoveTo(moveMgr, remoteMot.Motion, remoteMot.CellId, update), inside theelse(non-player) branch (L4784), also preceded byremoteMot.Motion.InterruptCurrentMovement?.Invoke()+.UnstickFromObject?.Invoke()(L4828-4829).
2f. RemoteMotion class — GameWindow.cs:411-510+ (nested internal sealed class RemoteMotion, NOT a separate file)
Manager-related fields:
PhysicsBody Body,MotionInterpreter Motion(L413-414)MotionTableDispatchSink? Sink(L419)MoveToManager? MoveTo(L436) — R4-V4- AP-79 tracker state:
TrackedTargetGuid,TrackedTargetRadius,TrackedTargetLastFedPos,TrackedTargetFedOnce,TargetQuantum(L442-446) - Legacy/parallel
RemoteMoveToDriver-oriented fields still present:ServerMoveToActive,HasMoveToDestination,MoveToDestinationWorld,MoveToMinDistance,MoveToDistanceToObject,MoveToMoveTowards,LastMoveToPacketTime(L448-503) — comments referenceAcDream.Core.Physics.RemoteMoveToDriver, which per the R4 handoff docs was retired in the R4 "three approximations" cleanup (per MEMORY.md: "RemoteMoveToDriver/PlanMoveToStart/B.6-auto-walk all deleted"). These fields may now be dead weight predating that retirement — worth confirming during R5 cleanup.
3. MotionInterpreter sticky/action seams — src/AcDream.Core/Physics/MotionInterpreter.cs (3,298 lines)
StickTo/Unstick/stickgrep: NOStickToseam exists onMotionInterpreter— onlyUnstickFromObject(L779:public Action? UnstickFromObject { get; set; }, doc L771-807), the R3-W2 no-op seam standing in for retailCPhysicsObj::unstick_from_object. This is a DIFFERENT seam fromMoveToManager.Unstick/.StickTo(§1) — MotionInterpreter's copy is called at L2329 and L2375 inside its own dispatch/cancel paths, unrelated to the MoveToManager sticky handoff.StandingLongJump: fieldpublic bool StandingLongJump;(L747). Arms ONLY atChargeJump(L25 comment, L1970StandingLongJump = true;). Cleared at L2023 and L2511 (StandingLongJump = false;). Consumed inApplyInterpretedMovement's three-way branch (L2947, quoted below) and inDoInterpretedMotion's verbatim retail body pseudocode (L2995-2997,label_528440goto).
The #164 action-dispatch loop — ApplyInterpretedMovement (MotionInterpreter.cs:2920-2981)
public void ApplyInterpretedMovement(
uint currentStyle, IInterpretedMotionSink? sink,
bool cancelMoveTo = false, bool allowJump = false)
{
if (PhysicsObj is null) return;
// Retail's rewritten var_2c (raw 305778 decoded; ACE 444-449):
// Speed is re-set per axis below, everything else rides the pass.
var p = new MovementParameters
{
SetHoldKey = false,
ModifyInterpretedState = false,
CancelMoveTo = cancelMoveTo,
DisableJumpDuringLink = !allowJump,
};
if (InterpretedState.ForwardCommand == MotionCommand.RunForward)
MyRunRate = InterpretedState.ForwardSpeed;
p.Speed = 1.0f;
DoInterpretedMotion(currentStyle, p, sink);
if (!contact_allows_move(InterpretedState.ForwardCommand))
{
p.Speed = 1.0f; // raw 305728: var_18_2 = 0x3f800000
DoInterpretedMotion(MotionCommand.Falling, p, sink);
}
else if (StandingLongJump)
{
p.Speed = 1.0f;
DoInterpretedMotion(MotionCommand.Ready, p, sink);
StopInterpretedMotion(MotionCommand.SideStepRight, p, sink);
}
else
{
p.Speed = InterpretedState.ForwardSpeed;
DoInterpretedMotion(InterpretedState.ForwardCommand, p, sink);
if (InterpretedState.SideStepCommand == 0)
{
StopInterpretedMotion(MotionCommand.SideStepRight, p, sink);
}
else
{
p.Speed = InterpretedState.SideStepSpeed;
DoInterpretedMotion(InterpretedState.SideStepCommand, p, sink);
}
}
if (InterpretedState.TurnCommand != 0)
{
p.Speed = InterpretedState.TurnSpeed;
DoInterpretedMotion(InterpretedState.TurnCommand, p, sink);
return; // retail early return — no idle-stop this call
}
// Tail: unconditional StopInterpretedMotion(TurnRight, params).
StopInterpretedMotion(MotionCommand.TurnRight, p, sink);
}
Correction to the task framing: the dispatched MovementParameters p is built ONCE at the top with Autonomous implicitly false (default, never set) and explicitly sets SetHoldKey=false, ModifyInterpretedState=false, CancelMoveTo=cancelMoveTo, DisableJumpDuringLink=!allowJump — only p.Speed is mutated per-branch (not re-constructed per dispatch as new MovementParameters { Speed }). The #161 doc comment (L2892-2918) explicitly discusses this exact bitfield and calls ModifyInterpretedState=false "load-bearing." The Autonomous bit is never read or written anywhere in this method — confirms the framing that the loop drops/never-carries the Autonomous bit through to per-dispatch MovementParameters.
This is called from MoveToInterpretedState (L2820-2866, the funnel entry) at L2843: ApplyInterpretedMovement(ims.CurrentStyle, sink, cancelMoveTo: true, allowJump: allowJump); — always cancelMoveTo: true for inbound wire state.
4. UpdateMotion wire parsing — src/AcDream.Core.Net/Messages/UpdateMotion.cs (479 lines)
mt-0 header flag parsing
- MotionFlags byte (wire term "header" in decomp comments) parsed at L161:
byte motionFlags = body[pos]; pos += 1;— comment (L148-149) documents0x1 = StickToObject, 0x2 = StandingLongJump(ACEMotionFlagsenum names). This is NOT the same as the "0x100"/"0x200" framing in the task — those are decomp-doc shorthand for the SAME bits at a different byte-shift (header & 0x100in the decomp refers to bit 0 of this byte after the 8-bitmotionFlagsis logically at bit-position 8 of the larger unpack_movement header dword). The actual C# test is(motionFlags & 0x1) != 0for sticky (L290) — there is no separate literal0x100/0x200mask in this file; grep for those literals returns nothing in UpdateMotion.cs. - 0x1 (StickToObject) parse site: L279-295, comment block cites ACE
MovementInvalid.Write+ decomp §2f case 0 @0052455d. Code:
Only fires forif ((motionFlags & 0x1) != 0 && body.Length - pos >= 4) { stickyObjectGuid = BinaryPrimitives.ReadUInt32LittleEndian(body.Slice(pos)); pos += 4; }movementType == 0(InterpretedMotionState branch, L188). The trailing u32 is read AFTER the InterpretedMotionState body + Commands list (matches ACE's write order). - 0x2 (StandingLongJump) — NOT consumed: comment L153-160 explicitly states "DOCUMENTED here but NOT consumed — retail's
unpack_movementcase 0 writes it ontomotion_interpreter->standing_longjump(§2f @0052458e), an R5 unpack_movement item (this parser has no MotionInterpreter reference to write it onto...)". No code readsmotionFlags & 0x2anywhere in the file.
Downstream consumers (grep of whole src tree)
StickyObjectGuid(the parsed field, carried onCreateObject.ServerMotionState, L317-326 return +CreateObject.cs:265): grep acrosssrc/shows it is assigned into the record and returned, but never read by any production consumer — the only non-definition hits are intests/AcDream.Core.Net.Tests/Messages/UpdateMotionTests.cs(asserting the parsed value, L662/683/716). No site inGameWindow.cs,MotionInterpreter.cs, orMoveToManager.csreads.StickyObjectGuidoffServerMotionState.motionFlags & 0x2/MotionFlags.StandingLongJump: zero matches anywhere in the repo outside the UpdateMotion.cs documentation comment itself.- Conclusion for R5: both the sticky-guid trailer and the standing-longjump wire bit are parsed-and-shelved. R5's PositionManager::StickTo (per the UpdateMotion.cs comment, "R5's PositionManager::StickTo body is the eventual consumer") and MovementManager (for standing-longjump) are the intended consumers — neither exists yet.
5. UM dispatch path for movement types 6-9 — funnel + head-stance question
- Funnel/router:
RouteServerMoveTo(GameWindow.cs:4361-4457, quoted in part in §2e) is the ONE function that inspectsupdate.MotionState.IsServerControlledMoveTo/IsServerControlledTurnTo+MovementType(6/7 → MoveToObject/MoveToPosition viapath; 8/9 → TurnToObject/TurnToHeading viaturnPath) and callsmgr.PerformMovement(ms). Both the player call site (GameWindow.cs:4772) and remote call site (GameWindow.cs:4835) funnel through this SAME function — confirmed shared body, not duplicated per the R4-V5 extraction comment (L4459-4468). - Does any site do a head stance-change dispatch (
InqStyle != wire style -> DoMotion(style)) BEFORE the type-6..9 switch? NO separate dispatch exists. For the remote path, the stance/style value folds directly intoInboundInterpretedState.CurrentStyle(GameWindow.cs:4867:CurrentStyle = stance != 0 ? (0x80000000u | (uint)stance) : 0x8000003Du), which is built and consumed only in theelsebranch AFTERRouteServerMoveToreturnsfalse(i.e., only for mt-0 / non-moveto events) — see GameWindow.cs:4834-4839 (RouteServerMoveTocall, early-returnon true) followed by theimsconstruction at L4865-4874. So: for a wire event that IS a type-6..9 moveto, the style/stance field is never separately dispatched at this layer —MoveToManager.PerformMovementdoesn't take or apply a style at all (it only setsMovementTypeStateand queues nodes; noDoMotion(style)call anywhere inPerformMovement/MoveToObject/MoveToPosition/TurnToObject/TurnToHeading, confirmed by reading all of §1's L411-637). The style-on-change dispatch is explicitly flagged as unported:RetailObserverTraceConformanceTests.cs:33documents an EXCLUSION — "motion==0x80000000 entries (the unpack-level DoMotion(command_ids[0]) style call for wire style index 0) — outside move_to_interpreted_state; S3 wires the unpack-level style-on-change" (not yet done). This is a concrete gap for R5/MovementManager: retail's realunpack_movementhead does anInqStyle-vs-wire-style compare and issues a styleDoMotionunconditionally (independent of movement type 0 vs 6-9), and acdream currently only applies style via the mt-0InboundInterpretedState.CurrentStylepath — mt 6-9 events carry no style application at all today.
6. PhysicsBody — src/AcDream.Core/Physics/PhysicsBody.cs (576 lines)
- IsFullyConstrained (TS-35, doc calls it TS-35 but file comment says "R3-W3 stub"):
public bool IsFullyConstrained { get; set; }(L263), doc block L251-262. RetailCPhysicsObj::IsFullyConstrained(0x0050f730), read byCMotionInterp::jump_is_allowed(if (IsFullyConstrained(physics_obj) != 0) return 0x47). Comment: "acdream has no equivalent constraint-tracking yet... stubbed false (never fires) — a real port needs the per-cell shadow-list contact accounting the physics digest tracks." Always false in production (settable property, but nothing in the grep'd files sets it true). - TransientState bits available (
TransientStateFlags, L62-70,[Flags] enum):Contact = 0x1(bit 0) — touching any surfaceOnWalkable = 0x2(bit 1) — standing on walkable surfaceSliding = 0x4(bit 2) — carry sliding normal into next transitionActive = 0x80(bit 7) — needs per-frame update- Convenience getters:
OnWalkable(L286),IsActive(L287),InContact(L288) properties onPhysicsBody.
- PositionManager: does not exist as a class anywhere in the repo. Grep for
PositionManageracrosssrc/returns zero hits outside doc comments referencing it as a future R5 port target (e.g. MoveToManager.cs:24, L109-112 doc, UpdateMotion.cs comment).IsInterpolating(the seam MoveToManager consumes) is currently satisfied by ad-hoc booleans:rmT.Interp.IsActivefor remotes (GameWindow.cs:4282) and a hardcodedfalsefor the player (GameWindow.cs:13001) — neither is a real PositionManager.
7. Test files (locations, not full case enumeration)
MoveToManager suite — tests/AcDream.Core.Tests/Physics/Motion/:
MoveToManagerTestHarness.cs— shared harness/fixtureMoveToManagerLifecycleTests.csMoveToManagerNodePlanTests.csMoveToManagerBeginMoveForwardTests.csMoveToManagerAuxTurnTests.csMoveToManagerArrivalAndProgressTests.csMoveToManagerTurnToHeadingTests.csMoveToManagerHandleUpdateTargetTests.csMoveToManagerStickyAndCancelTests.csMoveToManagerUseTimeGateTests.csMoveToManagerEndToEndTableDriveTests.csMoveToManagerCompletionSeamTests.csMovementTypeWideningTests.csMoveToMathCylinderDistanceTests.csMotionTableDispatchSinkTests.csInterpretedMotionStateActionFifoTests.cs
Funnel / 183-case conformance suite:
tests/AcDream.Core.Tests/Physics/RetailObserverTraceConformanceTests.cs(35+) — the live-retail-observer-cdb-trace conformance harness ("183/183" cited in MEMORY.md refers to this file's case count). ContainsLoginQueue_DrainsToEmpty_UnderProductionFeedat line 279 ([Fact]).tests/AcDream.Core.Tests/Physics/MotionInterpreterFunnelTests.cstests/AcDream.Core.Tests/Physics/Motion/(see above — dispatch-adjacent)
DispatchInterpreted / MotionInterpreter core suite — tests/AcDream.Core.Tests/Physics/:
MotionInterpreterTests.csMotionInterpreterDoMotionFamilyTests.csMotionInterpreterGroundLifecycleTests.csMotionInterpreterJumpFamilyTests.csMotionInterpreterPendingMotionsTests.csServerControlledLocomotionTests.csRemoteWeenieRunRateTests.csInWorldLinkGuardTests.csMotionNormalizationTests.csMotionVelocityPipelineTests.csAnimationSequencerCutoverTraceTests.csWeenieErrorCodeTableTests.cs
Player moveto cutover:
tests/AcDream.Core.Tests/Input/PlayerMoveToCutoverTests.cs
Summary answers to the task's specific questions
Q4 (UpdateMotion sticky/longjump): 0x1 (StickToObject) trailer IS parsed (UpdateMotion.cs:279-295, code at 290-293) into stickyObjectGuid → ServerMotionState.StickyObjectGuid; 0x2 (StandingLongJump) is documented but the bit is never even read. Neither has a production consumer — grep-wide, StickyObjectGuid is read only by unit tests; MotionFlags.StandingLongJump/0x2 has zero non-comment hits anywhere.
Q5 (type 6-9 head-stance dispatch): No such dispatch exists. RouteServerMoveTo (GameWindow.cs:4361) is the shared funnel for both player and remote; it never touches style/stance. MoveToManager.PerformMovement/its four entry points never call DoMotion(style). Style application only happens on the mt-0 path via InboundInterpretedState.CurrentStyle folded at GameWindow.cs:4867. RetailObserverTraceConformanceTests.cs:33's exclusion comment explicitly flags the unpack-level style-on-change dispatch as not yet wired ("S3 wires the unpack-level style-on-change") — this is a known, documented gap, not an oversight this recon discovered fresh.
Q6 (PhysicsBody / PositionManager): IsFullyConstrained is a settable bool stub, always false in practice (PhysicsBody.cs:263). TransientStateFlags has Contact/OnWalkable/Sliding/Active (PhysicsBody.cs:63-70). No PositionManager class exists anywhere in the repo — IsInterpolating is faked per-caller (Interp.IsActive for remotes, hardcoded false for the player).
File path of the written map: C:/Users/erikn/AppData/Local/Temp/claude/C--Users-erikn-source-repos-acdream--claude-worktrees-vigorous-joliot-f0c3ad/dd264e44-6468-4b4b-8d17-a8b3b15991c9/scratchpad/r5/acdream-seams.md