changeset 10475:e8c75aac31ac draft

(svn r14730) -Codechange: remove the need for networkclientsockets and networkclientinfo structs to be in a contiguous piece of memory and put them in a pool. -Note: 255 should really be enough for now... making it any more means network protocol bumps.
author rubidium <rubidium@openttd.org>
date Tue, 23 Dec 2008 20:52:27 +0000
parents 67e76a023edd
children 407e5dfa32ad
files src/network/core/tcp.cpp src/network/core/tcp.h src/network/network.cpp src/network/network_base.h src/network/network_chat_gui.cpp src/network/network_client.cpp src/network/network_gui.cpp src/network/network_server.cpp src/network/network_type.h
diffstat 9 files changed, 89 insertions(+), 135 deletions(-) [+]
line wrap: on
line diff
--- a/src/network/core/tcp.cpp
+++ b/src/network/core/tcp.cpp
@@ -16,30 +16,25 @@
 #include "tcp.h"
 
 #include "table/strings.h"
+#include "../../oldpool_func.h"
 
-/** Very ugly temporary hack !!! */
-void NetworkClientSocket::Initialize()
+/** Make very sure the preconditions given in network_type.h are actually followed */
+assert_compile(MAX_CLIENT_SLOTS == (MAX_CLIENT_SLOTS >> NCI_BITS_PER_POOL_BLOCK) << NCI_BITS_PER_POOL_BLOCK);
+assert_compile(MAX_CLIENT_SLOTS > MAX_CLIENTS);
+
+typedef ClientIndex NetworkClientSocketID;
+DEFINE_OLD_POOL_GENERIC(NetworkClientSocket, NetworkClientSocket);
+
+NetworkClientSocket::NetworkClientSocket(ClientID client_id)
 {
 	this->sock              = INVALID_SOCKET;
-
-	this->client_id         = INVALID_CLIENT_ID;
-	this->last_frame        = 0;
-	this->last_frame_server = 0;
-	this->lag_test          = 0;
-
+	this->client_id         = client_id;
 	this->status            = STATUS_INACTIVE;
-	this->has_quit          = false;
-	this->writable          = false;
-
-	this->packet_queue      = NULL;
-	this->packet_recv       = NULL;
-
-	this->command_queue     = NULL;
 }
 
-void NetworkClientSocket::Destroy()
+NetworkClientSocket::~NetworkClientSocket()
 {
-	closesocket(this->sock);
+	if (this->sock != INVALID_SOCKET) closesocket(this->sock);
 	this->writable = false;
 	this->has_quit = true;
 
@@ -57,6 +52,10 @@
 		free(this->command_queue);
 		this->command_queue = p;
 	}
+
+	this->sock = INVALID_SOCKET;
+	this->client_id = INVALID_CLIENT_ID;
+	this->status = STATUS_INACTIVE;
 }
 
 /**
--- a/src/network/core/tcp.h
+++ b/src/network/core/tcp.h
@@ -84,12 +84,17 @@
 	STATUS_ACTIVE,     ///< The client is active within in the game
 };
 
+
+class NetworkClientSocket;
+DECLARE_OLD_POOL(NetworkClientSocket, NetworkClientSocket, NCI_BITS_PER_POOL_BLOCK, MAX_CLIENT_SLOTS >> NCI_BITS_PER_POOL_BLOCK);
+
 /** Base socket handler for all TCP sockets */
