VoxelEngine/src/content/ContentPack.cpp

150 lines
4.7 KiB
C++

#include "ContentPack.hpp"
#include <algorithm>
#include <iostream>
#include <utility>
#include "constants.hpp"
#include "coders/json.hpp"
#include "data/dv.hpp"
#include "files/engine_paths.hpp"
#include "files/files.hpp"
namespace fs = std::filesystem;
ContentPack ContentPack::createCore(const EnginePaths* paths) {
return ContentPack {
"core", "Core", ENGINE_VERSION_STRING, "", "", paths->getResourcesFolder(), {}
};
}
const std::vector<std::string> ContentPack::RESERVED_NAMES = {
"res", "abs", "local", "core", "user", "world", "none", "null"};
contentpack_error::contentpack_error(
std::string packId, fs::path folder, const std::string& message
)
: std::runtime_error(message),
packId(std::move(packId)),
folder(std::move(folder)) {
}
std::string contentpack_error::getPackId() const {
return packId;
}
fs::path contentpack_error::getFolder() const {
return folder;
}
fs::path ContentPack::getContentFile() const {
return folder / fs::path(CONTENT_FILENAME);
}
bool ContentPack::is_pack(const fs::path& folder) {
return fs::is_regular_file(folder / fs::path(PACKAGE_FILENAME));
}
static void checkContentPackId(const std::string& id, const fs::path& folder) {
if (id.length() < 2 || id.length() > 24)
throw contentpack_error(
id, folder, "content-pack id length is out of range [2, 24]"
);
if (isdigit(id[0]))
throw contentpack_error(
id, folder, "content-pack id must not start with a digit"
);
for (char c : id) {
if (!isalnum(c) && c != '_') {
throw contentpack_error(
id, folder, "illegal character in content-pack id"
);
}
}
if (std::find(
ContentPack::RESERVED_NAMES.begin(),
ContentPack::RESERVED_NAMES.end(),
id
) != ContentPack::RESERVED_NAMES.end()) {
throw contentpack_error(id, folder, "this content-pack id is reserved");
}
}
ContentPack ContentPack::read(const fs::path& folder) {
auto root = files::read_json(folder / fs::path(PACKAGE_FILENAME));
ContentPack pack;
root.at("id").get(pack.id);
root.at("title").get(pack.title);
root.at("version").get(pack.version);
root.at("creator").get(pack.creator);
root.at("description").get(pack.description);
pack.folder = folder;
if (auto found = root.at("dependencies")) {
const auto& dependencies = *found;
for (const auto& elem : dependencies) {
pack.dependencies.push_back(
{DependencyLevel::required, elem.asString()}
);
}
}
if (pack.id == "none")
throw contentpack_error(
pack.id, folder, "content-pack id is not specified"
);
checkContentPackId(pack.id, folder);
return pack;
}
void ContentPack::scanFolder(
const fs::path& folder, std::vector<ContentPack>& 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;
try {
packs.push_back(read(folder));
} catch (const contentpack_error& err) {
std::cerr << "package.json error at " << err.getFolder().u8string();
std::cerr << ": " << err.what() << std::endl;
} catch (const std::runtime_error& err) {
std::cerr << err.what() << std::endl;
}
}
}
std::vector<std::string> ContentPack::worldPacksList(const fs::path& folder) {
fs::path listfile = folder / fs::path("packs.list");
if (!fs::is_regular_file(listfile)) {
std::cerr << "warning: packs.list not found (will be created)";
std::cerr << std::endl;
files::write_string(listfile, "# autogenerated, do not modify\nbase\n");
}
return files::read_list(listfile);
}
fs::path ContentPack::findPack(
const EnginePaths* paths, const fs::path& worldDir, const std::string& name
) {
fs::path folder = worldDir / fs::path("content") / fs::path(name);
if (fs::is_directory(folder)) {
return folder;
}
folder = paths->getUserFilesFolder() / fs::path("content") / fs::path(name);
if (fs::is_directory(folder)) {
return folder;
}
return paths->getResourcesFolder() / fs::path("content") / fs::path(name);
}
ContentPackRuntime::ContentPackRuntime(ContentPack info, scriptenv env)
: info(std::move(info)), env(std::move(env)) {
}
ContentPackRuntime::~ContentPackRuntime() = default;