diff --git a/code/common/world/world.c b/code/common/world/world.c index 0d24a6d..33e5791 100644 --- a/code/common/world/world.c +++ b/code/common/world/world.c @@ -151,7 +151,7 @@ int32_t world_init(int32_t seed, uint16_t block_size, uint16_t chunk_size, uint1 ECS_IMPORT(world.ecs, General); ECS_IMPORT(world.ecs, Net); - world.ecs_update = ecs_query_new(world.ecs, "Net.ClientInfo, general.Position"); + world.ecs_update = ecs_query_new(world.ecs, "net.ClientInfo, general.Position"); int32_t world_build_status = world_gen(); ZPL_ASSERT(world_build_status >= 0); @@ -218,7 +218,7 @@ static void world_tracker_update(uint8_t ticker, uint32_t freq, uint8_t radius) int32_t world_update() { - ecs_progress(world.ecs, platform_frametime()); + ecs_progress(world.ecs, 0.0f); world_tracker_update(0, WORLD_TRACKER_UPDATE_FAST_MS, 2); world_tracker_update(1, WORLD_TRACKER_UPDATE_NORMAL_MS, 4); diff --git a/code/game/source/game.c b/code/game/source/game.c index c889c33..5297420 100644 --- a/code/game/source/game.c +++ b/code/game/source/game.c @@ -100,7 +100,6 @@ void flecs_dash_init() { ECS_IMPORT(world_ecs(), FlecsSystemsCivetweb); ecs_set(world_ecs(), 0, EcsDashServer, {.port = 27001}); - // ecs_set_target_fps(world_ecs(), 60); } void game_init(int8_t play_mode, uint32_t num_viewers, int32_t seed, uint16_t block_size, uint16_t chunk_size, uint16_t chunk_amount, int8_t is_dash_enabled) { @@ -119,6 +118,7 @@ void game_init(int8_t play_mode, uint32_t num_viewers, int32_t seed, uint16_t bl world_setup_pkt_handlers(pkt_reader, sp_pkt_writer); world_init(seed, block_size, chunk_size, chunk_amount); if (is_dash_enabled) flecs_dash_init(); + ecs_set_target_fps(world_ecs(), 60); } for (uint32_t i = 0; i < num_viewers; i++) { diff --git a/code/vendors/flecs/CHANGELOG.md b/code/vendors/flecs/CHANGELOG.md new file mode 100644 index 0000000..6990b65 --- /dev/null +++ b/code/vendors/flecs/CHANGELOG.md @@ -0,0 +1,142 @@ +flecs +CHANGELOG +======================================================================= + +RELEASE NOTES: https://github.com/SanderMertens/flecs/releases +REPO: https://github.com/SanderMertens/flecs/commits/master +COMMUNITY: https://discord.gg/MRSAZqb +DOCS: https://flecs.docsforge.com/ +FAQ: https://github.com/SanderMertens/flecs/blob/master/docs/FAQ.md +QUICKSTART: https://github.com/SanderMertens/flecs/blob/master/docs/Quickstart.md +MANUAL: https://github.com/SanderMertens/flecs/blob/master/docs/Manual.md + +----------------------------------------------------------------------- + VERSION 2.3.2 (Released 2021-02-23) +----------------------------------------------------------------------- + +## Improvements +- replace iostream with initializer_list (C++ API, thanks @ikrima!) +- ensure entity::m_id is initialized to 0 (C++ API, thanks @ikrima!) +- use ecs_os_malloc instead of new (C++ API, thanks @ikrima!) +- remove superfluous copies of lambda functions (C++ API, thanks @ikrima!) +- add CHANGELOG (thanks @ikrima!) + +## Bugfixes +- fix matching for queries with shared componnents when base entity is deleted +- fix various issues with component registration in C++ +- fix issue with setting target FPS on Apple M1 (thanks @prime31!) +- fix issues with CMake file (thanks @Spacelm!) +- fix crash when creating & deleting queries +- guarantee that id returned by new_component_id is unused + + +----------------------------------------------------------------------- + VERSION 2.3.1 (Released 2021-02-02) +----------------------------------------------------------------------- + +## Improvements +- Improved lerp example +- Added OS API example C++ (thanks @mcmlevi!) +- Upgraded cmake buildfiles (thanks @rawbby!) +- Clarified text in README describing API/ABI stability + +## Bugfixes +- Fix crash when using overlapping UnSet systems +- Fix issue with passing incorrect row to UnSet systems +- Added .hpp files to cmake install +- Fixed issue with using get_mut with traits + + +----------------------------------------------------------------------- + VERSION 2.3.0 (Released 2021-01-17) +----------------------------------------------------------------------- + +## Highlights +- Big performance improvements & reduced memory usage for applications with lots of tables, such as when using hierarchies +- Component enabling/disabling allows for quick component mutations without moving entities between archetypes +- New statistics addon for retrieving metrics from running world (replaces stats module) + +Thanks to @randy408, @sh-dave, @kevinresol, @jasonliang-dev and @Alexandre-P-J for submitting PRs! 🎉 + +Thanks to @ikrima and @jtferson for writing two awesome testimonials on using Flecs with Unreal Engine 4 🙏 : +- https://bebylon.dev/blog/ecs-flecs-ue4/ +- https://jtferson.github.io/blog/flecs_and_unreal/ + +Thanks to the new Flecs sponsors ❤️ : + - @Zifkan + - @TFlippy + - @Hexlord + +## Breaking changes +- calling ecs_get for a tag will no longer return NULL, but will assert +- statistics module is removed in favor of easier to use statistics addon +- unused table_offset member is removed from iterator + +## Deprecated features +- ecs_entity() macro is now deprecated, use ecs_typeid() + +## Features +- Direct access addon, which provides fast access to underlying storage +- Added singleton API to C++ world class +- Added orphaning for subqueries, which allows an application to check if a parent query still exists +- Added ecs_get_typeid function to get the component id for traits +- The type used for time keeping is now customizable +- New statistics addon for retrieving metrics from running world +- Added get_parent function that accepts entity/tag +- Added component enabling/disabling +- Added support for sorting on shared components + +## Improvements +- Improved ecs_delete performance (reduced entity index accesses from 2 to 1) +- C & C++ code compiles warning-free with more warning flags, for more compiler versions & more platforms +- Improved error messages when application passes invalid entity id +- Made include paths relative so source code is easier to integrate with bazel +- Fixed typos in documentation +- Improve error message when trying to add 0 as base entity (using ECS_INSTANCEOF) +- Add check for conflicting source modifier in OR expressions +- Extended documentation, added new examples and fixed typos +- Removed dead/redundant code +- Improved performance of ecs_get +- Add sanitizer & custom builds to CI, remove Travis CI builds +- Don't add Name component when provided name for entity is NULL +- Allow flecs::system instances to be (re)created from entity id +- Added godbolt "try online" badge to README +- Improve allocation strategy of vector datastructure +- Improved performance for checking if entity id is a number +- Added missing query functions to C++ API for sorting +- Improved performance of table creation by two orders of magnitude +- Reduced memory footprint of table graph by a factor 60 +- Added example to demonstrate world merging with direct access API +- Added more inline documentation for datastructures +- Added assert when trying to instantiate child as instance +- Improve errors when invalid operation is invoked on iterator +- Throw assert when invoking ecs_modified on entity that does not have the component +- Allow for type lookups in systems, as long as the type already exists +- Add return types to ecs_defer_begin and ecs_defer_end to obtain defer status of world +- Remove redundant table_offset member from ecs_iter_t +- Improved portability of POSIX OS API example + +## Bugfixes +- Fixed issues with subqueries and query rematching +- Corrected wrong return type of ecs_column_size (was ecs_entity_t, is now size_t) +- Add missing \0 when appending to application buffer with strbuf datastructure +- Fixed crash when instantiating an empty prefab +- Fixed issue with shared tags and queries using each() in the C++ API +- Fixed issue with instantiating empty child table +- Fixed issue with ecs_bulk_new and traits +- Fixed issue when using ecs_entity_str with small buffers +- Fixed bug when adding trait to entity with switch +- Fixed name conflicts in amalgamated source +- Fixed path lookup in ecs_import +- Fixed EXPORT macro's in OS API examples +- Fixed issue with restoring worlds that have recycled ids +- Added missing EXPORT macro's to API functions +- Fixed assert after deleting entities from restored world +- Removed obsolete assert from ecs_modified +- Added missing statement to finalize system in C++ +- Fixed issues with removing case values +- Fixed issues in C++ API with implicit component registration +- Fixed assert when removing from switch list +- Fixed bug where obtaining type from entity would generate a new entity id +- Fixed incorrect description of ecs_delete in manual +- Fixed issues with custom builds diff --git a/code/vendors/flecs/flecs.c b/code/vendors/flecs/flecs.c index 6639317..5344a7f 100644 --- a/code/vendors/flecs/flecs.c +++ b/code/vendors/flecs/flecs.c @@ -646,6 +646,7 @@ struct ecs_world_t { bool quit_workers; /* Signals worker threads to quit */ bool in_progress; /* Is world being progressed */ bool is_merging; /* Is world currently being merged */ + bool is_fini; /* Is the world being cleaned up? */ bool auto_merge; /* Are stages auto-merged by ecs_progress */ bool measure_frame_time; /* Time spent on each frame */ bool measure_system_time; /* Time spent by each system */ @@ -678,7 +679,7 @@ ecs_type_t ecs_bootstrap_type( ecs_new_component(world, ecs_typeid(name), #name, sizeof(name), ECS_ALIGNOF(name)) #define ecs_bootstrap_tag(world, name)\ - ecs_set(world, name, EcsName, {.value = &#name[ecs_os_strlen("Ecs")], .symbol = #name});\ + ecs_set(world, name, EcsName, {.value = &#name[ecs_os_strlen("Ecs")], .symbol = (char*)#name});\ ecs_add_entity(world, name, ECS_CHILDOF | ecs_get_scope(world)) @@ -998,6 +999,7 @@ void ecs_table_clear_silent( /* Clear table data. Don't call OnRemove handlers. */ void ecs_table_clear_data( + ecs_world_t *world, ecs_table_t *table, ecs_data_t *data); @@ -2102,6 +2104,7 @@ void register_un_set( int32_t matched_table_index) { (void)world; + table->flags |= EcsTableHasUnSet; add_monitor(&table->un_set_all, query, matched_table_index); } @@ -2273,6 +2276,10 @@ void dtor_component( int32_t row, int32_t count) { + if (!count) { + return; + } + /* An old component is destructed */ ecs_xtor_t dtor; if (cdata && (dtor = cdata->lifecycle.dtor)) { @@ -2311,27 +2318,25 @@ static void run_remove_actions( ecs_world_t * world, ecs_table_t * table, - ecs_data_t * data, int32_t row, - int32_t count, - bool dtor_only) + int32_t count) { if (count) { - if (!dtor_only) { - ecs_run_monitors(world, table, NULL, row, count, table->un_set_all); - } - - dtor_all_components(world, table, data, row, count); + ecs_run_monitors(world, table, NULL, row, count, table->un_set_all); } } void ecs_table_clear_data( - ecs_table_t * table, - ecs_data_t * data) + ecs_world_t *world, + ecs_table_t *table, + ecs_data_t *data) { if (!data) { return; } + + int32_t count = ecs_table_data_count(data); + dtor_all_components(world, table, data, 0, count); ecs_column_t *columns = data->columns; if (columns) { @@ -2384,7 +2389,7 @@ void ecs_table_clear_silent( int32_t count = ecs_vector_count(data->entities); - ecs_table_clear_data(table, table->data); + ecs_table_clear_data(world, table, data); if (count) { ecs_table_activate(world, table, 0, false); @@ -2399,18 +2404,18 @@ void ecs_table_clear( ecs_table_t * table) { ecs_data_t *data = ecs_table_get_data(table); + if (data) { - run_remove_actions( - world, table, data, 0, ecs_table_data_count(data), false); + run_remove_actions(world, table, 0, ecs_table_data_count(data)); ecs_entity_t *entities = ecs_vector_first(data->entities, ecs_entity_t); int32_t i, count = ecs_vector_count(data->entities); for(i = 0; i < count; i ++) { ecs_eis_delete(world, entities[i]); } - } - ecs_table_clear_silent(world, table); + ecs_table_clear_silent(world, table); + } } /* Unset all components in table. This function is called before a table is @@ -2435,11 +2440,10 @@ void ecs_table_free( (void)world; ecs_data_t *data = ecs_table_get_data(table); if (data) { - run_remove_actions( - world, table, data, 0, ecs_table_data_count(data), false); + run_remove_actions(world, table, 0, ecs_table_data_count(data)); + ecs_table_clear_data(world, table, data); } - ecs_table_clear_data(table, table->data); ecs_table_clear_edges(world, table); ecs_os_free(table->lo_edges); @@ -3430,8 +3434,8 @@ void ecs_table_swap( static void merge_vector( - ecs_vector_t ** dst_out, - ecs_vector_t * src, + ecs_vector_t **dst_out, + ecs_vector_t *src, int16_t size, int16_t alignment) { @@ -3463,6 +3467,61 @@ void merge_vector( } } +static +void merge_column( + ecs_world_t *world, + ecs_table_t *table, + ecs_data_t *data, + int32_t column_id, + ecs_vector_t *src) +{ + ecs_entity_t *entities = ecs_vector_first(data->entities, ecs_entity_t); + ecs_c_info_t *c_info = table->c_info[column_id]; + ecs_column_t *column = &data->columns[column_id]; + ecs_vector_t *dst = column->data; + int16_t size = column->size; + int16_t alignment = column->alignment; + int32_t dst_count = ecs_vector_count(dst); + + if (!dst_count) { + if (dst) { + ecs_vector_free(dst); + } + + column->data = src; + + /* If the new table is not empty, copy the contents from the + * src into the dst. */ + } else { + int32_t src_count = ecs_vector_count(src); + ecs_vector_set_count_t(&dst, size, alignment, dst_count + src_count); + column->data = dst; + + /* Construct new values */ + if (c_info) { + ctor_component( + world, c_info, column, entities, dst_count, src_count); + } + + void *dst_ptr = ecs_vector_first_t(dst, size, alignment); + void *src_ptr = ecs_vector_first_t(src, size, alignment); + + dst_ptr = ECS_OFFSET(dst_ptr, size * dst_count); + + /* Move values into column */ + ecs_move_t move; + if (c_info && (move = c_info->lifecycle.move)) { + move(world, c_info->component, entities, entities, + dst_ptr, src_ptr, ecs_to_size_t(size), src_count, + c_info->lifecycle.ctx); + } else { + ecs_os_memcpy(dst_ptr, src_ptr, size * src_count); + } + + ecs_vector_free(src); + } +} + static void merge_table_data( ecs_world_t * world, @@ -3517,10 +3576,8 @@ void merge_table_data( } if (new_component == old_component) { - merge_vector( - &new_columns[i_new].data, old_columns[i_old].data, size, - alignment); - + merge_column(world, new_table, new_data, i_new, + old_columns[i_old].data); old_columns[i_old].data = NULL; /* Mark component column as dirty */ @@ -3643,7 +3700,7 @@ ecs_data_t* ecs_table_merge( /* If there is nothing to merge to, just clear the old table */ if (!new_table) { - ecs_table_clear_data(old_table, old_data); + ecs_table_clear_data(world, old_table, old_data); return NULL; } @@ -3711,9 +3768,8 @@ void ecs_table_replace_data( if (table_data) { prev_count = ecs_vector_count(table_data->entities); - run_remove_actions( - world, table, table_data, 0, ecs_table_data_count(table_data), false); - ecs_table_clear_data(table, table_data); + run_remove_actions(world, table, 0, ecs_table_data_count(table_data)); + ecs_table_clear_data(world, table, table_data); } if (data) { @@ -3801,6 +3857,10 @@ void ecs_table_notify( ecs_table_t * table, ecs_table_event_t * event) { + if (world->is_fini) { + return; + } + switch(event->kind) { case EcsTableQueryMatch: register_query( @@ -4211,7 +4271,6 @@ void ecs_run_monitors( } ecs_assert(!(dst_table->flags & EcsTableIsPrefab), ECS_INTERNAL_ERROR, NULL); - (void)dst_table; if (!v_src_monitors) { ecs_vector_each(v_dst_monitors, ecs_matched_query_t, monitor, { @@ -4241,7 +4300,7 @@ void ecs_run_monitors( } } - if (src->query->system == system) { + if (src && src->query->system == system) { continue; } @@ -4830,7 +4889,7 @@ int32_t move_entity( /* If entity was moved, invoke UnSet monitors for each component that * the entity no longer has */ ecs_run_monitors(world, dst_table, src_table->un_set_all, - dst_row, 1, dst_table->un_set_all); + src_row, 1, dst_table->un_set_all); ecs_run_remove_actions( world, src_table, src_data, src_row, 1, removed, false); @@ -5473,13 +5532,21 @@ ecs_entity_t ecs_new_component_id( ecs_assert(ecs_vector_count(world->workers) <= 1, ECS_INVALID_WHILE_ITERATING, NULL); } + + ecs_entity_t id; + + if (world->stats.last_component_id < ECS_HI_COMPONENT_ID) { + do { + id = world->stats.last_component_id ++; + } while (ecs_exists(world, id) && id < ECS_HI_COMPONENT_ID); + } if (world->stats.last_component_id >= ECS_HI_COMPONENT_ID) { /* If the low component ids are depleted, return a regular entity id */ - return ecs_new_id(world); - } else { - return world->stats.last_component_id ++; + id = ecs_new_id(world); } + + return id; } ecs_entity_t ecs_new_w_type( @@ -5678,10 +5745,15 @@ void ecs_delete( ecs_record_t *r = ecs_sparse_remove_get( world->store.entity_index, ecs_record_t, entity); if (r) { - ecs_entity_info_t info; + ecs_entity_info_t info = {0}; set_info_from_record(entity, &info, r); if (info.is_watched) { ecs_delete_children(world, entity); + + if (r->table) { + ecs_entities_t to_remove = ecs_type_to_entities(r->table->type); + update_component_monitors(world, entity, NULL, &to_remove); + } } /* If entity has components, remove them */ @@ -6469,7 +6541,7 @@ void flush_bulk_new( int c, c_count = op->components.count; for (c = 0; c < c_count; c ++) { ecs_entity_t component = components[c]; - const EcsComponent *cptr = ecs_get(world, component, EcsComponent); + const EcsComponent *cptr = ecs_component_from_id(world, component); ecs_assert(cptr != NULL, ECS_INTERNAL_ERROR, NULL); size_t size = ecs_to_size_t(cptr->size); void *ptr, *data = bulk_data[c]; @@ -6844,7 +6916,7 @@ bool ecs_defer_bulk_new( defer_data = ecs_os_malloc(ECS_SIZEOF(void*) * c_count); for (c = 0; c < c_count; c ++) { ecs_entity_t comp = components[c]; - const EcsComponent *cptr = ecs_get(world, comp, EcsComponent); + const EcsComponent *cptr = ecs_component_from_id(world, comp); ecs_assert(cptr != NULL, ECS_INVALID_PARAMETER, NULL); ecs_size_t size = cptr->size; @@ -6931,7 +7003,7 @@ bool ecs_defer_set( if (stage->defer) { world->set_count ++; if (!size) { - const EcsComponent *cptr = ecs_get(world, component, EcsComponent); + const EcsComponent *cptr = ecs_component_from_id(world, component); ecs_assert(cptr != NULL, ECS_INVALID_PARAMETER, NULL); size = cptr->size; } @@ -8873,9 +8945,11 @@ ecs_entity_t ecs_new_module( e = ecs_new_from_fullpath(world, module_path); EcsName *name_ptr = ecs_get_mut(world, e, EcsName, NULL); - name_ptr->symbol = name; + ecs_os_free(name_ptr->symbol); - ecs_os_free(module_path); + /* Assign full path to symbol. This allows for modules to be redefined + * in C++ without causing name conflicts */ + name_ptr->symbol = module_path; } ecs_entity_t result = ecs_new_component(world, e, NULL, size, alignment); @@ -9195,7 +9269,7 @@ void ecs_gauge_reduce( } if ((src->max[t] > dst->max[t_dst])) { dst->max[t_dst] = src->max[t]; - } + } } } @@ -9219,7 +9293,12 @@ void ecs_get_world_stats( record_counter(&s->pipeline_build_count_total, t, world->stats.pipeline_build_count_total); record_counter(&s->systems_ran_frame, t, world->stats.systems_ran_frame); - record_gauge(&s->fps, t, 1.0f / (delta_world_time / (float)delta_frame_count)); + if (delta_world_time != 0.0 && delta_frame_count != 0.0) { + record_gauge( + &s->fps, t, 1.0f / (delta_world_time / (float)delta_frame_count)); + } else { + record_gauge(&s->fps, t, 0); + } record_gauge(&s->entity_count, t, ecs_sparse_count(world->store.entity_index)); record_gauge(&s->component_count, t, ecs_count_entity(world, ecs_typeid(EcsComponent))); @@ -9630,6 +9709,7 @@ void ecs_snapshot_restore( for (t = 0; t < table_count; t ++) { ecs_table_t *table = ecs_sparse_get(world->store.tables, ecs_table_t, t); + if (table->flags & EcsTableHasBuiltins) { continue; } @@ -9659,7 +9739,7 @@ void ecs_snapshot_restore( /* Always delete entity, so that even if the entity is * in the current table, there won't be duplicates */ - ecs_table_delete(world, r->table, data, row, false); + ecs_table_delete(world, r->table, data, row, true); } else { ecs_eis_set_generation(world, *e_ptr); } @@ -9785,7 +9865,7 @@ void ecs_snapshot_free( int32_t i, count = ecs_vector_count(snapshot->tables); for (i = 0; i < count; i ++) { ecs_table_leaf_t *leaf = &tables[i]; - ecs_table_clear_data(leaf->table, leaf->data); + ecs_table_clear_data(snapshot->world, leaf->table, leaf->data); ecs_os_free(leaf->data); } @@ -11233,6 +11313,7 @@ ecs_world_t *ecs_mini(void) { world->quit_workers = false; world->in_progress = false; world->is_merging = false; + world->is_fini = false; world->auto_merge = true; world->measure_frame_time = false; world->measure_system_time = false; @@ -11581,9 +11662,12 @@ void fini_misc( int ecs_fini( ecs_world_t *world) { - assert(world->magic == ECS_WORLD_MAGIC); - assert(!world->in_progress); - assert(!world->is_merging); + ecs_assert(world->magic == ECS_WORLD_MAGIC, ECS_INVALID_PARAMETER, NULL); + ecs_assert(!world->in_progress, ECS_INVALID_OPERATION, NULL); + ecs_assert(!world->is_merging, ECS_INVALID_OPERATION, NULL); + ecs_assert(!world->is_fini, ECS_INVALID_OPERATION, NULL); + + world->is_fini = true; fini_unset_tables(world); @@ -17249,10 +17333,22 @@ void ecs_query_free( }); ecs_vector_each(query->empty_tables, ecs_matched_table_t, table, { + if (!(query->flags & EcsQueryIsSubquery)) { + ecs_table_notify(world, table->iter_data.table, &(ecs_table_event_t){ + .kind = EcsTableQueryUnmatch, + .query = query + }); + } free_matched_table(table); }); ecs_vector_each(query->tables, ecs_matched_table_t, table, { + if (!(query->flags & EcsQueryIsSubquery)) { + ecs_table_notify(world, table->iter_data.table, &(ecs_table_event_t){ + .kind = EcsTableQueryUnmatch, + .query = query + }); + } free_matched_table(table); }); @@ -19887,7 +19983,7 @@ uint64_t ecs_os_time_now(void) { QueryPerformanceCounter(&qpc_t); now = (uint64_t)(qpc_t.QuadPart / _ecs_os_time_win_freq); #elif defined(__APPLE__) && defined(__MACH__) - now = mach_absolute_time(); + now = (uint64_t) int64_muldiv((int64_t)mach_absolute_time(), (int64_t)_ecs_os_time_osx_timebase.numer, (int64_t)_ecs_os_time_osx_timebase.denom); #else struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); @@ -22408,10 +22504,14 @@ void ecs_set_symbol( const char *e_name = ecs_name_from_symbol(world, name); - ecs_set(world, e, EcsName, { - .value = e_name, - .symbol = name - }); + EcsName *name_ptr = ecs_get_mut(world, e, EcsName, NULL); + name_ptr->value = e_name; + + if (name_ptr->symbol) { + ecs_os_free(name_ptr->symbol); + } + + name_ptr->symbol = ecs_os_strdup(name); } ecs_entity_t ecs_lookup_w_id( @@ -22518,6 +22618,7 @@ ecs_entity_t ecs_new_component( ecs_assert(world != NULL, ECS_INVALID_PARAMETER, NULL); assert(world->magic == ECS_WORLD_MAGIC); bool in_progress = world->in_progress; + bool found = false; /* If world is in progress component may be registered, but only when not * in multithreading mode. */ @@ -22533,7 +22634,7 @@ ecs_entity_t ecs_new_component( ecs_entity_t result = ecs_lookup_w_id(world, e, name); if (!result) { result = ecs_new_component_id(world); - ecs_set_symbol(world, result, name); + found = true; } /* ecs_new_component_id does not add the scope, so add it explicitly */ @@ -22542,6 +22643,10 @@ ecs_entity_t ecs_new_component( ecs_add_entity(world, result, ECS_CHILDOF | scope); } + if (found) { + ecs_set_symbol(world, result, name); + } + bool added = false; EcsComponent *ptr = ecs_get_mut(world, result, EcsComponent, &added); @@ -22622,6 +22727,7 @@ static ECS_CTOR(EcsName, ptr, { static ECS_DTOR(EcsName, ptr, { ecs_os_free(ptr->alloc_value); + ecs_os_free(ptr->symbol); ptr->value = NULL; ptr->alloc_value = NULL; ptr->symbol = NULL; @@ -22632,6 +22738,11 @@ static ECS_COPY(EcsName, dst, src, { ecs_os_free(dst->alloc_value); dst->alloc_value = NULL; } + + if (dst->symbol) { + ecs_os_free(dst->symbol); + dst->symbol = NULL; + } if (src->alloc_value) { dst->alloc_value = ecs_os_strdup(src->alloc_value); @@ -22640,10 +22751,20 @@ static ECS_COPY(EcsName, dst, src, { dst->alloc_value = NULL; dst->value = src->value; } - dst->symbol = src->symbol; + + if (src->symbol) { + dst->symbol = ecs_os_strdup(src->symbol); + } }) static ECS_MOVE(EcsName, dst, src, { + if (dst->alloc_value) { + ecs_os_free(dst->alloc_value); + } + if (dst->symbol) { + ecs_os_free(dst->symbol); + } + dst->value = src->value; dst->alloc_value = src->alloc_value; dst->symbol = src->symbol; @@ -22691,7 +22812,7 @@ void _bootstrap_component( c_info[index].size = size; c_info[index].alignment = alignment; id_data[index].value = &id[ecs_os_strlen("Ecs")]; /* Skip prefix */ - id_data[index].symbol = id; + id_data[index].symbol = ecs_os_strdup(id); id_data[index].alloc_value = NULL; } @@ -22771,9 +22892,16 @@ void ecs_bootstrap( ecs_table_t *table = bootstrap_component_table(world); assert(table != NULL); + bootstrap_component(world, table, EcsName); bootstrap_component(world, table, EcsComponent); bootstrap_component(world, table, EcsType); - bootstrap_component(world, table, EcsName); + + ecs_set_component_actions(world, EcsName, { + .ctor = ecs_ctor(EcsName), + .dtor = ecs_dtor(EcsName), + .copy = ecs_copy(EcsName), + .move = ecs_move(EcsName) + }); world->stats.last_component_id = EcsFirstUserComponentId; world->stats.last_id = EcsFirstUserEntityId; @@ -22789,13 +22917,6 @@ void ecs_bootstrap( ecs_bootstrap_tag(world, EcsHidden); ecs_bootstrap_tag(world, EcsDisabled); - ecs_set_component_actions(world, EcsName, { - .ctor = ecs_ctor(EcsName), - .dtor = ecs_dtor(EcsName), - .copy = ecs_copy(EcsName), - .move = ecs_move(EcsName) - }); - /* Initialize scopes */ ecs_set(world, EcsFlecs, EcsName, {.value = "flecs"}); ecs_add_entity(world, EcsFlecs, EcsModule); diff --git a/code/vendors/flecs/flecs.h b/code/vendors/flecs/flecs.h index d0fd76e..b34a33e 100644 --- a/code/vendors/flecs/flecs.h +++ b/code/vendors/flecs/flecs.h @@ -18,6 +18,7 @@ /* FLECS_CUSTOM_BUILD should be defined when manually selecting features */ // #define FLECS_CUSTOM_BUILD +/* If this is a regular, non-custom build, build all modules and addons. */ #ifndef FLECS_CUSTOM_BUILD /* Modules */ #define FLECS_SYSTEM @@ -748,7 +749,7 @@ ecs_vector_t* _ecs_vector_copy( #ifdef __cplusplus #ifndef FLECS_NO_CPP -#include +#include namespace flecs { @@ -1179,7 +1180,7 @@ void ecs_bitset_swap( * * Note that while the implementation is a hashmap, it can only compute hashes * for the provided 64 bit keys. This means that the provided keys must always - * be unique. If the provided keys are hashes themselves, it is the + * be unique. If the provided keys are hashes themselves, it is the * responsibility of the user to ensure that collisions are handled. * * In debug mode the map verifies that the type provided to the map functions @@ -1210,7 +1211,7 @@ typedef struct ecs_map_iter_t { FLECS_API ecs_map_t * _ecs_map_new( ecs_size_t elem_size, - ecs_size_t alignment, + ecs_size_t alignment, int32_t elem_count); #define ecs_map_new(T, elem_count)\ @@ -1312,19 +1313,19 @@ void* _ecs_map_next_ptr( /** Grow number of buckets in the map for specified number of elements. */ FLECS_API void ecs_map_grow( - ecs_map_t *map, + ecs_map_t *map, int32_t elem_count); /** Set number of buckets in the map for specified number of elements. */ FLECS_API void ecs_map_set_size( - ecs_map_t *map, + ecs_map_t *map, int32_t elem_count); /** Return memory occupied by map. */ FLECS_API void ecs_map_memory( - ecs_map_t *map, + ecs_map_t *map, int32_t *allocd, int32_t *used); @@ -1349,14 +1350,15 @@ void ecs_map_memory( #ifdef __cplusplus #ifndef FLECS_NO_CPP -#include +#include +#include namespace flecs { template class map { public: - map(int32_t count = 0) { + map(int32_t count = 0) { init(count); } @@ -1541,7 +1543,7 @@ extern "C" { * there is hardly any overhead, while for large strings the overhead is offset * by the reduced time spent on copying memory. */ - + #ifndef FLECS_STRBUF_H_ #define FLECS_STRBUF_H_ @@ -1968,10 +1970,18 @@ FLECS_API void ecs_os_set_api_defaults(void); /* Memory management */ -#define ecs_os_malloc(size) ecs_os_api.malloc_(size); -#define ecs_os_free(ptr) ecs_os_api.free_(ptr); +#ifndef ecs_os_malloc +#define ecs_os_malloc(size) ecs_os_api.malloc_(size) +#endif +#ifndef ecs_os_free +#define ecs_os_free(ptr) ecs_os_api.free_(ptr) +#endif +#ifndef ecs_os_realloc #define ecs_os_realloc(ptr, size) ecs_os_api.realloc_(ptr, size) +#endif +#ifndef ecs_os_calloc #define ecs_os_calloc(size) ecs_os_api.calloc_(size) +#endif #if defined(_MSC_VER) || defined(__MINGW32__) #define ecs_os_alloca(size) _alloca((size_t)(size)) #else @@ -1979,7 +1989,9 @@ void ecs_os_set_api_defaults(void); #endif /* Strings */ +#ifndef ecs_os_strdup #define ecs_os_strdup(str) ecs_os_api.strdup_(str) +#endif #define ecs_os_strlen(str) (ecs_size_t)strlen(str) #define ecs_os_strcmp(str1, str2) strcmp(str1, str2) #define ecs_os_strncmp(str1, str2, num) strncmp(str1, str2, (size_t)(num)) @@ -3003,7 +3015,7 @@ int32_t ecs_type_trait_index_of( /** Entity name. */ typedef struct EcsName { const char *value; /**< Entity name */ - const char *symbol; /**< Optional symbol name, if it differs from name */ + char *symbol; /**< Optional symbol name, if it differs from name */ char *alloc_value; /**< If set, value will be freed on destruction */ } EcsName; @@ -7805,6 +7817,38 @@ FLECS_API void ecs_gauge_reduce( #include #include #include +#include + +// Macros so that C++ new calls can allocate using ecs_os_api memory allocation functions +// Rationale: +// - Using macros here instead of a templated function bc clients might override ecs_os_malloc +// to contain extra debug info like source tracking location. Using a template function +// in that scenario would collapse all source location into said function vs. the +// actual call site +// - FLECS_PLACEMENT_NEW(): exists to remove any naked new calls/make it easy to identify any regressions +// by grepping for new/delete +#define FLECS_PLACEMENT_NEW(_ptr, _type) ::new(flecs::_::placement_new_tag, _ptr) _type +#define FLECS_NEW(_type) FLECS_PLACEMENT_NEW(ecs_os_malloc(sizeof(_type)), _type) +#define FLECS_DELETE(_ptr) \ + do { \ + if (_ptr) { \ + flecs::_::destruct_obj(_ptr); \ + ecs_os_free(_ptr); \ + } \ + } while (false) + +namespace flecs { +namespace _ +{ +// Dummy Placement new tag to disambiguate from any other operator new overrides +struct placement_new_tag_t{}; +constexpr placement_new_tag_t placement_new_tag{}; +template inline void destruct_obj(Ty* _ptr) { _ptr->~Ty(); } +} +} + +inline void* operator new(size_t, flecs::_::placement_new_tag_t, void* _ptr) noexcept { return _ptr; } +inline void operator delete(void*, flecs::_::placement_new_tag_t, void*) noexcept { } namespace flecs { @@ -9470,7 +9514,7 @@ public: base_type& disable() const { static_cast(this)->invoke( [](world_t *world, entity_t id) { - ecs_enable(world, id, true); + ecs_enable(world, id, false); }); return *static_cast(this); } @@ -9556,10 +9600,13 @@ public: const base_type& set(T&& value) const { static_cast(this)->invoke( [&value](world_t *world, entity_t id) { + auto comp_id = _::component_info::id(world); + ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); + ecs_set_ptr_w_entity( - world, id, _::component_info::id(world), sizeof(T), &value); + world, id, comp_id, sizeof(T), &value); }); return *static_cast(this); } @@ -9575,10 +9622,13 @@ public: const base_type& set(const T& value) const { static_cast(this)->invoke( [&value](world_t *world, entity_t id) { + auto comp_id = _::component_info::id(world); + ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); + ecs_set_ptr_w_entity( - world, id, _::component_info::id(world), sizeof(T), &value); + world, id, comp_id, sizeof(T), &value); }); return *static_cast(this); } @@ -9595,11 +9645,13 @@ public: const base_type& set_trait(const T& value) const { static_cast(this)->invoke( [&value](world_t *world, entity_t id) { + auto t_id = _::component_info::id(world); + ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); + ecs_set_ptr_w_entity(world, id, - ecs_trait(_::component_info::id(world), - _::component_info::id(world)), + ecs_trait(_::component_info::id(world), t_id), sizeof(T), &value); }); return *static_cast(this); @@ -9642,15 +9694,17 @@ public: const base_type& patch(std::function func) const { static_cast(this)->invoke( [&func](world_t *world, entity_t id) { + auto comp_id = _::component_info::id(world); + ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); bool is_added; T *ptr = static_cast(ecs_get_mut_w_entity( - world, id, _::component_info::id(world), &is_added)); + world, id, comp_id, &is_added)); if (ptr) { func(*ptr, !is_added); - ecs_modified_w_entity(world, id, _::component_info::id(world)); + ecs_modified_w_entity(world, id, comp_id); } }); return *static_cast(this); @@ -9668,15 +9722,17 @@ public: const base_type& patch(std::function func) const { static_cast(this)->invoke( [&func](world_t *world, entity_t id) { + auto comp_id = _::component_info::id(world); + ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); bool is_added; T *ptr = static_cast(ecs_get_mut_w_entity( - world, id, _::component_info::id(world), &is_added)); + world, id, comp_id, &is_added)); if (ptr) { func(*ptr); - ecs_modified_w_entity(world, id, _::component_info::id(world)); + ecs_modified_w_entity(world, id, comp_id); } }); return *static_cast(this); @@ -9702,11 +9758,13 @@ public: , m_entity( entity ) , m_ref() { + auto comp_id = _::component_info::id(world); + ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); ecs_get_ref_w_entity( - m_world, &m_ref, m_entity, _::component_info::id(world)); + m_world, &m_ref, m_entity, comp_id); } const T* operator->() { @@ -9763,11 +9821,7 @@ public: */ explicit entity(world_t *world) : m_world( world ) - { - if (m_world) { - m_id = ecs_new_w_type(m_world, 0); - } - } + , m_id( world ? ecs_new_w_type(world, 0) : 0 ) { } /** Create a named entity. * Named entities can be looked up with the lookup functions. Entity names @@ -9998,10 +10052,14 @@ public: const T* get() const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); + + auto comp_id = _::component_info::id(m_world); + ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); + return static_cast( - ecs_get_w_entity(m_world, m_id, _::component_info::id(m_world))); + ecs_get_w_entity(m_world, m_id, comp_id)); } /** Get component value (untyped). @@ -10042,11 +10100,14 @@ public: T* get_mut(bool *is_added = nullptr) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); + + auto comp_id = _::component_info::id(m_world); + ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); + return static_cast( - ecs_get_mut_w_entity( - m_world, m_id, _::component_info::id(m_world), is_added)); + ecs_get_mut_w_entity(m_world, m_id, comp_id, is_added)); } /** Get mutable component value (untyped). @@ -10092,10 +10153,14 @@ public: const T* get_trait() const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); + + auto t_id = _::component_info::id(m_world); + ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); + return static_cast(ecs_get_w_entity(m_world, m_id, ecs_trait( - _::component_info::id(m_world), _::component_info::id(m_world)))); + _::component_info::id(m_world), t_id))); } /** Get trait value. @@ -10109,10 +10174,14 @@ public: const T* get_trait(flecs::entity component) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); + + auto comp_id = _::component_info::id(m_world); + ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); + return static_cast(ecs_get_w_entity(m_world, m_id, ecs_trait( - component.id(), _::component_info::id(m_world)))); + component.id(), comp_id))); } /** Get trait tag value. @@ -10129,10 +10198,14 @@ public: const C* get_trait_tag(flecs::entity trait) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); + + auto comp_id = _::component_info::id(m_world); + ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); + return static_cast(ecs_get_w_entity(m_world, m_id, ecs_trait( - _::component_info::id(m_world), trait.id()))); + comp_id, trait.id()))); } /** Get trait tag value (untyped). @@ -10166,14 +10239,16 @@ public: T* get_trait_mut(bool *is_added = nullptr) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); + + auto t_id = _::component_info::id(m_world); + ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); + return static_cast( ecs_get_mut_w_entity( - m_world, m_id, ecs_trait( - _::component_info::id(m_world), - _::component_info::id(m_world)), - is_added)); + m_world, m_id, ecs_trait(_::component_info::id(m_world), + t_id), is_added)); } /** Get mutable trait value. @@ -10191,14 +10266,15 @@ public: T* get_trait_mut(flecs::entity component, bool *is_added = nullptr) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); + + auto comp_id = _::component_info::id(m_world); + ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); + return static_cast( ecs_get_mut_w_entity( - m_world, m_id, ecs_trait( - _::component_info::id(m_world), - component.id()), - is_added)); + m_world, m_id, ecs_trait( comp_id, component.id()), is_added)); } /** Get mutable trait tag value. @@ -10236,9 +10312,13 @@ public: void modified() const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); + + auto comp_id = _::component_info::id(m_world); + ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); - ecs_modified_w_entity(m_world, m_id, _::component_info::id(m_world)); + + ecs_modified_w_entity(m_world, m_id, comp_id); } /** Signal that component was modified. @@ -10272,8 +10352,13 @@ public: ref get_ref() const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); + + // Ensure component is registered + _::component_info::id(m_world); + ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); + return ref(m_world, m_id); } @@ -10938,10 +11023,37 @@ namespace _ return typeName; } }; -#elif +#else #error "implicit component registration not supported" #endif +// Translate a typename into a langauge-agnostic identifier. This allows for +// registration of components/modules across language boundaries. +template +struct symbol_helper +{ + static char* symbol(void) { + const char *name = name_helper::name(); + + // Symbol is same as name, but with '::' replaced with '.' + char *ptr, *sym = ecs_os_strdup(name); + ecs_size_t i, len = ecs_os_strlen(sym); + ptr = sym; + for (i = 0, ptr = sym; i < len && *ptr; i ++, ptr ++) { + if (*ptr == ':') { + sym[i] = '.'; + ptr ++; + } else { + sym[i] = *ptr; + } + } + + sym[i] = '\0'; + + return sym; + } +}; + // The following functions are lifecycle callbacks that are automatically // registered with flecs to ensure component lifecycle is handled correctly. Not // all types require this, yet callbacks are registered by default, which @@ -10970,7 +11082,7 @@ void component_ctor( T *t_ptr = static_cast(ptr); for (int i = 0; i < count; i ++) { - new(&t_ptr[i]) T; + FLECS_PLACEMENT_NEW(&t_ptr[i], T); } } @@ -11146,14 +11258,7 @@ public: ecs_assert(s_id == entity, ECS_INCONSISTENT_COMPONENT_ID, _::name_helper::name()); - // Ensure the entity has the same name as what was registered. - if (s_id >= EcsFirstUserComponentId) { - char *path = ecs_get_fullpath(world, entity); - ecs_assert(!strcmp(path, s_name.c_str()), - ECS_INCONSISTENT_COMPONENT_NAME, - _::name_helper::name()); - ecs_os_free(path); - } + ecs_assert(allow_tag == s_allow_tag, ECS_INTERNAL_ERROR, NULL); // Component was already registered and data is consistent with new // identifier, so nothing else to be done. @@ -11177,10 +11282,12 @@ public: { // If no id has been registered yet, do it now. if (!s_id) { + const char *n = _::name_helper::name(); + if (!name) { // If no name was provided, retrieve the name implicitly from // the name_helper class. - name = _::name_helper::name(); + name = n; } s_allow_tag = allow_tag; @@ -11210,6 +11317,9 @@ public: flecs::world w(world); flecs::entity result = entity(w, name, true); + // Init the component_info instance with the identiifer. + init(world, result.id(), allow_tag); + // Now use the resulting identifier to register the component. Note // that the name is not passed into this function, as the entity was // already created with the correct name. @@ -11217,13 +11327,49 @@ public: world, result.id(), nullptr, size(), alignment()); + + ecs_assert(entity != 0, ECS_INTERNAL_ERROR, NULL); + + // Set the symbol in the Name component to the actual C++ name. + // Comparing symbols allows for verifying whether a different + // component is being registered under the same name. We can't use + // the name used for registration, because it is possible that a + // user (erroneously) attempts to register the same datatype with + // the same name. Without verifying that the actual C++ type name + // matches, that scenario would go undetected. + EcsName *name_comp = ecs_get_mut(world, entity, EcsName, NULL); + char *symbol = symbol_helper::symbol(); + + if (name_comp->symbol) { + ecs_assert( !strcmp(name_comp->symbol, symbol), + ECS_COMPONENT_NAME_IN_USE, name); + } else { + name_comp->symbol = symbol; + } // The identifier returned by the function should be the same as the // identifier that was passed in. ecs_assert(entity == result.id(), ECS_INTERNAL_ERROR, NULL); - // Init the component_info instance with the identiifer. - init(world, entity); + } else if (world && !ecs_exists(world, s_id)) { + const char *n = _::name_helper::name(); + + if (!name) { + // If no name was provided, retrieve the name implicitly from + // the name_helper class. + name = n; + } + + ecs_entity_t entity = ecs_new_component( + world, s_id, name, + size(), + alignment()); + + (void)entity; + + ecs_assert(entity == s_id, ECS_INTERNAL_ERROR, NULL); + + init(world, s_id, allow_tag); } // By now we should have a valid identifier @@ -11238,7 +11384,7 @@ public: bool allow_tag = true) { // If no id has been registered yet, do it now. - if (!s_id) { + if (!s_id || (world && !ecs_exists(world, s_id))) { // This will register a component id, but will not register // lifecycle callbacks. id_no_lifecycle(world, name, allow_tag); @@ -11315,6 +11461,8 @@ public: // Return the size of a component. static size_t size() { + ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); + // C++ types that have no members still have a size. Use std::is_empty // to check if the type is empty. If so, use 0 for the component size. // @@ -11330,6 +11478,8 @@ public: // Return the alignment of a component. static size_t alignment() { + ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); + if (size() == 0) { return 0; } else { @@ -11354,6 +11504,7 @@ private: static entity_t s_id; static type_t s_type; static std::string s_name; + static std::string s_symbol; static bool s_allow_tag; }; @@ -11381,14 +11532,15 @@ flecs::entity pod_component(const flecs::world& world, const char *name = nullpt entity_t id = 0; if (_::component_info::registered()) { - /* To support components across multiple worlds, ensure that the - * component ids are the same. */ + /* Obtain component id. Because the component is already registered, + * this operation does nothing besides returning the existing id */ id = _::component_info::id_no_lifecycle(world_ptr, name, allow_tag); /* If entity is not empty check if the name matches */ if (ecs_get_type(world_ptr, id) != nullptr) { if (id >= EcsFirstUserComponentId) { - char *path = ecs_get_fullpath(world_ptr, id); + char *path = ecs_get_path_w_sep( + world_ptr, 0, id, 0, "::", nullptr); ecs_assert(!strcmp(path, name), ECS_INCONSISTENT_COMPONENT_NAME, name); ecs_os_free(path); @@ -11426,10 +11578,23 @@ flecs::entity pod_component(const flecs::world& world, const char *name = nullpt * or entity has been registered with this name */ ecs_entity_t entity = ecs_lookup_fullpath(world_ptr, name); - (void)entity; + /* If entity exists, compare symbol name to ensure that the component + * we are trying to register under this name is the same */ + if (entity) { + const EcsName *name_comp = ecs_get_mut(world.c_ptr(), entity, EcsName, NULL); + ecs_assert(name_comp != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(name_comp->symbol != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(entity == 0, ECS_COMPONENT_NAME_IN_USE, name); + const char *symbol = _::name_helper::name(); + ecs_assert(!strcmp(name_comp->symbol, symbol), + ECS_COMPONENT_NAME_IN_USE, name); + + (void)name_comp; + (void)symbol; + } + + /* Register id as usual */ id = _::component_info::id_no_lifecycle(world_ptr, name, allow_tag); } @@ -11488,35 +11653,66 @@ flecs::entity module(const flecs::world& world, const char *name = nullptr) { //// Import a module //////////////////////////////////////////////////////////////////////////////// +template +ecs_entity_t do_import(world& world) { + ecs_trace_1("import %s", _::name_helper::name()); + ecs_log_push(); + + ecs_entity_t scope = ecs_get_scope(world.c_ptr()); + + // Allocate module, so the this ptr will remain stable + // TODO: make sure memory is cleaned up with world + T *module_data = FLECS_NEW(T)(world); + + ecs_set_scope(world.c_ptr(), scope); + + // It should now be possible to lookup the module + char *symbol = _::symbol_helper::symbol(); + ecs_entity_t m = ecs_lookup_symbol(world.c_ptr(), symbol); + ecs_assert(m != 0, ECS_MODULE_UNDEFINED, symbol); + ecs_os_free(symbol); + + _::component_info::init(world.c_ptr(), m, false); + + ecs_assert(_::component_info::size() != 0, ECS_INTERNAL_ERROR, NULL); + + // Set module singleton component + + ecs_set_ptr_w_entity( + world.c_ptr(), m, + _::component_info::id_no_lifecycle(world.c_ptr()), + _::component_info::size(), + module_data); + + ecs_log_pop(); + + return m; +} + template flecs::entity import(world& world) { + const char *symbol = _::name_helper::name(); + + ecs_entity_t m = ecs_lookup_symbol(world.c_ptr(), symbol); + if (!_::component_info::registered()) { - ecs_trace_1("import %s", _::name_helper::name()); - ecs_log_push(); - ecs_entity_t scope = ecs_get_scope(world.c_ptr()); + /* Module is registered with world, initialize static data */ + if (m) { + _::component_info::init(world.c_ptr(), m, false); + + /* Module is not yet registered, register it now */ + } else { + m = do_import(world); + } - // Allocate module, so the this ptr will remain stable - T *module_data = new T(world); - - ecs_set_scope(world.c_ptr(), scope); - - flecs::entity m = world.lookup(_::component_info::name_no_lifecycle(world.c_ptr())); - - ecs_set_ptr_w_entity( - world.c_ptr(), - m.id(), - _::component_info::id_no_lifecycle(world.c_ptr()), - _::component_info::size(), - module_data); - - ecs_log_pop(); - - return m; - } else { - return flecs::entity(world, - _::component_info::id_no_lifecycle(world.c_ptr())); + /* Module has been registered, but could have been for another world. Import + * if module hasn't been registered for this world. */ + } else if (!m) { + m = do_import(world); } + + return flecs::entity(world, m); } @@ -11647,12 +11843,13 @@ class each_invoker { using Columns = typename column_args::Columns; public: - explicit each_invoker(Func func) : m_func(func) { } + explicit each_invoker(Func&& func) noexcept : m_func(std::move(func)) { } + explicit each_invoker(const Func& func) noexcept : m_func(func) { } // Invoke system template ::type* = nullptr> - static void call_system(ecs_iter_t *iter, Func func, size_t index, Columns& columns, Targs... comps) { + static void call_system(ecs_iter_t *iter, const Func& func, size_t index, Columns& columns, Targs... comps) { flecs::iter iter_wrapper(iter); (void)index; (void)columns; @@ -11668,18 +11865,17 @@ public: // Add components one by one to parameter pack template ::type* = nullptr> - static void call_system(ecs_iter_t *iter, Func func, size_t index, Columns& columns, Targs... comps) { - call_system(iter, func, index + 1, columns, comps..., columns[index]); + static void call_system(ecs_iter_t *iter, const Func& func, size_t index, Columns& columns, Targs... comps) { + each_invoker::call_system(iter, func, index + 1, columns, comps..., columns[index]); } // Callback provided to flecs system static void run(ecs_iter_t *iter) { const Context *ctx = ecs_get(iter->world, iter->system, EcsContext); each_invoker *self = (each_invoker*)ctx->ctx; - Func func = self->m_func; column_args columns(iter); - call_system(iter, func, 0, columns.m_columns); - } + call_system(iter, self->m_func, 0, columns.m_columns); + } private: Func m_func; @@ -11695,13 +11891,13 @@ class action_invoker { using Columns = typename column_args::Columns; public: - explicit action_invoker(Func func) - : m_func(func) { } + explicit action_invoker(Func&& func) noexcept : m_func(std::move(func)) { } + explicit action_invoker(const Func& func) noexcept : m_func(func) { } /* Invoke system */ template ::type* = nullptr> - static void call_system(ecs_iter_t *iter, Func func, int index, Columns& columns, Targs... comps) { + static void call_system(ecs_iter_t *iter, const Func& func, int index, Columns& columns, Targs... comps) { (void)index; (void)columns; @@ -11714,18 +11910,17 @@ public: /** Add components one by one to parameter pack */ template ::type* = nullptr> - static void call_system(ecs_iter_t *iter, Func func, int index, Columns& columns, Targs... comps) { + static void call_system(ecs_iter_t *iter, const Func& func, int index, Columns& columns, Targs... comps) { call_system(iter, func, index + 1, columns, comps..., columns[index]); } /** Callback provided to flecs */ static void run(ecs_iter_t *iter) { const Context *ctx = ecs_get(iter->world, iter->system, EcsContext); - action_invoker *self = (action_invoker*)ctx->ctx; - Func func = self->m_func; + action_invoker *self = (action_invoker*)ctx->ctx; column_args columns(iter); - call_system(iter, func, 0, columns.m_columns); - } + call_system(iter, self->m_func, 0, columns.m_columns); + } private: Func m_func; @@ -11740,13 +11935,13 @@ class iter_invoker { using Columns = typename column_args::Columns; public: - explicit iter_invoker(Func func) - : m_func(func) { } + explicit iter_invoker(Func&& func) noexcept : m_func(std::move(func)) { } + explicit iter_invoker(const Func& func) noexcept : m_func(func) { } /* Invoke system */ template ::type* = nullptr> - static void call_system(ecs_iter_t *iter, Func func, size_t index, Columns& columns, Targs... comps) { + static void call_system(ecs_iter_t *iter, const Func& func, size_t index, Columns& columns, Targs... comps) { (void)index; (void)columns; flecs::iter iter_wrapper(iter); @@ -11756,18 +11951,17 @@ public: /** Add components one by one to parameter pack */ template ::type* = nullptr> - static void call_system(ecs_iter_t *iter, Func func, size_t index, Columns& columns, Targs... comps) { + static void call_system(ecs_iter_t *iter, const Func& func, size_t index, Columns& columns, Targs... comps) { call_system(iter, func, index + 1, columns, comps..., columns[index]); } /** Callback provided to flecs */ static void run(ecs_iter_t *iter) { const Context *ctx = ecs_get(iter->world, iter->system, EcsContext); - iter_invoker *self = (iter_invoker*)ctx->ctx; - Func func = self->m_func; + iter_invoker *self = (iter_invoker*)ctx->ctx; column_args columns(iter); - call_system(iter, func, 0, columns.m_columns); - } + call_system(iter, self->m_func, 0, columns.m_columns); + } private: Func m_func; @@ -11950,38 +12144,35 @@ public: query_iterator end() const; template - void each(Func func) const { + void each(Func&& func) const { ecs_iter_t it = ecs_query_iter(m_query); while (ecs_query_next(&it)) { _::column_args columns(&it); - _::each_invoker ctx(func); - ctx.call_system(&it, func, 0, columns.m_columns); + _::each_invoker::call_system(&it, func, 0, columns.m_columns); } } /* DEPRECATED */ template - void action(Func func) const { + void action(Func&& func) const { ecs_iter_t it = ecs_query_iter(m_query); while (ecs_query_next(&it)) { _::column_args columns(&it); - _::action_invoker ctx(func); - ctx.call_system(&it, func, 0, columns.m_columns); + _::action_invoker::call_system(&it, func, 0, columns.m_columns); } - } + } template - void iter(Func func) const { + void iter(Func&& func) const { ecs_iter_t it = ecs_query_iter(m_query); while (ecs_query_next(&it)) { _::column_args columns(&it); - _::iter_invoker ctx(func); - ctx.call_system(&it, func, 0, columns.m_columns); + _::iter_invoker::call_system(&it, func, 0, columns.m_columns); } - } + } }; @@ -12211,11 +12402,12 @@ public: /* DEPRECATED. Use iter instead. */ template - system& action(Func func) { + system& action(Func&& func) { ecs_assert(!m_finalized, ECS_INVALID_PARAMETER, NULL); - auto ctx = new _::action_invoker(func); + using invoker_t = typename _::action_invoker::type, Components...>; + auto ctx = FLECS_NEW(invoker_t)(std::forward(func)); - create_system(_::action_invoker::run, false); + create_system(invoker_t::run, false); EcsContext ctx_value = {ctx}; ecs_set_ptr(m_world, m_id, EcsContext, &ctx_value); @@ -12227,11 +12419,12 @@ public: * is added in the fluent method chain. Create system signature from both * template parameters and anything provided by the signature method. */ template - system& iter(Func func) { + system& iter(Func&& func) { ecs_assert(!m_finalized, ECS_INVALID_PARAMETER, NULL); - auto ctx = new _::iter_invoker(func); + using invoker_t = typename _::iter_invoker::type, Components...>; + auto ctx = FLECS_NEW(invoker_t)(std::forward(func)); - create_system(_::iter_invoker::run, false); + create_system(invoker_t::run, false); EcsContext ctx_value = {ctx}; ecs_set_ptr(m_world, m_id, EcsContext, &ctx_value); @@ -12242,10 +12435,11 @@ public: /* Each is similar to action, but accepts a function that operates on a * single entity */ template - system& each(Func func) { - auto ctx = new _::each_invoker(func); + system& each(Func&& func) { + using invoker_t = typename _::each_invoker::type, Components...>; + auto ctx = FLECS_NEW(invoker_t)(std::forward(func)); - create_system(_::each_invoker::run, true); + create_system(invoker_t::run, true); EcsContext ctx_value = {ctx}; ecs_set_ptr(m_world, m_id, EcsContext, &ctx_value);