From d02c4c0cc7e0d9e9bc776ecf5f89d58191cb697f Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Fri, 31 Jan 2020 15:57:04 +0100 Subject: [PATCH] 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