From 80bf4d6c2d1cc413c41b0df69e2cf9334675377b Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 12 Jun 2023 22:34:42 -0400
Subject: [PATCH] Port SRB2Kart join on intermission fix

All gamestates besides GS_LEVEL are unsupported by the save game functions. This commit forces players joining during these gamestates into GS_WAITINGPLAYERS, which is a basic gamestate that just maintains the connection until we can enter the start of a new one. Also provides an extremely simple drawer for GS_WAITINGPLAYERS so the joining player knows what's going on.
---
 src/console.c  |  2 +-
 src/d_clisrv.c |  2 ++
 src/d_main.c   |  7 +++++++
 src/d_netcmd.c |  2 +-
 src/f_finale.c | 33 +++++++++++++++++++++++++++++++++
 src/f_finale.h |  4 ++++
 src/g_game.c   |  9 ++++++---
 src/p_saveg.c  |  6 +++++-
 8 files changed, 59 insertions(+), 6 deletions(-)

diff --git a/src/console.c b/src/console.c
index 6d273f6207..119079464c 100644
--- a/src/console.c
+++ b/src/console.c
@@ -1889,7 +1889,7 @@ void CON_Drawer(void)
 		CON_DrawConsole();
 	else if (gamestate == GS_LEVEL
 	|| gamestate == GS_INTERMISSION || gamestate == GS_ENDING || gamestate == GS_CUTSCENE
-	|| gamestate == GS_CREDITS || gamestate == GS_EVALUATION)
+	|| gamestate == GS_CREDITS || gamestate == GS_EVALUATION || gamestate == GS_WAITINGPLAYERS)
 		CON_DrawHudlines();
 
 	Unlock_state();
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 2106191640..9b3187cbb7 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -2600,6 +2600,8 @@ static void CL_ConnectToServer(void)
 	}
 	while (!(cl_mode == CL_CONNECTED && (client || (server && nodewaited <= pnumnodes))));
 
+	if (netgame)
+		F_StartWaitingPlayers();
 	DEBFILE(va("Synchronisation Finished\n"));
 
 	displayplayer = consoleplayer;
diff --git a/src/d_main.c b/src/d_main.c
index 5861f98865..2db4002580 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -458,6 +458,13 @@ static void D_Display(void)
 
 		case GS_WAITINGPLAYERS:
 			// The clientconnect drawer is independent...
+			if (netgame)
+			{
+				// I don't think HOM from nothing drawing is independent...
+				F_WaitingPlayersDrawer();
+				HU_Erase();
+				HU_Drawer();
+			}
 		case GS_DEDICATEDSERVER:
 		case GS_NULL:
 			break;
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index b23aaa5a0a..2cdf70843b 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -2174,7 +2174,7 @@ static void Command_Pause(void)
 
 	if (cv_pause.value || server || (IsPlayerAdmin(consoleplayer)))
 	{
-		if (modeattacking || !(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) || (marathonmode && gamestate == GS_INTERMISSION))
+		if (modeattacking || !(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_WAITINGPLAYERS) || (marathonmode && gamestate == GS_INTERMISSION))
 		{
 			CONS_Printf(M_GetText("You can't pause here.\n"));
 			return;
diff --git a/src/f_finale.c b/src/f_finale.c
index 299a6a054c..529244b5a6 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -4660,3 +4660,36 @@ void F_TextPromptTicker(void)
 			animtimer--;
 	}
 }
+
+// ================
+//  WAITINGPLAYERS
+// ================
+
+void F_StartWaitingPlayers(void)
+{
+	wipegamestate = GS_TITLESCREEN; // technically wiping from title screen
+	finalecount = 0;
+}
+
+void F_WaitingPlayersTicker(void)
+{
+	if (paused)
+		return;
+
+	finalecount++;
+
+	// dumb hack, only start the music on the 1st tick so if you instantly go into the map you aren't hearing a tic of music
+	if (finalecount == 2)
+		S_ChangeMusicInternal("_CHSEL", true);
+}
+
+void F_WaitingPlayersDrawer(void)
+{
+	const char *waittext1 = "You will join";
+	const char *waittext2 = "next level...";
+
+	V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
+
+	V_DrawCreditString((160 - (V_CreditStringWidth(waittext1)>>1))<<FRACBITS, 48<<FRACBITS, 0, waittext1);
+	V_DrawCreditString((160 - (V_CreditStringWidth(waittext2)>>1))<<FRACBITS, 64<<FRACBITS, 0, waittext2);
+}
diff --git a/src/f_finale.h b/src/f_finale.h
index 7f53bfbad5..cb71775d05 100644
--- a/src/f_finale.h
+++ b/src/f_finale.h
@@ -74,6 +74,10 @@ void F_StartContinue(void);
 void F_ContinueTicker(void);
 void F_ContinueDrawer(void);
 
+void F_StartWaitingPlayers(void);
+void F_WaitingPlayersTicker(void);
+void F_WaitingPlayersDrawer(void);
+
 extern INT32 finalecount;
 extern INT32 titlescrollxspeed;
 extern INT32 titlescrollyspeed;
diff --git a/src/g_game.c b/src/g_game.c
index 47e670bfec..bcfe691051 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2437,14 +2437,17 @@ void G_Ticker(boolean run)
 		case GS_TITLESCREEN:
 			if (titlemapinaction)
 				P_Ticker(run);
-				// then intentionally fall through
-			/* FALLTHRU */
-		case GS_WAITINGPLAYERS:
 			if (run)
 				F_MenuPresTicker();
 			F_TitleScreenTicker(run);
 			break;
 
+		case GS_WAITINGPLAYERS:
+			if (netgame)
+				F_WaitingPlayersTicker();
+			HU_Ticker();
+			break;
+
 		case GS_DEDICATEDSERVER:
 		case GS_NULL:
 			break; // do nothing
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 40fd656386..62aa624fc1 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -4279,7 +4279,11 @@ static void P_NetArchiveMisc(boolean resending)
 	if (resending)
 		WRITEUINT32(save_p, gametic);
 	WRITEINT16(save_p, gamemap);
-	WRITEINT16(save_p, gamestate);
+
+	if (gamestate != GS_LEVEL)
+		WRITEINT16(save_p, GS_WAITINGPLAYERS); // nice hack to put people back into waitingplayers
+	else
+		WRITEINT16(save_p, gamestate);
 	WRITEINT16(save_p, gametype);
 
 	{
-- 
GitLab