2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
8 /** @file network_query.cpp Query part of the network protocol. */
10 #include "../stdafx.h"
11 #include "core/network_game_info.h"
12 #include "network_query.h"
13 #include "network_gamelist.h"
16 #include "table/strings.h"
18 #include "../safeguards.h"
20 std::vector
<std::unique_ptr
<QueryNetworkGameSocketHandler
>> QueryNetworkGameSocketHandler::queries
= {};
22 NetworkRecvStatus
QueryNetworkGameSocketHandler::CloseConnection(NetworkRecvStatus status
)
24 assert(status
!= NETWORK_RECV_STATUS_OKAY
);
25 assert(this->sock
!= INVALID_SOCKET
);
27 /* Connection is closed, but we never received a packet. Must be offline. */
28 NetworkGameList
*item
= NetworkGameListAddItem(this->connection_string
);
29 if (item
->refreshing
) {
30 item
->status
= NGLS_OFFLINE
;
31 item
->refreshing
= false;
33 UpdateNetworkGameWindow();
40 * Check the connection's state, i.e. is the connection still up?
42 bool QueryNetworkGameSocketHandler::CheckConnection()
44 std::chrono::steady_clock::duration lag
= std::chrono::steady_clock::now() - this->last_packet
;
46 /* If there was no response in 5 seconds, terminate the query. */
47 if (lag
> std::chrono::seconds(5)) {
48 Debug(net
, 0, "Timeout while waiting for response from {}", this->connection_string
);
49 this->CloseConnection(NETWORK_RECV_STATUS_CONNECTION_LOST
);
57 * Check whether we received/can send some data from/to the server and
58 * when that's the case handle it appropriately.
59 * @return true when everything went okay.
61 bool QueryNetworkGameSocketHandler::Receive()
63 if (this->CanSendReceive()) {
64 NetworkRecvStatus res
= this->ReceivePackets();
65 if (res
!= NETWORK_RECV_STATUS_OKAY
) {
66 this->CloseConnection(res
);
73 /** Send the packets of this socket handler. */
74 void QueryNetworkGameSocketHandler::Send()
80 * Query the server for server information.
82 NetworkRecvStatus
QueryNetworkGameSocketHandler::SendGameInfo()
84 Debug(net
, 9, "Query::SendGameInfo()");
86 this->SendPacket(std::make_unique
<Packet
>(this, PACKET_CLIENT_GAME_INFO
));
87 return NETWORK_RECV_STATUS_OKAY
;
90 NetworkRecvStatus
QueryNetworkGameSocketHandler::Receive_SERVER_FULL(Packet
&)
92 Debug(net
, 9, "Query::Receive_SERVER_FULL()");
94 NetworkGameList
*item
= NetworkGameListAddItem(this->connection_string
);
95 item
->status
= NGLS_FULL
;
96 item
->refreshing
= false;
98 UpdateNetworkGameWindow();
100 return NETWORK_RECV_STATUS_CLOSE_QUERY
;
103 NetworkRecvStatus
QueryNetworkGameSocketHandler::Receive_SERVER_BANNED(Packet
&)
105 Debug(net
, 9, "Query::Receive_SERVER_BANNED()");
107 NetworkGameList
*item
= NetworkGameListAddItem(this->connection_string
);
108 item
->status
= NGLS_BANNED
;
109 item
->refreshing
= false;
111 UpdateNetworkGameWindow();
113 return NETWORK_RECV_STATUS_CLOSE_QUERY
;
116 NetworkRecvStatus
QueryNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packet
&p
)
118 Debug(net
, 9, "Query::Receive_SERVER_GAME_INFO()");
120 NetworkGameList
*item
= NetworkGameListAddItem(this->connection_string
);
122 /* Clear any existing GRFConfig chain. */
123 ClearGRFConfigList(&item
->info
.grfconfig
);
124 /* Retrieve the NetworkGameInfo from the packet. */
125 DeserializeNetworkGameInfo(p
, item
->info
);
126 /* Check for compatability with the client. */
127 CheckGameCompatibility(item
->info
);
128 /* Ensure we consider the server online. */
129 item
->status
= NGLS_ONLINE
;
130 item
->refreshing
= false;
132 UpdateNetworkGameWindow();
134 return NETWORK_RECV_STATUS_CLOSE_QUERY
;
137 NetworkRecvStatus
QueryNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet
&p
)
139 NetworkErrorCode error
= (NetworkErrorCode
)p
.Recv_uint8();
141 Debug(net
, 9, "Query::Receive_SERVER_ERROR(): error={}", error
);
143 NetworkGameList
*item
= NetworkGameListAddItem(this->connection_string
);
145 if (error
== NETWORK_ERROR_NOT_EXPECTED
) {
146 /* If we query a server that is 1.11.1 or older, we get an
147 * NETWORK_ERROR_NOT_EXPECTED on requesting the game info. Show to the
148 * user this server is too old to query.
150 item
->status
= NGLS_TOO_OLD
;
152 item
->status
= NGLS_OFFLINE
;
154 item
->refreshing
= false;
156 UpdateNetworkGameWindow();
158 return NETWORK_RECV_STATUS_CLOSE_QUERY
;
162 * Check if any query needs to send or receive.
164 /* static */ void QueryNetworkGameSocketHandler::SendReceive()
166 for (auto it
= QueryNetworkGameSocketHandler::queries
.begin(); it
!= QueryNetworkGameSocketHandler::queries
.end(); /* nothing */) {
167 if (!(*it
)->Receive()) {
168 it
= QueryNetworkGameSocketHandler::queries
.erase(it
);
169 } else if (!(*it
)->CheckConnection()) {
170 it
= QueryNetworkGameSocketHandler::queries
.erase(it
);
176 for (auto &query
: QueryNetworkGameSocketHandler::queries
) {