Add: use Game Coordinator to annouce public servers
parent
e1e2212e0e
commit
b1280fd17e
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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); }
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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 */
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @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();
|
||||
}
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @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 */
|
Loading…
Reference in New Issue