refactor(camera): extract the update frame

Move fly/chase publication, combat target tracking, and local player projection behind typed runtime owners. Preserve the inbound-created projection/reconcile barrier while removing GameWindow callbacks and duplicate shadow helpers.
This commit is contained in:
Erik 2026-07-22 03:08:58 +02:00
parent eeb0f6b45c
commit 947c61d2d7
19 changed files with 988 additions and 356 deletions

View file

@ -14,17 +14,8 @@ namespace AcDream.App.Input;
/// </remarks>
internal sealed class RetailLocalPlayerFrameController : IPostNetworkCommandFramePhase
{
private readonly Func<bool> _canPresentPlayer;
private readonly Func<PlayerMovementController?> _getController;
private readonly ILocalPlayerFrameRuntime _runtime;
private readonly IMovementInputSource _movementInput;
private readonly Func<uint> _resolveLocalEntityId;
private readonly Action _handleTargeting;
private readonly Func<bool> _isHidden;
private readonly Action<PlayerMovementController, MovementResult, bool> _project;
private readonly Action<PlayerMovementController, MovementResult, bool> _sendPreNetwork;
private readonly Action<PlayerMovementController, bool> _sendPostNetwork;
private readonly Func<AcDream.Core.Physics.RetailObjectClockDisposition>
_objectClockDisposition;
private AdvancedFrame? _advancedFrame;
@ -51,38 +42,12 @@ internal sealed class RetailLocalPlayerFrameController : IPostNetworkCommandFram
};
public RetailLocalPlayerFrameController(
Func<bool> canPresentPlayer,
Func<PlayerMovementController?> getController,
IMovementInputSource movementInput,
Func<uint> resolveLocalEntityId,
Action handleTargeting,
Func<bool> isHidden,
Action<PlayerMovementController, MovementResult, bool> project,
Action<PlayerMovementController, MovementResult, bool> sendPreNetwork,
Action<PlayerMovementController, bool> sendPostNetwork,
Func<AcDream.Core.Physics.RetailObjectClockDisposition>?
objectClockDisposition = null)
ILocalPlayerFrameRuntime runtime,
IMovementInputSource movementInput)
{
_canPresentPlayer = canPresentPlayer
?? throw new ArgumentNullException(nameof(canPresentPlayer));
_getController = getController
?? throw new ArgumentNullException(nameof(getController));
_runtime = runtime ?? throw new ArgumentNullException(nameof(runtime));
_movementInput = movementInput
?? throw new ArgumentNullException(nameof(movementInput));
_resolveLocalEntityId = resolveLocalEntityId
?? throw new ArgumentNullException(nameof(resolveLocalEntityId));
_handleTargeting = handleTargeting
?? throw new ArgumentNullException(nameof(handleTargeting));
_isHidden = isHidden
?? throw new ArgumentNullException(nameof(isHidden));
_project = project
?? throw new ArgumentNullException(nameof(project));
_sendPreNetwork = sendPreNetwork
?? throw new ArgumentNullException(nameof(sendPreNetwork));
_sendPostNetwork = sendPostNetwork
?? throw new ArgumentNullException(nameof(sendPostNetwork));
_objectClockDisposition = objectClockDisposition
?? (() => AcDream.Core.Physics.RetailObjectClockDisposition.Advance);
}
/// <summary>
@ -92,15 +57,15 @@ internal sealed class RetailLocalPlayerFrameController : IPostNetworkCommandFram
public void AdvanceBeforeNetwork(float deltaSeconds)
{
_advancedFrame = null;
PlayerMovementController? controller = _getController();
if (!_canPresentPlayer() || controller is null)
PlayerMovementController? controller = _runtime.Controller;
if (!_runtime.CanPresentPlayer || controller is null)
return;
if (!float.IsFinite(deltaSeconds) || deltaSeconds <= 0f)
{
bool rejectedHidden = _isHidden();
bool rejectedHidden = _runtime.IsHidden;
MovementResult rejected = controller.CapturePresentationResult();
_project(controller, rejected, rejectedHidden);
_runtime.Project(controller, rejected, rejectedHidden);
_advancedFrame = new AdvancedFrame(
controller,
rejected,
@ -110,10 +75,10 @@ internal sealed class RetailLocalPlayerFrameController : IPostNetworkCommandFram
return;
}
controller.LocalEntityId = _resolveLocalEntityId();
controller.LocalEntityId = _runtime.ResolveLocalEntityId();
bool hidden = _isHidden();
if (_objectClockDisposition()
bool hidden = _runtime.IsHidden;
if (_runtime.ObjectClockDisposition
is AcDream.Core.Physics.RetailObjectClockDisposition.Suspend)
{
// CPhysicsObj::update_object returns before advancing time for a
@ -122,7 +87,7 @@ internal sealed class RetailLocalPlayerFrameController : IPostNetworkCommandFram
// physics tick that retail did not run.
controller.SuspendObjectUpdate(deltaSeconds);
MovementResult paused = controller.CapturePresentationResult();
_project(controller, paused, hidden);
_runtime.Project(controller, paused, hidden);
_advancedFrame = new AdvancedFrame(
controller,
paused,
@ -133,11 +98,14 @@ internal sealed class RetailLocalPlayerFrameController : IPostNetworkCommandFram
}
MovementResult movement = hidden
? controller.TickHidden(deltaSeconds, _handleTargeting)
: controller.Update(deltaSeconds, _movementInput.Capture(), _handleTargeting);
? controller.TickHidden(deltaSeconds, _runtime.HandleTargeting)
: controller.Update(
deltaSeconds,
_movementInput.Capture(),
_runtime.HandleTargeting);
_project(controller, movement, hidden);
_sendPreNetwork(controller, movement, hidden);
_runtime.Project(controller, movement, hidden);
_runtime.SendPreNetwork(controller, movement, hidden);
_advancedFrame = new AdvancedFrame(
controller,
movement,
@ -153,18 +121,18 @@ internal sealed class RetailLocalPlayerFrameController : IPostNetworkCommandFram
/// </summary>
public void RunPostNetworkCommandPhase()
{
PlayerMovementController? controller = _getController();
if (!_canPresentPlayer() || controller is null)
PlayerMovementController? controller = _runtime.Controller;
if (!_runtime.CanPresentPlayer || controller is null)
return;
bool hidden = _isHidden();
bool hidden = _runtime.IsHidden;
if (_advancedFrame is { } advanced
&& ReferenceEquals(advanced.Controller, controller))
{
MovementResult movement = RefreshSpatialFields(
advanced.Movement,
controller);
_project(controller, movement, hidden);
_runtime.Project(controller, movement, hidden);
_advancedFrame = new AdvancedFrame(
controller,
movement,
@ -176,7 +144,7 @@ internal sealed class RetailLocalPlayerFrameController : IPostNetworkCommandFram
return;
}
_sendPostNetwork(controller, hidden);
_runtime.SendPostNetwork(controller, hidden);
}
/// <summary>
@ -187,11 +155,11 @@ internal sealed class RetailLocalPlayerFrameController : IPostNetworkCommandFram
public bool TryGetPresentationAfterNetwork(out PresentationFrame frame)
{
frame = default;
PlayerMovementController? controller = _getController();
if (!_canPresentPlayer() || controller is null)
PlayerMovementController? controller = _runtime.Controller;
if (!_runtime.CanPresentPlayer || controller is null)
return false;
bool hidden = _isHidden();
bool hidden = _runtime.IsHidden;
if (_advancedFrame is { } advanced
&& ReferenceEquals(advanced.Controller, controller))
{
@ -206,7 +174,7 @@ internal sealed class RetailLocalPlayerFrameController : IPostNetworkCommandFram
}
MovementResult initial = controller.CapturePresentationResult();
_project(controller, initial, hidden);
_runtime.Project(controller, initial, hidden);
frame = new PresentationFrame(initial, hidden, AdvancedBeforeNetwork: false);
return true;
}