/** * @file bootstrap.c * @brief Bootstrap entities in the flecs.core namespace. * * Before the ECS storage can be used, core entities such first need to be * initialized. For example, components in Flecs are stored as entities in the * ECS storage itself with an EcsComponent component, but before this component * can be stored, the component itself needs to be initialized. * * The bootstrap code uses lower-level APIs to initialize the data structures. * After bootstrap is completed, regular ECS operations can be used to create * entities and components. * * The bootstrap file also includes several lifecycle hooks and observers for * builtin features, such as relationship properties and hooks for keeping the * entity name administration in sync with the (Identifier, Name) component. */ #include "private_api.h" /* -- Identifier Component -- */ static ECS_DTOR(EcsIdentifier, ptr, { ecs_os_strset(&ptr->value, NULL); }) static ECS_COPY(EcsIdentifier, dst, src, { ecs_os_strset(&dst->value, src->value); dst->hash = src->hash; dst->length = src->length; dst->index_hash = src->index_hash; dst->index = src->index; }) static ECS_MOVE(EcsIdentifier, dst, src, { ecs_os_strset(&dst->value, NULL); dst->value = src->value; dst->hash = src->hash; dst->length = src->length; dst->index_hash = src->index_hash; dst->index = src->index; src->value = NULL; src->hash = 0; src->index_hash = 0; src->index = 0; src->length = 0; }) static void ecs_on_set(EcsIdentifier)(ecs_iter_t *it) { EcsIdentifier *ptr = ecs_field(it, EcsIdentifier, 1); ecs_world_t *world = it->real_world; ecs_entity_t evt = it->event; ecs_id_t evt_id = it->event_id; ecs_entity_t kind = ECS_PAIR_SECOND(evt_id); /* Name, Symbol, Alias */ ecs_id_t pair = ecs_childof(0); ecs_hashmap_t *index = NULL; if (kind == EcsSymbol) { index = &world->symbols; } else if (kind == EcsAlias) { index = &world->aliases; } else if (kind == EcsName) { ecs_assert(it->table != NULL, ECS_INTERNAL_ERROR, NULL); ecs_search(world, it->table, ecs_childof(EcsWildcard), &pair); ecs_assert(pair != 0, ECS_INTERNAL_ERROR, NULL); if (evt == EcsOnSet) { index = flecs_id_name_index_ensure(world, pair); } else { index = flecs_id_name_index_get(world, pair); } } int i, count = it->count; for (i = 0; i < count; i ++) { EcsIdentifier *cur = &ptr[i]; uint64_t hash; ecs_size_t len; const char *name = cur->value; if (cur->index && cur->index != index) { /* If index doesn't match up, the value must have been copied from * another entity, so reset index & cached index hash */ cur->index = NULL; cur->index_hash = 0; } if (cur->value && (evt == EcsOnSet)) { len = cur->length = ecs_os_strlen(name); hash = cur->hash = flecs_hash(name, len); } else { len = cur->length = 0; hash = cur->hash = 0; cur->index = NULL; } if (index) { uint64_t index_hash = cur->index_hash; ecs_entity_t e = it->entities[i]; if (hash != index_hash) { if (index_hash) { flecs_name_index_remove(index, e, index_hash); } if (hash) { flecs_name_index_ensure(index, e, name, len, hash); cur->index_hash = hash; cur->index = index; } } else { /* Name didn't change, but the string could have been * reallocated. Make sure name index points to correct string */ flecs_name_index_update_name(index, e, hash, name); } } } } /* -- Poly component -- */ static ECS_COPY(EcsPoly, dst, src, { (void)dst; (void)src; ecs_abort(ECS_INVALID_OPERATION, "poly component cannot be copied"); }) static ECS_MOVE(EcsPoly, dst, src, { if (dst->poly && (dst->poly != src->poly)) { ecs_poly_dtor_t *dtor = ecs_get_dtor(dst->poly); ecs_assert(dtor != NULL, ECS_INTERNAL_ERROR, NULL); dtor[0](dst->poly); } dst->poly = src->poly; src->poly = NULL; }) static ECS_DTOR(EcsPoly, ptr, { if (ptr->poly) { ecs_poly_dtor_t *dtor = ecs_get_dtor(ptr->poly); ecs_assert(dtor != NULL, ECS_INTERNAL_ERROR, NULL); dtor[0](ptr->poly); } }) /* -- Builtin triggers -- */ static void flecs_assert_relation_unused( ecs_world_t *world, ecs_entity_t rel, ecs_entity_t property) { if (world->flags & (EcsWorldInit|EcsWorldFini)) { return; } ecs_vec_t *marked_ids = &world->store.marked_ids; int32_t i, count = ecs_vec_count(marked_ids); for (i = 0; i < count; i ++) { ecs_marked_id_t *mid = ecs_vec_get_t(marked_ids, ecs_marked_id_t, i); if (mid->id == ecs_pair(rel, EcsWildcard)) { /* If id is being cleaned up, no need to throw error as tables will * be cleaned up */ return; } } bool in_use = ecs_id_in_use(world, ecs_pair(rel, EcsWildcard)); if (property != EcsUnion) { in_use |= ecs_id_in_use(world, rel); } if (in_use) { char *r_str = ecs_get_fullpath(world, rel); char *p_str = ecs_get_fullpath(world, property); ecs_throw(ECS_ID_IN_USE, "cannot change property '%s' for relationship '%s': already in use", p_str, r_str); ecs_os_free(r_str); ecs_os_free(p_str); } error: return; } static bool flecs_set_id_flag( ecs_id_record_t *idr, ecs_flags32_t flag) { if (!(idr->flags & flag)) { idr->flags |= flag; return true; } return false; } static bool flecs_unset_id_flag( ecs_id_record_t *idr, ecs_flags32_t flag) { if ((idr->flags & flag)) { idr->flags &= ~flag; return true; } return false; } static void flecs_register_id_flag_for_relation( ecs_iter_t *it, ecs_entity_t prop, ecs_flags32_t flag, ecs_flags32_t not_flag, ecs_flags32_t entity_flag) { ecs_world_t *world = it->world; ecs_entity_t event = it->event; int i, count = it->count; for (i = 0; i < count; i ++) { ecs_entity_t e = it->entities[i]; bool changed = false; if (event == EcsOnAdd) { ecs_id_record_t *idr = flecs_id_record_ensure(world, e); changed |= flecs_set_id_flag(idr, flag); idr = flecs_id_record_ensure(world, ecs_pair(e, EcsWildcard)); do { changed |= flecs_set_id_flag(idr, flag); } while ((idr = idr->first.next)); if (entity_flag) flecs_add_flag(world, e, entity_flag); } else if (event == EcsOnRemove) { ecs_id_record_t *idr = flecs_id_record_get(world, e); if (idr) changed |= flecs_unset_id_flag(idr, not_flag); idr = flecs_id_record_get(world, ecs_pair(e, EcsWildcard)); if (idr) { do { changed |= flecs_unset_id_flag(idr, not_flag); } while ((idr = idr->first.next)); } } if (changed) { flecs_assert_relation_unused(world, e, prop); } } } static void flecs_register_final(ecs_iter_t *it) { ecs_world_t *world = it->world; int i, count = it->count; for (i = 0; i < count; i ++) { ecs_entity_t e = it->entities[i]; if (flecs_id_record_get(world, ecs_pair(EcsIsA, e)) != NULL) { char *e_str = ecs_get_fullpath(world, e); ecs_throw(ECS_ID_IN_USE, "cannot change property 'Final' for '%s': already inherited from", e_str); ecs_os_free(e_str); error: continue; } } } static void flecs_register_on_delete(ecs_iter_t *it) { ecs_id_t id = ecs_field_id(it, 1); flecs_register_id_flag_for_relation(it, EcsOnDelete, ECS_ID_ON_DELETE_FLAG(ECS_PAIR_SECOND(id)), EcsIdOnDeleteMask, EcsEntityIsId); } static void flecs_register_on_delete_object(ecs_iter_t *it) { ecs_id_t id = ecs_field_id(it, 1); flecs_register_id_flag_for_relation(it, EcsOnDeleteTarget, ECS_ID_ON_DELETE_TARGET_FLAG(ECS_PAIR_SECOND(id)), EcsIdOnDeleteObjectMask, EcsEntityIsId); } static void flecs_register_traversable(ecs_iter_t *it) { flecs_register_id_flag_for_relation(it, EcsAcyclic, EcsIdTraversable, EcsIdTraversable, 0); } static void flecs_register_tag(ecs_iter_t *it) { flecs_register_id_flag_for_relation(it, EcsTag, EcsIdTag, ~EcsIdTag, 0); /* Ensure that all id records for tag have type info set to NULL */ ecs_world_t *world = it->real_world; int i, count = it->count; for (i = 0; i < count; i ++) { ecs_entity_t e = it->entities[i]; if (it->event == EcsOnAdd) { ecs_id_record_t *idr = flecs_id_record_get(world, ecs_pair(e, EcsWildcard)); ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); do { if (idr->type_info != NULL) { flecs_assert_relation_unused(world, e, EcsTag); } idr->type_info = NULL; } while ((idr = idr->first.next)); } } } static void flecs_register_exclusive(ecs_iter_t *it) { flecs_register_id_flag_for_relation(it, EcsExclusive, EcsIdExclusive, EcsIdExclusive, 0); } static void flecs_register_dont_inherit(ecs_iter_t *it) { flecs_register_id_flag_for_relation(it, EcsDontInherit, EcsIdDontInherit, EcsIdDontInherit, 0); } static void flecs_register_always_override(ecs_iter_t *it) { flecs_register_id_flag_for_relation(it, EcsAlwaysOverride, EcsIdAlwaysOverride, EcsIdAlwaysOverride, 0); } static void flecs_register_with(ecs_iter_t *it) { flecs_register_id_flag_for_relation(it, EcsWith, EcsIdWith, 0, 0); } static void flecs_register_union(ecs_iter_t *it) { flecs_register_id_flag_for_relation(it, EcsUnion, EcsIdUnion, 0, 0); } static void flecs_register_slot_of(ecs_iter_t *it) { int i, count = it->count; for (i = 0; i < count; i ++) { ecs_add_id(it->world, it->entities[i], EcsUnion); } } static void flecs_on_symmetric_add_remove(ecs_iter_t *it) { ecs_entity_t pair = ecs_field_id(it, 1); if (!ECS_HAS_ID_FLAG(pair, PAIR)) { /* If relationship was not added as a pair, there's nothing to do */ return; } ecs_world_t *world = it->world; ecs_entity_t rel = ECS_PAIR_FIRST(pair); ecs_entity_t obj = ecs_pair_second(world, pair); ecs_entity_t event = it->event; int i, count = it->count; for (i = 0; i < count; i ++) { ecs_entity_t subj = it->entities[i]; if (event == EcsOnAdd) { if (!ecs_has_id(it->real_world, obj, ecs_pair(rel, subj))) { ecs_add_pair(it->world, obj, rel, subj); } } else { if (ecs_has_id(it->real_world, obj, ecs_pair(rel, subj))) { ecs_remove_pair(it->world, obj, rel, subj); } } } } static void flecs_register_symmetric(ecs_iter_t *it) { ecs_world_t *world = it->real_world; int i, count = it->count; for (i = 0; i < count; i ++) { ecs_entity_t r = it->entities[i]; flecs_assert_relation_unused(world, r, EcsSymmetric); /* Create observer that adds the reverse relationship when R(X, Y) is * added, or remove the reverse relationship when R(X, Y) is removed. */ ecs_observer(world, { .entity = ecs_entity(world, {.add = {ecs_childof(r)}}), .filter.terms[0] = { .id = ecs_pair(r, EcsWildcard) }, .callback = flecs_on_symmetric_add_remove, .events = {EcsOnAdd, EcsOnRemove} }); } } static void flecs_on_component(ecs_iter_t *it) { ecs_world_t *world = it->world; EcsComponent *c = ecs_field(it, EcsComponent, 1); int i, count = it->count; for (i = 0; i < count; i ++) { ecs_entity_t e = it->entities[i]; uint32_t component_id = (uint32_t)e; /* Strip generation */ ecs_assert(component_id < ECS_MAX_COMPONENT_ID, ECS_OUT_OF_RANGE, "component id must be smaller than %u", ECS_MAX_COMPONENT_ID); (void)component_id; if (it->event == EcsOnSet) { if (flecs_type_info_init_id( world, e, c[i].size, c[i].alignment, NULL)) { flecs_assert_relation_unused(world, e, ecs_id(EcsComponent)); } } else if (it->event == EcsOnRemove) { flecs_type_info_free(world, e); } } } static void flecs_ensure_module_tag(ecs_iter_t *it) { ecs_world_t *world = it->world; int i, count = it->count; for (i = 0; i < count; i ++) { ecs_entity_t e = it->entities[i]; ecs_entity_t parent = ecs_get_target(world, e, EcsChildOf, 0); if (parent) { ecs_add_id(world, parent, EcsModule); } } } /* -- Iterable mixins -- */ static void flecs_on_event_iterable_init( const ecs_world_t *world, const ecs_poly_t *poly, /* Observable */ ecs_iter_t *it, ecs_term_t *filter) { ecs_iter_poly(world, poly, it, filter); it->event_id = filter->id; } /* -- Bootstrapping -- */ #define flecs_bootstrap_builtin_t(world, table, name)\ flecs_bootstrap_builtin(world, table, ecs_id(name), #name, sizeof(name),\ ECS_ALIGNOF(name)) static void flecs_bootstrap_builtin( ecs_world_t *world, ecs_table_t *table, ecs_entity_t entity, const char *symbol, ecs_size_t size, ecs_size_t alignment) { ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); ecs_column_t *columns = table->data.columns; ecs_assert(columns != NULL, ECS_INTERNAL_ERROR, NULL); ecs_record_t *record = flecs_entities_ensure(world, entity); record->table = table; int32_t index = flecs_table_append(world, table, entity, false, false); record->row = ECS_ROW_TO_RECORD(index, 0); EcsComponent *component = ecs_vec_first(&columns[0].data); component[index].size = size; component[index].alignment = alignment; const char *name = &symbol[3]; /* Strip 'Ecs' */ ecs_size_t symbol_length = ecs_os_strlen(symbol); ecs_size_t name_length = symbol_length - 3; EcsIdentifier *name_col = ecs_vec_first(&columns[1].data); uint64_t name_hash = flecs_hash(name, name_length); name_col[index].value = ecs_os_strdup(name); name_col[index].length = name_length; name_col[index].hash = name_hash; name_col[index].index_hash = 0; name_col[index].index = table->_->name_index; flecs_name_index_ensure( table->_->name_index, entity, name, name_length, name_hash); EcsIdentifier *symbol_col = ecs_vec_first(&columns[2].data); symbol_col[index].value = ecs_os_strdup(symbol); symbol_col[index].length = symbol_length; symbol_col[index].hash = flecs_hash(symbol, symbol_length); symbol_col[index].index_hash = 0; symbol_col[index].index = NULL; } /** Initialize component table. This table is manually constructed to bootstrap * flecs. After this function has been called, the builtin components can be * created. * The reason this table is constructed manually is because it requires the size * and alignment of the EcsComponent and EcsIdentifier components, which haven't * been created yet */ static ecs_table_t* flecs_bootstrap_component_table( ecs_world_t *world) { /* Before creating table, manually set flags for ChildOf/Identifier, as this * can no longer be done after they are in use. */ ecs_id_record_t *idr = flecs_id_record_ensure(world, EcsChildOf); idr->flags |= EcsIdOnDeleteObjectDelete | EcsIdDontInherit | EcsIdTraversable | EcsIdTag; /* Initialize id records cached on world */ world->idr_childof_wildcard = flecs_id_record_ensure(world, ecs_pair(EcsChildOf, EcsWildcard)); world->idr_childof_wildcard->flags |= EcsIdOnDeleteObjectDelete | EcsIdDontInherit | EcsIdTraversable | EcsIdTag | EcsIdExclusive; idr = flecs_id_record_ensure(world, ecs_pair_t(EcsIdentifier, EcsWildcard)); idr->flags |= EcsIdDontInherit; world->idr_identifier_name = flecs_id_record_ensure(world, ecs_pair_t(EcsIdentifier, EcsName)); world->idr_childof_0 = flecs_id_record_ensure(world, ecs_pair(EcsChildOf, 0)); ecs_id_t ids[] = { ecs_id(EcsComponent), EcsFinal, ecs_pair_t(EcsIdentifier, EcsName), ecs_pair_t(EcsIdentifier, EcsSymbol), ecs_pair(EcsChildOf, EcsFlecsCore), ecs_pair(EcsOnDelete, EcsPanic) }; ecs_type_t array = { .array = ids, .count = 6 }; ecs_table_t *result = flecs_table_find_or_create(world, &array); ecs_data_t *data = &result->data; /* Preallocate enough memory for initial components */ ecs_allocator_t *a = &world->allocator; ecs_vec_init_t(a, &data->entities, ecs_entity_t, EcsFirstUserComponentId); ecs_vec_init_t(a, &data->columns[0].data, EcsComponent, EcsFirstUserComponentId); ecs_vec_init_t(a, &data->columns[1].data, EcsIdentifier, EcsFirstUserComponentId); ecs_vec_init_t(a, &data->columns[2].data, EcsIdentifier, EcsFirstUserComponentId); return result; } static void flecs_bootstrap_entity( ecs_world_t *world, ecs_entity_t id, const char *name, ecs_entity_t parent) { char symbol[256]; ecs_os_strcpy(symbol, "flecs.core."); ecs_os_strcat(symbol, name); ecs_ensure(world, id); ecs_add_pair(world, id, EcsChildOf, parent); ecs_set_name(world, id, name); ecs_set_symbol(world, id, symbol); ecs_assert(ecs_get_name(world, id) != NULL, ECS_INTERNAL_ERROR, NULL); if (!parent || parent == EcsFlecsCore) { ecs_assert(ecs_lookup_fullpath(world, name) == id, ECS_INTERNAL_ERROR, NULL); } } void flecs_bootstrap( ecs_world_t *world) { ecs_log_push(); ecs_set_name_prefix(world, "Ecs"); /* Ensure builtin ids are alive */ ecs_ensure(world, ecs_id(EcsComponent)); ecs_ensure(world, EcsFinal); ecs_ensure(world, ecs_id(EcsIdentifier)); ecs_ensure(world, EcsName); ecs_ensure(world, EcsSymbol); ecs_ensure(world, EcsAlias); ecs_ensure(world, EcsChildOf); ecs_ensure(world, EcsFlecs); ecs_ensure(world, EcsFlecsCore); ecs_ensure(world, EcsOnAdd); ecs_ensure(world, EcsOnRemove); ecs_ensure(world, EcsOnSet); ecs_ensure(world, EcsUnSet); ecs_ensure(world, EcsOnDelete); ecs_ensure(world, EcsPanic); ecs_ensure(world, EcsFlag); ecs_ensure(world, EcsIsA); ecs_ensure(world, EcsWildcard); ecs_ensure(world, EcsAny); ecs_ensure(world, EcsTag); /* Register type information for builtin components */ flecs_type_info_init(world, EcsComponent, { .ctor = ecs_default_ctor, .on_set = flecs_on_component, .on_remove = flecs_on_component }); flecs_type_info_init(world, EcsIdentifier, { .ctor = ecs_default_ctor, .dtor = ecs_dtor(EcsIdentifier), .copy = ecs_copy(EcsIdentifier), .move = ecs_move(EcsIdentifier), .on_set = ecs_on_set(EcsIdentifier), .on_remove = ecs_on_set(EcsIdentifier) }); flecs_type_info_init(world, EcsPoly, { .ctor = ecs_default_ctor, .copy = ecs_copy(EcsPoly), .move = ecs_move(EcsPoly), .dtor = ecs_dtor(EcsPoly) }); flecs_type_info_init(world, EcsIterable, { 0 }); flecs_type_info_init(world, EcsTarget, { 0 }); /* Create and cache often used id records on world */ flecs_init_id_records(world); /* Create table for builtin components. This table temporarily stores the * entities associated with builtin components, until they get moved to * other tables once properties are added (see below) */ ecs_table_t *table = flecs_bootstrap_component_table(world); assert(table != NULL); /* Bootstrap builtin components */ flecs_bootstrap_builtin_t(world, table, EcsIdentifier); flecs_bootstrap_builtin_t(world, table, EcsComponent); flecs_bootstrap_builtin_t(world, table, EcsIterable); flecs_bootstrap_builtin_t(world, table, EcsPoly); flecs_bootstrap_builtin_t(world, table, EcsTarget); /* Initialize default entity id range */ world->info.last_component_id = EcsFirstUserComponentId; flecs_entities_max_id(world) = EcsFirstUserEntityId; world->info.min_id = 0; world->info.max_id = 0; /* Make EcsOnAdd, EcsOnSet events iterable to enable .yield_existing */ ecs_set(world, EcsOnAdd, EcsIterable, { .init = flecs_on_event_iterable_init }); ecs_set(world, EcsOnSet, EcsIterable, { .init = flecs_on_event_iterable_init }); /* Register observer for tag property before adding EcsTag */ ecs_observer(world, { .entity = ecs_entity(world, {.add = { ecs_childof(EcsFlecsInternals)}}), .filter.terms[0] = { .id = EcsTag, .src.flags = EcsSelf }, .events = {EcsOnAdd, EcsOnRemove}, .callback = flecs_register_tag, .yield_existing = true }); /* Populate core module */ ecs_set_scope(world, EcsFlecsCore); flecs_bootstrap_tag(world, EcsName); flecs_bootstrap_tag(world, EcsSymbol); flecs_bootstrap_tag(world, EcsAlias); flecs_bootstrap_tag(world, EcsQuery); flecs_bootstrap_tag(world, EcsObserver); flecs_bootstrap_tag(world, EcsModule); flecs_bootstrap_tag(world, EcsPrivate); flecs_bootstrap_tag(world, EcsPrefab); flecs_bootstrap_tag(world, EcsSlotOf); flecs_bootstrap_tag(world, EcsDisabled); flecs_bootstrap_tag(world, EcsEmpty); /* Initialize builtin modules */ ecs_set_name(world, EcsFlecs, "flecs"); ecs_add_id(world, EcsFlecs, EcsModule); ecs_add_pair(world, EcsFlecs, EcsOnDelete, EcsPanic); ecs_add_pair(world, EcsFlecsCore, EcsChildOf, EcsFlecs); ecs_set_name(world, EcsFlecsCore, "core"); ecs_add_id(world, EcsFlecsCore, EcsModule); ecs_add_pair(world, EcsFlecsInternals, EcsChildOf, EcsFlecsCore); ecs_set_name(world, EcsFlecsInternals, "internals"); ecs_add_id(world, EcsFlecsInternals, EcsModule); /* Self check */ ecs_record_t *r = flecs_entities_get(world, EcsFlecs); ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(r->table != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(r->row & EcsEntityIsTraversable, ECS_INTERNAL_ERROR, NULL); (void)r; /* Initialize builtin entities */ flecs_bootstrap_entity(world, EcsWorld, "World", EcsFlecsCore); flecs_bootstrap_entity(world, EcsWildcard, "*", EcsFlecsCore); flecs_bootstrap_entity(world, EcsAny, "_", EcsFlecsCore); flecs_bootstrap_entity(world, EcsThis, "this", EcsFlecsCore); flecs_bootstrap_entity(world, EcsVariable, "$", EcsFlecsCore); flecs_bootstrap_entity(world, EcsFlag, "Flag", EcsFlecsCore); /* Component/relationship properties */ flecs_bootstrap_tag(world, EcsTransitive); flecs_bootstrap_tag(world, EcsReflexive); flecs_bootstrap_tag(world, EcsSymmetric); flecs_bootstrap_tag(world, EcsFinal); flecs_bootstrap_tag(world, EcsDontInherit); flecs_bootstrap_tag(world, EcsAlwaysOverride); flecs_bootstrap_tag(world, EcsTag); flecs_bootstrap_tag(world, EcsUnion); flecs_bootstrap_tag(world, EcsExclusive); flecs_bootstrap_tag(world, EcsAcyclic); flecs_bootstrap_tag(world, EcsTraversable); flecs_bootstrap_tag(world, EcsWith); flecs_bootstrap_tag(world, EcsOneOf); flecs_bootstrap_tag(world, EcsOnDelete); flecs_bootstrap_tag(world, EcsOnDeleteTarget); flecs_bootstrap_tag(world, EcsRemove); flecs_bootstrap_tag(world, EcsDelete); flecs_bootstrap_tag(world, EcsPanic); flecs_bootstrap_tag(world, EcsFlatten); flecs_bootstrap_tag(world, EcsDefaultChildComponent); /* Builtin predicates */ flecs_bootstrap_tag(world, EcsPredEq); flecs_bootstrap_tag(world, EcsPredMatch); flecs_bootstrap_tag(world, EcsPredLookup); flecs_bootstrap_tag(world, EcsScopeOpen); flecs_bootstrap_tag(world, EcsScopeClose); /* Builtin relationships */ flecs_bootstrap_tag(world, EcsIsA); flecs_bootstrap_tag(world, EcsChildOf); flecs_bootstrap_tag(world, EcsDependsOn); /* Builtin events */ flecs_bootstrap_entity(world, EcsOnAdd, "OnAdd", EcsFlecsCore); flecs_bootstrap_entity(world, EcsOnRemove, "OnRemove", EcsFlecsCore); flecs_bootstrap_entity(world, EcsOnSet, "OnSet", EcsFlecsCore); flecs_bootstrap_entity(world, EcsUnSet, "UnSet", EcsFlecsCore); flecs_bootstrap_entity(world, EcsMonitor, "EcsMonitor", EcsFlecsCore); flecs_bootstrap_entity(world, EcsOnTableCreate, "OnTableCreate", EcsFlecsCore); flecs_bootstrap_entity(world, EcsOnTableDelete, "OnTableDelete", EcsFlecsCore); flecs_bootstrap_entity(world, EcsOnTableEmpty, "OnTableEmpty", EcsFlecsCore); flecs_bootstrap_entity(world, EcsOnTableFill, "OnTableFilled", EcsFlecsCore); /* Tag relationships (relationships that should never have data) */ ecs_add_id(world, EcsIsA, EcsTag); ecs_add_id(world, EcsChildOf, EcsTag); ecs_add_id(world, EcsSlotOf, EcsTag); ecs_add_id(world, EcsDependsOn, EcsTag); ecs_add_id(world, EcsFlatten, EcsTag); ecs_add_id(world, EcsDefaultChildComponent, EcsTag); ecs_add_id(world, EcsUnion, EcsTag); ecs_add_id(world, EcsFlag, EcsTag); ecs_add_id(world, EcsWith, EcsTag); /* Exclusive properties */ ecs_add_id(world, EcsChildOf, EcsExclusive); ecs_add_id(world, EcsOnDelete, EcsExclusive); ecs_add_id(world, EcsOnDeleteTarget, EcsExclusive); ecs_add_id(world, EcsDefaultChildComponent, EcsExclusive); /* Sync properties of ChildOf and Identifier with bootstrapped flags */ ecs_add_pair(world, EcsChildOf, EcsOnDeleteTarget, EcsDelete); ecs_add_id(world, EcsChildOf, EcsAcyclic); ecs_add_id(world, EcsChildOf, EcsTraversable); ecs_add_id(world, EcsChildOf, EcsDontInherit); ecs_add_id(world, ecs_id(EcsIdentifier), EcsDontInherit); /* Create triggers in internals scope */ ecs_set_scope(world, EcsFlecsInternals); /* Term used to also match prefabs */ ecs_term_t match_prefab = { .id = EcsPrefab, .oper = EcsOptional, .src.flags = EcsSelf }; /* Register observers for components/relationship properties. Most observers * set flags on an id record when a property is added to a component, which * allows for quick property testing in various operations. */ ecs_observer(world, { .filter.terms = {{ .id = EcsFinal, .src.flags = EcsSelf }, match_prefab }, .events = {EcsOnAdd}, .callback = flecs_register_final }); ecs_observer(world, { .filter.terms = { { .id = ecs_pair(EcsOnDelete, EcsWildcard), .src.flags = EcsSelf }, match_prefab }, .events = {EcsOnAdd, EcsOnRemove}, .callback = flecs_register_on_delete }); ecs_observer(world, { .filter.terms = { { .id = ecs_pair(EcsOnDeleteTarget, EcsWildcard), .src.flags = EcsSelf }, match_prefab }, .events = {EcsOnAdd, EcsOnRemove}, .callback = flecs_register_on_delete_object }); ecs_observer(world, { .filter.terms = { { .id = EcsTraversable, .src.flags = EcsSelf }, match_prefab }, .events = {EcsOnAdd, EcsOnRemove}, .callback = flecs_register_traversable }); ecs_observer(world, { .filter.terms = {{ .id = EcsExclusive, .src.flags = EcsSelf }, match_prefab }, .events = {EcsOnAdd, EcsOnRemove}, .callback = flecs_register_exclusive }); ecs_observer(world, { .filter.terms = {{ .id = EcsSymmetric, .src.flags = EcsSelf }, match_prefab }, .events = {EcsOnAdd}, .callback = flecs_register_symmetric }); ecs_observer(world, { .filter.terms = {{ .id = EcsDontInherit, .src.flags = EcsSelf }, match_prefab }, .events = {EcsOnAdd}, .callback = flecs_register_dont_inherit }); ecs_observer(world, { .filter.terms = {{ .id = EcsAlwaysOverride, .src.flags = EcsSelf } }, .events = {EcsOnAdd}, .callback = flecs_register_always_override }); ecs_observer(world, { .filter.terms = { { .id = ecs_pair(EcsWith, EcsWildcard), .src.flags = EcsSelf }, match_prefab }, .events = {EcsOnAdd}, .callback = flecs_register_with }); ecs_observer(world, { .filter.terms = {{ .id = EcsUnion, .src.flags = EcsSelf }, match_prefab }, .events = {EcsOnAdd}, .callback = flecs_register_union }); /* Entities used as slot are marked as exclusive to ensure a slot can always * only point to a single entity. */ ecs_observer(world, { .filter.terms = { { .id = ecs_pair(EcsSlotOf, EcsWildcard), .src.flags = EcsSelf }, match_prefab }, .events = {EcsOnAdd}, .callback = flecs_register_slot_of }); /* Define observer to make sure that adding a module to a child entity also * adds it to the parent. */ ecs_observer(world, { .filter.terms = {{ .id = EcsModule, .src.flags = EcsSelf }, match_prefab}, .events = {EcsOnAdd}, .callback = flecs_ensure_module_tag }); /* Set scope back to flecs core */ ecs_set_scope(world, EcsFlecsCore); /* Traversable relationships are always acyclic */ ecs_add_pair(world, EcsTraversable, EcsWith, EcsAcyclic); /* Transitive relationships are always Traversable */ ecs_add_pair(world, EcsTransitive, EcsWith, EcsTraversable); /* DontInherit components */ ecs_add_id(world, EcsPrefab, EcsDontInherit); /* Acyclic/Traversable components */ ecs_add_id(world, EcsIsA, EcsTraversable); ecs_add_id(world, EcsDependsOn, EcsTraversable); ecs_add_id(world, EcsWith, EcsAcyclic); /* Transitive relationships */ ecs_add_id(world, EcsIsA, EcsTransitive); ecs_add_id(world, EcsIsA, EcsReflexive); /* Exclusive properties */ ecs_add_id(world, EcsSlotOf, EcsExclusive); ecs_add_id(world, EcsOneOf, EcsExclusive); ecs_add_id(world, EcsFlatten, EcsExclusive); /* Private properties */ ecs_add_id(world, ecs_id(EcsPoly), EcsPrivate); ecs_add_id(world, ecs_id(EcsIdentifier), EcsPrivate); ecs_add_id(world, EcsChildOf, EcsPrivate); ecs_add_id(world, EcsIsA, EcsPrivate); /* Run bootstrap functions for other parts of the code */ flecs_bootstrap_hierarchy(world); ecs_set_scope(world, 0); ecs_set_name_prefix(world, NULL); ecs_log_pop(); }