OpenTTD Source 20250205-master-gfd85ab1e2c
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(STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED, INVALID_STRING_ID, 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(STR_NETWORK_ERROR_COORDINATOR_REUSE_OF_INVITE_CODE, INVALID_STRING_ID, 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
187 ShowErrorMessage(STR_NETWORK_ERROR_COORDINATOR_ISOLATED, STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL, WL_ERROR);
188 }
189
190 /* Users can change the invite code in the settings, but this has no effect
191 * on the invite code as assigned by the server. So
192 * _network_server_invite_code contains the current invite code,
193 * and _settings_client.network.server_invite_code contains the one we will
194 * attempt to re-use when registering again. */
196
198
199 if (_network_dedicated) {
200 std::string connection_type;
202 case CONNECTION_TYPE_ISOLATED: connection_type = "Remote players can't connect"; break;
203 case CONNECTION_TYPE_DIRECT: connection_type = "Public"; break;
204 case CONNECTION_TYPE_STUN: connection_type = "Behind NAT"; break;
205 case CONNECTION_TYPE_TURN: connection_type = "Via relay"; break;
206
207 case CONNECTION_TYPE_UNKNOWN: // Never returned from Game Coordinator.
208 default: connection_type = "Unknown"; break; // Should never happen, but don't fail if it does.
209 }
210
211 std::string game_type;
213 case SERVER_GAME_TYPE_INVITE_ONLY: game_type = "Invite only"; break;
214 case SERVER_GAME_TYPE_PUBLIC: game_type = "Public"; break;
215
216 case SERVER_GAME_TYPE_LOCAL: // Impossible to register local servers.
217 default: game_type = "Unknown"; break; // Should never happen, but don't fail if it does.
218 }
219
220 Debug(net, 3, "----------------------------------------");
221 Debug(net, 3, "Your server is now registered with the Game Coordinator:");
222 Debug(net, 3, " Game type: {}", game_type);
223 Debug(net, 3, " Connection type: {}", connection_type);
224 Debug(net, 3, " Invite code: {}", _network_server_invite_code);
225 Debug(net, 3, "----------------------------------------");
226 } else {
227 Debug(net, 3, "Game Coordinator registered our server with invite code '{}'", _network_server_invite_code);
228 }
229
230 return true;
231}
232
234{
235 uint8_t servers = p.Recv_uint16();
236
237 /* End of list; we can now remove all expired items from the list. */
238 if (servers == 0) {
240 return true;
241 }
242
243 for (; servers > 0; servers--) {
244 std::string connection_string = p.Recv_string(NETWORK_HOSTNAME_PORT_LENGTH);
245
246 /* Now we know the connection string, we can add it to our list. */
247 NetworkGameList *item = NetworkGameListAddItem(connection_string);
248
249 /* Clear any existing GRFConfig chain. */
251 /* Read the NetworkGameInfo from the packet. */
252 DeserializeNetworkGameInfo(p, item->info, &this->newgrf_lookup_table);
253 /* Check for compatability with the client. */
254 CheckGameCompatibility(item->info);
255 /* Mark server as online. */
256 item->status = NGLS_ONLINE;
257 /* Mark the item as up-to-date. */
259 }
260
262 return true;
263}
264
266{
267 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
268 std::string invite_code = p.Recv_string(NETWORK_INVITE_CODE_LENGTH);
269
270 /* Find the connecter based on the invite code. */
271 auto connecter_pre_it = this->connecter_pre.find(invite_code);
272 if (connecter_pre_it == this->connecter_pre.end()) {
273 this->CloseConnection();
274 return false;
275 }
276
277 /* Now store it based on the token. */
278 this->connecter[token] = {invite_code, connecter_pre_it->second};
279 this->connecter_pre.erase(connecter_pre_it);
280
281 return true;
282}
283
285{
286 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
287 this->CloseToken(token);
288
289 return true;
290}
291
293{
294 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
295 uint8_t tracking_number = p.Recv_uint8();
296 std::string hostname = p.Recv_string(NETWORK_HOSTNAME_LENGTH);
297 uint16_t port = p.Recv_uint16();
298
299 /* Ensure all other pending connection attempts are killed. */
300 if (this->game_connecter != nullptr) {
301 this->game_connecter->Kill();
302 this->game_connecter = nullptr;
303 }
304
305 this->game_connecter = TCPConnecter::Create<NetworkDirectConnecter>(hostname, port, token, tracking_number);
306 return true;
307}
308
310{
311 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
312
313 this->stun_handlers[token][AF_INET6] = ClientNetworkStunSocketHandler::Stun(token, AF_INET6);
314 this->stun_handlers[token][AF_INET] = ClientNetworkStunSocketHandler::Stun(token, AF_INET);
315 return true;
316}
317
319{
320 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
321 uint8_t tracking_number = p.Recv_uint8();
322 uint8_t family = p.Recv_uint8();
323 std::string host = p.Recv_string(NETWORK_HOSTNAME_PORT_LENGTH);
324 uint16_t port = p.Recv_uint16();
325
326 /* Check if we know this token. */
327 auto stun_it = this->stun_handlers.find(token);
328 if (stun_it == this->stun_handlers.end()) return true;
329 auto family_it = stun_it->second.find(family);
330 if (family_it == stun_it->second.end()) return true;
331
332 /* Ensure all other pending connection attempts are killed. */
333 if (this->game_connecter != nullptr) {
334 this->game_connecter->Kill();
335 this->game_connecter = nullptr;
336 }
337
338 /* We now mark the connection as closed, but we do not really close the
339 * socket yet. We do this when the NetworkReuseStunConnecter is connected.
340 * This prevents any NAT to already remove the route while we create the
341 * second connection on top of the first. */
342 family_it->second->CloseConnection(false);
343
344 /* Connect to our peer from the same local address as we use for the
345 * STUN server. This means that if there is any NAT in the local network,
346 * the public ip:port is still pointing to the local address, and as such
347 * a connection can be established. */
348 this->game_connecter = TCPConnecter::Create<NetworkReuseStunConnecter>(host, port, family_it->second->local_addr, token, tracking_number, family);
349 return true;
350}
351
353{
355
356 uint16_t newgrfs = p.Recv_uint16();
357 for (; newgrfs> 0; newgrfs--) {
358 uint32_t index = p.Recv_uint32();
359 DeserializeGRFIdentifierWithName(p, this->newgrf_lookup_table[index]);
360 }
361 return true;
362}
363
365{
366 std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
367 uint8_t tracking_number = p.Recv_uint8();
368 std::string ticket = p.Recv_string(NETWORK_TOKEN_LENGTH);
369 std::string connection_string = p.Recv_string(NETWORK_HOSTNAME_PORT_LENGTH);
370
371 /* Ensure all other pending connection attempts are killed. */
372 if (this->game_connecter != nullptr) {
373 this->game_connecter->Kill();
374 this->game_connecter = nullptr;
375 }
376
377 this->turn_handlers[token] = ClientNetworkTurnSocketHandler::Turn(token, tracking_number, ticket, connection_string);
378
379 if (!_network_server) {
380 auto connecter_it = this->connecter.find(token);
381 if (connecter_it == this->connecter.end()) {
382 /* Make sure we are still interested in connecting to this server. */
383 this->ConnectFailure(token, 0);
384 return true;
385 }
386
388 case URS_NEVER:
389 this->ConnectFailure(token, 0);
390 break;
391
392 case URS_ASK:
393 ShowNetworkAskRelay(connecter_it->second.first, connection_string, token);
394 break;
395
396 case URS_ALLOW:
397 this->StartTurnConnection(token);
398 break;
399 }
400 } else {
401 this->StartTurnConnection(token);
402 }
403
404 return true;
405}
406
407void ClientNetworkCoordinatorSocketHandler::StartTurnConnection(std::string &token)
408{
409 auto turn_it = this->turn_handlers.find(token);
410 if (turn_it == this->turn_handlers.end()) return;
411
412 turn_it->second->Connect();
413}
414
415void ClientNetworkCoordinatorSocketHandler::Connect()
416{
417 /* We are either already connected or are trying to connect. */
418 if (this->sock != INVALID_SOCKET || this->connecting) return;
419
420 this->Reopen();
421
422 this->connecting = true;
423 this->last_activity = std::chrono::steady_clock::now();
424
425 TCPConnecter::Create<NetworkCoordinatorConnecter>(NetworkCoordinatorConnectionString());
426}
427
444
449{
451 this->next_update = {};
452
454
455 this->Connect();
456
457 auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_SERVER_REGISTER);
458 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
460 p->Send_uint16(_settings_client.network.server_port);
462 p->Send_string("");
463 p->Send_string("");
464 } else {
467 }
468
469 this->SendPacket(std::move(p));
470}
471
476{
477 Debug(net, 6, "Sending server update to Game Coordinator");
478
479 auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_SERVER_UPDATE, TCP_MTU);
480 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
481 SerializeNetworkGameInfo(*p, GetCurrentNetworkServerGameInfo(), this->next_update.time_since_epoch() != std::chrono::nanoseconds::zero());
482
483 this->SendPacket(std::move(p));
484
485 this->next_update = std::chrono::steady_clock::now() + NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES;
486}
487
492{
493 this->Connect();
494
496
497 auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_CLIENT_LISTING);
498 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
499 p->Send_uint8(NETWORK_GAME_INFO_VERSION);
500 p->Send_string(_openttd_revision);
501 p->Send_uint32(this->newgrf_lookup_table_cursor);
502
503 this->SendPacket(std::move(p));
504}
505
512{
513 assert(invite_code.starts_with("+"));
514
515 if (this->connecter_pre.find(invite_code) != this->connecter_pre.end()) {
516 /* If someone is hammering the refresh key, one can sent out two
517 * requests for the same invite code. There isn't really a great way
518 * of handling this, so just ignore this request. */
519 connecter->SetFailure();
520 return;
521 }
522
523 /* Initially we store based on invite code; on first reply we know the
524 * token, and will start using that key instead. */
525 this->connecter_pre[invite_code] = connecter;
526
527 this->Connect();
528
529 auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_CLIENT_CONNECT);
530 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
531 p->Send_string(invite_code);
532
533 this->SendPacket(std::move(p));
534}
535
541void ClientNetworkCoordinatorSocketHandler::ConnectFailure(const std::string &token, uint8_t tracking_number)
542{
543 /* Connecter will destroy itself. */
544 this->game_connecter = nullptr;
545
546 auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_SERCLI_CONNECT_FAILED);
547 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
548 p->Send_string(token);
549 p->Send_uint8(tracking_number);
550
551 this->SendPacket(std::move(p));
552
553 /* We do not close the associated connecter here yet, as the
554 * Game Coordinator might have other methods of connecting available. */
555}
556
563void ClientNetworkCoordinatorSocketHandler::ConnectSuccess(const std::string &token, SOCKET sock, NetworkAddress &address)
564{
565 assert(sock != INVALID_SOCKET);
566
567 /* Connecter will destroy itself. */
568 this->game_connecter = nullptr;
569
570 if (_network_server) {
571 if (!ServerNetworkGameSocketHandler::ValidateClient(sock, address)) return;
572 Debug(net, 3, "[{}] Client connected from {} on frame {}", ServerNetworkGameSocketHandler::GetName(), address.GetHostname(), _frame_counter);
574 } else {
575 /* The client informs the Game Coordinator about the success. The server
576 * doesn't have to, as it is implied by the client telling. */
577 auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_CLIENT_CONNECTED);
578 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
579 p->Send_string(token);
580 this->SendPacket(std::move(p));
581
582 /* Find the connecter; it can happen it no longer exist, in cases where
583 * we aborted the connect but the Game Coordinator was already in the
584 * processes of connecting us. */
585 auto connecter_it = this->connecter.find(token);
586 if (connecter_it != this->connecter.end()) {
587 connecter_it->second.second->SetConnected(sock);
588 this->connecter.erase(connecter_it);
589 }
590 }
591
592 /* Close all remaining connections. */
593 this->CloseToken(token);
594}
595
603void ClientNetworkCoordinatorSocketHandler::StunResult(const std::string &token, uint8_t family, bool result)
604{
605 auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_SERCLI_STUN_RESULT);
606 p->Send_uint8(NETWORK_COORDINATOR_VERSION);
607 p->Send_string(token);
608 p->Send_uint8(family);
609 p->Send_bool(result);
610 this->SendPacket(std::move(p));
611}
612
618void ClientNetworkCoordinatorSocketHandler::CloseStunHandler(const std::string &token, uint8_t family)
619{
620 auto stun_it = this->stun_handlers.find(token);
621 if (stun_it == this->stun_handlers.end()) return;
622
623 if (family == AF_UNSPEC) {
624 for (auto &[family, stun_handler] : stun_it->second) {
625 stun_handler->CloseConnection();
626 stun_handler->CloseSocket();
627 }
628
629 this->stun_handlers.erase(stun_it);
630 } else {
631 auto family_it = stun_it->second.find(family);
632 if (family_it == stun_it->second.end()) return;
633
634 family_it->second->CloseConnection();
635 family_it->second->CloseSocket();
636
637 stun_it->second.erase(family_it);
638 }
639}
640
646{
648
649 auto turn_it = this->turn_handlers.find(token);
650 if (turn_it == this->turn_handlers.end()) return;
651
652 turn_it->second->CloseConnection();
653 turn_it->second->CloseSocket();
654
655 /* We don't remove turn_handler here, as we can be called from within that
656 * turn_handler instance, so our object cannot be free'd yet. Instead, we
657 * check later if the connection is closed, and free the object then. */
658}
659
665{
666 /* Close all remaining STUN / TURN connections. */
667 this->CloseStunHandler(token);
668 this->CloseTurnHandler(token);
669
670 /* Close the caller of the connection attempt. */
671 auto connecter_it = this->connecter.find(token);
672 if (connecter_it != this->connecter.end()) {
673 connecter_it->second.second->SetFailure();
674 this->connecter.erase(connecter_it);
675 }
676}
677
682{
683 /* Ensure all other pending connection attempts are also killed. */
684 if (this->game_connecter != nullptr) {
685 this->game_connecter->Kill();
686 this->game_connecter = nullptr;
687 }
688
689 /* Mark any pending connecters as failed. */
690 for (auto &[token, it] : this->connecter) {
691 this->CloseStunHandler(token);
692 this->CloseTurnHandler(token);
693 it.second->SetFailure();
694
695 /* Inform the Game Coordinator he can stop trying to connect us to the server. */
696 this->ConnectFailure(token, 0);
697 }
698 this->stun_handlers.clear();
699 this->turn_handlers.clear();
700 this->connecter.clear();
701
702 /* Also close any pending invite-code requests. */
703 for (auto &[invite_code, it] : this->connecter_pre) {
704 it->SetFailure();
705 }
706 this->connecter_pre.clear();
707}
708
714{
715 /* Private games are not listed via the Game Coordinator. */
716 if (_network_server && _settings_client.network.server_game_type == SERVER_GAME_TYPE_LOCAL) {
717 if (this->sock != INVALID_SOCKET) {
718 this->CloseConnection();
719 }
720 return;
721 }
722
723 static int last_attempt_backoff = 1;
724 static bool first_reconnect = true;
725
726 if (this->sock == INVALID_SOCKET) {
727 static std::chrono::steady_clock::time_point last_attempt = {};
728
729 /* Don't auto-reconnect when we are not a server. */
730 if (!_network_server) return;
731 /* Don't reconnect if we are connecting. */
732 if (this->connecting) return;
733 /* Throttle how often we try to reconnect. */
734 if (std::chrono::steady_clock::now() < last_attempt + std::chrono::seconds(1) * last_attempt_backoff) return;
735
736 last_attempt = std::chrono::steady_clock::now();
737 /* Delay reconnecting with up to 32 seconds. */
738 if (last_attempt_backoff < 32) {
739 last_attempt_backoff *= 2;
740 }
741
742 /* Do not reconnect on the first attempt, but only initialize the
743 * last_attempt variables. Otherwise after an outage all servers
744 * reconnect at the same time, potentially overwhelming the
745 * Game Coordinator. */
746 if (first_reconnect) {
747 first_reconnect = false;
748 return;
749 }
750
751 Debug(net, 1, "Connection with Game Coordinator lost; reconnecting...");
752 this->Register();
753 return;
754 }
755
756 last_attempt_backoff = 1;
757 first_reconnect = true;
758
759 if (_network_server && _network_server_connection_type != CONNECTION_TYPE_UNKNOWN && std::chrono::steady_clock::now() > this->next_update) {
760 this->SendServerUpdate();
761 }
762
763 if (!_network_server && std::chrono::steady_clock::now() > this->last_activity + IDLE_TIMEOUT) {
764 this->CloseConnection();
765 return;
766 }
767
768 if (this->CanSendReceive()) {
769 if (this->ReceivePackets()) {
770 this->last_activity = std::chrono::steady_clock::now();
771 }
772 }
773
774 this->SendPackets();
775
776 for (const auto &[token, families] : this->stun_handlers) {
777 for (const auto &[family, stun_handler] : families) {
778 stun_handler->SendReceive();
779 }
780 }
781
782 /* Check for handlers that are not connecting nor connected. Destroy those objects. */
783 for (auto turn_it = this->turn_handlers.begin(); turn_it != this->turn_handlers.end(); /* nothing */) {
784 if (turn_it->second->connect_started && turn_it->second->connecter == nullptr && !turn_it->second->IsConnected()) {
785 turn_it = this->turn_handlers.erase(turn_it);
786 } else {
787 turn_it++;
788 }
789 }
790
791 for (const auto &[token, turn_handler] : this->turn_handlers) {
792 turn_handler->SendReceive();
793 }
794}
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:568
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,...)
Ouptut a line of debugging information.
Definition debug.h:37
@ WL_ERROR
Errors (eg. saving/loading failed)
Definition error.h:26
void ShowErrorMessage(StringID 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: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:56
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames)
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:42
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:1149
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3099
@ WC_NETWORK_ASK_RELAY
Network ask relay window; Window numbers:
@ WC_CLIENT_LIST
Client list; Window numbers: