diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 1b329c7a3..84af1816c 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -47,6 +47,7 @@ core.features = { particle_blend_clip = true, remove_item_match_meta = true, httpfetch_additional_methods = true, + abm_changeable = true, } function core.has_feature(arg) diff --git a/builtin/game/register.lua b/builtin/game/register.lua index d6ada6920..b45c59c06 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -14,6 +14,8 @@ core.unregister_item_raw = nil local register_alias_raw = core.register_alias_raw core.register_alias_raw = nil +local override_abm_raw = core.override_abm + -- -- Item / entity / ABM / LBM registration functions -- @@ -100,6 +102,19 @@ function core.register_abm(spec) spec.mod_origin = core.get_current_modname() or "??" end +function core.override_abm(name, redef) + for id, abm in pairs(core.registered_abms) do + if abm.name == name then + for key, value in pairs(redef) do + abm[key] = redef[key] + end + override_abm_raw(id, abm) + return + end + end + core.log("error", "ABM '"..name.."' not found.") +end + function core.register_lbm(spec) -- Add to core.registered_lbms check_modname_prefix(spec.name) diff --git a/doc/lua_api.md b/doc/lua_api.md index ca3a5fee7..f769b443a 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5810,6 +5810,8 @@ Utilities remove_item_match_meta = true, -- The HTTP API supports the HEAD and PATCH methods (5.12.0) httpfetch_additional_methods = true, + -- Registered and named ABM can be changed on fly (5.13.0) + abm_changeable = true, } ``` @@ -6034,6 +6036,9 @@ Call these functions only at load time! according to its nature (e.g. `core.registered_nodes`) * `core.register_entity(name, entity definition)` * `core.register_abm(abm definition)` +* `core.override_abm(name, new_values)` + * Change registered ABM + * Only on fields mentonied as changeable can be changed. * `core.register_lbm(lbm definition)` * `core.register_alias(alias, original_name)` * Also use this to set the 'mapgen aliases' needed in a game for the core @@ -9587,6 +9592,10 @@ in active mapblocks. ```lua { + name = "". + -- Optional name of ABM. + -- ABM with name can be changed at runtime (See `core.override_abm`). + label = "Lava cooling", -- Descriptive label for profiling purposes (optional). -- Definitions with identical labels will be listed as one. @@ -9608,14 +9617,18 @@ in active mapblocks. interval = 10.0, -- Operation interval in seconds + -- This value can be changed by `core.override_abm` call. chance = 50, -- Probability of triggering `action` per-node per-interval is 1.0 / chance (integers only) + -- This value can be changed by `core.override_abm` call. + -- If chance is set to 0, ABM is disabled. min_y = -32768, max_y = 32767, -- min and max height levels where ABM will be processed (inclusive) -- can be used to reduce CPU usage + -- This value can be changed by `core.override_abm` call. catch_up = true, -- If true, catch-up behavior is enabled: The `chance` value is @@ -9631,6 +9644,7 @@ in active mapblocks. -- mapblock plus all 26 neighboring mapblocks. If any neighboring -- mapblocks are unloaded an estimate is calculated for them based on -- loaded mapblocks. + -- This function can be changed by `core.override_abm` call. } ``` diff --git a/games/devtest/mods/testabms/changeable.lua b/games/devtest/mods/testabms/changeable.lua new file mode 100644 index 000000000..a8bbe8a28 --- /dev/null +++ b/games/devtest/mods/testabms/changeable.lua @@ -0,0 +1,50 @@ +local abm_first_run = false + +minetest.register_chatcommand("changeableABM", { + params = "enable | disable | interval_1 | interval_5", + description = "Active/Deactivate changeable ABM.", + func = function(name, param) + if param == "enable" then + minetest.override_abm("testchangeableabm:abm", { + interval = 30.0, + chance = 1, + }) + abm_first_run = true + return true, "ABM has been enabled." + elseif param == "interval_1" then + minetest.override_abm("testchangeableabm:abm", { + interval = 1.0, + chance = 1, + }) + abm_first_run = true + return true, "ABM has been enabled with interval 1." + elseif param == "interval_5" then + minetest.override_abm("testchangeableabm:abm", { + interval = 5.0, + chance = 1, + }) + abm_first_run = true + return true, "ABM has been enabled with interval 5." + elseif param == "disable" then + minetest.override_abm("testchangeableabm:abm", { + chance = 0, + }) + return true, "ABM has been disabled." + else + return false, "Check /help changeableABB for allowed parameters." + end + end, +}) + +minetest.register_abm({ + name = "testchangeableabm:abm", + nodenames = "basenodes:stone", + interval = 30.0, + chance = 0, -- as default, ABM is disabled + action = function(pos) + if abm_first_run then + minetest.chat_send_all("Changeable ABM runs first time after enable.") + abm_first_run = false + end + end, +}) diff --git a/games/devtest/mods/testabms/init.lua b/games/devtest/mods/testabms/init.lua index 8bf4975cf..8a02a11ab 100644 --- a/games/devtest/mods/testabms/init.lua +++ b/games/devtest/mods/testabms/init.lua @@ -5,3 +5,4 @@ dofile(path.."/chances.lua") dofile(path.."/intervals.lua") dofile(path.."/min_max.lua") dofile(path.."/neighbors.lua") +dofile(path.."/changeable.lua") diff --git a/games/devtest/mods/unittests/env.lua b/games/devtest/mods/unittests/env.lua new file mode 100644 index 000000000..1b32a17bb --- /dev/null +++ b/games/devtest/mods/unittests/env.lua @@ -0,0 +1,47 @@ +-- Object Passing / Serialization + +local abm_action1 = function () +end +local abm_action2 = function () +end + +core.register_abm({ + name = "unittests:abm", + nodenames = "testnodes:stone", + interval = 1, + chance = 0, + min_y = -10, + max_y = 20, + action = abm_action1 +}) + +local abm_index = #core.registered_abms + +local function test_abm_override() + core.override_abm("unittests:abm", { + interval = 500, + chance = 25, + min_y = -100, + max_y = 200, + action = abm_action2 + }) + assert(core.registered_abms[abm_index].interval == 500) + assert(core.registered_abms[abm_index].chance == 25) + assert(core.registered_abms[abm_index].min_y == -100) + assert(core.registered_abms[abm_index].max_y == 200) + assert(core.registered_abms[abm_index].action == abm_action2) + + core.override_abm("unittests:abm", { + interval = 1, + chance = 0, + min_y = -10, + max_y = 20, + action = abm_action1 + }) + assert(core.registered_abms[abm_index].interval == 1) + assert(core.registered_abms[abm_index].chance == 0) + assert(core.registered_abms[abm_index].min_y == -10) + assert(core.registered_abms[abm_index].max_y == 20) + assert(core.registered_abms[abm_index].action == abm_action1) +end +unittests.register("test_abm_override", test_abm_override) diff --git a/games/devtest/mods/unittests/init.lua b/games/devtest/mods/unittests/init.lua index 22057f26a..0b4a05267 100644 --- a/games/devtest/mods/unittests/init.lua +++ b/games/devtest/mods/unittests/init.lua @@ -201,6 +201,7 @@ dofile(modpath .. "/inventory.lua") dofile(modpath .. "/load_time.lua") dofile(modpath .. "/on_shutdown.lua") dofile(modpath .. "/color.lua") +dofile(modpath .. "/env.lua") -------------- diff --git a/src/script/cpp_api/s_env.cpp b/src/script/cpp_api/s_env.cpp index 980ba37cb..234e56990 100644 --- a/src/script/cpp_api/s_env.cpp +++ b/src/script/cpp_api/s_env.cpp @@ -17,76 +17,68 @@ LuaABM & LuaLBM */ -class LuaABM : public ActiveBlockModifier { -private: - const int m_id; +LuaABM::LuaABM(int id, + const std::string &name, + const std::vector &trigger_contents, + const std::vector &required_neighbors, + const std::vector &without_neighbors, + float trigger_interval, u32 trigger_chance, bool simple_catch_up, + s16 min_y, s16 max_y): + m_id(id), + m_name(name), + m_trigger_contents(trigger_contents), + m_required_neighbors(required_neighbors), + m_without_neighbors(without_neighbors), + m_trigger_interval(trigger_interval), + m_trigger_chance(trigger_chance), + m_simple_catch_up(simple_catch_up), + m_min_y(min_y), + m_max_y(max_y) +{ +} +const std::string &LuaABM::getName() const +{ + return m_name; +} +const std::vector &LuaABM::getTriggerContents() const +{ + return m_trigger_contents; +} +const std::vector &LuaABM::getRequiredNeighbors() const +{ + return m_required_neighbors; +} +const std::vector &LuaABM::getWithoutNeighbors() const +{ + return m_without_neighbors; +} +float LuaABM::getTriggerInterval() +{ + return m_trigger_interval; +} +u32 LuaABM::getTriggerChance() +{ + return m_trigger_chance; +} +bool LuaABM::getSimpleCatchUp() +{ + return m_simple_catch_up; +} +s16 LuaABM::getMinY() +{ + return m_min_y; +} +s16 LuaABM::getMaxY() +{ + return m_max_y; +} - std::vector m_trigger_contents; - std::vector m_required_neighbors; - std::vector m_without_neighbors; - float m_trigger_interval; - u32 m_trigger_chance; - bool m_simple_catch_up; - s16 m_min_y; - s16 m_max_y; -public: - LuaABM(int id, - const std::vector &trigger_contents, - const std::vector &required_neighbors, - const std::vector &without_neighbors, - float trigger_interval, u32 trigger_chance, bool simple_catch_up, - s16 min_y, s16 max_y): - m_id(id), - m_trigger_contents(trigger_contents), - m_required_neighbors(required_neighbors), - m_without_neighbors(without_neighbors), - m_trigger_interval(trigger_interval), - m_trigger_chance(trigger_chance), - m_simple_catch_up(simple_catch_up), - m_min_y(min_y), - m_max_y(max_y) - { - } - virtual const std::vector &getTriggerContents() const - { - return m_trigger_contents; - } - virtual const std::vector &getRequiredNeighbors() const - { - return m_required_neighbors; - } - virtual const std::vector &getWithoutNeighbors() const - { - return m_without_neighbors; - } - virtual float getTriggerInterval() - { - return m_trigger_interval; - } - virtual u32 getTriggerChance() - { - return m_trigger_chance; - } - virtual bool getSimpleCatchUp() - { - return m_simple_catch_up; - } - virtual s16 getMinY() - { - return m_min_y; - } - virtual s16 getMaxY() - { - return m_max_y; - } - - virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n, - u32 active_object_count, u32 active_object_count_wider) - { - auto *script = env->getScriptIface(); - script->triggerABM(m_id, p, n, active_object_count, active_object_count_wider); - } -}; +void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, + u32 active_object_count, u32 active_object_count_wider) +{ + auto *script = env->getScriptIface(); + script->triggerABM(m_id, p, n, active_object_count, active_object_count_wider); +} class LuaLBM : public LoadingBlockModifierDef { @@ -192,6 +184,50 @@ bool ScriptApiEnv::read_nodenames(lua_State *L, int idx, std::vector trigger_contents; + lua_getfield(L, abm_index, "nodenames"); + read_nodenames(L, -1, trigger_contents); + lua_pop(L, 1); + + std::vector required_neighbors; + lua_getfield(L, abm_index, "neighbors"); + read_nodenames(L, -1, required_neighbors); + lua_pop(L, 1); + + std::vector without_neighbors; + lua_getfield(L, abm_index, "without_neighbors"); + read_nodenames(L, -1, without_neighbors); + lua_pop(L, 1); + + float trigger_interval = 10.0; + getfloatfield(L, abm_index, "interval", trigger_interval); + + int trigger_chance = 50; + getintfield(L, abm_index, "chance", trigger_chance); + + bool simple_catch_up = true; + getboolfield(L, abm_index, "catch_up", simple_catch_up); + + s16 min_y = INT16_MIN; + getintfield(L, abm_index, "min_y", min_y); + + s16 max_y = INT16_MAX; + getintfield(L, abm_index, "max_y", max_y); + + lua_getfield(L, abm_index, "action"); + luaL_checktype(L, abm_index + 1, LUA_TFUNCTION); + lua_pop(L, 1); + + return new LuaABM(id, name, trigger_contents, required_neighbors, + without_neighbors, trigger_interval, trigger_chance, + simple_catch_up, min_y, max_y); +} + void ScriptApiEnv::readABMs() { SCRIPTAPI_PRECHECKHEADER @@ -212,45 +248,10 @@ void ScriptApiEnv::readABMs() int id = lua_tonumber(L, -2); int current_abm = lua_gettop(L); - std::vector trigger_contents; - lua_getfield(L, current_abm, "nodenames"); - read_nodenames(L, -1, trigger_contents); - lua_pop(L, 1); - - std::vector required_neighbors; - lua_getfield(L, current_abm, "neighbors"); - read_nodenames(L, -1, required_neighbors); - lua_pop(L, 1); - - std::vector without_neighbors; - lua_getfield(L, current_abm, "without_neighbors"); - read_nodenames(L, -1, without_neighbors); - lua_pop(L, 1); - - float trigger_interval = 10.0; - getfloatfield(L, current_abm, "interval", trigger_interval); - - int trigger_chance = 50; - getintfield(L, current_abm, "chance", trigger_chance); - - bool simple_catch_up = true; - getboolfield(L, current_abm, "catch_up", simple_catch_up); - - s16 min_y = INT16_MIN; - getintfield(L, current_abm, "min_y", min_y); - - s16 max_y = INT16_MAX; - getintfield(L, current_abm, "max_y", max_y); - - lua_getfield(L, current_abm, "action"); - luaL_checktype(L, current_abm + 1, LUA_TFUNCTION); - lua_pop(L, 1); - - LuaABM *abm = new LuaABM(id, trigger_contents, required_neighbors, - without_neighbors, trigger_interval, trigger_chance, - simple_catch_up, min_y, max_y); - - env->addActiveBlockModifier(abm); + LuaABM *abm = readABM(L, current_abm, id); + if (abm != nullptr) { + env->addActiveBlockModifier(abm); + } // removes value, keeps key for next iteration lua_pop(L, 1); diff --git a/src/script/cpp_api/s_env.h b/src/script/cpp_api/s_env.h index e32c6bc4c..65b33da66 100644 --- a/src/script/cpp_api/s_env.h +++ b/src/script/cpp_api/s_env.h @@ -7,6 +7,7 @@ #include "cpp_api/s_base.h" #include "irr_v3d.h" #include "mapnode.h" +#include "server/blockmodifier.h" #include #include @@ -14,6 +15,47 @@ class ServerEnvironment; class MapBlock; struct ScriptCallbackState; +/* + LuaABM +*/ + +class LuaABM : public ActiveBlockModifier { +private: + const int m_id; + + std::string m_name; + std::vector m_trigger_contents; + std::vector m_required_neighbors; + std::vector m_without_neighbors; + float m_trigger_interval; + u32 m_trigger_chance; + bool m_simple_catch_up; + s16 m_min_y; + s16 m_max_y; + +public: + LuaABM(int id, + const std::string &name, + const std::vector &trigger_contents, + const std::vector &required_neighbors, + const std::vector &without_neighbors, + float trigger_interval, u32 trigger_chance, bool simple_catch_up, + s16 min_y, s16 max_y); + + virtual const std::string &getName() const; + virtual const std::vector &getTriggerContents() const; + virtual const std::vector &getRequiredNeighbors() const; + virtual const std::vector &getWithoutNeighbors() const; + virtual float getTriggerInterval(); + virtual u32 getTriggerChance(); + virtual bool getSimpleCatchUp(); + virtual s16 getMinY(); + virtual s16 getMaxY(); + + virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n, + u32 active_object_count, u32 active_object_count_wider); +}; + class ScriptApiEnv : virtual public ScriptApiBase { public: @@ -50,6 +92,7 @@ public: void triggerLBM(int id, MapBlock *block, const std::unordered_set &positions, float dtime_s); + static LuaABM *readABM(lua_State *L, int abm_index, int id); private: void readABMs(); diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index e21a954ac..281e22868 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -1383,6 +1383,21 @@ int ModApiEnv::l_get_translated_string(lua_State * L) return 1; } +// override_abm(abm_name, parameters) +int ModApiEnv::l_override_abm(lua_State *L) +{ + GET_ENV_PTR; + + int abm_id = lua_tonumber(L, 1); + + LuaABM *abm = ScriptApiEnv::readABM(L, 2, abm_id); + if (abm != nullptr) { + env->replaceActiveBlockModifier(abm); + } + + return 0; +} + void ModApiEnv::Initialize(lua_State *L, int top) { API_FCT(set_node); @@ -1435,6 +1450,7 @@ void ModApiEnv::Initialize(lua_State *L, int top) API_FCT(forceload_free_block); API_FCT(compare_block_status); API_FCT(get_translated_string); + API_FCT(override_abm); } void ModApiEnv::InitializeClient(lua_State *L, int top) diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index e755166a7..60639f1a0 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -225,6 +225,9 @@ private: // get_translated_string(lang_code, string) static int l_get_translated_string(lua_State * L); + // override_abm(abm_name, new_data) + static int l_override_abm(lua_State * L); + public: static void Initialize(lua_State *L, int top); static void InitializeClient(lua_State *L, int top); diff --git a/src/server/blockmodifier.cpp b/src/server/blockmodifier.cpp index 1983d5def..c8767c0cf 100644 --- a/src/server/blockmodifier.cpp +++ b/src/server/blockmodifier.cpp @@ -47,19 +47,19 @@ ABMHandler::ABMHandler(std::vector &abms, for (ABMWithState &abmws : abms) { ActiveBlockModifier *abm = abmws.abm; float trigger_interval = abm->getTriggerInterval(); - if (trigger_interval < 0.001f) + if( trigger_interval < 0.001f) trigger_interval = 0.001f; float actual_interval = dtime_s; if (use_timers) { abmws.timer += dtime_s; if (abmws.timer < trigger_interval) continue; - abmws.timer -= trigger_interval; + abmws.timer = std::fmod(abmws.timer, trigger_interval); actual_interval = trigger_interval; } float chance = abm->getTriggerChance(); if (chance == 0) - chance = 1; + continue; ActiveABM aabm; aabm.abm = abm; diff --git a/src/server/blockmodifier.h b/src/server/blockmodifier.h index 55b2c355d..0c67640c9 100644 --- a/src/server/blockmodifier.h +++ b/src/server/blockmodifier.h @@ -28,6 +28,8 @@ public: ActiveBlockModifier() = default; virtual ~ActiveBlockModifier() = default; + // Optional name of modifier. + virtual const std::string &getName() const = 0; // Set of contents to trigger on virtual const std::vector &getTriggerContents() const = 0; // Set of required neighbors (trigger doesn't happen if none are found) diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 6368f4f8f..d4f1da9c9 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -582,6 +582,19 @@ void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm) m_abms.emplace_back(abm); } +bool ServerEnvironment::replaceActiveBlockModifier(ActiveBlockModifier *abm) +{ + std::string name(abm->getName()); + for (auto &&m_abm : m_abms) { + if (m_abm.abm->getName() == name) { + delete m_abm.abm; + m_abm = abm; + return true; + } + } + return false; +} + void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm) { m_lbm_mgr.addLBMDef(lbm); diff --git a/src/serverenvironment.h b/src/serverenvironment.h index 04153e944..9b8e657f5 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -205,6 +205,7 @@ public: */ void addActiveBlockModifier(ActiveBlockModifier *abm); + bool replaceActiveBlockModifier(ActiveBlockModifier *abm); void addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm); /*