From f3424f51bceb430d9dcad6b15bcc790f3b415f31 Mon Sep 17 00:00:00 2001
From: Darkvater <darkvater@openttd.org>
Date: Sat, 30 Dec 2006 23:20:00 +0000
Subject: [PATCH] (svn r7671) -Backport from trunk (r7560, r7561, r7566):  - No
 new company could join if 8 clients were connected in less than 8 companies
 (r7560).  - [FS#431] internal and visible settings of autorenew could go out
 of sync (r8561).  - Server told clients to start syncing from a bad position,
 causing asserts/crashes (r7566).

---
 economy.c        |  3 ++-
 genworld.c       |  4 ++--
 misc_gui.c       |  3 ++-
 network_client.c |  7 ++-----
 network_server.c |  2 +-
 openttd.c        | 41 +++++++++++++++++++++++------------------
 player.h         |  1 +
 players.c        | 33 ++++++++++++++++++++++++++++-----
 8 files changed, 61 insertions(+), 33 deletions(-)

diff --git a/economy.c b/economy.c
index 9ced54475c..97fc55c0d0 100644
--- a/economy.c
+++ b/economy.c
@@ -420,7 +420,8 @@ static void PlayersCheckBankrupt(Player *p)
 					p->bankrupt_timeout = 0x456;
 					break;
 				} else if (owner == _local_player) {
-					_local_player = _network_playas = PLAYER_SPECTATOR;
+					_network_playas = PLAYER_SPECTATOR;
+					SetLocalPlayer(PLAYER_SPECTATOR);
 				}
 
 #ifdef ENABLE_NETWORK
diff --git a/genworld.c b/genworld.c
index 70bb983c62..e6c646b0d7 100644
--- a/genworld.c
+++ b/genworld.c
@@ -133,7 +133,7 @@ static void *_GenerateWorld(void *arg)
 	}
 
 	ResetObjectToPlace();
-	_local_player = _gw.lp;
+	SetLocalPlayer(_gw.lp);
 
 	SetGeneratingWorldProgress(GWP_GAME_START, 1);
 	/* Call any callback */
@@ -249,7 +249,7 @@ void GenerateWorld(int mode, uint size_x, uint size_y)
 	_gw.threaded      = true;
 
 	/* This disables some commands and stuff */
-	_local_player   = PLAYER_SPECTATOR;
+	SetLocalPlayer(PLAYER_SPECTATOR);
 	/* Make sure everything is done via OWNER_NONE */
 	_current_player = OWNER_NONE;
 
diff --git a/misc_gui.c b/misc_gui.c
index a5ad7cb212..e5ffef5c3c 100644
--- a/misc_gui.c
+++ b/misc_gui.c
@@ -1647,7 +1647,8 @@ static int32 ClickChangePlayerCheat(int32 p1, int32 p2)
 {
 	while (IsValidPlayer((PlayerID)p1)) {
 		if (_players[p1].is_active) {
-			_local_player = (PlayerID)p1;
+			SetLocalPlayer((PlayerID)p1);
+
 			MarkWholeScreenDirty();
 			return _local_player;
 		}
diff --git a/network_client.c b/network_client.c
index c0eb717510..22d23720e7 100644
--- a/network_client.c
+++ b/network_client.c
@@ -509,7 +509,7 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_MAP)
 		if (_network_playas == PLAYER_NEW_COMPANY || !IsValidPlayer(_network_playas) ||
 				!GetPlayer(_network_playas)->is_active) {
 
-			_local_player = PLAYER_SPECTATOR;
+			SetLocalPlayer(PLAYER_SPECTATOR);
 
 			if (_network_playas == PLAYER_SPECTATOR) {
 				// The client wants to be a spectator..
@@ -521,10 +521,7 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_MAP)
 			}
 		} else {
 			// take control over an existing company
-			_local_player = _network_playas;
-			_patches.autorenew = GetPlayer(_local_player)->engine_renew;
-			_patches.autorenew_months = GetPlayer(_local_player)->engine_renew_months;
-			_patches.autorenew_money = GetPlayer(_local_player)->engine_renew_money;
+			SetLocalPlayer(_network_playas);
 			DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 		}
 	}
