changeset 11351:11acaad65229 draft

(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.
author frosch <frosch@openttd.org>
date Fri, 13 Mar 2009 21:28:40 +0000
parents 8a62f1cb73a3
children 62978bce3c1c
files src/articulated_vehicles.cpp src/articulated_vehicles.h src/lang/english.txt src/newgrf_config.h src/roadveh_cmd.cpp src/train_cmd.cpp src/vehicle.cpp src/vehicle_func.h
diffstat 8 files changed, 114 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- 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 @@
 	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)
 {
--- a/src/articulated_vehicles.h
+++ b/src/articulated_vehicles.h
@@ -15,5 +15,7 @@
 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 */
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -3170,6 +3170,9 @@
 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
--- a/src/newgrf_config.h
+++ b/src/newgrf_config.h
@@ -31,6 +31,7 @@
 /** 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 */
--- a/src/roadveh_cmd.cpp
+++ b/src/roadveh_cmd.cpp
@@ -275,10 +275,13 @@
 		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;
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -178,23 +178,7 @@
 	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 @@
 			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 @@
 		}
 
 		GetCompany(_current_company)->num_engines[p1]++;
+
+		CheckConsistencyOfArticulatedVehicle(v);
 	}
 
 	return value;
--- 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 @@
 	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) {
--- 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 @@
 
 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);