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

@ -15,7 +15,7 @@ public sealed class RetailLocalPlayerFrameControllerTests
var calls = new List<string>();
var preNetwork = new List<(PlayerState State, MovementResult Movement)>();
var postNetwork = new List<PlayerState>();
var local = new RetailLocalPlayerFrameController(
var local = CreateFrame(
canPresentPlayer: () => true,
getController: () => controller,
movementInput: Input(() => new MovementInput(Forward: true)),
@ -79,7 +79,7 @@ public sealed class RetailLocalPlayerFrameControllerTests
int projections = 0;
int preNetwork = 0;
int postNetwork = 0;
var local = new RetailLocalPlayerFrameController(
var local = CreateFrame(
canPresentPlayer: () => controller is not null,
getController: () => controller,
movementInput: Input(() => new MovementInput(Forward: true)),
@ -109,7 +109,7 @@ public sealed class RetailLocalPlayerFrameControllerTests
{
PlayerMovementController controller = CreateController();
Vector3 projectedRoot = default;
var local = new RetailLocalPlayerFrameController(
var local = CreateFrame(
canPresentPlayer: () => true,
getController: () => controller,
movementInput: Input(() => new MovementInput()),
@ -140,7 +140,7 @@ public sealed class RetailLocalPlayerFrameControllerTests
PlayerMovementController controller = CreateController();
Vector3 initial = controller.Position;
Vector3 positionSeenByTargetManager = new(float.NaN);
var local = new RetailLocalPlayerFrameController(
var local = CreateFrame(
canPresentPlayer: () => true,
getController: () => controller,
movementInput: Input(() => new MovementInput(Forward: true)),
@ -166,7 +166,7 @@ public sealed class RetailLocalPlayerFrameControllerTests
int projections = 0;
int preNetwork = 0;
int postNetwork = 0;
var local = new RetailLocalPlayerFrameController(
var local = CreateFrame(
canPresentPlayer: () => true,
getController: () => controller,
movementInput: Input(() =>
@ -201,7 +201,7 @@ public sealed class RetailLocalPlayerFrameControllerTests
public void HiddenPoseIsDirtyOnlyWhenACompleteObjectQuantumRuns()
{
PlayerMovementController controller = CreateController();
var local = new RetailLocalPlayerFrameController(
var local = CreateFrame(
canPresentPlayer: () => true,
getController: () => controller,
movementInput: Input(() => new MovementInput()),
@ -228,7 +228,7 @@ public sealed class RetailLocalPlayerFrameControllerTests
int projections = 0;
int preNetwork = 0;
int postNetwork = 0;
var local = new RetailLocalPlayerFrameController(
var local = CreateFrame(
canPresentPlayer: () => true,
getController: () => controller,
movementInput: Input(() =>
@ -297,6 +297,65 @@ public sealed class RetailLocalPlayerFrameControllerTests
private static IMovementInputSource Input(Func<MovementInput> capture) =>
new TestMovementInputSource(capture);
private static RetailLocalPlayerFrameController CreateFrame(
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<RetailObjectClockDisposition>? objectClockDisposition = null) =>
new(
new TestLocalPlayerFrameRuntime(
canPresentPlayer,
getController,
resolveLocalEntityId,
handleTargeting,
isHidden,
project,
sendPreNetwork,
sendPostNetwork,
objectClockDisposition),
movementInput);
private sealed class TestLocalPlayerFrameRuntime(
Func<bool> canPresentPlayer,
Func<PlayerMovementController?> getController,
Func<uint> resolveLocalEntityId,
Action handleTargeting,
Func<bool> isHidden,
Action<PlayerMovementController, MovementResult, bool> project,
Action<PlayerMovementController, MovementResult, bool> sendPreNetwork,
Action<PlayerMovementController, bool> sendPostNetwork,
Func<RetailObjectClockDisposition>? objectClockDisposition)
: ILocalPlayerFrameRuntime
{
public bool CanPresentPlayer => canPresentPlayer();
public PlayerMovementController? Controller => getController();
public uint ResolveLocalEntityId() => resolveLocalEntityId();
public void HandleTargeting() => handleTargeting();
public bool IsHidden => isHidden();
public RetailObjectClockDisposition ObjectClockDisposition =>
objectClockDisposition?.Invoke() ?? RetailObjectClockDisposition.Advance;
public void Project(
PlayerMovementController controller,
MovementResult movement,
bool hidden) => project(controller, movement, hidden);
public void SendPreNetwork(
PlayerMovementController controller,
MovementResult movement,
bool hidden) => sendPreNetwork(controller, movement, hidden);
public void SendPostNetwork(
PlayerMovementController controller,
bool hidden) => sendPostNetwork(controller, hidden);
}
private sealed class TestMovementInputSource(Func<MovementInput> capture)
: IMovementInputSource
{