]> git.lizzy.rs Git - minetest.git/commitdiff
Add callback on_mapblocks_changed
authorJude Melton-Houghton <jwmhjwmh@gmail.com>
Sun, 4 Sep 2022 02:05:07 +0000 (22:05 -0400)
committerJude Melton-Houghton <jwmhjwmh@gmail.com>
Sat, 24 Dec 2022 13:24:59 +0000 (08:24 -0500)
builtin/game/misc_s.lua
builtin/game/register.lua
builtin/profiler/instrumentation.lua
doc/lua_api.txt
games/devtest/mods/unittests/misc.lua
src/script/common/c_converter.h
src/script/cpp_api/s_env.cpp
src/script/cpp_api/s_env.h
src/serverenvironment.cpp
src/serverenvironment.h

index 67a0ec684bcc484b5bc35497ef8ac2a4b4920766..93d2bafa8d79acd455ba6768069eed87c30e5b1d 100644 (file)
@@ -8,10 +8,9 @@
 -- Misc. API functions
 --
 
+-- This must match the implementation in src/script/common/c_converter.h
 function core.hash_node_position(pos)
-       return (pos.z + 32768) * 65536 * 65536
-                + (pos.y + 32768) * 65536
-                +  pos.x + 32768
+       return (pos.z + 0x8000) * 0x100000000 + (pos.y + 0x8000) * 0x10000 + (pos.x + 0x8000)
 end
 
 
index f0a6cb49a267c05cb503714e701710a0cde6243c..cc58cad3115d02c3bc3073dea0afeb4e960caa0d 100644 (file)
@@ -633,6 +633,17 @@ core.registered_on_player_inventory_actions, core.register_on_player_inventory_a
 core.registered_allow_player_inventory_actions, core.register_allow_player_inventory_action = make_registration()
 core.registered_on_rightclickplayers, core.register_on_rightclickplayer = make_registration()
 core.registered_on_liquid_transformed, core.register_on_liquid_transformed = make_registration()
+core.registered_on_mapblocks_changed, core.register_on_mapblocks_changed = make_registration()
+
+core.register_on_mods_loaded(function()
+       core.after(0, function()
+               setmetatable(core.registered_on_mapblocks_changed, {
+                       __newindex = function()
+                               error("on_mapblocks_changed callbacks must be registered at load time")
+                       end,
+               })
+       end)
+end)
 
 --
 -- Compatibility for on_mapgen_init()
index f80314b3283166bc8fa5bc5bf4a9ddaf8ce2d8c2..80f1c66afea01f0ccc42725ad215f829156ed119 100644 (file)
@@ -43,6 +43,7 @@ local register_functions = {
        register_on_item_eat = 0,
        register_on_punchplayer = 0,
        register_on_player_hpchange = 0,
+       register_on_mapblocks_changed = 0,
 }
 
 ---
index 6f2f6c30726195046ecdc34529ac589753eae52b..b41189e9c6c999a2ce57facf8bd33ea05d78c140 100644 (file)
@@ -5372,6 +5372,16 @@ Call these functions only at load time!
     * `pos_list` is an array of all modified positions.
     * `node_list` is an array of the old node that was previously at the position
       with the corresponding index in pos_list.
+* `minetest.register_on_mapblocks_changed(function(modified_blocks, modified_block_count))`
+    * Called soon after any nodes or node metadata have been modified. No
+      modifications will be missed, but there may be false positives.
+    * Will never be called more than once per server step.
+    * `modified_blocks` is the set of modified mapblock position hashes. These
+      are in the same format as those produced by `minetest.hash_node_position`,
+      and can be converted to positions with `minetest.get_position_from_hash`.
+      The set is a table where the keys are hashes and the values are `true`.
+    * `modified_block_count` is the number of entries in the set.
+    * Note: callbacks must be registered at mod load time.
 
 Setting-related
 ---------------
index 3f327edece5ad5c5a186cd0fab6fcebe3f5eebd9..10af80585455500a098cc4b3a47ffd8ca5af08af 100644 (file)
@@ -147,3 +147,33 @@ local function test_mapgen_edges(cb)
        minetest.emerge_area(max_edge, max_edge:add(1), emerge_block, max_finished)
 end
 unittests.register("test_mapgen_edges", test_mapgen_edges, {map=true, async=true})
