using System.Numerics; using AcDream.App.Input; using AcDream.App.Physics; using AcDream.App.Rendering; using AcDream.App.Rendering.Vfx; using AcDream.App.Streaming; using AcDream.App.World; using AcDream.Core.Plugins; using AcDream.Core.Items; using AcDream.Core.Net; using AcDream.Core.Net.Messages; using AcDream.Core.Physics; using AcDream.Core.World; using AcDream.Runtime.Entities; using AcDream.Runtime.World; using DatReaderWriter.DBObjs; namespace AcDream.App.Tests.World; /// /// C3c contract integration tests: the flipped graphical host wiring driven /// end-to-end — registration through , /// the REAL behind the REAL /// , /// and the production /// pump. /// No conductor is ever hand-called. /// public sealed class RuntimeFirstEntryHostIntegrationTests { private const uint Cell = 0x01010001u; private const uint Guid = 0x70000301u; [Fact] public void InitialCreate_ResidenceConductorReceipt_BindsWorldVisibilityExactlyOnce() { using var fixture = new HostFixture(playerGuid: 0u); int residencesBegan = 0; fixture.EntityObjects.BindInitialResidenceBeginNotification( _ => residencesBegan++); bool visibleAtMaterialize = true; fixture.Materializer.AfterMaterialize = record => { // Clause 1: the sidecar exists but presentation stays suppressed // until the conductor's completion receipt binds it. visibleAtMaterialize = record.IsSpatiallyProjected || record.IsSpatiallyVisible; }; fixture.Controller.OnCreate(Spawn(Guid, Cell)); Assert.Equal(1, residencesBegan); Assert.False(visibleAtMaterialize); Assert.True(fixture.Runtime.TryGetRecord(Guid, out LiveEntityRecord record)); // The residence was consumed by the conductor inside the Create // transaction's own pump. Assert.False(fixture.Runtime.HasActiveInitialCreateResidence( record.Canonical)); Assert.Equal(Cell, record.Canonical.FullCellId); Assert.True(record.IsSpatiallyProjected); Assert.True(record.IsSpatiallyVisible); Assert.NotNull(record.PhysicsBody); Assert.Equal((record, true), Assert.Single(fixture.VisibilityEdges)); AcDream.Plugin.Abstractions.WorldEntitySnapshot snapshot = Assert.Single(fixture.WorldState.Entities); Assert.Equal(record.WorldEntity!.Position, snapshot.Position); Assert.Equal(0, fixture.EntityObjects.Placements.PendingCount); Assert.Equal(0, fixture.FirstEntry.PendingCount); } [Fact] public void DeferredParentCreate_StaysInvisibleUntilParentReplay() { const uint parentGuid = 0x70000302u; const uint childGuid = 0x70000303u; using var fixture = new HostFixture(playerGuid: 0u); int residencesBegan = 0; fixture.EntityObjects.BindInitialResidenceBeginNotification( _ => residencesBegan++); fixture.Controller.OnCreate(ParentedSpawn(childGuid, parentGuid)); // Retail queues the raw blob under the parent's GUID; nothing about // the child may escape — no canonical, no sidecar, no presentation. Assert.Equal(0, residencesBegan); Assert.False(fixture.Runtime.TryGetCanonical(childGuid, out _)); Assert.False(fixture.Runtime.TryGetRecord(childGuid, out _)); Assert.True(fixture.Runtime.ParentAttachments.ContainsDeferredCreate( childGuid, instanceSequence: 1)); Assert.Empty(fixture.WorldState.Entities); Assert.Empty(fixture.VisibilityEdges); fixture.Controller.OnCreate(Spawn(parentGuid, Cell)); // The replayed child's residence was recorded mid-drain; the next // frame's pump (the per-frame retry phase) drives its conductor. fixture.FirstEntry.DriveAll(); Assert.Equal(2, residencesBegan); Assert.False(fixture.Runtime.ParentAttachments.ContainsDeferredCreate( childGuid, instanceSequence: 1)); Assert.True(fixture.Runtime.TryGetCanonical( childGuid, out RuntimeEntityRecord child)); Assert.False(fixture.Runtime.HasActiveInitialCreateResidence(child)); Assert.Equal(0, fixture.FirstEntry.PendingCount); // The parented child is celless and presentation-suppressed until its // own attach/position flow — only the parent is world-visible. Assert.Equal(0u, child.FullCellId); Assert.False(fixture.Runtime.TryGetRecord(childGuid, out _)); Assert.True(fixture.Runtime.TryGetRecord( parentGuid, out LiveEntityRecord parent)); Assert.True(parent.IsSpatiallyVisible); Assert.Single(fixture.WorldState.Entities); } [Fact] public void LocalLogin_PresentationAttachFailure_RetriesWithoutRuntimeRollback() { using var fixture = new HostFixture(playerGuid: Guid); // The camera/shadow-analog App attach failure: the first // world-visibility binding throws AFTER Runtime committed the // controller/body/placement. fixture.VisibilityFailuresRemaining = 1; fixture.Controller.OnCreate(Spawn(Guid, Cell)); // Runtime is NOT rolled back by the App-side presentation failure: // the published movement controller, canonical body, and committed // cell all survive; only the completion receipt stays pending for // the per-frame retry. AcDream.Runtime.Gameplay.PlayerMovementController controller = Assert.IsType( fixture.Movement.Controller); Assert.True(controller.IsRuntimePublished); Assert.True(fixture.Runtime.TryGetRecord(Guid, out LiveEntityRecord record)); Assert.Equal(Cell, record.Canonical.FullCellId); Assert.NotNull(record.PhysicsBody); Assert.False(fixture.Runtime.HasActiveInitialCreateResidence( record.Canonical)); Assert.Equal(1, fixture.EntityObjects.Placements.PendingCount); Assert.Equal((record, true), Assert.Single(fixture.VisibilityEdges)); Assert.True(fixture.Subscription.RetryPending()); Assert.Same(controller, fixture.Movement.Controller); Assert.True(controller.IsRuntimePublished); Assert.Equal(0, fixture.EntityObjects.Placements.PendingCount); Assert.True(record.IsSpatiallyProjected); Assert.True(record.IsSpatiallyVisible); Assert.Equal(2, fixture.VisibilityEdges.Count); } /// /// C3c-F5: through the REAL flipped host wiring (hydration -> /// residence -> conductor -> publication -> dormant activation), a /// login onto flat ground must complete with retail's /// first-gravity-frame contact (SmartBox::HandleCreateObject 0x00454C80 /// -> init_player 0x00455010 -> CPhysicsObj::enter_world 0x00516170 + /// the first simulated frame's touch, compressed via the shared #270 /// settle) — and the outbound motion snapshot must report grounded, /// the exact bit LocalPlayerOutboundController serializes and ACE's /// "You can't do that while in the air!" gate reads. Spawn feet at 5 /// over a flat floor at 4.7 (0.3 m inside the settle reach), with the /// production human bottom-sphere origin so the authored placement /// stands clear of the floor. /// [Fact] public void LocalLogin_FlatGround_ReportsGroundedOutboundContactBit() { using var fixture = new HostFixture( playerGuid: Guid, terrainHeight: 4.7f, moverSphereOriginZ: 0.475f); fixture.Controller.OnCreate(Spawn(Guid, Cell)); AcDream.Runtime.Gameplay.PlayerMovementController controller = Assert.IsType( fixture.Movement.Controller); Assert.True(controller.IsRuntimePublished); Assert.True(fixture.Runtime.TryGetRecord( Guid, out LiveEntityRecord record)); PhysicsBody body = Assert.IsType(record.PhysicsBody); Assert.True(body.InWorld); Assert.True(body.InContact); Assert.True(body.OnWalkable); Assert.True(body.ContactPlaneValid); Assert.InRange(body.Position.Z, 4.65f, 4.76f); Assert.True(controller.CanSendPositionEvent); Assert.True(controller.CaptureMovementResult( mouseLookEvent: false).IsOnGround); Assert.Equal(0, fixture.FirstEntry.PendingCount); } /// /// C3c-F5 counterpart through the same real wiring: a login spawn with /// no floor within the settle's reach stays genuinely airborne — no /// forced grounding anywhere in the first-entry sequence. /// [Fact] public void LocalLogin_AirborneSpawn_StaysGenuinelyAirborne() { using var fixture = new HostFixture( playerGuid: Guid, moverSphereOriginZ: 0.475f); fixture.Controller.OnCreate(Spawn(Guid, Cell)); AcDream.Runtime.Gameplay.PlayerMovementController controller = Assert.IsType( fixture.Movement.Controller); Assert.True(controller.IsRuntimePublished); Assert.True(fixture.Runtime.TryGetRecord( Guid, out LiveEntityRecord record)); PhysicsBody body = Assert.IsType(record.PhysicsBody); Assert.True(body.InWorld); Assert.False(body.InContact); Assert.False(body.OnWalkable); Assert.False(controller.CanSendPositionEvent); Assert.False(controller.CaptureMovementResult( mouseLookEvent: false).IsOnGround); } [Fact] public void GraphicalAndDirectHosts_CommitIdenticalFirstEntryRuntimeFacts() { // Graphical host: full flipped wiring. using var graphical = new HostFixture(playerGuid: 0u); graphical.Controller.OnCreate(Spawn(Guid, Cell)); Assert.True(graphical.Runtime.TryGetCanonical( Guid, out RuntimeEntityRecord graphicalRecord)); // Direct (no-window) host: the same canonical machinery with no // presentation at all — registration, drive pump, ack-only // subscription (the headless host shape). LiveEntityRuntimeFixture.DrivenLiveEntityRuntime direct = LiveEntityRuntimeFixture.CreateDriven( new GpuWorldState(), new NoopResources()); RuntimeEntityRecord directRecord = Assert.IsType( direct.Lifetime.RegisterEntityWithInitialResidence( Spawn(Guid, Cell), isLocalPlayer: false).Canonical); Assert.True(direct.Lifetime.ApplyAcceptedSpawn( directRecord, directRecord.CreateIntegrationVersion, directRecord.Snapshot, replaceGeneration: false)); direct.Pump(); Assert.Equal( FirstEntryFacts.Capture(graphicalRecord), FirstEntryFacts.Capture(directRecord)); Assert.False(graphical.Runtime.HasActiveInitialCreateResidence( graphicalRecord)); Assert.Equal(0, direct.FirstEntry.PendingCount); Assert.Equal(0, graphical.FirstEntry.PendingCount); } private readonly record struct FirstEntryFacts( uint ServerGuid, ushort Incarnation, uint? LocalEntityId, uint FullCellId, uint CanonicalLandblockId, ulong PositionAuthorityVersion, ulong PlacementCommitVersion, ulong CreateIntegrationVersion, ushort SnapshotPositionSequence, bool HasBody, Vector3 BodyPosition, Quaternion BodyOrientation, PhysicsStateFlags BodyState, bool BodyInWorld) { internal static FirstEntryFacts Capture(RuntimeEntityRecord record) => new( record.ServerGuid, record.Incarnation, record.LocalEntityId, record.FullCellId, record.CanonicalLandblockId, record.PositionAuthorityVersion, record.PlacementCommitVersion, record.CreateIntegrationVersion, record.Snapshot.PositionSequence, record.PhysicsBody is not null, record.PhysicsBody?.Position ?? default, record.PhysicsBody?.Orientation ?? default, record.PhysicsBody?.State ?? default, record.PhysicsBody?.InWorld ?? false); } private static WorldSession.EntitySpawn Spawn(uint guid, uint cell) { var position = new CreateObject.ServerPosition( cell, 10f, 10f, 5f, 1f, 0f, 0f, 0f); var timestamps = new PhysicsTimestamps( Position: 1, Movement: 1, State: 1, Vector: 1, Teleport: 0, ServerControlledMove: 1, ForcePosition: 0, ObjDesc: 1, Instance: 1); var physics = new PhysicsSpawnData( RawState: (uint)PhysicsStateFlags.ReportCollisions, Position: position, Movement: null, AnimationFrame: null, SetupTableId: 0x02000001u, MotionTableId: 0x09000001u, SoundTableId: null, PhysicsScriptTableId: null, Parent: null, Children: null, Scale: null, Friction: null, Elasticity: null, Translucency: null, Velocity: null, Acceleration: null, AngularVelocity: null, DefaultScriptType: null, DefaultScriptIntensity: null, Timestamps: timestamps); return new WorldSession.EntitySpawn( guid, position, 0x02000001u, [], [], [], null, null, "first entry", (uint)ItemType.Creature, null, 0x09000001u, PhysicsState: (uint)PhysicsStateFlags.ReportCollisions, InstanceSequence: 1, MovementSequence: 1, ServerControlSequence: 1, PositionSequence: 1, Physics: physics); } private static WorldSession.EntitySpawn ParentedSpawn( uint guid, uint parentGuid) { var timestamps = new PhysicsTimestamps( Position: 1, Movement: 1, State: 1, Vector: 1, Teleport: 0, ServerControlledMove: 1, ForcePosition: 0, ObjDesc: 1, Instance: 1); var physics = new PhysicsSpawnData( RawState: (uint)PhysicsStateFlags.ReportCollisions, Position: null, Movement: null, AnimationFrame: 1u, SetupTableId: 0x02000001u, MotionTableId: 0x09000001u, SoundTableId: null, PhysicsScriptTableId: null, Parent: new PhysicsAttachment(parentGuid, LocationId: 1u), Children: null, Scale: null, Friction: null, Elasticity: null, Translucency: null, Velocity: null, Acceleration: null, AngularVelocity: null, DefaultScriptType: null, DefaultScriptIntensity: null, Timestamps: timestamps); return new WorldSession.EntitySpawn( guid, Position: null, SetupTableId: 0x02000001u, AnimPartChanges: [], TextureChanges: [], SubPalettes: [], BasePaletteId: null, ObjScale: null, Name: "deferred child", ItemType: (uint)ItemType.Creature, MotionState: null, MotionTableId: 0x09000001u, PhysicsState: (uint)PhysicsStateFlags.ReportCollisions, InstanceSequence: 1, MovementSequence: 1, ServerControlSequence: 1, PositionSequence: 1, ParentGuid: parentGuid, ParentLocation: 1u, PlacementId: 1u, Physics: physics); } private sealed class HostFixture : IDisposable { internal readonly RuntimeEntityObjectLifetime EntityObjects = new(); internal readonly LiveEntityRuntime Runtime; internal readonly LiveEntityHydrationController Controller; internal readonly HostMaterializer Materializer; internal readonly AcDream.Runtime.Session.RuntimeFirstEntryDriveController FirstEntry; internal readonly AcDream.Runtime.Physics .RuntimePlacementProjectionSubscription Subscription; internal readonly AcDream.Runtime.Gameplay.RuntimeLocalPlayerMovementState Movement; internal readonly WorldGameState WorldState = new(); internal readonly List<(LiveEntityRecord Record, bool Visible)> VisibilityEdges = []; internal int VisibilityFailuresRemaining; internal HostFixture( uint playerGuid, float terrainHeight = 0f, float moverSphereOriginZ = 0f) { EntityObjects.BindEventContext( static () => new AcDream.Runtime.RuntimeGenerationToken(1UL), static () => 1UL); EntityObjects.Physics.SetPosition.BeginCollisionGeneration( Cell & 0xFFFF0000u, 1UL); EntityObjects.Physics.Engine.AddLandblock( Cell & 0xFFFF0000u, new TerrainSurface( new byte[81], Enumerable.Repeat(terrainHeight, 256).ToArray()), Array.Empty(), Array.Empty(), worldOffsetX: 0f, worldOffsetY: 0f); EntityObjects.Physics.SetPosition.CommitCollisionGeneration( Cell & 0xFFFF0000u, 1UL, ready: true); // 670f307c: a first-entry conductor resolves its landblock-local // Create origin through Runtime's world frame // (RuntimeSetPositionState.PrepareMover:1526-1544) and parks on // RetrySetupUnavailable until it exists. Production publishes it // from the accepted local-player Create // (RuntimeEntityObjectLifetime.RegisterEntityCore:558-570). The // playerGuid: 0 cases below deliberately model a REMOTE entity, // so the frame must come from the ambient world here exactly as // it does in LiveEntityRuntimeFixture — otherwise the graphical // and direct hosts under comparison would differ only by a // missing precondition. RuntimeWorldFrameTests covers the real // local-player ordering. EntityObjects.Physics.ObserveLocalWorldFrame( Cell, teleportAdvanced: false); Movement = new AcDream.Runtime.Gameplay .RuntimeLocalPlayerMovementState(); var runtimeIdentity = new AcDream.Runtime.Gameplay .RuntimeLocalPlayerIdentityState(); var publication = new AcDream.Runtime.Gameplay .RuntimeLocalPlayerPhysicsPublicationState( EntityObjects.Entities, EntityObjects.Physics, Movement, runtimeIdentity); Movement.AttachPhysicsPublication(publication); EntityObjects.LocalPlayerFirstEntry.BindPublication(publication); runtimeIdentity.ServerGuid = playerGuid; var spatial = new GpuWorldState(); spatial.AddLandblock(new LoadedLandblock( (Cell & 0xFFFF0000u) | 0xFFFFu, new LandBlock(), Array.Empty())); Runtime = new LiveEntityRuntime( spatial, new NoopResources(), EntityObjects); FirstEntry = new AcDream.Runtime.Session .RuntimeFirstEntryDriveController( EntityObjects, new AcDream.Runtime.GameRuntimeClock(), new SphereCollisionSource(moverSphereOriginZ), static () => AcDream.Runtime.Gameplay .PlayerMovementConstructionOptions.Fallback, static _ => new AcDream.Runtime.Gameplay .RuntimeLocalPlayerPhysicsActivationPreparation( 0.48f, 1.835f, AcDream.Runtime.Gameplay .RuntimeLocalPlayerShadowDisposition .ProvenShapeless)); var localShadowState = new LocalPlayerShadowState(); var localShadowIdentity = new LocalPlayerIdentityState { ServerGuid = playerGuid, }; var localShadowOrigin = new LiveWorldOriginState(); localShadowOrigin.SetPlaceholder(0, 0); var localShadowSynchronizer = new LocalPlayerShadowSynchronizer( EntityObjects.Physics.Engine, Runtime, localShadowIdentity, localShadowOrigin, localShadowState); var sink = new RuntimePlacementPresentationSink( Runtime, new RuntimeWorldTransitState(), WorldState, new WorldEvents(), new EntityEffectPoseRegistry(), localShadowSynchronizer, () => playerGuid, _ => { }, [ (record, visible) => { VisibilityEdges.Add((record, visible)); if (VisibilityFailuresRemaining > 0) { VisibilityFailuresRemaining--; throw new InvalidOperationException( "fixture presentation attach failure"); } }, ]); Subscription = new AcDream.Runtime.Physics .RuntimePlacementProjectionSubscription( EntityObjects.Placements, static () => new AcDream.Runtime.RuntimeGenerationToken(1UL), sink); Materializer = new HostMaterializer(Runtime); var identity = new LocalPlayerIdentityState { ServerGuid = playerGuid, }; var dormant = new DormantLiveEntityStore(); var teardown = new NoopTeardown(); var deletion = new LiveEntityDeletionController( Runtime, EntityObjects, teardown, identity, dormant); Controller = new LiveEntityHydrationController( Runtime, EntityObjects, new object(), Materializer, new NoopRelationships(), new AcceptingReady(), new KnownOrigin(), new NoopNetworkSink(), new NoopTimestamps(), identity, deletion, dormant, firstEntry: FirstEntry); } public void Dispose() { try { Runtime.Clear(); } catch { // Failure-path tests assert their own exceptions. } } } /// /// Mirrors the production materializer /// (DatLiveEntityProjectionMaterializer.MaterializeProjection): route-1 /// world creates materialize residence-managed and self-project only when /// the committed cell already exists with no active residence. /// private sealed class HostMaterializer(LiveEntityRuntime runtime) : ILiveEntityProjectionMaterializer { internal Action? AfterMaterialize { get; set; } public bool TryMaterialize( RuntimeEntityRecord expectedCanonical, WorldSession.EntitySpawn canonicalSpawn, LiveProjectionPurpose purpose, ulong expectedCreateIntegrationVersion, AcDream.App.Rendering.LiveEntityAppearanceUpdateState? appearanceUpdate = null) { if (canonicalSpawn.Position is not { } position || canonicalSpawn.SetupTableId is null) { return false; } WorldEntity? entity = runtime.MaterializeLiveEntity( expectedCanonical, position.LandblockId, id => new WorldEntity { Id = id, ServerGuid = canonicalSpawn.Guid, SourceGfxObjOrSetupId = canonicalSpawn.SetupTableId.Value, Position = new Vector3( position.PositionX, position.PositionY, position.PositionZ), Rotation = Quaternion.Identity, MeshRefs = [], ParentCellId = position.LandblockId, }, LiveEntityProjectionKind.World, initializeProjection: null, out LiveEntityRecord? record, LiveEntityMaterializationResidence.AwaitRuntimePlacement); if (entity is null || record is null) return false; if (runtime.IsCurrentCreateIntegration( expectedCanonical, expectedCreateIntegrationVersion) && expectedCanonical.FullCellId != 0u && !runtime.HasActiveInitialCreateResidence(expectedCanonical) && !runtime.RebucketLiveEntity( canonicalSpawn.Guid, expectedCanonical.FullCellId)) { return false; } AfterMaterialize?.Invoke(record); return runtime.IsCurrentRecord(record); } public void ResetSessionState() { } } private sealed class NoopResources : ILiveEntityResourceLifecycle { public void Register(WorldEntity entity) { } public void Unregister(WorldEntity entity) { } } private sealed class NoopTeardown : ILiveEntityTeardownCoordinator { public void TearDown(LiveEntityRecord record) { } public void ForgetUnknownOwner(uint serverGuid) { } } private sealed class NoopRelationships : ILiveEntityRelationshipProjection { public void OnSpawn(WorldSession.EntitySpawn spawn) { } public void OnParent(ParentEvent.Parsed update) { } public void OnCreateParentAccepted(CreateParentUpdate update) { } public ChildUnparentDisposition OnChildBecameUnparented(uint childGuid) => ChildUnparentDisposition.Completed; public bool TryApplyAttachedAppearance( LiveEntityRecord record, ulong objDescAuthorityVersion) => false; } private sealed class AcceptingReady : ILiveEntityReadyPublisher { public bool Publish(LiveEntityReadyCandidate candidate) => true; } private sealed class KnownOrigin : ILiveEntityWorldOriginCoordinator { public bool IsKnown => true; public LiveEntityOriginInitialization TryInitialize( WorldSession.EntitySpawn spawn) => new(true, []); } private sealed class NoopNetworkSink : ILiveEntityNetworkUpdateSink { public void ApplySameGeneration(SameGenerationCreateObjectEvents events) { } } private sealed class NoopTimestamps : IAcceptedLocalPhysicsTimestampPublisher { public void Publish(uint serverGuid, AcceptedPhysicsTimestamps timestamps) { } } private sealed class SphereCollisionSource(float sphereOriginZ = 0f) : AcDream.Content.IPreparedCollisionSource { public AcDream.Content.PreparedAssetPresence ProbeCollision( AcDream.Content.Pak.PakAssetType type, uint sourceFileId) => AcDream.Content.PreparedAssetPresence.Available; public AcDream.Content.PreparedCollisionReadResult< FlatSetupCollision> ReadSetupCollision( uint sourceFileId, CancellationToken cancellationToken = default) => AcDream.Content.PreparedCollisionReadResult .Loaded(new FlatSetupCollision( System.Collections.Immutable.ImmutableArray< FlatCollisionCylinder>.Empty, [new FlatCollisionSphere( new Vector3(0f, 0f, sphereOriginZ), 0.48f)], height: 0f, radius: 0f, stepUpHeight: 0.4f, stepDownHeight: 0.4f)); public AcDream.Content.PreparedCollisionReadResult< FlatGfxObjCollisionAsset> ReadGfxObjCollision( uint sourceFileId, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public AcDream.Content.PreparedCollisionReadResult< FlatCellStructureCollisionAsset> ReadCellStructureCollision( uint sourceFileId, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public AcDream.Content.PreparedCollisionReadResult< FlatEnvCellTopology> ReadEnvCellTopology( uint sourceFileId, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public AcDream.Content.PreparedCollisionSourceStats CollisionStats => default; public void Dispose() { } } }