From afddfcb2b1a37358459158ce83a64535f13cd05e Mon Sep 17 00:00:00 2001 From: rubidium Date: Tue, 23 Dec 2008 20:52:27 +0000 Subject: [PATCH] (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. --- src/network/core/tcp.cpp | 33 +++++++------ src/network/core/tcp.h | 32 ++++++------- src/network/network.cpp | 79 ++++++++------------------------ src/network/network_base.h | 18 ++++---- src/network/network_chat_gui.cpp | 9 +++- src/network/network_client.cpp | 28 ++++------- src/network/network_gui.cpp | 4 +- src/network/network_server.cpp | 2 +- src/network/network_type.h | 19 ++++---- 9 files changed, 89 insertions(+), 135 deletions(-) diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp index 5ce7d4831b..304e1d37e9 100644 --- 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 @@ void NetworkClientSocket::Destroy() free(this->command_queue); this->command_queue = p; } + + this->sock = INVALID_SOCKET; + this->client_id = INVALID_CLIENT_ID; + this->status = STATUS_INACTIVE; } /** diff --git a/src/network/core/tcp.h b/src/network/core/tcp.h index aa5bb7c4a4..06d382f6c3 100644 --- a/src/network/core/tcp.h +++ b/src/network/core/tcp.h @@ -84,12 +84,17 @@ enum ClientStatus { 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, 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 @@ public: 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 @@ public: Packet *Recv_Packet(NetworkRecvStatus *status); - inline bool IsValid() const { return this->IsConnected(); } - inline NetworkClientInfo *GetInfo() const - { - extern NetworkClientSocket _clients[MAX_CLIENTS]; - return GetNetworkClientInfo(this - _clients); - } -}; + NetworkClientSocket(ClientID client_id = INVALID_CLIENT_ID); + ~NetworkClientSocket(); -// 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]) + inline bool IsValid() const { return this->IsConnected(); } + inline void SetInfo(NetworkClientInfo *info) { assert(info != NULL && this->info == NULL); this->info = info; } + inline NetworkClientInfo *GetInfo() const { return this->info; } +}; 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 */ diff --git a/src/network/network.cpp b/src/network/network.cpp index eed479a1e8..3d854c3209 100644 --- 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_client_socket; ///< udp client socket 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 @@ static void NetworkClientError(NetworkRecvStatus res, NetworkClientSocket* cs) 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 @@ void ParseConnectionString(const char **company, const char **port, char *connec // 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 @@ static NetworkClientSocket *NetworkAllocClient(SOCKET s) // 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 @@ void NetworkCloseClient(NetworkClientSocket *cs) 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 @@ static void NetworkClose() 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 @@ bool NetworkClientConnectGame(const char *host, uint16 port) 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 @@ static void NetworkInitGameInfo() _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)); diff --git a/src/network/network_base.h b/src/network/network_base.h index e8171a027c..be3ada0c8e 100644 --- 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 { 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 @@ struct NetworkClientInfo { 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) -{ - extern NetworkClientInfo _network_client_info[MAX_CLIENT_SLOTS]; - return &_network_client_info[ci]; -} - static inline bool IsValidNetworkClientInfoIndex(ClientIndex index) { - return (uint)index < MAX_CLIENT_SLOTS && GetNetworkClientInfo(index)->IsValid(); + return (uint)index < GetNetworkClientInfoPoolSize() && 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 */ diff --git a/src/network/network_chat_gui.cpp b/src/network/network_chat_gui.cpp index fc36bec535..4f4447779c 100644 --- a/src/network/network_chat_gui.cpp +++ b/src/network/network_chat_gui.cpp @@ -301,8 +301,13 @@ struct NetworkChatWindow : public QueryStringBaseWindow { /* 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 */ diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index f4d49539e3..5abb151fd1 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -420,23 +420,15 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_CLIENT_INFO) } // 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)); + strecpy(ci->client_name, name, lastof(ci->client_name)); - InvalidateWindow(WC_CLIENT_LIST, 0); + InvalidateWindow(WC_CLIENT_LIST, 0); - return NETWORK_RECV_STATUS_OKAY; - } - - // 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 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_ERROR_QUIT) 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 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_QUIT) 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); } diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index e4bcde4fac..2a3ff1ca35 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -967,7 +967,7 @@ struct NetworkStartServerWindow : public QueryStringBaseWindow { 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 @@ struct NetworkStartServerWindow : public QueryStringBaseWindow { 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 diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index e00e201cd9..d48efe55ea 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -917,7 +917,7 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) * 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 diff --git a/src/network/network_type.h b/src/network/network_type.h index c04fd1f5a8..2878819b51 100644 --- 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 @@ enum ClientID { }; /** 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 {