载入中...
搜索中...
未找到
GeometryCollectionSnapshot.cpp
浏览该文件的文档.
2
4
5#include <cmath>
6#include <initializer_list>
7#include <limits>
8#include <string>
9#include <utility>
10
11namespace eve::physics {
12namespace {
13
14const eve::Value* field(const eve::Value::Object& object, std::string_view name) {
15 const auto found = object.find(std::string(name));
16 return found == object.end() ? nullptr : &found->second;
17}
18
19bool hasExactFields(const eve::Value::Object& object, std::initializer_list<std::string_view> expected) {
20 if (object.size() != expected.size()) return false;
21 for (const auto name : expected)
22 if (!object.contains(std::string(name))) return false;
23 return true;
24}
25
26eve::Result<std::int64_t> integer64(const eve::Value::Object& object, std::string_view name) {
27 const eve::Value* value = field(object, name);
28 if (!value || !value->isInt64())
30 eve::DiagnosticCode::InvalidArgument, "instance-snapshot field must be an integer", std::string(name)));
32}
33
34eve::Result<float> number(const eve::Value::Object& object, std::string_view name) {
35 const eve::Value* value = field(object, name);
36 if (!value || !value->isNumeric())
38 eve::DiagnosticCode::InvalidArgument, "instance-snapshot field must be numeric", std::string(name)));
39 const double parsed = value->isDouble() ? value->asDouble() : static_cast<double>(value->asInt());
40 if (!std::isfinite(parsed) || parsed < -std::numeric_limits<float>::max() ||
41 parsed > std::numeric_limits<float>::max())
43 eve::DiagnosticCode::InvalidArgument, "instance-snapshot number must be a finite float",
44 std::string(name)));
45 return eve::Result<float>::success(static_cast<float>(parsed));
46}
47
48eve::Result<bool> boolean(const eve::Value::Object& object, std::string_view name) {
49 const eve::Value* value = field(object, name);
50 if (!value || !value->isBool())
52 eve::DiagnosticCode::InvalidArgument, "instance-snapshot field must be boolean", std::string(name)));
53 return eve::Result<bool>::success(value->asBool());
54}
55
57 const auto* object = value.getIf<eve::Value::Object>();
58 if (!object)
60 eve::DiagnosticCode::InvalidArgument, "instance-snapshot bone must be an object", "bones"));
61 if (!hasExactFields(*object, {"state", "anchored", "clusterId", "fractureLevel", "halfExtentX", "halfExtentY",
62 "halfExtentZ"}))
64 eve::DiagnosticCode::InvalidArgument, "instance-snapshot bone has unknown or missing fields", "bones"));
65 auto state = integer64(*object, "state");
66 if (!state)
68 if (state.value() < 0 || state.value() > 2)
70 eve::DiagnosticCode::InvalidArgument, "instance-snapshot bone state must be 0..2", "state"));
71 auto anchored = boolean(*object, "anchored");
72 if (!anchored)
74 auto clusterId = integer64(*object, "clusterId");
75 if (!clusterId)
77 auto fractureLevel = integer64(*object, "fractureLevel");
78 if (!fractureLevel)
80 auto hx = number(*object, "halfExtentX");
82 auto hy = number(*object, "halfExtentY");
84 auto hz = number(*object, "halfExtentZ");
86 GeometryCollectionInstanceSnapshot::BoneSnapshot bone;
87 bone.state = static_cast<std::uint8_t>(state.value());
88 bone.anchored = anchored.value();
89 bone.clusterId = static_cast<int>(clusterId.value());
90 bone.fractureLevel = static_cast<int>(fractureLevel.value());
91 bone.halfExtentX = hx.value();
92 bone.halfExtentY = hy.value();
93 bone.halfExtentZ = hz.value();
95}
96
98 const auto* object = value.getIf<eve::Value::Object>();
99 if (!object)
101 eve::DiagnosticCode::InvalidArgument, "instance-snapshot edge must be an object", "edges"));
102 if (!hasExactFields(*object, {"boneA", "boneB", "strainThreshold", "accumulatedStrain", "broken"}))
104 eve::DiagnosticCode::InvalidArgument, "instance-snapshot edge has unknown or missing fields", "edges"));
105 auto a = integer64(*object, "boneA");
107 auto b = integer64(*object, "boneB");
109 auto threshold = number(*object, "strainThreshold");
110 if (!threshold) return eve::Result<GeometryCollectionInstanceSnapshot::EdgeSnapshot>::failure(threshold.status());
111 auto strain = number(*object, "accumulatedStrain");
113 auto broken = boolean(*object, "broken");
115 GeometryCollectionInstanceSnapshot::EdgeSnapshot edge;
116 edge.boneA = static_cast<int>(a.value());
117 edge.boneB = static_cast<int>(b.value());
118 edge.strainThreshold = threshold.value();
119 edge.accumulatedStrain = strain.value();
120 edge.broken = broken.value();
122}
123
124} // namespace
125
127 if (bones.empty())
129 eve::DiagnosticCode::InvalidArgument, "instance-snapshot requires at least one bone", "bones"));
130 if (bones.size() > 4096 || edges.size() > 16384)
132 eve::DiagnosticCode::InvalidArgument, "instance-snapshot exceeds size limits", "snapshot"));
133 if (!std::isfinite(originX) || !std::isfinite(originY) || !std::isfinite(originZ))
135 eve::DiagnosticCode::InvalidArgument, "instance-snapshot origin must be finite", "origin"));
136 if (lastTick < 0)
138 eve::DiagnosticCode::InvalidArgument, "instance-snapshot lastTick must be non-negative", "lastTick"));
139 for (const auto& bone : bones) {
140 if (bone.state > 2)
142 eve::DiagnosticCode::InvalidArgument, "instance-snapshot bone state must be 0..2", "bones"));
143 if (bone.clusterId < 0 || bone.fractureLevel < 0)
145 eve::DiagnosticCode::InvalidArgument, "instance-snapshot cluster fields must be non-negative",
146 "bones"));
147 if (!(bone.halfExtentX > 0.f) || !(bone.halfExtentY > 0.f) || !(bone.halfExtentZ > 0.f) ||
148 !std::isfinite(bone.halfExtentX) || !std::isfinite(bone.halfExtentY) || !std::isfinite(bone.halfExtentZ))
150 eve::DiagnosticCode::InvalidArgument, "instance-snapshot half-extents must be finite and positive",
151 "bones"));
152 }
153 for (const auto& edge : edges) {
154 if (edge.boneA < 0 || edge.boneB < 0 || static_cast<std::size_t>(edge.boneA) >= bones.size() ||
155 static_cast<std::size_t>(edge.boneB) >= bones.size() || edge.boneA == edge.boneB)
157 eve::DiagnosticCode::InvalidArgument, "instance-snapshot edge indices invalid", "edges"));
158 if (!std::isfinite(edge.strainThreshold) || edge.strainThreshold <= 0.f ||
159 !std::isfinite(edge.accumulatedStrain) || edge.accumulatedStrain < 0.f)
161 eve::DiagnosticCode::InvalidArgument, "instance-snapshot edge strain values invalid", "edges"));
162 }
164}
165
167 auto valid = validate();
168 if (!valid) return eve::Result<eve::Value>::failure(valid.status());
169 eve::Value::Array boneValues;
170 boneValues.reserve(bones.size());
171 for (const auto& bone : bones) {
173 object["state"] = static_cast<std::int64_t>(bone.state);
174 object["anchored"] = bone.anchored;
175 object["clusterId"] = static_cast<std::int64_t>(bone.clusterId);
176 object["fractureLevel"] = static_cast<std::int64_t>(bone.fractureLevel);
177 object["halfExtentX"] = static_cast<double>(bone.halfExtentX);
178 object["halfExtentY"] = static_cast<double>(bone.halfExtentY);
179 object["halfExtentZ"] = static_cast<double>(bone.halfExtentZ);
180 boneValues.push_back(eve::Value(std::move(object)));
181 }
182 eve::Value::Array edgeValues;
183 edgeValues.reserve(edges.size());
184 for (const auto& edge : edges) {
186 object["boneA"] = static_cast<std::int64_t>(edge.boneA);
187 object["boneB"] = static_cast<std::int64_t>(edge.boneB);
188 object["strainThreshold"] = static_cast<double>(edge.strainThreshold);
189 object["accumulatedStrain"] = static_cast<double>(edge.accumulatedStrain);
190 object["broken"] = edge.broken;
191 edgeValues.push_back(eve::Value(std::move(object)));
192 }
194 object["schema"] = std::string(SchemaId);
195 object["schemaVersion"] = static_cast<std::int64_t>(SchemaVersion);
196 object["originX"] = static_cast<double>(originX);
197 object["originY"] = static_cast<double>(originY);
198 object["originZ"] = static_cast<double>(originZ);
199 object["lastTick"] = lastTick;
200 object["sleepBatchRevision"] = static_cast<std::int64_t>(sleepBatchRevision);
201 object["bones"] = eve::Value(std::move(boneValues));
202 object["edges"] = eve::Value(std::move(edgeValues));
203 return eve::Result<eve::Value>::success(eve::Value(std::move(object)));
204}
205
209 schema.id = std::string(SchemaId);
210 schema.version = static_cast<int>(SchemaVersion);
211 schema.title = "Geometry Collection Instance Snapshot";
212 schema.description = "Runtime bone/edge state for a geometry-collection instance.";
213 schema.additionalProperties = false;
214 auto makeField = [](std::string name, ValueType type, bool required) {
216 field.name = std::move(name);
217 field.type = type;
218 field.required = required;
219 return field;
220 };
221 schema.fields = {
222 makeField("schema", ValueType::String, true),
223 makeField("schemaVersion", ValueType::Integer, true),
224 makeField("originX", ValueType::Number, true),
225 makeField("originY", ValueType::Number, true),
226 makeField("originZ", ValueType::Number, true),
227 makeField("lastTick", ValueType::Integer, true),
228 makeField("sleepBatchRevision", ValueType::Integer, true),
229 makeField("bones", ValueType::Array, true),
230 makeField("edges", ValueType::Array, true),
231 };
232 return schema;
233}
234
242
244 const auto* object = value.getIf<eve::Value::Object>();
245 if (!object)
247 eve::DiagnosticCode::InvalidArgument, "instance-snapshot must be an object", "snapshot"));
248 if (!hasExactFields(*object, {"schema", "schemaVersion", "originX", "originY", "originZ", "lastTick",
249 "sleepBatchRevision", "bones", "edges"}))
251 eve::DiagnosticCode::InvalidArgument, "instance-snapshot has unknown or missing fields", "snapshot"));
252 const eve::Value* schemaField = field(*object, "schema");
253 if (!schemaField || !schemaField->isString() || schemaField->asString() != SchemaId)
255 eve::DiagnosticCode::InvalidArgument, "instance-snapshot schema id mismatch", "schema"));
256 auto version = integer64(*object, "schemaVersion");
257 if (!version) return eve::Result<GeometryCollectionInstanceSnapshot>::failure(version.status());
258 if (version.value() != static_cast<std::int64_t>(SchemaVersion))
260 eve::DiagnosticCode::Unsupported, "instance-snapshot schema version is unsupported", "schemaVersion"));
262 auto ox = number(*object, "originX");
264 auto oy = number(*object, "originY");
266 auto oz = number(*object, "originZ");
268 auto lastTick = integer64(*object, "lastTick");
270 auto sleepRev = integer64(*object, "sleepBatchRevision");
271 if (!sleepRev) return eve::Result<GeometryCollectionInstanceSnapshot>::failure(sleepRev.status());
272 if (sleepRev.value() < 0)
274 eve::DiagnosticCode::InvalidArgument, "sleepBatchRevision must be non-negative", "sleepBatchRevision"));
275 snapshot.originX = ox.value();
276 snapshot.originY = oy.value();
277 snapshot.originZ = oz.value();
278 snapshot.lastTick = lastTick.value();
279 snapshot.sleepBatchRevision = static_cast<std::uint64_t>(sleepRev.value());
280 const eve::Value* bonesValue = field(*object, "bones");
281 const auto* boneArray = bonesValue ? bonesValue->getIf<eve::Value::Array>() : nullptr;
282 if (!boneArray)
284 eve::DiagnosticCode::InvalidArgument, "instance-snapshot bones must be an array", "bones"));
285 const eve::Value* edgesValue = field(*object, "edges");
286 const auto* edgeArray = edgesValue ? edgesValue->getIf<eve::Value::Array>() : nullptr;
287 if (!edgeArray)
289 eve::DiagnosticCode::InvalidArgument, "instance-snapshot edges must be an array", "edges"));
290 snapshot.bones.reserve(boneArray->size());
291 for (const auto& boneValue : *boneArray) {
292 auto bone = decodeBone(boneValue);
294 snapshot.bones.push_back(bone.value());
295 }
296 snapshot.edges.reserve(edgeArray->size());
297 for (const auto& edgeValue : *edgeArray) {
298 auto edge = decodeEdge(edgeValue);
300 snapshot.edges.push_back(edge.value());
301 }
302 auto valid = snapshot.validate();
304 auto registered = ensureSchemaRegistered();
305 if (!registered) return eve::Result<GeometryCollectionInstanceSnapshot>::failure(registered.status());
307}
308
309} // namespace eve::physics
double value
std::uint32_t bone
bool required
std::string name
bool valid
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
const RoadEdge * edge
double number
bool found
bool boolean
std::string string
Json object
float size
Definition TreeMesh.cpp:156
double oy
double ox
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
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
const std::string & asString() const
Return the string payload; the caller must have checked the kind.
Definition Value.cpp:87
std::map< std::string, Value > Object
Definition Value.h:34
bool isString() const noexcept
Return true when this value is a string.
Definition Value.h:95
std::vector< Value > Array
Definition Value.h:33
const T * getIf() const noexcept
Return a typed pointer, or nullptr when the kind differs.
Definition Value.h:180
Type type() const
Return the active value type.
static eve::Result< SchemaRegistrationStatus > registerVersioned(const SchemaDefinition &definition)
Registers a new exact (id, version) entry without replacing it.
static const SchemaDefinition * resolve(const std::string &schemaId, int schemaVersion)
Resolves one exact (schemaId, schemaVersion) pair, or nullptr.
Optional physics backend for vehicle mobility and body attach.
Definition Climbing.h:36
bool decodeEdge(const EditorValue &value, std::uint32_t &id, std::vector< procgen::road::RoadControlPoint > &points)
const EditorValue * field(const EditorValue &value, const char *name)
ValueType
JSON-compatible value kinds understood by a schema field.
Definition SchemaTypes.h:16
Persistent runtime snapshot for one geometry-collection instance.
static eve::schema::SchemaDefinition schemaDefinition()
Build the tooling schema that mirrors validate().
eve::Result< eve::Value > toValue() const
Encode the complete canonical version-1 value.
static eve::Result< void > ensureSchemaRegistered()
Idempotently register version 1 in the process schema registry.
eve::Result< void > validate() const
Validate counts and numeric ranges.
static eve::Result< GeometryCollectionInstanceSnapshot > fromValue(const eve::Value &value)
Decode and validate version 1 transactionally.
Metadata and validation constraints for one object member.
Definition SchemaTypes.h:77
Versioned runtime schema for a JSON object.
Definition SchemaTypes.h:93
std::vector< FieldDefinition > fields
Definition SchemaTypes.h:99