From 5b9f6b65c407ecd789ceac73a2e5375300b1003e Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 17 Nov 2025 02:36:05 -0500 Subject: [PATCH 1/7] Split big switch statment for PT switches into seperate funcs --- src/d_clisrv.c | 1495 ++++++++++++++++++++++++------------------------ src/d_netfil.c | 47 +- src/d_netfil.h | 8 +- 3 files changed, 791 insertions(+), 759 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index ea3cd0eb1..bf4857943 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4364,87 +4364,6 @@ static void HandleShutdown(SINT8 node) M_StartMessage(M_GetText("Server has shutdown\n\nPress Esc\n"), NULL, MM_NOTHING); } -static void PT_Login(SINT8 node) -{ - doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); - - if (client) - return; - - I_Assert((UINT8)node < sizeof(adminsalt) / sizeof(adminsalt[0])); - - // I_GetRandomBytes should get it's data from a CSPRNG, so it's safe to use it here. - I_GetRandomBytes(adminsalt[node], sizeof(adminsalt[node])); - adminsalt[node][8] = '\0'; // convenience - netbuffer->packettype = PT_LOGINCHALLENGE; - memcpy(netbuffer->u.salt, adminsalt[node], sizeof(adminsalt[node])); - HSendPacket(node, true, 0, sizeof(adminsalt[node])); -} - -static void PT_LoginChallenge(SINT8 node) -{ - char salt[9]; - doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); - - if (node != servernode) - return; - - if ((size_t)doomcom->datalength < sizeof(netbuffer->u.salt))/* ignore partial sends */ - return; - - if (reqpass == NULL) - return; // got PT_LOGINCHALLENGE but we didn't request a login - - memcpy(salt, netbuffer->u.salt, sizeof(netbuffer->u.salt)); - D_SHA256PasswordPass((const UINT8 *)reqpass, strlen(reqpass), salt, netbuffer->u.sha256sum); - Z_Free(reqpass); - reqpass = NULL; - - netbuffer->packettype = PT_LOGINAUTH; - HSendPacket(servernode, true, 0, sizeof(netbuffer->u.sha256sum)); -} - -static void PT_LoginAuth(SINT8 node, INT32 netconsole) -{ - doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); - UINT8 finalsha256[32];/* Well, it's the cool thing to do? */ - UINT32 i; - if (client) - return; - - if ((size_t)doomcom->datalength < sizeof(netbuffer->u.sha256sum))/* ignore partial sends */ - return; - - if (adminsalt[node][0] == 0) - { - CONS_Printf(M_GetText("Password from %s failed (no login request).\n"), player_names[netconsole]); - return; - } - if (adminpasscount == 0) - { - adminsalt[node][0] = 0; - CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[netconsole]); - return; - } - - for (i = 0; i < adminpasscount; i++) - { - // Do the final pass to compare with the sent md5 - D_SHA256PasswordPass((const UINT8 *)adminpass[i], strlen(adminpass[i]), adminsalt[node], finalsha256); - - if (!memcmp(netbuffer->u.sha256sum, finalsha256, 32)) - { - adminsalt[node][0] = 0; - CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]); - COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately - return; - } - } - - adminsalt[node][0] = 0; - CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]); -} - void ServerInfoUpdateSend(void) { UINT32 n; @@ -4521,19 +4440,673 @@ static void HandleServerInfo(SINT8 node) SL_InsertServer(&netbuffer->u.serverinfo, node); } -static void HandlePlayerInfo(SINT8 node) +// Helper function for packets that should only be sent by the server +// If it is NOT from the server, bail out and close the connection! +static boolean ServerOnly(SINT8 node) { - (void)node; - doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); - for (INT32 i = 0; i < MAXPLAYERINFO; i++) - { - playerinfo[i] = netbuffer->u.playerinfo[i]; - } - receivedplayerinfo = true; + if (node == servernode) + return false; + + Net_CloseConnection(node); + return true; } -static void PT_WillResendGamestate(void) +static void PT_AskInfoViaMS(SINT8 node) { + Net_CloseConnection(node); +} + +static void PT_TellFilesNeeded(SINT8 node) +{ + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); + + if (server && serverrunning) + { + UINT8 *p; + INT32 firstfile = netbuffer->u.filesneedednum; + + netbuffer->packettype = PT_MOREFILESNEEDED; + netbuffer->u.filesneededcfg.first = firstfile; + netbuffer->u.filesneededcfg.more = 0; + + p = PutFileNeeded(firstfile); + + HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); + } + else // Shouldn't get this if you aren't the server...? + Net_CloseConnection(node); +} + +static void PT_MoreFilesNeeded(SINT8 node) +{ + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); + if (server && serverrunning) + { // But wait I thought I'm the server? + Net_CloseConnection(node); + return; + } + if (ServerOnly(node)) + return; + if (cl_mode == CL_ASKFULLFILELIST && netbuffer->u.filesneededcfg.first == fileneedednum) + { + D_ParseFileneeded(netbuffer->u.filesneededcfg.num, netbuffer->u.filesneededcfg.files, netbuffer->u.filesneededcfg.first); + if (!netbuffer->u.filesneededcfg.more) + cl_lastcheckedfilecount = UINT16_MAX; // Got the whole file list + } +} + +static void PT_AskInfo(SINT8 node) +{ + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); + + if (server && serverrunning) + { + SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time)); + SV_SendPlayerInfo(node); // Send extra info + } + Net_CloseConnection(node); +} + +// Negative response of client join request +static void PT_ServerRefuse(SINT8 node) +{ + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); + if (server && serverrunning) + { // But wait I thought I'm the server? + Net_CloseConnection(node); + return; + } + if (ServerOnly(node)) + return; + if (cl_mode == CL_WAITJOINRESPONSE) + { + // Save the reason so it can be displayed after quitting the netgame + char *reason = strdup(netbuffer->u.serverrefuse.reason); + if (!reason) + I_Error("Out of memory!\n"); + + if (strstr(reason, "Maximum players reached")) + { + serverisfull = true; + //Special timeout for when refusing due to player cap. The client will wait 3 seconds between join requests when waiting for a slot, so we need this to be much longer + //We set it back to the value of cv_nettimeout.value in CL_Reset + connectiontimeout = NEWTICRATE*7; + cl_mode = CL_ASKJOIN; + free(reason); + return; + } + + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + + if (reason[1] == '|') + { + M_StartMessage(va("You have been %sfrom the server\n\nReason:\n%s", + (reason[0] == 'B') ? "banned\n" : "temporarily\nkicked ", + reason+2), NULL, MM_NOTHING); + } + else + { + M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"), + reason), NULL, MM_NOTHING); + } + + free(reason); + + // Will be reset by caller. Signals refusal. + cl_mode = CL_ABORTED; + } +} + +// Positive response of client join request +static void PT_ServerCFG(SINT8 node) +{ + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); + if (server && serverrunning && node != servernode) + { // but wait I thought I'm the server? + Net_CloseConnection(node); + return; + } + if (ServerOnly(node)) + return; + /// \note how would this happen? and is it doing the right thing if it does? + if (!(cl_mode == CL_WAITJOINRESPONSE || cl_mode == CL_ASKJOIN)) + return; + + if (client) + { + maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic); + + G_SetGametype(netbuffer->u.servercfg.gametype); + + modifiedgame = netbuffer->u.servercfg.modifiedgame; + connectedtodedicated = netbuffer->u.servercfg.dedicated; + + memcpy(server_context, netbuffer->u.servercfg.server_context, 8); + + CopyCaretColors(connectedservername, netbuffer->u.servercfg.server_name, MAXSERVERNAME); + CopyCaretColors(connectedservercontact, netbuffer->u.servercfg.server_contact, MAXSERVERCONTACT); + strncpy(connectedserverdescription, netbuffer->u.servercfg.server_description, MAXSERVERDESCRIPTION); + } + +#ifdef HAVE_DISCORDRPC + discordInfo.maxPlayers = netbuffer->u.servercfg.maxplayer; + discordInfo.joinsAllowed = netbuffer->u.servercfg.allownewplayer; + discordInfo.everyoneCanInvite = netbuffer->u.servercfg.discordinvites; +#endif + + nodeingame[(UINT8)servernode] = true; + serverplayer = netbuffer->u.servercfg.serverplayer; + doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum); + mynode = netbuffer->u.servercfg.clientnode; + if (serverplayer >= 0) + playernode[(UINT8)serverplayer] = servernode; + + if (netgame) + CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n")); + DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode)); + + /// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook? + /// Shouldn't them be downloaded even at intermission time? + /// Also, according to HandleConnect, the server will send the savegame even during intermission... + /// Sryder 2018-07-05: If we don't want to send the player config another way we need to send the gamestate + /// At almost any gamestate there could be joiners... So just always send gamestate? + cl_mode = ((server) ? CL_CONNECTED : CL_DOWNLOADSAVEGAME); +} + +/** Checks ticcmd for "speed hacks" + * + * \param p Which player + * \return True if player is hacking + * \sa HandlePacketFromPlayer + * + */ +static boolean CheckForSpeedHacks(UINT8 p) +{ + if (netcmds[maketic%BACKUPTICS][p].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][p].forwardmove < -MAXPLMOVE + || netcmds[maketic%BACKUPTICS][p].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][p].sidemove < -MAXPLMOVE + || netcmds[maketic%BACKUPTICS][p].turning > KART_FULLTURN || netcmds[maketic%BACKUPTICS][p].turning < -KART_FULLTURN + || netcmds[maketic%BACKUPTICS][p].throwdir > KART_FULLTURN || netcmds[maketic%BACKUPTICS][p].throwdir < -KART_FULLTURN) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), playernode[p]); + //D_Clearticcmd(k); + + SendKick(p, KICK_MSG_CON_FAIL); + return true; + } + + return false; +} + +static void PT_ClientCmd(SINT8 node, INT32 netconsole) +{ + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); + tic_t realend, realstart; + + if (client) + return; + + // To save bytes, only the low byte of tic numbers are sent + // Use ExpandTics to figure out what the rest of the bytes are + + realstart = ExpandTics(netbuffer->u.clientpak.client_tic, nettics[node]); + realend = ExpandTics(netbuffer->u.clientpak.resendfrom, nettics[node]); + + if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS + || netbuffer->packettype == PT_CLIENT3MIS || netbuffer->packettype == PT_CLIENT4MIS + || netbuffer->packettype == PT_NODEKEEPALIVEMIS + || supposedtics[node] < realend) + { + supposedtics[node] = realend; + } + // Discard out of order packet + if (nettics[node] > realend) + { + DEBFILE(va("out of order ticcmd discarded nettics = %u\n", nettics[node])); + return; + } + + // Update the nettics + nettics[node] = realend; + + // This should probably still timeout though, as the node should always have a player 1 number + if (netconsole == -1) + return; + + // As long as clients send valid ticcmds, the server can keep running, so reset the timeout + /// \todo Use a separate cvar for that kind of timeout? + freezetimeout[node] = I_GetTime() + connectiontimeout; + + // Don't do anything for packets of type NODEKEEPALIVE? + // Sryder 2018/07/01: Update the freezetimeout still! + if (netbuffer->packettype == PT_NODEKEEPALIVE + || netbuffer->packettype == PT_NODEKEEPALIVEMIS) + return; + + // store it in an internal buffer so the last packet takes precedence, which minimizes input lag + G_MoveTiccmd(&playercmds[netconsole], &netbuffer->u.clientpak.cmd, 1); + + // Check ticcmd for "speed hacks" + if (CheckForSpeedHacks((UINT8)netconsole)) + return; + + // Splitscreen cmd + if (((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS) + || (netbuffer->packettype == PT_CLIENT3CMD || netbuffer->packettype == PT_CLIENT3MIS) + || (netbuffer->packettype == PT_CLIENT4CMD || netbuffer->packettype == PT_CLIENT4MIS)) + && (nodetoplayer2[node] >= 0)) + { + G_MoveTiccmd(&playercmds[(UINT8)nodetoplayer2[node]], &netbuffer->u.client2pak.cmd2, 1); + + if (CheckForSpeedHacks((UINT8)nodetoplayer2[node])) + return; + } + + if (((netbuffer->packettype == PT_CLIENT3CMD || netbuffer->packettype == PT_CLIENT3MIS) + || (netbuffer->packettype == PT_CLIENT4CMD || netbuffer->packettype == PT_CLIENT4MIS)) + && (nodetoplayer3[node] >= 0)) + { + G_MoveTiccmd(&playercmds[(UINT8)nodetoplayer3[node]], &netbuffer->u.client3pak.cmd3, 1); + + if (CheckForSpeedHacks((UINT8)nodetoplayer3[node])) + return; + } + + if ((netbuffer->packettype == PT_CLIENT4CMD || netbuffer->packettype == PT_CLIENT4MIS) + && (nodetoplayer4[node] >= 0)) + { + G_MoveTiccmd(&playercmds[(UINT8)nodetoplayer4[node]], &netbuffer->u.client4pak.cmd4, 1); + + if (CheckForSpeedHacks((UINT8)nodetoplayer4[node])) + return; + } + + // Check player consistancy during the level + if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL + && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy) + && !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime() + && !SV_ResendingSavegameToAnyone()) + { + if (cv_resynchattempts.value) + { + // Tell the client we are about to resend them the gamestate + netbuffer->packettype = PT_WILLRESENDGAMESTATE; + HSendPacket(node, true, 0, 0); + + resendingsavegame[node] = true; + + if (cv_blamecfail.value) + CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"), + netconsole+1, player_names[netconsole], + consistancy[realstart%BACKUPTICS], + SHORT(netbuffer->u.clientpak.consistancy)); + DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n", + netconsole, realstart, consistancy[realstart%BACKUPTICS], + SHORT(netbuffer->u.clientpak.consistancy))); + return; + } + else + { + SendKick(netconsole, KICK_MSG_CON_FAIL); + DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n", + netconsole, realstart, consistancy[realstart%BACKUPTICS], + SHORT(netbuffer->u.clientpak.consistancy))); + return; + } + } +} + +static void PT_BasicKeepAlive(SINT8 node, INT32 netconsole) +{ + if (client) + return; + + // This should probably still timeout though, as the node should always have a player 1 number + if (netconsole == -1) + return; + + // If a client sends this it should mean they are done receiving the savegame + sendingsavegame[node] = false; + + // As long as clients send keep alives, the server can keep running, so reset the timeout + /// \todo Use a separate cvar for that kind of timeout? + freezetimeout[node] = I_GetTime() + connectiontimeout; +} + +static void PT_TextCmd(SINT8 node, INT32 netconsole) +{ + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); + + if (client) + return; + + if (netbuffer->packettype == PT_TEXTCMD2) // splitscreen special + netconsole = nodetoplayer2[node]; + else if (netbuffer->packettype == PT_TEXTCMD3) + netconsole = nodetoplayer3[node]; + else if (netbuffer->packettype == PT_TEXTCMD4) + netconsole = nodetoplayer4[node]; + + if (netconsole < 0 || netconsole >= MAXPLAYERS) + Net_UnAcknowledgePacket(node); + else + { + size_t j; + tic_t tic = maketic; + UINT8 *textcmd; + + UINT16 incoming_size; + + { + UINT8 *incoming = netbuffer->u.textcmd; + + incoming_size = READUINT16(incoming); + } + + // ignore if the textcmd has a reported size of zero + // this shouldn't be sent at all + if (!incoming_size) + { + DEBFILE(va("GetPacket: Textcmd with size 0 detected! (node %u, player %d)\n", + node, netconsole)); + Net_UnAcknowledgePacket(node); + return; + } + + // ignore if the textcmd size var is actually larger than it should be + // BASEPACKETSIZE + 2 (for size) + textcmd[0] should == datalength + if (incoming_size > (size_t)doomcom->datalength-BASEPACKETSIZE-2) + { + DEBFILE(va("GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d)\n", + incoming_size, sizeu1((size_t)doomcom->datalength-BASEPACKETSIZE-2), + node, netconsole)); + Net_UnAcknowledgePacket(node); + return; + } + + // check if tic that we are making isn't too large else we cannot send it :( + // doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time + j = software_MAXPACKETLENGTH + - (incoming_size + 3 + BASESERVERTICSSIZE + + (doomcom->numslots+1)*sizeof(ticcmd_t)); + + // search a tic that have enougth space in the ticcmd + while ((textcmd = D_GetExistingTextcmd(tic, netconsole)), + (TotalTextCmdPerTic(tic) > j || incoming_size + (textcmd ? ((UINT16*)textcmd)[0] : 0) > MAXTEXTCMD) + && tic < firstticstosend + BACKUPTICS) + tic++; + + if (tic >= firstticstosend + BACKUPTICS) + { + DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, " + "tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)), + maketic, firstticstosend, node, netconsole)); + Net_UnAcknowledgePacket(node); + return; + } + + // Make sure we have a buffer + if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole); + + DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n", + tic, ((UINT16*)textcmd)[0]+2, netconsole, firstticstosend, maketic)); + + memcpy(&textcmd[((UINT16*)textcmd)[0]+2], netbuffer->u.textcmd+2, incoming_size); + ((UINT16*)textcmd)[0] += incoming_size; + } +} + +static void PT_Login(SINT8 node) +{ + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); + + if (client) + return; + + I_Assert((UINT8)node < sizeof(adminsalt) / sizeof(adminsalt[0])); + + // I_GetRandomBytes should get it's data from a CSPRNG, so it's safe to use it here. + I_GetRandomBytes(adminsalt[node], sizeof(adminsalt[node])); + adminsalt[node][8] = '\0'; // convenience + netbuffer->packettype = PT_LOGINCHALLENGE; + memcpy(netbuffer->u.salt, adminsalt[node], sizeof(adminsalt[node])); + HSendPacket(node, true, 0, sizeof(adminsalt[node])); +} + +static void PT_LoginAuth(SINT8 node, INT32 netconsole) +{ + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); + UINT8 finalsha256[32];/* Well, it's the cool thing to do? */ + UINT32 i; + if (client) + return; + + if ((size_t)doomcom->datalength < sizeof(netbuffer->u.sha256sum))/* ignore partial sends */ + return; + + if (adminsalt[node][0] == 0) + { + CONS_Printf(M_GetText("Password from %s failed (no login request).\n"), player_names[netconsole]); + return; + } + if (adminpasscount == 0) + { + adminsalt[node][0] = 0; + CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[netconsole]); + return; + } + + for (i = 0; i < adminpasscount; i++) + { + // Do the final pass to compare with the sent md5 + D_SHA256PasswordPass((const UINT8 *)adminpass[i], strlen(adminpass[i]), adminsalt[node], finalsha256); + + if (!memcmp(netbuffer->u.sha256sum, finalsha256, 32)) + { + adminsalt[node][0] = 0; + CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]); + COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately + return; + } + } + + adminsalt[node][0] = 0; + CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]); +} + +static void PT_ClientQuit(SINT8 node, INT32 netconsole) +{ + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); + + if (client) + return; + + // nodeingame will be put false in the execution of kick command + // this allow to send some packets to the quitting client to have their ack back + nodewaiting[node] = 0; + if (netconsole != -1 && playeringame[netconsole]) + { + UINT8 kickmsg; + + if (netbuffer->packettype == PT_NODETIMEOUT) + kickmsg = KICK_MSG_TIMEOUT; + else + kickmsg = KICK_MSG_PLAYER_QUIT; + + SendKick(netconsole, kickmsg); + + /* + n odetoplayer[node] = -1;* + + if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0 + && playeringame[(UINT8)nodetoplayer2[node]]) + { + SendKick(nodetoplayer2[node], kickmsg); + nodetoplayer2[node] = -1; + } + + if (nodetoplayer3[node] != -1 && nodetoplayer3[node] >= 0 + && playeringame[(UINT8)nodetoplayer3[node]]) + { + SendKick(nodetoplayer3[node], kickmsg); + nodetoplayer3[node] = -1; + } + + if (nodetoplayer4[node] != -1 && nodetoplayer4[node] >= 0 + && playeringame[(UINT8)nodetoplayer4[node]]) + { + SendKick(nodetoplayer4[node], kickmsg); + nodetoplayer4[node] = -1; + } + */ + } + Net_CloseConnection(node); + nodeingame[node] = false; +} + +static void PT_CanReceiveGamestate(SINT8 node) +{ + if (client || sendingsavegame[node]) + return; + + CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[nodetoplayer[node]]); + + SV_SendSaveGame(node, true); // Resend a complete game state + resendingsavegame[node] = true; +} + +static void PT_AskLuaFile(SINT8 node) +{ + if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED) + AddLuaFileToSendQueue(node, luafiletransfers->realfilename); +} + +static void PT_HasLuaFile(SINT8 node) +{ + if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING) + SV_HandleLuaFileSent(node); +} + +static void PT_ReceivedGamestate(SINT8 node) +{ + sendingsavegame[node] = false; + resendingsavegame[node] = false; + savegameresendcooldown[node] = I_GetTime() + 5 * TICRATE; +} + +static void PT_ServerTics(SINT8 node, INT32 netconsole) +{ + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); + + UINT8 *pak, *txtpak, numtxtpak; + tic_t realend, realstart; + + if (!nodeingame[node]) + { + // Do not remove my own server (we have just get a out of order packet) + if (node != servernode) + { + DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype)); + Net_CloseConnection(node); + } + return; + } + + // Only accept PT_SERVERTICS from the server. + if (node != servernode) + { + CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node); + if (server) + SendKick(netconsole, KICK_MSG_CON_FAIL); + return;; + } + + realstart = ExpandTics(netbuffer->u.serverpak.starttic, maketic); + realend = realstart + netbuffer->u.serverpak.numtics; + + txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots + * netbuffer->u.serverpak.numtics]; + + if (realend > gametic + CLIENTBACKUPTICS) + realend = gametic + CLIENTBACKUPTICS; + cl_packetmissed = realstart > neededtic; + + if (realstart <= neededtic && realend > neededtic) + { + tic_t i, j; + pak = (UINT8 *)&netbuffer->u.serverpak.cmds; + + for (i = realstart; i < realend; i++) + { + // clear first + D_Clearticcmd(i); + + // copy the tics + pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak, + netbuffer->u.serverpak.numslots*sizeof (ticcmd_t)); + + // copy the textcmds + numtxtpak = *txtpak++; + for (j = 0; j < numtxtpak; j++) + { + INT32 k = *txtpak++; // playernum + const size_t txtsize = ((UINT16*)txtpak)[0]+2; + + if (i >= gametic) // Don't copy old net commands + memcpy(D_GetTextcmd(i, k), txtpak, txtsize); + txtpak += txtsize; + } + } + + neededtic = realend; + } + else + { + DEBFILE(va("frame not in bound: %u\n", neededtic)); + /*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart) + * I_Error("Received an out of order PT_SERVERTICS packet!\n" + * "Got tics %d-%d, needed tic %d\n\n" + * "Please report this crash on the Master Board,\n" + * "IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/ + } +} + +static void PT_Ping(SINT8 node, INT32 netconsole) +{ + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); + + // Only accept PT_PING from the server. + if (node != servernode) + { + CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_PING", node); + if (server) + SendKick(netconsole, KICK_MSG_CON_FAIL); + return; + } + + //Update client ping table from the server. + if (client) + { + UINT8 i; + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + playerpingtable[i] = (tic_t)netbuffer->u.netinfo.pingtable[i]; + playerpacketlosstable[i] = netbuffer->u.netinfo.packetloss[i]; + playerdelaytable[i] = netbuffer->u.netinfo.delay[i]; + } + } + + servermaxping = (tic_t)netbuffer->u.netinfo.pingtable[MAXPLAYERS]; + } +} + +static void PT_WillResendGamestate(SINT8 node) +{ + (void)node; + char tmpsave[256]; doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); @@ -4559,15 +5132,46 @@ static void PT_WillResendGamestate(void) cl_redownloadinggamestate = true; } -static void PT_CanReceiveGamestate(SINT8 node) +static void PT_LoginChallenge(SINT8 node) { - if (client || sendingsavegame[node]) + char salt[9]; + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); + + if (node != servernode) return; - CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[nodetoplayer[node]]); + if ((size_t)doomcom->datalength < sizeof(netbuffer->u.salt))/* ignore partial sends */ + return; - SV_SendSaveGame(node, true); // Resend a complete game state - resendingsavegame[node] = true; + if (reqpass == NULL) + return; // got PT_LOGINCHALLENGE but we didn't request a login + + memcpy(salt, netbuffer->u.salt, sizeof(netbuffer->u.salt)); + D_SHA256PasswordPass((const UINT8 *)reqpass, strlen(reqpass), salt, netbuffer->u.sha256sum); + Z_Free(reqpass); + reqpass = NULL; + + netbuffer->packettype = PT_LOGINAUTH; + HSendPacket(servernode, true, 0, sizeof(netbuffer->u.sha256sum)); +} + +static void PT_SendingLuaFile(SINT8 node) +{ + (void)node; + + if (client) + CL_PrepareDownloadLuaFile(); +} + +static void HandlePlayerInfo(SINT8 node) +{ + (void)node; + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); + for (INT32 i = 0; i < MAXPLAYERINFO; i++) + { + playerinfo[i] = netbuffer->u.playerinfo[i]; + } + receivedplayerinfo = true; } /** Handles a packet received from a node that isn't in game @@ -4584,245 +5188,28 @@ static void HandlePacketFromAwayNode(SINT8 node) if (node != servernode) DEBFILE(va("Received packet from unknown host %d\n", node)); -// macro for packets that should only be sent by the server -// if it is NOT from the server, bail out and close the connection! -#define SERVERONLY \ - if (node != servernode) \ - { \ - Net_CloseConnection(node); \ - break; \ - } switch (netbuffer->packettype) { - case PT_ASKINFOVIAMS: - Net_CloseConnection(node); - break; - - case PT_TELLFILESNEEDED: - if (server && serverrunning) - { - UINT8 *p; - INT32 firstfile = netbuffer->u.filesneedednum; - - netbuffer->packettype = PT_MOREFILESNEEDED; - netbuffer->u.filesneededcfg.first = firstfile; - netbuffer->u.filesneededcfg.more = 0; - - p = PutFileNeeded(firstfile); - - HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); - } - else // Shouldn't get this if you aren't the server...? - Net_CloseConnection(node); - break; - - case PT_MOREFILESNEEDED: - if (server && serverrunning) - { // But wait I thought I'm the server? - Net_CloseConnection(node); - break; - } - SERVERONLY - if (cl_mode == CL_ASKFULLFILELIST && netbuffer->u.filesneededcfg.first == fileneedednum) - { - D_ParseFileneeded(netbuffer->u.filesneededcfg.num, netbuffer->u.filesneededcfg.files, netbuffer->u.filesneededcfg.first); - if (!netbuffer->u.filesneededcfg.more) - cl_lastcheckedfilecount = UINT16_MAX; // Got the whole file list - } - break; - - case PT_ASKINFO: - if (server && serverrunning) - { - SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time)); - SV_SendPlayerInfo(node); // Send extra info - } - Net_CloseConnection(node); - break; - - case PT_SERVERREFUSE: // Negative response of client join request - if (server && serverrunning) - { // But wait I thought I'm the server? - Net_CloseConnection(node); - break; - } - SERVERONLY - if (cl_mode == CL_WAITJOINRESPONSE) - { - // Save the reason so it can be displayed after quitting the netgame - char *reason = strdup(netbuffer->u.serverrefuse.reason); - if (!reason) - I_Error("Out of memory!\n"); - - if (strstr(reason, "Maximum players reached")) - { - serverisfull = true; - //Special timeout for when refusing due to player cap. The client will wait 3 seconds between join requests when waiting for a slot, so we need this to be much longer - //We set it back to the value of cv_nettimeout.value in CL_Reset - connectiontimeout = NEWTICRATE*7; - cl_mode = CL_ASKJOIN; - free(reason); - break; - } - - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - - if (reason[1] == '|') - { - M_StartMessage(va("You have been %sfrom the server\n\nReason:\n%s", - (reason[0] == 'B') ? "banned\n" : "temporarily\nkicked ", - reason+2), NULL, MM_NOTHING); - } - else - { - M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"), - reason), NULL, MM_NOTHING); - } - - free(reason); - - // Will be reset by caller. Signals refusal. - cl_mode = CL_ABORTED; - } - break; - - case PT_SERVERCFG: // Positive response of client join request - { - if (server && serverrunning && node != servernode) - { // but wait I thought I'm the server? - Net_CloseConnection(node); - break; - } - SERVERONLY - /// \note how would this happen? and is it doing the right thing if it does? - if (!(cl_mode == CL_WAITJOINRESPONSE || cl_mode == CL_ASKJOIN)) - break; - - if (client) - { - maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic); - - G_SetGametype(netbuffer->u.servercfg.gametype); - - modifiedgame = netbuffer->u.servercfg.modifiedgame; - connectedtodedicated = netbuffer->u.servercfg.dedicated; - - memcpy(server_context, netbuffer->u.servercfg.server_context, 8); - - CopyCaretColors(connectedservername, netbuffer->u.servercfg.server_name, MAXSERVERNAME); - CopyCaretColors(connectedservercontact, netbuffer->u.servercfg.server_contact, MAXSERVERCONTACT); - strncpy(connectedserverdescription, netbuffer->u.servercfg.server_description, MAXSERVERDESCRIPTION); - } - -#ifdef HAVE_DISCORDRPC - discordInfo.maxPlayers = netbuffer->u.servercfg.maxplayer; - discordInfo.joinsAllowed = netbuffer->u.servercfg.allownewplayer; - discordInfo.everyoneCanInvite = netbuffer->u.servercfg.discordinvites; -#endif - - nodeingame[(UINT8)servernode] = true; - serverplayer = netbuffer->u.servercfg.serverplayer; - doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum); - mynode = netbuffer->u.servercfg.clientnode; - if (serverplayer >= 0) - playernode[(UINT8)serverplayer] = servernode; - - if (netgame) - CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n")); - DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode)); - - /// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook? - /// Shouldn't them be downloaded even at intermission time? - /// Also, according to HandleConnect, the server will send the savegame even during intermission... - /// Sryder 2018-07-05: If we don't want to send the player config another way we need to send the gamestate - /// At almost any gamestate there could be joiners... So just always send gamestate? - cl_mode = ((server) ? CL_CONNECTED : CL_DOWNLOADSAVEGAME); - break; - } - - // Handled in d_netfil.c - case PT_FILEFRAGMENT: - if (server) - { // But wait I thought I'm the server? - Net_CloseConnection(node); - break; - } - SERVERONLY - PT_FileFragment(); - break; - - case PT_FILEACK: - if (server) - PT_FileAck(); - break; - - case PT_FILERECEIVED: - if (server) - PT_FileReceived(); - break; - - case PT_REQUESTFILE: - if (server) - { - if (!cv_downloading.value || !PT_RequestFile(node)) - Net_CloseConnection(node); // close connection if one of the requested files could not be sent, or you disabled downloading anyway - } - else - Net_CloseConnection(node); // nope - break; - - case PT_NODETIMEOUT: - case PT_CLIENTQUIT: - if (server) - Net_CloseConnection(node); - break; - - case PT_CLIENTCMD: - break; // This is not an "unknown packet" - - case PT_PLAYERINFO: - HandlePlayerInfo(node); - break; - - case PT_SERVERTICS: - // Do not remove my own server (we have just get a out of order packet) - if (node == servernode) - break; - /* FALLTHRU */ + case PT_ASKINFOVIAMS : PT_AskInfoViaMS (node ); break; + case PT_TELLFILESNEEDED: PT_TellFilesNeeded(node ); break; + case PT_MOREFILESNEEDED: PT_MoreFilesNeeded(node ); break; + case PT_ASKINFO : PT_AskInfo (node ); break; + case PT_SERVERREFUSE : PT_ServerRefuse (node ); break; + case PT_SERVERCFG : PT_ServerCFG (node ); break; + case PT_FILEFRAGMENT : PT_FileFragment (node, -1); break; + case PT_FILEACK : PT_FileAck (node ); break; + case PT_FILERECEIVED : PT_FileReceived (node ); break; + case PT_REQUESTFILE : PT_RequestFile (node ); break; + case PT_NODETIMEOUT : PT_ClientQuit (node, -1); break; + case PT_CLIENTQUIT : PT_ClientQuit (node, -1); break; + case PT_SERVERTICS : PT_ServerTics (node, -1); break; + case PT_CLIENTCMD : break; // This is not an "unknown packet" + case PT_PLAYERINFO : HandlePlayerInfo (node ); break; default: DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype)); Net_CloseConnection(node); - break; // Ignore it - } -#undef SERVERONLY -} - -/** Checks ticcmd for "speed hacks" - * - * \param p Which player - * \return True if player is hacking - * \sa HandlePacketFromPlayer - * - */ -static boolean CheckForSpeedHacks(UINT8 p) -{ - if (netcmds[maketic%BACKUPTICS][p].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][p].forwardmove < -MAXPLMOVE - || netcmds[maketic%BACKUPTICS][p].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][p].sidemove < -MAXPLMOVE - || netcmds[maketic%BACKUPTICS][p].turning > KART_FULLTURN || netcmds[maketic%BACKUPTICS][p].turning < -KART_FULLTURN - || netcmds[maketic%BACKUPTICS][p].throwdir > KART_FULLTURN || netcmds[maketic%BACKUPTICS][p].throwdir < -KART_FULLTURN) - { - CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), playernode[p]); - //D_Clearticcmd(k); - - SendKick(p, KICK_MSG_CON_FAIL); - return true; - } - - return false; } /** Handles a packet received from a node that is in game @@ -4837,10 +5224,6 @@ static void HandlePacketFromPlayer(SINT8 node) { doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); INT32 netconsole; - tic_t realend, realstart; - UINT8 *pak, *txtpak, numtxtpak; - - txtpak = NULL; if (dedicated && node == 0) netconsole = 0; @@ -4853,423 +5236,45 @@ static void HandlePacketFromPlayer(SINT8 node) switch (netbuffer->packettype) { -// -------------------------------------------- SERVER RECEIVE ---------- - case PT_CLIENTCMD: - case PT_CLIENT2CMD: - case PT_CLIENT3CMD: - case PT_CLIENT4CMD: - case PT_CLIENTMIS: - case PT_CLIENT2MIS: - case PT_CLIENT3MIS: - case PT_CLIENT4MIS: - case PT_NODEKEEPALIVE: - case PT_NODEKEEPALIVEMIS: - if (client) - break; - - // To save bytes, only the low byte of tic numbers are sent - // Use ExpandTics to figure out what the rest of the bytes are - - realstart = ExpandTics(netbuffer->u.clientpak.client_tic, nettics[node]); - realend = ExpandTics(netbuffer->u.clientpak.resendfrom, nettics[node]); - - if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS - || netbuffer->packettype == PT_CLIENT3MIS || netbuffer->packettype == PT_CLIENT4MIS - || netbuffer->packettype == PT_NODEKEEPALIVEMIS - || supposedtics[node] < realend) - { - supposedtics[node] = realend; - } - // Discard out of order packet - if (nettics[node] > realend) - { - DEBFILE(va("out of order ticcmd discarded nettics = %u\n", nettics[node])); - break; - } - - // Update the nettics - nettics[node] = realend; - - // This should probably still timeout though, as the node should always have a player 1 number - if (netconsole == -1) - break; - - // As long as clients send valid ticcmds, the server can keep running, so reset the timeout - /// \todo Use a separate cvar for that kind of timeout? - freezetimeout[node] = I_GetTime() + connectiontimeout; - - // Don't do anything for packets of type NODEKEEPALIVE? - // Sryder 2018/07/01: Update the freezetimeout still! - if (netbuffer->packettype == PT_NODEKEEPALIVE - || netbuffer->packettype == PT_NODEKEEPALIVEMIS) - break; - - // store it in an internal buffer so the last packet takes precedence, which minimizes input lag - G_MoveTiccmd(&playercmds[netconsole], &netbuffer->u.clientpak.cmd, 1); - - // Check ticcmd for "speed hacks" - if (CheckForSpeedHacks((UINT8)netconsole)) - break; - - // Splitscreen cmd - if (((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS) - || (netbuffer->packettype == PT_CLIENT3CMD || netbuffer->packettype == PT_CLIENT3MIS) - || (netbuffer->packettype == PT_CLIENT4CMD || netbuffer->packettype == PT_CLIENT4MIS)) - && (nodetoplayer2[node] >= 0)) - { - G_MoveTiccmd(&playercmds[(UINT8)nodetoplayer2[node]], &netbuffer->u.client2pak.cmd2, 1); - - if (CheckForSpeedHacks((UINT8)nodetoplayer2[node])) - break; - } - - if (((netbuffer->packettype == PT_CLIENT3CMD || netbuffer->packettype == PT_CLIENT3MIS) - || (netbuffer->packettype == PT_CLIENT4CMD || netbuffer->packettype == PT_CLIENT4MIS)) - && (nodetoplayer3[node] >= 0)) - { - G_MoveTiccmd(&playercmds[(UINT8)nodetoplayer3[node]], &netbuffer->u.client3pak.cmd3, 1); - - if (CheckForSpeedHacks((UINT8)nodetoplayer3[node])) - break; - } - - if ((netbuffer->packettype == PT_CLIENT4CMD || netbuffer->packettype == PT_CLIENT4MIS) - && (nodetoplayer4[node] >= 0)) - { - G_MoveTiccmd(&playercmds[(UINT8)nodetoplayer4[node]], &netbuffer->u.client4pak.cmd4, 1); - - if (CheckForSpeedHacks((UINT8)nodetoplayer4[node])) - break; - } - - // Check player consistancy during the level - if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL - && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy) - && !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime() - && !SV_ResendingSavegameToAnyone()) - { - if (cv_resynchattempts.value) - { - // Tell the client we are about to resend them the gamestate - netbuffer->packettype = PT_WILLRESENDGAMESTATE; - HSendPacket(node, true, 0, 0); - - resendingsavegame[node] = true; - - if (cv_blamecfail.value) - CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"), - netconsole+1, player_names[netconsole], - consistancy[realstart%BACKUPTICS], - SHORT(netbuffer->u.clientpak.consistancy)); - DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n", - netconsole, realstart, consistancy[realstart%BACKUPTICS], - SHORT(netbuffer->u.clientpak.consistancy))); - break; - } - else - { - SendKick(netconsole, KICK_MSG_CON_FAIL); - DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n", - netconsole, realstart, consistancy[realstart%BACKUPTICS], - SHORT(netbuffer->u.clientpak.consistancy))); - break; - } - } - break; - case PT_BASICKEEPALIVE: - if (client) - break; - - // This should probably still timeout though, as the node should always have a player 1 number - if (netconsole == -1) - break; - - // If a client sends this it should mean they are done receiving the savegame - sendingsavegame[node] = false; - - // As long as clients send keep alives, the server can keep running, so reset the timeout - /// \todo Use a separate cvar for that kind of timeout? - freezetimeout[node] = I_GetTime() + connectiontimeout; - break; - case PT_TEXTCMD: - case PT_TEXTCMD2: - case PT_TEXTCMD3: - case PT_TEXTCMD4: - if (netbuffer->packettype == PT_TEXTCMD2) // splitscreen special - netconsole = nodetoplayer2[node]; - else if (netbuffer->packettype == PT_TEXTCMD3) - netconsole = nodetoplayer3[node]; - else if (netbuffer->packettype == PT_TEXTCMD4) - netconsole = nodetoplayer4[node]; - - if (client) - break; - - if (netconsole < 0 || netconsole >= MAXPLAYERS) - Net_UnAcknowledgePacket(node); - else - { - size_t j; - tic_t tic = maketic; - UINT8 *textcmd; - - UINT16 incoming_size; - - { - UINT8 *incoming = netbuffer->u.textcmd; - - incoming_size = READUINT16(incoming); - } - - // ignore if the textcmd has a reported size of zero - // this shouldn't be sent at all - if (!incoming_size) - { - DEBFILE(va("GetPacket: Textcmd with size 0 detected! (node %u, player %d)\n", - node, netconsole)); - Net_UnAcknowledgePacket(node); - break; - } - - // ignore if the textcmd size var is actually larger than it should be - // BASEPACKETSIZE + 2 (for size) + textcmd[0] should == datalength - if (incoming_size > (size_t)doomcom->datalength-BASEPACKETSIZE-2) - { - DEBFILE(va("GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d)\n", - incoming_size, sizeu1((size_t)doomcom->datalength-BASEPACKETSIZE-2), - node, netconsole)); - Net_UnAcknowledgePacket(node); - break; - } - - // check if tic that we are making isn't too large else we cannot send it :( - // doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time - j = software_MAXPACKETLENGTH - - (incoming_size + 3 + BASESERVERTICSSIZE - + (doomcom->numslots+1)*sizeof(ticcmd_t)); - - // search a tic that have enougth space in the ticcmd - while ((textcmd = D_GetExistingTextcmd(tic, netconsole)), - (TotalTextCmdPerTic(tic) > j || incoming_size + (textcmd ? ((UINT16*)textcmd)[0] : 0) > MAXTEXTCMD) - && tic < firstticstosend + BACKUPTICS) - tic++; - - if (tic >= firstticstosend + BACKUPTICS) - { - DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, " - "tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)), - maketic, firstticstosend, node, netconsole)); - Net_UnAcknowledgePacket(node); - break; - } - - // Make sure we have a buffer - if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole); - - DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n", - tic, ((UINT16*)textcmd)[0]+2, netconsole, firstticstosend, maketic)); - - memcpy(&textcmd[((UINT16*)textcmd)[0]+2], netbuffer->u.textcmd+2, incoming_size); - ((UINT16*)textcmd)[0] += incoming_size; - } - break; - case PT_LOGIN: - PT_Login(node); - break; - case PT_LOGINAUTH: - PT_LoginAuth(node, netconsole); - break; - case PT_NODETIMEOUT: - case PT_CLIENTQUIT: - if (client) - break; - - // nodeingame will be put false in the execution of kick command - // this allow to send some packets to the quitting client to have their ack back - nodewaiting[node] = 0; - if (netconsole != -1 && playeringame[netconsole]) - { - UINT8 kickmsg; - - if (netbuffer->packettype == PT_NODETIMEOUT) - kickmsg = KICK_MSG_TIMEOUT; - else - kickmsg = KICK_MSG_PLAYER_QUIT; - - SendKick(netconsole, kickmsg); - - /* - nodetoplayer[node] = -1; - - if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0 - && playeringame[(UINT8)nodetoplayer2[node]]) - { - SendKick(nodetoplayer2[node], kickmsg); - nodetoplayer2[node] = -1; - } - - if (nodetoplayer3[node] != -1 && nodetoplayer3[node] >= 0 - && playeringame[(UINT8)nodetoplayer3[node]]) - { - SendKick(nodetoplayer3[node], kickmsg); - nodetoplayer3[node] = -1; - } - - if (nodetoplayer4[node] != -1 && nodetoplayer4[node] >= 0 - && playeringame[(UINT8)nodetoplayer4[node]]) - { - SendKick(nodetoplayer4[node], kickmsg); - nodetoplayer4[node] = -1; - } - */ - } - Net_CloseConnection(node); - nodeingame[node] = false; - break; - case PT_CANRECEIVEGAMESTATE: - PT_CanReceiveGamestate(node); - break; - case PT_ASKLUAFILE: - if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED) - AddLuaFileToSendQueue(node, luafiletransfers->realfilename); - break; - case PT_HASLUAFILE: - if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING) - SV_HandleLuaFileSent(node); - break; - case PT_RECEIVEDGAMESTATE: - sendingsavegame[node] = false; - resendingsavegame[node] = false; - savegameresendcooldown[node] = I_GetTime() + 5 * TICRATE; - break; -// -------------------------------------------- CLIENT RECEIVE ---------- - case PT_SERVERTICS: - // Only accept PT_SERVERTICS from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node); - if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL); - break; - } - - realstart = ExpandTics(netbuffer->u.serverpak.starttic, maketic); - realend = realstart + netbuffer->u.serverpak.numtics; - - if (!txtpak) - txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots - * netbuffer->u.serverpak.numtics]; - - if (realend > gametic + CLIENTBACKUPTICS) - realend = gametic + CLIENTBACKUPTICS; - cl_packetmissed = realstart > neededtic; - - if (realstart <= neededtic && realend > neededtic) - { - tic_t i, j; - pak = (UINT8 *)&netbuffer->u.serverpak.cmds; - - for (i = realstart; i < realend; i++) - { - // clear first - D_Clearticcmd(i); - - // copy the tics - pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak, - netbuffer->u.serverpak.numslots*sizeof (ticcmd_t)); - - // copy the textcmds - numtxtpak = *txtpak++; - for (j = 0; j < numtxtpak; j++) - { - INT32 k = *txtpak++; // playernum - const size_t txtsize = ((UINT16*)txtpak)[0]+2; - - if (i >= gametic) // Don't copy old net commands - memcpy(D_GetTextcmd(i, k), txtpak, txtsize); - txtpak += txtsize; - } - } - - neededtic = realend; - } - else - { - DEBFILE(va("frame not in bound: %u\n", neededtic)); - /*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart) - I_Error("Received an out of order PT_SERVERTICS packet!\n" - "Got tics %d-%d, needed tic %d\n\n" - "Please report this crash on the Master Board,\n" - "IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/ - } - break; - case PT_PING: - // Only accept PT_PING from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_PING", node); - if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL); - break; - } - - //Update client ping table from the server. - if (client) - { - UINT8 i; - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i]) - { - playerpingtable[i] = (tic_t)netbuffer->u.netinfo.pingtable[i]; - playerpacketlosstable[i] = netbuffer->u.netinfo.packetloss[i]; - playerdelaytable[i] = netbuffer->u.netinfo.delay[i]; - } - } - - servermaxping = (tic_t)netbuffer->u.netinfo.pingtable[MAXPLAYERS]; - } - - break; - case PT_SERVERCFG: - break; - case PT_FILEFRAGMENT: - // Only accept PT_FILEFRAGMENT from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_FILEFRAGMENT", node); - if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL); - break; - } - if (client) - PT_FileFragment(); - break; - case PT_FILEACK: - if (server) - PT_FileAck(); - break; - case PT_FILERECEIVED: - if (server) - PT_FileReceived(); - break; - case PT_LOGINCHALLENGE: - PT_LoginChallenge(node); - break; - case PT_SERVERINFOUPDATE: - PT_ServerInfoUpdate(node); - break; - case PT_WILLRESENDGAMESTATE: - PT_WillResendGamestate(); - break; - case PT_SENDINGLUAFILE: - if (client) - CL_PrepareDownloadLuaFile(); - break; + // SERVER RECEIVE + case PT_CLIENTCMD : PT_ClientCmd (node, netconsole); break; + case PT_CLIENT2CMD : PT_ClientCmd (node, netconsole); break; + case PT_CLIENT3CMD : PT_ClientCmd (node, netconsole); break; + case PT_CLIENT4CMD : PT_ClientCmd (node, netconsole); break; + case PT_CLIENTMIS : PT_ClientCmd (node, netconsole); break; + case PT_CLIENT2MIS : PT_ClientCmd (node, netconsole); break; + case PT_CLIENT3MIS : PT_ClientCmd (node, netconsole); break; + case PT_CLIENT4MIS : PT_ClientCmd (node, netconsole); break; + case PT_NODEKEEPALIVE : PT_ClientCmd (node, netconsole); break; + case PT_NODEKEEPALIVEMIS : PT_ClientCmd (node, netconsole); break; + case PT_BASICKEEPALIVE : PT_BasicKeepAlive (node, netconsole); break; + case PT_TEXTCMD : PT_TextCmd (node, netconsole); break; + case PT_TEXTCMD2 : PT_TextCmd (node, netconsole); break; + case PT_TEXTCMD3 : PT_TextCmd (node, netconsole); break; + case PT_TEXTCMD4 : PT_TextCmd (node, netconsole); break; + case PT_LOGIN : PT_Login (node ); break; + case PT_LOGINAUTH : PT_LoginAuth (node, netconsole); break; + case PT_NODETIMEOUT : PT_ClientQuit (node, netconsole); break; + case PT_CLIENTQUIT : PT_ClientQuit (node, netconsole); break; + case PT_CANRECEIVEGAMESTATE: PT_CanReceiveGamestate(node ); break; + case PT_ASKLUAFILE : PT_AskLuaFile (node ); break; + case PT_HASLUAFILE : PT_HasLuaFile (node ); break; + case PT_RECEIVEDGAMESTATE : PT_ReceivedGamestate (node ); break; + // CLIENT RECEIVE + case PT_SERVERTICS : PT_ServerTics (node, netconsole); break; + case PT_PING : PT_Ping (node, netconsole); break; + case PT_FILEFRAGMENT : PT_FileFragment (node, netconsole); break; + case PT_FILEACK : PT_FileAck (node ); break; + case PT_FILERECEIVED : PT_FileReceived (node ); break; + case PT_LOGINCHALLENGE : PT_LoginChallenge (node ); break; + case PT_SERVERINFOUPDATE : PT_ServerInfoUpdate (node ); break; + case PT_WILLRESENDGAMESTATE: PT_WillResendGamestate(node ); break; + case PT_SENDINGLUAFILE : PT_SendingLuaFile (node ); break; + case PT_SERVERCFG : break; default: DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n", netbuffer->packettype, node)); - } // end switch + } } /** Handles all received packets, if any diff --git a/src/d_netfil.c b/src/d_netfil.c index 99f6601c8..af8c75e79 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -496,14 +496,19 @@ tryagain: } // get request filepak and put it on the send queue -// returns false if a requested file was not found or cannot be sent -boolean PT_RequestFile(INT32 node) +void PT_RequestFile(INT32 node) { doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); char wad[MAX_WADPATH+1]; UINT8 *p = netbuffer->u.textcmd; UINT8 id; + if (client || !cv_downloading.value) + { + Net_CloseConnection(node); // close connection if you are not the server or disabled downloading + return; + } + while (p < netbuffer->u.textcmd + MAXTEXTCMD) // Don't allow hacked client to overflow { id = READUINT8(p); @@ -515,10 +520,11 @@ boolean PT_RequestFile(INT32 node) if (cv_noticedownload.value) CONS_Printf("Bad PT_REQUESTFILE from node %d!\n", node); SV_AbortSendFiles(node); - return false; // don't read any more + Net_CloseConnection(node); // close connection if one of the requested files could not be sent + return; // don't read the rest of the files } } - return true; // no problems with any files + return; // no problems with any files } /** Checks if the files needed aren't already loaded or on the disk @@ -1262,14 +1268,16 @@ void FileSendTicker(void) } } -void PT_FileAck(void) +void PT_FileAck(SINT8 node) { doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); fileack_pak *packet = (void*)&netbuffer->u.fileack; - INT32 node = doomcom->remotenode; filetran_t *trans = &transfer[node]; INT32 i, j; + if (client) + return; + // Wrong file id? Ignore it, it's probably a late packet if (!(trans->txlist && packet->fileid == trans->txlist->fileid)) return; @@ -1316,12 +1324,12 @@ void PT_FileAck(void) } } -void PT_FileReceived(void) +void PT_FileReceived(SINT8 node) { - filetx_t *trans = transfer[doomcom->remotenode].txlist; + filetx_t *trans = transfer[node].txlist; doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); - if (trans && netbuffer->u.filereceived == trans->fileid) + if (server && trans && netbuffer->u.filereceived == trans->fileid) SV_EndFileSend(doomcom->remotenode); } @@ -1409,8 +1417,27 @@ void FileReceiveTicker(void) } } -void PT_FileFragment(void) +void PT_FileFragment(SINT8 node, INT32 netconsole) { + if (nodeingame[node]) + { + // Only accept PT_FILEFRAGMENT from the server. + if (node != servernode) + { + CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_FILEFRAGMENT", node); + if (server) + SendKick(netconsole, KICK_MSG_CON_FAIL /*| KICK_MSG_KEEP_BODY*/); + return; + } + if (server) + return; + } + else if (server || node != servernode) + { + Net_CloseConnection(node); + return; + } + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); filetx_pak *pak = (void*)&netbuffer->u.filetxpak; INT32 filenum = pak->fileid; diff --git a/src/d_netfil.h b/src/d_netfil.h index 0d2c2b06f..cf4617fbe 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -96,16 +96,16 @@ void AddRamToSendQueue(INT32 node, void *data, size_t size, freemethod_t freemet UINT8 fileid); void FileSendTicker(void); -void PT_FileAck(void); -void PT_FileReceived(void); +void PT_FileAck(SINT8 node); +void PT_FileReceived(SINT8 node); boolean SendingFile(INT32 node); void FileReceiveTicker(void); -void PT_FileFragment(void); +void PT_FileFragment(SINT8 node, INT32 netconsole); boolean CL_CheckDownloadable(void); boolean CL_SendFileRequest(void); -boolean PT_RequestFile(INT32 node); +void PT_RequestFile(INT32 node); typedef enum { From 84422603d03e371666125cd6b3711687de6689f2 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 17 Nov 2025 03:20:16 -0500 Subject: [PATCH 2/7] Properly unset TICCMD_RECEIVED like it use to packet loss indication logic is commented out until I port a fix --- src/d_clisrv.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index bf4857943..a247f5701 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5762,31 +5762,30 @@ static void Local_Maketic(INT32 realtics) static void SV_Maketic(void) { INT32 i; - - // Moved here so bots and packetloss indication doesn't break.... - G_MoveTiccmd(netcmds[maketic % BACKUPTICS], playercmds, MAXPLAYERS); - for (i = 0; i < MAXPLAYERS; i++) { packetloss[i][maketic%PACKETMEASUREWINDOW] = false; - if (!playeringame[i]) continue; - if (K_PlayerUsesBotMovement(&players[i])) { - K_BuildBotTiccmd(&players[i], &netcmds[maketic%BACKUPTICS][i]); + K_BuildBotTiccmd(&players[i], &playercmds[i]); continue; } - // We didn't receive this tic if ((netcmds[maketic % BACKUPTICS][i].flags & TICCMD_RECEIVED) == 0) { + ticcmd_t * ticcmd = &netcmds[(maketic ) % BACKUPTICS][i]; + ticcmd_t *prevticcmd = &netcmds[(maketic - 1) % BACKUPTICS][i]; + // Copy the input from the previous tic + *ticcmd = *prevticcmd; + ticcmd->flags &= ~TICCMD_RECEIVED; // packetloss[i][leveltime%PACKETMEASUREWINDOW] = (cmd->flags & TICCMD_RECEIVED) ? false : true; - packetloss[i][maketic%PACKETMEASUREWINDOW] = true; + //packetloss[i][maketic%PACKETMEASUREWINDOW] = true; } } - + // Moved here so bots and packetloss indication doesn't break.... + G_MoveTiccmd(netcmds[maketic % BACKUPTICS], playercmds, MAXPLAYERS); maketic++; } From fcba6319d31bfcd62e4697b394fe63418df74d07 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 17 Nov 2025 03:36:41 -0500 Subject: [PATCH 3/7] Port SRB2Classic Packetloss indication logic --- src/d_clisrv.c | 19 ++++++++++--------- src/d_clisrv.h | 2 +- src/d_net.c | 15 +++++++++++++-- src/d_net.h | 5 ++--- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index a247f5701..96ac2bb01 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5764,7 +5764,6 @@ static void SV_Maketic(void) INT32 i; for (i = 0; i < MAXPLAYERS; i++) { - packetloss[i][maketic%PACKETMEASUREWINDOW] = false; if (!playeringame[i]) continue; if (K_PlayerUsesBotMovement(&players[i])) @@ -5780,8 +5779,6 @@ static void SV_Maketic(void) // Copy the input from the previous tic *ticcmd = *prevticcmd; ticcmd->flags &= ~TICCMD_RECEIVED; - // packetloss[i][leveltime%PACKETMEASUREWINDOW] = (cmd->flags & TICCMD_RECEIVED) ? false : true; - //packetloss[i][maketic%PACKETMEASUREWINDOW] = true; } } // Moved here so bots and packetloss indication doesn't break.... @@ -5933,7 +5930,7 @@ static INT32 pingtimeout[MAXPLAYERS]; static inline void PingUpdate(void) { - INT32 i, j; + INT32 i; boolean laggers[MAXPLAYERS]; UINT8 numlaggers = 0; doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); @@ -5997,14 +5994,18 @@ static inline void PingUpdate(void) playerpingtable[i] = realpingtable[i] / pingmeasurecount; realpingtable[i] = 0; //Reset each as we go. - UINT32 lost = 0; - for (j = 0; j < PACKETMEASUREWINDOW; j++) + UINT32 node = playernode[i]; + if (node < MAXNETNODES) { - if (packetloss[i][j]) - lost++; + if (sentpackets[node] == 0) + playerpacketlosstable[i] = 0; + else + playerpacketlosstable[i] = lostpackets[node]; + lostpackets[node] = 0; + sentpackets[node] = 0; } - netbuffer->u.netinfo.packetloss[i] = lost; + netbuffer->u.netinfo.packetloss[i] = playerpacketlosstable[i]; netbuffer->u.netinfo.delay[i] = playerdelaytable[i]; } diff --git a/src/d_clisrv.h b/src/d_clisrv.h index a7943dace..0f9b3413e 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -360,7 +360,7 @@ struct doomdata_t UINT8 ackreturn; // The return of the ack number UINT8 packettype; - UINT8 reserved; // Padding + UINT8 packetindex; union { clientcmd_pak clientpak; diff --git a/src/d_net.c b/src/d_net.c index 5dd350a8f..10b11985a 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -97,12 +97,12 @@ bannednode_t *bannednode = NULL; static tic_t statstarttic; INT32 getbytes = 0; INT64 sendbytes = 0; +UINT32 sentpackets[MAXNETNODES]; +UINT32 lostpackets[MAXNETNODES]; static INT32 retransmit = 0, duppacket = 0; static INT32 sendackpacket = 0, getackpacket = 0; INT32 ticruned = 0, ticmiss = 0; -boolean packetloss[MAXPLAYERS][PACKETMEASUREWINDOW]; - // globals INT32 getbps, sendbps; float lostpercent, duppercent, gamelostpercent; @@ -194,6 +194,8 @@ typedef struct UINT8 nextacknum; UINT8 flags; + UINT8 sendnum; + UINT8 recvnum; } netnode_t; static netnode_t nodes[MAXNETNODES]; @@ -523,6 +525,7 @@ void Net_AckTicker(void) ackpak[i].senttime = I_GetTime(); ackpak[i].resentnum++; ackpak[i].nextacknum = node->nextacknum; + netbuffer->packetindex = node->sendnum++; retransmit++; // For stat HSendPacket((INT32)(node - nodes), false, ackpak[i].acknum, (size_t)(ackpak[i].length - BASEPACKETSIZE)); @@ -633,6 +636,8 @@ static void InitNode(netnode_t *node) node->nextacknum = 1; node->remotefirstack = 0; node->flags = 0; + node->sendnum = 0; + node->recvnum = 0; } static void InitAck(void) @@ -1077,6 +1082,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen else netbuffer->ack = acknum; + netbuffer->packetindex = nodes[doomcom->remotenode].sendnum++; netbuffer->checksum = NetbufferChecksum(); sendbytes += packetheaderlength + doomcom->datalength; // For stat @@ -1196,6 +1202,11 @@ boolean HGetPacket(void) continue; // discarded (duplicated) } + // measure packet loss + sentpackets[doomcom->remotenode] += (UINT32)netbuffer->packetindex - nodes[doomcom->remotenode].recvnum; + lostpackets[doomcom->remotenode] += (UINT32)netbuffer->packetindex - nodes[doomcom->remotenode].recvnum - 1; + nodes[doomcom->remotenode].recvnum = netbuffer->packetindex; + // A packet with just ackreturn if (netbuffer->packettype == PT_NOTHING) { diff --git a/src/d_net.h b/src/d_net.h index fe5a1d35f..406bc815d 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -41,9 +41,8 @@ extern INT32 packetheaderlength; boolean Net_GetNetStat(void); extern INT32 getbytes; extern INT64 sendbytes; // Realtime updated - -#define PACKETMEASUREWINDOW (TICRATE*2) -extern boolean packetloss[MAXPLAYERS][PACKETMEASUREWINDOW]; +extern UINT32 sentpackets[MAXNETNODES]; +extern UINT32 lostpackets[MAXNETNODES]; extern SINT8 nodetoplayer[MAXNETNODES]; extern SINT8 nodetoplayer2[MAXNETNODES]; // Say the numplayer for this node if any (splitscreen) From d4ed32a799e1aa0075ba056200f1061bf12d58ba Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 17 Nov 2025 03:49:35 -0500 Subject: [PATCH 4/7] Port packetloss cycles commit as well --- src/d_clisrv.c | 21 +++++++++++++++++---- src/d_net.c | 19 ++++++++++++++----- src/d_net.h | 5 +++-- src/doomdef.h | 1 + 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 96ac2bb01..419c8db95 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5997,18 +5997,31 @@ static inline void PingUpdate(void) UINT32 node = playernode[i]; if (node < MAXNETNODES) { - if (sentpackets[node] == 0) + playerpacketlosstable[i] = 0; + UINT32 totalpackets = 0; + for (INT32 j = 0; j < PACKETLOSSCYCLES; j++) + { + playerpacketlosstable[i] += lostpackets[j][node]; + totalpackets += sentpackets[j][node]; + } + if (totalpackets == 0) playerpacketlosstable[i] = 0; else - playerpacketlosstable[i] = lostpackets[node]; - lostpackets[node] = 0; - sentpackets[node] = 0; + playerpacketlosstable[i] = playerpacketlosstable[i]; } netbuffer->u.netinfo.packetloss[i] = playerpacketlosstable[i]; netbuffer->u.netinfo.delay[i] = playerdelaytable[i]; } + if (++plcycle >= PACKETLOSSCYCLES) + plcycle = 0; + for (INT32 node = 0; node < MAXNETNODES; node++) + { + lostpackets[plcycle][node] = 0; + sentpackets[plcycle][node] = 0; + } + // send the server's maxping as last element of our ping table. This is useful to let us know when we're about to get kicked. netbuffer->u.netinfo.pingtable[MAXPLAYERS] = cv_maxping.value; diff --git a/src/d_net.c b/src/d_net.c index 10b11985a..5ec023ef4 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -97,8 +97,9 @@ bannednode_t *bannednode = NULL; static tic_t statstarttic; INT32 getbytes = 0; INT64 sendbytes = 0; -UINT32 sentpackets[MAXNETNODES]; -UINT32 lostpackets[MAXNETNODES]; +UINT8 plcycle = 0; +UINT32 sentpackets[PACKETLOSSCYCLES][MAXNETNODES]; +UINT32 lostpackets[PACKETLOSSCYCLES][MAXNETNODES]; static INT32 retransmit = 0, duppacket = 0; static INT32 sendackpacket = 0, getackpacket = 0; INT32 ticruned = 0, ticmiss = 0; @@ -1203,9 +1204,17 @@ boolean HGetPacket(void) } // measure packet loss - sentpackets[doomcom->remotenode] += (UINT32)netbuffer->packetindex - nodes[doomcom->remotenode].recvnum; - lostpackets[doomcom->remotenode] += (UINT32)netbuffer->packetindex - nodes[doomcom->remotenode].recvnum - 1; - nodes[doomcom->remotenode].recvnum = netbuffer->packetindex; + if ((SINT8)(netbuffer->packetindex - nodes[doomcom->remotenode].recvnum) <= 0) + { + // got out of order packet, so compensate + lostpackets[plcycle][doomcom->remotenode]--; + } + else + { + sentpackets[plcycle][doomcom->remotenode] += (SINT8)(netbuffer->packetindex - nodes[doomcom->remotenode].recvnum); + lostpackets[plcycle][doomcom->remotenode] += (SINT8)(netbuffer->packetindex - nodes[doomcom->remotenode].recvnum) - 1; + nodes[doomcom->remotenode].recvnum = netbuffer->packetindex; + } // A packet with just ackreturn if (netbuffer->packettype == PT_NOTHING) diff --git a/src/d_net.h b/src/d_net.h index 406bc815d..b1fa9ac7e 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -41,8 +41,9 @@ extern INT32 packetheaderlength; boolean Net_GetNetStat(void); extern INT32 getbytes; extern INT64 sendbytes; // Realtime updated -extern UINT32 sentpackets[MAXNETNODES]; -extern UINT32 lostpackets[MAXNETNODES]; +extern UINT8 plcycle; +extern UINT32 sentpackets[PACKETLOSSCYCLES][MAXNETNODES]; +extern UINT32 lostpackets[PACKETLOSSCYCLES][MAXNETNODES]; extern SINT8 nodetoplayer[MAXNETNODES]; extern SINT8 nodetoplayer2[MAXNETNODES]; // Say the numplayer for this node if any (splitscreen) diff --git a/src/doomdef.h b/src/doomdef.h index d4a5bf465..820e40b1a 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -193,6 +193,7 @@ extern char logfilename[1024]; #define MAXPLAYERNAME 21 #define MAXSPLITSCREENPLAYERS 4 // Max number of players on a single computer #define MAXGAMEPADS (MAXSPLITSCREENPLAYERS * 2) // Number of gamepads we'll be allowing +#define PACKETLOSSCYCLES 4 // amount of cycles to do when measuring packet loss #define MAXSKINS 4096 #define MAXFOLLOWERS UINT16_MAX From 242ef6bf769c69f81c1d608e9bbcb906068dfc96 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 17 Nov 2025 03:55:33 -0500 Subject: [PATCH 5/7] This is kinda redundent eh? --- src/d_clisrv.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 419c8db95..4a59e6fbd 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -6006,8 +6006,6 @@ static inline void PingUpdate(void) } if (totalpackets == 0) playerpacketlosstable[i] = 0; - else - playerpacketlosstable[i] = playerpacketlosstable[i]; } netbuffer->u.netinfo.packetloss[i] = playerpacketlosstable[i]; From 13d4c71b7414b74cf753169141d19247dbbfad2e Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Mon, 17 Nov 2025 15:53:54 +0100 Subject: [PATCH 6/7] Fix compiling without DISCORDRPC --- src/r_skins.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/r_skins.c b/src/r_skins.c index f6fc09cdb..bd8d70e19 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -238,10 +238,10 @@ void R_InitSkins(void) R_PatchSkins((UINT16)i); R_LoadSpriteInfoLumps(i, wadfiles[i]->numlumps); +#ifdef HAVE_DISCORDRPC if (i < NUMMAINWADS) - { g_discord_skins = numskins; - } +#endif } ST_ReloadSkinFaceGraphics(); } From f11df6c1029ad35c8e8ff538b711c11e96cc5864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= Date: Wed, 1 Nov 2023 17:18:26 +0100 Subject: [PATCH 7/7] Fix buffer overflow when tag bits are set --- src/taglist.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/taglist.c b/src/taglist.c index b87296dcd..c03b56095 100644 --- a/src/taglist.c +++ b/src/taglist.c @@ -178,10 +178,10 @@ void Taggroup_Add (taggroup_t *garray[], const mtag_t tag, size_t id) if (Taggroup_Find(group, id) != (size_t)-1) return; - if (! in_bit_array(tags_available, tag)) + if (! in_bit_array(tags_available, (UINT16)tag)) { num_tags++; - set_bit_array(tags_available, tag); + set_bit_array(tags_available, (UINT16)tag); } // Create group if empty. @@ -218,10 +218,10 @@ static void Taggroup_Add_Init(taggroup_t *garray[], const mtag_t tag, size_t id) group = garray[(UINT16)tag]; - if (! in_bit_array(tags_available, tag)) + if (! in_bit_array(tags_available, (UINT16)tag)) { num_tags++; - set_bit_array(tags_available, tag); + set_bit_array(tags_available, (UINT16)tag); } // Create group if empty. @@ -268,7 +268,7 @@ void Taggroup_Remove (taggroup_t *garray[], const mtag_t tag, size_t id) if (group->count == 1 && total_elements_with_tag(tag) == 1) { num_tags--; - unset_bit_array(tags_available, tag); + unset_bit_array(tags_available, (UINT16)tag); } // Strip away taggroup if no elements left.