Changeset - r21283:ba592c088a09
[Not reviewed]
master
0 6 0
frosch - 10 years ago 2014-02-23 22:03:08
frosch@openttd.org
(svn r26371) -Fix [FS#5831]: Calling DoCommandP during the gameloop cleared pending persistent storage changes.
6 files changed with 105 insertions and 50 deletions:
0 comments (0 inline, 0 general)
src/command.cpp
Show inline comments
 
@@ -601,7 +601,7 @@ bool DoCommandP(TileIndex tile, uint32 p
 
 * @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
 
#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
 
	 * 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
 
	/* 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
 
			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
 
		 * 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
 
	/* 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
 
	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
 
		 * 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
 
	/* update signals if needed */
 
	UpdateSignalsInBuffer();
 

	
 
	return_dcpi(res2, true);
 
	return_dcpi(res2);
 
}
 
#undef return_dcpi
 

	
src/genworld.cpp
Show inline comments
 
@@ -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();
src/newgrf_storage.cpp
Show inline comments
 
@@ -22,6 +22,10 @@ INSTANTIATE_POOL_METHODS(PersistentStora
 
/** 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(BasePer
 
}
 

	
 
/**
 
 * 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();
 
}
src/newgrf_storage.h
Show inline comments
 
@@ -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:
 
	/**
 
	 * 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 : BasePers
 
		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 : BasePers
 
		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;
 

	
src/openttd.cpp
Show inline comments
 
@@ -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();
src/saveload/storage_sl.cpp
Show inline comments
 
@@ -39,6 +39,7 @@ static void Save_PSAC()
 

	
 
	/* Write the industries */
 
	FOR_ALL_STORAGES(ps) {
 
		ps->ClearChanges();
 
		SlSetArrayIndex(ps->index);
 
		SlObject(ps, _storage_desc);
 
	}
0 comments (0 inline, 0 general)