diff options
author | rubidium <rubidium@openttd.org> | 2009-01-23 22:18:06 +0000 |
---|---|---|
committer | rubidium <rubidium@openttd.org> | 2009-01-23 22:18:06 +0000 |
commit | 2722cabcce204b1903d2af6fd8c94f825c10a60d (patch) | |
tree | af25f72d8f74dc4f3847b4a36733b273b2c642b0 /src/network | |
parent | ac7883fe9396fca915ce59567a7d71ae417d16ae (diff) | |
download | openttd-2722cabcce204b1903d2af6fd8c94f825c10a60d.tar.xz |
(svn r15242) -Feature: allow moving clients between companies/spectators by the server and the clients themselves (dihedral)
Diffstat (limited to 'src/network')
-rw-r--r-- | src/network/core/tcp_game.h | 4 | ||||
-rw-r--r-- | src/network/network.cpp | 23 | ||||
-rw-r--r-- | src/network/network_client.cpp | 97 | ||||
-rw-r--r-- | src/network/network_client.h | 1 | ||||
-rw-r--r-- | src/network/network_func.h | 7 | ||||
-rw-r--r-- | src/network/network_internal.h | 2 | ||||
-rw-r--r-- | src/network/network_server.cpp | 125 | ||||
-rw-r--r-- | src/network/network_server.h | 1 | ||||
-rw-r--r-- | src/network/network_type.h | 3 |
9 files changed, 263 insertions, 0 deletions
diff --git a/src/network/core/tcp_game.h b/src/network/core/tcp_game.h index 1c0f047ea..3acd61310 100644 --- a/src/network/core/tcp_game.h +++ b/src/network/core/tcp_game.h @@ -54,6 +54,10 @@ enum { PACKET_CLIENT_RCON, PACKET_SERVER_CHECK_NEWGRFS, PACKET_CLIENT_NEWGRFS_CHECKED, + PACKET_SERVER_MOVE, + PACKET_CLIENT_MOVE, + PACKET_SERVER_COMPANY_UPDATE, + PACKET_SERVER_CONFIG_UPDATE, PACKET_END ///< Must ALWAYS be on the end of this list!! (period) }; diff --git a/src/network/network.cpp b/src/network/network.cpp index 4fbad7535..237dbd57a 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -75,6 +75,7 @@ bool _network_first_time; bool _network_udp_server; uint16 _network_udp_broadcast; uint8 _network_advertise_retries; +CompanyMask _network_company_passworded; ///< Bitmask of the password status of all companies. /* Check whether NETWORK_NUM_LANDSCAPES is still in sync with NUM_LANDSCAPE */ assert_compile((int)NETWORK_NUM_LANDSCAPES == (int)NUM_LANDSCAPE); @@ -182,6 +183,16 @@ byte NetworkSpectatorCount() return count; } +/** + * Check if the company we want to join requires a password. + * @param company_id id of the company we want to check the 'passworded' flag for. + * @return true if the company requires a password. + */ +bool NetworkCompanyIsPassworded(CompanyID company_id) +{ + return HasBit(_network_company_passworded, company_id); +} + // 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 @@ -199,6 +210,18 @@ void NetworkTextMessage(NetworkAction action, ConsoleColour color, bool self_sen color = CC_DEFAULT; data = STR_NETWORK_SERVER_MESSAGE_GAME_PAUSED_PLAYERS + data; break; + case NETWORK_ACTION_COMPANY_SPECTATOR: + color = CC_DEFAULT; + strid = STR_NETWORK_CLIENT_COMPANY_SPECTATE; + break; + case NETWORK_ACTION_COMPANY_JOIN: + color = CC_DEFAULT; + strid = STR_NETWORK_CLIENT_COMPANY_JOIN; + break; + case NETWORK_ACTION_COMPANY_NEW: + color = CC_DEFAULT; + strid = STR_NETWORK_CLIENT_COMPANY_NEW; + break; case NETWORK_ACTION_JOIN: strid = STR_NETWORK_CLIENT_JOINED; break; case NETWORK_ACTION_LEAVE: strid = STR_NETWORK_CLIENT_LEFT; break; case NETWORK_ACTION_NAME_CHANGE: strid = STR_NETWORK_NAME_CHANGE; break; diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index c8116fc4c..28be67608 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -7,6 +7,7 @@ #include "../stdafx.h" #include "../debug.h" #include "../openttd.h" +#include "../gfx_func.h" #include "network_internal.h" #include "core/tcp.h" #include "network_client.h" @@ -25,6 +26,7 @@ #include "../company_func.h" #include "../company_base.h" #include "../company_gui.h" +#include "../company_type.h" #include "../settings_type.h" #include "../rev.h" @@ -43,6 +45,11 @@ static uint32 _password_game_seed; /** The other bit of 'entropy' used to generate a salt for the company passwords. */ static char _password_server_unique_id[NETWORK_UNIQUE_ID_LENGTH]; +/** Maximum number of companies of the currently joined server. */ +static uint8 _network_server_max_companies; +/** Maximum number of spectators of the currently joined server. */ +static uint8 _network_server_max_spectators; + /** Make sure the unique ID length is the same as a md5 hash. */ assert_compile(NETWORK_UNIQUE_ID_LENGTH == 16 * 2 + 1); @@ -86,6 +93,10 @@ void HashCurrentCompanyPassword(const char *password) const char *new_pw = GenerateCompanyPasswordHash(password); strecpy(_network_company_states[_local_company].password, new_pw, lastof(_network_company_states[_local_company].password)); + + if (_network_server) { + NetworkServerUpdateCompanyPassworded(_local_company, !StrEmpty(_network_company_states[_local_company].password)); + } } @@ -315,6 +326,14 @@ DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_RCON)(const char *pass, const char * MY_CLIENT->Send_Packet(p); } +DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_MOVE)(CompanyID company, const char *pass) +{ + Packet *p = NetworkSend_Init(PACKET_CLIENT_MOVE); + p->Send_uint8(company); + p->Send_string(GenerateCompanyPasswordHash(pass)); + MY_CLIENT->Send_Packet(p); +} + // ********** // Receiving functions @@ -811,6 +830,51 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_RCON) return NETWORK_RECV_STATUS_OKAY; } +DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_MOVE) +{ + /* Nothing more in this packet... */ + ClientID client_id = (ClientID)p->Recv_uint32(); + CompanyID company_id = (CompanyID)p->Recv_uint8(); + + if (client_id == 0) { + /* definitely an invalid client id, debug message and do nothing. */ + DEBUG(net, 0, "[move] received invalid client index = 0"); + return NETWORK_RECV_STATUS_MALFORMED_PACKET; + } + + const NetworkClientInfo *ci = NetworkFindClientInfoFromClientID(client_id); + /* Just make sure we do not try to use a client_index that does not exist */ + if (ci == NULL) return NETWORK_RECV_STATUS_OKAY; + + /* if not valid player, force spectator, else check player exists */ + if (!IsValidCompanyID(company_id)) company_id = COMPANY_SPECTATOR; + + if (client_id == _network_own_client_id) { + _network_playas = company_id; + SetLocalCompany(company_id); + + /* Disable any buttons in any windows the client is now not supposed to get to, and do it fast. */ + /* Do this ASAP else the client has a chance of sending DoCommands with an incorrect company_id (=kick)! */ + MarkWholeScreenDirty(); + } + + return NETWORK_RECV_STATUS_OKAY; +} + +DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_CONFIG_UPDATE) +{ + _network_server_max_companies = p->Recv_uint8(); + _network_server_max_spectators = p->Recv_uint8(); + + return NETWORK_RECV_STATUS_OKAY; +} + +DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_COMPANY_UPDATE) +{ + _network_company_passworded = p->Recv_uint16(); + + return NETWORK_RECV_STATUS_OKAY; +} // The layout for the receive-functions by the client @@ -855,6 +919,10 @@ static NetworkClientPacket * const _network_client_packet[] = { NULL, /*PACKET_CLIENT_RCON,*/ RECEIVE_COMMAND(PACKET_SERVER_CHECK_NEWGRFS), NULL, /*PACKET_CLIENT_NEWGRFS_CHECKED,*/ + RECEIVE_COMMAND(PACKET_SERVER_MOVE), + NULL, /* PACKET_CLIENT_MOVE */ + RECEIVE_COMMAND(PACKET_SERVER_COMPANY_UPDATE), + RECEIVE_COMMAND(PACKET_SERVER_CONFIG_UPDATE), }; // If this fails, check the array above with network_data.h @@ -897,6 +965,17 @@ void NetworkClientSendRcon(const char *password, const char *command) SEND_COMMAND(PACKET_CLIENT_RCON)(password, command); } +/** + * Notify the server of this client wanting to be moved to another company. + * @param company_id id of the company the client wishes to be moved to. + * @param pass the password, is only checked on the server end if a password is needed. + * @return void + */ +void NetworkClientRequestMove(CompanyID company_id, const char *pass) +{ + SEND_COMMAND(PACKET_CLIENT_MOVE)(company_id, pass); +} + void NetworkUpdateClientName() { NetworkClientInfo *ci = NetworkFindClientInfoFromClientID(_network_own_client_id); @@ -946,6 +1025,24 @@ bool NetworkClientPreferTeamChat(const NetworkClientInfo *cio) } /** + * Check if max_companies has been reached on the server (local check only). + * @return true if the max value has been reached or exceeded, false otherwise. + */ +bool NetworkMaxCompaniesReached() +{ + return ActiveCompanyCount() >= (_network_server ? _settings_client.network.max_companies : _network_server_max_companies); +} + +/** + * Check if max_spectatos has been reached on the server (local check only). + * @return true if the max value has been reached or exceeded, false otherwise. + */ +bool NetworkMaxSpectatorsReached() +{ + return NetworkSpectatorCount() >= (_network_server ? _settings_client.network.max_spectators : _network_server_max_spectators); +} + +/** * Print all the clients to the console */ void NetworkPrintClients() diff --git a/src/network/network_client.h b/src/network/network_client.h index 28ab3affe..88747d587 100644 --- a/src/network/network_client.h +++ b/src/network/network_client.h @@ -18,6 +18,7 @@ DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_SET_PASSWORD)(const char *password); DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_SET_NAME)(const char *name); DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_ACK); DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_RCON)(const char *pass, const char *command); +DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_MOVE)(CompanyID company, const char *pass); NetworkRecvStatus NetworkClient_ReadPackets(NetworkClientSocket *cs); void NetworkClient_Connected(); diff --git a/src/network/network_func.h b/src/network/network_func.h index e51ff38d7..70bc96ff0 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -38,18 +38,24 @@ void NetworkPopulateCompanyStats(NetworkCompanyStats *stats); void NetworkUpdateClientInfo(ClientID client_id); void NetworkClientConnectGame(NetworkAddress address); +void NetworkClientRequestMove(CompanyID company, const char *pass = ""); void NetworkClientSendRcon(const char *password, const char *command); void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const char *msg, int64 data = 0); void NetworkClientSetPassword(const char *password); bool NetworkClientPreferTeamChat(const NetworkClientInfo *cio); +bool NetworkCompanyIsPassworded(CompanyID company_id); +bool NetworkMaxCompaniesReached(); +bool NetworkMaxSpectatorsReached(); void NetworkPrintClients(); /*** Commands ran by the server ***/ void NetworkServerMonthlyLoop(); void NetworkServerYearlyLoop(); void NetworkServerChangeOwner(Owner current_owner, Owner new_owner); +void NetworkServerSendConfigUpdate(); void NetworkServerShowStatusToConsole(); bool NetworkServerStart(); +void NetworkServerUpdateCompanyPassworded(CompanyID company_id, bool passworded); bool NetworkServerChangeClientName(ClientID client_id, const char *new_name); NetworkClientInfo *NetworkFindClientInfoFromIndex(ClientIndex index); @@ -57,6 +63,7 @@ NetworkClientInfo *NetworkFindClientInfoFromClientID(ClientID client_id); NetworkClientInfo *NetworkFindClientInfoFromIP(const char *ip); const char *GetClientIP(const NetworkClientInfo *ci); +void NetworkServerDoMove(ClientID client_id, CompanyID company_id); void NetworkServerSendRcon(ClientID client_id, ConsoleColour colour_code, const char *string); void NetworkServerSendError(ClientID client_id, NetworkErrorCode error); void NetworkServerSendChat(NetworkAction action, DestType type, int dest, const char *msg, ClientID from_id, int64 data = 0); diff --git a/src/network/network_internal.h b/src/network/network_internal.h index b5c6d4de2..b73fe1d0f 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -121,6 +121,8 @@ extern uint16 _network_udp_broadcast; extern uint8 _network_advertise_retries; +extern CompanyMask _network_company_passworded; + void NetworkTCPQueryServer(NetworkAddress address); void NetworkAddServer(const char *b); diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index d291bb92a..647358ecf 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -586,6 +586,32 @@ DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_RCON)(NetworkClientSocket *cs, uint1 cs->Send_Packet(p); } +DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_MOVE)(NetworkClientSocket *cs, ClientID client_id, CompanyID company_id) +{ + Packet *p = NetworkSend_Init(PACKET_SERVER_MOVE); + + p->Send_uint32(client_id); + p->Send_uint8(company_id); + cs->Send_Packet(p); +} + +DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_COMPANY_UPDATE)(NetworkClientSocket *cs) +{ + Packet *p = NetworkSend_Init(PACKET_SERVER_COMPANY_UPDATE); + + p->Send_uint16(_network_company_passworded); + cs->Send_Packet(p); +} + +DEF_SERVER_SEND_COMMAND(PACKET_SERVER_CONFIG_UPDATE) +{ + Packet *p = NetworkSend_Init(PACKET_SERVER_CONFIG_UPDATE); + + p->Send_uint8(_settings_client.network.max_companies); + p->Send_uint8(_settings_client.network.max_spectators); + cs->Send_Packet(p); +} + // ********** // Receiving functions // DEF_SERVER_RECEIVE_COMMAND has parameter: NetworkClientSocket *cs, Packet *p @@ -803,6 +829,12 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_MAP_OK) NetworkServerSendChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "", CLIENT_ID_SERVER, NETWORK_SERVER_MESSAGE_GAME_PAUSED_CONNECT); } + + /* also update the new client with our max values */ + SEND_COMMAND(PACKET_SERVER_CONFIG_UPDATE)(cs); + + /* quickly update the syncing client with company details */ + SEND_COMMAND(PACKET_SERVER_COMPANY_UPDATE)(cs); } else { // Wrong status for this packet, give a warning to client, and close connection SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED); @@ -872,6 +904,12 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) return; } + /* Check if we are full - else it's possible for spectators to send a CMD_COMPANY_CTRL and the company is created regardless of max_companies! */ + if (ActiveCompanyCount() >= _settings_client.network.max_companies) { + NetworkServerSendChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_CLIENT, ci->client_id, "cannot create new company, server full", CLIENT_ID_SERVER); + return; + } + /* XXX - Execute the command as a valid company. Normally this would be done by a * 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... */ @@ -1145,6 +1183,7 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_SET_PASSWORD) if (IsValidCompanyID(ci->client_playas)) { strecpy(_network_company_states[ci->client_playas].password, password, lastof(_network_company_states[ci->client_playas].password)); + NetworkServerUpdateCompanyPassworded(ci->client_playas, !StrEmpty(_network_company_states[ci->client_playas].password)); } } @@ -1197,6 +1236,30 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_RCON) return; } +DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_MOVE) +{ + CompanyID company_id = (Owner)p->Recv_uint8(); + + /* Check if the company is valid */ + if (!IsValidCompanyID(company_id) && company_id != COMPANY_SPECTATOR) return; + + /* Check if we require a password for this company */ + if (company_id != COMPANY_SPECTATOR && !StrEmpty(_network_company_states[company_id].password)) { + /* we need a password from the client - should be in this packet */ + char password[NETWORK_PASSWORD_LENGTH]; + p->Recv_string(password, sizeof(password)); + + /* Incorrect password sent, return! */ + if (strcmp(password, _network_company_states[company_id].password) != 0) { + DEBUG(net, 2, "[move] wrong password from client-id #%d for company #%d", cs->client_id, company_id + 1); + return; + } + } + + /* if we get here we can move the client */ + NetworkServerDoMove(cs->client_id, company_id); +} + // The layout for the receive-functions by the server typedef void NetworkServerPacket(NetworkClientSocket *cs, Packet *p); @@ -1240,6 +1303,10 @@ static NetworkServerPacket * const _network_server_packet[] = { RECEIVE_COMMAND(PACKET_CLIENT_RCON), NULL, /*PACKET_CLIENT_CHECK_NEWGRFS,*/ RECEIVE_COMMAND(PACKET_CLIENT_NEWGRFS_CHECKED), + NULL, /*PACKET_SERVER_MOVE,*/ + RECEIVE_COMMAND(PACKET_CLIENT_MOVE), + NULL, /*PACKET_SERVER_COMPANY_UPDATE,*/ + NULL, /*PACKET_SERVER_CONFIG_UPDATE,*/ }; // If this fails, check the array above with network_data.h @@ -1394,6 +1461,7 @@ static void NetworkAutoCleanCompanies() _network_company_states[c->index].password[0] = '\0'; IConsolePrintF(CC_DEFAULT, "Auto-removed protection from company #%d", c->index + 1); _network_company_states[c->index].months_empty = 0; + NetworkServerUpdateCompanyPassworded(c->index, false); } } else { /* It is not empty, reset the date */ @@ -1635,6 +1703,63 @@ void NetworkServerShowStatusToConsole() } } +/** + * Send Config Update + */ +void NetworkServerSendConfigUpdate() +{ + NetworkClientSocket *cs; + + FOR_ALL_CLIENT_SOCKETS(cs) { + SEND_COMMAND(PACKET_SERVER_CONFIG_UPDATE)(cs); + } +} + +void NetworkServerUpdateCompanyPassworded(CompanyID company_id, bool passworded) +{ + if (NetworkCompanyIsPassworded(company_id) == passworded) return; + + SB(_network_company_passworded, company_id, 1, !!passworded); + + NetworkClientSocket *cs; + FOR_ALL_CLIENT_SOCKETS(cs) { + SEND_COMMAND(PACKET_SERVER_COMPANY_UPDATE)(cs); + } +} + +/** + * Handle the tid-bits of moving a client from one company to another. + * @param client_id id of the client we want to move. + * @param company_id id of the company we want to move the client to. + * @return void + **/ +void NetworkServerDoMove(ClientID client_id, CompanyID company_id) +{ + /* Only allow non-dedicated servers and normal clients to be moved */ + if (client_id == CLIENT_ID_SERVER && _network_dedicated) return; + + NetworkClientInfo *ci = NetworkFindClientInfoFromClientID(client_id); + + /* No need to waste network resources if the client is in the company already! */ + if (ci->client_playas == company_id) return; + + ci->client_playas = company_id; + + if (client_id == CLIENT_ID_SERVER) { + SetLocalCompany(company_id); + } else { + SEND_COMMAND(PACKET_SERVER_MOVE)(NetworkFindClientStateFromClientID(client_id), client_id, company_id); + } + + /* announce the client's move */ + NetworkUpdateClientInfo(client_id); + + NetworkAction action = (company_id == COMPANY_SPECTATOR) ? NETWORK_ACTION_COMPANY_SPECTATOR : NETWORK_ACTION_COMPANY_JOIN; + NetworkServerSendChat(action, DESTTYPE_BROADCAST, 0, "", client_id, company_id + 1); + + CheckMinActiveClients(); +} + void NetworkServerSendRcon(ClientID client_id, ConsoleColour colour_code, const char *string) { SEND_COMMAND(PACKET_SERVER_RCON)(NetworkFindClientStateFromClientID(client_id), colour_code, string); diff --git a/src/network/network_server.h b/src/network/network_server.h index 593579aaa..4950b74d5 100644 --- a/src/network/network_server.h +++ b/src/network/network_server.h @@ -13,6 +13,7 @@ DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_ERROR)(NetworkClientSocket *cs, Netw DEF_SERVER_SEND_COMMAND(PACKET_SERVER_SHUTDOWN); DEF_SERVER_SEND_COMMAND(PACKET_SERVER_NEWGAME); DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_RCON)(NetworkClientSocket *cs, uint16 color, const char *command); +DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_MOVE)(NetworkClientSocket *cs, uint16 client_id, CompanyID company_id); bool NetworkServer_ReadPackets(NetworkClientSocket *cs); void NetworkServer_Tick(bool send_frame); diff --git a/src/network/network_type.h b/src/network/network_type.h index 9104e2f0f..f2208b8c7 100644 --- a/src/network/network_type.h +++ b/src/network/network_type.h @@ -80,6 +80,9 @@ enum NetworkAction { NETWORK_ACTION_CHAT_CLIENT, NETWORK_ACTION_GIVE_MONEY, NETWORK_ACTION_NAME_CHANGE, + NETWORK_ACTION_COMPANY_SPECTATOR, + NETWORK_ACTION_COMPANY_JOIN, + NETWORK_ACTION_COMPANY_NEW, }; /** Messages the server can give */ |