OpenTTD Source 20260421-master-gc2fbc6fdeb
network_coordinator.cpp
Go to the documentation of this file.
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
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"
26
27#include "../safeguards.h"
28
29static const auto NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES = std::chrono::seconds(30);
33
35class NetworkDirectConnecter : public TCPConnecter {
36private:
37 std::string token;
39
40public:
48 NetworkDirectConnecter(std::string_view hostname, uint16_t port, std::string &&token, uint8_t tracking_number) : TCPConnecter(hostname, port), token(std::move(token)), tracking_number(tracking_number) {}
49
50 void OnFailure() override
51 {
52 _network_coordinator_client.ConnectFailure(this->token, this->tracking_number);
53 }
54
55 void OnConnect(SOCKET s) override
56 {
58 _network_coordinator_client.ConnectSuccess(this->token, s, address);
59 }
60};
61
63class NetworkReuseStunConnecter : public TCPConnecter {
64private:
65 std::string token;
67 uint8_t family;
68
69public:
79 NetworkReuseStunConnecter(std::string_view hostname, uint16_t port, const NetworkAddress &bind_address, std::string &&token, uint8_t tracking_number, uint8_t family) :
80 TCPConnecter(hostname, port, bind_address),
81 token(std::move(token)),
84 {
85 }
86
87 void OnFailure() override
88 {
89 /* Close the STUN connection too, as it is no longer of use. */
90 _network_coordinator_client.CloseStunHandler(this->token, this->family);
91
92 _network_coordinator_client.ConnectFailure(this->token, this->tracking_number);
93 }
94
95 void OnConnect(SOCKET s) override
96 {
98 _network_coordinator_client.ConnectSuccess(this->token, s, address);
99 }
100};
101
103class NetworkCoordinatorConnecter : public TCPConnecter {
104public:
110
111 void OnFailure() override
112 {
113 _network_coordinator_client.connecting = false;
114 _network_coordinator_client.CloseConnection(true);
115 }
116
117 void OnConnect(SOCKET s) override
118 {
119 assert(_network_coordinator_client.sock == INVALID_SOCKET);
120
122 _network_coordinator_client.last_activity = std::chrono::steady_clock::now();
123 _network_coordinator_client.connecting = false;
124 }
125};
126
128{
130 std::string detail = p.Recv_string(NETWORK_ERROR_DETAIL_LENGTH);
131 Debug(net, 9, "Coordinator::ReceiveGameCoordinatorError({}, {})", error, detail);
132
133 switch (error) {
135 this->CloseConnection();
136 return false;
137
139 ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED), {}, WL_ERROR);
140
141 /* To prevent that we constantly try to reconnect, switch to local game. */
142 _settings_client.network.server_game_type = ServerGameType::Local;
143
144 this->CloseConnection();
145 return false;
146
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);
152 }
153
154 /* Mark the server as offline. */
155 NetworkGame *item = NetworkGameListAddItem(detail);
156 item->status = NGLS_OFFLINE;
157
159 return true;
160 }
161
163 ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_COORDINATOR_REUSE_OF_INVITE_CODE), {}, WL_ERROR);
164
165 /* To prevent that we constantly battle for the same invite-code, switch to local game. */
166 _settings_client.network.server_game_type = ServerGameType::Local;
167
168 this->CloseConnection();
169 return false;
170
171 default:
172 Debug(net, 0, "Invalid error type {} received from Game Coordinator", error);
173 this->CloseConnection();
174 return false;
175 }
176}
177
179{
180 /* Schedule sending an update. */
181 this->next_update = std::chrono::steady_clock::now();
182
183 _settings_client.network.server_invite_code = p.Recv_string(NETWORK_INVITE_CODE_LENGTH);
184 _settings_client.network.server_invite_code_secret = p.Recv_string(NETWORK_INVITE_CODE_SECRET_LENGTH);
186 Debug(net, 9, "Coordinator::ReceiveGameCoordinatorRegisterAck({}, {})", _settings_client.network.server_invite_code, _network_server_connection_type);
187
190 GetEncodedString(STR_NETWORK_ERROR_COORDINATOR_ISOLATED),
191 GetEncodedString(STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL),
192 WL_ERROR);
193 }
194
195 /* Users can change the invite code in the settings, but this has no effect
196 * on the invite code as assigned by the server. So
197 * _network_server_invite_code contains the current invite code,
198 * and _settings_client.network.server_invite_code contains the one we will
199 * attempt to re-use when registering again. */
200 _network_server_invite_code = _settings_client.network.server_invite_code;
201
203
204 if (_network_dedicated) {
205 std::string connection_type;
207 case CONNECTION_TYPE_ISOLATED: connection_type = "Remote players can't connect"; break;
208 case CONNECTION_TYPE_DIRECT: connection_type = "Public"; break;
209 case CONNECTION_TYPE_STUN: connection_type = "Behind NAT"; break;
210 case CONNECTION_TYPE_TURN: connection_type = "Via relay"; break;
211
212 case CONNECTION_TYPE_UNKNOWN: // Never returned from Game Coordinator.
213 default: connection_type = "Unknown"; break; // Should never happen, but don't fail if it does.
214 }
215
216 std::string game_type;
217 switch (_settings_client.network.server_game_type) {
218 case ServerGameType::InviteOnly: game_type = "Invite only"; break;
219 case ServerGameType::Public: game_type = "Public"; break;
220
221 case ServerGameType::Local: // Impossible to register local servers.
222 default: game_type = "Unknown"; break; // Should never happen, but don't fail if it does.
223 }
224
225 Debug(net, 3, "----------------------------------------");
226 Debug(net, 3, "Your server is now registered with the Game Coordinator:");
227 Debug(net, 3, " Game type: {}", game_type);
228 Debug(net, 3, " Connection type: {}", connection_type);
229 Debug(net, 3, " Invite code: {}", _network_server_invite_code);
230 Debug(net, 3, "----------------------------------------");
231 } else {
232 Debug(net, 3, "Game Coordinator registered our server with invite code '{}'", _network_server_invite_code);
233 }
234
235 return true;
236}
237
239{
240 uint8_t servers = p.Recv_uint16();
241 Debug(net, 9, "Coordinator::ReceiveGameCoordinatorListing({})", servers);
242
243 /* End of list; we can now remove all expired items from the list. */
244 if (servers == 0) {
246 return true;
247 }
248
249 for (; servers > 0; servers--) {
250 std::string connection_string = p.Recv_string(NETWORK_HOSTNAME_PORT_LENGTH);
251
252 /* Now we know the connection string, we can add it to our list. */
253 NetworkGame *item = NetworkGameListAddItem(connection_string);
254
255 /* Clear any existing GRFConfig chain. */
257 /* Read the NetworkGameInfo from the packet. */
258 DeserializeNetworkGameInfo(p, item->info, &this->newgrf_lookup_table);
259 /* Check for compatibility with the client. */
261 /* Mark server as online. */
262 item->status = NGLS_ONLINE;
263 /* Mark the item as up-to-date. */
265 }
266
268 return true;
269}
270
272{
273 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
274 std::string invite_code = p.Recv_string(NETWORK_INVITE_CODE_LENGTH);
275 Debug(net, 9, "Coordinator::ReceiveGameCoordinatorConnecting({}, {})", token, invite_code);
276
277 /* Find the connecter based on the invite code. */
278 auto connecter_pre_it = this->connecter_pre.find(invite_code);
279 if (connecter_pre_it == this->connecter_pre.end()) {
280 this->CloseConnection();
281 return false;
282 }
283
284 /* Now store it based on the token. */
285 this->connecter[token] = {invite_code, connecter_pre_it->second};
286 this->connecter_pre.erase(connecter_pre_it);
287
288 return true;
289}
290
292{
293 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
294 Debug(net, 9, "Coordinator::ReceiveGameCoordinatorConnectFailed({})", token);
295 this->CloseToken(token);
296
297 return true;
298}
299
301{
302 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
303 uint8_t tracking_number = p.Recv_uint8();
304 std::string hostname = p.Recv_string(NETWORK_HOSTNAME_LENGTH);
305 uint16_t port = p.Recv_uint16();
306 Debug(net, 9, "Coordinator::ReceiveGameCoordinatorDirectConnect({}, {}, {}, {})", token, tracking_number, hostname, port);
307
308 /* Ensure all other pending connection attempts are killed. */
309 if (this->game_connecter != nullptr) {
310 this->game_connecter->Kill();
311 this->game_connecter = nullptr;
312 }
313
314 this->game_connecter = TCPConnecter::Create<NetworkDirectConnecter>(hostname, port, std::move(token), tracking_number);
315 return true;
316}
317
319{
320 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
321 Debug(net, 9, "Coordinator::ReceiveGameCoordinatorStunRequest({})", token);
322
323 this->stun_handlers[token][AF_INET6] = ClientNetworkStunSocketHandler::Stun(token, AF_INET6);
324 this->stun_handlers[token][AF_INET] = ClientNetworkStunSocketHandler::Stun(token, AF_INET);
325 return true;
326}
327
329{
330 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
331 uint8_t tracking_number = p.Recv_uint8();
332 uint8_t family = p.Recv_uint8();
333 std::string host = p.Recv_string(NETWORK_HOSTNAME_PORT_LENGTH);
334 uint16_t port = p.Recv_uint16();
335
336 /* Check if we know this token. */
337 auto stun_it = this->stun_handlers.find(token);
338 if (stun_it == this->stun_handlers.end()) return true;
339 auto family_it = stun_it->second.find(family);
340 if (family_it == stun_it->second.end()) return true;
341
342 /* Ensure all other pending connection attempts are killed. */
343 if (this->game_connecter != nullptr) {
344 this->game_connecter->Kill();
345 this->game_connecter = nullptr;
346 }
347
348 /* We now mark the connection as closed, but we do not really close the
349 * socket yet. We do this when the NetworkReuseStunConnecter is connected.
350 * This prevents any NAT to already remove the route while we create the
351 * second connection on top of the first. */
352 family_it->second->CloseConnection(false);
353
354 /* Connect to our peer from the same local address as we use for the
355 * STUN server. This means that if there is any NAT in the local network,
356 * the public ip:port is still pointing to the local address, and as such
357 * a connection can be established. */
358 this->game_connecter = TCPConnecter::Create<NetworkReuseStunConnecter>(host, port, family_it->second->local_addr, std::move(token), tracking_number, family);
359 return true;
360}
361
363{
365
366 uint16_t newgrfs = p.Recv_uint16();
367 Debug(net, 9, "Coordinator::ReceiveGameCoordinatorNewGRFLookup({}, {})", this->newgrf_lookup_table_cursor, newgrfs);
368 for (; newgrfs> 0; newgrfs--) {
369 uint32_t index = p.Recv_uint32();
371 }
372 return true;
373}
374
376{
377 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
378 uint8_t tracking_number = p.Recv_uint8();
379 std::string ticket = p.Recv_string(NETWORK_TOKEN_LENGTH);
380 std::string connection_string = p.Recv_string(NETWORK_HOSTNAME_PORT_LENGTH);
381 Debug(net, 9, "Coordinator::ReceiveGameCoordinatorTurnConnect({}, {}, {}, {})", token, tracking_number, ticket, connection_string);
382
383 /* Ensure all other pending connection attempts are killed. */
384 if (this->game_connecter != nullptr) {
385 this->game_connecter->Kill();
386 this->game_connecter = nullptr;
387 }
388
389 this->turn_handlers[token] = ClientNetworkTurnSocketHandler::Turn(token, tracking_number, ticket, connection_string);
390
391 if (!_network_server) {
392 auto connecter_it = this->connecter.find(token);
393 if (connecter_it == this->connecter.end()) {
394 /* Make sure we are still interested in connecting to this server. */
395 this->ConnectFailure(token, 0);
396 return true;
397 }
398
399 switch (_settings_client.network.use_relay_service) {
400 case UseRelayService::Never:
401 this->ConnectFailure(token, 0);
402 break;
403
404 case UseRelayService::Ask:
405 ShowNetworkAskRelay(connecter_it->second.first, std::move(connection_string), std::move(token));
406 break;
407
408 case UseRelayService::Allow:
409 this->StartTurnConnection(token);
410 break;
411 }
412 } else {
413 this->StartTurnConnection(token);
414 }
415
416 return true;
417}
418
424{
425 auto turn_it = this->turn_handlers.find(token);
426 if (turn_it == this->turn_handlers.end()) return;
427
428 turn_it->second->Connect();
429}
430
433{
434 /* We are either already connected or are trying to connect. */
435 if (this->sock != INVALID_SOCKET || this->connecting) return;
436
437 this->Reopen();
438
439 this->connecting = true;
440 this->last_activity = std::chrono::steady_clock::now();
441
443}
444
461
466{
468 this->next_update = {};
469
471
472 this->Connect();
473
474 auto p = std::make_unique<Packet>(this, PacketCoordinatorType::ServerRegister);
475 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
476 p->Send_uint8(to_underlying(_settings_client.network.server_game_type));
477 p->Send_uint16(_settings_client.network.server_port);
478 if (_settings_client.network.server_invite_code.empty() || _settings_client.network.server_invite_code_secret.empty()) {
479 p->Send_string("");
480 p->Send_string("");
481 } else {
482 p->Send_string(_settings_client.network.server_invite_code);
483 p->Send_string(_settings_client.network.server_invite_code_secret);
484 }
485
486 Debug(net, 9, "Coordinator::SendRegister({}, {}, {})", _settings_client.network.server_game_type, _settings_client.network.server_port, _settings_client.network.server_invite_code);
487 this->SendPacket(std::move(p));
488}
489
494{
495 Debug(net, 6, "Sending server update to Game Coordinator");
496
497 auto p = std::make_unique<Packet>(this, PacketCoordinatorType::ServerUpdate, TCP_MTU);
498 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
499 SerializeNetworkGameInfo(*p, GetCurrentNetworkServerGameInfo(), this->next_update.time_since_epoch() != std::chrono::nanoseconds::zero());
500
501 Debug(net, 9, "Coordinator::SendServerUpdate()");
502 this->SendPacket(std::move(p));
503
504 this->next_update = std::chrono::steady_clock::now() + NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES;
505}
506
511{
512 this->Connect();
513
515
516 auto p = std::make_unique<Packet>(this, PacketCoordinatorType::ClientListing);
517 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
518 p->Send_uint8(NETWORK_GAME_INFO_VERSION);
519 p->Send_string(_openttd_revision);
520 p->Send_uint32(this->newgrf_lookup_table_cursor);
521
522 Debug(net, 9, "Coordinator::SendGetListing({}, {})", _openttd_revision, this->newgrf_lookup_table_cursor);
523 this->SendPacket(std::move(p));
524}
525
532{
533 assert(invite_code.starts_with("+"));
534
535 if (this->connecter_pre.find(invite_code) != this->connecter_pre.end()) {
536 /* If someone is hammering the refresh key, one can sent out two
537 * requests for the same invite code. There isn't really a great way
538 * of handling this, so just ignore this request. */
539 connecter->SetFailure();
540 return;
541 }
542
543 /* Initially we store based on invite code; on first reply we know the
544 * token, and will start using that key instead. */
545 this->connecter_pre[std::string(invite_code)] = connecter;
546
547 this->Connect();
548
549 auto p = std::make_unique<Packet>(this, PacketCoordinatorType::ClientConnect);
550 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
551 p->Send_string(invite_code);
552
553 Debug(net, 9, "Coordinator::SendConnectToClient({})", invite_code);
554 this->SendPacket(std::move(p));
555}
556
562void ClientNetworkCoordinatorSocketHandler::ConnectFailure(std::string_view token, uint8_t tracking_number)
563{
564 /* Connecter will destroy itself. */
565 this->game_connecter = nullptr;
566
567 auto p = std::make_unique<Packet>(this, PacketCoordinatorType::ServerOrClientConnectFailed);
568 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
569 p->Send_string(token);
570 p->Send_uint8(tracking_number);
571
572 Debug(net, 9, "Coordinator::SendConnectFailure({}, {})", token, tracking_number);
573 this->SendPacket(std::move(p));
574
575 /* We do not close the associated connecter here yet, as the
576 * Game Coordinator might have other methods of connecting available. */
577}
578
587{
588 assert(sock != INVALID_SOCKET);
589
590 /* Connecter will destroy itself. */
591 this->game_connecter = nullptr;
592
593 if (_network_server) {
595 Debug(net, 3, "[{}] Client connected from {} on frame {}", ServerNetworkGameSocketHandler::GetName(), address.GetHostname(), _frame_counter);
597 } else {
598 /* The client informs the Game Coordinator about the success. The server
599 * doesn't have to, as it is implied by the client telling. */
600 auto p = std::make_unique<Packet>(this, PacketCoordinatorType::ClientConnected);
601 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
602 p->Send_string(token);
603 Debug(net, 9, "Coordinator::SendConnectSuccess({})", token);
604 this->SendPacket(std::move(p));
605
606 /* Find the connecter; it can happen it no longer exist, in cases where
607 * we aborted the connect but the Game Coordinator was already in the
608 * processes of connecting us. */
609 auto connecter_it = this->connecter.find(token);
610 if (connecter_it != this->connecter.end()) {
611 connecter_it->second.second->SetConnected(sock);
612 this->connecter.erase(connecter_it);
613 }
614 }
615
616 /* Close all remaining connections. */
617 this->CloseToken(token);
618}
619
630void ClientNetworkCoordinatorSocketHandler::StunResult(std::string_view token, uint8_t family, bool result)
631{
632 auto p = std::make_unique<Packet>(this, PacketCoordinatorType::ServerOrClientStunResult);
633 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
634 p->Send_string(token);
635 p->Send_uint8(family);
636 p->Send_bool(result);
637 Debug(net, 9, "Coordinator::SendStunResult({}, {}, {})", token, family, result);
638 this->SendPacket(std::move(p));
639}
640
646void ClientNetworkCoordinatorSocketHandler::CloseStunHandler(std::string_view token, uint8_t family)
647{
648 auto stun_it = this->stun_handlers.find(token);
649 if (stun_it == this->stun_handlers.end()) return;
650
651 if (family == AF_UNSPEC) {
652 for (auto &[family, stun_handler] : stun_it->second) {
653 stun_handler->CloseConnection();
654 stun_handler->CloseSocket();
655 }
656
657 this->stun_handlers.erase(stun_it);
658 } else {
659 auto family_it = stun_it->second.find(family);
660 if (family_it == stun_it->second.end()) return;
661
662 family_it->second->CloseConnection();
663 family_it->second->CloseSocket();
664
665 stun_it->second.erase(family_it);
666 }
667}
668
674{
676
677 auto turn_it = this->turn_handlers.find(token);
678 if (turn_it == this->turn_handlers.end()) return;
679
680 turn_it->second->CloseConnection();
681 turn_it->second->CloseSocket();
682
683 /* We don't remove turn_handler here, as we can be called from within that
684 * turn_handler instance, so our object cannot be freed yet. Instead, we
685 * check later if the connection is closed, and free the object then. */
686}
687
693{
694 /* Close all remaining STUN / TURN connections. */
695 this->CloseStunHandler(token);
696 this->CloseTurnHandler(token);
697
698 /* Close the caller of the connection attempt. */
699 auto connecter_it = this->connecter.find(token);
700 if (connecter_it != this->connecter.end()) {
701 connecter_it->second.second->SetFailure();
702 this->connecter.erase(connecter_it);
703 }
704}
705
710{
711 /* Ensure all other pending connection attempts are also killed. */
712 if (this->game_connecter != nullptr) {
713 this->game_connecter->Kill();
714 this->game_connecter = nullptr;
715 }
716
717 /* Mark any pending connecters as failed. */
718 for (auto &[token, it] : this->connecter) {
719 this->CloseStunHandler(token);
720 this->CloseTurnHandler(token);
721 it.second->SetFailure();
722
723 /* Inform the Game Coordinator he can stop trying to connect us to the server. */
724 this->ConnectFailure(token, 0);
725 }
726 this->stun_handlers.clear();
727 this->turn_handlers.clear();
728 this->connecter.clear();
729
730 /* Also close any pending invite-code requests. */
731 for (auto &[invite_code, it] : this->connecter_pre) {
732 it->SetFailure();
733 }
734 this->connecter_pre.clear();
735}
736
742{
743 /* Private games are not listed via the Game Coordinator. */
744 if (_network_server && _settings_client.network.server_game_type == ServerGameType::Local) {
745 if (this->sock != INVALID_SOCKET) {
746 this->CloseConnection();
747 }
748 return;
749 }
750
751 static int last_attempt_backoff = 1;
752 static bool first_reconnect = true;
753
754 if (this->sock == INVALID_SOCKET) {
755 static std::chrono::steady_clock::time_point last_attempt = {};
756
757 /* Don't auto-reconnect when we are not a server. */
758 if (!_network_server) return;
759 /* Don't reconnect if we are connecting. */
760 if (this->connecting) return;
761 /* Throttle how often we try to reconnect. */
762 if (std::chrono::steady_clock::now() < last_attempt + std::chrono::seconds(1) * last_attempt_backoff) return;
763
764 last_attempt = std::chrono::steady_clock::now();
765 /* Delay reconnecting with up to 32 seconds. */
766 if (last_attempt_backoff < 32) {
767 last_attempt_backoff *= 2;
768 }
769
770 /* Do not reconnect on the first attempt, but only initialize the
771 * last_attempt variables. Otherwise after an outage all servers
772 * reconnect at the same time, potentially overwhelming the
773 * Game Coordinator. */
774 if (first_reconnect) {
775 first_reconnect = false;
776 return;
777 }
778
779 Debug(net, 1, "Connection with Game Coordinator lost; reconnecting...");
780 this->Register();
781 return;
782 }
783
784 last_attempt_backoff = 1;
785 first_reconnect = true;
786
787 if (_network_server && _network_server_connection_type != CONNECTION_TYPE_UNKNOWN && std::chrono::steady_clock::now() > this->next_update) {
788 this->SendServerUpdate();
789 }
790
791 if (!_network_server && std::chrono::steady_clock::now() > this->last_activity + IDLE_TIMEOUT) {
792 this->CloseConnection();
793 return;
794 }
795
796 if (this->CanSendReceive()) {
797 if (this->ReceivePackets()) {
798 this->last_activity = std::chrono::steady_clock::now();
799 }
800 }
801
802 this->SendPackets();
803
804 for (const auto &[token, families] : this->stun_handlers) {
805 for (const auto &[family, stun_handler] : families) {
806 stun_handler->SendReceive();
807 }
808 }
809
810 /* Check for handlers that are not connecting nor connected. Destroy those objects. */
811 for (auto turn_it = this->turn_handlers.begin(); turn_it != this->turn_handlers.end(); /* nothing */) {
812 if (turn_it->second->connect_started && turn_it->second->connecter == nullptr && !turn_it->second->IsConnected()) {
813 turn_it = this->turn_handlers.erase(turn_it);
814 } else {
815 turn_it++;
816 }
817 }
818
819 for (const auto &[token, turn_handler] : this->turn_handlers) {
820 turn_handler->SendReceive();
821 }
822}
Game Coordinator communication.
GameInfoNewGRFLookupTable newgrf_lookup_table
Table to look up NewGRFs in the GC_LISTING packets.
bool ReceiveGameCoordinatorDirectConnect(Packet &p) override
Game Coordinator requests that the Client makes a direct connection to the indicated peer,...
void StartTurnConnection(std::string_view token)
Perform the TURN connection with the given token.
void CloseTurnHandler(std::string_view token)
Close the TURN handler.
std::map< std::string, std::unique_ptr< ClientNetworkTurnSocketHandler >, std::less<> > turn_handlers
Pending TURN handler (if any), stored by token.
void Register()
Register our server to receive our invite code.
void ConnectToServer(std::string_view invite_code, TCPServerConnecter *connecter)
Join a server based on an invite code.
void CloseStunHandler(std::string_view token, uint8_t family=AF_UNSPEC)
Close the STUN handler.
std::map< std::string, std::pair< std::string, TCPServerConnecter * >, std::less<> > connecter
Based on tokens, the current (invite-code, connecter) that are pending.
bool ReceiveGameCoordinatorStunConnect(Packet &p) override
Game Coordinator informs the client/server of its STUN peer (the host:ip of the other side).
uint32_t newgrf_lookup_table_cursor
Last received cursor for the GameInfoNewGRFLookupTable updates.
void SendReceive()
Check whether we received/can send some data from/to the Game Coordinator server and when that's the ...
static constexpr std::chrono::seconds IDLE_TIMEOUT
The idle timeout; when to close the connection because it's idle.
std::chrono::steady_clock::time_point next_update
When to send the next update (if server and public).
bool connecting
Are we connecting to the Game Coordinator?
bool ReceiveGameCoordinatorError(Packet &p) override
Game Coordinator indicates there was an error.
void CloseAllConnections()
Close all pending connection tokens.
void SendServerUpdate()
Send an update of our server status to the Game Coordinator.
bool ReceiveGameCoordinatorStunRequest(Packet &p) override
Game Coordinator requests the client/server to do a STUN request to the STUN server.
void Connect()
Connect to the coordinator.
std::map< std::string, TCPServerConnecter *, std::less<> > connecter_pre
Based on invite codes, the current connecters that are pending.
bool ReceiveGameCoordinatorConnectFailed(Packet &p) override
Game Coordinator informs the Client that it failed to find a way to connect the Client to the Server.
void ConnectFailure(std::string_view token, uint8_t tracking_number)
Callback from a Connecter to let the Game Coordinator know the connection failed.
bool ReceiveGameCoordinatorRegisterAck(Packet &p) override
Game Coordinator acknowledges the registration.
bool ReceiveGameCoordinatorConnecting(Packet &p) override
Game Coordinator informs the Client under what token it will start the attempt to connect the Server ...
void CloseToken(std::string_view token)
Close everything related to this connection token.
std::chrono::steady_clock::time_point last_activity
The last time there was network activity.
void StunResult(std::string_view token, uint8_t family, bool result)
Callback from the STUN connecter to inform the Game Coordinator about the result of the STUN.
bool ReceiveGameCoordinatorListing(Packet &p) override
Game Coordinator replies with a list of all public servers.
bool ReceiveGameCoordinatorTurnConnect(Packet &p) override
Game Coordinator requests that we make a connection to the indicated peer, which is a TURN server.
NetworkRecvStatus CloseConnection(bool error=true) override
This will put this socket handler in a close state.
void ConnectSuccess(std::string_view token, SOCKET sock, NetworkAddress &address)
Callback from a Connecter to let the Game Coordinator know the connection to the game server is estab...
bool ReceiveGameCoordinatorNewGRFLookup(Packet &p) override
Game Coordinator informs the client of updates for the NewGRFs lookup table as used by the NewGRF des...
std::map< std::string, std::map< int, std::unique_ptr< ClientNetworkStunSocketHandler > >, std::less<> > stun_handlers
All pending STUN handlers, stored by token:family.
std::shared_ptr< TCPConnecter > game_connecter
Pending connecter to the game server.
void GetListing()
Request a listing of all public servers.
static std::unique_ptr< ClientNetworkStunSocketHandler > Stun(std::string_view token, uint8_t family)
Send a STUN packet to the STUN server.
static std::unique_ptr< ClientNetworkTurnSocketHandler > Turn(std::string_view token, uint8_t tracking_number, std::string_view ticket, std::string_view connection_string)
Prepare a TURN connection.
Wrapper for (un)resolved network addresses; there's no reason to transform a numeric IP to a string a...
Definition address.h:28
static NetworkAddress GetPeerAddress(SOCKET sock)
Get the peer address of a socket as NetworkAddress.
Definition address.cpp:397
const std::string & GetHostname()
Get the hostname; in case it wasn't given the IPv4 dotted representation is given.
Definition address.cpp:24
void OnConnect(SOCKET s) override
Callback when the connection succeeded.
void OnFailure() override
Callback for when the connection attempt failed.
NetworkCoordinatorConnecter(std::string_view connection_string)
Initiate the connecting.
bool ReceivePackets()
Receive a packet at TCP level.
uint8_t tracking_number
Tracking number of this connection.
void OnFailure() override
Callback for when the connection attempt failed.
NetworkDirectConnecter(std::string_view hostname, uint16_t port, std::string &&token, uint8_t tracking_number)
Try to establish a direct (hostname:port based) connection.
void OnConnect(SOCKET s) override
Callback when the connection succeeded.
std::string token
Token of this connection.
void OnConnect(SOCKET s) override
Callback when the connection succeeded.
uint8_t tracking_number
Tracking number of this connection.
NetworkReuseStunConnecter(std::string_view hostname, uint16_t port, const NetworkAddress &bind_address, std::string &&token, uint8_t tracking_number, uint8_t family)
Try to establish a STUN-based connection.
void OnFailure() override
Callback for when the connection attempt failed.
uint8_t family
Family of this connection.
std::string token
Token of this connection.
void Reopen()
Reopen the socket so we can send/receive stuff again.
Definition core.h:77
virtual NetworkRecvStatus CloseConnection(bool error=true)
This will put this socket handler in a close state.
Definition tcp.cpp:40
SOCKET sock
The socket currently connected to.
Definition tcp.h:35
virtual void SendPacket(std::unique_ptr< Packet > &&packet)
This function puts the packet in the send-queue and it is send as soon as possible.
Definition tcp.cpp:57
SendPacketsState SendPackets(bool closing_down=false)
Sends all the buffered packets out for this client.
Definition tcp.cpp:75
void CloseSocket()
Close the actual socket of the connection.
Definition tcp.cpp:28
bool CanSendReceive()
Check whether this socket can send or receive something.
Definition tcp.cpp:193
static std::string_view GetName()
Get the name used by the listener.
static void AcceptConnection(SOCKET s, const NetworkAddress &address)
Handle the accepting of a connection to the server.
Definition network.cpp:578
NetworkAddress bind_address
Address we're binding to, if any.
Definition tcp.h:101
std::string connection_string
Current address we are connecting to (before resolving).
Definition tcp.h:100
static std::shared_ptr< TCPConnecter > Create(Args &&... args)
Create the connecter, and initiate connecting by putting it in the collection of TCP connections to m...
Definition tcp.h:146
TCPConnecter that resolves the server invite code if needed before connecting.
Definition tcp.h:153
std::string_view NetworkCoordinatorConnectionString()
Get the connection string for the game coordinator from the environment variable OTTD_COORDINATOR_CS,...
Definition config.cpp:21
static const uint NETWORK_INVITE_CODE_SECRET_LENGTH
The maximum length of the invite code secret, in bytes including '\0'.
Definition config.h:68
static const size_t TCP_MTU
Number of bytes we can pack in a single TCP packet.
Definition config.h:43
static const uint16_t NETWORK_COORDINATOR_SERVER_PORT
The default port of the Game Coordinator server (TCP).
Definition config.h:19
static const uint8_t NETWORK_COORDINATOR_VERSION
What version of game-coordinator-protocol do we use?
Definition config.h:48
static const uint NETWORK_INVITE_CODE_LENGTH
The maximum length of the invite code, in bytes including '\0'.
Definition config.h:67
static const uint NETWORK_HOSTNAME_PORT_LENGTH
The maximum length of the host name + port, in bytes including '\0'. The extra six is ":" + port numb...
Definition config.h:53
static const uint NETWORK_HOSTNAME_LENGTH
The maximum length of the host name, in bytes including '\0'.
Definition config.h:52
static const uint NETWORK_TOKEN_LENGTH
The maximum length of a token, in bytes including '\0'.
Definition config.h:69
static const uint NETWORK_ERROR_DETAIL_LENGTH
The maximum length of the error detail, in bytes including '\0'.
Definition config.h:66
static const uint8_t NETWORK_GAME_INFO_VERSION
What version of game-info do we use?
Definition config.h:47
NetworkRecvStatus
Status of a network client; reasons why a client has quit.
Definition core.h:21
@ NETWORK_RECV_STATUS_OKAY
Everything is okay.
Definition core.h:22
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
Functions related to errors.
@ WL_ERROR
Errors (eg. saving/loading failed).
Definition error.h:26
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
uint32_t _frame_counter
The current frame.
Definition network.cpp:80
bool _network_dedicated
are we a dedicated server?
Definition network.cpp:70
bool _network_server
network-server is active
Definition network.cpp:68
Basic functions/variables used all over the place.
ConnectionType _network_server_connection_type
What type of connection the Game Coordinator detected we are on.
ClientNetworkCoordinatorSocketHandler _network_coordinator_client
The connection to the Game Coordinator.
static const auto NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES
How many time between updates the server sends to the Game Coordinator.
std::string _network_server_invite_code
Our invite code as indicated by the Game Coordinator.
Part of the network protocol handling Game Coordinator requests.
void SerializeNetworkGameInfo(Packet &p, const NetworkServerGameInfo &info, bool send_newgrf_names)
Serializes the NetworkGameInfo struct to the packet.
void CheckGameCompatibility(NetworkGameInfo &ngi)
Check if an game entry is compatible with our client.
const NetworkServerGameInfo & GetCurrentNetworkServerGameInfo()
Get the NetworkServerGameInfo structure with the latest information of the server.
void DeserializeGRFIdentifierWithName(Packet &p, NamedGRFIdentifier &grf)
Deserializes the NamedGRFIdentifier (GRF ID, MD5 checksum and name) from the packet.
void DeserializeNetworkGameInfo(Packet &p, NetworkGameInfo &info, const GameInfoNewGRFLookupTable *newgrf_lookup_table)
Deserializes the NetworkGameInfo struct from the packet.
NetworkGame * NetworkGameListAddItem(std::string_view connection_string)
Add a new item to the linked gamelist.
void NetworkGameListRemoveExpired()
Remove all servers that have not recently been updated.
int _network_game_list_version
Current version of all items in the list.
Handling of the list of games.
@ NGLS_ONLINE
Server is online.
@ NGLS_OFFLINE
Server is offline (or cannot be queried).
void UpdateNetworkGameWindow()
Update the network new window because a new server is found on the network.
void ShowNetworkAskRelay(std::string_view server_connection_string, std::string &&relay_connection_string, std::string &&token)
Show a modal confirmation window with "no" / "yes, once" / "yes, always" buttons.
GUIs related to networking.
@ NRWCD_HANDLED
Relay request is handled, either by user or by timeout.
Definition network_gui.h:45
Variables and function used internally.
Server part of the network protocol.
Part of the network protocol handling STUN requests.
@ Public
The game is publicly accessible.
@ Local
Do not communicate with the game coordinator.
@ InviteOnly
The game can be accessed if you know the invite code.
void ClearGRFConfigList(GRFConfigList &config)
Clear a GRF Config list, freeing all nodes.
Declaration of OTTD revision dependent variables.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
Types related to global configuration settings.
Definition of base types and functions in a cross-platform compatible way.
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
Functions related to OTTD's strings.
Structure with information shown in the game list (GUI).
int version
Used to see which servers are no longer available on the Game Coordinator and can be removed.
NetworkGameInfo info
The game information of this server.
NetworkGameStatus status
Stats of the server.
GRFConfigList grfconfig
List of NewGRF files used.
uint16_t Recv_uint16()
Read a 16 bits integer from the packet.
Definition packet.cpp:330
uint32_t Recv_uint32()
Read a 32 bits integer from the packet.
Definition packet.cpp:345
std::string Recv_string(size_t length, StringValidationSettings settings=StringValidationSetting::ReplaceWithQuestionMark)
Reads characters (bytes) from the packet until it finds a '\0', or reaches a maximum of length charac...
Definition packet.cpp:423
uint8_t Recv_uint8()
Read a 8 bits integer from the packet.
Definition packet.cpp:316
@ ServerOrClientConnectFailed
Client/server tells the Game Coordinator the current connection attempt failed.
@ ClientListing
Client is requesting a listing of all public servers.
@ ServerOrClientStunResult
Client/server informs the Game Coordinator of the result of the STUN request.
@ ServerRegister
Server registration.
@ ClientConnected
Client informs the Game Coordinator the connection with the server is established.
@ ServerUpdate
Server sends an set intervals an update of the server.
@ ClientConnect
Client wants to connect to a server based on an invite code.
NetworkCoordinatorErrorType
The type of error from the Game Coordinator.
@ NETWORK_COORDINATOR_ERROR_UNKNOWN
There was an unknown error.
@ NETWORK_COORDINATOR_ERROR_REGISTRATION_FAILED
Your request for registration failed.
@ NETWORK_COORDINATOR_ERROR_INVALID_INVITE_CODE
The invite code given is invalid.
@ NETWORK_COORDINATOR_ERROR_REUSE_OF_INVITE_CODE
The invite code is used by another (newer) server.
ConnectionType
The type of connection the Game Coordinator can detect we have.
@ CONNECTION_TYPE_ISOLATED
The Game Coordinator failed to find a way to connect to your server. Nobody will be able to join.
@ CONNECTION_TYPE_DIRECT
The Game Coordinator can directly connect to your server.
@ CONNECTION_TYPE_STUN
The Game Coordinator can connect to your server via a STUN request.
@ CONNECTION_TYPE_UNKNOWN
The Game Coordinator hasn't informed us yet what type of connection we have.
@ CONNECTION_TYPE_TURN
The Game Coordinator needs you to connect to a relay.
void CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition window.cpp:1222
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3200
Window functions not directly related to making/drawing windows.
Types related to windows.
@ WC_NETWORK_ASK_RELAY
Network ask relay window; Window numbers:
@ WC_CLIENT_LIST
Client list; Window numbers: