refactor: add ContentControl class

This commit is contained in:
MihailRis 2025-03-21 09:10:31 +03:00
parent 7262119f5b
commit 331734792d
21 changed files with 324 additions and 246 deletions

View File

@ -25,7 +25,7 @@ namespace fs = std::filesystem;
static debug::Logger logger("assets-loader"); static debug::Logger logger("assets-loader");
AssetsLoader::AssetsLoader(Engine& engine, Assets& assets, const ResPaths* paths) AssetsLoader::AssetsLoader(Engine& engine, Assets& assets, const ResPaths& paths)
: engine(engine), assets(assets), paths(paths) { : engine(engine), assets(assets), paths(paths) {
addLoader(AssetType::SHADER, assetload::shader); addLoader(AssetType::SHADER, assetload::shader);
addLoader(AssetType::TEXTURE, assetload::texture); addLoader(AssetType::TEXTURE, assetload::texture);
@ -200,7 +200,7 @@ void AssetsLoader::processPreloadConfig(const io::path& file) {
} }
void AssetsLoader::processPreloadConfigs(const Content* content) { void AssetsLoader::processPreloadConfigs(const Content* content) {
auto preloadFile = paths->getMainRoot() / "preload.json"; io::path preloadFile = "res:preload.json";
if (io::exists(preloadFile)) { if (io::exists(preloadFile)) {
processPreloadConfig(preloadFile); processPreloadConfig(preloadFile);
} }
@ -212,7 +212,7 @@ void AssetsLoader::processPreloadConfigs(const Content* content) {
continue; continue;
} }
const auto& pack = entry.second; const auto& pack = entry.second;
auto preloadFile = pack->getInfo().folder / "preload.json"; preloadFile = pack->getInfo().folder / "preload.json";
if (io::exists(preloadFile)) { if (io::exists(preloadFile)) {
processPreloadConfig(preloadFile); processPreloadConfig(preloadFile);
} }
@ -301,7 +301,7 @@ Engine& AssetsLoader::getEngine() {
return engine; return engine;
} }
const ResPaths* AssetsLoader::getPaths() const { const ResPaths& AssetsLoader::getPaths() const {
return paths; return paths;
} }

View File

@ -57,7 +57,7 @@ struct AtlasCfg : AssetCfg {
using aloader_func = std::function< using aloader_func = std::function<
assetload:: assetload::
postfunc(AssetsLoader*, const ResPaths*, const std::string&, const std::string&, std::shared_ptr<AssetCfg>)>; postfunc(AssetsLoader*, const ResPaths&, const std::string&, const std::string&, std::shared_ptr<AssetCfg>)>;
struct aloader_entry { struct aloader_entry {
AssetType tag; AssetType tag;
@ -72,7 +72,7 @@ class AssetsLoader {
std::map<AssetType, aloader_func> loaders; std::map<AssetType, aloader_func> loaders;
std::queue<aloader_entry> entries; std::queue<aloader_entry> entries;
std::set<std::pair<AssetType, std::string>> enqueued; std::set<std::pair<AssetType, std::string>> enqueued;
const ResPaths* paths; const ResPaths& paths;
void tryAddSound(const std::string& name); void tryAddSound(const std::string& name);
@ -83,7 +83,7 @@ class AssetsLoader {
void processPreloadConfig(const io::path& file); void processPreloadConfig(const io::path& file);
void processPreloadConfigs(const Content* content); void processPreloadConfigs(const Content* content);
public: public:
AssetsLoader(Engine& engine, Assets& assets, const ResPaths* paths); AssetsLoader(Engine& engine, Assets& assets, const ResPaths& paths);
void addLoader(AssetType tag, aloader_func func); void addLoader(AssetType tag, aloader_func func);
/// @brief Enqueue asset load /// @brief Enqueue asset load
@ -105,7 +105,7 @@ public:
std::shared_ptr<Task> startTask(runnable onDone); std::shared_ptr<Task> startTask(runnable onDone);
const ResPaths* getPaths() const; const ResPaths& getPaths() const;
aloader_func getLoader(AssetType tag); aloader_func getLoader(AssetType tag);
/// @brief Enqueue core and content assets /// @brief Enqueue core and content assets

View File

@ -34,7 +34,7 @@ namespace fs = std::filesystem;
static bool load_animation( static bool load_animation(
Assets* assets, Assets* assets,
const ResPaths* paths, const ResPaths& paths,
const std::string& atlasName, const std::string& atlasName,
const std::string& directory, const std::string& directory,
const std::string& name, const std::string& name,
@ -43,14 +43,14 @@ static bool load_animation(
assetload::postfunc assetload::texture( assetload::postfunc assetload::texture(
AssetsLoader*, AssetsLoader*,
const ResPaths* paths, const ResPaths& paths,
const std::string& filename, const std::string& filename,
const std::string& name, const std::string& name,
const std::shared_ptr<AssetCfg>& const std::shared_ptr<AssetCfg>&
) { ) {
auto actualFile = paths->find(filename + ".png"); auto actualFile = paths.find(filename + ".png");
try { try {
std::shared_ptr<ImageData> image(imageio::read(actualFile).release()); std::shared_ptr<ImageData> image(imageio::read(actualFile));
return [name, image, actualFile](auto assets) { return [name, image, actualFile](auto assets) {
assets->store(Texture::from(image.get()), name); assets->store(Texture::from(image.get()), name);
}; };
@ -62,13 +62,13 @@ assetload::postfunc assetload::texture(
assetload::postfunc assetload::shader( assetload::postfunc assetload::shader(
AssetsLoader*, AssetsLoader*,
const ResPaths* paths, const ResPaths& paths,
const std::string& filename, const std::string& filename,
const std::string& name, const std::string& name,
const std::shared_ptr<AssetCfg>& const std::shared_ptr<AssetCfg>&
) { ) {
io::path vertexFile = paths->find(filename + ".glslv"); io::path vertexFile = paths.find(filename + ".glslv");
io::path fragmentFile = paths->find(filename + ".glslf"); io::path fragmentFile = paths.find(filename + ".glslf");
std::string vertexSource = io::read_string(vertexFile); std::string vertexSource = io::read_string(vertexFile);
std::string fragmentSource = io::read_string(fragmentFile); std::string fragmentSource = io::read_string(fragmentFile);
@ -104,14 +104,14 @@ static bool append_atlas(AtlasBuilder& atlas, const io::path& file) {
assetload::postfunc assetload::atlas( assetload::postfunc assetload::atlas(
AssetsLoader* loader, AssetsLoader* loader,
const ResPaths* paths, const ResPaths& paths,
const std::string& directory, const std::string& directory,
const std::string& name, const std::string& name,
const std::shared_ptr<AssetCfg>& config const std::shared_ptr<AssetCfg>& config
) { ) {
auto atlasConfig = std::dynamic_pointer_cast<AtlasCfg>(config); auto atlasConfig = std::dynamic_pointer_cast<AtlasCfg>(config);
if (atlasConfig && atlasConfig->type == AtlasType::SEPARATE) { if (atlasConfig && atlasConfig->type == AtlasType::SEPARATE) {
for (const auto& file : paths->listdir(directory)) { for (const auto& file : paths.listdir(directory)) {
if (!imageio::is_read_supported(file.extension())) if (!imageio::is_read_supported(file.extension()))
continue; continue;
loader->add( loader->add(
@ -123,7 +123,7 @@ assetload::postfunc assetload::atlas(
return [](auto){}; return [](auto){};
} }
AtlasBuilder builder; AtlasBuilder builder;
for (const auto& file : paths->listdir(directory)) { for (const auto& file : paths.listdir(directory)) {
if (!imageio::is_read_supported(file.extension())) continue; if (!imageio::is_read_supported(file.extension())) continue;
if (!append_atlas(builder, file)) continue; if (!append_atlas(builder, file)) continue;
} }
@ -140,7 +140,7 @@ assetload::postfunc assetload::atlas(
assetload::postfunc assetload::font( assetload::postfunc assetload::font(
AssetsLoader*, AssetsLoader*,
const ResPaths* paths, const ResPaths& paths,
const std::string& filename, const std::string& filename,
const std::string& name, const std::string& name,
const std::shared_ptr<AssetCfg>& const std::shared_ptr<AssetCfg>&
@ -148,7 +148,7 @@ assetload::postfunc assetload::font(
auto pages = std::make_shared<std::vector<std::unique_ptr<ImageData>>>(); auto pages = std::make_shared<std::vector<std::unique_ptr<ImageData>>>();
for (size_t i = 0; i <= 1024; i++) { for (size_t i = 0; i <= 1024; i++) {
std::string pagefile = filename + "_" + std::to_string(i) + ".png"; std::string pagefile = filename + "_" + std::to_string(i) + ".png";
auto file = paths->find(pagefile); auto file = paths.find(pagefile);
if (io::exists(file)) { if (io::exists(file)) {
pages->push_back(imageio::read(file)); pages->push_back(imageio::read(file));
} else if (i == 0) { } else if (i == 0) {
@ -177,7 +177,7 @@ assetload::postfunc assetload::font(
assetload::postfunc assetload::layout( assetload::postfunc assetload::layout(
AssetsLoader*, AssetsLoader*,
const ResPaths* paths, const ResPaths&,
const std::string& file, const std::string& file,
const std::string& name, const std::string& name,
const std::shared_ptr<AssetCfg>& config const std::shared_ptr<AssetCfg>& config
@ -206,7 +206,7 @@ assetload::postfunc assetload::layout(
} }
assetload::postfunc assetload::sound( assetload::postfunc assetload::sound(
AssetsLoader*, AssetsLoader*,
const ResPaths* paths, const ResPaths& paths,
const std::string& file, const std::string& file,
const std::string& name, const std::string& name,
const std::shared_ptr<AssetCfg>& config const std::shared_ptr<AssetCfg>& config
@ -220,13 +220,13 @@ assetload::postfunc assetload::sound(
for (size_t i = 0; i < extensions.size(); i++) { for (size_t i = 0; i < extensions.size(); i++) {
extension = extensions[i]; extension = extensions[i];
// looking for 'sound_name' as base sound // looking for 'sound_name' as base sound
auto soundFile = paths->find(file + extension); auto soundFile = paths.find(file + extension);
if (io::exists(soundFile)) { if (io::exists(soundFile)) {
baseSound = audio::load_sound(soundFile, keepPCM); baseSound = audio::load_sound(soundFile, keepPCM);
break; break;
} }
// looking for 'sound_name_0' as base sound // looking for 'sound_name_0' as base sound
auto variantFile = paths->find(file + "_0" + extension); auto variantFile = paths.find(file + "_0" + extension);
if (io::exists(variantFile)) { if (io::exists(variantFile)) {
baseSound = audio::load_sound(variantFile, keepPCM); baseSound = audio::load_sound(variantFile, keepPCM);
break; break;
@ -239,7 +239,7 @@ assetload::postfunc assetload::sound(
// loading sound variants // loading sound variants
for (uint i = 1;; i++) { for (uint i = 1;; i++) {
auto variantFile = auto variantFile =
paths->find(file + "_" + std::to_string(i) + extension); paths.find(file + "_" + std::to_string(i) + extension);
if (!io::exists(variantFile)) { if (!io::exists(variantFile)) {
break; break;
} }
@ -265,12 +265,12 @@ static void request_textures(AssetsLoader* loader, const model::Model& model) {
assetload::postfunc assetload::model( assetload::postfunc assetload::model(
AssetsLoader* loader, AssetsLoader* loader,
const ResPaths* paths, const ResPaths& paths,
const std::string& file, const std::string& file,
const std::string& name, const std::string& name,
const std::shared_ptr<AssetCfg>& const std::shared_ptr<AssetCfg>&
) { ) {
auto path = paths->find(file + ".vec3"); auto path = paths.find(file + ".vec3");
if (io::exists(path)) { if (io::exists(path)) {
auto bytes = io::read_bytes_buffer(path); auto bytes = io::read_bytes_buffer(path);
auto modelVEC3 = std::make_shared<vec3::File>(vec3::load(path.string(), bytes)); auto modelVEC3 = std::make_shared<vec3::File>(vec3::load(path.string(), bytes));
@ -290,7 +290,7 @@ assetload::postfunc assetload::model(
} }
}; };
} }
path = paths->find(file + ".obj"); path = paths.find(file + ".obj");
auto text = io::read_string(path); auto text = io::read_string(path);
try { try {
auto model = obj::parse(path.string(), text).release(); auto model = obj::parse(path.string(), text).release();
@ -384,7 +384,7 @@ inline bool contains(
static bool load_animation( static bool load_animation(
Assets* assets, Assets* assets,
const ResPaths* paths, const ResPaths& paths,
const std::string& atlasName, const std::string& atlasName,
const std::string& directory, const std::string& directory,
const std::string& name, const std::string& name,
@ -392,20 +392,20 @@ static bool load_animation(
) { ) {
std::string animsDir = directory + "/animation"; std::string animsDir = directory + "/animation";
for (const auto& folder : paths->listdir(animsDir)) { for (const auto& folder : paths.listdir(animsDir)) {
if (!io::is_directory(folder)) continue; if (!io::is_directory(folder)) continue;
if (folder.name() != name) continue; if (folder.name() != name) continue;
//FIXME: if (fs::is_empty(folder)) continue; //FIXME: if (fs::is_empty(folder)) continue;
AtlasBuilder builder; AtlasBuilder builder;
append_atlas(builder, paths->find(directory + "/" + name + ".png")); append_atlas(builder, paths.find(directory + "/" + name + ".png"));
std::vector<std::pair<std::string, int>> frameList; std::vector<std::pair<std::string, int>> frameList;
std::string animFile = folder.string() + "/animation.json"; std::string animFile = folder.string() + "/animation.json";
if (io::exists(animFile)) { if (io::exists(animFile)) {
read_anim_file(animFile, frameList); read_anim_file(animFile, frameList);
} }
for (const auto& file : paths->listdir(animsDir + "/" + name)) { for (const auto& file : paths.listdir(animsDir + "/" + name)) {
if (!frameList.empty() && if (!frameList.empty() &&
!contains(frameList, file.stem())) { !contains(frameList, file.stem())) {
continue; continue;

View File

@ -15,49 +15,49 @@ struct AssetCfg;
namespace assetload { namespace assetload {
postfunc texture( postfunc texture(
AssetsLoader*, AssetsLoader*,
const ResPaths* paths, const ResPaths& paths,
const std::string& filename, const std::string& filename,
const std::string& name, const std::string& name,
const std::shared_ptr<AssetCfg>& settings const std::shared_ptr<AssetCfg>& settings
); );
postfunc shader( postfunc shader(
AssetsLoader*, AssetsLoader*,
const ResPaths* paths, const ResPaths& paths,
const std::string& filename, const std::string& filename,
const std::string& name, const std::string& name,
const std::shared_ptr<AssetCfg>& settings const std::shared_ptr<AssetCfg>& settings
); );
postfunc atlas( postfunc atlas(
AssetsLoader*, AssetsLoader*,
const ResPaths* paths, const ResPaths& paths,
const std::string& directory, const std::string& directory,
const std::string& name, const std::string& name,
const std::shared_ptr<AssetCfg>& settings const std::shared_ptr<AssetCfg>& settings
); );
postfunc font( postfunc font(
AssetsLoader*, AssetsLoader*,
const ResPaths* paths, const ResPaths& paths,
const std::string& filename, const std::string& filename,
const std::string& name, const std::string& name,
const std::shared_ptr<AssetCfg>& settings const std::shared_ptr<AssetCfg>& settings
); );
postfunc layout( postfunc layout(
AssetsLoader*, AssetsLoader*,
const ResPaths* paths, const ResPaths& paths,
const std::string& file, const std::string& file,
const std::string& name, const std::string& name,
const std::shared_ptr<AssetCfg>& settings const std::shared_ptr<AssetCfg>& settings
); );
postfunc sound( postfunc sound(
AssetsLoader*, AssetsLoader*,
const ResPaths* paths, const ResPaths& paths,
const std::string& file, const std::string& file,
const std::string& name, const std::string& name,
const std::shared_ptr<AssetCfg>& settings const std::shared_ptr<AssetCfg>& settings
); );
postfunc model( postfunc model(
AssetsLoader*, AssetsLoader*,
const ResPaths* paths, const ResPaths& paths,
const std::string& file, const std::string& file,
const std::string& name, const std::string& name,
const std::shared_ptr<AssetCfg>& settings const std::shared_ptr<AssetCfg>& settings

View File

@ -0,0 +1,140 @@
#include "ContentControl.hpp"
#include "io/io.hpp"
#include "io/engine_paths.hpp"
#include "coders/toml.hpp"
#include "Content.hpp"
#include "ContentPack.hpp"
#include "ContentBuilder.hpp"
#include "ContentLoader.hpp"
#include "PacksManager.hpp"
#include "objects/rigging.hpp"
#include "logic/scripting/scripting.hpp"
#include "window/input.hpp"
#include "core_defs.hpp"
static void load_configs(Input& input, const io::path& root) {
auto configFolder = root / "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
);
}
}
ContentControl::ContentControl(std::function<void()> postContent)
: postContent(std::move(postContent)) {
basePacks = io::read_list("res:config/builtins.list");
}
ContentControl::~ContentControl() = default;
Content* ContentControl::get() {
return content.get();
}
std::vector<std::string>& ContentControl::getBasePacks() {
return basePacks;
}
void ContentControl::resetContent(
EnginePaths& paths, Input& input, std::vector<ContentPack>& contentPacks
) {
scripting::cleanup();
std::vector<PathsRoot> resRoots;
{
auto pack = ContentPack::createCore(paths);
resRoots.push_back({"core", pack.folder});
load_configs(input, pack.folder);
}
PacksManager manager;
manager.setSources({
"user:content",
"res:content",
});
manager.scan();
for (const auto& pack : manager.getAll(basePacks)) {
resRoots.push_back({pack.id, pack.folder});
}
paths.resPaths = ResPaths(resRoots);
content.reset();
contentPacks.clear();
contentPacks = manager.getAll(basePacks);
postContent();
}
void ContentControl::loadContent(
EnginePaths& paths,
Input& input,
std::vector<ContentPack>& packs,
const std::vector<std::string>& names
) {
PacksManager manager;
manager.setSources(getDefaultSources());
manager.scan();
packs = manager.getAll(manager.assemble(names));
loadContent(paths, input, packs);
}
void ContentControl::loadContent(
EnginePaths& paths,
Input& input,
std::vector<ContentPack>& contentPacks
) {
scripting::cleanup();
std::vector<std::string> names;
for (auto& pack : contentPacks) {
names.push_back(pack.id);
}
PacksManager manager;
manager.setSources(getDefaultSources());
manager.scan();
names = manager.assemble(names);
contentPacks = manager.getAll(names);
std::vector<PathsRoot> entryPoints;
for (auto& pack : contentPacks) {
entryPoints.emplace_back(pack.id, pack.folder);
}
paths.setEntryPoints(std::move(entryPoints));
ContentBuilder contentBuilder;
corecontent::setup(input, contentBuilder);
auto corePack = ContentPack::createCore(paths);
auto allPacks = contentPacks;
allPacks.insert(allPacks.begin(), corePack);
// Setup filesystem entry points
std::vector<PathsRoot> resRoots;
for (auto& pack : allPacks) {
resRoots.push_back({pack.id, pack.folder});
}
paths.resPaths = ResPaths(resRoots);
// Load content
for (auto& pack : allPacks) {
ContentLoader(&pack, contentBuilder, paths.resPaths).load();
load_configs(input, pack.folder);
}
content = contentBuilder.build();
scripting::on_content_load(content.get());
ContentLoader::loadScripts(*content);
postContent();
}
std::vector<io::path> ContentControl::getDefaultSources() {
return {
"world:content",
"user:content",
"res:content",
};
}

View File

@ -0,0 +1,48 @@
#pragma once
#include <memory>
#include <vector>
#include <string>
#include <functional>
class Content;
struct ContentPack;
class EnginePaths;
class Input;
namespace io {
class path;
}
class ContentControl {
public:
ContentControl(std::function<void()> postContent);
~ContentControl();
Content* get();
std::vector<std::string>& getBasePacks();
void resetContent(
EnginePaths& paths, Input& input, std::vector<ContentPack>& packs
);
void loadContent(
EnginePaths& paths,
Input& input,
std::vector<ContentPack>& packs,
const std::vector<std::string>& names
);
void loadContent(
EnginePaths& paths,
Input& input,
std::vector<ContentPack>& packs
);
std::vector<io::path> getDefaultSources();
private:
std::unique_ptr<Content> content;
std::vector<std::string> basePacks;
std::function<void()> postContent;
};

View File

@ -124,7 +124,7 @@ ContentPack ContentPack::read(const std::string& path, const io::path& folder) {
} }
void ContentPack::scanFolder( void ContentPack::scanFolder(
const std::string& path, const io::path& folder, std::vector<ContentPack>& packs const io::path& folder, std::vector<ContentPack>& packs
) { ) {
if (!io::is_directory(folder)) { if (!io::is_directory(folder)) {
return; return;
@ -133,9 +133,7 @@ void ContentPack::scanFolder(
if (!io::is_directory(packFolder)) continue; if (!io::is_directory(packFolder)) continue;
if (!is_pack(packFolder)) continue; if (!is_pack(packFolder)) continue;
try { try {
packs.push_back( packs.push_back(read(packFolder.string(), packFolder));
read(path + "/" + packFolder.name(), packFolder)
);
} catch (const contentpack_error& err) { } catch (const contentpack_error& err) {
std::cerr << "package.json error at " << err.getFolder().string(); std::cerr << "package.json error at " << err.getFolder().string();
std::cerr << ": " << err.what() << std::endl; std::cerr << ": " << err.what() << std::endl;

View File

@ -58,14 +58,10 @@ struct ContentPack {
static const std::vector<std::string> RESERVED_NAMES; static const std::vector<std::string> RESERVED_NAMES;
static bool is_pack(const io::path& folder); static bool is_pack(const io::path& folder);
static ContentPack read( static ContentPack read(const std::string& path, const io::path& folder);
const std::string& path, const io::path& folder
);
static void scanFolder( static void scanFolder(
const std::string& path, const io::path& folder, std::vector<ContentPack>& packs
const io::path& folder,
std::vector<ContentPack>& packs
); );
static std::vector<std::string> worldPacksList( static std::vector<std::string> worldPacksList(

View File

@ -7,7 +7,7 @@
PacksManager::PacksManager() = default; PacksManager::PacksManager() = default;
void PacksManager::setSources(std::vector<std::pair<std::string, io::path>> sources) { void PacksManager::setSources(std::vector<io::path> sources) {
this->sources = std::move(sources); this->sources = std::move(sources);
} }
@ -15,8 +15,8 @@ void PacksManager::scan() {
packs.clear(); packs.clear();
std::vector<ContentPack> packsList; std::vector<ContentPack> packsList;
for (auto& [path, folder] : sources) { for (auto& folder : sources) {
ContentPack::scanFolder(path, folder, packsList); ContentPack::scanFolder(folder, packsList);
for (auto& pack : packsList) { for (auto& pack : packsList) {
packs.try_emplace(pack.id, pack); packs.try_emplace(pack.id, pack);
} }

View File

@ -8,12 +8,12 @@
class PacksManager { class PacksManager {
std::unordered_map<std::string, ContentPack> packs; std::unordered_map<std::string, ContentPack> packs;
std::vector<std::pair<std::string, io::path>> sources; std::vector<io::path> sources;
public: public:
PacksManager(); PacksManager();
/// @brief Set content packs sources (search folders) /// @brief Set content packs sources (search folders)
void setSources(std::vector<std::pair<std::string, io::path>> sources); void setSources(std::vector<io::path> sources);
/// @brief Scan sources and collect all found packs excluding duplication. /// @brief Scan sources and collect all found packs excluding duplication.
/// Scanning order depends on sources order /// Scanning order depends on sources order

View File

@ -7,14 +7,14 @@
#include "debug/Logger.hpp" #include "debug/Logger.hpp"
#include "assets/AssetsLoader.hpp" #include "assets/AssetsLoader.hpp"
#include "audio/audio.hpp" #include "audio/audio.hpp"
#include "voxels/Block.hpp"
#include "coders/GLSLExtension.hpp" #include "coders/GLSLExtension.hpp"
#include "coders/imageio.hpp" #include "coders/imageio.hpp"
#include "coders/json.hpp" #include "coders/json.hpp"
#include "coders/toml.hpp" #include "coders/toml.hpp"
#include "coders/commons.hpp" #include "coders/commons.hpp"
#include "content/Content.hpp" #include "content/Content.hpp"
#include "content/ContentBuilder.hpp" #include "content/ContentControl.hpp"
#include "content/ContentLoader.hpp"
#include "core_defs.hpp" #include "core_defs.hpp"
#include "io/io.hpp" #include "io/io.hpp"
#include "frontend/locale.hpp" #include "frontend/locale.hpp"
@ -146,7 +146,14 @@ void Engine::initialize(CoreParameters coreParameters) {
keepAlive(settings.ui.language.observe([this](auto lang) { keepAlive(settings.ui.language.observe([this](auto lang) {
setLanguage(lang); setLanguage(lang);
}, true)); }, true));
basePacks = io::read_list("res:config/builtins.list");
content = std::make_unique<ContentControl>([this]() {
langs::setup("res:", langs::get_current(), paths.resPaths.collectRoots());
if (!isHeadless()) {
loadAssets();
onAssetsLoaded();
}
});
} }
void Engine::loadSettings() { void Engine::loadSettings() {
@ -284,27 +291,19 @@ EngineController* Engine::getController() {
return controller.get(); return controller.get();
} }
PacksManager Engine::createPacksManager(const io::path& worldFolder) {
PacksManager manager;
manager.setSources({
{"world:content", worldFolder.empty() ? worldFolder : worldFolder / "content"},
{"user:content", "user:content"},
{"res:content", "res:content"}
});
return manager;
}
void Engine::setLevelConsumer(OnWorldOpen levelConsumer) { void Engine::setLevelConsumer(OnWorldOpen levelConsumer) {
this->levelConsumer = std::move(levelConsumer); this->levelConsumer = std::move(levelConsumer);
} }
void Engine::loadAssets() { void Engine::loadAssets() {
logger.info() << "loading assets"; logger.info() << "loading assets";
Shader::preprocessor->setPaths(resPaths.get()); Shader::preprocessor->setPaths(&paths.resPaths);
auto content = this->content->get();
auto new_assets = std::make_unique<Assets>(); auto new_assets = std::make_unique<Assets>();
AssetsLoader loader(*this, *new_assets, resPaths.get()); AssetsLoader loader(*this, *new_assets, paths.resPaths);
AssetsLoader::addDefaults(loader, content.get()); AssetsLoader::addDefaults(loader, content);
// no need // no need
// correct log messages order is more useful // correct log messages order is more useful
@ -345,118 +344,21 @@ void Engine::loadAssets() {
} }
} }
static void load_configs(Engine& engine, const io::path& root) {
auto& input = engine.getInput();
auto configFolder = root / "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
);
}
}
void Engine::loadContent() { void Engine::loadContent() {
scripting::cleanup(); content->loadContent(paths, *input, contentPacks);
std::vector<std::string> names;
for (auto& pack : contentPacks) {
names.push_back(pack.id);
}
PacksManager manager = createPacksManager(paths.getCurrentWorldFolder());
manager.scan();
names = manager.assemble(names);
contentPacks = manager.getAll(names);
std::vector<PathsRoot> entryPoints;
for (auto& pack : contentPacks) {
entryPoints.emplace_back(pack.id, pack.folder);
}
paths.setEntryPoints(std::move(entryPoints));
ContentBuilder contentBuilder;
corecontent::setup(*input, contentBuilder);
auto corePack = ContentPack::createCore(paths);
// Setup filesystem entry points
std::vector<PathsRoot> resRoots {
{"core", corePack.folder}
};
for (auto& pack : contentPacks) {
resRoots.push_back({pack.id, pack.folder});
}
resPaths = std::make_unique<ResPaths>("res:", resRoots);
// Load content
{
ContentLoader(&corePack, contentBuilder, *resPaths).load();
load_configs(*this, corePack.folder);
}
for (auto& pack : contentPacks) {
ContentLoader(&pack, contentBuilder, *resPaths).load();
load_configs(*this, pack.folder);
}
content = contentBuilder.build();
scripting::on_content_load(content.get());
ContentLoader::loadScripts(*content);
langs::setup("res:", langs::get_current(), resPaths->collectRoots());
if (!isHeadless()) {
loadAssets();
onAssetsLoaded();
}
} }
void Engine::resetContent() { void Engine::resetContent() {
scripting::cleanup(); paths.setCurrentWorldFolder("");
std::vector<PathsRoot> resRoots; content->resetContent(paths, *input, contentPacks);
{
auto pack = ContentPack::createCore(paths);
resRoots.push_back({"core", pack.folder});
load_configs(*this, pack.folder);
}
auto manager = createPacksManager(io::path());
manager.scan();
for (const auto& pack : manager.getAll(basePacks)) {
resRoots.push_back({pack.id, pack.folder});
}
resPaths = std::make_unique<ResPaths>("res:", resRoots);
contentPacks.clear();
content.reset();
langs::setup("res:", langs::get_current(), resPaths->collectRoots());
if (!isHeadless()) {
loadAssets();
onAssetsLoaded();
}
contentPacks = manager.getAll(basePacks);
} }
void Engine::loadWorldContent(const io::path& folder) { void Engine::loadWorldContent(const io::path& folder) {
contentPacks.clear(); contentPacks.clear();
auto packNames = ContentPack::worldPacksList(folder);
PacksManager manager;
manager.setSources(
{{"world:content", folder.empty() ? folder : folder / "content"},
{"user:content", "user:content"},
{"res:content", "res:content"}}
);
manager.scan();
contentPacks = manager.getAll(manager.assemble(packNames));
paths.setCurrentWorldFolder(folder); paths.setCurrentWorldFolder(folder);
loadContent(); content->loadContent(
} paths, *input, contentPacks, ContentPack::worldPacksList("world:")
);
void Engine::loadAllPacks() {
PacksManager manager = createPacksManager(paths.getCurrentWorldFolder());
manager.scan();
auto allnames = manager.getAllNames();
contentPacks = manager.getAll(manager.assemble(allnames));
} }
void Engine::setScreen(std::shared_ptr<Screen> screen) { void Engine::setScreen(std::shared_ptr<Screen> screen) {
@ -467,12 +369,7 @@ void Engine::setScreen(std::shared_ptr<Screen> screen) {
} }
void Engine::setLanguage(std::string locale) { void Engine::setLanguage(std::string locale) {
langs::setup( langs::setup("res:", std::move(locale), paths.resPaths.collectRoots());
"res:",
std::move(locale),
resPaths ? resPaths->collectRoots()
: std::vector<io::path> {{"core", "res:"}}
);
} }
void Engine::onWorldOpen(std::unique_ptr<Level> level, int64_t localPlayer) { void Engine::onWorldOpen(std::unique_ptr<Level> level, int64_t localPlayer) {
@ -505,11 +402,11 @@ Assets* Engine::getAssets() {
} }
const Content* Engine::getContent() const { const Content* Engine::getContent() const {
return content.get(); return content->get();
} }
Content* Engine::getWriteableContent() { Content* Engine::getWriteableContent() {
return content.get(); return content->get();
} }
std::vector<ContentPack> Engine::getAllContentPacks() { std::vector<ContentPack> Engine::getAllContentPacks() {
@ -522,16 +419,12 @@ std::vector<ContentPack>& Engine::getContentPacks() {
return contentPacks; return contentPacks;
} }
std::vector<std::string>& Engine::getBasePacks() {
return basePacks;
}
EnginePaths& Engine::getPaths() { EnginePaths& Engine::getPaths() {
return paths; return paths;
} }
ResPaths* Engine::getResPaths() { ResPaths& Engine::getResPaths() {
return resPaths.get(); return paths.resPaths;
} }
std::shared_ptr<Screen> Engine::getScreen() { std::shared_ptr<Screen> Engine::getScreen() {
@ -553,3 +446,7 @@ const CoreParameters& Engine::getCoreParameters() const {
bool Engine::isHeadless() const { bool Engine::isHeadless() const {
return params.headless; return params.headless;
} }
ContentControl& Engine::getContentControl() {
return *content;
}

View File

@ -23,6 +23,7 @@ class Level;
class Screen; class Screen;
class EnginePaths; class EnginePaths;
class ResPaths; class ResPaths;
class ContentControl;
class EngineController; class EngineController;
class SettingsHandler; class SettingsHandler;
struct EngineSettings; struct EngineSettings;
@ -64,14 +65,12 @@ class Engine : public util::ObjectsKeeper {
std::unique_ptr<Assets> assets; std::unique_ptr<Assets> assets;
std::shared_ptr<Screen> screen; std::shared_ptr<Screen> screen;
std::vector<ContentPack> contentPacks; std::vector<ContentPack> contentPacks;
std::unique_ptr<Content> content; std::unique_ptr<ContentControl> content;
std::unique_ptr<ResPaths> resPaths;
std::unique_ptr<EngineController> controller; std::unique_ptr<EngineController> controller;
std::unique_ptr<cmd::CommandsInterpreter> cmd; std::unique_ptr<cmd::CommandsInterpreter> cmd;
std::unique_ptr<network::Network> network; std::unique_ptr<network::Network> network;
std::unique_ptr<Window> window; std::unique_ptr<Window> window;
std::unique_ptr<Input> input; std::unique_ptr<Input> input;
std::vector<std::string> basePacks;
std::unique_ptr<gui::GUI> gui; std::unique_ptr<gui::GUI> gui;
PostRunnables postRunnables; PostRunnables postRunnables;
Time time; Time time;
@ -127,9 +126,6 @@ public:
/// @param folder world folder /// @param folder world folder
void loadWorldContent(const io::path& folder); void loadWorldContent(const io::path& folder);
/// @brief Collect all available content-packs from res/content
void loadAllPacks();
/// @brief Get active assets storage instance /// @brief Get active assets storage instance
Assets* getAssets(); Assets* getAssets();
@ -140,7 +136,7 @@ public:
EnginePaths& getPaths(); EnginePaths& getPaths();
/// @brief Get engine resource paths controller /// @brief Get engine resource paths controller
ResPaths* getResPaths(); ResPaths& getResPaths();
void onWorldOpen(std::unique_ptr<Level> level, int64_t localPlayer); void onWorldOpen(std::unique_ptr<Level> level, int64_t localPlayer);
void onWorldClosed(); void onWorldClosed();
@ -159,8 +155,6 @@ public:
std::vector<ContentPack> getAllContentPacks(); std::vector<ContentPack> getAllContentPacks();
std::vector<std::string>& getBasePacks();
/// @brief Get current screen /// @brief Get current screen
std::shared_ptr<Screen> getScreen(); std::shared_ptr<Screen> getScreen();
@ -173,8 +167,6 @@ public:
EngineController* getController(); EngineController* getController();
PacksManager createPacksManager(const io::path& worldFolder);
void setLevelConsumer(OnWorldOpen levelConsumer); void setLevelConsumer(OnWorldOpen levelConsumer);
SettingsHandler& getSettingsHandler(); SettingsHandler& getSettingsHandler();
@ -185,6 +177,8 @@ public:
bool isHeadless() const; bool isHeadless() const;
ContentControl& getContentControl();
gui::GUI& getGUI() { gui::GUI& getGUI() {
return *gui; return *gui;
} }

View File

@ -74,7 +74,7 @@ UiDocument* menus::show(
Engine& engine, const std::string& name, std::vector<dv::value> args Engine& engine, const std::string& name, std::vector<dv::value> args
) { ) {
auto menu = engine.getGUI().getMenu(); auto menu = engine.getGUI().getMenu();
auto file = engine.getResPaths()->find("layouts/" + name + ".xml"); auto file = engine.getResPaths().find("layouts/" + name + ".xml");
auto fullname = "core:layouts/" + name; auto fullname = "core:layouts/" + name;
auto documentPtr = UiDocument::read( auto documentPtr = UiDocument::read(

View File

@ -61,7 +61,7 @@ Decorator::Decorator(
player->getPosition() player->getPosition()
)); ));
} }
playerNamePreset.deserialize(engine.getResPaths()->readCombinedObject( playerNamePreset.deserialize(engine.getResPaths().readCombinedObject(
"presets/text3d/player_name.toml" "presets/text3d/player_name.toml"
)); ));
} }

View File

@ -247,8 +247,8 @@ std::tuple<std::string, std::string> EnginePaths::parsePath(std::string_view pat
return {prefix, filename}; return {prefix, filename};
} }
ResPaths::ResPaths(io::path mainRoot, std::vector<PathsRoot> roots) ResPaths::ResPaths(std::vector<PathsRoot> roots)
: mainRoot(std::move(mainRoot)), roots(std::move(roots)) { : roots(std::move(roots)) {
} }
io::path ResPaths::find(const std::string& filename) const { io::path ResPaths::find(const std::string& filename) const {
@ -259,7 +259,7 @@ io::path ResPaths::find(const std::string& filename) const {
return file; return file;
} }
} }
return mainRoot / filename; return io::path("res:") / filename;
} }
std::string ResPaths::findRaw(const std::string& filename) const { std::string ResPaths::findRaw(const std::string& filename) const {
@ -356,7 +356,3 @@ std::vector<io::path> ResPaths::collectRoots() {
} }
return collected; return collected;
} }
const io::path& ResPaths::getMainRoot() const {
return mainRoot;
}

View File

@ -19,8 +19,33 @@ struct PathsRoot {
} }
}; };
class ResPaths {
public:
ResPaths() = default;
ResPaths(std::vector<PathsRoot> roots);
io::path find(const std::string& filename) const;
std::string findRaw(const std::string& filename) const;
std::vector<io::path> listdir(const std::string& folder) const;
std::vector<std::string> listdirRaw(const std::string& folder) const;
/// @brief Read all found list versions from all packs and combine into a
/// single list. Invalid versions will be skipped with logging a warning
/// @param file *.json file path relative to entry point
dv::value readCombinedList(const std::string& file) const;
dv::value readCombinedObject(const std::string& file, bool deep=false) const;
std::vector<io::path> collectRoots();
private:
std::vector<PathsRoot> roots;
};
class EnginePaths { class EnginePaths {
public: public:
ResPaths resPaths;
void prepare(); void prepare();
void setUserFilesFolder(std::filesystem::path folder); void setUserFilesFolder(std::filesystem::path folder);
@ -65,28 +90,3 @@ private:
void cleanup(); void cleanup();
}; };
class ResPaths {
public:
ResPaths(io::path mainRoot, std::vector<PathsRoot> roots);
io::path find(const std::string& filename) const;
std::string findRaw(const std::string& filename) const;
std::vector<io::path> listdir(const std::string& folder) const;
std::vector<std::string> listdirRaw(const std::string& folder) const;
/// @brief Read all found list versions from all packs and combine into a
/// single list. Invalid versions will be skipped with logging a warning
/// @param file *.json file path relative to entry point
dv::value readCombinedList(const std::string& file) const;
dv::value readCombinedObject(const std::string& file, bool deep=false) const;
std::vector<io::path> collectRoots();
const io::path& getMainRoot() const;
private:
io::path mainRoot;
std::vector<PathsRoot> roots;
};

View File

@ -9,6 +9,7 @@
#include "debug/Logger.hpp" #include "debug/Logger.hpp"
#include "coders/json.hpp" #include "coders/json.hpp"
#include "content/ContentReport.hpp" #include "content/ContentReport.hpp"
#include "content/ContentControl.hpp"
#include "world/files/WorldConverter.hpp" #include "world/files/WorldConverter.hpp"
#include "world/files/WorldFiles.hpp" #include "world/files/WorldFiles.hpp"
#include "frontend/locale.hpp" #include "frontend/locale.hpp"
@ -318,7 +319,8 @@ void EngineController::reconfigPacks(
runnable removeFunc = [this, controller, packsToAdd, packsToRemove]() { runnable removeFunc = [this, controller, packsToAdd, packsToRemove]() {
if (controller == nullptr) { if (controller == nullptr) {
try { try {
auto manager = engine.createPacksManager(""); PacksManager manager;
manager.setSources(engine.getContentControl().getDefaultSources());
manager.scan(); manager.scan();
auto names = PacksManager::getNames(engine.getContentPacks()); auto names = PacksManager::getNames(engine.getContentPacks());
for (const auto& id : packsToAdd) { for (const auto& id : packsToAdd) {
@ -339,7 +341,9 @@ void EngineController::reconfigPacks(
auto world = controller->getLevel()->getWorld(); auto world = controller->getLevel()->getWorld();
auto& wfile = *world->wfile; auto& wfile = *world->wfile;
controller->saveWorld(); controller->saveWorld();
auto manager = engine.createPacksManager(wfile.getFolder());
PacksManager manager;
manager.setSources(engine.getContentControl().getDefaultSources());
manager.scan(); manager.scan();
auto names = PacksManager::getNames(world->getPacks()); auto names = PacksManager::getNames(world->getPacks());

View File

@ -71,8 +71,8 @@ inline audio::speakerid_t play_stream(
if (std::strchr(filename, ':')) { if (std::strchr(filename, ':')) {
file = std::string(filename); file = std::string(filename);
} else { } else {
auto paths = scripting::engine->getResPaths(); const auto& paths = scripting::engine->getResPaths();
file = paths->find(filename); file = paths.find(filename);
} }
return audio::play_stream( return audio::play_stream(
file, file,

View File

@ -17,7 +17,7 @@ using namespace scripting;
static int l_find(lua::State* L) { static int l_find(lua::State* L) {
auto path = lua::require_string(L, 1); auto path = lua::require_string(L, 1);
try { try {
return lua::pushstring(L, engine->getResPaths()->findRaw(path)); return lua::pushstring(L, engine->getResPaths().findRaw(path));
} catch (const std::runtime_error& err) { } catch (const std::runtime_error& err) {
return 0; return 0;
} }
@ -159,7 +159,7 @@ static int l_write_bytes(lua::State* L) {
} }
static int l_list_all_res(lua::State* L, const std::string& path) { static int l_list_all_res(lua::State* L, const std::string& path) {
auto files = engine->getResPaths()->listdirRaw(path); auto files = engine->getResPaths().listdirRaw(path);
lua::createtable(L, files.size(), 0); lua::createtable(L, files.size(), 0);
for (size_t i = 0; i < files.size(); i++) { for (size_t i = 0; i < files.size(); i++) {
lua::pushstring(L, files[i]); lua::pushstring(L, files[i]);
@ -222,7 +222,7 @@ static int l_read_combined_list(lua::State* L) {
if (path.find(':') != std::string::npos) { if (path.find(':') != std::string::npos) {
throw std::runtime_error("entry point must not be specified"); throw std::runtime_error("entry point must not be specified");
} }
return lua::pushvalue(L, engine->getResPaths()->readCombinedList(path)); return lua::pushvalue(L, engine->getResPaths().readCombinedList(path));
} }
static int l_read_combined_object(lua::State* L) { static int l_read_combined_object(lua::State* L) {
@ -230,7 +230,7 @@ static int l_read_combined_object(lua::State* L) {
if (path.find(':') != std::string::npos) { if (path.find(':') != std::string::npos) {
throw std::runtime_error("entry point must not be specified"); throw std::runtime_error("entry point must not be specified");
} }
return lua::pushvalue(L, engine->getResPaths()->readCombinedObject(path)); return lua::pushvalue(L, engine->getResPaths().readCombinedObject(path));
} }
static int l_is_writeable(lua::State* L) { static int l_is_writeable(lua::State* L) {

View File

@ -68,7 +68,7 @@ static int l_get_generators(lua::State* L) {
/// @return The ID of the default world generator /// @return The ID of the default world generator
static int l_get_default_generator(lua::State* L) { static int l_get_default_generator(lua::State* L) {
// content is not initialized yet // content is not initialized yet
auto combined = engine->getResPaths()->readCombinedObject( auto combined = engine->getResPaths().readCombinedObject(
EnginePaths::CONFIG_DEFAULTS.string() EnginePaths::CONFIG_DEFAULTS.string()
); );
return lua::pushstring(L, combined["generator"].asString()); return lua::pushstring(L, combined["generator"].asString());

View File

@ -6,6 +6,7 @@
#include "assets/AssetsLoader.hpp" #include "assets/AssetsLoader.hpp"
#include "content/Content.hpp" #include "content/Content.hpp"
#include "content/ContentControl.hpp"
#include "engine/Engine.hpp" #include "engine/Engine.hpp"
#include "graphics/ui/gui_util.hpp" #include "graphics/ui/gui_util.hpp"
#include "graphics/ui/elements/Menu.hpp" #include "graphics/ui/elements/Menu.hpp"
@ -47,7 +48,8 @@ static int l_pack_get_available(lua::State* L) {
if (level) { if (level) {
worldFolder = level->getWorld()->wfile->getFolder(); worldFolder = level->getWorld()->wfile->getFolder();
} }
auto manager = engine->createPacksManager(worldFolder); PacksManager manager;
manager.setSources(engine->getContentControl().getDefaultSources());
manager.scan(); manager.scan();
const auto& installed = engine->getContentPacks(); const auto& installed = engine->getContentPacks();
@ -153,7 +155,8 @@ static int pack_get_infos(lua::State* L) {
if (level) { if (level) {
worldFolder = level->getWorld()->wfile->getFolder(); worldFolder = level->getWorld()->wfile->getFolder();
} }
auto manager = engine->createPacksManager(worldFolder); PacksManager manager;
manager.setSources(engine->getContentControl().getDefaultSources());
manager.scan(); manager.scan();
auto vec = auto vec =
manager.getAll(std::vector<std::string>(ids.begin(), ids.end())); manager.getAll(std::vector<std::string>(ids.begin(), ids.end()));
@ -195,7 +198,8 @@ static int l_pack_get_info(lua::State* L) {
if (level) { if (level) {
worldFolder = level->getWorld()->wfile->getFolder(); worldFolder = level->getWorld()->wfile->getFolder();
} }
auto manager = engine->createPacksManager(worldFolder); PacksManager manager;
manager.setSources(engine->getContentControl().getDefaultSources());
manager.scan(); manager.scan();
auto vec = manager.getAll({packid}); auto vec = manager.getAll({packid});
if (!vec.empty()) { if (!vec.empty()) {
@ -208,7 +212,7 @@ static int l_pack_get_info(lua::State* L) {
} }
static int l_pack_get_base_packs(lua::State* L) { static int l_pack_get_base_packs(lua::State* L) {
auto& packs = engine->getBasePacks(); auto& packs = engine->getContentControl().getBasePacks();
lua::createtable(L, packs.size(), 0); lua::createtable(L, packs.size(), 0);
for (size_t i = 0; i < packs.size(); i++) { for (size_t i = 0; i < packs.size(); i++) {
lua::pushstring(L, packs[i]); lua::pushstring(L, packs[i]);
@ -232,7 +236,8 @@ static int l_pack_assemble(lua::State* L) {
if (level) { if (level) {
worldFolder = level->getWorld()->wfile->getFolder(); worldFolder = level->getWorld()->wfile->getFolder();
} }
auto manager = engine->createPacksManager(worldFolder); PacksManager manager;
manager.setSources(engine->getContentControl().getDefaultSources());
manager.scan(); manager.scan();
try { try {
ids = manager.assemble(ids); ids = manager.assemble(ids);