OpenTTD Source 20250312-master-gcdcc6b491d
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 <http://www.gnu.org/licenses/>.
6 */
7
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
36private:
37 std::string token;
39
40public:
48 NetworkDirectConnecter(const std::string &hostname, uint16_t port, const std::string &token, uint8_t tracking_number) : TCPConnecter(hostname, port), token(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
64private:
65 std::string token;
67 uint8_t family;
68
69public:
79 NetworkReuseStunConnecter(const std::string &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(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
104public:
110
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();
124 }
125};
126
128{
130 std::string detail = p.Recv_string(NETWORK_ERROR_DETAIL_LENGTH);
131
132 switch (error) {
134 this->CloseConnection();
135 return false;
136
138 ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED), {}, WL_ERROR);
139
140 /* To prevent that we constantly try to reconnect, switch to local game. */
141 _settings_client.network.server_game_type = SERVER_GAME_TYPE_LOCAL;
142
143 this->CloseConnection();
144 return false;
145
147 auto connecter_pre_it = this->connecter_pre.find(detail);
148 if (connecter_pre_it != this->connecter_pre.end()) {
149 connecter_pre_it->second->SetFailure();
150 this->connecter_pre.erase(connecter_pre_it);
151 }
152
153 /* Mark the server as offline. */
155 item->status = NGLS_OFFLINE;
156
158 return true;
159 }
160
162 ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_COORDINATOR_REUSE_OF_INVITE_CODE), {}, WL_ERROR);
163
164 /* To prevent that we constantly battle for the same invite-code, switch to local game. */
165 _settings_client.network.server_game_type = SERVER_GAME_TYPE_LOCAL;
166
167 this->CloseConnection();
168 return false;
169
170 default:
171 Debug(net, 0, "Invalid error type {} received from Game Coordinator", error);
172 this->CloseConnection();
173 return false;
174 }
175}
176
178{
179 /* Schedule sending an update. */
180 this->next_update = std::chrono::steady_clock::now();
181
185
188 GetEncodedString(STR_NETWORK_ERROR_COORDINATOR_ISOLATED),
189 GetEncodedString(STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL),
190 WL_ERROR);
191 }
192
193 /* Users can change the invite code in the settings, but this has no effect
194 * on the invite code as assigned by the server. So
195 * _network_server_invite_code contains the current invite code,
196 * and _settings_client.network.server_invite_code contains the one we will
197 * attempt to re-use when registering again. */
199
201
202 if (_network_dedicated) {
203 std::string connection_type;
205 case CONNECTION_TYPE_ISOLATED: connection_type = "Remote players can't connect"; break;
206 case CONNECTION_TYPE_DIRECT: connection_type = "Public"; break;
207 case CONNECTION_TYPE_STUN: connection_type = "Behind NAT"; break;
208 case CONNECTION_TYPE_TURN: connection_type = "Via relay"; break;
209
210 case CONNECTION_TYPE_UNKNOWN: // Never returned from Game Coordinator.
211 default: connection_type = "Unknown"; break; // Should never happen, but don't fail if it does.
212 }
213
214 std::string game_type;
216 case SERVER_GAME_TYPE_INVITE_ONLY: game_type = "Invite only"; break;
217 case SERVER_GAME_TYPE_PUBLIC: game_type = "Public"; break;
218
219 case SERVER_GAME_TYPE_LOCAL: // Impossible to register local servers.
220 default: game_type = "Unknown"; break; // Should never happen, but don't fail if it does.
221 }
222
223 Debug(net, 3, "----------------------------------------");
224 Debug(net, 3, "Your server is now registered with the Game Coordinator:");
225 Debug(net, 3, " Game type: {}", game_type);
226 Debug(net, 3, " Connection type: {}", connection_type);
227 Debug(net, 3, " Invite code: {}", _network_server_invite_code);
228 Debug(net, 3, "----------------------------------------");
229 } else {
230 Debug(net, 3, "Game Coordinator registered our server with invite code '{}'", _network_server_invite_code);
231 }
232
233 return true;
234}
235
237{
238 uint8_t servers = p.Recv_uint16();
239
240 /* End of list; we can now remove all expired items from the list. */
241 if (servers == 0) {
243 return true;
244 }
245
246 for (; servers > 0; servers--) {
247 std::string connection_string = p.Recv_string(NETWORK_HOSTNAME_PORT_LENGTH);
248
249 /* Now we know the connection string, we can add it to our list. */
250 NetworkGameList *item = NetworkGameListAddItem(connection_string);
251
252 /* Clear any existing GRFConfig chain. */
254 /* Read the NetworkGameInfo from the packet. */
255 DeserializeNetworkGameInfo(p, item->info, &this->newgrf_lookup_table);
256 /* Check for compatibility with the client. */
257 CheckGameCompatibility(item->info);
258 /* Mark server as online. */
259 item->status = NGLS_ONLINE;
260 /* Mark the item as up-to-date. */
262 }
263
265 return true;
266}
267
269{
270 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
271 std::string invite_code = p.Recv_string(NETWORK_INVITE_CODE_LENGTH);
272
273 /* Find the connecter based on the invite code. */
274 auto connecter_pre_it = this->connecter_pre.find(invite_code);
275 if (connecter_pre_it == this->connecter_pre.end()) {
276 this->CloseConnection();
277 return false;
278 }
279
280 /* Now store it based on the token. */
281 this->connecter[token] = {invite_code, connecter_pre_it->second};
282 this->connecter_pre.erase(connecter_pre_it);
283
284 return true;
285}
286
288{
289 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
290 this->CloseToken(token);
291
292 return true;
293}
294
296{
297 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
298 uint8_t tracking_number = p.Recv_uint8();
299 std::string hostname = p.Recv_string(NETWORK_HOSTNAME_LENGTH);
300 uint16_t port = p.Recv_uint16();
301
302 /* Ensure all other pending connection attempts are killed. */
303 if (this->game_connecter != nullptr) {
304 this->game_connecter->Kill();
305 this->game_connecter = nullptr;
306 }
307
308 this->game_connecter = TCPConnecter::Create<NetworkDirectConnecter>(hostname, port, token, tracking_number);
309 return true;
310}
311
313{
314 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
315
316 this->stun_handlers[token][AF_INET6] = ClientNetworkStunSocketHandler::Stun(token, AF_INET6);
317 this->stun_handlers[token][AF_INET] = ClientNetworkStunSocketHandler::Stun(token, AF_INET);
318 return true;
319}
320
322{
323 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
324 uint8_t tracking_number = p.Recv_uint8();
325 uint8_t family = p.Recv_uint8();
326 std::string host = p.Recv_string(NETWORK_HOSTNAME_PORT_LENGTH);
327 uint16_t port = p.Recv_uint16();
328
329 /* Check if we know this token. */
330 auto stun_it = this->stun_handlers.find(token);
331 if (stun_it == this->stun_handlers.end()) return true;
332 auto family_it = stun_it->second.find(family);
333 if (family_it == stun_it->second.end()) return true;
334
335 /* Ensure all other pending connection attempts are killed. */
336 if (this->game_connecter != nullptr) {
337 this->game_connecter->Kill();
338 this->game_connecter = nullptr;
339 }
340
341 /* We now mark the connection as closed, but we do not really close the
342 * socket yet. We do this when the NetworkReuseStunConnecter is connected.
343 * This prevents any NAT to already remove the route while we create the
344 * second connection on top of the first. */
345 family_it->second->CloseConnection(false);
346
347 /* Connect to our peer from the same local address as we use for the
348 * STUN server. This means that if there is any NAT in the local network,
349 * the public ip:port is still pointing to the local address, and as such
350 * a connection can be established. */
351 this->game_connecter = TCPConnecter::Create<NetworkReuseStunConnecter>(host, port, family_it->second->local_addr, token, tracking_number, family);
352 return true;
353}
354
356{
358
359 uint16_t newgrfs = p.Recv_uint16();
360 for (; newgrfs> 0; newgrfs--) {
361 uint32_t index = p.Recv_uint32();
362 DeserializeGRFIdentifierWithName(p, this->newgrf_lookup_table[index]);
363 }
364 return true;
365}
366
368{
369 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
370 uint8_t tracking_number = p.Recv_uint8();
371 std::string ticket = p.Recv_string(NETWORK_TOKEN_LENGTH);
372 std::string connection_string = p.Recv_string(NETWORK_HOSTNAME_PORT_LENGTH);
373
374 /* Ensure all other pending connection attempts are killed. */
375 if (this->game_connecter != nullptr) {
376 this->game_connecter->Kill();
377 this->game_connecter = nullptr;
378 }
379
380 this->turn_handlers[token] = ClientNetworkTurnSocketHandler::Turn(token, tracking_number, ticket, connection_string);
381
382 if (!_network_server) {
383 auto connecter_it = this->connecter.find(token);
384 if (connecter_it == this->connecter.end()) {
385 /* Make sure we are still interested in connecting to this server. */
386 this->ConnectFailure(token, 0);
387 return true;
388 }
389
391 case URS_NEVER:
392 this->ConnectFailure(token, 0);
393 break;
394
395 case URS_ASK:
396 ShowNetworkAskRelay(connecter_it->second.first, connection_string, token);
397 break;
398
399 case URS_ALLOW:
400 this->StartTurnConnection(token);
401 break;
402 }
403 } else {
404 this->StartTurnConnection(token);
405 }
406
407 return true;
408}
409
410void ClientNetworkCoordinatorSocketHandler::StartTurnConnection(std::string &token)
411{
412 auto turn_it = this->turn_handlers.find(token);
413 if (turn_it == this->turn_handlers.end()) return;
414
415 turn_it->second->Connect();
416}
417
418void ClientNetworkCoordinatorSocketHandler::Connect()
419{
420 /* We are either already connected or are trying to connect. */
421 if (this->sock != INVALID_SOCKET || this->connecting) return;
422
423 this->Reopen();
424
425 this->connecting = true;
426 this->last_activity = std::chrono::steady_clock::now();
427
428 TCPConnecter::Create<NetworkCoordinatorConnecter>(NetworkCoordinatorConnectionString());
429}
430
447
452{
454 this->next_update = {};
455
457
458 this->Connect();
459
460 auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_SERVER_REGISTER);
461 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
463 p->Send_uint16(_settings_client.network.server_port);
465 p->Send_string("");
466 p->Send_string("");
467 } else {
470 }
471
472 this->SendPacket(std::move(p));
473}
474
479{
480 Debug(net, 6, "Sending server update to Game Coordinator");
481
482 auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_SERVER_UPDATE, TCP_MTU);
483 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
484 SerializeNetworkGameInfo(*p, GetCurrentNetworkServerGameInfo(), this->next_update.time_since_epoch() != std::chrono::nanoseconds::zero());
485
486 this->SendPacket(std::move(p));
487
488 this->next_update = std::chrono::steady_clock::now() + NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES;
489}
490
495{
496 this->Connect();
497
499
500 auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_CLIENT_LISTING);
501 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
502 p->Send_uint8(NETWORK_GAME_INFO_VERSION);
503 p->Send_string(_openttd_revision);
504 p->Send_uint32(this->newgrf_lookup_table_cursor);
505
506 this->SendPacket(std::move(p));
507}
508
515{
516 assert(invite_code.starts_with("+"));
517
518 if (this->connecter_pre.find(invite_code) != this->connecter_pre.end()) {
519 /* If someone is hammering the refresh key, one can sent out two
520 * requests for the same invite code. There isn't really a great way
521 * of handling this, so just ignore this request. */
522 connecter->SetFailure();
523 return;
524 }
525
526 /* Initially we store based on invite code; on first reply we know the
527 * token, and will start using that key instead. */
528 this->connecter_pre[invite_code] = connecter;
529
530 this->Connect();
531
532 auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_CLIENT_CONNECT);
533 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
534 p->Send_string(invite_code);
535
536 this->SendPacket(std::move(p));
537}
538
544void ClientNetworkCoordinatorSocketHandler::ConnectFailure(const std::string &token, uint8_t tracking_number)
545{
546 /* Connecter will destroy itself. */
547 this->game_connecter = nullptr;
548
549 auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_SERCLI_CONNECT_FAILED);
550 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
551 p->Send_string(token);
552 p->Send_uint8(tracking_number);
553
554 this->SendPacket(std::move(p));
555
556 /* We do not close the associated connecter here yet, as the
557 * Game Coordinator might have other methods of connecting available. */
558}
559
566void ClientNetworkCoordinatorSocketHandler::ConnectSuccess(const std::string &token, SOCKET sock, NetworkAddress &address)
567{
568 assert(sock != INVALID_SOCKET);
569
570 /* Connecter will destroy itself. */
571 this->game_connecter = nullptr;
572
573 if (_network_server) {
574 if (!ServerNetworkGameSocketHandler::ValidateClient(sock, address)) return;
575 Debug(net, 3, "[{}] Client connected from {} on frame {}", ServerNetworkGameSocketHandler::GetName(), address.GetHostname(), _frame_counter);
577 } else {
578 /* The client informs the Game Coordinator about the success. The server
579 * doesn't have to, as it is implied by the client telling. */
580 auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_CLIENT_CONNECTED);
581 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
582 p->Send_string(token);
583 this->SendPacket(std::move(p));
584
585 /* Find the connecter; it can happen it no longer exist, in cases where
586 * we aborted the connect but the Game Coordinator was already in the
587 * processes of connecting us. */
588 auto connecter_it = this->connecter.find(token);
589 if (connecter_it != this->connecter.end()) {
590 connecter_it->second.second->SetConnected(sock);
591 this->connecter.erase(connecter_it);
592 }
593 }
594
595 /* Close all remaining connections. */
596 this->CloseToken(token);
597}
598
606void ClientNetworkCoordinatorSocketHandler::StunResult(const std::string &token, uint8_t family, bool result)
607{
608 auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_SERCLI_STUN_RESULT);
609 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
610 p->Send_string(token);
611 p->Send_uint8(family);
612 p->Send_bool(result);
613 this->SendPacket(std::move(p));
614}
615
621void ClientNetworkCoordinatorSocketHandler::CloseStunHandler(const std::string &token, uint8_t family)
622{
623 auto stun_it = this->stun_handlers.find(token);
624 if (stun_it == this->stun_handlers.end()) return;
625
626 if (family == AF_UNSPEC) {
627 for (auto &[family, stun_handler] : stun_it->second) {
628 stun_handler->CloseConnection();
629 stun_handler->CloseSocket();
630 }
631
632 this->stun_handlers.erase(stun_it);
633 } else {
634 auto family_it = stun_it->second.find(family);
635 if (family_it == stun_it->second.end()) return;
636
637 family_it->second->CloseConnection();
638 family_it->second->CloseSocket();
639
640 stun_it->second.erase(family_it);
641 }
642}
643
649{
651
652 auto turn_it = this->turn_handlers.find(token);
653 if (turn_it == this->turn_handlers.end()) return;
654
655 turn_it->second->CloseConnection();
656 turn_it->second->CloseSocket();
657
658 /* We don't remove turn_handler here, as we can be called from within that
659 * turn_handler instance, so our object cannot be free'd yet. Instead, we
660 * check later if the connection is closed, and free the object then. */
661}
662
668{
669 /* Close all remaining STUN / TURN connections. */
670 this->CloseStunHandler(token);
671 this->CloseTurnHandler(token);
672
673 /* Close the caller of the connection attempt. */
674 auto connecter_it = this->connecter.find(token);
675 if (connecter_it != this->connecter.end()) {
676 connecter_it->second.second->SetFailure();
677 this->connecter.erase(connecter_it);
678 }
679}
680
685{
686 /* Ensure all other pending connection attempts are also killed. */
687 if (this->game_connecter != nullptr) {
688 this->game_connecter->Kill();
689 this->game_connecter = nullptr;
690 }
691
692 /* Mark any pending connecters as failed. */
693 for (auto &[token, it] : this->connecter) {
694 this->CloseStunHandler(token);
695 this->CloseTurnHandler(token);
696 it.second->SetFailure();
697
698 /* Inform the Game Coordinator he can stop trying to connect us to the server. */
699 this->ConnectFailure(token, 0);
700 }
701 this->stun_handlers.clear();
702 this->turn_handlers.clear();
703 this->connecter.clear();
704
705 /* Also close any pending invite-code requests. */
706 for (auto &[invite_code, it] : this->connecter_pre) {
707 it->SetFailure();
708 }
709 this->connecter_pre.clear();
710}
711
717{
718 /* Private games are not listed via the Game Coordinator. */
719 if (_network_server && _settings_client.network.server_game_type == SERVER_GAME_TYPE_LOCAL) {
720 if (this->sock != INVALID_SOCKET) {
721 this->CloseConnection();
722 }
723 return;
724 }
725
726 static int last_attempt_backoff = 1;
727 static bool first_reconnect = true;
728
729 if (this->sock == INVALID_SOCKET) {
730 static std::chrono::steady_clock::time_point last_attempt = {};
731
732 /* Don't auto-reconnect when we are not a server. */
733 if (!_network_server) return;
734 /* Don't reconnect if we are connecting. */
735 if (this->connecting) return;
736 /* Throttle how often we try to reconnect. */
737 if (std::chrono::steady_clock::now() < last_attempt + std::chrono::seconds(1) * last_attempt_backoff) return;
738
739 last_attempt = std::chrono::steady_clock::now();
740 /* Delay reconnecting with up to 32 seconds. */
741 if (last_attempt_backoff < 32) {
742 last_attempt_backoff *= 2;
743 }
744
745 /* Do not reconnect on the first attempt, but only initialize the
746 * last_attempt variables. Otherwise after an outage all servers
747 * reconnect at the same time, potentially overwhelming the
748 * Game Coordinator. */
749 if (first_reconnect) {
750 first_reconnect = false;
751 return;
752 }
753
754 Debug(net, 1, "Connection with Game Coordinator lost; reconnecting...");
755 this->Register();
756 return;
757 }
758
759 last_attempt_backoff = 1;
760 first_reconnect = true;
761
762 if (_network_server && _network_server_connection_type != CONNECTION_TYPE_UNKNOWN && std::chrono::steady_clock::now() > this->next_update) {
763 this->SendServerUpdate();
764 }
765
766 if (!_network_server && std::chrono::steady_clock::now() > this->last_activity + IDLE_TIMEOUT) {
767 this->CloseConnection();
768 return;
769 }
770
771 if (this->CanSendReceive()) {
772 if (this->ReceivePackets()) {
773 this->last_activity = std::chrono::steady_clock::now();
774 }
775 }
776
777 this->SendPackets();
778
779 for (const auto &[token, families] : this->stun_handlers) {
780 for (const auto &[family, stun_handler] : families) {
781 stun_handler->SendReceive();
782 }
783 }
784
785 /* Check for handlers that are not connecting nor connected. Destroy those objects. */
786 for (auto turn_it = this->turn_handlers.begin(); turn_it != this->turn_handlers.end(); /* nothing */) {
787 if (turn_it->second->connect_started && turn_it->second->connecter == nullptr && !turn_it->second->IsConnected()) {
788 turn_it = this->turn_handlers.erase(turn_it);
789 } else {
790 turn_it++;
791 }
792 }
793
794 for (const auto &[token, turn_handler] : this->turn_handlers) {
795 turn_handler->SendReceive();
796 }
797}
Game Coordinator communication.
bool Receive_GC_NEWGRF_LOOKUP(Packet &p) override
Game Coordinator informs the client of updates for the NewGRFs lookup table as used by the NewGRF des...
GameInfoNewGRFLookupTable newgrf_lookup_table
Table to look up NewGRFs in the GC_LISTING packets.
bool Receive_GC_STUN_CONNECT(Packet &p) override
Game Coordinator informs the client/server of its STUN peer (the host:ip of the other side).
bool Receive_GC_STUN_REQUEST(Packet &p) override
Game Coordinator requests the client/server to do a STUN request to the STUN server.
bool Receive_GC_REGISTER_ACK(Packet &p) override
Game Coordinator acknowledges the registration.
void Register()
Register our server to receive our invite code.
std::map< std::string, TCPServerConnecter * > connecter_pre
Based on invite codes, the current connecters that are pending.
void StunResult(const std::string &token, uint8_t family, bool result)
Callback from the STUN connecter to inform the Game Coordinator about the result of the STUN.
void ConnectToServer(const std::string &invite_code, TCPServerConnecter *connecter)
Join a server based on an invite code.
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?
void CloseAllConnections()
Close all pending connection tokens.
void SendServerUpdate()
Send an update of our server status to the Game Coordinator.
void CloseStunHandler(const std::string &token, uint8_t family=AF_UNSPEC)
Close the STUN handler.
void ConnectSuccess(const std::string &token, SOCKET sock, NetworkAddress &address)
Callback from a Connecter to let the Game Coordinator know the connection to the game server is estab...
bool Receive_GC_LISTING(Packet &p) override
Game Coordinator replies with a list of all public servers.
void CloseToken(const std::string &token)
Close everything related to this connection token.
std::map< std::string, std::unique_ptr< ClientNetworkTurnSocketHandler > > turn_handlers
Pending TURN handler (if any), stored by token.
void CloseTurnHandler(const std::string &token)
Close the TURN handler.
std::map< std::string, std::map< int, std::unique_ptr< ClientNetworkStunSocketHandler > > > stun_handlers
All pending STUN handlers, stored by token:family.
std::chrono::steady_clock::time_point last_activity
The last time there was network activity.
std::map< std::string, std::pair< std::string, TCPServerConnecter * > > connecter
Based on tokens, the current (invite-code, connecter) that are pending.
bool Receive_GC_DIRECT_CONNECT(Packet &p) override
Game Coordinator requests that the Client makes a direct connection to the indicated peer,...
bool Receive_GC_CONNECTING(Packet &p) override
Game Coordinator informs the Client under what token it will start the attempt to connect the Server ...
bool Receive_GC_TURN_CONNECT(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.
bool Receive_GC_CONNECT_FAILED(Packet &p) override
Game Coordinator informs the Client that it failed to find a way to connect the Client to the Server.
void ConnectFailure(const std::string &token, uint8_t tracking_number)
Callback from a Connecter to let the Game Coordinator know the connection failed.
bool Receive_GC_ERROR(Packet &p) override
Game Coordinator indicates there was an error.
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(const std::string &token, uint8_t family)
Send a STUN packet to the STUN server.
static std::unique_ptr< ClientNetworkTurnSocketHandler > Turn(const std::string &token, uint8_t tracking_number, const std::string &ticket, const std::string &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:403
const std::string & GetHostname()
Get the hostname; in case it wasn't given the IPv4 dotted representation is given.
Definition address.cpp:23
Connect to the Game Coordinator server.
void OnConnect(SOCKET s) override
Callback when the connection succeeded.
void OnFailure() override
Callback for when the connection attempt failed.
NetworkCoordinatorConnecter(const std::string &connection_string)
Initiate the connecting.
bool ReceivePackets()
Receive a packet at TCP level.
Connect to a game server by IP:port.
NetworkDirectConnecter(const std::string &hostname, uint16_t port, const std::string &token, uint8_t tracking_number)
Try to establish a direct (hostname:port based) connection.
uint8_t tracking_number
Tracking number of this connection.
void OnFailure() override
Callback for when the connection attempt failed.
void OnConnect(SOCKET s) override
Callback when the connection succeeded.
std::string token
Token of this connection.
Connecter used after STUN exchange to connect from both sides to each other.
void OnConnect(SOCKET s) override
Callback when the connection succeeded.
uint8_t tracking_number
Tracking number of this connection.
void OnFailure() override
Callback for when the connection attempt failed.
uint8_t family
Family of this connection.
NetworkReuseStunConnecter(const std::string &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.
std::string token
Token of this connection.
void Reopen()
Reopen the socket so we can send/receive stuff again.
Definition core.h:79
virtual NetworkRecvStatus CloseConnection(bool error=true)
This will put this socket handler in a close state.
Definition tcp.cpp:41
SOCKET sock
The socket currently connected to.
Definition tcp.h:38
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:58
SendPacketsState SendPackets(bool closing_down=false)
Sends all the buffered packets out for this client.
Definition tcp.cpp:76
void CloseSocket()
Close the actual socket of the connection.
Definition tcp.cpp:29
bool CanSendReceive()
Check whether this socket can send or receive something.
Definition tcp.cpp:194
static void AcceptConnection(SOCKET s, const NetworkAddress &address)
Handle the accepting of a connection to the server.
Definition network.cpp:566
static const char * GetName()
Get the name used by the listener.
"Helper" class for creating TCP connections in a non-blocking manner
Definition tcp.h:74
NetworkAddress bind_address
Address we're binding to, if any.
Definition tcp.h:104
std::string connection_string
Current address we are connecting to (before resolving).
Definition tcp.h:103
const char * NetworkCoordinatorConnectionString()
Get the connection string for the game coordinator from the environment variable OTTD_COORDINATOR_CS,...
Definition config.cpp:35
static const uint NETWORK_INVITE_CODE_SECRET_LENGTH
The maximum length of the invite code secret, in bytes including '\0'.
Definition config.h:70
static const size_t TCP_MTU
Number of bytes we can pack in a single TCP packet.
Definition config.h:45
static const uint16_t NETWORK_COORDINATOR_SERVER_PORT
The default port of the Game Coordinator server (TCP)
Definition config.h:21
static const uint8_t NETWORK_COORDINATOR_VERSION
What version of game-coordinator-protocol do we use?
Definition config.h:50
static const uint NETWORK_INVITE_CODE_LENGTH
The maximum length of the invite code, in bytes including '\0'.
Definition config.h:69
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:55
static const uint NETWORK_HOSTNAME_LENGTH
The maximum length of the host name, in bytes including '\0'.
Definition config.h:54
static const uint NETWORK_TOKEN_LENGTH
The maximum length of a token, in bytes including '\0'.
Definition config.h:71
static const uint NETWORK_ERROR_DETAIL_LENGTH
The maximum length of the error detail, in bytes including '\0'.
Definition config.h:68
static const uint8_t NETWORK_GAME_INFO_VERSION
What version of game-info do we use?
Definition config.h:49
NetworkRecvStatus
Status of a network client; reasons why a client has quit.
Definition core.h:23
@ NETWORK_RECV_STATUS_OKAY
Everything is okay.
Definition core.h:24
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
@ WL_ERROR
Errors (eg. saving/loading failed)
Definition error.h:26
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, const CommandCost &cc)
Display an error message in a window.
uint32_t _frame_counter
The current frame.
Definition network.cpp:78
bool _network_dedicated
are we a dedicated server?
Definition network.cpp:68
bool _network_server
network-server is active
Definition network.cpp:66
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 NetworkGameListRemoveExpired()
Remove all servers that have not recently been updated.
int _network_game_list_version
Current version of all items in the list.
NetworkGameList * NetworkGameListAddItem(const std::string &connection_string)
Add a new item to the linked gamelist.
Handling of the list of games.
@ NGLS_ONLINE
Server is online.
@ NGLS_OFFLINE
Server is offline (or cannot be queried).
void ShowNetworkAskRelay(const std::string &server_connection_string, const std::string &relay_connection_string, const std::string &token)
Show a modal confirmation window with "no" / "yes, once" / "yes, always" buttons.
void UpdateNetworkGameWindow()
Update the network new window because a new server is found on the network.
GUIs related to networking.
@ NRWCD_HANDLED
Relay request is handled, either by user or by timeout.
Definition network_gui.h:43
Variables and function used internally.
Server part of the network protocol.
Part of the network protocol handling STUN requests.
void ClearGRFConfigList(GRFConfigList &config)
Clear a GRF Config list, freeing all nodes.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:57
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
NetworkSettings network
settings related to the network
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.
NetworkGameListStatus status
Stats of the server.
NetworkGameInfo info
The game information of this server.
GRFConfigList grfconfig
List of NewGRF files used.
std::string server_invite_code_secret
Secret to proof we got this invite code from the Game Coordinator.
ServerGameType server_game_type
Server type: local / public / invite-only.
UseRelayService use_relay_service
Use relay service?
uint16_t server_port
port the server listens on
std::string server_invite_code
Invite code to use when registering as server.
Internal entity of a packet.
Definition packet.h:43
uint16_t Recv_uint16()
Read a 16 bits integer from the packet.
Definition packet.cpp:332
uint32_t Recv_uint32()
Read a 32 bits integer from the packet.
Definition packet.cpp:347
uint8_t Recv_uint8()
Read a 8 bits integer from the packet.
Definition packet.cpp:318
std::string Recv_string(size_t length, StringValidationSettings settings=SVS_REPLACE_WITH_QUESTION_MARK)
Reads characters (bytes) from the packet until it finds a '\0', or reaches a maximum of length charac...
Definition packet.cpp:425
@ PACKET_COORDINATOR_SERCLI_CONNECT_FAILED
Client/server tells the Game Coordinator the current connection attempt failed.
@ PACKET_COORDINATOR_SERCLI_STUN_RESULT
Client/server informs the Game Coordinator of the result of the STUN request.
@ PACKET_COORDINATOR_CLIENT_LISTING
Client is requesting a listing of all public servers.
@ PACKET_COORDINATOR_SERVER_REGISTER
Server registration.
@ PACKET_COORDINATOR_CLIENT_CONNECT
Client wants to connect to a server based on an invite code.
@ PACKET_COORDINATOR_CLIENT_CONNECTED
Client informs the Game Coordinator the connection with the server is established.
@ PACKET_COORDINATOR_SERVER_UPDATE
Server sends an set intervals an update of the server.
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:1155
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3106
@ WC_NETWORK_ASK_RELAY
Network ask relay window; Window numbers:
@ WC_CLIENT_LIST
Client list; Window numbers: