Changeset - r25815:13efa809f0d9
[Not reviewed]
master
0 15 0
Patric Stout - 4 years ago 2021-04-29 13:37:02
truebrain@openttd.org
Feature: join servers based on their invite code

This removes the need to know a server IP to join it. Invite codes
are small (~7 characters) indentifiers for servers, which can be
exchanged with other players to join the servers.
15 files changed with 408 insertions and 15 deletions:
0 comments (0 inline, 0 general)
src/console_cmds.cpp
Show inline comments
 
@@ -702,6 +702,7 @@ DEF_CONSOLE_CMD(ConServerInfo)
 
		return true;
 
	}
 

	
 
	IConsolePrint(CC_DEFAULT, "Invite code:                {}", _network_server_invite_code);
 
	IConsolePrint(CC_DEFAULT, "Current/maximum clients:    {:3d}/{:3d}", _network_game_info.clients_on, _settings_client.network.max_clients);
 
	IConsolePrint(CC_DEFAULT, "Current/maximum companies:  {:3d}/{:3d}", Company::GetNumItems(), _settings_client.network.max_companies);
 
	IConsolePrint(CC_DEFAULT, "Current/maximum spectators: {:3d}/{:3d}", NetworkSpectatorCount(), _settings_client.network.max_spectators);
src/lang/english.txt
Show inline comments
 
@@ -2045,12 +2045,12 @@ STR_NETWORK_SERVER_LIST_SEARCH_SERVER_IN
 
STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN                       :{BLACK}Search LAN
 
STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN_TOOLTIP               :{BLACK}Search local area network for servers
 
STR_NETWORK_SERVER_LIST_ADD_SERVER                              :{BLACK}Add server
 
STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP                      :{BLACK}Adds a server to the list which will always be checked for running games
 
STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP                      :{BLACK}Adds a server to the list. This can either be a server address or an invite code
 
STR_NETWORK_SERVER_LIST_START_SERVER                            :{BLACK}Start server
 
STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP                    :{BLACK}Start your own server
 

	
 
STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE                    :{BLACK}Enter your name
 
STR_NETWORK_SERVER_LIST_ENTER_IP                                :{BLACK}Enter the address of the host
 
STR_NETWORK_SERVER_LIST_ENTER_SERVER_ADDRESS                    :{BLACK}Enter server address or invite code
 

	
 
# Start new multiplayer server
 
STR_NETWORK_START_SERVER_CAPTION                                :{WHITE}Start new multiplayer game
 
@@ -2136,6 +2136,8 @@ STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT
 
STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION               :Name of the server
 
STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY                       :{BLACK}Visibility
 
STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP               :{BLACK}Whether other people can see your server in the public listing
 
STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE                      :{BLACK}Invite code
 
STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE_TOOLTIP              :{BLACK}Invite code other players can use to join this server
 
STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE                  :{BLACK}Connection type
 
STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP          :{BLACK}Whether and how your server can be reached by others
 
STR_NETWORK_CLIENT_LIST_PLAYER                                  :{BLACK}Player
src/network/core/config.h
Show inline comments
 
@@ -47,7 +47,7 @@ static const uint16 COMPAT_MTU          
 
static const byte NETWORK_GAME_ADMIN_VERSION        =    1;           ///< What version of the admin network do we use?
 
static const byte NETWORK_GAME_INFO_VERSION         =    4;           ///< What version of game-info do we use?
 
static const byte NETWORK_COMPANY_INFO_VERSION      =    6;           ///< What version of company info is this?
 
static const byte NETWORK_COORDINATOR_VERSION       =    1;           ///< What version of game-coordinator-protocol do we use?
 
static const byte NETWORK_COORDINATOR_VERSION       =    2;           ///< What version of game-coordinator-protocol do we use?
 

	
 
static const uint NETWORK_NAME_LENGTH               =   80;           ///< The maximum length of the server name and map name, in bytes including '\0'
 
static const uint NETWORK_COMPANY_NAME_LENGTH       =  128;           ///< The maximum length of the company name, in bytes including '\0'
 
@@ -67,7 +67,10 @@ static const uint NETWORK_CONTENT_VERSIO
 
static const uint NETWORK_CONTENT_URL_LENGTH        =   96;           ///< The maximum length of a content's url, in bytes including '\0'.
 
static const uint NETWORK_CONTENT_DESC_LENGTH       =  512;           ///< The maximum length of a content's description, in bytes including '\0'.
 
static const uint NETWORK_CONTENT_TAG_LENGTH        =   32;           ///< The maximum length of a content's tag, in bytes including '\0'.
 
static const uint NETWORK_ERROR_DETAIL_LENGTH        = 100;           ///< The maximum length of the error detail, in bytes including '\0'
 