-class NetworkClientSocket : public NetworkSocketHandler {
+class NetworkClientSocket : public PoolItem<NetworkClientSocket, ClientIndex, &_NetworkClientSocket_pool>, public NetworkSocketHandler {
 /* TODO: rewrite into a proper class */
 private:
 	Packet *packet_queue;     ///< Packets that are awaiting delivery
 	Packet *packet_recv;      ///< Partially received packet
+	NetworkClientInfo *info;  ///< Client info related to this socket
 public:
 	ClientID client_id;       ///< Client identifier
 	uint32 last_frame;        ///< Last frame we have executed
@@ -102,8 +107,6 @@
 	CommandPacket *command_queue; ///< The command-queue awaiting delivery
 
 	NetworkRecvStatus CloseConnection();
-	void Initialize();
-	void Destroy();
 
 	void Send_Packet(Packet *packet);
 	bool Send_Packets();
@@ -111,27 +114,24 @@
 
 	Packet *Recv_Packet(NetworkRecvStatus *status);
 
+	NetworkClientSocket(ClientID client_id = INVALID_CLIENT_ID);
+	~NetworkClientSocket();
+
 	inline bool IsValid() const { return this->IsConnected(); }
-	inline NetworkClientInfo *GetInfo() const
-	{
-		extern NetworkClientSocket _clients[MAX_CLIENTS];
-		return GetNetworkClientInfo(this - _clients);
-	}
+	inline void SetInfo(NetworkClientInfo *info) { assert(info != NULL && this->info == NULL); this->info = info; }
+	inline NetworkClientInfo *GetInfo() const { return this->info; }
 };
 
-// Here we keep track of the clients
-//  (and the client uses [0] for his own communication)
-extern NetworkClientSocket _clients[MAX_CLIENTS];
-#define GetNetworkClientSocket(i) (&_clients[i])
-
 static inline bool IsValidNetworkClientSocketIndex(ClientIndex index)
 {
-	return (uint)index < MAX_CLIENTS && GetNetworkClientSocket(index)->IsValid();
+	return (uint)index < GetNetworkClientSocketPoolSize() && GetNetworkClientSocket(index)->IsValid();
 }
 
-#define FOR_ALL_CLIENT_SOCKETS_FROM(d, start) for (d = GetNetworkClientSocket(start); d != GetNetworkClientSocket(MAX_CLIENTS); d++) if (d->IsValid())
+#define FOR_ALL_CLIENT_SOCKETS_FROM(d, start) for (d = (start < GetNetworkClientSocketPoolSize() ? GetNetworkClientSocket(start) : NULL); d != NULL; d = (d->index + 1U < GetNetworkClientSocketPoolSize()) ? GetNetworkClientSocket(d->index + 1U) : NULL) if (d->IsValid())
 #define FOR_ALL_CLIENT_SOCKETS(d) FOR_ALL_CLIENT_SOCKETS_FROM(d, 0)
 
+typedef NetworkClientSocket NetworkTCPSocketHandler;
+
 #endif /* ENABLE_NETWORK */
 
 #endif /* NETWORK_CORE_TCP_H */
--- a/src/network/network.cpp
+++ b/src/network/network.cpp
@@ -42,15 +42,18 @@
 #endif /* DEBUG_DUMP_COMMANDS */
 #include "table/strings.h"
 #include "../company_base.h"
+#include "../oldpool_func.h"
 
 DECLARE_POSTFIX_INCREMENT(ClientID);
 
+typedef ClientIndex NetworkClientInfoID;
+DEFINE_OLD_POOL_GENERIC(NetworkClientInfo, NetworkClientInfo);
+
 bool _network_server;     ///< network-server is active
 bool _network_available;  ///< is network mode available?
 bool _network_dedicated;  ///< are we a dedicated server?
 bool _is_network_server;  ///< Does this client wants to be a network-server?
 NetworkServerGameInfo _network_game_info;
-NetworkClientInfo _network_client_info[MAX_CLIENT_SLOTS];
 NetworkCompanyState *_network_company_states = NULL;
 ClientID _network_own_client_id;
 ClientID _redirect_console_to_client;
@@ -83,12 +86,6 @@
 extern NetworkUDPSocketHandler *_udp_server_socket; ///< udp server socket
 extern NetworkUDPSocketHandler *_udp_master_socket; ///< udp master socket
 
-// Here we keep track of the clients
-//  (and the client uses [0] for his own communication)
-NetworkClientSocket _clients[MAX_CLIENTS];
-
-
-
 // The listen socket for the server
 static SOCKET _listensocket;
 
@@ -316,9 +313,6 @@
 	if (res != NETWORK_RECV_STATUS_SERVER_ERROR && res != NETWORK_RECV_STATUS_SERVER_FULL &&
 			res != NETWORK_RECV_STATUS_SERVER_BANNED) {
 		SEND_COMMAND(PACKET_CLIENT_ERROR)(errorno);
-
-		// Dequeue all commands before closing the socket
-		GetNetworkClientSocket(0)->Send_Packets();
 	}
 
 	_switch_mode = SM_MENU;
@@ -419,30 +413,24 @@
 //   Used both by the server and the client
 static NetworkClientSocket *NetworkAllocClient(SOCKET s)
 {
-	NetworkClientSocket *cs;
-	byte client_no = 0;
-
 	if (_network_server) {
 		// Can we handle a new client?
 		if (_network_clients_connected >= MAX_CLIENTS) return NULL;
 		if (_network_game_info.clients_on >= _settings_client.network.max_clients) return NULL;
 
 		// Register the login
-		client_no = _network_clients_connected++;
+		_network_clients_connected++;
 	}
 
-	cs = GetNetworkClientSocket(client_no);
-	cs->Initialize();
+	NetworkClientSocket *cs = new NetworkClientSocket(INVALID_CLIENT_ID);
 	cs->sock = s;
 	cs->last_frame = _frame_counter;
 	cs->last_frame_server = _frame_counter;
 
 	if (_network_server) {
-		NetworkClientInfo *ci = cs->GetInfo();
-		memset(ci, 0, sizeof(*ci));
-
 		cs->client_id = _network_client_id++;
-		ci->client_id = cs->client_id;
+		NetworkClientInfo *ci = new NetworkClientInfo(cs->client_id);
+		cs->SetInfo(ci);
 		ci->client_playas = COMPANY_INACTIVE_CLIENT;
 		ci->join_date = _date;
 
@@ -455,12 +443,7 @@
 // Close a connection
 void NetworkCloseClient(NetworkClientSocket *cs)
 {
-	NetworkClientInfo *ci;
-	// Socket is already dead
-	if (cs->sock == INVALID_SOCKET) {
-		cs->has_quit = true;
-		return;
-	}
+	assert(cs->sock != INVALID_SOCKET);
 
 	DEBUG(net, 1, "Closed client connection %d", cs->client_id);
 
@@ -491,31 +474,16 @@
 		NetworkServerSendChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game unpaused", CLIENT_ID_SERVER);
 	}
 
-	cs->Destroy();
-
-	// Close the gap in the client-list
-	ci = cs->GetInfo();
-
 	if (_network_server) {
 		// We just lost one client :(
 		if (cs->status >= STATUS_AUTH) _network_game_info.clients_on--;
 		_network_clients_connected--;
 
-		while ((cs + 1) != GetNetworkClientSocket(MAX_CLIENTS) && (cs + 1)->sock != INVALID_SOCKET) {
-			*cs = *(cs + 1);
-			*ci = *(ci + 1);
-			cs++;
-			ci++;
-		}
-
 		InvalidateWindow(WC_CLIENT_LIST, 0);
 	}
 
-	// Reset the status of the last socket
-	cs->sock = INVALID_SOCKET;
-	cs->status = STATUS_INACTIVE;
-	cs->client_id = INVALID_CLIENT_ID;
-	ci->client_id = INVALID_CLIENT_ID;
+	delete cs->GetInfo();
+	delete cs;
 
 	CheckMinActiveClients();
 }
@@ -697,22 +665,20 @@
 
 	free(_network_company_states);
 	_network_company_states = NULL;
+
+	_NetworkClientSocket_pool.CleanPool();
+	_NetworkClientInfo_pool.CleanPool();
 }
 
 // Inits the network (cleans sockets and stuff)
 static void NetworkInitialize()
 {
-	NetworkClientSocket *cs;
-
 	_local_command_queue = NULL;
 
-	// Clean all client-sockets
-	for (cs = _clients; cs != &_clients[MAX_CLIENTS]; cs++) {
-		cs->Initialize();
-	}
-
-	// Clean the client_info memory
-	memset(&_network_client_info, 0, sizeof(_network_client_info));
+	_NetworkClientSocket_pool.CleanPool();
+	_NetworkClientSocket_pool.AddBlockToPool();
+	_NetworkClientInfo_pool.CleanPool();
+	_NetworkClientInfo_pool.AddBlockToPool();
 
 	_sync_frame = 0;
 	_network_first_time = true;
@@ -817,8 +783,6 @@
 
 static void NetworkInitGameInfo()
 {
-	NetworkClientInfo *ci;
-
 	if (StrEmpty(_settings_client.network.server_name)) {
 		snprintf(_settings_client.network.server_name, sizeof(_settings_client.network.server_name), "Unnamed Server");
 	}
@@ -827,12 +791,7 @@
 	_network_game_info.clients_on = _network_dedicated ? 0 : 1;
 	_network_game_info.start_date = ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1);
 
-	// We use _network_client_info[MAX_CLIENT_SLOTS - 1] to store the server-data in it
-	//  The client identifier is CLIENT_ID_SERVER ( = 1)
-	ci = &_network_client_info[MAX_CLIENT_SLOTS - 1];
-	memset(ci, 0, sizeof(*ci));
-
-	ci->client_id = CLIENT_ID_SERVER;
+	NetworkClientInfo *ci = new NetworkClientInfo(CLIENT_ID_SERVER);
 	ci->client_playas = _network_dedicated ? COMPANY_SPECTATOR : _local_company;
 
 	strecpy(ci->client_name, _settings_client.network.client_name, lastof(ci->client_name));
--- a/src/network/network_base.h
+++ b/src/network/network_base.h
@@ -8,8 +8,11 @@
 #ifdef ENABLE_NETWORK
 
 #include "network_type.h"
+#include "../oldpool.h"
 
-struct NetworkClientInfo {
+DECLARE_OLD_POOL(NetworkClientInfo, NetworkClientInfo, NCI_BITS_PER_POOL_BLOCK, MAX_CLIENT_SLOTS >> NCI_BITS_PER_POOL_BLOCK);
+
+struct NetworkClientInfo : PoolItem<NetworkClientInfo, ClientIndex, &_NetworkClientInfo_pool> {
 	ClientID client_id;                             ///< Client identifier (same as ClientState->client_id)
 	char client_name[NETWORK_CLIENT_NAME_LENGTH];   ///< Name of the client
 	byte client_lang;                               ///< The language of the client
@@ -18,21 +21,18 @@
 	Date join_date;                                 ///< Gamedate the client has joined
 	char unique_id[NETWORK_UNIQUE_ID_LENGTH];       ///< Every play sends an unique id so we can indentify him
 
+	NetworkClientInfo(ClientID client_id = INVALID_CLIENT_ID) : client_id(client_id) {}
+	~NetworkClientInfo() { client_id = INVALID_CLIENT_ID; }
+
 	inline bool IsValid() const { return client_id != INVALID_CLIENT_ID; }
 };
 
-static NetworkClientInfo *GetNetworkClientInfo(int ci)
+static inline bool IsValidNetworkClientInfoIndex(ClientIndex index)
 {
-	extern NetworkClientInfo _network_client_info[MAX_CLIENT_SLOTS];
-	return &_network_client_info[ci];
+	return (uint)index < GetNetworkClientInfoPoolSize() && GetNetworkClientInfo(index)->IsValid();
 }
 
-static inline bool IsValidNetworkClientInfoIndex(ClientIndex index)
-{
-	return (uint)index < MAX_CLIENT_SLOTS && GetNetworkClientInfo(index)->IsValid();
-}
-
-#define FOR_ALL_CLIENT_INFOS_FROM(d, start) for (ci = GetNetworkClientInfo(start); ci != GetNetworkClientInfo(MAX_CLIENT_SLOTS); ci++) if (ci->IsValid())
+#define FOR_ALL_CLIENT_INFOS_FROM(d, start) for (d = GetNetworkClientInfo(start); d != NULL; d = (d->index + 1U < GetNetworkClientInfoPoolSize()) ? GetNetworkClientInfo(d->index + 1U) : NULL) if (d->IsValid())
 #define FOR_ALL_CLIENT_INFOS(d) FOR_ALL_CLIENT_INFOS_FROM(d, 0)
 
 #endif /* ENABLE_NETWORK */
--- a/src/network/network_chat_gui.cpp
+++ b/src/network/network_chat_gui.cpp
@@ -301,8 +301,13 @@
 		/* First, try clients */
 		if (*item < MAX_CLIENT_SLOTS) {
 			/* Skip inactive clients */
-			while (GetNetworkClientInfo(*item)->client_id == INVALID_CLIENT_ID && *item < MAX_CLIENT_SLOTS) (*item)++;
-			if (*item < MAX_CLIENT_SLOTS) return GetNetworkClientInfo(*item)->client_name;
+			NetworkClientInfo *ci;
+			FOR_ALL_CLIENT_INFOS_FROM(ci, *item + 1) break;
+			if (ci != NULL) {
+				*item = ci->index;
+				return ci->client_name;
+			}
+			*item = MAX_CLIENT_SLOTS;
 		}
 
 		/* Then, try townnames */
--- a/src/network/network_client.cpp
+++ b/src/network/network_client.cpp
@@ -420,23 +420,15 @@
 	}
 
 	// We don't have this client_id yet, find an empty client_id, and put the data there
-	for (int i = 0; i < MAX_CLIENT_SLOTS; i++) {
-		ci = GetNetworkClientInfo(i);
-		if (!ci->IsValid()) break;
-	}
-	if (ci != GetNetworkClientInfo(MAX_CLIENT_SLOTS)) {
-		ci->client_id = client_id;
-		ci->client_playas = playas;
+	ci = new NetworkClientInfo(client_id);
+	ci->client_playas = playas;
+	if (client_id == _network_own_client_id) MY_CLIENT->SetInfo(ci);
 
-		strecpy(ci->client_name, name, lastof(ci->client_name));
-
-		InvalidateWindow(WC_CLIENT_LIST, 0);
+	strecpy(ci->client_name, name, lastof(ci->client_name));
 
-		return NETWORK_RECV_STATUS_OKAY;
-	}
+	InvalidateWindow(WC_CLIENT_LIST, 0);
 
-	// Here the program should never ever come.....
-	return NETWORK_RECV_STATUS_MALFORMED_PACKET;
+	return NETWORK_RECV_STATUS_OKAY;
 }
 
 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_ERROR)
