acdream/src/AcDream.App/Net/LiveSessionResetManifest.cs
Erik a9a822f206 refactor(runtime): unify generation reset for direct hosts
Move canonical per-session teardown into one retryable Runtime transaction, reduce App reset to projection acknowledgements, and prove the same GameRuntime graph through deterministic no-window lifecycle, gameplay, portal, fault, reconnect, and isolation gates.\n\nCo-authored-by: Codex <noreply@openai.com>
2026-07-27 00:43:26 +02:00

108 lines
4.9 KiB
C#

namespace AcDream.App.Net;
using AcDream.App.World;
using AcDream.Runtime;
using AcDream.Runtime.Entities;
/// <summary>
/// Focused owner operations composed by the App host for one character-session
/// reset. Mixed world/static registries are deliberately absent: their
/// session-owned entries leave through canonical live-entity teardown.
/// </summary>
internal sealed class LiveSessionResetBindings
{
public required Action MouseCapture { get; init; }
public required Action PlayerPresentation { get; init; }
public required Action TeleportPresentation { get; init; }
public required Action SessionDialogs { get; init; }
public required Action SettingsCharacterContext { get; init; }
public required Action EquippedChildren { get; init; }
public required Action InteractionPresentation { get; init; }
public required Action SelectionPresentation { get; init; }
public required Action ParticleVisibility { get; init; }
public required Action InboundEventFifo { get; init; }
public required Action LiveLiveness { get; init; }
public required Action<RuntimeGenerationToken> RuntimeGeneration { get; init; }
public required Action<RuntimeGenerationToken> SessionIdentityPresentation
{ get; init; }
public required Action RemoteTeleport { get; init; }
public required Action NetworkEffects { get; init; }
public required Action AnimationHookFrames { get; init; }
public required Action LivePresentation { get; init; }
public required Action RemoteMovementDiagnostics { get; init; }
}
/// <summary>
/// The concrete, ordered character-session reset manifest. Retail
/// <c>CPlayerSystem::OnEndCharacterSession @ 0x00562870</c> calls End then
/// Begin; <c>ClientUISystem::OnEndCharacterSession @ 0x00564AD0</c>
/// independently resets gameplay UI. App resource dependencies impose the
/// exact projection-before-canonical-runtime order pinned here.
/// </summary>
internal static class LiveSessionResetManifest
{
public static LiveSessionResetPlan Create(LiveSessionResetBindings bindings)
{
ArgumentNullException.ThrowIfNull(bindings);
return new LiveSessionResetPlan(
[
new("mouse capture", bindings.MouseCapture),
new("player presentation", bindings.PlayerPresentation),
new("teleport presentation", bindings.TeleportPresentation),
new("session dialogs", bindings.SessionDialogs),
new("settings character context", bindings.SettingsCharacterContext),
// Attachment projections own GL-backed registrations and must leave
// before canonical live GUID records are released.
new("equipped children", bindings.EquippedChildren),
new("interaction presentation", bindings.InteractionPresentation),
new("selection presentation", bindings.SelectionPresentation),
new("particle visibility", bindings.ParticleVisibility),
new("inbound event fifo", bindings.InboundEventFifo),
new("live liveness", bindings.LiveLiveness),
new("runtime generation", bindings.RuntimeGeneration),
// Identity must remain A until live teardown has converged so
// player-specific teardown can still classify the old record.
new(
"session identity presentation",
bindings.SessionIdentityPresentation),
new("remote teleport", bindings.RemoteTeleport),
// F754/F755 can precede CreateObject, so pending network effects
// must clear even when no LiveEntityRecord was constructed.
new("network effects", bindings.NetworkEffects),
new("animation hook frames", bindings.AnimationHookFrames),
new("live presentation", bindings.LivePresentation),
new("remote movement diagnostics", bindings.RemoteMovementDiagnostics),
]);
}
}
/// <summary>
/// App's narrow borrowed projection acknowledgement for Runtime's canonical
/// generation-reset transaction. It owns no reset cursor or entity identity.
/// </summary>
internal sealed class GraphicalRuntimeGenerationResetHost
: IRuntimeGenerationResetHost
{
private readonly LiveEntityRuntime _entities;
private readonly Action _drainRenderProjection;
public GraphicalRuntimeGenerationResetHost(
LiveEntityRuntime entities,
Action drainRenderProjection)
{
_entities = entities
?? throw new ArgumentNullException(nameof(entities));
_drainRenderProjection = drainRenderProjection
?? throw new ArgumentNullException(nameof(drainRenderProjection));
}
public void RetireEntityProjection(
RuntimeEntityRecord entity) =>
_entities.RetireGenerationProjection(entity);
public void DrainEntityProjectionBoundary() =>
_drainRenderProjection();
public void CompleteEntityProjectionRetirement() =>
_entities.CompleteGenerationProjectionRetirement();
}