diff --git a/src/network/core/tcp_admin.cpp b/src/network/core/tcp_admin.cpp index 0d2d09fddc..b2da7475fe 100644 --- a/src/network/core/tcp_admin.cpp +++ b/src/network/core/tcp_admin.cpp @@ -54,6 +54,7 @@ NetworkRecvStatus NetworkAdminSocketHandler::HandlePacket(Packet *p) ADMIN_COMMAND(ADMIN_PACKET_ADMIN_QUIT) ADMIN_COMMAND(ADMIN_PACKET_ADMIN_UPDATE_FREQUENCY) ADMIN_COMMAND(ADMIN_PACKET_ADMIN_POLL) + ADMIN_COMMAND(ADMIN_PACKET_ADMIN_CHAT) ADMIN_COMMAND(ADMIN_PACKET_SERVER_FULL) ADMIN_COMMAND(ADMIN_PACKET_SERVER_BANNED) @@ -75,6 +76,7 @@ NetworkRecvStatus NetworkAdminSocketHandler::HandlePacket(Packet *p) ADMIN_COMMAND(ADMIN_PACKET_SERVER_COMPANY_REMOVE) ADMIN_COMMAND(ADMIN_PACKET_SERVER_COMPANY_ECONOMY) ADMIN_COMMAND(ADMIN_PACKET_SERVER_COMPANY_STATS) + ADMIN_COMMAND(ADMIN_PACKET_SERVER_CHAT) default: if (this->HasClientQuit()) { @@ -124,6 +126,7 @@ DEFINE_UNAVAILABLE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_ADMIN_JOIN) DEFINE_UNAVAILABLE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_ADMIN_QUIT) DEFINE_UNAVAILABLE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_ADMIN_UPDATE_FREQUENCY) DEFINE_UNAVAILABLE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_ADMIN_POLL) +DEFINE_UNAVAILABLE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_ADMIN_CHAT) DEFINE_UNAVAILABLE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_SERVER_FULL) DEFINE_UNAVAILABLE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_SERVER_BANNED) @@ -145,5 +148,6 @@ DEFINE_UNAVAILABLE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_SERVER_COMPANY_UPDATE) DEFINE_UNAVAILABLE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_SERVER_COMPANY_REMOVE) DEFINE_UNAVAILABLE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_SERVER_COMPANY_ECONOMY) DEFINE_UNAVAILABLE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_SERVER_COMPANY_STATS) +DEFINE_UNAVAILABLE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_SERVER_CHAT) #endif /* ENABLE_NETWORK */ diff --git a/src/network/core/tcp_admin.h b/src/network/core/tcp_admin.h index 15f3ba0a7b..e73d0202e3 100644 --- a/src/network/core/tcp_admin.h +++ b/src/network/core/tcp_admin.h @@ -30,6 +30,7 @@ enum PacketAdminType { ADMIN_PACKET_ADMIN_QUIT, ///< The admin tells the server that it is quitting. ADMIN_PACKET_ADMIN_UPDATE_FREQUENCY, ///< The admin tells the server the update frequency of a particular piece of information. ADMIN_PACKET_ADMIN_POLL, ///< The admin explicitly polls for a piece of information. + ADMIN_PACKET_ADMIN_CHAT, ///< The admin sends a chat message to be distributed. ADMIN_PACKET_SERVER_FULL = 100, ///< The server tells the admin it cannot accept the admin. ADMIN_PACKET_SERVER_BANNED, ///< The server tells the admin it is banned. @@ -51,6 +52,7 @@ enum PacketAdminType { ADMIN_PACKET_SERVER_COMPANY_REMOVE, ///< The server tells the admin that a company was removed. ADMIN_PACKET_SERVER_COMPANY_ECONOMY, ///< The server gives the admin some economy related company information. ADMIN_PACKET_SERVER_COMPANY_STATS, ///< The server gives the admin some statistics about a company. + ADMIN_PACKET_SERVER_CHAT, ///< The server received a chat message and relays it. INVALID_ADMIN_PACKET = 0xFF, ///< An invalid marker for admin packets. }; @@ -69,6 +71,7 @@ enum AdminUpdateType { ADMIN_UPDATE_COMPANY_INFO, ///< Updates about the generic information of companies. ADMIN_UPDATE_COMPANY_ECONOMY, ///< Updates about the economy of companies. ADMIN_UPDATE_COMPANY_STATS, ///< Updates about the statistics of companies. + ADMIN_UPDATE_CHAT, ///< The admin would like to have chat messages. ADMIN_UPDATE_END ///< Must ALWAYS be on the end of this list!! (period) }; @@ -130,6 +133,15 @@ protected: */ DECLARE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_ADMIN_POLL); + /** + * Send chat as the server: + * uint8 Action such as NETWORK_ACTION_CHAT_CLIENT (see #NetworkAction). + * uint8 Destination type such as DESTTYPE_BROADCAST (see #DestType). + * uint32 ID of the destination such as company or client id. + * string Message. + */ + DECLARE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_ADMIN_CHAT); + /** * The server is full (connection gets closed). */ @@ -294,6 +306,16 @@ protected: */ DECLARE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_SERVER_COMPANY_STATS); + /** + * Send chat from the game into the admin network: + * uint8 Action such as NETWORK_ACTION_CHAT_CLIENT (see #NetworkAction). + * uint8 Destination type such as DESTTYPE_BROADCAST (see #DestType). + * uint32 ID of the client who sent this message. + * string Message. + * uint64 Money (only when it is a 'give money' action). + */ + DECLARE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_SERVER_CHAT); + NetworkRecvStatus HandlePacket(Packet *p); public: NetworkRecvStatus CloseConnection(bool error = true); diff --git a/src/network/network_admin.cpp b/src/network/network_admin.cpp index 7a86a4892a..565976c701 100644 --- a/src/network/network_admin.cpp +++ b/src/network/network_admin.cpp @@ -45,6 +45,7 @@ static const AdminUpdateFrequency _admin_update_type_frequencies[] = { ADMIN_FREQUENCY_POLL | ADMIN_FREQUENCY_AUTOMATIC, ///< ADMIN_UPDATE_COMPANY_INFO ADMIN_FREQUENCY_POLL | ADMIN_FREQUENCY_WEEKLY | ADMIN_FREQUENCY_MONTHLY | ADMIN_FREQUENCY_QUARTERLY | ADMIN_FREQUENCY_ANUALLY, ///< ADMIN_UPDATE_COMPANY_ECONOMY ADMIN_FREQUENCY_POLL | ADMIN_FREQUENCY_WEEKLY | ADMIN_FREQUENCY_MONTHLY | ADMIN_FREQUENCY_QUARTERLY | ADMIN_FREQUENCY_ANUALLY, ///< ADMIN_UPDATE_COMPANY_STATS + ADMIN_FREQUENCY_AUTOMATIC, ///< ADMIN_UPDATE_CHAT }; assert_compile(lengthof(_admin_update_type_frequencies) == ADMIN_UPDATE_END); @@ -384,6 +385,20 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyStats() return NETWORK_RECV_STATUS_OKAY; } +NetworkRecvStatus ServerNetworkAdminSocketHandler::SendChat(NetworkAction action, DestType desttype, ClientID client_id, const char *msg, int64 data) +{ + Packet *p = new Packet(ADMIN_PACKET_SERVER_CHAT); + + p->Send_uint8 (action); + p->Send_uint8 (desttype); + p->Send_uint32(client_id); + p->Send_string(msg); + p->Send_uint64(data); + + this->Send_Packet(p); + return NETWORK_RECV_STATUS_OKAY; +} + /*********** * Receiving functions ************/ @@ -496,6 +511,33 @@ DEF_ADMIN_RECEIVE_COMMAND(Server, ADMIN_PACKET_ADMIN_POLL) return NETWORK_RECV_STATUS_OKAY; } +DEF_ADMIN_RECEIVE_COMMAND(Server, ADMIN_PACKET_ADMIN_CHAT) +{ + if (this->status == ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED); + + NetworkAction action = (NetworkAction)p->Recv_uint8(); + DestType desttype = (DestType)p->Recv_uint8(); + int dest = p->Recv_uint32(); + + char msg[NETWORK_CHAT_LENGTH]; + p->Recv_string(msg, NETWORK_CHAT_LENGTH); + + switch (action) { + case NETWORK_ACTION_CHAT: + case NETWORK_ACTION_CHAT_CLIENT: + case NETWORK_ACTION_CHAT_COMPANY: + case NETWORK_ACTION_SERVER_MESSAGE: + NetworkServerSendChat(action, desttype, dest, msg, _network_own_client_id, 0, true); + break; + + default: + DEBUG(net, 3, "[admin] Invalid chat action %d from admin '%s' (%s).", action, this->admin_name, this->admin_version); + return this->SendError(NETWORK_ERROR_ILLEGAL_PACKET); + } + + return NETWORK_RECV_STATUS_OKAY; +} + /* * Useful wrapper functions */ @@ -613,6 +655,22 @@ void NetworkAdminCompanyRemove(CompanyID company_id, AdminCompanyRemoveReason bc } } + +/** + * Send chat to the admin network (if they did opt in for the respective update). + */ +void NetworkAdminChat(NetworkAction action, DestType desttype, ClientID client_id, const char *msg, int64 data, bool from_admin) +{ + if (from_admin) return; + + ServerNetworkAdminSocketHandler *as; + FOR_ALL_ADMIN_SOCKETS(as) { + if (as->update_frequency[ADMIN_UPDATE_CHAT] & ADMIN_FREQUENCY_AUTOMATIC) { + as->SendChat(action, desttype, client_id, msg, data); + } + } +} + /** * Send a Welcome packet to all connected admins */ diff --git a/src/network/network_admin.h b/src/network/network_admin.h index 7f4f67de1f..7174a191ad 100644 --- a/src/network/network_admin.h +++ b/src/network/network_admin.h @@ -29,6 +29,7 @@ protected: DECLARE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_ADMIN_QUIT); DECLARE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_ADMIN_UPDATE_FREQUENCY); DECLARE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_ADMIN_POLL); + DECLARE_ADMIN_RECEIVE_COMMAND(ADMIN_PACKET_ADMIN_CHAT); NetworkRecvStatus SendProtocol(); public: @@ -43,6 +44,7 @@ public: NetworkRecvStatus SendWelcome(); NetworkRecvStatus SendNewGame(); NetworkRecvStatus SendShutdown(); + NetworkRecvStatus SendDate(); NetworkRecvStatus SendClientJoin(ClientID client_id); NetworkRecvStatus SendClientInfo(const NetworkClientInfo *ci); @@ -56,6 +58,8 @@ public: NetworkRecvStatus SendCompanyEconomy(); NetworkRecvStatus SendCompanyStats(); + NetworkRecvStatus SendChat(NetworkAction action, DestType desttype, ClientID client_id, const char *msg, int64 data); + static void Send(); static void AcceptConnection(SOCKET s, const NetworkAddress &address); static bool AllowConnection(); @@ -81,6 +85,8 @@ void NetworkAdminClientError(ClientID client_id, NetworkErrorCode error_code); void NetworkAdminCompanyInfo(const Company *company, bool new_company); void NetworkAdminCompanyUpdate(const Company *company); void NetworkAdminCompanyRemove(CompanyID company_id, AdminCompanyRemoveReason bcrr); + +void NetworkAdminChat(NetworkAction action, DestType desttype, ClientID client_id, const char *msg, int64 data = 0, bool from_admin = false); void NetworkAdminUpdate(AdminUpdateFrequency freq); #endif /* ENABLE_NETWORK */ diff --git a/src/network/network_func.h b/src/network/network_func.h index 00db245e33..d6fc752d8a 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -73,7 +73,7 @@ const char *GetClientIP(NetworkClientInfo *ci); void NetworkServerDoMove(ClientID client_id, CompanyID company_id); void NetworkServerSendRcon(ClientID client_id, ConsoleColour colour_code, const char *string); -void NetworkServerSendChat(NetworkAction action, DestType type, int dest, const char *msg, ClientID from_id, int64 data = 0); +void NetworkServerSendChat(NetworkAction action, DestType type, int dest, const char *msg, ClientID from_id, int64 data = 0, bool from_admin = false); void NetworkServerKickClient(ClientID client_id); uint NetworkServerKickOrBanIP(const char *ip, bool ban); diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index c49c9ed885..16b626ce59 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -1159,7 +1159,7 @@ DEF_GAME_RECEIVE_COMMAND(Server, PACKET_CLIENT_ACK) -void NetworkServerSendChat(NetworkAction action, DestType desttype, int dest, const char *msg, ClientID from_id, int64 data) +void NetworkServerSendChat(NetworkAction action, DestType desttype, int dest, const char *msg, ClientID from_id, int64 data, bool from_admin) { NetworkClientSocket *cs; const NetworkClientInfo *ci, *ci_own, *ci_to; @@ -1172,6 +1172,10 @@ void NetworkServerSendChat(NetworkAction action, DestType desttype, int dest, co /* Display the text locally, and that is it */ if (ci != NULL) { NetworkTextMessage(action, (ConsoleColour)GetDrawStringCompanyColour(ci->client_playas), false, ci->client_name, msg, data); + + if (_settings_client.network.server_admin_chat) { + NetworkAdminChat(action, desttype, from_id, msg, data, from_admin); + } } } else { /* Else find the client to send the message to */ @@ -1215,6 +1219,11 @@ void NetworkServerSendChat(NetworkAction action, DestType desttype, int dest, co } } + /* if the server can read it, let the admin network read it, too. */ + if (_local_company == (CompanyID)dest && _settings_client.network.server_admin_chat) { + NetworkAdminChat(action, desttype, from_id, msg, data, from_admin); + } + ci = NetworkFindClientInfoFromClientID(from_id); ci_own = NetworkFindClientInfoFromClientID(CLIENT_ID_SERVER); if (ci != NULL && ci_own != NULL && ci_own->client_playas == dest) { @@ -1251,6 +1260,9 @@ void NetworkServerSendChat(NetworkAction action, DestType desttype, int dest, co FOR_ALL_CLIENT_SOCKETS(cs) { cs->SendChat(action, from_id, false, msg, data); } + + NetworkAdminChat(action, desttype, from_id, msg, data, from_admin); + ci = NetworkFindClientInfoFromClientID(from_id); if (ci != NULL) { NetworkTextMessage(action, (ConsoleColour)GetDrawStringCompanyColour(ci->client_playas), false, ci->client_name, msg, data); diff --git a/src/settings_type.h b/src/settings_type.h index c6544043d5..8e1b1bc9ed 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -134,6 +134,7 @@ struct NetworkSettings { bool pause_on_join; ///< pause the game when people join uint16 server_port; ///< port the server listens on uint16 server_admin_port; ///< port the server listens on for the admin network + bool server_admin_chat; ///< allow private chat for the server to be distributed to the admin network char server_name[NETWORK_NAME_LENGTH]; ///< name of the server char server_password[NETWORK_PASSWORD_LENGTH]; ///< password for joining this server char rcon_password[NETWORK_PASSWORD_LENGTH]; ///< password for rconsole (server side) diff --git a/src/table/settings.h b/src/table/settings.h index 0d263a85eb..e01bc68233 100644 --- a/src/table/settings.h +++ b/src/table/settings.h @@ -632,6 +632,7 @@ const SettingDesc _settings[] = { SDTC_BOOL(network.pause_on_join, S, NO, true, STR_NULL, NULL), SDTC_VAR(network.server_port, SLE_UINT16, S, NO,NETWORK_DEFAULT_PORT,0,65535,0,STR_NULL, NULL), SDTC_VAR(network.server_admin_port, SLE_UINT16, S, NO, NETWORK_ADMIN_PORT,0,65535,0,STR_NULL, NULL), + SDTC_BOOL(network.server_admin_chat, S, NO, true, STR_NULL, NULL), SDTC_BOOL(network.server_advertise, S, NO, false, STR_NULL, NULL), SDTC_VAR(network.lan_internet, SLE_UINT8, S, NO, 0, 0, 1, 0, STR_NULL, NULL), SDTC_STR(network.client_name, SLE_STRB, S, 0, NULL, STR_NULL, UpdateClientName),