diff --git a/src/lang/english.txt b/src/lang/english.txt
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -1995,7 +1995,7 @@ STR_FACE_TIE
STR_FACE_EARRING :Earring:
STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Change tie or earring
-STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Private
+STR_NETWORK_SERVER_VISIBILITY_LOCAL :Local
STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Public
# Network server list
@@ -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_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
STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Name
STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Your player name
@@ -2154,6 +2156,12 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF
STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}This is the host of the game
STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} client{P "" s} / {NUM} compan{P y ies}
+############ Begin of ConnectionType
+STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN :{BLACK}Local
+STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_ISOLATED :{RED}Remote players can't connect
+STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_DIRECT :{BLACK}Public
+############ End of ConnectionType
+
STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Kick
STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Ban
STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Delete
@@ -2281,6 +2289,10 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN
STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}The server is restarting...{}Please wait...
STR_NETWORK_MESSAGE_KICKED :*** {RAW_STRING} was kicked. Reason: ({RAW_STRING})
+STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED :{WHITE}Server registration failed
+STR_NETWORK_ERROR_COORDINATOR_ISOLATED :{WHITE}Your server doesn't allow remote connections
+STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL :{WHITE}Other players won't be able to connect to your server
+
# Content downloading window
STR_CONTENT_TITLE :{WHITE}Content downloading
STR_CONTENT_TYPE_CAPTION :{BLACK}Type
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
--- a/src/network/CMakeLists.txt
+++ b/src/network/CMakeLists.txt
@@ -14,6 +14,8 @@ add_files(
network_content.h
network_content_gui.cpp
network_content_gui.h
+ network_coordinator.cpp
+ network_coordinator.h
network_func.h
network_gamelist.cpp
network_gamelist.h
diff --git a/src/network/core/CMakeLists.txt b/src/network/core/CMakeLists.txt
--- a/src/network/core/CMakeLists.txt
+++ b/src/network/core/CMakeLists.txt
@@ -20,6 +20,8 @@ add_files(
tcp_content.cpp
tcp_content.h
tcp_content_type.h
+ tcp_coordinator.cpp
+ tcp_coordinator.h
tcp_game.cpp
tcp_game.h
tcp_http.cpp
diff --git a/src/network/core/config.h b/src/network/core/config.h
--- a/src/network/core/config.h
+++ b/src/network/core/config.h
@@ -14,6 +14,8 @@
/** DNS hostname of the masterserver */
static const char * const NETWORK_MASTER_SERVER_HOST = "master.openttd.org";
+/** DNS hostname of the Game Coordinator server */
+static const char * const NETWORK_COORDINATOR_SERVER_HOST = "coordinator.openttd.org";
/** DNS hostname of the content server */
static const char * const NETWORK_CONTENT_SERVER_HOST = "content.openttd.org";
/** DNS hostname of the HTTP-content mirror server */
@@ -23,14 +25,15 @@ static const char * const NETWORK_CONTEN
/** Message sent to the masterserver to 'identify' this client as OpenTTD */
static const char * const NETWORK_MASTER_SERVER_WELCOME_MESSAGE = "OpenTTDRegister";
-static const uint16 NETWORK_MASTER_SERVER_PORT = 3978; ///< The default port of the master server (UDP)
-static const uint16 NETWORK_CONTENT_SERVER_PORT = 3978; ///< The default port of the content server (TCP)
-static const uint16 NETWORK_CONTENT_MIRROR_PORT = 80; ///< The default port of the content mirror (TCP)
-static const uint16 NETWORK_DEFAULT_PORT = 3979; ///< The default port of the game server (TCP & UDP)
-static const uint16 NETWORK_ADMIN_PORT = 3977; ///< The default port for admin network
-static const uint16 NETWORK_DEFAULT_DEBUGLOG_PORT = 3982; ///< The default port debug-log is sent to (TCP)
+static const uint16 NETWORK_MASTER_SERVER_PORT = 3978; ///< The default port of the master server (UDP)
+static const uint16 NETWORK_COORDINATOR_SERVER_PORT = 3976; ///< The default port of the Game Coordinator server (TCP)
+static const uint16 NETWORK_CONTENT_SERVER_PORT = 3978; ///< The default port of the content server (TCP)
+static const uint16 NETWORK_CONTENT_MIRROR_PORT = 80; ///< The default port of the content mirror (TCP)
+static const uint16 NETWORK_DEFAULT_PORT = 3979; ///< The default port of the game server (TCP & UDP)
+static const uint16 NETWORK_ADMIN_PORT = 3977; ///< The default port for admin network
+static const uint16 NETWORK_DEFAULT_DEBUGLOG_PORT = 3982; ///< The default port debug-log is sent to (TCP)
-static const uint16 UDP_MTU = 1460; ///< Number of bytes we can pack in a single UDP packet
+static const uint16 UDP_MTU = 1460; ///< Number of bytes we can pack in a single UDP packet
/*
* Technically a TCP packet could become 64kiB, however the high bit is kept so it becomes possible in the future
* to go to (significantly) larger packets if needed. This would entail a strategy such as employed for UTF-8.
@@ -45,40 +48,42 @@ static const uint16 UDP_MTU
* Send_uint16(GB(size, 16, 14) | 0b10 << 14)
* Send_uint16(GB(size, 0, 16))
*/
-static const uint16 TCP_MTU = 32767; ///< Number of bytes we can pack in a single TCP packet
-static const uint16 COMPAT_MTU = 1460; ///< Number of bytes we can pack in a single packet for backward compatibility
+static const uint16 TCP_MTU = 32767; ///< Number of bytes we can pack in a single TCP packet
+static const uint16 COMPAT_MTU = 1460; ///< Number of bytes we can pack in a single packet for backward compatibility
-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_MASTER_SERVER_VERSION = 2; ///< What version of master-server-protocol do we use?
+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_MASTER_SERVER_VERSION = 2; ///< What version of master-server-protocol do we use?
+static const byte NETWORK_COORDINATOR_VERSION = 1; ///< 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'
-static const uint NETWORK_HOSTNAME_LENGTH = 80; ///< The maximum length of the host name, in bytes including '\0'
-static const uint NETWORK_HOSTNAME_PORT_LENGTH = 80 + 6; ///< The maximum length of the host name + port, in bytes including '\0'. The extra six is ":" + port number (with a max of 65536)
-static const uint NETWORK_SERVER_ID_LENGTH = 33; ///< The maximum length of the network id of the servers, in bytes including '\0'
-static const uint NETWORK_REVISION_LENGTH = 33; ///< The maximum length of the revision, in bytes including '\0'
-static const uint NETWORK_PASSWORD_LENGTH = 33; ///< The maximum length of the password, in bytes including '\0' (must be >= NETWORK_SERVER_ID_LENGTH)
-static const uint NETWORK_CLIENTS_LENGTH = 200; ///< The maximum length for the list of clients that controls a company, in bytes including '\0'
-static const uint NETWORK_CLIENT_NAME_LENGTH = 25; ///< The maximum length of a client's name, in bytes including '\0'
-static const uint NETWORK_RCONCOMMAND_LENGTH = 500; ///< The maximum length of a rconsole command, in bytes including '\0'
-static const uint NETWORK_GAMESCRIPT_JSON_LENGTH = COMPAT_MTU-3; ///< The maximum length of a gamescript json string, in bytes including '\0'. Must not be longer than COMPAT_MTU including header (3 bytes)
-static const uint NETWORK_CHAT_LENGTH = 900; ///< The maximum length of a chat message, in bytes including '\0'
-static const uint NETWORK_CONTENT_FILENAME_LENGTH = 48; ///< The maximum length of a content's filename, in bytes including '\0'.
-static const uint NETWORK_CONTENT_NAME_LENGTH = 32; ///< The maximum length of a content's name, in bytes including '\0'.
-static const uint NETWORK_CONTENT_VERSION_LENGTH = 16; ///< The maximum length of a content's version, in bytes including '\0'.
-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_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'
+static const uint NETWORK_HOSTNAME_LENGTH = 80; ///< The maximum length of the host name, in bytes including '\0'
+static const uint NETWORK_HOSTNAME_PORT_LENGTH = 80 + 6; ///< The maximum length of the host name + port, in bytes including '\0'. The extra six is ":" + port number (with a max of 65536)
+static const uint NETWORK_SERVER_ID_LENGTH = 33; ///< The maximum length of the network id of the servers, in bytes including '\0'
+static const uint NETWORK_REVISION_LENGTH = 33; ///< The maximum length of the revision, in bytes including '\0'
+static const uint NETWORK_PASSWORD_LENGTH = 33; ///< The maximum length of the password, in bytes including '\0' (must be >= NETWORK_SERVER_ID_LENGTH)
+static const uint NETWORK_CLIENTS_LENGTH = 200; ///< The maximum length for the list of clients that controls a company, in bytes including '\0'
+static const uint NETWORK_CLIENT_NAME_LENGTH = 25; ///< The maximum length of a client's name, in bytes including '\0'
+static const uint NETWORK_RCONCOMMAND_LENGTH = 500; ///< The maximum length of a rconsole command, in bytes including '\0'
+static const uint NETWORK_GAMESCRIPT_JSON_LENGTH = COMPAT_MTU - 3; ///< The maximum length of a gamescript json string, in bytes including '\0'. Must not be longer than COMPAT_MTU including header (3 bytes)
+static const uint NETWORK_CHAT_LENGTH = 900; ///< The maximum length of a chat message, in bytes including '\0'
+static const uint NETWORK_CONTENT_FILENAME_LENGTH = 48; ///< The maximum length of a content's filename, in bytes including '\0'.
+static const uint NETWORK_CONTENT_NAME_LENGTH = 32; ///< The maximum length of a content's name, in bytes including '\0'.
+static const uint NETWORK_CONTENT_VERSION_LENGTH = 16; ///< The maximum length of a content's version, in bytes including '\0'.
+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_GRF_NAME_LENGTH = 80; ///< Maximum length of the name of a GRF
+static const uint NETWORK_GRF_NAME_LENGTH = 80; ///< Maximum length of the name of a GRF
/**
* Maximum number of GRFs that can be sent.
* This limit is reached when PACKET_UDP_SERVER_RESPONSE reaches the maximum size of UDP_MTU bytes.
*/
-static const uint NETWORK_MAX_GRF_COUNT = 62;
+static const uint NETWORK_MAX_GRF_COUNT = 62;
/**
* The number of landscapes in OpenTTD.
@@ -88,6 +93,6 @@ static const uint NETWORK_MAX_GRF_COUNT
* there is a compile assertion to check that this NUM_LANDSCAPE is equal
* to NETWORK_NUM_LANDSCAPES.
*/
-static const uint NETWORK_NUM_LANDSCAPES = 4;
+static const uint NETWORK_NUM_LANDSCAPES = 4;
#endif /* NETWORK_CORE_CONFIG_H */
diff --git a/src/network/core/tcp_coordinator.cpp b/src/network/core/tcp_coordinator.cpp
new file mode 100644
--- /dev/null
+++ b/src/network/core/tcp_coordinator.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/**
+ * @file tcp_coordinator.cpp Basic functions to receive and send Game Coordinator packets.
+ */
+
+#include "../../stdafx.h"
+#include "../../date_func.h"
+#include "../../debug.h"
+#include "tcp_coordinator.h"
+
+#include "../../safeguards.h"
+
+/**
+ * Handle the given packet, i.e. pass it to the right.
+ * parser receive command.
+ * @param p The packet to handle.
+ * @return True iff we should immediately handle further packets.
+ */
+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);
+
+ default:
+ Debug(net, 0, "[tcp/coordinator] Received invalid packet type {}", type);
+ return false;
+ }
+}
+
+/**
+ * Receive a packet at TCP level.
+ * @return Whether at least one packet was received.
+ */
+bool NetworkCoordinatorSocketHandler::ReceivePackets()
+{
+ /*
+ * We read only a few of the packets. This allows the GUI to update when
+ * a large set of servers is being received. Otherwise the interface
+ * "hangs" while the game is updating the server-list.
+ *
+ * What arbitrary number to choose is the ultimate question though.
+ */
+ Packet *p;
+ static const int MAX_PACKETS_TO_RECEIVE = 42;
+ int i = MAX_PACKETS_TO_RECEIVE;
+ while (--i != 0 && (p = this->ReceivePacket()) != nullptr) {
+ bool cont = this->HandlePacket(p);
+ delete p;
+ if (!cont) return true;
+ }
+
+ return i != MAX_PACKETS_TO_RECEIVE - 1;
+}
+
+/**
+ * Helper for logging receiving invalid packets.
+ * @param type The received packet type.
+ * @return Always false, as it's an error.
+ */
+bool NetworkCoordinatorSocketHandler::ReceiveInvalidPacket(PacketCoordinatorType type)
+{
+ Debug(net, 0, "[tcp/coordinator] Received illegal packet type {}", type);
+ return false;
+}
+
+bool NetworkCoordinatorSocketHandler::Receive_GC_ERROR(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_ERROR); }
+bool NetworkCoordinatorSocketHandler::Receive_SERVER_REGISTER(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_SERVER_REGISTER); }
+bool NetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_REGISTER_ACK); }
+bool NetworkCoordinatorSocketHandler::Receive_SERVER_UPDATE(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_SERVER_UPDATE); }
diff --git a/src/network/core/tcp_coordinator.h b/src/network/core/tcp_coordinator.h
new file mode 100644
--- /dev/null
+++ b/src/network/core/tcp_coordinator.h
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/**
+ * @file tcp_coordinator.h Basic functions to receive and send TCP packets to/from the Game Coordinator server.
+ */
+
+#ifndef NETWORK_CORE_TCP_COORDINATOR_H
+#define NETWORK_CORE_TCP_COORDINATOR_H
+
+#include "os_abstraction.h"
+#include "tcp.h"
+#include "packet.h"
+#include "game_info.h"
+
+/**
+ * Enum with all types of TCP Game Coordinator packets. The order MUST not be changed.
+ *
+ * GC -> packets from Game Coordinator to either Client or Server.
+ * SERVER -> packets from Server to Game Coordinator.
+ * CLIENT -> packets from 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_END, ///< Must ALWAYS be on the end of this list!! (period).
+};
+
+/**
+ * The type of connection the Game Coordinator can detect we have.
+ */
+enum ConnectionType {
+ CONNECTION_TYPE_UNKNOWN, ///< The Game Coordinator hasn't informed us yet what type of connection 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.
+};
+
+/**
+ * The type of error from the Game Coordinator.
+ */
+enum NetworkCoordinatorErrorType {
+ NETWORK_COORDINATOR_ERROR_UNKNOWN, ///< There was an unknown error.
+ NETWORK_COORDINATOR_ERROR_REGISTRATION_FAILED, ///< Your request for registration failed.
+};
+
+/** Base socket handler for all Game Coordinator TCP sockets. */
+class NetworkCoordinatorSocketHandler : public NetworkTCPSocketHandler {
+protected:
+ bool ReceiveInvalidPacket(PacketCoordinatorType type);
+
+ /**
+ * Game Coordinator indicates there was an error. This can either be a
+ * permanent error causing the connection to be dropped, or in response
+ * to a request that is invalid.
+ *
+ * uint8 Type of error (see NetworkCoordinatorErrorType).
+ * string Details of the error.
+ *
+ * @param p The packet that was just received.
+ * @return True upon success, otherwise false.
+ */
+ virtual bool Receive_GC_ERROR(Packet *p);
+
+ /**
+ * Server is starting a multiplayer game and wants to let the
+ * Game Coordinator know.
+ *
+ * uint8 Game Coordinator protocol version.
+ * uint8 Type of game (see ServerGameType).
+ * uint16 Local port of the server.
+ *
+ * @param p The packet that was just received.
+ * @return True upon success, otherwise false.
+ */
+ virtual bool Receive_SERVER_REGISTER(Packet *p);
+
+ /**
+ * Game Coordinator acknowledges the registration.
+ *
+ * uint8 Type of connection was detected (see ConnectionType).
+ *
+ * @param p The packet that was just received.
+ * @return True upon success, otherwise false.
+ */
+ virtual bool Receive_GC_REGISTER_ACK(Packet *p);
+
+ /**
+ * Send an update of the current state of the server to the Game Coordinator.
+ *
+ * uint8 Game Coordinator protocol version.
+ * Serialized NetworkGameInfo. See game_info.hpp for details.
+ *
+ * @param p The packet that was just received.
+ * @return True upon success, otherwise false.
+ */
+ virtual bool Receive_SERVER_UPDATE(Packet *p);
+
+ bool HandlePacket(Packet *p);
+public:
+ /**
+ * Create a new cs socket handler for a given cs.
+ * @param s The socket we are connected with.
+ */
+ NetworkCoordinatorSocketHandler(SOCKET s = INVALID_SOCKET) : NetworkTCPSocketHandler(s) {}
+
+ bool ReceivePackets();
+};
+
+#endif /* NETWORK_CORE_TCP_COORDINATOR_H */
diff --git a/src/network/network.cpp b/src/network/network.cpp
--- a/src/network/network.cpp
+++ b/src/network/network.cpp
@@ -19,6 +19,7 @@
#include "network_udp.h"
#include "network_gamelist.h"
#include "network_base.h"
+#include "network_coordinator.h"
#include "core/udp.h"
#include "core/host.h"
#include "network_gui.h"
@@ -591,6 +592,8 @@ void NetworkClose(bool close_admins)
}
ServerNetworkGameSocketHandler::CloseListeners();
ServerNetworkAdminSocketHandler::CloseListeners();
+
+ _network_coordinator_client.CloseConnection();
} else if (MyClient::my_client != nullptr) {
MyClient::SendQuit();
MyClient::my_client->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT);
@@ -932,6 +935,10 @@ bool NetworkServerStart()
NetworkInitGameInfo();
+ if (_settings_client.network.server_advertise) {
+ _network_coordinator_client.Register();
+ }
+
/* execute server initialization script */
IConsoleCmdExec("exec scripts/on_server.scr 0");
/* if the server is dedicated ... add some other script */
@@ -1032,6 +1039,7 @@ static void NetworkSend()
void NetworkBackgroundLoop()
{
_network_content_client.SendReceive();
+ _network_coordinator_client.SendReceive();
TCPConnecter::CheckCallbacks();
NetworkHTTPSocketHandler::HTTPReceive();
diff --git a/src/network/network_coordinator.cpp b/src/network/network_coordinator.cpp
new file mode 100644
--- /dev/null
+++ b/src/network/network_coordinator.cpp
@@ -0,0 +1,234 @@
+
+/*
+ * 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.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file network_coordinator.cpp Game Coordinator sending/receiving part of the network protocol. */
+
+#include "../stdafx.h"
+#include "../debug.h"
+#include "../error.h"
+#include "../rev.h"
+#include "../settings_type.h"
+#include "../strings_func.h"
+#include "../window_func.h"
+#include "../window_type.h"
+#include "network.h"
+#include "network_coordinator.h"
+#include "network_gamelist.h"
+#include "table/strings.h"
+
+#include "../safeguards.h"
+
+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.
+
+/** Connect to the Game Coordinator server. */
+class NetworkCoordinatorConnecter : TCPConnecter {
+public:
+ /**
+ * Initiate the connecting.
+ * @param address The address of the Game Coordinator server.
+ */
+ NetworkCoordinatorConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_COORDINATOR_SERVER_PORT) {}
+
+ void OnFailure() override
+ {
+ _network_coordinator_client.connecting = false;
+ _network_coordinator_client.CloseConnection(true);
+ }
+
+ void OnConnect(SOCKET s) override
+ {
+ assert(_network_coordinator_client.sock == INVALID_SOCKET);
+
+ _network_coordinator_client.sock = s;
+ _network_coordinator_client.connecting = false;
+ }
+};
+
+bool ClientNetworkCoordinatorSocketHandler::Receive_GC_ERROR(Packet *p)
+{
+ NetworkCoordinatorErrorType error = (NetworkCoordinatorErrorType)p->Recv_uint8();
+ std::string detail = p->Recv_string(NETWORK_ERROR_DETAIL_LENGTH);
+
+ switch (error) {
+ case NETWORK_COORDINATOR_ERROR_UNKNOWN:
+ this->CloseConnection();
+ return false;
+
+ case NETWORK_COORDINATOR_ERROR_REGISTRATION_FAILED:
+ SetDParamStr(0, detail);
+ ShowErrorMessage(STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED, STR_JUST_RAW_STRING, WL_ERROR);
+
+ /* To prevent that we constantly try to reconnect, switch to private game. */
+ _settings_client.network.server_advertise = false;
+
+ this->CloseConnection();
+ return false;
+
+ default:
+ Debug(net, 0, "Invalid error type {} received from Game Coordinator", error);
+ this->CloseConnection();
+ return false;
+ }
+}
+
+bool ClientNetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet *p)
+{
+ /* Schedule sending an update. */
+ this->next_update = std::chrono::steady_clock::now();
+
+ _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);
+ }
+
+ SetWindowDirty(WC_CLIENT_LIST, 0);
+
+ if (_network_dedicated) {
+ std::string connection_type;
+ switch (_network_server_connection_type) {
+ case CONNECTION_TYPE_ISOLATED: connection_type = "Remote players can't connect"; break;
+ case CONNECTION_TYPE_DIRECT: connection_type = "Public"; break;
+
+ case CONNECTION_TYPE_UNKNOWN: // Never returned from Game Coordinator.
+ default: connection_type = "Unknown"; break; // Should never happen, but don't fail if it does.
+ }
+
+ Debug(net, 3, "----------------------------------------");
+ 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, "----------------------------------------");
+ }
+
+ return true;
+}
+
+void ClientNetworkCoordinatorSocketHandler::Connect()
+{
+ /* We are either already connected or are trying to connect. */
+ if (this->sock != INVALID_SOCKET || this->connecting) return;
+
+ this->Reopen();
+
+ this->connecting = true;
+ new NetworkCoordinatorConnecter(NETWORK_COORDINATOR_SERVER_HOST);
+}
+
+NetworkRecvStatus ClientNetworkCoordinatorSocketHandler::CloseConnection(bool error)
+{
+ NetworkCoordinatorSocketHandler::CloseConnection(error);
+
+ this->CloseSocket();
+ this->connecting = false;
+
+ _network_server_connection_type = CONNECTION_TYPE_UNKNOWN;
+ this->next_update = {};
+
+ SetWindowDirty(WC_CLIENT_LIST, 0);
+
+ return NETWORK_RECV_STATUS_OKAY;
+}
+
+/**
+ * Register our server to receive our join-key.
+ */
+void ClientNetworkCoordinatorSocketHandler::Register()
+{
+ _network_server_connection_type = CONNECTION_TYPE_UNKNOWN;
+ this->next_update = {};
+
+ SetWindowDirty(WC_CLIENT_LIST, 0);
+
+ this->Connect();
+
+ Packet *p = new Packet(PACKET_COORDINATOR_SERVER_REGISTER);
+ p->Send_uint8(NETWORK_COORDINATOR_VERSION);
+ p->Send_uint8(SERVER_GAME_TYPE_PUBLIC);
+ p->Send_uint16(_settings_client.network.server_port);
+
+ this->SendPacket(p);
+}
+
+/**
+ * Send an update of our server status to the Game Coordinator.
+ */
+void ClientNetworkCoordinatorSocketHandler::SendServerUpdate()
+{
+ Debug(net, 6, "Sending server update to Game Coordinator");
+ this->next_update = std::chrono::steady_clock::now() + NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES;
+
+ Packet *p = new Packet(PACKET_COORDINATOR_SERVER_UPDATE);
+ p->Send_uint8(NETWORK_COORDINATOR_VERSION);
+ SerializeNetworkGameInfo(p, GetCurrentNetworkServerGameInfo());
+
+ this->SendPacket(p);
+}
+
+/**
+ * Check whether we received/can send some data from/to the Game Coordinator server and
+ * when that's the case handle it appropriately.
+ */
+void ClientNetworkCoordinatorSocketHandler::SendReceive()
+{
+ /* Private games are not listed via the Game Coordinator. */
+ if (_network_server && !_settings_client.network.server_advertise) {
+ if (this->sock != INVALID_SOCKET) {
+ this->CloseConnection();
+ }
+ return;
+ }
+
+ static int last_attempt_backoff = 1;
+ static bool first_reconnect = true;
+
+ if (this->sock == INVALID_SOCKET) {
+ static std::chrono::steady_clock::time_point last_attempt = {};
+
+ /* Don't auto-reconnect when we are not a server. */
+ if (!_network_server) return;
+ /* Don't reconnect if we are connecting. */
+ if (this->connecting) return;
+ /* Throttle how often we try to reconnect. */
+ if (std::chrono::steady_clock::now() < last_attempt + std::chrono::seconds(1) * last_attempt_backoff) return;
+
+ last_attempt = std::chrono::steady_clock::now();
+ /* Delay reconnecting with up to 32 seconds. */
+ if (last_attempt_backoff < 32) {
+ last_attempt_backoff *= 2;
+ }
+
+ /* Do not reconnect on the first attempt, but only initialize the
+ * last_attempt variables. Otherwise after an outage all servers
+ * reconnect at the same time, potentially overwhelming the
+ * Game Coordinator. */
+ if (first_reconnect) {
+ first_reconnect = false;
+ return;
+ }
+
+ Debug(net, 1, "Connection with Game Coordinator lost; reconnecting...");
+ this->Register();
+ return;
+ }
+
+ last_attempt_backoff = 1;
+ first_reconnect = true;
+
+ if (_network_server && _network_server_connection_type != CONNECTION_TYPE_UNKNOWN && std::chrono::steady_clock::now() > this->next_update) {
+ this->SendServerUpdate();
+ }
+
+ if (this->CanSendReceive()) {
+ this->ReceivePackets();
+ }
+
+ this->SendPackets();
+}
diff --git a/src/network/network_coordinator.h b/src/network/network_coordinator.h
new file mode 100644
--- /dev/null
+++ b/src/network/network_coordinator.h
@@ -0,0 +1,51 @@
+
+/*
+ * 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.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file network_coordinator.h Part of the network protocol handling Game Coordinator requests. */
+
+#ifndef NETWORK_COORDINATOR_H
+#define NETWORK_COORDINATOR_H
+
+#include "core/tcp_coordinator.h"
+
+/**
+ * Game Coordinator communication.
+ *
+ * For servers:
+ * - Server sends SERVER_REGISTER.
+ * - Game Coordinator probes server to check if it can directly connect.
+ * - Game Coordinator sends GC_REGISTER_ACK with type of connection.
+ * - Server sends every 30 seconds SERVER_UPDATE.
+ */
+
+/** 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).
+
+protected:
+ bool Receive_GC_ERROR(Packet *p) override;
+ bool Receive_GC_REGISTER_ACK(Packet *p) override;
+
+public:
+ bool connecting; ///< Are we connecting to the Game Coordinator?
+
+ ClientNetworkCoordinatorSocketHandler() : connecting(false) {}
+
+ NetworkRecvStatus CloseConnection(bool error = true) override;
+ void SendReceive();
+
+ void Connect();
+
+ void Register();
+ void SendServerUpdate();
+};
+
+extern ClientNetworkCoordinatorSocketHandler _network_coordinator_client;
+
+#endif /* NETWORK_COORDINATOR_H */
diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp
--- a/src/network/network_gui.cpp
+++ b/src/network/network_gui.cpp
@@ -62,7 +62,7 @@ static CompanyID _admin_company_id = INV
* do not.
*/
static const StringID _server_visibility_dropdown[] = {
- STR_NETWORK_SERVER_VISIBILITY_PRIVATE,
+ STR_NETWORK_SERVER_VISIBILITY_LOCAL,
STR_NETWORK_SERVER_VISIBILITY_PUBLIC,
INVALID_STRING_ID
};
@@ -1607,21 +1607,26 @@ static const NWidgetPart _nested_client_
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER, STR_NULL), SetPadding(4, 4, 0, 4), SetPIP(0, 2, 0),
NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_NAME, STR_NULL),
- NWidget(NWID_SPACER), SetMinimalSize(20, 0),
+ NWidget(NWID_SPACER), SetMinimalSize(10, 0),
NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_NAME), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CL_SERVER_NAME_EDIT), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP),
EndContainer(),
NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY, STR_NULL),
- NWidget(NWID_SPACER), SetMinimalSize(20, 0), SetFill(1, 0), SetResize(1, 0),
+ 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_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),
+ EndContainer(),
EndContainer(),
EndContainer(),
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_PLAYER, STR_NULL), SetPadding(4, 4, 4, 4), SetPIP(0, 2, 0),
NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_PLAYER_NAME, STR_NULL),
- NWidget(NWID_SPACER), SetMinimalSize(20, 0),
+ NWidget(NWID_SPACER), SetMinimalSize(10, 0),
NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_CLIENT_NAME), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CL_CLIENT_NAME_EDIT), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP),
EndContainer(),
@@ -2050,6 +2055,10 @@ public:
SetDParam(0, _server_visibility_dropdown[_settings_client.network.server_advertise]);
break;
+ case WID_CL_SERVER_CONNECTION_TYPE:
+ SetDParam(0, STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN + _network_server_connection_type);
+ break;
+
case WID_CL_CLIENT_NAME:
SetDParamStr(0, _settings_client.network.client_name);
break;
diff --git a/src/network/network_internal.h b/src/network/network_internal.h
--- a/src/network/network_internal.h
+++ b/src/network/network_internal.h
@@ -11,6 +11,7 @@
#define NETWORK_INTERNAL_H
#include "network_func.h"
+#include "core/tcp_coordinator.h"
#include "core/tcp_game.h"
#include "../command_type.h"
@@ -82,6 +83,7 @@ extern NetworkJoinStatus _network_join_s
extern uint8 _network_join_waiting;
extern uint32 _network_join_bytes;
extern uint32 _network_join_bytes_total;
+extern ConnectionType _network_server_connection_type;
extern uint8 _network_reconnect;
diff --git a/src/network/network_type.h b/src/network/network_type.h
--- a/src/network/network_type.h
+++ b/src/network/network_type.h
@@ -35,6 +35,15 @@ enum NetworkVehicleType {
NETWORK_VEH_END
};
+/**
+ * Game type the server can be using.
+ * Used on the network protocol to communicate with Game Coordinator.
+ */
+enum ServerGameType : uint8 {
+ SERVER_GAME_TYPE_LOCAL = 0,
+ SERVER_GAME_TYPE_PUBLIC,
+};
+
/** 'Unique' identifier to be given to clients */
enum ClientID : uint32 {
INVALID_CLIENT_ID = 0, ///< Client is not part of anything
diff --git a/src/widgets/network_widget.h b/src/widgets/network_widget.h
--- a/src/widgets/network_widget.h
+++ b/src/widgets/network_widget.h
@@ -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_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.
WID_CL_MATRIX, ///< Company/client list.