Changeset - r2944:25bed4e89f1d
[Not reviewed]
master
0 8 0
Darkvater - 18 years ago 2006-01-31 22:16:15
darkvater@openttd.org
(svn r3500) - Workaround the inaccurate count of spectators/companies that can happen in certain border-cases. For now just dynamically get this value when requested so it is always right. To do properly all player/client creation/destruction needs a hook for networking.
8 files changed with 40 insertions and 40 deletions:
0 comments (0 inline, 0 general)
console_cmds.c
Show inline comments
 
@@ -536,26 +536,26 @@ DEF_CONSOLE_CMD(ConStatus)
 
DEF_CONSOLE_CMD(ConServerInfo)
 
{
 
	const NetworkGameInfo *gi;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("List current and maximum client/player limits. Usage 'server_info'");
 
		IConsoleHelp("You can change these values by setting the variables 'max_clients', 'max_companies' and 'max_spectators'");
 
		return true;
 
	}
 

	
 
	gi = &_network_game_info;
 
	IConsolePrintF(_icolour_def, "Current/maximum clients:    %2d/%2d", gi->clients_on, gi->clients_max);
 
	IConsolePrintF(_icolour_def, "Current/maximum companies:  %2d/%2d", gi->companies_on, gi->companies_max);
 
	IConsolePrintF(_icolour_def, "Current/maximum spectators: %2d/%2d", gi->spectators_on, gi->spectators_max);
 
	IConsolePrintF(_icolour_def, "Current/maximum companies:  %2d/%2d", ActivePlayerCount(), gi->companies_max);
 
	IConsolePrintF(_icolour_def, "Current/maximum spectators: %2d/%2d", NetworkSpectatorCount(), gi->spectators_max);
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_HOOK(ConHookValidateMaxClientsCount) {
 
	/* XXX - hardcoded, string limiation -- TrueLight
 
	 * XXX - also see network.c:NetworkStartup ~1343 */
 
	if (_network_game_info.clients_max > 10) {
 
		_network_game_info.clients_max = 10;
 
		IConsoleError("Maximum clients is 10, truncating.");
 
	}
 

	
network.c
Show inline comments
 
@@ -94,24 +94,36 @@ NetworkClientState *NetworkFindClientSta
 

	
 
// NetworkGetClientName is a server-safe function to get the name of the client
 
//  if the user did not send it yet, Client #<no> is used.
 
void NetworkGetClientName(char *client_name, size_t size, const NetworkClientState *cs)
 
{
 
	NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs);
 
	if (ci->client_name[0] == '\0')
 
		snprintf(client_name, size, "Client #%d", cs->index);
 
	else
 
		snprintf(client_name, size, "%s", ci->client_name);
 
}
 

	
 
byte NetworkSpectatorCount(void)
 
{
 
	const NetworkClientState *cs;
 
	byte count = 0;
 

	
 
	FOR_ALL_CLIENTS(cs) {
 
		if (DEREF_CLIENT_INFO(cs)->client_playas == OWNER_SPECTATOR) count++;
 
	}
 

	
 
	return count;
 
}
 

	
 
// This puts a text-message to the console, or in the future, the chat-box,
 
//  (to keep it all a bit more general)
 
// If 'self_send' is true, this is the client who is sending the message
 
void CDECL NetworkTextMessage(NetworkAction action, uint16 color, bool self_send, const char *name, const char *str, ...)
 