@@ -765,9 +757,7 @@
 	ci = NetworkFindClientInfoFromClientID(client_id);
 	if (ci != NULL) {
 		NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, ci->client_name, "%s", str);
-
-		// The client is gone, give the NetworkClientInfo free
-		ci->client_id = INVALID_CLIENT_ID;
+		delete ci;
 	}
 
 	InvalidateWindow(WC_CLIENT_LIST, 0);
@@ -786,9 +776,7 @@
 	ci = NetworkFindClientInfoFromClientID(client_id);
 	if (ci != NULL) {
 		NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, ci->client_name, "%s", str);
-
-		// The client is gone, give the NetworkClientInfo free
-		ci->client_id = INVALID_CLIENT_ID;
+		delete ci;
 	} else {
 		DEBUG(net, 0, "Unknown client (%d) is leaving the game", client_id);
 	}
--- a/src/network/network_gui.cpp
+++ b/src/network/network_gui.cpp
@@ -967,7 +967,7 @@
 			case NSSW_CLIENTS_TXT:    // Click on number of clients
 				this->widget_id = NSSW_CLIENTS_TXT;
 				SetDParam(0, _settings_client.network.max_clients);
-				ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_CLIENTS,    3, 50, this, CS_NUMERAL, QSF_NONE);
+				ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_CLIENTS,    4, 50, this, CS_NUMERAL, QSF_NONE);
 				break;
 
 			case NSSW_COMPANIES_TXT:  // Click on number of companies
