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.cpp Base functions for networking support. */
10 #include "../stdafx.h"
12 #include "../strings_func.h"
13 #include "../command_func.h"
14 #include "../date_func.h"
15 #include "network_admin.h"
16 #include "network_client.h"
17 #include "network_server.h"
18 #include "network_content.h"
19 #include "network_udp.h"
20 #include "network_gamelist.h"
21 #include "network_base.h"
22 #include "network_coordinator.h"
24 #include "core/host.h"
25 #include "network_gui.h"
26 #include "../console_func.h"
27 #include "../3rdparty/md5/md5.h"
28 #include "../core/random_func.hpp"
29 #include "../window_func.h"
30 #include "../company_func.h"
31 #include "../company_base.h"
32 #include "../landscape_type.h"
34 #include "../core/pool_func.hpp"
35 #include "../gfx_func.h"
41 #include "../safeguards.h"
43 #ifdef DEBUG_DUMP_COMMANDS
44 #include "../fileio_func.h"
45 /** When running the server till the wait point, run as fast as we can! */
46 bool _ddc_fastforward
= true;
47 #endif /* DEBUG_DUMP_COMMANDS */
49 /** Make sure both pools have the same size. */
50 static_assert(NetworkClientInfoPool::MAX_SIZE
== NetworkClientSocketPool::MAX_SIZE
);
52 /** The pool with client information. */
53 NetworkClientInfoPool
_networkclientinfo_pool("NetworkClientInfo");
54 INSTANTIATE_POOL_METHODS(NetworkClientInfo
)
56 bool _networking
; ///< are we in networking mode?
57 bool _network_server
; ///< network-server is active
58 bool _network_available
; ///< is network mode available?
59 bool _network_dedicated
; ///< are we a dedicated server?
60 bool _is_network_server
; ///< Does this client wants to be a network-server?
61 NetworkCompanyState
*_network_company_states
= nullptr; ///< Statistics about some companies.
62 ClientID _network_own_client_id
; ///< Our client identifier.
63 ClientID _redirect_console_to_client
; ///< If not invalid, redirect the console output to a client.
64 uint8 _network_reconnect
; ///< Reconnect timeout
65 StringList _network_bind_list
; ///< The addresses to bind on.
66 StringList _network_host_list
; ///< The servers we know.
67 StringList _network_ban_list
; ///< The banned clients.
68 uint32 _frame_counter_server
; ///< The frame_counter of the server, if in network-mode
69 uint32 _frame_counter_max
; ///< To where we may go with our clients
70 uint32 _frame_counter
; ///< The current frame.
71 uint32 _last_sync_frame
; ///< Used in the server to store the last time a sync packet was sent to clients.
72 NetworkAddressList _broadcast_list
; ///< List of broadcast addresses.
73 uint32 _sync_seed_1
; ///< Seed to compare during sync checks.
74 #ifdef NETWORK_SEND_DOUBLE_SEED
75 uint32 _sync_seed_2
; ///< Second part of the seed.
77 uint32 _sync_frame
; ///< The frame to perform the sync check.
78 bool _network_first_time
; ///< Whether we have finished joining or not.
79 CompanyMask _network_company_passworded
; ///< Bitmask of the password status of all companies.
81 static_assert((int)NETWORK_COMPANY_NAME_LENGTH
== MAX_LENGTH_COMPANY_NAME_CHARS
* MAX_CHAR_LENGTH
);
83 /** The amount of clients connected */
84 byte _network_clients_connected
= 0;
86 /* Some externs / forwards */
87 extern void StateGameLoop();
90 * Return whether there is any client connected or trying to connect at all.
91 * @return whether we have any client activity
95 return !NetworkClientSocket::Iterate().empty();
99 * Basically a client is leaving us right now.
101 NetworkClientInfo::~NetworkClientInfo()
103 /* Delete the chat window, if you were chatting with this client. */
104 InvalidateWindowData(WC_SEND_NETWORK_MSG
, DESTTYPE_CLIENT
, this->client_id
);
108 * Return the CI given it's client-identifier
109 * @param client_id the ClientID to search for
110 * @return return a pointer to the corresponding NetworkClientInfo struct or nullptr when not found
112 /* static */ NetworkClientInfo
*NetworkClientInfo::GetByClientID(ClientID client_id
)
114 for (NetworkClientInfo
*ci
: NetworkClientInfo::Iterate()) {
115 if (ci
->client_id
== client_id
) return ci
;
122 * Return the client state given it's client-identifier
123 * @param client_id the ClientID to search for
124 * @return return a pointer to the corresponding NetworkClientSocket struct or nullptr when not found
126 /* static */ ServerNetworkGameSocketHandler
*ServerNetworkGameSocketHandler::GetByClientID(ClientID client_id
)
128 for (NetworkClientSocket
*cs
: NetworkClientSocket::Iterate()) {
129 if (cs
->client_id
== client_id
) return cs
;
135 byte
NetworkSpectatorCount()
139 for (const NetworkClientInfo
*ci
: NetworkClientInfo::Iterate()) {
140 if (ci
->client_playas
== COMPANY_SPECTATOR
) count
++;
143 /* Don't count a dedicated server as spectator */
144 if (_network_dedicated
) count
--;
150 * Change the company password of a given company.
151 * @param company_id ID of the company the password should be changed for.
152 * @param password The unhashed password we like to set ('*' or '' resets the password)
153 * @return The password.
155 std::string
NetworkChangeCompanyPassword(CompanyID company_id
, std::string password
)
157 if (password
.compare("*") == 0) password
= "";
159 if (_network_server
) {
160 NetworkServerSetCompanyPassword(company_id
, password
, false);
162 NetworkClientSetCompanyPassword(password
);
169 * Hash the given password using server ID and game seed.
170 * @param password Password to hash.
171 * @param password_server_id Server ID.
172 * @param password_game_seed Game seed.
173 * @return The hashed password.
175 std::string
GenerateCompanyPasswordHash(const std::string
&password
, const std::string
&password_server_id
, uint32 password_game_seed
)
177 if (password
.empty()) return password
;
179 size_t password_length
= password
.size();
180 size_t password_server_id_length
= password_server_id
.size();
182 std::ostringstream salted_password
;
183 /* Add the password with the server's ID and game seed as the salt. */
184 for (uint i
= 0; i
< NETWORK_SERVER_ID_LENGTH
- 1; i
++) {
185 char password_char
= (i
< password_length
? password
[i
] : 0);
186 char server_id_char
= (i
< password_server_id_length
? password_server_id
[i
] : 0);
187 char seed_char
= password_game_seed
>> (i
% 32);
188 salted_password
<< (char)(password_char
^ server_id_char
^ seed_char
); // Cast needed, otherwise interpreted as integer to format
194 /* Generate the MD5 hash */
195 std::string salted_password_string
= salted_password
.str();
196 checksum
.Append(salted_password_string
.data(), salted_password_string
.size());
197 checksum
.Finish(digest
);
199 std::ostringstream hashed_password
;
200 hashed_password
<< std::hex
<< std::setfill('0');
201 for (int di
= 0; di
< 16; di
++) hashed_password
<< std::setw(2) << (int)digest
[di
]; // Cast needed, otherwise interpreted as character to add
203 return hashed_password
.str();
207 * Check if the company we want to join requires a password.
208 * @param company_id id of the company we want to check the 'passworded' flag for.
209 * @return true if the company requires a password.
211 bool NetworkCompanyIsPassworded(CompanyID company_id
)
213 return HasBit(_network_company_passworded
, company_id
);
216 /* This puts a text-message to the console, or in the future, the chat-box,
217 * (to keep it all a bit more general)
218 * If 'self_send' is true, this is the client who is sending the message */
219 void NetworkTextMessage(NetworkAction action
, TextColour colour
, bool self_send
, const std::string
&name
, const std::string
&str
, int64 data
)
223 case NETWORK_ACTION_SERVER_MESSAGE
:
224 /* Ignore invalid messages */
225 strid
= STR_NETWORK_SERVER_MESSAGE
;
228 case NETWORK_ACTION_COMPANY_SPECTATOR
:
230 strid
= STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE
;
232 case NETWORK_ACTION_COMPANY_JOIN
:
234 strid
= STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN
;
236 case NETWORK_ACTION_COMPANY_NEW
:
238 strid
= STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW
;
240 case NETWORK_ACTION_JOIN
:
241 /* Show the Client ID for the server but not for the client. */
242 strid
= _network_server
? STR_NETWORK_MESSAGE_CLIENT_JOINED_ID
: STR_NETWORK_MESSAGE_CLIENT_JOINED
;
244 case NETWORK_ACTION_LEAVE
: strid
= STR_NETWORK_MESSAGE_CLIENT_LEFT
; break;
245 case NETWORK_ACTION_NAME_CHANGE
: strid
= STR_NETWORK_MESSAGE_NAME_CHANGE
; break;
246 case NETWORK_ACTION_GIVE_MONEY
: strid
= STR_NETWORK_MESSAGE_GIVE_MONEY
; break;
247 case NETWORK_ACTION_CHAT_COMPANY
: strid
= self_send
? STR_NETWORK_CHAT_TO_COMPANY
: STR_NETWORK_CHAT_COMPANY
; break;
248 case NETWORK_ACTION_CHAT_CLIENT
: strid
= self_send
? STR_NETWORK_CHAT_TO_CLIENT
: STR_NETWORK_CHAT_CLIENT
; break;
249 case NETWORK_ACTION_KICKED
: strid
= STR_NETWORK_MESSAGE_KICKED
; break;
250 default: strid
= STR_NETWORK_CHAT_ALL
; break;
254 SetDParamStr(0, name
);
255 SetDParamStr(1, str
);
258 /* All of these strings start with "***". These characters are interpreted as both left-to-right and
259 * right-to-left characters depending on the context. As the next text might be an user's name, the
260 * user name's characters will influence the direction of the "***" instead of the language setting
261 * of the game. Manually set the direction of the "***" by inserting a text-direction marker. */
262 char *msg_ptr
= message
+ Utf8Encode(message
, _current_text_dir
== TD_LTR
? CHAR_TD_LRM
: CHAR_TD_RLM
);
263 GetString(msg_ptr
, strid
, lastof(message
));
265 Debug(desync
, 1, "msg: {:08x}; {:02x}; {}", _date
, _date_fract
, message
);
266 IConsolePrint(colour
, message
);
267 NetworkAddChatMessage((TextColour
)colour
, _settings_client
.gui
.network_chat_timeout
, message
);
270 /* Calculate the frame-lag of a client */
271 uint
NetworkCalculateLag(const NetworkClientSocket
*cs
)
273 int lag
= cs
->last_frame_server
- cs
->last_frame
;
274 /* This client has missed their ACK packet after 1 DAY_TICKS..
275 * so we increase their lag for every frame that passes!
276 * The packet can be out by a max of _net_frame_freq */
277 if (cs
->last_frame_server
+ DAY_TICKS
+ _settings_client
.network
.frame_freq
< _frame_counter
) {
278 lag
+= _frame_counter
- (cs
->last_frame_server
+ DAY_TICKS
+ _settings_client
.network
.frame_freq
);
284 /* There was a non-recoverable error, drop back to the main menu with a nice
286 void ShowNetworkError(StringID error_string
)
288 _switch_mode
= SM_MENU
;
289 ShowErrorMessage(error_string
, INVALID_STRING_ID
, WL_CRITICAL
);
293 * Retrieve the string id of an internal error number
294 * @param err NetworkErrorCode
295 * @return the StringID
297 StringID
GetNetworkErrorMsg(NetworkErrorCode err
)
299 /* List of possible network errors, used by
300 * PACKET_SERVER_ERROR and PACKET_CLIENT_ERROR */
301 static const StringID network_error_strings
[] = {
302 STR_NETWORK_ERROR_CLIENT_GENERAL
,
303 STR_NETWORK_ERROR_CLIENT_DESYNC
,
304 STR_NETWORK_ERROR_CLIENT_SAVEGAME
,
305 STR_NETWORK_ERROR_CLIENT_CONNECTION_LOST
,
306 STR_NETWORK_ERROR_CLIENT_PROTOCOL_ERROR
,
307 STR_NETWORK_ERROR_CLIENT_NEWGRF_MISMATCH
,
308 STR_NETWORK_ERROR_CLIENT_NOT_AUTHORIZED
,
309 STR_NETWORK_ERROR_CLIENT_NOT_EXPECTED
,
310 STR_NETWORK_ERROR_CLIENT_WRONG_REVISION
,
311 STR_NETWORK_ERROR_CLIENT_NAME_IN_USE
,
312 STR_NETWORK_ERROR_CLIENT_WRONG_PASSWORD
,
313 STR_NETWORK_ERROR_CLIENT_COMPANY_MISMATCH
,
314 STR_NETWORK_ERROR_CLIENT_KICKED
,
315 STR_NETWORK_ERROR_CLIENT_CHEATER
,
316 STR_NETWORK_ERROR_CLIENT_SERVER_FULL
,
317 STR_NETWORK_ERROR_CLIENT_TOO_MANY_COMMANDS
,
318 STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD
,
319 STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER
,
320 STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP
,
321 STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN
,
322 STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME
,
324 static_assert(lengthof(network_error_strings
) == NETWORK_ERROR_END
);
326 if (err
>= (ptrdiff_t)lengthof(network_error_strings
)) err
= NETWORK_ERROR_GENERAL
;
328 return network_error_strings
[err
];
332 * Handle the pause mode change so we send the right messages to the chat.
333 * @param prev_mode The previous pause mode.
334 * @param changed_mode The pause mode that got changed.
336 void NetworkHandlePauseChange(PauseMode prev_mode
, PauseMode changed_mode
)
338 if (!_networking
) return;
340 switch (changed_mode
) {
341 case PM_PAUSED_NORMAL
:
343 case PM_PAUSED_GAME_SCRIPT
:
344 case PM_PAUSED_ACTIVE_CLIENTS
:
345 case PM_PAUSED_LINK_GRAPH
: {
346 bool changed
= ((_pause_mode
== PM_UNPAUSED
) != (prev_mode
== PM_UNPAUSED
));
347 bool paused
= (_pause_mode
!= PM_UNPAUSED
);
348 if (!paused
&& !changed
) return;
354 if ((_pause_mode
& PM_PAUSED_NORMAL
) != PM_UNPAUSED
) SetDParam(++i
, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_MANUAL
);
355 if ((_pause_mode
& PM_PAUSED_JOIN
) != PM_UNPAUSED
) SetDParam(++i
, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_CONNECTING_CLIENTS
);
356 if ((_pause_mode
& PM_PAUSED_GAME_SCRIPT
) != PM_UNPAUSED
) SetDParam(++i
, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_GAME_SCRIPT
);
357 if ((_pause_mode
& PM_PAUSED_ACTIVE_CLIENTS
) != PM_UNPAUSED
) SetDParam(++i
, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_NOT_ENOUGH_PLAYERS
);
358 if ((_pause_mode
& PM_PAUSED_LINK_GRAPH
) != PM_UNPAUSED
) SetDParam(++i
, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_LINK_GRAPH
);
359 str
= STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_1
+ i
;
361 switch (changed_mode
) {
362 case PM_PAUSED_NORMAL
: SetDParam(0, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_MANUAL
); break;
363 case PM_PAUSED_JOIN
: SetDParam(0, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_CONNECTING_CLIENTS
); break;
364 case PM_PAUSED_GAME_SCRIPT
: SetDParam(0, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_GAME_SCRIPT
); break;
365 case PM_PAUSED_ACTIVE_CLIENTS
: SetDParam(0, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_NOT_ENOUGH_PLAYERS
); break;
366 case PM_PAUSED_LINK_GRAPH
: SetDParam(0, STR_NETWORK_SERVER_MESSAGE_GAME_REASON_LINK_GRAPH
); break;
367 default: NOT_REACHED();
369 str
= paused
? STR_NETWORK_SERVER_MESSAGE_GAME_PAUSED
: STR_NETWORK_SERVER_MESSAGE_GAME_UNPAUSED
;
372 NetworkTextMessage(NETWORK_ACTION_SERVER_MESSAGE
, CC_DEFAULT
, false, "", GetString(str
));
383 * Helper function for the pause checkers. If pause is true and the
384 * current pause mode isn't set the game will be paused, if it it false
385 * and the pause mode is set the game will be unpaused. In the other
386 * cases nothing happens to the pause state.
387 * @param pause whether we'd like to pause
388 * @param pm the mode which we would like to pause with
390 static void CheckPauseHelper(bool pause
, PauseMode pm
)
392 if (pause
== ((_pause_mode
& pm
) != PM_UNPAUSED
)) return;
394 DoCommandP(0, pm
, pause
? 1 : 0, CMD_PAUSE
);
398 * Counts the number of active clients connected.
399 * It has to be in STATUS_ACTIVE and not a spectator
400 * @return number of active clients
402 static uint
NetworkCountActiveClients()
406 for (const NetworkClientSocket
*cs
: NetworkClientSocket::Iterate()) {
407 if (cs
->status
!= NetworkClientSocket::STATUS_ACTIVE
) continue;
408 if (!Company::IsValidID(cs
->GetInfo()->client_playas
)) continue;
416 * Check if the minimum number of active clients has been reached and pause or unpause the game as appropriate
418 static void CheckMinActiveClients()
420 if ((_pause_mode
& PM_PAUSED_ERROR
) != PM_UNPAUSED
||
421 !_network_dedicated
||
422 (_settings_client
.network
.min_active_clients
== 0 && (_pause_mode
& PM_PAUSED_ACTIVE_CLIENTS
) == PM_UNPAUSED
)) {
425 CheckPauseHelper(NetworkCountActiveClients() < _settings_client
.network
.min_active_clients
, PM_PAUSED_ACTIVE_CLIENTS
);
429 * Checks whether there is a joining client
430 * @return true iff one client is joining (but not authorizing)
432 static bool NetworkHasJoiningClient()
434 for (const NetworkClientSocket
*cs
: NetworkClientSocket::Iterate()) {
435 if (cs
->status
>= NetworkClientSocket::STATUS_AUTHORIZED
&& cs
->status
< NetworkClientSocket::STATUS_ACTIVE
) return true;
442 * Check whether we should pause on join
444 static void CheckPauseOnJoin()
446 if ((_pause_mode
& PM_PAUSED_ERROR
) != PM_UNPAUSED
||
447 (!_settings_client
.network
.pause_on_join
&& (_pause_mode
& PM_PAUSED_JOIN
) == PM_UNPAUSED
)) {
450 CheckPauseHelper(NetworkHasJoiningClient(), PM_PAUSED_JOIN
);
454 * Parse the company part ("#company" postfix) of a connecting string.
455 * @param connection_string The string with the connection data.
456 * @param company_id The company ID to set, if available.
457 * @return A std::string_view into the connection string without the company part.
459 std::string_view
ParseCompanyFromConnectionString(const std::string
&connection_string
, CompanyID
*company_id
)
461 std::string_view ip
= connection_string
;
462 if (company_id
== nullptr) return ip
;
464 size_t offset
= ip
.find_last_of('#');
465 if (offset
!= std::string::npos
) {
466 std::string_view company_string
= ip
.substr(offset
+ 1);
467 ip
= ip
.substr(0, offset
);
470 auto [_
, err
] = std::from_chars(company_string
.data(), company_string
.data() + company_string
.size(), company_value
);
471 if (err
== std::errc()) {
472 if (company_value
!= COMPANY_NEW_COMPANY
&& company_value
!= COMPANY_SPECTATOR
) {
473 if (company_value
> MAX_COMPANIES
|| company_value
== 0) {
474 *company_id
= COMPANY_SPECTATOR
;
476 /* "#1" means the first company, which has index 0. */
477 *company_id
= (CompanyID
)(company_value
- 1);
480 *company_id
= (CompanyID
)company_value
;
489 * Converts a string to ip/port/company
490 * Format: IP:port#company
492 * Returns the IP part as a string view into the passed string. This view is
493 * valid as long the passed connection string is valid. If there is no port
494 * present in the connection string, the port reference will not be touched.
495 * When there is no company ID present in the connection string or company_id
496 * is nullptr, then company ID will not be touched.
498 * @param connection_string The string with the connection data.
499 * @param port The port reference to set.
500 * @param company_id The company ID to set, if available.
501 * @return A std::string_view into the connection string with the (IP) address part.
503 std::string_view
ParseFullConnectionString(const std::string
&connection_string
, uint16
&port
, CompanyID
*company_id
)
505 std::string_view ip
= ParseCompanyFromConnectionString(connection_string
, company_id
);
507 size_t port_offset
= ip
.find_last_of(':');
508 size_t ipv6_close
= ip
.find_last_of(']');
509 if (port_offset
!= std::string::npos
&& (ipv6_close
== std::string::npos
|| ipv6_close
< port_offset
)) {
510 std::string_view port_string
= ip
.substr(port_offset
+ 1);
511 ip
= ip
.substr(0, port_offset
);
512 std::from_chars(port_string
.data(), port_string
.data() + port_string
.size(), port
);
518 * Normalize a connection string. That is, ensure there is a port in the string.
519 * @param connection_string The connection string to normalize.
520 * @param default_port The port to use if none is given.
521 * @return The normalized connection string.
523 std::string
NormalizeConnectionString(const std::string
&connection_string
, uint16 default_port
)
525 uint16 port
= default_port
;
526 std::string_view ip
= ParseFullConnectionString(connection_string
, port
);
527 return std::string(ip
) + ":" + std::to_string(port
);
531 * Convert a string containing either "hostname" or "hostname:ip" to a
534 * @param connection_string The string to parse.
535 * @param default_port The default port to set port to if not in connection_string.
536 * @return A valid NetworkAddress of the parsed information.
538 NetworkAddress
ParseConnectionString(const std::string
&connection_string
, uint16 default_port
)
540 uint16 port
= default_port
;
541 std::string_view ip
= ParseFullConnectionString(connection_string
, port
);
542 return NetworkAddress(ip
, port
);
546 * Handle the accepting of a connection to the server.
547 * @param s The socket of the new connection.
548 * @param address The address of the peer.
550 /* static */ void ServerNetworkGameSocketHandler::AcceptConnection(SOCKET s
, const NetworkAddress
&address
)
552 /* Register the login */
553 _network_clients_connected
++;
555 ServerNetworkGameSocketHandler
*cs
= new ServerNetworkGameSocketHandler(s
);
556 cs
->client_address
= address
; // Save the IP of the client
558 InvalidateWindowData(WC_CLIENT_LIST
, 0);
562 * Resets the pools used for network clients, and the admin pool if needed.
563 * @param close_admins Whether the admin pool has to be cleared as well.
565 static void InitializeNetworkPools(bool close_admins
= true)
567 PoolBase::Clean(PT_NCLIENT
| (close_admins
? PT_NADMIN
: PT_NONE
));
571 * Close current connections.
572 * @param close_admins Whether the admin connections have to be closed as well.
574 void NetworkClose(bool close_admins
)
576 if (_network_server
) {
578 for (ServerNetworkAdminSocketHandler
*as
: ServerNetworkAdminSocketHandler::Iterate()) {
579 as
->CloseConnection(true);
583 for (NetworkClientSocket
*cs
: NetworkClientSocket::Iterate()) {
584 cs
->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT
);
586 ServerNetworkGameSocketHandler::CloseListeners();
587 ServerNetworkAdminSocketHandler::CloseListeners();
589 _network_coordinator_client
.CloseConnection();
591 if (MyClient::my_client
!= nullptr) {
592 MyClient::SendQuit();
593 MyClient::my_client
->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT
);
596 _network_coordinator_client
.CloseAllConnections();
599 TCPConnecter::KillAll();
602 _network_server
= false;
604 NetworkFreeLocalCommandQueue();
606 delete[] _network_company_states
;
607 _network_company_states
= nullptr;
609 InitializeNetworkPools(close_admins
);
612 /* Initializes the network (cleans sockets and stuff) */
613 static void NetworkInitialize(bool close_admins
= true)
615 InitializeNetworkPools(close_admins
);
618 _network_first_time
= true;
620 _network_reconnect
= 0;
623 /** Non blocking connection to query servers for their game info. */
624 class TCPQueryConnecter
: TCPServerConnecter
{
626 std::string connection_string
;
629 TCPQueryConnecter(const std::string
&connection_string
) : TCPServerConnecter(connection_string
, NETWORK_DEFAULT_PORT
), connection_string(connection_string
) {}
631 void OnFailure() override
633 NetworkGameList
*item
= NetworkGameListAddItem(connection_string
);
634 item
->online
= false;
636 UpdateNetworkGameWindow();
639 void OnConnect(SOCKET s
) override
642 new ClientNetworkGameSocketHandler(s
, this->connection_string
);
643 MyClient::SendInformationQuery(false);
648 * Query a server to fetch the game-info.
649 * @param connection_string the address to query.
651 void NetworkQueryServer(const std::string
&connection_string
)
653 if (!_network_available
) return;
657 new TCPQueryConnecter(connection_string
);
660 /** Non blocking connection to query servers for their game and company info. */
661 class TCPLobbyQueryConnecter
: TCPServerConnecter
{
663 std::string connection_string
;
666 TCPLobbyQueryConnecter(const std::string
&connection_string
) : TCPServerConnecter(connection_string
, NETWORK_DEFAULT_PORT
), connection_string(connection_string
) {}
668 void OnFailure() override
670 CloseWindowById(WC_NETWORK_WINDOW
, WN_NETWORK_WINDOW_LOBBY
);
672 ShowErrorMessage(STR_NETWORK_ERROR_NOCONNECTION
, INVALID_STRING_ID
, WL_ERROR
);
675 void OnConnect(SOCKET s
) override
678 new ClientNetworkGameSocketHandler(s
, this->connection_string
);
679 MyClient::SendInformationQuery(true);
684 * Query a server to fetch the game-info for the lobby.
685 * @param connection_string the address to query.
687 void NetworkQueryLobbyServer(const std::string
&connection_string
)
689 if (!_network_available
) return;
693 new TCPLobbyQueryConnecter(connection_string
);
697 * Validates an address entered as a string and adds the server to
698 * the list. If you use this function, the games will be marked
700 * @param connection_string The IP:port of the server to add.
701 * @param manually Whether the enter should be marked as manual added.
702 * @param never_expire Whether the entry can expire (removed when no longer found in the public listing).
703 * @return The entry on the game list.
705 NetworkGameList
*NetworkAddServer(const std::string
&connection_string
, bool manually
, bool never_expire
)
707 if (connection_string
.empty()) return nullptr;
709 /* Ensure the item already exists in the list */
710 NetworkGameList
*item
= NetworkGameListAddItem(connection_string
);
711 if (item
->info
.server_name
.empty()) {
712 ClearGRFConfigList(&item
->info
.grfconfig
);
713 item
->info
.server_name
= connection_string
;
715 UpdateNetworkGameWindow();
717 NetworkQueryServer(connection_string
);
720 if (manually
) item
->manually
= true;
721 if (never_expire
) item
->version
= INT32_MAX
;
727 * Get the addresses to bind to.
728 * @param addresses the list to write to.
729 * @param port the port to bind to.
731 void GetBindAddresses(NetworkAddressList
*addresses
, uint16 port
)
733 for (const auto &iter
: _network_bind_list
) {
734 addresses
->emplace_back(iter
.c_str(), port
);
737 /* No address, so bind to everything. */
738 if (addresses
->size() == 0) {
739 addresses
->emplace_back("", port
);
743 /* Generates the list of manually added hosts from NetworkGameList and
744 * dumps them into the array _network_host_list. This array is needed
745 * by the function that generates the config file. */
746 void NetworkRebuildHostList()
748 _network_host_list
.clear();
750 for (NetworkGameList
*item
= _network_game_list
; item
!= nullptr; item
= item
->next
) {
751 if (item
->manually
) _network_host_list
.emplace_back(item
->connection_string
);
755 /** Non blocking connection create to actually connect to servers */
756 class TCPClientConnecter
: TCPServerConnecter
{
758 std::string connection_string
;
761 TCPClientConnecter(const std::string
&connection_string
) : TCPServerConnecter(connection_string
, NETWORK_DEFAULT_PORT
), connection_string(connection_string
) {}
763 void OnFailure() override
765 ShowNetworkError(STR_NETWORK_ERROR_NOCONNECTION
);
768 void OnConnect(SOCKET s
) override
771 new ClientNetworkGameSocketHandler(s
, this->connection_string
);
772 IConsoleCmdExec("exec scripts/on_client.scr 0");
773 NetworkClient_Connected();
778 * Join a client to the server at with the given connection string.
779 * The default for the passwords is \c nullptr. When the server or company needs a
780 * password and none is given, the user is asked to enter the password in the GUI.
781 * This function will return false whenever some information required to join is not
782 * correct such as the company number or the client's name, or when there is not
783 * networking avalabile at all. If the function returns false the connection with
784 * the existing server is not disconnected.
785 * It will return true when it starts the actual join process, i.e. when it
786 * actually shows the join status window.
788 * @param connection_string The IP address, port and company number to join as.
789 * @param default_company The company number to join as when none is given.
790 * @param join_server_password The password for the server.
791 * @param join_company_password The password for the company.
792 * @return Whether the join has started.
794 bool NetworkClientConnectGame(const std::string
&connection_string
, CompanyID default_company
, const std::string
&join_server_password
, const std::string
&join_company_password
)
796 CompanyID join_as
= default_company
;
797 std::string resolved_connection_string
= ServerAddress::Parse(connection_string
, NETWORK_DEFAULT_PORT
, &join_as
).connection_string
;
799 if (!_network_available
) return false;
800 if (!NetworkValidateOurClientName()) return false;
802 _network_join
.connection_string
= resolved_connection_string
;
803 _network_join
.company
= join_as
;
804 _network_join
.server_password
= join_server_password
;
805 _network_join
.company_password
= join_company_password
;
807 if (_game_mode
== GM_MENU
) {
808 /* From the menu we can immediately continue with the actual join. */
809 NetworkClientJoinGame();
811 /* When already playing a game, first go back to the main menu. This
812 * disconnects the user from the current game, meaning we can safely
813 * load in the new. After all, there is little point in continueing to
814 * play on a server if we are connecting to another one.
816 _switch_mode
= SM_JOIN_GAME
;
822 * Actually perform the joining to the server. Use #NetworkClientConnectGame
823 * when you want to connect to a specific server/company. This function
824 * assumes _network_join is already fully set up.
826 void NetworkClientJoinGame()
831 _settings_client
.network
.last_joined
= _network_join
.connection_string
;
832 _network_join_status
= NETWORK_JOIN_STATUS_CONNECTING
;
833 ShowJoinStatusWindow();
835 new TCPClientConnecter(_network_join
.connection_string
);
838 static void NetworkInitGameInfo()
840 FillStaticNetworkServerGameInfo();
841 /* The server is a client too */
842 _network_game_info
.clients_on
= _network_dedicated
? 0 : 1;
844 /* There should be always space for the server. */
845 assert(NetworkClientInfo::CanAllocateItem());
846 NetworkClientInfo
*ci
= new NetworkClientInfo(CLIENT_ID_SERVER
);
847 ci
->client_playas
= _network_dedicated
? COMPANY_SPECTATOR
: COMPANY_FIRST
;
849 ci
->client_name
= _settings_client
.network
.client_name
;
853 * Trim the given server name in place, i.e. remove leading and trailing spaces.
854 * After the trim check whether the server name is not empty.
855 * When the server name is empty a GUI error message is shown telling the
856 * user to set the servername and this function returns false.
858 * @param server_name The server name to validate. It will be trimmed of leading
859 * and trailing spaces.
860 * @return True iff the server name is valid.
862 bool NetworkValidateServerName(std::string
&server_name
)
864 StrTrimInPlace(server_name
);
865 if (!server_name
.empty()) return true;
867 ShowErrorMessage(STR_NETWORK_ERROR_BAD_SERVER_NAME
, INVALID_STRING_ID
, WL_ERROR
);
872 * Check whether the client and server name are set, for a dedicated server and if not set them to some default
873 * value and tell the user to change this as soon as possible.
874 * If the saved name is the default value, then the user is told to override this value too.
875 * This is only meant dedicated servers, as for the other servers the GUI ensures a name has been entered.
877 static void CheckClientAndServerName()
879 static const std::string fallback_client_name
= "Unnamed Client";
880 StrTrimInPlace(_settings_client
.network
.client_name
);
881 if (_settings_client
.network
.client_name
.empty() || _settings_client
.network
.client_name
.compare(fallback_client_name
) == 0) {
882 Debug(net
, 1, "No \"client_name\" has been set, using \"{}\" instead. Please set this now using the \"name <new name>\" command", fallback_client_name
);
883 _settings_client
.network
.client_name
= fallback_client_name
;
886 static const std::string fallback_server_name
= "Unnamed Server";
887 StrTrimInPlace(_settings_client
.network
.server_name
);
888 if (_settings_client
.network
.server_name
.empty() || _settings_client
.network
.server_name
.compare(fallback_server_name
) == 0) {
889 Debug(net
, 1, "No \"server_name\" has been set, using \"{}\" instead. Please set this now using the \"server_name <new name>\" command", fallback_server_name
);
890 _settings_client
.network
.server_name
= fallback_server_name
;
894 bool NetworkServerStart()
896 if (!_network_available
) return false;
898 /* Call the pre-scripts */
899 IConsoleCmdExec("exec scripts/pre_server.scr 0");
900 if (_network_dedicated
) IConsoleCmdExec("exec scripts/pre_dedicated.scr 0");
902 /* Check for the client and server names to be set, but only after the scripts had a chance to set them.*/
903 if (_network_dedicated
) CheckClientAndServerName();
905 NetworkDisconnect(false, false);
906 NetworkInitialize(false);
907 NetworkUDPInitialize();
908 Debug(net
, 5, "Starting listeners for clients");
909 if (!ServerNetworkGameSocketHandler::Listen(_settings_client
.network
.server_port
)) return false;
911 /* Only listen for admins when the password isn't empty. */
912 if (!_settings_client
.network
.admin_password
.empty()) {
913 Debug(net
, 5, "Starting listeners for admins");
914 if (!ServerNetworkAdminSocketHandler::Listen(_settings_client
.network
.server_admin_port
)) return false;
917 /* Try to start UDP-server */
918 Debug(net
, 5, "Starting listeners for incoming server queries");
919 NetworkUDPServerListen();
921 _network_company_states
= new NetworkCompanyState
[MAX_COMPANIES
];
922 _network_server
= true;
925 _frame_counter_server
= 0;
926 _frame_counter_max
= 0;
927 _last_sync_frame
= 0;
928 _network_own_client_id
= CLIENT_ID_SERVER
;
930 _network_clients_connected
= 0;
931 _network_company_passworded
= 0;
933 NetworkInitGameInfo();
935 if (_settings_client
.network
.server_game_type
!= SERVER_GAME_TYPE_LOCAL
) {
936 _network_coordinator_client
.Register();
939 /* execute server initialization script */
940 IConsoleCmdExec("exec scripts/on_server.scr 0");
941 /* if the server is dedicated ... add some other script */
942 if (_network_dedicated
) IConsoleCmdExec("exec scripts/on_dedicated.scr 0");
944 /* welcome possibly still connected admins - this can only happen on a dedicated server. */
945 if (_network_dedicated
) ServerNetworkAdminSocketHandler::WelcomeAll();
950 /* The server is rebooting...
951 * The only difference with NetworkDisconnect, is the packets that is sent */
954 if (_network_server
) {
955 for (NetworkClientSocket
*cs
: NetworkClientSocket::Iterate()) {
960 for (ServerNetworkAdminSocketHandler
*as
: ServerNetworkAdminSocketHandler::IterateActive()) {
966 /* For non-dedicated servers we have to kick the admins as we are not
967 * certain that we will end up in a new network game. */
968 NetworkClose(!_network_dedicated
);
972 * We want to disconnect from the host/clients.
973 * @param blocking whether to wait till everything has been closed.
974 * @param close_admins Whether the admin sockets need to be closed as well.
976 void NetworkDisconnect(bool blocking
, bool close_admins
)
978 if (_network_server
) {
979 for (NetworkClientSocket
*cs
: NetworkClientSocket::Iterate()) {
985 for (ServerNetworkAdminSocketHandler
*as
: ServerNetworkAdminSocketHandler::IterateActive()) {
992 CloseWindowById(WC_NETWORK_STATUS_WINDOW
, WN_NETWORK_STATUS_WINDOW_JOIN
);
994 NetworkClose(close_admins
);
996 /* Reinitialize the UDP stack, i.e. close all existing connections. */
997 NetworkUDPInitialize();
1001 * The setting server_game_type was updated; possibly we need to take some
1004 void NetworkUpdateServerGameType()
1006 if (!_networking
) return;
1008 switch (_settings_client
.network
.server_game_type
) {
1009 case SERVER_GAME_TYPE_LOCAL
:
1010 _network_coordinator_client
.CloseConnection();
1013 case SERVER_GAME_TYPE_INVITE_ONLY
:
1014 case SERVER_GAME_TYPE_PUBLIC
:
1015 _network_coordinator_client
.Register();
1024 * Receives something from the network.
1025 * @return true if everything went fine, false when the connection got closed.
1027 static bool NetworkReceive()
1029 if (_network_server
) {
1030 ServerNetworkAdminSocketHandler::Receive();
1031 return ServerNetworkGameSocketHandler::Receive();
1033 return ClientNetworkGameSocketHandler::Receive();
1037 /* This sends all buffered commands (if possible) */
1038 static void NetworkSend()
1040 if (_network_server
) {
1041 ServerNetworkAdminSocketHandler::Send();
1042 ServerNetworkGameSocketHandler::Send();
1044 ClientNetworkGameSocketHandler::Send();
1049 * We have to do some (simple) background stuff that runs normally,
1050 * even when we are not in multiplayer. For example stuff needed
1051 * for finding servers or downloading content.
1053 void NetworkBackgroundLoop()
1055 _network_content_client
.SendReceive();
1056 _network_coordinator_client
.SendReceive();
1057 TCPConnecter::CheckCallbacks();
1058 NetworkHTTPSocketHandler::HTTPReceive();
1060 NetworkBackgroundUDPLoop();
1063 /* The main loop called from ttd.c
1064 * Here we also have to do StateGameLoop if needed! */
1065 void NetworkGameLoop()
1067 if (!_networking
) return;
1069 if (!NetworkReceive()) return;
1071 if (_network_server
) {
1072 /* Log the sync state to check for in-syncedness of replays. */
1073 if (_date_fract
== 0) {
1074 /* We don't want to log multiple times if paused. */
1075 static Date last_log
;
1076 if (last_log
!= _date
) {
1077 Debug(desync
, 1, "sync: {:08x}; {:02x}; {:08x}; {:08x}", _date
, _date_fract
, _random
.state
[0], _random
.state
[1]);
1082 #ifdef DEBUG_DUMP_COMMANDS
1083 /* Loading of the debug commands from -ddesync>=1 */
1084 static FILE *f
= FioFOpenFile("commands.log", "rb", SAVE_DIR
);
1085 static Date next_date
= 0;
1086 static uint32 next_date_fract
;
1087 static CommandPacket
*cp
= nullptr;
1088 static bool check_sync_state
= false;
1089 static uint32 sync_state
[2];
1090 if (f
== nullptr && next_date
== 0) {
1091 Debug(desync
, 0, "Cannot open commands.log");
1095 while (f
!= nullptr && !feof(f
)) {
1096 if (_date
== next_date
&& _date_fract
== next_date_fract
) {
1097 if (cp
!= nullptr) {
1098 NetworkSendCommand(cp
->tile
, cp
->p1
, cp
->p2
, cp
->cmd
& ~CMD_FLAGS_MASK
, nullptr, cp
->text
, cp
->company
);
1099 Debug(desync
, 0, "Injecting: {:08x}; {:02x}; {:02x}; {:06x}; {:08x}; {:08x}; {:08x}; \"{}\" ({})", _date
, _date_fract
, (int)_current_company
, cp
->tile
, cp
->p1
, cp
->p2
, cp
->cmd
, cp
->text
, GetCommandName(cp
->cmd
));
1103 if (check_sync_state
) {
1104 if (sync_state
[0] == _random
.state
[0] && sync_state
[1] == _random
.state
[1]) {
1105 Debug(desync
, 0, "Sync check: {:08x}; {:02x}; match", _date
, _date_fract
);
1107 Debug(desync
, 0, "Sync check: {:08x}; {:02x}; mismatch expected {{:08x}, {:08x}}, got {{:08x}, {:08x}}",
1108 _date
, _date_fract
, sync_state
[0], sync_state
[1], _random
.state
[0], _random
.state
[1]);
1111 check_sync_state
= false;
1115 if (cp
!= nullptr || check_sync_state
) break;
1118 if (fgets(buff
, lengthof(buff
), f
) == nullptr) break;
1121 /* Ignore the "[date time] " part of the message */
1124 if (p
== nullptr) break;
1128 if (strncmp(p
, "cmd: ", 5) == 0
1129 #ifdef DEBUG_FAILED_DUMP_COMMANDS
1130 || strncmp(p
, "cmdf: ", 6) == 0
1135 cp
= new CommandPacket();
1138 int ret
= sscanf(p
, "%x; %x; %x; %x; %x; %x; %x; \"%127[^\"]\"", &next_date
, &next_date_fract
, &company
, &cp
->tile
, &cp
->p1
, &cp
->p2
, &cp
->cmd
, buffer
);
1140 /* There are 8 pieces of data to read, however the last is a
1141 * string that might or might not exist. Ignore it if that
1142 * string misses because in 99% of the time it's not used. */
1143 assert(ret
== 8 || ret
== 7);
1144 cp
->company
= (CompanyID
)company
;
1145 } else if (strncmp(p
, "join: ", 6) == 0) {
1146 /* Manually insert a pause when joining; this way the client can join at the exact right time. */
1147 int ret
= sscanf(p
+ 6, "%x; %x", &next_date
, &next_date_fract
);
1149 Debug(desync
, 0, "Injecting pause for join at {:08x}:{:02x}; please join when paused", next_date
, next_date_fract
);
1150 cp
= new CommandPacket();
1151 cp
->company
= COMPANY_SPECTATOR
;
1152 cp
->cmd
= CMD_PAUSE
;
1153 cp
->p1
= PM_PAUSED_NORMAL
;
1155 _ddc_fastforward
= false;
1156 } else if (strncmp(p
, "sync: ", 6) == 0) {
1157 int ret
= sscanf(p
+ 6, "%x; %x; %x; %x", &next_date
, &next_date_fract
, &sync_state
[0], &sync_state
[1]);
1159 check_sync_state
= true;
1160 } else if (strncmp(p
, "msg: ", 5) == 0 || strncmp(p
, "client: ", 8) == 0 ||
1161 strncmp(p
, "load: ", 6) == 0 || strncmp(p
, "save: ", 6) == 0) {
1162 /* A message that is not very important to the log playback, but part of the log. */
1163 #ifndef DEBUG_FAILED_DUMP_COMMANDS
1164 } else if (strncmp(p
, "cmdf: ", 6) == 0) {
1165 Debug(desync
, 0, "Skipping replay of failed command: {}", p
+ 6);
1168 /* Can't parse a line; what's wrong here? */
1169 Debug(desync
, 0, "Trying to parse: {}", p
);
1173 if (f
!= nullptr && feof(f
)) {
1174 Debug(desync
, 0, "End of commands.log");
1178 #endif /* DEBUG_DUMP_COMMANDS */
1179 if (_frame_counter
>= _frame_counter_max
) {
1180 /* Only check for active clients just before we're going to send out
1181 * the commands so we don't send multiple pause/unpause commands when
1182 * the frame_freq is more than 1 tick. Same with distributing commands. */
1184 CheckMinActiveClients();
1185 NetworkDistributeCommands();
1188 bool send_frame
= false;
1190 /* We first increase the _frame_counter */
1192 /* Update max-frame-counter */
1193 if (_frame_counter
> _frame_counter_max
) {
1194 _frame_counter_max
= _frame_counter
+ _settings_client
.network
.frame_freq
;
1198 NetworkExecuteLocalCommandQueue();
1200 /* Then we make the frame */
1203 _sync_seed_1
= _random
.state
[0];
1204 #ifdef NETWORK_SEND_DOUBLE_SEED
1205 _sync_seed_2
= _random
.state
[1];
1208 NetworkServer_Tick(send_frame
);
1212 /* Make sure we are at the frame were the server is (quick-frames) */
1213 if (_frame_counter_server
> _frame_counter
) {
1214 /* Run a number of frames; when things go bad, get out. */
1215 while (_frame_counter_server
> _frame_counter
) {
1216 if (!ClientNetworkGameSocketHandler::GameLoop()) return;
1219 /* Else, keep on going till _frame_counter_max */
1220 if (_frame_counter_max
> _frame_counter
) {
1221 /* Run one frame; if things went bad, get out. */
1222 if (!ClientNetworkGameSocketHandler::GameLoop()) return;
1230 static void NetworkGenerateServerId()
1234 char hex_output
[16 * 2 + 1];
1235 char coding_string
[NETWORK_NAME_LENGTH
];
1238 seprintf(coding_string
, lastof(coding_string
), "%d%s", (uint
)Random(), "OpenTTD Server ID");
1240 /* Generate the MD5 hash */
1241 checksum
.Append((const uint8
*)coding_string
, strlen(coding_string
));
1242 checksum
.Finish(digest
);
1244 for (di
= 0; di
< 16; ++di
) {
1245 seprintf(hex_output
+ di
* 2, lastof(hex_output
), "%02x", digest
[di
]);
1248 /* _settings_client.network.network_id is our id */
1249 _settings_client
.network
.network_id
= hex_output
;
1252 class TCPNetworkDebugConnecter
: TCPConnecter
{
1254 std::string connection_string
;
1257 TCPNetworkDebugConnecter(const std::string
&connection_string
) : TCPConnecter(connection_string
, NETWORK_DEFAULT_DEBUGLOG_PORT
), connection_string(connection_string
) {}
1259 void OnFailure() override
1261 Debug(net
, 0, "Failed to open connection to {} for redirecting Debug()", this->connection_string
);
1264 void OnConnect(SOCKET s
) override
1266 Debug(net
, 3, "Redirecting Debug() to {}", this->connection_string
);
1268 extern SOCKET _debug_socket
;
1273 void NetworkStartDebugLog(const std::string
&connection_string
)
1275 new TCPNetworkDebugConnecter(connection_string
);
1278 /** This tries to launch the network for a given OS */
1279 void NetworkStartUp()
1281 Debug(net
, 3, "Starting network");
1283 /* Network is available */
1284 _network_available
= NetworkCoreInitialize();
1285 _network_dedicated
= false;
1287 /* Generate an server id when there is none yet */
1288 if (_settings_client
.network
.network_id
.empty()) NetworkGenerateServerId();
1290 _network_game_info
= {};
1292 NetworkInitialize();
1293 NetworkUDPInitialize();
1294 Debug(net
, 3, "Network online, multiplayer available");
1295 NetworkFindBroadcastIPs(&_broadcast_list
);
1298 /** This shuts the network down */
1299 void NetworkShutDown()
1301 NetworkDisconnect(true);
1304 Debug(net
, 3, "Shutting down network");
1306 _network_available
= false;
1308 NetworkCoreShutdown();
1311 #ifdef __EMSCRIPTEN__
1314 void CDECL
em_openttd_add_server(const char *connection_string
)
1316 NetworkAddServer(connection_string
, false, true);