diff --git a/include/Cubed/gameplay/block.hpp b/include/Cubed/gameplay/block.hpp index 4411097..18c1053 100644 --- a/include/Cubed/gameplay/block.hpp +++ b/include/Cubed/gameplay/block.hpp @@ -1,5 +1,7 @@ #pragma once +#include "Cubed/tools/resource_location.hpp" + #include #include #include @@ -41,7 +43,7 @@ struct LookBlock { }; struct BlockData { - std::string name; + ResourceLocation name{}; BlockType id = 0; bool is_liquid = false; @@ -55,14 +57,7 @@ struct BlockData { bool is_blend = false; bool is_transitional = false; float roughness = 1.0f; - BlockData(std::string_view b_name, BlockType b_id, bool liquid, - bool passable, bool cross_plane, bool transparent, bool gas, - bool discard, bool blend, bool transitional, float r) - : name(b_name), id(b_id), is_liquid(liquid), is_gas(gas), - is_passable(passable), is_cross_plane(cross_plane), - is_transparent(transparent), is_discard(discard), is_blend(blend), - is_transitional(transitional), roughness(r) {} - BlockData() { name = ""; } + BlockData() = default; }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/block_manager.hpp b/include/Cubed/gameplay/block_manager.hpp index 4fb8e28..c29f6a4 100644 --- a/include/Cubed/gameplay/block_manager.hpp +++ b/include/Cubed/gameplay/block_manager.hpp @@ -10,7 +10,7 @@ public: static void init(); static unsigned sums(); static unsigned cross_plane_sum(); - static const std::string& name_form_id(BlockType id); + static const ResourceLocation& name_form_id(BlockType id); static bool is_gas(BlockType id); static bool is_liquid(BlockType id); @@ -24,13 +24,15 @@ public: static float roughness(BlockType id); static BlockType cross_plane_index(BlockType id); - static BlockType id_from_name(const std::string& name); + static BlockType id_from_name(std::string_view name); + static BlockType id_from_name(const ResourceLocation& name); private: using BlockMap = tbb::concurrent_hash_map; using acc = BlockMap::accessor; using cacc = BlockMap::const_accessor; - using IDMap = tbb::concurrent_hash_map; + using IDMap = tbb::concurrent_hash_map; using CrossPlaneMap = tbb::concurrent_hash_map; static inline const BlockData EMPTY; diff --git a/include/Cubed/tools/json_utils.hpp b/include/Cubed/tools/json_utils.hpp index 043742c..41c152d 100644 --- a/include/Cubed/tools/json_utils.hpp +++ b/include/Cubed/tools/json_utils.hpp @@ -1,8 +1,72 @@ +#pragma once +#include "Cubed/tools/log.hpp" +#include #include #include +#include #include -namespace Tools { -std::unordered_map +namespace Cubed::Tools { + +namespace detail { +template inline constexpr bool always_false_v = false; // NOLINT +} // namespace detail + +inline std::unordered_map doc_to_map(const rapidjson::Document& doc); -} \ No newline at end of file + +bool parse_json(rapidjson::Document& doc, const std::filesystem::path& path); + +template +bool get_json_value(const rapidjson::Value& value, const char* key, T& out) { + using ValueType = std::decay_t; + if (!value.HasMember(key)) { + Logger::error("json don't has key {}", key); + return false; + } + const auto& v = value[key]; + if constexpr (std::is_same_v) { + if (!v.IsBool()) { + Logger::error("json key {} value is not bool", key); + return false; + } + out = v.GetBool(); + } else if constexpr (std::is_same_v) { + if (!v.IsString()) { + Logger::error("json key {} value is not string", key); + return false; + } + out = v.GetString(); + } else if constexpr (std::is_same_v) { + if (!v.IsNumber()) { + Logger::error("json key {} value is not number", key); + return false; + } + out = v.GetFloat(); + } else if constexpr (std::is_same_v) { + if (!v.IsNumber()) { + Logger::error("json key {} value is not number", key); + return false; + } + out = v.GetDouble(); + } else if constexpr (std::is_integral_v && + std::is_unsigned_v) { + if (!v.IsUint64()) { + Logger::error("json key {} value is not uint", key); + return false; + } + out = static_cast(v.GetUint64()); + } else if constexpr (std::is_integral_v && + std::is_signed_v) { + if (!v.IsInt64()) { + Logger::error("json key {} value is not int", key); + return false; + } + out = static_cast(v.GetInt64()); + } else { + static_assert(detail::always_false_v, + "get_json_value: unsupported type"); + } + return true; +} +} // namespace Cubed::Tools \ No newline at end of file diff --git a/include/Cubed/tools/resource_location.hpp b/include/Cubed/tools/resource_location.hpp index 2bec44c..c1a13b4 100644 --- a/include/Cubed/tools/resource_location.hpp +++ b/include/Cubed/tools/resource_location.hpp @@ -28,9 +28,30 @@ struct ResourceLocation { std::string(str.substr(it + 1))}; } + static std::string get_assets_path(std::string_view ns) { + if (ns == "cubed") { + return ASSETS_PATH "cubed"; + } + return std::string(ns); + } + bool operator==(const ResourceLocation& o) const { return (ns == o.ns) && (path == o.path); } - std::size_t hash() const { return std::hash()(to_string()); } + struct Hash { + std::size_t hash(const ResourceLocation& p) const { + return ResourceLocation::hash(p); + } + bool equal(const ResourceLocation& a, const ResourceLocation& b) const { + return a == b; + } + }; + + static std::size_t hash(const ResourceLocation& p) { + return std::hash()(p.to_string()); + } + + std::string assets_path() const { return get_assets_path(ns); } }; + } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/block_manager.cpp b/src/gameplay/block_manager.cpp index 03c6527..71b1c32 100644 --- a/src/gameplay/block_manager.cpp +++ b/src/gameplay/block_manager.cpp @@ -1,20 +1,16 @@ #include "Cubed/gameplay/block_manager.hpp" #include "Cubed/tools/cubed_assert.hpp" +#include "Cubed/tools/json_utils.hpp" #include "Cubed/tools/log.hpp" -#include "Cubed/tools/toml.utils.hpp" +#include "Cubed/tools/resource_location.hpp" #include #include - +#include namespace fs = std::filesystem; - +using namespace rapidjson; using namespace std::string_literals; -using namespace Cubed::TOML; -namespace { -std::string block_data_dir = ASSETS_PATH + "data/block"s; - -} // namespace namespace Cubed { @@ -27,7 +23,7 @@ unsigned BlockManager::cross_plane_sum() { return m_cross_plane_map.size(); } -const std::string& BlockManager::name_form_id(BlockType id) { +const ResourceLocation& BlockManager::name_form_id(BlockType id) { cacc c; if (!m_datas.find(c, id)) { ASSERT(false); @@ -113,62 +109,91 @@ float BlockManager::roughness(BlockType id) { } void BlockManager::init() { - fs::path data_path{block_data_dir}; + fs::path root_path{ + ResourceLocation::get_assets_path(ResourceLocation::DEFAULT_NAMESPACE)}; + fs::path block_path = root_path / "blocks"; + + fs::create_directories(block_path); + + fs::path register_path = root_path / "registry.json"; + + Document registry; + if (!Tools::parse_json(registry, register_path)) { + Logger::error("Can't parse registry.json"); + ASSERT(false); + return; + } + + if (!registry.HasMember("blocks")) { + throw std::runtime_error("registry.json don't has blocks key"); + } + + auto& blocks_registry = registry["blocks"]; + std::vector> types; - for (auto entry : fs::recursive_directory_iterator(data_path)) { + + for (auto entry : fs::recursive_directory_iterator(block_path)) { if (!entry.is_regular_file()) { continue; } - if (entry.path().filename() == "template.toml") { + if (entry.path().extension() != ".json") { continue; } - toml::table block; - try { - block = toml::parse_file(entry.path().string()); - } catch (const toml::parse_error& err) { - Logger::error("Load Block Data {} Fail, Parser Error {}", - entry.path().string(), err.what()); - ASSERT(false); + + Document doc; + if (!Tools::parse_json(doc, entry.path())) { + continue; } - auto id = block["id"].value(); - if (id == std::nullopt) { - Logger::error("Very Serious Error, Block Id Not Find !!!, Please " - "Check The Block Data Integrity"); - std::abort(); - } - auto name = block["name"].value(); - if (name == std::nullopt) { + + BlockData data; + + std::string path; + if (!Tools::get_json_value(doc, "name", path)) { Logger::error("Very Serious Error, Block Name Not Find !!!, Please " "Check The Block Data Integrity"); - std::abort(); + continue; } - auto is_liquid = safe_get_value(block, "is_liquid", false); - auto is_passable = safe_get_value(block, "is_passable", false); - auto is_cross_plane = safe_get_value(block, "is_cross_plane", false); - auto is_transparent = safe_get_value(block, "is_transparent", false); - auto is_gas = safe_get_value(block, "is_gas", false); - auto is_discard = safe_get_value(block, "is_discard", false); - auto is_blend = safe_get_value(block, "is_blend", false); - auto is_transitional = safe_get_value(block, "is_transitional", false); - auto roughness = safe_get_value(block, "roughness", 1.0); - - BlockData data{*name, - static_cast(*id), - *is_liquid, - *is_passable, - *is_cross_plane, - *is_transparent, - *is_gas, - *is_discard, - *is_blend, - *is_transitional, - static_cast(*roughness)}; - - if (!m_datas.emplace(static_cast(*id), std::move(data))) { - Logger::error("Block Type {} already exist!", *id); + data.name.path = path; + data.name.ns = ResourceLocation::DEFAULT_NAMESPACE; + if (!Tools::get_json_value(blocks_registry, path.c_str(), data.id)) { + Logger::error("Very Serious Error, Block Id Not Find !!!, Please " + "Check The Block Data Integrity"); + continue; } - m_id_map.emplace(*name, static_cast(*id)); - types.emplace_back(*is_cross_plane, static_cast(*id)); + + if (!doc.HasMember("properties")) { + Logger::error("Block {} doesn't have properties", + data.name.to_string()); + continue; + } + if (!doc["properties"].IsObject()) { + Logger::error("Block {} properties are not json object", + data.name.to_string()); + continue; + } + auto& properties = doc["properties"]; + + Tools::get_json_value(properties, "is_liquid", data.is_liquid); + Tools::get_json_value(properties, "is_passable", data.is_passable); + Tools::get_json_value(properties, "is_cross_plane", + data.is_cross_plane); + Tools::get_json_value(properties, "is_transparent", + data.is_transparent); + Tools::get_json_value(properties, "is_gas", data.is_gas); + Tools::get_json_value(properties, "is_discard", data.is_discard); + Tools::get_json_value(properties, "is_blend", data.is_blend); + Tools::get_json_value(properties, "is_transitional", + data.is_transitional); + Tools::get_json_value(properties, "roughness", data.roughness); + + const auto LOCATION = data.name; + const auto IS_CROSS_PLANE = data.is_cross_plane; + const auto ID = data.id; + if (!m_datas.emplace(data.id, std::move(data))) { + Logger::error("Block {} already exist!", LOCATION.to_string()); + } + m_id_map.emplace(LOCATION, ID); + types.emplace_back(IS_CROSS_PLANE, ID); } std::sort(types.begin(), types.end(), [](const auto& a, const auto& b) { return a.second < b.second; }); @@ -188,12 +213,20 @@ BlockType BlockManager::cross_plane_index(BlockType id) { return c->second; } -BlockType BlockManager::id_from_name(const std::string& name) { +BlockType BlockManager::id_from_name(std::string_view name) { + auto s = ResourceLocation::parse(name); + if (s) { + return id_from_name(*s); + } + return 0; +} + +BlockType BlockManager::id_from_name(const ResourceLocation& name) { IDMap::const_accessor c; if (m_id_map.find(c, name)) { return c->second; } - Logger::error("BlockManager: Can't fin Block {}", name); + Logger::error("BlockManager: Can't fin Block {}", name.to_string()); ASSERT(false); return 0; } diff --git a/src/gameplay/server_world.cpp b/src/gameplay/server_world.cpp index 5fae431..b9b078e 100644 --- a/src/gameplay/server_world.cpp +++ b/src/gameplay/server_world.cpp @@ -4,6 +4,7 @@ #include "Cubed/gameplay/packet.hpp" #include "Cubed/gameplay/session.hpp" #include "Cubed/tools/cubed_assert.hpp" +#include "Cubed/tools/json_utils.hpp" #include "Cubed/tools/log.hpp" #include "Cubed/tools/math_tools.hpp" #include "Cubed/tools/net_utils.hpp" @@ -12,7 +13,6 @@ #include #include -#include #include using namespace std::chrono; using namespace std::chrono_literals; @@ -210,15 +210,13 @@ void ServerWorld::init_world(RunMode mode) { Logger::info("sensitive filter {}", m_enable_filter.load()); m_voice_chat = m_config.get("voice_chat", true); Logger::info("voice chat {}", m_voice_chat.load()); + try { fs::path path = std::format("{}SensitiveLexicon.json", ASSETS_PATH); - std::ifstream s{path}; - if (!s.is_open()) { - throw std::runtime_error("can't open SensitiveLexicon.json"); - } - IStreamWrapper isw(s); Document doc; - doc.ParseStream(isw); + if (!Tools::parse_json(doc, path)) { + throw std::runtime_error("Can't parse SensitiveLexicon.json"); + } m_filter.load(doc); } catch (const std::exception& e) { Logger::error("Load SensitiveLexicon.json Fail"); diff --git a/src/tools/json_utils.cpp b/src/tools/json_utils.cpp index 69b48d3..0814aef 100644 --- a/src/tools/json_utils.cpp +++ b/src/tools/json_utils.cpp @@ -1,15 +1,20 @@ #include "Cubed/tools/json_utils.hpp" +#include "Cubed/tools/log.hpp" + +#include #include +#include #include #include #include #include -namespace Tools { +namespace Cubed::Tools { using rapidjson::Document; +using rapidjson::IStreamWrapper; using rapidjson::Value; - +namespace fs = std::filesystem; namespace { std::string serialize_value(const Value& v, rapidjson::StringBuffer& buf) { rapidjson::Writer w(buf); @@ -51,4 +56,23 @@ std::unordered_map doc_to_map(const Document& doc) { } return m; } -} // namespace Tools \ No newline at end of file + +bool parse_json(rapidjson::Document& doc, const std::filesystem::path& path) { + + std::ifstream file{path}; + if (!file.is_open()) { + Logger::error("Can't parse json {}", path.string()); + return false; + } + IStreamWrapper isw{file}; + doc.ParseStream(isw); + if (doc.HasParseError()) { + auto code = doc.GetParseError(); + Logger::error("Parse {} failed, error code {}", path.string(), + static_cast(code)); + return false; + } + return true; +} + +} // namespace Cubed::Tools \ No newline at end of file