载入中...
搜索中...
未找到
SettlementRuleJson.cpp
浏览该文件的文档.
2
3#include <cmath>
4#include <limits>
5#include <utility>
6
7namespace eve::settlement {
8namespace {
9
10template <class T>
11eve::Result<T> invalid(eve::DiagnosticCode code, std::string message, std::string path) {
13}
14
16 std::initializer_list<std::string_view> fields,
17 const std::string& path) {
18 const auto* object = value.getIf<eve::Value::Object>();
19 if (object == nullptr)
20 return invalid<const eve::Value::Object*>(eve::DiagnosticCode::TypeMismatch, "expected object", path);
21 if (object->size() != fields.size())
22 return invalid<const eve::Value::Object*>(eve::DiagnosticCode::InvalidArgument,
23 "object contains missing or unknown fields", path);
24 for (const auto field : fields)
25 if (!object->contains(std::string(field)))
27 "object contains missing or unknown fields", path);
29}
30
31eve::Result<std::string> stringValue(const eve::Value::Object& object, std::string_view field,
32 const std::string& path) {
33 const auto* value = object.at(std::string(field)).getIf<std::string>();
34 if (value == nullptr)
35 return invalid<std::string>(eve::DiagnosticCode::TypeMismatch, "expected string",
36 path + "." + std::string(field));
38}
39
40eve::Result<std::int64_t> integerValue(const eve::Value::Object& object, std::string_view field,
41 const std::string& path) {
42 const auto* value = object.at(std::string(field)).getIf<std::int64_t>();
43 if (value == nullptr)
44 return invalid<std::int64_t>(eve::DiagnosticCode::TypeMismatch, "expected integer",
45 path + "." + std::string(field));
47}
48
49eve::Result<double> numberValue(const eve::Value::Object& object, std::string_view field, const std::string& path) {
50 const auto& value = object.at(std::string(field));
51 if (const auto* number = value.getIf<double>()) return eve::Result<double>::success(*number);
52 if (const auto* integer = value.getIf<std::int64_t>())
53 return eve::Result<double>::success(static_cast<double>(*integer));
54 return invalid<double>(eve::DiagnosticCode::TypeMismatch, "expected number",
55 path + "." + std::string(field));
56}
57
58eve::Result<std::vector<std::string>> stringsValue(const eve::Value& value, const std::string& path) {
59 const auto* array = value.getIf<eve::Value::Array>();
60 if (array == nullptr)
61 return invalid<std::vector<std::string>>(eve::DiagnosticCode::TypeMismatch, "expected string array", path);
62 std::vector<std::string> strings;
63 strings.reserve(array->size());
64 for (std::size_t index = 0; index < array->size(); ++index) {
65 const auto* item = (*array)[index].getIf<std::string>();
66 if (item == nullptr)
67 return invalid<std::vector<std::string>>(eve::DiagnosticCode::TypeMismatch, "expected string",
68 path + "[" + std::to_string(index) + "]");
69 strings.push_back(*item);
70 }
71 return eve::Result<std::vector<std::string>>::success(std::move(strings));
72}
73
74eve::Result<StageKind> stageValue(std::string_view value, std::string path) {
77 if (value == "source_modifiers") return eve::Result<StageKind>::success(StageKind::SourceModifiers);
78 if (value == "target_mitigation") return eve::Result<StageKind>::success(StageKind::TargetMitigation);
84 return invalid<StageKind>(eve::DiagnosticCode::InvalidArgument, "unknown settlement rule stage", std::move(path));
85}
86
87eve::Result<RuleOperation> operationValue(std::string_view value, std::string path) {
98 return invalid<RuleOperation>(eve::DiagnosticCode::InvalidArgument, "unknown settlement rule operation",
99 std::move(path));
100}
101
102eve::Result<SettlementRule> decodeRule(const eve::Value& value, const std::string& path) {
103 auto object = objectWithFields(value,
104 {"filter", "id", "operation", "priority", "source", "stacks", "stage", "value",
105 "valuePerExtraStack", "when"},
106 path);
107 if (!object) return eve::Result<SettlementRule>::failure(object.status());
108 auto id = stringValue(*object.value(), "id", path);
109 auto source = stringValue(*object.value(), "source", path);
110 auto stage = stringValue(*object.value(), "stage", path);
111 auto operation = stringValue(*object.value(), "operation", path);
112 auto priority = integerValue(*object.value(), "priority", path);
113 auto stacks = integerValue(*object.value(), "stacks", path);
114 auto operand = numberValue(*object.value(), "value", path);
115 auto perStack = numberValue(*object.value(), "valuePerExtraStack", path);
116 auto when = stringValue(*object.value(), "when", path);
117 if (!id) return eve::Result<SettlementRule>::failure(id.status());
119 if (!stage) return eve::Result<SettlementRule>::failure(stage.status());
122 if (!stacks) return eve::Result<SettlementRule>::failure(stacks.status());
123 if (!operand) return eve::Result<SettlementRule>::failure(operand.status());
124 if (!perStack) return eve::Result<SettlementRule>::failure(perStack.status());
125 if (!when) return eve::Result<SettlementRule>::failure(when.status());
126 if (priority.value() < std::numeric_limits<int>::min() || priority.value() > std::numeric_limits<int>::max())
127 return invalid<SettlementRule>(eve::DiagnosticCode::InvalidArgument, "priority is out of range",
128 path + ".priority");
129 if (stacks.value() <= 0 || stacks.value() > std::numeric_limits<std::uint32_t>::max())
130 return invalid<SettlementRule>(eve::DiagnosticCode::InvalidArgument, "stacks is out of range",
131 path + ".stacks");
132 auto decodedStage = stageValue(stage.value(), path + ".stage");
133 auto decodedOperation = operationValue(operation.value(), path + ".operation");
134 if (!decodedStage) return eve::Result<SettlementRule>::failure(decodedStage.status());
135 if (!decodedOperation) return eve::Result<SettlementRule>::failure(decodedOperation.status());
136
137 auto filter = objectWithFields(object.value()->at("filter"), {"excludedTags", "kinds", "requiredTags"},
138 path + ".filter");
140 auto kinds = stringsValue(filter.value()->at("kinds"), path + ".filter.kinds");
141 auto required = stringsValue(filter.value()->at("requiredTags"), path + ".filter.requiredTags");
142 auto excluded = stringsValue(filter.value()->at("excludedTags"), path + ".filter.excludedTags");
143 if (!kinds) return eve::Result<SettlementRule>::failure(kinds.status());
145 if (!excluded) return eve::Result<SettlementRule>::failure(excluded.status());
146
147 SettlementRule rule;
148 rule.id = std::move(id).takeValue();
149 rule.source = std::move(source).takeValue();
150 rule.stage = decodedStage.value();
151 rule.priority = static_cast<int>(priority.value());
152 rule.operation = decodedOperation.value();
153 rule.value = operand.value();
154 rule.valuePerExtraStack = perStack.value();
155 rule.stacks = static_cast<std::uint32_t>(stacks.value());
156 rule.filter.kinds = std::move(kinds).takeValue();
157 rule.filter.requiredTags = std::move(required).takeValue();
158 rule.filter.excludedTags = std::move(excluded).takeValue();
159 rule.when = std::move(when).takeValue();
160 return eve::Result<SettlementRule>::success(std::move(rule));
161}
162
163} // namespace
164
166 SettlementRuleSet candidate;
167 auto configured = candidate.configureJson(json);
168 if (!configured) return eve::Result<SettlementRuleSet>::failure(configured.status());
169 return eve::Result<SettlementRuleSet>::success(std::move(candidate),
171}
172
174 auto parsed = eve::Value::fromJson(json);
175 if (!parsed) return eve::Result<void>::failure(parsed.status());
176 auto root = objectWithFields(parsed.value(), {"conditionLanguageVersion", "rules", "schema", "version"},
177 "document");
178 if (!root) return eve::Result<void>::failure(root.status());
179 auto schema = stringValue(*root.value(), "schema", "document");
180 auto version = integerValue(*root.value(), "version", "document");
181 auto conditionVersion = integerValue(*root.value(), "conditionLanguageVersion", "document");
182 if (!schema) return eve::Result<void>::failure(schema.status());
183 if (!version) return eve::Result<void>::failure(version.status());
184 if (!conditionVersion) return eve::Result<void>::failure(conditionVersion.status());
185 if (schema.value() != "settlement.rules")
186 return invalid<void>(eve::DiagnosticCode::InvalidArgument, "unexpected settlement rules schema",
187 "document.schema");
188 if (version.value() != schemaVersion())
189 return invalid<void>(eve::DiagnosticCode::UnknownVersion, "unsupported settlement rules version",
190 "document.version");
191 if (conditionVersion.value() != 1)
192 return invalid<void>(eve::DiagnosticCode::UnknownVersion, "unsupported condition language version",
193 "document.conditionLanguageVersion");
194 const auto* encodedRules = root.value()->at("rules").getIf<eve::Value::Array>();
195 if (encodedRules == nullptr)
196 return invalid<void>(eve::DiagnosticCode::TypeMismatch, "expected rule array", "document.rules");
197 std::vector<SettlementRule> rules;
198 rules.reserve(encodedRules->size());
199 for (std::size_t index = 0; index < encodedRules->size(); ++index) {
200 auto rule = decodeRule((*encodedRules)[index], "document.rules[" + std::to_string(index) + "]");
201 if (!rule) return eve::Result<void>::failure(rule.status());
202 rules.push_back(std::move(rule).takeValue());
203 }
204 return configure(std::move(rules));
205}
206
207} // namespace eve::settlement
ActionParameterOperation operation
double value
int root
Definition AnimSmr.cpp:119
int priority
std::string message
DiagnosticCode code
wgpu::PopErrorScopeStatus status
bool required
std::string path
Definition PlayHost.cpp:110
std::vector< std::string > fields
Definition PlayHost.cpp:111
double number
std::string filter
std::string string
Declarative, domain-neutral rules for buffs and gameplay effects.
Json object
uint32_t index
const UnitySourceAsset & source
std::size_t at
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 Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
static Result< Value > fromJson(std::string_view json)
Parse one strict JSON value into an owning Value.
Definition Value.cpp:57
std::map< std::string, Value > Object
Definition Value.h:34
std::vector< Value > Array
Definition Value.h:33
Validated immutable-style rule collection installable into pipelines.
eve::Result< void > configureJson(std::string_view json)
Atomically decode and replace a strict versioned JSON rule document.
static constexpr std::uint32_t schemaVersion() noexcept
Stable schema version used by canonical rule-set identity.
eve::Result< void > configure(std::vector< SettlementRule > rules)
Atomically replace all rules after validating ids, operands and phases.
static eve::Result< SettlementRuleSet > fromJson(std::string_view json)
Decode and compile a strict versioned JSON rule document without mutating an existing rule set.
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
Definition Database.h:26
Result< int > invalid(std::string message)
Invalid.
const EditorValue * field(const EditorValue &value, const char *name)
NodeDesc when(bool cond, NodeDesc child)
Conditional: include child only when cond is true (empty group otherwise).
Definition NodeDesc.cpp:234
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
@ TypeMismatch
A stable reference resolved to a different canonical domain type.