Skip to content

WebSocket STOMP Protocol

This document describes the reverse-engineered WebSocket protocol used by dicechess.com for real-time multiplayer lobbies and gameplay synchronization.

The game uses STOMP (Simple Text Oriented Messaging Protocol) over a WebSocket connection, typically managed via Java Spring Boot on the backend.


The WebSocket connection is established by upgrading an HTTP connection to: wss://dicechess.com/ws (or similar endpoint indicated in the headers).

Once connected to the WebSocket, the client must negotiate the STOMP connection by sending a CONNECT frame.

The client passes the JWT auth token inside the Authorization header.

CONNECT
Authorization:<JWT_TOKEN>
accept-version:1.2,1.1,1.0
heart-beat:0,0

The server responds with a CONNECTED frame confirming authentication and providing the authenticated user’s ID as the user-name.

CONNECTED
version:1.2
heart-beat:0,0
user-name:<USER_ID>

To receive updates about lobby events (game creation, players joining, and active user stats), the client subscribes to public and user-specific topics.

The client sends a SUBSCRIBE frame to /topic/lobby:

SUBSCRIBE
id:sub-0
destination:/topic/lobby

Messages received from /topic/lobby are JSON payloads.

Sent immediately upon subscribing. Contains a list of all currently open lobby games waiting for a player.

{
"CURRENT_GAMES": [
{
"id": "83df5eee-6109-11f1-a103-15657712aeb3",
"created": 1780682892025,
"creator": {
"userId": 1837808,
"name": "BSmith72",
"avatar": "chessPieces_11",
"country": "US",
"rating": 1574.10,
"x2Rating": 1532.72
},
"secondPlayer": null,
"gameSetup": {
"board": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"dices": null,
"timeLimit": 120,
"timeBonus": 0,
"betAmount": "3",
"currency": "GOLD",
"allowDoubling": false,
"timeSetup": null,
"tournamentId": null,
"side": null,
"botLevel": null,
"isFriendGame": false
},
"state": "OPEN"
}
]
}

Sent when a game lobby state transitions (e.g., a player joins or a game starts).

  • JOINING State:

    {
    "CHANGE_GAME": {
    "creator": { "userId": 2010, "name": "Alehandro081", ... },
    "gameId": "8e177d82-6109-11f1-a103-bfc700bd1257",
    "newState": "JOINING",
    "secondPlayer": null,
    "lobbyVersion": 162191,
    "rematchedGameId": null
    }
    }
  • PLAY State:

    {
    "CHANGE_GAME": {
    "creator": { "userId": 163, "name": "Rabestro", ... },
    "gameId": "91498393-6109-11f1-a103-ef1e398a7dfc",
    "newState": "PLAY",
    "secondPlayer": { "userId": -43, "name": "DC Coach Master", "avatar": "bot_3", ... },
    "lobbyVersion": 162195,
    "rematchedGameId": null
    }
    }

Sent when a game is started or cancelled, instructing the lobby UI to remove it from the open games list.

{
"REMOVE_GAME": "91498393-6109-11f1-a103-ef1e398a7dfc"
}

A periodic heartbeat/status update containing current server load details.

{
"ACTIVITY_INFO": {
"activeUsers": 91,
"activeGames": 26,
"build": 1780404316,
"commitHash": "da6efbbc",
"serverTime": 1780682908491
}
}

When a game transitions to the PLAY state, the client subscribes to a specific game instance topic and swaps commands with the server.

The client subscribes to a user-specific destination using the game’s UUID:

SUBSCRIBE
id:sub-3
destination:/user/topic/game/instance/<GAME_UUID>

All commands are sent to the /app/game/instance/<GAME_UUID> destination using the SEND frame.

Sent when the client connects/re-connects to synchronise active game views and session configurations.

{
"type": "UPDATE_STATE",
"gameId": "91498393-6109-11f1-a103-ef1e398a7dfc",
"value": "{\"sessionId\":\"1ce9576b38ca29\",\"previousState\":\"\",\"debugInfo\":\"{\\\"url\\\":\\\"/game/91498393-6109-11f1-a103-ef1e398a7dfc\\\",\\\"username\\\":\\\"Rabestro\\\",\\\"userId\\\":163,\\\"sessionId\\\":\\\"1ce9576b38ca29\\\"}\"}"
}

Sent occasionally to request/assert sync of game clocks.

{
"type": "UPDATE_TIMER",
"gameId": "91498393-6109-11f1-a103-ef1e398a7dfc",
"value": "{\"sessionId\":\"1ce9576b38ca29\"}"
}

Requests rolling the dice at the start of a turn.

{
"type": "THROW_DICES",
"gameId": "91498393-6109-11f1-a103-ef1e398a7dfc"
}

Executes a single chess micro-move.

