Fix #8316: Make sort industries by production and transported with a cargo filter...
[openttd-github.git] / src / network / network.cpp
blob3bce6d4d5dae311cdaac4fafacd42eea4c466960
1 /*
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/>.
6 */
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"
23 #include "core/udp.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"
33 #include "../rev.h"
34 #include "../core/pool_func.hpp"
35 #include "../gfx_func.h"
36 #include "../error.h"
37 #include <charconv>
38 #include <sstream>
39 #include <iomanip>
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.
76 #endif
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();
89 /**
90 * Return whether there is any client connected or trying to connect at all.
91 * @return whether we have any client activity
93 bool HasClients()
95 return !NetworkClientSocket::Iterate().empty();
98 /**
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;
118 return nullptr;
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;
132 return nullptr;
135 byte NetworkSpectatorCount()
137 byte count = 0;
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--;
146 return 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);
161 } else {
162 NetworkClientSetCompanyPassword(password);
165 return 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
191 Md5 checksum;
192 uint8 digest[16];
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)
221 StringID strid;
222 switch (action) {
223 case NETWORK_ACTION_SERVER_MESSAGE:
224 /* Ignore invalid messages */
225 strid = STR_NETWORK_SERVER_MESSAGE;
226 colour = CC_DEFAULT;
227 break;
228 case NETWORK_ACTION_COMPANY_SPECTATOR:
229 colour = CC_DEFAULT;
230 strid = STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE;
231 break;
232 case NETWORK_ACTION_COMPANY_JOIN:
233 colour = CC_DEFAULT;
234 strid = STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN;
235 break;
236 case NETWORK_ACTION_COMPANY_NEW:
237 colour = CC_DEFAULT;
238 strid = STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW;
239 break;
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;
243 break;
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;
253 char message[1024];
254 SetDParamStr(0, name);
255 SetDParamStr(1, str);
256 SetDParam(2, data);
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);
280 return lag;
284 /* There was a non-recoverable error, drop back to the main menu with a nice
285 * error */
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:
342 case PM_PAUSED_JOIN:
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;
350 StringID str;
351 if (!changed) {
352 int i = -1;
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;
360 } else {
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));
373 break;
376 default:
377 return;
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()
404 uint count = 0;
406 for (const NetworkClientSocket *cs : NetworkClientSocket::Iterate()) {
407 if (cs->status != NetworkClientSocket::STATUS_ACTIVE) continue;
408 if (!Company::IsValidID(cs->GetInfo()->client_playas)) continue;
409 count++;
412 return count;
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)) {
423 return;
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;
438 return false;
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)) {
448 return;
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);
469 uint8 company_value;
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;
475 } else {
476 /* "#1" means the first company, which has index 0. */
477 *company_id = (CompanyID)(company_value - 1);
479 } else {
480 *company_id = (CompanyID)company_value;
485 return ip;
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);
514 return ip;
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
532 * NetworkAddress.
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) {
577 if (close_admins) {
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();
590 } else {
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();
601 _networking = false;
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);
617 _sync_frame = 0;
618 _network_first_time = true;
620 _network_reconnect = 0;
623 /** Non blocking connection to query servers for their game info. */
624 class TCPQueryConnecter : TCPServerConnecter {
625 private:
626 std::string connection_string;
628 public:
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
641 _networking = true;
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;
655 NetworkInitialize();
657 new TCPQueryConnecter(connection_string);
660 /** Non blocking connection to query servers for their game and company info. */
661 class TCPLobbyQueryConnecter : TCPServerConnecter {
662 private:
663 std::string connection_string;
665 public:
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
677 _networking = true;
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;
691 NetworkInitialize();
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
699 * as manually added.
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;
723 return item;
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 {
757 private:
758 std::string connection_string;
760 public:
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
770 _networking = true;
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();
810 } else {
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;
818 return true;
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()
828 NetworkDisconnect();
829 NetworkInitialize();
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);
868 return false;
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;
923 _networking = true;
924 _frame_counter = 0;
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();
947 return true;
950 /* The server is rebooting...
951 * The only difference with NetworkDisconnect, is the packets that is sent */
952 void NetworkReboot()
954 if (_network_server) {
955 for (NetworkClientSocket *cs : NetworkClientSocket::Iterate()) {
956 cs->SendNewGame();
957 cs->SendPackets();
960 for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::IterateActive()) {
961 as->SendNewGame();
962 as->SendPackets();
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()) {
980 cs->SendShutdown();
981 cs->SendPackets();
984 if (close_admins) {
985 for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::IterateActive()) {
986 as->SendShutdown();
987 as->SendPackets();
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
1002 * action.
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();
1011 break;
1013 case SERVER_GAME_TYPE_INVITE_ONLY:
1014 case SERVER_GAME_TYPE_PUBLIC:
1015 _network_coordinator_client.Register();
1016 break;
1018 default:
1019 NOT_REACHED();
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();
1032 } else {
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();
1043 } else {
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]);
1078 last_log = _date;
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");
1092 next_date = 1;
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));
1100 delete cp;
1101 cp = nullptr;
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);
1106 } else {
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]);
1109 NOT_REACHED();
1111 check_sync_state = false;
1115 if (cp != nullptr || check_sync_state) break;
1117 char buff[4096];
1118 if (fgets(buff, lengthof(buff), f) == nullptr) break;
1120 char *p = buff;
1121 /* Ignore the "[date time] " part of the message */
1122 if (*p == '[') {
1123 p = strchr(p, ']');
1124 if (p == nullptr) break;
1125 p += 2;
1128 if (strncmp(p, "cmd: ", 5) == 0
1129 #ifdef DEBUG_FAILED_DUMP_COMMANDS
1130 || strncmp(p, "cmdf: ", 6) == 0
1131 #endif
1133 p += 5;
1134 if (*p == ' ') p++;
1135 cp = new CommandPacket();
1136 int company;
1137 char buffer[128];
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);
1139 cp->text = 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);
1148 assert(ret == 2);
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;
1154 cp->p2 = 1;
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]);
1158 assert(ret == 4);
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);
1166 #endif
1167 } else {
1168 /* Can't parse a line; what's wrong here? */
1169 Debug(desync, 0, "Trying to parse: {}", p);
1170 NOT_REACHED();
1173 if (f != nullptr && feof(f)) {
1174 Debug(desync, 0, "End of commands.log");
1175 fclose(f);
1176 f = nullptr;
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. */
1183 CheckPauseOnJoin();
1184 CheckMinActiveClients();
1185 NetworkDistributeCommands();
1188 bool send_frame = false;
1190 /* We first increase the _frame_counter */
1191 _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;
1195 send_frame = true;
1198 NetworkExecuteLocalCommandQueue();
1200 /* Then we make the frame */
1201 StateGameLoop();
1203 _sync_seed_1 = _random.state[0];
1204 #ifdef NETWORK_SEND_DOUBLE_SEED
1205 _sync_seed_2 = _random.state[1];
1206 #endif
1208 NetworkServer_Tick(send_frame);
1209 } else {
1210 /* Client */
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;
1218 } else {
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;
1227 NetworkSend();
1230 static void NetworkGenerateServerId()
1232 Md5 checksum;
1233 uint8 digest[16];
1234 char hex_output[16 * 2 + 1];
1235 char coding_string[NETWORK_NAME_LENGTH];
1236 int di;
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 {
1253 private:
1254 std::string connection_string;
1256 public:
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;
1269 _debug_socket = s;
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);
1302 NetworkUDPClose();
1304 Debug(net, 3, "Shutting down network");
1306 _network_available = false;
1308 NetworkCoreShutdown();
1311 #ifdef __EMSCRIPTEN__
1312 extern "C" {
1314 void CDECL em_openttd_add_server(const char *connection_string)
1316 NetworkAddServer(connection_string, false, true);
1320 #endif