acdream/tests/AcDream.App.Tests/World/UpdateFrameOrchestratorTests.cs
Erik 9fab1feb46 fix(rendering): publish scene deltas at update boundary
Move the ordinary shadow-scene drain out of the conditional spatial reconciler and into an explicit final update-frame commit phase. This publishes deltas after streaming, network, teleport, camera, and conditional reconciles even while world simulation is quiesced.

Release gate: 3,733 App tests / 3 skips; 8,217 complete-solution tests / 5 skips.

Co-Authored-By: OpenAI Codex <codex@openai.com>
2026-07-24 23:00:31 +02:00

1073 lines
40 KiB
C#

using System.Reflection;
using AcDream.App.Rendering;
using AcDream.App.Streaming;
using AcDream.App.Update;
using AcDream.App.World;
namespace AcDream.App.Tests.World;
public sealed class UpdateFrameOrchestratorTests
{
[Fact]
public void Tick_PreservesTheCompleteAcceptedPhaseGraph()
{
var calls = new List<string>();
UpdateFrameOrchestrator frame = Create(calls);
frame.Tick(new UpdateFrameInput(1.0 / 60.0));
Assert.Equal(
[
"teardown",
"clock",
"streaming",
"input",
"objects",
"network",
"commands",
"ordinary-reconcile",
"liveness",
"teleport",
"auto-entry",
"camera",
"commit",
],
calls);
}
[Fact]
public void ConditionalReconciles_RemainAtTheirOwningEdges()
{
// The outer ownership oracle is complemented by the production
// teleport and camera tests that pin both conditional reconcile edges.
var calls = new List<string>();
UpdateFrameOrchestrator frame = Create(
calls,
teleportPlace: true,
inboundCreatedPlayer: true);
frame.Tick(new UpdateFrameInput(1.0 / 60.0));
Assert.Equal(
[
"teardown",
"clock",
"streaming",
"input",
"objects",
"network",
"commands",
"ordinary-reconcile",
"liveness",
"teleport-place",
"teleport-reconcile",
"teleport-reveal",
"auto-entry",
"inbound-player-projection",
"inbound-player-reconcile",
"camera",
"commit",
],
calls);
}
[Theory]
[InlineData(double.NaN)]
[InlineData(double.NegativeInfinity)]
[InlineData(double.PositiveInfinity)]
[InlineData(-1.0)]
[InlineData(0.0)]
[InlineData(double.MaxValue)]
public void InvalidSimulationDelta_BecomesZeroWithoutSuppressingOuterPhases(
double hostDelta)
{
var calls = new List<string>();
var observed = new FrameObservations();
UpdateFrameOrchestrator frame = Create(calls, observed: observed);
frame.Tick(new UpdateFrameInput(hostDelta));
Assert.Equal(
[
"teardown", "clock", "streaming", "input", "objects",
"network", "commands", "ordinary-reconcile", "liveness",
"teleport", "auto-entry", "camera", "commit",
],
calls);
Assert.Equal([0.0], observed.PublishedTimes);
Assert.Equal([0.0], observed.Input.Select(value => value.SimulationDeltaSeconds));
Assert.Equal([0f], observed.LiveDeltas);
Assert.Equal([0f], observed.TeleportDeltas);
Assert.Equal([0.0], observed.Camera.Select(value => value.SimulationDeltaSeconds));
Assert.All(
observed.Input.Concat(observed.Camera),
timing => Assert.Equal(0.0, timing.ScriptTime));
}
[Fact]
public void Clock_AdvancesOnceAndPublishesTheSameTimeToEveryConsumer()
{
var calls = new List<string>();
var observed = new FrameObservations();
UpdateFrameOrchestrator frame = Create(calls, observed: observed);
frame.Tick(new UpdateFrameInput(0.25));
frame.Tick(new UpdateFrameInput(double.NaN));
frame.Tick(new UpdateFrameInput(0.5));
Assert.Equal([0.25, 0.25, 0.75], observed.PublishedTimes);
Assert.Equal(
[0.25, 0.25, 0.75],
observed.Input.Select(value => value.ScriptTime));
Assert.Equal(
[0.25, 0.25, 0.75],
observed.Camera.Select(value => value.ScriptTime));
Assert.Equal([0.25f, 0f, 0.5f], observed.LiveDeltas);
Assert.Equal([0.25f, 0f, 0.5f], observed.TeleportDeltas);
Assert.Equal(
[0.25, 0.0, 0.5],
observed.Input.Select(value => value.SimulationDeltaSeconds));
Assert.Equal(
[0.25, 0.0, 0.5],
observed.Camera.Select(value => value.SimulationDeltaSeconds));
}
[Fact]
public void QuiescedWorld_FreezesScriptClockAndLivenessWhilePortalPhasesContinue()
{
var calls = new List<string>();
var observed = new FrameObservations();
var availability = new WorldGenerationAvailabilityState();
availability.Begin(7);
UpdateFrameOrchestrator frame = Create(
calls,
observed: observed,
availability: availability);
frame.Tick(new UpdateFrameInput(0.25));
frame.Tick(new UpdateFrameInput(0.25));
availability.End(7);
frame.Tick(new UpdateFrameInput(0.25));
Assert.Equal([0.0, 0.0, 0.25], observed.PublishedTimes);
Assert.Equal(1, calls.Count(call => call == "liveness"));
Assert.Equal(3, calls.Count(call => call == "streaming"));
Assert.Equal(3, calls.Count(call => call == "network"));
Assert.Equal(3, calls.Count(call => call == "teleport"));
Assert.Equal(3, calls.Count(call => call == "camera"));
Assert.Equal(3, calls.Count(call => call == "commit"));
Assert.Equal([0.25f, 0.25f, 0.25f], observed.LiveDeltas);
Assert.Equal([0.25f, 0.25f, 0.25f], observed.TeleportDeltas);
}
[Fact]
public void Clock_RejectsFiniteDeltaAboveTheSinglePrecisionConsumerRange()
{
double tooLarge = (double)float.MaxValue * 2.0;
Assert.True(double.IsFinite(tooLarge));
Assert.Equal(0.0, UpdateFrameClock.NormalizeDeltaSeconds(tooLarge));
}
[Fact]
public void AggregateTeardownFailure_IsReportedAndRetriedNextFrame()
{
var calls = new List<string>();
var teardown = new RecordingTeardown(calls)
{
Failure = new AggregateException(new InvalidOperationException("transient")),
};
var failures = new RecordingFailureSink();
UpdateFrameOrchestrator frame = Create(
calls,
teardown: teardown,
failureSink: failures);
frame.Tick(new UpdateFrameInput(0.1));
teardown.Failure = null;
frame.Tick(new UpdateFrameInput(0.1));
Assert.Equal(2, teardown.Attempts);
Assert.Single(failures.Errors);
string[] oneFrame =
[
"teardown", "clock", "streaming", "input", "objects", "network",
"commands", "ordinary-reconcile", "liveness", "teleport",
"auto-entry", "camera", "commit",
];
Assert.Equal(oneFrame.Concat(oneFrame), calls);
}
[Fact]
public void NonAggregateTeardownFailure_PropagatesBeforeTimeOrLaterPhases()
{
var calls = new List<string>();
var teardown = new RecordingTeardown(calls)
{
Failure = new InvalidOperationException("fatal"),
};
UpdateFrameOrchestrator frame = Create(calls, teardown: teardown);
InvalidOperationException error = Assert.Throws<InvalidOperationException>(
() => frame.Tick(new UpdateFrameInput(0.1)));
Assert.Equal("fatal", error.Message);
Assert.Equal(["teardown"], calls);
}
[Fact]
public void OrchestrationTypes_UseOnlyTheExplicitTypedOwnerGraph()
{
Type[] expectedFieldTypes =
[
typeof(IUpdateFrameTeardownPhase),
typeof(IUpdateFrameFailureSink),
typeof(UpdateFrameClock),
typeof(IUpdateFrameScriptClockPublisher),
typeof(IStreamingFramePhase),
typeof(IGameplayInputFramePhase),
typeof(IRetailLiveFramePhase),
typeof(ILiveEntityLivenessFramePhase),
typeof(ILocalPlayerTeleportFramePhase),
typeof(IPlayerModeAutoEntryFramePhase),
typeof(ICameraFramePhase),
typeof(IUpdateFrameCommitPhase),
typeof(IWorldGenerationAvailability),
];
FieldInfo[] fields = typeof(UpdateFrameOrchestrator).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.Equal(
expectedFieldTypes.OrderBy(type => type.FullName),
fields.Select(field => field.FieldType).OrderBy(type => type.FullName));
Assert.All(fields, field =>
{
Assert.NotEqual(typeof(GameWindow), field.FieldType);
Assert.False(typeof(Delegate).IsAssignableFrom(field.FieldType));
});
Type[] phaseInterfaces = expectedFieldTypes.Where(type => type.IsInterface).ToArray();
Assert.DoesNotContain(
phaseInterfaces,
phaseInterface => phaseInterface.IsAssignableFrom(typeof(GameWindow)));
Type[] productionPhaseOwners = typeof(GameWindow).Assembly.GetTypes()
.Where(type => !type.IsAbstract && !type.IsInterface)
.Where(type => phaseInterfaces.Any(contract => contract.IsAssignableFrom(type)))
.ToArray();
foreach (Type owner in productionPhaseOwners)
{
FieldInfo[] ownerFields = owner.GetFields(
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.DoesNotContain(ownerFields, field => field.FieldType == typeof(GameWindow));
Assert.DoesNotContain(
ownerFields,
field => typeof(Delegate).IsAssignableFrom(field.FieldType));
}
Assert.DoesNotContain(
typeof(GameWindow).GetFields(BindingFlags.Instance | BindingFlags.NonPublic),
field => field.Name == "_physicsScriptGameTime");
}
[Fact]
public void PhysicsScriptClockConsumers_RetainTheTypedSourceNotGameWindowClosures()
{
Type[] consumers =
[
typeof(DatLiveEntityProjectionMaterializer),
typeof(AcDream.App.Physics.LiveEntityNetworkUpdateController),
];
foreach (Type consumer in consumers)
{
FieldInfo clock = Assert.Single(
consumer.GetFields(BindingFlags.Instance | BindingFlags.NonPublic),
field => field.Name == "_gameTime");
Assert.Equal(typeof(IPhysicsScriptTimeSource), clock.FieldType);
Assert.DoesNotContain(
consumer.GetFields(BindingFlags.Instance | BindingFlags.NonPublic),
field => field.FieldType == typeof(GameWindow));
}
Assert.False(typeof(IPhysicsScriptTimeSource).IsAssignableFrom(typeof(GameWindow)));
}
[Fact]
public void ProductionFrame_PublishesPhysicsScriptTimeExactlyOnce()
{
string root = FindRepoRoot();
string source = File.ReadAllText(Path.Combine(
root,
"src",
"AcDream.App",
"Rendering",
"GameWindow.cs"));
string adapters = File.ReadAllText(Path.Combine(
root,
"src",
"AcDream.App",
"Update",
"UpdateFrameRuntimeAdapters.cs"));
string frameRoot = File.ReadAllText(Path.Combine(
root,
"src",
"AcDream.App",
"Composition",
"FrameRootComposition.cs"));
Assert.Equal(0, CountOccurrences(source, "PublishTime("));
Assert.Equal(1, CountOccurrences(adapters, "_runner.PublishTime("));
Assert.Contains("new PhysicsScriptClockPublisher(", frameRoot,
StringComparison.Ordinal);
}
[Fact]
public void ExtractedLiveObjectSource_PinsRetailAndRegisteredAdaptationOrder()
{
string source = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Update",
"LiveObjectFrameController.cs"));
AssertAppearsInOrder(
source,
"_localPlayerFrame.AdvanceBeforeNetwork",
"_selectionInteractions?.DrainOutbound",
"_animations.Tick",
"_staticAnimations.Tick",
"_animationPresenter.Present",
"_equippedChildren.Tick",
"_staticAnimations.ProcessHooks",
"_effects.Tick");
AssertAppearsInOrder(
source,
"_translucencyFades.AdvanceAll",
"_animationHooks.Drain",
"_entityEffects.RefreshLiveOwnerPoses",
"_particleSink.RefreshAttachedEmitters",
"_lights.Refresh",
"_particleVisibility.Apply",
"_particles.Tick",
"_scripts.Tick");
AssertAppearsInOrder(
source,
"public void Reconcile()",
"_entityEffects.RefreshLiveOwnerPoses",
"_equippedChildren.Tick",
"_particleSink.RefreshAttachedEmitters",
"_lights.Refresh");
Assert.Equal(1, CountOccurrences(source, "_staticAnimations.Tick("));
Assert.Equal(1, CountOccurrences(source, "_staticAnimations.ProcessHooks("));
Assert.Equal(1, CountOccurrences(source, "_effects.Tick("));
Assert.Equal(1, CountOccurrences(source, "_particles.Tick("));
Assert.Equal(1, CountOccurrences(source, "_scripts.Tick("));
}
[Fact]
public void GameWindow_ComposesTheLiveFrameOwnersWithoutOwningTheirBodies()
{
string source = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Rendering",
"GameWindow.cs"));
string livePresentation = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"LivePresentationComposition.cs"));
string sessionPlayer = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"SessionPlayerComposition.cs"));
string frameRoot = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"FrameRootComposition.cs"));
Assert.Contains("new LiveObjectFrameController(", sessionPlayer);
Assert.Contains("new LiveSpatialPresentationReconciler(", sessionPlayer);
Assert.Contains("new RetailLiveFrameCoordinator(", frameRoot);
Assert.DoesNotContain("AdvanceLiveObjectRuntime", source, StringComparison.Ordinal);
Assert.DoesNotContain("ReconcileLiveObjectSpatialPresentation", source,
StringComparison.Ordinal);
Assert.DoesNotContain("ILiveAnimationPresentationContext", source,
StringComparison.Ordinal);
}
[Fact]
public void GameWindow_DelegatesTheCompleteStreamingFrameBody()
{
string source = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Rendering",
"GameWindow.cs"));
string sessionPlayer = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"SessionPlayerComposition.cs"));
Assert.Contains("new StreamingFrameController(", sessionPlayer);
Assert.DoesNotContain("_streamingFrame", source, StringComparison.Ordinal);
Assert.Equal(1, CountOccurrences(
source,
"_frameGraphs.Tick("));
Assert.DoesNotContain("_streamingController.Tick(observerCx", source,
StringComparison.Ordinal);
Assert.DoesNotContain("DungeonStreamingGate.Compute", source,
StringComparison.Ordinal);
Assert.DoesNotContain("DrainRescued()", source, StringComparison.Ordinal);
}
[Fact]
public void GameWindow_DelegatesTheCompleteGameplayInputFrameBody()
{
string source = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Rendering",
"GameWindow.cs"));
string sessionPlayer = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"SessionPlayerComposition.cs"));
Assert.Contains("new GameplayInputFrameController(", sessionPlayer);
Assert.DoesNotContain("_gameplayInputFrame!.Tick", source,
StringComparison.Ordinal);
Assert.DoesNotContain("_inputDispatcher?.Tick()", source,
StringComparison.Ordinal);
Assert.DoesNotContain("TryTakeRawSample", source,
StringComparison.Ordinal);
Assert.DoesNotContain("_combatAttackController?.Tick()", source,
StringComparison.Ordinal);
Assert.DoesNotContain("CaptureMovementInput", source,
StringComparison.Ordinal);
Assert.DoesNotContain("EndMouseLookAndRestoreCursor", source,
StringComparison.Ordinal);
Assert.DoesNotContain("HideCursorForMouseLook", source,
StringComparison.Ordinal);
Assert.DoesNotContain("RestoreCursorAfterMouseLook", source,
StringComparison.Ordinal);
Assert.DoesNotContain("CanStartLiveCombatAttack", source,
StringComparison.Ordinal);
Assert.DoesNotContain("SendLiveCombatAttack", source,
StringComparison.Ordinal);
Assert.DoesNotContain("PreparePlayerForAttackRequest", source,
StringComparison.Ordinal);
Assert.DoesNotContain("DumpMovementTruthOutbound", source,
StringComparison.Ordinal);
Assert.DoesNotContain("DumpMovementTruthServerEcho", source,
StringComparison.Ordinal);
Assert.DoesNotContain("wantCaptureMouse: ()", source,
StringComparison.Ordinal);
Assert.DoesNotContain(
"_gameplayInputFrame?.EndMouseLook",
source,
StringComparison.Ordinal);
string pointerSource = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Input",
"CameraPointerInputController.cs"));
Assert.Contains("_gameplayFrame?.EndMouseLook();", pointerSource,
StringComparison.Ordinal);
string teleportSource = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Streaming",
"LocalPlayerTeleportController.cs"));
AssertAppearsInOrder(
teleportSource,
"_transit.CanBegin(teleportSequence)",
"_input.EndMouseLook();",
"_transit.QueueStart(teleportSequence)");
string playerModeSource = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Input",
"PlayerModeController.cs"));
Assert.Contains("_input.EndMouseLook();", playerModeSource,
StringComparison.Ordinal);
AssertAppearsInOrder(
source,
"private void OnFocusChanged(bool focused)",
"_cameraPointerInput?.HandleFocusChanged(focused);");
}
[Fact]
public void GameWindow_DelegatesTheCompleteLocalTeleportLifetime()
{
string root = FindRepoRoot();
string source = File.ReadAllText(Path.Combine(
root,
"src",
"AcDream.App",
"Rendering",
"GameWindow.cs"));
string networkSource = File.ReadAllText(Path.Combine(
root,
"src",
"AcDream.App",
"Physics",
"LiveEntityNetworkUpdateController.cs"));
string sessionPlayer = File.ReadAllText(Path.Combine(
root,
"src",
"AcDream.App",
"Composition",
"SessionPlayerComposition.cs"));
string frameRoot = File.ReadAllText(Path.Combine(
root,
"src",
"AcDream.App",
"Composition",
"FrameRootComposition.cs"));
Assert.Contains(
"new LocalPlayerTeleportController(",
sessionPlayer,
StringComparison.Ordinal);
Assert.DoesNotContain("_localPlayerTeleport!.Tick", source,
StringComparison.Ordinal);
Assert.DoesNotContain("_teleportTransit", source, StringComparison.Ordinal);
Assert.DoesNotContain("_teleportAnim", source, StringComparison.Ordinal);
Assert.DoesNotContain("_teleportViewPlane", source, StringComparison.Ordinal);
Assert.DoesNotContain("_pendingTeleport", source, StringComparison.Ordinal);
Assert.DoesNotContain("AimTeleportDestination", source, StringComparison.Ordinal);
Assert.DoesNotContain("ResetTeleportTransitState", source, StringComparison.Ordinal);
Assert.DoesNotContain("PlaceTeleportArrival", source, StringComparison.Ordinal);
Assert.DoesNotContain("TryActivatePendingTeleportPresentation", source,
StringComparison.Ordinal);
Assert.DoesNotContain("Action<WorldSession.EntityPositionUpdate>", networkSource,
StringComparison.Ordinal);
Assert.Contains("ILocalPlayerTeleportNetworkSink", networkSource,
StringComparison.Ordinal);
AssertAppearsInOrder(
frameRoot,
"new LiveEntityLivenessFramePhase(",
"session.LocalTeleport,",
"new PlayerModeAutoEntryFramePhase(",
"cameraFrame,",
"live.WorldAvailability);");
}
[Fact]
public void GameplayInputOwnersUseTypedSeamsWithoutGameWindowBackReferences()
{
Type[] owners =
[
typeof(AcDream.App.Input.GameplayInputFrameController),
typeof(AcDream.App.Input.DispatcherMovementInputSource),
typeof(AcDream.App.Input.MouseLookController),
];
foreach (Type owner in owners)
{
FieldInfo[] fields = owner.GetFields(
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.DoesNotContain(fields, field => field.FieldType == typeof(GameWindow));
Assert.DoesNotContain(
fields,
field => typeof(Delegate).IsAssignableFrom(field.FieldType));
}
Assert.DoesNotContain(
typeof(AcDream.App.Input.MouseLookController).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => field.FieldType == typeof(Silk.NET.Input.IMouse));
FieldInfo combatOperations = Assert.Single(
typeof(AcDream.App.Combat.CombatAttackController).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => field.Name == "_operations");
Assert.Equal(
typeof(AcDream.App.Combat.ICombatAttackOperations),
combatOperations.FieldType);
Assert.DoesNotContain(
typeof(AcDream.App.Combat.LiveCombatAttackOperations).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => typeof(Delegate).IsAssignableFrom(field.FieldType));
FieldInfo combatTargets = Assert.Single(
typeof(AcDream.App.Combat.LiveCombatAttackOperations).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => field.Name == "_targets");
Assert.Equal(
typeof(AcDream.App.Combat.ICombatAttackTargetSource),
combatTargets.FieldType);
Assert.DoesNotContain(
typeof(AcDream.App.Combat.CombatAttackTargetSource).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => typeof(Delegate).IsAssignableFrom(field.FieldType));
Assert.DoesNotContain(
typeof(AcDream.App.Combat.CombatAttackTargetSource).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => field.FieldType
== typeof(AcDream.App.Interaction.SelectionInteractionController));
FieldInfo outboundDiagnostics = Assert.Single(
typeof(AcDream.App.Input.LocalPlayerOutboundController).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => field.Name == "_diagnostic");
Assert.Equal(
typeof(AcDream.App.Input.IMovementTruthDiagnosticSink),
outboundDiagnostics.FieldType);
Assert.DoesNotContain(
typeof(AcDream.App.Input.MovementTruthDiagnosticController).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => typeof(Delegate).IsAssignableFrom(field.FieldType));
FieldInfo approachCompletions = Assert.Single(
typeof(AcDream.App.Input.PlayerModeController).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => field.Name == "_approachCompletions");
Assert.Equal(
typeof(AcDream.App.Interaction.IPlayerApproachCompletionLifetimeOwner),
approachCompletions.FieldType);
Assert.DoesNotContain(
typeof(AcDream.App.Input.PlayerModeController).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => field.FieldType
== typeof(AcDream.App.Interaction.SelectionInteractionController));
Assert.DoesNotContain(
typeof(AcDream.App.Interaction.PlayerApproachCompletionState).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => typeof(Delegate).IsAssignableFrom(field.FieldType));
string playerModeSource = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Input",
"PlayerModeController.cs"));
AssertAppearsInOrder(
playerModeSource,
"controller.PreparePositionForCommit(",
"_camera.EnterChaseMode(legacyCamera, retailCamera);",
"EntityPhysicsHost.SelectStableHostWithoutRebind(",
"_shadow.SyncPose(",
"EntityPhysicsHost.InstallOrRebind(",
"playerEntity.SetPosition(initial.Position);",
"controller.CommitPreparedPosition();",
"_controllerSlot.Controller = controller;",
"_mode.IsPlayerMode = true;");
Assert.Contains("_shadow.Restore(playerEntity, priorShadow);", playerModeSource,
StringComparison.Ordinal);
Assert.Contains("_camera.RestoreState(priorCamera);", playerModeSource,
StringComparison.Ordinal);
string mouseLookSource = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Input",
"MouseLookController.cs"));
AssertAppearsInOrder(
mouseLookSource,
"controller?.StopMouseDrift",
"retail.FilterMouseDelta",
"_state.ApplyDelta");
string localPlayerSource = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Input",
"RetailLocalPlayerFrameController.cs"));
Assert.DoesNotContain("Func<MovementInput>", localPlayerSource,
StringComparison.Ordinal);
Assert.Contains("IMovementInputSource", localPlayerSource,
StringComparison.Ordinal);
}
[Fact]
public void CameraAndLocalPlayerFrameOwnersUseTypedSeamsWithoutWindowCallbacks()
{
Type[] owners =
[
typeof(AcDream.App.Rendering.CameraFrameController),
typeof(AcDream.App.Input.RetailLocalPlayerFrameController),
typeof(AcDream.App.Input.LocalPlayerProjectionController),
typeof(AcDream.App.Input.LiveLocalPlayerFrameRuntime),
typeof(AcDream.App.Input.LiveLocalPlayerProjectionRuntime),
typeof(AcDream.App.Combat.CombatCameraTargetSource),
];
foreach (Type owner in owners)
{
FieldInfo[] fields = owner.GetFields(
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.DoesNotContain(fields, field => field.FieldType == typeof(GameWindow));
Assert.DoesNotContain(
fields,
field => typeof(Delegate).IsAssignableFrom(field.FieldType));
}
string root = FindRepoRoot();
string source = File.ReadAllText(Path.Combine(
root,
"src",
"AcDream.App",
"Rendering",
"GameWindow.cs"));
Assert.Equal(1, CountOccurrences(
source,
"_frameGraphs.Tick("));
Assert.DoesNotContain("CanAdvanceLocalPlayer", source, StringComparison.Ordinal);
Assert.DoesNotContain("GetCombatCameraTargetPoint()", source,
StringComparison.Ordinal);
Assert.DoesNotContain("_cameraController.Fly.Update(", source,
StringComparison.Ordinal);
Assert.DoesNotContain("_localPlayerFrame.TryGetPresentationAfterNetwork", source,
StringComparison.Ordinal);
Assert.DoesNotContain("_cameraFrame", source, StringComparison.Ordinal);
string cameraSource = File.ReadAllText(Path.Combine(
root,
"src",
"AcDream.App",
"Rendering",
"CameraFrameController.cs"));
AssertAppearsInOrder(
cameraSource,
"_localFrame.TryGetPresentationAfterNetwork",
"_spatialReconciler.Reconcile();",
"legacy.Update(",
"retail?.Update(");
}
[Fact]
public void GameWindow_OnUpdateOwnsOnlyProfilingAndOneOrchestratorTick()
{
string source = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Rendering",
"GameWindow.cs"));
Assert.Equal(1, CountOccurrences(
source,
"_frameGraphs.Tick("));
Assert.DoesNotContain("_updateFrameClock.Advance(", source,
StringComparison.Ordinal);
Assert.DoesNotContain("_liveFrameCoordinator", source,
StringComparison.Ordinal);
Assert.DoesNotContain("_liveEntityLiveness?.Tick", source,
StringComparison.Ordinal);
Assert.DoesNotContain("_playerModeAutoEntry?.TryEnter", source,
StringComparison.Ordinal);
AssertAppearsInOrder(
source,
"private void OnUpdate(double dt)",
"_frameProfiler.BeginStage(",
"_frameGraphs.Tick(",
"private void OnRender(double deltaSeconds)");
}
[Fact]
public void ProductionFrameAdaptersRetainTypedOwnersWithoutWindowCallbacks()
{
Type[] adapters =
[
typeof(LiveEntityTeardownFramePhase),
typeof(ConsoleUpdateFrameFailureSink),
typeof(PhysicsScriptClockPublisher),
typeof(LiveEntityLivenessFramePhase),
typeof(PlayerModeAutoEntryFramePhase),
];
foreach (Type adapter in adapters)
{
FieldInfo[] fields = adapter.GetFields(
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.DoesNotContain(fields, field => field.FieldType == typeof(GameWindow));
Assert.DoesNotContain(
fields,
field => typeof(Delegate).IsAssignableFrom(field.FieldType));
}
FieldInfo clock = Assert.Single(
typeof(LiveEntityLivenessFramePhase).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => field.Name == "_clock");
Assert.Equal(typeof(IClientMonotonicTimeSource), clock.FieldType);
FieldInfo teardownRuntime = Assert.Single(
typeof(LiveEntityTeardownFramePhase).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic));
Assert.Equal(typeof(LiveEntityRuntime), teardownRuntime.FieldType);
Type[] typedProductionOwners =
[
typeof(LiveEntityLivenessController),
typeof(AcDream.App.Input.LivePlayerModeAutoEntryContext),
typeof(LiveSessionLocalPhysicsTimestampPublisher),
typeof(AcDream.App.Physics.LiveEntityNetworkUpdateController),
typeof(AcDream.App.Rendering.LiveEntityPartArrayLifecycle),
typeof(AcDream.App.Physics.RemoteShadowPlacementSynchronizer),
typeof(AcDream.App.Physics.RemoteTeleportPlacementPresentation),
typeof(AcDream.App.Net.LiveEntitySessionController),
];
foreach (Type owner in typedProductionOwners)
{
FieldInfo[] fields = owner.GetFields(
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.DoesNotContain(fields, field => field.FieldType == typeof(GameWindow));
Assert.DoesNotContain(
fields,
field => typeof(Delegate).IsAssignableFrom(field.FieldType));
}
FieldInfo originIdentity = Assert.Single(
typeof(LiveEntityWorldOriginCoordinator).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => field.Name == "_identity");
Assert.Equal(
typeof(AcDream.App.Input.ILocalPlayerIdentitySource),
originIdentity.FieldType);
string source = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Rendering",
"GameWindow.cs"));
string livePresentation = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"LivePresentationComposition.cs"));
string sessionRuntime = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Net",
"LiveSessionRuntimeFactory.cs"));
Assert.DoesNotContain("PublishLocalPhysicsTimestamps", source,
StringComparison.Ordinal);
Assert.DoesNotContain("LoginWorldReady", source,
StringComparison.Ordinal);
Assert.DoesNotContain("candidate => _liveEntityHydration.OnPrune", source,
StringComparison.Ordinal);
Assert.DoesNotContain("CreateLiveEntitySessionSink", source,
StringComparison.Ordinal);
Assert.DoesNotContain("private System.Numerics.Vector3 CellLocalForSeed", source,
StringComparison.Ordinal);
Assert.DoesNotContain("OnPlayScriptReceived", source,
StringComparison.Ordinal);
Assert.Contains("d.WorldOrigin.GetCenter", livePresentation,
StringComparison.Ordinal);
Assert.Contains("d.WorldOrigin.CellLocalForSeed", livePresentation,
StringComparison.Ordinal);
Assert.DoesNotContain("CreateLiveSessionEventRouter", source,
StringComparison.Ordinal);
Assert.Contains("_world.EntitySession.CreateSink()", sessionRuntime,
StringComparison.Ordinal);
Assert.DoesNotContain("GameWindow", sessionRuntime,
StringComparison.Ordinal);
}
private static UpdateFrameOrchestrator Create(
List<string> calls,
RecordingTeardown? teardown = null,
RecordingFailureSink? failureSink = null,
FrameObservations? observed = null,
bool teleportPlace = false,
bool inboundCreatedPlayer = false,
IWorldGenerationAvailability? availability = null)
{
teardown ??= new RecordingTeardown(calls);
failureSink ??= new RecordingFailureSink();
return new UpdateFrameOrchestrator(
teardown,
failureSink,
new UpdateFrameClock(),
new RecordingClockPublisher(calls, observed),
new RecordingStreaming(calls),
new RecordingInput(calls, observed),
new RecordingLiveFrame(calls, observed),
new RecordingLiveness(calls),
new RecordingTeleport(calls, observed, teleportPlace),
new RecordingAutoEntry(calls),
new RecordingCamera(calls, observed, inboundCreatedPlayer),
new RecordingCommit(calls),
availability);
}
private sealed class RecordingTeardown(List<string> calls)
: IUpdateFrameTeardownPhase
{
public Exception? Failure { get; set; }
public int Attempts { get; private set; }
public void RetryPendingTeardowns()
{
calls.Add("teardown");
Attempts++;
if (Failure is not null)
throw Failure;
}
}
private sealed class RecordingFailureSink : IUpdateFrameFailureSink
{
public List<AggregateException> Errors { get; } = [];
public void ReportTeardownFailure(AggregateException error) => Errors.Add(error);
}
private sealed class RecordingClockPublisher(
List<string> calls,
FrameObservations? observed) : IUpdateFrameScriptClockPublisher
{
public void PublishTime(double scriptTime)
{
calls.Add("clock");
observed?.PublishedTimes.Add(scriptTime);
}
}
private sealed class RecordingStreaming(List<string> calls) : IStreamingFramePhase
{
public void Tick() => calls.Add("streaming");
}
private sealed class RecordingInput(
List<string> calls,
FrameObservations? observed) : IGameplayInputFramePhase
{
public void Tick(UpdateFrameTiming timing)
{
calls.Add("input");
observed?.Input.Add(timing);
}
}
private sealed class RecordingLiveFrame(
List<string> calls,
FrameObservations? observed) : IRetailLiveFramePhase
{
public void Tick(float deltaSeconds)
{
observed?.LiveDeltas.Add(deltaSeconds);
calls.Add("objects");
calls.Add("network");
calls.Add("commands");
calls.Add("ordinary-reconcile");
}
}
private sealed class RecordingLiveness(List<string> calls)
: ILiveEntityLivenessFramePhase
{
public void Tick() => calls.Add("liveness");
}
private sealed class RecordingTeleport(
List<string> calls,
FrameObservations? observed,
bool place)
: ILocalPlayerTeleportFramePhase
{
public void Tick(float deltaSeconds)
{
observed?.TeleportDeltas.Add(deltaSeconds);
if (!place)
{
calls.Add("teleport");
return;
}
calls.Add("teleport-place");
calls.Add("teleport-reconcile");
calls.Add("teleport-reveal");
}
}
private sealed class RecordingAutoEntry(List<string> calls)
: IPlayerModeAutoEntryFramePhase
{
public void TryEnter() => calls.Add("auto-entry");
}
private sealed class RecordingCamera(
List<string> calls,
FrameObservations? observed,
bool inboundCreatedPlayer) : ICameraFramePhase
{
public void Tick(UpdateFrameTiming timing)
{
if (inboundCreatedPlayer)
{
calls.Add("inbound-player-projection");
calls.Add("inbound-player-reconcile");
}
calls.Add("camera");
observed?.Camera.Add(timing);
}
}
private sealed class RecordingCommit(List<string> calls)
: IUpdateFrameCommitPhase
{
public void Commit() => calls.Add("commit");
}
private sealed class FrameObservations
{
public List<double> PublishedTimes { get; } = [];
public List<UpdateFrameTiming> Input { get; } = [];
public List<float> LiveDeltas { get; } = [];
public List<float> TeleportDeltas { get; } = [];
public List<UpdateFrameTiming> Camera { get; } = [];
}
private static string FindRepoRoot()
{
DirectoryInfo? directory = new(AppContext.BaseDirectory);
while (directory is not null)
{
if (File.Exists(Path.Combine(directory.FullName, "AcDream.slnx")))
return directory.FullName;
directory = directory.Parent;
}
throw new DirectoryNotFoundException("Could not find AcDream.slnx.");
}
private static void AssertAppearsInOrder(string source, params string[] markers)
{
int previous = -1;
foreach (string marker in markers)
{
int current = source.IndexOf(marker, previous + 1, StringComparison.Ordinal);
Assert.True(current >= 0, $"Missing source marker: {marker}");
Assert.True(current > previous, $"Out-of-order source marker: {marker}");
previous = current;
}
}
private static int CountOccurrences(string source, string marker) =>
source.Split(marker, StringSplitOptions.None).Length - 1;
}