From 341941af852d2a07c59d4c17247b294026878b30 Mon Sep 17 00:00:00 2001 From: glx Date: Tue, 21 Jan 2020 20:59:01 +0100 Subject: [PATCH 1/9] Fix #7952: don't try to access destroyed QueryStrings --- src/window.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/window.cpp b/src/window.cpp index 93777d28f6..30fa4d3d12 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -389,7 +389,7 @@ QueryString *Window::GetQueryString(uint widnum) */ /* virtual */ Point Window::GetCaretPosition() const { - if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) { + if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX && !this->querystrings.empty()) { return this->GetQueryString(this->nested_focus->index)->GetCaretPosition(this, this->nested_focus->index); } @@ -1094,6 +1094,9 @@ Window::~Window() /* We can't scroll the window when it's closed. */ if (_last_scroll_window == this) _last_scroll_window = nullptr; + /* Make sure we don't try to access non-existing query strings. */ + this->querystrings.clear(); + /* Make sure we don't try to access this window as the focused window when it doesn't exist anymore. */ if (_focused_window == this) { this->OnFocusLost(); From f88ac83408bff58022699b4d9488818d509ef974 Mon Sep 17 00:00:00 2001 From: translators Date: Fri, 24 Jan 2020 19:45:40 +0100 Subject: [PATCH 2/9] Update: Translations from eints indonesian: 1 change by fanioz --- src/lang/indonesian.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lang/indonesian.txt b/src/lang/indonesian.txt index 37ffc49af9..45d98ef355 100644 --- a/src/lang/indonesian.txt +++ b/src/lang/indonesian.txt @@ -3298,6 +3298,7 @@ STR_COMPANY_INFRASTRUCTURE_VIEW_CAPTION :{WHITE}Infrastr STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT :{GOLD}Bagian rel: STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS :{WHITE}Sinyal STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT :{GOLD}Bagian jalan: +STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT :{GOLD}Bagian Trem: STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT :{GOLD}Perairan: STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS :{WHITE}Kanal STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT :{GOLD}Stasiun: From c8779fb311c2665d3fc45c18b2f3460cd998d179 Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Sun, 26 Jan 2020 13:45:51 +0100 Subject: [PATCH 3/9] Feature: NewGRF callback profiling (#7868) Adds a console command newgrf_profile to collect some profiling data about NewGRF action 2 callbacks and produce a CSV file. --- docs/logging_and_performance_metrics.md | 40 ++++++ projects/openttd_vs140.vcxproj | 2 + projects/openttd_vs140.vcxproj.filters | 6 + projects/openttd_vs141.vcxproj | 2 + projects/openttd_vs141.vcxproj.filters | 6 + projects/openttd_vs142.vcxproj | 2 + projects/openttd_vs142.vcxproj.filters | 6 + source.list | 2 + src/console_cmds.cpp | 131 +++++++++++++++++++ src/date.cpp | 5 + src/misc.cpp | 3 + src/newgrf.cpp | 10 ++ src/newgrf_airport.cpp | 13 ++ src/newgrf_airporttiles.cpp | 10 ++ src/newgrf_airporttiles.h | 6 +- src/newgrf_canal.cpp | 17 ++- src/newgrf_cargo.cpp | 17 ++- src/newgrf_engine.cpp | 16 +++ src/newgrf_engine.h | 3 + src/newgrf_generic.cpp | 16 ++- src/newgrf_house.cpp | 10 ++ src/newgrf_house.h | 3 + src/newgrf_industries.cpp | 10 ++ src/newgrf_industries.h | 3 + src/newgrf_industrytiles.cpp | 13 +- src/newgrf_industrytiles.h | 4 + src/newgrf_object.cpp | 12 +- src/newgrf_object.h | 14 +- src/newgrf_profiling.cpp | 162 ++++++++++++++++++++++++ src/newgrf_profiling.h | 63 +++++++++ src/newgrf_railtype.cpp | 12 +- src/newgrf_railtype.h | 8 +- src/newgrf_roadtype.cpp | 20 ++- src/newgrf_roadtype.h | 6 +- src/newgrf_spritegroup.cpp | 18 ++- src/newgrf_spritegroup.h | 15 ++- src/newgrf_station.cpp | 10 ++ src/newgrf_station.h | 3 + src/spritecache.cpp | 11 ++ src/spritecache.h | 1 + 40 files changed, 691 insertions(+), 20 deletions(-) create mode 100644 src/newgrf_profiling.cpp create mode 100644 src/newgrf_profiling.h diff --git a/docs/logging_and_performance_metrics.md b/docs/logging_and_performance_metrics.md index 9f0679acde..1f5866aba0 100644 --- a/docs/logging_and_performance_metrics.md +++ b/docs/logging_and_performance_metrics.md @@ -99,3 +99,43 @@ The following is an explanation of the different statistics: If the frame rate window is shaded, the title bar will instead show just the current simulation rate and the game speed factor. +## 3.0) NewGRF callback profiling + +NewGRF developers can profile callback chains via the `newgrf_profile` +console command. The command controls a profiling mode where every sprite +request is measured and logged, and written to a CSV file in the end. + +The NewGRF developer tools need to be enabled for the command to function. + +View the syntax for the command in-game with the console command +`help newgrf_profile`. + +Profiling only works during game or in the editor, it's not possible to +profile across the main menu, world generation, or loading savegames. + +The CSV files contain one line per sprite request during the profiling. +They can get very large, especially on large games with many objects from +the GRF. Start profiling short periods such as 3 or 7 days, and watch the +file sizes. + +The produced CSV file contains the following fields: + +- *Tick* - Game tick counter, this may wrap to zero during recording. + Mainly useful to distinguish events from separate ticks. +- *Sprite* - Index of the root Action 2 sprite in the GRF file. This is + the sprite group being resolved. +- *Feature* - NewGRF feature number the sprite group is being resolved for. + This will be 0xFF for AI purchase selection and ambient sound callbacks. +- *Item* - The id of the item within the GRF. For cargotypes, railtypes, + roadtypes, and tramtypes, this is the integer representation of the label. +- *CallbackID* - The type of callback being resolved. ID 0 is regular graphics + lookup. See the `newgrf_callbacks.h` file in the OpenTTD source code for the + full list of callback IDs. +- *Microseconds* - Total time spent to resolve the Action 2, in microseconds. +- *Depth* - Number of recursive Action 2 lookups were made during resolution. + Value zero means the sprite group resolved directly. +- *Result* - Result of the callback resolution. For lookups that result in + a sprite, this is the index of the base action 2 in the GRF file. For + callbacks that give a numeric result, this is the callback result value. + For lookups that result in an industry production or tilelayout, this + is the sprite index of the action 2 defining the production/tilelayout. diff --git a/projects/openttd_vs140.vcxproj b/projects/openttd_vs140.vcxproj index 6c4bef5cea..6453d62b86 100644 --- a/projects/openttd_vs140.vcxproj +++ b/projects/openttd_vs140.vcxproj @@ -583,6 +583,7 @@ + @@ -1236,6 +1237,7 @@ + diff --git a/projects/openttd_vs140.vcxproj.filters b/projects/openttd_vs140.vcxproj.filters index 45753b1b6d..0405740d4c 100644 --- a/projects/openttd_vs140.vcxproj.filters +++ b/projects/openttd_vs140.vcxproj.filters @@ -837,6 +837,9 @@ Header Files + + Header Files + Header Files @@ -2796,6 +2799,9 @@ NewGRF + + NewGRF + NewGRF diff --git a/projects/openttd_vs141.vcxproj b/projects/openttd_vs141.vcxproj index a59eec0c64..ecfed4a038 100644 --- a/projects/openttd_vs141.vcxproj +++ b/projects/openttd_vs141.vcxproj @@ -583,6 +583,7 @@ + @@ -1236,6 +1237,7 @@ + diff --git a/projects/openttd_vs141.vcxproj.filters b/projects/openttd_vs141.vcxproj.filters index 45753b1b6d..0405740d4c 100644 --- a/projects/openttd_vs141.vcxproj.filters +++ b/projects/openttd_vs141.vcxproj.filters @@ -837,6 +837,9 @@ Header Files + + Header Files + Header Files @@ -2796,6 +2799,9 @@ NewGRF + + NewGRF + NewGRF diff --git a/projects/openttd_vs142.vcxproj b/projects/openttd_vs142.vcxproj index ce5244eb97..49858a3e1d 100644 --- a/projects/openttd_vs142.vcxproj +++ b/projects/openttd_vs142.vcxproj @@ -583,6 +583,7 @@ + @@ -1236,6 +1237,7 @@ + diff --git a/projects/openttd_vs142.vcxproj.filters b/projects/openttd_vs142.vcxproj.filters index 45753b1b6d..0405740d4c 100644 --- a/projects/openttd_vs142.vcxproj.filters +++ b/projects/openttd_vs142.vcxproj.filters @@ -837,6 +837,9 @@ Header Files + + Header Files + Header Files @@ -2796,6 +2799,9 @@ NewGRF + + NewGRF + NewGRF diff --git a/source.list b/source.list index a395970f73..fd68c010e8 100644 --- a/source.list +++ b/source.list @@ -270,6 +270,7 @@ newgrf_house.h newgrf_industries.h newgrf_industrytiles.h newgrf_object.h +newgrf_profiling.h newgrf_properties.h newgrf_railtype.h newgrf_roadtype.h @@ -986,6 +987,7 @@ newgrf_house.cpp newgrf_industries.cpp newgrf_industrytiles.cpp newgrf_object.cpp +newgrf_profiling.cpp newgrf_railtype.cpp newgrf_roadtype.cpp newgrf_sound.cpp diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 343524202b..748e4ab4cd 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -33,6 +33,7 @@ #include "ai/ai.hpp" #include "ai/ai_config.hpp" #include "newgrf.h" +#include "newgrf_profiling.h" #include "console_func.h" #include "engine_base.h" #include "game/game.hpp" @@ -1877,6 +1878,135 @@ DEF_CONSOLE_CMD(ConNewGRFReload) return true; } +DEF_CONSOLE_CMD(ConNewGRFProfile) +{ + if (argc == 0) { + IConsoleHelp("Collect performance data about NewGRF sprite requests and callbacks. Sub-commands can be abbreviated."); + IConsoleHelp("Usage: newgrf_profile [list]"); + IConsoleHelp(" List all NewGRFs that can be profiled, and their status."); + IConsoleHelp("Usage: newgrf_profile select ..."); + IConsoleHelp(" Select one or more GRFs for profiling."); + IConsoleHelp("Usage: newgrf_profile unselect ..."); + IConsoleHelp(" Unselect one or more GRFs from profiling. Use the keyword \"all\" instead of a GRF number to unselect all. Removing an active profiler aborts data collection."); + IConsoleHelp("Usage: newgrf_profile start []"); + IConsoleHelp(" Begin profiling all selected GRFs. If a number of days is provided, profiling stops after that many in-game days."); + IConsoleHelp("Usage: newgrf_profile stop"); + IConsoleHelp(" End profiling and write the collected data to CSV files."); + IConsoleHelp("Usage: newgrf_profile abort"); + IConsoleHelp(" End profiling and discard all collected data."); + return true; + } + + extern const std::vector &GetAllGRFFiles(); + const std::vector &files = GetAllGRFFiles(); + + /* "list" sub-command */ + if (argc == 1 || strncasecmp(argv[1], "lis", 3) == 0) { + IConsolePrint(CC_INFO, "Loaded GRF files:"); + int i = 1; + for (GRFFile *grf : files) { + auto profiler = std::find_if(_newgrf_profilers.begin(), _newgrf_profilers.end(), [&](NewGRFProfiler &pr) { return pr.grffile == grf; }); + bool selected = profiler != _newgrf_profilers.end(); + bool active = selected && profiler->active; + TextColour tc = active ? TC_LIGHT_BLUE : selected ? TC_GREEN : CC_INFO; + const char *statustext = active ? " (active)" : selected ? " (selected)" : ""; + IConsolePrintF(tc, "%d: [%08X] %s%s", i, BSWAP32(grf->grfid), grf->filename, statustext); + i++; + } + return true; + } + + /* "select" sub-command */ + if (strncasecmp(argv[1], "sel", 3) == 0 && argc >= 3) { + for (size_t argnum = 2; argnum < argc; ++argnum) { + int grfnum = atoi(argv[argnum]); + if (grfnum < 1 || grfnum > (int)files.size()) { // safe cast, files.size() should not be larger than a few hundred in the most extreme cases + IConsolePrintF(CC_WARNING, "GRF number %d out of range, not added.", grfnum); + continue; + } + GRFFile *grf = files[grfnum - 1]; + if (std::any_of(_newgrf_profilers.begin(), _newgrf_profilers.end(), [&](NewGRFProfiler &pr) { return pr.grffile == grf; })) { + IConsolePrintF(CC_WARNING, "GRF number %d [%08X] is already selected for profiling.", grfnum, BSWAP32(grf->grfid)); + continue; + } + _newgrf_profilers.emplace_back(grf); + } + return true; + } + + /* "unselect" sub-command */ + if (strncasecmp(argv[1], "uns", 3) == 0 && argc >= 3) { + for (size_t argnum = 2; argnum < argc; ++argnum) { + if (strcasecmp(argv[argnum], "all") == 0) { + _newgrf_profilers.clear(); + break; + } + int grfnum = atoi(argv[argnum]); + if (grfnum < 1 || grfnum > (int)files.size()) { + IConsolePrintF(CC_WARNING, "GRF number %d out of range, not removing.", grfnum); + continue; + } + GRFFile *grf = files[grfnum - 1]; + auto pos = std::find_if(_newgrf_profilers.begin(), _newgrf_profilers.end(), [&](NewGRFProfiler &pr) { return pr.grffile == grf; }); + if (pos != _newgrf_profilers.end()) _newgrf_profilers.erase(pos); + } + return true; + } + + /* "start" sub-command */ + if (strncasecmp(argv[1], "sta", 3) == 0) { + std::string grfids; + size_t started = 0; + for (NewGRFProfiler &pr : _newgrf_profilers) { + if (!pr.active) { + pr.Start(); + started++; + + if (!grfids.empty()) grfids += ", "; + char grfidstr[12]{ 0 }; + seprintf(grfidstr, lastof(grfidstr), "[%08X]", BSWAP32(pr.grffile->grfid)); + grfids += grfidstr; + } + } + if (started > 0) { + IConsolePrintF(CC_DEBUG, "Started profiling for GRFID%s %s", (started > 1) ? "s" : "", grfids.c_str()); + if (argc >= 3) { + int days = max(atoi(argv[2]), 1); + _newgrf_profile_end_date = _date + days; + + char datestrbuf[32]{ 0 }; + SetDParam(0, _newgrf_profile_end_date); + GetString(datestrbuf, STR_JUST_DATE_ISO, lastof(datestrbuf)); + IConsolePrintF(CC_DEBUG, "Profiling will automatically stop on game date %s", datestrbuf); + } else { + _newgrf_profile_end_date = MAX_DAY; + } + } else if (_newgrf_profilers.empty()) { + IConsolePrintF(CC_WARNING, "No GRFs selected for profiling, did not start."); + } else { + IConsolePrintF(CC_WARNING, "Did not start profiling for any GRFs, all selected GRFs are already profiling."); + } + return true; + } + + /* "stop" sub-command */ + if (strncasecmp(argv[1], "sto", 3) == 0) { + NewGRFProfiler::FinishAll(); + return true; + } + + /* "abort" sub-command */ + if (strncasecmp(argv[1], "abo", 3) == 0) { + for (NewGRFProfiler &pr : _newgrf_profilers) { + pr.Abort(); + } + _newgrf_profile_end_date = MAX_DAY; + return true; + } + + return false; +} + #ifdef _DEBUG /****************** * debug commands @@ -2056,4 +2186,5 @@ void IConsoleStdLibRegister() /* NewGRF development stuff */ IConsoleCmdRegister("reload_newgrfs", ConNewGRFReload, ConHookNewGRFDeveloperTool); + IConsoleCmdRegister("newgrf_profile", ConNewGRFProfile, ConHookNewGRFDeveloperTool); } diff --git a/src/date.cpp b/src/date.cpp index be0a7782de..97758a3ebf 100644 --- a/src/date.cpp +++ b/src/date.cpp @@ -18,6 +18,7 @@ #include "rail_gui.h" #include "linkgraph/linkgraph.h" #include "saveload/saveload.h" +#include "newgrf_profiling.h" #include "safeguards.h" @@ -245,6 +246,10 @@ static void OnNewMonth() */ static void OnNewDay() { + if (!_newgrf_profilers.empty() && _newgrf_profile_end_date <= _date) { + NewGRFProfiler::FinishAll(); + } + if (_network_server) NetworkServerDailyLoop(); DisasterDailyLoop(); diff --git a/src/misc.cpp b/src/misc.cpp index c56eb6a38d..dcb04fa026 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -29,6 +29,7 @@ #include "station_kdtree.h" #include "town_kdtree.h" #include "viewport_kdtree.h" +#include "newgrf_profiling.h" #include "safeguards.h" @@ -69,6 +70,8 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin _thd.redsq = INVALID_TILE; if (reset_settings) MakeNewgameSettingsLive(); + _newgrf_profilers.clear(); + if (reset_date) { SetDate(ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1), 0); InitializeOldNames(); diff --git a/src/newgrf.cpp b/src/newgrf.cpp index f4197c9aa9..c422fe99c0 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -66,6 +66,11 @@ /** List of all loaded GRF files */ static std::vector _grf_files; +const std::vector &GetAllGRFFiles() +{ + return _grf_files; +} + /** Miscellaneous GRF features, set by Action 0x0D, parameter 0x9E */ byte _misc_grf_features = 0; @@ -5000,6 +5005,7 @@ static void NewSpriteGroup(ByteReader *buf) assert(DeterministicSpriteGroup::CanAllocateItem()); DeterministicSpriteGroup *group = new DeterministicSpriteGroup(); + group->nfo_line = _cur.nfo_line; act_group = group; group->var_scope = HasBit(type, 1) ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF; @@ -5116,6 +5122,7 @@ static void NewSpriteGroup(ByteReader *buf) { assert(RandomizedSpriteGroup::CanAllocateItem()); RandomizedSpriteGroup *group = new RandomizedSpriteGroup(); + group->nfo_line = _cur.nfo_line; act_group = group; group->var_scope = HasBit(type, 1) ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF; @@ -5164,6 +5171,7 @@ static void NewSpriteGroup(ByteReader *buf) assert(RealSpriteGroup::CanAllocateItem()); RealSpriteGroup *group = new RealSpriteGroup(); + group->nfo_line = _cur.nfo_line; act_group = group; group->num_loaded = num_loaded; @@ -5197,6 +5205,7 @@ static void NewSpriteGroup(ByteReader *buf) assert(TileLayoutSpriteGroup::CanAllocateItem()); TileLayoutSpriteGroup *group = new TileLayoutSpriteGroup(); + group->nfo_line = _cur.nfo_line; act_group = group; /* On error, bail out immediately. Temporary GRF data was already freed */ @@ -5212,6 +5221,7 @@ static void NewSpriteGroup(ByteReader *buf) assert(IndustryProductionSpriteGroup::CanAllocateItem()); IndustryProductionSpriteGroup *group = new IndustryProductionSpriteGroup(); + group->nfo_line = _cur.nfo_line; act_group = group; group->version = type; if (type == 0) { diff --git a/src/newgrf_airport.cpp b/src/newgrf_airport.cpp index 4540698a1a..61ad46ac0e 100644 --- a/src/newgrf_airport.cpp +++ b/src/newgrf_airport.cpp @@ -58,6 +58,9 @@ struct AirportResolverObject : public ResolverObject { } const SpriteGroup *ResolveReal(const RealSpriteGroup *group) const override; + + GrfSpecFeature GetFeature() const override; + uint32 GetDebugID() const override; }; /** @@ -226,6 +229,16 @@ void AirportOverrideManager::SetEntitySpec(AirportSpec *as) return nullptr; } +GrfSpecFeature AirportResolverObject::GetFeature() const +{ + return GSF_AIRPORTS; +} + +uint32 AirportResolverObject::GetDebugID() const +{ + return AirportSpec::Get(this->airport_scope.airport_id)->grf_prop.local_id; +} + /* virtual */ uint32 AirportScopeResolver::GetRandomBits() const { return this->st == nullptr ? 0 : this->st->random_bits; diff --git a/src/newgrf_airporttiles.cpp b/src/newgrf_airporttiles.cpp index 7a420f3e1d..3059174a86 100644 --- a/src/newgrf_airporttiles.cpp +++ b/src/newgrf_airporttiles.cpp @@ -220,6 +220,16 @@ AirportTileResolverObject::AirportTileResolverObject(const AirportTileSpec *ats, this->root_spritegroup = ats->grf_prop.spritegroup[0]; } +GrfSpecFeature AirportTileResolverObject::GetFeature() const +{ + return GSF_AIRPORTTILES; +} + +uint32 AirportTileResolverObject::GetDebugID() const +{ + return this->tiles_scope.ats->grf_prop.local_id; +} + uint16 GetAirportTileCallback(CallbackID callback, uint32 param1, uint32 param2, const AirportTileSpec *ats, Station *st, TileIndex tile, int extra_data = 0) { AirportTileResolverObject object(ats, tile, st, callback, param1, param2); diff --git a/src/newgrf_airporttiles.h b/src/newgrf_airporttiles.h index 191b79e9ac..37460622d4 100644 --- a/src/newgrf_airporttiles.h +++ b/src/newgrf_airporttiles.h @@ -22,6 +22,7 @@ struct AirportTileScopeResolver : public ScopeResolver { struct Station *st; ///< %Station of the airport for which the callback is run, or \c nullptr for build gui. byte airport_id; ///< Type of airport for which the callback is run. TileIndex tile; ///< Tile for the callback, only valid for airporttile callbacks. + const AirportTileSpec *ats; /** * Constructor of the scope resolver specific for airport tiles. @@ -30,7 +31,7 @@ struct AirportTileScopeResolver : public ScopeResolver { * @param st Station of the airport for which the callback is run, or \c nullptr for build gui. */ AirportTileScopeResolver(ResolverObject &ro, const AirportTileSpec *ats, TileIndex tile, Station *st) - : ScopeResolver(ro), st(st), tile(tile) + : ScopeResolver(ro), st(st), tile(tile), ats(ats) { assert(st != nullptr); this->airport_id = st->airport.type; @@ -54,6 +55,9 @@ struct AirportTileResolverObject : public ResolverObject { default: return ResolverObject::GetScope(scope, relative); } } + + GrfSpecFeature GetFeature() const override; + uint32 GetDebugID() const override; }; /** diff --git a/src/newgrf_canal.cpp b/src/newgrf_canal.cpp index 0209aa9ad8..7295e5551b 100644 --- a/src/newgrf_canal.cpp +++ b/src/newgrf_canal.cpp @@ -13,6 +13,7 @@ #include "newgrf_canal.h" #include "water.h" #include "water_map.h" +#include "spritecache.h" #include "safeguards.h" @@ -35,6 +36,7 @@ struct CanalScopeResolver : public ScopeResolver { /** Resolver object for canals. */ struct CanalResolverObject : public ResolverObject { CanalScopeResolver canal_scope; + CanalFeature feature; CanalResolverObject(CanalFeature feature, TileIndex tile, CallbackID callback = CBID_NO_CALLBACK, uint32 callback_param1 = 0, uint32 callback_param2 = 0); @@ -48,6 +50,9 @@ struct CanalResolverObject : public ResolverObject { } const SpriteGroup *ResolveReal(const RealSpriteGroup *group) const override; + + GrfSpecFeature GetFeature() const override; + uint32 GetDebugID() const override; }; /* virtual */ uint32 CanalScopeResolver::GetRandomBits() const @@ -111,6 +116,16 @@ struct CanalResolverObject : public ResolverObject { return group->loaded[0]; } +GrfSpecFeature CanalResolverObject::GetFeature() const +{ + return GSF_CANALS; +} + +uint32 CanalResolverObject::GetDebugID() const +{ + return this->feature; +} + /** * Canal resolver constructor. * @param feature Which canal feature we want. @@ -121,7 +136,7 @@ struct CanalResolverObject : public ResolverObject { */ CanalResolverObject::CanalResolverObject(CanalFeature feature, TileIndex tile, CallbackID callback, uint32 callback_param1, uint32 callback_param2) - : ResolverObject(_water_feature[feature].grffile, callback, callback_param1, callback_param2), canal_scope(*this, tile) + : ResolverObject(_water_feature[feature].grffile, callback, callback_param1, callback_param2), canal_scope(*this, tile), feature(feature) { this->root_spritegroup = _water_feature[feature].group; } diff --git a/src/newgrf_cargo.cpp b/src/newgrf_cargo.cpp index b0fd632b5b..22c7120d82 100644 --- a/src/newgrf_cargo.cpp +++ b/src/newgrf_cargo.cpp @@ -15,9 +15,14 @@ /** Resolver of cargo. */ struct CargoResolverObject : public ResolverObject { + const CargoSpec *cargospec; + CargoResolverObject(const CargoSpec *cs, CallbackID callback = CBID_NO_CALLBACK, uint32 callback_param1 = 0, uint32 callback_param2 = 0); const SpriteGroup *ResolveReal(const RealSpriteGroup *group) const override; + + GrfSpecFeature GetFeature() const override; + uint32 GetDebugID() const override; }; /* virtual */ const SpriteGroup *CargoResolverObject::ResolveReal(const RealSpriteGroup *group) const @@ -30,6 +35,16 @@ struct CargoResolverObject : public ResolverObject { return nullptr; } +GrfSpecFeature CargoResolverObject::GetFeature() const +{ + return GSF_CARGOES; +} + +uint32 CargoResolverObject::GetDebugID() const +{ + return this->cargospec->label; +} + /** * Constructor of the cargo resolver. * @param cs Cargo being resolved. @@ -38,7 +53,7 @@ struct CargoResolverObject : public ResolverObject { * @param callback_param2 Second parameter (var 18) of the callback. */ CargoResolverObject::CargoResolverObject(const CargoSpec *cs, CallbackID callback, uint32 callback_param1, uint32 callback_param2) - : ResolverObject(cs->grffile, callback, callback_param1, callback_param2) + : ResolverObject(cs->grffile, callback, callback_param1, callback_param2), cargospec(cs) { this->root_spritegroup = cs->group; } diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp index dc0e1cfe01..db2f5ac43c 100644 --- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -946,6 +946,22 @@ static uint32 VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *object, return in_motion ? group->loaded[set] : group->loading[set]; } +GrfSpecFeature VehicleResolverObject::GetFeature() const +{ + switch (Engine::Get(this->self_scope.self_type)->type) { + case VEH_TRAIN: return GSF_TRAINS; + case VEH_ROAD: return GSF_ROADVEHICLES; + case VEH_SHIP: return GSF_SHIPS; + case VEH_AIRCRAFT: return GSF_AIRCRAFT; + default: return GSF_INVALID; + } +} + +uint32 VehicleResolverObject::GetDebugID() const +{ + return Engine::Get(this->self_scope.self_type)->grf_prop.local_id; +} + /** * Get the grf file associated with an engine type. * @param engine_type Engine to query. diff --git a/src/newgrf_engine.h b/src/newgrf_engine.h index 817fefb5d0..f830ff499d 100644 --- a/src/newgrf_engine.h +++ b/src/newgrf_engine.h @@ -65,6 +65,9 @@ struct VehicleResolverObject : public ResolverObject { ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override; const SpriteGroup *ResolveReal(const RealSpriteGroup *group) const override; + + GrfSpecFeature GetFeature() const override; + uint32 GetDebugID() const override; }; static const uint TRAININFO_DEFAULT_VEHICLE_WIDTH = 29; diff --git a/src/newgrf_generic.cpp b/src/newgrf_generic.cpp index be7caefb83..6538b79b66 100644 --- a/src/newgrf_generic.cpp +++ b/src/newgrf_generic.cpp @@ -29,6 +29,8 @@ struct GenericScopeResolver : public ScopeResolver { uint8 count; uint8 station_size; + uint8 feature; + /** * Generic scope resolver. * @param ro Surrounding resolver. @@ -36,7 +38,7 @@ struct GenericScopeResolver : public ScopeResolver { */ GenericScopeResolver(ResolverObject &ro, bool ai_callback) : ScopeResolver(ro), cargo_type(0), default_selection(0), src_industry(0), dst_industry(0), distance(0), - event(), count(0), station_size(0), ai_callback(ai_callback) + event(), count(0), station_size(0), feature(GSF_INVALID), ai_callback(ai_callback) { } @@ -62,6 +64,16 @@ struct GenericResolverObject : public ResolverObject { } const SpriteGroup *ResolveReal(const RealSpriteGroup *group) const override; + + GrfSpecFeature GetFeature() const override + { + return (GrfSpecFeature)this->generic_scope.feature; + } + + uint32 GetDebugID() const override + { + return 0; + } }; struct GenericCallback { @@ -226,6 +238,7 @@ uint16 GetAiPurchaseCallbackResult(uint8 feature, CargoID cargo_type, uint8 defa object.generic_scope.event = event; object.generic_scope.count = count; object.generic_scope.station_size = station_size; + object.generic_scope.feature = feature; uint16 callback = GetGenericCallbackResult(feature, object, 0, 0, file); if (callback != CALLBACK_FAILED) callback = GB(callback, 0, 8); @@ -247,6 +260,7 @@ void AmbientSoundEffectCallback(TileIndex tile) /* Prepare resolver object. */ GenericResolverObject object(false, CBID_SOUNDS_AMBIENT_EFFECT); + object.generic_scope.feature = GSF_SOUNDFX; uint32 param1_v7 = GetTileType(tile) << 28 | Clamp(TileHeight(tile), 0, 15) << 24 | GB(r, 16, 8) << 16 | GetTerrainType(tile); uint32 param1_v8 = GetTileType(tile) << 24 | GetTileZ(tile) << 16 | GB(r, 16, 8) << 8 | (HasTileWaterClass(tile) ? GetWaterClass(tile) : 0) << 3 | GetTerrainType(tile); diff --git a/src/newgrf_house.cpp b/src/newgrf_house.cpp index b7d8048c94..b585a682dc 100644 --- a/src/newgrf_house.cpp +++ b/src/newgrf_house.cpp @@ -62,6 +62,16 @@ HouseResolverObject::HouseResolverObject(HouseID house_id, TileIndex tile, Town this->root_spritegroup = HouseSpec::Get(house_id)->grf_prop.spritegroup[0]; } +GrfSpecFeature HouseResolverObject::GetFeature() const +{ + return GSF_HOUSES; +} + +uint32 HouseResolverObject::GetDebugID() const +{ + return HouseSpec::Get(this->house_scope.house_id)->grf_prop.local_id; +} + HouseClassID AllocateHouseClassID(byte grf_class_id, uint32 grfid) { /* Start from 1 because 0 means that no class has been assigned. */ diff --git a/src/newgrf_house.h b/src/newgrf_house.h index b59d28ab51..b4c1f61de3 100644 --- a/src/newgrf_house.h +++ b/src/newgrf_house.h @@ -64,6 +64,9 @@ struct HouseResolverObject : public ResolverObject { default: return ResolverObject::GetScope(scope, relative); } } + + GrfSpecFeature GetFeature() const override; + uint32 GetDebugID() const override; }; /** diff --git a/src/newgrf_industries.cpp b/src/newgrf_industries.cpp index fd32f3e948..a8748a4953 100644 --- a/src/newgrf_industries.cpp +++ b/src/newgrf_industries.cpp @@ -489,6 +489,16 @@ TownScopeResolver *IndustriesResolverObject::GetTown() return this->town_scope; } +GrfSpecFeature IndustriesResolverObject::GetFeature() const +{ + return GSF_INDUSTRIES; +} + +uint32 IndustriesResolverObject::GetDebugID() const +{ + return GetIndustrySpec(this->industries_scope.type)->grf_prop.local_id; +} + /** * Perform an industry callback. * @param callback The callback to perform. diff --git a/src/newgrf_industries.h b/src/newgrf_industries.h index d791eaf0d8..01a185aa19 100644 --- a/src/newgrf_industries.h +++ b/src/newgrf_industries.h @@ -63,6 +63,9 @@ struct IndustriesResolverObject : public ResolverObject { return ResolverObject::GetScope(scope, relative); } } + + GrfSpecFeature GetFeature() const override; + uint32 GetDebugID() const override; }; /** When should the industry(tile) be triggered for random bits? */ diff --git a/src/newgrf_industrytiles.cpp b/src/newgrf_industrytiles.cpp index 03d86a7717..e9c99f6cd5 100644 --- a/src/newgrf_industrytiles.cpp +++ b/src/newgrf_industrytiles.cpp @@ -139,11 +139,22 @@ IndustryTileResolverObject::IndustryTileResolverObject(IndustryGfx gfx, TileInde CallbackID callback, uint32 callback_param1, uint32 callback_param2) : ResolverObject(GetIndTileGrffile(gfx), callback, callback_param1, callback_param2), indtile_scope(*this, indus, tile), - ind_scope(*this, tile, indus, indus->type) + ind_scope(*this, tile, indus, indus->type), + gfx(gfx) { this->root_spritegroup = GetIndustryTileSpec(gfx)->grf_prop.spritegroup[0]; } +GrfSpecFeature IndustryTileResolverObject::GetFeature() const +{ + return GSF_INDUSTRYTILES; +} + +uint32 IndustryTileResolverObject::GetDebugID() const +{ + return GetIndustryTileSpec(gfx)->grf_prop.local_id; +} + static void IndustryDrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *group, byte rnd_colour, byte stage, IndustryGfx gfx) { const DrawTileSprites *dts = group->ProcessRegisters(&stage); diff --git a/src/newgrf_industrytiles.h b/src/newgrf_industrytiles.h index 838dcc5332..e5494ea302 100644 --- a/src/newgrf_industrytiles.h +++ b/src/newgrf_industrytiles.h @@ -39,6 +39,7 @@ struct IndustryTileScopeResolver : public ScopeResolver { struct IndustryTileResolverObject : public ResolverObject { IndustryTileScopeResolver indtile_scope; ///< Scope resolver for the industry tile. IndustriesScopeResolver ind_scope; ///< Scope resolver for the industry owning the tile. + IndustryGfx gfx; IndustryTileResolverObject(IndustryGfx gfx, TileIndex tile, Industry *indus, CallbackID callback = CBID_NO_CALLBACK, uint32 callback_param1 = 0, uint32 callback_param2 = 0); @@ -51,6 +52,9 @@ struct IndustryTileResolverObject : public ResolverObject { default: return ResolverObject::GetScope(scope, relative); } } + + GrfSpecFeature GetFeature() const override; + uint32 GetDebugID() const override; }; bool DrawNewIndustryTile(TileInfo *ti, Industry *i, IndustryGfx gfx, const IndustryTileSpec *inds); diff --git a/src/newgrf_object.cpp b/src/newgrf_object.cpp index 34e18c43a2..8827245d36 100644 --- a/src/newgrf_object.cpp +++ b/src/newgrf_object.cpp @@ -352,7 +352,7 @@ unhandled: */ ObjectResolverObject::ObjectResolverObject(const ObjectSpec *spec, Object *obj, TileIndex tile, uint8 view, CallbackID callback, uint32 param1, uint32 param2) - : ResolverObject(spec->grf_prop.grffile, callback, param1, param2), object_scope(*this, obj, tile, view) + : ResolverObject(spec->grf_prop.grffile, callback, param1, param2), object_scope(*this, obj, spec, tile, view) { this->town_scope = nullptr; this->root_spritegroup = (obj == nullptr && spec->grf_prop.spritegroup[CT_PURCHASE_OBJECT] != nullptr) ? @@ -384,6 +384,16 @@ TownScopeResolver *ObjectResolverObject::GetTown() return this->town_scope; } +GrfSpecFeature ObjectResolverObject::GetFeature() const +{ + return GSF_OBJECTS; +} + +uint32 ObjectResolverObject::GetDebugID() const +{ + return this->object_scope.spec->grf_prop.local_id; +} + /** * Perform a callback for an object. * @param callback The callback to perform. diff --git a/src/newgrf_object.h b/src/newgrf_object.h index 90be24042c..1776b760ab 100644 --- a/src/newgrf_object.h +++ b/src/newgrf_object.h @@ -98,9 +98,10 @@ struct ObjectSpec { /** Object scope resolver. */ struct ObjectScopeResolver : public ScopeResolver { - struct Object *obj; ///< The object the callback is ran for. - TileIndex tile; ///< The tile related to the object. - uint8 view; ///< The view of the object. + struct Object *obj; ///< The object the callback is ran for. + const ObjectSpec *spec; ///< Specification of the object type. + TileIndex tile; ///< The tile related to the object. + uint8 view; ///< The view of the object. /** * Constructor of an object scope resolver. @@ -109,8 +110,8 @@ struct ObjectScopeResolver : public ScopeResolver { * @param tile %Tile of the object. * @param view View of the object. */ - ObjectScopeResolver(ResolverObject &ro, Object *obj, TileIndex tile, uint8 view = 0) - : ScopeResolver(ro), obj(obj), tile(tile), view(view) + ObjectScopeResolver(ResolverObject &ro, Object *obj, const ObjectSpec *spec, TileIndex tile, uint8 view = 0) + : ScopeResolver(ro), obj(obj), spec(spec), tile(tile), view(view) { } @@ -144,6 +145,9 @@ struct ObjectResolverObject : public ResolverObject { } } + GrfSpecFeature GetFeature() const override; + uint32 GetDebugID() const override; + private: TownScopeResolver *GetTown(); }; diff --git a/src/newgrf_profiling.cpp b/src/newgrf_profiling.cpp new file mode 100644 index 0000000000..e9a0dd92d2 --- /dev/null +++ b/src/newgrf_profiling.cpp @@ -0,0 +1,162 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + + /** @file newgrf_profiling.cpp Profiling of NewGRF action 2 handling. */ + +#include "newgrf_profiling.h" +#include "date_func.h" +#include "fileio_func.h" +#include "string_func.h" +#include "console_func.h" +#include "spritecache.h" + +#include +#include + + +std::vector _newgrf_profilers; +Date _newgrf_profile_end_date; + + +/** + * Create profiler object and begin profiling session. + * @param grffile The GRF file to collect profiling data on + * @param end_date Game date to end profiling on + */ +NewGRFProfiler::NewGRFProfiler(const GRFFile *grffile) : grffile{ grffile }, active{ false }, cur_call{} +{ +} + +/** + * Complete profiling session and write data to file + */ +NewGRFProfiler::~NewGRFProfiler() +{ +} + +/** + * Capture the start of a sprite group resolution. + * @param resolver Data about sprite group being resolved + */ +void NewGRFProfiler::BeginResolve(const ResolverObject &resolver) +{ + using namespace std::chrono; + this->cur_call.root_sprite = resolver.root_spritegroup->nfo_line; + this->cur_call.subs = 0; + this->cur_call.time = (uint32)time_point_cast(high_resolution_clock::now()).time_since_epoch().count(); + this->cur_call.tick = _tick_counter; + this->cur_call.cb = resolver.callback; + this->cur_call.feat = resolver.GetFeature(); + this->cur_call.item = resolver.GetDebugID(); +} + +/** + * Capture the completion of a sprite group resolution. + */ +void NewGRFProfiler::EndResolve(const SpriteGroup *result) +{ + using namespace std::chrono; + this->cur_call.time = (uint32)time_point_cast(high_resolution_clock::now()).time_since_epoch().count() - this->cur_call.time; + + if (result == nullptr) { + this->cur_call.result = 0; + } else if (result->type == SGT_CALLBACK) { + this->cur_call.result = static_cast(result)->result; + } else if (result->type == SGT_RESULT) { + this->cur_call.result = GetSpriteLocalID(static_cast(result)->sprite); + } else { + this->cur_call.result = result->nfo_line; + } + + this->calls.push_back(this->cur_call); +} + +/** + * Capture a recursive sprite group resolution. + */ +void NewGRFProfiler::RecursiveResolve() +{ + this->cur_call.subs += 1; +} + +void NewGRFProfiler::Start() +{ + this->Abort(); + this->active = true; + this->start_tick = _tick_counter; +} + +uint32 NewGRFProfiler::Finish() +{ + if (!this->active) return 0; + + if (this->calls.empty()) { + IConsolePrintF(CC_DEBUG, "Finished profile of NewGRF [%08X], no events collected, not writing a file", BSWAP32(this->grffile->grfid)); + return 0; + } + + std::string filename = this->GetOutputFilename(); + IConsolePrintF(CC_DEBUG, "Finished profile of NewGRF [%08X], writing %u events to %s", BSWAP32(this->grffile->grfid), (uint)this->calls.size(), filename.c_str()); + + FILE *f = FioFOpenFile(filename.c_str(), "wt", Subdirectory::NO_DIRECTORY); + FileCloser fcloser(f); + + uint32 total_microseconds = 0; + + fputs("Tick,Sprite,Feature,Item,CallbackID,Microseconds,Depth,Result\n", f); + for (const Call &c : this->calls) { + fprintf(f, "%u,%u,0x%X,%d,0x%X,%u,%u,%u\n", c.tick, c.root_sprite, c.feat, c.item, (uint)c.cb, c.time, c.subs, c.result); + total_microseconds += c.time; + } + + this->Abort(); + + return total_microseconds; +} + +void NewGRFProfiler::Abort() +{ + this->active = false; + this->calls.clear(); +} + +/** + * Get name of the file that will be written. + * @return File name of profiling output file. + */ +std::string NewGRFProfiler::GetOutputFilename() const +{ + time_t write_time = time(nullptr); + + char timestamp[16] = {}; + strftime(timestamp, lengthof(timestamp), "%Y%m%d-%H%M", localtime(&write_time)); + + char filepath[MAX_PATH] = {}; + seprintf(filepath, lastof(filepath), "%sgrfprofile-%s-%08X.csv", FiosGetScreenshotDir(), timestamp, BSWAP32(this->grffile->grfid)); + + return std::string(filepath); +} + +uint32 NewGRFProfiler::FinishAll() +{ + int max_ticks = 0; + uint32 total_microseconds = 0; + for (NewGRFProfiler &pr : _newgrf_profilers) { + if (pr.active) { + total_microseconds += pr.Finish(); + max_ticks = max(max_ticks, _tick_counter - pr.start_tick); + } + } + + if (total_microseconds > 0 && max_ticks > 0) { + IConsolePrintF(CC_DEBUG, "Total NewGRF callback processing: %u microseconds over %d ticks", total_microseconds, max_ticks); + } + + _newgrf_profile_end_date = MAX_DAY; + + return total_microseconds; +} diff --git a/src/newgrf_profiling.h b/src/newgrf_profiling.h new file mode 100644 index 0000000000..e5b2813f59 --- /dev/null +++ b/src/newgrf_profiling.h @@ -0,0 +1,63 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + + /** @file newgrf_profiling.h Profiling of NewGRF action 2 handling. */ + +#ifndef NEWGRF_PROFILING_H +#define NEWGRF_PROFILING_H + +#include "stdafx.h" +#include "date_type.h" +#include "newgrf.h" +#include "newgrf_callbacks.h" +#include "newgrf_spritegroup.h" + +#include +#include +#include + +/** + * Callback profiler for NewGRF development + */ +struct NewGRFProfiler { + NewGRFProfiler(const GRFFile *grffile); + ~NewGRFProfiler(); + + void BeginResolve(const ResolverObject &resolver); + void EndResolve(const SpriteGroup *result); + void RecursiveResolve(); + + void Start(); + uint32 Finish(); + void Abort(); + std::string GetOutputFilename() const; + + static uint32 FinishAll(); + + /** Measurement of a single sprite group resolution */ + struct Call { + uint32 root_sprite; ///< Pseudo-sprite index in GRF file + uint32 item; ///< Local ID of item being resolved for + uint32 result; ///< Result of callback + uint32 subs; ///< Sub-calls to other sprite groups + uint32 time; ///< Time taken for resolution (microseconds) + uint16 tick; ///< Game tick + CallbackID cb; ///< Callback ID + GrfSpecFeature feat; ///< GRF feature being resolved for + }; + + const GRFFile *grffile; ///< Which GRF is being profiled + bool active; ///< Is this profiler collecting data + uint16 start_tick; ///< Tick number this profiler was started on + Call cur_call; ///< Data for current call in progress + std::vector calls; ///< All calls collected so far +}; + +extern std::vector _newgrf_profilers; +extern Date _newgrf_profile_end_date; + +#endif /* NEWGRF_PROFILING_H */ diff --git a/src/newgrf_railtype.cpp b/src/newgrf_railtype.cpp index 451076ccc2..2a98948e7b 100644 --- a/src/newgrf_railtype.cpp +++ b/src/newgrf_railtype.cpp @@ -65,6 +65,16 @@ return nullptr; } +GrfSpecFeature RailTypeResolverObject::GetFeature() const +{ + return GSF_RAILTYPES; +} + +uint32 RailTypeResolverObject::GetDebugID() const +{ + return this->railtype_scope.rti->label; +} + /** * Resolver object for rail types. * @param rti Railtype. nullptr in NewGRF Inspect window. @@ -75,7 +85,7 @@ * @param param2 Extra parameter (second parameter of the callback, except railtypes do not have callbacks). */ RailTypeResolverObject::RailTypeResolverObject(const RailtypeInfo *rti, TileIndex tile, TileContext context, RailTypeSpriteGroup rtsg, uint32 param1, uint32 param2) - : ResolverObject(rti != nullptr ? rti->grffile[rtsg] : nullptr, CBID_NO_CALLBACK, param1, param2), railtype_scope(*this, tile, context) + : ResolverObject(rti != nullptr ? rti->grffile[rtsg] : nullptr, CBID_NO_CALLBACK, param1, param2), railtype_scope(*this, rti, tile, context) { this->root_spritegroup = rti != nullptr ? rti->group[rtsg] : nullptr; } diff --git a/src/newgrf_railtype.h b/src/newgrf_railtype.h index 223cfdd63b..1e0ff01d82 100644 --- a/src/newgrf_railtype.h +++ b/src/newgrf_railtype.h @@ -18,6 +18,7 @@ struct RailTypeScopeResolver : public ScopeResolver { TileIndex tile; ///< Tracktile. For track on a bridge this is the southern bridgehead. TileContext context; ///< Are we resolving sprites for the upper halftile, or on a bridge? + const RailtypeInfo *rti; /** * Constructor of the railtype scope resolvers. @@ -25,8 +26,8 @@ struct RailTypeScopeResolver : public ScopeResolver { * @param tile %Tile containing the track. For track on a bridge this is the southern bridgehead. * @param context Are we resolving sprites for the upper halftile, or on a bridge? */ - RailTypeScopeResolver(ResolverObject &ro, TileIndex tile, TileContext context) - : ScopeResolver(ro), tile(tile), context(context) + RailTypeScopeResolver(ResolverObject &ro, const RailtypeInfo *rti, TileIndex tile, TileContext context) + : ScopeResolver(ro), tile(tile), context(context), rti(rti) { } @@ -49,6 +50,9 @@ struct RailTypeResolverObject : public ResolverObject { } const SpriteGroup *ResolveReal(const RealSpriteGroup *group) const override; + + GrfSpecFeature GetFeature() const override; + uint32 GetDebugID() const override; }; SpriteID GetCustomRailSprite(const RailtypeInfo *rti, TileIndex tile, RailTypeSpriteGroup rtsg, TileContext context = TCX_NORMAL, uint *num_results = nullptr); diff --git a/src/newgrf_roadtype.cpp b/src/newgrf_roadtype.cpp index 34a8eb11a6..c49faa59f1 100644 --- a/src/newgrf_roadtype.cpp +++ b/src/newgrf_roadtype.cpp @@ -65,16 +65,32 @@ return nullptr; } +GrfSpecFeature RoadTypeResolverObject::GetFeature() const +{ + RoadType rt = GetRoadTypeByLabel(this->roadtype_scope.rti->label, false); + switch (GetRoadTramType(rt)) { + case RTT_ROAD: return GSF_ROADTYPES; + case RTT_TRAM: return GSF_TRAMTYPES; + default: return GSF_INVALID; + } +} + +uint32 RoadTypeResolverObject::GetDebugID() const +{ + return this->roadtype_scope.rti->label; +} + /** * Constructor of the roadtype scope resolvers. * @param ro Surrounding resolver. * @param tile %Tile containing the track. For track on a bridge this is the southern bridgehead. * @param context Are we resolving sprites for the upper halftile, or on a bridge? */ -RoadTypeScopeResolver::RoadTypeScopeResolver(ResolverObject &ro, TileIndex tile, TileContext context) : ScopeResolver(ro) +RoadTypeScopeResolver::RoadTypeScopeResolver(ResolverObject &ro, const RoadTypeInfo *rti, TileIndex tile, TileContext context) : ScopeResolver(ro) { this->tile = tile; this->context = context; + this->rti = rti; } /** @@ -87,7 +103,7 @@ RoadTypeScopeResolver::RoadTypeScopeResolver(ResolverObject &ro, TileIndex tile, * @param param2 Extra parameter (second parameter of the callback, except roadtypes do not have callbacks). */ RoadTypeResolverObject::RoadTypeResolverObject(const RoadTypeInfo *rti, TileIndex tile, TileContext context, RoadTypeSpriteGroup rtsg, uint32 param1, uint32 param2) - : ResolverObject(rti != nullptr ? rti->grffile[rtsg] : nullptr, CBID_NO_CALLBACK, param1, param2), roadtype_scope(*this, tile, context) + : ResolverObject(rti != nullptr ? rti->grffile[rtsg] : nullptr, CBID_NO_CALLBACK, param1, param2), roadtype_scope(*this, rti, tile, context) { this->root_spritegroup = rti != nullptr ? rti->group[rtsg] : nullptr; } diff --git a/src/newgrf_roadtype.h b/src/newgrf_roadtype.h index ee0773d7df..56b65f1271 100644 --- a/src/newgrf_roadtype.h +++ b/src/newgrf_roadtype.h @@ -18,8 +18,9 @@ struct RoadTypeScopeResolver : public ScopeResolver { TileIndex tile; ///< Tracktile. For track on a bridge this is the southern bridgehead. TileContext context; ///< Are we resolving sprites for the upper halftile, or on a bridge? + const RoadTypeInfo *rti; - RoadTypeScopeResolver(ResolverObject &ro, TileIndex tile, TileContext context); + RoadTypeScopeResolver(ResolverObject &ro, const RoadTypeInfo *rti, TileIndex tile, TileContext context); /* virtual */ uint32 GetRandomBits() const; /* virtual */ uint32 GetVariable(byte variable, uint32 parameter, bool *available) const; @@ -40,6 +41,9 @@ struct RoadTypeResolverObject : public ResolverObject { } /* virtual */ const SpriteGroup *ResolveReal(const RealSpriteGroup *group) const; + + GrfSpecFeature GetFeature() const override; + uint32 GetDebugID() const override; }; SpriteID GetCustomRoadSprite(const RoadTypeInfo *rti, TileIndex tile, RoadTypeSpriteGroup rtsg, TileContext context = TCX_NORMAL, uint *num_results = nullptr); diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index 76f8df20f3..824ba0439e 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -11,6 +11,7 @@ #include #include "debug.h" #include "newgrf_spritegroup.h" +#include "newgrf_profiling.h" #include "core/pool_func.hpp" #include "safeguards.h" @@ -34,10 +35,23 @@ TemporaryStorageArray _temp_store; /* static */ const SpriteGroup *SpriteGroup::Resolve(const SpriteGroup *group, ResolverObject &object, bool top_level) { if (group == nullptr) return nullptr; - if (top_level) { + + const GRFFile *grf = object.grffile; + auto profiler = std::find_if(_newgrf_profilers.begin(), _newgrf_profilers.end(), [&](const NewGRFProfiler &pr) { return pr.grffile == grf; }); + + if (profiler == _newgrf_profilers.end() || !profiler->active) { + if (top_level) _temp_store.ClearChanges(); + return group->Resolve(object); + } else if (top_level) { + profiler->BeginResolve(object); _temp_store.ClearChanges(); + const SpriteGroup *result = group->Resolve(object); + profiler->EndResolve(result); + return result; + } else { + profiler->RecursiveResolve(); + return group->Resolve(object); } - return group->Resolve(object); } RealSpriteGroup::~RealSpriteGroup() diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index 00bfc49d04..80f70df55d 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -56,13 +56,14 @@ extern SpriteGroupPool _spritegroup_pool; /* Common wrapper for all the different sprite group types */ struct SpriteGroup : SpriteGroupPool::PoolItem<&_spritegroup_pool> { protected: - SpriteGroup(SpriteGroupType type) : type(type) {} + SpriteGroup(SpriteGroupType type) : nfo_line(0), type(type) {} /** Base sprite group resolver */ virtual const SpriteGroup *Resolve(ResolverObject &object) const { return this; }; public: virtual ~SpriteGroup() {} + uint32 nfo_line; SpriteGroupType type; virtual SpriteID GetResult() const { return 0; } @@ -398,6 +399,18 @@ struct ResolverObject { this->used_triggers = 0; memset(this->reseed, 0, sizeof(this->reseed)); } + + /** + * Get the feature number being resolved for. + * This function is mainly intended for the callback profiling feature. + */ + virtual GrfSpecFeature GetFeature() const { return GSF_INVALID; } + /** + * Get an identifier for the item being resolved. + * This function is mainly intended for the callback profiling feature, + * and should return an identifier recognisable by the NewGRF developer. + */ + virtual uint32 GetDebugID() const { return 0; } }; #endif /* NEWGRF_SPRITEGROUP_H */ diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index 8e8e52a629..312ca59d56 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -527,6 +527,16 @@ uint32 Waypoint::GetNewGRFVariable(const ResolverObject &object, byte variable, return group->loading[0]; } +GrfSpecFeature StationResolverObject::GetFeature() const +{ + return GSF_STATIONS; +} + +uint32 StationResolverObject::GetDebugID() const +{ + return this->station_scope.statspec->grf_prop.local_id; +} + /** * Resolver for stations. * @param statspec Station (type) specification. diff --git a/src/newgrf_station.h b/src/newgrf_station.h index a79ea3fe29..fac5d64ddd 100644 --- a/src/newgrf_station.h +++ b/src/newgrf_station.h @@ -75,6 +75,9 @@ struct StationResolverObject : public ResolverObject { } const SpriteGroup *ResolveReal(const RealSpriteGroup *group) const override; + + GrfSpecFeature GetFeature() const override; + uint32 GetDebugID() const override; }; enum StationClassID : byte { diff --git a/src/spritecache.cpp b/src/spritecache.cpp index f36354c013..da0ca80484 100644 --- a/src/spritecache.cpp +++ b/src/spritecache.cpp @@ -145,6 +145,17 @@ uint GetOriginFileSlot(SpriteID sprite) return GetSpriteCache(sprite)->file_slot; } +/** + * Get the GRF-local sprite id of a given sprite. + * @param sprite The sprite to look at. + * @return The GRF-local sprite id. + */ +uint32 GetSpriteLocalID(SpriteID sprite) +{ + if (!SpriteExists(sprite)) return 0; + return GetSpriteCache(sprite)->id; +} + /** * Count the sprites which originate from a specific file slot in a range of SpriteIDs. * @param file_slot FIOS file slot. diff --git a/src/spritecache.h b/src/spritecache.h index 3b3e1c9de5..1b738a8658 100644 --- a/src/spritecache.h +++ b/src/spritecache.h @@ -30,6 +30,7 @@ bool SpriteExists(SpriteID sprite); SpriteType GetSpriteType(SpriteID sprite); uint GetOriginFileSlot(SpriteID sprite); +uint32 GetSpriteLocalID(SpriteID sprite); uint GetSpriteCountForSlot(uint file_slot, SpriteID begin, SpriteID end); uint GetMaxSpriteID(); From bf4672864dc7ffe9f54b447d220cf58af36396ae Mon Sep 17 00:00:00 2001 From: glx Date: Sat, 25 Jan 2020 17:25:08 +0100 Subject: [PATCH 4/9] Fix #7960: use the same method as findversion.sh to determine tag --- azure-pipelines/changelog.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines/changelog.sh b/azure-pipelines/changelog.sh index be665a5a08..8231f66672 100755 --- a/azure-pipelines/changelog.sh +++ b/azure-pipelines/changelog.sh @@ -1,6 +1,6 @@ #!/bin/sh -tag=$(git describe --tags 2>/dev/null) +tag=$(git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null | sed 's@\^0$@@') # If we are a tag, show the part of the changelog till (but excluding) the last stable if [ -n "$tag" ]; then From 2158e26b9e602dd4e69c34b3f7e29d17da38e7ac Mon Sep 17 00:00:00 2001 From: JMcKiern Date: Sun, 26 Jan 2020 14:48:35 +0000 Subject: [PATCH 5/9] Fix #7950: Incorrect setup of normal screenshot viewport --- src/screenshot.cpp | 82 +++++++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/src/screenshot.cpp b/src/screenshot.cpp index 17efa9130c..07a8121fe3 100644 --- a/src/screenshot.cpp +++ b/src/screenshot.cpp @@ -712,38 +712,66 @@ static bool MakeSmallScreenshot(bool crashlog) */ void SetupScreenshotViewport(ScreenshotType t, ViewPort *vp) { - /* Determine world coordinates of screenshot */ - if (t == SC_WORLD) { - vp->zoom = ZOOM_LVL_WORLD_SCREENSHOT; + switch(t) { + case SC_VIEWPORT: + case SC_CRASHLOG: { + Window *w = FindWindowById(WC_MAIN_WINDOW, 0); + vp->virtual_left = w->viewport->virtual_left; + vp->virtual_top = w->viewport->virtual_top; + vp->virtual_width = w->viewport->virtual_width; + vp->virtual_height = w->viewport->virtual_height; - TileIndex north_tile = _settings_game.construction.freeform_edges ? TileXY(1, 1) : TileXY(0, 0); - TileIndex south_tile = MapSize() - 1; + /* Compute pixel coordinates */ + vp->left = 0; + vp->top = 0; + vp->width = _screen.width; + vp->height = _screen.height; + vp->overlay = w->viewport->overlay; + break; + } + case SC_WORLD: { + /* Determine world coordinates of screenshot */ + vp->zoom = ZOOM_LVL_WORLD_SCREENSHOT; - /* We need to account for a hill or high building at tile 0,0. */ - int extra_height_top = TilePixelHeight(north_tile) + 150; - /* If there is a hill at the bottom don't create a large black area. */ - int reclaim_height_bottom = TilePixelHeight(south_tile); + TileIndex north_tile = _settings_game.construction.freeform_edges ? TileXY(1, 1) : TileXY(0, 0); + TileIndex south_tile = MapSize() - 1; - vp->virtual_left = RemapCoords(TileX(south_tile) * TILE_SIZE, TileY(north_tile) * TILE_SIZE, 0).x; - vp->virtual_top = RemapCoords(TileX(north_tile) * TILE_SIZE, TileY(north_tile) * TILE_SIZE, extra_height_top).y; - vp->virtual_width = RemapCoords(TileX(north_tile) * TILE_SIZE, TileY(south_tile) * TILE_SIZE, 0).x - vp->virtual_left + 1; - vp->virtual_height = RemapCoords(TileX(south_tile) * TILE_SIZE, TileY(south_tile) * TILE_SIZE, reclaim_height_bottom).y - vp->virtual_top + 1; - } else { - vp->zoom = (t == SC_ZOOMEDIN) ? _settings_client.gui.zoom_min : ZOOM_LVL_VIEWPORT; + /* We need to account for a hill or high building at tile 0,0. */ + int extra_height_top = TilePixelHeight(north_tile) + 150; + /* If there is a hill at the bottom don't create a large black area. */ + int reclaim_height_bottom = TilePixelHeight(south_tile); - Window *w = FindWindowById(WC_MAIN_WINDOW, 0); - vp->virtual_left = w->viewport->virtual_left; - vp->virtual_top = w->viewport->virtual_top; - vp->virtual_width = w->viewport->virtual_width; - vp->virtual_height = w->viewport->virtual_height; + vp->virtual_left = RemapCoords(TileX(south_tile) * TILE_SIZE, TileY(north_tile) * TILE_SIZE, 0).x; + vp->virtual_top = RemapCoords(TileX(north_tile) * TILE_SIZE, TileY(north_tile) * TILE_SIZE, extra_height_top).y; + vp->virtual_width = RemapCoords(TileX(north_tile) * TILE_SIZE, TileY(south_tile) * TILE_SIZE, 0).x - vp->virtual_left + 1; + vp->virtual_height = RemapCoords(TileX(south_tile) * TILE_SIZE, TileY(south_tile) * TILE_SIZE, reclaim_height_bottom).y - vp->virtual_top + 1; + + /* Compute pixel coordinates */ + vp->left = 0; + vp->top = 0; + vp->width = UnScaleByZoom(vp->virtual_width, vp->zoom); + vp->height = UnScaleByZoom(vp->virtual_height, vp->zoom); + vp->overlay = nullptr; + break; + } + default: { + vp->zoom = (t == SC_ZOOMEDIN) ? _settings_client.gui.zoom_min : ZOOM_LVL_VIEWPORT; + + Window *w = FindWindowById(WC_MAIN_WINDOW, 0); + vp->virtual_left = w->viewport->virtual_left; + vp->virtual_top = w->viewport->virtual_top; + vp->virtual_width = w->viewport->virtual_width; + vp->virtual_height = w->viewport->virtual_height; + + /* Compute pixel coordinates */ + vp->left = 0; + vp->top = 0; + vp->width = UnScaleByZoom(vp->virtual_width, vp->zoom); + vp->height = UnScaleByZoom(vp->virtual_height, vp->zoom); + vp->overlay = nullptr; + break; + } } - - /* Compute pixel coordinates */ - vp->left = 0; - vp->top = 0; - vp->width = UnScaleByZoom(vp->virtual_width, vp->zoom); - vp->height = UnScaleByZoom(vp->virtual_height, vp->zoom); - vp->overlay = nullptr; } /** From fff05cf11783c6f698afa29d9f663a26501b3d3a Mon Sep 17 00:00:00 2001 From: PeterN Date: Sun, 26 Jan 2020 16:19:04 +0000 Subject: [PATCH 6/9] Fix #7868: Missing override attribute. (#7963) --- src/newgrf_roadtype.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/newgrf_roadtype.h b/src/newgrf_roadtype.h index 56b65f1271..07451f6566 100644 --- a/src/newgrf_roadtype.h +++ b/src/newgrf_roadtype.h @@ -32,7 +32,7 @@ struct RoadTypeResolverObject : public ResolverObject { RoadTypeResolverObject(const RoadTypeInfo *rti, TileIndex tile, TileContext context, RoadTypeSpriteGroup rtsg, uint32 param1 = 0, uint32 param2 = 0); - /* virtual */ ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) + ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override { switch (scope) { case VSG_SCOPE_SELF: return &this->roadtype_scope; @@ -40,7 +40,7 @@ struct RoadTypeResolverObject : public ResolverObject { } } - /* virtual */ const SpriteGroup *ResolveReal(const RealSpriteGroup *group) const; + const SpriteGroup *ResolveReal(const RealSpriteGroup *group) const override; GrfSpecFeature GetFeature() const override; uint32 GetDebugID() const override; From 791eaedb64e059fefe4a73fe798b1f05f9918f6d Mon Sep 17 00:00:00 2001 From: translators Date: Sun, 26 Jan 2020 19:45:44 +0100 Subject: [PATCH 7/9] Update: Translations from eints tamil: 13 changes by aswn --- src/lang/tamil.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/lang/tamil.txt b/src/lang/tamil.txt index 05ba96444a..bbc5ebeb14 100644 --- a/src/lang/tamil.txt +++ b/src/lang/tamil.txt @@ -938,6 +938,7 @@ STR_GAME_OPTIONS_LANGUAGE :{BLACK}மொ STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}பயன்படுத்தப்போகும் மொழியினை தேர்ந்தெடு STR_GAME_OPTIONS_FULLSCREEN :{BLACK}முழு படம் +STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}OpenTTD-ஐ முழுத்திரையில் விளையாட இந்த கட்டத்தினை சொடுக்கவும் STR_GAME_OPTIONS_RESOLUTION :{BLACK}திரையின் அளவு STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}திரை அளவினைத் தேர்ந்தெடுக்கவும் @@ -977,6 +978,7 @@ STR_CURRENCY_INCREASE_EXCHANGE_RATE_TOOLTIP :{BLACK}உங STR_CURRENCY_SET_EXCHANGE_RATE_TOOLTIP :{BLACK}உங்களது நாணயத்தின் நாணயமாற்று விகிதத்தை ஒரு பவுண்டாக (£) அமைக்கவும் STR_CURRENCY_SEPARATOR :{LTBLUE}பிரிப்பான்: {ORANGE}{STRING} +STR_CURRENCY_SET_CUSTOM_CURRENCY_SEPARATOR_TOOLTIP :{BLACK}உங்களது நாணயத்தின் பிரியியினை அமை STR_CURRENCY_PREFIX :{LTBLUE}முன் ஒட்டு: {ORANGE}{STRING} STR_CURRENCY_SET_CUSTOM_CURRENCY_PREFIX_TOOLTIP :{BLACK}உங்கள் நாணயத்தின் முன் ஒட்டத்தினை அமையுங்கள் @@ -1275,6 +1277,7 @@ STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_ALL_NON_LANDSCAPING :அனைத் STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_ALL_ACTIONS :அனைத்து செயல்கள் STR_CONFIG_SETTING_ADVANCED_VEHICLE_LISTS :வாகனப் பட்டியலினைப் பயன்படுத்தவும்: {STRING} STR_CONFIG_SETTING_LOADING_INDICATORS_HELPTEXT :வாகனங்களை ஏற்றுவதற்கு அல்லது இறக்குவதற்கு மேலே ஏற்றுதல் குறிகாட்டிகள் காட்டப்படுகிறதா என்பதைத் தேர்ந்தெடுக்கவும் +STR_CONFIG_SETTING_TIMETABLE_IN_TICKS :கால அட்டவணையை நாட்களில் அல்லாமல் சொடுக்குகளில் காட்டு: {STRING} STR_CONFIG_SETTING_TIMETABLE_SHOW_ARRIVAL_DEPARTURE :கால அட்டவணைகளில் காலங்களைக் காட்டவும்: {STRING} STR_CONFIG_SETTING_QUICKGOTO :வாகன கட்டளைகளை விரிவாக உருவாக்கவும்: {STRING} STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_FIRST :முதலில் கிடைக்கும் @@ -1365,6 +1368,7 @@ STR_CONFIG_SETTING_COLOURED_NEWS_YEAR :நிற ச STR_CONFIG_SETTING_STARTING_YEAR :தொடங்கும் வருடம்: {STRING} STR_CONFIG_SETTING_SMOOTH_ECONOMY :இயல்பான பொருளாதாரத்தினைச் செயல்படுத்தவும் (அதிகமான, சிறிய மாற்றங்கள்): {STRING} STR_CONFIG_SETTING_ALLOW_SHARES :மற்ற நிறுவனங்களின் பங்குகளை வாங்குவதை அனுமதிக்கவும்: {STRING} +STR_CONFIG_SETTING_MIN_YEARS_FOR_SHARES :பங்குகள் பரிமாற்றத்திற்கு தேவையான குறைந்தபட்ச நிறுவன வயது: {STRING} STR_CONFIG_SETTING_DRAG_SIGNALS_DENSITY :இழுத்தால், சிக்னல்களை இடவும், ஒவ்வொறு: {STRING} STR_CONFIG_SETTING_DRAG_SIGNALS_DENSITY_VALUE :{COMMA} கட்ட{P 0 "ம்" ங்கள்} STR_CONFIG_SETTING_DRAG_SIGNALS_FIXED_DISTANCE :இழுக்கும்போது, சிக்னல்களுக்கு இடையே சீரான இடைவேளியினை விடவும்: {STRING} @@ -1432,6 +1436,7 @@ STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER :தொடக் STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER_HELPTEXT :ஆட்டத்தின் தொடக்கத்தில் மாநகரங்களின் அளவு நகரங்களை ஒப்பிடுகையில் STR_CONFIG_SETTING_DISTRIBUTION_MANUAL :கைமுறை +STR_CONFIG_SETTING_DISTRIBUTION_ASYMMETRIC :சமச்சீர்மையிலா STR_CONFIG_SETTING_DISTRIBUTION_SYMMETRIC :சமச்சீரான STR_CONFIG_SETTING_DISTRIBUTION_PAX :பயணிகள் பரிமாற்றம் வகை: {STRING} STR_CONFIG_SETTING_DISTRIBUTION_MAIL :அஞ்சல் பரிமாற்றம் வகை: {STRING} @@ -1470,6 +1475,7 @@ STR_CONFIG_SETTING_SOUND :{ORANGE}ஒல STR_CONFIG_SETTING_INTERFACE :{ORANGE}Interface STR_CONFIG_SETTING_INTERFACE_GENERAL :{ORANGE} பொதுவான STR_CONFIG_SETTING_INTERFACE_CONSTRUCTION :{ORANGE}கட்டுமானம் +STR_CONFIG_SETTING_ADVISORS :{ORANGE}செய்திகள் / அறிவுரைஞர்கள் STR_CONFIG_SETTING_COMPANY :{ORANGE}நிறுவனம் STR_CONFIG_SETTING_ACCOUNTING :{ORANGE}கணக்கியல் STR_CONFIG_SETTING_VEHICLES :{ORANGE}வாகனங்கள் @@ -1503,6 +1509,7 @@ STR_CONFIG_ERROR_ARRAY :{WHITE}... '{ST STR_CONFIG_ERROR_INVALID_VALUE :{WHITE}... செல்லாத மதிப்பு '{STRING}' '{STRING}' இற்கு STR_CONFIG_ERROR_INVALID_GRF :{WHITE}... பயன்படுத்த இயலாத NewGRF இனை பயன்படுத்தவில்லை '{STRING}': {STRING} STR_CONFIG_ERROR_INVALID_GRF_NOT_FOUND :கிடைக்கவில்லை +STR_CONFIG_ERROR_INVALID_GRF_UNSAFE :நிலையான பயன்பாட்டிற்கு உகந்தது அல்ல STR_CONFIG_ERROR_INVALID_GRF_SYSTEM :கணினி NewGRF STR_CONFIG_ERROR_INVALID_GRF_INCOMPATIBLE :இந்த OpenTTD பதிப்புடன் பயன்படுத்த இயலாது STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN :தெரியவில்லை @@ -2408,7 +2415,9 @@ STR_FRAMERATE_BYTES_GOOD :{LTBLUE}{BYTES} STR_FRAMERATE_BYTES_WARN :{YELLOW}{BYTES} STR_FRAMERATE_BYTES_BAD :{RED}{BYTES} ############ Leave those lines in this order!! +STR_FRAMERATE_GL_LANDSCAPE :{BLACK} உலக சொடுக்குகள்: STR_FRAMERATE_GL_LINKGRAPH :{BLACK} இணைப்பு வரைபட தாமதம்: +STR_FRAMERATE_SOUND :{BLACK}இசை இயக்கம்: STR_FRAMERATE_ALLSCRIPTS :{BLACK} GS/AI மொத்தம்: ############ End of leave-in-this-order ############ Leave those lines in this order!! @@ -2551,6 +2560,7 @@ STR_NEWGRF_SETTINGS_VERSION :{BLACK}பத STR_NEWGRF_SETTINGS_MIN_VERSION :{BLACK}குறைந்த். பயன்படுத்தக்கூடிய பதிப்பு: {SILVER}{NUM} STR_NEWGRF_SETTINGS_MD5SUM :{BLACK}MD5sum: {SILVER}{STRING} STR_NEWGRF_SETTINGS_PALETTE :{BLACK}தட்டு: {SILVER}{STRING} +STR_NEWGRF_SETTINGS_PALETTE_DEFAULT_32BPP :முதன்மை / 32 bpp STR_NEWGRF_SETTINGS_PALETTE_LEGACY :மரபுரிமை (W) STR_NEWGRF_SETTINGS_PARAMETER :{BLACK}குணாதிசயங்கள்: {SILVER}{STRING} STR_NEWGRF_SETTINGS_PARAMETER_NONE :ஒன்றுமில்லை @@ -2952,6 +2962,7 @@ STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE :{YELLOW}தொ STR_INDUSTRY_VIEW_REQUIRES :{BLACK}வேண்டியவன: +STR_INDUSTRY_VIEW_ACCEPT_CARGO_AMOUNT :{YELLOW}{STRING}{BLACK}: {CARGO_SHORT} காத்துக்கொண்டிருக்கிறது{STRING} STR_CONFIG_GAME_PRODUCTION :{WHITE}தயாரிப்பினை மாற்றவும் (8 இன் பெருக்கங்கள், 2040 வரை) STR_CONFIG_GAME_PRODUCTION_LEVEL :{WHITE}தயாரிப்பு அளவினை மாற்றவும் (சதவிகிதம், 800% வரை) @@ -3079,6 +3090,7 @@ STR_BUY_VEHICLE_ROAD_VEHICLE_RENAME_TOOLTIP :{BLACK}சா STR_BUY_VEHICLE_SHIP_RENAME_TOOLTIP :{BLACK}கப்பல் வகையின் பெயரினை மாற்றவும் STR_BUY_VEHICLE_AIRCRAFT_RENAME_TOOLTIP :{BLACK}விமான வகையின் பெயரினை மாற்றவும் +STR_BUY_VEHICLE_AIRCRAFT_HIDE_TOGGLE_BUTTON :{BLACK}மறை STR_BUY_VEHICLE_ROAD_VEHICLE_SHOW_TOGGLE_BUTTON :{BLACK}காட்சி @@ -3201,6 +3213,7 @@ STR_REPLACE_ELRAIL_VEHICLES :மின்ச STR_REPLACE_MONORAIL_VEHICLES :மோனோ இரயில் வாகனங்கள் STR_REPLACE_MAGLEV_VEHICLES :மேக்லெவ் வாகனங்கள் +STR_REPLACE_ROAD_VEHICLES :சாலை வாகனங்கள் STR_REPLACE_TRAM_VEHICLES :அமிழ் தண்டூர்தி வாகனங்கள் STR_REPLACE_REMOVE_WAGON :{BLACK}பெட்டி நீக்கம்: {ORANGE}{STRING} From a38122e6f21374440f5d644b5d54b94ebfac4300 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 26 Jan 2020 21:58:49 +0100 Subject: [PATCH 8/9] Fix: [AzurePipelines] the changelog-generation-script was mixing UTC and non-UTC 'date -u' returns the time in UTC. 'git log' uses local time. In result, when a machine is on for example +0100, it would generate the changelog of 7 days 1 hour, instead of 7 days. This is a silly oversight. --- azure-pipelines/changelog.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines/changelog.sh b/azure-pipelines/changelog.sh index 8231f66672..ea0da948c1 100755 --- a/azure-pipelines/changelog.sh +++ b/azure-pipelines/changelog.sh @@ -12,5 +12,5 @@ fi # In all other cases, show the git log of the last 7 days revdate=$(git log -1 --pretty=format:"%ci") -last_week=$(date -u -d "$revdate -7days" +"%Y-%m-%d %H:%M") +last_week=$(date -d "$revdate -7days" +"%Y-%m-%d %H:%M") git log --after="${last_week}" --pretty=fuller From 2fee030a26996a167adbf28f96fca8c4fd749005 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Tue, 14 Jan 2020 03:11:57 +0000 Subject: [PATCH 9/9] Change: Algorithm for transfer feeder payments The original algorithm pays intermediate legs in feeder systems based on the start and end stations of that particular leg. This tends to result in large negative payments on the final leg for journeys with many feeder legs, as the overall feeder payment increases with the number of legs, and the final leg is penalised for discrepancies between the previous leg payments and the actual payment for delivery from the source to the destination. The feeder share setting is a partial mitigation, however it is difficult to tune as a suitable value depends on the number of legs and the network topology, which are often not the same for all vehicles. The new incremental algorithm pays the cargo payment from the source station to the end station of the current leg, minus any previous transfer feeder payments for each leg. This prevents unbounded increase of feeder payments and therefore avoids the issue of excessive negative payments on the final leg. Feeder payments may be negative, e.g. in the case of poorly performing or highly indirect legs. This is better than penalising the final leg. This mode reduces the need to tune the feeder shares setting to the current network. The feeder share setting applies in the existing way. --- src/economy.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/economy.cpp b/src/economy.cpp index a0907efbe9..00bdd6f534 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -1216,10 +1216,11 @@ void CargoPayment::PayFinalDelivery(const CargoPacket *cp, uint count) */ Money CargoPayment::PayTransfer(const CargoPacket *cp, uint count) { - Money profit = GetTransportedGoodsIncome( + Money profit = -cp->FeederShare(count) + GetTransportedGoodsIncome( count, - /* pay transfer vehicle for only the part of transfer it has done: ie. cargo_loaded_at_xy to here */ - DistanceManhattan(cp->LoadedAtXY(), Station::Get(this->current_station)->xy), + /* pay transfer vehicle the difference between the payment for the journey from + * the source to the current point, and the sum of the previous transfer payments */ + DistanceManhattan(cp->SourceStationXY(), Station::Get(this->current_station)->xy), cp->DaysInTransit(), this->ct);