diff --git a/src/Makefile b/src/Makefile index 49379682..76f013c5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -376,6 +376,14 @@ endif OPTS:=-fno-exceptions $(OPTS) +ifdef MOBJCONSISTANCY + OPTS+=-DMOBJCONSISTANCY +endif + +ifdef PACKETDROP + OPTS+=-DPACKETDROP +endif + ifdef DEBUGMODE # build with debugging information @@ -385,7 +393,7 @@ ifdef GCC48 else CFLAGS+=-O0 endif - CFLAGS+= -Wall -DPARANOIA -DRANGECHECK + CFLAGS+= -Wall -DPARANOIA -DRANGECHECK -DPACKETDROP -DMOBJCONSISTANCY else diff --git a/src/d_clisrv.c b/src/d_clisrv.c index c0f81ba3..7e9dcf8e 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -58,14 +58,14 @@ // NETWORKING // // gametic is the tic about to (or currently being) run -// maketic is the tic that hasn't had control made for it yet -// server: +// Server: +// maketic is the tic that hasn't had control made for it yet // nettics is the tic for each node // firstticstosend is the lowest value of nettics -// client: -// neededtic is the tic needed by the client for run the game +// Client: +// neededtic is the tic needed by the client to run the game // firstticstosend is used to optimize a condition -// normally maketic >= gametic > 0 +// Normally maketic >= gametic > 0 #define PREDICTIONQUEUE BACKUPTICS #define PREDICTIONMASK (PREDICTIONQUEUE-1) @@ -151,12 +151,6 @@ static consvar_t cv_showjoinaddress = {"showjoinaddress", "On", 0, CV_OnOff, NUL static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}}; consvar_t cv_playbackspeed = {"playbackspeed", "1", 0, playbackspeed_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -void D_ResetTiccmds(void) -{ - memset(&localcmds, 0, sizeof(ticcmd_t)); - memset(&localcmds2, 0, sizeof(ticcmd_t)); -} - static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n) { const size_t d = n / sizeof(ticcmd_t); @@ -185,11 +179,17 @@ static inline void *G_ScpyTiccmd(ticcmd_t* dest, void* src, const size_t n) -// some software don't support largest packet -// (original sersetup, not exactely, but the probabylity of sending a packet -// of 512 octet is like 0.1) +// Some software don't support largest packet +// (original sersetup, not exactely, but the probability of sending a packet +// of 512 bytes is like 0.1) UINT16 software_MAXPACKETLENGTH; +/** Guesses the value of a tic from its lowest byte and from maketic + * + * \param low The lowest byte of the tic value + * \return The full tic value + * + */ tic_t ExpandTics(INT32 low) { INT32 delta; @@ -214,7 +214,7 @@ void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum)) { #ifdef PARANOIA if (id >= MAXNETXCMD) - I_Error("command id %d too big", id); + I_Error("Command id %d too big", id); if (listnetxcmd[id] != 0) I_Error("Command id %d already used", id); #endif @@ -378,7 +378,7 @@ static void ExtraDataTicker(void) { const UINT8 id = *curpos; curpos++; - DEBFILE(va("executing x_cmd %u ply %u ", id, i)); + DEBFILE(va("executing x_cmd %s ply %u ", netxcmdnames[id - 1], i)); (listnetxcmd[id])(&curpos, i); DEBFILE("done\n"); } @@ -401,7 +401,11 @@ static void ExtraDataTicker(void) } } - D_FreeTextcmd(gametic); + // If you are a client, you can safely forget the net commands for this tic + // If you are the server, you need to remember them until every client has been aknowledged, + // because if you need to resend a PT_SERVERTICS packet, you need to put the commands in it + if (!server) + D_FreeTextcmd(gametic); } static void D_Clearticcmd(tic_t tic) @@ -416,6 +420,19 @@ static void D_Clearticcmd(tic_t tic) DEBFILE(va("clear tic %5u (%2u)\n", tic, tic%BACKUPTICS)); } +void D_ResetTiccmds(void) +{ + INT32 i; + + memset(&localcmds, 0, sizeof(ticcmd_t)); + memset(&localcmds2, 0, sizeof(ticcmd_t)); + + // Reset the net command list + for (i = 0; i < TEXTCMD_HASH_SIZE; i++) + while (textcmds[i]) + D_Clearticcmd(textcmds[i]->tic); +} + // ----------------------------------------------------------------- // end of extra data function // ----------------------------------------------------------------- @@ -1034,20 +1051,20 @@ static INT16 Consistancy(void); typedef enum { - cl_searching, - cl_downloadfiles, - cl_askjoin, - cl_waitjoinresponse, + CL_SEARCHING, + CL_DOWNLOADFILES, + CL_ASKJOIN, + CL_WAITJOINRESPONSE, #ifdef JOININGAME - cl_downloadsavegame, + CL_DOWNLOADSAVEGAME, #endif - cl_connected, - cl_aborted + CL_CONNECTED, + CL_ABORTED } cl_mode_t; static void GetPackets(void); -static cl_mode_t cl_mode = cl_searching; +static cl_mode_t cl_mode = CL_SEARCHING; // Player name send/load @@ -1100,10 +1117,10 @@ static inline void CL_DrawConnectionStatus(void) M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1); V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-24, V_YELLOWMAP, "Press ESC to abort"); - if (cl_mode != cl_downloadfiles) + if (cl_mode != CL_DOWNLOADFILES) { INT32 i, animtime = ((ccstime / 4) & 15) + 16; - UINT8 palstart = (cl_mode == cl_searching) ? 128 : 160; + UINT8 palstart = (cl_mode == CL_SEARCHING) ? 128 : 160; // 15 pal entries total. const char *cltext; @@ -1113,7 +1130,7 @@ static inline void CL_DrawConnectionStatus(void) switch (cl_mode) { #ifdef JOININGAME - case cl_downloadsavegame: + case CL_DOWNLOADSAVEGAME: cltext = M_GetText("Downloading game state..."); Net_GetNetStat(); V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, @@ -1122,8 +1139,8 @@ static inline void CL_DrawConnectionStatus(void) va("%3.1fK/s ", ((double)getbps)/1024)); break; #endif - case cl_askjoin: - case cl_waitjoinresponse: + case CL_ASKJOIN: + case CL_WAITJOINRESPONSE: cltext = M_GetText("Requesting to join..."); break; default: @@ -1157,11 +1174,16 @@ static inline void CL_DrawConnectionStatus(void) } #endif -// -// CL_SendJoin -// -// send a special packet for declare how many player in local -// used only in arbitratrenetstart() +/** Sends a special packet to declare how many players in local + * Used only in arbitratrenetstart() + * Sends a PT_CLIENTJOIN packet to the server + * + * \return True if the packet was successfully sent + * \todo Improve the description... + * Because to be honest, I have no idea what arbitratrenetstart is... + * Is it even used...? + * + */ static boolean CL_SendJoin(void) { UINT8 localplayers = 1; @@ -1296,6 +1318,12 @@ static void SV_SendPlayerInfo(INT32 node) HSendPacket(node, false, 0, sizeof(plrinfo) * MAXPLAYERS); } +/** Sends a PT_SERVERCFG packet + * + * \param node The destination + * \return True if the packet was successfully sent + * + */ static boolean SV_SendServerConfig(INT32 node) { INT32 i; @@ -1428,7 +1456,7 @@ static void SV_SendSaveGame(INT32 node) WRITEUINT32(savebuffer, 0); } - SendRam(node, buffertosend, length, SF_RAM, 0); + SV_SendRam(node, buffertosend, length, SF_RAM, 0); save_p = NULL; } @@ -1523,7 +1551,7 @@ static void CL_LoadReceivedSavegame(void) { CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl); if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) - CONS_Printf(M_GetText("ZONE")); + CONS_Printf(M_GetText(" ZONE")); if (actnum > 0) CONS_Printf(" %2d", actnum); } @@ -1679,11 +1707,252 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) #endif // ifndef NONET -// use adaptive send using net_bandwidth and stat.sendbytes +/** Called by CL_ServerConnectionTicker + * + * \param viams ??? + * \param asksent ??? + * \return False if the connection was aborted + * \sa CL_ServerConnectionTicker + * \sa CL_ConnectToServer + * + */ +static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent) +{ +#ifndef NONET + INT32 i; +#endif + +#ifndef NONET + // serverlist is updated by GetPacket function + if (serverlistcount > 0) + { + // this can be a responce to our broadcast request + if (servernode == -1 || servernode >= MAXNETNODES) + { + i = 0; + servernode = serverlist[i].node; + CONS_Printf(M_GetText("Found, ")); + } + else + { + i = SL_SearchServer(servernode); + if (i < 0) + return true; + } + + // Quit here rather than downloading files and being refused later. + if (serverlist[i].info.numberofplayer >= serverlist[i].info.maxplayer) + { + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(va(M_GetText("Maximum players reached: %d\n\nPress ESC\n"), serverlist[i].info.maxplayer), NULL, MM_NOTHING); + return false; + } + + if (!server) + { + D_ParseFileneeded(serverlist[i].info.fileneedednum, + serverlist[i].info.fileneeded); + CONS_Printf(M_GetText("Checking files...\n")); + i = CL_CheckFiles(); + if (i == 2) // cannot join for some reason + { + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText( + "You have WAD files loaded or have\n" + "modified the game in some way, and\n" + "your file list does not match\n" + "the server's file list.\n" + "Please restart SRB2 before connecting.\n\n" + "Press ESC\n" + ), NULL, MM_NOTHING); + return false; + } + else if (i == 1) + cl_mode = CL_ASKJOIN; + else + { + // must download something + // can we, though? + if (!CL_CheckDownloadable()) // nope! + { + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText( + "You cannot connect to this server\n" + "because you cannot download the files\n" + "that you are missing from the server.\n\n" + "See the console or log file for\n" + "more details.\n\n" + "Press ESC\n" + ), NULL, MM_NOTHING); + return false; + } + // no problem if can't send packet, we will retry later + if (CL_SendRequestFile()) + cl_mode = CL_DOWNLOADFILES; + } + } + else + cl_mode = CL_ASKJOIN; // files need not be checked for the server. + + return true; + } + + // Ask the info to the server (askinfo packet) + if (*asksent + NEWTICRATE < I_GetTime()) + { + SendAskInfo(servernode, viams); + *asksent = I_GetTime(); + } +#else + (void)viams; + (void)asksent; + // No netgames, so we skip this state. + cl_mode = CL_ASKJOIN; +#endif // ifndef NONET/else + + return true; +} + +/** Called by CL_ConnectToServer + * + * \param viams ??? + * \param tmpsave The name of the gamestate file??? + * \param oldtic Used for knowing when to poll events and redraw + * \param asksent ??? + * \return False if the connection was aborted + * \sa CL_ServerConnectionSearchTicker + * \sa CL_ConnectToServer + * + */ +static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic_t *oldtic, tic_t *asksent) +{ + boolean waitmore; + INT32 i; + +#ifdef NONET + (void)tmpsave; +#endif + + switch (cl_mode) + { + case CL_SEARCHING: + if (!CL_ServerConnectionSearchTicker(viams, asksent)) + return false; + break; + + case CL_DOWNLOADFILES: + waitmore = false; + for (i = 0; i < fileneedednum; i++) + if (fileneeded[i].status == FS_DOWNLOADING + || fileneeded[i].status == FS_REQUESTED) + { + waitmore = true; + break; + } + if (waitmore) + break; // exit the case + + cl_mode = CL_ASKJOIN; // don't break case continue to cljoin request now + + case CL_ASKJOIN: + CL_LoadServerFiles(); +#ifdef JOININGAME + // prepare structures to save the file + // WARNING: this can be useless in case of server not in GS_LEVEL + // but since the network layer doesn't provide ordered packets... + CL_PrepareDownloadSaveGame(tmpsave); +#endif + if (CL_SendJoin()) + cl_mode = CL_WAITJOINRESPONSE; + break; + +#ifdef JOININGAME + case CL_DOWNLOADSAVEGAME: + // At this state, the first (and only) needed file is the gamestate + if (fileneeded[0].status == FS_FOUND) + { + // Gamestate is now handled within CL_LoadReceivedSavegame() + CL_LoadReceivedSavegame(); + cl_mode = CL_CONNECTED; + } // don't break case continue to CL_CONNECTED + else + break; +#endif + + case CL_WAITJOINRESPONSE: + case CL_CONNECTED: + default: + break; + + // Connection closed by cancel, timeout or refusal. + case CL_ABORTED: + cl_mode = CL_SEARCHING; + return false; + + } + + GetPackets(); + Net_AckTicker(); + + // Call it only once by tic + if (*oldtic != I_GetTime()) + { + INT32 key; + + I_OsPolling(); + key = I_GetKey(); + if (key == KEY_ESCAPE) + { + CONS_Printf(M_GetText("Network game synchronization aborted.\n")); +// M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING); + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + return false; + } + + // why are these here? this is for servers, we're a client + //if (key == 's' && server) + // doomcom->numnodes = (INT16)pnumnodes; + //SV_FileSendTicker(); + *oldtic = I_GetTime(); + +#ifdef CLIENT_LOADINGSCREEN + if (!server && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED) + { + F_TitleScreenTicker(true); + F_TitleScreenDrawer(); + CL_DrawConnectionStatus(); + I_UpdateNoVsync(); // page flip or blit buffer + if (moviemode) + M_SaveFrame(); + } +#else + CON_Drawer(); + I_UpdateNoVsync(); +#endif + } + else + I_Sleep(); + + return true; +} + +/** Use adaptive send using net_bandwidth and stat.sendbytes + * + * \param viams ??? + * \todo Better description... + * + */ static void CL_ConnectToServer(boolean viams) { INT32 pnumnodes, nodewaited = doomcom->numnodes, i; - boolean waitmore; tic_t oldtic; #ifndef NONET tic_t asksent; @@ -1694,14 +1963,14 @@ static void CL_ConnectToServer(boolean viams) sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); #endif - cl_mode = cl_searching; + cl_mode = CL_SEARCHING; #ifdef CLIENT_LOADINGSCREEN lastfilenum = 0; #endif #ifdef JOININGAME - // don't get a corrupt savegame error because tmpsave already exists + // Don't get a corrupt savegame error because tmpsave already exists if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1) I_Error("Can't delete %s\n", tmpsave); #endif @@ -1725,7 +1994,7 @@ static void CL_ConnectToServer(boolean viams) pnumnodes = 1; oldtic = I_GetTime() - 1; #ifndef NONET - asksent = (tic_t)-TICRATE; + asksent = (tic_t) - TICRATE; i = SL_SearchServer(servernode); @@ -1752,197 +2021,23 @@ static void CL_ConnectToServer(boolean viams) do { - switch (cl_mode) - { - case cl_searching: + // If the connection was aborted for some reason, leave #ifndef NONET - // serverlist is updated by GetPacket function - if (serverlistcount > 0) - { - // this can be a responce to our broadcast request - if (servernode == -1 || servernode >= MAXNETNODES) - { - i = 0; - servernode = serverlist[i].node; - CONS_Printf(M_GetText("Found, ")); - } - else - { - i = SL_SearchServer(servernode); - if (i < 0) - break; // the case - } - - // Quit here rather than downloading files and being refused later. - if (serverlist[i].info.numberofplayer >= serverlist[i].info.maxplayer) - { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(va(M_GetText("Maximum players reached: %d\n\nPress ESC\n"), serverlist[i].info.maxplayer), NULL, MM_NOTHING); - return; - } - - if (!server) - { - D_ParseFileneeded(serverlist[i].info.fileneedednum, - serverlist[i].info.fileneeded); - CONS_Printf(M_GetText("Checking files...\n")); - i = CL_CheckFiles(); - if (i == 2) // cannot join for some reason - { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText( - "You have WAD files loaded or have\n" - "modified the game in some way, and\n" - "your file list does not match\n" - "the server's file list.\n" - "Please restart SRB2 before connecting.\n\n" - "Press ESC\n" - ), NULL, MM_NOTHING); - return; - } - else if (i == 1) - cl_mode = cl_askjoin; - else - { - // must download something - // can we, though? - if (!CL_CheckDownloadable()) // nope! - { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText( - "You cannot conect to this server\n" - "because you cannot download the files\n" - "that you are missing from the server.\n\n" - "See the console or log file for\n" - "more details.\n\n" - "Press ESC\n" - ), NULL, MM_NOTHING); - return; - } - // no problem if can't send packet, we will retry later - if (CL_SendRequestFile()) - cl_mode = cl_downloadfiles; - } - } - else - cl_mode = cl_askjoin; // files need not be checked for the server. - break; - } - // ask the info to the server (askinfo packet) - if (asksent + NEWTICRATE < I_GetTime()) - { - SendAskInfo(servernode, viams); - asksent = I_GetTime(); - } + if (!CL_ServerConnectionTicker(viams, tmpsave, &oldtic, &asksent)) #else - (void)viams; - // No netgames, so we skip this state. - cl_mode = cl_askjoin; -#endif // ifndef NONET/else - break; - case cl_downloadfiles: - waitmore = false; - for (i = 0; i < fileneedednum; i++) - if (fileneeded[i].status == FS_DOWNLOADING - || fileneeded[i].status == FS_REQUESTED) - { - waitmore = true; - break; - } - if (waitmore) - break; // exit the case - - cl_mode = cl_askjoin; // don't break case continue to cljoin request now - case cl_askjoin: - CL_LoadServerFiles(); -#ifdef JOININGAME - // prepare structures to save the file - // WARNING: this can be useless in case of server not in GS_LEVEL - // but since the network layer doesn't provide ordered packets... - CL_PrepareDownloadSaveGame(tmpsave); + if (!CL_ServerConnectionTicker(viams, (char*)NULL, &oldtic, (tic_t *)NULL)) #endif - if (CL_SendJoin()) - cl_mode = cl_waitjoinresponse; - break; -#ifdef JOININGAME - case cl_downloadsavegame: - if (fileneeded[0].status == FS_FOUND) - { - // Gamestate is now handled within CL_LoadReceivedSavegame() - CL_LoadReceivedSavegame(); - cl_mode = cl_connected; - } // don't break case continue to cl_connected - else - break; -#endif - case cl_waitjoinresponse: - case cl_connected: - default: - break; - - // Connection closed by cancel, timeout or refusal. - case cl_aborted: - cl_mode = cl_searching; - return; - } - - GetPackets(); - Net_AckTicker(); - - // call it only one by tic - if (oldtic != I_GetTime()) - { - INT32 key; - - I_OsPolling(); - key = I_GetKey(); - if (key == KEY_ESCAPE) - { - CONS_Printf(M_GetText("Network game synchronization aborted.\n")); -// M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING); - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - return; - } - - // why are these here? this is for servers, we're a client - //if (key == 's' && server) - // doomcom->numnodes = (INT16)pnumnodes; - //FiletxTicker(); - oldtic = I_GetTime(); - -#ifdef CLIENT_LOADINGSCREEN - if (!server && cl_mode != cl_connected && cl_mode != cl_aborted) - { - F_TitleScreenTicker(true); - F_TitleScreenDrawer(); - CL_DrawConnectionStatus(); - I_UpdateNoVsync(); // page flip or blit buffer - if (moviemode) - M_SaveFrame(); - } -#else - CON_Drawer(); - I_UpdateNoVsync(); -#endif - } - else I_Sleep(); + return; if (server) { pnumnodes = 0; for (i = 0; i < MAXNETNODES; i++) - if (nodeingame[i]) pnumnodes++; + if (nodeingame[i]) + pnumnodes++; } } - while (!(cl_mode == cl_connected && (!server || (server && nodewaited <= pnumnodes)))); + while (!(cl_mode == CL_CONNECTED && (!server || (server && nodewaited <= pnumnodes)))); DEBFILE(va("Synchronisation Finished\n")); @@ -2199,7 +2294,6 @@ void CL_ClearPlayer(INT32 playernum) P_RemoveMobj(players[playernum].mo->tracer); P_RemoveMobj(players[playernum].mo); } - players[playernum].mo = NULL; memset(&players[playernum], 0, sizeof (player_t)); } @@ -2711,6 +2805,10 @@ void D_ClientServerInit(void) COM_AddCommand("reloadbans", Command_ReloadBan); COM_AddCommand("connect", Command_connect); COM_AddCommand("nodes", Command_Nodes); +#ifdef PACKETDROP + COM_AddCommand("drop", Command_Drop); + COM_AddCommand("droprate", Command_Droprate); +#endif #endif RegisterNetXCmd(XD_KICK, Got_KickCmd); @@ -2754,7 +2852,7 @@ void SV_ResetServer(void) // +1 because this command will be executed in com_executebuffer in // tryruntic so gametic will be incremented, anyway maketic > gametic - // is not a issue + // is not an issue maketic = gametic + 1; neededtic = maketic; @@ -2808,7 +2906,7 @@ static inline void SV_GenContext(void) for (i = 0; i < 8; i++) { const char a = M_RandomKey(26*2); - if (a <= 26) // uppercase + if (a < 26) // uppercase server_context[i] = 'A'+a; else // lowercase server_context[i] = 'a'+(a-26); @@ -2843,7 +2941,7 @@ void D_QuitNetGame(void) if (serverrunning && ms_RoomId > 0) UnregisterServer(); } - else if (servernode > 0 && servernode < MAXNETNODES && nodeingame[(UINT8)servernode]!=0) + else if (servernode > 0 && servernode < MAXNETNODES && nodeingame[(UINT8)servernode]) { netbuffer->packettype = PT_CLIENTQUIT; HSendPacket(servernode, true, 0, 0); @@ -2864,12 +2962,12 @@ void D_QuitNetGame(void) #endif } -// add a node to the game (player will follow at map change or at savegame....) +// Adds a node to the game (player will follow at map change or at savegame....) static inline void SV_AddNode(INT32 node) { nettics[node] = gametic; supposedtics[node] = gametic; - // little hack because the server connect to itself and put + // little hack because the server connects to itself and puts // nodeingame when connected not here if (node) nodeingame[node] = true; @@ -3019,7 +3117,7 @@ static boolean SV_AddWaitingPlayers(void) void CL_AddSplitscreenPlayer(void) { - if (cl_mode == cl_connected) + if (cl_mode == CL_CONNECTED) CL_SendJoin(); } @@ -3027,7 +3125,7 @@ void CL_RemoveSplitscreenPlayer(void) { XBOXSTATIC UINT8 buf[2]; - if (cl_mode != cl_connected) + if (cl_mode != CL_CONNECTED) return; buf[0] = (UINT8)secondarydisplayplayer; @@ -3038,7 +3136,7 @@ void CL_RemoveSplitscreenPlayer(void) // is there a game running boolean Playing(void) { - return (server && serverrunning) || (!server && cl_mode == cl_connected); + return (server && serverrunning) || (!server && cl_mode == CL_CONNECTED); } boolean SV_SpawnServer(void) @@ -3086,7 +3184,7 @@ void SV_StopServer(void) D_Clearticcmd(i); consoleplayer = 0; - cl_mode = cl_searching; + cl_mode = CL_SEARCHING; maketic = gametic+1; neededtic = maketic; serverrunning = false; @@ -3132,6 +3230,11 @@ static size_t TotalTextCmdPerTic(tic_t tic) return total; } +/** Called when a PT_CLIENTJOIN packet is received + * + * \param node The packet sender + * + */ static void HandleConnect(SINT8 node) { if (bannednode && bannednode[node]) @@ -3163,6 +3266,9 @@ static void HandleConnect(SINT8 node) #endif SV_AddNode(node); + /// \note Wait what??? + /// What if the gamestate takes more than one second to get downloaded? + /// Or if a lagspike happens? // you get a free second before desynch checks. use it wisely. SV_InitResynchVars(node); @@ -3171,6 +3277,7 @@ static void HandleConnect(SINT8 node) if (!SV_SendServerConfig(node)) { G_SetGamestate(backupstate); + /// \note Shouldn't SV_SendRefuse be called before ResetNode? ResetNode(node); SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again")); /// \todo fix this !!! @@ -3201,6 +3308,11 @@ static void HandleConnect(SINT8 node) } } +/** Called when a PT_SERVERSHUTDOWN packet is received + * + * \param node The packet sender (should be the server) + * + */ static void HandleShutdown(SINT8 node) { (void)node; @@ -3210,6 +3322,11 @@ static void HandleShutdown(SINT8 node) M_StartMessage(M_GetText("Server has shutdown\n\nPress Esc\n"), NULL, MM_NOTHING); } +/** Called when a PT_NODETIMEOUT packet is received + * + * \param node The packet sender (should be the server) + * + */ static void HandleTimeout(SINT8 node) { (void)node; @@ -3220,6 +3337,12 @@ static void HandleTimeout(SINT8 node) } #ifndef NONET +/** Called when a PT_SERVERINFO packet is received + * + * \param node The packet sender + * \note What happens if the packet comes from a client or something like that? + * + */ static void HandleServerInfo(SINT8 node) { // compute ping in ms @@ -3233,36 +3356,547 @@ static void HandleServerInfo(SINT8 node) } #endif -/** \brief GetPackets +/** Handles a packet received from a node that isn't in game + * + * \param node The packet sender + * \todo Choose a better name, as the packet can also come from the server apparently? + * \sa HandlePacketFromPlayer + * \sa GetPackets + * + */ +static void HandlePacketFromAwayNode(SINT8 node) +{ + if (node != servernode) + DEBFILE(va("Received packet from unknown host %d\n", node)); - \todo break this 300 line function into multiple functions -*/ -static void GetPackets(void) + switch (netbuffer->packettype) + { + case PT_ASKINFOVIAMS: + if (server && serverrunning) + { + INT32 clientnode = I_NetMakeNode(netbuffer->u.msaskinfo.clientaddr); + SV_SendServerInfo(clientnode, (tic_t)LONG(netbuffer->u.msaskinfo.time)); + SV_SendPlayerInfo(clientnode); // Send extra info + Net_CloseConnection(clientnode); + // Don't close connection to MS. + } + 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; + } + if (cl_mode == CL_WAITJOINRESPONSE) + { + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + + M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"), + netbuffer->u.serverrefuse.reason), NULL, MM_NOTHING); + + // Will be reset by caller. Signals refusal. + cl_mode = CL_ABORTED; + } + break; + + case PT_SERVERCFG: // Positive response of client join request + { + INT32 j; + UINT8 *scp; + + if (server && serverrunning && node != servernode) + { // but wait I thought I'm the server? + Net_CloseConnection(node); + break; + } + /// \note how would this happen? and is it doing the right thing if it does? + if (cl_mode != CL_WAITJOINRESPONSE) + break; + + if (!server) + { + maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic); + gametype = netbuffer->u.servercfg.gametype; + modifiedgame = netbuffer->u.servercfg.modifiedgame; + adminplayer = netbuffer->u.servercfg.adminplayer; + 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) +#ifdef JOININGAME + 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)); + + memset(playeringame, 0, sizeof(playeringame)); + for (j = 0; j < MAXPLAYERS; j++) + { + if (netbuffer->u.servercfg.playerskins[j] == 0xFF + && netbuffer->u.servercfg.playercolor[j] == 0xFF) + continue; // not in game + + playeringame[j] = true; + SetPlayerSkinByNum(j, (INT32)netbuffer->u.servercfg.playerskins[j]); + players[j].skincolor = netbuffer->u.servercfg.playercolor[j]; + } + + scp = netbuffer->u.servercfg.varlengthinputs; + CV_LoadPlayerNames(&scp); + CV_LoadNetVars(&scp); +#ifdef JOININGAME + /// \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; + } + else + Got_Filetxpak(); + break; + + case PT_REQUESTFILE: + if (server) + Got_RequestFilePak(node); + 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; + + default: + DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype)); + Net_CloseConnection(node); + break; // Ignore it + + } +} + +/** Handles a packet received from a node that is in game + * + * \param node The packet sender + * \todo Choose a better name + * \sa HandlePacketFromAwayNode + * \sa GetPackets + * + */ +static void HandlePacketFromPlayer(SINT8 node) {FILESTAMP XBOXSTATIC INT32 netconsole; - XBOXSTATIC SINT8 node; - XBOXSTATIC tic_t realend,realstart; + XBOXSTATIC tic_t realend, realstart; XBOXSTATIC UINT8 *pak, *txtpak, numtxtpak; FILESTAMP + txtpak = NULL; + + if (dedicated && node == 0) + netconsole = 0; + else + netconsole = nodetoplayer[node]; +#ifdef PARANOIA + if (netconsole >= MAXPLAYERS) + I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole); +#endif + + switch (netbuffer->packettype) + { +// -------------------------------------------- SERVER RECEIVE ---------- + case PT_RESYNCHGET: + SV_AcknowledgeResynchAck(netconsole, netbuffer->u.resynchgot); + break; + case PT_CLIENTCMD: + case PT_CLIENT2CMD: + case PT_CLIENTMIS: + case PT_CLIENT2MIS: + case PT_NODEKEEPALIVE: + case PT_NODEKEEPALIVEMIS: + if (!server) + break; + + // Ignore tics from those not synched + if (resynch_inprogress[node]) + 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); + realend = ExpandTics(netbuffer->u.clientpak.resendfrom); + + 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; + + // 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) + { + XBOXSTATIC char buf[2]; + CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole); + //D_Clearticcmd(k); + + buf[0] = (char)netconsole; + buf[1] = KICK_MSG_CON_FAIL; + SendNetXCmd(XD_KICK, &buf, 2); + break; + } + + // Splitscreen cmd + if (netbuffer->packettype == PT_CLIENT2CMD && nodetoplayer2[node] >= 0) + G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]], + &netbuffer->u.client2pak.cmd2, 1); + + // A delay before we check resynching + // Used on join or just after a synch fail + if (resynch_delay[node]) + { + --resynch_delay[node]; + break; + } + // Check player consistancy during the level + if (realstart <= gametic && realstart > gametic - BACKUPTICS+1 && gamestate == GS_LEVEL + && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy)) + { + SV_RequireResynch(node); + + if (cv_resynchattempts.value && resynch_score[node] <= (unsigned)cv_resynchattempts.value*250) + { + 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 + { + XBOXSTATIC UINT8 buf[3]; + + buf[0] = (UINT8)netconsole; + buf[1] = KICK_MSG_CON_FAIL; + SendNetXCmd(XD_KICK, &buf, 2); + DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n", + netconsole, realstart, consistancy[realstart%BACKUPTICS], + SHORT(netbuffer->u.clientpak.consistancy))); + break; + } + } + else if (resynch_score[node]) + --resynch_score[node]; + break; + case PT_TEXTCMD2: // splitscreen special + netconsole = nodetoplayer2[node]; + case PT_TEXTCMD: + if (!server) + break; + + if (netconsole < 0 || netconsole >= MAXPLAYERS) + Net_UnAcknowledgePacket(node); + else + { + size_t j; + tic_t tic = maketic; + UINT8 *textcmd; + + // 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_NODETIMEOUT: + case PT_CLIENTQUIT: + if (!server) + 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]) + { + XBOXSTATIC UINT8 buf[2]; + buf[0] = (UINT8)netconsole; + if (netbuffer->packettype == PT_NODETIMEOUT) + buf[1] = KICK_MSG_TIMEOUT; + else + buf[1] = KICK_MSG_PLAYER_QUIT; + SendNetXCmd(XD_KICK, &buf, 2); + nodetoplayer[node] = -1; + if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0 + && playeringame[(UINT8)nodetoplayer2[node]]) + { + buf[0] = nodetoplayer2[node]; + SendNetXCmd(XD_KICK, &buf, 2); + nodetoplayer2[node] = -1; + } + } + Net_CloseConnection(node); + nodeingame[node] = false; + break; +// -------------------------------------------- CLIENT RECEIVE ---------- + case PT_RESYNCHEND: + // Only accept PT_RESYNCHEND from the server. + if (node != servernode) + { + CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_RESYNCHEND", node); + + if (server) + { + XBOXSTATIC UINT8 buf[2]; + buf[0] = (UINT8)node; + buf[1] = KICK_MSG_CON_FAIL; + SendNetXCmd(XD_KICK, &buf, 2); + } + + break; + } + resynch_local_inprogress = false; + + P_SetRandSeed(netbuffer->u.resynchend.randomseed); + + if (gametype == GT_CTF) + resynch_read_ctf(&netbuffer->u.resynchend); + resynch_read_others(&netbuffer->u.resynchend); + + break; + 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) + { + XBOXSTATIC UINT8 buf[2]; + buf[0] = (UINT8)node; + buf[1] = KICK_MSG_CON_FAIL; + SendNetXCmd(XD_KICK, &buf, 2); + } + + break; + } + + realstart = ExpandTics(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 + BACKUPTICS) + realend = gametic + BACKUPTICS; + 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; + + M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize); + txtpak += txtsize; + } + } + + neededtic = realend; + } + else + DEBFILE(va("frame not in bound: %u\n", neededtic)); + break; + case PT_RESYNCHING: + // Only accept PT_RESYNCHING from the server. + if (node != servernode) + { + CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_RESYNCHING", node); + + if (server) + { + XBOXSTATIC char buf[2]; + buf[0] = (char)node; + buf[1] = KICK_MSG_CON_FAIL; + SendNetXCmd(XD_KICK, &buf, 2); + } + + break; + } + resynch_local_inprogress = true; + CL_AcknowledgeResynch(&netbuffer->u.resynchpak); + break; +#ifdef NEWPING + case PT_PING: + // Only accept PT_PING from the server. + if (node != servernode) + { + CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_PING", node); + + if (server) + { + XBOXSTATIC char buf[2]; + buf[0] = (char)node; + buf[1] = KICK_MSG_CON_FAIL; + SendNetXCmd(XD_KICK, &buf, 2); + } + + break; + } + + //Update client ping table from the server. + if (!server) + { + INT32 i; + for (i = 0; i < MAXNETNODES; i++) + if (playeringame[i]) + playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i]; + } + + break; +#endif + case PT_SERVERCFG: + break; + case PT_FILEFRAGMENT: + if (!server) + Got_Filetxpak(); + break; + default: + DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n", + netbuffer->packettype, node)); + } // end switch +} + +/** Handles all received packets, if any + * + * \todo Add details to this description (lol) + * + */ +static void GetPackets(void) +{FILESTAMP + XBOXSTATIC SINT8 node; // The packet sender +FILESTAMP + player_joining = false; while (HGetPacket()) { node = (SINT8)doomcom->remotenode; + if (netbuffer->packettype == PT_CLIENTJOIN && server) { HandleConnect(node); continue; } if (netbuffer->packettype == PT_SERVERSHUTDOWN && node == servernode - && !server && cl_mode != cl_searching) + && !server && cl_mode != CL_SEARCHING) { HandleShutdown(node); continue; } if (netbuffer->packettype == PT_NODETIMEOUT && node == servernode - && !server && cl_mode != cl_searching) + && !server && cl_mode != CL_SEARCHING) { HandleTimeout(node); continue; @@ -3279,481 +3913,13 @@ FILESTAMP if (netbuffer->packettype == PT_PLAYERINFO) continue; // We do nothing with PLAYERINFO, that's for the MS browser. - if (!nodeingame[node]) - { - if (node != servernode) - DEBFILE(va("Received packet from unknown host %d\n", node)); - - // anyone trying to join - switch (netbuffer->packettype) - { - case PT_ASKINFOVIAMS: - if (server && serverrunning) - { - INT32 clientnode = I_NetMakeNode(netbuffer->u.msaskinfo.clientaddr); - SV_SendServerInfo(clientnode, (tic_t)LONG(netbuffer->u.msaskinfo.time)); - SV_SendPlayerInfo(clientnode); // send extra info - Net_CloseConnection(clientnode); - // Don't close connection to MS. - } - 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; - } - if (cl_mode == cl_waitjoinresponse) - { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - - M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"), - netbuffer->u.serverrefuse.reason), NULL, MM_NOTHING); - - // Will be reset by caller. Signals refusal. - cl_mode = cl_aborted; - } - break; - case PT_SERVERCFG: // positive response of client join request - { - INT32 j; - UINT8 *scp; - - if (server && serverrunning && node != servernode) - { // but wait I thought I'm the server? - Net_CloseConnection(node); - break; - } - /// \note how would this happen? and is it doing the right thing if it does? - if (cl_mode != cl_waitjoinresponse) - break; - - if (!server) - { - maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic); - gametype = netbuffer->u.servercfg.gametype; - modifiedgame = netbuffer->u.servercfg.modifiedgame; - adminplayer = netbuffer->u.servercfg.adminplayer; - 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) -#ifdef JOININGAME - 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)); - - memset(playeringame, 0, sizeof(playeringame)); - for (j = 0; j < MAXPLAYERS; j++) - { - if (netbuffer->u.servercfg.playerskins[j] == 0xFF - && netbuffer->u.servercfg.playercolor[j] == 0xFF) - continue; // not in game - - playeringame[j] = true; - SetPlayerSkinByNum(j, (INT32)netbuffer->u.servercfg.playerskins[j]); - players[j].skincolor = netbuffer->u.servercfg.playercolor[j]; - } - - scp = netbuffer->u.servercfg.varlengthinputs; - CV_LoadPlayerNames(&scp); - CV_LoadNetVars(&scp); -#ifdef JOININGAME - 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; - } - else - Got_Filetxpak(); - break; - case PT_REQUESTFILE: - if (server) - Got_RequestFilePak(node); - 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; - default: - DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype)); - Net_CloseConnection(node); - break; // ignore it - } // switch - continue; //while - } - if (dedicated && node == 0) netconsole = 0; - else netconsole = nodetoplayer[node]; -#ifdef PARANOIA - if (netconsole >= MAXPLAYERS) - I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole); -#endif - - txtpak = NULL; - - switch (netbuffer->packettype) - { -// -------------------------------------------- SERVER RECEIVE ---------- - case PT_RESYNCHGET: - SV_AcknowledgeResynchAck(netconsole, netbuffer->u.resynchgot); - break; - case PT_CLIENTCMD: - case PT_CLIENT2CMD: - case PT_CLIENTMIS: - case PT_CLIENT2MIS: - case PT_NODEKEEPALIVE: - case PT_NODEKEEPALIVEMIS: - if (!server) - break; - - // ignore tics from those not synched - if (resynch_inprogress[node]) - break; - - // to save bytes, only the low byte of tic numbers are sent - // Figure out what the rest of the bytes are - realstart = ExpandTics(netbuffer->u.clientpak.client_tic); - realend = ExpandTics(netbuffer->u.clientpak.resendfrom); - - 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; - - // 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) - { - XBOXSTATIC char buf[2]; - CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value recieved from node %d\n"), netconsole); - //D_Clearticcmd(k); - - buf[0] = (char)netconsole; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - break; - } - - // splitscreen cmd - if (netbuffer->packettype == PT_CLIENT2CMD && nodetoplayer2[node] >= 0) - G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]], - &netbuffer->u.client2pak.cmd2, 1); - - // a delay before we check resynching - // used on join or just after a synch fail - if (resynch_delay[node]) - { - --resynch_delay[node]; - break; - } - // check player consistancy during the level - if (realstart <= gametic && realstart > gametic - BACKUPTICS+1 && gamestate == GS_LEVEL - && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy)) - { - SV_RequireResynch(node); - - if (cv_resynchattempts.value && resynch_score[node] <= (unsigned)cv_resynchattempts.value*250) - { - 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 - { - XBOXSTATIC UINT8 buf[3]; - - buf[0] = (UINT8)netconsole; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n", - netconsole, realstart, consistancy[realstart%BACKUPTICS], - SHORT(netbuffer->u.clientpak.consistancy))); - break; - } - } - else if (resynch_score[node]) - --resynch_score[node]; - break; - case PT_TEXTCMD2: // splitscreen special - netconsole = nodetoplayer2[node]; - case PT_TEXTCMD: - if (!server) - break; - - if (netconsole < 0 || netconsole >= MAXPLAYERS) - Net_UnAcknowledgPacket(node); - else - { - size_t j; - tic_t tic = maketic; - UINT8 *textcmd; - - // 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_UnAcknowledgPacket(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_NODETIMEOUT: - case PT_CLIENTQUIT: - if (!server) - 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]) - { - XBOXSTATIC UINT8 buf[2]; - buf[0] = (UINT8)netconsole; - if (netbuffer->packettype == PT_NODETIMEOUT) - buf[1] = KICK_MSG_TIMEOUT; - else - buf[1] = KICK_MSG_PLAYER_QUIT; - SendNetXCmd(XD_KICK, &buf, 2); - nodetoplayer[node] = -1; - if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0 - && playeringame[(UINT8)nodetoplayer2[node]]) - { - buf[0] = nodetoplayer2[node]; - SendNetXCmd(XD_KICK, &buf, 2); - nodetoplayer2[node] = -1; - } - } - Net_CloseConnection(node); - nodeingame[node] = false; - break; -// -------------------------------------------- CLIENT RECEIVE ---------- - case PT_RESYNCHEND: - // Only accept PT_RESYNCHEND from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_RESYNCHEND", node); - - if (server) - { - XBOXSTATIC UINT8 buf[2]; - buf[0] = (UINT8)node; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } - - break; - } - resynch_local_inprogress = false; - - P_SetRandSeed(netbuffer->u.resynchend.randomseed); - - if (gametype == GT_CTF) - resynch_read_ctf(&netbuffer->u.resynchend); - resynch_read_others(&netbuffer->u.resynchend); - - break; - case PT_SERVERTICS: - // Only accept PT_SERVERTICS from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_SERVERTICS", node); - - if (server) - { - XBOXSTATIC UINT8 buf[2]; - buf[0] = (UINT8)node; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } - - break; - } - - realstart = ExpandTics(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 + BACKUPTICS) - realend = gametic + BACKUPTICS; - 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; - - M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize); - txtpak += txtsize; - } - } - - neededtic = realend; - } - else - DEBFILE(va("frame not in bound: %u\n", neededtic)); - break; - case PT_RESYNCHING: - // Only accept PT_RESYNCHING from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_RESYNCHING", node); - - if (server) - { - XBOXSTATIC char buf[2]; - buf[0] = (char)node; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } - - break; - } - resynch_local_inprogress = true; - CL_AcknowledgeResynch(&netbuffer->u.resynchpak); - break; -#ifdef NEWPING - case PT_PING: - // Only accept PT_PING from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_PING", node); - - if (server) - { - XBOXSTATIC char buf[2]; - buf[0] = (char)node; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } - - break; - } - - //Update client ping table from the server. - if (!server) - { - INT32 i; - for (i = 0; i < MAXNETNODES; i++) - if (playeringame[i]) - playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i]; - } - - break; -#endif - case PT_SERVERCFG: - break; - case PT_FILEFRAGMENT: - if (!server) - Got_Filetxpak(); - break; - default: - DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n", - netbuffer->packettype, node)); - } // end switch - } // end while + // Packet received from someone already playing + if (nodeingame[node]) + HandlePacketFromPlayer(node); + // Packet received from someone trying to join + else + HandlePacketFromAwayNode(node); + } } // @@ -3768,6 +3934,10 @@ static INT16 Consistancy(void) { INT32 i; UINT32 ret = 0; +#ifdef MOBJCONSISTANCY + thinker_t *th; + mobj_t *mo; +#endif DEBFILE(va("TIC %u ", gametic)); @@ -3789,6 +3959,77 @@ static INT16 Consistancy(void) if (!G_PlatformGametype()) ret += P_GetRandSeed(); +#ifdef MOBJCONSISTANCY + if (!thinkercap.next) + return ret; + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo = (mobj_t *)th; + + if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY)) + { + ret -= mo->type; + ret += mo->x; + ret -= mo->y; + ret += mo->z; + ret -= mo->momx; + ret += mo->momy; + ret -= mo->momz; + ret += mo->angle; + ret -= mo->flags; + ret += mo->flags2; + ret -= mo->eflags; + if (mo->target) + { + ret += mo->target->type; + ret -= mo->target->x; + ret += mo->target->y; + ret -= mo->target->z; + ret += mo->target->momx; + ret -= mo->target->momy; + ret += mo->target->momz; + ret -= mo->target->angle; + ret += mo->target->flags; + ret -= mo->target->flags2; + ret += mo->target->eflags; + ret -= mo->target->state - states; + ret += mo->target->tics; + ret -= mo->target->sprite; + ret += mo->target->frame; + } + else + ret ^= 0x3333; + if (mo->tracer && mo->tracer->type != MT_OVERLAY) + { + ret += mo->tracer->type; + ret -= mo->tracer->x; + ret += mo->tracer->y; + ret -= mo->tracer->z; + ret += mo->tracer->momx; + ret -= mo->tracer->momy; + ret += mo->tracer->momz; + ret -= mo->tracer->angle; + ret += mo->tracer->flags; + ret -= mo->tracer->flags2; + ret += mo->tracer->eflags; + ret -= mo->tracer->state - states; + ret += mo->tracer->tics; + ret -= mo->tracer->sprite; + ret += mo->tracer->frame; + } + else + ret ^= 0xAAAA; + ret -= mo->state - states; + ret += mo->tics; + ret -= mo->sprite; + ret += mo->frame; + } + } +#endif + return (INT16)(ret & 0xFFFF); } @@ -3829,7 +4070,7 @@ static void CL_SendClientCmd(void) HSendPacket(servernode, false, 0, packetsize); } - if (cl_mode == cl_connected || dedicated) + if (cl_mode == CL_CONNECTED || dedicated) { // send extra data if needed if (localtextcmd[0]) @@ -4212,12 +4453,12 @@ FILESTAMP // client send the command after a receive of the server // the server send before because in single player is beter - MasterClient_Ticker(); // acking the master server + MasterClient_Ticker(); // Acking the Master Server if (!server) { if (!resynch_local_inprogress) - CL_SendClientCmd(); // send tic cmd + CL_SendClientCmd(); // Send tic cmd hu_resynching = resynch_local_inprogress; } else @@ -4243,21 +4484,21 @@ FILESTAMP counts = -666; } - // do not make tics while resynching + // Do not make tics while resynching if (counts != -666) { if (maketic + counts >= firstticstosend + BACKUPTICS) counts = firstticstosend+BACKUPTICS-maketic-1; for (i = 0; i < counts; i++) - SV_Maketic(); // create missed tics and increment maketic + SV_Maketic(); // Create missed tics and increment maketic - for (; tictoclear < firstticstosend; tictoclear++) // clear only when acknoledged - D_Clearticcmd(tictoclear); // clear the maketic the new tic + for (; tictoclear < firstticstosend; tictoclear++) // Clear only when acknowledged + D_Clearticcmd(tictoclear); // Clear the maketic the new tic SV_SendTics(); - neededtic = maketic; // the server is a client too + neededtic = maketic; // The server is a client too } else hu_resynching = true; @@ -4271,7 +4512,7 @@ FILESTAMP M_Ticker(); CON_Ticker(); } - FiletxTicker(); + SV_FileSendTicker(); } /** Returns the number of players playing. diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 14b59092..fe80be1b 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -59,7 +59,7 @@ typedef enum // Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility. PT_CANFAIL, // This is kind of a priority. Anything bigger than CANFAIL - // allows HSendPacket(,true,,) to return false. + // allows HSendPacket(*, true, *, *) to return false. // In addition, this packet can't occupy all the available slots. PT_FILEFRAGMENT = PT_CANFAIL, // A part of a file. @@ -76,11 +76,16 @@ typedef enum NUMPACKETTYPE } packettype_t; +#ifdef PACKETDROP +void Command_Drop(void); +void Command_Droprate(void); +#endif + #if defined(_MSC_VER) #pragma pack(1) #endif -// client to server packet +// Client to server packet typedef struct { UINT8 client_tic; @@ -89,7 +94,7 @@ typedef struct ticcmd_t cmd; } ATTRPACK clientcmd_pak; -// splitscreen packet +// Splitscreen packet // WARNING: must have the same format of clientcmd_pak, for more easy use typedef struct { @@ -110,16 +115,16 @@ typedef struct UINT8 starttic; UINT8 numtics; UINT8 numslots; // "Slots filled": Highest player number in use plus one. - ticcmd_t cmds[45]; // normally [BACKUPTIC][MAXPLAYERS] but too large + ticcmd_t cmds[45]; // Normally [BACKUPTIC][MAXPLAYERS] but too large } ATTRPACK servertics_pak; -// sent to client when all consistency data +// Sent to client when all consistency data // for players has been restored typedef struct { UINT32 randomseed; - //ctf flag stuff + // CTF flag stuff SINT8 flagplayer[2]; INT32 flagloose[2]; INT32 flagflags[2]; @@ -127,11 +132,11 @@ typedef struct fixed_t flagy[2]; fixed_t flagz[2]; - UINT32 ingame; // spectator bit for each player - UINT32 ctfteam; // if not spectator, then which team? + UINT32 ingame; // Spectator bit for each player + UINT32 ctfteam; // If not spectator, then which team? // Resynch game scores and the like all at once - UINT32 score[MAXPLAYERS]; // Everyone's score. + UINT32 score[MAXPLAYERS]; // Everyone's score INT16 numboxes[MAXPLAYERS]; INT16 totalring[MAXPLAYERS]; tic_t realtime[MAXPLAYERS]; @@ -140,14 +145,14 @@ typedef struct typedef struct { - //player stuff + // Player stuff UINT8 playernum; // Do not send anything visual related. // Only send data that we need to know for physics. - UINT8 playerstate; //playerstate_t - UINT32 pflags; //pflags_t - UINT8 panim; //panim_t + UINT8 playerstate; // playerstate_t + UINT32 pflags; // pflags_t + UINT8 panim; // panim_t angle_t aiming; INT32 currentweapon; @@ -174,9 +179,9 @@ typedef struct UINT8 charability; UINT8 charability2; UINT32 charflags; - UINT32 thokitem; //mobjtype_t - UINT32 spinitem; //mobjtype_t - UINT32 revitem; //mobjtype_t + UINT32 thokitem; // mobjtype_t + UINT32 spinitem; // mobjtype_t + UINT32 revitem; // mobjtype_t fixed_t actionspd; fixed_t mindash; fixed_t maxdash; @@ -230,7 +235,7 @@ typedef struct INT32 onconveyor; //player->mo stuff - UINT8 hasmo; //boolean + UINT8 hasmo; // Boolean angle_t angle; fixed_t x; @@ -257,10 +262,10 @@ typedef struct typedef struct { - UINT8 version; // different versions don't work - UINT8 subversion; // contains build version + UINT8 version; // Different versions don't work + UINT8 subversion; // Contains build version - // server launch stuffs + // Server launch stuffs UINT8 serverplayer; UINT8 totalslotnum; // "Slots": highest player number in use plus one. @@ -274,18 +279,18 @@ typedef struct UINT8 gametype; UINT8 modifiedgame; - SINT8 adminplayer; // needs to be signed + SINT8 adminplayer; // Needs to be signed - char server_context[8]; // unique context id, generated at server startup. + char server_context[8]; // Unique context id, generated at server startup. - UINT8 varlengthinputs[0]; // playernames and netvars + UINT8 varlengthinputs[0]; // Playernames and netvars } ATTRPACK serverconfig_pak; typedef struct { UINT8 fileid; UINT32 position; UINT16 size; - UINT8 data[0]; // size is variable using hardware_MAXPACKETLENGTH + UINT8 data[0]; // Size is variable using hardware_MAXPACKETLENGTH } ATTRPACK filetx_pak; #ifdef _MSC_VER @@ -294,14 +299,14 @@ typedef struct { typedef struct { - UINT8 version; // different versions don't work - UINT8 subversion; // contains build version + UINT8 version; // Different versions don't work + UINT8 subversion; // Contains build version UINT8 localplayers; UINT8 mode; } ATTRPACK clientconfig_pak; #define MAXSERVERNAME 32 -// this packet is too large +// This packet is too large typedef struct { UINT8 version; @@ -367,45 +372,45 @@ typedef struct } ATTRPACK plrconfig; // -// Network packet data. +// Network packet data // typedef struct { UINT32 checksum; - UINT8 ack; // if not null the node asks for acknowledgement, the receiver must resend the ack - UINT8 ackreturn; // the return of the ack number + UINT8 ack; // If not zero the node asks for acknowledgement, the receiver must resend the ack + UINT8 ackreturn; // The return of the ack number UINT8 packettype; - UINT8 reserved; // padding + UINT8 reserved; // Padding union { - clientcmd_pak clientpak; // 144 bytes - client2cmd_pak client2pak; // 200 bytes - servertics_pak serverpak; // 132495 bytes - serverconfig_pak servercfg; // 773 bytes - resynchend_pak resynchend; // - resynch_pak resynchpak; // - UINT8 resynchgot; // - UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes - filetx_pak filetxpak; // 139 bytes - clientconfig_pak clientcfg; // 136 bytes - serverinfo_pak serverinfo; // 1024 bytes - serverrefuse_pak serverrefuse; // 65025 bytes - askinfo_pak askinfo; // 61 bytes - msaskinfo_pak msaskinfo; // 22 bytes - plrinfo playerinfo[MAXPLAYERS]; // 1152 bytes - plrconfig playerconfig[MAXPLAYERS]; // (up to) 896 bytes + clientcmd_pak clientpak; // 144 bytes + client2cmd_pak client2pak; // 200 bytes + servertics_pak serverpak; // 132495 bytes (more around 360, no?) + serverconfig_pak servercfg; // 773 bytes + resynchend_pak resynchend; // + resynch_pak resynchpak; // + UINT8 resynchgot; // + UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...) + filetx_pak filetxpak; // 139 bytes + clientconfig_pak clientcfg; // 136 bytes + serverinfo_pak serverinfo; // 1024 bytes + serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...) + askinfo_pak askinfo; // 61 bytes + msaskinfo_pak msaskinfo; // 22 bytes + plrinfo playerinfo[MAXPLAYERS]; // 1152 bytes (I'd say 36~38) + plrconfig playerconfig[MAXPLAYERS]; // (up to) 896 bytes (welp they ARE) #ifdef NEWPING - UINT32 pingtable[MAXPLAYERS]; // 128 bytes + UINT32 pingtable[MAXPLAYERS]; // 128 bytes #endif - } u; // this is needed to pack diff packet types data together + } u; // This is needed to pack diff packet types data together } ATTRPACK doomdata_t; #if defined(_MSC_VER) #pragma pack() #endif -#define MAXSERVERLIST 64 // depends only on the display +#define MAXSERVERLIST 64 // Depends only on the display typedef struct { SINT8 node; @@ -416,7 +421,7 @@ extern serverelem_t serverlist[MAXSERVERLIST]; extern UINT32 serverlistcount; extern INT32 mapchangepending; -// points inside doomcom +// Points inside doomcom extern doomdata_t *netbuffer; extern consvar_t cv_playbackspeed; @@ -437,7 +442,7 @@ extern consvar_t cv_playbackspeed; #define KICK_MSG_CUSTOM_BAN 8 extern boolean server; -extern boolean dedicated; // for dedicated server +extern boolean dedicated; // For dedicated server extern UINT16 software_MAXPACKETLENGTH; extern boolean acceptnewnode; extern SINT8 servernode; @@ -452,11 +457,11 @@ extern UINT32 playerpingtable[MAXPLAYERS]; extern consvar_t cv_joinnextround, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend; -// used in d_net, the only dependence +// Used in d_net, the only dependence tic_t ExpandTics(INT32 low); void D_ClientServerInit(void); -// initialise the other field +// Initialise the other field void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum)); void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam); void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam); // splitsreen player @@ -474,14 +479,14 @@ void CL_RemoveSplitscreenPlayer(void); void CL_Reset(void); void CL_ClearPlayer(INT32 playernum); void CL_UpdateServerList(boolean internetsearch, INT32 room); -// is there a game running +// Is there a game running boolean Playing(void); // Broadcasts special packets to other players // to notify of game exit void D_QuitNetGame(void); -//? how many ticks to run? +//? How many ticks to run? void TryRunTics(tic_t realtic); // extra data for lmps diff --git a/src/d_net.c b/src/d_net.c index 03e126b5..6be1dbe5 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -31,15 +31,15 @@ // // NETWORKING // -// gametic is the tic about to be (or currently being) run -// server: +// gametic is the tic about to (or currently being) run +// Server: // maketic is the tic that hasn't had control made for it yet -// nettics: is the tic for each node -// firsttictosend: is the lowest value of nettics -// client: -// neededtic: is the tic needed by the client to run the game -// firsttictosend: is used to optimize a condition -// normally maketic >= gametic > 0 +// nettics is the tic for each node +// firstticstosend is the lowest value of nettics +// Client: +// neededtic is the tic needed by the client to run the game +// firstticstosend is used to optimize a condition +// Normally maketic >= gametic > 0 #define FORCECLOSE 0x8000 tic_t connectiontimeout = (15*TICRATE); @@ -129,9 +129,9 @@ boolean Net_GetNetStat(void) // ----------------------------------------------------------------- // Some structs and functions for acknowledgement of packets // ----------------------------------------------------------------- -#define MAXACKPACKETS 96 // minimum number of nodes +#define MAXACKPACKETS 96 // Minimum number of nodes (wat) #define MAXACKTOSEND 96 -#define URGENTFREESLOTENUM 10 +#define URGENTFREESLOTNUM 10 #define ACKTOSENDTIMEOUT (TICRATE/11) #ifndef NONET @@ -139,10 +139,10 @@ typedef struct { UINT8 acknum; UINT8 nextacknum; - UINT8 destinationnode; - tic_t senttime; - UINT16 length; - UINT16 resentnum; + UINT8 destinationnode; // The node to send the ack to + tic_t senttime; // The time when the ack was sent + UINT16 length; // The packet size + UINT16 resentnum; // The number of union { SINT8 raw[MAXPACKETLENGTH]; doomdata_t data; @@ -212,11 +212,16 @@ FUNCMATH static INT32 cmpack(UINT8 a, UINT8 b) return d; } -// return a free acknum and copy netbuffer in the ackpak table +/** Sets freeack to a free acknum and copies the netbuffer in the ackpak table + * + * \param freeack The address to store the free acknum at + * \param lowtimer ??? + * \return True if a free acknum was found + */ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) { node_t *node = &nodes[doomcom->remotenode]; - INT32 i, numfreeslote = 0; + INT32 i, numfreeslot = 0; if (cmpack((UINT8)((node->remotefirstack + MAXACKTOSEND) % 256), node->nextacknum) < 0) { @@ -227,10 +232,13 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) for (i = 0; i < MAXACKPACKETS; i++) if (!ackpak[i].acknum) { - // for low priority packet, make sure let freeslotes so urgents packets can be sent - numfreeslote++; - if (netbuffer->packettype >= PT_CANFAIL && numfreeslote < URGENTFREESLOTENUM) - continue; + // For low priority packets, make sure to let freeslots so urgent packets can be sent + if (netbuffer->packettype >= PT_CANFAIL) + { + numfreeslot++; + if (numfreeslot <= URGENTFREESLOTNUM) + continue; + } ackpak[i].acknum = node->nextacknum; ackpak[i].nextacknum = node->nextacknum; @@ -241,7 +249,7 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) ackpak[i].length = doomcom->datalength; if (lowtimer) { - // lowtime mean can't be sent now so try it soon as possible + // Lowtime means can't be sent now so try it as soon as possible ackpak[i].senttime = 0; ackpak[i].resentnum = 1; } @@ -254,7 +262,7 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) *freeack = ackpak[i].acknum; - sendackpacket++; // for stat + sendackpacket++; // For stat return true; } @@ -266,14 +274,14 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) return false; } -// Get a ack to send in the queu of this node +// Get a ack to send in the queue of this node static UINT8 GetAcktosend(INT32 node) { nodes[node].lasttimeacktosend_sent = I_GetTime(); return nodes[node].firstacktosend; } -static void Removeack(INT32 i) +static void RemoveAck(INT32 i) { INT32 node = ackpak[i].destinationnode; #ifndef NEWPING @@ -294,27 +302,27 @@ static void Removeack(INT32 i) Net_CloseConnection(node); } -// we have got a packet proceed the ack request and ack return +// We have got a packet, proceed the ack request and ack return static boolean Processackpak(void) { INT32 i; boolean goodpacket = true; node_t *node = &nodes[doomcom->remotenode]; - // received an ack return, so remove the ack in the list + // Received an ack return, so remove the ack in the list if (netbuffer->ackreturn && cmpack(node->remotefirstack, netbuffer->ackreturn) < 0) { node->remotefirstack = netbuffer->ackreturn; - // search the ackbuffer and free it + // Search the ackbuffer and free it for (i = 0; i < MAXACKPACKETS; i++) if (ackpak[i].acknum && ackpak[i].destinationnode == node - nodes && cmpack(ackpak[i].acknum, netbuffer->ackreturn) <= 0) { - Removeack(i); + RemoveAck(i); } } - // received a packet with ack, queue it to send the ack back + // Received a packet with ack, queue it to send the ack back if (netbuffer->ack) { UINT8 ack = netbuffer->ack; @@ -323,23 +331,23 @@ static boolean Processackpak(void) { DEBFILE(va("Discard(1) ack %d (duplicated)\n", ack)); duppacket++; - goodpacket = false; // discard packet (duplicate) + goodpacket = false; // Discard packet (duplicate) } else { - // check if it is not already in the queue + // Check if it is not already in the queue for (i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND) if (node->acktosend[i] == ack) { DEBFILE(va("Discard(2) ack %d (duplicated)\n", ack)); duppacket++; - goodpacket = false; // discard packet (duplicate) + goodpacket = false; // Discard packet (duplicate) break; } if (goodpacket) { - // is a good packet so increment the acknowledge number, - // then search for a "hole" in the queue + // Is a good packet so increment the acknowledge number, + // Then search for a "hole" in the queue UINT8 nextfirstack = (UINT8)(node->firstacktosend + 1); if (!nextfirstack) nextfirstack = 1; @@ -383,10 +391,10 @@ static boolean Processackpak(void) } } } - else // out of order packet + else // Out of order packet { - // don't increment firsacktosend, put it in asktosend queue - // will be incremented when the nextfirstack comes (code above) + // Don't increment firsacktosend, put it in asktosend queue + // Will be incremented when the nextfirstack comes (code above) UINT8 newhead = (UINT8)((node->acktosend_head+1) % MAXACKTOSEND); DEBFILE(va("out of order packet (%d expected)\n", nextfirstack)); if (newhead != node->acktosend_tail) @@ -394,8 +402,8 @@ static boolean Processackpak(void) node->acktosend[node->acktosend_head] = ack; node->acktosend_head = newhead; } - else // buffer full discard packet, sender will resend it - { // we can admit the packet but we will not detect the duplication after :( + else // Buffer full discard packet, sender will resend it + { // We can admit the packet but we will not detect the duplication after :( DEBFILE("no more freeackret\n"); goodpacket = false; } @@ -430,25 +438,24 @@ static void GotAcks(void) if (ackpak[i].acknum && ackpak[i].destinationnode == doomcom->remotenode) { if (ackpak[i].acknum == netbuffer->u.textcmd[j]) - Removeack(i); - else - // nextacknum is first equal to acknum, then when receiving bigger ack - // there is big chance the packet is lost - // when resent, nextacknum = nodes[node].nextacknum - // will redo the same but with different value - if (cmpack(ackpak[i].nextacknum, netbuffer->u.textcmd[j]) <= 0 - && ackpak[i].senttime > 0) - { - ackpak[i].senttime--; // hurry up - } + RemoveAck(i); + // nextacknum is first equal to acknum, then when receiving bigger ack + // there is big chance the packet is lost + // When resent, nextacknum = nodes[node].nextacknum + // will redo the same but with different value + else if (cmpack(ackpak[i].nextacknum, netbuffer->u.textcmd[j]) <= 0 + && ackpak[i].senttime > 0) + { + ackpak[i].senttime--; // hurry up + } } } #endif static inline void Net_ConnectionTimeout(INT32 node) { - // send a very special packet to self (hack the reboundstore queue) - // main code will handle it + // Send a very special packet to self (hack the reboundstore queue) + // Main code will handle it reboundstore[rebound_head].packettype = PT_NODETIMEOUT; reboundstore[rebound_head].ack = 0; reboundstore[rebound_head].ackreturn = 0; @@ -456,12 +463,12 @@ static inline void Net_ConnectionTimeout(INT32 node) reboundsize[rebound_head] = (INT16)(BASEPACKETSIZE + 1); rebound_head = (rebound_head+1) % MAXREBOUND; - // do not redo it quickly (if we do not close connection it is + // Do not redo it quickly (if we do not close connection it is // for a good reason!) nodes[node].lasttimepacketreceived = I_GetTime(); } -// resend the data if needed +// Resend the data if needed void Net_AckTicker(void) { #ifndef NONET @@ -497,7 +504,7 @@ void Net_AckTicker(void) ackpak[i].senttime = I_GetTime(); ackpak[i].resentnum++; ackpak[i].nextacknum = node->nextacknum; - retransmit++; // for stat + retransmit++; // For stat HSendPacket((INT32)(node - nodes), false, ackpak[i].acknum, (size_t)(ackpak[i].length - BASEPACKETSIZE)); } @@ -505,11 +512,11 @@ void Net_AckTicker(void) for (i = 1; i < MAXNETNODES; i++) { - // this is something like node open flag + // This is something like node open flag if (nodes[i].firstacktosend) { - // we haven't sent a packet for a long time - // acknowledge packet if needed + // We haven't sent a packet for a long time + // Acknowledge packet if needed if (nodes[i].lasttimeacktosend_sent + ACKTOSENDTIMEOUT < I_GetTime()) Net_SendAcks(i); @@ -523,9 +530,9 @@ void Net_AckTicker(void) #endif } -// remove last packet received ack before resending the ackret +// Remove last packet received ack before resending the ackreturn // (the higher layer doesn't have room, or something else ....) -void Net_UnAcknowledgPacket(INT32 node) +void Net_UnAcknowledgePacket(INT32 node) { #ifdef NONET (void)node; @@ -564,20 +571,29 @@ void Net_UnAcknowledgPacket(INT32 node) #endif } -boolean Net_AllAckReceived(void) -{ #ifndef NONET +/** Checks if all acks have been received + * + * \return True if all acks have been received + * + */ +static boolean Net_AllAcksReceived(void) +{ INT32 i; for (i = 0; i < MAXACKPACKETS; i++) if (ackpak[i].acknum) return false; -#endif return true; } +#endif -// wait for all ackreturns with timeout in seconds +/** Waits for all ackreturns + * + * \param timeout Timeout in seconds + * + */ void Net_WaitAllAckReceived(UINT32 timeout) { #ifdef NONET @@ -587,7 +603,7 @@ void Net_WaitAllAckReceived(UINT32 timeout) timeout = tictac + timeout*NEWTICRATE; HGetPacket(); - while (timeout > I_GetTime() && !Net_AllAckReceived()) + while (timeout > I_GetTime() && !Net_AllAcksReceived()) { while (tictac == I_GetTime()) I_Sleep(); @@ -598,18 +614,18 @@ void Net_WaitAllAckReceived(UINT32 timeout) #endif } -static void InitNode(INT32 node) +static void InitNode(node_t *node) { - nodes[node].acktosend_head = nodes[node].acktosend_tail = 0; + node->acktosend_head = node->acktosend_tail = 0; #ifndef NEWPING - nodes[node].ping = PINGDEFAULT; - nodes[node].varping = VARPINGDEFAULT; - nodes[node].timeout = TIMEOUT(nodes[node].ping,nodes[node].varping); + node->ping = PINGDEFAULT; + node->varping = VARPINGDEFAULT; + node->timeout = TIMEOUT(node->ping, node->varping); #endif - nodes[node].firstacktosend = 0; - nodes[node].nextacknum = 1; - nodes[node].remotefirstack = 0; - nodes[node].flags = 0; + node->firstacktosend = 0; + node->nextacknum = 1; + node->remotefirstack = 0; + node->flags = 0; } static void InitAck(void) @@ -622,9 +638,14 @@ static void InitAck(void) #endif for (i = 0; i < MAXNETNODES; i++) - InitNode(i); + InitNode(&nodes[i]); } +/** Removes all acks of a given packet type + * + * \param packettype The packet type to forget + * + */ void Net_AbortPacketType(UINT8 packettype) { #ifdef NONET @@ -676,8 +697,8 @@ void Net_CloseConnection(INT32 node) ackpak[i].acknum = 0; } - InitNode(node); - AbortSendFiles(node); + InitNode(&nodes[node]); + SV_AbortSendFiles(node); I_NetFreeNodenum(node); #endif } @@ -729,9 +750,15 @@ static void fprintfstring(char *s, size_t len) } if (mode) fprintf(debugfile, "]"); +} + +static void fprintfstringnewline(char *s, size_t len) +{ + fprintfstring(s, len); fprintf(debugfile, "\n"); } +/// \warning Keep this up-to-date if you add/remove/rename packet types static const char *packettypename[NUMPACKETTYPE] = { "NOTHING", @@ -749,15 +776,22 @@ static const char *packettypename[NUMPACKETTYPE] = "ASKINFO", "SERVERINFO", + "PLAYERINFO", "REQUESTFILE", "ASKINFOVIAMS", - "PLAYERCONFIGS", + "RESYNCHEND", + "RESYNCHGET", + "FILEFRAGMENT", "TEXTCMD", "TEXTCMD2", "CLIENTJOIN", "NODETIMEOUT", + "RESYNCHING", +#ifdef NEWPING + "PING" +#endif }; static void DebugPrintpacket(const char *header) @@ -770,20 +804,29 @@ static void DebugPrintpacket(const char *header) { case PT_ASKINFO: case PT_ASKINFOVIAMS: - fprintf(debugfile, " time %u\n", (tic_t)LONG(netbuffer->u.askinfo.time) ); + fprintf(debugfile, " time %u\n", (tic_t)LONG(netbuffer->u.askinfo.time)); break; case PT_CLIENTJOIN: fprintf(debugfile, " number %d mode %d\n", netbuffer->u.clientcfg.localplayers, netbuffer->u.clientcfg.mode); break; case PT_SERVERTICS: + { + servertics_pak *serverpak = &netbuffer->u.serverpak; + ticcmd_t *cmd = &serverpak->cmds[serverpak->numslots * serverpak->numtics]; + size_t ntxtcmd = &((UINT8 *)netbuffer)[doomcom->datalength] - (UINT8 *)cmd; + fprintf(debugfile, " firsttic %u ply %d tics %d ntxtcmd %s\n ", - (UINT32)ExpandTics(netbuffer->u.serverpak.starttic), netbuffer->u.serverpak.numslots, - netbuffer->u.serverpak.numtics, - sizeu1((size_t)(&((UINT8 *)netbuffer)[doomcom->datalength] - (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics]))); - fprintfstring((char *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics],(size_t)( - &((UINT8 *)netbuffer)[doomcom->datalength] - (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics])); + (UINT32)ExpandTics(serverpak->starttic), serverpak->numslots, serverpak->numtics, sizeu1(ntxtcmd)); + fprintfstring((char *)cmd, 3); + if (ntxtcmd > 4) + { + fprintf(debugfile, "[%s]", netxcmdnames[*(((UINT8 *)cmd) + 3) - 1]); + fprintfstring(((char *)cmd) + 4, ntxtcmd - 4); + } + fprintf(debugfile, "\n"); break; + } case PT_CLIENTCMD: case PT_CLIENT2CMD: case PT_CLIENTMIS: @@ -797,7 +840,8 @@ static void DebugPrintpacket(const char *header) case PT_TEXTCMD: case PT_TEXTCMD2: fprintf(debugfile, " length %d\n ", netbuffer->u.textcmd[0]); - fprintfstring((char *)netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]); + fprintf(debugfile, "[%s]", netxcmdnames[netbuffer->u.textcmd[1] - 1]); + fprintfstringnewline((char *)netbuffer->u.textcmd + 2, netbuffer->u.textcmd[0] - 1); break; case PT_SERVERCFG: fprintf(debugfile, " playerslots %d clientnode %d serverplayer %d " @@ -813,7 +857,7 @@ static void DebugPrintpacket(const char *header) netbuffer->u.serverinfo.maxplayer, netbuffer->u.serverinfo.mapname, netbuffer->u.serverinfo.fileneedednum, (UINT32)LONG(netbuffer->u.serverinfo.time)); - fprintfstring((char *)netbuffer->u.serverinfo.fileneeded, + fprintfstringnewline((char *)netbuffer->u.serverinfo.fileneeded, (UINT8)((UINT8 *)netbuffer + doomcom->datalength - (UINT8 *)netbuffer->u.serverinfo.fileneeded)); break; @@ -827,20 +871,100 @@ static void DebugPrintpacket(const char *header) break; case PT_REQUESTFILE: default: // write as a raw packet - fprintfstring((char *)netbuffer->u.textcmd, + fprintfstringnewline((char *)netbuffer->u.textcmd, (UINT8)((UINT8 *)netbuffer + doomcom->datalength - (UINT8 *)netbuffer->u.textcmd)); break; } } #endif +#ifdef PACKETDROP +static INT32 packetdropquantity[NUMPACKETTYPE] = {0}; +static INT32 packetdroprate = 0; + +void Command_Drop(void) +{ + INT32 packetquantity; + const char *packetname; + size_t i; + + if (COM_Argc() < 2) + { + CONS_Printf("drop [quantity]: drop packets\n" + "drop reset: cancel all packet drops"); + return; + } + + if (!(stricmp(COM_Argv(1), "reset") && stricmp(COM_Argv(1), "cancel") && stricmp(COM_Argv(1), "stop"))) + { + memset(packetdropquantity, 0, sizeof(packetdropquantity)); + return; + } + + if (COM_Argc() >= 3) + { + packetquantity = atoi(COM_Argv(2)); + if (packetquantity <= 0 && COM_Argv(2)[0] != '0') + { + CONS_Printf("Invalid quantity\n"); + return; + } + } + else + packetquantity = -1; + + packetname = COM_Argv(1); + + if (!(stricmp(packetname, "all") && stricmp(packetname, "any"))) + for (i = 0; i < NUMPACKETTYPE; i++) + packetdropquantity[i] = packetquantity; + else + { + for (i = 0; i < NUMPACKETTYPE; i++) + if (!stricmp(packetname, packettypename[i])) + { + packetdropquantity[i] = packetquantity; + return; + } + + CONS_Printf("Unknown packet name\n"); + } +} + +void Command_Droprate(void) +{ + INT32 droprate; + + if (COM_Argc() < 2) + { + CONS_Printf("Packet drop rate: %d%%\n", packetdroprate); + return; + } + + droprate = atoi(COM_Argv(1)); + if ((droprate <= 0 && COM_Argv(1)[0] != '0') || droprate > 100) + { + CONS_Printf("Packet drop rate must be between 0 and 100!\n"); + return; + } + + packetdroprate = droprate; +} + +static boolean ShouldDropPacket(void) +{ + return (packetdropquantity[netbuffer->packettype]) + || (packetdroprate != 0 && rand() < (RAND_MAX * (packetdroprate / 100.f))) || packetdroprate == 100; +} +#endif + // // HSendPacket // boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlength) { doomcom->datalength = (INT16)(packetlength + BASEPACKETSIZE); - if (node == 0) // packet is to go back to us + if (node == 0) // Packet is to go back to us { if ((rebound_head+1) % MAXREBOUND == rebound_tail) { @@ -871,7 +995,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen (void)reliable; (void)acknum; #else - // do this before GetFreeAcknum because this function backup + // do this before GetFreeAcknum because this function backups // the current packet doomcom->remotenode = (INT16)node; if (doomcom->datalength <= 0) @@ -884,7 +1008,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen return false; } - if (node < MAXNETNODES) // can be a broadcast + if (node < MAXNETNODES) // Can be a broadcast netbuffer->ackreturn = GetAcktosend(node); else netbuffer->ackreturn = 0; @@ -905,20 +1029,30 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen netbuffer->ack = acknum; netbuffer->checksum = NetbufferChecksum(); - sendbytes += packetheaderlength + doomcom->datalength; // for stat + sendbytes += packetheaderlength + doomcom->datalength; // For stat - // simulate internet :) - if (true || rand()<(INT32)RAND_MAX/5) +#ifdef PACKETDROP + // Simulate internet :) + //if (rand() >= (INT32)(RAND_MAX * (PACKETLOSSRATE / 100.f))) + if (!ShouldDropPacket()) { +#endif #ifdef DEBUGFILE if (debugfile) - DebugPrintpacket("SEND"); + DebugPrintpacket("SENT"); #endif I_NetSend(); +#ifdef PACKETDROP } + else + { + if (packetdropquantity[netbuffer->packettype] > 0) + packetdropquantity[netbuffer->packettype]--; #ifdef DEBUGFILE - else if (debugfile) - DebugPrintpacket("NOTSEND"); + if (debugfile) + DebugPrintpacket("NOT SENT"); +#endif + } #endif #endif // ndef NONET @@ -933,7 +1067,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen // boolean HGetPacket(void) { - // get a packet from self + // Get a packet from self if (rebound_tail != rebound_head) { M_Memcpy(netbuffer, &reboundstore[rebound_tail], reboundsize[rebound_tail]); @@ -963,11 +1097,11 @@ boolean HGetPacket(void) if (doomcom->remotenode == -1) return false; - getbytes += packetheaderlength + doomcom->datalength; // for stat + getbytes += packetheaderlength + doomcom->datalength; // For stat if (doomcom->remotenode >= MAXNETNODES) { - DEBFILE(va("receive packet from node %d !\n", doomcom->remotenode)); + DEBFILE(va("Received packet from node %d!\n", doomcom->remotenode)); continue; } @@ -1230,4 +1364,6 @@ void D_CloseConnection(void) netgame = false; addedtogame = false; } + + D_ResetTiccmds(); } diff --git a/src/d_net.h b/src/d_net.h index 285b4423..190e07a6 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -18,10 +18,10 @@ #ifndef __D_NET__ #define __D_NET__ -// Max computers in a game. +// Max computers in a game #define MAXNETNODES 32 #define BROADCASTADDR MAXNETNODES -#define MAXSPLITSCREENPLAYERS 2 // max number of players on a single computer +#define MAXSPLITSCREENPLAYERS 2 // Max number of players on a single computer #define STATLENGTH (TICRATE*2) @@ -32,17 +32,16 @@ extern float lostpercent, duppercent, gamelostpercent; extern INT32 packetheaderlength; boolean Net_GetNetStat(void); extern INT32 getbytes; -extern INT64 sendbytes; // realtime updated +extern INT64 sendbytes; // Realtime updated extern SINT8 nodetoplayer[MAXNETNODES]; -extern SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen) -extern UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen -extern boolean nodeingame[MAXNETNODES]; // set false as nodes leave game +extern SINT8 nodetoplayer2[MAXNETNODES]; // Say the numplayer for this node if any (splitscreen) +extern UINT8 playerpernode[MAXNETNODES]; // Used specially for splitscreen +extern boolean nodeingame[MAXNETNODES]; // Set false as nodes leave game void Net_AckTicker(void); -boolean Net_AllAckReceived(void); -// if reliable return true if packet sent, 0 else +// If reliable return true if packet sent, 0 else boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlength); boolean HGetPacket(void); @@ -52,9 +51,10 @@ void D_SaveBan(void); #endif boolean D_CheckNetGame(void); void D_CloseConnection(void); -void Net_UnAcknowledgPacket(INT32 node); +void Net_UnAcknowledgePacket(INT32 node); void Net_CloseConnection(INT32 node); void Net_AbortPacketType(UINT8 packettype); void Net_SendAcks(INT32 node); void Net_WaitAllAckReceived(UINT32 timeout); + #endif diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 4f73a256..d70805e1 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -365,6 +365,35 @@ boolean splitscreen = false; boolean circuitmap = false; INT32 adminplayer = -1; +/// \warning Keep this up-to-date if you add/remove/rename net text commands +const char *netxcmdnames[MAXNETXCMD - 1] = +{ + "NAMEANDCOLOR", + "WEAPONPREF", + "KICK", + "NETVAR", + "SAY", + "MAP", + "EXITLEVEL", + "ADDFILE", + "PAUSE", + "ADDPLAYER", + "TEAMCHANGE", + "CLEARSCORES", + "LOGIN", + "VERIFIED", + "RANDOMSEED", + "RUNSOC", + "REQADDFILE", + "DELFILE", + "SETMOTD", + "SUICIDE", +#ifdef HAVE_BLUA + "LUACMD", + "LUAVAR" +#endif +}; + // ========================================================================= // SERVER STARTUP // ========================================================================= diff --git a/src/d_netcmd.h b/src/d_netcmd.h index c090699f..d8fae72f 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -162,6 +162,8 @@ typedef enum MAXNETXCMD } netxcmd_t; +extern const char *netxcmdnames[MAXNETXCMD - 1]; + #if defined(_MSC_VER) #pragma pack(1) #endif diff --git a/src/d_netfil.c b/src/d_netfil.c index 85196217..4e3d70fa 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -62,34 +62,37 @@ #include -static void SendFile(INT32 node, const char *filename, UINT8 fileid); +static void SV_SendFile(INT32 node, const char *filename, UINT8 fileid); -// sender structure +// Sender structure typedef struct filetx_s { INT32 ram; - char *filename; // name of the file or ptr of the data in ram - UINT32 size; + union { + char *filename; // Name of the file + char *ram; // Pointer to the data in RAM + } id; + UINT32 size; // Size of the file UINT8 fileid; - INT32 node; // destination - struct filetx_s *next; // a queue + INT32 node; // Destination + struct filetx_s *next; // Next file in the list } filetx_t; -// current transfers (one for each node) +// Current transfers (one for each node) typedef struct filetran_s { - filetx_t *txlist; - UINT32 position; - FILE *currentfile; + filetx_t *txlist; // Linked list of all files for the node + UINT32 position; // The current position in the file + FILE *currentfile; // The file currently being sent/received } filetran_t; static filetran_t transfer[MAXNETNODES]; -// read time of file: stat _stmtime -// write time of file: utime +// Read time of file: stat _stmtime +// Write time of file: utime -// receiver structure -INT32 fileneedednum; -fileneeded_t fileneeded[MAX_WADFILES]; +// Receiver structure +INT32 fileneedednum; // Number of files needed to join the server +fileneeded_t fileneeded[MAX_WADFILES]; // List of needed files char downloaddir[256] = "DOWNLOAD"; #ifdef CLIENT_LOADINGSCREEN @@ -100,6 +103,7 @@ INT32 lastfilenum = 0; /** Fills a serverinfo packet with information about wad files loaded. * * \todo Give this function a better name since it is in global scope. + * */ UINT8 *PutFileNeeded(void) { @@ -111,19 +115,19 @@ UINT8 *PutFileNeeded(void) for (i = 0; i < numwadfiles; i++) { - // if it has only music/sound lumps, mark it as unimportant + // If it has only music/sound lumps, mark it as unimportant if (W_VerifyNMUSlumps(wadfiles[i]->filename)) filestatus = 0; else - filestatus = 1; // important + filestatus = 1; // Important // Store in the upper four bits if (!cv_downloading.value) - filestatus += (2 << 4); // won't send + filestatus += (2 << 4); // Won't send else if ((wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024)) - filestatus += (0 << 4); // won't send + filestatus += (0 << 4); // Won't send else - filestatus += (1 << 4); // will send if requested + filestatus += (1 << 4); // Will send if requested bytesused += (nameonlylength(wadfilename) + 22); @@ -144,7 +148,12 @@ UINT8 *PutFileNeeded(void) return p; } -// parse the serverinfo packet and fill fileneeded table on client +/** Parses the serverinfo packet and fills the fileneeded table on client + * + * \param fileneedednum_parm The number of files needed to join the server + * \param fileneededstr The memory block containing the list of needed files + * + */ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr) { INT32 i; @@ -155,14 +164,14 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr) p = (UINT8 *)fileneededstr; for (i = 0; i < fileneedednum; i++) { - fileneeded[i].status = FS_NOTFOUND; - filestatus = READUINT8(p); + fileneeded[i].status = FS_NOTFOUND; // We haven't even started looking for the file yet + filestatus = READUINT8(p); // The first byte is the file status fileneeded[i].important = (UINT8)(filestatus & 3); fileneeded[i].willsend = (UINT8)(filestatus >> 4); - fileneeded[i].totalsize = READUINT32(p); - fileneeded[i].phandle = NULL; - READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH); - READMEM(p, fileneeded[i].md5sum, 16); + fileneeded[i].totalsize = READUINT32(p); // The four next bytes are the file size + fileneeded[i].file = NULL; // The file isn't open yet + READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH); // The next bytes are the file name + READMEM(p, fileneeded[i].md5sum, 16); // The last 16 bytes are the file checksum } } @@ -171,13 +180,16 @@ void CL_PrepareDownloadSaveGame(const char *tmpsave) fileneedednum = 1; fileneeded[0].status = FS_REQUESTED; fileneeded[0].totalsize = UINT32_MAX; - fileneeded[0].phandle = NULL; + fileneeded[0].file = NULL; memset(fileneeded[0].md5sum, 0, 16); strcpy(fileneeded[0].filename, tmpsave); } /** Checks the server to see if we CAN download all the files, * before starting to create them and requesting. + * + * \return True if we can download all the files + * */ boolean CL_CheckDownloadable(void) { @@ -239,8 +251,12 @@ boolean CL_CheckDownloadable(void) return false; } -/** Send requests for files in the ::fileneeded table with a status of +/** Sends requests for files in the ::fileneeded table with a status of * ::FS_NOTFOUND. + * + * \return True if the packet was successfully sent + * \note Sends a PT_REQUESTFILE packet + * */ boolean CL_SendRequestFile(void) { @@ -298,11 +314,17 @@ void Got_RequestFilePak(INT32 node) if (id == 0xFF) break; READSTRINGN(p, wad, MAX_WADPATH); - SendFile(node, wad, id); + SV_SendFile(node, wad, id); } } -// client check if the fileneeded aren't already loaded or on the disk +/** Checks if the files needed aren't already loaded or on the disk + * + * \return 0 if some files are missing + * 1 if all files exist + * 2 if some already loaded files are not requested or are in a different order + * + */ INT32 CL_CheckFiles(void) { INT32 i, j; @@ -333,7 +355,7 @@ INT32 CL_CheckFiles(void) } if (j < numwadfiles && W_VerifyNMUSlumps(wadfiles[j]->filename)) { - // unimportant on our side. still don't care. + // Unimportant on our side. still don't care. ++j; continue; } @@ -343,11 +365,11 @@ INT32 CL_CheckFiles(void) if (i >= fileneedednum || j >= numwadfiles) return 2; - // for the sake of speed, only bother with a md5 check + // For the sake of speed, only bother with a md5 check if (memcmp(wadfiles[j]->md5sum, fileneeded[i].md5sum, 16)) return 2; - // it's accounted for! let's keep going. + // It's accounted for! let's keep going. CONS_Debug(DBG_NETPLAY, "'%s' accounted for\n", fileneeded[i].filename); fileneeded[i].status = FS_OPEN; ++i; @@ -360,7 +382,7 @@ INT32 CL_CheckFiles(void) { CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename); - // check in allready loaded files + // Check in already loaded files for (j = 1; wadfiles[j]; j++) { nameonly(strcpy(wadfilename, wadfiles[j]->filename)); @@ -383,7 +405,7 @@ INT32 CL_CheckFiles(void) return ret; } -// load it now +// Load it now void CL_LoadServerFiles(void) { INT32 i; @@ -394,7 +416,7 @@ void CL_LoadServerFiles(void) for (i = 1; i < fileneedednum; i++) { if (fileneeded[i].status == FS_OPEN) - continue; // already loaded + continue; // Already loaded else if (fileneeded[i].status == FS_FOUND) { P_AddWadFile(fileneeded[i].filename, NULL); @@ -423,133 +445,200 @@ void CL_LoadServerFiles(void) DEBFILE(va("File %s found but with different md5sum\n", fileneeded[i].filename)); } else if (fileneeded[i].important) - I_Error("Try to load file %s with status of %d\n", fileneeded[i].filename, - fileneeded[i].status); + { + const char *s; + switch(fileneeded[i].status) + { + case FS_NOTFOUND: + s = "FS_NOTFOUND"; + break; + case FS_REQUESTED: + s = "FS_REQUESTED"; + break; + case FS_DOWNLOADING: + s = "FS_DOWNLOADING"; + break; + default: + s = "unknown"; + break; + } + I_Error("Try to load file \"%s\" with status of %d (%s)\n", fileneeded[i].filename, + fileneeded[i].status, s); + } } } -// little optimization to test if there is a file in the queue -static INT32 filetosend = 0; +// Number of files to send +// Little optimization to quickly test if there is a file in the queue +static INT32 filestosend = 0; -static void SendFile(INT32 node, const char *filename, UINT8 fileid) +/** Adds a file to the file list for a node + * + * \param node The node to send the file to + * \param filename The file to send + * \param fileid ??? + * \sa SV_SendRam + * + */ +static void SV_SendFile(INT32 node, const char *filename, UINT8 fileid) { - filetx_t **q; - filetx_t *p; + filetx_t **q; // A pointer to the "next" field of the last file in the list + filetx_t *p; // The new file request INT32 i; char wadfilename[MAX_WADPATH]; + // Find the last file in the list and set a pointer to its "next" field q = &transfer[node].txlist; while (*q) q = &((*q)->next); + + // Allocate a file request and append it to the file list p = *q = (filetx_t *)malloc(sizeof (filetx_t)); - if (p) - memset(p, 0, sizeof (filetx_t)); - else - I_Error("SendFile: No more ram\n"); - p->filename = (char *)malloc(MAX_WADPATH); - if (!p->filename) - I_Error("SendFile: No more ram\n"); + if (!p) + I_Error("SV_SendFile: No more memory\n"); - // a minimum of security, can get only file in srb2 direcory - strlcpy(p->filename, filename, MAX_WADPATH); - nameonly(p->filename); + // Initialise with zeros + memset(p, 0, sizeof (filetx_t)); - // check first in wads loaded the majority of case + // Allocate the file name + p->id.filename = (char *)malloc(MAX_WADPATH); + if (!p->id.filename) + I_Error("SV_SendFile: No more memory\n"); + + // Set the file name and get rid of the path + strlcpy(p->id.filename, filename, MAX_WADPATH); + nameonly(p->id.filename); + + // Look for the requested file through all loaded files for (i = 0; wadfiles[i]; i++) { strlcpy(wadfilename, wadfiles[i]->filename, MAX_WADPATH); nameonly(wadfilename); - if (!stricmp(wadfilename, p->filename)) + if (!stricmp(wadfilename, p->id.filename)) { - // copy filename with full path - strlcpy(p->filename, wadfiles[i]->filename, MAX_WADPATH); + // Copy file name with full path + strlcpy(p->id.filename, wadfiles[i]->filename, MAX_WADPATH); break; } } + // Handle non-loaded file requests if (!wadfiles[i]) { DEBFILE(va("%s not found in wadfiles\n", filename)); - // this formerly checked if (!findfile(p->filename, NULL, true)) + // This formerly checked if (!findfile(p->id.filename, NULL, true)) - // not found - // don't inform client (probably hacker) + // Not found + // Don't inform client (probably someone who thought they could leak 2.2 ACZ) DEBFILE(va("Client %d request %s: not found\n", node, filename)); - free(p->filename); + free(p->id.filename); free(p); *q = NULL; return; } + // Handle huge file requests (i.e. bigger than cv_maxsend.value KB) if (wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024) { - // too big - // don't inform client (client sucks, man) + // Too big + // Don't inform client (client sucks, man) DEBFILE(va("Client %d request %s: file too big, not sending\n", node, filename)); - free(p->filename); + free(p->id.filename); free(p); *q = NULL; return; } DEBFILE(va("Sending file %s (id=%d) to %d\n", filename, fileid, node)); - p->ram = SF_FILE; + p->ram = SF_FILE; // It's a file, we need to close it and free its name once we're done sending it p->fileid = fileid; - p->next = NULL; // end of list - filetosend++; + p->next = NULL; // End of list + filestosend++; } -void SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid) +/** Adds a memory block to the file list for a node + * + * \param node The node to send the memory block to + * \param data The memory block to send + * \param size The size of the block in bytes + * \param freemethod How to free the block after it has been sent + * \param fileid ??? + * \sa SV_SendFile + * + */ +void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid) { - filetx_t **q; - filetx_t *p; + filetx_t **q; // A pointer to the "next" field of the last file in the list + filetx_t *p; // The new file request + // Find the last file in the list and set a pointer to its "next" field q = &transfer[node].txlist; while (*q) q = &((*q)->next); + + // Allocate a file request and append it to the file list p = *q = (filetx_t *)malloc(sizeof (filetx_t)); - if (p) - memset(p, 0, sizeof (filetx_t)); - else - I_Error("SendRam: No more ram\n"); - p->ram = freemethod; - p->filename = data; + if (!p) + I_Error("SV_SendRam: No more memory\n"); + + // Initialise with zeros + memset(p, 0, sizeof (filetx_t)); + + p->ram = freemethod; // Remember how to free the memory block for when we're done sending it + p->id.ram = data; p->size = (UINT32)size; p->fileid = fileid; - p->next = NULL; // end of list + p->next = NULL; // End of list - DEBFILE(va("Sending ram %p(size:%u) to %d (id=%u)\n",p->filename,p->size,node,fileid)); + DEBFILE(va("Sending ram %p(size:%u) to %d (id=%u)\n",p->id.ram,p->size,node,fileid)); - filetosend++; + filestosend++; } -static void EndSend(INT32 node) +/** Stops sending a file for a node, and removes the file request from the list, + * either because the file has been fully sent or because the node was disconnected + * + * \param node The destination + * + */ +static void SV_EndFileSend(INT32 node) { filetx_t *p = transfer[node].txlist; + + // Free the file request according to the freemethod parameter used with SV_SendFile/Ram switch (p->ram) { - case SF_FILE: + case SF_FILE: // It's a file, close it and free its filename if (transfer[node].currentfile) fclose(transfer[node].currentfile); - free(p->filename); + free(p->id.filename); break; - case SF_Z_RAM: - Z_Free(p->filename); + case SF_Z_RAM: // It's a memory block allocated with Z_Alloc or the likes, use Z_Free + Z_Free(p->id.ram); break; - case SF_RAM: - free(p->filename); - case SF_NOFREERAM: + case SF_RAM: // It's a memory block allocated with malloc, use free + free(p->id.ram); + case SF_NOFREERAM: // Nothing to free break; } + + // Remove the file request from the list transfer[node].txlist = p->next; - transfer[node].currentfile = NULL; free(p); - filetosend--; + + // Indicate that the transmission is over + transfer[node].currentfile = NULL; + + filestosend--; } #define PACKETPERTIC net_bandwidth/(TICRATE*software_MAXPACKETLENGTH) -void FiletxTicker(void) +/** Handles file transmission + * + * + */ +void SV_FileSendTicker(void) { static INT32 currentnode = 0; filetx_pak *p; @@ -557,12 +646,12 @@ void FiletxTicker(void) filetx_t *f; INT32 packetsent = PACKETPERTIC, ram, i; - if (!filetosend) + if (!filestosend) return; if (!packetsent) packetsent++; // (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth) - while (packetsent-- && filetosend != 0) + while (packetsent-- && filestosend != 0) { for (i = currentnode, ram = 0; ram < MAXNETNODES; i = (i+1) % MAXNETNODES, ram++) @@ -571,24 +660,25 @@ void FiletxTicker(void) goto found; } // no transfer to do - I_Error("filetosend=%d but no filetosend found\n", filetosend); + I_Error("filestosend=%d but no file to send found\n", filestosend); found: currentnode = (i+1) % MAXNETNODES; f = transfer[i].txlist; ram = f->ram; - if (!transfer[i].currentfile) // file not already open + // Open the file if it isn't open yet, or + if (!transfer[i].currentfile) { - if (!ram) + if (!ram) // Sending a file { long filesize; transfer[i].currentfile = - fopen(f->filename, "rb"); + fopen(f->id.filename, "rb"); if (!transfer[i].currentfile) I_Error("File %s does not exist", - f->filename); + f->id.filename); fseek(transfer[i].currentfile, 0, SEEK_END); filesize = ftell(transfer[i].currentfile); @@ -596,45 +686,48 @@ void FiletxTicker(void) // Nobody wants to transfer a file bigger // than 4GB! if (filesize >= LONG_MAX) - I_Error("filesize of %s is too large", f->filename); - if (-1 == filesize) - I_Error("Error getting filesize of %s", f->filename); + I_Error("filesize of %s is too large", f->id.filename); + if (filesize == -1) + I_Error("Error getting filesize of %s", f->id.filename); f->size = (UINT32)filesize; fseek(transfer[i].currentfile, 0, SEEK_SET); } - else - transfer[i].currentfile = (FILE *)1; + else // Sending RAM + transfer[i].currentfile = (FILE *)1; // Set currentfile to a non-null value to indicate that it is open transfer[i].position = 0; } + // Build a packet containing a file fragment p = &netbuffer->u.filetxpak; size = software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE); if (f->size-transfer[i].position < size) size = f->size-transfer[i].position; if (ram) - M_Memcpy(p->data, &f->filename[transfer[i].position], size); + M_Memcpy(p->data, &f->id.ram[transfer[i].position], size); else if (fread(p->data, 1, size, transfer[i].currentfile) != size) - I_Error("FiletxTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->filename, transfer[i].position, strerror(ferror(transfer[i].currentfile))); + I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, strerror(ferror(transfer[i].currentfile))); p->position = LONG(transfer[i].position); - // put flag so receiver know the totalsize + // Put flag so receiver knows the total size if (transfer[i].position + size == f->size) p->position |= LONG(0x80000000); p->fileid = f->fileid; p->size = SHORT((UINT16)size); netbuffer->packettype = PT_FILEFRAGMENT; - if (!HSendPacket(i, true, 0, FILETXHEADER + size)) // reliable SEND - { // not sent for some odd reason, retry at next call - if (!ram) - fseek(transfer[i].currentfile,transfer[i].position,SEEK_SET); - // exit the while (can't send this one so why should i send the next?) - break; + + // Send the packet + if (HSendPacket(i, true, 0, FILETXHEADER + size)) // Reliable SEND + { // Success + transfer[i].position = (UINT32)(transfer[i].position + size); + if (transfer[i].position == f->size) // Finish? + SV_EndFileSend(i); } - else // success - { - transfer[i].position = (UINT32)(size+transfer[i].position); - if (transfer[i].position == f->size) // finish ? - EndSend(i); + else + { // Not sent for some odd reason, retry at next call + if (!ram) + fseek(transfer[i].currentfile,transfer[i].position, SEEK_SET); + // Exit the while (can't send this one so why should i send the next?) + break; } } } @@ -646,16 +739,18 @@ void Got_Filetxpak(void) if (filenum >= fileneedednum) { - DEBFILE(va("fileframent not needed %d>%d\n",filenum, fileneedednum)); + DEBFILE(va("fileframent not needed %d>%d\n", filenum, fileneedednum)); return; } if (fileneeded[filenum].status == FS_REQUESTED) { - if (fileneeded[filenum].phandle) I_Error("Got_Filetxpak: allready open file\n"); - fileneeded[filenum].phandle = fopen(fileneeded[filenum].filename, "wb"); - if (!fileneeded[filenum].phandle) I_Error("Can't create file %s: %s",fileneeded[filenum].filename, strerror(errno)); - CONS_Printf("\r%s...\n",fileneeded[filenum].filename); + if (fileneeded[filenum].file) + I_Error("Got_Filetxpak: already open file\n"); + fileneeded[filenum].file = fopen(fileneeded[filenum].filename, "wb"); + if (!fileneeded[filenum].file) + I_Error("Can't create file %s: %s", fileneeded[filenum].filename, strerror(errno)); + CONS_Printf("\r%s...\n",fileneeded[filenum].filename); fileneeded[filenum].currentsize = 0; fileneeded[filenum].status = FS_DOWNLOADING; } @@ -664,24 +759,24 @@ void Got_Filetxpak(void) { UINT32 pos = LONG(netbuffer->u.filetxpak.position); UINT16 size = SHORT(netbuffer->u.filetxpak.size); - // use a special tric to know when file is finished (not allways used) - // WARNING: filepak can arrive out of order so don't stop now ! + // Use a special trick to know when the file is complete (not always used) + // WARNING: file fragments can arrive out of order so don't stop yet! if (pos & 0x80000000) { pos &= ~0x80000000; fileneeded[filenum].totalsize = pos + size; } - // we can receive packet in the wrong order, anyway all os support gaped file - fseek(fileneeded[filenum].phandle,pos,SEEK_SET); - if (fwrite(netbuffer->u.filetxpak.data,size,1,fileneeded[filenum].phandle)!=1) - I_Error("Can't write to %s: %s\n",fileneeded[filenum].filename, strerror(ferror(fileneeded[filenum].phandle))); + // We can receive packet in the wrong order, anyway all os support gaped file + fseek(fileneeded[filenum].file, pos, SEEK_SET); + if (fwrite(netbuffer->u.filetxpak.data,size,1,fileneeded[filenum].file) != 1) + I_Error("Can't write to %s: %s\n",fileneeded[filenum].filename, strerror(ferror(fileneeded[filenum].file))); fileneeded[filenum].currentsize += size; - // finished? + // Finished? if (fileneeded[filenum].currentsize == fileneeded[filenum].totalsize) { - fclose(fileneeded[filenum].phandle); - fileneeded[filenum].phandle = NULL; + fclose(fileneeded[filenum].file); + fileneeded[filenum].file = NULL; fileneeded[filenum].status = FS_FOUND; CONS_Printf(M_GetText("Downloading %s...(done)\n"), fileneeded[filenum].filename); @@ -689,8 +784,8 @@ void Got_Filetxpak(void) } else I_Error("Received a file not requested\n"); - // send ack back quickly + // Send ack back quickly if (++filetime == 3) { Net_SendAcks(servernode); @@ -702,33 +797,39 @@ void Got_Filetxpak(void) #endif } -void AbortSendFiles(INT32 node) +/** Cancels all file requests for a node + * + * \param node The destination + * \sa SV_EndFileSend + * + */ +void SV_AbortSendFiles(INT32 node) { while (transfer[node].txlist) - EndSend(node); + SV_EndFileSend(node); } void CloseNetFile(void) { INT32 i; - // is sending? + // Is sending? for (i = 0; i < MAXNETNODES; i++) - AbortSendFiles(i); + SV_AbortSendFiles(i); - // receiving a file? + // Receiving a file? for (i = 0; i < MAX_WADFILES; i++) - if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].phandle) + if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].file) { - fclose(fileneeded[i].phandle); - // file is not complete delete it + fclose(fileneeded[i].file); + // File is not complete delete it remove(fileneeded[i].filename); } - // remove FILEFRAGMENT from acknledge list + // Remove PT_FILEFRAGMENT from acknowledge list Net_AbortPacketType(PT_FILEFRAGMENT); } -// functions cut and pasted from doomatic :) +// Functions cut and pasted from Doomatic :) void nameonly(char *s) { diff --git a/src/d_netfil.h b/src/d_netfil.h index a68119f1..e82b57d6 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -29,21 +29,21 @@ typedef enum FS_FOUND, FS_REQUESTED, FS_DOWNLOADING, - FS_OPEN, // is opened and used in w_wad + FS_OPEN, // Is opened and used in w_wad FS_MD5SUMBAD } filestatus_t; typedef struct { UINT8 important; - UINT8 willsend; // is the server willing to send it? + UINT8 willsend; // Is the server willing to send it? char filename[MAX_WADPATH]; UINT8 md5sum[16]; - // used only for download - FILE *phandle; + // Used only for download + FILE *file; UINT32 currentsize; UINT32 totalsize; - filestatus_t status; // the value returned by recsearch + filestatus_t status; // The value returned by recsearch } fileneeded_t; extern INT32 fileneedednum; @@ -58,28 +58,24 @@ UINT8 *PutFileNeeded(void); void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr); void CL_PrepareDownloadSaveGame(const char *tmpsave); -// check file list in wadfiles return 0 when a file is not found -// 1 if all file are found -// 2 if you cannot connect (different wad version or -// no enought space to download files) INT32 CL_CheckFiles(void); void CL_LoadServerFiles(void); -void SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, +void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid); -void FiletxTicker(void); +void SV_FileSendTicker(void); void Got_Filetxpak(void); boolean CL_CheckDownloadable(void); boolean CL_SendRequestFile(void); void Got_RequestFilePak(INT32 node); -void AbortSendFiles(INT32 node); +void SV_AbortSendFiles(INT32 node); void CloseNetFile(void); boolean fileexist(char *filename, time_t ptime); -// search a file in the wadpath, return FS_FOUND when found +// Search a file in the wadpath, return FS_FOUND when found filestatus_t findfile(char *filename, const UINT8 *wantedmd5sum, boolean completepath); filestatus_t checkfilemd5(char *filename, const UINT8 *wantedmd5sum);