static const uint NETWORK_ERROR_DETAIL_LENGTH       =  100;           ///< The maximum length of the error detail, in bytes including '\0'.
 
static const uint NETWORK_INVITE_CODE_LENGTH        =   64;           ///< The maximum length of the invite code, in bytes including '\0'.
 
static const uint NETWORK_INVITE_CODE_SECRET_LENGTH =   80;           ///< The maximum length of the invite code secret, in bytes including '\0'.
 
static const uint NETWORK_TOKEN_LENGTH              =   64;           ///< The maximum length of a token, in bytes including '\0'.
 

	
 
static const uint NETWORK_GRF_NAME_LENGTH           =   80;           ///< Maximum length of the name of a GRF
 

	
src/network/core/game_info.cpp
Show inline comments
 
@@ -141,7 +141,11 @@ void FillStaticNetworkServerGameInfo()
 
 */
 
const NetworkServerGameInfo *GetCurrentNetworkServerGameInfo()
 
{
 
	/* Client_on is used as global variable to keep track on the number of clients. */
 
	/* These variables are updated inside _network_game_info as if they are global variables:
 
	 *  - clients_on
 
	 *  - invite_code
 
	 * These don't need to be updated manually here.
 
	 */
 
	_network_game_info.companies_on  = (byte)Company::GetNumItems();
 
	_network_game_info.spectators_on = NetworkSpectatorCount();
 
	_network_game_info.game_date     = _date;
src/network/core/tcp_connect.cpp
Show inline comments
 
@@ -13,6 +13,7 @@
 
#include "../../thread.h"
 

	
 
#include "tcp.h"
 
#include "../network_coordinator.h"
 
#include "../network_internal.h"
 

	
 
#include <deque>
 
@@ -48,8 +49,7 @@ TCPServerConnecter::TCPServerConnecter(c
 

	
 
		case SERVER_ADDRESS_INVITE_CODE:
 
			this->status = Status::CONNECTING;
 

	
 
			// TODO -- The next commit will add this functionality.
 
			_network_coordinator_client.ConnectToServer(this->server_address.connection_string, this);
 
			break;
 

	
 
		default:
src/network/core/tcp_coordinator.cpp
Show inline comments
 
@@ -33,6 +33,12 @@ bool NetworkCoordinatorSocketHandler::Ha
 
		case PACKET_COORDINATOR_SERVER_UPDATE:   return this->Receive_SERVER_UPDATE(p);
 
		case PACKET_COORDINATOR_CLIENT_LISTING:  return this->Receive_CLIENT_LISTING(p);
 
		case PACKET_COORDINATOR_GC_LISTING:      return this->Receive_GC_LISTING(p);
 
		case PACKET_COORDINATOR_CLIENT_CONNECT:        return this->Receive_CLIENT_CONNECT(p);
 
		case PACKET_COORDINATOR_GC_CONNECTING:         return this->Receive_GC_CONNECTING(p);
 
		case PACKET_COORDINATOR_SERCLI_CONNECT_FAILED: return this->Receive_SERCLI_CONNECT_FAILED(p);
 
		case PACKET_COORDINATOR_GC_CONNECT_FAILED:     return this->Receive_GC_CONNECT_FAILED(p);
 
		case PACKET_COORDINATOR_CLIENT_CONNECTED:      return this->Receive_CLIENT_CONNECTED(p);
 
		case PACKET_COORDINATOR_GC_DIRECT_CONNECT:     return this->Receive_GC_DIRECT_CONNECT(p);
 

	
 
		default:
 
			Debug(net, 0, "[tcp/coordinator] Received invalid packet type {}", type);
 
@@ -82,3 +88,9 @@ bool NetworkCoordinatorSocketHandler::Re
 
bool NetworkCoordinatorSocketHandler::Receive_SERVER_UPDATE(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_SERVER_UPDATE); }
 
bool NetworkCoordinatorSocketHandler::Receive_CLIENT_LISTING(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_CLIENT_LISTING); }
 
bool NetworkCoordinatorSocketHandler::Receive_GC_LISTING(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_LISTING); }
 
bool NetworkCoordinatorSocketHandler::Receive_CLIENT_CONNECT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_CLIENT_CONNECT); }
 
bool NetworkCoordinatorSocketHandler::Receive_GC_CONNECTING(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_CONNECTING); }
 
bool NetworkCoordinatorSocketHandler::Receive_SERCLI_CONNECT_FAILED(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_SERCLI_CONNECT_FAILED); }
 
bool NetworkCoordinatorSocketHandler::Receive_GC_CONNECT_FAILED(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_CONNECT_FAILED); }
 
bool NetworkCoordinatorSocketHandler::Receive_CLIENT_CONNECTED(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_CLIENT_CONNECTED); }
 
