(svn r28004) -Update from Eints:
[openttd.git] / src / network / network_server.cpp
blobbcee408528d402e0c04c638d3e7ff867f890ac72
1 /* $Id$ */
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_server.cpp Server part of the 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_server.h"
19 #include "network_udp.h"
20 #include "network_base.h"
21 #include "../console_func.h"
22 #include "../company_base.h"
23 #include "../command_func.h"
24 #include "../saveload/saveload.h"
25 #include "../saveload/saveload_filter.h"
26 #include "../station_base.h"
27 #include "../genworld.h"
28 #include "../company_func.h"
29 #include "../company_gui.h"
30 #include "../roadveh.h"
31 #include "../order_backup.h"
32 #include "../core/pool_func.hpp"
33 #include "../core/random_func.hpp"
34 #include "../rev.h"
36 #include "../safeguards.h"
39 /* This file handles all the server-commands */
41 DECLARE_POSTFIX_INCREMENT(ClientID)
42 /** The identifier counter for new clients (is never decreased) */
43 static ClientID _network_client_id = CLIENT_ID_FIRST;
45 /** Make very sure the preconditions given in network_type.h are actually followed */
46 assert_compile(MAX_CLIENT_SLOTS > MAX_CLIENTS);
47 /** Yes... */
48 assert_compile(NetworkClientSocketPool::MAX_SIZE == MAX_CLIENT_SLOTS);
50 /** The pool with clients. */
51 NetworkClientSocketPool _networkclientsocket_pool("NetworkClientSocket");
52 INSTANTIATE_POOL_METHODS(NetworkClientSocket)
54 /** Instantiate the listen sockets. */
55 template SocketList TCPListenHandler<ServerNetworkGameSocketHandler, PACKET_SERVER_FULL, PACKET_SERVER_BANNED>::sockets;
57 /** Writing a savegame directly to a number of packets. */
58 struct PacketWriter : SaveFilter {
59 ServerNetworkGameSocketHandler *cs; ///< Socket we are associated with.
60 Packet *current; ///< The packet we're currently writing to.
61 size_t total_size; ///< Total size of the compressed savegame.
62 Packet *packets; ///< Packet queue of the savegame; send these "slowly" to the client.
63 ThreadMutex *mutex; ///< Mutex for making threaded saving safe.
65 /**
66 * Create the packet writer.
67 * @param cs The socket handler we're making the packets for.
69 PacketWriter(ServerNetworkGameSocketHandler *cs) : SaveFilter(NULL), cs(cs), current(NULL), total_size(0), packets(NULL)
71 this->mutex = ThreadMutex::New();
74 /** Make sure everything is cleaned up. */
75 ~PacketWriter()
77 if (this->mutex != NULL) this->mutex->BeginCritical();
79 if (this->cs != NULL && this->mutex != NULL) {
80 this->mutex->WaitForSignal();
83 /* This must all wait until the Destroy function is called. */
85 while (this->packets != NULL) {
86 Packet *p = this->packets->next;
87 delete this->packets;
88 this->packets = p;
91 delete this->current;
93 if (this->mutex != NULL) this->mutex->EndCritical();
95 delete this->mutex;
96 this->mutex = NULL;
99 /**
100 * Begin the destruction of this packet writer. It can happen in two ways:
101 * in the first case the client disconnected while saving the map. In this
102 * case the saving has not finished and killed this PacketWriter. In that
103 * case we simply set cs to NULL, triggering the appending to fail due to
104 * the connection problem and eventually triggering the destructor. In the
105 * second case the destructor is already called, and it is waiting for our
106 * signal which we will send. Only then the packets will be removed by the
107 * destructor.
109 void Destroy()
111 if (this->mutex != NULL) this->mutex->BeginCritical();
113 this->cs = NULL;
115 if (this->mutex != NULL) this->mutex->SendSignal();
117 if (this->mutex != NULL) this->mutex->EndCritical();
119 /* Make sure the saving is completely cancelled. Yes,
120 * we need to handle the save finish as well as the
121 * next connection might just be requesting a map. */
122 WaitTillSaved();
123 ProcessAsyncSaveFinish();
127 * Checks whether there are packets.
128 * It's not 100% threading safe, but this is only asked for when checking
129 * whether there still is something to send. Then another call will be made
130 * to actually get the Packet, which will be the only one popping packets
131 * and thus eventually setting this on false.
133 bool HasPackets()
135 return this->packets != NULL;
139 * Pop a single created packet from the queue with packets.
141 Packet *PopPacket()
143 if (this->mutex != NULL) this->mutex->BeginCritical();
145 Packet *p = this->packets;
146 this->packets = p->next;
147 p->next = NULL;
149 if (this->mutex != NULL) this->mutex->EndCritical();
151 return p;
154 /** Append the current packet to the queue. */
155 void AppendQueue()
157 if (this->current == NULL) return;
159 Packet **p = &this->packets;
160 while (*p != NULL) {
161 p = &(*p)->next;
163 *p = this->current;
165 this->current = NULL;
168 /* virtual */ void Write(byte *buf, size_t size)
170 /* We want to abort the saving when the socket is closed. */
171 if (this->cs == NULL) SlError(STR_NETWORK_ERROR_LOSTCONNECTION);
173 if (this->current == NULL) this->current = new Packet(PACKET_SERVER_MAP_DATA);
175 if (this->mutex != NULL) this->mutex->BeginCritical();
177 byte *bufe = buf + size;
178 while (buf != bufe) {
179 size_t to_write = min(SEND_MTU - this->current->size, bufe - buf);
180 memcpy(this->current->buffer + this->current->size, buf, to_write);
181 this->current->size += (PacketSize)to_write;
182 buf += to_write;
184 if (this->current->size == SEND_MTU) {
185 this->AppendQueue();
186 if (buf != bufe) this->current = new Packet(PACKET_SERVER_MAP_DATA);
190 if (this->mutex != NULL) this->mutex->EndCritical();
192 this->total_size += size;
195 /* virtual */ void Finish()
197 /* We want to abort the saving when the socket is closed. */
198 if (this->cs == NULL) SlError(STR_NETWORK_ERROR_LOSTCONNECTION);
200 if (this->mutex != NULL) this->mutex->BeginCritical();
202 /* Make sure the last packet is flushed. */
203 this->AppendQueue();
205 /* Add a packet stating that this is the end to the queue. */
206 this->current = new Packet(PACKET_SERVER_MAP_DONE);
207 this->AppendQueue();
209 /* Fast-track the size to the client. */
210 Packet *p = new Packet(PACKET_SERVER_MAP_SIZE);
211 p->Send_uint32((uint32)this->total_size);
212 this->cs->NetworkTCPSocketHandler::SendPacket(p);
214 if (this->mutex != NULL) this->mutex->EndCritical();
220 * Create a new socket for the server side of the game connection.
221 * @param s The socket to connect with.
223 ServerNetworkGameSocketHandler::ServerNetworkGameSocketHandler(SOCKET s) : NetworkGameSocketHandler(s)
225 this->status = STATUS_INACTIVE;
226 this->client_id = _network_client_id++;
227 this->receive_limit = _settings_client.network.bytes_per_frame_burst;
229 /* The Socket and Info pools need to be the same in size. After all,
230 * each Socket will be associated with at most one Info object. As
231 * such if the Socket was allocated the Info object can as well. */
232 assert_compile(NetworkClientSocketPool::MAX_SIZE == NetworkClientInfoPool::MAX_SIZE);
236 * Clear everything related to this client.
238 ServerNetworkGameSocketHandler::~ServerNetworkGameSocketHandler()
240 if (_redirect_console_to_client == this->client_id) _redirect_console_to_client = INVALID_CLIENT_ID;
241 OrderBackup::ResetUser(this->client_id);
243 if (this->savegame != NULL) {
244 this->savegame->Destroy();
245 this->savegame = NULL;
249 Packet *ServerNetworkGameSocketHandler::ReceivePacket()
251 /* Only allow receiving when we have some buffer free; this value
252 * can go negative, but eventually it will become positive again. */
253 if (this->receive_limit <= 0) return NULL;
255 /* We can receive a packet, so try that and if needed account for
256 * the amount of received data. */
257 Packet *p = this->NetworkTCPSocketHandler::ReceivePacket();
258 if (p != NULL) this->receive_limit -= p->size;
259 return p;
262 NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvStatus status)
264 assert(status != NETWORK_RECV_STATUS_OKAY);
266 * Sending a message just before leaving the game calls cs->SendPackets.
267 * This might invoke this function, which means that when we close the
268 * connection after cs->SendPackets we will close an already closed
269 * connection. This handles that case gracefully without having to make
270 * that code any more complex or more aware of the validity of the socket.
272 if (this->sock == INVALID_SOCKET) return status;
274 if (status != NETWORK_RECV_STATUS_CONN_LOST && !this->HasClientQuit() && this->status >= STATUS_AUTHORIZED) {
275 /* We did not receive a leave message from this client... */
276 char client_name[NETWORK_CLIENT_NAME_LENGTH];
277 NetworkClientSocket *new_cs;
279 this->GetClientName(client_name, lastof(client_name));
281 NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, NULL, STR_NETWORK_ERROR_CLIENT_CONNECTION_LOST);
283 /* Inform other clients of this... strange leaving ;) */
284 FOR_ALL_CLIENT_SOCKETS(new_cs) {
285 if (new_cs->status > STATUS_AUTHORIZED && this != new_cs) {
286 new_cs->SendErrorQuit(this->client_id, NETWORK_ERROR_CONNECTION_LOST);
291 NetworkAdminClientError(this->client_id, NETWORK_ERROR_CONNECTION_LOST);
292 DEBUG(net, 1, "Closed client connection %d", this->client_id);
294 /* We just lost one client :( */
295 if (this->status >= STATUS_AUTHORIZED) _network_game_info.clients_on--;
296 extern byte _network_clients_connected;
297 _network_clients_connected--;
299 DeleteWindowById(WC_CLIENT_LIST_POPUP, this->client_id);
300 SetWindowDirty(WC_CLIENT_LIST, 0);
302 this->SendPackets(true);
304 delete this->GetInfo();
305 delete this;
307 return status;
311 * Whether an connection is allowed or not at this moment.
312 * @return true if the connection is allowed.
314 /* static */ bool ServerNetworkGameSocketHandler::AllowConnection()
316 extern byte _network_clients_connected;
317 bool accept = _network_clients_connected < MAX_CLIENTS && _network_game_info.clients_on < _settings_client.network.max_clients;
319 /* We can't go over the MAX_CLIENTS limit here. However, the
320 * pool must have place for all clients and ourself. */
321 assert_compile(NetworkClientSocketPool::MAX_SIZE == MAX_CLIENTS + 1);
322 assert(!accept || ServerNetworkGameSocketHandler::CanAllocateItem());
323 return accept;
326 /** Send the packets for the server sockets. */
327 /* static */ void ServerNetworkGameSocketHandler::Send()
329 NetworkClientSocket *cs;
330 FOR_ALL_CLIENT_SOCKETS(cs) {
331 if (cs->writable) {
332 if (cs->SendPackets() != SPS_CLOSED && cs->status == STATUS_MAP) {
333 /* This client is in the middle of a map-send, call the function for that */
334 cs->SendMap();
340 static void NetworkHandleCommandQueue(NetworkClientSocket *cs);
342 /***********
343 * Sending functions
344 * DEF_SERVER_SEND_COMMAND has parameter: NetworkClientSocket *cs
345 ************/
348 * Send the client information about a client.
349 * @param ci The client to send information about.
351 NetworkRecvStatus ServerNetworkGameSocketHandler::SendClientInfo(NetworkClientInfo *ci)
353 if (ci->client_id != INVALID_CLIENT_ID) {
354 Packet *p = new Packet(PACKET_SERVER_CLIENT_INFO);
355 p->Send_uint32(ci->client_id);
356 p->Send_uint8 (ci->client_playas);
357 p->Send_string(ci->client_name);
359 this->SendPacket(p);
361 return NETWORK_RECV_STATUS_OKAY;
364 /** Send the client information about the companies. */
365 NetworkRecvStatus ServerNetworkGameSocketHandler::SendCompanyInfo()
367 /* Fetch the latest version of the stats */
368 NetworkCompanyStats company_stats[MAX_COMPANIES];
369 NetworkPopulateCompanyStats(company_stats);
371 /* Make a list of all clients per company */
372 char clients[MAX_COMPANIES][NETWORK_CLIENTS_LENGTH];
373 NetworkClientSocket *csi;
374 memset(clients, 0, sizeof(clients));
376 /* Add the local player (if not dedicated) */
377 const NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(CLIENT_ID_SERVER);
378 if (ci != NULL && Company::IsValidID(ci->client_playas)) {
379 strecpy(clients[ci->client_playas], ci->client_name, lastof(clients[ci->client_playas]));
382 FOR_ALL_CLIENT_SOCKETS(csi) {
383 char client_name[NETWORK_CLIENT_NAME_LENGTH];
385 ((ServerNetworkGameSocketHandler*)csi)->GetClientName(client_name, lastof(client_name));
387 ci = csi->GetInfo();
388 if (ci != NULL && Company::IsValidID(ci->client_playas)) {
389 if (!StrEmpty(clients[ci->client_playas])) {
390 strecat(clients[ci->client_playas], ", ", lastof(clients[ci->client_playas]));
393 strecat(clients[ci->client_playas], client_name, lastof(clients[ci->client_playas]));
397 /* Now send the data */
399 Company *company;
400 Packet *p;
402 FOR_ALL_COMPANIES(company) {
403 p = new Packet(PACKET_SERVER_COMPANY_INFO);
405 p->Send_uint8 (NETWORK_COMPANY_INFO_VERSION);
406 p->Send_bool (true);
407 this->SendCompanyInformation(p, company, &company_stats[company->index]);
409 if (StrEmpty(clients[company->index])) {
410 p->Send_string("<none>");
411 } else {
412 p->Send_string(clients[company->index]);
415 this->SendPacket(p);
418 p = new Packet(PACKET_SERVER_COMPANY_INFO);
420 p->Send_uint8 (NETWORK_COMPANY_INFO_VERSION);
421 p->Send_bool (false);
423 this->SendPacket(p);
424 return NETWORK_RECV_STATUS_OKAY;
428 * Send an error to the client, and close its connection.
429 * @param error The error to disconnect for.
431 NetworkRecvStatus ServerNetworkGameSocketHandler::SendError(NetworkErrorCode error)
433 char str[100];
434 Packet *p = new Packet(PACKET_SERVER_ERROR);
436 p->Send_uint8(error);
437 this->SendPacket(p);
439 StringID strid = GetNetworkErrorMsg(error);
440 GetString(str, strid, lastof(str));
442 /* Only send when the current client was in game */
443 if (this->status > STATUS_AUTHORIZED) {
444 NetworkClientSocket *new_cs;
445 char client_name[NETWORK_CLIENT_NAME_LENGTH];
447 this->GetClientName(client_name, lastof(client_name));
449 DEBUG(net, 1, "'%s' made an error and has been disconnected. Reason: '%s'", client_name, str);
451 NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, NULL, strid);
453 FOR_ALL_CLIENT_SOCKETS(new_cs) {
454 if (new_cs->status > STATUS_AUTHORIZED && new_cs != this) {
455 /* Some errors we filter to a more general error. Clients don't have to know the real
456 * reason a joining failed. */
457 if (error == NETWORK_ERROR_NOT_AUTHORIZED || error == NETWORK_ERROR_NOT_EXPECTED || error == NETWORK_ERROR_WRONG_REVISION) {
458 error = NETWORK_ERROR_ILLEGAL_PACKET;
460 new_cs->SendErrorQuit(this->client_id, error);
464 NetworkAdminClientError(this->client_id, error);
465 } else {
466 DEBUG(net, 1, "Client %d made an error and has been disconnected. Reason: '%s'", this->client_id, str);
469 /* The client made a mistake, so drop his connection now! */
470 return this->CloseConnection(NETWORK_RECV_STATUS_SERVER_ERROR);
473 /** Send the check for the NewGRFs. */
474 NetworkRecvStatus ServerNetworkGameSocketHandler::SendNewGRFCheck()
476 Packet *p = new Packet(PACKET_SERVER_CHECK_NEWGRFS);
477 const GRFConfig *c;
478 uint grf_count = 0;
480 for (c = _grfconfig; c != NULL; c = c->next) {
481 if (!HasBit(c->flags, GCF_STATIC)) grf_count++;
484 p->Send_uint8 (grf_count);
485 for (c = _grfconfig; c != NULL; c = c->next) {
486 if (!HasBit(c->flags, GCF_STATIC)) this->SendGRFIdentifier(p, &c->ident);
489 this->SendPacket(p);
490 return NETWORK_RECV_STATUS_OKAY;
493 /** Request the game password. */
494 NetworkRecvStatus ServerNetworkGameSocketHandler::SendNeedGamePassword()
496 /* Invalid packet when status is STATUS_AUTH_GAME or higher */
497 if (this->status >= STATUS_AUTH_GAME) return this->CloseConnection(NETWORK_RECV_STATUS_MALFORMED_PACKET);
499 this->status = STATUS_AUTH_GAME;
500 /* Reset 'lag' counters */
501 this->last_frame = this->last_frame_server = _frame_counter;
503 Packet *p = new Packet(PACKET_SERVER_NEED_GAME_PASSWORD);
504 this->SendPacket(p);
505 return NETWORK_RECV_STATUS_OKAY;
508 /** Request the company password. */
509 NetworkRecvStatus ServerNetworkGameSocketHandler::SendNeedCompanyPassword()
511 /* Invalid packet when status is STATUS_AUTH_COMPANY or higher */
512 if (this->status >= STATUS_AUTH_COMPANY) return this->CloseConnection(NETWORK_RECV_STATUS_MALFORMED_PACKET);
514 this->status = STATUS_AUTH_COMPANY;
515 /* Reset 'lag' counters */
516 this->last_frame = this->last_frame_server = _frame_counter;
518 Packet *p = new Packet(PACKET_SERVER_NEED_COMPANY_PASSWORD);
519 p->Send_uint32(_settings_game.game_creation.generation_seed);
520 p->Send_string(_settings_client.network.network_id);
521 this->SendPacket(p);
522 return NETWORK_RECV_STATUS_OKAY;
525 /** Send the client a welcome message with some basic information. */
526 NetworkRecvStatus ServerNetworkGameSocketHandler::SendWelcome()
528 Packet *p;
529 NetworkClientSocket *new_cs;
531 /* Invalid packet when status is AUTH or higher */
532 if (this->status >= STATUS_AUTHORIZED) return this->CloseConnection(NETWORK_RECV_STATUS_MALFORMED_PACKET);
534 this->status = STATUS_AUTHORIZED;
535 /* Reset 'lag' counters */
536 this->last_frame = this->last_frame_server = _frame_counter;
538 _network_game_info.clients_on++;
540 p = new Packet(PACKET_SERVER_WELCOME);
541 p->Send_uint32(this->client_id);
542 p->Send_uint32(_settings_game.game_creation.generation_seed);
543 p->Send_string(_settings_client.network.network_id);
544 this->SendPacket(p);
546 /* Transmit info about all the active clients */
547 FOR_ALL_CLIENT_SOCKETS(new_cs) {
548 if (new_cs != this && new_cs->status > STATUS_AUTHORIZED) {
549 this->SendClientInfo(new_cs->GetInfo());
552 /* Also send the info of the server */
553 return this->SendClientInfo(NetworkClientInfo::GetByClientID(CLIENT_ID_SERVER));
556 /** Tell the client that its put in a waiting queue. */
557 NetworkRecvStatus ServerNetworkGameSocketHandler::SendWait()
559 int waiting = 0;
560 NetworkClientSocket *new_cs;
561 Packet *p;
563 /* Count how many clients are waiting in the queue, in front of you! */
564 FOR_ALL_CLIENT_SOCKETS(new_cs) {
565 if (new_cs->status != STATUS_MAP_WAIT) continue;
566 if (new_cs->GetInfo()->join_date < this->GetInfo()->join_date || (new_cs->GetInfo()->join_date == this->GetInfo()->join_date && new_cs->client_id < this->client_id)) waiting++;
569 p = new Packet(PACKET_SERVER_WAIT);
570 p->Send_uint8(waiting);
571 this->SendPacket(p);
572 return NETWORK_RECV_STATUS_OKAY;
575 /** This sends the map to the client */
576 NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap()
578 static uint sent_packets; // How many packets we did send successfully last time
580 if (this->status < STATUS_AUTHORIZED) {
581 /* Illegal call, return error and ignore the packet */
582 return this->SendError(NETWORK_ERROR_NOT_AUTHORIZED);
585 if (this->status == STATUS_AUTHORIZED) {
586 this->savegame = new PacketWriter(this);
588 /* Now send the _frame_counter and how many packets are coming */
589 Packet *p = new Packet(PACKET_SERVER_MAP_BEGIN);
590 p->Send_uint32(_frame_counter);
591 this->SendPacket(p);
593 NetworkSyncCommandQueue(this);
594 this->status = STATUS_MAP;
595 /* Mark the start of download */
596 this->last_frame = _frame_counter;
597 this->last_frame_server = _frame_counter;
599 sent_packets = 4; // We start with trying 4 packets
601 /* Make a dump of the current game */
602 if (SaveWithFilter(this->savegame, true) != SL_OK) usererror("network savedump failed");
605 if (this->status == STATUS_MAP) {
606 bool last_packet = false;
607 bool has_packets = false;
609 for (uint i = 0; (has_packets = this->savegame->HasPackets()) && i < sent_packets; i++) {
610 Packet *p = this->savegame->PopPacket();
611 last_packet = p->buffer[2] == PACKET_SERVER_MAP_DONE;
613 this->SendPacket(p);
615 if (last_packet) {
616 /* There is no more data, so break the for */
617 break;
621 if (last_packet) {
622 /* Done reading, make sure saving is done as well */
623 this->savegame->Destroy();
624 this->savegame = NULL;
626 /* Set the status to DONE_MAP, no we will wait for the client
627 * to send it is ready (maybe that happens like never ;)) */
628 this->status = STATUS_DONE_MAP;
630 /* Find the best candidate for joining, i.e. the first joiner. */
631 NetworkClientSocket *new_cs;
632 NetworkClientSocket *best = NULL;
633 FOR_ALL_CLIENT_SOCKETS(new_cs) {
634 if (new_cs->status == STATUS_MAP_WAIT) {
635 if (best == NULL || best->GetInfo()->join_date > new_cs->GetInfo()->join_date || (best->GetInfo()->join_date == new_cs->GetInfo()->join_date && best->client_id > new_cs->client_id)) {
636 best = new_cs;
641 /* Is there someone else to join? */
642 if (best != NULL) {
643 /* Let the first start joining. */
644 best->status = STATUS_AUTHORIZED;
645 best->SendMap();
647 /* And update the rest. */
648 FOR_ALL_CLIENT_SOCKETS(new_cs) {
649 if (new_cs->status == STATUS_MAP_WAIT) new_cs->SendWait();
654 switch (this->SendPackets()) {
655 case SPS_CLOSED:
656 return NETWORK_RECV_STATUS_CONN_LOST;
658 case SPS_ALL_SENT:
659 /* All are sent, increase the sent_packets */
660 if (has_packets) sent_packets *= 2;
661 break;
663 case SPS_PARTLY_SENT:
664 /* Only a part is sent; leave the transmission state. */
665 break;
667 case SPS_NONE_SENT:
668 /* Not everything is sent, decrease the sent_packets */
669 if (sent_packets > 1) sent_packets /= 2;
670 break;
673 return NETWORK_RECV_STATUS_OKAY;
677 * Tell that a client joined.
678 * @param client_id The client that joined.
680 NetworkRecvStatus ServerNetworkGameSocketHandler::SendJoin(ClientID client_id)
682 Packet *p = new Packet(PACKET_SERVER_JOIN);
684 p->Send_uint32(client_id);
686 this->SendPacket(p);
687 return NETWORK_RECV_STATUS_OKAY;
690 /** Tell the client that they may run to a particular frame. */
691 NetworkRecvStatus ServerNetworkGameSocketHandler::SendFrame()
693 Packet *p = new Packet(PACKET_SERVER_FRAME);
694 p->Send_uint32(_frame_counter);
695 p->Send_uint32(_frame_counter_max);
696 #ifdef ENABLE_NETWORK_SYNC_EVERY_FRAME
697 p->Send_uint32(_sync_seed_1);
698 #ifdef NETWORK_SEND_DOUBLE_SEED
699 p->Send_uint32(_sync_seed_2);
700 #endif
701 #endif
703 /* If token equals 0, we need to make a new token and send that. */
704 if (this->last_token == 0) {
705 this->last_token = InteractiveRandomRange(UINT8_MAX - 1) + 1;
706 p->Send_uint8(this->last_token);
709 this->SendPacket(p);
710 return NETWORK_RECV_STATUS_OKAY;
713 /** Request the client to sync. */
714 NetworkRecvStatus ServerNetworkGameSocketHandler::SendSync()
716 Packet *p = new Packet(PACKET_SERVER_SYNC);
717 p->Send_uint32(_frame_counter);
718 p->Send_uint32(_sync_seed_1);
720 #ifdef NETWORK_SEND_DOUBLE_SEED
721 p->Send_uint32(_sync_seed_2);
722 #endif
723 this->SendPacket(p);
724 return NETWORK_RECV_STATUS_OKAY;
728 * Send a command to the client to execute.
729 * @param cp The command to send.
731 NetworkRecvStatus ServerNetworkGameSocketHandler::SendCommand(const CommandPacket *cp)
733 Packet *p = new Packet(PACKET_SERVER_COMMAND);
735 this->NetworkGameSocketHandler::SendCommand(p, cp);
736 p->Send_uint32(cp->frame);
737 p->Send_bool (cp->my_cmd);
739 this->SendPacket(p);
740 return NETWORK_RECV_STATUS_OKAY;
744 * Send a chat message.
745 * @param action The action associated with the message.
746 * @param client_id The origin of the chat message.
747 * @param self_send Whether we did send the message.
748 * @param msg The actual message.
749 * @param data Arbitrary extra data.
751 NetworkRecvStatus ServerNetworkGameSocketHandler::SendChat(NetworkAction action, ClientID client_id, bool self_send, const char *msg, int64 data)
753 if (this->status < STATUS_PRE_ACTIVE) return NETWORK_RECV_STATUS_OKAY;
755 Packet *p = new Packet(PACKET_SERVER_CHAT);
757 p->Send_uint8 (action);
758 p->Send_uint32(client_id);
759 p->Send_bool (self_send);
760 p->Send_string(msg);
761 p->Send_uint64(data);
763 this->SendPacket(p);
764 return NETWORK_RECV_STATUS_OKAY;
768 * Tell the client another client quit with an error.
769 * @param client_id The client that quit.
770 * @param errorno The reason the client quit.
772 NetworkRecvStatus ServerNetworkGameSocketHandler::SendErrorQuit(ClientID client_id, NetworkErrorCode errorno)
774 Packet *p = new Packet(PACKET_SERVER_ERROR_QUIT);
776 p->Send_uint32(client_id);
777 p->Send_uint8 (errorno);
779 this->SendPacket(p);
780 return NETWORK_RECV_STATUS_OKAY;
784 * Tell the client another client quit.
785 * @param client_id The client that quit.
787 NetworkRecvStatus ServerNetworkGameSocketHandler::SendQuit(ClientID client_id)
789 Packet *p = new Packet(PACKET_SERVER_QUIT);
791 p->Send_uint32(client_id);
793 this->SendPacket(p);
794 return NETWORK_RECV_STATUS_OKAY;
797 /** Tell the client we're shutting down. */
798 NetworkRecvStatus ServerNetworkGameSocketHandler::SendShutdown()
800 Packet *p = new Packet(PACKET_SERVER_SHUTDOWN);
801 this->SendPacket(p);
802 return NETWORK_RECV_STATUS_OKAY;
805 /** Tell the client we're starting a new game. */
806 NetworkRecvStatus ServerNetworkGameSocketHandler::SendNewGame()
808 Packet *p = new Packet(PACKET_SERVER_NEWGAME);
809 this->SendPacket(p);
810 return NETWORK_RECV_STATUS_OKAY;
814 * Send the result of a console action.
815 * @param colour The colour of the result.
816 * @param command The command that was executed.
818 NetworkRecvStatus ServerNetworkGameSocketHandler::SendRConResult(uint16 colour, const char *command)
820 Packet *p = new Packet(PACKET_SERVER_RCON);
822 p->Send_uint16(colour);
823 p->Send_string(command);
824 this->SendPacket(p);
825 return NETWORK_RECV_STATUS_OKAY;
829 * Tell that a client moved to another company.
830 * @param client_id The client that moved.
831 * @param company_id The company the client moved to.
833 NetworkRecvStatus ServerNetworkGameSocketHandler::SendMove(ClientID client_id, CompanyID company_id)
835 Packet *p = new Packet(PACKET_SERVER_MOVE);
837 p->Send_uint32(client_id);
838 p->Send_uint8(company_id);
839 this->SendPacket(p);
840 return NETWORK_RECV_STATUS_OKAY;
843 /** Send an update about the company password states. */
844 NetworkRecvStatus ServerNetworkGameSocketHandler::SendCompanyUpdate()
846 Packet *p = new Packet(PACKET_SERVER_COMPANY_UPDATE);
848 p->Send_uint16(_network_company_passworded);
849 this->SendPacket(p);
850 return NETWORK_RECV_STATUS_OKAY;
853 /** Send an update about the max company/spectator counts. */
854 NetworkRecvStatus ServerNetworkGameSocketHandler::SendConfigUpdate()
856 Packet *p = new Packet(PACKET_SERVER_CONFIG_UPDATE);
858 p->Send_uint8(_settings_client.network.max_companies);
859 p->Send_uint8(_settings_client.network.max_spectators);
860 this->SendPacket(p);
861 return NETWORK_RECV_STATUS_OKAY;
864 /***********
865 * Receiving functions
866 * DEF_SERVER_RECEIVE_COMMAND has parameter: NetworkClientSocket *cs, Packet *p
867 ************/
869 NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMPANY_INFO(Packet *p)
871 return this->SendCompanyInfo();
874 NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_NEWGRFS_CHECKED(Packet *p)
876 if (this->status != STATUS_NEWGRFS_CHECK) {
877 /* Illegal call, return error and ignore the packet */
878 return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
881 NetworkClientInfo *ci = this->GetInfo();
883 /* We now want a password from the client else we do not allow him in! */
884 if (!StrEmpty(_settings_client.network.server_password)) {
885 return this->SendNeedGamePassword();
888 if (Company::IsValidID(ci->client_playas) && !StrEmpty(_network_company_states[ci->client_playas].password)) {
889 return this->SendNeedCompanyPassword();
892 return this->SendWelcome();
895 NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p)
897 if (this->status != STATUS_INACTIVE) {
898 /* Illegal call, return error and ignore the packet */
899 return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
902 char name[NETWORK_CLIENT_NAME_LENGTH];
903 CompanyID playas;
904 NetworkLanguage client_lang;
905 char client_revision[NETWORK_REVISION_LENGTH];
907 p->Recv_string(client_revision, sizeof(client_revision));
908 uint32 newgrf_version = p->Recv_uint32();
910 /* Check if the client has revision control enabled */
911 if (!IsNetworkCompatibleVersion(client_revision) || _openttd_newgrf_version != newgrf_version) {
912 /* Different revisions!! */
913 return this->SendError(NETWORK_ERROR_WRONG_REVISION);
916 p->Recv_string(name, sizeof(name));
917 playas = (Owner)p->Recv_uint8();
918 client_lang = (NetworkLanguage)p->Recv_uint8();
920 if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST;
922 /* join another company does not affect these values */
923 switch (playas) {
924 case COMPANY_NEW_COMPANY: // New company
925 if (Company::GetNumItems() >= _settings_client.network.max_companies) {
926 return this->SendError(NETWORK_ERROR_FULL);
928 break;
929 case COMPANY_SPECTATOR: // Spectator
930 if (NetworkSpectatorCount() >= _settings_client.network.max_spectators) {
931 return this->SendError(NETWORK_ERROR_FULL);
933 break;
934 default: // Join another company (companies 1-8 (index 0-7))
935 if (!Company::IsValidHumanID(playas)) {
936 return this->SendError(NETWORK_ERROR_COMPANY_MISMATCH);
938 break;
941 /* We need a valid name.. make it Player */
942 if (StrEmpty(name)) strecpy(name, "Player", lastof(name));
944 if (!NetworkFindName(name, lastof(name))) { // Change name if duplicate
945 /* We could not create a name for this client */
946 return this->SendError(NETWORK_ERROR_NAME_IN_USE);
949 assert(NetworkClientInfo::CanAllocateItem());
950 NetworkClientInfo *ci = new NetworkClientInfo(this->client_id);
951 this->SetInfo(ci);
952 ci->join_date = _date;
953 strecpy(ci->client_name, name, lastof(ci->client_name));
954 ci->client_playas = playas;
955 ci->client_lang = client_lang;
956 DEBUG(desync, 1, "client: %08x; %02x; %02x; %02x", _date, _date_fract, (int)ci->client_playas, (int)ci->index);
958 /* Make sure companies to which people try to join are not autocleaned */
959 if (Company::IsValidID(playas)) _network_company_states[playas].months_empty = 0;
961 this->status = STATUS_NEWGRFS_CHECK;
963 if (_grfconfig == NULL) {
964 /* Behave as if we received PACKET_CLIENT_NEWGRFS_CHECKED */
965 return this->Receive_CLIENT_NEWGRFS_CHECKED(NULL);
968 return this->SendNewGRFCheck();
971 NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_GAME_PASSWORD(Packet *p)
973 if (this->status != STATUS_AUTH_GAME) {
974 return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
977 char password[NETWORK_PASSWORD_LENGTH];
978 p->Recv_string(password, sizeof(password));
980 /* Check game password. Allow joining if we cleared the password meanwhile */
981 if (!StrEmpty(_settings_client.network.server_password) &&
982 strcmp(password, _settings_client.network.server_password) != 0) {
983 /* Password is invalid */
984 return this->SendError(NETWORK_ERROR_WRONG_PASSWORD);
987 const NetworkClientInfo *ci = this->GetInfo();
988 if (Company::IsValidID(ci->client_playas) && !StrEmpty(_network_company_states[ci->client_playas].password)) {
989 return this->SendNeedCompanyPassword();
992 /* Valid password, allow user */
993 return this->SendWelcome();
996 NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMPANY_PASSWORD(Packet *p)
998 if (this->status != STATUS_AUTH_COMPANY) {
999 return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
1002 char password[NETWORK_PASSWORD_LENGTH];
1003 p->Recv_string(password, sizeof(password));
1005 /* Check company password. Allow joining if we cleared the password meanwhile.
1006 * Also, check the company is still valid - client could be moved to spectators
1007 * in the middle of the authorization process */
1008 CompanyID playas = this->GetInfo()->client_playas;
1009 if (Company::IsValidID(playas) && !StrEmpty(_network_company_states[playas].password) &&
1010 strcmp(password, _network_company_states[playas].password) != 0) {
1011 /* Password is invalid */
1012 return this->SendError(NETWORK_ERROR_WRONG_PASSWORD);
1015 return this->SendWelcome();
1018 NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_GETMAP(Packet *p)
1020 NetworkClientSocket *new_cs;
1021 /* The client was never joined.. so this is impossible, right?
1022 * Ignore the packet, give the client a warning, and close his connection */
1023 if (this->status < STATUS_AUTHORIZED || this->HasClientQuit()) {
1024 return this->SendError(NETWORK_ERROR_NOT_AUTHORIZED);
1027 /* Check if someone else is receiving the map */
1028 FOR_ALL_CLIENT_SOCKETS(new_cs) {
1029 if (new_cs->status == STATUS_MAP) {
1030 /* Tell the new client to wait */
1031 this->status = STATUS_MAP_WAIT;
1032 return this->SendWait();
1036 /* We receive a request to upload the map.. give it to the client! */
1037 return this->SendMap();
1040 NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_MAP_OK(Packet *p)
1042 /* Client has the map, now start syncing */
1043 if (this->status == STATUS_DONE_MAP && !this->HasClientQuit()) {
1044 char client_name[NETWORK_CLIENT_NAME_LENGTH];
1045 NetworkClientSocket *new_cs;
1047 this->GetClientName(client_name, lastof(client_name));
1049 NetworkTextMessage(NETWORK_ACTION_JOIN, CC_DEFAULT, false, client_name, NULL, this->client_id);
1051 /* Mark the client as pre-active, and wait for an ACK
1052 * so we know he is done loading and in sync with us */
1053 this->status = STATUS_PRE_ACTIVE;
1054 NetworkHandleCommandQueue(this);
1055 this->SendFrame();
1056 this->SendSync();
1058 /* This is the frame the client receives
1059 * we need it later on to make sure the client is not too slow */
1060 this->last_frame = _frame_counter;
1061 this->last_frame_server = _frame_counter;
1063 FOR_ALL_CLIENT_SOCKETS(new_cs) {
1064 if (new_cs->status > STATUS_AUTHORIZED) {
1065 new_cs->SendClientInfo(this->GetInfo());
1066 new_cs->SendJoin(this->client_id);
1070 NetworkAdminClientInfo(this, true);
1072 /* also update the new client with our max values */
1073 this->SendConfigUpdate();
1075 /* quickly update the syncing client with company details */
1076 return this->SendCompanyUpdate();
1079 /* Wrong status for this packet, give a warning to client, and close connection */
1080 return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
1084 * The client has done a command and wants us to handle it
1085 * @param p the packet in which the command was sent
1087 NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMMAND(Packet *p)
1089 /* The client was never joined.. so this is impossible, right?
1090 * Ignore the packet, give the client a warning, and close his connection */
1091 if (this->status < STATUS_DONE_MAP || this->HasClientQuit()) {
1092 return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
1095 if (this->incoming_queue.Count() >= _settings_client.network.max_commands_in_queue) {
1096 return this->SendError(NETWORK_ERROR_TOO_MANY_COMMANDS);
1099 CommandPacket cp;
1100 const char *err = this->ReceiveCommand(p, &cp);
1102 if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST;
1104 NetworkClientInfo *ci = this->GetInfo();
1106 if (err != NULL) {
1107 IConsolePrintF(CC_ERROR, "WARNING: %s from client %d (IP: %s).", err, ci->client_id, this->GetClientIP());
1108 return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
1112 if ((GetCommandFlags(cp.cmd) & CMD_SERVER) && ci->client_id != CLIENT_ID_SERVER) {
1113 IConsolePrintF(CC_ERROR, "WARNING: server only command from: client %d (IP: %s), kicking...", ci->client_id, this->GetClientIP());
1114 return this->SendError(NETWORK_ERROR_KICKED);
1117 if ((GetCommandFlags(cp.cmd) & CMD_SPECTATOR) == 0 && !Company::IsValidID(cp.company) && ci->client_id != CLIENT_ID_SERVER) {
1118 IConsolePrintF(CC_ERROR, "WARNING: spectator issueing command from client %d (IP: %s), kicking...", ci->client_id, this->GetClientIP());
1119 return this->SendError(NETWORK_ERROR_KICKED);
1123 * Only CMD_COMPANY_CTRL is always allowed, for the rest, playas needs
1124 * to match the company in the packet. If it doesn't, the client has done
1125 * something pretty naughty (or a bug), and will be kicked
1127 if (!(cp.cmd == CMD_COMPANY_CTRL && cp.p1 == 0 && ci->client_playas == COMPANY_NEW_COMPANY) && ci->client_playas != cp.company) {
1128 IConsolePrintF(CC_ERROR, "WARNING: client %d (IP: %s) tried to execute a command as company %d, kicking...",
1129 ci->client_playas + 1, this->GetClientIP(), cp.company + 1);
1130 return this->SendError(NETWORK_ERROR_COMPANY_MISMATCH);
1133 if (cp.cmd == CMD_COMPANY_CTRL) {
1134 if (cp.p1 != 0 || cp.company != COMPANY_SPECTATOR) {
1135 return this->SendError(NETWORK_ERROR_CHEATER);
1138 /* 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! */
1139 if (Company::GetNumItems() >= _settings_client.network.max_companies) {
1140 NetworkServerSendChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_CLIENT, ci->client_id, "cannot create new company, server full", CLIENT_ID_SERVER);
1141 return NETWORK_RECV_STATUS_OKAY;
1145 if (GetCommandFlags(cp.cmd) & CMD_CLIENT_ID) cp.p2 = this->client_id;
1147 this->incoming_queue.Append(&cp);
1148 return NETWORK_RECV_STATUS_OKAY;
1151 NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_ERROR(Packet *p)
1153 /* This packets means a client noticed an error and is reporting this
1154 * to us. Display the error and report it to the other clients */
1155 NetworkClientSocket *new_cs;
1156 char str[100];
1157 char client_name[NETWORK_CLIENT_NAME_LENGTH];
1158 NetworkErrorCode errorno = (NetworkErrorCode)p->Recv_uint8();
1160 /* The client was never joined.. thank the client for the packet, but ignore it */
1161 if (this->status < STATUS_DONE_MAP || this->HasClientQuit()) {
1162 return this->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST);
1165 this->GetClientName(client_name, lastof(client_name));
1167 StringID strid = GetNetworkErrorMsg(errorno);
1168 GetString(str, strid, lastof(str));
1170 DEBUG(net, 2, "'%s' reported an error and is closing its connection (%s)", client_name, str);
1172 NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, NULL, strid);
1174 FOR_ALL_CLIENT_SOCKETS(new_cs) {
1175 if (new_cs->status > STATUS_AUTHORIZED) {
1176 new_cs->SendErrorQuit(this->client_id, errorno);
1180 NetworkAdminClientError(this->client_id, errorno);
1182 return this->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST);
1185 NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_QUIT(Packet *p)
1187 /* The client wants to leave. Display this and report it to the other
1188 * clients. */
1189 NetworkClientSocket *new_cs;
1190 char client_name[NETWORK_CLIENT_NAME_LENGTH];
1192 /* The client was never joined.. thank the client for the packet, but ignore it */
1193 if (this->status < STATUS_DONE_MAP || this->HasClientQuit()) {
1194 return this->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST);
1197 this->GetClientName(client_name, lastof(client_name));
1199 NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, NULL, STR_NETWORK_MESSAGE_CLIENT_LEAVING);
1201 FOR_ALL_CLIENT_SOCKETS(new_cs) {
1202 if (new_cs->status > STATUS_AUTHORIZED && new_cs != this) {
1203 new_cs->SendQuit(this->client_id);
1207 NetworkAdminClientQuit(this->client_id);
1209 return this->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST);
1212 NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_ACK(Packet *p)
1214 if (this->status < STATUS_AUTHORIZED) {
1215 /* Illegal call, return error and ignore the packet */
1216 return this->SendError(NETWORK_ERROR_NOT_AUTHORIZED);
1219 uint32 frame = p->Recv_uint32();
1221 /* The client is trying to catch up with the server */
1222 if (this->status == STATUS_PRE_ACTIVE) {
1223 /* The client is not yet catched up? */
1224 if (frame + DAY_TICKS < _frame_counter) return NETWORK_RECV_STATUS_OKAY;
1226 /* Now he is! Unpause the game */
1227 this->status = STATUS_ACTIVE;
1228 this->last_token_frame = _frame_counter;
1230 /* Execute script for, e.g. MOTD */
1231 IConsoleCmdExec("exec scripts/on_server_connect.scr 0");
1234 /* Get, and validate the token. */
1235 uint8 token = p->Recv_uint8();
1236 if (token == this->last_token) {
1237 /* We differentiate between last_token_frame and last_frame so the lag
1238 * test uses the actual lag of the client instead of the lag for getting
1239 * the token back and forth; after all, the token is only sent every
1240 * time we receive a PACKET_CLIENT_ACK, after which we will send a new
1241 * token to the client. If the lag would be one day, then we would not
1242 * be sending the new token soon enough for the new daily scheduled
1243 * PACKET_CLIENT_ACK. This would then register the lag of the client as
1244 * two days, even when it's only a single day. */
1245 this->last_token_frame = _frame_counter;
1246 /* Request a new token. */
1247 this->last_token = 0;
1250 /* The client received the frame, make note of it */
1251 this->last_frame = frame;
1252 /* With those 2 values we can calculate the lag realtime */
1253 this->last_frame_server = _frame_counter;
1254 return NETWORK_RECV_STATUS_OKAY;
1259 * Send an actual chat message.
1260 * @param action The action that's performed.
1261 * @param desttype The type of destination.
1262 * @param dest The actual destination index.
1263 * @param msg The actual message.
1264 * @param from_id The origin of the message.
1265 * @param data Arbitrary data.
1266 * @param from_admin Whether the origin is an admin or not.
1268 void NetworkServerSendChat(NetworkAction action, DestType desttype, int dest, const char *msg, ClientID from_id, int64 data, bool from_admin)
1270 NetworkClientSocket *cs;
1271 const NetworkClientInfo *ci, *ci_own, *ci_to;
1273 switch (desttype) {
1274 case DESTTYPE_CLIENT:
1275 /* Are we sending to the server? */
1276 if ((ClientID)dest == CLIENT_ID_SERVER) {
1277 ci = NetworkClientInfo::GetByClientID(from_id);
1278 /* Display the text locally, and that is it */
1279 if (ci != NULL) {
1280 NetworkTextMessage(action, GetDrawStringCompanyColour(ci->client_playas), false, ci->client_name, msg, data);
1282 if (_settings_client.network.server_admin_chat) {
1283 NetworkAdminChat(action, desttype, from_id, msg, data, from_admin);
1286 } else {
1287 /* Else find the client to send the message to */
1288 FOR_ALL_CLIENT_SOCKETS(cs) {
1289 if (cs->client_id == (ClientID)dest) {
1290 cs->SendChat(action, from_id, false, msg, data);
1291 break;
1296 /* Display the message locally (so you know you have sent it) */
1297 if (from_id != (ClientID)dest) {
1298 if (from_id == CLIENT_ID_SERVER) {
1299 ci = NetworkClientInfo::GetByClientID(from_id);
1300 ci_to = NetworkClientInfo::GetByClientID((ClientID)dest);
1301 if (ci != NULL && ci_to != NULL) {
1302 NetworkTextMessage(action, GetDrawStringCompanyColour(ci->client_playas), true, ci_to->client_name, msg, data);
1304 } else {
1305 FOR_ALL_CLIENT_SOCKETS(cs) {
1306 if (cs->client_id == from_id) {
1307 cs->SendChat(action, (ClientID)dest, true, msg, data);
1308 break;
1313 break;
1314 case DESTTYPE_TEAM: {
1315 /* If this is false, the message is already displayed on the client who sent it. */
1316 bool show_local = true;
1317 /* Find all clients that belong to this company */
1318 ci_to = NULL;
1319 FOR_ALL_CLIENT_SOCKETS(cs) {
1320 ci = cs->GetInfo();
1321 if (ci != NULL && ci->client_playas == (CompanyID)dest) {
1322 cs->SendChat(action, from_id, false, msg, data);
1323 if (cs->client_id == from_id) show_local = false;
1324 ci_to = ci; // Remember a client that is in the company for company-name
1328 /* if the server can read it, let the admin network read it, too. */
1329 if (_local_company == (CompanyID)dest && _settings_client.network.server_admin_chat) {
1330 NetworkAdminChat(action, desttype, from_id, msg, data, from_admin);
1333 ci = NetworkClientInfo::GetByClientID(from_id);
1334 ci_own = NetworkClientInfo::GetByClientID(CLIENT_ID_SERVER);
1335 if (ci != NULL && ci_own != NULL && ci_own->client_playas == dest) {
1336 NetworkTextMessage(action, GetDrawStringCompanyColour(ci->client_playas), false, ci->client_name, msg, data);
1337 if (from_id == CLIENT_ID_SERVER) show_local = false;
1338 ci_to = ci_own;
1341 /* There is no such client */
1342 if (ci_to == NULL) break;
1344 /* Display the message locally (so you know you have sent it) */
1345 if (ci != NULL && show_local) {
1346 if (from_id == CLIENT_ID_SERVER) {
1347 char name[NETWORK_NAME_LENGTH];
1348 StringID str = Company::IsValidID(ci_to->client_playas) ? STR_COMPANY_NAME : STR_NETWORK_SPECTATORS;
1349 SetDParam(0, ci_to->client_playas);
1350 GetString(name, str, lastof(name));
1351 NetworkTextMessage(action, GetDrawStringCompanyColour(ci_own->client_playas), true, name, msg, data);
1352 } else {
1353 FOR_ALL_CLIENT_SOCKETS(cs) {
1354 if (cs->client_id == from_id) {
1355 cs->SendChat(action, ci_to->client_id, true, msg, data);
1360 break;
1362 default:
1363 DEBUG(net, 0, "[server] received unknown chat destination type %d. Doing broadcast instead", desttype);
1364 FALLTHROUGH;
1366 case DESTTYPE_BROADCAST:
1367 FOR_ALL_CLIENT_SOCKETS(cs) {
1368 cs->SendChat(action, from_id, false, msg, data);
1371 NetworkAdminChat(action, desttype, from_id, msg, data, from_admin);
1373 ci = NetworkClientInfo::GetByClientID(from_id);
1374 if (ci != NULL) {
1375 NetworkTextMessage(action, GetDrawStringCompanyColour(ci->client_playas), false, ci->client_name, msg, data);
1377 break;
1381 NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_CHAT(Packet *p)
1383 if (this->status < STATUS_PRE_ACTIVE) {
1384 /* Illegal call, return error and ignore the packet */
1385 return this->SendError(NETWORK_ERROR_NOT_AUTHORIZED);
1388 NetworkAction action = (NetworkAction)p->Recv_uint8();
1389 DestType desttype = (DestType)p->Recv_uint8();
1390 int dest = p->Recv_uint32();
1391 char msg[NETWORK_CHAT_LENGTH];
1393 p->Recv_string(msg, NETWORK_CHAT_LENGTH);
1394 int64 data = p->Recv_uint64();
1396 NetworkClientInfo *ci = this->GetInfo();
1397 switch (action) {
1398 case NETWORK_ACTION_GIVE_MONEY:
1399 if (!Company::IsValidID(ci->client_playas)) break;
1400 FALLTHROUGH;
1401 case NETWORK_ACTION_CHAT:
1402 case NETWORK_ACTION_CHAT_CLIENT:
1403 case NETWORK_ACTION_CHAT_COMPANY:
1404 NetworkServerSendChat(action, desttype, dest, msg, this->client_id, data);
1405 break;
1406 default:
1407 IConsolePrintF(CC_ERROR, "WARNING: invalid chat action from client %d (IP: %s).", ci->client_id, this->GetClientIP());
1408 return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
1410 return NETWORK_RECV_STATUS_OKAY;
1413 NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_SET_PASSWORD(Packet *p)
1415 if (this->status != STATUS_ACTIVE) {
1416 /* Illegal call, return error and ignore the packet */
1417 return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
1420 char password[NETWORK_PASSWORD_LENGTH];
1421 const NetworkClientInfo *ci;
1423 p->Recv_string(password, sizeof(password));
1424 ci = this->GetInfo();
1426 NetworkServerSetCompanyPassword(ci->client_playas, password);
1427 return NETWORK_RECV_STATUS_OKAY;
1430 NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_SET_NAME(Packet *p)
1432 if (this->status != STATUS_ACTIVE) {
1433 /* Illegal call, return error and ignore the packet */
1434 return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
1437 char client_name[NETWORK_CLIENT_NAME_LENGTH];
1438 NetworkClientInfo *ci;
1440 p->Recv_string(client_name, sizeof(client_name));
1441 ci = this->GetInfo();
1443 if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST;
1445 if (ci != NULL) {
1446 /* Display change */
1447 if (NetworkFindName(client_name, lastof(client_name))) {
1448 NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, CC_DEFAULT, false, ci->client_name, client_name);
1449 strecpy(ci->client_name, client_name, lastof(ci->client_name));
1450 NetworkUpdateClientInfo(ci->client_id);
1453 return NETWORK_RECV_STATUS_OKAY;
1456 NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_RCON(Packet *p)
1458 if (this->status != STATUS_ACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
1460 char pass[NETWORK_PASSWORD_LENGTH];
1461 char command[NETWORK_RCONCOMMAND_LENGTH];
1463 if (StrEmpty(_settings_client.network.rcon_password)) return NETWORK_RECV_STATUS_OKAY;
1465 p->Recv_string(pass, sizeof(pass));
1466 p->Recv_string(command, sizeof(command));
1468 if (strcmp(pass, _settings_client.network.rcon_password) != 0) {
1469 DEBUG(net, 0, "[rcon] wrong password from client-id %d", this->client_id);
1470 return NETWORK_RECV_STATUS_OKAY;
1473 DEBUG(net, 0, "[rcon] client-id %d executed: '%s'", this->client_id, command);
1475 _redirect_console_to_client = this->client_id;
1476 IConsoleCmdExec(command);
1477 _redirect_console_to_client = INVALID_CLIENT_ID;
1478 return NETWORK_RECV_STATUS_OKAY;
1481 NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_MOVE(Packet *p)
1483 if (this->status != STATUS_ACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
1485 CompanyID company_id = (Owner)p->Recv_uint8();
1487 /* Check if the company is valid, we don't allow moving to AI companies */
1488 if (company_id != COMPANY_SPECTATOR && !Company::IsValidHumanID(company_id)) return NETWORK_RECV_STATUS_OKAY;
1490 /* Check if we require a password for this company */
1491 if (company_id != COMPANY_SPECTATOR && !StrEmpty(_network_company_states[company_id].password)) {
1492 /* we need a password from the client - should be in this packet */
1493 char password[NETWORK_PASSWORD_LENGTH];
1494 p->Recv_string(password, sizeof(password));
1496 /* Incorrect password sent, return! */
1497 if (strcmp(password, _network_company_states[company_id].password) != 0) {
1498 DEBUG(net, 2, "[move] wrong password from client-id #%d for company #%d", this->client_id, company_id + 1);
1499 return NETWORK_RECV_STATUS_OKAY;
1503 /* if we get here we can move the client */
1504 NetworkServerDoMove(this->client_id, company_id);
1505 return NETWORK_RECV_STATUS_OKAY;
1509 * Package some generic company information into a packet.
1510 * @param p The packet that will contain the data.
1511 * @param c The company to put the of into the packet.
1512 * @param stats The statistics to put in the packet.
1513 * @param max_len The maximum length of the company name.
1515 void NetworkSocketHandler::SendCompanyInformation(Packet *p, const Company *c, const NetworkCompanyStats *stats, uint max_len)
1517 /* Grab the company name */
1518 char company_name[NETWORK_COMPANY_NAME_LENGTH];
1519 SetDParam(0, c->index);
1521 assert(max_len <= lengthof(company_name));
1522 GetString(company_name, STR_COMPANY_NAME, company_name + max_len - 1);
1524 /* Get the income */
1525 Money income = 0;
1526 if (_cur_year - 1 == c->inaugurated_year) {
1527 /* The company is here just 1 year, so display [2], else display[1] */
1528 for (uint i = 0; i < lengthof(c->yearly_expenses[2]); i++) {
1529 income -= c->yearly_expenses[2][i];
1531 } else {
1532 for (uint i = 0; i < lengthof(c->yearly_expenses[1]); i++) {
1533 income -= c->yearly_expenses[1][i];
1537 /* Send the information */
1538 p->Send_uint8 (c->index);
1539 p->Send_string(company_name);
1540 p->Send_uint32(c->inaugurated_year);
1541 p->Send_uint64(c->old_economy[0].company_value);
1542 p->Send_uint64(c->money);
1543 p->Send_uint64(income);
1544 p->Send_uint16(c->old_economy[0].performance_history);
1546 /* Send 1 if there is a password for the company else send 0 */
1547 p->Send_bool (!StrEmpty(_network_company_states[c->index].password));
1549 for (uint i = 0; i < NETWORK_VEH_END; i++) {
1550 p->Send_uint16(stats->num_vehicle[i]);
1553 for (uint i = 0; i < NETWORK_VEH_END; i++) {
1554 p->Send_uint16(stats->num_station[i]);
1557 p->Send_bool(c->is_ai);
1561 * Populate the company stats.
1562 * @param stats the stats to update
1564 void NetworkPopulateCompanyStats(NetworkCompanyStats *stats)
1566 const Vehicle *v;
1567 const Station *s;
1569 memset(stats, 0, sizeof(*stats) * MAX_COMPANIES);
1571 /* Go through all vehicles and count the type of vehicles */
1572 FOR_ALL_VEHICLES(v) {
1573 if (!Company::IsValidID(v->owner) || !v->IsPrimaryVehicle()) continue;
1574 byte type = 0;
1575 switch (v->type) {
1576 case VEH_TRAIN: type = NETWORK_VEH_TRAIN; break;
1577 case VEH_ROAD: type = RoadVehicle::From(v)->IsBus() ? NETWORK_VEH_BUS : NETWORK_VEH_LORRY; break;
1578 case VEH_AIRCRAFT: type = NETWORK_VEH_PLANE; break;
1579 case VEH_SHIP: type = NETWORK_VEH_SHIP; break;
1580 default: continue;
1582 stats[v->owner].num_vehicle[type]++;
1585 /* Go through all stations and count the types of stations */
1586 FOR_ALL_STATIONS(s) {
1587 if (Company::IsValidID(s->owner)) {
1588 NetworkCompanyStats *npi = &stats[s->owner];
1590 if (s->facilities & FACIL_TRAIN) npi->num_station[NETWORK_VEH_TRAIN]++;
1591 if (s->facilities & FACIL_TRUCK_STOP) npi->num_station[NETWORK_VEH_LORRY]++;
1592 if (s->facilities & FACIL_BUS_STOP) npi->num_station[NETWORK_VEH_BUS]++;
1593 if (s->facilities & FACIL_AIRPORT) npi->num_station[NETWORK_VEH_PLANE]++;
1594 if (s->facilities & FACIL_DOCK) npi->num_station[NETWORK_VEH_SHIP]++;
1600 * Send updated client info of a particular client.
1601 * @param client_id The client to send it for.
1603 void NetworkUpdateClientInfo(ClientID client_id)
1605 NetworkClientSocket *cs;
1606 NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(client_id);
1608 if (ci == NULL) return;
1610 DEBUG(desync, 1, "client: %08x; %02x; %02x; %04x", _date, _date_fract, (int)ci->client_playas, client_id);
1612 FOR_ALL_CLIENT_SOCKETS(cs) {
1613 cs->SendClientInfo(ci);
1616 NetworkAdminClientUpdate(ci);
1619 /** Check if we want to restart the map */
1620 static void NetworkCheckRestartMap()
1622 if (_settings_client.network.restart_game_year != 0 && _cur_year >= _settings_client.network.restart_game_year) {
1623 DEBUG(net, 0, "Auto-restarting map. Year %d reached", _cur_year);
1625 StartNewGameWithoutGUI(GENERATE_NEW_SEED);
1629 /** Check if the server has autoclean_companies activated
1630 * Two things happen:
1631 * 1) If a company is not protected, it is closed after 1 year (for example)
1632 * 2) If a company is protected, protection is disabled after 3 years (for example)
1633 * (and item 1. happens a year later)
1635 static void NetworkAutoCleanCompanies()
1637 const NetworkClientInfo *ci;
1638 const Company *c;
1639 bool clients_in_company[MAX_COMPANIES];
1640 int vehicles_in_company[MAX_COMPANIES];
1642 if (!_settings_client.network.autoclean_companies) return;
1644 memset(clients_in_company, 0, sizeof(clients_in_company));
1646 /* Detect the active companies */
1647 FOR_ALL_CLIENT_INFOS(ci) {
1648 if (Company::IsValidID(ci->client_playas)) clients_in_company[ci->client_playas] = true;
1651 if (!_network_dedicated) {
1652 ci = NetworkClientInfo::GetByClientID(CLIENT_ID_SERVER);
1653 if (Company::IsValidID(ci->client_playas)) clients_in_company[ci->client_playas] = true;
1656 if (_settings_client.network.autoclean_novehicles != 0) {
1657 memset(vehicles_in_company, 0, sizeof(vehicles_in_company));
1659 const Vehicle *v;
1660 FOR_ALL_VEHICLES(v) {
1661 if (!Company::IsValidID(v->owner) || !v->IsPrimaryVehicle()) continue;
1662 vehicles_in_company[v->owner]++;
1666 /* Go through all the companies */
1667 FOR_ALL_COMPANIES(c) {
1668 /* Skip the non-active once */
1669 if (c->is_ai) continue;
1671 if (!clients_in_company[c->index]) {
1672 /* The company is empty for one month more */
1673 _network_company_states[c->index].months_empty++;
1675 /* Is the company empty for autoclean_unprotected-months, and is there no protection? */
1676 if (_settings_client.network.autoclean_unprotected != 0 && _network_company_states[c->index].months_empty > _settings_client.network.autoclean_unprotected && StrEmpty(_network_company_states[c->index].password)) {
1677 /* Shut the company down */
1678 DoCommandP(0, 2 | c->index << 16, CRR_AUTOCLEAN, CMD_COMPANY_CTRL);
1679 IConsolePrintF(CC_DEFAULT, "Auto-cleaned company #%d with no password", c->index + 1);
1681 /* Is the company empty for autoclean_protected-months, and there is a protection? */
1682 if (_settings_client.network.autoclean_protected != 0 && _network_company_states[c->index].months_empty > _settings_client.network.autoclean_protected && !StrEmpty(_network_company_states[c->index].password)) {
1683 /* Unprotect the company */
1684 _network_company_states[c->index].password[0] = '\0';
1685 IConsolePrintF(CC_DEFAULT, "Auto-removed protection from company #%d", c->index + 1);
1686 _network_company_states[c->index].months_empty = 0;
1687 NetworkServerUpdateCompanyPassworded(c->index, false);
1689 /* Is the company empty for autoclean_novehicles-months, and has no vehicles? */
1690 if (_settings_client.network.autoclean_novehicles != 0 && _network_company_states[c->index].months_empty > _settings_client.network.autoclean_novehicles && vehicles_in_company[c->index] == 0) {
1691 /* Shut the company down */
1692 DoCommandP(0, 2 | c->index << 16, CRR_AUTOCLEAN, CMD_COMPANY_CTRL);
1693 IConsolePrintF(CC_DEFAULT, "Auto-cleaned company #%d with no vehicles", c->index + 1);
1695 } else {
1696 /* It is not empty, reset the date */
1697 _network_company_states[c->index].months_empty = 0;
1703 * Check whether a name is unique, and otherwise try to make it unique.
1704 * @param new_name The name to check/modify.
1705 * @param last The last writeable element of the buffer.
1706 * @return True if an unique name was achieved.
1708 bool NetworkFindName(char *new_name, const char *last)
1710 bool found_name = false;
1711 uint number = 0;
1712 char original_name[NETWORK_CLIENT_NAME_LENGTH];
1714 strecpy(original_name, new_name, lastof(original_name));
1716 while (!found_name) {
1717 const NetworkClientInfo *ci;
1719 found_name = true;
1720 FOR_ALL_CLIENT_INFOS(ci) {
1721 if (strcmp(ci->client_name, new_name) == 0) {
1722 /* Name already in use */
1723 found_name = false;
1724 break;
1727 /* Check if it is the same as the server-name */
1728 ci = NetworkClientInfo::GetByClientID(CLIENT_ID_SERVER);
1729 if (ci != NULL) {
1730 if (strcmp(ci->client_name, new_name) == 0) found_name = false; // name already in use
1733 if (!found_name) {
1734 /* Try a new name (<name> #1, <name> #2, and so on) */
1736 /* Something's really wrong when there're more names than clients */
1737 if (number++ > MAX_CLIENTS) break;
1738 seprintf(new_name, last, "%s #%d", original_name, number);
1742 return found_name;
1746 * Change the client name of the given client
1747 * @param client_id the client to change the name of
1748 * @param new_name the new name for the client
1749 * @return true iff the name was changed
1751 bool NetworkServerChangeClientName(ClientID client_id, const char *new_name)
1753 NetworkClientInfo *ci;
1754 /* Check if the name's already in use */
1755 FOR_ALL_CLIENT_INFOS(ci) {
1756 if (strcmp(ci->client_name, new_name) == 0) return false;
1759 ci = NetworkClientInfo::GetByClientID(client_id);
1760 if (ci == NULL) return false;
1762 NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, CC_DEFAULT, true, ci->client_name, new_name);
1764 strecpy(ci->client_name, new_name, lastof(ci->client_name));
1766 NetworkUpdateClientInfo(client_id);
1767 return true;
1771 * Set/Reset a company password on the server end.
1772 * @param company_id ID of the company the password should be changed for.
1773 * @param password The new password.
1774 * @param already_hashed Is the given password already hashed?
1776 void NetworkServerSetCompanyPassword(CompanyID company_id, const char *password, bool already_hashed)
1778 if (!Company::IsValidHumanID(company_id)) return;
1780 if (!already_hashed) {
1781 password = GenerateCompanyPasswordHash(password, _settings_client.network.network_id, _settings_game.game_creation.generation_seed);
1784 strecpy(_network_company_states[company_id].password, password, lastof(_network_company_states[company_id].password));
1785 NetworkServerUpdateCompanyPassworded(company_id, !StrEmpty(_network_company_states[company_id].password));
1789 * Handle the command-queue of a socket.
1790 * @param cs The socket to handle the queue for.
1792 static void NetworkHandleCommandQueue(NetworkClientSocket *cs)
1794 CommandPacket *cp;
1795 while ((cp = cs->outgoing_queue.Pop()) != NULL) {
1796 cs->SendCommand(cp);
1797 free(cp);
1802 * This is called every tick if this is a _network_server
1803 * @param send_frame Whether to send the frame to the clients.
1805 void NetworkServer_Tick(bool send_frame)
1807 NetworkClientSocket *cs;
1808 #ifndef ENABLE_NETWORK_SYNC_EVERY_FRAME
1809 bool send_sync = false;
1810 #endif
1812 #ifndef ENABLE_NETWORK_SYNC_EVERY_FRAME
1813 if (_frame_counter >= _last_sync_frame + _settings_client.network.sync_freq) {
1814 _last_sync_frame = _frame_counter;
1815 send_sync = true;
1817 #endif
1819 /* Now we are done with the frame, inform the clients that they can
1820 * do their frame! */
1821 FOR_ALL_CLIENT_SOCKETS(cs) {
1822 /* We allow a number of bytes per frame, but only to the burst amount
1823 * to be available for packet receiving at any particular time. */
1824 cs->receive_limit = min(cs->receive_limit + _settings_client.network.bytes_per_frame,
1825 _settings_client.network.bytes_per_frame_burst);
1827 /* Check if the speed of the client is what we can expect from a client */
1828 uint lag = NetworkCalculateLag(cs);
1829 switch (cs->status) {
1830 case NetworkClientSocket::STATUS_ACTIVE:
1831 if (lag > _settings_client.network.max_lag_time) {
1832 /* Client did still not report in within the specified limit. */
1833 IConsolePrintF(CC_ERROR, cs->last_packet + lag * MILLISECONDS_PER_TICK > _realtime_tick ?
1834 /* A packet was received in the last three game days, so the client is likely lagging behind. */
1835 "Client #%d is dropped because the client's game state is more than %d ticks behind" :
1836 /* No packet was received in the last three game days; sounds like a lost connection. */
1837 "Client #%d is dropped because the client did not respond for more than %d ticks",
1838 cs->client_id, lag);
1839 cs->SendError(NETWORK_ERROR_TIMEOUT_COMPUTER);
1840 continue;
1843 /* Report once per time we detect the lag, and only when we
1844 * received a packet in the last 2000 milliseconds. If we
1845 * did not receive a packet, then the client is not just
1846 * slow, but the connection is likely severed. Mentioning
1847 * frame_freq is not useful in this case. */
1848 if (lag > (uint)DAY_TICKS && cs->lag_test == 0 && cs->last_packet + 2000 > _realtime_tick) {
1849 IConsolePrintF(CC_WARNING, "[%d] Client #%d is slow, try increasing [network.]frame_freq to a higher value!", _frame_counter, cs->client_id);
1850 cs->lag_test = 1;
1853 if (cs->last_frame_server - cs->last_token_frame >= _settings_client.network.max_lag_time) {
1854 /* This is a bad client! It didn't send the right token back within time. */
1855 IConsolePrintF(CC_ERROR, "Client #%d is dropped because it fails to send valid acks", cs->client_id);
1856 cs->SendError(NETWORK_ERROR_TIMEOUT_COMPUTER);
1857 continue;
1859 break;
1861 case NetworkClientSocket::STATUS_INACTIVE:
1862 case NetworkClientSocket::STATUS_NEWGRFS_CHECK:
1863 case NetworkClientSocket::STATUS_AUTHORIZED:
1864 /* NewGRF check and authorized states should be handled almost instantly.
1865 * So give them some lee-way, likewise for the query with inactive. */
1866 if (lag > _settings_client.network.max_init_time) {
1867 IConsolePrintF(CC_ERROR, "Client #%d is dropped because it took longer than %d ticks to start the joining process", cs->client_id, _settings_client.network.max_init_time);
1868 cs->SendError(NETWORK_ERROR_TIMEOUT_COMPUTER);
1869 continue;
1871 break;
1873 case NetworkClientSocket::STATUS_MAP:
1874 /* Downloading the map... this is the amount of time since starting the saving. */
1875 if (lag > _settings_client.network.max_download_time) {
1876 IConsolePrintF(CC_ERROR, "Client #%d is dropped because it took longer than %d ticks to download the map", cs->client_id, _settings_client.network.max_download_time);
1877 cs->SendError(NETWORK_ERROR_TIMEOUT_MAP);
1878 continue;
1880 break;
1882 case NetworkClientSocket::STATUS_DONE_MAP:
1883 case NetworkClientSocket::STATUS_PRE_ACTIVE:
1884 /* The map has been sent, so this is for loading the map and syncing up. */
1885 if (lag > _settings_client.network.max_join_time) {
1886 IConsolePrintF(CC_ERROR, "Client #%d is dropped because it took longer than %d ticks to join", cs->client_id, _settings_client.network.max_join_time);
1887 cs->SendError(NETWORK_ERROR_TIMEOUT_JOIN);
1888 continue;
1890 break;
1892 case NetworkClientSocket::STATUS_AUTH_GAME:
1893 case NetworkClientSocket::STATUS_AUTH_COMPANY:
1894 /* These don't block? */
1895 if (lag > _settings_client.network.max_password_time) {
1896 IConsolePrintF(CC_ERROR, "Client #%d is dropped because it took longer than %d ticks to enter the password", cs->client_id, _settings_client.network.max_password_time);
1897 cs->SendError(NETWORK_ERROR_TIMEOUT_PASSWORD);
1898 continue;
1900 break;
1902 case NetworkClientSocket::STATUS_MAP_WAIT:
1903 /* This is an internal state where we do not wait
1904 * on the client to move to a different state. */
1905 break;
1907 case NetworkClientSocket::STATUS_END:
1908 /* Bad server/code. */
1909 NOT_REACHED();
1912 if (cs->status >= NetworkClientSocket::STATUS_PRE_ACTIVE) {
1913 /* Check if we can send command, and if we have anything in the queue */
1914 NetworkHandleCommandQueue(cs);
1916 /* Send an updated _frame_counter_max to the client */
1917 if (send_frame) cs->SendFrame();
1919 #ifndef ENABLE_NETWORK_SYNC_EVERY_FRAME
1920 /* Send a sync-check packet */
1921 if (send_sync) cs->SendSync();
1922 #endif
1926 /* See if we need to advertise */
1927 NetworkUDPAdvertise();
1930 /** Yearly "callback". Called whenever the year changes. */
1931 void NetworkServerYearlyLoop()
1933 NetworkCheckRestartMap();
1934 NetworkAdminUpdate(ADMIN_FREQUENCY_ANUALLY);
1937 /** Monthly "callback". Called whenever the month changes. */
1938 void NetworkServerMonthlyLoop()
1940 NetworkAutoCleanCompanies();
1941 NetworkAdminUpdate(ADMIN_FREQUENCY_MONTHLY);
1942 if ((_cur_month % 3) == 0) NetworkAdminUpdate(ADMIN_FREQUENCY_QUARTERLY);
1945 /** Daily "callback". Called whenever the date changes. */
1946 void NetworkServerDailyLoop()
1948 NetworkAdminUpdate(ADMIN_FREQUENCY_DAILY);
1949 if ((_date % 7) == 3) NetworkAdminUpdate(ADMIN_FREQUENCY_WEEKLY);
1953 * Get the IP address/hostname of the connected client.
1954 * @return The IP address.
1956 const char *ServerNetworkGameSocketHandler::GetClientIP()
1958 return this->client_address.GetHostname();
1961 /** Show the status message of all clients on the console. */
1962 void NetworkServerShowStatusToConsole()
1964 static const char * const stat_str[] = {
1965 "inactive",
1966 "checking NewGRFs",
1967 "authorizing (server password)",
1968 "authorizing (company password)",
1969 "authorized",
1970 "waiting",
1971 "loading map",
1972 "map done",
1973 "ready",
1974 "active"
1976 assert_compile(lengthof(stat_str) == NetworkClientSocket::STATUS_END);
1978 NetworkClientSocket *cs;
1979 FOR_ALL_CLIENT_SOCKETS(cs) {
1980 NetworkClientInfo *ci = cs->GetInfo();
1981 if (ci == NULL) continue;
1982 uint lag = NetworkCalculateLag(cs);
1983 const char *status;
1985 status = (cs->status < (ptrdiff_t)lengthof(stat_str) ? stat_str[cs->status] : "unknown");
1986 IConsolePrintF(CC_INFO, "Client #%1d name: '%s' status: '%s' frame-lag: %3d company: %1d IP: %s",
1987 cs->client_id, ci->client_name, status, lag,
1988 ci->client_playas + (Company::IsValidID(ci->client_playas) ? 1 : 0),
1989 cs->GetClientIP());
1994 * Send Config Update
1996 void NetworkServerSendConfigUpdate()
1998 NetworkClientSocket *cs;
2000 FOR_ALL_CLIENT_SOCKETS(cs) {
2001 if (cs->status >= NetworkClientSocket::STATUS_PRE_ACTIVE) cs->SendConfigUpdate();
2006 * Tell that a particular company is (not) passworded.
2007 * @param company_id The company that got/removed the password.
2008 * @param passworded Whether the password was received or removed.
2010 void NetworkServerUpdateCompanyPassworded(CompanyID company_id, bool passworded)
2012 if (NetworkCompanyIsPassworded(company_id) == passworded) return;
2014 SB(_network_company_passworded, company_id, 1, !!passworded);
2015 SetWindowClassesDirty(WC_COMPANY);
2017 NetworkClientSocket *cs;
2018 FOR_ALL_CLIENT_SOCKETS(cs) {
2019 if (cs->status >= NetworkClientSocket::STATUS_PRE_ACTIVE) cs->SendCompanyUpdate();
2022 NetworkAdminCompanyUpdate(Company::GetIfValid(company_id));
2026 * Handle the tid-bits of moving a client from one company to another.
2027 * @param client_id id of the client we want to move.
2028 * @param company_id id of the company we want to move the client to.
2029 * @return void
2031 void NetworkServerDoMove(ClientID client_id, CompanyID company_id)
2033 /* Only allow non-dedicated servers and normal clients to be moved */
2034 if (client_id == CLIENT_ID_SERVER && _network_dedicated) return;
2036 NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(client_id);
2038 /* No need to waste network resources if the client is in the company already! */
2039 if (ci->client_playas == company_id) return;
2041 ci->client_playas = company_id;
2043 if (client_id == CLIENT_ID_SERVER) {
2044 SetLocalCompany(company_id);
2045 } else {
2046 NetworkClientSocket *cs = NetworkClientSocket::GetByClientID(client_id);
2047 /* When the company isn't authorized we can't move them yet. */
2048 if (cs->status < NetworkClientSocket::STATUS_AUTHORIZED) return;
2049 cs->SendMove(client_id, company_id);
2052 /* announce the client's move */
2053 NetworkUpdateClientInfo(client_id);
2055 NetworkAction action = (company_id == COMPANY_SPECTATOR) ? NETWORK_ACTION_COMPANY_SPECTATOR : NETWORK_ACTION_COMPANY_JOIN;
2056 NetworkServerSendChat(action, DESTTYPE_BROADCAST, 0, "", client_id, company_id + 1);
2060 * Send an rcon reply to the client.
2061 * @param client_id The identifier of the client.
2062 * @param colour_code The colour of the text.
2063 * @param string The actual reply.
2065 void NetworkServerSendRcon(ClientID client_id, TextColour colour_code, const char *string)
2067 NetworkClientSocket::GetByClientID(client_id)->SendRConResult(colour_code, string);
2071 * Kick a single client.
2072 * @param client_id The client to kick.
2074 void NetworkServerKickClient(ClientID client_id)
2076 if (client_id == CLIENT_ID_SERVER) return;
2077 NetworkClientSocket::GetByClientID(client_id)->SendError(NETWORK_ERROR_KICKED);
2081 * Ban, or kick, everyone joined from the given client's IP.
2082 * @param client_id The client to check for.
2083 * @param ban Whether to ban or kick.
2085 uint NetworkServerKickOrBanIP(ClientID client_id, bool ban)
2087 return NetworkServerKickOrBanIP(NetworkClientSocket::GetByClientID(client_id)->GetClientIP(), ban);
2091 * Kick or ban someone based on an IP address.
2092 * @param ip The IP address/range to ban/kick.
2093 * @param ban Whether to ban or just kick.
2095 uint NetworkServerKickOrBanIP(const char *ip, bool ban)
2097 /* Add address to ban-list */
2098 if (ban) {
2099 bool contains = false;
2100 for (char **iter = _network_ban_list.Begin(); iter != _network_ban_list.End(); iter++) {
2101 if (strcmp(*iter, ip) == 0) {
2102 contains = true;
2103 break;
2106 if (!contains) *_network_ban_list.Append() = stredup(ip);
2109 uint n = 0;
2111 /* There can be multiple clients with the same IP, kick them all */
2112 NetworkClientSocket *cs;
2113 FOR_ALL_CLIENT_SOCKETS(cs) {
2114 if (cs->client_id == CLIENT_ID_SERVER) continue;
2115 if (cs->client_address.IsInNetmask(const_cast<char *>(ip))) {
2116 NetworkServerKickClient(cs->client_id);
2117 n++;
2121 return n;
2125 * Check whether a particular company has clients.
2126 * @param company The company to check.
2127 * @return True if at least one client is joined to the company.
2129 bool NetworkCompanyHasClients(CompanyID company)
2131 const NetworkClientInfo *ci;
2132 FOR_ALL_CLIENT_INFOS(ci) {
2133 if (ci->client_playas == company) return true;
2135 return false;
2140 * Get the name of the client, if the user did not send it yet, Client #<no> is used.
2141 * @param client_name The variable to write the name to.
2142 * @param last The pointer to the last element of the destination buffer
2144 void ServerNetworkGameSocketHandler::GetClientName(char *client_name, const char *last) const
2146 const NetworkClientInfo *ci = this->GetInfo();
2148 if (ci == NULL || StrEmpty(ci->client_name)) {
2149 seprintf(client_name, last, "Client #%4d", this->client_id);
2150 } else {
2151 strecpy(client_name, ci->client_name, last);
2156 * Print all the clients to the console
2158 void NetworkPrintClients()
2160 NetworkClientInfo *ci;
2161 FOR_ALL_CLIENT_INFOS(ci) {
2162 if (_network_server) {
2163 IConsolePrintF(CC_INFO, "Client #%1d name: '%s' company: %1d IP: %s",
2164 ci->client_id,
2165 ci->client_name,
2166 ci->client_playas + (Company::IsValidID(ci->client_playas) ? 1 : 0),
2167 ci->client_id == CLIENT_ID_SERVER ? "server" : NetworkClientSocket::GetByClientID(ci->client_id)->GetClientIP());
2168 } else {
2169 IConsolePrintF(CC_INFO, "Client #%1d name: '%s' company: %1d",
2170 ci->client_id,
2171 ci->client_name,
2172 ci->client_playas + (Company::IsValidID(ci->client_playas) ? 1 : 0));
2178 * Perform all the server specific administration of a new company.
2179 * @param c The newly created company; can't be NULL.
2180 * @param ci The client information of the client that made the company; can be NULL.
2182 void NetworkServerNewCompany(const Company *c, NetworkClientInfo *ci)
2184 assert(c != NULL);
2186 if (!_network_server) return;
2188 _network_company_states[c->index].months_empty = 0;
2189 _network_company_states[c->index].password[0] = '\0';
2190 NetworkServerUpdateCompanyPassworded(c->index, false);
2192 if (ci != NULL) {
2193 /* ci is NULL when replaying, or for AIs. In neither case there is a client. */
2194 ci->client_playas = c->index;
2195 NetworkUpdateClientInfo(ci->client_id);
2196 NetworkSendCommand(0, 0, 0, CMD_RENAME_PRESIDENT, NULL, ci->client_name, c->index);
2199 /* Announce new company on network. */
2200 NetworkAdminCompanyInfo(c, true);
2202 if (ci != NULL) {
2203 /* ci is NULL when replaying, or for AIs. In neither case there is a client.
2204 We need to send Admin port update here so that they first know about the new company
2205 and then learn about a possibly joining client (see FS#6025) */
2206 NetworkServerSendChat(NETWORK_ACTION_COMPANY_NEW, DESTTYPE_BROADCAST, 0, "", ci->client_id, c->index + 1);
2210 #endif /* ENABLE_NETWORK */