fix(app): Phase B.2 — use server position directly, fix yaw wrap + turn spam

Three more fixes from the diagnostic dump:

1. Initial position: PhysicsEngine.Resolve was mapping the player
   into an indoor EnvCell (foundry at Z=66) when they're standing
   on outdoor terrain at Z=93+. The cell-containment check was too
   aggressive for initial placement. Now uses the server-sent
   position directly — the server already gave us a valid position.

2. Yaw unbounded: mouse delta accumulated without wrapping, growing
   to 24+ radians. Now wraps to [-PI, PI] after every turn.

3. Turn command spam: MouseDeltaX > 0.5 threshold was too low for
   raw pixel deltas. Any mouse jitter triggered turnCmd flips every
   frame → stateChanged=True → MoveToState flood to the server.
   Mouse turning now only affects yaw directly; turn COMMANDS only
   come from A/D keyboard (matching retail client behavior where
   mouse-look doesn't generate a TurnRight/TurnLeft command).

265 tests still green.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-12 14:58:25 +02:00
parent 6202c5d153
commit 97c17c5bc3
3 changed files with 39 additions and 8 deletions

View file

@ -79,6 +79,10 @@ public sealed class PlayerMovementController
if (input.TurnLeft)
Yaw += TurnSpeed * dt;
Yaw -= input.MouseDeltaX * MouseTurnSensitivity;
// Wrap yaw to [-PI, PI] so it doesn't grow unbounded and cause
// NaN/overflow in sin/cos and confuse the chase camera offset.
while (Yaw > MathF.PI) Yaw -= 2f * MathF.PI;
while (Yaw < -MathF.PI) Yaw += 2f * MathF.PI;
// 2. Compute movement delta in the player's facing direction.
float speed = input.Run ? RunSpeed : WalkSpeed;
@ -130,12 +134,17 @@ public sealed class PlayerMovementController
sidestepSpeed = speed * 0.5f / WalkSpeed;
}
if (input.TurnRight || input.MouseDeltaX > 0.5f)
// Turn commands from KEYBOARD only (A/D). Mouse turning is applied
// directly to Yaw above and doesn't generate a turn command — if it
// did, mouse jitter would flip turnCmd between TurnRight/TurnLeft
// every frame, causing stateChanged=True on every frame and flooding
// the server with MoveToState spam.
if (input.TurnRight)
{
turnCmd = 0x6500000Du; // TurnRight
turnSpeed = TurnSpeed;
}
else if (input.TurnLeft || input.MouseDeltaX < -0.5f)
else if (input.TurnLeft)
{
turnCmd = 0x6500000Eu; // TurnLeft
turnSpeed = TurnSpeed;