bool NetworkCoordinatorSocketHandler::Receive_GC_DIRECT_CONNECT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_DIRECT_CONNECT); }
src/network/core/tcp_coordinator.h
Show inline comments
 
@@ -23,6 +23,7 @@
 
 * GC     -> packets from Game Coordinator to either Client or Server.
 
 * SERVER -> packets from Server to Game Coordinator.
 
 * CLIENT -> packets from Client to Game Coordinator.
 
 * SERCLI -> packets from either the Server or Client to Game Coordinator.
 
 **/
 
enum PacketCoordinatorType {
 
	PACKET_COORDINATOR_GC_ERROR,        ///< Game Coordinator indicates there was an error.
 
@@ -31,7 +32,13 @@ enum PacketCoordinatorType {
 
	PACKET_COORDINATOR_SERVER_UPDATE,   ///< Server sends an set intervals an update of the server.
 
	PACKET_COORDINATOR_CLIENT_LISTING,  ///< Client is requesting a listing of all public servers.
 
	PACKET_COORDINATOR_GC_LISTING,      ///< Game Coordinator returns a listing of all public servers.
 
	PACKET_COORDINATOR_END,             ///< Must ALWAYS be on the end of this list!! (period).
 
	PACKET_COORDINATOR_CLIENT_CONNECT,        ///< Client wants to connect to a server based on an invite code.
 
	PACKET_COORDINATOR_GC_CONNECTING,         ///< Game Coordinator informs the client of the token assigned to the connection attempt.
 
	PACKET_COORDINATOR_SERCLI_CONNECT_FAILED, ///< Client/server tells the Game Coordinator the current connection attempt failed.
 
	PACKET_COORDINATOR_GC_CONNECT_FAILED,     ///< Game Coordinator informs client/server it has given up on the connection attempt.
 
	PACKET_COORDINATOR_CLIENT_CONNECTED,      ///< Client informs the Game Coordinator the connection with the server is established.
 
	PACKET_COORDINATOR_GC_DIRECT_CONNECT,     ///< Game Coordinator tells client to directly connect to the hostname:port of the server.
 
	PACKET_COORDINATOR_END,                   ///< Must ALWAYS be on the end of this list!! (period)
 
};
 

	
 
/**
 
@@ -49,6 +56,7 @@ enum ConnectionType {
 
enum NetworkCoordinatorErrorType {
 
	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.
 
};
 

	
 
/** Base socket handler for all Game Coordinator TCP sockets. */
 
@@ -76,6 +84,8 @@ protected:
 
	 *  uint8   Game Coordinator protocol version.
 
	 *  uint8   Type of game (see ServerGameType).
 
	 *  uint16  Local port of the server.
 
	 *  string  Invite code the server wants to use (can be empty; coordinator will assign a new invite code).
 
	 *  string  Secret that belongs to the invite code (empty if invite code is empty).
 
	 *
 
	 * @param p The packet that was just received.
 
	 * @return True upon success, otherwise false.
 
@@ -85,6 +95,8 @@ protected:
 
	/**
 
	 * Game Coordinator acknowledges the registration.
 
	 *
 
	 *  string  Invite code that can be used to join this server.
 
	 *  string  Secret that belongs to the invite code (only needed if reusing the invite code on next SERVER_REGISTER).
 
	 *  uint8   Type of connection was detected (see ConnectionType).
 
	 *
 
	 * @param p The packet that was just received.
 
@@ -130,6 +142,79 @@ protected:
 
	 */
 
	virtual bool Receive_GC_LISTING(Packet *p);
 

	
 
	/**
 
	 * Client wants to connect to a Server.
 
	 *
 
	 *  uint8   Game Coordinator protocol version.
 
	 *  string  Invite code of the Server to join.
 
	 *
 
	 * @param p The packet that was just received.
 
	 * @return True upon success, otherwise false.
 
	 */
 
	virtual bool Receive_CLIENT_CONNECT(Packet *p);
 

	
 
	/**
 
	 * Game Coordinator informs the Client under what token it will start the
 
	 * attempt to connect the Server and Client together.
 
	 *
 
	 *  string  Token to track the current connect request.
 
	 *  string  Invite code of the Server to join.
 
	 *
 
	 * @param p The packet that was just received.
 
	 * @return True upon success, otherwise false.
 
	 */
 
	virtual bool Receive_GC_CONNECTING(Packet *p);
 

	
 
	/**
 
	 * Client or Server failed to connect to the remote side.
 
	 *
 
	 *  uint8   Game Coordinator protocol version.
 
	 *  string  Token to track the current connect request.
 
	 *  uint8   Tracking number to track current connect request.
 
	 *
 
	 * @param p The packet that was just received.
 
	 * @return True upon success, otherwise false.
 
	 */
 
