Files
voxelforge/core/include/vf/DataPack.h
T

157 lines
5.1 KiB
C++

#pragma once
#include <array>
#include <cstdint>
#include <map>
#include <string>
#include <vector>
#include "Crafting.h"
#include "Item.h"
#include "Json.h"
#include "Loot.h"
namespace vf {
// A namespaced identifier, "voxelforge:iron_ingot". Both halves are lowercase
// by construction: the space accepts [a-z0-9_.-] and the path additionally
// accepts '/', so one namespace can hold folders of resources.
struct ResourceId {
std::string space;
std::string path;
ResourceId() = default;
ResourceId(std::string space, std::string path);
// Accepts "space:path" or a bare "path", which takes defaultSpace.
static bool parse(const std::string& text, const std::string& defaultSpace, ResourceId& out);
static bool validSpace(const std::string& text);
static bool validPath(const std::string& text);
bool valid() const { return validSpace(space) && validPath(path); }
bool empty() const { return space.empty() && path.empty(); }
std::string text() const { return space + ":" + path; }
bool operator==(const ResourceId& other) const {
return space == other.space && path == other.path;
}
bool operator!=(const ResourceId& other) const { return !(*this == other); }
bool operator<(const ResourceId& other) const {
return space == other.space ? path < other.path : space < other.space;
}
};
// Maps a resource id onto the palettes the engine already has. The path is
// matched case insensitively with '_' and '-' removed, so "iron_ingot" finds
// ItemId::IronIngot through the shared name table in Command.cpp.
bool resolveItemId(const ResourceId& id, ItemId& out);
bool resolveBlockId(const ResourceId& id, Block& out);
struct DataPackMeta {
std::string name;
std::string description;
int formatVersion = 1;
// Higher priority is applied later, so it overwrites what came before.
int priority = 0;
std::string defaultSpace = "voxelforge";
static bool fromJson(const JsonValue& root, DataPackMeta& out, std::string& problem);
};
// One thing that went wrong while loading, always naming the file it came from.
// A pack keeps loading past its own errors: one broken recipe costs that recipe
// and nothing else.
struct DataPackError {
std::string pack;
std::string file;
std::string reason;
int line = 0;
int column = 0;
std::string text() const;
};
// A pack as a directory tree held in memory: a map from a slash separated path
// to the file's bytes. Nothing here ever touches a real filesystem.
//
// Layout, all below data/<space>/:
// recipes/<name>.json shaped, shapeless or smelting
// lootTables/<name>.json pools of entries, conditions and functions
// tags/blocks/<name>.json block tag, entries may reference other tags
// tags/items/<name>.json item tag
// advancements/<name>.json read by Advancement.h
// plus an optional pack.json at the root carrying the metadata.
class DataPack {
public:
DataPack() = default;
explicit DataPack(DataPackMeta meta) : packMeta(std::move(meta)) {}
const DataPackMeta& meta() const { return packMeta; }
DataPackMeta& meta() { return packMeta; }
void addFile(const std::string& path, std::string contents);
bool removeFile(const std::string& path);
bool hasFile(const std::string& path) const;
const std::string* fileContents(const std::string& path) const;
// Sorted, so loading order never depends on hashing.
std::vector<std::string> filesUnder(const std::string& prefix) const;
std::vector<std::string> allFiles() const;
std::size_t fileCount() const { return files.size(); }
private:
DataPackMeta packMeta;
std::map<std::string, std::string> files;
};
// A recipe read from data, in the shape the existing CraftingRegistry takes.
// The conversions below hand back the very structs Crafting.h declares, so a
// loaded recipe is matched by the same craftMatch as a built in one.
struct RecipeData {
enum class Kind : uint8_t { Shaped, Shapeless, Smelting };
ResourceId id;
Kind kind = Kind::Shaped;
int width = 0;
int height = 0;
std::array<ItemId, craftGridCells> pattern{};
bool allowMirror = true;
std::vector<ItemId> ingredients;
ItemId smeltInput = ItemId::None;
float smeltSeconds = 10.0f;
float experience = 0.0f;
ItemId resultItem = ItemId::None;
int resultCount = 1;
ShapedRecipe toShaped() const;
ShapelessRecipe toShapeless() const;
SmeltingRecipe toSmelting() const;
};
// A tag: a named set of resource ids that may itself name other tags. The same
// type carries block tags and item tags; which palette an entry resolves
// against is decided by the registry it was loaded into.
struct BlockTag {
struct Entry {
ResourceId id;
// Written as "#space:path" in data: pull in every member of that tag.
bool reference = false;
bool operator==(const Entry& other) const {
return id == other.id && reference == other.reference;
}
};
ResourceId id;
// false appends to whatever an earlier pack defined, true wipes it first.
bool replace = false;
std::vector<Entry> entries;
bool contains(const ResourceId& member) const;
};