feat(phys L.2g slice 1): GameWindow routes SetState + extends [entity-source] log

Two changes folded into one commit:

1. GameWindow subscribes to WorldSession.StateUpdated and routes the
   parsed (guid, newState) pair into
   ShadowObjectRegistry.UpdatePhysicsState. End-to-end wiring complete:
   server SetState (0xF74B) -> WorldSession dispatcher -> StateUpdated
   event -> GameWindow handler -> registry mutation -> next resolver
   tick sees the new ETHEREAL bit and CollisionExemption short-circuits
   the door cylinder. After this commit the M1 'open the inn door'
   scenario is unblocked at the code-path level; visual verification
   follows in Task 7 (user-driven).

   The handler also emits a [setstate] diagnostic line when
   ACDREAM_PROBE_BUILDING is enabled, giving a greppable trail when
   the visual test runs.

2. Slice 0.5 freebie folded in: the [entity-source] probe lines now
   include state=0x... flags=... so ETHEREAL flips are greppable
   end-to-end from spawn through state change. Resolves the 'slice
   1.6' suggestion from the L.2d ship handoff
   (docs/research/2026-05-13-l2d-slice1-shipped-handoff.md).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-12 22:35:57 +02:00
parent 536a608093
commit 108e3868a5

View file

@ -1791,6 +1791,7 @@ public sealed class GameWindow : IDisposable
_liveSession.MotionUpdated += OnLiveMotionUpdated; _liveSession.MotionUpdated += OnLiveMotionUpdated;
_liveSession.PositionUpdated += OnLivePositionUpdated; _liveSession.PositionUpdated += OnLivePositionUpdated;
_liveSession.VectorUpdated += OnLiveVectorUpdated; _liveSession.VectorUpdated += OnLiveVectorUpdated;
_liveSession.StateUpdated += OnLiveStateUpdated;
_liveSession.TeleportStarted += OnTeleportStarted; _liveSession.TeleportStarted += OnTeleportStarted;
_liveSession.AppearanceUpdated += OnLiveAppearanceUpdated; _liveSession.AppearanceUpdated += OnLiveAppearanceUpdated;
@ -2976,7 +2977,7 @@ public sealed class GameWindow : IDisposable
// L.2d slice 1 (2026-05-13): [entity-source] greppable from [resolve-bldg]. // L.2d slice 1 (2026-05-13): [entity-source] greppable from [resolve-bldg].
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled) if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled)
Console.WriteLine(System.FormattableString.Invariant( Console.WriteLine(System.FormattableString.Invariant(
$"[entity-source] id=0x{entity.Id:X8} entityId=0x{entity.Id:X8} src=0x{entity.SourceGfxObjOrSetupId:X8} gfxObj=0x{entity.SourceGfxObjOrSetupId:X8} lb=0x{spawn.Position.Value.LandblockId:X8} type=Cylinder note=server-spawn-root")); $"[entity-source] id=0x{entity.Id:X8} entityId=0x{entity.Id:X8} src=0x{entity.SourceGfxObjOrSetupId:X8} gfxObj=0x{entity.SourceGfxObjOrSetupId:X8} lb=0x{spawn.Position.Value.LandblockId:X8} type=Cylinder note=server-spawn-root state=0x{state:X8} flags={flags}"));
} }
private bool RemoveLiveEntityByServerGuid(uint serverGuid, bool logDelete) private bool RemoveLiveEntityByServerGuid(uint serverGuid, bool logDelete)
@ -3754,6 +3755,23 @@ public sealed class GameWindow : IDisposable
} }
} }
/// <summary>
/// L.2g slice 1: inbound SetState (0xF74B) handler. Propagates the
/// new <c>PhysicsState</c> bits into ShadowObjectRegistry so the
/// existing <see cref="CollisionExemption.ShouldSkip"/> check honors
/// the flip on the next resolver tick. Chiefly doors:
/// server flips <c>ETHEREAL_PS = 0x4</c> on Use, the door's
/// cylinder collision stops blocking the threshold.
/// </summary>
private void OnLiveStateUpdated(AcDream.Core.Net.Messages.SetState.Parsed parsed)
{
_physicsEngine.ShadowObjects.UpdatePhysicsState(parsed.Guid, parsed.PhysicsState);
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled)
Console.WriteLine(System.FormattableString.Invariant(
$"[setstate] guid=0x{parsed.Guid:X8} state=0x{parsed.PhysicsState:X8} instSeq={parsed.InstanceSequence} stateSeq={parsed.StateSequence}"));
}
private static bool IsRemoteLocomotion(uint motion) private static bool IsRemoteLocomotion(uint motion)
{ {
uint low = motion & 0xFFu; uint low = motion & 0xFFu;
@ -5540,9 +5558,11 @@ public sealed class GameWindow : IDisposable
// L.2d slice 1 (2026-05-13): [entity-source] greppable from [resolve-bldg]. // L.2d slice 1 (2026-05-13): [entity-source] greppable from [resolve-bldg].
// partCached?.BSP?.Root non-null was checked above (else `continue`), // partCached?.BSP?.Root non-null was checked above (else `continue`),
// so hasPhys=true on this path. // so hasPhys=true on this path.
// state/flags literals: landblock-baked scenery has no server PhysicsState
// broadcast and no PWD bitfield; defaults match static-solid semantics.
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled) if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled)
Console.WriteLine(System.FormattableString.Invariant( Console.WriteLine(System.FormattableString.Invariant(
$"[entity-source] id=0x{partId:X8} entityId=0x{entity.Id:X8} src=0x{entity.SourceGfxObjOrSetupId:X8} gfxObj=0x{meshRef.GfxObjId:X8} lb=0x{lb.LandblockId:X8} type=BSP note=partIdx={partIndex} hasPhys=true")); $"[entity-source] id=0x{partId:X8} entityId=0x{entity.Id:X8} src=0x{entity.SourceGfxObjOrSetupId:X8} gfxObj=0x{meshRef.GfxObjId:X8} lb=0x{lb.LandblockId:X8} type=BSP note=partIdx={partIndex} hasPhys=true state=0x{0u:X8} flags={AcDream.Core.Physics.EntityCollisionFlags.None}"));
entityBsp++; entityBsp++;
partIndex++; partIndex++;
@ -5595,9 +5615,10 @@ public sealed class GameWindow : IDisposable
origin.X, origin.Y, lb.LandblockId, origin.X, origin.Y, lb.LandblockId,
AcDream.Core.Physics.ShadowCollisionType.Cylinder, cylHeight); AcDream.Core.Physics.ShadowCollisionType.Cylinder, cylHeight);
// L.2d slice 1 (2026-05-13): [entity-source] greppable from [resolve-bldg]. // L.2d slice 1 (2026-05-13): [entity-source] greppable from [resolve-bldg].
// state/flags literals: landblock-baked scenery; no server PhysicsState.
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled) if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled)
Console.WriteLine(System.FormattableString.Invariant( Console.WriteLine(System.FormattableString.Invariant(
$"[entity-source] id=0x{shapeId:X8} entityId=0x{entity.Id:X8} src=0x{entity.SourceGfxObjOrSetupId:X8} gfxObj=0x{entity.SourceGfxObjOrSetupId:X8} lb=0x{lb.LandblockId:X8} type=Cylinder note=setup-cylsphere#{ci}")); $"[entity-source] id=0x{shapeId:X8} entityId=0x{entity.Id:X8} src=0x{entity.SourceGfxObjOrSetupId:X8} gfxObj=0x{entity.SourceGfxObjOrSetupId:X8} lb=0x{lb.LandblockId:X8} type=Cylinder note=setup-cylsphere#{ci} state=0x{0u:X8} flags={AcDream.Core.Physics.EntityCollisionFlags.None}"));
entityCyl++; entityCyl++;
} }
@ -5629,9 +5650,10 @@ public sealed class GameWindow : IDisposable
origin.X, origin.Y, lb.LandblockId, origin.X, origin.Y, lb.LandblockId,
AcDream.Core.Physics.ShadowCollisionType.Cylinder, sphHeight); AcDream.Core.Physics.ShadowCollisionType.Cylinder, sphHeight);
// L.2d slice 1 (2026-05-13): [entity-source] greppable from [resolve-bldg]. // L.2d slice 1 (2026-05-13): [entity-source] greppable from [resolve-bldg].
// state/flags literals: landblock-baked scenery; no server PhysicsState.
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled) if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled)
Console.WriteLine(System.FormattableString.Invariant( Console.WriteLine(System.FormattableString.Invariant(
$"[entity-source] id=0x{shapeId:X8} entityId=0x{entity.Id:X8} src=0x{entity.SourceGfxObjOrSetupId:X8} gfxObj=0x{entity.SourceGfxObjOrSetupId:X8} lb=0x{lb.LandblockId:X8} type=Cylinder note=setup-sphere#{si}")); $"[entity-source] id=0x{shapeId:X8} entityId=0x{entity.Id:X8} src=0x{entity.SourceGfxObjOrSetupId:X8} gfxObj=0x{entity.SourceGfxObjOrSetupId:X8} lb=0x{lb.LandblockId:X8} type=Cylinder note=setup-sphere#{si} state=0x{0u:X8} flags={AcDream.Core.Physics.EntityCollisionFlags.None}"));
entityCyl++; entityCyl++;
} }
} }
@ -5651,9 +5673,10 @@ public sealed class GameWindow : IDisposable
origin.X, origin.Y, lb.LandblockId, origin.X, origin.Y, lb.LandblockId,
AcDream.Core.Physics.ShadowCollisionType.Cylinder, fh); AcDream.Core.Physics.ShadowCollisionType.Cylinder, fh);
// L.2d slice 1 (2026-05-13): [entity-source] greppable from [resolve-bldg]. // L.2d slice 1 (2026-05-13): [entity-source] greppable from [resolve-bldg].
// state/flags literals: landblock-baked scenery; no server PhysicsState.
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled) if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled)
Console.WriteLine(System.FormattableString.Invariant( Console.WriteLine(System.FormattableString.Invariant(
$"[entity-source] id=0x{shapeId:X8} entityId=0x{entity.Id:X8} src=0x{entity.SourceGfxObjOrSetupId:X8} gfxObj=0x{entity.SourceGfxObjOrSetupId:X8} lb=0x{lb.LandblockId:X8} type=Cylinder note=setup-radius-fallback")); $"[entity-source] id=0x{shapeId:X8} entityId=0x{entity.Id:X8} src=0x{entity.SourceGfxObjOrSetupId:X8} gfxObj=0x{entity.SourceGfxObjOrSetupId:X8} lb=0x{lb.LandblockId:X8} type=Cylinder note=setup-radius-fallback state=0x{0u:X8} flags={AcDream.Core.Physics.EntityCollisionFlags.None}"));
entityCyl++; entityCyl++;
} }
} }
@ -5836,9 +5859,10 @@ public sealed class GameWindow : IDisposable
origin.X, origin.Y, lb.LandblockId, origin.X, origin.Y, lb.LandblockId,
AcDream.Core.Physics.ShadowCollisionType.Cylinder, cylHeight); AcDream.Core.Physics.ShadowCollisionType.Cylinder, cylHeight);
// L.2d slice 1 (2026-05-13): [entity-source] greppable from [resolve-bldg]. // L.2d slice 1 (2026-05-13): [entity-source] greppable from [resolve-bldg].
// state/flags literals: landblock-baked scenery; no server PhysicsState.
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled) if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled)
Console.WriteLine(System.FormattableString.Invariant( Console.WriteLine(System.FormattableString.Invariant(
$"[entity-source] id=0x{entity.Id:X8} entityId=0x{entity.Id:X8} src=0x{entity.SourceGfxObjOrSetupId:X8} gfxObj=0x{entity.SourceGfxObjOrSetupId:X8} lb=0x{lb.LandblockId:X8} type=Cylinder note=mesh-aabb-fallback")); $"[entity-source] id=0x{entity.Id:X8} entityId=0x{entity.Id:X8} src=0x{entity.SourceGfxObjOrSetupId:X8} gfxObj=0x{entity.SourceGfxObjOrSetupId:X8} lb=0x{lb.LandblockId:X8} type=Cylinder note=mesh-aabb-fallback state=0x{0u:X8} flags={AcDream.Core.Physics.EntityCollisionFlags.None}"));
entityCyl++; entityCyl++;
if (_isScenery) scRegistered++; if (_isScenery) scRegistered++;
} }