	virtual bool Receive_SERCLI_CONNECT_FAILED(Packet *p);
 

	
 
	/**
 
	 * Game Coordinator informs the Client that it failed to find a way to
 
	 * connect the Client to the Server. Any open connections for this token
 
	 * should be closed now.
 
	 *
 
	 *  string  Token to track the current connect request.
 
	 *
 
	 * @param p The packet that was just received.
 
	 * @return True upon success, otherwise false.
 
	 */
 
	virtual bool Receive_GC_CONNECT_FAILED(Packet *p);
 

	
 
	/**
 
	 * Client informs the Game Coordinator the connection with the Server is
 
	 * established. The Client will disconnect from the Game Coordinator next.
 
	 *
 
	 *  uint8   Game Coordinator protocol version.
 
	 *  string  Token to track the current connect request.
 
	 *
 
	 * @param p The packet that was just received.
 
	 * @return True upon success, otherwise false.
 
	 */
 
	virtual bool Receive_CLIENT_CONNECTED(Packet *p);
 

	
 
	/**
 
	 * Game Coordinator requests that the Client makes a direct connection to
 
	 * the indicated peer, which is a Server.
 
	 *
 
	 *  string  Token to track the current connect request.
 
	 *  uint8   Tracking number to track current connect request.
 
	 *  string  Hostname of the peer.
 
	 *  uint16  Port of the peer.
 
	 *
 
	 * @param p The packet that was just received.
 
	 * @return True upon success, otherwise false.
 
	 */
 
	virtual bool Receive_GC_DIRECT_CONNECT(Packet *p);
 

	
 
	bool HandlePacket(Packet *p);
 
public:
 
	/**
src/network/network.cpp
Show inline comments
 
@@ -589,11 +589,15 @@ void NetworkClose(bool close_admins)
 
		ServerNetworkAdminSocketHandler::CloseListeners();
 

	
 
		_network_coordinator_client.CloseConnection();
 
	} else if (MyClient::my_client != nullptr) {
 
	} else {
 
		if (MyClient::my_client != nullptr) {
 
		MyClient::SendQuit();
 
		MyClient::my_client->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT);
 
	}
 

	
 
		_network_coordinator_client.CloseAllTokens();
 
	}
 

	
 
	TCPConnecter::KillAll();
 

	
 
	_networking = false;
src/network/network_coordinator.cpp
Show inline comments
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * 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.
 
@@ -27,13 +26,41 @@
 
static const auto NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES = std::chrono::seconds(30); ///< How many time between updates the server sends to the Game Coordinator.
 
ClientNetworkCoordinatorSocketHandler _network_coordinator_client; ///< The connection to the Game Coordinator.
 
ConnectionType _network_server_connection_type = CONNECTION_TYPE_UNKNOWN; ///< What type of connection the Game Coordinator detected we are on.
 
std::string _network_server_invite_code = ""; ///< Our invite code as indicated by the Game Coordinator.
 

	
 
/** Connect to a game server by IP:port. */
 
class NetworkDirectConnecter : public TCPConnecter {
 
private:
 
	std::string token;     ///< Token of this connection.
 
	uint8 tracking_number; ///< Tracking number of this connection.
 

	
 
public:
 
	/**
 
	 * Try to establish a direct (hostname:port based) connection.
 
	 * @param hostname The hostname of the server.
 
	 * @param port The port of the server.
 
	 * @param token The token as given by the Game Coordinator to track this connection attempt.
 
	 * @param tracking_number The tracking number as given by the Game Coordinator to track this connection attempt.
 
	 */
 
	NetworkDirectConnecter(const std::string &hostname, uint16 port, const std::string &token, uint8 tracking_number) : TCPConnecter(hostname, port), token(token), tracking_number(tracking_number) {}
 

	
 
	void OnFailure() override
 
	{
 
		_network_coordinator_client.ConnectFailure(this->token, this->tracking_number);
 
	}
 

	
 
	void OnConnect(SOCKET s) override
 
	{
 
		_network_coordinator_client.ConnectSuccess(this->token, s);
 
	}
 
};
 

	
 
/** Connect to the Game Coordinator server. */
 
class NetworkCoordinatorConnecter : TCPConnecter {
 
public:
 
	/**
 
	 * Initiate the connecting.
 
	 * @param address The address of the Game Coordinator server.
 
	 * @param connection_string The address of the Game Coordinator server.
 
	 */
 
	NetworkCoordinatorConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_COORDINATOR_SERVER_PORT) {}
 

	
 
@@ -73,6 +100,20 @@ bool ClientNetworkCoordinatorSocketHandl
 
			this->CloseConnection();
 
			return false;
 

	
 
