From 108e3868a5a77e82827e0786c83b4e2341135f39 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 12 May 2026 22:35:57 +0200 Subject: [PATCH] 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) --- src/AcDream.App/Rendering/GameWindow.cs | 36 ++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index 3cc4b15..73127de 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -1791,6 +1791,7 @@ public sealed class GameWindow : IDisposable _liveSession.MotionUpdated += OnLiveMotionUpdated; _liveSession.PositionUpdated += OnLivePositionUpdated; _liveSession.VectorUpdated += OnLiveVectorUpdated; + _liveSession.StateUpdated += OnLiveStateUpdated; _liveSession.TeleportStarted += OnTeleportStarted; _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]. if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled) 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) @@ -3754,6 +3755,23 @@ public sealed class GameWindow : IDisposable } } + /// + /// L.2g slice 1: inbound SetState (0xF74B) handler. Propagates the + /// new PhysicsState bits into ShadowObjectRegistry so the + /// existing check honors + /// the flip on the next resolver tick. Chiefly doors: + /// server flips ETHEREAL_PS = 0x4 on Use, the door's + /// cylinder collision stops blocking the threshold. + /// + 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) { 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]. // partCached?.BSP?.Root non-null was checked above (else `continue`), // 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) 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++; partIndex++; @@ -5595,9 +5615,10 @@ public sealed class GameWindow : IDisposable origin.X, origin.Y, lb.LandblockId, AcDream.Core.Physics.ShadowCollisionType.Cylinder, cylHeight); // 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) 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++; } @@ -5629,9 +5650,10 @@ public sealed class GameWindow : IDisposable origin.X, origin.Y, lb.LandblockId, AcDream.Core.Physics.ShadowCollisionType.Cylinder, sphHeight); // 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) 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++; } } @@ -5651,9 +5673,10 @@ public sealed class GameWindow : IDisposable origin.X, origin.Y, lb.LandblockId, AcDream.Core.Physics.ShadowCollisionType.Cylinder, fh); // 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) 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++; } } @@ -5836,9 +5859,10 @@ public sealed class GameWindow : IDisposable origin.X, origin.Y, lb.LandblockId, AcDream.Core.Physics.ShadowCollisionType.Cylinder, cylHeight); // 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) 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++; if (_isScenery) scRegistered++; }