From 5b4fc1826440acaca3fc777d295c20a481ca171a Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 17 Jul 2023 18:06:57 +0100 Subject: [PATCH 1/7] P_PostLoadLevel: Move all events directly requiring finalised player lists into this function - Grand Prix (& backup) and Match Race bot initialisation - Fixes rival selection - P_InitPlayers - Has to be after bot init - Beginning of demo recording - Grand Prix and Match Race replays appear to both function far better, and equivalently to boot. - A desync in "RNG seed 24" is reported. This is PR_BOTS. This is the only notable desync I can see! - ACS_LoadLevelScripts - I'm not ironclad sold on this being the forever home, but it's also a not-from-netsave thing, and barring external input I could see some scripts being dependent on a finalised player list. --- src/d_netcmd.c | 6 ---- src/p_setup.c | 80 +++++++++++++++++++++++++++----------------------- 2 files changed, 44 insertions(+), 42 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 35c3fed88..30ac6377c 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3230,12 +3230,6 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) if (demo.timing) G_DoneLevelLoad(); - if (metalrecording) - G_BeginMetal(); - if (demo.recording) // Okay, level loaded, character spawned and skinned, - G_BeginRecording(); // I AM NOW READY TO RECORD. - demo.deferstart = true; - #ifdef HAVE_DISCORDRPC DRPC_UpdatePresence(); #endif diff --git a/src/p_setup.c b/src/p_setup.c index 27b771df1..4a6d41e73 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7789,33 +7789,6 @@ static void P_InitGametype(void) spectateGriefed = 0; K_CashInPowerLevels(); // Pushes power level changes even if intermission was skipped - if (grandprixinfo.gp == true) - { - if (savedata.lives > 0) - { - K_LoadGrandPrixSaveGame(); - savedata.lives = 0; - } - else if (grandprixinfo.initalize == true) - { - K_InitGrandPrixRank(&grandprixinfo.rank); - K_InitGrandPrixBots(); - grandprixinfo.initalize = false; - } - else if (grandprixinfo.wonround == true) - { - K_UpdateGrandPrixBots(); - grandprixinfo.wonround = false; - } - } - else - { - // We're in a Match Race, use simplistic randomized bots. - K_UpdateMatchRaceBots(); - } - - P_InitPlayers(); - if (modeattacking && !demo.playback) P_LoadRecordGhosts(); @@ -8344,12 +8317,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) K_InitDirector(); } - // Initialize ACS scripts - if (!fromnetsave) - { - ACS_LoadLevelScripts(gamemap-1); - } - // Remove the loading shit from the screen if (rendermode != render_none && !titlemapinaction && !reloadinggamestate) F_WipeColorFill(levelfadecol); @@ -8415,6 +8382,49 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) void P_PostLoadLevel(void) { + P_MapStart(); + + if (demo.playback) + ; + else if (grandprixinfo.gp == true) + { + if (savedata.lives > 0) + { + K_LoadGrandPrixSaveGame(); + savedata.lives = 0; + } + else if (grandprixinfo.initalize == true) + { + K_InitGrandPrixRank(&grandprixinfo.rank); + K_InitGrandPrixBots(); + grandprixinfo.initalize = false; + } + else if (grandprixinfo.wonround == true) + { + K_UpdateGrandPrixBots(); + grandprixinfo.wonround = false; + } + } + else + { + // We're in a Match Race, use simplistic randomized bots. + K_UpdateMatchRaceBots(); + } + + P_InitPlayers(); + + if (metalrecording) + G_BeginMetal(); + if (demo.recording) // Okay, level loaded, character spawned and skinned, + G_BeginRecording(); // I AM NOW READY TO RECORD. + demo.deferstart = true; + + // Initialize ACS scripts + //if (!fromnetsave) -- I don't know if it's appropriate to remove this entirely yet + { + ACS_LoadLevelScripts(gamemap-1); + } + K_TimerInit(); nextmapoverride = 0; @@ -8429,12 +8439,10 @@ void P_PostLoadLevel(void) marathonmode &= ~MA_INIT; } - P_MapStart(); // just in case MapLoad modifies tm.thing - ACS_RunLevelStartScripts(); LUA_HookInt(gamemap, HOOK(MapLoad)); - P_MapEnd(); // just in case MapLoad modifies tm.thing + P_MapEnd(); // We're now done loading the level. levelloading = false; From bf8d09980db86a940f15307b6fe38458aa6ee2bf Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 17 Jul 2023 18:09:11 +0100 Subject: [PATCH 2/7] G_DoPlayDemo: Make sure buffer is NULL so you can watch replays immediately after recording them. This is yet another g_demo.c-specific hack, and has been marked as such. --- src/g_demo.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/g_demo.c b/src/g_demo.c index 5b8a5854e..9b66237eb 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -3060,6 +3060,10 @@ void G_DoPlayDemo(const char *defdemoname) } else { + // FIXME: this file doesn't manage its memory and actually free this when it's done using it + Z_Free(demobuf.buffer); + demobuf.buffer = NULL; + n = defdemoname+strlen(defdemoname); while (*n != '/' && *n != '\\' && n != defdemoname) n--; From 859b3fa350f74d87ca1add7f85b1559db63da691 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 17 Jul 2023 18:20:22 +0100 Subject: [PATCH 3/7] On second thoughts, let's be EXTRA careful to avoid any potential double frees. --- src/g_demo.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/g_demo.c b/src/g_demo.c index 9b66237eb..d7f240f38 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -3061,7 +3061,7 @@ void G_DoPlayDemo(const char *defdemoname) else { // FIXME: this file doesn't manage its memory and actually free this when it's done using it - Z_Free(demobuf.buffer); + //Z_Free(demobuf.buffer); demobuf.buffer = NULL; n = defdemoname+strlen(defdemoname); @@ -4183,13 +4183,19 @@ boolean G_CheckDemoStatus(void) return true; } - if (demo.recording && (modeattacking || demo.savemode != DSM_NOTSAVING)) + if (!demo.recording) + return false; + + if (modeattacking || demo.savemode != DSM_NOTSAVING) { G_SaveDemo(); return true; } + Z_Free(demobuf.buffer); + demo.recording = false; + return false; } From 4885611f47a3b936095594232a912a4330c2ce3a Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 18 Jul 2023 10:20:42 +0100 Subject: [PATCH 4/7] Move ACS script loading back to P_DoLoadLevel, since on second thought it's just LOADING and not executing anything --- src/p_setup.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 4a6d41e73..758c4b425 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8304,8 +8304,13 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) // a netgame save is being loaded, and could actively be harmful by messing with // the client's view of the data.) if (!fromnetsave) + { P_InitGametype(); + // Initialize ACS scripts + ACS_LoadLevelScripts(gamemap-1); + } + // Now safe to free. vres_Free(curmapvirt); curmapvirt = NULL; @@ -8419,12 +8424,6 @@ void P_PostLoadLevel(void) G_BeginRecording(); // I AM NOW READY TO RECORD. demo.deferstart = true; - // Initialize ACS scripts - //if (!fromnetsave) -- I don't know if it's appropriate to remove this entirely yet - { - ACS_LoadLevelScripts(gamemap-1); - } - K_TimerInit(); nextmapoverride = 0; From e0b47b16e7d129a8fa7df839e6038bc0fdd9e126 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 22 Jul 2023 14:03:11 +0100 Subject: [PATCH 5/7] Improve Spectator entry handling - Re-entry SHOULDN'T get stuck nonzero on first join for a given node - Set to 0 for all players on level start - Set to 0 for all players on level end - DEFINITELY never intentionally set on join with teamchange finalisation - This basically just prevents people from spamming New Challenger after hopping out mid-session, since the previous version, in addition to being buggy, was a little annoying. - New Challenger no longer happens on tic 0 of mapload - Handles any late, pending PF_PF_WANTSTOJOINs without threat of reset in P_PostLoadLevel --- src/d_netcmd.c | 2 +- src/g_game.c | 4 +++- src/k_kart.c | 7 +++++-- src/k_kart.h | 2 +- src/p_mobj.c | 1 - src/p_setup.c | 5 +++++ 6 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 30ac6377c..f5e2f26e1 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1617,7 +1617,7 @@ static void FinalisePlaystateChange(INT32 playernum) // To attempt to discourage rage-spectators, we delay any rejoining. // If you're engaging in a DUEL and quit early, in addition to the // indignity of losing your PWR, you get a special extra-long delay. - if (netgame) + if (netgame && players[playernum].jointime > 1) { UINT8 pcount = 0; diff --git a/src/g_game.c b/src/g_game.c index 708479fbc..4a995e55e 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2374,7 +2374,7 @@ void G_Ticker(boolean run) && (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING // definitely good || gamestate == GS_WAITINGPLAYERS)) // definitely a problem if we don't do it at all in this gamestate, but might need more protection? { - K_CheckSpectateStatus(); + K_CheckSpectateStatus(true); } if (pausedelay && pausedelay != INT32_MIN) @@ -2436,6 +2436,8 @@ static inline void G_PlayerFinishLevel(INT32 player) p->starpostnum = 0; memset(&p->respawn, 0, sizeof (p->respawn)); + + p->spectatorReentry = 0; // Clean up any pending re-entry forbiddings } // diff --git a/src/k_kart.c b/src/k_kart.c index 4f570f3b5..d5077bbc2 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -11740,7 +11740,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) Obj_RingShooterInput(player); } -void K_CheckSpectateStatus(void) +void K_CheckSpectateStatus(boolean considermapreset) { UINT8 respawnlist[MAXPLAYERS]; UINT8 i, j, numingame = 0, numjoiners = 0; @@ -11770,7 +11770,7 @@ void K_CheckSpectateStatus(void) players[i].spectatewait = 0; } - if (gamestate != GS_LEVEL) + if (gamestate != GS_LEVEL || considermapreset == false) { players[i].spectatorReentry = 0; } @@ -11881,6 +11881,9 @@ void K_CheckSpectateStatus(void) break; } + if (considermapreset == false) + return; + // Reset the match when 2P joins 1P, DUEL mode // Reset the match when 3P joins 1P and 2P, DUEL mode must be disabled if (i > 0 && !mapreset && gamestate == GS_LEVEL && (numingame < 3 && numingame+i >= 2)) diff --git a/src/k_kart.h b/src/k_kart.h index 809c8673c..2f8eca77a 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -189,7 +189,7 @@ boolean K_FastFallBounce(player_t *player); fixed_t K_PlayerBaseFriction(player_t *player, fixed_t original); void K_AdjustPlayerFriction(player_t *player); void K_MoveKartPlayer(player_t *player, boolean onground); -void K_CheckSpectateStatus(void); +void K_CheckSpectateStatus(boolean considermapreset); UINT8 K_GetInvincibilityItemFrame(void); UINT8 K_GetOrbinautItemFrame(UINT8 count); boolean K_IsSPBInGame(void); diff --git a/src/p_mobj.c b/src/p_mobj.c index 103a1e452..e69faaefc 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11830,7 +11830,6 @@ void P_SpawnPlayer(INT32 playernum) else if (netgame && p->jointime <= 1 && pcount) { p->spectator = true; - p->spectatorReentry = 0; } else if (multiplayer && !netgame) { diff --git a/src/p_setup.c b/src/p_setup.c index 758c4b425..0c7c6738d 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8389,6 +8389,11 @@ void P_PostLoadLevel(void) { P_MapStart(); + if (G_GametypeHasSpectators()) + { + K_CheckSpectateStatus(false); + } + if (demo.playback) ; else if (grandprixinfo.gp == true) From 39d9dc99aa71ce13fe7757b8d5cd4cfaa3b29bb7 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 22 Jul 2023 14:33:32 +0100 Subject: [PATCH 6/7] FinalisePlaystateChange: Don't reset spectatorReentry timer if it's already nonzero --- src/d_netcmd.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f5e2f26e1..7f1042f8c 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1617,7 +1617,11 @@ static void FinalisePlaystateChange(INT32 playernum) // To attempt to discourage rage-spectators, we delay any rejoining. // If you're engaging in a DUEL and quit early, in addition to the // indignity of losing your PWR, you get a special extra-long delay. - if (netgame && players[playernum].jointime > 1) + if ( + netgame + && players[playernum].jointime > 1 + && players[playernum].spectatorReentry == 0 + ) { UINT8 pcount = 0; From 3d383f209eff6e0525963c7fb4e740db9f5b467f Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 22 Jul 2023 14:37:34 +0100 Subject: [PATCH 7/7] Got_TeamChange: Applying PF_WANTSTOJOIN is NOT a playstate change --- src/d_netcmd.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 7f1042f8c..4a53e4159 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -4076,6 +4076,8 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) //Now that we've done our error checking and killed the player //if necessary, put the player on the correct team/status. + boolean nochangeoccourred = false; + if (G_GametypeHasTeams()) { if (!NetPacket.packet.newteam) @@ -4087,6 +4089,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) { players[playernum].ctfteam = NetPacket.packet.newteam; players[playernum].pflags |= PF_WANTSTOJOIN; //players[playernum].spectator = false; + nochangeoccourred = true; } } else if (G_GametypeHasSpectators()) @@ -4094,7 +4097,10 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) if (!NetPacket.packet.newteam) players[playernum].spectator = true; else + { players[playernum].pflags |= PF_WANTSTOJOIN; //players[playernum].spectator = false; + nochangeoccourred = true; + } } if (NetPacket.packet.autobalance) @@ -4135,7 +4141,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) } }*/ - if (gamestate != GS_LEVEL) + if (gamestate != GS_LEVEL || nochangeoccourred == true) return; FinalisePlaystateChange(playernum);