From 09b90c3cf1745bfbed9609e906474d4de2f07de1 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 29 Jan 2024 02:16:03 -0800 Subject: [PATCH 1/5] Replays: add netdemo_size cvar, add to Data Options Controls size of replay buffer when recording --- src/cvars.cpp | 1 + src/g_demo.c | 9 +++------ src/k_menu.h | 1 + src/menus/options-data-1.c | 5 +++++ 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/cvars.cpp b/src/cvars.cpp index 4f4ab8e16..db0bebc84 100644 --- a/src/cvars.cpp +++ b/src/cvars.cpp @@ -537,6 +537,7 @@ consvar_t cv_maxping = Server("maxdelay", "20").values(CV_Unsigned); consvar_t cv_menujam = Server("menujam", "menu").values({{0, "menu"}, {1, "menu2"}, {2, "menu3"}}); consvar_t cv_menujam_update = Server("menujam_update", "Off").on_off(); consvar_t cv_netdemosyncquality = Server("netdemo_syncquality", "1").min_max(1, 35); +consvar_t cv_netdemosize = Server("netdemo_size", "4").values(CV_Natural); void NetTimeout_OnChange(void); consvar_t cv_nettimeout = Server("nettimeout", "210").min_max(TICRATE/7, 60*TICRATE).onchange(NetTimeout_OnChange); diff --git a/src/g_demo.c b/src/g_demo.c index 6fd166db1..a10d09abc 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -2088,16 +2088,13 @@ void G_WriteMetalTic(mobj_t *metal) // void G_RecordDemo(const char *name) { + extern consvar_t cv_netdemosize; + INT32 maxsize; strcpy(demoname, name); strcat(demoname, ".lmp"); - //@TODO make a maxdemosize cvar - // NOPE. We are kicking this can HELLA down the road. -Tyron 2024-01-20 - maxsize = 1024*1024*4; - - if (M_CheckParm("-maxdemo") && M_IsNextParm()) - maxsize = atoi(M_GetNextParm()) * 1024; + maxsize = 1024 * 1024 * cv_netdemosize.value; // if (demobuf.buffer) // Z_Free(demobuf.buffer); diff --git a/src/k_menu.h b/src/k_menu.h index da1b68f61..a623aedb3 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -342,6 +342,7 @@ typedef enum dopt_replay, dopt_rprecord, dopt_rpsync, + dopt_rpsize, #ifdef HAVE_DISCORDRPC dopt_discord, dopt_drp, diff --git a/src/menus/options-data-1.c b/src/menus/options-data-1.c index 97393948a..fd7f830d6 100644 --- a/src/menus/options-data-1.c +++ b/src/menus/options-data-1.c @@ -4,6 +4,8 @@ #include "../k_menu.h" #include "../discord.h" // discord rpc cvars +extern consvar_t cv_netdemosize; + // data options menu -- see dopt_e menuitem_t OPTIONS_Data[] = { @@ -26,6 +28,9 @@ menuitem_t OPTIONS_Data[] = {IT_STRING | IT_CVAR, "Net Consistency Quality", "For filesize, how often do we write position data in online replays?", NULL, {.cvar = &cv_netdemosyncquality}, 0, 0}, + {IT_STRING | IT_CVAR, "Buffer Size (MB)", "Lets replays last longer with more players. Uses more RAM.", + NULL, {.cvar = &cv_netdemosize}, 0, 0}, + #ifdef HAVE_DISCORDRPC {IT_HEADER, "Discord Rich Presence...", NULL, NULL, {NULL}, 0, 0}, From a879975e83d505d191495bf830fda482d3b4e5fb Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 29 Jan 2024 02:20:23 -0800 Subject: [PATCH 2/5] devmode DEMO: replay buffer usage displayed in real-time on the HUD - Buffer usage in megabytes, bytes and percentage - Approximate usage per second - Estimated time until buffer runs out and replay is stopped --- src/doomdef.h | 1 + src/g_demo.c | 2 ++ src/g_demo.h | 2 ++ src/m_cheat.c | 2 ++ src/st_stuff.c | 30 ++++++++++++++++++++++++++++++ 5 files changed, 37 insertions(+) diff --git a/src/doomdef.h b/src/doomdef.h index ffec260ef..21e8bdb4f 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -548,6 +548,7 @@ typedef enum DBG_SETUP = 0x00000400, DBG_LUA = 0x00000800, DBG_RNG = 0x00001000, + DBG_DEMO = 0x00002000, } debugFlags_t; struct debugFlagNames_s diff --git a/src/g_demo.c b/src/g_demo.c index a10d09abc..231a43b19 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -2106,6 +2106,7 @@ void G_RecordDemo(const char *name) demobuf.p = NULL; demo.recording = true; + demo.buffer = &demobuf; } void G_RecordMetal(void) @@ -3243,6 +3244,7 @@ void G_DoPlayDemo(const char *defdemoname) // read demo header gameaction = ga_nothing; demo.playback = true; + demo.buffer = &demobuf; if (memcmp(demobuf.p, DEMOHEADER, 12)) { snprintf(msg, 1024, M_GetText("%s is not a Ring Racers replay file.\n"), pdemoname); diff --git a/src/g_demo.h b/src/g_demo.h index f102e8517..e19f8f820 100644 --- a/src/g_demo.h +++ b/src/g_demo.h @@ -65,6 +65,8 @@ struct demovars_s { UINT8 numskins; democharlist_t *skinlist; UINT8 currentskinid[MAXPLAYERS]; + + const savebuffer_t *buffer; // debug, valid only if recording or playback }; extern struct demovars_s demo; diff --git a/src/m_cheat.c b/src/m_cheat.c index f9eb97cd8..c623a8f1e 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -605,6 +605,8 @@ struct debugFlagNames_s const debug_flag_names[] = {"Music", DBG_MUSIC}, {"PwrLv", DBG_PWRLV}, {"PowerLevel", DBG_PWRLV}, // alt name + {"Demo", DBG_DEMO}, + {"Replay", DBG_DEMO}, // alt name {NULL, 0} }; diff --git a/src/st_stuff.c b/src/st_stuff.c index 50e3ba29f..8e2505ef5 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -448,6 +448,31 @@ static void ST_drawRenderDebug(INT32 *height) ST_pushDebugString(height, va("Skybox Portals: %4s", sizeu1(i->skybox_portals))); } +static void ST_drawDemoDebug(INT32 *height) +{ + if (!demo.recording && !demo.playback) + return; + + size_t needle = demo.buffer->p - demo.buffer->buffer; + size_t size = demo.buffer->size; + double percent = (double)needle / size * 100.0; + double avg = (double)needle / leveltime; + + ST_pushDebugString(height, va("%s/%s bytes", sizeu1(needle), sizeu2(size))); + ST_pushDebugString(height, va( + "%.2f/%.2f MB %5.2f%%", + needle / (1024.0 * 1024.0), + size / (1024.0 * 1024.0), + percent + )); + ST_pushDebugString(height, va( + "%.2f KB/s (ETA %.2f minutes)", + avg * TICRATE / 1024.0, + (size - needle) / (avg * TICRATE * 60.0) + )); + ST_pushDebugString(height, va("Demo (%s)", demo.recording ? "recording" : "playback")); +} + void ST_drawDebugInfo(void) { INT32 height = 192; @@ -547,6 +572,11 @@ void ST_drawDebugInfo(void) ST_drawRenderDebug(&height); } + if (cht_debug & DBG_DEMO) + { + ST_drawDemoDebug(&height); + } + if (cht_debug & DBG_MEMORY) V_DrawRightAlignedString(320, height, V_MONOSPACE, va("Heap used: %7sKB", sizeu1(Z_TagsUsage(0, INT32_MAX)>>10))); } From 5a86041302cb362c768bf60e5291e4e43b8f52b8 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 29 Jan 2024 02:24:55 -0800 Subject: [PATCH 3/5] G_WriteAllGhostTics: check buffer size after each player's ghost data Instead of checking it after ALL player ghost data. This is less likely to write past the end of the demo buffer. --- src/g_demo.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/g_demo.c b/src/g_demo.c index 231a43b19..dc3682000 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -734,6 +734,7 @@ void G_GhostAddHit(INT32 playernum, mobj_t *victim) void G_WriteAllGhostTics(void) { + boolean toobig = false; INT32 i, counter = leveltime; for (i = 0; i < MAXPLAYERS; i++) { @@ -750,12 +751,18 @@ void G_WriteAllGhostTics(void) WRITEUINT8(demobuf.p, i); G_WriteGhostTic(players[i].mo, i); + + // attention here for the ticcmd size! + // latest demos with mouse aiming byte in ticcmd + if (demobuf.p >= demobuf.end - (13 + 9 + 9)) + { + toobig = true; + break; + } } WRITEUINT8(demobuf.p, 0xFF); - // attention here for the ticcmd size! - // latest demos with mouse aiming byte in ticcmd - if (demobuf.p >= demobuf.end - (13 + 9 + 9)) + if (toobig) { G_CheckDemoStatus(); // no more space return; From 1769b418df9e1d461d050db426d59378d245373d Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 29 Jan 2024 02:26:13 -0800 Subject: [PATCH 4/5] Replays: reserve 1 KB of space at end of buffer as a safeguard This should avoid buffer overruns in the middle of recording. There is already code that checks for buffer size around ticcmd and ghost data write. Demo header is still unsafe with many WAD filenames written, for example. Ghost data and ticcmd should not come close to reaching into this extra space. At the time of writing, ghost data can write up to 102 bytes and ticcmd 20 bytes, per player. --- src/g_demo.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/g_demo.c b/src/g_demo.c index dc3682000..b46970c90 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -2114,6 +2114,20 @@ void G_RecordDemo(const char *name) demo.recording = true; demo.buffer = &demobuf; + + /* FIXME: This whole file is in a wretched state. Take a + look at G_WriteAllGhostTics and G_WriteDemoTiccmd, they + write a lot of data. It's not realistic to refactor that + code in order to know exactly HOW MANY bytes it can write + out. So here's the deal. Reserve a decent block of memory + at the end of the buffer and never use it. Those bastard + functions will check if they overran the buffer, but it + should be safe enough because they'll think there's less + memory than there actually is and stop early. */ + const size_t deadspace = 1024; + I_Assert(demobuf.size > deadspace); + demobuf.size -= deadspace; + demobuf.end -= deadspace; } void G_RecordMetal(void) From eba9df570dbaf40fa476c32f5eeff4cd2178a098 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 29 Jan 2024 02:31:54 -0800 Subject: [PATCH 5/5] Replays: raise default buffer size from 4 to 6 MB --- src/cvars.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cvars.cpp b/src/cvars.cpp index db0bebc84..9a9082e17 100644 --- a/src/cvars.cpp +++ b/src/cvars.cpp @@ -537,7 +537,7 @@ consvar_t cv_maxping = Server("maxdelay", "20").values(CV_Unsigned); consvar_t cv_menujam = Server("menujam", "menu").values({{0, "menu"}, {1, "menu2"}, {2, "menu3"}}); consvar_t cv_menujam_update = Server("menujam_update", "Off").on_off(); consvar_t cv_netdemosyncquality = Server("netdemo_syncquality", "1").min_max(1, 35); -consvar_t cv_netdemosize = Server("netdemo_size", "4").values(CV_Natural); +consvar_t cv_netdemosize = Server("netdemo_size", "6").values(CV_Natural); void NetTimeout_OnChange(void); consvar_t cv_nettimeout = Server("nettimeout", "210").min_max(TICRATE/7, 60*TICRATE).onchange(NetTimeout_OnChange);