载入中...
搜索中...
未找到
Definitions.cpp
浏览该文件的文档.
2
5
6#include <simplesquirrel/simplesquirrel.hpp>
7
8#include <charconv>
9#include <cstdint>
10#include <exception>
11#include <limits>
12#include <utility>
13
15namespace {
16
17eve::schema::FieldDefinition schemaField(std::string name, eve::schema::ValueType type, bool required = true) {
19 result.name = std::move(name);
20 result.type = type;
21 result.required = required;
22 return result;
23}
24
25eve::schema::SchemaNode arrayOfObject(std::vector<eve::schema::FieldDefinition> fields) {
27 object.additionalProperties = false;
28 object.fields = std::move(fields);
31 item.objectSchema = std::make_shared<const eve::schema::SchemaDefinition>(std::move(object));
34 array.itemSchema = std::make_shared<const eve::schema::SchemaNode>(std::move(item));
35 return array;
36}
37
38eve::schema::SchemaDefinition definitionsSnapshotSchema() {
40 schema.id = "definitions:registry";
41 schema.version = 1;
42 schema.title = "Definition Registry Snapshot";
43 schema.description = "Persistent state owned by DefinitionRegistry.";
44 schema.additionalProperties = false;
45 auto definitions = schemaField("definitions", eve::schema::ValueType::Array);
46 static_cast<eve::schema::SchemaNode&>(definitions) = arrayOfObject({
47 schemaField("type", eve::schema::ValueType::String),
48 schemaField("id", eve::schema::ValueType::String),
49 schemaField("version", eve::schema::ValueType::Integer),
50 schemaField("generation", eve::schema::ValueType::String),
51 schemaField("json", eve::schema::ValueType::Any),
52 });
53 definitions.name = "definitions";
54 definitions.required = true;
55 auto generations = schemaField("generations", eve::schema::ValueType::Array);
56 static_cast<eve::schema::SchemaNode&>(generations) = arrayOfObject({
57 schemaField("type", eve::schema::ValueType::String),
58 schemaField("id", eve::schema::ValueType::String),
59 schemaField("generation", eve::schema::ValueType::String),
60 });
61 generations.name = "generations";
62 generations.required = true;
63 auto events = schemaField("events", eve::schema::ValueType::Array);
64 static_cast<eve::schema::SchemaNode&>(events) = arrayOfObject({
65 schemaField("sequence", eve::schema::ValueType::String),
66 schemaField("name", eve::schema::ValueType::String),
67 schemaField("type", eve::schema::ValueType::String),
68 schemaField("id", eve::schema::ValueType::String),
69 schemaField("version", eve::schema::ValueType::Integer),
70 schemaField("generation", eve::schema::ValueType::String),
71 });
72 events.name = "events";
73 events.required = true;
74 schema.fields = {
75 schemaField("version", eve::schema::ValueType::Integer),
76 std::move(definitions),
77 std::move(generations),
78 std::move(events),
79 schemaField("nextEventSequence", eve::schema::ValueType::String),
80 };
81 return schema;
82}
83
84eve::Result<void> validateDefinitionsSnapshot(const std::string& json) {
85 constexpr auto id = "definitions:registry";
87 auto registration = eve::schema::SchemaRegistry::registerVersioned(definitionsSnapshotSchema());
88 if (!registration.ok()) return eve::Result<void>::failure(registration.status());
89 }
90 const auto errors = eve::schema::SchemaRegistry::validate(id, 1, json);
91 if (errors.empty()) return eve::Result<void>::success();
92 const auto& error = errors.front();
95 eve::DiagnosticDetails{{"schemaId", id}, {"schemaVersion", "1"}, {"validationCode", error.code}},
96 "definitions.snapshot.schema"));
97}
98
99eve::Value definitionHandleProjection(const DefinitionHandle& handle) {
100 const auto& logical = handle.reference.id();
102 {"type", eve::Value(std::string(logical.namespaceName()))},
103 {"id", eve::Value(std::string(logical.name()))},
104 {"reference", eve::Value(handle.reference.format())},
105 {"generation", eve::Value(static_cast<std::int64_t>(handle.generation.value()))},
106 });
107}
108
109eve::Value definitionProjection(const Definition& definition) {
111 {"type", eve::Value(definition.type)},
112 {"id", eve::Value(definition.id)},
113 {"version", eve::Value(static_cast<std::int64_t>(definition.version.value()))},
114 {"generation", eve::Value(static_cast<std::int64_t>(definition.generation.value()))},
115 {"json", eve::Value(definition.json)},
116 });
117}
118
119const eve::Value* field(const eve::Value::Object& object, std::string_view name) {
120 const auto it = object.find(std::string(name));
121 return it == object.end() ? nullptr : &it->second;
122}
123
124bool readString(const eve::Value::Object& object, std::string_view name, std::string& output) {
125 const auto* value = field(object, name);
126 const auto* text = value ? value->getIf<std::string>() : nullptr;
127 if (!text) return false;
128 output = *text;
129 return true;
130}
131
132bool readPositiveSchemaVersion(const eve::Value& value, eve::SchemaVersion& output) {
133 const auto* number = value.getIf<std::int64_t>();
134 if (!number || *number <= 0) return false;
135 output = eve::SchemaVersion(static_cast<std::uint64_t>(*number));
136 return true;
137}
138
139bool readUint64String(const eve::Value& value, std::uint64_t& output) {
140 const auto* text = value.getIf<std::string>();
141 if (!text || text->empty()) return false;
142 const auto [end, error] = std::from_chars(text->data(), text->data() + text->size(), output);
143 return error == std::errc{} && end == text->data() + text->size();
144}
145
146eve::Value parseStoredJson(std::string_view text) {
147 auto parsed = eve::Value::fromJson(text);
148 if (!parsed.ok()) {
149 // A Definition can enter the registry only after this exact parse
150 // succeeds. Reaching this point means the registry's invariant was
151 // violated, not that a caller supplied recoverable input.
152 std::terminate();
153 }
154 return std::move(parsed).takeValue();
155}
156
157eve::LogicalId definitionsSchema() {
158 const auto schema = eve::LogicalId::parse("definitions:registry");
159 if (!schema) std::terminate();
160 return *schema;
161}
162
163DefinitionRef makeDefinitionReference(std::string_view type, std::string_view id) {
164 const auto logical = eve::LogicalId::fromParts(type, id);
165 if (!logical) return {};
166 auto reference = eve::DefinitionRef::fromId(*logical);
167 if (!reference.ok()) return {};
168 return std::move(reference).takeValue();
169}
170
171const eve::SnapshotMigrationChain& definitionsMigrations() {
172 static const eve::SnapshotMigrationChain chain = [] {
174 const auto registration =
175 result.add(definitionsSchema(), eve::SchemaVersion(0), eve::SchemaVersion(1),
177 const auto* object = payload.getIf<eve::Value::Object>();
178 if (!object)
180 eve::DiagnosticCode::ParseError, "definitions snapshot payload must be an object"));
181 eve::Value::Object migrated = *object;
182 migrated["version"] = eve::Value(std::int64_t(1));
183 return eve::Result<eve::Value>::success(eve::Value(std::move(migrated)));
184 });
185 if (!registration.ok()) std::terminate();
186 return result;
187 }();
188 return chain;
189}
190
191eve::Result<Definition> makeDefinition(const std::string& type, const std::string& id, int version,
192 const std::string& json) {
193 if (type.empty() || id.empty())
195 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "definition type and id must not be empty"));
198 eve::DiagnosticCode::InvalidArgument, "definition type and id must form a valid namespace:name reference"));
199 if (version <= 0)
201 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "definition schema version must be positive"));
202
203 auto parsed = eve::Value::fromJson(json);
204 if (!parsed.ok()) return eve::Result<Definition>::failure(parsed.status());
205 auto value = std::move(parsed).takeValue();
206 auto canonical = value.toJson();
207 if (!canonical.ok()) return eve::Result<Definition>::failure(canonical.status());
208
210 const auto errors = eve::schema::SchemaRegistry::validate(type, version, json);
211 if (!errors.empty()) {
212 const auto& error = errors.front();
216 {"schemaId", type}, {"schemaVersion", std::to_string(version)}, {"validationCode", error.code}},
217 "definitions.schema"));
218 }
219 }
220
221 Definition definition;
222 definition.type = type;
223 definition.id = id;
224 definition.version = eve::SchemaVersion(static_cast<std::uint64_t>(version));
225 definition.json = std::move(canonical).takeValue();
226 return eve::Result<Definition>::success(std::move(definition));
227}
228
229} // namespace
230
231DefinitionRegistry::DefinitionRegistry(eve::PersistentId instanceId)
232 : storage_([](Definition& value, eve::Generation generation) { value.generation = generation; }),
233 instanceId_(instanceId) {}
234
235DefinitionEvent DefinitionRegistry::projectEvent(const Storage::Event& event) {
236 DefinitionEvent result;
237 result.sequence = event.sequence;
238 result.name = event.operation == eve::RegistryOperation::Remove ? "definition_removed" : "definition_reloaded";
239 result.type = event.key.first;
240 result.id = event.key.second;
241 result.version = event.data.version;
242 result.generation = event.generation;
243 return result;
244}
245
246DefinitionHandle DefinitionRegistry::projectHandle(const Storage::Handle& handle) {
247 return DefinitionHandle{makeDefinitionReference(handle.key.first, handle.key.second), handle.generation};
248}
249
250DefinitionRegistry::Storage::Handle DefinitionRegistry::storageHandle(const DefinitionHandle& handle) {
251 const auto& logical = handle.reference.id();
252 return Storage::Handle{Key{std::string(logical.namespaceName()), std::string(logical.name())}, handle.generation};
253}
254
255void DefinitionRegistry::clearEventProjection() const { eventProjection_.clear(); }
256
257eve::Result<DefinitionHandle> DefinitionRegistry::insert(const std::string& type, const std::string& id, int version,
258 const std::string& json) {
259 auto definition = makeDefinition(type, id, version, json);
260 if (!definition.ok()) {
261 return eve::Result<DefinitionHandle>::failure(definition.status());
262 }
263 Definition value = std::move(definition).takeValue();
264 const EventData data{value.version};
265 auto result = storage_.insert(Key{type, id}, std::move(value), data);
266 if (!result.ok()) {
267 return eve::Result<DefinitionHandle>::failure(result.status());
268 }
269 const eve::Status mutationStatus = result.status();
270 const auto handle = std::move(result).takeValue();
271 clearEventProjection();
272 if (const auto next = revision_.incremented()) revision_ = *next;
273 return eve::Result<DefinitionHandle>::success(projectHandle(handle), mutationStatus);
274}
275
276eve::Result<DefinitionHandle> DefinitionRegistry::replace(const std::string& type, const std::string& id, int version,
277 const std::string& json) {
278 auto definition = makeDefinition(type, id, version, json);
279 if (!definition.ok()) {
280 return eve::Result<DefinitionHandle>::failure(definition.status());
281 }
282 Definition value = std::move(definition).takeValue();
283 const EventData data{value.version};
284 auto result = storage_.replace(Key{type, id}, std::move(value), data);
285 if (!result.ok()) {
286 return eve::Result<DefinitionHandle>::failure(result.status());
287 }
288 const eve::Status mutationStatus = result.status();
289 const auto handle = std::move(result).takeValue();
290 clearEventProjection();
291 if (const auto next = revision_.incremented()) revision_ = *next;
292 return eve::Result<DefinitionHandle>::success(projectHandle(handle), mutationStatus);
293}
294
295eve::Result<DefinitionHandle> DefinitionRegistry::remove(const std::string& type, const std::string& id) {
296 const Key key{type, id};
297 auto current = storage_.resolve(key);
298 if (!current.ok()) {
300 }
301 const EventData data{current.value().get().version};
302 auto result = storage_.remove(key, data);
303 if (!result.ok()) {
304 return eve::Result<DefinitionHandle>::failure(result.status());
305 }
306 const eve::Status mutationStatus = result.status();
307 const auto handle = std::move(result).takeValue();
308 clearEventProjection();
309 if (const auto next = revision_.incremented()) revision_ = *next;
310 return eve::Result<DefinitionHandle>::success(projectHandle(handle), mutationStatus);
311}
312
313eve::ResultRef<const Definition> DefinitionRegistry::resolve(const std::string& type, const std::string& id) const {
314 auto result = storage_.resolve(Key{type, id});
315 if (!result.ok()) return eve::ResultRef<const Definition>::failure(result.status());
316 return eve::ResultRef<const Definition>::success(std::cref(result.value().get()));
317}
318
320 auto result = storage_.resolve(storageHandle(handle));
321 if (!result.ok()) return eve::ResultRef<const Definition>::failure(result.status());
322 return eve::ResultRef<const Definition>::success(std::cref(result.value().get()));
323}
324
325eve::Result<DefinitionHandle> DefinitionRegistry::handle(const std::string& type, const std::string& id) const {
326 auto result = storage_.handle(Key{type, id});
327 if (!result.ok()) return eve::Result<DefinitionHandle>::failure(result.status());
328 return eve::Result<DefinitionHandle>::success(projectHandle(result.value()));
329}
330
331eve::Result<eve::Generation> DefinitionRegistry::generationOf(const std::string& type, const std::string& id) const {
332 return storage_.generationOf(Key{type, id});
333}
334
335bool DefinitionRegistry::isTombstone(const std::string& type, const std::string& id) const noexcept {
336 return storage_.isTombstone(Key{type, id});
337}
338
340 return storage_.isStale(storageHandle(handle));
341}
342
343DefinitionRef DefinitionRegistry::reference(const std::string& type, const std::string& id) const {
344 return makeDefinitionReference(type, id);
345}
346
347int DefinitionRegistry::size() const { return static_cast<int>(storage_.size()); }
348
349int DefinitionRegistry::countType(const std::string& type) const {
350 int result = 0;
351 for (std::size_t index = 0;; ++index) {
352 const auto* key = storage_.keyAt(index);
353 if (!key) break;
354 if (key->first == type) ++result;
355 }
356 return result;
357}
358
360 if (index < 0) return nullptr;
361 return storage_.at(static_cast<std::size_t>(index));
362}
363
364const Definition* DefinitionRegistry::atType(const std::string& type, int index) const {
365 if (index < 0) return nullptr;
366 int matching = 0;
367 for (std::size_t position = 0;; ++position) {
368 const auto* key = storage_.keyAt(position);
369 if (!key) break;
370 if (key->first != type) continue;
371 if (matching++ == index) return storage_.at(position);
372 }
373 return nullptr;
374}
375
376int DefinitionRegistry::eventCount() const { return static_cast<int>(storage_.eventCount()); }
377
379 if (index < 0 || static_cast<std::size_t>(index) >= storage_.eventCount()) return nullptr;
380 if (eventProjection_.size() != storage_.eventCount()) {
381 eventProjection_.clear();
382 eventProjection_.reserve(storage_.eventCount());
383 for (std::size_t i = 0; i < storage_.eventCount(); ++i)
384 eventProjection_.push_back(projectEvent(*storage_.eventAt(i)));
385 }
386 return &eventProjection_[static_cast<std::size_t>(index)];
387}
388
390 storage_.clearEvents();
391 clearEventProjection();
392 if (const auto next = revision_.incremented()) revision_ = *next;
393}
394
396 const auto state = storage_.snapshotState();
400 definitions.reserve(storage_.size());
401 events.reserve(storage_.eventCount());
402 generations.reserve(state.entries.size());
403
404 for (const auto& [key, entry] : state.entries) {
406 generation.emplace("generation", eve::Value(std::to_string(entry.generation.value())));
407 generation.emplace("id", eve::Value(key.second));
408 generation.emplace("type", eve::Value(key.first));
409 generations.emplace_back(std::move(generation));
410
411 if (!entry.value.has_value()) continue;
412 const auto& definition = *entry.value;
414 item.emplace("generation", eve::Value(std::to_string(entry.generation.value())));
415 item.emplace("id", eve::Value(definition.id));
416 item.emplace("json", parseStoredJson(definition.json));
417 item.emplace("type", eve::Value(definition.type));
418 item.emplace("version", eve::Value(static_cast<std::int64_t>(definition.version.value())));
419 definitions.emplace_back(std::move(item));
420 }
421
422 for (const auto& raw : state.events) {
423 const auto event = projectEvent(raw);
425 item.emplace("generation", eve::Value(std::to_string(event.generation.value())));
426 item.emplace("id", eve::Value(event.id));
427 item.emplace("name", eve::Value(event.name));
428 item.emplace("sequence", eve::Value(std::to_string(event.sequence.value())));
429 item.emplace("type", eve::Value(event.type));
430 item.emplace("version", eve::Value(static_cast<std::int64_t>(event.version.value())));
431 events.emplace_back(std::move(item));
432 }
433
435 root.emplace("definitions", eve::Value(std::move(definitions)));
436 root.emplace("events", eve::Value(std::move(events)));
437 root.emplace("generations", eve::Value(std::move(generations)));
438 root.emplace("nextEventSequence", eve::Value(std::to_string(state.nextEventSequence.value())));
439 root.emplace("version", eve::Value(std::int64_t(1)));
440 auto encoded = eve::Value(std::move(root)).toJson();
441 if (!encoded.ok()) std::terminate();
442 return std::move(encoded).takeValue();
443}
444
446 auto schemaValidation = validateDefinitionsSnapshot(json);
447 if (!schemaValidation.ok()) return schemaValidation;
448 auto parsed = eve::Value::fromJson(json);
449 if (!parsed.ok()) {
450 return eve::Result<void>::failure(parsed.status());
451 }
452 const eve::Value value = std::move(parsed).takeValue();
453 const auto* root = value.getIf<eve::Value::Object>();
454 if (!root) {
456 eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "definitions snapshot must be an object"));
457 }
458 const auto* snapshotVersion = field(*root, "version");
459 const auto* versionNumber = snapshotVersion ? snapshotVersion->getIf<std::int64_t>() : nullptr;
460 const auto* definitions = field(*root, "definitions");
461 const auto* generations = field(*root, "generations");
462 const auto* events = field(*root, "events");
463 const auto* nextSequenceValue = field(*root, "nextEventSequence");
464 const auto* definitionArray = definitions ? definitions->getIf<eve::Value::Array>() : nullptr;
465 const auto* generationArray = generations ? generations->getIf<eve::Value::Array>() : nullptr;
466 const auto* eventArray = events ? events->getIf<eve::Value::Array>() : nullptr;
467 std::uint64_t nextSequence = 0;
468 if (!versionNumber || *versionNumber != 1 || !definitionArray || !generationArray || !eventArray ||
469 !nextSequenceValue || !readUint64String(*nextSequenceValue, nextSequence) || nextSequence == 0) {
471 eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "invalid definitions snapshot fields"));
472 }
473
474 Storage::State candidate;
475 for (std::size_t index = 0; index < generationArray->size(); ++index) {
476 const auto* item = (*generationArray)[index].getIf<eve::Value::Object>();
477 std::string type;
478 std::string id;
479 std::uint64_t generation = 0;
480 const auto* generationValue = item ? field(*item, "generation") : nullptr;
481 if (!item || !readString(*item, "type", type) || !readString(*item, "id", id) || type.empty() || id.empty() ||
482 !generationValue || !readUint64String(*generationValue, generation) || generation == 0 ||
483 !candidate.entries.emplace(Key{type, id}, Storage::Entry{eve::Generation(generation), std::nullopt})
484 .second) {
486 eve::DiagnosticCode::ParseError, "invalid definition generation at index " + std::to_string(index)));
487 }
488 }
489
490 for (std::size_t index = 0; index < definitionArray->size(); ++index) {
491 const auto* item = (*definitionArray)[index].getIf<eve::Value::Object>();
492 std::string type;
493 std::string id;
494 eve::SchemaVersion versionValue;
495 std::uint64_t generation = 0;
496 const auto* jsonValue = item ? field(*item, "json") : nullptr;
497 const auto* versionField = item ? field(*item, "version") : nullptr;
498 const auto* generationField = item ? field(*item, "generation") : nullptr;
499 if (!item || !readString(*item, "type", type) || !readString(*item, "id", id) || type.empty() || id.empty() ||
500 !versionField || !readPositiveSchemaVersion(*versionField, versionValue) || !generationField ||
501 !readUint64String(*generationField, generation) || generation == 0 || !jsonValue) {
503 eve::DiagnosticCode::ParseError, "invalid definition at index " + std::to_string(index)));
504 }
505 const Key key{type, id};
506 auto entry = candidate.entries.find(key);
507 if (entry == candidate.entries.end() || entry->second.generation != eve::Generation(generation) ||
508 entry->second.value.has_value()) {
511 "inconsistent definition generation at index " + std::to_string(index)));
512 }
513 auto canonical = jsonValue->toJson();
514 if (!canonical.ok()) {
515 return eve::Result<void>::failure(canonical.status());
516 }
517 Definition definition;
518 definition.type = type;
519 definition.id = id;
520 definition.version = versionValue;
521 definition.generation = eve::Generation(generation);
522 definition.json = std::move(canonical).takeValue();
523 entry->second.value = std::move(definition);
524 }
525
526 eve::EventSequence previousSequence;
527 for (std::size_t index = 0; index < eventArray->size(); ++index) {
528 const auto* item = (*eventArray)[index].getIf<eve::Value::Object>();
529 std::string name;
530 std::string type;
531 std::string id;
532 eve::SchemaVersion versionValue;
533 std::uint64_t sequence = 0;
534 std::uint64_t generation = 0;
535 const auto* versionField = item ? field(*item, "version") : nullptr;
536 const auto* sequenceField = item ? field(*item, "sequence") : nullptr;
537 const auto* generationField = item ? field(*item, "generation") : nullptr;
538 if (!item || !readString(*item, "name", name) || !readString(*item, "type", type) ||
539 !readString(*item, "id", id) || (name != "definition_reloaded" && name != "definition_removed") ||
540 type.empty() || id.empty() || !versionField || !readPositiveSchemaVersion(*versionField, versionValue) ||
541 !sequenceField || !readUint64String(*sequenceField, sequence) || sequence == 0 ||
542 (!previousSequence.isZero() && eve::EventSequence(sequence) <= previousSequence) || !generationField ||
543 !readUint64String(*generationField, generation) || generation == 0) {
545 eve::DiagnosticCode::ParseError, "invalid definition event at index " + std::to_string(index)));
546 }
547 const bool removed = name == "definition_removed";
548 Storage::Event event;
549 event.sequence = eve::EventSequence(sequence);
550 event.operation = removed
553 event.key = Key{type, id};
554 event.generation = eve::Generation(generation);
555 event.tombstone = removed;
556 event.data.version = versionValue;
557 candidate.events.push_back(std::move(event));
558 previousSequence = eve::EventSequence(sequence);
559 }
560 if (!previousSequence.isZero() && eve::EventSequence(nextSequence) <= previousSequence) {
562 "next event sequence must exceed retained events"));
563 }
564 candidate.nextEventSequence = eve::EventSequence(nextSequence);
565
566 auto restored = storage_.restoreState(std::move(candidate));
567 if (!restored.ok()) {
568 return eve::Result<void>::failure(restored.status());
569 }
570 clearEventProjection();
571 revision_ = eve::Revision(nextSequence - 1);
572 tick_ = eve::SimulationTick{};
574}
575
579 return eve::makeSnapshotEnvelope("definitions.registry", definitionsSchema(), eve::SchemaVersion(1), instanceId_,
580 revision_, tick_, std::move(payload).takeValue(), hashProvider);
581}
582
584 const eve::SnapshotHashProvider& hashProvider) {
585 if (source.type != "definitions.registry" || source.schema != definitionsSchema())
587 eve::DiagnosticCode::InvalidArgument, "snapshot does not belong to definitions::DefinitionRegistry"));
588 if (!instanceId_.isNil() && source.instanceId != instanceId_)
590 eve::DiagnosticCode::Conflict, "snapshot instanceId does not match definitions::DefinitionRegistry"));
591
592 auto migrated = definitionsMigrations().migrate(source, eve::SchemaVersion(1), hashProvider);
593 if (!migrated.ok()) return eve::Result<void>::failure(migrated.status());
594 const auto& candidate = migrated.value();
595 auto metadata = eve::validateSnapshotPayloadMetadata(candidate.payload, candidate.revision, candidate.tick);
596 if (!metadata.ok()) return eve::Result<void>::failure(metadata.status());
597 auto payload = candidate.payload.toJson();
598 if (!payload.ok()) return eve::Result<void>::failure(payload.status());
599 const std::string payloadJson = std::move(payload).takeValue();
600
601 // restoreJson validates into a candidate State before the single storage swap.
602 auto restored = restoreJson(payloadJson);
603 if (!restored.ok()) return eve::Result<void>::failure(restored.status());
604
605 instanceId_ = candidate.instanceId;
606 revision_ = candidate.revision;
607 tick_ = candidate.tick;
609}
610
612 auto value = snapshot(hashProvider);
613 if (!value.ok()) return eve::Result<std::string>::failure(value.status());
614 return std::move(value).andThen(
615 [](eve::SnapshotEnvelope&& envelope) { return eve::serializeSnapshotEnvelope(envelope); });
616}
617
619 const eve::SnapshotHashProvider& hashProvider) {
620 auto source = eve::parseSnapshotEnvelope(json, hashProvider);
621 if (!source.ok()) return eve::Result<void>::failure(source.status());
622 return restoreSnapshot(std::move(source).takeValue(), hashProvider);
623}
624
626 return storage_.subscribe([callback = std::move(callback)](const Storage::Event& event) {
627 if (!callback) return;
628 const DefinitionEvent projection = DefinitionRegistry::projectEvent(event);
629 callback(projection);
630 });
631}
632
634 registries_.push_back(std::make_unique<DefinitionRegistry>());
635 return registries_.back().get();
636}
637
639
640void Definitions::expose(ssq::Table& table) {
641 const HSQUIRRELVM vm = table.getHandle();
642 auto definition = table.addClass<Definition>(
643 "Definition", std::function<Definition*()>([]() -> Definition* { return nullptr; }), false);
644 definition.addFunc("getType", [](Definition* value) { return value ? value->type : std::string{}; });
645 definition.addFunc("getId", [](Definition* value) { return value ? value->id : std::string{}; });
646 definition.addFunc("getVersion", [](Definition* value) {
647 return value ? static_cast<std::int64_t>(value->version.value()) : std::int64_t{0};
648 });
649 definition.addFunc("getGeneration", [](Definition* value) {
650 return value ? static_cast<std::int64_t>(value->generation.value()) : std::int64_t{0};
651 });
652 definition.addFunc("getJson", [](Definition* value) { return value ? value->json : std::string{}; });
653
654 auto event = table.addClass<DefinitionEvent>(
655 "DefinitionEvent", std::function<DefinitionEvent*()>([]() -> DefinitionEvent* { return nullptr; }), false);
656 event.addFunc("getSequence", [](DefinitionEvent* value) {
657 return value ? static_cast<std::int64_t>(value->sequence.value()) : std::int64_t{0};
658 });
659 event.addFunc("getName", [](DefinitionEvent* value) { return value ? value->name : std::string{}; });
660 event.addFunc("getType", [](DefinitionEvent* value) { return value ? value->type : std::string{}; });
661 event.addFunc("getId", [](DefinitionEvent* value) { return value ? value->id : std::string{}; });
662 event.addFunc("getVersion", [](DefinitionEvent* value) {
663 return value ? static_cast<std::int64_t>(value->version.value()) : std::int64_t{0};
664 });
665 event.addFunc("getGeneration", [](DefinitionEvent* value) {
666 return value ? static_cast<std::int64_t>(value->generation.value()) : std::int64_t{0};
667 });
668
669 auto registry = table.addClass<DefinitionRegistry>(
670 "DefinitionRegistry", std::function<DefinitionRegistry*()>([]() -> DefinitionRegistry* { return nullptr; }),
671 false);
672 registry.addFunc("insert", [vm](DefinitionRegistry* value, const std::string& type, const std::string& id,
673 int version, const std::string& json) {
674 if (!value)
676 vm,
678 "definition registry must not be null",
679 "registry", {}, "definitions.squirrel")),
680 [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); });
681 return eve::script::projectResult(vm, value->insert(type, id, version, json),
682 [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); });
683 });
684 registry.addFunc("replace", [vm](DefinitionRegistry* value, const std::string& type, const std::string& id,
685 int version, const std::string& json) {
686 if (!value)
688 vm,
690 "definition registry must not be null",
691 "registry", {}, "definitions.squirrel")),
692 [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); });
693 return eve::script::projectResult(vm, value->replace(type, id, version, json),
694 [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); });
695 });
696 registry.addFunc("remove", [vm](DefinitionRegistry* value, const std::string& type, const std::string& id) {
697 if (!value)
699 vm,
701 "definition registry must not be null",
702 "registry", {}, "definitions.squirrel")),
703 [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); });
704 return eve::script::projectResult(vm, value->remove(type, id),
705 [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); });
706 });
707 registry.addFunc("resolve", [vm](DefinitionRegistry* value, const std::string& type, const std::string& id) {
708 if (!value)
710 vm,
711 eve::Result<std::reference_wrapper<const Definition>>::failure(
712 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "definition registry must not be null",
713 "registry", {}, "definitions.squirrel")),
714 [](std::reference_wrapper<const Definition>&& definition) {
715 return definitionProjection(definition.get());
716 });
717 return eve::script::projectResult(vm, value->resolve(type, id),
718 [](std::reference_wrapper<const Definition>&& definition) {
719 return definitionProjection(definition.get());
720 });
721 });
722 registry.addFunc("resolveHandle", [vm](DefinitionRegistry* value, const std::string& type, const std::string& id,
723 std::int64_t generation) {
724 if (!value)
726 vm,
727 eve::Result<std::reference_wrapper<const Definition>>::failure(
728 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "definition registry must not be null",
729 "registry", {}, "definitions.squirrel")),
730 [](std::reference_wrapper<const Definition>&& definition) {
731 return definitionProjection(definition.get());
732 });
733 if (generation <= 0)
735 vm,
736 eve::Result<std::reference_wrapper<const Definition>>::failure(eve::Diagnostic::error(
737 eve::DiagnosticCode::InvalidArgument, "definition generation must be positive", "generation", {},
738 "definitions.squirrel")),
739 [](std::reference_wrapper<const Definition>&& definition) {
740 return definitionProjection(definition.get());
741 });
743 vm,
744 value->resolveHandle(
745 DefinitionHandle{value->reference(type, id), eve::Generation(static_cast<std::uint64_t>(generation))}),
746 [](std::reference_wrapper<const Definition>&& definition) {
747 return definitionProjection(definition.get());
748 });
749 });
750 registry.addFunc("size", &DefinitionRegistry::size);
751 registry.addFunc("countType", &DefinitionRegistry::countType);
752 registry.addFunc("at", [](DefinitionRegistry* value, int index) -> Definition* {
753 return value ? const_cast<Definition*>(value->at(index)) : nullptr;
754 });
755 registry.addFunc("atType", [](DefinitionRegistry* value, const std::string& type, int index) -> Definition* {
756 return value ? const_cast<Definition*>(value->atType(type, index)) : nullptr;
757 });
758 registry.addFunc("eventCount", &DefinitionRegistry::eventCount);
759 registry.addFunc("eventAt", [](DefinitionRegistry* value, int index) -> DefinitionEvent* {
760 return value ? const_cast<DefinitionEvent*>(value->eventAt(index)) : nullptr;
761 });
762 registry.addFunc("clearEvents", &DefinitionRegistry::clearEvents);
763 registry.addFunc("snapshotJson", &DefinitionRegistry::snapshotJson);
764 registry.addFunc("restoreJson", [vm](DefinitionRegistry* value, const std::string& json) {
765 if (!value)
768 "definition registry must not be null",
769 "registry", {}, "definitions.squirrel")));
770 return eve::script::projectResult(vm, value->restoreJson(json));
771 });
772 registry.addFunc("handle", [vm](DefinitionRegistry* value, const std::string& type, const std::string& id) {
773 if (!value)
775 vm,
777 "definition registry must not be null",
778 "registry", {}, "definitions.squirrel")),
779 [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); });
780 return eve::script::projectResult(vm, value->handle(type, id),
781 [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); });
782 });
783 registry.addFunc("generationOf", [vm](DefinitionRegistry* value, const std::string& type, const std::string& id) {
784 if (!value)
786 vm,
788 "definition registry must not be null",
789 "registry", {}, "definitions.squirrel")),
790 [](eve::Generation generation) { return eve::Value(static_cast<std::int64_t>(generation.value())); });
791 return eve::script::projectResult(vm, value->generationOf(type, id), [](eve::Generation generation) {
792 return eve::Value(static_cast<std::int64_t>(generation.value()));
793 });
794 });
795
796 auto cls = table.addClass(name, Definitions::create, false);
797 expose(cls);
798}
799
800void Definitions::expose(ssq::Class& cls) {
801 cls.addFunc("getName", &Definitions::getName);
802 cls.addFunc("newRegistry", &Definitions::newRegistry);
803}
804
805} // namespace eve::definitions
double value
Value::Object payload
int root
Definition AnimSmr.cpp:119
std::string output
AuthorityStoreHandleRef reference
Definition Authority.cpp:24
std::unordered_map< std::string, std::unordered_map< std::string, std::uint64_t > > generations
struct SQVM * HSQUIRRELVM
HSQUIRRELVM vm
Definition ECS.cpp:20
HSQOBJECT cls
Definition ECS.cpp:21
std::uint32_t key
std::string text
std::array< float, 3 > position
bool required
std::string name
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:26
std::unique_ptr< gpgpu::Sequence > sequence
Definition OnnxGpgpu.cpp:43
std::string error
Definition Package.cpp:60
std::vector< std::string > fields
Definition PlayHost.cpp:111
std::string id
Definition PlayHost.cpp:108
std::uint32_t generation
PrimitiveHandle handle
double number
int removed
double current
The single Squirrel projection for common Result, Status and Value.
Battle::Events events
Json object
uint32_t index
const UnitySourceAsset & source
const std::map< std::string, Value > & definitions
ViewPreparation callback
Stable definition reference backed by LogicalId.
Definition ResourceRef.h:85
static Result< DefinitionRef > fromId(LogicalId id)
Build a definition reference from a valid LogicalId.
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
Human-readable, scoped identifier in the form namespace:name.
Definition Identity.h:411
static std::optional< LogicalId > fromParts(std::string_view namespaceName, std::string_view name)
Builds a logical ID from separate namespace and name components.
Definition Identity.cpp:48
static std::optional< LogicalId > parse(std::string_view text)
Parses a scoped logical name.
Definition Identity.cpp:36
virtual std::string getName() const =0
Returns the name.
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
One directed payload migration step.
Definition Snapshot.h:145
Result< void > add(LogicalId schema, SchemaVersion from, SchemaVersion to, Migration migration)
Register one schema-local migration edge.
Definition Snapshot.cpp:196
Structured status and zero or more diagnostics for an operation.
Definition Status.h:68
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
Move-only RAII token that owns one observer registration.
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
std::map< std::string, Value > Object
Definition Value.h:34
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
const Key * keyAt(std::size_t index) const noexcept
Returns the key for a live enumeration index, or nullptr.
ResultRef< const Value > resolve(const Key &key) const
Resolves a live key to a borrowed value.
std::size_t size() const noexcept
Number of live values, excluding tombstones.
Result< Handle > replace(Key key, Value value, EventData data={}, std::string label={})
Replaces an existing live value and invalidates its old handle.
void clearEvents() noexcept
Clears retained events without changing entries or sequence monotonicity.
Result< Handle > handle(const Key &key) const
Returns the current live handle for a key.
Result< void > restoreState(State candidate)
Transactionally replaces all registry state from an owning image.
const Value * at(std::size_t index) const noexcept
Returns a live value by deterministic key order.
Subscription subscribe(ChangeCallback callback)
Subscribes to successful insert, replace and remove events.
Result< Generation > generationOf(const Key &key) const
Returns the latest generation for a live key or tombstone.
Result< Handle > insert(Key key, Value value, EventData data={}, std::string label={})
Inserts a new live value, or revives a tombstoned key.
Result< Handle > remove(Key key, EventData data={}, std::string label={})
Removes a live value while retaining its generation tombstone.
const Event * eventAt(std::size_t index) const noexcept
Returns a retained event by insertion sequence order, or nullptr.
State snapshotState() const
Takes an owning copy of live entries, tombstones and event history.
std::size_t eventCount() const noexcept
Number of retained mutation events.
Deterministic registry of topic-neutral, versioned JSON definitions.
Definition Definitions.h:58
int size() const
Returns the total number of definitions.
eve::ResultRef< const Definition > resolve(const std::string &type, const std::string &id) const
Resolves a definition through the common registry API.
eve::Result< DefinitionHandle > insert(const std::string &type, const std::string &id, int version, const std::string &json)
Inserts a definition using the common generation-qualified registry.
eve::Result< eve::Generation > generationOf(const std::string &type, const std::string &id) const
Returns the latest generation, including a retained tombstone.
DefinitionRef reference(const std::string &type, const std::string &id) const
Creates a reload-safe logical reference containing only type and id.
void clearEvents()
Clears retained events without resetting their sequence allocator.
std::string snapshotJson() const
Exports all registry state as deterministic canonical JSON.
eve::ResultRef< const Definition > resolveHandle(const DefinitionHandle &handle) const
Resolves a generation-qualified handle and reports stale identities.
eve::Result< DefinitionHandle > replace(const std::string &type, const std::string &id, int version, const std::string &json)
Replaces a live definition and invalidates its previous handle.
eve::Result< DefinitionHandle > remove(const std::string &type, const std::string &id)
Removes a definition and returns the retained tombstone handle.
const Definition * at(int index) const
Returns a definition by globally sorted type/id order, or nullptr.
eve::Subscription subscribe(ChangeCallback callback)
Subscribes to successful definition register, replace and remove operations.
eve::Result< DefinitionHandle > handle(const std::string &type, const std::string &id) const
Returns the current generation-qualified handle through Result.
int countType(const std::string &type) const
Returns the number of definitions registered for a type.
bool isTombstone(const std::string &type, const std::string &id) const noexcept
Returns whether a key is retained as a tombstone.
eve::Result< std::string > snapshotEnvelopeJson(const eve::SnapshotHashProvider &hashProvider) const
Serializes the common snapshot envelope as canonical JSON.
eve::Result< eve::SnapshotEnvelope > snapshot(const eve::SnapshotHashProvider &hashProvider) const
Captures this registry in the common versioned snapshot envelope.
eve::Result< void > restoreJson(const std::string &json)
Transactionally restores registry state from snapshotJson() output.
bool isStale(const DefinitionHandle &handle) const noexcept
Returns whether a generation-qualified handle is stale.
eve::Result< void > restoreSnapshotJson(std::string_view json, const eve::SnapshotHashProvider &hashProvider)
Parses and transactionally restores a common snapshot envelope.
eve::Result< void > restoreSnapshot(const eve::SnapshotEnvelope &snapshot, const eve::SnapshotHashProvider &hashProvider)
Restores a verified or migratable snapshot transactionally.
const Definition * atType(const std::string &type, int index) const
Returns a definition by id order within one type, or nullptr.
std::function< void(const DefinitionEvent &)> ChangeCallback
Definition Definitions.h:66
int eventCount() const
Returns the number of retained mutation events.
const DefinitionEvent * eventAt(int index) const
Returns a retained mutation event by sequence order, or nullptr.
Script module factory for independent definition registries.
DefinitionRegistry * newRegistry()
Allocates a module-owned definition registry.
Id128 public API.
Definition Identity.h:112
constexpr bool isNil() const noexcept
Returns whether this value is the all-zero nil ID.
Definition Identity.h:176
constexpr std::optional< StrongUint64 > incremented() const noexcept
Returns the next value, or empty instead of unsigned wraparound.
constexpr std::uint64_t value() const noexcept
Returns the underlying value at an explicit protocol boundary.
constexpr bool isZero() const noexcept
Returns whether this value is zero.
static eve::Result< SchemaRegistrationStatus > registerVersioned(const SchemaDefinition &definition)
Registers a new exact (id, version) entry without replacing it.
static std::vector< ValidationError > validate(const std::string &schemaId, const std::string &json)
Validates JSON text against the highest registered schema version.
static const SchemaDefinition * resolve(const std::string &schemaId, int schemaVersion)
Resolves one exact (schemaId, schemaVersion) pair, or nullptr.
eve::definition::DefinitionHandle DefinitionHandle
Definition Definitions.h:28
std::unordered_map< std::string, SkillDefinition > & table()
Definition Skill.cpp:65
ValueType
JSON-compatible value kinds understood by a schema field.
Definition SchemaTypes.h:16
ssq::Table projectResult(HSQUIRRELVM vm, Result< void > &&result)
Consume and project a void native Result using the common schema.
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16
std::vector< DiagnosticDetail > DiagnosticDetails
Owning collection of diagnostic details with stable insertion order.
Definition Diagnostic.h:88
detail::StrongUint64< detail::GenerationTag > Generation
Registry/object replacement generation used to reject stale handles.
Definition Generation.h:13
Result< void > validateSnapshotPayloadMetadata(const Value &payload, Revision revision, SimulationTick tick)
Validate optional payload copies of envelope revision and tick.
Definition Snapshot.cpp:80
Result< SnapshotEnvelope > parseSnapshotEnvelope(std::string_view json, const SnapshotHashProvider &hashProvider)
Parse and verify an envelope from canonical or compatible JSON text.
Definition Snapshot.cpp:190
detail::StrongUint64< detail::SchemaVersionTag > SchemaVersion
Persistent data-format version; not a runtime replacement generation.
Result< std::string > serializeSnapshotEnvelope(const SnapshotEnvelope &snapshot)
Serialize an envelope as deterministic compact JSON.
Definition Snapshot.cpp:184
Result< SnapshotEnvelope > makeSnapshotEnvelope(std::string type, LogicalId schema, SchemaVersion schemaVersion, PersistentId instanceId, Revision revision, SimulationTick tick, Value payload, const SnapshotHashProvider &hashProvider)
Construct and seal a snapshot envelope.
Definition Snapshot.cpp:103
detail::StrongUint64< detail::RevisionTag > Revision
Monotonic content/state revision used for optimistic-concurrency checks.
Definition Revision.h:13
detail::StrongUint64< detail::EventSequenceTag > EventSequence
Stream-local event ordering value; it is not a global event identity.
std::function< Result< ContentId >(std::string_view canonicalInput)> SnapshotHashProvider
Injected content-digest implementation used by snapshots.
Definition Snapshot.h:36
Stable outer format shared by persistence and cross-process snapshots.
Definition Snapshot.h:46
A generation-qualified reference to one definition incarnation.
Registry mutation notification in deterministic sequence order.
Definition Definitions.h:42
One immutable view of a registered versioned JSON definition.
Definition Definitions.h:31
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
Recursive node in the deliberately small Eve Schema language.
Definition SchemaTypes.h:47
ValueType type
The value kind accepted by this node.
Definition SchemaTypes.h:49
std::shared_ptr< const SchemaNode > itemSchema
Recursive schema for every array item.
Definition SchemaTypes.h:59
std::shared_ptr< const SchemaDefinition > objectSchema
Inline object shape for an object node.
Definition SchemaTypes.h:57