diff --git a/src/network/network_query.cpp b/src/network/network_query.cpp new file mode 100644 --- /dev/null +++ b/src/network/network_query.cpp @@ -0,0 +1,145 @@ +/* + * 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/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); + + 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)) { + 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() +{ + this->SendPacket(new Packet(PACKET_CLIENT_GAME_INFO)); + return NETWORK_RECV_STATUS_OKAY; +} + +NetworkRecvStatus QueryNetworkGameSocketHandler::Receive_SERVER_FULL(Packet *p) +{ + /* We try to join a server which is full */ + ShowErrorMessage(STR_NETWORK_ERROR_SERVER_FULL, INVALID_STRING_ID, WL_CRITICAL); + return NETWORK_RECV_STATUS_SERVER_FULL; +} + +NetworkRecvStatus QueryNetworkGameSocketHandler::Receive_SERVER_BANNED(Packet *p) +{ + /* We try to join a server where we are banned */ + ShowErrorMessage(STR_NETWORK_ERROR_SERVER_BANNED, INVALID_STRING_ID, WL_CRITICAL); + return NETWORK_RECV_STATUS_SERVER_BANNED; +} + +NetworkRecvStatus QueryNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packet *p) +{ + 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->online = true; + + UpdateNetworkGameWindow(); + + return NETWORK_RECV_STATUS_CLOSE_QUERY; +} + +NetworkRecvStatus QueryNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p) +{ + 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) { + ShowErrorMessage(STR_NETWORK_ERROR_SERVER_TOO_OLD, INVALID_STRING_ID, WL_CRITICAL); + return NETWORK_RECV_STATUS_CLOSE_QUERY; + } + + ShowErrorMessage(STR_NETWORK_ERROR_LOSTCONNECTION, INVALID_STRING_ID, WL_CRITICAL); + return NETWORK_RECV_STATUS_SERVER_ERROR; +} + +/** + * 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(); + } +}