		case NETWORK_COORDINATOR_ERROR_INVALID_INVITE_CODE: {
 
			/* Find the connecter based on the invite code. */
 
			auto connecter_it = this->connecter_pre.find(detail);
 
			if (connecter_it == this->connecter_pre.end()) return true;
 
			this->connecter_pre.erase(connecter_it);
 

	
 
			/* Mark the server as offline. */
 
			NetworkGameList *item = NetworkGameListAddItem(detail);
 
			item->online = false;
 

	
 
			UpdateNetworkGameWindow();
 
			return true;
 
		}
 

	
 
		default:
 
			Debug(net, 0, "Invalid error type {} received from Game Coordinator", error);
 
			this->CloseConnection();
 
@@ -85,12 +126,21 @@ bool ClientNetworkCoordinatorSocketHandl
 
	/* Schedule sending an update. */
 
	this->next_update = std::chrono::steady_clock::now();
 

	
 
	_settings_client.network.server_invite_code = p->Recv_string(NETWORK_INVITE_CODE_LENGTH);
 
	_settings_client.network.server_invite_code_secret = p->Recv_string(NETWORK_INVITE_CODE_SECRET_LENGTH);
 
	_network_server_connection_type = (ConnectionType)p->Recv_uint8();
 

	
 
	if (_network_server_connection_type == CONNECTION_TYPE_ISOLATED) {
 
		ShowErrorMessage(STR_NETWORK_ERROR_COORDINATOR_ISOLATED, STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL, WL_ERROR);
 
	}
 

	
 
	/* Users can change the invite code in the settings, but this has no effect
 
	 * on the invite code as assigned by the server. So
 
	 * _network_server_invite_code contains the current invite code,
 
	 * and _settings_client.network.server_invite_code contains the one we will
 
	 * attempt to re-use when registering again. */
 
	_network_server_invite_code = _settings_client.network.server_invite_code;
 

	
 
	SetWindowDirty(WC_CLIENT_LIST, 0);
 

	
 
	if (_network_dedicated) {
 
@@ -107,7 +157,10 @@ bool ClientNetworkCoordinatorSocketHandl
 
		Debug(net, 3, "Your server is now registered with the Game Coordinator:");
 
		Debug(net, 3, "  Game type:       Public");
 
		Debug(net, 3, "  Connection type: {}", connection_type);
 
		Debug(net, 3, "  Invite code:     {}", _network_server_invite_code);
 
		Debug(net, 3, "----------------------------------------");
 
	} else {
 
		Debug(net, 3, "Game Coordinator registered our server with invite code '{}'", _network_server_invite_code);
 
	}
 

	
 
	return true;
 
@@ -130,7 +183,7 @@ bool ClientNetworkCoordinatorSocketHandl
 
		NetworkGameInfo ngi = {};
 
		DeserializeNetworkGameInfo(p, &ngi);
 

	
 
		/* Now we know the join-key, we can add it to our list. */
 
		/* Now we know the connection string, we can add it to our list. */
 
		NetworkGameList *item = NetworkGameListAddItem(connection_string);
 

	
 
		/* Clear any existing GRFConfig chain. */
 
@@ -149,6 +202,58 @@ bool ClientNetworkCoordinatorSocketHandl
 
	return true;
 
}
 

	
 
bool ClientNetworkCoordinatorSocketHandler::Receive_GC_CONNECTING(Packet *p)
 
{
 
	std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH);
 
	std::string invite_code = p->Recv_string(NETWORK_INVITE_CODE_LENGTH);
 

	
 
	/* Find the connecter based on the invite code. */
 
	auto connecter_it = this->connecter_pre.find(invite_code);
 
	if (connecter_it == this->connecter_pre.end()) {
 
		this->CloseConnection();
 
		return false;
 
	}
 

	
 
	/* Now store it based on the token. */
 
	this->connecter[token] = connecter_it->second;
 
	this->connecter_pre.erase(connecter_it);
 

	
 
	return true;
 
}
 

	
 
bool ClientNetworkCoordinatorSocketHandler::Receive_GC_CONNECT_FAILED(Packet *p)
 
{
 
	std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH);
 

	
 
	auto connecter_it = this->connecter.find(token);
 
	if (connecter_it != this->connecter.end()) {
 
		connecter_it->second->SetFailure();
 
		this->connecter.erase(connecter_it);
 
	}
 

	
 
	/* Close all remaining connections. */
 
	this->CloseToken(token);
 

	
 
	return true;
 
}
 

	
 
bool ClientNetworkCoordinatorSocketHandler::Receive_GC_DIRECT_CONNECT(Packet *p)
 