diff --git a/network_server.c b/network_server.c
index 3b2861f91f..d6b07883d5 100644
--- a/network_server.c
+++ b/network_server.c
@@ -862,7 +862,7 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND)
 	// Queue the command for the clients (are send at the end of the frame
 	//   if they can handle it ;))
 	FOR_ALL_CLIENTS(new_cs) {
-		if (new_cs->status > STATUS_AUTH) {
+		if (new_cs->status >= STATUS_MAP) {
 			// Callbacks are only send back to the client who sent them in the
 			//  first place. This filters that out.
 			cp->callback = (new_cs != cs) ? 0 : callback;
diff --git a/openttd.c b/openttd.c
index c81702a5eb..7b02e6ceef 100644
--- a/openttd.c
+++ b/openttd.c
@@ -301,7 +301,7 @@ static void LoadIntroGame(void)
 	}
 
 	_pause = 0;
-	_local_player = 0;
+	SetLocalPlayer(0);
 	/* Make sure you can't scroll in the menu */
 	_scrolling_viewport = 0;
 	_cursor.fix_at = false;
@@ -595,14 +595,14 @@ static void MakeNewGameDone(void)
 {
 	/* In a dedicated server, the server does not play */
 	if (_network_dedicated) {
-		_local_player = PLAYER_SPECTATOR;
+		SetLocalPlayer(PLAYER_SPECTATOR);
 		return;
 	}
 
 	/* Create a single player */
 	DoStartupNewPlayer(false);
 
-	_local_player = 0;
+	SetLocalPlayer(0);
 	_current_player = _local_player;
 	DoCommandP(0, (_patches.autorenew << 15 ) | (_patches.autorenew_months << 16) | 4, _patches.autorenew_money, NULL, CMD_SET_AUTOREPLACE);
 
@@ -623,7 +623,7 @@ static void MakeNewGame(bool from_heightmap)
 
 static void MakeNewEditorWorldDone(void)
 {
-	_local_player = OWNER_NONE;
+	SetLocalPlayer(OWNER_NONE);
 
 	MarkWholeScreenDirty();
 }
@@ -682,7 +682,7 @@ static void StartScenario(void)
 	StartupEngines();
 	StartupDisasters();
 
-	_local_player = 0;
+	SetLocalPlayer(0);
 	_current_player = _local_player;
 	DoCommandP(0, (_patches.autorenew << 15 ) | (_patches.autorenew_months << 16) | 4, _patches.autorenew_money, NULL, CMD_SET_AUTOREPLACE);
 
@@ -773,7 +773,7 @@ void SwitchMode(int new_mode)
 		} else {
 			/* Update the local player for a loaded game. It is either always
 			 * player #1 (eg 0) or in the case of a dedicated server a spectator */
-			_local_player = _network_dedicated ? PLAYER_SPECTATOR : 0;
+			SetLocalPlayer(_network_dedicated ? PLAYER_SPECTATOR : 0);
 			DoCommandP(0, 0, 0, NULL, CMD_PAUSE); // decrease pause counter (was increased from opening load dialog)
 #ifdef ENABLE_NETWORK
 			if (_network_server) {
@@ -794,7 +794,7 @@ void SwitchMode(int new_mode)
 		break;
 
 	case SM_LOAD_HEIGHTMAP: /* Load heightmap from scenario editor */
-		_local_player = OWNER_NONE;
+		SetLocalPlayer(OWNER_NONE);
 
 		GenerateWorld(GW_HEIGHTMAP, 1 << _patches.map_x, 1 << _patches.map_y);
 		MarkWholeScreenDirty();
@@ -806,7 +806,7 @@ void SwitchMode(int new_mode)
 
 			_opt_ptr = &_opt;
 
-			_local_player = OWNER_NONE;
+			SetLocalPlayer(OWNER_NONE);
 			_generating_world = true;
 			/* Delete all players */
 			FOR_ALL_PLAYERS(p) {
@@ -838,7 +838,7 @@ void SwitchMode(int new_mode)
 		break;
 
 	case SM_GENRANDLAND: /* Generate random land within scenario editor */
-		_local_player = OWNER_NONE;
+		SetLocalPlayer(OWNER_NONE);
 		GenerateWorld(GW_RANDOM, 1 << _patches.map_x, 1 << _patches.map_y);
 		// XXX: set date
 		MarkWholeScreenDirty();
@@ -1275,18 +1275,23 @@ bool AfterLoadGame(void)
 	 *  of course, we do need to initialize them for older savegames. */
 	if (CheckSavegameVersion(16)) {
 		FOR_ALL_PLAYERS(p) {
-			p->engine_renew_list = NULL;
-			p->engine_renew = false;
+			p->engine_renew_list   = NULL;
+			p->engine_renew        = false;
 			p->engine_renew_months = -6;
-			p->engine_renew_money = 100000;
+			p->engine_renew_money  = 100000;
 		}
-		if (IsValidPlayer(_local_player)) {
-			// Set the human controlled player to the patch settings
-			// Scenario editor do not have any companies
-			p = GetPlayer(_local_player);
-			p->engine_renew = _patches.autorenew;
+
+		/* When loading a game, _local_player is not yet set to the correct value.
+		 * However, in a dedicated server we are a spectator, so nothing needs to
+		 * happen. In case we are not a dedicated server, the local player always
+		 * becomes player 0, unless we are in the scenario editor where all the
+		 * players are 'invalid'.
+		 */
+		if (!_network_dedicated && IsValidPlayer(0)) {
+			p = GetPlayer(0);
+			p->engine_renew        = _patches.autorenew;
 			p->engine_renew_months = _patches.autorenew_months;
-			p->engine_renew_money = _patches.autorenew_money;
+			p->engine_renew_money  = _patches.autorenew_money;
 		}
 	}
 
diff --git a/player.h b/player.h
index c06900257e..df959c6e04 100644
--- a/player.h
+++ b/player.h
@@ -206,6 +206,7 @@ void GetNameOfOwner(Owner owner, TileIndex tile);
 int64 CalculateCompanyValue(const Player* p);
 void InvalidatePlayerWindows(const Player* p);
 void UpdatePlayerMoney32(Player *p);
+void SetLocalPlayer(PlayerID new_player);
 #define FOR_ALL_PLAYERS(p) for (p = _players; p != endof(_players); p++)
 
 VARDEF PlayerID _local_player;
diff --git a/players.c b/players.c
index 5b32983984..c6652b7a38 100644
--- a/players.c
+++ b/players.c
@@ -28,6 +28,29 @@
 #include "date.h"
 #include "window.h"
 
+/**
+ * Sets the local player and updates the patch settings that are set on a
+ * per-company (player) basis to reflect the core's state in the GUI.
+ * @param new_player the new player
+ * @pre IsValidPlayer(new_player) || new_player == PLAYER_SPECTATOR || new_player == OWNER_NONE
+ */
+void SetLocalPlayer(PlayerID new_player)
+{
+	/* Player could also be PLAYER_SPECTATOR or OWNER_NONE */
+	assert(IsValidPlayer(new_player) || new_player == PLAYER_SPECTATOR || new_player == OWNER_NONE);
+
+	_local_player = new_player;
+
+	/* Do not update the patches if we are in the intro GUI */
+	if (IsValidPlayer(new_player) && _game_mode != GM_MENU) {
+		const Player *p = GetPlayer(new_player);
+		_patches.autorenew        = p->engine_renew;
+		_patches.autorenew_months = p->engine_renew_months;
+		_patches.autorenew_money  = p->engine_renew_money;
+		InvalidateWindow(WC_GAME_OPTIONS, 0);
+	}
+}
+
 
 uint16 GetDrawStringPlayerColor(PlayerID player)
 {
@@ -820,9 +843,8 @@ int32 CmdPlayerCtrl(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 		/* This command is only executed in a multiplayer game */
 		if (!_networking) return CMD_ERROR;
 
-		/* ClientID would be valid up to MAX_CLIENT_INFO, but as it has to be a
-		 * new player, its valid range is restricted to that of players */
-		if (!(flags & DC_EXEC) || !IsValidPlayer((PlayerID)cid)) return 0;
+		/* Has the network client a correct ClientID? */
+		if (!(flags & DC_EXEC) || cid >= MAX_CLIENT_INFO) return 0;
 
 		/* Delete multiplayer progress bar */
 		DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
@@ -839,7 +861,8 @@ int32 CmdPlayerCtrl(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 			} else
 #endif /* ENABLE_NETWORK */
 			{
-				_local_player = _network_playas = PLAYER_SPECTATOR;
+				_network_playas = PLAYER_SPECTATOR;
+				SetLocalPlayer(PLAYER_SPECTATOR);
 			}
 			break;
 		}
@@ -847,7 +870,7 @@ int32 CmdPlayerCtrl(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 		/* This is the joining client who wants a new company */
 		if (_local_player != _network_playas) {
 			assert(_local_player == PLAYER_SPECTATOR && _network_playas == p->index);
-			_local_player = p->index;
+			SetLocalPlayer(p->index);
 			MarkWholeScreenDirty();
 		}