From 7d367a9793763c3c9cc98a75b248e749c6a0222c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 10 Nov 2025 20:48:26 +0300 Subject: [PATCH 01/25] add app.set_title --- res/scripts/stdlib.lua | 1 + src/engine/WindowControl.cpp | 9 +++++---- src/logic/scripting/lua/libs/libcore.cpp | 8 ++++++++ src/window/Window.hpp | 1 + src/window/detail/GLFWWindow.cpp | 4 ++++ 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index eedc9d43..3cbf60a4 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -47,6 +47,7 @@ local function complete_app_lib(app) end app.reset_content = core.reset_content app.is_content_loaded = core.is_content_loaded + app.set_title = core.set_title function app.config_packs(packs_list) -- Check if packs are valid and add dependencies to the configuration diff --git a/src/engine/WindowControl.cpp b/src/engine/WindowControl.cpp index 5ae96965..a93c1599 100644 --- a/src/engine/WindowControl.cpp +++ b/src/engine/WindowControl.cpp @@ -33,11 +33,12 @@ WindowControl::Result WindowControl::initialize() { auto& settings = engine.getSettings(); std::string title = project.title; - if (title.empty()) { - title = "VoxelCore v" + - std::to_string(ENGINE_VERSION_MAJOR) + "." + - std::to_string(ENGINE_VERSION_MINOR); + if (!title.empty()) { + title += " - "; } + title += "VoxelCore v" + + std::to_string(ENGINE_VERSION_MAJOR) + "." + + std::to_string(ENGINE_VERSION_MINOR); if (ENGINE_DEBUG_BUILD) { title += " [debug]"; } diff --git a/src/logic/scripting/lua/libs/libcore.cpp b/src/logic/scripting/lua/libs/libcore.cpp index 8f8a7bc8..3989fd7f 100644 --- a/src/logic/scripting/lua/libs/libcore.cpp +++ b/src/logic/scripting/lua/libs/libcore.cpp @@ -25,6 +25,7 @@ #include "graphics/ui/gui_util.hpp" #include "graphics/ui/GUI.hpp" #include "graphics/ui/elements/Menu.hpp" +#include "window/Window.hpp" using namespace scripting; @@ -298,6 +299,12 @@ static int l_capture_output(lua::State* L) { return 1; } +static int l_set_title(lua::State* L) { + auto title = lua::require_string(L, 1); + engine->getWindow().setTitle(title); + return 0; +} + const luaL_Reg corelib[] = { {"blank", lua::wrap}, {"get_version", lua::wrap}, @@ -319,5 +326,6 @@ const luaL_Reg corelib[] = { {"open_url", lua::wrap}, {"quit", lua::wrap}, {"capture_output", lua::wrap}, + {"set_title", lua::wrap}, {nullptr, nullptr} }; diff --git a/src/window/Window.hpp b/src/window/Window.hpp index 71004470..a5a3240b 100644 --- a/src/window/Window.hpp +++ b/src/window/Window.hpp @@ -36,6 +36,7 @@ public: virtual void setMode(WindowMode mode) = 0; virtual WindowMode getMode() const = 0; + virtual void setTitle(const std::string& title) = 0; virtual void setIcon(const ImageData* image) = 0; virtual void pushScissor(glm::vec4 area) = 0; diff --git a/src/window/detail/GLFWWindow.cpp b/src/window/detail/GLFWWindow.cpp index a80c10e0..236ce16d 100644 --- a/src/window/detail/GLFWWindow.cpp +++ b/src/window/detail/GLFWWindow.cpp @@ -468,6 +468,10 @@ public: return mode; } + void setTitle(const std::string& title) override { + glfwSetWindowTitle(window, title.c_str()); + } + void setIcon(const ImageData* image) override { if (image == nullptr) { glfwSetWindowIcon(window, 0, nullptr); From fc68d6cecab89829b1c1fde24a4f4c4e07e857ce Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 12 Nov 2025 01:16:33 +0300 Subject: [PATCH 02/25] update InlineFrame::act --- src/graphics/ui/elements/InlineFrame.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/graphics/ui/elements/InlineFrame.cpp b/src/graphics/ui/elements/InlineFrame.cpp index 13625058..5df635b8 100644 --- a/src/graphics/ui/elements/InlineFrame.cpp +++ b/src/graphics/ui/elements/InlineFrame.cpp @@ -36,12 +36,11 @@ void InlineFrame::setDocument(const std::shared_ptr& document) { } void InlineFrame::act(float delta) { - Container::act(delta); - if (document || src.empty()) { - return; + if (document == nullptr && !src.empty()) { + const auto& assets = *gui.getEngine().getAssets(); + setDocument(assets.getShared(src)); } - const auto& assets = *gui.getEngine().getAssets(); - setDocument(assets.getShared(src)); + Container::act(delta); } void InlineFrame::setSize(glm::vec2 size) { From fb78e9f694a638a39f1a38be1a37cb7f35739ad6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 12 Nov 2025 01:17:10 +0300 Subject: [PATCH 03/25] replace glfwWaitEvents with glfwWaitEventsTimeout --- src/window/detail/GLFWWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/window/detail/GLFWWindow.cpp b/src/window/detail/GLFWWindow.cpp index 236ce16d..88df3940 100644 --- a/src/window/detail/GLFWWindow.cpp +++ b/src/window/detail/GLFWWindow.cpp @@ -188,7 +188,7 @@ public: codepoints.clear(); pressedKeys.clear(); if (waitForRefresh) { - glfwWaitEvents(); + glfwWaitEventsTimeout(0.5); } else { glfwPollEvents(); } From 1fd81da82e3e2cd6c4890050062952bf931820ee Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 14 Nov 2025 00:33:57 +0300 Subject: [PATCH 04/25] add 'onrightclick' uinode event --- src/graphics/ui/elements/Container.cpp | 1 + src/graphics/ui/elements/UINode.cpp | 11 +++++++++++ src/graphics/ui/elements/UINode.hpp | 5 ++++- src/graphics/ui/gui_xml.cpp | 4 ++++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/graphics/ui/elements/Container.cpp b/src/graphics/ui/elements/Container.cpp index 8d45c2a1..5d5375c1 100644 --- a/src/graphics/ui/elements/Container.cpp +++ b/src/graphics/ui/elements/Container.cpp @@ -177,6 +177,7 @@ void Container::add(const std::shared_ptr& node) { parent->setMustRefresh(); parent = parent->getParent(); } + gui.getWindow().setShouldRefresh(); } void Container::add(const std::shared_ptr& node, glm::vec2 pos) { diff --git a/src/graphics/ui/elements/UINode.cpp b/src/graphics/ui/elements/UINode.cpp index 1e8f48d6..8c3c0765 100644 --- a/src/graphics/ui/elements/UINode.cpp +++ b/src/graphics/ui/elements/UINode.cpp @@ -69,6 +69,11 @@ UINode* UINode::listenAction(const onaction& action) { return this; } +UINode* UINode::listenRightClick(const onaction& action) { + rightClickCallbacks.listen(action); + return this; +} + UINode* UINode::listenDoubleClick(const onaction& action) { doubleClickCallbacks.listen(action); return this; @@ -88,6 +93,12 @@ void UINode::click(int, int) { pressed = true; } +void UINode::clicked(Mousecode button) { + if (button == Mousecode::BUTTON_2) { + rightClickCallbacks.notify(gui); + } +} + void UINode::doubleClick(int x, int y) { pressed = true; if (isInside(glm::vec2(x, y))) { diff --git a/src/graphics/ui/elements/UINode.hpp b/src/graphics/ui/elements/UINode.hpp index fc64527f..6024f841 100644 --- a/src/graphics/ui/elements/UINode.hpp +++ b/src/graphics/ui/elements/UINode.hpp @@ -121,6 +121,8 @@ namespace gui { vec2supplier sizefunc = nullptr; /// @brief 'onclick' callbacks ActionsSet actions; + /// @brief 'onrightclick' callbacks + ActionsSet rightClickCallbacks; /// @brief 'ondoubleclick' callbacks ActionsSet doubleClickCallbacks; /// @brief 'onfocus' callbacks @@ -188,6 +190,7 @@ namespace gui { int getZIndex() const; virtual UINode* listenAction(const onaction& action); + virtual UINode* listenRightClick(const onaction& action); virtual UINode* listenDoubleClick(const onaction& action); virtual UINode* listenFocus(const onaction& action); virtual UINode* listenDefocus(const onaction& action); @@ -195,7 +198,7 @@ namespace gui { virtual void onFocus(); virtual void doubleClick(int x, int y); virtual void click(int x, int y); - virtual void clicked(Mousecode button) {} + virtual void clicked(Mousecode button); virtual void mouseMove(int x, int y) {}; virtual void mouseRelease(int x, int y); virtual void scrolled(int value); diff --git a/src/graphics/ui/gui_xml.cpp b/src/graphics/ui/gui_xml.cpp index 18d0fa4b..32e59b73 100644 --- a/src/graphics/ui/gui_xml.cpp +++ b/src/graphics/ui/gui_xml.cpp @@ -181,6 +181,10 @@ static void read_uinode( node.listenAction(onclick); } + if (auto onclick = create_action(reader, element, "onrightclick")) { + node.listenRightClick(onclick); + } + if (auto onfocus = create_action(reader, element, "onfocus")) { node.listenFocus(onfocus); } From 1e16ab5464b80d258fd955cf0485ebf596298ab3 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 14 Nov 2025 23:46:21 +0300 Subject: [PATCH 05/25] fix Schedule:set_timeout --- res/modules/schedule.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/modules/schedule.lua b/res/modules/schedule.lua index 457cbf90..3c75ead8 100644 --- a/res/modules/schedule.lua +++ b/res/modules/schedule.lua @@ -3,7 +3,7 @@ local Schedule = { set_interval = function(self, ms, callback, repetions) local id = self._next_interval self._intervals[id] = { - last_called = 0.0, + last_called = self._timer, delay = ms / 1000.0, callback = callback, repetions = repetions, From 65b4ac826263047293d0a3142d7cc9140972b2ab Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 14 Nov 2025 23:48:02 +0300 Subject: [PATCH 06/25] add textbox:indexByPos and :lineY methods --- src/graphics/ui/elements/Container.hpp | 4 +++- src/graphics/ui/elements/TextBox.cpp | 5 +++++ src/graphics/ui/elements/TextBox.hpp | 4 +++- src/graphics/ui/elements/UINode.hpp | 2 +- src/logic/scripting/lua/libs/libgui.cpp | 26 +++++++++++++++++++++++++ 5 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/graphics/ui/elements/Container.hpp b/src/graphics/ui/elements/Container.hpp index dd7dfd34..0506bf38 100644 --- a/src/graphics/ui/elements/Container.hpp +++ b/src/graphics/ui/elements/Container.hpp @@ -37,7 +37,9 @@ namespace gui { virtual void scrolled(int value) override; virtual void setScrollable(bool flag); void listenInterval(float interval, OnTimeOut callback, int repeat=-1); - virtual glm::vec2 getContentOffset() override {return glm::vec2(0.0f, scroll);}; + virtual glm::vec2 getContentOffset() const override { + return glm::vec2(0.0f, scroll); + }; virtual void setSize(const glm::vec2& size) override; virtual int getScrollStep() const; virtual void setScrollStep(int step); diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index 8f24217d..52c33bf1 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -671,6 +671,11 @@ int TextBox::calcIndexAt(int x, int y) const { ); } +int TextBox::getLineYOffset(int line) const { + if (rawTextCache.fontId == 0) return 0; + return label->getLineYOffset(line) + getContentOffset().y; +} + static inline std::wstring get_alphabet(wchar_t c) { std::wstring alphabet {c}; if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_') { diff --git a/src/graphics/ui/elements/TextBox.hpp b/src/graphics/ui/elements/TextBox.hpp index d5b640ac..682e300c 100644 --- a/src/graphics/ui/elements/TextBox.hpp +++ b/src/graphics/ui/elements/TextBox.hpp @@ -74,7 +74,6 @@ namespace gui { size_t normalizeIndex(int index); - int calcIndexAt(int x, int y) const; void setTextOffset(uint x); bool eraseSelected(); void resetSelection(); @@ -186,6 +185,9 @@ namespace gui { /// @return line position in text virtual size_t getLinePos(uint line) const; + int calcIndexAt(int x, int y) const; + int getLineYOffset(int line) const; + /// @brief Check text with validator set with setTextValidator /// @return true if text is valid virtual bool validate(); diff --git a/src/graphics/ui/elements/UINode.hpp b/src/graphics/ui/elements/UINode.hpp index 8aa51f5c..db9b97dc 100644 --- a/src/graphics/ui/elements/UINode.hpp +++ b/src/graphics/ui/elements/UINode.hpp @@ -267,7 +267,7 @@ namespace gui { virtual glm::vec4 calcColor() const; /// @brief Get inner content offset. Used for scroll - virtual glm::vec2 getContentOffset() {return glm::vec2(0.0f);}; + virtual glm::vec2 getContentOffset() const {return glm::vec2(0.0f);}; /// @brief Calculate screen position of the element virtual glm::vec2 calcPos() const; virtual void setPos(const glm::vec2& pos); diff --git a/src/logic/scripting/lua/libs/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp index 6e703104..4c642c6f 100644 --- a/src/logic/scripting/lua/libs/libgui.cpp +++ b/src/logic/scripting/lua/libs/libgui.cpp @@ -178,6 +178,24 @@ static int l_get_line_at(lua::State* L) { return 0; } +static int l_get_index_by_pos(lua::State* L) { + auto node = get_document_node(L, 1); + auto position = lua::tovec2(L, 2); + if (auto box = dynamic_cast(node.node.get())) { + return lua::pushinteger(L, box->calcIndexAt(position.x, position.y)); + } + return 0; +} + +static int l_get_line_y(lua::State* L) { + auto node = get_document_node(L, 1); + auto line = lua::tointeger(L, 2); + if (auto box = dynamic_cast(node.node.get())) { + return lua::pushinteger(L, box->getLineYOffset(line)); + } + return 0; +} + static int l_get_line_pos(lua::State* L) { auto node = get_document_node(L, 1); auto line = lua::tointeger(L, 2); @@ -509,6 +527,12 @@ static int p_get_focused(UINode* node, lua::State* L) { static int p_get_line_at(UINode*, lua::State* L) { return lua::pushcfunction(L, l_get_line_at); } +static int p_get_index_by_pos(UINode*, lua::State* L) { + return lua::pushcfunction(L, l_get_index_by_pos); +} +static int p_get_line_y(UINode*, lua::State* L) { + return lua::pushcfunction(L, l_get_line_y); +} static int p_get_line_pos(UINode*, lua::State* L) { return lua::pushcfunction(L, l_get_line_pos); } @@ -619,6 +643,8 @@ static int l_gui_getattr(lua::State* L) { {"edited", p_get_edited}, {"lineNumbers", p_get_line_numbers}, {"lineAt", p_get_line_at}, + {"indexByPos", p_get_index_by_pos}, + {"lineY", p_get_line_y}, {"linePos", p_get_line_pos}, {"syntax", p_get_syntax}, {"markup", p_get_markup}, From aba18ef836d5423fd075653144909607e4ec55e7 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 15 Nov 2025 14:28:20 +0300 Subject: [PATCH 07/25] add 'keep-line-selection' textbox attribute --- src/graphics/ui/elements/TextBox.cpp | 84 ++++++++++++++++------------ src/graphics/ui/elements/TextBox.hpp | 4 ++ src/graphics/ui/gui_xml.cpp | 5 ++ 3 files changed, 56 insertions(+), 37 deletions(-) diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index 52c33bf1..10127f10 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -231,7 +231,7 @@ TextBox::~TextBox() = default; void TextBox::draw(const DrawContext& pctx, const Assets& assets) { Container::draw(pctx, assets); - if (!isFocused()) { + if (!isFocused() && !keepLineSelection) { return; } const auto& labelText = getText(); @@ -252,7 +252,7 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) { float time = gui.getWindow().time(); - if (editable && static_cast((time - caretLastMove) * 2) % 2 == 0) { + if (isFocused() && editable && static_cast((time - caretLastMove) * 2) % 2 == 0) { uint line = label->getLineByTextIndex(caret); uint lcaret = caret - label->getTextLineOffset(line); int width = rawTextCache.metrics.calcWidth(input, 0, lcaret); @@ -308,42 +308,44 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) { } } - if (isFocused() && multiline) { - auto selectionCtx = subctx.sub(batch); - selectionCtx.setBlendMode(BlendMode::addition); - - batch->setColor(glm::vec4(1, 1, 1, 0.1f)); - - uint line = label->getLineByTextIndex(caret); - while (label->isFakeLine(line)) { - line--; - } - do { - int lineY = label->getLineYOffset(line); - - batch->setColor(glm::vec4(1, 1, 1, 0.05f)); - if (showLineNumbers) { - batch->rect( - lcoord.x - 8, - lcoord.y + lineY, - label->getSize().x, - lineHeight - ); - batch->setColor(glm::vec4(1, 1, 1, 0.10f)); - batch->rect( - lcoord.x - LINE_NUMBERS_PANE_WIDTH, - lcoord.y + lineY, - LINE_NUMBERS_PANE_WIDTH - 8, - lineHeight - ); - } else { - batch->rect( - lcoord.x, lcoord.y + lineY, label->getSize().x, lineHeight - ); - } - line++; - } while (line < label->getLinesNumber() && label->isFakeLine(line)); + if (!multiline) { + return; } + + auto selectionCtx = subctx.sub(batch); + selectionCtx.setBlendMode(BlendMode::addition); + + batch->setColor(glm::vec4(1, 1, 1, 0.1f)); + + uint line = label->getLineByTextIndex(caret); + while (label->isFakeLine(line)) { + line--; + } + do { + int lineY = label->getLineYOffset(line); + + batch->setColor(glm::vec4(1, 1, 1, 0.05f)); + if (showLineNumbers) { + batch->rect( + lcoord.x - 8, + lcoord.y + lineY, + label->getSize().x, + lineHeight + ); + batch->setColor(glm::vec4(1, 1, 1, 0.10f)); + batch->rect( + lcoord.x - LINE_NUMBERS_PANE_WIDTH, + lcoord.y + lineY, + LINE_NUMBERS_PANE_WIDTH - 8, + lineHeight + ); + } else { + batch->rect( + lcoord.x, lcoord.y + lineY, label->getSize().x, lineHeight + ); + } + line++; + } while (line < label->getLinesNumber() && label->isFakeLine(line)); } void TextBox::drawBackground(const DrawContext& pctx, const Assets& assets) { @@ -605,6 +607,14 @@ size_t TextBox::getSelectionEnd() const { return selectionEnd; } +void TextBox::setKeepLineSelection(bool flag) { + keepLineSelection = flag; +} + +bool TextBox::isKeepLineSelection() const { + return keepLineSelection; +} + void TextBox::setOnEditStart(runnable oneditstart) { onEditStart = oneditstart; } diff --git a/src/graphics/ui/elements/TextBox.hpp b/src/graphics/ui/elements/TextBox.hpp index 682e300c..2f9080a5 100644 --- a/src/graphics/ui/elements/TextBox.hpp +++ b/src/graphics/ui/elements/TextBox.hpp @@ -62,6 +62,7 @@ namespace gui { bool editable = true; bool autoresize = false; bool showLineNumbers = false; + bool keepLineSelection = false; std::string markup; std::string syntax; @@ -222,6 +223,9 @@ namespace gui { size_t getSelectionStart() const; size_t getSelectionEnd() const; + void setKeepLineSelection(bool flag); + bool isKeepLineSelection() const; + /// @brief Set runnable called on textbox focus virtual void setOnEditStart(runnable oneditstart); diff --git a/src/graphics/ui/gui_xml.cpp b/src/graphics/ui/gui_xml.cpp index 6dee06eb..3e8bcf5d 100644 --- a/src/graphics/ui/gui_xml.cpp +++ b/src/graphics/ui/gui_xml.cpp @@ -573,6 +573,11 @@ static std::shared_ptr read_text_box( if (element.has("line-numbers")) { textbox->setShowLineNumbers(element.attr("line-numbers").asBool()); } + if (element.has("keep-line-selection")) { + textbox->setKeepLineSelection( + element.attr("keep-line-selection").asBool() + ); + } if (element.has("markup")) { textbox->setMarkup(element.attr("markup").getText()); } From 7e33e08b79710998b62f729c0e6428e68733c1e1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 15 Nov 2025 14:29:18 +0300 Subject: [PATCH 08/25] update doc/*/xml-ui-layouts.md --- doc/en/xml-ui-layouts.md | 1 + doc/ru/xml-ui-layouts.md | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/en/xml-ui-layouts.md b/doc/en/xml-ui-layouts.md index 1127d79f..5a4d7be4 100644 --- a/doc/en/xml-ui-layouts.md +++ b/doc/en/xml-ui-layouts.md @@ -136,6 +136,7 @@ The key code for comparison can be obtained via `input.keycode("key_name")` - `text-wrap` - allows automatic text wrapping (works only with multiline: "true") - `editable` - determines whether the text can be edited. - `line-numbers` - enables line numbers display. +- `keep-line-selection` - keep showing selected line after defocus. - `error-color` - color when entering incorrect data (the text does not pass the validator check). Type: RGBA color. - `text-color` - text color. Type: RGBA color. - `validator` - lua function that checks text for correctness. Takes a string as input, returns true if the text is correct. diff --git a/doc/ru/xml-ui-layouts.md b/doc/ru/xml-ui-layouts.md index a2f068ca..b6efa7b2 100644 --- a/doc/ru/xml-ui-layouts.md +++ b/doc/ru/xml-ui-layouts.md @@ -137,6 +137,7 @@ - `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true") - `editable`- определяет возможность редактирования текста. - `line-numbers` - включает отображение номеров строк. +- `keep-line-selection` - продолжать отображать выбранную строку при потере фокуса. - `error-color` - цвет при вводе некорректных данных (текст не проходит проверку валидатора). Тип: RGBA цвет. - `text-color` - цвет текста. Тип: RGBA цвет. - `validator` - lua функция, проверяющая текст на корректность. Принимает на вход строку, возвращает true если текст корректен. From 5edd7b2bb52be7ffb1965469f678f8a7da214ec8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 15 Nov 2025 16:52:58 +0300 Subject: [PATCH 09/25] make network.find_free_port return nil if not found --- src/logic/scripting/lua/libs/libnetwork.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp index 580176cd..b1b3a41f 100644 --- a/src/logic/scripting/lua/libs/libnetwork.cpp +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -410,7 +410,11 @@ static int l_get_total_download(lua::State* L, network::Network& network) { } static int l_find_free_port(lua::State* L, network::Network& network) { - return lua::pushinteger(L, network.findFreePort()); + int port = network.findFreePort(); + if (port == -1) { + return 0; + } + return lua::pushinteger(L, port); } static int l_set_nodelay(lua::State* L, network::Network& network) { From 9c429f780950829cee50b1ba218206d8bb6b35fa Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 15 Nov 2025 16:53:45 +0300 Subject: [PATCH 10/25] update doc/*/scripting/builtins/libnetwork.md --- doc/en/scripting/builtins/libnetwork.md | 2 +- doc/ru/scripting/builtins/libnetwork.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/scripting/builtins/libnetwork.md b/doc/en/scripting/builtins/libnetwork.md index 515488f7..cef8facb 100644 --- a/doc/en/scripting/builtins/libnetwork.md +++ b/doc/en/scripting/builtins/libnetwork.md @@ -137,5 +137,5 @@ network.get_total_download() --> int ```lua -- Looks for a free port to use. -network.find_free_port() --> int +network.find_free_port() --> int or nil ``` diff --git a/doc/ru/scripting/builtins/libnetwork.md b/doc/ru/scripting/builtins/libnetwork.md index 269b1615..5c107923 100644 --- a/doc/ru/scripting/builtins/libnetwork.md +++ b/doc/ru/scripting/builtins/libnetwork.md @@ -202,5 +202,5 @@ network.get_total_download() --> int ```lua -- Ищет свободный для использования порт. -network.find_free_port() --> int +network.find_free_port() --> int или nil ``` From 31ea1983017fc8fd395f4675ce3d9a16702fe43e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 15 Nov 2025 16:57:24 +0300 Subject: [PATCH 11/25] add app.start_debug_instance --- src/engine/EnginePaths.cpp | 8 +++++++ src/engine/EnginePaths.hpp | 3 +++ src/logic/scripting/lua/libs/libapp.cpp | 30 ++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/engine/EnginePaths.cpp b/src/engine/EnginePaths.cpp index 8a786140..2d84317d 100644 --- a/src/engine/EnginePaths.cpp +++ b/src/engine/EnginePaths.cpp @@ -71,6 +71,14 @@ EnginePaths::EnginePaths(CoreParameters& params) io::create_subdevice("config", "user", "config"); } +std::filesystem::path EnginePaths::getResourcesFolder() const { + return resourcesFolder; +} + +std::filesystem::path EnginePaths::getUserFilesFolder() const { + return userFilesFolder; +} + io::path EnginePaths::getNewScreenshotFile(const std::string& ext) const { auto folder = SCREENSHOTS_FOLDER; if (!io::is_directory(folder)) { diff --git a/src/engine/EnginePaths.hpp b/src/engine/EnginePaths.hpp index 41d02236..4296a40b 100644 --- a/src/engine/EnginePaths.hpp +++ b/src/engine/EnginePaths.hpp @@ -48,6 +48,9 @@ public: EnginePaths(CoreParameters& params); + std::filesystem::path getResourcesFolder() const; + std::filesystem::path getUserFilesFolder() const; + io::path getWorldFolderByName(const std::string& name); io::path getWorldsFolder() const; diff --git a/src/logic/scripting/lua/libs/libapp.cpp b/src/logic/scripting/lua/libs/libapp.cpp index 4bd38f4b..7c8fab71 100644 --- a/src/logic/scripting/lua/libs/libapp.cpp +++ b/src/logic/scripting/lua/libs/libapp.cpp @@ -1,6 +1,34 @@ #include "api_lua.hpp" +#include "logic/scripting/scripting.hpp" +#include "engine/Engine.hpp" +#include "engine/EnginePaths.hpp" +#include "network/Network.hpp" +#include "util/platform.hpp" + +using namespace scripting; + +static int l_start_debug_instance(lua::State* L) { + int port = lua::tointeger(L, 1); + if (port == 0) { + port = engine->getNetwork().findFreePort(); + if (port == -1) { + throw std::runtime_error("could not find free port"); + } + } + const auto& paths = engine->getPaths(); + + std::vector args { + "--res", paths.getResourcesFolder().u8string(), + "--dir", paths.getUserFilesFolder().u8string(), + "--dbg-server", "tcp:" + std::to_string(port), + }; + platform::new_engine_instance(std::move(args)); + return lua::pushinteger(L, port); +} + const luaL_Reg applib[] = { - // see libcore.cpp an stdlib.lua + {"start_debug_instance", lua::wrap}, + // for other functions see libcore.cpp and stdlib.lua {nullptr, nullptr} }; From 637896fb0fe3e967d39c0280dbd9cc2c4818de78 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 15 Nov 2025 18:45:30 +0300 Subject: [PATCH 12/25] fix: textbox methods line numbers startfrom 0 --- src/logic/scripting/lua/libs/libgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/logic/scripting/lua/libs/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp index 4c642c6f..01bfd0d6 100644 --- a/src/logic/scripting/lua/libs/libgui.cpp +++ b/src/logic/scripting/lua/libs/libgui.cpp @@ -173,7 +173,7 @@ static int l_get_line_at(lua::State* L) { auto node = get_document_node(L, 1); auto position = lua::tointeger(L, 2); if (auto box = dynamic_cast(node.node.get())) { - return lua::pushinteger(L, box->getLineAt(position)); + return lua::pushinteger(L, box->getLineAt(position) + 1); } return 0; } @@ -191,7 +191,7 @@ static int l_get_line_y(lua::State* L) { auto node = get_document_node(L, 1); auto line = lua::tointeger(L, 2); if (auto box = dynamic_cast(node.node.get())) { - return lua::pushinteger(L, box->getLineYOffset(line)); + return lua::pushinteger(L, box->getLineYOffset(line - 1)); } return 0; } From 062188ede707483afb16bb206279e7db5865d5e5 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 15 Nov 2025 20:16:31 +0300 Subject: [PATCH 13/25] add socket:recv_async --- res/scripts/classes.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/res/scripts/classes.lua b/res/scripts/classes.lua index 950544bf..699c0fd2 100644 --- a/res/scripts/classes.lua +++ b/res/scripts/classes.lua @@ -39,6 +39,16 @@ end local Socket = {__index={ send=function(self, ...) return network.__send(self.id, ...) end, recv=function(self, ...) return network.__recv(self.id, ...) end, + recv_async=function(self, length, usetable) + while self:is_alive() do + local available = self:available() + if available >= length then + return self:recv(length, usetable) + end + coroutine.yield() + end + return self:recv(length, usetable) + end, close=function(self) return network.__close(self.id) end, available=function(self) return network.__available(self.id) or 0 end, is_alive=function(self) return network.__is_alive(self.id) end, From 6195d014cd76193224a7efc4e3e58e4ba4af815a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 15 Nov 2025 20:32:08 +0300 Subject: [PATCH 14/25] fix textbox:lineY --- src/graphics/ui/elements/TextBox.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index 10127f10..b7ac536c 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -683,7 +683,7 @@ int TextBox::calcIndexAt(int x, int y) const { int TextBox::getLineYOffset(int line) const { if (rawTextCache.fontId == 0) return 0; - return label->getLineYOffset(line) + getContentOffset().y; + return label->getLineYOffset(line); } static inline std::wstring get_alphabet(wchar_t c) { From c42d66122396a28ec5a174267a9819e7a528e67c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 15 Nov 2025 21:22:18 +0300 Subject: [PATCH 15/25] add app.focus function --- src/logic/scripting/lua/libs/libapp.cpp | 7 +++++++ src/window/Window.hpp | 2 ++ src/window/detail/GLFWWindow.cpp | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/src/logic/scripting/lua/libs/libapp.cpp b/src/logic/scripting/lua/libs/libapp.cpp index 7c8fab71..88091bc2 100644 --- a/src/logic/scripting/lua/libs/libapp.cpp +++ b/src/logic/scripting/lua/libs/libapp.cpp @@ -5,6 +5,7 @@ #include "engine/EnginePaths.hpp" #include "network/Network.hpp" #include "util/platform.hpp" +#include "window/Window.hpp" using namespace scripting; @@ -27,8 +28,14 @@ static int l_start_debug_instance(lua::State* L) { return lua::pushinteger(L, port); } +static int l_focus(lua::State* L) { + engine->getWindow().focus(); + return 0; +} + const luaL_Reg applib[] = { {"start_debug_instance", lua::wrap}, + {"focus", lua::wrap}, // for other functions see libcore.cpp and stdlib.lua {nullptr, nullptr} }; diff --git a/src/window/Window.hpp b/src/window/Window.hpp index a5a3240b..f5a50a73 100644 --- a/src/window/Window.hpp +++ b/src/window/Window.hpp @@ -36,6 +36,8 @@ public: virtual void setMode(WindowMode mode) = 0; virtual WindowMode getMode() const = 0; + virtual void focus() = 0; + virtual void setTitle(const std::string& title) = 0; virtual void setIcon(const ImageData* image) = 0; diff --git a/src/window/detail/GLFWWindow.cpp b/src/window/detail/GLFWWindow.cpp index 88df3940..07469306 100644 --- a/src/window/detail/GLFWWindow.cpp +++ b/src/window/detail/GLFWWindow.cpp @@ -468,6 +468,10 @@ public: return mode; } + void focus() override { + glfwFocusWindow(window); + } + void setTitle(const std::string& title) override { glfwSetWindowTitle(window, title.c_str()); } From 9e60a818e03f6954664c1d85e0a2fe0fd5a4ed8e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 15 Nov 2025 21:27:11 +0300 Subject: [PATCH 16/25] update doc/*/scripting/builtins/libapp.md --- doc/en/scripting/builtins/libapp.md | 6 ++++++ doc/ru/scripting/builtins/libapp.md | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/doc/en/scripting/builtins/libapp.md b/doc/en/scripting/builtins/libapp.md index 45414ba1..efb4ad9b 100644 --- a/doc/en/scripting/builtins/libapp.md +++ b/doc/en/scripting/builtins/libapp.md @@ -158,3 +158,9 @@ app.get_setting_info(name: str) -> { ``` Returns a table with information about a setting. Throws an exception if the setting does not exist. + +```lua +app.focus() +``` + +Brings the window to front and sets input focus. diff --git a/doc/ru/scripting/builtins/libapp.md b/doc/ru/scripting/builtins/libapp.md index d2ddec72..9b08fa88 100644 --- a/doc/ru/scripting/builtins/libapp.md +++ b/doc/ru/scripting/builtins/libapp.md @@ -159,3 +159,9 @@ app.get_setting_info(name: str) -> { ``` Возвращает таблицу с информацией о настройке. Бросает исключение, если настройки не существует. + +```lua +app.focus() +``` + +Переводит окно на передний план и устанавливает фокус ввода. From ff079ef57bc6f0efea488f3986e780fcbed44a23 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 15 Nov 2025 21:31:47 +0300 Subject: [PATCH 17/25] update doc/*/scripting/builtins/libnetwork.md --- doc/en/scripting/builtins/libnetwork.md | 10 ++++++++++ doc/ru/scripting/builtins/libnetwork.md | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/doc/en/scripting/builtins/libnetwork.md b/doc/en/scripting/builtins/libnetwork.md index cef8facb..3ceac9c8 100644 --- a/doc/en/scripting/builtins/libnetwork.md +++ b/doc/en/scripting/builtins/libnetwork.md @@ -82,6 +82,16 @@ socket:recv( -- Returns nil on error (socket is closed or does not exist). -- If there is no data yet, returns an empty byte array. +-- Asynchronous version for use in coroutines. +-- Waits for the entire specified number of bytes to be received. +-- If socket closes, function works like socket:recv +socket:recv_async( + -- Size of the byte array to read + length: int, + -- Use table instead of Bytearray + [optional] usetable: bool=false +) -> nil|table|Bytearray + -- Closes the connection socket:close() diff --git a/doc/ru/scripting/builtins/libnetwork.md b/doc/ru/scripting/builtins/libnetwork.md index 5c107923..8c574f36 100644 --- a/doc/ru/scripting/builtins/libnetwork.md +++ b/doc/ru/scripting/builtins/libnetwork.md @@ -82,6 +82,16 @@ socket:recv( -- В случае ошибки возвращает nil (сокет закрыт или несуществует). -- Если данных пока нет, возвращает пустой массив байт. +-- Асинхронный вариант для использования в корутинах. +-- Ожидает получение всего указанного числа байт. +-- При закрытии сокета работает как socket:recv +socket:recv_async( + -- Размер читаемого массива байт + length: int, + -- Использовать таблицу вместо Bytearray + [опционально] usetable: bool=false +) -> nil|table|Bytearray + -- Закрывает соединение socket:close() From 289c962df1cf9066299db65e1148b201afcc459f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 15 Nov 2025 23:52:17 +0300 Subject: [PATCH 18/25] update res/devtools/syntax/lua.toml --- 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 cbccce75..d2469d2f 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" + "until", "while", "self", "error" ] From 218c716892576ee045a9c2f55561174c7dd9d6ce Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 16 Nov 2025 01:19:27 +0300 Subject: [PATCH 19/25] fixes --- src/devtools/DebuggingServer.cpp | 1 + src/logic/scripting/lua/lua_extensions.cpp | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/devtools/DebuggingServer.cpp b/src/devtools/DebuggingServer.cpp index 09a2b368..b1aba638 100644 --- a/src/devtools/DebuggingServer.cpp +++ b/src/devtools/DebuggingServer.cpp @@ -197,6 +197,7 @@ bool DebuggingServer::performCommand( connectionEstablished = true; logger.info() << "client connection established"; connection->sendResponse("success"); + return true; } if (!connectionEstablished) { return false; diff --git a/src/logic/scripting/lua/lua_extensions.cpp b/src/logic/scripting/lua/lua_extensions.cpp index 27b47fae..a7de3fdd 100644 --- a/src/logic/scripting/lua/lua_extensions.cpp +++ b/src/logic/scripting/lua/lua_extensions.cpp @@ -309,14 +309,15 @@ static int l_debug_sendvalue(lua::State* L) { lua::pushnil(L); while (lua::next(L, 1)) { - auto key = lua::tolstring(L, -2); + lua::pushvalue(L, -2); - int type = lua::type(L, -1); + auto key = lua::tolstring(L, -1); + int type = lua::type(L, -2); table[std::string(key)] = dv::object({ {"type", std::string(lua::type_name(L, type))}, - {"short", get_short_value(L, -1, type)}, + {"short", get_short_value(L, -2, type)}, }); - lua::pop(L); + lua::pop(L, 2); } lua::pop(L); value = std::move(table); From 97c8bac9ff4495ece7b8278e4432d90b32a52ca1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 16 Nov 2025 14:01:22 +0300 Subject: [PATCH 20/25] fix paused mode value requests handling --- res/modules/internal/debugging.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/res/modules/internal/debugging.lua b/res/modules/internal/debugging.lua index cef40202..1b56ee4b 100644 --- a/res/modules/internal/debugging.lua +++ b/res/modules/internal/debugging.lua @@ -60,7 +60,9 @@ if is_debugging then current_func = _debug_getinfo(2).func current_func_stack_size = calc_stack_size() __pause("breakpoint") - debug.pull_events() + while debug.pull_events() do + __pause() + end end, "lr") end @@ -92,6 +94,7 @@ function debug.pull_events() if not events then return end + local keepPaused = false for i, event in ipairs(events) do if event[1] == DBG_EVENT_SET_BREAKPOINT then debug.set_breakpoint(event[2], event[3]) @@ -116,9 +119,10 @@ function debug.pull_events() value = value[key] end __sendvalue(value, event[2], event[3], event[4]) - __pause() + keepPaused = true end end + return keepPaused end function debug.set_breakpoint(source, line) From bcfdb6196a0b4dfea1a8182b2efe690ca5dd353b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 16 Nov 2025 14:25:16 +0300 Subject: [PATCH 21/25] add 'istoplevel' argument to input.add_callback --- src/engine/Engine.cpp | 1 + src/logic/scripting/lua/libs/libinput.cpp | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 6598562f..67ff6797 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -70,6 +70,7 @@ void Engine::onContentLoad() { for (auto& pack : content->getAllContentPacks()) { auto configFolder = pack.folder / "config"; auto bindsFile = configFolder / "bindings.toml"; + logger.info() << "loading bindings: " << bindsFile.string(); if (io::is_regular_file(bindsFile)) { input->getBindings().read( toml::parse( diff --git a/src/logic/scripting/lua/libs/libinput.cpp b/src/logic/scripting/lua/libs/libinput.cpp index eb605671..28f87f3c 100644 --- a/src/logic/scripting/lua/libs/libinput.cpp +++ b/src/logic/scripting/lua/libs/libinput.cpp @@ -50,8 +50,11 @@ static int l_add_callback(lua::State* L) { handler = input.addKeyCallback(key, actual_callback); } } - auto callback = [&gui, actual_callback]() -> bool { - if (!gui.isFocusCaught()) { + + bool isTopLevel = lua::toboolean(L, 4); + + auto callback = [&gui, actual_callback, isTopLevel]() -> bool { + if (isTopLevel || !gui.isFocusCaught()) { return actual_callback(); } return false; From 64c73f4d0dfbd3ceb3c3d28a742a3af729149057 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 16 Nov 2025 14:29:03 +0300 Subject: [PATCH 22/25] add doc/*/scripting/builtins/libinput.md --- doc/en/scripting/builtins/libinput.md | 11 ++++++++++- doc/ru/scripting/builtins/libinput.md | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/doc/en/scripting/builtins/libinput.md b/doc/en/scripting/builtins/libinput.md index 2c577389..9856abc0 100644 --- a/doc/en/scripting/builtins/libinput.md +++ b/doc/en/scripting/builtins/libinput.md @@ -13,7 +13,16 @@ input.mousecode(mousename: str) --> int Returns mouse button code or -1 if unknown ```lua -input.add_callback(bindname: str, callback: function) +input.add_callback( + -- Binding name + bindname: str, + -- Handler + callback: function + -- UI element that owns the handler (responsible for the handler's lifetime) + [optional] owner: Element, + -- Ignore input capture by UI elements + [optional] istoplevel: bool +) ``` Add binding activation callback. Example: diff --git a/doc/ru/scripting/builtins/libinput.md b/doc/ru/scripting/builtins/libinput.md index 96bc3105..6aadbdc1 100644 --- a/doc/ru/scripting/builtins/libinput.md +++ b/doc/ru/scripting/builtins/libinput.md @@ -13,7 +13,16 @@ input.mousecode(mousename: str) --> int Возвращает код кнопки мыши по имени, либо -1 ```lua -input.add_callback(bindname: str, callback: function) +input.add_callback( + -- Имя привязки + bindname: str, + -- Обработчик + callback: function + -- UI элемент-владелец обработчика (отвечает за срок жизни) + [опционально] owner: Element, + -- Игнорировать захват ввода UI элементами + [опционально] istoplevel: bool +) ``` Назначает функцию, которая будет вызываться при активации привязки. Пример: From 1a68371d858a7b62983848218b7101a748978b80 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 20 Nov 2025 18:10:46 +0300 Subject: [PATCH 23/25] fix -Wunused-lambda-capture --- src/graphics/ui/elements/Container.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphics/ui/elements/Container.cpp b/src/graphics/ui/elements/Container.cpp index 9a9b4608..5fb09f7e 100644 --- a/src/graphics/ui/elements/Container.cpp +++ b/src/graphics/ui/elements/Container.cpp @@ -96,7 +96,7 @@ void Container::act(float delta) { GUI& gui = this->gui; intervalEvents.erase(std::remove_if( intervalEvents.begin(), intervalEvents.end(), - [&gui](const IntervalEvent& event) { + [](const IntervalEvent& event) { return event.repeat == 0; } ), intervalEvents.end()); From 814f716ee5030ec70ee900f922c2a729560f1a72 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 20 Nov 2025 18:11:28 +0300 Subject: [PATCH 24/25] fix --- src/graphics/ui/elements/Container.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/graphics/ui/elements/Container.cpp b/src/graphics/ui/elements/Container.cpp index 5fb09f7e..aa9b5977 100644 --- a/src/graphics/ui/elements/Container.cpp +++ b/src/graphics/ui/elements/Container.cpp @@ -93,7 +93,6 @@ void Container::act(float delta) { } } } - GUI& gui = this->gui; intervalEvents.erase(std::remove_if( intervalEvents.begin(), intervalEvents.end(), [](const IntervalEvent& event) { From f1bb0e48fd2b483328848fb487ba107c8a34f9e9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 20 Nov 2025 21:43:44 +0300 Subject: [PATCH 25/25] add debugging protocol description draft --- doc/specs/debugging_protocol.md | 153 ++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 doc/specs/debugging_protocol.md diff --git a/doc/specs/debugging_protocol.md b/doc/specs/debugging_protocol.md new file mode 100644 index 00000000..ba4cf96a --- /dev/null +++ b/doc/specs/debugging_protocol.md @@ -0,0 +1,153 @@ +# VC-DBG protocol v1 + +## Notes + +- '?' in name means that the attribute is optional. + +## Connecting + +### Step 1 + +Connection initiating with binary header exchange: + +``` +'v' 'c' '-' 'd' 'b' 'g' NUL XX +76 63 2D 64 62 67 00 XX +``` + +XX - protocol version number + +Client sends header to the server. Then server responds. +Server closes connection after sending header if it mismatches. + +### Step 2 + +Client sends 'connect' command. + +## Messages + +Message is: +- 32 bit little-endian unsigned integer - number of encoded message bytes. +- message itself (UTF-8 encoded json). + +## Client-to-server + +### Establishing connection + +```json +{ + "type": "connect", + "?disconnect-action": "resume|detach|terminate" +} +``` + +Configuring connection. Disconnect-action is action that debugged instance must perform on debugging client connection closed/refused. + +- `resume` - Resume from pause mode and start listening for client. +- `detach` - Resume from pause mode and stop server. +- `terminate` - Stop the debugged application. + + +### Specific action signals. + +```json +{ + "type": "pause|resume|terminate|detach" +} +``` + +### Breakpoints management + +```json +{ + "type": "set-breakpoint|remove-breakpoint", + "source": "entry_point:path", + "line": 1 +} +``` + +### Local value details request + +```json +{ + "type": "get-value", + "frame": 0, + "local": 1, + "path": ["path", "to", 1, "value"] +} +``` + +- `frame` - Call stack frame index (indexing from most recent call) +- `local` - Local variable index (based on `paused` event stack trace) +- `path` - Requsted value path segments. Example: `['a', 'b', 5]` is `local_variable.a.b[5]` + +Responds with: + +```json +{ + "type": "value", + "frame": 0, + "local": 1, + "path": ["path", "to", 1, "value"], + "value": "value itself" +} +``` + +Example: actual value is table: +```lua +{a=5, b="test", 2={}} +``` + +Then `value` is: +```json +{ + "a": { + "type": "number", + "short": "5" + }, + "b": { + "type": "string", + "short": "test" + }, + "1": { + "type": "table", + "short": "{...}" + } +} +``` + +## Server-to-client + +### Response signals + +```json +{ + "type": "success|resumed" +} +``` + +### Pause event + +```json +{ + "type": "paused", + "?reason": "breakpoint|exception|step", + "?message": "...", + "?stack": [ + { + "?function": "function name", + "source": "source name", + "what": "what", + "line": 1, + "locals": [ + "name": { + "type": "local type", + "index": 1, + "short": "short value", + "size": 0 + } + ] + } + ] +} +```