summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorfrosch <frosch@openttd.org>2014-02-23 22:03:08 +0000
committerfrosch <frosch@openttd.org>2014-02-23 22:03:08 +0000
commita32d18cbb939b240bc16d909fce71ba72c24f040 (patch)
tree1cb4fa063b33b684a3a94fe60ade548dd7703624 /src
parentc6ce57e8a7529b970ef45eb7baa00560d6f4b16b (diff)
downloadopenttd-a32d18cbb939b240bc16d909fce71ba72c24f040.tar.xz
(svn r26371) -Fix [FS#5831]: Calling DoCommandP during the gameloop cleared pending persistent storage changes.
Diffstat (limited to 'src')
-rw-r--r--src/command.cpp22
-rw-r--r--src/genworld.cpp7
-rw-r--r--src/newgrf_storage.cpp63
-rw-r--r--src/newgrf_storage.h54
-rw-r--r--src/openttd.cpp8
-rw-r--r--src/saveload/storage_sl.cpp1
6 files changed, 105 insertions, 50 deletions
diff --git a/src/command.cpp b/src/command.cpp
index 7afd82185..2891a06af 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<CompanyByte> 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 cb03db062..822fe141f 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 9fd0885c8..7e8b9d8d0 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<BasePersistentStorageArray*> *_changed_storage_arrays = new std::set<BasePersistentStorageArray*>;
+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<BasePersistentStorageArray*>::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<BasePersistentStorageArray*>::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 7dccb053c..ae9782d88 100644
--- a/src/newgrf_storage.h
+++ b/src/newgrf_storage.h
@@ -16,6 +16,18 @@
#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:
+ /**
+ * Discard temporary changes.
+ */
+ virtual void ClearChanges() = 0;
+
/**
- * 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?
+ * Check whether currently changes to the storage shall be persistent or
+ * temporary till the next call to ClearChanges().
*/
- virtual void ClearChanges(bool keep_changes) = 0;
+ 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<TYPE>(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<int32, 16> OldPersistentStorage;
diff --git a/src/openttd.cpp b/src/openttd.cpp
index c0afb4a83..c901272c5 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<CompanyByte> 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 9fb1c8672..d35fa7cc9 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);
}