载入中...
搜索中...
未找到
ShopCatalogue.cpp
浏览该文件的文档.
1#include "rpg/ShopCatalogue.h"
2
3#include "common/Json.h"
4#include "inventory/Item.h"
5
6#include <climits>
7#include <unordered_map>
8#include <unordered_set>
9#include <utility>
10#include <vector>
11
12namespace eve::rpg {
13namespace {
14
15std::vector<ShopOffer> &offers() {
16 static std::vector<ShopOffer> value;
17 return value;
18}
19
20std::unordered_map<std::string, std::size_t> &offerIndex() {
21 static std::unordered_map<std::string, std::size_t> value;
22 return value;
23}
24
25bool validId(const std::string &value) {
26 if (value.empty() || value.size() > 256) return false;
27 for (unsigned char ch : value)
28 if (ch < 0x20 || ch == 0x7f) return false;
29 return true;
30}
31
32eve::Result<int> failure(std::string message, std::string path) {
35 "rpg.shop-catalogue"));
36}
37
38bool onlyKnownFields(const eve::json::Value &object) {
39 static const std::unordered_set<std::string> known = {
40 "id", "itemId", "name", "desc", "buyPrice", "sellPrice"};
41 for (const auto &key : object.keys())
42 if (known.count(key) == 0) return false;
43 return true;
44}
45
46} // namespace
47
49 std::string parseError;
50 const auto document = eve::json::Document::parse(json, &parseError);
51 if (!document.valid()) return failure(parseError.empty() ? "invalid JSON" : parseError, "$");
52 const auto root = document.root();
53 if (!root.isArray() || root.size() == 0)
54 return failure("shop catalogue must be a non-empty array", "$");
55
56 std::vector<ShopOffer> proposed;
57 std::unordered_map<std::string, std::size_t> proposedIndex;
58 proposed.reserve(root.size());
59 for (std::size_t index = 0; index < root.size(); ++index) {
60 const auto object = root.at(index);
61 const std::string path = "$[" + std::to_string(index) + "]";
62 if (!object.isObject()) return failure("shop offer must be an object", path);
63 if (!onlyKnownFields(object)) return failure("shop offer contains an unknown field", path);
64 const auto id = object.get("id");
65 const auto itemIdValue = object.get("itemId");
66 const auto name = object.get("name");
67 const auto description = object.get("desc");
68 const auto buyPrice = object.get("buyPrice");
69 const auto sellPrice = object.get("sellPrice");
70 if (!id.isString() || !validId(id.asString()))
71 return failure("offer id must be a stable non-empty id", path + ".id");
72 const std::string itemId = itemIdValue.isString() ? itemIdValue.asString() : std::string{};
73 if (!validId(itemId)) return failure("itemId must be a stable non-empty id", path + ".itemId");
75 return failure("shop offer references an unknown inventory item", path + ".itemId");
76 if (!name.isString() || name.asString().empty() || name.asString().size() > 512)
77 return failure("name must be a non-empty string of at most 512 bytes", path + ".name");
78 if (!description.isString() || description.asString().size() > 2048)
79 return failure("desc must be a string of at most 2048 bytes", path + ".desc");
80 if (!buyPrice.isInt64() || buyPrice.asInt64() < 0 || buyPrice.asInt64() > INT_MAX)
81 return failure("buyPrice must be a non-negative integer", path + ".buyPrice");
82 if (!sellPrice.isInt64() || sellPrice.asInt64() < 0 || sellPrice.asInt64() > buyPrice.asInt64())
83 return failure("sellPrice must be a non-negative integer no greater than buyPrice",
84 path + ".sellPrice");
85 if (proposedIndex.count(id.asString()) != 0)
86 return failure("duplicate offer id", path + ".id");
87 ShopOffer offer;
88 offer.id = id.asString();
89 offer.itemId = itemId;
90 offer.displayName = name.asString();
91 offer.description = description.asString();
92 offer.buyPrice = static_cast<int>(buyPrice.asInt64());
93 offer.sellPrice = static_cast<int>(sellPrice.asInt64());
94 proposedIndex.emplace(offer.id, proposed.size());
95 proposed.push_back(std::move(offer));
96 }
97 offers() = std::move(proposed);
98 offerIndex() = std::move(proposedIndex);
99 return eve::Result<int>::success(static_cast<int>(offers().size()));
100}
101
103 offers().clear();
104 offerIndex().clear();
105}
106
107int ShopCatalogue::count() { return static_cast<int>(offers().size()); }
108
109const ShopOffer *ShopCatalogue::find(const std::string &id) {
110 const auto found = offerIndex().find(id);
111 return found == offerIndex().end() ? nullptr : &offers()[found->second];
112}
113
115 return index < 0 || index >= count() ? nullptr : &offers()[static_cast<std::size_t>(index)];
116}
117
118} // namespace eve::rpg
double value
LogicalId itemId
int root
Definition AnimSmr.cpp:119
std::string message
std::uint32_t key
std::string description
std::string name
std::string path
Definition PlayHost.cpp:110
bool found
Strict, atomically replaceable RPG shop content.
std::uint32_t count
Json object
float size
Definition TreeMesh.cpp:156
uint32_t index
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static const ItemDefinition * find(const std::string &id)
Finds find.
Definition Item.cpp:53
static Document parse(const std::string &text, std::string *error=nullptr)
Parse.
Definition Json.cpp:581
EVENGINE_API_FOUNDATION public API.
Definition Json.h:38
static eve::Result< int > replaceFromJsonStrict(const std::string &json)
Strictly validate and atomically replace the entire offer catalogue.
static const ShopOffer * find(const std::string &id)
Find an offer by stable offer id, or null when absent.
static const ShopOffer * at(int index)
Return an offer by deterministic source index, or null when out of range.
static void clear()
Remove all offers. Intended for teardown and isolated tests.
static int count()
Return the number of committed offers in deterministic source order.
RPG 模块的基础实体:把属性 / 状态 / 技能三张表挂到一个 ECS 实体上。
One immutable offer in the process-local shop catalogue.
std::string description
std::string displayName