diff --git a/res/project.toml b/res/project.toml new file mode 100644 index 00000000..c30e72dd --- /dev/null +++ b/res/project.toml @@ -0,0 +1,3 @@ +# default project +name = "default" +base_packs = ["base"] diff --git a/src/content/ContentControl.cpp b/src/content/ContentControl.cpp index 37d68294..c0ed582d 100644 --- a/src/content/ContentControl.cpp +++ b/src/content/ContentControl.cpp @@ -8,6 +8,7 @@ #include "ContentLoader.hpp" #include "PacksManager.hpp" #include "objects/rigging.hpp" +#include "devtools/Project.hpp" #include "logic/scripting/scripting.hpp" #include "core_defs.hpp" @@ -16,12 +17,15 @@ static void load_configs(Input& input, const io::path& root) { } ContentControl::ContentControl( - EnginePaths& paths, Input& input, std::function postContent + const Project& project, + EnginePaths& paths, + Input& input, + std::function postContent ) : paths(paths), input(input), postContent(std::move(postContent)), - basePacks(io::read_list("res:config/builtins.list")), + basePacks(project.basePacks), manager(std::make_unique()) { manager->setSources({ "world:content", diff --git a/src/content/ContentControl.hpp b/src/content/ContentControl.hpp index db08fa5a..4c52ec10 100644 --- a/src/content/ContentControl.hpp +++ b/src/content/ContentControl.hpp @@ -11,6 +11,7 @@ class Content; class PacksManager; class EnginePaths; class Input; +struct Project; namespace io { class path; @@ -19,7 +20,10 @@ namespace io { class ContentControl { public: ContentControl( - EnginePaths& paths, Input& input, std::function postContent + const Project& project, + EnginePaths& paths, + Input& input, + std::function postContent ); ~ContentControl(); diff --git a/src/devtools/Project.cpp b/src/devtools/Project.cpp new file mode 100644 index 00000000..c1d382ee --- /dev/null +++ b/src/devtools/Project.cpp @@ -0,0 +1,17 @@ +#include "Project.hpp" + +#include "data/dv_util.hpp" + +dv::value Project::serialize() const { + return dv::object({ + {"name", name}, + {"title", title}, + {"base_packs", dv::to_value(basePacks)}, + }); +} + +void Project::deserialize(const dv::value& src) { + src.at("name").get(name); + src.at("title").get(title); + dv::get(src, "base_packs", basePacks); +} diff --git a/src/devtools/Project.hpp b/src/devtools/Project.hpp new file mode 100644 index 00000000..857b58f1 --- /dev/null +++ b/src/devtools/Project.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#include "interfaces/Serializable.hpp" + +struct Project : Serializable { + std::string name; + std::string title; + std::vector basePacks; + + 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 fb53978a..63db4490 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -13,6 +13,7 @@ #include "coders/toml.hpp" #include "coders/commons.hpp" #include "devtools/Editor.hpp" +#include "devtools/Project.hpp" #include "content/ContentControl.hpp" #include "core_defs.hpp" #include "io/io.hpp" @@ -74,17 +75,24 @@ Engine& Engine::getInstance() { void Engine::initialize(CoreParameters coreParameters) { params = std::move(coreParameters); settingsHandler = std::make_unique(settings); - editor = std::make_unique(*this); - cmd = std::make_unique(); - network = network::Network::create(settings.network); logger.info() << "engine version: " << ENGINE_VERSION_STRING; if (params.headless) { logger.info() << "headless mode is enabled"; } + if (params.projectFolder.empty()) { + params.projectFolder = params.resFolder; + } paths.setResourcesFolder(params.resFolder); paths.setUserFilesFolder(params.userFolder); + paths.setProjectFolder(params.projectFolder); paths.prepare(); + loadProject(); + + editor = std::make_unique(*this); + cmd = std::make_unique(); + network = network::Network::create(settings.network); + if (!params.scriptFile.empty()) { paths.setScriptFolder(params.scriptFile.parent_path()); } @@ -92,9 +100,12 @@ void Engine::initialize(CoreParameters coreParameters) { controller = std::make_unique(*this); if (!params.headless) { - std::string title = "VoxelCore v" + + 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]"; } @@ -135,7 +146,7 @@ void Engine::initialize(CoreParameters coreParameters) { langs::locale_by_envlocale(platform::detect_locale()) ); } - content = std::make_unique(paths, *input, [this]() { + content = std::make_unique(*project, paths, *input, [this]() { editor->loadTools(); langs::setup(langs::get_current(), paths.resPaths.collectRoots()); if (!isHeadless()) { @@ -325,6 +336,13 @@ void Engine::loadAssets() { gui->onAssetsLoad(assets.get()); } +void Engine::loadProject() { + io::path projectFile = "project:project.toml"; + project = std::make_unique(); + project->deserialize(io::read_object(projectFile)); + logger.info() << "loaded project " << util::quote(project->name); +} + void Engine::setScreen(std::shared_ptr screen) { // reset audio channels (stop all sources) audio::reset_channel(audio::get_channel_index("regular")); diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp index 5e854ae3..8f8683da 100644 --- a/src/engine/Engine.hpp +++ b/src/engine/Engine.hpp @@ -20,6 +20,7 @@ class Screen; class ContentControl; class EngineController; class Input; +struct Project; namespace gui { class GUI; @@ -48,6 +49,7 @@ struct CoreParameters { std::filesystem::path resFolder = "res"; std::filesystem::path userFolder = "."; std::filesystem::path scriptFile; + std::filesystem::path projectFolder; }; using OnWorldOpen = std::function, int64_t)>; @@ -57,6 +59,7 @@ class Engine : public util::ObjectsKeeper { EngineSettings settings; EnginePaths paths; + std::unique_ptr project; std::unique_ptr settingsHandler; std::unique_ptr assets; std::shared_ptr screen; @@ -78,6 +81,7 @@ class Engine : public util::ObjectsKeeper { void saveSettings(); void updateHotkeys(); void loadAssets(); + void loadProject(); public: Engine(); ~Engine(); diff --git a/src/io/engine_paths.cpp b/src/io/engine_paths.cpp index bc98e8a9..cb2d11e9 100644 --- a/src/io/engine_paths.cpp +++ b/src/io/engine_paths.cpp @@ -46,6 +46,7 @@ void EnginePaths::prepare() { } logger.info() << "resources folder: " << fs::canonical(resourcesFolder).u8string(); logger.info() << "user files folder: " << fs::canonical(userFilesFolder).u8string(); + logger.info() << "project folder: " << fs::canonical(projectFolder).u8string(); if (!io::is_directory(CONTENT_FOLDER)) { io::create_directories(CONTENT_FOLDER); @@ -142,6 +143,11 @@ void EnginePaths::setScriptFolder(std::filesystem::path folder) { this->scriptFolder = std::move(folder); } +void EnginePaths::setProjectFolder(std::filesystem::path folder) { + io::set_device("project", std::make_shared(folder)); + this->projectFolder = std::move(folder); +} + void EnginePaths::setCurrentWorldFolder(io::path folder) { this->currentWorldFolder = std::move(folder); io::create_subdevice("world", "user", currentWorldFolder); diff --git a/src/io/engine_paths.hpp b/src/io/engine_paths.hpp index 1b6739b8..8b9b7f8e 100644 --- a/src/io/engine_paths.hpp +++ b/src/io/engine_paths.hpp @@ -56,6 +56,8 @@ public: void setScriptFolder(std::filesystem::path folder); + void setProjectFolder(std::filesystem::path folder); + io::path getWorldFolderByName(const std::string& name); io::path getWorldsFolder() const; @@ -80,6 +82,7 @@ public: private: std::filesystem::path userFilesFolder {"."}; std::filesystem::path resourcesFolder {"res"}; + std::filesystem::path projectFolder = resourcesFolder; io::path currentWorldFolder; std::optional scriptFolder; std::vector entryPoints; diff --git a/src/util/command_line.cpp b/src/util/command_line.cpp index ee0ace92..b6ef091b 100644 --- a/src/util/command_line.cpp +++ b/src/util/command_line.cpp @@ -12,18 +12,19 @@ static bool perform_keyword( util::ArgsReader& reader, const std::string& keyword, CoreParameters& params ) { if (keyword == "--res") { - auto token = reader.next(); - params.resFolder = token; + params.resFolder = reader.next(); } else if (keyword == "--dir") { - auto token = reader.next(); - params.userFolder = token; + params.userFolder = reader.next(); + } else if (keyword == "--project") { + params.projectFolder = reader.next(); } else if (keyword == "--help" || keyword == "-h") { std::cout << "VoxelCore v" << ENGINE_VERSION_STRING << "\n\n"; std::cout << "command-line arguments:\n"; - std::cout << " --help - show help\n"; - std::cout << " --version - print engine version\n"; + std::cout << " --help - display this help\n"; + std::cout << " --version - display engine version\n"; std::cout << " --res - set resources directory\n"; std::cout << " --dir - set userfiles directory\n"; + std::cout << " --project - set project directory\n"; std::cout << " --headless - run in headless mode\n"; std::cout << " --test - test script file\n"; std::cout << " --script - main script file\n";