(svn r9319) [NoAI] -Add: added AITransactionMode, which allows you to record commands and execute it later on. noai
authortruelight
Mon, 19 Mar 2007 12:25:17 +0000
branchnoai
changeset 9474 2b53e154a8d5
parent 9473 dcbcd1c4496d
child 9475 58c20c0e394f
(svn r9319) [NoAI] -Add: added AITransactionMode, which allows you to record commands and execute it later on.
source.list
src/ai/ai_squirrel.cpp
src/ai/api/ai_transactionmode.cpp
src/ai/api/ai_transactionmode.hpp
--- a/source.list	Mon Mar 19 12:23:45 2007 +0000
+++ b/source.list	Mon Mar 19 12:25:17 2007 +0000
@@ -321,6 +321,7 @@
 ai/api/ai_settings.hpp
 ai/api/ai_town.hpp
 ai/api/ai_testmode.hpp
+ai/api/ai_transactionmode.hpp
 
 # AI API Implementation
 ai/api/ai_accounting.cpp
@@ -336,6 +337,7 @@
 ai/api/ai_settings.cpp
 ai/api/ai_town.cpp
 ai/api/ai_testmode.cpp
+ai/api/ai_transactionmode.cpp
 
 # NewGRF
 newgrf.cpp
--- a/src/ai/ai_squirrel.cpp	Mon Mar 19 12:23:45 2007 +0000
+++ b/src/ai/ai_squirrel.cpp	Mon Mar 19 12:25:17 2007 +0000
@@ -30,6 +30,7 @@
 #include "api/ai_settings.hpp"
 #include "api/ai_testmode.hpp"
 #include "api/ai_town.hpp"
+#include "api/ai_transactionmode.hpp"
 #undef DEFINE_SQUIRREL_CLASS
 
 static FSquirrel iFSquirrel; ///< Tell the AI-core that we have an AI with which we like to play.
@@ -192,6 +193,7 @@
 	SQAISettingsRegister(this->engine);
 	SQAITestModeRegister(this->engine);
 	SQAITownRegister(this->engine);
+	SQAITransactionModeRegister(this->engine);
 
 	this->engine->SetGlobalPointer(this->engine);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_transactionmode.cpp	Mon Mar 19 12:25:17 2007 +0000
