From cf0f174951969d2492eef6b7e1aadba5c23fd960 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sun, 14 Apr 2024 05:00:22 +0700 Subject: [PATCH] Upgrade to flecs 3.2.10 (#24) --- flecs-sys/README.md | 2 +- flecs-sys/flecs.c | 5466 +++++++++++++++++++++++++++---------- flecs-sys/flecs.h | 904 ++++-- flecs-sys/src/bindings.rs | 300 +- readme.md | 2 +- 5 files changed, 4892 insertions(+), 1782 deletions(-) diff --git a/flecs-sys/README.md b/flecs-sys/README.md index f5d6892..9cf3311 100644 --- a/flecs-sys/README.md +++ b/flecs-sys/README.md @@ -7,4 +7,4 @@ A Rust binding for the Flecs ECS library: -Wraps native Flecs v3.2.8 +Wraps native Flecs v3.2.10 diff --git a/flecs-sys/flecs.c b/flecs-sys/flecs.c index 0d748e1..73a3b54 100644 --- a/flecs-sys/flecs.c +++ b/flecs-sys/flecs.c @@ -310,7 +310,7 @@ FLECS_DBG_API void flecs_bitset_init( ecs_bitset_t *bs); -/** Deinialize bitset. */ +/** Deinitialize bitset. */ FLECS_DBG_API void flecs_bitset_fini( ecs_bitset_t *bs); @@ -625,7 +625,7 @@ typedef struct ecs_table_event_t { /* Event match */ ecs_entity_t event; - /* If the nubmer of fields gets out of hand, this can be turned into a union + /* If the number of fields gets out of hand, this can be turned into a union * but since events are very temporary objects, this works for now and makes * initializing an event a bit simpler. */ } ecs_table_event_t; @@ -661,7 +661,6 @@ typedef struct ecs_column_t { /** Table data */ struct ecs_data_t { ecs_vec_t entities; /* Entity ids */ - ecs_vec_t records; /* Ptrs to records in entity index */ ecs_column_t *columns; /* Component data */ }; @@ -743,7 +742,6 @@ int32_t flecs_table_append( ecs_world_t *world, ecs_table_t *table, ecs_entity_t entity, - ecs_record_t *record, bool construct, bool on_add); @@ -857,6 +855,12 @@ void flecs_table_traversable_add( ecs_table_t *table, int32_t value); +ecs_vec_t* flecs_table_entities( + ecs_table_t *table); + +ecs_entity_t* flecs_table_entities_array( + ecs_table_t *table); + #endif @@ -864,6 +868,7 @@ void flecs_table_traversable_add( extern const ecs_entity_t EcsFlag; #define ECS_MAX_JOBS_PER_WORKER (16) +#define ECS_MAX_DEFER_STACK (8) /* Magic number for a flecs object */ #define ECS_OBJECT_MAGIC (0x6563736f) @@ -1088,7 +1093,7 @@ struct ecs_query_t { /* Monitor generation */ int32_t monitor_generation; - int32_t cascade_by; /* Identify cascade column */ + int32_t cascade_by; /* Identify cascade term */ int32_t match_count; /* How often have tables been (un)matched */ int32_t prev_match_count; /* Track if sorting is needed */ int32_t rematch_count; /* Track which tables were added during rematch */ @@ -1154,24 +1159,32 @@ typedef struct ecs_stage_allocators_t { /** Types for deferred operations */ typedef enum ecs_cmd_kind_t { - EcsOpClone, - EcsOpBulkNew, - EcsOpAdd, - EcsOpRemove, - EcsOpSet, - EcsOpEmplace, - EcsOpMut, - EcsOpModified, - EcsOpAddModified, - EcsOpPath, - EcsOpDelete, - EcsOpClear, - EcsOpOnDeleteAction, - EcsOpEnable, - EcsOpDisable, - EcsOpSkip + EcsCmdClone, + EcsCmdBulkNew, + EcsCmdAdd, + EcsCmdRemove, + EcsCmdSet, + EcsCmdEmplace, + EcsCmdMut, + EcsCmdModified, + EcsCmdModifiedNoHook, + EcsCmdAddModified, + EcsCmdPath, + EcsCmdDelete, + EcsCmdClear, + EcsCmdOnDeleteAction, + EcsCmdEnable, + EcsCmdDisable, + EcsCmdEvent, + EcsCmdSkip } ecs_cmd_kind_t; +/* Entity specific metadata for command in queue */ +typedef struct ecs_cmd_entry_t { + int32_t first; + int32_t last; /* If -1, a delete command was inserted */ +} ecs_cmd_entry_t; + typedef struct ecs_cmd_1_t { void *value; /* Component value (used by set / get_mut) */ ecs_size_t size; /* Size of value */ @@ -1188,6 +1201,7 @@ typedef struct ecs_cmd_t { int32_t next_for_entity; /* Next operation for entity */ ecs_id_t id; /* (Component) id */ ecs_id_record_t *idr; /* Id record (only for set/mut/emplace) */ + ecs_cmd_entry_t *entry; ecs_entity_t entity; /* Entity id */ union { @@ -1196,11 +1210,12 @@ typedef struct ecs_cmd_t { } is; } ecs_cmd_t; -/* Entity specific metadata for command in defer queue */ -typedef struct ecs_cmd_entry_t { - int32_t first; - int32_t last; /* If -1, a delete command was inserted */ -} ecs_cmd_entry_t; +/* Data structures that store the command queue */ +typedef struct ecs_commands_t { + ecs_vec_t queue; + ecs_stack_t stack; /* Temp memory used by deferred commands */ + ecs_sparse_t entries; /* - command batching */ +} ecs_commands_t; /** A stage is a context that allows for safely using the API from multiple * threads. Stage pointers can be passed to the world argument of API @@ -1212,11 +1227,13 @@ struct ecs_stage_t { /* Unique id that identifies the stage */ int32_t id; - /* Deferred command queue */ + /* Zero if not deferred, positive if deferred, negative if suspended */ int32_t defer; - ecs_vec_t commands; - ecs_stack_t defer_stack; /* Temp memory used by deferred commands */ - ecs_sparse_t cmd_entries; /* - command combining */ + + /* Command queue stack, for nested execution */ + ecs_commands_t *cmd; + ecs_commands_t cmd_stack[ECS_MAX_DEFER_STACK]; + int32_t cmd_sp; /* Thread context */ ecs_world_t *thread_ctx; /* Points to stage when a thread stage */ @@ -1792,19 +1809,16 @@ ecs_poly_t* ecs_poly_get_( /* Utilities for testing/asserting an object type */ #ifndef FLECS_NDEBUG -void* ecs_poly_assert_( - const ecs_poly_t *object, - int32_t type, - const char *file, - int32_t line); - -#define ecs_poly_assert(object, type)\ - ecs_poly_assert_(object, type##_magic, __FILE__, __LINE__) - -#define ecs_poly(object, T) ((T*)ecs_poly_assert(object, T)) +#define ecs_poly_assert(object, ty)\ + do {\ + ecs_assert(object != NULL, ECS_INVALID_PARAMETER, NULL);\ + const ecs_header_t *hdr = (const ecs_header_t *)object;\ + const char *type_name = hdr->mixins->type_name;\ + ecs_assert(hdr->magic == ECS_OBJECT_MAGIC, ECS_INVALID_PARAMETER, type_name);\ + ecs_assert(hdr->type == ty##_magic, ECS_INVALID_PARAMETER, type_name);\ + } while (0) #else -#define ecs_poly_assert(object, type) -#define ecs_poly(object, T) ((T*)object) +#define ecs_poly_assert(object, ty) #endif /* Utility functions for getting a mixin from an object */ @@ -1909,8 +1923,7 @@ void* flecs_defer_set( ecs_entity_t entity, ecs_entity_t component, ecs_size_t size, - void *value, - bool need_value); + void *value); bool flecs_defer_end( ecs_world_t *world, @@ -1920,6 +1933,17 @@ bool flecs_defer_purge( ecs_world_t *world, ecs_stage_t *stage); +void flecs_enqueue( + ecs_world_t *world, + ecs_stage_t *stage, + ecs_event_desc_t *desc); + +void flecs_commands_push( + ecs_stage_t *stage); + +void flecs_commands_pop( + ecs_stage_t *stage); + #endif /** @@ -2438,6 +2462,12 @@ int32_t flecs_search_relation_w_idr( struct ecs_table_record_t **tr_out, ecs_id_record_t *idr); +bool flecs_type_can_inherit_id( + const ecs_world_t *world, + const ecs_table_t *table, + const ecs_id_record_t *idr, + ecs_id_t id); + #endif @@ -2907,7 +2937,7 @@ void flecs_bootstrap_builtin( ecs_record_t *record = flecs_entities_ensure(world, entity); record->table = table; - int32_t index = flecs_table_append(world, table, entity, record, false, false); + 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); @@ -2984,7 +3014,6 @@ ecs_table_t* flecs_bootstrap_component_table( /* 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->records, ecs_record_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); @@ -3337,6 +3366,8 @@ void flecs_bootstrap( /* DontInherit components */ ecs_add_id(world, EcsPrefab, EcsDontInherit); + ecs_add_id(world, ecs_id(EcsComponent), EcsDontInherit); + ecs_add_id(world, EcsOnDelete, EcsDontInherit); /* Acyclic/Traversable components */ ecs_add_id(world, EcsIsA, EcsTraversable); @@ -3365,6 +3396,9 @@ void flecs_bootstrap( ecs_set_name_prefix(world, NULL); + ecs_assert(world->idr_childof_wildcard != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(world->idr_isa_wildcard != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_log_pop(); } @@ -3688,7 +3722,8 @@ void flecs_instantiate_children( /* Keep track of the element that creates the ChildOf relationship with * the prefab parent. We need to replace this element to make sure the * created children point to the instance and not the prefab */ - if (ECS_HAS_RELATION(id, EcsChildOf) && (ECS_PAIR_SECOND(id) == base)) { + if (ECS_HAS_RELATION(id, EcsChildOf) && + (ECS_PAIR_SECOND(id) == (uint32_t)base)) { childof_base_index = pos; } @@ -3988,7 +4023,7 @@ ecs_record_t* flecs_new_entity( ecs_flags32_t evt_flags) { ecs_assert(record != NULL, ECS_INTERNAL_ERROR, NULL); - int32_t row = flecs_table_append(world, table, entity, record, ctor, true); + int32_t row = flecs_table_append(world, table, entity, ctor, true); record->table = table; record->row = ECS_ROW_TO_RECORD(row, record->row & ECS_ROW_FLAGS_MASK); @@ -4025,7 +4060,7 @@ void flecs_move_entity( /* Append new row to destination table */ int32_t dst_row = flecs_table_append(world, dst_table, entity, - record, false, false); + false, false); /* Invoke remove actions for removed components */ flecs_notify_on_remove( @@ -4214,12 +4249,10 @@ const ecs_entity_t* flecs_bulk_new( /* Update entity index. */ int i; - ecs_record_t **records = ecs_vec_first(&data->records); for (i = 0; i < count; i ++) { ecs_record_t *r = flecs_entities_get(world, entities[i]); r->table = table; r->row = ECS_ROW_TO_RECORD(row + i, 0); - records[row + i] = r; } flecs_defer_begin(world, &world->stages[0]); @@ -4746,7 +4779,7 @@ ecs_table_t *flecs_traverse_from_expr( const char *ptr = expr; if (ptr) { ecs_term_t term = {0}; - while (ptr[0] && (ptr = ecs_parse_term(world, name, expr, ptr, &term))){ + while (ptr[0] && (ptr = ecs_parse_term(world, name, expr, ptr, &term, NULL))){ if (!ecs_term_is_initialized(&term)) { break; } @@ -4809,7 +4842,7 @@ void flecs_defer_from_expr( const char *ptr = expr; if (ptr) { ecs_term_t term = {0}; - while (ptr[0] && (ptr = ecs_parse_term(world, name, expr, ptr, &term))){ + while (ptr[0] && (ptr = ecs_parse_term(world, name, expr, ptr, &term, NULL))) { if (!ecs_term_is_initialized(&term)) { break; } @@ -5460,10 +5493,9 @@ void flecs_targets_mark_for_delete( { ecs_id_record_t *idr; ecs_entity_t *entities = ecs_vec_first(&table->data.entities); - ecs_record_t **records = ecs_vec_first(&table->data.records); int32_t i, count = ecs_vec_count(&table->data.entities); for (i = 0; i < count; i ++) { - ecs_record_t *r = records[i]; + ecs_record_t *r = flecs_entities_get(world, entities[i]); if (!r) { continue; } @@ -6053,22 +6085,28 @@ ecs_entity_t ecs_clone( goto done; } - ecs_type_t src_type = src_table->type; - ecs_table_diff_t diff = { .added = src_type }; + ecs_table_t *dst_table = src_table; + if (src_table->flags & EcsTableHasName) { + dst_table = ecs_table_remove_id(world, src_table, + ecs_pair_t(EcsIdentifier, EcsName)); + } + + ecs_type_t dst_type = dst_table->type; + ecs_table_diff_t diff = { .added = dst_type }; ecs_record_t *dst_r = flecs_entities_get(world, dst); - flecs_new_entity(world, dst, dst_r, src_table, &diff, true, true); + flecs_new_entity(world, dst, dst_r, dst_table, &diff, true, true); int32_t row = ECS_RECORD_TO_ROW(dst_r->row); if (copy_value) { - flecs_table_move(world, dst, src, src_table, + flecs_table_move(world, dst, src, dst_table, row, src_table, ECS_RECORD_TO_ROW(src_r->row), true); - int32_t i, count = src_table->column_count; + int32_t i, count = dst_table->column_count; for (i = 0; i < count; i ++) { ecs_type_t type = { - .array = &src_table->data.columns[i].id, + .array = &dst_table->data.columns[i].id, .count = 1 }; - flecs_notify_on_set(world, src_table, row, 1, &type, true); + flecs_notify_on_set(world, dst_table, row, 1, &type, true); } } @@ -6128,8 +6166,7 @@ void* ecs_get_mut_id( ecs_stage_t *stage = flecs_stage_from_world(&world); if (flecs_defer_cmd(stage)) { - return flecs_defer_set( - world, stage, EcsOpMut, entity, id, 0, NULL, true); + return flecs_defer_set(world, stage, EcsCmdMut, entity, id, 0, NULL); } ecs_record_t *r = flecs_entities_get(world, entity); @@ -6155,8 +6192,7 @@ void* ecs_get_mut_modified_id( ecs_stage_t *stage = flecs_stage_from_world(&world); ecs_check(flecs_defer_cmd(stage), ECS_INVALID_PARAMETER, NULL); - return flecs_defer_set( - world, stage, EcsOpSet, entity, id, 0, NULL, true); + return flecs_defer_set(world, stage, EcsCmdSet, entity, id, 0, NULL); error: return NULL; } @@ -6390,8 +6426,7 @@ void* ecs_emplace_id( ecs_stage_t *stage = flecs_stage_from_world(&world); if (flecs_defer_cmd(stage)) { - return flecs_defer_set(world, stage, EcsOpEmplace, entity, id, 0, NULL, - true); + return flecs_defer_set(world, stage, EcsCmdEmplace, entity, id, 0, NULL); } ecs_record_t *r = flecs_entities_get(world, entity); @@ -6410,7 +6445,8 @@ static void flecs_modified_id_if( ecs_world_t *world, ecs_entity_t entity, - ecs_id_t id) + ecs_id_t id, + bool owned) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(ecs_is_alive(world, entity), ECS_INVALID_PARAMETER, NULL); @@ -6430,7 +6466,7 @@ void flecs_modified_id_if( } ecs_type_t ids = { .array = &id, .count = 1 }; - flecs_notify_on_set(world, table, ECS_RECORD_TO_ROW(r->row), 1, &ids, true); + flecs_notify_on_set(world, table, ECS_RECORD_TO_ROW(r->row), 1, &ids, owned); flecs_table_mark_dirty(world, table, id); flecs_defer_end(world, stage); @@ -6480,8 +6516,8 @@ void flecs_copy_ptr_w_id( void *ptr) { if (flecs_defer_cmd(stage)) { - flecs_defer_set(world, stage, EcsOpSet, entity, id, - flecs_utosize(size), ptr, false); + flecs_defer_set(world, stage, EcsCmdSet, entity, id, + flecs_utosize(size), ptr); return; } @@ -6527,7 +6563,7 @@ void flecs_move_ptr_w_id( { if (flecs_defer_cmd(stage)) { flecs_defer_set(world, stage, cmd_kind, entity, id, - flecs_utosize(size), ptr, false); + flecs_utosize(size), ptr); return; } @@ -6538,7 +6574,7 @@ void flecs_move_ptr_w_id( const ecs_type_info_t *ti = dst.ti; ecs_assert(ti != NULL, ECS_INTERNAL_ERROR, NULL); ecs_move_t move; - if (cmd_kind != EcsOpEmplace) { + if (cmd_kind != EcsCmdEmplace) { /* ctor will have happened by get_mut */ move = ti->hooks.move_dtor; } else { @@ -6552,7 +6588,7 @@ void flecs_move_ptr_w_id( flecs_table_mark_dirty(world, r->table, id); - if (cmd_kind == EcsOpSet) { + if (cmd_kind == EcsCmdSet) { ecs_table_t *table = r->table; if (table->flags & EcsTableHasOnSet || ti->hooks.on_set) { ecs_type_t ids = { .array = &id, .count = 1 }; @@ -6978,10 +7014,10 @@ void flecs_flatten( bool has_tgt = table->flags & EcsTableHasTarget; flecs_emit_propagate_invalidate(world, table, 0, count); - ecs_record_t **records = table->data.records.array; ecs_entity_t *entities = table->data.entities.array; for (i = 0; i < count; i ++) { - ecs_flags32_t flags = ECS_RECORD_TO_ROW_FLAGS(records[i]->row); + ecs_record_t *record = flecs_entities_get(world, entities[i]); + ecs_flags32_t flags = ECS_RECORD_TO_ROW_FLAGS(record->row); if (flags & EcsEntityIsTarget) { flecs_flatten(world, root, ecs_pair(rel, entities[i]), depth + 1, desc); @@ -7081,10 +7117,10 @@ void ecs_flatten( } int32_t i, count = ecs_table_count(table); - ecs_record_t **records = table->data.records.array; ecs_entity_t *entities = table->data.entities.array; for (i = 0; i < count; i ++) { - ecs_flags32_t flags = ECS_RECORD_TO_ROW_FLAGS(records[i]->row); + ecs_record_t *record = flecs_entities_get(world, entities[i]); + ecs_flags32_t flags = ECS_RECORD_TO_ROW_FLAGS(record->row); if (flags & EcsEntityIsTarget) { flecs_flatten(world, root, ecs_pair(rel, entities[i]), 1, &private_desc); @@ -7427,6 +7463,7 @@ const ecs_type_info_t* ecs_get_type_info( ecs_id_t id) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(id != 0, ECS_INVALID_PARAMETER, NULL); world = ecs_get_world(world); @@ -7465,6 +7502,7 @@ ecs_entity_t ecs_get_typeid( ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); const ecs_type_info_t *ti = ecs_get_type_info(world, id); if (ti) { + ecs_assert(ti->component != 0, ECS_INTERNAL_ERROR, NULL); return ti->component; } error: @@ -7811,19 +7849,42 @@ void flecs_dtor_value( } } +static +void flecs_free_cmd_event( + ecs_world_t *world, + ecs_event_desc_t *desc) +{ + if (desc->ids) { + flecs_stack_free_n(desc->ids->array, ecs_id_t, desc->ids->count); + } + + /* Safe const cast, command makes a copy of type object */ + flecs_stack_free_t(ECS_CONST_CAST(ecs_type_t*, desc->ids), + ecs_type_t); + + if (desc->param) { + flecs_dtor_value(world, desc->event, + /* Safe const cast, command makes copy of value */ + ECS_CONST_CAST(void*, desc->param), 1); + } +} + static void flecs_discard_cmd( ecs_world_t *world, ecs_cmd_t *cmd) { - if (cmd->kind != EcsOpBulkNew) { + if (cmd->kind == EcsCmdBulkNew) { + ecs_os_free(cmd->is._n.entities); + } else if (cmd->kind == EcsCmdEvent) { + flecs_free_cmd_event(world, cmd->is._1.value); + } else { + ecs_assert(cmd->kind != EcsCmdEvent, ECS_INTERNAL_ERROR, NULL); void *value = cmd->is._1.value; if (value) { flecs_dtor_value(world, cmd->id, value, 1); flecs_stack_free(value, cmd->is._1.size); } - } else { - ecs_os_free(cmd->is._n.entities); } } @@ -7879,6 +7940,51 @@ bool flecs_remove_invalid( return true; } +static +ecs_table_t* flecs_cmd_batch_add_diff( + ecs_world_t *world, + ecs_table_t *dst, + ecs_table_t *cur, + ecs_table_t *prev) +{ + int32_t p = 0, p_count = prev->type.count; + int32_t c = 0, c_count = cur->type.count; + ecs_id_t *p_ids = prev->type.array; + ecs_id_t *c_ids = cur->type.array; + + for (; c < c_count;) { + ecs_id_t c_id = c_ids[c]; + ecs_id_t p_id = p_ids[p]; + + if (p_id < c_id) { + /* Previous id no longer exists in new table, so it was removed */ + dst = ecs_table_remove_id(world, dst, p_id); + } + if (c_id < p_id) { + /* Current id didn't exist in previous table, so it was added */ + dst = ecs_table_add_id(world, dst, c_id); + } + + c += c_id <= p_id; + p += p_id <= c_id; + if (p == p_count) { + break; + } + } + + /* Remainder */ + for (; p < p_count; p ++) { + ecs_id_t p_id = p_ids[p]; + dst = ecs_table_remove_id(world, dst, p_id); + } + for (; c < c_count; c ++) { + ecs_id_t c_id = c_ids[c]; + dst = ecs_table_add_id(world, dst, c_id); + } + + return dst; +} + static void flecs_cmd_batch_for_entity( ecs_world_t *world, @@ -7918,13 +8024,13 @@ void flecs_cmd_batch_for_entity( if (flecs_remove_invalid(world, id, &id)) { if (!id) { /* Entity should remain alive but id should not be added */ - cmd->kind = EcsOpSkip; + cmd->kind = EcsCmdSkip; continue; } /* Entity should remain alive and id is still valid */ } else { /* Id was no longer valid and had a Delete policy */ - cmd->kind = EcsOpSkip; + cmd->kind = EcsCmdSkip; ecs_delete(world, entity); flecs_table_diff_builder_clear(diff); return; @@ -7933,16 +8039,16 @@ void flecs_cmd_batch_for_entity( ecs_cmd_kind_t kind = cmd->kind; switch(kind) { - case EcsOpAddModified: + case EcsCmdAddModified: /* Add is batched, but keep Modified */ - cmd->kind = EcsOpModified; + cmd->kind = EcsCmdModified; /* fall through */ - case EcsOpAdd: + case EcsCmdAdd: table = flecs_find_table_add(world, table, id, diff); world->info.cmd.batched_command_count ++; break; - case EcsOpModified: + case EcsCmdModified: if (start_table) { /* If a modified was inserted for an existing component, the value * of the component could have been changed. If this is the case, @@ -7958,27 +8064,42 @@ void flecs_cmd_batch_for_entity( const ecs_type_info_t *ti = ptr.ti; ecs_iter_action_t on_set; if ((on_set = ti->hooks.on_set)) { + ecs_table_t *prev_table = r->table; + ecs_defer_begin(world); flecs_invoke_hook(world, start_table, 1, row, &entity, ptr.ptr, cmd->id, ptr.ti, EcsOnSet, on_set); + ecs_defer_end(world); + + /* Don't run on_set hook twice, but make sure to still + * invoke observers. */ + cmd->kind = EcsCmdModifiedNoHook; + + /* If entity changed tables in hook, add difference to + * destination table, so we don't lose the side effects + * of the hook. */ + if (r->table != prev_table) { + table = flecs_cmd_batch_add_diff( + world, table, r->table, prev_table); + } } } } break; - case EcsOpSet: - case EcsOpMut: + case EcsCmdSet: + case EcsCmdMut: table = flecs_find_table_add(world, table, id, diff); world->info.cmd.batched_command_count ++; has_set = true; break; - case EcsOpEmplace: + case EcsCmdEmplace: /* Don't add for emplace, as this requires a special call to ensure * the constructor is not invoked for the component */ break; - case EcsOpRemove: + case EcsCmdRemove: table = flecs_find_table_remove(world, table, id, diff); world->info.cmd.batched_command_count ++; break; - case EcsOpClear: + case EcsCmdClear: if (table) { ecs_id_t *ids = ecs_vec_grow_t(&world->allocator, &diff->removed, ecs_id_t, table->type.count); @@ -7988,21 +8109,23 @@ void flecs_cmd_batch_for_entity( table = &world->store.root; world->info.cmd.batched_command_count ++; break; - case EcsOpClone: - case EcsOpBulkNew: - case EcsOpPath: - case EcsOpDelete: - case EcsOpOnDeleteAction: - case EcsOpEnable: - case EcsOpDisable: - case EcsOpSkip: + case EcsCmdClone: + case EcsCmdBulkNew: + case EcsCmdPath: + case EcsCmdDelete: + case EcsCmdOnDeleteAction: + case EcsCmdEnable: + case EcsCmdDisable: + case EcsCmdEvent: + case EcsCmdSkip: + case EcsCmdModifiedNoHook: break; } /* Add, remove and clear operations can be skipped since they have no * side effects besides adding/removing components */ - if (kind == EcsOpAdd || kind == EcsOpRemove || kind == EcsOpClear) { - cmd->kind = EcsOpSkip; + if (kind == EcsCmdAdd || kind == EcsCmdRemove || kind == EcsCmdClear) { + cmd->kind = EcsCmdSkip; } } while ((cur = next_for_entity)); @@ -8013,10 +8136,15 @@ void flecs_cmd_batch_for_entity( flecs_defer_end(world, &world->stages[0]); flecs_table_diff_builder_clear(diff); - /* If ids were both removed and set, check if there are ids that were both - * set and removed. If so, skip the set command so that the id won't get - * re-added */ - if (has_set && table_diff.removed.count) { + /* If the batch contains set commands, copy the component value from the + * temporary command storage to the actual component storage before OnSet + * observers are invoked. This ensures that for multi-component OnSet + * observers all components are assigned a valid value before the observer + * is invoked. + * This only happens for entities that didn't have the assigned component + * yet, as for entities that did have the component already the value will + * have been assigned directly to the component storage. */ + if (has_set) { cur = start; do { cmd = &cmds[cur]; @@ -8025,35 +8153,64 @@ void flecs_cmd_batch_for_entity( next_for_entity *= -1; } switch(cmd->kind) { - case EcsOpSet: - case EcsOpMut: { - ecs_id_record_t *idr = cmd->idr; - if (!idr) { - idr = flecs_id_record_get(world, cmd->id); + case EcsCmdSet: + case EcsCmdMut: { + flecs_component_ptr_t ptr = {0}; + if (r->table) { + ptr = flecs_get_component_ptr(world, + r->table, ECS_RECORD_TO_ROW(r->row), cmd->id); } - if (!flecs_id_record_get_table(idr, table)) { - /* Component was deleted */ - cmd->kind = EcsOpSkip; - world->info.cmd.batched_command_count --; - world->info.cmd.discard_count ++; + /* It's possible that even though the component was set, the + * command queue also contained a remove command, so before we + * do anything ensure the entity actually has the component. */ + if (ptr.ptr) { + const ecs_type_info_t *ti = ptr.ti; + ecs_move_t move = ti->hooks.move; + if (move) { + move(ptr.ptr, cmd->is._1.value, 1, ti); + ecs_xtor_t dtor = ti->hooks.dtor; + if (dtor) { + dtor(cmd->is._1.value, 1, ti); + cmd->is._1.value = NULL; + } + } else { + ecs_os_memcpy(ptr.ptr, cmd->is._1.value, ti->size); + } + if (cmd->kind == EcsCmdSet) { + /* A set operation is add + copy + modified. We just did + * the add the copy, so the only thing that's left is a + * modified command, which will call the OnSet + * observers. */ + cmd->kind = EcsCmdModified; + } else { + /* If this was a get_mut, nothing's left to be done */ + cmd->kind = EcsCmdSkip; + } + } else { + /* The entity no longer has the component which means that + * there was a remove command for the component in the + * command queue. In that case skip the command. */ + cmd->kind = EcsCmdSkip; } break; } - case EcsOpClone: - case EcsOpBulkNew: - case EcsOpAdd: - case EcsOpRemove: - case EcsOpEmplace: - case EcsOpModified: - case EcsOpAddModified: - case EcsOpPath: - case EcsOpDelete: - case EcsOpClear: - case EcsOpOnDeleteAction: - case EcsOpEnable: - case EcsOpDisable: - case EcsOpSkip: + case EcsCmdClone: + case EcsCmdBulkNew: + case EcsCmdAdd: + case EcsCmdRemove: + case EcsCmdEmplace: + case EcsCmdModified: + case EcsCmdModifiedNoHook: + case EcsCmdAddModified: + case EcsCmdPath: + case EcsCmdDelete: + case EcsCmdClear: + case EcsCmdOnDeleteAction: + case EcsCmdEnable: + case EcsCmdDisable: + case EcsCmdEvent: + case EcsCmdSkip: break; } } while ((cur = next_for_entity)); @@ -8085,22 +8242,16 @@ bool flecs_defer_end( } ecs_stage_t *dst_stage = flecs_stage_from_world(&world); - if (ecs_vec_count(&stage->commands)) { - ecs_vec_t commands = stage->commands; - stage->commands.array = NULL; - stage->commands.count = 0; - stage->commands.size = 0; - - ecs_cmd_t *cmds = ecs_vec_first(&commands); - int32_t i, count = ecs_vec_count(&commands); - ecs_vec_init_t(NULL, &stage->commands, ecs_cmd_t, 0); + ecs_commands_t *commands = stage->cmd; + ecs_vec_t *queue = &commands->queue; - ecs_stack_t stack = stage->defer_stack; - flecs_stack_init(&stage->defer_stack); + if (ecs_vec_count(queue)) { + ecs_cmd_t *cmds = ecs_vec_first(queue); + int32_t i, count = ecs_vec_count(queue); ecs_table_diff_builder_t diff; flecs_table_diff_builder_init(world, &diff); - flecs_sparse_clear(&stage->cmd_entries); + flecs_commands_push(stage); for (i = 0; i < count; i ++) { ecs_cmd_t *cmd = &cmds[i]; @@ -8117,11 +8268,16 @@ bool flecs_defer_end( } } + /* Invalidate entry */ + if (cmd->entry) { + cmd->entry->first = -1; + } + /* If entity is no longer alive, this could be because the queue * contained both a delete and a subsequent add/remove/set which * should be ignored. */ ecs_cmd_kind_t kind = cmd->kind; - if ((kind != EcsOpPath) && ((kind == EcsOpSkip) || (e && !is_alive))) { + if ((kind != EcsCmdPath) && ((kind == EcsCmdSkip) || (e && !is_alive))) { world->info.cmd.discard_count ++; flecs_discard_cmd(world, cmd); continue; @@ -8130,7 +8286,7 @@ bool flecs_defer_end( ecs_id_t id = cmd->id; switch(kind) { - case EcsOpAdd: + case EcsCmdAdd: ecs_assert(id != 0, ECS_INTERNAL_ERROR, NULL); if (flecs_remove_invalid(world, id, &id)) { if (id) { @@ -8144,20 +8300,20 @@ bool flecs_defer_end( ecs_delete(world, e); } break; - case EcsOpRemove: + case EcsCmdRemove: flecs_remove_id(world, e, id); world->info.cmd.remove_count ++; break; - case EcsOpClone: + case EcsCmdClone: ecs_clone(world, e, id, cmd->is._1.clone_value); break; - case EcsOpSet: + case EcsCmdSet: flecs_move_ptr_w_id(world, dst_stage, e, cmd->id, flecs_itosize(cmd->is._1.size), cmd->is._1.value, kind); world->info.cmd.set_count ++; break; - case EcsOpEmplace: + case EcsCmdEmplace: if (merge_to_world) { ecs_emplace_id(world, e, id); } @@ -8166,50 +8322,54 @@ bool flecs_defer_end( cmd->is._1.value, kind); world->info.cmd.get_mut_count ++; break; - case EcsOpMut: + case EcsCmdMut: flecs_move_ptr_w_id(world, dst_stage, e, cmd->id, flecs_itosize(cmd->is._1.size), cmd->is._1.value, kind); world->info.cmd.get_mut_count ++; break; - case EcsOpModified: - flecs_modified_id_if(world, e, id); + case EcsCmdModified: + flecs_modified_id_if(world, e, id, true); + world->info.cmd.modified_count ++; + break; + case EcsCmdModifiedNoHook: + flecs_modified_id_if(world, e, id, false); world->info.cmd.modified_count ++; break; - case EcsOpAddModified: + case EcsCmdAddModified: flecs_add_id(world, e, id); - flecs_modified_id_if(world, e, id); + flecs_modified_id_if(world, e, id, true); world->info.cmd.add_count ++; world->info.cmd.modified_count ++; break; - case EcsOpDelete: { + case EcsCmdDelete: { ecs_delete(world, e); world->info.cmd.delete_count ++; break; } - case EcsOpClear: + case EcsCmdClear: ecs_clear(world, e); world->info.cmd.clear_count ++; break; - case EcsOpOnDeleteAction: + case EcsCmdOnDeleteAction: ecs_defer_begin(world); flecs_on_delete(world, id, e, false); ecs_defer_end(world); world->info.cmd.other_count ++; break; - case EcsOpEnable: + case EcsCmdEnable: ecs_enable_id(world, e, id, true); world->info.cmd.other_count ++; break; - case EcsOpDisable: + case EcsCmdDisable: ecs_enable_id(world, e, id, false); world->info.cmd.other_count ++; break; - case EcsOpBulkNew: + case EcsCmdBulkNew: flecs_flush_bulk_new(world, cmd); world->info.cmd.other_count ++; continue; - case EcsOpPath: + case EcsCmdPath: ecs_ensure(world, e); if (cmd->id) { ecs_add_pair(world, e, EcsChildOf, cmd->id); @@ -8218,7 +8378,14 @@ bool flecs_defer_end( ecs_os_free(cmd->is._1.value); cmd->is._1.value = NULL; break; - case EcsOpSkip: + case EcsCmdEvent: { + ecs_event_desc_t *desc = cmd->is._1.value; + ecs_assert(desc != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_emit((ecs_world_t*)stage, desc); + flecs_free_cmd_event(world, desc); + break; + } + case EcsCmdSkip: break; } @@ -8227,16 +8394,9 @@ bool flecs_defer_end( } } - ecs_vec_fini_t(&stage->allocator, &stage->commands, ecs_cmd_t); - - /* Restore defer queue */ - ecs_vec_clear(&commands); - stage->commands = commands; - - /* Restore stack */ - flecs_stack_fini(&stage->defer_stack); - stage->defer_stack = stack; - flecs_stack_reset(&stage->defer_stack); + flecs_stack_reset(&commands->stack); + ecs_vec_clear(queue); + flecs_commands_pop(stage); flecs_table_diff_builder_fini(world, &diff); } @@ -8256,7 +8416,7 @@ bool flecs_defer_purge( ecs_check(stage != NULL, ECS_INVALID_PARAMETER, NULL); if (!--stage->defer) { - ecs_vec_t commands = stage->commands; + ecs_vec_t commands = stage->cmd->queue; if (ecs_vec_count(&commands)) { ecs_cmd_t *cmds = ecs_vec_first(&commands); @@ -8265,11 +8425,11 @@ bool flecs_defer_purge( flecs_discard_cmd(world, &cmds[i]); } - ecs_vec_fini_t(&stage->allocator, &stage->commands, ecs_cmd_t); + ecs_vec_fini_t(&stage->allocator, &stage->cmd->queue, ecs_cmd_t); ecs_vec_clear(&commands); - flecs_stack_reset(&stage->defer_stack); - flecs_sparse_clear(&stage->cmd_entries); + flecs_stack_reset(&stage->cmd->stack); + flecs_sparse_clear(&stage->cmd->entries); } return true; @@ -10335,14 +10495,14 @@ int flecs_term_finalize( if (term->oper == EcsNot && term->id == ecs_pair(EcsChildOf, EcsAny)) { term->oper = EcsAnd; term->id = ecs_pair(EcsChildOf, 0); - term->second.id = 0; - term->second.flags |= EcsIsEntity; - term->second.flags &= ~EcsIsVariable; + second->id = 0; + second->flags |= EcsIsEntity; + second->flags &= ~EcsIsVariable; } ecs_entity_t first_id = 0; - if (term->first.flags & EcsIsEntity) { - first_id = term->first.id; + if (first->flags & EcsIsEntity) { + first_id = first->id; } term->idr = flecs_query_id_record_get(world, term->id); @@ -10458,6 +10618,46 @@ int flecs_term_finalize( } } + /* Is term trivial */ + bool trivial_term = true; + if (term->oper != EcsAnd) { + trivial_term = false; + } + if (ecs_id_is_wildcard(term->id)) { + if (!(term->idr && term->idr->flags & EcsIdExclusive)) { + trivial_term = false; + } + if (first->flags & EcsIsVariable) { + if (!ecs_id_is_wildcard(first->id) || first->id == EcsAny) { + trivial_term = false; + } + } + if (second->flags & EcsIsVariable) { + if (!ecs_id_is_wildcard(second->id) || second->id == EcsAny) { + trivial_term = false; + } + } + } + + if (term->flags & EcsTermIdInherited) { + trivial_term = false; + } + if (src->trav && src->trav != EcsIsA) { + trivial_term = false; + } + if (first->trav && first->trav != EcsIsA) { + trivial_term = false; + } + if (second->trav && second->trav != EcsIsA) { + trivial_term = false; + } + if (!(src->flags & EcsSelf)) { + trivial_term = false; + } + if (trivial_term) { + ECS_BIT_SET(term->flags, EcsTermIsTrivial); + } + if (flecs_term_verify(world, term, ctx)) { return -1; } @@ -10749,7 +10949,9 @@ int ecs_filter_finalize( ctx.filter = f; f->flags |= EcsFilterMatchOnlyThis; + for (i = 0; i < term_count; i ++) { + bool filter_term = false; ecs_term_t *term = &terms[i]; ctx.term_index = i; if (flecs_term_finalize(world, term, &ctx)) { @@ -10770,18 +10972,12 @@ int ecs_filter_finalize( field_count ++; } - if (term->oper == EcsOr || (i && term[-1].oper == EcsOr)) { - ecs_term_t *first = flecs_filter_or_other_type(f, i); - if (first) { - filter_terms ++; - if (first == &term[-1]) { - filter_terms ++; - } - } - } - term->field_index = field_count - 1; + if (ecs_id_is_wildcard(term->id)) { + f->flags |= EcsFilterHasWildcards; + } + if (ecs_term_match_this(term)) { ECS_BIT_SET(f->flags, EcsFilterMatchThis); } else { @@ -10803,19 +10999,6 @@ int ecs_filter_finalize( term->inout = EcsInOutNone; } - if (term->inout == EcsInOutNone) { - filter_terms ++; - } else if (term->idr) { - if (!term->idr->type_info && !(term->idr->flags & EcsIdUnion)) { - filter_terms ++; - } - } else if (ecs_id_is_tag(world, term->id)) { - if (!ecs_id_is_union(world, term->id)) { - /* Union ids aren't filters because they return their target - * as component value with type ecs_entity_t */ - filter_terms ++; - } - } if ((term->id == EcsWildcard) || (term->id == ecs_pair(EcsWildcard, EcsWildcard))) { @@ -10827,6 +11010,46 @@ int ecs_filter_finalize( } } + if (term->inout == EcsInOutNone) { + filter_term = true; + } else if (term->idr) { + if (!term->idr->type_info && !(term->idr->flags & EcsIdUnion)) { + filter_term = true; + } + } else if (!ecs_id_is_union(world, term->id)) { + /* Union ids aren't filters because they return their target + * as component value with type ecs_entity_t */ + if (ecs_id_is_tag(world, term->id)) { + filter_term = true; + } else if (ECS_PAIR_SECOND(term->id) == EcsWildcard) { + /* If the second element of a pair is a wildcard and the first + * element is not a type, we can't know in advance what the + * type of the term is, so it can't provide data. */ + if (!ecs_get_type_info(world, ecs_pair_first(world, term->id))) { + filter_term = true; + } + } + } + + if (!filter_term) { + if (term->oper == EcsOr || (i && term[-1].oper == EcsOr)) { + ecs_term_t *first = flecs_filter_or_other_type(f, i); + if (first) { + if (first == &term[-1]) { + filter_terms ++; + } + filter_term = true; + } + } + } + + if (filter_term) { + filter_terms ++; + term->flags |= EcsTermNoData; + } else { + f->data_fields |= (1llu << term->field_index); + } + if (term->oper != EcsNot || !ecs_term_match_this(term)) { ECS_BIT_CLEAR(f->flags, EcsFilterMatchAnything); } @@ -10877,7 +11100,7 @@ int ecs_filter_finalize( return -1; } - f->field_count = field_count; + f->field_count = flecs_ito(int8_t, field_count); if (field_count) { for (i = 0; i < term_count; i ++) { @@ -10923,12 +11146,44 @@ int ecs_filter_finalize( f->sizes = NULL; } - if (filter_terms >= term_count) { + ecs_assert(filter_terms <= term_count, ECS_INTERNAL_ERROR, NULL); + if (filter_terms == term_count) { ECS_BIT_SET(f->flags, EcsFilterNoData); } ECS_BIT_COND(f->flags, EcsFilterHasCondSet, cond_set); + /* Check if this is a trivial filter */ + if ((f->flags & EcsFilterMatchOnlyThis)) { + if (!(f->flags & + (EcsFilterHasPred|EcsFilterMatchDisabled|EcsFilterMatchPrefab))) + { + ECS_BIT_SET(f->flags, EcsFilterMatchOnlySelf); + + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + ecs_term_id_t *src = &term->src; + + if (src->flags & EcsUp) { + ECS_BIT_CLEAR(f->flags, EcsFilterMatchOnlySelf); + } + + if (!(term->flags & EcsTermIsTrivial)) { + break; + } + + if (!(f->flags & EcsFilterNoData)) { + if (term->inout == EcsInOutNone) { + break; + } + } + } + if (term_count && (i == term_count)) { + ECS_BIT_SET(f->flags, EcsFilterIsTrivial); + } + } + } + return 0; } @@ -10971,7 +11226,7 @@ void flecs_filter_fini( ecs_term_fini(&filter->terms[i]); } - if (filter->terms_owned) { + if (filter->flags & EcsFilterOwnsTermsStorage) { /* Memory allocated for both terms & sizes */ ecs_os_free(filter->terms); } else { @@ -10981,7 +11236,7 @@ void flecs_filter_fini( filter->terms = NULL; - if (filter->owned) { + if (filter->flags & EcsFilterOwnsStorage) { ecs_os_free(filter); } } @@ -10989,7 +11244,7 @@ void flecs_filter_fini( void ecs_filter_fini( ecs_filter_t *filter) { - if (filter->owned && filter->entity) { + if ((filter->flags & EcsFilterOwnsStorage) && filter->entity) { /* If filter is associated with entity, use poly dtor path */ ecs_delete(filter->world, filter->entity); } else { @@ -11019,10 +11274,10 @@ ecs_filter_t* ecs_filter_init( ecs_poly_init(f, ecs_filter_t); } else { f = ecs_poly_new(ecs_filter_t); - f->owned = true; + f->flags |= EcsFilterOwnsStorage; } if (!storage_terms) { - f->terms_owned = true; + f->flags |= EcsFilterOwnsTermsStorage; } ECS_BIT_COND(f->flags, EcsFilterIsInstanced, desc->instanced); @@ -11053,33 +11308,62 @@ ecs_filter_t* ecs_filter_init( const char *name = NULL; const char *ptr = desc->expr; ecs_term_t term = {0}; + ecs_term_id_t extra_args[ECS_PARSER_MAX_ARGS]; int32_t expr_size = 0; + ecs_os_zeromem(extra_args); + if (entity) { name = ecs_get_name(world, entity); } - while (ptr[0] && (ptr = ecs_parse_term(world, name, expr, ptr, &term))){ + while (ptr[0] && + (ptr = ecs_parse_term(world, name, expr, ptr, &term, extra_args))) + { if (!ecs_term_is_initialized(&term)) { break; } - if (expr_count == expr_size) { - expr_size = expr_size ? expr_size * 2 : 8; - expr_terms = ecs_os_realloc_n(expr_terms, ecs_term_t, expr_size); - } + int32_t arg = 0; + + do { + ecs_assert(arg <= ECS_PARSER_MAX_ARGS, ECS_INTERNAL_ERROR, NULL); + + if (expr_count == expr_size) { + expr_size = expr_size ? expr_size * 2 : 8; + expr_terms = ecs_os_realloc_n(expr_terms, ecs_term_t, expr_size); + } + + ecs_term_t *expr_term = &expr_terms[expr_count ++]; + *expr_term = term; + + if (arg) { + expr_term->src = expr_term[-1].second; + expr_term->second = extra_args[arg - 1]; + + if (expr_term->first.name != NULL) { + expr_term->first.name = ecs_os_strdup( + expr_term->first.name); + } + if (expr_term->src.name != NULL) { + expr_term->src.name = ecs_os_strdup( + expr_term->src.name); + } + } + } while (ecs_term_id_is_set(&extra_args[arg ++])); - expr_terms[expr_count ++] = term; if (ptr[0] == '\n') { break; } + + ecs_os_zeromem(extra_args); } if (!ptr) { /* Set terms in filter object to make sur they get cleaned up */ f->terms = expr_terms; - f->term_count = expr_count; - f->terms_owned = true; + f->term_count = flecs_ito(int8_t, expr_count); + f->flags |= EcsFilterOwnsTermsStorage; goto error; } #else @@ -11095,15 +11379,16 @@ ecs_filter_t* ecs_filter_init( if (term_count || expr_count) { /* Allocate storage for terms and sizes array */ if (!storage_terms) { - ecs_assert(f->terms_owned == true, ECS_INTERNAL_ERROR, NULL); - f->term_count = term_count + expr_count; + ecs_assert(f->flags & EcsFilterOwnsTermsStorage, + ECS_INTERNAL_ERROR, NULL); + f->term_count = flecs_ito(int8_t, term_count + expr_count); ecs_size_t terms_size = ECS_SIZEOF(ecs_term_t) * f->term_count; ecs_size_t sizes_size = ECS_SIZEOF(int32_t) * f->term_count; f->terms = ecs_os_calloc(terms_size + sizes_size); f->sizes = ECS_OFFSET(f->terms, terms_size); } else { f->terms = storage_terms; - f->term_count = storage_count; + f->term_count = flecs_ito(int8_t, storage_count); f->sizes = ecs_os_calloc_n(ecs_size_t, term_count); } @@ -11138,7 +11423,7 @@ ecs_filter_t* ecs_filter_init( f->dtor = (ecs_poly_dtor_t)flecs_filter_fini; f->entity = entity; - if (entity && f->owned) { + if (entity && (f->flags & EcsFilterOwnsStorage)) { EcsPoly *poly = ecs_poly_bind(world, entity, ecs_filter_t); poly->poly = f; ecs_poly_modified(world, entity, ecs_filter_t); @@ -11166,7 +11451,7 @@ void ecs_filter_copy( ecs_size_t sizes_size = ECS_SIZEOF(int32_t) * term_count; dst->terms = ecs_os_malloc(terms_size + sizes_size); dst->sizes = ECS_OFFSET(dst->terms, terms_size); - dst->terms_owned = true; + dst->flags |= EcsFilterOwnsTermsStorage; ecs_os_memcpy_n(dst->sizes, src->sizes, int32_t, term_count); for (i = 0; i < term_count; i ++) { @@ -11187,10 +11472,10 @@ void ecs_filter_move( if (src) { *dst = *src; - if (src->terms_owned) { + if (src->flags & EcsFilterOwnsTermsStorage) { dst->terms = src->terms; dst->sizes = src->sizes; - dst->terms_owned = true; + dst->flags |= EcsFilterOwnsTermsStorage; } else { ecs_filter_copy(dst, src); } @@ -11562,7 +11847,12 @@ bool flecs_term_match_table( oper = EcsOptional; } - match_table = ecs_get_table(world, src_id); + if (ecs_is_alive(world, src_id)) { + match_table = ecs_get_table(world, src_id); + } else { + match_table = NULL; + } + if (match_table) { } else if (oper != EcsOptional) { return false; @@ -11620,6 +11910,7 @@ bool flecs_term_match_table( result = true; } + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); if ((column == -1) && (src->flags & EcsUp) && (table->flags & EcsTableHasTarget)) { ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL); ecs_id_t rel = ECS_PAIR_SECOND(table->type.array[table->_->ft_offset]); @@ -11709,7 +12000,11 @@ bool flecs_filter_match_table( } if (!ecs_term_match_this(term)) { - match_table = ecs_get_table(world, src_id); + if (ecs_is_alive(world, src_id)) { + match_table = ecs_get_table(world, src_id); + } else { + match_table = NULL; + } } else { if (ECS_BIT_IS_SET(iter_flags, EcsIterIgnoreThis)) { continue; @@ -11866,7 +12161,7 @@ ecs_iter_t ecs_term_iter( * they don't get overwritten by flecs_iter_validate. * * Note: the reason the term iterator doesn't use the iterator cache itself - * (which could easily accomodate a single term) is that the filter iterator + * (which could easily accommodate a single term) is that the filter iterator * is built on top of the term iterator. The private cache of the term * iterator keeps the filter iterator code simple, as it doesn't need to * worry about the term iter overwriting the iterator fields. */ @@ -12906,8 +13201,8 @@ bool flecs_iter_next_row( int t, field_count = it->field_count; for (t = 0; t < field_count; t ++) { - int32_t column = it->columns[t]; - if (column >= 0) { + ecs_entity_t src = it->sources[t]; + if (!src) { void *ptr = it->ptrs[t]; if (ptr) { it->ptrs[t] = ECS_OFFSET(ptr, it->sizes[t]); @@ -13240,6 +13535,9 @@ ecs_entity_t ecs_iter_get_var( ecs_entity_t e = var->entity; if (!e) { ecs_table_t *table = var->range.table; + if (!table && !var_id) { + table = it->table; + } if (table) { if ((var->range.count == 1) || (ecs_table_count(table) == 1)) { ecs_assert(ecs_table_count(table) > var->range.offset, @@ -13267,6 +13565,10 @@ ecs_table_t* ecs_iter_get_var_as_table( ecs_var_t *var = &it->variables[var_id]; ecs_table_t *table = var->range.table; + if (!table && !var_id) { + table = it->table; + } + if (!table) { /* If table is not set, try to get table from entity */ ecs_entity_t e = var->entity; @@ -13311,6 +13613,10 @@ ecs_table_range_t ecs_iter_get_var_as_range( ecs_var_t *var = &it->variables[var_id]; ecs_table_t *table = var->range.table; + if (!table && !var_id) { + table = it->table; + } + if (!table) { ecs_entity_t e = var->entity; if (e) { @@ -13705,7 +14011,7 @@ bool ecs_worker_next( /** * @file misc.c - * @brief Miscallaneous functions. + * @brief Miscellaneous functions. */ #include @@ -14109,25 +14415,30 @@ int32_t flecs_event_observers_get( /* Populate array with observer sets matching the id */ int32_t count = 0; - iders[0] = flecs_event_id_record_get_if(er, EcsAny); - count += iders[count] != 0; + + if (id != EcsAny) { + iders[0] = flecs_event_id_record_get_if(er, EcsAny); + count += iders[count] != 0; + } iders[count] = flecs_event_id_record_get_if(er, id); count += iders[count] != 0; - if (ECS_IS_PAIR(id)) { - ecs_id_t id_fwc = ecs_pair(EcsWildcard, ECS_PAIR_SECOND(id)); - ecs_id_t id_swc = ecs_pair(ECS_PAIR_FIRST(id), EcsWildcard); - ecs_id_t id_pwc = ecs_pair(EcsWildcard, EcsWildcard); - iders[count] = flecs_event_id_record_get_if(er, id_fwc); - count += iders[count] != 0; - iders[count] = flecs_event_id_record_get_if(er, id_swc); - count += iders[count] != 0; - iders[count] = flecs_event_id_record_get_if(er, id_pwc); - count += iders[count] != 0; - } else { - iders[count] = flecs_event_id_record_get_if(er, EcsWildcard); - count += iders[count] != 0; + if (id != EcsAny) { + if (ECS_IS_PAIR(id)) { + ecs_id_t id_fwc = ecs_pair(EcsWildcard, ECS_PAIR_SECOND(id)); + ecs_id_t id_swc = ecs_pair(ECS_PAIR_FIRST(id), EcsWildcard); + ecs_id_t id_pwc = ecs_pair(EcsWildcard, EcsWildcard); + iders[count] = flecs_event_id_record_get_if(er, id_fwc); + count += iders[count] != 0; + iders[count] = flecs_event_id_record_get_if(er, id_swc); + count += iders[count] != 0; + iders[count] = flecs_event_id_record_get_if(er, id_pwc); + count += iders[count] != 0; + } else { + iders[count] = flecs_event_id_record_get_if(er, EcsWildcard); + count += iders[count] != 0; + } } return count; @@ -14209,9 +14520,9 @@ void flecs_emit_propagate_id( continue; } - ecs_record_t **records = ecs_vec_first(&table->data.records); + ecs_entity_t *entities = ecs_vec_first(&table->data.entities); for (e = 0; e < entity_count; e ++) { - ecs_record_t *r = records[e]; + ecs_record_t *r = flecs_entities_get(world, entities[e]); ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); ecs_id_record_t *idr_t = r->idr; if (idr_t) { @@ -14298,10 +14609,11 @@ void flecs_emit_propagate_invalidate_tables( } int32_t e, entity_count = ecs_table_count(table); - ecs_record_t **records = ecs_vec_first(&table->data.records); + ecs_entity_t *entities = ecs_vec_first(&table->data.entities); for (e = 0; e < entity_count; e ++) { - ecs_id_record_t *idr_t = records[e]->idr; + ecs_record_t *r = flecs_entities_get(world, entities[e]); + ecs_id_record_t *idr_t = r->idr; if (idr_t) { /* Only notify for entities that are used in pairs with * traversable relationships */ @@ -14318,11 +14630,11 @@ void flecs_emit_propagate_invalidate( int32_t offset, int32_t count) { - ecs_record_t **recs = ecs_vec_get_t(&table->data.records, - ecs_record_t*, offset); + ecs_entity_t *entities = ecs_vec_get_t(&table->data.entities, + ecs_entity_t, offset); int32_t i; for (i = 0; i < count; i ++) { - ecs_record_t *record = recs[i]; + ecs_record_t *record = flecs_entities_get(world, entities[i]); if (!record) { /* If the event is emitted after a bulk operation, it's possible * that it hasn't been populated with entities yet. */ @@ -14898,7 +15210,6 @@ void flecs_emit_forward( ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(flecs_entities_get(world, elem->src) == r, ECS_INTERNAL_ERROR, NULL); - ecs_dbg_assert(r->table == elem->table, ECS_INTERNAL_ERROR, NULL); int32_t offset = ECS_RECORD_TO_ROW(r->row); @@ -15027,9 +15338,6 @@ void flecs_emit( * get notified of any new reachable components though the relationship. */ bool can_forward = event != EcsOnSet; - /* Set if event has been propagated */ - bool propagated = false; - /* Does table has observed entities */ bool has_observed = table_flags & EcsTableHasTraversable; @@ -15041,6 +15349,10 @@ void flecs_emit( ecs_event_id_record_t *iders[5] = {0}; int32_t unset_count = 0; + if (count && can_forward && has_observed) { + flecs_emit_propagate_invalidate(world, table, offset, count); + } + repeat_event: /* This is the core event logic, which is executed for each event. By * default this is just the event kind from the ecs_event_desc_t struct, but @@ -15221,10 +15533,6 @@ void flecs_emit( continue; } - /* If event is propagated, we don't have to manually invalidate entities - * lower in the tree(s). */ - propagated = true; - /* The table->traversable_count value indicates if the table contains any * entities that are used as targets of traversable relationships. If the * entity/entities for which the event was generated is used as such a @@ -15232,10 +15540,8 @@ void flecs_emit( ecs_entity_t *entities = it.entities; it.entities = NULL; - ecs_record_t **recs = ecs_vec_get_t(&storage->records, - ecs_record_t*, offset); for (r = 0; r < count; r ++) { - ecs_record_t *record = recs[r]; + ecs_record_t *record = flecs_entities_get(world, entities[r]); if (!record) { /* If the event is emitted after a bulk operation, it's possible * that it hasn't been populated with entities yet. */ @@ -15260,10 +15566,6 @@ void flecs_emit( it.sources[0] = 0; } - if (count && can_forward && has_observed && !propagated) { - flecs_emit_propagate_invalidate(world, table, offset, count); - } - can_override = false; /* Don't override twice */ can_unset = false; /* Don't unset twice */ can_forward = false; /* Don't forward twice */ @@ -15297,6 +15599,10 @@ void ecs_emit( ecs_event_desc_t *desc) { ecs_world_t *world = ECS_CONST_CAST(ecs_world_t*, ecs_get_world(stage)); + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(!(desc->param && desc->const_param), ECS_INVALID_PARAMETER, + "cannot set param and const_param at the same time"); + if (desc->entity) { ecs_assert(desc->table == NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(desc->offset == 0, ECS_INVALID_PARAMETER, NULL); @@ -15311,10 +15617,45 @@ void ecs_emit( desc->offset = ECS_RECORD_TO_ROW(r->row); desc->count = 1; } + if (!desc->observable) { desc->observable = world; } + + ecs_type_t default_ids = (ecs_type_t){ + .count = 1, + .array = (ecs_id_t[]){ EcsAny } + }; + + if (!desc->ids || !desc->ids->count) { + desc->ids = &default_ids; + } + + if (desc->const_param) { + desc->param = ECS_CONST_CAST(void*, desc->const_param); + desc->const_param = NULL; + } + flecs_emit(world, stage, desc); + + if (desc->ids == &default_ids) { + desc->ids = NULL; + } +error: + return; +} + +void ecs_enqueue( + ecs_world_t *world, + ecs_event_desc_t *desc) +{ + ecs_check(ecs_is_deferred(world), ECS_INVALID_PARAMETER, + "can't enqueue if not in deferred mode"); + ecs_stage_t *stage = flecs_stage_from_world(&world); + + flecs_enqueue(world, stage, desc); +error: + return; } /** @@ -16246,7 +16587,8 @@ ecs_entity_t ecs_observer_init( ecs_get_name(world, entity)); } } else { - ecs_observer_t *observer = ecs_poly(poly->poly, ecs_observer_t); + ecs_poly_assert(poly->poly, ecs_observer_t); + ecs_observer_t *observer = (ecs_observer_t*)poly->poly; if (desc->run) { observer->run = desc->run; @@ -16361,18 +16703,10 @@ static bool ecs_os_api_initialized = false; static bool ecs_os_api_initializing = false; static int ecs_os_api_init_count = 0; -#ifndef __EMSCRIPTEN__ ecs_os_api_t ecs_os_api = { .flags_ = EcsOsApiHighResolutionTimer | EcsOsApiLogWithColors, .log_level_ = -1 /* Disable tracing by default, but log warnings/errors */ }; -#else -/* Disable colors by default for emscripten */ -ecs_os_api_t ecs_os_api = { - .flags_ = EcsOsApiHighResolutionTimer, - .log_level_ = -1 /* Disable tracing by default, but log warnings/errors */ -}; -#endif int64_t ecs_os_api_malloc_count = 0; int64_t ecs_os_api_realloc_count = 0; @@ -17071,27 +17405,6 @@ ecs_poly_t* ecs_poly_get_( return NULL; } -#ifndef FLECS_NDEBUG -#define assert_object(cond, file, line, type_name)\ - ecs_assert_((cond), ECS_INVALID_PARAMETER, #cond, file, line, type_name);\ - assert(cond) - -void* ecs_poly_assert_( - const ecs_poly_t *poly, - int32_t type, - const char *file, - int32_t line) -{ - assert_object(poly != NULL, file, line, 0); - - const ecs_header_t *hdr = poly; - const char *type_name = hdr->mixins->type_name; - assert_object(hdr->magic == ECS_OBJECT_MAGIC, file, line, type_name); - assert_object(hdr->type == type, file, line, type_name); - return ECS_CONST_CAST(ecs_poly_t*, poly); -} -#endif - bool ecs_poly_is_( const ecs_poly_t *poly, int32_t type) @@ -17256,12 +17569,26 @@ ecs_query_table_match_t* flecs_query_find_group_insertion_node( ecs_map_iter_t it = ecs_map_iter(&query->groups); ecs_query_table_list_t *list, *closest_list = NULL; uint64_t id, closest_id = 0; + + bool desc = false; + + if (query->cascade_by) { + desc = (query->filter.terms[ + query->cascade_by - 1].src.flags & EcsDesc) != 0; + } /* Find closest smaller group id */ while (ecs_map_next(&it)) { id = ecs_map_key(&it); - if (id >= group_id) { - continue; + + if (!desc) { + if (id >= group_id) { + continue; + } + } else { + if (id <= group_id) { + continue; + } } list = ecs_map_ptr(&it); @@ -17270,7 +17597,14 @@ ecs_query_table_match_t* flecs_query_find_group_insertion_node( continue; } - if (!closest_list || ((group_id - id) < (group_id - closest_id))) { + bool comp; + if (!desc) { + comp = ((group_id - id) < (group_id - closest_id)); + } else { + comp = ((group_id - id) > (group_id - closest_id)); + } + + if (!closest_list || comp) { closest_id = id; closest_list = list; } @@ -19993,7 +20327,6 @@ int32_t flecs_type_offset_search( return -1; } -static bool flecs_type_can_inherit_id( const ecs_world_t *world, const ecs_table_t *table, @@ -20341,60 +20674,54 @@ int32_t flecs_relation_depth( static -ecs_cmd_t* flecs_cmd_alloc( +ecs_cmd_t* flecs_cmd_new( ecs_stage_t *stage) { - ecs_cmd_t *cmd = ecs_vec_append_t(&stage->allocator, &stage->commands, + ecs_cmd_t *cmd = ecs_vec_append_t(&stage->allocator, &stage->cmd->queue, ecs_cmd_t); - ecs_os_zeromem(cmd); + cmd->is._1.value = NULL; + cmd->next_for_entity = 0; + cmd->entry = NULL; return cmd; } static -ecs_cmd_t* flecs_cmd_new( +ecs_cmd_t* flecs_cmd_new_batched( ecs_stage_t *stage, - ecs_entity_t e, - bool is_delete, - bool can_batch) + ecs_entity_t e) { - if (e) { - ecs_vec_t *cmds = &stage->commands; - ecs_cmd_entry_t *entry = flecs_sparse_try_t( - &stage->cmd_entries, ecs_cmd_entry_t, e); - int32_t cur = ecs_vec_count(cmds); - if (entry) { - int32_t last = entry->last; - if (entry->last == -1) { - /* Entity was deleted, don't insert command */ - return NULL; - } + ecs_vec_t *cmds = &stage->cmd->queue; + ecs_cmd_entry_t *entry = flecs_sparse_get_any_t( + &stage->cmd->entries, ecs_cmd_entry_t, e); - if (can_batch) { - ecs_cmd_t *arr = ecs_vec_first_t(cmds, ecs_cmd_t); - ecs_assert(arr[last].entity == e, ECS_INTERNAL_ERROR, NULL); - ecs_cmd_t *last_op = &arr[last]; - last_op->next_for_entity = cur; - if (last == entry->first) { - /* Flip sign bit so flush logic can tell which command - * is the first for an entity */ - last_op->next_for_entity *= -1; - } - } - } else if (can_batch || is_delete) { - entry = flecs_sparse_ensure_t(&stage->cmd_entries, - ecs_cmd_entry_t, e); + int32_t cur = ecs_vec_count(cmds); + ecs_cmd_t *cmd = flecs_cmd_new(stage); + if (entry) { + if (entry->first == -1) { + /* Existing but invalidated entry */ entry->first = cur; + cmd->entry = entry; + } else { + int32_t last = entry->last; + ecs_cmd_t *arr = ecs_vec_first_t(cmds, ecs_cmd_t); + ecs_assert(arr[last].entity == e, ECS_INTERNAL_ERROR, NULL); + ecs_cmd_t *last_op = &arr[last]; + last_op->next_for_entity = cur; + if (last == entry->first) { + /* Flip sign bit so flush logic can tell which command + * is the first for an entity */ + last_op->next_for_entity *= -1; + } } - if (can_batch) { - entry->last = cur; - } - if (is_delete) { - /* Prevent insertion of more commands for entity */ - entry->last = -1; - } + } else { + cmd->entry = entry = flecs_sparse_ensure_fast_t( + &stage->cmd->entries, ecs_cmd_entry_t, e); + entry->first = cur; } - return flecs_cmd_alloc(stage); + entry->last = cur; + + return cmd; } static @@ -20496,9 +20823,9 @@ bool flecs_defer_modified( ecs_id_t id) { if (flecs_defer_cmd(stage)) { - ecs_cmd_t *cmd = flecs_cmd_new(stage, entity, false, true); + ecs_cmd_t *cmd = flecs_cmd_new_batched(stage, entity); if (cmd) { - cmd->kind = EcsOpModified; + cmd->kind = EcsCmdModified; cmd->id = id; cmd->entity = entity; } @@ -20514,13 +20841,11 @@ bool flecs_defer_clone( bool clone_value) { if (flecs_defer_cmd(stage)) { - ecs_cmd_t *cmd = flecs_cmd_new(stage, entity, false, false); - if (cmd) { - cmd->kind = EcsOpClone; - cmd->id = src; - cmd->entity = entity; - cmd->is._1.clone_value = clone_value; - } + ecs_cmd_t *cmd = flecs_cmd_new(stage); + cmd->kind = EcsCmdClone; + cmd->id = src; + cmd->entity = entity; + cmd->is._1.clone_value = clone_value; return true; } return false; @@ -20533,13 +20858,11 @@ bool flecs_defer_path( const char *name) { if (stage->defer > 0) { - ecs_cmd_t *cmd = flecs_cmd_new(stage, entity, false, false); - if (cmd) { - cmd->kind = EcsOpPath; - cmd->entity = entity; - cmd->id = parent; - cmd->is._1.value = ecs_os_strdup(name); - } + ecs_cmd_t *cmd = flecs_cmd_new(stage); + cmd->kind = EcsCmdPath; + cmd->entity = entity; + cmd->id = parent; + cmd->is._1.value = ecs_os_strdup(name); return true; } return false; @@ -20550,11 +20873,9 @@ bool flecs_defer_delete( ecs_entity_t entity) { if (flecs_defer_cmd(stage)) { - ecs_cmd_t *cmd = flecs_cmd_new(stage, entity, true, false); - if (cmd) { - cmd->kind = EcsOpDelete; - cmd->entity = entity; - } + ecs_cmd_t *cmd = flecs_cmd_new(stage); + cmd->kind = EcsCmdDelete; + cmd->entity = entity; return true; } return false; @@ -20565,11 +20886,9 @@ bool flecs_defer_clear( ecs_entity_t entity) { if (flecs_defer_cmd(stage)) { - ecs_cmd_t *cmd = flecs_cmd_new(stage, entity, false, true); - if (cmd) { - cmd->kind = EcsOpClear; - cmd->entity = entity; - } + ecs_cmd_t *cmd = flecs_cmd_new_batched(stage, entity); + cmd->kind = EcsCmdClear; + cmd->entity = entity; return true; } return false; @@ -20581,8 +20900,8 @@ bool flecs_defer_on_delete_action( ecs_entity_t action) { if (flecs_defer_cmd(stage)) { - ecs_cmd_t *cmd = flecs_cmd_alloc(stage); - cmd->kind = EcsOpOnDeleteAction; + ecs_cmd_t *cmd = flecs_cmd_new(stage); + cmd->kind = EcsCmdOnDeleteAction; cmd->id = id; cmd->entity = action; return true; @@ -20597,12 +20916,10 @@ bool flecs_defer_enable( bool enable) { if (flecs_defer_cmd(stage)) { - ecs_cmd_t *cmd = flecs_cmd_new(stage, entity, false, false); - if (cmd) { - cmd->kind = enable ? EcsOpEnable : EcsOpDisable; - cmd->entity = entity; - cmd->id = id; - } + ecs_cmd_t *cmd = flecs_cmd_new(stage); + cmd->kind = enable ? EcsCmdEnable : EcsCmdDisable; + cmd->entity = entity; + cmd->id = id; return true; } return false; @@ -20627,14 +20944,12 @@ bool flecs_defer_bulk_new( *ids_out = ids; /* Store data in op */ - ecs_cmd_t *cmd = flecs_cmd_alloc(stage); - if (cmd) { - cmd->kind = EcsOpBulkNew; - cmd->id = id; - cmd->is._n.entities = ids; - cmd->is._n.count = count; - } - + ecs_cmd_t *cmd = flecs_cmd_new(stage); + cmd->kind = EcsCmdBulkNew; + cmd->id = id; + cmd->is._n.entities = ids; + cmd->is._n.count = count; + cmd->entity = 0; return true; } return false; @@ -20647,12 +20962,10 @@ bool flecs_defer_add( { if (flecs_defer_cmd(stage)) { ecs_assert(id != 0, ECS_INTERNAL_ERROR, NULL); - ecs_cmd_t *cmd = flecs_cmd_new(stage, entity, false, true); - if (cmd) { - cmd->kind = EcsOpAdd; - cmd->id = id; - cmd->entity = entity; - } + ecs_cmd_t *cmd = flecs_cmd_new_batched(stage, entity); + cmd->kind = EcsCmdAdd; + cmd->id = id; + cmd->entity = entity; return true; } return false; @@ -20665,12 +20978,10 @@ bool flecs_defer_remove( { if (flecs_defer_cmd(stage)) { ecs_assert(id != 0, ECS_INTERNAL_ERROR, NULL); - ecs_cmd_t *cmd = flecs_cmd_new(stage, entity, false, true); - if (cmd) { - cmd->kind = EcsOpRemove; - cmd->id = id; - cmd->entity = entity; - } + ecs_cmd_t *cmd = flecs_cmd_new_batched(stage, entity); + cmd->kind = EcsCmdRemove; + cmd->id = id; + cmd->entity = entity; return true; } return false; @@ -20683,20 +20994,9 @@ void* flecs_defer_set( ecs_entity_t entity, ecs_id_t id, ecs_size_t size, - void *value, - bool need_value) + void *value) { - ecs_cmd_t *cmd = flecs_cmd_new(stage, entity, false, true); - if (!cmd) { - if (need_value) { - /* Entity is deleted by a previous command, but we still need to - * return a temporary storage to the application. */ - cmd_kind = EcsOpSkip; - } else { - /* No value needs to be returned, we can drop the command */ - return NULL; - } - } + ecs_cmd_t *cmd = flecs_cmd_new_batched(stage, entity); /* Find type info for id */ const ecs_type_info_t *ti = NULL; @@ -20752,14 +21052,14 @@ void* flecs_defer_set( /* Get existing value from storage */ void *cmd_value = existing; - bool emplace = cmd_kind == EcsOpEmplace; + bool emplace = cmd_kind == EcsCmdEmplace; /* If the component does not yet exist, create a temporary value. This is * necessary so we can store a component value in the deferred command, * without adding the component to the entity which is not allowed in * deferred mode. */ if (!existing) { - ecs_stack_t *stack = &stage->defer_stack; + ecs_stack_t *stack = &stage->cmd->stack; cmd_value = flecs_stack_alloc(stack, size, ti->alignment); /* If the component doesn't yet exist, construct it and move the @@ -20827,7 +21127,7 @@ void* flecs_defer_set( * already deleted. */ return cmd_value; } - cmd = flecs_cmd_alloc(stage); + cmd = flecs_cmd_new(stage); } if (!existing) { @@ -20842,10 +21142,10 @@ void* flecs_defer_set( /* If component already exists, still insert an Add command to ensure * that any preceding remove commands won't remove the component. If the * operation is a set, also insert a Modified command. */ - if (cmd_kind == EcsOpSet) { - cmd->kind = EcsOpAddModified; + if (cmd_kind == EcsCmdSet) { + cmd->kind = EcsCmdAddModified; } else { - cmd->kind = EcsOpAdd; + cmd->kind = EcsCmdAdd; } cmd->id = id; cmd->entity = entity; @@ -20856,6 +21156,62 @@ void* flecs_defer_set( return NULL; } +void flecs_enqueue( + ecs_world_t *world, + ecs_stage_t *stage, + ecs_event_desc_t *desc) +{ + ecs_cmd_t *cmd = flecs_cmd_new(stage); + cmd->kind = EcsCmdEvent; + cmd->entity = desc->entity; + + ecs_stack_t *stack = &stage->cmd->stack; + ecs_event_desc_t *desc_cmd = flecs_stack_alloc_t(stack, ecs_event_desc_t); + ecs_os_memcpy_t(desc_cmd, desc, ecs_event_desc_t); + + if (desc->ids && desc->ids->count != 0) { + ecs_type_t *type_cmd = flecs_stack_alloc_t(stack, ecs_type_t); + int32_t id_count = desc->ids->count; + type_cmd->count = id_count; + type_cmd->array = flecs_stack_alloc_n(stack, ecs_id_t, id_count); + ecs_os_memcpy_n(type_cmd->array, desc->ids->array, ecs_id_t, id_count); + desc_cmd->ids = type_cmd; + } else { + desc_cmd->ids = NULL; + } + + cmd->is._1.value = desc_cmd; + cmd->is._1.size = ECS_SIZEOF(ecs_event_desc_t); + + if (desc->param || desc->const_param) { + ecs_assert(!(desc->const_param && desc->param), ECS_INVALID_PARAMETER, + "cannot set param and const_param at the same time"); + + const ecs_type_info_t *ti = ecs_get_type_info(world, desc->event); + ecs_assert(ti != NULL, ECS_INVALID_PARAMETER, + "can only enqueue events with data for events that are components"); + + void *param_cmd = flecs_stack_alloc(stack, ti->size, ti->alignment); + ecs_assert(param_cmd != NULL, ECS_INTERNAL_ERROR, NULL); + if (desc->param) { + if (ti->hooks.move_ctor) { + ti->hooks.move_ctor(param_cmd, desc->param, 1, ti); + } else { + ecs_os_memcpy(param_cmd, desc->param, ti->size); + } + } else { + if (ti->hooks.copy_ctor) { + ti->hooks.copy_ctor(param_cmd, desc->const_param, 1, ti); + } else { + ecs_os_memcpy(param_cmd, desc->const_param, ti->size); + } + } + + desc_cmd->param = param_cmd; + desc_cmd->const_param = NULL; + } +} + void flecs_stage_merge_post_frame( ecs_world_t *world, ecs_stage_t *stage) @@ -20870,6 +21226,46 @@ void flecs_stage_merge_post_frame( ecs_vec_clear(&stage->post_frame_actions); } +static +void flecs_commands_init( + ecs_stage_t *stage, + ecs_commands_t *cmd) +{ + flecs_stack_init(&cmd->stack); + ecs_vec_init_t(&stage->allocator, &cmd->queue, ecs_cmd_t, 0); + flecs_sparse_init_t(&cmd->entries, &stage->allocator, + &stage->allocators.cmd_entry_chunk, ecs_cmd_entry_t); +} + +static +void flecs_commands_fini( + ecs_stage_t *stage, + ecs_commands_t *cmd) +{ + /* Make sure stage has no unmerged data */ + ecs_assert(ecs_vec_count(&stage->cmd->queue) == 0, ECS_INTERNAL_ERROR, NULL); + + flecs_stack_fini(&cmd->stack); + ecs_vec_fini_t(&stage->allocator, &cmd->queue, ecs_cmd_t); + flecs_sparse_fini(&cmd->entries); +} + +void flecs_commands_push( + ecs_stage_t *stage) +{ + int32_t sp = ++ stage->cmd_sp; + ecs_assert(sp < ECS_MAX_DEFER_STACK, ECS_INTERNAL_ERROR, NULL); + stage->cmd = &stage->cmd_stack[sp]; +} + +void flecs_commands_pop( + ecs_stage_t *stage) +{ + int32_t sp = -- stage->cmd_sp; + ecs_assert(sp >= 0, ECS_INTERNAL_ERROR, NULL); + stage->cmd = &stage->cmd_stack[sp]; +} + void flecs_stage_init( ecs_world_t *world, ecs_stage_t *stage) @@ -20882,7 +21278,6 @@ void flecs_stage_init( stage->auto_merge = true; stage->async = false; - flecs_stack_init(&stage->defer_stack); flecs_stack_init(&stage->allocators.iter_stack); flecs_stack_init(&stage->allocators.deser_stack); flecs_allocator_init(&stage->allocator); @@ -20890,10 +21285,14 @@ void flecs_stage_init( FLECS_SPARSE_PAGE_SIZE); ecs_allocator_t *a = &stage->allocator; - ecs_vec_init_t(a, &stage->commands, ecs_cmd_t, 0); ecs_vec_init_t(a, &stage->post_frame_actions, ecs_action_elem_t, 0); - flecs_sparse_init_t(&stage->cmd_entries, &stage->allocator, - &stage->allocators.cmd_entry_chunk, ecs_cmd_entry_t); + + int32_t i; + for (i = 0; i < ECS_MAX_DEFER_STACK; i ++) { + flecs_commands_init(stage, &stage->cmd_stack[i]); + } + + stage->cmd = &stage->cmd_stack[0]; } void flecs_stage_fini( @@ -20904,19 +21303,19 @@ void flecs_stage_fini( ecs_poly_assert(world, ecs_world_t); ecs_poly_assert(stage, ecs_stage_t); - /* Make sure stage has no unmerged data */ - ecs_assert(ecs_vec_count(&stage->commands) == 0, ECS_INTERNAL_ERROR, NULL); - ecs_poly_fini(stage, ecs_stage_t); - flecs_sparse_fini(&stage->cmd_entries); - ecs_allocator_t *a = &stage->allocator; - ecs_vec_fini_t(a, &stage->commands, ecs_cmd_t); + ecs_vec_fini_t(a, &stage->post_frame_actions, ecs_action_elem_t); ecs_vec_fini(NULL, &stage->variables, 0); ecs_vec_fini(NULL, &stage->operations, 0); - flecs_stack_fini(&stage->defer_stack); + + int32_t i; + for (i = 0; i < ECS_MAX_DEFER_STACK; i ++) { + flecs_commands_fini(stage, &stage->cmd_stack[i]); + } + flecs_stack_fini(&stage->allocators.iter_stack); flecs_stack_fini(&stage->allocators.deser_stack); flecs_ballocator_fini(&stage->allocators.cmd_entry_chunk); @@ -21562,33 +21961,34 @@ const ecs_entity_t ecs_id(ecs_f32_t) = FLECS_HI_COMPONENT_ID + 93; const ecs_entity_t ecs_id(ecs_f64_t) = FLECS_HI_COMPONENT_ID + 94; const ecs_entity_t ecs_id(ecs_string_t) = FLECS_HI_COMPONENT_ID + 95; const ecs_entity_t ecs_id(ecs_entity_t) = FLECS_HI_COMPONENT_ID + 96; +const ecs_entity_t ecs_id(ecs_id_t) = FLECS_HI_COMPONENT_ID + 97; /** Meta module component ids */ -const ecs_entity_t ecs_id(EcsMetaType) = FLECS_HI_COMPONENT_ID + 97; -const ecs_entity_t ecs_id(EcsMetaTypeSerialized) = FLECS_HI_COMPONENT_ID + 98; -const ecs_entity_t ecs_id(EcsPrimitive) = FLECS_HI_COMPONENT_ID + 99; -const ecs_entity_t ecs_id(EcsEnum) = FLECS_HI_COMPONENT_ID + 100; -const ecs_entity_t ecs_id(EcsBitmask) = FLECS_HI_COMPONENT_ID + 101; -const ecs_entity_t ecs_id(EcsMember) = FLECS_HI_COMPONENT_ID + 102; -const ecs_entity_t ecs_id(EcsMemberRanges) = FLECS_HI_COMPONENT_ID + 103; -const ecs_entity_t ecs_id(EcsStruct) = FLECS_HI_COMPONENT_ID + 104; -const ecs_entity_t ecs_id(EcsArray) = FLECS_HI_COMPONENT_ID + 105; -const ecs_entity_t ecs_id(EcsVector) = FLECS_HI_COMPONENT_ID + 106; -const ecs_entity_t ecs_id(EcsOpaque) = FLECS_HI_COMPONENT_ID + 107; -const ecs_entity_t ecs_id(EcsUnit) = FLECS_HI_COMPONENT_ID + 108; -const ecs_entity_t ecs_id(EcsUnitPrefix) = FLECS_HI_COMPONENT_ID + 109; -const ecs_entity_t EcsConstant = FLECS_HI_COMPONENT_ID + 110; -const ecs_entity_t EcsQuantity = FLECS_HI_COMPONENT_ID + 111; +const ecs_entity_t ecs_id(EcsMetaType) = FLECS_HI_COMPONENT_ID + 98; +const ecs_entity_t ecs_id(EcsMetaTypeSerialized) = FLECS_HI_COMPONENT_ID + 99; +const ecs_entity_t ecs_id(EcsPrimitive) = FLECS_HI_COMPONENT_ID + 100; +const ecs_entity_t ecs_id(EcsEnum) = FLECS_HI_COMPONENT_ID + 101; +const ecs_entity_t ecs_id(EcsBitmask) = FLECS_HI_COMPONENT_ID + 102; +const ecs_entity_t ecs_id(EcsMember) = FLECS_HI_COMPONENT_ID + 103; +const ecs_entity_t ecs_id(EcsMemberRanges) = FLECS_HI_COMPONENT_ID + 104; +const ecs_entity_t ecs_id(EcsStruct) = FLECS_HI_COMPONENT_ID + 105; +const ecs_entity_t ecs_id(EcsArray) = FLECS_HI_COMPONENT_ID + 106; +const ecs_entity_t ecs_id(EcsVector) = FLECS_HI_COMPONENT_ID + 107; +const ecs_entity_t ecs_id(EcsOpaque) = FLECS_HI_COMPONENT_ID + 108; +const ecs_entity_t ecs_id(EcsUnit) = FLECS_HI_COMPONENT_ID + 109; +const ecs_entity_t ecs_id(EcsUnitPrefix) = FLECS_HI_COMPONENT_ID + 110; +const ecs_entity_t EcsConstant = FLECS_HI_COMPONENT_ID + 111; +const ecs_entity_t EcsQuantity = FLECS_HI_COMPONENT_ID + 112; /* Doc module components */ -const ecs_entity_t ecs_id(EcsDocDescription) = FLECS_HI_COMPONENT_ID + 112; -const ecs_entity_t EcsDocBrief = FLECS_HI_COMPONENT_ID + 113; -const ecs_entity_t EcsDocDetail = FLECS_HI_COMPONENT_ID + 114; -const ecs_entity_t EcsDocLink = FLECS_HI_COMPONENT_ID + 115; -const ecs_entity_t EcsDocColor = FLECS_HI_COMPONENT_ID + 116; +const ecs_entity_t ecs_id(EcsDocDescription) = FLECS_HI_COMPONENT_ID + 113; +const ecs_entity_t EcsDocBrief = FLECS_HI_COMPONENT_ID + 114; +const ecs_entity_t EcsDocDetail = FLECS_HI_COMPONENT_ID + 115; +const ecs_entity_t EcsDocLink = FLECS_HI_COMPONENT_ID + 116; +const ecs_entity_t EcsDocColor = FLECS_HI_COMPONENT_ID + 117; /* REST module components */ -const ecs_entity_t ecs_id(EcsRest) = FLECS_HI_COMPONENT_ID + 117; +const ecs_entity_t ecs_id(EcsRest) = FLECS_HI_COMPONENT_ID + 118; /* Default lookup path */ static ecs_entity_t ecs_default_lookup_path[2] = { 0, 0 }; @@ -21811,13 +22211,13 @@ ecs_world_t* flecs_suspend_readonly( /* Hack around safety checks (this ought to look ugly) */ state->defer_count = stage->defer; - state->commands = stage->commands; - state->defer_stack = stage->defer_stack; - flecs_stack_init(&stage->defer_stack); + state->commands = stage->cmd->queue; + state->defer_stack = stage->cmd->stack; + flecs_stack_init(&stage->cmd->stack); state->scope = stage->scope; state->with = stage->with; stage->defer = 0; - ecs_vec_init_t(NULL, &stage->commands, ecs_cmd_t, 0); + ecs_vec_init_t(NULL, &stage->cmd->queue, ecs_cmd_t, 0); return world; } @@ -21840,10 +22240,10 @@ void flecs_resume_readonly( /* Restore readonly state / defer count */ ECS_BIT_COND(world->flags, EcsWorldReadonly, state->is_readonly); stage->defer = state->defer_count; - ecs_vec_fini_t(&stage->allocator, &stage->commands, ecs_cmd_t); - stage->commands = state->commands; - flecs_stack_fini(&stage->defer_stack); - stage->defer_stack = state->defer_stack; + ecs_vec_fini_t(&stage->allocator, &stage->cmd->queue, ecs_cmd_t); + stage->cmd->queue = state->commands; + flecs_stack_fini(&stage->cmd->stack); + stage->cmd->stack = state->defer_stack; stage->scope = state->scope; stage->with = state->with; } @@ -23816,6 +24216,7 @@ ecs_entity_t flecs_alert_out_of_range_kind( case EcsIPtr: case EcsString: case EcsEntity: + case EcsId: return 0; } @@ -24408,7 +24809,9 @@ int flecs_default_run_action( /* Ensure quit flag is set on world, which can be used to determine if * world needs to be cleaned up. */ +#ifndef __EMSCRIPTEN__ ecs_quit(world); +#endif if (result == 1) { return 0; /* Normal exit */ @@ -26439,7 +26842,7 @@ void http_recv_connection( ecs_http_reply_t reply; reply.body = ECS_STRBUF_INIT; reply.code = 200; - reply.content_type = NULL; + reply.content_type = "application/json"; reply.headers = ECS_STRBUF_INIT; reply.status = "OK"; ecs_strbuf_appendstrn(&reply.body, @@ -27096,8 +27499,13 @@ void flecs_journal_begin( char *path = NULL; char *var_id = NULL; if (entity) { - path = ecs_get_fullpath(world, entity); - var_id = flecs_journal_entitystr(world, entity); + if (kind != EcsJournalDeleteWith && kind != EcsJournalRemoveAll) { + path = ecs_get_fullpath(world, entity); + var_id = flecs_journal_entitystr(world, entity); + } else { + path = ecs_id_str(world, entity); + var_id = flecs_journal_idstr(world, entity); + } } if (kind == EcsJournalNew) { @@ -27486,8 +27894,7 @@ void ecs_abort_( ecs_os_api.log_last_error_ = err; } -bool ecs_assert_( - bool condition, +void ecs_assert_log_( int32_t err, const char *cond_str, const char *file, @@ -27495,23 +27902,19 @@ bool ecs_assert_( const char *fmt, ...) { - if (!condition) { - if (fmt) { - va_list args; - va_start(args, fmt); - char *msg = ecs_vasprintf(fmt, args); - va_end(args); - ecs_fatal_(file, line, "assert: %s %s (%s)", - cond_str, msg, ecs_strerror(err)); - ecs_os_free(msg); - } else { - ecs_fatal_(file, line, "assert: %s %s", - cond_str, ecs_strerror(err)); - } - ecs_os_api.log_last_error_ = err; + if (fmt) { + va_list args; + va_start(args, fmt); + char *msg = ecs_vasprintf(fmt, args); + va_end(args); + ecs_fatal_(file, line, "assert: %s %s (%s)", + cond_str, msg, ecs_strerror(err)); + ecs_os_free(msg); + } else { + ecs_fatal_(file, line, "assert: %s %s", + cond_str, ecs_strerror(err)); } - - return condition; + ecs_os_api.log_last_error_ = err; } void ecs_deprecated_( @@ -27644,8 +28047,7 @@ void ecs_abort_( (void)fmt; } -bool ecs_assert_( - bool condition, +void ecs_assert_log_( int32_t error_code, const char *condition_str, const char *file, @@ -27653,13 +28055,11 @@ bool ecs_assert_( const char *fmt, ...) { - (void)condition; (void)error_code; (void)condition_str; (void)file; (void)line; (void)fmt; - return true; } #endif @@ -28346,6 +28746,9 @@ ecs_entity_t meta_lookup( } else if (!ecs_os_strcmp(typename, "ecs_entity_t")) { type = ecs_id(ecs_entity_t); + } else if (!ecs_os_strcmp(typename, "ecs_id_t")) { + type = ecs_id(ecs_id_t); + } else if (!ecs_os_strcmp(typename, "char*")) { type = ecs_id(ecs_string_t); } else { @@ -28984,7 +29387,7 @@ int flecs_member_metric_init( ecs_entity_t metric, const ecs_metric_desc_t *desc) { - ecs_entity_t type = 0, member_type = 0, member = 0; + ecs_entity_t type = 0, member_type = 0, member = 0, id = 0; uintptr_t offset = 0; if (desc->dotmember) { @@ -29004,7 +29407,9 @@ int flecs_member_metric_init( goto error; } - ecs_meta_cursor_t cur = ecs_meta_cursor(world, desc->id, NULL); + type = ecs_get_typeid(world, desc->id); + + ecs_meta_cursor_t cur = ecs_meta_cursor(world, type, NULL); if (ecs_meta_push(&cur)) { char *metric_name = ecs_get_fullpath(world, metric); ecs_err("invalid type for metric '%s'", metric_name); @@ -29019,7 +29424,7 @@ int flecs_member_metric_init( goto error; } - type = desc->id; + id = desc->id; member_type = ecs_meta_get_type(&cur); offset = (uintptr_t)ecs_meta_get_ptr(&cur); member = ecs_meta_get_member_id(&cur); @@ -29046,6 +29451,22 @@ int flecs_member_metric_init( goto error; } + id = type; + if (desc->id) { + if (type != ecs_get_typeid(world, desc->id)) { + char *metric_name = ecs_get_fullpath(world, metric); + char *member_name = ecs_get_fullpath(world, desc->member); + char *id_name = ecs_get_fullpath(world, desc->id); + ecs_err("member '%s' for metric '%s' is not of type '%s'", + member_name, metric_name, id_name); + ecs_os_free(id_name); + ecs_os_free(member_name); + ecs_os_free(metric_name); + goto error; + } + id = desc->id; + } + member = desc->member; member_type = m->type; offset = flecs_ito(uintptr_t, m->offset); @@ -29093,7 +29514,7 @@ int flecs_member_metric_init( .entity = metric, .events = { EcsOnAdd }, .filter.terms[0] = { - .id = type, + .id = id, .src.flags = EcsSelf, .inout = EcsInOutNone }, @@ -29301,10 +29722,6 @@ ecs_entity_t ecs_metric_init( } if (desc->member || desc->dotmember) { - if (desc->id && desc->member) { - ecs_err("cannot specify both member and id for metric"); - goto error; - } if (flecs_member_metric_init(world, result, desc)) { goto error; } @@ -30041,6 +30458,9 @@ void FlecsMonitorImport( #ifdef FLECS_META ECS_IMPORT(world, FlecsMeta); #endif +#ifdef FLECS_UNITS + ECS_IMPORT(world, FlecsUnits); +#endif ecs_set_name_prefix(world, "Ecs"); @@ -30123,6 +30543,7 @@ void FlecsMonitorImport( #define TOK_DOWN "down" #define TOK_CASCADE "cascade" #define TOK_PARENT "parent" +#define TOK_DESC "desc" #define TOK_OVERRIDE "OVERRIDE" #define TOK_ROLE_AND "AND" @@ -30448,6 +30869,7 @@ const char* flecs_parse_annotation( ptr = ecs_parse_ws(ptr); if (ptr[0] != TOK_BRACKET_CLOSE) { + printf("errr\n"); ecs_parser_error(name, sig, column, "expected ]"); return NULL; } @@ -30467,6 +30889,8 @@ uint8_t flecs_parse_set_token( return EcsDown; } else if (!ecs_os_strcmp(token, TOK_CASCADE)) { return EcsCascade; + } else if (!ecs_os_strcmp(token, TOK_DESC)) { + return EcsDesc; } else if (!ecs_os_strcmp(token, TOK_PARENT)) { return EcsParent; } else { @@ -30577,15 +31001,24 @@ const char* flecs_parse_arguments( int64_t column, const char *ptr, char *token, - ecs_term_t *term) + ecs_term_t *term, + ecs_term_id_t *extra_args) { (void)column; int32_t arg = 0; + if (extra_args) { + ecs_os_memset_n(extra_args, 0, ecs_term_id_t, ECS_PARSER_MAX_ARGS); + } + + if (!term) { + arg = 2; + } + do { if (flecs_valid_token_start_char(ptr[0])) { - if (arg == 2) { + if ((arg == ECS_PARSER_MAX_ARGS) || (!extra_args && arg == 2)) { ecs_parser_error(name, expr, (ptr - expr), "too many arguments in term"); return NULL; @@ -30602,6 +31035,8 @@ const char* flecs_parse_arguments( term_id = &term->src; } else if (arg == 1) { term_id = &term->second; + } else { + term_id = &extra_args[arg - 2]; } /* If token is a colon, the token is an identifier followed by a @@ -30622,6 +31057,7 @@ const char* flecs_parse_arguments( /* Check for term flags */ } else if (!ecs_os_strcmp(token, TOK_CASCADE) || + !ecs_os_strcmp(token, TOK_DESC) || !ecs_os_strcmp(token, TOK_SELF) || !ecs_os_strcmp(token, TOK_UP) || !ecs_os_strcmp(token, TOK_DOWN) || @@ -30643,7 +31079,9 @@ const char* flecs_parse_arguments( if (ptr[0] == TOK_AND) { ptr = ecs_parse_ws(ptr + 1); - term->id_flags = ECS_PAIR; + if (term) { + term->id_flags = ECS_PAIR; + } } else if (ptr[0] == TOK_PAREN_CLOSE) { ptr = ecs_parse_ws(ptr + 1); @@ -30689,7 +31127,8 @@ const char* flecs_parse_term( const ecs_world_t *world, const char *name, const char *expr, - ecs_term_t *term_out) + ecs_term_t *term_out, + ecs_term_id_t *extra_args) { const char *ptr = expr; char token[ECS_MAX_TOKEN_SIZE] = {0}; @@ -30837,7 +31276,7 @@ const char* flecs_parse_term( ptr = ecs_parse_ws(ptr); } else { ptr = flecs_parse_arguments( - world, name, expr, (ptr - expr), ptr, token, &term); + world, name, expr, (ptr - expr), ptr, token, &term, extra_args); } goto parse_done; @@ -30955,8 +31394,7 @@ const char* flecs_parse_term( } } - if (ptr[0] == TOK_PAREN_CLOSE) { - ptr ++; + if (ptr[0] == TOK_PAREN_CLOSE || ptr[0] == TOK_AND) { goto parse_pair_object; } else { flecs_parser_unexpected_char(name, expr, ptr, ptr[0]); @@ -30983,6 +31421,17 @@ const char* flecs_parse_term( term.id_flags = ECS_PAIR; } + if (ptr[0] == TOK_AND) { + ptr = ecs_parse_ws(ptr + 1); + ptr = flecs_parse_arguments( + world, name, expr, (ptr - expr), ptr, token, NULL, extra_args); + if (!ptr) { + goto error; + } + } else { + ptr ++; + } + ptr = ecs_parse_ws(ptr); goto parse_done; @@ -31007,6 +31456,8 @@ bool flecs_is_valid_end_of_term( (ptr[0] == '/') || /* comment (in plecs) */ (ptr[0] == '{') || /* scope (in plecs) */ (ptr[0] == '}') || + (ptr[0] == '[') || /* collection scope (in plecs) */ + (ptr[0] == ']') || (ptr[0] == ':') || /* inheritance (in plecs) */ (ptr[0] == '=')) /* assignment (in plecs) */ { @@ -31020,7 +31471,8 @@ char* ecs_parse_term( const char *name, const char *expr, const char *ptr, - ecs_term_t *term) + ecs_term_t *term, + ecs_term_id_t *extra_args) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_check(ptr != NULL, ECS_INVALID_PARAMETER, NULL); @@ -31056,7 +31508,7 @@ char* ecs_parse_term( } /* Parse next element */ - ptr = flecs_parse_term(world, name, ptr, term); + ptr = flecs_parse_term(world, name, ptr, term, extra_args); if (!ptr) { goto error; } @@ -31077,7 +31529,14 @@ char* ecs_parse_term( term->src.flags |= EcsIsVariable; } - term->second.name = ecs_os_strdup(term->first.name); + const char *var_name = strrchr(term->first.name, '.'); + if (var_name) { + var_name ++; + } else { + var_name = term->first.name; + } + + term->second.name = ecs_os_strdup(var_name); term->second.flags |= EcsIsVariable; } } @@ -32887,7 +33346,7 @@ const char *plecs_parse_plecs_term( decl_id = state->last_predicate; } - ptr = ecs_parse_term(world, name, expr, ptr, &term); + ptr = ecs_parse_term(world, name, expr, ptr, &term, NULL); if (!ptr) { return NULL; } @@ -33048,6 +33507,7 @@ const char* plecs_parse_stmt( goto error; } + const char *tptr = ecs_parse_ws(ptr); if (flecs_isident(tptr[0])) { if (state->decl_stmt) { @@ -33071,7 +33531,7 @@ const char* plecs_parse_stmt( } else if (ptr[0] == ',') { ptr = plecs_parse_fluff(ptr + 1); goto term_expr; - } else if (ptr[0] == '{') { + } else if (ptr[0] == '{' || ptr[0] == '[') { if (state->assign_stmt) { goto assign_expr; } else if (state->with_stmt && !isspace(ptr[-1])) { @@ -33110,7 +33570,7 @@ const char* plecs_parse_stmt( ptr = plecs_parse_fluff(ptr); /* Assignment without a preceding component */ - if (ptr[0] == '{') { + if (ptr[0] == '{' || ptr[0] == '[') { goto assign_expr; } @@ -33499,23 +33959,6 @@ typedef struct { int32_t rc; } ecs_rest_ctx_t; -/* Global statistics */ -int64_t ecs_rest_request_count = 0; -int64_t ecs_rest_entity_count = 0; -int64_t ecs_rest_entity_error_count = 0; -int64_t ecs_rest_query_count = 0; -int64_t ecs_rest_query_error_count = 0; -int64_t ecs_rest_query_name_count = 0; -int64_t ecs_rest_query_name_error_count = 0; -int64_t ecs_rest_query_name_from_cache_count = 0; -int64_t ecs_rest_enable_count = 0; -int64_t ecs_rest_enable_error_count = 0; -int64_t ecs_rest_delete_count = 0; -int64_t ecs_rest_delete_error_count = 0; -int64_t ecs_rest_world_stats_count = 0; -int64_t ecs_rest_pipeline_stats_count = 0; -int64_t ecs_rest_stats_error_count = 0; - static ECS_COPY(EcsRest, dst, src, { ecs_rest_ctx_t *impl = src->impl; if (impl) { @@ -33557,14 +34000,16 @@ void flecs_rest_capture_log( { (void)file; (void)line; +#ifdef FLECS_DEBUG if (level < 0) { + /* Also log to previous log function in debug mode */ if (rest_prev_log) { - // Also log to previous log function ecs_log_enable_colors(true); rest_prev_log(level, file, line, msg); ecs_log_enable_colors(false); } } +#endif if (!rest_last_err && level < 0) { rest_last_err = ecs_os_strdup(msg); @@ -33701,15 +34146,12 @@ bool flecs_rest_reply_entity( char *path = &req->path[7]; ecs_dbg_2("rest: request entity '%s'", path); - ecs_os_linc(&ecs_rest_entity_count); - ecs_entity_t e = ecs_lookup_path_w_sep( world, 0, path, "/", NULL, false); if (!e) { ecs_dbg_2("rest: entity '%s' not found", path); flecs_reply_error(reply, "entity '%s' not found", path); reply->code = 404; - ecs_os_linc(&ecs_rest_entity_error_count); return true; } @@ -33788,7 +34230,6 @@ bool flecs_rest_delete( { ecs_entity_t e; if (!(e = flecs_rest_entity_from_path(world, reply, path))) { - ecs_os_linc(&ecs_rest_delete_error_count); return true; } @@ -33804,11 +34245,8 @@ bool flecs_rest_enable( const char *path, bool enable) { - ecs_os_linc(&ecs_rest_enable_count); - ecs_entity_t e; if (!(e = flecs_rest_entity_from_path(world, reply, path))) { - ecs_os_linc(&ecs_rest_enable_error_count); return true; } @@ -33846,7 +34284,6 @@ bool flecs_rest_script( char *err = flecs_rest_get_captured_log(); char *escaped_err = ecs_astresc('"', err); flecs_reply_error(reply, escaped_err); - ecs_os_linc(&ecs_rest_query_error_count); reply->code = 400; /* bad request */ ecs_os_free(escaped_err); ecs_os_free(err); @@ -33882,7 +34319,7 @@ int flecs_rest_iter_to_reply( ecs_http_reply_t *reply, ecs_iter_t *it) { - ecs_iter_to_json_desc_t desc = {false}; + ecs_iter_to_json_desc_t desc = {0}; desc.serialize_entities = true; desc.serialize_variables = true; flecs_rest_parse_json_ser_iter_params(&desc, req); @@ -33915,13 +34352,10 @@ bool flecs_rest_reply_existing_query( ecs_http_reply_t *reply, const char *name) { - ecs_os_linc(&ecs_rest_query_name_count); - ecs_entity_t q = ecs_lookup_fullpath(world, name); if (!q) { flecs_reply_error(reply, "unresolved identifier '%s'", name); reply->code = 404; - ecs_os_linc(&ecs_rest_query_name_error_count); return true; } @@ -33930,7 +34364,6 @@ bool flecs_rest_reply_existing_query( flecs_reply_error(reply, "resolved identifier '%s' is not a query", name); reply->code = 400; - ecs_os_linc(&ecs_rest_query_name_error_count); return true; } @@ -33948,12 +34381,10 @@ bool flecs_rest_reply_existing_query( flecs_reply_error(reply, "variables are only supported for rule queries"); reply->code = 400; - ecs_os_linc(&ecs_rest_query_name_error_count); return true; } if (ecs_rule_parse_vars(poly->poly, &it, vars) == NULL) { flecs_rest_reply_set_captured_log(reply); - ecs_os_linc(&ecs_rest_query_name_error_count); return true; } } @@ -33977,13 +34408,10 @@ bool flecs_rest_reply_query( return flecs_rest_reply_existing_query(world, req, reply, q_name); } - ecs_os_linc(&ecs_rest_query_count); - const char *q = ecs_http_get_param(req, "q"); if (!q) { ecs_strbuf_appendlit(&reply->body, "Missing parameter 'q'"); reply->code = 400; /* bad request */ - ecs_os_linc(&ecs_rest_query_error_count); return true; } @@ -33997,7 +34425,6 @@ bool flecs_rest_reply_query( }); if (!r) { flecs_rest_reply_set_captured_log(reply); - ecs_os_linc(&ecs_rest_query_error_count); } else { ecs_iter_t it = ecs_rule_iter(world, r); flecs_rest_iter_to_reply(world, req, reply, &it); @@ -34130,21 +34557,15 @@ void flecs_world_stats_to_json( ECS_GAUGE_APPEND(reply, stats, tables.count, "Tables in the world (including empty)"); ECS_GAUGE_APPEND(reply, stats, tables.empty_count, "Empty tables in the world"); - ECS_GAUGE_APPEND(reply, stats, tables.tag_only_count, "Tables with only tags"); - ECS_GAUGE_APPEND(reply, stats, tables.trivial_only_count, "Tables with only trivial types (no hooks)"); - ECS_GAUGE_APPEND(reply, stats, tables.record_count, "Table records registered with search indices"); - ECS_GAUGE_APPEND(reply, stats, tables.storage_count, "Component storages for all tables"); ECS_COUNTER_APPEND(reply, stats, tables.create_count, "Number of new tables created"); ECS_COUNTER_APPEND(reply, stats, tables.delete_count, "Number of tables deleted"); - ECS_GAUGE_APPEND(reply, stats, ids.count, "Component, tag and pair ids in use"); - ECS_GAUGE_APPEND(reply, stats, ids.tag_count, "Tag ids in use"); - ECS_GAUGE_APPEND(reply, stats, ids.component_count, "Component ids in use"); - ECS_GAUGE_APPEND(reply, stats, ids.pair_count, "Pair ids in use"); - ECS_GAUGE_APPEND(reply, stats, ids.wildcard_count, "Wildcard ids in use"); - ECS_GAUGE_APPEND(reply, stats, ids.type_count, "Registered component types"); - ECS_COUNTER_APPEND(reply, stats, ids.create_count, "Number of new component, tag and pair ids created"); - ECS_COUNTER_APPEND(reply, stats, ids.delete_count, "Number of component, pair and tag ids deleted"); + ECS_GAUGE_APPEND(reply, stats, components.tag_count, "Tag ids in use"); + ECS_GAUGE_APPEND(reply, stats, components.component_count, "Component ids in use"); + ECS_GAUGE_APPEND(reply, stats, components.pair_count, "Pair ids in use"); + ECS_GAUGE_APPEND(reply, stats, components.type_count, "Registered component types"); + ECS_COUNTER_APPEND(reply, stats, components.create_count, "Number of new component, tag and pair ids created"); + ECS_COUNTER_APPEND(reply, stats, components.delete_count, "Number of component, pair and tag ids deleted"); ECS_GAUGE_APPEND(reply, stats, queries.query_count, "Queries in the world"); ECS_GAUGE_APPEND(reply, stats, queries.observer_count, "Observers in the world"); @@ -34161,20 +34582,6 @@ void flecs_world_stats_to_json( ECS_COUNTER_APPEND(reply, stats, memory.stack_free_count, "Pages freed by stack allocators"); ECS_GAUGE_APPEND(reply, stats, memory.stack_outstanding_alloc_count, "Outstanding page allocations"); - ECS_COUNTER_APPEND(reply, stats, rest.request_count, "Received requests"); - ECS_COUNTER_APPEND(reply, stats, rest.entity_count, "Received entity/ requests"); - ECS_COUNTER_APPEND(reply, stats, rest.entity_error_count, "Failed entity/ requests"); - ECS_COUNTER_APPEND(reply, stats, rest.query_count, "Received query/ requests"); - ECS_COUNTER_APPEND(reply, stats, rest.query_error_count, "Failed query/ requests"); - ECS_COUNTER_APPEND(reply, stats, rest.query_name_count, "Received named query/ requests"); - ECS_COUNTER_APPEND(reply, stats, rest.query_name_error_count, "Failed named query/ requests"); - ECS_COUNTER_APPEND(reply, stats, rest.query_name_from_cache_count, "Named query/ requests from cache"); - ECS_COUNTER_APPEND(reply, stats, rest.enable_count, "Received enable/ and disable/ requests"); - ECS_COUNTER_APPEND(reply, stats, rest.enable_error_count, "Failed enable/ and disable/ requests"); - ECS_COUNTER_APPEND(reply, stats, rest.world_stats_count, "Received world stats requests"); - ECS_COUNTER_APPEND(reply, stats, rest.pipeline_stats_count, "Received pipeline stats requests"); - ECS_COUNTER_APPEND(reply, stats, rest.stats_error_count, "Failed stats requests"); - ECS_COUNTER_APPEND(reply, stats, http.request_received_count, "Received requests"); ECS_COUNTER_APPEND(reply, stats, http.request_invalid_count, "Received invalid requests"); ECS_COUNTER_APPEND(reply, stats, http.request_handled_ok_count, "Requests handled successfully"); @@ -34274,7 +34681,6 @@ bool flecs_rest_reply_stats( if (!period) { flecs_reply_error(reply, "bad request (invalid period string)"); reply->code = 400; - ecs_os_linc(&ecs_rest_stats_error_count); return false; } } @@ -34283,24 +34689,19 @@ bool flecs_rest_reply_stats( const EcsWorldStats *stats = ecs_get_pair(world, EcsWorld, EcsWorldStats, period); flecs_world_stats_to_json(&reply->body, stats); - ecs_os_linc(&ecs_rest_world_stats_count); return true; } else if (!ecs_os_strcmp(category, "pipeline")) { const EcsPipelineStats *stats = ecs_get_pair(world, EcsWorld, EcsPipelineStats, period); flecs_pipeline_stats_to_json(world, &reply->body, stats); - ecs_os_linc(&ecs_rest_pipeline_stats_count); return true; } else { flecs_reply_error(reply, "bad request (unsupported category)"); reply->code = 400; - ecs_os_linc(&ecs_rest_stats_error_count); return false; } - - return true; } #else static @@ -34342,9 +34743,7 @@ void flecs_rest_reply_table_append_memory( int32_t used = 0, allocated = 0; used += table->data.entities.count * ECS_SIZEOF(ecs_entity_t); - used += table->data.records.count * ECS_SIZEOF(ecs_record_t*); allocated += table->data.entities.size * ECS_SIZEOF(ecs_entity_t); - allocated += table->data.records.size * ECS_SIZEOF(ecs_record_t*); int32_t i, storage_count = table->column_count; ecs_column_t *columns = table->data.columns; @@ -34406,8 +34805,6 @@ bool flecs_rest_reply( ecs_rest_ctx_t *impl = ctx; ecs_world_t *world = impl->world; - ecs_os_linc(&ecs_rest_request_count); - if (req->path == NULL) { ecs_dbg("rest: bad request (missing path)"); flecs_reply_error(reply, "bad request (missing path)"); @@ -34658,10 +35055,9 @@ ecs_data_t* flecs_duplicate_data( result->columns = flecs_wdup_n(world, ecs_column_t, column_count, main_data->columns); - /* Copy entities and records */ + /* Copy entities */ ecs_allocator_t *a = &world->allocator; result->entities = ecs_vec_copy_t(a, &main_data->entities, ecs_entity_t); - result->records = ecs_vec_copy_t(a, &main_data->records, ecs_record_t*); /* Copy each column */ for (i = 0; i < column_count; i ++) { @@ -35500,14 +35896,12 @@ void ecs_world_stats_get( ECS_GAUGE_RECORD(&s->entities.count, t, flecs_entities_count(world)); ECS_GAUGE_RECORD(&s->entities.not_alive_count, t, flecs_entities_not_alive_count(world)); - ECS_GAUGE_RECORD(&s->ids.count, t, world->info.id_count); - ECS_GAUGE_RECORD(&s->ids.tag_count, t, world->info.tag_id_count); - ECS_GAUGE_RECORD(&s->ids.component_count, t, world->info.component_id_count); - ECS_GAUGE_RECORD(&s->ids.pair_count, t, world->info.pair_id_count); - ECS_GAUGE_RECORD(&s->ids.wildcard_count, t, world->info.wildcard_id_count); - ECS_GAUGE_RECORD(&s->ids.type_count, t, ecs_sparse_count(&world->type_info)); - ECS_COUNTER_RECORD(&s->ids.create_count, t, world->info.id_create_total); - ECS_COUNTER_RECORD(&s->ids.delete_count, t, world->info.id_delete_total); + ECS_GAUGE_RECORD(&s->components.tag_count, t, world->info.tag_id_count); + ECS_GAUGE_RECORD(&s->components.component_count, t, world->info.component_id_count); + ECS_GAUGE_RECORD(&s->components.pair_count, t, world->info.pair_id_count); + ECS_GAUGE_RECORD(&s->components.type_count, t, ecs_sparse_count(&world->type_info)); + ECS_COUNTER_RECORD(&s->components.create_count, t, world->info.id_create_total); + ECS_COUNTER_RECORD(&s->components.delete_count, t, world->info.id_delete_total); ECS_GAUGE_RECORD(&s->queries.query_count, t, ecs_count_id(world, EcsQuery)); ECS_GAUGE_RECORD(&s->queries.observer_count, t, ecs_count_id(world, EcsObserver)); @@ -35518,10 +35912,6 @@ void ecs_world_stats_get( ECS_COUNTER_RECORD(&s->tables.delete_count, t, world->info.table_delete_total); ECS_GAUGE_RECORD(&s->tables.count, t, world->info.table_count); ECS_GAUGE_RECORD(&s->tables.empty_count, t, world->info.empty_table_count); - ECS_GAUGE_RECORD(&s->tables.tag_only_count, t, world->info.tag_table_count); - ECS_GAUGE_RECORD(&s->tables.trivial_only_count, t, world->info.trivial_table_count); - ECS_GAUGE_RECORD(&s->tables.storage_count, t, world->info.table_storage_count); - ECS_GAUGE_RECORD(&s->tables.record_count, t, world->info.table_record_count); ECS_COUNTER_RECORD(&s->commands.add_count, t, world->info.cmd.add_count); ECS_COUNTER_RECORD(&s->commands.remove_count, t, world->info.cmd.remove_count); @@ -35552,22 +35942,6 @@ void ecs_world_stats_get( ECS_COUNTER_RECORD(&s->memory.stack_free_count, t, ecs_stack_allocator_free_count); ECS_GAUGE_RECORD(&s->memory.stack_outstanding_alloc_count, t, outstanding_allocs); -#ifdef FLECS_REST - ECS_COUNTER_RECORD(&s->rest.request_count, t, ecs_rest_request_count); - ECS_COUNTER_RECORD(&s->rest.entity_count, t, ecs_rest_entity_count); - ECS_COUNTER_RECORD(&s->rest.entity_error_count, t, ecs_rest_entity_error_count); - ECS_COUNTER_RECORD(&s->rest.query_count, t, ecs_rest_query_count); - ECS_COUNTER_RECORD(&s->rest.query_error_count, t, ecs_rest_query_error_count); - ECS_COUNTER_RECORD(&s->rest.query_name_count, t, ecs_rest_query_name_count); - ECS_COUNTER_RECORD(&s->rest.query_name_error_count, t, ecs_rest_query_name_error_count); - ECS_COUNTER_RECORD(&s->rest.query_name_from_cache_count, t, ecs_rest_query_name_from_cache_count); - ECS_COUNTER_RECORD(&s->rest.enable_count, t, ecs_rest_enable_count); - ECS_COUNTER_RECORD(&s->rest.enable_error_count, t, ecs_rest_enable_error_count); - ECS_COUNTER_RECORD(&s->rest.world_stats_count, t, ecs_rest_world_stats_count); - ECS_COUNTER_RECORD(&s->rest.pipeline_stats_count, t, ecs_rest_pipeline_stats_count); - ECS_COUNTER_RECORD(&s->rest.stats_error_count, t, ecs_rest_stats_error_count); -#endif - #ifdef FLECS_HTTP ECS_COUNTER_RECORD(&s->http.request_received_count, t, ecs_http_request_received_count); ECS_COUNTER_RECORD(&s->http.request_invalid_count, t, ecs_http_request_invalid_count); @@ -36039,14 +36413,12 @@ void ecs_world_stats_log( flecs_counter_print("merge time", t, &s->performance.merge_time); flecs_counter_print("simulation time elapsed", t, &s->performance.world_time); ecs_trace(""); - flecs_gauge_print("id count", t, &s->ids.count); - flecs_gauge_print("tag id count", t, &s->ids.tag_count); - flecs_gauge_print("component id count", t, &s->ids.component_count); - flecs_gauge_print("pair id count", t, &s->ids.pair_count); - flecs_gauge_print("wildcard id count", t, &s->ids.wildcard_count); - flecs_gauge_print("type count", t, &s->ids.type_count); - flecs_counter_print("id create count", t, &s->ids.create_count); - flecs_counter_print("id delete count", t, &s->ids.delete_count); + flecs_gauge_print("tag id count", t, &s->components.tag_count); + flecs_gauge_print("component id count", t, &s->components.component_count); + flecs_gauge_print("pair id count", t, &s->components.pair_count); + flecs_gauge_print("type count", t, &s->components.type_count); + flecs_counter_print("id create count", t, &s->components.create_count); + flecs_counter_print("id delete count", t, &s->components.delete_count); ecs_trace(""); flecs_gauge_print("alive entity count", t, &s->entities.count); flecs_gauge_print("not alive entity count", t, &s->entities.not_alive_count); @@ -36057,10 +36429,6 @@ void ecs_world_stats_log( ecs_trace(""); flecs_gauge_print("table count", t, &s->tables.count); flecs_gauge_print("empty table count", t, &s->tables.empty_count); - flecs_gauge_print("tag table count", t, &s->tables.tag_only_count); - flecs_gauge_print("trivial table count", t, &s->tables.trivial_only_count); - flecs_gauge_print("table storage count", t, &s->tables.storage_count); - flecs_gauge_print("table cache record count", t, &s->tables.record_count); flecs_counter_print("table create count", t, &s->tables.create_count); flecs_counter_print("table delete count", t, &s->tables.delete_count); ecs_trace(""); @@ -41925,6 +42293,7 @@ ecs_id_record_t* flecs_id_record_new( if (is_pair) { // rel = ecs_pair_first(world, id); rel = ECS_PAIR_FIRST(id); + rel = flecs_entities_get_generation(world, rel); ecs_assert(rel != 0, ECS_INTERNAL_ERROR, NULL); /* Relationship object can be 0, as tables without a ChildOf @@ -41934,7 +42303,7 @@ ecs_id_record_t* flecs_id_record_new( #ifdef FLECS_DEBUG /* Check constraints */ if (tgt) { - tgt = ecs_get_alive(world, tgt); + tgt = flecs_entities_get_generation(world, tgt); ecs_assert(tgt != 0, ECS_INTERNAL_ERROR, NULL); } if (tgt && !ecs_id_is_wildcard(tgt)) { @@ -42028,22 +42397,9 @@ ecs_id_record_t* flecs_id_record_new( /* Update counters */ world->info.id_create_total ++; - - if (!is_wildcard) { - world->info.id_count ++; - - if (idr->type_info) { - world->info.component_id_count ++; - } else { - world->info.tag_id_count ++; - } - - if (is_pair) { - world->info.pair_id_count ++; - } - } else { - world->info.wildcard_id_count ++; - } + world->info.component_id_count += idr->type_info != NULL; + world->info.tag_id_count += idr->type_info == NULL; + world->info.pair_id_count += is_pair; return idr; #ifdef FLECS_DEBUG @@ -42118,22 +42474,9 @@ void flecs_id_record_free( /* Update counters */ world->info.id_delete_total ++; - - if (!ecs_id_is_wildcard(id)) { - world->info.id_count --; - - if (ECS_IS_PAIR(id)) { - world->info.pair_id_count --; - } - - if (idr->type_info) { - world->info.component_id_count --; - } else { - world->info.tag_id_count --; - } - } else { - world->info.wildcard_id_count --; - } + world->info.pair_id_count -= ECS_IS_PAIR(id); + world->info.component_id_count -= idr->type_info != NULL; + world->info.tag_id_count -= idr->type_info == NULL; /* Unregister the id record from the world & free resources */ ecs_table_cache_fini(&idr->cache); @@ -42416,11 +42759,6 @@ static void flecs_table_check_sanity(ecs_table_t *table) { int32_t size = ecs_vec_size(&table->data.entities); int32_t count = ecs_vec_count(&table->data.entities); - - ecs_assert(size == ecs_vec_size(&table->data.records), - ECS_INTERNAL_ERROR, NULL); - ecs_assert(count == ecs_vec_count(&table->data.records), - ECS_INTERNAL_ERROR, NULL); int32_t i; int32_t sw_offset = table->_ ? table->_->sw_offset : 0; @@ -42574,7 +42912,6 @@ void flecs_table_init_data( { ecs_data_t *storage = &table->data; ecs_vec_init_t(NULL, &storage->entities, ecs_entity_t, 0); - ecs_vec_init_t(NULL, &storage->records, ecs_record_t*, 0); flecs_table_init_columns(world, table, table->column_count); @@ -43163,12 +43500,9 @@ void flecs_table_dtor_all( ecs_assert(!is_delete || update_entity_index, ECS_INTERNAL_ERROR, NULL); int32_t ids_count = table->column_count; - ecs_record_t **records = data->records.array; ecs_entity_t *entities = data->entities.array; int32_t i, c, end = row + count; - (void)records; - if (is_delete && table->_->traversable_count) { /* If table contains monitored entities with traversable relationships, * make sure to invalidate observer cache */ @@ -43205,10 +43539,6 @@ void flecs_table_dtor_all( ecs_entity_t e = entities[i]; ecs_assert(!e || ecs_is_valid(world, e), ECS_INTERNAL_ERROR, NULL); - ecs_assert(!e || records[i] == flecs_entities_get(world, e), - ECS_INTERNAL_ERROR, NULL); - ecs_assert(!e || records[i]->table == table, - ECS_INTERNAL_ERROR, NULL); if (is_delete) { flecs_entities_remove(world, e); @@ -43216,8 +43546,9 @@ void flecs_table_dtor_all( ECS_INTERNAL_ERROR, NULL); } else { // If this is not a delete, clear the entity index record - records[i]->table = NULL; - records[i]->row = 0; + ecs_record_t *record = flecs_entities_get(world, e); + record->table = NULL; + record->row = 0; } } else { /* This should only happen in rare cases, such as when the data @@ -43233,11 +43564,6 @@ void flecs_table_dtor_all( for (i = row; i < end; i ++) { ecs_entity_t e = entities[i]; ecs_assert(!e || ecs_is_valid(world, e), ECS_INTERNAL_ERROR, NULL); - ecs_assert(!e || records[i] == flecs_entities_get(world, e), - ECS_INTERNAL_ERROR, NULL); - ecs_assert(!e || records[i]->table == table, - ECS_INTERNAL_ERROR, NULL); - flecs_entities_remove(world, e); ecs_assert(!ecs_is_valid(world, e), ECS_INTERNAL_ERROR, NULL); } @@ -43245,12 +43571,9 @@ void flecs_table_dtor_all( for (i = row; i < end; i ++) { ecs_entity_t e = entities[i]; ecs_assert(!e || ecs_is_valid(world, e), ECS_INTERNAL_ERROR, NULL); - ecs_assert(!e || records[i] == flecs_entities_get(world, e), - ECS_INTERNAL_ERROR, NULL); - ecs_assert(!e || records[i]->table == table, - ECS_INTERNAL_ERROR, NULL); - records[i]->table = NULL; - records[i]->row = records[i]->row & ECS_ROW_FLAGS_MASK; + ecs_record_t *record = flecs_entities_get(world, e); + record->table = NULL; + record->row = record->row & ECS_ROW_FLAGS_MASK; (void)e; } } @@ -43284,10 +43607,6 @@ void flecs_table_fini_data( update_entity_index, is_delete); } - /* Sanity check */ - ecs_assert(data->records.count == - data->entities.count, ECS_INTERNAL_ERROR, NULL); - ecs_column_t *columns = data->columns; if (columns) { int32_t c, column_count = table->column_count; @@ -43324,7 +43643,6 @@ void flecs_table_fini_data( } ecs_vec_fini_t(&world->allocator, &data->entities, ecs_entity_t); - ecs_vec_fini_t(&world->allocator, &data->records, ecs_record_t*); if (deactivate && count) { flecs_table_set_empty(world, table); @@ -43334,6 +43652,18 @@ void flecs_table_fini_data( table->flags &= ~EcsTableHasTraversable; } +ecs_vec_t* flecs_table_entities( + ecs_table_t *table) +{ + return &table->data.entities; +} + +ecs_entity_t* flecs_table_entities_array( + ecs_table_t *table) +{ + return ecs_vec_first(flecs_table_entities(table)); +} + /* Cleanup, no OnRemove, don't update entity index, don't deactivate table */ void flecs_table_clear_data( ecs_world_t *world, @@ -43433,16 +43763,8 @@ void flecs_table_free( /* Update counters */ world->info.table_count --; - world->info.table_record_count -= table->_->record_count; - world->info.table_storage_count -= table->column_count; world->info.table_delete_total ++; - if (!table->column_count) { - world->info.tag_table_count --; - } else { - world->info.trivial_table_count -= !(table->flags & EcsTableIsComplex); - } - flecs_free_t(&world->allocator, ecs_table__t, table->_); if (!(world->flags & EcsWorldFini)) { @@ -43783,19 +44105,13 @@ int32_t flecs_table_grow_data( int32_t cur_count = flecs_table_data_count(data); int32_t column_count = table->column_count; - /* Add record to record ptr array */ - ecs_vec_set_size_t(&world->allocator, &data->records, ecs_record_t*, size); - ecs_record_t **r = ecs_vec_last_t(&data->records, ecs_record_t*) + 1; - data->records.count += to_add; - if (data->records.size > size) { - size = data->records.size; - } - /* Add entity to column with entity ids */ ecs_vec_set_size_t(&world->allocator, &data->entities, ecs_entity_t, size); ecs_entity_t *e = ecs_vec_last_t(&data->entities, ecs_entity_t) + 1; data->entities.count += to_add; - ecs_assert(data->entities.size == size, ECS_INTERNAL_ERROR, NULL); + if (data->entities.size > size) { + size = data->entities.size; + } /* Initialize entity ids and record ptrs */ int32_t i; @@ -43804,7 +44120,6 @@ int32_t flecs_table_grow_data( } else { ecs_os_memset(e, 0, ECS_SIZEOF(ecs_entity_t) * to_add); } - ecs_os_memset(r, 0, ECS_SIZEOF(ecs_record_t*) * to_add); /* Add elements to each column array */ ecs_column_t *columns = data->columns; @@ -43864,7 +44179,6 @@ int32_t flecs_table_append( ecs_world_t *world, ecs_table_t *table, ecs_entity_t entity, - ecs_record_t *record, bool construct, bool on_add) { @@ -43887,12 +44201,6 @@ int32_t flecs_table_append( &data->entities, ecs_entity_t); ecs_assert(e != NULL, ECS_INTERNAL_ERROR, NULL); *e = entity; - - /* Add record ptr to array with record ptrs */ - ecs_record_t **r = ecs_vec_append_t(&world->allocator, - &data->records, ecs_record_t*); - ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); - *r = record; /* If the table is monitored indicate that there has been a change */ flecs_table_mark_table_dirty(world, table, 0); @@ -44018,16 +44326,9 @@ void flecs_table_delete( entities[index] = entity_to_move; ecs_vec_remove_last(&data->entities); - /* Move last record ptr to index */ - ecs_assert(count < data->records.count, ECS_INTERNAL_ERROR, NULL); - - ecs_record_t **records = data->records.array; - ecs_record_t *record_to_move = records[count]; - records[index] = record_to_move; - ecs_vec_remove_last(&data->records); - /* Update record of moved entity in entity index */ if (index != count) { + ecs_record_t *record_to_move = flecs_entities_get(world, entity_to_move); if (record_to_move) { uint32_t row_flags = record_to_move->row & ECS_ROW_FLAGS_MASK; record_to_move->row = ECS_ROW_TO_RECORD(index, row_flags); @@ -44321,7 +44622,6 @@ bool flecs_table_shrink( ecs_data_t *data = &table->data; bool has_payload = data->entities.array != NULL; ecs_vec_reclaim_t(&world->allocator, &data->entities, ecs_entity_t); - ecs_vec_reclaim_t(&world->allocator, &data->records, ecs_record_t*); int32_t i, count = table->column_count; for (i = 0; i < count; i ++) { @@ -44405,9 +44705,8 @@ void flecs_table_swap( ecs_entity_t e1 = entities[row_1]; ecs_entity_t e2 = entities[row_2]; - ecs_record_t **records = table->data.records.array; - ecs_record_t *record_ptr_1 = records[row_1]; - ecs_record_t *record_ptr_2 = records[row_2]; + ecs_record_t *record_ptr_1 = flecs_entities_get(world, e1); + ecs_record_t *record_ptr_2 = flecs_entities_get(world, e2); ecs_assert(record_ptr_1 != NULL, ECS_INTERNAL_ERROR, NULL); ecs_assert(record_ptr_2 != NULL, ECS_INTERNAL_ERROR, NULL); @@ -44421,8 +44720,6 @@ void flecs_table_swap( entities[row_2] = e1; record_ptr_1->row = ECS_ROW_TO_RECORD(row_2, flags_1); record_ptr_2->row = ECS_ROW_TO_RECORD(row_1, flags_2); - records[row_1] = record_ptr_2; - records[row_2] = record_ptr_1; flecs_table_swap_switch_columns(table, row_1, row_2); flecs_table_swap_bitset_columns(table, row_1, row_2); @@ -44563,10 +44860,6 @@ void flecs_table_merge_data( int32_t column_size = dst_data->entities.size; ecs_allocator_t *a = &world->allocator; - /* Merge record pointers */ - flecs_table_merge_vec(world, &dst_data->records, &src_data->records, - ECS_SIZEOF(ecs_record_t*), 0); - for (; (i_new < dst_column_count) && (i_old < src_column_count); ) { ecs_column_t *dst_column = &dst_columns[i_new]; ecs_column_t *src_column = &src_columns[i_old]; @@ -44659,19 +44952,11 @@ void flecs_table_merge( ecs_entity_t *src_entities = src_data->entities.array; int32_t src_count = src_data->entities.count; int32_t dst_count = dst_data->entities.count; - ecs_record_t **src_records = src_data->records.array; /* First, update entity index so old entities point to new type */ int32_t i; for(i = 0; i < src_count; i ++) { - ecs_record_t *record; - if (dst_table != src_table) { - record = src_records[i]; - ecs_assert(record != NULL, ECS_INTERNAL_ERROR, NULL); - } else { - record = flecs_entities_ensure(world, src_entities[i]); - } - + ecs_record_t *record = flecs_entities_ensure(world, src_entities[i]); uint32_t flags = ECS_RECORD_TO_ROW_FLAGS(record->row); record->row = ECS_ROW_TO_RECORD(dst_count + i, flags); record->table = dst_table; @@ -45300,7 +45585,7 @@ ecs_table_cache_hdr_t* flecs_table_cache_next_( next = it->next_list; it->next_list = NULL; if (!next) { - return false; + return NULL; } } @@ -45876,16 +46161,8 @@ ecs_table_t *flecs_create_table( /* Update counters */ world->info.table_count ++; - world->info.table_record_count += result->_->record_count; - world->info.table_storage_count += result->column_count; world->info.empty_table_count ++; world->info.table_create_total ++; - - if (!result->column_count) { - world->info.tag_table_count ++; - } else { - world->info.trivial_table_count += !(result->flags & EcsTableIsComplex); - } ecs_log_pop_2(); @@ -46706,12 +46983,14 @@ const char *ecs_parse_expr_token( if (ptr[1] == '/') { // Single line comment for (ptr = &ptr[2]; (ch = ptr[0]) && (ch != '\n'); ptr ++) {} - return ptr; + token[0] = 0; + return ecs_parse_ws_eol(ptr); } else if (ptr[1] == '*') { // Multi line comment for (ptr = &ptr[2]; (ch = ptr[0]); ptr ++) { if (ch == '*' && ptr[1] == '/') { - return ptr + 2; + token[0] = 0; + return ecs_parse_ws_eol(ptr + 2); } } @@ -47033,6 +47312,7 @@ ecs_entity_t flecs_largest_type( case EcsIPtr: return ecs_id(ecs_i64_t); case EcsString: return ecs_id(ecs_string_t); case EcsEntity: return ecs_id(ecs_entity_t); + case EcsId: return ecs_id(ecs_id_t); default: ecs_throw(ECS_INTERNAL_ERROR, NULL); } error: @@ -47087,7 +47367,6 @@ bool flecs_oper_valid_for_type( return false; default: ecs_abort(ECS_INTERNAL_ERROR, NULL); - return false; } } @@ -47639,6 +47918,11 @@ const char* flecs_parse_expr( bool is_lvalue = false; bool newline = false; + if (!token[0]) { + /* Comment */ + continue; + } + if (!ecs_os_strcmp(token, "(")) { ecs_value_t temp_result, *out; if (!depth) { @@ -47698,7 +47982,7 @@ const char* flecs_parse_expr( } if (!ecs_meta_is_collection(&cur)) { - ecs_parser_error(name, expr, ptr - expr, "expected '{'"); + ecs_parser_error(name, expr, ptr - expr, "unexpected '['"); return NULL; } } @@ -48105,6 +48389,15 @@ int flecs_expr_ser_primitive( } break; } + case EcsId: { + ecs_id_t id = *(const ecs_id_t*)base; + if (!id) { + ecs_strbuf_appendch(str, '0'); + } else { + ecs_id_str_buf(world, id, str); + } + break; + } default: ecs_err("invalid primitive kind"); return -1; @@ -48334,6 +48627,7 @@ int flecs_expr_ser_type_op( case EcsOpUPtr: case EcsOpIPtr: case EcsOpEntity: + case EcsOpId: case EcsOpString: case EcsOpOpaque: if (flecs_expr_ser_primitive(world, flecs_expr_op_to_primitive_kind(op->kind), @@ -48418,6 +48712,7 @@ int flecs_expr_ser_type_ops( case EcsOpUPtr: case EcsOpIPtr: case EcsOpEntity: + case EcsOpId: case EcsOpString: case EcsOpOpaque: if (flecs_expr_ser_type_op(world, op, base, str, is_expr)) { @@ -49171,6 +49466,7 @@ typedef enum ecs_json_token_t { JsonTrue, JsonFalse, JsonNull, + JsonLargeInt, JsonLargeString, JsonInvalid } ecs_json_token_t; @@ -49425,6 +49721,11 @@ const char* ecs_ptr_from_json( if (ecs_meta_set_float(&cur, number) != 0) { goto error; } + } else if (token_kind == JsonLargeInt) { + int64_t number = flecs_ito(int64_t, atoll(token)); + if (ecs_meta_set_int(&cur, number) != 0) { + goto error; + } } else if (token_kind == JsonNull) { if (ecs_meta_set_null(&cur) != 0) { goto error; @@ -49974,9 +50275,6 @@ const char* flecs_json_parse_column( do { ecs_record_t *r = record_array[entity]; int32_t row = ECS_RECORD_TO_ROW(r->row); - ecs_assert(ecs_vec_get_t( - &table->data.records, ecs_record_t*, row)[0] == r, - ECS_INTERNAL_ERROR, NULL); void *ptr = ecs_vec_get(&column->data, size, row); ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); @@ -50326,6 +50624,7 @@ const char* flecs_json_token_str( case JsonColon: return ":"; case JsonComma: return ","; case JsonNumber: return "number"; + case JsonLargeInt: return "large integer"; case JsonLargeString: case JsonString: return "string"; case JsonTrue: return "true"; @@ -50396,7 +50695,17 @@ const char* flecs_json_parse( } } else if (isdigit(ch) || (ch == '-')) { token_kind[0] = JsonNumber; - return ecs_parse_digit(json, token); + const char *result = ecs_parse_digit(json, token); + + /* Cheap initial check if parsed token could represent large int */ + if (result - json > 15) { + /* Less cheap secondary check to see if number is integer */ + if (!strchr(token, '.')) { + token_kind[0] = JsonLargeInt; + } + } + + return result; } else if (isalpha(ch)) { if (!ecs_os_strncmp(json, "null", 4)) { token_kind[0] = JsonNull; @@ -51047,6 +51356,22 @@ int json_ser_type_op( const void *ptr, ecs_strbuf_t *str) { + void *vptr = ECS_OFFSET(ptr, op->offset); + bool large_int = false; + if (op->kind == EcsOpI64) { + if (*(int64_t*)vptr >= 2147483648) { + large_int = true; + } + } else if (op->kind == EcsOpU64) { + if (*(uint64_t*)vptr >= 2147483648) { + large_int = true; + } + } + + if (large_int) { + ecs_strbuf_appendch(str, '"'); + } + switch(op->kind) { case EcsOpPush: case EcsOpPop: @@ -51055,39 +51380,39 @@ int json_ser_type_op( break; case EcsOpF32: ecs_strbuf_appendflt(str, - (ecs_f64_t)*(const ecs_f32_t*)ECS_OFFSET(ptr, op->offset), '"'); + (ecs_f64_t)*(const ecs_f32_t*)vptr, '"'); break; case EcsOpF64: ecs_strbuf_appendflt(str, - *(ecs_f64_t*)ECS_OFFSET(ptr, op->offset), '"'); + *(ecs_f64_t*)vptr, '"'); break; case EcsOpEnum: - if (json_ser_enum(world, op, ECS_OFFSET(ptr, op->offset), str)) { + if (json_ser_enum(world, op, vptr, str)) { goto error; } break; case EcsOpBitmask: - if (json_ser_bitmask(world, op, ECS_OFFSET(ptr, op->offset), str)) { + if (json_ser_bitmask(world, op, vptr, str)) { goto error; } break; case EcsOpArray: - if (json_ser_array(world, op, ECS_OFFSET(ptr, op->offset), str)) { + if (json_ser_array(world, op, vptr, str)) { goto error; } break; case EcsOpVector: - if (json_ser_vector(world, op, ECS_OFFSET(ptr, op->offset), str)) { + if (json_ser_vector(world, op, vptr, str)) { goto error; } break; case EcsOpOpaque: - if (json_ser_custom_type(world, op, ECS_OFFSET(ptr, op->offset), str)) { + if (json_ser_custom_type(world, op, vptr, str)) { goto error; } break; case EcsOpEntity: { - ecs_entity_t e = *(const ecs_entity_t*)ECS_OFFSET(ptr, op->offset); + ecs_entity_t e = *(const ecs_entity_t*)vptr; if (!e) { ecs_strbuf_appendch(str, '0'); } else { @@ -51095,17 +51420,27 @@ int json_ser_type_op( } break; } + case EcsOpId: { + ecs_id_t id = *(const ecs_id_t*)vptr; + if (!id) { + ecs_strbuf_appendch(str, '0'); + } else { + flecs_json_id(str, world, id); + } + break; + } + + case EcsOpU64: + case EcsOpI64: case EcsOpBool: case EcsOpChar: case EcsOpByte: case EcsOpU8: case EcsOpU16: case EcsOpU32: - case EcsOpU64: case EcsOpI8: case EcsOpI16: case EcsOpI32: - case EcsOpI64: case EcsOpUPtr: case EcsOpIPtr: case EcsOpString: @@ -51116,12 +51451,17 @@ int json_ser_type_op( ecs_throw(ECS_INTERNAL_ERROR, NULL); } break; + case EcsOpPrimitive: case EcsOpScope: default: ecs_throw(ECS_INTERNAL_ERROR, NULL); } + if (large_int) { + ecs_strbuf_appendch(str, '"'); + } + return 0; error: return -1; @@ -51190,6 +51530,7 @@ int json_ser_type_ops( case EcsOpUPtr: case EcsOpIPtr: case EcsOpEntity: + case EcsOpId: case EcsOpString: case EcsOpOpaque: if (json_ser_type_op(world, op, base, str)) { @@ -51760,7 +52101,6 @@ int flecs_json_serialize_children_alerts( ecs_iter_t it = ecs_filter_iter(world, &f); while (ecs_filter_next(&it)) { - ecs_record_t **records = ecs_vec_first(&it.table->data.records); EcsAlertsActive *alerts = ecs_table_get_id( world, it.table, ecs_id(EcsAlertsActive), it.offset); @@ -51775,7 +52115,7 @@ int flecs_json_serialize_children_alerts( } } - ecs_record_t *r = records[i]; + ecs_record_t *r = flecs_entities_get(world, it.entities[i]); if (r->row & EcsEntityIsTraversable) { if (flecs_json_serialize_children_alerts( world, buf, child)) @@ -52689,7 +53029,6 @@ int flecs_json_serialize_iter_result_values( /* If term is not set, append empty array. This indicates that the term * could have had data but doesn't */ if (!ecs_field_is_set(it, i + 1)) { - ecs_assert(ptr == NULL, ECS_INTERNAL_ERROR, NULL); flecs_json_array_push(buf); flecs_json_array_pop(buf); continue; @@ -53083,6 +53422,9 @@ int json_typeinfo_ser_primitive( case EcsEntity: flecs_json_string(str, "entity"); break; + case EcsId: + flecs_json_string(str, "id"); + break; default: return -1; } @@ -53283,6 +53625,7 @@ int json_typeinfo_ser_type_op( case EcsOpUPtr: case EcsOpIPtr: case EcsOpEntity: + case EcsOpId: case EcsOpString: if (json_typeinfo_ser_primitive( flecs_json_op_to_primitive_kind(op->kind), str)) @@ -53395,6 +53738,7 @@ int json_typeinfo_ser_type_ops( case EcsOpUPtr: case EcsOpIPtr: case EcsOpEntity: + case EcsOpId: case EcsOpString: case EcsOpOpaque: if (json_typeinfo_ser_type_op(world, op, str, stack[sp - 1])) { @@ -53530,10 +53874,10 @@ bool flecs_type_is_number( case EcsIPtr: case EcsString: case EcsEntity: + case EcsId: return false; default: ecs_abort(ECS_INVALID_PARAMETER, NULL); - return false; } } @@ -53760,7 +54104,7 @@ ecs_entity_t ecs_struct_init( ranges = ecs_get_mut(world, m, EcsMemberRanges); if (range->min > range->max) { char *member_name = ecs_get_fullpath(world, m); - ecs_err("member '%s' has an invalid value range [%d..%d]", + ecs_err("member '%s' has an invalid value range [%f..%f]", member_name, range->min, range->max); ecs_os_free(member_name); goto error; @@ -53771,7 +54115,7 @@ ecs_entity_t ecs_struct_init( if (ECS_NEQ(error->min, error->max)) { if (error->min > error->max) { char *member_name = ecs_get_fullpath(world, m); - ecs_err("member '%s' has an invalid error range [%d..%d]", + ecs_err("member '%s' has an invalid error range [%f..%f]", member_name, error->min, error->max); ecs_os_free(member_name); goto error; @@ -53793,7 +54137,7 @@ ecs_entity_t ecs_struct_init( if (ECS_NEQ(warning->min, warning->max)) { if (warning->min > warning->max) { char *member_name = ecs_get_fullpath(world, m); - ecs_err("member '%s' has an invalid warning range [%d..%d]", + ecs_err("member '%s' has an invalid warning range [%f..%f]", member_name, warning->min, warning->max); ecs_os_free(member_name); goto error; @@ -53975,6 +54319,8 @@ ecs_entity_t ecs_quantity_init( #include #ifdef FLECS_META +#ifdef FLECS_PARSER +#endif static const char* flecs_meta_op_kind_str( @@ -54007,6 +54353,7 @@ const char* flecs_meta_op_kind_str( case EcsOpIPtr: return "IPtr"; case EcsOpString: return "String"; case EcsOpEntity: return "Entity"; + case EcsOpId: return "Id"; case EcsOpScope: return "Scope"; default: return "<< invalid kind >>"; } @@ -54344,7 +54691,7 @@ int ecs_meta_push( if (cursor->depth == 0) { if (!cursor->is_primitive_scope) { - if (op->kind > EcsOpScope) { + if ((op->kind > EcsOpScope) && (op->count <= 1)) { cursor->is_primitive_scope = true; return 0; } @@ -54543,7 +54890,8 @@ int ecs_meta_push( case EcsOpUPtr: case EcsOpIPtr: case EcsOpString: - case EcsOpEntity: { + case EcsOpEntity: + case EcsOpId: { char *path = ecs_get_fullpath(world, scope->type); ecs_err("invalid push for type '%s'", path); ecs_os_free(path); @@ -54630,9 +54978,6 @@ int ecs_meta_pop( * a complex or collection type */ ecs_assert(false, ECS_INTERNAL_ERROR, NULL); } - } else { - /* Make sure that this was an inline array */ - ecs_assert(next_scope->op_count > 1, ECS_INTERNAL_ERROR, NULL); } return 0; @@ -54716,6 +55061,7 @@ static struct { ((sizeof(void*) == 4) ? INT32_MAX : INT64_MAX) }, [EcsOpEntity] = {0, INT64_MAX}, + [EcsOpId] = {0, INT64_MAX}, [EcsOpEnum] = {INT32_MIN, INT32_MAX}, [EcsOpBitmask] = {0, INT32_MAX} }; @@ -54737,6 +55083,7 @@ static struct { [EcsOpUPtr] = {0, ((sizeof(void*) == 4) ? UINT32_MAX : UINT64_MAX)}, [EcsOpIPtr] = {0, ((sizeof(void*) == 4) ? INT32_MAX : INT64_MAX)}, [EcsOpEntity] = {0, UINT64_MAX}, + [EcsOpId] = {0, UINT64_MAX}, [EcsOpEnum] = {0, INT32_MAX}, [EcsOpBitmask] = {0, UINT32_MAX} }; @@ -54761,6 +55108,7 @@ static struct { ((sizeof(void*) == 4) ? INT32_MAX : (double)INT64_MAX) }, [EcsOpEntity] = {0, (double)UINT64_MAX}, + [EcsOpId] = {0, (double)UINT64_MAX}, [EcsOpEnum] = {INT32_MIN, INT32_MAX}, [EcsOpBitmask] = {0, UINT32_MAX} }; @@ -54804,6 +55152,7 @@ case kind:\ case_T_checked(EcsOpU64, ecs_u64_t, dst, src, bounds);\ case_T_checked(EcsOpUPtr, ecs_uptr_t, dst, src, bounds);\ case_T_checked(EcsOpEntity, ecs_u64_t, dst, src, bounds);\ + case_T_checked(EcsOpId, ecs_u64_t, dst, src, bounds);\ case_T_checked(EcsOpBitmask, ecs_u32_t, dst, src, bounds) #define cases_T_bool(dst, src)\ @@ -54909,6 +55258,7 @@ int ecs_meta_set_char( case EcsOpUPtr: case EcsOpString: case EcsOpEntity: + case EcsOpId: flecs_meta_conversion_error(cursor, op, "char"); return -1; default: @@ -55113,8 +55463,8 @@ int ecs_meta_set_value( case EcsUPtr: return ecs_meta_set_uint(cursor, *(uintptr_t*)value->ptr); case EcsIPtr: return ecs_meta_set_int(cursor, *(intptr_t*)value->ptr); case EcsString: return ecs_meta_set_string(cursor, *(char**)value->ptr); - case EcsEntity: return ecs_meta_set_entity(cursor, - *(ecs_entity_t*)value->ptr); + case EcsEntity: return ecs_meta_set_entity(cursor, *(ecs_entity_t*)value->ptr); + case EcsId: return ecs_meta_set_id(cursor, *(ecs_id_t*)value->ptr); default: ecs_throw(ECS_INTERNAL_ERROR, "invalid type kind"); goto error; @@ -55271,11 +55621,11 @@ int ecs_meta_set_string( break; case EcsOpI64: case EcsOpU64: - set_T(ecs_i64_t, ptr, atol(value)); + set_T(ecs_i64_t, ptr, atoll(value)); break; case EcsOpIPtr: case EcsOpUPtr: - set_T(ecs_iptr_t, ptr, atol(value)); + set_T(ecs_iptr_t, ptr, atoll(value)); break; case EcsOpF32: set_T(ecs_f32_t, ptr, atof(value)); @@ -55325,6 +55675,26 @@ int ecs_meta_set_string( set_T(ecs_entity_t, ptr, e); break; } + case EcsOpId: { + ecs_id_t id = 0; +#ifdef FLECS_PARSER + ecs_term_t term = {0}; + if (ecs_parse_term(cursor->world, NULL, value, value, &term, NULL)) { + if (ecs_term_finalize(cursor->world, &term)) { + ecs_term_fini(&term); + goto error; + } + id = term.id; + ecs_term_fini(&term); + } else { + ecs_term_fini(&term); + goto error; + } +#endif + set_T(ecs_id_t, ptr, id); + + break; + } case EcsOpPop: ecs_err("excess element '%s' in scope", value); goto error; @@ -55410,6 +55780,7 @@ int ecs_meta_set_string_literal( case EcsOpIPtr: case EcsOpString: case EcsOpEntity: + case EcsOpId: len -= 2; char *result = ecs_os_malloc(len + 1); @@ -55445,6 +55816,9 @@ int ecs_meta_set_entity( case EcsOpEntity: set_T(ecs_entity_t, ptr, value); break; + case EcsOpId: + set_T(ecs_id_t, ptr, value); /* entities are valid ids */ + break; case EcsOpOpaque: { const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); if (opaque && opaque->assign_entity) { @@ -55490,6 +55864,64 @@ int ecs_meta_set_entity( return -1; } +int ecs_meta_set_id( + ecs_meta_cursor_t *cursor, + ecs_entity_t value) +{ + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + + switch(op->kind) { + case EcsOpId: + set_T(ecs_id_t, ptr, value); + break; + case EcsOpOpaque: { + const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque); + if (opaque && opaque->assign_id) { + opaque->assign_id(ptr, + ECS_CONST_CAST(ecs_world_t*, cursor->world), value); + break; + } + } + /* fall through */ + case EcsOpArray: + case EcsOpVector: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpEnum: + case EcsOpBitmask: + case EcsOpPrimitive: + case EcsOpBool: + case EcsOpChar: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpU64: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpI64: + case EcsOpF32: + case EcsOpF64: + case EcsOpUPtr: + case EcsOpIPtr: + case EcsOpString: + case EcsOpEntity: + flecs_meta_conversion_error(cursor, op, "id"); + goto error; + default: + ecs_throw(ECS_INVALID_PARAMETER, "invalid operation"); + break; + } + + return 0; +error: + return -1; +} + int ecs_meta_set_null( ecs_meta_cursor_t *cursor) { @@ -55533,6 +55965,7 @@ int ecs_meta_set_null( case EcsOpUPtr: case EcsOpIPtr: case EcsOpEntity: + case EcsOpId: flecs_meta_conversion_error(cursor, op, "null"); goto error; default: @@ -55571,6 +56004,7 @@ bool ecs_meta_get_bool( case EcsOpEnum: return *(ecs_i32_t*)ptr != 0; case EcsOpBitmask: return *(ecs_u32_t*)ptr != 0; case EcsOpEntity: return *(ecs_entity_t*)ptr != 0; + case EcsOpId: return *(ecs_id_t*)ptr != 0; case EcsOpArray: case EcsOpVector: case EcsOpOpaque: @@ -55623,6 +56057,7 @@ char ecs_meta_get_char( case EcsOpIPtr: case EcsOpString: case EcsOpEntity: + case EcsOpId: ecs_throw(ECS_INVALID_PARAMETER, "invalid element for char"); break; } @@ -55660,6 +56095,10 @@ int64_t ecs_meta_get_int( ecs_throw(ECS_INVALID_PARAMETER, "invalid conversion from entity to int"); break; + case EcsOpId: + ecs_throw(ECS_INVALID_PARAMETER, + "invalid conversion from id to int"); + break; case EcsOpArray: case EcsOpVector: case EcsOpOpaque: @@ -55703,6 +56142,7 @@ uint64_t ecs_meta_get_uint( case EcsOpEnum: return flecs_ito(uint64_t, *(const ecs_i32_t*)ptr); case EcsOpBitmask: return *(const ecs_u32_t*)ptr; case EcsOpEntity: return *(const ecs_entity_t*)ptr; + case EcsOpId: return *(const ecs_id_t*)ptr; case EcsOpArray: case EcsOpVector: case EcsOpOpaque: @@ -55748,6 +56188,10 @@ double flecs_meta_to_float( ecs_throw(ECS_INVALID_PARAMETER, "invalid conversion from entity to float"); break; + case EcsOpId: + ecs_throw(ECS_INVALID_PARAMETER, + "invalid conversion from id to float"); + break; case EcsOpArray: case EcsOpVector: case EcsOpOpaque: @@ -55807,6 +56251,7 @@ const char* ecs_meta_get_string( case EcsOpUPtr: case EcsOpIPtr: case EcsOpEntity: + case EcsOpId: default: ecs_throw(ECS_INVALID_PARAMETER, "invalid element for string"); break; @@ -55847,6 +56292,48 @@ ecs_entity_t ecs_meta_get_entity( case EcsOpF64: case EcsOpUPtr: case EcsOpIPtr: + case EcsOpString: + case EcsOpId: + ecs_throw(ECS_INVALID_PARAMETER, "invalid element for entity"); + break; + } +error: + return 0; +} + +ecs_entity_t ecs_meta_get_id( + const ecs_meta_cursor_t *cursor) +{ + ecs_meta_scope_t *scope = flecs_meta_cursor_get_scope(cursor); + ecs_meta_type_op_t *op = flecs_meta_cursor_get_op(scope); + void *ptr = flecs_meta_cursor_get_ptr(cursor->world, scope); + switch(op->kind) { + case EcsOpEntity: return *(ecs_id_t*)ptr; /* Entities are valid ids */ + case EcsOpId: return *(ecs_id_t*)ptr; + case EcsOpArray: + case EcsOpVector: + case EcsOpOpaque: + case EcsOpPush: + case EcsOpPop: + case EcsOpScope: + case EcsOpEnum: + case EcsOpBitmask: + case EcsOpPrimitive: + case EcsOpChar: + case EcsOpBool: + case EcsOpByte: + case EcsOpU8: + case EcsOpU16: + case EcsOpU32: + case EcsOpU64: + case EcsOpI8: + case EcsOpI16: + case EcsOpI32: + case EcsOpI64: + case EcsOpF32: + case EcsOpF64: + case EcsOpUPtr: + case EcsOpIPtr: case EcsOpString: ecs_throw(ECS_INVALID_PARAMETER, "invalid element for entity"); break; @@ -56328,7 +56815,7 @@ int flecs_add_member_to_struct( if (!member_size || !member_alignment) { char *path = ecs_get_fullpath(world, elem->type); - ecs_err("member '%s' has 0 size/alignment"); + ecs_err("member '%s' has 0 size/alignment", path); ecs_os_free(path); return -1; } @@ -56378,7 +56865,7 @@ int flecs_add_member_to_struct( if (!member_size || !member_alignment) { char *path = ecs_get_fullpath(world, elem->type); - ecs_err("member '%s' has 0 size/alignment"); + ecs_err("member '%s' has 0 size/alignment", path); ecs_os_free(path); return -1; } @@ -56629,6 +57116,9 @@ void flecs_set_primitive(ecs_iter_t *it) { case EcsEntity: init_type_t(world, e, EcsPrimitiveType, ecs_entity_t); break; + case EcsId: + init_type_t(world, e, EcsPrimitiveType, ecs_id_t); + break; } } } @@ -57190,6 +57680,7 @@ void FlecsMetaImport( ECS_PRIMITIVE(world, f64, EcsF64); ECS_PRIMITIVE(world, string, EcsString); ECS_PRIMITIVE(world, entity, EcsEntity); + ECS_PRIMITIVE(world, id, EcsId); #undef ECS_PRIMITIVE @@ -57257,7 +57748,8 @@ void FlecsMetaImport( { .name = "UPtr "}, { .name = "IPtr" }, { .name = "String" }, - { .name = "Entity" } + { .name = "Entity" }, + { .name = "Id" } } }); @@ -58560,7 +59052,8 @@ bool flecs_pipeline_build( int32_t i; for (i = 0; i < it.count; i ++) { - ecs_system_t *sys = ecs_poly(poly[i].poly, ecs_system_t); + ecs_poly_assert(poly[i].poly, ecs_system_t); + ecs_system_t *sys = (ecs_system_t*)poly[i].poly; ecs_query_t *q = sys->query; bool needs_merge = false; @@ -58666,8 +59159,8 @@ bool flecs_pipeline_build( for (i = 0; i < count; i ++) { ecs_entity_t system = systems[i]; const EcsPoly *poly = ecs_get_pair(world, system, EcsPoly, EcsSystem); - ecs_assert(poly != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_system_t *sys = ecs_poly(poly->poly, ecs_system_t); + ecs_poly_assert(poly->poly, ecs_system_t); + ecs_system_t *sys = (ecs_system_t*)poly->poly; #ifdef FLECS_LOG_1 char *path = ecs_get_fullpath(world, system); @@ -58825,8 +59318,8 @@ int32_t flecs_run_pipeline_ops( for (; i < count; i++) { ecs_entity_t system = systems[i]; const EcsPoly* poly = ecs_get_pair(world, system, EcsPoly, EcsSystem); - ecs_assert(poly != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_system_t* sys = ecs_poly(poly->poly, ecs_system_t); + ecs_poly_assert(poly->poly, ecs_system_t); + ecs_system_t* sys = (ecs_system_t*)poly->poly; /* Keep track of the last frame for which the system has ran, so we * know from where to resume the schedule in case the schedule @@ -58930,7 +59423,7 @@ void flecs_run_pipeline( int32_t si; for (si = 0; si < stage_count; si ++) { ecs_stage_t *s = &world->stages[si]; - pq->cur_op->commands_enqueued += ecs_vec_count(&s->commands); + pq->cur_op->commands_enqueued += ecs_vec_count(&s->cmd->queue); } ecs_readonly_end(world); @@ -59544,11 +60037,13 @@ typedef enum { } ecs_var_kind_t; typedef struct ecs_rule_var_t { - int8_t kind; /* variable kind (EcsVarEntity or EcsVarTable)*/ + int8_t kind; /* variable kind (EcsVarEntity or EcsVarTable) */ bool anonymous; /* variable is anonymous */ ecs_var_id_t id; /* variable id */ ecs_var_id_t table_id; /* id to table variable, if any */ + ecs_var_id_t base_id; /* id to base entity variable, for lookups */ const char *name; /* variable name */ + const char *lookup; /* Lookup string for variable */ #ifdef FLECS_DEBUG const char *label; /* for debugging */ #endif @@ -59558,32 +60053,43 @@ typedef struct ecs_rule_var_t { typedef enum { EcsRuleAnd, /* And operator: find or match id against variable source */ EcsRuleAndId, /* And operator for fixed id (no wildcards/variables) */ - EcsRuleWith, /* Match id against fixed or variable source */ EcsRuleAndAny, /* And operator with support for matching Any src/id */ + EcsRuleTriv, /* Trivial search */ + EcsRuleTrivData, /* Trivial search with setting data fields */ + EcsRuleTrivWildcard, /* Trivial search with (exclusive) wildcard ids */ + EcsRuleSelectAny, /* Dedicated instruction for _ queries where the src is unknown */ + EcsRuleUp, /* Up traversal */ + EcsRuleUpId, /* Up traversal for fixed id (like AndId) */ + EcsRuleSelfUp, /* Self|up traversal */ + EcsRuleSelfUpId, /* Self|up traversal for fixed id (like AndId) */ + EcsRuleWith, /* Match id against fixed or variable source */ EcsRuleTrav, /* Support for transitive/reflexive queries */ EcsRuleIdsRight, /* Find ids in use that match (R, *) wildcard */ EcsRuleIdsLeft, /* Find ids in use that match (*, T) wildcard */ EcsRuleEach, /* Iterate entities in table, populate entity variable */ EcsRuleStore, /* Store table or entity in variable */ EcsRuleReset, /* Reset value of variable to wildcard (*) */ - EcsRuleUnion, /* Combine output of multiple operations */ - EcsRuleEnd, /* Used to denote end of EcsRuleUnion block */ + EcsRuleOr, /* Or operator */ + EcsRuleOptional, /* Optional operator */ + EcsRuleIf, /* Conditional execution */ EcsRuleNot, /* Sets iterator state after term was not matched */ + EcsRuleEnd, /* End of control flow block */ EcsRulePredEq, /* Test if variable is equal to, or assign to if not set */ EcsRulePredNeq, /* Test if variable is not equal to */ EcsRulePredEqName, /* Same as EcsRulePredEq but with matching by name */ EcsRulePredNeqName, /* Same as EcsRulePredNeq but with matching by name */ EcsRulePredEqMatch, /* Same as EcsRulePredEq but with fuzzy matching by name */ EcsRulePredNeqMatch, /* Same as EcsRulePredNeq but with fuzzy matching by name */ + EcsRuleLookup, /* Lookup relative to variable */ EcsRuleSetVars, /* Populate it.sources from variables */ EcsRuleSetThis, /* Populate This entity variable */ EcsRuleSetFixed, /* Set fixed source entity ids */ EcsRuleSetIds, /* Set fixed (component) ids */ + EcsRuleSetId, /* Set id if not set */ EcsRuleContain, /* Test if table contains entity */ EcsRulePairEq, /* Test if both elements of pair are the same */ - EcsRuleSetCond, /* Set conditional value for EcsRuleJmpCondFalse */ - EcsRuleJmpCondFalse, /* Jump if condition is false */ - EcsRuleJmpNotSet, /* Jump if variable(s) is not set */ + EcsRulePopulate, /* Populate any data fields */ + EcsRulePopulateSelf, /* Populate only self (owned) data fields */ EcsRuleYield, /* Yield result back to application */ EcsRuleNothing /* Must be last */ } ecs_rule_op_kind_t; @@ -59628,7 +60134,48 @@ typedef struct { int16_t remaining; } ecs_rule_and_ctx_t; -/* Cache for storing results of downward traversal */ +/* Down traversal cache (for resolving up queries w/unknown source) */ +typedef struct { + ecs_table_t *table; + bool leaf; /* Table owns and inherits id (for Up queries without Self) */ +} ecs_trav_down_elem_t; + +typedef struct { + ecs_vec_t elems; /* vector */ + bool ready; +} ecs_trav_down_t; + +typedef struct { + ecs_entity_t src; + ecs_id_t id; + int32_t column; + bool ready; +} ecs_trav_up_t; + +typedef struct { + ecs_map_t src; /* map or map */ + ecs_id_t with; + ecs_flags32_t dir; +} ecs_trav_up_cache_t; + +/* And up context */ +typedef struct { + ecs_rule_and_ctx_t and; + ecs_table_t *table; + int32_t row; + int32_t end; + ecs_entity_t trav; + ecs_id_t with; + ecs_id_t matched; + ecs_id_record_t *idr_with; + ecs_id_record_t *idr_trav; + ecs_trav_down_t *down; + int32_t cache_elem; + ecs_trav_up_cache_t cache; +} ecs_rule_up_ctx_t; + +/* Cache for storing results of upward/downward "all" traversal. This type of + * traversal iterates and caches the entire tree. */ typedef struct { ecs_entity_t entity; ecs_id_record_t *idr; @@ -59675,38 +60222,37 @@ typedef struct { ecs_id_record_t *cur; } ecs_rule_ids_ctx_t; -/* Ctrlflow context (used with Union) */ +/* Control flow context */ typedef struct { - ecs_rule_lbl_t lbl; -} ecs_rule_ctrlflow_ctx_t; + ecs_rule_lbl_t op_index; + ecs_id_t field_id; +} ecs_rule_ctrl_ctx_t; -/* Condition context */ +/* Trivial context */ typedef struct { - bool cond; -} ecs_rule_cond_ctx_t; + ecs_table_cache_iter_t it; + const ecs_table_record_t *tr; +} ecs_rule_trivial_ctx_t; typedef struct ecs_rule_op_ctx_t { union { ecs_rule_and_ctx_t and; + ecs_rule_up_ctx_t up; ecs_rule_trav_ctx_t trav; ecs_rule_ids_ctx_t ids; ecs_rule_eq_ctx_t eq; ecs_rule_each_ctx_t each; ecs_rule_setthis_ctx_t setthis; - ecs_rule_ctrlflow_ctx_t ctrlflow; - ecs_rule_cond_ctx_t cond; + ecs_rule_ctrl_ctx_t ctrl; + ecs_rule_trivial_ctx_t trivial; } is; } ecs_rule_op_ctx_t; typedef struct { /* Labels used for control flow */ - ecs_rule_lbl_t lbl_union; - ecs_rule_lbl_t lbl_not; - ecs_rule_lbl_t lbl_option; + ecs_rule_lbl_t lbl_query; /* Used to find the op that does the actual searching */ + ecs_rule_lbl_t lbl_begin; ecs_rule_lbl_t lbl_cond_eval; - ecs_rule_lbl_t lbl_or; - ecs_rule_lbl_t lbl_none; - ecs_rule_lbl_t lbl_prev; /* If set, use this as default value for prev */ ecs_write_flags_t cond_written_or; /* Cond written flags at start of or chain */ bool in_or; /* Whether we're in an or chain */ } ecs_rule_compile_ctrlflow_t; @@ -59729,14 +60275,14 @@ typedef struct { typedef struct { uint64_t *written; /* Bitset to check which variables have been written */ ecs_rule_lbl_t op_index; /* Currently evaluated operation */ - ecs_rule_lbl_t prev_index; /* Previously evaluated operation */ - ecs_rule_lbl_t jump; /* Set by control flow operations to jump to operation */ ecs_var_t *vars; /* Variable storage */ ecs_iter_t *it; /* Iterator */ ecs_rule_op_ctx_t *op_ctx; /* Operation context (stack) */ ecs_world_t *world; /* Reference to world */ const ecs_rule_t *rule; /* Reference to rule */ const ecs_rule_var_t *rule_vars; /* Reference to rule variable array */ + ecs_flags32_t *source_set; /* Whether ecs_iter_t::sources is written by instruction */ + ecs_rule_iter_t *rit; } ecs_rule_run_ctx_t; typedef struct { @@ -59757,8 +60303,8 @@ struct ecs_rule_t { ecs_hashmap_t evar_index; /* Name index for entity variables */ ecs_rule_var_cache_t vars_cache; /* For trivial rules with only This variables */ char **var_names; /* Array with variable names for iterator */ + ecs_var_id_t *src_vars; /* Array with ids to source variables for fields */ - ecs_rule_op_t *ops; /* Operations */ int32_t op_count; /* Number of operations */ @@ -59802,15 +60348,19 @@ int flecs_rule_compile( ecs_allocator_t* flecs_rule_get_allocator( const ecs_iter_t *it); + +/* Traversal cache for transitive queries. Finds all reachable entities by + * following a relationship */ + /* Find all entities when traversing downwards */ -void flecs_rule_get_down_cache( +void flecs_rule_get_trav_down_cache( const ecs_rule_run_ctx_t *ctx, ecs_trav_cache_t *cache, ecs_entity_t trav, ecs_entity_t entity); /* Find all entities when traversing upwards */ -void flecs_rule_get_up_cache( +void flecs_rule_get_trav_up_cache( const ecs_rule_run_ctx_t *ctx, ecs_trav_cache_t *cache, ecs_entity_t trav, @@ -59821,6 +60371,79 @@ void flecs_rule_trav_cache_fini( ecs_allocator_t *a, ecs_trav_cache_t *cache); +/* Traversal caches for up traversal. Enables searching upwards until an entity + * with the queried for id has been found. */ + +/* Traverse downwards from starting entity to find all tables for which the + * specified entity is the source of the queried for id ('with'). */ +ecs_trav_down_t* flecs_rule_get_down_cache( + const ecs_rule_run_ctx_t *ctx, + ecs_trav_up_cache_t *cache, + ecs_entity_t trav, + ecs_entity_t entity, + ecs_id_record_t *idr_with, + bool self); + +/* Free down traversal cache */ +void flecs_rule_down_cache_fini( + ecs_allocator_t *a, + ecs_trav_up_cache_t *cache); + +ecs_trav_up_t* flecs_rule_get_up_cache( + const ecs_rule_run_ctx_t *ctx, + ecs_trav_up_cache_t *cache, + ecs_table_t *table, + ecs_id_t with, + ecs_entity_t trav, + ecs_id_record_t *idr_with, + ecs_id_record_t *idr_trav); + +/* Free up traversal cache */ +void flecs_rule_up_cache_fini( + ecs_trav_up_cache_t *cache); + +/* Convert instruction kind to string */ +const char* flecs_rule_op_str( + uint16_t kind); + +/* Iterator for trivial queries. */ +bool flecs_rule_trivial_search( + const ecs_rule_t *rule, + const ecs_rule_run_ctx_t *ctx, + ecs_rule_trivial_ctx_t *op_ctx, + bool first, + int32_t until); + +/* Iterator for trivial queries. */ +bool flecs_rule_trivial_search_nodata( + const ecs_rule_t *rule, + const ecs_rule_run_ctx_t *ctx, + ecs_rule_trivial_ctx_t *op_ctx, + bool first, + int32_t until); + +/* Iterator for trivial queries with wildcard matching. */ +bool flecs_rule_trivial_search_w_wildcards( + const ecs_rule_t *rule, + const ecs_rule_run_ctx_t *ctx, + ecs_rule_trivial_ctx_t *op_ctx, + bool first, + int32_t until); + +/* Trivial test for constrained $this. */ +bool flecs_rule_trivial_test( + const ecs_rule_t *rule, + const ecs_rule_run_ctx_t *ctx, + bool first, + int32_t term_count); + +/* Trivial test for constrained $this with wildcard matching. */ +bool flecs_rule_trivial_test_w_wildcards( + const ecs_rule_t *rule, + const ecs_rule_run_ctx_t *ctx, + bool first, + int32_t term_count); + #endif #include @@ -59837,42 +60460,52 @@ static ecs_mixins_t ecs_rule_t_mixins = { } }; -static const char* flecs_rule_op_str( uint16_t kind) { switch(kind) { - case EcsRuleAnd: return "and "; - case EcsRuleAndId: return "and_id "; - case EcsRuleAndAny: return "andany "; - case EcsRuleWith: return "with "; - case EcsRuleTrav: return "trav "; - case EcsRuleIdsRight: return "idsr "; - case EcsRuleIdsLeft: return "idsl "; - case EcsRuleEach: return "each "; - case EcsRuleStore: return "store "; - case EcsRuleReset: return "reset "; - case EcsRuleUnion: return "union "; - case EcsRuleEnd: return "end "; - case EcsRuleNot: return "not "; - case EcsRulePredEq: return "eq "; - case EcsRulePredNeq: return "neq "; - case EcsRulePredEqName: return "eq_nm "; - case EcsRulePredNeqName: return "neq_nm "; - case EcsRulePredEqMatch: return "eq_m "; - case EcsRulePredNeqMatch: return "neq_m "; - case EcsRuleSetVars: return "setvars "; - case EcsRuleSetThis: return "setthis "; - case EcsRuleSetFixed: return "setfix "; - case EcsRuleSetIds: return "setids "; - case EcsRuleContain: return "contain "; - case EcsRulePairEq: return "pair_eq "; - case EcsRuleSetCond: return "setcond "; - case EcsRuleJmpCondFalse: return "jfalse "; - case EcsRuleJmpNotSet: return "jnotset "; - case EcsRuleYield: return "yield "; - case EcsRuleNothing: return "nothing "; - default: return "!invalid"; + case EcsRuleAnd: return "and "; + case EcsRuleAndId: return "andid "; + case EcsRuleAndAny: return "andany "; + case EcsRuleTriv: return "triv "; + case EcsRuleTrivData: return "trivpop "; + case EcsRuleTrivWildcard: return "trivwc "; + case EcsRuleSelectAny: return "any "; + case EcsRuleUp: return "up "; + case EcsRuleUpId: return "upid "; + case EcsRuleSelfUp: return "selfup "; + case EcsRuleSelfUpId: return "selfupid"; + case EcsRuleWith: return "with "; + case EcsRuleTrav: return "trav "; + case EcsRuleIdsRight: return "idsr "; + case EcsRuleIdsLeft: return "idsl "; + case EcsRuleEach: return "each "; + case EcsRuleStore: return "store "; + case EcsRuleReset: return "reset "; + case EcsRuleOr: return "or "; + case EcsRuleOptional: return "option "; + case EcsRuleIf: return "if "; + case EcsRuleEnd: return "end "; + case EcsRuleNot: return "not "; + case EcsRulePredEq: return "eq "; + case EcsRulePredNeq: return "neq "; + case EcsRulePredEqName: return "eq_nm "; + case EcsRulePredNeqName: return "neq_nm "; + case EcsRulePredEqMatch: return "eq_m "; + case EcsRulePredNeqMatch: return "neq_m "; + case EcsRuleLookup: return "lookup "; + case EcsRuleSetVars: return "setvars "; + case EcsRuleSetThis: return "setthis "; + case EcsRuleSetFixed: return "setfix "; + case EcsRuleSetIds: return "setids "; + case EcsRuleSetId: return "setid "; + case EcsRuleContain: return "contain "; + case EcsRulePairEq: return "pair_eq "; + case EcsRulePopulate: return "pop "; + case EcsRulePopulateSelf: return "popself "; + case EcsRuleYield: return "yield "; + case EcsRuleNothing: return "nothing "; + default: return "!invalid"; } } @@ -60045,20 +60678,17 @@ char* ecs_rule_str_w_profile( ecs_strbuf_appendstr(&buf, " "); int32_t written = ecs_strbuf_written(&buf); - for (int32_t j = 0; j < (10 - (written - start)); j ++) { - ecs_strbuf_appendch(&buf, ' '); - } - - if (op->kind == EcsRuleJmpCondFalse || op->kind == EcsRuleSetCond || - op->kind == EcsRuleJmpNotSet) - { - ecs_strbuf_appendint(&buf, op->other); + for (int32_t j = 0; j < (12 - (written - start)); j ++) { ecs_strbuf_appendch(&buf, ' '); } hidden_chars = flecs_rule_op_ref_str(rule, &op->src, src_flags, &buf); - if (op->kind == EcsRuleUnion) { + if (op->kind == EcsRuleNot || + op->kind == EcsRuleOr || + op->kind == EcsRuleOptional || + op->kind == EcsRuleIf) + { indent ++; } @@ -60072,6 +60702,11 @@ char* ecs_rule_str_w_profile( ecs_strbuf_appendch(&buf, ' '); } + if (!first_flags && !second_flags) { + ecs_strbuf_appendstr(&buf, "\n"); + continue; + } + ecs_strbuf_appendstr(&buf, "("); flecs_rule_op_ref_str(rule, &op->first, first_flags, &buf); @@ -60090,6 +60725,13 @@ char* ecs_rule_str_w_profile( ecs_strbuf_appendstr(&buf, "\"#[reset]"); break; } + case EcsRuleLookup: { + ecs_var_id_t src_id = op->src.var; + ecs_strbuf_appendstr(&buf, ", #[yellow]\""); + ecs_strbuf_appendstr(&buf, rule->vars[src_id].lookup); + ecs_strbuf_appendstr(&buf, "\"#[reset]"); + break; + } default: break; } @@ -60102,7 +60744,7 @@ char* ecs_rule_str_w_profile( #ifdef FLECS_LOG char *str = ecs_strbuf_get(&buf); - flecs_colorize_buf(str, true, &buf); + flecs_colorize_buf(str, ecs_os_api.flags_ & EcsOsApiLogWithColors, &buf); ecs_os_free(str); #endif return ecs_strbuf_get(&buf); @@ -60219,7 +60861,7 @@ const char* ecs_rule_parse_vars( #endif - /** +/** * @file addons/rules/compile.c * @brief Compile rule program from filter. */ @@ -60229,17 +60871,6 @@ const char* ecs_rule_parse_vars( #define FlecsRuleOrMarker ((int16_t)-2) /* Marks instruction in OR chain */ -static bool flecs_rule_op_is_test[] = { - [EcsRuleAnd] = true, - [EcsRuleAndAny] = true, - [EcsRuleAndId] = true, - [EcsRuleWith] = true, - [EcsRuleTrav] = true, - [EcsRuleContain] = true, - [EcsRulePairEq] = true, - [EcsRuleNothing] = false -}; - ecs_rule_lbl_t flecs_itolbl(int64_t val) { return flecs_ito(int16_t, val); } @@ -60480,6 +61111,14 @@ ecs_var_id_t flecs_rule_add_var( ecs_vec_t *vars, ecs_var_kind_t kind) { + const char *dot = NULL; + if (name) { + dot = strchr(name, '.'); + if (dot) { + kind = EcsVarEntity; /* lookup variables are always entities */ + } + } + ecs_hashmap_t *var_index = NULL; ecs_var_id_t var_id = EcsVarNone; if (name) { @@ -60515,28 +61154,38 @@ ecs_var_id_t flecs_rule_add_var( } ecs_rule_var_t *var; + ecs_var_id_t result; if (vars) { var = ecs_vec_append_t(NULL, vars, ecs_rule_var_t); - var->id = flecs_itovar(ecs_vec_count(vars)); + result = var->id = flecs_itovar(ecs_vec_count(vars)); } else { - ecs_dbg_assert(rule->var_count < rule->var_size, ECS_INTERNAL_ERROR, NULL); + ecs_dbg_assert(rule->var_count < rule->var_size, + ECS_INTERNAL_ERROR, NULL); var = &rule->vars[rule->var_count]; - var->id = flecs_itovar(rule->var_count); + result = var->id = flecs_itovar(rule->var_count); rule->var_count ++; } var->kind = flecs_ito(int8_t, kind); var->name = name; var->table_id = var_id; + var->base_id = 0; + var->lookup = NULL; flecs_set_var_label(var, NULL); if (name) { flecs_name_index_init_if(var_index, NULL); flecs_name_index_ensure(var_index, var->id, name, 0, 0); - var->anonymous = name ? name[0] == '_' : false; + var->anonymous = name[0] == '_'; + + /* Handle variables that require a by-name lookup, e.g. $this.wheel */ + if (dot != NULL) { + ecs_assert(var->table_id == EcsVarNone, ECS_INTERNAL_ERROR, NULL); + var->lookup = dot + 1; + } } - return var->id; + return result; } static @@ -60554,8 +61203,21 @@ ecs_var_id_t flecs_rule_add_var_for_term_id( return flecs_rule_add_var(rule, name, vars, kind); } +/* This function walks over terms to discover which variables are used in the + * query. It needs to provide the following functionality: + * - create table vars for all variables used as source + * - create entity vars for all variables not used as source + * - create entity vars for all non-$this vars + * - create anonymous vars to store the content of wildcards + * - create anonymous vars to store result of lookups (for $var.child_name) + * - create anonymous vars for resolving component inheritance + * - create array that stores the source variable for each field + * - ensure table vars for non-$this variables are anonymous + * - ensure variables created inside scopes are anonymous + * - place anonymous variables after public variables in vars array + */ static -void flecs_rule_discover_vars( +int flecs_rule_discover_vars( ecs_stage_t *stage, ecs_rule_t *rule) { @@ -60563,7 +61225,7 @@ void flecs_rule_discover_vars( ecs_vec_reset_t(NULL, vars, ecs_rule_var_t); ecs_term_t *terms = rule->filter.terms; - int32_t i, anonymous_count = 0, count = rule->filter.term_count; + int32_t a, i, anonymous_count = 0, count = rule->filter.term_count; int32_t anonymous_table_count = 0, scope = 0, scoped_var_index = 0; bool table_this = false, entity_before_table_this = false; @@ -60622,20 +61284,35 @@ void flecs_rule_discover_vars( if (var_id == EcsVarNone || var_id == first_var_id) { var_id = flecs_rule_add_var( rule, var_name, vars, EcsVarEntity); + } + if (var_id != EcsVarNone) { /* Mark variable as one for which we need to create a table * variable. Don't create table variable now, so that we can * store it in the non-public part of the variable array. */ ecs_rule_var_t *var = ecs_vec_get_t( vars, ecs_rule_var_t, (int32_t)var_id - 1); ecs_assert(var != NULL, ECS_INTERNAL_ERROR, NULL); - var->kind = EcsVarAny; + if (!var->lookup) { + var->kind = EcsVarAny; + anonymous_table_count ++; + } - anonymous_table_count ++; - } + if (!(term->flags & EcsTermNoData)) { + /* Can't have an anonymous variable as source of a term + * that returns a component. We need to return each + * instance of the component, whereas anonymous + * variables are not guaranteed to be resolved to + * individual entities. */ + if (var->anonymous) { + ecs_err( + "can't use anonymous variable '%s' as source of " + "data term", var->name); + goto error; + } + } - if (var_id != EcsVarNone) { - /* Track of which variable ids are used as field source */ + /* Track which variable ids are used as field source */ if (!rule->src_vars) { rule->src_vars = ecs_os_calloc_n(ecs_var_id_t, rule->filter.field_count); @@ -60684,6 +61361,68 @@ void flecs_rule_discover_vars( } int32_t var_count = ecs_vec_count(vars); + ecs_var_id_t placeholder = EcsVarNone - 1; + bool replace_placeholders = false; + + /* Ensure lookup variables have table and/or entity variables */ + for (i = 0; i < var_count; i ++) { + ecs_rule_var_t *var = ecs_vec_get_t(vars, ecs_rule_var_t, i); + if (var->lookup) { + char *var_name = ecs_os_strdup(var->name); + var_name[var->lookup - var->name - 1] = '\0'; + + ecs_var_id_t base_table_id = flecs_rule_find_var_id( + rule, var_name, EcsVarTable); + if (base_table_id != EcsVarNone) { + var->table_id = base_table_id; + } else if (anonymous_table_count) { + /* Scan for implicit anonymous table variables that haven't been + * inserted yet (happens after this step). Doing this here vs. + * ensures that anonymous variables are appended at the end of + * the variable array, while also ensuring that variable ids are + * stable (no swapping of table var ids that are in use). */ + for (a = 0; a < var_count; a ++) { + ecs_rule_var_t *avar = ecs_vec_get_t( + vars, ecs_rule_var_t, a); + if (avar->kind == EcsVarAny) { + if (!ecs_os_strcmp(avar->name, var_name)) { + base_table_id = (ecs_var_id_t)(a + 1); + break; + } + } + } + if (base_table_id != EcsVarNone) { + /* Set marker so we can set the new table id afterwards */ + var->table_id = placeholder; + replace_placeholders = true; + } + } + + ecs_var_id_t base_entity_id = flecs_rule_find_var_id( + rule, var_name, EcsVarEntity); + if (base_entity_id == EcsVarNone) { + /* Get name from table var (must exist). We can't use allocated + * name since variables don't own names. */ + const char *base_name = NULL; + if (base_table_id) { + ecs_rule_var_t *base_table_var = ecs_vec_get_t( + vars, ecs_rule_var_t, (int32_t)base_table_id - 1); + base_name = base_table_var->name; + } else { + base_name = EcsThisName; + } + + base_entity_id = flecs_rule_add_var( + rule, base_name, vars, EcsVarEntity); + var = ecs_vec_get_t(vars, ecs_rule_var_t, i); + } + + var->base_id = base_entity_id; + + ecs_os_free(var_name); + } + } + var_count = ecs_vec_count(vars); /* Add non-This table variables */ if (anonymous_table_count) { @@ -60703,6 +61442,25 @@ void flecs_rule_discover_vars( var_count = ecs_vec_count(vars); } + /* If any forward references to newly added anonymous tables exist, replace + * them with the actual table variable ids. */ + if (replace_placeholders) { + for (i = 0; i < var_count; i ++) { + ecs_rule_var_t *var = ecs_vec_get_t(vars, ecs_rule_var_t, i); + if (var->table_id == placeholder) { + char *var_name = ecs_os_strdup(var->name); + var_name[var->lookup - var->name - 1] = '\0'; + + var->table_id = flecs_rule_find_var_id( + rule, var_name, EcsVarTable); + ecs_assert(var->table_id != EcsVarNone, + ECS_INTERNAL_ERROR, NULL); + + ecs_os_free(var_name); + } + } + } + /* Always include spot for This variable, even if rule doesn't use it */ var_count ++; @@ -60731,6 +61489,7 @@ void flecs_rule_discover_vars( flecs_set_var_label(&rule_vars[0], NULL); rule_vars[0].id = 0; rule_vars[0].table_id = EcsVarNone; + rule_vars[0].lookup = NULL; var_names[0] = ECS_CONST_CAST(char*, rule_vars[0].name); rule_vars ++; var_names ++; @@ -60746,6 +61505,18 @@ void flecs_rule_discover_vars( /* Hide anonymous table variables from application */ rule->var_pub_count -= anonymous_table_count; + + /* Sanity check to make sure that the public part of the variable array only + * contains entity variables. */ +#ifdef FLECS_DEBUG + for (i = 1 /* first element = $this */; i < rule->var_pub_count; i ++) { + ecs_assert(rule->vars[i].kind == EcsVarEntity, ECS_INTERNAL_ERROR, NULL); + } +#endif + + return 0; +error: + return -1; } static @@ -60759,13 +61530,6 @@ ecs_var_id_t flecs_rule_most_specific_var( return flecs_rule_find_var_id(rule, name, kind); } - ecs_var_id_t tvar = flecs_rule_find_var_id(rule, name, EcsVarTable); - if ((tvar != EcsVarNone) && !flecs_rule_is_written(tvar, ctx->written)) { - /* If variable of any kind is requested and variable hasn't been written - * yet, write to table variable */ - return tvar; - } - ecs_var_id_t evar = flecs_rule_find_var_id(rule, name, EcsVarEntity); if ((evar != EcsVarNone) && flecs_rule_is_written(evar, ctx->written)) { /* If entity variable is available and written to, it contains the most @@ -60773,10 +61537,20 @@ ecs_var_id_t flecs_rule_most_specific_var( return evar; } + ecs_var_id_t tvar = flecs_rule_find_var_id(rule, name, EcsVarTable); + if ((tvar != EcsVarNone) && !flecs_rule_is_written(tvar, ctx->written)) { + /* If variable of any kind is requested and variable hasn't been written + * yet, write to table variable */ + return tvar; + } + /* If table var is written, and entity var doesn't exist or is not written, * return table var */ - ecs_assert(tvar != EcsVarNone, ECS_INTERNAL_ERROR, NULL); - return tvar; + if (tvar != EcsVarNone) { + return tvar; + } else { + return evar; + } } static @@ -60788,158 +61562,59 @@ ecs_rule_lbl_t flecs_rule_op_insert( int32_t count = ecs_vec_count(ctx->ops); *elem = *op; if (count > 1) { - if (ctx->cur->lbl_union == -1) { + if (ctx->cur->lbl_begin == -1) { /* Variables written by previous instruction can't be written by * this instruction, except when this is a union. */ elem->written &= ~elem[-1].written; } } - if (ctx->cur->lbl_union != -1) { - elem->prev = ctx->cur->lbl_union; - } else if (ctx->cur->lbl_prev != -1) { - elem->prev = ctx->cur->lbl_prev; - ctx->cur->lbl_prev = -1; - } else { - elem->prev = flecs_itolbl(count - 2); - } - elem->next = flecs_itolbl(count); + elem->prev = flecs_itolbl(count - 2); return flecs_itolbl(count - 1); } static -int32_t flecs_rule_not_insert( - ecs_rule_op_t *op, - ecs_rule_compile_ctx_t *ctx) -{ - ecs_rule_op_t *op_last = ecs_vec_last_t(ctx->ops, ecs_rule_op_t); - if (op_last && op_last->kind == EcsRuleNot) { - /* There can be multiple reasons for inserting a Not operation, ensure - * that only one is created. */ - ecs_assert(op_last->field_index == op->field_index, - ECS_INTERNAL_ERROR, NULL); - return ecs_vec_count(ctx->ops) - 1; - } - - ecs_rule_op_t not_op = {0}; - not_op.kind = EcsRuleNot; - not_op.field_index = op->field_index; - not_op.first = op->first; - not_op.second = op->second; - not_op.flags = op->flags; - not_op.flags &= (ecs_flags8_t)~(EcsRuleIsVar << EcsRuleSrc); - if (op->flags & (EcsRuleIsEntity << EcsRuleSrc)) { - not_op.src.entity = op->src.entity; - } - - return flecs_rule_op_insert(¬_op, ctx); -} - -static -void flecs_rule_begin_none( +ecs_rule_op_t* flecs_rule_begin_block( + ecs_rule_op_kind_t kind, ecs_rule_compile_ctx_t *ctx) { - ctx->cur->lbl_none = flecs_itolbl(ecs_vec_count(ctx->ops)); - - ecs_rule_op_t jmp = {0}; - jmp.kind = EcsRuleJmpCondFalse; - flecs_rule_op_insert(&jmp, ctx); + ecs_rule_op_t op = {0}; + op.kind = flecs_ito(uint8_t, kind); + ctx->cur->lbl_begin = flecs_rule_op_insert(&op, ctx); + return ecs_vec_get_t(ctx->ops, ecs_rule_op_t, ctx->cur->lbl_begin); } static -void flecs_rule_begin_not( +void flecs_rule_end_block( ecs_rule_compile_ctx_t *ctx) { - ctx->cur->lbl_not = flecs_itolbl(ecs_vec_count(ctx->ops)); -} - -static -void flecs_rule_end_not( - ecs_rule_op_t *op, - ecs_rule_compile_ctx_t *ctx, - bool update_labels) -{ - if (ctx->cur->lbl_none != -1) { - ecs_rule_op_t setcond = {0}; - setcond.kind = EcsRuleSetCond; - setcond.other = ctx->cur->lbl_none; - flecs_rule_op_insert(&setcond, ctx); - } - - flecs_rule_not_insert(op, ctx); - + ecs_rule_op_t new_op = {0}; + new_op.kind = EcsRuleEnd; + ecs_rule_lbl_t end = flecs_rule_op_insert(&new_op, ctx); + ecs_rule_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_rule_op_t); - int32_t i, count = ecs_vec_count(ctx->ops); - if (update_labels) { - for (i = ctx->cur->lbl_not; i < count; i ++) { - ecs_rule_op_t *cur = &ops[i]; - if (flecs_rule_op_is_test[cur->kind]) { - cur->prev = flecs_itolbl(count - 1); - if (i == (count - 2)) { - cur->next = flecs_itolbl(ctx->cur->lbl_not - 1); - } - } - } - } - - /* After a match was found, return to op before Not operation */ - ecs_rule_op_t *not_ptr = ecs_vec_last_t(ctx->ops, ecs_rule_op_t); - not_ptr->prev = flecs_itolbl(ctx->cur->lbl_not - 1); - - if (ctx->cur->lbl_none != -1) { - /* setcond */ - ops[count - 2].next = flecs_itolbl(ctx->cur->lbl_none - 1); - /* last actual instruction */ - if (update_labels) { - ops[count - 3].prev = flecs_itolbl(count - 4); - } - /* jfalse */ - ops[ctx->cur->lbl_none].other = - flecs_itolbl(count - 1); /* jump to not */ - /* not */ - ops[count - 1].prev = flecs_itolbl(ctx->cur->lbl_none - 1); - } - - ctx->cur->lbl_not = -1; - ctx->cur->lbl_none = -1; -} - -static -void flecs_rule_begin_option( - ecs_rule_compile_ctx_t *ctx) -{ - ctx->cur->lbl_option = flecs_itolbl(ecs_vec_count(ctx->ops)); - - { - ecs_rule_op_t new_op = {0}; - new_op.kind = EcsRuleJmpCondFalse; - flecs_rule_op_insert(&new_op, ctx); + ops[ctx->cur->lbl_begin].next = end; + + ecs_rule_op_t *end_op = &ops[end]; + if (ctx->cur->lbl_query != -1) { + ecs_rule_op_t *query_op = &ops[ctx->cur->lbl_query]; + end_op->prev = ctx->cur->lbl_begin; + end_op->src = query_op->src; + end_op->first = query_op->first; + end_op->second = query_op->second; + end_op->flags = query_op->flags; + end_op->field_index = query_op->field_index; + } else { + end_op->prev = ctx->cur->lbl_begin; + end_op->field_index = -1; } -} -static -void flecs_rule_end_option( - ecs_rule_op_t *op, - ecs_rule_compile_ctx_t *ctx) -{ - flecs_rule_not_insert(op, ctx); - ecs_rule_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_rule_op_t); - int32_t count = ecs_vec_count(ctx->ops); - - ops[ctx->cur->lbl_option].other = flecs_itolbl(count - 1); - ops[count - 2].next = flecs_itolbl(count); - - ecs_rule_op_t new_op = {0}; - new_op.kind = EcsRuleSetCond; - new_op.other = ctx->cur->lbl_option; - flecs_rule_op_insert(&new_op, ctx); - - ctx->cur->lbl_option = -1; + ctx->cur->lbl_begin = -1; } static -void flecs_rule_begin_cond_eval( +void flecs_rule_begin_block_cond_eval( ecs_rule_op_t *op, ecs_rule_compile_ctx_t *ctx, ecs_write_flags_t cond_write_state) @@ -60974,7 +61649,7 @@ void flecs_rule_begin_cond_eval( ctx->cur->lbl_cond_eval = flecs_itolbl(ecs_vec_count(ctx->ops)); ecs_rule_op_t jmp_op = {0}; - jmp_op.kind = EcsRuleJmpNotSet; + jmp_op.kind = EcsRuleIf; if ((first_var != EcsVarNone) && cond_write_state & (1ull << first_var)) { jmp_op.flags |= (EcsRuleIsVar << EcsRuleFirst); @@ -60996,152 +61671,88 @@ void flecs_rule_begin_cond_eval( } static -void flecs_rule_end_cond_eval( - ecs_rule_op_t *op, +void flecs_rule_end_block_cond_eval( ecs_rule_compile_ctx_t *ctx) { if (ctx->cur->lbl_cond_eval == -1) { return; } - flecs_rule_not_insert(op, ctx); + ecs_assert(ctx->cur->lbl_query >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_rule_op_t end_op = {0}; + end_op.kind = EcsRuleEnd; + ecs_rule_lbl_t end = flecs_rule_op_insert(&end_op, ctx); ecs_rule_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_rule_op_t); - int32_t count = ecs_vec_count(ctx->ops); - ops[ctx->cur->lbl_cond_eval].other = flecs_itolbl(count - 1); - ops[count - 2].next = flecs_itolbl(count); -} - -static -void flecs_rule_insert_reset_after_or( - ecs_rule_compile_ctx_t *ctx) -{ - /* Scan which variables were conditionally written in the OR chain and - * reset instructions after the OR chain. If a variable is set in part one - * of a chain but not part two, there would be nothing writing to the - * variable in part two, leaving it to the previous value. To address this - * a reset is inserted that resets the variable value on redo. */ - int32_t i; - for (i = 1; i < (8 * ECS_SIZEOF(ecs_write_flags_t)); i ++) { - ecs_write_flags_t prev = 1 & (ctx->ctrlflow->cond_written_or >> i); - ecs_write_flags_t cur = 1 & (ctx->cond_written >> i); + ops[ctx->cur->lbl_cond_eval].next = end; - if (!prev && cur) { - ecs_rule_op_t reset_op = {0}; - reset_op.kind = EcsRuleReset; - reset_op.flags |= (EcsRuleIsVar << EcsRuleSrc); - reset_op.src.var = flecs_itovar(i); - flecs_rule_op_insert(&reset_op, ctx); - } - } + ecs_rule_op_t *end_op_ptr = &ops[end]; + ecs_rule_op_t *query_op = &ops[ctx->cur->lbl_query]; + end_op_ptr->prev = ctx->cur->lbl_cond_eval; + end_op_ptr->src = query_op->src; + end_op_ptr->first = query_op->first; + end_op_ptr->second = query_op->second; + end_op_ptr->flags = query_op->flags; + end_op_ptr->field_index = query_op->field_index; } static -void flecs_rule_next_or( +void flecs_rule_begin_block_or( + ecs_rule_op_t *op, ecs_rule_compile_ctx_t *ctx) { - ecs_rule_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_rule_op_t); - int32_t count = ecs_vec_count(ctx->ops); - ecs_rule_op_t *op = &ops[count - 1]; - - if (op->kind == EcsRuleNot) { - /* If the engine reaches a Not operation, it means that the term in the - * chain was negated. This can happen when part(s) of the term have - * conditionally set variables: if a variable was not set this will - * cause the term to automatically evaluate to false. - * In this case, the Not operation should proceed to the next term in - * the chain, whereas the preceding operation should upon success skip - * to the end of the chain, which is what the marker is replaced with. */ - ops[count - 2].next = FlecsRuleOrMarker; - } else { - ops[count - 1].next = FlecsRuleOrMarker; + ecs_rule_op_t *or_op = flecs_rule_begin_block(EcsRuleNot, ctx); + or_op->kind = EcsRuleOr; + if (op->flags & (EcsRuleIsVar << EcsRuleSrc)) { + or_op->flags = (EcsRuleIsVar << EcsRuleSrc); + or_op->src = op->src; } } static -void flecs_rule_begin_or( - ecs_rule_compile_ctx_t *ctx, - ecs_rule_lbl_t lbl_start) -{ - ctx->cur->lbl_or = flecs_itolbl(lbl_start); - flecs_rule_next_or(ctx); -} - -static -void flecs_rule_end_or( +void flecs_rule_end_block_or( ecs_rule_compile_ctx_t *ctx) { - flecs_rule_next_or(ctx); - + ecs_rule_op_t op = {0}; + op.kind = EcsRuleEnd; + ecs_rule_lbl_t end = flecs_rule_op_insert(&op, ctx); + ecs_rule_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_rule_op_t); - int32_t i, count = ecs_vec_count(ctx->ops); - int32_t prev_or = -2; - - for (i = ctx->cur->lbl_or; i < count; i ++) { + int32_t i, prev_or = -2; + for (i = ctx->cur->lbl_begin + 1; i < end; i ++) { if (ops[i].next == FlecsRuleOrMarker) { if (prev_or != -2) { ops[prev_or].prev = flecs_itolbl(i); } - ops[i].next = flecs_itolbl(count); + ops[i].next = flecs_itolbl(end); prev_or = i; } } - ecs_rule_op_t *op = &ops[count - 1]; - op->prev = flecs_itolbl(ctx->cur->lbl_or - 1); - - if (op->kind == EcsRuleNot) { - /* If last op is a Not operation it means that this was a dependent term - * which should be ignored if one or more of its variables weren't set - * yet. In addition to setting the prev label of the Not operation, also - * set the prev label of the actual operation to the start of the OR - * chain. */ - op[-1].prev = flecs_itolbl(ctx->cur->lbl_or - 1); - } - - /* Set prev of next instruction to before the start of the OR chain */ - ctx->cur->lbl_prev = flecs_itolbl(ctx->cur->lbl_or - 1); - ctx->cur->lbl_or = -1; - - flecs_rule_insert_reset_after_or(ctx); - - ctx->ctrlflow->in_or = false; -} - -static -void flecs_rule_begin_union( - ecs_rule_compile_ctx_t *ctx) -{ - ecs_rule_op_t op = {0}; - op.kind = EcsRuleUnion; - ctx->cur->lbl_union = flecs_rule_op_insert(&op, ctx); -} + ops[ctx->cur->lbl_begin].next = flecs_itolbl(end); + ops[end].prev = ctx->cur->lbl_begin; + ops[end - 1].prev = ctx->cur->lbl_begin; -static -void flecs_rule_end_union( - ecs_rule_compile_ctx_t *ctx) -{ - flecs_rule_next_or(ctx); + /* Scan which variables were conditionally written in the OR chain and + * reset instructions after the OR chain. If a variable is set in part one + * of a chain but not part two, there would be nothing writing to the + * variable in part two, leaving it to the previous value. To address this + * a reset is inserted that resets the variable value on redo. */ + for (i = 1; i < (8 * ECS_SIZEOF(ecs_write_flags_t)); i ++) { + ecs_write_flags_t prev = 1 & (ctx->ctrlflow->cond_written_or >> i); + ecs_write_flags_t cur = 1 & (ctx->cond_written >> i); - ecs_rule_op_t op = {0}; - op.kind = EcsRuleEnd; - ctx->cur->lbl_union = -1; - ecs_rule_lbl_t next = flecs_rule_op_insert(&op, ctx); - - ecs_rule_op_t *ops = ecs_vec_first_t(ctx->ops, ecs_rule_op_t); - int32_t i = ecs_vec_count(ctx->ops) - 2; - for (; i >= 0 && (ops[i].kind != EcsRuleUnion); i --) { - if (ops[i].next == FlecsRuleOrMarker) { - ops[i].next = next; + if (!prev && cur) { + ecs_rule_op_t reset_op = {0}; + reset_op.kind = EcsRuleReset; + reset_op.flags |= (EcsRuleIsVar << EcsRuleSrc); + reset_op.src.var = flecs_itovar(i); + flecs_rule_op_insert(&reset_op, ctx); } } - ops[next].prev = flecs_itolbl(i); - ops[i].next = next; - - flecs_rule_insert_reset_after_or(ctx); - ctx->ctrlflow->in_or = false; + ctx->cur->lbl_begin = -1; } static @@ -61162,6 +61773,24 @@ void flecs_rule_insert_each( flecs_rule_op_insert(&each, ctx); } +static +void flecs_rule_insert_lookup( + ecs_var_id_t base_var, + ecs_var_id_t evar, + ecs_rule_compile_ctx_t *ctx, + bool cond_write) +{ + ecs_rule_op_t lookup = {0}; + lookup.kind = EcsRuleLookup; + lookup.src.var = evar; + lookup.first.var = base_var; + lookup.flags = (EcsRuleIsVar << EcsRuleSrc) | + (EcsRuleIsVar << EcsRuleFirst); + flecs_rule_write_ctx(evar, ctx, cond_write); + flecs_rule_write(evar, &lookup.written); + flecs_rule_op_insert(&lookup, ctx); +} + static void flecs_rule_insert_unconstrained_transitive( ecs_rule_t *rule, @@ -61248,7 +61877,8 @@ void flecs_rule_compile_term_id( ecs_rule_ref_t *ref, ecs_flags8_t ref_kind, ecs_var_kind_t kind, - ecs_rule_compile_ctx_t *ctx) + ecs_rule_compile_ctx_t *ctx, + bool create_wildcard_vars) { (void)world; @@ -61261,7 +61891,8 @@ void flecs_rule_compile_term_id( const char *name = flecs_term_id_var_name(term_id); if (name) { ref->var = flecs_rule_most_specific_var(rule, name, kind, ctx); - } else { + ecs_assert(ref->var != EcsVarNone, ECS_INTERNAL_ERROR, NULL); + } else if (create_wildcard_vars) { bool is_wildcard = flecs_term_id_is_wildcard(term_id); if (is_wildcard && (kind == EcsVarAny)) { ref->var = flecs_rule_add_var(rule, NULL, NULL, EcsVarTable); @@ -61272,8 +61903,8 @@ void flecs_rule_compile_term_id( flecs_set_var_label(&rule->vars[ref->var], ecs_get_name(world, term_id->id)); } + ecs_assert(ref->var != EcsVarNone, ECS_INTERNAL_ERROR, NULL); } - ecs_assert(ref->var != EcsVarNone, ECS_INTERNAL_ERROR, NULL); } if (term_id->flags & EcsIsEntity) { @@ -61283,13 +61914,14 @@ void flecs_rule_compile_term_id( } static -bool flecs_rule_compile_ensure_vars( +int flecs_rule_compile_ensure_vars( ecs_rule_t *rule, ecs_rule_op_t *op, ecs_rule_ref_t *ref, ecs_flags16_t ref_kind, ecs_rule_compile_ctx_t *ctx, - bool cond_write) + bool cond_write, + bool *written_out) { ecs_flags16_t flags = flecs_rule_ref_flags(op->flags, ref_kind); bool written = false; @@ -61297,16 +61929,40 @@ bool flecs_rule_compile_ensure_vars( if (flags & EcsRuleIsVar) { ecs_var_id_t var_id = ref->var; ecs_rule_var_t *var = &rule->vars[var_id]; - if (var->kind == EcsVarEntity && !flecs_rule_is_written(var_id, ctx->written)) { + + if (var->kind == EcsVarEntity && + !flecs_rule_is_written(var_id, ctx->written)) + { /* If entity variable is not yet written but a table variant exists * that has been written, insert each operation to translate from * entity variable to table */ ecs_var_id_t tvar = var->table_id; - if ((tvar != EcsVarNone) && flecs_rule_is_written(tvar, ctx->written)) { - flecs_rule_insert_each(tvar, var_id, ctx, cond_write); + if ((tvar != EcsVarNone) && + flecs_rule_is_written(tvar, ctx->written)) + { + if (var->lookup) { + if (!flecs_rule_is_written(tvar, ctx->written)) { + ecs_err("dependent variable of '$%s' is not written", + var->name); + return -1; + } + + if (!flecs_rule_is_written(var->base_id, ctx->written)) { + flecs_rule_insert_each( + tvar, var->base_id, ctx, cond_write); + } + } else { + flecs_rule_insert_each(tvar, var_id, ctx, cond_write); + } /* Variable was written, just not as entity */ written = true; + } else if (var->lookup) { + if (!flecs_rule_is_written(var->base_id, ctx->written)) { + ecs_err("dependent variable of '$%s' is not written", + var->name); + return -1; + } } } @@ -61320,7 +61976,27 @@ bool flecs_rule_compile_ensure_vars( written = true; } - return written; + if (written_out) { + *written_out = written; + } + + return 0; +} + +static +bool flecs_rule_compile_lookup( + ecs_rule_t *rule, + ecs_var_id_t var_id, + ecs_rule_compile_ctx_t *ctx, + bool cond_write) +{ + ecs_rule_var_t *var = &rule->vars[var_id]; + if (var->lookup) { + flecs_rule_insert_lookup(var->base_id, var_id, ctx, cond_write); + return true; + } else { + return false; + } } static @@ -61335,8 +62011,8 @@ void flecs_rule_insert_contains( contains.kind = EcsRuleContain; contains.src.var = src_var; contains.first.var = other_var; - contains.flags |=(EcsRuleIsVar << EcsRuleSrc) | - (EcsRuleIsVar << EcsRuleFirst); + contains.flags |= (EcsRuleIsVar << EcsRuleSrc) | + (EcsRuleIsVar << EcsRuleFirst); flecs_rule_op_insert(&contains, ctx); } } @@ -61431,15 +62107,85 @@ int flecs_rule_compile_builtin_pred( return 0; } +static +int flecs_rule_ensure_scope_var( + ecs_rule_t *rule, + ecs_rule_op_t *op, + ecs_rule_ref_t *ref, + ecs_flags16_t ref_kind, + ecs_rule_compile_ctx_t *ctx) +{ + ecs_var_id_t var = ref->var; + + if (rule->vars[var].kind == EcsVarEntity && + !flecs_rule_is_written(var, ctx->written)) + { + ecs_var_id_t table_var = rule->vars[var].table_id; + if (table_var != EcsVarNone && + flecs_rule_is_written(table_var, ctx->written)) + { + if (flecs_rule_compile_ensure_vars( + rule, op, ref, ref_kind, ctx, false, NULL)) + { + goto error; + } + + } + } + + return 0; +error: + return -1; +} + +static +int flecs_rule_ensure_scope_vars( + ecs_world_t *world, + ecs_rule_t *rule, + ecs_rule_compile_ctx_t *ctx, + ecs_term_t *term) +{ + /* If the scope uses variables as entity that have only been written as + * table, resolve them as entities before entering the scope. */ + ecs_term_t *cur = term; + while(cur->first.id != EcsScopeClose) { + /* Dummy operation to obtain variable information for term */ + ecs_rule_op_t op = {0}; + flecs_rule_compile_term_id(world, rule, &op, &cur->first, + &op.first, EcsRuleFirst, EcsVarEntity, ctx, false); + flecs_rule_compile_term_id(world, rule, &op, &cur->second, + &op.second, EcsRuleSecond, EcsVarEntity, ctx, false); + + if (op.flags & (EcsRuleIsVar << EcsRuleFirst)) { + if (flecs_rule_ensure_scope_var( + rule, &op, &op.first, EcsRuleFirst, ctx)) + { + goto error; + } + } + if (op.flags & (EcsRuleIsVar << EcsRuleSecond)) { + if (flecs_rule_ensure_scope_var( + rule, &op, &op.second, EcsRuleSecond, ctx)) + { + goto error; + } + } + + cur ++; + } + + return 0; +error: + return -1; +} + static void flecs_rule_compile_push( ecs_rule_compile_ctx_t *ctx) { ctx->cur = &ctx->ctrlflow[++ ctx->scope]; - ctx->cur->lbl_union = -1; - ctx->cur->lbl_prev = -1; - ctx->cur->lbl_not = -1; - ctx->cur->lbl_none = -1; + ctx->cur->lbl_begin = -1; + ctx->cur->lbl_begin = -1; } static @@ -61449,6 +62195,15 @@ void flecs_rule_compile_pop( ctx->cur = &ctx->ctrlflow[-- ctx->scope]; } +static +bool flecs_rule_term_is_or( + const ecs_filter_t *filter, + const ecs_term_t *term) +{ + bool first_term = term == filter->terms; + return (term->oper == EcsOr) || (!first_term && term[-1].oper == EcsOr); +} + static int flecs_rule_compile_term( ecs_world_t *world, @@ -61456,16 +62211,30 @@ int flecs_rule_compile_term( ecs_term_t *term, ecs_rule_compile_ctx_t *ctx) { - bool first_term = term == rule->filter.terms; + ecs_filter_t *filter = &rule->filter; + bool first_term = term == filter->terms; bool first_is_var = term->first.flags & EcsIsVariable; bool second_is_var = term->second.flags & EcsIsVariable; bool src_is_var = term->src.flags & EcsIsVariable; bool builtin_pred = flecs_rule_is_builtin_pred(term); bool is_not = (term->oper == EcsNot) && !builtin_pred; - bool is_or = (term->oper == EcsOr) || (!first_term && term[-1].oper == EcsOr); + bool is_or = flecs_rule_term_is_or(filter, term); + bool first_or = false, last_or = false; bool cond_write = term->oper == EcsOptional || is_or; ecs_rule_op_t op = {0}; + if (is_or) { + first_or = first_term || (term[-1].oper != EcsOr); + last_or = term->oper != EcsOr; + } + + /* !_ (don't match anything) terms always return nothing. */ + if (is_not && term->id == EcsAny) { + op.kind = EcsRuleNothing; + flecs_rule_op_insert(&op, ctx); + return 0; + } + if (is_or && (first_term || term[-1].oper != EcsOr)) { ctx->ctrlflow->cond_written_or = ctx->cond_written; ctx->ctrlflow->in_or = true; @@ -61474,45 +62243,67 @@ int flecs_rule_compile_term( if (!term->src.id && term->src.flags & EcsIsEntity) { /* If the term has a 0 source, check if it's a scope open/close */ if (term->first.id == EcsScopeOpen) { + if (flecs_rule_ensure_scope_vars(world, rule, ctx, term)) { + goto error; + } if (term->oper == EcsNot) { ctx->scope_is_not |= (ecs_flags32_t)(1ull << ctx->scope); + flecs_rule_begin_block(EcsRuleNot, ctx); } else { ctx->scope_is_not &= (ecs_flags32_t)~(1ull << ctx->scope); } + flecs_rule_compile_push(ctx); } else if (term->first.id == EcsScopeClose) { flecs_rule_compile_pop(ctx); - if (ctx->scope_is_not & (ecs_flags32_t)(1ull << ctx->scope)) { - op.field_index = -1; - flecs_rule_end_not(&op, ctx, false); + if (ctx->scope_is_not & (ecs_flags32_t)(1ull << (ctx->scope))) { + ctx->cur->lbl_query = -1; + flecs_rule_end_block(ctx); } } else { - /* Nothing to be done */ + /* Noop */ } return 0; } + if (builtin_pred) { + if (term->second.id == EcsWildcard || term->second.id == EcsAny) { + /* Noop */ + return 0; + } + } + /* Default instruction for And operators. If the source is fixed (like for * singletons or terms with an entity source), use With, which like And but * just matches against a source (vs. finding a source). */ op.kind = src_is_var ? EcsRuleAnd : EcsRuleWith; op.field_index = flecs_ito(int8_t, term->field_index); - op.term_index = flecs_ito(int8_t, term - rule->filter.terms); + op.term_index = flecs_ito(int8_t, term - filter->terms); /* If rule is transitive, use Trav(ersal) instruction */ if (term->flags & EcsTermTransitive) { ecs_assert(ecs_term_id_is_set(&term->second), ECS_INTERNAL_ERROR, NULL); op.kind = EcsRuleTrav; } else { + /* Ignore cascade & parent flags */ + ecs_flags32_t trav_flags = EcsTraverseFlags & ~(EcsCascade|EcsParent); if (term->flags & (EcsTermMatchAny|EcsTermMatchAnySrc)) { op.kind = EcsRuleAndAny; + } else if ((term->src.flags & trav_flags) == EcsUp) { + op.kind = EcsRuleUp; + } else if ((term->src.flags & trav_flags) == (EcsSelf|EcsUp)) { + op.kind = EcsRuleSelfUp; } } /* If term has fixed id, insert simpler instruction that skips dealing with * wildcard terms and variables */ - if (flecs_rule_term_fixed_id(&rule->filter, term)) { + if (flecs_rule_term_fixed_id(filter, term)) { if (op.kind == EcsRuleAnd) { op.kind = EcsRuleAndId; + } else if (op.kind == EcsRuleSelfUp) { + op.kind = EcsRuleSelfUpId; + } else if (op.kind == EcsRuleUp) { + op.kind = EcsRuleUpId; } } @@ -61526,30 +62317,30 @@ int flecs_rule_compile_term( /* Resolve variables and entities for operation arguments */ flecs_rule_compile_term_id(world, rule, &op, &term->first, - &op.first, EcsRuleFirst, EcsVarEntity, ctx); + &op.first, EcsRuleFirst, EcsVarEntity, ctx, true); flecs_rule_compile_term_id(world, rule, &op, &term->second, - &op.second, EcsRuleSecond, EcsVarEntity, ctx); + &op.second, EcsRuleSecond, EcsVarEntity, ctx, true); if (first_is_var) first_var = op.first.var; if (second_is_var) second_var = op.second.var; - /* Insert each instructions for table -> entity variable if needed */ - bool first_written = flecs_rule_compile_ensure_vars( - rule, &op, &op.first, EcsRuleFirst, ctx, cond_write); - bool second_written = flecs_rule_compile_ensure_vars( - rule, &op, &op.second, EcsRuleSecond, ctx, cond_write); - - /* Do src last, in case it uses the same variable as first/second */ flecs_rule_compile_term_id(world, rule, &op, &term->src, - &op.src, EcsRuleSrc, EcsVarAny, ctx); + &op.src, EcsRuleSrc, EcsVarAny, ctx, true); if (src_is_var) src_var = op.src.var; bool src_written = flecs_rule_is_written(src_var, ctx->written); - /* Cache the current value of op.first. This value may get overwritten with - * a variable when term has component inheritance, but Not operations may - * need the original value to initialize the result id with. */ - ecs_rule_ref_t prev_first = op.first; - ecs_flags8_t prev_op_flags = op.flags; + /* Insert each instructions for table -> entity variable if needed */ + bool first_written, second_written; + if (flecs_rule_compile_ensure_vars( + rule, &op, &op.first, EcsRuleFirst, ctx, cond_write, &first_written)) + { + goto error; + } + if (flecs_rule_compile_ensure_vars( + rule, &op, &op.second, EcsRuleSecond, ctx, cond_write, &second_written)) + { + goto error; + } /* If the query starts with a Not or Optional term, insert an operation that * matches all entities. */ @@ -61591,12 +62382,16 @@ int flecs_rule_compile_term( * worry about the last term in the chain. */ if (rule->vars[src_var].kind == EcsVarTable) { flecs_rule_compile_term_id(world, rule, &op, &term->src, - &op.src, EcsRuleSrc, EcsVarEntity, ctx); + &op.src, EcsRuleSrc, EcsVarEntity, ctx, true); src_var = op.src.var; } } - flecs_rule_compile_ensure_vars(rule, &op, &op.src, EcsRuleSrc, ctx, cond_write); + if (flecs_rule_compile_ensure_vars( + rule, &op, &op.src, EcsRuleSrc, ctx, cond_write, NULL)) + { + goto error; + } /* If source is Any (_) and first and/or second are unconstrained, insert an * ids instruction instead of an And */ @@ -61614,6 +62409,16 @@ int flecs_rule_compile_term( op.flags &= (ecs_flags8_t)~(EcsRuleIsVar << EcsRuleSrc); /* ids has no src */ op.flags &= (ecs_flags8_t)~(EcsRuleIsEntity << EcsRuleSrc); } + + /* If source variable is not written and we're querying just for Any, insert + * a dedicated instruction that uses the Any record in the id index. Any + * queries that are evaluated against written sources can use Wildcard + * records, which is what the AndAny instruction does. */ + } else if (!src_written && term->id == EcsAny && op.kind == EcsRuleAndAny) { + /* Lookup variables ($var.child_name) are always written */ + if (!rule->vars[src_var].lookup) { + op.kind = EcsRuleSelectAny; /* Uses Any (_) id record */ + } } /* If this is a transitive term and both the target and source are unknown, @@ -61627,47 +62432,27 @@ int flecs_rule_compile_term( } } - /* If term has component inheritance enabled, insert instruction to walk - * down the relationship tree of the id. */ - if (term->flags & EcsTermIdInherited) { - if (is_not) { - /* Ensure that term only matches if none of the inherited ids match - * with the source. */ - flecs_rule_begin_none(ctx); - } - flecs_rule_insert_inheritance(rule, term, &op, ctx, cond_write); - } - - /* If previous term was ScopeOpen with a Not operator, insert operation to - * ensure that none of the results inside the scope should match. */ - if (!first_term && term[-1].first.id == EcsScopeOpen) { - if (term[-1].oper == EcsNot) { - flecs_rule_begin_none(ctx); - flecs_rule_begin_not(ctx); + /* Check if this term has variables that have been conditionally written, + * like variables written by an optional term. */ + if (ctx->cond_written) { + if (!is_or || first_or) { + flecs_rule_begin_block_cond_eval(&op, ctx, cond_write_state); } - flecs_rule_compile_push(ctx); } /* Handle Not, Optional, Or operators */ if (is_not) { - flecs_rule_begin_not(ctx); + flecs_rule_begin_block(EcsRuleNot, ctx); } else if (term->oper == EcsOptional) { - flecs_rule_begin_option(ctx); - } else if (term->oper == EcsOr) { - if (first_term || term[-1].oper != EcsOr) { - if (!src_written) { - flecs_rule_begin_union(ctx); - } - } + flecs_rule_begin_block(EcsRuleOptional, ctx); + } else if (first_or) { + flecs_rule_begin_block_or(&op, ctx); } - /* Track label before inserting operations for current */ - ecs_rule_lbl_t lbl_start = flecs_itolbl(ecs_vec_count(ctx->ops)); - - /* Check if this term has variables that have been conditionally written, - * like variables written by an optional term. */ - if (ctx->cond_written) { - flecs_rule_begin_cond_eval(&op, ctx, cond_write_state); + /* If term has component inheritance enabled, insert instruction to walk + * down the relationship tree of the id. */ + if (term->flags & EcsTermIdInherited) { + flecs_rule_insert_inheritance(rule, term, &op, ctx, cond_write); } op.match_flags = term->flags; @@ -61682,13 +62467,58 @@ int flecs_rule_compile_term( op.flags |= EcsRuleIsSelf; } + /* Insert instructions for lookup variables */ + if (first_is_var) { + if (flecs_rule_compile_lookup(rule, first_var, ctx, cond_write)) { + write_state |= (1ull << first_var); // lookups are resolved inline + } + } + if (src_is_var) { + if (flecs_rule_compile_lookup(rule, src_var, ctx, cond_write)) { + write_state |= (1ull << src_var); // lookups are resolved inline + } + } + if (second_is_var) { + if (flecs_rule_compile_lookup(rule, second_var, ctx, cond_write)) { + write_state |= (1ull << second_var); // lookups are resolved inline + } + } + if (builtin_pred) { if (flecs_rule_compile_builtin_pred(term, &op, write_state)) { - return -1; + goto error; + } + } + + /* If we're writing the $this variable, filter out disabled/prefab entities + * unless the filter explicitly matches them. + * This could've been done with regular With instructions, but since + * filtering out disabled/prefab entities is the default and this check is + * cheap to perform on table flags, it's worth special casing. */ + if (!src_written && src_var == 0) { + ecs_flags32_t filter_flags = filter->flags; + if (!(filter_flags & EcsFilterMatchDisabled) || + !(filter_flags & EcsFilterMatchPrefab)) + { + ecs_flags32_t table_flags = 0; + if (!(filter_flags & EcsFilterMatchDisabled)) { + table_flags |= EcsTableIsDisabled; + } + if (!(filter_flags & EcsFilterMatchPrefab)) { + table_flags |= EcsTableIsPrefab; + } + + op.other = flecs_itolbl(table_flags); } } flecs_rule_op_insert(&op, ctx); + ctx->cur->lbl_query = flecs_itolbl(ecs_vec_count(ctx->ops) - 1); + if (is_or) { + ecs_rule_op_t *op_ptr = ecs_vec_get_t(ctx->ops, ecs_rule_op_t, + ctx->cur->lbl_query); + op_ptr->next = FlecsRuleOrMarker; + } /* Handle self-references between src and first/second variables */ if (src_is_var) { @@ -61705,42 +62535,271 @@ int flecs_rule_compile_term( flecs_rule_insert_pair_eq(term->field_index, ctx); } + /* Handle closing of Not, Optional and Or operators */ + if (is_not) { + flecs_rule_end_block(ctx); + } else if (term->oper == EcsOptional) { + flecs_rule_end_block(ctx); + } else if (last_or) { + flecs_rule_end_block_or(ctx); + } + /* Handle closing of conditional evaluation */ if (ctx->cond_written && (first_is_var || second_is_var || src_is_var)) { - flecs_rule_end_cond_eval(&op, ctx); + if (!is_or || last_or) { + flecs_rule_end_block_cond_eval(ctx); + } } - /* Handle closing of Not, Optional and Or operators */ - if (is_not) { - /* Restore original first id in case it got replaced with a variable */ - op.first = prev_first; - op.flags = prev_op_flags; - flecs_rule_end_not(&op, ctx, true); - } else if (term->oper == EcsOptional) { - flecs_rule_end_option(&op, ctx); - } else if (term->oper == EcsOr) { - if (ctx->cur->lbl_union != -1) { - flecs_rule_next_or(ctx); + /* Ensure that term id is set after evaluating Not */ + if (term->flags & EcsTermIdInherited) { + if (is_not) { + ecs_rule_op_t set_id = {0}; + set_id.kind = EcsRuleSetId; + set_id.first.entity = term->id; + set_id.flags = (EcsRuleIsEntity << EcsRuleFirst); + set_id.field_index = flecs_ito(int8_t, term->field_index); + flecs_rule_op_insert(&set_id, ctx); + } + } + + return 0; +error: + return -1; +} + +static +bool flecs_rule_var_is_unknown( + ecs_rule_t *rule, + ecs_var_id_t var_id, + ecs_rule_compile_ctx_t *ctx) +{ + ecs_rule_var_t *vars = rule->vars; + if (ctx->written & (1ull << var_id)) { + return false; + } else { + ecs_var_id_t table_var = vars[var_id].table_id; + if (table_var != EcsVarNone) { + return flecs_rule_var_is_unknown(rule, table_var, ctx); + } + } + return true; +} + +/* Returns whether term is unkown. A term is unknown when it has variable + * elements (first, second, src) that are all unknown. */ +static +bool flecs_rule_term_is_unknown( + ecs_rule_t *rule, + ecs_term_t *term, + ecs_rule_compile_ctx_t *ctx) +{ + ecs_rule_op_t dummy = {0}; + flecs_rule_compile_term_id(NULL, rule, &dummy, &term->first, + &dummy.first, EcsRuleFirst, EcsVarEntity, ctx, false); + flecs_rule_compile_term_id(NULL, rule, &dummy, &term->second, + &dummy.second, EcsRuleSecond, EcsVarEntity, ctx, false); + flecs_rule_compile_term_id(NULL, rule, &dummy, &term->src, + &dummy.src, EcsRuleSrc, EcsVarAny, ctx, false); + + bool has_vars = dummy.flags & + ((EcsRuleIsVar << EcsRuleFirst) | + (EcsRuleIsVar << EcsRuleSecond) | + (EcsRuleIsVar << EcsRuleSrc)); + if (!has_vars) { + /* If term has no variables (typically terms with a static src) there + * can't be anything that's unknown. */ + return false; + } + + if (dummy.flags & (EcsRuleIsVar << EcsRuleFirst)) { + if (!flecs_rule_var_is_unknown(rule, dummy.first.var, ctx)) { + return false; + } + } + if (dummy.flags & (EcsRuleIsVar << EcsRuleSecond)) { + if (!flecs_rule_var_is_unknown(rule, dummy.second.var, ctx)) { + return false; + } + } + if (dummy.flags & (EcsRuleIsVar << EcsRuleSrc)) { + if (!flecs_rule_var_is_unknown(rule, dummy.src.var, ctx)) { + return false; + } + } + + return true; +} + +/* Find the next known term from specified offset. This function is used to find + * a term that can be evaluated before a term that is unknown. Evaluating known + * before unknown terms can significantly decrease the search space. */ +static +int32_t flecs_rule_term_next_known( + ecs_rule_t *rule, + ecs_rule_compile_ctx_t *ctx, + int32_t offset, + ecs_flags64_t compiled) +{ + ecs_filter_t *filter = &rule->filter; + ecs_term_t *terms = filter->terms; + int32_t i, count = filter->term_count; + + for (i = offset; i < count; i ++) { + ecs_term_t *term = &terms[i]; + if (compiled & (1ull << i)) { + continue; + } + + /* Only evaluate And terms */ + if (term->oper != EcsAnd || flecs_rule_term_is_or(&rule->filter, term)){ + continue; + } + + /* Don't reorder terms before/after scopes */ + if (term->first.id == EcsScopeOpen || term->first.id == EcsScopeClose) { + return -1; + } + + if (flecs_rule_term_is_unknown(rule, term, ctx)) { + continue; + } + + return i; + } + + return -1; +} + +/* If the first part of a query contains more than one trivial term, insert a + * special instruction which batch-evaluates multiple terms. */ +static +int32_t flecs_rule_insert_trivial_search( + ecs_rule_t *rule, + ecs_rule_compile_ctx_t *ctx) +{ + ecs_filter_t *filter = &rule->filter; + ecs_term_t *terms = filter->terms; + int32_t i, term_count = filter->term_count; + + /* Find trivial terms, which can be handled in single instruction */ + int32_t trivial_wildcard_terms = 0; + int32_t trivial_data_terms = 0; + for (i = 0; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + if (!(term->flags & EcsTermIsTrivial)) { + break; + } + + /* We can only add trivial terms to plan if they no up traversal */ + if ((term->src.flags & EcsTraverseFlags) != EcsSelf) { + break; + } + + if (ecs_id_is_wildcard(term->id)) { + trivial_wildcard_terms ++; + } + + if (!(term->flags & EcsTermNoData)) { + trivial_data_terms ++; + } + } + + int32_t trivial_terms = i; + if (trivial_terms >= 2) { + /* If there's more than 1 trivial term, batch them in trivial search */ + ecs_rule_op_t trivial = {0}; + if (trivial_wildcard_terms) { + trivial.kind = EcsRuleTrivWildcard; } else { - if (first_term || term[-1].oper != EcsOr) { - if (ctx->cur->lbl_union == -1) { - flecs_rule_begin_or(ctx, lbl_start); + if (trivial_data_terms) { + /* Check to see if there are remaining data terms. If there are, + * we'll have to insert an instruction later that populates all + * fields, so don't do double work here. */ + for (i = trivial_terms; i < term_count; i ++) { + ecs_term_t *term = &terms[i]; + if (!(term->flags & EcsTermIsTrivial)) { + break; + } + } + if (trivial_terms == term_count || i != term_count) { + /* Nobody else is going to set the data fields, so we should + * do it here. */ + trivial.kind = EcsRuleTrivData; } - } else if (term->oper == EcsOr) { - flecs_rule_next_or(ctx); } + if (!trivial.kind) { + trivial.kind = EcsRuleTriv; + } + } + + /* Store on the operation how many trivial terms should be evaluated */ + trivial.other = (ecs_rule_lbl_t)trivial_terms; + flecs_rule_op_insert(&trivial, ctx); + } else { + /* If fewer than 1 trivial term, there's no point in batching them */ + trivial_terms = 0; + } + + return trivial_terms; +} + +/* Insert instruction to populate data fields. */ +static +void flecs_rule_insert_populate( + ecs_rule_t *rule, + ecs_rule_compile_ctx_t *ctx, + int32_t trivial_terms) +{ + ecs_filter_t *filter = &rule->filter; + int32_t i, term_count = filter->term_count; + + /* Insert instruction that populates data. This instruction does not + * have to be inserted if the filter provides no data, or if all terms + * of the filter are trivial, in which case the trivial search operation + * also sets the data. */ + if (!(filter->flags & EcsFilterNoData) && (trivial_terms != term_count)) { + int32_t data_fields = 0; + bool only_self = true; + + /* There are two instructions for setting data fields, a fast one + * that only supports owned fields, and one that supports any kind + * of field. Loop through (remaining) terms to check which one we + * need to use. */ + for (i = trivial_terms; i < term_count; i ++) { + ecs_term_t *term = &filter->terms[i]; + if (term->flags & EcsTermNoData) { + /* Don't care about terms that have no data */ + continue; + } + + data_fields ++; + + if (!ecs_term_match_this(term)) { + break; + } + + if (term->src.flags & EcsUp) { + break; + } + } + + if (i != filter->term_count) { + only_self = false; /* Needs the more complex operation */ } - } else if (term->oper == EcsAnd) { - if (!first_term && term[-1].oper == EcsOr) { - if (ctx->cur->lbl_union != -1) { - flecs_rule_end_union(ctx); + + if (data_fields) { + if (only_self) { + ecs_rule_op_t nothing = {0}; + nothing.kind = EcsRulePopulateSelf; + flecs_rule_op_insert(¬hing, ctx); } else { - flecs_rule_end_or(ctx); + ecs_rule_op_t nothing = {0}; + nothing.kind = EcsRulePopulate; + flecs_rule_op_insert(¬hing, ctx); } } } - - return 0; } int flecs_rule_compile( @@ -61754,20 +62813,18 @@ int flecs_rule_compile( ecs_vec_reset_t(NULL, &stage->operations, ecs_rule_op_t); ctx.ops = &stage->operations; ctx.cur = ctx.ctrlflow; - ctx.cur->lbl_union = -1; - ctx.cur->lbl_prev = -1; - ctx.cur->lbl_not = -1; - ctx.cur->lbl_none = -1; - ctx.cur->lbl_or = -1; - ctx.cur->lbl_union = -1; + ctx.cur->lbl_begin = -1; + ctx.cur->lbl_begin = -1; ecs_vec_clear(ctx.ops); /* Find all variables defined in query */ - flecs_rule_discover_vars(stage, rule); + if (flecs_rule_discover_vars(stage, rule)) { + return -1; + } /* If rule contains fixed source terms, insert operation to set sources */ - int32_t i, count = filter->term_count; - for (i = 0; i < count; i ++) { + int32_t i, term_count = filter->term_count; + for (i = 0; i < term_count; i ++) { ecs_term_t *term = &terms[i]; if (term->src.flags & EcsIsEntity) { ecs_rule_op_t set_fixed = {0}; @@ -61780,9 +62837,11 @@ int flecs_rule_compile( /* If the rule contains terms with fixed ids (no wildcards, variables), * insert instruction that initializes ecs_iter_t::ids. This allows for the * insertion of simpler instructions later on. */ - for (i = 0; i < count; i ++) { + for (i = 0; i < term_count; i ++) { ecs_term_t *term = &terms[i]; - if (flecs_rule_term_fixed_id(filter, term)) { + if (flecs_rule_term_fixed_id(filter, term) || + (term->src.flags & EcsIsEntity && !term->src.id)) + { ecs_rule_op_t set_ids = {0}; set_ids.kind = EcsRuleSetIds; flecs_rule_op_insert(&set_ids, &ctx); @@ -61790,17 +62849,52 @@ int flecs_rule_compile( } } - /* Compile query terms to instructions */ - for (i = 0; i < count; i ++) { + /* Insert trivial term search if query allows for it */ + int32_t trivial_terms = flecs_rule_insert_trivial_search(rule, &ctx); + + /* Compile remaining query terms to instructions */ + ecs_flags64_t compiled = 0; + for (i = trivial_terms; i < term_count; i ++) { ecs_term_t *term = &terms[i]; + int32_t compile = i; + + if (compiled & (1ull << i)) { + continue; /* Already compiled */ + } + + bool can_reorder = true; + if (term->oper != EcsAnd || flecs_rule_term_is_or(&rule->filter, term)){ + can_reorder = false; + } + + /* If variables have been written, but this term has no known variables, + * first try to resolve terms that have known variables. This can + * significantly reduce the search space. + * Only perform this optimization after at least one variable has been + * written to, as all terms are unknown otherwise. */ + if (can_reorder && ctx.written && + flecs_rule_term_is_unknown(rule, term, &ctx)) + { + int32_t term_index = flecs_rule_term_next_known( + rule, &ctx, i + 1, compiled); + if (term_index != -1) { + term = &rule->filter.terms[term_index]; + compile = term_index; + i --; /* Repeat current term */ + } + } + if (flecs_rule_compile_term(world, rule, term, &ctx)) { return -1; } + + compiled |= (1ull << compile); } + ecs_var_id_t this_id = flecs_rule_find_var_id(rule, "This", EcsVarEntity); + /* If This variable has been written as entity, insert an operation to * assign it to it.entities for consistency. */ - ecs_var_id_t this_id = flecs_rule_find_var_id(rule, "This", EcsVarEntity); if (this_id != EcsVarNone && (ctx.written & (1ull << this_id))) { ecs_rule_op_t set_this = {0}; set_this.kind = EcsRuleSetThis; @@ -61882,11 +62976,14 @@ int flecs_rule_compile( } /* If filter is empty, insert Nothing instruction */ - if (!rule->filter.term_count) { + if (!term_count) { ecs_rule_op_t nothing = {0}; nothing.kind = EcsRuleNothing; flecs_rule_op_insert(¬hing, &ctx); } else { + /* Insert instruction to populate data fields */ + flecs_rule_insert_populate(rule, &ctx, trivial_terms); + /* Insert yield. If program reaches this operation, a result was found */ ecs_rule_op_t yield = {0}; yield.kind = EcsRuleYield; @@ -61914,6 +63011,21 @@ int flecs_rule_compile( #ifdef FLECS_RULES +static +bool flecs_rule_dispatch( + const ecs_rule_op_t *op, + bool redo, + ecs_rule_run_ctx_t *ctx); + +static +bool flecs_rule_run_until( + bool redo, + ecs_rule_run_ctx_t *ctx, + const ecs_rule_op_t *ops, + ecs_rule_lbl_t first, + ecs_rule_lbl_t cur, + ecs_rule_op_kind_t until); + ecs_allocator_t* flecs_rule_get_allocator( const ecs_iter_t *it) { @@ -61935,6 +63047,24 @@ ecs_rule_op_ctx_t* flecs_op_ctx_( #define flecs_op_ctx(ctx, op_kind) (&flecs_op_ctx_(ctx)->is.op_kind) +static +void flecs_reset_source_set_flag( + const ecs_rule_run_ctx_t *ctx, + int32_t field_index) +{ + ecs_assert(field_index != -1, ECS_INTERNAL_ERROR, NULL); + (*ctx->source_set) &= ~(1u << field_index); +} + +static +void flecs_set_source_set_flag( + const ecs_rule_run_ctx_t *ctx, + int32_t field_index) +{ + ecs_assert(field_index != -1, ECS_INTERNAL_ERROR, NULL); + (*ctx->source_set) |= (1u << field_index); +} + static ecs_table_range_t flecs_range_from_entity( ecs_entity_t e, @@ -62112,7 +63242,7 @@ void flecs_rule_set_vars( if (op->written & (1ull << var)) { if (ECS_IS_PAIR(id)) { flecs_rule_var_set_entity( - op, var, ecs_pair_first(ctx->world, id), ctx); + op, var, ecs_get_alive(ctx->world, ECS_PAIR_FIRST(id)), ctx); } else { flecs_rule_var_set_entity(op, var, id, ctx); } @@ -62122,7 +63252,7 @@ void flecs_rule_set_vars( ecs_var_id_t var = op->second.var; if (op->written & (1ull << var)) { flecs_rule_var_set_entity( - op, var, ecs_pair_second(ctx->world, id), ctx); + op, var, ecs_get_alive(ctx->world, ECS_PAIR_SECOND(id)), ctx); } } } @@ -62183,7 +63313,7 @@ ecs_id_t flecs_rule_op_get_id_w_written( if (flags_2nd & (EcsRuleIsVar | EcsRuleIsEntity)) { return ecs_pair(first, second); } else { - return ecs_get_alive(ctx->world, first); + return flecs_entities_get_generation(ctx->world, first); } } @@ -62221,9 +63351,6 @@ void flecs_rule_it_set_column( ecs_assert(column >= 0, ECS_INTERNAL_ERROR, NULL); ecs_assert(field_index >= 0, ECS_INTERNAL_ERROR, NULL); it->columns[field_index] = column + 1; - if (it->sources[field_index] != 0) { - it->columns[field_index] *= -1; - } } static @@ -62279,12 +63406,23 @@ void flecs_rule_set_trav_match( flecs_rule_set_vars(op, matched, ctx); } +static +bool flecs_rule_table_filter( + ecs_table_t *table, + ecs_rule_lbl_t other, + ecs_flags32_t filter_mask) +{ + uint32_t filter = flecs_ito(uint32_t, other); + return (table->flags & filter_mask & filter) != 0; +} + static bool flecs_rule_select_w_id( const ecs_rule_op_t *op, bool redo, const ecs_rule_run_ctx_t *ctx, - ecs_id_t id) + ecs_id_t id, + ecs_flags32_t filter_mask) { ecs_rule_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); ecs_id_record_t *idr = op_ctx->idr; @@ -62299,11 +63437,18 @@ bool flecs_rule_select_w_id( } } - if (!flecs_table_cache_iter(&idr->cache, &op_ctx->it)) { - return false; + if (ctx->rule->filter.flags & EcsFilterMatchEmptyTables) { + if (!flecs_table_cache_all_iter(&idr->cache, &op_ctx->it)) { + return false; + } + } else { + if (!flecs_table_cache_iter(&idr->cache, &op_ctx->it)) { + return false; + } } } +repeat: if (!redo || !op_ctx->remaining) { tr = flecs_table_cache_next(&op_ctx->it, ecs_table_record_t); if (!tr) { @@ -62322,6 +63467,10 @@ bool flecs_rule_select_w_id( op_ctx->remaining --; } + if (flecs_rule_table_filter(table, op->other, filter_mask)) { + goto repeat; + } + flecs_rule_set_match(op, table, op_ctx->column, ctx); return true; } @@ -62336,7 +63485,8 @@ bool flecs_rule_select( if (!redo) { id = flecs_rule_op_get_id(op, ctx); } - return flecs_rule_select_w_id(op, redo, ctx, id); + return flecs_rule_select_w_id(op, redo, ctx, id, + (EcsTableIsPrefab|EcsTableIsDisabled)); } static @@ -62401,7 +63551,8 @@ static bool flecs_rule_select_id( const ecs_rule_op_t *op, bool redo, - const ecs_rule_run_ctx_t *ctx) + const ecs_rule_run_ctx_t *ctx, + ecs_flags32_t table_filter) { ecs_rule_and_ctx_t *op_ctx = flecs_op_ctx(ctx, and); ecs_iter_t *it = ctx->it; @@ -62418,11 +63569,18 @@ bool flecs_rule_select_id( } } - if (!flecs_table_cache_iter(&idr->cache, &op_ctx->it)) { - return false; + if (ctx->rule->filter.flags & EcsFilterMatchEmptyTables) { + if (!flecs_table_cache_all_iter(&idr->cache, &op_ctx->it)) { + return false; + } + } else { + if (!flecs_table_cache_iter(&idr->cache, &op_ctx->it)) { + return false; + } } } +repeat: {} const ecs_table_record_t *tr = flecs_table_cache_next( &op_ctx->it, ecs_table_record_t); if (!tr) { @@ -62430,6 +63588,10 @@ bool flecs_rule_select_id( } ecs_table_t *table = tr->hdr.table; + if (flecs_rule_table_filter(table, op->other, table_filter)) { + goto repeat; + } + flecs_rule_var_set_table(op, op->src.var, table, 0, 0, ctx); flecs_rule_it_set_column(it, field, tr->index); return true; @@ -62483,10 +63645,316 @@ bool flecs_rule_and_id( if (written & (1ull << op->src.var)) { return flecs_rule_with_id(op, redo, ctx); } else { - return flecs_rule_select_id(op, redo, ctx); + return flecs_rule_select_id(op, redo, ctx, + (EcsTableIsPrefab|EcsTableIsDisabled)); + } +} + +static +bool flecs_rule_up_select( + const ecs_rule_op_t *op, + bool redo, + const ecs_rule_run_ctx_t *ctx, + bool self, + bool id_only) +{ + ecs_rule_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); + ecs_world_t *world = ctx->world; + ecs_iter_t *it = ctx->it; + bool redo_select = redo; + const ecs_filter_t *filter = &ctx->rule->filter; + + /* Early out if traversal relationship doesn't exist */ + op_ctx->trav = filter->terms[op->term_index].src.trav; + if (!op_ctx->idr_trav) { + op_ctx->idr_trav = flecs_id_record_get(ctx->world, + ecs_pair(op_ctx->trav, EcsWildcard)); + } + if (!op_ctx->idr_trav || !flecs_table_cache_count(&op_ctx->idr_trav->cache)){ + if (!self) { + return false; + } else if (id_only) { + return flecs_rule_select_id(op, redo, ctx, + (EcsTableIsPrefab|EcsTableIsDisabled)); + } else { + return flecs_rule_select(op, redo, ctx); + } + } + + if (!redo) { + op_ctx->with = flecs_rule_op_get_id(op, ctx); + op_ctx->idr_with = flecs_id_record_get(ctx->world, op_ctx->with); + if (!op_ctx->idr_with) { + return false; + } + + op_ctx->down = NULL; + op_ctx->cache_elem = 0; + } + + ecs_trav_down_t *down = op_ctx->down; + + do { + while (!down) { + ecs_table_t *table = op_ctx->table; + if (!table) { + ecs_table_range_t range; + it->sources[op->field_index] = 0; + do { + bool result; + if (id_only) { + result = flecs_rule_select_id(op, redo_select, ctx, 0); + } else { + result = flecs_rule_select_w_id(op, redo_select, ctx, + op_ctx->with, 0); + } + if (!result) { + return false; + } + + redo_select = true; + + range = flecs_rule_get_range( + op, &op->src, EcsRuleSrc, ctx); + ecs_assert(range.table != NULL, ECS_INTERNAL_ERROR, NULL); + } while (!self && range.table->_->traversable_count == 0); + + if (!range.count) { + range.count = ecs_table_count(range.table); + } + + table = op_ctx->table = range.table; + op_ctx->row = range.offset; + op_ctx->end = range.offset + range.count; + op_ctx->matched = it->ids[op->field_index]; + + if (self) { + if (!flecs_rule_table_filter(table, op->other, + (EcsTableIsPrefab|EcsTableIsDisabled))) + { + flecs_reset_source_set_flag(ctx, op->field_index); + op_ctx->row --; + return true; + } + } + + redo_select = true; + } else { + op_ctx->row ++; + } + + if (table->_->traversable_count == 0) { + op_ctx->table = NULL; + continue; + } else { + int32_t row; + ecs_entity_t entity = 0; + ecs_entity_t *entities = flecs_table_entities_array(table); + + for (row = op_ctx->row; row < op_ctx->end; row ++) { + entity = entities[row]; + ecs_record_t *record = flecs_entities_get(world, entity); + if (record->row & EcsEntityIsTraversable) { + it->sources[op->field_index] = entity; + break; + } + } + + if (row == op_ctx->end) { + op_ctx->table = NULL; + continue; + } + + down = op_ctx->down = flecs_rule_get_down_cache(ctx, &op_ctx->cache, + op_ctx->trav, entity, op_ctx->idr_with, self); + op_ctx->cache_elem = -1; + } + } + +next_elem: + if ((++ op_ctx->cache_elem) >= ecs_vec_count(&down->elems)) { + down = NULL; + continue; + } + + ecs_trav_down_elem_t *elem = ecs_vec_get_t( + &down->elems, ecs_trav_down_elem_t, op_ctx->cache_elem); + + flecs_rule_var_set_table(op, op->src.var, elem->table, 0, 0, ctx); + flecs_rule_set_vars(op, op_ctx->matched, ctx); + + if (flecs_rule_table_filter(elem->table, op->other, + (EcsTableIsPrefab|EcsTableIsDisabled))) + { + goto next_elem; + } + + break; + } while (true); + + flecs_set_source_set_flag(ctx, op->field_index); + + return true; +} + +static +bool flecs_rule_up_with( + const ecs_rule_op_t *op, + bool redo, + const ecs_rule_run_ctx_t *ctx) +{ + const ecs_filter_t *filter = &ctx->rule->filter; + ecs_rule_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); + ecs_iter_t *it = ctx->it; + + /* Early out if traversal relationship doesn't exist */ + op_ctx->trav = filter->terms[op->term_index].src.trav; + if (!op_ctx->idr_trav) { + op_ctx->idr_trav = flecs_id_record_get(ctx->world, + ecs_pair(op_ctx->trav, EcsWildcard)); + } + if (!op_ctx->idr_trav || !flecs_table_cache_count(&op_ctx->idr_trav->cache)){ + return false; + } + + if (!redo) { + op_ctx->trav = filter->terms[op->term_index].src.trav; + op_ctx->with = flecs_rule_op_get_id(op, ctx); + op_ctx->idr_with = flecs_id_record_get(ctx->world, op_ctx->with); + + if (!op_ctx->idr_with) { + return false; + } + + ecs_table_range_t range = flecs_rule_get_range( + op, &op->src, EcsRuleSrc, ctx); + if (!range.table) { + return false; + } + + ecs_trav_up_t *up = flecs_rule_get_up_cache(ctx, &op_ctx->cache, + range.table, op_ctx->with, op_ctx->trav, op_ctx->idr_with, + op_ctx->idr_trav); + + if (!up) { + return false; + } + + it->sources[op->field_index] = flecs_entities_get_generation( + ctx->world, up->src); + it->columns[op->field_index] = up->column + 1; + it->ids[op->field_index] = up->id; + flecs_rule_set_vars(op, up->id, ctx); + flecs_set_source_set_flag(ctx, op->field_index); + return true; + } else { + return false; + } +} + +static +bool flecs_rule_self_up_with( + const ecs_rule_op_t *op, + bool redo, + const ecs_rule_run_ctx_t *ctx, + bool id_only) +{ + if (!redo) { + bool result; + if (id_only) { + result = flecs_rule_with_id(op, redo, ctx); + } else { + result = flecs_rule_with(op, redo, ctx); + } + if (result) { + ecs_rule_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); + op_ctx->trav = 0; + if (flecs_rule_ref_flags(op->flags, EcsRuleSrc) & EcsRuleIsVar) { + ecs_iter_t *it = ctx->it; + it->sources[op->field_index] = 0; + } + return true; + } + + flecs_reset_source_set_flag(ctx, op->field_index); + + return flecs_rule_up_with(op, redo, ctx); + } else { + ecs_rule_up_ctx_t *op_ctx = flecs_op_ctx(ctx, up); + if (op_ctx->trav == 0) { + return flecs_rule_with(op, redo, ctx); + } + } + + return false; +} + +static +bool flecs_rule_up( + const ecs_rule_op_t *op, + bool redo, + const ecs_rule_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; + if (flecs_ref_is_written(op, &op->src, EcsRuleSrc, written)) { + return flecs_rule_up_with(op, redo, ctx); + } else { + return flecs_rule_up_select(op, redo, ctx, false, false); } } +static +bool flecs_rule_self_up( + const ecs_rule_op_t *op, + bool redo, + const ecs_rule_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; + if (flecs_ref_is_written(op, &op->src, EcsRuleSrc, written)) { + return flecs_rule_self_up_with(op, redo, ctx, false); + } else { + return flecs_rule_up_select(op, redo, ctx, true, false); + } +} + +static +bool flecs_rule_up_id( + const ecs_rule_op_t *op, + bool redo, + const ecs_rule_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; + if (flecs_ref_is_written(op, &op->src, EcsRuleSrc, written)) { + return flecs_rule_up_with(op, redo, ctx); + } else { + return flecs_rule_up_select(op, redo, ctx, false, true); + } +} + +static +bool flecs_rule_self_up_id( + const ecs_rule_op_t *op, + bool redo, + const ecs_rule_run_ctx_t *ctx) +{ + uint64_t written = ctx->written[ctx->op_index]; + if (flecs_ref_is_written(op, &op->src, EcsRuleSrc, written)) { + return flecs_rule_self_up_with(op, redo, ctx, true); + } else { + return flecs_rule_up_select(op, redo, ctx, true, true); + } +} + +static +bool flecs_rule_select_any( + const ecs_rule_op_t *op, + bool redo, + const ecs_rule_run_ctx_t *ctx) +{ + return flecs_rule_select_w_id(op, redo, ctx, EcsAny, + (EcsTableIsPrefab|EcsTableIsDisabled)); +} + static bool flecs_rule_and_any( const ecs_rule_op_t *op, @@ -62525,6 +63993,57 @@ bool flecs_rule_and_any( return result; } +static +bool flecs_rule_triv( + const ecs_rule_op_t *op, + bool redo, + const ecs_rule_run_ctx_t *ctx) +{ + ecs_rule_trivial_ctx_t *op_ctx = flecs_op_ctx(ctx, trivial); + int32_t until = flecs_ito(int32_t, op->other); + uint64_t written = ctx->written[ctx->op_index]; + ctx->written[ctx->op_index] |= 1ull; + if (written & 1ull) { + return flecs_rule_trivial_test(ctx->rule, ctx, !redo, until); + } else { + return flecs_rule_trivial_search_nodata(ctx->rule, ctx, op_ctx, !redo, until); + } +} + +static +bool flecs_rule_triv_data( + const ecs_rule_op_t *op, + bool redo, + const ecs_rule_run_ctx_t *ctx) +{ + ecs_rule_trivial_ctx_t *op_ctx = flecs_op_ctx(ctx, trivial); + int32_t until = flecs_ito(int32_t, op->other); + uint64_t written = ctx->written[ctx->op_index]; + ctx->written[ctx->op_index] |= 1ull; + if (written & 1ull) { + return flecs_rule_trivial_test(ctx->rule, ctx, !redo, until); + } else { + return flecs_rule_trivial_search(ctx->rule, ctx, op_ctx, !redo, until); + } +} + +static +bool flecs_rule_triv_wildcard( + const ecs_rule_op_t *op, + bool redo, + const ecs_rule_run_ctx_t *ctx) +{ + ecs_rule_trivial_ctx_t *op_ctx = flecs_op_ctx(ctx, trivial); + int32_t until = flecs_ito(int32_t, op->other); + uint64_t written = ctx->written[ctx->op_index]; + ctx->written[ctx->op_index] |= 1ull; + if (written & 1ull) { + return flecs_rule_trivial_test_w_wildcards(ctx->rule, ctx, !redo, until); + } else { + return flecs_rule_trivial_search_w_wildcards(ctx->rule, ctx, op_ctx, !redo, until); + } +} + static bool flecs_rule_trav_fixed_src_reflexive( const ecs_rule_op_t *op, @@ -62534,6 +64053,7 @@ bool flecs_rule_trav_fixed_src_reflexive( ecs_entity_t second) { ecs_table_t *table = range->table; + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); ecs_entity_t *entities = table->data.entities.array; int32_t count = range->count; if (!count) { @@ -62644,7 +64164,7 @@ bool flecs_rule_trav_unknown_src_up_fixed_second( } /* Entity is traversable, which means it could have a subtree */ - flecs_rule_get_down_cache(ctx, &trav_ctx->cache, trav, second); + flecs_rule_get_trav_down_cache(ctx, &trav_ctx->cache, trav, second); trav_ctx->index = 0; if (op->match_flags & EcsTermReflexive) { @@ -62670,7 +64190,9 @@ bool flecs_rule_trav_unknown_src_up_fixed_second( for (; trav_ctx->index < count; trav_ctx->index ++) { ecs_trav_elem_t *el = &elems[trav_ctx->index]; trav_ctx->and.idr = el->idr; /* prevents lookup by select */ - if (flecs_rule_select_w_id(op, redo, ctx, ecs_pair(trav, el->entity))) { + if (flecs_rule_select_w_id(op, redo, ctx, ecs_pair(trav, el->entity), + (EcsTableIsPrefab|EcsTableIsDisabled))) + { return true; } @@ -62735,7 +64257,7 @@ bool flecs_rule_trav_fixed_src_up_unknown_second( ecs_rule_trav_ctx_t *trav_ctx = flecs_op_ctx(ctx, trav); if (!redo) { - flecs_rule_get_up_cache(ctx, &trav_ctx->cache, trav, table); + flecs_rule_get_trav_up_cache(ctx, &trav_ctx->cache, trav, table); trav_ctx->index = 0; if (op->match_flags & EcsTermReflexive) { trav_ctx->yield_reflexive = true; @@ -62778,7 +64300,6 @@ bool flecs_rule_trav( /* This can't happen, src or second should have been resolved */ ecs_abort(ECS_INTERNAL_ERROR, "invalid instruction sequence: unconstrained traversal"); - return false; } else { return flecs_rule_trav_unknown_src_up_fixed_second(op, redo, ctx); } @@ -62962,55 +64483,19 @@ bool flecs_rule_reset( } static -bool flecs_rule_union( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - if (!redo) { - ctx->jump = flecs_itolbl(ctx->op_index + 1); - return true; - } else { - ecs_rule_lbl_t next = flecs_itolbl(ctx->prev_index + 1); - if (next == op->next) { - return false; - } - ctx->jump = next; - return true; - } -} - -static -bool flecs_rule_end( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) -{ - (void)op; - - ecs_rule_ctrlflow_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrlflow); - if (!redo) { - op_ctx->lbl = ctx->prev_index; - return true; - } else { - ctx->jump = op_ctx->lbl; - return true; - } -} - -static -bool flecs_rule_not( - const ecs_rule_op_t *op, - bool redo, - ecs_rule_run_ctx_t *ctx) +void flecs_rule_reset_after_block( + const ecs_rule_op_t *start_op, + ecs_rule_run_ctx_t *ctx, + ecs_rule_ctrl_ctx_t *op_ctx) { - if (redo) { - return false; - } + ecs_rule_lbl_t op_index = start_op->next; + const ecs_rule_op_t *op = &ctx->rit->ops[op_index]; + ctx->written[op_index] = ctx->written[ctx->op_index]; + ctx->op_index = op_index; int32_t field = op->field_index; if (field == -1) { - return true; + goto done; } ecs_iter_t *it = ctx->it; @@ -63020,7 +64505,7 @@ bool flecs_rule_not( /* Ignore variables written by Not operation */ uint64_t *written = ctx->written; - uint64_t written_cur = written[ctx->op_index] = written[op->prev + 1]; + uint64_t written_cur = written[op->prev + 1]; ecs_flags16_t flags_1st = flecs_rule_ref_flags(op->flags, EcsRuleFirst); ecs_flags16_t flags_2nd = flecs_rule_ref_flags(op->flags, EcsRuleSecond); @@ -63047,7 +64532,8 @@ bool flecs_rule_not( it->sources[field] = op->src.entity; } - return true; /* Flip result */ +done: + op_ctx->op_index = op_index; } static @@ -63102,6 +64588,10 @@ bool flecs_rule_pred_eq_w_range( /* left = unknown, right = known. Assign right-hand value to left */ ecs_var_id_t l = first_var; ctx->vars[l].range = r; + if (r.count == 1) { + ctx->vars[l].entity = ecs_vec_get_t(&r.table->data.entities, + ecs_entity_t, r.offset)[0]; + } return true; } else { ecs_table_range_t l = flecs_rule_get_range( @@ -63356,6 +64846,32 @@ bool flecs_rule_pred_neq_name( return flecs_rule_pred_neq_w_range(op, redo, ctx, r); } +static +bool flecs_rule_lookup( + const ecs_rule_op_t *op, + bool redo, + ecs_rule_run_ctx_t *ctx) +{ + if (redo) { + return false; + } + + const ecs_rule_t *rule = ctx->rule; + ecs_entity_t first = flecs_rule_var_get_entity(op->first.var, ctx); + ecs_rule_var_t *var = &rule->vars[op->src.var]; + + ecs_entity_t result = ecs_lookup_path_w_sep(ctx->world, first, var->lookup, + NULL, NULL, false); + if (!result) { + flecs_rule_var_set_entity(op, op->src.var, EcsWildcard, ctx); + return false; + } + + flecs_rule_var_set_entity(op, op->src.var, result, ctx); + + return true; +} + static bool flecs_rule_setvars( const ecs_rule_op_t *op, @@ -63374,18 +64890,18 @@ bool flecs_rule_setvars( } int32_t i; + ecs_flags32_t source_set = *ctx->source_set; for (i = 0; i < filter->field_count; i ++) { ecs_var_id_t var_id = src_vars[i]; if (!var_id) { continue; } - it->sources[i] = flecs_rule_var_get_entity(var_id, ctx); - - int32_t column = it->columns[i]; - if (column > 0) { - it->columns[i] = -column; + if (source_set & (1u << i)) { + continue; } + + it->sources[i] = flecs_rule_var_get_entity(var_id, ctx); } return true; @@ -63469,6 +64985,21 @@ bool flecs_rule_setids( return true; } +static +bool flecs_rule_setid( + const ecs_rule_op_t *op, + bool redo, + ecs_rule_run_ctx_t *ctx) +{ + if (redo) { + return false; + } + + ecs_assert(op->field_index != -1, ECS_INTERNAL_ERROR, NULL); + ctx->it->ids[op->field_index] = op->first.entity; + return true; +} + /* Check if entity is stored in table */ static bool flecs_rule_contain( @@ -63484,6 +65015,7 @@ bool flecs_rule_contain( ecs_var_id_t first_id = op->first.var; ecs_table_t *table = flecs_rule_var_get_table(src_id, ctx); + ecs_entity_t e = flecs_rule_var_get_entity(first_id, ctx); return table == ecs_get_table(ctx->world, e); } @@ -63505,30 +65037,271 @@ bool flecs_rule_pair_eq( } static -bool flecs_rule_jmp_if_not( +bool flecs_rule_select_or( const ecs_rule_op_t *op, bool redo, ecs_rule_run_ctx_t *ctx) { + ecs_iter_t *it = ctx->it; + ecs_rule_iter_t *rit = &it->priv.iter.rule; + ecs_rule_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); + + ecs_rule_lbl_t first = flecs_itolbl(ctx->op_index + 1); if (!redo) { - flecs_op_ctx(ctx, cond)->cond = false; - return true; - } else { - if (!flecs_op_ctx(ctx, cond)->cond) { - ctx->jump = op->other; + op_ctx->op_index = first; + } + + const ecs_rule_op_t *cur = &rit->ops[op_ctx->op_index]; + bool result = false; + + /* Evaluate operations in OR chain one by one. */ + + do { + ctx->op_index = op_ctx->op_index; + ctx->written[op_ctx->op_index] = op->written; + + result = flecs_rule_dispatch(cur, redo, ctx); + if (result) { + ctx->written[op_ctx->op_index] |= cur->written; + + /* If a previous operation in the OR chain returned a result for the + * same matched source, skip it so we don't yield for each matching + * element in the chain. */ + ecs_rule_lbl_t prev_index; + for (prev_index = first; prev_index < op_ctx->op_index; + prev_index ++) + { + const ecs_rule_op_t *prev = &rit->ops[prev_index]; + ctx->op_index = prev_index; + ctx->written[prev_index] = ctx->written[op_ctx->op_index]; + if (flecs_rule_dispatch(prev, false, ctx)) { + break; + } + } + + if (prev_index != op_ctx->op_index) { + /* Duplicate match was found, redo search */ + redo = true; + continue; + } + break; + } + + /* No result was found, go to next operation in chain */ + op_ctx->op_index ++; + cur = &rit->ops[op_ctx->op_index]; + redo = false; + } while (cur->kind != EcsRuleEnd); + + return result; +} + +static +bool flecs_rule_run_block( + bool redo, + ecs_rule_run_ctx_t *ctx, + ecs_rule_ctrl_ctx_t *op_ctx) +{ + ecs_iter_t *it = ctx->it; + ecs_rule_iter_t *rit = &it->priv.iter.rule; + + if (!redo) { + op_ctx->op_index = flecs_itolbl(ctx->op_index + 1); + } else if (ctx->rit->ops[op_ctx->op_index].kind == EcsRuleEnd) { + return false; + } + + ctx->written[ctx->op_index + 1] = ctx->written[ctx->op_index]; + + return flecs_rule_run_until( + redo, ctx, rit->ops, ctx->op_index, op_ctx->op_index, EcsRuleEnd); +} + +static +bool flecs_rule_with_or( + const ecs_rule_op_t *op, + bool redo, + ecs_rule_run_ctx_t *ctx) +{ + ecs_rule_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); + + bool result = flecs_rule_run_block(redo, ctx, op_ctx); + if (result) { + /* If a match was found, no need to keep searching for this source */ + op_ctx->op_index = op->next; + } + + return result; +} + +static +bool flecs_rule_or( + const ecs_rule_op_t *op, + bool redo, + ecs_rule_run_ctx_t *ctx) +{ + if (op->flags & (EcsRuleIsVar << EcsRuleSrc)) { + uint64_t written = ctx->written[ctx->op_index]; + if (!(written & (1ull << op->src.var))) { + return flecs_rule_select_or(op, redo, ctx); } + } + + return flecs_rule_with_or(op, redo, ctx); +} + +static +bool flecs_rule_run_block_w_reset( + const ecs_rule_op_t *op, + bool redo, + ecs_rule_run_ctx_t *ctx) +{ + ecs_rule_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); + bool result = flecs_rule_run_block(redo, ctx, op_ctx); + if (!result) { + flecs_rule_reset_after_block(op, ctx, op_ctx); + } + return result; +} + +static +bool flecs_rule_not( + const ecs_rule_op_t *op, + bool redo, + ecs_rule_run_ctx_t *ctx) +{ + if (redo) { return false; } + + return !flecs_rule_run_block_w_reset(op, redo, ctx); +} + +static +bool flecs_rule_optional( + const ecs_rule_op_t *op, + bool redo, + ecs_rule_run_ctx_t *ctx) +{ + bool result = flecs_rule_run_block_w_reset(op, redo, ctx); + if (!redo) { + return true; /* Return at least once */ + } else { + return result; + } +} + +static +bool flecs_rule_eval_if( + const ecs_rule_op_t *op, + ecs_rule_run_ctx_t *ctx, + const ecs_rule_ref_t *ref, + ecs_flags16_t ref_kind) +{ + if (flecs_rule_ref_flags(op->flags, ref_kind) == EcsRuleIsVar) { + if (ctx->vars[ref->var].entity == EcsWildcard) { + ecs_rule_ctrl_ctx_t *op_ctx = flecs_op_ctx(ctx, ctrl); + flecs_rule_reset_after_block(op, ctx, op_ctx); + return false; + } + } + return true; +} + +static +bool flecs_rule_if( + const ecs_rule_op_t *op, + bool redo, + ecs_rule_run_ctx_t *ctx) +{ + if (!redo) { + if (!flecs_rule_eval_if(op, ctx, &op->src, EcsRuleSrc) || + !flecs_rule_eval_if(op, ctx, &op->first, EcsRuleFirst) || + !flecs_rule_eval_if(op, ctx, &op->second, EcsRuleSecond)) + { + return true; + } + } + + return flecs_rule_run_block_w_reset(op, redo, ctx); +} + +static +bool flecs_rule_end( + const ecs_rule_op_t *op, + bool redo, + ecs_rule_run_ctx_t *ctx) +{ + (void)op; (void)ctx; + return !redo; } static -bool flecs_rule_jmp_set_cond( +bool flecs_rule_populate( const ecs_rule_op_t *op, bool redo, ecs_rule_run_ctx_t *ctx) { + (void)op; if (!redo) { - ctx->op_ctx[op->other].is.cond.cond = true; + ecs_iter_t *it = ctx->it; + if (it->flags & EcsIterNoData) { + return true; + } + + ECS_BIT_CLEAR(it->flags, EcsIterHasShared); + + const ecs_rule_t *rule = ctx->rule; + const ecs_filter_t *filter = &rule->filter; + int32_t i, field_count = filter->field_count; + ecs_flags64_t data_fields = filter->data_fields; + ecs_table_range_t *range = &ctx->vars[0].range; + ecs_table_t *table = range->table; + if (table && !range->count) { + range->count = ecs_table_count(table); + } + + for (i = 0; i < field_count; i ++) { + if (!(data_fields & (1llu << i))) { + continue; + } + + int32_t index = it->columns[i]; + ecs_assert(index >= 0, ECS_INTERNAL_ERROR, NULL); + if (!index) { + continue; + } + + ecs_entity_t src = it->sources[i]; + if (!src) { + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + if (range->count && table->column_map) { + int32_t column = table->column_map[index - 1]; + if (column != -1) { + it->ptrs[i] = ECS_ELEM( + table->data.columns[column].data.array, + it->sizes[i], + range->offset); + continue; + } + } + } else { + ecs_record_t *r = flecs_entities_get(ctx->world, src); + ecs_table_t *src_table = r->table; + if (src_table->column_map) { + int32_t column = src_table->column_map[index - 1]; + if (column != -1) { + it->ptrs[i] = ecs_vec_get( + &src_table->data.columns[column].data, + it->sizes[i], + ECS_RECORD_TO_ROW(r->row)); + ECS_BIT_SET(it->flags, EcsIterHasShared); + continue; + } + } + } + } + return true; } else { return false; @@ -63536,29 +65309,46 @@ bool flecs_rule_jmp_set_cond( } static -bool flecs_rule_jmp_not_set( +bool flecs_rule_populate_self( const ecs_rule_op_t *op, bool redo, ecs_rule_run_ctx_t *ctx) { + (void)op; if (!redo) { - ecs_var_t *vars = ctx->vars; - if (flecs_rule_ref_flags(op->flags, EcsRuleFirst) == EcsRuleIsVar) { - if (vars[op->first.var].entity == EcsWildcard) { - ctx->jump = op->other; - return true; - } + const ecs_rule_t *rule = ctx->rule; + const ecs_filter_t *filter = &rule->filter; + int32_t i, field_count = filter->field_count; + ecs_flags64_t data_fields = filter->data_fields; + ecs_iter_t *it = ctx->it; + + ecs_table_range_t *range = &ctx->vars[0].range; + ecs_table_t *table = range->table; + if (!table->column_map) { + return true; } - if (flecs_rule_ref_flags(op->flags, EcsRuleSecond) == EcsRuleIsVar) { - if (vars[op->second.var].entity == EcsWildcard) { - ctx->jump = op->other; - return true; - } + + if (!ecs_table_count(table)) { + return true; } - if (flecs_rule_ref_flags(op->flags, EcsRuleSrc) == EcsRuleIsVar) { - if (vars[op->src.var].entity == EcsWildcard) { - ctx->jump = op->other; - return true; + + for (i = 0; i < field_count; i ++) { + if (!(data_fields & (1llu << i))) { + continue; + } + + int32_t index = it->columns[i]; + ecs_assert(index >= 0, ECS_INTERNAL_ERROR, NULL); /* Only owned */ + if (!index) { + continue; + } + + int32_t column = table->column_map[index - 1]; + if (column != -1) { + it->ptrs[i] = ECS_ELEM( + table->data.columns[column].data.array, + it->sizes[i], + range->offset); } } @@ -63569,7 +65359,7 @@ bool flecs_rule_jmp_not_set( } static -bool flecs_rule_run( +bool flecs_rule_dispatch( const ecs_rule_op_t *op, bool redo, ecs_rule_run_ctx_t *ctx) @@ -63578,6 +65368,14 @@ bool flecs_rule_run( case EcsRuleAnd: return flecs_rule_and(op, redo, ctx); case EcsRuleAndId: return flecs_rule_and_id(op, redo, ctx); case EcsRuleAndAny: return flecs_rule_and_any(op, redo, ctx); + case EcsRuleTriv: return flecs_rule_triv(op, redo, ctx); + case EcsRuleTrivData: return flecs_rule_triv_data(op, redo, ctx); + case EcsRuleTrivWildcard: return flecs_rule_triv_wildcard(op, redo, ctx); + case EcsRuleSelectAny: return flecs_rule_select_any(op, redo, ctx); + case EcsRuleUp: return flecs_rule_up(op, redo, ctx); + case EcsRuleUpId: return flecs_rule_up_id(op, redo, ctx); + case EcsRuleSelfUp: return flecs_rule_self_up(op, redo, ctx); + case EcsRuleSelfUpId: return flecs_rule_self_up_id(op, redo, ctx); case EcsRuleWith: return flecs_rule_with(op, redo, ctx); case EcsRuleTrav: return flecs_rule_trav(op, redo, ctx); case EcsRuleIdsRight: return flecs_rule_idsright(op, redo, ctx); @@ -63585,7 +65383,9 @@ bool flecs_rule_run( case EcsRuleEach: return flecs_rule_each(op, redo, ctx); case EcsRuleStore: return flecs_rule_store(op, redo, ctx); case EcsRuleReset: return flecs_rule_reset(op, redo, ctx); - case EcsRuleUnion: return flecs_rule_union(op, redo, ctx); + case EcsRuleOr: return flecs_rule_or(op, redo, ctx); + case EcsRuleOptional: return flecs_rule_optional(op, redo, ctx); + case EcsRuleIf: return flecs_rule_if(op, redo, ctx); case EcsRuleEnd: return flecs_rule_end(op, redo, ctx); case EcsRuleNot: return flecs_rule_not(op, redo, ctx); case EcsRulePredEq: return flecs_rule_pred_eq(op, redo, ctx); @@ -63594,69 +65394,129 @@ bool flecs_rule_run( case EcsRulePredNeqName: return flecs_rule_pred_neq_name(op, redo, ctx); case EcsRulePredEqMatch: return flecs_rule_pred_eq_match(op, redo, ctx); case EcsRulePredNeqMatch: return flecs_rule_pred_neq_match(op, redo, ctx); + case EcsRuleLookup: return flecs_rule_lookup(op, redo, ctx); case EcsRuleSetVars: return flecs_rule_setvars(op, redo, ctx); case EcsRuleSetThis: return flecs_rule_setthis(op, redo, ctx); case EcsRuleSetFixed: return flecs_rule_setfixed(op, redo, ctx); case EcsRuleSetIds: return flecs_rule_setids(op, redo, ctx); + case EcsRuleSetId: return flecs_rule_setid(op, redo, ctx); case EcsRuleContain: return flecs_rule_contain(op, redo, ctx); case EcsRulePairEq: return flecs_rule_pair_eq(op, redo, ctx); - case EcsRuleJmpCondFalse: return flecs_rule_jmp_if_not(op, redo, ctx); - case EcsRuleSetCond: return flecs_rule_jmp_set_cond(op, redo, ctx); - case EcsRuleJmpNotSet: return flecs_rule_jmp_not_set(op, redo, ctx); + case EcsRulePopulate: return flecs_rule_populate(op, redo, ctx); + case EcsRulePopulateSelf: return flecs_rule_populate_self(op, redo, ctx); case EcsRuleYield: return false; case EcsRuleNothing: return false; } return false; } +static +bool flecs_rule_run_until( + bool redo, + ecs_rule_run_ctx_t *ctx, + const ecs_rule_op_t *ops, + ecs_rule_lbl_t first, + ecs_rule_lbl_t cur, + ecs_rule_op_kind_t until) +{ + ecs_assert(first >= -1, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cur >= 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(cur > first, ECS_INTERNAL_ERROR, NULL); + + ctx->op_index = cur; + const ecs_rule_op_t *op = &ops[ctx->op_index]; + ecs_assert(op->kind != until, ECS_INTERNAL_ERROR, NULL); + + do { + #ifdef FLECS_DEBUG + ctx->rit->profile[ctx->op_index].count[redo] ++; + #endif + + bool result = flecs_rule_dispatch(op, redo, ctx); + cur = (&op->prev)[result]; + redo = cur < ctx->op_index; + + if (!redo) { + ctx->written[cur] |= ctx->written[ctx->op_index] | op->written; + } + + ctx->op_index = cur; + op = &ops[ctx->op_index]; + + if (cur <= first) { + return false; + } + if (op->kind == until) { + return true; + } + } while (true); + + return false; +} + static void flecs_rule_iter_init( ecs_rule_run_ctx_t *ctx) { + ecs_assert(ctx->written != NULL, ECS_INTERNAL_ERROR, NULL); ecs_iter_t *it = ctx->it; - if (ctx->written) { - const ecs_rule_t *rule = ctx->rule; - ecs_flags64_t it_written = it->constrained_vars; - ctx->written[0] = it_written; - if (it_written && ctx->rule->src_vars) { - /* If variables were constrained, check if there are any table - * variables that have a constrained entity variable. */ - ecs_var_t *vars = ctx->vars; - int32_t i, count = rule->filter.field_count; - for (i = 0; i < count; i ++) { - ecs_var_id_t var_id = rule->src_vars[i]; - ecs_rule_var_t *var = &rule->vars[var_id]; - if (!(it_written & (1ull << var_id)) || - (var->kind == EcsVarTable) || (var->table_id == EcsVarNone)) - { - continue; - } + const ecs_rule_t *rule = ctx->rule; + ecs_flags64_t it_written = it->constrained_vars; + ctx->written[0] = it_written; + if (it_written && ctx->rule->src_vars) { + /* If variables were constrained, check if there are any table + * variables that have a constrained entity variable. */ + ecs_var_t *vars = ctx->vars; + int32_t i, count = rule->filter.field_count; + for (i = 0; i < count; i ++) { + ecs_var_id_t var_id = rule->src_vars[i]; + ecs_rule_var_t *var = &rule->vars[var_id]; - /* Initialize table variable with constrained entity variable */ - ecs_var_t *tvar = &vars[var->table_id]; - tvar->range = flecs_range_from_entity(vars[var_id].entity, ctx); - ctx->written[0] |= (1ull << var->table_id); /* Mark as written */ + if (!(it_written & (1ull << var_id)) || + (var->kind == EcsVarTable) || (var->table_id == EcsVarNone)) + { + continue; } + + /* Initialize table variable with constrained entity variable */ + ecs_var_t *tvar = &vars[var->table_id]; + tvar->range = flecs_range_from_entity(vars[var_id].entity, ctx); + ctx->written[0] |= (1ull << var->table_id); /* Mark as written */ } } - flecs_iter_validate(it); -} - -bool ecs_rule_next( - ecs_iter_t *it) -{ - ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_check(it->next == ecs_rule_next, ECS_INVALID_PARAMETER, NULL); + ecs_flags32_t flags = rule->filter.flags; + if (flags & EcsFilterIsTrivial) { + if ((flags & EcsFilterMatchOnlySelf) || + !flecs_table_cache_count(&ctx->world->idr_isa_wildcard->cache)) + { + if (it_written) { + it->offset = ctx->vars[0].range.offset; + it->count = ctx->vars[0].range.count; + if (!it->count) { + ecs_assert(!it->offset, ECS_INVALID_PARAMETER, NULL); + it->count = ecs_table_count(ctx->vars[0].range.table); + } - if (flecs_iter_next_row(it)) { - return true; + it->flags |= EcsIterTrivialTest; + flecs_rule_setids(&rule->ops[0], false, ctx); + } else { + if (flags & EcsFilterHasWildcards) { + it->flags |= EcsIterTrivialSearchWildcard; + flecs_rule_setids(&rule->ops[0], false, ctx); + } else if (flags & EcsFilterNoData) { + it->flags |= EcsIterTrivialSearchNoData; + flecs_rule_setids(&rule->ops[0], false, ctx); + } else { + it->flags |= EcsIterTrivialSearch; + flecs_rule_setids(&rule->ops[0], false, ctx); + } + } + } } - return flecs_iter_next_instanced(it, ecs_rule_next_instanced(it)); -error: - return false; + flecs_iter_validate(it); } bool ecs_rule_next_instanced( @@ -63666,9 +65526,6 @@ bool ecs_rule_next_instanced( ecs_assert(it->next == ecs_rule_next, ECS_INVALID_PARAMETER, NULL); ecs_rule_iter_t *rit = &it->priv.iter.rule; - bool redo = it->flags & EcsIterIsValid; - ecs_rule_lbl_t next; - ecs_rule_run_ctx_t ctx; ctx.world = it->real_world; ctx.rule = rit->rule; @@ -63676,59 +65533,104 @@ bool ecs_rule_next_instanced( ctx.vars = rit->vars; ctx.rule_vars = rit->rule_vars; ctx.written = rit->written; - ctx.prev_index = -1; - ctx.jump = -1; ctx.op_ctx = rit->op_ctx; + ctx.source_set = &rit->source_set; + ctx.rit = rit; const ecs_rule_op_t *ops = rit->ops; + bool redo = true; if (!(it->flags & EcsIterIsValid)) { - if (!ctx.rule) { - goto done; - } + ecs_assert(ctx.rule != NULL, ECS_INVALID_PARAMETER, NULL); flecs_rule_iter_init(&ctx); + redo = false; + } else { + it->frame_offset += it->count; } - do { - ctx.op_index = rit->op; - const ecs_rule_op_t *op = &ops[ctx.op_index]; - -#ifdef FLECS_DEBUG - rit->profile[ctx.op_index].count[redo] ++; -#endif - - bool result = flecs_rule_run(op, redo, &ctx); - ctx.prev_index = ctx.op_index; - - next = (&op->prev)[result]; - if (ctx.jump != -1) { - next = ctx.jump; - ctx.jump = -1; + /* Specialized iterator modes for trivial queries */ + if (it->flags & EcsIterTrivialSearch) { + ecs_rule_trivial_ctx_t *op_ctx = &ctx.op_ctx[0].is.trivial; + int32_t fields = ctx.rule->filter.term_count; + if (!flecs_rule_trivial_search(ctx.rule, &ctx, op_ctx, !redo, fields)) { + goto done; } - - if ((next > ctx.op_index)) { - ctx.written[next] |= ctx.written[ctx.op_index] | op->written; + it->table = ctx.vars[0].range.table; + it->count = ecs_table_count(it->table); + it->entities = flecs_table_entities_array(it->table); + return true; + } else if (it->flags & EcsIterTrivialSearchNoData) { + ecs_rule_trivial_ctx_t *op_ctx = &ctx.op_ctx[0].is.trivial; + int32_t fields = ctx.rule->filter.term_count; + if (!flecs_rule_trivial_search_nodata(ctx.rule, &ctx, op_ctx, !redo, fields)) { + goto done; } + it->table = ctx.vars[0].range.table; + it->count = ecs_table_count(it->table); + it->entities = flecs_table_entities_array(it->table); + return true; + } else if (it->flags & EcsIterTrivialTest) { + int32_t fields = ctx.rule->filter.term_count; + if (!flecs_rule_trivial_test(ctx.rule, &ctx, !redo, fields)) { + goto done; + } + return true; + } else if (it->flags & EcsIterTrivialSearchWildcard) { + ecs_rule_trivial_ctx_t *op_ctx = &ctx.op_ctx[0].is.trivial; + int32_t fields = ctx.rule->filter.term_count; + if (!flecs_rule_trivial_search_w_wildcards(ctx.rule, &ctx, op_ctx, !redo, fields)) { + goto done; + } + it->table = ctx.vars[0].range.table; + it->count = ecs_table_count(it->table); + it->entities = flecs_table_entities_array(it->table); + return true; + } - redo = next < ctx.prev_index; - rit->op = next; - - if (op->kind == EcsRuleYield) { - ecs_table_range_t *range = &rit->vars[0].range; - ecs_table_t *table = range->table; - if (table && !range->count) { - range->count = ecs_table_count(table); + /* Default iterator mode */ + if (flecs_rule_run_until(redo, &ctx, ops, -1, rit->op, EcsRuleYield)) { + ecs_assert(ops[ctx.op_index].kind == EcsRuleYield, + ECS_INTERNAL_ERROR, NULL); + ecs_table_range_t *range = &ctx.vars[0].range; + ecs_table_t *table = range->table; + int32_t count = range->count; + if (table) { + if (!count) { + count = ecs_table_count(table); } - flecs_iter_populate_data(ctx.world, it, range->table, range->offset, - range->count, it->ptrs); - return true; + it->table = table; + it->offset = range->offset; + it->count = count; + it->entities = ECS_ELEM_T( + table->data.entities.array, ecs_entity_t, it->offset); + } else if (count == 1) { + it->count = 1; + it->entities = &ctx.vars[0].entity; } - } while (next >= 0); + + rit->op = flecs_itolbl(ctx.op_index - 1); + return true; + } done: ecs_iter_fini(it); return false; } +bool ecs_rule_next( + ecs_iter_t *it) +{ + ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(it->next == ecs_rule_next, ECS_INVALID_PARAMETER, NULL); + + if (flecs_iter_next_row(it)) { + return true; + } + + return flecs_iter_next_instanced(it, ecs_rule_next_instanced(it)); +error: + return false; +} + static void flecs_rule_iter_fini_ctx( ecs_iter_t *it, @@ -63746,6 +65648,18 @@ void flecs_rule_iter_fini_ctx( case EcsRuleTrav: flecs_rule_trav_cache_fini(a, &ctx[i].is.trav.cache); break; + case EcsRuleUp: + case EcsRuleSelfUp: + case EcsRuleUpId: + case EcsRuleSelfUpId: { + ecs_trav_up_cache_t *cache = &ctx[i].is.up.cache; + if (cache->dir == EcsDown) { + flecs_rule_down_cache_fini(a, cache); + } else { + flecs_rule_up_cache_fini(cache); + } + break; + } default: break; } @@ -63811,6 +65725,7 @@ ecs_iter_t ecs_rule_iter( rit->rule = rule; rit->rule_vars = rule->vars; rit->ops = rule->ops; + rit->source_set = 0; if (var_count) { rit->vars = flecs_iter_calloc_n(&it, ecs_var_t, var_count); } @@ -63823,7 +65738,7 @@ ecs_iter_t ecs_rule_iter( rit->profile = flecs_iter_calloc_n(&it, ecs_rule_op_profile_t, op_count); #endif - for (i = 0; i < var_count; i ++) { + for (i = 1; i < var_count; i ++) { rit->vars[i].entity = EcsWildcard; } @@ -63875,10 +65790,9 @@ void flecs_rule_build_down_cache( } int32_t i, count = ecs_table_count(table); - ecs_record_t **records = table->data.records.array; ecs_entity_t *entities = table->data.entities.array; for (i = 0; i < count; i ++) { - ecs_record_t *r = records[i]; + ecs_record_t *r = flecs_entities_get(world, entities[i]); if (r->row & EcsEntityIsTraversable) { flecs_rule_build_down_cache( world, a, ctx, cache, trav, entities[i]); @@ -63935,7 +65849,7 @@ void flecs_rule_trav_cache_fini( ecs_vec_fini_t(a, &cache->entities, ecs_trav_elem_t); } -void flecs_rule_get_down_cache( +void flecs_rule_get_trav_down_cache( const ecs_rule_run_ctx_t *ctx, ecs_trav_cache_t *cache, ecs_entity_t trav, @@ -63951,7 +65865,7 @@ void flecs_rule_get_down_cache( } } -void flecs_rule_get_up_cache( +void flecs_rule_get_trav_up_cache( const ecs_rule_run_ctx_t *ctx, ecs_trav_cache_t *cache, ecs_entity_t trav, @@ -63989,6 +65903,723 @@ void flecs_rule_get_up_cache( #endif + +#ifdef FLECS_RULES + +static +void flecs_trav_entity_down_isa( + ecs_world_t *world, + ecs_allocator_t *a, + ecs_trav_up_cache_t *cache, + ecs_trav_down_t *dst, + ecs_entity_t trav, + ecs_entity_t entity, + ecs_id_record_t *idr_with, + bool self); + +static +ecs_trav_down_t* flecs_trav_entity_down( + ecs_world_t *world, + ecs_allocator_t *a, + ecs_trav_up_cache_t *cache, + ecs_trav_down_t *dst, + ecs_entity_t trav, + ecs_entity_t entity, + ecs_id_record_t *idr_trav, + ecs_id_record_t *idr_with, + bool self); + +static +ecs_trav_down_t* flecs_trav_down_ensure( + const ecs_rule_run_ctx_t *ctx, + ecs_trav_up_cache_t *cache, + ecs_entity_t entity) +{ + ecs_trav_down_t **trav = ecs_map_ensure_ref( + &cache->src, ecs_trav_down_t, entity); + if (!trav[0]) { + trav[0] = flecs_iter_calloc_t(ctx->it, ecs_trav_down_t); + ecs_vec_init_t(NULL, &trav[0]->elems, ecs_trav_down_elem_t, 0); + } + + return trav[0]; +} + +static +ecs_trav_down_t* flecs_trav_table_down( + ecs_world_t *world, + ecs_allocator_t *a, + ecs_trav_up_cache_t *cache, + ecs_trav_down_t *dst, + ecs_entity_t trav, + const ecs_table_t *table, + ecs_id_record_t *idr_with, + bool self) +{ + ecs_assert(table->id != 0, ECS_INTERNAL_ERROR, NULL); + + if (!table->_->traversable_count) { + return dst; + } + + ecs_assert(idr_with != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_entity_t *entities = ecs_vec_first(&table->data.entities); + int32_t i, count = ecs_table_count(table); + for (i = 0; i < count; i ++) { + ecs_entity_t entity = entities[i]; + ecs_record_t *record = flecs_entities_get(world, entity); + if (!record) { + continue; + } + + uint32_t flags = ECS_RECORD_TO_ROW_FLAGS(record->row); + if (flags & EcsEntityIsTraversable) { + ecs_id_record_t *idr_trav = flecs_id_record_get(world, + ecs_pair(trav, entity)); + if (!idr_trav) { + continue; + } + + flecs_trav_entity_down(world, a, cache, dst, + trav, entity, idr_trav, idr_with, self); + } + } + + return dst; +} + +static +void flecs_trav_entity_down_isa( + ecs_world_t *world, + ecs_allocator_t *a, + ecs_trav_up_cache_t *cache, + ecs_trav_down_t *dst, + ecs_entity_t trav, + ecs_entity_t entity, + ecs_id_record_t *idr_with, + bool self) +{ + if (trav == EcsIsA || !world->idr_isa_wildcard) { + return; + } + + ecs_id_record_t *idr_isa = flecs_id_record_get( + world, ecs_pair(EcsIsA, entity)); + if (!idr_isa) { + return; + } + + ecs_table_cache_iter_t it; + if (flecs_table_cache_all_iter(&idr_isa->cache, &it)) { + ecs_table_record_t *tr; + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + ecs_table_t *table = tr->hdr.table; + if (!table->_->traversable_count) { + continue; + } + + ecs_entity_t *entities = ecs_vec_first(&table->data.entities); + int32_t i, count = ecs_table_count(table); + for (i = 0; i < count; i ++) { + ecs_entity_t e = entities[i]; + ecs_record_t *record = flecs_entities_get(world, e); + if (!record) { + continue; + } + + uint32_t flags = ECS_RECORD_TO_ROW_FLAGS(record->row); + if (flags & EcsEntityIsTraversable) { + ecs_id_record_t *idr_trav = flecs_id_record_get(world, + ecs_pair(trav, e)); + flecs_trav_entity_down(world, a, cache, dst, trav, e, + idr_trav, idr_with, self); + } + } + } + } +} + +static +ecs_trav_down_t* flecs_trav_entity_down( + ecs_world_t *world, + ecs_allocator_t *a, + ecs_trav_up_cache_t *cache, + ecs_trav_down_t *dst, + ecs_entity_t trav, + ecs_entity_t entity, + ecs_id_record_t *idr_trav, + ecs_id_record_t *idr_with, + bool self) +{ + ecs_assert(dst != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(idr_with != NULL, ECS_INTERNAL_ERROR, NULL); + + flecs_trav_entity_down_isa( + world, a, cache, dst, trav, entity, idr_with, self); + + int32_t first = ecs_vec_count(&dst->elems); + + ecs_table_cache_iter_t it; + if (flecs_table_cache_iter(&idr_trav->cache, &it)) { + ecs_table_record_t *tr; + while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { + ecs_assert(tr->count == 1, ECS_INTERNAL_ERROR, NULL); + ecs_table_t *table = tr->hdr.table; + bool leaf = false; + + if (flecs_id_record_get_table(idr_with, table) != NULL) { + if (self) { + continue; + } + leaf = true; + } + + /* If record is not the first instance of (trav, *), don't add it + * to the cache. */ + int32_t index = tr->index; + if (index) { + ecs_id_t id = table->type.array[index - 1]; + if (ECS_IS_PAIR(id) && ECS_PAIR_FIRST(id) == trav) { + int32_t col = ecs_search_relation(world, table, 0, + idr_with->id, trav, EcsUp, NULL, NULL, &tr); + ecs_assert(col >= 0, ECS_INTERNAL_ERROR, NULL); + + if (col != index) { + /* First relationship through which the id is + * reachable is not the current one, so skip. */ + continue; + } + } + } + + ecs_trav_down_elem_t *elem = ecs_vec_append_t( + a, &dst->elems, ecs_trav_down_elem_t); + elem->table = table; + elem->leaf = leaf; + } + } + + /* Breadth first walk */ + int32_t t, last = ecs_vec_count(&dst->elems); + for (t = first; t < last; t ++) { + ecs_trav_down_elem_t *elem = ecs_vec_get_t( + &dst->elems, ecs_trav_down_elem_t, t); + if (!elem->leaf) { + flecs_trav_table_down(world, a, cache, dst, trav, + elem->table, idr_with, self); + } + } + + return dst; +} + +ecs_trav_down_t* flecs_rule_get_down_cache( + const ecs_rule_run_ctx_t *ctx, + ecs_trav_up_cache_t *cache, + ecs_entity_t trav, + ecs_entity_t e, + ecs_id_record_t *idr_with, + bool self) +{ + ecs_world_t *world = ctx->it->real_world; + ecs_assert(!(cache->dir & EcsUp), ECS_INTERNAL_ERROR, NULL); + cache->dir = EcsDown; + + ecs_allocator_t *a = flecs_rule_get_allocator(ctx->it); + ecs_map_init_if(&cache->src, a); + + ecs_trav_down_t *result = flecs_trav_down_ensure(ctx, cache, e); + if (result->ready) { + return result; + } + + ecs_id_record_t *idr_trav = flecs_id_record_get(world, ecs_pair(trav, e)); + if (!idr_trav) { + if (trav != EcsIsA) { + flecs_trav_entity_down_isa( + world, a, cache, result, trav, e, idr_with, self); + } + result->ready = true; + return result; + } + + ecs_vec_init_t(a, &result->elems, ecs_trav_down_elem_t, 0); + flecs_trav_entity_down( + world, a, cache, result, trav, e, idr_trav, idr_with, self); + result->ready = true; + + return result; +} + +void flecs_rule_down_cache_fini( + ecs_allocator_t *a, + ecs_trav_up_cache_t *cache) +{ + ecs_map_iter_t it = ecs_map_iter(&cache->src); + while (ecs_map_next(&it)) { + ecs_trav_down_t *t = ecs_map_ptr(&it); + ecs_vec_fini_t(a, &t->elems, ecs_trav_down_elem_t); + } + ecs_map_fini(&cache->src); +} + +#endif + + +#ifdef FLECS_RULES + +static +ecs_trav_up_t* flecs_trav_up_ensure( + const ecs_rule_run_ctx_t *ctx, + ecs_trav_up_cache_t *cache, + uint64_t table_id) +{ + ecs_trav_up_t **trav = ecs_map_ensure_ref( + &cache->src, ecs_trav_up_t, table_id); + if (!trav[0]) { + trav[0] = flecs_iter_calloc_t(ctx->it, ecs_trav_up_t); + } + + return trav[0]; +} + +static +int32_t flecs_trav_type_search( + ecs_trav_up_t *up, + const ecs_table_t *table, + ecs_id_record_t *idr_with, + ecs_type_t *type) +{ + ecs_table_record_t *tr = ecs_table_cache_get(&idr_with->cache, table); + if (tr) { + up->id = type->array[tr->index]; + return up->column = tr->index; + } + + return -1; +} + +static +int32_t flecs_trav_type_offset_search( + ecs_trav_up_t *up, + int32_t offset, + ecs_id_t with, + ecs_type_t *type) +{ + ecs_assert(offset > 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(with != 0, ECS_INVALID_PARAMETER, NULL); + + while (offset < type->count) { + ecs_id_t type_id = type->array[offset ++]; + if (ecs_id_match(type_id, with)) { + up->id = flecs_to_public_id(type_id); + return up->column = offset - 1; + } + } + + return -1; +} + +static +ecs_trav_up_t* flecs_trav_table_up( + const ecs_rule_run_ctx_t *ctx, + ecs_allocator_t *a, + ecs_trav_up_cache_t *cache, + const ecs_world_t *world, + ecs_entity_t src, + ecs_id_t with, + ecs_id_t rel, + ecs_id_record_t *idr_with, + ecs_id_record_t *idr_trav) +{ + ecs_trav_up_t *up = flecs_trav_up_ensure(ctx, cache, src); + if (up->ready) { + return up; + } + + ecs_record_t *src_record = flecs_entities_get_any(world, src); + ecs_table_t *table = src_record->table; + if (!table) { + goto not_found; + } + + ecs_type_t type = table->type; + if (flecs_trav_type_search(up, table, idr_with, &type) >= 0) { + up->src = src; + goto found; + } + + ecs_flags32_t flags = table->flags; + if ((flags & EcsTableHasPairs) && rel) { + bool is_a = idr_trav == world->idr_isa_wildcard; + if (is_a) { + if (!(flags & EcsTableHasIsA)) { + goto not_found; + } + + if (!flecs_type_can_inherit_id(world, table, idr_with, with)) { + goto not_found; + } + } + + ecs_trav_up_t up_pair = {0}; + int32_t r_column = flecs_trav_type_search( + &up_pair, table, idr_trav, &type); + + while (r_column != -1) { + ecs_entity_t tgt = ECS_PAIR_SECOND(up_pair.id); + ecs_assert(tgt != 0, ECS_INTERNAL_ERROR, NULL); + + ecs_trav_up_t *up_parent = flecs_trav_table_up(ctx, a, cache, + world, tgt, with, rel, idr_with, idr_trav); + if (up_parent->column != -1) { + up->src = up_parent->src; + up->column = up_parent->column; + up->id = up_parent->id; + goto found; + } + + r_column = flecs_trav_type_offset_search( + &up_pair, r_column + 1, rel, &type); + } + + if (!is_a) { + idr_trav = world->idr_isa_wildcard; + r_column = flecs_trav_type_search( + &up_pair, table, idr_trav, &type); + + while (r_column != -1) { + ecs_entity_t tgt = ECS_PAIR_SECOND(up_pair.id); + ecs_assert(tgt != 0, ECS_INTERNAL_ERROR, NULL); + + ecs_trav_up_t *up_parent = flecs_trav_table_up(ctx, a, cache, + world, tgt, with, rel, idr_with, idr_trav); + if (up_parent->column != -1) { + up->src = up_parent->src; + up->column = up_parent->column; + up->id = up_parent->id; + goto found; + } + + r_column = flecs_trav_type_offset_search( + &up_pair, r_column + 1, rel, &type); + } + } + } + +not_found: + up->column = -1; +found: + up->ready = true; + return up; +} + +ecs_trav_up_t* flecs_rule_get_up_cache( + const ecs_rule_run_ctx_t *ctx, + ecs_trav_up_cache_t *cache, + ecs_table_t *table, + ecs_id_t with, + ecs_entity_t trav, + ecs_id_record_t *idr_with, + ecs_id_record_t *idr_trav) +{ + if (cache->with && cache->with != with) { + flecs_rule_up_cache_fini(cache); + } + + ecs_world_t *world = ctx->it->real_world; + ecs_allocator_t *a = flecs_rule_get_allocator(ctx->it); + ecs_map_init_if(&cache->src, a); + + ecs_assert(!(cache->dir & EcsDown), ECS_INTERNAL_ERROR, NULL); + cache->dir = EcsUp; + cache->with = with; + + ecs_assert(idr_with != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(idr_trav != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_table_record_t *tr = ecs_table_cache_get(&idr_trav->cache, table); + if (!tr) { + return NULL; /* Table doesn't have the relationship */ + } + + int32_t i = tr->index, end = i + tr->count; + for (; i < end; i ++) { + ecs_id_t id = table->type.array[i]; + ecs_entity_t tgt = ECS_PAIR_SECOND(id); + ecs_trav_up_t *result = flecs_trav_table_up(ctx, a, cache, world, tgt, + with, ecs_pair(trav, EcsWildcard), idr_with, idr_trav); + ecs_assert(result != NULL, ECS_INTERNAL_ERROR, NULL); + if (result->src != 0) { + return result; + } + } + + return NULL; +} + +void flecs_rule_up_cache_fini( + ecs_trav_up_cache_t *cache) +{ + ecs_map_fini(&cache->src); +} + +#endif + +/** + * @file addons/rules/engine.c + * @brief Iterator for trivial queries. + */ + + +#ifdef FLECS_RULES + +static +bool flecs_rule_trivial_init( + ecs_world_t *world, + const ecs_filter_t *filter) +{ + int32_t t, count = filter->term_count; + ecs_term_t *terms = filter->terms; + + for (t = 0; t < count; t ++) { + ecs_term_t *term = &terms[t]; + if (!term->idr) { + term->idr = flecs_id_record_get(world, term->id); + if (!term->idr) { + /* Id doesn't exist, so query can't match */ + return false; + } + } + } + + return true; +} + +bool flecs_rule_trivial_test( + const ecs_rule_t *rule, + const ecs_rule_run_ctx_t *ctx, + bool first, + int32_t term_count) +{ + if (first) { + const ecs_filter_t *filter = &rule->filter; + int32_t t; + ecs_term_t *terms = filter->terms; + ecs_iter_t *it = ctx->it; + + if (!flecs_rule_trivial_init(ctx->world, filter)) { + return false; + } + + ecs_table_t *table = ctx->vars[0].range.table; + ecs_assert(table != NULL, ECS_INVALID_OPERATION, NULL); + + for (t = 0; t < term_count; t ++) { + ecs_term_t *term = &terms[t]; + const ecs_table_record_t *tr = flecs_id_record_get_table( + term->idr, table); + if (!tr) { + return false; + } + + it->columns[t] = tr->index + 1; + if (it->count && tr->column != -1) { + it->ptrs[t] = ecs_vec_get( + &table->data.columns[tr->column].data, + it->sizes[t], + it->offset); + } + } + + it->table = table; + it->entities = &flecs_table_entities_array(table)[it->offset]; + return true; + } else { + return false; + } +} + +static +bool flecs_rule_trivial_search_init( + const ecs_rule_run_ctx_t *ctx, + ecs_rule_trivial_ctx_t *op_ctx, + const ecs_filter_t *filter, + bool first) +{ + if (first) { + ecs_term_t *terms = filter->terms; + if (!flecs_rule_trivial_init(ctx->world, filter)) { + return false; + } + + if (filter->flags & EcsFilterMatchEmptyTables) { + if (!flecs_table_cache_all_iter(&terms[0].idr->cache, &op_ctx->it)){ + return false; + } + } else { + if (!flecs_table_cache_iter(&terms[0].idr->cache, &op_ctx->it)) { + return false; + } + } + } + + return true; +} + +bool flecs_rule_trivial_search( + const ecs_rule_t *rule, + const ecs_rule_run_ctx_t *ctx, + ecs_rule_trivial_ctx_t *op_ctx, + bool first, + int32_t term_count) +{ + const ecs_filter_t *filter = &rule->filter; + int32_t t; + ecs_term_t *terms = filter->terms; + ecs_iter_t *it = ctx->it; + + if (!flecs_rule_trivial_search_init(ctx, op_ctx, filter, first)) { + return false; + } + + do { + const ecs_table_record_t *tr = flecs_table_cache_next( + &op_ctx->it, ecs_table_record_t); + if (!tr) { + return false; + } + + ecs_table_t *table = tr->hdr.table; + if (table->flags & (EcsTableIsPrefab|EcsTableIsDisabled)) { + continue; + } + + for (t = 1; t < term_count; t ++) { + ecs_term_t *term = &terms[t]; + const ecs_table_record_t *tr_with = flecs_id_record_get_table( + term->idr, table); + if (!tr_with) { + break; + } + + it->columns[t] = tr_with->index + 1; + if (tr_with->column != -1) { + it->ptrs[t] = ecs_vec_first( + &table->data.columns[tr_with->column].data); + } + } + + if (t == term_count) { + ctx->vars[0].range.table = table; + ctx->vars[0].range.count = 0; + ctx->vars[0].range.offset = 0; + it->columns[0] = tr->index + 1; + if (tr->column != -1) { + it->ptrs[0] = ecs_vec_first( + &table->data.columns[tr->column].data); + } + break; + } + } while (true); + + return true; +} + +bool flecs_rule_trivial_search_w_wildcards( + const ecs_rule_t *rule, + const ecs_rule_run_ctx_t *ctx, + ecs_rule_trivial_ctx_t *op_ctx, + bool first, + int32_t term_count) +{ + bool result = flecs_rule_trivial_search( + rule, ctx, op_ctx, first, term_count); + if (result) { + ecs_iter_t *it = ctx->it; + ecs_table_t *table = ctx->vars[0].range.table; + int32_t t; + for (t = 0; t < term_count; t ++) { + it->ids[t] = table->type.array[it->columns[t] - 1]; + } + } + + return result; +} + +bool flecs_rule_trivial_test_w_wildcards( + const ecs_rule_t *rule, + const ecs_rule_run_ctx_t *ctx, + bool first, + int32_t term_count) +{ + bool result = flecs_rule_trivial_test( + rule, ctx, first, term_count); + if (result) { + ecs_iter_t *it = ctx->it; + ecs_table_t *table = ctx->vars[0].range.table; + int32_t t; + for (t = 0; t < term_count; t ++) { + it->ids[t] = table->type.array[it->columns[t] - 1]; + } + } + + return result; +} + +bool flecs_rule_trivial_search_nodata( + const ecs_rule_t *rule, + const ecs_rule_run_ctx_t *ctx, + ecs_rule_trivial_ctx_t *op_ctx, + bool first, + int32_t term_count) +{ + const ecs_filter_t *filter = &rule->filter; + int32_t t; + ecs_term_t *terms = filter->terms; + ecs_iter_t *it = ctx->it; + + if (!flecs_rule_trivial_search_init(ctx, op_ctx, filter, first)) { + return false; + } + + do { + const ecs_table_record_t *tr = flecs_table_cache_next( + &op_ctx->it, ecs_table_record_t); + if (!tr) { + return false; + } + + ecs_table_t *table = tr->hdr.table; + if (table->flags & (EcsTableIsPrefab|EcsTableIsDisabled)) { + continue; + } + + for (t = 1; t < term_count; t ++) { + ecs_term_t *term = &terms[t]; + const ecs_table_record_t *tr_with = flecs_id_record_get_table( + term->idr, table); + if (!tr_with) { + break; + } + + it->columns[t] = tr_with->index + 1; + } + + if (t == term_count) { + ctx->vars[0].range.table = table; + ctx->vars[0].range.count = 0; + ctx->vars[0].range.offset = 0; + it->columns[0] = tr->index + 1; + break; + } + } while (true); + + return true; +} + +#endif + /** * @file addons/system/system.c * @brief System addon. @@ -64310,7 +66941,8 @@ ecs_entity_t ecs_system_init( ecs_defer_end(world); } else { - ecs_system_t *system = ecs_poly(poly->poly, ecs_system_t); + ecs_poly_assert(poly->poly, ecs_system_t); + ecs_system_t *system = (ecs_system_t*)poly->poly; if (desc->run) { system->run = desc->run; diff --git a/flecs-sys/flecs.h b/flecs-sys/flecs.h index 8aebdfb..4274b3e 100644 --- a/flecs-sys/flecs.h +++ b/flecs-sys/flecs.h @@ -86,8 +86,10 @@ * running tests or when debugging issues. This will severely slow down code. */ #ifdef FLECS_SANITIZE +#ifndef FLECS_DEBUG #define FLECS_DEBUG /* If sanitized mode is enabled, so is debug mode */ #endif +#endif /* Tip: if you see weird behavior that you think might be a bug, make sure to * test with the FLECS_DEBUG or FLECS_SANITIZE flags enabled. There's a good @@ -373,6 +375,10 @@ extern "C" { #define EcsIterMatchVar (1u << 8u) #define EcsIterHasCondSet (1u << 10u) /* Does iterator have conditionally set fields */ #define EcsIterProfile (1u << 11u) /* Profile iterator performance */ +#define EcsIterTrivialSearch (1u << 12u) /* Trivial iterator mode */ +#define EcsIterTrivialSearchNoData (1u << 13u) /* Trivial iterator w/no data */ +#define EcsIterTrivialTest (1u << 14u) /* Trivial test mode (constrained $this) */ +#define EcsIterTrivialSearchWildcard (1u << 15u) /* Trivial search with wildcard ids */ //////////////////////////////////////////////////////////////////////////////// //// Event flags (used by ecs_event_decs_t::flags) @@ -398,6 +404,11 @@ extern "C" { #define EcsFilterUnresolvedByName (1u << 11u) /* Use by-name matching for unresolved entity identifiers */ #define EcsFilterHasPred (1u << 12u) /* Filter has equality predicates */ #define EcsFilterHasScopes (1u << 13u) /* Filter has query scopes */ +#define EcsFilterIsTrivial (1u << 14u) /* Trivial filter */ +#define EcsFilterMatchOnlySelf (1u << 15u) /* Filter has no up traversal */ +#define EcsFilterHasWildcards (1u << 16u) /* Filter has no up traversal */ +#define EcsFilterOwnsStorage (1u << 17u) /* Is ecs_filter_t object owned by filter */ +#define EcsFilterOwnsTermsStorage (1u << 18u) /* Is terms array owned by filter */ //////////////////////////////////////////////////////////////////////////////// //// Table flags (used by ecs_table_t::flags) @@ -567,6 +578,9 @@ extern "C" { #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" /* This warning gets thrown when trying to cast pointer returned from dlproc */ #pragma clang diagnostic ignored "-Wcast-function-type-strict" +/* This warning can get thrown for expressions that evaluate to constants + * in debug/release mode. */ +#pragma clang diagnostic ignored "-Wconstant-logical-operand" #elif defined(ECS_TARGET_GNU) #ifndef __cplusplus #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" @@ -574,6 +588,13 @@ extern "C" { #endif #pragma GCC diagnostic ignored "-Wformat-nonliteral" #pragma GCC diagnostic ignored "-Wunused-macros" +/* This warning gets thrown *sometimes* when not all members for a struct are + * provided in an initializer. Flecs heavily relies on descriptor structs that + * only require partly initialization, so this warning isn't useful. + * It doesn't introduce any safety issues (fields are guaranteed to be 0 + * initialized), and later versions of gcc (>=11) seem to no longer throw this + * warning. */ +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif /* Standard library dependencies */ @@ -916,6 +937,7 @@ typedef struct ecs_allocator_t ecs_allocator_t; #ifndef FLECS_VEC_H #define FLECS_VEC_H + #ifdef __cplusplus extern "C" { #endif @@ -1684,7 +1706,12 @@ void* flecs_dup( extern "C" { #endif +#ifdef __cplusplus +/* Fixes missing field initializer warning on g++ */ +#define ECS_STRBUF_INIT (ecs_strbuf_t){} +#else #define ECS_STRBUF_INIT (ecs_strbuf_t){0} +#endif #define ECS_STRBUF_ELEMENT_SIZE (511) #define ECS_STRBUF_MAX_LIST_DEPTH (32) @@ -2153,7 +2180,7 @@ typedef struct ecs_os_api_t { ecs_os_api_thread_new_t task_new_; ecs_os_api_thread_join_t task_join_; - /* Atomic incremenet / decrement */ + /* Atomic increment / decrement */ ecs_os_api_ainc_t ainc_; ecs_os_api_ainc_t adec_; ecs_os_api_lainc_t lainc_; @@ -2290,6 +2317,10 @@ void ecs_os_set_api_defaults(void); #define ecs_os_memcpy_n(ptr1, ptr2, T, count) ecs_os_memcpy(ptr1, ptr2, ECS_SIZEOF(T) * count) #define ecs_os_memcmp_t(ptr1, ptr2, T) ecs_os_memcmp(ptr1, ptr2, ECS_SIZEOF(T)) +#define ecs_os_memmove_t(ptr1, ptr2, T) ecs_os_memmove(ptr1, ptr2, ECS_SIZEOF(T)) +#define ecs_os_memmove_n(ptr1, ptr2, T, count) ecs_os_memmove(ptr1, ptr2, ECS_SIZEOF(T) * count) +#define ecs_os_memmove_t(ptr1, ptr2, T) ecs_os_memmove(ptr1, ptr2, ECS_SIZEOF(T)) + #define ecs_os_strcmp(str1, str2) strcmp(str1, str2) #define ecs_os_memset_t(ptr, value, T) ecs_os_memset(ptr, value, ECS_SIZEOF(T)) #define ecs_os_memset_n(ptr, value, T, count) ecs_os_memset(ptr, value, ECS_SIZEOF(T) * count) @@ -2895,12 +2926,13 @@ typedef enum ecs_oper_kind_t { #define EcsDown (1u << 3) /**< Match by traversing downwards (derived, cannot be set) */ #define EcsTraverseAll (1u << 4) /**< Match all entities encountered through traversal */ #define EcsCascade (1u << 5) /**< Sort results breadth first */ -#define EcsParent (1u << 6) /**< Short for up(ChildOf) */ -#define EcsIsVariable (1u << 7) /**< Term id is a variable */ -#define EcsIsEntity (1u << 8) /**< Term id is an entity */ -#define EcsIsName (1u << 9) /**< Term id is a name (don't attempt to lookup as entity) */ -#define EcsFilter (1u << 10) /**< Prevent observer from triggering on term */ -#define EcsTraverseFlags (EcsUp|EcsDown|EcsTraverseAll|EcsSelf|EcsCascade|EcsParent) +#define EcsDesc (1u << 6) /**< Iterate groups in descending order */ +#define EcsParent (1u << 7) /**< Short for up(ChildOf) */ +#define EcsIsVariable (1u << 8) /**< Term id is a variable */ +#define EcsIsEntity (1u << 9) /**< Term id is an entity */ +#define EcsIsName (1u << 10) /**< Term id is a name (don't attempt to lookup as entity) */ +#define EcsFilter (1u << 11) /**< Prevent observer from triggering on term */ +#define EcsTraverseFlags (EcsUp|EcsDown|EcsTraverseAll|EcsSelf|EcsCascade|EcsDesc|EcsParent) /* Term flags discovered & set during filter creation. Mostly used internally to * store information relevant to queries. */ @@ -2911,6 +2943,8 @@ typedef enum ecs_oper_kind_t { #define EcsTermTransitive (1u << 4) #define EcsTermReflexive (1u << 5) #define EcsTermIdInherited (1u << 6) +#define EcsTermIsTrivial (1u << 7) +#define EcsTermNoData (1u << 8) /* Term flags used for term iteration */ #define EcsTermMatchDisabled (1u << 7) @@ -2969,15 +3003,12 @@ FLECS_API extern ecs_filter_t ECS_FILTER_INIT; struct ecs_filter_t { ecs_header_t hdr; - ecs_term_t *terms; /**< Array containing terms for filter */ - int32_t term_count; /**< Number of elements in terms array */ - int32_t field_count; /**< Number of fields in iterator for filter */ - - bool owned; /**< Is filter object owned by filter */ - bool terms_owned; /**< Is terms array owned by filter */ - - ecs_flags32_t flags; /**< Filter flags */ + int8_t term_count; /**< Number of elements in terms array */ + int8_t field_count; /**< Number of fields in iterator for filter */ + ecs_flags32_t flags; /**< Filter flags */ + ecs_flags64_t data_fields; /**< Bitset with fields that have data */ + ecs_term_t *terms; /**< Array containing terms for filter */ char *variable_names[1]; /**< Placeholder variable names array */ int32_t *sizes; /**< Field size (same for each result) */ @@ -3281,12 +3312,12 @@ typedef struct ecs_rule_iter_t { const struct ecs_rule_op_t *ops; struct ecs_rule_op_ctx_t *op_ctx; /* Operation-specific state */ uint64_t *written; + ecs_flags32_t source_set; #ifdef FLECS_DEBUG ecs_rule_op_profile_t *profile; #endif - bool redo; int16_t op; int16_t sp; } ecs_rule_iter_t; @@ -3900,10 +3931,10 @@ typedef struct ecs_observer_desc_t { * \ingroup observers */ typedef struct ecs_event_desc_t { - /** The event id. Only triggers for the specified event will be notified */ + /** The event id. Only observers for the specified event will be notified */ ecs_entity_t event; - /** Component ids. Only triggers with a matching component id will be + /** Component ids. Only observers with a matching component id will be * notified. Observers are guaranteed to get notified once, even if they * match more than one id. */ const ecs_type_t *ids; @@ -3926,8 +3957,16 @@ typedef struct ecs_event_desc_t { /** Single-entity alternative to setting table / offset / count */ ecs_entity_t entity; - /** Optional context. Assigned to iter param member */ - const void *param; + /** Optional context. + * The type of the param must be the event, where the event is a component. + * When an event is enqueued, the value of param is coped to a temporary + * storage of the event type. */ + void *param; + + /* Same as param, but with the guarantee that the value won't be modified. + * When an event with a const parameter is enqueued, the value of the param + * is copied to a temporary storage of the event type. */ + const void *const_param; /** Observable (usually the world) */ ecs_poly_t *observable; @@ -3979,18 +4018,12 @@ typedef struct ecs_world_info_t { int64_t systems_ran_frame; /**< Total number of systems ran in last frame */ int64_t observers_ran_frame; /**< Total number of times observer was invoked */ - int32_t id_count; /**< Number of ids in the world (excluding wildcards) */ int32_t tag_id_count; /**< Number of tag (no data) ids in the world */ int32_t component_id_count; /**< Number of component (data) ids in the world */ int32_t pair_id_count; /**< Number of pair ids in the world */ - int32_t wildcard_id_count; /**< Number of wildcard ids */ int32_t table_count; /**< Number of tables */ - int32_t tag_table_count; /**< Number of tag-only tables */ - int32_t trivial_table_count; /**< Number of tables with trivial components (no lifecycle callbacks) */ int32_t empty_table_count; /**< Number of tables without entities */ - int32_t table_record_count; /**< Total number of table records (entries in table caches) */ - int32_t table_storage_count; /**< Total number of table storages */ /* -- Command counts -- */ struct { @@ -5113,7 +5146,7 @@ ecs_entity_t ecs_new_low_id( /** Create new entity with (component) id. * This operation creates a new entity with an optional (component) id. When 0 - * is passed to the id paramter, no component is added to the new entity. + * is passed to the id parameter, no component is added to the new entity. * * @param world The world. * @param id The component id to initialize the new entity with. @@ -5209,6 +5242,10 @@ const ecs_entity_t* ecs_bulk_new_w_id( * This operation clones the components of one entity into another entity. If * no destination entity is provided, a new entity will be created. Component * values are not copied unless copy_value is true. + * + * If the source entity has a name, it will not be copied to the destination + * entity. This is to prevent having two entities with the same name under the + * same parent, which is not allowed. * * @param world The world. * @param dst The entity to copy the components to. @@ -7410,6 +7447,11 @@ void ecs_emit( ecs_world_t *world, ecs_event_desc_t *desc); +FLECS_API +void ecs_enqueue( + ecs_world_t *world, + ecs_event_desc_t *desc); + /** Create observer. * Observers are like triggers, but can subscribe for multiple terms. An * observer only triggers when the source of the event meets all terms. @@ -9619,8 +9661,7 @@ void ecs_abort_( ...); FLECS_API -bool ecs_assert_( - bool condition, +void ecs_assert_log_( int32_t error_code, const char *condition_str, const char *file, @@ -9814,7 +9855,8 @@ void ecs_parser_errorv_( #define ecs_assert(condition, error_code, ...) #else #define ecs_assert(condition, error_code, ...)\ - if (!ecs_assert_(condition, error_code, #condition, __FILE__, __LINE__, __VA_ARGS__)) {\ + if (!(condition)) {\ + ecs_assert_log_(error_code, #condition, __FILE__, __LINE__, __VA_ARGS__);\ ecs_os_abort();\ }\ assert(condition) /* satisfy compiler/static analyzers */ @@ -9854,7 +9896,8 @@ void ecs_parser_errorv_( #else #ifdef FLECS_SOFT_ASSERT #define ecs_check(condition, error_code, ...)\ - if (!ecs_assert_(condition, error_code, #condition, __FILE__, __LINE__, __VA_ARGS__)) {\ + if (!(condition)) {\ + ecs_assert_log_(error_code, #condition, __FILE__, __LINE__, __VA_ARGS__);\ goto error;\ } #else // FLECS_SOFT_ASSERT @@ -10491,23 +10534,6 @@ typedef struct { void *impl; } EcsRest; -/* Global statistics */ -extern int64_t ecs_rest_request_count; -extern int64_t ecs_rest_entity_count; -extern int64_t ecs_rest_entity_error_count; -extern int64_t ecs_rest_query_count; -extern int64_t ecs_rest_query_error_count; -extern int64_t ecs_rest_query_name_count; -extern int64_t ecs_rest_query_name_error_count; -extern int64_t ecs_rest_query_name_from_cache_count; -extern int64_t ecs_rest_enable_count; -extern int64_t ecs_rest_enable_error_count; -extern int64_t ecs_rest_delete_count; -extern int64_t ecs_rest_delete_error_count; -extern int64_t ecs_rest_world_stats_count; -extern int64_t ecs_rest_pipeline_stats_count; -extern int64_t ecs_rest_stats_error_count; - /** Create HTTP server for REST API. * This allows for the creation of a REST server that can be managed by the * application without using Flecs systems. @@ -10650,7 +10676,7 @@ ecs_ftime_t ecs_get_timeout( ecs_entity_t tick_source); /** Set timer interval. - * This operation will continously invoke systems associated with the timer + * This operation will continuously invoke systems associated with the timer * after the interval period expires. If the entity contains an existing timer, * the interval value will be reset. * @@ -10719,7 +10745,7 @@ void ecs_reset_timer( ecs_entity_t tick_source); /** Enable randomizing initial time value of timers. - * Intializes timers with a random time value, which can improve scheduling as + * Initializes timers with a random time value, which can improve scheduling as * systems/timers for the same interval don't all happen on the same tick. * * @param world The world. @@ -11136,13 +11162,13 @@ typedef struct ecs_system_desc_t { /** Rate at which the system should run */ int32_t rate; - /** External tick soutce that determines when system ticks */ + /** External tick source that determines when system ticks */ ecs_entity_t tick_source; /** If true, system will be ran on multiple threads */ bool multi_threaded; - /** If true, system will have access to actuall world. Cannot be true at the + /** If true, system will have access to the actual world. Cannot be true at the * same time as multi_threaded. */ bool no_readonly; } ecs_system_desc_t; @@ -11407,26 +11433,20 @@ typedef struct ecs_world_stats_t { ecs_metric_t not_alive_count; /**< Number of not alive (recyclable) entity ids */ } entities; - /* Components and ids */ + /* Component ids */ struct { - ecs_metric_t count; /**< Number of ids (excluding wildcards) */ ecs_metric_t tag_count; /**< Number of tag ids (ids without data) */ ecs_metric_t component_count; /**< Number of components ids (ids with data) */ ecs_metric_t pair_count; /**< Number of pair ids */ - ecs_metric_t wildcard_count; /**< Number of wildcard ids */ ecs_metric_t type_count; /**< Number of registered types */ ecs_metric_t create_count; /**< Number of times id has been created */ ecs_metric_t delete_count; /**< Number of times id has been deleted */ - } ids; + } components; /* Tables */ struct { ecs_metric_t count; /**< Number of tables */ ecs_metric_t empty_count; /**< Number of empty tables */ - ecs_metric_t tag_only_count; /**< Number of tables with only tags */ - ecs_metric_t trivial_only_count; /**< Number of tables with only trivial components */ - ecs_metric_t record_count; /**< Number of table cache records */ - ecs_metric_t storage_count; /**< Number of table storages */ ecs_metric_t create_count; /**< Number of times table has been created */ ecs_metric_t delete_count; /**< Number of times table has been deleted */ } tables; @@ -11493,23 +11513,6 @@ typedef struct ecs_world_stats_t { ecs_metric_t stack_outstanding_alloc_count; /**< Difference between allocs & frees */ } memory; - /* REST statistics */ - struct { - ecs_metric_t request_count; - ecs_metric_t entity_count; - ecs_metric_t entity_error_count; - ecs_metric_t query_count; - ecs_metric_t query_error_count; - ecs_metric_t query_name_count; - ecs_metric_t query_name_error_count; - ecs_metric_t query_name_from_cache_count; - ecs_metric_t enable_count; - ecs_metric_t enable_error_count; - ecs_metric_t world_stats_count; - ecs_metric_t pipeline_stats_count; - ecs_metric_t stats_error_count; - } rest; - /* HTTP statistics */ struct { ecs_metric_t request_received_count; @@ -12822,7 +12825,7 @@ typedef struct ecs_iter_to_json_desc_t { /** Serialize iterator into JSON string. * This operation will iterate the contents of the iterator and serialize them - * to JSON. The function acccepts iterators from any source. + * to JSON. The function accepts iterators from any source. * * @param world The world. * @param iter The iterator to serialize to JSON. @@ -13398,6 +13401,7 @@ FLECS_API extern const ecs_entity_t ecs_id(ecs_f32_t); FLECS_API extern const ecs_entity_t ecs_id(ecs_f64_t); FLECS_API extern const ecs_entity_t ecs_id(ecs_string_t); FLECS_API extern const ecs_entity_t ecs_id(ecs_entity_t); +FLECS_API extern const ecs_entity_t ecs_id(ecs_id_t); /** Type kinds supported by meta addon */ typedef enum ecs_type_kind_t { @@ -13437,7 +13441,8 @@ typedef enum ecs_primitive_kind_t { EcsIPtr, EcsString, EcsEntity, - EcsPrimitiveKindLast = EcsEntity + EcsId, + EcsPrimitiveKindLast = EcsId } ecs_primitive_kind_t; /** Component added to primitive types */ @@ -13656,6 +13661,12 @@ typedef struct EcsOpaque { ecs_world_t *world, ecs_entity_t entity); + /** Assign (component) id value */ + void (*assign_id)( + void *dst, + ecs_world_t *world, + ecs_id_t id); + /** Assign null value */ void (*assign_null)( void *dst); @@ -13746,7 +13757,8 @@ typedef enum ecs_meta_type_op_kind_t { EcsOpIPtr, EcsOpString, EcsOpEntity, - EcsMetaTypeOpKindLast = EcsOpEntity + EcsOpId, + EcsMetaTypeOpKindLast = EcsOpId } ecs_meta_type_op_kind_t; typedef struct ecs_meta_type_op_t { @@ -13922,6 +13934,18 @@ int ecs_meta_set_entity( ecs_meta_cursor_t *cursor, ecs_entity_t value); +/** Set field with (component) id value */ +FLECS_API +int ecs_meta_set_id( + ecs_meta_cursor_t *cursor, + ecs_id_t value); + +/** Set field with (component) id value */ +FLECS_API +int ecs_meta_set_component( + ecs_meta_cursor_t *cursor, + ecs_id_t value); + /** Set field with null value */ FLECS_API int ecs_meta_set_null( @@ -13974,6 +13998,11 @@ FLECS_API ecs_entity_t ecs_meta_get_entity( const ecs_meta_cursor_t *cursor); +/** Get field value as (component) id. + * This operation can convert from an entity. */ +ecs_id_t ecs_meta_get_id( + const ecs_meta_cursor_t *cursor); + /** Convert pointer of primitive kind to float. */ FLECS_API double ecs_meta_ptr_to_float( @@ -14260,7 +14289,7 @@ extern "C" { * * @param out The string to write the character to. * @param in The input character. - * @param delimiter The delimiiter used (for example '"') + * @param delimiter The delimiter used (for example '"') * @return Pointer to the character after the last one written. */ FLECS_API @@ -14286,7 +14315,7 @@ const char* ecs_chrparse( * argument for 'out', and use the returned size to allocate a string that is * large enough. * - * @param out Pointer to output string (msut be). + * @param out Pointer to output string (must be). * @param size Maximum number of characters written to output. * @param delimiter The delimiter used (for example '"'). * @param in The input string. @@ -14661,7 +14690,7 @@ int ecs_meta_from_desc( /* Private API */ -/* Utilities to switch beteen IMPL, DECLARE and EXTERN variants */ +/* Utilities to switch between IMPL, DECLARE and EXTERN variants */ #define ECS_META_IMPL_CALL_INNER(base, impl, name, type_desc)\ base ## impl(name, type_desc) @@ -14897,7 +14926,7 @@ void FlecsScriptImport( * @brief Rule query engine addon. * * Rules are advanced queries that in addition to the capabilities of regular - * queries and filters have the folllowing features: + * queries and filters have the following features: * * - query for all components of an entity (vs. all entities for a component) * - query for all relationship pairs of an entity @@ -15031,7 +15060,7 @@ const char* ecs_rule_var_name( * Internally the rule engine has entity variables and table variables. When * iterating through rule variables (by using ecs_rule_variable_count) only * the values for entity variables are accessible. This operation enables an - * appliction to check if a variable is an entity variable. + * application to check if a variable is an entity variable. * * @param rule The rule. * @param var_id The variable id. @@ -15088,7 +15117,7 @@ char* ecs_rule_str( /** Convert rule to string with profile. * To use this you must set the EcsIterProfile flag on an iterator before - * starting uteration: + * starting iteration: * it.flags |= EcsIterProfile * * @param rule The rule. @@ -15101,7 +15130,7 @@ char* ecs_rule_str_w_profile( /** Populate variables from key-value string. * Convenience function to set rule variables from a key-value string separated - * by comma's. The string must have the followig format: + * by comma's. The string must have the following format: * var_a: value, var_b: value * * The key-value list may optionally be enclosed in parenthesis. @@ -15267,6 +15296,9 @@ void ecs_snapshot_free( #ifndef FLECS_PARSER_H #define FLECS_PARSER_H +/** Maximum number of extra arguments in term expression */ +#define ECS_PARSER_MAX_ARGS (16) + #ifdef __cplusplus extern "C" { #endif @@ -15353,6 +15385,7 @@ const char* ecs_parse_token( * @param expr The expression to parse (optional, improves error logs) * @param ptr The pointer to the current term (must be in expr). * @param term_out Out parameter for the term. + * @param extra_args Out array for extra args, must be of size ECS_PARSER_MAX_ARGS. * @return pointer to next term if successful, NULL if failed. */ FLECS_API @@ -15361,7 +15394,8 @@ char* ecs_parse_term( const char *name, const char *expr, const char *ptr, - ecs_term_t *term_out); + ecs_term_t *term_out, + ecs_term_id_t *extra_args); #ifdef __cplusplus } @@ -15727,7 +15761,7 @@ template struct cpp_type; template -struct each_invoker; +struct each_delegate; } // namespace _ } // namespace flecs @@ -15826,6 +15860,7 @@ static const uint32_t Self = EcsSelf; static const uint32_t Up = EcsUp; static const uint32_t Down = EcsDown; static const uint32_t Cascade = EcsCascade; +static const uint32_t Desc = EcsDesc; static const uint32_t Parent = EcsParent; static const uint32_t IsVariable = EcsIsVariable; static const uint32_t IsEntity = EcsIsEntity; @@ -15845,6 +15880,7 @@ static const flecs::entity_t Transitive = EcsTransitive; static const flecs::entity_t Reflexive = EcsReflexive; static const flecs::entity_t Final = EcsFinal; static const flecs::entity_t DontInherit = EcsDontInherit; +static const flecs::entity_t AlwaysOverride = EcsAlwaysOverride; static const flecs::entity_t Tag = EcsTag; static const flecs::entity_t Union = EcsUnion; static const flecs::entity_t Exclusive = EcsExclusive; @@ -16001,6 +16037,12 @@ using transcribe_volatile_t = conditional_t::value, Dst volatil template using transcribe_cv_t = transcribe_const_t< Src, transcribe_volatile_t< Src, Dst> >; +template +using transcribe_pointer_t = conditional_t::value, Dst*, Dst>; + +template +using transcribe_cvp_t = transcribe_cv_t< Src, transcribe_pointer_t< Src, Dst> >; + // More convenience templates. The if_*_t templates use int as default type // instead of void. This enables writing code that's a bit less cluttered when @@ -16744,7 +16786,7 @@ using return_type_t = typename _::function_traits::return_type; template using arg_list_t = typename _::function_traits::args; - +// First arg template struct first_arg_impl; @@ -16761,6 +16803,23 @@ struct first_arg { template using first_arg_t = typename first_arg::type; +// Last arg +template +struct second_arg_impl; + +template +struct second_arg_impl > { + using type = T; +}; + +template +struct second_arg { + using type = typename second_arg_impl>::type; +}; + +template +using second_arg_t = typename second_arg::type; + } // flecs @@ -17049,15 +17108,7 @@ struct event_builder_base { /** Set entity for which to emit event */ Base& entity(flecs::entity_t e) { - ecs_record_t *r = ecs_record_find(m_world, e); - - /* Can't emit for empty entity */ - ecs_assert(r != nullptr, ECS_INVALID_PARAMETER, nullptr); - ecs_assert(r->table != nullptr, ECS_INVALID_PARAMETER, nullptr); - - m_desc.table = r->table; - m_desc.offset = ECS_RECORD_TO_ROW(r->row); - m_desc.count = 1; + m_desc.entity = e; return *this; } @@ -17071,19 +17122,30 @@ struct event_builder_base { /* Set event data */ Base& ctx(const E* ptr) { + m_desc.const_param = ptr; + return *this; + } + + /* Set event data */ + Base& ctx(E* ptr) { m_desc.param = ptr; return *this; } void emit() { - ecs_assert(m_ids.count != 0, ECS_INVALID_PARAMETER, NULL); - ecs_assert(m_desc.table != nullptr, ECS_INVALID_PARAMETER, NULL); m_ids.array = m_ids_array; m_desc.ids = &m_ids; m_desc.observable = const_cast(ecs_get_world(m_world)); ecs_emit(m_world, &m_desc); } + void enqueue() { + m_ids.array = m_ids_array; + m_desc.ids = &m_ids; + m_desc.observable = const_cast(ecs_get_world(m_world)); + ecs_enqueue(m_world, &m_desc); + } + protected: flecs::world_t *m_world; ecs_event_desc_t m_desc; @@ -17110,6 +17172,12 @@ struct event_builder_typed : event_builder_base, E> { /* Set event data */ Class& ctx(const E& ptr) { + this->m_desc.const_param = &ptr; + return *this; + } + + /* Set event data */ + Class& ctx(E&& ptr) { this->m_desc.param = &ptr; return *this; } @@ -17120,6 +17188,31 @@ struct event_builder_typed : event_builder_base, E> { } +namespace flecs { +namespace _ { + +// Utility to derive event type from function +template +struct event_from_func; + +// Specialization for observer callbacks with a single argument +template +struct event_from_func::value == 1>> { + using type = decay_t>; +}; + +// Specialization for observer callbacks with an initial entity src argument +template +struct event_from_func::value == 2>> { + using type = decay_t>; +}; + +template +using event_from_func_t = typename event_from_func::type; + +} +} + /** * @file addons/cpp/mixins/query/decl.hpp * @brief Query declarations. @@ -17655,6 +17748,11 @@ struct cursor { return ecs_meta_set_entity(&m_cursor, value); } + /** Set (component) id value */ + int set_id(flecs::id_t value) { + return ecs_meta_set_id(&m_cursor, value); + } + /** Set null value */ int set_null() { return ecs_meta_set_null(&m_cursor); @@ -17811,6 +17909,16 @@ struct opaque { return *this; } + /** Assign (component) id value */ + opaque& assign_id( + void (*func)(T *dst, ecs_world_t *world, ecs_id_t id)) + { + this->desc.type.assign_id = + reinterpret_castdesc.type.assign_id)>(func); + return *this; + } + /** Assign null value */ opaque& assign_null(void (*func)(T *dst)) { this->desc.type.assign_null = @@ -18343,6 +18451,11 @@ struct metric_builder { return id(_::cpp_type::id(m_world), second); } + template + metric_builder& id_second(flecs::entity_t first) { + return id(first, _::cpp_type::id(m_world)); + } + template metric_builder& id() { return id(_::cpp_type::id(m_world)); @@ -18747,25 +18860,26 @@ struct pair : _::pair_base { template ::value> = 0> using pair_object = pair; +template +using raw_type_t = remove_pointer_t>; /** Test if type is a pair. */ template struct is_pair { - static constexpr bool value = is_base_of<_::pair_base, remove_reference_t >::value; + static constexpr bool value = is_base_of<_::pair_base, raw_type_t >::value; }; - /** Get pair::first from pair while preserving cv qualifiers. */ template -using pair_first_t = transcribe_cv_t, typename remove_reference_t

::first>; +using pair_first_t = transcribe_cv_t, typename raw_type_t

::first>; /** Get pair::second from pair while preserving cv qualifiers. */ template -using pair_second_t = transcribe_cv_t, typename remove_reference_t

::second>; +using pair_second_t = transcribe_cv_t, typename raw_type_t

::second>; -/** Get pair::type type from pair while preserving cv qualifiers. */ +/** Get pair::type type from pair while preserving cv qualifiers and pointer type. */ template -using pair_type_t = transcribe_cv_t, typename remove_reference_t

::type>; +using pair_type_t = transcribe_cvp_t, typename raw_type_t

::type>; /** Get actual type from a regular type or pair. */ template @@ -19220,6 +19334,8 @@ namespace flecs */ template struct ref { + ref() : m_world(nullptr), m_ref{} { } + ref(world_t *world, entity_t entity, flecs::id_t id = 0) : m_ref() { @@ -19250,6 +19366,14 @@ struct ref { m_world, &m_ref, this->m_ref.id)); } + T* try_get() { + if (!m_world || !m_ref.entity) { + return nullptr; + } + + return get(); + } + flecs::entity entity() const; private: @@ -21774,7 +21898,7 @@ struct entity_view : public id { if (ecs_filter_init(m_world, &desc) != nullptr) { ecs_iter_t it = ecs_filter_iter(m_world, &f); while (ecs_filter_next(&it)) { - _::each_invoker(FLECS_MOV(func)).invoke(&it); + _::each_delegate(FLECS_MOV(func)).invoke(&it); } ecs_filter_fini(&f); @@ -22370,6 +22494,106 @@ template E to_constant() const; +/** + * @file addons/cpp/mixins/event/entity_builder.hpp + * @brief Event entity mixin. + */ + +/** Emit event for entity. + * + * \memberof flecs::entity_view + * + * @param evt The event to emit. + */ +void emit(flecs::entity_t evt) { + flecs::world(m_world) + .event(evt) + .entity(m_id) + .emit(); +} + +/** Emit event for entity. + * + * \memberof flecs::entity_view + * + * @param evt The event to emit. + */ +void emit(flecs::entity evt); + +/** Emit event for entity. + * + * \memberof flecs::entity_view + * + * @tparam Evt The event to emit. + */ +template ::value> = 0> +void emit() { + this->emit(_::cpp_type::id(m_world)); +} + +/** Emit event with payload for entity. + * + * \memberof flecs::entity_view + * + * @tparam Evt The event to emit. + */ +template ::value> = 0> +void emit(const Evt& payload) { + flecs::world(m_world) + .event(_::cpp_type::id(m_world)) + .entity(m_id) + .ctx(&payload) + .emit(); +} + + +/** Enqueue event for entity. + * + * \memberof flecs::entity_view + * + * @param evt The event to enqueue. + */ +void enqueue(flecs::entity_t evt) { + flecs::world(m_world) + .event(evt) + .entity(m_id) + .enqueue(); +} + +/** Enqueue event for entity. + * + * \memberof flecs::entity_view + * + * @param evt The event to enqueue. + */ +void enqueue(flecs::entity evt); + +/** Enqueue event for entity. + * + * \memberof flecs::entity_view + * + * @tparam Evt The event to enqueue. + */ +template ::value> = 0> +void enqueue() { + this->enqueue(_::cpp_type::id(m_world)); +} + +/** Enqueue event with payload for entity. + * + * \memberof flecs::entity_view + * + * @tparam Evt The event to enqueue. + */ +template ::value> = 0> +void enqueue(const Evt& payload) { + flecs::world(m_world) + .event(_::cpp_type::id(m_world)) + .entity(m_id) + .ctx(&payload) + .enqueue(); +} + private: flecs::entity set_stage(world_t *stage); @@ -23592,6 +23816,45 @@ Self& set_json_second( # endif +/** + * @file addons/cpp/mixins/event/entity_builder.hpp + * @brief Event entity mixin. + */ + +/** Observe event on entity + * + * \memberof flecs::entity_builder + * + * @param evt The event id. + * @param callback The observer callback. + * @return Event builder. + */ +template +Self& observe(flecs::entity_t evt, Func&& callback); + +/** Observe event on entity + * + * \memberof flecs::entity_builder + * + * @tparam Evt The event type. + * @param callback The observer callback. + * @return Event builder. + */ +template +Self& observe(Func&& callback); + +/** Observe event on entity + * + * \memberof flecs::entity_builder + * + * @param callback The observer callback. + * @return Event builder. + */ +template +Self& observe(Func&& callback); + + + protected: Self& to_base() { @@ -23911,8 +24174,8 @@ const char* from_json(const char *json) { /** @} */ /** - * @file addons/cpp/invoker.hpp - * @brief Utilities for invoking each/iter callbacks. + * @file addons/cpp/delegate.hpp + * @brief Wrappers around C++ functions that provide callbacks for C APIs. */ #pragma once @@ -23978,7 +24241,7 @@ struct term_ptrs { } }; -struct invoker { }; +struct delegate { }; // Template that figures out from the template parameters of a query/system // how to pass the value to the each callback @@ -24051,7 +24314,7 @@ struct each_column::value && each_column(const _::term_ptr& term, size_t row) : each_column_base(term, row) { } - T get_row() { + actual_type_t get_row() { if (this->m_term.ptr) { return &static_cast>(this->m_term.ptr)[this->m_row]; } else { @@ -24082,7 +24345,7 @@ struct each_ref_column : public each_column { }; template -struct each_invoker : public invoker { +struct each_delegate : public delegate { // If the number of arguments in the function signature is one more than the // number of components in the query, an extra entity arg is required. static constexpr bool PassEntity = @@ -24099,14 +24362,14 @@ struct each_invoker : public invoker { using Terms = typename term_ptrs::array; template < if_not_t< is_same< decay_t, decay_t& >::value > = 0> - explicit each_invoker(Func&& func) noexcept + explicit each_delegate(Func&& func) noexcept : m_func(FLECS_MOV(func)) { } - explicit each_invoker(const Func& func) noexcept + explicit each_delegate(const Func& func) noexcept : m_func(func) { } // Invoke object directly. This operation is useful when the calling - // function has just constructed the invoker, such as what happens when + // function has just constructed the delegate, such as what happens when // iterating a query. void invoke(ecs_iter_t *iter) const { term_ptrs terms; @@ -24120,11 +24383,21 @@ struct each_invoker : public invoker { // Static function that can be used as callback for systems/triggers static void run(ecs_iter_t *iter) { - auto self = static_cast(iter->binding_ctx); + auto self = static_cast(iter->binding_ctx); ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); self->invoke(iter); } + // Create instance of delegate + static each_delegate* make(const Func& func) { + return FLECS_NEW(each_delegate)(func); + } + + // Function that can be used as callback to free delegate + static void free(void *obj) { + _::free_obj(static_cast(obj)); + } + // Static function to call for component on_add hook static void run_add(ecs_iter_t *iter) { component_binding_ctx *ctx = reinterpret_cast( @@ -24149,7 +24422,7 @@ struct each_invoker : public invoker { run(iter); } - // Each invokers always use instanced iterators + // Each delegates always use instanced iterators static bool instanced() { return true; } @@ -24246,7 +24519,7 @@ struct each_invoker : public invoker { }; template -struct find_invoker : public invoker { +struct find_delegate : public delegate { // If the number of arguments in the function signature is one more than the // number of components in the query, an extra entity arg is required. static constexpr bool PassEntity = @@ -24263,14 +24536,14 @@ struct find_invoker : public invoker { using Terms = typename term_ptrs::array; template < if_not_t< is_same< decay_t, decay_t& >::value > = 0> - explicit find_invoker(Func&& func) noexcept + explicit find_delegate(Func&& func) noexcept : m_func(FLECS_MOV(func)) { } - explicit find_invoker(const Func& func) noexcept + explicit find_delegate(const Func& func) noexcept : m_func(func) { } // Invoke object directly. This operation is useful when the calling - // function has just constructed the invoker, such as what happens when + // function has just constructed the delegate, such as what happens when // iterating a query. flecs::entity invoke(ecs_iter_t *iter) const { term_ptrs terms; @@ -24282,7 +24555,7 @@ struct find_invoker : public invoker { } } - // Find invokers always use instanced iterators + // Find delegates always use instanced iterators static bool instanced() { return true; } @@ -24404,7 +24677,7 @@ struct find_invoker : public invoker { //////////////////////////////////////////////////////////////////////////////// template -struct iter_invoker : invoker { +struct iter_delegate : delegate { private: static constexpr bool IterOnly = arity::value == 1; @@ -24412,14 +24685,14 @@ struct iter_invoker : invoker { public: template < if_not_t< is_same< decay_t, decay_t& >::value > = 0> - explicit iter_invoker(Func&& func) noexcept + explicit iter_delegate(Func&& func) noexcept : m_func(FLECS_MOV(func)) { } - explicit iter_invoker(const Func& func) noexcept + explicit iter_delegate(const Func& func) noexcept : m_func(func) { } // Invoke object directly. This operation is useful when the calling - // function has just constructed the invoker, such as what happens when + // function has just constructed the delegate, such as what happens when // iterating a query. void invoke(ecs_iter_t *iter) const { term_ptrs terms; @@ -24429,12 +24702,12 @@ struct iter_invoker : invoker { // Static function that can be used as callback for systems/triggers static void run(ecs_iter_t *iter) { - auto self = static_cast(iter->binding_ctx); + auto self = static_cast(iter->binding_ctx); ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); self->invoke(iter); } - // Instancing needs to be enabled explicitly for iter invokers + // Instancing needs to be enabled explicitly for iter delegates static bool instanced() { return false; } @@ -24484,15 +24757,85 @@ struct iter_invoker : invoker { }; +//////////////////////////////////////////////////////////////////////////////// +//// Utility class to invoke an entity observer delegate +//////////////////////////////////////////////////////////////////////////////// + +template +struct entity_observer_delegate : delegate { + explicit entity_observer_delegate(Func&& func) noexcept + : m_func(FLECS_MOV(func)) { } + + // Static function that can be used as callback for systems/triggers + static void run(ecs_iter_t *iter) { + invoke(iter); + } +private: + template ::value == 1> = 0> + static void invoke(ecs_iter_t *iter) { + auto self = static_cast(iter->binding_ctx); + ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); + self->m_func(flecs::entity(iter->world, ecs_field_src(iter, 1))); + } + + template ::value == 0> = 0> + static void invoke(ecs_iter_t *iter) { + auto self = static_cast(iter->binding_ctx); + ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); + self->m_func(); + } + + Func m_func; +}; + +template +struct entity_payload_observer_delegate : delegate { + explicit entity_payload_observer_delegate(Func&& func) noexcept + : m_func(FLECS_MOV(func)) { } + + // Static function that can be used as callback for systems/triggers + static void run(ecs_iter_t *iter) { + invoke(iter); + } + +private: + template ::value == 1> = 0> + static void invoke(ecs_iter_t *iter) { + auto self = static_cast( + iter->binding_ctx); + ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); + ecs_assert(iter->param != nullptr, ECS_INVALID_OPERATION, + "entity observer invoked without payload"); + + Event *data = static_cast(iter->param); + self->m_func(*data); + } + + template ::value == 2> = 0> + static void invoke(ecs_iter_t *iter) { + auto self = static_cast( + iter->binding_ctx); + ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); + ecs_assert(iter->param != nullptr, ECS_INVALID_OPERATION, + "entity observer invoked without payload"); + + Event *data = static_cast(iter->param); + self->m_func(flecs::entity(iter->world, ecs_field_src(iter, 1)), *data); + } + + Func m_func; +}; + + //////////////////////////////////////////////////////////////////////////////// //// Utility to invoke callback on entity if it has components in signature //////////////////////////////////////////////////////////////////////////////// template -struct entity_with_invoker_impl; +struct entity_with_delegate_impl; template -struct entity_with_invoker_impl> { +struct entity_with_delegate_impl> { using ColumnArray = flecs::array; using ArrayType = flecs::array; using DummyArray = flecs::array; @@ -24566,8 +24909,8 @@ struct entity_with_invoker_impl> { } ArrayType ptrs; - bool has_components; - if ((has_components = get_ptrs(world, r, table, ptrs))) { + bool has_components = get_ptrs(world, r, table, ptrs); + if (has_components) { invoke_callback(func, 0, ptrs); } @@ -24589,8 +24932,8 @@ struct entity_with_invoker_impl> { } ArrayType ptrs; - bool has_components; - if ((has_components = get_ptrs(world, r, table, ptrs))) { + bool has_components = get_ptrs(world, r, table, ptrs); + if (has_components) { invoke_callback(func, 0, ptrs); } @@ -24711,13 +25054,13 @@ struct entity_with_invoker_impl> { }; template -struct entity_with_invoker { +struct entity_with_delegate { static_assert(function_traits::value, "type is not callable"); }; template -struct entity_with_invoker::value > > - : entity_with_invoker_impl< arg_list_t > +struct entity_with_delegate::value > > + : entity_with_delegate_impl< arg_list_t > { static_assert(function_traits::arity > 0, "function must have at least one argument"); @@ -24725,6 +25068,10 @@ struct entity_with_invoker::value > > } // namespace _ +// Experimental: allows using the each delegate for use cases outside of flecs +template +using delegate = _::each_delegate::type, Args...>; + } // namespace flecs /** @@ -24762,25 +25109,25 @@ struct iterable { template void each(flecs::world_t *world, Func&& func) const { - iterate<_::each_invoker>(world, FLECS_FWD(func), + iterate<_::each_delegate>(world, FLECS_FWD(func), this->next_each_action()); } template void each(flecs::iter& it, Func&& func) const { - iterate<_::each_invoker>(it.world(), FLECS_FWD(func), + iterate<_::each_delegate>(it.world(), FLECS_FWD(func), this->next_each_action()); } template void each(flecs::entity e, Func&& func) const { - iterate<_::each_invoker>(e.world(), FLECS_FWD(func), + iterate<_::each_delegate>(e.world(), FLECS_FWD(func), this->next_each_action()); } template flecs::entity find(Func&& func) const { - return iterate_find<_::find_invoker>(nullptr, FLECS_FWD(func), + return iterate_find<_::find_delegate>(nullptr, FLECS_FWD(func), this->next_each_action()); } @@ -24797,25 +25144,25 @@ struct iterable { */ template void iter(Func&& func) const { - iterate<_::iter_invoker>(nullptr, FLECS_FWD(func), + iterate<_::iter_delegate>(nullptr, FLECS_FWD(func), this->next_action()); } template void iter(flecs::world_t *world, Func&& func) const { - iterate<_::iter_invoker>(world, FLECS_FWD(func), + iterate<_::iter_delegate>(world, FLECS_FWD(func), this->next_action()); } template void iter(flecs::iter& it, Func&& func) const { - iterate<_::iter_invoker>(it.world(), FLECS_FWD(func), + iterate<_::iter_delegate>(it.world(), FLECS_FWD(func), this->next_action()); } template void iter(flecs::entity e, Func&& func) const { - iterate<_::iter_invoker>(e.world(), FLECS_FWD(func), + iterate<_::iter_delegate>(e.world(), FLECS_FWD(func), this->next_action()); } @@ -24868,28 +25215,28 @@ struct iterable { virtual ecs_iter_next_action_t next_action() const = 0; virtual ecs_iter_next_action_t next_each_action() const = 0; - template < template class Invoker, typename Func, typename NextFunc, typename ... Args> + template < template class Delegate, typename Func, typename NextFunc, typename ... Args> void iterate(flecs::world_t *stage, Func&& func, NextFunc next, Args &&... args) const { ecs_iter_t it = this->get_iter(stage); - if (Invoker::instanced()) { + if (Delegate::instanced()) { ECS_BIT_SET(it.flags, EcsIterIsInstanced); } while (next(&it, FLECS_FWD(args)...)) { - Invoker(func).invoke(&it); + Delegate(func).invoke(&it); } } - template < template class Invoker, typename Func, typename NextFunc, typename ... Args> + template < template class Delegate, typename Func, typename NextFunc, typename ... Args> flecs::entity iterate_find(flecs::world_t *stage, Func&& func, NextFunc next, Args &&... args) const { ecs_iter_t it = this->get_iter(stage); - if (Invoker::instanced()) { + if (Delegate::instanced()) { ECS_BIT_SET(it.flags, EcsIterIsInstanced); } flecs::entity result; while (!result && next(&it, FLECS_FWD(args)...)) { - result = Invoker(func).invoke(&it); + result = Delegate(func).invoke(&it); } if (result) { ecs_iter_fini(&it); @@ -25276,7 +25623,7 @@ struct cpp_type_impl { // If no id has been registered yet for the component (indicating the // component has not yet been registered, or the component is used // across more than one binary), or if the id does not exists in the - // world (indicating a multi-world application), register it. */ + // world (indicating a multi-world application), register it. if (!s_id || (world && !ecs_exists(world, s_id))) { init(s_id ? s_id : id, allow_tag); @@ -25335,7 +25682,7 @@ struct cpp_type_impl { // Register lifecycle callbacks, but only if the component has a // size. Components that don't have a size are tags, and tags don't - // require construction/destruction/copy/move's. */ + // require construction/destruction/copy/move's. if (size() && !existing) { register_lifecycle_actions(world, s_id); } @@ -25552,6 +25899,17 @@ untyped_component& bit(const char *name, uint32_t value) { return *this; } +/** Register array metadata for component */ +template +untyped_component& array(int32_t elem_count) { + ecs_array_desc_t desc = {}; + desc.entity = m_id; + desc.type = _::cpp_type::id(m_world); + desc.count = elem_count; + ecs_array_init(m_world, &desc); + return *this; +} + /** Add member value range */ untyped_component& range(double min, double max) { const flecs::member_t *m = ecs_cpp_last_member(m_world, m_id); @@ -25734,16 +26092,15 @@ struct component : untyped_component { /** Register on_add hook. */ template component& on_add(Func&& func) { - using Invoker = typename _::each_invoker< - typename std::decay::type, T>; + using Delegate = typename _::each_delegate::type, T>; flecs::type_hooks_t h = get_hooks(); ecs_assert(h.on_add == nullptr, ECS_INVALID_OPERATION, "on_add hook is already set"); BindingCtx *ctx = get_binding_ctx(h); - h.on_add = Invoker::run_add; - ctx->on_add = FLECS_NEW(Invoker)(FLECS_FWD(func)); + h.on_add = Delegate::run_add; + ctx->on_add = FLECS_NEW(Delegate)(FLECS_FWD(func)); ctx->free_on_add = reinterpret_cast( - _::free_obj); + _::free_obj); ecs_set_hooks_id(m_world, m_id, &h); return *this; } @@ -25751,16 +26108,16 @@ struct component : untyped_component { /** Register on_remove hook. */ template component& on_remove(Func&& func) { - using Invoker = typename _::each_invoker< + using Delegate = typename _::each_delegate< typename std::decay::type, T>; flecs::type_hooks_t h = get_hooks(); ecs_assert(h.on_remove == nullptr, ECS_INVALID_OPERATION, "on_remove hook is already set"); BindingCtx *ctx = get_binding_ctx(h); - h.on_remove = Invoker::run_remove; - ctx->on_remove = FLECS_NEW(Invoker)(FLECS_FWD(func)); + h.on_remove = Delegate::run_remove; + ctx->on_remove = FLECS_NEW(Delegate)(FLECS_FWD(func)); ctx->free_on_remove = reinterpret_cast( - _::free_obj); + _::free_obj); ecs_set_hooks_id(m_world, m_id, &h); return *this; } @@ -25768,16 +26125,16 @@ struct component : untyped_component { /** Register on_set hook. */ template component& on_set(Func&& func) { - using Invoker = typename _::each_invoker< + using Delegate = typename _::each_delegate< typename std::decay::type, T>; flecs::type_hooks_t h = get_hooks(); ecs_assert(h.on_set == nullptr, ECS_INVALID_OPERATION, "on_set hook is already set"); BindingCtx *ctx = get_binding_ctx(h); - h.on_set = Invoker::run_set; - ctx->on_set = FLECS_NEW(Invoker)(FLECS_FWD(func)); + h.on_set = Delegate::run_set; + ctx->on_set = FLECS_NEW(Delegate)(FLECS_FWD(func)); ctx->free_on_set = reinterpret_cast( - _::free_obj); + _::free_obj); ecs_set_hooks_id(m_world, m_id, &h); return *this; } @@ -26439,7 +26796,7 @@ flecs::entity ref::entity() const { template template ::value > > inline Self& entity_builder::set(const Func& func) { - _::entity_with_invoker::invoke_get_mut( + _::entity_with_delegate::invoke_get_mut( this->m_world, this->m_id, func); return to_base(); } @@ -26602,7 +26959,7 @@ inline void entity_view::each(const flecs::entity_view& rel, const Func& func) c template ::value > > inline bool entity_view::get(const Func& func) const { - return _::entity_with_invoker::invoke_get(m_world, m_id, func); + return _::entity_with_delegate::invoke_get(m_world, m_id, func); } inline flecs::entity entity_view::lookup(const char *path, bool search_path) const { @@ -26834,6 +27191,13 @@ struct term_id_builder_i { return this->cascade(_::cpp_type::id(this->world_v())); } + /* Use with cascade to iterate results in descending (bottom -> top) order */ + Base& desc() { + this->assert_term_id(); + m_term_id->flags |= flecs::Desc; + return *this; + } + /* The parent flag is short for up(flecs::ChildOf) */ Base& parent() { this->assert_term_id(); @@ -27490,17 +27854,17 @@ struct filter_builder_i : term_builder_i { template Base& with(Args&&... args) { - return this->term(FLECS_FWD(args)...); + return this->term(FLECS_FWD(args)...).inout_none(); } template Base& with(Args&&... args) { - return this->term(FLECS_FWD(args)...); + return this->term(FLECS_FWD(args)...).inout_none(); } template Base& with() { - return this->term(); + return this->term().inout_none(); } template @@ -27926,12 +28290,12 @@ namespace _ { // Each with entity parameter template -struct filter_invoker_w_ent; +struct filter_delegate_w_ent; template -struct filter_invoker_w_ent > +struct filter_delegate_w_ent > { - filter_invoker_w_ent(const flecs::world& world, Func&& func) { + filter_delegate_w_ent(const flecs::world& world, Func&& func) { auto f = world.filter(); f.each(FLECS_MOV(func)); } @@ -27939,12 +28303,12 @@ struct filter_invoker_w_ent > // Each without entity parameter template -struct filter_invoker_no_ent; +struct filter_delegate_no_ent; template -struct filter_invoker_no_ent > +struct filter_delegate_no_ent > { - filter_invoker_no_ent(const flecs::world& world, Func&& func) { + filter_delegate_no_ent(const flecs::world& world, Func&& func) { auto f = world.filter(); f.each(FLECS_MOV(func)); } @@ -27952,19 +28316,19 @@ struct filter_invoker_no_ent > // Switch between function with & without entity parameter template -struct filter_invoker; +struct filter_delegate; template -struct filter_invoker, flecs::entity>::value> > { - filter_invoker(const flecs::world& world, Func&& func) { - filter_invoker_w_ent>(world, FLECS_MOV(func)); +struct filter_delegate, flecs::entity>::value> > { + filter_delegate(const flecs::world& world, Func&& func) { + filter_delegate_w_ent>(world, FLECS_MOV(func)); } }; template -struct filter_invoker, flecs::entity>::value> > { - filter_invoker(const flecs::world& world, Func&& func) { - filter_invoker_no_ent>(world, FLECS_MOV(func)); +struct filter_delegate, flecs::entity>::value> > { + filter_delegate(const flecs::world& world, Func&& func) { + filter_delegate_no_ent>(world, FLECS_MOV(func)); } }; @@ -27972,7 +28336,7 @@ struct filter_invoker, flecs::entity>:: template inline void world::each(Func&& func) const { - _::filter_invoker f_invoker(*this, FLECS_MOV(func)); + _::filter_delegate f_delegate(*this, FLECS_MOV(func)); } template @@ -27982,7 +28346,7 @@ inline void world::each(Func&& func) const { ecs_iter_t it = ecs_term_iter(m_world, &t); while (ecs_term_next(&it)) { - _::each_invoker(func).invoke(&it); + _::each_delegate(func).invoke(&it); } } @@ -27993,7 +28357,7 @@ inline void world::each(flecs::id_t term_id, Func&& func) const { ecs_iter_t it = ecs_term_iter(m_world, &t); while (ecs_term_next(&it)) { - _::each_invoker(func).invoke(&it); + _::each_delegate(func).invoke(&it); } } @@ -28008,30 +28372,6 @@ inline filter_base::operator flecs::filter<> () const { } -/** - * @file addons/cpp/mixins/event/impl.hpp - * @brief Event implementation. - */ - -#pragma once - - -namespace flecs -{ - -// Mixin implementation - -inline flecs::event_builder world::event(flecs::entity_t evt) const { - return flecs::event_builder(m_world, evt); -} - -template -inline flecs::event_builder_typed world::event() const { - return flecs::event_builder_typed(m_world, _::cpp_type().id(m_world)); -} - -} // namespace flecs - /** * @file addons/cpp/mixins/query/impl.hpp * @brief Query implementation. @@ -28459,6 +28799,7 @@ struct node_builder : IBuilder ecs_entity_desc_t entity_desc = {}; entity_desc.name = name; entity_desc.sep = "::"; + entity_desc.root_sep = "::"; m_desc.entity = ecs_entity_init(m_world, &entity_desc); } @@ -28467,19 +28808,19 @@ struct node_builder : IBuilder * template parameters and anything provided by the signature method. */ template T iter(Func&& func) { - using Invoker = typename _::iter_invoker< + using Delegate = typename _::iter_delegate< typename std::decay::type, Components...>; - return build(FLECS_FWD(func)); + return build(FLECS_FWD(func)); } /* Each is similar to action, but accepts a function that operates on a * single entity */ template T each(Func&& func) { - using Invoker = typename _::each_invoker< + using Delegate = typename _::each_delegate< typename std::decay::type, Components...>; m_instanced = true; - return build(FLECS_FWD(func)); + return build(FLECS_FWD(func)); } protected: @@ -28489,13 +28830,13 @@ struct node_builder : IBuilder bool m_instanced; private: - template + template T build(Func&& func) { - auto ctx = FLECS_NEW(Invoker)(FLECS_FWD(func)); - m_desc.callback = Invoker::run; + auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(func)); + m_desc.callback = Delegate::run; m_desc.binding_ctx = ctx; m_desc.binding_ctx_free = reinterpret_cast< - ecs_ctx_free_t>(_::free_obj); + ecs_ctx_free_t>(_::free_obj); return T(m_world, &m_desc, m_instanced); } @@ -28660,6 +29001,113 @@ inline observer_builder world::observer(Args &&... args) const { } // namespace flecs +/** + * @file addons/cpp/mixins/event/impl.hpp + * @brief Event implementation. + */ + +#pragma once + + +namespace flecs +{ + +// Mixin implementation + +inline flecs::event_builder world::event(flecs::entity_t evt) const { + return flecs::event_builder(m_world, evt); +} + +template +inline flecs::event_builder_typed world::event() const { + return flecs::event_builder_typed(m_world, _::cpp_type().id(m_world)); +} + +namespace _ { + inline void entity_observer_create( + flecs::world_t *world, + flecs::entity_t event, + flecs::entity_t entity, + ecs_iter_action_t callback, + void *binding_ctx, + ecs_ctx_free_t binding_ctx_free) + { + ecs_observer_desc_t desc = {}; + desc.events[0] = event; + desc.filter.terms[0].id = EcsAny; + desc.filter.terms[0].src.id = entity; + desc.callback = callback; + desc.binding_ctx = binding_ctx; + desc.binding_ctx_free = binding_ctx_free; + + flecs::entity_t o = ecs_observer_init(world, &desc); + ecs_add_pair(world, o, EcsChildOf, entity); + } + + template + struct entity_observer_factory { + template ::value> = 0> + static void create( + flecs::world_t *world, + flecs::entity_t entity, + Func&& f) + { + using Delegate = _::entity_observer_delegate; + auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(f)); + entity_observer_create(world, _::cpp_type::id(world), entity, Delegate::run, ctx, + reinterpret_cast(_::free_obj)); + } + + template ::value> = 0> + static void create( + flecs::world_t *world, + flecs::entity_t entity, + Func&& f) + { + using Delegate = _::entity_payload_observer_delegate; + auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(f)); + entity_observer_create(world, _::cpp_type::id(world), entity, Delegate::run, ctx, + reinterpret_cast(_::free_obj)); + } + }; +} + +template +template +inline Self& entity_builder::observe(flecs::entity_t evt, Func&& f) { + using Delegate = _::entity_observer_delegate; + auto ctx = FLECS_NEW(Delegate)(FLECS_FWD(f)); + + _::entity_observer_create(m_world, evt, m_id, Delegate::run, ctx, + reinterpret_cast(_::free_obj)); + + return to_base(); +} + +template +template +inline Self& entity_builder::observe(Func&& f) { + _::entity_observer_factory::template create( + m_world, m_id, FLECS_FWD(f)); + return to_base(); +} + +template +template +inline Self& entity_builder::observe(Func&& f) { + return this->observe<_::event_from_func_t>(FLECS_FWD(f)); +} + +inline void entity_view::emit(flecs::entity evt) { + this->emit(evt.id()); +} + +inline void entity_view::enqueue(flecs::entity evt) { + this->enqueue(evt.id()); +} + +} // namespace flecs + /** * @file addons/cpp/mixins/enum/impl.hpp * @brief Enum implementation. @@ -30188,7 +30636,17 @@ inline metric_builder::~metric_builder() { } inline metric_builder& metric_builder::member(const char *name) { - return member(flecs::world(m_world).lookup(name)); + flecs::entity m; + if (m_desc.id) { + flecs::entity_t type = ecs_get_typeid(m_world, m_desc.id); + m = flecs::entity(m_world, type).lookup(name); + } else { + m = flecs::world(m_world).lookup(name); + } + if (!m) { + flecs::log::err("member '%s' not found", name); + } + return member(m); } template @@ -30839,14 +31297,14 @@ inline flecs::entity world::target( template ::value > > inline void world::get(const Func& func) const { static_assert(arity::value == 1, "singleton component must be the only argument"); - _::entity_with_invoker::invoke_get( + _::entity_with_delegate::invoke_get( this->m_world, this->singleton>(), func); } template ::value > > inline void world::set(const Func& func) const { static_assert(arity::value == 1, "singleton component must be the only argument"); - _::entity_with_invoker::invoke_get_mut( + _::entity_with_delegate::invoke_get_mut( this->m_world, this->singleton>(), func); } diff --git a/flecs-sys/src/bindings.rs b/flecs-sys/src/bindings.rs index 78f3dd9..f80038a 100644 --- a/flecs-sys/src/bindings.rs +++ b/flecs-sys/src/bindings.rs @@ -60,6 +60,10 @@ pub const EcsIterIgnoreThis: u32 = 128; pub const EcsIterMatchVar: u32 = 256; pub const EcsIterHasCondSet: u32 = 1024; pub const EcsIterProfile: u32 = 2048; +pub const EcsIterTrivialSearch: u32 = 4096; +pub const EcsIterTrivialSearchNoData: u32 = 8192; +pub const EcsIterTrivialTest: u32 = 16384; +pub const EcsIterTrivialSearchWildcard: u32 = 32768; pub const EcsEventTableOnly: u32 = 16; pub const EcsEventNoOnSet: u32 = 65536; pub const EcsFilterMatchThis: u32 = 2; @@ -75,6 +79,11 @@ pub const EcsFilterHasCondSet: u32 = 1024; pub const EcsFilterUnresolvedByName: u32 = 2048; pub const EcsFilterHasPred: u32 = 4096; pub const EcsFilterHasScopes: u32 = 8192; +pub const EcsFilterIsTrivial: u32 = 16384; +pub const EcsFilterMatchOnlySelf: u32 = 32768; +pub const EcsFilterHasWildcards: u32 = 65536; +pub const EcsFilterOwnsStorage: u32 = 131072; +pub const EcsFilterOwnsTermsStorage: u32 = 262144; pub const EcsTableHasBuiltins: u32 = 2; pub const EcsTableIsPrefab: u32 = 4; pub const EcsTableHasIsA: u32 = 8; @@ -140,12 +149,13 @@ pub const EcsUp: u32 = 4; pub const EcsDown: u32 = 8; pub const EcsTraverseAll: u32 = 16; pub const EcsCascade: u32 = 32; -pub const EcsParent: u32 = 64; -pub const EcsIsVariable: u32 = 128; -pub const EcsIsEntity: u32 = 256; -pub const EcsIsName: u32 = 512; -pub const EcsFilter: u32 = 1024; -pub const EcsTraverseFlags: u32 = 126; +pub const EcsDesc: u32 = 64; +pub const EcsParent: u32 = 128; +pub const EcsIsVariable: u32 = 256; +pub const EcsIsEntity: u32 = 512; +pub const EcsIsName: u32 = 1024; +pub const EcsFilter: u32 = 2048; +pub const EcsTraverseFlags: u32 = 254; pub const EcsTermMatchAny: u32 = 1; pub const EcsTermMatchAnySrc: u32 = 2; pub const EcsTermSrcFirstEq: u32 = 4; @@ -153,6 +163,8 @@ pub const EcsTermSrcSecondEq: u32 = 8; pub const EcsTermTransitive: u32 = 16; pub const EcsTermReflexive: u32 = 32; pub const EcsTermIdInherited: u32 = 64; +pub const EcsTermIsTrivial: u32 = 128; +pub const EcsTermNoData: u32 = 256; pub const EcsTermMatchDisabled: u32 = 128; pub const EcsTermMatchPrefab: u32 = 256; pub const flecs_iter_cache_ids: u32 = 1; @@ -219,7 +231,8 @@ pub const ECS_STAT_WINDOW: u32 = 60; pub const ECS_ALERT_MAX_SEVERITY_FILTERS: u32 = 4; pub const ECS_MEMBER_DESC_CACHE_SIZE: u32 = 32; pub const ECS_META_MAX_SCOPE_DEPTH: u32 = 32; -pub type va_list = __builtin_va_list; +pub const ECS_PARSER_MAX_ARGS: u32 = 16; +pub type va_list = *mut ::std::os::raw::c_char; #[doc = "Utility types to indicate usage as bitmask"] pub type ecs_flags8_t = u8; pub type ecs_flags16_t = u16; @@ -1068,7 +1081,7 @@ pub struct ecs_os_api_t { #[doc = "Tasks"] pub task_new_: ecs_os_api_thread_new_t, pub task_join_: ecs_os_api_thread_join_t, - #[doc = "Atomic incremenet / decrement"] + #[doc = "Atomic increment / decrement"] pub ainc_: ecs_os_api_ainc_t, pub adec_: ecs_os_api_ainc_t, pub lainc_: ecs_os_api_lainc_t, @@ -1414,7 +1427,7 @@ pub const ecs_inout_kind_t_EcsIn: ecs_inout_kind_t = 3; #[doc = "< Term is only written"] pub const ecs_inout_kind_t_EcsOut: ecs_inout_kind_t = 4; #[doc = "Specify read/write access for term"] -pub type ecs_inout_kind_t = ::std::os::raw::c_uint; +pub type ecs_inout_kind_t = ::std::os::raw::c_int; #[doc = "< The term must match"] pub const ecs_oper_kind_t_EcsAnd: ecs_oper_kind_t = 0; #[doc = "< One of the terms in an or chain must match"] @@ -1430,7 +1443,7 @@ pub const ecs_oper_kind_t_EcsOrFrom: ecs_oper_kind_t = 5; #[doc = "< Term must match none of the components from term id"] pub const ecs_oper_kind_t_EcsNotFrom: ecs_oper_kind_t = 6; #[doc = "Specify operator for term"] -pub type ecs_oper_kind_t = ::std::os::raw::c_uint; +pub type ecs_oper_kind_t = ::std::os::raw::c_int; #[doc = "Type that describes a single identifier in a term"] #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -1482,18 +1495,16 @@ extern "C" { #[derive(Debug, Copy, Clone)] pub struct ecs_filter_t { pub hdr: ecs_header_t, - #[doc = "< Array containing terms for filter"] - pub terms: *mut ecs_term_t, #[doc = "< Number of elements in terms array"] - pub term_count: i32, + pub term_count: i8, #[doc = "< Number of fields in iterator for filter"] - pub field_count: i32, - #[doc = "< Is filter object owned by filter"] - pub owned: bool, - #[doc = "< Is terms array owned by filter"] - pub terms_owned: bool, + pub field_count: i8, #[doc = "< Filter flags"] pub flags: ecs_flags32_t, + #[doc = "< Bitset with fields that have data"] + pub data_fields: ecs_flags64_t, + #[doc = "< Array containing terms for filter"] + pub terms: *mut ecs_term_t, #[doc = "< Placeholder variable names array"] pub variable_names: [*mut ::std::os::raw::c_char; 1usize], #[doc = "< Field size (same for each result)"] @@ -1746,7 +1757,7 @@ pub const ecs_iter_kind_t_EcsIterEvalCondition: ecs_iter_kind_t = 0; pub const ecs_iter_kind_t_EcsIterEvalTables: ecs_iter_kind_t = 1; pub const ecs_iter_kind_t_EcsIterEvalChain: ecs_iter_kind_t = 2; pub const ecs_iter_kind_t_EcsIterEvalNone: ecs_iter_kind_t = 3; -pub type ecs_iter_kind_t = ::std::os::raw::c_uint; +pub type ecs_iter_kind_t = ::std::os::raw::c_int; #[doc = "Filter-iterator specific data"] #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -1797,8 +1808,8 @@ pub struct ecs_rule_iter_t { #[doc = "Operation-specific state"] pub op_ctx: *mut ecs_rule_op_ctx_t, pub written: *mut u64, + pub source_set: ecs_flags32_t, pub profile: *mut ecs_rule_op_profile_t, - pub redo: bool, pub op: i16, pub sp: i16, } @@ -2212,9 +2223,9 @@ pub struct ecs_observer_desc_t { #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct ecs_event_desc_t { - #[doc = "The event id. Only triggers for the specified event will be notified"] + #[doc = "The event id. Only observers for the specified event will be notified"] pub event: ecs_entity_t, - #[doc = "Component ids. Only triggers with a matching component id will be\n notified. Observers are guaranteed to get notified once, even if they\n match more than one id."] + #[doc = "Component ids. Only observers with a matching component id will be\n notified. Observers are guaranteed to get notified once, even if they\n match more than one id."] pub ids: *const ecs_type_t, #[doc = "The table for which to notify."] pub table: *mut ecs_table_t, @@ -2226,8 +2237,10 @@ pub struct ecs_event_desc_t { pub count: i32, #[doc = "Single-entity alternative to setting table / offset / count"] pub entity: ecs_entity_t, - #[doc = "Optional context. Assigned to iter param member"] - pub param: *const ::std::os::raw::c_void, + #[doc = "Optional context.\n The type of the param must be the event, where the event is a component.\n When an event is enqueued, the value of param is coped to a temporary\n storage of the event type."] + pub param: *mut ::std::os::raw::c_void, + #[doc = "Same as param, but with the guarantee that the value won't be modified.\n When an event with a const parameter is enqueued, the value of the param\n is copied to a temporary storage of the event type."] + pub const_param: *const ::std::os::raw::c_void, #[doc = "Observable (usually the world)"] pub observable: *mut ecs_poly_t, #[doc = "Event flags"] @@ -2292,28 +2305,16 @@ pub struct ecs_world_info_t { pub systems_ran_frame: i64, #[doc = "< Total number of times observer was invoked"] pub observers_ran_frame: i64, - #[doc = "< Number of ids in the world (excluding wildcards)"] - pub id_count: i32, #[doc = "< Number of tag (no data) ids in the world"] pub tag_id_count: i32, #[doc = "< Number of component (data) ids in the world"] pub component_id_count: i32, #[doc = "< Number of pair ids in the world"] pub pair_id_count: i32, - #[doc = "< Number of wildcard ids"] - pub wildcard_id_count: i32, #[doc = "< Number of tables"] pub table_count: i32, - #[doc = "< Number of tag-only tables"] - pub tag_table_count: i32, - #[doc = "< Number of tables with trivial components (no lifecycle callbacks)"] - pub trivial_table_count: i32, #[doc = "< Number of tables without entities"] pub empty_table_count: i32, - #[doc = "< Total number of table records (entries in table caches)"] - pub table_record_count: i32, - #[doc = "< Total number of table storages"] - pub table_storage_count: i32, pub cmd: ecs_world_info_t__bindgen_ty_1, #[doc = "< Value set by ecs_set_name_prefix. Used\n to remove library prefixes of symbol\n names (such as Ecs, ecs_) when\n registering them as names."] pub name_prefix: *const ::std::os::raw::c_char, @@ -2413,6 +2414,7 @@ extern "C" { pub static ECS_AND: ecs_id_t; } extern "C" { + #[doc = "Builtin component ids"] pub static FLECS_IDEcsComponentID_: ecs_entity_t; } extern "C" { @@ -2438,9 +2440,11 @@ extern "C" { pub static FLECS_IDEcsTickSourceID_: ecs_entity_t; } extern "C" { + #[doc = "Pipeline module component ids"] pub static FLECS_IDEcsPipelineQueryID_: ecs_entity_t; } extern "C" { + #[doc = "Timer module component ids"] pub static FLECS_IDEcsTimerID_: ecs_entity_t; } extern "C" { @@ -2627,6 +2631,7 @@ extern "C" { pub static EcsPanic: ecs_entity_t; } extern "C" { + #[doc = "Component that stores data for flattened relationships"] pub static FLECS_IDEcsTargetID_: ecs_entity_t; } extern "C" { @@ -2659,6 +2664,7 @@ extern "C" { pub static EcsEmpty: ecs_entity_t; } extern "C" { + #[doc = "Pipeline module tags"] pub static FLECS_IDEcsPipelineID_: ecs_entity_t; } extern "C" { @@ -2920,7 +2926,7 @@ extern "C" { pub fn ecs_new_low_id(world: *mut ecs_world_t) -> ecs_entity_t; } extern "C" { - #[doc = "Create new entity with (component) id.\n This operation creates a new entity with an optional (component) id. When 0\n is passed to the id paramter, no component is added to the new entity.\n\n @param world The world.\n @param id The component id to initialize the new entity with.\n @return The new entity."] + #[doc = "Create new entity with (component) id.\n This operation creates a new entity with an optional (component) id. When 0\n is passed to the id parameter, no component is added to the new entity.\n\n @param world The world.\n @param id The component id to initialize the new entity with.\n @return The new entity."] pub fn ecs_new_w_id(world: *mut ecs_world_t, id: ecs_id_t) -> ecs_entity_t; } extern "C" { @@ -2948,7 +2954,7 @@ extern "C" { ) -> *const ecs_entity_t; } extern "C" { - #[doc = "Clone an entity\n This operation clones the components of one entity into another entity. If\n no destination entity is provided, a new entity will be created. Component\n values are not copied unless copy_value is true.\n\n @param world The world.\n @param dst The entity to copy the components to.\n @param src The entity to copy the components from.\n @param copy_value If true, the value of components will be copied to dst.\n @return The destination entity."] + #[doc = "Clone an entity\n This operation clones the components of one entity into another entity. If\n no destination entity is provided, a new entity will be created. Component\n values are not copied unless copy_value is true.\n\n If the source entity has a name, it will not be copied to the destination\n entity. This is to prevent having two entities with the same name under the\n same parent, which is not allowed.\n\n @param world The world.\n @param dst The entity to copy the components to.\n @param src The entity to copy the components from.\n @param copy_value If true, the value of components will be copied to dst.\n @return The destination entity."] pub fn ecs_clone( world: *mut ecs_world_t, dst: ecs_entity_t, @@ -3651,6 +3657,9 @@ extern "C" { #[doc = "Send event.\n This sends an event to matching triggers & is the mechanism used by flecs\n itself to send OnAdd, OnRemove, etc events.\n\n Applications can use this function to send custom events, where a custom\n event can be any regular entity.\n\n Applications should not send builtin flecs events, as this may violate\n assumptions the code makes about the conditions under which those events are\n sent.\n\n Triggers are invoked synchronously. It is therefore safe to use stack-based\n data as event context, which can be set in the \"param\" member.\n\n @param world The world.\n @param desc Event parameters."] pub fn ecs_emit(world: *mut ecs_world_t, desc: *mut ecs_event_desc_t); } +extern "C" { + pub fn ecs_enqueue(world: *mut ecs_world_t, desc: *mut ecs_event_desc_t); +} extern "C" { #[doc = "Create observer.\n Observers are like triggers, but can subscribe for multiple terms. An\n observer only triggers when the source of the event meets all terms.\n\n See the documentation for ecs_observer_desc_t for more details.\n\n @param world The world.\n @param desc The observer creation parameters."] pub fn ecs_observer_init( @@ -4150,15 +4159,14 @@ extern "C" { ); } extern "C" { - pub fn ecs_assert_( - condition: bool, + pub fn ecs_assert_log_( error_code: i32, condition_str: *const ::std::os::raw::c_char, file: *const ::std::os::raw::c_char, line: i32, fmt: *const ::std::os::raw::c_char, ... - ) -> bool; + ); } extern "C" { pub fn ecs_parser_error_( @@ -4290,7 +4298,7 @@ pub const ecs_http_method_t_EcsHttpDelete: ecs_http_method_t = 3; pub const ecs_http_method_t_EcsHttpOptions: ecs_http_method_t = 4; pub const ecs_http_method_t_EcsHttpMethodUnsupported: ecs_http_method_t = 5; #[doc = "Supported request methods"] -pub type ecs_http_method_t = ::std::os::raw::c_uint; +pub type ecs_http_method_t = ::std::os::raw::c_int; #[doc = "A request"] #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -4428,6 +4436,7 @@ extern "C" { ) -> *const ::std::os::raw::c_char; } extern "C" { + #[doc = "Component that instantiates the REST API"] pub static FLECS_IDEcsRestID_: ecs_entity_t; } #[repr(C)] @@ -4439,52 +4448,6 @@ pub struct EcsRest { pub ipaddr: *mut ::std::os::raw::c_char, pub impl_: *mut ::std::os::raw::c_void, } -extern "C" { - #[doc = "Global statistics"] - pub static mut ecs_rest_request_count: i64; -} -extern "C" { - pub static mut ecs_rest_entity_count: i64; -} -extern "C" { - pub static mut ecs_rest_entity_error_count: i64; -} -extern "C" { - pub static mut ecs_rest_query_count: i64; -} -extern "C" { - pub static mut ecs_rest_query_error_count: i64; -} -extern "C" { - pub static mut ecs_rest_query_name_count: i64; -} -extern "C" { - pub static mut ecs_rest_query_name_error_count: i64; -} -extern "C" { - pub static mut ecs_rest_query_name_from_cache_count: i64; -} -extern "C" { - pub static mut ecs_rest_enable_count: i64; -} -extern "C" { - pub static mut ecs_rest_enable_error_count: i64; -} -extern "C" { - pub static mut ecs_rest_delete_count: i64; -} -extern "C" { - pub static mut ecs_rest_delete_error_count: i64; -} -extern "C" { - pub static mut ecs_rest_world_stats_count: i64; -} -extern "C" { - pub static mut ecs_rest_pipeline_stats_count: i64; -} -extern "C" { - pub static mut ecs_rest_stats_error_count: i64; -} extern "C" { #[doc = "Create HTTP server for REST API.\n This allows for the creation of a REST server that can be managed by the\n application without using Flecs systems.\n\n @param world The world.\n @param desc The HTTP server descriptor.\n @return The HTTP server, or NULL if failed."] pub fn ecs_rest_server_init( @@ -4543,7 +4506,7 @@ extern "C" { pub fn ecs_get_timeout(world: *const ecs_world_t, tick_source: ecs_entity_t) -> f32; } extern "C" { - #[doc = "Set timer interval.\n This operation will continously invoke systems associated with the timer\n after the interval period expires. If the entity contains an existing timer,\n the interval value will be reset.\n\n The timer is synchronous, and is incremented each frame by delta_time.\n\n The tick_source entity will be a tick source after this operation. Tick\n sources can be read by getting the EcsTickSource component. If the tick\n source ticked this frame, the 'tick' member will be true. When the tick\n source is a system, the system will tick when the timer ticks.\n\n @param world The world.\n @param tick_source The timer for which to set the interval (0 to create one).\n @param interval The interval value.\n @return The timer entity."] + #[doc = "Set timer interval.\n This operation will continuously invoke systems associated with the timer\n after the interval period expires. If the entity contains an existing timer,\n the interval value will be reset.\n\n The timer is synchronous, and is incremented each frame by delta_time.\n\n The tick_source entity will be a tick source after this operation. Tick\n sources can be read by getting the EcsTickSource component. If the tick\n source ticked this frame, the 'tick' member will be true. When the tick\n source is a system, the system will tick when the timer ticks.\n\n @param world The world.\n @param tick_source The timer for which to set the interval (0 to create one).\n @param interval The interval value.\n @return The timer entity."] pub fn ecs_set_interval( world: *mut ecs_world_t, tick_source: ecs_entity_t, @@ -4567,7 +4530,7 @@ extern "C" { pub fn ecs_reset_timer(world: *mut ecs_world_t, tick_source: ecs_entity_t); } extern "C" { - #[doc = "Enable randomizing initial time value of timers.\n Intializes timers with a random time value, which can improve scheduling as\n systems/timers for the same interval don't all happen on the same tick.\n\n @param world The world."] + #[doc = "Enable randomizing initial time value of timers.\n Initializes timers with a random time value, which can improve scheduling as\n systems/timers for the same interval don't all happen on the same tick.\n\n @param world The world."] pub fn ecs_randomize_timers(world: *mut ecs_world_t); } extern "C" { @@ -4680,11 +4643,11 @@ pub struct ecs_system_desc_t { pub interval: f32, #[doc = "Rate at which the system should run"] pub rate: i32, - #[doc = "External tick soutce that determines when system ticks"] + #[doc = "External tick source that determines when system ticks"] pub tick_source: ecs_entity_t, #[doc = "If true, system will be ran on multiple threads"] pub multi_threaded: bool, - #[doc = "If true, system will have access to actuall world. Cannot be true at the\n same time as multi_threaded."] + #[doc = "If true, system will have access to the actual world. Cannot be true at the\n same time as multi_threaded."] pub no_readonly: bool, } extern "C" { @@ -4775,15 +4738,14 @@ pub union ecs_metric_t { pub struct ecs_world_stats_t { pub first_: i64, pub entities: ecs_world_stats_t__bindgen_ty_1, - pub ids: ecs_world_stats_t__bindgen_ty_2, + pub components: ecs_world_stats_t__bindgen_ty_2, pub tables: ecs_world_stats_t__bindgen_ty_3, pub queries: ecs_world_stats_t__bindgen_ty_4, pub commands: ecs_world_stats_t__bindgen_ty_5, pub frame: ecs_world_stats_t__bindgen_ty_6, pub performance: ecs_world_stats_t__bindgen_ty_7, pub memory: ecs_world_stats_t__bindgen_ty_8, - pub rest: ecs_world_stats_t__bindgen_ty_9, - pub http: ecs_world_stats_t__bindgen_ty_10, + pub http: ecs_world_stats_t__bindgen_ty_9, pub last_: i64, #[doc = "Current position in ringbuffer"] pub t: i32, @@ -4797,20 +4759,16 @@ pub struct ecs_world_stats_t__bindgen_ty_1 { #[doc = "< Number of not alive (recyclable) entity ids"] pub not_alive_count: ecs_metric_t, } -#[doc = "Components and ids"] +#[doc = "Component ids"] #[repr(C)] #[derive(Copy, Clone)] pub struct ecs_world_stats_t__bindgen_ty_2 { - #[doc = "< Number of ids (excluding wildcards)"] - pub count: ecs_metric_t, #[doc = "< Number of tag ids (ids without data)"] pub tag_count: ecs_metric_t, #[doc = "< Number of components ids (ids with data)"] pub component_count: ecs_metric_t, #[doc = "< Number of pair ids"] pub pair_count: ecs_metric_t, - #[doc = "< Number of wildcard ids"] - pub wildcard_count: ecs_metric_t, #[doc = "< Number of registered types"] pub type_count: ecs_metric_t, #[doc = "< Number of times id has been created"] @@ -4826,14 +4784,6 @@ pub struct ecs_world_stats_t__bindgen_ty_3 { pub count: ecs_metric_t, #[doc = "< Number of empty tables"] pub empty_count: ecs_metric_t, - #[doc = "< Number of tables with only tags"] - pub tag_only_count: ecs_metric_t, - #[doc = "< Number of tables with only trivial components"] - pub trivial_only_count: ecs_metric_t, - #[doc = "< Number of table cache records"] - pub record_count: ecs_metric_t, - #[doc = "< Number of table storages"] - pub storage_count: ecs_metric_t, #[doc = "< Number of times table has been created"] pub create_count: ecs_metric_t, #[doc = "< Number of times table has been deleted"] @@ -4932,28 +4882,10 @@ pub struct ecs_world_stats_t__bindgen_ty_8 { #[doc = "< Difference between allocs & frees"] pub stack_outstanding_alloc_count: ecs_metric_t, } -#[doc = "REST statistics"] -#[repr(C)] -#[derive(Copy, Clone)] -pub struct ecs_world_stats_t__bindgen_ty_9 { - pub request_count: ecs_metric_t, - pub entity_count: ecs_metric_t, - pub entity_error_count: ecs_metric_t, - pub query_count: ecs_metric_t, - pub query_error_count: ecs_metric_t, - pub query_name_count: ecs_metric_t, - pub query_name_error_count: ecs_metric_t, - pub query_name_from_cache_count: ecs_metric_t, - pub enable_count: ecs_metric_t, - pub enable_error_count: ecs_metric_t, - pub world_stats_count: ecs_metric_t, - pub pipeline_stats_count: ecs_metric_t, - pub stats_error_count: ecs_metric_t, -} #[doc = "HTTP statistics"] #[repr(C)] #[derive(Copy, Clone)] -pub struct ecs_world_stats_t__bindgen_ty_10 { +pub struct ecs_world_stats_t__bindgen_ty_9 { pub request_received_count: ecs_metric_t, pub request_invalid_count: ecs_metric_t, pub request_handled_ok_count: ecs_metric_t, @@ -5168,45 +5100,59 @@ extern "C" { pub static mut FLECS_IDFlecsMetricsID_: ecs_entity_t; } extern "C" { + #[doc = "Tag added to metrics, and used as first element of metric kind pair"] pub static mut EcsMetric: ecs_entity_t; } extern "C" { + #[doc = "Tag added to metrics, and used as first element of metric kind pair"] pub static mut FLECS_IDEcsMetricID_: ecs_entity_t; } extern "C" { + #[doc = "Metric that has monotonically increasing value"] pub static mut EcsCounter: ecs_entity_t; } extern "C" { + #[doc = "Metric that has monotonically increasing value"] pub static mut FLECS_IDEcsCounterID_: ecs_entity_t; } extern "C" { + #[doc = "Counter metric that is auto-incremented by source value"] pub static mut EcsCounterIncrement: ecs_entity_t; } extern "C" { + #[doc = "Counter metric that is auto-incremented by source value"] pub static mut FLECS_IDEcsCounterIncrementID_: ecs_entity_t; } extern "C" { + #[doc = "Counter metric that counts the number of entities with an id"] pub static mut EcsCounterId: ecs_entity_t; } extern "C" { + #[doc = "Counter metric that counts the number of entities with an id"] pub static mut FLECS_IDEcsCounterIdID_: ecs_entity_t; } extern "C" { + #[doc = "Metric that represents current value"] pub static mut EcsGauge: ecs_entity_t; } extern "C" { + #[doc = "Metric that represents current value"] pub static mut FLECS_IDEcsGaugeID_: ecs_entity_t; } extern "C" { + #[doc = "Tag added to metric instances"] pub static mut EcsMetricInstance: ecs_entity_t; } extern "C" { + #[doc = "Tag added to metric instances"] pub static mut FLECS_IDEcsMetricInstanceID_: ecs_entity_t; } extern "C" { + #[doc = "Component with metric instance value"] pub static mut FLECS_IDEcsMetricValueID_: ecs_entity_t; } extern "C" { + #[doc = "Component with entity source of metric instance"] pub static mut FLECS_IDEcsMetricSourceID_: ecs_entity_t; } #[repr(C)] @@ -5248,9 +5194,11 @@ extern "C" { pub fn FlecsMetricsImport(world: *mut ecs_world_t); } extern "C" { + #[doc = "Module id"] pub static mut FLECS_IDFlecsAlertsID_: ecs_entity_t; } extern "C" { + #[doc = "Tag added to alert, and used as first element of alert severity pair"] pub static mut FLECS_IDEcsAlertID_: ecs_entity_t; } extern "C" { @@ -5263,9 +5211,11 @@ extern "C" { pub static mut FLECS_IDEcsAlertTimeoutID_: ecs_entity_t; } extern "C" { + #[doc = "Alert severity tags"] pub static mut EcsAlertInfo: ecs_entity_t; } extern "C" { + #[doc = "Alert severity tags"] pub static mut FLECS_IDEcsAlertInfoID_: ecs_entity_t; } extern "C" { @@ -5436,6 +5386,7 @@ extern "C" { pub fn FlecsCoreDocImport(world: *mut ecs_world_t); } extern "C" { + #[doc = "@defgroup c_addons_doc Doc\n @brief Utilities for documenting entities, components and systems.\n\n \\ingroup c_addons\n @{"] pub static FLECS_IDEcsDocDescriptionID_: ecs_entity_t; } extern "C" { @@ -5726,7 +5677,7 @@ pub struct ecs_iter_to_json_desc_t { pub serialize_table: bool, } extern "C" { - #[doc = "Serialize iterator into JSON string.\n This operation will iterate the contents of the iterator and serialize them\n to JSON. The function acccepts iterators from any source.\n\n @param world The world.\n @param iter The iterator to serialize to JSON.\n @return A JSON string with the serialized iterator data, or NULL if failed."] + #[doc = "Serialize iterator into JSON string.\n This operation will iterate the contents of the iterator and serialize them\n to JSON. The function accepts iterators from any source.\n\n @param world The world.\n @param iter The iterator to serialize to JSON.\n @return A JSON string with the serialized iterator data, or NULL if failed."] pub fn ecs_iter_to_json( world: *const ecs_world_t, iter: *mut ecs_iter_t, @@ -5767,9 +5718,11 @@ extern "C" { ) -> ::std::os::raw::c_int; } extern "C" { + #[doc = "Parent scope for prefixes"] pub static mut EcsUnitPrefixes: ecs_entity_t; } extern "C" { + #[doc = "Parent scope for prefixes"] pub static mut FLECS_IDEcsUnitPrefixesID_: ecs_entity_t; } extern "C" { @@ -5941,9 +5894,11 @@ extern "C" { pub static mut FLECS_IDEcsYobiID_: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_duration Duration\n\n \\ingroup c_addons_units\n @{"] pub static mut EcsDuration: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_duration Duration\n\n \\ingroup c_addons_units\n @{"] pub static mut FLECS_IDEcsDurationID_: ecs_entity_t; } extern "C" { @@ -5995,9 +5950,11 @@ extern "C" { pub static mut FLECS_IDEcsDaysID_: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_time Time\n\n \\ingroup c_addons_units\n @{"] pub static mut EcsTime: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_time Time\n\n \\ingroup c_addons_units\n @{"] pub static mut FLECS_IDEcsTimeID_: ecs_entity_t; } extern "C" { @@ -6007,9 +5964,11 @@ extern "C" { pub static mut FLECS_IDEcsDateID_: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_mass Mass\n\n \\ingroup c_addons_units\n @{"] pub static mut EcsMass: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_mass Mass\n\n \\ingroup c_addons_units\n @{"] pub static mut FLECS_IDEcsMassID_: ecs_entity_t; } extern "C" { @@ -6025,9 +5984,11 @@ extern "C" { pub static mut FLECS_IDEcsKiloGramsID_: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_electric_Current Electric Current\n\n \\ingroup c_addons_units\n @{"] pub static mut EcsElectricCurrent: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_electric_Current Electric Current\n\n \\ingroup c_addons_units\n @{"] pub static mut FLECS_IDEcsElectricCurrentID_: ecs_entity_t; } extern "C" { @@ -6037,9 +5998,11 @@ extern "C" { pub static mut FLECS_IDEcsAmpereID_: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_amount Amount\n\n \\ingroup c_addons_units\n @{"] pub static mut EcsAmount: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_amount Amount\n\n \\ingroup c_addons_units\n @{"] pub static mut FLECS_IDEcsAmountID_: ecs_entity_t; } extern "C" { @@ -6049,9 +6012,11 @@ extern "C" { pub static mut FLECS_IDEcsMoleID_: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_luminous_intensity Luminous Intensity\n\n \\ingroup c_addons_units\n @{"] pub static mut EcsLuminousIntensity: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_luminous_intensity Luminous Intensity\n\n \\ingroup c_addons_units\n @{"] pub static mut FLECS_IDEcsLuminousIntensityID_: ecs_entity_t; } extern "C" { @@ -6061,9 +6026,11 @@ extern "C" { pub static mut FLECS_IDEcsCandelaID_: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_force Force\n\n \\ingroup c_addons_units\n @{"] pub static mut EcsForce: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_force Force\n\n \\ingroup c_addons_units\n @{"] pub static mut FLECS_IDEcsForceID_: ecs_entity_t; } extern "C" { @@ -6073,9 +6040,11 @@ extern "C" { pub static mut FLECS_IDEcsNewtonID_: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_length Length\n\n \\ingroup c_addons_units\n @{"] pub static mut EcsLength: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_length Length\n\n \\ingroup c_addons_units\n @{"] pub static mut FLECS_IDEcsLengthID_: ecs_entity_t; } extern "C" { @@ -6133,9 +6102,11 @@ extern "C" { pub static mut FLECS_IDEcsPixelsID_: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_pressure Pressure\n\n \\ingroup c_addons_units\n @{"] pub static mut EcsPressure: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_pressure Pressure\n\n \\ingroup c_addons_units\n @{"] pub static mut FLECS_IDEcsPressureID_: ecs_entity_t; } extern "C" { @@ -6151,9 +6122,11 @@ extern "C" { pub static mut FLECS_IDEcsBarID_: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_speed Speed\n\n \\ingroup c_addons_units\n @{"] pub static mut EcsSpeed: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_speed Speed\n\n \\ingroup c_addons_units\n @{"] pub static mut FLECS_IDEcsSpeedID_: ecs_entity_t; } extern "C" { @@ -6181,9 +6154,11 @@ extern "C" { pub static mut FLECS_IDEcsMilesPerHourID_: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_temperature Temperature\n\n \\ingroup c_addons_units\n @{"] pub static mut EcsTemperature: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_temperature Temperature\n\n \\ingroup c_addons_units\n @{"] pub static mut FLECS_IDEcsTemperatureID_: ecs_entity_t; } extern "C" { @@ -6205,9 +6180,11 @@ extern "C" { pub static mut FLECS_IDEcsFahrenheitID_: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_data Data\n\n \\ingroup c_addons_units\n @{"] pub static mut EcsData: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_data Data\n\n \\ingroup c_addons_units\n @{"] pub static mut FLECS_IDEcsDataID_: ecs_entity_t; } extern "C" { @@ -6277,9 +6254,11 @@ extern "C" { pub static mut FLECS_IDEcsGibiBytesID_: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_datarate Data Rate\n\n \\ingroup c_addons_units\n @{"] pub static mut EcsDataRate: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_datarate Data Rate\n\n \\ingroup c_addons_units\n @{"] pub static mut FLECS_IDEcsDataRateID_: ecs_entity_t; } extern "C" { @@ -6331,9 +6310,11 @@ extern "C" { pub static mut FLECS_IDEcsGigaBytesPerSecondID_: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_duration Duration\n\n \\ingroup c_addons_units\n @{"] pub static mut EcsAngle: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_duration Duration\n\n \\ingroup c_addons_units\n @{"] pub static mut FLECS_IDEcsAngleID_: ecs_entity_t; } extern "C" { @@ -6349,9 +6330,11 @@ extern "C" { pub static mut FLECS_IDEcsDegreesID_: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_angle Angle\n\n \\ingroup c_addons_units\n @{"] pub static mut EcsFrequency: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_angle Angle\n\n \\ingroup c_addons_units\n @{"] pub static mut FLECS_IDEcsFrequencyID_: ecs_entity_t; } extern "C" { @@ -6379,9 +6362,11 @@ extern "C" { pub static mut FLECS_IDEcsGigaHertzID_: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_uri Uri\n\n \\ingroup c_addons_units\n @{"] pub static mut EcsUri: ecs_entity_t; } extern "C" { + #[doc = "@defgroup c_addons_units_uri Uri\n\n \\ingroup c_addons_units\n @{"] pub static mut FLECS_IDEcsUriID_: ecs_entity_t; } extern "C" { @@ -6403,9 +6388,11 @@ extern "C" { pub static mut FLECS_IDEcsUriFileID_: ecs_entity_t; } extern "C" { + #[doc = "@}"] pub static mut EcsAcceleration: ecs_entity_t; } extern "C" { + #[doc = "@}"] pub static mut FLECS_IDEcsAccelerationID_: ecs_entity_t; } extern "C" { @@ -6448,6 +6435,7 @@ pub type ecs_f32_t = f32; pub type ecs_f64_t = f64; pub type ecs_string_t = *mut ::std::os::raw::c_char; extern "C" { + #[doc = "Meta module component ids"] pub static FLECS_IDEcsMetaTypeID_: ecs_entity_t; } extern "C" { @@ -6493,6 +6481,7 @@ extern "C" { pub static EcsQuantity: ecs_entity_t; } extern "C" { + #[doc = "Primitive type component ids"] pub static FLECS_IDecs_bool_tID_: ecs_entity_t; } extern "C" { @@ -6543,6 +6532,9 @@ extern "C" { extern "C" { pub static FLECS_IDecs_entity_tID_: ecs_entity_t; } +extern "C" { + pub static FLECS_IDecs_id_tID_: ecs_entity_t; +} pub const ecs_type_kind_t_EcsPrimitiveType: ecs_type_kind_t = 0; pub const ecs_type_kind_t_EcsBitmaskType: ecs_type_kind_t = 1; pub const ecs_type_kind_t_EcsEnumType: ecs_type_kind_t = 2; @@ -6552,7 +6544,7 @@ pub const ecs_type_kind_t_EcsVectorType: ecs_type_kind_t = 5; pub const ecs_type_kind_t_EcsOpaqueType: ecs_type_kind_t = 6; pub const ecs_type_kind_t_EcsTypeKindLast: ecs_type_kind_t = 6; #[doc = "Type kinds supported by meta addon"] -pub type ecs_type_kind_t = ::std::os::raw::c_uint; +pub type ecs_type_kind_t = ::std::os::raw::c_int; #[doc = "Component that is automatically added to every type with the right kind."] #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -6580,9 +6572,10 @@ pub const ecs_primitive_kind_t_EcsUPtr: ecs_primitive_kind_t = 14; pub const ecs_primitive_kind_t_EcsIPtr: ecs_primitive_kind_t = 15; pub const ecs_primitive_kind_t_EcsString: ecs_primitive_kind_t = 16; pub const ecs_primitive_kind_t_EcsEntity: ecs_primitive_kind_t = 17; -pub const ecs_primitive_kind_t_EcsPrimitiveKindLast: ecs_primitive_kind_t = 17; +pub const ecs_primitive_kind_t_EcsId: ecs_primitive_kind_t = 18; +pub const ecs_primitive_kind_t_EcsPrimitiveKindLast: ecs_primitive_kind_t = 18; #[doc = "Primitive type kinds supported by meta addon"] -pub type ecs_primitive_kind_t = ::std::os::raw::c_uint; +pub type ecs_primitive_kind_t = ::std::os::raw::c_int; #[doc = "Component added to primitive types"] #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -6759,6 +6752,14 @@ pub struct EcsOpaque { entity: ecs_entity_t, ), >, + #[doc = "Assign (component) id value"] + pub assign_id: ::std::option::Option< + unsafe extern "C" fn( + dst: *mut ::std::os::raw::c_void, + world: *mut ecs_world_t, + id: ecs_id_t, + ), + >, #[doc = "Assign null value"] pub assign_null: ::std::option::Option, #[doc = "Clear collection elements"] @@ -6842,9 +6843,10 @@ pub const ecs_meta_type_op_kind_t_EcsOpUPtr: ecs_meta_type_op_kind_t = 22; pub const ecs_meta_type_op_kind_t_EcsOpIPtr: ecs_meta_type_op_kind_t = 23; pub const ecs_meta_type_op_kind_t_EcsOpString: ecs_meta_type_op_kind_t = 24; pub const ecs_meta_type_op_kind_t_EcsOpEntity: ecs_meta_type_op_kind_t = 25; -pub const ecs_meta_type_op_kind_t_EcsMetaTypeOpKindLast: ecs_meta_type_op_kind_t = 25; +pub const ecs_meta_type_op_kind_t_EcsOpId: ecs_meta_type_op_kind_t = 26; +pub const ecs_meta_type_op_kind_t_EcsMetaTypeOpKindLast: ecs_meta_type_op_kind_t = 26; #[doc = "Serializer utilities"] -pub type ecs_meta_type_op_kind_t = ::std::os::raw::c_uint; +pub type ecs_meta_type_op_kind_t = ::std::os::raw::c_int; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct ecs_meta_type_op_t { @@ -7028,6 +7030,20 @@ extern "C" { value: ecs_entity_t, ) -> ::std::os::raw::c_int; } +extern "C" { + #[doc = "Set field with (component) id value"] + pub fn ecs_meta_set_id( + cursor: *mut ecs_meta_cursor_t, + value: ecs_id_t, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = "Set field with (component) id value"] + pub fn ecs_meta_set_component( + cursor: *mut ecs_meta_cursor_t, + value: ecs_id_t, + ) -> ::std::os::raw::c_int; +} extern "C" { #[doc = "Set field with null value"] pub fn ecs_meta_set_null(cursor: *mut ecs_meta_cursor_t) -> ::std::os::raw::c_int; @@ -7067,6 +7083,10 @@ extern "C" { #[doc = "Get field value as entity.\n This operation does not perform conversions."] pub fn ecs_meta_get_entity(cursor: *const ecs_meta_cursor_t) -> ecs_entity_t; } +extern "C" { + #[doc = "Get field value as (component) id.\n This operation can convert from an entity."] + pub fn ecs_meta_get_id(cursor: *const ecs_meta_cursor_t) -> ecs_id_t; +} extern "C" { #[doc = "Convert pointer of primitive kind to float."] pub fn ecs_meta_ptr_to_float( @@ -7220,7 +7240,7 @@ extern "C" { pub fn FlecsMetaImport(world: *mut ecs_world_t); } extern "C" { - #[doc = "Write an escaped character.\n Write a character to an output string, insert escape character if necessary.\n\n @param out The string to write the character to.\n @param in The input character.\n @param delimiter The delimiiter used (for example '\"')\n @return Pointer to the character after the last one written."] + #[doc = "Write an escaped character.\n Write a character to an output string, insert escape character if necessary.\n\n @param out The string to write the character to.\n @param in The input character.\n @param delimiter The delimiter used (for example '\"')\n @return Pointer to the character after the last one written."] pub fn ecs_chresc( out: *mut ::std::os::raw::c_char, in_: ::std::os::raw::c_char, @@ -7235,7 +7255,7 @@ extern "C" { ) -> *const ::std::os::raw::c_char; } extern "C" { - #[doc = "Write an escaped string.\n Write an input string to an output string, escape characters where necessary.\n To determine the size of the output string, call the operation with a NULL\n argument for 'out', and use the returned size to allocate a string that is\n large enough.\n\n @param out Pointer to output string (msut be).\n @param size Maximum number of characters written to output.\n @param delimiter The delimiter used (for example '\"').\n @param in The input string.\n @return The number of characters that (would) have been written."] + #[doc = "Write an escaped string.\n Write an input string to an output string, escape characters where necessary.\n To determine the size of the output string, call the operation with a NULL\n argument for 'out', and use the returned size to allocate a string that is\n large enough.\n\n @param out Pointer to output string (must be).\n @param size Maximum number of characters written to output.\n @param delimiter The delimiter used (for example '\"').\n @param in The input string.\n @return The number of characters that (would) have been written."] pub fn ecs_stresc( out: *mut ::std::os::raw::c_char, size: ecs_size_t, @@ -7504,7 +7524,7 @@ extern "C" { -> *const ::std::os::raw::c_char; } extern "C" { - #[doc = "Test if variable is an entity.\n Internally the rule engine has entity variables and table variables. When\n iterating through rule variables (by using ecs_rule_variable_count) only\n the values for entity variables are accessible. This operation enables an\n appliction to check if a variable is an entity variable.\n\n @param rule The rule.\n @param var_id The variable id."] + #[doc = "Test if variable is an entity.\n Internally the rule engine has entity variables and table variables. When\n iterating through rule variables (by using ecs_rule_variable_count) only\n the values for entity variables are accessible. This operation enables an\n application to check if a variable is an entity variable.\n\n @param rule The rule.\n @param var_id The variable id."] pub fn ecs_rule_var_is_entity(rule: *const ecs_rule_t, var_id: i32) -> bool; } extern "C" { @@ -7524,14 +7544,14 @@ extern "C" { pub fn ecs_rule_str(rule: *const ecs_rule_t) -> *mut ::std::os::raw::c_char; } extern "C" { - #[doc = "Convert rule to string with profile.\n To use this you must set the EcsIterProfile flag on an iterator before\n starting uteration:\n it.flags |= EcsIterProfile\n\n @param rule The rule.\n @return The string"] + #[doc = "Convert rule to string with profile.\n To use this you must set the EcsIterProfile flag on an iterator before\n starting iteration:\n it.flags |= EcsIterProfile\n\n @param rule The rule.\n @return The string"] pub fn ecs_rule_str_w_profile( rule: *const ecs_rule_t, it: *const ecs_iter_t, ) -> *mut ::std::os::raw::c_char; } extern "C" { - #[doc = "Populate variables from key-value string.\n Convenience function to set rule variables from a key-value string separated\n by comma's. The string must have the followig format:\n var_a: value, var_b: value\n\n The key-value list may optionally be enclosed in parenthesis.\n\n @param rule The rule.\n @param it The iterator for which to set the variables.\n @param expr The key-value expression."] + #[doc = "Populate variables from key-value string.\n Convenience function to set rule variables from a key-value string separated\n by comma's. The string must have the following format:\n var_a: value, var_b: value\n\n The key-value list may optionally be enclosed in parenthesis.\n\n @param rule The rule.\n @param it The iterator for which to set the variables.\n @param expr The key-value expression."] pub fn ecs_rule_parse_vars( rule: *mut ecs_rule_t, it: *mut ecs_iter_t, @@ -7602,13 +7622,14 @@ extern "C" { ) -> *const ::std::os::raw::c_char; } extern "C" { - #[doc = "Parse term in expression.\n This operation parses a single term in an expression and returns a pointer\n to the next term expression.\n\n If the returned pointer points to the 0-terminator, the expression is fully\n parsed. The function would typically be called in a while loop:\n\n const char *ptr = expr;\n while (ptr\\[0\\] && (ptr = ecs_parse_term(world, name, expr, ptr, &term))) { }\n\n The operation does not attempt to find entity ids from the names in the\n expression. Use the ecs_term_resolve_ids function to resolve the identifiers\n in the parsed term.\n\n The returned term will in most cases contain allocated resources, which\n should freed (or used) by the application. To free the resources for a term,\n use the ecs_term_free function.\n\n The parser accepts expressions in the legacy string format.\n\n @param world The world.\n @param name The name of the expression (optional, improves error logs)\n @param expr The expression to parse (optional, improves error logs)\n @param ptr The pointer to the current term (must be in expr).\n @param term_out Out parameter for the term.\n @return pointer to next term if successful, NULL if failed."] + #[doc = "Parse term in expression.\n This operation parses a single term in an expression and returns a pointer\n to the next term expression.\n\n If the returned pointer points to the 0-terminator, the expression is fully\n parsed. The function would typically be called in a while loop:\n\n const char *ptr = expr;\n while (ptr\\[0\\] && (ptr = ecs_parse_term(world, name, expr, ptr, &term))) { }\n\n The operation does not attempt to find entity ids from the names in the\n expression. Use the ecs_term_resolve_ids function to resolve the identifiers\n in the parsed term.\n\n The returned term will in most cases contain allocated resources, which\n should freed (or used) by the application. To free the resources for a term,\n use the ecs_term_free function.\n\n The parser accepts expressions in the legacy string format.\n\n @param world The world.\n @param name The name of the expression (optional, improves error logs)\n @param expr The expression to parse (optional, improves error logs)\n @param ptr The pointer to the current term (must be in expr).\n @param term_out Out parameter for the term.\n @param extra_args Out array for extra args, must be of size ECS_PARSER_MAX_ARGS.\n @return pointer to next term if successful, NULL if failed."] pub fn ecs_parse_term( world: *const ecs_world_t, name: *const ::std::os::raw::c_char, expr: *const ::std::os::raw::c_char, ptr: *const ::std::os::raw::c_char, term_out: *mut ecs_term_t, + extra_args: *mut ecs_term_id_t, ) -> *mut ::std::os::raw::c_char; } extern "C" { @@ -7736,7 +7757,6 @@ extern "C" { type_: ecs_entity_t, ) -> *const ecs_member_t; } -pub type __builtin_va_list = *mut ::std::os::raw::c_char; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct ecs_event_id_record_t { diff --git a/readme.md b/readme.md index ddff7c4..3c32891 100644 --- a/readme.md +++ b/readme.md @@ -7,7 +7,7 @@ A Rust binding for the Flecs ECS library: -Wraps native Flecs v3.2.8 +Wraps native Flecs v3.2.10 ## A Simple Example