{
 
	std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH);
 
	uint8 tracking_number = p->Recv_uint8();
 
	std::string hostname = p->Recv_string(NETWORK_HOSTNAME_LENGTH);
 
	uint16 port = p->Recv_uint16();
 

	
 
	/* Ensure all other pending connection attempts are killed. */
 
	if (this->game_connecter != nullptr) {
 
		this->game_connecter->Kill();
 
		this->game_connecter = nullptr;
 
	}
 

	
 
	this->game_connecter = new NetworkDirectConnecter(hostname, port, token, tracking_number);
 
	return true;
 
}
 

	
 
void ClientNetworkCoordinatorSocketHandler::Connect()
 
{
 
	/* We are either already connected or are trying to connect. */
 
@@ -172,13 +277,15 @@ NetworkRecvStatus ClientNetworkCoordinat
 
	_network_server_connection_type = CONNECTION_TYPE_UNKNOWN;
 
	this->next_update = {};
 

	
 
	this->CloseAllTokens();
 

	
 
	SetWindowDirty(WC_CLIENT_LIST, 0);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Register our server to receive our join-key.
 
 * Register our server to receive our invite code.
 
 */
 
void ClientNetworkCoordinatorSocketHandler::Register()
 
{
 
@@ -193,6 +300,13 @@ void ClientNetworkCoordinatorSocketHandl
 
	p->Send_uint8(NETWORK_COORDINATOR_VERSION);
 
	p->Send_uint8(SERVER_GAME_TYPE_PUBLIC);
 
	p->Send_uint16(_settings_client.network.server_port);
 
	if (_settings_client.network.server_invite_code.empty() || _settings_client.network.server_invite_code_secret.empty()) {
 
		p->Send_string("");
 
		p->Send_string("");
 
	} else {
 
		p->Send_string(_settings_client.network.server_invite_code);
 
		p->Send_string(_settings_client.network.server_invite_code_secret);
 
	}
 

	
 
	this->SendPacket(p);
 
}
 
@@ -230,6 +344,123 @@ void ClientNetworkCoordinatorSocketHandl
 
}
 

	
 
/**
 
 * Join a server based on an invite code.
 
 * @param invite_code The invite code of the server to connect to.
 
 * @param connecter The connecter of the request.
 
 */
 
void ClientNetworkCoordinatorSocketHandler::ConnectToServer(const std::string &invite_code, TCPServerConnecter *connecter)
 
{
 
	assert(StrStartsWith(invite_code, "+"));
 

	
 
	if (this->connecter_pre.find(invite_code) != this->connecter_pre.end()) {
 
		/* If someone is hammering the refresh key, one can sent out two
 
		 * requests for the same invite code. There isn't really a great way
 
		 * of handling this, so just ignore this request. */
 
		connecter->SetFailure();
 
		return;
 
	}
 

	
 
	/* Initially we store based on invite code; on first reply we know the
 
	 * token, and will start using that key instead. */
 
	this->connecter_pre[invite_code] = connecter;
 

	
 
	this->Connect();
 

	
 
	Packet *p = new Packet(PACKET_COORDINATOR_CLIENT_CONNECT);
 
	p->Send_uint8(NETWORK_COORDINATOR_VERSION);
 
	p->Send_string(invite_code);
 

	
 
	this->SendPacket(p);
 
}
 

	
 
/**
 
 * Callback from a Connecter to let the Game Coordinator know the connection failed.
 
 * @param token Token of the connecter that failed.
 
 * @param tracking_number Tracking number of the connecter that failed.
 
 */
 
void ClientNetworkCoordinatorSocketHandler::ConnectFailure(const std::string &token, uint8 tracking_number)
 
{
 
	/* Connecter will destroy itself. */
 
	this->game_connecter = nullptr;
 

	
 
	Packet *p = new Packet(PACKET_COORDINATOR_SERCLI_CONNECT_FAILED);
 
	p->Send_uint8(NETWORK_COORDINATOR_VERSION);
 
	p->Send_string(token);
 
	p->Send_uint8(tracking_number);
 

	
 
	this->SendPacket(p);
 

	
 
	auto connecter_it = this->connecter.find(token);
 
	assert(connecter_it != this->connecter.end());
 

	
 
	connecter_it->second->SetFailure();
 
	this->connecter.erase(connecter_it);
 
}
 

	
 
/**
 
 * Callback from a Connecter to let the Game Coordinator know the connection
 
 * to the game server is established.
 
 * @param token Token of the connecter that succeeded.
 
 * @param sock The socket that the connecter can now use.
 
 */
 
void ClientNetworkCoordinatorSocketHandler::ConnectSuccess(const std::string &token, SOCKET sock)
 
