载入中...
搜索中...
未找到
Action.cpp
浏览该文件的文档.
1#include "action/Action.h"
2
4
5#include <algorithm>
6#include <limits>
7#include <string>
8#include <utility>
9
10namespace eve::action {
11namespace {
12
13Result<void> failureFrom(const Status& status) { return Result<void>::failure(status); }
14
15bool isEmptyCondition(const decision::Condition& condition) {
16 return condition.kind() == decision::ConditionKind::All && condition.children().empty() && condition.isValid();
17}
18
19bool isTerminal(ActionPhase phase) {
21}
22
23Status cancelledStatus() {
25 Diagnostic::error(DiagnosticCode::Cancelled, "action execution was cancelled", "action"));
26}
27
28Status notFoundStatus() {
30 Diagnostic::error(DiagnosticCode::NotFound, "action execution was not found", "execution"));
31}
32
33Status invalidStatus(std::string message, std::string path = {}) {
36}
37
38Status pendingStatus() { return Status::success(StatusCode::Pending); }
39
40} // namespace
41
42const char* actionPhaseName(ActionPhase phase) noexcept {
43 switch (phase) {
44 case ActionPhase::Requested: return "requested";
45 case ActionPhase::Validating: return "validating";
46 case ActionPhase::Windup: return "windup";
47 case ActionPhase::Active: return "active";
48 case ActionPhase::Recover: return "recover";
49 case ActionPhase::Completed: return "completed";
50 case ActionPhase::Cancelled: return "cancelled";
51 case ActionPhase::Failed: return "failed";
52 }
53 return "unknown";
54}
55
57 if (!id.isValid()) return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "action definition id is invalid", "id"));
58 if (!condition.isValid())
59 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "action condition is invalid", "condition"));
61 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "action phase durations must be non-negative", "timing"));
62
63 switch (targetingMode) {
66 if (targetingSpec)
67 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "targetingSpec is only valid for Query actions", "targetingSpec"));
68 break;
70 if (!targetingSpec)
71 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "Query actions require a targetingSpec", "targetingSpec"));
72 {
73 auto valid = targetingSpec->validate();
74 if (!valid) return failureFrom(valid.status());
75 }
76 break;
77 }
78
79 if (cost && !cost->isValid())
80 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "action cost must be a validated non-empty CostSpec", "cost"));
81 for (const auto& effectId : effectIds) {
82 if (effectId.empty())
83 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "action effect ids must not be empty", "effectIds"));
84 }
85 if (timeline) {
86 auto timelineValid = timeline->validate();
87 if (!timelineValid) return failureFrom(timelineValid.status());
88 if (timeline->actionId != id)
89 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "action timeline id does not match its definition", "timeline.actionId"));
90 auto windupAndActive = timing.windup.tryAdd(timing.active);
91 if (!windupAndActive) return failureFrom(windupAndActive.status());
92 auto total = windupAndActive.value().tryAdd(timing.recover);
93 if (!total) return failureFrom(total.status());
94 if (timeline->duration != total.value())
95 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "action timeline duration must equal windup + active + recover", "timeline.durationNs"));
96 }
97 return Result<void>::success();
98}
99
101 auto definitionValid = definition.validate();
102 if (!definitionValid) return failureFrom(definitionValid.status());
103 if (actionId != definition.id)
104 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "action request id does not match its definition", "actionId"));
105
106 switch (definition.targetingMode) {
108 if (!targetEntities.empty() || targetingQuery)
109 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "a non-targeted action cannot carry target selection", "targets"));
110 break;
112 if (targetEntities.empty())
113 return Result<void>::failure(Diagnostic::error(DiagnosticCode::PreconditionViolation, "an Explicit action requires at least one target", "targetEntities"));
114 if (targetingQuery)
115 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "Explicit actions cannot carry a targeting query", "targetingQuery"));
116 break;
118 if (!targetingQuery)
119 return Result<void>::failure(Diagnostic::error(DiagnosticCode::PreconditionViolation, "a Query action requires a targeting query", "targetingQuery"));
120 if (!targetEntities.empty())
121 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "Query actions cannot carry explicit targets", "targetEntities"));
122 {
123 auto valid = targetingQuery->validate();
124 if (!valid) return failureFrom(valid.status());
125 }
126 break;
127 }
128 return Result<void>::success();
129}
130
134
135Result<ActionExecutionId> ActionRuntime::nextExecutionId() {
136 if (nextId_.isZero())
138 Diagnostic::error(DiagnosticCode::InvariantViolation, "action execution id is zero", "execution.id"));
139 const ActionExecutionId id = nextId_;
140 const auto next = nextId_.incremented();
141 if (!next)
143 Diagnostic::error(DiagnosticCode::InvariantViolation, "action execution id exhausted", "execution.id"));
144 nextId_ = *next;
146}
147
149 auto definitionValid = definition.validate();
150 if (!definitionValid) return Result<ActionExecutionId>::failure(definitionValid.status());
151 auto requestValid = request.validate(definition);
152 if (!requestValid) return Result<ActionExecutionId>::failure(requestValid.status());
153
154 auto idResult = nextExecutionId();
155 if (!idResult) return Result<ActionExecutionId>::failure(idResult.status());
156 const ActionExecutionId id = std::move(idResult).takeValue();
157 // Construct inside this friend member rather than using make_unique:
158 // ActionExecution's constructor is intentionally private so adapters
159 // cannot manufacture lifecycle owners.
160 std::unique_ptr<ActionExecution> execution(new ActionExecution(id, std::move(definition), std::move(request)));
161 const auto [it, inserted] = executions_.emplace(id, std::move(execution));
162 if (!inserted)
164 Diagnostic::error(DiagnosticCode::Conflict, "action execution id collided", "execution.id"));
166}
167
169 const auto it = executions_.find(id);
170 return it == executions_.end() ? nullptr : it->second.get();
171}
172
174 const auto it = executions_.find(id);
175 return it == executions_.end() ? nullptr : it->second.get();
176}
177
178Result<void> ActionRuntime::validateExecution(ActionExecution& execution) {
179 const auto& definition = execution.definition_;
180 const auto& request = execution.request_;
181
182 if (!isEmptyCondition(definition.condition)) {
183 if (services_.conditions == nullptr)
184 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "action condition requires an evaluator", "services.conditions"));
185 auto condition = services_.conditions->evaluate(definition, request);
186 if (!condition) return failureFrom(condition.status());
187 auto checked = std::move(condition).takeValue();
188 if (!checked.passed()) {
189 DiagnosticDetails details;
190 details.emplace_back("reason", decision::conditionReasonCodeName(checked.reasonCode()));
192 "action condition was rejected", "condition",
193 details));
194 }
195 }
196
197 if (definition.targetingMode == TargetingMode::Query) {
198 if (services_.targeting == nullptr)
199 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "Query action requires a target resolver", "services.targeting"));
200 sensing::TargetingQuery query = *request.targetingQuery;
201 query.spec = *definition.targetingSpec;
202 auto targets = services_.targeting->resolve(query);
203 if (!targets) return failureFrom(targets.status());
204 execution.resolvedTargets_ = std::move(targets).takeValue();
205 }
206
207 if (definition.activeExecutionRequired || !definition.effectIds.empty()) {
208 if (services_.effects == nullptr && services_.transactionEffect == nullptr)
209 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "action Active phase requires an effect executor", "services.effects"));
210 }
211
212 if (definition.cost) {
213 const bool transactionBacked =
214 services_.transactionEffect != nullptr && services_.transactionAccount != nullptr;
215 if (services_.resources == nullptr && !transactionBacked)
216 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "cost-bearing action requires a resource provider", "services.resources"));
217 auto affordability = services_.resources != nullptr
218 ? services_.resources->canAfford(definition, request, *definition.cost)
219 : services_.transactionAccount->canAfford(*definition.cost);
220 if (!affordability) return failureFrom(affordability.status());
221 auto checked = std::move(affordability).takeValue();
222 if (!checked.affordable) {
223 DiagnosticDetails details;
224 for (const auto& shortfall : checked.shortfalls) {
225 details.emplace_back("resource", shortfall.resource.value());
226 details.emplace_back("required", std::to_string(shortfall.required.value()));
227 details.emplace_back("available", std::to_string(shortfall.available.value()));
228 }
230 "action resource cost is not affordable", "cost",
231 details));
232 }
233 }
235}
236
237Result<void> ActionRuntime::enterActive(ActionExecution& execution, SimulationTick tick) {
238 const auto& definition = execution.definition_;
239 const auto& request = execution.request_;
240
241 if (services_.transactionEffect != nullptr) {
242 if (definition.cost && services_.transactionAccount == nullptr)
243 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "transaction-backed action cost requires an account", "services.transactionAccount"));
244
245 const std::string transactionId = request.transactionId.empty() ? "action." + definition.id.format() + "." +
246 std::to_string(execution.id_.value())
247 : request.transactionId;
248 transaction::TransactionContext context(transactionId);
249 auto transactionResult =
250 definition.cost
252 *services_.transactionEffect)
253 : transaction::AtomicResourcePayment::execute(context, *services_.transactionEffect);
254 if (!transactionResult) return failureFrom(transactionResult.status());
255 execution.transactionReceipt_ = std::move(transactionResult).takeValue();
256 execution.activeExecuted_ = true;
258 }
259
260 std::unique_ptr<IActionEffectOperation> stagedEffect;
261 if (definition.activeExecutionRequired || !definition.effectIds.empty()) {
262 if (services_.effects == nullptr)
263 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "action Active phase requires an effect executor", "services.effects"));
264 auto prepared = services_.effects->prepare(
265 definition, request, execution.resolvedTargets_ ? &*execution.resolvedTargets_ : nullptr, tick);
266 if (!prepared) return failureFrom(prepared.status());
267 stagedEffect = std::move(prepared).takeValue();
268 if (!stagedEffect)
269 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvariantViolation, "effect executor returned an empty staged operation", "effects"));
270 }
271
272 std::optional<resource::Reservation> reservation;
273 if (definition.cost) {
274 if (services_.resources == nullptr) {
275 if (stagedEffect) stagedEffect->rollback();
276 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "activating a cost-bearing action requires a resource provider", "services.resources"));
277 }
278 auto reserved = services_.resources->reserve(definition, request, *definition.cost);
279 if (!reserved) {
280 if (stagedEffect) stagedEffect->rollback();
281 return failureFrom(reserved.status());
282 }
283 auto credential = std::move(reserved).takeValue();
284 if (!credential.isValid()) {
285 if (stagedEffect) stagedEffect->rollback();
286 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvariantViolation, "resource provider returned an invalid reservation", "cost.reservation"));
287 }
288 reservation = std::move(credential);
289
290 // The provider routes this operation by reservation.account. The
291 // action runtime never retains an account pointer, so a provider may
292 // change its default/request routing without misdirecting rollback.
293 auto committed = services_.resources->commit(*reservation);
294 if (!committed) {
295 auto rolledBack = services_.resources->rollback(*reservation);
296 if (!rolledBack) {
297 rolledBack.ignore("commit failed and rollback also failed; commit diagnostic retained");
298 }
299 if (stagedEffect) stagedEffect->rollback();
300 return failureFrom(committed.status());
301 }
302 auto receipt = std::move(committed).takeValue();
303 execution.receipt_ = std::move(receipt);
304 }
305
306 if (stagedEffect) {
307 // Resource commit is the only fallible commit boundary. Once it has
308 // succeeded, the staged effect commit is noexcept and cannot leave a
309 // successfully debited action without its Active operation.
310 stagedEffect->commit();
311 }
312 // Mark the whole Active transaction as complete even for a timing-only or
313 // cost-only action. Otherwise a zero-effect action would reserve/commit
314 // its cost again on every subsequent advance while it remains Active.
315 execution.activeExecuted_ = true;
317}
318
319Result<void> ActionRuntime::addElapsed(ActionExecution& execution, Duration amount) {
320 auto phase = execution.phaseElapsed_.tryAdd(amount);
321 if (!phase) return failureFrom(phase.status());
322 auto total = execution.totalElapsed_.tryAdd(amount);
323 if (!total) return failureFrom(total.status());
324 execution.phaseElapsed_ = std::move(phase).takeValue();
325 execution.totalElapsed_ = std::move(total).takeValue();
326 return Result<void>::success();
327}
328
329void ActionRuntime::transition(ActionExecution& execution, ActionPhase next, SimulationTick tick,
330 std::vector<ActionTransition>& transitions) {
331 if (execution.phase_ == next) return;
332 transitions.push_back({execution.phase_, next, tick});
333 execution.phase_ = next;
334 execution.phaseElapsed_ = Duration::zero();
335 if (next == ActionPhase::Completed)
336 execution.status_ = Status::success(StatusCode::Applied);
337 else if (next == ActionPhase::Requested || next == ActionPhase::Validating || next == ActionPhase::Windup ||
338 next == ActionPhase::Active || next == ActionPhase::Recover)
339 execution.status_ = pendingStatus();
340}
341
342void ActionRuntime::failExecution(ActionExecution& execution, Status status, SimulationTick tick,
343 std::vector<ActionTransition>* transitions) {
344 if (status.isSuccess())
346 Diagnostic::error(DiagnosticCode::Failed, "action execution failed", "action"));
347 if (transitions && execution.phase_ != ActionPhase::Failed)
348 transition(execution, ActionPhase::Failed, tick, *transitions);
349 else
350 execution.phase_ = ActionPhase::Failed;
351 execution.status_ = std::move(status);
352}
353
355 ActionExecution* execution = find(id);
356 if (execution == nullptr) return Result<ActionAdvance>::failure(notFoundStatus());
357 if (delta.nanoseconds() < 0)
358 return Result<ActionAdvance>::failure(invalidStatus("action advance duration must be non-negative", "delta"));
359 if (tick < execution->lastTick_)
360 return Result<ActionAdvance>::failure(invalidStatus("action simulation tick moved backwards", "tick"));
361 execution->lastTick_ = tick;
362 const Duration timelinePrevious = execution->totalElapsed_;
363
364 if (execution->phase_ == ActionPhase::Completed) {
365 ActionAdvance result{id, execution->phase_, {}, execution->phaseElapsed_, execution->totalElapsed_};
367 }
368 if (execution->phase_ == ActionPhase::Cancelled || execution->phase_ == ActionPhase::Failed)
369 return Result<ActionAdvance>::failure(execution->status());
370
371 Duration remaining = delta;
372 std::vector<ActionTransition> transitions;
373 for (int guard = 0; guard < 8; ++guard) {
374 if (execution->phase_ == ActionPhase::Requested) {
375 transition(*execution, ActionPhase::Validating, tick, transitions);
376 auto valid = validateExecution(*execution);
377 if (!valid) {
378 failExecution(*execution, valid.status(), tick, &transitions);
379 return Result<ActionAdvance>::failure(execution->status());
380 }
381 continue;
382 }
383
384 if (execution->phase_ == ActionPhase::Validating) {
385 if (execution->definition_.timing.windup.isZero())
386 transition(*execution, ActionPhase::Active, tick, transitions);
387 else
388 transition(*execution, ActionPhase::Windup, tick, transitions);
389 continue;
390 }
391
392 if (execution->phase_ == ActionPhase::Windup) {
393 const Duration phase = execution->definition_.timing.windup;
394 const auto needed = phase.nanoseconds() - execution->phaseElapsed_.nanoseconds();
395 if (remaining.nanoseconds() < needed) {
396 auto added = addElapsed(*execution, remaining);
397 if (!added) {
398 failExecution(*execution, added.status(), tick, &transitions);
399 return Result<ActionAdvance>::failure(execution->status());
400 }
401 remaining = Duration::zero();
402 break;
403 }
404 auto added = addElapsed(*execution, Duration::fromNanoseconds(needed));
405 if (!added) {
406 failExecution(*execution, added.status(), tick, &transitions);
407 return Result<ActionAdvance>::failure(execution->status());
408 }
409 remaining = Duration::fromNanoseconds(remaining.nanoseconds() - needed);
410 transition(*execution, ActionPhase::Active, tick, transitions);
411 continue;
412 }
413
414 if (execution->phase_ == ActionPhase::Active) {
415 if (!execution->activeExecuted_) {
416 auto active = enterActive(*execution, tick);
417 if (!active) {
418 failExecution(*execution, active.status(), tick, &transitions);
419 return Result<ActionAdvance>::failure(execution->status());
420 }
421 }
422 const Duration phase = execution->definition_.timing.active;
423 const auto needed = phase.nanoseconds() - execution->phaseElapsed_.nanoseconds();
424 if (needed == 0) {
425 transition(*execution, ActionPhase::Recover, tick, transitions);
426 continue;
427 }
428 if (remaining.nanoseconds() < needed) {
429 auto added = addElapsed(*execution, remaining);
430 if (!added) {
431 failExecution(*execution, added.status(), tick, &transitions);
432 return Result<ActionAdvance>::failure(execution->status());
433 }
434 remaining = Duration::zero();
435 break;
436 }
437 auto added = addElapsed(*execution, Duration::fromNanoseconds(needed));
438 if (!added) {
439 failExecution(*execution, added.status(), tick, &transitions);
440 return Result<ActionAdvance>::failure(execution->status());
441 }
442 remaining = Duration::fromNanoseconds(remaining.nanoseconds() - needed);
443 transition(*execution, ActionPhase::Recover, tick, transitions);
444 continue;
445 }
446
447 if (execution->phase_ == ActionPhase::Recover) {
448 const Duration phase = execution->definition_.timing.recover;
449 const auto needed = phase.nanoseconds() - execution->phaseElapsed_.nanoseconds();
450 if (needed == 0) {
451 transition(*execution, ActionPhase::Completed, tick, transitions);
452 continue;
453 }
454 if (remaining.nanoseconds() < needed) {
455 auto added = addElapsed(*execution, remaining);
456 if (!added) {
457 failExecution(*execution, added.status(), tick, &transitions);
458 return Result<ActionAdvance>::failure(execution->status());
459 }
460 remaining = Duration::zero();
461 break;
462 }
463 auto added = addElapsed(*execution, Duration::fromNanoseconds(needed));
464 if (!added) {
465 failExecution(*execution, added.status(), tick, &transitions);
466 return Result<ActionAdvance>::failure(execution->status());
467 }
468 remaining = Duration::fromNanoseconds(remaining.nanoseconds() - needed);
469 transition(*execution, ActionPhase::Completed, tick, transitions);
470 continue;
471 }
472
473 if (isTerminal(execution->phase_)) break;
474 }
475
476 std::vector<ActionTimelineEvent> timelineEvents;
477 std::vector<ActionActiveBlock> activeBlocks;
478 if (execution->definition_.timeline) {
479 auto sampled = execution->definition_.timeline->sample(timelinePrevious, execution->totalElapsed_,
480 !execution->timelineStarted_);
481 if (!sampled) {
482 failExecution(*execution, sampled.status(), tick, &transitions);
483 return Result<ActionAdvance>::failure(execution->status());
484 }
485 timelineEvents = std::move(sampled).takeValue();
486 execution->timelineStarted_ = true;
487 auto active = execution->definition_.timeline->activeBlocks(execution->totalElapsed_);
488 if (!active) {
489 failExecution(*execution, active.status(), tick, &transitions);
490 return Result<ActionAdvance>::failure(execution->status());
491 }
492 activeBlocks = std::move(active).takeValue();
493 }
494 if (execution->phase_ == ActionPhase::Completed) {
495 ActionAdvance result{id,
496 execution->phase_,
497 std::move(transitions),
498 execution->phaseElapsed_,
499 execution->totalElapsed_,
500 std::move(timelineEvents),
501 std::move(activeBlocks)};
503 }
504 ActionAdvance result{id,
505 execution->phase_,
506 std::move(transitions),
507 execution->phaseElapsed_,
508 execution->totalElapsed_,
509 std::move(timelineEvents),
510 std::move(activeBlocks)};
511 return Result<ActionAdvance>::success(std::move(result), pendingStatus());
512}
513
515 ActionExecution* execution = find(id);
516 if (execution == nullptr) return Result<void>::failure(notFoundStatus());
517 if (execution->phase_ == ActionPhase::Completed)
520 Diagnostic::error(DiagnosticCode::Conflict, "completed action execution cannot be cancelled", "action")));
521 if (execution->phase_ == ActionPhase::Failed) return Result<void>::failure(execution->status_);
523
524 execution->phase_ = ActionPhase::Cancelled;
525 execution->phaseElapsed_ = Duration::zero();
526 execution->lastTick_ = tick;
527 execution->status_ = cancelledStatus();
529}
530
531void ActionRuntime::clear() noexcept {
532 executions_.clear();
533 nextId_ = ActionExecutionId{1};
534}
535
536} // namespace eve::action
Renderer- and ruleset-neutral gameplay action pipeline.
bool & active
Small transaction facade for a resource cost plus one domain effect.
float phase
Definition CaveMesh.cpp:58
eve::Value condition
std::string message
const GltfImportRequest & request
wgpu::PopErrorScopeStatus status
bool valid
std::string path
Definition PlayHost.cpp:110
std::string id
Definition PlayHost.cpp:108
SimulationTick tick
const VegetationPresetContext & context
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
Signed, fixed-resolution simulation duration in nanoseconds.
Definition Time.h:43
constexpr std::int64_t nanoseconds() const noexcept
Return the exact signed nanosecond representation.
Definition Time.h:69
Result< Duration > tryAdd(Duration other) const
Add two durations with overflow validation.
Definition Time.cpp:30
static constexpr Duration fromNanoseconds(std::int64_t nanoseconds) noexcept
Construct an exact duration from nanoseconds.
Definition Time.h:55
static constexpr Duration zero() noexcept
Return zero duration.
Definition Time.h:60
constexpr bool isZero() const noexcept
Whether this duration is exactly zero.
Definition Time.h:75
void ignore(std::string_view reason={}) const noexcept
Explicitly discard this result after documenting the reason.
Definition Result.h:537
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 failure(StatusCode code, Diagnostic diagnostic)
Construct a failed status with one diagnostic.
Definition Status.h:84
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
Type kind() const noexcept
Compatibility spelling for the former dialogue data tree.
Definition Value.h:85
Read-only view of one execution owned by ActionRuntime.
Definition Action.h:390
const Status & status() const noexcept
Current structured execution status.
Definition Action.h:409
Result< ActionAdvance > advance(ActionExecutionId id, SimulationTick tick, Duration delta)
Advance one execution by an injected simulation duration.
Definition Action.cpp:354
Result< void > cancel(ActionExecutionId id, SimulationTick tick)
Cancel a non-terminal execution before its Active transaction.
Definition Action.cpp:514
void clear() noexcept
Discard every retained execution and restart deterministic local ids at one.
Definition Action.cpp:531
const ActionExecution * find(ActionExecutionId id) const noexcept
Find a borrowed read-only execution view, or null when absent.
Definition Action.cpp:168
Result< ActionExecutionId > submit(ActionDefinition definition, ActionRequest request)
Submit a validated definition/request pair in Requested phase.
Definition Action.cpp:148
virtual Result< decision::ConditionResult > evaluate(const ActionDefinition &definition, const ActionRequest &request) const =0
Evaluate one action condition.
virtual Result< std::unique_ptr< IActionEffectOperation > > prepare(const ActionDefinition &definition, const ActionRequest &request, const sensing::TargetSet *targets, SimulationTick tick)=0
Prepare the active operation once without applying its effect.
virtual Result< resource::Receipt > commit(const resource::Reservation &reservation)=0
Commit a reservation by its account nonce.
virtual Result< resource::Reservation > reserve(const ActionDefinition &definition, const ActionRequest &request, const resource::CostSpec &cost)=0
Reserve a cost at the Active boundary.
virtual Result< void > rollback(const resource::Reservation &reservation)=0
Roll back a reservation by its account nonce.
virtual Result< resource::Affordability > canAfford(const ActionDefinition &definition, const ActionRequest &request, const resource::CostSpec &cost) const =0
Query affordability without reserving or mutating an account.
virtual Result< sensing::TargetSet > resolve(const sensing::TargetingQuery &query) const =0
Resolve a validated targeting query.
Result< sensing::TargetSet > resolve(const sensing::TargetingQuery &query) const override
Resolve a validated targeting query.
Definition Action.cpp:131
bool isValid() const noexcept
Return whether the factory invariants for this node hold.
constexpr std::optional< StrongUint64 > incremented() const noexcept
Returns the next value, or empty instead of unsigned wraparound.
constexpr bool isZero() const noexcept
Returns whether this value is zero.
virtual eve::Result< Affordability > canAfford(const CostSpec &cost) const =0
Query whether a cost can be paid from currently available funds.
Result< TargetSet > resolve(const TargetingQuery &query) const
Resolves a constrained candidate set through registered capabilities.
static eve::Result< TransactionReceipt > execute(const TransactionContext &context, std::span< ITransactionParticipant * > participants)
Execute already prepared domain participants as one transaction.
const char * actionPhaseName(ActionPhase phase) noexcept
Return the stable lowercase spelling of an action phase.
Definition Action.cpp:42
ActionPhase
Lifecycle phase shared by every gameplay action adapter.
Definition Action.h:40
@ Query
The request supplies a sensing query resolved during validation.
@ None
The action has no target selection.
@ Explicit
The request already owns the selected ECS target handles.
const char * conditionReasonCodeName(ConditionReasonCode code) noexcept
Return the stable lowercase spelling of a condition reason code. @ownership Borrowed process-static t...
Definition Condition.cpp:82
eve::StatusCode Status
constexpr HexDirection next(HexDirection d) noexcept
The next direction clockwise (NW wraps to NE).
Definition HexMetrics.h:76
std::vector< RuntimeTensor > execute(const Node &n, const std::vector< const RuntimeTensor * > &in, OnnxCompute *compute)
Execute.
std::vector< DiagnosticDetail > DiagnosticDetails
Owning collection of diagnostic details with stable insertion order.
Definition Diagnostic.h:88
detail::StrongUint64< detail::SimulationTickTag > SimulationTick
Deterministic simulation time step; it is not wall-clock time.
Definition Time.h:31
Result payload for one deterministic advance call.
Definition Action.h:175
Immutable-by-convention definition of one action kind.
Definition Action.h:90
TargetingMode targetingMode
Target acquisition policy for this action.
Definition Action.h:98
std::vector< std::string > effectIds
Stable effect-definition ids interpreted by the active executor.
Definition Action.h:104
decision::Condition condition
Side-effect-free precondition tree.
Definition Action.h:96
std::optional< ActionTimeline > timeline
Optional versioned choreography authored by the action editor.
Definition Action.h:110
std::optional< resource::CostSpec > cost
Optional canonical cost reserved and committed by the pipeline.
Definition Action.h:102
ActionTiming timing
Timed phase policy in simulation durations.
Definition Action.h:94
Result< void > validate() const
Validate definition invariants without changing the definition.
Definition Action.cpp:56
std::optional< sensing::TargetingSpec > targetingSpec
Constraints used when targetingMode is Query.
Definition Action.h:100
One caller-owned request to instantiate an action execution.
Definition Action.h:127
Result< void > validate(const ActionDefinition &definition) const
Validate request/definition correspondence without side effects.
Definition Action.cpp:100
LogicalId actionId
Definition identity this request intends to execute.
Definition Action.h:129
std::optional< sensing::TargetingQuery > targetingQuery
Query input, used only when the definition selects Query.
Definition Action.h:135
std::vector< ecs::EntityHandle > targetEntities
Explicit targets, used only when the definition selects Explicit.
Definition Action.h:133
IActionEffectExecutor * effects
Optional active executor; required by effects/active operations.
Definition Action.h:359
IActionTargetResolver * targeting
Optional query resolver; required by Query actions.
Definition Action.h:355
IActionResourceProvider * resources
Optional account provider; required by cost-bearing actions.
Definition Action.h:357
transaction::ITransactionParticipant * transactionEffect
Optional common transaction effect participant.
Definition Action.h:367
resource::IResourceAccount * transactionAccount
Account paired with transactionEffect for a cost-bearing action.
Definition Action.h:369
IActionConditionEvaluator * conditions
Optional precondition evaluator; empty is valid for an empty Condition.
Definition Action.h:353
Duration recover
Time spent in Recover before completion.
Definition Action.h:80
Duration active
Time for which the Active result remains in progress.
Definition Action.h:78
Duration windup
Time spent in Windup before Active.
Definition Action.h:76
A query passed from a domain targeting algorithm to sensing.
Definition Targeting.h:350