From 7749da4a85549d690913d935645acf4ecaffcb75 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 5 Aug 2025 23:28:52 +0300 Subject: [PATCH 01/28] feat: project scripts --- src/engine/Engine.hpp | 4 ++++ src/engine/Mainloop.cpp | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp index 8f8683da..a3427c14 100644 --- a/src/engine/Engine.hpp +++ b/src/engine/Engine.hpp @@ -174,4 +174,8 @@ public: devtools::Editor& getEditor() { return *editor; } + + const Project& getProject() { + return *project; + } }; diff --git a/src/engine/Mainloop.cpp b/src/engine/Mainloop.cpp index ecfd17bc..bdde4f53 100644 --- a/src/engine/Mainloop.cpp +++ b/src/engine/Mainloop.cpp @@ -2,8 +2,11 @@ #include "Engine.hpp" #include "debug/Logger.hpp" +#include "devtools/Project.hpp" #include "frontend/screens/MenuScreen.hpp" #include "frontend/screens/LevelScreen.hpp" +#include "interfaces/Process.hpp" +#include "logic/scripting/scripting.hpp" #include "window/Window.hpp" #include "world/Level.hpp" @@ -28,12 +31,24 @@ void Mainloop::run() { )); } }); + + io::path projectScript = "project:project_script.lua"; + std::unique_ptr process; + if (io::exists(projectScript)) { + logger.info() << "starting project script"; + process = scripting::start_coroutine(projectScript); + } else { + logger.warning() << "project script does not exists"; + } logger.info() << "starting menu screen"; engine.setScreen(std::make_shared(engine)); logger.info() << "main loop started"; while (!window.isShouldClose()){ + if (process) { + process->update(); + } time.update(window.time()); engine.updateFrontend(); if (!window.isIconified()) { From 52dac7a94f0a0199284f20ca8e7cce41fc97ac33 Mon Sep 17 00:00:00 2001 From: VOXEL <111713723+VOXEL0798@users.noreply.github.com> Date: Wed, 6 Aug 2025 04:22:04 +0300 Subject: [PATCH 02/28] Update flake.nix --- flake.nix | 69 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/flake.nix b/flake.nix index 2c392d27..bc9ead2b 100644 --- a/flake.nix +++ b/flake.nix @@ -1,16 +1,69 @@ { + description = "VoxelCore – voxel game engine in C++"; + inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; flake-utils.url = "github:numtide/flake-utils"; }; - outputs = { self, nixpkgs, flake-utils }: - flake-utils.lib.eachDefaultSystem (system: { - devShells.default = with nixpkgs.legacyPackages.${system}; mkShell { - nativeBuildInputs = [ cmake pkg-config ]; - buildInputs = [ glm glfw glew zlib libpng libvorbis openal luajit curl ]; # libglvnd - packages = [ glfw mesa freeglut entt ]; - LD_LIBRARY_PATH = "${wayland}/lib:$LD_LIBRARY_PATH"; + outputs = + { + self, + nixpkgs, + flake-utils, + }: + flake-utils.lib.eachDefaultSystem ( + system: + let + pkgs = import nixpkgs { inherit system; }; + voxel-core = pkgs.stdenv.mkDerivation { + name = "voxel-core"; + + src = ./.; + + nativeBuildInputs = with pkgs; [ + cmake + pkg-config + ]; + + buildInputs = with pkgs; [ + glm + glfw + glew + zlib + libpng + libvorbis + openal + luajit + curl + entt + mesa + freeglut + ]; # libglvnd + + packages = with pkgs; [ + glfw + mesa + freeglut + entt + ]; + cmakeFlags = [ + "-DCMAKE_PREFIX_PATH=${pkgs.entt}" + "-DCMAKE_INCLUDE_PATH=${pkgs.entt}/include" + ]; + + installPhase = '' + mkdir -p $out/bin + cp VoxelEngine $out/bin/ + ''; }; - }); + in + { + packages.default = voxel-core; + apps.default = { + type = "app"; + program = "${voxel-core}/bin/VoxelCore"; + }; + } + ); } From 6a68d7cf1b0151aab60908476080fae6b756f349 Mon Sep 17 00:00:00 2001 From: VOXEL <111713723+VOXEL0798@users.noreply.github.com> Date: Wed, 6 Aug 2025 04:22:25 +0300 Subject: [PATCH 03/28] Update flake.lock From 0a02d3fbec6de2f27a265ef14d2753cbc196e0d5 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 7 Aug 2025 22:14:52 +0300 Subject: [PATCH 04/28] add on_screen_change project script event & fix UIDocument::rebuildIndices & move menu background to project script --- res/project_script.lua | 26 ++++++++++++++++++ src/engine/Engine.cpp | 22 +++++++++++++++ src/engine/Engine.hpp | 9 +++++++ src/engine/Mainloop.cpp | 17 +++--------- src/frontend/UiDocument.cpp | 4 ++- src/frontend/screens/LevelScreen.cpp | 5 +++- src/frontend/screens/LevelScreen.hpp | 5 ++++ src/frontend/screens/MenuScreen.cpp | 36 +++++-------------------- src/frontend/screens/MenuScreen.hpp | 6 +++++ src/frontend/screens/Screen.hpp | 2 ++ src/graphics/ui/GUI.cpp | 18 ++++++------- src/graphics/ui/GUI.hpp | 3 +++ src/logic/scripting/lua/libs/libgui.cpp | 7 +++-- src/logic/scripting/lua/lua_engine.cpp | 6 +---- src/logic/scripting/scripting.cpp | 35 ++++++++++++++++++++++-- src/logic/scripting/scripting.hpp | 13 ++++++--- 16 files changed, 148 insertions(+), 66 deletions(-) create mode 100644 res/project_script.lua diff --git a/res/project_script.lua b/res/project_script.lua new file mode 100644 index 00000000..5f28c300 --- /dev/null +++ b/res/project_script.lua @@ -0,0 +1,26 @@ +local menubg + +function on_screen_changed(screen) + if screen ~= "menu" then + if menubg then + menubg:destruct() + menubg = nil + end + return + end + local controller = {} + function controller.resize_menu_bg() + local w, h = unpack(gui.get_viewport()) + if menubg then + menubg.region = {0, math.floor(h / 48), math.floor(w / 48), 0} + menubg.pos = {0, 0} + end + return w, h + end + _GUI_ROOT.root:add( + "", controller) + menubg = _GUI_ROOT.menubg + controller.resize_menu_bg() + menu.page = "main" +end diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 6596733f..5e5ba681 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -60,6 +60,17 @@ static std::unique_ptr load_icon() { return nullptr; } +static std::unique_ptr load_project_script() { + io::path scriptFile = "project:project_script.lua"; + if (io::exists(scriptFile)) { + logger.info() << "starting project script"; + return scripting::load_project_script(scriptFile); + } else { + logger.warning() << "project script does not exists"; + } + return nullptr; +} + Engine::Engine() = default; Engine::~Engine() = default; @@ -166,12 +177,15 @@ void Engine::initialize(CoreParameters coreParameters) { } }); scripting::initialize(this); + if (!isHeadless()) { gui->setPageLoader(scripting::create_page_loader()); } keepAlive(settings.ui.language.observe([this](auto lang) { langs::setup(lang, paths.resPaths.collectRoots()); }, true)); + + projectScript = load_project_script(); } void Engine::loadSettings() { @@ -228,6 +242,7 @@ void Engine::run() { } } +#include "graphics/ui/elements/Container.hpp" void Engine::postUpdate() { network->update(); postRunnables.run(); @@ -270,6 +285,7 @@ void Engine::saveSettings() { } void Engine::close() { + projectScript.reset(); saveSettings(); logger.info() << "shutting down"; if (screen) { @@ -349,6 +365,12 @@ void Engine::setScreen(std::shared_ptr screen) { audio::reset_channel(audio::get_channel_index("regular")); audio::reset_channel(audio::get_channel_index("ambient")); this->screen = std::move(screen); + if (this->screen) { + this->screen->onOpen(); + } + if (projectScript && this->screen) { + projectScript->onScreenChange(this->screen->getName()); + } } void Engine::onWorldOpen(std::unique_ptr level, int64_t localPlayer) { diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp index a3427c14..d82fc4e6 100644 --- a/src/engine/Engine.hpp +++ b/src/engine/Engine.hpp @@ -38,6 +38,10 @@ namespace devtools { class Editor; } +namespace scripting { + class IProjectScript; +} + class initialize_error : public std::runtime_error { public: initialize_error(const std::string& message) : std::runtime_error(message) {} @@ -71,6 +75,7 @@ class Engine : public util::ObjectsKeeper { std::unique_ptr input; std::unique_ptr gui; std::unique_ptr editor; + std::unique_ptr projectScript; PostRunnables postRunnables; Time time; OnWorldOpen levelConsumer; @@ -178,4 +183,8 @@ public: const Project& getProject() { return *project; } + + scripting::IProjectScript* getProjectScript() { + return projectScript.get(); + } }; diff --git a/src/engine/Mainloop.cpp b/src/engine/Mainloop.cpp index bdde4f53..a14c5464 100644 --- a/src/engine/Mainloop.cpp +++ b/src/engine/Mainloop.cpp @@ -5,10 +5,10 @@ #include "devtools/Project.hpp" #include "frontend/screens/MenuScreen.hpp" #include "frontend/screens/LevelScreen.hpp" -#include "interfaces/Process.hpp" -#include "logic/scripting/scripting.hpp" #include "window/Window.hpp" #include "world/Level.hpp" +#include "graphics/ui/GUI.hpp" +#include "graphics/ui/elements/Container.hpp" static debug::Logger logger("mainloop"); @@ -31,26 +31,15 @@ void Mainloop::run() { )); } }); - - io::path projectScript = "project:project_script.lua"; - std::unique_ptr process; - if (io::exists(projectScript)) { - logger.info() << "starting project script"; - process = scripting::start_coroutine(projectScript); - } else { - logger.warning() << "project script does not exists"; - } logger.info() << "starting menu screen"; engine.setScreen(std::make_shared(engine)); logger.info() << "main loop started"; while (!window.isShouldClose()){ - if (process) { - process->update(); - } time.update(window.time()); engine.updateFrontend(); + if (!window.isIconified()) { engine.renderFrame(); } diff --git a/src/frontend/UiDocument.cpp b/src/frontend/UiDocument.cpp index 7c6f8d13..3e9a3a27 100644 --- a/src/frontend/UiDocument.cpp +++ b/src/frontend/UiDocument.cpp @@ -14,11 +14,13 @@ UiDocument::UiDocument( const std::shared_ptr& root, scriptenv env ) : id(std::move(id)), script(script), root(root), env(std::move(env)) { - gui::UINode::getIndices(root, map); + rebuildIndices(); } void UiDocument::rebuildIndices() { + map.clear(); gui::UINode::getIndices(root, map); + map["root"] = root; } const UINodesMap& UiDocument::getMap() const { diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index ca80ed11..aa007293 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -97,7 +97,6 @@ LevelScreen::LevelScreen( animator->addAnimations(assets.getAnimations()); loadDecorations(); - initializeContent(); } LevelScreen::~LevelScreen() { @@ -112,6 +111,10 @@ LevelScreen::~LevelScreen() { engine.getPaths().setCurrentWorldFolder(""); } +void LevelScreen::onOpen() { + initializeContent(); +} + void LevelScreen::initializeContent() { auto& content = controller->getLevel()->content; for (auto& entry : content.getPacks()) { diff --git a/src/frontend/screens/LevelScreen.hpp b/src/frontend/screens/LevelScreen.hpp index b4f6c93a..616258e5 100644 --- a/src/frontend/screens/LevelScreen.hpp +++ b/src/frontend/screens/LevelScreen.hpp @@ -53,8 +53,13 @@ public: ); ~LevelScreen(); + void onOpen() override; void update(float delta) override; void draw(float delta) override; void onEngineShutdown() override; + + const char* getName() const override { + return "level"; + } }; diff --git a/src/frontend/screens/MenuScreen.cpp b/src/frontend/screens/MenuScreen.cpp index be114ed3..c1339494 100644 --- a/src/frontend/screens/MenuScreen.cpp +++ b/src/frontend/screens/MenuScreen.cpp @@ -13,12 +13,6 @@ #include "engine/Engine.hpp" MenuScreen::MenuScreen(Engine& engine) : Screen(engine) { - engine.getContentControl().resetContent(); - - auto menu = engine.getGUI().getMenu(); - menu->reset(); - menu->setPage("main"); - uicamera = std::make_unique(glm::vec3(), engine.getWindow().getSize().y); uicamera->perspective = false; @@ -29,33 +23,17 @@ MenuScreen::MenuScreen(Engine& engine) : Screen(engine) { MenuScreen::~MenuScreen() = default; +void MenuScreen::onOpen() { + engine.getContentControl().resetContent(); + + auto menu = engine.getGUI().getMenu(); + menu->reset(); +} + void MenuScreen::update(float delta) { } void MenuScreen::draw(float delta) { - auto assets = engine.getAssets(); - display::clear(); display::setBgColor(glm::vec3(0.2f)); - - const auto& size = engine.getWindow().getSize(); - uint width = size.x; - uint height = size.y; - - uicamera->setFov(height); - uicamera->setAspectRatio(width / static_cast(height)); - auto uishader = assets->get("ui"); - uishader->use(); - uishader->uniformMatrix("u_projview", uicamera->getProjView()); - - auto bg = assets->get("gui/menubg"); - batch->begin(); - batch->texture(bg); - batch->rect( - 0, 0, - width, height, 0, 0, 0, - UVRegion(0, 0, width / bg->getWidth(), height / bg->getHeight()), - false, false, glm::vec4(1.0f) - ); - batch->flush(); } diff --git a/src/frontend/screens/MenuScreen.hpp b/src/frontend/screens/MenuScreen.hpp index f9b5dd48..a8caf0fe 100644 --- a/src/frontend/screens/MenuScreen.hpp +++ b/src/frontend/screens/MenuScreen.hpp @@ -13,6 +13,12 @@ public: MenuScreen(Engine& engine); ~MenuScreen(); + void onOpen() override; + void update(float delta) override; void draw(float delta) override; + + const char* getName() const override { + return "menu"; + } }; diff --git a/src/frontend/screens/Screen.hpp b/src/frontend/screens/Screen.hpp index dde2f555..c24dc8b3 100644 --- a/src/frontend/screens/Screen.hpp +++ b/src/frontend/screens/Screen.hpp @@ -13,7 +13,9 @@ protected: public: Screen(Engine& engine); virtual ~Screen(); + virtual void onOpen() = 0; virtual void update(float delta) = 0; virtual void draw(float delta) = 0; virtual void onEngineShutdown() {}; + virtual const char* getName() const = 0; }; diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp index ceb2f4a0..79e9fd63 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -56,6 +56,13 @@ GUI::GUI(Engine& engine) store("tooltip", tooltip); store("tooltip.label", UINode::find(tooltip, "tooltip.label")); container->add(tooltip); + + rootDocument = std::make_unique( + "core:root", + uidocscript {}, + std::dynamic_pointer_cast(container), + nullptr + ); } GUI::~GUI() = default; @@ -74,15 +81,8 @@ std::shared_ptr GUI::getMenu() { } void GUI::onAssetsLoad(Assets* assets) { - assets->store( - std::make_unique( - "core:root", - uidocscript {}, - std::dynamic_pointer_cast(container), - nullptr - ), - "core:root" - ); + rootDocument->rebuildIndices(); + assets->store(rootDocument, "core:root"); } void GUI::resetTooltip() { diff --git a/src/graphics/ui/GUI.hpp b/src/graphics/ui/GUI.hpp index 92e58db5..f2255fb5 100644 --- a/src/graphics/ui/GUI.hpp +++ b/src/graphics/ui/GUI.hpp @@ -22,6 +22,8 @@ namespace devtools { class Editor; } +class UiDocument; + /* Some info about padding and margin. Padding is element inner space, margin is outer @@ -70,6 +72,7 @@ namespace gui { std::shared_ptr pressed; std::shared_ptr focus; std::shared_ptr tooltip; + std::shared_ptr rootDocument; std::unordered_map> storage; std::unique_ptr uicamera; diff --git a/src/logic/scripting/lua/libs/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp index 66a8f648..2f37bcb2 100644 --- a/src/logic/scripting/lua/libs/libgui.cpp +++ b/src/logic/scripting/lua/libs/libgui.cpp @@ -79,9 +79,12 @@ static int l_textbox_paste(lua::State* L) { static int l_container_add(lua::State* L) { auto docnode = get_document_node(L); + if (docnode.document == nullptr) { + throw std::runtime_error("target document not found"); + } auto node = dynamic_cast(docnode.node.get()); if (node == nullptr) { - return 0; + throw std::runtime_error("target container not found"); } auto xmlsrc = lua::require_string(L, 2); try { @@ -99,7 +102,7 @@ static int l_container_add(lua::State* L) { UINode::getIndices(subnode, docnode.document->getMapWriteable()); node->add(std::move(subnode)); } catch (const std::exception& err) { - throw std::runtime_error(err.what()); + throw std::runtime_error("container:add(...): " + std::string(err.what())); } return 0; } diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 891b89c7..be37e7bb 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -58,12 +58,8 @@ static void create_libs(State* L, StateType stateType) { openlib(L, "vec4", vec4lib); openlib(L, "yaml", yamllib); - if (stateType == StateType::SCRIPT) { - openlib(L, "app", applib); - } else if (stateType == StateType::BASE) { - openlib(L, "__vc_app", applib); - } if (stateType == StateType::BASE || stateType == StateType::SCRIPT) { + openlib(L, "__vc_app", applib); openlib(L, "assets", assetslib); openlib(L, "audio", audiolib); openlib(L, "console", consolelib); diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 39168843..04db321e 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -116,11 +116,42 @@ public: } }; -std::unique_ptr scripting::start_coroutine( +class LuaProjectScript : public IProjectScript { +public: + LuaProjectScript(lua::State* L, scriptenv env) : L(L), env(std::move(env)) {} + + void onScreenChange(const std::string& name) override { + if (!lua::pushenv(L, *env)) { + return; + } + if (!lua::getfield(L, "on_screen_changed")) { + lua::pop(L); + return; + } + lua::pushlstring(L, name); + lua::call_nothrow(L, 1, 0); + lua::pop(L); + } +private: + lua::State* L; + scriptenv env; +}; + +std::unique_ptr scripting::load_project_script( const io::path& script ) { auto L = lua::get_main_state(); - if (lua::getglobal(L, "__vc_start_coroutine")) { + auto source = io::read_string(script); + auto env = create_environment(nullptr); + lua::loadbuffer(L, *env, source, script.name()); + lua::call(L, 0); + return std::make_unique(L, std::move(env)); +} + +std::unique_ptr scripting::start_coroutine(const io::path& script) { + auto L = lua::get_main_state(); + auto method = "__vc_start_coroutine"; + if (lua::getglobal(L, method)) { auto source = io::read_string(script); lua::loadbuffer(L, 0, source, script.name()); if (lua::call(L, 1)) { diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index bae5aac6..b6f8733d 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -65,9 +65,16 @@ namespace scripting { void process_post_runnables(); - std::unique_ptr start_coroutine( - const io::path& script - ); + class IProjectScript { + public: + virtual ~IProjectScript() {} + + virtual void onScreenChange(const std::string& name) = 0; + }; + + std::unique_ptr load_project_script(const io::path& script); + + std::unique_ptr start_coroutine(const io::path& script); void on_world_load(LevelController* controller); void on_world_tick(int tps); From 9fcff65ccacab06e36731b4c34fd857a86167fff Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 7 Aug 2025 22:37:40 +0300 Subject: [PATCH 05/28] cleanup --- res/project_script.lua | 24 ++++++++++++++++-------- src/devtools/Project.cpp | 3 +++ src/devtools/Project.hpp | 8 ++++++++ src/engine/Engine.cpp | 9 ++++----- src/engine/Engine.hpp | 5 ----- 5 files changed, 31 insertions(+), 18 deletions(-) diff --git a/res/project_script.lua b/res/project_script.lua index 5f28c300..0da3b54d 100644 --- a/res/project_script.lua +++ b/res/project_script.lua @@ -1,13 +1,13 @@ local menubg -function on_screen_changed(screen) - if screen ~= "menu" then - if menubg then - menubg:destruct() - menubg = nil - end - return +local function clear_menu() + if menubg then + menubg:destruct() + menubg = nil end +end + +local function configure_menu() local controller = {} function controller.resize_menu_bg() local w, h = unpack(gui.get_viewport()) @@ -19,8 +19,16 @@ function on_screen_changed(screen) end _GUI_ROOT.root:add( "", controller) + "z-index='-1' interactive='true'/>", controller) menubg = _GUI_ROOT.menubg controller.resize_menu_bg() menu.page = "main" end + +function on_screen_changed(screen) + if screen ~= "menu" then + clear_menu() + else + configure_menu() + end +end diff --git a/src/devtools/Project.cpp b/src/devtools/Project.cpp index c1d382ee..881f6cc7 100644 --- a/src/devtools/Project.cpp +++ b/src/devtools/Project.cpp @@ -1,6 +1,9 @@ #include "Project.hpp" #include "data/dv_util.hpp" +#include "logic/scripting/scripting.hpp" + +Project::~Project() = default; dv::value Project::serialize() const { return dv::object({ diff --git a/src/devtools/Project.hpp b/src/devtools/Project.hpp index 857b58f1..3824ecfd 100644 --- a/src/devtools/Project.hpp +++ b/src/devtools/Project.hpp @@ -2,13 +2,21 @@ #include #include +#include #include "interfaces/Serializable.hpp" +namespace scripting { + class IProjectScript; +} + struct Project : Serializable { std::string name; std::string title; std::vector basePacks; + std::unique_ptr script; + + ~Project(); dv::value serialize() const override; void deserialize(const dv::value& src) override; diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 5e5ba681..3f1ae9e5 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -185,7 +185,7 @@ void Engine::initialize(CoreParameters coreParameters) { langs::setup(lang, paths.resPaths.collectRoots()); }, true)); - projectScript = load_project_script(); + project->script = load_project_script(); } void Engine::loadSettings() { @@ -242,7 +242,6 @@ void Engine::run() { } } -#include "graphics/ui/elements/Container.hpp" void Engine::postUpdate() { network->update(); postRunnables.run(); @@ -285,7 +284,6 @@ void Engine::saveSettings() { } void Engine::close() { - projectScript.reset(); saveSettings(); logger.info() << "shutting down"; if (screen) { @@ -302,6 +300,7 @@ void Engine::close() { audio::close(); network.reset(); clearKeepedObjects(); + project.reset(); scripting::close(); logger.info() << "scripting finished"; if (!params.headless) { @@ -368,8 +367,8 @@ void Engine::setScreen(std::shared_ptr screen) { if (this->screen) { this->screen->onOpen(); } - if (projectScript && this->screen) { - projectScript->onScreenChange(this->screen->getName()); + if (project->script && this->screen) { + project->script->onScreenChange(this->screen->getName()); } } diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp index d82fc4e6..35619a2a 100644 --- a/src/engine/Engine.hpp +++ b/src/engine/Engine.hpp @@ -75,7 +75,6 @@ class Engine : public util::ObjectsKeeper { std::unique_ptr input; std::unique_ptr gui; std::unique_ptr editor; - std::unique_ptr projectScript; PostRunnables postRunnables; Time time; OnWorldOpen levelConsumer; @@ -183,8 +182,4 @@ public: const Project& getProject() { return *project; } - - scripting::IProjectScript* getProjectScript() { - return projectScript.get(); - } }; From 5785b9fdda49dc730758e0988208047f4d12c1da Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 7 Aug 2025 23:05:50 +0300 Subject: [PATCH 06/28] revert lua_engine.cpp changes --- src/logic/scripting/lua/lua_engine.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index be37e7bb..891b89c7 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -58,8 +58,12 @@ static void create_libs(State* L, StateType stateType) { openlib(L, "vec4", vec4lib); openlib(L, "yaml", yamllib); - if (stateType == StateType::BASE || stateType == StateType::SCRIPT) { + if (stateType == StateType::SCRIPT) { + openlib(L, "app", applib); + } else if (stateType == StateType::BASE) { openlib(L, "__vc_app", applib); + } + if (stateType == StateType::BASE || stateType == StateType::SCRIPT) { openlib(L, "assets", assetslib); openlib(L, "audio", audiolib); openlib(L, "console", consolelib); From 3760fb86f7453dd7673fdb58f713383218211fdf Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 8 Aug 2025 00:29:12 +0300 Subject: [PATCH 07/28] move events library implementation to core:internal/events & disable access to core:internal modules outside of stdlib --- res/layouts/code_editor.xml.lua | 2 +- res/layouts/files_panel.xml.lua | 7 ++--- res/modules/internal/events.lua | 48 ++++++++++++++++++++++++++++ res/scripts/post_content.lua | 3 +- res/scripts/stdlib.lua | 55 +++------------------------------ res/scripts/stdmin.lua | 11 +++++++ 6 files changed, 67 insertions(+), 59 deletions(-) create mode 100644 res/modules/internal/events.lua diff --git a/res/layouts/code_editor.xml.lua b/res/layouts/code_editor.xml.lua index de460f2e..66a70ed8 100644 --- a/res/layouts/code_editor.xml.lua +++ b/res/layouts/code_editor.xml.lua @@ -256,7 +256,7 @@ function open_file_in_editor(filename, line, mutable) end function on_open(mode) - registry = require "core:internal/scripts_registry" + registry = __vc_scripts_registry document.codePanel:setInterval(200, refresh_file_title) diff --git a/res/layouts/files_panel.xml.lua b/res/layouts/files_panel.xml.lua index a4d6ca39..30b0ddc8 100644 --- a/res/layouts/files_panel.xml.lua +++ b/res/layouts/files_panel.xml.lua @@ -43,11 +43,8 @@ function build_files_list(filenames, highlighted_part) end end -function on_open(mode) - registry = require "core:internal/scripts_registry" - - local files_list = document.filesList - +function on_open() + registry = __vc_scripts_registry filenames = registry.filenames table.sort(filenames) build_files_list(filenames) diff --git a/res/modules/internal/events.lua b/res/modules/internal/events.lua new file mode 100644 index 00000000..4820ddd5 --- /dev/null +++ b/res/modules/internal/events.lua @@ -0,0 +1,48 @@ +local events = { + handlers = {} +} + +function events.on(event, func) + if events.handlers[event] == nil then + events.handlers[event] = {} + end + table.insert(events.handlers[event], func) +end + +function events.reset(event, func) + if func == nil then + events.handlers[event] = nil + else + events.handlers[event] = {func} + end +end + +function events.remove_by_prefix(prefix) + for name, handlers in pairs(events.handlers) do + local actualname = name + if type(name) == 'table' then + actualname = name[1] + end + if actualname:sub(1, #prefix+1) == prefix..':' then + events.handlers[actualname] = nil + end + end +end + +function events.emit(event, ...) + local result = nil + local handlers = events.handlers[event] + if handlers == nil then + return nil + end + for _, func in ipairs(handlers) do + local status, newres = xpcall(func, __vc__error, ...) + if not status then + debug.error("error in event ("..event..") handler: "..newres) + else + result = result or newres + end + end + return result +end +return events diff --git a/res/scripts/post_content.lua b/res/scripts/post_content.lua index 7b17876e..2ad3633b 100644 --- a/res/scripts/post_content.lua +++ b/res/scripts/post_content.lua @@ -62,5 +62,4 @@ end cache_names(block) cache_names(item) -local scripts_registry = require "core:internal/scripts_registry" -scripts_registry.build_registry() +__vc_scripts_registry.build_registry() diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index b0fb86ea..c5d48faf 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -169,61 +169,12 @@ function inventory.set_description(invid, slot, description) inventory.set_data(invid, slot, "description", description) end ------------------------------------------------- -------------------- Events --------------------- ------------------------------------------------- -events = { - handlers = {} -} - -function events.on(event, func) - if events.handlers[event] == nil then - events.handlers[event] = {} - end - table.insert(events.handlers[event], func) -end - -function events.reset(event, func) - if func == nil then - events.handlers[event] = nil - else - events.handlers[event] = {func} - end -end - -function events.remove_by_prefix(prefix) - for name, handlers in pairs(events.handlers) do - local actualname = name - if type(name) == 'table' then - actualname = name[1] - end - if actualname:sub(1, #prefix+1) == prefix..':' then - events.handlers[actualname] = nil - end - end -end +events = require "core:internal/events" function pack.unload(prefix) events.remove_by_prefix(prefix) end -function events.emit(event, ...) - local result = nil - local handlers = events.handlers[event] - if handlers == nil then - return nil - end - for _, func in ipairs(handlers) do - local status, newres = xpcall(func, __vc__error, ...) - if not status then - debug.error("error in event ("..event..") handler: "..newres) - else - result = result or newres - end - end - return result -end - gui_util = require "core:internal/gui_util" Document = gui_util.Document @@ -319,11 +270,12 @@ entities.get_all = function(uids) end local bytearray = require "core:internal/bytearray" - Bytearray = bytearray.FFIBytearray Bytearray_as_string = bytearray.FFIBytearray_as_string Bytearray_construct = function(...) return Bytearray(...) end +__vc_scripts_registry = require "core:internal/scripts_registry" + file.open = require "core:internal/stream_providers/file" file.open_named_pipe = require "core:internal/stream_providers/named_pipe" @@ -342,6 +294,7 @@ else end ffi = nil +__vc_lock_internal_modules() math.randomseed(time.uptime() * 1536227939) diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 38fdbfdc..669d5874 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -550,6 +550,8 @@ function reload_module(name) end end +local internal_locked = false + -- Load script with caching -- -- path - script path `contentpack:filename`. @@ -559,6 +561,11 @@ end function __load_script(path, nocache) local packname, filename = parse_path(path) + if internal_locked and (packname == "res" or packname == "core") + and filename:starts_with("modules/internal") then + error("access to core:internal modules outside of [core]") + end + -- __cached_scripts used in condition because cached result may be nil if not nocache and __cached_scripts[path] ~= nil then return package.loaded[path] @@ -579,6 +586,10 @@ function __load_script(path, nocache) return result end +function __vc_lock_internal_modules() + internal_locked = true +end + function require(path) if not string.find(path, ':') then local prefix, _ = parse_path(_debug_getinfo(2).source) From e6812371ecdc7cbecbcf3136df0f04c00525e550 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 8 Aug 2025 19:58:52 +0300 Subject: [PATCH 08/28] update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9056cce5..c0b0e1c9 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ Debug/voxel_engine /export /config /out +/projects /misc /world @@ -47,4 +48,4 @@ appimage-build/ # libs /libs/ -/vcpkg_installed/ \ No newline at end of file +/vcpkg_installed/ From bfbfe59f9be12a1c6fe88948ce8f32857e5cd1d0 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 8 Aug 2025 21:36:53 +0300 Subject: [PATCH 09/28] add gui.root & add 'app' library to project scripts --- doc/en/scripting/builtins/libgui.md | 6 ++++++ doc/ru/scripting/builtins/libgui.md | 6 ++++++ res/project_script.lua | 2 +- res/scripts/stdlib.lua | 1 + src/logic/scripting/scripting.cpp | 6 ++++++ 5 files changed, 20 insertions(+), 1 deletion(-) diff --git a/doc/en/scripting/builtins/libgui.md b/doc/en/scripting/builtins/libgui.md index cf6839db..cbcca4ce 100644 --- a/doc/en/scripting/builtins/libgui.md +++ b/doc/en/scripting/builtins/libgui.md @@ -103,3 +103,9 @@ gui.load_document( ``` Loads a UI document with its script, returns the name of the document if successfully loaded. + +```lua +gui.root: Document +``` + +Root UI document diff --git a/doc/ru/scripting/builtins/libgui.md b/doc/ru/scripting/builtins/libgui.md index aea2f44a..a9bffe48 100644 --- a/doc/ru/scripting/builtins/libgui.md +++ b/doc/ru/scripting/builtins/libgui.md @@ -100,3 +100,9 @@ gui.load_document( ``` Загружает UI документ с его скриптом, возвращает имя документа, если успешно загружен. + +```lua +gui.root: Document +``` + +Корневой UI документ diff --git a/res/project_script.lua b/res/project_script.lua index 0da3b54d..9158f361 100644 --- a/res/project_script.lua +++ b/res/project_script.lua @@ -17,7 +17,7 @@ local function configure_menu() end return w, h end - _GUI_ROOT.root:add( + gui.root.root:add( "", controller) menubg = _GUI_ROOT.menubg diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index c5d48faf..68b9e4f6 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -189,6 +189,7 @@ end _GUI_ROOT = Document.new("core:root") _MENU = _GUI_ROOT.menu menu = _MENU +gui.root = _GUI_ROOT --- Console library extension --- console.cheats = {} diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 04db321e..00d18668 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -143,6 +143,12 @@ std::unique_ptr scripting::load_project_script( auto L = lua::get_main_state(); auto source = io::read_string(script); auto env = create_environment(nullptr); + lua::pushenv(L, *env); + if (lua::getglobal(L, "__vc_app")) { + lua::setfield(L, "app"); + } + lua::pop(L); + lua::loadbuffer(L, *env, source, script.name()); lua::call(L, 0); return std::make_unique(L, std::move(env)); From 2783b0af7be2f1a5dc6dc93041feb4fe11755641 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 11 Aug 2025 23:53:49 +0300 Subject: [PATCH 10/28] possible hud.open bug fix --- src/frontend/hud.cpp | 1 + src/logic/scripting/lua/libs/libhud.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 60cee6c4..b122c632 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -538,6 +538,7 @@ void Hud::closeInventory() { exchangeSlotInv = nullptr; inventoryOpen = false; inventoryView = nullptr; + secondInvView = nullptr; secondUI = nullptr; for (auto& element : elements) { diff --git a/src/logic/scripting/lua/libs/libhud.cpp b/src/logic/scripting/lua/libs/libhud.cpp index 988685cc..432d8eb6 100644 --- a/src/logic/scripting/lua/libs/libhud.cpp +++ b/src/logic/scripting/lua/libs/libhud.cpp @@ -46,7 +46,7 @@ static int l_open(lua::State* L) { } return lua::pushinteger(L, hud->openInventory( layout, - level->inventories->get(invid), + lua::isnoneornil(L, 3) ? nullptr : level->inventories->get(invid), playerInventory )->getId()); } From 303346f5e55711454cbb76cf1525556e138e4eb4 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 11 Aug 2025 23:58:00 +0300 Subject: [PATCH 11/28] fix: hud.close after hud.show_overlay bug --- src/frontend/hud.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index b122c632..08dfcf40 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -598,6 +598,9 @@ void Hud::remove(const std::shared_ptr& node) { } } cleanup(); + if (node == secondUI) { + closeInventory(); + } } void Hud::setDebug(bool flag) { From 2e17c74f91f4219fc42868124ea4b124d2f050e1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 12 Aug 2025 20:35:46 +0300 Subject: [PATCH 12/28] fix text 3d position and culling --- src/graphics/core/Batch3D.cpp | 10 +++++++++- src/graphics/render/TextsRenderer.cpp | 8 ++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/graphics/core/Batch3D.cpp b/src/graphics/core/Batch3D.cpp index 8f222a51..df6fdef7 100644 --- a/src/graphics/core/Batch3D.cpp +++ b/src/graphics/core/Batch3D.cpp @@ -126,7 +126,15 @@ void Batch3D::sprite( float scale = 1.0f / static_cast(atlasRes); float u = (index % atlasRes) * scale; float v = 1.0f - ((index / atlasRes) * scale) - scale; - sprite(pos, up, right, w, h, UVRegion(u, v, u+scale, v+scale), tint); + sprite( + pos + right * w + up * h, // revert centering + up, + right, + w, + h, + UVRegion(u, v, u + scale, v + scale), + tint + ); } void Batch3D::sprite( diff --git a/src/graphics/render/TextsRenderer.cpp b/src/graphics/render/TextsRenderer.cpp index 9e4210df..4b905e13 100644 --- a/src/graphics/render/TextsRenderer.cpp +++ b/src/graphics/render/TextsRenderer.cpp @@ -51,6 +51,7 @@ void TextsRenderer::renderNote( glm::vec3 yvec = note.getAxisY(); int width = font.calcWidth(text, text.length()); + int height = font.getLineHeight(); if (preset.displayMode == NoteDisplayMode::Y_FREE_BILLBOARD || preset.displayMode == NoteDisplayMode::XY_FREE_BILLBOARD) { xvec = camera.position - pos; @@ -96,8 +97,11 @@ void TextsRenderer::renderNote( pos = screenPos / screenPos.w; } - } else if (!frustum.isBoxVisible(pos - xvec * (width * 0.5f * preset.scale), - pos + xvec * (width * 0.5f * preset.scale))) { + } else if (!frustum.isBoxVisible( + pos - xvec * (width * 0.5f) * preset.scale, + pos + xvec * (width * 0.5f) * preset.scale + + yvec * static_cast(height) * preset.scale + )) { return; } auto color = preset.color; From bef8e4b9d6a503e3b56334080a336373cb85591c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 13 Aug 2025 23:42:06 +0300 Subject: [PATCH 13/28] feat: model autorefresh in editor --- res/layouts/code_editor.xml.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/res/layouts/code_editor.xml.lua b/res/layouts/code_editor.xml.lua index 66a70ed8..0f9a02e8 100644 --- a/res/layouts/code_editor.xml.lua +++ b/res/layouts/code_editor.xml.lua @@ -81,6 +81,11 @@ local function refresh_file_title() document.saveIcon.enabled = edited document.title.text = gui.str('File')..' - '..current_file.filename ..(edited and ' *' or '') + + local info = registry.get_info(current_file.filename) + if info and info.type == "model" then + pcall(run_current_file) + end end function on_control_combination(keycode) From 2cdcebf9d97d6c7aff051ae18fec1875a07fb23c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 13 Aug 2025 23:59:05 +0300 Subject: [PATCH 14/28] add 'assets' library docs --- doc/en/scripting.md | 1 + doc/en/scripting/builtins/libassets.md | 28 ++++++++++++++++++++++ doc/ru/scripting.md | 1 + doc/ru/scripting/builtins/libassets.md | 28 ++++++++++++++++++++++ res/layouts/code_editor.xml.lua | 1 - src/coders/vcm.cpp | 1 - src/logic/scripting/lua/libs/libassets.cpp | 3 +++ 7 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 doc/en/scripting/builtins/libassets.md create mode 100644 doc/ru/scripting/builtins/libassets.md diff --git a/doc/en/scripting.md b/doc/en/scripting.md index 05a89a26..131b9cfa 100644 --- a/doc/en/scripting.md +++ b/doc/en/scripting.md @@ -10,6 +10,7 @@ Subsections: - [Entities and components](scripting/ecs.md) - [Libraries](#) - [app](scripting/builtins/libapp.md) + - [assets](scripting/builtins/libassets.md) - [base64](scripting/builtins/libbase64.md) - [bjson, json, toml, yaml](scripting/filesystem.md) - [block](scripting/builtins/libblock.md) diff --git a/doc/en/scripting/builtins/libassets.md b/doc/en/scripting/builtins/libassets.md new file mode 100644 index 00000000..bd095435 --- /dev/null +++ b/doc/en/scripting/builtins/libassets.md @@ -0,0 +1,28 @@ +# *assets* library + +A library for working with audio/visual assets. + +## Functions + +```lua +-- Loads a texture +assets.load_texture( + -- Array of bytes of an image file + data: table | Bytearray, + -- Texture name after loading + name: str, + -- Image file format (only png is supported) + [optional] + format: str = "png" +) + +-- Parses and loads a 3D model +assets.parse_model( + -- Model file format (xml / vcm) + format: str, + -- Contents of the model file + content: str, + -- Model name after loading + name: str +) +``` diff --git a/doc/ru/scripting.md b/doc/ru/scripting.md index eaae3302..aa25e072 100644 --- a/doc/ru/scripting.md +++ b/doc/ru/scripting.md @@ -10,6 +10,7 @@ - [Сущности и компоненты](scripting/ecs.md) - [Библиотеки](#) - [app](scripting/builtins/libapp.md) + - [assets](scripting/builtins/libassets.md) - [base64](scripting/builtins/libbase64.md) - [bjson, json, toml, yaml](scripting/filesystem.md) - [block](scripting/builtins/libblock.md) diff --git a/doc/ru/scripting/builtins/libassets.md b/doc/ru/scripting/builtins/libassets.md new file mode 100644 index 00000000..2c510948 --- /dev/null +++ b/doc/ru/scripting/builtins/libassets.md @@ -0,0 +1,28 @@ +# Библиотека *assets* + +Библиотека для работы с аудио/визуальными загружаемыми ресурсами. + +## Функции + +```lua +-- Загружает текстуру +assets.load_texture( + -- Массив байт файла изображения + data: table | Bytearray, + -- Имя текстуры после загрузки + name: str, + -- Формат файла изображения (поддерживается только png) + [опционально] + format: str = "png" +) + +-- Парсит и загружает 3D модель +assets.parse_model( + -- Формат файла модели (xml / vcm) + format: str, + -- Содержимое файла модели + content: str, + -- Имя модели после загрузки + name: str +) +``` diff --git a/res/layouts/code_editor.xml.lua b/res/layouts/code_editor.xml.lua index 0f9a02e8..4e6c68d4 100644 --- a/res/layouts/code_editor.xml.lua +++ b/res/layouts/code_editor.xml.lua @@ -123,7 +123,6 @@ function run_current_file() local unit = info and info.unit if script_type == "model" then - print(current_file.filename) clear_output() local _, err = pcall(reload_model, current_file.filename, unit) if err then diff --git a/src/coders/vcm.cpp b/src/coders/vcm.cpp index a12524c0..9b564787 100644 --- a/src/coders/vcm.cpp +++ b/src/coders/vcm.cpp @@ -174,7 +174,6 @@ std::unique_ptr vcm::parse( "'model' tag expected as root, got '" + root.getTag() + "'" ); } - std::cout << xml::stringify(*doc) << std::endl; return load_model(root); } catch (const parsing_error& err) { throw std::runtime_error(err.errorLog()); diff --git a/src/logic/scripting/lua/libs/libassets.cpp b/src/logic/scripting/lua/libs/libassets.cpp index 8cfb8ca8..047b6330 100644 --- a/src/logic/scripting/lua/libs/libassets.cpp +++ b/src/logic/scripting/lua/libs/libassets.cpp @@ -23,6 +23,9 @@ static void load_texture( } static int l_load_texture(lua::State* L) { + if (lua::isstring(L, 3) && lua::require_lstring(L, 3) != "png") { + throw std::runtime_error("unsupportd image format"); + } if (lua::istable(L, 1)) { lua::pushvalue(L, 1); size_t size = lua::objlen(L, 1); From 47939c052724259d2b000fa3f620cbb06ebc8c61 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 14 Aug 2025 02:24:48 +0300 Subject: [PATCH 15/28] feat: textbox indentation manipulations using tab, shift+tab --- src/graphics/ui/elements/TextBox.cpp | 78 +++++++++++++++++++++++++++- src/graphics/ui/elements/TextBox.hpp | 2 + 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index 0ed75882..34e2784a 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -810,6 +810,82 @@ void TextBox::stepDefaultUp(bool shiftPressed, bool breakSelection) { } } +static int calc_indent(int linestart, std::wstring_view input) { + int indent = 0; + while (linestart + indent < input.length() && + input[linestart + indent] == L' ') + indent++; + return indent; +} + +void TextBox::onTab(bool shiftPressed) { + std::wstring indentStr = L" "; + + if (!shiftPressed && getSelectionLength() == 0) { + paste(indentStr); + return; + } + if (getSelectionLength() == 0) { + selectionStart = caret; + selectionEnd = caret; + selectionOrigin = caret; + } + + int lineA = getLineAt(selectionStart); + int lineB = getLineAt(selectionEnd); + int caretLine = getLineAt(caret); + + size_t lineAStart = getLinePos(lineA); + size_t lineBStart = getLinePos(lineB); + size_t caretLineStart = getLinePos(caretLine); + size_t caretIndent = calc_indent(caretLineStart, input); + size_t aIndent = calc_indent(lineAStart, input); + size_t bIndent = calc_indent(lineBStart, input); + + int lastSelectionStart = selectionStart; + int lastSelectionEnd = selectionEnd; + size_t lastCaret = caret; + + resetSelection(); + + for (int line = lineA; line <= lineB; line++) { + size_t linestart = getLinePos(line); + int indent = calc_indent(linestart, input); + + if (shiftPressed) { + if (indent >= indentStr.length()) { + setCaret(linestart); + select(linestart, linestart + indentStr.length()); + eraseSelected(); + } + } else { + setCaret(linestart); + paste(indentStr); + } + refreshLabel(); // todo: replace with textbox cache + } + + int linestart = getLinePos(caretLine); + int linestartA = getLinePos(lineA); + int linestartB = getLinePos(lineB); + int la = lastSelectionStart - lineAStart; + int lb = lastSelectionEnd - lineBStart; + if (shiftPressed) { + setCaret(lastCaret - caretLineStart + linestart - std::min(caretIndent, indentStr.length())); + selectionStart = la + linestartA - std::min(std::min(la, aIndent), indentStr.length()); + selectionEnd = lb + linestartB - std::min(std::min(lb, bIndent), indentStr.length()); + } else { + setCaret(lastCaret - caretLineStart + linestart + indentStr.length()); + selectionStart = la + linestartA + indentStr.length(); + selectionEnd = lb + linestartB + indentStr.length(); + } + if (selectionOrigin == lastSelectionStart) { + selectionOrigin = selectionStart; + } else { + selectionOrigin = selectionEnd; + } +} + void TextBox::refreshSyntax() { if (!syntax.empty()) { const auto& processor = gui.getEditor().getSyntaxProcessor(); @@ -868,7 +944,7 @@ void TextBox::performEditingKeyboardEvents(Keycode key) { } } } else if (key == Keycode::TAB) { - paste(L" "); + onTab(shiftPressed); } else if (key == Keycode::LEFT) { stepLeft(shiftPressed, breakSelection); } else if (key == Keycode::RIGHT) { diff --git a/src/graphics/ui/elements/TextBox.hpp b/src/graphics/ui/elements/TextBox.hpp index 321d71eb..9bbfac39 100644 --- a/src/graphics/ui/elements/TextBox.hpp +++ b/src/graphics/ui/elements/TextBox.hpp @@ -71,6 +71,8 @@ namespace gui { void stepDefaultDown(bool shiftPressed, bool breakSelection); void stepDefaultUp(bool shiftPressed, bool breakSelection); + void onTab(bool shiftPressed); + size_t normalizeIndex(int index); int calcIndexAt(int x, int y) const; From 140841f65cbdeb2974bc137e20f00311f7d4c299 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 15 Aug 2025 07:45:16 +0300 Subject: [PATCH 16/28] refresh root document indices on GUI::add --- src/graphics/ui/GUI.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp index 79e9fd63..3ab438b1 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -302,6 +302,7 @@ bool GUI::isFocusCaught() const { } void GUI::add(std::shared_ptr node) { + UINode::getIndices(node, rootDocument->getMapWriteable()); container->add(std::move(node)); } From 547a39554ccbf816344c1283e7800dbde894e826 Mon Sep 17 00:00:00 2001 From: clasher113 Date: Fri, 15 Aug 2025 12:27:27 +0300 Subject: [PATCH 17/28] fix: script overriding --- src/content/ContentLoader.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 2aaff315..e260ebd7 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -416,19 +416,20 @@ void ContentLoader::load() { template static void load_script(const Content& content, T& def) { - const auto& name = def.name; - size_t pos = name.find(':'); + const auto& scriptName = def.scriptFile; + if (scriptName.empty()) return; + size_t pos = scriptName.find(':'); if (pos == std::string::npos) { throw std::runtime_error("invalid content unit name"); } - const auto runtime = content.getPackRuntime(name.substr(0, pos)); + const auto runtime = content.getPackRuntime(scriptName.substr(0, pos)); const auto& pack = runtime->getInfo(); const auto& folder = pack.folder; auto scriptfile = folder / ("scripts/" + def.scriptName + ".lua"); if (io::is_regular_file(scriptfile)) { scripting::load_content_script( runtime->getEnvironment(), - name, + def.name, scriptfile, def.scriptFile, def.rt.funcsset From 9ce70d70e1aa9933b550c6df60bc6a9908cb58f0 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 15 Aug 2025 12:41:37 +0300 Subject: [PATCH 18/28] fix indentation manipulations history writing --- src/graphics/ui/elements/TextBox.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index 34e2784a..1755b6b9 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -846,6 +846,8 @@ void TextBox::onTab(bool shiftPressed) { int lastSelectionEnd = selectionEnd; size_t lastCaret = caret; + auto combination = history->beginCombination(); + resetSelection(); for (int line = lineA; line <= lineB; line++) { @@ -884,6 +886,7 @@ void TextBox::onTab(bool shiftPressed) { } else { selectionOrigin = selectionEnd; } + historian->sync(); } void TextBox::refreshSyntax() { From 53ce219127b30cdabf3a3eabd002c76277d27d16 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 15 Aug 2025 23:48:04 +0300 Subject: [PATCH 19/28] add 'is_new' argument to on_world_open --- src/frontend/hud.cpp | 10 +++++----- src/logic/scripting/scripting.cpp | 7 ++++++- src/world/World.cpp | 2 ++ src/world/World.hpp | 2 ++ 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 60cee6c4..660b63fc 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -324,7 +324,7 @@ void Hud::updateWorldGenDebug() { void Hud::update(bool visible) { const auto& chunks = *player.chunks; - bool is_menu_open = menu.hasOpenPage(); + bool isMenuOpen = menu.hasOpenPage(); debugPanel->setVisible( debug && visible && !(inventoryOpen && inventoryView == nullptr) @@ -333,13 +333,13 @@ void Hud::update(bool visible) { if (!visible && inventoryOpen) { closeInventory(); } - if (pause && !is_menu_open) { + if (pause && !isMenuOpen) { setPause(false); } if (!gui.isFocusCaught()) { processInput(visible); } - if ((is_menu_open || inventoryOpen) == input.getCursor().locked) { + if ((isMenuOpen || inventoryOpen) == input.getCursor().locked) { input.toggleCursor(); } @@ -360,8 +360,8 @@ void Hud::update(bool visible) { contentAccessPanel->setSize(glm::vec2(caSize.x, windowSize.y)); contentAccess->setMinSize(glm::vec2(1, windowSize.y)); hotbarView->setVisible(visible && !(secondUI && !inventoryView)); - darkOverlay->setVisible(is_menu_open); - menu.setVisible(is_menu_open); + darkOverlay->setVisible(isMenuOpen); + menu.setVisible(isMenuOpen); if (visible) { for (auto& element : elements) { diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 00d18668..bb33a8bc 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -27,6 +27,7 @@ #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" #include "world/Level.hpp" +#include "world/World.hpp" #include "interfaces/Process.hpp" using namespace scripting; @@ -330,7 +331,11 @@ void scripting::on_world_load(LevelController* controller) { } for (auto& pack : content_control->getAllContentPacks()) { - lua::emit_event(L, pack.id + ":.worldopen"); + lua::emit_event(L, pack.id + ":.worldopen", [](auto L) { + return lua::pushboolean( + L, !scripting::level->getWorld()->getInfo().isLoaded + ); + }); } } diff --git a/src/world/World.cpp b/src/world/World.cpp index 96264336..6508a4da 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -116,6 +116,8 @@ std::unique_ptr World::load( if (!info.has_value()) { throw world_load_error("could not to find world.json"); } + info->isLoaded = true; + logger.info() << "loading world " << info->name << " (" << worldFilesPtr->getFolder().string() << ")"; logger.info() << "world version: " << info->major << "." << info->minor diff --git a/src/world/World.hpp b/src/world/World.hpp index 295ddac3..725b9182 100644 --- a/src/world/World.hpp +++ b/src/world/World.hpp @@ -45,6 +45,8 @@ struct WorldInfo : public Serializable { int major = 0, minor = -1; + bool isLoaded = false; + dv::value serialize() const override; void deserialize(const dv::value& src) override; }; From 6af74fc21955550086d68187f2790fd3b46db73e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 16 Aug 2025 12:48:42 +0300 Subject: [PATCH 20/28] improve blocks.set performance --- src/graphics/render/ChunksRenderer.cpp | 3 +++ src/logic/scripting/lua/libs/libblock.cpp | 5 +---- src/voxels/Chunk.cpp | 1 + src/voxels/Chunk.hpp | 1 + src/voxels/blocks_agent.cpp | 18 ++++++++++-------- src/voxels/blocks_agent.hpp | 4 ++-- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index bb816c37..4100e7b2 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -171,6 +171,9 @@ const Mesh* ChunksRenderer::retrieveChunk( if (mesh == nullptr) { return nullptr; } + if (chunk->flags.dirtyHeights) { + chunk->updateHeights(); + } if (culling) { glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D); glm::vec3 max( diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index eb699bc5..52e2931c 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -101,12 +101,9 @@ static int l_set(lua::State* L) { if (static_cast(id) >= indices->blocks.count()) { return 0; } - int cx = floordiv(x); - int cz = floordiv(z); - if (!blocks_agent::get_chunk(*level->chunks, cx, cz)) { + if (!blocks_agent::set(*level->chunks, x, y, z, id, int2blockstate(state))) { return 0; } - blocks_agent::set(*level->chunks, x, y, z, id, int2blockstate(state)); auto chunksController = controller->getChunksController(); if (chunksController == nullptr) { diff --git a/src/voxels/Chunk.cpp b/src/voxels/Chunk.cpp index 2c1f4fd8..e6da850e 100644 --- a/src/voxels/Chunk.cpp +++ b/src/voxels/Chunk.cpp @@ -14,6 +14,7 @@ Chunk::Chunk(int xpos, int zpos) : x(xpos), z(zpos) { } void Chunk::updateHeights() { + flags.dirtyHeights = false; for (uint i = 0; i < CHUNK_VOL; i++) { if (voxels[i].id != 0) { bottom = i / (CHUNK_D * CHUNK_W); diff --git a/src/voxels/Chunk.hpp b/src/voxels/Chunk.hpp index 3ce6e641..a15907f0 100644 --- a/src/voxels/Chunk.hpp +++ b/src/voxels/Chunk.hpp @@ -37,6 +37,7 @@ public: bool loadedLights : 1; bool entities : 1; bool blocksData : 1; + bool dirtyHeights : 1; } flags {}; /// @brief Block inventories map where key is index of block in voxels array diff --git a/src/voxels/blocks_agent.cpp b/src/voxels/blocks_agent.cpp index a23fa9ef..d56b1838 100644 --- a/src/voxels/blocks_agent.cpp +++ b/src/voxels/blocks_agent.cpp @@ -7,7 +7,7 @@ using namespace blocks_agent; template -static inline void set_block( +static inline bool set_block( Storage& chunks, int32_t x, int32_t y, @@ -16,14 +16,14 @@ static inline void set_block( blockstate state ) { if (y < 0 || y >= CHUNK_H) { - return; + return false; } const auto& indices = chunks.getContentIndices(); int cx = floordiv(x); int cz = floordiv(z); Chunk* chunk = get_chunk(chunks, cx, cz); if (chunk == nullptr) { - return; + return false; } int lx = x - cx * CHUNK_W; int lz = z - cz * CHUNK_D; @@ -60,7 +60,8 @@ static inline void set_block( else if (y + 1 > chunk->top) chunk->top = y + 1; else if (id == 0) - chunk->updateHeights(); + chunk->flags.dirtyHeights = true; + if (lx == 0 && (chunk = get_chunk(chunks, cx - 1, cz))) { chunk->flags.modified = true; @@ -74,9 +75,10 @@ static inline void set_block( if (lz == CHUNK_D - 1 && (chunk = get_chunk(chunks, cx, cz + 1))) { chunk->flags.modified = true; } + return true; } -void blocks_agent::set( +bool blocks_agent::set( Chunks& chunks, int32_t x, int32_t y, @@ -84,10 +86,10 @@ void blocks_agent::set( uint32_t id, blockstate state ) { - set_block(chunks, x, y, z, id, state); + return set_block(chunks, x, y, z, id, state); } -void blocks_agent::set( +bool blocks_agent::set( GlobalChunks& chunks, int32_t x, int32_t y, @@ -95,7 +97,7 @@ void blocks_agent::set( uint32_t id, blockstate state ) { - set_block(chunks, x, y, z, id, state); + return set_block(chunks, x, y, z, id, state); } template diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index 34fde035..efecf4e3 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -119,7 +119,7 @@ inline bool is_replaceable_at(const Storage& chunks, int32_t x, int32_t y, int32 /// @param z block position Z /// @param id new block id /// @param state new block state -void set( +bool set( Chunks& chunks, int32_t x, int32_t y, @@ -135,7 +135,7 @@ void set( /// @param z block position Z /// @param id new block id /// @param state new block state -void set( +bool set( GlobalChunks& chunks, int32_t x, int32_t y, From 8d29c59125936b6f906440d6f74efefd577d7cae Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 16 Aug 2025 13:38:51 +0300 Subject: [PATCH 21/28] fix rigidbody:set_gravity_scale --- src/logic/scripting/lua/libs/lib__rigidbody.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/logic/scripting/lua/libs/lib__rigidbody.cpp b/src/logic/scripting/lua/libs/lib__rigidbody.cpp index d0e66940..f5558ac4 100644 --- a/src/logic/scripting/lua/libs/lib__rigidbody.cpp +++ b/src/logic/scripting/lua/libs/lib__rigidbody.cpp @@ -54,7 +54,7 @@ static int l_get_gravity_scale(lua::State* L) { static int l_set_gravity_scale(lua::State* L) { if (auto entity = get_entity(L, 1)) { - entity->getRigidbody().hitbox.gravityScale = lua::tonumber(L, 2); + entity->getRigidbody().hitbox.gravityScale = lua::tovec3(L, 2).y; } return 0; } From c8d760e83edd82d82d94aff3a322b992ebee47e6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 18 Aug 2025 21:20:18 +0300 Subject: [PATCH 22/28] update project script methods, replace project script with project client script --- ...{project_script.lua => project_client.lua} | 12 +- src/devtools/Project.hpp | 4 +- src/engine/Engine.cpp | 136 ++++++++++-------- src/engine/Engine.hpp | 7 +- src/logic/scripting/scripting.cpp | 11 +- src/logic/scripting/scripting.hpp | 10 +- 6 files changed, 92 insertions(+), 88 deletions(-) rename res/{project_script.lua => project_client.lua} (76%) diff --git a/res/project_script.lua b/res/project_client.lua similarity index 76% rename from res/project_script.lua rename to res/project_client.lua index 9158f361..ede54110 100644 --- a/res/project_script.lua +++ b/res/project_client.lua @@ -1,13 +1,13 @@ local menubg -local function clear_menu() +function on_menu_clear() if menubg then menubg:destruct() menubg = nil end end -local function configure_menu() +function on_menu_setup() local controller = {} function controller.resize_menu_bg() local w, h = unpack(gui.get_viewport()) @@ -24,11 +24,3 @@ local function configure_menu() controller.resize_menu_bg() menu.page = "main" end - -function on_screen_changed(screen) - if screen ~= "menu" then - clear_menu() - else - configure_menu() - end -end diff --git a/src/devtools/Project.hpp b/src/devtools/Project.hpp index 3824ecfd..be9d88a3 100644 --- a/src/devtools/Project.hpp +++ b/src/devtools/Project.hpp @@ -7,14 +7,14 @@ #include "interfaces/Serializable.hpp" namespace scripting { - class IProjectScript; + class IClientProjectScript; } struct Project : Serializable { std::string name; std::string title; std::vector basePacks; - std::unique_ptr script; + std::unique_ptr clientScript; ~Project(); diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 3f1ae9e5..0288d609 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -60,11 +60,11 @@ static std::unique_ptr load_icon() { return nullptr; } -static std::unique_ptr load_project_script() { - io::path scriptFile = "project:project_script.lua"; +static std::unique_ptr load_client_project_script() { + io::path scriptFile = "project:project_client.lua"; if (io::exists(scriptFile)) { logger.info() << "starting project script"; - return scripting::load_project_script(scriptFile); + return scripting::load_client_project_script(scriptFile); } else { logger.warning() << "project script does not exists"; } @@ -83,6 +83,68 @@ Engine& Engine::getInstance() { return *instance; } +void Engine::onContentLoad() { + editor->loadTools(); + langs::setup(langs::get_current(), paths.resPaths.collectRoots()); + + if (isHeadless()) { + return; + } + for (auto& pack : content->getAllContentPacks()) { + auto configFolder = pack.folder / "config"; + auto bindsFile = configFolder / "bindings.toml"; + if (io::is_regular_file(bindsFile)) { + input->getBindings().read( + toml::parse( + bindsFile.string(), io::read_string(bindsFile) + ), + BindType::BIND + ); + } + } + loadAssets(); +} + +void Engine::initializeClient() { + std::string title = project->title; + if (title.empty()) { + title = "VoxelCore v" + + std::to_string(ENGINE_VERSION_MAJOR) + "." + + std::to_string(ENGINE_VERSION_MINOR); + } + if (ENGINE_DEBUG_BUILD) { + title += " [debug]"; + } + auto [window, input] = Window::initialize(&settings.display, title); + if (!window || !input){ + throw initialize_error("could not initialize window"); + } + window->setFramerate(settings.display.framerate.get()); + + time.set(window->time()); + if (auto icon = load_icon()) { + icon->flipY(); + window->setIcon(icon.get()); + } + this->window = std::move(window); + this->input = std::move(input); + + loadControls(); + + gui = std::make_unique(*this); + if (ENGINE_DEBUG_BUILD) { + menus::create_version_label(*gui); + } + keepAlive(settings.display.fullscreen.observe( + [this](bool value) { + if (value != this->window->isFullscreen()) { + this->window->toggleFullscreen(); + } + }, + true + )); +} + void Engine::initialize(CoreParameters coreParameters) { params = std::move(coreParameters); settingsHandler = std::make_unique(settings); @@ -111,70 +173,17 @@ void Engine::initialize(CoreParameters coreParameters) { controller = std::make_unique(*this); if (!params.headless) { - std::string title = project->title; - if (title.empty()) { - title = "VoxelCore v" + - std::to_string(ENGINE_VERSION_MAJOR) + "." + - std::to_string(ENGINE_VERSION_MINOR); - } - if (ENGINE_DEBUG_BUILD) { - title += " [debug]"; - } - auto [window, input] = Window::initialize(&settings.display, title); - if (!window || !input){ - throw initialize_error("could not initialize window"); - } - window->setFramerate(settings.display.framerate.get()); - - time.set(window->time()); - if (auto icon = load_icon()) { - icon->flipY(); - window->setIcon(icon.get()); - } - this->window = std::move(window); - this->input = std::move(input); - - loadControls(); - - gui = std::make_unique(*this); - if (ENGINE_DEBUG_BUILD) { - menus::create_version_label(*gui); - } - keepAlive(settings.display.fullscreen.observe( - [this](bool value) { - if (value != this->window->isFullscreen()) { - this->window->toggleFullscreen(); - } - }, - true - )); + initializeClient(); } audio::initialize(!params.headless, settings.audio); - bool langNotSet = settings.ui.language.get() == "auto"; - if (langNotSet) { + if (settings.ui.language.get() == "auto") { settings.ui.language.set( langs::locale_by_envlocale(platform::detect_locale()) ); } content = std::make_unique(*project, paths, *input, [this]() { - editor->loadTools(); - langs::setup(langs::get_current(), paths.resPaths.collectRoots()); - if (!isHeadless()) { - for (auto& pack : content->getAllContentPacks()) { - auto configFolder = pack.folder / "config"; - auto bindsFile = configFolder / "bindings.toml"; - if (io::is_regular_file(bindsFile)) { - input->getBindings().read( - toml::parse( - bindsFile.string(), io::read_string(bindsFile) - ), - BindType::BIND - ); - } - } - loadAssets(); - } + onContentLoad(); }); scripting::initialize(this); @@ -185,7 +194,7 @@ void Engine::initialize(CoreParameters coreParameters) { langs::setup(lang, paths.resPaths.collectRoots()); }, true)); - project->script = load_project_script(); + project->clientScript = load_client_project_script(); } void Engine::loadSettings() { @@ -360,6 +369,9 @@ void Engine::loadProject() { } void Engine::setScreen(std::shared_ptr screen) { + if (project->clientScript && this->screen) { + project->clientScript->onScreenChange(this->screen->getName(), false); + } // reset audio channels (stop all sources) audio::reset_channel(audio::get_channel_index("regular")); audio::reset_channel(audio::get_channel_index("ambient")); @@ -367,8 +379,8 @@ void Engine::setScreen(std::shared_ptr screen) { if (this->screen) { this->screen->onOpen(); } - if (project->script && this->screen) { - project->script->onScreenChange(this->screen->getName()); + if (project->clientScript && this->screen) { + project->clientScript->onScreenChange(this->screen->getName(), true); } } diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp index 35619a2a..98bcd497 100644 --- a/src/engine/Engine.hpp +++ b/src/engine/Engine.hpp @@ -38,10 +38,6 @@ namespace devtools { class Editor; } -namespace scripting { - class IProjectScript; -} - class initialize_error : public std::runtime_error { public: initialize_error(const std::string& message) : std::runtime_error(message) {} @@ -86,6 +82,9 @@ class Engine : public util::ObjectsKeeper { void updateHotkeys(); void loadAssets(); void loadProject(); + + void initializeClient(); + void onContentLoad(); public: Engine(); ~Engine(); diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index bb33a8bc..19ad225d 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -117,20 +117,19 @@ public: } }; -class LuaProjectScript : public IProjectScript { +class LuaProjectScript : public IClientProjectScript { public: LuaProjectScript(lua::State* L, scriptenv env) : L(L), env(std::move(env)) {} - void onScreenChange(const std::string& name) override { + void onScreenChange(const std::string& name, bool show) override { if (!lua::pushenv(L, *env)) { return; } - if (!lua::getfield(L, "on_screen_changed")) { + if (!lua::getfield(L, "on_" + name + (show ? "_setup" : "_clear"))) { lua::pop(L); return; } - lua::pushlstring(L, name); - lua::call_nothrow(L, 1, 0); + lua::call_nothrow(L, 0, 0); lua::pop(L); } private: @@ -138,7 +137,7 @@ private: scriptenv env; }; -std::unique_ptr scripting::load_project_script( +std::unique_ptr scripting::load_client_project_script( const io::path& script ) { auto L = lua::get_main_state(); diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index b6f8733d..78eb8e13 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -65,14 +65,16 @@ namespace scripting { void process_post_runnables(); - class IProjectScript { + class IClientProjectScript { public: - virtual ~IProjectScript() {} + virtual ~IClientProjectScript() {} - virtual void onScreenChange(const std::string& name) = 0; + virtual void onScreenChange(const std::string& name, bool show) = 0; }; - std::unique_ptr load_project_script(const io::path& script); + std::unique_ptr load_client_project_script( + const io::path& script + ); std::unique_ptr start_coroutine(const io::path& script); From b5f1698e7809633b60b6ca8b47cb17f219b673d9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 18 Aug 2025 22:27:38 +0300 Subject: [PATCH 23/28] fix ffi usage in named_pipe_unix.lua --- res/modules/internal/stream_providers/named_pipe_unix.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/modules/internal/stream_providers/named_pipe_unix.lua b/res/modules/internal/stream_providers/named_pipe_unix.lua index 0a9c8904..8de356fe 100644 --- a/res/modules/internal/stream_providers/named_pipe_unix.lua +++ b/res/modules/internal/stream_providers/named_pipe_unix.lua @@ -22,9 +22,9 @@ local O_NONBLOCK = 0x800 local F_GETFL = 3 local function getError() - local err = ffi.errno() + local err = FFI.errno() - return ffi.string(C.strerror(err)).." ("..err..")" + return FFI.string(C.strerror(err)).." ("..err..")" end local lib = {} @@ -101,4 +101,4 @@ return function(path, mode) end return io_stream.new(fd, mode:find('b') ~= nil, lib) -end \ No newline at end of file +end From d32407f1bbe6e2e75db333135f91c7934ec5f1e8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 18 Aug 2025 22:27:50 +0300 Subject: [PATCH 24/28] cleanup --- res/content/base/scripts/hud.lua | 3 +++ res/project_client.lua | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/res/content/base/scripts/hud.lua b/res/content/base/scripts/hud.lua index 00f6bdc6..9fc10f2a 100644 --- a/res/content/base/scripts/hud.lua +++ b/res/content/base/scripts/hud.lua @@ -21,6 +21,9 @@ function on_hud_open() local ppos = vec3.add({player.get_pos(pid)}, {0, 0.7, 0}) local throw_force = vec3.mul(player.get_dir(pid), DROP_FORCE) local drop = base_util.drop(ppos, itemid, 1, data, 1.5) + if not drop then + return + end local velocity = vec3.add(throw_force, vec3.add(pvel, DROP_INIT_VEL)) drop.rigidbody:set_vel(velocity) end) diff --git a/res/project_client.lua b/res/project_client.lua index ede54110..a1952c08 100644 --- a/res/project_client.lua +++ b/res/project_client.lua @@ -20,7 +20,7 @@ function on_menu_setup() gui.root.root:add( "", controller) - menubg = _GUI_ROOT.menubg + menubg = gui.root.menubg controller.resize_menu_bg() menu.page = "main" end From 31cd7912ec8ce0ef66587b79a04b8b7856a96889 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 18 Aug 2025 23:32:12 +0300 Subject: [PATCH 25/28] fix named pipe read (unix) & produce core:error event on error in schedule callback & fix editor traceback --- res/layouts/console.xml.lua | 2 +- res/modules/internal/stream_providers/named_pipe_unix.lua | 2 +- res/modules/schedule.lua | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/res/layouts/console.xml.lua b/res/layouts/console.xml.lua index 94cbda8b..f7e47d9b 100644 --- a/res/layouts/console.xml.lua +++ b/res/layouts/console.xml.lua @@ -4,7 +4,7 @@ history = session.get_entry("commands_history") history_pointer = #history events.on("core:open_traceback", function() - if modes then + if modes and modes.current ~= 'debug' then modes:set('debug') end end) diff --git a/res/modules/internal/stream_providers/named_pipe_unix.lua b/res/modules/internal/stream_providers/named_pipe_unix.lua index 8de356fe..1843daba 100644 --- a/res/modules/internal/stream_providers/named_pipe_unix.lua +++ b/res/modules/internal/stream_providers/named_pipe_unix.lua @@ -31,7 +31,7 @@ local lib = {} function lib.read(fd, len) local buffer = FFI.new("uint8_t[?]", len) - local result = C.read(fd, buffer, len) + local result = tonumber(C.read(fd, buffer, len)) local out = Bytearray() diff --git a/res/modules/schedule.lua b/res/modules/schedule.lua index 24d216f0..5d24a086 100644 --- a/res/modules/schedule.lua +++ b/res/modules/schedule.lua @@ -15,8 +15,9 @@ local Schedule = { local timer = self._timer + dt for id, interval in pairs(self._intervals) do if timer - interval.last_called >= interval.delay then - xpcall(interval.callback, function(s) - debug.error(s..'\n'..debug.traceback()) + local stack_size = debug.count_frames() + xpcall(interval.callback, function(msg) + __vc__error(msg, 1, 1, stack_size) end) interval.last_called = timer local repetions = interval.repetions From abc937769dcbc2d76a086dcc359837daa46f768e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 18 Aug 2025 23:56:46 +0300 Subject: [PATCH 26/28] add nil checks for inventory id, slot index to inventory slot related functions --- src/logic/scripting/lua/libs/libinventory.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/logic/scripting/lua/libs/libinventory.cpp b/src/logic/scripting/lua/libs/libinventory.cpp index 629283a7..5ed3f8cc 100644 --- a/src/logic/scripting/lua/libs/libinventory.cpp +++ b/src/logic/scripting/lua/libs/libinventory.cpp @@ -45,6 +45,12 @@ namespace { template int wrap_slot(lua::State* L) { + if (lua::isnoneornil(L, 1)) { + throw std::runtime_error("inventory id is nil"); + } + if (lua::isnoneornil(L, 2)) { + throw std::runtime_error("slot index is nil"); + } auto invid = lua::tointeger(L, 1); auto slotid = lua::tointeger(L, 2); auto& inv = get_inventory(invid); From 79bb61bbbe282603b35dd02e0f0aaa8d540036c8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 19 Aug 2025 01:33:20 +0300 Subject: [PATCH 27/28] add 'debug.enable-experimental' setting & add experimental vec3, vec2 optimization --- res/modules/internal/maths_inline.lua | 212 ++++++++++++++++++++++++++ res/scripts/stdlib.lua | 6 + src/io/settings_io.cpp | 1 + src/settings.hpp | 2 + 4 files changed, 221 insertions(+) create mode 100644 res/modules/internal/maths_inline.lua diff --git a/res/modules/internal/maths_inline.lua b/res/modules/internal/maths_inline.lua new file mode 100644 index 00000000..e3e37fcd --- /dev/null +++ b/res/modules/internal/maths_inline.lua @@ -0,0 +1,212 @@ +-- =================================================== -- +-- ====================== vec3 ======================= -- +-- =================================================== -- +function vec3.add(a, b, dst) + local btype = type(b) + if dst then + if btype == "table" then + dst[1] = a[1] + b[1] + dst[2] = a[2] + b[2] + dst[3] = a[3] + b[3] + else + dst[1] = a[1] + b + dst[2] = a[2] + b + dst[3] = a[3] + b + end + return dst + else + if btype == "table" then + return {a[1] + b[1], a[2] + b[2], a[3] + b[3]} + else + return {a[1] + b, a[2] + b, a[3] + b} + end + end +end + +function vec3.sub(a, b, dst) + local btype = type(b) + if dst then + if btype == "table" then + dst[1] = a[1] - b[1] + dst[2] = a[2] - b[2] + dst[3] = a[3] - b[3] + else + dst[1] = a[1] - b + dst[2] = a[2] - b + dst[3] = a[3] - b + end + return dst + else + if btype == "table" then + return {a[1] - b[1], a[2] - b[2], a[3] - b[3]} + else + return {a[1] - b, a[2] - b, a[3] - b} + end + end +end + +function vec3.mul(a, b, dst) + local btype = type(b) + if dst then + if btype == "table" then + dst[1] = a[1] * b[1] + dst[2] = a[2] * b[2] + dst[3] = a[3] * b[3] + else + dst[1] = a[1] * b + dst[2] = a[2] * b + dst[3] = a[3] * b + end + return dst + else + if btype == "table" then + return {a[1] * b[1], a[2] * b[2], a[3] * b[3]} + else + return {a[1] * b, a[2] * b, a[3] * b} + end + end +end + +function vec3.div(a, b, dst) + local btype = type(b) + if dst then + if btype == "table" then + dst[1] = a[1] / b[1] + dst[2] = a[2] / b[2] + dst[3] = a[3] / b[3] + else + dst[1] = a[1] / b + dst[2] = a[2] / b + dst[3] = a[3] / b + end + return dst + else + if btype == "table" then + return {a[1] / b[1], a[2] / b[2], a[3] / b[3]} + else + return {a[1] / b, a[2] / b, a[3] / b} + end + end +end + +function vec3.abs(a, dst) + local x = a[1] + local y = a[2] + local z = a[3] + if dst then + dst[1] = x < 0.0 and -x or x + dst[2] = y < 0.0 and -y or y + dst[3] = z < 0.0 and -z or z + else + return { + x < 0.0 and -x or x, + y < 0.0 and -y or y, + z < 0.0 and -z or z, + } + end +end + +function vec3.dot(a, b) + return a[1] * b[1] + a[2] * b[2] + a[3] * b[3] +end + +-- =================================================== -- +-- ====================== vec2 ======================= -- +-- =================================================== -- +function vec2.add(a, b, dst) + local btype = type(b) + if dst then + if btype == "table" then + dst[1] = a[1] + b[1] + dst[2] = a[2] + b[2] + else + dst[1] = a[1] + b + dst[2] = a[2] + b + end + return dst + else + if btype == "table" then + return {a[1] + b[1], a[2] + b[2]} + else + return {a[1] + b, a[2] + b} + end + end +end + +function vec2.sub(a, b, dst) + local btype = type(b) + if dst then + if btype == "table" then + dst[1] = a[1] - b[1] + dst[2] = a[2] - b[2] + else + dst[1] = a[1] - b + dst[2] = a[2] - b + end + return dst + else + if btype == "table" then + return {a[1] - b[1], a[2] - b[2]} + else + return {a[1] - b, a[2] - b} + end + end +end + +function vec2.mul(a, b, dst) + local btype = type(b) + if dst then + if btype == "table" then + dst[1] = a[1] * b[1] + dst[2] = a[2] * b[2] + else + dst[1] = a[1] * b + dst[2] = a[2] * b + end + return dst + else + if btype == "table" then + return {a[1] * b[1], a[2] * b[2]} + else + return {a[1] * b, a[2] * b} + end + end +end + +function vec2.div(a, b, dst) + local btype = type(b) + if dst then + if btype == "table" then + dst[1] = a[1] / b[1] + dst[2] = a[2] / b[2] + else + dst[1] = a[1] / b + dst[2] = a[2] / b + end + return dst + else + if btype == "table" then + return {a[1] / b[1], a[2] / b[2]} + else + return {a[1] / b, a[2] / b} + end + end +end + +function vec2.abs(a, dst) + local x = a[1] + local y = a[2] + if dst then + dst[1] = x < 0.0 and -x or x + dst[2] = y < 0.0 and -y or y + else + return { + x < 0.0 and -x or x, + y < 0.0 and -y or y, + } + end +end + +function vec2.dot(a, b) + return a[1] * b[1] + a[2] * b[2] +end diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 68b9e4f6..9e4f9556 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -1,3 +1,5 @@ +local enable_experimental = core.get_setting("debug.enable-experimental") + ------------------------------------------------ ------ Extended kit of standard functions ------ ------------------------------------------------ @@ -169,6 +171,10 @@ function inventory.set_description(invid, slot, description) inventory.set_data(invid, slot, "description", description) end +if enable_experimental then + require "core:internal/maths_inline" +end + events = require "core:internal/events" function pack.unload(prefix) diff --git a/src/io/settings_io.cpp b/src/io/settings_io.cpp index cdbf567e..56c5f038 100644 --- a/src/io/settings_io.cpp +++ b/src/io/settings_io.cpp @@ -86,6 +86,7 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) { builder.section("debug"); builder.add("generator-test-mode", &settings.debug.generatorTestMode); builder.add("do-write-lights", &settings.debug.doWriteLights); + builder.add("enable-experimental", &settings.debug.enableExperimental); } dv::value SettingsHandler::getValue(const std::string& name) const { diff --git a/src/settings.hpp b/src/settings.hpp index d4c21688..10986f39 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -90,6 +90,8 @@ struct DebugSettings { FlagSetting generatorTestMode {false}; /// @brief Write lights cache FlagSetting doWriteLights {true}; + /// @brief Enable experimental optimizations and features + FlagSetting enableExperimental {false}; }; struct UiSettings { From eba7eed8c0987661b2ef556a98b1ca17becf1afa Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 19 Aug 2025 19:40:16 +0300 Subject: [PATCH 28/28] fix: network events not processing (#591) --- res/scripts/stdlib.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index aea785ac..1ad98cac 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -36,7 +36,6 @@ local function complete_app_lib(app) app.set_setting = core.set_setting app.tick = function() coroutine.yield() - network.__process_events() end app.get_version = core.get_version app.get_setting_info = core.get_setting_info @@ -520,6 +519,8 @@ function __process_post_runnables() for _, name in ipairs(dead) do __vc_named_coroutines[name] = nil end + + network.__process_events() end function time.post_runnable(runnable)