Merge branch 'jgrpp' into jgrpp-beta

# Conflicts:
#	src/settings.cpp
#	src/settings_gui.cpp
#	src/settings_internal.h
#	src/table/company_settings.ini
#	src/table/currency_settings.ini
#	src/table/gameopt_settings.ini
#	src/table/misc_settings.ini
#	src/table/settings.h.preamble
#	src/table/settings.ini
#	src/table/win32_settings.ini
#	src/table/window_settings.ini
pull/332/head
Jonathan G Rennison 3 years ago
commit 4042480806

@ -1,2 +1,2 @@
jgrpp-0.43.0 20210912 0 9d18a8869f43c09ac0ca90d1067695872b0188b6 1 0 2021
c3b70543dc0c3b29485abf7b81a99d157e7e70bbe0fcf2fa30fd075f1eabd663 -
jgrpp-0.43.1 20211004 0 fe8da3ae3a190cff0a9dcc552fe0825ca28c5850 1 0 2021
fbeb6b45e2e75ab56dd72ed06410f90827a489b5cf6b3236f3605b51c5747bdc -

@ -1,4 +1,4 @@
## JGR's Patchpack version 0.43.0
## JGR's Patchpack version 0.43.1
This is a collection of patches applied to [OpenTTD](http://www.openttd.org/)

@ -2,6 +2,15 @@
* * *
### v0.43.1 (2021-10-04)
* Fix multi-aspect signal graphics not being immediately enabled for newly generated maps.
* Fix premature PBS reservations with using reverse at waypoint orders with timetabled wait times.
* Fix incorrect font heights when using custom fonts on MacOS.
* Fix crash when trying to place multitile objects at map edge.
* Routing restrictions:
* The reverse behind signal pathfinder now takes into account the train length to avoid reversing sidings which are too short.
* Add sort by maximum speed (fully loaded) to train list window.
### v0.43.0 (2021-09-12)
* Fix reversing a train inside a depot disrupting the PBS reservation of another train heading into the depot.
* Fix ships being drawn with the wrong image direction after rotating in place in some circumstances.

@ -62,6 +62,24 @@ struct SQRefCounted
SQUnsignedInteger _uiRef;
struct SQWeakRef *_weakref;
virtual void Release()=0;
/* Placement new/delete to prevent memory leaks if constructor throws an exception. */
inline void *operator new(size_t size, SQRefCounted *place)
{
place->size = size;
return place;
}
inline void operator delete(void *ptr, SQRefCounted *place)
{
SQ_FREE(ptr, place->size);
}
/* Never used but required. */
inline void operator delete(void *ptr) { NOT_REACHED(); }
private:
size_t size;
};
struct SQWeakRef : SQRefCounted

