Change: use TCP for everything except for master-server and initial server scan (#9130)

This means that pressing Refresh button and adding servers manually
now uses TCP.

The master-server and initial scan are still UDP as they will be
replaced by Game Coordinator; no need to change this now.

If we query a server that is too old, show a proper warning to the
user informing him the server is too old.
This commit is contained in:
Patric Stout 2021-04-30 11:34:47 +02:00 committed by GitHub
parent f00564eeb2
commit 69118d063f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 78 additions and 43 deletions

View File

@ -65,10 +65,14 @@ Module.preRun.push(function() {
} }
window.openttd_server_list = function() { window.openttd_server_list = function() {
add_server = Module.cwrap("em_openttd_add_server", null, ["string", "number"]); add_server = Module.cwrap("em_openttd_add_server", null, ["string"]);
/* Add servers that support WebSocket here. Example: /* Add servers that support WebSocket here. Examples:
* add_server("localhost", 3979); */ * add_server("localhost");
* add_server("localhost:3979");
* add_server("127.0.0.1:3979");
* add_server("[::1]:3979");
*/
} }
var leftButtonDown = false; var leftButtonDown = false;

View File

@ -2217,6 +2217,7 @@ STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Your com
STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Your computer took too long to download the map STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Your computer took too long to download the map
STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Your computer took too long to join the server STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Your computer took too long to join the server
STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Your player name is not valid STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Your player name is not valid
STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}The queried server is too old for this client
############ Leave those lines in this order!! ############ Leave those lines in this order!!
STR_NETWORK_ERROR_CLIENT_GENERAL :general error STR_NETWORK_ERROR_CLIENT_GENERAL :general error

View File