@@ -979,7 +979,7 @@
 			case NSSW_SPECTATORS_TXT: // Click on number of spectators
 				this->widget_id = NSSW_SPECTATORS_TXT;
 				SetDParam(0, _settings_client.network.max_spectators);
-				ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_SPECTATORS, 3, 50, this, CS_NUMERAL, QSF_NONE);
+				ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_SPECTATORS, 4, 50, this, CS_NUMERAL, QSF_NONE);
 				break;
 
 			case NSSW_LANGUAGE_BTN: { // Language
--- a/src/network/network_server.cpp
+++ b/src/network/network_server.cpp
@@ -917,7 +917,7 @@
 		 * spectator, but that is not allowed any commands. So do an impersonation. The drawback
 		 * of this is that the first company's last_built_tile is also updated... */
 		cp->company = OWNER_BEGIN;
-		cp->p2 = cs - _clients; // XXX - UGLY! p2 is mis-used to get the client-id in CmdCompanyCtrl
+		cp->p2 = cs->index; // XXX - UGLY! p2 is mis-used to get the client-id in CmdCompanyCtrl
 	}
 
 	// The frame can be executed in the same frame as the next frame-packet
--- a/src/network/network_type.h
+++ b/src/network/network_type.h
@@ -13,12 +13,18 @@
 #include "core/game.h"
 
 enum {
+	/** How many clients can we have */
+	MAX_CLIENTS = 255,
+
+	/** The number of bits per pool client block */
+	NCI_BITS_PER_POOL_BLOCK = 3, // => 8 items per block
 	/**
-	 * How many clients can we have? Like.. MAX_COMPANIES is the amount of
-	 *  companies that can really play.. so.. a max of 3 spectators.. gives us..
-	 *  MAX_COMPANIES + 3
+	 * The number of slots; must be a multiple of (1 << NCI_BITS_PER_POOL_BLOCK)
+	 * and be at least 1 more than MAX_CLIENTS. It must furthermore be less than
+	 * or equal to 256 as client indices (sent over the network) are 8 bits.
+	 * It needs 1 more for the dedicated server.
 	 */
-	MAX_CLIENTS = MAX_COMPANIES + 3,
+	MAX_CLIENT_SLOTS = 256,
 
 	/** Maximum number of internet interfaces supported. */
 	MAX_INTERFACES = 9,
@@ -36,10 +42,7 @@
 };
 
 /** Indices into the client tables */
-enum ClientIndex {
-	/** Do not change this next line. It should _ALWAYS_ be MAX_CLIENTS + 1. This due to the (dedicated) server taking one slot. */
-	MAX_CLIENT_SLOTS = MAX_CLIENTS + 1,
-};
+typedef uint8 ClientIndex;
 
 /** Simple calculated statistics of a company */
 struct NetworkCompanyStats {