载入中...
搜索中...
未找到
CardDefinitionRuntime.cpp
浏览该文件的文档.
2
3#include "common/Value.h"
4
5#include <algorithm>
6#include <cmath>
7#include <limits>
8#include <set>
9#include <stdexcept>
10#include <utility>
11
12namespace eve::card {
13namespace {
14
17 const auto& logical = reference.id();
18 return registry.handle(std::string(logical.namespaceName()), std::string(logical.name()));
19}
20
21const eve::Value* field(const eve::Value::Object& object, const char* name) {
22 const auto it = object.find(name);
23 return it == object.end() ? nullptr : &it->second;
24}
25
26bool readInt(const eve::Value::Object& object, const char* name, int& output) {
27 const auto* value = field(object, name);
28 if (const auto* integer = value ? value->getIf<std::int64_t>() : nullptr) {
29 if (*integer < std::numeric_limits<int>::min() || *integer > std::numeric_limits<int>::max()) return false;
30 output = static_cast<int>(*integer);
31 return true;
32 }
33 return false;
34}
35
38 auto parsed = eve::Value::fromJson(definition.json);
39 if (!parsed) return eve::Result<CardRuntimeState>::failure(parsed.status());
40 auto value = std::move(parsed).takeValue();
41 const auto* object = value.getIf<eve::Value::Object>();
42 if (!object)
44 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "card definition payload must be an object",
45 "json", {}, "card.definition_runtime"));
46
47 CardRuntimeState state;
48 state.name = std::string(reference.id().name());
49 if (const auto* value = field(*object, "name")) {
50 const auto* text = value->getIf<std::string>();
51 if (!text)
53 "card name must be a string", "name",
54 {}, "card.definition_runtime"));
55 state.name = *text;
56 }
57 if (const auto* value = field(*object, "kind")) {
58 const auto* text = value->getIf<std::string>();
59 if (!text)
61 "card kind must be a string", "kind",
62 {}, "card.definition_runtime"));
63 state.kind = *text;
64 }
65 if (!readInt(*object, "cost", state.cost) || !readInt(*object, "attack", state.attack) ||
66 !readInt(*object, "health", state.maxHealth))
68 eve::DiagnosticCode::InvalidArgument, "card cost, attack and health must be Int64 fields", "stats", {},
69 "card.definition_runtime"));
70 if (state.cost < 0 || state.attack < 0 || state.maxHealth < 0)
72 "card stats must not be negative", "stats",
73 {}, "card.definition_runtime"));
74 state.currentHealth = state.maxHealth;
75
76 if (const auto* value = field(*object, "tint")) {
77 const auto* array = value->getIf<eve::Value::Array>();
78 if (!array || array->size() < 3)
80 eve::DiagnosticCode::InvalidArgument, "card tint must contain three numeric channels", "tint", {},
81 "card.definition_runtime"));
82 float* channels[] = {&state.tint.r, &state.tint.g, &state.tint.b};
83 for (std::size_t i = 0; i < 3; ++i) {
84 const auto* number = (*array)[i].getIf<double>();
85 const auto* integer = (*array)[i].getIf<std::int64_t>();
86 const double channel = number ? *number : integer ? static_cast<double>(*integer) : -1.0;
87 if (!std::isfinite(channel) || channel < 0.0 || channel > 1.0)
89 eve::DiagnosticCode::InvalidArgument, "card tint channel must be finite in [0,1]", "tint", {},
90 "card.definition_runtime"));
91 *channels[i] = static_cast<float>(channel);
92 }
93 }
94 if (const auto* value = field(*object, "tags")) {
95 const auto* array = value->getIf<eve::Value::Array>();
96 if (!array)
98 "card tags must be an array", "tags",
99 {}, "card.definition_runtime"));
100 for (const auto& item : *array) {
101 const auto* tag = item.getIf<std::string>();
102 if (!tag || tag->empty())
104 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "card tag must be a non-empty string",
105 "tags", {}, "card.definition_runtime"));
106 state.tags.push_back(*tag);
107 }
108 std::sort(state.tags.begin(), state.tags.end());
109 state.tags.erase(std::unique(state.tags.begin(), state.tags.end()), state.tags.end());
110 }
111 return eve::Result<CardRuntimeState>::success(std::move(state));
112}
113
117 auto definition = registry.resolveHandle(handle);
118 if (!definition) return eve::Result<CardRuntimeState>::failure(definition.status());
119 return parseState(definition.value().get(), reference);
120}
121
122eve::LogicalId cardSchema() {
123 static const eve::LogicalId value = [] {
124 const auto parsed = eve::LogicalId::parse("card:runtime");
125 if (!parsed) throw std::logic_error("invalid built-in card snapshot schema");
126 return *parsed;
127 }();
128 return value;
129}
130
131eve::Value encodeState(const CardRuntimeState& state) {
132 eve::Value::Array tint{eve::Value(static_cast<double>(state.tint.r)), eve::Value(static_cast<double>(state.tint.g)),
133 eve::Value(static_cast<double>(state.tint.b))};
135 for (const auto& tag : state.tags) tags.emplace_back(tag);
137 {"attack", eve::Value(static_cast<std::int64_t>(state.attack))},
138 {"cost", eve::Value(static_cast<std::int64_t>(state.cost))},
139 {"currentHealth", eve::Value(static_cast<std::int64_t>(state.currentHealth))},
140 {"kind", eve::Value(state.kind)},
141 {"maxHealth", eve::Value(static_cast<std::int64_t>(state.maxHealth))},
142 {"name", eve::Value(state.name)},
143 {"tags", eve::Value(std::move(tags))},
144 {"tint", eve::Value(std::move(tint))},
145 });
146}
147
149 const auto* object = value.getIf<eve::Value::Object>();
150 if (object == nullptr)
152 "card runtime state must be an object",
153 "state", {}, "card.definition_runtime"));
154 static const std::set<std::string> fields = {"attack", "cost", "currentHealth", "kind",
155 "maxHealth", "name", "tags", "tint"};
156 for (const auto& [name, unused] : *object) {
157 (void)unused;
158 if (!fields.contains(name))
160 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "unknown card runtime state field",
161 "state." + name, {}, "card.definition_runtime"));
162 }
163 for (const auto& name : fields)
164 if (!object->contains(name))
165 return eve::Result<CardRuntimeState>::failure(
166 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "card runtime state is missing a field",
167 "state." + name, {}, "card.definition_runtime"));
168 CardRuntimeState result;
169 const auto readIntField = [&](const char* name, int& output) {
170 const auto* integer = object->at(name).getIf<std::int64_t>();
171 if (integer == nullptr || *integer < std::numeric_limits<int>::min() ||
172 *integer > std::numeric_limits<int>::max())
173 return false;
174 output = static_cast<int>(*integer);
175 return true;
176 };
177 const auto* name = object->at("name").getIf<std::string>();
178 const auto* kind = object->at("kind").getIf<std::string>();
179 const auto* tags = object->at("tags").getIf<eve::Value::Array>();
180 const auto* tint = object->at("tint").getIf<eve::Value::Array>();
181 if (name == nullptr || kind == nullptr || tags == nullptr || tint == nullptr || tint->size() != 3 ||
182 !readIntField("attack", result.attack) || !readIntField("cost", result.cost) ||
183 !readIntField("currentHealth", result.currentHealth) || !readIntField("maxHealth", result.maxHealth))
185 "card runtime state field is invalid",
186 "state", {}, "card.definition_runtime"));
187 result.name = *name;
188 result.kind = *kind;
189 if (result.cost < 0 || result.attack < 0 || result.maxHealth < 0 || result.currentHealth < 0 ||
190 result.currentHealth > result.maxHealth)
192 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "card runtime health/stat values are invalid",
193 "state", {}, "card.definition_runtime"));
194 float* channels[] = {&result.tint.r, &result.tint.g, &result.tint.b};
195 for (std::size_t index = 0; index < 3; ++index) {
196 const auto* number = (*tint)[index].getIf<double>();
197 if (number == nullptr || !std::isfinite(*number) || *number < 0.0 || *number > 1.0)
199 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "card tint channel is invalid",
200 "state.tint", {}, "card.definition_runtime"));
201 *channels[index] = static_cast<float>(*number);
202 }
203 for (const auto& tag : *tags) {
204 const auto* text = tag.getIf<std::string>();
205 if (text == nullptr || text->empty())
208 "state.tags", {}, "card.definition_runtime"));
209 result.tags.push_back(*text);
210 }
211 return eve::Result<CardRuntimeState>::success(std::move(result));
212}
213
214} // namespace
215
217 eve::DefinitionRef definition,
218 eve::PersistentId instanceId,
220 if (!definition.id().isValid())
222 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "card definition reference is invalid", {}, {},
223 "card.definition_runtime"));
224 auto handle = currentHandle(registry, definition);
226 auto state = resolveState(registry, definition, handle.value());
229 instanceId, std::move(definition), handle.value().generation, std::move(state).takeValue());
230 if (!runtime) return eve::Result<CardDefinitionRuntime>::failure(runtime.status());
232 CardDefinitionRuntime(registry, std::move(runtime).takeValue(), policy));
233}
234
236 return runtime_.identity();
237}
238
239const CardRuntimeState& CardDefinitionRuntime::state() const noexcept { return runtime_.state(); }
240
241CardRuntimeState& CardDefinitionRuntime::state() noexcept { return runtime_.state(); }
242
244 return runtime_.identity().definitionHandle();
245}
246
247void CardDefinitionRuntime::setActive(bool active) noexcept { runtime_.setActive(active); }
248
249bool CardDefinitionRuntime::isActive() const noexcept { return runtime_.isActive(); }
250
252 if (card == nullptr)
254 "card runtime cannot project to a null CardData",
255 "card", {}, "card.definition_runtime"));
256
257 CardData::Identity identity = *card->identity().operator->();
258 CardData::Stats stats = *card->stats().operator->();
259 CardData::Visual visual = *card->visual().operator->();
260 CardData::DefinitionBinding binding = *card->definitionBinding().operator->();
261
262 identity.definitionId = this->identity().definition.id().name();
263 identity.name = state().name;
264 identity.kind = state().kind;
265 stats.cost = state().cost;
266 stats.attack = state().attack;
267 stats.health = state().currentHealth;
268 visual.tint = state().tint;
269 binding.identity = this->identity();
270 binding.reloadPolicy = policy_;
271 binding.active = isActive();
272
273 using std::swap;
274 swap(*card->identity().operator->(), identity);
275 swap(*card->stats().operator->(), stats);
276 swap(*card->visual().operator->(), visual);
277 swap(*card->definitionBinding().operator->(), binding);
279}
280
282 if (registry_ == nullptr)
284 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "card definition registry is not bound",
285 "registry", {}, "card.definition_runtime"));
286 auto next = currentHandle(*registry_, identity().definition);
287 if (!next) return eve::Result<eve::definition::ReloadOutcome>::failure(next.status());
288 auto defaults = resolveState(*registry_, identity().definition, next.value());
289 if (!defaults) return eve::Result<eve::definition::ReloadOutcome>::failure(defaults.status());
290 CardRuntimeState defaultState = std::move(defaults).takeValue();
291 auto rebuild = [defaultState](const CardRuntimeState& oldState, const eve::definition::InstanceIdentity&,
293 CardRuntimeState rebuilt = defaultState;
294 rebuilt.currentHealth = std::clamp(oldState.currentHealth, 0, rebuilt.maxHealth);
295 return eve::Result<CardRuntimeState>::success(std::move(rebuilt));
296 };
297 auto result = runtime_.reload(next.value(), policy, defaultState, std::move(rebuild));
298 if (result.ok()) policy_ = policy;
299 return result;
300}
301
310
312 const eve::SnapshotHashProvider& hashProvider) const {
313 auto result = snapshot(revision, tick, hashProvider);
314 if (!result) return eve::Result<std::string>::failure(result.status());
315 return std::move(result).andThen(
317}
318
320 const eve::SnapshotHashProvider& hashProvider) {
321 if (registry_ == nullptr)
323 "card definition registry is not bound", "registry",
324 {}, "card.definition_runtime"));
325 auto current = currentHandle(*registry_, identity().definition);
326 if (!current) return eve::Result<void>::failure(current.status());
327 if (current.value() != definitionHandle())
329 eve::DiagnosticCode::StaleHandle, "card snapshot cannot restore against a replaced definition",
330 "definitionGeneration", {}, "card.definition_runtime"));
332 return eve::definition::restoreRuntimeInstance(runtime_, snapshotValue, "card.runtime", cardSchema(),
333 eve::SchemaVersion(1), hashProvider, decoder);
334}
335
336} // namespace eve::card
double value
bool & active
std::map< std::string, std::vector< Key >, std::less<> > channels
std::string output
AuthorityStoreHandleRef reference
Definition Authority.cpp:24
Strongly typed CardDefinition to CardData runtime adapter.
glm::vec4 tint
std::string text
TokenKind kind
std::string name
const std::string * tag
OnnxTransferStats stats
Definition OnnxGpgpu.cpp:56
std::string error
Definition Package.cpp:60
eve::action::ActionVfxBinding binding
std::uint64_t revision
std::vector< std::string > fields
Definition PlayHost.cpp:111
PrimitiveHandle handle
double number
double current
SimulationTick tick
Json object
uint32_t index
Owning, renderer-independent dynamic values.
Stable definition reference backed by LogicalId.
Definition ResourceRef.h:85
const LogicalId & id() const noexcept
Return the stable logical definition identity.
Definition ResourceRef.h:97
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
Human-readable, scoped identifier in the form namespace:name.
Definition Identity.h:411
static std::optional< LogicalId > parse(std::string_view text)
Parses a scoped logical name.
Definition Identity.cpp:36
std::string_view name() const noexcept
Returns the name component as a view into this object.
Definition Identity.cpp:63
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
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
单张卡牌:ECS 实体,数据拆成 Identity / Stats / Visual / Layout / State。
Definition CardTypes.h:94
Binds a typed card runtime to one Definitions registry incarnation.
eve::Result< eve::definition::ReloadOutcome > reload(eve::definition::ReloadPolicy policy)
Reload from the registry's current generation and execute policy.
const CardRuntimeState & state() const noexcept
Borrow the strongly typed card runtime state.
const eve::definition::InstanceIdentity & identity() const noexcept
Borrow the unified instance identity.
eve::Result< std::string > snapshotJson(eve::Revision revision, eve::SimulationTick tick, const eve::SnapshotHashProvider &hashProvider) const
Serialize snapshot() as canonical JSON.
eve::Result< void > applyTo(CardData *card) const
Apply the typed state and common identity to an ECS CardData.
bool isActive() const noexcept
Return whether RejectWhileActive currently treats the instance as active.
static eve::Result< CardDefinitionRuntime > create(eve::definitions::DefinitionRegistry &registry, eve::DefinitionRef definition, eve::PersistentId instanceId, eve::definition::ReloadPolicy policy=eve::definition::ReloadPolicy::KeepInstanceValues)
Create a card runtime from the current typed definition.
void setActive(bool active) noexcept
Mark the instance active/inactive for RejectWhileActive.
eve::Result< eve::SnapshotEnvelope > snapshot(eve::Revision revision, eve::SimulationTick tick, const eve::SnapshotHashProvider &hashProvider) const
Capture card instance state in the common SnapshotEnvelope.
eve::definition::DefinitionHandle definitionHandle() const noexcept
Borrow the exact generation-qualified definition handle.
eve::Result< void > restore(const eve::SnapshotEnvelope &snapshot, const eve::SnapshotHashProvider &hashProvider)
Verify and atomically restore a same-generation card snapshot.
static eve::Result< RuntimeInstance > create(eve::PersistentId instanceId, eve::DefinitionRef definition, eve::Generation definitionGeneration, State state, bool active=true)
Construct a runtime instance after validating its identity.
Deterministic registry of topic-neutral, versioned JSON definitions.
Definition Definitions.h:58
eve::ResultRef< const Definition > resolveHandle(const DefinitionHandle &handle) const
Resolves a generation-qualified handle and reports stale identities.
eve::Result< DefinitionHandle > handle(const std::string &type, const std::string &id) const
Returns the current generation-qualified handle through Result.
Id128 public API.
Definition Identity.h:112
ReloadPolicy
Policy used when a live definition is replaced.
eve::Result< eve::SnapshotEnvelope > snapshotRuntimeInstance(const RuntimeInstance< State > &runtime, std::string type, eve::LogicalId schema, eve::SchemaVersion schemaVersion, eve::Revision revision, eve::SimulationTick tick, const eve::SnapshotHashProvider &hashProvider, const RuntimeStateEncoder< State > &encode)
Capture a typed runtime instance in the common snapshot envelope.
eve::Result< void > restoreRuntimeInstance(RuntimeInstance< State > &runtime, const eve::SnapshotEnvelope &snapshot, std::string_view expectedType, const eve::LogicalId &expectedSchema, eve::SchemaVersion expectedVersion, const eve::SnapshotHashProvider &hashProvider, const RuntimeStateDecoder< State > &decode)
Verify and atomically restore a typed runtime instance.
std::function< eve::Result< State >(const eve::Value &)> RuntimeStateDecoder
Decodes one domain-owned runtime state from an owning Value.
std::function< eve::Result< eve::Value >(const State &)> RuntimeStateEncoder
Converts one domain-owned runtime state into an owning Value.
eve::Diagnostic Diagnostic
eve::Result< T > Result
const EditorValue * field(const EditorValue &value, const char *name)
int64_t integer(const RuntimeTensor &v, size_t i=0)
Integer.
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
Result< std::string > serializeSnapshotEnvelope(const SnapshotEnvelope &snapshot)
Serialize an envelope as deterministic compact JSON.
Definition Snapshot.cpp:184
std::function< Result< ContentId >(std::string_view canonicalInput)> SnapshotHashProvider
Injected content-digest implementation used by snapshots.
Definition Snapshot.h:36
Stable outer format shared by persistence and cross-process snapshots.
Definition Snapshot.h:46
Definition link and reload policy for this card instance.
Definition CardTypes.h:163
eve::definition::ReloadPolicy reloadPolicy
Definition CardTypes.h:165
稳定实例 id、显示名、种类。
Definition CardTypes.h:103
费用 / 攻击 / 生命。
Definition CardTypes.h:112
朝向、禁用、色调、卡图。
Definition CardTypes.h:132
Typed mutable values owned by one card definition instance.
A generation-qualified reference to one definition incarnation.
Canonical identity carried by every typed runtime instance.
One immutable view of a registered versioned JSON definition.
Definition Definitions.h:31