{
"type": "MOVE",
"gameId": "91498393-6109-11f1-a103-ef1e398a7dfc",
"value": "e7e5",
"leftTime": 594960.75
}
  • value: The move coordinates in UCI format (e.g. e7e5, d8f6).
  • leftTime: The client’s remaining time in milliseconds (float format).

Messages from the game channel arrive at /user/topic/game/instance/<GAME_UUID> formatted with a message envelope that encapsulates a list of updates (messageList).

{
"gameId": "91498393-6109-11f1-a103-ef1e398a7dfc",
"players": [163],
"messageList": [
{
"gameMessageType": "UPDATE_STATE | TIMER | DICES | ANIMATIONS | START_TURN | CHAT | END_GAME | CHECKSUM",
"messageNumber": 12,
"message": { ... }
}
],
"currentTime": 1780682916010,
"queue": "queue_game_gameserver_to_web"
}
  • messageNumber: Incremental ID sequence, allowing the client to process incoming events in exact order.

Provides a full snapshot of the board, timers, players, chat logs, and game status.

{
"gameMessageType": "UPDATE_STATE",
"message": {
"state": {
"board": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR",
"currency": "GOLD",
"allowDoubling": false,
"players": {
"163": { "side": "BLACK", "betAmount": 0, "canOffer": { ... }, "publicProfile": { ... } },
"-43": { "side": "WHITE", "betAmount": 0, "canOffer": { ... }, "publicProfile": { ... } }
},
"capturedPieces": {},
"dices": [],
"dicesToShow": [],
"allowedMoves": [],
"status": "ACTIVE",
"turnParticipant": -43,
"timerUserId": -43,
"leftTime": { "163": 600000, "-43": 600000 },
"chat": [ ... ]
}
}
}

Published in response to a THROW_DICES action. Details the rolled dice values, allowed moves, and turn index.

{
"gameMessageType": "DICES",
"message": {
"activePlayerId": 163,
"dices": [
{ "value": "q", "allowed": true, "used": false },
{ "value": "p", "allowed": true, "used": false },
{ "value": "n", "allowed": true, "used": false }
],
"dicesToShow": [
{ "value": "q", "allowed": false, "used": false },
{ "value": "p", "allowed": true, "used": false },
{ "value": "n", "allowed": true, "used": false }
],
"allowedMoves": ["c7c5", "c7c6", "d7d5", "b8c6", "g8f6"],
"throwCount": 2
}
}
  • dices: The physical raw dice values. Lowercase designates Black pieces (q, p, n), uppercase designates White (Q, P, N).
  • dicesToShow: Dice values exposed in the UI. A die may be marked allowed: false if no legal moves exist for that piece type.
  • allowedMoves: UCI move strings currently legal for this specific dice throw state.

Specifies piece animations for moves executed by a player.

{
"gameMessageType": "ANIMATIONS",
"message": {
"moves": ["e7e5"],
"captures": [],
"capturedPieces": {}
}
}

Clocks delta update, broadcast to ensure synchronization.

{
"gameMessageType": "TIMER",
"message": {
"timerPlayerId": 163,
"leftTime": { "163": 600000, "-43": 607038 },
"leftFreeTime": { "163": 0, "-43": 0 },
"lastTimeCalculation": 1780682917505,
"messageServerTime": 1780682917881,
"timerDisabled": false
}
}

Signals that a player’s micro-turn has begun.

{
"gameMessageType": "START_TURN",
"message": {
"activePlayerId": -43,
"await": 163,
"board": "r1b1kbnr/pppp1ppp/2n2q2/4p3/8/2N5/PPPPPPPP/R1BQKBNR",
"dicesToShow": [
{ "value": "q", "allowed": false, "used": true },
{ "value": "p", "allowed": false, "used": true },
{ "value": "n", "allowed": false, "used": true }
]
}
}

Broadcasts system chat messages representing in-game logs (dice rolls, physical moves) and user-typed chat messages.

  • Dice Roll Chat representation:

    {
    "gameMessageType": "CHAT",
    "message": {
    "authorId": 163,
    "type": "DICES",
    "text": "qpn",
    "time": 1780682917882,
    "throwCount": 2
    }
    }
  • Move Chat representation:

    {
    "gameMessageType": "CHAT",
    "message": {
    "authorId": 163,
    "type": "MOVE",
    "move": { "movedPiece": "p", "capturedPiece": " ", "move": "e5", "isCastling": false },
    "text": "",
    "time": 1780682922528
    }
    }

An integrity validation hash containing a representation of the board state.

{
"gameMessageType": "CHECKSUM",
"message": {
"checksum": 1696035071
}
}

Dispatched when the game ends, outlining final outcomes.

{
"gameMessageType": "END_GAME",
"message": {
"loseCondition": "KING_LOST",
"playerStates": {
"163": "WINNER",
"-43": "LOSER"
},
"prizes": {
"163": [0, 0],
"-43": [0, 0]
},
"gameEndTime": 1780682934038,
"playerLeftTimes": {
"163": 597382,
"-43": 613161
}
}
}