From fc8065e5505654906e59d744ba636753d79e0210 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 22 Aug 2026 16:17:59 +0300 Subject: [PATCH 1/8] fix xml escapes in attribute values --- src/coders/xml.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/coders/xml.cpp b/src/coders/xml.cpp index a907b49db..f4f9799ed 100644 --- a/src/coders/xml.cpp +++ b/src/coders/xml.cpp @@ -208,6 +208,14 @@ namespace { class Parser : BasicParser { std::unique_ptr document; + void processEscapes(std::string& text) { + util::replaceAll(text, """, "\""); + util::replaceAll(text, "'", "'"); + util::replaceAll(text, "<", "<"); + util::replaceAll(text, ">", ">"); + util::replaceAll(text, "&", "&"); + } + std::unique_ptr parseOpenTag() { std::string tag = parseXMLName(); auto node = std::make_unique(tag); @@ -230,6 +238,7 @@ class Parser : BasicParser { } skip(1); attrtext = parseString(quote); + processEscapes(attrtext); } node->set(attrname, attrtext); } @@ -241,11 +250,7 @@ class Parser : BasicParser { if (peek() != '<') { auto element = std::make_unique("#"); auto text = parseText(); - util::replaceAll(text, """, "\""); - util::replaceAll(text, "'", "'"); - util::replaceAll(text, "<", "<"); - util::replaceAll(text, ">", ">"); - util::replaceAll(text, "&", "&"); + processEscapes(text); element->set("#", text); return element; } From f2018334cb97c7b9a8187eb4e155dbda97b0b1cb Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 22 Aug 2026 17:39:01 +0300 Subject: [PATCH 2/8] fix Texture::reload --- src/graphics/core/Texture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphics/core/Texture.cpp b/src/graphics/core/Texture.cpp index 5f326d20c..65afd4342 100644 --- a/src/graphics/core/Texture.cpp +++ b/src/graphics/core/Texture.cpp @@ -46,7 +46,7 @@ void Texture::resize(uint width, uint height) { } void Texture::reload(const ImageData& image) { - reload(image.getData(), width, height); + reload(image.getData(), image.getWidth(), image.getHeight()); } void Texture::reload(const ubyte* data, uint width, uint height) { From 78e27c812a87c8ee2cec1d6605049b9f8535ef2d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 22 Aug 2026 19:25:49 +0300 Subject: [PATCH 3/8] fix side collision --- src/physics/PhysicsSolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 7ecc07f05..a3a190f82 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -122,7 +122,7 @@ static void calc_collision( auto aabb = AABB(pos - half, pos + half); glm::vec3 scale(1.0f); - scale[nz] = 1.0f - E * 8.0f; + scale[nz] = 1.0f - E * 2.0f; aabb.scale(scale); aabb = aabb + offset; aabb.b.y -= stepHeight + E * 2; From fbe992755e70a3b4d321dc7d17981aec512bf0c1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 22 Aug 2026 21:11:23 +0300 Subject: [PATCH 4/8] fix WorldConverter::createBlockFieldsConvertTasks --- src/world/files/WorldConverter.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/world/files/WorldConverter.cpp b/src/world/files/WorldConverter.cpp index 2f7716d3f..72634076b 100644 --- a/src/world/files/WorldConverter.cpp +++ b/src/world/files/WorldConverter.cpp @@ -109,7 +109,7 @@ void WorldConverter::createBlockFieldsConvertTasks() { REGION_LAYER_BLOCKS_DATA, ConvertTaskType::CONVERT_BLOCKS_DATA ); - break; + return; default: break; } @@ -189,7 +189,7 @@ void WorldConverter::upgradeRegion( } void WorldConverter::convertVoxels(const io::path& file, int x, int z) const { - logger.info() << "converting voxels region " << x << "_" << z; + logger.info() << "converting voxels in region " << x << "_" << z; wfile->getRegions().processRegion(x, z, REGION_LAYER_VOXELS, [=](std::unique_ptr data, uint32_t*) { Chunk::convert(data.get(), report.get()); @@ -212,7 +212,7 @@ void WorldConverter::convertPlayer(const io::path& file) const { } void WorldConverter::convertBlocksData(int x, int z, const ContentReport& report) const { - logger.info() << "converting blocks data"; + logger.info() << "converting blocks data in region " << x << ", " << z; wfile->getRegions().processBlocksData(x, z, [=](BlocksMetadata* heap, std::unique_ptr voxelsData) { Chunk chunk(0, 0); From 2972b0ee8517f066ec50e65136247c6e57089bd8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 22 Aug 2026 21:15:41 +0300 Subject: [PATCH 5/8] cleanup --- src/world/files/WorldConverter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/world/files/WorldConverter.cpp b/src/world/files/WorldConverter.cpp index 72634076b..73a525d87 100644 --- a/src/world/files/WorldConverter.cpp +++ b/src/world/files/WorldConverter.cpp @@ -198,7 +198,7 @@ void WorldConverter::convertVoxels(const io::path& file, int x, int z) const { } void WorldConverter::convertInventories(const io::path& file, int x, int z) const { - logger.info() << "converting inventories region " << x << "_" << z; + logger.info() << "converting inventories in region " << x << "_" << z; wfile->getRegions().processInventories(x, z, [=](Inventory* inventory) { inventory->convert(report.get()); }); @@ -212,7 +212,7 @@ void WorldConverter::convertPlayer(const io::path& file) const { } void WorldConverter::convertBlocksData(int x, int z, const ContentReport& report) const { - logger.info() << "converting blocks data in region " << x << ", " << z; + logger.info() << "converting blocks data in region " << x << "_" << z; wfile->getRegions().processBlocksData(x, z, [=](BlocksMetadata* heap, std::unique_ptr voxelsData) { Chunk chunk(0, 0); From 4b4026b0284ce84fb518824c162247d5a7ada076 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 23 Aug 2026 12:01:18 +0300 Subject: [PATCH 6/8] add rigidbody:is_selectable, :set_selectable --- res/modules/internal/stdcomp.lua | 2 ++ src/logic/scripting/lua/libs/lib__rigidbody.cpp | 16 ++++++++++++++++ src/objects/Entities.cpp | 5 +++-- src/objects/Rigidbody.cpp | 3 +++ src/objects/Rigidbody.hpp | 1 + src/physics/Hitbox.hpp | 1 + 6 files changed, 26 insertions(+), 2 deletions(-) diff --git a/res/modules/internal/stdcomp.lua b/res/modules/internal/stdcomp.lua index dbf7f9776..ac11692ca 100644 --- a/res/modules/internal/stdcomp.lua +++ b/res/modules/internal/stdcomp.lua @@ -39,6 +39,8 @@ local Rigidbody = {__index={ set_mass=function(self, v) return __rigidbody.set_mass(self.eid, v) end, set_elasticity=function(self, v) return __rigidbody.set_elasticity(self.eid, v) end, get_ground_vel=function(self) return __rigidbody.get_ground_vel(self.eid) end, + is_selectable=function (self) return __rigidbody.is_selectable(self.eid) end, + set_selectable=function (self, b) return __rigidbody.set_selectable(self.eid, b) end, }} local function new_Rigidbody(eid) diff --git a/src/logic/scripting/lua/libs/lib__rigidbody.cpp b/src/logic/scripting/lua/libs/lib__rigidbody.cpp index a36f2b080..5a65b1b53 100644 --- a/src/logic/scripting/lua/libs/lib__rigidbody.cpp +++ b/src/logic/scripting/lua/libs/lib__rigidbody.cpp @@ -199,6 +199,20 @@ static int l_get_ground_vel(lua::State* L) { return 0; } +static int l_is_selectable(lua::State* L) { + if (auto entity = get_entity(L, 1)) { + return lua::pushboolean(L, entity->getRigidbody().hitbox.selectable); + } + return 0; +} + +static int l_set_selectable(lua::State* L) { + if (auto entity = get_entity(L, 1)) { + entity->getRigidbody().selectable = lua::toboolean(L, 2); + } + return 0; +} + const luaL_Reg rigidbodylib[] = { {"is_enabled", lua::wrap}, @@ -226,5 +240,7 @@ const luaL_Reg rigidbodylib[] = { {"get_elasticity", lua::wrap}, {"set_elasticity", lua::wrap}, {"get_ground_vel", lua::wrap}, + {"is_selectable", lua::wrap}, + {"set_selectable", lua::wrap}, {nullptr, nullptr} }; diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 70376e5fe..848b9d296 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -175,9 +175,10 @@ std::optional Entities::rayCast( glm::ivec3 foundNormal; for (auto [entity, eid, transform, body] : view.each()) { + const auto& hitbox = body.hitbox; if (eid.uid == settings.ignoredUid || !body.enabled || (settings.solidEntitiesOnly && !eid.def.solid) || - (!eid.def.selectable && !settings.includeNonSelectable)) { + (!hitbox.selectable && !settings.includeNonSelectable)) { continue; } if (settings.entitiesFilter) { @@ -187,7 +188,6 @@ std::optional Entities::rayCast( continue; } } - const auto& hitbox = body.hitbox; glm::ivec3 normal; double distance; if (ray.intersectAABB( @@ -330,6 +330,7 @@ void Entities::preparePhysics(float delta) { ? rigidbody.mass : std::numeric_limits::infinity(); rigidbody.hitbox.elasticity = rigidbody.elasticity; + rigidbody.hitbox.selectable = rigidbody.selectable; hitboxes.emplace_back(&rigidbody.hitbox); if (!eid.def.solid) { continue; diff --git a/src/objects/Rigidbody.cpp b/src/objects/Rigidbody.cpp index 2426aabac..337bb9f67 100644 --- a/src/objects/Rigidbody.cpp +++ b/src/objects/Rigidbody.cpp @@ -24,6 +24,7 @@ dv::value Rigidbody::serialize(bool saveVelocity, bool saveBodySettings) const { } bodymap["mass"] = mass; bodymap["elasticity"] = elasticity; + bodymap["selectable"] = selectable; } return bodymap; } @@ -37,6 +38,7 @@ void Rigidbody::deserialize(const dv::value& root) { root["damping"].asNumber(hitbox.linearDamping); root["mass"].asNumber(mass); root["elasticity"].asNumber(elasticity); + root["selectable"].asBoolean(selectable); } template @@ -55,6 +57,7 @@ void Rigidbody::initialize( ) { mass = def.mass; elasticity = def.elasticity; + selectable = def.selectable; sensors.resize(def.radialSensors.size() + def.boxSensors.size()); for (auto& [i, box] : def.boxSensors) { SensorParams params {}; diff --git a/src/objects/Rigidbody.hpp b/src/objects/Rigidbody.hpp index bc2a85f90..fb96f138f 100644 --- a/src/objects/Rigidbody.hpp +++ b/src/objects/Rigidbody.hpp @@ -15,6 +15,7 @@ struct Rigidbody { std::vector sensors; float mass; float elasticity; + bool selectable; dv::value serialize(bool saveVelocity, bool saveBodySettings) const; void deserialize(const dv::value& root); diff --git a/src/physics/Hitbox.hpp b/src/physics/Hitbox.hpp index 80c628491..91cabb2b3 100644 --- a/src/physics/Hitbox.hpp +++ b/src/physics/Hitbox.hpp @@ -65,6 +65,7 @@ struct Hitbox { float stepHeight = 0.5f; float mass = 1.0f; float elasticity = 0.0f; + bool selectable = true; std::string material; std::string groundMaterial; glm::vec3 groundVelocity {}; From 1e2baf9d5eccd43b024326248c8a8fa29d6968bc Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 23 Aug 2026 12:10:47 +0300 Subject: [PATCH 7/8] update doc/*/entity-properties.md --- doc/en/entity-properties.md | 3 ++- doc/ru/entity-properties.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/en/entity-properties.md b/doc/en/entity-properties.md index b0aee9aea..e25c6d71d 100644 --- a/doc/en/entity-properties.md +++ b/doc/en/entity-properties.md @@ -67,9 +67,10 @@ If true - the entity blocks the placement of a block that intersects with the en Default value: *true*. -### *selectable* +### *selectable* (raycast opacity) If set to `false` the cursor will ignore the entity, passing the ray. +Applies to all uses of raycast without the include_non_selectable flag. ### *sensors* diff --git a/doc/ru/entity-properties.md b/doc/ru/entity-properties.md index 2f5ca3791..71dc6dc88 100644 --- a/doc/ru/entity-properties.md +++ b/doc/ru/entity-properties.md @@ -67,9 +67,10 @@ Значение по-умолчанию: *true*. -### Выделяемость - *selectable* +### Непрозрачность для лучей - *selectable* При значении в `false` курсор будет игнорировать cущность, пропуская луч далее. +Распространяется на все использования raycast без флага include_non_selectable. ### Список сенсоров - *sensors* From b768730067aaa99f07816c1051a72edc001a95df Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 23 Aug 2026 12:14:35 +0300 Subject: [PATCH 8/8] update doc/*/scripting/ecs.md --- doc/en/scripting/ecs.md | 5 +++++ doc/ru/scripting/ecs.md | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/doc/en/scripting/ecs.md b/doc/en/scripting/ecs.md index 9636cf763..f8bce6d3f 100644 --- a/doc/en/scripting/ecs.md +++ b/doc/en/scripting/ecs.md @@ -36,6 +36,11 @@ entity:set_enabled(name: str, enable: bool) -- At components initialization -1 is also returned, -- since the binding occurs after initialization. entity:get_player() -> int or nil + +-- Checks the 'selectable' property of an entity (raycast opacity) +body:is_selectable() -> bool +-- Sets the value of the 'selectable' property of an entity +body:set_selectable(flag: bool) ``` ## Custom Components diff --git a/doc/ru/scripting/ecs.md b/doc/ru/scripting/ecs.md index cb4a79bec..be99774d5 100644 --- a/doc/ru/scripting/ecs.md +++ b/doc/ru/scripting/ecs.md @@ -134,6 +134,11 @@ body:set_elasticity(elasticity: number) -- Возвращает скорость поверхности, на которой находится тело, либо {0,0,0} body:get_ground_vel() -> vec3 + +-- Проверяет свойство 'selectable' у сущности (непрозрачность для лучей) +body:is_selectable() -> bool +-- Устанавливает значение свойство 'selectable' у сущности +body:set_selectable(flag: bool) ``` ### Skeleton