diff --git a/src/lang/english.txt b/src/lang/english.txt --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2412,6 +2412,7 @@ STR_NETWORK_CHAT_TO_COMPANY STR_NETWORK_CHAT_CLIENT :[Private] {RAW_STRING}: {WHITE}{RAW_STRING} STR_NETWORK_CHAT_TO_CLIENT :[Private] To {RAW_STRING}: {WHITE}{RAW_STRING} STR_NETWORK_CHAT_ALL :[All] {RAW_STRING}: {WHITE}{RAW_STRING} +STR_NETWORK_CHAT_EXTERNAL :[{3:RAW_STRING}] {0:RAW_STRING}: {WHITE}{1:RAW_STRING} STR_NETWORK_CHAT_OSKTITLE :{BLACK}Enter text for network chat # Network messages diff --git a/src/network/core/tcp_admin.cpp b/src/network/core/tcp_admin.cpp --- a/src/network/core/tcp_admin.cpp +++ b/src/network/core/tcp_admin.cpp @@ -53,6 +53,7 @@ NetworkRecvStatus NetworkAdminSocketHand case ADMIN_PACKET_ADMIN_UPDATE_FREQUENCY: return this->Receive_ADMIN_UPDATE_FREQUENCY(p); case ADMIN_PACKET_ADMIN_POLL: return this->Receive_ADMIN_POLL(p); case ADMIN_PACKET_ADMIN_CHAT: return this->Receive_ADMIN_CHAT(p); + case ADMIN_PACKET_ADMIN_EXTERNAL_CHAT: return this->Receive_ADMIN_EXTERNAL_CHAT(p); case ADMIN_PACKET_ADMIN_RCON: return this->Receive_ADMIN_RCON(p); case ADMIN_PACKET_ADMIN_GAMESCRIPT: return this->Receive_ADMIN_GAMESCRIPT(p); case ADMIN_PACKET_ADMIN_PING: return this->Receive_ADMIN_PING(p); @@ -132,6 +133,7 @@ NetworkRecvStatus NetworkAdminSocketHand NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_UPDATE_FREQUENCY(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_UPDATE_FREQUENCY); } NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_POLL(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_POLL); } NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_CHAT(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_CHAT); } +NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_EXTERNAL_CHAT(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_EXTERNAL_CHAT); } NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_RCON(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_RCON); } NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_GAMESCRIPT(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_GAMESCRIPT); } NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_PING(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_PING); } diff --git a/src/network/core/tcp_admin.h b/src/network/core/tcp_admin.h --- a/src/network/core/tcp_admin.h +++ b/src/network/core/tcp_admin.h @@ -30,6 +30,7 @@ enum PacketAdminType { ADMIN_PACKET_ADMIN_RCON, ///< The admin sends a remote console command. ADMIN_PACKET_ADMIN_GAMESCRIPT, ///< The admin sends a JSON string for the GameScript. ADMIN_PACKET_ADMIN_PING, ///< The admin sends a ping to the server, expecting a ping-reply (PONG) packet. + ADMIN_PACKET_ADMIN_EXTERNAL_CHAT, ///< The admin sends a chat message from external source. 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. @@ -164,6 +165,17 @@ protected: virtual NetworkRecvStatus Receive_ADMIN_CHAT(Packet *p); /** + * Send chat from the external source: + * string Name of the source this message came from. + * uint16 TextColour to use for the message. + * string Name of the user who sent the messsage. + * string Message. + * @param p The packet that was just received. + * @return The state the network should have. + */ + virtual NetworkRecvStatus Receive_ADMIN_EXTERNAL_CHAT(Packet *p); + + /** * Execute a command on the servers console: * string Command to be executed. * @param p The packet that was just received. diff --git a/src/network/core/tcp_game.cpp b/src/network/core/tcp_game.cpp --- a/src/network/core/tcp_game.cpp +++ b/src/network/core/tcp_game.cpp @@ -94,6 +94,7 @@ NetworkRecvStatus NetworkGameSocketHandl case PACKET_SERVER_COMMAND: return this->Receive_SERVER_COMMAND(p); case PACKET_CLIENT_CHAT: return this->Receive_CLIENT_CHAT(p); case PACKET_SERVER_CHAT: return this->Receive_SERVER_CHAT(p); + case PACKET_SERVER_EXTERNAL_CHAT: return this->Receive_SERVER_EXTERNAL_CHAT(p); case PACKET_CLIENT_SET_PASSWORD: return this->Receive_CLIENT_SET_PASSWORD(p); case PACKET_CLIENT_SET_NAME: return this->Receive_CLIENT_SET_NAME(p); case PACKET_CLIENT_QUIT: return this->Receive_CLIENT_QUIT(p); @@ -180,6 +181,7 @@ NetworkRecvStatus NetworkGameSocketHandl NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_COMMAND(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_COMMAND); } NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_CHAT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_CHAT); } NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_CHAT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_CHAT); } +NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_EXTERNAL_CHAT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_EXTERNAL_CHAT); } NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_SET_PASSWORD(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_SET_PASSWORD); } NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_SET_NAME(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_SET_NAME); } NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_QUIT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_QUIT); } diff --git a/src/network/core/tcp_game.h b/src/network/core/tcp_game.h --- a/src/network/core/tcp_game.h +++ b/src/network/core/tcp_game.h @@ -99,6 +99,7 @@ enum PacketGameType { /* Human communication! */ PACKET_CLIENT_CHAT, ///< Client said something that should be distributed. PACKET_SERVER_CHAT, ///< Server distributing the message of a client (or itself). + PACKET_SERVER_EXTERNAL_CHAT, ///< Server distributing the message from external source. /* Remote console. */ PACKET_CLIENT_RCON, ///< Client asks the server to execute some command. @@ -379,6 +380,16 @@ protected: virtual NetworkRecvStatus Receive_SERVER_CHAT(Packet *p); /** + * Sends a chat-packet for external source to the client: + * string Name of the source this message came from. + * uint16 TextColour to use for the message. + * string Name of the user who sent the messsage. + * string Message (max NETWORK_CHAT_LENGTH). + * @param p The packet that was just received. + */ + virtual NetworkRecvStatus Receive_SERVER_EXTERNAL_CHAT(Packet *p); + + /** * Set the password for the clients current company: * string The password. * @param p The packet that was just received. diff --git a/src/network/network.cpp b/src/network/network.cpp --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -217,7 +217,7 @@ bool NetworkCompanyIsPassworded(CompanyI /* 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 NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send, const std::string &name, const std::string &str, int64 data) +void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send, const std::string &name, const std::string &str, int64 data, const std::string &data_str) { StringID strid; switch (action) { @@ -248,6 +248,7 @@ void NetworkTextMessage(NetworkAction ac case NETWORK_ACTION_CHAT_COMPANY: strid = self_send ? STR_NETWORK_CHAT_TO_COMPANY : STR_NETWORK_CHAT_COMPANY; break; case NETWORK_ACTION_CHAT_CLIENT: strid = self_send ? STR_NETWORK_CHAT_TO_CLIENT : STR_NETWORK_CHAT_CLIENT; break; case NETWORK_ACTION_KICKED: strid = STR_NETWORK_MESSAGE_KICKED; break; + case NETWORK_ACTION_EXTERNAL_CHAT: strid = STR_NETWORK_CHAT_EXTERNAL; break; default: strid = STR_NETWORK_CHAT_ALL; break; } @@ -255,6 +256,7 @@ void NetworkTextMessage(NetworkAction ac SetDParamStr(0, name); SetDParamStr(1, str); SetDParam(2, data); + SetDParamStr(3, data_str); /* All of these strings start with "***". These characters are interpreted as both left-to-right and * right-to-left characters depending on the context. As the next text might be an user's name, the @@ -265,7 +267,7 @@ void NetworkTextMessage(NetworkAction ac Debug(desync, 1, "msg: {:08x}; {:02x}; {}", _date, _date_fract, message); IConsolePrint(colour, message); - NetworkAddChatMessage((TextColour)colour, _settings_client.gui.network_chat_timeout, message); + NetworkAddChatMessage(colour, _settings_client.gui.network_chat_timeout, message); } /* Calculate the frame-lag of a client */ diff --git a/src/network/network_admin.cpp b/src/network/network_admin.cpp --- a/src/network/network_admin.cpp +++ b/src/network/network_admin.cpp @@ -790,6 +790,25 @@ NetworkRecvStatus ServerNetworkAdminSock return NETWORK_RECV_STATUS_OKAY; } +NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_EXTERNAL_CHAT(Packet *p) +{ + if (this->status == ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED); + + std::string source = p->Recv_string(NETWORK_CHAT_LENGTH); + TextColour colour = (TextColour)p->Recv_uint16(); + std::string user = p->Recv_string(NETWORK_CHAT_LENGTH); + std::string msg = p->Recv_string(NETWORK_CHAT_LENGTH); + + if (!IsValidConsoleColour(colour)) { + Debug(net, 1, "[admin] Not supported chat colour {} ({}, {}, {}) from '{}' ({}).", (uint16)colour, source, user, msg, this->admin_name, this->admin_version); + return this->SendError(NETWORK_ERROR_ILLEGAL_PACKET); + } + + NetworkServerSendExternalChat(source, colour, user, msg); + + return NETWORK_RECV_STATUS_OKAY; +} + /* * Useful wrapper functions */ diff --git a/src/network/network_admin.h b/src/network/network_admin.h --- a/src/network/network_admin.h +++ b/src/network/network_admin.h @@ -29,6 +29,7 @@ protected: NetworkRecvStatus Receive_ADMIN_UPDATE_FREQUENCY(Packet *p) override; NetworkRecvStatus Receive_ADMIN_POLL(Packet *p) override; NetworkRecvStatus Receive_ADMIN_CHAT(Packet *p) override; + NetworkRecvStatus Receive_ADMIN_EXTERNAL_CHAT(Packet *p) override; NetworkRecvStatus Receive_ADMIN_RCON(Packet *p) override; NetworkRecvStatus Receive_ADMIN_GAMESCRIPT(Packet *p) override; NetworkRecvStatus Receive_ADMIN_PING(Packet *p) override; diff --git a/src/network/network_chat_gui.cpp b/src/network/network_chat_gui.cpp --- a/src/network/network_chat_gui.cpp +++ b/src/network/network_chat_gui.cpp @@ -95,7 +95,7 @@ void CDECL NetworkAddChatMessage(TextCol ChatMessage *cmsg = &_chatmsg_list.emplace_front(); cmsg->message = message; - cmsg->colour = (colour & TC_IS_PALETTE_COLOUR) ? colour : TC_WHITE; + cmsg->colour = colour; cmsg->remove_time = std::chrono::steady_clock::now() + std::chrono::seconds(duration); _chatmessage_dirty_time = std::chrono::steady_clock::now(); diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -963,6 +963,22 @@ NetworkRecvStatus ClientNetworkGameSocke return NETWORK_RECV_STATUS_OKAY; } +NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_EXTERNAL_CHAT(Packet *p) +{ + if (this->status != STATUS_ACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET; + + std::string source = p->Recv_string(NETWORK_CHAT_LENGTH); + TextColour colour = (TextColour)p->Recv_uint16(); + std::string user = p->Recv_string(NETWORK_CHAT_LENGTH); + std::string msg = p->Recv_string(NETWORK_CHAT_LENGTH); + + if (!IsValidConsoleColour(colour)) return NETWORK_RECV_STATUS_MALFORMED_PACKET; + + NetworkTextMessage(NETWORK_ACTION_EXTERNAL_CHAT, colour, false, user, msg, 0, source); + + return NETWORK_RECV_STATUS_OKAY; +} + NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR_QUIT(Packet *p) { if (this->status < STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET; diff --git a/src/network/network_client.h b/src/network/network_client.h --- a/src/network/network_client.h +++ b/src/network/network_client.h @@ -57,6 +57,7 @@ protected: NetworkRecvStatus Receive_SERVER_SYNC(Packet *p) override; NetworkRecvStatus Receive_SERVER_COMMAND(Packet *p) override; NetworkRecvStatus Receive_SERVER_CHAT(Packet *p) override; + NetworkRecvStatus Receive_SERVER_EXTERNAL_CHAT(Packet *p) override; NetworkRecvStatus Receive_SERVER_QUIT(Packet *p) override; NetworkRecvStatus Receive_SERVER_ERROR_QUIT(Packet *p) override; NetworkRecvStatus Receive_SERVER_SHUTDOWN(Packet *p) override; diff --git a/src/network/network_func.h b/src/network/network_func.h --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -78,6 +78,7 @@ bool NetworkServerChangeClientName(Clien void NetworkServerDoMove(ClientID client_id, CompanyID company_id); void NetworkServerSendRcon(ClientID client_id, TextColour colour_code, const std::string &string); void NetworkServerSendChat(NetworkAction action, DestType type, int dest, const std::string &msg, ClientID from_id, int64 data = 0, bool from_admin = false); +void NetworkServerSendExternalChat(const std::string &source, TextColour colour, const std::string &user, const std::string &msg); void NetworkServerKickClient(ClientID client_id, const std::string &reason); uint NetworkServerKickOrBanIP(ClientID client_id, bool ban, const std::string &reason); diff --git a/src/network/network_internal.h b/src/network/network_internal.h --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -119,7 +119,7 @@ void NetworkFreeLocalCommandQueue(); void NetworkSyncCommandQueue(NetworkClientSocket *cs); void ShowNetworkError(StringID error_string); -void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send, const std::string &name, const std::string &str = "", int64 data = 0); +void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send, const std::string &name, const std::string &str = "", int64 data = 0, const std::string &data_str = ""); uint NetworkCalculateLag(const NetworkClientSocket *cs); StringID GetNetworkErrorMsg(NetworkErrorCode err); bool NetworkMakeClientNameUnique(std::string &new_name); diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -671,6 +671,28 @@ NetworkRecvStatus ServerNetworkGameSocke } /** + * Send a chat message from external source. + * @param source Name of the source this message came from. + * @param colour TextColour to use for the message. + * @param user Name of the user who sent the messsage. + * @param msg The actual message. + */ +NetworkRecvStatus ServerNetworkGameSocketHandler::SendExternalChat(const std::string &source, TextColour colour, const std::string &user, const std::string &msg) +{ + if (this->status < STATUS_PRE_ACTIVE) return NETWORK_RECV_STATUS_OKAY; + + Packet *p = new Packet(PACKET_SERVER_EXTERNAL_CHAT); + + p->Send_string(source); + p->Send_uint16(colour); + p->Send_string(user); + p->Send_string(msg); + + this->SendPacket(p); + return NETWORK_RECV_STATUS_OKAY; +} + +/** * Tell the client another client quit with an error. * @param client_id The client that quit. * @param errorno The reason the client quit. @@ -1262,12 +1284,27 @@ void NetworkServerSendChat(NetworkAction ci = NetworkClientInfo::GetByClientID(from_id); if (ci != nullptr) { - NetworkTextMessage(action, GetDrawStringCompanyColour(ci->client_playas), false, ci->client_name, msg, data); + NetworkTextMessage(action, GetDrawStringCompanyColour(ci->client_playas), false, ci->client_name, msg, data, ""); } break; } } +/** + * Send a chat message from external source. + * @param source Name of the source this message came from. + * @param colour TextColour to use for the message. + * @param user Name of the user who sent the messsage. + * @param msg The actual message. + */ +void NetworkServerSendExternalChat(const std::string &source, TextColour colour, const std::string &user, const std::string &msg) +{ + for (NetworkClientSocket *cs : NetworkClientSocket::Iterate()) { + cs->SendExternalChat(source, colour, user, msg); + } + NetworkTextMessage(NETWORK_ACTION_EXTERNAL_CHAT, colour, false, user, msg, 0, source); +} + NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_CHAT(Packet *p) { if (this->status < STATUS_PRE_ACTIVE) { diff --git a/src/network/network_server.h b/src/network/network_server.h --- a/src/network/network_server.h +++ b/src/network/network_server.h @@ -93,6 +93,7 @@ public: NetworkRecvStatus SendClientInfo(NetworkClientInfo *ci); NetworkRecvStatus SendError(NetworkErrorCode error, const std::string &reason = {}); NetworkRecvStatus SendChat(NetworkAction action, ClientID client_id, bool self_send, const std::string &msg, int64 data); + NetworkRecvStatus SendExternalChat(const std::string &source, TextColour colour, const std::string &user, const std::string &msg); NetworkRecvStatus SendJoin(ClientID client_id); NetworkRecvStatus SendFrame(); NetworkRecvStatus SendSync(); diff --git a/src/network/network_type.h b/src/network/network_type.h --- a/src/network/network_type.h +++ b/src/network/network_type.h @@ -109,6 +109,7 @@ enum NetworkAction { NETWORK_ACTION_COMPANY_JOIN, NETWORK_ACTION_COMPANY_NEW, NETWORK_ACTION_KICKED, + NETWORK_ACTION_EXTERNAL_CHAT, }; /**