using AcDream.App.Streaming;
using AcDream.App.World;
using AcDream.Core.Physics;
using AcDream.Runtime.Entities;
namespace AcDream.App.Tests;
///
/// Explicit isolated lifetime-group seam for App projection tests. Production
/// composition supplies its session-owned group from GameWindow; focused tests
/// use a fresh group per runtime so identity never leaks between fixtures.
///
internal static class LiveEntityRuntimeFixture
{
///
/// C3c: initial-Create registration now begins the canonical residence
/// lease, whose admission requires a live session generation. Focused
/// App fixtures bind a fixed non-zero token exactly like the Runtime
/// conductor fixtures do.
///
private static RuntimeEntityObjectLifetime WithGeneration(
RuntimeEntityObjectLifetime lifetime)
{
lifetime.BindEventContext(
static () => new AcDream.Runtime.RuntimeGenerationToken(1UL),
static () => 1UL);
return lifetime;
}
public static LiveEntityRuntime Create(
GpuWorldState spatial,
ILiveEntityResourceLifecycle resources,
uint firstLocalEntityId = RuntimeEntityDirectory.FirstLocalEntityId)
{
var lifetime = WithGeneration(
new RuntimeEntityObjectLifetime(firstLocalEntityId));
return new LiveEntityRuntime(spatial, resources, lifetime);
}
///
/// C3c: a runtime whose initial-create residences can actually be driven
/// to completion — collision generation committed for
/// , the production
///
/// constructed against the lifetime, and an acknowledge-only placement
/// subscription mirroring the host rules (Discard/ExecutorCompleted
/// acknowledged; Place/Withdraw left at the head for the conductors).
/// Tests exercising post-residence legacy update paths register, then
/// call to complete the
/// conductors exactly like the composed host's Create-transaction pump.
///
internal sealed class DrivenLiveEntityRuntime
{
internal required LiveEntityRuntime Runtime { get; init; }
internal required RuntimeEntityObjectLifetime Lifetime { get; init; }
internal required AcDream.Runtime.Session.RuntimeFirstEntryDriveController
FirstEntry { get; init; }
internal required AcDream.Runtime.Physics
.RuntimePlacementProjectionSubscription Subscription { get; init; }
internal void Pump() => FirstEntry.DriveAll();
}
public static DrivenLiveEntityRuntime CreateDriven(
GpuWorldState spatial,
ILiveEntityResourceLifecycle resources,
uint landblockId = 0x01010000u)
{
var lifetime = WithGeneration(new RuntimeEntityObjectLifetime());
lifetime.Physics.SetPosition.BeginCollisionGeneration(
landblockId & 0xFFFF0000u, 1UL);
lifetime.Physics.Engine.AddLandblock(
landblockId & 0xFFFF0000u,
new AcDream.Core.Physics.TerrainSurface(
new byte[81], new float[256]),
Array.Empty(),
Array.Empty(),
worldOffsetX: 0f,
worldOffsetY: 0f);
lifetime.Physics.SetPosition.CommitCollisionGeneration(
landblockId & 0xFFFF0000u, 1UL, ready: true);
// 670f307c: Create frames are landblock-local, so a first-entry
// conductor resolves them through Runtime's world frame
// (RuntimeSetPositionState.PrepareMover:1526-1544) and parks on
// RetrySetupUnavailable until that frame exists. Only the accepted
// local-player Create publishes it
// (RuntimeEntityObjectLifetime.RegisterEntityCore:558-570 ->
// RuntimePhysicsState.ObserveLocalWorldFrame), and production always
// registers the player before driving any broadcast Create. Model
// that ambient world fact beside the resident landblock above rather
// than registering a second entity, which would consume a local
// entity id and perturb every exact-ledger assertion built on this
// fixture. RuntimeWorldFrameTests covers the real ordering.
lifetime.Physics.ObserveLocalWorldFrame(
(landblockId & 0xFFFF0000u) | 0x0001u,
teleportAdvanced: false);
var runtime = new LiveEntityRuntime(spatial, resources, lifetime);
var movement = new AcDream.Runtime.Gameplay
.RuntimeLocalPlayerMovementState();
var identity = new AcDream.Runtime.Gameplay
.RuntimeLocalPlayerIdentityState();
var publication = new AcDream.Runtime.Gameplay
.RuntimeLocalPlayerPhysicsPublicationState(
lifetime.Entities,
lifetime.Physics,
movement,
identity);
movement.AttachPhysicsPublication(publication);
lifetime.LocalPlayerFirstEntry.BindPublication(publication);
var firstEntry = new AcDream.Runtime.Session
.RuntimeFirstEntryDriveController(
lifetime,
new AcDream.Runtime.GameRuntimeClock(),
new SphereCollisionSource(),
static () => AcDream.Runtime.Gameplay
.PlayerMovementConstructionOptions.Fallback,
static _ => new AcDream.Runtime.Gameplay
.RuntimeLocalPlayerPhysicsActivationPreparation(
0.48f,
1.835f,
AcDream.Runtime.Gameplay
.RuntimeLocalPlayerShadowDisposition
.ProvenShapeless));
var subscription = new AcDream.Runtime.Physics
.RuntimePlacementProjectionSubscription(
lifetime.Placements,
static () => new AcDream.Runtime.RuntimeGenerationToken(1UL),
new AckOnlyPlacementSink(runtime));
return new DrivenLiveEntityRuntime
{
Runtime = runtime,
Lifetime = lifetime,
FirstEntry = firstEntry,
Subscription = subscription,
};
}
private sealed class AckOnlyPlacementSink(LiveEntityRuntime runtime)
: AcDream.Runtime.Physics.IRuntimePlacementProjectionSink
{
public bool TryApply(
in AcDream.Runtime.Physics.RuntimePlacementProjectionSnapshot
projection)
{
if (projection.Kind is AcDream.Runtime.Physics
.RuntimePlacementProjectionKind.Discard)
{
return true;
}
if (projection.Kind is AcDream.Runtime.Physics
.RuntimePlacementProjectionKind.ExecutorCompleted)
{
return projection.Token.ExactCellId == 0u
|| runtime.TryApplyInitialCreateCompletionPresentation(
in projection);
}
return !runtime.HasActiveInitialCreateResidence(
projection.Token.Entity)
&& runtime.TryApplyRuntimePlacementProjection(in projection);
}
}
private sealed class SphereCollisionSource
: AcDream.Content.IPreparedCollisionSource
{
public AcDream.Content.PreparedAssetPresence ProbeCollision(
AcDream.Content.Pak.PakAssetType type,
uint sourceFileId) =>
AcDream.Content.PreparedAssetPresence.Available;
public AcDream.Content.PreparedCollisionReadResult<
AcDream.Core.Physics.FlatSetupCollision> ReadSetupCollision(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
AcDream.Content.PreparedCollisionReadResult<
AcDream.Core.Physics.FlatSetupCollision>.Loaded(
new AcDream.Core.Physics.FlatSetupCollision(
System.Collections.Immutable.ImmutableArray<
AcDream.Core.Physics.FlatCollisionCylinder>.Empty,
[new AcDream.Core.Physics.FlatCollisionSphere(
System.Numerics.Vector3.Zero, 0.48f)],
height: 0f,
radius: 0f,
stepUpHeight: 0.4f,
stepDownHeight: 0.4f));
public AcDream.Content.PreparedCollisionReadResult<
AcDream.Core.Physics.FlatGfxObjCollisionAsset>
ReadGfxObjCollision(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
throw new NotSupportedException();
public AcDream.Content.PreparedCollisionReadResult<
AcDream.Core.Physics.FlatCellStructureCollisionAsset>
ReadCellStructureCollision(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
throw new NotSupportedException();
public AcDream.Content.PreparedCollisionReadResult<
AcDream.Core.Physics.FlatEnvCellTopology> ReadEnvCellTopology(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
throw new NotSupportedException();
public AcDream.Content.PreparedCollisionSourceStats CollisionStats =>
default;
public void Dispose()
{
}
}
public static LiveEntityRuntime Create(
GpuWorldState spatial,
ILiveEntityResourceLifecycle resources,
PhysicsEngine physicsEngine,
uint firstLocalEntityId = RuntimeEntityDirectory.FirstLocalEntityId)
{
var lifetime = WithGeneration(new RuntimeEntityObjectLifetime(
physicsEngine,
firstLocalEntityId));
return new LiveEntityRuntime(spatial, resources, lifetime);
}
public static LiveEntityRuntime Create(
GpuWorldState spatial,
ILiveEntityResourceLifecycle resources,
Action tearDownRuntimeComponents,
uint firstLocalEntityId = RuntimeEntityDirectory.FirstLocalEntityId)
{
var lifetime = WithGeneration(
new RuntimeEntityObjectLifetime(firstLocalEntityId));
return new LiveEntityRuntime(
spatial,
resources,
tearDownRuntimeComponents,
lifetime);
}
public static LiveEntityRuntime Create(
GpuWorldState spatial,
ILiveEntityResourceLifecycle resources,
ILiveEntityRuntimeComponentLifecycle runtimeComponentLifecycle,
uint firstLocalEntityId = RuntimeEntityDirectory.FirstLocalEntityId)
{
var lifetime = WithGeneration(
new RuntimeEntityObjectLifetime(firstLocalEntityId));
return new LiveEntityRuntime(
spatial,
resources,
runtimeComponentLifecycle,
lifetime);
}
public static LiveEntityRuntime Create(
GpuWorldState spatial,
ILiveEntityResourceLifecycle resources,
ILiveEntityRuntimeComponentLifecycle runtimeComponentLifecycle,
PhysicsEngine physicsEngine,
uint firstLocalEntityId = RuntimeEntityDirectory.FirstLocalEntityId)
{
var lifetime = WithGeneration(new RuntimeEntityObjectLifetime(
physicsEngine,
firstLocalEntityId));
return new LiveEntityRuntime(
spatial,
resources,
runtimeComponentLifecycle,
lifetime);
}
}