Watch feature was half-implemented since day one and stayed broken through
the typed-protobuf migration:
1. Server (WatchGameHandler): only pushed the WatchGameSuccessResponse ack.
A mid-game watcher joined a blank scene because the server never told
them who the players were or what moves had been played.
2. Client: had a Watch button that sent the request but no event bus
handler for GAME_WATCH_SUCCESSFUL, so nothing happened visually.
Server fix:
- On successful watch, if room.status == STARTING, bootstrap the watcher
with a synthesised GameStartingResponse (player ids, nicknames, board
size) and replay every move in room.getMoveHistory() as individual
GameMoveSuccessResponse messages on that channel.
- Black/white player lookup uses room.getBlackPlayerId/getWhitePlayerId
against the clientSideMap so we don't reassign roles.
- Move replay resolves playerNickname from the same map.
Client fix:
- menu-ui.js: new GAME_WATCH_SUCCESSFUL handler flips gameState.isSpectating
= true. The subsequent GameStartingResponse flows through the existing
menu-scene handler (transitions to GameScene) and the existing
game-state-service handler (populates room state, resets moves).
- Move replay events propagate through the global GAME_MOVE_SUCCESS handler
in game-state-service before GameScene.create() runs, so GameScene's
existing rejoin/spectate loop at create() renders every stone.
Last Build & Test run failed with exit 127 "./gradlew: No such file or
directory". After the Maven-to-Gradle conversion the wrapper lives at
server/gradlew, not at the repo root. Two fixes:
1. Workflow invokes server/gradlew directly instead of ./gradlew.
2. Mark server/gradlew as executable (mode 100755) in the git index so the
Linux runner can exec it without a chmod step.
Phase 03 cleanup — now that typed records replace JSON envelopes and inner
JSON payloads, remove the helpers that served them:
- delete common/entity/Msg.java (envelope record)
- delete common/utils/JsonUtils.java (gson wrapper)
- delete common/helper/MapHelper.java (JSON dict builder)
- delete common/helper/TimeHelper.java (orphaned)
- delete common/transfer/{ByteKit,ByteLink,TransferProtocolUtils}.java (TCP framing)
- delete common/handler/DefaultDecoder.java (TCP framing)
- delete common/enums/ServerEventCode.java (string keys for reflection dispatch)
- delete common/enums/ClientEventCode.java (no longer referenced after proto migration)
- drop com.google.code.gson:gson from build.gradle.kts
All 37 unit tests still pass.
Replace every UnsupportedOperationException stub in RequestDispatcher with a
real handler call. All 15 handlers now live under com.miti99.caro.server.event.handler
and take a typed ClientRequest record, emitting typed Response protos via
ChannelUtils.push.
Handlers ported:
- SetClientInfoHandler, SetNicknameHandler
- CreateRoomHandler, CreatePveRoomHandler, GetRoomsHandler, JoinRoomHandler
- GameStartingHandler, GameReadyHandler
- GameMoveHandler (full PVP + PVE AI + game-over broadcast)
- GameResetHandler (noop, never wired before)
- WatchGameHandler, WatchGameExitHandler
- ClientExitHandler, ClientOfflineHandler
WebsocketTransferHandler.clientOfflineEvent now dispatches to ClientOfflineHandler.
RoomClearTask reuses ClientExitHandler for stale-room cleanup.
All 37 unit tests pass.
Version change:
- Drop the "-beta" suffix across all version declarations.
- server/build.gradle.kts: version = "0.0.1"
- client/package.json: "version": "0.0.1"
- client/package-lock.json: regenerated
- server/Dockerfile: COPY path references the new jar filename
- All docs + README refer to caro-server-0.0.1.jar.
Terminology cleanup:
- Replace the word "frontend" with "client" so the whole project uses
one consistent term (server / client).
- README.md Credits section: "Frontend build tool" -> "Client build tool".
- No other "frontend" occurrences found in code or docs.
Also correct two lingering Maven-era stale paths in deployment-guide.md
("server/target/..." -> Gradle output location) that slipped past the
earlier Maven-to-Gradle commit.
Versioning note in codebase-summary.md simplified to plain MAJOR.MINOR.PATCH
(dropped the "-beta suffix during pre-1.0" clause).
Validation: gradlew clean shadowJar + test passes (37 tests on Java 25);
client build succeeds with new package name/version.
Modernization (opportunistic, low-risk only):
- Convert Msg (WebSocket JSON envelope) from mutable POJO to record.
Gson 2.11 natively supports record serialization via canonical
constructor + accessor methods, so wire format is preserved
(null components still skipped by default). Update both producers
(ChannelUtils) and consumer (WebsocketTransferHandler).
- Convert 3 switch statements to switch expressions:
GomokuHelper.getWinnerMessage (GameResult -> String, exhaustive),
GomokuHelper board-cell rendering (PieceType -> char),
GomokuAI.getNextMove (difficulty -> strategy),
ServerEventListener_CODE_ROOM_CREATE_PVE.getDifficultyName.
- Sprinkle var for obvious local types in ChannelUtils and
WebsocketTransferHandler where RHS type is self-evident.
Non-goals preserved: no Netty handler rewrites, no threading changes,
no sealed types, no pattern matching in switches.
Validation: mvn verify on Java 25 — all 37 tests pass.
- Move all 11 shared sub-packages (channel, entity, enums, exception,
features, handler, helper, print, robot, transfer, utils) under
com.miti99.caro.common.
- Move server sub-packages (event, handler, proxy, timer) + SimpleServer
+ ServerContains under com.miti99.caro.server.
- Move tests under com.miti99.caro.common.{helper,robot}.tests.
- Rewrite package declarations and imports across all 58 .java files via
regex script (server rules applied before common to avoid overlap).
- Update <mainClass> in server/pom.xml to com.miti99.caro.server.SimpleServer.
- Update .proto files' package + java_package to com.miti99.caro.common.entity
(for future regeneration).
- Fix generate.sh relative output path (common/ no longer exists).
- Include rewrite-packages.py script under plans/ for auditability.
Note: protoc-generated ClientTransferData.java / ServerTransferData.java
retain internal_static_* variable names and embedded descriptor byte strings
with the old package — these are implementation details that do not affect
the public Java package and preserve protobuf wire compatibility.
Validation: mvn -f server/pom.xml clean verify on Java 25 — all 37 tests
pass (29 GomokuHelperTest + 8 GomokuAITest).