Properly link flecs library
This commit is contained in:
945
engine/libs/flecs/src/bootstrap.c
Normal file
945
engine/libs/flecs/src/bootstrap.c
Normal file
@@ -0,0 +1,945 @@
|
||||
/**
|
||||
* @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();
|
||||
}
|
||||
Reference in New Issue
Block a user