From 014b93406ed2a605027466f9f3e3815c19e5d77a Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Fri, 31 Jan 2020 15:29:22 +0100 Subject: [PATCH 01/14] Store most data in gamestate instead of server config packet --- src/d_clisrv.c | 82 +------------------------------------------------- src/d_clisrv.h | 8 ----- src/p_saveg.c | 19 ++++++++++-- 3 files changed, 17 insertions(+), 92 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index ee4e62b91..694240579 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1117,40 +1117,6 @@ static void GetPackets(void); static cl_mode_t cl_mode = CL_SEARCHING; -// Player name send/load - -static void CV_SavePlayerNames(UINT8 **p) -{ - INT32 i = 0; - // Players in game only. - for (; i < MAXPLAYERS; ++i) - { - if (!playeringame[i]) - { - WRITEUINT8(*p, 0); - continue; - } - WRITESTRING(*p, player_names[i]); - } -} - -static void CV_LoadPlayerNames(UINT8 **p) -{ - INT32 i = 0; - char tmp_name[MAXPLAYERNAME+1]; - tmp_name[MAXPLAYERNAME] = 0; - - for (; i < MAXPLAYERS; ++i) - { - READSTRING(*p, tmp_name); - if (tmp_name[0] == 0) - continue; - if (tmp_name[MAXPLAYERNAME]) // overflow detected - I_Error("Received bad server config packet when trying to join"); - memcpy(player_names[i], tmp_name, MAXPLAYERNAME+1); - } -} - #ifdef CLIENT_LOADINGSCREEN // // CL_DrawConnectionStatus @@ -1412,8 +1378,6 @@ static void SV_SendPlayerInfo(INT32 node) */ static boolean SV_SendServerConfig(INT32 node) { - INT32 i; - UINT8 *p, *op; boolean waspacketsent; netbuffer->packettype = PT_SERVERCFG; @@ -1429,32 +1393,10 @@ static boolean SV_SendServerConfig(INT32 node) netbuffer->u.servercfg.gametype = (UINT8)gametype; netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame; - // we fill these structs with FFs so that any players not in game get sent as 0xFFFF - // which is nice and easy for us to detect - memset(netbuffer->u.servercfg.playerskins, 0xFF, sizeof(netbuffer->u.servercfg.playerskins)); - memset(netbuffer->u.servercfg.playercolor, 0xFF, sizeof(netbuffer->u.servercfg.playercolor)); - memset(netbuffer->u.servercfg.playeravailabilities, 0xFF, sizeof(netbuffer->u.servercfg.playeravailabilities)); - - memset(netbuffer->u.servercfg.adminplayers, -1, sizeof(netbuffer->u.servercfg.adminplayers)); - - for (i = 0; i < MAXPLAYERS; i++) - { - netbuffer->u.servercfg.adminplayers[i] = (SINT8)adminplayers[i]; - - if (!playeringame[i]) - continue; - netbuffer->u.servercfg.playerskins[i] = (UINT8)players[i].skin; - netbuffer->u.servercfg.playercolor[i] = (UINT8)players[i].skincolor; - netbuffer->u.servercfg.playeravailabilities[i] = (UINT32)LONG(players[i].availabilities); - } - memcpy(netbuffer->u.servercfg.server_context, server_context, 8); - op = p = netbuffer->u.servercfg.varlengthinputs; - CV_SavePlayerNames(&p); - CV_SaveNetVars(&p); { - const size_t len = sizeof (serverconfig_pak) + (size_t)(p - op); + const size_t len = sizeof (serverconfig_pak); #ifdef DEBUGFILE if (debugfile) @@ -3813,9 +3755,6 @@ static void HandlePacketFromAwayNode(SINT8 node) 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); @@ -3831,8 +3770,6 @@ static void HandlePacketFromAwayNode(SINT8 node) maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic); G_SetGametype(netbuffer->u.servercfg.gametype); modifiedgame = netbuffer->u.servercfg.modifiedgame; - for (j = 0; j < MAXPLAYERS; j++) - adminplayers[j] = netbuffer->u.servercfg.adminplayers[j]; memcpy(server_context, netbuffer->u.servercfg.server_context, 8); } @@ -3851,23 +3788,6 @@ static void HandlePacketFromAwayNode(SINT8 node) #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 - && netbuffer->u.servercfg.playeravailabilities[j] == 0xFFFFFFFF) - continue; // not in game - - playeringame[j] = true; - players[j].availabilities = (UINT32)LONG(netbuffer->u.servercfg.playeravailabilities[j]); - 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? diff --git a/src/d_clisrv.h b/src/d_clisrv.h index df93fe31d..a48ab91a9 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -305,18 +305,10 @@ typedef struct UINT8 clientnode; UINT8 gamestate; - // 0xFF == not in game; else player skin num - UINT8 playerskins[MAXPLAYERS]; - UINT8 playercolor[MAXPLAYERS]; - UINT32 playeravailabilities[MAXPLAYERS]; - UINT8 gametype; UINT8 modifiedgame; - SINT8 adminplayers[MAXPLAYERS]; // Needs to be signed char server_context[8]; // Unique context id, generated at server startup. - - UINT8 varlengthinputs[0]; // Playernames and netvars } ATTRPACK serverconfig_pak; typedef struct { diff --git a/src/p_saveg.c b/src/p_saveg.c index 853856880..19c5b532e 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -107,13 +107,16 @@ static void P_NetArchivePlayers(void) for (i = 0; i < MAXPLAYERS; i++) { + WRITESINT8(save_p, (SINT8)adminplayers[i]); + if (!playeringame[i]) continue; flags = 0; - // no longer send ticcmds, player name, skin, or color + // no longer send ticcmds + WRITESTRINGN(save_p, player_names[i], MAXPLAYERNAME); WRITEANGLE(save_p, players[i].aiming); WRITEANGLE(save_p, players[i].drawangle); WRITEANGLE(save_p, players[i].awayviewaiming); @@ -140,6 +143,9 @@ static void P_NetArchivePlayers(void) WRITEUINT16(save_p, players[i].flashpal); WRITEUINT16(save_p, players[i].flashcount); + WRITEUINT8(save_p, players[i].skincolor); + WRITEINT32(save_p, players[i].skin); + WRITEUINT32(save_p, players[i].availabilities); WRITEUINT32(save_p, players[i].score); WRITEFIXED(save_p, players[i].dashspeed); WRITESINT8(save_p, players[i].lives); @@ -314,6 +320,8 @@ static void P_NetUnArchivePlayers(void) for (i = 0; i < MAXPLAYERS; i++) { + adminplayers[i] = (INT32)READSINT8(save_p); + // Do NOT memset player struct to 0 // other areas may initialize data elsewhere //memset(&players[i], 0, sizeof (player_t)); @@ -321,9 +329,8 @@ static void P_NetUnArchivePlayers(void) continue; // NOTE: sending tics should (hopefully) no longer be necessary - // sending player names, skin and color should not be necessary at all! - // (that data is handled in the server config now) + READSTRINGN(save_p, player_names[i], MAXPLAYERNAME); players[i].aiming = READANGLE(save_p); players[i].drawangle = READANGLE(save_p); players[i].awayviewaiming = READANGLE(save_p); @@ -350,6 +357,9 @@ static void P_NetUnArchivePlayers(void) players[i].flashpal = READUINT16(save_p); players[i].flashcount = READUINT16(save_p); + players[i].skincolor = READUINT8(save_p); + players[i].skin = READINT32(save_p); + players[i].availabilities = READUINT32(save_p); players[i].score = READUINT32(save_p); players[i].dashspeed = READFIXED(save_p); // dashing speed players[i].lives = READSINT8(save_p); @@ -3983,6 +3993,7 @@ static void P_NetArchiveMisc(void) WRITEINT16(save_p, gamemap); WRITEINT16(save_p, gamestate); + WRITEINT16(save_p, gametype); { UINT32 pig = 0; @@ -4065,6 +4076,8 @@ static inline boolean P_NetUnArchiveMisc(void) G_SetGamestate(READINT16(save_p)); + gametype = READINT16(save_p); + { UINT32 pig = READUINT32(save_p); for (i = 0; i < MAXPLAYERS; i++) From d02c4c0cc7e0d9e9bc776ecf5f89d58191cb697f Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Fri, 31 Jan 2020 15:57:04 +0100 Subject: [PATCH 02/14] Add "resendgamestate" command --- src/d_clisrv.c | 120 +++++++++++++++++++++++++++++++++++++++++++++---- src/d_clisrv.h | 3 ++ src/d_main.c | 2 +- src/d_net.c | 3 ++ src/g_game.c | 2 +- src/p_saveg.c | 19 +++++--- src/p_saveg.h | 4 +- src/p_setup.c | 27 +++++++---- src/p_setup.h | 2 +- 9 files changed, 153 insertions(+), 29 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 694240579..c1703b82d 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -121,6 +121,7 @@ static ticcmd_t localcmds2; static boolean cl_packetmissed; // here it is for the secondary local player (splitscreen) static UINT8 mynode; // my address pointofview server +static boolean cl_redownloadinggamestate = false; static UINT8 localtextcmd[MAXTEXTCMD]; static UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen @@ -1429,7 +1430,7 @@ static boolean SV_SendServerConfig(INT32 node) #ifdef JOININGAME #define SAVEGAMESIZE (768*1024) -static void SV_SendSaveGame(INT32 node) +static void SV_SendSaveGame(INT32 node, boolean resending) { size_t length, compressedlen; UINT8 *savebuffer; @@ -1447,7 +1448,7 @@ static void SV_SendSaveGame(INT32 node) // Leave room for the uncompressed length. save_p = savebuffer + sizeof(UINT32); - P_SaveNetGame(); + P_SaveNetGame(resending); length = save_p - savebuffer; if (length > SAVEGAMESIZE) @@ -1520,7 +1521,7 @@ static void SV_SavedGame(void) return; } - P_SaveNetGame(); + P_SaveNetGame(false); length = save_p - savebuffer; if (length > SAVEGAMESIZE) @@ -1543,7 +1544,7 @@ static void SV_SavedGame(void) #define TMPSAVENAME "$$$.sav" -static void CL_LoadReceivedSavegame(void) +static void CL_LoadReceivedSavegame(boolean reloading) { UINT8 *savebuffer = NULL; size_t length, decompressedlen; @@ -1579,7 +1580,7 @@ static void CL_LoadReceivedSavegame(void) automapactive = false; // load a base level - if (P_LoadNetGame()) + if (P_LoadNetGame(reloading)) { const INT32 actnum = mapheaderinfo[gamemap-1]->actnum; CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap)); @@ -1611,6 +1612,32 @@ static void CL_LoadReceivedSavegame(void) consistancy[gametic%BACKUPTICS] = Consistancy(); CON_ToggleOff(); } + +static void CL_ReloadReceivedSavegame(void) +{ + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + { +#ifdef HAVE_BLUA + LUA_InvalidatePlayer(&players[i]); +#endif + sprintf(player_names[i], "Player %d", i + 1); + } + + CL_LoadReceivedSavegame(true); + + if (neededtic < gametic) + neededtic = gametic; + maketic = neededtic; + + camera.subsector = R_PointInSubsector(camera.x, camera.y); + camera2.subsector = R_PointInSubsector(camera2.x, camera2.y); + + cl_redownloadinggamestate = false; + + CONS_Printf(M_GetText("Game state reloaded\n")); +} #endif #ifndef NONET @@ -1950,7 +1977,7 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic if (fileneeded[0].status == FS_FOUND) { // Gamestate is now handled within CL_LoadReceivedSavegame() - CL_LoadReceivedSavegame(); + CL_LoadReceivedSavegame(false); cl_mode = CL_CONNECTED; } // don't break case continue to CL_CONNECTED else @@ -2974,6 +3001,32 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) CL_RemovePlayer(pnum, kickreason); } +static void Command_ResendGamestate(void) +{ + if (COM_Argc() == 1) + { + CONS_Printf(M_GetText("resendgamestate : resend the game state to a player\n")); + return; + } + else if (client) + { + CONS_Printf(M_GetText("Only the server can use this.\n")); + return; + } + + const SINT8 playernum = nametonum(COM_Argv(1)); + if (playernum == -1 || playernum == 0) + return; + + // Send a PT_WILLRESENDGAMESTATE packet to the client so they know what's going on + netbuffer->packettype = PT_WILLRESENDGAMESTATE; + if (!HSendPacket(playernode[playernum], true, 0, 0)) + { + CONS_Alert(CONS_ERROR, M_GetText("A problem occured, please try again.\n")); + return; + } +} + consvar_t cv_allownewplayer = {"allowjoin", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL }; consvar_t cv_joinnextround = {"joinnextround", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}}; @@ -3012,6 +3065,7 @@ void D_ClientServerInit(void) COM_AddCommand("reloadbans", Command_ReloadBan); COM_AddCommand("connect", Command_connect); COM_AddCommand("nodes", Command_Nodes); + COM_AddCommand("resendgamestate", Command_ResendGamestate); #ifdef PACKETDROP COM_AddCommand("drop", Command_Drop); COM_AddCommand("droprate", Command_Droprate); @@ -3082,6 +3136,7 @@ void SV_ResetServer(void) mynode = 0; cl_packetmissed = false; + cl_redownloadinggamestate = false; if (dedicated) { @@ -3598,7 +3653,7 @@ static void HandleConnect(SINT8 node) { if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) && newnode) { - SV_SendSaveGame(node); // send a complete game state + SV_SendSaveGame(node, false); // send a complete game state DEBFILE("send savegame\n"); } SV_AddWaitingPlayers(names[0], names[1]); @@ -3665,6 +3720,42 @@ static void HandleServerInfo(SINT8 node) } #endif +static void PT_WillResendGamestate(void) +{ + char tmpsave[256]; + + if (server || cl_redownloadinggamestate) + return; + + // Send back a PT_CANRESENDGAMESTATE packet to the server + // so they know they can start sending the game state + netbuffer->packettype = PT_CANRESENDGAMESTATE; + if (!HSendPacket(servernode, true, 0, 0)) + return; + + CONS_Printf(M_GetText("Reloading game state...\n")); + + sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); + + // 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); + + CL_PrepareDownloadSaveGame(tmpsave); + + cl_redownloadinggamestate = true; +} + +static void PT_CanResendGamestate(SINT8 node) +{ + if (client || sendingsavegame[node]) + return; + + CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[nodetoplayer[node]]); + + SV_SendSaveGame(node, true); // Resend a complete game state +} + /** Handles a packet received from a node that isn't in game * * \param node The packet sender @@ -4107,6 +4198,9 @@ static void HandlePacketFromPlayer(SINT8 node) Net_CloseConnection(node); nodeingame[node] = false; break; + case PT_CANRESENDGAMESTATE: + PT_CanResendGamestate(node); + break; // -------------------------------------------- CLIENT RECEIVE ---------- case PT_RESYNCHEND: // Only accept PT_RESYNCHEND from the server. @@ -4136,6 +4230,9 @@ static void HandlePacketFromPlayer(SINT8 node) break; } + if (cl_redownloadinggamestate) + break; + realstart = ExpandTics(netbuffer->u.serverpak.starttic); realend = realstart + netbuffer->u.serverpak.numtics; @@ -4234,6 +4331,9 @@ static void HandlePacketFromPlayer(SINT8 node) if (client) Got_Filetxpak(); break; + case PT_WILLRESENDGAMESTATE: + PT_WillResendGamestate(); + break; default: DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n", netbuffer->packettype, node)); @@ -4876,7 +4976,11 @@ void NetUpdate(void) if (client) { - if (!resynch_local_inprogress) + // If the client just finished redownloading the game state, load it + if (cl_redownloadinggamestate && fileneeded[0].status == FS_FOUND) + CL_ReloadReceivedSavegame(); + + if (!(resynch_local_inprogress || cl_redownloadinggamestate)) CL_SendClientCmd(); // Send tic cmd hu_resynching = resynch_local_inprogress; } diff --git a/src/d_clisrv.h b/src/d_clisrv.h index a48ab91a9..6761b47cc 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -67,6 +67,9 @@ typedef enum PT_RESYNCHEND, // Player is now resynched and is being requested to remake the gametic PT_RESYNCHGET, // Player got resynch packet + PT_WILLRESENDGAMESTATE, // Hey Client, I am about to resend you the gamestate! + PT_CANRESENDGAMESTATE, // Okay Server, I'm ready to receive it, you can go ahead. + // Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility. PT_CANFAIL, // This is kind of a priority. Anything bigger than CANFAIL diff --git a/src/d_main.c b/src/d_main.c index 15d3c8041..f70f80b32 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1514,7 +1514,7 @@ void D_SRB2Main(void) { levelstarttic = gametic; G_SetGamestate(GS_LEVEL); - if (!P_LoadLevel(false)) + if (!P_LoadLevel(false, false)) I_Quit(); // fail so reset game stuff } } diff --git a/src/d_net.c b/src/d_net.c index 573c9cfe9..648d5683f 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -799,6 +799,9 @@ static const char *packettypename[NUMPACKETTYPE] = "RESYNCHEND", "RESYNCHGET", + "WILLRESENDGAMESTATE", + "CANRESENDGAMESTATE", + "FILEFRAGMENT", "TEXTCMD", "TEXTCMD2", diff --git a/src/g_game.c b/src/g_game.c index f5d7cd2fb..4f9e7a580 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1839,7 +1839,7 @@ void G_DoLoadLevel(boolean resetplayer) } // Setup the level. - if (!P_LoadLevel(false)) // this never returns false? + if (!P_LoadLevel(false, false)) // this never returns false? { // fail so reset game stuff Command_ExitGame_f(); diff --git a/src/p_saveg.c b/src/p_saveg.c index 19c5b532e..1e65098cf 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -3985,12 +3985,14 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride) playeringame[consoleplayer] = true; } -static void P_NetArchiveMisc(void) +static void P_NetArchiveMisc(boolean resending) { INT32 i; WRITEUINT32(save_p, ARCHIVEBLOCK_MISC); + if (resending) + WRITEUINT32(save_p, gametic); WRITEINT16(save_p, gamemap); WRITEINT16(save_p, gamestate); WRITEINT16(save_p, gametype); @@ -4056,13 +4058,16 @@ static void P_NetArchiveMisc(void) WRITEUINT8(save_p, 0x2e); } -static inline boolean P_NetUnArchiveMisc(void) +static inline boolean P_NetUnArchiveMisc(boolean reloading) { INT32 i; if (READUINT32(save_p) != ARCHIVEBLOCK_MISC) I_Error("Bad $$$.sav at archive block Misc"); + if (reloading) + gametic = READUINT32(save_p); + gamemap = READINT16(save_p); // gamemap changed; we assume that its map header is always valid, @@ -4091,7 +4096,7 @@ static inline boolean P_NetUnArchiveMisc(void) tokenlist = READUINT32(save_p); - if (!P_LoadLevel(true)) + if (!P_LoadLevel(true, reloading)) return false; // get the time @@ -4192,14 +4197,14 @@ void P_SaveGame(void) P_ArchiveLuabanksAndConsistency(); } -void P_SaveNetGame(void) +void P_SaveNetGame(boolean resending) { thinker_t *th; mobj_t *mobj; INT32 i = 1; // don't start from 0, it'd be confused with a blank pointer otherwise CV_SaveNetVars(&save_p); - P_NetArchiveMisc(); + P_NetArchiveMisc(resending); // Assign the mobjnumber for pointer tracking for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) @@ -4250,10 +4255,10 @@ boolean P_LoadGame(INT16 mapoverride) return true; } -boolean P_LoadNetGame(void) +boolean P_LoadNetGame(boolean reloading) { CV_LoadNetVars(&save_p); - if (!P_NetUnArchiveMisc()) + if (!P_NetUnArchiveMisc(reloading)) return false; P_NetUnArchivePlayers(); if (gamestate == GS_LEVEL) diff --git a/src/p_saveg.h b/src/p_saveg.h index 16d47bea8..b75c02117 100644 --- a/src/p_saveg.h +++ b/src/p_saveg.h @@ -22,9 +22,9 @@ // These are the load / save game routines. void P_SaveGame(void); -void P_SaveNetGame(void); +void P_SaveNetGame(boolean resending); boolean P_LoadGame(INT16 mapoverride); -boolean P_LoadNetGame(void); +boolean P_LoadNetGame(boolean reloading); mobj_t *P_FindNewPosition(UINT32 oldposition); diff --git a/src/p_setup.c b/src/p_setup.c index 729ee00c2..95552a2d0 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2777,8 +2777,6 @@ static void P_InitLevelSettings(void) leveltime = 0; - localaiming = 0; - localaiming2 = 0; modulothing = 0; // special stage tokens, emeralds, and ring total @@ -2893,6 +2891,9 @@ void P_RespawnThings(void) P_InitLevelSettings(); + localaiming = 0; + localaiming2 = 0; + P_SpawnMapThings(true); // restore skybox viewpoint/centerpoint if necessary, set them to defaults if we can't do that @@ -3387,7 +3388,7 @@ static void P_InitGametype(void) * \param fromnetsave If true, skip some stuff because we're loading a netgame snapshot. * \todo Clean up, refactor, split up; get rid of the bloat. */ -boolean P_LoadLevel(boolean fromnetsave) +boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) { // use gamemap to get map number. // 99% of the things already did, so. @@ -3457,7 +3458,10 @@ boolean P_LoadLevel(boolean fromnetsave) players[consoleplayer].viewz = 1; // Cancel all d_main.c fadeouts (keep fade in though). - wipegamestate = FORCEWIPEOFF; + if (reloadinggamestate) + wipegamestate = gamestate; // Don't fade if reloading the gamestate + else + wipegamestate = FORCEWIPEOFF; wipestyleflags = 0; // Special stage fade to white @@ -3491,7 +3495,7 @@ boolean P_LoadLevel(boolean fromnetsave) // Let's fade to black here // But only if we didn't do the special stage wipe - if (rendermode != render_none && !ranspecialwipe) + if (rendermode != render_none && !(ranspecialwipe || reloadinggamestate)) P_RunLevelWipe(); if (!titlemapinaction) @@ -3622,7 +3626,12 @@ boolean P_LoadLevel(boolean fromnetsave) if (!fromnetsave) P_InitGametype(); - P_InitCamera(); + if (!reloadinggamestate) + { + P_InitCamera(); + localaiming = 0; + localaiming2 = 0; + } // clear special respawning que iquehead = iquetail = 0; @@ -3633,7 +3642,7 @@ boolean P_LoadLevel(boolean fromnetsave) P_MapEnd(); // Remove the loading shit from the screen - if (rendermode != render_none && !titlemapinaction) + if (rendermode != render_none && !(titlemapinaction || reloadinggamestate)) F_WipeColorFill(levelfadecol); if (precache || dedicated) @@ -3671,8 +3680,8 @@ boolean P_LoadLevel(boolean fromnetsave) #endif } - // No render mode, stop here. - if (rendermode == render_none) + // No render mode or reloading gamestate, stop here. + if (rendermode == render_none || reloadinggamestate) return true; // Title card! diff --git a/src/p_setup.h b/src/p_setup.h index d7e2d8861..c638bafca 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -97,7 +97,7 @@ void P_SetupLevelSky(INT32 skynum, boolean global); void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum); #endif void P_RespawnThings(void); -boolean P_LoadLevel(boolean fromnetsave); +boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate); #ifdef HWRENDER void HWR_SetupLevel(void); #endif From a927d67259c27cf66dffcbdc47df9383bc0b70f9 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Fri, 28 Feb 2020 18:17:37 +0100 Subject: [PATCH 03/14] Fix warning --- src/d_clisrv.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index c1703b82d..8eeda23a6 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3003,6 +3003,8 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) static void Command_ResendGamestate(void) { + SINT8 playernum; + if (COM_Argc() == 1) { CONS_Printf(M_GetText("resendgamestate : resend the game state to a player\n")); @@ -3014,7 +3016,7 @@ static void Command_ResendGamestate(void) return; } - const SINT8 playernum = nametonum(COM_Argv(1)); + playernum = nametonum(COM_Argv(1)); if (playernum == -1 || playernum == 0) return; From 4f7591a044d07c89f811bc6b179e90070db226bd Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Sat, 29 Feb 2020 13:40:15 +0100 Subject: [PATCH 04/14] Rename packet --- src/d_clisrv.c | 10 +++++----- src/d_clisrv.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 8eeda23a6..42e7b472f 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3729,9 +3729,9 @@ static void PT_WillResendGamestate(void) if (server || cl_redownloadinggamestate) return; - // Send back a PT_CANRESENDGAMESTATE packet to the server + // Send back a PT_CANRECEIVEGAMESTATE packet to the server // so they know they can start sending the game state - netbuffer->packettype = PT_CANRESENDGAMESTATE; + netbuffer->packettype = PT_CANRECEIVEGAMESTATE; if (!HSendPacket(servernode, true, 0, 0)) return; @@ -3748,7 +3748,7 @@ static void PT_WillResendGamestate(void) cl_redownloadinggamestate = true; } -static void PT_CanResendGamestate(SINT8 node) +static void PT_CanReceiveGamestate(SINT8 node) { if (client || sendingsavegame[node]) return; @@ -4200,8 +4200,8 @@ static void HandlePacketFromPlayer(SINT8 node) Net_CloseConnection(node); nodeingame[node] = false; break; - case PT_CANRESENDGAMESTATE: - PT_CanResendGamestate(node); + case PT_CANRECEIVEGAMESTATE: + PT_CanReceiveGamestate(node); break; // -------------------------------------------- CLIENT RECEIVE ---------- case PT_RESYNCHEND: diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 6761b47cc..5b4b7d2d8 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -68,7 +68,7 @@ typedef enum PT_RESYNCHGET, // Player got resynch packet PT_WILLRESENDGAMESTATE, // Hey Client, I am about to resend you the gamestate! - PT_CANRESENDGAMESTATE, // Okay Server, I'm ready to receive it, you can go ahead. + PT_CANRECEIVEGAMESTATE, // Okay Server, I'm ready to receive it, you can go ahead. // Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility. From 846560910d28b7f0766fe4730dd6492bba79008a Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Sun, 1 Mar 2020 03:22:47 +0100 Subject: [PATCH 05/14] Obliterate resynch Okay, more precisely this substitutes the old resynch with the newly added gamestate resend code. --- src/d_clisrv.c | 706 ++----------------------------------------------- src/d_clisrv.h | 171 +----------- src/hu_stuff.c | 2 +- 3 files changed, 31 insertions(+), 848 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f04676162..6b68c4a18 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -83,6 +83,7 @@ char playeraddress[MAXPLAYERS][64]; // The actual timeout will be longer depending on the savegame length tic_t jointimeout = (10*TICRATE); static boolean sendingsavegame[MAXNETNODES]; // Are we sending the savegame? +static boolean resendingsavegame[MAXNETNODES]; // Are we resending the savegame? static tic_t freezetimeout[MAXNETNODES]; // Until when can this node freeze the server before getting a timeout? UINT16 pingmeasurecount = 1; @@ -102,15 +103,8 @@ static tic_t maketic; static INT16 consistancy[BACKUPTICS]; -// Resynching shit! -static UINT32 resynch_score[MAXNETNODES]; // "score" for kicking -- if this gets too high then cfail kick -static UINT16 resynch_delay[MAXNETNODES]; // delay time before the player can be considered to have desynched -static UINT32 resynch_status[MAXNETNODES]; // 0 bit means synched for that player, 1 means possibly desynched -static UINT8 resynch_sent[MAXNETNODES][MAXPLAYERS]; // what synch packets have we attempted to send to the player -static UINT8 resynch_inprogress[MAXNETNODES]; -static UINT8 resynch_local_inprogress = false; // WE are desynched and getting packets to fix it. static UINT8 player_joining = false; -UINT8 hu_resynching = 0; +UINT8 hu_redownloadinggamestate = 0; UINT8 adminpassmd5[16]; boolean adminpasswordset = false; @@ -504,597 +498,6 @@ void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum) // end extra data function for lmps // ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// resynch player data -// ----------------------------------------------------------------- -static inline void resynch_write_player(resynch_pak *rsp, const size_t i) -{ - size_t j; - - rsp->playernum = (UINT8)i; - - // Do not send anything visual related. - // Only send data that we need to know for physics. - rsp->playerstate = (UINT8)players[i].playerstate; //playerstate_t - rsp->pflags = (UINT32)LONG(players[i].pflags); //pflags_t - rsp->panim = (UINT8)players[i].panim; //panim_t - - rsp->aiming = (angle_t)LONG(players[i].aiming); - rsp->currentweapon = LONG(players[i].currentweapon); - rsp->ringweapons = LONG(players[i].ringweapons); - - rsp->ammoremoval = (UINT16)SHORT(players[i].ammoremoval); - rsp->ammoremovaltimer = (tic_t)LONG(players[i].ammoremovaltimer); - rsp->ammoremovalweapon = LONG(players[i].ammoremovalweapon); - - for (j = 0; j < NUMPOWERS; ++j) - rsp->powers[j] = (UINT16)SHORT(players[i].powers[j]); - - // Score is resynched in the rspfirm resync packet - rsp->rings = SHORT(players[i].rings); - rsp->spheres = SHORT(players[i].spheres); - rsp->lives = players[i].lives; - rsp->continues = players[i].continues; - rsp->scoreadd = players[i].scoreadd; - rsp->xtralife = players[i].xtralife; - rsp->pity = players[i].pity; - - rsp->skincolor = players[i].skincolor; - rsp->skin = LONG(players[i].skin); - rsp->availabilities = LONG(players[i].availabilities); - // Just in case Lua does something like - // modify these at runtime - rsp->camerascale = (fixed_t)LONG(players[i].camerascale); - rsp->shieldscale = (fixed_t)LONG(players[i].shieldscale); - rsp->normalspeed = (fixed_t)LONG(players[i].normalspeed); - rsp->runspeed = (fixed_t)LONG(players[i].runspeed); - rsp->thrustfactor = players[i].thrustfactor; - rsp->accelstart = players[i].accelstart; - rsp->acceleration = players[i].acceleration; - rsp->charability = players[i].charability; - rsp->charability2 = players[i].charability2; - rsp->charflags = (UINT32)LONG(players[i].charflags); - rsp->thokitem = (UINT32)LONG(players[i].thokitem); //mobjtype_t - rsp->spinitem = (UINT32)LONG(players[i].spinitem); //mobjtype_t - rsp->revitem = (UINT32)LONG(players[i].revitem); //mobjtype_t - rsp->followitem = (UINT32)LONG(players[i].followitem); //mobjtype_t - rsp->actionspd = (fixed_t)LONG(players[i].actionspd); - rsp->mindash = (fixed_t)LONG(players[i].mindash); - rsp->maxdash = (fixed_t)LONG(players[i].maxdash); - rsp->jumpfactor = (fixed_t)LONG(players[i].jumpfactor); - rsp->playerheight = (fixed_t)LONG(players[i].height); - rsp->playerspinheight = (fixed_t)LONG(players[i].spinheight); - - rsp->speed = (fixed_t)LONG(players[i].speed); - rsp->secondjump = players[i].secondjump; - rsp->fly1 = players[i].fly1; - rsp->glidetime = (tic_t)LONG(players[i].glidetime); - rsp->climbing = players[i].climbing; - rsp->deadtimer = players[i].deadtimer; - rsp->exiting = (tic_t)LONG(players[i].exiting); - rsp->homing = players[i].homing; - rsp->dashmode = (tic_t)LONG(players[i].dashmode); - rsp->skidtime = (tic_t)LONG(players[i].skidtime); - rsp->cmomx = (fixed_t)LONG(players[i].cmomx); - rsp->cmomy = (fixed_t)LONG(players[i].cmomy); - rsp->rmomx = (fixed_t)LONG(players[i].rmomx); - rsp->rmomy = (fixed_t)LONG(players[i].rmomy); - - rsp->weapondelay = LONG(players[i].weapondelay); - rsp->tossdelay = LONG(players[i].tossdelay); - - rsp->starpostx = SHORT(players[i].starpostx); - rsp->starposty = SHORT(players[i].starposty); - rsp->starpostz = SHORT(players[i].starpostz); - rsp->starpostnum = LONG(players[i].starpostnum); - rsp->starposttime = (tic_t)LONG(players[i].starposttime); - rsp->starpostangle = (angle_t)LONG(players[i].starpostangle); - rsp->starpostscale = (fixed_t)LONG(players[i].starpostscale); - - rsp->maxlink = LONG(players[i].maxlink); - rsp->dashspeed = (fixed_t)LONG(players[i].dashspeed); - rsp->angle_pos = (angle_t)LONG(players[i].angle_pos); - rsp->old_angle_pos = (angle_t)LONG(players[i].old_angle_pos); - rsp->bumpertime = (tic_t)LONG(players[i].bumpertime); - rsp->flyangle = LONG(players[i].flyangle); - rsp->drilltimer = (tic_t)LONG(players[i].drilltimer); - rsp->linkcount = LONG(players[i].linkcount); - rsp->linktimer = (tic_t)LONG(players[i].linktimer); - rsp->anotherflyangle = LONG(players[i].anotherflyangle); - rsp->nightstime = (tic_t)LONG(players[i].nightstime); - rsp->drillmeter = LONG(players[i].drillmeter); - rsp->drilldelay = players[i].drilldelay; - rsp->bonustime = players[i].bonustime; - rsp->mare = players[i].mare; - rsp->lastsidehit = SHORT(players[i].lastsidehit); - rsp->lastlinehit = SHORT(players[i].lastlinehit); - - rsp->losstime = (tic_t)LONG(players[i].losstime); - rsp->timeshit = players[i].timeshit; - rsp->onconveyor = LONG(players[i].onconveyor); - - rsp->hasmo = false; - //Transfer important mo information if the player has a body. - //This lets us resync players even if they are dead. - if (!players[i].mo) - return; - rsp->hasmo = true; - - rsp->health = LONG(players[i].mo->health); - rsp->angle = (angle_t)LONG(players[i].mo->angle); - rsp->rollangle = (angle_t)LONG(players[i].mo->rollangle); - rsp->x = LONG(players[i].mo->x); - rsp->y = LONG(players[i].mo->y); - rsp->z = LONG(players[i].mo->z); - rsp->momx = LONG(players[i].mo->momx); - rsp->momy = LONG(players[i].mo->momy); - rsp->momz = LONG(players[i].mo->momz); - rsp->friction = LONG(players[i].mo->friction); - rsp->movefactor = LONG(players[i].mo->movefactor); - - rsp->sprite = (spritenum_t)LONG(players[i].mo->sprite); - rsp->frame = LONG(players[i].mo->frame); - rsp->sprite2 = players[i].mo->sprite2; - rsp->anim_duration = SHORT(players[i].mo->anim_duration); - rsp->tics = LONG(players[i].mo->tics); - rsp->statenum = (statenum_t)LONG(players[i].mo->state-states); // :( - rsp->eflags = (UINT16)SHORT(players[i].mo->eflags); - rsp->flags = LONG(players[i].mo->flags); - rsp->flags2 = LONG(players[i].mo->flags2); - - rsp->radius = LONG(players[i].mo->radius); - rsp->height = LONG(players[i].mo->height); - rsp->scale = LONG(players[i].mo->scale); - rsp->destscale = LONG(players[i].mo->destscale); - rsp->scalespeed = LONG(players[i].mo->scalespeed); -} - -static void resynch_read_player(resynch_pak *rsp) -{ - INT32 i = rsp->playernum, j; - mobj_t *savedmo = players[i].mo; - - // Do not send anything visual related. - // Only send data that we need to know for physics. - players[i].playerstate = (UINT8)rsp->playerstate; //playerstate_t - players[i].pflags = (UINT32)LONG(rsp->pflags); //pflags_t - players[i].panim = (UINT8)rsp->panim; //panim_t - - players[i].aiming = (angle_t)LONG(rsp->aiming); - players[i].currentweapon = LONG(rsp->currentweapon); - players[i].ringweapons = LONG(rsp->ringweapons); - - players[i].ammoremoval = (UINT16)SHORT(rsp->ammoremoval); - players[i].ammoremovaltimer = (tic_t)LONG(rsp->ammoremovaltimer); - players[i].ammoremovalweapon = LONG(rsp->ammoremovalweapon); - - for (j = 0; j < NUMPOWERS; ++j) - players[i].powers[j] = (UINT16)SHORT(rsp->powers[j]); - - // Score is resynched in the rspfirm resync packet - players[i].rings = SHORT(rsp->rings); - players[i].spheres = SHORT(rsp->spheres); - players[i].lives = rsp->lives; - players[i].continues = rsp->continues; - players[i].scoreadd = rsp->scoreadd; - players[i].xtralife = rsp->xtralife; - players[i].pity = rsp->pity; - - players[i].skincolor = rsp->skincolor; - players[i].skin = LONG(rsp->skin); - players[i].availabilities = LONG(rsp->availabilities); - // Just in case Lua does something like - // modify these at runtime - players[i].camerascale = (fixed_t)LONG(rsp->camerascale); - players[i].shieldscale = (fixed_t)LONG(rsp->shieldscale); - players[i].normalspeed = (fixed_t)LONG(rsp->normalspeed); - players[i].runspeed = (fixed_t)LONG(rsp->runspeed); - players[i].thrustfactor = rsp->thrustfactor; - players[i].accelstart = rsp->accelstart; - players[i].acceleration = rsp->acceleration; - players[i].charability = rsp->charability; - players[i].charability2 = rsp->charability2; - players[i].charflags = (UINT32)LONG(rsp->charflags); - players[i].thokitem = (UINT32)LONG(rsp->thokitem); //mobjtype_t - players[i].spinitem = (UINT32)LONG(rsp->spinitem); //mobjtype_t - players[i].revitem = (UINT32)LONG(rsp->revitem); //mobjtype_t - players[i].followitem = (UINT32)LONG(rsp->followitem); //mobjtype_t - players[i].actionspd = (fixed_t)LONG(rsp->actionspd); - players[i].mindash = (fixed_t)LONG(rsp->mindash); - players[i].maxdash = (fixed_t)LONG(rsp->maxdash); - players[i].jumpfactor = (fixed_t)LONG(rsp->jumpfactor); - players[i].height = (fixed_t)LONG(rsp->playerheight); - players[i].spinheight = (fixed_t)LONG(rsp->playerspinheight); - - players[i].speed = (fixed_t)LONG(rsp->speed); - players[i].secondjump = rsp->secondjump; - players[i].fly1 = rsp->fly1; - players[i].glidetime = (tic_t)LONG(rsp->glidetime); - players[i].climbing = rsp->climbing; - players[i].deadtimer = rsp->deadtimer; - players[i].exiting = (tic_t)LONG(rsp->exiting); - players[i].homing = rsp->homing; - players[i].dashmode = (tic_t)LONG(rsp->dashmode); - players[i].skidtime = (tic_t)LONG(rsp->skidtime); - players[i].cmomx = (fixed_t)LONG(rsp->cmomx); - players[i].cmomy = (fixed_t)LONG(rsp->cmomy); - players[i].rmomx = (fixed_t)LONG(rsp->rmomx); - players[i].rmomy = (fixed_t)LONG(rsp->rmomy); - - players[i].weapondelay = LONG(rsp->weapondelay); - players[i].tossdelay = LONG(rsp->tossdelay); - - players[i].starpostx = SHORT(rsp->starpostx); - players[i].starposty = SHORT(rsp->starposty); - players[i].starpostz = SHORT(rsp->starpostz); - players[i].starpostnum = LONG(rsp->starpostnum); - players[i].starposttime = (tic_t)LONG(rsp->starposttime); - players[i].starpostangle = (angle_t)LONG(rsp->starpostangle); - players[i].starpostscale = (fixed_t)LONG(rsp->starpostscale); - - players[i].maxlink = LONG(rsp->maxlink); - players[i].dashspeed = (fixed_t)LONG(rsp->dashspeed); - players[i].angle_pos = (angle_t)LONG(rsp->angle_pos); - players[i].old_angle_pos = (angle_t)LONG(rsp->old_angle_pos); - players[i].bumpertime = (tic_t)LONG(rsp->bumpertime); - players[i].flyangle = LONG(rsp->flyangle); - players[i].drilltimer = (tic_t)LONG(rsp->drilltimer); - players[i].linkcount = LONG(rsp->linkcount); - players[i].linktimer = (tic_t)LONG(rsp->linktimer); - players[i].anotherflyangle = LONG(rsp->anotherflyangle); - players[i].nightstime = (tic_t)LONG(rsp->nightstime); - players[i].drillmeter = LONG(rsp->drillmeter); - players[i].drilldelay = rsp->drilldelay; - players[i].bonustime = rsp->bonustime; - players[i].mare = rsp->mare; - players[i].lastsidehit = SHORT(rsp->lastsidehit); - players[i].lastlinehit = SHORT(rsp->lastlinehit); - - players[i].losstime = (tic_t)LONG(rsp->losstime); - players[i].timeshit = rsp->timeshit; - players[i].onconveyor = LONG(rsp->onconveyor); - - //We get a packet for each player in game. - if (!playeringame[i]) - return; - - //...but keep old mo even if it is corrupt or null! - players[i].mo = savedmo; - - //Transfer important mo information if they have a valid mo. - if (!rsp->hasmo) - return; - - //server thinks player has a body. - //Give them a new body that can be then manipulated by the server's info. - if (!players[i].mo) //client thinks it has no body. - P_SpawnPlayer(i); - - //At this point, the player should have a body, whether they were respawned or not. - P_UnsetThingPosition(players[i].mo); - players[i].mo->angle = (angle_t)LONG(rsp->angle); - players[i].mo->rollangle = (angle_t)LONG(rsp->rollangle); - players[i].mo->eflags = (UINT16)SHORT(rsp->eflags); - players[i].mo->flags = LONG(rsp->flags); - players[i].mo->flags2 = LONG(rsp->flags2); - players[i].mo->friction = LONG(rsp->friction); - players[i].mo->health = LONG(rsp->health); - players[i].mo->momx = LONG(rsp->momx); - players[i].mo->momy = LONG(rsp->momy); - players[i].mo->momz = LONG(rsp->momz); - players[i].mo->movefactor = LONG(rsp->movefactor); - - // Don't use P_SetMobjStateNF to restore state, write/read all the values manually! - // This should stop those stupid console errors, hopefully. - // -- Monster Iestyn - players[i].mo->sprite = (spritenum_t)LONG(rsp->sprite); - players[i].mo->frame = LONG(rsp->frame); - players[i].mo->sprite2 = rsp->sprite2; - players[i].mo->anim_duration = SHORT(rsp->anim_duration); - players[i].mo->tics = LONG(rsp->tics); - players[i].mo->state = &states[LONG(rsp->statenum)]; - - players[i].mo->x = LONG(rsp->x); - players[i].mo->y = LONG(rsp->y); - players[i].mo->z = LONG(rsp->z); - players[i].mo->radius = LONG(rsp->radius); - players[i].mo->height = LONG(rsp->height); - // P_SetScale is redundant for this, as all related variables are already restored properly. - players[i].mo->scale = LONG(rsp->scale); - players[i].mo->destscale = LONG(rsp->destscale); - players[i].mo->scalespeed = LONG(rsp->scalespeed); - - // And finally, SET THE MOBJ SKIN damn it. - if ((players[i].powers[pw_carry] == CR_NIGHTSMODE) && (skins[players[i].skin].sprites[SPR2_NFLY].numframes == 0)) - { - players[i].mo->skin = &skins[DEFAULTNIGHTSSKIN]; - players[i].mo->color = skins[DEFAULTNIGHTSSKIN].prefcolor; // this will be corrected by thinker to super flash - } - else - { - players[i].mo->skin = &skins[players[i].skin]; - players[i].mo->color = players[i].skincolor; // this will be corrected by thinker to super flash/mario star - } - - P_SetThingPosition(players[i].mo); -} - -static inline void resynch_write_ctf(resynchend_pak *rst) -{ - mobj_t *mflag; - UINT8 i, j; - - for (i = 0, mflag = redflag; i < 2; ++i, mflag = blueflag) - { - rst->flagx[i] = rst->flagy[i] = rst->flagz[i] = 0; - rst->flagloose[i] = rst->flagflags[i] = 0; - rst->flagplayer[i] = -1; - - if (!mflag) - { - // Should be held by a player - for (j = 0; j < MAXPLAYERS; ++j) - { - // GF_REDFLAG is 1, GF_BLUEFLAG is 2 - // redflag handling is i=0, blueflag is i=1 - // so check for gotflag == (i+1) - if (!playeringame[j] || players[j].gotflag != (i+1)) - continue; - rst->flagplayer[i] = (SINT8)j; - break; - } - if (j == MAXPLAYERS) // fine, no I_Error - { - CONS_Alert(CONS_ERROR, "One of the flags has gone completely missing...\n"); - rst->flagplayer[i] = -2; - } - continue; - } - - rst->flagx[i] = (fixed_t)LONG(mflag->x); - rst->flagy[i] = (fixed_t)LONG(mflag->y); - rst->flagz[i] = (fixed_t)LONG(mflag->z); - rst->flagflags[i] = LONG(mflag->flags2); - rst->flagloose[i] = LONG(mflag->fuse); // Dropped or not? - } -} - -static inline void resynch_read_ctf(resynchend_pak *p) -{ - UINT8 i; - - for (i = 0; i < MAXPLAYERS; ++i) - players[i].gotflag = 0; - - // Red flag - if (p->flagplayer[0] == -2) - ; // The server doesn't even know what happened to it... - else if (p->flagplayer[0] != -1) // Held by a player - { - if (!playeringame[p->flagplayer[0]]) - I_Error("Invalid red flag player %d who isn't in the game!", (INT32)p->flagplayer[0]); - players[p->flagplayer[0]].gotflag = GF_REDFLAG; - if (redflag) - { - P_RemoveMobj(redflag); - redflag = NULL; - } - } - else - { - if (!redflag) - redflag = P_SpawnMobj(0,0,0,MT_REDFLAG); - - P_UnsetThingPosition(redflag); - redflag->x = (fixed_t)LONG(p->flagx[0]); - redflag->y = (fixed_t)LONG(p->flagy[0]); - redflag->z = (fixed_t)LONG(p->flagz[0]); - redflag->flags2 = LONG(p->flagflags[0]); - redflag->fuse = LONG(p->flagloose[0]); - P_SetThingPosition(redflag); - } - - // Blue flag - if (p->flagplayer[1] == -2) - ; // The server doesn't even know what happened to it... - else if (p->flagplayer[1] != -1) // Held by a player - { - if (!playeringame[p->flagplayer[1]]) - I_Error("Invalid blue flag player %d who isn't in the game!", (INT32)p->flagplayer[1]); - players[p->flagplayer[1]].gotflag = GF_BLUEFLAG; - if (blueflag) - { - P_RemoveMobj(blueflag); - blueflag = NULL; - } - } - else - { - if (!blueflag) - blueflag = P_SpawnMobj(0,0,0,MT_BLUEFLAG); - - P_UnsetThingPosition(blueflag); - blueflag->x = (fixed_t)LONG(p->flagx[1]); - blueflag->y = (fixed_t)LONG(p->flagy[1]); - blueflag->z = (fixed_t)LONG(p->flagz[1]); - blueflag->flags2 = LONG(p->flagflags[1]); - blueflag->fuse = LONG(p->flagloose[1]); - P_SetThingPosition(blueflag); - } -} - -static inline void resynch_write_others(resynchend_pak *rst) -{ - UINT8 i; - - rst->ingame = 0; - rst->outofcoop = 0; - - for (i = 0; i < MAXPLAYERS; ++i) - { - if (!playeringame[i]) - { - rst->ctfteam[i] = 0; - rst->score[i] = 0; - rst->numboxes[i] = 0; - rst->totalring[i] = 0; - rst->realtime[i] = 0; - rst->laps[i] = 0; - continue; - } - - if (!players[i].spectator) - rst->ingame |= (1<outofcoop |= (1<ctfteam[i] = (INT32)LONG(players[i].ctfteam); - rst->score[i] = (UINT32)LONG(players[i].score); - rst->numboxes[i] = SHORT(players[i].numboxes); - rst->totalring[i] = SHORT(players[i].totalring); - rst->realtime[i] = (tic_t)LONG(players[i].realtime); - rst->laps[i] = players[i].laps; - } - - // endian safeness - rst->ingame = (UINT32)LONG(rst->ingame); -} - -static inline void resynch_read_others(resynchend_pak *p) -{ - UINT8 i; - UINT32 loc_ingame = (UINT32)LONG(p->ingame); - UINT32 loc_outofcoop = (UINT32)LONG(p->outofcoop); - - for (i = 0; i < MAXPLAYERS; ++i) - { - // We don't care if they're in the game or not, just write all the data. - players[i].spectator = !(loc_ingame & (1<ctfteam[i]); // no, 0 does not mean spectator, at least not in Match - players[i].score = (UINT32)LONG(p->score[i]); - players[i].numboxes = SHORT(p->numboxes[i]); - players[i].totalring = SHORT(p->totalring[i]); - players[i].realtime = (tic_t)LONG(p->realtime[i]); - players[i].laps = p->laps[i]; - } -} - -static void SV_InitResynchVars(INT32 node) -{ - resynch_delay[node] = TICRATE; // initial one second delay - resynch_score[node] = 0; // clean slate - resynch_status[node] = 0x00; - resynch_inprogress[node] = false; - memset(resynch_sent[node], 0, MAXPLAYERS); -} - -static void SV_RequireResynch(INT32 node) -{ - INT32 i; - - resynch_delay[node] = 10; // Delay before you can fail sync again - resynch_score[node] += 200; // Add score for initial desynch - resynch_status[node] = 0xFFFFFFFF; // No players assumed synched - resynch_inprogress[node] = true; // so we know to send a PT_RESYNCHEND after sync - - // Initial setup - memset(resynch_sent[node], 0, MAXPLAYERS); - for (i = 0; i < MAXPLAYERS; ++i) - { - if (!playeringame[i]) // Player not in game so just drop it from required synch - resynch_status[node] &= ~(1<>1)+1; - } -} - -static void SV_SendResynch(INT32 node) -{ - INT32 i, j; - - if (!nodeingame[node]) - { - // player left during resynch - // so obviously we don't need to do any of this anymore - resynch_inprogress[node] = false; - return; - } - - // resynched? - if (!resynch_status[node]) - { - // you are now synched - resynch_inprogress[node] = false; - - netbuffer->packettype = PT_RESYNCHEND; - - netbuffer->u.resynchend.randomseed = P_GetRandSeed(); - if (gametyperules & GTR_TEAMFLAGS) - resynch_write_ctf(&netbuffer->u.resynchend); - resynch_write_others(&netbuffer->u.resynchend); - - HSendPacket(node, true, 0, (sizeof(resynchend_pak))); - return; - } - - netbuffer->packettype = PT_RESYNCHING; - for (i = 0, j = 0; i < MAXPLAYERS; ++i) - { - // if already synched don't bother - if (!(resynch_status[node] & 1<u.resynchpak, i); - HSendPacket(node, false, 0, (sizeof(resynch_pak))); - - resynch_sent[node][i] = TICRATE; - resynch_score[node] += 2; // penalty for send - - if (++j > 3) - break; - } - - if (resynch_score[node] > (unsigned)cv_resynchattempts.value*250) - { - SendKick(nodetoplayer[node], KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - resynch_score[node] = 0; - } -} - -static void CL_AcknowledgeResynch(resynch_pak *rsp) -{ - resynch_read_player(rsp); - - netbuffer->packettype = PT_RESYNCHGET; - netbuffer->u.resynchgot = rsp->playernum; - HSendPacket(servernode, true, 0, sizeof(UINT8)); -} - -static void SV_AcknowledgeResynchAck(INT32 node, UINT8 rsg) -{ - if (rsg >= MAXPLAYERS) - resynch_score[node] += 16384; // lol. - else - { - resynch_status[node] &= ~(1<packettype = PT_RECEIVEDGAMESTATE; + HSendPacket(servernode, true, 0, 0); } static void CL_ReloadReceivedSavegame(void) @@ -2535,7 +1943,6 @@ void CL_Reset(void) multiplayer = false; servernode = 0; server = true; - resynch_local_inprogress = false; doomcom->numnodes = 1; doomcom->numslots = 1; SV_StopServer(); @@ -3112,7 +2519,7 @@ static void ResetNode(INT32 node) nodewaiting[node] = 0; playerpernode[node] = 0; sendingsavegame[node] = false; - SV_InitResynchVars(node); + resendingsavegame[node] = false; } void SV_ResetServer(void) @@ -3634,12 +3041,6 @@ 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); - if (cv_joinnextround.value && gameaction == ga_nothing) G_SetGamestate(GS_WAITINGPLAYERS); if (!SV_SendServerConfig(node)) @@ -3762,6 +3163,7 @@ static void PT_CanReceiveGamestate(SINT8 node) CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[nodetoplayer[node]]); SV_SendSaveGame(node, true); // Resend a complete game state + resendingsavegame[node] = true; } /** Handles a packet received from a node that isn't in game @@ -3976,11 +3378,6 @@ static void HandlePacketFromPlayer(SINT8 node) switch (netbuffer->packettype) { // -------------------------------------------- SERVER RECEIVE ---------- - case PT_RESYNCHGET: - if (client) - break; - SV_AcknowledgeResynchAck(netconsole, netbuffer->u.resynchgot); - break; case PT_CLIENTCMD: case PT_CLIENT2CMD: case PT_CLIENTMIS: @@ -3990,10 +3387,6 @@ static void HandlePacketFromPlayer(SINT8 node) if (client) break; - // Ignore tics from those not synched - if (resynch_inprogress[node] && nettics[node] == gametic) - 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); @@ -4020,9 +3413,6 @@ static void HandlePacketFromPlayer(SINT8 node) || netbuffer->packettype == PT_NODEKEEPALIVEMIS) break; - // If a client sends a ticcmd it should mean they are done receiving the savegame - sendingsavegame[node] = false; - // As long as clients send valid ticcmds, the server can keep running, so reset the timeout /// \todo Use a separate cvar for that kind of timeout? freezetimeout[node] = I_GetTime() + connectiontimeout; @@ -4047,21 +3437,19 @@ static void HandlePacketFromPlayer(SINT8 node) 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)) + && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy) + && !resendingsavegame[node]) { - SV_RequireResynch(node); - - if (cv_resynchattempts.value && resynch_score[node] <= (unsigned)cv_resynchattempts.value*250) + if (cv_resynchattempts.value) { + // Tell the client we are about to resend them the gamestate + netbuffer->packettype = PT_WILLRESENDGAMESTATE; + HSendPacket(node, true, 0, 0); + + resendingsavegame[node] = true; + if (cv_blamecfail.value) CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"), netconsole+1, player_names[netconsole], @@ -4081,8 +3469,6 @@ static void HandlePacketFromPlayer(SINT8 node) break; } } - else if (resynch_score[node]) - --resynch_score[node]; break; case PT_TEXTCMD2: // splitscreen special netconsole = nodetoplayer2[node]; @@ -4211,25 +3597,11 @@ static void HandlePacketFromPlayer(SINT8 node) case PT_CANRECEIVEGAMESTATE: PT_CanReceiveGamestate(node); break; -// -------------------------------------------- CLIENT RECEIVE ---------- - case PT_RESYNCHEND: - // Only accept PT_RESYNCHEND from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_RESYNCHEND", node); - if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - break; - } - resynch_local_inprogress = false; - - P_SetRandSeed(netbuffer->u.resynchend.randomseed); - - if (gametyperules & GTR_TEAMFLAGS) - resynch_read_ctf(&netbuffer->u.resynchend); - resynch_read_others(&netbuffer->u.resynchend); - + case PT_RECEIVEDGAMESTATE: + sendingsavegame[node] = false; + resendingsavegame[node] = false; break; +// -------------------------------------------- CLIENT RECEIVE ---------- case PT_SERVERTICS: // Only accept PT_SERVERTICS from the server. if (node != servernode) @@ -4293,18 +3665,6 @@ static void HandlePacketFromPlayer(SINT8 node) "IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/ } break; - case PT_RESYNCHING: - // Only accept PT_RESYNCHING from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_RESYNCHING", node); - if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - break; - } - resynch_local_inprogress = true; - CL_AcknowledgeResynch(&netbuffer->u.resynchpak); - break; case PT_PING: // Only accept PT_PING from the server. if (node != servernode) @@ -4832,7 +4192,7 @@ void TryRunTics(tic_t realtics) if (player_joining) return; - if (neededtic > gametic && !resynch_local_inprogress) + if (neededtic > gametic) { if (advancedemo) { @@ -4991,9 +4351,9 @@ void NetUpdate(void) if (cl_redownloadinggamestate && fileneeded[0].status == FS_FOUND) CL_ReloadReceivedSavegame(); - if (!(resynch_local_inprogress || cl_redownloadinggamestate)) + if (!cl_redownloadinggamestate) CL_SendClientCmd(); // Send tic cmd - hu_resynching = resynch_local_inprogress; + hu_redownloadinggamestate = cl_redownloadinggamestate; } else { @@ -5001,7 +4361,7 @@ void NetUpdate(void) { INT32 counts; - hu_resynching = false; + hu_redownloadinggamestate = false; firstticstosend = gametic; for (i = 0; i < MAXNETNODES; i++) @@ -5011,18 +4371,6 @@ void NetUpdate(void) // Don't erase tics not acknowledged counts = realtics; - for (i = 0; i < MAXNETNODES; ++i) - if (resynch_inprogress[i]) - { - if (!nodeingame[i] || nettics[i] == gametic) - { - SV_SendResynch(i); - counts = -666; - } - else - counts = 0; // Let the client catch up with the server - } - // Do not make tics while resynching if (counts != -666) { @@ -5040,7 +4388,7 @@ void NetUpdate(void) neededtic = maketic; // The server is a client too } else - hu_resynching = true; + hu_redownloadinggamestate = true; } } Net_AckTicker(); diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 0f1998d51..81ea17638 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -64,11 +64,10 @@ typedef enum PT_REQUESTFILE, // Client requests a file transfer PT_ASKINFOVIAMS, // Packet from the MS requesting info be sent to new client. // If this ID changes, update masterserver definition. - PT_RESYNCHEND, // Player is now resynched and is being requested to remake the gametic - PT_RESYNCHGET, // Player got resynch packet PT_WILLRESENDGAMESTATE, // Hey Client, I am about to resend you the gamestate! - PT_CANRECEIVEGAMESTATE, // Okay Server, I'm ready to receive it, you can go ahead. + PT_CANRECEIVEGAMESTATE, // Okay Server, I'm ready to receive it, you can go ahead. + PT_RECEIVEDGAMESTATE, // Thank you Server, I am ready to play again! // Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility. @@ -82,8 +81,6 @@ typedef enum PT_TEXTCMD2, // Splitscreen text commands. PT_CLIENTJOIN, // Client wants to join; used in start game. PT_NODETIMEOUT, // Packet sent to self if the connection times out. - PT_RESYNCHING, // Packet sent to resync players. - // Blocks game advance until synched. PT_LOGIN, // Login attempt from the client. @@ -136,165 +133,6 @@ typedef struct ticcmd_t cmds[45]; // Normally [BACKUPTIC][MAXPLAYERS] but too large } ATTRPACK servertics_pak; -// Sent to client when all consistency data -// for players has been restored -typedef struct -{ - UINT32 randomseed; - - // CTF flag stuff - SINT8 flagplayer[2]; - INT32 flagloose[2]; - INT32 flagflags[2]; - fixed_t flagx[2]; - fixed_t flagy[2]; - fixed_t flagz[2]; - - UINT32 ingame; // Spectator bit for each player - UINT32 outofcoop; // outofcoop bit for each player - INT32 ctfteam[MAXPLAYERS]; // Which team? (can't be 1 bit, since in regular Match there are no teams) - - // Resynch game scores and the like all at once - UINT32 score[MAXPLAYERS]; // Everyone's score - INT16 numboxes[MAXPLAYERS]; - INT16 totalring[MAXPLAYERS]; - tic_t realtime[MAXPLAYERS]; - UINT8 laps[MAXPLAYERS]; -} ATTRPACK resynchend_pak; - -typedef struct -{ - // 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 - - angle_t aiming; - INT32 currentweapon; - INT32 ringweapons; - UINT16 ammoremoval; - tic_t ammoremovaltimer; - INT32 ammoremovalweapon; - UINT16 powers[NUMPOWERS]; - - // Score is resynched in the confirm resync packet - INT16 rings; - INT16 spheres; - SINT8 lives; - SINT8 continues; - UINT8 scoreadd; - SINT8 xtralife; - SINT8 pity; - - UINT8 skincolor; - INT32 skin; - UINT32 availabilities; - // Just in case Lua does something like - // modify these at runtime - fixed_t camerascale; - fixed_t shieldscale; - fixed_t normalspeed; - fixed_t runspeed; - UINT8 thrustfactor; - UINT8 accelstart; - UINT8 acceleration; - UINT8 charability; - UINT8 charability2; - UINT32 charflags; - UINT32 thokitem; // mobjtype_t - UINT32 spinitem; // mobjtype_t - UINT32 revitem; // mobjtype_t - UINT32 followitem; // mobjtype_t - fixed_t actionspd; - fixed_t mindash; - fixed_t maxdash; - fixed_t jumpfactor; - fixed_t playerheight; - fixed_t playerspinheight; - - fixed_t speed; - UINT8 secondjump; - UINT8 fly1; - tic_t glidetime; - UINT8 climbing; - INT32 deadtimer; - tic_t exiting; - UINT8 homing; - tic_t dashmode; - tic_t skidtime; - fixed_t cmomx; - fixed_t cmomy; - fixed_t rmomx; - fixed_t rmomy; - - INT32 weapondelay; - INT32 tossdelay; - - INT16 starpostx; - INT16 starposty; - INT16 starpostz; - INT32 starpostnum; - tic_t starposttime; - angle_t starpostangle; - fixed_t starpostscale; - - INT32 maxlink; - fixed_t dashspeed; - angle_t angle_pos; - angle_t old_angle_pos; - tic_t bumpertime; - INT32 flyangle; - tic_t drilltimer; - INT32 linkcount; - tic_t linktimer; - INT32 anotherflyangle; - tic_t nightstime; - INT32 drillmeter; - UINT8 drilldelay; - UINT8 bonustime; - UINT8 mare; - INT16 lastsidehit, lastlinehit; - - tic_t losstime; - UINT8 timeshit; - INT32 onconveyor; - - //player->mo stuff - UINT8 hasmo; // Boolean - - INT32 health; - angle_t angle; - angle_t rollangle; - fixed_t x; - fixed_t y; - fixed_t z; - fixed_t momx; - fixed_t momy; - fixed_t momz; - fixed_t friction; - fixed_t movefactor; - - spritenum_t sprite; - UINT32 frame; - UINT8 sprite2; - UINT16 anim_duration; - INT32 tics; - statenum_t statenum; - UINT32 flags; - UINT32 flags2; - UINT16 eflags; - - fixed_t radius; - fixed_t height; - fixed_t scale; - fixed_t destscale; - fixed_t scalespeed; -} ATTRPACK resynch_pak; - typedef struct { UINT8 version; // Different versions don't work @@ -430,9 +268,6 @@ typedef struct 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 @@ -567,7 +402,7 @@ UINT8 GetFreeXCmdSize(void); void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest); -extern UINT8 hu_resynching; +extern UINT8 hu_redownloadinggamestate; extern UINT8 adminpassmd5[16]; extern boolean adminpasswordset; diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 98f3ca5a9..561c04677 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -2206,7 +2206,7 @@ void HU_Drawer(void) HU_DrawCrosshair2(); // draw desynch text - if (hu_resynching) + if (hu_redownloadinggamestate) { static UINT32 resynch_ticker = 0; char resynch_text[14]; From 46d8546f49335334092e0a67219bb914866d77b3 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Sun, 1 Mar 2020 03:26:15 +0100 Subject: [PATCH 06/14] Update packet names --- src/d_net.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/d_net.c b/src/d_net.c index 140226990..703e9810e 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -796,18 +796,15 @@ static const char *packettypename[NUMPACKETTYPE] = "REQUESTFILE", "ASKINFOVIAMS", - "RESYNCHEND", - "RESYNCHGET", - "WILLRESENDGAMESTATE", - "CANRESENDGAMESTATE", + "CANRECEIVEGAMESTATE", + "RECEIVEDGAMESTATE", "FILEFRAGMENT", "TEXTCMD", "TEXTCMD2", "CLIENTJOIN", "NODETIMEOUT", - "RESYNCHING", "PING" }; From b85ac6537804b7ea38ebb5cba69d3ae33d3ad50b Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Sun, 8 Mar 2020 20:04:29 +0100 Subject: [PATCH 07/14] Fix missing break --- src/d_clisrv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index de477c99b..9e0294152 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3604,6 +3604,7 @@ static void HandlePacketFromPlayer(SINT8 node) break; case PT_CANRECEIVEGAMESTATE: PT_CanReceiveGamestate(node); + break; #ifdef HAVE_BLUA case PT_ASKLUAFILE: if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED) From 9f5686ef48956cd64ef92f737e59cbbaa27d8cf9 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Mon, 26 Oct 2020 23:15:22 +0100 Subject: [PATCH 08/14] Fix underflow in consistancy checking code --- src/d_clisrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index cc2715cb1..e671bced6 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4067,7 +4067,7 @@ static void HandlePacketFromPlayer(SINT8 node) &netbuffer->u.client2pak.cmd2, 1); // Check player consistancy during the level - if (realstart <= gametic && realstart > gametic - BACKUPTICS+1 && gamestate == GS_LEVEL + if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy) && !resendingsavegame[node]) { From 7ae53364f2ea73f54bccf317339ac83499049a95 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Tue, 27 Oct 2020 01:20:05 +0100 Subject: [PATCH 09/14] Add a 15 seconds cooldown between successive gamestate resends --- src/d_clisrv.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index e671bced6..0823f3c8a 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -85,6 +85,7 @@ char playeraddress[MAXPLAYERS][64]; tic_t jointimeout = (10*TICRATE); static boolean sendingsavegame[MAXNETNODES]; // Are we sending the savegame? static boolean resendingsavegame[MAXNETNODES]; // Are we resending the savegame? +static tic_t savegameresendcooldown[MAXNETNODES]; // How long before we can resend again? static tic_t freezetimeout[MAXNETNODES]; // Until when can this node freeze the server before getting a timeout? // Incremented by cv_joindelay when a client joins, decremented each tic. @@ -3149,14 +3150,18 @@ void D_ClientServerInit(void) static void ResetNode(INT32 node) { nodeingame[node] = false; - nodetoplayer[node] = -1; - nodetoplayer2[node] = -1; + nodewaiting[node] = 0; + nettics[node] = gametic; supposedtics[node] = gametic; - nodewaiting[node] = 0; + + nodetoplayer[node] = -1; + nodetoplayer2[node] = -1; playerpernode[node] = 0; + sendingsavegame[node] = false; resendingsavegame[node] = false; + savegameresendcooldown[node] = 0; } void SV_ResetServer(void) @@ -4069,7 +4074,7 @@ static void HandlePacketFromPlayer(SINT8 node) // Check player consistancy during the level if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy) - && !resendingsavegame[node]) + && !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime()) { if (cv_resynchattempts.value) { @@ -4237,6 +4242,7 @@ static void HandlePacketFromPlayer(SINT8 node) case PT_RECEIVEDGAMESTATE: sendingsavegame[node] = false; resendingsavegame[node] = false; + savegameresendcooldown[node] = I_GetTime() + 15 * TICRATE; break; // -------------------------------------------- CLIENT RECEIVE ---------- case PT_SERVERTICS: From 499bb56436c7571d3247a5a813af2a84a58c5913 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Tue, 27 Oct 2020 01:22:31 +0100 Subject: [PATCH 10/14] Only resend the gamestate to one client at a time --- src/d_clisrv.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 0823f3c8a..af7907e67 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1391,6 +1391,16 @@ static boolean SV_SendServerConfig(INT32 node) #ifndef NONET #define SAVEGAMESIZE (768*1024) +static boolean SV_ResendingSavegameToAnyone(void) +{ + INT32 i; + + for (i = 0; i < MAXNETNODES; i++) + if (resendingsavegame[i]) + return true; + return false; +} + static void SV_SendSaveGame(INT32 node, boolean resending) { size_t length, compressedlen; @@ -4074,7 +4084,8 @@ static void HandlePacketFromPlayer(SINT8 node) // Check player consistancy during the level if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy) - && !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime()) + && !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime() + && !SV_ResendingSavegameToAnyone()) { if (cv_resynchattempts.value) { From 5c48b92b2c423b910829521546a01bf7df5fbfd4 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Tue, 27 Oct 2020 20:21:56 +0100 Subject: [PATCH 11/14] Fix camera going wild after reloading the gamestate --- src/d_clisrv.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index af7907e67..30c558f2f 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1607,6 +1607,14 @@ static void CL_ReloadReceivedSavegame(void) neededtic = gametic; maketic = neededtic; + ticcmd_oldangleturn[0] = players[consoleplayer].oldrelangleturn; + P_ForceLocalAngle(&players[consoleplayer], (angle_t)(players[consoleplayer].angleturn << 16)); + if (splitscreen) + { + ticcmd_oldangleturn[1] = players[secondarydisplayplayer].oldrelangleturn; + P_ForceLocalAngle(&players[secondarydisplayplayer], (angle_t)(players[secondarydisplayplayer].angleturn << 16)); + } + camera.subsector = R_PointInSubsector(camera.x, camera.y); camera2.subsector = R_PointInSubsector(camera2.x, camera2.y); From 804ad44e89324eb501db9a1278a73af9ae850862 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Tue, 27 Oct 2020 20:22:15 +0100 Subject: [PATCH 12/14] Fix music resetting after reloading the gamestate --- src/p_setup.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index d36401ac3..ea4a24f35 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -4038,18 +4038,20 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) // Fade out music here. Deduct 2 tics so the fade volume actually reaches 0. // But don't halt the music! S_Start will take care of that. This dodges a MIDI crash bug. - if (!titlemapinaction && (RESETMUSIC || + if (!(reloadinggamestate || titlemapinaction) && (RESETMUSIC || strnicmp(S_MusicName(), (mapmusflags & MUSIC_RELOADRESET) ? mapheaderinfo[gamemap-1]->musname : mapmusname, 7))) + { S_FadeMusic(0, FixedMul( FixedDiv((F_GetWipeLength(wipedefs[wipe_level_toblack])-2)*NEWTICRATERATIO, NEWTICRATE), MUSICRATE)); + } // Let's fade to black here // But only if we didn't do the special stage wipe if (rendermode != render_none && !(ranspecialwipe || reloadinggamestate)) P_RunLevelWipe(); - if (!titlemapinaction) + if (!(reloadinggamestate || titlemapinaction)) { if (ranspecialwipe == 2) { From 395d1f1b8f38e0dc8c00cf29318cceffc1b9862b Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Tue, 27 Oct 2020 20:23:32 +0100 Subject: [PATCH 13/14] Do not pause the client while redownloading the gamestate --- src/d_clisrv.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 30c558f2f..aa18ee380 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4274,9 +4274,6 @@ static void HandlePacketFromPlayer(SINT8 node) break; } - if (cl_redownloadinggamestate) - break; - realstart = netbuffer->u.serverpak.starttic; realend = realstart + netbuffer->u.serverpak.numtics; @@ -5001,8 +4998,7 @@ void NetUpdate(void) if (cl_redownloadinggamestate && fileneeded[0].status == FS_FOUND) CL_ReloadReceivedSavegame(); - if (!cl_redownloadinggamestate) - CL_SendClientCmd(); // Send tic cmd + CL_SendClientCmd(); // Send tic cmd hu_redownloadinggamestate = cl_redownloadinggamestate; } else From e1789663671ca2318f732d3d0b475d9a8778730e Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Tue, 27 Oct 2020 20:28:54 +0100 Subject: [PATCH 14/14] Remove useless condition --- src/d_clisrv.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index aa18ee380..a3fbe88d9 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5022,24 +5022,18 @@ void NetUpdate(void) // Don't erase tics not acknowledged counts = realtics; - // Do not make tics while resynching - if (counts != -666) - { - if (maketic + counts >= firstticstosend + BACKUPTICS) - counts = firstticstosend+BACKUPTICS-maketic-1; + if (maketic + counts >= firstticstosend + BACKUPTICS) + counts = firstticstosend+BACKUPTICS-maketic-1; - for (i = 0; i < counts; i++) - SV_Maketic(); // Create missed tics and increment maketic + for (i = 0; i < counts; i++) + SV_Maketic(); // Create missed tics and increment maketic - for (; tictoclear < firstticstosend; tictoclear++) // Clear only when acknowledged - 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(); + SV_SendTics(); - neededtic = maketic; // The server is a client too - } - else - hu_redownloadinggamestate = true; + neededtic = maketic; // The server is a client too } }