From d5c1318a0f9692a219a1a9bd97ed77c7bc8f5374 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Sat, 11 Apr 2026 10:00:12 +0700 Subject: [PATCH] chore: remove shipped plans All 5 plans implemented and merged. Deleting to keep the plans/ directory focused on active work: - 260409-1701-caro-simplification (shipped d871cc2) - 260409-1812-web-gomoku-client (shipped 77e141c, later replaced by Phaser) - 260410-0913-phaser-web-client (shipped 22bb9c1) - 260410-1843-refactor-project-structure (shipped c71aa6a/5b68ee9/2d74117/1297b7d) - 260410-2101-websocket-protobuf-migration (shipped 945a249 through 42d94a2) plans/reports/ kept for historical cross-plan reports. --- .../phase-01-delete-dead-files.md | 80 ----- .../phase-02-clean-shared-code.md | 92 ------ .../phase-03-rewrite-server-events.md | 164 ---------- .../phase-04-rewrite-client-events.md | 144 -------- .../phase-05-integration-verify.md | 116 ------- plans/260409-1701-caro-simplification/plan.md | 46 --- .../phase-01-server-static-file-handler.md | 128 -------- .../phase-02-client-html-css.md | 170 ---------- .../phase-03-client-connection-state.md | 243 -------------- .../phase-04-client-board-rendering.md | 200 ------------ .../phase-05-client-ui-panels.md | 242 -------------- .../phase-06-client-audio.md | 172 ---------- .../phase-07-integration-test.md | 103 ------ plans/260409-1812-web-gomoku-client/plan.md | 89 ----- .../phase-01-project-scaffold.md | 100 ------ .../phase-02-services-layer.md | 190 ----------- .../phase-03-boot-menu-scenes.md | 206 ------------ .../phase-04-game-scene-board.md | 202 ------------ .../phase-05-gameover-spectator.md | 227 ------------- .../phase-06-polish-errors.md | 170 ---------- plans/260410-0913-phaser-web-client/plan.md | 101 ------ .../phase-01-deletions.md | 115 ------- .../phase-02-consolidate-java-sources.md | 125 ------- ...e-03-standalone-maven-and-rename-server.md | 225 ------------- ...package-rename-and-java25-modernization.md | 187 ----------- .../phase-05-rename-web-client-to-client.md | 124 ------- .../phase-06-docs-and-readme-sweep.md | 164 ---------- .../plan.md | 40 --- .../rewrite-packages.py | 50 --- .../phase-01-proto-schemas-and-build.md | 241 -------------- .../phase-02a-dispatcher-scaffolding.md | 308 ------------------ .../phase-02b-migrate-handlers.md | 192 ----------- .../phase-03-server-cleanup.md | 110 ------- .../phase-04-client-protobuf.md | 236 -------------- .../phase-05-infra-and-docs.md | 100 ------ .../phase-06-e2e-smoke-test.md | 90 ----- .../plan.md | 141 -------- 37 files changed, 5633 deletions(-) delete mode 100644 plans/260409-1701-caro-simplification/phase-01-delete-dead-files.md delete mode 100644 plans/260409-1701-caro-simplification/phase-02-clean-shared-code.md delete mode 100644 plans/260409-1701-caro-simplification/phase-03-rewrite-server-events.md delete mode 100644 plans/260409-1701-caro-simplification/phase-04-rewrite-client-events.md delete mode 100644 plans/260409-1701-caro-simplification/phase-05-integration-verify.md delete mode 100644 plans/260409-1701-caro-simplification/plan.md delete mode 100644 plans/260409-1812-web-gomoku-client/phase-01-server-static-file-handler.md delete mode 100644 plans/260409-1812-web-gomoku-client/phase-02-client-html-css.md delete mode 100644 plans/260409-1812-web-gomoku-client/phase-03-client-connection-state.md delete mode 100644 plans/260409-1812-web-gomoku-client/phase-04-client-board-rendering.md delete mode 100644 plans/260409-1812-web-gomoku-client/phase-05-client-ui-panels.md delete mode 100644 plans/260409-1812-web-gomoku-client/phase-06-client-audio.md delete mode 100644 plans/260409-1812-web-gomoku-client/phase-07-integration-test.md delete mode 100644 plans/260409-1812-web-gomoku-client/plan.md delete mode 100644 plans/260410-0913-phaser-web-client/phase-01-project-scaffold.md delete mode 100644 plans/260410-0913-phaser-web-client/phase-02-services-layer.md delete mode 100644 plans/260410-0913-phaser-web-client/phase-03-boot-menu-scenes.md delete mode 100644 plans/260410-0913-phaser-web-client/phase-04-game-scene-board.md delete mode 100644 plans/260410-0913-phaser-web-client/phase-05-gameover-spectator.md delete mode 100644 plans/260410-0913-phaser-web-client/phase-06-polish-errors.md delete mode 100644 plans/260410-0913-phaser-web-client/plan.md delete mode 100644 plans/260410-1843-refactor-project-structure/phase-01-deletions.md delete mode 100644 plans/260410-1843-refactor-project-structure/phase-02-consolidate-java-sources.md delete mode 100644 plans/260410-1843-refactor-project-structure/phase-03-standalone-maven-and-rename-server.md delete mode 100644 plans/260410-1843-refactor-project-structure/phase-04-package-rename-and-java25-modernization.md delete mode 100644 plans/260410-1843-refactor-project-structure/phase-05-rename-web-client-to-client.md delete mode 100644 plans/260410-1843-refactor-project-structure/phase-06-docs-and-readme-sweep.md delete mode 100644 plans/260410-1843-refactor-project-structure/plan.md delete mode 100644 plans/260410-1843-refactor-project-structure/rewrite-packages.py delete mode 100644 plans/260410-2101-websocket-protobuf-migration/phase-01-proto-schemas-and-build.md delete mode 100644 plans/260410-2101-websocket-protobuf-migration/phase-02a-dispatcher-scaffolding.md delete mode 100644 plans/260410-2101-websocket-protobuf-migration/phase-02b-migrate-handlers.md delete mode 100644 plans/260410-2101-websocket-protobuf-migration/phase-03-server-cleanup.md delete mode 100644 plans/260410-2101-websocket-protobuf-migration/phase-04-client-protobuf.md delete mode 100644 plans/260410-2101-websocket-protobuf-migration/phase-05-infra-and-docs.md delete mode 100644 plans/260410-2101-websocket-protobuf-migration/phase-06-e2e-smoke-test.md delete mode 100644 plans/260410-2101-websocket-protobuf-migration/plan.md diff --git a/plans/260409-1701-caro-simplification/phase-01-delete-dead-files.md b/plans/260409-1701-caro-simplification/phase-01-delete-dead-files.md deleted file mode 100644 index 90891fe..0000000 --- a/plans/260409-1701-caro-simplification/phase-01-delete-dead-files.md +++ /dev/null @@ -1,80 +0,0 @@ -## Phase 1: Delete Dead Files - -### Context Links -- [Plan overview](./plan.md) - -### Overview -- **Priority:** P1 (do first -- unblocks everything) -- **Status:** Pending -- **Effort:** 30m - -Pure deletion phase. No logic changes. Every file listed is either landlords-specific code or obsolete documentation. - -### Files to Delete - -**Common module -- Poker domain:** -- `landlords-common/src/main/java/org/nico/ratel/landlords/entity/Poker.java` -- `landlords-common/src/main/java/org/nico/ratel/landlords/entity/PokerSell.java` -- `landlords-common/src/main/java/org/nico/ratel/landlords/enums/PokerLevel.java` -- `landlords-common/src/main/java/org/nico/ratel/landlords/enums/PokerType.java` -- `landlords-common/src/main/java/org/nico/ratel/landlords/helper/PokerHelper.java` -- `landlords-common/src/main/java/org/nico/ratel/landlords/utils/LastCardsUtils.java` - -**Common module -- Old robot system:** -- `landlords-common/src/main/java/org/nico/ratel/landlords/robot/AbstractRobotDecisionMakers.java` -- `landlords-common/src/main/java/org/nico/ratel/landlords/robot/EasyRobotDecisionMakers.java` -- `landlords-common/src/main/java/org/nico/ratel/landlords/robot/MediumRobotDecisionMakers.java` -- `landlords-common/src/main/java/org/nico/ratel/landlords/robot/RobotDecisionMakers.java` - -**Common module -- Tests:** -- `landlords-common/src/test/java/org/nico/ratel/landlords/helper/tests/PokerHelperTest.java` -- `landlords-common/src/test/java/org/nico/ratel/landlords/robot/tests/MediumRobotDecisionMakersTests.java` - -**Common module -- Chinese i18n:** -- `landlords-common/src/main/resources/messages_zh_CN.properties` - -**Server module -- Landlord event handlers (6 files):** -- `landlords-server/.../event/ServerEventListener_CODE_GAME_LANDLORD_ELECT.java` -- `landlords-server/.../event/ServerEventListener_CODE_GAME_POKER_PLAY.java` -- `landlords-server/.../event/ServerEventListener_CODE_GAME_POKER_PLAY_PASS.java` -- `landlords-server/.../event/ServerEventListener_CODE_GAME_POKER_PLAY_REDIRECT.java` -- `landlords-server/.../robot/RobotEventListener_CODE_GAME_LANDLORD_ELECT.java` -- `landlords-server/.../robot/RobotEventListener_CODE_GAME_POKER_PLAY.java` - -**Client module -- Landlord event handlers (12 files):** -- `landlords-client/.../event/ClientEventListener_CODE_GAME_LANDLORD_CONFIRM.java` -- `landlords-client/.../event/ClientEventListener_CODE_GAME_LANDLORD_CYCLE.java` -- `landlords-client/.../event/ClientEventListener_CODE_GAME_LANDLORD_ELECT.java` -- `landlords-client/.../event/ClientEventListener_CODE_GAME_POKER_PLAY.java` -- `landlords-client/.../event/ClientEventListener_CODE_GAME_POKER_PLAY_CANT_PASS.java` -- `landlords-client/.../event/ClientEventListener_CODE_GAME_POKER_PLAY_INVALID.java` -- `landlords-client/.../event/ClientEventListener_CODE_GAME_POKER_PLAY_LESS.java` -- `landlords-client/.../event/ClientEventListener_CODE_GAME_POKER_PLAY_MISMATCH.java` -- `landlords-client/.../event/ClientEventListener_CODE_GAME_POKER_PLAY_ORDER_ERROR.java` -- `landlords-client/.../event/ClientEventListener_CODE_GAME_POKER_PLAY_PASS.java` -- `landlords-client/.../event/ClientEventListener_CODE_GAME_POKER_PLAY_REDIRECT.java` -- `landlords-client/.../event/ClientEventListener_CODE_SHOW_POKERS.java` - -**Root-level files:** -- `GOMOKU_CONVERSION_SUMMARY.md` -- `PROTOCO_CN.md` -- `UPDATE.md` -- `serverlist.json` -- `docker/` (entire directory) - -### Total: ~35 files/dirs deleted - -### Implementation Steps -1. Delete all files listed above via `git rm` -2. Run `mvn compile` -- expect failures (imports of deleted classes). Those are fixed in Phase 2. -3. Commit: `refactor: delete all landlords card game code and obsolete docs` - -### Risk Assessment -- **Risk:** Accidentally delete a file still referenced by kept code -- **Mitigation:** Phase 2 explicitly fixes all broken imports. Compile verification in Phase 5. -- **Likelihood:** Low (all files audited against grep results) - -### Success Criteria -- [ ] All listed files removed from repo -- [ ] No landlord/poker .java files remain -- [ ] Commit is clean and focused diff --git a/plans/260409-1701-caro-simplification/phase-02-clean-shared-code.md b/plans/260409-1701-caro-simplification/phase-02-clean-shared-code.md deleted file mode 100644 index 5c8999d..0000000 --- a/plans/260409-1701-caro-simplification/phase-02-clean-shared-code.md +++ /dev/null @@ -1,92 +0,0 @@ -## Phase 2: Clean Shared Code (Common Module) - -### Context Links -- [Plan overview](./plan.md) -- [Phase 1](./phase-01-delete-dead-files.md) (must complete first) - -### Overview -- **Priority:** P1 -- **Status:** Pending -- **Effort:** 1h -- **Blocked by:** Phase 1 - -Remove all landlords references from shared entities, enums, helpers, and printers. After this phase, the common module compiles cleanly with only Gomoku domain code. - -### Key Insights -- `Room.java` is already mostly clean (gomoku fields present). But it still has `setCurrentSellClient()` called from server code, and leftover `scoreRate`/`baseScore`/`score` fields. -- `ClientSide.java` has no `pokers` field (already removed) but still has `score`, `scoreInc`, `type` (ClientType = LANDLORD/PEASANT), and linked-list `next`/`pre` fields used for 3-player turn order. -- `ClientEventCode.java` and `ServerEventCode.java` are already cleaned up -- only Gomoku codes remain. No changes needed. -- `ClientRole.java` has `PLAYER, ROBOT, BLACK_PLAYER, WHITE_PLAYER` -- needs simplification. -- `ClientStatus.java` has `CALL_LANDLORD` -- remove it. -- `ClientType.java` (LANDLORD/PEASANT) -- delete entire enum, not applicable to Gomoku. -- `SimplePrinter.java` has `printPokers()` method referencing deleted PokerHelper -- must remove. -- `ClientEventListener.java` (client base class) has `lastPokers`, `lastSellClientNickname`, `lastSellClientType` static fields -- must remove. - -### Files to Modify - -| File | Changes | -|------|---------| -| `entity/Room.java` | Remove `scoreRate`, `baseScore`, `currentSellClient`, `firstSellClient`, `landlordPokers`, `landlordId`, `lastSellClient` and all their getters/setters. Keep gomoku fields. | -| `entity/ClientSide.java` | Remove `score`, `scoreInc`, `type` (ClientType), `next`, `pre`, `round` fields + getters/setters. Gomoku doesn't need linked-list player chaining (use Room's clientSideMap). | -| `enums/ClientRole.java` | Remove `PLAYER` and `ROBOT`. Keep `BLACK_PLAYER`, `WHITE_PLAYER`. Add `SPECTATOR`. | -| `enums/ClientStatus.java` | Remove `CALL_LANDLORD`. Keep `TO_CHOOSE`, `NO_READY`, `READY`, `WAIT`, `PLAYING`. | -| `enums/ClientType.java` | **Delete entire file** -- no concept of landlord/peasant in Gomoku. | -| `print/SimplePrinter.java` | Remove `printPokers()` method and `import Poker/PokerHelper`. Remove `pokerDisplayFormat` field. | -| `helper/I18nHelper.java` | Verify no reference to `messages_zh_CN.properties` (already handles fallback to en_US). | - -### Files to Delete -- `landlords-common/src/main/java/org/nico/ratel/landlords/enums/ClientType.java` - -### Implementation Steps - -1. **Delete `ClientType.java`** - -2. **Clean `Room.java`:** - - Remove fields: `scoreRate`, `baseScore` (and `getScore()`, `initScoreRate()`, `increaseRate()` methods) - - Verify no `landlordPokers`, `currentSellClient`, `firstSellClient`, `lastSellClient`, `landlordId` fields exist (grep confirms Room.java already lacks these -- they exist only in deleted server files that call non-existent setters) - - Actually: grep shows `room.setCurrentSellClient()` called from server files, but Room.java has no such method. These are in files being deleted in Phase 1. **No Room.java changes needed for these.** - - Remove: `scoreRate`, `baseScore`, `getScore()`, `getBaseScore()`, `setBaseScore()`, `getScoreRate()`, `setScoreRate()`, `initScoreRate()`, `increaseRate()` -- these are unused in Gomoku - -3. **Clean `ClientSide.java`:** - - Remove `type` field (ClientType) + getter/setter - - Remove `score`, `scoreInc` fields + getter/setter/`addScore()` - - Remove `next`, `pre` fields + getter/setter (3-player circular list not needed for 2-player Gomoku; use Room.clientSideMap) - - Remove `round`, `resetRound()`, `addRound()`, `getRound()` - - Update `init()` to remove references to removed fields - -4. **Clean `ClientRole.java`:** - - Remove `PLAYER` and `ROBOT` - - Add `SPECTATOR` - - Result: `BLACK_PLAYER, WHITE_PLAYER, SPECTATOR` - -5. **Clean `ClientStatus.java`:** - - Remove `CALL_LANDLORD` - -6. **Clean `SimplePrinter.java`:** - - Remove `import org.nico.ratel.landlords.entity.Poker` - - Remove `import org.nico.ratel.landlords.helper.PokerHelper` - - Remove `pokerDisplayFormat` static field - - Remove `printPokers()` method - -7. **Clean `ClientEventListener.java` (client module base class):** - - Remove `import org.nico.ratel.landlords.entity.Poker` - - Remove static fields: `lastPokers`, `lastSellClientNickname`, `lastSellClientType` - - Remove `initLastSellInfo()` method - -8. Run `mvn compile -pl landlords-common` -- must pass - -### Risk Assessment -- **Risk:** Removing `next`/`pre` from ClientSide breaks server event handlers that use circular linked list for turn order -- **Mitigation:** Server handlers are rewritten in Phase 3 to use Room's `isPlayerTurn()` / `currentTurn` instead. Phase 3 must not use `client.getNext()`. -- **Likelihood:** Medium -- **Impact:** Compile error (caught immediately) - -### Security Considerations -None -- no auth/data changes. - -### Success Criteria -- [ ] `ClientType.java` deleted -- [ ] No `import.*Poker` in any kept file -- [ ] No `score`/`scoreRate`/`baseScore` in Room or ClientSide -- [ ] ClientRole has exactly: `BLACK_PLAYER, WHITE_PLAYER, SPECTATOR` -- [ ] `mvn compile -pl landlords-common` passes diff --git a/plans/260409-1701-caro-simplification/phase-03-rewrite-server-events.md b/plans/260409-1701-caro-simplification/phase-03-rewrite-server-events.md deleted file mode 100644 index 42f7854..0000000 --- a/plans/260409-1701-caro-simplification/phase-03-rewrite-server-events.md +++ /dev/null @@ -1,164 +0,0 @@ -## Phase 3: Rewrite Server Event Handlers - -### Context Links -- [Plan overview](./plan.md) -- [Phase 2](./phase-02-clean-shared-code.md) (must complete first) -- Key domain files: `Board.java`, `GomokuHelper.java`, `GomokuAI.java`, `Room.java` - -### Overview -- **Priority:** P1 -- **Status:** Pending -- **Effort:** 2h -- **Blocked by:** Phase 2 - -Rewrite the server-side event handlers to implement Gomoku game flow. Create the missing `ServerEventListener_CODE_GAME_MOVE.java`. Fix existing handlers that still contain landlords logic. - -### Data Flow: Gomoku Game Lifecycle - -``` -Client Server - |-- CODE_ROOM_CREATE ---------->| Create room, assign client as BLACK_PLAYER - |<-- CODE_ROOM_CREATE_SUCCESS --| - | | - |-- CODE_ROOM_JOIN ------------>| Join room, assign as WHITE_PLAYER - |<-- CODE_ROOM_JOIN_SUCCESS ----| (to both players) - | | Auto-start: call CODE_GAME_STARTING - |<-- CODE_GAME_STARTING --------| (to both players: board state, who is black/white) - | | - |-- CODE_GAME_MOVE ------------>| Validate move via GomokuHelper - |<-- CODE_GAME_MOVE_SUCCESS ----| (broadcast to both + spectators) - | or CODE_GAME_MOVE_INVALID | - | or CODE_GAME_MOVE_OCCUPIED | - | or CODE_GAME_MOVE_NOT_YOUR_TURN | - | | - |<-- CODE_GAME_OVER -----------| (when GomokuHelper detects win/draw) -``` - -### Files to Create - -**`ServerEventListener_CODE_GAME_MOVE.java`** -- the core missing handler - -``` -Package: org.nico.ratel.landlords.server.event -``` - -Logic: -1. Parse `data` as JSON: `{ "row": int, "col": int }` -2. Get room from `ServerContains.getRoom(clientSide.getRoomId())` -3. Null-check room -> push `CODE_ROOM_PLAY_FAIL_BY_INEXIST` -4. Check `room.isPlayerTurn(clientSide.getId())` -> if false, push `CODE_GAME_MOVE_NOT_YOUR_TURN` -5. Check `room.getGameBoard().isValidMove(row, col)`: - - Out of bounds -> `CODE_GAME_MOVE_OUT_OF_BOUNDS` - - Position occupied -> `CODE_GAME_MOVE_OCCUPIED` -6. Call `GomokuHelper.makeMove(room, row, col, clientSide.getId())` -7. Build result JSON: `{ row, col, piece, playerId, playerNickname, nextPlayerId }` -8. Broadcast `CODE_GAME_MOVE_SUCCESS` to all players + spectators -9. Check `GomokuHelper.isGameOver(room)`: - - If yes, determine winner, broadcast `CODE_GAME_OVER` with `{ result, winnerNickname, board }` - - For PVE: if next turn is AI, trigger AI move via `GomokuAI.getNextMove()` and recurse - -**`RobotEventListener_CODE_GAME_MOVE.java`** -- AI move handler for PVE - -``` -Package: org.nico.ratel.landlords.server.robot -``` - -Logic: -1. Get room, get AI piece color from room -2. Call `GomokuAI.getNextMove(board, difficulty)` -3. Delegate to `ServerEventListener_CODE_GAME_MOVE` with the AI's move data - -### Files to Rewrite - -**`ServerEventListener_CODE_GAME_STARTING.java`** -- Currently distributes poker cards. Rewrite for Gomoku: -1. Get room -2. Assign players: first player = BLACK, second = WHITE -3. Set `room.setBlackPlayerId(first.getId())`, `room.setWhitePlayerId(second.getId())` -4. Set player roles: `first.setRole(ClientRole.BLACK_PLAYER)`, `second.setRole(ClientRole.WHITE_PLAYER)` -5. Set `room.setStatus(RoomStatus.STARTING)` -6. Set `room.setCurrentTurn(PieceType.BLACK)` -7. Reset board: `room.getGameBoard().reset()` -8. Build result JSON: `{ roomId, blackPlayer: {id, nickname}, whitePlayer: {id, nickname}, boardSize: 15 }` -9. Push `CODE_GAME_STARTING` to both players + spectators -10. For PVE: if AI is BLACK, trigger AI first move - -**`ServerEventListener_CODE_GAME_READY.java`** -- Currently checks for 3 players and uses `ClientRole.PLAYER`. Rewrite: -1. Change player count check from 3 to 2 -2. Replace `ClientRole.PLAYER` references with check for non-null channel (human player) -3. Remove Chinese log messages (`"房间状态"`, `"玩家状态"`) -4. When all ready, call `CODE_GAME_STARTING` - -**`ServerEventListener_CODE_ROOM_CREATE.java`** -- Currently calls `room.setCurrentSellClient()`. Rewrite: -1. Remove `room.setCurrentSellClient()` call -2. Set first player as `ClientRole.BLACK_PLAYER` -3. Rest is fine - -**`ServerEventListener_CODE_ROOM_CREATE_PVE.java`** -- Currently creates 2 robots for 3-player game. Rewrite: -1. Create room with human player -2. Create 1 AI robot (not 2) -- Gomoku is 2-player -3. Assign human as BLACK, AI as WHITE (or configurable) -4. Replace `RobotDecisionMakers.contains()` with simple difficulty range check (1-3) -5. Remove `client.setNext()`/`client.setPre()` linked-list wiring -6. Auto-start game immediately - -**`ServerEventListener_CODE_ROOM_JOIN.java`** -- Currently allows up to 3 players. Rewrite: -1. Change full-room check from `size == 3` to `size == 2` -2. Remove `next`/`pre` linked-list wiring -3. When 2nd player joins, auto-start game (call `CODE_GAME_STARTING`) -4. Remove Chinese comments - -**`ServerEventListener_CODE_CLIENT_EXIT.java`** -- Currently uses `ClientRole.PLAYER`. Update: -1. Replace `ClientRole.PLAYER` with check against `BLACK_PLAYER`/`WHITE_PLAYER` -2. Remove Chinese comments - -**`RoomClearTask.java`** -- Heavy landlords logic (robot substitution, poker custody). Rewrite: -1. Keep timeout-based room cleanup (waitingStatusInterval, liveTime) -2. Remove all robot-substitution logic (lines 76-130) -3. Remove references to `currentSellClient`, `lastSellClient`, `landlordId`, `setPokers`, `setType` -4. On timeout: just close the room and notify players -5. Remove `RobotEventListener` import and call - -**`RobotEventListener.java`** (interface) -- Keep but will only resolve `CODE_GAME_MOVE`: -1. No structural change needed, reflection-based lookup still works - -### Files to Verify (minor touch-ups) - -- `ServerEventListener_CODE_GAME_WATCH.java` -- likely uses Chinese comments, remove them -- `ServerEventListener_CODE_GAME_WATCH_EXIT.java` -- same -- `ServerEventListener_CODE_CLIENT_OFFLINE.java` -- verify no poker references -- `ServerEventListener_CODE_CLIENT_INFO_SET.java` -- verify clean -- `ServerEventListener_CODE_CLIENT_NICKNAME_SET.java` -- verify clean -- `ServerEventListener_CODE_GET_ROOMS.java` -- verify clean - -### Architecture: PVE Move Flow - -``` -Human makes move -> ServerEventListener_CODE_GAME_MOVE - -> validate + apply move - -> check game over? - -> if not over && next turn is AI: - -> GomokuAI.getNextMove(board, difficulty) - -> apply AI move to board via GomokuHelper - -> broadcast AI move as CODE_GAME_MOVE_SUCCESS - -> check game over again -``` - -No separate robot event listener needed for moves -- handle AI inline in CODE_GAME_MOVE handler to avoid complexity. Delete RobotEventListener_CODE_GAME_MOVE if created, or simply don't create it. - -**Revised approach:** Handle AI response inline in `ServerEventListener_CODE_GAME_MOVE.java` rather than via separate RobotEventListener. Simpler, fewer files, same behavior. - -### Risk Assessment - -| Risk | Likelihood | Impact | Mitigation | -|------|-----------|--------|------------| -| Breaking room join flow (2 vs 3 players) | Medium | High | Unit test: create room, join, verify auto-start | -| AI move infinite loop (AI triggers AI) | Low | High | Guard: only trigger AI if current turn belongs to AI player, and game not over | -| Race condition on concurrent moves | Low | Medium | Room operations already single-threaded per room via Netty event loop | - -### Success Criteria -- [ ] `ServerEventListener_CODE_GAME_MOVE.java` exists and handles move validation + win detection -- [ ] PVE mode creates 1 AI robot, not 2 -- [ ] Room join auto-starts at 2 players -- [ ] No references to Poker, PokerSell, PokerHelper, LastCardsUtils in any server file -- [ ] No Chinese text in any server file (except LICENSE) -- [ ] `mvn compile -pl landlords-server` passes diff --git a/plans/260409-1701-caro-simplification/phase-04-rewrite-client-events.md b/plans/260409-1701-caro-simplification/phase-04-rewrite-client-events.md deleted file mode 100644 index fc37a15..0000000 --- a/plans/260409-1701-caro-simplification/phase-04-rewrite-client-events.md +++ /dev/null @@ -1,144 +0,0 @@ -## Phase 4: Rewrite Client Event Handlers - -### Context Links -- [Plan overview](./plan.md) -- [Phase 2](./phase-02-clean-shared-code.md) (must complete first) -- Can run **in parallel** with Phase 3 (no file overlap) - -### Overview -- **Priority:** P1 -- **Status:** Pending -- **Effort:** 1.5h -- **Blocked by:** Phase 2 - -Rewrite client-side event handlers to display Gomoku game state. The client is a CLI app -- it reads server events and prints board/prompts to the console, then sends user input back. - -### Files to Rewrite - -**`ClientEventListener_CODE_GAME_STARTING.java`** -- Currently prints poker cards. Rewrite: -1. Parse server data: `{ roomId, blackPlayer: {id, nickname}, whitePlayer: {id, nickname}, boardSize }` -2. Print: "Game starting! You are [BLACK/WHITE]" -3. Print initial empty board via `GomokuHelper.formatBoardForDisplay()` -4. If player is BLACK, prompt for first move -5. Remove all Poker imports and references - -**`ClientEventListener_CODE_GAME_OVER.java`** -- Currently shows poker scores. Rewrite: -1. Parse: `{ result, winnerNickname, board }` -2. Print final board state -3. Print result: "Black wins!" / "White wins!" / "Draw!" via `GomokuHelper.getWinnerMessage()` -4. Remove score display logic -5. Call `ClientEventListener_CODE_GAME_READY.gameReady(channel)` to offer rematch - -**`ClientEventListener_CODE_SHOW_OPTIONS_PVE.java`** -- Currently calls `initLastSellInfo()`. Update: -1. Remove `initLastSellInfo()` call (method deleted in Phase 2) -2. Keep difficulty selection (Easy/Medium/Hard maps to 1/2/3) -3. Rest is clean - -**`ClientEventListener_CODE_SHOW_OPTIONS_SETTING.java`** -- Check for poker display format references: -1. Remove any `pokerDisplayFormat` references -2. Keep language selection if present - -**New: `ClientEventListener_CODE_GAME_MOVE_SUCCESS.java`** -- Handle successful move broadcast: -1. Parse: `{ row, col, piece, playerNickname, nextPlayerId }` -2. Print: "[playerNickname] placed [BLACK/WHITE] at (row, col)" -3. Print updated board via `GomokuHelper.formatBoardForDisplay()` -4. If it's this player's turn next, prompt for move input -5. Read input as "row,col", send `CODE_GAME_MOVE` to server with `{ row, col }` - -**New: `ClientEventListener_CODE_GAME_MOVE_INVALID.java`** -- Handle invalid move: -1. Print "Invalid move. Please try again." -2. Re-prompt for move input - -**New: `ClientEventListener_CODE_GAME_MOVE_OCCUPIED.java`** -- Handle occupied position: -1. Print "Position already occupied. Please choose another." -2. Re-prompt for move input - -**New: `ClientEventListener_CODE_GAME_MOVE_OUT_OF_BOUNDS.java`** -- Handle out of bounds: -1. Print "Move out of bounds. Board is 15x15 (0-14)." -2. Re-prompt for move input - -**New: `ClientEventListener_CODE_GAME_MOVE_NOT_YOUR_TURN.java`** -- Handle wrong turn: -1. Print "It's not your turn. Please wait." - -**New: `ClientEventListener_CODE_SHOW_BOARD.java`** -- Handle board display request: -1. This is a client-only code; may need special handling -2. Or simply handle "board" command locally in the move input loop - -### Files Unchanged (already clean) -- `ClientEventListener_CODE_CLIENT_CONNECT.java` -- `ClientEventListener_CODE_CLIENT_EXIT.java` -- `ClientEventListener_CODE_CLIENT_KICK.java` -- `ClientEventListener_CODE_CLIENT_NICKNAME_SET.java` -- `ClientEventListener_CODE_ROOM_CREATE_SUCCESS.java` -- `ClientEventListener_CODE_ROOM_JOIN_SUCCESS.java` -- `ClientEventListener_CODE_ROOM_JOIN_FAIL_BY_FULL.java` -- `ClientEventListener_CODE_ROOM_JOIN_FAIL_BY_INEXIST.java` -- `ClientEventListener_CODE_ROOM_PLAY_FAIL_BY_INEXIST.java` -- `ClientEventListener_CODE_SHOW_OPTIONS.java` -- `ClientEventListener_CODE_SHOW_OPTIONS_PVP.java` -- `ClientEventListener_CODE_SHOW_ROOMS.java` -- `ClientEventListener_CODE_PVE_DIFFICULTY_NOT_SUPPORT.java` -- `ClientEventListener_CODE_GAME_READY.java` -- `ClientEventListener_CODE_GAME_WATCH.java` -- `ClientEventListener_CODE_GAME_WATCH_SUCCESSFUL.java` - -### Move Input Pattern - -The client prompts for a move and sends it to server. Pattern used in new handlers: - -```java -String input = SimpleWriter.write(nickname, "move"); -// Parse "row,col" format -// Handle special commands: "board"/"b", "history"/"h", "exit"/"e" -if (input matches "\\d+,\\d+") { - String moveData = MapHelper.newInstance() - .put("row", row).put("col", col).json(); - pushToServer(channel, ServerEventCode.CODE_GAME_MOVE, moveData); -} else if (input is "board" or "b") { - // print board locally (need to store board state client-side or request from server) -} else if (input is "exit" or "e") { - pushToServer(channel, ServerEventCode.CODE_CLIENT_EXIT, null); -} -``` - -**Key decision:** Store board state client-side (in a static field on the listener or a shared client state object) so "board" and "history" commands work without server round-trip. - -### Client-Side State - -Add a simple static state holder (or use existing `User.java`): - -```java -// In ClientEventListener or a new small class -static Board localBoard = null; -static PieceType myPiece = null; -static String myNickname = null; -``` - -Set these in `CODE_GAME_STARTING` handler. Update board in `CODE_GAME_MOVE_SUCCESS` handler. - -### Files to Verify (Chinese text removal) -- `ClientEventListener_CODE_GAME_WATCH.java` -- grep found Chinese; remove -- `ClientEventListener_CODE_GAME_WATCH_SUCCESSFUL.java` -- check - -### SimpleClient.java Changes -- Remove `serverAddressSource` array (fetches from upstream ratel repo) -- When no `-h` flag provided, print error asking user to specify host instead of fetching server list -- Remove `getServerAddressList()` method -- Keep language selection logic (only en_US matters now) - -### Risk Assessment - -| Risk | Likelihood | Impact | Mitigation | -|------|-----------|--------|------------| -| Move input parsing errors (non "row,col" format) | Medium | Low | Validate format, re-prompt on bad input | -| Client board state out of sync with server | Low | Medium | Server is authoritative; client board is display-only. Re-sync on each CODE_GAME_MOVE_SUCCESS | -| Blocking on SimpleWriter.write() during opponent's turn | Low | Low | Existing pattern -- client blocks on stdin. Move prompt only shown when it's player's turn | - -### Success Criteria -- [ ] 5 new client event handler files created for Gomoku move codes -- [ ] `CODE_GAME_STARTING` prints board, not poker cards -- [ ] `CODE_GAME_OVER` shows winner without scores -- [ ] Client can display board and prompt for "row,col" input -- [ ] No Poker imports in any client file -- [ ] No Chinese text in any client file -- [ ] `mvn compile -pl landlords-client` passes diff --git a/plans/260409-1701-caro-simplification/phase-05-integration-verify.md b/plans/260409-1701-caro-simplification/phase-05-integration-verify.md deleted file mode 100644 index 49537e9..0000000 --- a/plans/260409-1701-caro-simplification/phase-05-integration-verify.md +++ /dev/null @@ -1,116 +0,0 @@ -## Phase 5: Integration Test & Compile Verify - -### Context Links -- [Plan overview](./plan.md) -- [Phase 3](./phase-03-rewrite-server-events.md), [Phase 4](./phase-04-rewrite-client-events.md) (must both complete first) - -### Overview -- **Priority:** P1 -- **Status:** Pending -- **Effort:** 1h -- **Blocked by:** Phase 3, Phase 4 - -Full build verification, fix remaining compile errors, clean up README, and validate the game flow works end-to-end. - -### Implementation Steps - -#### 1. Full Maven Build -```bash -mvn clean compile -``` -Fix any compile errors iteratively. Common expected issues: -- Missing imports of deleted classes in files not yet touched -- `ClientRole.PLAYER` references in files outside the main event handlers -- `ClientType` references anywhere -- `getNext()`/`getPre()` calls if any kept file uses them - -#### 2. Run Existing Tests -```bash -mvn test -``` -- `GomokuHelperTest.java` should pass (tests Gomoku logic, no poker deps) -- Deleted tests (PokerHelperTest, MediumRobotDecisionMakersTests) are gone -- no failures from those - -#### 3. Add Basic Gomoku Integration Test - -Create `landlords-common/src/test/java/org/nico/ratel/landlords/helper/tests/GomokuIntegrationTest.java`: -- Test full game flow in-memory: create Room, assign players, make moves, detect win -- Test draw detection (fill board) -- Test invalid move rejection (occupied, out of bounds, wrong turn) -- Test board reset - -#### 4. Clean Up README.md -- Remove references to original ratel/landlords -- Remove badge URLs pointing to ainilili/ratel -- Remove serverlist.json references -- Remove ecosystem links (go-ratel-client etc.) or mark as incompatible -- Remove bilibili video link -- Keep installation instructions, update to reflect current project -- Update game commands section (already correct for Gomoku) - -#### 5. Grep Sweep -- Verify No Leftovers - -Run these greps to ensure nothing was missed: - -```bash -# No poker/landlord references in kept Java files -grep -r "Poker\|PokerSell\|PokerHelper\|PokerLevel\|PokerType" --include="*.java" . -grep -r "landlord\|LANDLORD\|Landlord" --include="*.java" . -grep -r "LastCardsUtils\|lastCards\|lastPokers" --include="*.java" . - -# No Chinese characters in Java files -grep -rP "[\x{4e00}-\x{9fff}]" --include="*.java" . - -# No references to deleted event codes -grep -r "CODE_GAME_POKER\|CODE_GAME_LANDLORD\|CODE_SHOW_POKERS" --include="*.java" . -``` - -Allowed exceptions: -- `LandlordException.java` class name -- rename to `GameException.java` or leave (low priority) -- Package names contain `landlords` -- intentionally kept (see plan.md decision #1) -- `LICENSE` file -- keep as-is - -#### 6. Verify Game Flow Manually (if time permits) - -Start server: -```bash -java -jar landlords-server/target/landlords-server-1.4.0.jar -p 1024 -``` - -Start 2 clients: -```bash -java -jar landlords-client/target/landlords-client-1.4.0.jar -h 127.0.0.1 -p 1024 -``` - -Test flow: -1. Client 1: set nickname, create room -2. Client 2: set nickname, join room -3. Verify game auto-starts, board displays -4. Make alternating moves, verify board updates -5. Play to win condition, verify game over message - -### Cleanup Items (Low Priority, Optional) - -| Item | Rationale | -|------|-----------| -| Rename `LandlordException` to `GameException` | Cosmetic, low value | -| Rename modules `landlords-*` to `caro-*` | High churn, defer to separate PR | -| Remove `FormatPrinter.java` | Check if used; if not, delete | -| Remove `features/Features.java` | Check if only VERSION constant; if so, keep | -| Simplify `SimpleClient.java` server list fetching | Already addressed in Phase 4 | - -### Risk Assessment - -| Risk | Likelihood | Impact | Mitigation | -|------|-----------|--------|------------| -| Hidden compile error in untouched file | Medium | Low | Full `mvn compile` catches all | -| GomokuHelperTest relies on deleted code | Low | Low | Test file already reviewed -- uses only Gomoku classes | -| Manual test reveals game flow bug | Medium | Medium | Fix in this phase before merging | - -### Success Criteria -- [ ] `mvn clean compile` passes with zero errors -- [ ] `mvn test` passes -- all tests green -- [ ] Grep sweep shows no poker/landlord references in Java code (except package names and LandlordException) -- [ ] No Chinese characters in Java files -- [ ] README reflects current Gomoku project -- [ ] GomokuIntegrationTest covers: valid move, invalid move, win detection, draw detection diff --git a/plans/260409-1701-caro-simplification/plan.md b/plans/260409-1701-caro-simplification/plan.md deleted file mode 100644 index fb25af2..0000000 --- a/plans/260409-1701-caro-simplification/plan.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: "Caro/Gomoku Codebase Simplification" -description: "Strip landlords card game code, fix Gomoku game flow end-to-end, simplify to working client-server Gomoku" -status: pending -priority: P1 -effort: 6h -branch: master -tags: [cleanup, gomoku, simplification] -created: 2026-04-09 ---- - -# Caro/Gomoku Codebase Simplification - -## Overview - -Strip all leftover Chinese Landlords card game code from this Netty-based project, leaving a clean Gomoku (Five-in-a-Row) client-server application. The Gomoku domain classes (Board, GameMove, GomokuHelper, GomokuAI, PieceType, GameResult) already exist and are well-implemented. The main gap is the server/client event handlers still run landlords logic. - -## Phase Summary - -| # | Phase | Status | Effort | Blocked By | -|---|-------|--------|--------|------------| -| 1 | Delete dead files | Pending | 30m | - | -| 2 | Clean shared code (common module) | Pending | 1h | Phase 1 | -| 3 | Rewrite server event handlers | Pending | 2h | Phase 2 | -| 4 | Rewrite client event handlers | Pending | 1.5h | Phase 2 | -| 5 | Integration test & compile verify | Pending | 1h | Phase 3, 4 | - -## Phases - -- [Phase 1: Delete Dead Files](./phase-01-delete-dead-files.md) -- [Phase 2: Clean Shared Code](./phase-02-clean-shared-code.md) -- [Phase 3: Rewrite Server Event Handlers](./phase-03-rewrite-server-events.md) -- [Phase 4: Rewrite Client Event Handlers](./phase-04-rewrite-client-events.md) -- [Phase 5: Integration Test & Compile Verify](./phase-05-integration-verify.md) - -## Key Architectural Decisions - -1. **Keep module names as `landlords-*`** -- renaming Maven modules cascades into groupId, package names, imports across every file. High churn, zero functional value. Defer to a separate PR if desired. -2. **Keep WebSocket support** -- already wired, removing adds risk, keeping costs nothing. -3. **2-player rooms** -- Gomoku is 2-player. Change room full check from `size == 3` to `size == 2`. Auto-start when second player joins. -4. **Remove scoring system** -- Gomoku has no points/scoring. Strip `score`, `scoreRate`, `baseScore`, `scoreInc` from Room and ClientSide. -5. **PVE uses GomokuAI** -- Replace old robot system with single GomokuAI class. Delete AbstractRobotDecisionMakers, Easy/MediumRobotDecisionMakers, RobotDecisionMakers. - -## Rollback Plan - -Each phase is a separate commit. `git revert` any phase independently. Phase 1 (file deletion) is fully recoverable from git history. diff --git a/plans/260409-1812-web-gomoku-client/phase-01-server-static-file-handler.md b/plans/260409-1812-web-gomoku-client/phase-01-server-static-file-handler.md deleted file mode 100644 index 738aeea..0000000 --- a/plans/260409-1812-web-gomoku-client/phase-01-server-static-file-handler.md +++ /dev/null @@ -1,128 +0,0 @@ -# Phase 1: Server — Static File Handler - -## Context Links -- [WebsocketProxy.java](../../landlords-server/src/main/java/org/nico/ratel/landlords/server/proxy/WebsocketProxy.java) — pipeline setup -- [WebsocketTransferHandler.java](../../landlords-server/src/main/java/org/nico/ratel/landlords/server/handler/WebsocketTransferHandler.java) — WS handler - -## Overview -- **Priority:** P1 (blocks all client work for integration testing) -- **Status:** Pending -- **Effort:** 1.5h - -Add an HTTP static file handler to the existing Netty WebSocket pipeline so `http://localhost:1025/` serves the web client. The WebSocket upgrade at `/ratel` must continue working untouched. - -## Key Insight - -The current pipeline is: `IdleStateHandler -> HttpServerCodec -> ChunkedWriteHandler -> HttpObjectAggregator -> WebSocketServerProtocolHandler("/ratel") -> WebsocketTransferHandler`. - -`WebSocketServerProtocolHandler` only upgrades requests to `/ratel`. For any other URI, it passes through as a regular `FullHttpRequest`. We insert our `StaticFileHandler` **before** the WS protocol handler to intercept non-`/ratel` HTTP requests and serve files. Requests to `/ratel` get passed through to the WS handler as before. - -**Alternative considered:** Adding handler after WS handler. Rejected because `WebSocketServerProtocolHandler` may consume or reject non-upgrade HTTP requests. - -**Chosen approach:** Add handler before WS handler. Check URI: if `/ratel`, pass through via `ctx.fireChannelRead(msg)`. Otherwise, serve static file. - -## Architecture - -``` -Pipeline order (updated): - IdleStateHandler - HttpServerCodec - ChunkedWriteHandler - HttpObjectAggregator(8192) - StaticFileHandler <-- NEW: serves files or passes /ratel through - WebSocketServerProtocolHandler("/ratel") - WebsocketTransferHandler -``` - -**Data flow:** -1. HTTP GET `/` arrives as `FullHttpRequest` -2. `StaticFileHandler.channelRead0()` checks URI -3. If URI is `/ratel` -> `ctx.fireChannelRead(msg.retain())` (pass to WS handler) -4. If URI is `/` -> rewrite to `/index.html` -5. Load resource from classpath `static/` prefix -6. Set Content-Type from MIME map -7. Write `DefaultFullHttpResponse` with file bytes -8. Close connection (HTTP/1.1 keep-alive optional, not required) - -## Related Code Files - -### Files to Create -- `landlords-server/src/main/java/org/nico/ratel/landlords/server/handler/StaticFileHandler.java` - -### Files to Modify -- `landlords-server/src/main/java/org/nico/ratel/landlords/server/proxy/WebsocketProxy.java` — add handler to pipeline - -## Implementation Steps - -### Step 1: Create `StaticFileHandler.java` - -Location: `landlords-server/src/main/java/org/nico/ratel/landlords/server/handler/StaticFileHandler.java` - -```java -// Extends SimpleChannelInboundHandler -// MIME types map: .html->text/html, .css->text/css, .js->application/javascript, -// .json->application/json, .mp3->audio/mpeg, .jpg->image/jpeg, .png->image/png, .svg->image/svg+xml -``` - -Logic: -1. Check `msg.uri()`. If starts with `/ratel`, call `ctx.fireChannelRead(msg.retain())` and return. -2. Sanitize URI: strip query string, decode `%20` etc, reject `..` path traversal. -3. Map `/` to `/index.html`. -4. Build classpath path: `"static" + sanitizedUri`. -5. Load via `getClass().getClassLoader().getResourceAsStream(path)`. -6. If null -> 404 response. -7. Read all bytes into `ByteBuf`. -8. Build `DefaultFullHttpResponse(OK)`, set `Content-Type` and `Content-Length` headers. -9. Write and flush, close if not keep-alive. - -**Security:** Reject URIs containing `..` to prevent directory traversal. Only serve from `static/` classpath prefix. - -**Keep file under 200 lines.** The handler is straightforward — MIME map + resource loading + response building. - -### Step 2: Modify `WebsocketProxy.java` - -Add one line to the pipeline, before the WS handler: - -```java -.addLast(new StaticFileHandler()) // <-- NEW -.addLast("ws", new WebSocketServerProtocolHandler("/ratel")) -``` - -Import: `org.nico.ratel.landlords.server.handler.StaticFileHandler` - -## Todo List - -- [ ] Create `StaticFileHandler.java` with MIME map and classpath resource serving -- [ ] Handle `/ratel` passthrough (retain + fireChannelRead) -- [ ] Handle `/` -> `/index.html` redirect -- [ ] Handle 404 for missing resources -- [ ] Sanitize URI (reject `..`, strip query string) -- [ ] Add handler to `WebsocketProxy.java` pipeline -- [ ] Create placeholder `static/index.html` for testing -- [ ] Run `mvn clean compile` — must pass -- [ ] Run `mvn test` — must pass -- [ ] Manual test: start server, `curl http://localhost:1025/` returns HTML - -## Success Criteria - -- `http://localhost:1025/` returns `index.html` content with `Content-Type: text/html` -- `http://localhost:1025/css/style.css` returns CSS with correct MIME type -- `http://localhost:1025/js/game-board.js` returns JS with correct MIME type -- WebSocket at `ws://localhost:1025/ratel` still works (existing Java client connects) -- `mvn clean compile` and `mvn test` pass -- No path traversal vulnerability - -## Risk Assessment - -| Risk | Mitigation | -|------|------------| -| Handler breaks WS upgrade | Check URI first; `/ratel` requests passed through untouched | -| `FullHttpRequest` refcount leak | Call `msg.retain()` before fireChannelRead, use `ReferenceCountUtil` | -| Large files OOM | Game assets are tiny (<1MB total); read fully into memory is fine | -| Classpath resource not found in packaged JAR | Spring Boot Maven plugin repackages resources correctly; `getResourceAsStream` works in fat JARs | - -## Backwards Compatibility - -- Existing TCP Protobuf clients (port 1024) are unaffected — different port/pipeline -- Existing WebSocket clients connecting to `/ratel` are unaffected — passthrough logic -- No changes to game logic, event codes, or message format diff --git a/plans/260409-1812-web-gomoku-client/phase-02-client-html-css.md b/plans/260409-1812-web-gomoku-client/phase-02-client-html-css.md deleted file mode 100644 index b23a16f..0000000 --- a/plans/260409-1812-web-gomoku-client/phase-02-client-html-css.md +++ /dev/null @@ -1,170 +0,0 @@ -# Phase 2: Client — HTML Shell + CSS - -## Context Links -- [Phase 1](phase-01-server-static-file-handler.md) — server serves these files -- [Phase 3](phase-03-client-connection-state.md) — JS loaded by this HTML - -## Overview -- **Priority:** P1 -- **Status:** Pending -- **Effort:** 2h - -Create the single-page HTML shell and all CSS. The HTML defines all screens (hidden by default, shown via JS class toggling). CSS provides the professional game aesthetic: dark theme, wooden board area, clean typography, transitions. - -## Key Insights - -- All screens live in one HTML file, toggled via `.screen.active` CSS class -- Screens: nickname, lobby (main menu), pvp-menu, pve-menu, room-list, waiting-room, game, game-over -- Board rendered on `` (Phase 4), everything else is DOM -- CSS handles transitions between screens (fade or slide) -- Responsive: flexbox layout, max-width container, canvas scales - -## Architecture - -### Screen Flow (DOM sections) - -``` -#screen-nickname --> #screen-lobby -#screen-lobby --> #screen-pvp-menu | #screen-pve-menu -#screen-pvp-menu --> #screen-room-list | (create room -> #screen-waiting-room) -#screen-pve-menu --> #screen-game (auto-starts) -#screen-room-list --> #screen-game (join) | #screen-game (watch) -#screen-waiting-room --> #screen-game (when opponent joins) -#screen-game --> #screen-game-over -#screen-game-over --> #screen-game (rematch) | #screen-lobby (exit) -``` - -### Layout Structure - -``` -.app-container (centered, max-width: 1200px) - header.game-header (logo, connection status, nickname) - main#screens-container - section.screen#screen-nickname - section.screen#screen-lobby - section.screen#screen-pvp-menu - section.screen#screen-pve-menu - section.screen#screen-room-list - section.screen#screen-waiting-room - section.screen#screen-game - .game-layout (flexbox row) - .game-sidebar-left (player info, turn indicator) - .game-board-container (canvas) - .game-sidebar-right (move history, chat) - section.screen#screen-game-over - footer (version, credits) - #toast-container (floating notifications) -``` - -## Related Code Files - -### Files to Create -- `landlords-server/src/main/resources/static/index.html` -- `landlords-server/src/main/resources/static/css/style.css` - -## Implementation Steps - -### Step 1: Create `index.html` - -Key elements per screen: - -**#screen-nickname:** -- Title "Gomoku" -- Input field for nickname -- "Play" button -- Subtitle text - -**#screen-lobby:** -- Welcome message with nickname -- Two large buttons: "Player vs Player", "Player vs AI" -- Subtitle describing each mode - -**#screen-pvp-menu:** -- "Create Room" button -- "Join Room" (shows room list) button -- "Back" button - -**#screen-pve-menu:** -- Three difficulty buttons: Easy / Medium / Hard -- Brief description per difficulty -- "Back" button - -**#screen-room-list:** -- Table: Room ID, Owner, Players, Type, Actions (Join / Watch) -- "Refresh" button -- "Back" button -- Empty state message - -**#screen-waiting-room:** -- Room info display -- "Waiting for opponent..." message with spinner -- "Leave" button - -**#screen-game:** -- Left sidebar: player cards (black/white), turn indicator arrow, timer placeholder -- Center: `` (responsive) -- Right sidebar: move history list (scrollable), coordinates display -- Bottom bar: "Exit" button, sound toggle - -**#screen-game-over:** -- Result (Win/Lose/Draw) with large text -- Winner name -- Final board snapshot (reuse canvas) -- "Rematch" and "Exit to Lobby" buttons - -**Script tags** at bottom: load JS files in order (game-state.js first, then game-connection.js, game-board.js, game-ui.js, game-audio.js) or use `type="module"`. - -**Decision: Use classic `` - - Basic CSS: body margin 0, background #1a1a2e, flex-center the container, overlay absolute positioned over canvas -5. Create `src/config/game-config.js`: - - Export Phaser config object: `type: Phaser.AUTO`, `width: 800`, `height: 800` - - `scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH }` - - `parent: 'game-container'` - - `backgroundColor: '#2d2d44'` - - `scene: [BootScene]` (import from scenes) -6. Create `src/scenes/boot-scene.js`: - - Extends `Phaser.Scene`, key: `'BootScene'` - - `create()`: display centered "Loading..." text - - Will be expanded in Phase 3 to transition to MenuScene -7. Create `src/main.js`: - - Import config from `game-config.js` - - `new Phaser.Game(config)` - - Export game instance for potential service access -8. Run `npm install` and `npm run dev` to verify Phaser boots - -## Success Criteria -- [ ] `npm run dev` starts Vite on port 5173 -- [ ] Browser shows Phaser canvas with "Loading..." text -- [ ] No console errors -- [ ] `npm run build` produces working static build in `dist/` - -## Risk Assessment -- **Node.js not installed:** User must have Node.js. Document in README. -- **Phaser version mismatch:** Pin to `^3.80.0` for stability. - -## Next Steps -- Phase 2: Services layer (can start immediately after scaffold) diff --git a/plans/260410-0913-phaser-web-client/phase-02-services-layer.md b/plans/260410-0913-phaser-web-client/phase-02-services-layer.md deleted file mode 100644 index 34f4e91..0000000 --- a/plans/260410-0913-phaser-web-client/phase-02-services-layer.md +++ /dev/null @@ -1,190 +0,0 @@ -# Phase 2: Services Layer - -## Context Links -- [Plan overview](plan.md) -- [Phase 1: Scaffold](phase-01-project-scaffold.md) -- Server `Msg` entity: `landlords-common/.../entity/Msg.java` -- `{code, data, info}` -- Server event codes: `landlords-common/.../enums/ServerEventCode.java`, `ClientEventCode.java` -- Server WS handler: `landlords-server/.../handler/WebsocketTransferHandler.java` - -## Overview -- **Priority:** P1 (all scenes depend on these services) -- **Status:** Pending -- **Blocked by:** Phase 1 -- **Description:** Build three decoupled service modules: WebSocket connection, event bus, game state. Plus protocol constants extracted from server source. - -## Key Insights -- Server WS message format: `{"code": "CODE_...", "data": "json_string_or_plain_string", "info": ""}` -- `data` field is a JSON **string** (not nested object) -- must `JSON.parse(data)` when data is structured -- Server sends `CODE_CLIENT_CONNECT` + `CODE_CLIENT_NICKNAME_SET` ~2s after WS handshake (server has `Thread.sleep(2000L)`) -- Heartbeat: server reads idle timeout; client must send `CODE_CLIENT_HEAD_BEAT` every ~50s -- Server ignores heartbeat messages (no handler called, just keeps connection alive) - -## Architecture - -``` -connection-service.js - |-- wraps browser WebSocket - |-- auto-reconnect with backoff - |-- heartbeat timer (50s interval) - |-- on message: parse JSON -> event-bus.emit(code, parsedData) - |-- send(code, data): serialize to Msg format -> ws.send() - -event-bus.js - |-- on(event, callback): subscribe - |-- off(event, callback): unsubscribe - |-- emit(event, data): notify all subscribers - -game-state-service.js - |-- stores: clientId, nickname, roomId, isBlack, isMyTurn, boardState[][], moves[] - |-- reset methods for new game / exit room - |-- no logic, pure state container - -protocol-constants.js - |-- SERVER_EVENTS: all CODE_CLIENT_* codes (server -> client) - |-- CLIENT_EVENTS: all CODE_* codes (client -> server) - |-- string constants, no enums needed -``` - -### Data Flow: Sending a Move -``` -GameScene.onBoardClick(row, col) - -> connectionService.send('CODE_GAME_MOVE', JSON.stringify({row, col})) - -> ws.send('{"code":"CODE_GAME_MOVE","data":"{\"row\":7,\"col\":7}","info":""}') -``` - -### Data Flow: Receiving a Move -``` -ws.onmessage(frame) - -> JSON.parse(frame.data) => {code: "CODE_GAME_MOVE_SUCCESS", data: "{\"row\":7,...}", info: ""} - -> eventBus.emit('CODE_GAME_MOVE_SUCCESS', {row:7, col:7, piece:"BLACK", playerNickname:"p1", playerId:1}) - (data string auto-parsed to object by connection-service) -``` - -## Related Code Files - -### Files to Create -- `web-client/src/services/connection-service.js` -- `web-client/src/services/event-bus.js` -- `web-client/src/services/game-state-service.js` -- `web-client/src/config/protocol-constants.js` - -### Files to Modify -- None - -## Implementation Steps - -### 1. `protocol-constants.js` - -Export two frozen objects with all event code strings: - -```js -/** @enum {string} Codes the client sends TO the server */ -export const ServerEventCode = Object.freeze({ - NICKNAME_SET: 'CODE_CLIENT_NICKNAME_SET', - INFO_SET: 'CODE_CLIENT_INFO_SET', - ROOM_CREATE: 'CODE_ROOM_CREATE', - ROOM_CREATE_PVE: 'CODE_ROOM_CREATE_PVE', - ROOM_JOIN: 'CODE_ROOM_JOIN', - GET_ROOMS: 'CODE_GET_ROOMS', - GAME_MOVE: 'CODE_GAME_MOVE', - GAME_READY: 'CODE_GAME_READY', - CLIENT_EXIT: 'CODE_CLIENT_EXIT', - GAME_WATCH: 'CODE_GAME_WATCH', - GAME_WATCH_EXIT: 'CODE_GAME_WATCH_EXIT', - HEARTBEAT: 'CODE_CLIENT_HEAD_BEAT', -}); - -/** @enum {string} Codes the server sends TO the client */ -export const ClientEventCode = Object.freeze({ - CLIENT_CONNECT: 'CODE_CLIENT_CONNECT', - NICKNAME_SET: 'CODE_CLIENT_NICKNAME_SET', - SHOW_OPTIONS: 'CODE_SHOW_OPTIONS', - SHOW_ROOMS: 'CODE_SHOW_ROOMS', - ROOM_CREATE_SUCCESS: 'CODE_ROOM_CREATE_SUCCESS', - ROOM_JOIN_SUCCESS: 'CODE_ROOM_JOIN_SUCCESS', - ROOM_JOIN_FAIL_FULL: 'CODE_ROOM_JOIN_FAIL_BY_FULL', - ROOM_JOIN_FAIL_INEXIST: 'CODE_ROOM_JOIN_FAIL_BY_INEXIST', - GAME_STARTING: 'CODE_GAME_STARTING', - GAME_MOVE_SUCCESS: 'CODE_GAME_MOVE_SUCCESS', - GAME_MOVE_INVALID: 'CODE_GAME_MOVE_INVALID', - GAME_MOVE_OCCUPIED: 'CODE_GAME_MOVE_OCCUPIED', - GAME_MOVE_OUT_OF_BOUNDS: 'CODE_GAME_MOVE_OUT_OF_BOUNDS', - GAME_MOVE_NOT_YOUR_TURN: 'CODE_GAME_MOVE_NOT_YOUR_TURN', - GAME_OVER: 'CODE_GAME_OVER', - GAME_READY: 'CODE_GAME_READY', - CLIENT_EXIT: 'CODE_CLIENT_EXIT', - CLIENT_KICK: 'CODE_CLIENT_KICK', - GAME_WATCH: 'CODE_GAME_WATCH', - GAME_WATCH_SUCCESSFUL: 'CODE_GAME_WATCH_SUCCESSFUL', - PVE_DIFFICULTY_NOT_SUPPORT: 'CODE_PVE_DIFFICULTY_NOT_SUPPORT', -}); -``` - -### 2. `event-bus.js` - -Simple pub/sub: -- `_listeners` Map of `event -> Set` -- `on(event, cb)` -- add listener -- `off(event, cb)` -- remove listener -- `emit(event, data)` -- call all listeners for event -- Export singleton instance -- ~40 lines - -### 3. `connection-service.js` - -WebSocket wrapper: -- `connect(url)` -- create WebSocket, attach handlers -- `send(code, data)` -- build `{code, data, info:""}`, `ws.send(JSON.stringify(msg))` -- `disconnect()` -- close WS, clear heartbeat -- Internal: `_onMessage(event)`: - 1. `JSON.parse(event.data)` to get `{code, data, info}` - 2. Try `JSON.parse(msg.data)` for structured data; fall back to raw string - 3. `eventBus.emit(msg.code, parsedData)` -- Internal: `_startHeartbeat()` -- `setInterval` every 50000ms, send `CODE_CLIENT_HEAD_BEAT` -- Internal: `_stopHeartbeat()` -- `clearInterval` -- `onopen`: emit internal `'ws:connected'` event -- `onclose`: emit `'ws:disconnected'`, attempt reconnect with exponential backoff (1s, 2s, 4s, max 30s) -- `onerror`: log, let `onclose` handle reconnect -- Export singleton -- ~80 lines - -**Critical detail:** The `data` field in outgoing messages must be a **string**. For structured data like `{row, col}`, use `JSON.stringify({row, col})` as the data value. For simple strings like nickname, pass the string directly. - -### 4. `game-state-service.js` - -Plain state object: -- `clientId` -- set on `CODE_CLIENT_CONNECT` -- `nickname` -- set after nickname submission -- `roomId` -- set on room create/join -- `isBlack` -- derived from `CODE_GAME_STARTING` comparing clientId to blackPlayerId -- `isMyTurn` -- toggled on each `CODE_GAME_MOVE_SUCCESS` -- `board` -- 15x15 2D array, initialized to `null`, set cells on move success -- `moves` -- array of `{row, col, piece, playerNickname}` for move history -- `isSpectating` -- boolean -- `reset()` -- clear room/game state -- `resetBoard()` -- clear board/moves for rematch -- Export singleton -- ~60 lines - -## Todo List -- [ ] Create `protocol-constants.js` with all event codes -- [ ] Create `event-bus.js` with on/off/emit -- [ ] Create `connection-service.js` with connect/send/heartbeat/reconnect -- [ ] Create `game-state-service.js` with state fields and reset methods -- [ ] Verify WS connection to running server in browser console - -## Success Criteria -- [ ] `eventBus.on('CODE_CLIENT_CONNECT', cb)` fires when server sends connect event -- [ ] `connectionService.send('CODE_CLIENT_NICKNAME_SET', 'TestUser')` accepted by server -- [ ] Heartbeat keeps connection alive beyond 60s -- [ ] `game-state-service` stores and resets state correctly -- [ ] All files under 100 lines each -- [ ] JSDoc on all exported functions - -## Risk Assessment -- **`data` field double-encoding:** Server expects `data` as a string. If we pass an object, server's `MapHelper.parser()` will fail. Mitigation: `connection-service.send()` must `JSON.stringify` objects before placing in `data` field. -- **Reconnect during active game:** If WS drops mid-game, server has no rejoin mechanism. Mitigation: show "Connection lost" overlay; on reconnect, user starts fresh (server already cleaned up the room via `CODE_CLIENT_OFFLINE`). - -## Next Steps -- Phase 3 (Boot + Menu scenes) depends on these services being complete diff --git a/plans/260410-0913-phaser-web-client/phase-03-boot-menu-scenes.md b/plans/260410-0913-phaser-web-client/phase-03-boot-menu-scenes.md deleted file mode 100644 index e4cf5c5..0000000 --- a/plans/260410-0913-phaser-web-client/phase-03-boot-menu-scenes.md +++ /dev/null @@ -1,206 +0,0 @@ -# Phase 3: Boot + Menu Scenes - -## Context Links -- [Plan overview](plan.md) -- [Phase 2: Services](phase-02-services-layer.md) -- Server nickname handler: `landlords-server/.../event/ServerEventListener_CODE_CLIENT_NICKNAME_SET.java` -- max 10 chars, non-empty -- Server show options flow: after nickname set, server sends `CODE_SHOW_OPTIONS` -- Server room list: `CODE_GET_ROOMS` -> `CODE_SHOW_ROOMS` with `[{roomId, roomOwner, roomClientCount, roomType}]` - -## Overview -- **Priority:** P1 -- **Status:** Pending -- **Blocked by:** Phase 1, Phase 2 -- **Description:** BootScene connects to server, MenuScene manages all pre-game UI via DOM overlays (nickname, lobby, PVP/PVE menus). - -## Key Insights -- Server flow after WS connect: waits 2s, sends `CODE_CLIENT_CONNECT` (data=clientId), then `CODE_CLIENT_NICKNAME_SET` (data=null, meaning "please set nickname") -- If nickname invalid (empty or >10 chars), server sends `CODE_CLIENT_NICKNAME_SET` again with `{invalidLength: N}` -- client should re-prompt -- After valid nickname, server sends `CODE_SHOW_OPTIONS` -- client shows main menu -- PVP: `CODE_ROOM_CREATE` -> `CODE_ROOM_CREATE_SUCCESS` (room JSON) -> wait for opponent -> auto-starts on join -- PVE: `CODE_ROOM_CREATE_PVE` with data "1"/"2"/"3" -> server auto-starts immediately -- Join room: `CODE_ROOM_JOIN` with data=roomId string -> `CODE_ROOM_JOIN_SUCCESS` -> auto-starts if 2 players - -## Architecture - -### Scene Flow -``` -BootScene - create(): connect to WS, show "Connecting..." text on canvas - on 'CODE_CLIENT_CONNECT': store clientId, show "Connected!" - on 'CODE_CLIENT_NICKNAME_SET': transition to MenuScene - -MenuScene - create(): show nickname form via menu-ui.js - Substates (managed by menu-ui.js DOM swaps): - 1. NICKNAME -- input + submit button - 2. LOBBY -- main menu: PVP / PVE / Spectate buttons - 3. PVP_MENU -- "Create Room" button + room list table + join button - 4. PVE_MENU -- difficulty picker (Easy/Medium/Hard) - 5. SPECTATE_MENU -- room list + watch button - 6. WAITING -- "Waiting for opponent..." (after PVP room create) - - on 'CODE_SHOW_OPTIONS': switch to LOBBY substate - on 'CODE_SHOW_ROOMS': populate room list table - on 'CODE_ROOM_CREATE_SUCCESS': switch to WAITING, store roomId - on 'CODE_ROOM_JOIN_SUCCESS': store room info - on 'CODE_GAME_STARTING': hide all overlays, transition to GameScene - on 'CODE_ROOM_JOIN_FAIL_*': show error toast - on 'CODE_PVE_DIFFICULTY_NOT_SUPPORT': show error toast - on 'CODE_GAME_WATCH_SUCCESSFUL': transition to GameScene (spectator mode) -``` - -### DOM Overlay Strategy -- All menus live in `#ui-overlay` div (positioned absolute over Phaser canvas) -- `menu-ui.js` manages showing/hiding substate containers -- Phaser canvas stays visible as background (dark board aesthetic) -- On transition to GameScene, hide `#ui-overlay` entirely - -## Related Code Files - -### Files to Create -- `web-client/src/scenes/boot-scene.js` (overwrite Phase 1 placeholder) -- `web-client/src/scenes/menu-scene.js` -- `web-client/src/ui/menu-ui.js` - -### Files to Modify -- `web-client/index.html` -- add DOM overlay structure and CSS -- `web-client/src/config/game-config.js` -- add MenuScene to scene list - -## Implementation Steps - -### 1. Update `index.html` -- Add DOM overlay structure - -Inside `#ui-overlay`, add containers for each substate: - -```html - -``` - -CSS: dark theme, centered cards, simple button styles. Keep inline in `