ModernWormfare — Async PvP Firing Fix

Automated Playwright run · 2026-07-26 · async turn-batch · dev server on :5080

Overview

Date: 2026-07-26

Mode: Async (turn-batch PvP) — client records a whole turn, server simulates deterministically, broadcasts a replay

Player: net (host, Worm race)

Map: Hills

Server: dev server on http://localhost:5080

Tester: automated via Playwright (headless Chromium)

Result: PASS — move + fire works; the shot is visible in the replay; turn advances; no SignalR disconnection.

What was broken

The previous async PvP pass ("Async PvP — Turn-Batch Multiplayer") left firing unusable:

Fixes applied

1. Resolve projectiles in-turn FIXED

GameSession.ProcessBatch now steps idle ticks after the player's input ticks until in-flight projectiles resolve (capped at ~5s), before OnTurnBoundary/AdvanceTurn. This mirrors sync mode's 90-tick weapon cooldown. The resolution frames are appended to the replay, so both players see the shot fly and land, and the projectile no longer leaks into the next turn.

2. Aim cone follows the worm FIXED

BuildTrajectoryEnvelopes now origins the trajectory cone at the movement-preview position when the preview is active, so the aim follows the worm as the player walks it.

3. No more teleport-back on fire FIXED

MovementPreview now freezes (keeps its last position, stops stepping) on non-movement actions instead of clearing the position, so the worm stays where the player moved it when they fire.

4. Fire auto-submits the turn FIXED

The moment the player fires, the turn batch auto-submits (once per turn via _firedThisTurn), so the replay — now extended with the projectile's flight — plays immediately. This gives PvE-like feedback: you fire, you see the shot, the turn ends.

Match flow exercised

1. Lobby (create)→ 2. Loadout→ 3. Begin Match→ 4. Record move (D)→ 5. Fire (Enter) → auto-submit→ 6. Replay (projectile flies)→ 7. Turn advances

Screenshots

Lobby with game created
1. Lobby — game created. net creates an async game on the Hills map.
Async game at turn start
2. Game start — net's turn (unit 1/3). Recording armed; the worm sits at its spawn with the Rocket selected, aim at 45°.
Worm moved right, aim cone following
3. Moved + aiming. Held D for ~0.9s: the live movement preview walks the worm to the right in real time, and the aim trajectory cone follows it (no longer pinned to the start spot).
Replay playing after fire
4. Replay playing after fire. Pressed Enter to fire → the turn auto-submitted → the replay plays, showing the recorded move and the Rocket in flight (post-fire resolution frames).
Turn advanced to unit 2/3
5. Turn advanced. After the replay, the turn rotated to unit 2/3 and recording re-armed. Server log: SubmitTurnBatch ticks=175 accepted=True, zero maximum message size disconnections.

Verification

Server log: SubmitTurnBatch: ... ticks=175, accepted=True

Disconnections (2 MiB message limit): 0

Unit tests: 394 pass, 0 fail (incl. ProcessBatch_Fire_ResolvesProjectileWithinTheTurn: projectile gone from post-turn world + visible in replay frames)

Preflight: dotnet build 0 errors · dotnet test green · dotnet format --verify-no-changes clean