mirror of
https://github.com/tiennm99/caro.git
synced 2026-05-14 00:57:55 +00:00
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
This commit is contained in:
@@ -11,7 +11,7 @@ Built on [Netty](https://netty.io/) (server) and [Phaser 3](https://phaser.io/)
|
||||
- **Player vs AI (PVE)** — three difficulty levels (Easy, Medium, Hard)
|
||||
- **Spectator mode** — watch ongoing games in real-time
|
||||
- **Phaser web client** — professional 2D game UI with canvas board, stone animations, sound effects
|
||||
- **WebSocket + TCP** — dual protocol support
|
||||
- **Typed protobuf over WebSocket** — single binary wire protocol on port 1999
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -27,7 +27,7 @@ cd caro
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Then open `http://localhost:8080/` in your browser. The server listens on ports `1024` (TCP) and `1025` (WebSocket); the client is served at `8080`.
|
||||
Then open `http://localhost:8080/` in your browser. The server listens on port `1999` (WebSocket); the client is served at `8080`.
|
||||
|
||||
## Quick Start (Local)
|
||||
|
||||
@@ -35,14 +35,13 @@ Then open `http://localhost:8080/` in your browser. The server listens on ports
|
||||
|
||||
```bash
|
||||
./server/gradlew -p server clean build
|
||||
java -jar server/build/libs/caro-server-0.0.1.jar -p 1024
|
||||
java -jar server/build/libs/caro-server-0.0.1.jar -p 1999
|
||||
```
|
||||
|
||||
On Windows use `server\gradlew.bat` instead of `./server/gradlew`.
|
||||
|
||||
The server starts two listeners:
|
||||
- **TCP** on port `1024` (Protobuf)
|
||||
- **WebSocket** on port `1025` (JSON)
|
||||
The server starts one listener:
|
||||
- **WebSocket** on port `1999` (typed protobuf binary frames at `/ratel`)
|
||||
|
||||
### 2. Run the client (Vite dev server)
|
||||
|
||||
@@ -81,10 +80,23 @@ caro/
|
||||
```
|
||||
Client (browser)
|
||||
|
|
||||
+-- TCP :1024 --> ProtobufTransferHandler --> ServerEventListener_*
|
||||
+-- WS :1025 --> WebsocketTransferHandler --> ServerEventListener_*
|
||||
+-- WS :1999 /ratel --> WebsocketTransferHandler
|
||||
|
|
||||
v
|
||||
RequestConverter (wire Request -> sealed ClientRequest record)
|
||||
|
|
||||
v
|
||||
RequestDispatcher (pattern-match switch on record)
|
||||
|
|
||||
v
|
||||
<Verb>Handler (typed business logic, emits typed Response)
|
||||
```
|
||||
|
||||
Protobuf schemas live at `server/src/main/proto/{request,response}.proto`.
|
||||
The `com.google.protobuf` Gradle plugin generates Java classes into
|
||||
`server/build/generated/sources/proto/main/java/com/miti99/caro/protocol/`.
|
||||
No reflection, no string-keyed lookups.
|
||||
|
||||
### Client Architecture
|
||||
|
||||
```
|
||||
@@ -92,7 +104,10 @@ client/src/
|
||||
main.js Phaser game boot
|
||||
config/
|
||||
game-config.js Phaser config (800x800, Scale.FIT)
|
||||
protocol-constants.js Server/client event code enums
|
||||
protocol-constants.js ClientEventCode (event bus keys) only
|
||||
generated/
|
||||
protocol.js pbjs static-module output (Request/Response)
|
||||
protocol.d.ts pbts TypeScript typings for protocol.js
|
||||
scenes/
|
||||
boot-scene.js Connect to server
|
||||
menu-scene.js DOM overlay menus
|
||||
@@ -112,7 +127,7 @@ client/src/
|
||||
## Server Options
|
||||
|
||||
```
|
||||
-p, -port TCP port (default: 1024, WebSocket = TCP + 1)
|
||||
-p, -port WebSocket port (default: 1999)
|
||||
```
|
||||
|
||||
## Client Scripts
|
||||
@@ -121,19 +136,25 @@ client/src/
|
||||
npm --prefix client run dev # Start Vite dev server (port 5173)
|
||||
npm --prefix client run build # Production build to client/dist/
|
||||
npm --prefix client run preview # Preview production build
|
||||
npm --prefix client run proto:gen # Regenerate client/src/generated/protocol.{js,d.ts}
|
||||
```
|
||||
|
||||
## Protocol
|
||||
|
||||
Communication uses JSON messages over WebSocket or Protobuf over TCP.
|
||||
Communication uses **typed protobuf binary frames** over WebSocket.
|
||||
|
||||
WebSocket message format:
|
||||
```json
|
||||
{"code": "CODE_GAME_MOVE", "data": "{\"row\":7,\"col\":7}", "info": ""}
|
||||
Every client-to-server message is an instance of the `Request` oneof wrapper
|
||||
(`server/src/main/proto/request.proto`); every server-to-client message is an
|
||||
instance of the `Response` oneof wrapper (`server/src/main/proto/response.proto`).
|
||||
The oneof case IS the event type — there are no string codes on the wire.
|
||||
|
||||
WebSocket endpoint: `ws://host:1999/ratel`
|
||||
|
||||
Example (send a move at row 7, col 7):
|
||||
```js
|
||||
connectionService.sendGameMove(7, 7); // builds Request { game_move: { row: 7, col: 7 } }
|
||||
```
|
||||
|
||||
WebSocket endpoint: `ws://host:{tcp_port + 1}/ratel`
|
||||
|
||||
## Credits
|
||||
|
||||
This project is based on [Ratel](https://github.com/ainilili/ratel) by [ainilili](https://github.com/ainilili), originally a Chinese Landlords (Dou Di Zhu) card game. It has been converted to Gomoku (Five-in-a-Row) with a new web client.
|
||||
|
||||
+1
-2
@@ -5,8 +5,7 @@ services:
|
||||
dockerfile: server/Dockerfile
|
||||
container_name: caro-server
|
||||
ports:
|
||||
- "1024:1024" # TCP / Protobuf
|
||||
- "1025:1025" # WebSocket
|
||||
- "1999:1999" # WebSocket (typed protobuf binary frames)
|
||||
restart: unless-stopped
|
||||
|
||||
client:
|
||||
|
||||
@@ -36,23 +36,23 @@ com.miti99.caro.{common|server}.{subcomponent}
|
||||
|
||||
**Patterns:**
|
||||
- `FooBar` — Regular classes
|
||||
- `FooBarListener` — Event listeners
|
||||
- `FooBarHandler` — Protocol/network handlers
|
||||
- `FooBarProxy` — Message sending proxies
|
||||
- `FooBarTest` — Unit test classes
|
||||
- `FooBarImpl` — Concrete implementations (rare)
|
||||
- `FooRequest` / `FooRequestRecord` — Sealed record variants (typed requests)
|
||||
- `FooTest` — Unit test classes
|
||||
- `FooImpl` — Concrete implementations (rare)
|
||||
- `AbstractFooBar` — Base classes
|
||||
|
||||
**Examples:**
|
||||
```java
|
||||
// Good
|
||||
public class Board { ... }
|
||||
public class ServerEventListener_CODE_GAME_MOVE { ... }
|
||||
public class GameMoveHandler { ... }
|
||||
public record GameMoveRequestRecord(...) implements ClientRequest { }
|
||||
public class GomokuHelper { ... }
|
||||
|
||||
// Avoid
|
||||
public class board { ... } // lowercase
|
||||
public class GameMoveListener { ... } // unclear purpose
|
||||
public class board { ... } // lowercase
|
||||
public class game_move_handler { ... } // snake_case — use PascalCase
|
||||
```
|
||||
|
||||
### File Organization
|
||||
@@ -411,7 +411,7 @@ const board = Board(); // Should use new
|
||||
/**
|
||||
* Connects to the WebSocket server and establishes game communication.
|
||||
*
|
||||
* @param {string} url - The WebSocket server URL (e.g., 'ws://localhost:1025/ratel')
|
||||
* @param {string} url - The WebSocket server URL (e.g., 'ws://localhost:1999/ratel')
|
||||
* @returns {Promise<WebSocket>} Promise resolving to the connected WebSocket
|
||||
* @throws {Error} If connection fails or timeout occurs
|
||||
*/
|
||||
|
||||
+50
-48
@@ -11,25 +11,27 @@ caro/
|
||||
│ ├── src/main/java/com/miti99/caro/
|
||||
│ │ ├── common/
|
||||
│ │ │ ├── channel/ Netty utilities (ChannelUtils)
|
||||
│ │ │ ├── entity/ Data models (Board, Room, GameMove, Msg, ClientSide)
|
||||
│ │ │ ├── enums/ ServerEventCode, ClientEventCode, PieceType, etc.
|
||||
│ │ │ ├── entity/ Data models (Board, Room, GameMove, ClientSide)
|
||||
│ │ │ ├── enums/ ClientEventCode, PieceType, GameResult, RoomType, etc.
|
||||
│ │ │ ├── exception/ LandlordException
|
||||
│ │ │ ├── features/ Feature flags
|
||||
│ │ │ ├── handler/ Protocol codec (DefaultDecoder)
|
||||
│ │ │ ├── helper/ GomokuHelper, MapHelper
|
||||
│ │ │ ├── helper/ GomokuHelper (win detection)
|
||||
│ │ │ ├── print/ SimplePrinter
|
||||
│ │ │ ├── robot/ AI engine (GomokuAI)
|
||||
│ │ │ ├── transfer/ Binary serialization (ByteKit, ByteLink, TransferProtocolUtils)
|
||||
│ │ │ └── utils/ JsonUtils (gson), ListUtils, OptionsUtils, StreamUtils
|
||||
│ │ │ └── utils/ ListUtils, OptionsUtils, StreamUtils
|
||||
│ │ └── server/
|
||||
│ │ ├── event/ ServerEventListener_* handlers
|
||||
│ │ ├── handler/ Netty pipeline (Protobuf/WS codecs)
|
||||
│ │ ├── proxy/ ProtobufProxy, WebsocketProxy
|
||||
│ │ ├── event/
|
||||
│ │ │ ├── request/ ClientRequest sealed interface + record variants (14 types)
|
||||
│ │ │ ├── handler/ 14 typed request handlers (*Handler.java)
|
||||
│ │ │ ├── RequestConverter.java Protobuf→ClientRequest conversion
|
||||
│ │ │ └── RequestDispatcher.java Pattern-match dispatch logic
|
||||
│ │ ├── handler/ Netty pipeline (WebsocketTransferHandler)
|
||||
│ │ ├── timer/ RoomClearTask
|
||||
│ │ ├── SimpleServer.java Server entry point
|
||||
│ │ └── ServerContains.java Global state container
|
||||
│ ├── src/main/resources/
|
||||
│ │ └── proto/ .proto files + generate.sh (future proto-over-WS)
|
||||
│ ├── src/main/proto/
|
||||
│ │ ├── request.proto Client→server typed message definitions
|
||||
│ │ └── response.proto Server→client typed message definitions
|
||||
│ ├── src/test/java/com/miti99/caro/common/
|
||||
│ │ ├── helper/tests/GomokuHelperTest.java 29 JUnit 5 tests
|
||||
│ │ └── robot/tests/GomokuAITest.java 8 JUnit 5 tests
|
||||
@@ -85,9 +87,8 @@ caro/
|
||||
- `Board.java` — 15x15 game board, move validation, win/draw detection (`BOARD_SIZE=15`, `WIN_CONDITION=5`)
|
||||
- `Room.java` — Game session state (id, type, status, players, board, moveHistory)
|
||||
- `GameMove.java` — Single move (row, col, piece, playerId, timestamp)
|
||||
- `Msg.java` — WebSocket JSON envelope (record: code, data, info)
|
||||
- `ServerTransferData.java` / `ClientTransferData.java` — Protobuf-generated wire types
|
||||
- `ClientSide.java` — Player connection state (nickname, status, role)
|
||||
- Generated proto messages — `Request`, `Response` oneof wrappers (in `server/build/generated/sources/proto/main/java/`)
|
||||
|
||||
**Enums:**
|
||||
- `ServerEventCode` — client→server action codes
|
||||
@@ -101,37 +102,31 @@ caro/
|
||||
- `GomokuAI.java` — AI move selection (Easy/Medium/Hard)
|
||||
|
||||
**Utilities:**
|
||||
- `JsonUtils.java` — gson wrapper (`toJson`, `fromJson`)
|
||||
- `ListUtils`, `OptionsUtils`, `StreamUtils`
|
||||
- `MapHelper.java` — Fluent map builder (uses gson)
|
||||
|
||||
**Protocol:**
|
||||
- `ByteKit`, `ByteLink` — Byte buffer operations
|
||||
- `TransferProtocolUtils.java` — Protocol framing (`#...$` delimiters, gson JSON body)
|
||||
- `DefaultDecoder.java` — Protobuf message decoder
|
||||
**Dispatch (typed records):**
|
||||
- `ClientRequest` — Sealed interface representing all possible client requests (14 variants)
|
||||
- Record types: `SetNicknameRequest`, `CreateRoomRequest`, `CreatePveRoomRequest`, `GetRoomsRequest`, `JoinRoomRequest`, `GameStartingRequest`, `GameReadyRequest`, `GameMoveRequest`, `GameResetRequest`, `WatchGameRequest`, `WatchGameExitRequest`, `ClientExitRequest`, `ClientOfflineRequest`, `SetClientInfoRequest`
|
||||
|
||||
---
|
||||
|
||||
### `com.miti99.caro.server` (Server code)
|
||||
|
||||
**Entry Point:**
|
||||
- `SimpleServer.java` — Bootstrap (`-p {port}`, default 1024)
|
||||
- Starts TCP proxy on `port` and WebSocket proxy on `port+1`
|
||||
- `SimpleServer.java` — Bootstrap (WebSocket server defaults to port 1999 at `/ratel`)
|
||||
- `ServerContains.java` — Singleton global state (rooms, client sides, channel map)
|
||||
|
||||
**Event Handlers (ServerEventListener_*):**
|
||||
- `CODE_CLIENT_NICKNAME_SET`, `CODE_ROOM_CREATE`, `CODE_ROOM_CREATE_PVE`, `CODE_GET_ROOMS`
|
||||
- `CODE_ROOM_JOIN`, `CODE_GAME_STARTING`, `CODE_GAME_READY`, `CODE_GAME_MOVE`
|
||||
- `CODE_GAME_WATCH` / `CODE_GAME_WATCH_EXIT`, `CODE_CLIENT_OFFLINE`
|
||||
**Event Handlers (14 *Handler classes):**
|
||||
- `SetNicknameHandler`, `CreateRoomHandler`, `CreatePveRoomHandler`, `GetRoomsHandler`, `JoinRoomHandler`
|
||||
- `GameStartingHandler`, `GameReadyHandler`, `GameMoveHandler`, `GameResetHandler`
|
||||
- `WatchGameHandler`, `WatchGameExitHandler`, `ClientExitHandler`, `ClientOfflineHandler`, `SetClientInfoHandler`
|
||||
|
||||
**Network Handlers:**
|
||||
- `ProtobufTransferHandler` — TCP/Protobuf codec
|
||||
- `WebsocketTransferHandler` — WebSocket JSON codec (uses `JsonUtils.fromJson(text, Msg.class)`)
|
||||
- `SecondProtobufCodec` — Second-pass protobuf decoder
|
||||
**Request Processing:**
|
||||
- `RequestConverter` — Decodes `Request` oneof from binary wire format → typed `ClientRequest` variant
|
||||
- `RequestDispatcher` — Pattern-matches `ClientRequest` variant → dispatches to corresponding handler
|
||||
|
||||
**Message Proxies:**
|
||||
- `ProtobufProxy` — TCP server bootstrap
|
||||
- `WebsocketProxy` — WebSocket server bootstrap (no static file handler; non-WS HTTP → default Netty 400/403)
|
||||
**Network Handler:**
|
||||
- `WebsocketTransferHandler` — Netty pipeline handler: decodes binary frame to `Request` protobuf message
|
||||
|
||||
**Background Tasks:**
|
||||
- `RoomClearTask` — Periodic cleanup
|
||||
@@ -181,20 +176,21 @@ caro/
|
||||
|
||||
**Plugins:**
|
||||
- `java` — standard Java plugin
|
||||
- `com.gradleup.shadow:8.3.5` — fat jar packaging
|
||||
- `com.google.protobuf:0.9.6` — protobuf code generation
|
||||
- `com.gradleup.shadow:8.3.8` — fat jar packaging
|
||||
|
||||
**Toolchain:** `JavaLanguageVersion.of(25)` — Gradle auto-provisions Java 25 if missing.
|
||||
|
||||
**Dependencies:**
|
||||
- `io.netty:netty-all:4.1.115.Final` — async networking
|
||||
- `com.google.protobuf:protobuf-java:3.25.5` — binary serialization
|
||||
- `com.google.code.gson:gson:2.11.0` — JSON (supports records)
|
||||
- `org.junit:junit-bom:5.11.3` (platform) + `org.junit.jupiter:junit-jupiter` — testing
|
||||
- `io.netty:netty-all:4.1.128.Final` — async networking
|
||||
- `com.google.protobuf:protobuf-java:3.25.5` — binary serialization, generated code
|
||||
- `com.google.protobuf:protoc:3.25.5` — protobuf compiler (build time)
|
||||
- `org.junit:junit-bom:5.11.4` (platform) + `org.junit.jupiter:junit-jupiter` — testing
|
||||
|
||||
**Shadow jar config:**
|
||||
- Main class: `com.miti99.caro.server.SimpleServer`
|
||||
- No special command-line args (server defaults to port 1999)
|
||||
- `mergeServiceFiles()` — preserve Netty SPIs
|
||||
- `append("META-INF/io.netty.versions.properties")` — merge Netty version file
|
||||
- Excludes signing metadata (`*.SF`, `*.DSA`, `*.RSA`)
|
||||
|
||||
**Build Command:**
|
||||
@@ -213,10 +209,13 @@ caro/
|
||||
- `npm run dev` — Dev server (port 5173, hot reload)
|
||||
- `npm run build` — Production build to `dist/`
|
||||
- `npm run preview` — Preview production build
|
||||
- `npm run proto:gen` — Regenerate protobuf code from server `.proto` files (uses `pbjs` + `pbts`)
|
||||
|
||||
**Dependencies:**
|
||||
- `phaser ^3.87.0` — Game engine
|
||||
- `protobufjs ^7.5.4` — JavaScript protobuf codec
|
||||
- `vite ^6.3.1` — Bundler (dev-only)
|
||||
- `@protobufjs/cli ^1.1.3` — Protobuf code generator (dev-only)
|
||||
|
||||
**Output:** `client/dist/` (index.html + bundled JS, ~1.5 MB / 346 KB gzipped)
|
||||
|
||||
@@ -243,10 +242,11 @@ caro/
|
||||
|
||||
| Scope | Dependencies |
|
||||
|-------|--------------|
|
||||
| **server runtime** | Netty 4.1.115, Protobuf 3.25.5, gson 2.11.0 |
|
||||
| **server test** | JUnit Jupiter 5.11.3 |
|
||||
| **client runtime** | Phaser 3.87 |
|
||||
| **client build** | Vite 6.3 |
|
||||
| **server runtime** | Netty 4.1.128, Protobuf 3.25.5 (generated code) |
|
||||
| **server test** | JUnit Jupiter 5.11.4 |
|
||||
| **server build** | Gradle 9.2.1, Protobuf Gradle plugin 0.9.6, Shadow 8.3.8 |
|
||||
| **client runtime** | Phaser 3.87, protobufjs 7.5.4 |
|
||||
| **client build** | Vite 6.3, protobufjs-cli 1.1.3 |
|
||||
|
||||
---
|
||||
|
||||
@@ -258,14 +258,16 @@ caro/
|
||||
- `server/src/main/java/com/miti99/caro/common/robot/GomokuAI.java`
|
||||
|
||||
### Networking
|
||||
- `server/src/main/java/com/miti99/caro/server/handler/WebsocketTransferHandler.java`
|
||||
- `server/src/main/java/com/miti99/caro/server/handler/ProtobufTransferHandler.java`
|
||||
- `server/src/main/java/com/miti99/caro/common/channel/ChannelUtils.java`
|
||||
- `client/src/services/connection-service.js`
|
||||
- `server/src/main/java/com/miti99/caro/server/handler/WebsocketTransferHandler.java` — Binary decoder
|
||||
- `server/src/main/java/com/miti99/caro/server/event/RequestConverter.java` — Protobuf→record conversion
|
||||
- `server/src/main/java/com/miti99/caro/server/event/RequestDispatcher.java` — Dispatch logic
|
||||
- `server/src/main/java/com/miti99/caro/common/channel/ChannelUtils.java` — Response encoder/sender
|
||||
- `client/src/services/connection-service.js` — WebSocket client (binary mode)
|
||||
- `server/src/main/proto/*.proto` — Message definitions
|
||||
|
||||
### Game Flow
|
||||
- `server/src/main/java/com/miti99/caro/server/event/ServerEventListener_CODE_GAME_MOVE.java`
|
||||
- `server/src/main/java/com/miti99/caro/server/event/ServerEventListener_CODE_GAME_STARTING.java`
|
||||
- `server/src/main/java/com/miti99/caro/server/event/handler/GameMoveHandler.java`
|
||||
- `server/src/main/java/com/miti99/caro/server/event/handler/GameStartingHandler.java`
|
||||
- `client/src/scenes/game-scene.js`
|
||||
|
||||
### UI
|
||||
|
||||
+38
-50
@@ -27,8 +27,7 @@ docker compose up --build -d
|
||||
|
||||
Then:
|
||||
- Client: `http://localhost:8080/`
|
||||
- Server TCP: `localhost:1024` (Protobuf)
|
||||
- Server WebSocket: `ws://localhost:1025/ratel`
|
||||
- Server WebSocket: `ws://localhost:1999/ratel` (binary typed protobuf)
|
||||
|
||||
Stop:
|
||||
```bash
|
||||
@@ -36,7 +35,7 @@ docker compose down
|
||||
```
|
||||
|
||||
What's running:
|
||||
- **caro-server** — Java 25 server, listens on `1024` (TCP) + `1025` (WebSocket)
|
||||
- **caro-server** — Java 25 server, listens on port `1999` (WebSocket only at `/ratel`)
|
||||
- **caro-client** — Nginx serving the built Vite bundle on host port `8080`
|
||||
|
||||
Storage: in-memory only; games are not persisted. Restarting clears all rooms.
|
||||
@@ -84,20 +83,19 @@ Tests: 29 GomokuHelperTest + 8 GomokuAITest (JUnit 5). All must pass.
|
||||
### 3. Run the Server
|
||||
|
||||
```bash
|
||||
java -jar server/build/libs/caro-server-0.0.1.jar -p 1024
|
||||
java -jar server/build/libs/caro-server-0.0.1.jar
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
[INFO] Server listening on port 1024 (TCP)
|
||||
[INFO] WebSocket server listening on port 1025
|
||||
[INFO] Server listening on port 1999 (WebSocket)
|
||||
[INFO] WebSocket endpoint: /ratel
|
||||
```
|
||||
|
||||
What's running:
|
||||
- **TCP port 1024** — Protobuf protocol
|
||||
- **WebSocket port 1025** — JSON protocol at `/ratel`
|
||||
- **WebSocket port 1999** — Binary typed protobuf at `/ratel`
|
||||
|
||||
Note: non-WebSocket HTTP requests to `1025` return a 400/403 (no static file serving).
|
||||
Connect via: `ws://localhost:1999/ratel`
|
||||
|
||||
### 4. Run the Client (Vite dev server)
|
||||
|
||||
@@ -124,11 +122,13 @@ Open `http://localhost:5173/` — Phaser 3 client with full game UI.
|
||||
### Option A: Standalone JAR
|
||||
|
||||
```bash
|
||||
java -jar caro-server-0.0.1.jar -p 1024
|
||||
java -jar caro-server-0.0.1.jar
|
||||
```
|
||||
|
||||
Server defaults to port 1999 (WebSocket only).
|
||||
|
||||
For production:
|
||||
- Run in background: `nohup java -jar ... &`
|
||||
- Run in background: `nohup java -jar caro-server-0.0.1.jar &`
|
||||
- Or use systemd (see below)
|
||||
- Or container (Docker)
|
||||
|
||||
@@ -138,10 +138,6 @@ System requirements:
|
||||
- 100 MB disk
|
||||
- Java 25 runtime
|
||||
|
||||
Port configuration:
|
||||
- Server listens on `-p {port}` (TCP)
|
||||
- WebSocket automatically uses `{port}+1`
|
||||
|
||||
### Option B: Docker Container
|
||||
|
||||
The included `server/Dockerfile` is multi-stage:
|
||||
@@ -156,8 +152,7 @@ docker build -f server/Dockerfile -t caro-server:0.0.1 .
|
||||
Run:
|
||||
```bash
|
||||
docker run -d --name caro-server \
|
||||
-p 1024:1024/tcp \
|
||||
-p 1025:1025/tcp \
|
||||
-p 1999:1999/tcp \
|
||||
caro-server:0.0.1
|
||||
```
|
||||
|
||||
@@ -174,7 +169,7 @@ After=network.target
|
||||
Type=simple
|
||||
User=gameserver
|
||||
WorkingDirectory=/opt/caro
|
||||
ExecStart=/usr/bin/java -jar /opt/caro/caro-server-0.0.1.jar -p 1024
|
||||
ExecStart=/usr/bin/java -jar /opt/caro/caro-server-0.0.1.jar
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
StandardOutput=journal
|
||||
@@ -244,7 +239,7 @@ server {
|
||||
|
||||
# WebSocket proxy to server
|
||||
location /ratel {
|
||||
proxy_pass http://localhost:1025;
|
||||
proxy_pass http://localhost:1999;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
@@ -267,25 +262,17 @@ sudo systemctl restart nginx
|
||||
|
||||
### Server Options
|
||||
|
||||
```bash
|
||||
java -jar caro-server-0.0.1.jar [OPTIONS]
|
||||
```
|
||||
Server uses defaults. No command-line arguments to configure port. Default port is 1999.
|
||||
|
||||
Available options:
|
||||
```
|
||||
-p, -port TCP port (default: 1024)
|
||||
WebSocket uses port + 1
|
||||
```
|
||||
|
||||
Examples:
|
||||
```bash
|
||||
java -jar caro-server-0.0.1.jar -p 1024 # TCP:1024, WS:1025
|
||||
java -jar caro-server-0.0.1.jar -p 8080 # TCP:8080, WS:8081
|
||||
To change port, modify `SimpleServer.java` and rebuild:
|
||||
```java
|
||||
// Default: 1999
|
||||
private static final int DEFAULT_PORT = 1999;
|
||||
```
|
||||
|
||||
### Client Configuration
|
||||
|
||||
Connection endpoint is in `client/src/services/connection-service.js`. By default the client connects to WS on `window.location.hostname:1025/ratel`. To override, edit that file and rebuild.
|
||||
Connection endpoint is in `client/src/services/connection-service.js`. By default the client connects to WS on `window.location.hostname:1999/ratel`. To override, edit that file and rebuild.
|
||||
|
||||
---
|
||||
|
||||
@@ -329,13 +316,13 @@ No automated tests currently. Manual testing:
|
||||
### Server Health Check
|
||||
|
||||
```bash
|
||||
# Check TCP port
|
||||
netstat -an | grep 1024
|
||||
# Check WebSocket port
|
||||
netstat -an | grep 1999
|
||||
# or
|
||||
lsof -i :1024
|
||||
lsof -i :1999
|
||||
|
||||
# Test WebSocket
|
||||
websocat ws://localhost:1025/ratel
|
||||
websocat ws://localhost:1999/ratel
|
||||
```
|
||||
|
||||
### Logs
|
||||
@@ -359,7 +346,7 @@ netstat -an | grep -c ESTABLISHED
|
||||
Restart:
|
||||
```bash
|
||||
kill $(pgrep -f caro-server)
|
||||
java -jar server/build/libs/caro-server-0.0.1.jar -p 1024
|
||||
java -jar server/build/libs/caro-server-0.0.1.jar
|
||||
```
|
||||
|
||||
---
|
||||
@@ -369,24 +356,25 @@ java -jar server/build/libs/caro-server-0.0.1.jar -p 1024
|
||||
### Port Already in Use
|
||||
|
||||
```bash
|
||||
lsof -i :1024
|
||||
lsof -i :1999
|
||||
kill -9 <PID>
|
||||
# Or use a different port
|
||||
java -jar caro-server-0.0.1.jar -p 9090
|
||||
```
|
||||
|
||||
To use a different port, modify `SimpleServer.java` and rebuild.
|
||||
|
||||
### Connection Refused
|
||||
|
||||
1. Verify server is running: `ps aux | grep caro-server`
|
||||
2. Check firewall: `sudo ufw allow 1024/tcp && sudo ufw allow 1025/tcp`
|
||||
3. Test connectivity: `nc -zv localhost 1024`
|
||||
2. Check firewall: `sudo ufw allow 1999/tcp`
|
||||
3. Test connectivity: `nc -zv localhost 1999`
|
||||
4. Docker: `docker compose ps` and `docker compose port server`
|
||||
|
||||
### WebSocket Connection Fails
|
||||
|
||||
1. WebSocket listens on `TCP port + 1` (default 1025)
|
||||
2. Ensure the client URL is correct: `ws://host:1025/ratel`
|
||||
3. Test directly: `websocat ws://localhost:1025/ratel`
|
||||
1. WebSocket listens on port 1999 at path `/ratel`
|
||||
2. Ensure the client URL is correct: `ws://host:1999/ratel`
|
||||
3. Test directly: `websocat ws://localhost:1999/ratel`
|
||||
4. Verify server logs for request decoding errors
|
||||
|
||||
### High Memory Usage
|
||||
|
||||
@@ -395,7 +383,7 @@ java -jar caro-server-0.0.1.jar -p 9090
|
||||
netstat -an | grep ESTABLISHED | wc -l
|
||||
|
||||
# Increase JVM heap
|
||||
java -Xmx2g -jar caro-server-0.0.1.jar -p 1024
|
||||
java -Xmx2g -jar caro-server-0.0.1.jar
|
||||
|
||||
# Restart to reclaim memory
|
||||
```
|
||||
@@ -409,7 +397,7 @@ Before going live:
|
||||
- [ ] Java 25 installed and verified
|
||||
- [ ] `./server/gradlew -p server clean build` passes (37 tests)
|
||||
- [ ] Server boots without errors
|
||||
- [ ] TCP port 1024 and WebSocket port 1025 open (firewall)
|
||||
- [ ] WebSocket port 1999 open (firewall)
|
||||
- [ ] Client built: `npm --prefix client ci && npm --prefix client run build`
|
||||
- [ ] Client connects to correct server endpoint
|
||||
- [ ] Manual test: create game, make moves, join room, spectate
|
||||
@@ -425,7 +413,7 @@ Before going live:
|
||||
1. Back up current jar
|
||||
2. Pull + rebuild: `git pull && ./server/gradlew -p server clean build`
|
||||
3. Stop: `kill $(pgrep -f caro-server)`
|
||||
4. Start new jar: `java -jar server/build/libs/caro-server-0.0.1.jar -p 1024`
|
||||
4. Start new jar: `java -jar server/build/libs/caro-server-0.0.1.jar`
|
||||
|
||||
### Update Client
|
||||
|
||||
@@ -447,7 +435,7 @@ Before going live:
|
||||
For production servers with 4+ GB RAM:
|
||||
|
||||
```bash
|
||||
java -Xmx4g -XX:+UseG1GC -jar caro-server-0.0.1.jar -p 1024
|
||||
java -Xmx4g -XX:+UseG1GC -jar caro-server-0.0.1.jar
|
||||
```
|
||||
|
||||
Java 25 defaults are already sensible; tune only if needed.
|
||||
|
||||
+19
-16
@@ -36,10 +36,11 @@ Caro (also known as Gomoku or Five-in-a-Row) is a classic strategy board game. T
|
||||
### Game UI
|
||||
- **Client (Phaser 3):** 800x800 board with wood texture, stone animations, sound effects, move history panel
|
||||
|
||||
### Cross-Protocol Support
|
||||
- **TCP/Protobuf:** lower latency, binary protocol
|
||||
- **WebSocket/JSON:** easier browser integration
|
||||
- Both run simultaneously on different ports (`1024` TCP, `1025` WebSocket by default)
|
||||
### WebSocket Protocol
|
||||
- **Binary WebSocket:** BINARY frames carrying TYPED PROTOBUF messages at port 1999
|
||||
- `ws://localhost:1999/ratel` endpoint
|
||||
- Typed message dispatch via `ClientRequest` records and sealed interface pattern
|
||||
- Single-port architecture (no legacy transports)
|
||||
|
||||
---
|
||||
|
||||
@@ -71,8 +72,8 @@ Caro (also known as Gomoku or Five-in-a-Row) is a classic strategy board game. T
|
||||
|
||||
| Component | Technology | Details |
|
||||
|-----------|-----------|---------|
|
||||
| **Server** | Java 25 + Netty 4.1 | Asynchronous, event-driven, low-latency |
|
||||
| **Network Protocol** | Protobuf (TCP) + JSON/gson (WebSocket) | Dual protocol, language-agnostic |
|
||||
| **Server** | Java 25 + Netty 4.1.128 | Asynchronous, event-driven, low-latency |
|
||||
| **Network Protocol** | Typed Protobuf over WebSocket (binary frames) | Single protocol, typed record dispatch |
|
||||
| **Game Logic** | Pure Java 25 (records, switch expressions) | Board state, move validation, win detection, AI |
|
||||
| **Client** | Phaser 3 + Vite + Vanilla JS | No framework dependencies (besides Phaser) |
|
||||
| **Build** | Gradle 9.x (Kotlin DSL) + Shadow plugin / Vite | Standalone server jar + static client bundle |
|
||||
@@ -139,16 +140,18 @@ Caro (also known as Gomoku or Five-in-a-Row) is a classic strategy board game. T
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────────┐
|
||||
│ Web Browser │◄───────►│ Phaser 3 Client │
|
||||
│ (http://...) │ WS/JSON │ (Vite + JS, :8080) │
|
||||
└─────────────────┘ └─────────────────────┘
|
||||
│ (http://...) │ Typed │ (Vite + JS, :8080) │
|
||||
└─────────────────┘ Protobuf└─────────────────────┘
|
||||
│
|
||||
│ WebSocket :1025/ratel
|
||||
WS :1999/ratel
|
||||
BINARY frames
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Java 25 Netty Server (com.miti99.caro.server) │
|
||||
│ ├─ WebsocketTransferHandler (WS → game events) │
|
||||
│ ├─ ProtobufTransferHandler (TCP → game events) │
|
||||
│ └─ ServerEventListener_* (process moves, AI) │
|
||||
│ ├─ WebsocketTransferHandler (WS decoder) │
|
||||
│ ├─ RequestConverter (wire → ClientRequest) │
|
||||
│ ├─ RequestDispatcher (pattern-match dispatch) │
|
||||
│ └─ *Handler classes (14 typed request handlers) │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
@@ -167,7 +170,7 @@ docker compose up --build -d
|
||||
### Quick Start (Local)
|
||||
```bash
|
||||
./server/gradlew -p server build -x test
|
||||
java -jar server/build/libs/caro-server-0.0.1.jar -p 1024
|
||||
java -jar server/build/libs/caro-server-0.0.1.jar
|
||||
# In another terminal:
|
||||
npm --prefix client install
|
||||
npm --prefix client run dev
|
||||
@@ -191,9 +194,9 @@ See `deployment-guide.md` for detailed setup instructions.
|
||||
| Dependency | Version | Purpose |
|
||||
|-----------|---------|---------|
|
||||
| Java | 25 (LTS) | Language runtime |
|
||||
| Netty | 4.1.115.Final | Async networking |
|
||||
| Protobuf | 3.25.5 | Binary serialization (TCP wire) |
|
||||
| gson | 2.11.0 | JSON serialization (WS wire) |
|
||||
| Netty | 4.1.128.Final | Async networking |
|
||||
| Protobuf | 3.25.5 | Binary serialization (WS wire) |
|
||||
| protobufjs | 7.5.4 | JavaScript protobuf codec (client) |
|
||||
| JUnit Jupiter | 5.11.3 | Test framework |
|
||||
| Gradle | 9.2.1 (wrapper) | Java build tool |
|
||||
| Shadow plugin | 8.3.5 | Fat jar packaging |
|
||||
|
||||
+58
-41
@@ -2,14 +2,14 @@
|
||||
|
||||
## High-Level Overview
|
||||
|
||||
Caro is a **client-server multiplayer game** with dual-protocol networking:
|
||||
Caro is a **client-server multiplayer game** with typed-protobuf WebSocket:
|
||||
|
||||
```
|
||||
┌──────────────┐ WebSocket ┌──────────────────────────────┐
|
||||
│ Client │◄────/JSON───►│ │
|
||||
│ (Phaser 3) │ │ Java 25 Netty Server │
|
||||
└──────────────┘ │ Port 1024: TCP/Protobuf │
|
||||
│ Port 1025: WebSocket/JSON │
|
||||
│ Client │◄──BINARY────►│ │
|
||||
│ (Phaser 3) │ Typed Proto │ Java 25 Netty Server │
|
||||
└──────────────┘ │ Port 1999: WebSocket only │
|
||||
│ Path: /ratel │
|
||||
│ │
|
||||
│ Game Logic: │
|
||||
│ - Room Management │
|
||||
@@ -19,8 +19,6 @@ Caro is a **client-server multiplayer game** with dual-protocol networking:
|
||||
└──────────────────────────────┘
|
||||
```
|
||||
|
||||
Non-WebSocket HTTP requests to port `1025` are rejected by Netty with a default 400/403 (there is no static file handler).
|
||||
|
||||
---
|
||||
|
||||
## Component Architecture
|
||||
@@ -30,21 +28,19 @@ Non-WebSocket HTTP requests to port `1025` are rejected by Netty with a default
|
||||
**File:** `server/src/main/java/com/miti99/caro/server/`
|
||||
|
||||
**Responsibilities:**
|
||||
- Listen on TCP (1024) and WebSocket (1025) simultaneously
|
||||
- Parse incoming messages (Protobuf or JSON via gson)
|
||||
- Listen on WebSocket port 1999 at path `/ratel`
|
||||
- Parse incoming binary TYPED PROTOBUF messages (`ClientRequest` oneof wrapper)
|
||||
- Execute game logic (move validation, win checks)
|
||||
- Broadcast state updates to all connected clients
|
||||
- Broadcast state updates via typed `Response` messages (binary frames)
|
||||
- Run AI for PVE games
|
||||
- Manage room lifecycle (create, join, spectate, cleanup)
|
||||
|
||||
**Key Classes:**
|
||||
- `SimpleServer` — Entry point; starts Netty bootstrap for both ports
|
||||
- `ServerEventListener` — Base for event handlers (in `event/`)
|
||||
- `ServerEventListener_CODE_*` — Individual handlers for each ServerEventCode
|
||||
- `ProtobufTransferHandler` — Netty pipeline handler for TCP
|
||||
- `WebsocketTransferHandler` — Netty pipeline handler for WebSocket (deserializes `Msg` record via gson)
|
||||
- `SecondProtobufCodec` — Second-pass protobuf codec
|
||||
- `ProtobufProxy` / `WebsocketProxy` — Bootstrap the TCP and WebSocket server sockets
|
||||
- `SimpleServer` — Entry point; starts Netty bootstrap for WebSocket only
|
||||
- `WebsocketTransferHandler` — Netty pipeline handler (decodes binary frame to `Request` protobuf)
|
||||
- `RequestConverter` — Converts protobuf `Request` oneof to `ClientRequest` sealed records
|
||||
- `RequestDispatcher` — Pattern-matching switch dispatching `ClientRequest` to typed handlers
|
||||
- `*Handler` — 14 individual request handlers (SetNicknameHandler, CreateRoomHandler, GameMoveHandler, etc.)
|
||||
|
||||
**Event Codes (ServerEventCode)** — sent by clients:
|
||||
```
|
||||
@@ -109,7 +105,7 @@ client/src/
|
||||
- **Event Bus:** Decouples scenes, services, UI components. `emit(event, data)` → listeners respond.
|
||||
- **Game State Service:** Single source of truth for board, room, players.
|
||||
- **Connection Service:** Reconnect logic and heartbeat (30-second interval).
|
||||
- **WebSocket Message Format:** `{ code: "CODE_GAME_MOVE", data: "{...}", info: "" }`
|
||||
- **Client Connection:** Initiates WebSocket handshake to `ws://localhost:1999/ratel`, then sends typed `Request` messages in binary
|
||||
|
||||
---
|
||||
|
||||
@@ -118,17 +114,16 @@ client/src/
|
||||
**File:** `server/src/main/java/com/miti99/caro/common/`
|
||||
|
||||
**Responsibilities:**
|
||||
- Shared entities (Board, Room, GameMove, Msg, ClientSide)
|
||||
- Shared enums (ServerEventCode, ClientEventCode, PieceType, GameResult, RoomType, RoomStatus)
|
||||
- Shared entities (Board, Room, GameMove, ClientSide)
|
||||
- Shared enums (ClientEventCode, PieceType, GameResult, RoomType, RoomStatus)
|
||||
- Game logic (move validation, win detection, AI)
|
||||
- Utilities (gson JSON, list, options, stream helpers)
|
||||
- Utilities (list, options, stream helpers)
|
||||
|
||||
**Key Classes:**
|
||||
- `Board` — 15x15 grid, move validation, win/draw detection
|
||||
- `Room` — Encapsulates game state, players, spectators
|
||||
- `GameMove` — Represents a single move (row, col, piece type, playerId, timestamp)
|
||||
- `Msg` — **record** for WebSocket JSON envelope (`code`, `data`, `info`)
|
||||
- `ServerTransferData` / `ClientTransferData` — Protobuf-generated wire types
|
||||
- `ClientSide` — Player connection metadata
|
||||
- `GomokuHelper` — Win detection (4 directions)
|
||||
- `GomokuAI` — AI move selection (Easy, Medium, Hard)
|
||||
- Enums under `common/enums/`
|
||||
@@ -141,19 +136,33 @@ Note: `common` is a sub-package within the single `server/` Gradle project. It i
|
||||
|
||||
### Message Format
|
||||
|
||||
**WebSocket (JSON, via gson):**
|
||||
```json
|
||||
{
|
||||
"code": "CODE_GAME_MOVE",
|
||||
"data": "{\"row\": 7, \"col\": 7}",
|
||||
"info": ""
|
||||
**WebSocket (Binary Protobuf):**
|
||||
|
||||
Client sends `Request` oneof message (defined in `server/src/main/proto/request.proto`):
|
||||
```
|
||||
message Request {
|
||||
oneof payload {
|
||||
SetNicknameRequest set_nickname = 1;
|
||||
CreateRoomRequest create_room = 2;
|
||||
CreatePveRoomRequest create_pve_room = 3;
|
||||
... 11 more typed requests ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
gson 2.11 serializes the `Msg` record by reading its canonical components (`code`, `data`, `info`). Null components are skipped by default, preserving wire compatibility with the previous setter-based class.
|
||||
Server replies with `Response` oneof message (defined in `server/src/main/proto/response.proto`):
|
||||
```
|
||||
message Response {
|
||||
oneof payload {
|
||||
ClientConnectResponse client_connect = 1;
|
||||
RoomCreateResponse room_create = 2;
|
||||
GameMoveResponse game_move = 3;
|
||||
... additional typed responses ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**TCP (Protobuf):**
|
||||
Binary format (serialized via Protobuf 3.25.5).
|
||||
Each message is **binary-encoded** in a WebSocket frame (`binaryType='arraybuffer'`).
|
||||
|
||||
### Connection Flow
|
||||
|
||||
@@ -268,10 +277,14 @@ HttpObjectAggregator (8 KB)
|
||||
↓
|
||||
WebSocketServerProtocolHandler ("/ratel")
|
||||
↓
|
||||
WebsocketTransferHandler (decode Msg record via gson, dispatch event listener)
|
||||
WebsocketTransferHandler (decode binary Request frame, dispatch to RequestDispatcher)
|
||||
↓
|
||||
RequestConverter (Request oneof → ClientRequest sealed variant)
|
||||
↓
|
||||
RequestDispatcher (pattern-match dispatch to typed *Handler)
|
||||
```
|
||||
|
||||
The pipeline has no static file handler. Non-WS HTTP requests to port 1025 return Netty's default HTTP error.
|
||||
Outgoing: Handler classes call `ChannelUtils.push(Response)` which encodes to binary and sends `BinaryWebSocketFrame`.
|
||||
|
||||
---
|
||||
|
||||
@@ -400,7 +413,7 @@ client/ (no dependencies except Phaser 3, Vite dev-only)
|
||||
│ └─ caro-client (Nginx + dist/) │
|
||||
└────────────────────────────────────┘
|
||||
|
||||
Server listens on `:1024` (TCP) and `:1025` (WebSocket only).
|
||||
Server listens on `:1999` (WebSocket only, at `/ratel`).
|
||||
```
|
||||
|
||||
Docker Compose runs both services (`caro-server` + `caro-client`) from the single repo context.
|
||||
@@ -412,23 +425,27 @@ Docker Compose runs both services (`caro-server` + `caro-client`) from the singl
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `server/src/main/java/com/miti99/caro/server/SimpleServer.java` | Server entry point |
|
||||
| `server/src/main/proto/request.proto` | Client→server typed message definitions |
|
||||
| `server/src/main/proto/response.proto` | Server→client typed message definitions |
|
||||
| `client/src/main.js` | Client entry point (Phaser) |
|
||||
| `server/src/main/java/com/miti99/caro/common/entity/Board.java` | Game board state + validation |
|
||||
| `server/src/main/java/com/miti99/caro/common/helper/GomokuHelper.java` | Win detection algorithm |
|
||||
| `server/src/main/java/com/miti99/caro/common/robot/GomokuAI.java` | AI move selection (3 difficulties) |
|
||||
| `server/src/main/java/com/miti99/caro/common/entity/Room.java` | Game room state container |
|
||||
| `server/src/main/java/com/miti99/caro/common/entity/Msg.java` | WebSocket JSON envelope (record) |
|
||||
| `server/src/main/java/com/miti99/caro/server/event/ServerEventListener_*.java` | Event handlers (game logic) |
|
||||
| `server/src/main/java/com/miti99/caro/server/handler/WebsocketTransferHandler.java` | WS codec |
|
||||
| `server/src/main/java/com/miti99/caro/server/event/RequestConverter.java` | Protobuf→ClientRequest record |
|
||||
| `server/src/main/java/com/miti99/caro/server/event/RequestDispatcher.java` | Dispatch ClientRequest→Handler |
|
||||
| `server/src/main/java/com/miti99/caro/server/event/handler/*.java` | 14 typed request handlers |
|
||||
| `server/src/main/java/com/miti99/caro/server/handler/WebsocketTransferHandler.java` | WS binary codec |
|
||||
| `client/src/scenes/game-scene.js` | Client main gameplay scene |
|
||||
| `client/src/services/connection-service.js` | WebSocket client |
|
||||
| `client/src/config/protocol-constants.js` | Event code enums |
|
||||
| `client/src/services/connection-service.js` | WebSocket client (binary mode) |
|
||||
| `client/src/config/protocol-constants.js` | ClientEventCode enum (event-bus keys) |
|
||||
| `client/src/generated/protocol.{js,d.ts}` | Protobuf codegen (protobufjs) |
|
||||
|
||||
---
|
||||
|
||||
## Future Architectural Improvements
|
||||
|
||||
1. **Proto-over-WebSocket** — migrate WS payloads from JSON to Protobuf (the `.proto` files are already staged under `server/src/main/resources/proto/`).
|
||||
1. **Enhanced request validation** — add server-side schema validation for stricter type safety.
|
||||
2. **Database integration** — Persist games, leaderboards, accounts.
|
||||
3. **Virtual threads** — Java 25 has mature virtual-thread support; some blocking code paths (e.g. AI hard-depth search) could be offloaded.
|
||||
4. **Message broker (Kafka/RabbitMQ)** — Decouple game logic from network I/O.
|
||||
|
||||
+3
-3
@@ -21,7 +21,7 @@ WORKDIR /app
|
||||
|
||||
COPY --from=build /build/server/build/libs/caro-server-0.0.1.jar app.jar
|
||||
|
||||
# TCP (protobuf) and WebSocket ports
|
||||
EXPOSE 1024 1025
|
||||
# Single WebSocket port carrying typed protobuf binary frames
|
||||
EXPOSE 1999
|
||||
|
||||
ENTRYPOINT ["java", "-jar", "app.jar", "-p", "1024"]
|
||||
ENTRYPOINT ["java", "-jar", "app.jar", "-p", "1999"]
|
||||
|
||||
Reference in New Issue
Block a user