diff --git a/docs/ISSUES.md b/docs/ISSUES.md index 1fe5c09e..9c7882ce 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -803,7 +803,62 @@ If the one-frame deletion is ever judged unacceptable, the lever is the ## #330 — The headless host registers no live-entity collision at all: a bot walks through every NPC and every server-spawned object -**Status:** OPEN +**Status:** OPEN — **SCOPE MAPPED 2026-08-07 by a dual Opus review of a +withheld implementation; the builder hoist landed, the wiring did not.** + +An overnight implementation attempt wired spawn-time registration into the +no-window route. Both review lenses failed it, converging (two reviewers +converging = near-proof, per the project's own rule), and the findings ARE +the issue's true scope — recorded here so the next attempt starts from the +map instead of rediscovering it: + +1. **A headless remote-motion tick does not exist.** `RuntimeRemotePhysicsUpdater` + is Runtime-HOMED but App-DRIVEN (constructed only at + `RemotePhysicsUpdater.cs:46`, ticked only from + `LiveEntityAnimationScheduler.cs:351`), and `GetOrCreateRemoteMotion` has + one production caller, in App. A shadow registered at spawn therefore + FREEZES at the unsettled wire pose: a walking NPC becomes a phantom + obstacle at its spawn point while the real NPC still passes through the + bot — strictly worse than the honest gap. This is the load-bearing + prerequisite. +2. **Live collision-asset publication does not exist headless-side.** The + headless `PhysicsDataCache` holds only the per-landblock static closure; + there is no `IPreparedCollisionSource` on-demand pull + (graphical: `LiveCollisionAssetPublisher`). Without it, + `_physicsBspBounds` is null for live-entity parts, BSP dispatch never + fires, and doors/chests/statues/portals get the wrong shape or none. +3. **Degrade resolution:** the graphical route resolves collision part ids + through `GfxObjDegradeResolver` slot-0 walking; raw `setup.Parts` is the + wrong id for every humanoid part. +4. **Shadow lifetime hangs off FIVE edges, not two:** wire delete, pickup + (`TryApplyPickup` leaves a permanent invisible collider from a looted + item), same-guid generation supersession (`RetireCanonicalOnly` path has + no unregister → duplicate phantom), GENERATION RESET + (`HeadlessGenerationResetHost.RetireEntityProjection` is an empty no-op + and `ResetSessionPhysics` does not clear `ShadowObjects` — registrations + leak across reconnects), and hidden/withdrawn suspension. +5. **The K-ledger cannot see any of this:** `RuntimePhysicsOwnershipSnapshot.IsConverged` + checks retained shadow count only AFTER disposal, and disposal clears the + registry. Extending the convergence oracle to pre-disposal retained + counts is part of this issue's test work, or every leak above ships green. +6. **Ordering + retry:** registration must follow `ProjectSpawn` (a throw + after `ApplyAcceptedSpawn` leaves a committed-but-unprojected entity), + and a spawn arriving before the world frame publishes needs a retry pump + — the withheld code silently dropped it for the incarnation's lifetime. +7. **The appearance route EXISTS** (`OnAppearanceUpdated`) and must rebuild + collision, as the graphical binding does. + +**What DID land 2026-08-07:** the builder hoist — +`LiveEntityCollisionBuilder` + `LiveEntityDefaultPoseResolver` moved to +`AcDream.Runtime.Physics` (internal + existing IVT), `Build(...)`'s +App-record parameter replaced by presentation-free primitives including the +`FinalPhysicsState` the contract had missed. Both reviewers passed the hoist +explicitly; the graphical host is diff-verified unchanged. The wiring +attempt itself is preserved in the review transcripts, not in the tree. + +**Original entry below.** + +**Status (original):** OPEN **Severity:** HIGH for headless gameplay fidelity; zero impact on the graphical client. **Filed:** 2026-08-06, from the AP-22 deletion's blast-radius survey (§5 of [`docs/research/2026-08-06-ap22-contract.md`](research/2026-08-06-ap22-contract.md)). diff --git a/src/AcDream.App/Composition/ContentEffectsAudioComposition.cs b/src/AcDream.App/Composition/ContentEffectsAudioComposition.cs index ab545721..b92a76c8 100644 --- a/src/AcDream.App/Composition/ContentEffectsAudioComposition.cs +++ b/src/AcDream.App/Composition/ContentEffectsAudioComposition.cs @@ -14,6 +14,7 @@ using AcDream.Core.Spells; using AcDream.Core.Vfx; using AcDream.Runtime; using AcDream.Runtime.Gameplay; +using AcDream.Runtime.Physics; using DatReaderWriter; using Silk.NET.Input; diff --git a/src/AcDream.App/Rendering/DatLiveEntityProjectionMaterializer.cs b/src/AcDream.App/Rendering/DatLiveEntityProjectionMaterializer.cs index 8887903f..1a78c234 100644 --- a/src/AcDream.App/Rendering/DatLiveEntityProjectionMaterializer.cs +++ b/src/AcDream.App/Rendering/DatLiveEntityProjectionMaterializer.cs @@ -12,6 +12,7 @@ using AcDream.Core.Physics; using AcDream.Core.Plugins; using AcDream.Core.World; using AcDream.Runtime.Entities; +using AcDream.Runtime.Physics; using DatReaderWriter; using DatReaderWriter.DBObjs; using DatReaderWriter.Types; @@ -834,7 +835,10 @@ internal sealed class DatLiveEntityProjectionMaterializer setup, collisionPartGfxObjIds, spawn, - expectedRecord, + expectedRecord.ServerGuid, + expectedRecord.Generation, + expectedRecord.WorldEntity!, + expectedRecord.FinalPhysicsState, worldOrigin) is { } collision) { LiveEntityCollisionBuilder.Register(_shadows, collision); diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index 9b2996bf..b3b472b9 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -267,7 +267,7 @@ public sealed class GameWindow : private readonly AcDream.App.Rendering.Wb.EntityClassificationCache _classificationCache = new(); private AcDream.Core.Physics.IAnimationLoader? _animLoader; - private AcDream.App.Physics.LiveEntityCollisionBuilder? _liveEntityCollisionBuilder; + private AcDream.Runtime.Physics.LiveEntityCollisionBuilder? _liveEntityCollisionBuilder; // Phase E.1: central fan-out for animation hooks. Audio (E.2), // particles (E.3), combat (E.4), and renderer state mutators all @@ -818,7 +818,7 @@ public sealed class GameWindow : PublishCompositionOwner(ref _animLoader, value, "animation loader"); void IGameWindowContentEffectsAudioPublication.PublishLiveEntityCollisionBuilder( - AcDream.App.Physics.LiveEntityCollisionBuilder value) => + AcDream.Runtime.Physics.LiveEntityCollisionBuilder value) => PublishCompositionOwner( ref _liveEntityCollisionBuilder, value, diff --git a/src/AcDream.App/Rendering/LiveEntityAppearanceBinding.cs b/src/AcDream.App/Rendering/LiveEntityAppearanceBinding.cs index 96e156bf..dce6618f 100644 --- a/src/AcDream.App/Rendering/LiveEntityAppearanceBinding.cs +++ b/src/AcDream.App/Rendering/LiveEntityAppearanceBinding.cs @@ -1,9 +1,9 @@ using System.Numerics; -using AcDream.App.Physics; using AcDream.App.World; using AcDream.Core.Net; using AcDream.Core.Physics; using AcDream.Core.World; +using AcDream.Runtime.Physics; using DatReaderWriter.DBObjs; namespace AcDream.App.Rendering; @@ -95,7 +95,10 @@ internal static class LiveEntityAppearanceBinding setup, effectivePartGfxObjIds, spawn, - record, + record.ServerGuid, + record.Generation, + record.WorldEntity!, + record.FinalPhysicsState, worldOrigin, retainEmptyPayload: true)); } diff --git a/src/AcDream.App/Physics/LiveEntityCollisionBuilder.cs b/src/AcDream.Runtime/Physics/LiveEntityCollisionBuilder.cs similarity index 77% rename from src/AcDream.App/Physics/LiveEntityCollisionBuilder.cs rename to src/AcDream.Runtime/Physics/LiveEntityCollisionBuilder.cs index 59929e9c..9299b13a 100644 --- a/src/AcDream.App/Physics/LiveEntityCollisionBuilder.cs +++ b/src/AcDream.Runtime/Physics/LiveEntityCollisionBuilder.cs @@ -1,5 +1,4 @@ using System.Numerics; -using AcDream.App.World; using AcDream.Core.Items; using AcDream.Core.Net; using AcDream.Core.Physics; @@ -7,7 +6,7 @@ using AcDream.Core.World; using DatReaderWriter.DBObjs; using DatReaderWriter.Types; -namespace AcDream.App.Physics; +namespace AcDream.Runtime.Physics; /// /// Immutable input to the canonical shadow registry for one live object. @@ -45,6 +44,23 @@ internal sealed record LiveEntityCollisionRegistration( /// set — Setup.Radius/Height serve attack cones, /// cylinder_distance, and MoveTo, never collision geometry. /// +/// +/// #330 hoist (2026-08-07): moved from AcDream.App.Physics to +/// AcDream.Runtime.Physics so a no-window host can build the same +/// collision shapes a graphical host does +/// (src/AcDream.Headless/Hosting/HeadlessLiveEntityCollisionRegistrar.cs). +/// 's identity guard used to take the App-only +/// LiveEntityRecord exactRecord parameter; it now takes the four +/// presentation-free primitives that record exposed +/// ('s expectedServerGuid/ +/// expectedGeneration/expectedEntity/ +/// expectedFinalPhysicsState below) with IDENTICAL guard and +/// registration-state semantics — no behaviour change. Both graphical call +/// sites (DatLiveEntityProjectionMaterializer, +/// LiveEntityAppearanceBinding.PrepareCollision) pass +/// record.ServerGuid, record.Generation, record.WorldEntity!, +/// record.FinalPhysicsState for these four parameters. +/// internal sealed class LiveEntityCollisionBuilder { private readonly Func _physicsBspBounds; @@ -110,24 +126,47 @@ internal sealed class LiveEntityCollisionBuilder return result; } + /// Identity guard term 1: must equal + /// 's wire guid. Presentation-free replacement for + /// the App-only LiveEntityRecord.ServerGuid this parameter used to + /// read (#330 hoist). + /// Identity guard term 2: must equal + /// 's wire instance sequence. Replaces + /// LiveEntityRecord.Generation () — + /// widened to so a Runtime canonical caller can + /// pass its own generation counter without a narrowing cast; the + /// comparison below is exact either way. + /// Identity guard term 3: must be reference- + /// equal to . Replaces + /// LiveEntityRecord.WorldEntity. + /// Replaces + /// LiveEntityRecord.FinalPhysicsState, which the original + /// exactRecord parameter also supplied for the emitted + /// registration's — + /// this is NOT part of the identity guard, only of the registration + /// payload, but it was only reachable through the same record and so + /// travels alongside the guard's three primitives. public LiveEntityCollisionRegistration? Build( WorldEntity entity, Setup setup, IReadOnlyList effectivePartGfxObjIds, WorldSession.EntitySpawn spawn, - LiveEntityRecord exactRecord, + uint expectedServerGuid, + ulong expectedGeneration, + WorldEntity expectedEntity, + PhysicsStateFlags expectedFinalPhysicsState, Vector3 worldOrigin, bool retainEmptyPayload = false) { ArgumentNullException.ThrowIfNull(entity); ArgumentNullException.ThrowIfNull(setup); ArgumentNullException.ThrowIfNull(effectivePartGfxObjIds); - ArgumentNullException.ThrowIfNull(exactRecord); + ArgumentNullException.ThrowIfNull(expectedEntity); if (spawn.Position is not { } position) return null; - if (spawn.Guid != exactRecord.ServerGuid - || spawn.InstanceSequence != exactRecord.Generation - || !ReferenceEquals(exactRecord.WorldEntity, entity)) + if (spawn.Guid != expectedServerGuid + || spawn.InstanceSequence != expectedGeneration + || !ReferenceEquals(expectedEntity, entity)) { throw new InvalidOperationException( "Live collision construction requires the exact materialized record."); @@ -164,7 +203,7 @@ internal sealed class LiveEntityCollisionBuilder entity.Position, entity.Rotation, shapes, - (uint)exactRecord.FinalPhysicsState, + (uint)expectedFinalPhysicsState, flags, worldOrigin.X, worldOrigin.Y, diff --git a/src/AcDream.App/Physics/LiveEntityDefaultPoseResolver.cs b/src/AcDream.Runtime/Physics/LiveEntityDefaultPoseResolver.cs similarity index 85% rename from src/AcDream.App/Physics/LiveEntityDefaultPoseResolver.cs rename to src/AcDream.Runtime/Physics/LiveEntityDefaultPoseResolver.cs index 40d9a74f..32a31926 100644 --- a/src/AcDream.App/Physics/LiveEntityDefaultPoseResolver.cs +++ b/src/AcDream.Runtime/Physics/LiveEntityDefaultPoseResolver.cs @@ -3,7 +3,7 @@ using AcDream.Core.Physics.Motion; using DatReaderWriter.DBObjs; using DatReaderWriter.Types; -namespace AcDream.App.Physics; +namespace AcDream.Runtime.Physics; /// /// Resolves the motion table's authored default-state part pose used by a @@ -13,6 +13,12 @@ namespace AcDream.App.Physics; /// The retained default-pose snapshot approximation is tracked as AP-84 in /// the retail divergence register until live per-frame collision poses land. /// +/// +/// #330 hoist (2026-08-07): moved from AcDream.App.Physics to +/// AcDream.Runtime.Physics so a no-window host can build the same +/// collision shapes a graphical host does. No behaviour change - this class +/// was already presentation-free (DAT/physics-only dependencies). +/// internal sealed class LiveEntityDefaultPoseResolver { private readonly Func _loadMotionTable; diff --git a/tests/AcDream.App.Tests/Composition/ContentEffectsAudioCompositionTests.cs b/tests/AcDream.App.Tests/Composition/ContentEffectsAudioCompositionTests.cs index ff2c6b0f..0b5aaede 100644 --- a/tests/AcDream.App.Tests/Composition/ContentEffectsAudioCompositionTests.cs +++ b/tests/AcDream.App.Tests/Composition/ContentEffectsAudioCompositionTests.cs @@ -16,6 +16,7 @@ using AcDream.Core.Rendering; using AcDream.Core.Spells; using AcDream.Core.Vfx; using AcDream.Runtime.Gameplay; +using AcDream.Runtime.Physics; using DatReaderWriter.DBObjs; using Silk.NET.Input; using Silk.NET.OpenAL; diff --git a/tests/AcDream.App.Tests/Physics/LiveEntityCollisionBuilderTests.cs b/tests/AcDream.App.Tests/Physics/LiveEntityCollisionBuilderTests.cs index 320034d5..b104f751 100644 --- a/tests/AcDream.App.Tests/Physics/LiveEntityCollisionBuilderTests.cs +++ b/tests/AcDream.App.Tests/Physics/LiveEntityCollisionBuilderTests.cs @@ -1,11 +1,11 @@ using System.Numerics; -using AcDream.App.Physics; using AcDream.App.World; using AcDream.Core.Items; using AcDream.Core.Net; using AcDream.Core.Net.Messages; using AcDream.Core.Physics; using AcDream.Core.World; +using AcDream.Runtime.Physics; using DatReaderWriter.DBObjs; using DatReaderWriter.Types; @@ -55,7 +55,16 @@ public sealed class LiveEntityCollisionBuilderTests var builder = Builder(); LiveEntityCollisionRegistration registration = Assert.IsType( - builder.Build(entity, setup, Array.Empty(), spawn, record, new Vector3(192f, -192f, 0f))); + builder.Build( + entity, + setup, + Array.Empty(), + spawn, + record.ServerGuid, + record.Generation, + record.WorldEntity!, + record.FinalPhysicsState, + new Vector3(192f, -192f, 0f))); ShadowShape shape = Assert.Single(registration.Shapes); Assert.Equal(ShadowCollisionType.Cylinder, shape.CollisionType); @@ -94,7 +103,10 @@ public sealed class LiveEntityCollisionBuilderTests setup, Array.Empty(), spawn, - record, + record.ServerGuid, + record.Generation, + record.WorldEntity!, + record.FinalPhysicsState, new Vector3(192f, -192f, 0f))); } @@ -125,7 +137,16 @@ public sealed class LiveEntityCollisionBuilderTests PoseResolver()); LiveEntityCollisionRegistration registration = Assert.IsType( - builder.Build(entity, setup, [part], spawn, record, Vector3.Zero)); + builder.Build( + entity, + setup, + [part], + spawn, + record.ServerGuid, + record.Generation, + record.WorldEntity!, + record.FinalPhysicsState, + Vector3.Zero)); ShadowShape shape = Assert.Single(registration.Shapes); Assert.Equal(ShadowCollisionType.BSP, shape.CollisionType); @@ -168,7 +189,15 @@ public sealed class LiveEntityCollisionBuilderTests LiveEntityCollisionRegistration registration = Assert.IsType(builder.Build( - entity, setup, [part], spawn, record, Vector3.Zero)); + entity, + setup, + [part], + spawn, + record.ServerGuid, + record.Generation, + record.WorldEntity!, + record.FinalPhysicsState, + Vector3.Zero)); ShadowShape shape = Assert.Single(registration.Shapes); Assert.Equal(ShadowCollisionType.BSP, shape.CollisionType); @@ -197,7 +226,10 @@ public sealed class LiveEntityCollisionBuilderTests setup, [replacement], spawn, - record, + record.ServerGuid, + record.Generation, + record.WorldEntity!, + record.FinalPhysicsState, Vector3.Zero)); } @@ -222,7 +254,10 @@ public sealed class LiveEntityCollisionBuilderTests setup, [replacement], spawn, - record, + record.ServerGuid, + record.Generation, + record.WorldEntity!, + record.FinalPhysicsState, Vector3.Zero)); ShadowShape shape = Assert.Single(registration.Shapes); @@ -246,12 +281,28 @@ public sealed class LiveEntityCollisionBuilderTests PoseResolver()); LiveEntityCollisionRegistration initial = Assert.IsType(builder.Build( - entity, setup, [basePart], spawn, record, Vector3.Zero)); + entity, + setup, + [basePart], + spawn, + record.ServerGuid, + record.Generation, + record.WorldEntity!, + record.FinalPhysicsState, + Vector3.Zero)); var registry = new ShadowObjectRegistry(); LiveEntityCollisionBuilder.Register(registry, initial); LiveEntityCollisionRegistration? changed = builder.Build( - entity, setup, [replacement], spawn, record, Vector3.Zero, + entity, + setup, + [replacement], + spawn, + record.ServerGuid, + record.Generation, + record.WorldEntity!, + record.FinalPhysicsState, + Vector3.Zero, retainEmptyPayload: true); LiveEntityCollisionBuilder.ReconcileAppearance( registry, entity.Id, changed, suspendIfNew: false); @@ -278,13 +329,29 @@ public sealed class LiveEntityCollisionBuilderTests PoseResolver()); LiveEntityCollisionRegistration initial = Assert.IsType(builder.Build( - entity, setup, [basePart], spawn, record, Vector3.Zero)); + entity, + setup, + [basePart], + spawn, + record.ServerGuid, + record.Generation, + record.WorldEntity!, + record.FinalPhysicsState, + Vector3.Zero)); var registry = new ShadowObjectRegistry(); LiveEntityCollisionBuilder.Register(registry, initial); Assert.True(registry.Suspend(entity.Id)); LiveEntityCollisionRegistration changed = Assert.IsType(builder.Build( - entity, setup, [replacement], spawn, record, Vector3.Zero)); + entity, + setup, + [replacement], + spawn, + record.ServerGuid, + record.Generation, + record.WorldEntity!, + record.FinalPhysicsState, + Vector3.Zero)); LiveEntityCollisionBuilder.ReconcileAppearance( registry, entity.Id, changed, suspendIfNew: true); @@ -301,7 +368,10 @@ public sealed class LiveEntityCollisionBuilderTests setup, [0x0100AC13u], spawn, - record, + record.ServerGuid, + record.Generation, + record.WorldEntity!, + record.FinalPhysicsState, Vector3.Zero, retainEmptyPayload: true)); LiveEntityCollisionBuilder.ReconcileAppearance( @@ -342,12 +412,28 @@ public sealed class LiveEntityCollisionBuilderTests PoseResolver()); LiveEntityCollisionRegistration initial = Assert.IsType(builder.Build( - entity, setup, [basePart], spawn, record, Vector3.Zero)); + entity, + setup, + [basePart], + spawn, + record.ServerGuid, + record.Generation, + record.WorldEntity!, + record.FinalPhysicsState, + Vector3.Zero)); var registry = new ShadowObjectRegistry(); LiveEntityCollisionBuilder.Register(registry, initial); LiveEntityCollisionRegistration changed = Assert.IsType(builder.Build( - entity, setup, [replacement], spawn, record, Vector3.Zero)) + entity, + setup, + [replacement], + spawn, + record.ServerGuid, + record.Generation, + record.WorldEntity!, + record.FinalPhysicsState, + Vector3.Zero)) with { LandblockId = 0u, @@ -376,7 +462,10 @@ public sealed class LiveEntityCollisionBuilderTests new Setup(), Array.Empty(), spawn, - record, + record.ServerGuid, + record.Generation, + record.WorldEntity!, + record.FinalPhysicsState, Vector3.Zero)); } @@ -396,7 +485,10 @@ public sealed class LiveEntityCollisionBuilderTests new Setup { Radius = 1f }, Array.Empty(), spawn, - other, + other.ServerGuid, + other.Generation, + other.WorldEntity!, + other.FinalPhysicsState, Vector3.Zero)); } diff --git a/tests/AcDream.App.Tests/Physics/PvpBitfieldSurvivesAppearanceRebuildTests.cs b/tests/AcDream.App.Tests/Physics/PvpBitfieldSurvivesAppearanceRebuildTests.cs index 40136a30..ca00278b 100644 --- a/tests/AcDream.App.Tests/Physics/PvpBitfieldSurvivesAppearanceRebuildTests.cs +++ b/tests/AcDream.App.Tests/Physics/PvpBitfieldSurvivesAppearanceRebuildTests.cs @@ -1,5 +1,4 @@ using System.Numerics; -using AcDream.App.Physics; using AcDream.App.Streaming; using AcDream.App.World; using AcDream.Core.Items; @@ -9,6 +8,7 @@ using AcDream.Core.Physics; using AcDream.Core.World; using AcDream.Runtime; using AcDream.Runtime.Entities; +using AcDream.Runtime.Physics; using DatReaderWriter.DBObjs; namespace AcDream.App.Tests.Physics; @@ -155,7 +155,16 @@ public sealed class PvpBitfieldSurvivesAppearanceRebuildTests // Initial shadow registration, mirroring CreateObject-time behavior. LiveEntityCollisionRegistration initial = Assert.IsType( - builder.Build(entity, setup, [], record.Snapshot, record, Vector3.Zero)); + builder.Build( + entity, + setup, + [], + record.Snapshot, + record.ServerGuid, + record.Generation, + record.WorldEntity!, + record.FinalPhysicsState, + Vector3.Zero)); LiveEntityCollisionBuilder.Register(registry, initial); ShadowEntry beforeObjDesc = Assert.Single(registry.GetObjectsInCell(Cell)); Assert.True(beforeObjDesc.Flags.HasFlag(EntityCollisionFlags.IsPKLite)); @@ -184,7 +193,16 @@ public sealed class PvpBitfieldSurvivesAppearanceRebuildTests // LiveEntityAppearanceBinding.PrepareCollision/CommitCollision -> // LiveEntityCollisionBuilder.ReconcileAppearance). LiveEntityCollisionRegistration rebuilt = Assert.IsType( - builder.Build(entity, setup, [], accepted, record, Vector3.Zero)); + builder.Build( + entity, + setup, + [], + accepted, + record.ServerGuid, + record.Generation, + record.WorldEntity!, + record.FinalPhysicsState, + Vector3.Zero)); LiveEntityCollisionBuilder.ReconcileAppearance( registry, entity.Id, rebuilt, suspendIfNew: false); diff --git a/tests/AcDream.App.Tests/Rendering/LiveAppearanceAnimationTests.cs b/tests/AcDream.App.Tests/Rendering/LiveAppearanceAnimationTests.cs index f0d53ba3..99187874 100644 --- a/tests/AcDream.App.Tests/Rendering/LiveAppearanceAnimationTests.cs +++ b/tests/AcDream.App.Tests/Rendering/LiveAppearanceAnimationTests.cs @@ -1,12 +1,12 @@ using System.Numerics; using AcDream.App.Rendering; -using AcDream.App.Physics; using AcDream.App.Streaming; using AcDream.App.World; using AcDream.Core.Net; using AcDream.Core.Net.Messages; using AcDream.Core.Physics; using AcDream.Core.World; +using AcDream.Runtime.Physics; using DatReaderWriter.DBObjs; namespace AcDream.App.Tests.Rendering; diff --git a/tests/AcDream.App.Tests/World/GameWindowLiveEntityCompositionTests.cs b/tests/AcDream.App.Tests/World/GameWindowLiveEntityCompositionTests.cs index 46cae8f9..066d7ab5 100644 --- a/tests/AcDream.App.Tests/World/GameWindowLiveEntityCompositionTests.cs +++ b/tests/AcDream.App.Tests/World/GameWindowLiveEntityCompositionTests.cs @@ -6,6 +6,7 @@ using AcDream.App.World; using AcDream.Core.Net; using AcDream.Core.Physics; using AcDream.Core.World; +using AcDream.Runtime.Physics; namespace AcDream.App.Tests.World;