{
 
	/* Connecter will destroy itself. */
 
	this->game_connecter = nullptr;
 

	
 
	assert(!_network_server);
 

	
 
	Packet *p = new Packet(PACKET_COORDINATOR_CLIENT_CONNECTED);
 
	p->Send_uint8(NETWORK_COORDINATOR_VERSION);
 
	p->Send_string(token);
 
	this->SendPacket(p);
 

	
 
	auto connecter_it = this->connecter.find(token);
 
	assert(connecter_it != this->connecter.end());
 

	
 
	connecter_it->second->SetConnected(sock);
 
	this->connecter.erase(connecter_it);
 

	
 
	/* Close all remaining connections. */
 
	this->CloseToken(token);
 
}
 

	
 
/**
 
 * Close everything related to this connection token.
 
 * @param token The connection token to close.
 
 */
 
void ClientNetworkCoordinatorSocketHandler::CloseToken(const std::string &token)
 
{
 
	/* Ensure all other pending connection attempts are also killed. */
 
	if (this->game_connecter != nullptr) {
 
		this->game_connecter->Kill();
 
		this->game_connecter = nullptr;
 
	}
 
}
 

	
 
/**
 
 * Close all pending connection tokens.
 
 */
 
void ClientNetworkCoordinatorSocketHandler::CloseAllTokens()
 
{
 
	/* Ensure all other pending connection attempts are also killed. */
 
	if (this->game_connecter != nullptr) {
 
		this->game_connecter->Kill();
 
		this->game_connecter = nullptr;
 
	}
 

	
 
	/* Mark any pending connecters as failed. */
 
	for (auto &[token, it] : this->connecter) {
 
		it->SetFailure();
 
	}
 
	for (auto &[invite_code, it] : this->connecter_pre) {
 
		it->SetFailure();
 
	}
 
	this->connecter.clear();
 
	this->connecter_pre.clear();
 
}
 

	
 
/**
 
 * Check whether we received/can send some data from/to the Game Coordinator server and
 
 * when that's the case handle it appropriately.
 
 */
src/network/network_coordinator.h
Show inline comments
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * 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.
 
@@ -12,6 +11,7 @@
 
#define NETWORK_COORDINATOR_H
 

	
 
#include "core/tcp_coordinator.h"
 
#include <map>
 

	
 
/**
 
 * Game Coordinator communication.
 
@@ -25,17 +25,32 @@
 
 * For clients (listing):
 
 *  - Client sends CLIENT_LISTING.
 
 *  - Game Coordinator returns the full list of public servers via GC_LISTING (multiple packets).
 
 *
 
 * For clients (connecting):
 
 *  - Client sends CLIENT_CONNECT.
 
 *  - Game Coordinator checks what type of connections the servers supports:
 
 *    1) Direct connect?
 
 *        - Send the client a GC_CONNECT with the peer address.
 
 *        - a) Client connects, client sends CLIENT_CONNECTED to Game Coordinator.
 
 *        - b) Client connect fails, client sends CLIENT_CONNECT_FAILED to Game Coordinator.
 
 *  - If all fails, Game Coordinator sends GC_CONNECT_FAILED to indicate no connection is possible.
 
 */
 

	
 
/** Class for handling the client side of the Game Coordinator connection. */
 
class ClientNetworkCoordinatorSocketHandler : public NetworkCoordinatorSocketHandler {
 
private:
 
	std::chrono::steady_clock::time_point next_update; ///< When to send the next update (if server and public).
 
	std::map<std::string, TCPServerConnecter *> connecter; ///< Based on tokens, the current connecters that are pending.
 
	std::map<std::string, TCPServerConnecter *> connecter_pre; ///< Based on invite codes, the current connecters that are pending.
 
	TCPConnecter *game_connecter = nullptr; ///< Pending connecter to the game server.
 

	
 
protected:
 
	bool Receive_GC_ERROR(Packet *p) override;
 
	bool Receive_GC_REGISTER_ACK(Packet *p) override;
 
	bool Receive_GC_LISTING(Packet *p) override;
 
	bool Receive_GC_CONNECTING(Packet *p) override;
 
	bool Receive_GC_CONNECT_FAILED(Packet *p) override;
 
	bool Receive_GC_DIRECT_CONNECT(Packet *p) override;
 

	
 
public:
 
	/** The idle timeout; when to close the connection because it's idle. */
 
@@ -49,11 +64,18 @@ public:
 
	NetworkRecvStatus CloseConnection(bool error = true) override;
 
	void SendReceive();
 

	
 
	void ConnectFailure(const std::string &token, uint8 tracking_number);
 
	void ConnectSuccess(const std::string &token, SOCKET sock);
 

	
 
	void Connect();
 
	void CloseToken(const std::string &token);
 
	void CloseAllTokens();
 

	
 
	void Register();
 
	void SendServerUpdate();
 
	void GetListing();
 

	
 
	void ConnectToServer(const std::string &invite_code, TCPServerConnecter *connecter);
 
};
 

	
 
