From 0d7640b411a07f42106e6ef5d4220ac2a47a57f7 Mon Sep 17 00:00:00 2001
From: terencehill <piuntn@gmail.com>
Date: Mon, 16 Aug 2021 20:41:01 +0200
Subject: [PATCH] LMS: improve handling of forced spectators in warmup and
 countdown to game start (e.g. by movetospec or sv_maxidle_playertospectator
 timer)

---
 .../gamemode/clanarena/sv_clanarena.qc         |  4 ++++
 qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc  | 10 ++++++++--
 qcsrc/common/minigames/sv_minigames.qc         |  4 +---
 qcsrc/server/client.qc                         | 18 +++++++-----------
 qcsrc/server/client.qh                         |  2 +-
 qcsrc/server/clientkill.qc                     |  2 +-
 qcsrc/server/command/sv_cmd.qc                 |  6 ++----
 qcsrc/server/mutators/events.qh                |  1 +
 8 files changed, 25 insertions(+), 22 deletions(-)

diff --git a/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc b/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc
index ba2402f27..c7ba41984 100644
--- a/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc
+++ b/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc
@@ -302,6 +302,10 @@ MUTATOR_HOOKFUNCTION(ca, MakePlayerObserver)
 {
 	entity player = M_ARGV(0, entity);
 
+	bool is_forced = M_ARGV(1, bool);
+	if (is_forced && player.caplayer)
+		player.caplayer = 0;
+
 	if (IS_PLAYER(player) && !IS_DEAD(player))
 		ca_LastPlayerForTeam_Notify(player);
 	if (player.killindicator_teamchange == -2) // player wants to spectate
diff --git a/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc b/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc
index b05a960dd..556f76294 100644
--- a/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc
+++ b/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc
@@ -310,6 +310,7 @@ MUTATOR_HOOKFUNCTION(lms, ClientDisconnect)
 MUTATOR_HOOKFUNCTION(lms, MakePlayerObserver)
 {
 	entity player = M_ARGV(0, entity);
+	bool is_forced = M_ARGV(1, bool);
 
 	if (!IS_PLAYER(player))
 		return true;
@@ -321,8 +322,13 @@ MUTATOR_HOOKFUNCTION(lms, MakePlayerObserver)
 		TRANSMUTE(Observer, player);
 		player.lmsplayer = 0;
 	}
-	else if (!GameRules_scoring_add(player, LMS_RANK, 0))
-		lms_RemovePlayer(player);
+	else
+	{
+		if (is_forced)
+			player.lms_spectate_warning = 2;
+		if (!GameRules_scoring_add(player, LMS_RANK, 0))
+			lms_RemovePlayer(player);
+	}
 	return true;  // prevent team reset
 }
 
diff --git a/qcsrc/common/minigames/sv_minigames.qc b/qcsrc/common/minigames/sv_minigames.qc
index 21f81055a..a1fb3631d 100644
--- a/qcsrc/common/minigames/sv_minigames.qc
+++ b/qcsrc/common/minigames/sv_minigames.qc
@@ -148,9 +148,7 @@ int minigame_addplayer(entity minigame_session, entity player)
 		Net_LinkEntity(player_pointer, false, 0, minigame_SendEntity);
 
 		if ( !IS_OBSERVER(player) && autocvar_sv_minigames_observer )
-		{
-			PutObserverInServer(player);
-		}
+			PutObserverInServer(player, true);
 		if ( autocvar_sv_minigames_observer == 2 )
 			Player_SetForcedTeamIndex(player, TEAM_FORCE_SPECTATOR);
 
diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc
index a9758146f..943404e02 100644
--- a/qcsrc/server/client.qc
+++ b/qcsrc/server/client.qc
@@ -6,7 +6,6 @@
 #include <common/effects/qc/globalsound.qh>
 #include <common/ent_cs.qh>
 #include <common/gamemodes/_mod.qh>
-#include <common/gamemodes/gamemode/lms/sv_lms.qh>
 #include <common/gamemodes/gamemode/nexball/sv_nexball.qh>
 #include <common/items/_mod.qh>
 #include <common/items/inventory.qh>
@@ -236,9 +235,9 @@ void setplayermodel(entity e, string modelname)
 }
 
 /** putting a client as observer in the server */
-void PutObserverInServer(entity this)
+void PutObserverInServer(entity this, bool is_forced)
 {
-	bool mutator_returnvalue = MUTATOR_CALLHOOK(MakePlayerObserver, this);
+	bool mutator_returnvalue = MUTATOR_CALLHOOK(MakePlayerObserver, this, is_forced);
 	PlayerState_detach(this);
 
 	if (IS_PLAYER(this))
@@ -820,7 +819,7 @@ void PutClientInServer(entity this)
 	MUTATOR_CALLHOOK(PutClientInServer, this);
 
 	if (IS_OBSERVER(this)) {
-		PutObserverInServer(this);
+		PutObserverInServer(this, false);
 	} else if (IS_PLAYER(this)) {
 		PutPlayerInServer(this);
 	}
@@ -1789,7 +1788,7 @@ bool SpectateSet(entity this)
 	accuracy_resend(this);
 
 	if(!SpectateUpdate(this))
-		PutObserverInServer(this);
+		PutObserverInServer(this, false);
 
 	return true;
 }