+
+local finish_test_on_mapblocks_changed
+minetest.register_on_mapblocks_changed(function(modified_blocks, modified_block_count)
+       if finish_test_on_mapblocks_changed then
+               finish_test_on_mapblocks_changed(modified_blocks, modified_block_count)
+               finish_test_on_mapblocks_changed = nil
+       end
+end)
+local function test_on_mapblocks_changed(cb, player, pos)
+       local bp1 = (pos / minetest.MAP_BLOCKSIZE):floor()
+       local bp2 = bp1:add(1)
+       for _, bp in ipairs({bp1, bp2}) do
+               -- Make a modification in the block.
+               local p = bp * minetest.MAP_BLOCKSIZE
+               minetest.load_area(p)
+               local meta = minetest.get_meta(p)
+               meta:set_int("test_on_mapblocks_changed", meta:get_int("test_on_mapblocks_changed") + 1)
+       end
+       finish_test_on_mapblocks_changed = function(modified_blocks, modified_block_count)
+               if modified_block_count < 2 then
+                       return cb("Expected at least two mapblocks to be recorded as modified")
+               end
+               if not modified_blocks[minetest.hash_node_position(bp1)] or
+                               not modified_blocks[minetest.hash_node_position(bp2)] then
+                       return cb("The expected mapblocks were not recorded as modified")
+               end
+               cb()
+       end
+end
+unittests.register("test_on_mapblocks_changed", test_on_mapblocks_changed, {map=true, async=true})
index 2af726d1618d7d336a4b69a41b62e19d8ea7e639..f1e4e47ec255b8e3ab48c7e7f622b2d0461a66a2 100644 (file)
@@ -121,3 +121,12 @@ void                warn_if_field_exists(lua_State *L, int table,
 
 size_t write_array_slice_float(lua_State *L, int table_index, float *data,
        v3u16 data_size, v3u16 slice_offset, v3u16 slice_size);
+
+// This must match the implementation in builtin/game/misc_s.lua
+// Note that this returns a floating point result as Lua integers are 32-bit
+inline lua_Number hash_node_position(v3s16 pos)
+{
+       return (((s64)pos.Z + 0x8000L) << 32)
+                       | (((s64)pos.Y + 0x8000L) << 16)
+                       | ((s64)pos.X + 0x8000L);
+}
index e49113405e4ae0b6bd60b2dc8334e7ed17d1a81c..3cbb13cd2235e7a09602a6bc2254d79dfd8bcedf 100644 (file)
@@ -299,3 +299,36 @@ void ScriptApiEnv::on_liquid_transformed(
 
        runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
 }
+
+void ScriptApiEnv::on_mapblocks_changed(const std::unordered_set<v3s16> &set)
+{
+       SCRIPTAPI_PRECHECKHEADER
+
+       // Get core.registered_on_mapblocks_changed
+       lua_getglobal(L, "core");
+       lua_getfield(L, -1, "registered_on_mapblocks_changed");
+       luaL_checktype(L, -1, LUA_TTABLE);
+       lua_remove(L, -2);
+
+       // Convert the set to a set of position hashes
+       lua_createtable(L, 0, set.size());
+       for(const v3s16 &p : set) {
+               lua_pushnumber(L, hash_node_position(p));
+               lua_pushboolean(L, true);
+               lua_rawset(L, -3);
+       }
+       lua_pushinteger(L, set.size());
+
+       runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
+}
+
+bool ScriptApiEnv::has_on_mapblocks_changed()
+{
+       SCRIPTAPI_PRECHECKHEADER
+
+       // Get core.registered_on_mapblocks_changed
+       lua_getglobal(L, "core");
+       lua_getfield(L, -1, "registered_on_mapblocks_changed");
+       luaL_checktype(L, -1, LUA_TTABLE);
+       return lua_objlen(L, -1) > 0;
+}
index 9a50a01cc33c6d956c4bbbe9305ae660a6b66588..bc4c4cd4d40db3f081a2dfd389839e43b36fd5f9 100644 (file)
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "cpp_api/s_base.h"
 #include "irr_v3d.h"
 #include "mapnode.h"
+#include <unordered_set>
 #include <vector>
 
 class ServerEnvironment;
@@ -48,5 +49,11 @@ class ScriptApiEnv : virtual public ScriptApiBase
        // Called after liquid transform changes
        void on_liquid_transformed(const std::vector<std::pair<v3s16, MapNode>> &list);
 
+       // Called after mapblock changes
+       void on_mapblocks_changed(const std::unordered_set<v3s16> &set);
+
+       // Determines whether there are any on_mapblocks_changed callbacks
+       bool has_on_mapblocks_changed();
+
        void initializeEnvironment(ServerEnvironment *env);
 };
