From 1558ef9c9808a095c2d695db6045d729044a561d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 25 Aug 2025 18:18:05 +0300 Subject: [PATCH 01/21] update vcm docs (#600) --- doc/ru/vcm.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/ru/vcm.md b/doc/ru/vcm.md index 9e52ca49..0eb555b4 100644 --- a/doc/ru/vcm.md +++ b/doc/ru/vcm.md @@ -22,6 +22,11 @@ На данный момент существует два вида примитивов: box и rect, а также, part (описывает часть примитива, такую как, например, сторона куба). +> [!NOTE] +> По-умолчанию текстурные координаты определяются размерами примитивов. +> При размерах более 1.0 результат не определён и может измениться в последующих обновлениях. +> Используйте `region` или `region-scale` для ручной настройки. + ### Свойства `rect` - `from` - точка начала примитива. Пример: `from (0,0.5,0.125)` From 08b5cccf95b3c8d2632366146a35ba2b7f533ae3 Mon Sep 17 00:00:00 2001 From: clasher113 Date: Fri, 29 Aug 2025 19:37:50 +0300 Subject: [PATCH 02/21] fix: content menu panel height --- res/layouts/pages/content.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/layouts/pages/content.xml b/res/layouts/pages/content.xml index 37025c69..dfa7b654 100644 --- a/res/layouts/pages/content.xml +++ b/res/layouts/pages/content.xml @@ -31,11 +31,11 @@ size='32' margin='0,0,425,64' gravity='bottom-right' color='#FFFFFF50' hover-color='#FFFFFF10'/> - + - + \ No newline at end of file From 77b251ef094c4561b6e5ecbeec090105f2c003b8 Mon Sep 17 00:00:00 2001 From: clasher113 Date: Fri, 29 Aug 2025 19:38:04 +0300 Subject: [PATCH 03/21] update dnf package name --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c3b512f6..4777cb25 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ sudo apt install libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopen #### RHEL based distros ```sh -sudo dnf install glfw-devel glfw glew-devel glm-devel libpng-devel libvorbis-devel openal-devel luajit-devel libcurl-devel +sudo dnf install glfw-devel glfw glew-devel glm-devel libpng-devel libvorbis-devel openal-soft-devel luajit-devel libcurl-devel ``` #### Arch based distros From 599061780379458c1e69d038e5557d61f2b64f1e Mon Sep 17 00:00:00 2001 From: clasher113 Date: Fri, 29 Aug 2025 19:38:57 +0300 Subject: [PATCH 04/21] fix: fullscreen monitor refresh rate --- src/window/detail/GLFWWindow.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/window/detail/GLFWWindow.cpp b/src/window/detail/GLFWWindow.cpp index cbf628af..3ac45a1f 100644 --- a/src/window/detail/GLFWWindow.cpp +++ b/src/window/detail/GLFWWindow.cpp @@ -418,7 +418,7 @@ public: if (fullscreen) { glfwGetWindowPos(window, &posX, &posY); glfwSetWindowMonitor( - window, monitor, 0, 0, mode->width, mode->height, GLFW_DONT_CARE + window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate ); } else { glfwSetWindowMonitor( @@ -596,6 +596,17 @@ static void cursor_pos_callback(GLFWwindow* window, double xpos, double ypos) { handler->input.setCursorPosition(xpos, ypos); } +static void iconify_callback(GLFWwindow* window, int iconified) { + auto handler = static_cast(glfwGetWindowUserPointer(window)); + if (handler->isFullscreen() && iconified == 0) { + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + glfwSetWindowMonitor( + window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate + ); + } +} + static void create_standard_cursors() { for (int i = 0; i <= static_cast(CursorShape::LAST); i++) { int cursor = GLFW_ARROW_CURSOR + i; @@ -615,6 +626,7 @@ static void setup_callbacks(GLFWwindow* window) { glfwSetWindowSizeCallback(window, window_size_callback); glfwSetCharCallback(window, character_callback); glfwSetScrollCallback(window, scroll_callback); + glfwSetWindowIconifyCallback(window, iconify_callback); } std::tuple< From 4362f1c912e0da7046f45305c7400f0255278cef Mon Sep 17 00:00:00 2001 From: clasher113 Date: Fri, 29 Aug 2025 19:39:41 +0300 Subject: [PATCH 05/21] fix: viewport size on toggle fullscreen --- src/window/detail/GLFWWindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/window/detail/GLFWWindow.cpp b/src/window/detail/GLFWWindow.cpp index 3ac45a1f..accd969f 100644 --- a/src/window/detail/GLFWWindow.cpp +++ b/src/window/detail/GLFWWindow.cpp @@ -19,6 +19,7 @@ static debug::Logger logger("window"); static std::unordered_set supported_gl_extensions; +static void window_size_callback(GLFWwindow* window, int width, int height); static void init_gl_extensions_list() { GLint numExtensions = 0; @@ -430,6 +431,7 @@ public: settings->height.get(), GLFW_DONT_CARE ); + window_size_callback(window, settings->width.get(), settings->height.get()); } double xPos, yPos; From 83f7146a14bf7949a00d4db1433b1849cc731881 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 20 Sep 2025 11:16:37 +0300 Subject: [PATCH 06/21] fix missing core:pathfinding 'reset_route' function --- res/scripts/components/pathfinding.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/res/scripts/components/pathfinding.lua b/res/scripts/components/pathfinding.lua index 2a6e51aa..2ae8b3e7 100644 --- a/res/scripts/components/pathfinding.lua +++ b/res/scripts/components/pathfinding.lua @@ -25,6 +25,10 @@ function get_route() return route end +function reset_route() + route = nil +end + function next_waypoint() if not route or #route == 0 then return From 1c54de2d762af718b3a75a9d29916876534a89d5 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 20 Sep 2025 11:29:15 +0300 Subject: [PATCH 07/21] fix missing core:mob 'get_dir' function --- res/scripts/components/mob.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/res/scripts/components/mob.lua b/res/scripts/components/mob.lua index 4a9efaaa..f81658a9 100644 --- a/res/scripts/components/mob.lua +++ b/res/scripts/components/mob.lua @@ -51,6 +51,7 @@ end function move_vertical(speed, vel) vel = vel or body:get_vel() + speed = speed or 1.0 vel[2] = vel[2] * 0.2 + props.movement_speed * speed * 0.8 body:set_vel(vel) end @@ -145,6 +146,10 @@ function set_dir(new_dir) dir = new_dir end +function get_dir() + return dir +end + function is_flight() return flight end function set_flight(flag) flight = flag end From e46d83e092204dc2c3df593b892c86b3f5368673 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 20 Sep 2025 11:29:34 +0300 Subject: [PATCH 08/21] add doc/ru/scripting/core_components.md --- doc/ru/scripting.md | 1 + doc/ru/scripting/core_components.md | 89 +++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 doc/ru/scripting/core_components.md diff --git a/doc/ru/scripting.md b/doc/ru/scripting.md index d8f1b3be..95a62007 100644 --- a/doc/ru/scripting.md +++ b/doc/ru/scripting.md @@ -45,6 +45,7 @@ - [Модуль core:bit_converter](scripting/modules/core_bit_converter.md) - [Модуль core:data_buffer](scripting/modules/core_data_buffer.md) - [Модули core:vector2, core:vector3](scripting/modules/core_vector2_vector3.md) +- [Встроенные компоненты сущностей](scripting/core_components.md) ## Аннотации типов данных diff --git a/doc/ru/scripting/core_components.md b/doc/ru/scripting/core_components.md new file mode 100644 index 00000000..a90cf1d8 --- /dev/null +++ b/doc/ru/scripting/core_components.md @@ -0,0 +1,89 @@ +# Встроенные компоненты + +## *core:pathfinding* + +Компонент для построение путей движения мобов. + +```lua +local pathfinding = entity:require_component("core:pathfinding") + +-- Устанавливает цель движения, не сбрасывая текущий маршрут +pathfinding.set_target(target: vec3) + +-- Возвращает текущую цель движения +pathfinding.get_target() --> vec3 + +-- Устанавливает высоту преодолимого прыжком препятствия +pathfinding.set_jump_height(height: number) + +-- Возвращает текущий построенный маршрут или nil +pathfinding.get_route() + +-- Сбрасывает текущий построенный маршрут +pathfinding.reset_route() + +-- Возвращает следующую точку маршрута, по текущим координатам. +-- (следует использовать компонент core:mob - функция mob.follow_waypoints) +pathfinding.next_waypoint() --> vec3 или nil + +-- Устанавливает интервал перестройки маршрута в тактах обновления. +pathfinding.set_refresh_interval(interval: number) +``` + +## *core:mob* + +Компонент для управления движением (включая полёт) и вращением мобов. + +```lua +local mob = entity:require_component("core:mob") + +-- Выполняет прыжок с силой jump_force * multiplier +mob.jump([опционально] multiplier: number = 1.0) + + +-- Вертикальное движение (работает в полёте или в плавании (в будущем)) +mob.move_vertical( + -- Скорость вертикального движения + speed: number, + -- Текущая скорость сущности (для минимизации вызовов rigidbody:get_vel()) + [опционально] current_velocity +) + +-- Горизонтальное движение +mob.go( + -- 2D вектор направления движения + dir: vec2, + -- Множитель скорости + speed_multiplier: number, + -- Бег + sprint: bool, + -- Присядь + crouch: bool, + -- Текущая скорость сущности (для минимизации вызовов rigidbody:get_vel()) + [опционально] current_velocity +) + +-- Меняет направление взгляда сущности, направляя на указанную точку +mob.look_at( + -- Целевая точка + point: vec3, + -- Менять ли направление всей сущности + change_dir: bool = false +) + +-- Движение по построенному маршруту. +-- Если не указан pathfinding, требуется наличие у сущности компонента core:pathfinding +mob.follow_waypoints( + -- Возможная замена компонента pathfinding + [опционально] pathfinding +) + +-- Устанавливает направление всей сущности +mob.set_dir(dir: vec3) + +-- Проверяет, включён ли режим полёта +mob.is_flight() --> bool + +-- Включает/выключает режим полёта +mob.set_flight(flag: bool) +``` From d990cbbdc4ca63bc55575951dab2062f4d8e33c2 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 20 Sep 2025 17:51:00 +0300 Subject: [PATCH 09/21] fix core:player component --- res/scripts/components/player.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/scripts/components/player.lua b/res/scripts/components/player.lua index 2a63f681..3bc58e68 100644 --- a/res/scripts/components/player.lua +++ b/res/scripts/components/player.lua @@ -53,7 +53,7 @@ end function on_physics_update(delta) local pid = entity:get_player() - if pid ~= -1 then + if pid ~= -1 and (hud and pid == hud.get_player()) then local pos = tsf:get_pos() local cam = cameras.get("core:first-person") process_player_inputs(pid, delta) From 49ba8e2185292593f29c48f328075830816ecf8c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 20 Sep 2025 22:03:48 +0300 Subject: [PATCH 10/21] remove extra blocks registering debug info output --- res/scripts/classes.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/res/scripts/classes.lua b/res/scripts/classes.lua index e950aa0d..f5457ef5 100644 --- a/res/scripts/classes.lua +++ b/res/scripts/classes.lua @@ -225,8 +225,6 @@ block.__process_register_events = function() end end end - - print(type, id, x, y, z) end end From 7dab7225614387f2fb160560708b211438080866 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 21 Sep 2025 23:26:03 +0300 Subject: [PATCH 11/21] fix core:player component --- res/scripts/components/player.lua | 36 ++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/res/scripts/components/player.lua b/res/scripts/components/player.lua index 3bc58e68..f3f37584 100644 --- a/res/scripts/components/player.lua +++ b/res/scripts/components/player.lua @@ -4,13 +4,22 @@ local mob = entity:require_component("core:mob") local cheat_speed_mul = 10.0 -local function process_player_inputs(pid, delta) +local function get_player_rotation(pid) + local rx, ry, rz = player.get_rot(pid) + local matrix = mat4.rotate({0, 1, 0}, rx) + mat4.rotate(matrix, {1, 0, 0}, ry, matrix) + mat4.rotate(matrix, {0, 0, 1}, rz, matrix) + return matrix +end + +local function process_player_inputs(pid, rot, delta) if not hud or hud.is_inventory_open() or menu.page ~= "" then return end - local cam = cameras.get("core:first-person") - local front = cam:get_front() - local right = cam:get_right() + + local front = mat4.mul(rot, {0, 0, -1}) + local right = mat4.mul(rot, {1, 0, 0}) + front[2] = 0.0 vec3.normalize(front, front) @@ -22,8 +31,6 @@ local function process_player_inputs(pid, delta) local isback = input.is_active('movement.back') local isleft = input.is_active('movement.left') local isright = input.is_active('movement.right') - mob.set_flight(player.is_flight(pid)) - body:set_body_type(player.is_noclip(pid) and "kinematic" or "dynamic") body:set_crouching(iscrouch) local vel = body:get_vel() @@ -53,10 +60,19 @@ end function on_physics_update(delta) local pid = entity:get_player() - if pid ~= -1 and (hud and pid == hud.get_player()) then + if pid == -1 then + return + end + + mob.set_flight(player.is_flight(pid)) + body:set_body_type(player.is_noclip(pid) and "kinematic" or "dynamic") + + if hud and pid == hud.get_player() then local pos = tsf:get_pos() - local cam = cameras.get("core:first-person") - process_player_inputs(pid, delta) - mob.look_at(vec3.add(pos, cam:get_front())) + local rot = get_player_rotation(pid) + local front = mat4.mul(rot, {0, 0, -1}) + + process_player_inputs(pid, rot, delta) + mob.look_at(vec3.add(pos, front)) end end From f02e6c65d88ed97df987725853892c1c7a86dc75 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 21 Sep 2025 23:46:09 +0300 Subject: [PATCH 12/21] initial fix --- res/layouts/pages/content.xml.lua | 45 +++++++++++++----------- src/logic/scripting/lua/libs/libpack.cpp | 9 ++++- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/res/layouts/pages/content.xml.lua b/res/layouts/pages/content.xml.lua index 8ed37630..611aca63 100644 --- a/res/layouts/pages/content.xml.lua +++ b/res/layouts/pages/content.xml.lua @@ -255,30 +255,33 @@ function check_dependencies(packinfo) if packinfo.dependencies == nil then return end - for i,dep in ipairs(packinfo.dependencies) do + for i, dep in ipairs(packinfo.dependencies) do local depid, depver = unpack(string.split(dep:sub(2,-1), "@")) - if dep:sub(1,1) == '!' then - if not table.has(packs_all, depid) then - return string.format( - "%s (%s)", gui.str("error.dependency-not-found"), depid - ) - end - - - local dep_pack = pack.get_info(depid); - - if not compare_version(depver, dep_pack.version) then - local op, ver = Version.parse(depver); - - print(string.format("%s: %s !%s %s (%s)", gui.str("error.dependency-version-not-met"), dep_pack.version, op, ver, depid)); - return string.format("%s: %s != %s (%s)", gui.str("error.dependency-version-not-met"), dep_pack.version, ver, depid); - end - - if table.has(packs_installed, packinfo.id) then - table.insert(required, depid) - end + if dep:sub(1,1) ~= '!' then + goto continue end + if not table.has(packs_all, depid) then + return string.format( + "%s (%s)", gui.str("error.dependency-not-found"), depid + ) + end + + local dep_pack = pack.get_info(depid); + + if not compare_version(depver, dep_pack.version) then + local op, ver = Version.parse(depver) + return string.format( + "%s: %s != %s (%s)", + gui.str("error.dependency-version-not-met"), + dep_pack.version, ver, depid + ); + end + + if table.has(packs_installed, packinfo.id) then + table.insert(required, depid) + end + ::continue:: end return end diff --git a/src/logic/scripting/lua/libs/libpack.cpp b/src/logic/scripting/lua/libs/libpack.cpp index 29ceef99..f5f952b7 100644 --- a/src/logic/scripting/lua/libs/libpack.cpp +++ b/src/logic/scripting/lua/libs/libpack.cpp @@ -115,7 +115,14 @@ static int l_pack_get_info( throw std::runtime_error(""); } - lua::pushfstring(L, "%s%s@%s%s", prefix.c_str(), dpack.id.c_str(), dpack.op.c_str(), dpack.version.c_str()); + lua::pushfstring( + L, + "%s%s@%s%s", + prefix.c_str(), + dpack.id.c_str(), + dpack.op != "=" ? dpack.op.c_str() : "", + dpack.version.c_str() + ); lua::rawseti(L, i + 1); } lua::setfield(L, "dependencies"); From 64b94a3e1bfcca9201e89a8a94a35ccaeecc9908 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 22 Sep 2025 00:06:21 +0300 Subject: [PATCH 13/21] cleanup --- src/content/ContentPack.cpp | 14 ++++++++++++-- src/content/ContentPack.hpp | 21 +++++++++++++++------ src/content/ContentPackVersion.cpp | 17 +---------------- src/content/ContentPackVersion.hpp | 19 ++++++++----------- src/content/PacksManager.cpp | 11 ++++++++--- src/logic/scripting/lua/libs/libpack.cpp | 15 +++++++++------ 6 files changed, 53 insertions(+), 44 deletions(-) diff --git a/src/content/ContentPack.cpp b/src/content/ContentPack.cpp index 522b818f..021f083e 100644 --- a/src/content/ContentPack.cpp +++ b/src/content/ContentPack.cpp @@ -1,3 +1,4 @@ +#define VC_ENABLE_REFLECTION #include "ContentPack.hpp" #include @@ -146,7 +147,7 @@ ContentPack ContentPack::read(const io::path& folder) { std::uint8_t op_size = 0; // Two symbol operators - if (op == ">=" || op == "=>" || op == "<=" || op == "=<") { + if (op == ">=" || op == "<=") { op_size = 2; depVerOperator = op; } @@ -169,7 +170,16 @@ ContentPack ContentPack::read(const io::path& folder) { } } - pack.dependencies.push_back({level, depName, depVer, depVerOperator}); + VersionOperator versionOperator; + if (VersionOperatorMeta.getItem(depVerOperator, versionOperator)) { + pack.dependencies.push_back( + {level, depName, depVer, versionOperator} + ); + } else { + throw contentpack_error( + pack.id, folder, "invalid version operator" + ); + } } } diff --git a/src/content/ContentPack.hpp b/src/content/ContentPack.hpp index 1aa3a9ea..bcc91ecb 100644 --- a/src/content/ContentPack.hpp +++ b/src/content/ContentPack.hpp @@ -1,14 +1,15 @@ #pragma once +#include "typedefs.hpp" +#include "content_fwd.hpp" +#include "io/io.hpp" +#include "util/EnumMetadata.hpp" + #include #include #include #include -#include "typedefs.hpp" -#include "content_fwd.hpp" -#include "io/io.hpp" - class EnginePaths; class contentpack_error : public std::runtime_error { @@ -25,11 +26,19 @@ public: io::path getFolder() const; }; -enum class DependencyVersionOperator { +enum class VersionOperator { EQUAL, GREATHER, LESS, GREATHER_OR_EQUAL, LESS_OR_EQUAL }; +VC_ENUM_METADATA(VersionOperator) + {"=", VersionOperator::EQUAL}, + {">", VersionOperator::GREATHER}, + {"<", VersionOperator::LESS}, + {">=", VersionOperator::GREATHER_OR_EQUAL}, + {"<=", VersionOperator::LESS_OR_EQUAL}, +VC_ENUM_END + enum class DependencyLevel { REQUIRED, // dependency must be installed OPTIONAL, // dependency will be installed if found @@ -41,7 +50,7 @@ struct DependencyPack { DependencyLevel level; std::string id; std::string version; - std::string op; + VersionOperator op; }; struct ContentPackStats { diff --git a/src/content/ContentPackVersion.cpp b/src/content/ContentPackVersion.cpp index 7a3d8801..7f32a291 100644 --- a/src/content/ContentPackVersion.cpp +++ b/src/content/ContentPackVersion.cpp @@ -26,26 +26,11 @@ Version::Version(const std::string& version) { if (parts.size() > 2) patch = parts[2]; } -DependencyVersionOperator Version::string_to_operator(const std::string& op) { - if (op == "=") - return DependencyVersionOperator::EQUAL; - else if (op == ">") - return DependencyVersionOperator::GREATHER; - else if (op == "<") - return DependencyVersionOperator::LESS; - else if (op == ">=" || op == "=>") - return DependencyVersionOperator::GREATHER_OR_EQUAL; - else if (op == "<=" || op == "=<") - return DependencyVersionOperator::LESS_OR_EQUAL; - else - return DependencyVersionOperator::EQUAL; -} - bool isNumber(const std::string& s) { return !s.empty() && std::all_of(s.begin(), s.end(), ::is_digit); } -bool Version::matches_pattern(const std::string& version) { +bool Version::matchesPattern(const std::string& version) { for (char c : version) { if (!isdigit(c) && c != '.') { return false; diff --git a/src/content/ContentPackVersion.hpp b/src/content/ContentPackVersion.hpp index e922ddd3..54deaf3c 100644 --- a/src/content/ContentPackVersion.hpp +++ b/src/content/ContentPackVersion.hpp @@ -33,25 +33,22 @@ public: return !(*this > other); } - bool process_operator(const std::string& op, const Version& other) const { - auto dep_op = Version::string_to_operator(op); - - switch (dep_op) { - case DependencyVersionOperator::EQUAL: + bool processOperator(VersionOperator op, const Version& other) const { + switch (op) { + case VersionOperator::EQUAL: return *this == other; - case DependencyVersionOperator::GREATHER: + case VersionOperator::GREATHER: return *this > other; - case DependencyVersionOperator::LESS: + case VersionOperator::LESS: return *this < other; - case DependencyVersionOperator::LESS_OR_EQUAL: + case VersionOperator::LESS_OR_EQUAL: return *this <= other; - case DependencyVersionOperator::GREATHER_OR_EQUAL: + case VersionOperator::GREATHER_OR_EQUAL: return *this >= other; default: return false; } } - static DependencyVersionOperator string_to_operator(const std::string& op); - static bool matches_pattern(const std::string& version); + static bool matchesPattern(const std::string& version); }; diff --git a/src/content/PacksManager.cpp b/src/content/PacksManager.cpp index 2d9759cb..3cbde0f2 100644 --- a/src/content/PacksManager.cpp +++ b/src/content/PacksManager.cpp @@ -1,3 +1,4 @@ +#define VC_ENABLE_REFLECTION #include "PacksManager.hpp" #include @@ -109,9 +110,9 @@ static bool resolve_dependencies( auto dep_pack = found -> second; - if (Version::matches_pattern(dep.version) && Version::matches_pattern(dep_pack.version) + if (Version::matchesPattern(dep.version) && Version::matchesPattern(dep_pack.version) && Version(dep_pack.version) - .process_operator(dep.op, Version(dep.version)) + .processOperator(dep.op, Version(dep.version)) ) { // dependency pack version meets the required one continue; @@ -120,7 +121,11 @@ static bool resolve_dependencies( continue; } else { throw contentpack_error( - dep.id, io::path(), "does not meet required version '" + dep.op + dep.version +"' of '" + pack->id + "'" + dep.id, + io::path(), + "does not meet required version '" + + VersionOperatorMeta.getNameString(dep.op) + dep.version + + "' of '" + pack->id + "'" ); } diff --git a/src/logic/scripting/lua/libs/libpack.cpp b/src/logic/scripting/lua/libs/libpack.cpp index f5f952b7..77eed4f0 100644 --- a/src/logic/scripting/lua/libs/libpack.cpp +++ b/src/logic/scripting/lua/libs/libpack.cpp @@ -1,8 +1,4 @@ -#include -#include -#include -#include -#include +#define VC_ENABLE_REFLECTION #include "assets/AssetsLoader.hpp" #include "content/Content.hpp" @@ -19,6 +15,12 @@ #include "world/World.hpp" #include "api_lua.hpp" +#include +#include +#include +#include +#include + using namespace scripting; static int l_pack_get_folder(lua::State* L) { @@ -114,13 +116,14 @@ static int l_pack_get_info( default: throw std::runtime_error(""); } + auto opString = VersionOperatorMeta.getNameString(dpack.op); lua::pushfstring( L, "%s%s@%s%s", prefix.c_str(), dpack.id.c_str(), - dpack.op != "=" ? dpack.op.c_str() : "", + (dpack.op == VersionOperator::EQUAL ? "" : opString).c_str(), dpack.version.c_str() ); lua::rawseti(L, i + 1); From 8b38d5796685fe356f230114d964ab7b59a4044b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 22 Sep 2025 23:52:50 +0300 Subject: [PATCH 14/21] fix body:set_gravity_scale & fix docs --- doc/en/scripting/ecs.md | 4 ++-- doc/ru/scripting/ecs.md | 4 ++-- res/scripts/components/mob.lua | 2 +- src/logic/scripting/lua/libs/lib__rigidbody.cpp | 7 ++++++- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/doc/en/scripting/ecs.md b/doc/en/scripting/ecs.md index e72723af..7da294c2 100644 --- a/doc/en/scripting/ecs.md +++ b/doc/en/scripting/ecs.md @@ -86,9 +86,9 @@ body:get_size() -> vec3 body:set_size(size: vec3) -- Returns the gravity multiplier -body:get_gravity_scale() -> vec3 +body:get_gravity_scale() -> number -- Sets the gravity multiplier -body:set_gravity_scale(scale: vec3) +body:set_gravity_scale(scale: number) -- Returns the linear velocity attenuation multiplier (used to simulate air resistance and friction) body:get_linear_damping() -> number diff --git a/doc/ru/scripting/ecs.md b/doc/ru/scripting/ecs.md index ea217b00..6c1b041d 100644 --- a/doc/ru/scripting/ecs.md +++ b/doc/ru/scripting/ecs.md @@ -86,9 +86,9 @@ body:get_size() -> vec3 body:set_size(size: vec3) -- Возвращает множитель гравитации -body:get_gravity_scale() -> vec3 +body:get_gravity_scale() -> number -- Устанавливает множитель гравитации -body:set_gravity_scale(scale: vec3) +body:set_gravity_scale(scale: number) -- Возвращает множитель затухания линейной скорости (используется для имитации сопротивления воздуха и трения) body:get_linear_damping() -> number diff --git a/res/scripts/components/mob.lua b/res/scripts/components/mob.lua index f81658a9..91ddf026 100644 --- a/res/scripts/components/mob.lua +++ b/res/scripts/components/mob.lua @@ -159,7 +159,7 @@ 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({0, flight and 0.0 or props.gravity_scale, 0}) + 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/src/logic/scripting/lua/libs/lib__rigidbody.cpp b/src/logic/scripting/lua/libs/lib__rigidbody.cpp index 71b6987b..726b7193 100644 --- a/src/logic/scripting/lua/libs/lib__rigidbody.cpp +++ b/src/logic/scripting/lua/libs/lib__rigidbody.cpp @@ -54,7 +54,12 @@ static int l_get_gravity_scale(lua::State* L) { static int l_set_gravity_scale(lua::State* L) { if (auto entity = get_entity(L, 1)) { - entity->getRigidbody().hitbox.gravityScale = lua::tovec3(L, 2).y; + auto& hitbox = entity->getRigidbody().hitbox; + if (lua::istable(L, 2)) { + hitbox.gravityScale = lua::tovec3(L, 2).y; + } else { + hitbox.gravityScale = lua::tonumber(L, 2); + } } return 0; } From 28c821006af35b70b9859a7572eab30e5902a2dc Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 23 Sep 2025 00:08:05 +0300 Subject: [PATCH 15/21] fix unicode string literal escape --- src/coders/BasicParser.hpp | 2 +- src/coders/BasicParser.inl | 9 ++++-- src/graphics/ui/markdown.hpp | 2 +- src/util/stringutil.cpp | 2 +- test/util/stringutil.cpp | 54 ++++++++++++++++++++++++++++++++++++ 5 files changed, 63 insertions(+), 6 deletions(-) diff --git a/src/coders/BasicParser.hpp b/src/coders/BasicParser.hpp index e0a4445a..3b756f57 100644 --- a/src/coders/BasicParser.hpp +++ b/src/coders/BasicParser.hpp @@ -32,7 +32,7 @@ protected: void goBack(size_t count = 1); void reset(); - int64_t parseSimpleInt(int base); + int64_t parseSimpleInt(int base, size_t maxLength = 0xFFFFFFFF); dv::value parseNumber(int sign); dv::value parseNumber(); StringT parseString(CharT chr, bool closeRequired = true); diff --git a/src/coders/BasicParser.inl b/src/coders/BasicParser.inl index 2e7a3623..7af27fe5 100644 --- a/src/coders/BasicParser.inl +++ b/src/coders/BasicParser.inl @@ -349,7 +349,10 @@ std::basic_string BasicParser::parseXmlName() { } template -int64_t BasicParser::parseSimpleInt(int base) { +int64_t BasicParser::parseSimpleInt(int base, size_t maxLength) { + if (maxLength == 0) return 0; + + size_t start = pos; CharT c = peek(); int index = hexchar2int(c); if (index == -1 || index >= base) { @@ -357,7 +360,7 @@ int64_t BasicParser::parseSimpleInt(int base) { } int64_t value = index; pos++; - while (hasNext()) { + while (hasNext() && pos - start < maxLength) { c = source[pos]; while (c == '_') { c = source[++pos]; @@ -476,7 +479,7 @@ std::basic_string BasicParser::parseString( continue; } if (c == 'u' || c == 'x') { - int codepoint = parseSimpleInt(16); + int codepoint = parseSimpleInt(16, c == 'u' ? 4 : 2); ubyte bytes[4]; int size = util::encode_utf8(codepoint, bytes); CharT chars[4]; diff --git a/src/graphics/ui/markdown.hpp b/src/graphics/ui/markdown.hpp index 0f089d58..39b2f1ec 100644 --- a/src/graphics/ui/markdown.hpp +++ b/src/graphics/ui/markdown.hpp @@ -22,7 +22,7 @@ namespace markdown { Result process(std::wstring_view source, bool eraseMarkdown); template - inline std::basic_string escape(std::string_view source) { + inline std::basic_string escape(std::basic_string_view source) { std::basic_stringstream ss; int pos = 0; while (pos < source.size()) { diff --git a/src/util/stringutil.cpp b/src/util/stringutil.cpp index 79f8a1c5..63271a13 100644 --- a/src/util/stringutil.cpp +++ b/src/util/stringutil.cpp @@ -40,7 +40,7 @@ std::string util::escape(std::string_view s, bool escapeUnicode) { uint cpsize; int codepoint = decode_utf8(cpsize, s.data() + pos); if (escapeUnicode) { - ss << "\\u" << std::hex << codepoint; + ss << "\\u" << std::setw(4) << std::setfill('0') << std::hex << codepoint; } else { ss << std::string(s.data() + pos, cpsize); } diff --git a/test/util/stringutil.cpp b/test/util/stringutil.cpp index fd459663..22df2b35 100644 --- a/test/util/stringutil.cpp +++ b/test/util/stringutil.cpp @@ -1,4 +1,5 @@ #include "util/stringutil.hpp" +#include "coders/BasicParser.hpp" #include @@ -16,6 +17,25 @@ TEST(stringutil, utf8) { EXPECT_EQ(str, str2); } +static std::wstring gen_random_unicode_wstring(int n) { + std::wstring str; + str.resize(n); + for (int i = 0; i < n; i++) { + // wstring is 16 bit in some systems + str[i] = rand() & 0xFFFF; + } + return str; +} + +TEST(stringutil, utf8_random) { + srand(5436324); + + auto str = gen_random_unicode_wstring(10'000); + auto utf8str = util::wstr2str_utf8(str); + auto back = util::str2wstr_utf8(utf8str); + EXPECT_EQ(str, back); +} + TEST(stringutil, base64) { srand(2019); for (size_t size = 0; size < 30; size++) { @@ -47,3 +67,37 @@ TEST(stringutil, base64_urlsafe) { } } } + +class StringParser : BasicParser { +public: + StringParser(std::string_view source) : BasicParser("", source) {} + + std::string parse() { + ++pos; + return parseString(source[0], true); + } +}; + +TEST(stringutil, escape_cases) { + auto escaped = util::escape("тест5", true); + auto expected = "\"\\u0442\\u0435\\u0441\\u04425\""; + ASSERT_EQ(expected, escaped); + + srand(345873458); + for (int i = 0; i < 36; i++) { + rand(); + } + + auto str = gen_random_unicode_wstring(40); + auto utf8str = util::wstr2str_utf8(str); + escaped = util::escape(utf8str, true); + + StringParser parser(escaped); + auto restored = parser.parse(); + for (int i = 0; i < utf8str.length(); i++) { + if (utf8str[i] != restored[i]) { + std::cout << i << ": " << (int)utf8str[i] << " " << (int)restored[i] << std::endl; + } + } + EXPECT_EQ(utf8str, restored); +} From 4ff18fec990fd4cabcc7e7eaf96453fb80dab88b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 23 Sep 2025 00:51:21 +0300 Subject: [PATCH 16/21] fix sky shader --- res/shaders/lib/shadows.glsl | 2 +- res/shaders/lib/sky.glsl | 2 +- res/shaders/skybox_gen.glslf | 6 +++++- src/graphics/core/Shadows.cpp | 6 +++++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/res/shaders/lib/shadows.glsl b/res/shaders/lib/shadows.glsl index 71ef4776..658e9a69 100644 --- a/res/shaders/lib/shadows.glsl +++ b/res/shaders/lib/shadows.glsl @@ -43,7 +43,7 @@ float calc_shadow( // TODO: add array textures support float calc_shadow(vec4 modelPos, vec3 realnormal, float distance) { #ifdef ENABLE_SHADOWS - float s = pow(abs(cos(u_dayTime * PI2)), 0.25) * u_shadowsOpacity; + float s = u_shadowsOpacity; vec3 normalOffset = realnormal * (distance > 64.0 ? 0.2 : 0.04); // as slow as mix(...) diff --git a/res/shaders/lib/sky.glsl b/res/shaders/lib/sky.glsl index 028fb51a..ce3604f5 100644 --- a/res/shaders/lib/sky.glsl +++ b/res/shaders/lib/sky.glsl @@ -4,7 +4,7 @@ #include vec3 pick_sky_color(samplerCube cubemap) { - vec3 skyLightColor = texture(cubemap, vec3(0.8f, 0.01f, 0.4f)).rgb; + vec3 skyLightColor = texture(cubemap, vec3(0.4f, 0.05f, 0.4f)).rgb; skyLightColor *= SKY_LIGHT_TINT; skyLightColor = min(vec3(1.0f), skyLightColor * SKY_LIGHT_MUL); skyLightColor = max(MIN_SKY_LIGHT, skyLightColor); diff --git a/res/shaders/skybox_gen.glslf b/res/shaders/skybox_gen.glslf index 481203f3..583104eb 100644 --- a/res/shaders/skybox_gen.glslf +++ b/res/shaders/skybox_gen.glslf @@ -268,7 +268,11 @@ void main() { camera_vector, // the camera vector (ray direction of this pixel) 1e12f, // max dist, essentially the scene depth vec3(0.0f), // scene color, the color of the current pixel being rendered - vec3(u_lightDir.x, pow(u_lightDir.y, 3.0), u_lightDir.z), // light direction + vec3( + u_lightDir.x, + u_lightDir.y, + u_lightDir.z + ), // light direction vec3(40.0*fog), // light intensity, 40 looks nice PLANET_POS, // position of the planet PLANET_RADIUS, // radius of the planet in meters diff --git a/src/graphics/core/Shadows.cpp b/src/graphics/core/Shadows.cpp index d37dc557..c47b8a6a 100644 --- a/src/graphics/core/Shadows.cpp +++ b/src/graphics/core/Shadows.cpp @@ -96,12 +96,16 @@ void Shadows::setup(Shader& shader, const Weather& weather) { if (shadows) { const auto& worldInfo = level.getWorld()->getInfo(); float cloudsIntensity = glm::max(worldInfo.fog, weather.clouds()); + float shadowsOpacity = 1.0f - cloudsIntensity; + shadowsOpacity *= glm::sqrt(glm::abs( + glm::mod((worldInfo.daytime + 0.5f) * 2.0f, 1.0f) * 2.0f - 1.0f + )); shader.uniform1i("u_screen", 0); shader.uniformMatrix("u_shadowsMatrix[0]", shadowCamera.getProjView()); shader.uniformMatrix("u_shadowsMatrix[1]", wideShadowCamera.getProjView()); shader.uniform3f("u_sunDir", shadowCamera.front); shader.uniform1i("u_shadowsRes", shadowMap->getResolution()); - shader.uniform1f("u_shadowsOpacity", 1.0f - cloudsIntensity); // TODO: make it configurable + shader.uniform1f("u_shadowsOpacity", shadowsOpacity); // TODO: make it configurable shader.uniform1f("u_shadowsSoftness", 1.0f + cloudsIntensity * 4); // TODO: make it configurable glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS0); From 370c95c14fdb518b0fabcb5b69b73b50433ca3ab Mon Sep 17 00:00:00 2001 From: Rost Alexeev <148265518+R0STUS@users.noreply.github.com> Date: Wed, 24 Sep 2025 20:34:16 +0200 Subject: [PATCH 17/21] Replace if-else chain with ArgC vector (#625) --- src/util/command_line.cpp | 97 +++++++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 35 deletions(-) diff --git a/src/util/command_line.cpp b/src/util/command_line.cpp index b6ef091b..8f8c981b 100644 --- a/src/util/command_line.cpp +++ b/src/util/command_line.cpp @@ -1,6 +1,9 @@ #include "command_line.hpp" #include +#include +#include +#include #include "io/engine_paths.hpp" #include "util/ArgsReader.hpp" @@ -8,45 +11,69 @@ namespace fs = std::filesystem; +class ArgC { + public: + std::string keyword; + std::function execute; + std::string help; + ArgC(const std::string& keyword, std::function execute, const std::string& help) { + this->keyword = keyword; + this->execute = execute; + this->help = help; + } +}; + + static bool perform_keyword( util::ArgsReader& reader, const std::string& keyword, CoreParameters& params ) { - if (keyword == "--res") { - params.resFolder = reader.next(); - } else if (keyword == "--dir") { - params.userFolder = reader.next(); - } else if (keyword == "--project") { - params.projectFolder = reader.next(); - } else if (keyword == "--help" || keyword == "-h") { - std::cout << "VoxelCore v" << ENGINE_VERSION_STRING << "\n\n"; - std::cout << "command-line arguments:\n"; - std::cout << " --help - display this help\n"; - std::cout << " --version - display engine version\n"; - std::cout << " --res - set resources directory\n"; - std::cout << " --dir - set userfiles directory\n"; - std::cout << " --project - set project directory\n"; - std::cout << " --headless - run in headless mode\n"; - std::cout << " --test - test script file\n"; - std::cout << " --script - main script file\n"; - std::cout << std::endl; - return false; - } else if (keyword == "--version") { - std::cout << ENGINE_VERSION_STRING << std::endl; - return false; - } else if (keyword == "--headless") { - params.headless = true; - } else if (keyword == "--test") { - auto token = reader.next(); - params.testMode = true; - params.scriptFile = token; - } else if (keyword == "--script") { - auto token = reader.next(); - params.testMode = false; - params.scriptFile = token; - } else { - throw std::runtime_error("unknown argument " + keyword); + static const std::vector argumentsCommandline = { + ArgC("--res", [¶ms, &reader]() -> bool { + params.resFolder = reader.next(); + return true; + }, " - set resources directory."), + ArgC("--dir", [¶ms, &reader]() -> bool { + params.userFolder = reader.next(); + return true; + }, " - set userfiles directory."), + ArgC("--project", [¶ms, &reader]() -> bool { + params.projectFolder = reader.next(); + return true; + }, " - set project directory."), + ArgC("--test", [¶ms, &reader]() -> bool { + params.testMode = true; + params.scriptFile = reader.next(); + return true; + }, " - test script file."), + ArgC("--script", [¶ms, &reader]() -> bool { + params.testMode = false; + params.scriptFile = reader.next(); + return true; + }, " - main script file."), + ArgC("--headless", [¶ms]() -> bool { + params.headless = true; + return true; + }, "- run in headless mode."), + ArgC("--version", []() -> bool { + std::cout << ENGINE_VERSION_STRING << std::endl; + return false; + }, "- display the engine version."), + ArgC("--help", []() -> bool { + std::cout << "VoxelCore v" << ENGINE_VERSION_STRING << "\n\n"; + std::cout << "Command-line arguments:\n"; + for (auto& a : argumentsCommandline) { + std::cout << a.keyword << " " << a.help << std::endl; + } + std::cout << std::endl; + return false; + }, "- display this help.") + }; + for (auto& a : argumentsCommandline) { + if (a.keyword == keyword) { + return a.execute(); + } } - return true; + throw std::runtime_error("unknown argument " + keyword); } bool parse_cmdline(int argc, char** argv, CoreParameters& params) { From 3a40162f1ef13809acffcdf39b8dbf5fed0bd59c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 25 Sep 2025 17:02:42 +0300 Subject: [PATCH 18/21] Fix udp address resolve (#624) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * тест для udp + фикс udp * fix udp addresses resolve * update SocketUdpConnection::connect * update SocketUdpServer::sendTo * update udp test * revert * test * test * test * test * test * test? * update test * cleanup * cleanup * update test * update * revert * cleanup * update test * update test * update * fix test * additional error handling * update Network.cpp * update Network.cpp 2 * fix the test * cleanup Network.cpp * revert network_tcp.lua extra changes --------- Co-authored-by: Xertis <118364459+Xertis@users.noreply.github.com> --- dev/tests/network_tcp.lua | 2 +- dev/tests/network_udp.lua | 35 +++++++++++++++++++++++++++++ res/scripts/classes.lua | 16 ++++++++++--- src/network/Network.cpp | 47 ++++++++++++++++++++++++++------------- 4 files changed, 80 insertions(+), 20 deletions(-) create mode 100644 dev/tests/network_udp.lua diff --git a/dev/tests/network_tcp.lua b/dev/tests/network_tcp.lua index eb6b1880..37906547 100644 --- a/dev/tests/network_tcp.lua +++ b/dev/tests/network_tcp.lua @@ -1,5 +1,5 @@ for i=1,3 do - print(string.format("iteration %s", i + 1)) + print(string.format("iteration %s", i)) local text = "" local complete = false diff --git a/dev/tests/network_udp.lua b/dev/tests/network_udp.lua new file mode 100644 index 00000000..975321a4 --- /dev/null +++ b/dev/tests/network_udp.lua @@ -0,0 +1,35 @@ +math.randomseed(43172) +for i = 1, 15 do + debug.log(string.format("iteration %s", i)) + local complete = false + + local server = network.udp_open(8645 + i, function (address, port, data, srv) + debug.log(string.format("server received %s byte(s) from %s:%s", #data, address, port)) + srv:send(address, port, "pong") + end) + + app.tick() + network.udp_connect("localhost", 8645 + i, function (data) + debug.log(string.format("client received %s byte(s) from server", #data)) + complete = true + end, function (socket) + debug.log("udp socket opened") + start_coroutine(function() + debug.log("udp data-sender started") + for k = 1, 15 do + local payload = "" + for j = 1, 16 do + payload = payload .. math.random(0, 9) + end + socket:send(payload) + debug.log(string.format("sent packet %s (%s bytes)", k, #payload)) + coroutine.yield() + end + app.sleep_until(function () return complete end, nil, 5) + socket:close() + end, "udp-data-sender") + end) + + app.sleep_until(function () return complete end, nil, 5) + server:close() +end diff --git a/res/scripts/classes.lua b/res/scripts/classes.lua index f5457ef5..49d11268 100644 --- a/res/scripts/classes.lua +++ b/res/scripts/classes.lua @@ -144,7 +144,11 @@ network.udp_connect = function (address, port, datagramHandler, openCallback) socket.id = network.__connect_udp(address, port) _udp_client_datagram_callbacks[socket.id] = datagramHandler - _udp_client_open_callbacks[socket.id] = openCallback + if openCallback then + _udp_client_open_callbacks[socket.id] = function() + openCallback(socket) + end + end return socket end @@ -254,9 +258,15 @@ network.__process_events = function() end elseif etype == DATAGRAM then if side == ON_CLIENT then - _udp_client_datagram_callbacks[cid](data) + local callback = _udp_client_datagram_callbacks[cid] + if callback then + callback(data) + end elseif side == ON_SERVER then - _udp_server_callbacks[sid](addr, port, data) + local callback = _udp_server_callbacks[sid] + if callback then + callback(addr, port, data) + end end elseif etype == RESPONSE then if event[2] / 100 == 2 then diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 7d33536c..b3ff07cf 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -620,6 +620,26 @@ public: } }; +static sockaddr_in resolve_address_dgram(const std::string& address, int port) { + sockaddr_in serverAddr{}; + addrinfo hints {}; + + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + + addrinfo* addrinfo = nullptr; + if (int res = getaddrinfo( + address.c_str(), nullptr, &hints, &addrinfo + )) { + throw std::runtime_error(gai_strerror(res)); + } + + std::memcpy(&serverAddr, addrinfo->ai_addr, sizeof(sockaddr_in)); + serverAddr.sin_port = htons(port); + freeaddrinfo(addrinfo); + return serverAddr; +} + class SocketUdpConnection : public UdpConnection { u64id_t id; SOCKET descriptor; @@ -652,13 +672,7 @@ public: throw std::runtime_error("could not create udp socket"); } - sockaddr_in serverAddr{}; - serverAddr.sin_family = AF_INET; - if (inet_pton(AF_INET, address.c_str(), &serverAddr.sin_addr) <= 0) { - closesocket(descriptor); - throw std::runtime_error("invalid udp address: " + address); - } - serverAddr.sin_port = htons(port); + sockaddr_in serverAddr = resolve_address_dgram(address, port); if (::connect(descriptor, (sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) { auto err = handle_socket_error("udp connect failed"); @@ -683,6 +697,7 @@ public: while (open) { int size = recv(descriptor, buffer.data(), buffer.size(), 0); if (size <= 0) { + logger.error() <(&client), sizeof(client)); + sockaddr_in client = resolve_address_dgram(addr, port); + if (sendto(descriptor, buffer, length, 0, + reinterpret_cast(&client), sizeof(client)) < 0) { + logger.error() << handle_socket_error("sendto").what(); + } } void close() override { From 088bf63f3f336dd16267f2a59b3c4b725853531a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 25 Sep 2025 23:53:11 +0300 Subject: [PATCH 19/21] replace lua Random implementation with usertype --- res/modules/internal/random_generator.lua | 35 ------------- res/scripts/stdmin.lua | 31 +++++++++++- src/logic/scripting/lua/libs/librandom.cpp | 4 +- src/logic/scripting/lua/lua_custom_types.hpp | 17 +++++++ src/logic/scripting/lua/lua_engine.cpp | 8 +++ src/logic/scripting/lua/lua_util.hpp | 9 ++++ .../lua/usertypes/lua_type_random.cpp | 49 +++++++++++++++++++ 7 files changed, 115 insertions(+), 38 deletions(-) delete mode 100644 res/modules/internal/random_generator.lua create mode 100644 src/logic/scripting/lua/usertypes/lua_type_random.cpp diff --git a/res/modules/internal/random_generator.lua b/res/modules/internal/random_generator.lua deleted file mode 100644 index 0c9073cc..00000000 --- a/res/modules/internal/random_generator.lua +++ /dev/null @@ -1,35 +0,0 @@ -local Random = {} - -local M = 2 ^ 31 -local A = 1103515245 -local C = 12345 - -function Random.randint(self) - self._seed = (A * self._seed + C) % M - return self._seed -end - -function Random.random(self, a, b) - local num = self:randint() % M / M - if b then - return math.floor(num * (b - a + 1) + a) - elseif a then - return math.floor(num * a + 1) - else - return num - end -end - -function Random.seed(self, number) - if type(number) ~= "number" then - error("number expected") - end - self._seed = number -end - -return function(seed) - if seed and type(seed) ~= "number" then - error("number expected") - end - return setmetatable({_seed = seed or random.random(M)}, {__index = Random}) -end diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 742c9506..7fb2eca8 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -669,4 +669,33 @@ end bit.compile = require "core:bitwise/compiler" bit.execute = require "core:bitwise/executor" -random.Random = require "core:internal/random_generator" +function __vc_create_random_methods(random_methods) + local index = 1 + local buffer = nil + local buffer_size = 64 + + function random_methods:bytes(n) + local bytes = Bytearray(n) + for i=1,n do + bytes[i] = self:random(255) + end + return bytes + end + + function random_methods:random(a, b) + if not buffer or index > #buffer then + buffer = self:generate(buffer_size) + index = 1 + end + local value = buffer[index] + if b then + value = math.floor(value * (b - a + 1) + a) + elseif a then + value = math.floor(value * a + 1) + end + + index = index + 4 + return value + end + return random_methods +end diff --git a/src/logic/scripting/lua/libs/librandom.cpp b/src/logic/scripting/lua/libs/librandom.cpp index 192f83d1..b86bda58 100644 --- a/src/logic/scripting/lua/libs/librandom.cpp +++ b/src/logic/scripting/lua/libs/librandom.cpp @@ -22,7 +22,7 @@ static int l_random(lua::State* L) { } } -static int l_bytes(lua::State* L) { +static int l_generate(lua::State* L) { size_t size = lua::tointeger(L, 1); auto randomEngine = util::seeded_random_engine(random_device); @@ -40,7 +40,7 @@ static int l_uuid(lua::State* L) { const luaL_Reg randomlib[] = { {"random", lua::wrap}, - {"bytes", lua::wrap}, + {"bytes", lua::wrap}, {"uuid", lua::wrap}, {NULL, NULL} }; diff --git a/src/logic/scripting/lua/lua_custom_types.hpp b/src/logic/scripting/lua/lua_custom_types.hpp index 87080274..c17a78bc 100644 --- a/src/logic/scripting/lua/lua_custom_types.hpp +++ b/src/logic/scripting/lua/lua_custom_types.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "lua_commons.hpp" @@ -114,4 +115,20 @@ namespace lua { std::shared_ptr mData; }; static_assert(!std::is_abstract()); + + class LuaRandom : public Userdata { + public: + std::mt19937 rng; + + explicit LuaRandom(uint64_t seed) : rng(seed) {} + virtual ~LuaRandom() override = default; + + const std::string& getTypeName() const override { + return TYPENAME; + } + + static int createMetatable(lua::State*); + inline static std::string TYPENAME = "__vc_Random"; + }; + static_assert(!std::is_abstract()); } diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 891614f0..6ffa9057 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -169,5 +169,13 @@ State* lua::create_state(const EnginePaths& paths, StateType stateType) { auto file = "res:scripts/stdmin.lua"; auto src = io::read_string(file); lua::pop(L, lua::execute(L, 0, src, "core:scripts/stdmin.lua")); + + newusertype(L); + if (getglobal(L, "random")) { + if (getglobal(L, "__vc_Random")) { + setfield(L, "Random"); + } + pop(L); + } return L; } diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index 121ebe29..75f30b54 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -275,6 +275,15 @@ namespace lua { } return nullptr; } + + template + inline T& require_userdata(lua::State* L, int idx) { + if (void* rawptr = lua_touserdata(L, idx)) { + return *static_cast(rawptr); + } + throw std::runtime_error("invalid 'self' value"); + } + template inline int newuserdata(lua::State* L, Args&&... args) { const auto& found = usertypeNames.find(typeid(T)); diff --git a/src/logic/scripting/lua/usertypes/lua_type_random.cpp b/src/logic/scripting/lua/usertypes/lua_type_random.cpp new file mode 100644 index 00000000..4d6a876c --- /dev/null +++ b/src/logic/scripting/lua/usertypes/lua_type_random.cpp @@ -0,0 +1,49 @@ +#include "../lua_custom_types.hpp" +#include "../lua_util.hpp" + +#include + +using namespace lua; +using namespace std::chrono; + +static int l_generate(lua::State* L) { + std::uniform_int_distribution<> dist(0, std::numeric_limits::max()); + + auto& rng = require_userdata(L, 1).rng; + size_t n = touinteger(L, 2); + createtable(L, n, 0); + + for (size_t i = 0; i < n; i++) { + pushnumber(L, dist(rng) / (double)std::numeric_limits::max()); + rawseti(L, i + 1); + } + return 1; +} + +static int l_meta_meta_call(lua::State* L) { + integer_t seed; + if (lua::isnoneornil(L, 1)) { + seed = system_clock::now().time_since_epoch().count(); + } else { + seed = tointeger(L, 1); + } + return newuserdata(L, seed); +} + +int LuaRandom::createMetatable(lua::State* L) { + createtable(L, 0, 3); + + requireglobal(L, "__vc_create_random_methods"); + createtable(L, 0, 0); + pushcfunction(L, wrap); + setfield(L, "generate"); + call(L, 1, 1); + + setfield(L, "__index"); + + createtable(L, 0, 1); + pushcfunction(L, wrap); + setfield(L, "__call"); + setmetatable(L); + return 1; +} From 1350910d28f3410d3aedaeec8b90eced08347c8c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 26 Sep 2025 00:25:00 +0300 Subject: [PATCH 20/21] fix: missing Random:seed method --- res/scripts/stdmin.lua | 10 +++++++++- src/logic/scripting/lua/libs/librandom.cpp | 4 ++-- .../scripting/lua/usertypes/lua_type_random.cpp | 13 ++++++++++--- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 7fb2eca8..bbf3e7e6 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -674,6 +674,9 @@ function __vc_create_random_methods(random_methods) local buffer = nil local buffer_size = 64 + local seed_func = random_methods.seed + local random_func = random_methods.random + function random_methods:bytes(n) local bytes = Bytearray(n) for i=1,n do @@ -682,9 +685,14 @@ function __vc_create_random_methods(random_methods) return bytes end + function random_methods:seed(x) + seed_func(self, x) + buffer = nil + end + function random_methods:random(a, b) if not buffer or index > #buffer then - buffer = self:generate(buffer_size) + buffer = random_func(self, buffer_size) index = 1 end local value = buffer[index] diff --git a/src/logic/scripting/lua/libs/librandom.cpp b/src/logic/scripting/lua/libs/librandom.cpp index b86bda58..192f83d1 100644 --- a/src/logic/scripting/lua/libs/librandom.cpp +++ b/src/logic/scripting/lua/libs/librandom.cpp @@ -22,7 +22,7 @@ static int l_random(lua::State* L) { } } -static int l_generate(lua::State* L) { +static int l_bytes(lua::State* L) { size_t size = lua::tointeger(L, 1); auto randomEngine = util::seeded_random_engine(random_device); @@ -40,7 +40,7 @@ static int l_uuid(lua::State* L) { const luaL_Reg randomlib[] = { {"random", lua::wrap}, - {"bytes", lua::wrap}, + {"bytes", lua::wrap}, {"uuid", lua::wrap}, {NULL, NULL} }; diff --git a/src/logic/scripting/lua/usertypes/lua_type_random.cpp b/src/logic/scripting/lua/usertypes/lua_type_random.cpp index 4d6a876c..99177056 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_random.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_random.cpp @@ -6,7 +6,7 @@ using namespace lua; using namespace std::chrono; -static int l_generate(lua::State* L) { +static int l_random(lua::State* L) { std::uniform_int_distribution<> dist(0, std::numeric_limits::max()); auto& rng = require_userdata(L, 1).rng; @@ -20,6 +20,11 @@ static int l_generate(lua::State* L) { return 1; } +static int l_seed(lua::State* L) { + require_userdata(L, 1).rng = std::mt19937(lua::touinteger(L, 2)); + return 0; +} + static int l_meta_meta_call(lua::State* L) { integer_t seed; if (lua::isnoneornil(L, 1)) { @@ -35,8 +40,10 @@ int LuaRandom::createMetatable(lua::State* L) { requireglobal(L, "__vc_create_random_methods"); createtable(L, 0, 0); - pushcfunction(L, wrap); - setfield(L, "generate"); + pushcfunction(L, wrap); + setfield(L, "random"); + pushcfunction(L, wrap); + setfield(L, "seed"); call(L, 1, 1); setfield(L, "__index"); From c15302510ac492eea4757fdbfac7a1c2501af7cb Mon Sep 17 00:00:00 2001 From: annel0 Date: Sat, 27 Sep 2025 14:06:51 +0300 Subject: [PATCH 21/21] docs: fix admonition syntax, improve build instructions, and add --parallel flag (#628) * docs: fix admonition syntax, improve build instructions, and add --parallel flag * docs: add libglfw3 to Debian dependencies per author note --- README.md | 117 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 71 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 4777cb25..30925fef 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ - [Download](https://github.com/MihailRis/VoxelCore/releases/latest) | [Скачать](https://github.com/MihailRis/VoxelCore/releases/latest) - [Documentation](https://github.com/MihailRis/VoxelCore/blob/release-0.28/doc/en/main-page.md) | [Документация](https://github.com/MihailRis/VoxelCore/blob/release-0.28/doc/ru/main-page.md) +--- + ## Build project in Linux ### Install libraries @@ -13,13 +15,14 @@ ```sh git clone https://github.com/skypjack/entt.git -cd entt/build -cmake -DCMAKE_BUILD_TYPE=Release -DENTT_INSTALL=on .. +cd entt +mkdir build && cd build +cmake -DCMAKE_BUILD_TYPE=Release -DENTT_INSTALL=ON .. sudo make install ``` > [!WARNING] -> If you are using ALT Linux, you should not use this EnTT installation method +> If you are using ALT Linux, do **not** use this EnTT installation method. #### ALT Linux based distros @@ -31,11 +34,11 @@ apt-get install entt-devel libglfw3-devel libGLEW-devel libglm-devel libpng-deve #### Debian based distros ```sh -sudo apt install libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev +sudo apt install libglfw3 libglfw3-dev libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev ``` > [!TIP] -> CMake missing LUA_INCLUDE_DIR and LUA_LIBRARIES fix: +> CMake missing `LUA_INCLUDE_DIR` and `LUA_LIBRARIES` fix: > > ```sh > sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua5.1.a @@ -45,24 +48,24 @@ sudo apt install libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopen #### RHEL based distros ```sh -sudo dnf install glfw-devel glfw glew-devel glm-devel libpng-devel libvorbis-devel openal-soft-devel luajit-devel libcurl-devel +sudo dnf install glfw-devel glew-devel glm-devel libpng-devel libvorbis-devel openal-soft-devel luajit-devel libcurl-devel ``` #### Arch based distros -If you use X11 +If you use X11: ```sh sudo pacman -S glfw-x11 glew glm libpng libvorbis openal luajit libcurl ``` -If you use Wayland +If you use Wayland: ```sh sudo pacman -S glfw-wayland glew glm libpng libvorbis openal luajit libcurl ``` -And you need entt. In yay you can use +And install EnTT: ```sh yay -S entt @@ -76,9 +79,14 @@ cd VoxelCore mkdir build cd build cmake -DCMAKE_BUILD_TYPE=Release .. -cmake --build . +cmake --build . --parallel ``` +> [!TIP] +> Use `--parallel` to utilize all CPU cores during build. + +--- + ## Building project in macOS ### Install libraries @@ -88,9 +96,7 @@ brew install glfw3 glew glm libpng libvorbis lua luajit libcurl openal-soft skyp ``` > [!TIP] -> If homebrew for some reason could not install the necessary packages: -> ```lua luajit openal-soft```, then download, install and compile them manually -> (Lua, LuaJIT and OpenAL). +> If Homebrew fails to install `lua`, `luajit`, or `openal-soft`, download, compile, and install them manually. ### Building engine with CMake @@ -100,90 +106,109 @@ cd VoxelCore mkdir build cd build cmake -DCMAKE_BUILD_TYPE=Release .. -cmake --build . +cmake --build . --parallel ``` +--- + ## Building in Windows ->[!NOTE] -> Requirement: -> -> vcpkg, CMake, Git +> [!NOTE] +> Requirements: **vcpkg**, **CMake**, **Git**, and **Visual Studio** (with C++ tools). + There are two options to use vcpkg: -1. If you have Visual Studio installed, most likely the **VCPKG_ROOT** environment variable will already exist in **Developer Command Prompt for VS** -2. If you want use **vcpkg**, install **vcpkg** from git to you system: -```PowerShell -cd C:/ + +1. If you have Visual Studio installed, the **VCPKG_ROOT** environment variable is often already set in the **Developer Command Prompt for VS**. +2. Otherwise, install **vcpkg** manually: + +```powershell +cd C:\ git clone https://github.com/microsoft/vcpkg.git cd vcpkg .\bootstrap-vcpkg.bat ``` -After installing **vcpkg**, setup env variable **VCPKG_ROOT** and add it to **PATH**: -```PowerShell -$env:VCPKG_ROOT = "C:\path\to\vcpkg" + +Then set the `VCPKG_ROOT` environment variable and add it to `PATH`: + +```powershell +$env:VCPKG_ROOT = "C:\vcpkg" $env:PATH = "$env:VCPKG_ROOT;$env:PATH" ``` ->[!TIP] ->For troubleshooting you can read full [documentation](https://learn.microsoft.com/ru-ru/vcpkg/get_started/get-started?pivots=shell-powershell) for **vcpkg** -After installing **vcpkg** you can build project: -```PowerShell +> [!TIP] +> For troubleshooting, refer to the official [vcpkg documentation](https://learn.microsoft.com/ru-ru/vcpkg/get_started/get-started?pivots=shell-powershell). + +After installing **vcpkg**, build the project: + +```powershell git clone --recursive https://github.com/MihailRis/VoxelCore.git cd VoxelCore cmake --preset default-vs-msvc-windows cmake --build --preset default-vs-msvc-windows ``` +> [!NOTE] +> Make sure your `CMakeUserPresets.json` (if used) contains the correct `VCPKG_ROOT` path. + +--- + ## Build using Docker -### Step 0. Install docker on your system +> [!NOTE] +> First, install Docker Engine: [https://docs.docker.com/engine/install](https://docs.docker.com/engine/install) -See +### On Linux -### Do you have Linux - -### Step 1. Build docker container +#### Step 1. Build Docker image ```sh docker build -t voxel-engine . ``` -### Step 2. Build project using the docker container +#### Step 2. Build project inside container ```sh -docker run --rm -it -v$(pwd):/project voxel-engine bash -c "cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && cmake --build build" +docker run --rm -it -v "$(pwd):/project" voxel-engine bash -c "cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && cmake --build build --parallel" ``` -### Step 3. Run project using the docker container +#### Step 3. Run the application (requires X11 forwarding) ```sh -docker run --rm -it -v$(pwd):/project -v/tmp/.X11-unix:/tmp/.X11-unix -v${XAUTHORITY}:/home/user/.Xauthority:ro -eDISPLAY --network=host voxel-engine ./build/VoxelEngine +docker run --rm -it \ + -v "$(pwd):/project" \ + -v /tmp/.X11-unix:/tmp/.X11-unix \ + -v "$XAUTHORITY:/home/user/.Xauthority:ro" \ + -e DISPLAY="$DISPLAY" \ + --network=host \ + voxel-engine ./build/VoxelEngine ``` -### Do you have Windows +### On Windows -### Step 1. You need to install VcXsrv +> [!NOTE] +> You need an X server like **VcXsrv** to display the GUI. -### Step 2. Run VcXsrv with the command +#### Step 1. Install and run VcXsrv +Launch with: ```powershell .\vcxsrv.exe :0 -multiwindow -ac ``` -### Step 3. Build docker container +#### Step 2. Build Docker image ```powershell docker build -t voxel-engine . ``` -### Step 4. Build project using the docker container +#### Step 3. Build project ```powershell -docker run --rm -it -v "${PWD}:/project" voxel-engine bash -c "cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && cmake --build build" +docker run --rm -it -v "${PWD}:/project" voxel-engine bash -c "cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && cmake --build build --parallel" ``` -### Step 5. Run project using the docker container +#### Step 4. Run the application ```powershell -docker run --rm -it -v "${PWD}:/project" -e DISPLAY=host.docker.internal:0.0 --network host voxel-engine ./build/VoxelEngine +docker run --rm -it -v "${PWD}:/project" -e DISPLAY=host.docker.internal:0.0 --network=host voxel-engine ./build/VoxelEngine ```