From 2358cfd9a4ce7669db96fb05570d611f204b5b81 Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Tue, 4 Aug 2026 14:24:10 +0800 Subject: [PATCH] feat(item): add item manager with JSON asset loading Implement ItemManager to load and query item definitions from assets/item JSON files. Add ItemData struct, item asset metadata, CMake source registration, and initialize the manager during app startup. Also add AGENTS.md repository guidelines. --- AGENTS.md | 251 ++++++++++++++++++++++++ assets/item/air.json | 5 + assets/item/dirt.json | 6 + assets/item/grass.json | 6 + assets/item/grass_block.json | 6 + assets/item/leaf.json | 6 + assets/item/log.json | 6 + assets/item/sand.json | 6 + assets/item/snowy_grass_block.json | 6 + assets/item/stone.json | 6 + assets/item/water.json | 6 + include/Cubed/gameplay/item.hpp | 14 ++ include/Cubed/gameplay/item_manager.hpp | 36 ++++ src/CMakeLists.txt | 1 + src/app.cpp | 3 +- src/gameplay/item_manager.cpp | 106 ++++++++++ 16 files changed, 469 insertions(+), 1 deletion(-) create mode 100644 AGENTS.md create mode 100644 assets/item/air.json create mode 100644 assets/item/dirt.json create mode 100644 assets/item/grass.json create mode 100644 assets/item/grass_block.json create mode 100644 assets/item/leaf.json create mode 100644 assets/item/log.json create mode 100644 assets/item/sand.json create mode 100644 assets/item/snowy_grass_block.json create mode 100644 assets/item/stone.json create mode 100644 assets/item/water.json create mode 100644 include/Cubed/gameplay/item.hpp create mode 100644 include/Cubed/gameplay/item_manager.hpp create mode 100644 src/gameplay/item_manager.cpp diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..a6457c9 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,251 @@ +# Code Modification Guidelines + +## 1. Git Branch Protection + +- Never modify code directly on the `main` branch. +- Before editing, check the current branch: + +```bash +git branch --show-current +```` + +* If the result is `main`, create or switch to a development branch first: + +```bash +git checkout -b feature/xxx +``` + +* Do not bypass branch protection by: + + * Committing directly to `main`. + * Running code generation, formatting, or large refactors on `main`. + * Rewriting or damaging `main` history. + +--- + +## 2. Code Comments + +* All new comments must be written in English. +* Comments must be: + + * Short and clear. + * Explain the purpose, not repeat the code. + * Avoid unnecessary details. + +Recommended: + +```cpp +// AI-generated: Prevent stale handle access. +``` + +Avoid: + +```cpp +// AI-generated: This checks if the handle is invalid because... +``` + +* Important AI-added or modified logic should include: + +```cpp +// AI-generated +``` + +* Do not add comments for trivial changes such as: + + * One or two line logic fixes. + * Spelling corrections. + * Simple variable renames. + +### 2.1 Do not write excessive or granular comments + +AI must avoid sprinkling small explanatory comments throughout code. +Specifically: + +* **No line-by-line narration.** Do not add a comment above (or beside) + every field, signal, slot, branch, or block just to restate what it is. + If the name is self-explanatory, no comment is needed. +* **No multi-part explanations on a single marker.** A line like + `// AI-generated: abort the in-flight download; finished handler clears state.` + is too much. Either drop it, or shorten to a single short clause + (e.g. `// AI-generated: abort in-flight download.`). +* **Prefer zero or one comment per file/section**, not many. A short + header comment explaining the file's purpose is acceptable; dozens of + inline labels are not. +* **Do not comment obvious QML/C++ bindings, layout splits, or default + values** (e.g. `// AI-generated: index 0 -> zh_CN, index 1 -> en.`). + The code already says that. +* **When in doubt, omit the comment.** A missing comment is fine; a + redundant one is noise that future maintainers must clean up. + +Rule of thumb: if removing the comment would not lose useful +information, remove it. + +--- + +## 3. Project Style + +Follow the existing project style strictly: + +* Naming conventions. +* File organization. +* Formatting rules. +* Include order. +* Existing architecture. + +Do not: + +* Introduce unrelated coding styles. +* Add unnecessary design patterns. +* Create duplicate systems. +* Add abstractions without need. +* Change module responsibilities. + +--- + +## 4. Modification Scope + +Only modify the minimum code required. + +Do not: + +* Format unrelated files. +* Remove existing comments. +* Rename large numbers of symbols. +* Change public APIs. +* Perform large refactors without confirmation. + +If large changes are required, explain first: + +1. Current problem. +2. Required changes. +3. Affected modules. +4. Expected impact. + +--- + +## 5. Build and Test + +After modifications: + +* Verify compilation. +* Check for new warnings. +* Ensure existing features still work. + +Do not: + +* Commit without verification. +* Ignore compiler errors. +* Hide problems with temporary hacks. + +--- + +## 6. Compatibility + +Consider: + +* Existing callers. +* API/ABI compatibility. +* Serialization formats. +* Network protocols. +* Save data. +* Threading impact. + +Do not: + +* Change network structures without compatibility handling. +* Break old data formats. +* Change public data layouts carelessly. + +--- + +## 7. Multi-threading Safety + +For multi-threaded code, check: + +* Data races. +* Object lifetime. +* Lock contention. +* Atomic correctness. +* Cross-thread resource access. + +Do not: + +* Assume single-threaded execution. +* Modify shared data without protection. +* Add hidden locks that hurt performance. + +--- + +## 8. Memory Safety + +Consider: + +* Ownership. +* Lifetime. +* RAII. +* Memory leaks. +* Dangling references. +* Iterator invalidation. + +Do not: + +* Return references to local variables. +* Store pointers to temporary objects. +* Use uninitialized data. +* Introduce undefined behavior. + +--- + +## 9. Third-party Libraries + +Before adding or changing dependencies: + +Check: + +* Existing project dependencies. +* Build system impact. +* Maintenance cost. + +Do not: + +* Add large libraries for simple features. +* Duplicate existing library functionality. +* Modify third-party source code. + +--- + +## 10. AI Behavior Rules + +AI-generated code must: + +* Reuse existing code first. +* Preserve current architecture. +* Prefer simple solutions. +* Minimize risk and code changes. + +Do not: + +* Guess requirements. +* Add unrequested features. +* Redesign architecture. +* Add unnecessary abstraction layers. + +Choose the implementation with: + +> The smallest change, lowest risk, and best compatibility with existing code. + +--- + +## 11. Pre-commit Checklist + +Before committing: + +* [ ] Not on `main`. +* [ ] Changes match the requested task. +* [ ] New comments are in English. +* [ ] AI changes are marked when needed. +* [ ] No unrelated formatting changes. +* [ ] Build passes. +* [ ] No obvious performance/security/thread issues. +* [ ] No API, protocol, or data format breakage. + diff --git a/assets/item/air.json b/assets/item/air.json new file mode 100644 index 0000000..8fe69c8 --- /dev/null +++ b/assets/item/air.json @@ -0,0 +1,5 @@ +{ + "id": 0, + "name": "air", + "description": "" +} \ No newline at end of file diff --git a/assets/item/dirt.json b/assets/item/dirt.json new file mode 100644 index 0000000..acd1206 --- /dev/null +++ b/assets/item/dirt.json @@ -0,0 +1,6 @@ +{ + "id": 2, + "name": "dirt", + "description": "", + "texture": "texture/item/block/dirt.png" +} diff --git a/assets/item/grass.json b/assets/item/grass.json new file mode 100644 index 0000000..8ff4b76 --- /dev/null +++ b/assets/item/grass.json @@ -0,0 +1,6 @@ +{ + "id": 9, + "name": "grass", + "description": "", + "texture": "texture/item/block/grass.png" +} diff --git a/assets/item/grass_block.json b/assets/item/grass_block.json new file mode 100644 index 0000000..781bc8a --- /dev/null +++ b/assets/item/grass_block.json @@ -0,0 +1,6 @@ +{ + "id": 1, + "name": "grass_block", + "description": "", + "texture": "texture/item/block/grass_block.png" +} \ No newline at end of file diff --git a/assets/item/leaf.json b/assets/item/leaf.json new file mode 100644 index 0000000..59a5257 --- /dev/null +++ b/assets/item/leaf.json @@ -0,0 +1,6 @@ +{ + "id": 6, + "name": "leaf", + "description": "", + "texture": "texture/item/block/leaf.png" +} diff --git a/assets/item/log.json b/assets/item/log.json new file mode 100644 index 0000000..2a6f38b --- /dev/null +++ b/assets/item/log.json @@ -0,0 +1,6 @@ +{ + "id": 5, + "name": "log", + "description": "", + "texture": "texture/item/block/log.png" +} diff --git a/assets/item/sand.json b/assets/item/sand.json new file mode 100644 index 0000000..59a506e --- /dev/null +++ b/assets/item/sand.json @@ -0,0 +1,6 @@ +{ + "id": 4, + "name": "sand", + "description": "", + "texture": "texture/item/block/sand.png" +} diff --git a/assets/item/snowy_grass_block.json b/assets/item/snowy_grass_block.json new file mode 100644 index 0000000..27f4382 --- /dev/null +++ b/assets/item/snowy_grass_block.json @@ -0,0 +1,6 @@ +{ + "id": 8, + "name": "snowy_grass_block", + "description": "", + "texture": "texture/item/block/snowy_grass_block.png" +} diff --git a/assets/item/stone.json b/assets/item/stone.json new file mode 100644 index 0000000..1ad7bd5 --- /dev/null +++ b/assets/item/stone.json @@ -0,0 +1,6 @@ +{ + "id": 3, + "name": "stone", + "description": "", + "texture": "texture/item/block/stone.png" +} diff --git a/assets/item/water.json b/assets/item/water.json new file mode 100644 index 0000000..25b6318 --- /dev/null +++ b/assets/item/water.json @@ -0,0 +1,6 @@ +{ + "id": 7, + "name": "water", + "description": "", + "texture": "texture/item/block/water.png" +} diff --git a/include/Cubed/gameplay/item.hpp b/include/Cubed/gameplay/item.hpp new file mode 100644 index 0000000..922ee8c --- /dev/null +++ b/include/Cubed/gameplay/item.hpp @@ -0,0 +1,14 @@ +#pragma once +#include +#include +namespace Cubed { +using ItemID = uint16_t; + +struct ItemData { + ItemID id = 0; + std::string name; + std::string description; + std::string path; +}; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/item_manager.hpp b/include/Cubed/gameplay/item_manager.hpp new file mode 100644 index 0000000..00058bb --- /dev/null +++ b/include/Cubed/gameplay/item_manager.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "Cubed/gameplay/item.hpp" + +#include +#include +#include +namespace Cubed { +class ItemManager { +public: + ItemManager(); + void init(); + static ItemManager& instance(); + + static const ItemData& get(std::string_view key); + static const ItemData& get(ItemID id); + + const ItemData& get_item_data(std::string_view key) const; + const ItemData& get_item_data(ItemID id) const; + +private: + using ItemMap = tbb::concurrent_hash_map; + using acc = ItemMap::accessor; + using cacc = ItemMap::const_accessor; + + using IDMap = tbb::concurrent_hash_map; + + void add(const std::filesystem::path& path); + + ItemMap m_map; + + IDMap m_id_map; + + static constexpr ItemData EMPTY; +}; +} // namespace Cubed \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 99ce13c..c0494f5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -104,4 +104,5 @@ target_sources(${PROJECT_NAME} gameplay/client_entity_manager.cpp gameplay/systems/wander_ai_system.cpp tools/json_utils.cpp + gameplay/item_manager.cpp ) \ No newline at end of file diff --git a/src/app.cpp b/src/app.cpp index 1abc51a..cde9eb5 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -2,6 +2,7 @@ #include "Cubed/config.hpp" #include "Cubed/debug_collector.hpp" +#include "Cubed/gameplay/item_manager.hpp" #include "Cubed/localization.hpp" #include "Cubed/tools/arg_parser.hpp" #include "Cubed/tools/cubed_assert.hpp" @@ -73,7 +74,7 @@ void App::init(int argc, char** argv) { Localization::instance().load_language( m_game_config.get("language", default_value)); } - + ItemManager::instance().init(); m_window.init(m_argument); m_window.imgui_init(); m_opengl_init = true; diff --git a/src/gameplay/item_manager.cpp b/src/gameplay/item_manager.cpp new file mode 100644 index 0000000..e13fdfd --- /dev/null +++ b/src/gameplay/item_manager.cpp @@ -0,0 +1,106 @@ +#include "Cubed/gameplay/item_manager.hpp" + +#include "Cubed/tools/cubed_assert.hpp" +#include "Cubed/tools/log.hpp" + +#include +#include +#include +#include +namespace fs = std::filesystem; +using namespace rapidjson; +namespace Cubed { +ItemManager::ItemManager() {} + +ItemManager& ItemManager::instance() { + static ItemManager inst; + return inst; +} + +void ItemManager::init() { + m_id_map.clear(); + m_map.clear(); + + fs::path dir = ASSETS_PATH "item"; + if (!fs::is_directory(dir)) { + throw std::runtime_error("Item path not exist!"); + } + for (const auto& entry : fs::recursive_directory_iterator( + dir, fs::directory_options::skip_permission_denied)) { + if (fs::is_regular_file(entry)) { + if (entry.path().extension().string() == ".json") { + add(entry.path()); + } + } + } +} + +void ItemManager::add(const std::filesystem::path& path) { + + std::ifstream s(path); + if (!s.is_open()) { + return; + } + IStreamWrapper isw(s); + + Document doc; + doc.ParseStream(isw); + if (doc.HasParseError()) { + Logger::error("Can't Parse File {}, error code {}", path.string(), + static_cast(doc.GetParseError())); + return; + } + ItemData data; + if (doc.HasMember("id")) { + data.id = static_cast(doc["id"].GetInt()); + } + if (doc.HasMember("name")) { + data.name = doc["name"].GetString(); + } + if (doc.HasMember("description")) { + data.description = doc["description"].GetString(); + } + if (doc.HasMember("texture")) { + data.path = doc["texture"].GetString(); + } + acc a; + if (m_map.emplace(a, data.id, std::move(data))) { + if (!m_id_map.emplace(a->second.name, a->first)) { + Logger::error("ItemManager: Can't Insterd {} {} to id map", + a->second.name, a->first); + } + } else { + Logger::error("ItemManager: Can't Insterd {} to map", path.string()); + } +} + +const ItemData& ItemManager::get_item_data(std::string_view key) const { + IDMap::const_accessor cacc; + ItemID id = 0; + if (m_id_map.find(cacc, key)) { + id = cacc->second; + } else { + Logger::error("Can't Find key {} in id map", key); + ASSERT(false); + return EMPTY; + } + return get_item_data(id); +} +const ItemData& ItemManager::get_item_data(ItemID id) const { + cacc c; + if (!m_map.find(c, id)) { + Logger::error("Can't find item {} in map", id); + ASSERT(false); + return EMPTY; + } + return c->second; +} + +const ItemData& ItemManager::get(std::string_view key) { + return instance().get_item_data(key); +} +const ItemData& ItemManager::get(ItemID id) { + return instance().get_item_data(id); +} + +} // namespace Cubed \ No newline at end of file