acdream/src/AcDream.App/World/RetailLiveFrameCoordinator.cs
Erik 7593078774 refactor(runtime): move session lifetime and ordered transport
Move the canonical WorldSession generation, connect/enter/tick/stop transaction, inbound subscription owner, and retryable teardown acknowledgements into AcDream.Runtime. Keep App as a borrowing graphical host with a single inertable command projection and no mirrored session state.

Validated by 79 Runtime tests, 3,776 App tests with three existing skips, the Release solution build, and 8,428 complete Release tests with five existing skips.

Co-authored-by: Codex <codex@openai.com>
2026-07-25 19:39:24 +02:00

66 lines
2.8 KiB
C#

using AcDream.App.Update;
using AcDream.Runtime.Session;
using AcDream.App.Input;
using AcDream.App.Net;
using AcDream.App.Streaming;
namespace AcDream.App.World;
/// <summary>
/// Owns retail's live-object, inbound-dispatch, and command-interpreter frame
/// phases.
/// </summary>
/// <remarks>
/// Retail <c>SmartBox::UseTime</c> (<c>0x00455410</c>) advances
/// <c>CObjectMaint::UseTime</c> and <c>CPhysics::UseTime</c> before draining
/// <c>SmartBox::in_queue</c>, then calls <c>CommandInterpreter::UseTime</c>.
/// Keeping both boundaries explicit prevents an inbound Hidden transition
/// from freezing an action due to complete this frame while ensuring the
/// periodic AutonomousPosition check observes accepted inbound state.
/// </remarks>
internal sealed class RetailLiveFrameCoordinator : IRetailLiveFramePhase
{
private readonly ILiveObjectFramePhase _objects;
private readonly GpuWorldState _worldState;
private readonly IRuntimeLiveSessionFramePhase _session;
private readonly IPostNetworkCommandFramePhase _localPlayer;
private readonly ILiveSpatialReconcilePhase _spatialReconciler;
private readonly IWorldGenerationAvailability _availability;
private readonly IRenderProjectionSyncPhase? _renderProjectionSync;
public RetailLiveFrameCoordinator(
ILiveObjectFramePhase objects,
GpuWorldState worldState,
IRuntimeLiveSessionFramePhase session,
IPostNetworkCommandFramePhase localPlayer,
ILiveSpatialReconcilePhase spatialReconciler,
IWorldGenerationAvailability? availability = null,
IRenderProjectionSyncPhase? renderProjectionSync = null)
{
_objects = objects ?? throw new ArgumentNullException(nameof(objects));
_worldState = worldState ?? throw new ArgumentNullException(nameof(worldState));
_session = session ?? throw new ArgumentNullException(nameof(session));
_localPlayer = localPlayer ?? throw new ArgumentNullException(nameof(localPlayer));
_spatialReconciler = spatialReconciler
?? throw new ArgumentNullException(nameof(spatialReconciler));
_availability = availability ?? AlwaysAvailableWorldGeneration.Instance;
_renderProjectionSync = renderProjectionSync;
}
public void Tick(float deltaSeconds)
{
float frameDelta = (float)NormalizeDeltaSeconds(deltaSeconds);
if (_availability.IsWorldAvailable)
_objects.Tick(frameDelta);
using (_worldState.BeginMutationBatch())
_session.Tick();
_localPlayer.RunPostNetworkCommandPhase();
if (_availability.IsWorldAvailable)
_spatialReconciler.Reconcile();
else
_renderProjectionSync?.SynchronizeActiveSources();
}
public static double NormalizeDeltaSeconds(double deltaSeconds) =>
UpdateFrameClock.NormalizeDeltaSeconds(deltaSeconds);
}