From 136c35591c8fbe611d574b149cbbf1640c4092bd Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 5 Jan 2025 22:44:32 +0300 Subject: [PATCH] add file.name(), file.stem() & add 'path' to pack info & add start_coroutine() --- doc/en/scripting/builtins/libfile.md | 12 +++++ doc/en/scripting/builtins/libpack.md | 1 + doc/ru/scripting/builtins/libfile.md | 12 +++++ doc/ru/scripting/builtins/libpack.md | 1 + res/layouts/pages/new_world.xml.lua | 2 +- res/scripts/stdlib.lua | 62 ++++++++++++++++-------- res/scripts/stdmin.lua | 13 +++++ src/content/ContentPack.cpp | 17 ++++--- src/content/ContentPack.hpp | 51 ++++++++++--------- src/content/PacksManager.cpp | 6 +-- src/content/PacksManager.hpp | 4 +- src/engine/Engine.cpp | 17 ++++--- src/logic/scripting/lua/libs/libpack.cpp | 5 +- src/logic/scripting/lua/lua_engine.cpp | 14 +++--- 14 files changed, 147 insertions(+), 70 deletions(-) diff --git a/doc/en/scripting/builtins/libfile.md b/doc/en/scripting/builtins/libfile.md index 1e683b65..9d9532d8 100644 --- a/doc/en/scripting/builtins/libfile.md +++ b/doc/en/scripting/builtins/libfile.md @@ -120,3 +120,15 @@ file.read_combined_object(path: str) -> array ``` Combines objects from JSON files of different packs. + +```lua +file.name(path: str) --> str +``` + +Extracts the file name from the path. + +``lua +file.stem(path: str) --> str +``` + +Extracts the file name from the path, removing the extension. diff --git a/doc/en/scripting/builtins/libpack.md b/doc/en/scripting/builtins/libpack.md index e643e95e..35b11088 100644 --- a/doc/en/scripting/builtins/libpack.md +++ b/doc/en/scripting/builtins/libpack.md @@ -83,6 +83,7 @@ pack.get_info(packid: str) -> { creator: str, description: str, version: str, + path: str, icon: str, -- not available in headless mode dependencies: optional strings array } diff --git a/doc/ru/scripting/builtins/libfile.md b/doc/ru/scripting/builtins/libfile.md index d7ed7792..3a93e20d 100644 --- a/doc/ru/scripting/builtins/libfile.md +++ b/doc/ru/scripting/builtins/libfile.md @@ -120,3 +120,15 @@ file.read_combined_object(путь: str) -> массив ``` Совмещает объекты из JSON файлов разных паков. + +```lua +file.name(путь: str) --> str +``` + +Извлекает имя файла из пути. + +```lua +file.stem(путь: str) --> str +``` + +Извлекает имя файла из пути, удаляя расширение. diff --git a/doc/ru/scripting/builtins/libpack.md b/doc/ru/scripting/builtins/libpack.md index a3f174ff..7b3a03c1 100644 --- a/doc/ru/scripting/builtins/libpack.md +++ b/doc/ru/scripting/builtins/libpack.md @@ -70,6 +70,7 @@ pack.get_info(packid: str) -> { creator: str, description: str, version: str, + path: str, icon: str, -- отсутствует в headless режиме dependencies: опциональный массив строк } diff --git a/res/layouts/pages/new_world.xml.lua b/res/layouts/pages/new_world.xml.lua index 25e6f424..341827b1 100644 --- a/res/layouts/pages/new_world.xml.lua +++ b/res/layouts/pages/new_world.xml.lua @@ -1,6 +1,6 @@ settings = session.get_entry('new_world') -function world_name_validator(name) +local function world_name_validator(name) return name:match("^[%w-\\.\\ ]+$") ~= nil and not world.exists(name) end diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 4b5761ae..b0c614ad 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -163,7 +163,7 @@ function Document.new(docname) end local _RadioGroup = {} -function _RadioGroup.set(self, key) +function _RadioGroup:set(key) if type(self) ~= 'table' then error("called as non-OOP via '.', use radiogroup:set") end @@ -176,7 +176,7 @@ function _RadioGroup.set(self, key) self.callback(key) end end -function _RadioGroup.__call(self, elements, onset, default) +function _RadioGroup:__call(elements, onset, default) local group = setmetatable({ elements=elements, callback=onset, @@ -192,24 +192,6 @@ _GUI_ROOT = Document.new("core:root") _MENU = _GUI_ROOT.menu menu = _MENU -local __post_runnables = {} - -function __process_post_runnables() - if #__post_runnables then - for _, func in ipairs(__post_runnables) do - local status, result = xpcall(func, __vc__error) - if not status then - debug.error("error in post_runnable: "..result) - end - end - __post_runnables = {} - end -end - -function time.post_runnable(runnable) - table.insert(__post_runnables, runnable) -end - --- Console library extension --- console.cheats = {} @@ -409,6 +391,7 @@ function __vc_on_world_quit() end local __vc_coroutines = {} +local __vc_named_coroutines = {} local __vc_next_coroutine = 1 local __vc_coroutine_error = nil @@ -447,6 +430,45 @@ function __vc_stop_coroutine(id) end end +function start_coroutine(chunk, name) + local co = coroutine.create(function() + local status, error = xpcall(chunk, __vc__error) + if not status then + debug.error(error) + end + end) + __vc_named_coroutines[name] = co +end + +local __post_runnables = {} + +function __process_post_runnables() + if #__post_runnables then + for _, func in ipairs(__post_runnables) do + local status, result = xpcall(func, __vc__error) + if not status then + debug.error("error in post_runnable: "..result) + end + end + __post_runnables = {} + end + + local dead = {} + for name, co in pairs(__vc_named_coroutines) do + coroutine.resume(co) + if coroutine.status(co) == "dead" then + table.insert(dead, name) + end + end + for _, name in ipairs(dead) do + __vc_named_coroutines[name] = nil + end +end + +function time.post_runnable(runnable) + table.insert(__post_runnables, runnable) +end + assets = {} assets.load_texture = core.__load_texture diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 5702d7ef..769b74a3 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -361,3 +361,16 @@ function __vc_warning(msg, detail, n) "core:warning", msg, detail, debug.get_traceback(1 + (n or 0))) end end + +function file.name(path) + return path:match("([^:/\\]+)$") +end + +function file.stem(path) + local name = file.name(path) + return name:match("(.+)%.[^%.]+$") or name +end + +function file.ext(path) + return path:match("%.([^:/\\]+)$") +end diff --git a/src/content/ContentPack.cpp b/src/content/ContentPack.cpp index 72d4540f..7135f231 100644 --- a/src/content/ContentPack.cpp +++ b/src/content/ContentPack.cpp @@ -15,7 +15,7 @@ namespace fs = std::filesystem; ContentPack ContentPack::createCore(const EnginePaths& paths) { return ContentPack { - "core", "Core", ENGINE_VERSION_STRING, "", "", paths.getResourcesFolder(), {} + "core", "Core", ENGINE_VERSION_STRING, "", "", paths.getResourcesFolder(), "res:", {} }; } @@ -70,7 +70,7 @@ static void checkContentPackId(const std::string& id, const fs::path& folder) { } } -ContentPack ContentPack::read(const fs::path& folder) { +ContentPack ContentPack::read(const std::string& path, const fs::path& folder) { auto root = files::read_json(folder / fs::path(PACKAGE_FILENAME)); ContentPack pack; root.at("id").get(pack.id); @@ -90,6 +90,7 @@ ContentPack ContentPack::read(const fs::path& folder) { root.at("description").get(pack.description); root.at("source").get(pack.source); pack.folder = folder; + pack.path = path; if (auto found = root.at("dependencies")) { const auto& dependencies = *found; @@ -123,17 +124,19 @@ ContentPack ContentPack::read(const fs::path& folder) { } void ContentPack::scanFolder( - const fs::path& folder, std::vector& packs + const std::string& path, const fs::path& folder, std::vector& packs ) { if (!fs::is_directory(folder)) { return; } for (const auto& entry : fs::directory_iterator(folder)) { - const fs::path& folder = entry.path(); - if (!fs::is_directory(folder)) continue; - if (!is_pack(folder)) continue; + const fs::path& packFolder = entry.path(); + if (!fs::is_directory(packFolder)) continue; + if (!is_pack(packFolder)) continue; try { - packs.push_back(read(folder)); + packs.push_back( + read(path + "/" + packFolder.filename().string(), packFolder) + ); } catch (const contentpack_error& err) { std::cerr << "package.json error at " << err.getFolder().u8string(); std::cerr << ": " << err.what() << std::endl; diff --git a/src/content/ContentPack.hpp b/src/content/ContentPack.hpp index e5d69447..c8e0de9b 100644 --- a/src/content/ContentPack.hpp +++ b/src/content/ContentPack.hpp @@ -10,18 +10,18 @@ class EnginePaths; -namespace fs = std::filesystem; - class contentpack_error : public std::runtime_error { std::string packId; - fs::path folder; + std::filesystem::path folder; public: contentpack_error( - std::string packId, fs::path folder, const std::string& message + std::string packId, + std::filesystem::path folder, + const std::string& message ); std::string getPackId() const; - fs::path getFolder() const; + std::filesystem::path getFolder() const; }; enum class DependencyLevel { @@ -42,45 +42,52 @@ struct ContentPack { std::string version = "0.0"; std::string creator = ""; std::string description = "no description"; - fs::path folder; + std::filesystem::path folder; + std::string path; std::vector dependencies; std::string source = ""; - fs::path getContentFile() const; + std::filesystem::path getContentFile() const; static inline const std::string PACKAGE_FILENAME = "package.json"; static inline const std::string CONTENT_FILENAME = "content.json"; - static inline const fs::path BLOCKS_FOLDER = "blocks"; - static inline const fs::path ITEMS_FOLDER = "items"; - static inline const fs::path ENTITIES_FOLDER = "entities"; - static inline const fs::path GENERATORS_FOLDER = "generators"; + static inline const std::filesystem::path BLOCKS_FOLDER = "blocks"; + static inline const std::filesystem::path ITEMS_FOLDER = "items"; + static inline const std::filesystem::path ENTITIES_FOLDER = "entities"; + static inline const std::filesystem::path GENERATORS_FOLDER = "generators"; static const std::vector RESERVED_NAMES; - static bool is_pack(const fs::path& folder); - static ContentPack read(const fs::path& folder); - - static void scanFolder( - const fs::path& folder, std::vector& packs + static bool is_pack(const std::filesystem::path& folder); + static ContentPack read( + const std::string& path, const std::filesystem::path& folder ); - static std::vector worldPacksList(const fs::path& folder); + static void scanFolder( + const std::string& path, + const std::filesystem::path& folder, + std::vector& packs + ); - static fs::path findPack( + static std::vector worldPacksList( + const std::filesystem::path& folder + ); + + static std::filesystem::path findPack( const EnginePaths* paths, - const fs::path& worldDir, + const std::filesystem::path& worldDir, const std::string& name ); static ContentPack createCore(const EnginePaths&); - static inline fs::path getFolderFor(ContentType type) { + static inline std::filesystem::path getFolderFor(ContentType type) { switch (type) { case ContentType::BLOCK: return ContentPack::BLOCKS_FOLDER; case ContentType::ITEM: return ContentPack::ITEMS_FOLDER; case ContentType::ENTITY: return ContentPack::ENTITIES_FOLDER; case ContentType::GENERATOR: return ContentPack::GENERATORS_FOLDER; - case ContentType::NONE: return fs::u8path(""); - default: return fs::u8path(""); + case ContentType::NONE: return std::filesystem::u8path(""); + default: return std::filesystem::u8path(""); } } }; diff --git a/src/content/PacksManager.cpp b/src/content/PacksManager.cpp index 04eeb241..fdf39bb3 100644 --- a/src/content/PacksManager.cpp +++ b/src/content/PacksManager.cpp @@ -7,7 +7,7 @@ PacksManager::PacksManager() = default; -void PacksManager::setSources(std::vector sources) { +void PacksManager::setSources(std::vector> sources) { this->sources = std::move(sources); } @@ -15,8 +15,8 @@ void PacksManager::scan() { packs.clear(); std::vector packsList; - for (auto& folder : sources) { - ContentPack::scanFolder(folder, packsList); + for (auto& [path, folder] : sources) { + ContentPack::scanFolder(path, folder, packsList); for (auto& pack : packsList) { packs.try_emplace(pack.id, pack); } diff --git a/src/content/PacksManager.hpp b/src/content/PacksManager.hpp index 6d77e471..b60b06ce 100644 --- a/src/content/PacksManager.hpp +++ b/src/content/PacksManager.hpp @@ -10,12 +10,12 @@ namespace fs = std::filesystem; class PacksManager { std::unordered_map packs; - std::vector sources; + std::vector> sources; public: PacksManager(); /// @brief Set content packs sources (search folders) - void setSources(std::vector sources); + void setSources(std::vector> sources); /// @brief Scan sources and collect all found packs excluding duplication. /// Scanning order depends on sources order diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index c209569b..fdcf2e0a 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -262,9 +262,9 @@ cmd::CommandsInterpreter* Engine::getCommandsInterpreter() { PacksManager Engine::createPacksManager(const fs::path& worldFolder) { PacksManager manager; manager.setSources({ - worldFolder/fs::path("content"), - paths.getUserFilesFolder()/fs::path("content"), - paths.getResourcesFolder()/fs::path("content") + {"world:content", worldFolder.empty() ? worldFolder : worldFolder/fs::path("content")}, + {"user:content", paths.getUserFilesFolder()/fs::path("content")}, + {"res:content", paths.getResourcesFolder()/fs::path("content")} }); return manager; } @@ -413,11 +413,12 @@ void Engine::loadWorldContent(const fs::path& folder) { contentPacks.clear(); auto packNames = ContentPack::worldPacksList(folder); PacksManager manager; - manager.setSources({ - folder/fs::path("content"), - paths.getUserFilesFolder()/fs::path("content"), - paths.getResourcesFolder()/fs::path("content") - }); + manager.setSources( + {{"world:content", + folder.empty() ? folder : folder / fs::path("content")}, + {"user:content", paths.getUserFilesFolder() / fs::path("content")}, + {"res:content", paths.getResourcesFolder() / fs::path("content")}} + ); manager.scan(); contentPacks = manager.getAll(manager.assemble(packNames)); paths.setCurrentWorldFolder(folder); diff --git a/src/logic/scripting/lua/libs/libpack.cpp b/src/logic/scripting/lua/libs/libpack.cpp index 3b56f96d..95bb9e99 100644 --- a/src/logic/scripting/lua/libs/libpack.cpp +++ b/src/logic/scripting/lua/libs/libpack.cpp @@ -64,7 +64,7 @@ static int l_pack_get_available(lua::State* L) { static int l_pack_get_info( lua::State* L, const ContentPack& pack, const Content* content ) { - lua::createtable(L, 0, 5); + lua::createtable(L, 0, 6); lua::pushstring(L, pack.id); lua::setfield(L, "id"); @@ -81,6 +81,9 @@ static int l_pack_get_info( lua::pushstring(L, pack.version); lua::setfield(L, "version"); + lua::pushstring(L, pack.path); + lua::setfield(L, "path"); + if (!engine->isHeadless()) { auto assets = engine->getAssets(); std::string icon = pack.id + ".icon"; diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index c6cdfc25..39cb1b8f 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -44,7 +44,6 @@ static void create_libs(State* L, StateType stateType) { openlib(L, "bjson", bjsonlib); openlib(L, "block", blocklib); openlib(L, "byteutil", byteutillib); - openlib(L, "core", corelib); openlib(L, "file", filelib); openlib(L, "generation", generationlib); openlib(L, "item", itemlib); @@ -52,7 +51,6 @@ static void create_libs(State* L, StateType stateType) { openlib(L, "mat4", mat4lib); openlib(L, "pack", packlib); openlib(L, "quat", quatlib); - openlib(L, "time", timelib); openlib(L, "toml", tomllib); openlib(L, "utf8", utf8lib); openlib(L, "vec2", vec2lib); @@ -61,16 +59,20 @@ static void create_libs(State* L, StateType stateType) { 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, "audio", audiolib); + openlib(L, "console", consolelib); + openlib(L, "core", corelib); openlib(L, "gui", guilib); openlib(L, "input", inputlib); openlib(L, "inventory", inventorylib); - openlib(L, "world", worldlib); - openlib(L, "audio", audiolib); - openlib(L, "console", consolelib); - openlib(L, "player", playerlib); openlib(L, "network", networklib); + openlib(L, "player", playerlib); + openlib(L, "time", timelib); + openlib(L, "world", worldlib); openlib(L, "entities", entitylib); openlib(L, "cameras", cameralib);