From 9b3f95083742250c6684ea987f0f30ead0ad3caf Mon Sep 17 00:00:00 2001 From: Sander Mertens Date: Wed, 16 Oct 2024 16:41:41 -0700 Subject: [PATCH] Implement world local component ids --- distr/flecs.c | 180 ++++---- distr/flecs.h | 413 +++++++----------- .../cpp/systems/multiple_queries/project.json | 2 +- include/flecs/addons/cpp/component.hpp | 320 +++++--------- include/flecs/addons/cpp/impl/world.hpp | 6 +- .../addons/cpp/mixins/entity/builder.hpp | 10 +- .../flecs/addons/cpp/mixins/entity/impl.hpp | 3 +- include/flecs/addons/cpp/mixins/meta/impl.hpp | 8 +- .../flecs/addons/cpp/mixins/module/impl.hpp | 4 +- .../flecs/addons/cpp/mixins/query/impl.hpp | 2 +- include/flecs/addons/cpp/utils/enum.hpp | 20 +- include/flecs/addons/flecs_cpp.h | 20 +- include/flecs/private/api_support.h | 20 + src/addons/flecs_cpp.c | 110 +---- src/addons/meta/meta.c | 1 + src/entity.c | 3 +- src/private_types.h | 3 + src/world.c | 63 +++ test/cpp/project.json | 6 +- test/cpp/src/Entity.cpp | 1 + test/cpp/src/Enum.cpp | 26 ++ test/cpp/src/ImplicitComponents.cpp | 38 +- test/cpp/src/QueryBuilder.cpp | 4 +- test/cpp/src/Singleton.cpp | 8 +- test/cpp/src/World.cpp | 154 ++++--- test/cpp/src/main.cpp | 24 +- 26 files changed, 673 insertions(+), 776 deletions(-) diff --git a/distr/flecs.c b/distr/flecs.c index 5a7da33d7b..8e2b3fffbc 100644 --- a/distr/flecs.c +++ b/distr/flecs.c @@ -991,6 +991,9 @@ struct ecs_world_t { ecs_stage_t **stages; /* Stages */ int32_t stage_count; /* Number of stages */ + /* -- Component ids -- */ + ecs_vec_t component_ids; /* World local component ids */ + /* Internal callback for command inspection. Only one callback can be set at * a time. After assignment the action will become active at the start of * the next frame, set by ecs_frame_begin, and will be reset by @@ -7371,7 +7374,8 @@ int flecs_traverse_add( const char *sym = ecs_get_symbol(world, result); if (sym) { ecs_assert(!ecs_os_strcmp(desc->symbol, sym), - ECS_INCONSISTENT_NAME, desc->symbol); + ECS_INCONSISTENT_NAME, "%s (provided) vs. %s (existing)", + desc->symbol, sym); } else { ecs_set_symbol(world, result, desc->symbol); } @@ -19390,6 +19394,7 @@ ecs_world_t *ecs_mini(void) { flecs_name_index_init(&world->aliases, a); flecs_name_index_init(&world->symbols, a); ecs_vec_init_t(a, &world->fini_actions, ecs_action_elem_t, 0); + ecs_vec_init_t(a, &world->component_ids, ecs_id_t, 0); world->info.time_scale = 1.0; if (ecs_os_has_time()) { @@ -20065,6 +20070,7 @@ int ecs_fini( flecs_name_index_fini(&world->aliases); flecs_name_index_fini(&world->symbols); ecs_set_stage_count(world, 0); + ecs_vec_fini_t(&world->allocator, &world->component_ids, ecs_id_t); ecs_log_pop_1(); flecs_world_allocators_fini(world); @@ -20833,6 +20839,67 @@ ecs_flags32_t ecs_world_get_flags( } } +static int32_t flecs_component_ids_last_index = 0; + +int32_t flecs_component_ids_index_get(void) { + if (ecs_os_api.ainc_) { + return ecs_os_ainc(&flecs_component_ids_last_index); + } else { + return ++ flecs_component_ids_last_index; + } +} + +ecs_entity_t flecs_component_ids_get( + const ecs_world_t *stage_world, + int32_t index) +{ + ecs_world_t *world = + ECS_CONST_CAST(ecs_world_t*, ecs_get_world(stage_world)); + flecs_poly_assert(world, ecs_world_t); + + if (index >= ecs_vec_count(&world->component_ids)) { + return 0; + } + + return ecs_vec_get_t( + &world->component_ids, ecs_entity_t, index)[0]; +} + +ecs_entity_t flecs_component_ids_get_alive( + const ecs_world_t *stage_world, + int32_t index) +{ + ecs_world_t *world = + ECS_CONST_CAST(ecs_world_t*, ecs_get_world(stage_world)); + flecs_poly_assert(world, ecs_world_t); + + if (index >= ecs_vec_count(&world->component_ids)) { + return 0; + } + + ecs_entity_t result = ecs_vec_get_t( + &world->component_ids, ecs_entity_t, index)[0]; + if (!flecs_entities_is_alive(world, result)) { + return 0; + } + + return result; +} + +void flecs_component_ids_set( + ecs_world_t *stage_world, + int32_t index, + ecs_entity_t component) +{ + ecs_world_t *world = + ECS_CONST_CAST(ecs_world_t*, ecs_get_world(stage_world)); + flecs_poly_assert(world, ecs_world_t); + + ecs_vec_set_min_count_zeromem_t( + &world->allocator, &world->component_ids, ecs_entity_t, index + 1); + ecs_vec_get_t(&world->component_ids, ecs_entity_t, index)[0] = component; +} + /** * @file addons/alerts.c * @brief Alerts addon. @@ -22272,94 +22339,31 @@ const char* ecs_cpp_trim_module( return type_name; } -// Validate registered component -void ecs_cpp_component_validate( +ecs_entity_t ecs_cpp_component_find( ecs_world_t *world, ecs_entity_t id, const char *name, const char *symbol, size_t size, size_t alignment, - bool implicit_name) -{ - /* If entity has a name check if it matches */ - if (ecs_is_valid(world, id) && ecs_get_name(world, id) != NULL) { - if (!implicit_name && id >= EcsFirstUserComponentId) { -#ifndef FLECS_NDEBUG - char *path = ecs_get_path_w_sep( - world, 0, id, "::", NULL); - if (ecs_os_strcmp(path, name)) { - ecs_abort(ECS_INCONSISTENT_NAME, - "component '%s' already registered with name '%s'", - name, path); - } - ecs_os_free(path); -#endif - } - - if (symbol) { - const char *existing_symbol = ecs_get_symbol(world, id); - if (existing_symbol) { - if (ecs_os_strcmp(symbol, existing_symbol)) { - ecs_abort(ECS_INCONSISTENT_NAME, - "component '%s' with symbol '%s' already registered with symbol '%s'", - name, symbol, existing_symbol); - } - } - } - } else { - /* Ensure that the entity id valid */ - if (!ecs_is_alive(world, id)) { - ecs_make_alive(world, id); - } - - /* Register name with entity, so that when the entity is created the - * correct id will be resolved from the name. Only do this when the - * entity is empty. */ - ecs_add_path_w_sep(world, id, 0, name, "::", "::"); - } - - /* If a component was already registered with this id but with a - * different size, the ecs_component_init function will fail. */ - - /* We need to explicitly call ecs_component_init here again. Even though - * the component was already registered, it may have been registered - * with a different world. This ensures that the component is registered - * with the same id for the current world. - * If the component was registered already, nothing will change. */ - ecs_entity_t ent = ecs_component_init(world, &(ecs_component_desc_t){ - .entity = id, - .type.size = flecs_uto(int32_t, size), - .type.alignment = flecs_uto(int32_t, alignment) - }); - (void)ent; - ecs_assert(ent == id, ECS_INTERNAL_ERROR, NULL); -} - -ecs_entity_t ecs_cpp_component_register( - ecs_world_t *world, - ecs_entity_t id, - const char *name, - const char *symbol, - ecs_size_t size, - ecs_size_t alignment, bool implicit_name, bool *existing_out) { (void)size; (void)alignment; + ecs_assert(existing_out != NULL, ECS_INTERNAL_ERROR, NULL); + /* If the component is not yet registered, ensure no other component * or entity has been registered with this name. Ensure component is * looked up from root. */ - bool existing = false; ecs_entity_t prev_scope = ecs_set_scope(world, 0); ecs_entity_t ent; if (id) { ent = id; } else { ent = ecs_lookup_path_w_sep(world, 0, name, "::", "::", false); - existing = ent != 0 && ecs_has(world, ent, EcsComponent); + *existing_out = ent != 0 && ecs_has(world, ent, EcsComponent); } ecs_set_scope(world, prev_scope); @@ -22387,16 +22391,23 @@ ecs_entity_t ecs_cpp_component_register( * to alias the type, vs. accidentally registering an unrelated * type with the same size/alignment. */ char *type_path = ecs_get_path(world, ent); - if (ecs_os_strcmp(type_path, symbol) || - component->size != size || - component->alignment != alignment) - { + if (ecs_os_strcmp(type_path, symbol)) { ecs_err( "component with name '%s' is already registered for"\ " type '%s' (trying to register for type '%s')", name, sym, symbol); ecs_abort(ECS_NAME_IN_USE, NULL); } + + if (flecs_itosize(component->size) != size || + flecs_itosize(component->alignment) != alignment) + { + ecs_err( + "component with name '%s' is already registered with"\ + " mismatching size/alignment)", name); + ecs_abort(ECS_INVALID_COMPONENT_SIZE, NULL); + } + ecs_os_free(type_path); } else if (!sym) { ecs_set_symbol(world, ent, symbol); @@ -22407,17 +22418,14 @@ ecs_entity_t ecs_cpp_component_register( * registered under a different name. */ } else if (!implicit_name) { ent = ecs_lookup_symbol(world, symbol, false, false); - ecs_assert(ent == 0 || (ent == id), ECS_INCONSISTENT_COMPONENT_ID, symbol); - } - - if (existing_out) { - *existing_out = existing; + ecs_assert(ent == 0 || (ent == id), + ECS_INCONSISTENT_COMPONENT_ID, symbol); } return ent; } -ecs_entity_t ecs_cpp_component_register_explicit( +ecs_entity_t ecs_cpp_component_register( ecs_world_t *world, ecs_entity_t s_id, ecs_entity_t id, @@ -22430,7 +22438,8 @@ ecs_entity_t ecs_cpp_component_register_explicit( bool *existing_out) { char *existing_name = NULL; - if (existing_out) *existing_out = false; + + ecs_assert(existing_out != NULL, ECS_INTERNAL_ERROR, NULL); // If an explicit id is provided, it is possible that the symbol and // name differ from the actual type, as the application may alias @@ -22443,7 +22452,7 @@ ecs_entity_t ecs_cpp_component_register_explicit( if (id) { existing_name = ecs_get_path_w_sep(world, 0, id, "::", "::"); name = existing_name; - if (existing_out) *existing_out = true; + *existing_out = true; } else { // If type is not yet known, derive from type name name = ecs_cpp_trim_module(world, type_name); @@ -22556,16 +22565,6 @@ ecs_entity_t ecs_cpp_enum_constant_register( return id; } -static int32_t flecs_reset_count = 0; - -int32_t ecs_cpp_reset_count_get(void) { - return flecs_reset_count; -} - -int32_t ecs_cpp_reset_count_inc(void) { - return ++flecs_reset_count; -} - #ifdef FLECS_META const ecs_member_t* ecs_cpp_last_member( const ecs_world_t *world, @@ -51685,6 +51684,7 @@ void flecs_set_custom_type(ecs_iter_t *it) { if (!comp || !comp->size || !comp->alignment) { ecs_err("custom type '%s' has no size/alignment, register as component first", ecs_get_name(world, e)); + flecs_dump_backtrace(stdout); continue; } diff --git a/distr/flecs.h b/distr/flecs.h index 407e7ef02b..572c77d3a8 100644 --- a/distr/flecs.h +++ b/distr/flecs.h @@ -3992,8 +3992,28 @@ FLECS_API int32_t flecs_poly_refcount( ecs_poly_t *poly); +FLECS_API +int32_t flecs_component_ids_index_get(void); + +FLECS_API +ecs_entity_t flecs_component_ids_get( + const ecs_world_t *world, + int32_t index); + +FLECS_API +ecs_entity_t flecs_component_ids_get_alive( + const ecs_world_t *stage_world, + int32_t index); + +FLECS_API +void flecs_component_ids_set( + ecs_world_t *world, + int32_t index, + ecs_entity_t id); + #define flecs_poly_claim(poly) \ flecs_poly_claim_(ECS_CONST_CAST(void*, reinterpret_cast(poly))) + #define flecs_poly_release(poly) \ flecs_poly_release_(ECS_CONST_CAST(void*, reinterpret_cast(poly))) @@ -16992,28 +17012,18 @@ const char* ecs_cpp_trim_module( const char *type_name); FLECS_API -void ecs_cpp_component_validate( +ecs_entity_t ecs_cpp_component_find( ecs_world_t *world, ecs_entity_t id, const char *name, const char *symbol, size_t size, size_t alignment, - bool implicit_name); - -FLECS_API -ecs_entity_t ecs_cpp_component_register( - ecs_world_t *world, - ecs_entity_t id, - const char *name, - const char *symbol, - ecs_size_t size, - ecs_size_t alignment, bool implicit_name, bool *existing_out); FLECS_API -ecs_entity_t ecs_cpp_component_register_explicit( +ecs_entity_t ecs_cpp_component_register( ecs_world_t *world, ecs_entity_t s_id, ecs_entity_t id, @@ -17038,12 +17048,6 @@ ecs_entity_t ecs_cpp_enum_constant_register( const char *name, int value); -FLECS_API -int32_t ecs_cpp_reset_count_get(void); - -FLECS_API -int32_t ecs_cpp_reset_count_inc(void); - #ifdef FLECS_META FLECS_API const ecs_member_t* ecs_cpp_last_member( @@ -17868,7 +17872,7 @@ static const char* enum_constant_to_name() { /** Enumeration constant data */ template struct enum_constant_data { - flecs::entity_t id; + int32_t index; // Global index used to obtain world local entity id T offset; }; @@ -17979,7 +17983,6 @@ struct enum_data_impl { }; public: - flecs::entity_t id; int min; int max; bool has_contiguous; @@ -18031,8 +18034,17 @@ struct enum_type { "Signed integer enums causes integer overflow when recording offset from high positive to" " low negative. Consider using unsigned integers as underlying type."); enum_type::data.constants[enum_type::data.max].offset = v - last_value; - enum_type::data.constants[enum_type::data.max].id = ecs_cpp_enum_constant_register( - world, enum_type::data.id, 0, name, static_cast(v)); + if (!enum_type::data.constants[enum_type::data.max].index) { + enum_type::data.constants[enum_type::data.max].index = + flecs_component_ids_index_get(); + } + + flecs::entity_t constant = ecs_cpp_enum_constant_register( + world, type::id(world), 0, name, static_cast(v)); + flecs_component_ids_set(world, + enum_type::data.constants[enum_type::data.max].index, + constant); + return v; } }; @@ -18065,7 +18077,7 @@ struct enum_type { ecs_log_push(); ecs_cpp_enum_init(world, id); - data.id = id; + // data.id = id; // Generate reflection data enum_reflection::template each_enum< static_cast(enum_last::value) >(world); @@ -18107,7 +18119,7 @@ struct enum_data { if (index < 0) { return false; } - return impl_.constants[index].id != 0; + return impl_.constants[index].index != 0; } /** @@ -25127,7 +25139,7 @@ struct entity_builder : entity_view { */ template const Self& enable(flecs::id_t second) const { - return this->enable(_::type::id(), second); + return this->enable(_::type::id(world_), second); } /** Enable a pair. @@ -25138,7 +25150,7 @@ struct entity_builder : entity_view { */ template const Self& enable() const { - return this->enable(_::type::id()); + return this->enable(_::type::id(world_)); } /** Disable an id. @@ -25161,7 +25173,7 @@ struct entity_builder : entity_view { */ template const Self& disable() const { - return this->disable(_::type::id()); + return this->disable(_::type::id(world_)); } /** Disable a pair. @@ -25182,7 +25194,7 @@ struct entity_builder : entity_view { */ template const Self& disable(flecs::id_t second) const { - return this->disable(_::type::id(), second); + return this->disable(_::type::id(world_), second); } /** Disable a pair. @@ -25193,7 +25205,7 @@ struct entity_builder : entity_view { */ template const Self& disable() const { - return this->disable(_::type::id()); + return this->disable(_::type::id(world_)); } const Self& set_ptr(entity_t comp, size_t size, const void *ptr) const { @@ -27217,16 +27229,6 @@ void register_lifecycle_actions( } } -// Class that manages component ids across worlds & binaries. -// The type class stores the component id for a C++ type in a static global -// variable that is shared between worlds. Whenever a component is used this -// class will check if it already has been registered (has the global id been -// set), and if not, register the component with the world. -// -// If the id has been set, the class will ensure it is known by the world. If it -// is not known the component has been registered by another world and will be -// registered with the world using the same id. If the id does exist, the class -// will register it as a component, and verify whether the input is consistent. template struct type_impl { static_assert(is_pointer::value == false, @@ -27234,27 +27236,9 @@ struct type_impl { // Initialize component identifier static void init( - entity_t entity, bool allow_tag = true) { - if (s_reset_count != ecs_cpp_reset_count_get()) { - reset(); - } - - // If an identifier was already set, check for consistency - if (s_id) { - ecs_assert(s_id == entity, ECS_INCONSISTENT_COMPONENT_ID, - type_name()); - ecs_assert(allow_tag == s_allow_tag, ECS_INVALID_PARAMETER, NULL); - - // Component was already registered and data is consistent with new - // identifier, so nothing else to be done. - return; - } - - // Component wasn't registered yet, set the values. Register component - // name as the fully qualified flecs path. - s_id = entity; + s_index = flecs_component_ids_index_get(); s_allow_tag = allow_tag; s_size = sizeof(T); s_alignment = alignof(T); @@ -27262,161 +27246,158 @@ struct type_impl { s_size = 0; s_alignment = 0; } + } - s_reset_count = ecs_cpp_reset_count_get(); + static void init_builtin( + flecs::world_t *world, + flecs::entity_t id, + bool allow_tag = true) + { + init(allow_tag); + flecs_component_ids_set(world, s_index, id); } - // Obtain a component identifier for explicit component registration. - static entity_t id_explicit(world_t *world = nullptr, + // Register component id. + static entity_t register_id(world_t *world, const char *name = nullptr, bool allow_tag = true, flecs::id_t id = 0, - bool is_component = true, bool *existing = nullptr) + bool is_component = false, bool implicit_name = true, const char *n = nullptr, + flecs::entity_t module = 0) { - if (!s_id) { - // If no world was provided the component cannot be registered - ecs_assert(world != nullptr, ECS_COMPONENT_NOT_REGISTERED, name); - } else { - ecs_assert(!id || s_id == id, ECS_INCONSISTENT_COMPONENT_ID, NULL); + if (!s_index) { + // This is the first time (in this binary image) that this type is + // being used. Generate a static index that will identify the type + // across worlds. + init(allow_tag); + ecs_assert(s_index != 0, ECS_INTERNAL_ERROR, NULL); } - // 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. - if (!s_id || (world && !ecs_is_alive(world, s_id))) { - init(s_id ? s_id : id, allow_tag); - - ecs_assert(!id || s_id == id, ECS_INTERNAL_ERROR, NULL); + flecs::entity_t c = flecs_component_ids_get(world, s_index); + + if (!c || !ecs_is_alive(world, c)) { + // When a component is implicitly registered, ensure that it's not + // registered in the current scope of the application/that "with" + // components get added to the component entity. + ecs_entity_t prev_scope = ecs_set_scope(world, module); + ecs_entity_t prev_with = ecs_set_with(world, 0); + + // At this point it is possible that the type was already registered + // with the world, just not for this binary. The registration code + // uses the type symbol to check if it was already registered. Note + // that the symbol is separate from the typename, as an application + // can override a component name when registering a type. + bool existing = false; + c = ecs_cpp_component_find( + world, id, n, symbol_name(), size(), alignment(), + implicit_name, &existing); const char *symbol = nullptr; - if (id) { - symbol = ecs_get_symbol(world, id); + if (c) { + symbol = ecs_get_symbol(world, c); } if (!symbol) { symbol = symbol_name(); } - entity_t entity = ecs_cpp_component_register_explicit( - world, s_id, id, name, type_name(), symbol, - s_size, s_alignment, is_component, existing); - - s_id = entity; + c = ecs_cpp_component_register(world, c, c, name, type_name(), + symbol, size(), alignment(), is_component, &existing); - // If component is enum type, register constants - #if FLECS_CPP_ENUM_REFLECTION_SUPPORT - _::init_enum(world, entity); - #endif - } - - // By now the identifier must be valid and known with the world. - ecs_assert(s_id != 0 && ecs_exists(world, s_id), - ECS_INTERNAL_ERROR, NULL); - - return s_id; - } - - // Obtain a component identifier for implicit component registration. This - // is almost the same as id_explicit, except that this operation - // automatically registers lifecycle callbacks. - // Additionally, implicit registration temporarily resets the scope & with - // state of the world, so that the component is not implicitly created with - // the scope/with of the code it happens to be first used by. - static id_t id(world_t *world = nullptr, const char *name = nullptr, - bool allow_tag = true) - { - // If no id has been registered yet, do it now. -#ifndef FLECS_CPP_NO_AUTO_REGISTRATION - if (!registered(world)) { - ecs_entity_t prev_scope = 0; - ecs_id_t prev_with = 0; - - if (world) { - prev_scope = ecs_set_scope(world, 0); - prev_with = ecs_set_with(world, 0); - } - - // This will register a component id, but will not register - // lifecycle callbacks. - bool existing; - id_explicit(world, name, allow_tag, 0, true, &existing); + ecs_set_with(world, prev_with); + ecs_set_scope(world, prev_scope); // 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. if (size() && !existing) { - register_lifecycle_actions(world, s_id); + register_lifecycle_actions(world, c); } - if (prev_with) { - ecs_set_with(world, prev_with); - } - if (prev_scope) { - ecs_set_scope(world, prev_scope); - } + // Set world local component id + flecs_component_ids_set(world, s_index, c); + + // If component is enum type, register constants. Make sure to do + // this after setting the component id, because the enum code will + // be calling type::id(). + #if FLECS_CPP_ENUM_REFLECTION_SUPPORT + _::init_enum(world, c); + #endif } -#else - (void)world; - (void)name; - (void)allow_tag; + ecs_assert(c != 0, ECS_INTERNAL_ERROR, NULL); + + return c; + } + + // Get type (component) id. + // If type was not yet registered and automatic registration is allowed, + // this function will also register the type. + static entity_t id(world_t *world) + { +#ifdef FLECS_CPP_NO_AUTO_REGISTRATION ecs_assert(registered(world), ECS_INVALID_OPERATION, - "component '%s' was not registered before use", + "component '%s' must be registered before use", type_name()); -#endif - // By now we should have a valid identifier - ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); - - return s_id; + flecs::entity_t c = flecs_component_ids_get(world, s_index); + ecs_assert(c != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(ecs_is_alive(world, c), ECS_INVALID_OPERATION, + "component '%s' was deleted, reregister before using", + type_name()); +#else + flecs::entity_t c = flecs_component_ids_get_alive(world, s_index); + if (!c) { + c = register_id(world); + } +#endif + return c; } // Return the size of a component. static size_t size() { - ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(s_index != 0, ECS_INTERNAL_ERROR, NULL); return s_size; } // Return the alignment of a component. static size_t alignment() { - ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(s_index != 0, ECS_INTERNAL_ERROR, NULL); return s_alignment; } // Was the component already registered. static bool registered(flecs::world_t *world) { - if (s_reset_count != ecs_cpp_reset_count_get()) { - reset(); - } - if (s_id == 0) { + ecs_assert(world != nullptr, ECS_INVALID_PARAMETER, NULL); + + if (s_index == 0) { return false; } - if (world && !ecs_exists(world, s_id)) { + + if (!flecs_component_ids_get(world, s_index)) { return false; } + return true; } // This function is only used to test cross-translation unit features. No // code other than test cases should invoke this function. static void reset() { - s_id = 0; + s_index = 0; s_size = 0; s_alignment = 0; s_allow_tag = true; } - static entity_t s_id; + static int32_t s_index; static size_t s_size; static size_t s_alignment; static bool s_allow_tag; - static int32_t s_reset_count; }; // Global templated variables that hold component identifier and other info -template entity_t type_impl::s_id; -template size_t type_impl::s_size; -template size_t type_impl::s_alignment; -template bool type_impl::s_allow_tag( true ); -template int32_t type_impl::s_reset_count; +template int32_t type_impl::s_index; +template size_t type_impl::s_size; +template size_t type_impl::s_alignment; +template bool type_impl::s_allow_tag( true ); // Front facing class for implicitly registering a component & obtaining // static component data @@ -27825,62 +27806,42 @@ struct component : untyped_component { if (!n) { n = _::type_name(); - /* Keep track of whether name was explicitly set. If not, and the - * component was already registered, just use the registered name. - * - * The registered name may differ from the typename as the registered - * name includes the flecs scope. This can in theory be different from - * the C++ namespace though it is good practice to keep them the same */ + // Keep track of whether name was explicitly set. If not, and the + // component was already registered, just use the registered name. + // The registered name may differ from the typename as the registered + // name includes the flecs scope. This can in theory be different from + // the C++ namespace though it is good practice to keep them the same */ implicit_name = true; } - if (_::type::registered(world)) { - /* Obtain component id. Because the component is already registered, - * this operation does nothing besides returning the existing id */ - id = _::type::id_explicit(world, name, allow_tag, id); - - ecs_cpp_component_validate(world, id, n, _::symbol_name(), - _::type::size(), - _::type::alignment(), - implicit_name); - } else { - /* If component is registered from an existing scope, ignore the - * namespace in the name of the component. */ - if (implicit_name && (ecs_get_scope(world) != 0)) { - /* If the type is a template type, make sure to ignore ':' - * inside the template parameter list. */ - const char *start = strchr(n, '<'), *last_elem = NULL; - if (start) { - const char *ptr = start; - while (ptr[0] && (ptr[0] != ':') && (ptr > n)) { - ptr --; - } - if (ptr[0] == ':') { - last_elem = ptr; - } + // If component is registered by module, ensure it's registered in the + // scope of the module. + flecs::entity_t module = ecs_get_scope(world); + + // Strip off the namespace part of the component name, unless a name was + // explicitly provided by the application. + if (module && implicit_name) { + // If the type is a template type, make sure to ignore + // inside the template parameter list. + const char *start = strchr(n, '<'), *last_elem = NULL; + if (start) { + const char *ptr = start; + while (ptr[0] && (ptr[0] != ':') && (ptr > n)) { + ptr --; } - - if (last_elem) { - name = last_elem + 1; + if (ptr[0] == ':') { + last_elem = ptr; } } - /* Find or register component */ - bool existing; - id = ecs_cpp_component_register(world, id, n, _::symbol_name(), - ECS_SIZEOF(T), ECS_ALIGNOF(T), implicit_name, &existing); - - /* Initialize static component data */ - id = _::type::id_explicit(world, name, allow_tag, id); - - /* Initialize lifecycle actions (ctor, dtor, copy, move) */ - if (_::type::size() && !existing) { - _::register_lifecycle_actions(world, id); + if (last_elem) { + name = last_elem + 1; } } world_ = world; - id_ = id; + id_ = _::type::register_id( + world, name, allow_tag, id, true, implicit_name, n, module); } /** Register on_add hook. */ @@ -27992,43 +27953,6 @@ component& constant(const char *name, T value) { } }; -/** Get id currently assigned to component. If no world has registered the - * component yet, this operation will return 0. */ -template -flecs::entity_t type_id() { - if (_::type::s_reset_count == ecs_cpp_reset_count_get()) { - return _::type::s_id; - } else { - return 0; - } -} - -/** Reset static component ids. - * When components are registered their component ids are stored in a static - * type specific variable. This stored id is passed into component registration - * functions to ensure consistent ids across worlds. - * - * In some cases this can be undesirable, like when a process repeatedly creates - * worlds with different components. A typical example where this can happen is - * when running multiple tests in a single process, where each test registers - * its own set of components. - * - * This operation can be used to prevent reusing of component ids and force - * generating a new ids upon registration. - * - * Note that this operation should *never* be called while there are still - * alive worlds in a process. Doing so results in undefined behavior. - * - * Also note that this operation does not actually change the static component - * variables. It only ensures that the next time a component id is requested, a - * new id will be generated. - * - * @ingroup cpp_components - */ -inline void reset() { - ecs_cpp_reset_count_inc(); -} - } /** @} */ @@ -29253,8 +29177,7 @@ inline flecs::entity world::entity(E value) const { template inline flecs::entity world::entity(const char *name) const { - return flecs::entity(world_, - _::type::id_explicit(world_, name, true, 0, false) ); + return flecs::entity(world_, _::type::register_id(world_, name, true) ); } template @@ -30747,7 +30670,7 @@ inline void world::each(Func&& func) const { template inline void world::each(Func&& func) const { - ecs_iter_t it = ecs_each_id(world_, _::type::id()); + ecs_iter_t it = ecs_each_id(world_, _::type::id(world_)); while (ecs_each_next(&it)) { _::each_delegate(func).invoke(&it); @@ -31190,7 +31113,7 @@ flecs::entity import(world& world) { if (!_::type::registered(world)) { /* Module is registered with world, initialize static data */ if (m) { - _::type::init(m, false); + _::type::init_builtin(world, m, false); /* Module is not yet registered, register it now */ } else { @@ -31218,7 +31141,7 @@ flecs::entity import(world& world) { template inline flecs::entity world::module(const char *name) const { - flecs::entity result = this->entity(_::type::id( + flecs::entity result = this->entity(_::type::register_id( world_, nullptr, false)); if (name) { @@ -32163,18 +32086,14 @@ inline void init(flecs::world& world) { // specific types. if (!flecs::is_same() && !flecs::is_same()) { - flecs::_::type::init(flecs::Iptr, true); - ecs_assert(flecs::type_id() == flecs::Iptr, - ECS_INTERNAL_ERROR, NULL); + flecs::_::type::init_builtin(world, flecs::Iptr, true); // Remove symbol to prevent validation errors, as it doesn't match with // the typename ecs_remove_pair(world, flecs::Iptr, ecs_id(EcsIdentifier), EcsSymbol); } if (!flecs::is_same() && !flecs::is_same()) { - flecs::_::type::init(flecs::Uptr, true); - ecs_assert(flecs::type_id() == flecs::Uptr, - ECS_INTERNAL_ERROR, NULL); + flecs::_::type::init_builtin(world, flecs::Uptr, true); // Remove symbol to prevent validation errors, as it doesn't match with // the typename ecs_remove_pair(world, flecs::Uptr, ecs_id(EcsIdentifier), EcsSymbol); @@ -33369,14 +33288,16 @@ inline flecs::entity world::make_alive(flecs::entity_t e) const { template inline flecs::entity enum_data::entity() const { - return flecs::entity(world_, impl_.id); + return flecs::entity(world_, _::type::id(world_)); } template inline flecs::entity enum_data::entity(underlying_type_t value) const { int index = index_by_value(value); if (index >= 0) { - return flecs::entity(world_, impl_.constants[index].id); + int32_t constant_i = impl_.constants[index].index; + flecs::entity_t entity = flecs_component_ids_get(world_, constant_i); + return flecs::entity(world_, entity); } #ifdef FLECS_META // Reflection data lookup failed. Try value lookup amongst flecs::Constant relationships diff --git a/examples/cpp/systems/multiple_queries/project.json b/examples/cpp/systems/multiple_queries/project.json index 19d74caf24..7352c7751e 100644 --- a/examples/cpp/systems/multiple_queries/project.json +++ b/examples/cpp/systems/multiple_queries/project.json @@ -6,6 +6,6 @@ "flecs" ], "language": "c++", - "private": false + "public": false } } \ No newline at end of file diff --git a/include/flecs/addons/cpp/component.hpp b/include/flecs/addons/cpp/component.hpp index 76b1684e9f..67b3b2104a 100644 --- a/include/flecs/addons/cpp/component.hpp +++ b/include/flecs/addons/cpp/component.hpp @@ -120,16 +120,6 @@ void register_lifecycle_actions( } } -// Class that manages component ids across worlds & binaries. -// The type class stores the component id for a C++ type in a static global -// variable that is shared between worlds. Whenever a component is used this -// class will check if it already has been registered (has the global id been -// set), and if not, register the component with the world. -// -// If the id has been set, the class will ensure it is known by the world. If it -// is not known the component has been registered by another world and will be -// registered with the world using the same id. If the id does exist, the class -// will register it as a component, and verify whether the input is consistent. template struct type_impl { static_assert(is_pointer::value == false, @@ -137,27 +127,9 @@ struct type_impl { // Initialize component identifier static void init( - entity_t entity, bool allow_tag = true) { - if (s_reset_count != ecs_cpp_reset_count_get()) { - reset(); - } - - // If an identifier was already set, check for consistency - if (s_id) { - ecs_assert(s_id == entity, ECS_INCONSISTENT_COMPONENT_ID, - type_name()); - ecs_assert(allow_tag == s_allow_tag, ECS_INVALID_PARAMETER, NULL); - - // Component was already registered and data is consistent with new - // identifier, so nothing else to be done. - return; - } - - // Component wasn't registered yet, set the values. Register component - // name as the fully qualified flecs path. - s_id = entity; + s_index = flecs_component_ids_index_get(); s_allow_tag = allow_tag; s_size = sizeof(T); s_alignment = alignof(T); @@ -165,161 +137,158 @@ struct type_impl { s_size = 0; s_alignment = 0; } + } - s_reset_count = ecs_cpp_reset_count_get(); + static void init_builtin( + flecs::world_t *world, + flecs::entity_t id, + bool allow_tag = true) + { + init(allow_tag); + flecs_component_ids_set(world, s_index, id); } - // Obtain a component identifier for explicit component registration. - static entity_t id_explicit(world_t *world = nullptr, + // Register component id. + static entity_t register_id(world_t *world, const char *name = nullptr, bool allow_tag = true, flecs::id_t id = 0, - bool is_component = true, bool *existing = nullptr) + bool is_component = false, bool implicit_name = true, const char *n = nullptr, + flecs::entity_t module = 0) { - if (!s_id) { - // If no world was provided the component cannot be registered - ecs_assert(world != nullptr, ECS_COMPONENT_NOT_REGISTERED, name); - } else { - ecs_assert(!id || s_id == id, ECS_INCONSISTENT_COMPONENT_ID, NULL); + if (!s_index) { + // This is the first time (in this binary image) that this type is + // being used. Generate a static index that will identify the type + // across worlds. + init(allow_tag); + ecs_assert(s_index != 0, ECS_INTERNAL_ERROR, NULL); } - // 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. - if (!s_id || (world && !ecs_is_alive(world, s_id))) { - init(s_id ? s_id : id, allow_tag); - - ecs_assert(!id || s_id == id, ECS_INTERNAL_ERROR, NULL); + flecs::entity_t c = flecs_component_ids_get(world, s_index); + + if (!c || !ecs_is_alive(world, c)) { + // When a component is implicitly registered, ensure that it's not + // registered in the current scope of the application/that "with" + // components get added to the component entity. + ecs_entity_t prev_scope = ecs_set_scope(world, module); + ecs_entity_t prev_with = ecs_set_with(world, 0); + + // At this point it is possible that the type was already registered + // with the world, just not for this binary. The registration code + // uses the type symbol to check if it was already registered. Note + // that the symbol is separate from the typename, as an application + // can override a component name when registering a type. + bool existing = false; + c = ecs_cpp_component_find( + world, id, n, symbol_name(), size(), alignment(), + implicit_name, &existing); const char *symbol = nullptr; - if (id) { - symbol = ecs_get_symbol(world, id); + if (c) { + symbol = ecs_get_symbol(world, c); } if (!symbol) { symbol = symbol_name(); } - entity_t entity = ecs_cpp_component_register_explicit( - world, s_id, id, name, type_name(), symbol, - s_size, s_alignment, is_component, existing); + c = ecs_cpp_component_register(world, c, c, name, type_name(), + symbol, size(), alignment(), is_component, &existing); - s_id = entity; - - // If component is enum type, register constants - #if FLECS_CPP_ENUM_REFLECTION_SUPPORT - _::init_enum(world, entity); - #endif - } - - // By now the identifier must be valid and known with the world. - ecs_assert(s_id != 0 && ecs_exists(world, s_id), - ECS_INTERNAL_ERROR, NULL); - - return s_id; - } - - // Obtain a component identifier for implicit component registration. This - // is almost the same as id_explicit, except that this operation - // automatically registers lifecycle callbacks. - // Additionally, implicit registration temporarily resets the scope & with - // state of the world, so that the component is not implicitly created with - // the scope/with of the code it happens to be first used by. - static id_t id(world_t *world = nullptr, const char *name = nullptr, - bool allow_tag = true) - { - // If no id has been registered yet, do it now. -#ifndef FLECS_CPP_NO_AUTO_REGISTRATION - if (!registered(world)) { - ecs_entity_t prev_scope = 0; - ecs_id_t prev_with = 0; - - if (world) { - prev_scope = ecs_set_scope(world, 0); - prev_with = ecs_set_with(world, 0); - } - - // This will register a component id, but will not register - // lifecycle callbacks. - bool existing; - id_explicit(world, name, allow_tag, 0, true, &existing); + ecs_set_with(world, prev_with); + ecs_set_scope(world, prev_scope); // 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. if (size() && !existing) { - register_lifecycle_actions(world, s_id); + register_lifecycle_actions(world, c); } - if (prev_with) { - ecs_set_with(world, prev_with); - } - if (prev_scope) { - ecs_set_scope(world, prev_scope); - } + // Set world local component id + flecs_component_ids_set(world, s_index, c); + + // If component is enum type, register constants. Make sure to do + // this after setting the component id, because the enum code will + // be calling type::id(). + #if FLECS_CPP_ENUM_REFLECTION_SUPPORT + _::init_enum(world, c); + #endif } -#else - (void)world; - (void)name; - (void)allow_tag; + ecs_assert(c != 0, ECS_INTERNAL_ERROR, NULL); + + return c; + } + + // Get type (component) id. + // If type was not yet registered and automatic registration is allowed, + // this function will also register the type. + static entity_t id(world_t *world) + { +#ifdef FLECS_CPP_NO_AUTO_REGISTRATION ecs_assert(registered(world), ECS_INVALID_OPERATION, - "component '%s' was not registered before use", + "component '%s' must be registered before use", type_name()); -#endif - // By now we should have a valid identifier - ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); - - return s_id; + flecs::entity_t c = flecs_component_ids_get(world, s_index); + ecs_assert(c != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(ecs_is_alive(world, c), ECS_INVALID_OPERATION, + "component '%s' was deleted, reregister before using", + type_name()); +#else + flecs::entity_t c = flecs_component_ids_get_alive(world, s_index); + if (!c) { + c = register_id(world); + } +#endif + return c; } // Return the size of a component. static size_t size() { - ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(s_index != 0, ECS_INTERNAL_ERROR, NULL); return s_size; } // Return the alignment of a component. static size_t alignment() { - ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(s_index != 0, ECS_INTERNAL_ERROR, NULL); return s_alignment; } // Was the component already registered. static bool registered(flecs::world_t *world) { - if (s_reset_count != ecs_cpp_reset_count_get()) { - reset(); - } - if (s_id == 0) { + ecs_assert(world != nullptr, ECS_INVALID_PARAMETER, NULL); + + if (s_index == 0) { return false; } - if (world && !ecs_exists(world, s_id)) { + + if (!flecs_component_ids_get(world, s_index)) { return false; } + return true; } // This function is only used to test cross-translation unit features. No // code other than test cases should invoke this function. static void reset() { - s_id = 0; + s_index = 0; s_size = 0; s_alignment = 0; s_allow_tag = true; } - static entity_t s_id; + static int32_t s_index; static size_t s_size; static size_t s_alignment; static bool s_allow_tag; - static int32_t s_reset_count; }; // Global templated variables that hold component identifier and other info -template entity_t type_impl::s_id; -template size_t type_impl::s_size; -template size_t type_impl::s_alignment; -template bool type_impl::s_allow_tag( true ); -template int32_t type_impl::s_reset_count; +template int32_t type_impl::s_index; +template size_t type_impl::s_size; +template size_t type_impl::s_alignment; +template bool type_impl::s_allow_tag( true ); // Front facing class for implicitly registering a component & obtaining // static component data @@ -386,62 +355,42 @@ struct component : untyped_component { if (!n) { n = _::type_name(); - /* Keep track of whether name was explicitly set. If not, and the - * component was already registered, just use the registered name. - * - * The registered name may differ from the typename as the registered - * name includes the flecs scope. This can in theory be different from - * the C++ namespace though it is good practice to keep them the same */ + // Keep track of whether name was explicitly set. If not, and the + // component was already registered, just use the registered name. + // The registered name may differ from the typename as the registered + // name includes the flecs scope. This can in theory be different from + // the C++ namespace though it is good practice to keep them the same */ implicit_name = true; } - if (_::type::registered(world)) { - /* Obtain component id. Because the component is already registered, - * this operation does nothing besides returning the existing id */ - id = _::type::id_explicit(world, name, allow_tag, id); - - ecs_cpp_component_validate(world, id, n, _::symbol_name(), - _::type::size(), - _::type::alignment(), - implicit_name); - } else { - /* If component is registered from an existing scope, ignore the - * namespace in the name of the component. */ - if (implicit_name && (ecs_get_scope(world) != 0)) { - /* If the type is a template type, make sure to ignore ':' - * inside the template parameter list. */ - const char *start = strchr(n, '<'), *last_elem = NULL; - if (start) { - const char *ptr = start; - while (ptr[0] && (ptr[0] != ':') && (ptr > n)) { - ptr --; - } - if (ptr[0] == ':') { - last_elem = ptr; - } + // If component is registered by module, ensure it's registered in the + // scope of the module. + flecs::entity_t module = ecs_get_scope(world); + + // Strip off the namespace part of the component name, unless a name was + // explicitly provided by the application. + if (module && implicit_name) { + // If the type is a template type, make sure to ignore + // inside the template parameter list. + const char *start = strchr(n, '<'), *last_elem = NULL; + if (start) { + const char *ptr = start; + while (ptr[0] && (ptr[0] != ':') && (ptr > n)) { + ptr --; } - - if (last_elem) { - name = last_elem + 1; + if (ptr[0] == ':') { + last_elem = ptr; } } - /* Find or register component */ - bool existing; - id = ecs_cpp_component_register(world, id, n, _::symbol_name(), - ECS_SIZEOF(T), ECS_ALIGNOF(T), implicit_name, &existing); - - /* Initialize static component data */ - id = _::type::id_explicit(world, name, allow_tag, id); - - /* Initialize lifecycle actions (ctor, dtor, copy, move) */ - if (_::type::size() && !existing) { - _::register_lifecycle_actions(world, id); + if (last_elem) { + name = last_elem + 1; } } world_ = world; - id_ = id; + id_ = _::type::register_id( + world, name, allow_tag, id, true, implicit_name, n, module); } /** Register on_add hook. */ @@ -518,43 +467,6 @@ struct component : untyped_component { } }; -/** Get id currently assigned to component. If no world has registered the - * component yet, this operation will return 0. */ -template -flecs::entity_t type_id() { - if (_::type::s_reset_count == ecs_cpp_reset_count_get()) { - return _::type::s_id; - } else { - return 0; - } -} - -/** Reset static component ids. - * When components are registered their component ids are stored in a static - * type specific variable. This stored id is passed into component registration - * functions to ensure consistent ids across worlds. - * - * In some cases this can be undesirable, like when a process repeatedly creates - * worlds with different components. A typical example where this can happen is - * when running multiple tests in a single process, where each test registers - * its own set of components. - * - * This operation can be used to prevent reusing of component ids and force - * generating a new ids upon registration. - * - * Note that this operation should *never* be called while there are still - * alive worlds in a process. Doing so results in undefined behavior. - * - * Also note that this operation does not actually change the static component - * variables. It only ensures that the next time a component id is requested, a - * new id will be generated. - * - * @ingroup cpp_components - */ -inline void reset() { - ecs_cpp_reset_count_inc(); -} - } /** @} */ diff --git a/include/flecs/addons/cpp/impl/world.hpp b/include/flecs/addons/cpp/impl/world.hpp index 18a53751d7..0baad3e779 100644 --- a/include/flecs/addons/cpp/impl/world.hpp +++ b/include/flecs/addons/cpp/impl/world.hpp @@ -275,14 +275,16 @@ inline flecs::entity world::make_alive(flecs::entity_t e) const { template inline flecs::entity enum_data::entity() const { - return flecs::entity(world_, impl_.id); + return flecs::entity(world_, _::type::id(world_)); } template inline flecs::entity enum_data::entity(underlying_type_t value) const { int index = index_by_value(value); if (index >= 0) { - return flecs::entity(world_, impl_.constants[index].id); + int32_t constant_i = impl_.constants[index].index; + flecs::entity_t entity = flecs_component_ids_get(world_, constant_i); + return flecs::entity(world_, entity); } #ifdef FLECS_META // Reflection data lookup failed. Try value lookup amongst flecs::Constant relationships diff --git a/include/flecs/addons/cpp/mixins/entity/builder.hpp b/include/flecs/addons/cpp/mixins/entity/builder.hpp index 31ec886c3d..ebbd6633fa 100644 --- a/include/flecs/addons/cpp/mixins/entity/builder.hpp +++ b/include/flecs/addons/cpp/mixins/entity/builder.hpp @@ -610,7 +610,7 @@ struct entity_builder : entity_view { */ template const Self& enable(flecs::id_t second) const { - return this->enable(_::type::id(), second); + return this->enable(_::type::id(world_), second); } /** Enable a pair. @@ -621,7 +621,7 @@ struct entity_builder : entity_view { */ template const Self& enable() const { - return this->enable(_::type::id()); + return this->enable(_::type::id(world_)); } /** Disable an id. @@ -644,7 +644,7 @@ struct entity_builder : entity_view { */ template const Self& disable() const { - return this->disable(_::type::id()); + return this->disable(_::type::id(world_)); } /** Disable a pair. @@ -665,7 +665,7 @@ struct entity_builder : entity_view { */ template const Self& disable(flecs::id_t second) const { - return this->disable(_::type::id(), second); + return this->disable(_::type::id(world_), second); } /** Disable a pair. @@ -676,7 +676,7 @@ struct entity_builder : entity_view { */ template const Self& disable() const { - return this->disable(_::type::id()); + return this->disable(_::type::id(world_)); } const Self& set_ptr(entity_t comp, size_t size, const void *ptr) const { diff --git a/include/flecs/addons/cpp/mixins/entity/impl.hpp b/include/flecs/addons/cpp/mixins/entity/impl.hpp index 47ec8c3fd1..b631320b52 100644 --- a/include/flecs/addons/cpp/mixins/entity/impl.hpp +++ b/include/flecs/addons/cpp/mixins/entity/impl.hpp @@ -210,8 +210,7 @@ inline flecs::entity world::entity(E value) const { template inline flecs::entity world::entity(const char *name) const { - return flecs::entity(world_, - _::type::id_explicit(world_, name, true, 0, false) ); + return flecs::entity(world_, _::type::register_id(world_, name, true) ); } template diff --git a/include/flecs/addons/cpp/mixins/meta/impl.hpp b/include/flecs/addons/cpp/mixins/meta/impl.hpp index e6f18a8431..a504ef426c 100644 --- a/include/flecs/addons/cpp/mixins/meta/impl.hpp +++ b/include/flecs/addons/cpp/mixins/meta/impl.hpp @@ -65,18 +65,14 @@ inline void init(flecs::world& world) { // specific types. if (!flecs::is_same() && !flecs::is_same()) { - flecs::_::type::init(flecs::Iptr, true); - ecs_assert(flecs::type_id() == flecs::Iptr, - ECS_INTERNAL_ERROR, NULL); + flecs::_::type::init_builtin(world, flecs::Iptr, true); // Remove symbol to prevent validation errors, as it doesn't match with // the typename ecs_remove_pair(world, flecs::Iptr, ecs_id(EcsIdentifier), EcsSymbol); } if (!flecs::is_same() && !flecs::is_same()) { - flecs::_::type::init(flecs::Uptr, true); - ecs_assert(flecs::type_id() == flecs::Uptr, - ECS_INTERNAL_ERROR, NULL); + flecs::_::type::init_builtin(world, flecs::Uptr, true); // Remove symbol to prevent validation errors, as it doesn't match with // the typename ecs_remove_pair(world, flecs::Uptr, ecs_id(EcsIdentifier), EcsSymbol); diff --git a/include/flecs/addons/cpp/mixins/module/impl.hpp b/include/flecs/addons/cpp/mixins/module/impl.hpp index d8ee634874..e72cb26422 100644 --- a/include/flecs/addons/cpp/mixins/module/impl.hpp +++ b/include/flecs/addons/cpp/mixins/module/impl.hpp @@ -50,7 +50,7 @@ flecs::entity import(world& world) { if (!_::type::registered(world)) { /* Module is registered with world, initialize static data */ if (m) { - _::type::init(m, false); + _::type::init_builtin(world, m, false); /* Module is not yet registered, register it now */ } else { @@ -78,7 +78,7 @@ flecs::entity import(world& world) { template inline flecs::entity world::module(const char *name) const { - flecs::entity result = this->entity(_::type::id( + flecs::entity result = this->entity(_::type::register_id( world_, nullptr, false)); if (name) { diff --git a/include/flecs/addons/cpp/mixins/query/impl.hpp b/include/flecs/addons/cpp/mixins/query/impl.hpp index 747e3d6086..160c228ef6 100644 --- a/include/flecs/addons/cpp/mixins/query/impl.hpp +++ b/include/flecs/addons/cpp/mixins/query/impl.hpp @@ -298,7 +298,7 @@ inline void world::each(Func&& func) const { template inline void world::each(Func&& func) const { - ecs_iter_t it = ecs_each_id(world_, _::type::id()); + ecs_iter_t it = ecs_each_id(world_, _::type::id(world_)); while (ecs_each_next(&it)) { _::each_delegate(func).invoke(&it); diff --git a/include/flecs/addons/cpp/utils/enum.hpp b/include/flecs/addons/cpp/utils/enum.hpp index 7399646518..807e3db16b 100644 --- a/include/flecs/addons/cpp/utils/enum.hpp +++ b/include/flecs/addons/cpp/utils/enum.hpp @@ -173,7 +173,7 @@ static const char* enum_constant_to_name() { /** Enumeration constant data */ template struct enum_constant_data { - flecs::entity_t id; + int32_t index; // Global index used to obtain world local entity id T offset; }; @@ -284,7 +284,6 @@ struct enum_data_impl { }; public: - flecs::entity_t id; int min; int max; bool has_contiguous; @@ -336,8 +335,17 @@ struct enum_type { "Signed integer enums causes integer overflow when recording offset from high positive to" " low negative. Consider using unsigned integers as underlying type."); enum_type::data.constants[enum_type::data.max].offset = v - last_value; - enum_type::data.constants[enum_type::data.max].id = ecs_cpp_enum_constant_register( - world, enum_type::data.id, 0, name, static_cast(v)); + if (!enum_type::data.constants[enum_type::data.max].index) { + enum_type::data.constants[enum_type::data.max].index = + flecs_component_ids_index_get(); + } + + flecs::entity_t constant = ecs_cpp_enum_constant_register( + world, type::id(world), 0, name, static_cast(v)); + flecs_component_ids_set(world, + enum_type::data.constants[enum_type::data.max].index, + constant); + return v; } }; @@ -370,7 +378,7 @@ struct enum_type { ecs_log_push(); ecs_cpp_enum_init(world, id); - data.id = id; + // data.id = id; // Generate reflection data enum_reflection::template each_enum< static_cast(enum_last::value) >(world); @@ -412,7 +420,7 @@ struct enum_data { if (index < 0) { return false; } - return impl_.constants[index].id != 0; + return impl_.constants[index].index != 0; } /** diff --git a/include/flecs/addons/flecs_cpp.h b/include/flecs/addons/flecs_cpp.h index 2ee2642f79..cf8fb0046d 100644 --- a/include/flecs/addons/flecs_cpp.h +++ b/include/flecs/addons/flecs_cpp.h @@ -66,28 +66,18 @@ const char* ecs_cpp_trim_module( const char *type_name); FLECS_API -void ecs_cpp_component_validate( +ecs_entity_t ecs_cpp_component_find( ecs_world_t *world, ecs_entity_t id, const char *name, const char *symbol, size_t size, size_t alignment, - bool implicit_name); - -FLECS_API -ecs_entity_t ecs_cpp_component_register( - ecs_world_t *world, - ecs_entity_t id, - const char *name, - const char *symbol, - ecs_size_t size, - ecs_size_t alignment, bool implicit_name, bool *existing_out); FLECS_API -ecs_entity_t ecs_cpp_component_register_explicit( +ecs_entity_t ecs_cpp_component_register( ecs_world_t *world, ecs_entity_t s_id, ecs_entity_t id, @@ -112,12 +102,6 @@ ecs_entity_t ecs_cpp_enum_constant_register( const char *name, int value); -FLECS_API -int32_t ecs_cpp_reset_count_get(void); - -FLECS_API -int32_t ecs_cpp_reset_count_inc(void); - #ifdef FLECS_META FLECS_API const ecs_member_t* ecs_cpp_last_member( diff --git a/include/flecs/private/api_support.h b/include/flecs/private/api_support.h index 5c9ce00fd9..82814832b1 100644 --- a/include/flecs/private/api_support.h +++ b/include/flecs/private/api_support.h @@ -159,8 +159,28 @@ FLECS_API int32_t flecs_poly_refcount( ecs_poly_t *poly); +FLECS_API +int32_t flecs_component_ids_index_get(void); + +FLECS_API +ecs_entity_t flecs_component_ids_get( + const ecs_world_t *world, + int32_t index); + +FLECS_API +ecs_entity_t flecs_component_ids_get_alive( + const ecs_world_t *stage_world, + int32_t index); + +FLECS_API +void flecs_component_ids_set( + ecs_world_t *world, + int32_t index, + ecs_entity_t id); + #define flecs_poly_claim(poly) \ flecs_poly_claim_(ECS_CONST_CAST(void*, reinterpret_cast(poly))) + #define flecs_poly_release(poly) \ flecs_poly_release_(ECS_CONST_CAST(void*, reinterpret_cast(poly))) diff --git a/src/addons/flecs_cpp.c b/src/addons/flecs_cpp.c index e8c2ddbbdd..26fed17ffe 100644 --- a/src/addons/flecs_cpp.c +++ b/src/addons/flecs_cpp.c @@ -204,94 +204,31 @@ const char* ecs_cpp_trim_module( return type_name; } -// Validate registered component -void ecs_cpp_component_validate( +ecs_entity_t ecs_cpp_component_find( ecs_world_t *world, ecs_entity_t id, const char *name, const char *symbol, size_t size, size_t alignment, - bool implicit_name) -{ - /* If entity has a name check if it matches */ - if (ecs_is_valid(world, id) && ecs_get_name(world, id) != NULL) { - if (!implicit_name && id >= EcsFirstUserComponentId) { -#ifndef FLECS_NDEBUG - char *path = ecs_get_path_w_sep( - world, 0, id, "::", NULL); - if (ecs_os_strcmp(path, name)) { - ecs_abort(ECS_INCONSISTENT_NAME, - "component '%s' already registered with name '%s'", - name, path); - } - ecs_os_free(path); -#endif - } - - if (symbol) { - const char *existing_symbol = ecs_get_symbol(world, id); - if (existing_symbol) { - if (ecs_os_strcmp(symbol, existing_symbol)) { - ecs_abort(ECS_INCONSISTENT_NAME, - "component '%s' with symbol '%s' already registered with symbol '%s'", - name, symbol, existing_symbol); - } - } - } - } else { - /* Ensure that the entity id valid */ - if (!ecs_is_alive(world, id)) { - ecs_make_alive(world, id); - } - - /* Register name with entity, so that when the entity is created the - * correct id will be resolved from the name. Only do this when the - * entity is empty. */ - ecs_add_path_w_sep(world, id, 0, name, "::", "::"); - } - - /* If a component was already registered with this id but with a - * different size, the ecs_component_init function will fail. */ - - /* We need to explicitly call ecs_component_init here again. Even though - * the component was already registered, it may have been registered - * with a different world. This ensures that the component is registered - * with the same id for the current world. - * If the component was registered already, nothing will change. */ - ecs_entity_t ent = ecs_component_init(world, &(ecs_component_desc_t){ - .entity = id, - .type.size = flecs_uto(int32_t, size), - .type.alignment = flecs_uto(int32_t, alignment) - }); - (void)ent; - ecs_assert(ent == id, ECS_INTERNAL_ERROR, NULL); -} - -ecs_entity_t ecs_cpp_component_register( - ecs_world_t *world, - ecs_entity_t id, - const char *name, - const char *symbol, - ecs_size_t size, - ecs_size_t alignment, bool implicit_name, bool *existing_out) { (void)size; (void)alignment; + ecs_assert(existing_out != NULL, ECS_INTERNAL_ERROR, NULL); + /* If the component is not yet registered, ensure no other component * or entity has been registered with this name. Ensure component is * looked up from root. */ - bool existing = false; ecs_entity_t prev_scope = ecs_set_scope(world, 0); ecs_entity_t ent; if (id) { ent = id; } else { ent = ecs_lookup_path_w_sep(world, 0, name, "::", "::", false); - existing = ent != 0 && ecs_has(world, ent, EcsComponent); + *existing_out = ent != 0 && ecs_has(world, ent, EcsComponent); } ecs_set_scope(world, prev_scope); @@ -319,16 +256,23 @@ ecs_entity_t ecs_cpp_component_register( * to alias the type, vs. accidentally registering an unrelated * type with the same size/alignment. */ char *type_path = ecs_get_path(world, ent); - if (ecs_os_strcmp(type_path, symbol) || - component->size != size || - component->alignment != alignment) - { + if (ecs_os_strcmp(type_path, symbol)) { ecs_err( "component with name '%s' is already registered for"\ " type '%s' (trying to register for type '%s')", name, sym, symbol); ecs_abort(ECS_NAME_IN_USE, NULL); } + + if (flecs_itosize(component->size) != size || + flecs_itosize(component->alignment) != alignment) + { + ecs_err( + "component with name '%s' is already registered with"\ + " mismatching size/alignment)", name); + ecs_abort(ECS_INVALID_COMPONENT_SIZE, NULL); + } + ecs_os_free(type_path); } else if (!sym) { ecs_set_symbol(world, ent, symbol); @@ -339,17 +283,14 @@ ecs_entity_t ecs_cpp_component_register( * registered under a different name. */ } else if (!implicit_name) { ent = ecs_lookup_symbol(world, symbol, false, false); - ecs_assert(ent == 0 || (ent == id), ECS_INCONSISTENT_COMPONENT_ID, symbol); - } - - if (existing_out) { - *existing_out = existing; + ecs_assert(ent == 0 || (ent == id), + ECS_INCONSISTENT_COMPONENT_ID, symbol); } return ent; } -ecs_entity_t ecs_cpp_component_register_explicit( +ecs_entity_t ecs_cpp_component_register( ecs_world_t *world, ecs_entity_t s_id, ecs_entity_t id, @@ -362,7 +303,8 @@ ecs_entity_t ecs_cpp_component_register_explicit( bool *existing_out) { char *existing_name = NULL; - if (existing_out) *existing_out = false; + + ecs_assert(existing_out != NULL, ECS_INTERNAL_ERROR, NULL); // If an explicit id is provided, it is possible that the symbol and // name differ from the actual type, as the application may alias @@ -375,7 +317,7 @@ ecs_entity_t ecs_cpp_component_register_explicit( if (id) { existing_name = ecs_get_path_w_sep(world, 0, id, "::", "::"); name = existing_name; - if (existing_out) *existing_out = true; + *existing_out = true; } else { // If type is not yet known, derive from type name name = ecs_cpp_trim_module(world, type_name); @@ -488,16 +430,6 @@ ecs_entity_t ecs_cpp_enum_constant_register( return id; } -static int32_t flecs_reset_count = 0; - -int32_t ecs_cpp_reset_count_get(void) { - return flecs_reset_count; -} - -int32_t ecs_cpp_reset_count_inc(void) { - return ++flecs_reset_count; -} - #ifdef FLECS_META const ecs_member_t* ecs_cpp_last_member( const ecs_world_t *world, diff --git a/src/addons/meta/meta.c b/src/addons/meta/meta.c index eff54d9685..f0164b7f62 100644 --- a/src/addons/meta/meta.c +++ b/src/addons/meta/meta.c @@ -946,6 +946,7 @@ void flecs_set_custom_type(ecs_iter_t *it) { if (!comp || !comp->size || !comp->alignment) { ecs_err("custom type '%s' has no size/alignment, register as component first", ecs_get_name(world, e)); + flecs_dump_backtrace(stdout); continue; } diff --git a/src/entity.c b/src/entity.c index 33d65b461a..9db494a17d 100644 --- a/src/entity.c +++ b/src/entity.c @@ -1745,7 +1745,8 @@ int flecs_traverse_add( const char *sym = ecs_get_symbol(world, result); if (sym) { ecs_assert(!ecs_os_strcmp(desc->symbol, sym), - ECS_INCONSISTENT_NAME, desc->symbol); + ECS_INCONSISTENT_NAME, "%s (provided) vs. %s (existing)", + desc->symbol, sym); } else { ecs_set_symbol(world, result, desc->symbol); } diff --git a/src/private_types.h b/src/private_types.h index f2649dbce4..2dad2903ac 100644 --- a/src/private_types.h +++ b/src/private_types.h @@ -340,6 +340,9 @@ struct ecs_world_t { ecs_stage_t **stages; /* Stages */ int32_t stage_count; /* Number of stages */ + /* -- Component ids -- */ + ecs_vec_t component_ids; /* World local component ids */ + /* Internal callback for command inspection. Only one callback can be set at * a time. After assignment the action will become active at the start of * the next frame, set by ecs_frame_begin, and will be reset by diff --git a/src/world.c b/src/world.c index ac9c533d02..566f2294e8 100644 --- a/src/world.c +++ b/src/world.c @@ -982,6 +982,7 @@ ecs_world_t *ecs_mini(void) { flecs_name_index_init(&world->aliases, a); flecs_name_index_init(&world->symbols, a); ecs_vec_init_t(a, &world->fini_actions, ecs_action_elem_t, 0); + ecs_vec_init_t(a, &world->component_ids, ecs_id_t, 0); world->info.time_scale = 1.0; if (ecs_os_has_time()) { @@ -1657,6 +1658,7 @@ int ecs_fini( flecs_name_index_fini(&world->aliases); flecs_name_index_fini(&world->symbols); ecs_set_stage_count(world, 0); + ecs_vec_fini_t(&world->allocator, &world->component_ids, ecs_id_t); ecs_log_pop_1(); flecs_world_allocators_fini(world); @@ -2424,3 +2426,64 @@ ecs_flags32_t ecs_world_get_flags( return stage->world->flags; } } + +static int32_t flecs_component_ids_last_index = 0; + +int32_t flecs_component_ids_index_get(void) { + if (ecs_os_api.ainc_) { + return ecs_os_ainc(&flecs_component_ids_last_index); + } else { + return ++ flecs_component_ids_last_index; + } +} + +ecs_entity_t flecs_component_ids_get( + const ecs_world_t *stage_world, + int32_t index) +{ + ecs_world_t *world = + ECS_CONST_CAST(ecs_world_t*, ecs_get_world(stage_world)); + flecs_poly_assert(world, ecs_world_t); + + if (index >= ecs_vec_count(&world->component_ids)) { + return 0; + } + + return ecs_vec_get_t( + &world->component_ids, ecs_entity_t, index)[0]; +} + +ecs_entity_t flecs_component_ids_get_alive( + const ecs_world_t *stage_world, + int32_t index) +{ + ecs_world_t *world = + ECS_CONST_CAST(ecs_world_t*, ecs_get_world(stage_world)); + flecs_poly_assert(world, ecs_world_t); + + if (index >= ecs_vec_count(&world->component_ids)) { + return 0; + } + + ecs_entity_t result = ecs_vec_get_t( + &world->component_ids, ecs_entity_t, index)[0]; + if (!flecs_entities_is_alive(world, result)) { + return 0; + } + + return result; +} + +void flecs_component_ids_set( + ecs_world_t *stage_world, + int32_t index, + ecs_entity_t component) +{ + ecs_world_t *world = + ECS_CONST_CAST(ecs_world_t*, ecs_get_world(stage_world)); + flecs_poly_assert(world, ecs_world_t); + + ecs_vec_set_min_count_zeromem_t( + &world->allocator, &world->component_ids, ecs_entity_t, index + 1); + ecs_vec_get_t(&world->component_ids, ecs_entity_t, index)[0] = component; +} diff --git a/test/cpp/project.json b/test/cpp/project.json index aa7ea4f62e..c03ff8d5f7 100644 --- a/test/cpp/project.json +++ b/test/cpp/project.json @@ -415,7 +415,8 @@ "component_registered_as_enum", "mixed_auto_manual_constants", "enum_class_mixed_auto_manual_constants", - "enum_child_count" + "enum_child_count", + "multi_world_constant_ids" ] }, { "id": "Union", @@ -1161,7 +1162,6 @@ "multi_world_component", "multi_world_component_namespace", "multi_world_module", - "multi_world_recycled_component", "multi_world_recycled_component_different_generation", "type_id", "different_comp_same_name", @@ -1178,6 +1178,7 @@ "reimport_namespaced_module", "c_interop_module", "c_interop_after_reset", + "c_interop_module_no_names", "implicit_register_w_new_world", "implicit_register_after_reset_register_w_custom_name", "register_after_reset_register_w_custom_name", @@ -1243,7 +1244,6 @@ "exists", "get_alive", "make_alive", - "reset_all", "get_tick", "register_from_scope", "register_nested_from_scope", diff --git a/test/cpp/src/Entity.cpp b/test/cpp/src/Entity.cpp index 6a22a7b066..778830ea5a 100644 --- a/test/cpp/src/Entity.cpp +++ b/test/cpp/src/Entity.cpp @@ -4092,6 +4092,7 @@ void Entity_prefab_hierarchy_w_child_override(void) { test_assert(i != 0); auto ib = i.lookup("Base"); test_assert(ib != 0); + test_assert(ib.has()); test_assert(ib.has()); } diff --git a/test/cpp/src/Enum.cpp b/test/cpp/src/Enum.cpp index 69a07a2b54..509ba178c9 100644 --- a/test/cpp/src/Enum.cpp +++ b/test/cpp/src/Enum.cpp @@ -1179,3 +1179,29 @@ void Enum_enum_child_count(void) { test_assert(f.count() == 3); } + +void Enum_multi_world_constant_ids(void) { + flecs::world world_a, world_b; + + world_a.component(); // Offset low ids by one + world_a.entity(); // Offset regular ids by one + + flecs::entity e_a = world_a.component(); + flecs::entity red_a = world_a.to_entity(StandardEnum::Red); + flecs::entity green_a = world_a.to_entity(StandardEnum::Green); + flecs::entity blue_a = world_a.to_entity(StandardEnum::Blue); + + world_b.component(); + + // Make sure we're actually testing different ids across worlds + test_assert(e_a != world_b.component()); + test_assert(red_a != world_b.to_entity(StandardEnum::Red)); + test_assert(green_a != world_b.to_entity(StandardEnum::Green)); + test_assert(blue_a != world_b.to_entity(StandardEnum::Blue)); + + // Make sure ids didn't get overwritten for world_a + test_assert(e_a == world_a.component()); + test_assert(red_a == world_a.to_entity(StandardEnum::Red)); + test_assert(green_a == world_a.to_entity(StandardEnum::Green)); + test_assert(blue_a == world_a.to_entity(StandardEnum::Blue)); +} diff --git a/test/cpp/src/ImplicitComponents.cpp b/test/cpp/src/ImplicitComponents.cpp index 2abd95f3aa..4f82359880 100644 --- a/test/cpp/src/ImplicitComponents.cpp +++ b/test/cpp/src/ImplicitComponents.cpp @@ -223,7 +223,7 @@ void ImplicitComponents_reinit(void) { auto comp_1 = world.component(); - test_assert(flecs::type_id() == comp_1.id()); + test_assert(world.id() == comp_1.id()); // Reset component id using internals (currently the only way to simulate // registration across translation units) @@ -232,7 +232,7 @@ void ImplicitComponents_reinit(void) { world.entity() .add(); - test_assert(flecs::type_id() == comp_1.id()); + test_assert(world.id() == comp_1.id()); } namespace Foo { @@ -247,7 +247,7 @@ void ImplicitComponents_reinit_scoped(void) { auto comp_1 = world.component(); - test_assert(flecs::type_id() == comp_1.id()); + test_assert(world.id() == comp_1.id()); // Reset component id using internals (currently the only way to simulate // registration across translation units) @@ -256,7 +256,7 @@ void ImplicitComponents_reinit_scoped(void) { world.entity() .add(); - test_assert(flecs::type_id() == comp_1.id()); + test_assert(world.id() == comp_1.id()); } static int position_ctor_invoked = 0; @@ -270,7 +270,7 @@ void ImplicitComponents_reinit_w_lifecycle(void) { auto comp_1 = world.component(); - test_assert(flecs::type_id() == comp_1.id()); + test_assert(world.id() == comp_1.id()); // Explicitly register constructor ecs_type_hooks_t cl{}; @@ -291,7 +291,7 @@ void ImplicitComponents_reinit_w_lifecycle(void) { test_assert(e.has()); test_int(position_ctor_invoked, 2); - test_assert(flecs::type_id() == comp_1.id()); + test_assert(world.id() == comp_1.id()); } void ImplicitComponents_first_use_in_system(void) { @@ -421,9 +421,9 @@ void ImplicitComponents_implicit_base(void) { auto v = world.use(); - test_int(v.id(), flecs::type_id()); - test_int(v.id(), flecs::type_id()); - test_int(v.id(), flecs::type_id()); + test_int(v.id(), world.id()); + test_int(v.id(), world.id()); + test_int(v.id(), world.id()); } void ImplicitComponents_implicit_const(void) { @@ -431,9 +431,9 @@ void ImplicitComponents_implicit_const(void) { auto v = world.use(); - test_int(v.id(), flecs::type_id()); - test_int(v.id(), flecs::type_id()); - test_int(v.id(), flecs::type_id()); + test_int(v.id(), world.id()); + test_int(v.id(), world.id()); + test_int(v.id(), world.id()); } void ImplicitComponents_implicit_ref(void) { @@ -441,9 +441,9 @@ void ImplicitComponents_implicit_ref(void) { auto v = world.use(); - test_int(v.id(), flecs::type_id()); - test_int(v.id(), flecs::type_id()); - test_int(v.id(), flecs::type_id()); + test_int(v.id(), world.id()); + test_int(v.id(), world.id()); + test_int(v.id(), world.id()); } void ImplicitComponents_implicit_const_ref(void) { @@ -451,9 +451,9 @@ void ImplicitComponents_implicit_const_ref(void) { auto v = world.use(); - test_int(v.id(), flecs::type_id()); - test_int(v.id(), flecs::type_id()); - test_int(v.id(), flecs::type_id()); + test_int(v.id(), world.id()); + test_int(v.id(), world.id()); + test_int(v.id(), world.id()); } @@ -465,8 +465,6 @@ void ImplicitComponents_vector_elem_type(void) { test_assert(v != 0); } - flecs::reset(); - { flecs::entity v = world.vector(); test_assert(v != 0); diff --git a/test/cpp/src/QueryBuilder.cpp b/test/cpp/src/QueryBuilder.cpp index c540008f34..9b1fa8b65d 100644 --- a/test/cpp/src/QueryBuilder.cpp +++ b/test/cpp/src/QueryBuilder.cpp @@ -2138,12 +2138,12 @@ void QueryBuilder_group_by_raw(void) { auto q = ecs.query_builder() .with() - .group_by(flecs::type_id(), group_by_first_id) + .group_by(ecs.id(), group_by_first_id) .build(); auto q_reverse = ecs.query_builder() .with() - .group_by(flecs::type_id(), group_by_first_id_negated) + .group_by(ecs.id(), group_by_first_id_negated) .build(); auto e3 = ecs.entity().add().add(); diff --git a/test/cpp/src/Singleton.cpp b/test/cpp/src/Singleton.cpp index 5e013b19ce..afb34ea6c2 100644 --- a/test/cpp/src/Singleton.cpp +++ b/test/cpp/src/Singleton.cpp @@ -145,7 +145,7 @@ void Singleton_get_singleton(void) { auto s = world.singleton(); test_assert(s.has()); - test_assert(s.id() == flecs::type_id()); + test_assert(s.id() == world.id()); const Position* p = s.get(); test_int(p->x, 10); @@ -158,11 +158,11 @@ void Singleton_type_id_from_world(void) { world.set({10, 20}); flecs::entity_t id = world.id(); - test_assert(id == flecs::type_id()); + test_assert(id == world.id()); auto s = world.singleton(); - test_assert(s.id() == flecs::type_id()); - test_assert(s.id() == flecs::type_id()); + test_assert(s.id() == world.id()); + test_assert(s.id() == world.id()); } void Singleton_set_lambda(void) { diff --git a/test/cpp/src/World.cpp b/test/cpp/src/World.cpp index 1dff2c2d17..5f51da4567 100644 --- a/test/cpp/src/World.cpp +++ b/test/cpp/src/World.cpp @@ -23,7 +23,13 @@ typedef struct TestInteropModule { static void TestInteropModuleImport(ecs_world_t *world) { ECS_MODULE(world, TestInteropModule); + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); +} +static +void TestInteropModule2Import(ecs_world_t *world) { + ECS_MODULE(world, TestInteropModule2); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); } @@ -44,6 +50,19 @@ class module : TestInteropModule { } }; +class module2 { +public: + struct Velocity : ::Velocity { }; + + module2(flecs::world& ecs) { + TestInteropModule2Import(ecs); + + ecs.module(); + ecs.component(); + ecs.component(); + } +}; + } } @@ -109,13 +128,52 @@ void World_multi_world_component(void) { auto v_1 = w1.component(); auto v_2 = w2.component(); auto m_2 = w2.component(); + w2.component(); + + test_assert(p_1 == v_2); + test_assert(v_1 == m_2); - test_assert(v_1.id() == v_2.id()); - test_assert(p_1.id() != m_2.id()); - test_assert(m_2.id() > v_2.id()); + flecs::entity w1_e = w1.entity() + .set(Position{10, 20}) + .set(Velocity{1, 2}) + .set(Mass{100}); - auto m_1 = w2.component(); - test_assert(m_1.id() == m_2.id()); + flecs::entity w2_e = w2.entity() + .set(Position{10, 20}) + .set(Velocity{1, 2}) + .set(Mass{100}); + + { + const Position *p = w1_e.get(); + test_assert(p != nullptr); + test_int(p->x, 10); + test_int(p->y, 20); + + const Velocity *v = w1_e.get(); + test_assert(v != nullptr); + test_int(v->x, 1); + test_int(v->y, 2); + + const Mass *m = w1_e.get(); + test_assert(m != nullptr); + test_int(m->value, 100); + } + + { + const Position *p = w2_e.get(); + test_assert(p != nullptr); + test_int(p->x, 10); + test_int(p->y, 20); + + const Velocity *v = w2_e.get(); + test_assert(v != nullptr); + test_int(v->x, 1); + test_int(v->y, 2); + + const Mass *m = w2_e.get(); + test_assert(m != nullptr); + test_int(m->value, 100); + } } namespace A { @@ -157,23 +215,6 @@ void World_multi_world_module(void) { test_int(ns::namespace_module::system_invoke_count, 2); } -void World_multi_world_recycled_component(void) { - flecs::entity c; - { - flecs::world ecs; - for (int i = 0; i < FLECS_HI_COMPONENT_ID; i ++) { - ecs_new_low_id(ecs); - } - - ecs.entity().destruct(); - c = ecs.component(); - } - { - flecs::world ecs; - test_assert((c == ecs.component())); - } -} - void World_multi_world_recycled_component_different_generation(void) { flecs::entity c; { @@ -201,7 +242,7 @@ void World_type_id(void) { auto p = ecs.component(); - test_assert(p.id() == flecs::type_id()); + test_assert(p.id() == ecs.id()); } void World_different_comp_same_name(void) { @@ -233,14 +274,14 @@ void World_implicit_reregister_after_reset(void) { ecs.entity().add(); - flecs::entity_t p_id_1 = flecs::type_id(); + flecs::entity_t p_id_1 = ecs.id(); // Simulate different binary flecs::_::type::reset(); ecs.entity().add(); - flecs::entity_t p_id_2 = flecs::type_id(); + flecs::entity_t p_id_2 = ecs.id(); test_assert(p_id_1 == p_id_2); } @@ -250,14 +291,14 @@ void World_reregister_after_reset_w_namespace(void) { ecs.component(); - flecs::entity_t p_id_1 = flecs::type_id(); + flecs::entity_t p_id_1 = ecs.id(); // Simulate different binary flecs::_::type::reset(); ecs.component(); - flecs::entity_t p_id_2 = flecs::type_id(); + flecs::entity_t p_id_2 = ecs.id(); test_assert(p_id_1 == p_id_2); } @@ -267,11 +308,11 @@ void World_reregister_namespace(void) { ecs.component(); - flecs::entity_t p_id_1 = flecs::type_id(); + flecs::entity_t p_id_1 = ecs.id(); ecs.component(); - flecs::entity_t p_id_2 = flecs::type_id(); + flecs::entity_t p_id_2 = ecs.id(); test_assert(p_id_1 == p_id_2); } @@ -304,12 +345,12 @@ void World_reregister_after_delete(void) { test_assert(!c.is_alive()); auto d = ecs.component(); - test_assert(c == d); - test_assert(c.is_alive()); + test_assert(!c.is_alive()); + test_assert(d.is_alive()); - test_str(c.name(), "Position"); - test_str(c.path(), "::Position"); - test_str(c.symbol(), "Position"); + test_str(d.name(), "Position"); + test_str(d.path(), "::Position"); + test_str(d.symbol(), "Position"); } void World_register_component_w_reset_in_multithreaded(void) { @@ -440,6 +481,15 @@ void World_c_interop_after_reset(void) { ecs.import(); } +void World_c_interop_module_no_names(void) { + flecs::world ecs; + + ecs.import(); + + auto e_pos = ecs.lookup("test::interop::module2::Position"); + test_assert(e_pos.id() != 0); +} + void World_implicit_register_w_new_world(void) { { flecs::world ecs; @@ -471,7 +521,7 @@ void World_implicit_register_after_reset_register_w_custom_name(void) { flecs::entity c = ecs.component("MyPosition"); test_str(c.name(), "MyPosition"); - flecs::reset(); // Simulate working across boundary + flecs::_::type().reset(); // Simulate working across boundary auto e = ecs.entity().add(); test_assert(e.has()); @@ -484,7 +534,7 @@ void World_register_after_reset_register_w_custom_name(void) { flecs::entity c1 = ecs.component("MyPosition"); test_str(c1.name(), "MyPosition"); - flecs::reset(); // Simulate working across boundary + flecs::_::type().reset(); // Simulate working across boundary flecs::entity c2 = ecs.component(); test_str(c2.name(), "MyPosition"); @@ -496,7 +546,7 @@ void World_register_builtin_after_reset(void) { auto c1 = ecs.component(); test_assert(c1 == ecs_id(EcsComponent)); - flecs::reset(); // Simulate working across boundary + flecs::_::type().reset(); // Simulate working across boundary auto c2 = ecs.component(); test_assert(c2 == ecs_id(EcsComponent)); @@ -508,7 +558,7 @@ void World_register_meta_after_reset(void) { auto c1 = ecs.component(); - flecs::reset(); // Simulate working across boundary + flecs::_::type().reset(); // Simulate working across boundary auto c2 = ecs.component() .member("x") @@ -1555,27 +1605,7 @@ void World_make_alive(void) { } void World_reset_all(void) { - flecs::entity pos, vel; - { - flecs::world ecs; - pos = ecs.component(); - vel = ecs.component(); - } - - test_assert(flecs::type_id() == pos); - test_assert(flecs::type_id() == vel); - - flecs::reset(); - - test_assert(flecs::type_id() == 0); - - /* Register components in opposite order, should result in different ids */ - { - flecs::world ecs; - test_assert(ecs.component() != 0); - test_assert(ecs.component() != 0); - } } void World_get_tick(void) { @@ -1696,7 +1726,7 @@ void World_reregister_after_reset_w_hooks_and_in_use(void) { ecs.entity().add(); test_int(1, Pod::ctor_invoked); - flecs::reset(); + flecs::_::type().reset(); ecs.component(); @@ -1712,7 +1742,7 @@ void World_reregister_after_reset_w_hooks_and_in_use_implicit(void) { ecs.entity().add(); test_int(1, Pod::ctor_invoked); - flecs::reset(); + flecs::_::type().reset(); ecs.entity().add(); test_int(2, Pod::ctor_invoked); @@ -1884,8 +1914,8 @@ void World_register_nested_component_in_module(void) { ecs.import(); - test_assert(flecs::type_id() != 0); - test_assert(flecs::type_id() != 0); + test_assert(ecs.id() != 0); + test_assert(ecs.id() != 0); flecs::entity foo = ecs.component(); flecs::entity bar = ecs.component(); diff --git a/test/cpp/src/main.cpp b/test/cpp/src/main.cpp index ff63ac3a2b..cb47f881c2 100644 --- a/test/cpp/src/main.cpp +++ b/test/cpp/src/main.cpp @@ -404,6 +404,7 @@ void Enum_component_registered_as_enum(void); void Enum_mixed_auto_manual_constants(void); void Enum_enum_class_mixed_auto_manual_constants(void); void Enum_enum_child_count(void); +void Enum_multi_world_constant_ids(void); // Testsuite 'Union' void Union_add_case(void); @@ -1116,7 +1117,6 @@ void World_multi_world_empty(void); void World_multi_world_component(void); void World_multi_world_component_namespace(void); void World_multi_world_module(void); -void World_multi_world_recycled_component(void); void World_multi_world_recycled_component_different_generation(void); void World_type_id(void); void World_different_comp_same_name(void); @@ -1133,6 +1133,7 @@ void World_reimport_module_new_world(void); void World_reimport_namespaced_module(void); void World_c_interop_module(void); void World_c_interop_after_reset(void); +void World_c_interop_module_no_names(void); void World_implicit_register_w_new_world(void); void World_implicit_register_after_reset_register_w_custom_name(void); void World_register_after_reset_register_w_custom_name(void); @@ -1198,7 +1199,6 @@ void World_is_valid(void); void World_exists(void); void World_get_alive(void); void World_make_alive(void); -void World_reset_all(void); void World_get_tick(void); void World_register_from_scope(void); void World_register_nested_from_scope(void); @@ -2976,6 +2976,10 @@ bake_test_case Enum_testcases[] = { { "enum_child_count", Enum_enum_child_count + }, + { + "multi_world_constant_ids", + Enum_multi_world_constant_ids } }; @@ -5746,10 +5750,6 @@ bake_test_case World_testcases[] = { "multi_world_module", World_multi_world_module }, - { - "multi_world_recycled_component", - World_multi_world_recycled_component - }, { "multi_world_recycled_component_different_generation", World_multi_world_recycled_component_different_generation @@ -5814,6 +5814,10 @@ bake_test_case World_testcases[] = { "c_interop_after_reset", World_c_interop_after_reset }, + { + "c_interop_module_no_names", + World_c_interop_module_no_names + }, { "implicit_register_w_new_world", World_implicit_register_w_new_world @@ -6074,10 +6078,6 @@ bake_test_case World_testcases[] = { "make_alive", World_make_alive }, - { - "reset_all", - World_reset_all - }, { "get_tick", World_get_tick @@ -6926,7 +6926,7 @@ static bake_test_suite suites[] = { "Enum", NULL, NULL, - 39, + 40, Enum_testcases }, { @@ -7033,7 +7033,7 @@ static bake_test_suite suites[] = { "World", NULL, NULL, - 117, + 116, World_testcases }, {