diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 5f100df78a..d34fbb55c2 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -13,6 +13,7 @@ #include "engine_func.h" #include "landscape.h" #include "saveload/saveload.h" +#include "network/core/game_info.h" #include "network/network.h" #include "network/network_func.h" #include "network/network_base.h" diff --git a/src/network/core/CMakeLists.txt b/src/network/core/CMakeLists.txt index c9368a5b46..37cc3e1954 100644 --- a/src/network/core/CMakeLists.txt +++ b/src/network/core/CMakeLists.txt @@ -4,7 +4,8 @@ add_files( config.h core.cpp core.h - game.h + game_info.cpp + game_info.h host.cpp host.h os_abstraction.h diff --git a/src/network/core/core.cpp b/src/network/core/core.cpp index 8c5c5c2292..5c12cb2242 100644 --- a/src/network/core/core.cpp +++ b/src/network/core/core.cpp @@ -65,31 +65,3 @@ const char *NetworkGetErrorString(int error) return buffer; } #endif /* defined(_WIN32) */ - -/** - * Serializes the GRFIdentifier (GRF ID and MD5 checksum) to the packet - * @param p the packet to write the data to - * @param grf the GRFIdentifier to serialize - */ -void NetworkSocketHandler::SendGRFIdentifier(Packet *p, const GRFIdentifier *grf) -{ - uint j; - p->Send_uint32(grf->grfid); - for (j = 0; j < sizeof(grf->md5sum); j++) { - p->Send_uint8 (grf->md5sum[j]); - } -} - -/** - * Deserializes the GRFIdentifier (GRF ID and MD5 checksum) from the packet - * @param p the packet to read the data from - * @param grf the GRFIdentifier to deserialize - */ -void NetworkSocketHandler::ReceiveGRFIdentifier(Packet *p, GRFIdentifier *grf) -{ - uint j; - grf->grfid = p->Recv_uint32(); - for (j = 0; j < sizeof(grf->md5sum); j++) { - grf->md5sum[j] = p->Recv_uint8(); - } -} diff --git a/src/network/core/core.h b/src/network/core/core.h index bf83adc72c..aac36080ff 100644 --- a/src/network/core/core.h +++ b/src/network/core/core.h @@ -71,8 +71,6 @@ public: */ void Reopen() { this->has_quit = false; } - void SendGRFIdentifier(Packet *p, const GRFIdentifier *grf); - void ReceiveGRFIdentifier(Packet *p, GRFIdentifier *grf); void SendCompanyInformation(Packet *p, const struct Company *c, const struct NetworkCompanyStats *stats, uint max_len = NETWORK_COMPANY_NAME_LENGTH); }; diff --git a/src/network/core/game.h b/src/network/core/game.h deleted file mode 100644 index 13f17bbaea..0000000000 --- a/src/network/core/game.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** - * @file game.h Information about a game that is sent between a - * game server, game client and masterserver. - */ - -#ifndef NETWORK_CORE_GAME_H -#define NETWORK_CORE_GAME_H - -#include "config.h" -#include "../../newgrf_config.h" -#include "../../date_type.h" - -/** - * The game information that is not generated on-the-fly and has to - * be sent to the clients. - */ -struct NetworkServerGameInfo { - byte clients_on; ///< Current count of clients on server -}; - -/** - * The game information that is sent from the server to the clients. - */ -struct NetworkGameInfo : NetworkServerGameInfo { - GRFConfig *grfconfig; ///< List of NewGRF files used - Date start_date; ///< When the game started - Date game_date; ///< Current date - uint32 map_width; ///< Map width - uint32 map_height; ///< Map height - char server_name[NETWORK_NAME_LENGTH]; ///< Server name - char hostname[NETWORK_HOSTNAME_LENGTH]; ///< Hostname of the server (if any) - char short_server_revision[NETWORK_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0) (truncated) - char server_revision[NETWORK_LONG_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0) - bool dedicated; ///< Is this a dedicated server? - bool version_compatible; ///< Can we connect to this server or not? (based on server_revision) - bool compatible; ///< Can we connect to this server or not? (based on server_revision _and_ grf_match - bool use_password; ///< Is this server passworded? - byte game_info_version; ///< Version of the game info - byte clients_max; ///< Max clients allowed on server - byte companies_on; ///< How many started companies do we have - byte companies_max; ///< Max companies allowed on server - byte spectators_on; ///< How many spectators do we have? - byte spectators_max; ///< Max spectators allowed on server - byte map_set; ///< Graphical set -}; - -#endif /* NETWORK_CORE_GAME_H */ diff --git a/src/network/core/game_info.cpp b/src/network/core/game_info.cpp new file mode 100644 index 0000000000..c80a7573e7 --- /dev/null +++ b/src/network/core/game_info.cpp @@ -0,0 +1,457 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** + * @file game_info.cpp Functions to convert NetworkGameInfo to Packet and back. + */ + +#include "../../stdafx.h" +#include "game_info.h" +#include "../../core/bitmath_func.hpp" +#include "../../company_base.h" +#include "../../date_func.h" +#include "../../debug.h" +#include "../../map_func.h" +#include "../../settings_type.h" +#include "../../string_func.h" +#include "../../rev.h" +#include "../network_func.h" +#include "../network.h" +#include "packet.h" + +#include "../../safeguards.h" + +extern const uint8 _out_of_band_grf_md5[16]; + +/** + * How many hex digits of the git hash to include in network revision string. + * Determined as 10 hex digits + 2 characters for -g/-u/-m prefix. + */ +static const uint GITHASH_SUFFIX_LEN = 12; + +NetworkServerGameInfo _network_game_info; ///< Information about our game. + +/** + * Get the network version string used by this build. + * The returned string is guaranteed to be at most NETWORK_REVISON_LENGTH bytes. + */ +const char *GetNetworkRevisionString() +{ + /* This will be allocated on heap and never free'd, but only once so not a "real" leak. */ + static char *network_revision = nullptr; + + if (!network_revision) { + /* Start by taking a chance on the full revision string. */ + network_revision = stredup(_openttd_revision); + /* Ensure it's not longer than the packet buffer length. */ + if (strlen(network_revision) >= NETWORK_REVISION_LENGTH) network_revision[NETWORK_REVISION_LENGTH - 1] = '\0'; + + /* Tag names are not mangled further. */ + if (_openttd_revision_tagged) { + DEBUG(net, 1, "Network revision name is '%s'", network_revision); + return network_revision; + } + + /* Prepare a prefix of the git hash. + * Size is length + 1 for terminator, +2 for -g prefix. */ + assert(_openttd_revision_modified < 3); + char githash_suffix[GITHASH_SUFFIX_LEN + 1] = "-"; + githash_suffix[1] = "gum"[_openttd_revision_modified]; + for (uint i = 2; i < GITHASH_SUFFIX_LEN; i++) { + githash_suffix[i] = _openttd_revision_hash[i-2]; + } + + /* Where did the hash start in the original string? + * Overwrite from that position, unless that would go past end of packet buffer length. */ + ptrdiff_t hashofs = strrchr(_openttd_revision, '-') - _openttd_revision; + if (hashofs + strlen(githash_suffix) + 1 > NETWORK_REVISION_LENGTH) hashofs = strlen(network_revision) - strlen(githash_suffix); + /* Replace the git hash in revision string. */ + strecpy(network_revision + hashofs, githash_suffix, network_revision + NETWORK_REVISION_LENGTH); + assert(strlen(network_revision) < NETWORK_REVISION_LENGTH); // strlen does not include terminator, constant does, hence strictly less than + DEBUG(net, 1, "Network revision name is '%s'", network_revision); + } + + return network_revision; +} + +/** + * Extract the git hash from the revision string. + * @param revstr The revision string (formatted as DATE-BRANCH-GITHASH). + * @return The git has part of the revision. + */ +static const char *ExtractNetworkRevisionHash(const char *revstr) +{ + return strrchr(revstr, '-'); +} + +/** + * Checks whether the given version string is compatible with our version. + * First tries to match the full string, if that fails, attempts to compare just git hashes. + * @param other the version string to compare to + */ +bool IsNetworkCompatibleVersion(const char *other, bool extended) +{ + if (strncmp(GetNetworkRevisionString(), other, (extended ? NETWORK_LONG_REVISION_LENGTH : NETWORK_REVISION_LENGTH) - 1) == 0) return true; + + /* If this version is tagged, then the revision string must be a complete match, + * since there is no git hash suffix in it. + * This is needed to avoid situations like "1.9.0-beta1" comparing equal to "2.0.0-beta1". */ + if (_openttd_revision_tagged) return false; + + const char *hash1 = ExtractNetworkRevisionHash(GetNetworkRevisionString()); + const char *hash2 = ExtractNetworkRevisionHash(other); + return hash1 != nullptr && hash2 != nullptr && strncmp(hash1, hash2, GITHASH_SUFFIX_LEN) == 0; +} + +/** + * Check if an game entry is compatible with our client. + */ +void CheckGameCompatibility(NetworkGameInfo &ngi) +{ + /* Check if we are allowed on this server based on the revision-check. */ + ngi.version_compatible = IsNetworkCompatibleVersion(ngi.server_revision); + ngi.compatible = ngi.version_compatible; + + /* Check if we have all the GRFs on the client-system too. */ + for (const GRFConfig *c = ngi.grfconfig; c != nullptr; c = c->next) { + if (c->status == GCS_NOT_FOUND) ngi.compatible = false; + } +} + +/** + * Fill a NetworkGameInfo structure with the latest information of the server. + * @param ngi the NetworkGameInfo struct to fill with data. + */ +void FillNetworkGameInfo(NetworkGameInfo &ngi) +{ + /* Update some game_info */ + ngi.clients_on = _network_game_info.clients_on; + ngi.start_date = ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1); + + ngi.use_password = !StrEmpty(_settings_client.network.server_password); + ngi.clients_max = _settings_client.network.max_clients; + ngi.companies_on = (byte)Company::GetNumItems(); + ngi.companies_max = _settings_client.network.max_companies; + ngi.spectators_on = NetworkSpectatorCount(); + ngi.spectators_max = _settings_client.network.max_spectators; + ngi.game_date = _date; + ngi.map_width = MapSizeX(); + ngi.map_height = MapSizeY(); + ngi.map_set = _settings_game.game_creation.landscape; + ngi.dedicated = _network_dedicated; + ngi.grfconfig = _grfconfig; + + strecpy(ngi.server_name, _settings_client.network.server_name, lastof(ngi.server_name)); + strecpy(ngi.server_revision, GetNetworkRevisionString(), lastof(ngi.server_revision)); + strecpy(ngi.short_server_revision, _openttd_revision, lastof(ngi.short_server_revision)); +} + +/** + * Function that is called for every GRFConfig that is read when receiving + * a NetworkGameInfo. Only grfid and md5sum are set, the rest is zero. This + * function must set all appropriate fields. This GRF is later appended to + * the grfconfig list of the NetworkGameInfo. + * @param config the GRF to handle. + */ +static void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config) +{ + /* Find the matching GRF file */ + const GRFConfig *f = FindGRFConfig(config->ident.grfid, FGCM_EXACT, config->ident.md5sum); + if (f == nullptr) { + /* Don't know the GRF, so mark game incompatible and the (possibly) + * already resolved name for this GRF (another server has sent the + * name of the GRF already */ + config->name = FindUnknownGRFName(config->ident.grfid, config->ident.md5sum, true); + config->status = GCS_NOT_FOUND; + } else { + config->filename = f->filename; + config->name = f->name; + config->info = f->info; + config->url = f->url; + } + SetBit(config->flags, GCF_COPY); +} + +/** + * Serializes the NetworkGameInfo struct to the packet. + * @param p the packet to write the data to. + * @param info the NetworkGameInfo struct to serialize from. + */ +void SerializeNetworkGameInfo(Packet *p, const NetworkGameInfo *info) +{ + p->Send_uint8 (NETWORK_GAME_INFO_VERSION); + + /* + * Please observe the order. + * The parts must be read in the same order as they are sent! + */ + + /* Update the documentation in game_info.h on changes + * to the NetworkGameInfo wire-protocol! */ + + /* NETWORK_GAME_INFO_VERSION = 4 */ + { + /* Only send the GRF Identification (GRF_ID and MD5 checksum) of + * the GRFs that are needed, i.e. the ones that the server has + * selected in the NewGRF GUI and not the ones that are used due + * to the fact that they are in [newgrf-static] in openttd.cfg */ + const GRFConfig *c; + uint count = 0; + + /* Count number of GRFs to send information about */ + for (c = info->grfconfig; c != nullptr; c = c->next) { + if (!HasBit(c->flags, GCF_STATIC)) count++; + } + p->Send_uint8(std::min(count, NETWORK_MAX_GRF_COUNT)); // Send number of GRFs + + /* Send actual GRF Identifications */ + uint index = 0; + for (c = info->grfconfig; c != nullptr; c = c->next) { + if (!HasBit(c->flags, GCF_STATIC)) { + if (index == NETWORK_MAX_GRF_COUNT - 1 && count > NETWORK_MAX_GRF_COUNT) { + /* Send fake GRF ID */ + + p->Send_uint32(0x56D2B000); + p->Send_binary((const char*) _out_of_band_grf_md5, 16); + } else if (index >= NETWORK_MAX_GRF_COUNT) { + break; + } else { + SerializeGRFIdentifier(p, &c->ident); + } + index++; + } + } + } + + /* NETWORK_GAME_INFO_VERSION = 3 */ + p->Send_uint32(info->game_date); + p->Send_uint32(info->start_date); + + /* NETWORK_GAME_INFO_VERSION = 2 */ + p->Send_uint8 (info->companies_max); + p->Send_uint8 (info->companies_on); + p->Send_uint8 (info->spectators_max); + + /* NETWORK_GAME_INFO_VERSION = 1 */ + p->Send_string(info->server_name); + p->Send_string(info->server_revision); + p->Send_uint8 (0); // Used to be server-lang. + p->Send_bool (info->use_password); + p->Send_uint8 (info->clients_max); + p->Send_uint8 (info->clients_on); + p->Send_uint8 (info->spectators_on); + p->Send_string(""); // Used to be map-name. + p->Send_uint16(info->map_width); + p->Send_uint16(info->map_height); + p->Send_uint8 (info->map_set); + p->Send_bool (info->dedicated); +} + +/** + * Serializes the NetworkGameInfo struct to the packet + * @param p the packet to write the data to + * @param info the NetworkGameInfo struct to serialize + */ +void SerializeNetworkGameInfoExtended(Packet *p, const NetworkGameInfo *info, uint16 flags, uint16 version) +{ + p->Send_uint8(0); // version num + + p->Send_uint32(info->game_date); + p->Send_uint32(info->start_date); + p->Send_uint8 (info->companies_max); + p->Send_uint8 (info->companies_on); + p->Send_uint8 (info->spectators_max); + p->Send_string(info->server_name); + p->Send_string(info->server_revision); + p->Send_uint8 (0); // Used to be server-lang. + p->Send_bool (info->use_password); + p->Send_uint8 (info->clients_max); + p->Send_uint8 (info->clients_on); + p->Send_uint8 (info->spectators_on); + p->Send_string(""); // Used to be map-name. + p->Send_uint32(info->map_width); + p->Send_uint32(info->map_height); + p->Send_uint8 (info->map_set); + p->Send_bool (info->dedicated); + + { + /* Only send the GRF Identification (GRF_ID and MD5 checksum) of + * the GRFs that are needed, i.e. the ones that the server has + * selected in the NewGRF GUI and not the ones that are used due + * to the fact that they are in [newgrf-static] in openttd.cfg */ + const GRFConfig *c; + uint count = 0; + + /* Count number of GRFs to send information about */ + for (c = info->grfconfig; c != nullptr; c = c->next) { + if (!HasBit(c->flags, GCF_STATIC)) count++; + } + p->Send_uint32(count); // Send number of GRFs + + /* Send actual GRF Identifications */ + for (c = info->grfconfig; c != nullptr; c = c->next) { + if (!HasBit(c->flags, GCF_STATIC)) { + SerializeGRFIdentifier(p, &c->ident); + } + } + } +} + +/** + * Deserializes the NetworkGameInfo struct from the packet. + * @param p the packet to read the data from. + * @param info the NetworkGameInfo to deserialize into. + */ +void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info) +{ + static const Date MAX_DATE = ConvertYMDToDate(MAX_YEAR, 11, 31); // December is month 11 + + info->game_info_version = p->Recv_uint8(); + + /* + * Please observe the order. + * The parts must be read in the same order as they are sent! + */ + + /* Update the documentation in game_info.h on changes + * to the NetworkGameInfo wire-protocol! */ + + switch (info->game_info_version) { + case 4: { + GRFConfig **dst = &info->grfconfig; + uint i; + uint num_grfs = p->Recv_uint8(); + + /* Broken/bad data. It cannot have that many NewGRFs. */ + if (num_grfs > NETWORK_MAX_GRF_COUNT) return; + + for (i = 0; i < num_grfs; i++) { + GRFConfig *c = new GRFConfig(); + DeserializeGRFIdentifier(p, &c->ident); + HandleIncomingNetworkGameInfoGRFConfig(c); + + /* Append GRFConfig to the list */ + *dst = c; + dst = &c->next; + } + FALLTHROUGH; + } + + case 3: + info->game_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); + info->start_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); + FALLTHROUGH; + + case 2: + info->companies_max = p->Recv_uint8 (); + info->companies_on = p->Recv_uint8 (); + info->spectators_max = p->Recv_uint8 (); + FALLTHROUGH; + + case 1: + p->Recv_string(info->server_name, sizeof(info->server_name)); + p->Recv_string(info->server_revision, sizeof(info->server_revision)); + p->Recv_uint8 (); // Used to contain server-lang. + info->use_password = p->Recv_bool (); + info->clients_max = p->Recv_uint8 (); + info->clients_on = p->Recv_uint8 (); + info->spectators_on = p->Recv_uint8 (); + if (info->game_info_version < 3) { // 16 bits dates got scrapped and are read earlier + info->game_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR; + info->start_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR; + } + while (p->Recv_uint8() != 0) {} // Used to contain the map-name. + info->map_width = p->Recv_uint16(); + info->map_height = p->Recv_uint16(); + info->map_set = p->Recv_uint8 (); + info->dedicated = p->Recv_bool (); + + if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0; + } +} + +/** + * Deserializes the NetworkGameInfo struct from the packet + * @param p the packet to read the data from + * @param info the NetworkGameInfo to deserialize into + */ +void DeserializeNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info) +{ + static const Date MAX_DATE = ConvertYMDToDate(MAX_YEAR, 11, 31); // December is month 11 + + const uint8 version = p->Recv_uint8(); + if (version > 0) return; // Unknown version + + info->game_info_version = 255; + + info->game_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); + info->start_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); + info->companies_max = p->Recv_uint8 (); + info->companies_on = p->Recv_uint8 (); + info->spectators_max = p->Recv_uint8 (); + p->Recv_string(info->server_name, sizeof(info->server_name)); + p->Recv_string(info->server_revision, sizeof(info->server_revision)); + p->Recv_uint8 (); // Used to contain server-lang. + info->use_password = p->Recv_bool (); + info->clients_max = p->Recv_uint8 (); + info->clients_on = p->Recv_uint8 (); + info->spectators_on = p->Recv_uint8 (); + while (p->Recv_uint8() != 0) {} // Used to contain the map-name. + info->map_width = p->Recv_uint32(); + info->map_height = p->Recv_uint32(); + info->map_set = p->Recv_uint8 (); + info->dedicated = p->Recv_bool (); + + { + GRFConfig **dst = &info->grfconfig; + uint i; + uint num_grfs = p->Recv_uint32(); + + /* Broken/bad data. It cannot have that many NewGRFs. */ + if (num_grfs > MAX_NON_STATIC_GRF_COUNT) return; + + for (i = 0; i < num_grfs; i++) { + GRFConfig *c = new GRFConfig(); + DeserializeGRFIdentifier(p, &c->ident); + HandleIncomingNetworkGameInfoGRFConfig(c); + + /* Append GRFConfig to the list */ + *dst = c; + dst = &c->next; + } + } + + if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0; +} + +/** + * Serializes the GRFIdentifier (GRF ID and MD5 checksum) to the packet + * @param p the packet to write the data to. + * @param grf the GRFIdentifier to serialize. + */ +void SerializeGRFIdentifier(Packet *p, const GRFIdentifier *grf) +{ + uint j; + p->Send_uint32(grf->grfid); + for (j = 0; j < sizeof(grf->md5sum); j++) { + p->Send_uint8(grf->md5sum[j]); + } +} + +/** + * Deserializes the GRFIdentifier (GRF ID and MD5 checksum) from the packet + * @param p the packet to read the data from. + * @param grf the GRFIdentifier to deserialize. + */ +void DeserializeGRFIdentifier(Packet *p, GRFIdentifier *grf) +{ + uint j; + grf->grfid = p->Recv_uint32(); + for (j = 0; j < sizeof(grf->md5sum); j++) { + grf->md5sum[j] = p->Recv_uint8(); + } +} diff --git a/src/network/core/game_info.h b/src/network/core/game_info.h new file mode 100644 index 0000000000..ce889b7c42 --- /dev/null +++ b/src/network/core/game_info.h @@ -0,0 +1,109 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** + * @file game_info.h Convert NetworkGameInfo to Packet and back. + */ + +#ifndef NETWORK_CORE_GAME_INFO_H +#define NETWORK_CORE_GAME_INFO_H + +#include "config.h" +#include "core.h" +#include "../../newgrf_config.h" +#include "../../date_type.h" + +/* + * NetworkGameInfo has several revisions which we still need to support on the + * wire. The table below shows the version and size for each field of the + * serialized NetworkGameInfo. + * + * Version: Bytes: Description: + * all 1 the version of this packet's structure + * + * 4+ 1 number of GRFs attached (n) + * 4+ n * 20 unique identifier for GRF files. Consists of: + * - one 4 byte variable with the GRF ID + * - 16 bytes (sent sequentially) for the MD5 checksum + * of the GRF + * + * 3+ 4 current game date in days since 1-1-0 (DMY) + * 3+ 4 game introduction date in days since 1-1-0 (DMY) + * + * 2+ 1 maximum number of companies allowed on the server + * 2+ 1 number of companies on the server + * 2+ 1 maximum number of spectators allowed on the server + * + * 1+ var string with the name of the server + * 1+ var string with the revision of the server + * 1+ 1 the language run on the server + * (0 = any, 1 = English, 2 = German, 3 = French) + * 1+ 1 whether the server uses a password (0 = no, 1 = yes) + * 1+ 1 maximum number of clients allowed on the server + * 1+ 1 number of clients on the server + * 1+ 1 number of spectators on the server + * 1 & 2 2 current game date in days since 1-1-1920 (DMY) + * 1 & 2 2 game introduction date in days since 1-1-1920 (DMY) + * 1+ var string with the name of the map + * 1+ 2 width of the map in tiles + * 1+ 2 height of the map in tiles + * 1+ 1 type of map: + * (0 = temperate, 1 = arctic, 2 = desert, 3 = toyland) + * 1+ 1 whether the server is dedicated (0 = no, 1 = yes) + */ + +/** + * The game information that is not generated on-the-fly and has to + * be sent to the clients. + */ +struct NetworkServerGameInfo { + byte clients_on; ///< Current count of clients on server +}; + +/** + * The game information that is sent from the server to the clients. + */ +struct NetworkGameInfo : NetworkServerGameInfo { + GRFConfig *grfconfig; ///< List of NewGRF files used + Date start_date; ///< When the game started + Date game_date; ///< Current date + uint32 map_width; ///< Map width + uint32 map_height; ///< Map height + char server_name[NETWORK_NAME_LENGTH]; ///< Server name + char hostname[NETWORK_HOSTNAME_LENGTH]; ///< Hostname of the server (if any) + char short_server_revision[NETWORK_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0) (truncated) + char server_revision[NETWORK_LONG_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0) + bool dedicated; ///< Is this a dedicated server? + bool version_compatible; ///< Can we connect to this server or not? (based on server_revision) + bool compatible; ///< Can we connect to this server or not? (based on server_revision _and_ grf_match + bool use_password; ///< Is this server passworded? + byte game_info_version; ///< Version of the game info + byte clients_max; ///< Max clients allowed on server + byte companies_on; ///< How many started companies do we have + byte companies_max; ///< Max companies allowed on server + byte spectators_on; ///< How many spectators do we have? + byte spectators_max; ///< Max spectators allowed on server + byte map_set; ///< Graphical set +}; + +extern NetworkServerGameInfo _network_game_info; + +const char *GetNetworkRevisionString(); +bool IsNetworkCompatibleVersion(const char *other, bool extended = false); +void CheckGameCompatibility(NetworkGameInfo &ngi); + +void FillNetworkGameInfo(NetworkGameInfo &ngi); + +void DeserializeGRFIdentifier(Packet *p, GRFIdentifier *grf); +void SerializeGRFIdentifier(Packet *p, const GRFIdentifier *grf); + +void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info); +void DeserializeNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info); +void SerializeNetworkGameInfo(Packet *p, const NetworkGameInfo *info); +void SerializeNetworkGameInfoExtended(Packet *p, const NetworkGameInfo *info, uint16 flags, uint16 version); + +#endif /* NETWORK_CORE_GAME_INFO_H */ diff --git a/src/network/core/tcp_game.cpp b/src/network/core/tcp_game.cpp index 9acb166885..3540bc0f61 100644 --- a/src/network/core/tcp_game.cpp +++ b/src/network/core/tcp_game.cpp @@ -27,6 +27,8 @@ static const char* _packet_game_type_names[] { "SERVER_ERROR", "CLIENT_COMPANY_INFO", "SERVER_COMPANY_INFO", + "CLIENT_GAME_INFO", + "SERVER_GAME_INFO", "SERVER_CHECK_NEWGRFS", "CLIENT_NEWGRFS_CHECKED", "SERVER_NEED_GAME_PASSWORD", @@ -135,6 +137,8 @@ NetworkRecvStatus NetworkGameSocketHandler::HandlePacket(Packet *p) case PACKET_SERVER_BANNED: return this->Receive_SERVER_BANNED(p); case PACKET_CLIENT_JOIN: return this->Receive_CLIENT_JOIN(p); case PACKET_SERVER_ERROR: return this->Receive_SERVER_ERROR(p); + case PACKET_CLIENT_GAME_INFO: return this->Receive_CLIENT_GAME_INFO(p); + case PACKET_SERVER_GAME_INFO: return this->Receive_SERVER_GAME_INFO(p); case PACKET_CLIENT_COMPANY_INFO: return this->Receive_CLIENT_COMPANY_INFO(p); case PACKET_SERVER_COMPANY_INFO: return this->Receive_SERVER_COMPANY_INFO(p); case PACKET_SERVER_CLIENT_INFO: return this->Receive_SERVER_CLIENT_INFO(p); @@ -225,6 +229,8 @@ NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_FULL(Packet *p) { ret NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_BANNED(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_BANNED); } NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_JOIN); } NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_ERROR); } +NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_GAME_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_GAME_INFO); } +NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_GAME_INFO); } NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_COMPANY_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_COMPANY_INFO); } NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_COMPANY_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_COMPANY_INFO); } NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_CLIENT_INFO); } diff --git a/src/network/core/tcp_game.h b/src/network/core/tcp_game.h index c14a9efdbe..e0f1fc65d1 100644 --- a/src/network/core/tcp_game.h +++ b/src/network/core/tcp_game.h @@ -25,7 +25,7 @@ */ enum PacketGameType { /* - * These first three pair of packets (thus six in + * These first four pair of packets (thus eight in * total) must remain in this order for backward * and forward compatibility between clients that * are trying to join directly. @@ -43,6 +43,10 @@ enum PacketGameType { PACKET_CLIENT_COMPANY_INFO, ///< Request information about all companies. PACKET_SERVER_COMPANY_INFO, ///< Information about a single company. + /* Packets used to get the game info. */ + PACKET_CLIENT_GAME_INFO, ///< Request information about the server. + PACKET_SERVER_GAME_INFO, ///< Information about the server. + /* * Packets after here assume that the client * and server are running the same version. As @@ -195,6 +199,19 @@ protected: */ virtual NetworkRecvStatus Receive_SERVER_ERROR(Packet *p); + /** + * Request game information. + * @param p The packet that was just received. + */ + virtual NetworkRecvStatus Receive_CLIENT_GAME_INFO(Packet *p); + + /** + * Sends information about the game. + * Serialized NetworkGameInfo. See game_info.h for details. + * @param p The packet that was just received. + */ + virtual NetworkRecvStatus Receive_SERVER_GAME_INFO(Packet *p); + /** * Request company information (in detail). * @param p The packet that was just received. diff --git a/src/network/core/tcp_http.cpp b/src/network/core/tcp_http.cpp index 17eb3a720b..39283ce407 100644 --- a/src/network/core/tcp_http.cpp +++ b/src/network/core/tcp_http.cpp @@ -13,6 +13,7 @@ #include "../../debug.h" #include "../../rev.h" #include "../network_func.h" +#include "game_info.h" #include "tcp_http.h" diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index 198cfc1279..646e28a654 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -13,13 +13,10 @@ #include "../../date_func.h" #include "../../debug.h" #include "../../core/random_func.hpp" -#include "../../fios.h" #include "udp.h" #include "../../safeguards.h" -extern const uint8 _out_of_band_grf_md5[16]; - /** * Create an UDP socket but don't listen yet. * @param bind the addresses to bind to. @@ -186,258 +183,6 @@ void NetworkUDPSocketHandler::ReceivePackets() } } - -/** - * Serializes the NetworkGameInfo struct to the packet - * @param p the packet to write the data to - * @param info the NetworkGameInfo struct to serialize - */ -void NetworkUDPSocketHandler::SendNetworkGameInfo(Packet *p, const NetworkGameInfo *info) -{ - p->Send_uint8 (NETWORK_GAME_INFO_VERSION); - - /* - * Please observe the order. - * The parts must be read in the same order as they are sent! - */ - - /* Update the documentation in udp.h on changes - * to the NetworkGameInfo wire-protocol! */ - - /* NETWORK_GAME_INFO_VERSION = 4 */ - { - /* Only send the GRF Identification (GRF_ID and MD5 checksum) of - * the GRFs that are needed, i.e. the ones that the server has - * selected in the NewGRF GUI and not the ones that are used due - * to the fact that they are in [newgrf-static] in openttd.cfg */ - const GRFConfig *c; - uint count = 0; - - /* Count number of GRFs to send information about */ - for (c = info->grfconfig; c != nullptr; c = c->next) { - if (!HasBit(c->flags, GCF_STATIC)) count++; - } - p->Send_uint8(std::min(count, NETWORK_MAX_GRF_COUNT)); // Send number of GRFs - - /* Send actual GRF Identifications */ - uint index = 0; - for (c = info->grfconfig; c != nullptr; c = c->next) { - if (!HasBit(c->flags, GCF_STATIC)) { - if (index == NETWORK_MAX_GRF_COUNT - 1 && count > NETWORK_MAX_GRF_COUNT) { - /* Send fake GRF ID */ - - p->Send_uint32(0x56D2B000); - p->Send_binary((const char*) _out_of_band_grf_md5, 16); - } else if (index >= NETWORK_MAX_GRF_COUNT) { - break; - } else { - this->SendGRFIdentifier(p, &c->ident); - } - index++; - } - } - } - - /* NETWORK_GAME_INFO_VERSION = 3 */ - p->Send_uint32(info->game_date); - p->Send_uint32(info->start_date); - - /* NETWORK_GAME_INFO_VERSION = 2 */ - p->Send_uint8 (info->companies_max); - p->Send_uint8 (info->companies_on); - p->Send_uint8 (info->spectators_max); - - /* NETWORK_GAME_INFO_VERSION = 1 */ - p->Send_string(info->server_name); - p->Send_string(info->short_server_revision); - p->Send_uint8 (0); // Used to be server-lang. - p->Send_bool (info->use_password); - p->Send_uint8 (info->clients_max); - p->Send_uint8 (info->clients_on); - p->Send_uint8 (info->spectators_on); - p->Send_string(""); // Used to be map-name. - p->Send_uint16(info->map_width); - p->Send_uint16(info->map_height); - p->Send_uint8 (info->map_set); - p->Send_bool (info->dedicated); -} - -/** - * Serializes the NetworkGameInfo struct to the packet - * @param p the packet to write the data to - * @param info the NetworkGameInfo struct to serialize - */ -void NetworkUDPSocketHandler::SendNetworkGameInfoExtended(Packet *p, const NetworkGameInfo *info, uint16 flags, uint16 version) -{ - p->Send_uint8(0); // version num - - p->Send_uint32(info->game_date); - p->Send_uint32(info->start_date); - p->Send_uint8 (info->companies_max); - p->Send_uint8 (info->companies_on); - p->Send_uint8 (info->spectators_max); - p->Send_string(info->server_name); - p->Send_string(info->server_revision); - p->Send_uint8 (0); // Used to be server-lang. - p->Send_bool (info->use_password); - p->Send_uint8 (info->clients_max); - p->Send_uint8 (info->clients_on); - p->Send_uint8 (info->spectators_on); - p->Send_string(""); // Used to be map-name. - p->Send_uint32(info->map_width); - p->Send_uint32(info->map_height); - p->Send_uint8 (info->map_set); - p->Send_bool (info->dedicated); - - { - /* Only send the GRF Identification (GRF_ID and MD5 checksum) of - * the GRFs that are needed, i.e. the ones that the server has - * selected in the NewGRF GUI and not the ones that are used due - * to the fact that they are in [newgrf-static] in openttd.cfg */ - const GRFConfig *c; - uint count = 0; - - /* Count number of GRFs to send information about */ - for (c = info->grfconfig; c != nullptr; c = c->next) { - if (!HasBit(c->flags, GCF_STATIC)) count++; - } - p->Send_uint32(count); // Send number of GRFs - - /* Send actual GRF Identifications */ - for (c = info->grfconfig; c != nullptr; c = c->next) { - if (!HasBit(c->flags, GCF_STATIC)) { - this->SendGRFIdentifier(p, &c->ident); - } - } - } -} - -/** - * Deserializes the NetworkGameInfo struct from the packet - * @param p the packet to read the data from - * @param info the NetworkGameInfo to deserialize into - */ -void NetworkUDPSocketHandler::ReceiveNetworkGameInfo(Packet *p, NetworkGameInfo *info) -{ - static const Date MAX_DATE = ConvertYMDToDate(MAX_YEAR, 11, 31); // December is month 11 - - info->game_info_version = p->Recv_uint8(); - - /* - * Please observe the order. - * The parts must be read in the same order as they are sent! - */ - - /* Update the documentation in udp.h on changes - * to the NetworkGameInfo wire-protocol! */ - - switch (info->game_info_version) { - case 4: { - GRFConfig **dst = &info->grfconfig; - uint i; - uint num_grfs = p->Recv_uint8(); - - /* Broken/bad data. It cannot have that many NewGRFs. */ - if (num_grfs > NETWORK_MAX_GRF_COUNT) return; - - for (i = 0; i < num_grfs; i++) { - GRFConfig *c = new GRFConfig(); - this->ReceiveGRFIdentifier(p, &c->ident); - this->HandleIncomingNetworkGameInfoGRFConfig(c); - - /* Append GRFConfig to the list */ - *dst = c; - dst = &c->next; - } - FALLTHROUGH; - } - - case 3: - info->game_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); - info->start_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); - FALLTHROUGH; - - case 2: - info->companies_max = p->Recv_uint8 (); - info->companies_on = p->Recv_uint8 (); - info->spectators_max = p->Recv_uint8 (); - FALLTHROUGH; - - case 1: - p->Recv_string(info->server_name, sizeof(info->server_name)); - p->Recv_string(info->server_revision, sizeof(info->server_revision)); - p->Recv_uint8 (); // Used to contain server-lang. - info->use_password = p->Recv_bool (); - info->clients_max = p->Recv_uint8 (); - info->clients_on = p->Recv_uint8 (); - info->spectators_on = p->Recv_uint8 (); - if (info->game_info_version < 3) { // 16 bits dates got scrapped and are read earlier - info->game_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR; - info->start_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR; - } - while (p->Recv_uint8() != 0) {} // Used to contain the map-name. - info->map_width = p->Recv_uint16(); - info->map_height = p->Recv_uint16(); - info->map_set = p->Recv_uint8 (); - info->dedicated = p->Recv_bool (); - - if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0; - } -} -/** - * Deserializes the NetworkGameInfo struct from the packet - * @param p the packet to read the data from - * @param info the NetworkGameInfo to deserialize into - */ -void NetworkUDPSocketHandler::ReceiveNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info) -{ - static const Date MAX_DATE = ConvertYMDToDate(MAX_YEAR, 11, 31); // December is month 11 - - const uint8 version = p->Recv_uint8(); - if (version > 0) return; // Unknown version - - info->game_info_version = 255; - - info->game_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); - info->start_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); - info->companies_max = p->Recv_uint8 (); - info->companies_on = p->Recv_uint8 (); - info->spectators_max = p->Recv_uint8 (); - p->Recv_string(info->server_name, sizeof(info->server_name)); - p->Recv_string(info->server_revision, sizeof(info->server_revision)); - p->Recv_uint8 (); // Used to contain server-lang. - info->use_password = p->Recv_bool (); - info->clients_max = p->Recv_uint8 (); - info->clients_on = p->Recv_uint8 (); - info->spectators_on = p->Recv_uint8 (); - while (p->Recv_uint8() != 0) {} // Used to contain the map-name. - info->map_width = p->Recv_uint32(); - info->map_height = p->Recv_uint32(); - info->map_set = p->Recv_uint8 (); - info->dedicated = p->Recv_bool (); - - { - GRFConfig **dst = &info->grfconfig; - uint i; - uint num_grfs = p->Recv_uint32(); - - /* Broken/bad data. It cannot have that many NewGRFs. */ - if (num_grfs > MAX_NON_STATIC_GRF_COUNT) return; - - for (i = 0; i < num_grfs; i++) { - GRFConfig *c = new GRFConfig(); - this->ReceiveGRFIdentifier(p, &c->ident); - this->HandleIncomingNetworkGameInfoGRFConfig(c); - - /* Append GRFConfig to the list */ - *dst = c; - dst = &c->next; - } - } - - if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0; -} - /** * Handle an incoming packets by sending it to the correct function. * @param p the received packet diff --git a/src/network/core/udp.h b/src/network/core/udp.h index 5321b9067d..6fc3687c6e 100644 --- a/src/network/core/udp.h +++ b/src/network/core/udp.h @@ -13,7 +13,6 @@ #define NETWORK_CORE_UDP_H #include "address.h" -#include "game.h" #include "packet.h" #include @@ -80,40 +79,7 @@ protected: /** * Return of server information to the client. - * This packet has several legacy versions, so we list the version and size of each "field": - * - * Version: Bytes: Description: - * all 1 the version of this packet's structure - * - * 4+ 1 number of GRFs attached (n) - * 4+ n * 20 unique identifier for GRF files. Consists of: - * - one 4 byte variable with the GRF ID - * - 16 bytes (sent sequentially) for the MD5 checksum - * of the GRF - * - * 3+ 4 current game date in days since 1-1-0 (DMY) - * 3+ 4 game introduction date in days since 1-1-0 (DMY) - * - * 2+ 1 maximum number of companies allowed on the server - * 2+ 1 number of companies on the server - * 2+ 1 maximum number of spectators allowed on the server - * - * 1+ var string with the name of the server - * 1+ var string with the revision of the server - * 1+ 1 the language run on the server - * (0 = any, 1 = English, 2 = German, 3 = French) - * 1+ 1 whether the server uses a password (0 = no, 1 = yes) - * 1+ 1 maximum number of clients allowed on the server - * 1+ 1 number of clients on the server - * 1+ 1 number of spectators on the server - * 1 & 2 2 current game date in days since 1-1-1920 (DMY) - * 1 & 2 2 game introduction date in days since 1-1-1920 (DMY) - * 1+ var string with the name of the map - * 1+ 2 width of the map in tiles - * 1+ 2 height of the map in tiles - * 1+ 1 type of map: - * (0 = temperate, 1 = arctic, 2 = desert, 3 = toyland) - * 1+ 1 whether the server is dedicated (0 = no, 1 = yes) + * Serialized NetworkGameInfo. See game_info.h for details. * @param p The received packet. * @param client_addr The origin of the packet. */ @@ -237,15 +203,6 @@ protected: void HandleUDPPacket(Packet *p, NetworkAddress *client_addr); - /** - * Function that is called for every GRFConfig that is read when receiving - * a NetworkGameInfo. Only grfid and md5sum are set, the rest is zero. This - * function must set all appropriate fields. This GRF is later appended to - * the grfconfig list of the NetworkGameInfo. - * @param config the GRF to handle - */ - virtual void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config) { NOT_REACHED(); } - virtual void Receive_EX_MULTI(Packet *p, NetworkAddress *client_addr); public: NetworkUDPSocketHandler(NetworkAddressList *bind = nullptr); @@ -258,11 +215,6 @@ public: void SendPacket(Packet *p, NetworkAddress *recv, bool all = false, bool broadcast = false, bool short_mtu = false); void ReceivePackets(); - - void SendNetworkGameInfo(Packet *p, const NetworkGameInfo *info); - void SendNetworkGameInfoExtended(Packet *p, const NetworkGameInfo *info, uint16 flags, uint16 version); - void ReceiveNetworkGameInfo(Packet *p, NetworkGameInfo *info); - void ReceiveNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info); }; #endif /* NETWORK_CORE_UDP_H */ diff --git a/src/network/network.cpp b/src/network/network.cpp index 8b484f79e7..be4aec9ce0 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -56,7 +56,6 @@ bool _network_available; ///< is network mode available? bool _network_dedicated; ///< are we a dedicated server? bool _is_network_server; ///< Does this client wants to be a network-server? bool _network_settings_access; ///< Can this client change server settings? -NetworkServerGameInfo _network_game_info; ///< Information about our game. NetworkCompanyState *_network_company_states = nullptr; ///< Statistics about some companies. ClientID _network_own_client_id; ///< Our client identifier. ClientID _redirect_console_to_client; ///< If not invalid, redirect the console output to a client. @@ -632,13 +631,14 @@ public: { _networking = true; new ClientNetworkGameSocketHandler(s); - MyClient::SendCompanyInformationQuery(); + MyClient::SendInformationQuery(); } }; -/* Query a server to fetch his game-info - * If game_info is true, only the gameinfo is fetched, - * else only the client_info is fetched */ +/** + * Query a server to fetch his game-info. + * @param address the address to query. + */ void NetworkTCPQueryServer(NetworkAddress address) { if (!_network_available) return; @@ -1177,15 +1177,6 @@ void NetworkShutDown() NetworkCoreShutdown(); } -/** - * Checks whether the given version string is compatible with our version. - * @param other the version string to compare to - */ -bool IsNetworkCompatibleVersion(const char *other, bool extended) -{ - return strncmp(_openttd_revision, other, (extended ? NETWORK_LONG_REVISION_LENGTH : NETWORK_REVISION_LENGTH) - 1) == 0; -} - #ifdef __EMSCRIPTEN__ extern "C" { diff --git a/src/network/network_admin.cpp b/src/network/network_admin.cpp index c547cd5d4a..5ff1c1cb6b 100644 --- a/src/network/network_admin.cpp +++ b/src/network/network_admin.cpp @@ -10,6 +10,7 @@ #include "../stdafx.h" #include "../strings_func.h" #include "../date_func.h" +#include "core/game_info.h" #include "network_admin.h" #include "network_base.h" #include "network_server.h" diff --git a/src/network/network_base.h b/src/network/network_base.h index 15f410dbc4..9ad0300629 100644 --- a/src/network/network_base.h +++ b/src/network/network_base.h @@ -14,6 +14,7 @@ #include "core/address.h" #include "../core/pool_type.hpp" #include "../company_type.h" +#include "../date_type.h" /** Type for the pool with client information. */ typedef Pool NetworkClientInfoPool; diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 1e76424559..2dd21fd10b 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -23,10 +23,11 @@ #include "../gfx_func.h" #include "../error.h" #include "../rev.h" -#include "../fios.h" +#include "core/game_info.h" #include "network.h" #include "network_base.h" #include "network_client.h" +#include "network_gamelist.h" #include "../core/backup_type.hpp" #include "../thread.h" #include "../crashlog.h" @@ -404,15 +405,18 @@ static_assert(NETWORK_SERVER_ID_LENGTH == 16 * 2 + 1); * DEF_CLIENT_SEND_COMMAND has no parameters ************/ -/** Query the server for company information. */ -NetworkRecvStatus ClientNetworkGameSocketHandler::SendCompanyInformationQuery() +/** + * Query the server for server information. + */ +NetworkRecvStatus ClientNetworkGameSocketHandler::SendInformationQuery() { my_client->status = STATUS_COMPANY_INFO; _network_join_status = NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO; SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN); - Packet *p = new Packet(PACKET_CLIENT_COMPANY_INFO, SHRT_MAX); - my_client->SendPacket(p); + my_client->SendPacket(new Packet(PACKET_CLIENT_GAME_INFO)); + my_client->SendPacket(new Packet(PACKET_CLIENT_COMPANY_INFO)); + return NETWORK_RECV_STATUS_OKAY; } @@ -686,6 +690,28 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_BANNED(Packet * return NETWORK_RECV_STATUS_SERVER_BANNED; } +NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packet *p) +{ + if (this->status != STATUS_COMPANY_INFO && this->status != STATUS_INACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET; + + NetworkGameList *item = GetLobbyGameInfo(); + + /* Clear any existing GRFConfig chain. */ + ClearGRFConfigList(&item->info.grfconfig); + /* Retrieve the NetworkGameInfo from the packet. */ + DeserializeNetworkGameInfo(p, &item->info); + /* Check for compatability with the client. */ + CheckGameCompatibility(item->info); + /* Ensure we consider the server online. */ + item->online = true; + + SetWindowDirty(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY); + + /* We will receive company info next, so keep connection open. */ + if (this->status == STATUS_COMPANY_INFO) return NETWORK_RECV_STATUS_OKAY; + return NETWORK_RECV_STATUS_CLOSE_QUERY; +} + NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_COMPANY_INFO(Packet *p) { if (this->status != STATUS_COMPANY_INFO) return NETWORK_RECV_STATUS_MALFORMED_PACKET; @@ -847,7 +873,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CHECK_NEWGRFS(P /* Check all GRFs */ for (; grf_count > 0; grf_count--) { GRFIdentifier c; - this->ReceiveGRFIdentifier(p, &c); + DeserializeGRFIdentifier(p, &c); /* Check whether we know this GRF */ const GRFConfig *f = FindGRFConfig(c.grfid, FGCM_EXACT, c.md5sum); diff --git a/src/network/network_client.h b/src/network/network_client.h index 224e521448..a8fd11c2a8 100644 --- a/src/network/network_client.h +++ b/src/network/network_client.h @@ -50,6 +50,7 @@ protected: NetworkRecvStatus Receive_SERVER_FULL(Packet *p) override; NetworkRecvStatus Receive_SERVER_BANNED(Packet *p) override; NetworkRecvStatus Receive_SERVER_ERROR(Packet *p) override; + NetworkRecvStatus Receive_SERVER_GAME_INFO(Packet *p) override; NetworkRecvStatus Receive_SERVER_COMPANY_INFO(Packet *p) override; NetworkRecvStatus Receive_SERVER_CLIENT_INFO(Packet *p) override; NetworkRecvStatus Receive_SERVER_NEED_GAME_PASSWORD(Packet *p) override; @@ -90,7 +91,7 @@ public: std::string GetDebugInfo() const override; - static NetworkRecvStatus SendCompanyInformationQuery(); + static NetworkRecvStatus SendInformationQuery(); static NetworkRecvStatus SendJoin(); static NetworkRecvStatus SendCommand(const CommandPacket *cp); diff --git a/src/network/network_func.h b/src/network/network_func.h index 90bf12b2c0..6e890cf289 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -22,8 +22,8 @@ #include "../gfx_type.h" #include "../openttd.h" #include "../company_type.h" +#include "../string_type.h" -extern NetworkServerGameInfo _network_game_info; extern NetworkCompanyState *_network_company_states; extern ClientID _network_own_client_id; diff --git a/src/network/network_gamelist.h b/src/network/network_gamelist.h index 8e61f3a93b..ce35c01d8b 100644 --- a/src/network/network_gamelist.h +++ b/src/network/network_gamelist.h @@ -11,6 +11,7 @@ #define NETWORK_GAMELIST_H #include "core/address.h" +#include "core/game_info.h" #include "network_type.h" /** Structure with information shown in the game list (GUI) */ diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 844326775b..fcb38929a9 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -1499,10 +1499,10 @@ struct NetworkLobbyWindow : public Window { break; case WID_NL_REFRESH: // Refresh - NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); // company info - NetworkUDPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); // general data /* Clear the information so removed companies don't remain */ for (auto &company : this->company_info) company = {}; + + NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); break; } } @@ -1570,8 +1570,7 @@ static void ShowNetworkLobbyWindow(NetworkGameList *ngl) DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_START); DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME); - NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); // company info - NetworkUDPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); // general data + NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); new NetworkLobbyWindow(&_network_lobby_window_desc, ngl); } @@ -1587,6 +1586,16 @@ NetworkCompanyInfo *GetLobbyCompanyInfo(CompanyID company) return (lobby != nullptr && company < MAX_COMPANIES) ? &lobby->company_info[company] : nullptr; } +/** + * Get the game information for the lobby. + * @return the game info struct to write the (downloaded) data to. + */ +NetworkGameList *GetLobbyGameInfo() +{ + NetworkLobbyWindow *lobby = dynamic_cast(FindWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY)); + return lobby != nullptr ? lobby->server : nullptr; +} + /* The window below gives information about the connected clients * and also makes able to kick them (if server) and stuff like that. */ diff --git a/src/network/network_gui.h b/src/network/network_gui.h index b9439599b5..249968abda 100644 --- a/src/network/network_gui.h +++ b/src/network/network_gui.h @@ -11,9 +11,11 @@ #define NETWORK_GUI_H #include "../company_type.h" +#include "../date_type.h" #include "../economy_type.h" #include "../window_type.h" #include "network_type.h" +#include "network_gamelist.h" void ShowNetworkNeedPassword(NetworkPasswordType npt); void ShowNetworkGiveMoneyWindow(CompanyID company); @@ -37,5 +39,6 @@ struct NetworkCompanyInfo : NetworkCompanyStats { }; NetworkCompanyInfo *GetLobbyCompanyInfo(CompanyID company); +NetworkGameList *GetLobbyGameInfo(); #endif /* NETWORK_GUI_H */ diff --git a/src/network/network_internal.h b/src/network/network_internal.h index 4c355685a1..e792ac514c 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -14,6 +14,7 @@ #include "core/tcp_game.h" #include "../command_type.h" +#include "../date_type.h" #ifdef RANDOM_DEBUG /** @@ -98,8 +99,6 @@ void NetworkAddServer(const char *b); void NetworkRebuildHostList(); void UpdateNetworkGameWindow(); -bool IsNetworkCompatibleVersion(const char *version, bool extended = false); - /* From network_command.cpp */ /** * Everything we need to know about a command to be able to execute it. diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index eda9e5162b..0aec81d395 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -10,6 +10,7 @@ #include "../stdafx.h" #include "../strings_func.h" #include "../date_func.h" +#include "core/game_info.h" #include "network_admin.h" #include "network_server.h" #include "network_udp.h" @@ -356,6 +357,20 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendClientInfo(NetworkClientIn return NETWORK_RECV_STATUS_OKAY; } +/** Send the client information about the server. */ +NetworkRecvStatus ServerNetworkGameSocketHandler::SendGameInfo() +{ + NetworkGameInfo ngi; + FillNetworkGameInfo(ngi); + + Packet *p = new Packet(PACKET_SERVER_GAME_INFO, SHRT_MAX); + SerializeNetworkGameInfo(p, &ngi); + + this->SendPacket(p); + + return NETWORK_RECV_STATUS_OKAY; +} + /** Send the client information about the companies. */ NetworkRecvStatus ServerNetworkGameSocketHandler::SendCompanyInfo() { @@ -495,7 +510,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendNewGRFCheck() p->Send_uint32 (grf_count); for (c = _grfconfig; c != nullptr; c = c->next) { - if (!HasBit(c->flags, GCF_STATIC)) this->SendGRFIdentifier(p, &c->ident); + if (!HasBit(c->flags, GCF_STATIC)) SerializeGRFIdentifier(p, &c->ident); } this->SendPacket(p); @@ -864,6 +879,11 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendSettingsAccessUpdate(bool * DEF_SERVER_RECEIVE_COMMAND has parameter: NetworkClientSocket *cs, Packet *p ************/ +NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_GAME_INFO(Packet *p) +{ + return this->SendGameInfo(); +} + NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMPANY_INFO(Packet *p) { return this->SendCompanyInfo(); diff --git a/src/network/network_server.h b/src/network/network_server.h index 3a0c2def2e..566f54dff4 100644 --- a/src/network/network_server.h +++ b/src/network/network_server.h @@ -24,6 +24,7 @@ extern NetworkClientSocketPool _networkclientsocket_pool; class ServerNetworkGameSocketHandler : public NetworkClientSocketPool::PoolItem<&_networkclientsocket_pool>, public NetworkGameSocketHandler, public TCPListenHandler { protected: NetworkRecvStatus Receive_CLIENT_JOIN(Packet *p) override; + NetworkRecvStatus Receive_CLIENT_GAME_INFO(Packet *p) override; NetworkRecvStatus Receive_CLIENT_COMPANY_INFO(Packet *p) override; NetworkRecvStatus Receive_CLIENT_GAME_PASSWORD(Packet *p) override; NetworkRecvStatus Receive_CLIENT_COMPANY_PASSWORD(Packet *p) override; @@ -43,6 +44,7 @@ protected: NetworkRecvStatus Receive_CLIENT_NEWGRFS_CHECKED(Packet *p) override; NetworkRecvStatus Receive_CLIENT_MOVE(Packet *p) override; + NetworkRecvStatus SendGameInfo(); NetworkRecvStatus SendCompanyInfo(); NetworkRecvStatus SendNewGRFCheck(); NetworkRecvStatus SendWelcome(); diff --git a/src/network/network_type.h b/src/network/network_type.h index 8608253a3b..c84687db71 100644 --- a/src/network/network_type.h +++ b/src/network/network_type.h @@ -10,7 +10,7 @@ #ifndef NETWORK_TYPE_H #define NETWORK_TYPE_H -#include "core/game.h" +#include "core/config.h" /** How many clients can we have */ static const uint MAX_CLIENTS = 255; diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index 47c194a588..0df07f052d 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -16,6 +16,7 @@ #include "../date_func.h" #include "../map_func.h" #include "../debug.h" +#include "core/game_info.h" #include "network_gamelist.h" #include "network_internal.h" #include "network_udp.h" @@ -192,27 +193,7 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, Networ } NetworkGameInfo ngi; - - /* Update some game_info */ - ngi.clients_on = _network_game_info.clients_on; - ngi.start_date = ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1); - - ngi.use_password = !StrEmpty(_settings_client.network.server_password); - ngi.clients_max = _settings_client.network.max_clients; - ngi.companies_on = (byte)Company::GetNumItems(); - ngi.companies_max = _settings_client.network.max_companies; - ngi.spectators_on = NetworkSpectatorCount(); - ngi.spectators_max = _settings_client.network.max_spectators; - ngi.game_date = _date; - ngi.map_width = MapSizeX(); - ngi.map_height = MapSizeY(); - ngi.map_set = _settings_game.game_creation.landscape; - ngi.dedicated = _network_dedicated; - ngi.grfconfig = _grfconfig; - - strecpy(ngi.server_name, _settings_client.network.server_name, lastof(ngi.server_name)); - strecpy(ngi.server_revision, _openttd_revision, lastof(ngi.server_revision)); - strecpy(ngi.short_server_revision, _openttd_revision, lastof(ngi.short_server_revision)); + FillNetworkGameInfo(ngi); if (p->CanReadFromPacket(8) && p->Recv_uint32() == FIND_SERVER_EXTENDED_TOKEN) { this->Reply_CLIENT_FIND_SERVER_extended(p, client_addr, &ngi); @@ -220,7 +201,7 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, Networ } Packet packet(PACKET_UDP_SERVER_RESPONSE); - this->SendNetworkGameInfo(&packet, &ngi); + SerializeNetworkGameInfo(&packet, &ngi); /* Let the client know that we are here */ this->SendPacket(&packet, client_addr); @@ -234,7 +215,7 @@ void ServerNetworkUDPSocketHandler::Reply_CLIENT_FIND_SERVER_extended(Packet *p, uint16 version = p->Recv_uint16(); Packet packet(PACKET_UDP_EX_SERVER_RESPONSE, SHRT_MAX); - this->SendNetworkGameInfoExtended(&packet, ngi, flags, version); + SerializeNetworkGameInfoExtended(&packet, ngi, flags, version); /* Let the client know that we are here */ this->SendPacket(&packet, client_addr, false, false, true); @@ -335,7 +316,7 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_GET_NEWGRFS(Packet *p, Networ /* The name could be an empty string, if so take the filename */ strecpy(name, info.name, lastof(name)); - this->SendGRFIdentifier(&packet, &info.ident); + SerializeGRFIdentifier(&packet, &info.ident); packet.Send_string(name); } @@ -348,7 +329,7 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_GET_NEWGRFS(Packet *p, Networ for (i = 0; i < num_grfs; i++) { GRFInfo info; - this->ReceiveGRFIdentifier(p, &info.ident); + DeserializeGRFIdentifier(p, &info.ident); if (memcmp(info.ident.md5sum, _out_of_band_grf_md5, 16) == 0) { if (info.ident.grfid == 0x56D2B000) { @@ -393,7 +374,6 @@ protected: void Receive_EX_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr) override; void Receive_MASTER_RESPONSE_LIST(Packet *p, NetworkAddress *client_addr) override; void Receive_SERVER_NEWGRFS(Packet *p, NetworkAddress *client_addr) override; - void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config) override; public: virtual ~ClientNetworkUDPSocketHandler() {} }; @@ -420,14 +400,20 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE_Common(Packet *p, Ne /* Find next item */ item = NetworkGameListAddItem(*client_addr); + /* Clear any existing GRFConfig chain. */ ClearGRFConfigList(&item->info.grfconfig); if (extended) { - this->ReceiveNetworkGameInfoExtended(p, &item->info); + /* Retrieve the NetworkGameInfo from the packet. */ + DeserializeNetworkGameInfoExtended(p, &item->info); } else { - this->ReceiveNetworkGameInfo(p, &item->info); + /* Retrieve the NetworkGameInfo from the packet. */ + DeserializeNetworkGameInfo(p, &item->info); } + /* Check for compatability with the client. */ + CheckGameCompatibility(item->info); + /* Ensure we consider the server online. */ + item->online = true; - item->info.compatible = true; { /* Checks whether there needs to be a request for names of GRFs and makes * the request if necessary. GRFs that need to be requested are the GRFs @@ -447,7 +433,7 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE_Common(Packet *p, Ne packet.Send_uint8(in_request_count); for (i = 0; i < in_request_count; i++) { - this->SendGRFIdentifier(&packet, &in_request[i]->ident); + SerializeGRFIdentifier(&packet, &in_request[i]->ident); } this->SendPacket(&packet, &item->address); @@ -476,12 +462,6 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE_Common(Packet *p, Ne strecat(item->info.server_name, " (IPv6)", lastof(item->info.server_name)); } - /* Check if we are allowed on this server based on the revision-match */ - item->info.version_compatible = IsNetworkCompatibleVersion(item->info.server_revision, extended); - item->info.compatible &= item->info.version_compatible; // Already contains match for GRFs - - item->online = true; - UpdateNetworkGameWindow(); } @@ -535,7 +515,7 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_NEWGRFS(Packet *p, NetworkAdd char name[NETWORK_GRF_NAME_LENGTH]; GRFIdentifier c; - this->ReceiveGRFIdentifier(p, &c); + DeserializeGRFIdentifier(p, &c); p->Recv_string(name, sizeof(name)); /* An empty name is not possible under normal circumstances @@ -552,25 +532,6 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_NEWGRFS(Packet *p, NetworkAdd } } -void ClientNetworkUDPSocketHandler::HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config) -{ - /* Find the matching GRF file */ - const GRFConfig *f = FindGRFConfig(config->ident.grfid, FGCM_EXACT, config->ident.md5sum); - if (f == nullptr) { - /* Don't know the GRF, so mark game incompatible and the (possibly) - * already resolved name for this GRF (another server has sent the - * name of the GRF already */ - config->name = FindUnknownGRFName(config->ident.grfid, config->ident.md5sum, true); - config->status = GCS_NOT_FOUND; - } else { - config->filename = f->filename; - config->name = f->name; - config->info = f->info; - config->url = f->url; - } - SetBit(config->flags, GCF_COPY); -} - /** Broadcast to all ips */ static void NetworkUDPBroadCast(NetworkUDPSocketHandler *socket) {