diff --git a/include/asset_loader.h b/include/asset_loader.h index 49c71949..7807d019 100644 --- a/include/asset_loader.h +++ b/include/asset_loader.h @@ -34,12 +34,18 @@ public: MODEL_FOLDER_NOT_FOUND, TEXTURE_FOLDER_NOT_FOUND, TAGS_FOLDER_NOT_FOUND, - INCORRECT_NAMESPACE + TAGS_BLOCKSTATES_NOT_FOUND, + INVALID_BLOCKSTATE_FORMAT, + INCORRECT_NAMESPACE, + INCORRECT_TAG_FILE }; load_failure_t(const type_t type): type{type} // NOLINT {} + load_failure_t(const type_t type, const std::string& message): type{type}, message{message} + {} + operator type_t() const // NOLINT { return type; @@ -50,24 +56,28 @@ public: switch (type) { case ASSET_FOLDER_NOT_FOUND: - return "Asset folder could not be found"; + return "Asset folder could not be found. " + message.value_or(""); case MODEL_FOLDER_NOT_FOUND: - return "Model folder could not be found"; + return "Model folder could not be found. " + message.value_or(""); case TEXTURE_FOLDER_NOT_FOUND: - return "Texture folder could not be found"; + return "Texture folder could not be found. " + message.value_or(""); case TAGS_FOLDER_NOT_FOUND: - return "Tags folder could not be found"; + return "Tags folder could not be found. " + message.value_or(""); case INCORRECT_NAMESPACE: - return "Namespace names of models, textures, or data files do not match!"; + return "Namespace names of models, textures, or data files do not match! " + message.value_or(""); + case INVALID_BLOCKSTATE_FORMAT: + return "Blockstate json file is not structured correctly. " + message.value_or(""); default: - return "Unknown failure type"; + return "Unknown failure type. " + message.value_or(""); } } private: type_t type; + std::optional message; }; + struct namespaced_object { std::string namespace_str; @@ -85,9 +95,16 @@ struct model_data_t std::optional> textures; }; +struct tag_data_t +{ + blt::hashset_t list; + blt::hashmap_t> models; +}; + struct namespace_data_t { blt::hashmap_t models; + blt::hashmap_t tags; std::string asset_namespace_folder; std::string data_namespace_folder; @@ -102,7 +119,7 @@ struct asset_data_t { blt::hashmap_t json_data; blt::hashmap_t> solid_textures_to_load; - blt::hashmap_t> non_solid_textures_to_load; + blt::hashmap_t> non_solid_textures_to_load; [[nodiscard]] std::vector resolve_parents(const namespaced_object& model) const; }; diff --git a/src/asset_loader.cpp b/src/asset_loader.cpp index 6112f1ce..6c03f106 100644 --- a/src/asset_loader.cpp +++ b/src/asset_loader.cpp @@ -31,8 +31,63 @@ using json = nlohmann::json; inline namespaced_object empty_object{"NULL", "NULL"}; -asset_loader_t::asset_loader_t(std::string name): - db{name + ".assets"}, name{std::move(name)} +struct search_for_t +{ + search_for_t(const json& obj, std::string search_tag): search_tag{std::move(search_tag)} + { + objects.push_back(obj); + } + + std::optional next() + { + while (true) + { + if (objects.empty()) + return {}; + if (!current_iter) + current_iter = objects.front().items().begin(); + + while (true) + { + if (iter() == objects.front().items().end()) + { + objects.erase(objects.begin()); + current_iter = {}; + break; + } + auto value = iter().value(); + if (value.is_array() || value.is_object()) + { + const auto distance = std::distance(objects.front().items().begin(), iter()); + objects.push_back(value); + current_iter = objects.front().items().begin(); + std::advance(*current_iter, distance + 1); + continue; + } + if (iter().key() != search_tag) + { + ++*current_iter; + continue; + } + auto str = iter().value().get(); + ++*current_iter; + return str; + } + } + } + +private: + nlohmann::detail::iteration_proxy_value& iter() + { + return *current_iter; + } + + std::vector objects; + std::optional> current_iter; + std::string search_tag; +}; + +asset_loader_t::asset_loader_t(std::string name): db{name + ".assets"}, name{std::move(name)} {} std::optional asset_loader_t::load_assets(const std::string& asset_folder, const std::optional& data_folder) @@ -43,6 +98,7 @@ std::optional asset_loader_t::load_assets(const std::string& ass std::optional model_folder; std::optional texture_folder; std::optional tags_folder; + std::optional blockstate_folder; for (const auto& entry : std::filesystem::recursive_directory_iterator(asset_folder)) { @@ -56,6 +112,10 @@ std::optional asset_loader_t::load_assets(const std::string& ass { texture_folder = entry.path(); } + if (data_folder && entry.path().filename().compare("blockstates") == 0) + { + blockstate_folder = entry.path(); + } } if (!model_folder) @@ -92,6 +152,9 @@ std::optional asset_loader_t::load_assets(const std::string& ass if (data_folder && !exists(*tags_folder / "block")) return load_failure_t::TAGS_FOLDER_NOT_FOUND; + if (data_folder && !blockstate_folder) + return load_failure_t::TAGS_BLOCKSTATES_NOT_FOUND; + if (data_folder) { data.json_data[namespace_name.string()].tag_folder = tags_folder->string(); @@ -176,10 +239,13 @@ std::optional asset_loader_t::load_assets(const std::string& ass continue; if (!entry.is_regular_file()) continue; - if (entry.path().extension().compare(".png") != 0 && - entry.path().extension().compare(".jpg") != 0 && - entry.path().extension().compare(".jpeg") != 0 && - entry.path().extension().compare(".bmp") != 0) + if (entry.path().extension().compare(".png") != 0 && entry.path().extension().compare(".jpg") != 0 && entry.path().extension(). + compare(".jpeg") != 0 && entry. + path(). + extension() + .compare( + ".bmp") + != 0) continue; std::filesystem::path relative_path; { @@ -200,6 +266,65 @@ std::optional asset_loader_t::load_assets(const std::string& ass auto& map = data.json_data[namespace_name.string()]; + if (data_folder) + { + for (const auto& entry : std::filesystem::recursive_directory_iterator{*tags_folder / "block"}) + { + if (!entry.path().has_extension()) + continue; + if (!entry.is_regular_file()) + continue; + if (entry.path().extension().compare(".json") != 0) + continue; + std::filesystem::path relative_path; + { + auto p_begin = entry.path().begin(); + auto p_end = entry.path().end(); + auto m_begin = tags_folder->begin(); + while (p_begin != p_end && m_begin != tags_folder->end() && *p_begin == *m_begin) + { + ++p_begin; + ++m_begin; + } + for (; p_begin != p_end; ++p_begin) + relative_path /= p_begin->stem(); + } + std::ifstream json_file{entry.path()}; + json jdata = json::parse(json_file); + if (!jdata.contains("values")) + return load_failure_t{load_failure_t::INCORRECT_TAG_FILE, "Failed at file: " + entry.path().string()}; + + auto& tag_value_list = data.json_data[namespace_name.string()].tags[relative_path.string()].list; + + for (const auto& v : jdata["values"]) + tag_value_list.insert(v.get()); + } + + // blockstate folder consists of only json files + for (const auto& entry : std::filesystem::recursive_directory_iterator{*blockstate_folder}) + { + if (!entry.is_regular_file()) + continue; + if (!(entry.path().has_extension() && entry.path().extension().compare("json"))) + continue; + auto block_name = entry.path().stem().string(); + + std::ifstream json_file(entry.path()); + json jdata = json::parse(json_file); + + search_for_t search{jdata, "model"}; + while (auto next = search.next()) + { + const auto& model = *next; + const auto parts = blt::string::split(model, ':'); + if (parts.size() == 1) + data.json_data[namespace_name.string()].tags[block_name].models[namespace_name.string()].insert(parts[0]); + else + data.json_data[namespace_name.string()].tags[block_name].models[parts[0]].insert(parts[1]); + } + } + } + std::vector textures_to_load; for (auto& [name, model] : map.models) @@ -267,6 +392,45 @@ database_t& asset_loader_t::load_textures() process_texture(insert_non_solid_stmt, namespace_str, texture); } + auto tag_table = db.builder().create_table("tags"); + tag_table.with_column("namespace").primary_key(); + tag_table.with_column("tag").primary_key(); + tag_table.with_column("block").primary_key(); + tag_table.build().execute(); + + auto tag_models = db.builder().create_table("tag_models"); + tag_models.with_column("namespace").primary_key().foreign_key("tags", "namespace"); + tag_models.with_column("tag").primary_key().foreign_key("tags", "tag"); + tag_models.with_column("model_namespace").primary_key(); + tag_models.with_column("model").primary_key(); + tag_models.build().execute(); + + const static auto insert_tag_sql = "INSERT INTO tags VALUES (?, ?, ?)"; + const static auto insert_tag_model_sql = "INSERT INTO tag_models VALUES (?, ?, ?, ?)"; + const auto insert_tag_stmt = db.prepare(insert_tag_sql); + const auto insert_tag_model_stmt = db.prepare(insert_tag_model_sql); + for (const auto& [namespace_str, jdata] : data.json_data) + { + for (const auto& [tag_name, tag_data] : jdata.tags) + { + for (const auto& block_tag : tag_data.list) + { + insert_tag_stmt.bind().bind_all(namespace_str, tag_name, block_tag); + if (!insert_tag_stmt.execute()) + BLT_WARN("[Tag List] Unable to insert {} into {}:{} reason '{}'", block_tag, namespace_str, tag_name, db.get_error()); + } + for (const auto& [model_namespace, model_list] : tag_data.models) + { + for (const auto& model : model_list) + { + insert_tag_model_stmt.bind().bind_all(namespace_str, tag_name, model_namespace, model); + if (!insert_tag_model_stmt.execute()) + BLT_WARN("[Model List] Unable to insert {}:{} into {}:{} reason '{}'", namespace_str, tag_name, model_namespace, model, db.get_error()); + } + } + } + } + return db; } diff --git a/src/sql.cpp b/src/sql.cpp index c9d83f86..48d3c19a 100644 --- a/src/sql.cpp +++ b/src/sql.cpp @@ -66,7 +66,7 @@ statement_t table_builder_t::build() for (const auto& [i, key] : blt::enumerate(foreign_keys)) { sql += ", FOREIGN KEY ("; - sql += key.local_name + " REFERENCES " + key.foreign_table + "(" + key.foreign_name + ")"; + sql += key.local_name + ") REFERENCES " + key.foreign_table + "(" + key.foreign_name + ")"; } } sql += ");";