/* * 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_query.cpp Query part of the network protocol. */ #include "../stdafx.h" #include "core/network_game_info.h" #include "network_query.h" #include "network_gamelist.h" #include "../error.h" #include "table/strings.h" #include "../safeguards.h" std::vector> QueryNetworkGameSocketHandler::queries = {}; NetworkRecvStatus QueryNetworkGameSocketHandler::CloseConnection(NetworkRecvStatus status) { assert(status != NETWORK_RECV_STATUS_OKAY); assert(this->sock != INVALID_SOCKET); /* Connection is closed, but we never received a packet. Must be offline. */ NetworkGameList *item = NetworkGameListAddItem(this->connection_string); if (item->refreshing) { item->status = NGLS_OFFLINE; item->refreshing = false; UpdateNetworkGameWindow(); } return status; } /** * Check the connection's state, i.e. is the connection still up? */ bool QueryNetworkGameSocketHandler::CheckConnection() { std::chrono::steady_clock::duration lag = std::chrono::steady_clock::now() - this->last_packet; /* If there was no response in 5 seconds, terminate the query. */ if (lag > std::chrono::seconds(5)) { Debug(net, 0, "Timeout while waiting for response from {}", this->connection_string); this->CloseConnection(NETWORK_RECV_STATUS_CONNECTION_LOST); return false; } return true; } /** * Check whether we received/can send some data from/to the server and * when that's the case handle it appropriately. * @return true when everything went okay. */ bool QueryNetworkGameSocketHandler::Receive() { if (this->CanSendReceive()) { NetworkRecvStatus res = this->ReceivePackets(); if (res != NETWORK_RECV_STATUS_OKAY) { this->CloseConnection(res); return false; } } return true; } /** Send the packets of this socket handler. */ void QueryNetworkGameSocketHandler::Send() { this->SendPackets(); } /** * Query the server for server information. */ NetworkRecvStatus QueryNetworkGameSocketHandler::SendGameInfo() { Debug(net, 9, "Query::SendGameInfo()"); this->SendPacket(new Packet(PACKET_CLIENT_GAME_INFO)); return NETWORK_RECV_STATUS_OKAY; } NetworkRecvStatus QueryNetworkGameSocketHandler::Receive_SERVER_FULL(Packet *) { Debug(net, 9, "Query::Receive_SERVER_FULL()"); NetworkGameList *item = NetworkGameListAddItem(this->connection_string); item->status = NGLS_FULL; item->refreshing = false; UpdateNetworkGameWindow(); return NETWORK_RECV_STATUS_CLOSE_QUERY; } NetworkRecvStatus QueryNetworkGameSocketHandler::Receive_SERVER_BANNED(Packet *) { Debug(net, 9, "Query::Receive_SERVER_BANNED()"); NetworkGameList *item = NetworkGameListAddItem(this->connection_string); item->status = NGLS_BANNED; item->refreshing = false; UpdateNetworkGameWindow(); return NETWORK_RECV_STATUS_CLOSE_QUERY; } NetworkRecvStatus QueryNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packet *p) { Debug(net, 9, "Query::Receive_SERVER_GAME_INFO()"); NetworkGameList *item = NetworkGameListAddItem(this->connection_string); /* Clear any existing GRFConfig chain. */ ClearGRFConfigList(&item->info.grfconfig); /* Retrieve the NetworkGameInfo from the packet. */ DeserializeNetworkGameInfo(p, &item->info); /* Check for compatability with the client. */ CheckGameCompatibility(item->info); /* Ensure we consider the server online. */ item->status = NGLS_ONLINE; item->refreshing = false; UpdateNetworkGameWindow(); return NETWORK_RECV_STATUS_CLOSE_QUERY; } NetworkRecvStatus QueryNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p) { NetworkErrorCode error = (NetworkErrorCode)p->Recv_uint8(); Debug(net, 9, "Query::Receive_SERVER_ERROR(): error={}", error); NetworkGameList *item = NetworkGameListAddItem(this->connection_string); if (error == NETWORK_ERROR_NOT_EXPECTED) { /* 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 to the * user this server is too old to query. */ item->status = NGLS_TOO_OLD; } else { item->status = NGLS_OFFLINE; } item->refreshing = false; UpdateNetworkGameWindow(); return NETWORK_RECV_STATUS_CLOSE_QUERY; } /** * Check if any query needs to send or receive. */ /* static */ void QueryNetworkGameSocketHandler::SendReceive() { for (auto it = QueryNetworkGameSocketHandler::queries.begin(); it != QueryNetworkGameSocketHandler::queries.end(); /* nothing */) { if (!(*it)->Receive()) { it = QueryNetworkGameSocketHandler::queries.erase(it); } else if (!(*it)->CheckConnection()) { it = QueryNetworkGameSocketHandler::queries.erase(it); } else { it++; } } for (auto &query : QueryNetworkGameSocketHandler::queries) { query->Send(); } }