Route room inputs through one toRoom helper, iterate live seats with
connected(), and share seating and reconnect-window code. Declare every
error code once in errcodes.go, which the web vocabulary test now reads.
Fixes:
- a kicked player is released before being told, so their next action
is answered not_in_a_room
- a rejected word is counted before move_rejected is sent
- room code draw and registration share one lock hold
- two concurrent resumes with one token can no longer both take a seat
- multiplayer tests ready guests one at a time instead of racing
Behaviour changes:
- opening or joining another room while a game is running is refused
with already_in_a_game
- resigning in the lobby answers game_not_started
room.go was 1919 lines with every room concern in one file. Pure
moves, no signature or behaviour changes: room.go keeps the struct,
its constructor, the input loop and the small seat-authority helpers;
room_inputs.go the message types; room_lobby.go seating and the lobby
between games; room_game.go everything that touches a running game;
room_presence.go the reconnect window and resume; room_chat.go the
room's own conversation; bot_board.go the bot's frozen view of a
position.
session.go was two unrelated halves in one 762-line file: the socket
(session.go, kept) and the protocol (dispatch.go, new) — dispatch and
the handshake/resume flow it routes into.
Verified with go build, go vet, golangci-lint and go test -race, and
by counting: every one of the 89 room.go and 27 session.go top-level
declarations appears in the split exactly once.