* The client has done a command and wants us to handle it * @param p the packet in which the command was sent */
| 1052 | * @param p the packet in which the command was sent |
| 1053 | */ |
| 1054 | NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMMAND(Packet &p) |
| 1055 | { |
| 1056 | /* The client was never joined.. so this is impossible, right? |
| 1057 | * Ignore the packet, give the client a warning, and close the connection */ |
| 1058 | if (this->status < STATUS_DONE_MAP || this->HasClientQuit()) { |
| 1059 | return this->SendError(NETWORK_ERROR_NOT_EXPECTED); |
| 1060 | } |
| 1061 | |
| 1062 | if (this->incoming_queue.size() >= _settings_client.network.max_commands_in_queue) { |
| 1063 | return this->SendError(NETWORK_ERROR_TOO_MANY_COMMANDS); |
| 1064 | } |
| 1065 | |
| 1066 | Debug(net, 9, "client[{}] Receive_CLIENT_COMMAND()", this->client_id); |
| 1067 | |
| 1068 | CommandPacket cp; |
| 1069 | auto err = this->ReceiveCommand(p, cp); |
| 1070 | |
| 1071 | if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CLIENT_QUIT; |
| 1072 | |
| 1073 | NetworkClientInfo *ci = this->GetInfo(); |
| 1074 | |
| 1075 | if (err.has_value()) { |
| 1076 | IConsolePrint(CC_WARNING, "Dropping client #{} (IP: {}) due to {}.", ci->client_id, this->GetClientIP(), *err); |
| 1077 | return this->SendError(NETWORK_ERROR_NOT_EXPECTED); |
| 1078 | } |
| 1079 | |
| 1080 | if (GetCommandFlags(cp.cmd).Test(CommandFlag::Server) && ci->client_id != CLIENT_ID_SERVER) { |
| 1081 | IConsolePrint(CC_WARNING, "Kicking client #{} (IP: {}) due to calling a server only command {}.", ci->client_id, this->GetClientIP(), cp.cmd); |
| 1082 | return this->SendError(NETWORK_ERROR_KICKED); |
| 1083 | } |
| 1084 | |
| 1085 | if (!GetCommandFlags(cp.cmd).Test(CommandFlag::Spectator) && !Company::IsValidID(cp.company) && ci->client_id != CLIENT_ID_SERVER) { |
| 1086 | IConsolePrint(CC_WARNING, "Kicking client #{} (IP: {}) due to calling a non-spectator command {}.", ci->client_id, this->GetClientIP(), cp.cmd); |
| 1087 | return this->SendError(NETWORK_ERROR_KICKED); |
| 1088 | } |
| 1089 | |
| 1090 | /** |
| 1091 | * Only CMD_COMPANY_CTRL is always allowed, for the rest, playas needs |
| 1092 | * to match the company in the packet. If it doesn't, the client has done |
| 1093 | * something pretty naughty (or a bug), and will be kicked |
| 1094 | */ |
| 1095 | CompanyCtrlAction cca = cp.cmd == CMD_COMPANY_CTRL ? std::get<0>(EndianBufferReader::ToValue<CommandTraits<CMD_COMPANY_CTRL>::Args>(cp.data)) : CCA_NEW; |
| 1096 | if (!(cp.cmd == CMD_COMPANY_CTRL && cca == CCA_NEW && ci->client_playas == COMPANY_NEW_COMPANY) && ci->client_playas != cp.company) { |
| 1097 | IConsolePrint(CC_WARNING, "Kicking client #{} (IP: {}) due to calling a command as another company {}.", |
| 1098 | ci->client_playas + 1, this->GetClientIP(), cp.company + 1); |
| 1099 | return this->SendError(NETWORK_ERROR_COMPANY_MISMATCH); |
| 1100 | } |
| 1101 | |
| 1102 | if (cp.cmd == CMD_COMPANY_CTRL) { |
| 1103 | if (cca != CCA_NEW || cp.company != COMPANY_SPECTATOR) { |
| 1104 | return this->SendError(NETWORK_ERROR_CHEATER); |
| 1105 | } |
| 1106 | |
| 1107 | /* 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! */ |
| 1108 | if (Company::GetNumItems() >= _settings_client.network.max_companies) { |
| 1109 | NetworkServerSendChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_CLIENT, ci->client_id, "cannot create new company, server full", CLIENT_ID_SERVER); |
| 1110 | return NETWORK_RECV_STATUS_OKAY; |
| 1111 | } |
nothing calls this directly
no test coverage detected