refactor(app): compose session and player startup
Move streaming, live-session, hydration, local-player, combat, and teleport construction behind the typed Phase-7 boundary. Add exact-owner runtime bindings and focused spawn-claim classification so partial startup rolls back without retaining old session targets while preserving the accepted construction and frame dependencies. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
3573da12e1
commit
7771c07fb6
31 changed files with 1759 additions and 532 deletions
749
src/AcDream.App/Composition/SessionPlayerComposition.cs
Normal file
749
src/AcDream.App/Composition/SessionPlayerComposition.cs
Normal file
|
|
@ -0,0 +1,749 @@
|
|||
using AcDream.App.Combat;
|
||||
using AcDream.App.Input;
|
||||
using AcDream.App.Interaction;
|
||||
using AcDream.App.Net;
|
||||
using AcDream.App.Physics;
|
||||
using AcDream.App.Rendering;
|
||||
using AcDream.App.Rendering.Vfx;
|
||||
using AcDream.App.Rendering.Wb;
|
||||
using AcDream.App.Settings;
|
||||
using AcDream.App.Streaming;
|
||||
using AcDream.App.Update;
|
||||
using AcDream.App.World;
|
||||
using AcDream.Core.Combat;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Core.Physics.Motion;
|
||||
using AcDream.Core.Plugins;
|
||||
using AcDream.Core.Rendering;
|
||||
using AcDream.Core.Selection;
|
||||
using AcDream.Core.World;
|
||||
using Silk.NET.Windowing;
|
||||
|
||||
namespace AcDream.App.Composition;
|
||||
|
||||
internal sealed record SessionPlayerDependencies(
|
||||
RuntimeOptions Options,
|
||||
IWindow Window,
|
||||
object DatLock,
|
||||
RuntimeSettingsController Settings,
|
||||
SettingsDevToolsResult SettingsDevTools,
|
||||
PhysicsEngine PhysicsEngine,
|
||||
PhysicsDataCache PhysicsDataCache,
|
||||
WorldGameState WorldGameState,
|
||||
WorldEvents WorldEvents,
|
||||
SelectionState Selection,
|
||||
ClientObjectTable Objects,
|
||||
EntityClassificationCache ClassificationCache,
|
||||
LiveEntityRuntimeSlot RuntimeSlot,
|
||||
LiveEntityAnimationRuntimeView<LiveEntityAnimationState> AnimatedEntities,
|
||||
RemoteMovementObservationTracker RemoteMovementObservations,
|
||||
RemotePhysicsUpdater RemotePhysicsUpdater,
|
||||
RemoteInboundMotionDispatcher RemoteInboundMotion,
|
||||
RetailInboundEventDispatcher InboundEntityEvents,
|
||||
DeferredLiveEntityMotionRuntimeBindings MotionBindings,
|
||||
LocalPlayerIdentityState PlayerIdentity,
|
||||
LocalPlayerControllerSlot PlayerController,
|
||||
LocalPlayerPhysicsHostSlot PlayerHost,
|
||||
LocalPlayerModeState PlayerMode,
|
||||
ChaseCameraInputState ChaseCameraInput,
|
||||
LocalPlayerOutboundController PlayerOutbound,
|
||||
DeferredLocalPlayerTeleportNetworkSink TeleportSink,
|
||||
LiveWorldOriginState WorldOrigin,
|
||||
WorldRenderRangeState RenderRange,
|
||||
LocalPlayerShadowState PlayerShadow,
|
||||
LocalPlayerSkillState PlayerSkills,
|
||||
ViewportAspectState ViewportAspect,
|
||||
PlayerApproachCompletionState PlayerApproachCompletions,
|
||||
PointerPositionState PointerPosition,
|
||||
DispatcherMovementInputSource MovementInput,
|
||||
IInputCaptureSource InputCapture,
|
||||
AcDream.App.Rendering.Vfx.ParticleVisibilityController ParticleVisibility,
|
||||
TranslucencyFadeManager TranslucencyFades,
|
||||
EntityEffectPoseRegistry EffectPoses,
|
||||
UpdateFrameClock UpdateClock,
|
||||
MovementTruthDiagnosticController MovementDiagnostics,
|
||||
CombatAttackOperationsSlot CombatAttackOperations,
|
||||
CombatState Combat,
|
||||
CombatFeedbackSlot CombatFeedback,
|
||||
TransferableResourceSlot<PortalTunnelPresentation> PortalTunnelFallback,
|
||||
Action<string> Log);
|
||||
|
||||
internal sealed record SessionPlayerResult(
|
||||
LandblockStreamer Streamer,
|
||||
StreamingController Streaming,
|
||||
StreamingOriginRecenterCoordinator StreamingOriginRecenter,
|
||||
WorldRevealCoordinator WorldReveal,
|
||||
DatSpawnClaimHydrationClassifier SpawnClaimHydration,
|
||||
LiveSessionController LiveSession,
|
||||
LiveEntityHydrationController Hydration,
|
||||
LiveEntityNetworkUpdateController NetworkUpdates,
|
||||
LiveEntityLivenessController Liveness,
|
||||
LiveEntitySessionController SessionEvents,
|
||||
GameplayInputFrameController GameplayInput,
|
||||
StreamingFrameController StreamingFrame,
|
||||
LocalPlayerAnimationController LocalPlayerAnimation,
|
||||
LocalPlayerShadowSynchronizer LocalPlayerShadow,
|
||||
LiveLocalPlayerFrameRuntime LocalPlayerFrameRuntime,
|
||||
RetailLocalPlayerFrameController LocalPlayerFrame,
|
||||
LiveSpatialPresentationReconciler LiveSpatialReconciler,
|
||||
LiveObjectFrameController LiveObjectFrame,
|
||||
PlayerModeController PlayerMode,
|
||||
PlayerModeAutoEntry PlayerModeAutoEntry,
|
||||
LocalPlayerTeleportController LocalTeleport,
|
||||
SessionPlayerRuntimeBindings RuntimeBindings);
|
||||
|
||||
internal interface IGameWindowSessionPlayerPublication
|
||||
{
|
||||
void PublishSessionPlayer(SessionPlayerResult result);
|
||||
}
|
||||
|
||||
internal enum SessionPlayerCompositionPoint
|
||||
{
|
||||
StreamingRadiiResolved,
|
||||
StreamerCreated,
|
||||
StreamerStarted,
|
||||
StreamingCreated,
|
||||
RuntimeSettingsBound,
|
||||
WorldRevealCreated,
|
||||
LiveSessionCreated,
|
||||
HydrationCreated,
|
||||
LiveEntityGraphBound,
|
||||
CombatOperationsBound,
|
||||
GameplayInputBound,
|
||||
UpdateLeavesCreated,
|
||||
PlayerModeBound,
|
||||
PortalTransferred,
|
||||
TeleportBound,
|
||||
ResultPublished,
|
||||
}
|
||||
|
||||
internal sealed class SessionPlayerCompositionPhase
|
||||
: ISessionPlayerCompositionPhase<
|
||||
HostInputCameraResult,
|
||||
ContentEffectsAudioResult,
|
||||
WorldRenderResult,
|
||||
InteractionRetainedUiResult,
|
||||
LivePresentationResult,
|
||||
SessionPlayerResult>
|
||||
{
|
||||
private readonly SessionPlayerDependencies _dependencies;
|
||||
private readonly IGameWindowSessionPlayerPublication _publication;
|
||||
private readonly Action<SessionPlayerCompositionPoint>? _faultInjection;
|
||||
|
||||
public SessionPlayerCompositionPhase(
|
||||
SessionPlayerDependencies dependencies,
|
||||
IGameWindowSessionPlayerPublication publication,
|
||||
Action<SessionPlayerCompositionPoint>? faultInjection = null)
|
||||
{
|
||||
_dependencies = dependencies
|
||||
?? throw new ArgumentNullException(nameof(dependencies));
|
||||
_publication = publication
|
||||
?? throw new ArgumentNullException(nameof(publication));
|
||||
_faultInjection = faultInjection;
|
||||
}
|
||||
|
||||
public SessionPlayerResult Compose(
|
||||
HostInputCameraResult host,
|
||||
ContentEffectsAudioResult content,
|
||||
WorldRenderResult world,
|
||||
InteractionRetainedUiResult interaction,
|
||||
LivePresentationResult live)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(host);
|
||||
ArgumentNullException.ThrowIfNull(content);
|
||||
ArgumentNullException.ThrowIfNull(world);
|
||||
ArgumentNullException.ThrowIfNull(interaction);
|
||||
ArgumentNullException.ThrowIfNull(live);
|
||||
|
||||
var scope = new CompositionAcquisitionScope();
|
||||
SessionPlayerRuntimeBindings? bindings = null;
|
||||
bool bindingsOwnedByScope = false;
|
||||
try
|
||||
{
|
||||
SessionPlayerResult result = ComposeCore(
|
||||
host,
|
||||
content,
|
||||
world,
|
||||
interaction,
|
||||
live,
|
||||
scope,
|
||||
ref bindings,
|
||||
ref bindingsOwnedByScope);
|
||||
scope.Complete();
|
||||
return result;
|
||||
}
|
||||
catch (Exception failure)
|
||||
{
|
||||
if (bindings is not null && !bindingsOwnedByScope)
|
||||
{
|
||||
scope.Own(
|
||||
"session/player runtime bindings",
|
||||
bindings,
|
||||
static value => value.Dispose());
|
||||
bindingsOwnedByScope = true;
|
||||
}
|
||||
|
||||
scope.RollbackAndThrow(failure);
|
||||
throw new System.Diagnostics.UnreachableException();
|
||||
}
|
||||
}
|
||||
|
||||
private SessionPlayerResult ComposeCore(
|
||||
HostInputCameraResult host,
|
||||
ContentEffectsAudioResult content,
|
||||
WorldRenderResult world,
|
||||
InteractionRetainedUiResult interaction,
|
||||
LivePresentationResult live,
|
||||
CompositionAcquisitionScope scope,
|
||||
ref SessionPlayerRuntimeBindings? bindings,
|
||||
ref bool bindingsOwnedByScope)
|
||||
{
|
||||
SessionPlayerDependencies d = _dependencies;
|
||||
WorldRenderFoundation foundation = world.Foundation;
|
||||
int nearRadius = d.Settings.ResolvedQuality.NearRadius;
|
||||
int farRadius = d.Settings.ResolvedQuality.FarRadius;
|
||||
if (d.Options.LegacyStreamRadius is { } legacyRadius)
|
||||
{
|
||||
nearRadius = legacyRadius;
|
||||
farRadius = Math.Max(legacyRadius, farRadius);
|
||||
}
|
||||
d.RenderRange.NearRadius = nearRadius;
|
||||
d.RenderRange.FarRadius = farRadius;
|
||||
d.Log(
|
||||
$"streaming: nearRadius={nearRadius} " +
|
||||
$"(window={2 * nearRadius + 1}x{2 * nearRadius + 1}) " +
|
||||
$"farRadius={farRadius} " +
|
||||
$"(window={2 * farRadius + 1}x{2 * farRadius + 1})");
|
||||
Fault(SessionPlayerCompositionPoint.StreamingRadiiResolved);
|
||||
|
||||
var landblockBuildFactory = new LandblockBuildFactory(
|
||||
content.Dats,
|
||||
d.DatLock,
|
||||
world.TerrainBuild.HeightTable,
|
||||
d.PhysicsDataCache,
|
||||
d.Options.DumpSceneryZ);
|
||||
var streamerLease = scope.Acquire(
|
||||
"landblock streamer",
|
||||
() => LandblockStreamer.CreateForRequests(
|
||||
landblockBuildFactory.Build,
|
||||
(id, landblock) =>
|
||||
{
|
||||
if (landblock is null)
|
||||
return null;
|
||||
uint x = (id >> 24) & 0xFFu;
|
||||
uint y = (id >> 16) & 0xFFu;
|
||||
return AcDream.Core.Terrain.LandblockMesh.Build(
|
||||
landblock.Heightmap,
|
||||
x,
|
||||
y,
|
||||
world.TerrainBuild.HeightTable,
|
||||
world.TerrainBuild.Blending,
|
||||
world.TerrainBuild.SurfaceCache);
|
||||
}),
|
||||
static value => value.Dispose());
|
||||
Fault(SessionPlayerCompositionPoint.StreamerCreated);
|
||||
streamerLease.Resource.Start();
|
||||
Fault(SessionPlayerCompositionPoint.StreamerStarted);
|
||||
|
||||
var streaming = new StreamingController(
|
||||
enqueueLoad: (id, kind, generation) => streamerLease.Resource.EnqueueLoad(
|
||||
new LandblockBuildRequest(
|
||||
id,
|
||||
kind,
|
||||
generation,
|
||||
new LandblockBuildOrigin(
|
||||
d.WorldOrigin.CenterX,
|
||||
d.WorldOrigin.CenterY))),
|
||||
enqueueUnload: streamerLease.Resource.EnqueueUnload,
|
||||
drainCompletions: streamerLease.Resource.DrainCompletions,
|
||||
state: live.WorldState,
|
||||
nearRadius: nearRadius,
|
||||
farRadius: farRadius,
|
||||
presentationPipeline: live.LandblockPipeline,
|
||||
clearPendingLoads: streamerLease.Resource.ClearPendingLoads);
|
||||
var streamingOriginRecenter = new StreamingOriginRecenterCoordinator(
|
||||
streaming,
|
||||
d.WorldOrigin);
|
||||
streaming.MaxCompletionsPerFrame =
|
||||
d.Settings.ResolvedQuality.MaxCompletionsPerFrame;
|
||||
Fault(SessionPlayerCompositionPoint.StreamingCreated);
|
||||
|
||||
bindings = new SessionPlayerRuntimeBindings();
|
||||
var settingsTargets = new RuntimeSettingsTargets(
|
||||
new SilkRuntimeDisplayWindowTarget(d.Window),
|
||||
live.DrawDispatcher,
|
||||
foundation.TerrainAtlas,
|
||||
streaming,
|
||||
d.RenderRange,
|
||||
interaction.RetainedUi?.Host.Root,
|
||||
d.Log);
|
||||
bindings.Adopt(
|
||||
"runtime settings targets",
|
||||
d.Settings.BindRuntimeTargetsOwned(settingsTargets));
|
||||
Fault(SessionPlayerCompositionPoint.RuntimeSettingsBound);
|
||||
|
||||
var spawnClaimClassifier = new DatSpawnClaimHydrationClassifier(
|
||||
content.Dats,
|
||||
d.DatLock);
|
||||
var worldReveal = new WorldRevealCoordinator(
|
||||
streaming.IsRenderNeighborhoodResident,
|
||||
d.PhysicsEngine.IsSpawnCellReady,
|
||||
d.PhysicsEngine.IsNeighborhoodTerrainResident,
|
||||
() => live.DrawDispatcher.CompositeTexturesReady,
|
||||
(destinationCell, radius) => live.DrawDispatcher.PrepareCompositeTextures(
|
||||
live.WorldState.Entities,
|
||||
live.WorldState.FlatViewGeneration,
|
||||
destinationCell,
|
||||
radius),
|
||||
live.DrawDispatcher.InvalidateCompositeWarmupReadiness,
|
||||
spawnClaimClassifier.IsUnhydratable,
|
||||
d.Log);
|
||||
Fault(SessionPlayerCompositionPoint.WorldRevealCreated);
|
||||
|
||||
return CompleteSessionPlayer(
|
||||
host,
|
||||
content,
|
||||
world,
|
||||
interaction,
|
||||
live,
|
||||
scope,
|
||||
streamerLease,
|
||||
streaming,
|
||||
streamingOriginRecenter,
|
||||
worldReveal,
|
||||
spawnClaimClassifier,
|
||||
bindings,
|
||||
ref bindingsOwnedByScope);
|
||||
}
|
||||
|
||||
private SessionPlayerResult CompleteSessionPlayer(
|
||||
HostInputCameraResult host,
|
||||
ContentEffectsAudioResult content,
|
||||
WorldRenderResult world,
|
||||
InteractionRetainedUiResult interaction,
|
||||
LivePresentationResult live,
|
||||
CompositionAcquisitionScope scope,
|
||||
CompositionAcquisitionScope.CompositionAcquisitionLease<LandblockStreamer>
|
||||
streamerLease,
|
||||
StreamingController streaming,
|
||||
StreamingOriginRecenterCoordinator streamingOriginRecenter,
|
||||
WorldRevealCoordinator worldReveal,
|
||||
DatSpawnClaimHydrationClassifier spawnClaimClassifier,
|
||||
SessionPlayerRuntimeBindings bindings,
|
||||
ref bool bindingsOwnedByScope)
|
||||
{
|
||||
SessionPlayerDependencies d = _dependencies;
|
||||
var networkUpdateBridge = new DeferredLiveEntityNetworkUpdateSink();
|
||||
var sealedDungeonCells = new DatSealedDungeonCellClassifier(
|
||||
content.Dats,
|
||||
d.DatLock);
|
||||
var projectionMaterializer = new DatLiveEntityProjectionMaterializer(
|
||||
d.Options,
|
||||
content.Dats,
|
||||
live.LiveEntities,
|
||||
d.PhysicsDataCache,
|
||||
content.AnimationLoader,
|
||||
live.EntitySpawnAdapter,
|
||||
world.Foundation.TextureCache,
|
||||
d.ClassificationCache,
|
||||
d.EffectPoses,
|
||||
live.EquippedChildren,
|
||||
d.WorldGameState,
|
||||
d.WorldEvents,
|
||||
d.PhysicsEngine.ShadowObjects,
|
||||
content.CollisionBuilder,
|
||||
live.ProjectileController,
|
||||
live.AnimationPresenter,
|
||||
live.StaticAnimationScheduler,
|
||||
d.WorldOrigin,
|
||||
d.UpdateClock);
|
||||
var originCoordinator = new LiveEntityWorldOriginCoordinator(
|
||||
d.WorldOrigin,
|
||||
streaming,
|
||||
live.WorldState,
|
||||
worldReveal,
|
||||
d.PlayerIdentity,
|
||||
sealedDungeonCells,
|
||||
d.Log);
|
||||
|
||||
var liveSessionLease = scope.Acquire(
|
||||
"live-session controller",
|
||||
static () => new LiveSessionController(),
|
||||
static value => value.Dispose());
|
||||
LiveSessionController liveSession = liveSessionLease.Resource;
|
||||
bindings.Adopt(
|
||||
"retained-UI live session",
|
||||
interaction.LateBindings.Session.Bind(liveSession));
|
||||
var localPhysicsTimestamps =
|
||||
new LiveSessionLocalPhysicsTimestampPublisher(
|
||||
d.PlayerIdentity,
|
||||
liveSession);
|
||||
Fault(SessionPlayerCompositionPoint.LiveSessionCreated);
|
||||
|
||||
var teardown = new LiveEntityRuntimeTeardownController(
|
||||
live.LiveEntities,
|
||||
live.Presentation,
|
||||
live.EntityEffects,
|
||||
live.RemoteTeleport,
|
||||
live.SelectionInteractions,
|
||||
d.Selection,
|
||||
d.AnimatedEntities,
|
||||
d.RemoteMovementObservations,
|
||||
d.TranslucencyFades,
|
||||
live.ProjectionWithdrawal,
|
||||
live.EquippedChildren,
|
||||
d.PhysicsEngine.ShadowObjects,
|
||||
live.Lights,
|
||||
d.ClassificationCache,
|
||||
d.PlayerIdentity);
|
||||
var deletion = new LiveEntityDeletionController(
|
||||
live.LiveEntities,
|
||||
d.Objects,
|
||||
teardown,
|
||||
d.PlayerIdentity,
|
||||
d.Options.DumpLiveSpawns ? d.Log : null);
|
||||
var hydration = new LiveEntityHydrationController(
|
||||
live.LiveEntities,
|
||||
d.Objects,
|
||||
d.DatLock,
|
||||
projectionMaterializer,
|
||||
new LiveEntityRelationshipProjection(live.EquippedChildren),
|
||||
new LiveEntityReadyPublisher(
|
||||
live.LiveEntities,
|
||||
live.EntityEffects,
|
||||
live.Presentation),
|
||||
originCoordinator,
|
||||
networkUpdateBridge,
|
||||
localPhysicsTimestamps,
|
||||
d.PlayerIdentity,
|
||||
deletion,
|
||||
d.Options.DumpLiveSpawns ? d.Log : null);
|
||||
bindings.Adopt(
|
||||
"landblock-loaded hydration",
|
||||
live.LandblockLoaded.Bind(hydration));
|
||||
Fault(SessionPlayerCompositionPoint.HydrationCreated);
|
||||
|
||||
var networkUpdates = new LiveEntityNetworkUpdateController(
|
||||
live.LiveEntities,
|
||||
d.Objects,
|
||||
hydration,
|
||||
live.EntityEffects,
|
||||
live.Presentation,
|
||||
live.Lights,
|
||||
live.EquippedChildren,
|
||||
live.ProjectileController,
|
||||
live.RemoteTeleport,
|
||||
d.AnimatedEntities,
|
||||
new LiveEntityRemoteMotionRuntimeView<RemoteMotion>(d.RuntimeSlot),
|
||||
d.RemoteMovementObservations,
|
||||
d.RemotePhysicsUpdater,
|
||||
d.RemoteInboundMotion,
|
||||
live.MotionRuntime,
|
||||
d.PhysicsEngine,
|
||||
content.Dats,
|
||||
content.AnimationLoader,
|
||||
interaction.CombatTarget,
|
||||
d.WorldOrigin,
|
||||
d.TeleportSink,
|
||||
d.PlayerController,
|
||||
d.PlayerOutbound,
|
||||
d.PlayerHost,
|
||||
d.PlayerIdentity,
|
||||
d.UpdateClock,
|
||||
liveSession,
|
||||
localPhysicsTimestamps.Publish,
|
||||
d.MovementDiagnostics);
|
||||
var liveness = new LiveEntityLivenessController(
|
||||
live.LiveEntities,
|
||||
d.PlayerIdentity,
|
||||
deletion);
|
||||
var sessionEvents = new LiveEntitySessionController(
|
||||
d.InboundEntityEvents,
|
||||
hydration,
|
||||
networkUpdates,
|
||||
d.TeleportSink,
|
||||
live.EntityEffects);
|
||||
bindings.Adopt(
|
||||
"live parent acceptance",
|
||||
live.ParentAcceptance.BindOwned(
|
||||
hydration.TryAcceptParentForProjection));
|
||||
bindings.Adopt(
|
||||
"same-generation network updates",
|
||||
networkUpdateBridge.BindOwned(networkUpdates));
|
||||
bindings.BindEntityReady(
|
||||
live.EquippedChildren,
|
||||
candidate => _ = hydration.OnEntityReady(candidate));
|
||||
bindings.BindAppearanceApplied(
|
||||
hydration,
|
||||
guid =>
|
||||
{
|
||||
if (guid == d.PlayerIdentity.ServerGuid)
|
||||
live.PaperdollPresenter?.MarkDirty();
|
||||
});
|
||||
Fault(SessionPlayerCompositionPoint.LiveEntityGraphBound);
|
||||
|
||||
d.WorldOrigin.SetPlaceholder(
|
||||
world.TerrainBuild.InitialCenterX,
|
||||
world.TerrainBuild.InitialCenterY);
|
||||
bindings.Adopt(
|
||||
"combat attack operations",
|
||||
d.CombatAttackOperations.BindOwned(
|
||||
new LiveCombatAttackOperations(
|
||||
d.Combat,
|
||||
new CombatAttackTargetSource(
|
||||
d.Selection,
|
||||
live.LiveEntities,
|
||||
d.Objects,
|
||||
d.PlayerIdentity),
|
||||
d.Settings,
|
||||
d.PlayerController,
|
||||
d.PlayerOutbound,
|
||||
liveSession,
|
||||
liveSession,
|
||||
d.CombatFeedback)));
|
||||
Fault(SessionPlayerCompositionPoint.CombatOperationsBound);
|
||||
|
||||
MouseLookController? mouseLook =
|
||||
host.MouseSource is not null && host.MouseLookCursor is not null
|
||||
? new MouseLookController(
|
||||
host.MouseSource,
|
||||
d.PointerPosition,
|
||||
d.PlayerMode,
|
||||
d.PlayerController,
|
||||
host.CameraController,
|
||||
d.ChaseCameraInput,
|
||||
d.MovementInput,
|
||||
d.PlayerOutbound,
|
||||
liveSession,
|
||||
host.MouseLookCursor,
|
||||
new EnvironmentInputMonotonicClock())
|
||||
: null;
|
||||
var gameplayInput = new GameplayInputFrameController(
|
||||
host.InputDispatcher,
|
||||
d.MovementInput,
|
||||
mouseLook,
|
||||
new CombatAttackInputFrameAdapter(interaction.CombatAttack));
|
||||
if (host.CameraPointerInput is { } cameraPointer)
|
||||
{
|
||||
bindings.Adopt(
|
||||
"camera pointer gameplay frame",
|
||||
cameraPointer.BindGameplayFrameOwned(gameplayInput));
|
||||
}
|
||||
Fault(SessionPlayerCompositionPoint.GameplayInputBound);
|
||||
|
||||
var streamingFrame = new StreamingFrameController(
|
||||
d.Options.LiveMode,
|
||||
d.PlayerMode,
|
||||
d.PlayerController,
|
||||
liveSession,
|
||||
d.WorldOrigin,
|
||||
networkUpdates,
|
||||
new FlyCameraStreamingObserverSource(host.CameraController),
|
||||
new PhysicsStreamingDungeonCellSource(d.PhysicsEngine),
|
||||
streamingOriginRecenter,
|
||||
streaming,
|
||||
new LiveProjectionRescueRebucketter(
|
||||
live.WorldState,
|
||||
live.LiveEntities));
|
||||
var liveEffectFrame = new LiveEffectFrameController(
|
||||
d.TranslucencyFades,
|
||||
content.AnimationHookFrames,
|
||||
live.EntityEffects,
|
||||
content.ParticleSink,
|
||||
live.Lights,
|
||||
d.ParticleVisibility,
|
||||
content.ParticleSystem,
|
||||
content.ScriptRunner,
|
||||
d.UpdateClock,
|
||||
new SettingsParticleRangeSource(d.Settings));
|
||||
var liveSpatialReconciler = new LiveSpatialPresentationReconciler(
|
||||
live.EntityEffects,
|
||||
live.EquippedChildren,
|
||||
content.ParticleSink,
|
||||
live.Lights);
|
||||
var localPlayerAnimation = new LocalPlayerAnimationController(
|
||||
live.LiveEntities,
|
||||
d.PlayerIdentity,
|
||||
d.AnimatedEntities,
|
||||
live.AnimationPresenter,
|
||||
content.AnimationHookFrames);
|
||||
var localPlayerShadow = new LocalPlayerShadowSynchronizer(
|
||||
d.PhysicsEngine,
|
||||
live.LiveEntities,
|
||||
d.PlayerIdentity,
|
||||
d.WorldOrigin,
|
||||
d.PlayerShadow);
|
||||
var localPlayerProjection = new LocalPlayerProjectionController(
|
||||
new LiveLocalPlayerProjectionRuntime(
|
||||
live.LiveEntities,
|
||||
d.PlayerIdentity,
|
||||
d.WorldOrigin,
|
||||
localPlayerShadow));
|
||||
var localPlayerFrameRuntime = new LiveLocalPlayerFrameRuntime(
|
||||
host.CameraController,
|
||||
d.PlayerMode,
|
||||
d.PlayerController,
|
||||
d.ChaseCameraInput,
|
||||
d.MovementInput,
|
||||
d.InputCapture,
|
||||
live.LiveEntities,
|
||||
d.PlayerIdentity,
|
||||
d.PlayerHost,
|
||||
localPlayerProjection,
|
||||
d.PlayerOutbound,
|
||||
liveSession);
|
||||
var localPlayerFrame = new RetailLocalPlayerFrameController(
|
||||
localPlayerFrameRuntime,
|
||||
d.MovementInput);
|
||||
var liveObjectFrame = new LiveObjectFrameController(
|
||||
d.InboundEntityEvents,
|
||||
localPlayerFrame,
|
||||
live.SelectionInteractions,
|
||||
live.LiveEntities,
|
||||
d.PlayerIdentity,
|
||||
d.WorldOrigin,
|
||||
live.AnimationScheduler,
|
||||
live.StaticAnimationScheduler,
|
||||
live.AnimationPresenter,
|
||||
d.AnimatedEntities,
|
||||
live.EquippedChildren,
|
||||
liveEffectFrame);
|
||||
Fault(SessionPlayerCompositionPoint.UpdateLeavesCreated);
|
||||
|
||||
var playerMode = new PlayerModeController(
|
||||
d.PlayerMode,
|
||||
d.PlayerController,
|
||||
d.PlayerHost,
|
||||
d.ChaseCameraInput,
|
||||
host.CameraController,
|
||||
d.PhysicsEngine,
|
||||
live.LiveEntities,
|
||||
d.PlayerIdentity,
|
||||
d.WorldOrigin,
|
||||
d.MotionBindings,
|
||||
content.Dats,
|
||||
d.DatLock,
|
||||
d.PhysicsDataCache,
|
||||
d.AnimatedEntities,
|
||||
localPlayerAnimation,
|
||||
localPlayerShadow,
|
||||
d.PlayerApproachCompletions,
|
||||
gameplayInput,
|
||||
liveSession,
|
||||
d.MovementDiagnostics,
|
||||
d.PlayerSkills,
|
||||
d.ViewportAspect);
|
||||
if (d.SettingsDevTools.DevTools is { } devTools)
|
||||
{
|
||||
bindings.Adopt(
|
||||
"developer player mode",
|
||||
devTools.LateBindings.PlayerModeCommands.BindOwned(playerMode));
|
||||
bindings.Adopt(
|
||||
"developer command bus",
|
||||
devTools.CommandBus.BindOwned(liveSession));
|
||||
}
|
||||
var playerModeAutoEntry = new PlayerModeAutoEntry(
|
||||
new LivePlayerModeAutoEntryContext(
|
||||
liveSession,
|
||||
live.LiveEntities,
|
||||
d.PlayerIdentity,
|
||||
worldReveal,
|
||||
d.PlayerMode,
|
||||
playerMode));
|
||||
playerMode.BindAutoEntry(playerModeAutoEntry);
|
||||
Fault(SessionPlayerCompositionPoint.PlayerModeBound);
|
||||
|
||||
LocalPlayerTeleportController localTeleport =
|
||||
d.PortalTunnelFallback.Transfer(
|
||||
portalTunnel => new LocalPlayerTeleportController(
|
||||
new LiveLocalPlayerTeleportAuthority(
|
||||
live.LiveEntities,
|
||||
d.PlayerIdentity),
|
||||
gameplayInput,
|
||||
playerMode,
|
||||
new LocalPlayerTeleportStreamingOperations(
|
||||
d.WorldOrigin,
|
||||
streamingOriginRecenter,
|
||||
streaming,
|
||||
sealedDungeonCells),
|
||||
worldReveal,
|
||||
new LocalPlayerTeleportPlacement(
|
||||
d.PhysicsEngine,
|
||||
live.LiveEntities,
|
||||
d.PlayerIdentity,
|
||||
d.PlayerController,
|
||||
d.PlayerHost,
|
||||
d.ChaseCameraInput,
|
||||
d.WorldOrigin,
|
||||
liveSpatialReconciler),
|
||||
new LocalPlayerTeleportSession(liveSession),
|
||||
new LocalPlayerTeleportPresentation(portalTunnel)));
|
||||
var teleportLease = scope.Own(
|
||||
"local-player teleport",
|
||||
localTeleport,
|
||||
static value => value.Dispose());
|
||||
Fault(SessionPlayerCompositionPoint.PortalTransferred);
|
||||
bindings.Adopt(
|
||||
"local teleport network sink",
|
||||
d.TeleportSink.BindOwned(localTeleport));
|
||||
bindings.Adopt(
|
||||
"selection view plane",
|
||||
interaction.LateBindings.SelectionViewPlane.Bind(localTeleport));
|
||||
Fault(SessionPlayerCompositionPoint.TeleportBound);
|
||||
|
||||
IDisposable componentLifecycleBinding =
|
||||
live.ComponentLifecycle.BindOwned(teardown);
|
||||
try
|
||||
{
|
||||
live.RuntimeBindings.Adopt(
|
||||
"live runtime component teardown",
|
||||
componentLifecycleBinding);
|
||||
}
|
||||
catch
|
||||
{
|
||||
componentLifecycleBinding.Dispose();
|
||||
throw;
|
||||
}
|
||||
|
||||
var bindingsLease = scope.Own(
|
||||
"session/player runtime bindings",
|
||||
bindings,
|
||||
static value => value.Dispose());
|
||||
bindingsOwnedByScope = true;
|
||||
var result = new SessionPlayerResult(
|
||||
streamerLease.Resource,
|
||||
streaming,
|
||||
streamingOriginRecenter,
|
||||
worldReveal,
|
||||
spawnClaimClassifier,
|
||||
liveSession,
|
||||
hydration,
|
||||
networkUpdates,
|
||||
liveness,
|
||||
sessionEvents,
|
||||
gameplayInput,
|
||||
streamingFrame,
|
||||
localPlayerAnimation,
|
||||
localPlayerShadow,
|
||||
localPlayerFrameRuntime,
|
||||
localPlayerFrame,
|
||||
liveSpatialReconciler,
|
||||
liveObjectFrame,
|
||||
playerMode,
|
||||
playerModeAutoEntry,
|
||||
teleportLease.Resource,
|
||||
bindings);
|
||||
_publication.PublishSessionPlayer(result);
|
||||
|
||||
streamerLease.Transfer();
|
||||
liveSessionLease.Transfer();
|
||||
teleportLease.Transfer();
|
||||
bindingsLease.Transfer();
|
||||
Fault(SessionPlayerCompositionPoint.ResultPublished);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void Fault(SessionPlayerCompositionPoint point) =>
|
||||
_faultInjection?.Invoke(point);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue