Maintain a circular buffer of recent commands, add to crashlog.
[openttd-joker.git] / src / network / network_admin.cpp
blob360e85701ce5699ce672ba442c971e15f89f17f2
1 /* $Id: network_admin.cpp 25845 2013-10-12 17:03:15Z planetmaker $ */
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
10 /** @file network_admin.cpp Server part of the admin network protocol. */
12 #ifdef ENABLE_NETWORK
14 #include "../stdafx.h"
15 #include "../strings_func.h"
16 #include "../date_func.h"
17 #include "network_admin.h"
18 #include "network_base.h"
19 #include "network_server.h"
20 #include "../command_func.h"
21 #include "../company_base.h"
22 #include "../console_func.h"
23 #include "../core/pool_func.hpp"
24 #include "../map_func.h"
25 #include "../rev.h"
26 #include "../game/game.hpp"
28 #include "../safeguards.h"
31 /* This file handles all the admin network commands. */
33 /** Redirection of the (remote) console to the admin. */
34 AdminIndex _redirect_console_to_admin = INVALID_ADMIN_ID;
36 /** The amount of admins connected. */
37 byte _network_admins_connected = 0;
39 /** The pool with sockets/clients. */
40 NetworkAdminSocketPool _networkadminsocket_pool("NetworkAdminSocket");
41 INSTANTIATE_POOL_METHODS(NetworkAdminSocket)
43 /** The timeout for authorisation of the client. */
44 static const int ADMIN_AUTHORISATION_TIMEOUT = 10000;
47 /** Frequencies, which may be registered for a certain update type. */
48 static const AdminUpdateFrequency _admin_update_type_frequencies[] = {
49 ADMIN_FREQUENCY_POLL | ADMIN_FREQUENCY_DAILY | ADMIN_FREQUENCY_WEEKLY | ADMIN_FREQUENCY_MONTHLY | ADMIN_FREQUENCY_QUARTERLY | ADMIN_FREQUENCY_ANUALLY, ///< ADMIN_UPDATE_DATE
50 ADMIN_FREQUENCY_POLL | ADMIN_FREQUENCY_AUTOMATIC, ///< ADMIN_UPDATE_CLIENT_INFO
51 ADMIN_FREQUENCY_POLL | ADMIN_FREQUENCY_AUTOMATIC, ///< ADMIN_UPDATE_COMPANY_INFO
52 ADMIN_FREQUENCY_POLL | ADMIN_FREQUENCY_WEEKLY | ADMIN_FREQUENCY_MONTHLY | ADMIN_FREQUENCY_QUARTERLY | ADMIN_FREQUENCY_ANUALLY, ///< ADMIN_UPDATE_COMPANY_ECONOMY
53 ADMIN_FREQUENCY_POLL | ADMIN_FREQUENCY_WEEKLY | ADMIN_FREQUENCY_MONTHLY | ADMIN_FREQUENCY_QUARTERLY | ADMIN_FREQUENCY_ANUALLY, ///< ADMIN_UPDATE_COMPANY_STATS
54 ADMIN_FREQUENCY_AUTOMATIC, ///< ADMIN_UPDATE_CHAT
55 ADMIN_FREQUENCY_AUTOMATIC, ///< ADMIN_UPDATE_CONSOLE
56 ADMIN_FREQUENCY_POLL, ///< ADMIN_UPDATE_CMD_NAMES
57 ADMIN_FREQUENCY_AUTOMATIC, ///< ADMIN_UPDATE_CMD_LOGGING
58 ADMIN_FREQUENCY_AUTOMATIC, ///< ADMIN_UPDATE_GAMESCRIPT
60 /** Sanity check. */
61 assert_compile(lengthof(_admin_update_type_frequencies) == ADMIN_UPDATE_END);
63 /**
64 * Create a new socket for the server side of the admin network.
65 * @param s The socket to connect with.
67 ServerNetworkAdminSocketHandler::ServerNetworkAdminSocketHandler(SOCKET s) : NetworkAdminSocketHandler(s)
69 _network_admins_connected++;
70 this->status = ADMIN_STATUS_INACTIVE;
71 this->realtime_connect = _realtime_tick;
74 /**
75 * Clear everything related to this admin.
77 ServerNetworkAdminSocketHandler::~ServerNetworkAdminSocketHandler()
79 _network_admins_connected--;
80 DEBUG(net, 1, "[admin] '%s' (%s) has disconnected", this->admin_name, this->admin_version);
81 if (_redirect_console_to_admin == this->index) _redirect_console_to_admin = INVALID_ADMIN_ID;
84 /**
85 * Whether a connection is allowed or not at this moment.
86 * @return Whether the connection is allowed.
88 /* static */ bool ServerNetworkAdminSocketHandler::AllowConnection()
90 bool accept = !StrEmpty(_settings_client.network.admin_password) && _network_admins_connected < MAX_ADMINS;
91 /* We can't go over the MAX_ADMINS limit here. However, if we accept
92 * the connection, there has to be space in the pool. */
93 assert_compile(NetworkAdminSocketPool::MAX_SIZE == MAX_ADMINS);
94 assert(!accept || ServerNetworkAdminSocketHandler::CanAllocateItem());
95 return accept;
98 /** Send the packets for the server sockets. */
99 /* static */ void ServerNetworkAdminSocketHandler::Send()
101 ServerNetworkAdminSocketHandler *as;
102 FOR_ALL_ADMIN_SOCKETS(as) {
103 if (as->status == ADMIN_STATUS_INACTIVE && as->realtime_connect + ADMIN_AUTHORISATION_TIMEOUT < _realtime_tick) {
104 DEBUG(net, 1, "[admin] Admin did not send its authorisation within %d seconds", ADMIN_AUTHORISATION_TIMEOUT / 1000);
105 as->CloseConnection(true);
106 continue;
108 if (as->writable) {
109 as->SendPackets();
115 * Handle the acception of a connection.
116 * @param s The socket of the new connection.
117 * @param address The address of the peer.
119 /* static */ void ServerNetworkAdminSocketHandler::AcceptConnection(SOCKET s, const NetworkAddress &address)
121 ServerNetworkAdminSocketHandler *as = new ServerNetworkAdminSocketHandler(s);
122 as->address = address; // Save the IP of the client
125 /***********
126 * Sending functions for admin network
127 ************/
130 * Send an error to the admin.
131 * @param error The error to send.
133 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendError(NetworkErrorCode error)
135 Packet *p = new Packet(ADMIN_PACKET_SERVER_ERROR);
137 p->Send_uint8(error);
138 this->SendPacket(p);
140 char str[100];
141 StringID strid = GetNetworkErrorMsg(error);
142 GetString(str, strid, lastof(str));
144 DEBUG(net, 1, "[admin] the admin '%s' (%s) made an error and has been disconnected. Reason: '%s'", this->admin_name, this->admin_version, str);
146 return this->CloseConnection(true);
149 /** Send the protocol version to the admin. */
150 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendProtocol()
152 Packet *p = new Packet(ADMIN_PACKET_SERVER_PROTOCOL);
154 /* announce the protocol version */
155 p->Send_uint8(NETWORK_GAME_ADMIN_VERSION);
157 for (int i = 0; i < ADMIN_UPDATE_END; i++) {
158 p->Send_bool (true);
159 p->Send_uint16(i);
160 p->Send_uint16(_admin_update_type_frequencies[i]);
163 p->Send_bool(false);
164 this->SendPacket(p);
166 return this->SendWelcome();
169 /** Send a welcome message to the admin. */
170 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendWelcome()
172 Packet *p = new Packet(ADMIN_PACKET_SERVER_WELCOME);
174 p->Send_string(_settings_client.network.server_name);
175 p->Send_string(_openttd_revision);
176 p->Send_bool (_network_dedicated);
178 p->Send_string(_network_game_info.map_name);
179 p->Send_uint32(_settings_game.game_creation.generation_seed);
180 p->Send_uint8 (_settings_game.game_creation.landscape);
181 p->Send_uint32(ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1));
182 p->Send_uint16(MapSizeX());
183 p->Send_uint16(MapSizeY());
185 this->SendPacket(p);
187 return NETWORK_RECV_STATUS_OKAY;
190 /** Tell the admin we started a new game. */
191 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendNewGame()
193 Packet *p = new Packet(ADMIN_PACKET_SERVER_NEWGAME);
194 this->SendPacket(p);
195 return NETWORK_RECV_STATUS_OKAY;
198 /** Tell the admin we're shutting down. */
199 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendShutdown()
201 Packet *p = new Packet(ADMIN_PACKET_SERVER_SHUTDOWN);
202 this->SendPacket(p);
203 return NETWORK_RECV_STATUS_OKAY;
206 /** Tell the admin the date. */
207 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendDate()
209 Packet *p = new Packet(ADMIN_PACKET_SERVER_DATE);
211 p->Send_uint32(_date);
212 this->SendPacket(p);
214 return NETWORK_RECV_STATUS_OKAY;
218 * Tell the admin that a client joined.
219 * @param client_id The client that joined.
221 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientJoin(ClientID client_id)
223 Packet *p = new Packet(ADMIN_PACKET_SERVER_CLIENT_JOIN);
225 p->Send_uint32(client_id);
226 this->SendPacket(p);
228 return NETWORK_RECV_STATUS_OKAY;
232 * Send an initial set of data from some client's information.
233 * @param cs The socket of the client.
234 * @param ci The information about the client.
236 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientInfo(const NetworkClientSocket *cs, const NetworkClientInfo *ci)
238 /* Only send data when we're a proper client, not just someone trying to query the server. */
239 if (ci == NULL) return NETWORK_RECV_STATUS_OKAY;
241 Packet *p = new Packet(ADMIN_PACKET_SERVER_CLIENT_INFO);
243 p->Send_uint32(ci->client_id);
244 p->Send_string(cs == NULL ? "" : const_cast<NetworkAddress &>(cs->client_address).GetHostname());
245 p->Send_string(ci->client_name);
246 p->Send_uint8 (ci->client_lang);
247 p->Send_uint32(ci->join_date);
248 p->Send_uint8 (ci->client_playas);
250 this->SendPacket(p);
252 return NETWORK_RECV_STATUS_OKAY;
257 * Send an update for some client's information.
258 * @param ci The information about a client.
260 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientUpdate(const NetworkClientInfo *ci)
262 Packet *p = new Packet(ADMIN_PACKET_SERVER_CLIENT_UPDATE);
264 p->Send_uint32(ci->client_id);
265 p->Send_string(ci->client_name);
266 p->Send_uint8 (ci->client_playas);
268 this->SendPacket(p);
270 return NETWORK_RECV_STATUS_OKAY;
274 * Tell the admin that a client quit.
275 * @param client_id The client that quit.
277 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientQuit(ClientID client_id)
279 Packet *p = new Packet(ADMIN_PACKET_SERVER_CLIENT_QUIT);
281 p->Send_uint32(client_id);
282 this->SendPacket(p);
284 return NETWORK_RECV_STATUS_OKAY;
288 * Tell the admin that a client made an error.
289 * @param client_id The client that made the error.
290 * @param error The error that was made.
292 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientError(ClientID client_id, NetworkErrorCode error)
294 Packet *p = new Packet(ADMIN_PACKET_SERVER_CLIENT_ERROR);
296 p->Send_uint32(client_id);
297 p->Send_uint8 (error);
298 this->SendPacket(p);
300 return NETWORK_RECV_STATUS_OKAY;
304 * Tell the admin that a new company was founded.
305 * @param company_id The company that was founded.
307 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyNew(CompanyID company_id)
309 Packet *p = new Packet(ADMIN_PACKET_SERVER_COMPANY_NEW);
310 p->Send_uint8(company_id);
312 this->SendPacket(p);
314 return NETWORK_RECV_STATUS_OKAY;
318 * Send the admin some information about a company.
319 * @param c The company to send the information about.
321 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyInfo(const Company *c)
323 char company_name[NETWORK_COMPANY_NAME_LENGTH];
324 char manager_name[NETWORK_COMPANY_NAME_LENGTH];
326 SetDParam(0, c->index);
327 GetString(company_name, STR_COMPANY_NAME, lastof(company_name));
329 SetDParam(0, c->index);
330 GetString(manager_name, STR_PRESIDENT_NAME, lastof(manager_name));
332 Packet *p = new Packet(ADMIN_PACKET_SERVER_COMPANY_INFO);
334 p->Send_uint8 (c->index);
335 p->Send_string(company_name);
336 p->Send_string(manager_name);
337 p->Send_uint8 (c->colour);
338 p->Send_bool (NetworkCompanyIsPassworded(c->index));
339 p->Send_uint32(c->inaugurated_year);
340 p->Send_bool (c->is_ai);
341 p->Send_uint8 (CeilDiv(c->months_of_bankruptcy, 3)); // send as quarters_of_bankruptcy
343 for (size_t i = 0; i < lengthof(c->share_owners); i++) {
344 p->Send_uint8(c->share_owners[i]);
347 this->SendPacket(p);
349 return NETWORK_RECV_STATUS_OKAY;
354 * Send an update about a company.
355 * @param c The company to send the update of.
357 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyUpdate(const Company *c)
359 char company_name[NETWORK_COMPANY_NAME_LENGTH];
360 char manager_name[NETWORK_COMPANY_NAME_LENGTH];
362 SetDParam(0, c->index);
363 GetString(company_name, STR_COMPANY_NAME, lastof(company_name));
365 SetDParam(0, c->index);
366 GetString(manager_name, STR_PRESIDENT_NAME, lastof(manager_name));
368 Packet *p = new Packet(ADMIN_PACKET_SERVER_COMPANY_UPDATE);
370 p->Send_uint8 (c->index);
371 p->Send_string(company_name);
372 p->Send_string(manager_name);
373 p->Send_uint8 (c->colour);
374 p->Send_bool (NetworkCompanyIsPassworded(c->index));
375 p->Send_uint8 (CeilDiv(c->months_of_bankruptcy, 3)); // send as quarters_of_bankruptcy
377 for (size_t i = 0; i < lengthof(c->share_owners); i++) {
378 p->Send_uint8(c->share_owners[i]);
381 this->SendPacket(p);
383 return NETWORK_RECV_STATUS_OKAY;
387 * Tell the admin that a company got removed.
388 * @param company_id The company that got removed.
389 * @param acrr The reason for removal, e.g. bankruptcy or merger.
391 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyRemove(CompanyID company_id, AdminCompanyRemoveReason acrr)
393 Packet *p = new Packet(ADMIN_PACKET_SERVER_COMPANY_REMOVE);
395 p->Send_uint8(company_id);
396 p->Send_uint8(acrr);
398 this->SendPacket(p);
400 return NETWORK_RECV_STATUS_OKAY;
403 /** Send economic information of all companies. */
404 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyEconomy()
406 const Company *company;
407 FOR_ALL_COMPANIES(company) {
408 /* Get the income. */
409 Money income = 0;
410 for (uint i = 0; i < lengthof(company->yearly_expenses[0]); i++) {
411 income -= company->yearly_expenses[0][i];
414 Packet *p = new Packet(ADMIN_PACKET_SERVER_COMPANY_ECONOMY);
416 p->Send_uint8(company->index);
418 /* Current information. */
419 p->Send_uint64(company->money);
420 p->Send_uint64(company->current_loan);
421 p->Send_uint64(income);
422 p->Send_uint16(min(UINT16_MAX, company->cur_economy.delivered_cargo.GetSum<OverflowSafeInt64>()));
424 /* Send stats for the last 2 quarters. */
425 for (uint i = 0; i < 2; i++) {
426 p->Send_uint64(company->old_economy[i].company_value);
427 p->Send_uint16(company->old_economy[i].performance_history);
428 p->Send_uint16(min(UINT16_MAX, company->old_economy[i].delivered_cargo.GetSum<OverflowSafeInt64>()));
431 this->SendPacket(p);
435 return NETWORK_RECV_STATUS_OKAY;
438 /** Send statistics about the companies. */
439 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyStats()
441 /* Fetch the latest version of the stats. */
442 NetworkCompanyStats company_stats[MAX_COMPANIES];
443 NetworkPopulateCompanyStats(company_stats);
445 const Company *company;
447 /* Go through all the companies. */
448 FOR_ALL_COMPANIES(company) {
449 Packet *p = new Packet(ADMIN_PACKET_SERVER_COMPANY_STATS);
451 /* Send the information. */
452 p->Send_uint8(company->index);
454 for (uint i = 0; i < NETWORK_VEH_END; i++) {
455 p->Send_uint16(company_stats[company->index].num_vehicle[i]);
458 for (uint i = 0; i < NETWORK_VEH_END; i++) {
459 p->Send_uint16(company_stats[company->index].num_station[i]);
462 this->SendPacket(p);
465 return NETWORK_RECV_STATUS_OKAY;
469 * Send a chat message.
470 * @param action The action associated with the message.
471 * @param desttype The destination type.
472 * @param client_id The origin of the chat message.
473 * @param msg The actual message.
474 * @param data Arbitrary extra data.
476 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendChat(NetworkAction action, DestType desttype, ClientID client_id, const char *msg, int64 data)
478 Packet *p = new Packet(ADMIN_PACKET_SERVER_CHAT);
480 p->Send_uint8 (action);
481 p->Send_uint8 (desttype);
482 p->Send_uint32(client_id);
483 p->Send_string(msg);
484 p->Send_uint64(data);
486 this->SendPacket(p);
487 return NETWORK_RECV_STATUS_OKAY;
491 * Send a notification indicating the rcon command has completed.
492 * @param command The original command sent.
494 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendRconEnd(const char *command)
496 Packet *p = new Packet(ADMIN_PACKET_SERVER_RCON_END);
498 p->Send_string(command);
499 this->SendPacket(p);
501 return NETWORK_RECV_STATUS_OKAY;
505 * Send the reply of an rcon command.
506 * @param colour The colour of the text.
507 * @param result The result of the command.
509 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendRcon(uint16 colour, const char *result)
511 Packet *p = new Packet(ADMIN_PACKET_SERVER_RCON);
513 p->Send_uint16(colour);
514 p->Send_string(result);
515 this->SendPacket(p);
517 return NETWORK_RECV_STATUS_OKAY;
520 NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_RCON(Packet *p)
522 if (this->status == ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
524 char command[NETWORK_RCONCOMMAND_LENGTH];
526 p->Recv_string(command, sizeof(command));
528 DEBUG(net, 2, "[admin] Rcon command from '%s' (%s): '%s'", this->admin_name, this->admin_version, command);
530 _redirect_console_to_admin = this->index;
531 IConsoleCmdExec(command);
532 _redirect_console_to_admin = INVALID_ADMIN_ID;
533 return this->SendRconEnd(command);
536 NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_GAMESCRIPT(Packet *p)
538 if (this->status == ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
540 char json[NETWORK_GAMESCRIPT_JSON_LENGTH];
542 p->Recv_string(json, sizeof(json));
544 DEBUG(net, 2, "[admin] GameScript JSON from '%s' (%s): '%s'", this->admin_name, this->admin_version, json);
546 Game::NewEvent(new ScriptEventAdminPort(json));
547 return NETWORK_RECV_STATUS_OKAY;
550 NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_PING(Packet *p)
552 if (this->status == ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
554 uint32 d1 = p->Recv_uint32();
556 DEBUG(net, 2, "[admin] Ping from '%s' (%s): '%d'", this->admin_name, this->admin_version, d1);
558 return this->SendPong(d1);
562 * Send console output of other clients.
563 * @param origin The origin of the string.
564 * @param string The string that's put on the console.
566 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendConsole(const char *origin, const char *string)
568 /* If the length of both strings, plus the 2 '\0' terminations and 3 bytes of the packet
569 * are bigger than the MTU, just ignore the message. Better safe than sorry. It should
570 * never occur though as the longest strings are chat messages, which are still 30%
571 * smaller than SEND_MTU. */
572 if (strlen(origin) + strlen(string) + 2 + 3 >= SEND_MTU) return NETWORK_RECV_STATUS_OKAY;
574 Packet *p = new Packet(ADMIN_PACKET_SERVER_CONSOLE);
576 p->Send_string(origin);
577 p->Send_string(string);
578 this->SendPacket(p);
580 return NETWORK_RECV_STATUS_OKAY;
584 * Send GameScript JSON output.
585 * @param json The JSON string.
587 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendGameScript(const char *json)
589 /* At the moment we cannot transmit anything larger than MTU. So we limit
590 * the maximum amount of json data that can be sent. Account also for
591 * the trailing \0 of the string */
592 if (strlen(json) + 1 >= NETWORK_GAMESCRIPT_JSON_LENGTH) return NETWORK_RECV_STATUS_OKAY;
594 Packet *p = new Packet(ADMIN_PACKET_SERVER_GAMESCRIPT);
596 p->Send_string(json);
597 this->SendPacket(p);
599 return NETWORK_RECV_STATUS_OKAY;
602 /** Send ping-reply (pong) to admin **/
603 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendPong(uint32 d1)
605 Packet *p = new Packet(ADMIN_PACKET_SERVER_PONG);
607 p->Send_uint32(d1);
608 this->SendPacket(p);
610 return NETWORK_RECV_STATUS_OKAY;
613 /** Send the names of the commands. */
614 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCmdNames()
616 Packet *p = new Packet(ADMIN_PACKET_SERVER_CMD_NAMES);
618 for (uint i = 0; i < CMD_END; i++) {
619 const char *cmdname = GetCommandName(i);
621 /* Should SEND_MTU be exceeded, start a new packet
622 * (magic 5: 1 bool "more data" and one uint16 "command id", one
623 * byte for string '\0' termination and 1 bool "no more data" */
624 if (p->size + strlen(cmdname) + 5 >= SEND_MTU) {
625 p->Send_bool(false);
626 this->SendPacket(p);
628 p = new Packet(ADMIN_PACKET_SERVER_CMD_NAMES);
631 p->Send_bool(true);
632 p->Send_uint16(i);
633 p->Send_string(cmdname);
636 /* Marker to notify the end of the packet has been reached. */
637 p->Send_bool(false);
638 this->SendPacket(p);
640 return NETWORK_RECV_STATUS_OKAY;
644 * Send a command for logging purposes.
645 * @param client_id The client executing the command.
646 * @param cp The command that would be executed.
648 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCmdLogging(ClientID client_id, const CommandPacket *cp)
650 Packet *p = new Packet(ADMIN_PACKET_SERVER_CMD_LOGGING);
652 p->Send_uint32(client_id);
653 p->Send_uint8 (cp->company);
654 p->Send_uint16(cp->cmd & CMD_ID_MASK);
655 p->Send_uint32(cp->p1);
656 p->Send_uint32(cp->p2);
657 p->Send_uint32(cp->tile);
658 p->Send_string(cp->text);
659 p->Send_uint32(cp->frame);
661 this->SendPacket(p);
663 return NETWORK_RECV_STATUS_OKAY;
666 /***********
667 * Receiving functions
668 ************/
670 NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_JOIN(Packet *p)
672 if (this->status != ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
674 char password[NETWORK_PASSWORD_LENGTH];
675 p->Recv_string(password, sizeof(password));
677 if (StrEmpty(_settings_client.network.admin_password) ||
678 strcmp(password, _settings_client.network.admin_password) != 0) {
679 /* Password is invalid */
680 return this->SendError(NETWORK_ERROR_WRONG_PASSWORD);
683 p->Recv_string(this->admin_name, sizeof(this->admin_name));
684 p->Recv_string(this->admin_version, sizeof(this->admin_version));
686 if (StrEmpty(this->admin_name) || StrEmpty(this->admin_version)) {
687 /* no name or version supplied */
688 return this->SendError(NETWORK_ERROR_ILLEGAL_PACKET);
691 this->status = ADMIN_STATUS_ACTIVE;
693 DEBUG(net, 1, "[admin] '%s' (%s) has connected", this->admin_name, this->admin_version);
695 return this->SendProtocol();
698 NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_QUIT(Packet *p)
700 /* The admin is leaving nothing else to do */
701 return this->CloseConnection();
704 NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_UPDATE_FREQUENCY(Packet *p)
706 if (this->status == ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
708 AdminUpdateType type = (AdminUpdateType)p->Recv_uint16();
709 AdminUpdateFrequency freq = (AdminUpdateFrequency)p->Recv_uint16();
711 if (type >= ADMIN_UPDATE_END || (_admin_update_type_frequencies[type] & freq) != freq) {
712 /* The server does not know of this UpdateType. */
713 DEBUG(net, 3, "[admin] Not supported update frequency %d (%d) from '%s' (%s).", type, freq, this->admin_name, this->admin_version);
714 return this->SendError(NETWORK_ERROR_ILLEGAL_PACKET);
717 this->update_frequency[type] = freq;
719 return NETWORK_RECV_STATUS_OKAY;
722 NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_POLL(Packet *p)
724 if (this->status == ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
726 AdminUpdateType type = (AdminUpdateType)p->Recv_uint8();
727 uint32 d1 = p->Recv_uint32();
729 switch (type) {
730 case ADMIN_UPDATE_DATE:
731 /* The admin is requesting the current date. */
732 this->SendDate();
733 break;
735 case ADMIN_UPDATE_CLIENT_INFO:
736 /* The admin is requesting client info. */
737 const NetworkClientSocket *cs;
738 if (d1 == UINT32_MAX) {
739 this->SendClientInfo(NULL, NetworkClientInfo::GetByClientID(CLIENT_ID_SERVER));
740 FOR_ALL_CLIENT_SOCKETS(cs) {
741 this->SendClientInfo(cs, cs->GetInfo());
743 } else {
744 if (d1 == CLIENT_ID_SERVER) {
745 this->SendClientInfo(NULL, NetworkClientInfo::GetByClientID(CLIENT_ID_SERVER));
746 } else {
747 cs = NetworkClientSocket::GetByClientID((ClientID)d1);
748 if (cs != NULL) this->SendClientInfo(cs, cs->GetInfo());
751 break;
753 case ADMIN_UPDATE_COMPANY_INFO:
754 /* The admin is asking for company info. */
755 const Company *company;
756 if (d1 == UINT32_MAX) {
757 FOR_ALL_COMPANIES(company) {
758 this->SendCompanyInfo(company);
760 } else {
761 company = Company::GetIfValid(d1);
762 if (company != NULL) this->SendCompanyInfo(company);
764 break;
766 case ADMIN_UPDATE_COMPANY_ECONOMY:
767 /* The admin is requesting economy info. */
768 this->SendCompanyEconomy();
769 break;
771 case ADMIN_UPDATE_COMPANY_STATS:
772 /* the admin is requesting company stats. */
773 this->SendCompanyStats();
774 break;
776 case ADMIN_UPDATE_CMD_NAMES:
777 /* The admin is requesting the names of DoCommands. */
778 this->SendCmdNames();
779 break;
781 default:
782 /* An unsupported "poll" update type. */
783 DEBUG(net, 3, "[admin] Not supported poll %d (%d) from '%s' (%s).", type, d1, this->admin_name, this->admin_version);
784 return this->SendError(NETWORK_ERROR_ILLEGAL_PACKET);
787 return NETWORK_RECV_STATUS_OKAY;
790 NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_CHAT(Packet *p)
792 if (this->status == ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
794 NetworkAction action = (NetworkAction)p->Recv_uint8();
795 DestType desttype = (DestType)p->Recv_uint8();
796 int dest = p->Recv_uint32();
798 char msg[NETWORK_CHAT_LENGTH];
799 p->Recv_string(msg, NETWORK_CHAT_LENGTH);
801 switch (action) {
802 case NETWORK_ACTION_CHAT:
803 case NETWORK_ACTION_CHAT_CLIENT:
804 case NETWORK_ACTION_CHAT_COMPANY:
805 case NETWORK_ACTION_SERVER_MESSAGE:
806 NetworkServerSendChat(action, desttype, dest, msg, _network_own_client_id, 0, true);
807 break;
809 default:
810 DEBUG(net, 3, "[admin] Invalid chat action %d from admin '%s' (%s).", action, this->admin_name, this->admin_version);
811 return this->SendError(NETWORK_ERROR_ILLEGAL_PACKET);
814 return NETWORK_RECV_STATUS_OKAY;
818 * Useful wrapper functions
822 * Notify the admin network of a new client (if they did opt in for the respective update).
823 * @param cs the client info.
824 * @param new_client if this is a new client, send the respective packet too.
826 void NetworkAdminClientInfo(const NetworkClientSocket *cs, bool new_client)
828 ServerNetworkAdminSocketHandler *as;
829 FOR_ALL_ACTIVE_ADMIN_SOCKETS(as) {
830 if (as->update_frequency[ADMIN_UPDATE_CLIENT_INFO] & ADMIN_FREQUENCY_AUTOMATIC) {
831 as->SendClientInfo(cs, cs->GetInfo());
832 if (new_client) {
833 as->SendClientJoin(cs->client_id);
840 * Notify the admin network of a client update (if they did opt in for the respective update).
841 * @param ci the client info.
843 void NetworkAdminClientUpdate(const NetworkClientInfo *ci)
845 ServerNetworkAdminSocketHandler *as;
846 FOR_ALL_ACTIVE_ADMIN_SOCKETS(as) {
847 if (as->update_frequency[ADMIN_UPDATE_CLIENT_INFO] & ADMIN_FREQUENCY_AUTOMATIC) {
848 as->SendClientUpdate(ci);
854 * Notify the admin network that a client quit (if they have opt in for the respective update).
855 * @param client_id of the client that quit.
857 void NetworkAdminClientQuit(ClientID client_id)
859 ServerNetworkAdminSocketHandler *as;
860 FOR_ALL_ACTIVE_ADMIN_SOCKETS(as) {
861 if (as->update_frequency[ADMIN_UPDATE_CLIENT_INFO] & ADMIN_FREQUENCY_AUTOMATIC) {
862 as->SendClientQuit(client_id);
868 * Notify the admin network of a client error (if they have opt in for the respective update).
869 * @param client_id the client that made the error.
870 * @param error_code the error that was caused.
872 void NetworkAdminClientError(ClientID client_id, NetworkErrorCode error_code)
874 ServerNetworkAdminSocketHandler *as;
875 FOR_ALL_ACTIVE_ADMIN_SOCKETS(as) {
876 if (as->update_frequency[ADMIN_UPDATE_CLIENT_INFO] & ADMIN_FREQUENCY_AUTOMATIC) {
877 as->SendClientError(client_id, error_code);
883 * Notify the admin network of company details.
884 * @param company the company of which details will be sent into the admin network.
885 * @param new_company whether this is a new company or not.
887 void NetworkAdminCompanyInfo(const Company *company, bool new_company)
889 if (company == NULL) {
890 DEBUG(net, 1, "[admin] Empty company given for update");
891 return;
894 ServerNetworkAdminSocketHandler *as;
895 FOR_ALL_ACTIVE_ADMIN_SOCKETS(as) {
896 if (as->update_frequency[ADMIN_UPDATE_COMPANY_INFO] != ADMIN_FREQUENCY_AUTOMATIC) continue;
898 as->SendCompanyInfo(company);
899 if (new_company) {
900 as->SendCompanyNew(company->index);
906 * Notify the admin network of company updates.
907 * @param company company of which updates are going to be sent into the admin network.
909 void NetworkAdminCompanyUpdate(const Company *company)
911 if (company == NULL) return;
913 ServerNetworkAdminSocketHandler *as;
914 FOR_ALL_ACTIVE_ADMIN_SOCKETS(as) {
915 if (as->update_frequency[ADMIN_UPDATE_COMPANY_INFO] != ADMIN_FREQUENCY_AUTOMATIC) continue;
917 as->SendCompanyUpdate(company);
922 * Notify the admin network of a company to be removed (including the reason why).
923 * @param company_id ID of the company that got removed.
924 * @param bcrr the reason why the company got removed (e.g. bankruptcy).
926 void NetworkAdminCompanyRemove(CompanyID company_id, AdminCompanyRemoveReason bcrr)
928 ServerNetworkAdminSocketHandler *as;
929 FOR_ALL_ACTIVE_ADMIN_SOCKETS(as) {
930 as->SendCompanyRemove(company_id, bcrr);
936 * Send chat to the admin network (if they did opt in for the respective update).
938 void NetworkAdminChat(NetworkAction action, DestType desttype, ClientID client_id, const char *msg, int64 data, bool from_admin)
940 if (from_admin) return;
942 ServerNetworkAdminSocketHandler *as;
943 FOR_ALL_ACTIVE_ADMIN_SOCKETS(as) {
944 if (as->update_frequency[ADMIN_UPDATE_CHAT] & ADMIN_FREQUENCY_AUTOMATIC) {
945 as->SendChat(action, desttype, client_id, msg, data);
951 * Pass the rcon reply to the admin.
952 * @param admin_index The admin to give the reply.
953 * @param colour_code The colour of the string.
954 * @param string The string to show.
956 void NetworkServerSendAdminRcon(AdminIndex admin_index, TextColour colour_code, const char *string)
958 ServerNetworkAdminSocketHandler::Get(admin_index)->SendRcon(colour_code, string);
962 * Send console to the admin network (if they did opt in for the respective update).
963 * @param origin the origin of the message.
964 * @param string the message as printed on the console.
966 void NetworkAdminConsole(const char *origin, const char *string)
968 ServerNetworkAdminSocketHandler *as;
969 FOR_ALL_ACTIVE_ADMIN_SOCKETS(as) {
970 if (as->update_frequency[ADMIN_UPDATE_CONSOLE] & ADMIN_FREQUENCY_AUTOMATIC) {
971 as->SendConsole(origin, string);
977 * Send GameScript JSON to the admin network (if they did opt in for the respective update).
978 * @param json The JSON data as received from the GameScript.
980 void NetworkAdminGameScript(const char *json)
982 ServerNetworkAdminSocketHandler *as;
983 FOR_ALL_ACTIVE_ADMIN_SOCKETS(as) {
984 if (as->update_frequency[ADMIN_UPDATE_GAMESCRIPT] & ADMIN_FREQUENCY_AUTOMATIC) {
985 as->SendGameScript(json);
991 * Distribute CommandPacket details over the admin network for logging purposes.
992 * @param owner The owner of the CommandPacket (who sent us the CommandPacket).
993 * @param cp The CommandPacket to be distributed.
995 void NetworkAdminCmdLogging(const NetworkClientSocket *owner, const CommandPacket *cp)
997 ClientID client_id = owner == NULL ? _network_own_client_id : owner->client_id;
999 ServerNetworkAdminSocketHandler *as;
1000 FOR_ALL_ACTIVE_ADMIN_SOCKETS(as) {
1001 if (as->update_frequency[ADMIN_UPDATE_CMD_LOGGING] & ADMIN_FREQUENCY_AUTOMATIC) {
1002 as->SendCmdLogging(client_id, cp);
1008 * Send a Welcome packet to all connected admins
1010 void ServerNetworkAdminSocketHandler::WelcomeAll()
1012 ServerNetworkAdminSocketHandler *as;
1013 FOR_ALL_ACTIVE_ADMIN_SOCKETS(as) {
1014 as->SendWelcome();
1019 * Send (push) updates to the admin network as they have registered for these updates.
1020 * @param freq the frequency to be processed.
1022 void NetworkAdminUpdate(AdminUpdateFrequency freq)
1024 ServerNetworkAdminSocketHandler *as;
1025 FOR_ALL_ACTIVE_ADMIN_SOCKETS(as) {
1026 for (int i = 0; i < ADMIN_UPDATE_END; i++) {
1027 if (as->update_frequency[i] & freq) {
1028 /* Update the admin for the required details */
1029 switch (i) {
1030 case ADMIN_UPDATE_DATE:
1031 as->SendDate();
1032 break;
1034 case ADMIN_UPDATE_COMPANY_ECONOMY:
1035 as->SendCompanyEconomy();
1036 break;
1038 case ADMIN_UPDATE_COMPANY_STATS:
1039 as->SendCompanyStats();
1040 break;
1042 default: NOT_REACHED();
1049 #endif /* ENABLE_NETWORK */