diff --git a/src/content/Content.cpp b/src/content/Content.cpp index 4c924087..773a0bda 100644 --- a/src/content/Content.cpp +++ b/src/content/Content.cpp @@ -4,22 +4,43 @@ #include #include "../voxels/Block.h" +#include "../content/ItemDef.h" using glm::vec3; -using std::vector; using std::string; using std::unordered_map; void ContentBuilder::add(Block* def) { - if (blockDefs.find(def->name) != blockDefs.end()) { - throw std::runtime_error("block name duplicate: "+def->name); - } + checkIdentifier(def->name); blockDefs[def->name] = def; blockIds.push_back(def->name); } +void ContentBuilder::add(ItemDef* def) { + checkIdentifier(def->name); + itemDefs[def->name] = def; + itemIds.push_back(def->name); +} + +void ContentBuilder::checkIdentifier(std::string id) { + contenttype result; + if ((checkContentType(id) != contenttype::none)) { + throw contentindexreuse_error("identifier "+id+" is already used", result); + } +} + +contenttype ContentBuilder::checkContentType(std::string id) { + if (blockDefs.find(id) != blockDefs.end()) { + return contenttype::block; + } + if (itemDefs.find(id) != itemDefs.end()) { + return contenttype::item; + } + return contenttype::none; +} + Content* ContentBuilder::build() { - vector blockDefsIndices; + std::vector blockDefsIndices; DrawGroups* groups = new DrawGroups; for (const string& name : blockIds) { Block* def = blockDefs[name]; @@ -42,15 +63,26 @@ Content* ContentBuilder::build() { if (groups->find(def->drawGroup) == groups->end()) { groups->insert(def->drawGroup); } - - } - ContentIndices* indices = new ContentIndices(blockDefsIndices); + + std::vector itemDefsIndices; + for (const string& name : itemIds) { + ItemDef* def = itemDefs[name]; + + // Generating runtime info + def->rt.id = itemDefsIndices.size(); + itemDefsIndices.push_back(def); + } + + auto indices = new ContentIndices(blockDefsIndices, itemDefsIndices); return new Content(indices, groups, blockDefs); } -ContentIndices::ContentIndices(vector blockDefs) - : blockDefs(blockDefs) { +ContentIndices::ContentIndices( + std::vector blockDefs, + std::vector itemDefs) + : blockDefs(blockDefs), + itemDefs(itemDefs) { } Content::Content(ContentIndices* indices, DrawGroups* drawGroups, @@ -80,3 +112,19 @@ Block* Content::requireBlock(string id) const { } return found->second; } + +ItemDef* Content::findItem(string id) const { + auto found = itemDefs.find(id); + if (found == itemDefs.end()) { + return nullptr; + } + return found->second; +} + +ItemDef* Content::requireItem(string id) const { + auto found = itemDefs.find(id); + if (found == itemDefs.end()) { + throw std::runtime_error("missing item "+id); + } + return found->second; +} diff --git a/src/content/Content.h b/src/content/Content.h index f54f7fe5..3c661582 100644 --- a/src/content/Content.h +++ b/src/content/Content.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include "../typedefs.h" @@ -10,23 +11,47 @@ typedef std::set DrawGroups; class Block; +class ItemDef; class Content; +enum class contenttype { + none, block, item +}; + +class contentindexreuse_error: public std::runtime_error { + contenttype type; +public: + contentindexreuse_error(const std::string& msg, contenttype type) + : std::runtime_error(msg), type(type) {} + + inline contenttype getType() const { + return type; + } +}; + class ContentBuilder { std::unordered_map blockDefs; std::vector blockIds; + + std::unordered_map itemDefs; + std::vector itemIds; public: void add(Block* def); + void add(ItemDef* def); + + void checkIdentifier(std::string id); + contenttype checkContentType(std::string id); Content* build(); }; /* Runtime defs cache: indices */ class ContentIndices { - // blockDefs must be a plain vector with block id used as index std::vector blockDefs; + std::vector itemDefs; public: - ContentIndices(std::vector blockDefs); + ContentIndices(std::vector blockDefs, + std::vector itemDefs); inline Block* getBlockDef(blockid_t id) const { if (id >= blockDefs.size()) @@ -34,19 +59,34 @@ public: return blockDefs[id]; } + inline ItemDef* getItemDef(itemid_t id) const { + if (id >= itemDefs.size()) + return nullptr; + return itemDefs[id]; + } + inline size_t countBlockDefs() const { return blockDefs.size(); } + inline size_t countItemDefs() const { + return itemDefs.size(); + } + // use this for critical spots to prevent range check overhead const Block* const* getBlockDefs() const { return blockDefs.data(); } + + const ItemDef* const* getItemDefs() const { + return itemDefs.data(); + } }; /* Content is a definitions repository */ class Content { std::unordered_map blockDefs; + std::unordered_map itemDefs; public: ContentIndices* const indices; DrawGroups* const drawGroups; @@ -57,6 +97,9 @@ public: Block* findBlock(std::string id) const; Block* requireBlock(std::string id) const; + + ItemDef* findItem(std::string id) const; + ItemDef* requireItem(std::string id) const; }; #endif // CONTENT_CONTENT_H_ \ No newline at end of file diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 08009520..5b9c91a9 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -7,6 +7,7 @@ #include #include "Content.h" +#include "ItemDef.h" #include "../util/listutil.h" #include "../voxels/Block.h" #include "../files/files.h" @@ -21,10 +22,56 @@ namespace fs = std::filesystem; ContentLoader::ContentLoader(ContentPack* pack) : pack(pack) { } +bool ContentLoader::fixPackIndices(fs::path folder, + json::JObject* indicesRoot, + std::string contentSection) { + + std::vector detected; + std::vector indexed; + if (fs::is_directory(folder)) { + for (auto entry : fs::directory_iterator(folder)) { + fs::path file = entry.path(); + if (fs::is_regular_file(file) && file.extension() == ".json") { + std::string name = file.stem().string(); + if (name[0] == '_') + continue; + detected.push_back(name); + } + } + } + + bool modified = false; + if (!indicesRoot->has(contentSection)) { + indicesRoot->putArray(contentSection); + } + json::JArray* arr = indicesRoot->arr(contentSection); + if (arr) { + for (uint i = 0; i < arr->size(); i++) { + std::string name = arr->str(i); + if (!util::contains(detected, name)) { + arr->remove(i); + i--; + modified = true; + continue; + } + indexed.push_back(name); + } + } + for (auto name : detected) { + if (!util::contains(indexed, name)) { + arr->put(name); + modified = true; + } + } + return modified; +} + void ContentLoader::fixPackIndices() { auto folder = pack->folder; auto indexFile = pack->getContentFile(); auto blocksFolder = folder/ContentPack::BLOCKS_FOLDER; + auto itemsFolder = folder/ContentPack::ITEMS_FOLDER; + std::unique_ptr root; if (fs::is_regular_file(indexFile)) { root.reset(files::read_json(indexFile)); @@ -32,43 +79,11 @@ void ContentLoader::fixPackIndices() { root.reset(new json::JObject()); } - std::vector detectedBlocks; - std::vector indexedBlocks; - if (fs::is_directory(blocksFolder)) { - for (auto entry : fs::directory_iterator(blocksFolder)) { - fs::path file = entry.path(); - if (fs::is_regular_file(file) && file.extension() == ".json") { - std::string name = file.stem().string(); - if (name[0] == '_') - continue; - detectedBlocks.push_back(name); - } - } - } - bool modified = false; - if (!root->has("blocks")) { - root->putArray("blocks"); - } - json::JArray* blocksarr = root->arr("blocks"); - if (blocksarr) { - for (uint i = 0; i < blocksarr->size(); i++) { - std::string name = blocksarr->str(i); - if (!util::contains(detectedBlocks, name)) { - blocksarr->remove(i); - i--; - modified = true; - continue; - } - indexedBlocks.push_back(name); - } - } - for (auto name : detectedBlocks) { - if (!util::contains(indexedBlocks, name)) { - blocksarr->put(name); - modified = true; - } - } + + modified |= fixPackIndices(blocksFolder, root.get(), "blocks"); + modified |= fixPackIndices(itemsFolder, root.get(), "items"); + if (modified){ // rewrite modified json std::cout << indexFile << std::endl; @@ -150,6 +165,39 @@ Block* ContentLoader::loadBlock(std::string name, fs::path file) { return def.release(); } +ItemDef* ContentLoader::loadItem(std::string name, std::filesystem::path file) { + std::unique_ptr root(files::read_json(file)); + std::unique_ptr def(new ItemDef(name)); + + return def.release(); +} + +Block* ContentLoader::loadBlock(std::string name) { + auto folder = pack->folder; + + std::string prefix = pack->id+":"+name; + fs::path configFile = folder/fs::path("blocks/"+name+".json"); + fs::path scriptfile = folder/fs::path("scripts/"+name+".lua"); + Block* def = loadBlock(prefix, configFile); + if (fs::is_regular_file(scriptfile)) { + scripting::load_block_script(prefix, scriptfile, &def->rt.funcsset); + } + return def; +} + +ItemDef* ContentLoader::loadItem(std::string name) { + auto folder = pack->folder; + + std::string prefix = pack->id+":"+name; + fs::path configFile = folder/fs::path("items/"+name+".json"); + fs::path scriptfile = folder/fs::path("scripts/"+name+".lua"); + ItemDef* def = loadItem(prefix, configFile); + if (fs::is_regular_file(scriptfile)) { + scripting::load_item_script(prefix, scriptfile, &def->rt.funcsset); + } + return def; +} + void ContentLoader::load(ContentBuilder* builder) { std::cout << "-- loading pack [" << pack->id << "]" << std::endl; @@ -163,15 +211,14 @@ void ContentLoader::load(ContentBuilder* builder) { json::JArray* blocksarr = root->arr("blocks"); if (blocksarr) { for (uint i = 0; i < blocksarr->size(); i++) { - std::string name = blocksarr->str(i); - std::string prefix = pack->id+":"+name; - fs::path blockfile = folder/fs::path("blocks/"+name+".json"); - Block* block = loadBlock(prefix, blockfile); - builder->add(block); - fs::path scriptfile = folder/fs::path("scripts/"+name+".lua"); - if (fs::is_regular_file(scriptfile)) { - scripting::load_block_script(prefix, scriptfile, &block->rt.funcsset); - } + builder->add(loadBlock(blocksarr->str(i))); + } + } + + json::JArray* itemsarr = root->arr("items"); + if (itemsarr) { + for (uint i = 0; i < itemsarr->size(); i++) { + builder->add(loadItem(itemsarr->str(i))); } } } diff --git a/src/content/ContentLoader.h b/src/content/ContentLoader.h index 7961da86..983ce581 100644 --- a/src/content/ContentLoader.h +++ b/src/content/ContentLoader.h @@ -5,16 +5,28 @@ #include class Block; +class ItemDef; class ContentPack; class ContentBuilder; +namespace json { + class JObject; +} + class ContentLoader { const ContentPack* pack; + + Block* loadBlock(std::string name); + ItemDef* loadItem(std::string name); public: ContentLoader(ContentPack* pack); + bool fixPackIndices(std::filesystem::path folder, + json::JObject* indicesRoot, + std::string contentSection); void fixPackIndices(); Block* loadBlock(std::string name, std::filesystem::path file); + ItemDef* loadItem(std::string name, std::filesystem::path file); void load(ContentBuilder* builder); }; diff --git a/src/content/ContentPack.cpp b/src/content/ContentPack.cpp index e79a664a..ae3ea758 100644 --- a/src/content/ContentPack.cpp +++ b/src/content/ContentPack.cpp @@ -11,6 +11,7 @@ namespace fs = std::filesystem; const std::string ContentPack::PACKAGE_FILENAME = "package.json"; const std::string ContentPack::CONTENT_FILENAME = "content.json"; const fs::path ContentPack::BLOCKS_FOLDER = "blocks"; +const fs::path ContentPack::ITEMS_FOLDER = "items"; contentpack_error::contentpack_error( std::string packId, diff --git a/src/content/ContentPack.h b/src/content/ContentPack.h index 98ef7db9..e7bcf335 100644 --- a/src/content/ContentPack.h +++ b/src/content/ContentPack.h @@ -31,6 +31,7 @@ struct ContentPack { static const std::string PACKAGE_FILENAME; static const std::string CONTENT_FILENAME; static const std::filesystem::path BLOCKS_FOLDER; + static const std::filesystem::path ITEMS_FOLDER; static bool is_pack(std::filesystem::path folder); static ContentPack read(std::filesystem::path folder); diff --git a/src/content/ItemDef.cpp b/src/content/ItemDef.cpp new file mode 100644 index 00000000..c5bef078 --- /dev/null +++ b/src/content/ItemDef.cpp @@ -0,0 +1,4 @@ +#include "ItemDef.h" + +ItemDef::ItemDef(std::string name) : name(name) { +} diff --git a/src/content/ItemDef.h b/src/content/ItemDef.h new file mode 100644 index 00000000..2223c1e0 --- /dev/null +++ b/src/content/ItemDef.h @@ -0,0 +1,25 @@ +#ifndef CONTENT_ITEM_DEF_H_ +#define CONTENT_ITEM_DEF_H_ + +#include +#include + +#include "../typedefs.h" + +struct item_funcs_set { + bool init: 1; +}; + +class ItemDef { +public: + std::string name; + + struct { + itemid_t id; + item_funcs_set funcsset {}; + } rt; + + ItemDef(std::string name); +}; + +#endif //CONTENT_ITEM_DEF_H_ diff --git a/src/files/WorldFiles.cpp b/src/files/WorldFiles.cpp index 10ceb1ec..254e3188 100644 --- a/src/files/WorldFiles.cpp +++ b/src/files/WorldFiles.cpp @@ -18,6 +18,8 @@ #include "../coders/json.h" #include "../constants.h" +#include "../content/ItemDef.h" + #include #include #include @@ -461,12 +463,21 @@ void WorldFiles::writePacks(const World* world) { void WorldFiles::writeIndices(const ContentIndices* indices) { json::JObject root; + uint count; json::JArray& blocks = root.putArray("blocks"); - uint count = indices->countBlockDefs(); + count = indices->countBlockDefs(); for (uint i = 0; i < count; i++) { const Block* def = indices->getBlockDef(i); blocks.put(def->name); } + + json::JArray& items = root.putArray("items"); + count = indices->countItemDefs(); + for (uint i = 0; i < count; i++) { + const ItemDef* def = indices->getItemDef(i); + items.put(def->name); + } + files::write_string(getIndicesFile(), json::stringify(&root, true, " ")); } diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index e4e1c355..d88c5d60 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -9,6 +9,7 @@ #include "../../util/timeutil.h" #include "../../world/Level.h" #include "../../voxels/Block.h" +#include "../../content/ItemDef.h" #include "api_lua.h" using namespace scripting; @@ -131,6 +132,8 @@ void scripting::on_block_interact(Player* player, const Block* block, int x, int call_func(L, 3, name); } +// todo: refactor + void scripting::load_block_script(std::string prefix, fs::path file, block_funcs_set* funcsset) { std::string src = files::read_string(file); std::cout << "loading script " << file.u8string() << std::endl; @@ -147,6 +150,17 @@ void scripting::load_block_script(std::string prefix, fs::path file, block_funcs funcsset->oninteract=rename_global(L, "on_interact", (prefix+".oninteract").c_str()); } +void scripting::load_item_script(std::string prefix, fs::path file, item_funcs_set* funcsset) { + std::string src = files::read_string(file); + std::cout << "loading script " << file.u8string() << std::endl; + if (luaL_loadbuffer(L, src.c_str(), src.size(), file.string().c_str())) { + std::cerr << "Lua error:" << lua_tostring(L,-1) << std::endl; + return; + } + call_func(L, 0, "