Files @ r25849:38205b3e59c6
Branch filter:

Location: cpp/openttd-patchpack/source/src/network/network_coordinator.h

Patric Stout
Feature: allow the use of TURN to connect client and server together

TURN is a last resort, used only if all other methods failed.
TURN is a relay approach to connect client and server together, where
openttd.org (by default) is the middleman.

It is very unlikely either the client or server cannot connect to
the STUN server, as they are both already connected to the Game
Coordinator. But in the odd case it does fail, estabilishing the
connection fails without any further possibility to recover.
/*
 * 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"
#include "network_stun.h"
#include "network_turn.h"
#include <map>

/**
 * Game Coordinator communication.
 * For more detail about what the Game Coordinator does, please see
 * docs/game_coordinator.md.
 *
 * 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.
 *
 * 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.
 *    2) STUN?
 *        - Game Coordinator sends GC_STUN_REQUEST to server/client (asking for both IPv4 and IPv6 STUN requests).
 *        - Game Coordinator collects what combination works and sends GC_STUN_CONNECT to server/client.
 *        - a) Server/client connect, client sends CLIENT_CONNECTED to Game Coordinator.
 *        - b) Server/client connect fails, both send SERCLI_CONNECT_FAILED to Game Coordinator.
 *        - Game Coordinator tries other combination if available.
 *    3) TURN?
 *        - Game Coordinator sends GC_TURN_CONNECT to server/client.
 *        - a) Server/client connect, client sends CLIENT_CONNECTED to Game Coordinator.
 *        - b) Server/client connect fails, both send SERCLI_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.
	std::map<std::string, std::map<int, std::unique_ptr<ClientNetworkStunSocketHandler>>> stun_handlers; ///< All pending STUN handlers, stored by token:family.
	std::map<std::string, std::unique_ptr<ClientNetworkTurnSocketHandler>> turn_handlers; ///< Pending TURN handler (if any), stored by token.
	TCPConnecter *game_connecter = nullptr; ///< Pending connecter to the game server.

	uint32 newgrf_lookup_table_cursor = 0; ///< Last received cursor for the #GameInfoNewGRFLookupTable updates.
	GameInfoNewGRFLookupTable newgrf_lookup_table; ///< Table to look up NewGRFs in the GC_LISTING packets.

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;
	bool Receive_GC_STUN_REQUEST(Packet *p) override;
	bool Receive_GC_STUN_CONNECT(Packet *p) override;
	bool Receive_GC_NEWGRF_LOOKUP(Packet *p) override;
	bool Receive_GC_TURN_CONNECT(Packet *p) override;

public:
	/** The idle timeout; when to close the connection because it's idle. */
	static constexpr std::chrono::seconds IDLE_TIMEOUT = std::chrono::seconds(60);

	std::chrono::steady_clock::time_point last_activity;  ///< The last time there was network activity.
	bool connecting; ///< Are we connecting to the Game Coordinator?

	ClientNetworkCoordinatorSocketHandler() : connecting(false) {}

	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, NetworkAddress &address);
	void StunResult(const std::string &token, uint8 family, bool result);

	void Connect();
	void CloseToken(const std::string &token);
	void CloseAllConnections();
	void CloseStunHandler(const std::string &token, uint8 family = AF_UNSPEC);
	void CloseTurnHandler(const std::string &token);

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

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

extern ClientNetworkCoordinatorSocketHandler _network_coordinator_client;

#endif /* NETWORK_COORDINATOR_H */