extern ClientNetworkCoordinatorSocketHandler _network_coordinator_client;
src/network/network_gui.cpp
Show inline comments
 
@@ -751,7 +751,7 @@ public:
 
				SetDParamStr(0, _settings_client.network.connect_to_ip);
 
				ShowQueryString(
 
					STR_JUST_RAW_STRING,
 
					STR_NETWORK_SERVER_LIST_ENTER_IP,
 
					STR_NETWORK_SERVER_LIST_ENTER_SERVER_ADDRESS,
 
					NETWORK_HOSTNAME_PORT_LENGTH,  // maximum number of characters including '\0'
 
					this, CS_ALPHANUMERAL, QSF_ACCEPT_UNCHANGED);
 
				break;
 
@@ -1633,6 +1633,11 @@ static const NWidgetPart _nested_client_
 
					NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_CL_SERVER_VISIBILITY), SetDataTip(STR_BLACK_STRING, STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP),
 
				EndContainer(),
 
				NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0),
 
					NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE, STR_NULL),
 
					NWidget(NWID_SPACER), SetMinimalSize(10, 0),
 
					NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_INVITE_CODE), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
 
				EndContainer(),
 
				NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0),
 
					NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE, STR_NULL),
 
					NWidget(NWID_SPACER), SetMinimalSize(10, 0),
 
					NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_CONNECTION_TYPE), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_STRING, STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
 
@@ -2071,6 +2076,12 @@ public:
 
				SetDParam(0, _server_visibility_dropdown[_settings_client.network.server_advertise]);
 
				break;
 

	
 
			case WID_CL_SERVER_INVITE_CODE: {
 
				static std::string empty = {};
 
				SetDParamStr(0, _network_server_connection_type == CONNECTION_TYPE_UNKNOWN ? empty : _network_server_invite_code);
 
				break;
 
			}
 

	
 
			case WID_CL_SERVER_CONNECTION_TYPE:
 
				SetDParam(0, STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN + _network_server_connection_type);
 
				break;
src/network/network_internal.h
Show inline comments
 
@@ -84,6 +84,7 @@ extern uint8 _network_join_waiting;
 
extern uint32 _network_join_bytes;
 
extern uint32 _network_join_bytes_total;
 
extern ConnectionType _network_server_connection_type;
 
extern std::string _network_server_invite_code;
 

	
 
extern uint8 _network_reconnect;
 

	
src/settings_type.h
Show inline comments
 
@@ -266,6 +266,8 @@ struct NetworkSettings {
 
	uint16      server_port;                              ///< port the server listens on
 
	uint16      server_admin_port;                        ///< port the server listens on for the admin network
 
	bool        server_admin_chat;                        ///< allow private chat for the server to be distributed to the admin network
 
	std::string server_invite_code;                       ///< Invite code to use when registering as server.
 
	std::string server_invite_code_secret;                ///< Secret to proof we got this invite code from the Game Coordinator.
 
	std::string server_name;                              ///< name of the server
 
	std::string server_password;                          ///< password for joining this server
 
	std::string rcon_password;                            ///< password for rconsole (server side)
src/table/settings/network_secrets_settings.ini
Show inline comments
 
@@ -74,3 +74,17 @@ type     = SLE_STR
 
length   = NETWORK_SERVER_ID_LENGTH
 
flags    = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC | SF_NETWORK_ONLY
 
def      = nullptr
 

	
 
[SDTC_SSTR]
 
var      = network.server_invite_code
 
type     = SLE_STR
 
length   = NETWORK_INVITE_CODE_LENGTH
 
flags    = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC | SF_NETWORK_ONLY
 
def      = nullptr
 

	
 
[SDTC_SSTR]
 
var      = network.server_invite_code_secret
 
type     = SLE_STR
 
length   = NETWORK_INVITE_CODE_SECRET_LENGTH
 
flags    = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC | SF_NETWORK_ONLY
 
def      = nullptr
src/widgets/network_widget.h
Show inline comments
 
@@ -101,6 +101,7 @@ enum ClientListWidgets {
 
	WID_CL_SERVER_NAME,                ///< Server name.
 
	WID_CL_SERVER_NAME_EDIT,           ///< Edit button for server name.
 
	WID_CL_SERVER_VISIBILITY,          ///< Server visibility.
 
	WID_CL_SERVER_INVITE_CODE,         ///< Invite code for this server.
 
	WID_CL_SERVER_CONNECTION_TYPE,     ///< The type of connection the Game Coordinator detected for this server.
 
	WID_CL_CLIENT_NAME,                ///< Client name.
 
	WID_CL_CLIENT_NAME_EDIT,           ///< Edit button for client name.
0 comments (0 inline, 0 general)