{
 
	char buf[1024];
 
	va_list va;
 
	const int duration = 10; // Game days the messages stay visible
 
	char message[1024];
 
	char temp[1024];
 

	
 
	va_start(va, str);
 
@@ -545,29 +557,25 @@ void NetworkCloseClient(NetworkClientSta
 

	
 
	while (cs->command_queue != NULL) {
 
		CommandPacket *p = cs->command_queue->next;
 
		free(cs->command_queue);
 
		cs->command_queue = p;
 
	}
 

	
 
	// Close the gap in the client-list
 
	ci = DEREF_CLIENT_INFO(cs);
 

	
 
	if (_network_server) {
 
		// We just lost one client :(
 
		if (cs->status > STATUS_INACTIVE) {
 
			_network_game_info.clients_on--;
 
			if (DEREF_CLIENT_INFO(cs)->client_playas == OWNER_SPECTATOR)
 
				_network_game_info.spectators_on--;
 
		}
 
		if (cs->status > STATUS_INACTIVE) _network_game_info.clients_on--;
 
		_network_clients_connected--;
 

	
 
		while ((cs + 1) != DEREF_CLIENT(MAX_CLIENTS) && (cs + 1)->socket != INVALID_SOCKET) {
 
			*cs = *(cs + 1);
 
			*ci = *(ci + 1);
 
			cs++;
 
			ci++;
 
		}
 

	
 
		InvalidateWindow(WC_CLIENT_LIST, 0);
 
	}
 

	
network.h
Show inline comments
 
@@ -64,27 +64,27 @@ typedef struct NetworkGameInfo {
 
	char server_name[NETWORK_NAME_LENGTH];          // Server name
 
	char hostname[NETWORK_HOSTNAME_LENGTH];         // Hostname of the server (if any)
 
	char server_revision[NETWORK_REVISION_LENGTH];  // The SVN version number the server is using (e.g.: 'r304')
 
	                                                //  It even shows a SVN version in release-version, so
 
	                                                //  it is easy to compare if a server is of the correct version
 
	bool compatible;                                // Can we connect to this server or not? (based on server_revision)
 
	byte server_lang;                               // Language of the server (we should make a nice table for this)
 
	byte use_password;                              // Is set to != 0 if it uses a password
 
	char server_password[NETWORK_PASSWORD_LENGTH];  // On the server: the game password, on the client: != "" if server has password
 
	byte clients_max;                               // Max clients allowed on server
 
	byte clients_on;                                // Current count of clients on server
 
	byte companies_max;                             // Max companies allowed on server
 
	byte companies_on;                              // How many started companies do we have
 
	byte companies_on;                              // How many started companies do we have (XXX - disabled for server atm, use ActivePlayerCount())
 
	byte spectators_max;                            // Max spectators allowed on server
 
	byte spectators_on;                             // How many spectators do we have?
 
	byte spectators_on;                             // How many spectators do we have? (XXX - disabled for server atm, use NetworkSpectatorCount())
 
	uint16 game_date;                               // Current date
 
	uint16 start_date;                              // When the game started
 
	char map_name[NETWORK_NAME_LENGTH];             // Map which is played ["random" for a randomized map]
 
	uint16 map_width;                               // Map width
 
	uint16 map_height;                              // Map height
 
	byte map_set;                                   // Graphical set
 
	bool dedicated;                                 // Is this a dedicated server?
 
	char rcon_password[NETWORK_PASSWORD_LENGTH];    // RCon password for the server. "" if rcon is disabled
 
} NetworkGameInfo;
 

	
 
typedef struct NetworkPlayerInfo {
 
	char company_name[NETWORK_NAME_LENGTH];					// Company name
 
@@ -201,24 +201,26 @@ VARDEF bool _network_advertise;
 
VARDEF bool _network_need_advertise;
 
VARDEF uint32 _network_last_advertise_frame;
 
VARDEF uint8 _network_advertise_retries;
 

	
 
VARDEF bool _network_autoclean_companies;
 
VARDEF uint8 _network_autoclean_unprotected; // Remove a company after X months
 
VARDEF uint8 _network_autoclean_protected;   // Unprotect a company after X months
 

	
 
VARDEF uint16 _network_restart_game_date;    // If this year is reached, the server automaticly restarts
 

	
 
NetworkGameList *NetworkQueryServer(const char* host, unsigned short port, bool game_info);
 

	
 
byte NetworkSpectatorCount(void);
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
// Those variables must always be registered!
 
#define MAX_SAVED_SERVERS 10
 
VARDEF char *_network_host_list[MAX_SAVED_SERVERS];
 
#define MAX_BANS 25
 
VARDEF char *_network_ban_list[MAX_BANS];
 
VARDEF bool _networking;
 
VARDEF bool _network_available;  // is network mode available?
 
VARDEF bool _network_server; // network-server is active
 
VARDEF bool _network_dedicated; // are we a dedicated server?
 
VARDEF byte _network_playas; // an id to play as..
network_server.c
Show inline comments
 
@@ -65,30 +65,25 @@ DEF_SERVER_SEND_COMMAND(PACKET_SERVER_CO
 
{
 
//
 
	// Packet: SERVER_COMPANY_INFO
 
	// Function: Sends info about the companies
 
	// Data:
 
	//
 

	
 
	int i;
 

	
 
	Player *player;
 
	Packet *p;
 

	
 
	byte active = 0;
 

	
 
	FOR_ALL_PLAYERS(player) {
 
		if (player->is_active)
 
			active++;
 
	}
 
	byte active = ActivePlayerCount();
 

	
 
	if (active == 0) {
 
		Packet *p = NetworkSend_Init(PACKET_SERVER_COMPANY_INFO);
 

	
 
		NetworkSend_uint8 (p, NETWORK_COMPANY_INFO_VERSION);
 
		NetworkSend_uint8 (p, active);
 

	
 
		NetworkSend_Packet(p, cs);
 
		return;
 
	}
 

	
 
	NetworkPopulateCompanyInfo();
 
@@ -205,33 +200,30 @@ DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SER
 

	
 
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_WELCOME)
 
{
 
	//
 
	// Packet: SERVER_WELCOME
 
	// Function: The client is joined and ready to receive his map
 
	// Data:
 
	//    uint16:  Own ClientID
 
	//
 

	
 
	Packet *p;
 
	const NetworkClientState *new_cs;
 
	const NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs);
 

	
 
	// Invalid packet when status is AUTH or higher
 
	if (cs->status >= STATUS_AUTH) return;
 

	
 
	cs->status = STATUS_AUTH;
 
	_network_game_info.clients_on++;
 
	/* increment spectator; defer company addition for when player is actually created */
 
	if (ci->client_playas == OWNER_SPECTATOR) _network_game_info.spectators_on++;
 

	
 
	p = NetworkSend_Init(PACKET_SERVER_WELCOME);
 
	NetworkSend_uint16(p, cs->index);
 
	NetworkSend_Packet(p, cs);
 

	
 
		// Transmit info about all the active clients
 
	FOR_ALL_CLIENTS(new_cs) {
 
		if (new_cs != cs && new_cs->status > STATUS_AUTH)
 
			SEND_COMMAND(PACKET_SERVER_CLIENT_INFO)(cs, DEREF_CLIENT_INFO(new_cs));
 
	}
 
	// Also send the info of the server
 
	SEND_COMMAND(PACKET_SERVER_CLIENT_INFO)(cs, NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX));
 
@@ -606,31 +598,31 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT
 
#endif
 

	
 
	NetworkRecv_string(cs, p, name, sizeof(name));
 
	playas = NetworkRecv_uint8(cs, p);
 
	client_lang = NetworkRecv_uint8(cs, p);
 
	NetworkRecv_string(cs, p, unique_id, sizeof(unique_id));
 

	
 
	if (cs->quited) return;
 

	
 
	// join another company does not affect these values
 
	switch (playas) {
 
		case 0: /* New company */
 
			if (_network_game_info.companies_on >= _network_game_info.companies_max) {
 
			if (ActivePlayerCount() >= _network_game_info.companies_max) {
 
				SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_FULL);
 
				return;
 
			}
 
			break;
 
		case OWNER_SPECTATOR: /* Spectator */
 
			if (_network_game_info.spectators_on >= _network_game_info.spectators_max) {
 
			if (NetworkSpectatorCount() >= _network_game_info.spectators_max) {
 
				SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_FULL);
 
				return;
 
			}
 
			break;
 
	}
 

	
 
	// We need a valid name.. make it Player
 
	if (name[0] == '\0') snprintf(name, sizeof(name), "Player");
 

	
 
	if (!NetworkFindName(name)) { // Change name if duplicate
 
		// We could not create a name for this player
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NAME_IN_USE);
network_udp.c
Show inline comments
 
@@ -55,35 +55,35 @@ DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIEN
 
	packet = NetworkSend_Init(PACKET_UDP_SERVER_RESPONSE);
 

	
 
	// Update some game_info
 
	_network_game_info.game_date = _date;
 
	_network_game_info.map_width = MapSizeX();
 
	_network_game_info.map_height = MapSizeY();
 
	_network_game_info.map_set = _opt.landscape;
 

	
 
	NetworkSend_uint8 (packet, NETWORK_GAME_INFO_VERSION);
 

	
 
	/* NETWORK_GAME_INFO_VERSION = 2 */
 
	NetworkSend_uint8 (packet, _network_game_info.companies_max);
 
	NetworkSend_uint8 (packet, _network_game_info.companies_on);
 
	NetworkSend_uint8 (packet, ActivePlayerCount());
 
	NetworkSend_uint8 (packet, _network_game_info.spectators_max);
 

	
 
	/* NETWORK_GAME_INFO_VERSION = 1 */
 
	NetworkSend_string(packet, _network_game_info.server_name);
 
	NetworkSend_string(packet, _network_game_info.server_revision);
 
	NetworkSend_uint8 (packet, _network_game_info.server_lang);
 
	NetworkSend_uint8 (packet, _network_game_info.use_password);
 
	NetworkSend_uint8 (packet, _network_game_info.clients_max);
 
	NetworkSend_uint8 (packet, _network_game_info.clients_on);
 
	NetworkSend_uint8 (packet, _network_game_info.spectators_on);
 
	NetworkSend_uint8 (packet, NetworkSpectatorCount());
 
	NetworkSend_uint16(packet, _network_game_info.game_date);
 
	NetworkSend_uint16(packet, _network_game_info.start_date);
 
	NetworkSend_string(packet, _network_game_info.map_name);
 
	NetworkSend_uint16(packet, _network_game_info.map_width);
 
	NetworkSend_uint16(packet, _network_game_info.map_height);
 
	NetworkSend_uint8 (packet, _network_game_info.map_set);
 
	NetworkSend_uint8 (packet, _network_game_info.dedicated);
 

	
 
	// Let the client know that we are here
 
	NetworkSendUDP_Packet(_udp_server_socket, packet, client_addr);
 

	
 
	free(packet);
 
@@ -153,42 +153,36 @@ DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVE
 

	
 
	item->online = true;
 

	
 
	UpdateNetworkGameWindow(false);
 
}
 

	
 
DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_DETAIL_INFO)
 
{
 
	NetworkClientState *cs;
 
	NetworkClientInfo *ci;
 
	Packet *packet;
 
	Player *player;
 
	byte active = 0;
 
	byte current = 0;
 
	int i;
 

	
 
	// Just a fail-safe.. should never happen
 
	if (!_network_udp_server)
 
		return;
 

	
 
	packet = NetworkSend_Init(PACKET_UDP_SERVER_DETAIL_INFO);
 

	
 
	FOR_ALL_PLAYERS(player) {
 
		if (player->is_active)
 
			active++;
 
	}
 

	
 
	/* Send the amount of active companies */
 
	NetworkSend_uint8 (packet, NETWORK_COMPANY_INFO_VERSION);
 
	NetworkSend_uint8 (packet, active);
 
	NetworkSend_uint8 (packet, ActivePlayerCount());
 

	
 
	/* Fetch the latest version of everything */
 
	NetworkPopulateCompanyInfo();
 

	
 
	/* Go through all the players */
 
	FOR_ALL_PLAYERS(player) {
 
		/* Skip non-active players */
 
		if (!player->is_active)
 
			continue;
 

	
 
		current++;
 

	
openttd.c
Show inline comments
 
@@ -753,31 +753,26 @@ void SwitchMode(int new_mode)
 
		break;
 

	
 
	case SM_LOAD: { /* Load game, Play Scenario */
 
		_opt_ptr = &_opt;
 

	
 
		if (!SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL)) {
 
			LoadIntroGame();
 
			ShowErrorMessage(INVALID_STRING_ID, STR_4009_GAME_LOAD_FAILED, 0, 0);
 
		} else {
 
			_local_player = 0;
 
			DoCommandP(0, 0, 0, NULL, CMD_PAUSE); // decrease pause counter (was increased from opening load dialog)
 
#ifdef ENABLE_NETWORK
 
			if (_network_server) {
 
				/* If we have loaded a game we need to correctly update the company-count */
 
				const Player *p;
 
				_network_game_info.companies_on = 0;
 
				FOR_ALL_PLAYERS(p) {if (p->is_active) _network_game_info.companies_on++;}
 
			if (_network_server)
 
				snprintf(_network_game_info.map_name, NETWORK_NAME_LENGTH, "%s (Loaded game)", _file_to_saveload.title);
 
			}
 
#endif /* ENABLE_NETWORK */
 
		}
 
		break;
 
	}
 

	
 
	case SM_LOAD_SCENARIO: { /* Load scenario from scenario editor */
 
		if (SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_EDITOR)) {
 
			PlayerID i;
 

	
 
			_opt_ptr = &_opt;
 

	
 
			_local_player = OWNER_NONE;
player.h
Show inline comments
 
@@ -202,24 +202,26 @@ int64 CalculateCompanyValue(const Player
 
void InvalidatePlayerWindows(const Player* p);
 
void UpdatePlayerMoney32(Player *p);
 
#define FOR_ALL_PLAYERS(p) for(p=_players; p != endof(_players); p++)
 

	
 
VARDEF PlayerID _local_player;
 
VARDEF PlayerID _current_player;
 

	
 
#define MAX_PLAYERS 8
 
VARDEF Player _players[MAX_PLAYERS];
 
// NOSAVE: can be determined from player structs
 
VARDEF byte _player_colors[MAX_PLAYERS];
 

	
 
byte ActivePlayerCount(void);
 

	
 
static inline Player* GetPlayer(PlayerID i)
 
{
 
	assert(i < lengthof(_players));
 
	return &_players[i];
 
}
 

	
 
static inline bool IsLocalPlayer(void)
 
{
 
	return _local_player == _current_player;
 
}
 

	
 
void DeletePlayerWindows(PlayerID pi);
players.c
Show inline comments
 
@@ -171,24 +171,36 @@ void DrawPlayerFace(uint32 face, int col
 
	/* draw the glasses */
 
	{
 
		uint val = GB(face, 28, 3);
 

	
 
		if (flag & 2) {
 
			if (val <= 1) DrawSprite(0x3AE + val, x, y);
 
		} else {
 
			if (val <= 1) DrawSprite(0x347 + val, x, y);
 
		}
 
	}
 
}
 

	
 
byte ActivePlayerCount(void)
 
{
 
	const Player *p;
 
	byte count = 0;
 

	
 
	FOR_ALL_PLAYERS(p) {
 
		if (p->is_active) count++;
 
	}
 

	
 
	return count;
 
}
 

	
 
void InvalidatePlayerWindows(const Player *p)
 
{
 
	PlayerID pid = p->index;
 

	
 
	if (pid == _local_player) InvalidateWindow(WC_STATUS_BAR, 0);
 
	InvalidateWindow(WC_FINANCES, pid);
 
}
 

	
 
bool CheckPlayerHasMoney(int32 cost)
 
{
 
	if (cost > 0) {
 
		PlayerID pid = _current_player;
 
@@ -848,33 +860,31 @@ int32 CmdPlayerCtrl(int x, int y, uint32
 
					 * 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 - 1;
 
					NetworkSend_Command(0, 0, 0, CMD_CHANGE_PRESIDENT_NAME, NULL);
 
					_local_player = player_backup;
 
					_network_game_info.companies_on++;
 
				}
 
			}
 
		} else if (_network_server) { // Creating player failed, defer client to spectator
 
				/* 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[pid];
 
			ci->client_playas = OWNER_SPECTATOR;
 
			_network_game_info.spectators_on++;
 
			NetworkUpdateClientInfo(ci->client_index);
 
		}
 
#else
 
		}
 
#endif /* ENABLE_NETWORK */
 
	} break;
 

	
 
	case 1: /* Make a new AI player */
 
		if (!(flags & DC_EXEC)) return 0;
 

	
 
		DoStartupNewPlayer(true);
 
		break;
 
@@ -895,27 +905,24 @@ int32 CmdPlayerCtrl(int x, int y, uint32
 

	
 
			/* Show the bankrupt news */
 
			SetDParam(0, p->name_1);
 
			SetDParam(1, p->name_2);
 
			AddNewsItem( (StringID)(p->index + 16*3), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
 

	
 
			/* Remove the company */
 
			ChangeOwnershipOfPlayerItems(p->index, OWNER_SPECTATOR);
 
			p->money64 = p->player_money = 100000000; // XXX - wtf?
 
			p->is_active = false;
 
		}
 
		RemoveAllEngineReplacementForPlayer(p);
 
#ifdef ENABLE_NETWORK
 
		_network_game_info.companies_on--;
 
#endif /* ENABLE_NETWORK */
 

	
 
	} break;
 

	
 
	case 3: { /* Merge a company (#1) into another company (#2), elimination company #1 */
 
		PlayerID pid_old = GB(p2,  0, 16);
 
		PlayerID pid_new = GB(p2, 16, 16);
 

	
 
		if (pid_old >= MAX_PLAYERS || pid_new >= MAX_PLAYERS) return CMD_ERROR;
 

	
 
		if (!(flags & DC_EXEC)) return CMD_ERROR;
 

	
 
		ChangeOwnershipOfPlayerItems(pid_old, pid_new);
0 comments (0 inline, 0 general)