From 473f9f1a6a36d65d6ab7d0ce66400e73d198bdfa Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 17 Nov 2025 21:33:44 +0300 Subject: [PATCH 01/15] move block ticks register to separate script --- res/scripts/classes.lua | 67 ----------------------- res/scripts/internal_events.lua | 66 ++++++++++++++++++++++ src/logic/scripting/lua/libs/libblock.cpp | 2 +- src/logic/scripting/scripting.cpp | 1 + src/voxels/blocks_agent.cpp | 56 ++++++++++++------- src/voxels/blocks_agent.hpp | 9 ++- 6 files changed, 109 insertions(+), 92 deletions(-) create mode 100644 res/scripts/internal_events.lua diff --git a/res/scripts/classes.lua b/res/scripts/classes.lua index 950544bf..9076808c 100644 --- a/res/scripts/classes.lua +++ b/res/scripts/classes.lua @@ -171,73 +171,6 @@ 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 - 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, tps) - 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.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 - 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 - end -end - network.__process_events = function() local CLIENT_CONNECTED = 1 local CONNECTED_TO_SERVER = 2 diff --git a/res/scripts/internal_events.lua b/res/scripts/internal_events.lua new file mode 100644 index 00000000..06666d04 --- /dev/null +++ b/res/scripts/internal_events.lua @@ -0,0 +1,66 @@ +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 + 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, tps) + 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 event_bits = 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 bit.band(event_bits, TYPE_REGISTER) ~= 0 then + if not list then + list = {} + list.event = block.name(id) .. ".blocktick" + 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 + end + table.insert(list, x) + table.insert(list, y) + table.insert(list, z) + elseif bit.band(event_bits, TYPE_UNREGISTER) ~= 0 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 + end +end diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index 8ac548e5..fa525eb0 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -730,7 +730,7 @@ static int l_pull_register_events(lua::State* L) { 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::pushinteger(L, static_cast(event.bits) | event.id << 16); lua::rawseti(L, i * 4 + 1); for (int j = 0; j < 3; j++) { diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 19accc89..07b35d1e 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -72,6 +72,7 @@ void scripting::initialize(Engine* engine) { load_script(io::path("stdlib.lua"), true); load_script(io::path("classes.lua"), true); + load_script(io::path("internal_events.lua"), true); } class LuaCoroutine : public Process { diff --git a/src/voxels/blocks_agent.cpp b/src/voxels/blocks_agent.cpp index d64f7727..e4e17e27 100644 --- a/src/voxels/blocks_agent.cpp +++ b/src/voxels/blocks_agent.cpp @@ -14,39 +14,46 @@ std::vector blocks_agent::pull_register_events() { return events; } +static uint16_t get_events_bits(bool present, const Block& def) { + uint16_t bits = 0; + if (def.rt.funcsset.onblocktick) { + bits |= present ? BlockRegisterEvent::REGISTER_UPDATING_BIT + : BlockRegisterEvent::UNREGISTER_UPDATING_BIT; + } + return bits; +} + static void on_chunk_register_event( const ContentIndices& indices, const Chunk& chunk, - BlockRegisterEvent::Type type + bool present ) { 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} - }); + uint16_t bits = get_events_bits(present, def); + if (bits == 0) { + continue; } + 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 { + bits, 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 - ); + on_chunk_register_event(indices, chunk, true); } void blocks_agent::on_chunk_remove( const ContentIndices& indices, const Chunk& chunk ) { - on_chunk_register_event( - indices, chunk, BlockRegisterEvent::Type::UNREGISTER_UPDATING - ); + on_chunk_register_event(indices, chunk, false); } template @@ -101,11 +108,14 @@ 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} - }); + + uint16_t bits = get_events_bits(false, def); + if (bits == 0) { + return; } + block_register_events.push_back(BlockRegisterEvent { + bits, def.rt.id, {x, y, z} + }); } template @@ -131,9 +141,17 @@ static void initialize_block( refresh_chunk_heights(chunk, id == BLOCK_AIR, y); mark_neighboirs_modified(chunks, cx, cz, lx, lz); + uint16_t bits = get_events_bits(true, def); + if (bits == 0) { + return; + } + block_register_events.push_back(BlockRegisterEvent { + bits, def.rt.id, {x, y, z} + }); + if (def.rt.funcsset.onblocktick) { block_register_events.push_back(BlockRegisterEvent { - BlockRegisterEvent::Type::REGISTER_UPDATING, def.rt.id, {x, y, z} + bits, def.rt.id, {x, y, z} }); } } diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index ab798133..b5ca82a6 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -25,11 +25,10 @@ struct AABB; namespace blocks_agent { struct BlockRegisterEvent { - enum class Type : uint16_t { - REGISTER_UPDATING, - UNREGISTER_UPDATING, - }; - Type type; + static inline constexpr uint16_t REGISTER_UPDATING_BIT = 0x1; + static inline constexpr uint16_t UNREGISTER_UPDATING_BIT = 0x2; + static inline constexpr uint16_t PRESENT_EVENT_BIT = 0x4; + uint16_t bits; blockid_t id; glm::ivec3 coord; }; From 1c81f8b7ad6901f2bbc1ef37af990290f2ddc905 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 17 Nov 2025 22:14:34 +0300 Subject: [PATCH 02/15] fix warning --- src/frontend/debug_panel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index c42b2930..32439b65 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -87,7 +87,7 @@ std::shared_ptr create_debug_panel( fpsMax = fps; }); - panel->listenInterval(1.0f, [&engine, &gui]() { + panel->listenInterval(1.0f, [&engine]() { const auto& network = engine.getNetwork(); size_t totalDownload = network.getTotalDownload(); size_t totalUpload = network.getTotalUpload(); From ca3bc45d0f70abe4baf1e9187a08fe053b26008c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 17 Nov 2025 23:50:52 +0300 Subject: [PATCH 03/15] add 'on_block_present' event --- res/scripts/internal_events.lua | 105 ++++++++++++++++++++++++------ src/logic/ChunksController.cpp | 4 +- src/logic/scripting/scripting.cpp | 2 + src/voxels/Block.hpp | 1 + src/voxels/GlobalChunks.cpp | 2 - src/voxels/blocks_agent.cpp | 32 +++++---- src/voxels/blocks_agent.hpp | 8 +-- 7 files changed, 113 insertions(+), 41 deletions(-) diff --git a/res/scripts/internal_events.lua b/res/scripts/internal_events.lua index 06666d04..4f72e1b1 100644 --- a/res/scripts/internal_events.lua +++ b/res/scripts/internal_events.lua @@ -1,6 +1,8 @@ local updating_blocks = {} -local TYPE_REGISTER = 0 -local TYPE_UNREGISTER = 1 +local present_queues = {} +local TYPE_REGISTER = 1 +local TYPE_UPDATING = 2 +local TYPE_PRESENT = 4 block.__perform_ticks = function(delta) for id, entry in pairs(updating_blocks) do @@ -21,10 +23,45 @@ block.__perform_ticks = function(delta) end ::continue:: end + for id, queue in pairs(present_queues) do + queue.timer = queue.timer + delta + local steps = math.floor(queue.timer / queue.delta * #queue / 4) + if steps == 0 then + goto continue + end + queue.timer = 0.0 + local event = queue.event + local update_list = updating_blocks[id] + for i=1, steps do + local index = #queue - 3 + if index <= 0 then + break + end + local is_register = queue[index] + local x = queue[index + 1] + local y = queue[index + 2] + local z = queue[index + 3] + + for j=1,4 do + table.remove(queue, index) + end + events.emit(event, x, y, z) + + if queue.updating then + table.insert(update_list, x) + table.insert(update_list, y) + table.insert(update_list, z) + end + end + ::continue:: + end end +local block_pull_register_events = block.__pull_register_events +block.__pull_register_events = nil + block.__process_register_events = function() - local register_events = block.__pull_register_events() + local register_events = block_pull_register_events() if not register_events then return end @@ -36,31 +73,59 @@ block.__process_register_events = function() local y = register_events[i + 2] local z = register_events[i + 3] + local is_register = bit.band(event_bits, TYPE_REGISTER) ~= 0 + local is_updating = bit.band(event_bits, TYPE_UPDATING) ~= 0 + local is_present = bit.band(event_bits, TYPE_PRESENT) ~= 0 local list = updating_blocks[id] - if bit.band(event_bits, TYPE_REGISTER) ~= 0 then - if not list then - list = {} - list.event = block.name(id) .. ".blocktick" - 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 + + if not list and is_register and is_updating then + list = {} + list.event = block.name(id) .. ".blocktick" + 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 + end + + if is_register and is_present then + local present_queue = present_queues[id] + if not present_queue then + present_queue = {} + present_queue.event = block.name(id) .. ".blockpresent" + present_queue.tps = 20 / (block.properties[id]["tick-interval"] or 1) + present_queue.delta = 1.0 / present_queue.tps + present_queue.timer = 0.0 + present_queue.pointer = 0 + present_queue.updating = is_updating + present_queues[id] = present_queue end + table.insert(present_queue, is_register) + table.insert(present_queue, x) + table.insert(present_queue, y) + table.insert(present_queue, z) + goto continue + end + if not is_updating then + goto continue + end + if is_register then table.insert(list, x) table.insert(list, y) table.insert(list, z) - elseif bit.band(event_bits, TYPE_UNREGISTER) ~= 0 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 + else + if not list then + goto continue + end + 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 + ::continue:: end end diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index 97bbdf99..b521b0d8 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -15,6 +15,7 @@ #include "voxels/Chunks.hpp" #include "voxels/GlobalChunks.hpp" #include "world/Level.hpp" +#include "world/LevelEvents.hpp" #include "world/World.hpp" #include "world/generator/WorldGenerator.hpp" @@ -172,13 +173,12 @@ void ChunksController::createChunk(const Player& player, int x, int z) const { auto chunk = level.chunks->create(x, z); player.chunks->putChunk(chunk); auto& chunkFlags = chunk->flags; - if (!chunkFlags.loaded) { generator->generate(chunk->voxels, x, z); chunkFlags.unsaved = true; } chunk->updateHeights(); - + level.events->trigger(LevelEventType::CHUNK_PRESENT, chunk.get()); if (!chunkFlags.loadedLights) { Lighting::prebuildSkyLight(*chunk, *level.content.getIndices()); } diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 07b35d1e..fb2e997b 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -675,6 +675,8 @@ void scripting::load_content_script( register_event(env, "on_block_tick", prefix + ".blocktick"); funcsset.onblockstick = register_event(env, "on_blocks_tick", prefix + ".blockstick"); + funcsset.onblockpresent = + register_event(env, "on_block_present", prefix + ".blockpresent"); } void scripting::load_content_script( diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index 59115e84..9d091893 100644 --- a/src/voxels/Block.hpp +++ b/src/voxels/Block.hpp @@ -50,6 +50,7 @@ struct BlockFuncsSet { bool randupdate : 1; bool onblocktick : 1; bool onblockstick : 1; + bool onblockpresent : 1; }; struct CoordSystem { diff --git a/src/voxels/GlobalChunks.cpp b/src/voxels/GlobalChunks.cpp index 7451a563..66da7792 100644 --- a/src/voxels/GlobalChunks.cpp +++ b/src/voxels/GlobalChunks.cpp @@ -127,8 +127,6 @@ std::shared_ptr GlobalChunks::create(int x, int z) { chunk->flags.loadedLights = true; } chunk->blocksMetadata = regions.getBlocksData(chunk->x, chunk->z); - - level.events->trigger(LevelEventType::CHUNK_PRESENT, chunk.get()); return chunk; } diff --git a/src/voxels/blocks_agent.cpp b/src/voxels/blocks_agent.cpp index e4e17e27..fa71aad9 100644 --- a/src/voxels/blocks_agent.cpp +++ b/src/voxels/blocks_agent.cpp @@ -14,12 +14,12 @@ std::vector blocks_agent::pull_register_events() { return events; } -static uint16_t get_events_bits(bool present, const Block& def) { - uint16_t bits = 0; - if (def.rt.funcsset.onblocktick) { - bits |= present ? BlockRegisterEvent::REGISTER_UPDATING_BIT - : BlockRegisterEvent::UNREGISTER_UPDATING_BIT; - } +static uint8_t get_events_bits(bool reg, const Block& def) { + uint8_t bits = 0; + auto funcsset = def.rt.funcsset; + bits |= BlockRegisterEvent::REGISTER_BIT * reg; + bits |= BlockRegisterEvent::UPDATING_BIT * funcsset.onblocktick; + bits |= BlockRegisterEvent::PRESENT_EVENT_BIT * funcsset.onblockpresent; return bits; } @@ -28,10 +28,16 @@ static void on_chunk_register_event( const Chunk& chunk, bool present ) { - for (int i = 0; i < CHUNK_VOL; i++) { - const auto& def = - indices.blocks.require(chunk.voxels[i].id); - uint16_t bits = get_events_bits(present, def); + const auto& voxels = chunk.voxels; + + int totalBegin = chunk.bottom * (CHUNK_W * CHUNK_D); + int totalEnd = chunk.top * (CHUNK_W * CHUNK_D); + + for (int i = totalBegin; i <= totalEnd; i++) { + blockid_t id = voxels[i].id; + const auto& def = + indices.blocks.require(id); + uint8_t bits = get_events_bits(present, def); if (bits == 0) { continue; } @@ -39,7 +45,7 @@ static void on_chunk_register_event( int z = (i / CHUNK_W) % CHUNK_D + chunk.z * CHUNK_D; int y = (i / CHUNK_W / CHUNK_D); block_register_events.push_back(BlockRegisterEvent { - bits, def.rt.id, {x, y, z} + bits, id, {x, y, z} }); } } @@ -109,7 +115,7 @@ static void finalize_block( } } - uint16_t bits = get_events_bits(false, def); + uint8_t bits = get_events_bits(false, def); if (bits == 0) { return; } @@ -141,7 +147,7 @@ static void initialize_block( refresh_chunk_heights(chunk, id == BLOCK_AIR, y); mark_neighboirs_modified(chunks, cx, cz, lx, lz); - uint16_t bits = get_events_bits(true, def); + uint8_t bits = get_events_bits(true, def); if (bits == 0) { return; } diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index b5ca82a6..93a3c1ad 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -25,10 +25,10 @@ struct AABB; namespace blocks_agent { struct BlockRegisterEvent { - static inline constexpr uint16_t REGISTER_UPDATING_BIT = 0x1; - static inline constexpr uint16_t UNREGISTER_UPDATING_BIT = 0x2; - static inline constexpr uint16_t PRESENT_EVENT_BIT = 0x4; - uint16_t bits; + static inline constexpr uint8_t REGISTER_BIT = 0x1; + static inline constexpr uint8_t UPDATING_BIT = 0x2; + static inline constexpr uint8_t PRESENT_EVENT_BIT = 0x4; + uint8_t bits; blockid_t id; glm::ivec3 coord; }; From 957f9f59983790583fb57c9e9a3661631f380153 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 18 Nov 2025 00:14:53 +0300 Subject: [PATCH 04/15] fix major chunks loading performance issue --- src/voxels/blocks_agent.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/voxels/blocks_agent.cpp b/src/voxels/blocks_agent.cpp index fa71aad9..338c3cb8 100644 --- a/src/voxels/blocks_agent.cpp +++ b/src/voxels/blocks_agent.cpp @@ -14,10 +14,9 @@ std::vector blocks_agent::pull_register_events() { return events; } -static uint8_t get_events_bits(bool reg, const Block& def) { +static uint8_t get_events_bits(const Block& def) { uint8_t bits = 0; auto funcsset = def.rt.funcsset; - bits |= BlockRegisterEvent::REGISTER_BIT * reg; bits |= BlockRegisterEvent::UPDATING_BIT * funcsset.onblocktick; bits |= BlockRegisterEvent::PRESENT_EVENT_BIT * funcsset.onblockpresent; return bits; @@ -33,11 +32,17 @@ static void on_chunk_register_event( int totalBegin = chunk.bottom * (CHUNK_W * CHUNK_D); int totalEnd = chunk.top * (CHUNK_W * CHUNK_D); + uint8_t flagsCache[1024] {}; + for (int i = totalBegin; i <= totalEnd; i++) { blockid_t id = voxels[i].id; - const auto& def = - indices.blocks.require(id); - uint8_t bits = get_events_bits(present, def); + uint8_t bits = id < sizeof(flagsCache) ? flagsCache[id] : 0; + if ((bits & 0x80) == 0) { + const auto& def = indices.blocks.require(id); + bits = get_events_bits(def); + flagsCache[id] = bits | 0x80; + } + bits &= 0x7F; if (bits == 0) { continue; } @@ -45,7 +50,7 @@ static void on_chunk_register_event( int z = (i / CHUNK_W) % CHUNK_D + chunk.z * CHUNK_D; int y = (i / CHUNK_W / CHUNK_D); block_register_events.push_back(BlockRegisterEvent { - bits, id, {x, y, z} + static_cast(bits | (present ? 1 : 0)), id, {x, y, z} }); } } @@ -115,7 +120,7 @@ static void finalize_block( } } - uint8_t bits = get_events_bits(false, def); + uint8_t bits = get_events_bits(def); if (bits == 0) { return; } @@ -147,12 +152,12 @@ static void initialize_block( refresh_chunk_heights(chunk, id == BLOCK_AIR, y); mark_neighboirs_modified(chunks, cx, cz, lx, lz); - uint8_t bits = get_events_bits(true, def); + uint8_t bits = get_events_bits(def); if (bits == 0) { return; } block_register_events.push_back(BlockRegisterEvent { - bits, def.rt.id, {x, y, z} + static_cast(bits | 1), def.rt.id, {x, y, z} }); if (def.rt.funcsset.onblocktick) { From 922a86d2b0b4a5f077d3b99010d9162c8aea44d9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 18 Nov 2025 18:41:21 +0300 Subject: [PATCH 05/15] add that thing --- src/logic/ChunksController.cpp | 4 ++++ src/voxels/GlobalChunks.cpp | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index b521b0d8..692d8705 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -163,7 +163,11 @@ bool ChunksController::buildLights( return false; } +#include "util/timeutil.hpp" + void ChunksController::createChunk(const Player& player, int x, int z) const { + timeutil::ScopeLogTimer log(111); + if (!player.isLoadingChunks()) { if (auto chunk = level.chunks->fetch(x, z)) { player.chunks->putChunk(chunk); diff --git a/src/voxels/GlobalChunks.cpp b/src/voxels/GlobalChunks.cpp index 66da7792..830e13d6 100644 --- a/src/voxels/GlobalChunks.cpp +++ b/src/voxels/GlobalChunks.cpp @@ -89,13 +89,19 @@ static inline auto load_inventories( return invs; } +#include "util/timeutil.hpp" + std::shared_ptr GlobalChunks::create(int x, int z) { const auto& found = chunksMap.find(keyfrom(x, z)); if (found != chunksMap.end()) { return found->second; } - auto chunk = std::make_shared(x, z); + std::shared_ptr chunk; + { + timeutil::ScopeLogTimer log(555); + chunk = std::make_shared(x, z); + } chunksMap[keyfrom(x, z)] = chunk; World& world = *level.getWorld(); From 5fdb71c47bfb2daef6a15c15692d15a528bf17a9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 18 Nov 2025 19:47:35 +0300 Subject: [PATCH 06/15] add ObjectsPool --- src/util/BufferPool.hpp | 2 -- src/util/ObjectsPool.hpp | 51 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 src/util/ObjectsPool.hpp diff --git a/src/util/BufferPool.hpp b/src/util/BufferPool.hpp index aeeebb8c..c3a1112d 100644 --- a/src/util/BufferPool.hpp +++ b/src/util/BufferPool.hpp @@ -5,8 +5,6 @@ #include #include -#include "typedefs.hpp" - namespace util { /// @brief Thread-safe pool of same-sized buffers /// @tparam T array type diff --git a/src/util/ObjectsPool.hpp b/src/util/ObjectsPool.hpp new file mode 100644 index 00000000..de3e30ae --- /dev/null +++ b/src/util/ObjectsPool.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include + +namespace util { + struct AlignedDeleter { + void operator()(void* p) const { + std::free(p); + } + }; + + template + class ObjectsPool { + public: + ObjectsPool(size_t preallocated = 0) { + for (size_t i = 0; i < preallocated; i++) { + allocateNew(); + } + } + + template + std::shared_ptr create(Args&&... args) { + std::lock_guard lock(mutex); + if (freeObjects.empty()) { + allocateNew(); + } + auto ptr = freeObjects.front(); + freeObjects.pop(); + new (ptr)T(std::forward(args)...); + return std::shared_ptr(reinterpret_cast(ptr), [this](T* ptr) { + std::lock_guard lock(mutex); + freeObjects.push(ptr); + }); + } + private: + std::vector> objects; + std::queue freeObjects; + std::mutex mutex; + + void allocateNew() { + std::unique_ptr ptr( + std::aligned_alloc(alignof(T), sizeof(T)) + ); + freeObjects.push(ptr.get()); + objects.push_back(std::move(ptr)); + } + }; +} From 5317106d812c400d4f4b1d71e3228d88bbdc81a1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 18 Nov 2025 19:48:00 +0300 Subject: [PATCH 07/15] test chunks pool --- src/voxels/GlobalChunks.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/voxels/GlobalChunks.cpp b/src/voxels/GlobalChunks.cpp index 830e13d6..58734dc6 100644 --- a/src/voxels/GlobalChunks.cpp +++ b/src/voxels/GlobalChunks.cpp @@ -90,6 +90,9 @@ static inline auto load_inventories( } #include "util/timeutil.hpp" +#include "util/ObjectsPool.hpp" + +static util::ObjectsPool chunks_pool(1'024); std::shared_ptr GlobalChunks::create(int x, int z) { const auto& found = chunksMap.find(keyfrom(x, z)); @@ -100,7 +103,8 @@ std::shared_ptr GlobalChunks::create(int x, int z) { std::shared_ptr chunk; { timeutil::ScopeLogTimer log(555); - chunk = std::make_shared(x, z); + // chunk = std::make_shared(x, z); + chunk = chunks_pool.create(x, z); } chunksMap[keyfrom(x, z)] = chunk; From 8e66d78ef0f314fab1b650e2f8c510ab7c21bcf0 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 18 Nov 2025 20:08:11 +0300 Subject: [PATCH 08/15] fix msvc build --- src/util/ObjectsPool.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/util/ObjectsPool.hpp b/src/util/ObjectsPool.hpp index de3e30ae..35b3753b 100644 --- a/src/util/ObjectsPool.hpp +++ b/src/util/ObjectsPool.hpp @@ -4,11 +4,16 @@ #include #include #include +#include namespace util { struct AlignedDeleter { void operator()(void* p) const { +#if defined(_WIN32) + _aligned_free(p); +#else std::free(p); +#endif } }; @@ -42,7 +47,11 @@ namespace util { void allocateNew() { std::unique_ptr ptr( +#if defined(_WIN32) + _aligned_malloc(sizeof(T), alignof(T)); +#else std::aligned_alloc(alignof(T), sizeof(T)) +#endif ); freeObjects.push(ptr.get()); objects.push_back(std::move(ptr)); From 263c0e3b570a3510736122b7f1de2e8928f956f6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 18 Nov 2025 20:14:37 +0300 Subject: [PATCH 09/15] fix macos build --- src/util/ObjectsPool.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/util/ObjectsPool.hpp b/src/util/ObjectsPool.hpp index 35b3753b..bcd3294c 100644 --- a/src/util/ObjectsPool.hpp +++ b/src/util/ObjectsPool.hpp @@ -4,7 +4,10 @@ #include #include #include + +#if defined(_WIN32) #include +#endif namespace util { struct AlignedDeleter { From d3e73be3fe3bb72ed14840571bc3a5fb611ab67d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 18 Nov 2025 20:24:18 +0300 Subject: [PATCH 10/15] fix windows build again --- src/util/ObjectsPool.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/ObjectsPool.hpp b/src/util/ObjectsPool.hpp index bcd3294c..0dc97fd2 100644 --- a/src/util/ObjectsPool.hpp +++ b/src/util/ObjectsPool.hpp @@ -51,7 +51,7 @@ namespace util { void allocateNew() { std::unique_ptr ptr( #if defined(_WIN32) - _aligned_malloc(sizeof(T), alignof(T)); + _aligned_malloc(sizeof(T), alignof(T)) #else std::aligned_alloc(alignof(T), sizeof(T)) #endif From 1a6880a572eb8f5b3df768c0ad892c139f0542ad Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 18 Nov 2025 20:46:24 +0300 Subject: [PATCH 11/15] remove that thing --- src/logic/ChunksController.cpp | 4 ---- src/voxels/GlobalChunks.cpp | 23 ++++++++--------------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index 692d8705..b521b0d8 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -163,11 +163,7 @@ bool ChunksController::buildLights( return false; } -#include "util/timeutil.hpp" - void ChunksController::createChunk(const Player& player, int x, int z) const { - timeutil::ScopeLogTimer log(111); - if (!player.isLoadingChunks()) { if (auto chunk = level.chunks->fetch(x, z)) { player.chunks->putChunk(chunk); diff --git a/src/voxels/GlobalChunks.cpp b/src/voxels/GlobalChunks.cpp index 58734dc6..438ae6d8 100644 --- a/src/voxels/GlobalChunks.cpp +++ b/src/voxels/GlobalChunks.cpp @@ -2,22 +2,23 @@ #include -#include "content/Content.hpp" +#include "Block.hpp" +#include "Chunk.hpp" #include "coders/json.hpp" +#include "content/Content.hpp" #include "debug/Logger.hpp" -#include "world/files/WorldFiles.hpp" #include "items/Inventories.hpp" #include "lighting/Lightmap.hpp" #include "maths/voxmaths.hpp" #include "objects/Entities.hpp" #include "objects/Entity.hpp" -#include "voxels/blocks_agent.hpp" #include "typedefs.hpp" -#include "world/LevelEvents.hpp" +#include "util/ObjectsPool.hpp" +#include "voxels/blocks_agent.hpp" +#include "world/files/WorldFiles.hpp" #include "world/Level.hpp" +#include "world/LevelEvents.hpp" #include "world/World.hpp" -#include "Block.hpp" -#include "Chunk.hpp" static debug::Logger logger("chunks-storage"); @@ -89,9 +90,6 @@ static inline auto load_inventories( return invs; } -#include "util/timeutil.hpp" -#include "util/ObjectsPool.hpp" - static util::ObjectsPool chunks_pool(1'024); std::shared_ptr GlobalChunks::create(int x, int z) { @@ -100,12 +98,7 @@ std::shared_ptr GlobalChunks::create(int x, int z) { return found->second; } - std::shared_ptr chunk; - { - timeutil::ScopeLogTimer log(555); - // chunk = std::make_shared(x, z); - chunk = chunks_pool.create(x, z); - } + auto chunk = chunks_pool.create(x, z); chunksMap[keyfrom(x, z)] = chunk; World& world = *level.getWorld(); From b3e5f2a1c01de2f7bda4f864c1d19ce13914d4d5 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 18 Nov 2025 20:58:12 +0300 Subject: [PATCH 12/15] update doc/*/scripting/events.md --- doc/en/scripting/events.md | 9 +++++++++ doc/ru/scripting/events.md | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/doc/en/scripting/events.md b/doc/en/scripting/events.md index fce3abfb..ec66b9f9 100644 --- a/doc/en/scripting/events.md +++ b/doc/en/scripting/events.md @@ -53,6 +53,15 @@ 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_block_present(x, y, z) +``` + +Called for a specific block when it appears in the world (generated/loaded/placed). +The call occurs within a time period that may depend on the event queue load. +Under light load, it occurs during the first tick interval of the block. +on_block_tick is not called until the event is called. + ```lua function on_player_tick(playerid: int, tps: int) ``` diff --git a/doc/ru/scripting/events.md b/doc/ru/scripting/events.md index 73a6bc0c..311e5a32 100644 --- a/doc/ru/scripting/events.md +++ b/doc/ru/scripting/events.md @@ -53,6 +53,15 @@ function on_block_tick(x, y, z, tps: number) Вызывается tps (20 / tick-interval) раз в секунду для конкретного блока. Используйте 1/tps вместо `time.delta()`. +```lua +function on_block_present(x, y, z) +``` + +Вызывается для конкретного блока при появлении (генерации/загрузке/размещении). +Вызов происходит в течение времени, которое может зависеть от нагрузки очереди событий. +При малой нагрузке происходит в течение первого такта блока (tick-interval). +До вызова события on_block_tick не вызывается. + ```lua function on_player_tick(playerid: int, tps: int) ``` From f721731ecc57fe71989187ed64a08ef7051aa1e7 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 18 Nov 2025 22:11:44 +0300 Subject: [PATCH 13/15] add 'on_block_removed' event --- res/scripts/internal_events.lua | 41 ++++++++++++++++-------- res/scripts/stdlib.lua | 4 +++ src/frontend/screens/LevelScreen.cpp | 2 ++ src/logic/EngineController.cpp | 4 ++- src/logic/LevelController.cpp | 10 +++++- src/logic/LevelController.hpp | 2 ++ src/logic/scripting/lua/libs/libcore.cpp | 1 + src/logic/scripting/scripting.cpp | 9 ++++++ src/logic/scripting/scripting.hpp | 1 + src/voxels/Block.hpp | 1 + src/voxels/blocks_agent.cpp | 1 + src/voxels/blocks_agent.hpp | 1 + 12 files changed, 61 insertions(+), 16 deletions(-) diff --git a/res/scripts/internal_events.lua b/res/scripts/internal_events.lua index 4f72e1b1..ec8e8dd6 100644 --- a/res/scripts/internal_events.lua +++ b/res/scripts/internal_events.lua @@ -1,8 +1,9 @@ local updating_blocks = {} local present_queues = {} -local TYPE_REGISTER = 1 -local TYPE_UPDATING = 2 -local TYPE_PRESENT = 4 +local REGISTER_BIT = 0x1 +local UPDATING_BIT = 0x2 +local PRESENT_BIT = 0x4 +local REMOVED_BIT = 0x8 block.__perform_ticks = function(delta) for id, entry in pairs(updating_blocks) do @@ -25,7 +26,7 @@ block.__perform_ticks = function(delta) end for id, queue in pairs(present_queues) do queue.timer = queue.timer + delta - local steps = math.floor(queue.timer / queue.delta * #queue / 4) + local steps = math.floor(queue.timer / queue.delta * #queue / 3) if steps == 0 then goto continue end @@ -33,16 +34,15 @@ block.__perform_ticks = function(delta) local event = queue.event local update_list = updating_blocks[id] for i=1, steps do - local index = #queue - 3 + local index = #queue - 2 if index <= 0 then break end - local is_register = queue[index] - local x = queue[index + 1] - local y = queue[index + 2] - local z = queue[index + 3] + local x = queue[index] + local y = queue[index + 1] + local z = queue[index + 2] - for j=1,4 do + for j=1,3 do table.remove(queue, index) end events.emit(event, x, y, z) @@ -65,6 +65,10 @@ block.__process_register_events = function() if not register_events then return end + + local emit_event = events.emit + local removed_events = {} + for i=1, #register_events, 4 do local header = register_events[i] local event_bits = bit.band(header, 0xFFFF) @@ -73,11 +77,21 @@ block.__process_register_events = function() local y = register_events[i + 2] local z = register_events[i + 3] - local is_register = bit.band(event_bits, TYPE_REGISTER) ~= 0 - local is_updating = bit.band(event_bits, TYPE_UPDATING) ~= 0 - local is_present = bit.band(event_bits, TYPE_PRESENT) ~= 0 + local is_register = bit.band(event_bits, REGISTER_BIT) ~= 0 + local is_updating = bit.band(event_bits, UPDATING_BIT) ~= 0 + local is_present = bit.band(event_bits, PRESENT_BIT) ~= 0 + local is_removed = bit.band(event_bits, REMOVED_BIT) ~= 0 local list = updating_blocks[id] + if not is_register and is_removed then + local rm_event = removed_events[id] + if not rm_event then + rm_event = block.name(id) .. ".blockremoved" + removed_events[id] = rm_event + end + emit_event(rm_event, x, y, z) + end + if not list and is_register and is_updating then list = {} list.event = block.name(id) .. ".blocktick" @@ -100,7 +114,6 @@ block.__process_register_events = function() present_queue.updating = is_updating present_queues[id] = present_queue end - table.insert(present_queue, is_register) table.insert(present_queue, x) table.insert(present_queue, y) table.insert(present_queue, z) diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index a9199132..8624a844 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -357,6 +357,10 @@ function __vc_on_world_tick(tps) time.schedules.world:tick(1.0 / tps) end +function __vc_process_before_quit() + block.__process_register_events() +end + function __vc_on_world_save() local rule_values = {} for name, rule in pairs(rules.rules) do diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index eab517da..10348734 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -109,6 +109,7 @@ LevelScreen::~LevelScreen() { scripting::on_frontend_close(); // unblock all bindings input.getBindings().enableAll(); + playerController->getPlayer()->chunks->saveAndClear(); controller->onWorldQuit(); engine.getPaths().setCurrentWorldFolder(""); } @@ -278,5 +279,6 @@ void LevelScreen::onEngineShutdown() { if (hud->isInventoryOpen()) { hud->closeInventory(); } + controller->processBeforeQuit(); controller->saveWorld(); } diff --git a/src/logic/EngineController.cpp b/src/logic/EngineController.cpp index d6820fa1..db956e15 100644 --- a/src/logic/EngineController.cpp +++ b/src/logic/EngineController.cpp @@ -366,7 +366,9 @@ void EngineController::reconfigPacks( ); } } else { - auto world = controller->getLevel()->getWorld(); + auto level = controller->getLevel(); + auto world = level->getWorld(); + controller->processBeforeQuit(); controller->saveWorld(); auto names = PacksManager::getNames(world->getPacks()); diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 95d91014..11717d9a 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -27,7 +27,8 @@ LevelController::LevelController( : settings(engine->getSettings()), level(std::move(levelPtr)), chunks(std::make_unique(*level)), - playerTickClock(20, 3) { + playerTickClock(20, 3), + localPlayer(clientPlayer) { level->events->listen(LevelEventType::CHUNK_PRESENT, [](auto, Chunk* chunk) { scripting::on_chunk_present(*chunk, chunk->flags.loaded); @@ -121,6 +122,13 @@ void LevelController::update(float delta, bool pause) { level->entities->clean(); } +void LevelController::processBeforeQuit() { + if (localPlayer) { + localPlayer->chunks->saveAndClear(); + } + scripting::process_before_quit(); +} + void LevelController::saveWorld() { auto world = level->getWorld(); if (world->isNameless()) { diff --git a/src/logic/LevelController.hpp b/src/logic/LevelController.hpp index 7ae0fc5b..c7b98623 100644 --- a/src/logic/LevelController.hpp +++ b/src/logic/LevelController.hpp @@ -20,6 +20,7 @@ class LevelController { std::unique_ptr chunks; util::Clock playerTickClock; + Player* localPlayer; public: LevelController(Engine* engine, std::unique_ptr level, Player* clientPlayer); @@ -27,6 +28,7 @@ public: /// @param pause is world and player simulation paused void update(float delta, bool pause); + void processBeforeQuit(); void saveWorld(); void onWorldQuit(); diff --git a/src/logic/scripting/lua/libs/libcore.cpp b/src/logic/scripting/lua/libs/libcore.cpp index 8f8a7bc8..8454e5c3 100644 --- a/src/logic/scripting/lua/libs/libcore.cpp +++ b/src/logic/scripting/lua/libs/libcore.cpp @@ -119,6 +119,7 @@ static int l_close_world(lua::State* L) { if (controller == nullptr) { throw std::runtime_error("no world open"); } + controller->processBeforeQuit(); bool save_world = lua::toboolean(L, 1); if (save_world) { controller->saveWorld(); diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index fb2e997b..3a68917d 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -341,6 +341,13 @@ void scripting::on_world_save() { } } +void scripting::process_before_quit() { + auto L = lua::get_main_state(); + if (lua::getglobal(L, "__vc_process_before_quit")) { + lua::call_nothrow(L, 0, 0); + } +} + void scripting::on_world_quit() { auto L = lua::get_main_state(); for (auto& pack : content_control->getAllContentPacks()) { @@ -677,6 +684,8 @@ void scripting::load_content_script( register_event(env, "on_blocks_tick", prefix + ".blockstick"); funcsset.onblockpresent = register_event(env, "on_block_present", prefix + ".blockpresent"); + funcsset.onblockremoved = + register_event(env, "on_block_removed", prefix + ".blockremoved"); } void scripting::load_content_script( diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index a096e9cc..966450c6 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -81,6 +81,7 @@ namespace scripting { void on_world_load(LevelController* controller); void on_world_tick(int tps); void on_world_save(); + void process_before_quit(); void on_world_quit(); void cleanup(const std::vector& nonReset); void on_blocks_tick(const Block& block, int tps); diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index 9d091893..29eda249 100644 --- a/src/voxels/Block.hpp +++ b/src/voxels/Block.hpp @@ -51,6 +51,7 @@ struct BlockFuncsSet { bool onblocktick : 1; bool onblockstick : 1; bool onblockpresent : 1; + bool onblockremoved : 1; }; struct CoordSystem { diff --git a/src/voxels/blocks_agent.cpp b/src/voxels/blocks_agent.cpp index 338c3cb8..7041f67a 100644 --- a/src/voxels/blocks_agent.cpp +++ b/src/voxels/blocks_agent.cpp @@ -19,6 +19,7 @@ static uint8_t get_events_bits(const Block& def) { auto funcsset = def.rt.funcsset; bits |= BlockRegisterEvent::UPDATING_BIT * funcsset.onblocktick; bits |= BlockRegisterEvent::PRESENT_EVENT_BIT * funcsset.onblockpresent; + bits |= BlockRegisterEvent::REMOVED_EVENT_BIT * funcsset.onblockremoved; return bits; } diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index 93a3c1ad..7c0223cb 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -28,6 +28,7 @@ struct BlockRegisterEvent { static inline constexpr uint8_t REGISTER_BIT = 0x1; static inline constexpr uint8_t UPDATING_BIT = 0x2; static inline constexpr uint8_t PRESENT_EVENT_BIT = 0x4; + static inline constexpr uint8_t REMOVED_EVENT_BIT = 0x8; uint8_t bits; blockid_t id; glm::ivec3 coord; From b1312c354d8af44c51b5c7b4eba2adf0168ca5a6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 18 Nov 2025 22:18:56 +0300 Subject: [PATCH 14/15] update doc/*/scripting/events.md --- doc/en/scripting/events.md | 10 ++++++++++ doc/ru/scripting/events.md | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/doc/en/scripting/events.md b/doc/en/scripting/events.md index ec66b9f9..fc8bb27f 100644 --- a/doc/en/scripting/events.md +++ b/doc/en/scripting/events.md @@ -4,6 +4,10 @@ Callbacks specified in block script. +> [!WARNING] +> events such as on_block_tick, on_block_present, and on_block_removed +> can cause performance issues if used carelessly or excessively. + ```lua function on_placed(x, y, z, playerid) ``` @@ -62,6 +66,12 @@ The call occurs within a time period that may depend on the event queue load. Under light load, it occurs during the first tick interval of the block. on_block_tick is not called until the event is called. +```lua +function on_block_removed(x, y, z) +``` + +Called when chunk containing the block unloads. + ```lua function on_player_tick(playerid: int, tps: int) ``` diff --git a/doc/ru/scripting/events.md b/doc/ru/scripting/events.md index 311e5a32..83baef25 100644 --- a/doc/ru/scripting/events.md +++ b/doc/ru/scripting/events.md @@ -4,6 +4,10 @@ Функции для обработки событий, прописываемые в скрипте блока. +> [!WARNING] +> Mass events such as on_block_tick, on_block_present, and on_block_removed, +> if used carelessly or excessively, can lead to performance issues. + ```lua function on_placed(x, y, z, playerid) ``` @@ -62,6 +66,12 @@ function on_block_present(x, y, z) При малой нагрузке происходит в течение первого такта блока (tick-interval). До вызова события on_block_tick не вызывается. +```lua +function on_block_removed(x, y, z) +``` + +Вызывается при выгрузке чанка, в котором находится блок. + ```lua function on_player_tick(playerid: int, tps: int) ``` From c951d787b797cfdefbad135c20f04b1fca586d25 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 18 Nov 2025 22:25:04 +0300 Subject: [PATCH 15/15] fix: ObjectsPool missing destructor call --- src/util/ObjectsPool.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/ObjectsPool.hpp b/src/util/ObjectsPool.hpp index 0dc97fd2..55238959 100644 --- a/src/util/ObjectsPool.hpp +++ b/src/util/ObjectsPool.hpp @@ -39,6 +39,7 @@ namespace util { freeObjects.pop(); new (ptr)T(std::forward(args)...); return std::shared_ptr(reinterpret_cast(ptr), [this](T* ptr) { + ptr->~T(); std::lock_guard lock(mutex); freeObjects.push(ptr); });