From ec5b8fcf04aa2cbec3ea9ce92af5120b3f5d045b Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 27 Mar 2026 23:36:55 -0400 Subject: [PATCH] XD_REQMAPQUEUE --> PT_REQMAPQUEUE It was technically possible for custom clients to spoil future rounds of a tournament queued while they are connected to a server. Making it a PT direct packet to the servernode both solves this problem AND reduces irrelevant NetXCmd traffic for clients. --- src/d_clisrv.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++- src/d_clisrv.h | 10 ++++++ src/d_netcmd.c | 82 ++++++------------------------------------------ src/d_netcmd.h | 3 +- src/hu_stuff.c | 1 - src/typedef.h | 1 + 6 files changed, 104 insertions(+), 77 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 3b9125856..e7bb65414 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5415,6 +5415,87 @@ static void HandlePlayerInfo(SINT8 node) receivedplayerinfo = true; } +static void PT_ReqMapQueue(int node) +{ + if (client) + return; // Only sent to servers, why are we receiving this? + + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); + reqmapqueue_pak reqmapqueue = netbuffer->u.reqmapqueue; + + // Check for a spoofed source. + if (reqmapqueue.source == serverplayer) + { + // Servers aren't guaranteed to have a playernode, dedis exist. + if (node != servernode) + return; + } + else + { + if (playernode[reqmapqueue.source] != node) + return; + + if (!IsPlayerAdmin(reqmapqueue.source)) + { + CONS_Debug(DBG_NETPLAY,"Received illegal request map queue cmd from Player %d (%s).\n", reqmapqueue.source+1, player_names[reqmapqueue.source]); + SendKick(reqmapqueue.source, KICK_MSG_CON_FAIL); + return; + } + } + + const boolean doclear = (reqmapqueue.newgametype == ROUNDQUEUE_CLEAR); + + // The following prints will only appear when multiple clients + // attempt to affect the round queue at similar time increments + if (doclear == true) + { + if (roundqueue.size == 0) + { + // therefore this one doesn't really need a error print + // because what both players wanted was done anyways + //CONS_Alert(CONS_ERROR, "queuemap: Queue is already empty!\n"); + return; + } + } + else if (roundqueue.size >= ROUNDQUEUE_MAX) + { + CONS_Alert(CONS_ERROR, "Recieved REQMAPQUEUE, but unable to add map beyond %u\n", roundqueue.size); + + // But this one does, because otherwise it's silent failure! + // Todo print the map's name, maybe? + char rejectmsg[256]; + strlcpy(rejectmsg, "The server couldn't queue your chosen map.", 256); + SendServerNotice(reqmapqueue.source, rejectmsg); + + return; + } + + UINT8 buf[1+2+1]; + UINT8 *buf_p = buf; + + WRITEUINT8(buf_p, reqmapqueue.flags); + WRITEUINT16(buf_p, reqmapqueue.newgametype); + + WRITEUINT8(buf_p, roundqueue.size); + + // Match Got_MapQueuecmd, but with the addition of reqmapqueue.newmapnum available to us + if (doclear == true) + { + memset(&roundqueue, 0, sizeof(struct roundqueue)); + } + else + { + G_MapIntoRoundQueue( + reqmapqueue.newmapnum, + reqmapqueue.newgametype, + ((reqmapqueue.flags & 1) != 0), + false + ); + } + + SendNetXCmd(XD_MAPQUEUE, buf, buf_p - buf); +} + /** Handles a packet received from a node that isn't in game * * \param node The packet sender @@ -5494,6 +5575,7 @@ static void HandlePacketFromPlayer(SINT8 node) case PT_TEXTCMD3 : PT_TextCmd (node, netconsole); break; case PT_TEXTCMD4 : PT_TextCmd (node, netconsole); break; case PT_SAY : PT_Say (node ); break; + case PT_REQMAPQUEUE : PT_ReqMapQueue (node ); break; case PT_LOGIN : PT_Login (node ); break; case PT_LOGINAUTH : PT_LoginAuth (node, netconsole); break; case PT_NODETIMEOUT : PT_ClientQuit (node, netconsole); break; @@ -6927,10 +7009,10 @@ void DoSayPacketFromCommand(SINT8 target, size_t usedargs, UINT8 flags) DoSayPacket(target, flags, consoleplayer, msg); } +// This is meant to be targeted at player indices, not whatever the hell XD_SAY is doing with 1-indexed players. void SendServerNotice(SINT8 target, char *message) { if (client) return; DoSayCommand(message, target + 1, HU_PRIVNOTICE, servernode); } - diff --git a/src/d_clisrv.h b/src/d_clisrv.h index c3be9de11..049704d7d 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -136,6 +136,7 @@ typedef enum PT_SERVERINFOUPDATE, // Update server info for clients. PT_SAY, // "Hey server, please send this chat message to everyone via XD_SAY" + PT_REQMAPQUEUE, // Client requesting a roundqueue operation NUMPACKETTYPE } packettype_t; @@ -376,6 +377,14 @@ struct filesneededconfig_pak UINT8 files[MAXFILENEEDED]; // is filled with writexxx (byteptr.h) } ATTRPACK; +struct reqmapqueue_pak +{ + UINT16 newmapnum; + UINT16 newgametype; + UINT8 flags; + UINT8 source; +} ATTRPACK; + struct netinfo_pak { UINT32 pingtable[MAXPLAYERS+1]; @@ -427,6 +436,7 @@ struct doomdata_t filesneededconfig_pak filesneededcfg; netinfo_pak netinfo; say_pak say; + reqmapqueue_pak reqmapqueue; // Formerly XD_REQMAPQUEUE } u; // This is needed to pack diff packet types data together } ATTRPACK; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 1685ae648..6667219b7 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -106,7 +106,6 @@ static void Got_DiscordInfo(UINT8 **cp, INT32 playernum); static void Got_ScheduleTaskcmd(UINT8 **cp, INT32 playernum); static void Got_ScheduleClearcmd(UINT8 **cp, INT32 playernum); static void Got_Automatecmd(UINT8 **cp, INT32 playernum); -static void Got_RequestMapQueuecmd(UINT8 **cp, INT32 playernum); static void Got_MapQueuecmd(UINT8 **cp, INT32 playernum); static void Got_Cheat(UINT8 **cp, INT32 playernum); static void Command_ScoreboardAdd(void); @@ -954,7 +953,6 @@ const char *netxcmdnames[MAXNETXCMD - 1] = "SCHEDULECLEAR", // XD_SCHEDULECLEAR "AUTOMATE", // XD_AUTOMATE "CHEAT", // XD_CHEAT - "REQMAPQUEUE", // XD_REQMAPQUEUE "MAPQUEUE", // XD_MAPQUEUE }; @@ -1005,7 +1003,6 @@ void D_RegisterServerCommands(void) RegisterNetXCmd(XD_SCHEDULETASK, Got_ScheduleTaskcmd); RegisterNetXCmd(XD_SCHEDULECLEAR, Got_ScheduleClearcmd); RegisterNetXCmd(XD_AUTOMATE, Got_Automatecmd); - RegisterNetXCmd(XD_REQMAPQUEUE, Got_RequestMapQueuecmd); RegisterNetXCmd(XD_MAPQUEUE, Got_MapQueuecmd); RegisterNetXCmd(XD_CHEAT, Got_Cheat); @@ -4130,41 +4127,26 @@ static void Command_RestartLevel(void) static void Handle_MapQueueSend(UINT16 newmapnum, UINT16 newgametype, boolean newencoremode) { - static char buf[1+2+2]; - static char *buf_p = buf; - + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); UINT8 flags = 0; - boolean doclear = (newgametype == ROUNDQUEUE_CLEAR); CONS_Debug(DBG_GAMELOGIC, "Map queue: mapnum=%d newgametype=%d newencoremode=%d\n", newmapnum, newgametype, newencoremode); - buf_p = buf; - if (newencoremode) flags |= 1; - WRITEUINT8(buf_p, flags); - WRITEUINT16(buf_p, newgametype); + netbuffer->packettype = PT_REQMAPQUEUE; + reqmapqueue_pak *reqmapqueue = &netbuffer->u.reqmapqueue; - if (client) - { - WRITEUINT16(buf_p, newmapnum); - SendNetXCmd(XD_REQMAPQUEUE, buf, buf_p - buf); - return; - } + reqmapqueue->newmapnum = newmapnum; + reqmapqueue->flags = flags; + reqmapqueue->newgametype = newgametype; + reqmapqueue->source = consoleplayer; - WRITEUINT8(buf_p, roundqueue.size); - if (doclear == true) - { - memset(&roundqueue, 0, sizeof(struct roundqueue)); - } - else - { - G_MapIntoRoundQueue(newmapnum, newgametype, newencoremode, false); - } + HSendPacket(servernode, false, 0, sizeof(reqmapqueue_pak)); - SendNetXCmd(XD_MAPQUEUE, buf, buf_p - buf); + // See PT_ReqMapQueue and Got_MapQueuecmd for the next stages } static void Command_QueueMap_f(void) @@ -4321,52 +4303,6 @@ static void Command_QueueMap_f(void) Z_Free(mapname); } -static void Got_RequestMapQueuecmd(UINT8 **cp, INT32 playernum) -{ - UINT8 flags; - boolean setencore; - UINT16 mapnumber, setgametype; - boolean doclear = false; - - flags = READUINT8(*cp); - - setencore = ((flags & 1) != 0); - - setgametype = READUINT16(*cp); - - mapnumber = READUINT16(*cp); - - if (!IsPlayerAdmin(playernum)) - { - CONS_Alert(CONS_WARNING, M_GetText("Illegal request map queue command received from %s\n"), player_names[playernum]); - if (server && playernum != serverplayer) - SendKick(playernum, KICK_MSG_CON_FAIL); - return; - } - - if (roundqueue.size >= ROUNDQUEUE_MAX) - doclear = (setgametype == ROUNDQUEUE_CLEAR); - - if (doclear == true) - { - if (roundqueue.size == 0) - { - CONS_Alert(CONS_ERROR, "queuemap: Queue is already empty!\n"); - return; - } - } - else if (roundqueue.size >= ROUNDQUEUE_MAX) - { - CONS_Alert(CONS_ERROR, "queuemap: Unable to add map beyond %u\n", roundqueue.size); - return; - } - - if (client) - return; - - Handle_MapQueueSend(mapnumber, setgametype, setencore); -} - static void Got_MapQueuecmd(UINT8 **cp, INT32 playernum) { UINT8 flags, queueposition; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index fe8c5c486..b1905cfb7 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -370,8 +370,7 @@ typedef enum XD_SCHEDULECLEAR, // 36 XD_AUTOMATE, // 37 XD_CHEAT, // 38 - XD_REQMAPQUEUE, // 39 - XD_MAPQUEUE, // 40 + XD_MAPQUEUE, // 39 MAXNETXCMD } netxcmd_t; diff --git a/src/hu_stuff.c b/src/hu_stuff.c index c8672ba31..1efb2f9e1 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -814,7 +814,6 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) Z_Free(tempchar); return; // I pretend I do not see it } - } else // To everyone or sayteam, it doesn't change anything. fmt2 = "%s<%s%s> %s%s"; diff --git a/src/typedef.h b/src/typedef.h index 16d587a01..67470adaf 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -72,6 +72,7 @@ TYPEDEF (serverelem_t); TYPEDEF (rewind_t); TYPEDEF (netinfo_pak); TYPEDEF (say_pak); +TYPEDEF (reqmapqueue_pak); // d_event.h TYPEDEF (event_t);