@ -602,8 +602,11 @@ static void NetworkInitialize(bool close_admins = true)
/** Non blocking connection create to query servers */ /** Non blocking connection create to query servers */
class TCPQueryConnecter : TCPConnecter { class TCPQueryConnecter : TCPConnecter {
private:
bool request_company_info;
public: public:
TCPQueryConnecter(const NetworkAddress &address) : TCPConnecter(address) {} TCPQueryConnecter(const NetworkAddress &address, bool request_company_info) : TCPConnecter(address), request_company_info(request_company_info) {}
void OnFailure() override void OnFailure() override
{ {
@ -613,36 +616,53 @@ public:
void OnConnect(SOCKET s) override void OnConnect(SOCKET s) override
{ {
_networking = true; _networking = true;
new ClientNetworkGameSocketHandler(s); new ClientNetworkGameSocketHandler(s, address);
MyClient::SendInformationQuery(); MyClient::SendInformationQuery(request_company_info);
} }
}; };
/** /**
* Query a server to fetch his game-info. * Query a server to fetch his game-info.
* @param address the address to query. * @param address the address to query.
* @param request_company_info Whether to request company info too.
*/ */
void NetworkTCPQueryServer(NetworkAddress address) void NetworkTCPQueryServer(NetworkAddress address, bool request_company_info)
{ {
if (!_network_available) return; if (!_network_available) return;
NetworkDisconnect(); NetworkDisconnect();
NetworkInitialize(); NetworkInitialize();
new TCPQueryConnecter(address); new TCPQueryConnecter(address, request_company_info);
} }
/** /**
* Validates an address entered as a string and adds the server to * Validates an address entered as a string and adds the server to
* the list. If you use this function, the games will be marked * the list. If you use this function, the games will be marked
* as manually added. * as manually added.
* @param connection_string The IP:port to add to the list. * @param connection_string The IP:port of the server to add.
* @return The entry on the game list.
*/ */
void NetworkAddServer(const char *connection_string) NetworkGameList *NetworkAddServer(const std::string &connection_string)
{ {
if (StrEmpty(connection_string)) return; if (connection_string.empty()) return nullptr;
NetworkUDPQueryServer(ParseConnectionString(connection_string, NETWORK_DEFAULT_PORT), true); NetworkAddress address = ParseConnectionString(connection_string, NETWORK_DEFAULT_PORT);
/* Ensure the item already exists in the list */
NetworkGameList *item = NetworkGameListAddItem(address);
if (StrEmpty(item->info.server_name)) {
ClearGRFConfigList(&item->info.grfconfig);
address.GetAddressAsString(item->info.server_name, lastof(item->info.server_name));
item->manually = true;
NetworkRebuildHostList();
UpdateNetworkGameWindow();
}
NetworkTCPQueryServer(address);
return item;
} }
/** /**
@ -687,7 +707,7 @@ public:
void OnConnect(SOCKET s) override void OnConnect(SOCKET s) override
{ {
_networking = true; _networking = true;
new ClientNetworkGameSocketHandler(s); new ClientNetworkGameSocketHandler(s, this->address);
IConsoleCmdExec("exec scripts/on_client.scr 0"); IConsoleCmdExec("exec scripts/on_client.scr 0");
NetworkClient_Connected(); NetworkClient_Connected();
} }
@ -1132,9 +1152,9 @@ void NetworkShutDown()
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
extern "C" { extern "C" {
void CDECL em_openttd_add_server(const char *host, int port) void CDECL em_openttd_add_server(const char *connection_string)
{ {
NetworkUDPQueryServer(NetworkAddress(host, port), true); NetworkAddServer(connection_string);
} }
} }

View File

@ -145,7 +145,7 @@ void ClientNetworkEmergencySave()
* Create a new socket for the client side of the game connection. * Create a new socket for the client side of the game connection.
* @param s The socket to connect with. * @param s The socket to connect with.
*/ */
ClientNetworkGameSocketHandler::ClientNetworkGameSocketHandler(SOCKET s) : NetworkGameSocketHandler(s), savegame(nullptr), status(STATUS_INACTIVE) ClientNetworkGameSocketHandler::ClientNetworkGameSocketHandler(SOCKET s, NetworkAddress address) : NetworkGameSocketHandler(s), address(address), savegame(nullptr), status(STATUS_INACTIVE)
{ {
assert(ClientNetworkGameSocketHandler::my_client == nullptr); assert(ClientNetworkGameSocketHandler::my_client == nullptr);
ClientNetworkGameSocketHandler::my_client = this; ClientNetworkGameSocketHandler::my_client = this;
@ -345,14 +345,18 @@ static_assert(NETWORK_SERVER_ID_LENGTH == 16 * 2 + 1);
/** /**
* Query the server for server information. * Query the server for server information.
*/ */
NetworkRecvStatus ClientNetworkGameSocketHandler::SendInformationQuery() NetworkRecvStatus ClientNetworkGameSocketHandler::SendInformationQuery(bool request_company_info)
{ {
my_client->status = STATUS_COMPANY_INFO; my_client->status = STATUS_GAME_INFO;
_network_join_status = NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO;
SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
my_client->SendPacket(new Packet(PACKET_CLIENT_GAME_INFO)); my_client->SendPacket(new Packet(PACKET_CLIENT_GAME_INFO));
my_client->SendPacket(new Packet(PACKET_CLIENT_COMPANY_INFO));
if (request_company_info) {
my_client->status = STATUS_COMPANY_INFO;
_network_join_status = NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO;
SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
my_client->SendPacket(new Packet(PACKET_CLIENT_COMPANY_INFO));
}
return NETWORK_RECV_STATUS_OKAY; return NETWORK_RECV_STATUS_OKAY;
} }
@ -577,9 +581,13 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_BANNED(Packet *
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packet *p) NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packet *p)
{ {
if (this->status != STATUS_COMPANY_INFO && this->status != STATUS_INACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET; if (this->status != STATUS_COMPANY_INFO && this->status != STATUS_GAME_INFO) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
NetworkGameList *item = GetLobbyGameInfo(); NetworkGameList *item = GetLobbyGameInfo();
if (item == nullptr) {
/* This is not the lobby, so add it to the game list. */
item = NetworkGameListAddItem(this->address);
}
/* Clear any existing GRFConfig chain. */ /* Clear any existing GRFConfig chain. */
ClearGRFConfigList(&item->info.grfconfig); ClearGRFConfigList(&item->info.grfconfig);
@ -590,6 +598,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packe
/* Ensure we consider the server online. */ /* Ensure we consider the server online. */
item->online = true; item->online = true;
/* It could be either window, but only one is open, so redraw both. */
SetWindowDirty(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME);
SetWindowDirty(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY); SetWindowDirty(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY);
/* We will receive company info next, so keep connection open. */ /* We will receive company info next, so keep connection open. */
@ -727,6 +737,15 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p
NetworkErrorCode error = (NetworkErrorCode)p->Recv_uint8(); NetworkErrorCode error = (NetworkErrorCode)p->Recv_uint8();
/* If we query a server that is 1.11.1 or older, we get an
* NETWORK_ERROR_NOT_EXPECTED on requesting the game info. Show a special
* error popup in that case.
*/
if (error == NETWORK_ERROR_NOT_EXPECTED && (this->status == STATUS_GAME_INFO || this->status == STATUS_COMPANY_INFO)) {
ShowErrorMessage(STR_NETWORK_ERROR_SERVER_TOO_OLD, INVALID_STRING_ID, WL_CRITICAL);
return NETWORK_RECV_STATUS_CLOSE_QUERY;
}
StringID err = STR_NETWORK_ERROR_LOSTCONNECTION; StringID err = STR_NETWORK_ERROR_LOSTCONNECTION;
if (error < (ptrdiff_t)lengthof(network_error_strings)) err = network_error_strings[error]; if (error < (ptrdiff_t)lengthof(network_error_strings)) err = network_error_strings[error];
/* In case of kicking a client, we assume there is a kick message in the packet if we can read one byte */ /* In case of kicking a client, we assume there is a kick message in the packet if we can read one byte */

View File

@ -15,12 +15,14 @@
/** Class for handling the client side of the game connection. */ /** Class for handling the client side of the game connection. */
class ClientNetworkGameSocketHandler : public ZeroedMemoryAllocator, public NetworkGameSocketHandler { class ClientNetworkGameSocketHandler : public ZeroedMemoryAllocator, public NetworkGameSocketHandler {
private: private:
NetworkAddress address; ///< Address we are connected to.
struct PacketReader *savegame; ///< Packet reader for reading the savegame. struct PacketReader *savegame; ///< Packet reader for reading the savegame.
byte token; ///< The token we need to send back to the server to prove we're the right client. byte token; ///< The token we need to send back to the server to prove we're the right client.
/** Status of the connection with the server. */ /** Status of the connection with the server. */
enum ServerStatus { enum ServerStatus {
STATUS_INACTIVE, ///< The client is not connected nor active. STATUS_INACTIVE, ///< The client is not connected nor active.
STATUS_GAME_INFO, ///< We are trying to get the game information.
STATUS_COMPANY_INFO, ///< We are trying to get company information. STATUS_COMPANY_INFO, ///< We are trying to get company information.
STATUS_JOIN, ///< We are trying to join a server. STATUS_JOIN, ///< We are trying to join a server.
STATUS_NEWGRFS_CHECK, ///< Last action was checking NewGRFs. STATUS_NEWGRFS_CHECK, ///< Last action was checking NewGRFs.
@ -74,13 +76,13 @@ protected:
static NetworkRecvStatus SendMapOk(); static NetworkRecvStatus SendMapOk();
void CheckConnection(); void CheckConnection();
public: public:
ClientNetworkGameSocketHandler(SOCKET s); ClientNetworkGameSocketHandler(SOCKET s, NetworkAddress address);
~ClientNetworkGameSocketHandler(); ~ClientNetworkGameSocketHandler();
NetworkRecvStatus CloseConnection(NetworkRecvStatus status) override; NetworkRecvStatus CloseConnection(NetworkRecvStatus status) override;
void ClientError(NetworkRecvStatus res); void ClientError(NetworkRecvStatus res);
static NetworkRecvStatus SendInformationQuery(); static NetworkRecvStatus SendInformationQuery(bool request_company_info);
static NetworkRecvStatus SendJoin(); static NetworkRecvStatus SendJoin();
static NetworkRecvStatus SendCommand(const CommandPacket *cp); static NetworkRecvStatus SendCommand(const CommandPacket *cp);

View File

@ -69,15 +69,6 @@ static void NetworkGameListHandleDelayedInsert()
*/ */
NetworkGameList *NetworkGameListAddItem(NetworkAddress address) NetworkGameList *NetworkGameListAddItem(NetworkAddress address)
{ {
const char *hostname = address.GetHostname();
/* Do not query the 'any' address. */
if (StrEmpty(hostname) ||
strcmp(hostname, "0.0.0.0") == 0 ||
strcmp(hostname, "::") == 0) {
return nullptr;
}
NetworkGameList *item, *prev_item; NetworkGameList *item, *prev_item;
prev_item = nullptr; prev_item = nullptr;
@ -95,7 +86,6 @@ NetworkGameList *NetworkGameListAddItem(NetworkAddress address)
} else { } else {
prev_item->next = item; prev_item->next = item;
} }
DEBUG(net, 4, "[gamelist] added server to list");
UpdateNetworkGameWindow(); UpdateNetworkGameWindow();

View File

@ -472,9 +472,8 @@ public:
EM_ASM(if (window["openttd_server_list"]) openttd_server_list()); EM_ASM(if (window["openttd_server_list"]) openttd_server_list());
#endif #endif
this->last_joined = NetworkGameListAddItem(ParseConnectionString(_settings_client.network.last_joined, NETWORK_DEFAULT_PORT)); this->last_joined = NetworkAddServer(_settings_client.network.last_joined);
this->server = this->last_joined; this->server = this->last_joined;
if (this->last_joined != nullptr) NetworkUDPQueryServer(this->last_joined->address);
this->requery_timer.SetInterval(MILLISECONDS_PER_TICK); this->requery_timer.SetInterval(MILLISECONDS_PER_TICK);
@ -750,7 +749,7 @@ public:
break; break;
case WID_NG_REFRESH: // Refresh case WID_NG_REFRESH: // Refresh
if (this->server != nullptr) NetworkUDPQueryServer(this->server->address); if (this->server != nullptr) NetworkTCPQueryServer(this->server->address);
break; break;
case WID_NG_NEWGRF: // NewGRF Settings case WID_NG_NEWGRF: // NewGRF Settings
@ -971,7 +970,7 @@ void ShowNetworkGameWindow()
first = false; first = false;
/* Add all servers from the config file to our list. */ /* Add all servers from the config file to our list. */
for (const auto &iter : _network_host_list) { for (const auto &iter : _network_host_list) {
NetworkAddServer(iter.c_str()); NetworkAddServer(iter);
} }
} }
@ -1485,7 +1484,7 @@ struct NetworkLobbyWindow : public Window {
/* Clear the information so removed companies don't remain */ /* Clear the information so removed companies don't remain */
for (auto &company : this->company_info) company = {}; for (auto &company : this->company_info) company = {};
NetworkTCPQueryServer(this->server->address); NetworkTCPQueryServer(this->server->address, true);
break; break;
} }
} }
@ -1555,7 +1554,7 @@ static void ShowNetworkLobbyWindow(NetworkGameList *ngl)
strecpy(_settings_client.network.last_joined, ngl->address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined)); strecpy(_settings_client.network.last_joined, ngl->address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined));
NetworkTCPQueryServer(ngl->address); NetworkTCPQueryServer(ngl->address, true);
new NetworkLobbyWindow(&_network_lobby_window_desc, ngl); new NetworkLobbyWindow(&_network_lobby_window_desc, ngl);
} }

View File

@ -87,10 +87,10 @@ extern uint8 _network_reconnect;
extern CompanyMask _network_company_passworded; extern CompanyMask _network_company_passworded;
void NetworkTCPQueryServer(NetworkAddress address); void NetworkTCPQueryServer(NetworkAddress address, bool request_company_info = false);
void GetBindAddresses(NetworkAddressList *addresses, uint16 port); void GetBindAddresses(NetworkAddressList *addresses, uint16 port);
void NetworkAddServer(const char *b); struct NetworkGameList *NetworkAddServer(const std::string &connection_string);
void NetworkRebuildHostList(); void NetworkRebuildHostList();
void UpdateNetworkGameWindow(); void UpdateNetworkGameWindow();