change how a game is ended in multiplayer

Mon, 24 Aug 2026 14:31:37 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 24 Aug 2026 14:31:37 +0200
changeset 188
bb967c8e51e9
parent 187
2834e444a956
child 189
444dfdf790b8

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

mercurial