refactor(runtime): extract local teleport and player mode

Move local teleport, player-mode, animation, shadow, and sealed-dungeon lifetimes out of GameWindow. Make player-mode entry transactional and isolate approach completions by controller lifetime and approach generation so stale callbacks cannot affect replacements.

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Erik 2026-07-22 02:50:15 +02:00
parent c557038353
commit eeb0f6b45c
29 changed files with 3311 additions and 1073 deletions

View file

@ -1092,7 +1092,10 @@ public sealed class PlayerMovementController
// route through here. When PhysicsDiagnostics.ProbeCellEnabled is
// off this collapses to a single bool-compare + assignment — zero
// logging cost.
private void UpdateCellId(uint newCellId, string reason)
private void UpdateCellId(
uint newCellId,
string reason,
bool publishRenderRoot = true)
{
if (newCellId != CellId && PhysicsDiagnostics.ProbeCellEnabled)
{
@ -1109,7 +1112,8 @@ public sealed class PlayerMovementController
// jump-looping near the cottage doorway clobbered the render root every tick → the render
// rooted at the NPC's tiny connector cell → only its ~8-tri shell drew, rest = GL clear
// color = the cottage doorway "blue-hole" flap (diagnosed 2026-06-03 via [flap-cam]/[shell]).
_physics.UpdatePlayerCurrCell(newCellId);
if (publishRenderRoot)
_physics.UpdatePlayerCurrCell(newCellId);
}
public void SetPosition(Vector3 pos, uint cellId)
@ -1127,11 +1131,47 @@ public sealed class PlayerMovementController
/// directly via <c>SnapToCell</c> rather than delta-syncing through the setter.
/// </summary>
public void SetPosition(Vector3 pos, uint cellId, Vector3 cellLocal)
=> SetPositionCore(
pos,
cellId,
cellLocal,
publishSharedState: true);
/// <summary>
/// Builds a new local controller's canonical body pose without publishing
/// the renderer's global current cell or mutating the incarnation-stable
/// PositionManager. Player-mode entry commits those shared edges only
/// after camera, shadow, and host preparation succeeds.
/// </summary>
internal void PreparePositionForCommit(
Vector3 pos,
uint cellId,
Vector3 cellLocal) =>
SetPositionCore(
pos,
cellId,
cellLocal,
publishSharedState: false);
internal void CommitPreparedPosition()
{
_physics.UpdatePlayerCurrCell(CellId);
PositionManager?.UnStick();
}
private void SetPositionCore(
Vector3 pos,
uint cellId,
Vector3 cellLocal,
bool publishSharedState)
{
_body.SnapToCell(cellId, pos, cellLocal);
_prevPhysicsPos = pos;
_currPhysicsPos = pos;
UpdateCellId(_body.CellPosition.ObjCellId, "teleport");
UpdateCellId(
_body.CellPosition.ObjCellId,
"teleport",
publishSharedState);
// Treat as grounded after a server-side position snap.
_body.TransientState = TransientStateFlags.Contact
@ -1164,7 +1204,8 @@ public sealed class PlayerMovementController
// tears down any active stick. (StopInterpolating/UnConstrain have no
// armed acdream counterparts — no local-player InterpolationManager,
// constraint leash unarmed per #167.)
PositionManager?.UnStick();
if (publishSharedState)
PositionManager?.UnStick();
// Reset the edge tracker: the stop wiped the motion state, so keys
// still physically held must re-fire as press edges on the next
// Update (matches the pre-W6 level-triggered behavior of walking