/** * @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; }) /* -- 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)) { flecs_poly_dtor_t *dtor = flecs_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) { flecs_poly_dtor_t *dtor = flecs_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 trait) { 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)); in_use |= ecs_id_in_use(world, rel); if (in_use) { char *r_str = ecs_get_path(world, rel); char *p_str = ecs_get_path(world, trait); ecs_throw(ECS_INVALID_OPERATION, "cannot change trait '%s' for '%s': component is 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_world_t *world, ecs_component_record_t *cr, ecs_flags32_t flag, ecs_entity_t trait) { (void)trait; if (!(cr->flags & flag)) { cr->flags |= flag; if (flag == EcsIdSparse) { flecs_component_init_sparse(world, cr); } if (flag == EcsIdDontFragment) { flecs_component_record_init_dont_fragment(world, cr); } if (flag == EcsIdExclusive) { flecs_component_record_init_exclusive(world, cr); } return true; } return false; } static bool flecs_unset_id_flag( ecs_component_record_t *cr, ecs_flags32_t flag) { if (cr->flags & EcsIdMarkedForDelete) { /* Don't change flags for record that's about to be deleted */ return false; } if ((cr->flags & flag)) { cr->flags &= ~flag; return true; } return false; } typedef struct ecs_on_trait_ctx_t { ecs_flags32_t flag, not_flag; } ecs_on_trait_ctx_t; static bool flecs_trait_can_add_after_query( ecs_entity_t trait) { if (trait == EcsWith) { return true; } return false; } static void flecs_register_flag_for_trait( ecs_iter_t *it, ecs_entity_t trait, 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) { if (flag == EcsIdOnInstantiateInherit) { if (e < FLECS_HI_COMPONENT_ID) { world->non_trivial_lookup[e] |= EcsNonTrivialIdInherit; } } if (!(world->flags & EcsWorldInit) && !flecs_trait_can_add_after_query(trait)) { ecs_check(!flecs_component_is_trait_locked(world, e), ECS_INVALID_OPERATION, "cannot set '%s' trait for component '%s' because it is already" " queried for (apply traits before creating queries)", flecs_errstr(ecs_get_path(world, trait)), flecs_errstr_1(ecs_id_str(world, e))); } ecs_component_record_t *cr = flecs_components_get(world, e); if (cr) { changed |= flecs_set_id_flag(world, cr, flag, trait); } cr = flecs_components_get(world, ecs_pair(e, EcsWildcard)); if (cr) { do { changed |= flecs_set_id_flag(world, cr, flag, trait); } while ((cr = flecs_component_first_next(cr))); } if (entity_flag) flecs_add_flag(world, e, entity_flag); } else if (event == EcsOnRemove) { ecs_component_record_t *cr = flecs_components_get(world, e); if (cr) changed |= flecs_unset_id_flag(cr, not_flag); cr = flecs_components_get(world, ecs_pair(e, EcsWildcard)); if (cr) { do { changed |= flecs_unset_id_flag(cr, not_flag); } while ((cr = flecs_component_first_next(cr))); } } if (changed) { flecs_assert_relation_unused(world, e, trait); } } error: return; } 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_components_get(world, ecs_pair(EcsIsA, e)) != NULL) { ecs_throw(ECS_INVALID_OPERATION, "cannot change trait 'Final' for '%s': already inherited from", flecs_errstr(ecs_get_path(world, e))); } ecs_check(!flecs_component_is_trait_locked(world, e), ECS_INVALID_OPERATION, "cannot change " "trait 'Final' for '%s': already queried for (apply traits " "before creating queries)", flecs_errstr(ecs_get_path(world, e))); error: continue; } } static void flecs_register_tag(ecs_iter_t *it) { flecs_register_flag_for_trait(it, EcsPairIsTag, EcsIdPairIsTag, EcsIdPairIsTag, 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_component_record_t *cr = flecs_components_get(world, ecs_pair(e, EcsWildcard)); if (cr) { do { if (cr->type_info != NULL) { flecs_assert_relation_unused(world, e, EcsPairIsTag); } cr->type_info = NULL; } while ((cr = flecs_component_first_next(cr))); } } } } static void flecs_register_on_delete(ecs_iter_t *it) { ecs_id_t id = ecs_field_id(it, 0); flecs_register_flag_for_trait(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, 0); flecs_register_flag_for_trait(it, EcsOnDeleteTarget, ECS_ID_ON_DELETE_TARGET_FLAG(ECS_PAIR_SECOND(id)), EcsIdOnDeleteTargetMask, EcsEntityIsId); } static void flecs_register_on_instantiate(ecs_iter_t *it) { ecs_id_t id = ecs_field_id(it, 0); flecs_register_flag_for_trait(it, EcsOnInstantiate, ECS_ID_ON_INSTANTIATE_FLAG(ECS_PAIR_SECOND(id)), 0, 0); } static void flecs_register_trait(ecs_iter_t *it) { ecs_on_trait_ctx_t *ctx = it->ctx; flecs_register_flag_for_trait( it, it->ids[0], ctx->flag, ctx->not_flag, 0); } static void flecs_register_trait_pair(ecs_iter_t *it) { ecs_on_trait_ctx_t *ctx = it->ctx; flecs_register_flag_for_trait( it, ecs_pair_first(it->world, it->ids[0]), ctx->flag, ctx->not_flag, 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], EcsDontFragment); ecs_add_id(it->world, it->entities[i], EcsExclusive); } } static void flecs_on_symmetric_add_remove(ecs_iter_t *it) { ecs_entity_t pair = ecs_field_id(it, 0); 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 tgt = ecs_pair_second(world, pair); ecs_entity_t event = it->event; if (tgt) { 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, tgt, ecs_pair(rel, subj))) { ecs_add_pair(it->world, tgt, rel, subj); } } else { if (ecs_has_id(it->real_world, tgt, ecs_pair(rel, subj))) { ecs_remove_pair(it->world, tgt, 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, { .parent = r }), .query.terms[0] = { .id = ecs_pair(r, EcsWildcard) }, .callback = flecs_on_symmetric_add_remove, .events = {EcsOnAdd, EcsOnRemove} }); } } #ifdef FLECS_DEBUG static void flecs_on_singleton_add_remove(ecs_iter_t *it) { ecs_entity_t component = ecs_field_id(it, 0); ecs_world_t *world = it->real_world; (void)world; int i, count = it->count; for (i = 0; i < count; i ++) { ecs_entity_t e = it->entities[i]; if (ECS_IS_PAIR(component)) { ecs_entity_t relationship = ECS_PAIR_FIRST(component); e = (uint32_t)e; ecs_check(relationship == e, ECS_CONSTRAINT_VIOLATED, "cannot add singleton pair '%s' to entity '%s': singleton " "component '%s' must be added to itself", flecs_errstr(ecs_id_str(world, component)), flecs_errstr_1(ecs_get_path(world, it->entities[i])), flecs_errstr_2(ecs_get_path(it->world, relationship))); (void)relationship; } else { ecs_check(component == e, ECS_CONSTRAINT_VIOLATED, "cannot add singleton component '%s' to entity '%s': singleton" " component must be added to itself", flecs_errstr(ecs_get_path(it->world, component)), flecs_errstr_1(ecs_get_path(it->world, it->entities[i]))); } error: continue; } } #endif static void flecs_register_singleton(ecs_iter_t *it) { ecs_world_t *world = it->real_world; (void)world; flecs_register_flag_for_trait(it, EcsSingleton, EcsIdSingleton, 0, 0); int i, count = it->count; for (i = 0; i < count; i ++) { ecs_entity_t component = it->entities[i]; (void)component; /* Create observer that enforces that singleton is only added to self */ #ifdef FLECS_DEBUG ecs_observer(world, { .entity = ecs_entity(world, { .parent = component }), .query.terms[0] = { .id = component, .src.id = EcsThis|EcsSelf }, .callback = flecs_on_singleton_add_remove, .events = {EcsOnAdd} }); ecs_observer(world, { .entity = ecs_entity(world, { .parent = component }), .query.terms[0] = { .id = ecs_pair(component, EcsWildcard), .src.id = EcsThis|EcsSelf }, .callback = flecs_on_singleton_add_remove, .events = {EcsOnAdd} }); #endif } } static void flecs_on_component(ecs_iter_t *it) { ecs_world_t *world = it->world; EcsComponent *c = ecs_field(it, EcsComponent, 0); 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 != EcsOnRemove) { ecs_entity_t parent = ecs_get_target(world, e, EcsChildOf, 0); if (parent) { ecs_add_id(world, parent, EcsModule); } } 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) { #ifdef FLECS_DEBUG if (ecs_should_log(0)) { char *path = ecs_get_path(world, e); ecs_trace("unregistering component '%s'", path); ecs_os_free(path); } #endif if (!ecs_vec_count(&world->store.marked_ids)) { flecs_type_info_free(world, e); } else { ecs_vec_append_t(&world->allocator, &world->store.deleted_components, ecs_entity_t)[0] = 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); } } } static void flecs_disable_observer( ecs_iter_t *it) { ecs_world_t *world = it->world; ecs_entity_t evt = it->event; int32_t i, count = it->count; for (i = 0; i < count; i ++) { flecs_observer_set_disable_bit(world, it->entities[i], EcsObserverIsDisabled, evt == EcsOnAdd); } } static void flecs_disable_module_observers( ecs_world_t *world, ecs_entity_t module, bool should_disable) { ecs_iter_t child_it = ecs_children(world, module); while (ecs_children_next(&child_it)) { ecs_table_t *table = child_it.table; bool table_disabled = table->flags & EcsTableIsDisabled; int32_t i; /* Recursively walk modules, don't propagate to disabled modules */ if (ecs_table_has_id(world, table, EcsModule) && !table_disabled) { for (i = 0; i < child_it.count; i ++) { flecs_disable_module_observers( world, child_it.entities[i], should_disable); } continue; } /* Only disable observers */ if (!ecs_table_has_id(world, table, EcsObserver)) { continue; } for (i = 0; i < child_it.count; i ++) { flecs_observer_set_disable_bit(world, child_it.entities[i], EcsObserverIsParentDisabled, should_disable); } } } static void flecs_disable_module(ecs_iter_t *it) { int32_t i; for (i = 0; i < it->count; i ++) { flecs_disable_module_observers( it->real_world, it->entities[i], it->event == EcsOnAdd); } } static void flecs_register_ordered_children(ecs_iter_t *it) { int32_t i; if (it->event == EcsOnAdd) { for (i = 0; i < it->count; i ++) { ecs_entity_t parent = it->entities[i]; ecs_component_record_t *cr = flecs_components_ensure( it->world, ecs_childof(parent)); if (!(cr->flags & EcsIdOrderedChildren)) { flecs_ordered_children_populate(it->world, cr); cr->flags |= EcsIdOrderedChildren; } } } else { ecs_assert(it->event == EcsOnRemove, ECS_INTERNAL_ERROR, NULL); for (i = 0; i < it->count; i ++) { ecs_entity_t parent = it->entities[i]; ecs_component_record_t *cr = flecs_components_get( it->world, ecs_childof(parent)); if (cr && (cr->flags & EcsIdOrderedChildren)) { flecs_ordered_children_clear(cr); cr->flags &= ~EcsIdOrderedChildren; } } } } /* -- 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); ecs_assert(record->table == &world->store.root, ECS_INTERNAL_ERROR, NULL); flecs_table_delete( world, &world->store.root, ECS_RECORD_TO_ROW(record->row), false); record->table = table; int32_t index = flecs_table_append(world, table, entity, false, false); record->row = ECS_ROW_TO_RECORD(index, 0); EcsComponent *component = 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 = 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; ecs_hashmap_t *name_index = table->_->childof_r->name_index; name_col[index].index = name_index; flecs_name_index_ensure(name_index, entity, name, name_length, name_hash); EcsIdentifier *symbol_col = 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. */ /* Initialize id records cached on world */ world->cr_childof_wildcard = flecs_components_ensure(world, ecs_pair(EcsChildOf, EcsWildcard)); world->cr_childof_wildcard->flags |= EcsIdOnDeleteTargetDelete | EcsIdOnInstantiateDontInherit | EcsIdTraversable | EcsIdPairIsTag | EcsIdExclusive; ecs_component_record_t *cr = flecs_components_ensure( world, ecs_pair_t(EcsIdentifier, EcsWildcard)); cr->flags |= EcsIdOnInstantiateDontInherit; world->cr_identifier_name = flecs_components_ensure(world, ecs_pair_t(EcsIdentifier, EcsName)); world->cr_childof_0 = flecs_components_ensure(world, ecs_pair(EcsChildOf, 0)); /* Initialize root table */ flecs_init_root_table(world); 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); /* Preallocate enough memory for initial components */ ecs_allocator_t *a = &world->allocator; ecs_vec_t v_entities = ecs_vec_from_entities(result); ecs_vec_init_t(a, &v_entities, ecs_entity_t, EcsFirstUserComponentId); { ecs_column_t *column = &result->data.columns[0]; ecs_vec_t v = ecs_vec_from_column_t(column, result, EcsComponent); ecs_vec_init_t(a, &v, EcsComponent, EcsFirstUserComponentId); ecs_assert(v.count == v_entities.count, ECS_INTERNAL_ERROR, NULL); ecs_assert(v.size == v_entities.size, ECS_INTERNAL_ERROR, NULL); column->data = v.array; } { ecs_column_t *column = &result->data.columns[1]; ecs_vec_t v = ecs_vec_from_column_t(column, result, EcsIdentifier); ecs_vec_init_t(a, &v, EcsIdentifier, EcsFirstUserComponentId); ecs_assert(v.count == v_entities.count, ECS_INTERNAL_ERROR, NULL); ecs_assert(v.size == v_entities.size, ECS_INTERNAL_ERROR, NULL); column->data = v.array; } { ecs_column_t *column = &result->data.columns[2]; ecs_vec_t v = ecs_vec_from_column_t(column, result, EcsIdentifier); ecs_vec_init_t(a, &v, EcsIdentifier, EcsFirstUserComponentId); ecs_assert(v.count == v_entities.count, ECS_INTERNAL_ERROR, NULL); ecs_assert(v.size == v_entities.size, ECS_INTERNAL_ERROR, NULL); column->data = v.array; } result->data.entities = v_entities.array; result->data.count = 0; result->data.size = v_entities.size; return result; } /* Make entities alive before the root table is initialized */ static void flecs_bootstrap_make_alive( ecs_world_t *world, ecs_entity_t e) { ecs_table_t *root = &world->store.root; flecs_entities_make_alive(world, e); ecs_record_t *r = flecs_entities_ensure(world, e); ecs_assert(r->table == NULL || r->table == root, ECS_INTERNAL_ERROR, NULL); if (r->table == NULL) { r->table = root; r->row = flecs_ito(uint32_t, root->data.count); ecs_vec_t v_entities = ecs_vec_from_entities(root); ecs_entity_t *array = ecs_vec_append_t(&world->allocator, &v_entities, ecs_entity_t); array[0] = e; root->data.entities = v_entities.array; root->data.count = v_entities.count; root->data.size = v_entities.size; } } 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); flecs_bootstrap_make_alive(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(world, name) == id, ECS_INTERNAL_ERROR, NULL); } } static void flecs_bootstrap_sanity_check( ecs_world_t *world) { (void)world; #ifdef FLECS_DEBUG int32_t i, e, count = flecs_sparse_count(&world->store.tables); int32_t total_count = 0; for (i = -1; i < count; i ++) { ecs_table_t *table; if (i == -1) { table = &world->store.root; } else { table = flecs_sparse_get_dense_t( &world->store.tables, ecs_table_t, i); } for (e = 0; e < table->data.count; e ++) { ecs_record_t *r = flecs_entities_get( world, table->data.entities[e]); ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(r->table == table, ECS_INTERNAL_ERROR, NULL); ecs_assert(ECS_RECORD_TO_ROW(r->row) == e, ECS_INTERNAL_ERROR, NULL); total_count ++; } } count = flecs_entities_count(world); ecs_assert(count == total_count, ECS_INTERNAL_ERROR, NULL); for (i = 1; i < count; i ++) { ecs_entity_t entity = flecs_entities_ids(world)[i]; ecs_assert(entity != 0, ECS_INTERNAL_ERROR, NULL); ecs_record_t *r = flecs_entities_get(world, entity); ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(r->dense == (i + 1), ECS_INTERNAL_ERROR, NULL); ecs_assert(r->table != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(r->table->data.entities[ECS_RECORD_TO_ROW(r->row)] == entity, ECS_INTERNAL_ERROR, NULL); } #endif } void flecs_bootstrap( ecs_world_t *world) { ecs_log_push(); ecs_set_name_prefix(world, "Ecs"); /* Ensure builtin ids are alive */ flecs_bootstrap_make_alive(world, ecs_id(EcsComponent)); flecs_bootstrap_make_alive(world, ecs_id(EcsIdentifier)); flecs_bootstrap_make_alive(world, ecs_id(EcsPoly)); flecs_bootstrap_make_alive(world, ecs_id(EcsDefaultChildComponent)); flecs_bootstrap_make_alive(world, EcsFinal); flecs_bootstrap_make_alive(world, EcsName); flecs_bootstrap_make_alive(world, EcsSymbol); flecs_bootstrap_make_alive(world, EcsAlias); flecs_bootstrap_make_alive(world, EcsChildOf); flecs_bootstrap_make_alive(world, EcsFlecs); flecs_bootstrap_make_alive(world, EcsFlecsCore); flecs_bootstrap_make_alive(world, EcsFlecsInternals); flecs_bootstrap_make_alive(world, EcsOnAdd); flecs_bootstrap_make_alive(world, EcsOnRemove); flecs_bootstrap_make_alive(world, EcsOnSet); flecs_bootstrap_make_alive(world, EcsOnDelete); flecs_bootstrap_make_alive(world, EcsPanic); flecs_bootstrap_make_alive(world, EcsFlag); flecs_bootstrap_make_alive(world, EcsIsA); flecs_bootstrap_make_alive(world, EcsWildcard); flecs_bootstrap_make_alive(world, EcsAny); flecs_bootstrap_make_alive(world, EcsCanToggle); flecs_bootstrap_make_alive(world, EcsTrait); flecs_bootstrap_make_alive(world, EcsRelationship); flecs_bootstrap_make_alive(world, EcsTarget); flecs_bootstrap_make_alive(world, EcsSparse); flecs_bootstrap_make_alive(world, EcsDontFragment); flecs_bootstrap_make_alive(world, EcsObserver); flecs_bootstrap_make_alive(world, EcsPairIsTag); /* Register type information for builtin components */ flecs_type_info_init(world, EcsComponent, { .ctor = flecs_default_ctor, .on_set = flecs_on_component, .on_remove = flecs_on_component }); flecs_type_info_init(world, EcsIdentifier, { .ctor = flecs_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 = flecs_default_ctor, .copy = ecs_copy(EcsPoly), .move = ecs_move(EcsPoly), .dtor = ecs_dtor(EcsPoly) }); flecs_type_info_init(world, EcsDefaultChildComponent, { .ctor = flecs_default_ctor, }); /* Create and cache often used id records on world */ flecs_components_init(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, EcsPoly); flecs_bootstrap_builtin_t(world, table, EcsDefaultChildComponent); /* 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; /* Register observer for trait before adding EcsPairIsTag */ ecs_observer(world, { .entity = ecs_entity(world, { .parent = EcsFlecsInternals }), .query.terms[0] = { .id = EcsPairIsTag }, .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, EcsNotQueryable); 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_trait(world, EcsTransitive); flecs_bootstrap_trait(world, EcsReflexive); flecs_bootstrap_trait(world, EcsSymmetric); flecs_bootstrap_trait(world, EcsSingleton); flecs_bootstrap_trait(world, EcsFinal); flecs_bootstrap_trait(world, EcsInheritable); flecs_bootstrap_trait(world, EcsPairIsTag); flecs_bootstrap_trait(world, EcsExclusive); flecs_bootstrap_trait(world, EcsAcyclic); flecs_bootstrap_trait(world, EcsTraversable); flecs_bootstrap_trait(world, EcsWith); flecs_bootstrap_trait(world, EcsOneOf); flecs_bootstrap_trait(world, EcsCanToggle); flecs_bootstrap_trait(world, EcsTrait); flecs_bootstrap_trait(world, EcsRelationship); flecs_bootstrap_trait(world, EcsTarget); flecs_bootstrap_trait(world, EcsOnDelete); flecs_bootstrap_trait(world, EcsOnDeleteTarget); flecs_bootstrap_trait(world, EcsOnInstantiate); flecs_bootstrap_trait(world, EcsSparse); flecs_bootstrap_trait(world, EcsDontFragment); flecs_bootstrap_tag(world, EcsRemove); flecs_bootstrap_tag(world, EcsDelete); flecs_bootstrap_tag(world, EcsPanic); flecs_bootstrap_tag(world, EcsOverride); flecs_bootstrap_tag(world, EcsInherit); flecs_bootstrap_tag(world, EcsDontInherit); flecs_bootstrap_tag(world, EcsOrderedChildren); /* 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, EcsMonitor, "EcsMonitor", EcsFlecsCore); flecs_bootstrap_entity(world, EcsOnTableCreate, "OnTableCreate", EcsFlecsCore); flecs_bootstrap_entity(world, EcsOnTableDelete, "OnTableDelete", EcsFlecsCore); /* Sync properties of ChildOf and Identifier with bootstrapped flags */ ecs_add_pair(world, EcsChildOf, EcsOnDeleteTarget, EcsDelete); ecs_add_id(world, EcsChildOf, EcsTrait); ecs_add_id(world, EcsChildOf, EcsAcyclic); ecs_add_id(world, EcsChildOf, EcsTraversable); ecs_add_pair(world, EcsChildOf, EcsOnInstantiate, EcsDontInherit); ecs_add_pair(world, ecs_id(EcsIdentifier), EcsOnInstantiate, EcsDontInherit); /* Create triggers in internals scope */ ecs_set_scope(world, EcsFlecsInternals); /* Register observers for components/relationship properties. Most observers * set flags on an component record when a trait is added to a component, which * allows for quick trait testing in various operations. */ ecs_observer(world, { .query.terms = {{ .id = EcsFinal }}, .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd}, .callback = flecs_register_final }); static ecs_on_trait_ctx_t inheritable_trait = { EcsIdInheritable, 0 }; ecs_observer(world, { .query.terms = {{ .id = EcsInheritable }}, .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd}, .callback = flecs_register_trait, .ctx = &inheritable_trait }); ecs_observer(world, { .query.terms = { { .id = ecs_pair(EcsOnDelete, EcsWildcard) } }, .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd, EcsOnRemove}, .callback = flecs_register_on_delete }); ecs_observer(world, { .query.terms = { { .id = ecs_pair(EcsOnDeleteTarget, EcsWildcard) } }, .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd, EcsOnRemove}, .callback = flecs_register_on_delete_object }); ecs_observer(world, { .query.terms = { { .id = ecs_pair(EcsOnInstantiate, EcsWildcard) } }, .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd}, .callback = flecs_register_on_instantiate }); ecs_observer(world, { .query.terms = {{ .id = EcsSymmetric }}, .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd}, .callback = flecs_register_symmetric }); ecs_observer(world, { .query.terms = {{ .id = EcsSingleton }}, .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd}, .callback = flecs_register_singleton }); static ecs_on_trait_ctx_t traversable_trait = { EcsIdTraversable, EcsIdTraversable }; ecs_observer(world, { .query.terms = {{ .id = EcsTraversable }}, .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd, EcsOnRemove}, .callback = flecs_register_trait, .ctx = &traversable_trait }); static ecs_on_trait_ctx_t exclusive_trait = { EcsIdExclusive, 0 }; ecs_observer(world, { .query.terms = {{ .id = EcsExclusive }}, .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd, EcsOnRemove}, .callback = flecs_register_trait, .ctx = &exclusive_trait }); static ecs_on_trait_ctx_t toggle_trait = { EcsIdCanToggle, 0 }; ecs_observer(world, { .query.terms = {{ .id = EcsCanToggle }}, .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd}, .callback = flecs_register_trait, .ctx = &toggle_trait }); static ecs_on_trait_ctx_t with_trait = { EcsIdWith, 0 }; ecs_observer(world, { .query.terms = { { .id = ecs_pair(EcsWith, EcsWildcard) }, }, .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd}, .callback = flecs_register_trait_pair, .ctx = &with_trait }); static ecs_on_trait_ctx_t sparse_trait = { EcsIdSparse, 0 }; ecs_observer(world, { .query.terms = {{ .id = EcsSparse }}, .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd}, .callback = flecs_register_trait, .ctx = &sparse_trait }); static ecs_on_trait_ctx_t dont_fragment_trait = { EcsIdDontFragment, 0 }; ecs_observer(world, { .query.terms = {{ .id = EcsDontFragment }}, .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd}, .callback = flecs_register_trait, .ctx = &dont_fragment_trait }); ecs_observer(world, { .query.terms = {{ .id = EcsOrderedChildren }}, .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd, EcsOnRemove}, .callback = flecs_register_ordered_children }); /* Entities used as slot are marked as exclusive to ensure a slot can always * only point to a single entity. */ ecs_observer(world, { .query.terms = { { .id = ecs_pair(EcsSlotOf, EcsWildcard) } }, .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .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, { .query.terms = {{ .id = EcsModule } }, .query.flags = EcsQueryMatchPrefab|EcsQueryMatchDisabled, .events = {EcsOnAdd}, .callback = flecs_ensure_module_tag }); /* Observer that tracks whether observers are disabled */ ecs_observer(world, { .query.terms = { { .id = EcsObserver }, { .id = EcsDisabled }, }, .events = {EcsOnAdd, EcsOnRemove}, .callback = flecs_disable_observer }); /* Observer that tracks whether modules are disabled */ ecs_observer(world, { .query.terms = { { .id = EcsModule }, { .id = EcsDisabled }, }, .events = {EcsOnAdd, EcsOnRemove}, .callback = flecs_disable_module }); /* Set scope back to flecs core */ ecs_set_scope(world, EcsFlecsCore); /* Exclusive properties */ ecs_add_id(world, EcsChildOf, EcsExclusive); ecs_add_id(world, EcsOnDelete, EcsExclusive); ecs_add_id(world, EcsOnDeleteTarget, EcsExclusive); ecs_add_id(world, EcsOnInstantiate, EcsExclusive); /* Unqueryable entities */ ecs_add_id(world, EcsThis, EcsNotQueryable); ecs_add_id(world, EcsWildcard, EcsNotQueryable); ecs_add_id(world, EcsAny, EcsNotQueryable); ecs_add_id(world, EcsVariable, EcsNotQueryable); /* Tag relationships (relationships that should never have data) */ ecs_add_id(world, EcsIsA, EcsPairIsTag); ecs_add_id(world, EcsChildOf, EcsPairIsTag); ecs_add_id(world, EcsSlotOf, EcsPairIsTag); ecs_add_id(world, EcsDependsOn, EcsPairIsTag); ecs_add_id(world, EcsFlag, EcsPairIsTag); ecs_add_id(world, EcsWith, EcsPairIsTag); /* Relationships */ ecs_add_id(world, EcsChildOf, EcsRelationship); ecs_add_id(world, EcsIsA, EcsRelationship); ecs_add_id(world, EcsSlotOf, EcsRelationship); ecs_add_id(world, EcsDependsOn, EcsRelationship); ecs_add_id(world, EcsWith, EcsRelationship); ecs_add_id(world, EcsOnDelete, EcsRelationship); ecs_add_id(world, EcsOnDeleteTarget, EcsRelationship); ecs_add_id(world, EcsOnInstantiate, EcsRelationship); ecs_add_id(world, ecs_id(EcsIdentifier), EcsRelationship); /* Targets */ ecs_add_id(world, EcsOverride, EcsTarget); ecs_add_id(world, EcsInherit, EcsTarget); ecs_add_id(world, EcsDontInherit, EcsTarget); /* Traversable relationships are always acyclic */ ecs_add_pair(world, EcsTraversable, EcsWith, EcsAcyclic); /* Transitive relationships are always Traversable */ ecs_add_pair(world, EcsTransitive, EcsWith, EcsTraversable); /* DontFragment components are always sparse */ ecs_add_pair(world, EcsDontFragment, EcsWith, EcsSparse); /* DontInherit components */ ecs_add_pair(world, EcsPrefab, EcsOnInstantiate, EcsDontInherit); ecs_add_pair(world, ecs_id(EcsComponent), EcsOnInstantiate, EcsDontInherit); ecs_add_pair(world, EcsOnDelete, EcsOnInstantiate, EcsDontInherit); ecs_add_pair(world, EcsExclusive, EcsOnInstantiate, EcsDontInherit); ecs_add_pair(world, EcsDontFragment, EcsOnInstantiate, 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); /* Private properties */ ecs_add_id(world, ecs_id(EcsPoly), EcsPrivate); ecs_add_id(world, ecs_id(EcsIdentifier), EcsPrivate); /* Inherited components */ ecs_add_pair(world, EcsIsA, EcsOnInstantiate, EcsInherit); ecs_add_pair(world, EcsDependsOn, EcsOnInstantiate, EcsInherit); /* Run bootstrap functions for other parts of the code */ flecs_bootstrap_entity_name(world); /* Register constant tag */ ecs_component(world, { .entity = ecs_entity(world, { .id = EcsConstant, .name = "constant", .symbol = "EcsConstant", .add = ecs_ids(ecs_pair(EcsOnInstantiate, EcsDontInherit)) }) }); ecs_set_scope(world, 0); ecs_set_name_prefix(world, NULL); ecs_assert(world->cr_childof_wildcard != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(world->cr_isa_wildcard != NULL, ECS_INTERNAL_ERROR, NULL); /* Verify that all entities are where they're supposed to be */ flecs_bootstrap_sanity_check(world); ecs_log_pop(); }