11 Commits
Author SHA1 Message Date
tiennm99 42d94a2aed fix: wire up spectator mode end-to-end
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.
2026-04-11 09:22:54 +07:00
tiennm99 35fb8cf2ef ci: fix gradlew path and permissions for Build Server job
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.
2026-04-11 08:39:13 +07:00
tiennm99 cbad690565 docs,chore: single-port 1999 websocket protobuf
Phase 05 — sync infrastructure and documentation with the typed-protobuf
refactor:
- docker-compose.yml: drop 1024/1025 mappings, single "1999:1999"
- server/Dockerfile: EXPOSE 1999, -p 1999 entrypoint
- README.md: rewrite transport description, architecture diagram, protocol
  section, server options, project structure, add proto:gen script note
- docs/project-overview.md: update transport + dependencies sections
- docs/system-architecture.md: rewrite diagrams + pipeline + file inventory
  for the WebSocket-only typed-dispatch path
- docs/codebase-summary.md: refresh file tree, java package inventory,
  gradle deps, vite deps, networking and game-flow sections
- docs/deployment-guide.md: single-port walkthrough for local / docker /
  systemd / nginx; remove all 1024/1025 firewall and troubleshooting
- docs/code-standards.md: replace dead ServerEventListener_CODE_* class-name
  example, fix sample ws:// URL to port 1999
2026-04-11 08:33:46 +07:00
tiennm99 3ad9a7b9d1 refactor(server): drop gson and dead json/map/tcp helpers
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.
2026-04-11 08:15:39 +07:00
tiennm99 b75733fd2d refactor(server): migrate event handlers to typed records
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.
2026-04-11 08:14:52 +07:00
tiennm99 945a249b89 refactor(server): typed protobuf wire + sealed record dispatcher scaffolding
Phase 01+02a of the WebSocket protobuf migration:
- Add request.proto / response.proto with typed oneofs (14 request + 20 response variants)
- Wire com.google.protobuf Gradle plugin v0.9.6 (protoc 3.25.5)
- Drop TCP stack: ProtobufProxy, Proxy, ProtobufTransferHandler, SecondProtobufCodec
- Delete old envelope types ClientTransferData, ServerTransferData
- Delete reflection-based ServerEventListener + 13 ServerEventListener_CODE_* classes
- Create sealed ClientRequest interface + 14 record variants
- Create RequestConverter (wire -> record) and RequestDispatcher (record -> handler)
- Rewrite ChannelUtils.push(Channel, Response) for BinaryWebSocketFrame
- Rewrite WebsocketTransferHandler for BinaryWebSocketFrame + typed dispatch
- Flip default port 1024 -> 1999; SimpleServer starts only WebsocketProxy
- Move RoomClearTask scheduling into WebsocketProxy.start()
- Bump netty 4.1.115.Final -> 4.1.128.Final, junit-bom 5.11.3 -> 5.11.4, shadow 8.3.5 -> 8.3.8
- Heartbeat handled as no-op in dispatcher; all other cases throw until phase 02b

Phase 02b will port the 14 business-logic handlers and wire the dispatcher.
2026-04-11 08:10:16 +07:00
tiennm99 45e22124ac chore: bump version to 0.0.1 and replace "frontend" with "client"
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.
2026-04-10 20:55:29 +07:00
tiennm99 b7ff184387 refactor: convert server from Maven to Gradle
Replace server/pom.xml with Gradle 9.2.1 (Kotlin DSL) + Shadow plugin
for fat jar packaging.

New files:
- server/build.gradle.kts   (Kotlin DSL script)
- server/settings.gradle.kts
- server/gradle/wrapper/    (committed wrapper, pinned to 9.2.1)
- server/gradlew, gradlew.bat

Deleted:
- server/pom.xml

Gradle config:
- plugins: java, com.gradleup.shadow 8.3.5
- toolchain: JavaLanguageVersion.of(25) (auto-provisions if missing)
- deps: netty-all 4.1.115.Final, protobuf-java 3.25.5, gson 2.11.0,
  junit-bom 5.11.3 + junit-jupiter (test)
- compiler: -parameters, UTF-8
- test: useJUnitPlatform()
- shadowJar: main class com.miti99.caro.server.SimpleServer,
  mergeServiceFiles(), append META-INF/io.netty.versions.properties
- default assembly depends on shadowJar

Output path migration:
- server/target/caro-server-0.0.1-beta.jar moves under Gradle output conventions.

Infrastructure:
- server/Dockerfile: eclipse-temurin:25-jdk + committed wrapper
  (no Maven image); runtime stage unchanged. COPY order optimized.
- .github/workflows/build.yml: setup-java temurin 25 +
  gradle/actions/setup-gradle, run gradlew with -p server.
- .gitignore: add .gradle/, whitelist wrapper jar after *.jar rule.

Docs + README fully updated to Gradle commands across:
README.md, codebase-summary.md, code-standards.md, deployment-guide.md,
project-overview.md, system-architecture.md.

Validation: gradlew clean assemble check passes all 37 tests on Java 25.
2026-04-10 20:50:53 +07:00
tiennm99 a5aa3606cd refactor(java25): convert Msg to record, use switch expressions + var
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.
2026-04-10 19:17:02 +07:00
tiennm99 2d74117fe2 refactor: rename packages org.nico.ratel.landlords -> com.miti99.caro.{common,server}
- 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).
2026-04-10 19:14:02 +07:00
tiennm99 5b68ee9cc4 refactor: standalone maven, java 25, shade, gson, junit 5, rename to server/
Build system modernization:
- Delete root parent pom.xml; server/pom.xml is now standalone.
- Drop Spring Boot parent; use maven-shade-plugin 3.6.0 for fat jar.
- Upgrade source/target to Java 25 (LTS); pin explicit dep versions:
    netty-all 4.1.115.Final, protobuf-java 3.25.5, gson 2.11.0,
    junit-jupiter 5.11.3, maven-compiler-plugin 3.13.0,
    maven-surefire-plugin 3.5.2, maven-shade-plugin 3.6.0.
- New coordinates: com.miti99.caro:caro-server:0.0.1-beta.
- Shade transformers: manifest (main class), services, appending
  (Netty io.netty.versions.properties merge).

Dependency migration:
- Replace com.smallnico:noson with gson across 5 files (7 call sites):
    MapHelper, TransferProtocolUtils, ServerEventListener_CODE_GAME_WATCH,
    ServerEventListener_CODE_GET_ROOMS, ServerEventListener_CODE_ROOM_CREATE.
  All call sites now funnel through existing JsonUtils wrapper (DRY).
- Migrate GomokuHelperTest + GomokuAITest from JUnit 4 to JUnit 5
  (org.junit.Test -> org.junit.jupiter.api.Test,
   org.junit.Assert.* -> org.junit.jupiter.api.Assertions.*).

Directory + infra:
- git mv landlords-server/ -> server/.
- Rewrite server/Dockerfile: Java 25 base images
  (maven:3.9-eclipse-temurin-25 build, eclipse-temurin:25-jre-alpine runtime),
  simplified COPY paths (no more multi-module layout).
- docker-compose.yml: dockerfile points to server/Dockerfile.
- .github/workflows/build.yml: setup-java temurin 25,
  `mvn -f server/pom.xml -B clean verify`.

Validation: mvn verify on Java 25 passes all 37 tests (29 GomokuHelper
+ 8 GomokuAI), shade produces caro-server-0.0.1-beta.jar cleanly.

Note: package names remain org.nico.ratel.landlords.* in this phase;
renamed to com.miti99.caro.{common,server}.* in Phase 4.
2026-04-10 19:10:51 +07:00