Skip to content

fix(battleship): no shots until both fleets are deployed - #33

Merged
Github-Samuel merged 2 commits into
mainfrom
fix/23-battleship-ready-gate
Jul 19, 2026
Merged

Github-Samuel merged 2 commits into
mainfrom
fix/23-battleship-ready-gate

Conversation

@Epixx1337

Copy link
Copy Markdown
Collaborator

The first player to deploy could fire on an opponent who was still shuffling
their board.

Nothing server-side knew what "ready" meant. The session was created with
turn = sides[1] the instant the lobby started, and the only gate on a move
was g.turn ~= mySide. Board placement lived entirely in the client:
confirmPlacement() flipped its own phase to 'firing' and set
myTurn = humanSide === '1' without telling anyone, so player 1 could shoot
immediately, and player 2 ate a buffered shot the moment they finished
shuffling - with their layout fixed after player 1's.

Move the readiness handshake to the server, which is the only place both
clients agree:

  • Sessions carry ready = {}, and games opt in with requiresSetup
    (battleship only - chess/connectfour have no setup phase, and wordle
    bypasses turn logic via freeRelay, so both are unaffected).
  • New games:setupReady callback marks a side ready, tells the other side
    ('oppReady'), and once BOTH are in, picks who moves first and pushes
    'begin' to both clients.
  • The move handler rejects everything before that with "Opponent is still
    deploying", so the client gate can no longer be bypassed.

Client: confirmPlacement() now reports ready and enters a new 'waiting'
phase instead of starting the match itself; firing begins only on 'begin',
which is also what decides myTurn. The waiting state shows "Waiting for X to
deploy…". The pre-existing pendingShot buffer is kept purely as a safety net

  • with the gate in place a shot can no longer arrive early.

Not verified in-game (no FiveM server here); reasoned through the engine and
typechecked. Worth a two-player smoke test before merge.

Fixes #23

Epixx1337 and others added 2 commits July 18, 2026 11:16
The first player to deploy could fire on an opponent who was still shuffling
their board.

Nothing server-side knew what "ready" meant. The session was created with
turn = sides[1] the instant the lobby started, and the only gate on a move
was `g.turn ~= mySide`. Board placement lived entirely in the client:
confirmPlacement() flipped its own phase to 'firing' and set
myTurn = humanSide === '1' without telling anyone, so player 1 could shoot
immediately, and player 2 ate a buffered shot the moment they finished
shuffling - with their layout fixed after player 1's.

Move the readiness handshake to the server, which is the only place both
clients agree:

- Sessions carry `ready = {}`, and games opt in with `requiresSetup`
  (battleship only - chess/connectfour have no setup phase, and wordle
  bypasses turn logic via freeRelay, so both are unaffected).
- New games:setupReady callback marks a side ready, tells the other side
  ('oppReady'), and once BOTH are in, picks who moves first and pushes
  'begin' to both clients.
- The move handler rejects everything before that with "Opponent is still
  deploying", so the client gate can no longer be bypassed.

Client: confirmPlacement() now reports ready and enters a new 'waiting'
phase instead of starting the match itself; firing begins only on 'begin',
which is also what decides myTurn. The waiting state shows "Waiting for X to
deploy…". The pre-existing pendingShot buffer is kept purely as a safety net
- with the gate in place a shot can no longer arrive early.

Not verified in-game (no FiveM server here); reasoned through the engine and
typechecked. Worth a two-player smoke test before merge.
@Github-Samuel
Github-Samuel merged commit b2fdf37 into main Jul 19, 2026
1 check passed
@Github-Samuel
Github-Samuel deleted the fix/23-battleship-ready-gate branch July 19, 2026 22:53
Github-Samuel pushed a commit that referenced this pull request Sep 15, 2026
Readiness was client-only, so the first player to deploy could fire on an opponent still arranging their board. The ready handshake now lives server-side: games opt in with requiresSetup, a setupReady callback marks each side ready, and firing only begins once both are in and the server has picked who moves first. The move handler rejects shots before that, so the client gate can't be bypassed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

battleship app

2 participants