载入中...
搜索中...
未找到
EffectDefinitionRuntime.cpp
浏览该文件的文档.
2
3#include "common/Value.h"
4
5#include <algorithm>
6#include <cmath>
7#include <iterator>
8#include <limits>
9#include <utility>
10
11namespace eve::effects {
12namespace {
13
16 const auto& logical = reference.id();
17 return registry.handle(std::string(logical.namespaceName()), std::string(logical.name()));
18}
19
20const eve::Value* field(const eve::Value::Object& object, const char* name) {
21 const auto it = object.find(name);
22 return it == object.end() ? nullptr : &it->second;
23}
24
25bool readString(const eve::Value::Object& object, const char* name, std::string& output) {
26 const auto* value = field(object, name);
27 const auto* text = value ? value->getIf<std::string>() : nullptr;
28 if (!text) return false;
29 output = *text;
30 return true;
31}
32
33bool readInt(const eve::Value::Object& object, const char* name, int& output) {
34 const auto* value = field(object, name);
35 if (const auto* integer = value ? value->getIf<std::int64_t>() : nullptr) {
36 if (*integer < std::numeric_limits<int>::min() || *integer > std::numeric_limits<int>::max()) return false;
37 output = static_cast<int>(*integer);
38 return true;
39 }
40 return false;
41}
42
43bool readUInt(const eve::Value::Object& object, const char* name, std::uint32_t& output) {
44 const auto* value = field(object, name);
45 const auto* integer = value ? value->getIf<std::int64_t>() : nullptr;
46 if (!integer || *integer < 0 || static_cast<std::uint64_t>(*integer) > std::numeric_limits<std::uint32_t>::max())
47 return false;
48 output = static_cast<std::uint32_t>(*integer);
49 return true;
50}
51
52bool readDouble(const eve::Value::Object& object, const char* name, double& output) {
53 const auto* value = field(object, name);
54 if (const auto* number = value ? value->getIf<double>() : nullptr) {
55 output = *number;
56 return std::isfinite(output);
57 }
58 if (const auto* integer = value ? value->getIf<std::int64_t>() : nullptr) {
59 output = static_cast<double>(*integer);
60 return std::isfinite(output);
61 }
62 return false;
63}
64
65template <class Enum>
66bool readEnum(const eve::Value::Object& object, const char* name, const char* const* names, const Enum* values,
67 std::size_t count, Enum& output) {
68 const auto* value = field(object, name);
69 const auto* text = value ? value->getIf<std::string>() : nullptr;
70 if (!text) return false;
71 for (std::size_t i = 0; i < count; ++i) {
72 if (*text == names[i]) {
73 output = values[i];
74 return true;
75 }
76 }
77 return false;
78}
79
80template <class Enum>
81bool readOptionalEnum(const eve::Value::Object& object, const char* name, const char* const* names, const Enum* values,
82 std::size_t count, Enum& output) {
83 const auto* value = field(object, name);
84 if (value == nullptr) return true;
85 return readEnum(object, name, names, values, count, output);
86}
87
90 auto parsed = eve::Value::fromJson(definition.json);
91 if (!parsed) return eve::Result<EffectRuntimeState>::failure(parsed.status());
92 auto value = std::move(parsed).takeValue();
93 const auto* object = value.getIf<eve::Value::Object>();
94 if (!object)
96 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect definition payload must be an object",
97 "json", {}, "effects.definition_runtime"));
98
99 EffectRuntimeState state;
100 state.stackKey = std::string(reference.id().name());
101 if (const auto* value = field(*object, "stackKey")) {
102 const auto* text = value->getIf<std::string>();
103 if (!text)
105 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect stackKey must be a string",
106 "stackKey", {}, "effects.definition_runtime"));
107 state.stackKey = *text;
108 }
109 if (const auto* value = field(*object, "priority")) {
110 if (!readInt(*object, "priority", state.priority))
112 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect priority must be Int64",
113 "priority", {}, "effects.definition_runtime"));
114 }
115 if (const auto* value = field(*object, "duration")) {
116 (void)value;
117 if (!readDouble(*object, "duration", state.duration))
119 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect duration must be finite",
120 "duration", {}, "effects.definition_runtime"));
121 }
122 if (const auto* value = field(*object, "magnitude")) {
123 (void)value;
124 if (!readDouble(*object, "magnitude", state.magnitude))
126 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect magnitude must be finite",
127 "magnitude", {}, "effects.definition_runtime"));
128 }
129 if (const auto* value = field(*object, "stackCount")) {
130 (void)value;
131 if (!readUInt(*object, "stackCount", state.stackCount) || state.stackCount == 0)
133 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect stackCount must be positive",
134 "stackCount", {}, "effects.definition_runtime"));
135 }
136 if (const auto* value = field(*object, "maxStacks")) {
137 (void)value;
138 if (!readUInt(*object, "maxStacks", state.maxStacks))
140 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect maxStacks must be UInt32",
141 "maxStacks", {}, "effects.definition_runtime"));
142 }
143 if (state.maxStacks != 0 && state.stackCount > state.maxStacks)
145 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect stackCount exceeds maxStacks",
146 "stackCount", {}, "effects.definition_runtime"));
147 constexpr const char* stackModeNames[] = {"replace", "new_instance", "reuse", "accumulate"};
150 if (!readOptionalEnum(*object, "stackMode", stackModeNames, stackModeValues, std::size(stackModeNames),
151 state.policy.stackMode))
153 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect stackMode is invalid", "stackMode", {},
154 "effects.definition_runtime"));
155 constexpr const char* stackCountPolicyNames[] = {"keep", "increment", "set"};
156 constexpr StackCountPolicy stackCountPolicyValues[] = {StackCountPolicy::Keep, StackCountPolicy::Increment,
158 if (!readOptionalEnum(*object, "stackCountPolicy", stackCountPolicyNames, stackCountPolicyValues,
159 std::size(stackCountPolicyNames), state.policy.stackCount))
161 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect stackCountPolicy is invalid",
162 "stackCountPolicy", {}, "effects.definition_runtime"));
163 constexpr const char* durationPolicyNames[] = {"keep", "replace", "extend"};
164 constexpr DurationPolicy durationPolicyValues[] = {DurationPolicy::Keep, DurationPolicy::Replace,
166 if (!readOptionalEnum(*object, "durationPolicy", durationPolicyNames, durationPolicyValues,
167 std::size(durationPolicyNames), state.policy.duration))
169 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect durationPolicy is invalid",
170 "durationPolicy", {}, "effects.definition_runtime"));
171 constexpr const char* magnitudePolicyNames[] = {"keep", "replace", "add", "max"};
172 constexpr MagnitudePolicy magnitudePolicyValues[] = {MagnitudePolicy::Keep, MagnitudePolicy::Replace,
174 if (!readOptionalEnum(*object, "magnitudePolicy", magnitudePolicyNames, magnitudePolicyValues,
175 std::size(magnitudePolicyNames), state.policy.magnitude))
177 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect magnitudePolicy is invalid",
178 "magnitudePolicy", {}, "effects.definition_runtime"));
179 constexpr const char* overflowPolicyNames[] = {"reject", "clamp", "replace_oldest"};
180 constexpr OverflowPolicy overflowPolicyValues[] = {OverflowPolicy::Reject, OverflowPolicy::Clamp,
182 if (!readOptionalEnum(*object, "overflowPolicy", overflowPolicyNames, overflowPolicyValues,
183 std::size(overflowPolicyNames), state.policy.overflow))
185 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect overflowPolicy is invalid",
186 "overflowPolicy", {}, "effects.definition_runtime"));
187 state.policy.maxStacks = state.maxStacks;
188 state.remaining = state.duration > 0.0 ? state.duration : -1.0;
189
190 if (const auto* value = field(*object, "tags")) {
191 const auto* array = value->getIf<eve::Value::Array>();
192 if (!array)
194 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect tags must be an array", "tags", {},
195 "effects.definition_runtime"));
196 for (const auto& item : *array) {
197 const auto* tag = item.getIf<std::string>();
198 if (!tag || tag->empty())
200 eve::DiagnosticCode::InvalidArgument, "effect tag must be a non-empty string", "tags", {},
201 "effects.definition_runtime"));
202 state.tags.push_back(*tag);
203 }
204 std::sort(state.tags.begin(), state.tags.end());
205 state.tags.erase(std::unique(state.tags.begin(), state.tags.end()), state.tags.end());
206 }
207 if (const auto* value = field(*object, "payload")) {
208 const auto* payload = value->getIf<eve::Value::Object>();
209 if (!payload)
211 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect payload must be an object",
212 "payload", {}, "effects.definition_runtime"));
213 for (const auto& [key, item] : *payload) {
214 auto json = item.toJson();
215 if (!json) return eve::Result<EffectRuntimeState>::failure(json.status());
216 auto stored = state.payload.setJson(key, std::move(json).takeValue());
217 if (!stored.ok())
219 eve::DiagnosticCode::InvalidArgument, "effect payload member could not be stored", "payload." + key,
220 {}, "effects.definition_runtime"));
221 }
222 }
223 return eve::Result<EffectRuntimeState>::success(std::move(state));
224}
225
229 auto definition = registry.resolveHandle(handle);
230 if (!definition) return eve::Result<EffectRuntimeState>::failure(definition.status());
231 return parseState(definition.value().get(), reference);
232}
233
234} // namespace
235
237 eve::DefinitionRef definition, std::string subject,
238 std::string source, eve::PersistentId instanceId,
240 if (!definition.id().isValid())
242 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect definition reference is invalid", {},
243 {}, "effects.definition_runtime"));
244 if (subject.empty())
246 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect subject must not be empty", "subject",
247 {}, "effects.definition_runtime"));
248 auto handle = currentHandle(registry, definition);
250 auto state = resolveState(registry, definition, handle.value());
253 instanceId, std::move(definition), handle.value().generation, std::move(state).takeValue());
254 if (!runtime) return eve::Result<EffectDefinitionRuntime>::failure(runtime.status());
256 registry, std::move(runtime).takeValue(), std::move(subject), std::move(source), policy));
257}
258
260 return runtime_.identity();
261}
262
263const EffectRuntimeState& EffectDefinitionRuntime::state() const noexcept { return runtime_.state(); }
264
265EffectRuntimeState& EffectDefinitionRuntime::state() noexcept { return runtime_.state(); }
266
268 return runtime_.identity().definitionHandle();
269}
270
271void EffectDefinitionRuntime::setActive(bool active) noexcept { runtime_.setActive(active); }
272
273bool EffectDefinitionRuntime::isActive() const noexcept { return runtime_.isActive(); }
274
276 if (effect == nullptr)
278 "effect runtime cannot project to a null instance",
279 "effect", {}, "effects.definition_runtime"));
280 EffectInstance candidate = *effect;
281 candidate.id = identity().instanceId.format();
282 candidate.subject = subject_;
283 candidate.type = identity().definition.id().name();
284 candidate.source = source_;
285 candidate.stackKey = state().stackKey;
286 candidate.priority = state().priority;
287 candidate.duration = state().duration;
288 candidate.remaining = state().remaining;
289 candidate.magnitude = state().magnitude;
290 candidate.stackCount = state().stackCount;
291 candidate.policy = state().policy;
292 candidate.payload = state().payload;
293 candidate.tags = state().tags;
294 candidate.identity = eve::EffectId::fromUuid(identity().instanceId);
295 candidate.definitionIdentity = identity();
296 candidate.reloadPolicy = policy_;
297 using std::swap;
298 swap(*effect, candidate);
300}
301
303 if (registry_ == nullptr)
305 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect definition registry is not bound",
306 "registry", {}, "effects.definition_runtime"));
307 auto next = currentHandle(*registry_, identity().definition);
308 if (!next) return eve::Result<eve::definition::ReloadOutcome>::failure(next.status());
309 auto defaults = resolveState(*registry_, identity().definition, next.value());
310 if (!defaults) return eve::Result<eve::definition::ReloadOutcome>::failure(defaults.status());
311 EffectRuntimeState defaultState = std::move(defaults).takeValue();
312 auto rebuild = [defaultState](const EffectRuntimeState& oldState, const eve::definition::InstanceIdentity&,
314 EffectRuntimeState rebuilt = defaultState;
315 if (oldState.remaining >= 0.0)
316 rebuilt.remaining = rebuilt.duration > 0.0 ? std::min(oldState.remaining, rebuilt.duration) : -1.0;
317 rebuilt.stackCount =
318 rebuilt.maxStacks == 0 ? oldState.stackCount : std::min(oldState.stackCount, rebuilt.maxStacks);
319 if (rebuilt.stackCount == 0) rebuilt.stackCount = 1;
320 return eve::Result<EffectRuntimeState>::success(std::move(rebuilt));
321 };
322 auto result = runtime_.reload(next.value(), policy, defaultState, std::move(rebuild));
323 if (result.ok()) policy_ = policy;
324 return result;
325}
326
327} // namespace eve::effects
double value
Value::Object payload
bool & active
int subject
Definition AnimSmr.cpp:163
std::string output
AuthorityStoreHandleRef reference
Definition Authority.cpp:24
std::map< std::string, Var > values
Strongly typed effect definition/runtime adapter.
std::vector< std::uint8_t > stored
Definition Evpack.cpp:171
std::uint32_t key
std::string text
std::string name
const std::string * tag
std::unique_ptr< ParticleEffect > effect
PrimitiveHandle handle
double number
std::uint32_t count
const UnitySourceAsset & source
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
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
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
std::string format() const
Formats the ID in lower-case canonical UUID text.
Definition Identity.h:173
static constexpr Id128 fromUuid(const Id128< OtherTag > &value) noexcept
Re-tags an existing UUID at an explicit domain boundary.
Definition Identity.h:136
Binds a typed effect runtime to a generation-qualified definition.
eve::Result< eve::definition::ReloadOutcome > reload(eve::definition::ReloadPolicy policy)
Resolve the registry's current generation and execute a policy.
bool isActive() const noexcept
Return whether the effect is active for reload policy decisions.
static eve::Result< EffectDefinitionRuntime > create(eve::definitions::DefinitionRegistry &registry, eve::DefinitionRef definition, std::string subject, std::string source, eve::PersistentId instanceId, eve::definition::ReloadPolicy policy=eve::definition::ReloadPolicy::KeepInstanceValues)
Create a typed effect runtime from a registry definition.
const eve::definition::InstanceIdentity & identity() const noexcept
Borrow the unified instance identity.
eve::Result< void > applyTo(EffectInstance *effect) const
Atomically project the typed runtime into an EffectInstance.
const EffectRuntimeState & state() const noexcept
Borrow the strongly typed effect runtime state.
void setActive(bool active) noexcept
Mark this effect active/inactive for RejectWhileActive.
eve::definition::DefinitionHandle definitionHandle() const noexcept
Borrow the current generation-qualified definition handle.
ReloadPolicy
Policy used when a live definition is replaced.
StackMode
Determines whether a matching application replaces, reuses, or creates an instance.
Definition EffectTypes.h:31
OverflowPolicy
Determines what happens when a stack-count limit would be exceeded.
Definition EffectTypes.h:43
DurationPolicy
Determines how an application changes an existing remaining duration.
Definition EffectTypes.h:37
StackCountPolicy
Determines how an application changes an existing stack count.
Definition EffectTypes.h:34
MagnitudePolicy
Determines how an application changes an existing magnitude value.
Definition EffectTypes.h:40
bool readString(const eve::Value::Object &object, const char *name, std::string &output)
const EditorValue * field(const EditorValue &value, const char *name)
int64_t integer(const RuntimeTensor &v, size_t i=0)
Integer.
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
One owned runtime effect fact.
eve::EffectId identity
UUID-backed identity for persistence/event boundaries; nil in the legacy facade.
EffectPolicy policy
Typed application policy projected from the current definition.
eve::definition::InstanceIdentity definitionIdentity
Canonical definition link used by the typed definition adapter.
eve::definition::ReloadPolicy reloadPolicy
std::vector< std::string > tags
Typed mutable lifecycle values for one effect instance.