From 262ed6b7f34440ec292c139e7977c0af18366404 Mon Sep 17 00:00:00 2001 From: LJ Sonic <lamr@free.fr> Date: Thu, 29 Dec 2022 10:23:12 +0100 Subject: [PATCH] Split packet handling switch into functions --- src/d_clisrv.c | 1111 ++++++++++++++++++++++++------------------------ src/d_netfil.c | 49 ++- src/d_netfil.h | 8 +- 3 files changed, 605 insertions(+), 563 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index be10e4f9b..d3f6854c7 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3677,8 +3677,531 @@ static void HandleServerInfo(SINT8 node) } #endif -static void PT_WillResendGamestate(void) +// 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) { + if (node == servernode) + return false; + + Net_CloseConnection(node); + return true; +} + +static void PT_AskInfoViaMS(SINT8 node) +{ + Net_CloseConnection(node); +} + +static void PT_TellFilesNeeded(SINT8 node) +{ + 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) +{ + 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) +{ + 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) +{ + 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; + } + + M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"), + reason), NULL, MM_NOTHING); + + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + + 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) +{ + 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) + return; + + if (client) + { + maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic); + G_SetGametype(netbuffer->u.servercfg.gametype); + modifiedgame = netbuffer->u.servercfg.modifiedgame; + memcpy(server_context, netbuffer->u.servercfg.server_context, 8); + } + + 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) +#ifndef NONET + CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n")); +#else + CONS_Printf(M_GetText("Join accepted, waiting for next level change...\n")); +#endif + DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode)); + +#ifndef NONET + /// \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... + if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* || + netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/) + cl_mode = CL_DOWNLOADSAVEGAME; + else +#endif + cl_mode = CL_CONNECTED; +} + +static void PT_ClientCmd(SINT8 node, INT32 netconsole) +{ + 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, node); + realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node); + + if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS + || 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; + + // Don't do anything for packets of type NODEKEEPALIVE? + if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE + || netbuffer->packettype == PT_NODEKEEPALIVEMIS) + 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; + + // Copy ticcmd + G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1); + + // Check ticcmd for "speed hacks" + if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE + || netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole); + //D_Clearticcmd(k); + + SendKick(netconsole, KICK_MSG_CON_FAIL); + return; + } + + // Splitscreen cmd + if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS) + && nodetoplayer2[node] >= 0) + G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]], + &netbuffer->u.client2pak.cmd2, 1); + + // Check player consistancy during the level + if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL + && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy) +#ifndef NONET + && !SV_ResendingSavegameToAnyone() +#endif + && !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime()) + { + 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 | KICK_MSG_KEEP_BODY); + 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_TextCmd(SINT8 node, INT32 netconsole) +{ + if (client) + return; + + // splitscreen special + if (netbuffer->packettype == PT_TEXTCMD2) + netconsole = nodetoplayer2[node]; + + if (netconsole < 0 || netconsole >= MAXPLAYERS) + Net_UnAcknowledgePacket(node); + else + { + size_t j; + tic_t tic = maketic; + UINT8 *textcmd; + + // ignore if the textcmd has a reported size of zero + // this shouldn't be sent at all + if (!netbuffer->u.textcmd[0]) + { + 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 + 1 (for size) + textcmd[0] should == datalength + if (netbuffer->u.textcmd[0] > (size_t)doomcom->datalength-BASEPACKETSIZE-1) + { + DEBFILE(va("GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d)\n", + netbuffer->u.textcmd[0], sizeu1((size_t)doomcom->datalength-BASEPACKETSIZE-1), + 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 + - (netbuffer->u.textcmd[0]+2+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 || netbuffer->u.textcmd[0] + (textcmd ? 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, textcmd[0]+1, netconsole, firstticstosend, maketic)); + + M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]); + textcmd[0] += (UINT8)netbuffer->u.textcmd[0]; + } +} + +static void PT_Login(SINT8 node, INT32 netconsole) +{ + (void)node; + + if (client) + return; + +#ifndef NOMD5 + UINT8 finalmd5[16];/* Well, it's the cool thing to do? */ + + if (doomcom->datalength < 16)/* ignore partial sends */ + return; + + if (!adminpasswordset) + { + CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[netconsole]); + return; + } + + // Do the final pass to compare with the sent md5 + D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", netconsole), &finalmd5); + + if (!memcmp(netbuffer->u.md5sum, finalmd5, 16)) + { + CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]); + COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately + } + else + CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]); +#else + (void)netconsole; +#endif +} + +static void PT_ClientQuit(SINT8 node, INT32 netconsole) +{ + if (client) + return; + + if (!nodeingame[node]) + { + Net_CloseConnection(node); + 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; + kickmsg |= KICK_MSG_KEEP_BODY; + + SendKick(netconsole, kickmsg); + nodetoplayer[node] = -1; + + if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0 + && playeringame[(UINT8)nodetoplayer2[node]]) + { + SendKick(nodetoplayer2[node], kickmsg); + nodetoplayer2[node] = -1; + } + } + Net_CloseConnection(node); + nodeingame[node] = false; +} + +static void PT_CanReceiveGamestate(SINT8 node) +{ +#ifndef NONET + 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; +#else + (void)node; +#endif +} + +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) +{ + 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 | KICK_MSG_KEEP_BODY); + return; + } + + realstart = netbuffer->u.serverpak.starttic; + 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 = txtpak[0]+1; + + if (i >= gametic) // Don't copy old net commands + M_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) +{ + // 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 | KICK_MSG_KEEP_BODY); + 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.pingtable[i]; + + servermaxping = (tic_t)netbuffer->u.pingtable[MAXPLAYERS]; + } +} + +static void PT_WillResendGamestate(SINT8 node) +{ + (void)node; + #ifndef NONET char tmpsave[256]; @@ -3705,19 +4228,12 @@ static void PT_WillResendGamestate(void) #endif } -static void PT_CanReceiveGamestate(SINT8 node) +static void PT_SendingLuaFile(SINT8 node) { -#ifndef NONET - 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; -#else (void)node; -#endif + + if (client) + CL_PrepareDownloadLuaFile(); } /** Handles a packet received from a node that isn't in game @@ -3733,202 +4249,27 @@ 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; - } - - M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"), - reason), NULL, MM_NOTHING); - - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - - 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) - break; - - if (client) - { - maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic); - G_SetGametype(netbuffer->u.servercfg.gametype); - modifiedgame = netbuffer->u.servercfg.modifiedgame; - memcpy(server_context, netbuffer->u.servercfg.server_context, 8); - } - - 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) -#ifndef NONET - CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n")); -#else - CONS_Printf(M_GetText("Join accepted, waiting for next level change...\n")); -#endif - DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode)); - -#ifndef NONET - /// \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... - if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* || - netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/) - cl_mode = CL_DOWNLOADSAVEGAME; - else -#endif - cl_mode = CL_CONNECTED; - 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_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" default: DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype)); Net_CloseConnection(node); - break; // Ignore it - } -#undef SERVERONLY } /** Handles a packet received from a node that is in game @@ -3942,13 +4283,6 @@ static void HandlePacketFromAwayNode(SINT8 node) static void HandlePacketFromPlayer(SINT8 node) { INT32 netconsole; - tic_t realend, realstart; - UINT8 *pak, *txtpak, numtxtpak; -#ifndef NOMD5 - UINT8 finalmd5[16];/* Well, it's the cool thing to do? */ -#endif - - txtpak = NULL; if (dedicated && node == 0) netconsole = 0; @@ -3961,358 +4295,39 @@ static void HandlePacketFromPlayer(SINT8 node) switch (netbuffer->packettype) { -// -------------------------------------------- SERVER RECEIVE ---------- + // SERVER RECEIVE case PT_CLIENTCMD: case PT_CLIENT2CMD: case PT_CLIENTMIS: case PT_CLIENT2MIS: 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, node); - realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node); - - if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS - || 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; - - // Don't do anything for packets of type NODEKEEPALIVE? - if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE - || netbuffer->packettype == PT_NODEKEEPALIVEMIS) - 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; - - // Copy ticcmd - G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1); - - // Check ticcmd for "speed hacks" - if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE - || netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE) - { - CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole); - //D_Clearticcmd(k); - - SendKick(netconsole, KICK_MSG_CON_FAIL); - break; - } - - // Splitscreen cmd - if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS) - && nodetoplayer2[node] >= 0) - G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]], - &netbuffer->u.client2pak.cmd2, 1); - - // Check player consistancy during the level - if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL - && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy) -#ifndef NONET - && !SV_ResendingSavegameToAnyone() -#endif - && !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime()) - { - 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 | KICK_MSG_KEEP_BODY); - DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n", - netconsole, realstart, consistancy[realstart%BACKUPTICS], - SHORT(netbuffer->u.clientpak.consistancy))); - break; - } - } + PT_ClientCmd(node, netconsole); break; - case PT_TEXTCMD2: // splitscreen special - netconsole = nodetoplayer2[node]; - /* FALLTHRU */ - case PT_TEXTCMD: - if (client) - break; + case PT_TEXTCMD : PT_TextCmd (node, netconsole); break; + case PT_TEXTCMD2 : PT_TextCmd (node, netconsole); break; + case PT_LOGIN : PT_Login (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; - if (netconsole < 0 || netconsole >= MAXPLAYERS) - Net_UnAcknowledgePacket(node); - else - { - size_t j; - tic_t tic = maketic; - UINT8 *textcmd; + // 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_WILLRESENDGAMESTATE: PT_WillResendGamestate(node ); break; + case PT_SENDINGLUAFILE : PT_SendingLuaFile (node ); break; + case PT_SERVERCFG : break; - // ignore if the textcmd has a reported size of zero - // this shouldn't be sent at all - if (!netbuffer->u.textcmd[0]) - { - 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 + 1 (for size) + textcmd[0] should == datalength - if (netbuffer->u.textcmd[0] > (size_t)doomcom->datalength-BASEPACKETSIZE-1) - { - DEBFILE(va("GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d)\n", - netbuffer->u.textcmd[0], sizeu1((size_t)doomcom->datalength-BASEPACKETSIZE-1), - 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 - - (netbuffer->u.textcmd[0]+2+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 || netbuffer->u.textcmd[0] + (textcmd ? 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, textcmd[0]+1, netconsole, firstticstosend, maketic)); - - M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]); - textcmd[0] += (UINT8)netbuffer->u.textcmd[0]; - } - break; - case PT_LOGIN: - if (client) - break; - -#ifndef NOMD5 - if (doomcom->datalength < 16)/* ignore partial sends */ - break; - - if (!adminpasswordset) - { - CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[netconsole]); - break; - } - - // Do the final pass to compare with the sent md5 - D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", netconsole), &finalmd5); - - if (!memcmp(netbuffer->u.md5sum, finalmd5, 16)) - { - CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]); - COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately - } - else - CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]); -#endif - 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; - kickmsg |= KICK_MSG_KEEP_BODY; - - SendKick(netconsole, kickmsg); - nodetoplayer[node] = -1; - - if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0 - && playeringame[(UINT8)nodetoplayer2[node]]) - { - SendKick(nodetoplayer2[node], kickmsg); - nodetoplayer2[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 | KICK_MSG_KEEP_BODY); - break; - } - - realstart = netbuffer->u.serverpak.starttic; - 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 = txtpak[0]+1; - - if (i >= gametic) // Don't copy old net commands - M_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 | KICK_MSG_KEEP_BODY); - 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.pingtable[i]; - - servermaxping = (tic_t)netbuffer->u.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 | KICK_MSG_KEEP_BODY); - break; - } - if (client) - PT_FileFragment(); - break; - case PT_FILEACK: - if (server) - PT_FileAck(); - break; - case PT_FILERECEIVED: - if (server) - PT_FileReceived(); - break; - case PT_WILLRESENDGAMESTATE: - PT_WillResendGamestate(); - break; - case PT_SENDINGLUAFILE: - if (client) - CL_PrepareDownloadLuaFile(); - 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 edbef30bb..7c9ed54d8 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -413,12 +413,17 @@ boolean CL_SendFileRequest(void) } // 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(SINT8 node) { 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-1) // Don't allow hacked client to overflow { id = READUINT8(p); @@ -428,11 +433,12 @@ boolean PT_RequestFile(INT32 node) if (!AddFileToSendQueue(node, id)) { SV_AbortSendFiles(node); - return false; // don't read the rest of the files + 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 @@ -1137,13 +1143,15 @@ void FileSendTicker(void) } } -void PT_FileAck(void) +void PT_FileAck(SINT8 node) { fileack_pak *packet = &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; @@ -1190,12 +1198,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; - if (trans && netbuffer->u.filereceived == trans->fileid) - SV_EndFileSend(doomcom->remotenode); + if (server && trans && netbuffer->u.filereceived == trans->fileid) + SV_EndFileSend(node); } static void SendAckPacket(fileack_pak *packet, UINT8 fileid) @@ -1281,8 +1289,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; + } + INT32 filenum = netbuffer->u.filetxpak.fileid; fileneeded_t *file = &fileneeded[filenum]; UINT32 fragmentpos = LONG(netbuffer->u.filetxpak.position); diff --git a/src/d_netfil.h b/src/d_netfil.h index f778a518f..5c7f4ef49 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -90,16 +90,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(SINT8 node); typedef enum {