mirror of https://github.com/OpenTTD/OpenTTD.git
(svn r26371) -Fix [FS#5831]: Calling DoCommandP during the gameloop cleared pending persistent storage changes.
This commit is contained in:
parent
c6ce57e8a7
commit
a32d18cbb9
|
@ -601,7 +601,7 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallbac
|
||||||
* @param cmd the command cost to return.
|
* @param cmd the command cost to return.
|
||||||
* @param clear whether to keep the storage changes or not.
|
* @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.
|
* 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
|
#endif
|
||||||
|
|
||||||
/* Do not even think about executing out-of-bounds tile-commands */
|
/* 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 */
|
/* Always execute server and spectator commands as spectator */
|
||||||
bool exec_as_spectator = (cmd_flags & (CMD_SPECTATOR | CMD_SERVER)) != 0;
|
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
|
* 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. */
|
* 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)) {
|
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);
|
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. */
|
/* Test the command. */
|
||||||
_cleared_object_areas.Clear();
|
_cleared_object_areas.Clear();
|
||||||
SetTownRatingTestMode(true);
|
SetTownRatingTestMode(true);
|
||||||
ClearPersistentStorageChanges(false);
|
BasePersistentStorageArray::SwitchMode(PSM_ENTER_TESTMODE);
|
||||||
CommandCost res = proc(tile, flags, p1, p2, text);
|
CommandCost res = proc(tile, flags, p1, p2, text);
|
||||||
|
BasePersistentStorageArray::SwitchMode(PSM_LEAVE_TESTMODE);
|
||||||
SetTownRatingTestMode(false);
|
SetTownRatingTestMode(false);
|
||||||
|
|
||||||
/* Make sure we're not messing things up here. */
|
/* 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));
|
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();
|
cur_company.Restore();
|
||||||
return_dcpi(res, false);
|
return_dcpi(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_NETWORK
|
#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
|
* This way it's not handled by DoCommand and only the
|
||||||
* actual execution of the command causes messages. Also
|
* actual execution of the command causes messages. Also
|
||||||
* reset the storages as we've not executed the command. */
|
* reset the storages as we've not executed the command. */
|
||||||
return_dcpi(CommandCost(), false);
|
return_dcpi(CommandCost());
|
||||||
}
|
}
|
||||||
#endif /* ENABLE_NETWORK */
|
#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));
|
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
|
/* Actually try and execute the command. If no cost-type is given
|
||||||
* use the construction one */
|
* use the construction one */
|
||||||
_cleared_object_areas.Clear();
|
_cleared_object_areas.Clear();
|
||||||
ClearPersistentStorageChanges(false);
|
BasePersistentStorageArray::SwitchMode(PSM_ENTER_COMMAND);
|
||||||
CommandCost res2 = proc(tile, flags | DC_EXEC, p1, p2, text);
|
CommandCost res2 = proc(tile, flags | DC_EXEC, p1, p2, text);
|
||||||
|
BasePersistentStorageArray::SwitchMode(PSM_LEAVE_COMMAND);
|
||||||
|
|
||||||
if (cmd_id == CMD_COMPANY_CTRL) {
|
if (cmd_id == CMD_COMPANY_CTRL) {
|
||||||
cur_company.Trash();
|
cur_company.Trash();
|
||||||
|
@ -731,7 +733,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd,
|
||||||
if (!test_and_exec_can_differ) {
|
if (!test_and_exec_can_differ) {
|
||||||
assert(res.GetCost() == res2.GetCost() && res.Failed() == res2.Failed()); // sanity check
|
assert(res.GetCost() == res2.GetCost() && res.Failed() == res2.Failed()); // sanity check
|
||||||
} else if (res2.Failed()) {
|
} else if (res2.Failed()) {
|
||||||
return_dcpi(res2, false);
|
return_dcpi(res2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we're needing more money and we haven't done
|
/* 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 */
|
* So make sure the signal buffer is empty even in this case */
|
||||||
UpdateSignalsInBuffer();
|
UpdateSignalsInBuffer();
|
||||||
SetDParam(0, _additional_cash_required);
|
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. */
|
/* update last build coordinate of company. */
|
||||||
|
@ -755,7 +757,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd,
|
||||||
/* update signals if needed */
|
/* update signals if needed */
|
||||||
UpdateSignalsInBuffer();
|
UpdateSignalsInBuffer();
|
||||||
|
|
||||||
return_dcpi(res2, true);
|
return_dcpi(res2);
|
||||||
}
|
}
|
||||||
#undef return_dcpi
|
#undef return_dcpi
|
||||||
|
|
||||||
|
|
|
@ -105,6 +105,8 @@ static void _GenerateWorld(void *)
|
||||||
SetGeneratingWorldProgress(GWP_MAP_INIT, 2);
|
SetGeneratingWorldProgress(GWP_MAP_INIT, 2);
|
||||||
SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0);
|
SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0);
|
||||||
|
|
||||||
|
BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP);
|
||||||
|
|
||||||
IncreaseGeneratingWorldProgress(GWP_MAP_INIT);
|
IncreaseGeneratingWorldProgress(GWP_MAP_INIT);
|
||||||
/* Must start economy early because of the costs. */
|
/* Must start economy early because of the costs. */
|
||||||
StartupEconomy();
|
StartupEconomy();
|
||||||
|
@ -141,8 +143,6 @@ static void _GenerateWorld(void *)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearPersistentStorageChanges(true);
|
|
||||||
|
|
||||||
/* These are probably pointless when inside the scenario editor. */
|
/* These are probably pointless when inside the scenario editor. */
|
||||||
SetGeneratingWorldProgress(GWP_GAME_INIT, 3);
|
SetGeneratingWorldProgress(GWP_GAME_INIT, 3);
|
||||||
StartupCompanies();
|
StartupCompanies();
|
||||||
|
@ -179,6 +179,8 @@ static void _GenerateWorld(void *)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP);
|
||||||
|
|
||||||
ResetObjectToPlace();
|
ResetObjectToPlace();
|
||||||
_cur_company.Trash();
|
_cur_company.Trash();
|
||||||
_current_company = _local_company = _gw.lc;
|
_current_company = _local_company = _gw.lc;
|
||||||
|
@ -202,6 +204,7 @@ static void _GenerateWorld(void *)
|
||||||
SaveOrLoad(name, SL_SAVE, AUTOSAVE_DIR, false);
|
SaveOrLoad(name, SL_SAVE, AUTOSAVE_DIR, false);
|
||||||
}
|
}
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP, true);
|
||||||
if (_cur_company.IsValid()) _cur_company.Restore();
|
if (_cur_company.IsValid()) _cur_company.Restore();
|
||||||
_generating_world = false;
|
_generating_world = false;
|
||||||
_modal_progress_work_mutex->EndCritical();
|
_modal_progress_work_mutex->EndCritical();
|
||||||
|
|
|
@ -22,6 +22,10 @@ INSTANTIATE_POOL_METHODS(PersistentStorage)
|
||||||
/** The changed storage arrays */
|
/** The changed storage arrays */
|
||||||
static std::set<BasePersistentStorageArray*> *_changed_storage_arrays = new std::set<BasePersistentStorageArray*>;
|
static std::set<BasePersistentStorageArray*> *_changed_storage_arrays = new std::set<BasePersistentStorageArray*>;
|
||||||
|
|
||||||
|
bool BasePersistentStorageArray::gameloop;
|
||||||
|
bool BasePersistentStorageArray::command;
|
||||||
|
bool BasePersistentStorageArray::testmode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove references to use.
|
* Remove references to use.
|
||||||
*/
|
*/
|
||||||
|
@ -42,25 +46,54 @@ void AddChangedPersistentStorage(BasePersistentStorageArray *storage)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the changes made since the last #ClearStorageChanges.
|
* Clear temporary changes made since the last call to SwitchMode, and
|
||||||
* This is done for *all* storages that have been registered to with
|
* set whether subsequent changes shall be persistent or temporary.
|
||||||
* #AddChangedStorage since the previous #ClearStorageChanges.
|
|
||||||
*
|
*
|
||||||
* This can be done in two ways:
|
* @param mode Mode switch affecting temporary/persistent changes.
|
||||||
* - saving the changes permanently
|
* @param ignore_prev_mode Disable some sanity checks for exceptional call circumstances.
|
||||||
* - reverting to the previous version
|
|
||||||
* @param keep_changes do we save or revert the changes since the last #ClearChanges?
|
|
||||||
*/
|
*/
|
||||||
void ClearPersistentStorageChanges(bool keep_changes)
|
/* static */ void BasePersistentStorageArray::SwitchMode(PersistentStorageMode mode, bool ignore_prev_mode)
|
||||||
{
|
{
|
||||||
/* Loop over all changes arrays */
|
switch (mode) {
|
||||||
for (std::set<BasePersistentStorageArray*>::iterator it = _changed_storage_arrays->begin(); it != _changed_storage_arrays->end(); it++) {
|
case PSM_ENTER_GAMELOOP:
|
||||||
if (!keep_changes) {
|
assert(ignore_prev_mode || !gameloop);
|
||||||
DEBUG(desync, 1, "Discarding persistent storage changes: Feature %d, GrfID %08X, Tile %d", (*it)->feature, BSWAP32((*it)->grfid), (*it)->tile);
|
assert(!command && !testmode);
|
||||||
}
|
gameloop = true;
|
||||||
(*it)->ClearChanges(keep_changes);
|
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();
|
_changed_storage_arrays->clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,18 @@
|
||||||
#include "core/pool_type.hpp"
|
#include "core/pool_type.hpp"
|
||||||
#include "tile_type.h"
|
#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
|
* Base class for all persistent NewGRF storage arrays. Nothing fancy, only here
|
||||||
* so we have a generalised access to the virtual methods.
|
* so we have a generalised access to the virtual methods.
|
||||||
|
@ -26,14 +38,24 @@ struct BasePersistentStorageArray {
|
||||||
|
|
||||||
virtual ~BasePersistentStorageArray();
|
virtual ~BasePersistentStorageArray();
|
||||||
|
|
||||||
|
static void SwitchMode(PersistentStorageMode mode, bool ignore_prev_mode = false);
|
||||||
|
|
||||||
|
protected:
|
||||||
/**
|
/**
|
||||||
* Clear the changes made since the last #ClearChanges.
|
* Discard temporary changes.
|
||||||
* 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?
|
|
||||||
*/
|
*/
|
||||||
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;
|
if (this->storage[pos] == value) return;
|
||||||
|
|
||||||
/* We do not have made a backup; lets do so */
|
/* 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);
|
this->prev_storage = MallocT<TYPE>(SIZE);
|
||||||
memcpy(this->prev_storage, this->storage, sizeof(this->storage));
|
memcpy(this->prev_storage, this->storage, sizeof(this->storage));
|
||||||
|
|
||||||
|
@ -107,19 +131,13 @@ struct PersistentStorageArray : BasePersistentStorageArray {
|
||||||
return this->storage[pos];
|
return this->storage[pos];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void ClearChanges()
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
assert(this->prev_storage != NULL);
|
if (this->prev_storage != NULL) {
|
||||||
|
|
||||||
if (!keep_changes) {
|
|
||||||
memcpy(this->storage, this->prev_storage, sizeof(this->storage));
|
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 AddChangedPersistentStorage(BasePersistentStorageArray *storage);
|
||||||
void ClearPersistentStorageChanges(bool keep_changes);
|
|
||||||
|
|
||||||
|
|
||||||
typedef PersistentStorageArray<int32, 16> OldPersistentStorage;
|
typedef PersistentStorageArray<int32, 16> OldPersistentStorage;
|
||||||
|
|
||||||
|
|
|
@ -1355,15 +1355,14 @@ void StateGameLoop()
|
||||||
}
|
}
|
||||||
if (HasModalProgress()) return;
|
if (HasModalProgress()) return;
|
||||||
|
|
||||||
ClearPersistentStorageChanges(false);
|
|
||||||
|
|
||||||
Layouter::ReduceLineCache();
|
Layouter::ReduceLineCache();
|
||||||
|
|
||||||
if (_game_mode == GM_EDITOR) {
|
if (_game_mode == GM_EDITOR) {
|
||||||
|
BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP);
|
||||||
RunTileLoop();
|
RunTileLoop();
|
||||||
CallVehicleTicks();
|
CallVehicleTicks();
|
||||||
CallLandscapeTick();
|
CallLandscapeTick();
|
||||||
ClearPersistentStorageChanges(true);
|
BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP);
|
||||||
UpdateLandscapingLimits();
|
UpdateLandscapingLimits();
|
||||||
|
|
||||||
CallWindowTickEvent();
|
CallWindowTickEvent();
|
||||||
|
@ -1382,12 +1381,13 @@ void StateGameLoop()
|
||||||
* for multiplayer compatibility */
|
* for multiplayer compatibility */
|
||||||
Backup<CompanyByte> cur_company(_current_company, OWNER_NONE, FILE_LINE);
|
Backup<CompanyByte> cur_company(_current_company, OWNER_NONE, FILE_LINE);
|
||||||
|
|
||||||
|
BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP);
|
||||||
AnimateAnimatedTiles();
|
AnimateAnimatedTiles();
|
||||||
IncreaseDate();
|
IncreaseDate();
|
||||||
RunTileLoop();
|
RunTileLoop();
|
||||||
CallVehicleTicks();
|
CallVehicleTicks();
|
||||||
CallLandscapeTick();
|
CallLandscapeTick();
|
||||||
ClearPersistentStorageChanges(true);
|
BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP);
|
||||||
|
|
||||||
#ifndef DEBUG_DUMP_COMMANDS
|
#ifndef DEBUG_DUMP_COMMANDS
|
||||||
AI::GameLoop();
|
AI::GameLoop();
|
||||||
|
|
|
@ -39,6 +39,7 @@ static void Save_PSAC()
|
||||||
|
|
||||||
/* Write the industries */
|
/* Write the industries */
|
||||||
FOR_ALL_STORAGES(ps) {
|
FOR_ALL_STORAGES(ps) {
|
||||||
|
ps->ClearChanges();
|
||||||
SlSetArrayIndex(ps->index);
|
SlSetArrayIndex(ps->index);
|
||||||
SlObject(ps, _storage_desc);
|
SlObject(ps, _storage_desc);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue