4.6 KiB
WebSocket Networking: First Iteration
Scope
The first networking iteration adds a Windows-only WebSocket transport using Boost.Asio and Boost.Beast.
The goal of this iteration is connection and replication stability. It does not yet implement multiplayer commands, authentication, per-connection players, state deltas, or presentation-event replication.
Runtime Architecture
Server
WebsocketGameServer wraps the headless InternalGameServer.
- It accepts any WebSocket connection.
- It sends the current complete game state immediately after connection.
- It broadcasts the current complete state every five seconds.
- Incoming WebSocket messages are read and discarded.
- The authoritative simulation remains on the main server thread.
- Network sessions only receive serialized state and never access
InternalGameServerdirectly.
The standalone server creates the existing fixed player ID "player" and does
not enable the battle-zone test bot.
Client
WebsocketGameClient implements IGameServer.
- It connects asynchronously to
ws://127.0.0.1:8080/. - Received messages are queued by the network thread and applied on the game
thread from
update(). - It maintains an internal
InternalGameServerreplica so existing snapshot consumers and transitional runtime-state bindings continue to work. submit()intentionally discards all commands.drainEvents()returns no events.- Client-side save and load calls are ignored because persistence remains server-side.
RemoteGameServerHost prepares local presentation resources and runtime
locations against the replicated state. It does not advance authoritative
simulation or write Lua/dialogue auxiliary state back to the server.
Campaign Routing
The graphical game uses stable router objects for IGameServer and
IGameServerHost, allowing existing UI and runtime references to remain valid.
When NETWORK is defined:
battle_zoneusesWebsocketGameClientandRemoteGameServerHost.- Every other campaign uses
InternalGameServerandLocalGameServerHost.
The WebSocket client is created only when battle_zone is selected and is
destroyed when that campaign is unloaded.
JSON Protocol
Messages use a versioned JSON envelope:
{
"protocolVersion": 1,
"type": "state",
"payload": {
"schemaVersion": 1
}
}
The payload temporarily uses the existing GameState::save() representation.
The client applies it through InternalGameServer::load().
Malformed JSON, unsupported protocol versions, unsupported message types, and invalid state payloads are logged and ignored.
This temporary full-state format is expected to be replaced or separated from the persistence schema as command handling and production replication are implemented.
Build Configuration
Networking is enabled only for Windows:
proj-windows/CMakeLists.txtdefinesNETWORK, compiles the WebSocket client and remote host, and linksws2_32andmswsock.server/CMakeLists.txtdefinesNETWORKfor the WindowsGameServer, compiles the WebSocket server, and links the Windows socket libraries.- Linux, Android, and Web builds do not define
NETWORKand do not compile the network transport.
The previous Web-build NETWORK definition was removed.
Running
Build the server:
cmake -S server -B server/build
cmake --build server/build --config Debug --target GameServer
Run it from the repository root:
.\server\build\Debug\GameServer.exe --campaign battle_zone --location uni_exterior
The listener defaults to 0.0.0.0:8080. It can be changed with:
--address IP
--port N
Launch the Windows game and select battle_zone. The client connects to
127.0.0.1:8080. Selecting another campaign continues to use the local server.
Verification
The implementation was verified with:
- A successful Windows graphical-game build.
- A successful Windows headless-server build.
- Server state and protocol tests.
- A WebSocket smoke test receiving the initial state and the next five-second broadcast.
- A check that the shared
"player"exists and the battle-zone test bot is not created for the network server.
Current Limitations
- All clients share the fixed player ID
"player". - Client commands are not sent to the server.
- The server does not process commands.
- State is sent as a complete snapshot rather than a delta.
- There is no authentication, authorization, reconnect loop, or session identity.
- Presentation events are not replicated.
- The endpoint is hardcoded in the graphical client.
- Multiplayer Lua and dialogue behavior remains unsupported.