diff --git a/src/command.cpp b/src/command.cpp index 7afd821859..2891a06af6 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -601,7 +601,7 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallbac * @param cmd the command cost to return. * @param clear whether to keep the storage changes or not. */ -#define return_dcpi(cmd, clear) { _docommand_recursive = 0; ClearPersistentStorageChanges(clear); return cmd; } +#define return_dcpi(cmd) { _docommand_recursive = 0; return cmd; } /*! * Helper function for the toplevel network safe docommand function for the current company. @@ -645,7 +645,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, #endif /* Do not even think about executing out-of-bounds tile-commands */ - if (tile != 0 && (tile >= MapSize() || (!IsValidTile(tile) && (cmd_flags & CMD_ALL_TILES) == 0))) return_dcpi(CMD_ERROR, false); + if (tile != 0 && (tile >= MapSize() || (!IsValidTile(tile) && (cmd_flags & CMD_ALL_TILES) == 0))) return_dcpi(CMD_ERROR); /* Always execute server and spectator commands as spectator */ bool exec_as_spectator = (cmd_flags & (CMD_SPECTATOR | CMD_SERVER)) != 0; @@ -654,7 +654,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, * The server will ditch any server commands a client sends to it, so effectively * this guards the server from executing functions for an invalid company. */ if (_game_mode == GM_NORMAL && !exec_as_spectator && !Company::IsValidID(_current_company) && !(_current_company == OWNER_DEITY && (cmd_flags & CMD_DEITY) != 0)) { - return_dcpi(CMD_ERROR, false); + return_dcpi(CMD_ERROR); } Backup cur_company(_current_company, FILE_LINE); @@ -665,8 +665,9 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, /* Test the command. */ _cleared_object_areas.Clear(); SetTownRatingTestMode(true); - ClearPersistentStorageChanges(false); + BasePersistentStorageArray::SwitchMode(PSM_ENTER_TESTMODE); CommandCost res = proc(tile, flags, p1, p2, text); + BasePersistentStorageArray::SwitchMode(PSM_LEAVE_TESTMODE); SetTownRatingTestMode(false); /* Make sure we're not messing things up here. */ @@ -685,7 +686,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, DEBUG(desync, 1, "cmdf: %08x; %02x; %02x; %06x; %08x; %08x; %08x; \"%s\" (%s)", _date, _date_fract, (int)_current_company, tile, p1, p2, cmd & ~CMD_NETWORK_COMMAND, text, GetCommandName(cmd)); } cur_company.Restore(); - return_dcpi(res, false); + return_dcpi(res); } #ifdef ENABLE_NETWORK @@ -701,7 +702,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, * This way it's not handled by DoCommand and only the * actual execution of the command causes messages. Also * reset the storages as we've not executed the command. */ - return_dcpi(CommandCost(), false); + return_dcpi(CommandCost()); } #endif /* ENABLE_NETWORK */ DEBUG(desync, 1, "cmd: %08x; %02x; %02x; %06x; %08x; %08x; %08x; \"%s\" (%s)", _date, _date_fract, (int)_current_company, tile, p1, p2, cmd & ~CMD_NETWORK_COMMAND, text, GetCommandName(cmd)); @@ -709,8 +710,9 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, /* Actually try and execute the command. If no cost-type is given * use the construction one */ _cleared_object_areas.Clear(); - ClearPersistentStorageChanges(false); + BasePersistentStorageArray::SwitchMode(PSM_ENTER_COMMAND); CommandCost res2 = proc(tile, flags | DC_EXEC, p1, p2, text); + BasePersistentStorageArray::SwitchMode(PSM_LEAVE_COMMAND); if (cmd_id == CMD_COMPANY_CTRL) { cur_company.Trash(); @@ -731,7 +733,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, if (!test_and_exec_can_differ) { assert(res.GetCost() == res2.GetCost() && res.Failed() == res2.Failed()); // sanity check } else if (res2.Failed()) { - return_dcpi(res2, false); + return_dcpi(res2); } /* If we're needing more money and we haven't done @@ -741,7 +743,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, * So make sure the signal buffer is empty even in this case */ UpdateSignalsInBuffer(); SetDParam(0, _additional_cash_required); - return_dcpi(CommandCost(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY), false); + return_dcpi(CommandCost(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY)); } /* update last build coordinate of company. */ @@ -755,7 +757,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, /* update signals if needed */ UpdateSignalsInBuffer(); - return_dcpi(res2, true); + return_dcpi(res2); } #undef return_dcpi diff --git a/src/genworld.cpp b/src/genworld.cpp index cb03db0622..822fe141fa 100644 --- a/src/genworld.cpp +++ b/src/genworld.cpp @@ -105,6 +105,8 @@ static void _GenerateWorld(void *) SetGeneratingWorldProgress(GWP_MAP_INIT, 2); SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0); + BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP); + IncreaseGeneratingWorldProgress(GWP_MAP_INIT); /* Must start economy early because of the costs. */ StartupEconomy(); @@ -141,8 +143,6 @@ static void _GenerateWorld(void *) } } - ClearPersistentStorageChanges(true); - /* These are probably pointless when inside the scenario editor. */ SetGeneratingWorldProgress(GWP_GAME_INIT, 3); StartupCompanies(); @@ -179,6 +179,8 @@ static void _GenerateWorld(void *) } } + BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP); + ResetObjectToPlace(); _cur_company.Trash(); _current_company = _local_company = _gw.lc; @@ -202,6 +204,7 @@ static void _GenerateWorld(void *) SaveOrLoad(name, SL_SAVE, AUTOSAVE_DIR, false); } } catch (...) { + BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP, true); if (_cur_company.IsValid()) _cur_company.Restore(); _generating_world = false; _modal_progress_work_mutex->EndCritical(); diff --git a/src/newgrf_storage.cpp b/src/newgrf_storage.cpp index 9fd0885c8c..7e8b9d8d07 100644 --- a/src/newgrf_storage.cpp +++ b/src/newgrf_storage.cpp @@ -22,6 +22,10 @@ INSTANTIATE_POOL_METHODS(PersistentStorage) /** The changed storage arrays */ static std::set *_changed_storage_arrays = new std::set; +bool BasePersistentStorageArray::gameloop; +bool BasePersistentStorageArray::command; +bool BasePersistentStorageArray::testmode; + /** * Remove references to use. */ @@ -42,25 +46,54 @@ void AddChangedPersistentStorage(BasePersistentStorageArray *storage) } /** - * Clear the changes made since the last #ClearStorageChanges. - * This is done for *all* storages that have been registered to with - * #AddChangedStorage since the previous #ClearStorageChanges. + * Clear temporary changes made since the last call to SwitchMode, and + * set whether subsequent changes shall be persistent or temporary. * - * This can be done in two ways: - * - saving the changes permanently - * - reverting to the previous version - * @param keep_changes do we save or revert the changes since the last #ClearChanges? + * @param mode Mode switch affecting temporary/persistent changes. + * @param ignore_prev_mode Disable some sanity checks for exceptional call circumstances. */ -void ClearPersistentStorageChanges(bool keep_changes) +/* static */ void BasePersistentStorageArray::SwitchMode(PersistentStorageMode mode, bool ignore_prev_mode) { - /* Loop over all changes arrays */ - for (std::set::iterator it = _changed_storage_arrays->begin(); it != _changed_storage_arrays->end(); it++) { - if (!keep_changes) { - DEBUG(desync, 1, "Discarding persistent storage changes: Feature %d, GrfID %08X, Tile %d", (*it)->feature, BSWAP32((*it)->grfid), (*it)->tile); - } - (*it)->ClearChanges(keep_changes); + switch (mode) { + case PSM_ENTER_GAMELOOP: + assert(ignore_prev_mode || !gameloop); + assert(!command && !testmode); + gameloop = true; + break; + + case PSM_LEAVE_GAMELOOP: + assert(ignore_prev_mode || gameloop); + assert(!command && !testmode); + gameloop = false; + break; + + case PSM_ENTER_COMMAND: + assert((ignore_prev_mode || !command) && !testmode); + command = true; + break; + + case PSM_LEAVE_COMMAND: + assert(ignore_prev_mode || command); + command = false; + break; + + case PSM_ENTER_TESTMODE: + assert(!command && (ignore_prev_mode || !testmode)); + testmode = true; + break; + + case PSM_LEAVE_TESTMODE: + assert(ignore_prev_mode || testmode); + testmode = false; + break; + + default: NOT_REACHED(); } - /* And then clear that array */ + /* Discard all temporary changes */ + for (std::set::iterator it = _changed_storage_arrays->begin(); it != _changed_storage_arrays->end(); it++) { + DEBUG(desync, 1, "Discarding persistent storage changes: Feature %d, GrfID %08X, Tile %d", (*it)->feature, BSWAP32((*it)->grfid), (*it)->tile); + (*it)->ClearChanges(); + } _changed_storage_arrays->clear(); } diff --git a/src/newgrf_storage.h b/src/newgrf_storage.h index 7dccb053c8..ae9782d88c 100644 --- a/src/newgrf_storage.h +++ b/src/newgrf_storage.h @@ -15,6 +15,18 @@ #include "core/pool_type.hpp" #include "tile_type.h" +/** + * Mode switches to the behaviour of persistent storage array. + */ +enum PersistentStorageMode { + PSM_ENTER_GAMELOOP, ///< Enter the gameloop, changes will be permanent. + PSM_LEAVE_GAMELOOP, ///< Leave the gameloop, changes will be temporary. + PSM_ENTER_COMMAND, ///< Enter command scope, changes will be permanent. + PSM_LEAVE_COMMAND, ///< Leave command scope, revert to previous mode. + PSM_ENTER_TESTMODE, ///< Enter command test mode, changes will be tempoary. + PSM_LEAVE_TESTMODE, ///< Leave command test mode, revert to previous mode. +}; + /** * Base class for all persistent NewGRF storage arrays. Nothing fancy, only here * so we have a generalised access to the virtual methods. @@ -26,14 +38,24 @@ struct BasePersistentStorageArray { virtual ~BasePersistentStorageArray(); + static void SwitchMode(PersistentStorageMode mode, bool ignore_prev_mode = false); + +protected: /** - * Clear the changes made since the last #ClearChanges. - * This can be done in two ways: - * - saving the changes permanently - * - reverting to the previous version - * @param keep_changes do we save or revert the changes since the last #ClearChanges? + * Discard temporary changes. */ - virtual void ClearChanges(bool keep_changes) = 0; + virtual void ClearChanges() = 0; + + /** + * Check whether currently changes to the storage shall be persistent or + * temporary till the next call to ClearChanges(). + */ + static bool AreChangesPersistent() { return (gameloop || command) && !testmode; } + +private: + static bool gameloop; + static bool command; + static bool testmode; }; /** @@ -82,7 +104,9 @@ struct PersistentStorageArray : BasePersistentStorageArray { if (this->storage[pos] == value) return; /* We do not have made a backup; lets do so */ - if (this->prev_storage == NULL) { + if (AreChangesPersistent()) { + assert(this->prev_storage == NULL); + } else if (this->prev_storage == NULL) { this->prev_storage = MallocT(SIZE); memcpy(this->prev_storage, this->storage, sizeof(this->storage)); @@ -107,19 +131,13 @@ struct PersistentStorageArray : BasePersistentStorageArray { return this->storage[pos]; } - /** - * Clear the changes, or assign them permanently to the storage. - * @param keep_changes Whether to assign or ditch the changes. - */ - void ClearChanges(bool keep_changes) + void ClearChanges() { - assert(this->prev_storage != NULL); - - if (!keep_changes) { + if (this->prev_storage != NULL) { memcpy(this->storage, this->prev_storage, sizeof(this->storage)); + free(this->prev_storage); + this->prev_storage = NULL; } - free(this->prev_storage); - this->prev_storage = NULL; } }; @@ -189,8 +207,6 @@ struct TemporaryStorageArray { }; void AddChangedPersistentStorage(BasePersistentStorageArray *storage); -void ClearPersistentStorageChanges(bool keep_changes); - typedef PersistentStorageArray OldPersistentStorage; diff --git a/src/openttd.cpp b/src/openttd.cpp index c0afb4a83d..c901272c57 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -1355,15 +1355,14 @@ void StateGameLoop() } if (HasModalProgress()) return; - ClearPersistentStorageChanges(false); - Layouter::ReduceLineCache(); if (_game_mode == GM_EDITOR) { + BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP); RunTileLoop(); CallVehicleTicks(); CallLandscapeTick(); - ClearPersistentStorageChanges(true); + BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP); UpdateLandscapingLimits(); CallWindowTickEvent(); @@ -1382,12 +1381,13 @@ void StateGameLoop() * for multiplayer compatibility */ Backup cur_company(_current_company, OWNER_NONE, FILE_LINE); + BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP); AnimateAnimatedTiles(); IncreaseDate(); RunTileLoop(); CallVehicleTicks(); CallLandscapeTick(); - ClearPersistentStorageChanges(true); + BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP); #ifndef DEBUG_DUMP_COMMANDS AI::GameLoop(); diff --git a/src/saveload/storage_sl.cpp b/src/saveload/storage_sl.cpp index 9fb1c86721..d35fa7cc94 100644 --- a/src/saveload/storage_sl.cpp +++ b/src/saveload/storage_sl.cpp @@ -39,6 +39,7 @@ static void Save_PSAC() /* Write the industries */ FOR_ALL_STORAGES(ps) { + ps->ClearChanges(); SlSetArrayIndex(ps->index); SlObject(ps, _storage_desc); }