/** * @file id.c * @brief Id utilities. */ #include "private_api.h" #ifdef FLECS_QUERY_DSL #include "addons/query_dsl/query_dsl.h" #endif bool ecs_id_match( ecs_id_t id, ecs_id_t pattern) { if (id == pattern) { return true; } if (ECS_HAS_ID_FLAG(pattern, PAIR)) { if (!ECS_HAS_ID_FLAG(id, PAIR)) { return false; } ecs_entity_t id_first = ECS_PAIR_FIRST(id); ecs_entity_t id_second = ECS_PAIR_SECOND(id); ecs_entity_t pattern_first = ECS_PAIR_FIRST(pattern); ecs_entity_t pattern_second = ECS_PAIR_SECOND(pattern); ecs_check(id_first != 0, ECS_INVALID_PARAMETER, "first element of pair cannot be 0"); ecs_check(id_second != 0, ECS_INVALID_PARAMETER, "second element of pair cannot be 0"); ecs_check(pattern_first != 0, ECS_INVALID_PARAMETER, "first element of pair cannot be 0"); ecs_check(pattern_second != 0, ECS_INVALID_PARAMETER, "second element of pair cannot be 0"); if (pattern_first == EcsWildcard) { if (pattern_second == EcsWildcard || pattern_second == id_second) { return true; } } else if (pattern_first == EcsFlag) { /* Used for internals, helps to keep track of which ids are used in * pairs that have additional flags (like OVERRIDE and TOGGLE) */ if (ECS_HAS_ID_FLAG(id, PAIR) && !ECS_IS_PAIR(id)) { if (ECS_PAIR_FIRST(id) == pattern_second) { return true; } if (ECS_PAIR_SECOND(id) == pattern_second) { return true; } } } else if (pattern_second == EcsWildcard) { if (pattern_first == id_first) { return true; } } } else { if ((id & ECS_ID_FLAGS_MASK) != (pattern & ECS_ID_FLAGS_MASK)) { return false; } if ((ECS_COMPONENT_MASK & pattern) == EcsWildcard) { return true; } } error: return false; } bool ecs_id_is_pair( ecs_id_t id) { return ECS_HAS_ID_FLAG(id, PAIR); } bool ecs_id_is_wildcard( ecs_id_t id) { if ((id == EcsWildcard) || (id == EcsAny)) { return true; } bool is_pair = ECS_IS_PAIR(id); if (!is_pair) { return false; } ecs_entity_t first = ECS_PAIR_FIRST(id); ecs_entity_t second = ECS_PAIR_SECOND(id); return (first == EcsWildcard) || (second == EcsWildcard) || (first == EcsAny) || (second == EcsAny); } bool ecs_id_is_any( ecs_id_t id) { if (id == EcsAny) { return true; } bool is_pair = ECS_IS_PAIR(id); if (!is_pair) { return false; } ecs_entity_t first = ECS_PAIR_FIRST(id); ecs_entity_t second = ECS_PAIR_SECOND(id); return (first == EcsAny) || (second == EcsAny); } const char* flecs_id_invalid_reason( const ecs_world_t *world, ecs_id_t id) { if (!id) { return "components cannot be 0 (is the component registered?)"; } if (ecs_id_is_wildcard(id)) { return "cannot add wildcards"; } if (ECS_HAS_ID_FLAG(id, PAIR)) { if (!ECS_PAIR_FIRST(id) && !ECS_PAIR_SECOND(id)) { return "invalid pair: both elements are 0"; } if (!ECS_PAIR_FIRST(id)) { return "invalid pair: first element is 0 (is the relationship registered?)"; } if (!ECS_PAIR_SECOND(id)) { return "invalid pair: second element is 0"; } } else if (id & ECS_ID_FLAGS_MASK) { if (!ecs_is_valid(world, id & ECS_COMPONENT_MASK)) { ecs_abort(ECS_INTERNAL_ERROR, NULL); } } return NULL; } bool ecs_id_is_valid( const ecs_world_t *world, ecs_id_t id) { return flecs_id_invalid_reason(world, id) == NULL; } ecs_flags32_t ecs_id_get_flags( const ecs_world_t *world, ecs_id_t id) { ecs_component_record_t *cr = flecs_components_get(world, id); if (cr) { return cr->flags; } else { return 0; } } ecs_id_t ecs_id_from_str( const ecs_world_t *world, const char *expr) { #ifdef FLECS_QUERY_DSL ecs_id_t result; /* Temporarily disable parser logging */ int prev_level = ecs_log_set_level(-3); if (!flecs_id_parse(world, NULL, expr, &result)) { /* Invalid expression */ ecs_log_set_level(prev_level); return 0; } ecs_log_set_level(prev_level); return result; #else (void)world; (void)expr; ecs_abort(ECS_UNSUPPORTED, "ecs_id_from_str requires FLECS_QUERY_DSL addon"); #endif } const char* ecs_id_flag_str( ecs_entity_t entity) { if (ECS_HAS_ID_FLAG(entity, PAIR)) { return "PAIR"; } else if (ECS_HAS_ID_FLAG(entity, TOGGLE)) { return "TOGGLE"; } else if (ECS_HAS_ID_FLAG(entity, AUTO_OVERRIDE)) { return "AUTO_OVERRIDE"; } else { return "UNKNOWN"; } } void ecs_id_str_buf( const ecs_world_t *world, ecs_id_t id, ecs_strbuf_t *buf) { ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL); world = ecs_get_world(world); if (ECS_HAS_ID_FLAG(id, TOGGLE)) { ecs_strbuf_appendstr(buf, ecs_id_flag_str(ECS_TOGGLE)); ecs_strbuf_appendch(buf, '|'); } if (ECS_HAS_ID_FLAG(id, AUTO_OVERRIDE)) { ecs_strbuf_appendstr(buf, ecs_id_flag_str(ECS_AUTO_OVERRIDE)); ecs_strbuf_appendch(buf, '|'); } if (ECS_HAS_ID_FLAG(id, PAIR)) { ecs_entity_t rel = ECS_PAIR_FIRST(id); ecs_entity_t obj = ECS_PAIR_SECOND(id); ecs_entity_t e; if ((e = ecs_get_alive(world, rel))) { rel = e; } if ((e = ecs_get_alive(world, obj))) { obj = e; } ecs_strbuf_appendch(buf, '('); ecs_get_path_w_sep_buf(world, 0, rel, NULL, NULL, buf, false); ecs_strbuf_appendch(buf, ','); ecs_get_path_w_sep_buf(world, 0, obj, NULL, NULL, buf, false); ecs_strbuf_appendch(buf, ')'); } else { ecs_entity_t e = id & ECS_COMPONENT_MASK; ecs_get_path_w_sep_buf(world, 0, e, NULL, NULL, buf, false); } error: return; } char* ecs_id_str( const ecs_world_t *world, ecs_id_t id) { ecs_strbuf_t buf = ECS_STRBUF_INIT; ecs_id_str_buf(world, id, &buf); return ecs_strbuf_get(&buf); } ecs_id_t ecs_make_pair( ecs_entity_t relationship, ecs_entity_t target) { ecs_assert(!ECS_IS_PAIR(relationship) && !ECS_IS_PAIR(target), ECS_INVALID_PARAMETER, "cannot create nested pairs"); return ecs_pair(relationship, target); } bool ecs_id_is_tag( const ecs_world_t *world, ecs_id_t id) { if (ecs_id_is_wildcard(id)) { /* If id is a wildcard, we can't tell if it's a tag or not, except * when the relationship part of a pair has the Tag property */ if (ECS_HAS_ID_FLAG(id, PAIR)) { if (ECS_PAIR_FIRST(id) != EcsWildcard) { ecs_entity_t rel = ecs_pair_first(world, id); if (ecs_is_valid(world, rel)) { if (ecs_has_id(world, rel, EcsPairIsTag)) { return true; } } else { /* During bootstrap it's possible that not all ids are valid * yet. Using ecs_get_typeid will ensure correct values are * returned for only those components initialized during * bootstrap, while still asserting if another invalid id * is provided. */ if (ecs_get_typeid(world, id) == 0) { return true; } } } else { /* If relationship is wildcard id is not guaranteed to be a tag */ } } } else { if (ecs_get_typeid(world, id) == 0) { return true; } } return false; } ecs_entity_t ecs_get_typeid( const ecs_world_t *world, ecs_id_t id) { 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: return 0; } bool ecs_id_in_use( const ecs_world_t *world, ecs_id_t id) { ecs_component_record_t *cr = flecs_components_get(world, id); if (!cr) { return false; } return (flecs_table_cache_count(&cr->cache) != 0); }