index 9dd5ba621f565bd13b0126c7b2880ec1c66fe961..aa6ba2f96770d89c76b4707e27fbab97ad525c84 100644 (file)
@@ -381,6 +381,18 @@ void ActiveBlockList::update(std::vector<PlayerSAO*> &active_players,
        m_list = std::move(newlist);
 }
 
+/*
+       OnMapblocksChangedReceiver
+*/
+
+void OnMapblocksChangedReceiver::onMapEditEvent(const MapEditEvent &event)
+{
+       assert(receiving);
+       for (const v3s16 &p : event.modified_blocks) {
+               modified_blocks.insert(p);
+       }
+}
+
 /*
        ServerEnvironment
 */
@@ -476,6 +488,11 @@ void ServerEnvironment::init()
 
        m_player_database = openPlayerDatabase(player_backend_name, m_path_world, conf);
        m_auth_database = openAuthDatabase(auth_backend_name, m_path_world, conf);
+
+       if (m_map && m_script->has_on_mapblocks_changed()) {
+               m_map->addEventReceiver(&m_on_mapblocks_changed_receiver);
+               m_on_mapblocks_changed_receiver.receiving = true;
+       }
 }
 
 ServerEnvironment::~ServerEnvironment()
@@ -1570,6 +1587,14 @@ void ServerEnvironment::step(float dtime)
        // Send outdated detached inventories
        m_server->sendDetachedInventories(PEER_ID_INEXISTENT, true);
 
+       // Notify mods of modified mapblocks
+       if (m_on_mapblocks_changed_receiver.receiving &&
+                       !m_on_mapblocks_changed_receiver.modified_blocks.empty()) {
+               std::unordered_set<v3s16> modified_blocks;
+               std::swap(modified_blocks, m_on_mapblocks_changed_receiver.modified_blocks);
+               m_script->on_mapblocks_changed(modified_blocks);
+       }
+
        const auto end_time = porting::getTimeUs();
        m_step_time_counter->increment(end_time - start_time);
 }
index 5c4b23f402d6a5e26e36d35f14dcc45fba4eade8..bb40a33ce221c09bbfb5eda439fcc238fa2fab86 100644 (file)
@@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "activeobject.h"
 #include "environment.h"
-#include "mapnode.h"
+#include "map.h"
 #include "settings.h"
 #include "server/activeobjectmgr.h"
 #include "util/numeric.h"
@@ -30,9 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <random>
 
 class IGameDef;
-class ServerMap;
 struct GameParams;
-class MapBlock;
 class RemotePlayer;
 class PlayerDatabase;
 class AuthDatabase;
@@ -193,6 +191,16 @@ class ActiveBlockList
        std::set<v3s16> m_forceloaded_list;
 };
 
+/*
+       ServerEnvironment::m_on_mapblocks_changed_receiver
+*/
+struct OnMapblocksChangedReceiver : public MapEventReceiver {
+       std::unordered_set<v3s16> modified_blocks;
+       bool receiving = false;
+
+       void onMapEditEvent(const MapEditEvent &event) override;
+};
+
 /*
        Operation mode for ServerEnvironment::clearObjects()
 */
@@ -455,6 +463,8 @@ class ServerEnvironment final : public Environment
        Server *m_server;
        // Active Object Manager
        server::ActiveObjectMgr m_ao_manager;
+       // on_mapblocks_changed map event receiver
+       OnMapblocksChangedReceiver m_on_mapblocks_changed_receiver;
        // World path
        const std::string m_path_world;
        // Outgoing network message buffer for active objects