载入中...
搜索中...
未找到
AttributeProjection.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <cmath>
5#include <limits>
6#include <string>
7#include <utility>
8
9namespace eve::attributes {
10namespace {
11
12bool sameOwner(const ecs::EntityHandle& left, const ecs::EntityHandle& right) noexcept {
13 return left.table == right.table && left.type == right.type && left.id == right.id &&
14 left.generation == right.generation;
15}
16
17bool contains(std::span<const std::string_view> names, std::string_view name) noexcept {
18 return std::find(names.begin(), names.end(), name) != names.end();
19}
20
21} // namespace
22
24
26 if (owner.table == nullptr || ecs::try_get(owner) == nullptr)
28 Diagnostic::error(DiagnosticCode::StaleHandle, "attribute projection owner is not a live ECS generation", "owner", {}, "attributes.projection"));
29 if (owner_.table == nullptr) {
30 owner_ = owner;
32 }
33 if (!sameOwner(owner_, owner))
35 Diagnostic::error(DiagnosticCode::Conflict, "attribute projection cannot be rebound to another owner", "owner", {}, "attributes.projection"));
37}
38
39bool AttributeProjection::isStale() const noexcept {
40 return owner_.table != nullptr && ecs::try_get(owner_) == nullptr;
41}
42
43bool AttributeProjection::ownsSameLiveEntity(ecs::EntityHandle owner) const noexcept {
44 return sameOwner(owner_, owner) && owner_.table != nullptr && ecs::try_get(owner_) != nullptr;
45}
46
47Result<void> AttributeProjection::advanceRevision() {
48 const auto next = revision_.incremented();
49 if (!next)
51 Diagnostic::error(DiagnosticCode::InvariantViolation, "attribute projection revision overflowed", "revision", {}, "attributes.projection"));
52 revision_ = *next;
54}
55
56Result<void> AttributeProjection::initialize(std::span<const AttributeSnapshotBase> values) {
58 AttributeSet candidate = values_;
59 for (const auto& entry : values) {
60 if (entry.attribute.empty() || !std::isfinite(entry.base))
62 Diagnostic::error(DiagnosticCode::InvalidArgument, "attribute projection seed requires finite named bases", "bases", {}, "attributes.projection"));
63 candidate.setBase(entry.attribute, entry.base);
64 }
65 values_ = std::move(candidate);
66 initialized_ = true;
67 return advanceRevision();
68}
69
70Result<void> AttributeProjection::setBase(std::string_view attribute, double value) {
71 if (attribute.empty() || !std::isfinite(value))
73 Diagnostic::error(DiagnosticCode::InvalidArgument, "attribute base requires a non-empty finite value", "attribute", {}, "attributes.projection"));
74 auto next = advanceRevision();
75 if (!next) return next;
76 values_.setBase(std::string(attribute), value);
77 initialized_ = true;
78 return next;
79}
80
81Result<void> AttributeProjection::modifyBase(std::string_view attribute, double delta) {
82 if (attribute.empty() || !std::isfinite(delta))
84 Diagnostic::error(DiagnosticCode::InvalidArgument, "attribute delta requires a non-empty finite value", "attribute", {}, "attributes.projection"));
85 auto next = advanceRevision();
86 if (!next) return next;
87 values_.modifyBase(std::string(attribute), delta);
88 initialized_ = true;
89 return next;
90}
91
92Result<double> AttributeProjection::getFinal(std::string_view attribute, double fallback) const {
93 if (attribute.empty() || !std::isfinite(fallback))
95 "attribute query requires a non-empty finite key/fallback",
96 "attribute", {}, "attributes.projection"));
97 return Result<double>::success(values_.getFinal(std::string(attribute), fallback));
98}
99
100bool AttributeProjection::has(std::string_view attribute) const {
101 return !attribute.empty() && values_.has(std::string(attribute));
102}
103
105 auto result = values_.addModifier(std::move(modifier));
106 if (!result) return result;
107 auto next = advanceRevision();
108 if (!next) return Result<ModifierId>::failure(next.status());
109 initialized_ = true;
110 return result;
111}
112
113Result<AttributeProjectionSnapshot> AttributeProjection::snapshot(std::span<const std::string_view> attributes) const {
114 for (const auto name : attributes) {
115 if (name.empty())
117 Diagnostic::error(DiagnosticCode::InvalidArgument, "snapshot attribute name is empty", "attributes", {},
118 "attributes.projection"));
119 }
121 result.owner = owner_;
122 result.capturedRevision = revision_;
123 result.bases.reserve(attributes.size());
124 for (const auto name : attributes)
125 result.bases.push_back({std::string(name), values_.getBase(std::string(name), 0.0)});
126
127 for (int index = 0; index < values_.modifierCount(); ++index) {
128 const auto* modifier = values_.modifierAt(index);
129 if (modifier != nullptr && contains(attributes, modifier->attribute)) result.modifiers.push_back(*modifier);
130 }
131 return Result<AttributeProjectionSnapshot>::success(std::move(result));
132}
133
134Result<void> AttributeProjection::restore(const AttributeProjectionSnapshot& snapshotValue, Revision expectedRevision) {
135 if (!ownsSameLiveEntity(snapshotValue.owner))
137 Diagnostic::error(DiagnosticCode::StaleHandle, "attribute snapshot owner is stale or does not match", "owner", {}, "attributes.projection"));
138 if (revision_ != expectedRevision)
140 Diagnostic::error(DiagnosticCode::Conflict, "attribute snapshot revision does not match current state", "revision", {}, "attributes.projection"));
141
142 std::vector<std::string_view> names;
143 names.reserve(snapshotValue.bases.size());
144 for (const auto& base : snapshotValue.bases) {
145 if (base.attribute.empty() || !std::isfinite(base.base))
147 Diagnostic::error(DiagnosticCode::InvalidArgument, "attribute snapshot contains invalid base data", "bases", {}, "attributes.projection"));
148 names.push_back(base.attribute);
149 }
150
151 AttributeSet candidate = values_;
152 std::vector<ModifierId> toRemove;
153 for (int index = 0; index < candidate.modifierCount(); ++index) {
154 const auto* modifier = candidate.modifierAt(index);
155 if (modifier != nullptr && contains(names, modifier->attribute)) toRemove.push_back(modifier->id);
156 }
157 for (const auto& id : toRemove) {
158 auto removed = candidate.removeModifier(id);
159 if (!removed)
161 Diagnostic::error(DiagnosticCode::InvariantViolation, "attribute snapshot could not remove candidate modifier", "modifiers", {}, "attributes.projection"));
162 }
163 for (const auto& base : snapshotValue.bases) candidate.setBase(base.attribute, base.base);
164
165 auto modifiers = snapshotValue.modifiers;
166 std::stable_sort(modifiers.begin(), modifiers.end(),
167 [](const auto& left, const auto& right) { return left.sequence < right.sequence; });
168 for (const auto& modifier : modifiers) {
169 if (!contains(names, modifier.attribute))
171 Diagnostic::error(DiagnosticCode::InvalidArgument, "attribute snapshot modifier is outside its allow-list", "modifiers", {}, "attributes.projection"));
172 auto added = candidate.addModifier(modifier);
173 if (!added)
175 Diagnostic::error(DiagnosticCode::InvalidArgument, "attribute snapshot contains an invalid modifier", "modifiers", {}, "attributes.projection"));
176 }
177
178 auto next = advanceRevision();
179 if (!next) return next;
180 values_ = std::move(candidate);
181 initialized_ = true;
182 return next;
183}
184
185} // namespace eve::attributes
double value
int subject
Definition AnimSmr.cpp:163
Owner-aware canonical attribute state shared by gameplay adapters.
std::map< std::string, Var > values
HexVec3 left
HexVec3 right
std::string name
int removed
uint32_t index
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
bool has(std::string_view attribute) const
Return whether the canonical set contains a base or modifier for the name.
Result< void > bindOwner(ecs::EntityHandle owner)
Bind the projection to one live generation-qualified ECS owner.
Result< double > getFinal(std::string_view attribute, double fallback=0.0) const
Read a deterministic final value, or the supplied default when absent.
Result< AttributeProjectionSnapshot > snapshot(std::span< const std::string_view > attributes) const
Capture only named attributes and their modifiers.
Result< void > initialize(std::span< const AttributeSnapshotBase > values)
Seed base values once from an existing domain compatibility field.
bool isStale() const noexcept
Return whether the bound owner no longer resolves at its generation.
Result< void > modifyBase(std::string_view attribute, double delta)
Add to a canonical base value and advance the projection revision.
Result< void > setBase(std::string_view attribute, double value)
Set a canonical base value and advance the projection revision.
const ecs::EntityHandle & owner() const noexcept
Return the owner handle used for stale detection.
AttributeProjection(std::string subject={})
Construct an empty projection with an optional stable subject id.
Result< ModifierId > addModifier(AttributeModifier modifier)
Add a canonical modifier using the shared source/priority/sequence rules.
Generic owning collection of canonical attributes and modifiers.
double getFinal(const AttributeId &attribute, double fallback=0.0, const AttributeOperationRegistry *customOperations=nullptr) const
Return the deterministic final value after all modifiers.
eve::Result< ModifierId > addModifier(AttributeModifier modifier)
Add or replace a canonical modifier and return its stable id.
void modifyBase(const AttributeId &attribute, double delta)
Add delta to a base value, creating it from zero when absent.
void setBase(const AttributeId &attribute, double value)
Set or replace a base value. Empty attribute ids are ignored for compatibility.
int modifierCount() const
Number of modifiers.
const AttributeModifier * modifierAt(int index) const
Return a modifier by deterministic sequence order, or null when out of range.
double getBase(const AttributeId &attribute, double fallback=0.0) const
Return a base value, or fallback when absent.
bool has(const AttributeId &attribute) const
Return whether a base value or modifier names the attribute.
eve::Result< void > removeModifier(const ModifierId &id)
Remove one modifier with a structured result.
constexpr std::optional< StrongUint64 > incremented() const noexcept
Returns the next value, or empty instead of unsigned wraparound.
One canonical runtime modifier applied to one named attribute.
Transaction-safe snapshot of selected attributes and their modifiers.
std::vector< AttributeModifier > modifiers
std::vector< AttributeSnapshotBase > bases