From c83a73102eea4a9cc31a93a2c61442d26fff6699 Mon Sep 17 00:00:00 2001 From: dagger Date: Tue, 1 Sep 2026 20:11:31 +0700 Subject: [PATCH 1/7] add input.get_mouse_scroll() --- doc/en/scripting/builtins/libinput.md | 7 +++++++ doc/ru/scripting/builtins/libinput.md | 4 ++++ src/logic/scripting/lua/libs/libinput.cpp | 7 +++++++ 3 files changed, 18 insertions(+) diff --git a/doc/en/scripting/builtins/libinput.md b/doc/en/scripting/builtins/libinput.md index 9856abc0b..9cf754d33 100644 --- a/doc/en/scripting/builtins/libinput.md +++ b/doc/en/scripting/builtins/libinput.md @@ -63,6 +63,13 @@ input.get_mouse_delta() --> {int, int} Returns cursor movement delta. +```lua +input.get_mouse_scroll() --> int +``` + +Returns the vertical mouse wheel offset accumulated during the current frame. +A positive value indicates scrolling up; a negative value indicates scrolling down. + ```lua input.get_bindings() --> strings array ``` diff --git a/doc/ru/scripting/builtins/libinput.md b/doc/ru/scripting/builtins/libinput.md index 17df1f8aa..f3ac4b699 100644 --- a/doc/ru/scripting/builtins/libinput.md +++ b/doc/ru/scripting/builtins/libinput.md @@ -34,6 +34,10 @@ input.get_mouse_pos() -> {number, number} -- Возращает дельту позиции курсора. input.get_mouse_delta() -> {number, number} + +-- Возвращает вертикальное смещение колеса мыши, накопленное за текущий кадр. +-- Положительное значение соответствует прокрутке вверх, отрицательное - вниз. +input.get_mouse_scroll() -> number ``` ## Создание обработчиков diff --git a/src/logic/scripting/lua/libs/libinput.cpp b/src/logic/scripting/lua/libs/libinput.cpp index 28f87f3c2..68469bb23 100644 --- a/src/logic/scripting/lua/libs/libinput.cpp +++ b/src/logic/scripting/lua/libs/libinput.cpp @@ -91,6 +91,12 @@ static int l_get_mouse_delta(lua::State* L) { return lua::pushvec2(L, engine->getInput().getCursor().delta); } +static int l_get_mouse_scroll(lua::State* L) { + if (engine->isHeadless()) + return 0; + return lua::pushinteger(L, engine->getInput().getScroll()); +} + static int l_get_bindings(lua::State* L) { if (engine->isHeadless()) return 0; @@ -181,6 +187,7 @@ const luaL_Reg inputlib[] = { {"add_callback", lua::wrap}, {"get_mouse_pos", lua::wrap}, {"get_mouse_delta", lua::wrap}, + {"get_mouse_scroll", lua::wrap}, {"get_bindings", lua::wrap}, {"get_binding_text", lua::wrap}, {"is_active", lua::wrap}, From 9dacf7038fda3a931c7aa8cb72329327c3648394 Mon Sep 17 00:00:00 2001 From: Xertis <118364459+Xertis@users.noreply.github.com> Date: Thu, 3 Sep 2026 19:32:49 +0300 Subject: [PATCH 2/7] Implementation of socket:peek --- doc/en/scripting/builtins/libnetwork.md | 15 +++++++++++++++ doc/ru/scripting/builtins/libnetwork.md | 13 +++++++++++++ res/scripts/classes.lua | 11 +++++++++++ src/logic/scripting/lua/libs/libnetwork.cpp | 20 +++++++++++++++++--- src/network/Sockets.cpp | 19 +++++++++++++++---- src/network/commons.hpp | 1 + 6 files changed, 72 insertions(+), 7 deletions(-) diff --git a/doc/en/scripting/builtins/libnetwork.md b/doc/en/scripting/builtins/libnetwork.md index 3ceac9c84..c4e61837d 100644 --- a/doc/en/scripting/builtins/libnetwork.md +++ b/doc/en/scripting/builtins/libnetwork.md @@ -92,6 +92,21 @@ socket:recv_async( [optional] usetable: bool=false ) -> nil|table|Bytearray +-- `peek` and `peek_async` are analogous to the `recv` and `recv_async` methods +-- with the exception that `peek` and `peek_async` do not advance the socket buffer position +-- This means they do not remove bytes from the socket, so the bytes can be received after +socket:peek( + length: int, + [optional] usetable: boolean=false +) -> nil|table|Bytearray + +socket:peek_async( + length: int, + [optional] usetable: boolean=false +) -> nil|table|Bytearray + +Translated with DeepL.com (free version) + -- Closes the connection socket:close() diff --git a/doc/ru/scripting/builtins/libnetwork.md b/doc/ru/scripting/builtins/libnetwork.md index 46a3eded8..98c4e51e0 100644 --- a/doc/ru/scripting/builtins/libnetwork.md +++ b/doc/ru/scripting/builtins/libnetwork.md @@ -92,6 +92,19 @@ socket:recv_async( [опционально] usetable: boolean=false ) -> nil|table|Bytearray +-- `peek` и `peek_async` являются аналогами методов `recv` и `recv_async` +-- за тем исключением, что `peek` и `peek_async` не двигают позицию буфера сокета +-- Это означает, что они не удаляют байты из сокета, а значит байты могут быть прочитаны после +socket:peek( + length: int, + [опционально] usetable: boolean=false +) -> nil|table|Bytearray + +socket:peek_async( + length: int, + [опционально] usetable: boolean=false +) -> nil|table|Bytearray + -- Оборачивает сокет в io_stream (см. ../io_stream.md) socket:as_stream( [опционально] binary_mode: boolean=true diff --git a/res/scripts/classes.lua b/res/scripts/classes.lua index a057d52e6..280c5ddeb 100644 --- a/res/scripts/classes.lua +++ b/res/scripts/classes.lua @@ -39,6 +39,7 @@ end local Socket = {__index={ send=function(self, ...) return network.__send(self.id, ...) end, recv=function(self, ...) return network.__recv(self.id, ...) end, + peek=function(self, ...) return network.__peek(self.id, ...) end, recv_async=function(self, length, usetable) while self:is_alive() do local available = self:available() @@ -49,6 +50,16 @@ local Socket = {__index={ end return self:recv(length, usetable) end, + peek_async=function(self, length, usetable) + while self:is_alive() do + local available = self:available() + if available >= length then + return self:peek(length, usetable) + end + coroutine.yield() + end + return self:peek(length, usetable) + end, as_stream=network.__as_stream, close=function(self) return network.__close(self.id) end, available=function(self) return network.__available(self.id) or 0 end, diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp index 7fbf6df8d..55db6e550 100644 --- a/src/logic/scripting/lua/libs/libnetwork.cpp +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -237,7 +237,7 @@ static int l_udp_server_send_to(lua::State* L, network::Network& network) { return 0; } -static int l_recv(lua::State* L, network::Network& network) { +static int read(lua::State* L, network::Network& network, int (*fn)(network::TcpConnection*, char*, int)) { u64id_t id = lua::tointeger(L, 1); int length = lua::tointeger(L, 2); @@ -248,11 +248,12 @@ static int l_recv(lua::State* L, network::Network& network) { } auto tcpConnection = dynamic_cast(connection); + auto tcp = dynamic_cast(connection); length = glm::min(length, tcpConnection->available()); util::Buffer buffer(length); - - int size = tcpConnection->recv(buffer.data(), length); + + int size = fn(tcp, buffer.data(), length); if (size == -1) { return 0; } @@ -268,6 +269,18 @@ static int l_recv(lua::State* L, network::Network& network) { } } +static int l_recv(lua::State* L, network::Network& network) { + return read(L, network, [](auto* tcp, char* buf, int len) { + return tcp->recv(buf, len); + }); +} + +static int l_peek(lua::State* L, network::Network& network) { + return read(L, network, [](auto* tcp, char* buf, int len) { + return tcp->peek(buf, len); + }); +} + static int l_available(lua::State* L, network::Network& network) { u64id_t id = lua::tointeger(L, 1); @@ -566,6 +579,7 @@ const luaL_Reg networklib[] = { {"__close", wrap}, {"__send", wrap}, {"__recv", wrap}, + {"__peek", wrap}, {"__available", wrap}, {"__is_alive", wrap}, {"__is_connected", wrap}, diff --git a/src/network/Sockets.cpp b/src/network/Sockets.cpp index 7db81849c..a0b575c44 100644 --- a/src/network/Sockets.cpp +++ b/src/network/Sockets.cpp @@ -196,15 +196,26 @@ public: }); } - int recv(char* buffer, size_t length) override { - std::lock_guard lock(mutex); - + int read(char* buffer, size_t length) { if (state != ConnectionState::CONNECTED && readBatch.empty()) { return -1; } int size = std::min(readBatch.size(), length); std::memcpy(buffer, readBatch.data(), size); - readBatch.erase(readBatch.begin(), readBatch.begin() + size); + return size; + } + + int peek(char* buffer, size_t length) override { + std::lock_guard lock(mutex); + return read(buffer, length); + } + + int recv(char* buffer, size_t length) override { + std::lock_guard lock(mutex); + int size = read(buffer, length); + if (size != -1) { + readBatch.erase(readBatch.begin(), readBatch.begin() + size); + } return size; } diff --git a/src/network/commons.hpp b/src/network/commons.hpp index 37fc1a01e..8b834bb3e 100644 --- a/src/network/commons.hpp +++ b/src/network/commons.hpp @@ -79,6 +79,7 @@ namespace network { class ReadableConnection : public Connection { public: virtual int recv(char* buffer, size_t length) = 0; + virtual int peek(char* buffer, size_t length) = 0; virtual int available() = 0; }; From 2060f3f153aedb39807c33faf10f731e45ecb550 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 3 Sep 2026 22:25:58 +0300 Subject: [PATCH 3/7] add project arguments table passing in app.start_background_instance --- src/logic/scripting/lua/libs/libapp.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/logic/scripting/lua/libs/libapp.cpp b/src/logic/scripting/lua/libs/libapp.cpp index b5a385bb9..0058fcada 100644 --- a/src/logic/scripting/lua/libs/libapp.cpp +++ b/src/logic/scripting/lua/libs/libapp.cpp @@ -405,6 +405,20 @@ static int l_start_background_instance(lua::State* L) { auto scriptPath = lua::require_lstring(L, 1); io::path outputPath = lua::isstring(L, 2) ? lua::require_lstring(L, 2) : ""; + + std::vector> projectArgs; + if (lua::istable(L, 3)) { + lua::pushnil(L); + while (lua::next(L, 3)) { + lua::pushvalue(L, -2); + auto key = lua::tolstring(L, -1); + auto value = lua::tolstring(L, -2); + projectArgs.emplace_back(key, value); + lua::pop(L, 2); + } + lua::pop(L); + } + const auto& paths = engine->getPaths(); std::vector args { @@ -419,6 +433,17 @@ static int l_start_background_instance(lua::State* L) { args.emplace_back("--project"); args.emplace_back(io::resolve(engine->getProject().path).u8string()); + if (!projectArgs.empty()) { + args.emplace_back("--"); + for (const auto& [key, value] : projectArgs) { + if (key.empty()) { + throw std::runtime_error("empty project argument name passed"); + } + args.emplace_back("--" + key); + args.emplace_back(value); + } + } + int handle = -1; for (int i = 0; i < ::processes.size(); i++) { if (!::processes[i] || !::processes[i]->isActive()) { From fb20db047d86042977341e68d68aa16e2a831eed Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 3 Sep 2026 22:31:28 +0300 Subject: [PATCH 4/7] update doc/*/scripting/builtins/libapp.md --- doc/en/scripting/builtins/libapp.md | 4 +++- doc/ru/scripting/builtins/libapp.md | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/en/scripting/builtins/libapp.md b/doc/en/scripting/builtins/libapp.md index 06365fc99..f3d46238f 100644 --- a/doc/en/scripting/builtins/libapp.md +++ b/doc/en/scripting/builtins/libapp.md @@ -172,7 +172,9 @@ app.start_background_instance( -- script file app_script: string, -- log file - output_file: string | nil + output_file: string, + -- project parameters (can be read with vc.get_project_arg(name)) + project_args: table | nil ) -> int -- Checks if the engine sub-instance is alive. diff --git a/doc/ru/scripting/builtins/libapp.md b/doc/ru/scripting/builtins/libapp.md index 53a2224f2..71886a61f 100644 --- a/doc/ru/scripting/builtins/libapp.md +++ b/doc/ru/scripting/builtins/libapp.md @@ -162,7 +162,9 @@ app.start_background_instance( -- файл сценария app_script: string, -- файл лога - output_file: string | nil + output_file: string, + -- параметры проекта, что будут доступны через vc.get_project_arg(name) + project_args: table | nil, ) -> int -- Проверяет, жив ли под-экземпляр движка. From be6be8839ff0097824ab3e236887dbf619817ff5 Mon Sep 17 00:00:00 2001 From: Xertis <118364459+Xertis@users.noreply.github.com> Date: Thu, 3 Sep 2026 23:48:08 +0300 Subject: [PATCH 5/7] Update network_tcp.lua --- dev/tests/network_tcp.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/network_tcp.lua b/dev/tests/network_tcp.lua index 379065476..a7e57ff22 100644 --- a/dev/tests/network_tcp.lua +++ b/dev/tests/network_tcp.lua @@ -13,6 +13,7 @@ for i=1,3 do print("client-listener started") local received_text = "" while client:is_alive() and #received_text < #text do + client:peek(math.random(0, 100)) local received = client:recv(512) if received then received_text = received_text .. utf8.tostring(received) From 9de8331692c3e4d9c1d10b49dbf204ebfa3aad5b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 3 Sep 2026 23:58:20 +0300 Subject: [PATCH 6/7] fix SystemProcess::isActive macos warning --- src/util/platform.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/platform.cpp b/src/util/platform.cpp index 5d5e489bc..36b36767c 100644 --- a/src/util/platform.cpp +++ b/src/util/platform.cpp @@ -267,12 +267,12 @@ public: } bool isActive() const override { -#ifdef __linux__ +#ifndef _WIN32 if (kill(pid, 0) == 0) { return true; } return errno != ESRCH; -#elif defined(_WIN32) +#else if (processHandle == nullptr) { return false; } From 9fe42edca3cdfb3ab1b5ac5d61bcd68769ad05dc Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 4 Sep 2026 00:09:31 +0300 Subject: [PATCH 7/7] fix the fix --- src/util/platform.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/util/platform.cpp b/src/util/platform.cpp index 36b36767c..278fbaa1b 100644 --- a/src/util/platform.cpp +++ b/src/util/platform.cpp @@ -267,12 +267,12 @@ public: } bool isActive() const override { -#ifndef _WIN32 +#ifdef __linux__ if (kill(pid, 0) == 0) { return true; } return errno != ESRCH; -#else +#elif defined(_WIN32) if (processHandle == nullptr) { return false; } @@ -281,6 +281,9 @@ public: return exitCode == STILL_ACTIVE; } return false; +#elif defined(__APPLE__) + // FIXME: completely implement SystemProcess for MacOS + return false; #endif }