OpenTTD Source 20260208-master-g43af8e94d0
network_game_info.cpp
Go to the documentation of this file.
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "../../stdafx.h"
11#include "network_game_info.h"
12#include "../../company_base.h"
15#include "../../debug.h"
16#include "../../map_func.h"
17#include "../../game/game.hpp"
19#include "../../settings_type.h"
20#include "../../string_func.h"
21#include "../../rev.h"
22#include "../network_func.h"
23#include "../network.h"
24#include "../network_internal.h"
25#include "packet.h"
26
27#include "table/strings.h"
28
29#include "../../safeguards.h"
30
31
36static const uint GITHASH_SUFFIX_LEN = 12;
37
39
44std::string_view GetNetworkRevisionString()
45{
46 static std::string network_revision;
47
48 if (network_revision.empty()) {
49#if not defined(NETWORK_INTERNAL_H)
50# error("network_internal.h must be included, otherwise the debug related preprocessor tokens won't be picked up correctly.")
51#elif not defined(ENABLE_NETWORK_SYNC_EVERY_FRAME)
52 /* Just a standard build. */
53 network_revision = _openttd_revision;
54#elif defined(NETWORK_SEND_DOUBLE_SEED)
55 /* Build for debugging that sends both parts of the seeds and by doing that practically syncs every frame. */
56 network_revision = fmt::format("dbg_seed-{}", _openttd_revision);
57#else
58 /* Build for debugging that sends the first part of the seed every frame, practically syncing every frame. */
59 network_revision = fmt::format("dbg_sync-{}", _openttd_revision);
60#endif
61 if (_openttd_revision_tagged) {
62 /* Tagged; do not mangle further, though ensure it's not too long. */
63 if (network_revision.size() >= NETWORK_REVISION_LENGTH) network_revision.resize(NETWORK_REVISION_LENGTH - 1);
64 } else {
65 /* Not tagged; add the githash suffix while ensuring the string does not become too long. */
66 assert(_openttd_revision_modified < 3);
67 std::string githash_suffix = fmt::format("-{}{}", "gum"[_openttd_revision_modified], _openttd_revision_hash);
68 if (githash_suffix.size() > GITHASH_SUFFIX_LEN) githash_suffix.resize(GITHASH_SUFFIX_LEN);
69
70 /* Where did the hash start in the original string? Overwrite from that position, unless that would create a too long string. */
71 size_t hash_end = network_revision.find_last_of('-');
72 if (hash_end == std::string::npos) hash_end = network_revision.size();
73 if (hash_end + githash_suffix.size() >= NETWORK_REVISION_LENGTH) hash_end = NETWORK_REVISION_LENGTH - githash_suffix.size() - 1;
74
75 /* Replace the git hash in revision string. */
76 network_revision.replace(hash_end, std::string::npos, githash_suffix);
77 }
78 assert(network_revision.size() < NETWORK_REVISION_LENGTH); // size does not include terminator, constant does, hence strictly less than
79 Debug(net, 3, "Network revision name: {}", network_revision);
80 }
81
82 return network_revision;
83}
84
90static std::string_view ExtractNetworkRevisionHash(std::string_view revision_string)
91{
92 size_t index = revision_string.find_last_of('-');
93 if (index == std::string::npos) return {};
94 return revision_string.substr(index);
95}
96
102bool IsNetworkCompatibleVersion(std::string_view other)
103{
104 std::string_view our_revision = GetNetworkRevisionString();
105 if (our_revision == other) return true;
106
107 /* If this version is tagged, then the revision string must be a complete match,
108 * since there is no git hash suffix in it.
109 * This is needed to avoid situations like "1.9.0-beta1" comparing equal to "2.0.0-beta1". */
110 if (_openttd_revision_tagged) return false;
111
112 /* One of the versions is for some sort of debugging, but not both. */
113 if (other.starts_with("dbg_seed") != our_revision.starts_with("dbg_seed")) return false;
114 if (other.starts_with("dbg_sync") != our_revision.starts_with("dbg_sync")) return false;
115
116 std::string_view hash1 = ExtractNetworkRevisionHash(our_revision);
117 std::string_view hash2 = ExtractNetworkRevisionHash(other);
118 return hash1 == hash2;
119}
120
125{
126 /* Check if we are allowed on this server based on the revision-check. */
129
130 /* Check if we have all the GRFs on the client-system too. */
131 for (const auto &c : ngi.grfconfig) {
132 if (c->status == GCS_NOT_FOUND) ngi.compatible = false;
133 }
134}
135
141{
142 _network_game_info.use_password = !_settings_client.network.server_password.empty();
143 _network_game_info.calendar_start = TimerGameCalendar::ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1);
144 _network_game_info.clients_max = _settings_client.network.max_clients;
145 _network_game_info.companies_max = _settings_client.network.max_companies;
146 _network_game_info.map_width = Map::SizeX();
147 _network_game_info.map_height = Map::SizeY();
148 _network_game_info.landscape = _settings_game.game_creation.landscape;
151
152 _network_game_info.server_name = _settings_client.network.server_name;
154}
155
161{
162 /* These variables are updated inside _network_game_info as if they are global variables:
163 * - clients_on
164 * - invite_code
165 * These don't need to be updated manually here.
166 */
167 _network_game_info.companies_on = (uint8_t)Company::GetNumItems();
168 _network_game_info.spectators_on = NetworkSpectatorCount();
171 return _network_game_info;
172}
173
182static void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig &config, std::string_view name)
183{
184 /* Find the matching GRF file */
185 const GRFConfig *f = FindGRFConfig(config.ident.grfid, FGCM_EXACT, &config.ident.md5sum);
186 if (f == nullptr) {
187 AddGRFTextToList(config.name, name.empty() ? GetString(STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN) : name);
188 config.status = GCS_NOT_FOUND;
189 } else {
190 config.filename = f->filename;
191 config.name = f->name;
192 config.info = f->info;
193 config.url = f->url;
194 }
196}
197
204void SerializeNetworkGameInfo(Packet &p, const NetworkServerGameInfo &info, bool send_newgrf_names)
205{
207
208 /*
209 * Please observe the order.
210 * The parts must be read in the same order as they are sent!
211 */
212
213 /* Update the documentation in game_info.h on changes
214 * to the NetworkGameInfo wire-protocol! */
215
216 /* NETWORK_GAME_INFO_VERSION = 7 */
218
219 /* NETWORK_GAME_INFO_VERSION = 6 */
220 p.Send_uint8(send_newgrf_names ? NST_GRFID_MD5_NAME : NST_GRFID_MD5);
221
222 /* NETWORK_GAME_INFO_VERSION = 5 */
223 GameInfo *game_info = Game::GetInfo();
224 p.Send_uint32(game_info == nullptr ? -1 : (uint32_t)game_info->GetVersion());
225 p.Send_string(game_info == nullptr ? "" : game_info->GetName());
226
227 /* NETWORK_GAME_INFO_VERSION = 4 */
228 {
229 /* Only send the GRF Identification (GRF_ID and MD5 checksum) of
230 * the GRFs that are needed, i.e. the ones that the server has
231 * selected in the NewGRF GUI and not the ones that are used due
232 * to the fact that they are in [newgrf-static] in openttd.cfg */
233 uint count = std::ranges::count_if(info.grfconfig, [](const auto &c) { return !c->flags.Test(GRFConfigFlag::Static); });
234 p.Send_uint8 (count); // Send number of GRFs
235
236 /* Send actual GRF Identifications */
237 for (const auto &c : info.grfconfig) {
238 if (c->flags.Test(GRFConfigFlag::Static)) continue;
239
240 SerializeGRFIdentifier(p, c->ident);
241 if (send_newgrf_names) p.Send_string(c->GetName());
242 }
243 }
244
245 /* NETWORK_GAME_INFO_VERSION = 3 */
246 p.Send_uint32(info.calendar_date.base());
247 p.Send_uint32(info.calendar_start.base());
248
249 /* NETWORK_GAME_INFO_VERSION = 2 */
250 p.Send_uint8 (info.companies_max);
251 p.Send_uint8 (info.companies_on);
252 p.Send_uint8 (info.clients_max); // Used to be max-spectators
253
254 /* NETWORK_GAME_INFO_VERSION = 1 */
255 p.Send_string(info.server_name);
257 p.Send_bool (info.use_password);
258 p.Send_uint8 (info.clients_max);
259 p.Send_uint8 (info.clients_on);
260 p.Send_uint8 (info.spectators_on);
261 p.Send_uint16(info.map_width);
262 p.Send_uint16(info.map_height);
264 p.Send_bool (info.dedicated);
265}
266
274{
275 uint8_t game_info_version = p.Recv_uint8();
276 NewGRFSerializationType newgrf_serialisation = NST_GRFID_MD5;
277
278 /*
279 * Please observe the order.
280 * The parts must be read in the same order as they are sent!
281 */
282
283 /* Update the documentation in game_info.h on changes
284 * to the NetworkGameInfo wire-protocol! */
285
286 switch (game_info_version) {
287 case 7:
288 info.ticks_playing = p.Recv_uint64();
289 [[fallthrough]];
290
291 case 6:
292 newgrf_serialisation = (NewGRFSerializationType)p.Recv_uint8();
293 if (newgrf_serialisation >= NST_END) return;
294 [[fallthrough]];
295
296 case 5: {
297 info.gamescript_version = (int)p.Recv_uint32();
299 [[fallthrough]];
300 }
301
302 case 4: {
303 /* Ensure that the maximum number of NewGRFs and the field in the network
304 * protocol are matched to each other. If that is not the case anymore a
305 * check must be added to ensure the received data is still valid. */
306 static_assert(std::numeric_limits<uint8_t>::max() == NETWORK_MAX_GRF_COUNT);
307 uint num_grfs = p.Recv_uint8();
308
309 GRFConfigList &dst = info.grfconfig;
310 for (uint i = 0; i < num_grfs; i++) {
312 switch (newgrf_serialisation) {
313 case NST_GRFID_MD5:
315 break;
316
319 break;
320
321 case NST_LOOKUP_ID: {
322 if (newgrf_lookup_table == nullptr) return;
323 auto it = newgrf_lookup_table->find(p.Recv_uint32());
324 if (it == newgrf_lookup_table->end()) return;
325 grf = it->second;
326 break;
327 }
328
329 default:
330 NOT_REACHED();
331 }
332
333 auto c = std::make_unique<GRFConfig>();
334 c->ident = grf.ident;
336
337 /* Append GRFConfig to the list */
338 dst.push_back(std::move(c));
339 }
340 [[fallthrough]];
341 }
342
343 case 3:
344 info.calendar_date = TimerGameCalendar::Date{Clamp(p.Recv_uint32(), 0, CalendarTime::MAX_DATE.base())};
345 info.calendar_start = TimerGameCalendar::Date{Clamp(p.Recv_uint32(), 0, CalendarTime::MAX_DATE.base())};
346 [[fallthrough]];
347
348 case 2:
349 info.companies_max = p.Recv_uint8 ();
350 info.companies_on = p.Recv_uint8 ();
351 p.Recv_uint8(); // Used to contain max-spectators.
352 [[fallthrough]];
353
354 case 1:
357 if (game_info_version < 6) p.Recv_uint8 (); // Used to contain server-lang.
358 info.use_password = p.Recv_bool ();
359 info.clients_max = p.Recv_uint8 ();
360 info.clients_on = p.Recv_uint8 ();
361 info.spectators_on = p.Recv_uint8 ();
362 if (game_info_version < 3) { // 16 bits dates got scrapped and are read earlier
365 }
366 if (game_info_version < 6) while (p.Recv_uint8() != 0) {} // Used to contain the map-name.
367 info.map_width = p.Recv_uint16();
368 info.map_height = p.Recv_uint16();
370 info.dedicated = p.Recv_bool ();
371
372 if (to_underlying(info.landscape) >= NUM_LANDSCAPE) info.landscape = LandscapeType::Temperate;
373 }
374
375 /* For older servers, estimate the ticks running based on the calendar date. */
376 if (game_info_version < 7) {
377 info.ticks_playing = static_cast<uint64_t>(std::max(0, info.calendar_date.base() - info.calendar_start.base())) * Ticks::DAY_TICKS;
378 }
379}
380
387{
388 p.Send_uint32(grf.grfid);
389 p.Send_bytes(grf.md5sum);
390}
391
398{
399 grf.grfid = p.Recv_uint32();
400 p.Recv_bytes(grf.md5sum);
401}
402
constexpr Timpl & Set()
Set all bits.
All static information from an Game like name, version, etc.
Definition game_info.hpp:16
static class GameInfo * GetInfo()
Get the current GameInfo.
Definition game.hpp:70
const std::string & GetName() const
Get the Name of the script.
int GetVersion() const
Get the version of the script.
static constexpr TimerGameTick::Ticks DAY_TICKS
1 day is 74 ticks; TimerGameCalendar::date_fract used to be uint16_t and incremented by 885.
static Date ConvertYMDToDate(Year year, Month month, Day day)
Converts a tuple of Year, Month and Day to a Date.
static Date date
Current date in days (day counter).
static constexpr TimerGame< struct Calendar >::Date MAX_DATE
static constexpr TimerGame< struct Calendar >::Date DAYS_TILL_ORIGINAL_BASE_YEAR
static TickCounter counter
Monotonic counter, in ticks, since start of game.
Definition of stuff that is very close to a company, like the company struct itself.
static const uint NETWORK_NAME_LENGTH
The maximum length of the server name and map name, in bytes including '\0'.
Definition config.h:51
static const uint NETWORK_GRF_NAME_LENGTH
Maximum length of the name of a GRF.
Definition config.h:71
static const uint NETWORK_MAX_GRF_COUNT
Maximum number of GRFs that can be sent.
Definition config.h:88
static const uint NETWORK_REVISION_LENGTH
The maximum length of the revision, in bytes including '\0'.
Definition config.h:54
static const uint8_t NETWORK_GAME_INFO_VERSION
What version of game-info do we use?
Definition config.h:47
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:17
Base functions for all Games.
GameInfo keeps track of all information of an Game, like Author, Description, ...
LandscapeType
Landscape types.
@ Temperate
Base landscape.
Functions related to maps.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
bool _network_dedicated
are we a dedicated server?
Definition network.cpp:69
Basic functions/variables used all over the place.
Network functions used by other parts of OpenTTD.
void SerializeNetworkGameInfo(Packet &p, const NetworkServerGameInfo &info, bool send_newgrf_names)
Serializes the NetworkGameInfo struct to the packet.
std::string_view GetNetworkRevisionString()
Get the network version string used by this build.
void CheckGameCompatibility(NetworkGameInfo &ngi)
Check if an game entry is compatible with our client.
void DeserializeGRFIdentifier(Packet &p, GRFIdentifier &grf)
Deserializes the GRFIdentifier (GRF ID and MD5 checksum) from the packet.
static void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig &config, std::string_view name)
Function that is called for every GRFConfig that is read when receiving a NetworkGameInfo.
static std::string_view ExtractNetworkRevisionHash(std::string_view revision_string)
Extract the git hash from the revision string.
const NetworkServerGameInfo & GetCurrentNetworkServerGameInfo()
Get the NetworkServerGameInfo structure with the latest information of the server.
void SerializeGRFIdentifier(Packet &p, const GRFIdentifier &grf)
Serializes the GRFIdentifier (GRF ID and MD5 checksum) to the packet.
void FillStaticNetworkServerGameInfo()
Fill a NetworkServerGameInfo structure with the static content, or things that are so static they can...
void DeserializeGRFIdentifierWithName(Packet &p, NamedGRFIdentifier &grf)
Deserializes the NamedGRFIdentifier (GRF ID, MD5 checksum and name) from the packet.
void DeserializeNetworkGameInfo(Packet &p, NetworkGameInfo &info, const GameInfoNewGRFLookupTable *newgrf_lookup_table)
Deserializes the NetworkGameInfo struct from the packet.
NetworkServerGameInfo _network_game_info
Information about our game.
static const uint GITHASH_SUFFIX_LEN
How many hex digits of the git hash to include in network revision string.
bool IsNetworkCompatibleVersion(std::string_view other)
Checks whether the given version string is compatible with our version.
Convert NetworkGameInfo to Packet and back.
std::unordered_map< uint32_t, NamedGRFIdentifier > GameInfoNewGRFLookupTable
Lookup table for the GameInfo in case of NST_LOOKUP_ID.
NewGRFSerializationType
The different types/ways a NewGRF can be serialized in the GameInfo since version 6.
@ NST_LOOKUP_ID
Unique ID into a lookup table that is sent before.
@ NST_END
The end of the list (period).
@ NST_GRFID_MD5_NAME
Unique GRF ID, MD5 checksum and name.
@ NST_GRFID_MD5
Unique GRF ID and MD5 checksum.
Variables and function used internally.
GRFConfigList _grfconfig
First item in list of current GRF set up.
void CopyGRFConfigList(GRFConfigList &dst, const GRFConfigList &src, bool init_only)
Copy a GRF Config list.
const GRFConfig * FindGRFConfig(uint32_t grfid, FindGRFConfigMode mode, const MD5Hash *md5sum, uint32_t desired_version)
Find a NewGRF in the scanned list.
@ GCS_NOT_FOUND
GRF file was not found in the local cache.
@ Copy
The data is copied from a grf in _all_grfs.
@ Static
GRF file is used statically (can be used in any MP game).
@ FGCM_EXACT
Only find Grfs matching md5sum.
static void AddGRFTextToList(GRFTextList &list, uint8_t langid, std::string_view text_to_add)
Add a new text to a GRFText list.
Basic functions to create, fill and read packets.
Declaration of OTTD revision dependent variables.
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
Types related to global configuration settings.
Definition of base types and functions in a cross-platform compatible way.
Functions related to low-level strings.
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
Information about GRF, used in the game and (part of it) in savegames.
GRFTextWrapper url
NOSAVE: URL belonging to this GRF.
GRFTextWrapper info
NOSAVE: GRF info (author, copyright, ...) (Action 0x08).
GRFTextWrapper name
NOSAVE: GRF name (Action 0x08).
GRFStatus status
NOSAVE: GRFStatus, enum.
GRFConfigFlags flags
NOSAVE: GCF_Flags, bitset.
std::string filename
Filename - either with or without full path.
GRFIdentifier ident
grfid and md5sum to uniquely identify newgrfs
Basic data to distinguish a GRF.
uint32_t grfid
GRF ID (defined by Action 0x08).
MD5Hash md5sum
MD5 checksum of file to distinguish files with the same GRF ID (eg. newer version of GRF).
static uint SizeX()
Get the size of the map along the X.
Definition map_func.h:262
static uint SizeY()
Get the size of the map along the Y.
Definition map_func.h:271
Container to hold the GRF identifier (GRF ID + MD5 checksum) and the name associated with that NewGRF...
std::string name
The name of the NewGRF.
GRFIdentifier ident
The unique identifier of the NewGRF.
The game information that is sent from the server to the clients with extra information only required...
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.
The game information that is sent from the server to the client.
TimerGameCalendar::Date calendar_start
When the game started.
bool dedicated
Is this a dedicated server?
std::string server_revision
The version number the server is using (e.g.: 'r304' or 0.5.0).
bool use_password
Is this server passworded?
uint8_t clients_max
Max clients allowed on server.
uint8_t spectators_on
How many spectators do we have?
GRFConfigList grfconfig
List of NewGRF files used.
uint16_t map_height
Map height.
std::string server_name
Server name.
uint16_t map_width
Map width.
TimerGameTick::TickCounter ticks_playing
Amount of ticks the game has been running unpaused.
LandscapeType landscape
The used landscape.
uint8_t companies_max
Max companies allowed on server.
std::string gamescript_name
Name of the gamescript.
TimerGameCalendar::Date calendar_date
Current calendar date.
int gamescript_version
Version of the gamescript.
uint8_t companies_on
How many started companies do we have.
uint8_t clients_on
Current count of clients on server.
Internal entity of a packet.
Definition packet.h:41
uint16_t Recv_uint16()
Read a 16 bits integer from the packet.
Definition packet.cpp:330
size_t Recv_bytes(std::span< uint8_t > span)
Extract at most the length of the span bytes from the packet into the span.
Definition packet.cpp:401
uint64_t Recv_uint64()
Read a 64 bits integer from the packet.
Definition packet.cpp:362
bool Recv_bool()
Read a boolean from the packet.
Definition packet.cpp:307
uint32_t Recv_uint32()
Read a 32 bits integer from the packet.
Definition packet.cpp:345
void Send_string(std::string_view data)
Sends a string over the network.
Definition packet.cpp:170
std::string Recv_string(size_t length, StringValidationSettings settings=StringValidationSetting::ReplaceWithQuestionMark)
Reads characters (bytes) from the packet until it finds a '\0', or reaches a maximum of length charac...
Definition packet.cpp:423
void Send_bool(bool data)
Package a boolean in the packet.
Definition packet.cpp:109
std::span< const uint8_t > Send_bytes(const std::span< const uint8_t > span)
Send as many of the bytes as possible in the packet.
Definition packet.cpp:195
uint8_t Recv_uint8()
Read a 8 bits integer from the packet.
Definition packet.cpp:316
void Send_uint8(uint8_t data)
Package a 8 bits integer in the packet.
Definition packet.cpp:118
void Send_uint32(uint32_t data)
Package a 32 bits integer in the packet.
Definition packet.cpp:139
void Send_uint16(uint16_t data)
Package a 16 bits integer in the packet.
Definition packet.cpp:128
void Send_uint64(uint64_t data)
Package a 64 bits integer in the packet.
Definition packet.cpp:152
Definition of the game-calendar-timer.
Definition of the tick-based game-timer.