From 51531e26214f927757c293e75061da7664d28d08 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 14 Sep 2025 22:21:32 +0300 Subject: [PATCH 1/4] minor refactor --- src/voxels/Chunks.cpp | 4 +- src/voxels/Chunks.hpp | 2 +- src/voxels/blocks_agent.cpp | 144 ++++++++++++++++++++++-------------- src/voxels/blocks_agent.hpp | 2 +- 4 files changed, 93 insertions(+), 59 deletions(-) diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index b9650b99..dcdee71e 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -171,10 +171,10 @@ void Chunks::eraseSegments( blocks_agent::erase_segments(*this, def, state, x, y, z); } -void Chunks::repairSegments( +void Chunks::restoreSegments( const Block& def, blockstate state, int x, int y, int z ) { - blocks_agent::repair_segments(*this, def, state, x, y, z); + blocks_agent::restore_segments(*this, def, state, x, y, z); } bool Chunks::checkReplaceability( diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index 2b2183f5..214e47c6 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -28,7 +28,7 @@ class Chunks { const ContentIndices& indices; void eraseSegments(const Block& def, blockstate state, int x, int y, int z); - void repairSegments( + void restoreSegments( const Block& def, blockstate state, int x, int y, int z ); void setRotationExtended( diff --git a/src/voxels/blocks_agent.cpp b/src/voxels/blocks_agent.cpp index d56b1838..c41899d4 100644 --- a/src/voxels/blocks_agent.cpp +++ b/src/voxels/blocks_agent.cpp @@ -7,62 +7,10 @@ using namespace blocks_agent; template -static inline bool set_block( - Storage& chunks, - int32_t x, - int32_t y, - int32_t z, - uint32_t id, - blockstate state +static void mark_neighboirs_modified( + Storage& chunks, int32_t cx, int32_t cz, int32_t lx, int32_t lz ) { - if (y < 0 || y >= CHUNK_H) { - return false; - } - const auto& indices = chunks.getContentIndices(); - int cx = floordiv(x); - int cz = floordiv(z); - Chunk* chunk = get_chunk(chunks, cx, cz); - if (chunk == nullptr) { - return false; - } - int lx = x - cx * CHUNK_W; - int lz = z - cz * CHUNK_D; - size_t index = vox_index(lx, y, lz); - - // block finalization - voxel& vox = chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; - const auto& prevdef = indices.blocks.require(vox.id); - if (prevdef.inventorySize != 0) { - chunk->removeBlockInventory(lx, y, lz); - } - if (prevdef.rt.extended && !vox.state.segment) { - erase_segments(chunks, prevdef, vox.state, x, y, z); - } - if (prevdef.dataStruct) { - if (auto found = chunk->blocksMetadata.find(index)) { - chunk->blocksMetadata.free(found); - chunk->flags.unsaved = true; - chunk->flags.blocksData = true; - } - } - - // block initialization - const auto& newdef = indices.blocks.require(id); - vox.id = id; - vox.state = state; - chunk->setModifiedAndUnsaved(); - if (!state.segment && newdef.rt.extended) { - repair_segments(chunks, newdef, state, x, y, z); - } - - if (y < chunk->bottom) - chunk->bottom = y; - else if (y + 1 > chunk->top) - chunk->top = y + 1; - else if (id == 0) - chunk->flags.dirtyHeights = true; - - + Chunk* chunk; if (lx == 0 && (chunk = get_chunk(chunks, cx - 1, cz))) { chunk->flags.modified = true; } @@ -75,6 +23,92 @@ static inline bool set_block( if (lz == CHUNK_D - 1 && (chunk = get_chunk(chunks, cx, cz + 1))) { chunk->flags.modified = true; } +} + +static void refresh_chunk_heights(Chunk& chunk, bool isAir, int y) { + if (y < chunk.bottom) + chunk.bottom = y; + else if (y + 1 > chunk.top) + chunk.top = y + 1; + else if (isAir) + chunk.flags.dirtyHeights = true; +} + +template +static void finalize_block( + Storage& chunks, + Chunk& chunk, + voxel& vox, + int32_t x, int32_t y, int32_t z, + int32_t lx, int32_t lz +) { + size_t index = vox_index(lx, y, lz); + const auto& indices = chunks.getContentIndices(); + const auto& def = indices.blocks.require(vox.id); + if (def.inventorySize != 0) { + chunk.removeBlockInventory(lx, y, lz); + } + if (def.rt.extended && !vox.state.segment) { + erase_segments(chunks, def, vox.state, x, y, z); + } + if (def.dataStruct) { + if (auto found = chunk.blocksMetadata.find(index)) { + chunk.blocksMetadata.free(found); + chunk.flags.unsaved = true; + chunk.flags.blocksData = true; + } + } +} + +template +static void initialize_block( + Storage& chunks, + Chunk& chunk, + voxel& vox, + blockid_t id, + blockstate state, + int32_t x, int32_t y, int32_t z, + int32_t lx, int32_t lz, + int32_t cx, int32_t cz +) { + const auto& indices = chunks.getContentIndices(); + const auto& def = indices.blocks.require(id); + vox.id = id; + vox.state = state; + chunk.setModifiedAndUnsaved(); + if (!state.segment && def.rt.extended) { + restore_segments(chunks, def, state, x, y, z); + } + + refresh_chunk_heights(chunk, id == BLOCK_AIR, y); + mark_neighboirs_modified(chunks, cx, cz, lx, lz); +} + +template +static inline bool set_block( + Storage& chunks, + int32_t x, + int32_t y, + int32_t z, + blockid_t id, + blockstate state +) { + if (y < 0 || y >= CHUNK_H) { + return false; + } + int cx = floordiv(x); + int cz = floordiv(z); + Chunk* chunk = get_chunk(chunks, cx, cz); + if (chunk == nullptr) { + return false; + } + int lx = x - cx * CHUNK_W; + int lz = z - cz * CHUNK_D; + + voxel& vox = chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; + + finalize_block(chunks, *chunk, vox, x, y, z, lx, lz); + initialize_block(chunks, *chunk, vox, id, state, x, y, z, lx, lz, cx, cz); return true; } diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index efecf4e3..40b5687b 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -191,7 +191,7 @@ static constexpr inline uint8_t segment_to_int(int sx, int sy, int sz) { /// @param y origin position Y /// @param z origin position Z template -inline void repair_segments( +inline void restore_segments( Storage& chunks, const Block& def, blockstate state, int x, int y, int z ) { const auto& rotation = def.rotations.variants[state.rotation]; From bb35fc665fb697f9cfbaae1b2e36e7825e4dfd16 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 14 Sep 2025 22:26:22 +0300 Subject: [PATCH 2/4] fix lua::getfield and events registering --- src/logic/scripting/lua/lua_util.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index 54b6f678..121ebe29 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -455,7 +455,7 @@ namespace lua { inline bool getfield(lua::State* L, const std::string& name, int idx = -1) { lua_getfield(L, idx, name.c_str()); - if (isnil(L, idx)) { + if (isnoneornil(L, -1)) { pop(L); return false; } From b863d47d7f993e98895894ee143be94167ceb8ff Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 14 Sep 2025 23:57:45 +0300 Subject: [PATCH 3/4] add 'on_block_tick' event --- res/scripts/classes.lua | 67 +++++++++++++++++++++++ res/scripts/post_content.lua | 2 +- res/scripts/stdlib.lua | 2 + src/logic/scripting/lua/libs/libblock.cpp | 20 +++++++ src/logic/scripting/scripting.cpp | 5 ++ src/voxels/Block.hpp | 1 + src/voxels/blocks_agent.cpp | 54 ++++++++++++++++++ src/voxels/blocks_agent.hpp | 15 +++++ 8 files changed, 165 insertions(+), 1 deletion(-) diff --git a/res/scripts/classes.lua b/res/scripts/classes.lua index c50443f8..b6de85d2 100644 --- a/res/scripts/classes.lua +++ b/res/scripts/classes.lua @@ -161,6 +161,73 @@ local function clean(iterable, checkFun, ...) end end +local updating_blocks = {} +local TYPE_REGISTER = 0 +local TYPE_UNREGISTER = 1 + +block.__perform_ticks = function(delta) + for id, entry in pairs(updating_blocks) do + entry.timer = entry.timer + delta + local steps = math.floor(entry.timer / entry.delta * #entry / 3) + if steps == 0 then + goto continue + end + entry.timer = 0.0 + local event = entry.event + for i=1, steps do + local x = entry[entry.pointer + 1] + local y = entry[entry.pointer + 2] + local z = entry[entry.pointer + 3] + entry.pointer = (entry.pointer + 3) % #entry + events.emit(event, x, y, z) + end + ::continue:: + end +end + +block.__process_register_events = function() + local register_events = block.__pull_register_events() + if not register_events then + return + end + for i=1, #register_events, 4 do + local header = register_events[i] + local type = bit.band(header, 0xFFFF) + local id = bit.rshift(header, 16) + local x = register_events[i + 1] + local y = register_events[i + 2] + local z = register_events[i + 3] + + local list = updating_blocks[id] + if type == TYPE_REGISTER then + if not list then + list = {} + list.event = block.name(id) .. ".blocktick" + list.delta = 1.0 / (20.0 / (block.properties[id]["tick-interval"] or 1)) + list.timer = 0.0 + list.pointer = 0 + updating_blocks[id] = list + end + table.insert(list, x) + table.insert(list, y) + table.insert(list, z) + elseif type == TYPE_UNREGISTER then + if list then + for j=1, #list, 3 do + if list[j] == x and list[j + 1] == y and list[j + 2] == z then + for k=1,3 do + table.remove(list, j) + end + j = j - 3 + end + end + end + end + + print(type, id, x, y, z) + end +end + network.__process_events = function() local CLIENT_CONNECTED = 1 local CONNECTED_TO_SERVER = 2 diff --git a/res/scripts/post_content.lua b/res/scripts/post_content.lua index c8fffc6e..1b69f42f 100644 --- a/res/scripts/post_content.lua +++ b/res/scripts/post_content.lua @@ -6,7 +6,7 @@ local names = { "shadeless", "ambient-occlusion", "breakable", "selectable", "grounded", "hidden", "draw-group", "picking-item", "surface-replacement", "script-name", "ui-layout", "inventory-size", "tick-interval", "overlay-texture", - "translucent", "fields", "particles", "icon-type", "icon", "placing-block", + "translucent", "fields", "particles", "icon-type", "icon", "placing-block", "stack-size", "name", "script-file", "culling" } for name, _ in pairs(user_props) do diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index b07d9387..b08e8c02 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -588,6 +588,8 @@ function __process_post_runnables() end network.__process_events() + block.__process_register_events() + block.__perform_ticks(time.delta()) end function time.post_runnable(runnable) diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index fd0496fe..29f7747c 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -707,6 +707,25 @@ static int l_has_tag(lua::State* L) { return 0; } +static int l_pull_register_events(lua::State* L) { + auto events = blocks_agent::pull_register_events(); + if (events.empty()) + return 0; + + lua::createtable(L, events.size() * 4, 0); + for (int i = 0; i < events.size(); i++) { + const auto& event = events[i]; + lua::pushinteger(L, static_cast(event.type) | event.id << 16); + lua::rawseti(L, i * 4 + 1); + + for (int j = 0; j < 3; j++) { + lua::pushinteger(L, event.coord[j]); + lua::rawseti(L, i * 4 + j + 2); + } + } + return 1; +} + const luaL_Reg blocklib[] = { {"index", lua::wrap}, {"name", lua::wrap}, @@ -747,5 +766,6 @@ const luaL_Reg blocklib[] = { {"set_field", lua::wrap}, {"reload_script", lua::wrap}, {"has_tag", lua::wrap}, + {"__pull_register_events", lua::wrap}, {NULL, NULL} }; diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index b13439d9..57285945 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -24,6 +24,7 @@ #include "util/timeutil.hpp" #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" +#include "voxels/blocks_agent.hpp" #include "world/Level.hpp" #include "world/World.hpp" #include "interfaces/Process.hpp" @@ -464,6 +465,7 @@ void scripting::on_chunk_present(const Chunk& chunk, bool loaded) { ); } } + blocks_agent::on_chunk_present(*content->getIndices(), chunk); } void scripting::on_chunk_remove(const Chunk& chunk) { @@ -478,6 +480,7 @@ void scripting::on_chunk_remove(const Chunk& chunk) { ); } } + blocks_agent::on_chunk_remove(*content->getIndices(), chunk); } void scripting::on_inventory_open(const Player* player, const Inventory& inventory) { @@ -648,6 +651,8 @@ void scripting::load_content_script( register_event(env, "on_replaced", prefix + ".replaced"); funcsset.oninteract = register_event(env, "on_interact", prefix + ".interact"); + funcsset.onblocktick = + register_event(env, "on_block_tick", prefix + ".blocktick"); funcsset.onblockstick = register_event(env, "on_blocks_tick", prefix + ".blockstick"); } diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index 707f6b8b..3dbd1cf0 100644 --- a/src/voxels/Block.hpp +++ b/src/voxels/Block.hpp @@ -48,6 +48,7 @@ struct BlockFuncsSet { bool onreplaced : 1; bool oninteract : 1; bool randupdate : 1; + bool onblocktick : 1; bool onblockstick : 1; }; diff --git a/src/voxels/blocks_agent.cpp b/src/voxels/blocks_agent.cpp index c41899d4..d64f7727 100644 --- a/src/voxels/blocks_agent.cpp +++ b/src/voxels/blocks_agent.cpp @@ -6,6 +6,49 @@ using namespace blocks_agent; +static std::vector block_register_events {}; + +std::vector blocks_agent::pull_register_events() { + auto events = block_register_events; + block_register_events.clear(); + return events; +} + +static void on_chunk_register_event( + const ContentIndices& indices, + const Chunk& chunk, + BlockRegisterEvent::Type type +) { + for (int i = 0; i < CHUNK_VOL; i++) { + const auto& def = + indices.blocks.require(chunk.voxels[i].id); + if (def.rt.funcsset.onblocktick) { + int x = i % CHUNK_W + chunk.x * CHUNK_W; + int z = (i / CHUNK_W) % CHUNK_D + chunk.z * CHUNK_D; + int y = (i / CHUNK_W / CHUNK_D); + block_register_events.push_back(BlockRegisterEvent { + type, def.rt.id, {x, y, z} + }); + } + } +} + +void blocks_agent::on_chunk_present( + const ContentIndices& indices, const Chunk& chunk +) { + on_chunk_register_event( + indices, chunk, BlockRegisterEvent::Type::REGISTER_UPDATING + ); +} + +void blocks_agent::on_chunk_remove( + const ContentIndices& indices, const Chunk& chunk +) { + on_chunk_register_event( + indices, chunk, BlockRegisterEvent::Type::UNREGISTER_UPDATING + ); +} + template static void mark_neighboirs_modified( Storage& chunks, int32_t cx, int32_t cz, int32_t lx, int32_t lz @@ -58,6 +101,11 @@ static void finalize_block( chunk.flags.blocksData = true; } } + if (def.rt.funcsset.onblocktick) { + block_register_events.push_back(BlockRegisterEvent { + BlockRegisterEvent::Type::UNREGISTER_UPDATING, def.rt.id, {x, y, z} + }); + } } template @@ -82,6 +130,12 @@ static void initialize_block( refresh_chunk_heights(chunk, id == BLOCK_AIR, y); mark_neighboirs_modified(chunks, cx, cz, lx, lz); + + if (def.rt.funcsset.onblocktick) { + block_register_events.push_back(BlockRegisterEvent { + BlockRegisterEvent::Type::REGISTER_UPDATING, def.rt.id, {x, y, z} + }); + } } template diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index 40b5687b..ab798133 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -24,6 +24,21 @@ struct AABB; namespace blocks_agent { +struct BlockRegisterEvent { + enum class Type : uint16_t { + REGISTER_UPDATING, + UNREGISTER_UPDATING, + }; + Type type; + blockid_t id; + glm::ivec3 coord; +}; + +std::vector pull_register_events(); + +void on_chunk_present(const ContentIndices& indices, const Chunk& chunk); +void on_chunk_remove(const ContentIndices& indices, const Chunk& chunk); + /// @brief Get specified chunk. /// @tparam Storage /// @param chunks From a1177f2601a69849dbbc15263960cb81374513fa Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 15 Sep 2025 00:31:38 +0300 Subject: [PATCH 4/4] update doc/*/scripting/events.md & add 'tps' argument --- doc/en/scripting/events.md | 7 +++++++ doc/ru/scripting/events.md | 7 +++++++ res/scripts/classes.lua | 6 ++++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/doc/en/scripting/events.md b/doc/en/scripting/events.md index 21e1cee5..712dcb8c 100644 --- a/doc/en/scripting/events.md +++ b/doc/en/scripting/events.md @@ -46,6 +46,13 @@ function on_blocks_tick(tps: int) Called tps (20) times per second. Use 1/tps instead of `time.delta()`. +```lua +function on_block_tick(x, y, z, tps: number) +``` + +Called tps (20 / tick-interval) times per second for a block. +Use 1/tps instead of `time.delta()`. + ```lua function on_player_tick(playerid: int, tps: int) ``` diff --git a/doc/ru/scripting/events.md b/doc/ru/scripting/events.md index c7781879..4ca3fda5 100644 --- a/doc/ru/scripting/events.md +++ b/doc/ru/scripting/events.md @@ -46,6 +46,13 @@ function on_blocks_tick(tps: int) Вызывается tps (20) раз в секунду. Используйте 1/tps вместо `time.delta()`. +```lua +function on_block_tick(x, y, z, tps: number) +``` + +Вызывается tps (20 / tick-interval) раз в секунду для конкретного блока. +Используйте 1/tps вместо `time.delta()`. + ```lua function on_player_tick(playerid: int, tps: int) ``` diff --git a/res/scripts/classes.lua b/res/scripts/classes.lua index b6de85d2..5454fbfb 100644 --- a/res/scripts/classes.lua +++ b/res/scripts/classes.lua @@ -174,12 +174,13 @@ block.__perform_ticks = function(delta) end entry.timer = 0.0 local event = entry.event + local tps = entry.tps for i=1, steps do local x = entry[entry.pointer + 1] local y = entry[entry.pointer + 2] local z = entry[entry.pointer + 3] entry.pointer = (entry.pointer + 3) % #entry - events.emit(event, x, y, z) + events.emit(event, x, y, z, tps) end ::continue:: end @@ -203,7 +204,8 @@ block.__process_register_events = function() if not list then list = {} list.event = block.name(id) .. ".blocktick" - list.delta = 1.0 / (20.0 / (block.properties[id]["tick-interval"] or 1)) + list.tps = 20 / (block.properties[id]["tick-interval"] or 1) + list.delta = 1.0 / list.tps list.timer = 0.0 list.pointer = 0 updating_blocks[id] = list