14#include "../settings_type.h"
15#include "../strings_func.h"
16#include "../window_func.h"
17#include "../window_type.h"
25#include "table/strings.h"
27#include "../safeguards.h"
131 Debug(net, 9,
"Coordinator::Receive_GC_ERROR({}, {})", error, detail);
150 connecter_pre_it->second->SetFailure();
172 Debug(net, 0,
"Invalid error type {} received from Game Coordinator", error);
181 this->
next_update = std::chrono::steady_clock::now();
205 std::string connection_type;
213 default: connection_type =
"Unknown";
break;
216 std::string game_type;
218 case SERVER_GAME_TYPE_INVITE_ONLY: game_type =
"Invite only";
break;
219 case SERVER_GAME_TYPE_PUBLIC: game_type =
"Public";
break;
221 case SERVER_GAME_TYPE_LOCAL:
222 default: game_type =
"Unknown";
break;
225 Debug(net, 3,
"----------------------------------------");
226 Debug(net, 3,
"Your server is now registered with the Game Coordinator:");
227 Debug(net, 3,
" Game type: {}", game_type);
228 Debug(net, 3,
" Connection type: {}", connection_type);
230 Debug(net, 3,
"----------------------------------------");
241 Debug(net, 9,
"Coordinator::Receive_GC_LISTING({})", servers);
249 for (; servers > 0; servers--) {
275 Debug(net, 9,
"Coordinator::Receive_GC_CONNECTING({}, {})", token, invite_code);
278 auto connecter_pre_it = this->
connecter_pre.find(invite_code);
285 this->
connecter[token] = {invite_code, connecter_pre_it->second};
294 Debug(net, 9,
"Coordinator::Receive_GC_CONNECT_FAILED({})", token);
306 Debug(net, 9,
"Coordinator::Receive_GC_DIRECT_CONNECT({}, {}, {}, {})", token, tracking_number, hostname, port);
314 this->
game_connecter = TCPConnecter::Create<NetworkDirectConnecter>(hostname, port, std::move(token), tracking_number);
321 Debug(net, 9,
"Coordinator::Receive_GC_STUN_REQUEST({})", token);
339 auto family_it = stun_it->second.find(family);
340 if (family_it == stun_it->second.end())
return true;
352 family_it->second->CloseConnection(
false);
358 this->
game_connecter = TCPConnecter::Create<NetworkReuseStunConnecter>(host, port, family_it->second->local_addr, std::move(token), tracking_number, family);
368 for (; newgrfs> 0; newgrfs--) {
381 Debug(net, 9,
"Coordinator::Receive_GC_TURN_CONNECT({}, {}, {}, {})", token, tracking_number, ticket, connection_string);
392 auto connecter_it = this->
connecter.find(token);
393 if (connecter_it == this->
connecter.end()) {
405 ShowNetworkAskRelay(connecter_it->second.first, std::move(connection_string), std::move(token));
409 this->StartTurnConnection(token);
413 this->StartTurnConnection(token);
419void ClientNetworkCoordinatorSocketHandler::StartTurnConnection(std::string_view token)
424 turn_it->second->Connect();
427void ClientNetworkCoordinatorSocketHandler::Connect()
490 Debug(net, 6,
"Sending server update to Game Coordinator");
496 Debug(net, 9,
"Coordinator::SendServerUpdate()");
514 p->Send_string(_openttd_revision);
528 assert(invite_code.starts_with(
"+"));
530 if (this->
connecter_pre.find(invite_code) != this->connecter_pre.end()) {
546 p->Send_string(invite_code);
548 Debug(net, 9,
"Coordinator::SendConnectToClient({})", invite_code);
564 p->Send_string(token);
565 p->Send_uint8(tracking_number);
567 Debug(net, 9,
"Coordinator::SendConnectFailure({}, {})", token, tracking_number);
582 assert(
sock != INVALID_SOCKET);
588 if (!ServerNetworkGameSocketHandler::ValidateClient(
sock, address))
return;
596 p->Send_string(token);
597 Debug(net, 9,
"Coordinator::SendConnectSuccess({})", token);
603 auto connecter_it = this->
connecter.find(token);
604 if (connecter_it != this->
connecter.end()) {
605 connecter_it->second.second->SetConnected(
sock);
625 p->Send_string(token);
626 p->Send_uint8(family);
627 p->Send_bool(result);
628 Debug(net, 9,
"Coordinator::SendStunResult({}, {}, {})", token, family, result);
642 if (family == AF_UNSPEC) {
643 for (
auto &[family, stun_handler] : stun_it->second) {
644 stun_handler->CloseConnection();
645 stun_handler->CloseSocket();
650 auto family_it = stun_it->second.find(family);
651 if (family_it == stun_it->second.end())
return;
653 family_it->second->CloseConnection();
654 family_it->second->CloseSocket();
656 stun_it->second.erase(family_it);
671 turn_it->second->CloseConnection();
672 turn_it->second->CloseSocket();
690 auto connecter_it = this->
connecter.find(token);
691 if (connecter_it != this->
connecter.end()) {
692 connecter_it->second.second->SetFailure();
709 for (
auto &[token, it] : this->
connecter) {
712 it.second->SetFailure();
736 if (this->
sock != INVALID_SOCKET) {
742 static int last_attempt_backoff = 1;
743 static bool first_reconnect =
true;
745 if (this->
sock == INVALID_SOCKET) {
746 static std::chrono::steady_clock::time_point last_attempt = {};
753 if (std::chrono::steady_clock::now() < last_attempt + std::chrono::seconds(1) * last_attempt_backoff)
return;
755 last_attempt = std::chrono::steady_clock::now();
757 if (last_attempt_backoff < 32) {
758 last_attempt_backoff *= 2;
765 if (first_reconnect) {
766 first_reconnect =
false;
770 Debug(net, 1,
"Connection with Game Coordinator lost; reconnecting...");
775 last_attempt_backoff = 1;
776 first_reconnect =
true;
796 for (
const auto &[family, stun_handler] : families) {
797 stun_handler->SendReceive();
803 if (turn_it->second->connect_started && turn_it->second->connecter ==
nullptr && !turn_it->second->IsConnected()) {
810 for (
const auto &[token, turn_handler] : this->
turn_handlers) {
811 turn_handler->SendReceive();
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.
void CloseTurnHandler(std::string_view token)
Close the TURN handler.
std::map< std::string, std::unique_ptr< ClientNetworkTurnSocketHandler >, std::less<> > turn_handlers
Pending TURN handler (if any), stored by token.
bool Receive_GC_REGISTER_ACK(Packet &p) override
Game Coordinator acknowledges the registration.
void Register()
Register our server to receive our invite code.
void ConnectToServer(std::string_view invite_code, TCPServerConnecter *connecter)
Join a server based on an invite code.
void CloseStunHandler(std::string_view token, uint8_t family=AF_UNSPEC)
Close the STUN handler.
std::map< std::string, std::pair< std::string, TCPServerConnecter * >, std::less<> > connecter
Based on tokens, the current (invite-code, connecter) that are pending.
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.
std::map< std::string, TCPServerConnecter *, std::less<> > connecter_pre
Based on invite codes, the current connecters that are pending.
bool Receive_GC_LISTING(Packet &p) override
Game Coordinator replies with a list of all public servers.
void ConnectFailure(std::string_view token, uint8_t tracking_number)
Callback from a Connecter to let the Game Coordinator know the connection failed.
void CloseToken(std::string_view token)
Close everything related to this connection token.
std::chrono::steady_clock::time_point last_activity
The last time there was network activity.
void StunResult(std::string_view token, uint8_t family, bool result)
Callback from the STUN connecter to inform the Game Coordinator about the result of the STUN.
bool 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.
void ConnectSuccess(std::string_view token, SOCKET sock, NetworkAddress &address)
Callback from a Connecter to let the Game Coordinator know the connection to the game server is estab...
bool 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.
bool Receive_GC_ERROR(Packet &p) override
Game Coordinator indicates there was an error.
std::map< std::string, std::map< int, std::unique_ptr< ClientNetworkStunSocketHandler > >, std::less<> > stun_handlers
All pending STUN handlers, stored by token:family.
std::shared_ptr< TCPConnecter > game_connecter
Pending connecter to the game server.
void GetListing()
Request a listing of all public servers.
static std::unique_ptr< ClientNetworkStunSocketHandler > Stun(std::string_view token, uint8_t family)
Send a STUN packet to the STUN server.
static std::unique_ptr< ClientNetworkTurnSocketHandler > Turn(std::string_view token, uint8_t tracking_number, std::string_view ticket, std::string_view connection_string)
Prepare a TURN connection.
Wrapper for (un)resolved network addresses; there's no reason to transform a numeric IP to a string a...
static NetworkAddress GetPeerAddress(SOCKET sock)
Get the peer address of a socket as NetworkAddress.
const std::string & GetHostname()
Get the hostname; in case it wasn't given the IPv4 dotted representation is given.
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(std::string_view connection_string)
Initiate the connecting.
bool ReceivePackets()
Receive a packet at TCP level.
Connect to a game server by IP:port.
uint8_t tracking_number
Tracking number of this connection.
void OnFailure() override
Callback for when the connection attempt failed.
NetworkDirectConnecter(std::string_view hostname, uint16_t port, std::string &&token, uint8_t tracking_number)
Try to establish a direct (hostname:port based) connection.
void OnConnect(SOCKET s) override
Callback when the connection succeeded.
std::string token
Token of this connection.
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.
NetworkReuseStunConnecter(std::string_view hostname, uint16_t port, const NetworkAddress &bind_address, std::string &&token, uint8_t tracking_number, uint8_t family)
Try to establish a STUN-based connection.
void OnFailure() override
Callback for when the connection attempt failed.
uint8_t family
Family of this connection.
std::string token
Token of this connection.
void Reopen()
Reopen the socket so we can send/receive stuff again.
virtual NetworkRecvStatus CloseConnection(bool error=true)
This will put this socket handler in a close state.
SOCKET sock
The socket currently connected to.
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.
SendPacketsState SendPackets(bool closing_down=false)
Sends all the buffered packets out for this client.
void CloseSocket()
Close the actual socket of the connection.
bool CanSendReceive()
Check whether this socket can send or receive something.
static std::string_view GetName()
Get the name used by the listener.
static void AcceptConnection(SOCKET s, const NetworkAddress &address)
Handle the accepting of a connection to the server.
"Helper" class for creating TCP connections in a non-blocking manner
NetworkAddress bind_address
Address we're binding to, if any.
std::string connection_string
Current address we are connecting to (before resolving).
std::string_view NetworkCoordinatorConnectionString()
Get the connection string for the game coordinator from the environment variable OTTD_COORDINATOR_CS,...
static const uint NETWORK_INVITE_CODE_SECRET_LENGTH
The maximum length of the invite code secret, in bytes including '\0'.
static const size_t TCP_MTU
Number of bytes we can pack in a single TCP packet.
static const uint16_t NETWORK_COORDINATOR_SERVER_PORT
The default port of the Game Coordinator server (TCP)
static const uint8_t NETWORK_COORDINATOR_VERSION
What version of game-coordinator-protocol do we use?
static const uint NETWORK_INVITE_CODE_LENGTH
The maximum length of the invite code, in bytes including '\0'.
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...
static const uint NETWORK_HOSTNAME_LENGTH
The maximum length of the host name, in bytes including '\0'.
static const uint NETWORK_TOKEN_LENGTH
The maximum length of a token, in bytes including '\0'.
static const uint NETWORK_ERROR_DETAIL_LENGTH
The maximum length of the error detail, in bytes including '\0'.
static const uint8_t NETWORK_GAME_INFO_VERSION
What version of game-info do we use?
NetworkRecvStatus
Status of a network client; reasons why a client has quit.
@ NETWORK_RECV_STATUS_OKAY
Everything is okay.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
@ WL_ERROR
Errors (eg. saving/loading failed)
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
uint32_t _frame_counter
The current frame.
bool _network_dedicated
are we a dedicated server?
bool _network_server
network-server is active
Basic functions/variables used all over the place.
ConnectionType _network_server_connection_type
What type of connection the Game Coordinator detected we are on.
ClientNetworkCoordinatorSocketHandler _network_coordinator_client
The connection to the Game Coordinator.
static const auto NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES
How many time between updates the server sends to the Game Coordinator.
std::string _network_server_invite_code
Our invite code as indicated by the Game Coordinator.
Part of the network protocol handling Game Coordinator requests.
void SerializeNetworkGameInfo(Packet &p, const NetworkServerGameInfo &info, bool send_newgrf_names)
Serializes the NetworkGameInfo struct to the packet.
void CheckGameCompatibility(NetworkGameInfo &ngi)
Check if an game entry is compatible with our client.
const NetworkServerGameInfo & GetCurrentNetworkServerGameInfo()
Get the NetworkServerGameInfo structure with the latest information of the server.
void DeserializeGRFIdentifierWithName(Packet &p, NamedGRFIdentifier &grf)
Deserializes the NamedGRFIdentifier (GRF ID, MD5 checksum and name) from the packet.
void DeserializeNetworkGameInfo(Packet &p, NetworkGameInfo &info, const GameInfoNewGRFLookupTable *newgrf_lookup_table)
Deserializes the NetworkGameInfo struct from the packet.
NetworkGame * NetworkGameListAddItem(std::string_view connection_string)
Add a new item to the linked gamelist.
void NetworkGameListRemoveExpired()
Remove all servers that have not recently been updated.
int _network_game_list_version
Current version of all items in the list.
Handling of the list of games.
@ NGLS_ONLINE
Server is online.
@ NGLS_OFFLINE
Server is offline (or cannot be queried).
void UpdateNetworkGameWindow()
Update the network new window because a new server is found on the network.
void ShowNetworkAskRelay(std::string_view server_connection_string, std::string &&relay_connection_string, std::string &&token)
Show a modal confirmation window with "no" / "yes, once" / "yes, always" buttons.
GUIs related to networking.
@ NRWCD_HANDLED
Relay request is handled, either by user or by timeout.
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.
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
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.
NetworkGameInfo info
The game information of this server.
NetworkGameStatus status
Stats of the 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.
uint16_t Recv_uint16()
Read a 16 bits integer from the packet.
uint32_t Recv_uint32()
Read a 32 bits integer from the packet.
std::string Recv_string(size_t length, StringValidationSettings settings=StringValidationSetting::ReplaceWithQuestionMark)
Reads characters (bytes) from the packet until it finds a '\0', or reaches a maximum of length charac...
uint8_t Recv_uint8()
Read a 8 bits integer from the packet.
@ 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.
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
@ WC_NETWORK_ASK_RELAY
Network ask relay window; Window numbers:
@ WC_CLIENT_LIST
Client list; Window numbers: