mirror of https://github.com/OpenTTD/OpenTTD.git
(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.
This commit is contained in:
parent
82f7140357
commit
73ca5faa2e
14
command.c
14
command.c
|
@ -424,9 +424,9 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback,
|
|||
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 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback,
|
|||
* 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;
|
||||
|
|
59
economy.c
59
economy.c
|
@ -414,40 +414,51 @@ static void PlayersCheckBankrupt(Player *p)
|
|||
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;
|
||||
|
||||
if (!IsHumanPlayer(owner) && (!_networking || _network_server) && _ai.enabled)
|
||||
AI_PlayerDied(owner);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
104
players.c
104
players.c
|
@ -819,74 +819,81 @@ int32 CmdPlayerCtrl(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
|
|||
|
||||
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;
|
||||
|
||||
/* 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
|
||||
#endif /* ENABLE_NETWORK */
|
||||
{
|
||||
_local_player = _network_playas = PLAYER_SPECTATOR;
|
||||
}
|
||||
} else if (_network_server) {
|
||||
// Creating player failed, defer client to spectator
|
||||
break;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
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 @@ int32 CmdPlayerCtrl(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
|
|||
ChangeOwnershipOfPlayerItems(pid_old, pid_new);
|
||||
DeletePlayerStuff(pid_old);
|
||||
} break;
|
||||
|
||||
default: return CMD_ERROR;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue