Update to 14.0-beta1

This commit is contained in:
dP
2024-02-04 02:18:17 +05:30
parent 79037e2c65
commit 33ef333b57
1325 changed files with 138461 additions and 70983 deletions
+283 -185
View File
@@ -17,10 +17,12 @@
#include "fontcache.h"
#include "error.h"
#include "error_func.h"
#include "gui.h"
#include "base_media_base.h"
#include "saveload/saveload.h"
#include "company_cmd.h"
#include "company_func.h"
#include "command_func.h"
#include "news_func.h"
@@ -32,6 +34,7 @@
#include "console_func.h"
#include "screenshot.h"
#include "network/network.h"
#include "network/network_server.h"
#include "network/network_func.h"
#include "ai/ai.hpp"
#include "ai/ai_config.hpp"
@@ -39,7 +42,6 @@
#include "genworld.h"
#include "progress.h"
#include "strings_func.h"
#include "date_func.h"
#include "vehicle_func.h"
#include "gamelog.h"
#include "animated_tile_func.h"
@@ -67,11 +69,17 @@
#include "framerate_type.h"
#include "industry.h"
#include "network/network_gui.h"
#include "network/network_survey.h"
#include "misc_cmd.h"
#include "timer/timer.h"
#include "timer/timer_game_calendar.h"
#include "timer/timer_game_economy.h"
#include "timer/timer_game_realtime.h"
#include "timer/timer_game_tick.h"
#include "social_integration.h"
#include "linkgraph/linkgraphschedule.h"
#include <stdarg.h>
#include <system_error>
#include "safeguards.h"
@@ -82,14 +90,15 @@
#endif
void CallLandscapeTick();
void IncreaseDate();
void DoPaletteAnimations();
void MusicLoop();
void ResetMusic();
void CallWindowGameTickEvent();
bool HandleBootstrap();
extern void AfterLoadCompanyStats();
extern Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY);
extern void OSOpenBrowser(const std::string &url);
extern void RebuildTownCaches();
extern void ShowOSErrorBox(const char *buf, bool system);
extern std::string _config_file;
@@ -99,19 +108,12 @@ NewGRFScanCallback *_request_newgrf_scan_callback = nullptr;
/**
* Error handling for fatal user errors.
* @param s the string to print.
* @param str the string to print.
* @note Does NEVER return.
*/
void CDECL usererror(const char *s, ...)
void UserErrorI(const std::string &str)
{
va_list va;
char buf[512];
va_start(va, s);
vseprintf(buf, lastof(buf), s, va);
va_end(va);
ShowOSErrorBox(buf, false);
ShowOSErrorBox(str.c_str(), false);
if (VideoDriver::GetInstance() != nullptr) VideoDriver::GetInstance()->Stop();
#ifdef __EMSCRIPTEN__
@@ -119,60 +121,39 @@ void CDECL usererror(const char *s, ...)
/* In effect, the game ends here. As emscripten_set_main_loop() caused
* the stack to be unwound, the code after MainLoop() in
* openttd_main() is never executed. */
EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
EM_ASM(if (window["openttd_abort"]) openttd_abort());
#endif
exit(1);
_exit(1);
}
/**
* Error handling for fatal non-user errors.
* @param s the string to print.
* @param str the string to print.
* @note Does NEVER return.
*/
void CDECL error(const char *s, ...)
void FatalErrorI(const std::string &str)
{
va_list va;
char buf[2048];
va_start(va, s);
vseprintf(buf, lastof(buf), s, va);
va_end(va);
if (VideoDriver::GetInstance() == nullptr || VideoDriver::GetInstance()->HasGUI()) {
ShowOSErrorBox(buf, true);
ShowOSErrorBox(str.c_str(), true);
}
/* Set the error message for the crash log and then invoke it. */
CrashLog::SetErrorMessage(buf);
CrashLog::SetErrorMessage(str);
abort();
}
/**
* Shows some information on the console/a popup box depending on the OS.
* @param str the text to show.
*/
void CDECL ShowInfoF(const char *str, ...)
{
va_list va;
char buf[1024];
va_start(va, str);
vseprintf(buf, lastof(buf), str, va);
va_end(va);
ShowInfo(buf);
}
/**
* Show the help message when someone passed a wrong parameter.
*/
static void ShowHelp()
{
char buf[8192];
char *p = buf;
std::string str;
str.reserve(8192);
p += seprintf(p, lastof(buf), "OpenTTD %s\n", _openttd_revision);
p = strecpy(p,
std::back_insert_iterator<std::string> output_iterator = std::back_inserter(str);
fmt::format_to(output_iterator, "OpenTTD {}\n", _openttd_revision);
str +=
"\n"
"\n"
"Command line options:\n"
@@ -185,13 +166,12 @@ static void ShowHelp()
" -t year = Set starting year\n"
" -d [[fac=]lvl[,...]]= Debug mode\n"
" -e = Start Editor\n"
" -g [savegame] = Start new/save game immediately\n"
" -g [savegame|scenario|heightmap] = Start new/savegame/scenario/heightmap immediately\n"
" -G seed = Set random seed\n"
" -n [ip:port#company]= Join network game\n"
" -n host[:port][#company]= Join network game\n"
" -p password = Password to join server\n"
" -P password = Password to join company\n"
" -D [ip][:port] = Start dedicated server\n"
" -l ip[:port] = Redirect Debug()\n"
" -D [host][:port] = Start dedicated server\n"
#if !defined(_WIN32)
" -f = Fork into the background (dedicated only)\n"
#endif
@@ -204,84 +184,79 @@ static void ShowHelp()
" -q savegame = Write some information about the savegame and exit\n"
" -Q = Don't scan for/load NewGRF files on startup\n"
" -QQ = Disable NewGRF scanning/loading entirely\n"
"\n",
lastof(buf)
);
"\n";
/* List the graphics packs */
p = BaseGraphics::GetSetsList(p, lastof(buf));
BaseGraphics::GetSetsList(output_iterator);
/* List the sounds packs */
p = BaseSounds::GetSetsList(p, lastof(buf));
BaseSounds::GetSetsList(output_iterator);
/* List the music packs */
p = BaseMusic::GetSetsList(p, lastof(buf));
BaseMusic::GetSetsList(output_iterator);
/* List the drivers */
p = DriverFactoryBase::GetDriversInfo(p, lastof(buf));
DriverFactoryBase::GetDriversInfo(output_iterator);
/* List the blitters */
p = BlitterFactory::GetBlittersInfo(p, lastof(buf));
BlitterFactory::GetBlittersInfo(output_iterator);
/* List the debug facilities. */
p = DumpDebugFacilityNames(p, lastof(buf));
DumpDebugFacilityNames(output_iterator);
/* We need to initialize the AI, so it finds the AIs */
AI::Initialize();
const std::string ai_list = AI::GetConsoleList(true);
p = strecpy(p, ai_list.c_str(), lastof(buf));
AI::GetConsoleList(output_iterator, true);
AI::Uninitialize(true);
/* We need to initialize the GameScript, so it finds the GSs */
Game::Initialize();
const std::string game_list = Game::GetConsoleList(true);
p = strecpy(p, game_list.c_str(), lastof(buf));
Game::GetConsoleList(output_iterator, true);
Game::Uninitialize(true);
/* ShowInfo put output to stderr, but version information should go
* to stdout; this is the only exception */
#if !defined(_WIN32)
printf("%s\n", buf);
fmt::print("{}\n", str);
#else
ShowInfo(buf);
ShowInfoI(str);
#endif
}
static void WriteSavegameInfo(const char *name)
static void WriteSavegameInfo(const std::string &name)
{
extern SaveLoadVersion _sl_version;
uint32 last_ottd_rev = 0;
uint32_t last_ottd_rev = 0;
byte ever_modified = 0;
bool removed_newgrfs = false;
GamelogInfo(_load_check_data.gamelog_action, _load_check_data.gamelog_actions, &last_ottd_rev, &ever_modified, &removed_newgrfs);
_gamelog.Info(&last_ottd_rev, &ever_modified, &removed_newgrfs);
char buf[8192];
char *p = buf;
p += seprintf(p, lastof(buf), "Name: %s\n", name);
p += seprintf(p, lastof(buf), "Savegame ver: %d\n", _sl_version);
p += seprintf(p, lastof(buf), "NewGRF ver: 0x%08X\n", last_ottd_rev);
p += seprintf(p, lastof(buf), "Modified: %d\n", ever_modified);
std::string message;
message.reserve(1024);
fmt::format_to(std::back_inserter(message), "Name: {}\n", name);
fmt::format_to(std::back_inserter(message), "Savegame ver: {}\n", _sl_version);
fmt::format_to(std::back_inserter(message), "NewGRF ver: 0x{:08X}\n", last_ottd_rev);
fmt::format_to(std::back_inserter(message), "Modified: {}\n", ever_modified);
if (removed_newgrfs) {
p += seprintf(p, lastof(buf), "NewGRFs have been removed\n");
fmt::format_to(std::back_inserter(message), "NewGRFs have been removed\n");
}
p = strecpy(p, "NewGRFs:\n", lastof(buf));
message += "NewGRFs:\n";
if (_load_check_data.HasNewGrfs()) {
for (GRFConfig *c = _load_check_data.grfconfig; c != nullptr; c = c->next) {
char md5sum[33];
md5sumToString(md5sum, lastof(md5sum), HasBit(c->flags, GCF_COMPATIBLE) ? c->original_md5sum : c->ident.md5sum);
p += seprintf(p, lastof(buf), "%08X %s %s\n", c->ident.grfid, md5sum, c->filename);
fmt::format_to(std::back_inserter(message), "{:08X} {} {}\n", c->ident.grfid,
FormatArrayAsHex(HasBit(c->flags, GCF_COMPATIBLE) ? c->original_md5sum : c->ident.md5sum), c->filename);
}
}
/* ShowInfo put output to stderr, but version information should go
* to stdout; this is the only exception */
#if !defined(_WIN32)
printf("%s\n", buf);
fmt::print("{}\n", message);
#else
ShowInfo(buf);
ShowInfoI(message);
#endif
}
@@ -296,12 +271,12 @@ static void ParseResolution(Dimension *res, const char *s)
{
const char *t = strchr(s, 'x');
if (t == nullptr) {
ShowInfoF("Invalid resolution '%s'", s);
ShowInfo("Invalid resolution '{}'", s);
return;
}
res->width = std::max(strtoul(s, nullptr, 0), 64UL);
res->height = std::max(strtoul(t + 1, nullptr, 0), 64UL);
res->width = std::max(std::strtoul(s, nullptr, 0), 64UL);
res->height = std::max(std::strtoul(t + 1, nullptr, 0), 64UL);
}
@@ -315,6 +290,7 @@ static void ShutdownGame()
if (_network_available) NetworkShutDown(); // Shut down the network and close any open connections
SocialIntegration::Shutdown();
DriverFactoryBase::ShutdownDrivers();
UnInitWindowSystem();
@@ -324,7 +300,7 @@ static void ShutdownGame()
Game::Uninitialize(false);
/* Uninitialize variables that are allocated dynamically */
GamelogReset();
_gamelog.Reset();
LinkGraphSchedule::Clear();
PoolBase::Clean(PT_ALL);
@@ -386,9 +362,6 @@ void MakeNewgameSettingsLive()
_settings_game.ai_config[c] = nullptr;
if (_settings_newgame.ai_config[c] != nullptr) {
_settings_game.ai_config[c] = new AIConfig(_settings_newgame.ai_config[c]);
if (!AIConfig::GetConfig(c, AIConfig::SSS_FORCE_GAME)->HasScript()) {
AIConfig::GetConfig(c, AIConfig::SSS_FORCE_GAME)->Change(nullptr);
}
}
}
_settings_game.game_config = nullptr;
@@ -397,21 +370,20 @@ void MakeNewgameSettingsLive()
}
}
void OpenBrowser(const char *url)
void OpenBrowser(const std::string &url)
{
/* Make sure we only accept urls that are sure to open a browser. */
if (strstr(url, "http://") != url && strstr(url, "https://") != url) return;
extern void OSOpenBrowser(const char *url);
OSOpenBrowser(url);
if (url.starts_with("http://") || url.starts_with("https://")) {
OSOpenBrowser(url);
}
}
/** Callback structure of statements to be executed after the NewGRF scan. */
struct AfterNewGRFScan : NewGRFScanCallback {
Year startyear = INVALID_YEAR; ///< The start year.
uint32 generation_seed = GENERATE_NEW_SEED; ///< Seed for the new game.
TimerGameCalendar::Year startyear = CalendarTime::INVALID_YEAR; ///< The start year.
uint32_t generation_seed = GENERATE_NEW_SEED; ///< Seed for the new game.
std::string dedicated_host; ///< Hostname for the dedicated server.
uint16 dedicated_port = 0; ///< Port for the dedicated server.
uint16_t dedicated_port = 0; ///< Port for the dedicated server.
std::string connection_string; ///< Information about the server to connect to
std::string join_server_password; ///< The password to join the server with.
std::string join_company_password; ///< The password to join the company with.
@@ -427,7 +399,7 @@ struct AfterNewGRFScan : NewGRFScanCallback {
static_assert(sizeof(generation_seed) == sizeof(_settings_game.game_creation.generation_seed));
}
virtual void OnNewGRFsScanned()
void OnNewGRFsScanned() override
{
ResetGRFConfig(false);
@@ -457,8 +429,8 @@ struct AfterNewGRFScan : NewGRFScanCallback {
MusicDriver::GetInstance()->SetVolume(_settings_client.music.music_vol);
SetEffectVolume(_settings_client.music.effect_vol);
if (startyear != INVALID_YEAR) IConsoleSetSetting("game_creation.starting_year", startyear);
if (generation_seed != GENERATE_NEW_SEED) _settings_newgame.game_creation.generation_seed = generation_seed;
if (startyear != CalendarTime::INVALID_YEAR) IConsoleSetSetting("game_creation.starting_year", startyear.base());
_settings_newgame.game_creation.generation_seed = generation_seed;
if (!dedicated_host.empty()) {
_network_bind_list.clear();
@@ -486,6 +458,22 @@ struct AfterNewGRFScan : NewGRFScanCallback {
}
};
void PostMainLoop()
{
WaitTillSaved();
/* only save config if we have to */
if (_save_config) {
SaveToConfig();
SaveHotkeysToConfig();
WindowDesc::SaveToConfig();
SaveToHighScore();
}
/* Reset windowing system, stop drivers, free used memory, ... */
ShutdownGame();
}
#if defined(UNIX)
extern void DedicatedFork();
#endif
@@ -500,8 +488,7 @@ static const OptionData _options[] = {
GETOPT_SHORT_VALUE('v'),
GETOPT_SHORT_VALUE('b'),
GETOPT_SHORT_OPTVAL('D'),
GETOPT_SHORT_OPTVAL('n'),
GETOPT_SHORT_VALUE('l'),
GETOPT_SHORT_VALUE('n'),
GETOPT_SHORT_VALUE('p'),
GETOPT_SHORT_VALUE('P'),
#if !defined(_WIN32)
@@ -540,7 +527,6 @@ int openttd_main(int argc, char *argv[])
Dimension resolution = {0, 0};
std::unique_ptr<AfterNewGRFScan> scanner(new AfterNewGRFScan());
bool dedicated = false;
char *debuglog_conn = nullptr;
bool only_local_path = false;
extern bool _dedicated_forks;
@@ -568,17 +554,14 @@ int openttd_main(int argc, char *argv[])
videodriver = "dedicated";
blitter = "null";
dedicated = true;
SetDebugString("net=4", ShowInfo);
SetDebugString("net=4", ShowInfoI);
if (mgo.opt != nullptr) {
scanner->dedicated_host = ParseFullConnectionString(mgo.opt, scanner->dedicated_port);
}
break;
case 'f': _dedicated_forks = true; break;
case 'n':
scanner->connection_string = mgo.opt; // optional IP:port#company parameter
break;
case 'l':
debuglog_conn = mgo.opt;
scanner->connection_string = mgo.opt; // host:port#company parameter
break;
case 'p':
scanner->join_server_password = mgo.opt;
@@ -592,24 +575,40 @@ int openttd_main(int argc, char *argv[])
#if defined(_WIN32)
CreateConsole();
#endif
if (mgo.opt != nullptr) SetDebugString(mgo.opt, ShowInfo);
if (mgo.opt != nullptr) SetDebugString(mgo.opt, ShowInfoI);
break;
}
case 'e': _switch_mode = (_switch_mode == SM_LOAD_GAME || _switch_mode == SM_LOAD_SCENARIO ? SM_LOAD_SCENARIO : SM_EDITOR); break;
case 'e':
/* Allow for '-e' before or after '-g'. */
switch (_switch_mode) {
case SM_MENU: _switch_mode = SM_EDITOR; break;
case SM_LOAD_GAME: _switch_mode = SM_LOAD_SCENARIO; break;
case SM_START_HEIGHTMAP: _switch_mode = SM_LOAD_HEIGHTMAP; break;
default: break;
}
break;
case 'g':
if (mgo.opt != nullptr) {
_file_to_saveload.SetName(mgo.opt);
bool is_scenario = _switch_mode == SM_EDITOR || _switch_mode == SM_LOAD_SCENARIO;
_switch_mode = is_scenario ? SM_LOAD_SCENARIO : SM_LOAD_GAME;
_file_to_saveload.SetMode(SLO_LOAD, is_scenario ? FT_SCENARIO : FT_SAVEGAME, DFT_GAME_FILE);
_file_to_saveload.name = mgo.opt;
/* if the file doesn't exist or it is not a valid savegame, let the saveload code show an error */
auto t = _file_to_saveload.name.find_last_of('.');
if (t != std::string::npos) {
FiosType ft = FiosGetSavegameListCallback(SLO_LOAD, _file_to_saveload.name, _file_to_saveload.name.substr(t).c_str(), nullptr, nullptr);
if (ft != FIOS_TYPE_INVALID) _file_to_saveload.SetMode(ft);
std::string extension = std::filesystem::path(_file_to_saveload.name).extension().string();
auto [ft, _] = FiosGetSavegameListCallback(SLO_LOAD, _file_to_saveload.name, extension);
if (ft == FIOS_TYPE_INVALID) {
std::tie(ft, _) = FiosGetScenarioListCallback(SLO_LOAD, _file_to_saveload.name, extension);
}
if (ft == FIOS_TYPE_INVALID) {
std::tie(ft, _) = FiosGetHeightmapListCallback(SLO_LOAD, _file_to_saveload.name, extension);
}
/* Allow for '-e' before or after '-g'. */
switch (GetAbstractFileType(ft)) {
case FT_SAVEGAME: _switch_mode = (_switch_mode == SM_EDITOR ? SM_LOAD_SCENARIO : SM_LOAD_GAME); break;
case FT_SCENARIO: _switch_mode = (_switch_mode == SM_EDITOR ? SM_LOAD_SCENARIO : SM_LOAD_GAME); break;
case FT_HEIGHTMAP: _switch_mode = (_switch_mode == SM_EDITOR ? SM_LOAD_HEIGHTMAP : SM_START_HEIGHTMAP); break;
default: break;
}
_file_to_saveload.SetMode(SLO_LOAD, GetAbstractFileType(ft), GetDetailedFileType(ft));
break;
}
@@ -626,20 +625,16 @@ int openttd_main(int argc, char *argv[])
return ret;
}
char title[80];
title[0] = '\0';
FiosGetSavegameListCallback(SLO_LOAD, mgo.opt, strrchr(mgo.opt, '.'), title, lastof(title));
auto [_, title] = FiosGetSavegameListCallback(SLO_LOAD, mgo.opt, strrchr(mgo.opt, '.'));
_load_check_data.Clear();
SaveOrLoadResult res = SaveOrLoad(mgo.opt, SLO_CHECK, DFT_GAME_FILE, SAVE_DIR, false);
if (res != SL_OK || _load_check_data.HasErrors()) {
fprintf(stderr, "Failed to open savegame\n");
fmt::print(stderr, "Failed to open savegame\n");
if (_load_check_data.HasErrors()) {
InitializeLanguagePacks(); // A language pack is needed for GetString()
char buf[256];
SetDParamStr(0, _load_check_data.error_data);
GetString(buf, _load_check_data.error, lastof(buf));
fprintf(stderr, "%s\n", buf);
SetDParamStr(0, _load_check_data.error_msg);
fmt::print(stderr, "{}\n", GetString(_load_check_data.error));
}
return ret;
}
@@ -652,7 +647,7 @@ int openttd_main(int argc, char *argv[])
_skip_all_newgrf_scanning += 1;
break;
}
case 'G': scanner->generation_seed = strtoul(mgo.opt, nullptr, 10); break;
case 'G': scanner->generation_seed = std::strtoul(mgo.opt, nullptr, 10); break;
case 'c': _config_file = mgo.opt; break;
case 'x': scanner->save_config = false; break;
case 'X': only_local_path = true; break;
@@ -716,15 +711,31 @@ int openttd_main(int argc, char *argv[])
InitWindowSystem();
BaseGraphics::FindSets();
if (graphics_set.empty() && !BaseGraphics::ini_set.empty()) graphics_set = BaseGraphics::ini_set;
if (!BaseGraphics::SetSet(graphics_set)) {
if (!graphics_set.empty()) {
BaseGraphics::SetSet({});
ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_BASE_GRAPHICS_NOT_FOUND);
msg.SetDParamStr(0, graphics_set);
ScheduleErrorMessage(msg);
bool valid_graphics_set;
if (!graphics_set.empty()) {
valid_graphics_set = BaseGraphics::SetSetByName(graphics_set);
} else if (BaseGraphics::ini_data.shortname != 0) {
graphics_set = BaseGraphics::ini_data.name;
valid_graphics_set = BaseGraphics::SetSetByShortname(BaseGraphics::ini_data.shortname);
if (valid_graphics_set && !BaseGraphics::ini_data.extra_params.empty()) {
GRFConfig &extra_cfg = BaseGraphics::GetUsedSet()->GetOrCreateExtraConfig();
if (extra_cfg.IsCompatible(BaseGraphics::ini_data.extra_version)) {
extra_cfg.SetParams(BaseGraphics::ini_data.extra_params);
}
}
} else if (!BaseGraphics::ini_data.name.empty()) {
graphics_set = BaseGraphics::ini_data.name;
valid_graphics_set = BaseGraphics::SetSetByName(BaseGraphics::ini_data.name);
} else {
valid_graphics_set = true;
BaseGraphics::SetSet(nullptr); // ignore error, continue to bootstrap GUI
}
if (!valid_graphics_set) {
BaseGraphics::SetSet(nullptr);
ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_BASE_GRAPHICS_NOT_FOUND);
msg.SetDParamStr(0, graphics_set);
ScheduleErrorMessage(msg);
}
/* Initialize game palette */
@@ -744,8 +755,8 @@ int openttd_main(int argc, char *argv[])
BlitterFactory::SelectBlitter("32bpp-anim") == nullptr) {
if (BlitterFactory::SelectBlitter(blitter) == nullptr) {
blitter.empty() ?
usererror("Failed to autoprobe blitter") :
usererror("Failed to select requested blitter '%s'; does it exist?", blitter.c_str());
UserError("Failed to autoprobe blitter") :
UserError("Failed to select requested blitter '{}'; does it exist?", blitter);
}
}
@@ -760,12 +771,9 @@ int openttd_main(int argc, char *argv[])
/* The video driver is now selected, now initialise GUI zoom */
AdjustGUIZoom(false);
SocialIntegration::Initialize();
NetworkStartUp(); // initialize network-core
if (debuglog_conn != nullptr && _network_available) {
NetworkStartDebugLog(debuglog_conn);
}
if (!HandleBootstrap()) {
ShutdownGame();
return ret;
@@ -778,9 +786,9 @@ int openttd_main(int argc, char *argv[])
BaseSounds::FindSets();
if (sounds_set.empty() && !BaseSounds::ini_set.empty()) sounds_set = BaseSounds::ini_set;
if (!BaseSounds::SetSet(sounds_set)) {
if (!BaseSounds::SetSetByName(sounds_set)) {
if (sounds_set.empty() || !BaseSounds::SetSet({})) {
usererror("Failed to find a sounds set. Please acquire a sounds set for OpenTTD. See section 1.4 of README.md.");
UserError("Failed to find a sounds set. Please acquire a sounds set for OpenTTD. See section 1.4 of README.md.");
} else {
ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_BASE_SOUNDS_NOT_FOUND);
msg.SetDParamStr(0, sounds_set);
@@ -790,9 +798,9 @@ int openttd_main(int argc, char *argv[])
BaseMusic::FindSets();
if (music_set.empty() && !BaseMusic::ini_set.empty()) music_set = BaseMusic::ini_set;
if (!BaseMusic::SetSet(music_set)) {
if (!BaseMusic::SetSetByName(music_set)) {
if (music_set.empty() || !BaseMusic::SetSet({})) {
usererror("Failed to find a music set. Please acquire a music set for OpenTTD. See section 1.4 of README.md.");
UserError("Failed to find a music set. Please acquire a music set for OpenTTD. See section 1.4 of README.md.");
} else {
ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_BASE_MUSIC_NOT_FOUND);
msg.SetDParamStr(0, music_set);
@@ -816,18 +824,7 @@ int openttd_main(int argc, char *argv[])
VideoDriver::GetInstance()->MainLoop();
WaitTillSaved();
/* only save config if we have to */
if (_save_config) {
SaveToConfig();
SaveHotkeysToConfig();
WindowDesc::SaveToConfig();
SaveToHighScore();
}
/* Reset windowing system, stop drivers, free used memory, ... */
ShutdownGame();
PostMainLoop();
return ret;
}
@@ -837,6 +834,7 @@ void HandleExitGameRequest()
_exit_game = true;
} else if (_settings_client.gui.autosave_on_exit) {
DoExitSave();
_survey.Transmit(NetworkSurveyHandler::Reason::EXIT, true);
_exit_game = true;
} else {
AskExitGame();
@@ -853,7 +851,7 @@ static void OnStartScenario()
/* Make sure all industries were built "this year", to avoid too early closures. (#9918) */
for (Industry *i : Industry::Iterate()) {
i->last_prod_year = _cur_year;
i->last_prod_year = TimerGameEconomy::year;
}
}
@@ -892,10 +890,15 @@ static void MakeNewGameDone()
/* Overwrite color from settings if needed
* COLOUR_END corresponds to Random colour */
if (_settings_client.gui.starting_colour != COLOUR_END) {
c->colour = _settings_client.gui.starting_colour;
ResetCompanyLivery(c);
_company_colours[c->index] = (Colours)c->colour;
_company_colours[c->index] = c->colour;
}
if (_settings_client.gui.starting_colour_secondary != COLOUR_END && HasBit(_loaded_newgrf_features.used_liveries, LS_DEFAULT)) {
Command<CMD_SET_COMPANY_COLOUR>::Post(LS_DEFAULT, false, _settings_client.gui.starting_colour_secondary);
}
OnStartGame(false);
@@ -915,7 +918,11 @@ static void MakeNewGameDone()
CheckIndustries();
MarkWholeScreenDirty();
if (_network_server && !_network_dedicated) ShowClientList();
if (_network_server) {
ChangeNetworkRestartTime(true);
if (!_network_dedicated) ShowClientList();
}
}
static void MakeNewGame(bool from_heightmap, bool reset_settings)
@@ -1014,6 +1021,28 @@ bool SafeLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileTy
return false;
}
static void UpdateSocialIntegration(GameMode game_mode)
{
switch (game_mode) {
case GM_BOOTSTRAP:
case GM_MENU:
SocialIntegration::EventEnterMainMenu();
break;
case GM_NORMAL:
if (_networking) {
SocialIntegration::EventEnterMultiplayer(Map::SizeX(), Map::SizeY());
} else {
SocialIntegration::EventEnterSingleplayer(Map::SizeX(), Map::SizeY());
}
break;
case GM_EDITOR:
SocialIntegration::EventEnterScenarioEditor(Map::SizeX(), Map::SizeY());
break;
}
}
void SwitchToMode(SwitchMode new_mode)
{
/* If we are saving something, the network stays in its current state */
@@ -1048,9 +1077,21 @@ void SwitchToMode(SwitchMode new_mode)
/* Make sure all AI controllers are gone at quitting game */
if (new_mode != SM_SAVE_GAME) AI::KillAll();
/* When we change mode, reset the autosave. */
if (new_mode != SM_SAVE_GAME) ChangeAutosaveFrequency(true);
/* Transmit the survey if we were in normal-mode and not saving. It always means we leaving the current game. */
if (_game_mode == GM_NORMAL && new_mode != SM_SAVE_GAME) _survey.Transmit(NetworkSurveyHandler::Reason::LEAVE);
/* Keep track when we last switch mode. Used for survey, to know how long someone was in a game. */
if (new_mode != SM_SAVE_GAME) _switch_mode_time = std::chrono::steady_clock::now();
switch (new_mode) {
case SM_EDITOR: // Switch to scenario editor
MakeNewEditorWorld();
GenerateSavegameId();
UpdateSocialIntegration(GM_EDITOR);
break;
case SM_RELOADGAME: // Reload with what-ever started the game
@@ -1067,11 +1108,17 @@ void SwitchToMode(SwitchMode new_mode)
}
MakeNewGame(false, new_mode == SM_NEWGAME);
GenerateSavegameId();
UpdateSocialIntegration(GM_NORMAL);
break;
case SM_RESTARTGAME: // Restart --> 'Random game' with current settings
case SM_NEWGAME: // New Game --> 'Random game'
MakeNewGame(false, new_mode == SM_NEWGAME);
GenerateSavegameId();
UpdateSocialIntegration(GM_NORMAL);
break;
case SM_LOAD_GAME: { // Load game, Play Scenario
@@ -1080,7 +1127,7 @@ void SwitchToMode(SwitchMode new_mode)
if (!SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, GM_NORMAL, NO_DIRECTORY)) {
SetDParamStr(0, GetSaveLoadErrorString());
ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_CRITICAL);
} else {
if (_file_to_saveload.abstract_ftype == FT_SCENARIO) {
OnStartScenario();
@@ -1089,37 +1136,52 @@ void SwitchToMode(SwitchMode new_mode)
/* Decrease pause counter (was increased from opening load dialog) */
Command<CMD_PAUSE>::Post(PM_PAUSED_SAVELOAD, false);
}
UpdateSocialIntegration(GM_NORMAL);
break;
}
case SM_RESTART_HEIGHTMAP: // Load a heightmap and start a new game from it with current settings
case SM_START_HEIGHTMAP: // Load a heightmap and start a new game from it
MakeNewGame(true, new_mode == SM_START_HEIGHTMAP);
GenerateSavegameId();
UpdateSocialIntegration(GM_NORMAL);
break;
case SM_LOAD_HEIGHTMAP: // Load heightmap from scenario editor
SetLocalCompany(OWNER_NONE);
_game_mode = GM_EDITOR;
GenerateWorld(GWM_HEIGHTMAP, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y);
GenerateSavegameId();
MarkWholeScreenDirty();
UpdateSocialIntegration(GM_EDITOR);
break;
case SM_LOAD_SCENARIO: { // Load scenario from scenario editor
if (SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, GM_EDITOR, NO_DIRECTORY)) {
SetLocalCompany(OWNER_NONE);
_settings_newgame.game_creation.starting_year = _cur_year;
GenerateSavegameId();
_settings_newgame.game_creation.starting_year = TimerGameCalendar::year;
/* Cancel the saveload pausing */
Command<CMD_PAUSE>::Post(PM_PAUSED_SAVELOAD, false);
} else {
SetDParamStr(0, GetSaveLoadErrorString());
ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_CRITICAL);
}
UpdateSocialIntegration(GM_EDITOR);
break;
}
case SM_JOIN_GAME: // Join a multiplayer game
LoadIntroGame();
NetworkClientJoinGame();
SocialIntegration::EventJoiningMultiplayer();
break;
case SM_MENU: // Switch to game intro menu
@@ -1128,6 +1190,16 @@ void SwitchToMode(SwitchMode new_mode)
ShowErrorMessage(STR_WARNING_FALLBACK_SOUNDSET, INVALID_STRING_ID, WL_CRITICAL);
BaseSounds::ini_set = BaseSounds::GetUsedSet()->name;
}
if (_settings_client.network.participate_survey == PS_ASK) {
/* No matter how often you go back to the main menu, only ask the first time. */
static bool asked_once = false;
if (!asked_once) {
asked_once = true;
ShowNetworkAskSurvey();
}
}
UpdateSocialIntegration(GM_MENU);
break;
case SM_SAVE_GAME: // Save game.
@@ -1175,7 +1247,6 @@ static void CheckCaches()
old_town_caches.push_back(t->cache);
}
extern void RebuildTownCaches();
RebuildTownCaches();
RebuildSubsidisedSourceAndDestinationCache();
@@ -1191,7 +1262,6 @@ static void CheckCaches()
std::vector<CompanyInfrastructure> old_infrastructure;
for (const Company *c : Company::Iterate()) old_infrastructure.push_back(c->infrastructure);
extern void AfterLoadCompanyStats();
AfterLoadCompanyStats();
i = 0;
@@ -1204,7 +1274,7 @@ static void CheckCaches()
/* Strict checking of the road stop cache entries */
for (const RoadStop *rs : RoadStop::Iterate()) {
if (IsStandardRoadStopTile(rs->xy)) continue;
if (IsBayRoadStopTile(rs->xy)) continue;
assert(rs->GetEntry(DIAGDIR_NE) != rs->GetEntry(DIAGDIR_NW));
rs->GetEntry(DIAGDIR_NE)->CheckIntegrity(rs);
@@ -1212,7 +1282,6 @@ static void CheckCaches()
}
for (Vehicle *v : Vehicle::Iterate()) {
extern void FillNewGRFVehicleCache(const Vehicle *v);
if (v != v->First() || v->vehstatus & VS_CRASHED || !v->IsPrimaryVehicle()) continue;
uint length = 0;
@@ -1301,11 +1370,11 @@ static void CheckCaches()
for (Industry *ind : Industry::Iterate()) old_industry_stations_near.push_back(ind->stations_near);
for (Station *st : Station::Iterate()) {
for (CargoID c = 0; c < NUM_CARGO; c++) {
for (GoodsEntry &ge : st->goods) {
byte buff[sizeof(StationCargoList)];
memcpy(buff, &st->goods[c].cargo, sizeof(StationCargoList));
st->goods[c].cargo.InvalidateCache();
assert(memcmp(&st->goods[c].cargo, buff, sizeof(StationCargoList)) == 0);
memcpy(buff, &ge.cargo, sizeof(StationCargoList));
ge.cargo.InvalidateCache();
assert(memcmp(&ge.cargo, buff, sizeof(StationCargoList)) == 0);
}
/* Check docking tiles */
@@ -1394,10 +1463,9 @@ void StateGameLoop()
CallWindowGameTickEvent();
NewsLoop();
} else {
if (_debug_desync_level > 2 && _date_fract == 0 && (_date & 0x1F) == 0) {
if (_debug_desync_level > 2 && TimerGameEconomy::date_fract == 0 && (TimerGameEconomy::date.base() & 0x1F) == 0) {
/* Save the desync savegame if needed. */
char name[MAX_PATH];
seprintf(name, lastof(name), "dmp_cmds_%08x_%08x.sav", _settings_game.game_creation.generation_seed, _date);
std::string name = fmt::format("dmp_cmds_{:08x}_{:08x}.sav", _settings_game.game_creation.generation_seed, TimerGameEconomy::date);
SaveOrLoad(name, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false);
}
@@ -1409,7 +1477,11 @@ void StateGameLoop()
BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP);
AnimateAnimatedTiles();
IncreaseDate();
if (TimerManager<TimerGameCalendar>::Elapsed(1)) {
RunVehicleCalendarDayProc();
}
TimerManager<TimerGameEconomy>::Elapsed(1);
TimerManager<TimerGameTick>::Elapsed(1);
RunTileLoop();
CallVehicleTicks();
CallLandscapeTick();
@@ -1417,7 +1489,7 @@ void StateGameLoop()
#ifndef DEBUG_DUMP_COMMANDS
{
PerformanceMeasurer framerate(PFE_ALLSCRIPTS);
PerformanceMeasurer script_framerate(PFE_ALLSCRIPTS);
AI::GameLoop();
Game::GameLoop();
}
@@ -1432,14 +1504,35 @@ void StateGameLoop()
assert(IsLocalCompany());
}
/**
* Create an autosave. The default name is "autosave#.sav". However with
* the setting 'keep_all_autosave' the name defaults to company-name + date
*/
static void DoAutosave()
/** Interval for regular autosaves. Initialized at zero to disable till settings are loaded. */
static IntervalTimer<TimerGameRealtime> _autosave_interval({std::chrono::milliseconds::zero(), TimerGameRealtime::AUTOSAVE}, [](auto)
{
/* We reset the command-during-pause mode here, so we don't continue
* to make auto-saves when nothing more is changing. */
_pause_mode &= ~PM_COMMAND_DURING_PAUSE;
_do_autosave = true;
SetWindowDirty(WC_STATUS_BAR, 0);
static FiosNumberedSaveName _autosave_ctr("autosave");
DoAutoOrNetsave(_autosave_ctr);
_do_autosave = false;
SetWindowDirty(WC_STATUS_BAR, 0);
});
/**
* Reset the interval of the autosave.
*
* If reset is not set, this does not set the elapsed time on the timer,
* so if the interval is smaller, it might result in an autosave being done
* immediately.
*
* @param reset Whether to reset the timer back to zero, or to continue.
*/
void ChangeAutosaveFrequency(bool reset)
{
_autosave_interval.SetInterval({std::chrono::minutes(_settings_client.gui.autosave_interval), TimerGameRealtime::AUTOSAVE}, reset);
}
/**
@@ -1477,17 +1570,21 @@ void GameLoop()
ProcessAsyncSaveFinish();
/* autosave game? */
if (_do_autosave) {
DoAutosave();
_do_autosave = false;
SetWindowDirty(WC_STATUS_BAR, 0);
if (_game_mode == GM_NORMAL) {
static auto last_time = std::chrono::steady_clock::now();
auto now = std::chrono::steady_clock::now();
auto delta_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_time);
if (delta_ms.count() != 0) {
TimerManager<TimerGameRealtime>::Elapsed(delta_ms);
last_time = now;
}
}
/* switch game mode? */
if (_switch_mode != SM_NONE && !HasModalProgress()) {
SwitchToMode(_switch_mode);
_switch_mode = SM_NONE;
if (_exit_game) return;
}
IncreaseSpriteLRU();
@@ -1514,4 +1611,5 @@ void GameLoop()
SoundDriver::GetInstance()->MainLoop();
MusicLoop();
SocialIntegration::RunCallbacks();
}