载入中...
搜索中...
未找到
StateAccessAdapter.cpp
浏览该文件的文档.
2
4
5#include <array>
6#include <cmath>
7#include <limits>
8#include <map>
9#include <optional>
10#include <string>
11#include <utility>
12
13namespace eve::statepatch {
14namespace {
15
16eve::Result<eve::MutationReceipt> failure(eve::DiagnosticCode code, std::string message, std::string path = {}) {
19}
20
21eve::Result<eve::Value> readJsonValue(const Store& store, std::string_view subject, std::string_view key) {
22 if (!store.has(std::string(subject), std::string(key)))
24 eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "state value is absent", "state"));
25 auto parsed = eve::Value::fromJson(store.get(std::string(subject), std::string(key)));
26 if (!parsed) return eve::Result<eve::Value>::failure(parsed.status());
27 return parsed;
28}
29
30bool numeric(const eve::Value& value, double& output) {
31 if (value.isInt64()) {
32 output = static_cast<double>(value.asInt());
33 return true;
34 }
35 if (value.isDouble()) {
36 output = value.asDouble();
37 return std::isfinite(output);
38 }
39 return false;
40}
41
42std::string tagKey(std::string_view tag) { return "tag." + std::string(tag); }
43
44} // namespace
45
46std::optional<eve::Value> StatePatchStateAdapter::value(std::string_view subject, std::string_view key) const {
47 auto result = readJsonValue(store_, subject, key);
48 if (!result) return std::nullopt;
49 return std::move(result).takeValue();
50}
51
52std::optional<bool> StatePatchStateAdapter::hasTag(std::string_view subject, std::string_view tag) const {
53 if (subject.empty() || tag.empty()) return std::nullopt;
54 const std::string key = tagKey(tag);
55 if (!store_.has(std::string(subject), key)) return false;
56 auto value = readJsonValue(store_, subject, key);
57 if (!value) return std::nullopt;
58 const eve::Value& parsed = value.value();
59 if (!parsed.isBool()) return std::nullopt;
60 return parsed.asBool();
61}
62
63std::optional<eve::Value> StatePatchStateAdapter::state(std::string_view subject, std::string_view key) const {
64 return value(subject, key);
65}
66
67eve::Result<eve::MutationReceipt> StatePatchStateAdapter::apply(std::span<const eve::StateMutation> mutations,
69 if (context.transactionId.empty())
70 return failure(eve::DiagnosticCode::InvalidArgument, "persistent dialogue mutations require a transaction id",
71 "transactionId");
72
73 using Key = std::pair<std::string, std::string>;
74 std::map<Key, std::optional<eve::Value>> pending;
75 auto batchReference = store_.newBatch();
76 if (!batchReference) return eve::Result<eve::MutationReceipt>::failure(batchReference.status());
77 const PatchBatchHandleRef batchHandle = std::move(batchReference).takeValue();
78 auto batch = store_.resolveBatch(batchHandle);
79 if (!batch.isBound())
80 return failure(eve::DiagnosticCode::StaleHandle, "StatePatch patch batch became stale during allocation",
81 "batch");
82 struct BatchLease {
83 Store& store;
85 ~BatchLease() { store.releaseBatch(handle).ignore("release StatePatch adapter batch"); }
86 } batchLease{store_, batchHandle};
87
88 for (std::size_t index = 0; index < mutations.size(); ++index) {
89 const auto& mutation = mutations[index];
90 const std::string path = "mutations[" + std::to_string(index) + "]";
91 if (!mutation.persistent)
92 return failure(eve::DiagnosticCode::Unsupported, "StatePatch adapter accepts persistent mutations only",
93 path);
94 if (mutation.subject.empty() || mutation.key.empty())
95 return failure(eve::DiagnosticCode::InvalidArgument, "persistent mutation requires subject and key", path);
96
97 const std::string sourceKey = mutation.key;
98 const std::string key =
99 mutation.kind == eve::MutationKind::AddTag || mutation.kind == eve::MutationKind::RemoveTag
100 ? tagKey(sourceKey)
101 : sourceKey;
102 const Key pendingKey{mutation.subject, key};
103 auto current = [&]() -> std::optional<eve::Value> {
104 const auto found = pending.find(pendingKey);
105 if (found != pending.end()) return found->second;
106 if (!store_.has(mutation.subject, key)) return std::nullopt;
107 auto existing = readJsonValue(store_, mutation.subject, key);
108 if (!existing) return std::nullopt;
109 return std::move(existing).takeValue();
110 };
111
112 bool appended = false;
113 switch (mutation.kind) {
114 case eve::MutationKind::Set: {
115 auto json = mutation.value.toJson();
116 if (!json)
117 return failure(json.error() ? json.error()->code() : eve::DiagnosticCode::SerializationError,
118 json.error() ? json.error()->message() : "state value cannot be serialized",
119 path + ".value");
120 appended = batch->set(mutation.subject, key, json.value());
121 if (appended) pending[pendingKey] = mutation.value;
122 break;
123 }
124 case eve::MutationKind::Remove:
125 appended = batch->remove(mutation.subject, key);
126 if (appended) pending[pendingKey] = std::nullopt;
127 break;
128 case eve::MutationKind::AddTag:
129 appended = batch->set(mutation.subject, key, "true");
130 if (appended) pending[pendingKey] = eve::Value(true);
131 break;
132 case eve::MutationKind::RemoveTag:
133 appended = batch->remove(mutation.subject, key);
134 if (appended) pending[pendingKey] = std::nullopt;
135 break;
136 case eve::MutationKind::AddNumber: {
137 double delta = 0.0;
138 if (!numeric(mutation.value, delta))
139 return failure(eve::DiagnosticCode::InvalidArgument, "AddNumber requires a finite numeric value",
140 path + ".value");
141 const auto existing = current();
142 double base = 0.0;
143 if (existing && !numeric(*existing, base))
145 "AddNumber cannot add to a non-numeric state value", path);
146 const double total = base + delta;
147 if (!std::isfinite(total))
148 return failure(eve::DiagnosticCode::InvalidArgument, "AddNumber result must be finite", path);
149 const eve::Value result(total);
150 auto json = result.toJson();
151 if (!json)
152 return failure(eve::DiagnosticCode::SerializationError, "AddNumber result cannot be serialized",
153 path);
154 appended = batch->set(mutation.subject, key, json.value());
155 if (appended) pending[pendingKey] = result;
156 break;
157 }
158 }
159 if (!appended) return failure(eve::DiagnosticCode::Failed, "StatePatch rejected a mutation", path);
160 }
161
162 eve::transaction::TransactionContext transaction(context.transactionId, context.correlationId, context.causationId);
163 StoreTransactionParticipant participant(store_, *batch);
164 std::array<eve::transaction::ITransactionParticipant*, 1> participants{&participant};
166 auto committed = coordinator.execute(transaction, participants);
167 if (!committed) return eve::Result<eve::MutationReceipt>::failure(committed.status());
168
169 const PatchResult& result = batch->result();
171 eve::MutationReceipt{context.transactionId, static_cast<std::size_t>(result.changedCount)},
173}
174
175} // namespace eve::statepatch
double value
int subject
Definition AnimSmr.cpp:163
std::string output
std::vector< QuestEvent > pending
std::string message
DiagnosticCode code
std::uint32_t key
const std::string * tag
std::string path
Definition PlayHost.cpp:110
PrimitiveHandle handle
bool found
double current
uint32_t index
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
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 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
Result< std::string > toJson() const
Serialize this value as deterministic compact JSON.
Definition Value.cpp:67
static Result< Value > fromJson(std::string_view json)
Parse one strict JSON value into an owning Value.
Definition Value.cpp:57
bool asBool() const
Return the boolean payload; the caller must have checked the kind.
Definition Value.cpp:81
bool isBool() const noexcept
Return true when this value is a boolean.
Definition Value.h:89
std::optional< eve::Value > state(std::string_view subject, std::string_view key) const override
Query a persistent/world state value.
std::optional< eve::Value > value(std::string_view subject, std::string_view key) const override
Query a generic world value.
std::optional< bool > hasTag(std::string_view subject, std::string_view tag) const override
Query exact tag membership.
eve::Result< eve::MutationReceipt > apply(std::span< const eve::StateMutation > mutations, const eve::MutationContext &context) override
Apply a complete mutation set atomically.
Transaction participant that atomically applies one StatePatch batch.
Definition StatePatch.h:230
Deterministic subject-and-key JSON value store with atomic patching.
Definition StatePatch.h:119
bool has(const std::string &subject, const std::string &key) const
Returns whether a subject and key currently exist.
std::string get(const std::string &subject, const std::string &key) const
Returns canonical JSON for a value, or an empty string when absent.
eve::Result< void > releaseBatch(PatchBatchHandleRef reference)
Releases a store-owned patch batch.
eve::script::Borrowed< PatchBatch > resolveBatch(PatchBatchHandleRef reference) noexcept
Resolves a live batch as a non-owning observation.
eve::Result< PatchBatchHandleRef > newBatch()
Allocates a store-owned empty patch batch and returns its handle.
Coordinates participant lifecycles with deterministic ordering.
Result< TransactionReceipt > execute(const TransactionContext &context, std::span< ITransactionParticipant * > participants) const
Atomically execute borrowed participants for one context.
Immutable context shared by every participant of one transaction.
Definition Transaction.h:30
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
StatePatch implementation of the common world-state contracts.
Correlation metadata shared by a mutation transaction.
Definition StateAccess.h:57
Observable result of an all-or-nothing mutation request.
Definition StateAccess.h:64
Summary of the most recent batch commit attempt.
Definition StatePatch.h:66