@@ -0,0 +1,137 @@
+/* $Id$ */
+
+/** @file ai_transactionmode.cpp class to switch the AI to Transaction mode */
+
+#include "ai_transactionmode.hpp"
+#include "ai_execmode.hpp"
+#include "../../command.h"
+#include "../../debug.h"
+#include <stack>
+
+bool AITransactionMode::ModeProc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc, int32 costs)
+{
+	AITransactionMode *instance = (AITransactionMode *)AIObject::GetDoCommandModeInstance();
+	/* Don't record if we are stopped */
+	if (instance->stopped) return false;
+	AITransactionModeCommand command;
+
+	/* Store all params in a nice list, so we can execute them later */
+	command.tile  = tile;
+	command.p1    = p1;
+	command.p2    = p2;
+	command.flags = flags;
+	command.procc = procc;
+	command.text  = (_cmd_text == NULL) ? NULL : strdup(_cmd_text);
+	instance->command_stack.push(command);
+	instance->costs += costs;
+
+	/* Now we return 'false', as we don't want to execute the commands just yet */
+	return false;
+}
+
+AITransactionMode::AITransactionMode()
+{
+	this->costs         = 0;
+	this->stopped       = false;
+	this->last_mode     = this->GetDoCommandMode();
+	this->last_instance = this->GetDoCommandModeInstance();
+	this->SetDoCommandMode(&AITransactionMode::ModeProc, this);
+}
+
+AITransactionMode::~AITransactionMode()
+{
+	this->SetDoCommandMode(this->last_mode, this->last_instance);
+
+	/* Clean up the command_stack nicely (as in: free the cmd_text) */
+	AITransactionModeCommand command;
+	while (!this->command_stack.empty()) {
+		command = this->command_stack.front();
+		free(command.text);
+		this->command_stack.pop();
+	}
+
+	/* Clean up the reverse_stack too */
+	while (!this->reverse_stack.empty()) {
+		command = this->reverse_stack.front();
+		free(command.text);
+		this->reverse_stack.pop();
+	}
+}
+
+bool AITransactionMode::Execute()
+{
+	/* Make sure we are stopped */
+	this->Stop();
+
+	/* Switch to a forced execute mode */
+	AIExecMode exec;
+
+	/* Reverse stack should be empty */
+	assert(this->reverse_stack.empty());
+
+	AITransactionModeCommand command;
+	while (!this->command_stack.empty()) {
+		command = this->command_stack.front();
+		_cmd_text = command.text;
+		/* Make sure we return 'false' if the command fails to execute. This allows rollback of failed builds. */
+		if (!this->DoCommand(command.tile, command.p1, command.p2, command.flags, command.procc)) return false;
+		/* Add the command to the reverse stack */
+		this->reverse_stack.push(command);
+		/* Remove the item from the list */
+		this->command_stack.pop();
+	}
+
+	/* Everything went okay, clean the reverse stack */
+	while (!this->reverse_stack.empty()) {
+		command = this->reverse_stack.front();
+		free(command.text);
+		this->reverse_stack.pop();
+	}
+
+	/* Reverse stack should be empty again */
+	assert(this->reverse_stack.empty());
+
+	return true;
+}
+
+void AITransactionMode::Stop()
+{
+	this->stopped = true;
+}
+
+void AITransactionMode::Rollback()
+{
+	AITransactionModeCommand command;
+
+	DEBUG(ai, 0, "AITransactionMode::Rollback is not yet implemented");
+	return;
+
+	while (!this->reverse_stack.empty()) {
+		command = this->reverse_stack.front();
+		_cmd_text = command.text;
+
+		/* Find the reverse, and issue it */
+		/* TODO -- Make a table which reverses every command.procc */
+		this->DoCommand(command.tile, command.p1, command.p2, command.flags, command.procc);
+
+		/* Remove the item from the list */
+		this->reverse_stack.pop();
+	}
+}
+
+int32 AITransactionMode::GetCosts()
+{
+	return this->costs;
+}
+
+void AITransactionMode::Append(AITransactionMode *transaction)
+{
+	/* Can't append to myself! */
+	if (transaction == this) return;
+
+	/* Simply move the queue from transaction to ourself */
+	while (!transaction->command_stack.empty()) {
+		this->command_stack.push(transaction->command_stack.front());
+		transaction->command_stack.pop();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_transactionmode.hpp	Mon Mar 19 12:25:17 2007 +0000
@@ -0,0 +1,115 @@
+/* $Id$ */
+
+/** @file ai_testmode.hpp class to switch the AI to Transaction mode */
+
+#ifndef AI_TRANSACTIONMODE_HPP
+#define AI_TRANSACTIONMODE_HPP
+
+#include "ai_object.hpp"
+#include <queue>
+
+/**
+ * Class to switch current mode to Transaction mode.
+ * If you create an instance of this class, the mode will be switched to
+ *   Transaction. The original mode is stored and recovered from when ever the
+ *   instance is destroyed.
+ * In Transaction mode all commands will be tested, and if not fail, queued.
+ *   Later on you can execute a queue for real. The Transaction keeps on
+ *   recording all commands till it is destructed or execute or stopped.
+ * On execute the transaction can return false, because maps change over time.
+ *   If this happens you can use the rollback feature to remove all already
+ *   built things.
+ */
+class AITransactionMode : public AIObject {
+private:
+	struct AITransactionModeCommand {
+		TileIndex tile;
+		uint32 p1;
+		uint32 p2;
+		uint32 flags;
+		uint procc;
+		char *text;
+	};
+
+	AIModeProc *last_mode;
+	AIObject *last_instance;
+	std::queue<AITransactionModeCommand> command_stack;
+	std::queue<AITransactionModeCommand> reverse_stack;
+	bool stopped;
+	int32 costs;
+
+protected:
+	static bool ModeProc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc, int32 costs);
+
+public:
+	/**
+	 * Creating instance of this class switches the build mode to Transaction.
+	 * @note when the instance is destroyed, he restores the mode that was
+	 *   current when the instance was created!
+	 */
+	AITransactionMode();
+
+	/**
+	 * Destroying this instance reset the building mode to the mode it was
+	 *   in when the instance was created.
+	 */
+	~AITransactionMode();
+
+	/**
+	 * Execute all recorded commands.
+	 * @return false if any command recorded failed to execute. All other
+	 *   commands that follow won't be executed either.
+	 * @note when Execute() is called, the transaction is stopped (like calling
+	 *   Stop() yourself).
+	 * @note Execute() is always executed, no matter what mode you gave it in
+	 *   the outside world. It forces itself into executing it for real. To add
+	 *   it to an other list, use Append().
+	 */
+	bool Execute();
+
+	/**
+	 * Stop recording the commands and switch to the last mode, like the
+	 *   instance was destroyed.
+	 */
+	void Stop();
+
+	/**
+	 * Rollbacks rolls the whole transaction back in case Execute() returned
+	 *   false. In case Execute() returned true, there won't be anything to roll
+	 *   back, so this command will do nothing.
+	 * It rolls back all commands by looking up the reverse of every command
+	 *   issued and executes that. Of course it can happen that even that fails.
+	 *   In that case the problem will be silently ignored.
+	 * @note as you might want to get a costs estimate about the rollback first
+	 *   you need to make sure you set the right mode yourself!
+	 * @note this command isn't finished yet!!
+	 */
+	void Rollback();
+
+	/**
+	 * Get the costs it takes to execute this transaction (on average, real
+	 *   numbers can always differ).
+	 */
+	int32 GetCosts();
+
+	/**
+	 * Append one transaction list to an other.
+	 * @param transaction the list that will be appended after the instance you call append on.
+	 */
+	void Append(AITransactionMode *transaction);
+};
+
+#ifdef DEFINE_SQUIRREL_CLASS
+void SQAITransactionModeRegister(Squirrel *engine) {
+	DefSQClass <AITransactionMode> SQAITransactionMode("AITransactionMode");
+	SQAITransactionMode.PreRegister(engine);
+	SQAITransactionMode.AddConstructor(engine);
+	SQAITransactionMode.DefSQFunction(engine, &AITransactionMode::Execute,  "Execute");
+	SQAITransactionMode.DefSQFunction(engine, &AITransactionMode::Stop,     "Stop");
+	SQAITransactionMode.DefSQFunction(engine, &AITransactionMode::Rollback, "Rollback");
+//	SQAITransactionMode.DefSQFunction(engine, &AITransactionMode::Append,   "Append");
+	SQAITransactionMode.PostRegister(engine);
+}
+#endif /* SQUIRREL_CLASS */
+
+#endif /* AI_TRANSACTIONMODE_HPP */