Mon, 24 Aug 2026 14:31:37 +0200
change how a game is ended in multiplayer
relates to #976
| 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:30:47 2026 +0200 +++ b/PROTOCOL.md Mon Aug 24 14:31:37 2026 +0200 @@ -143,7 +143,6 @@ | NETCODE_GAMEINFO | 0x10 | yes | | NETCODE_PGNDATA | 0x11 | yes | | NETCODE_MOVE | 0x20 | yes | -| NETCODE_CHECK | 0x22 | no | | NETCODE_CHECKMATE | 0x23 | no | | NETCODE_STALEMATE | 0x28 | no | | NETCODE_NOMATERIAL | 0x29 | no |
--- a/src/main.c Mon Aug 24 14:30:47 2026 +0200 +++ b/src/main.c Mon Aug 24 14:31:37 2026 +0200 @@ -667,26 +667,18 @@ NetMove nmove = hton_move(move); net_send_data(opponent, NETCODE_MOVE, &nmove, NETMOVE_LEN); code = net_recieve_code(opponent); - /* we could validate the move's check/checkmate flag with - * the network response code, but we choose not to do it */ - gamestate->checkmate = code == NETCODE_CHECKMATE; - gamestate->stalemate = code == NETCODE_STALEMATE; if (code == NETCODE_DECLINE_MOVE) { uint8_t reason; net_recieve_data(opponent, &reason, 1); eval_move_failed_msg(reason); - } else if (code == NETCODE_ACCEPT - || code == NETCODE_CHECK - || code == NETCODE_CHECKMATE - || code == NETCODE_STALEMATE - || code == NETCODE_NOMATERIAL) { + } else if (code == NETCODE_ACCEPT) { format_move(gamestate, &move); apply_move(gamestate, &move); - if (is_game_running(gamestate)) { - return 0; - } else { - return 1; - } + /* switch to waiting, even when the game ended. + * we want to receive the "game end" code from the + * other party. + */ + return 0; } else if (code == NETCODE_CONNLOST) { printw("Your opponent left the game."); return 1; @@ -801,11 +793,16 @@ } } break; + /* validate "the game has ended" claims */ case NETCODE_THREEFOLD: - /* validate the claim */ - if (check_threefold_repetition(gamestate)) { + 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)) { /* auto-accept the claim */ - gamestate->threefold = true; net_send_code(opponent, NETCODE_ACCEPT); return 1; } else { @@ -821,43 +818,42 @@ if (code == VALID_MOVE_SEMANTICS) { format_move(gamestate, &move); apply_move(gamestate, &move); + net_send_code(opponent, NETCODE_ACCEPT); + + /* send a code for ending the game, if required */ + bool endgame = true; if (gamestate->checkmate) { net_send_code(opponent, NETCODE_CHECKMATE); - return 1; } else if (gamestate->stalemate) { net_send_code(opponent, NETCODE_STALEMATE); - return 1; } else if (gamestate->nomaterial) { net_send_code(opponent, NETCODE_NOMATERIAL); - return 1; - } else if (move.check) { - net_send_code(opponent, NETCODE_CHECK); + } else if (gamestate->threefold) { + /* Note: in our implementation it is an auto-draw claimed + * by the player confronted with the position. By standard + * chess rules, also the player who creates the position may + * claim the draw. But since we do it automatically, it + * makes no practical difference. + */ + net_send_code(opponent, NETCODE_THREEFOLD); } else { - net_send_code(opponent, NETCODE_ACCEPT); + endgame = false; } - /* check for threefold repetition */ - /* Note: in our implementation it is an auto-draw claimed - * by the player confronted with the position. By standard - * chess rules, also the player who creates the position may - * claim the draw. But since we do it automatically, it makes - * no practical difference. - */ - if (gamestate->threefold) { - /* send this as a new package (basically as next move) */ - net_send_code(opponent, NETCODE_THREEFOLD); - /* the protocol supports declining the claim */ + /* await a response to the "end game" code */ + if (endgame) { uint8_t resp = net_recieve_code(opponent); if (resp == NETCODE_ACCEPT) { return 1; } else { /* does not happen in our implementation */ - // TODO: but what do we do if some other client declines? simply rage quit? - // should we instead rework the protocol that it's not possible to decline? + // TODO: but what do we do if some other client declines? + // currently, we simply continue playing, + // but that does not make any sense return 0; } - } else { - return 0; } + /* now it's our turn */ + return 0; } else { net_send_data(opponent, NETCODE_DECLINE_MOVE, &code, 1); }
--- a/src/network.h Mon Aug 24 14:30:47 2026 +0200 +++ b/src/network.h Mon Aug 24 14:31:37 2026 +0200 @@ -43,7 +43,6 @@ #define NETCODE_GAMEINFO 0x10 #define NETCODE_PGNDATA 0x11 #define NETCODE_MOVE 0x20 -#define NETCODE_CHECK 0x22 #define NETCODE_CHECKMATE 0x23 #define NETCODE_STALEMATE 0x28 #define NETCODE_NOMATERIAL 0x29