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();
+ }
+}