From 4f76d929c6e872ebe21354cf14170a0536330a26 Mon Sep 17 00:00:00 2001 From: rubidium Date: Wed, 9 Jul 2008 19:13:21 +0000 Subject: [PATCH] (svn r13686) [0.6] -Backport from trunk: - Fix: Memory leak when NewGRFs got forcefully disabled and they defined GOTO labels (r13675) - Fix: Crash when drawing a non-real sprite caused by NewGRF interference [FS#2127] (r13674) - Fix: Disable static NewGRFs when non-static NewGRFs query them in the context of network games. This makes it impossible for static NewGRFs to disable non-static NewGRFs and 'bad' things happening because the non-static NewGRF doesn't know about the static NewGRF (r13576) - Fix: First determine where to *exactly* build a house before asking a NewGRF whether the location is good instead of possibly moving the house a tile after the NewGRF said the location is good (r13489) - Fix: Do not crash when resolving vehicle sprite groups with zero sprites (r13397) - Fix: In the purchase list, CB36 for capacity was not called for the first part of rail and road vehicles (r13385) --- src/articulated_vehicles.cpp | 4 +- src/lang/english.txt | 1 + src/newgrf.cpp | 85 ++++++++++++++++++++++++++---------- src/newgrf_engine.cpp | 4 +- src/spritecache.cpp | 10 ++++- src/spriteloader/png.cpp | 2 +- src/town_cmd.cpp | 12 ++--- 7 files changed, 84 insertions(+), 34 deletions(-) diff --git a/src/articulated_vehicles.cpp b/src/articulated_vehicles.cpp index fabafb8c83..df82d49d10 100644 --- a/src/articulated_vehicles.cpp +++ b/src/articulated_vehicles.cpp @@ -42,11 +42,11 @@ uint16 *GetCapacityOfArticulatedParts(EngineID engine, VehicleType type) if (type == VEH_TRAIN) { const RailVehicleInfo *rvi = RailVehInfo(engine); - capacity[rvi->cargo_type] = rvi->capacity; + capacity[rvi->cargo_type] = GetEngineProperty(engine, 0x14, rvi->capacity); if (rvi->railveh_type == RAILVEH_MULTIHEAD) capacity[rvi->cargo_type] += rvi->capacity; } else if (type == VEH_ROAD) { const RoadVehicleInfo *rvi = RoadVehInfo(engine); - capacity[rvi->cargo_type] = rvi->capacity; + capacity[rvi->cargo_type] = GetEngineProperty(engine, 0x0F, rvi->capacity); } if (!HasBit(EngInfo(engine)->callbackmask, CBM_VEHICLE_ARTIC_ENGINE)) return capacity; diff --git a/src/lang/english.txt b/src/lang/english.txt index c9755f37d9..b89efe563d 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3098,6 +3098,7 @@ STR_NEWGRF_ERROR_LOAD_AFTER :{STRING} must b STR_NEWGRF_ERROR_OTTD_VERSION_NUMBER :{STRING} requires OpenTTD version {STRING} or better. STR_NEWGRF_ERROR_AFTER_TRANSLATED_FILE :the GRF file it was designed to translate STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED :Too many NewGRFs are loaded. +STR_NEWGRF_ERROR_STATIC_GRF_CAUSES_DESYNC :Loading {STRING} as static NewGRF with {STRING} could cause desyncs. STR_NEWGRF_ADD :{BLACK}Add STR_NEWGRF_ADD_TIP :{BLACK}Add a NewGRF file to the list diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 0641ee1968..943ca63a68 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -44,6 +44,7 @@ #include "road_func.h" #include "player_base.h" #include "settings_type.h" +#include "network/network.h" #include "map_func.h" #include "table/strings.h" @@ -224,6 +225,23 @@ static GRFFile *GetFileByFilename(const char *filename) return file; } +/** Reset all NewGRFData that was used only while processing data */ +static void ClearTemporaryNewGRFData() +{ + /* Clear the GOTO labels used for GRF processing */ + for (GRFLabel *l = _cur_grffile->label; l != NULL;) { + GRFLabel *l2 = l->next; + free(l); + l = l2; + } + _cur_grffile->label = NULL; + + /* Clear the list of spritegroups */ + free(_cur_grffile->spritegroups); + _cur_grffile->spritegroups = NULL; + _cur_grffile->spritegroups_count = 0; +} + /** Used when setting an object's property to map to the GRF's strings * while taking in consideration the "drift" between TTDPatch string system and OpenTTD's one @@ -3670,6 +3688,32 @@ static void CfgApply(byte *buf, int len) } } +/** + * Disable a static NewGRF when it is influencing another (non-static) + * NewGRF as this could cause desyncs. + * + * We could just tell the NewGRF querying that the file doesn't exist, + * but that might give unwanted results. Disabling the NewGRF gives the + * best result as no NewGRF author can complain about that. + * @param c the NewGRF to disable. + */ +static void DisableStaticNewGRFInfluencingNonStaticNewGRFs(GRFConfig *c) +{ + if (c->error != NULL) { + free(c->error->custom_message); + free(c->error->data); + free(c->error); + } + c->status = GCS_DISABLED; + c->error = CallocT(1); + c->error->data = strdup(_cur_grfconfig->name); + c->error->severity = STR_NEWGRF_ERROR_MSG_FATAL; + c->error->message = STR_NEWGRF_ERROR_STATIC_GRF_CAUSES_DESYNC; + + ClearTemporaryNewGRFData(); + _skip_sprites = -1; +} + /* Action 0x07 */ /* Action 0x09 */ static void SkipIf(byte *buf, int len) @@ -3723,7 +3767,12 @@ static void SkipIf(byte *buf, int len) if (param == 0x88 && condtype != 0x0B && condtype != 0x0C) { /* GRF ID checks */ - const GRFConfig *c = GetGRFConfig(cond_val); + GRFConfig *c = GetGRFConfig(cond_val); + + if (c != NULL && HasBit(c->flags, GCF_STATIC) && !HasBit(_cur_grfconfig->flags, GCF_STATIC) && c->status != GCS_DISABLED && _networking) { + DisableStaticNewGRFInfluencingNonStaticNewGRFs(c); + c = NULL; + } if (condtype != 10 && c == NULL) { grfmsg(7, "SkipIf: GRFID 0x%08X unknown, skipping test", BSWAP32(cond_val)); @@ -3821,6 +3870,7 @@ static void SkipIf(byte *buf, int len) /* If an action 8 hasn't been encountered yet, disable the grf. */ if (_cur_grfconfig->status != GCS_ACTIVATED) { _cur_grfconfig->status = GCS_DISABLED; + ClearTemporaryNewGRFData(); } } } @@ -3993,7 +4043,7 @@ static void GRFLoadError(byte *buf, int len) /* This is a fatal error, so make sure the GRF is deactivated and no * more of it gets loaded. */ _cur_grfconfig->status = GCS_DISABLED; - + ClearTemporaryNewGRFData(); _skip_sprites = -1; } @@ -4176,6 +4226,7 @@ static uint32 PerformGRM(uint32 *grm, uint16 num_ids, uint16 count, uint8 op, ui /* Deactivate GRF */ grfmsg(0, "ParamSet: GRM: Unable to allocate %d %s, deactivating", count, type); _cur_grfconfig->status = GCS_DISABLED; + ClearTemporaryNewGRFData(); _skip_sprites = -1; return UINT_MAX; } @@ -4266,8 +4317,8 @@ static void ParamSet(byte *buf, int len) if (_cur_spriteid + count >= 16384) { grfmsg(0, "ParamSet: GRM: Unable to allocate %d sprites; try changing NewGRF order", count); _cur_grfconfig->status = GCS_DISABLED; - - _skip_sprites = -1; + ClearTemporaryNewGRFData(); + _skip_sprites = -1; return; } @@ -4299,7 +4350,12 @@ static void ParamSet(byte *buf, int len) } else { /* Read another GRF File's parameter */ const GRFFile *file = GetFileByGRFID(data); - if (file == NULL || src1 >= file->param_end) { + GRFConfig *c = GetGRFConfig(data); + if (c != NULL && HasBit(c->status, GCF_STATIC) && !HasBit(_cur_grfconfig->status, GCF_STATIC) && _networking) { + /* Disable the read GRF if it is a static NewGRF. */ + DisableStaticNewGRFInfluencingNonStaticNewGRFs(c); + src1 = 0; + } else if (file == NULL || src1 >= file->param_end || (c != NULL && c->status == GCS_DISABLED)) { src1 = 0; } else { src1 = file->param[src1]; @@ -4584,6 +4640,7 @@ static void FeatureTownName(byte *buf, int len) grfmsg(0, "FeatureTownName: definition 0x%02X doesn't exist, deactivating", ref_id); DelGRFTownName(grfid); _cur_grfconfig->status = GCS_DISABLED; + ClearTemporaryNewGRFData(); _skip_sprites = -1; return; } @@ -4874,6 +4931,7 @@ static void TranslateGRFStrings(byte *buf, int len) _cur_grfconfig->error = error; _cur_grfconfig->status = GCS_DISABLED; + ClearTemporaryNewGRFData(); _skip_sprites = -1; return; } @@ -5241,23 +5299,6 @@ static void ResetNewGRFData() InitializeSpriteGroupPool(); } -/** Reset all NewGRFData that was used only while processing data */ -static void ClearTemporaryNewGRFData() -{ - /* Clear the GOTO labels used for GRF processing */ - for (GRFLabel *l = _cur_grffile->label; l != NULL;) { - GRFLabel *l2 = l->next; - free(l); - l = l2; - } - _cur_grffile->label = NULL; - - /* Clear the list of spritegroups */ - free(_cur_grffile->spritegroups); - _cur_grffile->spritegroups = NULL; - _cur_grffile->spritegroups_count = 0; -} - static void BuildCargoTranslationMap() { memset(_cur_grffile->cargo_map, 0xFF, sizeof(_cur_grffile->cargo_map)); diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp index 6e0cc07a57..552b5fca57 100644 --- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -866,7 +866,7 @@ SpriteID GetCustomEngineSprite(EngineID engine, const Vehicle *v, Direction dire NewVehicleResolver(&object, engine, v); group = Resolve(GetVehicleSpriteGroup(engine, v), &object); - if (group == NULL || group->type != SGT_RESULT) return 0; + if (group == NULL || group->type != SGT_RESULT || group->g.result.num_sprites == 0) return 0; return group->g.result.sprite + (direction % group->g.result.num_sprites); } @@ -890,7 +890,7 @@ SpriteID GetRotorOverrideSprite(EngineID engine, const Vehicle *v, bool info_vie group = GetWagonOverrideSpriteSet(engine, CT_DEFAULT, engine); group = Resolve(group, &object); - if (group == NULL || group->type != SGT_RESULT) return 0; + if (group == NULL || group->type != SGT_RESULT || group->g.result.num_sprites == 0) return 0; if (v == NULL) return group->g.result.sprite; diff --git a/src/spritecache.cpp b/src/spritecache.cpp index da0f6892f5..1c4bed6319 100644 --- a/src/spritecache.cpp +++ b/src/spritecache.cpp @@ -28,6 +28,7 @@ struct SpriteCache { uint32 file_pos; uint16 file_slot; int16 lru; + bool real_sprite; ///< In some cases a single sprite is misused by two NewGRFs. Once as real sprite and once as non-real sprite. If the non-real sprite gets into the cache it might be drawn as real sprite which causes enormous trouble. }; @@ -176,6 +177,7 @@ static void* ReadSprite(SpriteCache *sc, SpriteID id, bool real_sprite) byte *dest = (byte *)AllocSprite(num); sc->ptr = dest; + sc->real_sprite = false; FioReadBlock(dest, num); return sc->ptr; @@ -217,9 +219,13 @@ static void* ReadSprite(SpriteCache *sc, SpriteID id, bool real_sprite) } } + sc->real_sprite = false; + return sc->ptr; } + sc->real_sprite = true; + if (!real_sprite) { static byte warning_level = 0; DEBUG(sprite, warning_level, "Tried to load real sprite #%d as a non sprite. Probable cause: NewGRF interference", id); @@ -255,6 +261,7 @@ bool LoadNextSprite(int load_index, byte file_slot, uint file_sprite_id) sc->ptr = NULL; sc->lru = 0; sc->id = file_sprite_id; + sc->real_sprite = false; return true; } @@ -269,6 +276,7 @@ void DupSprite(SpriteID old_spr, SpriteID new_spr) scnew->file_pos = scold->file_pos; scnew->ptr = NULL; scnew->id = scold->id; + scnew->real_sprite = scold->real_sprite; } @@ -454,7 +462,7 @@ const void *GetRawSprite(SpriteID sprite, bool real_sprite) p = sc->ptr; /* Load the sprite, if it is not loaded, yet */ - if (p == NULL) p = ReadSprite(sc, sprite, real_sprite); + if (p == NULL || sc->real_sprite != real_sprite) p = ReadSprite(sc, sprite, real_sprite); return p; } diff --git a/src/spriteloader/png.cpp b/src/spriteloader/png.cpp index 1041be7d5f..363dce46cf 100644 --- a/src/spriteloader/png.cpp +++ b/src/spriteloader/png.cpp @@ -44,7 +44,7 @@ static bool OpenPNGFile(const char *filename, uint32 id, bool mask) return false; } -static bool LoadPNG(SpriteLoader::Sprite *sprite, const char *filename, uint32 id, bool mask) +static bool LoadPNG(SpriteLoader::Sprite *sprite, const char *filename, uint32 id, volatile bool mask) { png_byte header[8]; png_structp png_ptr; diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 202cafcd53..47b7b73605 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -1902,11 +1902,6 @@ static bool BuildTownHouse(Town *t, TileIndex tile) } if ((hs->extra_flags & BUILDING_IS_HISTORICAL) && !_generating_world) continue; - - if (HasBit(hs->callback_mask, CBM_HOUSE_ALLOW_CONSTRUCTION)) { - uint16 callback_res = GetHouseCallback(CBID_HOUSE_ALLOW_CONSTRUCTION, 0, 0, house, t, tile); - if (callback_res != CALLBACK_FAILED && GB(callback_res, 0, 8) == 0) continue; - } } if (_cur_year < hs->min_date || _cur_year > hs->max_date) continue; @@ -1920,7 +1915,7 @@ static bool BuildTownHouse(Town *t, TileIndex tile) SetBit(oneof, TOWN_HAS_STADIUM); } - if (HASBITS(t->flags12 , oneof)) continue; + if (HASBITS(t->flags12, oneof)) continue; /* Make sure there is no slope? */ bool noslope = (hs->building_flags & TILE_NOT_SLOPED) != 0; @@ -1936,6 +1931,11 @@ static bool BuildTownHouse(Town *t, TileIndex tile) /* 1x1 house checks are already done */ } + if (HasBit(hs->callback_mask, CBM_HOUSE_ALLOW_CONSTRUCTION)) { + uint16 callback_res = GetHouseCallback(CBID_HOUSE_ALLOW_CONSTRUCTION, 0, 0, house, t, tile); + if (callback_res != CALLBACK_FAILED && GB(callback_res, 0, 8) == 0) continue; + } + /* build the house */ t->num_houses++; IncreaseBuildingCount(t, house);