Fix: Don't allow right-click to close world generation progress window. (#13084)
[openttd-github.git] / src / survey.cpp
blob123c82563e6345935d5b2a9f644759f5977c11e3
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 survey.cpp Functions to survey the current game / system, for crashlog and network-survey. */
10 #include "stdafx.h"
12 #include "survey.h"
14 #include "settings_table.h"
15 #include "network/network.h"
16 #include "rev.h"
17 #include "settings_type.h"
18 #include "timer/timer_game_tick.h"
19 #include "timer/timer_game_calendar.h"
20 #include "timer/timer_game_economy.h"
22 #include "currency.h"
23 #include "fontcache.h"
24 #include "language.h"
26 #include "ai/ai_info.hpp"
27 #include "game/game.hpp"
28 #include "game/game_info.hpp"
30 #include "music/music_driver.hpp"
31 #include "sound/sound_driver.hpp"
32 #include "video/video_driver.hpp"
34 #include "base_media_base.h"
35 #include "blitter/factory.hpp"
37 #include "social_integration.h"
39 #ifdef WITH_ALLEGRO
40 # include <allegro.h>
41 #endif /* WITH_ALLEGRO */
42 #ifdef WITH_FONTCONFIG
43 # include <fontconfig/fontconfig.h>
44 #endif /* WITH_FONTCONFIG */
45 #ifdef WITH_PNG
46 /* pngconf.h, included by png.h doesn't like something in the
47 * freetype headers. As such it's not alphabetically sorted. */
48 # include <png.h>
49 #endif /* WITH_PNG */
50 #ifdef WITH_FREETYPE
51 # include <ft2build.h>
52 # include FT_FREETYPE_H
53 #endif /* WITH_FREETYPE */
54 #ifdef WITH_HARFBUZZ
55 # include <hb.h>
56 #endif /* WITH_HARFBUZZ */
57 #ifdef WITH_ICU_I18N
58 # include <unicode/uversion.h>
59 #endif /* WITH_ICU_I18N */
60 #ifdef WITH_LIBLZMA
61 # include <lzma.h>
62 #endif
63 #ifdef WITH_LZO
64 #include <lzo/lzo1x.h>
65 #endif
66 #if defined(WITH_SDL) || defined(WITH_SDL2)
67 # include <SDL.h>
68 #endif /* WITH_SDL || WITH_SDL2 */
69 #ifdef WITH_ZLIB
70 # include <zlib.h>
71 #endif
72 #ifdef WITH_CURL
73 # include <curl/curl.h>
74 #endif
76 #include "safeguards.h"
78 NLOHMANN_JSON_SERIALIZE_ENUM(GRFStatus, {
79 {GRFStatus::GCS_UNKNOWN, "unknown"},
80 {GRFStatus::GCS_DISABLED, "disabled"},
81 {GRFStatus::GCS_NOT_FOUND, "not found"},
82 {GRFStatus::GCS_INITIALISED, "initialised"},
83 {GRFStatus::GCS_ACTIVATED, "activated"},
86 NLOHMANN_JSON_SERIALIZE_ENUM(SocialIntegrationPlugin::State, {
87 {SocialIntegrationPlugin::State::RUNNING, "running"},
88 {SocialIntegrationPlugin::State::FAILED, "failed"},
89 {SocialIntegrationPlugin::State::PLATFORM_NOT_RUNNING, "platform_not_running"},
90 {SocialIntegrationPlugin::State::UNLOADED, "unloaded"},
91 {SocialIntegrationPlugin::State::DUPLICATE, "duplicate"},
92 {SocialIntegrationPlugin::State::UNSUPPORTED_API, "unsupported_api"},
93 {SocialIntegrationPlugin::State::INVALID_SIGNATURE, "invalid_signature"},
97 /** Lookup table to convert a VehicleType to a string. */
98 static const std::string _vehicle_type_to_string[] = {
99 "train",
100 "roadveh",
101 "ship",
102 "aircraft",
106 * List of all the generic setting tables.
108 * There are a few tables that are special and not processed like the rest:
109 * - _currency_settings
110 * - _misc_settings
111 * - _company_settings
112 * - _win32_settings
113 * As such, they are not part of this list.
115 static auto &GenericSettingTables()
117 static const SettingTable _generic_setting_tables[] = {
118 _difficulty_settings,
119 _economy_settings,
120 _game_settings,
121 _gui_settings,
122 _linkgraph_settings,
123 _locale_settings,
124 _multimedia_settings,
125 _network_settings,
126 _news_display_settings,
127 _pathfinding_settings,
128 _script_settings,
129 _world_settings,
131 return _generic_setting_tables;
135 * Convert a settings table to JSON.
137 * @param survey The JSON object.
138 * @param table The settings table to convert.
139 * @param object The object to get the settings from.
140 * @param skip_if_default If true, skip any settings that are on their default value.
142 static void SurveySettingsTable(nlohmann::json &survey, const SettingTable &table, void *object, bool skip_if_default)
144 for (auto &desc : table) {
145 const SettingDesc *sd = GetSettingDesc(desc);
146 /* Skip any old settings we no longer save/load. */
147 if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
149 auto name = sd->GetName();
150 if (skip_if_default && sd->IsDefaultValue(object)) continue;
151 survey[name] = sd->FormatValue(object);
156 * Convert settings to JSON.
158 * @param survey The JSON object.
160 void SurveySettings(nlohmann::json &survey, bool skip_if_default)
162 SurveySettingsTable(survey, _misc_settings, nullptr, skip_if_default);
163 #if defined(_WIN32) && !defined(DEDICATED)
164 SurveySettingsTable(survey, _win32_settings, nullptr, skip_if_default);
165 #endif
166 for (auto &table : GenericSettingTables()) {
167 SurveySettingsTable(survey, table, &_settings_game, skip_if_default);
169 SurveySettingsTable(survey, _currency_settings, &GetCustomCurrency(), skip_if_default);
170 SurveySettingsTable(survey, _company_settings, &_settings_client.company, skip_if_default);
174 * Convert compiler information to JSON.
176 * @param survey The JSON object.
178 void SurveyCompiler(nlohmann::json &survey)
180 #if defined(_MSC_VER)
181 survey["name"] = "MSVC";
182 survey["version"] = _MSC_VER;
183 #elif defined(__ICC) && defined(__GNUC__)
184 survey["name"] = "ICC";
185 survey["version"] = __ICC;
186 # if defined(__GNUC__)
187 survey["extra"] = fmt::format("GCC {}.{}.{} mode", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
188 # endif
189 #elif defined(__GNUC__)
190 survey["name"] = "GCC";
191 survey["version"] = fmt::format("{}.{}.{}", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
192 #else
193 survey["name"] = "unknown";
194 #endif
196 #if defined(__VERSION__)
197 survey["extra"] = __VERSION__;
198 #endif
202 * Convert generic OpenTTD information to JSON.
204 * @param survey The JSON object.
206 void SurveyOpenTTD(nlohmann::json &survey)
208 survey["version"]["revision"] = std::string(_openttd_revision);
209 survey["version"]["modified"] = _openttd_revision_modified;
210 survey["version"]["tagged"] = _openttd_revision_tagged;
211 survey["version"]["hash"] = std::string(_openttd_revision_hash);
212 survey["version"]["newgrf"] = fmt::format("{:X}", _openttd_newgrf_version);
213 survey["version"]["content"] = std::string(_openttd_content_version);
214 survey["build_date"] = std::string(_openttd_build_date);
215 survey["bits"] =
216 #ifdef POINTER_IS_64BIT
218 #else
220 #endif
222 if constexpr (std::endian::native == std::endian::little) survey["endian"] = "little";
223 if constexpr (std::endian::native == std::endian::big) survey["endian"] = "big";
224 survey["dedicated_build"] =
225 #ifdef DEDICATED
226 "yes"
227 #else
228 "no"
229 #endif
234 * Convert game session information to JSON.
236 * @param survey The JSON object.
238 void SurveyGameSession(nlohmann::json &survey)
240 survey["id"] = _game_session_stats.savegame_id;
241 survey["seconds"] = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - _game_session_stats.start_time).count();
242 if (_game_session_stats.savegame_size.has_value()) {
243 survey["savegame_size"] = _game_session_stats.savegame_size.value();
248 * Convert generic game information to JSON.
250 * @param survey The JSON object.
252 void SurveyConfiguration(nlohmann::json &survey)
254 survey["network"] = _networking ? (_network_server ? "server" : "client") : "no";
255 if (_current_language != nullptr) {
256 survey["language"]["filename"] = FS2OTTD(_current_language->file.filename());
257 survey["language"]["name"] = _current_language->name;
258 survey["language"]["isocode"] = _current_language->isocode;
260 if (BlitterFactory::GetCurrentBlitter() != nullptr) {
261 survey["blitter"] = BlitterFactory::GetCurrentBlitter()->GetName();
263 if (MusicDriver::GetInstance() != nullptr) {
264 survey["music_driver"] = MusicDriver::GetInstance()->GetName();
266 if (SoundDriver::GetInstance() != nullptr) {
267 survey["sound_driver"] = SoundDriver::GetInstance()->GetName();
269 if (VideoDriver::GetInstance() != nullptr) {
270 survey["video_driver"] = VideoDriver::GetInstance()->GetName();
271 survey["video_info"] = VideoDriver::GetInstance()->GetInfoString();
273 if (BaseGraphics::GetUsedSet() != nullptr) {
274 survey["graphics_set"] = fmt::format("{}.{}", BaseGraphics::GetUsedSet()->name, BaseGraphics::GetUsedSet()->version);
275 const GRFConfig *extra_cfg = BaseGraphics::GetUsedSet()->GetExtraConfig();
276 if (extra_cfg != nullptr && extra_cfg->num_params > 0) {
277 survey["graphics_set_parameters"] = std::span<const uint32_t>(extra_cfg->param.data(), extra_cfg->num_params);
278 } else {
279 survey["graphics_set_parameters"] = std::span<const uint32_t>();
282 if (BaseMusic::GetUsedSet() != nullptr) {
283 survey["music_set"] = fmt::format("{}.{}", BaseMusic::GetUsedSet()->name, BaseMusic::GetUsedSet()->version);
285 if (BaseSounds::GetUsedSet() != nullptr) {
286 survey["sound_set"] = fmt::format("{}.{}", BaseSounds::GetUsedSet()->name, BaseSounds::GetUsedSet()->version);
291 * Convert font information to JSON.
293 * @param survey The JSON object.
295 void SurveyFont(nlohmann::json &survey)
297 survey["small"] = FontCache::Get(FS_SMALL)->GetFontName();
298 survey["medium"] = FontCache::Get(FS_NORMAL)->GetFontName();
299 survey["large"] = FontCache::Get(FS_LARGE)->GetFontName();
300 survey["mono"] = FontCache::Get(FS_MONO)->GetFontName();
304 * Convert company information to JSON.
306 * @param survey The JSON object.
308 void SurveyCompanies(nlohmann::json &survey)
310 for (const Company *c : Company::Iterate()) {
311 auto &company = survey[std::to_string(c->index)];
312 if (c->ai_info == nullptr) {
313 company["type"] = "human";
314 } else {
315 company["type"] = "ai";
316 company["script"] = fmt::format("{}.{}", c->ai_info->GetName(), c->ai_info->GetVersion());
319 for (VehicleType type = VEH_BEGIN; type < VEH_COMPANY_END; type++) {
320 uint amount = c->group_all[type].num_vehicle;
321 company["vehicles"][_vehicle_type_to_string[type]] = amount;
324 company["infrastructure"]["road"] = c->infrastructure.GetRoadTotal();
325 company["infrastructure"]["tram"] = c->infrastructure.GetTramTotal();
326 company["infrastructure"]["rail"] = c->infrastructure.GetRailTotal();
327 company["infrastructure"]["signal"] = c->infrastructure.signal;
328 company["infrastructure"]["water"] = c->infrastructure.water;
329 company["infrastructure"]["station"] = c->infrastructure.station;
330 company["infrastructure"]["airport"] = c->infrastructure.airport;
335 * Convert timer information to JSON.
337 * @param survey The JSON object.
339 void SurveyTimers(nlohmann::json &survey)
341 survey["ticks"] = TimerGameTick::counter;
343 TimerGameEconomy::YearMonthDay economy_ymd = TimerGameEconomy::ConvertDateToYMD(TimerGameEconomy::date);
344 survey["economy"] = fmt::format("{:04}-{:02}-{:02} ({})", economy_ymd.year, economy_ymd.month + 1, economy_ymd.day, TimerGameEconomy::date_fract);
346 TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::date);
347 survey["calendar"] = fmt::format("{:04}-{:02}-{:02} ({})", ymd.year, ymd.month + 1, ymd.day, TimerGameCalendar::date_fract);
351 * Convert GRF information to JSON.
353 * @param survey The JSON object.
355 void SurveyGrfs(nlohmann::json &survey)
357 for (GRFConfig *c = _grfconfig; c != nullptr; c = c->next) {
358 auto grfid = fmt::format("{:08x}", BSWAP32(c->ident.grfid));
359 auto &grf = survey[grfid];
361 grf["md5sum"] = FormatArrayAsHex(c->ident.md5sum);
362 grf["status"] = c->status;
364 if ((c->palette & GRFP_GRF_MASK) == GRFP_GRF_UNSET) grf["palette"] = "unset";
365 if ((c->palette & GRFP_GRF_MASK) == GRFP_GRF_DOS) grf["palette"] = "dos";
366 if ((c->palette & GRFP_GRF_MASK) == GRFP_GRF_WINDOWS) grf["palette"] = "windows";
367 if ((c->palette & GRFP_GRF_MASK) == GRFP_GRF_ANY) grf["palette"] = "any";
369 if ((c->palette & GRFP_BLT_MASK) == GRFP_BLT_UNSET) grf["blitter"] = "unset";
370 if ((c->palette & GRFP_BLT_MASK) == GRFP_BLT_32BPP) grf["blitter"] = "32bpp";
372 grf["is_static"] = HasBit(c->flags, GCF_STATIC);
373 grf["parameters"] = std::span<const uint32_t>(c->param.data(), c->num_params);
378 * Convert game-script information to JSON.
380 * @param survey The JSON object.
382 void SurveyGameScript(nlohmann::json &survey)
384 if (Game::GetInfo() == nullptr) return;
386 survey = fmt::format("{}.{}", Game::GetInfo()->GetName(), Game::GetInfo()->GetVersion());
390 * Convert compiled libraries information to JSON.
392 * @param survey The JSON object.
394 void SurveyLibraries(nlohmann::json &survey)
396 #ifdef WITH_ALLEGRO
397 survey["allegro"] = std::string(allegro_id);
398 #endif /* WITH_ALLEGRO */
400 #ifdef WITH_FONTCONFIG
401 int version = FcGetVersion();
402 survey["fontconfig"] = fmt::format("{}.{}.{}", version / 10000, (version / 100) % 100, version % 100);
403 #endif /* WITH_FONTCONFIG */
405 #ifdef WITH_FREETYPE
406 FT_Library library;
407 int major, minor, patch;
408 FT_Init_FreeType(&library);
409 FT_Library_Version(library, &major, &minor, &patch);
410 FT_Done_FreeType(library);
411 survey["freetype"] = fmt::format("{}.{}.{}", major, minor, patch);
412 #endif /* WITH_FREETYPE */
414 #if defined(WITH_HARFBUZZ)
415 survey["harfbuzz"] = hb_version_string();
416 #endif /* WITH_HARFBUZZ */
418 #if defined(WITH_ICU_I18N)
419 /* 4 times 0-255, separated by dots (.) and a trailing '\0' */
420 char buf[4 * 3 + 3 + 1];
421 UVersionInfo ver;
422 u_getVersion(ver);
423 u_versionToString(ver, buf);
424 survey["icu_i18n"] = buf;
425 #endif /* WITH_ICU_I18N */
427 #ifdef WITH_LIBLZMA
428 survey["lzma"] = lzma_version_string();
429 #endif
431 #ifdef WITH_LZO
432 survey["lzo"] = lzo_version_string();
433 #endif
435 #ifdef WITH_PNG
436 survey["png"] = png_get_libpng_ver(nullptr);
437 #endif /* WITH_PNG */
439 #ifdef WITH_SDL
440 const SDL_version *sdl_v = SDL_Linked_Version();
441 survey["sdl"] = fmt::format("{}.{}.{}", sdl_v->major, sdl_v->minor, sdl_v->patch);
442 #elif defined(WITH_SDL2)
443 SDL_version sdl2_v;
444 SDL_GetVersion(&sdl2_v);
445 survey["sdl2"] = fmt::format("{}.{}.{}", sdl2_v.major, sdl2_v.minor, sdl2_v.patch);
446 #endif
448 #ifdef WITH_ZLIB
449 survey["zlib"] = zlibVersion();
450 #endif
452 #ifdef WITH_CURL
453 auto *curl_v = curl_version_info(CURLVERSION_NOW);
454 survey["curl"] = curl_v->version;
455 survey["curl_ssl"] = curl_v->ssl_version == nullptr ? "none" : curl_v->ssl_version;
456 #endif
460 * Convert plugin information to JSON.
462 * @param survey The JSON object.
464 void SurveyPlugins(nlohmann::json &survey)
466 auto _plugins = SocialIntegration::GetPlugins();
468 for (auto &plugin : _plugins) {
469 auto &platform = survey[plugin->social_platform];
470 platform.push_back({
471 {"name", plugin->name},
472 {"version", plugin->version},
473 {"basepath", plugin->basepath},
474 {"state", plugin->state},
480 * Change the bytes of memory into a textual version rounded up to the biggest unit.
482 * For example, 16751108096 would become 16 GiB.
484 * @param memory The bytes of memory.
485 * @return std::string A textual representation.
487 std::string SurveyMemoryToText(uint64_t memory)
489 memory = memory / 1024; // KiB
490 memory = CeilDiv(memory, 1024); // MiB
492 /* Anything above 512 MiB we represent in GiB. */
493 if (memory > 512) {
494 return fmt::format("{} GiB", CeilDiv(memory, 1024));
497 /* Anything above 64 MiB we represent in a multiplier of 128 MiB. */
498 if (memory > 64) {
499 return fmt::format("{} MiB", Ceil(memory, 128));
502 /* Anything else in a multiplier of 4 MiB. */
503 return fmt::format("{} MiB", Ceil(memory, 4));