refactor(input): extract the gameplay frame

This commit is contained in:
Erik 2026-07-22 01:38:40 +02:00
parent 0bc9fda9de
commit c557038353
24 changed files with 2433 additions and 559 deletions

View file

@ -230,12 +230,15 @@ public sealed class UpdateFrameOrchestratorTests
Assert.DoesNotContain(ownerFields, field => field.FieldType == typeof(GameWindow));
if (owner == typeof(AcDream.App.Input.RetailLocalPlayerFrameController))
{
// Checkpoint B deliberately leaves this single legacy callback
// composition for D/F, which own input capture and the mutable
// player-mode/controller slot. No other phase owner may add one.
// Checkpoint D replaced input capture with its typed owner.
// Checkpoint F still owns the remaining player presentation
// callback composition. No other phase owner may add one.
Assert.Contains(
ownerFields,
field => typeof(Delegate).IsAssignableFrom(field.FieldType));
Assert.DoesNotContain(
ownerFields,
field => field.FieldType == typeof(Func<AcDream.App.Input.MovementInput>));
continue;
}
Assert.DoesNotContain(
@ -364,7 +367,7 @@ public sealed class UpdateFrameOrchestratorTests
AssertAppearsInOrder(
source,
"_streamingFrame.Tick();",
"_inputDispatcher?.Tick();",
"_gameplayInputFrame!.Tick(frameTiming);",
"_liveFrameCoordinator.Tick(frameDelta);");
Assert.DoesNotContain("_streamingController.Tick(observerCx", source,
StringComparison.Ordinal);
@ -373,6 +376,152 @@ public sealed class UpdateFrameOrchestratorTests
Assert.DoesNotContain("DrainRescued()", source, StringComparison.Ordinal);
}
[Fact]
public void GameWindow_DelegatesTheCompleteGameplayInputFrameBody()
{
string source = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Rendering",
"GameWindow.cs"));
Assert.Contains("new AcDream.App.Input.GameplayInputFrameController(", source);
Assert.Equal(1, CountOccurrences(source, "_gameplayInputFrame!.Tick(frameTiming);"));
Assert.DoesNotContain("_inputDispatcher?.Tick()", source,
StringComparison.Ordinal);
Assert.DoesNotContain("TryTakeRawSample", source,
StringComparison.Ordinal);
Assert.DoesNotContain("_combatAttackController?.Tick()", source,
StringComparison.Ordinal);
Assert.DoesNotContain("CaptureMovementInput", source,
StringComparison.Ordinal);
Assert.DoesNotContain("EndMouseLookAndRestoreCursor", source,
StringComparison.Ordinal);
Assert.DoesNotContain("HideCursorForMouseLook", source,
StringComparison.Ordinal);
Assert.DoesNotContain("RestoreCursorAfterMouseLook", source,
StringComparison.Ordinal);
Assert.DoesNotContain("CanStartLiveCombatAttack", source,
StringComparison.Ordinal);
Assert.DoesNotContain("SendLiveCombatAttack", source,
StringComparison.Ordinal);
Assert.DoesNotContain("PreparePlayerForAttackRequest", source,
StringComparison.Ordinal);
Assert.DoesNotContain("DumpMovementTruthOutbound", source,
StringComparison.Ordinal);
Assert.DoesNotContain("DumpMovementTruthServerEcho", source,
StringComparison.Ordinal);
Assert.DoesNotContain("wantCaptureMouse: ()", source,
StringComparison.Ordinal);
Assert.Equal(5, CountOccurrences(
source,
"_gameplayInputFrame?.EndMouseLook();"));
Assert.Contains(
"new(\"mouse capture\", () => _gameplayInputFrame?.EndMouseLook())",
source,
StringComparison.Ordinal);
AssertAppearsInOrder(
source,
"_teleportTransit.CanBegin(teleportSequence)",
"_gameplayInputFrame?.EndMouseLook();",
"_teleportTransit.QueueStart(teleportSequence)");
AssertAppearsInOrder(
source,
"private void OnFocusChanged(bool focused)",
"if (!focused)",
"_gameplayInputFrame?.EndMouseLook();");
}
[Fact]
public void GameplayInputOwnersUseTypedSeamsWithoutGameWindowBackReferences()
{
Type[] owners =
[
typeof(AcDream.App.Input.GameplayInputFrameController),
typeof(AcDream.App.Input.DispatcherMovementInputSource),
typeof(AcDream.App.Input.MouseLookController),
];
foreach (Type owner in owners)
{
FieldInfo[] fields = owner.GetFields(
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.DoesNotContain(fields, field => field.FieldType == typeof(GameWindow));
Assert.DoesNotContain(
fields,
field => typeof(Delegate).IsAssignableFrom(field.FieldType));
}
Assert.DoesNotContain(
typeof(AcDream.App.Input.MouseLookController).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => field.FieldType == typeof(Silk.NET.Input.IMouse));
FieldInfo combatOperations = Assert.Single(
typeof(AcDream.App.Combat.CombatAttackController).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => field.Name == "_operations");
Assert.Equal(
typeof(AcDream.App.Combat.ICombatAttackOperations),
combatOperations.FieldType);
Assert.DoesNotContain(
typeof(AcDream.App.Combat.LiveCombatAttackOperations).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => typeof(Delegate).IsAssignableFrom(field.FieldType));
FieldInfo combatTargets = Assert.Single(
typeof(AcDream.App.Combat.LiveCombatAttackOperations).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => field.Name == "_targets");
Assert.Equal(
typeof(AcDream.App.Combat.ICombatAttackTargetSource),
combatTargets.FieldType);
Assert.DoesNotContain(
typeof(AcDream.App.Combat.CombatAttackTargetSource).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => typeof(Delegate).IsAssignableFrom(field.FieldType));
Assert.DoesNotContain(
typeof(AcDream.App.Combat.CombatAttackTargetSource).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => field.FieldType
== typeof(AcDream.App.Interaction.SelectionInteractionController));
FieldInfo outboundDiagnostics = Assert.Single(
typeof(AcDream.App.Input.LocalPlayerOutboundController).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => field.Name == "_diagnostic");
Assert.Equal(
typeof(AcDream.App.Input.IMovementTruthDiagnosticSink),
outboundDiagnostics.FieldType);
Assert.DoesNotContain(
typeof(AcDream.App.Input.MovementTruthDiagnosticController).GetFields(
BindingFlags.Instance | BindingFlags.NonPublic),
field => typeof(Delegate).IsAssignableFrom(field.FieldType));
string mouseLookSource = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Input",
"MouseLookController.cs"));
AssertAppearsInOrder(
mouseLookSource,
"controller?.StopMouseDrift",
"retail.FilterMouseDelta",
"_state.ApplyDelta");
string localPlayerSource = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Input",
"RetailLocalPlayerFrameController.cs"));
Assert.DoesNotContain("Func<MovementInput>", localPlayerSource,
StringComparison.Ordinal);
Assert.Contains("IMovementInputSource", localPlayerSource,
StringComparison.Ordinal);
}
private static UpdateFrameOrchestrator Create(
List<string> calls,
RecordingTeardown? teardown = null,