From 279fb92b94a0f8b8d28ed141b54b160450ec91f7 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 18 Jul 2026 13:14:38 +0300 Subject: [PATCH 01/12] fix canvas:encode produces flipped images for png --- src/coders/png.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coders/png.cpp b/src/coders/png.cpp index 030844352..18cc0a474 100644 --- a/src/coders/png.cpp +++ b/src/coders/png.cpp @@ -49,9 +49,9 @@ static util::Buffer write_to_memory(uint width, uint height, const ubyte* png_write_info(png_ptr, info_ptr); auto row = std::make_unique(pixsize * width); - for (uint y = 0; y < height; y++) { - for (uint x = 0; x < width; x++) { - for (uint i = 0; i < pixsize; i++) { + for (int y = height-1; y >= 0; y--) { + for (int x = 0; x < width; x++) { + for (int i = 0; i < pixsize; i++) { row[x * pixsize + i] = (png_byte)data[(y * width + x) * pixsize + i]; } From a5596ed5cd65bc62b983ec9dff3263beeced2dd7 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 18 Jul 2026 13:25:04 +0300 Subject: [PATCH 02/12] fix canvas:line lines clipping --- src/graphics/core/ImageData.cpp | 121 +++++++++++++----- .../lua/usertypes/lua_type_canvas.cpp | 11 +- 2 files changed, 93 insertions(+), 39 deletions(-) diff --git a/src/graphics/core/ImageData.cpp b/src/graphics/core/ImageData.cpp index 6875d7ab9..5f7761188 100644 --- a/src/graphics/core/ImageData.cpp +++ b/src/graphics/core/ImageData.cpp @@ -1,5 +1,7 @@ #include "ImageData.hpp" +#include "debug/Logger.hpp" + #include #include #include @@ -7,6 +9,8 @@ #include #include +static debug::Logger logger("image-data"); + ImageData::ImageData(ImageFormat format, uint width, uint height) : format(format), width(width), height(height) { size_t pixsize; @@ -106,47 +110,83 @@ std::unique_ptr ImageData::cropped(int x, int y, int width, int heigh return subImage; } -static bool clip_line(int& x1, int& y1, int& x2, int& y2, int width, int height) { - const int left = 0; - const int right = width; - const int bottom = 0; - const int top = height; +static bool clip_line( + int& x0, int& y0, int& x1, int& y1, int width, int height +) { + if (width <= 0 || height <= 0) { + return false; + } + constexpr int INSIDE = 0; + constexpr int LEFT = 1; + constexpr int RIGHT = 2; + constexpr int BOTTOM = 4; + constexpr int TOP = 8; - int dx = x2 - x1; - int dy = y2 - y1; + const int xmin = 0; + const int ymin = 0; + const int xmax = width - 1; + const int ymax = height - 1; - float t0 = 0.0f; - float t1 = 1.0f; + auto outcode = [&](int x, int y) { + int code = INSIDE; - auto clip = [](int p, int q, float& t0, float& t1) { - if (p == 0) { - return q >= 0; - } - float t = static_cast(q) / p; - if (p < 0) { - if (t > t1) return false; - if (t > t0) t0 = t; - } else { - if (t < t0) return false; - if (t < t1) t1 = t; - } - return true; + if (x < xmin) + code |= LEFT; + else if (x > xmax) + code |= RIGHT; + + if (y < ymin) + code |= BOTTOM; + else if (y > ymax) + code |= TOP; + + return code; }; - if (!clip(-dx, x1 - left, t0, t1)) return false; - if (!clip( dx, right - x1, t0, t1)) return false; - if (!clip(-dy, y1 - bottom, t0, t1)) return false; - if (!clip( dy, top - y1, t0, t1)) return false; + int c0 = outcode(x0, y0); + int c1 = outcode(x1, y1); - if (t1 < 1.0f) { - x2 = x1 + static_cast(std::round(t1 * dx)); - y2 = y1 + static_cast(std::round(t1 * dy)); + while (true) { + if (!(c0 | c1)) { + return true; + } + if (c0 & c1) { + return false; + } + + const int out = c0 ? c0 : c1; + + double x = 0.0; + double y = 0.0; + + if (out & TOP) { + y = ymax; + x = x0 + (x1 - x0) * static_cast(ymax - y0) / (y1 - y0); + } else if (out & BOTTOM) { + y = ymin; + x = x0 + (x1 - x0) * static_cast(ymin - y0) / (y1 - y0); + } else if (out & RIGHT) { + x = xmax; + y = y0 + (y1 - y0) * static_cast(xmax - x0) / (x1 - x0); + } else { // LEFT + x = xmin; + y = y0 + (y1 - y0) * static_cast(xmin - x0) / (x1 - x0); + } + + if (out == c0) { + x0 = std::clamp(static_cast(std::lround(x)), xmin, xmax); + y0 = std::clamp(static_cast(std::lround(y)), ymin, ymax); + c0 = outcode(x0, y0); + } else { + x1 = std::clamp(static_cast(std::lround(x)), xmin, xmax); + y1 = std::clamp(static_cast(std::lround(y)), ymin, ymax); + c1 = outcode(x1, y1); + } } - if (t0 > 0.0f) { - x1 = x1 + static_cast(std::round(t0 * dx)); - y1 = y1 + static_cast(std::round(t0 * dy)); - } - return true; +} + +static bool is_point_outside(int x, int y, int width, int height) { + return x < 0 || y < 0 || x >= width || y >= height; } template @@ -155,11 +195,24 @@ static void draw_line(ImageData& image, int x1, int y1, int x2, int y2, const gl uint width = image.getWidth(); uint height = image.getHeight(); + glm::ivec4 init {x1, y1, x2, y2}; + if ((x1 < 0 || x1 >= width || x2 < 0 || x2 >= width || y1 < 0 || y1 >= height || y2 < 0 || y2 >= height) && !clip_line(x1, y1, x2, y2, width, height)) { return; } + // spam info for bug report + if (is_point_outside(x1, y1, width, height) || is_point_outside(x2, y2, width, height)) { + logger.warning() << "clip_line fault: [" << init.x << ", " << init.y + << "] - [" << init.z << ", " << init.w << "] --> [" + << x1 << ", " << y1 << "] - [" << x2 << ", " << y2 + << "]"; + x1 = glm::clamp(x1, 0, static_cast(width) - 1); + y1 = glm::clamp(y1, 0, static_cast(height) - 1); + x2 = glm::clamp(x2, 0, static_cast(width) - 1); + y2 = glm::clamp(y2, 0, static_cast(height) - 1); + } int dx = std::abs(x2 - x1); int dy = -std::abs(y2 - y1); diff --git a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp index 37c53abd9..c670e23df 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp @@ -81,16 +81,17 @@ union RGBA { uint8_t arr[4]; uint32_t rgba; }; +static_assert(sizeof(RGBA) == 4); -static RGBA* get_at(const ImageData& data, uint index) { - if (index >= data.getWidth() * data.getHeight()) { - return nullptr; - } +static RGBA* get_at(const ImageData& data, size_t index) { return reinterpret_cast(data.getData() + index * sizeof(RGBA)); } static RGBA* get_at(const ImageData& data, uint x, uint y) { - return get_at(data, y * data.getWidth() + x); + if (x >= data.getWidth() || y >= data.getHeight()) { + return nullptr; + } + return get_at(data, static_cast(y) * data.getWidth() + x); } static RGBA* get_at(State* L, uint x, uint y) { From 586ab3948323e2160ac10198c97528d891ab05d8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 18 Jul 2026 13:26:49 +0300 Subject: [PATCH 03/12] add canvas comparsion to asserts.equals --- res/modules/internal/asserts.lua | 73 +++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/res/modules/internal/asserts.lua b/res/modules/internal/asserts.lua index 2317bb7ce..5c52de875 100644 --- a/res/modules/internal/asserts.lua +++ b/res/modules/internal/asserts.lua @@ -1,8 +1,77 @@ local this = {} +local app = __vc_app -function this.equals(expected, fact) +local function diff_canvas(expected, fact) + this.equals(expected.width, fact.width, "image width") + this.equals(expected.width, fact.width, "image height") + + local diff = Canvas({expected.width, expected.height}) + + local mismatches = 0 + + for y = 0, expected.height - 1 do + for x = 0, expected.width - 1 do + local ca = expected:at(x, y) + local cb = fact:at(x, y) + + if ca == cb then + diff:set(x, y, ca) + else + diff:set(x, y, 0xff0000ff) + mismatches = mismatches + 1 + end + end + end + + return diff, mismatches +end + +local TEST_ARTIFACTS_PATH = "export:test-results" + +local function get_test_artifact_path(name) + local path = TEST_ARTIFACTS_PATH + if not file.isdir(path) then + file.mkdirs(path) + end + return file.join(path, name) +end + +function this.equals(expected, fact, prefix) + local mt = getmetatable(expected) + if mt == Canvas then + local diff, mismatches = diff_canvas(expected, fact) + if mismatches > 0 then + local test_name = app.script or random.uuid() + local path_diff = get_test_artifact_path( + string.format("diff_%s.png", test_name) + ) + local path_fact = get_test_artifact_path( + string.format("fact_%s.png", test_name) + ) + local path_expected = get_test_artifact_path( + string.format("expected_%s.png", test_name) + ) + file.write_bytes(path_diff, diff:encode('png')) + file.write_bytes(path_fact, fact:encode('png')) + file.write_bytes(path_expected, expected:encode('png')) + local total_pixels = expected.width * expected.height + assert(mismatches == 0, string.format( + "%s pixels of %s (%s%%) are different, diff saved as %s", + mismatches, + total_pixels, + math.round(mismatches / total_pixels * 100), + string.escape(path_diff)) + ) + end + return + end + if prefix then + prefix = prefix .. " " + else + prefix = "" + end assert(fact == expected, string.format( - "(fact == expected) assertion failed\n Expected: %s\n Fact: %s", + prefix .. "(fact == expected) assertion failed\n Expected: %s\n Fact: %s", expected, fact )) end From 917806a7ef8f67dee14a6a258ab3e9ae357a89d3 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 18 Jul 2026 13:27:27 +0300 Subject: [PATCH 04/12] add canvas lines test --- dev/tests/attachments/line_clipping_test.png | Bin 0 -> 1523 bytes dev/tests/canvas_lines.lua | 51 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 dev/tests/attachments/line_clipping_test.png create mode 100644 dev/tests/canvas_lines.lua diff --git a/dev/tests/attachments/line_clipping_test.png b/dev/tests/attachments/line_clipping_test.png new file mode 100644 index 0000000000000000000000000000000000000000..bb20d18415b55895486fc5ec65ebfcb54c2d6b6f GIT binary patch literal 1523 zcmVK^PVmfgrKJFx)a61v5=>Eu2#vd395Ypb#UO$uAVZ0toAT&1Kdw=%Z zb6$G%-#yQqCyU1~JbRD*?7jc;-uu4q>6~Ngd)B6N9aBHO{M=nS`tR4DOUt|C*N{EO z-Z{qzoWc*Le+W`UOZlzl{>^^6|3wM}R=(^!kMyVKKiGr7%9owFY5oNOY(rq>%g(!V zyjcLS4uO>~J4+|{Zvj9Yg7JoxFF2pB?iK;SG6Hw$7_Qq7&T?#GF6}=8fO!Nq?8{E< zx=s`Tctl|3%g&lby+3Q7I3VaZtQcDIWbKM>^auby@BEZ2hL*J1vYa0c;C|RQ1oE== zUQQH%xjEku$jR37Ap*eC3E$5@9XI`BOPcH{AK>eJ_Ws>3$MeAO--m+70T^M}d<#CbUTVn{;C)X|aEC9d|tVz!O7TExRA&5!t%{Ey9 zfFW2)iJey20e~TxOQ~&kMF4;yxKGJFmc;;oA%OB5Y>NVbwh@#r9|Qn^HW5fDAA|sa zwh%}u9|Qq_HV{ZEAA|vbx)De#9|Qt`IuS@LAA|ycx)4Y$9|Qw{IuJ-MAA|z{-3X+Y z4+;Q)P6Qgt2PFVN7XmHigCYQ+1A(UUK^Xv$jX+!Zpb!AaM4+*JPznHKA<$YrCAgHN)s0;uci=ejhp+W#~41yZV zhe`oJS_HM04;2G|Gze-gA1Vg``w-M#J~RLTwjpStd}ss!tV7UB`Opvmh(pj)`Op{u zSVquV`OqK$m`BiJ`Oqi;ctp@@`S9W6;-jMZHC;? z;CBSjdi;A|d*|G`Zw;#{Vc73nhojvopviN9=Q&@(*0NG*4#2w;zE%-51>n=!ev1g2 z0T6qs*BXK*0Ia>vZwW#50Jbjjw1S{&0DD*XT0l@OfKCfLyn9YeP^4fZX@)?AP@~eYxyA> z5Qqh!z5EdA2t)#?rTh@d2*d%Xt^5$F2t)y>wfqo?2*d!Wz5Ebq2t)vArTh>{2#Nz} zt^5!v2#Nw|wfqnX2#Nt{z5I~U2#Nr(@*h(YL3RLE{(MRy$O^#9Urq@G*#KDiamkG! z3jiyBU2-Bg9DtR-Ex8aJ3c$+Wm%|aPX9gO7#KFb&T}JkG5}V7p4=mVA^LIersX74 zfR&#&{rALp8-TcvieEY>OYnUGtbr&miofq*JP!=laq;#4^nD0l-h03G`vSUu!_d@E zn;tJAo$J&df9E>J8w}TRb_D)(HRhb#J_XpYFC$?(c)mJ_y%gVc5@_Yi%JbF1nsu1f z0IYnOxks>ON?$bvSo!iZHytb`dIYfYW#@j_@2>HWiy8r}eA#)P^QXR#9tA!Eu<~W+ z-3ebTf<*vUzU+)Q7SHx$5WEFo<;#w{bPN%5{U-#o0IYo3S-an23Bd~hD_?fDZnk-c z-~oV@FFSj8TFoK&1z_dNPO5Eo&j?On Date: Sat, 18 Jul 2026 13:54:50 +0300 Subject: [PATCH 05/12] update asserts --- res/modules/internal/asserts.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/res/modules/internal/asserts.lua b/res/modules/internal/asserts.lua index 5c52de875..b84d5d37f 100644 --- a/res/modules/internal/asserts.lua +++ b/res/modules/internal/asserts.lua @@ -41,15 +41,15 @@ function this.equals(expected, fact, prefix) if mt == Canvas then local diff, mismatches = diff_canvas(expected, fact) if mismatches > 0 then - local test_name = app.script or random.uuid() + prefix = prefix or app.script or base64.encode_urlsafe(random.bytes(6)) local path_diff = get_test_artifact_path( - string.format("diff_%s.png", test_name) + string.format("%s.diff.png", prefix) ) local path_fact = get_test_artifact_path( - string.format("fact_%s.png", test_name) + string.format("%s.fact.png", prefix) ) local path_expected = get_test_artifact_path( - string.format("expected_%s.png", test_name) + string.format("%s.expected.png", prefix) ) file.write_bytes(path_diff, diff:encode('png')) file.write_bytes(path_fact, fact:encode('png')) @@ -59,7 +59,7 @@ function this.equals(expected, fact, prefix) "%s pixels of %s (%s%%) are different, diff saved as %s", mismatches, total_pixels, - math.round(mismatches / total_pixels * 100), + math.round(mismatches / total_pixels * 100, 2), string.escape(path_diff)) ) end From e4a11f182efd7096bd32bf772a370f2a4c398764 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 18 Jul 2026 19:09:43 +0300 Subject: [PATCH 06/12] add on_player_set entity component event --- res/scripts/components/mob.lua | 6 ++++-- res/scripts/components/player.lua | 12 ++++-------- src/logic/LevelController.cpp | 3 +-- src/logic/scripting/scripting.hpp | 1 + src/logic/scripting/scripting_entities.cpp | 10 ++++++++++ src/objects/Entity.cpp | 7 ++++++- src/objects/Player.cpp | 2 +- src/objects/Player.hpp | 2 +- src/objects/ScriptComponents.hpp | 1 + 9 files changed, 29 insertions(+), 15 deletions(-) diff --git a/res/scripts/components/mob.lua b/res/scripts/components/mob.lua index 92c2a3723..9f2dc53b7 100644 --- a/res/scripts/components/mob.lua +++ b/res/scripts/components/mob.lua @@ -142,14 +142,16 @@ end function is_flight() return flight end -function set_flight(flag) flight = flag end +function set_flight(flag) + flight = flag + body:set_gravity_scale(flight and 0.0 or props.gravity_scale) +end local prev_angle = (vec2.angle({dir[3], dir[1]})) % 360 function on_physics_update(delta) local grounded = body:is_grounded() body:set_vdamping(flight) - body:set_gravity_scale(flight and 0.0 or props.gravity_scale) body:set_linear_damping( (flight or not grounded) and props.air_damping or props.ground_damping ) diff --git a/res/scripts/components/player.lua b/res/scripts/components/player.lua index 4f2ec53a8..e0f8cb900 100644 --- a/res/scripts/components/player.lua +++ b/res/scripts/components/player.lua @@ -58,16 +58,12 @@ local function process_player_inputs(pid, rot, delta) end end -local prepared = false -body:set_enabled(false) -mob.set_flight(true) +function on_player_set(pid) + mob.set_flight(player.is_flight(pid)) + body:set_body_type(player.is_noclip(pid) and "kinematic" or "dynamic") +end function on_physics_update(delta) - if not prepared then - prepared = true - body:set_enabled(true) - end - local pid = entity:get_player() if pid == -1 then return diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index ae0fa6798..704949851 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -82,7 +82,6 @@ void LevelController::update(float delta, bool pause) { continue; } player->rotationInterpolation.updateTimer(delta); - player->updateEntity(); glm::vec3 position = player->getPosition(); player->chunks->configure( glm::floor(position.x), @@ -96,9 +95,9 @@ void LevelController::update(float delta, bool pause) { *player, player.get() == clientPlayer ); + player->updateEntity(); } if (!pause) { - // update all objects that needed blocks->update(delta, settings.chunks.padding.get()); level->entities->update(delta); for (const auto& [_, player] : *level->players) { diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index 9d29528b8..4c71a3aa6 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -142,6 +142,7 @@ namespace scripting { void on_entity_grounded(const Entity& entity, float force); void on_entity_fall(const Entity& entity); void on_entity_save(const Entity& entity); + void on_entity_player_set(const Entity& entity, int64_t pid); void on_entities_update(int tps, int parts, int part); void on_entities_physics_update(float delta); void on_entities_render(float delta); diff --git a/src/logic/scripting/scripting_entities.cpp b/src/logic/scripting/scripting_entities.cpp index 375b382a9..ca9ed6db1 100644 --- a/src/logic/scripting/scripting_entities.cpp +++ b/src/logic/scripting/scripting_entities.cpp @@ -71,6 +71,7 @@ static void init_component_events( funcsset.on_aim_off = lua::hasfield(L, "on_aim_off"); funcsset.on_attacked = lua::hasfield(L, "on_attacked"); funcsset.on_used = lua::hasfield(L, "on_used"); + funcsset.on_player_set = lua::hasfield(L, "on_player_set"); lua::pop(L); } @@ -238,6 +239,15 @@ void scripting::on_entity_save(const Entity& entity) { ); } +void scripting::on_entity_player_set(const Entity& entity, int64_t pid) { + process_entity_callback( + entity, + "on_player_set", + &EntityFuncsSet::on_player_set, + [pid](auto L) { return lua::pushinteger(L, pid); } + ); +} + void scripting::on_sensor_enter( const Entity& entity, size_t index, entityid_t oid ) { diff --git a/src/objects/Entity.cpp b/src/objects/Entity.cpp index 0bf898351..6629d2981 100644 --- a/src/objects/Entity.cpp +++ b/src/objects/Entity.cpp @@ -113,6 +113,11 @@ int64_t Entity::getPlayer() const { } void Entity::setPlayer(int64_t id) { - registry.get(entity).player = id; + auto& eid = registry.get(entity); + if (eid.player == id) { + return; + } + eid.player = id; + scripting::on_entity_player_set(*this, id); } diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp index fec65d7de..c0fe61744 100644 --- a/src/objects/Player.cpp +++ b/src/objects/Player.cpp @@ -81,7 +81,7 @@ void Player::updateEntity() { } } -Hitbox* Player::getHitbox() { +Hitbox* Player::getHitbox() const { if (auto entity = level.entities->get(eid)) { return &entity->getRigidbody().hitbox; } diff --git a/src/objects/Player.hpp b/src/objects/Player.hpp index a095ccfb7..54ed70023 100644 --- a/src/objects/Player.hpp +++ b/src/objects/Player.hpp @@ -135,7 +135,7 @@ public: bool isCurrentCameraBuiltin() const; - Hitbox* getHitbox(); + Hitbox* getHitbox() const; void setSpawnPoint(glm::vec3 point); glm::vec3 getSpawnPoint() const; diff --git a/src/objects/ScriptComponents.hpp b/src/objects/ScriptComponents.hpp index 2711d4823..92c21d49f 100644 --- a/src/objects/ScriptComponents.hpp +++ b/src/objects/ScriptComponents.hpp @@ -17,6 +17,7 @@ struct EntityFuncsSet { bool on_aim_off; bool on_attacked; bool on_used; + bool on_player_set; }; struct UserComponent { From b99be08e88770bff77e19f22424f1449e4956895 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 18 Jul 2026 19:46:21 +0300 Subject: [PATCH 07/12] update doc/*/scripting/ecs.md --- doc/en/scripting/ecs.md | 6 ++++++ doc/ru/scripting/ecs.md | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/doc/en/scripting/ecs.md b/doc/en/scripting/ecs.md index 878452928..52be70363 100644 --- a/doc/en/scripting/ecs.md +++ b/doc/en/scripting/ecs.md @@ -264,3 +264,9 @@ function on_used(playerid: int) ``` Called when an entity is used (RMB by entity). The player ID is passed as an argument. + +```lua +function on_player_set(playerid: int) +``` + +Called when an entity is attached to a player. diff --git a/doc/ru/scripting/ecs.md b/doc/ru/scripting/ecs.md index 1a5f6ccf3..d659f342d 100644 --- a/doc/ru/scripting/ecs.md +++ b/doc/ru/scripting/ecs.md @@ -265,3 +265,8 @@ function on_used(playerid: int) Вызывается при использовании сущности (ПКМ по сущности). ID игрока передается в качестве аргумента. +```lua +function on_player_set(playerid: int) +``` + +Вызывается при прикреплении сущности к игроку. From 8164a16db830ee1a22ab2bd922c664a43fb95dcb Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 19 Jul 2026 19:41:52 +0300 Subject: [PATCH 08/12] add counter-based manual gc trigger for FFIBytearray --- res/modules/internal/bytearray.lua | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/res/modules/internal/bytearray.lua b/res/modules/internal/bytearray.lua index 739bb7cea..bc0907869 100644 --- a/res/modules/internal/bytearray.lua +++ b/res/modules/internal/bytearray.lua @@ -12,18 +12,34 @@ FFI.cdef[[ } bytearray_t; ]] -local malloc = FFI.C.malloc -local free = FFI.C.free local FFIBytearray local bytearray_type +local allocated_bytes = 0 +local GC_THRESHOLD = 50 * 1024 * 1024 + +local function malloc(size) + local raw = FFI.C.malloc(size) + if raw == nil then + return nil + end + + allocated_bytes = allocated_bytes + size + + if allocated_bytes >= GC_THRESHOLD then + collectgarbage("step", 200) + allocated_bytes = 0 + end + + return FFI.gc(raw, FFI.C.free) +end + local function grow_buffer(self, elems) local new_capacity = math.ceil(self.capacity / 0.75 + elems) local prev = self.bytes self.bytes = malloc(new_capacity) FFI.copy(self.bytes, prev, self.size) self.capacity = new_capacity - free(prev) end local function trim_buffer(self) @@ -35,7 +51,6 @@ local function trim_buffer(self) self.bytes = malloc(size) FFI.copy(self.bytes, prev, self.size) self.capacity = size - free(prev) end local function count_elements(b) @@ -114,7 +129,6 @@ local function reserve(self, new_capacity) self.bytes = malloc(new_capacity) FFI.copy(self.bytes, prev, self.size) self.capacity = new_capacity - free(prev) end local function get_capacity(self) @@ -173,9 +187,7 @@ local bytearray_mt = { __len = function(self) return tonumber(self.size) end, - __gc = function(self) - free(self.bytes) - end, + __gc = function(self) end, __ipairs = function(self) local i = 0 return function() From 46b4aab6dce9e7c195b5c29584168f8e5b8fbb52 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 19 Jul 2026 22:20:23 +0300 Subject: [PATCH 09/12] fix newly created raycast bug --- src/voxels/blocks_agent.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/voxels/blocks_agent.cpp b/src/voxels/blocks_agent.cpp index 9d1f0cc17..d72ddcceb 100644 --- a/src/voxels/blocks_agent.cpp +++ b/src/voxels/blocks_agent.cpp @@ -309,7 +309,9 @@ static inline voxel* raycast_blocks( } else { iend = {ix, iy, iz}; norm = {0, 0, 0}; - norm[steppedIndex] = -step[steppedIndex]; + if (steppedIndex != -1) { + norm[steppedIndex] = -step[steppedIndex]; + } return voxel; } } From 8e719eace65bae6578849fd57c50895c19d0f05f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 19 Jul 2026 22:43:50 +0300 Subject: [PATCH 10/12] add missing inventory function --- doc/en/scripting/builtins/libinventory.md | 14 +++++++++++++- doc/ru/scripting/builtins/libinventory.md | 16 ++++++++++++++-- src/items/ItemStack.hpp | 10 ++++++++++ src/logic/scripting/lua/libs/libinventory.cpp | 9 +++++++++ 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/doc/en/scripting/builtins/libinventory.md b/doc/en/scripting/builtins/libinventory.md index 8cf2d7c3e..172ec63e6 100644 --- a/doc/en/scripting/builtins/libinventory.md +++ b/doc/en/scripting/builtins/libinventory.md @@ -162,7 +162,19 @@ inventory.get_all_data( invid: int, -- slot index slot: int, -) -> table +) -> table | nil + +-- Sets the values ​​of all local item properties. +inventory.set_all_data( + -- inventory id + invid: int, + -- slot index + slot: int, + -- property:value table + fields: table, + -- clear other property values + [optional] clear: boolean = false +) ``` ```lua diff --git a/doc/ru/scripting/builtins/libinventory.md b/doc/ru/scripting/builtins/libinventory.md index 50ed8dccb..4a836f83b 100644 --- a/doc/ru/scripting/builtins/libinventory.md +++ b/doc/ru/scripting/builtins/libinventory.md @@ -170,7 +170,19 @@ inventory.get_all_data( invid: int, -- индекс слота slot: int, -) -> table +) -> table | nil + +-- Устанавливает значения локальных cвойств предмета. +inventory.set_all_data( + -- id инвентаря + invid: int, + -- индекс слота + slot: int, + -- таблица cвойство:значение + fields: table, + -- удалить остальные значения свойств + [опционально] clear: boolean = false +) ``` ```lua @@ -222,4 +234,4 @@ inventory.use( -- индекс слота slot: int ) -``` \ No newline at end of file +``` diff --git a/src/items/ItemStack.hpp b/src/items/ItemStack.hpp index 91f2d3e74..4896bc39e 100644 --- a/src/items/ItemStack.hpp +++ b/src/items/ItemStack.hpp @@ -55,6 +55,16 @@ public: return fields; } + void setFields(dv::value&& table, bool clear) { + if (clear) { + fields = std::move(table); + return; + } + for (auto&& [key, value] : table.asObject()) { + fields[std::move(key)] = std::move(value); + } + } + bool hasFields() const { return fields != nullptr; } diff --git a/src/logic/scripting/lua/libs/libinventory.cpp b/src/logic/scripting/lua/libs/libinventory.cpp index 3ec9d2ca3..2aea52394 100644 --- a/src/logic/scripting/lua/libs/libinventory.cpp +++ b/src/logic/scripting/lua/libs/libinventory.cpp @@ -237,6 +237,14 @@ static int l_get_all_data(lua::State* L, ItemStack& stack) { return lua::pushvalue(L, stack.getFields()); } +static int l_set_all_data(lua::State* L, ItemStack& stack) { + if (!lua::istable(L, 3)) { + throw std::runtime_error("table expected as argument 1"); + } + stack.setFields(lua::tovalue(L, 3), lua::toboolean(L, 4)); + return 0; +} + static int l_has_data(lua::State* L, ItemStack& stack) { auto key = lua::tostring(L, 3); if (key == nullptr) { @@ -267,6 +275,7 @@ const luaL_Reg inventorylib[] = { {"get_data", lua::wrap>}, {"set_data", lua::wrap>}, {"get_all_data", lua::wrap>}, + {"set_all_data", lua::wrap>}, {"has_data", lua::wrap>}, {"create", lua::wrap}, {"remove", lua::wrap}, From d55b494bb0a2c21e3f95f4f232c51ff1509abf20 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 20 Jul 2026 00:05:50 +0300 Subject: [PATCH 11/12] update bytearray,lua --- res/modules/internal/bytearray.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/res/modules/internal/bytearray.lua b/res/modules/internal/bytearray.lua index bc0907869..58b33a520 100644 --- a/res/modules/internal/bytearray.lua +++ b/res/modules/internal/bytearray.lua @@ -12,6 +12,8 @@ FFI.cdef[[ } bytearray_t; ]] +local free = FFI.C.free + local FFIBytearray local bytearray_type @@ -31,7 +33,7 @@ local function malloc(size) allocated_bytes = 0 end - return FFI.gc(raw, FFI.C.free) + return raw end local function grow_buffer(self, elems) @@ -40,6 +42,7 @@ local function grow_buffer(self, elems) self.bytes = malloc(new_capacity) FFI.copy(self.bytes, prev, self.size) self.capacity = new_capacity + free(prev) end local function trim_buffer(self) @@ -51,6 +54,7 @@ local function trim_buffer(self) self.bytes = malloc(size) FFI.copy(self.bytes, prev, self.size) self.capacity = size + free(prev) end local function count_elements(b) @@ -129,6 +133,7 @@ local function reserve(self, new_capacity) self.bytes = malloc(new_capacity) FFI.copy(self.bytes, prev, self.size) self.capacity = new_capacity + free(prev) end local function get_capacity(self) @@ -187,7 +192,9 @@ local bytearray_mt = { __len = function(self) return tonumber(self.size) end, - __gc = function(self) end, + __gc = function(self) + free(self.bytes) + end, __ipairs = function(self) local i = 0 return function() From a0b7e81a94d8a4f2750fbbd507ba6bd2e688f8b4 Mon Sep 17 00:00:00 2001 From: Onran Date: Wed, 1 Jul 2026 20:34:35 +0900 Subject: [PATCH 12/12] fix Skeleton::setConfig --- src/objects/rigging.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/objects/rigging.cpp b/src/objects/rigging.cpp index f686bfbcf..f47f7c733 100644 --- a/src/objects/rigging.cpp +++ b/src/objects/rigging.cpp @@ -88,12 +88,22 @@ void Skeleton::deserialize(const dv::value& root) { void Skeleton::setConfig(std::shared_ptr rigConfig) { config = std::move(rigConfig); + + const auto& bones = config->getBones(); + pose.matrices.resize( - config->getBones().size(), glm::mat4(1.0f) + bones.size(), glm::mat4(1.0f) ); calculated.matrices.resize( - config->getBones().size(), glm::mat4(1.0f) + bones.size(), glm::mat4(1.0f) ); + + modelOverrides.resize(bones.size()); + flags.resize(bones.size()); + + for (size_t i = 0; i < bones.size(); i++) { + flags[i].visible = true; + } } static void get_all_nodes(std::vector& nodes, Bone* node) {