Fix #8316: Make sort industries by production and transported with a cargo filter...
[openttd-github.git] / src / network / network_coordinator.cpp
blob6456ac9e89bbf99b1ebc14a3b7e4ca6e51b5fb33
1 /*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
6 */
8 /** @file network_coordinator.cpp Game Coordinator sending/receiving part of the network protocol. */
10 #include "../stdafx.h"
11 #include "../debug.h"
12 #include "../error.h"
13 #include "../rev.h"
14 #include "../settings_type.h"
15 #include "../strings_func.h"
16 #include "../window_func.h"
17 #include "../window_type.h"
18 #include "network.h"
19 #include "network_coordinator.h"
20 #include "network_gamelist.h"
21 #include "network_gui.h"
22 #include "network_internal.h"
23 #include "network_server.h"
24 #include "network_stun.h"
25 #include "table/strings.h"
27 #include "../safeguards.h"
29 static const auto NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES = std::chrono::seconds(30); ///< How many time between updates the server sends to the Game Coordinator.
30 ClientNetworkCoordinatorSocketHandler _network_coordinator_client; ///< The connection to the Game Coordinator.
31 ConnectionType _network_server_connection_type = CONNECTION_TYPE_UNKNOWN; ///< What type of connection the Game Coordinator detected we are on.
32 std::string _network_server_invite_code = ""; ///< Our invite code as indicated by the Game Coordinator.
34 /** Connect to a game server by IP:port. */
35 class NetworkDirectConnecter : public TCPConnecter {
36 private:
37 std::string token; ///< Token of this connection.
38 uint8 tracking_number; ///< Tracking number of this connection.
40 public:
41 /**
42 * Try to establish a direct (hostname:port based) connection.
43 * @param hostname The hostname of the server.
44 * @param port The port of the server.
45 * @param token The token as given by the Game Coordinator to track this connection attempt.
46 * @param tracking_number The tracking number as given by the Game Coordinator to track this connection attempt.
48 NetworkDirectConnecter(const std::string &hostname, uint16 port, const std::string &token, uint8 tracking_number) : TCPConnecter(hostname, port), token(token), tracking_number(tracking_number) {}
50 void OnFailure() override
52 _network_coordinator_client.ConnectFailure(this->token, this->tracking_number);
55 void OnConnect(SOCKET s) override
57 NetworkAddress address = NetworkAddress::GetPeerAddress(s);
58 _network_coordinator_client.ConnectSuccess(this->token, s, address);
62 /** Connecter used after STUN exchange to connect from both sides to each other. */
63 class NetworkReuseStunConnecter : public TCPConnecter {
64 private:
65 std::string token; ///< Token of this connection.
66 uint8 tracking_number; ///< Tracking number of this connection.
67 uint8 family; ///< Family of this connection.
69 public:
70 /**
71 * Try to establish a STUN-based connection.
72 * @param hostname The hostname of the peer.
73 * @param port The port of the peer.
74 * @param bind_address The local bind address used for this connection.
75 * @param token The connection token.
76 * @param tracking_number The tracking number of the connection.
77 * @param family The family this connection is using.
79 NetworkReuseStunConnecter(const std::string &hostname, uint16 port, const NetworkAddress &bind_address, std::string token, uint8 tracking_number, uint8 family) :
80 TCPConnecter(hostname, port, bind_address),
81 token(token),
82 tracking_number(tracking_number),
83 family(family)
87 void OnFailure() override
89 /* Close the STUN connection too, as it is no longer of use. */
90 _network_coordinator_client.CloseStunHandler(this->token, this->family);
92 _network_coordinator_client.ConnectFailure(this->token, this->tracking_number);
95 void OnConnect(SOCKET s) override
97 NetworkAddress address = NetworkAddress::GetPeerAddress(s);
98 _network_coordinator_client.ConnectSuccess(this->token, s, address);
102 /** Connect to the Game Coordinator server. */
103 class NetworkCoordinatorConnecter : TCPConnecter {
104 public:
106 * Initiate the connecting.
107 * @param connection_string The address of the Game Coordinator server.
109 NetworkCoordinatorConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_COORDINATOR_SERVER_PORT) {}
111 void OnFailure() override
113 _network_coordinator_client.connecting = false;
114 _network_coordinator_client.CloseConnection(true);
117 void OnConnect(SOCKET s) override
119 assert(_network_coordinator_client.sock == INVALID_SOCKET);
121 _network_coordinator_client.sock = s;
122 _network_coordinator_client.last_activity = std::chrono::steady_clock::now();
123 _network_coordinator_client.connecting = false;
127 bool ClientNetworkCoordinatorSocketHandler::Receive_GC_ERROR(Packet *p)
129 NetworkCoordinatorErrorType error = (NetworkCoordinatorErrorType)p->Recv_uint8();
130 std::string detail = p->Recv_string(NETWORK_ERROR_DETAIL_LENGTH);
132 switch (error) {
133 case NETWORK_COORDINATOR_ERROR_UNKNOWN:
134 this->CloseConnection();
135 return false;
137 case NETWORK_COORDINATOR_ERROR_REGISTRATION_FAILED:
138 SetDParamStr(0, detail);
139 ShowErrorMessage(STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED, STR_JUST_RAW_STRING, WL_ERROR);
141 /* To prevent that we constantly try to reconnect, switch to local game. */
142 _settings_client.network.server_game_type = SERVER_GAME_TYPE_LOCAL;
144 this->CloseConnection();
145 return false;
147 case NETWORK_COORDINATOR_ERROR_INVALID_INVITE_CODE: {
148 auto connecter_pre_it = this->connecter_pre.find(detail);
149 if (connecter_pre_it != this->connecter_pre.end()) {
150 connecter_pre_it->second->SetFailure();
151 this->connecter_pre.erase(connecter_pre_it);
154 /* Mark the server as offline. */
155 NetworkGameList *item = NetworkGameListAddItem(detail);
156 item->online = false;
158 UpdateNetworkGameWindow();
159 return true;
162 default:
163 Debug(net, 0, "Invalid error type {} received from Game Coordinator", error);
164 this->CloseConnection();
165 return false;
169 bool ClientNetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet *p)
171 /* Schedule sending an update. */
172 this->next_update = std::chrono::steady_clock::now();
174 _settings_client.network.server_invite_code = p->Recv_string(NETWORK_INVITE_CODE_LENGTH);
175 _settings_client.network.server_invite_code_secret = p->Recv_string(NETWORK_INVITE_CODE_SECRET_LENGTH);
176 _network_server_connection_type = (ConnectionType)p->Recv_uint8();
178 if (_network_server_connection_type == CONNECTION_TYPE_ISOLATED) {
179 ShowErrorMessage(STR_NETWORK_ERROR_COORDINATOR_ISOLATED, STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL, WL_ERROR);
182 /* Users can change the invite code in the settings, but this has no effect
183 * on the invite code as assigned by the server. So
184 * _network_server_invite_code contains the current invite code,
185 * and _settings_client.network.server_invite_code contains the one we will
186 * attempt to re-use when registering again. */
187 _network_server_invite_code = _settings_client.network.server_invite_code;
189 SetWindowDirty(WC_CLIENT_LIST, 0);
191 if (_network_dedicated) {
192 std::string connection_type;
193 switch (_network_server_connection_type) {
194 case CONNECTION_TYPE_ISOLATED: connection_type = "Remote players can't connect"; break;
195 case CONNECTION_TYPE_DIRECT: connection_type = "Public"; break;
196 case CONNECTION_TYPE_STUN: connection_type = "Behind NAT"; break;
197 case CONNECTION_TYPE_TURN: connection_type = "Via relay"; break;
199 case CONNECTION_TYPE_UNKNOWN: // Never returned from Game Coordinator.
200 default: connection_type = "Unknown"; break; // Should never happen, but don't fail if it does.
203 std::string game_type;
204 switch (_settings_client.network.server_game_type) {
205 case SERVER_GAME_TYPE_INVITE_ONLY: game_type = "Invite only"; break;
206 case SERVER_GAME_TYPE_PUBLIC: game_type = "Public"; break;
208 case SERVER_GAME_TYPE_LOCAL: // Impossible to register local servers.
209 default: game_type = "Unknown"; break; // Should never happen, but don't fail if it does.
212 Debug(net, 3, "----------------------------------------");
213 Debug(net, 3, "Your server is now registered with the Game Coordinator:");
214 Debug(net, 3, " Game type: {}", game_type);
215 Debug(net, 3, " Connection type: {}", connection_type);
216 Debug(net, 3, " Invite code: {}", _network_server_invite_code);
217 Debug(net, 3, "----------------------------------------");
218 } else {
219 Debug(net, 3, "Game Coordinator registered our server with invite code '{}'", _network_server_invite_code);
222 return true;
225 bool ClientNetworkCoordinatorSocketHandler::Receive_GC_LISTING(Packet *p)
227 uint8 servers = p->Recv_uint16();
229 /* End of list; we can now remove all expired items from the list. */
230 if (servers == 0) {
231 NetworkGameListRemoveExpired();
232 return true;
235 for (; servers > 0; servers--) {
236 std::string connection_string = p->Recv_string(NETWORK_HOSTNAME_PORT_LENGTH);
238 /* Read the NetworkGameInfo from the packet. */
239 NetworkGameInfo ngi = {};
240 DeserializeNetworkGameInfo(p, &ngi, &this->newgrf_lookup_table);
242 /* Now we know the connection string, we can add it to our list. */
243 NetworkGameList *item = NetworkGameListAddItem(connection_string);
245 /* Clear any existing GRFConfig chain. */
246 ClearGRFConfigList(&item->info.grfconfig);
247 /* Copy the new NetworkGameInfo info. */
248 item->info = ngi;
249 /* Check for compatability with the client. */
250 CheckGameCompatibility(item->info);
251 /* Mark server as online. */
252 item->online = true;
253 /* Mark the item as up-to-date. */
254 item->version = _network_game_list_version;
257 UpdateNetworkGameWindow();
258 return true;
261 bool ClientNetworkCoordinatorSocketHandler::Receive_GC_CONNECTING(Packet *p)
263 std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH);
264 std::string invite_code = p->Recv_string(NETWORK_INVITE_CODE_LENGTH);
266 /* Find the connecter based on the invite code. */
267 auto connecter_pre_it = this->connecter_pre.find(invite_code);
268 if (connecter_pre_it == this->connecter_pre.end()) {
269 this->CloseConnection();
270 return false;
273 /* Now store it based on the token. */
274 this->connecter[token] = connecter_pre_it->second;
275 this->connecter_pre.erase(connecter_pre_it);
277 return true;
280 bool ClientNetworkCoordinatorSocketHandler::Receive_GC_CONNECT_FAILED(Packet *p)
282 std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH);
283 this->CloseToken(token);
285 return true;
288 bool ClientNetworkCoordinatorSocketHandler::Receive_GC_DIRECT_CONNECT(Packet *p)
290 std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH);
291 uint8 tracking_number = p->Recv_uint8();
292 std::string hostname = p->Recv_string(NETWORK_HOSTNAME_LENGTH);
293 uint16 port = p->Recv_uint16();
295 /* Ensure all other pending connection attempts are killed. */
296 if (this->game_connecter != nullptr) {
297 this->game_connecter->Kill();
298 this->game_connecter = nullptr;
301 this->game_connecter = new NetworkDirectConnecter(hostname, port, token, tracking_number);
302 return true;
305 bool ClientNetworkCoordinatorSocketHandler::Receive_GC_STUN_REQUEST(Packet *p)
307 std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH);
309 this->stun_handlers[token][AF_INET6] = ClientNetworkStunSocketHandler::Stun(token, AF_INET6);
310 this->stun_handlers[token][AF_INET] = ClientNetworkStunSocketHandler::Stun(token, AF_INET);
311 return true;
314 bool ClientNetworkCoordinatorSocketHandler::Receive_GC_STUN_CONNECT(Packet *p)
316 std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH);
317 uint8 tracking_number = p->Recv_uint8();
318 uint8 family = p->Recv_uint8();
319 std::string host = p->Recv_string(NETWORK_HOSTNAME_PORT_LENGTH);
320 uint16 port = p->Recv_uint16();
322 /* Check if we know this token. */
323 auto stun_it = this->stun_handlers.find(token);
324 if (stun_it == this->stun_handlers.end()) return true;
325 auto family_it = stun_it->second.find(family);
326 if (family_it == stun_it->second.end()) return true;
328 /* Ensure all other pending connection attempts are killed. */
329 if (this->game_connecter != nullptr) {
330 this->game_connecter->Kill();
331 this->game_connecter = nullptr;
334 /* We now mark the connection as closed, but we do not really close the
335 * socket yet. We do this when the NetworkReuseStunConnecter is connected.
336 * This prevents any NAT to already remove the route while we create the
337 * second connection on top of the first. */
338 family_it->second->CloseConnection(false);
340 /* Connect to our peer from the same local address as we use for the
341 * STUN server. This means that if there is any NAT in the local network,
342 * the public ip:port is still pointing to the local address, and as such
343 * a connection can be established. */
344 this->game_connecter = new NetworkReuseStunConnecter(host, port, family_it->second->local_addr, token, tracking_number, family);
345 return true;
348 bool ClientNetworkCoordinatorSocketHandler::Receive_GC_NEWGRF_LOOKUP(Packet *p)
350 this->newgrf_lookup_table_cursor = p->Recv_uint32();
352 uint16 newgrfs = p->Recv_uint16();
353 for (; newgrfs> 0; newgrfs--) {
354 uint32 index = p->Recv_uint32();
355 DeserializeGRFIdentifierWithName(p, &this->newgrf_lookup_table[index]);
357 return true;
360 bool ClientNetworkCoordinatorSocketHandler::Receive_GC_TURN_CONNECT(Packet *p)
362 std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH);
363 uint8 tracking_number = p->Recv_uint8();
364 std::string ticket = p->Recv_string(NETWORK_TOKEN_LENGTH);
365 std::string connection_string = p->Recv_string(NETWORK_HOSTNAME_PORT_LENGTH);
367 /* Ensure all other pending connection attempts are killed. */
368 if (this->game_connecter != nullptr) {
369 this->game_connecter->Kill();
370 this->game_connecter = nullptr;
373 this->turn_handlers[token] = ClientNetworkTurnSocketHandler::Turn(token, tracking_number, ticket, connection_string);
375 if (!_network_server) {
376 switch (_settings_client.network.use_relay_service) {
377 case URS_NEVER:
378 this->ConnectFailure(token, 0);
379 break;
381 case URS_ASK:
382 ShowNetworkAskRelay(connection_string, token);
383 break;
385 case URS_ALLOW:
386 this->StartTurnConnection(token);
387 break;
389 } else {
390 this->StartTurnConnection(token);
393 return true;
396 void ClientNetworkCoordinatorSocketHandler::StartTurnConnection(std::string &token)
398 auto turn_it = this->turn_handlers.find(token);
399 if (turn_it == this->turn_handlers.end()) return;
401 turn_it->second->Connect();
404 void ClientNetworkCoordinatorSocketHandler::Connect()
406 /* We are either already connected or are trying to connect. */
407 if (this->sock != INVALID_SOCKET || this->connecting) return;
409 this->Reopen();
411 this->connecting = true;
412 this->last_activity = std::chrono::steady_clock::now();
414 new NetworkCoordinatorConnecter(NetworkCoordinatorConnectionString());
417 NetworkRecvStatus ClientNetworkCoordinatorSocketHandler::CloseConnection(bool error)
419 NetworkCoordinatorSocketHandler::CloseConnection(error);
421 this->CloseSocket();
422 this->connecting = false;
424 _network_server_connection_type = CONNECTION_TYPE_UNKNOWN;
425 this->next_update = {};
427 this->CloseAllConnections();
429 SetWindowDirty(WC_CLIENT_LIST, 0);
431 return NETWORK_RECV_STATUS_OKAY;
435 * Register our server to receive our invite code.
437 void ClientNetworkCoordinatorSocketHandler::Register()
439 _network_server_connection_type = CONNECTION_TYPE_UNKNOWN;
440 this->next_update = {};
442 SetWindowDirty(WC_CLIENT_LIST, 0);
444 this->Connect();
446 Packet *p = new Packet(PACKET_COORDINATOR_SERVER_REGISTER);
447 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
448 p->Send_uint8(_settings_client.network.server_game_type);
449 p->Send_uint16(_settings_client.network.server_port);
450 if (_settings_client.network.server_invite_code.empty() || _settings_client.network.server_invite_code_secret.empty()) {
451 p->Send_string("");
452 p->Send_string("");
453 } else {
454 p->Send_string(_settings_client.network.server_invite_code);
455 p->Send_string(_settings_client.network.server_invite_code_secret);
458 this->SendPacket(p);
462 * Send an update of our server status to the Game Coordinator.
464 void ClientNetworkCoordinatorSocketHandler::SendServerUpdate()
466 Debug(net, 6, "Sending server update to Game Coordinator");
468 Packet *p = new Packet(PACKET_COORDINATOR_SERVER_UPDATE, TCP_MTU);
469 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
470 SerializeNetworkGameInfo(p, GetCurrentNetworkServerGameInfo(), this->next_update.time_since_epoch() != std::chrono::nanoseconds::zero());
472 this->SendPacket(p);
474 this->next_update = std::chrono::steady_clock::now() + NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES;
478 * Request a listing of all public servers.
480 void ClientNetworkCoordinatorSocketHandler::GetListing()
482 this->Connect();
484 _network_game_list_version++;
486 Packet *p = new Packet(PACKET_COORDINATOR_CLIENT_LISTING);
487 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
488 p->Send_uint8(NETWORK_GAME_INFO_VERSION);
489 p->Send_string(_openttd_revision);
490 p->Send_uint32(this->newgrf_lookup_table_cursor);
492 this->SendPacket(p);
496 * Join a server based on an invite code.
497 * @param invite_code The invite code of the server to connect to.
498 * @param connecter The connecter of the request.
500 void ClientNetworkCoordinatorSocketHandler::ConnectToServer(const std::string &invite_code, TCPServerConnecter *connecter)
502 assert(StrStartsWith(invite_code, "+"));
504 if (this->connecter_pre.find(invite_code) != this->connecter_pre.end()) {
505 /* If someone is hammering the refresh key, one can sent out two
506 * requests for the same invite code. There isn't really a great way
507 * of handling this, so just ignore this request. */
508 connecter->SetFailure();
509 return;
512 /* Initially we store based on invite code; on first reply we know the
513 * token, and will start using that key instead. */
514 this->connecter_pre[invite_code] = connecter;
516 this->Connect();
518 Packet *p = new Packet(PACKET_COORDINATOR_CLIENT_CONNECT);
519 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
520 p->Send_string(invite_code);
522 this->SendPacket(p);
526 * Callback from a Connecter to let the Game Coordinator know the connection failed.
527 * @param token Token of the connecter that failed.
528 * @param tracking_number Tracking number of the connecter that failed.
530 void ClientNetworkCoordinatorSocketHandler::ConnectFailure(const std::string &token, uint8 tracking_number)
532 /* Connecter will destroy itself. */
533 this->game_connecter = nullptr;
535 Packet *p = new Packet(PACKET_COORDINATOR_SERCLI_CONNECT_FAILED);
536 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
537 p->Send_string(token);
538 p->Send_uint8(tracking_number);
540 this->SendPacket(p);
542 /* We do not close the associated connecter here yet, as the
543 * Game Coordinator might have other methods of connecting available. */
547 * Callback from a Connecter to let the Game Coordinator know the connection
548 * to the game server is established.
549 * @param token Token of the connecter that succeeded.
550 * @param sock The socket that the connecter can now use.
552 void ClientNetworkCoordinatorSocketHandler::ConnectSuccess(const std::string &token, SOCKET sock, NetworkAddress &address)
554 /* Connecter will destroy itself. */
555 this->game_connecter = nullptr;
557 if (_network_server) {
558 if (!ServerNetworkGameSocketHandler::ValidateClient(sock, address)) return;
559 Debug(net, 3, "[{}] Client connected from {} on frame {}", ServerNetworkGameSocketHandler::GetName(), address.GetHostname(), _frame_counter);
560 ServerNetworkGameSocketHandler::AcceptConnection(sock, address);
561 } else {
562 /* The client informs the Game Coordinator about the success. The server
563 * doesn't have to, as it is implied by the client telling. */
564 Packet *p = new Packet(PACKET_COORDINATOR_CLIENT_CONNECTED);
565 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
566 p->Send_string(token);
567 this->SendPacket(p);
569 /* Find the connecter; it can happen it no longer exist, in cases where
570 * we aborted the connect but the Game Coordinator was already in the
571 * processes of connecting us. */
572 auto connecter_it = this->connecter.find(token);
573 if (connecter_it != this->connecter.end()) {
574 connecter_it->second->SetConnected(sock);
575 this->connecter.erase(connecter_it);
579 /* Close all remaining connections. */
580 this->CloseToken(token);
584 * Callback from the STUN connecter to inform the Game Coordinator about the
585 * result of the STUN.
587 * This helps the Game Coordinator not to wait for a timeout on its end, but
588 * rather react as soon as the client/server knows the result.
590 void ClientNetworkCoordinatorSocketHandler::StunResult(const std::string &token, uint8 family, bool result)
592 Packet *p = new Packet(PACKET_COORDINATOR_SERCLI_STUN_RESULT);
593 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
594 p->Send_string(token);
595 p->Send_uint8(family);
596 p->Send_bool(result);
597 this->SendPacket(p);
601 * Close the STUN handler.
602 * @param token The token used for the STUN handlers.
603 * @param family The family of STUN handlers to close. AF_UNSPEC to close all STUN handlers for this token.
605 void ClientNetworkCoordinatorSocketHandler::CloseStunHandler(const std::string &token, uint8 family)
607 auto stun_it = this->stun_handlers.find(token);
608 if (stun_it == this->stun_handlers.end()) return;
610 if (family == AF_UNSPEC) {
611 for (auto &[family, stun_handler] : stun_it->second) {
612 stun_handler->CloseConnection();
613 stun_handler->CloseSocket();
616 this->stun_handlers.erase(stun_it);
617 } else {
618 auto family_it = stun_it->second.find(family);
619 if (family_it == stun_it->second.end()) return;
621 family_it->second->CloseConnection();
622 family_it->second->CloseSocket();
624 stun_it->second.erase(family_it);
629 * Close the TURN handler.
630 * @param token The token used for the TURN handler.
632 void ClientNetworkCoordinatorSocketHandler::CloseTurnHandler(const std::string &token)
634 CloseWindowByClass(WC_NETWORK_ASK_RELAY);
636 auto turn_it = this->turn_handlers.find(token);
637 if (turn_it == this->turn_handlers.end()) return;
639 turn_it->second->CloseConnection();
640 turn_it->second->CloseSocket();
642 /* We don't remove turn_handler here, as we can be called from within that
643 * turn_handler instance, so our object cannot be free'd yet. Instead, we
644 * check later if the connection is closed, and free the object then. */
648 * Close everything related to this connection token.
649 * @param token The connection token to close.
651 void ClientNetworkCoordinatorSocketHandler::CloseToken(const std::string &token)
653 /* Close all remaining STUN / TURN connections. */
654 this->CloseStunHandler(token);
655 this->CloseTurnHandler(token);
657 /* Close the caller of the connection attempt. */
658 auto connecter_it = this->connecter.find(token);
659 if (connecter_it != this->connecter.end()) {
660 connecter_it->second->SetFailure();
661 this->connecter.erase(connecter_it);
666 * Close all pending connection tokens.
668 void ClientNetworkCoordinatorSocketHandler::CloseAllConnections()
670 /* Ensure all other pending connection attempts are also killed. */
671 if (this->game_connecter != nullptr) {
672 this->game_connecter->Kill();
673 this->game_connecter = nullptr;
676 /* Mark any pending connecters as failed. */
677 for (auto &[token, it] : this->connecter) {
678 this->CloseStunHandler(token);
679 this->CloseTurnHandler(token);
680 it->SetFailure();
682 /* Inform the Game Coordinator he can stop trying to connect us to the server. */
683 this->ConnectFailure(token, 0);
685 this->stun_handlers.clear();
686 this->turn_handlers.clear();
687 this->connecter.clear();
689 /* Also close any pending invite-code requests. */
690 for (auto &[invite_code, it] : this->connecter_pre) {
691 it->SetFailure();
693 this->connecter_pre.clear();
697 * Check whether we received/can send some data from/to the Game Coordinator server and
698 * when that's the case handle it appropriately.
700 void ClientNetworkCoordinatorSocketHandler::SendReceive()
702 /* Private games are not listed via the Game Coordinator. */
703 if (_network_server && _settings_client.network.server_game_type == SERVER_GAME_TYPE_LOCAL) {
704 if (this->sock != INVALID_SOCKET) {
705 this->CloseConnection();
707 return;
710 static int last_attempt_backoff = 1;
711 static bool first_reconnect = true;
713 if (this->sock == INVALID_SOCKET) {
714 static std::chrono::steady_clock::time_point last_attempt = {};
716 /* Don't auto-reconnect when we are not a server. */
717 if (!_network_server) return;
718 /* Don't reconnect if we are connecting. */
719 if (this->connecting) return;
720 /* Throttle how often we try to reconnect. */
721 if (std::chrono::steady_clock::now() < last_attempt + std::chrono::seconds(1) * last_attempt_backoff) return;
723 last_attempt = std::chrono::steady_clock::now();
724 /* Delay reconnecting with up to 32 seconds. */
725 if (last_attempt_backoff < 32) {
726 last_attempt_backoff *= 2;
729 /* Do not reconnect on the first attempt, but only initialize the
730 * last_attempt variables. Otherwise after an outage all servers
731 * reconnect at the same time, potentially overwhelming the
732 * Game Coordinator. */
733 if (first_reconnect) {
734 first_reconnect = false;
735 return;
738 Debug(net, 1, "Connection with Game Coordinator lost; reconnecting...");
739 this->Register();
740 return;
743 last_attempt_backoff = 1;
744 first_reconnect = true;
746 if (_network_server && _network_server_connection_type != CONNECTION_TYPE_UNKNOWN && std::chrono::steady_clock::now() > this->next_update) {
747 this->SendServerUpdate();
750 if (!_network_server && std::chrono::steady_clock::now() > this->last_activity + IDLE_TIMEOUT) {
751 this->CloseConnection();
752 return;
755 if (this->CanSendReceive()) {
756 if (this->ReceivePackets()) {
757 this->last_activity = std::chrono::steady_clock::now();
761 this->SendPackets();
763 for (const auto &[token, families] : this->stun_handlers) {
764 for (const auto &[family, stun_handler] : families) {
765 stun_handler->SendReceive();
769 /* Check for handlers that are not connecting nor connected. Destroy those objects. */
770 for (auto turn_it = this->turn_handlers.begin(); turn_it != this->turn_handlers.end(); /* nothing */) {
771 if (turn_it->second->connect_started && turn_it->second->connecter == nullptr && !turn_it->second->IsConnected()) {
772 turn_it = this->turn_handlers.erase(turn_it);
773 } else {
774 turn_it++;
778 for (const auto &[token, turn_handler] : this->turn_handlers) {
779 turn_handler->SendReceive();