acdream/src/AcDream.App/Input/RetailLocalPlayerFrameController.cs

87 lines
3.3 KiB
C#

using AcDream.App.Update;
using AcDream.Runtime;
namespace AcDream.App.Input;
/// <summary>
/// Owns the local player's once-per-frame retail object phase.
/// </summary>
/// <remarks>
/// Retail advances the player and sends input-originated movement/jump output
/// before <c>SmartBox::UseTime</c> drains inbound events. Its later
/// <c>CommandInterpreter::UseTime</c> position check uses current post-inbound
/// state. The cached input result is presentation-only after the barrier: its
/// one-shot commands are never replayed against a later authoritative cell.
/// </remarks>
internal sealed class RetailLocalPlayerFrameController : IPostNetworkCommandFramePhase
{
private readonly RuntimeLocalPlayerFrameController _runtime;
public readonly record struct PresentationFrame(
MovementResult Movement,
bool Hidden,
bool AdvancedBeforeNetwork);
/// <summary>
/// Whether the pre-network Hidden player update admitted a complete object
/// quantum whose retained CPartArray pose must be sampled this frame.
/// </summary>
internal bool HiddenPartPoseDirty =>
_runtime.HiddenPartPoseDirty;
public RetailLocalPlayerFrameController(
ILocalPlayerFrameRuntime runtime,
IMovementInputSource movementInput)
{
_runtime = new RuntimeLocalPlayerFrameController(
runtime ?? throw new ArgumentNullException(nameof(runtime)),
movementInput
?? throw new ArgumentNullException(nameof(movementInput)));
}
public RetailLocalPlayerFrameController(
GameRuntime gameRuntime,
ILocalPlayerFrameRuntime runtime,
IMovementInputSource movementInput)
{
ArgumentNullException.ThrowIfNull(gameRuntime);
_runtime = gameRuntime.CreateLocalPlayerFrameController(
runtime ?? throw new ArgumentNullException(nameof(runtime)),
movementInput
?? throw new ArgumentNullException(nameof(movementInput)));
}
/// <summary>
/// Advances the existing local object exactly once on the object side of
/// the inbound-network barrier.
/// </summary>
public void AdvanceBeforeNetwork(float deltaSeconds)
=> _runtime.AdvanceBeforeNetwork(deltaSeconds);
/// <summary>
/// Runs retail's post-inbound command-interpreter phase. The split
/// controller/render projection is reconciled without advancing physics,
/// then AutonomousPosition is evaluated from that current state.
/// </summary>
public void RunPostNetworkCommandPhase()
=> _runtime.RunPostNetworkCommandPhase();
/// <summary>
/// Produces the post-network presentation sample without advancing
/// physics. An owner first created by the inbound pass is seeded from its
/// current state and receives its first real object tick next frame.
/// </summary>
public bool TryGetPresentationAfterNetwork(out PresentationFrame frame)
{
bool available = _runtime.TryGetPresentationAfterNetwork(
out AcDream.Runtime.Gameplay.RuntimeLocalPlayerPresentationFrame
shared);
frame = available
? new PresentationFrame(
shared.Movement,
shared.Hidden,
shared.AdvancedBeforeNetwork)
: default;
return available;
}
}