Mon, 24 Aug 2026 15:48:19 +0200
complete documentation of the network protocol
resolves #938
| PROTOCOL.md | file | annotate | diff | comparison | revisions | |
| src/main.c | file | annotate | diff | comparison | revisions | |
| src/network.h | file | annotate | diff | comparison | revisions |
--- a/PROTOCOL.md Mon Aug 24 14:43:33 2026 +0200 +++ b/PROTOCOL.md Mon Aug 24 15:48:19 2026 +0200 @@ -84,7 +84,7 @@ ### Starting a New Game -When the server wants to start a new game, they SHALL send a `NETCODE_GAMEINFO` +When the server wants to start a new game, they SHALL send a `GAMEINFO` message. The payload of this message is as follows: * one BYTE denoting the color the server wants to play (16 or 0x10 = white, 32 or 0x20 = black) @@ -98,9 +98,9 @@ ### Continuing a Game -When the server wants to continue a game, they SHALL send a `NETCODE_PGNDATA` +When the server wants to continue a game, they SHALL send a `PGNDATA` message. The payload of this message is as follows: -* the same two BYTEs and three WORDs as in `NETCODE_GAMEINFO`, followed by +* the same two BYTEs and three WORDs as in `GAMEINFO`, followed by * one WORD for the number `n` of moves that will follow * the `n` MOVEs that have been played already @@ -125,41 +125,161 @@ ### Make a Move -### Resign +If the active player wants to make a move, they SHALL send a `MOVE` message +with one MOVE payload to the waiting player. +The active player SHALL then wait for an `ACCEPT_MOVE` or `DECLINE_MOVE` +message. + +When the waiting player receives a `MOVE` message, they SHOULD validate the +move. When validation fails, they SHALL send a `DECLINE_MOVE` message with one +byte indicating the reason why the move is declined. +Otherwise, they SHALL send an `ACCEPT_MOVE` message. + +When the move is accepted, both players SHALL swap the active / waiting role. + +The reasons for declining a move are listed in the following table: + +| Reason | Value | Description | +|-----------------------|-------|---------------------------------------------| +| INVALID_MOVE_SYNTAX | 1 | E.g., indices out of bounds | +| PIECE_NOT_FOUND | 2 | The piece is not at the specified position. | +| NEED_PROMOTION | 4 | Target piece for promotion is missing. | +| PIECE_PINNED | 5 | Piece cannot move because it is pinned. | +| KING_IN_CHECK | 6 | The move would leave the king in check. | +| KING_MOVES_INTO_CHECK | 7 | The king would be moved into check. | +| MISSING_CHECK | 8 | The check-flag was not set. | +| MISSING_CHECKMATE | 9 | The checkmate-flag is not set. | +| INVALID_CHECK | 10 | The check-flag was incorrectly set. | +| INVALID_CHECKMATE | 11 | The checkmate-flag was incorrectly set. | +| RULES_VIOLATED | 32 | Other rules would be violated by this move. | + +### End the Game + +The active player, before making a move, SHALL determine if the game has ended. +The game SHALL be ended if a [checkmate](#checkmate) or [stalemate](#stalemate) +position has been reached, or the [clock runs out](#clock-timeout). + +The game SHOULD be ended if a draw can be [claimed](#claim-a-draw) due to a +threefold repetition of the same position, insufficient material, or playing +50 moves without any captures or pawn moves. + +The game MAY be ended at any time by any (including the waiting) player by +[resignation](#resign). + +#### Checkmate + +Whe the active player determines they were checkmated with the last move, +they SHALL send a `CHECKMATE` message to the waiting player. +When receiving such a message, the waiting player SHALL end the game without +sending any confirmation. + +#### Stalemate -### Offer or Claim a Draw +When the active player determines a stalemate position, they SHALL send a +`STALEMATE` message to the waiting player. +The waiting player SHOULD verify this claim. +When verification fails, they SHALL send a `DECLINE` message, and they SHALL +send an `ACCEPT` message, otherwise. + +Both parties SHALL end the game when the stalemate was agreed on. +Otherwise, both parties SHOULD continue playing. + +#### Clock Timeout + +When the clock of the active player times out, they SHALL send a `TIMEOVER` +message to the waiting player. +When receiving such a message, the waiting player SHALL end the game without +sending any confirmation. + +#### Claim a Draw + +The active player SHOULD claim a draw, when +* the position is already repeated a third time (threefold repetition rule) +* there is not enough material for both players on the board (nobody can win) +* no capture or pawn move was made within the last 50 moves + +The draw is claimed by sending a `THREEFOLD`, `NOMATERIAL`, or `50MOVES` +message, respectively. +The waiting player SHOULD verify this claim. +When verification fails, they SHALL send a `DECLINE` message, and they SHALL +send an `ACCEPT` message, otherwise. + +Both parties SHALL end the game when the draw was agreed on. +Otherwise, both parties SHOULD continue playing. + +#### Resign + +Both the active and the waiting player MAY send a `RESIGN` message any time. +When a player receives a `RESIGN` message, they SHALL end the game without +any further confirmation. + +### Offer a Draw + +_TODO: draw offers by the active player are bugged - see issue #980_ + +_TODO: the description is very confusing and should be cleaned up after fixing issue #980_ + +Both the active and the waiting player MAY send a `REMIS` message any time to +offer a draw. + +When the waiting player receives such an offer, they MAY choose to answer it +with either an `ACCEPT` or a `DECLINE` message. + +When the offer was accepted, both players SHALL end the game. +Otherwise, they SHALL continue playing. +When an offer was declined, the requesting player SHOULD NOT send another offer +for the same position. + +When the active player receives such an offer, they MAY ignore it. +No confirmation is sent. +Instead, if the player chooses to accept the offer, they SHALL send a `REMIS` +message on their own. ### Propose Resignation +The waiting player MAY propose resignation by sending a `TAUNT` message. + +The active player MAY ignore such messages completely. +Implementations MAY choose to display a message to the active player. +If the active player decides to follow the proposal, they SHOULD send a +`RESIGN` message, as described [above](#resign). + +The waiting player SHOULD NOT use this message excessively or inappropriately. +It serves to inform the opponent about a decisive position and is intended to +encourage them to resign a lost game so that both players can make better use +of their time. + ## Message Code Table The following table shows the definitions of the network codes. -| Code Name | Byte | With Payload | -|----------------------|------|--------------| -| NETCODE_ACCEPT | 0x02 | no | -| NETCODE_DECLINE | 0x04 | no | -| NETCODE_DECLINE_MOVE | 0x05 | yes | -| NETCODE_GAMEINFO | 0x10 | yes | -| NETCODE_PGNDATA | 0x11 | yes | -| NETCODE_MOVE | 0x20 | yes | -| NETCODE_CHECKMATE | 0x23 | no | -| NETCODE_STALEMATE | 0x28 | no | -| NETCODE_NOMATERIAL | 0x29 | no | -| NETCODE_THREEFOLD | 0x30 | no | -| NETCODE_RESIGN | 0x41 | no | -| NETCODE_REMIS | 0x42 | no | -| NETCODE_TAUNT | 0x43 | no | -| NETCODE_TIMEOVER | 0x44 | no | +| Code Name | Byte | With Payload | +|--------------|------|--------------| +| ACCEPT | 0x01 | no | +| DECLINE | 0x02 | no | +| GAMEINFO | 0x10 | yes | +| PGNDATA | 0x11 | yes | +| MOVE | 0x20 | yes | +| ACCEPT_MOVE | 0x21 | no | +| DECLINE_MOVE | 0x22 | yes | +| CHECKMATE | 0x30 | no | +| STALEMATE | 0x31 | no | +| NOMATERIAL | 0x32 | no | +| THREEFOLD | 0x33 | no | +| 50MOVES | 0x34 | no | +| RESIGN | 0x41 | no | +| REMIS | 0x42 | no | +| TAUNT | 0x43 | no | +| TIMEOVER | 0x44 | no | The following codes are reserved for implementation. They are not used during transmissions and can be used as return values for functions to indicate errors. -| Code Name | Byte | -|----------------------|------| -| NETCODE_AGAIN | 0x70 | -| NETCODE_CONNLOST | 0x80 | -| NETCODE_ERROR | 0xFF | +| Code Name | Byte | +|-----------|------| +| AGAIN | 0x70 | +| CONNLOST | 0x80 | +| ERROR | 0xFF |
--- a/src/main.c Mon Aug 24 14:43:33 2026 +0200 +++ b/src/main.c Mon Aug 24 15:48:19 2026 +0200 @@ -793,14 +793,14 @@ } break; /* validate "the game has ended" claims */ + case NETCODE_CHECKMATE: + return 1; /* no need to accept, they say they're toast, OK for us */ case NETCODE_THREEFOLD: case NETCODE_NOMATERIAL: case NETCODE_STALEMATE: - case NETCODE_CHECKMATE: if ((code == NETCODE_THREEFOLD && gamestate->threefold) || (code == NETCODE_NOMATERIAL && gamestate->nomaterial) || - (code == NETCODE_STALEMATE && gamestate->stalemate) || - (code == NETCODE_CHECKMATE && gamestate->checkmate)) { + (code == NETCODE_STALEMATE && gamestate->stalemate)) { /* auto-accept the claim */ net_send_code(opponent, NETCODE_ACCEPT); return 1; @@ -823,6 +823,8 @@ bool endgame = true; if (gamestate->checkmate) { net_send_code(opponent, NETCODE_CHECKMATE); + /* checkmate does not need to be accepted */ + return 1; } else if (gamestate->stalemate) { net_send_code(opponent, NETCODE_STALEMATE); } else if (gamestate->nomaterial) {
--- a/src/network.h Mon Aug 24 14:43:33 2026 +0200 +++ b/src/network.h Mon Aug 24 15:48:19 2026 +0200 @@ -48,6 +48,7 @@ #define NETCODE_STALEMATE 0x31 #define NETCODE_NOMATERIAL 0x32 #define NETCODE_THREEFOLD 0x33 +#define NETCODE_50MOVES 0x34 #define NETCODE_RESIGN 0x41 #define NETCODE_REMIS 0x42 #define NETCODE_TAUNT 0x43