changeset 5067:69a8138f1456 draft

(svn r7125) -Fix: Several errors/glitches related to multiplayer and bankrupcy, mainly such a thing happening to a server, and non updated company-information. Also fixes FS#393.
author Darkvater <Darkvater@openttd.org>
date Fri, 10 Nov 2006 17:52:51 +0000
parents bac7446fc18f
children 0add58cb9880
files command.c economy.c players.c
diffstat 3 files changed, 98 insertions(+), 77 deletions(-) [+]
line wrap: on
line diff
--- a/command.c
+++ b/command.c
@@ -424,9 +424,9 @@
 	error_part1 = GB(cmd, 16, 16);
 	_additional_cash_required = 0;
 
-	/** Spectator has no rights except for the dedicated server which
-	 * is a spectator but is the server, so can do anything */
-	if (_current_player == PLAYER_SPECTATOR && !_network_dedicated) {
+	/** Spectator has no rights except for the (dedicated) server which
+	 * is/can be a spectator but as the server it can do anything */
+	if (_current_player == PLAYER_SPECTATOR && !_network_server) {
 		ShowErrorMessage(_error_message, error_part1, x, y);
 		_cmd_text = NULL;
 		return false;
@@ -495,12 +495,14 @@
 	 * send it to the command-queue and abort execution
 	 * If we are a dedicated server temporarily switch local player, otherwise
 	 * the other parties won't be able to execute our command and will desync.
-	 * @todo Rewrite dedicated server to something more than a dirty hack!
+	 * We also need to do this if the server's company has gone bankrupt
+	 * @todo Rewrite (dedicated) server to something more than a dirty hack!
 	 */
 	if (_networking && !(cmd & CMD_NETWORK_COMMAND)) {
-		if (_network_dedicated) _local_player = 0;
+		PlayerID pbck = _local_player;
+		if (_network_dedicated || (_network_server && pbck == PLAYER_SPECTATOR)) _local_player = 0;
 		NetworkSend_Command(tile, p1, p2, cmd, callback);
-		if (_network_dedicated) _local_player = PLAYER_SPECTATOR;
+		if (_network_dedicated || (_network_server && pbck == PLAYER_SPECTATOR)) _local_player = pbck;
 		_docommand_recursive = 0;
 		_cmd_text = NULL;
 		return true;
--- a/economy.c
+++ b/economy.c
@@ -414,40 +414,51 @@
 			SetDParam(1, p->name_2);
 			AddNewsItem( (StringID)(owner | NB_BBANKRUPT), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
 
-			// If the player is human, and it is no network play, leave the player playing
-			if (IsHumanPlayer(owner) && !_networking) {
-				p->bankrupt_asked = 255;
-				p->bankrupt_timeout = 0x456;
-			} else {
+			if (IsHumanPlayer(owner)) {
+				/* XXX - If we are in offline mode, leave the player playing. Eg. there
+				 * is no THE-END, otherwise mark the player as spectator to make sure
+				 * he/she is no long in control of this company */
+				if (!_networking) {
+					p->bankrupt_asked = 0xFF;
+					p->bankrupt_timeout = 0x456;
+					break;
+				} else if (owner == _local_player) {
+					_local_player = _network_playas = PLAYER_SPECTATOR;
+				}
+
 #ifdef ENABLE_NETWORK
-				/* If we are the server make sure it is clear that this player is not active */
-				if (IsHumanPlayer(owner) && _network_server) {
+				/* The server has to handle all administrative issues, for example
+				 * updating and notifying all clients of what has happened */
+				if (_network_server) {
 					const NetworkClientState *cs;
-					/* Find all clients that were in control of this company */
+					NetworkClientInfo *ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
+
+					/* The server has just gone belly-up, mark it as spectator */
+					if (owner == ci->client_playas) {
+						ci->client_playas = PLAYER_SPECTATOR;
+						NetworkUpdateClientInfo(NETWORK_SERVER_INDEX);
+					}
+
+					/* Find all clients that were in control of this company,
+					 * and mark them as spectator; broadcast this message to everyone */
 					FOR_ALL_CLIENTS(cs) {
-						NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs);
+						ci = DEREF_CLIENT_INFO(cs);
 						if (ci->client_playas == owner) {
 							ci->client_playas = PLAYER_SPECTATOR;
-							// Send the new info to all the clients
-							NetworkUpdateClientInfo(_network_own_client_index);
+							NetworkUpdateClientInfo(ci->client_index);
 						}
 					}
 				}
-				// Make sure the player no longer controls the company
-				if (IsHumanPlayer(owner) && owner == _local_player) {
-					// Switch the player to spectator..
-					_local_player = PLAYER_SPECTATOR;
-				}
 #endif /* ENABLE_NETWORK */
+			}
 
-				/* Remove the player */
-				ChangeOwnershipOfPlayerItems(owner, PLAYER_SPECTATOR);
-				// Register the player as not-active
-				p->is_active = false;
+			/* Remove the player */
+			ChangeOwnershipOfPlayerItems(owner, PLAYER_SPECTATOR);
+			/* Register the player as not-active */
+			p->is_active = false;
 
-				if (!IsHumanPlayer(owner) && (!_networking || _network_server) && _ai.enabled)
-					AI_PlayerDied(owner);
-			}
+			if (!IsHumanPlayer(owner) && (!_networking || _network_server) && _ai.enabled)
+				AI_PlayerDied(owner);
 		}
 	}
 }
--- a/players.c
+++ b/players.c
@@ -819,74 +819,81 @@
 
 	switch (p1) {
 	case 0: { /* Create a new player */
+		/* Joining Client:
+		 * _local_player: PLAYER_SPECTATOR
+		 * _network_playas/cid = requested company/player
+		 *
+		 * Other client(s)/server:
+		 * _local_player/_network_playas: what they play as
+		 * cid = requested company/player of joining client */
 		Player *p;
 		uint16 cid = p2; // ClientID
 
+		/* 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;
 
+		/* Delete multiplayer progress bar */
+		DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
+
 		p = DoStartupNewPlayer(false);
 
-#ifdef ENABLE_NETWORK
-		if (_networking && !_network_server && _local_player == PLAYER_SPECTATOR) {
-			/* In case we are a client joining a server... */
-			DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
-		}
-#endif /* ENABLE_NETWORK */
-
-		if (p != NULL) {
-			if (_local_player == PLAYER_SPECTATOR) {
-				/* Check if we do not want to be a spectator in network */
-				if (!_networking ||
-						(_network_server && !_network_dedicated) ||
-						_network_playas != PLAYER_SPECTATOR) {
-						_local_player = p->index;
-					MarkWholeScreenDirty();
-				}
-			} else if (p->index == _local_player) {
-				DoCommandP(0, (_patches.autorenew << 15 ) | (_patches.autorenew_months << 16) | 4, _patches.autorenew_money, NULL, CMD_SET_AUTOREPLACE);
-			}
+		/* A new player could not be created, revert to being a spectator */
+		if (p == NULL) {
 #ifdef ENABLE_NETWORK
 			if (_network_server) {
-				/* XXX - UGLY! p2 (pid) is mis-used to fetch the client-id, done at
-				 * server-side in network_server.c:838, function
-				 * DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) */
 				NetworkClientInfo *ci = &_network_client_info[cid];
-				ci->client_playas = p->index;
+				ci->client_playas = PLAYER_SPECTATOR;
 				NetworkUpdateClientInfo(ci->client_index);
-
-				if (IsValidPlayer(ci->client_playas)) {
-					PlayerID player_backup = _local_player;
-					_network_player_info[p->index].months_empty = 0;
+			} else
+#endif /* ENABLE_NETWORK */
+			{
+				_local_player = _network_playas = PLAYER_SPECTATOR;
+			}
+			break;
+		}
 
-					/* XXX - When a client joins, we automatically set its name to the
-					 * player's name (for some reason). As it stands now only the server
-					 * knows the client's name, so it needs to send out a "broadcast" to
-					 * do this. To achieve this we send a network command. However, it
-					 * uses _local_player to execute the command as.  To prevent abuse
-					 * (eg. only yourself can change your name/company), we 'cheat' by
-					 * impersonation _local_player as the server. Not the best solution;
-					 * but it works.
-					 * TODO: Perhaps this could be improved by when the client is ready
-					 * with joining to let it send itself the command, and not the server?
-					 * For example in network_client.c:534? */
-					_cmd_text = ci->client_name;
-					_local_player = ci->client_playas;
-					NetworkSend_Command(0, 0, 0, CMD_CHANGE_PRESIDENT_NAME, NULL);
-					_local_player = player_backup;
-				}
-			}
-		} else if (_network_server) {
-			// Creating player failed, defer client to spectator
+		/* 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;
+			MarkWholeScreenDirty();
+		}
+
+#ifdef ENABLE_NETWORK
+		if (_network_server) {
 			/* XXX - UGLY! p2 (pid) is mis-used to fetch the client-id, done at
 			 * server-side in network_server.c:838, function
 			 * DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) */
 			NetworkClientInfo *ci = &_network_client_info[cid];
-			ci->client_playas = PLAYER_SPECTATOR;
+			ci->client_playas = p->index;
 			NetworkUpdateClientInfo(ci->client_index);
+
+			if (IsValidPlayer(ci->client_playas)) {
+				PlayerID player_backup = _local_player;
+				_network_player_info[p->index].months_empty = 0;
+
+				/* XXX - When a client joins, we automatically set its name to the
+				 * player's name (for some reason). As it stands now only the server
+				 * knows the client's name, so it needs to send out a "broadcast" to
+				 * do this. To achieve this we send a network command. However, it
+				 * uses _local_player to execute the command as.  To prevent abuse
+				 * (eg. only yourself can change your name/company), we 'cheat' by
+				 * impersonation _local_player as the server. Not the best solution;
+				 * but it works.
+				 * TODO: Perhaps this could be improved by when the client is ready
+				 * with joining to let it send itself the command, and not the server?
+				 * For example in network_client.c:534? */
+				_cmd_text = ci->client_name;
+				_local_player = ci->client_playas;
+				NetworkSend_Command(0, 0, 0, CMD_CHANGE_PRESIDENT_NAME, NULL);
+				_local_player = player_backup;
+			}
+		}
 #endif /* ENABLE_NETWORK */
-		}
 	} break;
 
 	case 1: /* Make a new AI player */
@@ -933,6 +940,7 @@
 		ChangeOwnershipOfPlayerItems(pid_old, pid_new);
 		DeletePlayerStuff(pid_old);
 	} break;
+
 	default: return CMD_ERROR;
 	}