1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00
This commit is contained in:
sfence 2025-06-21 16:29:00 +03:00 committed by GitHub
commit 1a53032508
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 233 additions and 43 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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.
}
```

View file

@ -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,
})

View file

@ -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")

View file

@ -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)

View file

@ -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")
--------------

View file

@ -21,6 +21,7 @@ class LuaABM : public ActiveBlockModifier {
private:
const int m_id;
std::string m_name;
std::vector<std::string> m_trigger_contents;
std::vector<std::string> m_required_neighbors;
std::vector<std::string> m_without_neighbors;
@ -31,12 +32,14 @@ private:
s16 m_max_y;
public:
LuaABM(int id,
const std::string name,
const std::vector<std::string> &trigger_contents,
const std::vector<std::string> &required_neighbors,
const std::vector<std::string> &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),
@ -47,6 +50,10 @@ public:
m_max_y(max_y)
{
}
virtual const std::string &getName() const
{
return m_name;
}
virtual const std::vector<std::string> &getTriggerContents() const
{
return m_trigger_contents;
@ -79,7 +86,6 @@ public:
{
return m_max_y;
}
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n,
u32 active_object_count, u32 active_object_count_wider)
{
@ -192,6 +198,62 @@ bool ScriptApiEnv::read_nodenames(lua_State *L, int idx, std::vector<std::string
return true;
}
int ScriptApiEnv::override_abm(lua_State *L, ServerEnvironment *env)
{
int abm_id = lua_tonumber(L, 1);
LuaABM *abm = ScriptApiEnv::readABM(L, 2, abm_id);
if (abm != nullptr) {
env->replaceActiveBlockModifier(abm);
}
return 0;
}
LuaABM *ScriptApiEnv::readABM(lua_State *L, int abm_index, int id)
{
std::string name;
getstringfield(L, abm_index, "name", name);
std::vector<std::string> trigger_contents;
lua_getfield(L, abm_index, "nodenames");
read_nodenames(L, -1, trigger_contents);
lua_pop(L, 1);
std::vector<std::string> required_neighbors;
lua_getfield(L, abm_index, "neighbors");
read_nodenames(L, -1, required_neighbors);
lua_pop(L, 1);
std::vector<std::string> 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 +274,10 @@ void ScriptApiEnv::readABMs()
int id = lua_tonumber(L, -2);
int current_abm = lua_gettop(L);
std::vector<std::string> trigger_contents;
lua_getfield(L, current_abm, "nodenames");
read_nodenames(L, -1, trigger_contents);
lua_pop(L, 1);
std::vector<std::string> required_neighbors;
lua_getfield(L, current_abm, "neighbors");
read_nodenames(L, -1, required_neighbors);
lua_pop(L, 1);
std::vector<std::string> 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);

View file

@ -7,11 +7,13 @@
#include "cpp_api/s_base.h"
#include "irr_v3d.h"
#include "mapnode.h"
#include "server/blockmodifier.h"
#include <unordered_set>
#include <vector>
class ServerEnvironment;
class MapBlock;
class LuaABM;
struct ScriptCallbackState;
class ScriptApiEnv : virtual public ScriptApiBase
@ -50,7 +52,10 @@ public:
void triggerLBM(int id, MapBlock *block,
const std::unordered_set<v3s16> &positions, float dtime_s);
static int override_abm(lua_State * L, ServerEnvironment *env);
private:
static LuaABM *readABM(lua_State *L, int abm_index, int id);
void readABMs();
void readLBMs();

View file

@ -1383,6 +1383,15 @@ 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;
// redirect to s_env.cpp to avoid need of public LuaABM definiton or reinterpret_cast
return ScriptApiEnv::override_abm(L, env);
}
void ModApiEnv::Initialize(lua_State *L, int top)
{
API_FCT(set_node);
@ -1435,6 +1444,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)

View file

@ -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);

View file

@ -47,19 +47,19 @@ ABMHandler::ABMHandler(std::vector<ABMWithState> &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;

View file

@ -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<std::string> &getTriggerContents() const = 0;
// Set of required neighbors (trigger doesn't happen if none are found)

View file

@ -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);

View file

@ -205,6 +205,7 @@ public:
*/
void addActiveBlockModifier(ActiveBlockModifier *abm);
bool replaceActiveBlockModifier(ActiveBlockModifier *abm);
void addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm);
/*