/* * This file is part of OpenTTD. * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . */ /** @file script_controller.cpp Implementation of ScriptControler. */ #include "../../stdafx.h" #include "../../string_func.h" #include "../../script/squirrel.hpp" #include "../../rev.h" #include "script_controller.hpp" #include "script_error.hpp" #include "../script_fatalerror.hpp" #include "../script_info.hpp" #include "../script_instance.hpp" #include "script_log.hpp" #include "../../ai/ai_gui.hpp" #include "../../settings_type.h" #include "../../network/network.h" #include "../../misc_cmd.h" #include "../../safeguards.h" /* static */ void ScriptController::SetCommandDelay(int ticks) { if (ticks <= 0) return; ScriptObject::SetDoCommandDelay(ticks); } /* static */ void ScriptController::Sleep(int ticks) { if (!ScriptObject::CanSuspend()) { throw Script_FatalError("You are not allowed to call Sleep in your constructor, Save(), Load(), and any valuator."); } if (ticks <= 0) { ScriptLog::Warning("Sleep() value should be > 0. Assuming value 1."); ticks = 1; } throw Script_Suspend(ticks, nullptr); } /* static */ void ScriptController::Break(const char* message) { if (_network_dedicated || !_settings_client.gui.ai_developer_tools) return; ScriptObject::GetActiveInstance()->Pause(); char log_message[1024]; seprintf(log_message, lastof(log_message), "Break: %s", message); ScriptLog::Log(ScriptLog::LOG_SQ_ERROR, log_message); /* Inform script developer that their script has been paused and * needs manual action to continue. */ ShowAIDebugWindow(ScriptObject::GetRootCompany()); if ((_pause_mode & PM_PAUSED_NORMAL) == PM_UNPAUSED) { ScriptObject::Command::Do(PM_PAUSED_NORMAL, true); } } /* static */ void ScriptController::Print(bool error_msg, const char *message) { ScriptLog::Log(error_msg ? ScriptLog::LOG_SQ_ERROR : ScriptLog::LOG_SQ_INFO, message); } ScriptController::ScriptController(CompanyID company) : ticks(0), loaded_library_count(0) { ScriptObject::SetCompany(company); } ScriptController::~ScriptController() { for (const auto &item : this->loaded_library) { free(item.second); free(item.first); } this->loaded_library.clear(); } /* static */ uint ScriptController::GetTick() { return ScriptObject::GetActiveInstance()->GetController()->ticks; } /* static */ int ScriptController::GetOpsTillSuspend() { return ScriptObject::GetActiveInstance()->GetOpsTillSuspend(); } /* static */ int ScriptController::GetSetting(const char *name) { return ScriptObject::GetActiveInstance()->GetSetting(name); } /* static */ uint ScriptController::GetVersion() { return _openttd_newgrf_version; } /* static */ HSQOBJECT ScriptController::Import(const char *library, const char *class_name, int version) { ScriptController *controller = ScriptObject::GetActiveInstance()->GetController(); Squirrel *engine = ScriptObject::GetActiveInstance()->engine; HSQUIRRELVM vm = engine->GetVM(); ScriptInfo *lib = ScriptObject::GetActiveInstance()->FindLibrary(library, version); if (lib == nullptr) { char error[1024]; seprintf(error, lastof(error), "couldn't find library '%s' with version %d", library, version); throw sq_throwerror(vm, error); } /* Internally we store libraries as 'library.version' */ char library_name[1024]; seprintf(library_name, lastof(library_name), "%s.%d", library, version); strtolower(library_name); /* Get the current table/class we belong to */ HSQOBJECT parent; sq_getstackobj(vm, 1, &parent); char fake_class[1024]; LoadedLibraryList::iterator it = controller->loaded_library.find(library_name); if (it != controller->loaded_library.end()) { strecpy(fake_class, (*it).second, lastof(fake_class)); } else { int next_number = ++controller->loaded_library_count; /* Create a new fake internal name */ seprintf(fake_class, lastof(fake_class), "_internalNA%d", next_number); /* Load the library in a 'fake' namespace, so we can link it to the name the user requested */ sq_pushroottable(vm); sq_pushstring(vm, fake_class, -1); sq_newclass(vm, SQFalse); /* Load the library */ if (!engine->LoadScript(vm, lib->GetMainScript(), false)) { char error[1024]; seprintf(error, lastof(error), "there was a compile error when importing '%s' version %d", library, version); throw sq_throwerror(vm, error); } /* Create the fake class */ sq_newslot(vm, -3, SQFalse); sq_pop(vm, 1); controller->loaded_library[stredup(library_name)] = stredup(fake_class); } /* Find the real class inside the fake class (like 'sets.Vector') */ sq_pushroottable(vm); sq_pushstring(vm, fake_class, -1); if (SQ_FAILED(sq_get(vm, -2))) { throw sq_throwerror(vm, "internal error assigning library class"); } sq_pushstring(vm, lib->GetInstanceName(), -1); if (SQ_FAILED(sq_get(vm, -2))) { char error[1024]; seprintf(error, lastof(error), "unable to find class '%s' in the library '%s' version %d", lib->GetInstanceName(), library, version); throw sq_throwerror(vm, error); } HSQOBJECT obj; sq_getstackobj(vm, -1, &obj); sq_pop(vm, 3); if (StrEmpty(class_name)) return obj; /* Now link the name the user wanted to our 'fake' class */ sq_pushobject(vm, parent); sq_pushstring(vm, class_name, -1); sq_pushobject(vm, obj); sq_newclass(vm, SQTrue); sq_newslot(vm, -3, SQFalse); sq_pop(vm, 1); return obj; }