87 static constexpr uint16_t ARCHETYPE_LIFESPAN_BITS = 7;
89 static_assert(ARCHETYPE_LIFESPAN_BITS >= ChunkHeader::CHUNK_LIFESPAN_BITS);
91 static constexpr uint16_t MAX_ARCHETYPE_LIFESPAN = (1 << ARCHETYPE_LIFESPAN_BITS) - 1;
109 QueryMask m_queryMask{};
111 Properties m_properties{};
113 const World& m_world;
115 const ComponentCache& m_cc;
117 uint32_t& m_worldVersion;
120 cnt::darray<Chunk*> m_chunks;
124 ArchetypeGraph m_graph;
127 ChunkDataOffsets m_dataOffsets;
129 Entity m_ids[ChunkHeader::MAX_COMPONENTS];
131 uint8_t m_pairs_as_index_buffer[ChunkHeader::MAX_COMPONENTS];
133 Component m_comps[ChunkHeader::MAX_COMPONENTS];
135 ChunkDataOffset m_compOffs[ChunkHeader::MAX_COMPONENTS];
138 uint32_t m_firstFreeChunkIdx = 0;
143 uint32_t m_deleteReq : 1;
147 uint32_t m_lifespanCountdownMax : ARCHETYPE_LIFESPAN_BITS;
149 uint32_t m_lifespanCountdown : ARCHETYPE_LIFESPAN_BITS;
151 uint32_t m_pairCnt : ChunkHeader::MAX_COMPONENTS_BITS;
153 uint32_t m_pairCnt_is : ChunkHeader::MAX_COMPONENTS_BITS;
158 Archetype(
const World& world,
const ComponentCache& cc, uint32_t& worldVersion):
159 m_world(world), m_cc(cc), m_worldVersion(worldVersion), m_listIdx(BadIndex),
160 m_deleteReq(0), m_dead(0),
161 m_lifespanCountdownMax(1), m_lifespanCountdown(0),
162 m_pairCnt(0), m_pairCnt_is(0) {}
166 for (
auto* pChunk: m_chunks)
173 void update_data_offsets(uintptr_t memoryAddress) {
174 uintptr_t offset = 0;
180 offset += mem::padding<alignof(ComponentVersion)>(memoryAddress);
182 const auto cnt = comps_view().size() + 1;
183 GAIA_ASSERT(offset < 256);
184 m_dataOffsets.firstByte_Versions = (ChunkDataVersionOffset)offset;
185 offset +=
sizeof(ComponentVersion) * cnt;
190 offset += mem::padding<alignof(Entity)>(offset);
192 const auto cnt = comps_view().size();
194 m_dataOffsets.firstByte_CompEntities = (ChunkDataOffset)offset;
197 offset +=
sizeof(Entity) * ChunkHeader::MAX_COMPONENTS;
203 offset += mem::padding<alignof(ComponentRecord)>(offset);
205 const auto cnt = comps_view().size();
208 m_dataOffsets.firstByte_Records = (ChunkDataOffset)offset;
211 offset +=
sizeof(ComponentRecord) * cnt;
217 offset += mem::padding<alignof(Entity)>(offset);
218 m_dataOffsets.firstByte_EntityData = (ChunkDataOffset)offset;
223 static bool est_max_entities_per_chunk(
224 const ComponentCache& cc, uint32_t offs, ComponentSpan comps, uint32_t cap, uint32_t maxDataOffset) {
225 for (
const auto comp: comps) {
226 if (comp.alig() == 0)
229 const auto& desc = cc.get(comp.id());
232 offs = desc.calc_new_mem_offset(offs, cap);
233 if (offs >= maxDataOffset)
240 static void reg_components(
241 Archetype& arch, EntitySpan ids, ComponentSpan comps, uint8_t from, uint8_t to, uint32_t& currOff,
243 auto& ofs = arch.m_compOffs;
246 GAIA_FOR2(from, to) arch.m_ids[i] = ids[i];
249 GAIA_FOR2(from, to) {
250 const auto comp = comps[i];
251 const auto compIdx = i;
253 const auto alig = comp.alig();
257 currOff = mem::align(currOff, alig);
258 ofs[compIdx] = (ChunkDataOffset)currOff;
261 currOff += comp.size() * count;
267 Archetype(Archetype&&) =
delete;
268 Archetype(
const Archetype&) =
delete;
269 Archetype& operator=(Archetype&&) =
delete;
270 Archetype& operator=(
const Archetype&) =
delete;
272 void save(ser::ISerializer& s) {
273 s.save(m_firstFreeChunkIdx);
276 s.save((uint32_t)m_chunks.size());
277 for (
auto* pChunk: m_chunks) {
278 s.save((uint32_t)pChunk->idx());
283 void load(ser::ISerializer& s) {
284 s.load(m_firstFreeChunkIdx);
287 uint32_t chunkCnt = 0;
290 const auto chunkCnt0 = (uint32_t)m_chunks.size();
291 m_chunks.resize(chunkCnt);
293 GAIA_FOR2(chunkCnt0, chunkCnt) m_chunks[i] =
nullptr;
297 uint32_t chunkIdx = 0;
300 auto* pChunk = m_chunks[chunkIdx];
302 if (pChunk ==
nullptr) {
303 pChunk = Chunk::create(
304 m_world, m_cc, chunkIdx,
305 m_properties.capacity, m_properties.cntEntities,
306 m_properties.genEntities, m_properties.chunkDataBytes,
307 m_worldVersion, m_dataOffsets, m_ids, m_comps, m_compOffs);
308 m_chunks[chunkIdx] = pChunk;
311 pChunk->set_idx(chunkIdx);
316 void list_idx(uint32_t idx) {
320 uint32_t list_idx()
const {
324 GAIA_NODISCARD
bool cmp_comps(
const ArchetypeLookupChecker& other)
const {
325 return detail::cmp_comps(ids_view(), other.m_comps);
328 GAIA_NODISCARD
static Archetype*
329 create(
const World& world, ArchetypeId archetypeId, uint32_t& worldVersion, EntitySpan ids) {
330 const auto& cc = comp_cache(world);
332 auto* newArch = mem::AllocHelper::alloc<Archetype>(
"Archetype");
333 (void)
new (newArch) Archetype(world, cc, worldVersion);
335 newArch->m_archetypeId = archetypeId;
336 newArch->m_archetypeIdHash = ArchetypeIdLookupKey::calc(archetypeId);
341 newArch->m_queryMask = build_entity_mask({ids.data(), ids.size()});
343 const auto cnt = (uint32_t)ids.size();
344 newArch->m_properties.cntEntities = (uint8_t)ids.size();
346 auto as_comp = [&](Entity entity) {
347 const auto* pDesc = cc.find(entity);
348 return pDesc ==
nullptr
349 ? Component(IdentifierIdBad, 0, 0, 0)
354 auto comps =
std::span(&newArch->m_comps[0], cnt);
359 Entity pairEntities[] = {entity_from_id(world, ids[i].
id()), entity_from_id(world, ids[i].gen())};
360 Component pairComponents[] = {as_comp(pairEntities[0]), as_comp(pairEntities[1])};
361 const uint32_t idx = (pairComponents[0].size() != 0U || pairComponents[1].size() == 0U) ? 0 : 1;
362 comps[i] = pairComponents[idx];
364 comps[i] = as_comp(ids[i]);
369 static auto ChunkDataAreaOffset = Chunk::chunk_data_area_offset();
370 newArch->update_data_offsets(
376 ChunkDataAreaOffset);
377 const auto& offs = newArch->m_dataOffsets;
384 ++newArch->m_pairCnt;
387 if (ids[i].
id() == Is.id())
388 newArch->m_pairs_as_index_buffer[newArch->m_pairCnt_is++] = (uint8_t)i;
392 const auto entsCnt = (uint32_t)ids.size();
393 uint32_t entsGeneric = entsCnt;
395 for (
auto i = entsCnt - 1; i != (uint32_t)-1; --i) {
396 if (ids[i].kind() != EntityKind::EK_Uni)
402 uint32_t genCompsSize = 0;
403 uint32_t uniCompsSize = 0;
404 GAIA_FOR(entsGeneric) genCompsSize += newArch->m_comps[i].size();
405 GAIA_FOR2(entsGeneric, cnt) uniCompsSize += newArch->m_comps[i].size();
407 auto compute_max_entities_for_chunk = [&](uint32_t maxEntities, uint32_t dataLimit) -> uint32_t {
409 uint32_t high = maxEntities;
413 auto try_fit = [&](uint32_t count) ->
bool {
414 const uint32_t currOff = offs.firstByte_EntityData + (count *
sizeof(Entity));
416 if (!est_max_entities_per_chunk(cc, currOff, comps.subspan(0, entsGeneric), count, dataLimit))
418 if (!est_max_entities_per_chunk(cc, currOff, comps.subspan(entsGeneric), 1, dataLimit))
425 while (low <= high) {
426 uint32_t mid = (low + high) / 2;
441 constexpr uint32_t MinEntitiesPerChunk = 384;
442 uint32_t maxGenItemsInArchetype = 0;
445 if (archetypeId == 0) {
446 const uint32_t size2 = Chunk::chunk_data_bytes(mem_block_size(2));
447 maxGenItemsInArchetype =
448 (size2 - offs.firstByte_EntityData - uniCompsSize - 1) / (genCompsSize + (uint32_t)
sizeof(Entity));
449 maxGenItemsInArchetype = compute_max_entities_for_chunk(maxGenItemsInArchetype, size2);
450 if (maxGenItemsInArchetype > ChunkHeader::MAX_CHUNK_ENTITIES)
451 maxGenItemsInArchetype = ChunkHeader::MAX_CHUNK_ENTITIES;
455 const uint32_t size0 = Chunk::chunk_data_bytes(mem_block_size(0));
456 maxGenItemsInArchetype =
457 (size0 - offs.firstByte_EntityData - uniCompsSize - 1) / (genCompsSize + (uint32_t)
sizeof(Entity));
458 maxGenItemsInArchetype = compute_max_entities_for_chunk(maxGenItemsInArchetype, size0);
461 if (maxGenItemsInArchetype < MinEntitiesPerChunk) {
462 const uint32_t size1 = Chunk::chunk_data_bytes(mem_block_size(1));
463 maxGenItemsInArchetype =
464 (size1 - offs.firstByte_EntityData - uniCompsSize - 1) / (genCompsSize + (uint32_t)
sizeof(Entity));
465 maxGenItemsInArchetype = compute_max_entities_for_chunk(maxGenItemsInArchetype, size1);
469 if (maxGenItemsInArchetype < MinEntitiesPerChunk) {
470 const uint32_t size2 = Chunk::chunk_data_bytes(mem_block_size(2));
471 maxGenItemsInArchetype =
472 (size2 - offs.firstByte_EntityData - uniCompsSize - 1) / (genCompsSize + (uint32_t)
sizeof(Entity));
473 maxGenItemsInArchetype = compute_max_entities_for_chunk(maxGenItemsInArchetype, size2);
478 if (maxGenItemsInArchetype > ChunkHeader::MAX_CHUNK_ENTITIES)
479 maxGenItemsInArchetype = ChunkHeader::MAX_CHUNK_ENTITIES;
484 auto currOff = offs.firstByte_EntityData + ((uint32_t)
sizeof(Entity) * maxGenItemsInArchetype);
485 reg_components(*newArch, ids, comps, (uint8_t)0, (uint8_t)entsGeneric, currOff, maxGenItemsInArchetype);
486 reg_components(*newArch, ids, comps, (uint8_t)entsGeneric, (uint8_t)ids.size(), currOff, 1);
488 newArch->m_properties.capacity = (uint16_t)maxGenItemsInArchetype;
489 newArch->m_properties.chunkDataBytes = (ChunkDataOffset)currOff;
490 newArch->m_properties.genEntities = (uint8_t)entsGeneric;
495 void static destroy(Archetype* pArchetype) {
496 GAIA_ASSERT(pArchetype !=
nullptr);
497 pArchetype->~Archetype();
498 mem::AllocHelper::free(
"Archetype", pArchetype);
501 QueryMask queryMask()
const {
505 ArchetypeIdLookupKey::LookupHash id_hash()
const {
506 return m_archetypeIdHash;
512 m_hashLookup = hashLookup;
529 GAIA_ASSERT(!m_chunks.empty());
531 const auto chunkIndex = pChunk->
idx();
534 GAIA_ASSERT(chunkIndex == core::get_index(m_chunks, pChunk));
539 m_chunks.back()->set_idx(chunkIndex);
540 core::swap_erase(m_chunks, chunkIndex);
551 const auto chunkCnt = m_chunks.
size();
554 for (uint32_t i = m_firstFreeChunkIdx; i < m_chunks.size(); ++i) {
555 auto* pChunk = m_chunks[i];
556 GAIA_ASSERT(pChunk !=
nullptr);
557 const auto entityCnt = pChunk->size();
558 if (entityCnt < pChunk->capacity()) {
559 m_firstFreeChunkIdx = i;
566 GAIA_ASSERT(chunkCnt < UINT32_MAX);
569 auto* pChunk = Chunk::create(
570 m_world, m_cc, chunkCnt,
571 m_properties.capacity, m_properties.cntEntities,
572 m_properties.genEntities, m_properties.chunkDataBytes,
573 m_worldVersion, m_dataOffsets, m_ids, m_comps, m_compOffs);
575 m_firstFreeChunkIdx = m_chunks.size();
576 m_chunks.push_back(pChunk);
585 GAIA_ASSERT(!m_chunks.empty());
587 auto* pChunk = m_chunks[m_firstFreeChunkIdx];
588 if (pChunk->size() >= pChunk->capacity())
589 ++m_firstFreeChunkIdx;
597 GAIA_ASSERT(!m_chunks.empty());
599 if (chunkThatRemovedEntity.
idx() == m_firstFreeChunkIdx)
602 if (chunkThatRemovedEntity.
idx() < m_firstFreeChunkIdx) {
603 m_firstFreeChunkIdx = chunkThatRemovedEntity.
idx();
607 auto* pChunk = m_chunks[m_firstFreeChunkIdx];
608 if (pChunk->size() >= pChunk->capacity())
609 ++m_firstFreeChunkIdx;
618 try_update_free_chunk_idx(chunk);
626 remove_entity_raw(chunk, row, recs);
630 GAIA_NODISCARD
const Properties& props()
const {
638 GAIA_NODISCARD LookupHash lookup_hash()
const {
642 GAIA_NODISCARD EntitySpan ids_view()
const {
643 return {&m_ids[0], m_properties.cntEntities};
646 GAIA_NODISCARD ComponentSpan comps_view()
const {
647 return {&m_comps[0], m_properties.cntEntities};
650 GAIA_NODISCARD ChunkDataOffsetSpan comp_offs_view()
const {
651 return {&m_compOffs[0], m_properties.cntEntities};
656 GAIA_NODISCARD uint32_t
pairs()
const {
666 GAIA_NODISCARD
Entity entity_from_pairs_as_idx(uint32_t idx)
const {
667 const auto ids_idx = m_pairs_as_index_buffer[idx];
668 return m_ids[ids_idx];
675 return core::has_if(ids_view(), [&](
Entity e) {
683 template <
typename T>
684 GAIA_NODISCARD
bool has()
const {
686 const auto rel = m_cc.get<
typename T::rel>().entity;
687 const auto tgt = m_cc.get<
typename T::tgt>().entity;
690 const auto* pComp = m_cc.find<T>();
691 return pComp !=
nullptr && has(pComp->entity);
698 template <
bool Enabled>
701 for (
Chunk* pChunk: chunks()) {
706 if constexpr (Enabled) {
707 cnt = pChunk->size_enabled();
709 cnt = pChunk->size_disabled();
712 if (flatIndex < offset + cnt) {
713 if constexpr (Enabled) {
714 const auto idx = (uint32_t)(flatIndex - offset) + pChunk->size_disabled();
715 return pChunk->entity_view()[idx];
717 const auto idx = (uint32_t)(flatIndex - offset);
718 return pChunk->entity_view()[idx];
730 template <
bool Enabled>
731 const void*
getValue(uint32_t compIdx,
size_t flatIndex,
Entity& outEntity)
const {
733 for (
Chunk* pChunk: chunks()) {
738 if constexpr (Enabled) {
739 cnt = pChunk->size_enabled();
741 cnt = pChunk->size_disabled();
744 if (flatIndex < offset + cnt) {
745 if constexpr (Enabled) {
746 const auto idx = (uint32_t)(flatIndex - offset) + pChunk->size_disabled();
747 const auto* pData = pChunk->comp_ptr_mut(compIdx, idx);
748 outEntity = pChunk->entity_view()[idx];
751 const auto idx = (uint32_t)(flatIndex - offset);
752 const auto* pData = pChunk->comp_ptr_mut(compIdx, idx);
753 outEntity = pChunk->entity_view()[idx];
766 template <
bool Enabled>
771 Entity pivotEntity = getValue<Enabled>(high);
774 for (
size_t j = low; j < high; ++j) {
775 Entity jEntity = getValue<Enabled>(j);
776 if (func(m_world, &jEntity, &pivotEntity) < 0) {
778 Entity iEntity = getValue<Enabled>(i);
779 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), iEntity, jEntity);
786 Entity iEntity = getValue<Enabled>(i);
787 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), iEntity, pivotEntity);
791 sort_entities_inter<Enabled>(low, i - 1, func);
792 sort_entities_inter<Enabled>(i + 1, high, func);
796 template <
bool Enabled>
798 const ComponentCacheItem* pItem, uint32_t compIdx,
size_t low,
size_t high, TSortByFunc func) {
803 const void* pPivotData = getValue<Enabled>(compIdx, high, pivotEntity);
806 for (
size_t j = low; j < high; ++j) {
808 const void* jData = getValue<Enabled>(compIdx, j, jEntity);
809 if (func(m_world, jData, pPivotData) < 0) {
812 (void)getValue<Enabled>(compIdx, i, iEntity);
813 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), iEntity, jEntity);
821 (void)getValue<Enabled>(compIdx, i, iEntity);
822 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), iEntity, pivotEntity);
826 sort_entities_inter<Enabled>(pItem, compIdx, low, i - 1, func);
827 sort_entities_inter<Enabled>(pItem, compIdx, i + 1, high, func);
840 if (entity == EntityBad) {
842 uint32_t entities = 0;
843 for (
const auto* pChunk: m_chunks)
844 entities += pChunk->size_enabled();
846 sort_entities_inter<true>(0, entities - 1, func);
849 uint32_t entities = 0;
850 for (
const auto* pChunk: m_chunks)
851 entities += pChunk->size_disabled();
853 sort_entities_inter<false>(0, entities - 1, func);
856 const auto* pItem = m_cc.find(entity);
857 GAIA_ASSERT(pItem !=
nullptr &&
"Trying to sort by a component that has not been registered");
858 if (pItem ==
nullptr)
861 const auto compIdx = chunks()[0]->comp_idx(entity);
863 uint32_t entities = 0;
864 for (
const auto* pChunk: m_chunks)
865 entities += pChunk->size_enabled();
867 sort_entities_inter<true>(pItem, compIdx, 0, entities - 1, func);
870 uint32_t entities = 0;
871 for (
const auto* pChunk: m_chunks)
872 entities += pChunk->size_disabled();
874 sort_entities_inter<false>(pItem, compIdx, 0, entities - 1, func);
886 GAIA_ASSERT(pArchetypeRight !=
this);
888 m_graph.add_edge_right(entity, pArchetypeRight->id(), pArchetypeRight->id_hash());
889 pArchetypeRight->build_graph_edges_left(
this, entity);
892 void build_graph_edges_left(
Archetype* pArchetypeLeft,
Entity entity) {
894 GAIA_ASSERT(pArchetypeLeft !=
this);
896 m_graph.add_edge_left(entity, pArchetypeLeft->id(), pArchetypeLeft->id_hash());
899 void del_graph_edges(Archetype* pArchetypeRight, Entity entity) {
901 GAIA_ASSERT(pArchetypeRight !=
this);
903 m_graph.del_edge_right(entity);
904 pArchetypeRight->del_graph_edges_left(
this, entity);
907 void del_graph_edges_left([[maybe_unused]] Archetype* pArchetypeLeft, Entity entity) {
909 GAIA_ASSERT(pArchetypeLeft !=
this);
911 m_graph.del_edge_left(entity);
917 return m_graph.find_edge_right(entity);
923 return m_graph.find_edge_left(entity);
926 GAIA_NODISCARD
auto& right_edges() {
927 return m_graph.right_edges();
930 GAIA_NODISCARD
const auto& right_edges()
const {
931 return m_graph.right_edges();
934 GAIA_NODISCARD
auto& left_edges() {
935 return m_graph.left_edges();
938 GAIA_NODISCARD
const auto& left_edges()
const {
939 return m_graph.left_edges();
944 return m_chunks.empty();
961 GAIA_ASSERT(lifespan <= MAX_ARCHETYPE_LIFESPAN);
963 m_lifespanCountdownMax = lifespan;
969 return m_lifespanCountdownMax;
974 return m_lifespanCountdown > 0;
983 GAIA_NODISCARD
bool dead()
const {
989 GAIA_ASSERT(!dead());
990 m_lifespanCountdown = m_lifespanCountdownMax;
995 GAIA_ASSERT(!dead());
996 m_lifespanCountdown = 0;
1003 GAIA_ASSERT(dying());
1004 GAIA_ASSERT(m_lifespanCountdownMax > 0);
1005 --m_lifespanCountdown;
1011 return m_lifespanCountdownMax > 0 && !dying() && empty();
1014 static void diag_entity(
const World& world,
Entity entity) {
1015 if (entity.entity()) {
1017 " ent [%u:%u] %s [%s]", entity.id(), entity.gen(), entity_name(world, entity),
1018 EntityKindString[entity.kind()]);
1019 }
else if (entity.pair()) {
1021 " pair [%u:%u] %s -> %s", entity.id(), entity.gen(), entity_name(world, entity.id()),
1022 entity_name(world, entity.gen()));
1024 const auto& cc = comp_cache(world);
1025 const auto& desc = cc.get(entity);
1027 " hash:%016" PRIx64
", size:%3u B, align:%3u B, [%u:%u] %s [%s]", desc.hashLookup.hash,
1028 desc.comp.size(), desc.comp.alig(), desc.entity.id(), desc.entity.gen(), desc.name.str(),
1029 EntityKindString[entity.kind()]);
1033 static void diag_basic_info(
const World& world,
const Archetype& archetype) {
1034 auto ids = archetype.ids_view();
1035 auto comps = archetype.comps_view();
1038 uint32_t entCnt = 0;
1039 uint32_t entCntDisabled = 0;
1040 for (
const auto* chunk: archetype.m_chunks) {
1041 entCnt += chunk->size();
1042 entCntDisabled += chunk->size_disabled();
1046 uint32_t genCompsSize = 0;
1047 uint32_t uniCompsSize = 0;
1049 const auto& p = archetype.props();
1050 GAIA_FOR(p.genEntities) genCompsSize += comps[i].size();
1051 GAIA_FOR2(p.genEntities, comps.size()) uniCompsSize += comps[i].size();
1054 const auto chunkBytes = Chunk::chunk_total_bytes(archetype.props().chunkDataBytes);
1055 const auto sizeType = mem_block_size_type(chunkBytes);
1056 const auto allocSize = mem_block_size(sizeType) / 1024;
1060 "hash:%016" PRIx64 ", "
1061 "chunks:%u (%uK), data:%u/%u/%u B, "
1062 "entities:%u/%u/%u",
1063 archetype.
id(), archetype.lookup_hash().hash, (uint32_t)archetype.chunks().size(), allocSize, genCompsSize,
1064 uniCompsSize, archetype.props().chunkDataBytes, entCnt, entCntDisabled, archetype.props().capacity);
1067 GAIA_LOG_N(
" Components - count:%u", (uint32_t)ids.size());
1068 for (
const auto ent: ids)
1069 diag_entity(world, ent);
1073 static void diag_graph_info(
const World& world,
const Archetype& archetype) {
1074 archetype.m_graph.diag(world);
1077 static void diag_chunk_info(
const Archetype& archetype) {
1078 const auto& chunks = archetype.m_chunks;
1082 GAIA_LOG_N(
" Chunks");
1083 for (
const auto* pChunk: chunks)
1087 static void diag_entity_info(
const World& world,
const Archetype& archetype) {
1088 const auto& chunks = archetype.m_chunks;
1092 GAIA_LOG_N(
" Entities");
1093 bool noEntities =
true;
1094 for (
const auto* pChunk: chunks) {
1095 if (pChunk->empty())
1099 auto ev = pChunk->entity_view();
1100 for (
auto entity: ev)
1101 diag_entity(world, entity);
1111 diag_basic_info(world, archetype);
1112 diag_graph_info(world, archetype);
1113 diag_chunk_info(archetype);
1114 diag_entity_info(world, archetype);