summaryrefslogtreecommitdiff
path: root/src/network/network_coordinator.cpp
diff options
context:
space:
mode:
authorPatric Stout <truebrain@openttd.org>2021-07-03 11:04:32 +0200
committerPatric Stout <github@truebrain.nl>2021-07-10 20:17:07 +0200
commitb1280fd17ebaf9e4df086b63074d0bd8b8c9155d (patch)
tree6a421d4e079a765ba21181ad01ab21d13434ba9a /src/network/network_coordinator.cpp
parente1e2212e0e09c7739f2eb8a4421a9ed7982801f5 (diff)
downloadopenttd-b1280fd17ebaf9e4df086b63074d0bd8b8c9155d.tar.xz
Add: use Game Coordinator to annouce public servers
Diffstat (limited to 'src/network/network_coordinator.cpp')
-rw-r--r--src/network/network_coordinator.cpp234
1 files changed, 234 insertions, 0 deletions
diff --git a/src/network/network_coordinator.cpp b/src/network/network_coordinator.cpp
new file mode 100644
index 000000000..3e4079740
--- /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 <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();
+}