refactor(app): complete session startup composition
Move the live-session reset and routing graph, combat and diagnostic command targets, and the sole gameplay input subscriber into Phase 7 before frame publication. Add exact retryable ownership for late bindings so partial startup cannot strand session or component teardown edges. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
7fa60971e2
commit
826f9ea9b5
22 changed files with 924 additions and 412 deletions
|
|
@ -1,6 +1,7 @@
|
|||
using AcDream.App.Combat;
|
||||
using AcDream.App.Input;
|
||||
using AcDream.App.Interaction;
|
||||
using AcDream.App.Diagnostics;
|
||||
using AcDream.App.Net;
|
||||
using AcDream.App.Physics;
|
||||
using AcDream.App.Rendering;
|
||||
|
|
@ -12,11 +13,15 @@ using AcDream.App.Update;
|
|||
using AcDream.App.World;
|
||||
using AcDream.Core.Combat;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Chat;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Core.Physics.Motion;
|
||||
using AcDream.Core.Plugins;
|
||||
using AcDream.Core.Rendering;
|
||||
using AcDream.Core.Selection;
|
||||
using AcDream.Core.Player;
|
||||
using AcDream.Core.Social;
|
||||
using AcDream.Core.Spells;
|
||||
using AcDream.Core.World;
|
||||
using Silk.NET.Windowing;
|
||||
|
||||
|
|
@ -28,6 +33,11 @@ internal sealed record SessionPlayerDependencies(
|
|||
object DatLock,
|
||||
RuntimeSettingsController Settings,
|
||||
SettingsDevToolsResult SettingsDevTools,
|
||||
WorldEnvironmentController WorldEnvironment,
|
||||
WorldSceneDebugState WorldSceneDebugState,
|
||||
RuntimeDiagnosticCommandSlot RuntimeDiagnosticCommands,
|
||||
LiveCombatModeCommandSlot CombatModeCommands,
|
||||
HostQuiescenceGate HostQuiescence,
|
||||
PhysicsEngine PhysicsEngine,
|
||||
PhysicsDataCache PhysicsDataCache,
|
||||
WorldGameState WorldGameState,
|
||||
|
|
@ -55,6 +65,9 @@ internal sealed record SessionPlayerDependencies(
|
|||
LocalPlayerSkillState PlayerSkills,
|
||||
ViewportAspectState ViewportAspect,
|
||||
PlayerApproachCompletionState PlayerApproachCompletions,
|
||||
PlayerCharacterOptionsState CharacterOptions,
|
||||
ShortcutSnapshotState Shortcuts,
|
||||
DesiredComponentSnapshotState DesiredComponents,
|
||||
PointerPositionState PointerPosition,
|
||||
DispatcherMovementInputSource MovementInput,
|
||||
IInputCaptureSource InputCapture,
|
||||
|
|
@ -66,6 +79,14 @@ internal sealed record SessionPlayerDependencies(
|
|||
CombatAttackOperationsSlot CombatAttackOperations,
|
||||
CombatState Combat,
|
||||
CombatFeedbackSlot CombatFeedback,
|
||||
ChatLog Chat,
|
||||
LocalPlayerState LocalPlayer,
|
||||
Spellbook Spellbook,
|
||||
ItemManaState ItemMana,
|
||||
FriendsState Friends,
|
||||
SquelchState Squelch,
|
||||
TurbineChatState TurbineChat,
|
||||
ExternalContainerState ExternalContainers,
|
||||
TransferableResourceSlot<PortalTunnelPresentation> PortalTunnelFallback,
|
||||
Action<string> Log);
|
||||
|
||||
|
|
@ -91,6 +112,8 @@ internal sealed record SessionPlayerResult(
|
|||
PlayerModeController PlayerMode,
|
||||
PlayerModeAutoEntry PlayerModeAutoEntry,
|
||||
LocalPlayerTeleportController LocalTeleport,
|
||||
LiveSessionHost SessionHost,
|
||||
GameplayInputActionRouter? GameplayActions,
|
||||
SessionPlayerRuntimeBindings RuntimeBindings);
|
||||
|
||||
internal interface IGameWindowSessionPlayerPublication
|
||||
|
|
@ -115,6 +138,9 @@ internal enum SessionPlayerCompositionPoint
|
|||
PlayerModeBound,
|
||||
PortalTransferred,
|
||||
TeleportBound,
|
||||
SessionHostCreated,
|
||||
CommandsBound,
|
||||
GameplayActionsAttached,
|
||||
ResultPublished,
|
||||
}
|
||||
|
||||
|
|
@ -694,9 +720,10 @@ internal sealed class SessionPlayerCompositionPhase
|
|||
|
||||
IDisposable componentLifecycleBinding =
|
||||
live.ComponentLifecycle.BindOwned(teardown);
|
||||
IDisposable componentLifecycleAdoption;
|
||||
try
|
||||
{
|
||||
live.RuntimeBindings.Adopt(
|
||||
componentLifecycleAdoption = live.RuntimeBindings.AdoptOwned(
|
||||
"live runtime component teardown",
|
||||
componentLifecycleBinding);
|
||||
}
|
||||
|
|
@ -705,6 +732,153 @@ internal sealed class SessionPlayerCompositionPhase
|
|||
componentLifecycleBinding.Dispose();
|
||||
throw;
|
||||
}
|
||||
var componentLifecycleLease = scope.Own(
|
||||
"live runtime component teardown adoption",
|
||||
componentLifecycleAdoption,
|
||||
static value => value.Dispose());
|
||||
|
||||
AcDream.UI.Abstractions.Panels.Vitals.VitalsVM? vitals =
|
||||
d.SettingsDevTools.DevTools?.Vitals
|
||||
?? interaction.RetainedUi?.Vitals;
|
||||
AcDream.UI.Abstractions.Panels.Chat.ChatVM? retailChat =
|
||||
interaction.RetainedUi?.Chat;
|
||||
var sessionRuntimeFactory = new LiveSessionRuntimeFactory(
|
||||
new LiveSessionPlayerRuntime(
|
||||
d.PlayerIdentity,
|
||||
d.PlayerController,
|
||||
d.PlayerSkills,
|
||||
d.WorldOrigin,
|
||||
d.CharacterOptions,
|
||||
d.Shortcuts,
|
||||
d.DesiredComponents),
|
||||
new LiveSessionDomainRuntime(
|
||||
d.Objects,
|
||||
d.LocalPlayer,
|
||||
d.Spellbook,
|
||||
d.Combat,
|
||||
d.ItemMana,
|
||||
d.Friends,
|
||||
d.Squelch,
|
||||
d.TurbineChat,
|
||||
d.ExternalContainers,
|
||||
d.Chat),
|
||||
new LiveSessionUiRuntime(
|
||||
interaction.RetainedUi?.Runtime,
|
||||
vitals,
|
||||
retailChat,
|
||||
interaction.RetainedUi?.CharacterSheet,
|
||||
interaction.RetainedUi?.Magic,
|
||||
live.PaperdollPresenter),
|
||||
new LiveSessionInteractionRuntime(
|
||||
d.Settings,
|
||||
gameplayInput,
|
||||
playerMode,
|
||||
playerModeAutoEntry,
|
||||
interaction.ItemInteraction,
|
||||
interaction.CombatAttack,
|
||||
live.SelectionInteractions),
|
||||
new LiveSessionWorldRuntime(
|
||||
content.Dats,
|
||||
live.WorldState,
|
||||
live.LiveEntities,
|
||||
sessionEvents,
|
||||
d.WorldEnvironment,
|
||||
d.TeleportSink,
|
||||
spawnClaimClassifier,
|
||||
live.EquippedChildren,
|
||||
live.SelectionScene,
|
||||
d.ParticleVisibility,
|
||||
d.InboundEntityEvents,
|
||||
liveness,
|
||||
networkUpdates,
|
||||
hydration,
|
||||
live.RemoteTeleport,
|
||||
live.EntityEffects,
|
||||
content.AnimationHookFrames,
|
||||
live.Presentation,
|
||||
d.RemoteMovementObservations),
|
||||
d.Log);
|
||||
LiveSessionHost sessionHost = sessionRuntimeFactory.Create(liveSession);
|
||||
Fault(SessionPlayerCompositionPoint.SessionHostCreated);
|
||||
|
||||
Action<string>? debugToast = d.SettingsDevTools.DevTools is { } devOwner
|
||||
? message => devOwner.Debug.AddToast(message)
|
||||
: null;
|
||||
var combatCommand = new LiveCombatModeCommandController(
|
||||
new LiveSessionCombatModeAuthority(sessionHost),
|
||||
new LocalPlayerCombatEquipmentSource(
|
||||
d.Objects,
|
||||
d.PlayerIdentity),
|
||||
d.Combat,
|
||||
new ItemInteractionCombatModeIntentSink(
|
||||
interaction.ItemInteraction),
|
||||
d.Log,
|
||||
debugToast,
|
||||
text => d.Chat.OnSystemMessage(text, 0x1Au));
|
||||
bindings.Adopt(
|
||||
"live combat-mode commands",
|
||||
d.CombatModeCommands.BindOwned(combatCommand));
|
||||
|
||||
var nearbyDiagnostics = new NearbyWorldDiagnosticDumper(
|
||||
new RuntimeNearbyWorldDiagnosticSource(
|
||||
d.PlayerMode,
|
||||
d.PlayerController,
|
||||
host.CameraController,
|
||||
d.WorldOrigin,
|
||||
live.WorldState,
|
||||
d.PhysicsEngine),
|
||||
d.Log);
|
||||
var runtimeDiagnostics = new RuntimeDiagnosticCommandController(
|
||||
d.WorldEnvironment,
|
||||
d.WorldSceneDebugState,
|
||||
host.CameraPointerInput,
|
||||
nearbyDiagnostics,
|
||||
debugToast);
|
||||
bindings.Adopt(
|
||||
"runtime diagnostic commands",
|
||||
d.RuntimeDiagnosticCommands.BindOwned(runtimeDiagnostics));
|
||||
Fault(SessionPlayerCompositionPoint.CommandsBound);
|
||||
|
||||
CompositionAcquisitionScope.CompositionAcquisitionLease<
|
||||
GameplayInputActionRouter>? gameplayActionsLease = null;
|
||||
if (host.InputDispatcher is { } dispatcher)
|
||||
{
|
||||
CameraPointerInputController pointer = host.CameraPointerInput
|
||||
?? throw new InvalidOperationException(
|
||||
"Gameplay action routing requires the composed pointer owner.");
|
||||
var commands = new GameplayInputCommandController(
|
||||
new RetainedGameplayWindowCommands(
|
||||
interaction.RetainedUi?.Runtime),
|
||||
new DevToolsGameplayCommands(
|
||||
d.SettingsDevTools.DevTools?.Presenter),
|
||||
runtimeDiagnostics,
|
||||
new PlayerModeGameplayCommands(
|
||||
d.PlayerMode,
|
||||
playerMode),
|
||||
new ItemTargetModeCommands(interaction.ItemInteraction),
|
||||
new GameplayCameraModeCommands(host.CameraController),
|
||||
combatCommand,
|
||||
new GameplayWindowCommands(d.Window.Close));
|
||||
var targets = new RuntimeGameplayInputPriorityTargets(
|
||||
gameplayInput,
|
||||
pointer,
|
||||
interaction.RetainedUi?.Runtime,
|
||||
live.SelectionInteractions,
|
||||
commands);
|
||||
GameplayInputActionRouter gameplayActions =
|
||||
GameplayInputActionRouter.Create(
|
||||
dispatcher,
|
||||
d.Combat,
|
||||
targets,
|
||||
d.HostQuiescence,
|
||||
d.Log);
|
||||
gameplayActionsLease = scope.Own(
|
||||
"gameplay input actions",
|
||||
gameplayActions,
|
||||
static value => value.Dispose());
|
||||
gameplayActions.Attach();
|
||||
}
|
||||
Fault(SessionPlayerCompositionPoint.GameplayActionsAttached);
|
||||
|
||||
var bindingsLease = scope.Own(
|
||||
"session/player runtime bindings",
|
||||
|
|
@ -733,12 +907,16 @@ internal sealed class SessionPlayerCompositionPhase
|
|||
playerMode,
|
||||
playerModeAutoEntry,
|
||||
teleportLease.Resource,
|
||||
sessionHost,
|
||||
gameplayActionsLease?.Resource,
|
||||
bindings);
|
||||
_publication.PublishSessionPlayer(result);
|
||||
|
||||
streamerLease.Transfer();
|
||||
liveSessionLease.Transfer();
|
||||
teleportLease.Transfer();
|
||||
gameplayActionsLease?.Transfer();
|
||||
componentLifecycleLease.Transfer();
|
||||
bindingsLease.Transfer();
|
||||
Fault(SessionPlayerCompositionPoint.ResultPublished);
|
||||
return result;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue