Gaia-ECS v0.9.3
A simple and powerful entity component system
Loading...
Searching...
No Matches
world_json.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <cstring>
5
6#include "gaia/ser/ser_json.h"
7
8namespace gaia {
9 namespace ecs {
13 inline bool component_to_json(const ComponentCacheItem& item, const void* pComponentData, ser::ser_json& writer) {
14 GAIA_ASSERT(pComponentData != nullptr);
15 if (pComponentData == nullptr)
16 return false;
17
18 bool ok = true;
19 const auto* pBase = reinterpret_cast<const uint8_t*>(pComponentData);
20
21 writer.begin_object();
22 for (const auto& field: item.schema) {
23 writer.key(field.name);
24 if (field.offset + field.size > item.comp.size()) {
25 writer.value_null();
26 ok = false;
27 continue;
28 }
29 const auto* pFieldData = pBase + field.offset;
30 ok = ser::detail::write_schema_field_json(writer, pFieldData, field.type, field.size) && ok;
31 }
32 writer.end_object();
33
34 return ok;
35 }
36
38 inline ser::json_str component_to_json(const ComponentCacheItem& item, const void* pComponentData, bool& ok) {
39 ser::ser_json writer;
40 ok = component_to_json(item, pComponentData, writer);
41 return writer.str();
42 }
43
52 inline bool json_to_component(
53 const ComponentCacheItem& item, void* pComponentData, ser::ser_json& reader, ser::JsonDiagnostics& diagnostics,
54 ser::json_str_view componentPath = {}) {
55 GAIA_ASSERT(pComponentData != nullptr);
56 if (pComponentData == nullptr)
57 return false;
58
59 auto make_field_path = [&](ser::json_str_view fieldName) {
60 if (componentPath.empty())
61 return ser::json_str(fieldName);
62 if (fieldName.empty())
63 return ser::json_str(componentPath);
64
65 ser::json_str path;
66 path.reserve(componentPath.size() + 1 + fieldName.size());
67 path.append(componentPath.data(), componentPath.size());
68 path.append('.');
69 path.append(fieldName.data(), fieldName.size());
70 return path;
71 };
72 auto warn = [&](ser::JsonDiagReason reason, const ser::json_str& path, const char* message) {
73 diagnostics.add(ser::JsonDiagSeverity::Warning, reason, path, message);
74 };
75
76 if (reader.parse_null()) {
77 warn(ser::JsonDiagReason::NullComponentPayload, make_field_path(""), "Component payload is null.");
78 return true;
79 }
80
81 if (!reader.expect('{'))
82 return false;
83
84 bool rawFound = false;
85 bool schemaFound = false;
86 ser::ser_buffer_binary rawPayload;
87 auto* pBase = reinterpret_cast<uint8_t*>(pComponentData);
88
89 reader.ws();
90 if (reader.consume('}')) {
91 warn(
92 ser::JsonDiagReason::MissingSchemaOrRawPayload, make_field_path(""),
93 "Component object is empty and contains no schema fields or $raw payload.");
94 return true;
95 }
96
97 while (true) {
98 ser::json_str_view key;
99 bool keyFromScratch = false;
100 if (!reader.parse_string_view(key, &keyFromScratch))
101 return false;
102 ser::json_str keyStorage;
103 if (keyFromScratch) {
104 keyStorage.assign(key.data(), key.size());
105 key = keyStorage;
106 }
107 if (!reader.expect(':'))
108 return false;
109
110 if (key == "$raw") {
111 rawFound = true;
112 if (!ser::detail::parse_json_byte_array(reader, rawPayload))
113 return false;
114 } else if (item.has_fields() && item.comp.soa() == 0) {
115 const ComponentCacheItem::SchemaField* pField = nullptr;
116 for (const auto& field: item.schema) {
117 const auto fieldNameLen = (size_t)GAIA_STRLEN(field.name, ComponentCacheItem::MaxNameLength);
118 if (key.size() == fieldNameLen && memcmp(key.data(), field.name, key.size()) == 0) {
119 pField = &field;
120 break;
121 }
122 }
123
124 if (pField == nullptr) {
125 warn(ser::JsonDiagReason::UnknownField, make_field_path(key), "Unknown schema field.");
126 if (!reader.skip_value())
127 return false;
128 } else if (pField->offset + pField->size > item.comp.size()) {
129 warn(
130 ser::JsonDiagReason::FieldOutOfBounds, make_field_path(key),
131 "Schema field points outside component size.");
132 if (!reader.skip_value())
133 return false;
134 } else {
135 schemaFound = true;
136 auto* pFieldData = pBase + pField->offset;
137 bool fieldOk = true;
138 if (!ser::detail::read_schema_field_json(reader, pFieldData, pField->type, pField->size, fieldOk))
139 return false;
140 if (!fieldOk) {
141 warn(
142 ser::JsonDiagReason::FieldValueAdjusted, make_field_path(key),
143 "Field value was lossy, truncated, or unsupported for the target schema type.");
144 }
145 }
146 } else {
147 warn(
148 ser::JsonDiagReason::MissingSchemaOrRawPayload, make_field_path(key),
149 "Component schema is unavailable for keyed field payloads.");
150 if (!reader.skip_value())
151 return false;
152 }
153
154 reader.ws();
155 if (reader.consume(','))
156 continue;
157 if (reader.consume('}'))
158 break;
159 return false;
160 }
161
162 if (rawFound) {
163 if (item.comp.soa() != 0) {
164 warn(
165 ser::JsonDiagReason::SoaRawUnsupported, make_field_path("$raw"),
166 "$raw payload is not supported for SoA components.");
167 return true;
168 }
169
170 auto s = ser::make_serializer(rawPayload);
171 s.seek(0);
172 item.load(s, pBase, 0, 1, 1);
173 }
174
175 if (!rawFound && !schemaFound)
176 warn(
177 ser::JsonDiagReason::MissingSchemaOrRawPayload, make_field_path(""),
178 "Component payload contains neither recognized schema fields nor $raw data.");
179
180 return true;
181 }
182
184 inline bool
185 json_to_component(const ComponentCacheItem& item, void* pComponentData, ser::ser_json& reader, bool& ok) {
186 ser::JsonDiagnostics diagnostics;
187 const bool parsed = json_to_component(item, pComponentData, reader, diagnostics);
188 ok = !diagnostics.has_issues();
189 return parsed;
190 }
191
196 inline bool World::save_json(ser::ser_json& writer, ser::JsonSaveFlags flags) const {
197 auto write_raw_component = [&](const ComponentCacheItem& item, const uint8_t* pData, uint32_t from, uint32_t to,
198 uint32_t cap) {
200 auto s = ser::make_serializer(raw);
201 item.save(s, pData, from, to, cap);
202
203 writer.begin_object();
204 writer.key("$raw");
205 writer.begin_array();
206 const auto* pRaw = raw.data();
207 GAIA_FOR(raw.bytes()) writer.value_int(pRaw[i]);
208 writer.end_array();
209 writer.end_object();
210 };
211
212 bool ok = true;
213 const bool includeBinarySnapshot = (flags & ser::JsonSaveFlags::BinarySnapshot) != 0;
214 const bool allowRawFallback = (flags & ser::JsonSaveFlags::RawFallback) != 0;
215 ser::bin_stream binarySnapshot;
216 if (includeBinarySnapshot) {
217 auto s = ser::make_serializer(binarySnapshot);
218 s.reset();
219 save_to(s);
220 }
221
222 writer.clear();
223 writer.begin_object();
224 writer.key("format");
225 writer.value_int(WorldSerializerJSONVersion);
226 writer.key("worldVersion");
227 writer.value_int(m_worldVersion);
228 if (includeBinarySnapshot) {
229 writer.key("binary");
230 writer.begin_array();
231 {
232 const auto* pData = (const uint8_t*)binarySnapshot.data();
233 GAIA_FOR(binarySnapshot.bytes()) writer.value_int(pData[i]);
234 }
235 writer.end_array();
236 }
237 writer.key("archetypes");
238 writer.begin_array();
239
240 for (const auto* pArchetype: m_archetypes) {
241 if (pArchetype == nullptr || pArchetype->chunks().empty())
242 continue;
243
244 writer.begin_object();
245 writer.key("id");
246 writer.value_int((uint32_t)pArchetype->id());
247 writer.key("hash");
248 writer.value_int((uint64_t)pArchetype->lookup_hash().hash);
249
250 writer.key("components");
251 writer.begin_array();
252 {
253 for (const auto entity: pArchetype->ids_view()) {
254 const auto itemName = name(entity);
255 if (!itemName.empty())
256 writer.value_string(itemName.data(), itemName.size());
257 else
258 writer.value_string("<unnamed>");
259 }
260 }
261 writer.end_array();
262
263 writer.key("entities");
264 writer.begin_array();
265 {
266 for (const auto* pChunk: pArchetype->chunks()) {
267 if (pChunk == nullptr || pChunk->empty())
268 continue;
269
270 const auto ents = pChunk->entity_view();
271 const auto recs = pChunk->comp_rec_view();
272 GAIA_FOR((uint32_t)ents.size()) {
273 const auto entity = ents[i];
274
275 writer.begin_object();
276 {
277 writer.key("entity");
278 {
279 writer.begin_object();
280 writer.key("id");
281 writer.value_int(entity.id());
282 writer.key("gen");
283 writer.value_int(entity.gen());
284 writer.key("pair");
285 writer.value_bool(entity.pair());
286 writer.key("kind");
287 writer.value_string(EntityKindString[entity.kind()]);
288 const auto entityName = name(entity);
289 if (!entityName.empty()) {
290 writer.key("name");
291 writer.value_string(entityName.data(), entityName.size());
292 }
293 writer.end_object();
294 }
295
296 writer.key("components");
297 writer.begin_object();
298 {
299 GAIA_FOR_((uint32_t)recs.size(), j) {
300 const auto& rec = recs[j];
301 const auto& item = *rec.pItem;
302 const auto name = symbol(item.entity);
303 writer.key(name.data(), name.size());
304
305 // Tags have no associated payload.
306 if (rec.comp.size() == 0) {
307 writer.value_bool(true);
308 continue;
309 }
310
311 const auto row = item.entity.kind() == EntityKind::EK_Uni ? 0U : i;
312
313 if (item.has_fields() && rec.comp.soa() == 0) {
314 const auto* pCompData = pChunk->comp_ptr(j, row);
315 ok = ecs::component_to_json(item, pCompData, writer) && ok;
316 } else {
317 if (allowRawFallback)
318 write_raw_component(item, rec.pData, row, row + 1, pChunk->capacity());
319 else {
320 writer.value_null();
321 ok = false;
322 }
323 }
324 }
325 writer.end_object();
326 }
327 }
328 writer.end_object();
329 }
330 }
331 }
332
333 writer.end_array();
334 writer.end_object();
335 }
336
337 writer.end_array();
338 writer.end_object();
339 return ok;
340 }
341
343 inline ser::json_str World::save_json(bool& ok, ser::JsonSaveFlags flags) const {
344 ser::ser_json writer;
345 ok = save_json(writer, flags);
346 return writer.str();
347 }
348
350 inline bool World::load_json(const char* json, uint32_t len, ser::JsonDiagnostics& diagnostics) {
351 diagnostics.clear();
352 if (json == nullptr)
353 return false;
354 if (len == 0) {
355 diagnostics.add(
356 ser::JsonDiagSeverity::Error, ser::JsonDiagReason::InvalidJson, "$",
357 "Input JSON length must be provided and non-zero.");
358 return false;
359 }
360
361 const auto dataLen = len;
362 const auto* p = json;
363 const auto* end = json + dataLen;
364 auto warn = [&](ser::JsonDiagReason reason, ser::json_str_view path, const char* message) {
365 diagnostics.add(ser::JsonDiagSeverity::Warning, reason, path, message);
366 };
367 auto error = [&](ser::JsonDiagReason reason, ser::json_str_view path, const char* message) {
368 diagnostics.add(ser::JsonDiagSeverity::Error, reason, path, message);
369 };
370
371 // Validate top-level format version first.
372 {
373 ser::ser_json header(json, dataLen);
374 if (!header.expect('{')) {
375 error(ser::JsonDiagReason::InvalidJson, "$", "Root JSON value must be an object.");
376 return false;
377 }
378
379 bool hasFormat = false;
380 uint32_t formatValue = 0;
381
382 header.ws();
383 if (!header.consume('}')) {
384 while (true) {
386 if (!header.parse_string_view(key))
387 return false;
388 if (!header.expect(':'))
389 return false;
390
391 if (key == "format") {
392 double d = 0.0;
393 if (!header.parse_number(d))
394 return false;
395 if (d < 0.0 || d > 4294967295.0)
396 return false;
397 const auto v = (uint32_t)d;
398 if ((double)v != d)
399 return false;
400 formatValue = v;
401 hasFormat = true;
402 } else {
403 if (!header.skip_value())
404 return false;
405 }
406
407 header.ws();
408 if (header.consume(','))
409 continue;
410 if (header.consume('}'))
411 break;
412 return false;
413 }
414 }
415
416 header.ws();
417 if (!header.eof())
418 return false;
419
420 if (!hasFormat) {
421 error(ser::JsonDiagReason::MissingFormatField, "$.format", "Missing required 'format' field.");
422 return false;
423 }
424
425 if (formatValue != WorldSerializerJSONVersion) {
426 error(
427 ser::JsonDiagReason::UnsupportedFormatVersion, "$.format",
428 "Unsupported format version. Expected numeric value 1.");
429 return false;
430 }
431 }
432
433 // Prefer fast-path: binary snapshot payload.
434 {
435 const char key[] = "\"binary\"";
436 const uint32_t keyLen = (uint32_t)(sizeof(key) - 1);
437 const char* keyPos = nullptr;
438 for (const char* it = p; it + keyLen <= end; ++it) {
439 if (memcmp(it, key, keyLen) == 0) {
440 keyPos = it;
441 break;
442 }
443 }
444 if (keyPos != nullptr) {
445 const char* arr = nullptr;
446 for (const char* it = keyPos + keyLen; it < end; ++it) {
447 if (*it == '[') {
448 arr = it;
449 break;
450 }
451 }
452 if (arr != nullptr) {
453 ser::bin_stream serializer;
454 ser::ser_json binaryReader(arr, (uint32_t)(end - arr));
455 if (!ser::detail::parse_json_byte_array(binaryReader, serializer))
456 return false;
457
458 return load(serializer);
459 }
460 }
461 }
462
463 // Fallback: semantic world JSON parser.
464 ser::ser_json jp(json, dataLen);
465
466 struct CompDataLoc {
467 uint8_t* pBase = nullptr;
468 uint32_t row = 0;
469 uint32_t cap = 0;
470 bool valid = false;
471 };
472
473 auto locate_component_data = [&](Entity entity, const ComponentCacheItem& item) -> CompDataLoc {
474 CompDataLoc loc{};
475 if (!has(entity))
476 return loc;
477
478 auto& ec = fetch(entity);
479 const auto compIdx = core::get_index(ec.pChunk->ids_view(), item.entity);
480 if (compIdx == BadIndex)
481 return loc;
482
483 loc.pBase = ec.pChunk->comp_ptr_mut(compIdx, 0);
484 loc.row = item.entity.kind() == EntityKind::EK_Uni ? 0U : ec.row;
485 loc.cap = ec.pChunk->capacity();
486 loc.valid = true;
487 return loc;
488 };
489
490 auto has_direct_component = [&](Entity entity, Entity componentEntity) -> bool {
491 if (!has(entity))
492 return false;
493 const auto& ec = fetch(entity);
494 return core::get_index(ec.pChunk->ids_view(), componentEntity) != BadIndex;
495 };
496
497 auto parse_and_apply_component_value = [&](Entity entity, const ComponentCacheItem& item,
498 ser::json_str_view compPath) -> bool {
499 jp.ws();
500 if (jp.eof())
501 return false;
502
503 // Tag values are currently ignored by semantic loader.
504 const char next = jp.peek();
505 if (next == 't' || next == 'f') {
506 bool tagValue = false;
507 if (!jp.parse_bool(tagValue))
508 return false;
509 warn(
510 ser::JsonDiagReason::TagValueIgnored, compPath,
511 "Tag-like boolean component payload is ignored in semantic mode.");
512 return true;
513 }
514
515 if (jp.parse_null()) {
516 warn(
517 ser::JsonDiagReason::NullComponentPayload, compPath,
518 "Null component payload is ignored in semantic mode.");
519 return true;
520 }
521
522 if (!has_direct_component(entity, item.entity))
523 add(entity, item.entity);
524
525 auto loc = locate_component_data(entity, item);
526 if (!loc.valid) {
527 warn(
528 ser::JsonDiagReason::MissingComponentStorage, compPath,
529 "Component storage is unavailable on the target entity.");
530 return jp.skip_value();
531 }
532
533 auto* pRowData = loc.pBase + (uintptr_t)item.comp.size() * loc.row;
534 if (!ecs::json_to_component(item, pRowData, jp, diagnostics, compPath))
535 return false;
536 return true;
537 };
538
539 auto parse_entity_meta = [&](bool& isPair, ser::json_str& nameOut) -> bool {
540 if (!jp.expect('{'))
541 return false;
542
543 jp.ws();
544 if (jp.consume('}'))
545 return true;
546
547 while (true) {
549 if (!jp.parse_string_view(key))
550 return false;
551 if (!jp.expect(':'))
552 return false;
553
554 if (key == "pair") {
555 if (!jp.parse_bool(isPair))
556 return false;
557 } else if (key == "name") {
558 if (!jp.parse_string(nameOut))
559 return false;
560 } else {
561 if (!jp.skip_value())
562 return false;
563 }
564
565 jp.ws();
566 if (jp.consume(','))
567 continue;
568 if (jp.consume('}'))
569 break;
570 return false;
571 }
572
573 return true;
574 };
575
576 auto parse_components_for_entity = [&](Entity& entity, bool& created, bool isPair,
577 const ser::json_str& entityName) -> bool {
578 if (!jp.expect('{'))
579 return false;
580
581 jp.ws();
582 if (jp.consume('}'))
583 return true;
584
585 while (true) {
586 ser::json_str_view compName;
587 bool compNameFromScratch = false;
588 if (!jp.parse_string_view(compName, &compNameFromScratch))
589 return false;
590 ser::json_str compNameStorage;
591 if (compNameFromScratch) {
592 compNameStorage.assign(compName.data(), compName.size());
593 compName = compNameStorage;
594 }
595 if (!jp.expect(':'))
596 return false;
597
598 const bool isInternalComp = compName.size() >= 10 && memcmp(compName.data(), "gaia::ecs::", 10) == 0;
599 if (isPair || isInternalComp) {
600 if (!jp.skip_value())
601 return false;
602 } else {
603 const auto componentEntity = get(compName.data(), (uint32_t)compName.size());
604 const auto* pItem = componentEntity != EntityBad ? comp_cache().find(componentEntity) : nullptr;
605 if (pItem == nullptr) {
606 warn(
607 ser::JsonDiagReason::UnknownComponent, compName,
608 "Component is not registered in the component cache.");
609 if (!jp.skip_value())
610 return false;
611 } else if (pItem->comp.size() == 0) {
612 // Ignore tag-only components in semantic mode for now.
613 warn(
614 ser::JsonDiagReason::TagComponentUnsupported, compName,
615 "Tag-only component semantic JSON loading is currently unsupported.");
616 if (!jp.skip_value())
617 return false;
618 } else {
619 if (!created) {
620 entity = add();
621 created = true;
622 if (!entityName.empty()) {
623 const auto existing = get(entityName.data(), (uint32_t)entityName.size());
624 if (existing == EntityBad)
625 name(entity, entityName.data(), (uint32_t)entityName.size());
626 else
627 warn(
628 ser::JsonDiagReason::DuplicateEntityName, "entity.name",
629 "Entity name already exists; keeping existing mapping.");
630 }
631 }
632
633 if (!parse_and_apply_component_value(entity, *pItem, compName))
634 return false;
635 }
636 }
637
638 jp.ws();
639 if (jp.consume(','))
640 continue;
641 if (jp.consume('}'))
642 break;
643 return false;
644 }
645
646 return true;
647 };
648
649 auto parse_entity_entry = [&]() -> bool {
650 if (!jp.expect('{')) {
651 error(ser::JsonDiagReason::InvalidJson, "$", "Root JSON value must be an object.");
652 return false;
653 }
654
655 bool isPair = false;
656 ser::json_str entityName;
657 Entity entity = EntityBad;
658 bool created = false;
659
660 jp.ws();
661 if (jp.consume('}'))
662 return true;
663
664 while (true) {
666 if (!jp.parse_string_view(key))
667 return false;
668 if (!jp.expect(':'))
669 return false;
670
671 if (key == "entity") {
672 if (!parse_entity_meta(isPair, entityName))
673 return false;
674 } else if (key == "components") {
675 if (!parse_components_for_entity(entity, created, isPair, entityName))
676 return false;
677 } else {
678 if (!jp.skip_value())
679 return false;
680 }
681
682 jp.ws();
683 if (jp.consume(','))
684 continue;
685 if (jp.consume('}'))
686 break;
687 return false;
688 }
689
690 return true;
691 };
692
693 auto parse_archetypes = [&]() -> bool {
694 if (!jp.expect('['))
695 return false;
696
697 jp.ws();
698 if (jp.consume(']'))
699 return true;
700
701 while (true) {
702 if (!jp.expect('{'))
703 return false;
704
705 jp.ws();
706 if (!jp.consume('}')) {
707 while (true) {
709 if (!jp.parse_string_view(key))
710 return false;
711 if (!jp.expect(':'))
712 return false;
713
714 if (key == "entities") {
715 if (!jp.expect('['))
716 return false;
717
718 jp.ws();
719 if (!jp.consume(']')) {
720 while (true) {
721 if (!parse_entity_entry())
722 return false;
723
724 jp.ws();
725 if (jp.consume(','))
726 continue;
727 if (jp.consume(']'))
728 break;
729 return false;
730 }
731 }
732 } else {
733 if (!jp.skip_value())
734 return false;
735 }
736
737 jp.ws();
738 if (jp.consume(','))
739 continue;
740 if (jp.consume('}'))
741 break;
742 return false;
743 }
744 }
745
746 jp.ws();
747 if (jp.consume(','))
748 continue;
749 if (jp.consume(']'))
750 break;
751 return false;
752 }
753
754 return true;
755 };
756
757 if (!jp.expect('{'))
758 return false;
759
760 bool hasArchetypes = false;
761 jp.ws();
762 if (!jp.consume('}')) {
763 while (true) {
765 if (!jp.parse_string_view(key))
766 return false;
767 if (!jp.expect(':'))
768 return false;
769
770 if (key == "archetypes") {
771 hasArchetypes = true;
772 if (!parse_archetypes())
773 return false;
774 } else {
775 if (!jp.skip_value())
776 return false;
777 }
778
779 jp.ws();
780 if (jp.consume(','))
781 continue;
782 if (jp.consume('}'))
783 break;
784 return false;
785 }
786 }
787
788 jp.ws();
789 if (!jp.eof())
790 return false;
791 if (!hasArchetypes) {
792 error(ser::JsonDiagReason::MissingArchetypesSection, "$.archetypes", "Missing required 'archetypes' section.");
793 return false;
794 }
795
796 return true;
797 }
798
799 inline bool World::load_json(const char* json, uint32_t len) {
800 ser::JsonDiagnostics diagnostics;
801 const bool parsed = load_json(json, len, diagnostics);
802 return parsed && !diagnostics.has_issues();
803 }
804
806 return load_json(json.data(), json.size(), diagnostics);
807 }
808
810 ser::JsonDiagnostics diagnostics;
811 const bool parsed = load_json(json.data(), json.size(), diagnostics);
812 return parsed && !diagnostics.has_issues();
813 }
814 } // namespace ecs
815} // namespace gaia
GAIA_NODISCARD const ComponentCacheItem * find(detail::ComponentDescId compDescId) const noexcept
Searches for the component cache item given the compDescId.
Definition component_cache.h:355
bool save_json(ser::ser_json &writer, ser::JsonSaveFlags flags=ser::JsonSaveFlags::Default) const
Serializes world state into a JSON document. Components with runtime schema are emitted as structured...
Definition world_json.h:196
GAIA_NODISCARD const ComponentCache & comp_cache() const
Returns read-only access to the world component cache.
Definition world.h:4036
bool load_json(const char *json, uint32_t len, ser::JsonDiagnostics &diagnostics)
Loads world state from JSON previously emitted by save_json(). Returns true when JSON shape is valid ...
Definition world_json.h:350
GAIA_NODISCARD Entity symbol(const char *symbol, uint32_t len=0) const
Finds a component entity by its exact registered symbol.
Definition world.h:4046
GAIA_NODISCARD const ComponentCacheItem & add()
Creates a new component if not found already.
Definition world.h:4450
GAIA_NODISCARD Entity get() const
Returns the entity registered for component type T.
Definition world.h:4381
GAIA_NODISCARD EntityContainer & fetch(Entity entity)
Returns the internal record for entity.
Definition world.h:2275
void name(Entity entity, const char *name, uint32_t len=0)
Assigns a name to entity. Ignored if used with pair. The string is copied and kept internally.
Definition world.h:6803
GAIA_NODISCARD bool has(Entity entity) const
Checks if entity is currently used by the world.
Definition world.h:6477
GAIA_NODISCARD Entity path(const char *path, uint32_t len=0) const
Finds a component entity by its exact scoped path.
Definition world.h:4066
bool load(ser::serializer inputSerializer={})
Loads a world state from a buffer. The buffer is sought to 0 before any loading happens....
Definition world.h:9083
Default in-memory binary backend used by ECS world/runtime serialization. Provides aligned raw read/w...
Definition ser_binary.h:12
uint32_t bytes() const
Returns total buffered byte count.
Definition ser_binary.h:53
const char * data() const
Returns pointer to serialized bytes.
Definition ser_binary.h:38
GAIA_NODISCARD uint32_t bytes() const
Returns the number of bytes written in the buffer.
Definition ser_buffer_binary.h:35
GAIA_NODISCARD const auto * data() const
Returns the pointer to the data in the buffer.
Definition ser_buffer_binary.h:45
Minimal in-memory binary serializer. It stores raw bytes only (no schema, versioning,...
Definition ser_buffer_binary.h:155
Lightweight JSON serializer/deserializer.
Definition ser_json.h:101
GAIA_NODISCARD const json_str & str() const
Returns currently emitted output text.
Definition ser_json.h:197
void ws()
Skips whitespace in parser input.
Definition ser_json.h:213
GAIA_NODISCARD bool eof() const
Returns true if parser has reached end of input.
Definition ser_json.h:202
void clear()
Clears output JSON text and writer context.
Definition ser_json.h:190
GAIA_NODISCARD char peek() const
Returns next non-consumed character.
Definition ser_json.h:207
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Definition component_cache_item.h:25
Entity entity
Component entity.
Definition component_cache_item.h:75
Definition id.h:241
Definition ser_json.h:57
Lightweight non-owning string view over a character sequence.
Definition str.h:13
GAIA_NODISCARD constexpr uint32_t size() const
Returns the number of characters in the view.
Definition str.h:41
GAIA_NODISCARD constexpr const char * data() const
Returns the underlying character pointer.
Definition str.h:35
Lightweight owning string container with explicit length semantics (no implicit null terminator).
Definition str.h:331
void assign(const char *data, uint32_t size)
Replaces contents with size characters from data.
Definition str.h:365