From 45bbb4826a727640b0676c7b93c848acc019702c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 22 Oct 2024 02:22:31 -0400 Subject: [PATCH] DUMPCONSISTENCY for the modern age - dumpconsistency cvar is always enabled (rather than a define), but is now a cheat. - It now dumps on resend, instead of on consistency failure kick. (Those don't even happen on too many resyncs anymore, anyways...) - It now dumps for both the server & the client that is resyncing, so there's gamestates to compare. The two files are given names with metadata so they can be matched up. It's not great, but it was easy enough to do and more useable than having 0 tools to inspect resync at all. --- src/cvars.cpp | 11 +++----- src/d_clisrv.c | 75 +++++++++++++++++++++++++++++++------------------- src/doomdef.h | 3 -- src/m_misc.cpp | 6 ++++ src/m_misc.h | 2 ++ 5 files changed, 59 insertions(+), 38 deletions(-) diff --git a/src/cvars.cpp b/src/cvars.cpp index de8859f4a..32dffe32e 100644 --- a/src/cvars.cpp +++ b/src/cvars.cpp @@ -244,10 +244,6 @@ X (cvlist_timer); X (cvlist_execversion); -#ifdef DUMPCONSISTENCY - X (cvlist_dumpconsistency); -#endif - #undef X namespace @@ -602,9 +598,10 @@ consvar_t cv_blamecfail = NetVar("blamecfail", "Off").on_off(); // Speed of file downloading (in packets per tic) consvar_t cv_downloadspeed = NetVar("downloadspeed", "32").min_max(1, 300); -#ifdef DUMPCONSISTENCY - consvar_t cv_dumpconsistency = NetVar(cvlist_dumpconsistency)("dumpconsistency", "Off").on_off(); -#endif +// Dump gamestates to an external file when a resync occurs. +// This is a cheat because enabling this can take up file storage +// for connected players very fast. +consvar_t cv_dumpconsistency = OnlineCheat("dumpconsistency", "Off").on_off(); // Intermission time Tails 04-19-2002 consvar_t cv_inttime = NetVar("inttime", "10").min_max(0, 3600); diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 30e4fe110..0ddb7ddcb 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1317,26 +1317,18 @@ static void SV_SendSaveGame(INT32 node, boolean resending) freezetimeout[node] = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte } -#ifdef DUMPCONSISTENCY -#define TMPSAVENAME "badmath.sav" - -static void SV_SavedGame(void) +static void CL_DumpConsistency(const char *file_name) { - extern consvar_t *cv_dumpconsistency; - size_t length; savebuffer_t save = {0}; - char tmpsave[256]; + char tmpsave[1024]; - if (!cv_dumpconsistency.value) - return; - - sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); + snprintf(tmpsave, sizeof(tmpsave), "%s" PATHSEP "%s", srb2home, file_name); // first save it in a malloced buffer if (P_SaveBufferAlloc(&save, NETSAVEGAMESIZE) == false) { - CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); + CONS_Alert(CONS_ERROR, M_GetText("No more free memory for consistency dump\n")); return; } @@ -1346,21 +1338,18 @@ static void SV_SavedGame(void) if (length > NETSAVEGAMESIZE) { P_SaveBufferFree(&save); - I_Error("Savegame buffer overrun"); + I_Error("Consistency dump buffer overrun"); } // then save it! if (!FIL_WriteFile(tmpsave, save.buffer, length)) - CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave); + CONS_Printf(M_GetText("Didn't save %s for consistency dump"), tmpsave); P_SaveBufferFree(&save); } -#undef TMPSAVENAME -#endif #define TMPSAVENAME "$$$.sav" - static void CL_LoadReceivedSavegame(boolean reloading) { savebuffer_t save = {0}; @@ -1451,8 +1440,13 @@ static void CL_LoadReceivedSavegame(boolean reloading) static void CL_ReloadReceivedSavegame(void) { - INT32 i; + extern consvar_t cv_dumpconsistency; + if (cv_dumpconsistency.value) + { + CL_DumpConsistency("TEMP.consdump"); + } + INT32 i; for (i = 0; i < MAXPLAYERS; i++) { LUA_InvalidatePlayer(&players[i]); @@ -1478,6 +1472,26 @@ static void CL_ReloadReceivedSavegame(void) cl_redownloadinggamestate = false; CONS_Printf(M_GetText("Game state reloaded\n")); + + if (cv_dumpconsistency.value) + { + // This is dumb, but we want the file names + // to be pairable together with the server's + // version, and gametic being randomly off + // is a deal breaker. + char dump_name[1024]; + snprintf( + dump_name, sizeof(dump_name), + "%s_%u_%s-client.consdump", + server_context, + gametic, + player_names[consoleplayer] + ); + if (FIL_RenameFile("TEMP.consdump", dump_name) == false) + { + CONS_Alert(CONS_WARNING, "Failed to rename temporary consdump file.\n"); + } + } } static void SendAskInfo(INT32 node) @@ -3074,9 +3088,6 @@ static void Got_KickCmd(const UINT8 **p, INT32 playernum) if (playernode[pnum] == playernode[consoleplayer]) { -#ifdef DUMPCONSISTENCY - if (msg == KICK_MSG_CON_FAIL) SV_SavedGame(); -#endif LUA_HookBool(false, HOOK(GameQuit)); //Lua hooks handled differently now Command_ExitGame_f(); @@ -3283,12 +3294,6 @@ void D_ClientServerInit(void) RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer); RegisterNetXCmd(XD_REMOVEPLAYER, Got_RemovePlayer); RegisterNetXCmd(XD_ADDBOT, Got_AddBot); -#ifdef DUMPCONSISTENCY - { - extern struct CVarList *cvlist_dumpconsistency; - CV_RegisterList(cvlist_dumpconsistency); - } -#endif gametic = 0; localgametic = 0; @@ -4361,7 +4366,7 @@ static void HandleServerInfo(SINT8 node) static void PT_WillResendGamestate(void) { - char tmpsave[256]; + char tmpsave[1024]; if (server || cl_redownloadinggamestate) return; @@ -4400,6 +4405,20 @@ static void PT_CanReceiveGamestate(SINT8 node) CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[nodetoplayer[node]]); + extern consvar_t cv_dumpconsistency; + if (cv_dumpconsistency.value) + { + char dump_name[1024]; + snprintf( + dump_name, sizeof(dump_name), + "%s_%u_%s-server.consdump", + server_context, + gametic, + player_names[nodetoplayer[node]] + ); + CL_DumpConsistency(dump_name); + } + SV_SendSaveGame(node, true); // Resend a complete game state resendingsavegame[node] = true; } diff --git a/src/doomdef.h b/src/doomdef.h index 3b0df2279..62a482f4b 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -663,9 +663,6 @@ extern int // None of these that are disabled in the normal build are guaranteed to work perfectly // Compile them at your own risk! -/// Dumps the contents of a network save game upon consistency failure for debugging. -//#define DUMPCONSISTENCY - /// Who put weights on my recycler? ... Inuyasha did. /// \note XMOD port. //#define WEIGHTEDRECYCLER diff --git a/src/m_misc.cpp b/src/m_misc.cpp index 9b488b93c..582455b35 100644 --- a/src/m_misc.cpp +++ b/src/m_misc.cpp @@ -341,6 +341,12 @@ boolean FIL_ConvertTextFileToBinary(const char *textfilename, const char *binfil return success; } +boolean FIL_RenameFile(char const *old_name, char const *new_name) +{ + int result = rename(old_name, new_name); + return (result == 0); +} + /** Check if the filename exists * * \param name Filename to check. diff --git a/src/m_misc.h b/src/m_misc.h index 4ef377074..75bb4a5c8 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -90,6 +90,8 @@ size_t FIL_ReadFileTag(char const *name, UINT8 **buffer, INT32 tag); boolean FIL_ConvertTextFileToBinary(const char *textfilename, const char *binfilename); +boolean FIL_RenameFile(const char *old_name, const char *new_name); + boolean FIL_FileExists(const char *name); boolean FIL_WriteFileOK(char const *name); boolean FIL_ReadFileOK(char const *name);