OpenTTD Source  20241120-master-g6d3adc6169
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 
29 static const auto NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES = std::chrono::seconds(30);
32 std::string _network_server_invite_code = "";
33 
36 private:
37  std::string token;
38  uint8_t tracking_number;
39 
40 public:
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 
64 private:
65  std::string token;
66  uint8_t tracking_number;
67  uint8_t family;
68 
69 public:
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),
83  family(family)
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 
104 public:
110 
111  void OnFailure() override
112  {
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();
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. */
154  NetworkGameList *item = NetworkGameListAddItem(detail);
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  /* Read the NetworkGameInfo from the packet. */
247  NetworkGameInfo ngi = {};
248  DeserializeNetworkGameInfo(p, ngi, &this->newgrf_lookup_table);
249 
250  /* Now we know the connection string, we can add it to our list. */
251  NetworkGameList *item = NetworkGameListAddItem(connection_string);
252 
253  /* Clear any existing GRFConfig chain. */
255  /* Copy the new NetworkGameInfo info. */
256  item->info = ngi;
257  /* Check for compatability with the client. */
258  CheckGameCompatibility(item->info);
259  /* Mark server as online. */
260  item->status = NGLS_ONLINE;
261  /* Mark the item as up-to-date. */
263  }
264 
266  return true;
267 }
268 
270 {
271  std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
272  std::string invite_code = p.Recv_string(NETWORK_INVITE_CODE_LENGTH);
273 
274  /* Find the connecter based on the invite code. */
275  auto connecter_pre_it = this->connecter_pre.find(invite_code);
276  if (connecter_pre_it == this->connecter_pre.end()) {
277  this->CloseConnection();
278  return false;
279  }
280 
281  /* Now store it based on the token. */
282  this->connecter[token] = {invite_code, connecter_pre_it->second};
283  this->connecter_pre.erase(connecter_pre_it);
284 
285  return true;
286 }
287 
289 {
290  std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
291  this->CloseToken(token);
292 
293  return true;
294 }
295 
297 {
298  std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
299  uint8_t tracking_number = p.Recv_uint8();
300  std::string hostname = p.Recv_string(NETWORK_HOSTNAME_LENGTH);
301  uint16_t port = p.Recv_uint16();
302 
303  /* Ensure all other pending connection attempts are killed. */
304  if (this->game_connecter != nullptr) {
305  this->game_connecter->Kill();
306  this->game_connecter = nullptr;
307  }
308 
309  this->game_connecter = TCPConnecter::Create<NetworkDirectConnecter>(hostname, port, token, tracking_number);
310  return true;
311 }
312 
314 {
315  std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
316 
317  this->stun_handlers[token][AF_INET6] = ClientNetworkStunSocketHandler::Stun(token, AF_INET6);
318  this->stun_handlers[token][AF_INET] = ClientNetworkStunSocketHandler::Stun(token, AF_INET);
319  return true;
320 }
321 
323 {
324  std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
325  uint8_t tracking_number = p.Recv_uint8();
326  uint8_t family = p.Recv_uint8();
327  std::string host = p.Recv_string(NETWORK_HOSTNAME_PORT_LENGTH);
328  uint16_t port = p.Recv_uint16();
329 
330  /* Check if we know this token. */
331  auto stun_it = this->stun_handlers.find(token);
332  if (stun_it == this->stun_handlers.end()) return true;
333  auto family_it = stun_it->second.find(family);
334  if (family_it == stun_it->second.end()) return true;
335 
336  /* Ensure all other pending connection attempts are killed. */
337  if (this->game_connecter != nullptr) {
338  this->game_connecter->Kill();
339  this->game_connecter = nullptr;
340  }
341 
342  /* We now mark the connection as closed, but we do not really close the
343  * socket yet. We do this when the NetworkReuseStunConnecter is connected.
344  * This prevents any NAT to already remove the route while we create the
345  * second connection on top of the first. */
346  family_it->second->CloseConnection(false);
347 
348  /* Connect to our peer from the same local address as we use for the
349  * STUN server. This means that if there is any NAT in the local network,
350  * the public ip:port is still pointing to the local address, and as such
351  * a connection can be established. */
352  this->game_connecter = TCPConnecter::Create<NetworkReuseStunConnecter>(host, port, family_it->second->local_addr, token, tracking_number, family);
353  return true;
354 }
355 
357 {
359 
360  uint16_t newgrfs = p.Recv_uint16();
361  for (; newgrfs> 0; newgrfs--) {
362  uint32_t index = p.Recv_uint32();
363  DeserializeGRFIdentifierWithName(p, this->newgrf_lookup_table[index]);
364  }
365  return true;
366 }
367 
369 {
370  std::string token = p.Recv_string(NETWORK_TOKEN_LENGTH);
371  uint8_t tracking_number = p.Recv_uint8();
372  std::string ticket = p.Recv_string(NETWORK_TOKEN_LENGTH);
373  std::string connection_string = p.Recv_string(NETWORK_HOSTNAME_PORT_LENGTH);
374 
375  /* Ensure all other pending connection attempts are killed. */
376  if (this->game_connecter != nullptr) {
377  this->game_connecter->Kill();
378  this->game_connecter = nullptr;
379  }
380 
381  this->turn_handlers[token] = ClientNetworkTurnSocketHandler::Turn(token, tracking_number, ticket, connection_string);
382 
383  if (!_network_server) {
384  auto connecter_it = this->connecter.find(token);
385  if (connecter_it == this->connecter.end()) {
386  /* Make sure we are still interested in connecting to this server. */
387  this->ConnectFailure(token, 0);
388  return true;
389  }
390 
392  case URS_NEVER:
393  this->ConnectFailure(token, 0);
394  break;
395 
396  case URS_ASK:
397  ShowNetworkAskRelay(connecter_it->second.first, connection_string, token);
398  break;
399 
400  case URS_ALLOW:
401  this->StartTurnConnection(token);
402  break;
403  }
404  } else {
405  this->StartTurnConnection(token);
406  }
407 
408  return true;
409 }
410 
411 void ClientNetworkCoordinatorSocketHandler::StartTurnConnection(std::string &token)
412 {
413  auto turn_it = this->turn_handlers.find(token);
414  if (turn_it == this->turn_handlers.end()) return;
415 
416  turn_it->second->Connect();
417 }
418 
419 void ClientNetworkCoordinatorSocketHandler::Connect()
420 {
421  /* We are either already connected or are trying to connect. */
422  if (this->sock != INVALID_SOCKET || this->connecting) return;
423 
424  this->Reopen();
425 
426  this->connecting = true;
427  this->last_activity = std::chrono::steady_clock::now();
428 
429  TCPConnecter::Create<NetworkCoordinatorConnecter>(NetworkCoordinatorConnectionString());
430 }
431 
433 {
435 
436  this->CloseSocket();
437  this->connecting = false;
438 
440  this->next_update = {};
441 
442  this->CloseAllConnections();
443 
445 
447 }
448 
453 {
455  this->next_update = {};
456 
458 
459  this->Connect();
460 
461  auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_SERVER_REGISTER);
462  p->Send_uint8(NETWORK_COORDINATOR_VERSION);
464  p->Send_uint16(_settings_client.network.server_port);
466  p->Send_string("");
467  p->Send_string("");
468  } else {
471  }
472 
473  this->SendPacket(std::move(p));
474 }
475 
480 {
481  Debug(net, 6, "Sending server update to Game Coordinator");
482 
483  auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_SERVER_UPDATE, TCP_MTU);
484  p->Send_uint8(NETWORK_COORDINATOR_VERSION);
485  SerializeNetworkGameInfo(*p, GetCurrentNetworkServerGameInfo(), this->next_update.time_since_epoch() != std::chrono::nanoseconds::zero());
486 
487  this->SendPacket(std::move(p));
488 
489  this->next_update = std::chrono::steady_clock::now() + NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES;
490 }
491 
496 {
497  this->Connect();
498 
500 
501  auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_CLIENT_LISTING);
502  p->Send_uint8(NETWORK_COORDINATOR_VERSION);
503  p->Send_uint8(NETWORK_GAME_INFO_VERSION);
504  p->Send_string(_openttd_revision);
505  p->Send_uint32(this->newgrf_lookup_table_cursor);
506 
507  this->SendPacket(std::move(p));
508 }
509 
515 void ClientNetworkCoordinatorSocketHandler::ConnectToServer(const std::string &invite_code, TCPServerConnecter *connecter)
516 {
517  assert(invite_code.starts_with("+"));
518 
519  if (this->connecter_pre.find(invite_code) != this->connecter_pre.end()) {
520  /* If someone is hammering the refresh key, one can sent out two
521  * requests for the same invite code. There isn't really a great way
522  * of handling this, so just ignore this request. */
523  connecter->SetFailure();
524  return;
525  }
526 
527  /* Initially we store based on invite code; on first reply we know the
528  * token, and will start using that key instead. */
529  this->connecter_pre[invite_code] = connecter;
530 
531  this->Connect();
532 
533  auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_CLIENT_CONNECT);
534  p->Send_uint8(NETWORK_COORDINATOR_VERSION);
535  p->Send_string(invite_code);
536 
537  this->SendPacket(std::move(p));
538 }
539 
545 void ClientNetworkCoordinatorSocketHandler::ConnectFailure(const std::string &token, uint8_t tracking_number)
546 {
547  /* Connecter will destroy itself. */
548  this->game_connecter = nullptr;
549 
550  auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_SERCLI_CONNECT_FAILED);
551  p->Send_uint8(NETWORK_COORDINATOR_VERSION);
552  p->Send_string(token);
553  p->Send_uint8(tracking_number);
554 
555  this->SendPacket(std::move(p));
556 
557  /* We do not close the associated connecter here yet, as the
558  * Game Coordinator might have other methods of connecting available. */
559 }
560 
567 void ClientNetworkCoordinatorSocketHandler::ConnectSuccess(const std::string &token, SOCKET sock, NetworkAddress &address)
568 {
569  assert(sock != INVALID_SOCKET);
570 
571  /* Connecter will destroy itself. */
572  this->game_connecter = nullptr;
573 
574  if (_network_server) {
575  if (!ServerNetworkGameSocketHandler::ValidateClient(sock, address)) return;
576  Debug(net, 3, "[{}] Client connected from {} on frame {}", ServerNetworkGameSocketHandler::GetName(), address.GetHostname(), _frame_counter);
578  } else {
579  /* The client informs the Game Coordinator about the success. The server
580  * doesn't have to, as it is implied by the client telling. */
581  auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_CLIENT_CONNECTED);
582  p->Send_uint8(NETWORK_COORDINATOR_VERSION);
583  p->Send_string(token);
584  this->SendPacket(std::move(p));
585 
586  /* Find the connecter; it can happen it no longer exist, in cases where
587  * we aborted the connect but the Game Coordinator was already in the
588  * processes of connecting us. */
589  auto connecter_it = this->connecter.find(token);
590  if (connecter_it != this->connecter.end()) {
591  connecter_it->second.second->SetConnected(sock);
592  this->connecter.erase(connecter_it);
593  }
594  }
595 
596  /* Close all remaining connections. */
597  this->CloseToken(token);
598 }
599 
607 void ClientNetworkCoordinatorSocketHandler::StunResult(const std::string &token, uint8_t family, bool result)
608 {
609  auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_SERCLI_STUN_RESULT);
610  p->Send_uint8(NETWORK_COORDINATOR_VERSION);
611  p->Send_string(token);
612  p->Send_uint8(family);
613  p->Send_bool(result);
614  this->SendPacket(std::move(p));
615 }
616 
622 void ClientNetworkCoordinatorSocketHandler::CloseStunHandler(const std::string &token, uint8_t family)
623 {
624  auto stun_it = this->stun_handlers.find(token);
625  if (stun_it == this->stun_handlers.end()) return;
626 
627  if (family == AF_UNSPEC) {
628  for (auto &[family, stun_handler] : stun_it->second) {
629  stun_handler->CloseConnection();
630  stun_handler->CloseSocket();
631  }
632 
633  this->stun_handlers.erase(stun_it);
634  } else {
635  auto family_it = stun_it->second.find(family);
636  if (family_it == stun_it->second.end()) return;
637 
638  family_it->second->CloseConnection();
639  family_it->second->CloseSocket();
640 
641  stun_it->second.erase(family_it);
642  }
643 }
644 
650 {
652 
653  auto turn_it = this->turn_handlers.find(token);
654  if (turn_it == this->turn_handlers.end()) return;
655 
656  turn_it->second->CloseConnection();
657  turn_it->second->CloseSocket();
658 
659  /* We don't remove turn_handler here, as we can be called from within that
660  * turn_handler instance, so our object cannot be free'd yet. Instead, we
661  * check later if the connection is closed, and free the object then. */
662 }
663 
669 {
670  /* Close all remaining STUN / TURN connections. */
671  this->CloseStunHandler(token);
672  this->CloseTurnHandler(token);
673 
674  /* Close the caller of the connection attempt. */
675  auto connecter_it = this->connecter.find(token);
676  if (connecter_it != this->connecter.end()) {
677  connecter_it->second.second->SetFailure();
678  this->connecter.erase(connecter_it);
679  }
680 }
681 
686 {
687  /* Ensure all other pending connection attempts are also killed. */
688  if (this->game_connecter != nullptr) {
689  this->game_connecter->Kill();
690  this->game_connecter = nullptr;
691  }
692 
693  /* Mark any pending connecters as failed. */
694  for (auto &[token, it] : this->connecter) {
695  this->CloseStunHandler(token);
696  this->CloseTurnHandler(token);
697  it.second->SetFailure();
698 
699  /* Inform the Game Coordinator he can stop trying to connect us to the server. */
700  this->ConnectFailure(token, 0);
701  }
702  this->stun_handlers.clear();
703  this->turn_handlers.clear();
704  this->connecter.clear();
705 
706  /* Also close any pending invite-code requests. */
707  for (auto &[invite_code, it] : this->connecter_pre) {
708  it->SetFailure();
709  }
710  this->connecter_pre.clear();
711 }
712 
718 {
719  /* Private games are not listed via the Game Coordinator. */
720  if (_network_server && _settings_client.network.server_game_type == SERVER_GAME_TYPE_LOCAL) {
721  if (this->sock != INVALID_SOCKET) {
722  this->CloseConnection();
723  }
724  return;
725  }
726 
727  static int last_attempt_backoff = 1;
728  static bool first_reconnect = true;
729 
730  if (this->sock == INVALID_SOCKET) {
731  static std::chrono::steady_clock::time_point last_attempt = {};
732 
733  /* Don't auto-reconnect when we are not a server. */
734  if (!_network_server) return;
735  /* Don't reconnect if we are connecting. */
736  if (this->connecting) return;
737  /* Throttle how often we try to reconnect. */
738  if (std::chrono::steady_clock::now() < last_attempt + std::chrono::seconds(1) * last_attempt_backoff) return;
739 
740  last_attempt = std::chrono::steady_clock::now();
741  /* Delay reconnecting with up to 32 seconds. */
742  if (last_attempt_backoff < 32) {
743  last_attempt_backoff *= 2;
744  }
745 
746  /* Do not reconnect on the first attempt, but only initialize the
747  * last_attempt variables. Otherwise after an outage all servers
748  * reconnect at the same time, potentially overwhelming the
749  * Game Coordinator. */
750  if (first_reconnect) {
751  first_reconnect = false;
752  return;
753  }
754 
755  Debug(net, 1, "Connection with Game Coordinator lost; reconnecting...");
756  this->Register();
757  return;
758  }
759 
760  last_attempt_backoff = 1;
761  first_reconnect = true;
762 
763  if (_network_server && _network_server_connection_type != CONNECTION_TYPE_UNKNOWN && std::chrono::steady_clock::now() > this->next_update) {
764  this->SendServerUpdate();
765  }
766 
767  if (!_network_server && std::chrono::steady_clock::now() > this->last_activity + IDLE_TIMEOUT) {
768  this->CloseConnection();
769  return;
770  }
771 
772  if (this->CanSendReceive()) {
773  if (this->ReceivePackets()) {
774  this->last_activity = std::chrono::steady_clock::now();
775  }
776  }
777 
778  this->SendPackets();
779 
780  for (const auto &[token, families] : this->stun_handlers) {
781  for (const auto &[family, stun_handler] : families) {
782  stun_handler->SendReceive();
783  }
784  }
785 
786  /* Check for handlers that are not connecting nor connected. Destroy those objects. */
787  for (auto turn_it = this->turn_handlers.begin(); turn_it != this->turn_handlers.end(); /* nothing */) {
788  if (turn_it->second->connect_started && turn_it->second->connecter == nullptr && !turn_it->second->IsConnected()) {
789  turn_it = this->turn_handlers.erase(turn_it);
790  } else {
791  turn_it++;
792  }
793  }
794 
795  for (const auto &[token, turn_handler] : this->turn_handlers) {
796  turn_handler->SendReceive();
797  }
798 }
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 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.
std::string token
Token of this connection.
Connecter used after STUN exchange to connect from both sides to each other.
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:51
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:68
SendPacketsState SendPackets(bool closing_down=false)
Sends all the buffered packets out for this client.
Definition: tcp.cpp:86
void CloseSocket()
Close the actual socket of the connection.
Definition: tcp.cpp:39
bool CanSendReceive()
Check whether this socket can send or receive something.
Definition: tcp.cpp:204
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:70
NetworkAddress bind_address
Address we're binding to, if any.
Definition: tcp.h:100
std::string connection_string
Current address we are connecting to (before resolving).
Definition: tcp.h:99
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
void ShowErrorMessage(StringID summary_msg, int x, int y, CommandCost cc)
Display an error message in a window.
Definition: error_gui.cpp:367
@ WL_ERROR
Errors (eg. saving/loading failed)
Definition: error.h:26
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.
NetworkGameList * NetworkGameListAddItem(const std::string &connection_string)
Add a new item to the linked gamelist.
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 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.
Definition: network_gui.cpp:66
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(GRFConfig **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)
Definition: strings_type.h:17
NetworkSettings network
settings related to the network
The game information that is sent from the server to the clients with extra information only required...
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.
GRFConfig * 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:1152
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition: window.cpp:3093
@ WC_NETWORK_ASK_RELAY
Network ask relay window; Window numbers:
Definition: window_type.h:497
@ WC_CLIENT_LIST
Client list; Window numbers:
Definition: window_type.h:484