mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-08 17:57:02 +08:00
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.
This commit is contained in:
251
AGENTS.md
Normal file
251
AGENTS.md
Normal file
@@ -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.
|
||||||
|
|
||||||
5
assets/item/air.json
Normal file
5
assets/item/air.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "air",
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
6
assets/item/dirt.json
Normal file
6
assets/item/dirt.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "dirt",
|
||||||
|
"description": "",
|
||||||
|
"texture": "texture/item/block/dirt.png"
|
||||||
|
}
|
||||||
6
assets/item/grass.json
Normal file
6
assets/item/grass.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"name": "grass",
|
||||||
|
"description": "",
|
||||||
|
"texture": "texture/item/block/grass.png"
|
||||||
|
}
|
||||||
6
assets/item/grass_block.json
Normal file
6
assets/item/grass_block.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "grass_block",
|
||||||
|
"description": "",
|
||||||
|
"texture": "texture/item/block/grass_block.png"
|
||||||
|
}
|
||||||
6
assets/item/leaf.json
Normal file
6
assets/item/leaf.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"name": "leaf",
|
||||||
|
"description": "",
|
||||||
|
"texture": "texture/item/block/leaf.png"
|
||||||
|
}
|
||||||
6
assets/item/log.json
Normal file
6
assets/item/log.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": "log",
|
||||||
|
"description": "",
|
||||||
|
"texture": "texture/item/block/log.png"
|
||||||
|
}
|
||||||
6
assets/item/sand.json
Normal file
6
assets/item/sand.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "sand",
|
||||||
|
"description": "",
|
||||||
|
"texture": "texture/item/block/sand.png"
|
||||||
|
}
|
||||||
6
assets/item/snowy_grass_block.json
Normal file
6
assets/item/snowy_grass_block.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"name": "snowy_grass_block",
|
||||||
|
"description": "",
|
||||||
|
"texture": "texture/item/block/snowy_grass_block.png"
|
||||||
|
}
|
||||||
6
assets/item/stone.json
Normal file
6
assets/item/stone.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "stone",
|
||||||
|
"description": "",
|
||||||
|
"texture": "texture/item/block/stone.png"
|
||||||
|
}
|
||||||
6
assets/item/water.json
Normal file
6
assets/item/water.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"name": "water",
|
||||||
|
"description": "",
|
||||||
|
"texture": "texture/item/block/water.png"
|
||||||
|
}
|
||||||
14
include/Cubed/gameplay/item.hpp
Normal file
14
include/Cubed/gameplay/item.hpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
namespace Cubed {
|
||||||
|
using ItemID = uint16_t;
|
||||||
|
|
||||||
|
struct ItemData {
|
||||||
|
ItemID id = 0;
|
||||||
|
std::string name;
|
||||||
|
std::string description;
|
||||||
|
std::string path;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Cubed
|
||||||
36
include/Cubed/gameplay/item_manager.hpp
Normal file
36
include/Cubed/gameplay/item_manager.hpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Cubed/gameplay/item.hpp"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string_view>
|
||||||
|
#include <tbb/concurrent_hash_map.h>
|
||||||
|
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<ItemID, ItemData>;
|
||||||
|
using acc = ItemMap::accessor;
|
||||||
|
using cacc = ItemMap::const_accessor;
|
||||||
|
|
||||||
|
using IDMap = tbb::concurrent_hash_map<std::string_view, ItemID>;
|
||||||
|
|
||||||
|
void add(const std::filesystem::path& path);
|
||||||
|
|
||||||
|
ItemMap m_map;
|
||||||
|
|
||||||
|
IDMap m_id_map;
|
||||||
|
|
||||||
|
static constexpr ItemData EMPTY;
|
||||||
|
};
|
||||||
|
} // namespace Cubed
|
||||||
@@ -104,4 +104,5 @@ target_sources(${PROJECT_NAME}
|
|||||||
gameplay/client_entity_manager.cpp
|
gameplay/client_entity_manager.cpp
|
||||||
gameplay/systems/wander_ai_system.cpp
|
gameplay/systems/wander_ai_system.cpp
|
||||||
tools/json_utils.cpp
|
tools/json_utils.cpp
|
||||||
|
gameplay/item_manager.cpp
|
||||||
)
|
)
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "Cubed/config.hpp"
|
#include "Cubed/config.hpp"
|
||||||
#include "Cubed/debug_collector.hpp"
|
#include "Cubed/debug_collector.hpp"
|
||||||
|
#include "Cubed/gameplay/item_manager.hpp"
|
||||||
#include "Cubed/localization.hpp"
|
#include "Cubed/localization.hpp"
|
||||||
#include "Cubed/tools/arg_parser.hpp"
|
#include "Cubed/tools/arg_parser.hpp"
|
||||||
#include "Cubed/tools/cubed_assert.hpp"
|
#include "Cubed/tools/cubed_assert.hpp"
|
||||||
@@ -73,7 +74,7 @@ void App::init(int argc, char** argv) {
|
|||||||
Localization::instance().load_language(
|
Localization::instance().load_language(
|
||||||
m_game_config.get("language", default_value));
|
m_game_config.get("language", default_value));
|
||||||
}
|
}
|
||||||
|
ItemManager::instance().init();
|
||||||
m_window.init(m_argument);
|
m_window.init(m_argument);
|
||||||
m_window.imgui_init();
|
m_window.imgui_init();
|
||||||
m_opengl_init = true;
|
m_opengl_init = true;
|
||||||
|
|||||||
106
src/gameplay/item_manager.cpp
Normal file
106
src/gameplay/item_manager.cpp
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
#include "Cubed/gameplay/item_manager.hpp"
|
||||||
|
|
||||||
|
#include "Cubed/tools/cubed_assert.hpp"
|
||||||
|
#include "Cubed/tools/log.hpp"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <rapidjson/document.h>
|
||||||
|
#include <rapidjson/istreamwrapper.h>
|
||||||
|
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<int>(doc.GetParseError()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ItemData data;
|
||||||
|
if (doc.HasMember("id")) {
|
||||||
|
data.id = static_cast<ItemID>(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
|
||||||
Reference in New Issue
Block a user