diff --git a/src/script/script_instance.cpp b/src/script/script_instance.cpp index e9bad5966c..8edd871bdc 100644 --- a/src/script/script_instance.cpp +++ b/src/script/script_instance.cpp @@ -710,6 +710,16 @@ SQInteger ScriptInstance::GetOpsTillSuspend() return this->engine->GetOpsTillSuspend(); } +void ScriptInstance::LimitOpsTillSuspend(SQInteger suspend) +{ + SQInteger current = this->GetOpsTillSuspend(); + if (suspend < current) { + /* Reduce script ops. */ + HSQUIRRELVM vm = this->engine->GetVM(); + Squirrel::DecreaseOps(vm, current - suspend); + } +} + bool ScriptInstance::DoCommandCallback(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd) { ScriptObject::ActiveInstance active(this); @@ -746,6 +756,11 @@ size_t ScriptInstance::GetAllocatedMemory() const return this->engine->GetAllocatedMemory(); } +void ScriptInstance::SetMemoryAllocationLimit(size_t limit) const +{ + if (this->engine != nullptr) this->engine->SetMemoryAllocationLimit(limit); +} + void ScriptInstance::ReleaseSQObject(HSQOBJECT *obj) { if (!this->in_shutdown) this->engine->ReleaseObject(obj); diff --git a/src/script/script_instance.hpp b/src/script/script_instance.hpp index b06f79a030..b2a6c5f7a9 100644 --- a/src/script/script_instance.hpp +++ b/src/script/script_instance.hpp @@ -174,6 +174,8 @@ public: */ SQInteger GetOpsTillSuspend(); + void LimitOpsTillSuspend(SQInteger suspend); + /** * DoCommand callback function for all commands executed by scripts. * @param result The result of the command. @@ -200,6 +202,8 @@ public: size_t GetAllocatedMemory() const; + void SetMemoryAllocationLimit(size_t limit) const; + /** * Indicate whether this instance is currently being destroyed. */ diff --git a/src/script/squirrel.cpp b/src/script/squirrel.cpp index 2bd2c9c744..bea0edbc88 100644 --- a/src/script/squirrel.cpp +++ b/src/script/squirrel.cpp @@ -129,6 +129,13 @@ size_t Squirrel::GetAllocatedMemory() const noexcept return this->allocator->allocated_size; } +void Squirrel::SetMemoryAllocationLimit(size_t limit) noexcept +{ + if (this->allocator != nullptr) { + this->allocator->allocation_limit = limit; + } +} + void Squirrel::CompileError(HSQUIRRELVM vm, const SQChar *desc, const SQChar *source, SQInteger line, SQInteger column) { diff --git a/src/script/squirrel.hpp b/src/script/squirrel.hpp index 412011cccc..3ec7023b02 100644 --- a/src/script/squirrel.hpp +++ b/src/script/squirrel.hpp @@ -280,6 +280,8 @@ public: * Get number of bytes allocated by this VM. */ size_t GetAllocatedMemory() const noexcept; + + void SetMemoryAllocationLimit(size_t limit) noexcept; }; diff --git a/src/settings.cpp b/src/settings.cpp index 01d3ba14c5..4e0aec5b6e 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -58,6 +58,8 @@ #include "ai/ai.hpp" #include "game/game_config.hpp" #include "game/game.hpp" +#include "ai/ai_instance.hpp" +#include "game/game_instance.hpp" #include "ship.h" #include "smallmap_gui.h" #include "roadveh.h" @@ -1229,6 +1231,54 @@ static bool InvalidateAISettingsWindow(int32 p1) return true; } +static bool ScriptMaxOpsChange(int32 p1) +{ + if (_networking && !_network_server) return true; + + GameInstance *g = Game::GetGameInstance(); + if (g != nullptr && !g->IsDead()) { + g->LimitOpsTillSuspend(p1); + } + + for (const Company *c : Company::Iterate()) { + if (c->is_ai && c->ai_instance != nullptr && !c->ai_instance->IsDead()) { + c->ai_instance->LimitOpsTillSuspend(p1); + } + } + + return true; +} + +static bool ScriptMaxMemoryChange(int32 p1) +{ + if (_networking && !_network_server) return true; + + size_t limit = static_cast(p1) << 20; + + GameInstance *g = Game::GetGameInstance(); + if (g != nullptr && !g->IsDead()) { + if (g->GetAllocatedMemory() > limit) return false; + } + + for (const Company *c : Company::Iterate()) { + if (c->is_ai && c->ai_instance != nullptr && !c->ai_instance->IsDead()) { + if (c->ai_instance->GetAllocatedMemory() > limit) return false; + } + } + + if (g != nullptr && !g->IsDead()) { + g->SetMemoryAllocationLimit(limit); + } + + for (const Company *c : Company::Iterate()) { + if (c->is_ai && c->ai_instance != nullptr && !c->ai_instance->IsDead()) { + c->ai_instance->SetMemoryAllocationLimit(limit); + } + } + + return true; +} + /** * Update the town authority window after a town authority setting change. * @param p1 Unused. diff --git a/src/table/settings.ini b/src/table/settings.ini index 6d1ffbfaa3..cc111e7d1f 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -40,6 +40,8 @@ static bool InvalidateCompanyLiveryWindow(int32 p1); static bool InvalidateNewGRFChangeWindows(int32 p1); static bool InvalidateIndustryViewWindow(int32 p1); static bool InvalidateAISettingsWindow(int32 p1); +static bool ScriptMaxOpsChange(int32 p1); +static bool ScriptMaxMemoryChange(int32 p1); static bool RedrawTownAuthority(int32 p1); static bool InvalidateCompanyInfrastructureWindow(int32 p1); static bool InvalidateCompanyWindow(int32 p1); @@ -2593,7 +2595,6 @@ base = GameSettings var = script.script_max_opcode_till_suspend type = SLE_UINT32 from = SLV_107 -guiflags = SGF_NEWGAME_ONLY def = 10000 min = 500 max = 250000 @@ -2601,6 +2602,7 @@ interval = 2500 str = STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES strhelp = STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES_HELPTEXT strval = STR_JUST_COMMA +proc = ScriptMaxOpsChange cat = SC_EXPERT [SDT_VAR] @@ -2608,7 +2610,6 @@ base = GameSettings var = script.script_max_memory_megabytes type = SLE_UINT32 from = SLV_SCRIPT_MEMLIMIT -guiflags = SGF_NEWGAME_ONLY def = 1024 min = 8 max = 8192 @@ -2616,6 +2617,7 @@ interval = 8 str = STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY strhelp = STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY_HELPTEXT strval = STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY_VALUE +proc = ScriptMaxMemoryChange cat = SC_EXPERT ##