@ -118,6 +118,7 @@ add_files(
date_type.h
debug.cpp
debug.h
debug_desync.h
debug_settings.h
dedicated.cpp
departures.cpp
@ -156,6 +157,8 @@ add_files(
engine_type.h
error.h
error_gui.cpp
event_logs.cpp
event_logs.h
fileio.cpp
fileio_func.h
fileio_type.h

@ -30,6 +30,8 @@
#include "core/random_func.hpp"
#include "settings_func.h"
#include "signal_func.h"
#include "debug_settings.h"
#include "debug_desync.h"
#include <array>
#include "table/strings.h"
@ -554,7 +556,7 @@ struct CommandLogEntry {
};
struct CommandLog {
std::array<CommandLogEntry, 128> log;
std::array<CommandLogEntry, 256> log;
unsigned int count = 0;
unsigned int next = 0;
@ -619,7 +621,7 @@ static void DumpSubCommandLog(char *&buffer, const char *last, const CommandLog
char *DumpCommandLog(char *buffer, const char *last)
{
const unsigned int count = std::min<unsigned int>(_command_log.count, 128);
const unsigned int count = std::min<unsigned int>(_command_log.count, 256);
buffer += seprintf(buffer, last, "Command Log:\n Showing most recent %u of %u commands\n", count, _command_log.count);
DumpSubCommandLog(buffer, last, _command_log, count);
@ -889,6 +891,12 @@ bool DoCommandPEx(TileIndex tile, uint32 p1, uint32 p2, uint64 p3, uint32 cmd, C
if (!random_state.Check()) log_flags |= CLEF_RANDOM;
AppendCommandLogEntry(res, tile, p1, p2, p3, cmd, log_flags);
if (unlikely(HasChickenBit(DCBF_DESYNC_CHECK_POST_COMMAND)) && !(GetCommandFlags(cmd) & CMD_LOG_AUX)) {
CheckCachesFlags flags = CHECK_CACHE_ALL | CHECK_CACHE_EMIT_LOG;
if (HasChickenBit(DCBF_DESYNC_CHECK_NO_GENERAL)) flags &= ~CHECK_CACHE_GENERAL;
CheckCaches(true, nullptr, flags);
}
if (res.Failed()) {
/* Only show the error when it's for us. */
StringID error_part1 = GB(cmd, 16, 16);
@ -929,6 +937,12 @@ CommandCost DoCommandPScript(TileIndex tile, uint32 p1, uint32 p2, uint64 p3, ui
if (!random_state.Check()) log_flags |= CLEF_RANDOM;
AppendCommandLogEntry(res, tile, p1, p2, p3, cmd, log_flags);
if (unlikely(HasChickenBit(DCBF_DESYNC_CHECK_POST_COMMAND)) && !(GetCommandFlags(cmd) & CMD_LOG_AUX)) {
CheckCachesFlags flags = CHECK_CACHE_ALL | CHECK_CACHE_EMIT_LOG;
if (HasChickenBit(DCBF_DESYNC_CHECK_NO_GENERAL)) flags &= ~CHECK_CACHE_GENERAL;
CheckCaches(true, nullptr, flags);
}
return res;
}

@ -37,6 +37,7 @@
#include "zoning.h"
#include "tbtr_template_vehicle_func.h"
#include "widgets/statusbar_widget.h"
#include "debug_desync.h"
#include "table/strings.h"
@ -937,8 +938,7 @@ CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
InvalidateWindowData(WC_CLIENT_LIST, 0);
InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0);
extern void CheckCaches(bool force_check, std::function<void(const char *)> log);
CheckCaches(true, nullptr);
CheckCaches(true, nullptr, CHECK_CACHE_ALL | CHECK_CACHE_EMIT_LOG);
break;
}

@ -55,6 +55,9 @@
#include "base_media_base.h"
#include "debug_settings.h"
#include "walltime_func.h"
#include "debug_desync.h"
#include "scope_info.h"
#include "event_logs.h"
#include <time.h>
#include "safeguards.h"
@ -173,11 +176,24 @@ DEF_CONSOLE_HOOK(ConHookNoNetwork)
return CHR_ALLOW;
}
/**
* Check if are either in singleplayer or a server.
* @return True iff we are either in singleplayer or a server.
*/
DEF_CONSOLE_HOOK(ConHookServerOrNoNetwork)
{
if (_networking && !_network_server) {
if (echo) IConsoleError("This command is only available to a network server.");
return CHR_DISALLOW;
}
return CHR_ALLOW;
}
DEF_CONSOLE_HOOK(ConHookNewGRFDeveloperTool)
{
if (_settings_client.gui.newgrf_developer_tools) {
if (_game_mode == GM_MENU) {
if (echo) IConsoleError("This command is only available in game and editor.");
if (echo) IConsoleError("This command is only available in-game and in the editor.");
return CHR_DISALLOW;
}
return ConHookNoNetwork(echo);
@ -222,7 +238,7 @@ DEF_CONSOLE_CMD(ConResetEnginePool)
}
if (_game_mode == GM_MENU) {
IConsoleError("This command is only available in game and editor.");
IConsoleError("This command is only available in-game and in the editor.");
return true;
}
@ -683,6 +699,11 @@ DEF_CONSOLE_CMD(ConPauseGame)
return true;
}
if (_game_mode == GM_MENU) {
IConsoleError("This command is only available in-game and in the editor.");
return true;
}
if ((_pause_mode & PM_PAUSED_NORMAL) == PM_UNPAUSED) {
DoCommandP(0, PM_PAUSED_NORMAL, 1, CMD_PAUSE);
if (!_networking) IConsolePrint(CC_DEFAULT, "Game paused.");
@ -700,6 +721,11 @@ DEF_CONSOLE_CMD(ConUnpauseGame)
return true;
}
if (_game_mode == GM_MENU) {
IConsoleError("This command is only available in-game and in the editor.");
return true;
}
if ((_pause_mode & PM_PAUSED_NORMAL) != PM_UNPAUSED) {
DoCommandP(0, PM_PAUSED_NORMAL, 0, CMD_PAUSE);
if (!_networking) IConsolePrint(CC_DEFAULT, "Game unpaused.");
@ -2218,6 +2244,19 @@ DEF_CONSOLE_CMD(ConDumpCommandLog)
return true;
}
DEF_CONSOLE_CMD(ConDumpSpecialEventsLog)
{
if (argc == 0) {
IConsoleHelp("Dump log of special events.");
return true;
}
char buffer[32768];
DumpSpecialEventsLog(buffer, lastof(buffer));
PrintLineByLine(buffer);
return true;
}
DEF_CONSOLE_CMD(ConDumpDesyncMsgLog)
{
if (argc == 0) {
@ -2593,6 +2632,23 @@ DEF_CONSOLE_CMD(ConDumpCargoTypes)
return true;
}
DEF_CONSOLE_CMD(ConDumpVehicle)
{
if (argc != 2) {
IConsoleHelp("Debug: Show vehicle information. Usage: 'dump_vehicle <vehicle-id>'");
return true;
}
const Vehicle *v = Vehicle::GetIfValid(atoi(argv[1]));
if (v != nullptr) {
IConsolePrint(CC_DEFAULT, scope_dumper().VehicleInfo(v));
} else {
IConsolePrint(CC_DEFAULT, "No such vehicle");
}
return true;
}
/**
* Dump the state of a tile on the map.
* param x tile number or tile x coordinate.
@ -2648,7 +2704,7 @@ DEF_CONSOLE_CMD(ConDumpTile)
DEF_CONSOLE_CMD(ConCheckCaches)
{
if (argc == 0) {
IConsoleHelp("Debug: Check caches");
IConsoleHelp("Debug: Check caches. Usage: 'check_caches [<broadcast>]'");
return true;
}
@ -2658,8 +2714,7 @@ DEF_CONSOLE_CMD(ConCheckCaches)
if (broadcast) {
DoCommandP(0, 0, 0, CMD_DESYNC_CHECK);
} else {
extern void CheckCaches(bool force_check, std::function<void(const char *)> log);
CheckCaches(true, nullptr);
CheckCaches(true, nullptr, CHECK_CACHE_ALL | CHECK_CACHE_EMIT_LOG);
}
return true;
@ -3369,8 +3424,8 @@ void IConsoleStdLibRegister()
IConsole::CmdRegister("unban", ConUnBan, ConHookServerOnly);
IConsole::CmdRegister("banlist", ConBanList, ConHookServerOnly);
IConsole::CmdRegister("pause", ConPauseGame, ConHookServerOnly);
IConsole::CmdRegister("unpause", ConUnpauseGame, ConHookServerOnly);
IConsole::CmdRegister("pause", ConPauseGame, ConHookServerOrNoNetwork);
IConsole::CmdRegister("unpause", ConUnpauseGame, ConHookServerOrNoNetwork);
IConsole::CmdRegister("company_pw", ConCompanyPassword, ConHookNeedNetwork);
IConsole::AliasRegister("company_password", "company_pw %+");
@ -3422,6 +3477,7 @@ void IConsoleStdLibRegister()
IConsole::CmdRegister("getfulldate", ConGetFullDate, nullptr, true);
IConsole::CmdRegister("dump_command_log", ConDumpCommandLog, nullptr, true);
IConsole::CmdRegister("dump_special_events_log", ConDumpSpecialEventsLog, nullptr, true);
IConsole::CmdRegister("dump_desync_msgs", ConDumpDesyncMsgLog, nullptr, true);
IConsole::CmdRegister("dump_inflation", ConDumpInflation, nullptr, true);
IConsole::CmdRegister("dump_cpdp_stats", ConDumpCpdpStats, nullptr, true);
@ -3436,6 +3492,7 @@ void IConsoleStdLibRegister()
IConsole::CmdRegister("dump_rail_types", ConDumpRailTypes, nullptr, true);
IConsole::CmdRegister("dump_bridge_types", ConDumpBridgeTypes, nullptr, true);
IConsole::CmdRegister("dump_cargo_types", ConDumpCargoTypes, nullptr, true);
IConsole::CmdRegister("dump_vehicle", ConDumpVehicle, nullptr, true);
IConsole::CmdRegister("dump_tile", ConDumpTile, nullptr, true);
IConsole::CmdRegister("check_caches", ConCheckCaches, nullptr, true);
IConsole::CmdRegister("show_town_window", ConShowTownWindow, nullptr, true);

@ -30,6 +30,8 @@
#include "scope_info.h"
#include "command_func.h"
#include "thread.h"
#include "debug_desync.h"
#include "event_logs.h"
#include "ai/ai_info.hpp"
#include "game/game.hpp"
@ -408,6 +410,8 @@ char *CrashLog::LogCommandLog(char *buffer, const char *last) const
{
buffer = DumpCommandLog(buffer, last);
buffer += seprintf(buffer, last, "\n");
buffer = DumpSpecialEventsLog(buffer, last);
buffer += seprintf(buffer, last, "\n");
return buffer;
}
@ -520,7 +524,6 @@ char *CrashLog::FillDesyncCrashLog(char *buffer, const char *last, const DesyncE
buffer = DumpDesyncMsgLog(buffer, last);
bool have_cache_log = false;
extern void CheckCaches(bool force_check, std::function<void(const char *)> log);
CheckCaches(true, [&](const char *str) {
if (!have_cache_log) buffer += seprintf(buffer, last, "CheckCaches:\n");
buffer += seprintf(buffer, last, " %s\n", str);
@ -533,6 +536,62 @@ char *CrashLog::FillDesyncCrashLog(char *buffer, const char *last, const DesyncE
return buffer;
}
/**
* Fill the crash log buffer with all data of an inconsistency event.
* @param buffer The begin where to write at.
* @param last The last position in the buffer to write to.
* @return the position of the \c '\0' character after the buffer.
*/
char *CrashLog::FillInconsistencyLog(char *buffer, const char *last, const InconsistencyExtraInfo &info) const
{
time_t cur_time = time(nullptr);
buffer += seprintf(buffer, last, "*** OpenTTD Inconsistency Report ***\n\n");
buffer += seprintf(buffer, last, "Inconsistency at: %s", asctime(gmtime(&cur_time)));
#ifdef USE_SCOPE_INFO
buffer += WriteScopeLog(buffer, last);
#endif
buffer += seprintf(buffer, last, "In game date: %i-%02i-%02i (%i, %i) (DL: %u)\n", _cur_date_ymd.year, _cur_date_ymd.month + 1, _cur_date_ymd.day, _date_fract, _tick_skip_counter, _settings_game.economy.day_length_factor);
if (_game_load_time != 0) {
buffer += seprintf(buffer, last, "Game loaded at: %i-%02i-%02i (%i, %i), %s",
_game_load_cur_date_ymd.year, _game_load_cur_date_ymd.month + 1, _game_load_cur_date_ymd.day, _game_load_date_fract, _game_load_tick_skip_counter, asctime(gmtime(&_game_load_time)));
}
if (_networking && !_network_server) {
extern Date _last_sync_date;
extern DateFract _last_sync_date_fract;
extern uint8 _last_sync_tick_skip_counter;
YearMonthDay ymd;
ConvertDateToYMD(_last_sync_date, &ymd);
buffer += seprintf(buffer, last, "Last sync at: %i-%02i-%02i (%i, %i)",
ymd.year, ymd.month + 1, ymd.day, _last_sync_date_fract, _last_sync_tick_skip_counter);
}
buffer += seprintf(buffer, last, "\n");
buffer = this->LogOpenTTDVersion(buffer, last);
buffer = this->LogOSVersion(buffer, last);
buffer = this->LogCompiler(buffer, last);
buffer = this->LogOSVersionDetail(buffer, last);
buffer = this->LogConfiguration(buffer, last);
buffer = this->LogLibraries(buffer, last);
buffer = this->LogGamelog(buffer, last);
buffer = this->LogRecentNews(buffer, last);
buffer = this->LogCommandLog(buffer, last);
buffer = DumpDesyncMsgLog(buffer, last);
if (!info.check_caches_result.empty()) {
buffer += seprintf(buffer, last, "CheckCaches:\n");
for (const std::string &str : info.check_caches_result) {
buffer += seprintf(buffer, last, " %s\n", str.c_str());
}
}
buffer += seprintf(buffer, last, "*** End of OpenTTD Inconsistency Report ***\n");
return buffer;
}
/**
* Fill the version info log buffer.
* @param buffer The begin where to write at.
@ -756,6 +815,61 @@ bool CrashLog::MakeDesyncCrashLog(const std::string *log_in, std::string *log_ou
return ret;
}
/**
* Makes an inconsistency log, writes it to a file and then subsequently tries
* to make a crash savegame. It uses DEBUG to write
* information like paths to the console.
* @return true when everything is made successfully.
*/
bool CrashLog::MakeInconsistencyLog(const InconsistencyExtraInfo &info) const
{
char filename[MAX_PATH];
char buffer[65536 * 2];
bool ret = true;
char name_buffer[64];
char *name_buffer_date = name_buffer + seprintf(name_buffer, lastof(name_buffer), "inconsistency-");
time_t cur_time = time(nullptr);
strftime(name_buffer_date, lastof(name_buffer) - name_buffer_date, "%Y%m%dT%H%M%SZ", gmtime(&cur_time));
printf("Inconsistency encountered, generating diagnostics log...\n");
this->FillInconsistencyLog(buffer, lastof(buffer), info);
bool bret = this->WriteCrashLog(buffer, filename, lastof(filename), name_buffer);
if (bret) {
printf("Inconsistency log written to %s. Please add this file to any bug reports.\n\n", filename);
} else {
printf("Writing inconsistency log failed.\n\n");
ret = false;
}
_savegame_DBGL_data = buffer;
_save_DBGC_data = true;
bret = this->WriteSavegame(filename, lastof(filename), name_buffer);
if (bret) {
printf("info savegame written to %s. Please add this file and the last (auto)save to any bug reports.\n\n", filename);
} else {
ret = false;
printf("Writing inconsistency savegame failed. Please attach the last (auto)save to any bug reports.\n\n");
}
_savegame_DBGL_data = nullptr;
_save_DBGC_data = false;
if (!(_screen.width < 1 || _screen.height < 1 || _screen.dst_ptr == nullptr)) {
SetScreenshotAuxiliaryText("Inconsistency Log", buffer);
bret = this->WriteScreenshot(filename, lastof(filename), name_buffer);
if (bret) {
printf("Inconsistency screenshot written to %s. Please add this file to any bug reports.\n\n", filename);
} else {
ret = false;
printf("Writing inconsistency screenshot failed.\n\n");
}
ClearScreenshotAuxiliaryText();
}
return ret;
}
/**
* Makes a version info log, writes it to a file. It uses DEBUG to write
* information like paths to the console.

@ -12,6 +12,7 @@
#include "core/enum_type.hpp"
#include <string>
#include <vector>
struct DesyncExtraInfo {
enum Flags {
@ -27,6 +28,10 @@ struct DesyncExtraInfo {
};
DECLARE_ENUM_AS_BIT_SET(DesyncExtraInfo::Flags)
struct InconsistencyExtraInfo {
std::vector<std::string> check_caches_result;
};
/**
* Helper class for creating crash logs.
*/
@ -130,6 +135,7 @@ public:
char *FillCrashLog(char *buffer, const char *last) const;
char *FillDesyncCrashLog(char *buffer, const char *last, const DesyncExtraInfo &info) const;
char *FillInconsistencyLog(char *buffer, const char *last, const InconsistencyExtraInfo &info) const;
char *FillVersionInfoLog(char *buffer, const char *last) const;
bool WriteCrashLog(const char *buffer, char *filename, const char *filename_last, const char *name = "crash", FILE **crashlog_file = nullptr) const;
@ -148,6 +154,7 @@ public:
bool MakeCrashLog();
bool MakeDesyncCrashLog(const std::string *log_in, std::string *log_out, const DesyncExtraInfo &info) const;
bool MakeInconsistencyLog(const InconsistencyExtraInfo &info) const;
bool MakeVersionInfoLog() const;
bool MakeCrashSavegameAndScreenshot() const;
@ -165,6 +172,7 @@ public:
static void InitThread();
static void DesyncCrashLog(const std::string *log_in, std::string *log_out, const DesyncExtraInfo &info);
static void InconsistencyLog(const InconsistencyExtraInfo &info);
static void VersionInfoLog();
static void SetErrorMessage(const char *message);

@ -350,7 +350,7 @@ struct DesyncMsgLogEntry {
};
struct DesyncMsgLog {
std::array<DesyncMsgLogEntry, 128> log;
std::array<DesyncMsgLogEntry, 256> log;
unsigned int count = 0;
unsigned int next = 0;

@ -0,0 +1,26 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file debug_desync.h Desync debugging. */
#ifndef DEBUG_DESYNC_H
#define DEBUG_DESYNC_H
#include <functional>
enum CheckCachesFlags : uint32 {
CHECK_CACHE_NONE = 0,
CHECK_CACHE_GENERAL = 1 << 0,
CHECK_CACHE_INFRA_TOTALS = 1 << 1,
CHECK_CACHE_ALL = UINT16_MAX,
CHECK_CACHE_EMIT_LOG = 1 << 16,
};
DECLARE_ENUM_AS_BIT_SET(CheckCachesFlags)
extern void CheckCaches(bool force_check, std::function<void(const char *)> log = nullptr, CheckCachesFlags flags = CHECK_CACHE_ALL);
#endif /* DEBUG_DESYNC_H */

@ -16,6 +16,9 @@
enum ChickenBitFlags {
DCBF_VEH_TICK_CACHE = 0,
DCBF_MP_NO_STATE_CSUM_CHECK = 1,
DCBF_DESYNC_CHECK_PERIODIC = 2,
DCBF_DESYNC_CHECK_POST_COMMAND = 3,
DCBF_DESYNC_CHECK_NO_GENERAL = 4,
};
inline bool HasChickenBit(ChickenBitFlags flag)

@ -46,6 +46,7 @@
#include "core/random_func.hpp"
#include "core/backup_type.hpp"
#include "core/checksum_func.hpp"
#include "event_logs.h"
#include "table/strings.h"

@ -54,6 +54,8 @@
#include "tbtr_template_vehicle_func.h"
#include "scope_info.h"
#include "pathfinder/yapf/yapf_cache.h"
#include "debug_desync.h"
#include "event_logs.h"
#include "table/strings.h"
#include "table/pricebase.h"
@ -588,6 +590,12 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner)
cur_company.Restore();
if (new_owner != INVALID_OWNER) {
AppendSpecialEventsLogEntry(stdstr_fmt("Company merge: old: %u, new %u", old_owner, new_owner));
} else {
AppendSpecialEventsLogEntry(stdstr_fmt("Company deletion: old: %u", old_owner));
}
RegisterGameEvents(new_owner != INVALID_OWNER ? GEF_COMPANY_MERGE : GEF_COMPANY_DELETE);
MarkWholeScreenDirty();
@ -2287,8 +2295,7 @@ static void DoAcquireCompany(Company *c)
delete c;
extern void CheckCaches(bool force_check, std::function<void(const char *)> log);
CheckCaches(true, nullptr);
CheckCaches(true, nullptr, CHECK_CACHE_ALL | CHECK_CACHE_EMIT_LOG);
}
extern int GetAmountOwnedBy(const Company *c, Owner owner);

@ -0,0 +1,104 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file event_logs.cpp Functions related to event logging. */
#include "stdafx.h"
#include "event_logs.h"
#include "string_func.h"
#include "date_func.h"
#include "company_func.h"
#include <array>
#include <string>
#include "safeguards.h"
GameEventFlags _game_events_since_load;
GameEventFlags _game_events_overall;
time_t _game_load_time;
char *DumpGameEventFlags(GameEventFlags events, char *b, const char *last)
{
if (b <= last) *b = 0;
auto dump = [&](char c, GameEventFlags ev) {
if (events & ev) b += seprintf(b, last, "%c", c);
};
dump('d', GEF_COMPANY_DELETE);
dump('m', GEF_COMPANY_MERGE);
dump('n', GEF_RELOAD_NEWGRF);
dump('t', GEF_TBTR_REPLACEMENT);
dump('D', GEF_DISASTER_VEH);
dump('c', GEF_TRAIN_CRASH);
dump('i', GEF_INDUSTRY_CREATE);
dump('j', GEF_INDUSTRY_DELETE);
dump('v', GEF_VIRT_TRAIN);
return b;
}
struct SpecialEventLogEntry {
std::string msg;
Date date;
DateFract date_fract;
uint8 tick_skip_counter;
CompanyID current_company;
CompanyID local_company;
SpecialEventLogEntry() { }
SpecialEventLogEntry(std::string msg)
: msg(std::move(msg)), date(_date), date_fract(_date_fract), tick_skip_counter(_tick_skip_counter),
current_company(_current_company), local_company(_local_company) { }
};
struct SpecialEventLog {
std::array<SpecialEventLogEntry, 64> log;
unsigned int count = 0;
unsigned int next = 0;
void Reset()
{
this->count = 0;
this->next = 0;
}
};
static SpecialEventLog _special_event_log;
void AppendSpecialEventsLogEntry(std::string message)
{
_special_event_log.log[_special_event_log.next] = SpecialEventLogEntry(std::move(message));
_special_event_log.next = (_special_event_log.next + 1) % _special_event_log.log.size();
_special_event_log.count++;
}
char *DumpSpecialEventsLog(char *buffer, const char *last)
{
const unsigned int count = std::min<unsigned int>(_special_event_log.count, 64);
buffer += seprintf(buffer, last, "Special Events Log:\n Showing most recent %u of %u events\n", count, _special_event_log.count);
unsigned int log_index = _special_event_log.next;
for (unsigned int i = 0 ; i < count; i++) {
if (log_index > 0) {
log_index--;
} else {
log_index = (uint)_special_event_log.log.size() - 1;
}
const SpecialEventLogEntry &entry = _special_event_log.log[log_index];
YearMonthDay ymd;
ConvertDateToYMD(entry.date, &ymd);
buffer += seprintf(buffer, last, " %3u | %4i-%02i-%02i, %2i, %3i | cc: %3u, lc: %3u | %s\n",
i, ymd.year, ymd.month + 1, ymd.day, entry.date_fract, entry.tick_skip_counter, (uint) entry.current_company, (uint) entry.local_company, entry.msg.c_str());
}
return buffer;
}
void ClearSpecialEventsLog()
{
_special_event_log.Reset();
}

@ -0,0 +1,46 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file event_logs.h Functions related to event logging. */
#ifndef EVENT_LOGS_H
#define EVENT_LOGS_H
#include "core/enum_type.hpp"
#include <time.h>
enum GameEventFlags : uint32 {
GEF_COMPANY_DELETE = 1 << 0, ///< (d) A company has been deleted
GEF_COMPANY_MERGE = 1 << 1, ///< (m) A company has been bought by another
GEF_RELOAD_NEWGRF = 1 << 2, ///< (n) ReloadNewGRFData() has been called
GEF_TBTR_REPLACEMENT = 1 << 3, ///< (t) CMD_TEMPLATE_REPLACE_VEHICLE has been called
GEF_DISASTER_VEH = 1 << 4, ///< (D) A disaster vehicle exists or has been created
GEF_TRAIN_CRASH = 1 << 5, ///< (c) A train crash has occurred
GEF_INDUSTRY_CREATE = 1 << 6, ///< (i) An industry has been created (in game)
GEF_INDUSTRY_DELETE = 1 << 7, ///< (j) An industry has been deleted (in game)
GEF_VIRT_TRAIN = 1 << 8, ///< (v) A virtual train has been created
};
DECLARE_ENUM_AS_BIT_SET(GameEventFlags)
extern GameEventFlags _game_events_since_load;
extern GameEventFlags _game_events_overall;
inline void RegisterGameEvents(GameEventFlags events)
{
_game_events_since_load |= events;
_game_events_overall |= events;
}
char *DumpGameEventFlags(GameEventFlags events, char *b, const char *last);
extern time_t _game_load_time;
void AppendSpecialEventsLogEntry(std::string message);
char *DumpSpecialEventsLog(char *buffer, const char *last);
void ClearSpecialEventsLog();
#endif /* EVENT_LOGS_H */

@ -79,10 +79,22 @@ static bool IsValidSearchPath(Searchpath sp)
return sp < _searchpaths.size() && !_searchpaths[sp].empty();
}
static void FillValidSearchPaths()
static void FillValidSearchPaths(bool only_local_path)
{
_valid_searchpaths.clear();
for (Searchpath sp = SP_FIRST_DIR; sp < NUM_SEARCHPATHS; sp++) {
if (only_local_path) {
switch (sp) {
case SP_WORKING_DIR: // Can be influence by "-c" option.
case SP_BINARY_DIR: // Most likely contains all the language files.
case SP_AUTODOWNLOAD_DIR: // Otherwise we cannot download in-game content.
break;
default:
continue;
}
}
if (IsValidSearchPath(sp)) _valid_searchpaths.emplace_back(sp);
}
}
@ -535,9 +547,6 @@ bool TarScanner::AddFile(const std::string &filename, size_t basepath_length, co
switch (th.typeflag) {
case '\0':
case '0': { // regular file
/* Ignore empty files */
if (skip == 0) break;
if (strlen(name) == 0) break;
/* Store this entry in the list */
@ -958,11 +967,12 @@ std::string _personal_dir;
* fill all other paths (save dir, autosave dir etc) and
* make the save and scenario directories.
* @param exe the path from the current path to the executable
* @param only_local_path Whether we shouldn't fill searchpaths with global folders.
*/
void DeterminePaths(const char *exe)
void DeterminePaths(const char *exe, bool only_local_path)
{
DetermineBasePaths(exe);
FillValidSearchPaths();
FillValidSearchPaths(only_local_path);
#ifdef USE_XDG
std::string config_home;
@ -1029,6 +1039,13 @@ void DeterminePaths(const char *exe)
/* We are using the XDG configuration home for the config file,
* then store the rest in the XDG data home folder. */
_personal_dir = _searchpaths[SP_PERSONAL_DIR_XDG];
if (only_local_path) {
/* In case of XDG and we only want local paths and we detected that
* the user either manually indicated the XDG path or didn't use
* "-c" option, we change the working-dir to the XDG personal-dir,
* as this is most likely what the user is expecting. */
_searchpaths[SP_WORKING_DIR] = _searchpaths[SP_PERSONAL_DIR_XDG];
}
} else
#endif
{
@ -1053,8 +1070,9 @@ void DeterminePaths(const char *exe)
/* If we have network we make a directory for the autodownloading of content */
_searchpaths[SP_AUTODOWNLOAD_DIR] = _personal_dir + "content_download" PATHSEP;
DEBUG(misc, 4, "%s added as search path", _searchpaths[SP_AUTODOWNLOAD_DIR].c_str());
FioCreateDirectory(_searchpaths[SP_AUTODOWNLOAD_DIR]);
FillValidSearchPaths();
FillValidSearchPaths(only_local_path);
/* Create the directory for each of the types of content */
const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR };

@ -27,7 +27,7 @@ const char *FiosGetScreenshotDir();
void SanitizeFilename(char *filename);
void AppendPathSeparator(std::string &buf);
void DeterminePaths(const char *exe);
void DeterminePaths(const char *exe, bool only_local_path);
std::unique_ptr<char[]> ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize);
bool FileExists(const std::string &filename);
bool ExtractTar(const std::string &tar_filename, Subdirectory subdir);

@ -48,7 +48,7 @@ bool _right_button_clicked; ///< Is right mouse button clicked?
DrawPixelInfo _screen;
bool _screen_disable_anim = false; ///< Disable palette animation (important for 32bpp-anim blitter during giant screenshot)
bool _check_special_modes;
bool _exit_game;
std::atomic<bool> _exit_game;
GameMode _game_mode;
SwitchMode _switch_mode; ///< The next mainloop command.
PauseMode _pause_mode;

@ -731,8 +731,7 @@ public:
return;
case WID_GL_SORT_BY_DROPDOWN: // Select sorting criteria dropdown menu
ShowDropDownMenu(this, this->GetVehicleSorterNames(), this->vehgroups.SortType(), WID_GL_SORT_BY_DROPDOWN, 0,
(this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : this->vehicle_sorter_non_ground_veh_disable_mask);
ShowDropDownMenu(this, this->GetVehicleSorterNames(), this->vehgroups.SortType(), WID_GL_SORT_BY_DROPDOWN, 0, this->GetSorterDisableMask(this->vli.vtype));
return;
case WID_GL_FILTER_BY_CARGO: // Select filtering criteria dropdown menu

@ -22,6 +22,40 @@
#include "safeguards.h"
/**
* Maximum number of pixels for one dimension of a heightmap image.
* Do not allow images for which the longest side is twice the maximum number of
* tiles along the longest side of the (tile) map.
*/
static const uint MAX_HEIGHTMAP_SIDE_LENGTH_IN_PIXELS = 2 * (1 << 16);
/*
* Maximum size in pixels of the heightmap image.
*/
static const uint MAX_HEIGHTMAP_SIZE_PIXELS = 256 << 20; // ~256 million
/*
* When loading a PNG or BMP the 24 bpp variant requires at least 4 bytes per pixel
* of memory to load the data. Make sure the "reasonable" limit is well within the
* maximum amount of memory allocatable on 32 bit platforms.
*/
static_assert(MAX_HEIGHTMAP_SIZE_PIXELS < UINT32_MAX / 8);
/**
* Check whether the loaded dimension of the heightmap image are considered valid enough
* to attempt to load the image. In other words, the width and height are not beyond the
* #MAX_HEIGHTMAP_SIDE_LENGTH_IN_PIXELS limit and the total number of pixels does not
* exceed #MAX_HEIGHTMAP_SIZE_PIXELS. A width or height less than 1 are disallowed too.
* @param width The width of the to be loaded height map.
* @param height The height of the to be loaded height map.
* @return True iff the dimensions are within the limits.
*/
static inline bool IsValidHeightmapDimension(size_t width, size_t height)
{
return (uint64)width * height <= MAX_HEIGHTMAP_SIZE_PIXELS &&
width > 0 && width <= MAX_HEIGHTMAP_SIDE_LENGTH_IN_PIXELS &&
height > 0 && height <= MAX_HEIGHTMAP_SIDE_LENGTH_IN_PIXELS;
}
/**
* Convert RGB colours to Grayscale using 29.9% Red, 58.7% Green, 11.4% Blue
* (average luminosity formula, NTSC Colour Space)
@ -146,8 +180,7 @@ static bool ReadHeightmapPNG(const char *filename, uint *x, uint *y, byte **map)
uint width = png_get_image_width(png_ptr, info_ptr);
uint height = png_get_image_height(png_ptr, info_ptr);
/* Check if image dimensions don't overflow a size_t to avoid memory corruption. */
if ((uint64)width * height >= (size_t)-1) {
if (!IsValidHeightmapDimension(width, height)) {
ShowErrorMessage(STR_ERROR_PNGMAP, STR_ERROR_HEIGHTMAP_TOO_LARGE, WL_ERROR);
fclose(fp);
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
@ -255,8 +288,7 @@ static bool ReadHeightmapBMP(const char *filename, uint *x, uint *y, byte **map)
return false;
}
/* Check if image dimensions don't overflow a size_t to avoid memory corruption. */
if ((uint64)info.width * info.height >= (size_t)-1 / (info.bpp == 24 ? 3 : 1)) {
if (!IsValidHeightmapDimension(info.width, info.height)) {
ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_HEIGHTMAP_TOO_LARGE, WL_ERROR);
fclose(f);
BmpDestroyData(&data);
@ -295,6 +327,8 @@ static void GrayscaleToMapHeights(uint img_width, uint img_height, byte *map)
{
/* Defines the detail of the aspect ratio (to avoid doubles) */
const uint num_div = 16384;
/* Ensure multiplication with num_div does not cause overflows. */
static_assert(num_div <= std::numeric_limits<uint>::max() / MAX_HEIGHTMAP_SIDE_LENGTH_IN_PIXELS);
uint width, height;
uint row, col;

@ -43,6 +43,7 @@
#include "error.h"
#include "cmd_helper.h"
#include "string_func.h"
#include "event_logs.h"
#include "table/strings.h"
#include "table/industry_land.h"

@ -769,6 +769,7 @@ public:
const IndustrySpec *indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? nullptr : GetIndustrySpec(this->selected_type);
if (indsp == nullptr) this->enabled[this->selected_index] = _settings_game.difficulty.industry_density != ID_FUND_ONLY;
this->SetButtons();
this->SetDirty();
}
};
@ -2021,11 +2022,7 @@ struct CargoesField {
assert(this->type == CFT_CARGO);
int n = this->u.cargo.num_cargoes;
if (n % 2 == 0) {
return xpos + cargo_field_width / 2 - (CargoesField::cargo_line.width + CargoesField::cargo_space.width / 2) * (n / 2);
} else {
return xpos + cargo_field_width / 2 - CargoesField::cargo_line.width / 2 - (CargoesField::cargo_line.width + CargoesField::cargo_space.width) * (n / 2);
}
return xpos + cargo_field_width / 2 - (CargoesField::cargo_line.width * n + CargoesField::cargo_space.width * (n - 1)) / 2;
}
/**

@ -306,6 +306,7 @@ STR_SORT_BY_LENGTH :Length
STR_SORT_BY_LIFE_TIME :Remaining lifetime
STR_SORT_BY_TIMETABLE_DELAY :Timetable delay
STR_SORT_BY_AVG_ORDER_OCCUPANCY :Average order occupancy
STR_SORT_BY_MAX_SPEED_LOADED :Maximum speed (fully loaded)
STR_SORT_BY_FACILITY :Station type
STR_SORT_BY_WAITING_TOTAL :Total waiting cargo
STR_SORT_BY_WAITING_AVAILABLE :Available waiting cargo
@ -2060,6 +2061,7 @@ STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO :Distribution mo
STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO_PARAM :Distribution mode override for {STRING}: {STRING2}
STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO_HELPTEXT :"(default)" means that the distribution mode is the default for the class of this cargo. "symmetric" means that roughly the same number of cargo will go from a station A to a station B as from B to A. "asymmetric" means that arbitrary amounts of cargo can be sent in either direction. "manual" means that no automatic distribution will take place for this cargo.
STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO_DEFAULT :(default)
STR_CONFIG_SETTING_DISTRIBUTION_HELPTEXT_EXTRA :{STRING}{}"asymmetric (equal distribution)" means that cargo will be distributed such that each accepting station receives approximately the same amount of cargo in total. "asymmetric (nearest)" means that cargo is sent to whichever accepting station is nearest.
STR_CONFIG_SETTING_LINKGRAPH_ACCURACY :Distribution accuracy: {STRING2}
STR_CONFIG_SETTING_LINKGRAPH_ACCURACY_HELPTEXT :The higher you set this the more CPU time the calculation of the link graph will take. If it takes too long you may notice lag. If you set it to a low value, however, the distribution will be inaccurate, and you may notice cargo not being sent to the places you expect it to go.
STR_CONFIG_SETTING_DEMAND_DISTANCE :Effect of distance on demands: {STRING2}

@ -1418,7 +1418,7 @@ STR_CONFIG_SETTING_SHOW_ADV_TRACE_RESTRICT_FEATURES :Zeige erweitert
STR_CONFIG_SETTING_SHOW_ADV_TRACE_RESTRICT_FEATURES_HELPTEXT :Zeige die erweiterten Features der Routing-Beschränkungen. Wenn inaktiv, werden einige erweiterte Features zwar nicht in der Oberfläche angezeigt, stehen aber dennoch allen Spielern zur Verfügung.
STR_CONFIG_SETTING_SHOW_PROGSIG_FEATURES :Zeige programmierbares Einfahrtssignal-Feature: {STRING}
STR_CONFIG_SETTING_SHOW_PROGSIG_FEATURES_HELPTEXT :Zeige das programmierbares Einfahrtssignal-Feature. Falls deaktiviert, werden die Bedienelemente zum Bau und zur Konfiguration von programmierbaren Einfahrtssignalen zwar nicht in der Oberfläche angezeigt, stehen aber dennoch allen Spielern zur Verfügung.
STR_CONFIG_SETTING_SHOW_VEH_LIST_CARGO_FILTER :Zeige Filterung nach Frachart in Fahrzeuglisten: {STRING}
STR_CONFIG_SETTING_SHOW_VEH_LIST_CARGO_FILTER :Zeige Filterung nach Frachtart in Fahrzeuglisten: {STRING}
STR_CONFIG_SETTING_SHOW_VEH_LIST_CARGO_FILTER_HELPTEXT :Zeige die Filterung nach Frachtart in Fahrzeuglisten. Falls aktiviert, beinhalten die Fahrzeuglisten-Fenster eine weitere Filteroption.
STR_CONFIG_SETTING_SHOW_ADV_LOADING_MODE_FEATURES :Zeige erweiterter Lademodus-Features: {STRING}
STR_CONFIG_SETTING_SHOW_ADV_LOADING_MODE_FEATURES_HELPTEXT :Zeige erweiterter Lademodus-Features (durchgehendes Laden/Entladen). Falls deaktiviert, werden einige erweiterte Lademodus-Features zwar nicht in der Oberfläche angezeigt, stehen aber dennoch allen Spielern zur Verfügung.
@ -1651,14 +1651,14 @@ STR_CONFIG_SETTING_EXPENSES_LAYOUT_HELPTEXT :Lege das Layout
STR_CONFIG_SETTING_AUTO_REMOVE_SIGNALS :Automatische Entfernung von Signalen während der Errichtung von Bahntrassen: {STRING}
STR_CONFIG_SETTING_AUTO_REMOVE_SIGNALS_HELPTEXT :Automatische Entfernung von Signalen während der Errichtung der Bahntrasse, wenn diese sich im Weg befinden. Hinweis: Dies kann zu Unfällen führen!
STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT :Zeitraffer Geschwindigkeitslimit: {STRING}
STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_HELPTEXT :Limit wie schnell das Spiel läuft, wenn Zeitraffer aktiviert ist. 0= unlimitiert (so schnell, wie ihr Computer es erlaubt). Eingaben unter 100% können das Spiel verlangsamen. Das obere Limit hängt von den technischen Spezifikationen ihres Computer ab und kann großen Einfluss auf das Spiel haben
STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_HELPTEXT :Limit wie schnell das Spiel läuft, wenn Zeitraffer aktiviert ist. 0= unlimitiert (so schnell, wie Ihr Computer es erlaubt). Eingaben unter 100% können das Spiel verlangsamen. Das obere Limit hängt von den technischen Spezifikationen Ihres Computers ab und kann großen Einfluss auf das Spiel haben
STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_VAL :{NUM}% normale Spielgeschwindigkeit
STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_ZERO :Keine Beschränkung (so schnell wie ihr Computer es erlaubt)
STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_ZERO :Keine Beschränkung (so schnell wie Ihr Computer es erlaubt)
STR_CONFIG_SETTING_ENABLE_BUILD_RIVER :Aktiviere das Bauen von Flüssen: {STRING}
STR_CONFIG_SETTING_ENABLE_BUILD_RIVER_HELPTEXT :Aktiviere das Bauen von Flüssen außerhalb des Szenarioeditors
STR_CONFIG_SETTING_ENABLE_REMOVE_WATER :Aktiviere das entfernen von Meereskacheln und Flüssen: {STRING}
STR_CONFIG_SETTING_ENABLE_REMOVE_WATER_HELPTEXT :Aktiviere das entfernen von Meereskacheln und Flüssen außerhalb des Szenarioeditors
STR_CONFIG_SETTING_ENABLE_REMOVE_WATER :Aktiviere das Entfernen von Meereskacheln und Flüssen: {STRING}
STR_CONFIG_SETTING_ENABLE_REMOVE_WATER_HELPTEXT :Aktiviere das Entfernen von Meereskacheln und Flüssen außerhalb des Szenarioeditors
STR_CONFIG_SETTING_SOUND_TICKER :Nachrichtenticker: {STRING}
STR_CONFIG_SETTING_SOUND_TICKER_HELPTEXT :Soundeffekte für Kurzfassungen von Nachrichten abspielen (Ticker)

@ -38,6 +38,7 @@
#include "zoning.h"
#include "cargopacket.h"
#include "tbtr_template_vehicle_func.h"
#include "event_logs.h"
#include "safeguards.h"
@ -78,6 +79,7 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin
ViewportMapClearTunnelCache();
ClearCommandLog();
ClearSpecialEventsLog();
ClearDesyncMsgLog();
_pause_mode = PM_UNPAUSED;

@ -2361,7 +2361,7 @@ public:
if (this->hover_index >= 0) {
uint offset = this->hover_index * this->line_height;
GfxFillRect(r.left + 2, r.top + offset, r.right - 1, r.top + offset + this->line_height - 1, GREY_SCALE(9));
GfxFillRect(r.left + 2, r.top + offset, r.right - 1, r.top + offset + this->line_height - 2, GREY_SCALE(9));
}
NetworkClientInfo *own_ci = NetworkClientInfo::GetByClientID(_network_own_client_id);

@ -5393,6 +5393,11 @@ static void NewSpriteGroup(ByteReader *buf)
return;
}
if (num_loaded + num_loading == 0) {
grfmsg(1, "NewSpriteGroup: no result, skipping invalid RealSpriteGroup");
break;
}
assert(RealSpriteGroup::CanAllocateItem());
RealSpriteGroup *group = new RealSpriteGroup();
group->nfo_line = _cur.nfo_line;

@ -32,6 +32,7 @@
#include "date_func.h"
#include "newgrf_debug.h"
#include "vehicle_func.h"
#include "station_func.h"
#include "table/strings.h"
#include "table/object_land.h"
@ -122,7 +123,9 @@ void BuildObject(ObjectType type, TileIndex tile, CompanyID owner, Town *town, u
Company::Get(owner)->infrastructure.water++;
DirtyCompanyInfrastructureWindows(owner);
}
bool remove = IsDockingTile(t);
MakeObject(t, owner, o->index, wc, Random());
if (remove) RemoveDockingTile(t);
MarkTileDirtyByTile(t, VMDF_NOT_MAP_MODE);
}

@ -78,6 +78,9 @@
#include "cargopacket.h"
#include "core/checksum_func.hpp"
#include "tbtr_template_vehicle_func.h"
#include "debug_settings.h"
#include "debug_desync.h"
#include "event_logs.h"
#include "linkgraph/linkgraphschedule.h"
#include "tracerestrict.h"
@ -112,11 +115,6 @@ bool _save_config = false;
bool _request_newgrf_scan = false;
NewGRFScanCallback *_request_newgrf_scan_callback = nullptr;
GameEventFlags _game_events_since_load;
GameEventFlags _game_events_overall;
time_t _game_load_time;
SimpleChecksum64 _state_checksum;
/**
@ -251,6 +249,7 @@ static void ShowHelp()
" -M music_set = Force the music set (see below)\n"
" -c config_file = Use 'config_file' instead of 'openttd.cfg'\n"
" -x = Never save configuration changes to disk\n"
" -X = Don't use global folders to search for files\n"
" -q savegame = Write some information about the savegame and exit\n"
" -Z = Write detailed version information and exit\n"
"\n",
@ -459,6 +458,7 @@ static void ShutdownGame()
ClearVehicleTickCaches();
InvalidateTemplateReplacementImages();
ClearCommandLog();
ClearSpecialEventsLog();
ClearDesyncMsgLog();
_loaded_local_company = COMPANY_SPECTATOR;
@ -659,6 +659,7 @@ static const OptionData _options[] = {
GETOPT_SHORT_VALUE('G'),
GETOPT_SHORT_VALUE('c'),
GETOPT_SHORT_NOVAL('x'),
GETOPT_SHORT_NOVAL('X'),
GETOPT_SHORT_VALUE('q'),
GETOPT_SHORT_VALUE('K'),
GETOPT_SHORT_NOVAL('h'),
@ -688,6 +689,7 @@ int openttd_main(int argc, char *argv[])
std::unique_ptr<AfterNewGRFScan> scanner(new AfterNewGRFScan());
bool dedicated = false;
char *debuglog_conn = nullptr;
bool only_local_path = false;
extern bool _dedicated_forks;
_dedicated_forks = false;
@ -767,7 +769,7 @@ int openttd_main(int argc, char *argv[])
break;
case 'q':
case 'K': {
DeterminePaths(argv[0]);
DeterminePaths(argv[0], only_local_path);
if (StrEmpty(mgo.opt)) {
ret = 1;
return ret;
@ -808,6 +810,7 @@ int openttd_main(int argc, char *argv[])
CrashLog::VersionInfoLog();
return ret;
}
case 'X': only_local_path = true; break;
case 'h':
i = -2; // Force printing of help.
break;
@ -821,7 +824,7 @@ int openttd_main(int argc, char *argv[])
*
* The next two functions are needed to list the graphics sets. We can't do them earlier
* because then we cannot show it on the debug console as that hasn't been configured yet. */
DeterminePaths(argv[0]);
DeterminePaths(argv[0], only_local_path);
TarScanner::DoScan(TarScanner::BASESET);
BaseGraphics::FindSets();
BaseSounds::FindSets();
@ -830,7 +833,7 @@ int openttd_main(int argc, char *argv[])
return ret;
}
DeterminePaths(argv[0]);
DeterminePaths(argv[0], only_local_path);
TarScanner::DoScan(TarScanner::BASESET);
if (dedicated) DEBUG(net, 3, "Starting dedicated server, version %s", _openttd_revision);
@ -1336,14 +1339,28 @@ void WriteVehicleInfo(char *&p, const char *last, const Vehicle *u, const Vehicl
* the cached value and what the value would
* be when calculated from the 'base' data.
*/
void CheckCaches(bool force_check, std::function<void(const char *)> log)
void CheckCaches(bool force_check, std::function<void(const char *)> log, CheckCachesFlags flags)
{
if (!force_check) {
int desync_level = _debug_desync_level;
if (unlikely(HasChickenBit(DCBF_DESYNC_CHECK_PERIODIC)) && desync_level < 1) {
desync_level = 1;
if (HasChickenBit(DCBF_DESYNC_CHECK_NO_GENERAL)) flags &= ~CHECK_CACHE_GENERAL;
}
/* Return here so it is easy to add checks that are run
* always to aid testing of caches. */
if (_debug_desync_level < 1) return;
if (desync_level < 1) return;
if (_debug_desync_level == 1 && _scaled_date_ticks % 500 != 0) return;
if (desync_level == 1 && _scaled_date_ticks % 500 != 0) return;
}
std::vector<std::string> saved_messages;
if (flags & CHECK_CACHE_EMIT_LOG) {
log = [&saved_messages](const char *str) {
saved_messages.emplace_back(str);
};
}
char cclog_buffer[1024];
@ -1372,362 +1389,377 @@ void CheckCaches(bool force_check, std::function<void(const char *)> log)
} \
}
/* Check the town caches. */
std::vector<TownCache> old_town_caches;
std::vector<StationList> old_town_stations_nears;
for (const Town *t : Town::Iterate()) {
old_town_caches.push_back(t->cache);
old_town_stations_nears.push_back(t->stations_near);
}
if (flags & CHECK_CACHE_GENERAL) {
/* Check the town caches. */
std::vector<TownCache> old_town_caches;
std::vector<StationList> old_town_stations_nears;
for (const Town *t : Town::Iterate()) {
old_town_caches.push_back(t->cache);
old_town_stations_nears.push_back(t->stations_near);
}
std::vector<IndustryList> old_station_industries_nears;
std::vector<BitmapTileArea> old_station_catchment_tiles;
std::vector<uint> old_station_tiles;
for (Station *st : Station::Iterate()) {
old_station_industries_nears.push_back(st->industries_near);
old_station_catchment_tiles.push_back(st->catchment_tiles);
old_station_tiles.push_back(st->station_tiles);
}
std::vector<IndustryList> old_station_industries_nears;
std::vector<BitmapTileArea> old_station_catchment_tiles;
std::vector<uint> old_station_tiles;
for (Station *st : Station::Iterate()) {
old_station_industries_nears.push_back(st->industries_near);
old_station_catchment_tiles.push_back(st->catchment_tiles);
old_station_tiles.push_back(st->station_tiles);
}
std::vector<StationList> old_industry_stations_nears;
for (Industry *ind : Industry::Iterate()) {
old_industry_stations_nears.push_back(ind->stations_near);
}
std::vector<StationList> old_industry_stations_nears;
for (Industry *ind : Industry::Iterate()) {
old_industry_stations_nears.push_back(ind->stations_near);
}
extern void RebuildTownCaches(bool cargo_update_required, bool old_map_position);
RebuildTownCaches(false, false);
RebuildSubsidisedSourceAndDestinationCache();
extern void RebuildTownCaches(bool cargo_update_required, bool old_map_position);
RebuildTownCaches(false, false);
RebuildSubsidisedSourceAndDestinationCache();
Station::RecomputeCatchmentForAll();
Station::RecomputeCatchmentForAll();
uint i = 0;
for (Town *t : Town::Iterate()) {
if (MemCmpT(old_town_caches.data() + i, &t->cache) != 0) {
CCLOG("town cache mismatch: town %i", (int)t->index);
}
if (old_town_stations_nears[i] != t->stations_near) {
CCLOG("town stations_near mismatch: town %i, (old size: %u, new size: %u)", (int)t->index, (uint)old_town_stations_nears[i].size(), (uint)t->stations_near.size());
}
i++;
}
i = 0;
for (Station *st : Station::Iterate()) {
if (old_station_industries_nears[i] != st->industries_near) {
CCLOG("station industries_near mismatch: st %i, (old size: %u, new size: %u)", (int)st->index, (uint)old_station_industries_nears[i].size(), (uint)st->industries_near.size());
}
if (!(old_station_catchment_tiles[i] == st->catchment_tiles)) {
CCLOG("station catchment_tiles mismatch: st %i", (int)st->index);
}
if (!(old_station_tiles[i] == st->station_tiles)) {
CCLOG("station station_tiles mismatch: st %i, (old: %u, new: %u)", (int)st->index, old_station_tiles[i], st->station_tiles);
uint i = 0;
for (Town *t : Town::Iterate()) {
if (MemCmpT(old_town_caches.data() + i, &t->cache) != 0) {
CCLOG("town cache mismatch: town %i", (int)t->index);
}
if (old_town_stations_nears[i] != t->stations_near) {
CCLOG("town stations_near mismatch: town %i, (old size: %u, new size: %u)", (int)t->index, (uint)old_town_stations_nears[i].size(), (uint)t->stations_near.size());
}
i++;
}
i++;
}
i = 0;
for (Industry *ind : Industry::Iterate()) {
if (old_industry_stations_nears[i] != ind->stations_near) {
CCLOG("industry stations_near mismatch: ind %i, (old size: %u, new size: %u)", (int)ind->index, (uint)old_industry_stations_nears[i].size(), (uint)ind->stations_near.size());
i = 0;
for (Station *st : Station::Iterate()) {
if (old_station_industries_nears[i] != st->industries_near) {
CCLOG("station industries_near mismatch: st %i, (old size: %u, new size: %u)", (int)st->index, (uint)old_station_industries_nears[i].size(), (uint)st->industries_near.size());
}
if (!(old_station_catchment_tiles[i] == st->catchment_tiles)) {
CCLOG("station catchment_tiles mismatch: st %i", (int)st->index);
}
if (!(old_station_tiles[i] == st->station_tiles)) {
CCLOG("station station_tiles mismatch: st %i, (old: %u, new: %u)", (int)st->index, old_station_tiles[i], st->station_tiles);
}
i++;
}
StationList stlist;
if (ind->neutral_station != nullptr && !_settings_game.station.serve_neutral_industries) {
stlist.insert(ind->neutral_station);
if (ind->stations_near != stlist) {
CCLOG("industry neutral station stations_near mismatch: ind %i, (recalc size: %u, neutral size: %u)", (int)ind->index, (uint)ind->stations_near.size(), (uint)stlist.size());
i = 0;
for (Industry *ind : Industry::Iterate()) {
if (old_industry_stations_nears[i] != ind->stations_near) {
CCLOG("industry stations_near mismatch: ind %i, (old size: %u, new size: %u)", (int)ind->index, (uint)old_industry_stations_nears[i].size(), (uint)ind->stations_near.size());
}
} else {
ForAllStationsAroundTiles(ind->location, [ind, &stlist](Station *st, TileIndex tile) {
if (!IsTileType(tile, MP_INDUSTRY) || GetIndustryIndex(tile) != ind->index) return false;
stlist.insert(st);
return true;
});
if (ind->stations_near != stlist) {
CCLOG("industry FindStationsAroundTiles mismatch: ind %i, (recalc size: %u, find size: %u)", (int)ind->index, (uint)ind->stations_near.size(), (uint)stlist.size());
StationList stlist;
if (ind->neutral_station != nullptr && !_settings_game.station.serve_neutral_industries) {
stlist.insert(ind->neutral_station);
if (ind->stations_near != stlist) {
CCLOG("industry neutral station stations_near mismatch: ind %i, (recalc size: %u, neutral size: %u)", (int)ind->index, (uint)ind->stations_near.size(), (uint)stlist.size());
}
} else {
ForAllStationsAroundTiles(ind->location, [ind, &stlist](Station *st, TileIndex tile) {
if (!IsTileType(tile, MP_INDUSTRY) || GetIndustryIndex(tile) != ind->index) return false;
stlist.insert(st);
return true;
});
if (ind->stations_near != stlist) {
CCLOG("industry FindStationsAroundTiles mismatch: ind %i, (recalc size: %u, find size: %u)", (int)ind->index, (uint)ind->stations_near.size(), (uint)stlist.size());
}
}
i++;
}
i++;
}
/* Check company infrastructure cache. */
std::vector<CompanyInfrastructure> old_infrastructure;
for (const Company *c : Company::Iterate()) old_infrastructure.push_back(c->infrastructure);
extern void AfterLoadCompanyStats();
AfterLoadCompanyStats();
i = 0;
for (const Company *c : Company::Iterate()) {
if (MemCmpT(old_infrastructure.data() + i, &c->infrastructure) != 0) {
CCLOG("infrastructure cache mismatch: company %i", (int)c->index);
char buffer[4096];
old_infrastructure[i].Dump(buffer, lastof(buffer));
CCLOG("Previous:");
ProcessLineByLine(buffer, [&](const char *line) {
CCLOG(" %s", line);
});
c->infrastructure.Dump(buffer, lastof(buffer));
CCLOG("Recalculated:");
ProcessLineByLine(buffer, [&](const char *line) {
CCLOG(" %s", line);
});
if (flags & CHECK_CACHE_INFRA_TOTALS) {
/* Check company infrastructure cache. */
std::vector<CompanyInfrastructure> old_infrastructure;
for (const Company *c : Company::Iterate()) old_infrastructure.push_back(c->infrastructure);
extern void AfterLoadCompanyStats();
AfterLoadCompanyStats();
uint i = 0;
for (const Company *c : Company::Iterate()) {
if (MemCmpT(old_infrastructure.data() + i, &c->infrastructure) != 0) {
CCLOG("infrastructure cache mismatch: company %i", (int)c->index);
char buffer[4096];
old_infrastructure[i].Dump(buffer, lastof(buffer));
CCLOG("Previous:");
ProcessLineByLine(buffer, [&](const char *line) {
CCLOG(" %s", line);
});
c->infrastructure.Dump(buffer, lastof(buffer));
CCLOG("Recalculated:");
ProcessLineByLine(buffer, [&](const char *line) {
CCLOG(" %s", line);
});
}
i++;
}
i++;
}
/* Strict checking of the road stop cache entries */
for (const RoadStop *rs : RoadStop::Iterate()) {
if (IsStandardRoadStopTile(rs->xy)) continue;
assert(rs->GetEntry(DIAGDIR_NE) != rs->GetEntry(DIAGDIR_NW));
rs->GetEntry(DIAGDIR_NE)->CheckIntegrity(rs);
rs->GetEntry(DIAGDIR_NW)->CheckIntegrity(rs);
}
if (flags & CHECK_CACHE_GENERAL) {
/* Strict checking of the road stop cache entries */
for (const RoadStop *rs : RoadStop::Iterate()) {
if (IsStandardRoadStopTile(rs->xy)) continue;
for (Vehicle *v : Vehicle::Iterate()) {
extern bool ValidateVehicleTileHash(const Vehicle *v);
if (!ValidateVehicleTileHash(v)) {
CCLOG("vehicle tile hash mismatch: type %i, vehicle %i, company %i, unit number %i", (int)v->type, v->index, (int)v->owner, v->unitnumber);
assert(rs->GetEntry(DIAGDIR_NE) != rs->GetEntry(DIAGDIR_NW));
rs->GetEntry(DIAGDIR_NE)->CheckIntegrity(rs);
rs->GetEntry(DIAGDIR_NW)->CheckIntegrity(rs);
}
extern void FillNewGRFVehicleCache(const Vehicle *v);
if (v != v->First() || v->vehstatus & VS_CRASHED || !v->IsPrimaryVehicle()) continue;
for (Vehicle *v : Vehicle::Iterate()) {
extern bool ValidateVehicleTileHash(const Vehicle *v);
if (!ValidateVehicleTileHash(v)) {
CCLOG("vehicle tile hash mismatch: type %i, vehicle %i, company %i, unit number %i", (int)v->type, v->index, (int)v->owner, v->unitnumber);
}
extern void FillNewGRFVehicleCache(const Vehicle *v);
if (v != v->First() || v->vehstatus & VS_CRASHED || !v->IsPrimaryVehicle()) continue;
uint length = 0;
for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
if (u->IsGroundVehicle() && (HasBit(u->GetGroundVehicleFlags(), GVF_GOINGUP_BIT) || HasBit(u->GetGroundVehicleFlags(), GVF_GOINGDOWN_BIT)) && u->GetGroundVehicleCache()->cached_slope_resistance && HasBit(v->vcache.cached_veh_flags, VCF_GV_ZERO_SLOPE_RESIST)) {
CCLOGV("VCF_GV_ZERO_SLOPE_RESIST set incorrectly (1)");
uint length = 0;
for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
if (u->IsGroundVehicle() && (HasBit(u->GetGroundVehicleFlags(), GVF_GOINGUP_BIT) || HasBit(u->GetGroundVehicleFlags(), GVF_GOINGDOWN_BIT)) && u->GetGroundVehicleCache()->cached_slope_resistance && HasBit(v->vcache.cached_veh_flags, VCF_GV_ZERO_SLOPE_RESIST)) {
CCLOGV("VCF_GV_ZERO_SLOPE_RESIST set incorrectly (1)");
}
if (u->type == VEH_TRAIN && u->breakdown_ctr != 0 && !HasBit(Train::From(v)->flags, VRF_CONSIST_BREAKDOWN)) {
CCLOGV("VRF_CONSIST_BREAKDOWN incorrectly not set");
}
if (u->type == VEH_TRAIN && ((Train::From(u)->track & TRACK_BIT_WORMHOLE && !(Train::From(u)->vehstatus & VS_HIDDEN)) || Train::From(u)->track == TRACK_BIT_DEPOT) && !HasBit(Train::From(v)->flags, VRF_CONSIST_SPEED_REDUCTION)) {
CCLOGV("VRF_CONSIST_SPEED_REDUCTION incorrectly not set");
}
length++;
}
if (u->type == VEH_TRAIN && u->breakdown_ctr != 0 && !HasBit(Train::From(v)->flags, VRF_CONSIST_BREAKDOWN)) {
CCLOGV("VRF_CONSIST_BREAKDOWN incorrectly not set");
NewGRFCache *grf_cache = CallocT<NewGRFCache>(length);
VehicleCache *veh_cache = CallocT<VehicleCache>(length);
GroundVehicleCache *gro_cache = CallocT<GroundVehicleCache>(length);
AircraftCache *air_cache = CallocT<AircraftCache>(length);
TrainCache *tra_cache = CallocT<TrainCache>(length);
Vehicle **veh_old = CallocT<Vehicle *>(length);
length = 0;
for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
FillNewGRFVehicleCache(u);
grf_cache[length] = u->grf_cache;
veh_cache[length] = u->vcache;
switch (u->type) {
case VEH_TRAIN:
gro_cache[length] = Train::From(u)->gcache;
tra_cache[length] = Train::From(u)->tcache;
veh_old[length] = CallocT<Train>(1);
memcpy((void *) veh_old[length], (const void *) Train::From(u), sizeof(Train));
break;
case VEH_ROAD:
gro_cache[length] = RoadVehicle::From(u)->gcache;
veh_old[length] = CallocT<RoadVehicle>(1);
memcpy((void *) veh_old[length], (const void *) RoadVehicle::From(u), sizeof(RoadVehicle));
break;
case VEH_AIRCRAFT:
air_cache[length] = Aircraft::From(u)->acache;
veh_old[length] = CallocT<Aircraft>(1);
memcpy((void *) veh_old[length], (const void *) Aircraft::From(u), sizeof(Aircraft));
break;
default:
veh_old[length] = CallocT<Vehicle>(1);
memcpy((void *) veh_old[length], (const void *) u, sizeof(Vehicle));
break;
}
length++;
}
if (u->type == VEH_TRAIN && ((Train::From(u)->track & TRACK_BIT_WORMHOLE && !(Train::From(u)->vehstatus & VS_HIDDEN)) || Train::From(u)->track == TRACK_BIT_DEPOT) && !HasBit(Train::From(v)->flags, VRF_CONSIST_SPEED_REDUCTION)) {
CCLOGV("VRF_CONSIST_SPEED_REDUCTION incorrectly not set");
switch (v->type) {
case VEH_TRAIN: Train::From(v)->ConsistChanged(CCF_TRACK); break;
case VEH_ROAD: RoadVehUpdateCache(RoadVehicle::From(v)); break;
case VEH_AIRCRAFT: UpdateAircraftCache(Aircraft::From(v)); break;
case VEH_SHIP: Ship::From(v)->UpdateCache(); break;
default: break;
}
length++;
}
NewGRFCache *grf_cache = CallocT<NewGRFCache>(length);
VehicleCache *veh_cache = CallocT<VehicleCache>(length);
GroundVehicleCache *gro_cache = CallocT<GroundVehicleCache>(length);
AircraftCache *air_cache = CallocT<AircraftCache>(length);
TrainCache *tra_cache = CallocT<TrainCache>(length);
Vehicle **veh_old = CallocT<Vehicle *>(length);
length = 0;
for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
FillNewGRFVehicleCache(u);
grf_cache[length] = u->grf_cache;
veh_cache[length] = u->vcache;
switch (u->type) {
case VEH_TRAIN:
gro_cache[length] = Train::From(u)->gcache;
tra_cache[length] = Train::From(u)->tcache;
veh_old[length] = CallocT<Train>(1);
memcpy((void *) veh_old[length], (const void *) Train::From(u), sizeof(Train));
break;
case VEH_ROAD:
gro_cache[length] = RoadVehicle::From(u)->gcache;
veh_old[length] = CallocT<RoadVehicle>(1);
memcpy((void *) veh_old[length], (const void *) RoadVehicle::From(u), sizeof(RoadVehicle));
break;
case VEH_AIRCRAFT:
air_cache[length] = Aircraft::From(u)->acache;
veh_old[length] = CallocT<Aircraft>(1);
memcpy((void *) veh_old[length], (const void *) Aircraft::From(u), sizeof(Aircraft));
break;
default:
veh_old[length] = CallocT<Vehicle>(1);
memcpy((void *) veh_old[length], (const void *) u, sizeof(Vehicle));
break;
length = 0;
for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
FillNewGRFVehicleCache(u);
if (memcmp(&grf_cache[length], &u->grf_cache, sizeof(NewGRFCache)) != 0) {
CCLOGV("newgrf cache mismatch");
}
if (veh_cache[length].cached_max_speed != u->vcache.cached_max_speed || veh_cache[length].cached_cargo_age_period != u->vcache.cached_cargo_age_period ||
veh_cache[length].cached_vis_effect != u->vcache.cached_vis_effect || HasBit(veh_cache[length].cached_veh_flags ^ u->vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT)) {
CCLOGV("vehicle cache mismatch: %c%c%c%c",
veh_cache[length].cached_max_speed != u->vcache.cached_max_speed ? 'm' : '-',
veh_cache[length].cached_cargo_age_period != u->vcache.cached_cargo_age_period ? 'c' : '-',
veh_cache[length].cached_vis_effect != u->vcache.cached_vis_effect ? 'v' : '-',
HasBit(veh_cache[length].cached_veh_flags ^ u->vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT) ? 'l' : '-');
}
if (u->IsGroundVehicle() && (HasBit(u->GetGroundVehicleFlags(), GVF_GOINGUP_BIT) || HasBit(u->GetGroundVehicleFlags(), GVF_GOINGDOWN_BIT)) && u->GetGroundVehicleCache()->cached_slope_resistance && HasBit(v->vcache.cached_veh_flags, VCF_GV_ZERO_SLOPE_RESIST)) {
CCLOGV("VCF_GV_ZERO_SLOPE_RESIST set incorrectly (2)");
}
if (veh_old[length]->acceleration != u->acceleration) {
CCLOGV("acceleration mismatch");
}
if (veh_old[length]->breakdown_chance != u->breakdown_chance) {
CCLOGV("breakdown_chance mismatch");
}
if (veh_old[length]->breakdown_ctr != u->breakdown_ctr) {
CCLOGV("breakdown_ctr mismatch");
}
if (veh_old[length]->breakdown_delay != u->breakdown_delay) {
CCLOGV("breakdown_delay mismatch");
}
if (veh_old[length]->breakdowns_since_last_service != u->breakdowns_since_last_service) {
CCLOGV("breakdowns_since_last_service mismatch");
}
if (veh_old[length]->breakdown_severity != u->breakdown_severity) {
CCLOGV("breakdown_severity mismatch");
}
if (veh_old[length]->breakdown_type != u->breakdown_type) {
CCLOGV("breakdown_type mismatch");
}
if (veh_old[length]->vehicle_flags != u->vehicle_flags) {
CCLOGV("vehicle_flags mismatch");
}
auto print_gv_cache_diff = [&](const char *vtype, const GroundVehicleCache &a, const GroundVehicleCache &b) {
CCLOGV("%s ground vehicle cache mismatch: %c%c%c%c%c%c%c%c%c%c",
vtype,
a.cached_weight != b.cached_weight ? 'w' : '-',
a.cached_slope_resistance != b.cached_slope_resistance ? 'r' : '-',
a.cached_max_te != b.cached_max_te ? 't' : '-',
a.cached_axle_resistance != b.cached_axle_resistance ? 'a' : '-',
a.cached_max_track_speed != b.cached_max_track_speed ? 's' : '-',
a.cached_power != b.cached_power ? 'p' : '-',
a.cached_air_drag != b.cached_air_drag ? 'd' : '-',
a.cached_total_length != b.cached_total_length ? 'l' : '-',
a.first_engine != b.first_engine ? 'e' : '-',
a.cached_veh_length != b.cached_veh_length ? 'L' : '-');
};
switch (u->type) {
case VEH_TRAIN:
if (memcmp(&gro_cache[length], &Train::From(u)->gcache, sizeof(GroundVehicleCache)) != 0) {
print_gv_cache_diff("train", gro_cache[length], Train::From(u)->gcache);
}
if (memcmp(&tra_cache[length], &Train::From(u)->tcache, sizeof(TrainCache)) != 0) {
CCLOGV("train cache mismatch: %c%c%c%c%c%c%c%c%c",
tra_cache[length].cached_override != Train::From(u)->tcache.cached_override ? 'o' : '-',
tra_cache[length].cached_tflags != Train::From(u)->tcache.cached_tflags ? 'f' : '-',
tra_cache[length].cached_num_engines != Train::From(u)->tcache.cached_num_engines ? 'e' : '-',
tra_cache[length].cached_centre_mass != Train::From(u)->tcache.cached_centre_mass ? 'm' : '-',
tra_cache[length].cached_veh_weight != Train::From(u)->tcache.cached_veh_weight ? 'w' : '-',
tra_cache[length].cached_uncapped_decel != Train::From(u)->tcache.cached_uncapped_decel ? 'D' : '-',
tra_cache[length].cached_deceleration != Train::From(u)->tcache.cached_deceleration ? 'd' : '-',
tra_cache[length].user_def_data != Train::From(u)->tcache.user_def_data ? 'u' : '-',
tra_cache[length].cached_max_curve_speed != Train::From(u)->tcache.cached_max_curve_speed ? 'c' : '-');
}
if (Train::From(veh_old[length])->railtype != Train::From(u)->railtype) {
CCLOGV("railtype mismatch");
}
if (Train::From(veh_old[length])->compatible_railtypes != Train::From(u)->compatible_railtypes) {
CCLOGV("compatible_railtypes mismatch");
}
if (Train::From(veh_old[length])->flags != Train::From(u)->flags) {
CCLOGV("train flags mismatch");
}
break;
case VEH_ROAD:
if (memcmp(&gro_cache[length], &RoadVehicle::From(u)->gcache, sizeof(GroundVehicleCache)) != 0) {
print_gv_cache_diff("road vehicle", gro_cache[length], Train::From(u)->gcache);
}
break;
case VEH_AIRCRAFT:
if (memcmp(&air_cache[length], &Aircraft::From(u)->acache, sizeof(AircraftCache)) != 0) {
CCLOGV("Aircraft vehicle cache mismatch: %c%c",
air_cache[length].cached_max_range != Aircraft::From(u)->acache.cached_max_range ? 'r' : '-',
air_cache[length].cached_max_range_sqr != Aircraft::From(u)->acache.cached_max_range_sqr ? 's' : '-');
}
break;
default:
break;
}
free(veh_old[length]);
length++;
}
length++;
free(grf_cache);
free(veh_cache);
free(gro_cache);
free(air_cache);
free(tra_cache);
free(veh_old);
}
switch (v->type) {
case VEH_TRAIN: Train::From(v)->ConsistChanged(CCF_TRACK); break;
case VEH_ROAD: RoadVehUpdateCache(RoadVehicle::From(v)); break;
case VEH_AIRCRAFT: UpdateAircraftCache(Aircraft::From(v)); break;
case VEH_SHIP: Ship::From(v)->UpdateCache(); break;
default: break;
/* Check whether the caches are still valid */
for (Vehicle *v : Vehicle::Iterate()) {
byte buff[sizeof(VehicleCargoList)];
memcpy(buff, &v->cargo, sizeof(VehicleCargoList));
v->cargo.InvalidateCache();
assert(memcmp(&v->cargo, buff, sizeof(VehicleCargoList)) == 0);
}
length = 0;
for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
FillNewGRFVehicleCache(u);
if (memcmp(&grf_cache[length], &u->grf_cache, sizeof(NewGRFCache)) != 0) {
CCLOGV("newgrf cache mismatch");
}
if (veh_cache[length].cached_max_speed != u->vcache.cached_max_speed || veh_cache[length].cached_cargo_age_period != u->vcache.cached_cargo_age_period ||
veh_cache[length].cached_vis_effect != u->vcache.cached_vis_effect || HasBit(veh_cache[length].cached_veh_flags ^ u->vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT)) {
CCLOGV("vehicle cache mismatch: %c%c%c%c",
veh_cache[length].cached_max_speed != u->vcache.cached_max_speed ? 'm' : '-',
veh_cache[length].cached_cargo_age_period != u->vcache.cached_cargo_age_period ? 'c' : '-',
veh_cache[length].cached_vis_effect != u->vcache.cached_vis_effect ? 'v' : '-',
HasBit(veh_cache[length].cached_veh_flags ^ u->vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT) ? 'l' : '-');
for (Station *st : Station::Iterate()) {
for (CargoID c = 0; c < NUM_CARGO; c++) {
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);
}
if (u->IsGroundVehicle() && (HasBit(u->GetGroundVehicleFlags(), GVF_GOINGUP_BIT) || HasBit(u->GetGroundVehicleFlags(), GVF_GOINGDOWN_BIT)) && u->GetGroundVehicleCache()->cached_slope_resistance && HasBit(v->vcache.cached_veh_flags, VCF_GV_ZERO_SLOPE_RESIST)) {
CCLOGV("VCF_GV_ZERO_SLOPE_RESIST set incorrectly (2)");
}
if (veh_old[length]->acceleration != u->acceleration) {
CCLOGV("acceleration mismatch");
}
if (veh_old[length]->breakdown_chance != u->breakdown_chance) {
CCLOGV("breakdown_chance mismatch");
}
if (veh_old[length]->breakdown_ctr != u->breakdown_ctr) {
CCLOGV("breakdown_ctr mismatch");
}
if (veh_old[length]->breakdown_delay != u->breakdown_delay) {
CCLOGV("breakdown_delay mismatch");
}
if (veh_old[length]->breakdowns_since_last_service != u->breakdowns_since_last_service) {
CCLOGV("breakdowns_since_last_service mismatch");
}
if (veh_old[length]->breakdown_severity != u->breakdown_severity) {
CCLOGV("breakdown_severity mismatch");
}
if (veh_old[length]->breakdown_type != u->breakdown_type) {
CCLOGV("breakdown_type mismatch");
/* Check docking tiles */
TileArea ta;
std::map<TileIndex, bool> docking_tiles;
for (TileIndex tile : st->docking_station) {
ta.Add(tile);
docking_tiles[tile] = IsDockingTile(tile);
}
if (veh_old[length]->vehicle_flags != u->vehicle_flags) {
CCLOGV("vehicle_flags mismatch");
UpdateStationDockingTiles(st);
if (ta.tile != st->docking_station.tile || ta.w != st->docking_station.w || ta.h != st->docking_station.h) {
CCLOG("station docking mismatch: station %i, company %i, prev: (%X, %u, %u), recalc: (%X, %u, %u)",
st->index, (int)st->owner, ta.tile, ta.w, ta.h, st->docking_station.tile, st->docking_station.w, st->docking_station.h);
}
auto print_gv_cache_diff = [&](const char *vtype, const GroundVehicleCache &a, const GroundVehicleCache &b) {
CCLOGV("%s ground vehicle cache mismatch: %c%c%c%c%c%c%c%c%c%c",
vtype,
a.cached_weight != b.cached_weight ? 'w' : '-',
a.cached_slope_resistance != b.cached_slope_resistance ? 'r' : '-',
a.cached_max_te != b.cached_max_te ? 't' : '-',
a.cached_axle_resistance != b.cached_axle_resistance ? 'a' : '-',
a.cached_max_track_speed != b.cached_max_track_speed ? 's' : '-',
a.cached_power != b.cached_power ? 'p' : '-',
a.cached_air_drag != b.cached_air_drag ? 'd' : '-',
a.cached_total_length != b.cached_total_length ? 'l' : '-',
a.first_engine != b.first_engine ? 'e' : '-',
a.cached_veh_length != b.cached_veh_length ? 'L' : '-');
};
switch (u->type) {
case VEH_TRAIN:
if (memcmp(&gro_cache[length], &Train::From(u)->gcache, sizeof(GroundVehicleCache)) != 0) {
print_gv_cache_diff("train", gro_cache[length], Train::From(u)->gcache);
}
if (memcmp(&tra_cache[length], &Train::From(u)->tcache, sizeof(TrainCache)) != 0) {
CCLOGV("train cache mismatch: %c%c%c%c%c%c%c%c%c",
tra_cache[length].cached_override != Train::From(u)->tcache.cached_override ? 'o' : '-',
tra_cache[length].cached_tflags != Train::From(u)->tcache.cached_tflags ? 'f' : '-',
tra_cache[length].cached_num_engines != Train::From(u)->tcache.cached_num_engines ? 'e' : '-',
tra_cache[length].cached_centre_mass != Train::From(u)->tcache.cached_centre_mass ? 'm' : '-',
tra_cache[length].cached_veh_weight != Train::From(u)->tcache.cached_veh_weight ? 'w' : '-',
tra_cache[length].cached_uncapped_decel != Train::From(u)->tcache.cached_uncapped_decel ? 'D' : '-',
tra_cache[length].cached_deceleration != Train::From(u)->tcache.cached_deceleration ? 'd' : '-',
tra_cache[length].user_def_data != Train::From(u)->tcache.user_def_data ? 'u' : '-',
tra_cache[length].cached_max_curve_speed != Train::From(u)->tcache.cached_max_curve_speed ? 'c' : '-');
}
if (Train::From(veh_old[length])->railtype != Train::From(u)->railtype) {
CCLOGV("railtype mismatch");
}
if (Train::From(veh_old[length])->compatible_railtypes != Train::From(u)->compatible_railtypes) {
CCLOGV("compatible_railtypes mismatch");
}
if (Train::From(veh_old[length])->flags != Train::From(u)->flags) {
CCLOGV("train flags mismatch");
}
break;
case VEH_ROAD:
if (memcmp(&gro_cache[length], &RoadVehicle::From(u)->gcache, sizeof(GroundVehicleCache)) != 0) {
print_gv_cache_diff("road vehicle", gro_cache[length], Train::From(u)->gcache);
}
break;
case VEH_AIRCRAFT:
if (memcmp(&air_cache[length], &Aircraft::From(u)->acache, sizeof(AircraftCache)) != 0) {
CCLOGV("Aircraft vehicle cache mismatch: %c%c",
air_cache[length].cached_max_range != Aircraft::From(u)->acache.cached_max_range ? 'r' : '-',
air_cache[length].cached_max_range_sqr != Aircraft::From(u)->acache.cached_max_range_sqr ? 's' : '-');
}
break;
default:
break;
for (TileIndex tile : ta) {
if (docking_tiles[tile] != IsDockingTile(tile)) {
CCLOG("docking tile mismatch: tile %i", (int)tile);
}
}
free(veh_old[length]);
length++;
}
free(grf_cache);
free(veh_cache);
free(gro_cache);
free(air_cache);
free(tra_cache);
free(veh_old);
}
/* Check whether the caches are still valid */
for (Vehicle *v : Vehicle::Iterate()) {
byte buff[sizeof(VehicleCargoList)];
memcpy(buff, &v->cargo, sizeof(VehicleCargoList));
v->cargo.InvalidateCache();
assert(memcmp(&v->cargo, buff, sizeof(VehicleCargoList)) == 0);
}
for (Station *st : Station::Iterate()) {
for (CargoID c = 0; c < NUM_CARGO; c++) {
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);
for (OrderList *order_list : OrderList::Iterate()) {
order_list->DebugCheckSanity();
}
/* Check docking tiles */
TileArea ta;
std::map<TileIndex, bool> docking_tiles;
for (TileIndex tile : st->docking_station) {
ta.Add(tile);
docking_tiles[tile] = IsDockingTile(tile);
}
UpdateStationDockingTiles(st);
if (ta.tile != st->docking_station.tile || ta.w != st->docking_station.w || ta.h != st->docking_station.h) {
CCLOG("station docking mismatch: station %i, company %i, prev: (%X, %u, %u), recalc: (%X, %u, %u)",
st->index, (int)st->owner, ta.tile, ta.w, ta.h, st->docking_station.tile, st->docking_station.w, st->docking_station.h);
extern void ValidateVehicleTickCaches();
ValidateVehicleTickCaches();
for (Vehicle *v : Vehicle::Iterate()) {
if (v->Previous()) assert_msg(v->Previous()->Next() == v, "%u", v->index);
if (v->Next()) assert_msg(v->Next()->Previous() == v, "%u", v->index);
}
for (TileIndex tile : ta) {
if (docking_tiles[tile] != IsDockingTile(tile)) {
CCLOG("docking tile mismatch: tile %i", (int)tile);
}
for (const TemplateVehicle *tv : TemplateVehicle::Iterate()) {
if (tv->Prev()) assert_msg(tv->Prev()->Next() == tv, "%u", tv->index);
if (tv->Next()) assert_msg(tv->Next()->Prev() == tv, "%u", tv->index);
}
}
for (OrderList *order_list : OrderList::Iterate()) {
order_list->DebugCheckSanity();
}
if (!TraceRestrictSlot::ValidateVehicleIndex()) CCLOG("Trace restrict slot vehicle index validation failed");
TraceRestrictSlot::ValidateSlotOccupants(log);
extern void ValidateVehicleTickCaches();
ValidateVehicleTickCaches();
if (!CargoPacket::ValidateDeferredCargoPayments()) CCLOG("Cargo packets deferred payments validation failed");
for (Vehicle *v : Vehicle::Iterate()) {
if (v->Previous()) assert_msg(v->Previous()->Next() == v, "%u", v->index);
if (v->Next()) assert_msg(v->Next()->Previous() == v, "%u", v->index);
}
for (const TemplateVehicle *tv : TemplateVehicle::Iterate()) {
if (tv->Prev()) assert_msg(tv->Prev()->Next() == tv, "%u", tv->index);
if (tv->Next()) assert_msg(tv->Next()->Prev() == tv, "%u", tv->index);
if (_order_destination_refcount_map_valid) {
btree::btree_map<uint32, uint32> saved_order_destination_refcount_map = std::move(_order_destination_refcount_map);
for (auto iter = saved_order_destination_refcount_map.begin(); iter != saved_order_destination_refcount_map.end();) {
if (iter->second == 0) {
iter = saved_order_destination_refcount_map.erase(iter);
} else {
++iter;
}
}
IntialiseOrderDestinationRefcountMap();
if (saved_order_destination_refcount_map != _order_destination_refcount_map) CCLOG("Order destination refcount map mismatch");
} else {
CCLOG("Order destination refcount map not valid");
}
}
if (!TraceRestrictSlot::ValidateVehicleIndex()) CCLOG("Trace restrict slot vehicle index validation failed");
TraceRestrictSlot::ValidateSlotOccupants(log);
if (!CargoPacket::ValidateDeferredCargoPayments()) CCLOG("Cargo packets deferred payments validation failed");
if (_order_destination_refcount_map_valid) {
btree::btree_map<uint32, uint32> saved_order_destination_refcount_map = std::move(_order_destination_refcount_map);
for (auto iter = saved_order_destination_refcount_map.begin(); iter != saved_order_destination_refcount_map.end();) {
if (iter->second == 0) {
iter = saved_order_destination_refcount_map.erase(iter);
} else {
++iter;
}
if ((flags & CHECK_CACHE_EMIT_LOG) && !saved_messages.empty()) {
InconsistencyExtraInfo info;
info.check_caches_result = std::move(saved_messages);
CrashLog::InconsistencyLog(info);
for (std::string &str : info.check_caches_result) {
LogDesyncMsg(std::move(str));
}
IntialiseOrderDestinationRefcountMap();
if (saved_order_destination_refcount_map != _order_destination_refcount_map) CCLOG("Order destination refcount map mismatch");
} else {
CCLOG("Order destination refcount map not valid");
}
#undef CCLOGV
@ -1746,7 +1778,7 @@ void CheckCaches(bool force_check, std::function<void(const char *)> log)
CommandCost CmdDesyncCheck(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
if (flags & DC_EXEC) {
CheckCaches(true, nullptr);
CheckCaches(true, nullptr, CHECK_CACHE_ALL | CHECK_CACHE_EMIT_LOG);
}
return CommandCost();
@ -1803,7 +1835,7 @@ void StateGameLoop()
SaveOrLoad(name, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false);
}
CheckCaches(false, nullptr);
CheckCaches(false, nullptr, CHECK_CACHE_ALL | CHECK_CACHE_EMIT_LOG);
/* All these actions has to be done from OWNER_NONE
* for multiplayer compatibility */
@ -1969,21 +2001,3 @@ void GameLoop()
SoundDriver::GetInstance()->MainLoop();
MusicLoop();
}
char *DumpGameEventFlags(GameEventFlags events, char *b, const char *last)
{
if (b <= last) *b = 0;
auto dump = [&](char c, GameEventFlags ev) {
if (events & ev) b += seprintf(b, last, "%c", c);
};
dump('d', GEF_COMPANY_DELETE);
dump('m', GEF_COMPANY_MERGE);
dump('n', GEF_RELOAD_NEWGRF);
dump('t', GEF_TBTR_REPLACEMENT);
dump('D', GEF_DISASTER_VEH);
dump('c', GEF_TRAIN_CRASH);
dump('i', GEF_INDUSTRY_CREATE);
dump('j', GEF_INDUSTRY_DELETE);
dump('v', GEF_VIRT_TRAIN);
return b;
}

@ -10,6 +10,7 @@
#ifndef OPENTTD_H
#define OPENTTD_H
#include <atomic>
#include "core/enum_type.hpp"
/** Mode which defines the state of the game. */
@ -58,7 +59,7 @@ enum ExtraDisplayOptions {
extern GameMode _game_mode;
extern SwitchMode _switch_mode;
extern bool _check_special_modes;
extern bool _exit_game;
extern std::atomic<bool> _exit_game;
extern bool _save_config;
/** Modes of pausing we've got */
@ -80,32 +81,6 @@ DECLARE_ENUM_AS_BIT_SET(PauseMode)
/** The current pause mode */
extern PauseMode _pause_mode;
enum GameEventFlags : uint32 {
GEF_COMPANY_DELETE = 1 << 0, ///< (d) A company has been deleted
GEF_COMPANY_MERGE = 1 << 1, ///< (m) A company has been bought by another
GEF_RELOAD_NEWGRF = 1 << 2, ///< (n) ReloadNewGRFData() has been called
GEF_TBTR_REPLACEMENT = 1 << 3, ///< (t) CMD_TEMPLATE_REPLACE_VEHICLE has been called
GEF_DISASTER_VEH = 1 << 4, ///< (D) A disaster vehicle exists or has been created
GEF_TRAIN_CRASH = 1 << 5, ///< (c) A train crash has occurred
GEF_INDUSTRY_CREATE = 1 << 6, ///< (i) An industry has been created (in game)
GEF_INDUSTRY_DELETE = 1 << 7, ///< (j) An industry has been deleted (in game)
GEF_VIRT_TRAIN = 1 << 8, ///< (v) A virtual train has been created
};
DECLARE_ENUM_AS_BIT_SET(GameEventFlags)
extern GameEventFlags _game_events_since_load;
extern GameEventFlags _game_events_overall;
inline void RegisterGameEvents(GameEventFlags events)
{
_game_events_since_load |= events;
_game_events_overall |= events;
}
char *DumpGameEventFlags(GameEventFlags events, char *b, const char *last);
extern time_t _game_load_time;
void AskExitGame();
void AskExitToGameMenu();

@ -492,6 +492,13 @@ void CDECL HandleCrash(int signum, siginfo_t *si, void *context)
log.MakeDesyncCrashLog(log_in, log_out, info);
}
/* static */ void CrashLog::InconsistencyLog(const InconsistencyExtraInfo &info)
{
CrashLogOSX log(CrashLogOSX::DesyncTag{});
log.MakeInconsistencyLog(info);
}
/* static */ void CrashLog::VersionInfoLog()
{
CrashLogOSX log(CrashLogOSX::DesyncTag{});

@ -215,6 +215,8 @@ void CoreTextFontCache::SetFontSize(int pixels)
this->descender = -(int)std::ceil(CTFontGetDescent(this->font.get()));
this->height = this->ascender - this->descender;
font_height_cache[this->fs] = this->GetHeight();
/* Get real font name. */
char name[128];
CFAutoRelease<CFStringRef> font_name((CFStringRef)CTFontCopyAttribute(this->font.get(), kCTFontDisplayNameAttribute));

@ -628,6 +628,12 @@ static void CDECL HandleCrash(int signum)
log.MakeDesyncCrashLog(log_in, log_out, info);
}
/* static */ void CrashLog::InconsistencyLog(const InconsistencyExtraInfo &info)
{
CrashLogUnix log(CrashLogUnix::DesyncTag{});
log.MakeInconsistencyLog(info);
}
/* static */ void CrashLog::VersionInfoLog()
{
CrashLogUnix log(CrashLogUnix::DesyncTag{});

@ -758,6 +758,12 @@ static void CDECL CustomAbort(int signal)
log.MakeDesyncCrashLog(log_in, log_out, info);
}
/* static */ void CrashLog::InconsistencyLog(const InconsistencyExtraInfo &info)
{
CrashLogWindows log(nullptr);
log.MakeInconsistencyLog(info);
}
/* static */ void CrashLog::VersionInfoLog()
{
CrashLogWindows log(nullptr);

@ -337,16 +337,6 @@ struct CYapfShip1 : CYapfT<CYapfShip_TypesT<CYapfShip1, CFollowTrackWater , C
/* YAPF type 2 - uses TileIndex/DiagDirection as Node key */
struct CYapfShip2 : CYapfT<CYapfShip_TypesT<CYapfShip2, CFollowTrackWater , CShipNodeListExitDir > > {};
static inline bool RequireTrackdirKey()
{
/* If the two curve penalties are not equal, then it is not possible to use the
* ExitDir keyed node list, as it there will be key overlap. Using Trackdir keyed
* nodes means potentially more paths are tested, which would be wasteful if it's
* not necessary.
*/
return _settings_game.pf.yapf.ship_curve45_penalty != _settings_game.pf.yapf.ship_curve90_penalty;
}
/** Ship controller helper - path finder invoker */
Track YapfShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found, ShipPathCache &path_cache)
{
@ -355,7 +345,7 @@ Track YapfShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir,
PfnChooseShipTrack pfnChooseShipTrack = CYapfShip2::ChooseShipTrack; // default: ExitDir
/* check if non-default YAPF type needed */
if (_settings_game.pf.yapf.disable_node_optimization || RequireTrackdirKey()) {
if (_settings_game.pf.yapf.disable_node_optimization) {
pfnChooseShipTrack = &CYapfShip1::ChooseShipTrack; // Trackdir
}
@ -373,7 +363,7 @@ bool YapfShipCheckReverse(const Ship *v)
PfnCheckReverseShip pfnCheckReverseShip = CYapfShip2::CheckShipReverse; // default: ExitDir
/* check if non-default YAPF type needed */
if (_settings_game.pf.yapf.disable_node_optimization || RequireTrackdirKey()) {
if (_settings_game.pf.yapf.disable_node_optimization) {
pfnCheckReverseShip = &CYapfShip1::CheckShipReverse; // Trackdir
}

@ -67,6 +67,7 @@
#include "../animated_tile.h"
#include "../company_func.h"
#include "../infrastructure_func.h"
#include "../event_logs.h"
#include "saveload_internal.h"
@ -443,9 +444,11 @@ static void CDECL HandleSavegameLoadCrash(int signum)
for (const GRFConfig *c = _grfconfig; c != nullptr; c = c->next) {
if (HasBit(c->flags, GCF_COMPATIBLE)) {
const GRFIdentifier *replaced = GetOverriddenIdentifier(c);
char buf[40];
md5sumToString(buf, lastof(buf), replaced->md5sum);
p += seprintf(p, lastof(buffer), "NewGRF %08X (checksum %s) not found.\n Loaded NewGRF \"%s\" with same GRF ID instead.\n", BSWAP32(c->ident.grfid), buf, c->filename);
char original_md5[40];
char replaced_md5[40];
md5sumToString(original_md5, lastof(original_md5), c->original_md5sum);
md5sumToString(replaced_md5, lastof(replaced_md5), replaced->md5sum);
p += seprintf(p, lastof(buffer), "NewGRF %08X (checksum %s) not found.\n Loaded NewGRF \"%s\" (checksum %s) with same GRF ID instead.\n", BSWAP32(c->ident.grfid), original_md5, c->filename, replaced_md5);
}
if (c->status == GCS_NOT_FOUND) {
char buf[40];
@ -1719,7 +1722,7 @@ bool AfterLoadGame()
c->avail_roadtypes = GetCompanyRoadTypes(c->index);
}
if (!IsSavegameVersionBefore(SLV_27)) AfterLoadStations();
AfterLoadStations();
/* Time starts at 0 instead of 1920.
* Account for this in older games by adding an offset */
@ -3743,7 +3746,7 @@ bool AfterLoadGame()
}
}
if (IsSavegameVersionUntil(SLV_ENDING_YEAR) || !SlXvIsFeaturePresent(XSLFI_MULTIPLE_DOCKS, 2) || !SlXvIsFeaturePresent(XSLFI_DOCKING_CACHE_VER, 1)) {
if (IsSavegameVersionUntil(SLV_ENDING_YEAR) || !SlXvIsFeaturePresent(XSLFI_MULTIPLE_DOCKS, 2) || !SlXvIsFeaturePresent(XSLFI_DOCKING_CACHE_VER, 2)) {
/* Update station docking tiles. Was only needed for pre-SLV_MULTITLE_DOCKS
* savegames, but a bug in docking tiles touched all savegames between
* SLV_MULTITILE_DOCKS and SLV_ENDING_YEAR. */
@ -4030,6 +4033,7 @@ bool AfterLoadGame()
void ReloadNewGRFData()
{
RegisterGameEvents(GEF_RELOAD_NEWGRF);
AppendSpecialEventsLogEntry("NewGRF reload");
RailTypeLabel rail_type_label_map[RAILTYPE_END];
for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {

@ -30,7 +30,7 @@ static void Load_XBSS()
LongBridgeSignalStorage &lbss = _long_bridge_signal_sim_map[index];
SlObject(&stub, _long_bridge_signal_storage_stub_desc);
lbss.signal_red_bits.resize(stub.length);
SlArray(&(lbss.signal_red_bits[0]), stub.length, SLE_UINT64);
SlArray(lbss.signal_red_bits.data(), stub.length, SLE_UINT64);
}
}
@ -39,7 +39,7 @@ static void RealSave_XBSS(const LongBridgeSignalStorage *lbss)
LongBridgeSignalStorageStub stub;
stub.length = (uint32)lbss->signal_red_bits.size();
SlObject(&stub, _long_bridge_signal_storage_stub_desc);
SlArray(const_cast<uint64*>(&(lbss->signal_red_bits[0])), stub.length, SLE_UINT64);
SlArray(const_cast<uint64*>(lbss->signal_red_bits.data()), stub.length, SLE_UINT64);
}
static void Save_XBSS()

@ -131,7 +131,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_FLOW_STAT_FLAGS, XSCF_NULL, 1, 1, "flow_stat_flags", nullptr, nullptr, nullptr },
{ XSLFI_SPEED_RESTRICTION, XSCF_NULL, 1, 1, "speed_restriction", nullptr, nullptr, "VESR" },
{ XSLFI_STATION_GOODS_EXTRA, XSCF_NULL, 1, 1, "station_goods_extra", nullptr, nullptr, nullptr },
{ XSLFI_DOCKING_CACHE_VER, XSCF_IGNORABLE_ALL, 1, 1, "docking_cache_ver", nullptr, nullptr, nullptr },
{ XSLFI_DOCKING_CACHE_VER, XSCF_IGNORABLE_ALL, 2, 2, "docking_cache_ver", nullptr, nullptr, nullptr },
{ XSLFI_EXTRA_CHEATS, XSCF_NULL, 1, 1, "extra_cheats", nullptr, nullptr, "CHTX" },
{ XSLFI_TOWN_MULTI_BUILDING, XSCF_NULL, 1, 1, "town_multi_building", nullptr, nullptr, nullptr },
{ XSLFI_SHIP_LOST_COUNTER, XSCF_NULL, 1, 1, "ship_lost_counter", nullptr, nullptr, nullptr },

@ -18,6 +18,7 @@
#include "../fios.h"
#include "../road_type.h"
#include "../core/checksum_func.hpp"
#include "../event_logs.h"
#include "saveload.h"

@ -32,7 +32,7 @@ static void RealSave_PLAN(Plan *p)
for (size_t i = 0; i < p->lines.size(); i++) {
PlanLine *pl = p->lines[i];
SlWriteUint32((uint32)pl->tiles.size());
SlArray(&pl->tiles[0], pl->tiles.size(), SLE_UINT32);
SlArray(pl->tiles.data(), pl->tiles.size(), SLE_UINT32);
}
}
@ -60,7 +60,7 @@ static void Load_PLAN()
p->lines[i] = pl;
const size_t tile_count = SlReadUint32();
pl->tiles.resize(tile_count);
SlArray(&pl->tiles[0], tile_count, SLE_UINT32);
SlArray(pl->tiles.data(), tile_count, SLE_UINT32);
pl->UpdateVisualExtents();
}
p->SetVisibility(false);
@ -80,7 +80,7 @@ static void Load_PLANLINE()
p->lines[line_index] = pl;
size_t plsz = SlGetFieldLength() / sizeof(TileIndex);
pl->tiles.resize(plsz);
SlArray(&pl->tiles[0], plsz, SLE_UINT32);
SlArray(pl->tiles.data(), plsz, SLE_UINT32);
pl->UpdateVisualExtents();
}

@ -64,7 +64,7 @@ static void Load_TRRP()
TraceRestrictProgram *prog = new (index) TraceRestrictProgram();
SlObject(&stub, _trace_restrict_program_stub_desc);
prog->items.resize(stub.length);
SlArray(&(prog->items[0]), stub.length, SLE_UINT32);
if (stub.length > 0) SlArray(prog->items.data(), stub.length, SLE_UINT32);
if (SlXvIsFeaturePresent(XSLFI_JOKERPP)) {
for (size_t i = 0; i < prog->items.size(); i++) {
TraceRestrictItem &item = prog->items[i]; // note this is a reference,
@ -99,7 +99,7 @@ static void RealSave_TRRP(TraceRestrictProgram *prog)
TraceRestrictProgramStub stub;
stub.length = (uint32)prog->items.size();
SlObject(&stub, _trace_restrict_program_stub_desc);
SlArray(&(prog->items[0]), stub.length, SLE_UINT32);
SlArray(prog->items.data(), stub.length, SLE_UINT32);
}
/**
@ -142,7 +142,7 @@ static void Load_TRRS()
SlObject(slot, _trace_restrict_slot_desc);
SlObject(&stub, _trace_restrict_slot_stub_desc);
slot->occupants.resize(stub.length);
if (stub.length) SlArray(&(slot->occupants[0]), stub.length, SLE_UINT32);
if (stub.length) SlArray(slot->occupants.data(), stub.length, SLE_UINT32);
}
TraceRestrictSlot::RebuildVehicleIndex();
}
@ -156,7 +156,7 @@ static void RealSave_TRRS(TraceRestrictSlot *slot)
TraceRestrictSlotStub stub;
stub.length = (uint32)slot->occupants.size();
SlObject(&stub, _trace_restrict_slot_stub_desc);
if (stub.length) SlArray(&(slot->occupants[0]), stub.length, SLE_UINT32);
if (stub.length) SlArray(slot->occupants.data(), stub.length, SLE_UINT32);
}
/**

@ -76,7 +76,7 @@ public:
SQInteger Peek(HSQUIRRELVM vm);
SQInteger Exists(HSQUIRRELVM vm);
SQInteger Clear(HSQUIRRELVM vm);
#endif
#endif /* DOXYGEN_API */
/**
* Check if the queue is empty.

@ -88,7 +88,7 @@ public:
* @param ... Optional arguments for this string.
*/
ScriptText(StringID string, ...);
#endif
#endif /* DOXYGEN_API */
~ScriptText();
#ifndef DOXYGEN_API

@ -1,19 +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 <http://www.gnu.org/licenses/>.
*/
/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
#include "../script_priorityqueue.hpp"
namespace SQConvert {
/* Allow ScriptPriorityQueue to be used as Squirrel parameter */
template <> inline ScriptPriorityQueue *GetParam(ForceType<ScriptPriorityQueue *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (ScriptPriorityQueue *)instance; }
template <> inline ScriptPriorityQueue &GetParam(ForceType<ScriptPriorityQueue &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptPriorityQueue *)instance; }
template <> inline const ScriptPriorityQueue *GetParam(ForceType<const ScriptPriorityQueue *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (ScriptPriorityQueue *)instance; }
template <> inline const ScriptPriorityQueue &GetParam(ForceType<const ScriptPriorityQueue &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptPriorityQueue *)instance; }
template <> inline int Return<ScriptPriorityQueue *>(HSQUIRRELVM vm, ScriptPriorityQueue *res) { if (res == nullptr) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "PriorityQueue", res, nullptr, DefSQDestructorCallback<ScriptPriorityQueue>, true); return 1; }
} // namespace SQConvert

@ -278,9 +278,12 @@ void ScriptInstance::GameLoop()
}
}
void ScriptInstance::CollectGarbage() const
void ScriptInstance::CollectGarbage()
{
if (this->is_started && !this->IsDead()) this->engine->CollectGarbage();
if (this->is_started && !this->IsDead()) {
ScriptObject::ActiveInstance active(this);
this->engine->CollectGarbage();
}
}
/* static */ void ScriptInstance::DoCommandReturn(ScriptInstance *instance)

@ -68,7 +68,7 @@ public:
/**
* Let the VM collect any garbage.
*/
void CollectGarbage() const;
void CollectGarbage();
/**
* Get the storage of this script.

@ -67,16 +67,18 @@ struct ScriptAllocator {
* @param requested_size The requested size that was requested to be allocated.
* @param p The pointer to the allocated object, or null if allocation failed.
*/
void CheckAllocation(size_t requested_size, const void *p)
void CheckAllocation(size_t requested_size, void *p)
{
if (this->allocated_size > this->allocation_limit && !this->error_thrown) {
if (this->allocated_size + requested_size > this->allocation_limit && !this->error_thrown) {
/* Do not allow allocating more than the allocation limit, except when an error is
* already as then the allocation is for throwing that error in Squirrel, the
* associated stack trace information and while cleaning up the AI. */
this->error_thrown = true;
char buff[128];
seprintf(buff, lastof(buff), "Maximum memory allocation exceeded by " PRINTF_SIZE " bytes when allocating " PRINTF_SIZE " bytes",
this->allocated_size - this->allocation_limit, requested_size);
this->allocated_size + requested_size - this->allocation_limit, requested_size);
/* Don't leak the rejected allocation. */
free(p);
throw Script_FatalError(buff);
}
@ -100,10 +102,11 @@ struct ScriptAllocator {
void *Malloc(SQUnsignedInteger size)
{
void *p = malloc(size);
this->allocated_size += size;
this->CheckAllocation(size, p);
this->allocated_size += size;
#ifdef SCRIPT_DEBUG_ALLOCATIONS
assert(p != nullptr);
assert(this->allocations.find(p) == this->allocations.end());
@ -127,14 +130,21 @@ struct ScriptAllocator {
assert(this->allocations[p] == oldsize);
this->allocations.erase(p);
#endif
/* Can't use realloc directly because memory limit check.
* If memory exception is thrown, the old pointer is expected
* to be valid for engine cleanup.
*/
void *new_p = malloc(size);
void *new_p = realloc(p, size);
this->CheckAllocation(size - oldsize, new_p);
/* Memory limit test passed, we can copy data and free old pointer. */
memcpy(new_p, p, std::min(oldsize, size));
free(p);
this->allocated_size -= oldsize;
this->allocated_size += size;
this->CheckAllocation(size, p);
#ifdef SCRIPT_DEBUG_ALLOCATIONS
assert(new_p != nullptr);
assert(this->allocations.find(p) == this->allocations.end());
@ -765,6 +775,11 @@ void Squirrel::Uninitialize()
/* Clean up the stuff */
sq_pop(this->vm, 1);
sq_close(this->vm);
assert(this->allocator->allocated_size == 0);
/* Reset memory allocation errors. */
this->allocator->error_thrown = false;
}
void Squirrel::Reset()

@ -1813,29 +1813,53 @@ static bool UpdateClientConfigValues(int32 p1)
/* End - Callback Functions */
/* Begin - GUI order callbacks */
/* Begin - xref conversion callbacks */
static int OrderTownGrowthRate(uint nth)
static int64 LinkGraphDistModeXrefChillPP(int64 val)
{
if (nth == 0) {
return 0;
} else if (nth <= 2) {
return nth - 3;
} else {
return nth - 2;
}
return val ^ 2;
}
/* End - GUI order callbacks */
/* End - xref conversion callbacks */
/* Begin - xref conversion callbacks */
/* Begin - GUI callbacks */
static int64 LinkGraphDistModeXrefChillPP(int64 val)
static bool OrderTownGrowthRate(SettingOnGuiCtrlData &data)
{
return val ^ 2;
switch (data.type) {
case SOGCT_MULTISTRING_ORDER: {
int in = data.val;
int out;
if (in == 0) {
out = 0;
} else if (in <= 2) {
out = in - 3;
} else {
out = in - 2;
}
data.val = out;
return true;
}
default:
return false;
}
}
/* End - xref conversion callbacks */
static bool LinkGraphDistributionSettingGUI(SettingOnGuiCtrlData &data)
{
switch (data.type) {
case SOGCT_DESCRIPTION_TEXT:
SetDParam(0, data.text);
data.text = STR_CONFIG_SETTING_DISTRIBUTION_HELPTEXT_EXTRA;
return true;
default:
return false;
}
}
/* End - GUI callbacks */
/**
* Prepare for reading and old diff_custom by zero-ing the memory.

@ -842,15 +842,7 @@ struct SettingEntry : BaseSettingEntry {
virtual bool UpdateFilterState(SettingFilter &filter, bool force_visible);
void SetButtons(byte new_val);
/**
* Get the help text of a single setting.
* @return The requested help text.
*/
inline StringID GetHelpText() const
{
return this->setting->desc.str_help;
}
StringID GetHelpText() const;
struct SetValueDParamsTempData {
char buffer[512];
@ -867,6 +859,24 @@ private:
bool IsVisibleByRestrictionMode(RestrictionMode mode) const;
};
/**
* Get the help text of a single setting.
* @return The requested help text.
*/
StringID SettingEntry::GetHelpText() const
{
StringID str = this->setting->desc.str_help;
if (this->setting->desc.guiproc != nullptr) {
SettingOnGuiCtrlData data;
data.type = SOGCT_DESCRIPTION_TEXT;
data.text = str;
if (this->setting->desc.guiproc(data)) {
str = data.text;
}
}
return str;
}
/** Cargodist per-cargo setting */
struct CargoDestPerCargoSettingEntry : SettingEntry {
CargoID cargo;
@ -2564,7 +2574,15 @@ struct GameSettingsWindow : Window {
DropDownList list;
if (sd->desc.flags & SGF_MULTISTRING) {
for (int i = sdb->min; i <= (int)sdb->max; i++) {
int val = sd->orderproc ? sd->orderproc(i - sdb->min) : i;
int val = i;
if (sd->desc.guiproc != nullptr) {
SettingOnGuiCtrlData data;
data.type = SOGCT_MULTISTRING_ORDER;
data.val = i - sdb->min;
if (sd->desc.guiproc(data)) {
val = data.val;
}
}
assert_msg(val >= sdb->min && val <= (int)sdb->max, "min: %d, max: %d, val: %d", sdb->min, sdb->max, val);
list.emplace_back(new DropDownListStringItem(sdb->str_val + val - sdb->min, val, false));
}

@ -82,9 +82,20 @@ enum SettingType {
ST_ALL, ///< Used in setting filter to match all types.
};
enum SettingOnGuiCtrlType {
SOGCT_DESCRIPTION_TEXT, ///< Description text callback
SOGCT_MULTISTRING_ORDER, ///< SGF_MULTISTRING reordering callback
};
struct SettingOnGuiCtrlData {
SettingOnGuiCtrlType type;
StringID text;
int val;
};
typedef bool OnChange(int32 var); ///< callback prototype on data modification
typedef size_t OnConvert(const char *value); ///< callback prototype for conversion error
typedef int OnGuiOrder(uint nth); ///< callback prototype for GUI ordering
typedef bool OnGuiCtrl(SettingOnGuiCtrlData &data); ///< callback prototype for GUI operations
typedef int64 OnXrefValueConvert(int64 val); ///< callback prototype for xref value conversion
/** The last entry in an array of struct SettingDescEnumEntry must use STR_NULL. */
@ -107,6 +118,7 @@ struct SettingDescBase {
StringID str_help; ///< (Translated) string with help text; gui only.
StringID str_val; ///< (Translated) first string describing the value.
OnChange *proc; ///< callback procedure for when the value is changed
OnGuiCtrl *guiproc; ///< callback procedure for GUI operations
OnConvert *proc_cnvt; ///< callback procedure when loading value mechanism fails
SettingCategory cat; ///< assigned categories of the setting
bool startup; ///< setting has to be loaded directly at startup?
@ -126,7 +138,6 @@ struct SettingDesc {
SaveLoad save; ///< Internal structure (going to savegame, parts to config)
const char *patx_name; ///< Name to save/load setting from in PATX chunk, if nullptr save/load from PATS chunk as normal
SettingsXref xref; ///< Details of SettingDesc to use instead of the contents of this one, useful for loading legacy savegames, if target field nullptr save/load as normal
OnGuiOrder *orderproc; ///< Callback procedure for GUI re-ordering
bool IsEditable(bool do_command = false) const;
SettingType GetType() const;

@ -16,8 +16,8 @@ static const SettingDesc _company_settings[] = {
[post-amble]
};
[templates]
SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname),
SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname, $orderproc),
SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname),
SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname),
SDT_NULL = SDT_NULL($length, $from, $to, $extver),
SDT_END = SDT_END()
@ -32,6 +32,7 @@ str = STR_NULL
strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
proc = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION
to = SL_MAX_VERSION
@ -39,7 +40,6 @@ cat = SC_ADVANCED
startup = false
extver = SlXvFeatureTest()
patxname = nullptr
orderproc = nullptr

@ -9,8 +9,8 @@ static const SettingDesc _currency_settings[] = {
[post-amble]
};
[templates]
SDT_VAR = SDT_VAR ($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr, $orderproc),
SDT_SSTR = SDT_SSTR($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr),
SDT_VAR = SDT_VAR ($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr),
SDT_SSTR = SDT_SSTR($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr),
SDT_END = SDT_END()
[validation]
@ -24,13 +24,13 @@ str = STR_NULL
strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
proc = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION
to = SL_MAX_VERSION
cat = SC_ADVANCED
startup = false
extver = SlXvFeatureTest()
orderproc = nullptr

@ -37,13 +37,13 @@ static const SettingDesc _gameopt_settings[] = {
[post-amble]
};
[templates]
SDTG_GENERAL = SDTG_GENERAL($name, $sdt_cmd, $sle_cmd, $type, $flags, $guiflags, $var, $length, $def, $min, $max, $interval, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr, $orderproc),
SDTG_GENERAL = SDTG_GENERAL($name, $sdt_cmd, $sle_cmd, $type, $flags, $guiflags, $var, $length, $def, $min, $max, $interval, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr),
SDT_NULL = SDT_NULL($length, $from, $to, $extver),
SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr),
SDT_OMANY = SDT_OMANY($base, $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $load, $cat, $startup, $extver, nullptr),
SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr, $orderproc),
SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr),
SDT_OMANY = SDT_OMANY($base, $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $load, $cat, $startup, $extver, nullptr),
SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr),
SDT_END = SDT_END()
[validation]
@ -61,13 +61,13 @@ str = STR_NULL
strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
proc = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION
to = SL_MAX_VERSION
cat = SC_ADVANCED
startup = false
extver = SlXvFeatureTest()
orderproc = nullptr

@ -20,12 +20,12 @@ static const SettingDesc _misc_settings[] = {
[post-amble]
};
[templates]
SDTG_LIST = SDTG_LIST($name, $type, $length, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_MMANY = SDTG_MMANY($name, $type, $flags, $guiflags, $var, $def, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_SSTR = SDTG_SSTR($name, $type, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr, $orderproc),
SDTG_LIST = SDTG_LIST($name, $type, $length, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_MMANY = SDTG_MMANY($name, $type, $flags, $guiflags, $var, $def, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_SSTR = SDTG_SSTR($name, $type, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_END = SDTG_END()
[validation]
@ -40,13 +40,13 @@ str = STR_NULL
strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
proc = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION
to = SL_MAX_VERSION
cat = SC_ADVANCED
startup = true
extver = SlXvFeatureTest()
orderproc = nullptr

@ -502,7 +502,7 @@ class NIHHouse : public NIHelper {
print(buffer);
seprintf(buffer, lastof(buffer), " extra_flags: 0x%X", hs->extra_flags);
print(buffer);
seprintf(buffer, lastof(buffer), " remove_rating_decrease: %u", hs->remove_rating_decrease);
seprintf(buffer, lastof(buffer), " remove_rating_decrease: %u, minimum_life: %u", hs->remove_rating_decrease, hs->minimum_life);
print(buffer);
seprintf(buffer, lastof(buffer), " population: %u, mail_generation: %u", hs->population, hs->mail_generation);
print(buffer);
@ -1149,6 +1149,8 @@ class NIHStationStruct : public NIHelper {
print(buffer);
seprintf(buffer, lastof(buffer), " Delete counter: %u", st->delete_ctr);
print(buffer);
seprintf(buffer, lastof(buffer), " Docking tiles: %X, %u x %u", st->docking_station.tile, st->docking_station.w, st->docking_station.h);
print(buffer);
}
}
};

@ -54,105 +54,105 @@ static size_t ConvertLandscape(const char *value);
* on the appropriate macro.
*/
#define NSD_GENERAL(name, def, cmd, guiflags, min, max, interval, many, str, strhelp, strval, proc, load, cat, startup, enumlist)\
{name, (const void*)(size_t)(def), cmd, guiflags, min, max, interval, many, str, strhelp, strval, proc, load, cat, startup, enumlist}
#define NSD_GENERAL(name, def, cmd, guiflags, min, max, interval, many, str, strhelp, strval, proc, guiproc, load, cat, startup, enumlist)\
{name, (const void*)(size_t)(def), cmd, guiflags, min, max, interval, many, str, strhelp, strval, proc, guiproc, load, cat, startup, enumlist}
/* Macros for various objects to go in the configuration file.
* This section is for global variables */
#define SDTG_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname, orderproc, enumlist)\
{NSD_GENERAL(name, def, sdt_cmd, guiflags, min, max, interval, full, str, strhelp, strval, proc, nullptr, cat, startup, enumlist), SLEG_GENERAL_X(sle_cmd, var, type | flags, length, from, to, extver), patxname, SettingsXref(), orderproc}
#define SDTG_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname, enumlist)\
{NSD_GENERAL(name, def, sdt_cmd, guiflags, min, max, interval, full, str, strhelp, strval, proc, guiproc, nullptr, cat, startup, enumlist), SLEG_GENERAL_X(sle_cmd, var, type | flags, length, from, to, extver), patxname, SettingsXref()}
#define SDTG_GENERAL(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname, nullptr, nullptr)
#define SDTG_GENERAL(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname, nullptr)
#define SDTG_VAR(name, type, flags, guiflags, var, def, min, max, interval, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname, orderproc)\
SDTG_GENERAL2(name, SDT_NUMX, SL_VAR, type, flags, guiflags, var, 0, def, min, max, interval, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname, orderproc, nullptr)
#define SDTG_VAR(name, type, flags, guiflags, var, def, min, max, interval, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL2(name, SDT_NUMX, SL_VAR, type, flags, guiflags, var, 0, def, min, max, interval, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname, nullptr)
#define SDTG_ENUM(name, type, flags, guiflags, var, def, str, strhelp, proc, from, to, cat, startup, extver, patxname, enumlist)\
SDTG_GENERAL2(name, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, var, 0, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, nullptr, from, to, cat, startup, extver, patxname, nullptr, enumlist)
#define SDTG_ENUM(name, type, flags, guiflags, var, def, str, strhelp, proc, guiproc, from, to, cat, startup, extver, patxname, enumlist)\
SDTG_GENERAL2(name, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, var, 0, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname, enumlist)
#define SDTG_BOOL(name, flags, guiflags, var, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(name, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, var, 0, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)
#define SDTG_BOOL(name, flags, guiflags, var, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(name, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, var, 0, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)
#define SDTG_LIST(name, type, length, flags, guiflags, var, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(name, SDT_INTLIST, SL_ARR, type, flags, guiflags, var, length, def, 0, 0, 0, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)
#define SDTG_LIST(name, type, length, flags, guiflags, var, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(name, SDT_INTLIST, SL_ARR, type, flags, guiflags, var, length, def, 0, 0, 0, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)
#define SDTG_SSTR(name, type, flags, guiflags, var, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(name, SDT_STDSTRING, SL_STDSTR, type, flags, guiflags, var, sizeof(var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)
#define SDTG_SSTR(name, type, flags, guiflags, var, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(name, SDT_STDSTRING, SL_STDSTR, type, flags, guiflags, var, sizeof(var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)
#define SDTG_STR(name, type, flags, guiflags, var, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(name, SDT_STRING, SL_STR, type, flags, guiflags, var, sizeof(var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)
#define SDTG_STR(name, type, flags, guiflags, var, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(name, SDT_STRING, SL_STR, type, flags, guiflags, var, sizeof(var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)
#define SDTG_OMANY(name, type, flags, guiflags, var, def, max, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(name, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, var, 0, def, 0, max, 0, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)
#define SDTG_OMANY(name, type, flags, guiflags, var, def, max, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(name, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, var, 0, def, 0, max, 0, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)
#define SDTG_MMANY(name, type, flags, guiflags, var, def, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(name, SDT_MANYOFMANY, SL_VAR, type, flags, guiflags, var, 0, def, 0, 0, 0, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)
#define SDTG_MMANY(name, type, flags, guiflags, var, def, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(name, SDT_MANYOFMANY, SL_VAR, type, flags, guiflags, var, 0, def, 0, 0, 0, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)
#define SDTG_NULL(length, from, to, extver)\
{{"", nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, false, nullptr}, SLEG_NULL_X(length, from, to, extver), nullptr, SettingsXref(), nullptr}
{{"", nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, nullptr, SC_NONE, false, nullptr}, SLEG_NULL_X(length, from, to, extver), nullptr, SettingsXref()}
#define SDTG_END() {{nullptr, nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, false, nullptr}, SLEG_END(), nullptr, SettingsXref(), nullptr}
#define SDTG_END() {{nullptr, nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, nullptr, SC_NONE, false, nullptr}, SLEG_END(), nullptr, SettingsXref()}
/* Macros for various objects to go in the configuration file.
* This section is for structures where their various members are saved */
#define SDT_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, load, from, to, cat, startup, extver, patxname, orderproc, enumlist)\
{NSD_GENERAL(name, def, sdt_cmd, guiflags, min, max, interval, full, str, strhelp, strval, proc, load, cat, startup, enumlist), SLE_GENERAL_X(sle_cmd, base, var, type | flags, length, from, to, extver), patxname, SettingsXref(), orderproc}
#define SDT_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, guiproc, load, from, to, cat, startup, extver, patxname, enumlist)\
{NSD_GENERAL(name, def, sdt_cmd, guiflags, min, max, interval, full, str, strhelp, strval, proc, guiproc, load, cat, startup, enumlist), SLE_GENERAL_X(sle_cmd, base, var, type | flags, length, from, to, extver), patxname, SettingsXref()}
#define SDT_GENERAL(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, load, from, to, cat, startup, extver, patxname)\
SDT_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, load, from, to, cat, startup, extver, patxname, nullptr, nullptr)
#define SDT_GENERAL(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, guiproc, load, from, to, cat, startup, extver, patxname)\
SDT_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, guiproc, load, from, to, cat, startup, extver, patxname, nullptr)
#define SDT_VAR(base, var, type, flags, guiflags, def, min, max, interval, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname, orderproc)\
SDT_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags, base, var, 1, def, min, max, interval, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, startup, extver, patxname, orderproc, nullptr)
#define SDT_VAR(base, var, type, flags, guiflags, def, min, max, interval, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDT_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags, base, var, 1, def, min, max, interval, nullptr, str, strhelp, strval, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname, nullptr)
#define SDT_ENUM(base, var, type, flags, guiflags, def, str, strhelp, proc, from, to, cat, startup, extver, patxname, enumlist)\
SDT_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, base, var, 1, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, nullptr, from, to, cat, startup, extver, patxname, nullptr, enumlist)
#define SDT_ENUM(base, var, type, flags, guiflags, def, str, strhelp, proc, guiproc, from, to, cat, startup, extver, patxname, enumlist)\
SDT_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, base, var, 1, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname, enumlist)
#define SDT_BOOL(base, var, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\
SDT_GENERAL(#var, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, base, var, 1, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, startup, extver, patxname)
#define SDT_BOOL(base, var, flags, guiflags, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDT_GENERAL(#var, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, base, var, 1, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname)
#define SDT_LIST(base, var, type, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\
SDT_GENERAL(#var, SDT_INTLIST, SL_ARR, type, flags, guiflags, base, var, lengthof(((base*)8)->var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, startup, extver, patxname)
#define SDT_LIST(base, var, type, flags, guiflags, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDT_GENERAL(#var, SDT_INTLIST, SL_ARR, type, flags, guiflags, base, var, lengthof(((base*)8)->var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname)
#define SDT_STR(base, var, type, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\
SDT_GENERAL(#var, SDT_STRING, SL_STR, type, flags, guiflags, base, var, sizeof(((base*)8)->var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, startup, extver, patxname)
#define SDT_STR(base, var, type, flags, guiflags, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDT_GENERAL(#var, SDT_STRING, SL_STR, type, flags, guiflags, base, var, sizeof(((base*)8)->var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname)
#define SDT_SSTR(base, var, type, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\
SDT_GENERAL(#var, SDT_STDSTRING, SL_STDSTR, type, flags, guiflags, base, var, sizeof(((base*)8)->var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, startup, extver, patxname)
#define SDT_SSTR(base, var, type, flags, guiflags, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDT_GENERAL(#var, SDT_STDSTRING, SL_STDSTR, type, flags, guiflags, base, var, sizeof(((base*)8)->var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname)
#define SDT_OMANY(base, var, type, flags, guiflags, def, max, full, str, strhelp, strval, proc, from, to, load, cat, startup, extver, patxname)\
SDT_GENERAL(#var, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, base, var, 1, def, 0, max, 0, full, str, strhelp, strval, proc, load, from, to, cat, startup, extver, patxname)
#define SDT_OMANY(base, var, type, flags, guiflags, def, max, full, str, strhelp, strval, proc, guiproc, from, to, load, cat, startup, extver, patxname)\
SDT_GENERAL(#var, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, base, var, 1, def, 0, max, 0, full, str, strhelp, strval, proc, guiproc, load, from, to, cat, startup, extver, patxname)
#define SDT_MMANY(base, var, type, flags, guiflags, def, full, str, proc, strhelp, strval, from, to, cat, startup, extver, patxname)\
SDT_GENERAL(#var, SDT_MANYOFMANY, SL_VAR, type, flags, guiflags, base, var, 1, def, 0, 0, 0, full, str, strhelp, strval, proc, nullptr, from, to, cat, startup, extver, patxname)
#define SDT_MMANY(base, var, type, flags, guiflags, def, full, str, proc, guiproc, strhelp, strval, from, to, cat, startup, extver, patxname)\
SDT_GENERAL(#var, SDT_MANYOFMANY, SL_VAR, type, flags, guiflags, base, var, 1, def, 0, 0, 0, full, str, strhelp, strval, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname)
#define SDT_NULL(length, from, to, extver)\
{{"", nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, false, nullptr}, SLE_CONDNULL_X(length, from, to, extver), nullptr, SettingsXref(), nullptr}
{{"", nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, nullptr, SC_NONE, false, nullptr}, SLE_CONDNULL_X(length, from, to, extver), nullptr, SettingsXref()}
#define SDTC_VAR(var, type, flags, guiflags, def, min, max, interval, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname, orderproc)\
SDTG_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags, _settings_client.var, 1, def, min, max, interval, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname, orderproc, nullptr)
#define SDTC_VAR(var, type, flags, guiflags, def, min, max, interval, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags, _settings_client.var, 1, def, min, max, interval, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname, nullptr)
#define SDTC_ENUM(var, type, flags, guiflags, def, str, strhelp, proc, from, to, cat, startup, extver, patxname, enumlist)\
SDTG_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, _settings_client.var, 1, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, nullptr, from, to, cat, startup, extver, patxname, nullptr, enumlist)
#define SDTC_ENUM(var, type, flags, guiflags, def, str, strhelp, proc, guiproc, from, to, cat, startup, extver, patxname, enumlist)\
SDTG_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, _settings_client.var, 1, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname,, enumlist)
#define SDTC_BOOL(var, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(#var, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, _settings_client.var, 1, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)
#define SDTC_BOOL(var, flags, guiflags, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(#var, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, _settings_client.var, 1, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)
#define SDTC_LIST(var, type, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(#var, SDT_INTLIST, SL_ARR, type, flags, guiflags, _settings_client.var, lengthof(_settings_client.var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)
#define SDTC_LIST(var, type, flags, guiflags, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(#var, SDT_INTLIST, SL_ARR, type, flags, guiflags, _settings_client.var, lengthof(_settings_client.var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)
#define SDTC_STR(var, type, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(#var, SDT_STRING, SL_STR, type, flags, guiflags, _settings_client.var, sizeof(_settings_client.var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)
#define SDTC_STR(var, type, flags, guiflags, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(#var, SDT_STRING, SL_STR, type, flags, guiflags, _settings_client.var, sizeof(_settings_client.var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)
#define SDTC_SSTR(var, type, flags, guiflags, def, max_length, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(#var, SDT_STDSTRING, SL_STDSTR, type, flags, guiflags, _settings_client.var, sizeof(_settings_client.var), def, 0, max_length, 0, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)
#define SDTC_SSTR(var, type, flags, guiflags, def, max_length, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(#var, SDT_STDSTRING, SL_STDSTR, type, flags, guiflags, _settings_client.var, sizeof(_settings_client.var), def, 0, max_length, 0, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)
#define SDTC_OMANY(var, type, flags, guiflags, def, max, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(#var, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, _settings_client.var, 1, def, 0, max, 0, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)
#define SDTC_OMANY(var, type, flags, guiflags, def, max, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\
SDTG_GENERAL(#var, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, _settings_client.var, 1, def, 0, max, 0, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)
#define SDT_XREF(from, to, extver, xref, xrefcvt)\
{{"", nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, false, nullptr}, SLE_CONDNULL_X(0, from, to, extver), nullptr, SettingsXref(xref, xrefcvt), nullptr}
{{"", nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, nullptr, SC_NONE, false, nullptr}, SLE_CONDNULL_X(0, from, to, extver), nullptr, SettingsXref(xref, xrefcvt)}
#define SDT_END() {{nullptr, nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, false, nullptr}, SLE_END(), nullptr, SettingsXref(), nullptr}
#define SDT_END() {{nullptr, nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, nullptr, SC_NONE, false, nullptr}, SLE_END(), nullptr, SettingsXref()}

@ -79,18 +79,19 @@ static bool CheckSharingAir(int32 p1);
/* End - Callback Functions for the various settings */
/* Begin - GUI order callbacks */
static int OrderTownGrowthRate(uint nth);
/* End - GUI order callbacks */
/* Begin - xref conversion callbacks */
static int64 LinkGraphDistModeXrefChillPP(int64 val);
/* End - xref conversion callbacks */
/* Begin - GUI callbacks */
static bool LinkGraphDistributionSettingGUI(SettingOnGuiCtrlData &data);
static bool OrderTownGrowthRate(SettingOnGuiCtrlData &data);
/* End - GUI callbacks */
static const SettingDescEnumEntry _linkgraph_mode_symmetric[] = {
{ DT_MANUAL, STR_CONFIG_SETTING_DISTRIBUTION_MANUAL },
{ DT_SYMMETRIC, STR_CONFIG_SETTING_DISTRIBUTION_SYMMETRIC },
@ -142,25 +143,25 @@ const SettingDesc _settings[] = {
[post-amble]
};
[templates]
SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname),
SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname, $orderproc),
SDTG_ENUM = SDTG_ENUM($name, $type, $flags, $guiflags, $var, $def, $str, $strhelp, $proc, $from, $to, $cat, $startup, $extver, $patxname, $enumlist),
SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname),
SDTC_BOOL = SDTC_BOOL( $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname),
SDTC_LIST = SDTC_LIST( $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname),
SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname),
SDTC_SSTR = SDTC_SSTR( $var, $type, $flags, $guiflags, $def, $length, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname),
SDTC_VAR = SDTC_VAR( $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname, $orderproc),
SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname),
SDT_OMANY = SDT_OMANY($base, $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $load, $cat, $startup, $extver, $patxname),
SDT_SSTR = SDT_SSTR($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname),
SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname, $orderproc),
SDT_ENUM = SDT_ENUM($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $proc, $from, $to, $cat, $startup, $extver, $patxname, $enumlist),
SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname),
SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname),
SDTG_ENUM = SDTG_ENUM($name, $type, $flags, $guiflags, $var, $def, $str, $strhelp, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname, $enumlist),
SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname),
SDTC_BOOL = SDTC_BOOL( $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname),
SDTC_LIST = SDTC_LIST( $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname),
SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname),
SDTC_SSTR = SDTC_SSTR( $var, $type, $flags, $guiflags, $def, $length, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname),
SDTC_VAR = SDTC_VAR( $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname),
SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname),
SDT_OMANY = SDT_OMANY($base, $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $load, $cat, $startup, $extver, $patxname),
SDT_SSTR = SDT_SSTR($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname),
SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname),
SDT_ENUM = SDT_ENUM($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname, $enumlist),
SDT_NULL = SDT_NULL($length, $from, $to, $extver),
SDT_XREF = SDT_XREF( $from, $to, $extver, $xref, $xrefcvt),
SDT_END = SDT_END()
SDT_LINKGRAPH_PER_CARGO = SDT_ENUM(GameSettings, linkgraph.distribution_per_cargo[$linkgraph_cargo], SLE_UINT8, $flags | SLF_NOT_IN_CONFIG, $guiflags | SGF_NO_NEWGAME, DT_PER_CARGO_DEFAULT, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO_HELPTEXT, $proc, $from, $to, SC_EXPERT, false, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_MODES), nullptr, _linkgraph_mode_per_cargo),
SDT_LINKGRAPH_PER_CARGO = SDT_ENUM(GameSettings, linkgraph.distribution_per_cargo[$linkgraph_cargo], SLE_UINT8, $flags | SLF_NOT_IN_CONFIG, $guiflags | SGF_NO_NEWGAME, DT_PER_CARGO_DEFAULT, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO_HELPTEXT, $proc, LinkGraphDistributionSettingGUI, $from, $to, SC_EXPERT, false, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_MODES), nullptr, _linkgraph_mode_per_cargo),
[validation]
SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
@ -178,6 +179,7 @@ str = STR_NULL
strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
proc = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION
to = SL_MAX_VERSION
@ -187,7 +189,6 @@ extver = SlXvFeatureTest()
patxname = nullptr
xref = <this parameter must be set>
xrefcvt = nullptr
orderproc = nullptr
enumlist = <this parameter must be set>
@ -1006,6 +1007,7 @@ def = DT_MANUAL
enumlist = _linkgraph_mode_symmetric
str = STR_CONFIG_SETTING_DISTRIBUTION_PAX
strhelp = STR_CONFIG_SETTING_DISTRIBUTION_PAX_HELPTEXT
guiproc = LinkGraphDistributionSettingGUI
[SDT_ENUM]
base = GameSettings
@ -1016,6 +1018,7 @@ def = DT_MANUAL
enumlist = _linkgraph_mode_symmetric
str = STR_CONFIG_SETTING_DISTRIBUTION_MAIL
strhelp = STR_CONFIG_SETTING_DISTRIBUTION_MAIL_HELPTEXT
guiproc = LinkGraphDistributionSettingGUI
[SDT_ENUM]
base = GameSettings
@ -1026,6 +1029,7 @@ def = DT_MANUAL
enumlist = _linkgraph_mode_symmetric
str = STR_CONFIG_SETTING_DISTRIBUTION_ARMOURED
strhelp = STR_CONFIG_SETTING_DISTRIBUTION_ARMOURED_HELPTEXT
guiproc = LinkGraphDistributionSettingGUI
[SDT_ENUM]
base = GameSettings
@ -1036,6 +1040,7 @@ def = DT_MANUAL
enumlist = _linkgraph_mode_asymmetric
str = STR_CONFIG_SETTING_DISTRIBUTION_DEFAULT
strhelp = STR_CONFIG_SETTING_DISTRIBUTION_DEFAULT_HELPTEXT
guiproc = LinkGraphDistributionSettingGUI
[SDT_LINKGRAPH_PER_CARGO]
linkgraph_cargo = 0
@ -2498,7 +2503,7 @@ max = 4
str = STR_CONFIG_SETTING_TOWN_GROWTH
strhelp = STR_CONFIG_SETTING_TOWN_GROWTH_HELPTEXT
strval = STR_CONFIG_SETTING_TOWN_GROWTH_EXTREME_SLOW
orderproc = OrderTownGrowthRate
guiproc = OrderTownGrowthRate
[SDT_BOOL]
base = GameSettings

@ -14,8 +14,8 @@ static const SettingDesc _win32_settings[] = {
};
#endif /* _WIN32 */
[templates]
SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr, $orderproc),
SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr),
SDTG_END = SDTG_END()
[validation]
@ -29,13 +29,13 @@ str = STR_NULL
strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
proc = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION
to = SL_MAX_VERSION
cat = SC_ADVANCED
startup = true
extver = SlXvFeatureTest()
orderproc = nullptr
[SDTG_BOOL]

@ -10,8 +10,8 @@ static const SettingDesc _window_settings[] = {
[post-amble]
};
[templates]
SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr),
SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr, $orderproc),
SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr),
SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr),
SDT_END = SDT_END()
[validation]
@ -26,13 +26,13 @@ str = STR_NULL
strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
proc = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION
to = SL_MAX_VERSION
cat = SC_ADVANCED
startup = false
extver = SlXvFeatureTest()
orderproc = nullptr

@ -14,6 +14,7 @@
#include "crashlog.h"
#include <system_error>
#include <thread>
#include <mutex>
#if defined(__MINGW32__)
#include "3rdparty/mingw-std-threads/mingw.thread.h"
#endif
@ -97,7 +98,17 @@ inline bool StartNewThread(std::thread *thr, const char *name, TFn&& _Fx, TArgs&
{
#ifndef NO_THREADS
try {
static std::mutex thread_startup_mutex;
std::lock_guard<std::mutex> lock(thread_startup_mutex);
std::thread t([] (const char *name, TFn&& F, TArgs&&... A) {
/* Delay starting the thread till the main thread is finished
* with the administration. This prevent race-conditions on
* startup. */
{
std::lock_guard<std::mutex> lock(thread_startup_mutex);
}
SetCurrentThreadName(name);
PerThreadSetup();
CrashLog::InitThread();

@ -45,6 +45,7 @@
#include "core/checksum_func.hpp"
#include "debug_settings.h"
#include "train_speed_adaptation.h"
#include "event_logs.h"
#include "table/strings.h"
#include "table/train_cmd.h"

@ -66,6 +66,7 @@ static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleLengthSorter;
static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleTimeToLiveSorter;
static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleTimetableDelaySorter;
static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleAverageOrderOccupancySorter;
static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleMaxSpeedLoadedSorter;
static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupLengthSorter;
static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupTotalProfitThisYearSorter;
static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupTotalProfitLastYearSorter;
@ -96,6 +97,7 @@ enum VehicleSortType
VST_TIME_TO_LIVE,
VST_TIMETABLE_DELAY,
VST_AVERAGE_ORDER_OCCUPANCY,
VST_MAX_SPEED_LOADED,
};
BaseVehicleListWindow::VehicleGroupSortFunction * const BaseVehicleListWindow::vehicle_group_none_sorter_funcs[] = {
@ -114,6 +116,7 @@ BaseVehicleListWindow::VehicleGroupSortFunction * const BaseVehicleListWindow::v
&VehicleIndividualToGroupSorterWrapper<VehicleTimeToLiveSorter>,
&VehicleIndividualToGroupSorterWrapper<VehicleTimetableDelaySorter>,
&VehicleIndividualToGroupSorterWrapper<VehicleAverageOrderOccupancySorter>,
&VehicleIndividualToGroupSorterWrapper<VehicleMaxSpeedLoadedSorter>,
};
const StringID BaseVehicleListWindow::vehicle_group_none_sorter_names[] = {
@ -132,6 +135,7 @@ const StringID BaseVehicleListWindow::vehicle_group_none_sorter_names[] = {
STR_SORT_BY_LIFE_TIME,
STR_SORT_BY_TIMETABLE_DELAY,
STR_SORT_BY_AVG_ORDER_OCCUPANCY,
STR_SORT_BY_MAX_SPEED_LOADED,
INVALID_STRING_ID
};
@ -477,12 +481,15 @@ DropDownList BaseVehicleListWindow::BuildActionDropdownList(bool show_autoreplac
/* cached values for VehicleNameSorter to spare many GetString() calls */
static const Vehicle *_last_vehicle[2] = { nullptr, nullptr };
static btree::btree_map<VehicleID, int> _vehicle_max_speed_loaded;
void BaseVehicleListWindow::SortVehicleList()
{
if (this->vehgroups.Sort()) return;
/* invalidate cached values for name sorter - vehicle names could change */
_last_vehicle[0] = _last_vehicle[1] = nullptr;
_vehicle_max_speed_loaded.clear();
}
void DepotSortList(VehicleList *list)
@ -1606,6 +1613,29 @@ static bool VehicleAverageOrderOccupancySorter(const Vehicle * const &a, const V
return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
}
/** Sort vehicles by the max speed (fully loaded) */
static bool VehicleMaxSpeedLoadedSorter(const Vehicle * const &a, const Vehicle * const &b)
{
auto get_max_speed_loaded = [](const Train * const v) -> int {
auto res = _vehicle_max_speed_loaded.insert({ v->index, 0 });
if (!res.second) {
/* This vehicle's speed was already in _vehicle_max_speed_loaded */
return res.first->second;
}
int loaded_weight = 0;
for (const Train *u = v; u != nullptr; u = u->Next()) {
loaded_weight += u->GetWeightWithoutCargo() + u->GetCargoWeight(u->cargo_cap);
}
int loaded_max_speed = GetTrainEstimatedMaxAchievableSpeed(v, loaded_weight, v->GetDisplayMaxSpeed());
res.first->second = loaded_max_speed;
return loaded_max_speed;
};
int r = get_max_speed_loaded(Train::From(a)) - get_max_speed_loaded(Train::From(b));
return (r != 0) ? r < 0 : VehicleNumberSorter(a, b);
}
void InitializeGUI()
{
MemSetT(&_grouping, 0);
@ -2034,6 +2064,16 @@ void BaseVehicleListWindow::UpdateVehicleGroupBy(GroupBy group_by)
}
}
uint BaseVehicleListWindow::GetSorterDisableMask(VehicleType type) const
{
uint mask = 0;
if (this->grouping == GB_NONE) {
if (type != VEH_TRAIN && type != VEH_ROAD) mask |= (1 << VST_LENGTH);
if (type != VEH_TRAIN || _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) mask |= (1 << VST_MAX_SPEED_LOADED);
}
return mask;
}
/**
* Window for the (old) vehicle listing.
*
@ -2250,7 +2290,7 @@ public:
case WID_VL_SORT_BY_PULLDOWN: // Select sorting criteria dropdown menu
ShowDropDownMenu(this, this->GetVehicleSorterNames(), this->vehgroups.SortType(), WID_VL_SORT_BY_PULLDOWN, 0,
(this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : this->vehicle_sorter_non_ground_veh_disable_mask, 0, DDSF_LOST_FOCUS);
this->GetSorterDisableMask(this->vli.vtype), 0, DDSF_LOST_FOCUS);
return;
case WID_VL_FILTER_BY_CARGO: // Cargo filter dropdown

@ -127,8 +127,6 @@ public:
static VehicleGroupSortFunction * const vehicle_group_none_sorter_funcs[];
static VehicleGroupSortFunction * const vehicle_group_shared_orders_sorter_funcs[];
const uint vehicle_sorter_non_ground_veh_disable_mask = (1 << 11); // STR_SORT_BY_LENGTH
BaseVehicleListWindow(WindowDesc *desc, WindowNumber wno);
void OnInit() override;
@ -173,6 +171,8 @@ public:
NOT_REACHED();
}
}
uint GetSorterDisableMask(VehicleType type) const;
};
uint GetVehicleListHeight(VehicleType type, uint divisor = 1);

@ -10,16 +10,19 @@
#include "../stdafx.h"
#include "../gfx_func.h"
#include "../blitter/factory.hpp"
#include "../saveload/saveload.h"
#include "../window_func.h"
#include "../thread.h"
#include "null_v.h"
#include <atomic>
#include "../safeguards.h"
/** Factory for the null video driver. */
static FVideoDriver_Null iFVideoDriver_Null;
extern bool _exit_game;
extern std::atomic<bool> _exit_game;
const char *VideoDriver_Null::Start(const StringList &parm)
{
@ -63,6 +66,12 @@ void VideoDriver_Null::MainLoop()
::UpdateWindows();
}
}
/* If requested, make a save just before exit. The normal exit-flow is
* not triggered from this driver, so we have to do this manually. */
if (_settings_client.gui.autosave_on_exit) {
DoExitSave();
}
}
bool VideoDriver_Null::ChangeResolution(int w, int h) { return false; }

Loading…
Cancel
Save