mirror of https://github.com/OpenTTD/OpenTTD.git
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.
This commit is contained in:
parent
1baec41542
commit
e4d216e44b
|
@ -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);
|
||||
|
|
|
@ -2045,12 +2045,12 @@ STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK}Search i
|
|||
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_TOOLTIP :{BLACK}Edit the
|
|||
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
|
||||
|
|
|
@ -47,7 +47,7 @@ static const uint16 COMPAT_MTU = 1460; ///< Numbe
|
|||
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_VERSION_LENGTH = 16; ///< The m
|
|||
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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(const std::string &connection_string, uin
|
|||
|
||||
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:
|
||||
|
|
|
@ -27,12 +27,18 @@ bool NetworkCoordinatorSocketHandler::HandlePacket(Packet *p)
|
|||
PacketCoordinatorType type = (PacketCoordinatorType)p->Recv_uint8();
|
||||
|
||||
switch (type) {
|
||||
case PACKET_COORDINATOR_GC_ERROR: return this->Receive_GC_ERROR(p);
|
||||
case PACKET_COORDINATOR_SERVER_REGISTER: return this->Receive_SERVER_REGISTER(p);
|
||||
case PACKET_COORDINATOR_GC_REGISTER_ACK: return this->Receive_GC_REGISTER_ACK(p);
|
||||
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_GC_ERROR: return this->Receive_GC_ERROR(p);
|
||||
case PACKET_COORDINATOR_SERVER_REGISTER: return this->Receive_SERVER_REGISTER(p);
|
||||
case PACKET_COORDINATOR_GC_REGISTER_ACK: return this->Receive_GC_REGISTER_ACK(p);
|
||||
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::Receive_GC_REGISTER_ACK(Packet *p) { retur
|
|||
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); }
|
||||
|
|
|
@ -23,15 +23,22 @@
|
|||
* 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.
|
||||
PACKET_COORDINATOR_SERVER_REGISTER, ///< Server registration.
|
||||
PACKET_COORDINATOR_GC_REGISTER_ACK, ///< Game Coordinator accepts the registration.
|
||||
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_GC_ERROR, ///< Game Coordinator indicates there was an error.
|
||||
PACKET_COORDINATOR_SERVER_REGISTER, ///< Server registration.
|
||||
PACKET_COORDINATOR_GC_REGISTER_ACK, ///< Game Coordinator accepts the registration.
|
||||
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_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:
|
||||
/**
|
||||
|
|
|
@ -589,9 +589,13 @@ void NetworkClose(bool close_admins)
|
|||
ServerNetworkAdminSocketHandler::CloseListeners();
|
||||
|
||||
_network_coordinator_client.CloseConnection();
|
||||
} else if (MyClient::my_client != nullptr) {
|
||||
MyClient::SendQuit();
|
||||
MyClient::my_client->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT);
|
||||
} else {
|
||||
if (MyClient::my_client != nullptr) {
|
||||
MyClient::SendQuit();
|
||||
MyClient::my_client->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT);
|
||||
}
|
||||
|
||||
_network_coordinator_client.CloseAllTokens();
|
||||
}
|
||||
|
||||
TCPConnecter::KillAll();
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/*
|
||||
* 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 ClientNetworkCoordinatorSocketHandler::Receive_GC_ERROR(Packet *p)
|
|||
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 ClientNetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet *p)
|
|||
/* 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 ClientNetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet *p)
|
|||
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 ClientNetworkCoordinatorSocketHandler::Receive_GC_LISTING(Packet *p)
|
|||
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 ClientNetworkCoordinatorSocketHandler::Receive_GC_LISTING(Packet *p)
|
|||
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 ClientNetworkCoordinatorSocketHandler::CloseConnection(bool er
|
|||
_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 ClientNetworkCoordinatorSocketHandler::Register()
|
|||
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);
|
||||
}
|
||||
|
@ -229,6 +343,123 @@ void ClientNetworkCoordinatorSocketHandler::GetListing()
|
|||
this->SendPacket(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/*
|
||||
* 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;
|
||||
|
|
|
@ -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;
|
||||
|
@ -1632,6 +1632,11 @@ static const NWidgetPart _nested_client_list_widgets[] = {
|
|||
NWidget(NWID_SPACER), SetMinimalSize(10, 0), SetFill(1, 0), SetResize(1, 0),
|
||||
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),
|
||||
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue