diff --git a/src/ai/ai_core.cpp b/src/ai/ai_core.cpp --- a/src/ai/ai_core.cpp +++ b/src/ai/ai_core.cpp @@ -52,7 +52,8 @@ c->ai_info = info; assert(c->ai_instance == NULL); - c->ai_instance = new AIInstance(info); + c->ai_instance = new AIInstance(); + c->ai_instance->Initialize(info); cur_company.Restore(); @@ -214,7 +215,7 @@ /* Queue the event */ Backup cur_company(_current_company, company, FILE_LINE); - AIEventController::InsertEvent(event); + Company::Get(_current_company)->ai_instance->InsertEvent(event); cur_company.Restore(); event->Release(); @@ -248,15 +249,7 @@ */ void CcAI(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2) { - AIObject::SetLastCommandRes(result.Succeeded()); - - if (result.Failed()) { - AIObject::SetLastError(AIError::StringToError(result.GetErrorMessage())); - } else { - AIObject::IncreaseDoCommandCosts(result.GetCost()); - AIObject::SetLastCost(result.GetCost()); - } - + Company::Get(_current_company)->ai_instance->DoCommandCallback(result, tile, p1, p2); Company::Get(_current_company)->ai_instance->Continue(); } @@ -327,7 +320,7 @@ void CcAI(const CommandCost &result, Til /* static */ bool AI::ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm) { - return AI::ai_scanner->ImportLibrary(library, class_name, version, vm, Company::Get(_current_company)->ai_instance->GetController()); + return AI::ai_scanner->ImportLibrary(library, class_name, version, vm, AIObject::GetActiveInstance()->GetController()); } /* static */ void AI::Rescan() diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp --- a/src/ai/ai_gui.cpp +++ b/src/ai/ai_gui.cpp @@ -931,9 +931,7 @@ struct AIDebugWindow : public QueryStrin /* If there are no active companies, don't display anything else. */ if (ai_debug_company == INVALID_COMPANY) return; - Backup cur_company(_current_company, ai_debug_company, FILE_LINE); - AILog::LogData *log = (AILog::LogData *)AIObject::GetLogPointer(); - cur_company.Restore(); + AILog::LogData *log = (AILog::LogData *)Company::Get(ai_debug_company)->ai_instance->GetLogPointer(); int scroll_count = (log == NULL) ? 0 : log->used; if (this->vscroll->GetCount() != scroll_count) { @@ -986,9 +984,7 @@ struct AIDebugWindow : public QueryStrin switch (widget) { case AID_WIDGET_LOG_PANEL: { - Backup cur_company(_current_company, ai_debug_company, FILE_LINE); - AILog::LogData *log = (AILog::LogData *)AIObject::GetLogPointer(); - cur_company.Restore(); + AILog::LogData *log = (AILog::LogData *)Company::Get(ai_debug_company)->ai_instance->GetLogPointer(); if (log == NULL) return; int y = this->top_offset; @@ -1029,9 +1025,7 @@ struct AIDebugWindow : public QueryStrin this->RaiseWidget(ai_debug_company + AID_WIDGET_COMPANY_BUTTON_START); ai_debug_company = show_ai; - Backup cur_company(_current_company, ai_debug_company, FILE_LINE); - AILog::LogData *log = (AILog::LogData *)AIObject::GetLogPointer(); - cur_company.Restore(); + AILog::LogData *log = (AILog::LogData *)Company::Get(ai_debug_company)->ai_instance->GetLogPointer(); this->vscroll->SetCount((log == NULL) ? 0 : log->used); this->LowerWidget(ai_debug_company + AID_WIDGET_COMPANY_BUTTON_START); @@ -1130,8 +1124,7 @@ struct AIDebugWindow : public QueryStrin * This needs to be done in gameloop-scope, so the AI is suspended immediately. */ if (!gui_scope && data == ai_debug_company && this->break_check_enabled && !StrEmpty(this->edit_str_buf)) { /* Get the log instance of the active company */ - Backup cur_company(_current_company, ai_debug_company, FILE_LINE); - AILog::LogData *log = (AILog::LogData *)AIObject::GetLogPointer(); + AILog::LogData *log = (AILog::LogData *)Company::Get(ai_debug_company)->ai_instance->GetLogPointer(); if (log != NULL && case_sensitive_break_check? strstr(log->lines[log->pos], this->edit_str_buf) != 0 : @@ -1149,8 +1142,6 @@ struct AIDebugWindow : public QueryStrin /* Highlight row that matched */ this->highlight_row = log->pos; } - - cur_company.Restore(); } } diff --git a/src/ai/ai_instance.cpp b/src/ai/ai_instance.cpp --- a/src/ai/ai_instance.cpp +++ b/src/ai/ai_instance.cpp @@ -100,7 +100,7 @@ static void PrintFunc(bool error_msg, co AIController::Print(error_msg, SQ2OTTD(message)); } -AIInstance::AIInstance(AIInfo *info) : +AIInstance::AIInstance() : controller(NULL), storage(NULL), engine(NULL), @@ -111,13 +111,16 @@ AIInstance::AIInstance(AIInfo *info) : suspend(0), callback(NULL) { - /* Set the instance already, so we can use AIObject::Set commands */ - Company::Get(_current_company)->ai_instance = this; + this->storage = new AIStorage(); + this->engine = new Squirrel(); + this->engine->SetPrintFunction(&PrintFunc); +} + +void AIInstance::Initialize(AIInfo *info) +{ + AIObject::ActiveInstance active(this); this->controller = new AIController(); - this->storage = new AIStorage(); - this->engine = new Squirrel(); - this->engine->SetPrintFunction(&PrintFunc); /* The import method is available at a very early stage */ this->engine->AddMethod("import", &AILibrary::Import, 4, ".ssi"); @@ -163,6 +166,8 @@ AIInstance::AIInstance(AIInfo *info) : AIInstance::~AIInstance() { + AIObject::ActiveInstance active(this); + if (instance != NULL) this->engine->ReleaseObject(this->instance); if (engine != NULL) delete this->engine; delete this->storage; @@ -316,6 +321,8 @@ void AIInstance::Died() void AIInstance::GameLoop() { + AIObject::ActiveInstance active(this); + if (this->IsDead()) return; if (this->engine->HasScriptCrashed()) { /* The script crashed during saving, kill it here. */ @@ -423,10 +430,16 @@ void AIInstance::CollectGarbage() const instance->engine->InsertResult(AIObject::GetNewGroupID()); } -/* static */ AIStorage *AIInstance::GetStorage() +AIStorage *AIInstance::GetStorage() { - assert(Company::IsValidAiID(_current_company)); - return Company::Get(_current_company)->ai_instance->storage; + return this->storage; +} + +void *AIInstance::GetLogPointer() +{ + AIObject::ActiveInstance active(this); + + return AIObject::GetLogPointer(); } /* @@ -598,6 +611,8 @@ static const uint AISAVE_MAX_DEPTH = 25; void AIInstance::Save() { + AIObject::ActiveInstance active(this); + /* Don't save data if the AI didn't start yet or if it crashed. */ if (this->engine == NULL || this->engine->HasScriptCrashed()) { SaveEmpty(); @@ -662,7 +677,6 @@ void AIInstance::Save() _ai_sl_byte = 0; SlObject(NULL, _ai_byte); } - } void AIInstance::Suspend() @@ -739,6 +753,8 @@ void AIInstance::Suspend() void AIInstance::Load(int version) { + AIObject::ActiveInstance active(this); + if (this->engine == NULL || version == -1) { LoadEmpty(); return; @@ -795,3 +811,24 @@ SQInteger AIInstance::GetOpsTillSuspend( { return this->engine->GetOpsTillSuspend(); } + +void AIInstance::DoCommandCallback(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2) +{ + AIObject::ActiveInstance active(this); + + AIObject::SetLastCommandRes(result.Succeeded()); + + if (result.Failed()) { + AIObject::SetLastError(AIError::StringToError(result.GetErrorMessage())); + } else { + AIObject::IncreaseDoCommandCosts(result.GetCost()); + AIObject::SetLastCost(result.GetCost()); + } +} + +void AIInstance::InsertEvent(class AIEvent *event) +{ + AIObject::ActiveInstance active(this); + + AIEventController::InsertEvent(event); +} diff --git a/src/ai/ai_instance.hpp b/src/ai/ai_instance.hpp --- a/src/ai/ai_instance.hpp +++ b/src/ai/ai_instance.hpp @@ -81,10 +81,15 @@ public: /** * Create a new AI. + */ + AIInstance(); + ~AIInstance(); + + /** + * Initialize the AI and prepare it for its first run. * @param info The AI to create the instance of. */ - AIInstance(class AIInfo *info); - ~AIInstance(); + void Initialize(class AIInfo *info); /** * An AI in multiplayer waits for the server to handle his DoCommand. @@ -105,7 +110,12 @@ public: /** * Get the storage of this AI. */ - static class AIStorage *GetStorage(); + class AIStorage *GetStorage(); + + /** + * Get the log pointer of this AI. + */ + void *GetLogPointer(); /** * Return a true/false reply for a DoCommand. @@ -173,6 +183,22 @@ public: * @return The number of operations to execute. */ SQInteger GetOpsTillSuspend(); + + /** + * DoCommand callback function for all commands executed by AIs. + * @param result The result of the command. + * @param tile The tile on which the command was executed. + * @param p1 p1 as given to DoCommandPInternal. + * @param p2 p2 as given to DoCommandPInternal. + */ + void DoCommandCallback(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2); + + /** + * Insert an event for this AI. + * @param event The event to insert. + */ + void InsertEvent(class AIEvent *event); + private: class AIController *controller; ///< The AI main class. class AIStorage *storage; ///< Some global information for each running AI. diff --git a/src/ai/api/ai_controller.cpp b/src/ai/api/ai_controller.cpp --- a/src/ai/api/ai_controller.cpp +++ b/src/ai/api/ai_controller.cpp @@ -63,12 +63,12 @@ AIController::~AIController() /* static */ uint AIController::GetTick() { - return ::Company::Get(_current_company)->ai_instance->GetController()->ticks; + return AIObject::GetActiveInstance()->GetController()->ticks; } /* static */ int AIController::GetOpsTillSuspend() { - return ::Company::Get(_current_company)->ai_instance->GetOpsTillSuspend(); + return AIObject::GetActiveInstance()->GetOpsTillSuspend(); } /* static */ int AIController::GetSetting(const char *name) diff --git a/src/ai/api/ai_execmode.cpp b/src/ai/api/ai_execmode.cpp --- a/src/ai/api/ai_execmode.cpp +++ b/src/ai/api/ai_execmode.cpp @@ -32,9 +32,8 @@ AIExecMode::AIExecMode() AIExecMode::~AIExecMode() { if (this->GetDoCommandModeInstance() != this) { - AIInstance *instance = Company::Get(_current_company)->ai_instance; /* Ignore this error if the AI already died. */ - if (!instance->IsDead()) { + if (!AIObject::GetActiveInstance()->IsDead()) { throw AI_FatalError("AIExecMode object was removed while it was not the latest AI*Mode object created."); } } diff --git a/src/ai/api/ai_object.cpp b/src/ai/api/ai_object.cpp --- a/src/ai/api/ai_object.cpp +++ b/src/ai/api/ai_object.cpp @@ -12,8 +12,6 @@ #include "../../stdafx.h" #include "../../script/squirrel.hpp" #include "../../command_func.h" -#include "../../company_base.h" -#include "../../company_func.h" #include "../../network/network.h" #include "../../tunnelbridge.h" @@ -27,9 +25,30 @@ */ static AIStorage *GetStorage() { - return AIInstance::GetStorage(); + return AIObject::GetActiveInstance()->GetStorage(); +} + + +/* static */ AIInstance *AIObject::ActiveInstance::active = NULL; + +AIObject::ActiveInstance::ActiveInstance(AIInstance *instance) +{ + this->last_active = AIObject::ActiveInstance::active; + AIObject::ActiveInstance::active = instance; } +AIObject::ActiveInstance::~ActiveInstance() +{ + AIObject::ActiveInstance::active = this->last_active; +} + +/* static */ AIInstance *AIObject::GetActiveInstance() +{ + assert(AIObject::ActiveInstance::active != NULL); + return AIObject::ActiveInstance::active; +} + + /* static */ void AIObject::SetDoCommandDelay(uint ticks) { assert(ticks > 0); @@ -179,7 +198,7 @@ static AIStorage *GetStorage() /* static */ bool AIObject::CanSuspend() { - Squirrel *squirrel = Company::Get(_current_company)->ai_instance->engine; + Squirrel *squirrel = AIObject::GetActiveInstance()->engine; return GetStorage()->allow_do_command && squirrel->CanSuspend(); } diff --git a/src/ai/api/ai_object.hpp b/src/ai/api/ai_object.hpp --- a/src/ai/api/ai_object.hpp +++ b/src/ai/api/ai_object.hpp @@ -35,10 +35,26 @@ typedef bool (AIModeProc)(); * command processing, and command-validation checks. */ class AIObject : public SimpleCountedObject { -friend void CcAI(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2); friend class AIInstance; -friend class AIController; #ifndef DOXYGEN_AI_DOCS +protected: + /** + * A class that handles the current active instance. By instantiating it at + * the beginning of a function with the current active instance, it remains + * active till the scope of the variable closes. It then automatically + * reverts to the active instance it was before instantiating. + */ + class ActiveInstance { + friend class AIObject; + public: + ActiveInstance(AIInstance *instance); + ~ActiveInstance(); + private: + AIInstance *last_active; ///< The active instance before we go instantiated. + + static AIInstance *active; ///< The global current active instance. + }; + public: /** * Store the latest result of a DoCommand per company. @@ -47,9 +63,10 @@ public: static void SetLastCommandRes(bool res); /** - * Get the pointer to store log message in. + * Get the currently active instance. + * @return The instance. */ - static void *&GetLogPointer(); + static class AIInstance *GetActiveInstance(); protected: /** @@ -197,6 +214,11 @@ protected: */ static void *&GetEventPointer(); + /** + * Get the pointer to store log message in. + */ + static void *&GetLogPointer(); + private: /** * Store a new_vehicle_id per company. diff --git a/src/ai/api/ai_testmode.cpp b/src/ai/api/ai_testmode.cpp --- a/src/ai/api/ai_testmode.cpp +++ b/src/ai/api/ai_testmode.cpp @@ -32,9 +32,8 @@ AITestMode::AITestMode() AITestMode::~AITestMode() { if (this->GetDoCommandModeInstance() != this) { - AIInstance *instance = Company::Get(_current_company)->ai_instance; /* Ignore this error if the AI already died. */ - if (!instance->IsDead()) { + if (!AIObject::GetActiveInstance()->IsDead()) { throw AI_FatalError("AITestmode object was removed while it was not the latest AI*Mode object created."); } }