From be4dda36461e57c2ccef95f9a4a2c6dd53afc346 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 12 May 2026 02:29:48 +0300 Subject: [PATCH 01/33] add time.precise_time --- src/logic/scripting/lua/libs/libtime.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/logic/scripting/lua/libs/libtime.cpp b/src/logic/scripting/lua/libs/libtime.cpp index f953955be..342226035 100644 --- a/src/logic/scripting/lua/libs/libtime.cpp +++ b/src/logic/scripting/lua/libs/libtime.cpp @@ -62,11 +62,20 @@ static int l_utc_offset(lua::State* L) { return lua::pushnumber(L, offset); } +static auto start_time = std::chrono::steady_clock::now(); + +static int l_precise_time(lua::State* L) { + auto time = std::chrono::steady_clock::now(); + std::chrono::duration delta = time - start_time; + return lua::pushnumber(L, delta.count()); +} + const luaL_Reg timelib[] = { {"uptime", lua::wrap}, {"delta", lua::wrap}, {"utc_time", lua::wrap}, {"utc_offset", lua::wrap}, {"local_time", lua::wrap}, + {"precise_time", lua::wrap}, {nullptr, nullptr} }; From c7f35dd6be0fe8489f2182c8b536bc18932e09bc Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 12 May 2026 02:47:47 +0300 Subject: [PATCH 02/33] replace os.clock with preciser implementation --- src/logic/scripting/lua/lua_extensions.cpp | 12 ++++++++++++ src/util/platform.cpp | 18 ++++++++++++++++++ src/util/platform.hpp | 2 ++ 3 files changed, 32 insertions(+) diff --git a/src/logic/scripting/lua/lua_extensions.cpp b/src/logic/scripting/lua/lua_extensions.cpp index a7de3fdd8..f8a200de1 100644 --- a/src/logic/scripting/lua/lua_extensions.cpp +++ b/src/logic/scripting/lua/lua_extensions.cpp @@ -8,6 +8,7 @@ #include "engine/Engine.hpp" #include "devtools/DebuggingServer.hpp" #include "logic/scripting/scripting.hpp" +#include "util/platform.hpp" using namespace devtools; using namespace scripting; @@ -381,6 +382,10 @@ static int l_debug_is_debugging(lua::State* L) { return lua::pushboolean(L, engine->getDebuggingServer() != nullptr); } +static int l_os_clock(lua::State* L) { + return lua::pushnumber(L, platform::clock()); +} + void initialize_libs_extends(lua::State* L) { if (lua::getglobal(L, "debug")) { lua::pushcfunction(L, lua::wrap); @@ -415,4 +420,11 @@ void initialize_libs_extends(lua::State* L) { lua::pop(L); } + + if (lua::getglobal(L, "os")) { + lua::pushcfunction(L, lua::wrap); + lua::setfield(L, "clock"); + + lua::pop(L); + } } diff --git a/src/util/platform.cpp b/src/util/platform.cpp index f113641e5..d2df3e77e 100644 --- a/src/util/platform.cpp +++ b/src/util/platform.cpp @@ -327,3 +327,21 @@ bool platform::stdin_has_data() { return poll(&fds, 1, 0) == 1; #endif } + +double platform::clock() { +#ifdef _WIN32 + FILETIME createTime, exitTime, kernelTime, userTime; + + if (GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime)) { + ULARGE_INTEGER kernel, user; + kernel.LowPart = kernelTime.dwLowDateTime; + kernel.HighPart = kernelTime.dwHighDateTime; + user.LowPart = userTime.dwLowDateTime; + user.HighPart = userTime.dwHighDateTime; + + return static_cast(kernel.QuadPart + user.QuadPart) / 1000'0000.0; + } +#else + return std::clock() / static_cast(CLOCKS_PER_SEC); +#endif +} diff --git a/src/util/platform.hpp b/src/util/platform.hpp index 54c56a0c5..4c049a0da 100644 --- a/src/util/platform.hpp +++ b/src/util/platform.hpp @@ -25,4 +25,6 @@ namespace platform { bool open_url(const std::string& url); /// @brief Check if stdin has input bool stdin_has_data(); + /// @brief Precise wrapper for std::clock (especially for Windows) + double clock(); } From bcbf09c91642359588d77297e9948fa83d073946 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 12 May 2026 03:03:04 +0300 Subject: [PATCH 03/33] update platform::clock --- src/util/platform.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/util/platform.cpp b/src/util/platform.cpp index d2df3e77e..026c6feb4 100644 --- a/src/util/platform.cpp +++ b/src/util/platform.cpp @@ -331,8 +331,10 @@ bool platform::stdin_has_data() { double platform::clock() { #ifdef _WIN32 FILETIME createTime, exitTime, kernelTime, userTime; + + static HANDLE currentProcess = GetCurrentProcess(); - if (GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime)) { + if (GetProcessTimes(currentProcess, &createTime, &exitTime, &kernelTime, &userTime)) { ULARGE_INTEGER kernel, user; kernel.LowPart = kernelTime.dwLowDateTime; kernel.HighPart = kernelTime.dwHighDateTime; From 7c2b7bd8139eec83f994ffb842a90165415e2083 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 12 May 2026 03:51:58 +0300 Subject: [PATCH 04/33] revert os.clock changes --- src/logic/scripting/lua/lua_extensions.cpp | 12 ------------ src/util/platform.cpp | 20 -------------------- src/util/platform.hpp | 2 -- 3 files changed, 34 deletions(-) diff --git a/src/logic/scripting/lua/lua_extensions.cpp b/src/logic/scripting/lua/lua_extensions.cpp index f8a200de1..a7de3fdd8 100644 --- a/src/logic/scripting/lua/lua_extensions.cpp +++ b/src/logic/scripting/lua/lua_extensions.cpp @@ -8,7 +8,6 @@ #include "engine/Engine.hpp" #include "devtools/DebuggingServer.hpp" #include "logic/scripting/scripting.hpp" -#include "util/platform.hpp" using namespace devtools; using namespace scripting; @@ -382,10 +381,6 @@ static int l_debug_is_debugging(lua::State* L) { return lua::pushboolean(L, engine->getDebuggingServer() != nullptr); } -static int l_os_clock(lua::State* L) { - return lua::pushnumber(L, platform::clock()); -} - void initialize_libs_extends(lua::State* L) { if (lua::getglobal(L, "debug")) { lua::pushcfunction(L, lua::wrap); @@ -420,11 +415,4 @@ void initialize_libs_extends(lua::State* L) { lua::pop(L); } - - if (lua::getglobal(L, "os")) { - lua::pushcfunction(L, lua::wrap); - lua::setfield(L, "clock"); - - lua::pop(L); - } } diff --git a/src/util/platform.cpp b/src/util/platform.cpp index 026c6feb4..f113641e5 100644 --- a/src/util/platform.cpp +++ b/src/util/platform.cpp @@ -327,23 +327,3 @@ bool platform::stdin_has_data() { return poll(&fds, 1, 0) == 1; #endif } - -double platform::clock() { -#ifdef _WIN32 - FILETIME createTime, exitTime, kernelTime, userTime; - - static HANDLE currentProcess = GetCurrentProcess(); - - if (GetProcessTimes(currentProcess, &createTime, &exitTime, &kernelTime, &userTime)) { - ULARGE_INTEGER kernel, user; - kernel.LowPart = kernelTime.dwLowDateTime; - kernel.HighPart = kernelTime.dwHighDateTime; - user.LowPart = userTime.dwLowDateTime; - user.HighPart = userTime.dwHighDateTime; - - return static_cast(kernel.QuadPart + user.QuadPart) / 1000'0000.0; - } -#else - return std::clock() / static_cast(CLOCKS_PER_SEC); -#endif -} diff --git a/src/util/platform.hpp b/src/util/platform.hpp index 4c049a0da..54c56a0c5 100644 --- a/src/util/platform.hpp +++ b/src/util/platform.hpp @@ -25,6 +25,4 @@ namespace platform { bool open_url(const std::string& url); /// @brief Check if stdin has input bool stdin_has_data(); - /// @brief Precise wrapper for std::clock (especially for Windows) - double clock(); } From 186889624371eac86a5fedfb6e15f3f2cc43be65 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 12 May 2026 04:00:10 +0300 Subject: [PATCH 05/33] update doc/*/scripting/builtins/libtime.md --- doc/en/scripting/builtins/libtime.md | 30 ++++++++++++++++++---------- doc/ru/scripting/builtins/libtime.md | 3 +++ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/doc/en/scripting/builtins/libtime.md b/doc/en/scripting/builtins/libtime.md index 8fcd4035f..51e125e9c 100644 --- a/doc/en/scripting/builtins/libtime.md +++ b/doc/en/scripting/builtins/libtime.md @@ -1,13 +1,21 @@ -# *time* library +# *time* Library -```python -time.uptime() -> float +```lua +-- Returns the time since engine startup in seconds and milliseconds. +time.uptime() -> number + +-- Returns the time delta in seconds and milliseconds (time elapsed since the previous frame) +time.delta() -> number + +-- Returns UTC time in seconds +time.utc_time() -> int + +-- Returns local (system) time in seconds +time.local_time() -> int + +-- Returns the local time offset from UTC in seconds +time.utc_offset() -> int + +-- Returns high-precision time in seconds (elapsed since the engine startup). +time.precise_time() -> number ``` - -Returns time elapsed since the engine started. - -```python -time.delta() -> float -``` - -Returns time elapsed since the last frame. diff --git a/doc/ru/scripting/builtins/libtime.md b/doc/ru/scripting/builtins/libtime.md index 81bae58f7..62e95892a 100644 --- a/doc/ru/scripting/builtins/libtime.md +++ b/doc/ru/scripting/builtins/libtime.md @@ -15,4 +15,7 @@ time.local_time() -> int -- Возвращает смещение локального времени от UTC в секундах time.utc_offset() -> int + +-- Возвращает время высокой точности в секундах. За точку отсчёта берётся запуск движка. +time.precise_time() -> number ``` From 5435caf53bc541ef7e8671790f5c93b7c19b72a1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 12 May 2026 23:09:21 +0300 Subject: [PATCH 06/33] add 'require' function to lua syntax hightlight config as keyword --- res/devtools/syntax/lua.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/devtools/syntax/lua.toml b/res/devtools/syntax/lua.toml index d2469d2fb..45e697bbe 100644 --- a/res/devtools/syntax/lua.toml +++ b/res/devtools/syntax/lua.toml @@ -8,5 +8,5 @@ multiline-string-end = "]]" keywords = [ "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", - "until", "while", "self", "error" + "until", "while", "self", "error", "require" ] From b307b5c93b6ab2d2102e81310e3441930f55e682 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 12 May 2026 23:41:05 +0300 Subject: [PATCH 07/33] fix lua syntax highlighting --- src/coders/commons.hpp | 4 ++++ src/coders/syntax_parser.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/coders/commons.hpp b/src/coders/commons.hpp index 990474990..d374cd92b 100644 --- a/src/coders/commons.hpp +++ b/src/coders/commons.hpp @@ -21,6 +21,10 @@ inline bool is_identifier_part(int c) { return is_identifier_start(c) || is_digit(c) || c == '-'; } +inline bool is_minimal_identifier_part(int c) { + return is_identifier_start(c) || is_digit(c); +} + inline bool is_json_identifier_start(int c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' || c == '.'; diff --git a/src/coders/syntax_parser.cpp b/src/coders/syntax_parser.cpp index 871cd3146..b0196ec2d 100644 --- a/src/coders/syntax_parser.cpp +++ b/src/coders/syntax_parser.cpp @@ -93,7 +93,7 @@ public: throw error("identifier expected"); } int start = pos; - while (hasNext() && is_identifier_part(source[pos])) { + while (hasNext() && is_minimal_identifier_part(source[pos])) { pos++; } return std::wstring(source.substr(start, pos - start)); From 411021052e4a27702fb0013721124b80f893310b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 21 May 2026 21:30:42 +0300 Subject: [PATCH 08/33] add gui.process_template, vc.get_setting,vc.get_setting_str, vc.get_setting_info --- res/scripts/stdlib.lua | 10 +++++++--- res/scripts/stdmin.lua | 7 +++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 0e72ad86d..6e4df3558 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -319,9 +319,8 @@ function console.chat(...) events.emit("core:chat", ...) end -function gui.template(name, params) - local text = file.read(file.find("layouts/templates/"..name..".xml")) - text = text:gsub("%%{([^}]+)}", function(n) +function gui.process_template(source, params) + local text = source:gsub("%%{([^}]+)}", function(n) local s = params[n] if s == nil then return @@ -341,6 +340,11 @@ function gui.template(name, params) return text end +function gui.template(name, params) + local text = file.read(file.find("layouts/templates/"..name..".xml")) + return gui.process_template(text, params) +end + session = require "core:internal/session" stdcomp = require "core:internal/stdcomp" entities.get = stdcomp.get_Entity diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 70e27f561..905c626e7 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -5,9 +5,12 @@ vc = { is_headless = function() return _vc_headless end, - is_client = function () + is_client = function() return not _vc_headless - end + end, + get_setting = __vc_app.get_setting, + get_setting_str = __vc_app.get_setting_str, + get_setting_info = __vc_app.get_setting_info, } local _ffi = ffi From d3760d5b7cca228b617af3a1b13d18ac7ad0886f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 21 May 2026 21:43:14 +0300 Subject: [PATCH 09/33] update doc/*/scripting/builtins/libgui.md --- doc/en/scripting/builtins/libgui.md | 14 ++++++++++---- doc/ru/scripting/builtins/libgui.md | 14 ++++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/doc/en/scripting/builtins/libgui.md b/doc/en/scripting/builtins/libgui.md index e3c109566..53d6e4ad2 100644 --- a/doc/en/scripting/builtins/libgui.md +++ b/doc/en/scripting/builtins/libgui.md @@ -149,16 +149,22 @@ gui.load_document( args: table ) -> str --- Loads a template into the layout +-- Loads and processes layout template from file gui.template( -- template name in /layouts/templates without path and extension name: str, -- variable table (can be used in markup) -- * Ex: -- * text in this case is the value from params with the text key - params: table, - -- table, available in events as the global variable DATA - [optional] data: table + params: table +) -> str + +-- Processes layout template from string +gui.process_template( + -- template source code + source: str, + -- variable table (same as gui.template) + params: table ) -> str ``` diff --git a/doc/ru/scripting/builtins/libgui.md b/doc/ru/scripting/builtins/libgui.md index a744639ea..9c2eb7418 100644 --- a/doc/ru/scripting/builtins/libgui.md +++ b/doc/ru/scripting/builtins/libgui.md @@ -147,17 +147,23 @@ gui.load_document( args: table -- параметры для события on_open ) -> string --- Загружает шаблон в лояут +-- Обрабатывает xml шаблон макета из файла gui.template( -- имя шаблона в /layouts/templates без пути и расширения name: string, -- таблица переменных (может быть использована в разметке) -- * Пр: -- * text в данном случае, это значение из params по ключу text - params: table, - -- таблица, доступная в событиях как глобальная переменная DATA - [опционально] data: table + params: table ) -> string + +-- Обрабатывает xml шаблон макета из строки +gui.process_template( + -- шаблон в виде строки + source: string, + -- таблица переменных, как в gui.template + params: table +) ``` ## Корневой документ From dc9ae6e99e57f69ac20b4b97656dccbeff516710 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 21 May 2026 22:31:27 +0300 Subject: [PATCH 10/33] test direct non-copying reading FFIBytearray in C++ context --- res/modules/internal/bytearray.lua | 8 ++++++++ res/scripts/stdmin.lua | 1 + src/constants.hpp | 2 ++ src/logic/scripting/lua/lua_util.hpp | 11 +++++++++++ 4 files changed, 22 insertions(+) diff --git a/res/modules/internal/bytearray.lua b/res/modules/internal/bytearray.lua index c7c756f1c..739bb7cea 100644 --- a/res/modules/internal/bytearray.lua +++ b/res/modules/internal/bytearray.lua @@ -215,6 +215,13 @@ FFIBytearray = { } table.merge(FFIBytearray, bytearray_methods) +local function FFIBytearray_as_ptr(bytes) + if type(bytes) == "cdata" then + return tostring(bytes.bytes):sub(27), bytes.size + end + return "0" +end + local function FFIBytearray_as_string(bytes) local t = type(bytes) if t == "cdata" then @@ -283,6 +290,7 @@ local FFIU32view = create_FFIview_class("FFIU32view", "uint32_t", 4) return { FFIBytearray = setmetatable(FFIBytearray, FFIBytearray), FFIBytearray_as_string = FFIBytearray_as_string, + FFIBytearray_as_ptr = FFIBytearray_as_ptr, FFIU16view = FFIU16view, FFII16view = FFII16view, FFIU32view = FFIU32view, diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 905c626e7..98d937b1a 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -303,6 +303,7 @@ require "core:internal/extensions/string" local bytearray = require "core:internal/bytearray" Bytearray = bytearray.FFIBytearray Bytearray_as_string = bytearray.FFIBytearray_as_string +Bytearray_as_ptr = bytearray.FFIBytearray_as_ptr U16view = bytearray.FFIU16view I16view = bytearray.FFII16view U32view = bytearray.FFIU32view diff --git a/src/constants.hpp b/src/constants.hpp index f43314f4a..f0dc24d40 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -5,6 +5,8 @@ #include #include +#define VC_LUA_DIRECT_BYTEARRAY_ACCESS + inline constexpr int ENGINE_VERSION_MAJOR = 0; inline constexpr int ENGINE_VERSION_MINOR = 32; diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index a7881bef7..88fa0f021 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -833,11 +833,22 @@ namespace lua { return tolstring(L, idx); } pushvalue(L, idx); +#ifdef VC_LUA_DIRECT_BYTEARRAY_ACCESS + requireglobal(L, "Bytearray_as_ptr"); + pushvalue(L, -2); + call(L, 1, 2); + auto view = tolstring(L, -2); + uint64_t size = touinteger(L, -1); + auto ptr = (const char*)std::stoull(std::string(view), nullptr, 16); + pop(L, 3); + return std::string_view(ptr, size); +#else requireglobal(L, "Bytearray_as_string"); pushvalue(L, -2); call(L, 1, 1); auto view = tolstring(L, -1); pop(L, 2); return view; +#endif // VC_LUA_DIRECT_BYTEARRAY_ACCESS } } From 24a2c35f4116b7735c73452e1e38d3c6b10dbcfa Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 24 May 2026 13:24:58 +0300 Subject: [PATCH 11/33] add direct-scripting-data-access setting --- src/constants.hpp | 2 -- src/io/settings_io.cpp | 1 + src/logic/scripting/lua/lua_util.cpp | 27 +++++++++++++++++++++++++++ src/logic/scripting/lua/lua_util.hpp | 24 +----------------------- src/settings.hpp | 2 ++ 5 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/constants.hpp b/src/constants.hpp index f0dc24d40..f43314f4a 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -5,8 +5,6 @@ #include #include -#define VC_LUA_DIRECT_BYTEARRAY_ACCESS - inline constexpr int ENGINE_VERSION_MAJOR = 0; inline constexpr int ENGINE_VERSION_MINOR = 32; diff --git a/src/io/settings_io.cpp b/src/io/settings_io.cpp index c66b5c451..5f93e5be7 100644 --- a/src/io/settings_io.cpp +++ b/src/io/settings_io.cpp @@ -101,6 +101,7 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) { builder.addSection("system"); builder.add("max-bg-asset-loaders", &settings.system.maxBgAssetLoaders); builder.add("preserve-assets-during-frame", &settings.system.preserveAssetsDuringFrame); + builder.add("direct-scripting-data-access", &settings.system.directScriptingDataAccess); } dv::value SettingsHandler::getValue(const std::string& name) const { diff --git a/src/logic/scripting/lua/lua_util.cpp b/src/logic/scripting/lua/lua_util.cpp index ded9cc79b..ddd05cd19 100644 --- a/src/logic/scripting/lua/lua_util.cpp +++ b/src/logic/scripting/lua/lua_util.cpp @@ -5,6 +5,7 @@ #include #include "util/stringutil.hpp" +#include "engine/Engine.hpp" using namespace lua; @@ -369,3 +370,29 @@ void lua::remove_environment(State* L, int id) { pushnil(L); store_env(L, id); } + +std::string_view lua::bytearray_as_string(lua::State* L, int idx) { + const auto& settings = scripting::engine->getSettings(); + if (type(L, idx) == LUA_TSTRING) { + return tolstring(L, idx); + } + pushvalue(L, idx); + + if (settings.system.directScriptingDataAccess.get()) { + requireglobal(L, "Bytearray_as_ptr"); + pushvalue(L, -2); + call(L, 1, 2); + auto view = tolstring(L, -2); + uint64_t size = touinteger(L, -1); + auto ptr = (const char*)std::stoull(std::string(view), nullptr, 16); + pop(L, 3); + return std::string_view(ptr, size); + } else { + requireglobal(L, "Bytearray_as_string"); + pushvalue(L, -2); + call(L, 1, 1); + auto view = tolstring(L, -1); + pop(L, 2); + return view; + } +} diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index 88fa0f021..562e641ef 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -828,27 +828,5 @@ namespace lua { return create_bytearray(L, bytes.data(), bytes.size()); } - inline std::string_view bytearray_as_string(lua::State* L, int idx) { - if (type(L, idx) == LUA_TSTRING) { - return tolstring(L, idx); - } - pushvalue(L, idx); -#ifdef VC_LUA_DIRECT_BYTEARRAY_ACCESS - requireglobal(L, "Bytearray_as_ptr"); - pushvalue(L, -2); - call(L, 1, 2); - auto view = tolstring(L, -2); - uint64_t size = touinteger(L, -1); - auto ptr = (const char*)std::stoull(std::string(view), nullptr, 16); - pop(L, 3); - return std::string_view(ptr, size); -#else - requireglobal(L, "Bytearray_as_string"); - pushvalue(L, -2); - call(L, 1, 1); - auto view = tolstring(L, -1); - pop(L, 2); - return view; -#endif // VC_LUA_DIRECT_BYTEARRAY_ACCESS - } + std::string_view bytearray_as_string(lua::State* L, int idx); } diff --git a/src/settings.hpp b/src/settings.hpp index 782330f14..6c3df896a 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -126,6 +126,8 @@ struct SystemSettings { IntegerSetting maxBgAssetLoaders {3, -4, 16}; /// @brief Preserve assets during current frame from being destroyed FlagSetting preserveAssetsDuringFrame {true}; + /// @brief Allow luajit cdata objects direct data access optimizations + FlagSetting directScriptingDataAccess {true}; }; struct EngineSettings { From c4bddba6b73a42b7ed1da4cbc81e4c79d48b7117 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 24 May 2026 13:33:34 +0300 Subject: [PATCH 12/33] fix controls settings panel height --- res/layouts/pages/settings_controls.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/layouts/pages/settings_controls.xml b/res/layouts/pages/settings_controls.xml index c5655e1d8..acd45c209 100644 --- a/res/layouts/pages/settings_controls.xml +++ b/res/layouts/pages/settings_controls.xml @@ -8,7 +8,7 @@ @settings.Controls Search Mode - + From eb453d81704a261fcf23bf87026596daeb735a74 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 24 May 2026 14:15:31 +0300 Subject: [PATCH 13/33] add audio.recording-enabled setting --- res/layouts/pages/settings_audio.xml.lua | 36 ++++++++++++++------- res/layouts/pages/settings_display.xml.lua | 12 +++---- res/layouts/pages/settings_graphics.xml.lua | 18 +++++------ res/layouts/templates/track_setting.xml | 2 +- src/audio/audio.cpp | 32 +++++++++++++----- src/engine/Engine.cpp | 2 +- src/graphics/ui/elements/CheckBox.cpp | 4 +++ src/io/settings_io.cpp | 1 + src/settings.hpp | 1 + 9 files changed, 71 insertions(+), 37 deletions(-) diff --git a/res/layouts/pages/settings_audio.xml.lua b/res/layouts/pages/settings_audio.xml.lua index 1ff217534..b63b06234 100644 --- a/res/layouts/pages/settings_audio.xml.lua +++ b/res/layouts/pages/settings_audio.xml.lua @@ -1,4 +1,4 @@ -function create_setting(id, name, step, postfix) +function create_trackbar_setting(id, name, step, postfix) local info = app.get_setting_info(id) postfix = postfix or "" document.root:add(gui.template("track_setting", { @@ -10,10 +10,10 @@ function create_setting(id, name, step, postfix) step=step, postfix=postfix })) - update_setting(app.get_setting(id), id, name, postfix) + update_trackbar_label(app.get_setting(id), id, name, postfix) end -function update_setting(x, id, name, postfix) +function update_trackbar_label(x, id, name, postfix) app.set_setting(id, x) -- updating label document[id..".L"].text = string.format( @@ -24,12 +24,22 @@ function update_setting(x, id, name, postfix) ) end +function update_checkbox_setting(id, value) + app.set_setting(id, value) + + local recording_enabled = app.get_setting("audio.recording-enabled") + document.input_device_select.enabled = recording_enabled + document.input_volume_inner.enabled = recording_enabled + document.input_volume_outer.enabled = recording_enabled +end + function create_checkbox(id, name, tooltip) tooltip = tooltip or '' document.root:add(string.format( - "%s", + "%s", id, app.str_setting(id), gui.str(tooltip, "settings"), gui.str(name, "settings") )) + update_checkbox_setting(id, app.get_setting(id)) end local initialized = false @@ -55,11 +65,12 @@ function on_open() prev_amplitude = amplitude end) end - create_setting("audio.volume-master", "Master Volume", 0.01) - create_setting("audio.volume-regular", "Regular Sounds", 0.01) - create_setting("audio.volume-ui", "UI Sounds", 0.01) - create_setting("audio.volume-ambient", "Ambient", 0.01) - create_setting("audio.volume-music", "Music", 0.01) + create_trackbar_setting("audio.volume-master", "Master Volume", 0.01) + create_trackbar_setting("audio.volume-regular", "Regular Sounds", 0.01) + create_trackbar_setting("audio.volume-ui", "UI Sounds", 0.01) + create_trackbar_setting("audio.volume-ambient", "Ambient", 0.01) + create_trackbar_setting("audio.volume-music", "Music", 0.01) + document.root:add("") document.root:add("".. @@ -70,5 +70,5 @@ function on_open() create_checkbox("camera.inertia", "Camera Inertia") create_checkbox("camera.fov-effects", "Camera FOV Effects") create_checkbox("display.limit-fps-iconified", "Limit Background FPS") - create_setting("graphics.gamma", "Gamma", 0.05, "", "graphics.gamma.tooltip") + create_trackbar_setting("graphics.gamma", "Gamma", 0.05, "", "graphics.gamma.tooltip") end diff --git a/res/layouts/pages/settings_graphics.xml.lua b/res/layouts/pages/settings_graphics.xml.lua index 806f48d0c..6a4596a04 100644 --- a/res/layouts/pages/settings_graphics.xml.lua +++ b/res/layouts/pages/settings_graphics.xml.lua @@ -1,4 +1,4 @@ -function create_setting(id, name, step, postfix, tooltip, changeonrelease) +function create_trackbar_setting(id, name, step, postfix, tooltip, changeonrelease) local info = app.get_setting_info(id) postfix = postfix or "" tooltip = tooltip or "" @@ -14,10 +14,10 @@ function create_setting(id, name, step, postfix, tooltip, changeonrelease) tooltip=tooltip, changeonrelease=changeonrelease })) - update_setting(app.get_setting(id), id, name, postfix) + update_trackbar_label(app.get_setting(id), id, name, postfix) end -function update_setting(x, id, name, postfix) +function update_trackbar_label(x, id, name, postfix) -- updating label document[id..".L"].text = string.format( "%s: %s%s", @@ -36,15 +36,15 @@ function create_checkbox(id, name, tooltip) end function on_open() - create_setting("chunks.load-distance", "Load Distance", 1) - create_setting("chunks.load-speed", "Load Speed", 1) - create_setting("graphics.fog-curve", "Fog Curve", 0.1) + create_trackbar_setting("chunks.load-distance", "Load Distance", 1) + create_trackbar_setting("chunks.load-speed", "Load Speed", 1) + create_trackbar_setting("graphics.fog-curve", "Fog Curve", 0.1) create_checkbox("graphics.backlight", "Backlight", "graphics.backlight.tooltip") create_checkbox("graphics.soft-lighting", "Soft lighting", "graphics.soft-lighting.tooltip") create_checkbox("graphics.dense-render", "Dense blocks render", "graphics.dense-render.tooltip") create_checkbox("graphics.advanced-render", "Advanced render", "graphics.advanced-render.tooltip") - create_setting("graphics.ssao", "SSAO", 1, "", "graphics.ssao.tooltip") - create_setting("graphics.shadows-quality", "Shadows quality", 1) - create_setting("graphics.clouds-quality", "Clouds quality", 1) + create_trackbar_setting("graphics.ssao", "SSAO", 1, "", "graphics.ssao.tooltip") + create_trackbar_setting("graphics.shadows-quality", "Shadows quality", 1) + create_trackbar_setting("graphics.clouds-quality", "Clouds quality", 1) end diff --git a/res/layouts/templates/track_setting.xml b/res/layouts/templates/track_setting.xml index f01b8f842..79cdb41c1 100644 --- a/res/layouts/templates/track_setting.xml +++ b/res/layouts/templates/track_setting.xml @@ -3,5 +3,5 @@ + sub-consumer='function(x) update_trackbar_label(x, "%{id}", "%{name}", "%{postfix}") end'/> diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index cfc84ce00..d6e32d089 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -190,18 +190,34 @@ void audio::initialize( audio::get_channel(channel.name)->setVolume(value * value); }, true)); } - objects_keeper.keepAlive(settings.acousticEffects.observe([=](auto value) { + objects_keeper.keepAlive(settings.acousticEffects.observe([=](bool value) { if (value) return; backend->setAcoustics(audio::Acoustics {}); }, true)); - if (inputEnabled) { - ::input_device = backend->openInputDevice("", 44100, 1, 16); - ::input_enabled = true; - } - if (::input_device) { - ::input_device->startCapture(); - } + ::input_enabled = inputEnabled; + + objects_keeper.keepAlive(settings.recordingEnabled.observe([&settings](bool enabled) { + if (!::input_enabled) { + return; + } + if (!enabled && ::input_device == nullptr) { + return; + } + if (enabled) { + logger.info() << "recording enabled; input device is " + << settings.inputDevice.get(); + ::input_device = backend->openInputDevice( + settings.inputDevice.get(), 44100, 1, 16 + ); + ::input_device->startCapture(); + } else { + if (::input_device) { + ::input_device->stopCapture(); + } + ::input_device = nullptr; + } + }, true)); } InputDevice* audio::get_input_device() { diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 89a345ff6..879d95f0b 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -189,7 +189,7 @@ void Engine::initialize(CoreParameters coreParameters) { keepAlive(settings.audio.inputDevice.observe([](auto name) { audio::set_input_device(name == "auto" ? "" : name); - }, true)); + })); project->loadProjectStartScript(); if (!params.headless) { diff --git a/src/graphics/ui/elements/CheckBox.cpp b/src/graphics/ui/elements/CheckBox.cpp index 8694dc085..fe3a342dd 100644 --- a/src/graphics/ui/elements/CheckBox.cpp +++ b/src/graphics/ui/elements/CheckBox.cpp @@ -57,6 +57,10 @@ FullCheckBox::FullCheckBox( add(checkbox); + auto& checkboxActions = actions; label->setMargin(glm::vec4(5.f, 5.f, 0.f, 0.f)); + label->listenAction(UIAction::CLICK, [&checkboxActions](auto& gui) { + checkboxActions.notify(UIAction::CLICK, gui); + }); add(label); } diff --git a/src/io/settings_io.cpp b/src/io/settings_io.cpp index 5f93e5be7..8e8b0a2dc 100644 --- a/src/io/settings_io.cpp +++ b/src/io/settings_io.cpp @@ -38,6 +38,7 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) { builder.addSection("audio"); builder.add("enabled", &settings.audio.enabled, false); + builder.add("recording-enabled", &settings.audio.recordingEnabled); builder.add("volume-master", &settings.audio.volumeMaster); builder.add("volume-regular", &settings.audio.volumeRegular); builder.add("volume-ui", &settings.audio.volumeUI); diff --git a/src/settings.hpp b/src/settings.hpp index 6c3df896a..5220ca328 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -10,6 +10,7 @@ struct AudioSettings { /// @brief try to initialize AL backend FlagSetting enabled {true}; + FlagSetting recordingEnabled {false}; NumberSetting volumeMaster {1.0f, 0.0f, 1.0f, setting_format::percent}; NumberSetting volumeRegular {1.0f, 0.0f, 1.0f, setting_format::percent}; From 3f81494d4b14f3a8d6be0f9e9a489f0d26c88e37 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 24 May 2026 14:48:00 +0300 Subject: [PATCH 14/33] fix lua::bytearray_as_string --- src/logic/scripting/lua/lua_commons.hpp | 5 ++++ src/logic/scripting/lua/lua_util.cpp | 32 +++++++++++++++++++------ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/logic/scripting/lua/lua_commons.hpp b/src/logic/scripting/lua/lua_commons.hpp index bb3163c86..8e77cbbc0 100644 --- a/src/logic/scripting/lua/lua_commons.hpp +++ b/src/logic/scripting/lua/lua_commons.hpp @@ -17,6 +17,11 @@ #error LuaJIT required #endif +#ifndef LUA_TCDATA +// unsafe, use for filtering only +#define LUA_TCDATA 10 +#endif + namespace lua { class luaerror : public std::runtime_error { public: diff --git a/src/logic/scripting/lua/lua_util.cpp b/src/logic/scripting/lua/lua_util.cpp index ddd05cd19..090e47a5d 100644 --- a/src/logic/scripting/lua/lua_util.cpp +++ b/src/logic/scripting/lua/lua_util.cpp @@ -6,6 +6,7 @@ #include "util/stringutil.hpp" #include "engine/Engine.hpp" +#include "debug/Logger.hpp" using namespace lua; @@ -13,6 +14,8 @@ static int next_environment = 1; std::unordered_map lua::usertypeNames; +static debug::Logger logger("lua-util"); + int lua::userdata_destructor(lua::State* L) { if (auto obj = touserdata(L, 1)) { obj->~Userdata(); @@ -371,10 +374,23 @@ void lua::remove_environment(State* L, int id) { store_env(L, id); } +static inline std::string_view bytearray_as_string_indirect(lua::State* L, int idx) { + requireglobal(L, "Bytearray_as_string"); + pushvalue(L, -2); + call(L, 1, 1); + auto view = tolstring(L, -1); + pop(L, 2); + return view; +} + std::string_view lua::bytearray_as_string(lua::State* L, int idx) { const auto& settings = scripting::engine->getSettings(); - if (type(L, idx) == LUA_TSTRING) { + + int luaType = type(L, idx); + if (luaType == LUA_TSTRING) { return tolstring(L, idx); + } else if (luaType == LUA_TTABLE) { + return bytearray_as_string_indirect(L, idx); } pushvalue(L, idx); @@ -383,16 +399,18 @@ std::string_view lua::bytearray_as_string(lua::State* L, int idx) { pushvalue(L, -2); call(L, 1, 2); auto view = tolstring(L, -2); + if (view == "0") { + if (luaType == LUA_TCDATA) { + logger.error() << "FFI-based Bytearray_as_ptr returned null-pointer"; + } + pop(L, 2); + return bytearray_as_string_indirect(L, idx); + } uint64_t size = touinteger(L, -1); auto ptr = (const char*)std::stoull(std::string(view), nullptr, 16); pop(L, 3); return std::string_view(ptr, size); } else { - requireglobal(L, "Bytearray_as_string"); - pushvalue(L, -2); - call(L, 1, 1); - auto view = tolstring(L, -1); - pop(L, 2); - return view; + return bytearray_as_string_indirect(L, idx); } } From 362acd04367354a0a13bd9e287d067f7cb07b499 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 24 May 2026 16:12:07 +0300 Subject: [PATCH 15/33] update texts --- res/layouts/pages/settings_audio.xml.lua | 2 +- res/texts/en_US.txt | 1 + res/texts/ru_RU.txt | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/res/layouts/pages/settings_audio.xml.lua b/res/layouts/pages/settings_audio.xml.lua index b63b06234..475ccc51b 100644 --- a/res/layouts/pages/settings_audio.xml.lua +++ b/res/layouts/pages/settings_audio.xml.lua @@ -91,6 +91,6 @@ function on_open() selectbox.value = app.get_setting("audio.input-device") end - create_checkbox("audio.recording-enabled", "Enable microphone", "audio.recording-enabled.tooltip") + create_checkbox("audio.recording-enabled", "Microphone access", "audio.recording-enabled.tooltip") create_checkbox("audio.acoustic-effects", "Acoustic effects", "audio.acoustic-effects.tooltip") end diff --git a/res/texts/en_US.txt b/res/texts/en_US.txt index a6c9c5391..ff44ec754 100644 --- a/res/texts/en_US.txt +++ b/res/texts/en_US.txt @@ -18,6 +18,7 @@ devtools.output=Output # Tooltips audio.acoustic-effects.tooltip=Acoustic effects may not be supported by your system +audio.recording-enabled.tooltip=Enables audio recording features graphics.gamma.tooltip=Lighting brightness curve graphics.backlight.tooltip=Backlight to prevent total darkness graphics.dense-render.tooltip=Enables transparency in blocks like leaves diff --git a/res/texts/ru_RU.txt b/res/texts/ru_RU.txt index 66dc40a42..ed3bb9686 100644 --- a/res/texts/ru_RU.txt +++ b/res/texts/ru_RU.txt @@ -39,6 +39,7 @@ pack.remove-confirm=Удалить весь поставляемый паком/ # Подсказки audio.acoustic-effects.tooltip=Акустические эффекты могут не поддерживаться системой +audio.recording-enabled.tooltip=Включает функции записи звука graphics.gamma.tooltip=Кривая яркости освещения graphics.backlight.tooltip=Подсветка, предотвращающая полную темноту graphics.dense-render.tooltip=Включает прозрачность блоков, таких как листья @@ -114,6 +115,7 @@ settings.Microphone=Микрофон settings.microphone.None=Нет settings.Clouds quality=Качество облаков settings.Acoustic effects=Акустические эффекты +settings.Microphone access=Доступ к микрофону # Управление chunks.reload=Перезагрузить Чанки From 3656739c3c48d432478ad8b90b7f94d356565609 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 25 May 2026 23:17:16 +0300 Subject: [PATCH 16/33] extract stdlib.lua parts to separate internal modules --- res/modules/internal/console.lua | 62 +++++ res/modules/internal/lifetime_events.lua | 224 ++++++++++++++++ res/scripts/stdlib.lua | 314 ++--------------------- 3 files changed, 312 insertions(+), 288 deletions(-) create mode 100644 res/modules/internal/console.lua create mode 100644 res/modules/internal/lifetime_events.lua diff --git a/res/modules/internal/console.lua b/res/modules/internal/console.lua new file mode 100644 index 000000000..5b4f6b69c --- /dev/null +++ b/res/modules/internal/console.lua @@ -0,0 +1,62 @@ + +--- Console library extension --- +console.cheats = {} + +local log_element = Document.new("core:console").log +function console.log(...) + local args = {...} + local text = '' + for i,v in ipairs(args) do + if i ~= 1 then + text = text..' '..v + else + text = text..v + end + end + log_element.caret = -1 + if log_element.caret > 0 then + text = '\n'..text + end + log_element:paste(text) +end + +local console_add_command = console.__add_command +console.__add_command = nil + +function console.add_command(scheme, description, handler, is_cheat) + console_add_command(scheme, description, handler) + if not is_cheat then return end + + local name = string.match(scheme, "^(%S+)") + if not name then + error("Incorrect command syntax, command name not found") + end + + table.insert_unique(console.cheats, name) +end + +function console.is_cheat(name) + if not table.has(console.get_commands_list(), name) then + error(string.format("command \"%s\" not found", name)) + end + + return table.has(console.cheats, name) +end + +function console.set_cheat(name, status) + local is_cheat = console.is_cheat(name) + if status and not is_cheat then + table.insert(console.cheats, name) + return true + elseif not status and is_cheat then + table.remove_value(console.cheats, name) + return true + end + + return false +end + +function console.chat(...) + console.log(...) + events.emit("core:chat", ...) +end diff --git a/res/modules/internal/lifetime_events.lua b/res/modules/internal/lifetime_events.lua new file mode 100644 index 000000000..48a96c550 --- /dev/null +++ b/res/modules/internal/lifetime_events.lua @@ -0,0 +1,224 @@ +local __app = __vc_app +local __rules = rules + +local function configure_SSAO() + -- Temporary using slot to configure built-in SSAO effect + local slot = gfx.posteffects.index("core:ssao") + gfx.posteffects.set_effect(slot, "ssao") + + -- Generating random SSAO samples + local buffer = Bytearray(0) + for i = 0, 63 do + local x = math.random() * 2.0 - 1.0 + local y = math.random() * 2.0 - 1.0 + local z = math.random() * 2.0 + local len = math.sqrt(x * x + y * y + z * z) + if len > 0 then + x = x / len + y = y / len + z = z / len + end + Bytearray.append(buffer, byteutil.pack("fff", x, y, z)) + end + gfx.posteffects.set_array(slot, "u_ssaoSamples", Bytearray_as_string(buffer)) + + local function update_ssao_quality(value) + value = math.min(value, 3) + gfx.posteffects.set_params(slot, { + u_kernelSize = value * 16, + u_radius = 0.4 / value, + u_bias = 0.006 / value / value, + }) + end + events.on("core:setting.graphics.ssao.set", update_ssao_quality) + + update_ssao_quality(__app.get_setting("graphics.ssao")) +end + +local function __vc_on_hud_open() + local _hud_is_content_access = hud._is_content_access + local _hud_set_content_access = hud._set_content_access + local _hud_set_debug_cheats = hud._set_debug_cheats + + __rules.create("allow-cheats", true) + + __rules.create("allow-content-access", _hud_is_content_access(), function(value) + _hud_set_content_access(value) + end) + __rules.create("allow-flight", true, function(value) + input.set_enabled("player.flight", value) + end) + __rules.create("allow-noclip", true, function(value) + input.set_enabled("player.noclip", value) + end) + __rules.create("allow-attack", true, function(value) + input.set_enabled("player.attack", value) + end) + __rules.create("allow-destroy", true, function(value) + input.set_enabled("player.destroy", value) + end) + __rules.create("allow-cheat-movement", true, function(value) + input.set_enabled("movement.cheat", value) + end) + __rules.create("allow-fast-interaction", true, function(value) + input.set_enabled("player.fast_interaction", value) + end) + __rules.create("allow-debug-cheats", true, function(value) + _hud_set_debug_cheats(value) + end) + input.add_callback("devtools.console", function() + if menu.page ~= "" then + return + end + time.post_runnable(function() + hud.show_overlay("core:console", false, {"console"}) + end) + end) + input.add_callback("hud.chat", function() + if menu.page ~= "" then + return + end + time.post_runnable(function() + hud.show_overlay("core:console", false, {"chat"}) + end) + end) + input.add_callback("key:escape", function() + if menu.page ~= "" then + if not menu:back() then + menu:reset() + gui.set_active_frame("") + end + elseif hud.is_inventory_open() then + hud.close_inventory() + elseif gui.get_active_frame() then + gui.set_active_frame("") + else + hud.pause() + end + end) + hud.open_permanent("core:ingame_chat") + + configure_SSAO() +end + +local Schedule = require "core:schedule" +local ScheduleGroup_mt = { + __index = { + publish = function(self, schedule) + local id = self._next_schedule + self._schedules[id] = schedule + self._next_schedule = id + 1 + end, + tick = function(self, dt) + for id, schedule in pairs(self._schedules) do + schedule:tick(dt) + end + self.common:tick(dt) + end, + remove = function(self, id) + self._schedules[id] = nil + end, + } +} + +local function ScheduleGroup() + return setmetatable({ + _next_schedule = 1, + _schedules = {}, + common = Schedule() + }, ScheduleGroup_mt) +end + +local RULES_FILE = "world:rules.toml" +local function __vc_on_world_open() + time.schedules.world = ScheduleGroup() + + if not file.exists(RULES_FILE) then + return + end + local rule_values = toml.parse(file.read(RULES_FILE)) + for name, value in pairs(rule_values) do + __rules.set(name, value) + end +end + +local function __vc_on_world_tick(tps) + time.schedules.world:tick(1.0 / tps) +end + +local function __vc_process_before_quit() + block.__process_register_events() +end + +local function __vc_on_world_save() + local rule_values = {} + for name, rule in pairs(__rules.rules) do + rule_values[name] = rule.value + end + file.write(RULES_FILE, toml.tostring(rule_values)) +end + +local __close_all_descriptors = file.__close_all_descriptors +local __gui_util_reset_local = gui_util.__reset_local +local __stdcomp_reset = stdcomp.__reset +file.__close_all_descriptors = nil +gui_util.__reset_local = nil +stdcomp.reset = nil + +local function __vc_on_world_quit() + __rules.clear() + __gui_util_reset_local() + __stdcomp_reset() + __close_all_descriptors() +end + +local __post_runnables = {} +local fn_audio_reset_fetch_buffer = audio.__reset_fetch_buffer +audio.__reset_fetch_buffer = nil + +local __vc_update_coroutines = _G.__vc_update_coroutines + +local function __process_post_runnables() + if #__post_runnables > 0 then + for _, func in ipairs(__post_runnables) do + local status, result = xpcall(func, __vc__error) + if not status then + debug.error("error in post_runnable: "..result) + end + end + __post_runnables = {} + end + + __vc_update_coroutines() + + fn_audio_reset_fetch_buffer() + debug.pull_events() + network.__process_events() + if not hud or not hud.is_paused() then + block.__process_register_events() + block.__perform_ticks(time.delta()) + end +end + +function __vc__process_post_runnables() + __vc__is_post_runnable = true + local success, err = pcall(__process_post_runnables) + if not success then + debug.error("an error ocurred while processing post-runnables: ".. err) + end + __vc__is_post_runnable = false +end + +function time.post_runnable(runnable) + table.insert(__post_runnables, runnable) +end + +return { + __vc_on_hud_open = __vc_on_hud_open, + __vc_on_world_open = __vc_on_world_open, + __vc_on_world_tick = __vc_on_world_tick, + __vc_process_before_quit = __vc_process_before_quit, + __vc_on_world_save = __vc_on_world_save, + __vc_on_world_quit = __vc_on_world_quit, + __vc__process_post_runnables = __vc__process_post_runnables, +} diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 6e4df3558..702d50072 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -12,7 +12,7 @@ function sleep(timesec) end end -function tb_frame_tostring(frame) +local function tb_frame_tostring(frame) local s = frame.short_src if frame.what ~= "C" then s = s .. ":" .. tostring(frame.currentline) @@ -192,6 +192,22 @@ function start_coroutine(chunk, name) __vc_named_coroutines[name] = co end +function __vc_update_coroutines() + local dead = {} + for name, co in pairs(__vc_named_coroutines) do + local success, err = coroutine.resume(co) + if not success then + debug.error(err) + end + if coroutine.status(co) == "dead" then + table.insert(dead, name) + end + end + for _, name in ipairs(dead) do + __vc_named_coroutines[name] = nil + end +end + function __vc_start_app_script(path, name) debug.log("starting application script "..path) @@ -257,68 +273,6 @@ do end end ---- Console library extension --- -console.cheats = {} - -local log_element = Document.new("core:console").log -function console.log(...) - local args = {...} - local text = '' - for i,v in ipairs(args) do - if i ~= 1 then - text = text..' '..v - else - text = text..v - end - end - log_element.caret = -1 - if log_element.caret > 0 then - text = '\n'..text - end - log_element:paste(text) -end - -local console_add_command = console.__add_command -console.__add_command = nil - -function console.add_command(scheme, description, handler, is_cheat) - console_add_command(scheme, description, handler) - if not is_cheat then return end - - local name = string.match(scheme, "^(%S+)") - if not name then - error("Incorrect command syntax, command name not found") - end - - table.insert_unique(console.cheats, name) -end - -function console.is_cheat(name) - if not table.has(console.get_commands_list(), name) then - error(string.format("command \"%s\" not found", name)) - end - - return table.has(console.cheats, name) -end - -function console.set_cheat(name, status) - local is_cheat = console.is_cheat(name) - if status and not is_cheat then - table.insert(console.cheats, name) - return true - elseif not status and is_cheat then - table.remove_value(console.cheats, name) - return true - end - - return false -end - -function console.chat(...) - console.log(...) - events.emit("core:chat", ...) -end - function gui.process_template(source, params) local text = source:gsub("%%{([^}]+)}", function(n) local s = params[n] @@ -369,7 +323,7 @@ if ffi.os == "Windows" then ffi.cdef[[ unsigned long GetCurrentProcessId(); ]] - + os.pid = ffi.C.GetCurrentProcessId() else ffi.cdef[[ @@ -382,236 +336,20 @@ end math.randomseed(time.uptime() * 1536227939) rules = require "core:internal/rules" -local _rules = rules - -local function configure_SSAO() - -- Temporary using slot to configure built-in SSAO effect - local slot = gfx.posteffects.index("core:ssao") - gfx.posteffects.set_effect(slot, "ssao") - - -- Generating random SSAO samples - local buffer = Bytearray(0) - for i = 0, 63 do - local x = math.random() * 2.0 - 1.0 - local y = math.random() * 2.0 - 1.0 - local z = math.random() * 2.0 - local len = math.sqrt(x * x + y * y + z * z) - if len > 0 then - x = x / len - y = y / len - z = z / len - end - Bytearray.append(buffer, byteutil.pack("fff", x, y, z)) - end - gfx.posteffects.set_array(slot, "u_ssaoSamples", Bytearray_as_string(buffer)) - - local function update_ssao_quality(value) - value = math.min(value, 3) - gfx.posteffects.set_params(slot, { - u_kernelSize = value * 16, - u_radius = 0.4 / value, - u_bias = 0.006 / value / value, - }) - end - events.on("core:setting.graphics.ssao.set", update_ssao_quality) - - update_ssao_quality(__app.get_setting("graphics.ssao")) -end - -function __vc_on_hud_open() - local _hud_is_content_access = hud._is_content_access - local _hud_set_content_access = hud._set_content_access - local _hud_set_debug_cheats = hud._set_debug_cheats - - _rules.create("allow-cheats", true) - - _rules.create("allow-content-access", _hud_is_content_access(), function(value) - _hud_set_content_access(value) - end) - _rules.create("allow-flight", true, function(value) - input.set_enabled("player.flight", value) - end) - _rules.create("allow-noclip", true, function(value) - input.set_enabled("player.noclip", value) - end) - _rules.create("allow-attack", true, function(value) - input.set_enabled("player.attack", value) - end) - _rules.create("allow-destroy", true, function(value) - input.set_enabled("player.destroy", value) - end) - _rules.create("allow-cheat-movement", true, function(value) - input.set_enabled("movement.cheat", value) - end) - _rules.create("allow-fast-interaction", true, function(value) - input.set_enabled("player.fast_interaction", value) - end) - _rules.create("allow-debug-cheats", true, function(value) - _hud_set_debug_cheats(value) - end) - input.add_callback("devtools.console", function() - if menu.page ~= "" then - return - end - time.post_runnable(function() - hud.show_overlay("core:console", false, {"console"}) - end) - end) - input.add_callback("hud.chat", function() - if menu.page ~= "" then - return - end - time.post_runnable(function() - hud.show_overlay("core:console", false, {"chat"}) - end) - end) - input.add_callback("key:escape", function() - if menu.page ~= "" then - if not menu:back() then - menu:reset() - gui.set_active_frame("") - end - elseif hud.is_inventory_open() then - hud.close_inventory() - elseif gui.get_active_frame() then - gui.set_active_frame("") - else - hud.pause() - end - end) - hud.open_permanent("core:ingame_chat") - - configure_SSAO() -end - -local Schedule = require "core:schedule" - -local ScheduleGroup_mt = { - __index = { - publish = function(self, schedule) - local id = self._next_schedule - self._schedules[id] = schedule - self._next_schedule = id + 1 - end, - tick = function(self, dt) - for id, schedule in pairs(self._schedules) do - schedule:tick(dt) - end - self.common:tick(dt) - end, - remove = function(self, id) - self._schedules[id] = nil - end, - } -} - -local function ScheduleGroup() - return setmetatable({ - _next_schedule = 1, - _schedules = {}, - common = Schedule() - }, ScheduleGroup_mt) -end - time.schedules = {} -local RULES_FILE = "world:rules.toml" -function __vc_on_world_open() - time.schedules.world = ScheduleGroup() +table.merge(_G, require "core:internal/lifetime_events") - if not file.exists(RULES_FILE) then - return - end - local rule_values = toml.parse(file.read(RULES_FILE)) - for name, value in pairs(rule_values) do - _rules.set(name, value) - end -end - -function __vc_on_world_tick(tps) - time.schedules.world:tick(1.0 / tps) -end - -function __vc_process_before_quit() - block.__process_register_events() -end - -function __vc_on_world_save() - local rule_values = {} - for name, rule in pairs(rules.rules) do - rule_values[name] = rule.value - end - file.write(RULES_FILE, toml.tostring(rule_values)) -end - -local __close_all_descriptors = file.__close_all_descriptors -local __gui_util_reset_local = gui_util.__reset_local -local __stdcomp_reset = stdcomp.__reset -file.__close_all_descriptors = nil -gui_util.__reset_local = nil -stdcomp.reset = nil - -function __vc_on_world_quit() - _rules.clear() - __gui_util_reset_local() - __stdcomp_reset() - __close_all_descriptors() -end - -local fn_audio_reset_fetch_buffer = audio.__reset_fetch_buffer -audio.__reset_fetch_buffer = nil core.get_core_token = audio.input.__get_core_token -local __post_runnables = {} +require "core:internal/console" +require "core:internal/deprecated" -local function __process_post_runnables() - if #__post_runnables > 0 then - for _, func in ipairs(__post_runnables) do - local status, result = xpcall(func, __vc__error) - if not status then - debug.error("error in post_runnable: "..result) - end - end - __post_runnables = {} - end - local dead = {} - for name, co in pairs(__vc_named_coroutines) do - local success, err = coroutine.resume(co) - if not success then - debug.error(err) - end - if coroutine.status(co) == "dead" then - table.insert(dead, name) - end - end - for _, name in ipairs(dead) do - __vc_named_coroutines[name] = nil - end +------------------------------------------ +---- Unsafe functions cleanup section ---- +------------------------------------------ - fn_audio_reset_fetch_buffer() - debug.pull_events() - network.__process_events() - if not hud or not hud.is_paused() then - block.__process_register_events() - block.__perform_ticks(time.delta()) - end -end - -function __vc__process_post_runnables() - __vc__is_post_runnable = true - local success, err = pcall(__process_post_runnables) - if not success then - debug.error("an error ocurred while processing post-runnables: ".. err) - end - __vc__is_post_runnable = false -end - -function time.post_runnable(runnable) - table.insert(__post_runnables, runnable) -end - --- Hide unsafe debug.* functions local removed_names = { "getregistry", "getupvalue", "setupvalue", "upvalueid", "upvaluejoin", "sethook", "gethook", "getinfo" @@ -630,9 +368,9 @@ debug.getinfo = function(lvl, fields) return debuginfo end -require "core:internal/deprecated" - ffi = nil __vc_app = nil __vc_lock_internal_modules() __vc_lock_internal_modules = nil +__vc_update_coroutines = nil +__VC_SCRIPT_NAME = "" From 09af59c8162db14f7e4d41277b5c085585098982 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 25 May 2026 23:34:25 +0300 Subject: [PATCH 17/33] fix scripting::load_client_project_script --- src/logic/scripting/scripting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 806f92c24..37597b058 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -141,7 +141,7 @@ std::unique_ptr scripting::load_client_project_script( auto source = io::read_string(script); auto env = create_environment(nullptr); lua::pushenv(L, *env); - if (lua::getglobal(L, "__vc_app")) { + if (lua::getregistry(L, "app")) { lua::setfield(L, "app"); } lua::pop(L); From ce42bf1ace5c7ab73495ec91c416f47719baafdc Mon Sep 17 00:00:00 2001 From: RomanDonw Date: Thu, 28 May 2026 13:02:26 +1000 Subject: [PATCH 18/33] fixed missed include in /src/util/random.hpp --- src/util/random.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/random.hpp b/src/util/random.hpp index 075d5aa72..d6405dfac 100644 --- a/src/util/random.hpp +++ b/src/util/random.hpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace util { template < From 4565c6a3bdb660d9f310b0d5bd11df693f80fa18 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 28 May 2026 21:02:52 +0300 Subject: [PATCH 19/33] fix vc.is_headless --- src/logic/scripting/lua/lua_engine.cpp | 56 +++++++++++++------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index bd2f0359b..1f459e98e 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -17,6 +17,7 @@ static debug::Logger logger("lua-state"); static lua::State* main_thread = nullptr; +static bool headless_mode = false; using namespace lua; @@ -146,20 +147,45 @@ void lua::init_state(State* L, StateType stateType) { newusertype(L); newusertype(L); newusertype(L); + + pushboolean(L, headless_mode); + setglobal(L, "__VC_HEADLESS"); + + 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); + } + newusertype(L); + if (getglobal(L, "audio")) { + if (getglobal(L, "__vc_PCMStream")) { + setfield(L, "PCMStream"); + } + pop(L); + } + + if (stateType == StateType::GENERATOR) { + pushnil(L); + setglobal(L, "ffi"); + } } void lua::initialize(const EnginePaths& paths, const CoreParameters& params) { logger.info() << LUA_VERSION; logger.info() << LUAJIT_VERSION; + headless_mode = params.headless; main_thread = create_state( paths, params.headless ? StateType::SCRIPT : StateType::BASE ); lua::pushstring(main_thread, params.scriptFile.stem().u8string()); lua::setglobal(main_thread, "__VC_SCRIPT_NAME"); - - lua::pushboolean(main_thread, params.headless); - lua::setglobal(main_thread, "__VC_HEADLESS"); } void lua::finalize() { @@ -191,29 +217,5 @@ State* lua::create_state(const EnginePaths& paths, StateType stateType) { throw luaerror("could not initialize Lua state"); } init_state(L, 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); - } - newusertype(L); - if (getglobal(L, "audio")) { - if (getglobal(L, "__vc_PCMStream")) { - setfield(L, "PCMStream"); - } - pop(L); - } - - if (stateType == StateType::GENERATOR) { - pushnil(L); - setglobal(L, "ffi"); - } return L; } From e6ac30143c8e0ade446d61b92119ebdecb616e28 Mon Sep 17 00:00:00 2001 From: clasher113 Date: Fri, 29 May 2026 11:28:08 +0300 Subject: [PATCH 20/33] particles fix --- src/frontend/screens/LevelScreen.cpp | 2 +- src/graphics/render/Decorator.cpp | 3 +-- src/graphics/render/ParticlesRenderer.cpp | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index 452fc3111..33b637531 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -261,6 +261,7 @@ void LevelScreen::update(float delta) { const Player& player = playerController->getPlayer(); const Camera& camera = *player.currentCamera; decorator->update(paused ? 0.0f : delta, camera, weather); + renderer->update(camera, delta * !hud->isPause()); } void LevelScreen::draw(float delta) { @@ -271,7 +272,6 @@ void LevelScreen::draw(float delta) { if (!hud->isPause()) { scripting::on_entities_render(engine.getTime().getDelta()); } - renderer->update(camera, delta * !hud->isPause()); renderer->renderFrame(ctx, camera, hudVisible, *postProcessing); if (!hud->isPause()) { scripting::on_frontend_render(); diff --git a/src/graphics/render/Decorator.cpp b/src/graphics/render/Decorator.cpp index 59840770a..b988898ce 100644 --- a/src/graphics/render/Decorator.cpp +++ b/src/graphics/render/Decorator.cpp @@ -271,8 +271,7 @@ void Decorator::updateBlockEmitters(const Camera& camera) { remove = true; } } else { - iter = blockEmitters.erase(iter); - continue; + remove = true; } if (util::distance2(iter->first, glm::ivec3(camera.position)) > UPDATE_AREA_DIAMETER * UPDATE_AREA_DIAMETER) { diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index 3c00830dd..d588cd63a 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -48,6 +48,7 @@ static inline void update_particle( void ParticlesRenderer::updateParticles(float delta) { std::vector unusedTextures; + visibleParticles = 0; for (auto& [texture, vec] : particles) { if (vec.empty()) { @@ -186,7 +187,6 @@ void ParticlesRenderer::update(const Camera& camera, float delta) { void ParticlesRenderer::render(const Camera& camera) { aliveEmitters = emitters.size(); - visibleParticles = 0; bool backlight = settings.backlight.get(); From 31a90bd91172eb07923ea41c0ed18bdaee83f462 Mon Sep 17 00:00:00 2001 From: clasher113 Date: Fri, 29 May 2026 11:41:40 +0300 Subject: [PATCH 21/33] blocks.fill fix --- res/scripts/stdcmd.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/res/scripts/stdcmd.lua b/res/scripts/stdcmd.lua index 779ad50bf..c2b102675 100644 --- a/res/scripts/stdcmd.lua +++ b/res/scripts/stdcmd.lua @@ -133,6 +133,9 @@ console.add_command( "Fill specified zone with blocks", function(args, kwargs) local name, x1,y1,z1, x2,y2,z2 = unpack(args) + x1, x2 = math.min(x1, x2), math.max(x1, x2) + y1, y2 = math.min(y1, y2), math.max(y1, y2) + z1, z2 = math.min(z1, z2), math.max(z1, z2) local id = block.index(name) for y=y1,y2 do for z=z1,z2 do From 0bbde3a778762f7f6740cd65b494824111313c2e Mon Sep 17 00:00:00 2001 From: clasher113 Date: Fri, 29 May 2026 19:16:20 +0300 Subject: [PATCH 22/33] lighting fix --- src/lighting/LightSolver.cpp | 2 +- src/lighting/Lighting.cpp | 126 +++++++++++++++++----------------- src/lighting/Lighting.hpp | 5 +- src/logic/LevelController.cpp | 3 +- 4 files changed, 69 insertions(+), 67 deletions(-) diff --git a/src/lighting/LightSolver.cpp b/src/lighting/LightSolver.cpp index f43b4d487..a8d97424a 100644 --- a/src/lighting/LightSolver.cpp +++ b/src/lighting/LightSolver.cpp @@ -55,7 +55,7 @@ void LightSolver::remove(int x, int y, int z) { } void LightSolver::solve(Chunk* prevailingChunk) { - const int coords[] = { + static const int coords[] = { 0, 0, 1, 0, 0,-1, 0, 1, 0, diff --git a/src/lighting/Lighting.cpp b/src/lighting/Lighting.cpp index b17b6f333..c7f3445a5 100644 --- a/src/lighting/Lighting.cpp +++ b/src/lighting/Lighting.cpp @@ -14,9 +14,8 @@ static debug::Logger logger("lighting"); -Lighting::Lighting(const Content& content, Chunks& chunks) - : content(content), chunks(chunks) { - auto& indices = *content.getIndices(); +Lighting::Lighting(const ContentIndices& indices, Chunks& chunks) + : indices(indices), chunks(chunks) { solverR = std::make_unique(indices, chunks, 0); solverG = std::make_unique(indices, chunks, 1); solverB = std::make_unique(indices, chunks, 2); @@ -70,7 +69,7 @@ void Lighting::prebuildSkyLight(Chunk& chunk, const ContentIndices& indices) { } void Lighting::buildSkyLight(int cx, int cz){ - const auto blockDefs = content.getIndices()->blocks.getDefs(); + const auto blockDefs = indices.blocks.getDefs(); Chunk* chunk = chunks.getChunk(cx, cz); if (chunk == nullptr) { @@ -110,7 +109,7 @@ void Lighting::onChunkLoaded(int cx, int cz, bool expand) { auto& solverB = *this->solverB; auto& solverS = *this->solverS; - auto blockDefs = content.getIndices()->blocks.getDefs(); + auto blockDefs = indices.blocks.getDefs(); auto chunk = chunks.getChunk(cx, cz); if (chunk == nullptr) { logger.error() << "attempted to build lights to chunk missing in local matrix"; @@ -174,69 +173,72 @@ void Lighting::onChunkLoaded(int cx, int cz, bool expand) { } void Lighting::onBlockSet(int x, int y, int z, blockid_t id){ - const auto& block = content.getIndices()->blocks.require(id); - solverR->remove(x,y,z); - solverG->remove(x,y,z); - solverB->remove(x,y,z); + const auto& block = indices.blocks.require(id); - if (id == 0){ - auto chunk = chunks.getChunkByVoxel(glm::ivec3{x, y, z}); - solverR->solve(chunk); - solverG->solve(chunk); - solverB->solve(chunk); - if (chunks.getLight(x,y+1,z, 3) == 0xF){ - for (int i = y; i >= 0; i--){ - voxel* vox = chunks.get(x,i,z); - if ((vox == nullptr || vox->id != 0) && block.skyLightPassing) + auto chunk = chunks.getChunkByVoxel(glm::ivec3{x, y, z}); + + if (block.skyLightPassing) { + if (chunks.getLight(x, y + 1, z, 3) == 0xF) { + for (int i = y; i >= 0; i--) { + voxel* vox = chunks.get(x, i, z); + if (vox == nullptr || + indices.blocks.require(vox->id).skyLightPassing == + false) break; - solverS->add(x,i,z, 0xF); + solverS->add(x, i, z, 0xF); } } - const int coords[] = { - 0, 0, 1, - 0, 0,-1, - 0, 1, 0, - 0,-1, 0, - 1, 0, 0, - -1, 0, 0 - }; - for (int i = 0; i < 6; i++) { - int lx = x + coords[i * 3]; - int ly = y + coords[i * 3 + 1]; - int lz = z + coords[i * 3 + 2]; - - solverR->add(lx, ly, lz); - solverG->add(lx, ly, lz); - solverB->add(lx, ly, lz); - solverS->add(lx, ly, lz); - solverR->solve(chunk); - solverG->solve(chunk); - solverB->solve(chunk); - solverS->solve(chunk); - } } else { - auto chunk = chunks.getChunkByVoxel(glm::ivec3{x, y, z}); - if (!block.skyLightPassing){ - solverS->remove(x,y,z); - for (int i = y-1; i >= 0; i--){ - solverS->remove(x,i,z); - if (i == 0 || chunks.get(x,i-1,z)->id != 0){ - break; - } + solverS->remove(x, y, z); + for (int i = y - 1; i >= 0; i--) { + voxel* vox = chunks.get(x, i, z); + if (vox == nullptr || + indices.blocks.require(vox->id).skyLightPassing == false) { + break; } - solverS->solve(chunk); - } - solverR->solve(chunk); - solverG->solve(chunk); - solverB->solve(chunk); - - if (block.emission[0] || block.emission[1] || block.emission[2]){ - solverR->add(x,y,z,block.emission[0]); - solverG->add(x,y,z,block.emission[1]); - solverB->add(x,y,z,block.emission[2]); - solverR->solve(chunk); - solverG->solve(chunk); - solverB->solve(chunk); + solverS->remove(x, i, z); } + solverS->solve(); } + + solverR->remove(x, y, z); + solverG->remove(x, y, z); + solverB->remove(x, y, z); + + solverR->solve(chunk); + solverG->solve(chunk); + solverB->solve(chunk); + + static const int coords[] = { + 0, 0, 1, + 0, 0,-1, + 0, 1, 0, + 0,-1, 0, + 1, 0, 0, + -1, 0, 0 + }; + for (int i = 0; i < 6; i++) { + int lx = x + coords[i * 3]; + int ly = y + coords[i * 3 + 1]; + int lz = z + coords[i * 3 + 2]; + + solverR->add(lx, ly, lz); + solverG->add(lx, ly, lz); + solverB->add(lx, ly, lz); + solverS->add(lx, ly, lz); + } + + if (block.emission[0]) { + solverR->add(x, y, z, block.emission[0]); + } + if (block.emission[1]) { + solverG->add(x, y, z, block.emission[1]); + } + if (block.emission[2]) { + solverB->add(x, y, z, block.emission[2]); + } + solverR->solve(chunk); + solverG->solve(chunk); + solverB->solve(chunk); + solverS->solve(chunk); } diff --git a/src/lighting/Lighting.hpp b/src/lighting/Lighting.hpp index 622027f05..b5aec6d8f 100644 --- a/src/lighting/Lighting.hpp +++ b/src/lighting/Lighting.hpp @@ -2,21 +2,20 @@ #include "typedefs.hpp" -class Content; class ContentIndices; class Chunk; class Chunks; class LightSolver; class Lighting { - const Content& content; + const ContentIndices& indices; Chunks& chunks; std::unique_ptr solverR; std::unique_ptr solverG; std::unique_ptr solverB; std::unique_ptr solverS; public: - Lighting(const Content& content, Chunks& chunks); + Lighting(const ContentIndices& indices, Chunks& chunks); ~Lighting(); void clear(); diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 870fed923..ae0fa6798 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -2,6 +2,7 @@ #include +#include "content/Content.hpp" #include "debug/Logger.hpp" #include "engine/Engine.hpp" #include "engine/EnginePaths.hpp" @@ -41,7 +42,7 @@ LevelController::LevelController( if (clientPlayer) { chunks->lighting = std::make_unique( - level->content, *clientPlayer->chunks + *level->content.getIndices(), *clientPlayer->chunks ); } blocks = std::make_unique( From aa5bd51206a85b1ab6784d3b47041396c919f0e7 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 31 May 2026 21:04:51 +0300 Subject: [PATCH 23/33] add project args & add vc.get_project_args --- res/scripts/stdmin.lua | 4 ++++ src/engine/CoreParameters.hpp | 2 ++ src/logic/scripting/lua/lua_engine.cpp | 17 ++++++++++++++--- src/util/ArgsReader.hpp | 7 +++++++ src/util/command_line.cpp | 21 +++++++++++++++++++++ 5 files changed, 48 insertions(+), 3 deletions(-) diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 98d937b1a..904ca6081 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -1,5 +1,7 @@ local _vc_headless = __VC_HEADLESS +local _vc_project_args = __VC_PROJECT_ARGS __VC_HEADLESS = nil +__VC_PROJECT_ARGS = nil vc = { is_headless = function() @@ -300,6 +302,8 @@ require "core:internal/extensions/file" require "core:internal/extensions/table" require "core:internal/extensions/string" +vc.get_project_args = table.copy(_vc_project_args) + local bytearray = require "core:internal/bytearray" Bytearray = bytearray.FFIBytearray Bytearray_as_string = bytearray.FFIBytearray_as_string diff --git a/src/engine/CoreParameters.hpp b/src/engine/CoreParameters.hpp index b482f2bb5..b9f585f47 100644 --- a/src/engine/CoreParameters.hpp +++ b/src/engine/CoreParameters.hpp @@ -2,6 +2,7 @@ #include #include +#include struct CoreParameters { bool headless = false; @@ -13,4 +14,5 @@ struct CoreParameters { std::filesystem::path projectFolder; std::string debugServerString; int tps = 20; + std::unordered_map projectArgs; }; diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 1f459e98e..afb2f42eb 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -15,9 +15,12 @@ #include "usertypes/lua_type_pcmstream.hpp" #include "engine/Engine.hpp" -static debug::Logger logger("lua-state"); -static lua::State* main_thread = nullptr; -static bool headless_mode = false; +namespace { + debug::Logger logger("lua-state"); + lua::State* main_thread = nullptr; + bool headless_mode = false; + const std::unordered_map* project_args; +} using namespace lua; @@ -151,6 +154,13 @@ void lua::init_state(State* L, StateType stateType) { pushboolean(L, headless_mode); setglobal(L, "__VC_HEADLESS"); + createtable(L, 0, project_args->size()); + for (const auto& [key, value] : *project_args) { + pushstring(L, value); + setfield(L, key); + } + setglobal(L, "__VC_PROJECT_ARGS"); + auto file = "res:scripts/stdmin.lua"; auto src = io::read_string(file); lua::pop(L, lua::execute(L, 0, src, "core:scripts/stdmin.lua")); @@ -181,6 +191,7 @@ void lua::initialize(const EnginePaths& paths, const CoreParameters& params) { logger.info() << LUAJIT_VERSION; headless_mode = params.headless; + project_args = ¶ms.projectArgs; main_thread = create_state( paths, params.headless ? StateType::SCRIPT : StateType::BASE ); diff --git a/src/util/ArgsReader.hpp b/src/util/ArgsReader.hpp index 4c8453766..c13c1dd3e 100644 --- a/src/util/ArgsReader.hpp +++ b/src/util/ArgsReader.hpp @@ -26,6 +26,13 @@ namespace util { return last[0] == '-'; } + bool isNextKeywordArg() const { + if (pos >= argc) { + throw std::runtime_error("unexpected end"); + } + return argv[pos][0] == '-'; + } + std::string next() { if (pos >= argc) { throw std::runtime_error("unexpected end"); diff --git a/src/util/command_line.cpp b/src/util/command_line.cpp index e41a0ea57..7f34bcea4 100644 --- a/src/util/command_line.cpp +++ b/src/util/command_line.cpp @@ -97,11 +97,32 @@ static bool perform_keyword( throw std::runtime_error("unknown argument " + keyword); } +static void parse_project_args(util::ArgsReader& reader, CoreParameters& params) { + while (reader.hasNext()) { + std::string key = reader.next(); + if (!reader.isKeywordArg()) { + std::cerr << "-- argument expected" << std::endl; + return; + } + key = key.substr(2); + if (!reader.hasNext() || reader.isNextKeywordArg()) { + params.projectArgs[std::move(key)] = ""; + continue; + } + std::string value = reader.next(); + params.projectArgs[std::move(key)] = std::move(value); + } +} + bool parse_cmdline(int argc, char** argv, CoreParameters& params) { util::ArgsReader reader(argc, argv); reader.skip(); while (reader.hasNext()) { std::string token = reader.next(); + if (token == "--") { + parse_project_args(reader, params); + return true; + } if (reader.isKeywordArg()) { if (!perform_keyword(reader, token, params)) { return false; From cfacc3dd48960dabf9a2b22bfa543f3793e3322e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 31 May 2026 22:08:26 +0300 Subject: [PATCH 24/33] add table.merge_replace --- res/modules/internal/extensions/table.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/res/modules/internal/extensions/table.lua b/res/modules/internal/extensions/table.lua index a1856d383..b7c645568 100644 --- a/res/modules/internal/extensions/table.lua +++ b/res/modules/internal/extensions/table.lua @@ -57,6 +57,18 @@ function table.merge(t1, t2) return t1 end +function table.merge_replace(t1, t2) + for i, v in pairs(t2) do + if type(i) == "number" then + t1[#t1 + 1] = v + else + t1[i] = v + end + end + + return t1 +end + function table.map(t, func) for i, v in pairs(t) do t[i] = func(i, v) From b1974405c69596edf0cfd4a4b5cd42ba0d65448e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 31 May 2026 22:09:14 +0300 Subject: [PATCH 25/33] fix gui.load_document environment extension --- src/frontend/UiDocument.cpp | 19 ++++++++---------- src/frontend/UiDocument.hpp | 6 ++---- src/graphics/ui/gui_xml.cpp | 7 +++---- src/graphics/ui/gui_xml.hpp | 4 ++-- src/logic/scripting/lua/libs/libgui.cpp | 26 +++++++++++++++---------- src/logic/scripting/lua/lua_util.cpp | 11 +++++++++++ src/logic/scripting/lua/lua_util.hpp | 11 ++--------- 7 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/frontend/UiDocument.cpp b/src/frontend/UiDocument.cpp index a202c0d96..7e0c9bfc6 100644 --- a/src/frontend/UiDocument.cpp +++ b/src/frontend/UiDocument.cpp @@ -61,16 +61,19 @@ std::unique_ptr UiDocument::read( const scriptenv& penv, const std::string& name, const io::path& file, - const std::string& fileName + const std::string& fileName, + scriptenv&& env ) { const std::string text = io::read_string(file); auto xmldoc = xml::parse(file.string(), text); - auto env = penv == nullptr - ? scripting::create_doc_environment(scripting::get_root_environment(), name) - : scripting::create_doc_environment(penv, name); + if (env == nullptr) { + env = penv == nullptr + ? scripting::create_doc_environment(scripting::get_root_environment(), name) + : scripting::create_doc_environment(penv, name); + } - gui::UiXmlReader reader(gui, scriptenv(env)); + gui::UiXmlReader reader(gui, env); auto view = reader.readXML(file.string(), *xmldoc->getRoot()); view->setId("root"); UiDocScript script {}; @@ -82,9 +85,3 @@ std::unique_ptr UiDocument::read( } return std::make_unique(name, script, view, env); } - -std::shared_ptr UiDocument::readElement( - gui::GUI& gui, const io::path& file, const std::string& fileName -) { - return read(gui, nullptr, file.name(), file, fileName)->getRoot(); -} diff --git a/src/frontend/UiDocument.hpp b/src/frontend/UiDocument.hpp index 57077bec0..5b40ca8ca 100644 --- a/src/frontend/UiDocument.hpp +++ b/src/frontend/UiDocument.hpp @@ -50,9 +50,7 @@ public: const scriptenv& parent_env, const std::string& name, const io::path& file, - const std::string& fileName - ); - static std::shared_ptr readElement( - gui::GUI&, const io::path& file, const std::string& fileName + const std::string& fileName, + scriptenv&& env = nullptr ); }; diff --git a/src/graphics/ui/gui_xml.cpp b/src/graphics/ui/gui_xml.cpp index 33d57aa9a..e91b4553f 100644 --- a/src/graphics/ui/gui_xml.cpp +++ b/src/graphics/ui/gui_xml.cpp @@ -889,8 +889,8 @@ static std::shared_ptr read_iframe( return iframe; } -UiXmlReader::UiXmlReader(gui::GUI& gui, scriptenv&& env) - : gui(gui), env(std::move(env)) { +UiXmlReader::UiXmlReader(gui::GUI& gui, const scriptenv& env) + : gui(gui), env(env) { contextStack.emplace(""); add("image", read_image); add("canvas", read_canvas); @@ -922,8 +922,7 @@ void UiXmlReader::addIgnore(const std::string& tag) { ignored.insert(tag); } -std::shared_ptr UiXmlReader::readUINode(const xml::xmlelement& element -) { +std::shared_ptr UiXmlReader::readUINode(const xml::xmlelement& element) { if (element.has("if")) { const auto& cond = element.attr("if").getText(); if (cond.empty() || cond == "false" || cond == "nil") return nullptr; diff --git a/src/graphics/ui/gui_xml.hpp b/src/graphics/ui/gui_xml.hpp index 4a8a301ff..733503526 100644 --- a/src/graphics/ui/gui_xml.hpp +++ b/src/graphics/ui/gui_xml.hpp @@ -20,9 +20,9 @@ namespace gui { std::unordered_set ignored; std::stack contextStack; std::string filename; - scriptenv env; + const scriptenv& env; public: - UiXmlReader(gui::GUI& gui, scriptenv&& env); + UiXmlReader(gui::GUI& gui, const scriptenv& env); void add(const std::string& tag, uinode_reader reader); bool hasReader(const std::string& tag) const; diff --git a/src/logic/scripting/lua/libs/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp index 58a2f2be1..42aa0facf 100644 --- a/src/logic/scripting/lua/libs/libgui.cpp +++ b/src/logic/scripting/lua/libs/libgui.cpp @@ -1116,28 +1116,34 @@ static int l_gui_load_document(lua::State* L) { auto args = lua::tovalue(L, 3); auto prefix = filename.entryPoint(); - auto env = scripting::get_root_environment(); + auto parentEnv = scripting::get_root_environment(); if (content) { if (auto runtime = content->getPackRuntime(prefix)) { - env = runtime->getEnvironment(); + parentEnv = runtime->getEnvironment(); } } - auto documentPtr = UiDocument::read( - engine->getGUI(), std::move(env), alias, filename, filename.string() - ); - auto document = documentPtr.get(); - engine->requireAssets().store(std::move(documentPtr), alias); - + auto env = scripting::create_doc_environment(parentEnv, alias); // namespace extension if (lua::istable(L, 4)) { - if (lua::get_from(L, "table", "merge")) { - lua::pushenv(L, *document->getEnvironment()); + if (lua::get_from(L, "table", "merge_replace")) { + lua::pushenv(L, *env); lua::pushvalue(L, 4); lua::call(L, 2, 0); lua::pop(L); } } + auto documentPtr = UiDocument::read( + engine->getGUI(), + nullptr, + alias, + filename, + filename.string(), + std::move(env) + ); + auto document = documentPtr.get(); + engine->requireAssets().store(std::move(documentPtr), alias); + scripting::on_ui_open(document, {args}); return 0; } diff --git a/src/logic/scripting/lua/lua_util.cpp b/src/logic/scripting/lua/lua_util.cpp index 090e47a5d..e28e6ad37 100644 --- a/src/logic/scripting/lua/lua_util.cpp +++ b/src/logic/scripting/lua/lua_util.cpp @@ -414,3 +414,14 @@ std::string_view lua::bytearray_as_string(lua::State* L, int idx) { return bytearray_as_string_indirect(L, idx); } } + +void lua::loadbuffer( + lua::State* L, int env, const std::string& src, const std::string& file +) { + if (luaL_loadbuffer(L, src.c_str(), src.length(), file.c_str())) { + throw luaerror(tostring(L, -1)); + } + if (env && getregistry(L, ENVS_TABLE, env_name(env))) { + lua_setfenv(L, -2); + } +} diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index 562e641ef..d78afaad1 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -555,16 +555,9 @@ namespace lua { return lua_setfenv(L, idx); } - inline void loadbuffer( + void loadbuffer( lua::State* L, int env, const std::string& src, const std::string& file - ) { - if (luaL_loadbuffer(L, src.c_str(), src.length(), file.c_str())) { - throw luaerror(tostring(L, -1)); - } - if (env && getregistry(L, ENVS_TABLE, env_name(env))) { - lua_setfenv(L, -2); - } - } + ); inline void store_in( lua::State* L, const std::string& tableName, const std::string& name From 2f729bd3cbb793f20c5e060a75e38a18d0d88d0f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 31 May 2026 22:49:47 +0300 Subject: [PATCH 26/33] fix serialization error for specific types --- src/logic/scripting/lua/lua_util.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/logic/scripting/lua/lua_util.cpp b/src/logic/scripting/lua/lua_util.cpp index ded9cc79b..219b41c2c 100644 --- a/src/logic/scripting/lua/lua_util.cpp +++ b/src/logic/scripting/lua/lua_util.cpp @@ -76,6 +76,10 @@ int lua::pushwstring(State* L, const std::wstring& str) { return pushstring(L, util::wstr2str_utf8(str)); } +static std::string to_ptr_string(State* L, int idx) { + return std::to_string(reinterpret_cast(lua_topointer(L, idx))); +} + dv::value lua::tovalue(State* L, int idx) { using dv::value_type; auto type = lua::type(L, idx); @@ -95,11 +99,13 @@ dv::value lua::tovalue(State* L, int idx) { } } case LUA_TFUNCTION: - return "(lua_topointer(L, idx)) - ) + - ">"; + return ""; + case LUA_TTHREAD: + return ""; + case LUA_TUSERDATA: + return ""; + case LUA_TLIGHTUSERDATA: + return ""; case LUA_TSTRING: return std::string(tostring(L, idx)); case LUA_TTABLE: { From 1300488a8225589ffe9d21d5ad60694f66962765 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 31 May 2026 22:57:31 +0300 Subject: [PATCH 27/33] update userdata serialization --- src/logic/scripting/lua/lua_util.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/logic/scripting/lua/lua_util.cpp b/src/logic/scripting/lua/lua_util.cpp index 219b41c2c..a674535db 100644 --- a/src/logic/scripting/lua/lua_util.cpp +++ b/src/logic/scripting/lua/lua_util.cpp @@ -103,6 +103,9 @@ dv::value lua::tovalue(State* L, int idx) { case LUA_TTHREAD: return ""; case LUA_TUSERDATA: + if (auto userdata = touserdata(L, idx)) { + return "getTypeName() + " " + to_ptr_string(L, idx) + ">"; + } return ""; case LUA_TLIGHTUSERDATA: return ""; From d9ae1fe6d4725a01df5cece9005845228d1ebf83 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 31 May 2026 23:36:24 +0300 Subject: [PATCH 28/33] replace vc.get_project_args with vc.get_project_arg & rename table.merge_replace to table.extend --- res/modules/internal/extensions/table.lua | 2 +- res/scripts/stdmin.lua | 4 +++- src/logic/scripting/lua/libs/libgui.cpp | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/res/modules/internal/extensions/table.lua b/res/modules/internal/extensions/table.lua index b7c645568..d2d665755 100644 --- a/res/modules/internal/extensions/table.lua +++ b/res/modules/internal/extensions/table.lua @@ -57,7 +57,7 @@ function table.merge(t1, t2) return t1 end -function table.merge_replace(t1, t2) +function table.extend(t1, t2) for i, v in pairs(t2) do if type(i) == "number" then t1[#t1 + 1] = v diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 904ca6081..2868dd634 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -302,7 +302,9 @@ require "core:internal/extensions/file" require "core:internal/extensions/table" require "core:internal/extensions/string" -vc.get_project_args = table.copy(_vc_project_args) +vc.get_project_arg = function (name) + return _vc_project_args[name] +end local bytearray = require "core:internal/bytearray" Bytearray = bytearray.FFIBytearray diff --git a/src/logic/scripting/lua/libs/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp index 42aa0facf..cdd3dad28 100644 --- a/src/logic/scripting/lua/libs/libgui.cpp +++ b/src/logic/scripting/lua/libs/libgui.cpp @@ -1126,7 +1126,7 @@ static int l_gui_load_document(lua::State* L) { auto env = scripting::create_doc_environment(parentEnv, alias); // namespace extension if (lua::istable(L, 4)) { - if (lua::get_from(L, "table", "merge_replace")) { + if (lua::get_from(L, "table", "extend")) { lua::pushenv(L, *env); lua::pushvalue(L, 4); lua::call(L, 2, 0); From 38bebac37ee2a290315c3148576e98c825efa46d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 1 Jun 2026 00:08:13 +0300 Subject: [PATCH 29/33] fix vcm texture sides --- src/coders/vcm.cpp | 12 ++++++------ src/graphics/commons/Model.cpp | 24 ++++++++++++------------ src/graphics/ui/elements/ModelViewer.cpp | 10 +++++----- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/coders/vcm.cpp b/src/coders/vcm.cpp index 308fb8462..3083e1d36 100644 --- a/src/coders/vcm.cpp +++ b/src/coders/vcm.cpp @@ -55,12 +55,12 @@ model::Model VcmModel::squashed() const { } static const std::unordered_map side_indices { - {"north", 0}, - {"south", 1}, - {"top", 2}, - {"bottom", 3}, - {"west", 4}, - {"east", 5}, + {"east", 0}, + {"west", 1}, + {"bottom", 2}, + {"top", 3}, + {"south", 4}, + {"north", 5}, }; static bool to_boolean(const xml::Attribute& attr) { diff --git a/src/graphics/commons/Model.cpp b/src/graphics/commons/Model.cpp index 2de10ad30..8c2194775 100644 --- a/src/graphics/commons/Model.cpp +++ b/src/graphics/commons/Model.cpp @@ -136,18 +136,18 @@ void Mesh::addBox( const bool enabledSides[6], const glm::mat4& transform ) { - if (enabledSides[0]) // north - addPlane(pos+Z*size, X*size, Y*size, Z, uvs[0], transform); - if (enabledSides[1]) // south - addPlane(pos-Z*size, -X*size, Y*size, -Z, uvs[1], transform); - if (enabledSides[2]) // top - addPlane(pos+Y*size, X*size, -Z*size, Y, uvs[2] * glm::vec2(-1), transform); - if (enabledSides[3]) // bottom - addPlane(pos-Y*size, X*size, Z*size, -Y, uvs[3] * glm::vec2(-1, 1), transform); - if (enabledSides[4]) // west - addPlane(pos+X*size, -Z*size, Y*size, X, uvs[4], transform); - if (enabledSides[5]) // east - addPlane(pos-X*size, Z*size, Y*size, -X, uvs[5] * glm::vec2(-1, 1), transform); + if (enabledSides[0]) // east + addPlane(pos-X*size, Z*size, Y*size, -X, uvs[0], transform); + if (enabledSides[1]) // west + addPlane(pos+X*size, -Z*size, Y*size, X, uvs[1] * glm::vec2(-1, 1), transform); + if (enabledSides[2]) // bottom + addPlane(pos-Y*size, X*size, Z*size, -Y, uvs[2], transform); + if (enabledSides[3]) // top + addPlane(pos+Y*size, X*size, -Z*size, Y, uvs[3] * glm::vec2(-1, 1), transform); + if (enabledSides[4]) // south + addPlane(pos-Z*size, -X*size, Y*size, -Z, uvs[4], transform); + if (enabledSides[5]) // north + addPlane(pos+Z*size, X*size, Y*size, Z, uvs[5] * glm::vec2(-1, 1), transform); } void Mesh::scale(const glm::vec3& size) { diff --git a/src/graphics/ui/elements/ModelViewer.cpp b/src/graphics/ui/elements/ModelViewer.cpp index c1d736d71..727110f8b 100644 --- a/src/graphics/ui/elements/ModelViewer.cpp +++ b/src/graphics/ui/elements/ModelViewer.cpp @@ -83,12 +83,12 @@ static util::TextureRegion determine_texture_region( const Assets& assets, const std::string& texture ) { static std::array faces { - "blocks:dbg_north", - "blocks:dbg_south", - "blocks:dbg_top", - "blocks:dbg_bottom", - "blocks:dbg_west", "blocks:dbg_east", + "blocks:dbg_west", + "blocks:dbg_bottom", + "blocks:dbg_top", + "blocks:dbg_south", + "blocks:dbg_north", }; if (texture.length() < 2 || texture.at(0) != '$') { From 0ffefddee1781389fc6e8014ea00856e09cfff1d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 1 Jun 2026 01:08:24 +0300 Subject: [PATCH 30/33] fix post-runnable context in content-related functions --- res/modules/internal/lifetime_events.lua | 5 +++++ res/scripts/stdlib.lua | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/res/modules/internal/lifetime_events.lua b/res/modules/internal/lifetime_events.lua index 48a96c550..e1460c22c 100644 --- a/res/modules/internal/lifetime_events.lua +++ b/res/modules/internal/lifetime_events.lua @@ -200,6 +200,8 @@ local function __process_post_runnables() end end +local __vc__is_post_runnable = false + function __vc__process_post_runnables() __vc__is_post_runnable = true local success, err = pcall(__process_post_runnables) @@ -221,4 +223,7 @@ return { __vc_on_world_save = __vc_on_world_save, __vc_on_world_quit = __vc_on_world_quit, __vc__process_post_runnables = __vc__process_post_runnables, + __vc_is_post_runnable_context = function() + return __vc__is_post_runnable + end } diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 702d50072..093501d75 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -26,7 +26,6 @@ local function tb_frame_tostring(frame) end local __vc__app_script_coroutine -local __vc__is_post_runnable = false local function complete_app_lib(app) local __app_load_content = app.load_content @@ -46,7 +45,7 @@ local function complete_app_lib(app) app.tick = __app_tick local function call_in_app_script_co(func, ...) - if __vc__is_post_runnable then + if __vc_is_post_runnable_context() then func(...) return end From ce189c810d13512225b82933724603781a1ecef1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 1 Jun 2026 01:38:06 +0300 Subject: [PATCH 31/33] fix: app.reconfig_packs attempts to remove indices in generator test mode --- src/logic/EngineController.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/logic/EngineController.cpp b/src/logic/EngineController.cpp index 9231ffe67..c3d775919 100644 --- a/src/logic/EngineController.cpp +++ b/src/logic/EngineController.cpp @@ -378,8 +378,11 @@ void EngineController::reconfigPacks( auto& manager = contentControl.scan(); reconfig_packs_inside(manager, names, packsToAdd, packsToRemove); + const auto& settings = engine.getSettings(); auto& wfile = *world->wfile; - wfile.removeIndices(packsToRemove); + if (!settings.debug.generatorTestMode.get()) { + wfile.removeIndices(packsToRemove); + } wfile.writePacks(manager.getAll(names)); reopenWorld(world); } From d69716ed11369cc3ccacefb90361c8203cf99389 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 3 Jun 2026 19:00:40 +0300 Subject: [PATCH 32/33] fix this something --- src/graphics/core/Mesh.inl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/graphics/core/Mesh.inl b/src/graphics/core/Mesh.inl index c4cec9669..913da2bce 100644 --- a/src/graphics/core/Mesh.inl +++ b/src/graphics/core/Mesh.inl @@ -73,7 +73,7 @@ Mesh::~Mesh() { MeshStats::meshesCount--; glDeleteVertexArrays(1, &vao); glDeleteBuffers(1, &vbo); - for (int i = ibos.size() - 1; i >= 0; i--) { + for (int i = 0; i < ibos.size(); i++) { glDeleteBuffers(1, &ibos[i].ibo); } } @@ -98,7 +98,7 @@ void Mesh::reload( glBufferData(GL_ARRAY_BUFFER, 0, {}, GL_STREAM_DRAW); } - for (int i = indices.size(); i < ibos.size(); i++) { + for (int i = 0; i < ibos.size(); i++) { glDeleteBuffers(1, &ibos[i].ibo); } ibos.clear(); From fd6a6e731106a0cc316df0dca55a8c308631a72c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 3 Jun 2026 19:12:46 +0300 Subject: [PATCH 33/33] fix build warning --- src/audio/AL/ALAudio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/audio/AL/ALAudio.cpp b/src/audio/AL/ALAudio.cpp index 527fc7c71..d681d15a2 100644 --- a/src/audio/AL/ALAudio.cpp +++ b/src/audio/AL/ALAudio.cpp @@ -711,7 +711,7 @@ std::unique_ptr ALAudio::create(const AudioSettings& settings) { return nullptr; } bool effects = true; - ALint attribs[4] {}; + [[maybe_unused]] ALint attribs[4] {}; if (alcIsExtensionPresent(device, "ALC_EXT_EFX") == AL_TRUE) { logger.info() << "AL effects extension present"; attribs[0] = ALC_MAX_AUXILIARY_SENDS;