@@ -2293,7 +2292,7 @@ void ObserverOrSpectatorThink(entity this)
 				TRANSMUTE(Observer, this);
 				PutClientInServer(this);
 			} else if(!SpectateUpdate(this) && !SpectateNext(this)) {
-				PutObserverInServer(this);
+				PutObserverInServer(this, false);
 				this.would_spectate = true;
 			}
 		}
@@ -2319,7 +2318,7 @@ void ObserverOrSpectatorThink(entity this)
 			}
 		}
 		if(is_spec && !SpectateUpdate(this))
-			PutObserverInServer(this);
+			PutObserverInServer(this, false);
 	}
 	if (is_spec)
 		this.flags |= FL_CLIENT | FL_NOTARGET;
@@ -2677,10 +2676,7 @@ void PlayerPostThink (entity this)
 				if (IS_PLAYER(this) && autocvar_sv_maxidle_playertospectator > 0)
 				{
 					Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MOVETOSPEC_IDLING, this.netname, maxidle_time);
-					if (this.caplayer)
-						this.caplayer = 0;
-					this.lms_spectate_warning = 2; // TODO: mutator hook for players forcibly moved to spectator?
-					PutObserverInServer(this);
+					PutObserverInServer(this, true);
 				}
 				else
 				{
diff --git a/qcsrc/server/client.qh b/qcsrc/server/client.qh
index 3199d155a..2f7bfbb0b 100644
--- a/qcsrc/server/client.qh
+++ b/qcsrc/server/client.qh
@@ -381,7 +381,7 @@ bool Spectate(entity this, entity pl);
 
 void ClientInit_Spawn();
 
-void PutObserverInServer(entity this);
+void PutObserverInServer(entity this, bool is_forced);
 
 void SetSpectatee(entity this, entity spectatee);
 void SetSpectatee_status(entity this, int spectatee_num);
diff --git a/qcsrc/server/clientkill.qc b/qcsrc/server/clientkill.qc
index e87a2b118..9b29273a6 100644
--- a/qcsrc/server/clientkill.qc
+++ b/qcsrc/server/clientkill.qc
@@ -24,7 +24,7 @@ void ClientKill_Now_TeamChange(entity this)
 	{
 		if (blockSpectators)
 			Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
-		PutObserverInServer(this);
+		PutObserverInServer(this, false);
 	}
 	else
 	{
diff --git a/qcsrc/server/command/sv_cmd.qc b/qcsrc/server/command/sv_cmd.qc
index 5a9fae567..47e9f922a 100644
--- a/qcsrc/server/command/sv_cmd.qc
+++ b/qcsrc/server/command/sv_cmd.qc
@@ -184,8 +184,7 @@ void GameCommand_allspec(int request, int argc)
 			string reason = argv(1);
 			int n = 0;
 			FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), {
-				if (it.caplayer) it.caplayer = 0;
-				PutObserverInServer(it);
+				PutObserverInServer(it, true);
 				++n;
 			});
 			if (n)   bprint(strcat("Successfully forced all (", ftos(n), ") players to spectate", (reason ? strcat(" for reason: '", reason, "'") : ""), ".\n"));
@@ -1009,8 +1008,7 @@ void GameCommand_moveplayer(int request, int argc)
 						string pl_name = playername(client.netname, client.team, false);
 						if (!IS_SPEC(client) && !IS_OBSERVER(client))
 						{
-							if (client.caplayer) client.caplayer = 0;
-							PutObserverInServer(client);
+							PutObserverInServer(client, true);
 
 							successful = strcat(successful, (successful ? ", " : ""), pl_name);
 						}
diff --git a/qcsrc/server/mutators/events.qh b/qcsrc/server/mutators/events.qh
index f33fc4598..ee5d18a69 100644
--- a/qcsrc/server/mutators/events.qh
+++ b/qcsrc/server/mutators/events.qh
@@ -10,6 +10,7 @@
 /** called when a player becomes observer, after shared setup */
 #define EV_MakePlayerObserver(i, o) \
     /** player */ i(entity, MUTATOR_ARGV_0_entity) \
+    /** is_forced */ i(bool, MUTATOR_ARGV_1_bool) \
     /**/
 MUTATOR_HOOKABLE(MakePlayerObserver, EV_MakePlayerObserver)
 
-- 
2.39.5