1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-08-06 17:41:04 +00:00

Add proff of concept for blocks callback.

This commit is contained in:
SFENCE 2025-04-09 20:27:26 +02:00
parent a00b9cab36
commit 18c740712a
7 changed files with 212 additions and 0 deletions

View file

@ -6636,6 +6636,17 @@ Environment access
* mode = `"quick"`: Clear objects immediately in loaded mapblocks,
clear objects in unloaded mapblocks only when the
mapblocks are next activated.
* `core.blocks_callback(options)`
* Call Lua function over all loaded or loadable blocks
* Takes an table as an argument with the fields:
* `mode`
* mode = "full": Load and go through every mapblock, clearing objects (default).
* mode = "quick": Clear objects immediately in loaded mapblocks, clear objects in unloaded mapblocks only when the mapblocks are next activated.
* `callback`: Lua callback function function(name, staticdata, params.
* `blockpos_hash`: block position hash compatible with `core.hash_node_position` and `core.get_position_from_hash`
* `params`: Some Lua value.
* `params`: Params for Lua callback functions.
* `core.load_area(pos1[, pos2])`
* Load the mapblocks containing the area from `pos1` to `pos2`.
`pos2` defaults to `pos1` if not specified.

View file

@ -419,6 +419,40 @@ bool ScriptApiEnv::has_on_mapblocks_changed()
return lua_objlen(L, -1) > 0;
}
void ScriptApiEnv::block_callback(const v3s16 blockpos, ScriptCallbackState *state)
{
Server *server = getServer();
// This function should be executed with envlock held.
// The caller (LuaClearObjectback in src/script/lua_api/l_env.cpp)
// should have obtained the lock.
// Note that the order of these locks is important! Envlock must *ALWAYS*
// be acquired before attempting to acquire scriptlock, or else ServerThread
// will try to acquire scriptlock after it already owns envlock, thus
// deadlocking EmergeThread and ServerThread
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
lua_rawgeti(L, LUA_REGISTRYINDEX, state->callback_ref);
lua_pushnumber(L, hash_node_position(blockpos));
lua_rawgeti(L, LUA_REGISTRYINDEX, state->args_ref);
setOriginDirect(state->origin.c_str());
try {
PCALL_RES(lua_pcall(L, 2, 1, error_handler));
lua_pop(L, 2); // Pop callback result and error handler
} catch (LuaError &e) {
// Note: don't throw here, we still need to run the cleanup code below
server->setAsyncFatalError(e);
}
}
void ScriptApiEnv::triggerABM(int id, v3s16 p, MapNode n,
u32 active_object_count, u32 active_object_count_wider)
{

View file

@ -41,6 +41,9 @@ public:
// Determines whether there are any on_mapblocks_changed callbacks
bool has_on_mapblocks_changed();
// Calll Lua callback for block
void block_callback(const v3s16 blockpos, ScriptCallbackState *state);
// Initializes environment and loads some definitions from Lua
void initializeEnvironment(ServerEnvironment *env);

View file

@ -155,6 +155,19 @@ void LuaEmergeAreaCallback(v3s16 blockpos, EmergeAction action, void *param)
delete state;
}
void LuaBlockCallback(const v3s16 blockpos, void *param)
{
ScriptCallbackState *state = static_cast<ScriptCallbackState *>(param);
assert(state != NULL);
assert(state->script != NULL);
// state must be protected by envlock
//Server *server = state->script->getServer();
//MutexAutoLock envlock(server->m_env_mutex);
state->script->block_callback(blockpos, state);
}
/* Exported functions */
// set_node(pos, node)
@ -1092,6 +1105,56 @@ int ModApiEnv::l_clear_objects(lua_State *L)
return 0;
}
// blocks_callback
int ModApiEnv::l_blocks_callback(lua_State *L)
{
GET_ENV_PTR;
ScriptCallbackState *state = NULL;
BlocksCallbackConfig config;
config.mode = CLEAR_OBJECTS_MODE_QUICK;
config.callback = nullptr;
if (lua_istable(L, 1)) {
lua_getfield(L, 1, "mode");
config.mode = (ClearObjectsMode)getenumfield(L, 1, "mode",
ModApiEnv::es_ClearObjectsMode, config.mode);
lua_pop(L, 1);
lua_getfield(L, 1, "callback");
if (lua_isfunction(L, -1)) {
config.callback = LuaBlockCallback;
int callback_ref = luaL_ref(L, LUA_REGISTRYINDEX);
lua_getfield(L, 1, "params");
int args_ref = luaL_ref(L, LUA_REGISTRYINDEX);
state = new ScriptCallbackState;
state->script = getServer(L)->getScriptIface();
state->callback_ref = callback_ref;
state->args_ref = args_ref;
state->refcount = 1;
state->origin = getScriptApiBase(L)->getOrigin();
config.param = static_cast<void *>(state);
}
else {
lua_pop(L, 1);
}
}
env->blocksCallback(config);
if (state) {
luaL_unref(L, LUA_REGISTRYINDEX, state->callback_ref);
luaL_unref(L, LUA_REGISTRYINDEX, state->args_ref);
delete state;
}
return 0;
}
// line_of_sight(pos1, pos2) -> true/false, pos
int ModApiEnv::l_line_of_sight(lua_State *L)
{
@ -1419,6 +1482,7 @@ void ModApiEnv::Initialize(lua_State *L, int top)
API_FCT(get_perlin_map);
API_FCT(get_voxel_manip);
API_FCT(clear_objects);
API_FCT(blocks_callback);
API_FCT(spawn_tree);
API_FCT(find_path);
API_FCT(line_of_sight);

View file

@ -195,6 +195,9 @@ private:
// clear all objects in the environment
static int l_clear_objects(lua_State *L);
// blocks_callback()
static int l_blocks_callback(lua_State *L);
// spawn_tree(pos, treedef)
static int l_spawn_tree(lua_State *L);

View file

@ -866,6 +866,90 @@ void ServerEnvironment::clearObjects(ClearObjectsMode mode)
<< " in " << num_blocks_cleared << " blocks" << std::endl;
}
void ServerEnvironment::blocksCallback(BlocksCallbackConfig &config)
{
infostream << "ServerEnvironment::blocksCallback(): "
<< "Call Lua callback over blocks" << std::endl;
// Get list of loaded blocks
std::vector<v3s16> loaded_blocks;
infostream << "ServerEnvironment::blocksCallback(): "
<< "Listing all loaded blocks" << std::endl;
m_map->listAllLoadedBlocks(loaded_blocks);
infostream << "ServerEnvironment::blocksCallback(): "
<< "Done listing all loaded blocks: "
<< loaded_blocks.size()<<std::endl;
// Get list of loadable blocks
std::vector<v3s16> loadable_blocks;
if (config.mode == CLEAR_OBJECTS_MODE_FULL) {
infostream << "ServerEnvironment::blocksCallback(): "
<< "Listing all loadable blocks" << std::endl;
m_map->listAllLoadableBlocks(loadable_blocks);
infostream << "ServerEnvironment::blocksCallback(): "
<< "Done listing all loadable blocks: "
<< loadable_blocks.size() << std::endl;
} else {
loadable_blocks = loaded_blocks;
}
actionstream << "ServerEnvironment::blocksCallback(): "
<< "Now processing " << loadable_blocks.size()
<< " blocks" << std::endl;
// Grab a reference on each loaded block to avoid unloading it
for (v3s16 p : loaded_blocks) {
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
assert(block != NULL);
block->refGrab();
}
// Remove objects in all loadable blocks
u32 unload_interval = U32_MAX;
if (config.mode == CLEAR_OBJECTS_MODE_FULL) {
unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
unload_interval = MYMAX(unload_interval, 1);
}
u32 report_interval = loadable_blocks.size() / 10;
u32 num_blocks_processed = 0;
for (auto i = loadable_blocks.begin();
i != loadable_blocks.end(); ++i) {
v3s16 p = *i;
MapBlock *block = m_map->emergeBlock(p, false);
if (!block) {
errorstream << "ServerEnvironment::blocksCallback(): "
<< "Failed to emerge block " << p << std::endl;
continue;
}
config.callback(p, config.param);
num_blocks_processed++;
if (report_interval != 0 &&
num_blocks_processed % report_interval == 0) {
float percent = 100.0 * (float)num_blocks_processed /
loadable_blocks.size();
actionstream << "ServerEnvironment::blocksCallback(): "
<< "Processed" << num_blocks_processed << " blocks ("
<< percent << "%)" << std::endl;
}
if (num_blocks_processed % unload_interval == 0) {
m_map->unloadUnreferencedBlocks();
}
}
m_map->unloadUnreferencedBlocks();
// Drop references that were added above
for (v3s16 p : loaded_blocks) {
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
assert(block);
block->refDrop();
}
actionstream << "ServerEnvironment::blocksCallback(): "
<< "Finished: Processed " << num_blocks_processed << " blocks" << std::endl;
}
void ServerEnvironment::step(float dtime)
{
ScopeProfiler sp2(g_profiler, "ServerEnv::step()", SPT_AVG);

View file

@ -101,6 +101,16 @@ enum ClearObjectsMode {
CLEAR_OBJECTS_MODE_QUICK,
};
// blocksCallback
typedef void (*BlocksCallback)(const v3s16 blockpos, void *param);
struct BlocksCallbackConfig {
ClearObjectsMode mode;
BlocksCallback callback;
void *param;
};
class ServerEnvironment final : public Environment
{
public:
@ -242,6 +252,9 @@ public:
// Clear objects, loading and going through every MapBlock
void clearObjects(ClearObjectsMode mode);
// Call Lua functon for akk loaded or loadable MapBlock
void blocksCallback(BlocksCallbackConfig &config);
// to be called before destructor
void deactivateBlocksAndObjects();