From 4129b418ccab3ed2bc6b7ab196e99d401ca67599 Mon Sep 17 00:00:00 2001 From: frosch Date: Fri, 13 Mar 2009 21:28:40 +0000 Subject: [PATCH] (svn r15701) -Fix [FS#2595]: Blame NewGRFs returning inconsistent information in purchase-list/after building before users have a chance to blame OpenTTD for incorrectly autorenewing/-replacing. --- src/articulated_vehicles.cpp | 60 ++++++++++++++++++++++++++++++++++++ src/articulated_vehicles.h | 2 ++ src/lang/english.txt | 3 ++ src/newgrf_config.h | 1 + src/roadveh_cmd.cpp | 5 ++- src/train_cmd.cpp | 22 +++---------- src/vehicle.cpp | 37 ++++++++++++++++++++++ src/vehicle_func.h | 2 ++ 8 files changed, 114 insertions(+), 18 deletions(-) diff --git a/src/articulated_vehicles.cpp b/src/articulated_vehicles.cpp index 4729b5a55a..2a6fb6b2c9 100644 --- a/src/articulated_vehicles.cpp +++ b/src/articulated_vehicles.cpp @@ -9,6 +9,8 @@ #include "newgrf_engine.h" #include "vehicle_func.h" +#include "table/strings.h" + static const uint MAX_ARTICULATED_PARTS = 100; ///< Maximum of articulated parts per vehicle, i.e. when to abort calling the articulated vehicle callback. uint CountArticulatedParts(EngineID engine_type, bool purchase_window) @@ -238,6 +240,64 @@ bool IsArticulatedVehicleCarryingDifferentCargos(const Vehicle *v, CargoID *carg return false; } +/** + * Checks whether the specs of freshly build articulated vehicles are consistent with the information specified in the purchase list. + * Only essential information is checked to leave room for magic tricks/workarounds to grfcoders. + * It checks: + * For autoreplace/-renew: + * - Default cargo type (without capacity) + * - intersection and union of refit masks. + */ +void CheckConsistencyOfArticulatedVehicle(const Vehicle *v) +{ + const Engine *engine = GetEngine(v->engine_type); + + uint32 purchase_refit_union = GetUnionOfArticulatedRefitMasks(v->engine_type, v->type, true); + uint32 purchase_refit_intersection = GetIntersectionOfArticulatedRefitMasks(v->engine_type, v->type, true); + uint16 *purchase_default_capacity = GetCapacityOfArticulatedParts(v->engine_type, v->type); + + uint32 real_refit_union = 0; + uint32 real_refit_intersection = UINT_MAX; + uint16 real_default_capacity[NUM_CARGO]; + memset(real_default_capacity, 0, sizeof(real_default_capacity)); + + do { + uint32 refit_mask = GetAvailableVehicleCargoTypes(v->engine_type, v->type, true); + real_refit_union |= refit_mask; + if (refit_mask != 0) real_refit_intersection &= refit_mask; + + assert(v->cargo_type < NUM_CARGO); + real_default_capacity[v->cargo_type] += v->cargo_cap; + + switch (v->type) { + case VEH_TRAIN: + v = (EngineHasArticPart(v) ? GetNextArticPart(v) : NULL); + break; + + case VEH_ROAD: + v = (RoadVehHasArticPart(v) ? v->Next() : NULL); + break; + + default: + v = NULL; + break; + } + } while (v != NULL); + + /* Check whether the vehicle carries more cargos than expected */ + bool carries_more = false; + for (CargoID cid = 0; cid < NUM_CARGO; cid++) { + if (real_default_capacity[cid] != 0 && purchase_default_capacity[cid] == 0) { + carries_more = true; + break; + } + } + + /* show a warning once for each GRF after each game load */ + if (real_refit_union != purchase_refit_union || real_refit_intersection != purchase_refit_intersection || carries_more) { + ShowNewGrfVehicleError(engine->index, STR_NEWGRF_BUGGY, STR_NEWGRF_BUGGY_ARTICULATED_CARGO, GBUG_VEH_REFIT, false); + } +} void AddArticulatedParts(Vehicle **vl, VehicleType type) { diff --git a/src/articulated_vehicles.h b/src/articulated_vehicles.h index 02e546364c..b26cc28703 100644 --- a/src/articulated_vehicles.h +++ b/src/articulated_vehicles.h @@ -15,5 +15,7 @@ uint32 GetUnionOfArticulatedRefitMasks(EngineID engine, VehicleType type, bool i uint32 GetIntersectionOfArticulatedRefitMasks(EngineID engine, VehicleType type, bool include_initial_cargo_type); bool IsArticulatedVehicleCarryingDifferentCargos(const Vehicle *v, CargoID *cargo_type); bool IsArticulatedVehicleRefittable(EngineID engine); +void CheckConsistencyOfArticulatedVehicle(const Vehicle *v); + #endif /* ARTICULATED_VEHICLES_H */ diff --git a/src/lang/english.txt b/src/lang/english.txt index 5889648602..739ea4c904 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3170,6 +3170,9 @@ STR_NEWGRF_BROKEN :{WHITE}Behaviou STR_NEWGRF_BROKEN_VEHICLE_LENGTH :{WHITE}It changes vehicle length for '{1:ENGINE}' when not inside a depot. STR_BROKEN_VEHICLE_LENGTH :{WHITE}Train '{VEHICLE}' belonging to '{COMPANY}' has invalid length. It is probably caused by problems with NewGRFs. Game may desync or crash. +STR_NEWGRF_BUGGY :{WHITE}NewGRF '{0:RAW_STRING}' provides incorrect information. +STR_NEWGRF_BUGGY_ARTICULATED_CARGO :{WHITE}Cargo/refit information for '{1:ENGINE}' differs from purchase list after construction. This might cause autorenew/-replace to fail refitting correctly. + STR_LOADGAME_REMOVED_TRAMS :{WHITE}Game was saved in version without tram support. All trams have been removed. STR_CURRENCY_WINDOW :{WHITE}Custom currency diff --git a/src/newgrf_config.h b/src/newgrf_config.h index 4dedd5b29f..aba8dc330d 100644 --- a/src/newgrf_config.h +++ b/src/newgrf_config.h @@ -31,6 +31,7 @@ enum GRFStatus { /** Encountered GRF bugs */ enum GRFBugs { GBUG_VEH_LENGTH, ///< Length of rail vehicle changes when not inside a depot + GBUG_VEH_REFIT, ///< Articulated vehicles carry different cargos resp. are differently refittable than specified in purchase list }; /** Status of post-gameload GRF compatibility check */ diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index fa68a9247c..d7d5b927d8 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -275,10 +275,13 @@ CommandCost CmdBuildRoadVeh(TileIndex tile, DoCommandFlag flags, uint32 p1, uint InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); InvalidateWindowClassesData(WC_ROADVEH_LIST, 0); InvalidateWindow(WC_COMPANY, v->owner); - if (IsLocalCompany()) + if (IsLocalCompany()) { InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Road window + } GetCompany(_current_company)->num_engines[p1]++; + + CheckConsistencyOfArticulatedVehicle(v); } return cost; diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 8cccb6763f..eede8507d6 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -178,23 +178,7 @@ static void RailVehicleLengthChanged(const Vehicle *u) uint32 grfid = engine->grffile->grfid; GRFConfig *grfconfig = GetGRFConfig(grfid); if (GamelogGRFBugReverse(grfid, engine->internal_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) { - SetBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH); - SetDParamStr(0, grfconfig->name); - SetDParam(1, u->engine_type); - ShowErrorMessage(STR_NEWGRF_BROKEN_VEHICLE_LENGTH, STR_NEWGRF_BROKEN, 0, 0); - - /* debug output */ - char buffer[512]; - - SetDParamStr(0, grfconfig->name); - GetString(buffer, STR_NEWGRF_BROKEN, lastof(buffer)); - DEBUG(grf, 0, "%s", buffer + 3); - - SetDParam(1, u->engine_type); - GetString(buffer, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, lastof(buffer)); - DEBUG(grf, 0, "%s", buffer + 3); - - if (!_networking) _pause_game = -1; + ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true); } } @@ -678,6 +662,8 @@ static CommandCost CmdBuildRailWagon(EngineID engine, TileIndex tile, DoCommandF InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Train window } GetCompany(_current_company)->num_engines[engine]++; + + CheckConsistencyOfArticulatedVehicle(v); } return value; @@ -857,6 +843,8 @@ CommandCost CmdBuildRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, } GetCompany(_current_company)->num_engines[p1]++; + + CheckConsistencyOfArticulatedVehicle(v); } return value; diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 0f62292b7b..bdc6d0c6b7 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -3,7 +3,9 @@ /** @file vehicle.cpp Base implementations of all vehicles. */ #include "stdafx.h" +#include "gui.h" #include "openttd.h" +#include "debug.h" #include "roadveh.h" #include "ship.h" #include "spritecache.h" @@ -36,6 +38,7 @@ #include "core/smallmap_type.hpp" #include "depot_func.h" #include "settings_type.h" +#include "network/network.h" #include "table/sprites.h" #include "table/strings.h" @@ -101,6 +104,40 @@ bool Vehicle::NeedsAutomaticServicing() const return NeedsServicing(); } +/** + * Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking. + * @param engine The engine that caused the problem + * @param part1 Part 1 of the error message, taking the grfname as parameter 1 + * @param part2 Part 2 of the error message, taking the engine as parameter 2 + * @param bug_type Flag to check and set in grfconfig + * @param critical Shall the "OpenTTD might crash"-message be shown when the player tries to unpause? + */ +void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical) +{ + const Engine *e = GetEngine(engine); + uint32 grfid = e->grffile->grfid; + GRFConfig *grfconfig = GetGRFConfig(grfid); + + if (!HasBit(grfconfig->grf_bugs, bug_type)) { + SetBit(grfconfig->grf_bugs, bug_type); + SetDParamStr(0, grfconfig->name); + SetDParam(1, engine); + ShowErrorMessage(part2, part1, 0, 0); + if (!_networking) _pause_game = (critical ? -1 : 1); + } + + /* debug output */ + char buffer[512]; + + SetDParamStr(0, grfconfig->name); + GetString(buffer, part1, lastof(buffer)); + DEBUG(grf, 0, "%s", buffer + 3); + + SetDParam(1, engine); + GetString(buffer, part2, lastof(buffer)); + DEBUG(grf, 0, "%s", buffer + 3); +} + StringID VehicleInTheWayErrMsg(const Vehicle *v) { switch (v->type) { diff --git a/src/vehicle_func.h b/src/vehicle_func.h index 4bbdb8f6ba..0e9e1edede 100644 --- a/src/vehicle_func.h +++ b/src/vehicle_func.h @@ -14,6 +14,7 @@ #include "vehicle_type.h" #include "engine_type.h" #include "transport_type.h" +#include "newgrf_config.h" #define is_custom_sprite(x) (x >= 0xFD) #define IS_CUSTOM_FIRSTHEAD_SPRITE(x) (x == 0xFD) @@ -46,6 +47,7 @@ void ViewportAddVehicles(DrawPixelInfo *dpi); SpriteID GetRotorImage(const Vehicle *v); +void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical); StringID VehicleInTheWayErrMsg(const Vehicle *v); bool HasVehicleOnTunnelBridge(TileIndex tile, TileIndex endtile, const Vehicle *ignore = NULL);