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:
parent
a00b9cab36
commit
18c740712a
7 changed files with 212 additions and 0 deletions
|
@ -6636,6 +6636,17 @@ Environment access
|
||||||
* mode = `"quick"`: Clear objects immediately in loaded mapblocks,
|
* mode = `"quick"`: Clear objects immediately in loaded mapblocks,
|
||||||
clear objects in unloaded mapblocks only when the
|
clear objects in unloaded mapblocks only when the
|
||||||
mapblocks are next activated.
|
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])`
|
* `core.load_area(pos1[, pos2])`
|
||||||
* Load the mapblocks containing the area from `pos1` to `pos2`.
|
* Load the mapblocks containing the area from `pos1` to `pos2`.
|
||||||
`pos2` defaults to `pos1` if not specified.
|
`pos2` defaults to `pos1` if not specified.
|
||||||
|
|
|
@ -419,6 +419,40 @@ bool ScriptApiEnv::has_on_mapblocks_changed()
|
||||||
return lua_objlen(L, -1) > 0;
|
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,
|
void ScriptApiEnv::triggerABM(int id, v3s16 p, MapNode n,
|
||||||
u32 active_object_count, u32 active_object_count_wider)
|
u32 active_object_count, u32 active_object_count_wider)
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,6 +41,9 @@ public:
|
||||||
// Determines whether there are any on_mapblocks_changed callbacks
|
// Determines whether there are any on_mapblocks_changed callbacks
|
||||||
bool has_on_mapblocks_changed();
|
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
|
// Initializes environment and loads some definitions from Lua
|
||||||
void initializeEnvironment(ServerEnvironment *env);
|
void initializeEnvironment(ServerEnvironment *env);
|
||||||
|
|
||||||
|
|
|
@ -155,6 +155,19 @@ void LuaEmergeAreaCallback(v3s16 blockpos, EmergeAction action, void *param)
|
||||||
delete state;
|
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 */
|
/* Exported functions */
|
||||||
|
|
||||||
// set_node(pos, node)
|
// set_node(pos, node)
|
||||||
|
@ -1092,6 +1105,56 @@ int ModApiEnv::l_clear_objects(lua_State *L)
|
||||||
return 0;
|
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
|
// line_of_sight(pos1, pos2) -> true/false, pos
|
||||||
int ModApiEnv::l_line_of_sight(lua_State *L)
|
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_perlin_map);
|
||||||
API_FCT(get_voxel_manip);
|
API_FCT(get_voxel_manip);
|
||||||
API_FCT(clear_objects);
|
API_FCT(clear_objects);
|
||||||
|
API_FCT(blocks_callback);
|
||||||
API_FCT(spawn_tree);
|
API_FCT(spawn_tree);
|
||||||
API_FCT(find_path);
|
API_FCT(find_path);
|
||||||
API_FCT(line_of_sight);
|
API_FCT(line_of_sight);
|
||||||
|
|
|
@ -195,6 +195,9 @@ private:
|
||||||
// clear all objects in the environment
|
// clear all objects in the environment
|
||||||
static int l_clear_objects(lua_State *L);
|
static int l_clear_objects(lua_State *L);
|
||||||
|
|
||||||
|
// blocks_callback()
|
||||||
|
static int l_blocks_callback(lua_State *L);
|
||||||
|
|
||||||
// spawn_tree(pos, treedef)
|
// spawn_tree(pos, treedef)
|
||||||
static int l_spawn_tree(lua_State *L);
|
static int l_spawn_tree(lua_State *L);
|
||||||
|
|
||||||
|
|
|
@ -866,6 +866,90 @@ void ServerEnvironment::clearObjects(ClearObjectsMode mode)
|
||||||
<< " in " << num_blocks_cleared << " blocks" << std::endl;
|
<< " 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)
|
void ServerEnvironment::step(float dtime)
|
||||||
{
|
{
|
||||||
ScopeProfiler sp2(g_profiler, "ServerEnv::step()", SPT_AVG);
|
ScopeProfiler sp2(g_profiler, "ServerEnv::step()", SPT_AVG);
|
||||||
|
|
|
@ -101,6 +101,16 @@ enum ClearObjectsMode {
|
||||||
CLEAR_OBJECTS_MODE_QUICK,
|
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
|
class ServerEnvironment final : public Environment
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -242,6 +252,9 @@ public:
|
||||||
// Clear objects, loading and going through every MapBlock
|
// Clear objects, loading and going through every MapBlock
|
||||||
void clearObjects(ClearObjectsMode mode);
|
void clearObjects(ClearObjectsMode mode);
|
||||||
|
|
||||||
|
// Call Lua functon for akk loaded or loadable MapBlock
|
||||||
|
void blocksCallback(BlocksCallbackConfig &config);
|
||||||
|
|
||||||
// to be called before destructor
|
// to be called before destructor
|
||||||
void deactivateBlocksAndObjects();
|
void deactivateBlocksAndObjects();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue