(svn r9319) [NoAI] -Add: added AITransactionMode, which allows you to record commands and execute it later on.
--- 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 */