update flecs to 2.3.2

isolation_bkp/dynres
Dominik Madarász 2021-05-10 16:41:13 +02:00
parent 5d9f44f2b0
commit 3dc99c9198
5 changed files with 651 additions and 194 deletions

View File

@ -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, General);
ECS_IMPORT(world.ecs, Net); 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(); int32_t world_build_status = world_gen();
ZPL_ASSERT(world_build_status >= 0); 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() { 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(0, WORLD_TRACKER_UPDATE_FAST_MS, 2);
world_tracker_update(1, WORLD_TRACKER_UPDATE_NORMAL_MS, 4); world_tracker_update(1, WORLD_TRACKER_UPDATE_NORMAL_MS, 4);

View File

@ -100,7 +100,6 @@ void flecs_dash_init() {
ECS_IMPORT(world_ecs(), FlecsSystemsCivetweb); ECS_IMPORT(world_ecs(), FlecsSystemsCivetweb);
ecs_set(world_ecs(), 0, EcsDashServer, {.port = 27001}); 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) { 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_setup_pkt_handlers(pkt_reader, sp_pkt_writer);
world_init(seed, block_size, chunk_size, chunk_amount); world_init(seed, block_size, chunk_size, chunk_amount);
if (is_dash_enabled) flecs_dash_init(); if (is_dash_enabled) flecs_dash_init();
ecs_set_target_fps(world_ecs(), 60);
} }
for (uint32_t i = 0; i < num_viewers; i++) { for (uint32_t i = 0; i < num_viewers; i++) {

142
code/vendors/flecs/CHANGELOG.md vendored 100644
View File

@ -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

View File

@ -646,6 +646,7 @@ struct ecs_world_t {
bool quit_workers; /* Signals worker threads to quit */ bool quit_workers; /* Signals worker threads to quit */
bool in_progress; /* Is world being progressed */ bool in_progress; /* Is world being progressed */
bool is_merging; /* Is world currently being merged */ 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 auto_merge; /* Are stages auto-merged by ecs_progress */
bool measure_frame_time; /* Time spent on each frame */ bool measure_frame_time; /* Time spent on each frame */
bool measure_system_time; /* Time spent by each system */ 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)) ecs_new_component(world, ecs_typeid(name), #name, sizeof(name), ECS_ALIGNOF(name))
#define ecs_bootstrap_tag(world, 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)) 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. */ /* Clear table data. Don't call OnRemove handlers. */
void ecs_table_clear_data( void ecs_table_clear_data(
ecs_world_t *world,
ecs_table_t *table, ecs_table_t *table,
ecs_data_t *data); ecs_data_t *data);
@ -2102,6 +2104,7 @@ void register_un_set(
int32_t matched_table_index) int32_t matched_table_index)
{ {
(void)world; (void)world;
table->flags |= EcsTableHasUnSet;
add_monitor(&table->un_set_all, query, matched_table_index); add_monitor(&table->un_set_all, query, matched_table_index);
} }
@ -2273,6 +2276,10 @@ void dtor_component(
int32_t row, int32_t row,
int32_t count) int32_t count)
{ {
if (!count) {
return;
}
/* An old component is destructed */ /* An old component is destructed */
ecs_xtor_t dtor; ecs_xtor_t dtor;
if (cdata && (dtor = cdata->lifecycle.dtor)) { if (cdata && (dtor = cdata->lifecycle.dtor)) {
@ -2311,21 +2318,16 @@ static
void run_remove_actions( void run_remove_actions(
ecs_world_t * world, ecs_world_t * world,
ecs_table_t * table, ecs_table_t * table,
ecs_data_t * data,
int32_t row, int32_t row,
int32_t count, int32_t count)
bool dtor_only)
{ {
if (count) { if (count) {
if (!dtor_only) {
ecs_run_monitors(world, table, NULL, row, count, table->un_set_all); ecs_run_monitors(world, table, NULL, row, count, table->un_set_all);
} }
dtor_all_components(world, table, data, row, count);
}
} }
void ecs_table_clear_data( void ecs_table_clear_data(
ecs_world_t *world,
ecs_table_t *table, ecs_table_t *table,
ecs_data_t *data) ecs_data_t *data)
{ {
@ -2333,6 +2335,9 @@ void ecs_table_clear_data(
return; return;
} }
int32_t count = ecs_table_data_count(data);
dtor_all_components(world, table, data, 0, count);
ecs_column_t *columns = data->columns; ecs_column_t *columns = data->columns;
if (columns) { if (columns) {
int32_t c, column_count = table->column_count; int32_t c, column_count = table->column_count;
@ -2384,7 +2389,7 @@ void ecs_table_clear_silent(
int32_t count = ecs_vector_count(data->entities); int32_t count = ecs_vector_count(data->entities);
ecs_table_clear_data(table, table->data); ecs_table_clear_data(world, table, data);
if (count) { if (count) {
ecs_table_activate(world, table, 0, false); ecs_table_activate(world, table, 0, false);
@ -2399,19 +2404,19 @@ void ecs_table_clear(
ecs_table_t * table) ecs_table_t * table)
{ {
ecs_data_t *data = ecs_table_get_data(table); ecs_data_t *data = ecs_table_get_data(table);
if (data) { if (data) {
run_remove_actions( run_remove_actions(world, table, 0, ecs_table_data_count(data));
world, table, data, 0, ecs_table_data_count(data), false);
ecs_entity_t *entities = ecs_vector_first(data->entities, ecs_entity_t); ecs_entity_t *entities = ecs_vector_first(data->entities, ecs_entity_t);
int32_t i, count = ecs_vector_count(data->entities); int32_t i, count = ecs_vector_count(data->entities);
for(i = 0; i < count; i ++) { for(i = 0; i < count; i ++) {
ecs_eis_delete(world, entities[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 /* Unset all components in table. This function is called before a table is
* deleted, and invokes all UnSet handlers, if any */ * deleted, and invokes all UnSet handlers, if any */
@ -2435,11 +2440,10 @@ void ecs_table_free(
(void)world; (void)world;
ecs_data_t *data = ecs_table_get_data(table); ecs_data_t *data = ecs_table_get_data(table);
if (data) { if (data) {
run_remove_actions( run_remove_actions(world, table, 0, ecs_table_data_count(data));
world, table, data, 0, ecs_table_data_count(data), false); ecs_table_clear_data(world, table, data);
} }
ecs_table_clear_data(table, table->data);
ecs_table_clear_edges(world, table); ecs_table_clear_edges(world, table);
ecs_os_free(table->lo_edges); ecs_os_free(table->lo_edges);
@ -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 static
void merge_table_data( void merge_table_data(
ecs_world_t * world, ecs_world_t * world,
@ -3517,10 +3576,8 @@ void merge_table_data(
} }
if (new_component == old_component) { if (new_component == old_component) {
merge_vector( merge_column(world, new_table, new_data, i_new,
&new_columns[i_new].data, old_columns[i_old].data, size, old_columns[i_old].data);
alignment);
old_columns[i_old].data = NULL; old_columns[i_old].data = NULL;
/* Mark component column as dirty */ /* 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 there is nothing to merge to, just clear the old table */
if (!new_table) { if (!new_table) {
ecs_table_clear_data(old_table, old_data); ecs_table_clear_data(world, old_table, old_data);
return NULL; return NULL;
} }
@ -3711,9 +3768,8 @@ void ecs_table_replace_data(
if (table_data) { if (table_data) {
prev_count = ecs_vector_count(table_data->entities); prev_count = ecs_vector_count(table_data->entities);
run_remove_actions( run_remove_actions(world, table, 0, ecs_table_data_count(table_data));
world, table, table_data, 0, ecs_table_data_count(table_data), false); ecs_table_clear_data(world, table, table_data);
ecs_table_clear_data(table, table_data);
} }
if (data) { if (data) {
@ -3801,6 +3857,10 @@ void ecs_table_notify(
ecs_table_t * table, ecs_table_t * table,
ecs_table_event_t * event) ecs_table_event_t * event)
{ {
if (world->is_fini) {
return;
}
switch(event->kind) { switch(event->kind) {
case EcsTableQueryMatch: case EcsTableQueryMatch:
register_query( register_query(
@ -4211,7 +4271,6 @@ void ecs_run_monitors(
} }
ecs_assert(!(dst_table->flags & EcsTableIsPrefab), ECS_INTERNAL_ERROR, NULL); ecs_assert(!(dst_table->flags & EcsTableIsPrefab), ECS_INTERNAL_ERROR, NULL);
(void)dst_table;
if (!v_src_monitors) { if (!v_src_monitors) {
ecs_vector_each(v_dst_monitors, ecs_matched_query_t, monitor, { 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; continue;
} }
@ -4830,7 +4889,7 @@ int32_t move_entity(
/* If entity was moved, invoke UnSet monitors for each component that /* If entity was moved, invoke UnSet monitors for each component that
* the entity no longer has */ * the entity no longer has */
ecs_run_monitors(world, dst_table, src_table->un_set_all, 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( ecs_run_remove_actions(
world, src_table, src_data, src_row, 1, removed, false); world, src_table, src_data, src_row, 1, removed, false);
@ -5474,12 +5533,20 @@ ecs_entity_t ecs_new_component_id(
ECS_INVALID_WHILE_ITERATING, NULL); 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 (world->stats.last_component_id >= ECS_HI_COMPONENT_ID) {
/* If the low component ids are depleted, return a regular entity id */ /* If the low component ids are depleted, return a regular entity id */
return ecs_new_id(world); id = ecs_new_id(world);
} else {
return world->stats.last_component_id ++;
} }
return id;
} }
ecs_entity_t ecs_new_w_type( ecs_entity_t ecs_new_w_type(
@ -5678,10 +5745,15 @@ void ecs_delete(
ecs_record_t *r = ecs_sparse_remove_get( ecs_record_t *r = ecs_sparse_remove_get(
world->store.entity_index, ecs_record_t, entity); world->store.entity_index, ecs_record_t, entity);
if (r) { if (r) {
ecs_entity_info_t info; ecs_entity_info_t info = {0};
set_info_from_record(entity, &info, r); set_info_from_record(entity, &info, r);
if (info.is_watched) { if (info.is_watched) {
ecs_delete_children(world, entity); 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 */ /* If entity has components, remove them */
@ -6469,7 +6541,7 @@ void flush_bulk_new(
int c, c_count = op->components.count; int c, c_count = op->components.count;
for (c = 0; c < c_count; c ++) { for (c = 0; c < c_count; c ++) {
ecs_entity_t component = components[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); ecs_assert(cptr != NULL, ECS_INTERNAL_ERROR, NULL);
size_t size = ecs_to_size_t(cptr->size); size_t size = ecs_to_size_t(cptr->size);
void *ptr, *data = bulk_data[c]; 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); defer_data = ecs_os_malloc(ECS_SIZEOF(void*) * c_count);
for (c = 0; c < c_count; c ++) { for (c = 0; c < c_count; c ++) {
ecs_entity_t comp = components[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_assert(cptr != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_size_t size = cptr->size; ecs_size_t size = cptr->size;
@ -6931,7 +7003,7 @@ bool ecs_defer_set(
if (stage->defer) { if (stage->defer) {
world->set_count ++; world->set_count ++;
if (!size) { 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); ecs_assert(cptr != NULL, ECS_INVALID_PARAMETER, NULL);
size = cptr->size; size = cptr->size;
} }
@ -8873,9 +8945,11 @@ ecs_entity_t ecs_new_module(
e = ecs_new_from_fullpath(world, module_path); e = ecs_new_from_fullpath(world, module_path);
EcsName *name_ptr = ecs_get_mut(world, e, EcsName, NULL); 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); ecs_entity_t result = ecs_new_component(world, e, NULL, size, alignment);
@ -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->pipeline_build_count_total, t, world->stats.pipeline_build_count_total);
record_counter(&s->systems_ran_frame, t, world->stats.systems_ran_frame); 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->entity_count, t, ecs_sparse_count(world->store.entity_index));
record_gauge(&s->component_count, t, ecs_count_entity(world, ecs_typeid(EcsComponent))); 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 ++) { for (t = 0; t < table_count; t ++) {
ecs_table_t *table = ecs_sparse_get(world->store.tables, ecs_table_t, t); ecs_table_t *table = ecs_sparse_get(world->store.tables, ecs_table_t, t);
if (table->flags & EcsTableHasBuiltins) { if (table->flags & EcsTableHasBuiltins) {
continue; continue;
} }
@ -9659,7 +9739,7 @@ void ecs_snapshot_restore(
/* Always delete entity, so that even if the entity is /* Always delete entity, so that even if the entity is
* in the current table, there won't be duplicates */ * 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 { } else {
ecs_eis_set_generation(world, *e_ptr); ecs_eis_set_generation(world, *e_ptr);
} }
@ -9785,7 +9865,7 @@ void ecs_snapshot_free(
int32_t i, count = ecs_vector_count(snapshot->tables); int32_t i, count = ecs_vector_count(snapshot->tables);
for (i = 0; i < count; i ++) { for (i = 0; i < count; i ++) {
ecs_table_leaf_t *leaf = &tables[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); ecs_os_free(leaf->data);
} }
@ -11233,6 +11313,7 @@ ecs_world_t *ecs_mini(void) {
world->quit_workers = false; world->quit_workers = false;
world->in_progress = false; world->in_progress = false;
world->is_merging = false; world->is_merging = false;
world->is_fini = false;
world->auto_merge = true; world->auto_merge = true;
world->measure_frame_time = false; world->measure_frame_time = false;
world->measure_system_time = false; world->measure_system_time = false;
@ -11581,9 +11662,12 @@ void fini_misc(
int ecs_fini( int ecs_fini(
ecs_world_t *world) ecs_world_t *world)
{ {
assert(world->magic == ECS_WORLD_MAGIC); ecs_assert(world->magic == ECS_WORLD_MAGIC, ECS_INVALID_PARAMETER, NULL);
assert(!world->in_progress); ecs_assert(!world->in_progress, ECS_INVALID_OPERATION, NULL);
assert(!world->is_merging); 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); fini_unset_tables(world);
@ -17249,10 +17333,22 @@ void ecs_query_free(
}); });
ecs_vector_each(query->empty_tables, ecs_matched_table_t, table, { 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); free_matched_table(table);
}); });
ecs_vector_each(query->tables, ecs_matched_table_t, 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); free_matched_table(table);
}); });
@ -19887,7 +19983,7 @@ uint64_t ecs_os_time_now(void) {
QueryPerformanceCounter(&qpc_t); QueryPerformanceCounter(&qpc_t);
now = (uint64_t)(qpc_t.QuadPart / _ecs_os_time_win_freq); now = (uint64_t)(qpc_t.QuadPart / _ecs_os_time_win_freq);
#elif defined(__APPLE__) && defined(__MACH__) #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 #else
struct timespec ts; struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts); clock_gettime(CLOCK_MONOTONIC, &ts);
@ -22408,10 +22504,14 @@ void ecs_set_symbol(
const char *e_name = ecs_name_from_symbol(world, name); const char *e_name = ecs_name_from_symbol(world, name);
ecs_set(world, e, EcsName, { EcsName *name_ptr = ecs_get_mut(world, e, EcsName, NULL);
.value = e_name, name_ptr->value = e_name;
.symbol = 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( 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); ecs_assert(world != NULL, ECS_INVALID_PARAMETER, NULL);
assert(world->magic == ECS_WORLD_MAGIC); assert(world->magic == ECS_WORLD_MAGIC);
bool in_progress = world->in_progress; bool in_progress = world->in_progress;
bool found = false;
/* If world is in progress component may be registered, but only when not /* If world is in progress component may be registered, but only when not
* in multithreading mode. */ * in multithreading mode. */
@ -22533,7 +22634,7 @@ ecs_entity_t ecs_new_component(
ecs_entity_t result = ecs_lookup_w_id(world, e, name); ecs_entity_t result = ecs_lookup_w_id(world, e, name);
if (!result) { if (!result) {
result = ecs_new_component_id(world); 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 */ /* 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); ecs_add_entity(world, result, ECS_CHILDOF | scope);
} }
if (found) {
ecs_set_symbol(world, result, name);
}
bool added = false; bool added = false;
EcsComponent *ptr = ecs_get_mut(world, result, EcsComponent, &added); EcsComponent *ptr = ecs_get_mut(world, result, EcsComponent, &added);
@ -22622,6 +22727,7 @@ static ECS_CTOR(EcsName, ptr, {
static ECS_DTOR(EcsName, ptr, { static ECS_DTOR(EcsName, ptr, {
ecs_os_free(ptr->alloc_value); ecs_os_free(ptr->alloc_value);
ecs_os_free(ptr->symbol);
ptr->value = NULL; ptr->value = NULL;
ptr->alloc_value = NULL; ptr->alloc_value = NULL;
ptr->symbol = NULL; ptr->symbol = NULL;
@ -22633,6 +22739,11 @@ static ECS_COPY(EcsName, dst, src, {
dst->alloc_value = NULL; dst->alloc_value = NULL;
} }
if (dst->symbol) {
ecs_os_free(dst->symbol);
dst->symbol = NULL;
}
if (src->alloc_value) { if (src->alloc_value) {
dst->alloc_value = ecs_os_strdup(src->alloc_value); dst->alloc_value = ecs_os_strdup(src->alloc_value);
dst->value = dst->alloc_value; dst->value = dst->alloc_value;
@ -22640,10 +22751,20 @@ static ECS_COPY(EcsName, dst, src, {
dst->alloc_value = NULL; dst->alloc_value = NULL;
dst->value = src->value; dst->value = src->value;
} }
dst->symbol = src->symbol;
if (src->symbol) {
dst->symbol = ecs_os_strdup(src->symbol);
}
}) })
static ECS_MOVE(EcsName, dst, src, { 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->value = src->value;
dst->alloc_value = src->alloc_value; dst->alloc_value = src->alloc_value;
dst->symbol = src->symbol; dst->symbol = src->symbol;
@ -22691,7 +22812,7 @@ void _bootstrap_component(
c_info[index].size = size; c_info[index].size = size;
c_info[index].alignment = alignment; c_info[index].alignment = alignment;
id_data[index].value = &id[ecs_os_strlen("Ecs")]; /* Skip prefix */ 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; id_data[index].alloc_value = NULL;
} }
@ -22771,9 +22892,16 @@ void ecs_bootstrap(
ecs_table_t *table = bootstrap_component_table(world); ecs_table_t *table = bootstrap_component_table(world);
assert(table != NULL); assert(table != NULL);
bootstrap_component(world, table, EcsName);
bootstrap_component(world, table, EcsComponent); bootstrap_component(world, table, EcsComponent);
bootstrap_component(world, table, EcsType); 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_component_id = EcsFirstUserComponentId;
world->stats.last_id = EcsFirstUserEntityId; world->stats.last_id = EcsFirstUserEntityId;
@ -22789,13 +22917,6 @@ void ecs_bootstrap(
ecs_bootstrap_tag(world, EcsHidden); ecs_bootstrap_tag(world, EcsHidden);
ecs_bootstrap_tag(world, EcsDisabled); 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 */ /* Initialize scopes */
ecs_set(world, EcsFlecs, EcsName, {.value = "flecs"}); ecs_set(world, EcsFlecs, EcsName, {.value = "flecs"});
ecs_add_entity(world, EcsFlecs, EcsModule); ecs_add_entity(world, EcsFlecs, EcsModule);

View File

@ -18,6 +18,7 @@
/* FLECS_CUSTOM_BUILD should be defined when manually selecting features */ /* FLECS_CUSTOM_BUILD should be defined when manually selecting features */
// #define FLECS_CUSTOM_BUILD // #define FLECS_CUSTOM_BUILD
/* If this is a regular, non-custom build, build all modules and addons. */
#ifndef FLECS_CUSTOM_BUILD #ifndef FLECS_CUSTOM_BUILD
/* Modules */ /* Modules */
#define FLECS_SYSTEM #define FLECS_SYSTEM
@ -748,7 +749,7 @@ ecs_vector_t* _ecs_vector_copy(
#ifdef __cplusplus #ifdef __cplusplus
#ifndef FLECS_NO_CPP #ifndef FLECS_NO_CPP
#include <iostream> #include <initializer_list>
namespace flecs { namespace flecs {
@ -1349,7 +1350,8 @@ void ecs_map_memory(
#ifdef __cplusplus #ifdef __cplusplus
#ifndef FLECS_NO_CPP #ifndef FLECS_NO_CPP
#include <iostream> #include <initializer_list>
#include <utility>
namespace flecs { namespace flecs {
@ -1968,10 +1970,18 @@ FLECS_API
void ecs_os_set_api_defaults(void); void ecs_os_set_api_defaults(void);
/* Memory management */ /* Memory management */
#define ecs_os_malloc(size) ecs_os_api.malloc_(size); #ifndef ecs_os_malloc
#define ecs_os_free(ptr) ecs_os_api.free_(ptr); #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) #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) #define ecs_os_calloc(size) ecs_os_api.calloc_(size)
#endif
#if defined(_MSC_VER) || defined(__MINGW32__) #if defined(_MSC_VER) || defined(__MINGW32__)
#define ecs_os_alloca(size) _alloca((size_t)(size)) #define ecs_os_alloca(size) _alloca((size_t)(size))
#else #else
@ -1979,7 +1989,9 @@ void ecs_os_set_api_defaults(void);
#endif #endif
/* Strings */ /* Strings */
#ifndef ecs_os_strdup
#define ecs_os_strdup(str) ecs_os_api.strdup_(str) #define ecs_os_strdup(str) ecs_os_api.strdup_(str)
#endif
#define ecs_os_strlen(str) (ecs_size_t)strlen(str) #define ecs_os_strlen(str) (ecs_size_t)strlen(str)
#define ecs_os_strcmp(str1, str2) strcmp(str1, str2) #define ecs_os_strcmp(str1, str2) strcmp(str1, str2)
#define ecs_os_strncmp(str1, str2, num) strncmp(str1, str2, (size_t)(num)) #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. */ /** Entity name. */
typedef struct EcsName { typedef struct EcsName {
const char *value; /**< Entity name */ 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 */ char *alloc_value; /**< If set, value will be freed on destruction */
} EcsName; } EcsName;
@ -7805,6 +7817,38 @@ FLECS_API void ecs_gauge_reduce(
#include <sstream> #include <sstream>
#include <array> #include <array>
#include <functional> #include <functional>
#include <iostream>
// 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<class Ty> 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 { namespace flecs {
@ -9470,7 +9514,7 @@ public:
base_type& disable() const { base_type& disable() const {
static_cast<base_type*>(this)->invoke( static_cast<base_type*>(this)->invoke(
[](world_t *world, entity_t id) { [](world_t *world, entity_t id) {
ecs_enable(world, id, true); ecs_enable(world, id, false);
}); });
return *static_cast<base_type*>(this); return *static_cast<base_type*>(this);
} }
@ -9556,10 +9600,13 @@ public:
const base_type& set(T&& value) const { const base_type& set(T&& value) const {
static_cast<base_type*>(this)->invoke( static_cast<base_type*>(this)->invoke(
[&value](world_t *world, entity_t id) { [&value](world_t *world, entity_t id) {
auto comp_id = _::component_info<T>::id(world);
ecs_assert(_::component_info<T>::size() != 0, ecs_assert(_::component_info<T>::size() != 0,
ECS_INVALID_PARAMETER, NULL); ECS_INVALID_PARAMETER, NULL);
ecs_set_ptr_w_entity( ecs_set_ptr_w_entity(
world, id, _::component_info<T>::id(world), sizeof(T), &value); world, id, comp_id, sizeof(T), &value);
}); });
return *static_cast<base_type*>(this); return *static_cast<base_type*>(this);
} }
@ -9575,10 +9622,13 @@ public:
const base_type& set(const T& value) const { const base_type& set(const T& value) const {
static_cast<base_type*>(this)->invoke( static_cast<base_type*>(this)->invoke(
[&value](world_t *world, entity_t id) { [&value](world_t *world, entity_t id) {
auto comp_id = _::component_info<T>::id(world);
ecs_assert(_::component_info<T>::size() != 0, ecs_assert(_::component_info<T>::size() != 0,
ECS_INVALID_PARAMETER, NULL); ECS_INVALID_PARAMETER, NULL);
ecs_set_ptr_w_entity( ecs_set_ptr_w_entity(
world, id, _::component_info<T>::id(world), sizeof(T), &value); world, id, comp_id, sizeof(T), &value);
}); });
return *static_cast<base_type*>(this); return *static_cast<base_type*>(this);
} }
@ -9595,11 +9645,13 @@ public:
const base_type& set_trait(const T& value) const { const base_type& set_trait(const T& value) const {
static_cast<base_type*>(this)->invoke( static_cast<base_type*>(this)->invoke(
[&value](world_t *world, entity_t id) { [&value](world_t *world, entity_t id) {
auto t_id = _::component_info<T>::id(world);
ecs_assert(_::component_info<T>::size() != 0, ecs_assert(_::component_info<T>::size() != 0,
ECS_INVALID_PARAMETER, NULL); ECS_INVALID_PARAMETER, NULL);
ecs_set_ptr_w_entity(world, id, ecs_set_ptr_w_entity(world, id,
ecs_trait(_::component_info<C>::id(world), ecs_trait(_::component_info<C>::id(world), t_id),
_::component_info<T>::id(world)),
sizeof(T), &value); sizeof(T), &value);
}); });
return *static_cast<base_type*>(this); return *static_cast<base_type*>(this);
@ -9642,15 +9694,17 @@ public:
const base_type& patch(std::function<void(T&, bool)> func) const { const base_type& patch(std::function<void(T&, bool)> func) const {
static_cast<base_type*>(this)->invoke( static_cast<base_type*>(this)->invoke(
[&func](world_t *world, entity_t id) { [&func](world_t *world, entity_t id) {
auto comp_id = _::component_info<T>::id(world);
ecs_assert(_::component_info<T>::size() != 0, ecs_assert(_::component_info<T>::size() != 0,
ECS_INVALID_PARAMETER, NULL); ECS_INVALID_PARAMETER, NULL);
bool is_added; bool is_added;
T *ptr = static_cast<T*>(ecs_get_mut_w_entity( T *ptr = static_cast<T*>(ecs_get_mut_w_entity(
world, id, _::component_info<T>::id(world), &is_added)); world, id, comp_id, &is_added));
if (ptr) { if (ptr) {
func(*ptr, !is_added); func(*ptr, !is_added);
ecs_modified_w_entity(world, id, _::component_info<T>::id(world)); ecs_modified_w_entity(world, id, comp_id);
} }
}); });
return *static_cast<base_type*>(this); return *static_cast<base_type*>(this);
@ -9668,15 +9722,17 @@ public:
const base_type& patch(std::function<void(T&)> func) const { const base_type& patch(std::function<void(T&)> func) const {
static_cast<base_type*>(this)->invoke( static_cast<base_type*>(this)->invoke(
[&func](world_t *world, entity_t id) { [&func](world_t *world, entity_t id) {
auto comp_id = _::component_info<T>::id(world);
ecs_assert(_::component_info<T>::size() != 0, ecs_assert(_::component_info<T>::size() != 0,
ECS_INVALID_PARAMETER, NULL); ECS_INVALID_PARAMETER, NULL);
bool is_added; bool is_added;
T *ptr = static_cast<T*>(ecs_get_mut_w_entity( T *ptr = static_cast<T*>(ecs_get_mut_w_entity(
world, id, _::component_info<T>::id(world), &is_added)); world, id, comp_id, &is_added));
if (ptr) { if (ptr) {
func(*ptr); func(*ptr);
ecs_modified_w_entity(world, id, _::component_info<T>::id(world)); ecs_modified_w_entity(world, id, comp_id);
} }
}); });
return *static_cast<base_type*>(this); return *static_cast<base_type*>(this);
@ -9702,11 +9758,13 @@ public:
, m_entity( entity ) , m_entity( entity )
, m_ref() , m_ref()
{ {
auto comp_id = _::component_info<T>::id(world);
ecs_assert(_::component_info<T>::size() != 0, ecs_assert(_::component_info<T>::size() != 0,
ECS_INVALID_PARAMETER, NULL); ECS_INVALID_PARAMETER, NULL);
ecs_get_ref_w_entity( ecs_get_ref_w_entity(
m_world, &m_ref, m_entity, _::component_info<T>::id(world)); m_world, &m_ref, m_entity, comp_id);
} }
const T* operator->() { const T* operator->() {
@ -9763,11 +9821,7 @@ public:
*/ */
explicit entity(world_t *world) explicit entity(world_t *world)
: m_world( world ) : m_world( world )
{ , m_id( world ? ecs_new_w_type(world, 0) : 0 ) { }
if (m_world) {
m_id = ecs_new_w_type(m_world, 0);
}
}
/** Create a named entity. /** Create a named entity.
* Named entities can be looked up with the lookup functions. Entity names * Named entities can be looked up with the lookup functions. Entity names
@ -9998,10 +10052,14 @@ public:
const T* get() const { const T* get() const {
ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL);
auto comp_id = _::component_info<T>::id(m_world);
ecs_assert(_::component_info<T>::size() != 0, ecs_assert(_::component_info<T>::size() != 0,
ECS_INVALID_PARAMETER, NULL); ECS_INVALID_PARAMETER, NULL);
return static_cast<const T*>( return static_cast<const T*>(
ecs_get_w_entity(m_world, m_id, _::component_info<T>::id(m_world))); ecs_get_w_entity(m_world, m_id, comp_id));
} }
/** Get component value (untyped). /** Get component value (untyped).
@ -10042,11 +10100,14 @@ public:
T* get_mut(bool *is_added = nullptr) const { T* get_mut(bool *is_added = nullptr) const {
ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL);
auto comp_id = _::component_info<T>::id(m_world);
ecs_assert(_::component_info<T>::size() != 0, ecs_assert(_::component_info<T>::size() != 0,
ECS_INVALID_PARAMETER, NULL); ECS_INVALID_PARAMETER, NULL);
return static_cast<T*>( return static_cast<T*>(
ecs_get_mut_w_entity( ecs_get_mut_w_entity(m_world, m_id, comp_id, is_added));
m_world, m_id, _::component_info<T>::id(m_world), is_added));
} }
/** Get mutable component value (untyped). /** Get mutable component value (untyped).
@ -10092,10 +10153,14 @@ public:
const T* get_trait() const { const T* get_trait() const {
ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL);
auto t_id = _::component_info<T>::id(m_world);
ecs_assert(_::component_info<T>::size() != 0, ecs_assert(_::component_info<T>::size() != 0,
ECS_INVALID_PARAMETER, NULL); ECS_INVALID_PARAMETER, NULL);
return static_cast<const T*>(ecs_get_w_entity(m_world, m_id, ecs_trait( return static_cast<const T*>(ecs_get_w_entity(m_world, m_id, ecs_trait(
_::component_info<C>::id(m_world), _::component_info<T>::id(m_world)))); _::component_info<C>::id(m_world), t_id)));
} }
/** Get trait value. /** Get trait value.
@ -10109,10 +10174,14 @@ public:
const T* get_trait(flecs::entity component) const { const T* get_trait(flecs::entity component) const {
ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL);
auto comp_id = _::component_info<T>::id(m_world);
ecs_assert(_::component_info<T>::size() != 0, ecs_assert(_::component_info<T>::size() != 0,
ECS_INVALID_PARAMETER, NULL); ECS_INVALID_PARAMETER, NULL);
return static_cast<const T*>(ecs_get_w_entity(m_world, m_id, ecs_trait( return static_cast<const T*>(ecs_get_w_entity(m_world, m_id, ecs_trait(
component.id(), _::component_info<T>::id(m_world)))); component.id(), comp_id)));
} }
/** Get trait tag value. /** Get trait tag value.
@ -10129,10 +10198,14 @@ public:
const C* get_trait_tag(flecs::entity trait) const { const C* get_trait_tag(flecs::entity trait) const {
ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL);
auto comp_id = _::component_info<C>::id(m_world);
ecs_assert(_::component_info<C>::size() != 0, ecs_assert(_::component_info<C>::size() != 0,
ECS_INVALID_PARAMETER, NULL); ECS_INVALID_PARAMETER, NULL);
return static_cast<const C*>(ecs_get_w_entity(m_world, m_id, ecs_trait( return static_cast<const C*>(ecs_get_w_entity(m_world, m_id, ecs_trait(
_::component_info<C>::id(m_world), trait.id()))); comp_id, trait.id())));
} }
/** Get trait tag value (untyped). /** Get trait tag value (untyped).
@ -10166,14 +10239,16 @@ public:
T* get_trait_mut(bool *is_added = nullptr) const { T* get_trait_mut(bool *is_added = nullptr) const {
ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL);
auto t_id = _::component_info<T>::id(m_world);
ecs_assert(_::component_info<T>::size() != 0, ecs_assert(_::component_info<T>::size() != 0,
ECS_INVALID_PARAMETER, NULL); ECS_INVALID_PARAMETER, NULL);
return static_cast<T*>( return static_cast<T*>(
ecs_get_mut_w_entity( ecs_get_mut_w_entity(
m_world, m_id, ecs_trait( m_world, m_id, ecs_trait(_::component_info<C>::id(m_world),
_::component_info<C>::id(m_world), t_id), is_added));
_::component_info<T>::id(m_world)),
is_added));
} }
/** Get mutable trait value. /** Get mutable trait value.
@ -10191,14 +10266,15 @@ public:
T* get_trait_mut(flecs::entity component, bool *is_added = nullptr) const { T* get_trait_mut(flecs::entity component, bool *is_added = nullptr) const {
ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL);
auto comp_id = _::component_info<T>::id(m_world);
ecs_assert(_::component_info<T>::size() != 0, ecs_assert(_::component_info<T>::size() != 0,
ECS_INVALID_PARAMETER, NULL); ECS_INVALID_PARAMETER, NULL);
return static_cast<T*>( return static_cast<T*>(
ecs_get_mut_w_entity( ecs_get_mut_w_entity(
m_world, m_id, ecs_trait( m_world, m_id, ecs_trait( comp_id, component.id()), is_added));
_::component_info<T>::id(m_world),
component.id()),
is_added));
} }
/** Get mutable trait tag value. /** Get mutable trait tag value.
@ -10236,9 +10312,13 @@ public:
void modified() const { void modified() const {
ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL);
auto comp_id = _::component_info<T>::id(m_world);
ecs_assert(_::component_info<T>::size() != 0, ecs_assert(_::component_info<T>::size() != 0,
ECS_INVALID_PARAMETER, NULL); ECS_INVALID_PARAMETER, NULL);
ecs_modified_w_entity(m_world, m_id, _::component_info<T>::id(m_world));
ecs_modified_w_entity(m_world, m_id, comp_id);
} }
/** Signal that component was modified. /** Signal that component was modified.
@ -10272,8 +10352,13 @@ public:
ref<T> get_ref() const { ref<T> get_ref() const {
ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL);
// Ensure component is registered
_::component_info<T>::id(m_world);
ecs_assert(_::component_info<T>::size() != 0, ecs_assert(_::component_info<T>::size() != 0,
ECS_INVALID_PARAMETER, NULL); ECS_INVALID_PARAMETER, NULL);
return ref<T>(m_world, m_id); return ref<T>(m_world, m_id);
} }
@ -10938,10 +11023,37 @@ namespace _
return typeName; return typeName;
} }
}; };
#elif #else
#error "implicit component registration not supported" #error "implicit component registration not supported"
#endif #endif
// Translate a typename into a langauge-agnostic identifier. This allows for
// registration of components/modules across language boundaries.
template <typename T>
struct symbol_helper
{
static char* symbol(void) {
const char *name = name_helper<T>::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 // The following functions are lifecycle callbacks that are automatically
// registered with flecs to ensure component lifecycle is handled correctly. Not // registered with flecs to ensure component lifecycle is handled correctly. Not
// all types require this, yet callbacks are registered by default, which // all types require this, yet callbacks are registered by default, which
@ -10970,7 +11082,7 @@ void component_ctor(
T *t_ptr = static_cast<T*>(ptr); T *t_ptr = static_cast<T*>(ptr);
for (int i = 0; i < count; i ++) { 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, ecs_assert(s_id == entity, ECS_INCONSISTENT_COMPONENT_ID,
_::name_helper<T>::name()); _::name_helper<T>::name());
// Ensure the entity has the same name as what was registered. ecs_assert(allow_tag == s_allow_tag, ECS_INTERNAL_ERROR, NULL);
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<T>::name());
ecs_os_free(path);
}
// Component was already registered and data is consistent with new // Component was already registered and data is consistent with new
// identifier, so nothing else to be done. // identifier, so nothing else to be done.
@ -11177,10 +11282,12 @@ public:
{ {
// If no id has been registered yet, do it now. // If no id has been registered yet, do it now.
if (!s_id) { if (!s_id) {
const char *n = _::name_helper<T>::name();
if (!name) { if (!name) {
// If no name was provided, retrieve the name implicitly from // If no name was provided, retrieve the name implicitly from
// the name_helper class. // the name_helper class.
name = _::name_helper<T>::name(); name = n;
} }
s_allow_tag = allow_tag; s_allow_tag = allow_tag;
@ -11210,6 +11317,9 @@ public:
flecs::world w(world); flecs::world w(world);
flecs::entity result = entity(w, name, true); 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 // Now use the resulting identifier to register the component. Note
// that the name is not passed into this function, as the entity was // that the name is not passed into this function, as the entity was
// already created with the correct name. // already created with the correct name.
@ -11218,12 +11328,48 @@ public:
size(), size(),
alignment()); 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<T>::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 // The identifier returned by the function should be the same as the
// identifier that was passed in. // identifier that was passed in.
ecs_assert(entity == result.id(), ECS_INTERNAL_ERROR, NULL); ecs_assert(entity == result.id(), ECS_INTERNAL_ERROR, NULL);
// Init the component_info instance with the identiifer. } else if (world && !ecs_exists(world, s_id)) {
init(world, entity); const char *n = _::name_helper<T>::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 // By now we should have a valid identifier
@ -11238,7 +11384,7 @@ public:
bool allow_tag = true) bool allow_tag = true)
{ {
// If no id has been registered yet, do it now. // 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 // This will register a component id, but will not register
// lifecycle callbacks. // lifecycle callbacks.
id_no_lifecycle(world, name, allow_tag); id_no_lifecycle(world, name, allow_tag);
@ -11315,6 +11461,8 @@ public:
// Return the size of a component. // Return the size of a component.
static size_t size() { 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 // 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. // 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. // Return the alignment of a component.
static size_t alignment() { static size_t alignment() {
ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL);
if (size() == 0) { if (size() == 0) {
return 0; return 0;
} else { } else {
@ -11354,6 +11504,7 @@ private:
static entity_t s_id; static entity_t s_id;
static type_t s_type; static type_t s_type;
static std::string s_name; static std::string s_name;
static std::string s_symbol;
static bool s_allow_tag; 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; entity_t id = 0;
if (_::component_info<T>::registered()) { if (_::component_info<T>::registered()) {
/* To support components across multiple worlds, ensure that the /* Obtain component id. Because the component is already registered,
* component ids are the same. */ * this operation does nothing besides returning the existing id */
id = _::component_info<T>::id_no_lifecycle(world_ptr, name, allow_tag); id = _::component_info<T>::id_no_lifecycle(world_ptr, name, allow_tag);
/* If entity is not empty check if the name matches */ /* If entity is not empty check if the name matches */
if (ecs_get_type(world_ptr, id) != nullptr) { if (ecs_get_type(world_ptr, id) != nullptr) {
if (id >= EcsFirstUserComponentId) { 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_assert(!strcmp(path, name),
ECS_INCONSISTENT_COMPONENT_NAME, name); ECS_INCONSISTENT_COMPONENT_NAME, name);
ecs_os_free(path); 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 */ * or entity has been registered with this name */
ecs_entity_t entity = ecs_lookup_fullpath(world_ptr, 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<T>::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<T>::id_no_lifecycle(world_ptr, name, allow_tag); id = _::component_info<T>::id_no_lifecycle(world_ptr, name, allow_tag);
} }
@ -11489,23 +11654,32 @@ flecs::entity module(const flecs::world& world, const char *name = nullptr) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
template <typename T> template <typename T>
flecs::entity import(world& world) { ecs_entity_t do_import(world& world) {
if (!_::component_info<T>::registered()) {
ecs_trace_1("import %s", _::name_helper<T>::name()); ecs_trace_1("import %s", _::name_helper<T>::name());
ecs_log_push(); ecs_log_push();
ecs_entity_t scope = ecs_get_scope(world.c_ptr()); ecs_entity_t scope = ecs_get_scope(world.c_ptr());
// Allocate module, so the this ptr will remain stable // Allocate module, so the this ptr will remain stable
T *module_data = new T(world); // TODO: make sure memory is cleaned up with world
T *module_data = FLECS_NEW(T)(world);
ecs_set_scope(world.c_ptr(), scope); ecs_set_scope(world.c_ptr(), scope);
flecs::entity m = world.lookup(_::component_info<T>::name_no_lifecycle(world.c_ptr())); // It should now be possible to lookup the module
char *symbol = _::symbol_helper<T>::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<T>::init(world.c_ptr(), m, false);
ecs_assert(_::component_info<T>::size() != 0, ECS_INTERNAL_ERROR, NULL);
// Set module singleton component
ecs_set_ptr_w_entity( ecs_set_ptr_w_entity(
world.c_ptr(), world.c_ptr(), m,
m.id(),
_::component_info<T>::id_no_lifecycle(world.c_ptr()), _::component_info<T>::id_no_lifecycle(world.c_ptr()),
_::component_info<T>::size(), _::component_info<T>::size(),
module_data); module_data);
@ -11513,10 +11687,32 @@ flecs::entity import(world& world) {
ecs_log_pop(); ecs_log_pop();
return m; return m;
} else {
return flecs::entity(world,
_::component_info<T>::id_no_lifecycle(world.c_ptr()));
} }
template <typename T>
flecs::entity import(world& world) {
const char *symbol = _::name_helper<T>::name();
ecs_entity_t m = ecs_lookup_symbol(world.c_ptr(), symbol);
if (!_::component_info<T>::registered()) {
/* Module is registered with world, initialize static data */
if (m) {
_::component_info<T>::init(world.c_ptr(), m, false);
/* Module is not yet registered, register it now */
} else {
m = do_import<T>(world);
}
/* 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<T>(world);
}
return flecs::entity(world, m);
} }
@ -11647,12 +11843,13 @@ class each_invoker {
using Columns = typename column_args<Components ...>::Columns; using Columns = typename column_args<Components ...>::Columns;
public: 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 // Invoke system
template <typename... Targs, template <typename... Targs,
typename std::enable_if<sizeof...(Targs) == sizeof...(Components), void>::type* = nullptr> typename std::enable_if<sizeof...(Targs) == sizeof...(Components), void>::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); flecs::iter iter_wrapper(iter);
(void)index; (void)index;
(void)columns; (void)columns;
@ -11668,17 +11865,16 @@ public:
// Add components one by one to parameter pack // Add components one by one to parameter pack
template <typename... Targs, template <typename... Targs,
typename std::enable_if<sizeof...(Targs) != sizeof...(Components), void>::type* = nullptr> typename std::enable_if<sizeof...(Targs) != sizeof...(Components), void>::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]); each_invoker::call_system(iter, func, index + 1, columns, comps..., columns[index]);
} }
// Callback provided to flecs system // Callback provided to flecs system
static void run(ecs_iter_t *iter) { static void run(ecs_iter_t *iter) {
const Context *ctx = ecs_get(iter->world, iter->system, EcsContext); const Context *ctx = ecs_get(iter->world, iter->system, EcsContext);
each_invoker *self = (each_invoker*)ctx->ctx; each_invoker *self = (each_invoker*)ctx->ctx;
Func func = self->m_func;
column_args<Components...> columns(iter); column_args<Components...> columns(iter);
call_system(iter, func, 0, columns.m_columns); call_system(iter, self->m_func, 0, columns.m_columns);
} }
private: private:
@ -11695,13 +11891,13 @@ class action_invoker {
using Columns = typename column_args<Components ...>::Columns; using Columns = typename column_args<Components ...>::Columns;
public: public:
explicit action_invoker(Func func) explicit action_invoker(Func&& func) noexcept : m_func(std::move(func)) { }
: m_func(func) { } explicit action_invoker(const Func& func) noexcept : m_func(func) { }
/* Invoke system */ /* Invoke system */
template <typename... Targs, template <typename... Targs,
typename std::enable_if<sizeof...(Targs) == sizeof...(Components), void>::type* = nullptr> typename std::enable_if<sizeof...(Targs) == sizeof...(Components), void>::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)index;
(void)columns; (void)columns;
@ -11714,7 +11910,7 @@ public:
/** Add components one by one to parameter pack */ /** Add components one by one to parameter pack */
template <typename... Targs, template <typename... Targs,
typename std::enable_if<sizeof...(Targs) != sizeof...(Components), void>::type* = nullptr> typename std::enable_if<sizeof...(Targs) != sizeof...(Components), void>::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]); call_system(iter, func, index + 1, columns, comps..., columns[index]);
} }
@ -11722,9 +11918,8 @@ public:
static void run(ecs_iter_t *iter) { static void run(ecs_iter_t *iter) {
const Context *ctx = ecs_get(iter->world, iter->system, EcsContext); const Context *ctx = ecs_get(iter->world, iter->system, EcsContext);
action_invoker *self = (action_invoker*)ctx->ctx; action_invoker *self = (action_invoker*)ctx->ctx;
Func func = self->m_func;
column_args<Components...> columns(iter); column_args<Components...> columns(iter);
call_system(iter, func, 0, columns.m_columns); call_system(iter, self->m_func, 0, columns.m_columns);
} }
private: private:
@ -11740,13 +11935,13 @@ class iter_invoker {
using Columns = typename column_args<Components ...>::Columns; using Columns = typename column_args<Components ...>::Columns;
public: public:
explicit iter_invoker(Func func) explicit iter_invoker(Func&& func) noexcept : m_func(std::move(func)) { }
: m_func(func) { } explicit iter_invoker(const Func& func) noexcept : m_func(func) { }
/* Invoke system */ /* Invoke system */
template <typename... Targs, template <typename... Targs,
typename std::enable_if<sizeof...(Targs) == sizeof...(Components), void>::type* = nullptr> typename std::enable_if<sizeof...(Targs) == sizeof...(Components), void>::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)index;
(void)columns; (void)columns;
flecs::iter iter_wrapper(iter); flecs::iter iter_wrapper(iter);
@ -11756,7 +11951,7 @@ public:
/** Add components one by one to parameter pack */ /** Add components one by one to parameter pack */
template <typename... Targs, template <typename... Targs,
typename std::enable_if<sizeof...(Targs) != sizeof...(Components), void>::type* = nullptr> typename std::enable_if<sizeof...(Targs) != sizeof...(Components), void>::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]); call_system(iter, func, index + 1, columns, comps..., columns[index]);
} }
@ -11764,9 +11959,8 @@ public:
static void run(ecs_iter_t *iter) { static void run(ecs_iter_t *iter) {
const Context *ctx = ecs_get(iter->world, iter->system, EcsContext); const Context *ctx = ecs_get(iter->world, iter->system, EcsContext);
iter_invoker *self = (iter_invoker*)ctx->ctx; iter_invoker *self = (iter_invoker*)ctx->ctx;
Func func = self->m_func;
column_args<Components...> columns(iter); column_args<Components...> columns(iter);
call_system(iter, func, 0, columns.m_columns); call_system(iter, self->m_func, 0, columns.m_columns);
} }
private: private:
@ -11950,36 +12144,33 @@ public:
query_iterator<Components...> end() const; query_iterator<Components...> end() const;
template <typename Func> template <typename Func>
void each(Func func) const { void each(Func&& func) const {
ecs_iter_t it = ecs_query_iter(m_query); ecs_iter_t it = ecs_query_iter(m_query);
while (ecs_query_next(&it)) { while (ecs_query_next(&it)) {
_::column_args<Components...> columns(&it); _::column_args<Components...> columns(&it);
_::each_invoker<Func, Components...> ctx(func); _::each_invoker<Func, Components...>::call_system(&it, func, 0, columns.m_columns);
ctx.call_system(&it, func, 0, columns.m_columns);
} }
} }
/* DEPRECATED */ /* DEPRECATED */
template <typename Func> template <typename Func>
void action(Func func) const { void action(Func&& func) const {
ecs_iter_t it = ecs_query_iter(m_query); ecs_iter_t it = ecs_query_iter(m_query);
while (ecs_query_next(&it)) { while (ecs_query_next(&it)) {
_::column_args<Components...> columns(&it); _::column_args<Components...> columns(&it);
_::action_invoker<Func, Components...> ctx(func); _::action_invoker<Func, Components...>::call_system(&it, func, 0, columns.m_columns);
ctx.call_system(&it, func, 0, columns.m_columns);
} }
} }
template <typename Func> template <typename Func>
void iter(Func func) const { void iter(Func&& func) const {
ecs_iter_t it = ecs_query_iter(m_query); ecs_iter_t it = ecs_query_iter(m_query);
while (ecs_query_next(&it)) { while (ecs_query_next(&it)) {
_::column_args<Components...> columns(&it); _::column_args<Components...> columns(&it);
_::iter_invoker<Func, Components...> ctx(func); _::iter_invoker<Func, Components...>::call_system(&it, func, 0, columns.m_columns);
ctx.call_system(&it, func, 0, columns.m_columns);
} }
} }
}; };
@ -12211,11 +12402,12 @@ public:
/* DEPRECATED. Use iter instead. */ /* DEPRECATED. Use iter instead. */
template <typename Func> template <typename Func>
system& action(Func func) { system& action(Func&& func) {
ecs_assert(!m_finalized, ECS_INVALID_PARAMETER, NULL); ecs_assert(!m_finalized, ECS_INVALID_PARAMETER, NULL);
auto ctx = new _::action_invoker<Func, Components...>(func); using invoker_t = typename _::action_invoker<typename std::decay<Func>::type, Components...>;
auto ctx = FLECS_NEW(invoker_t)(std::forward<Func>(func));
create_system(_::action_invoker<Func, Components...>::run, false); create_system(invoker_t::run, false);
EcsContext ctx_value = {ctx}; EcsContext ctx_value = {ctx};
ecs_set_ptr(m_world, m_id, EcsContext, &ctx_value); 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 * is added in the fluent method chain. Create system signature from both
* template parameters and anything provided by the signature method. */ * template parameters and anything provided by the signature method. */
template <typename Func> template <typename Func>
system& iter(Func func) { system& iter(Func&& func) {
ecs_assert(!m_finalized, ECS_INVALID_PARAMETER, NULL); ecs_assert(!m_finalized, ECS_INVALID_PARAMETER, NULL);
auto ctx = new _::iter_invoker<Func, Components...>(func); using invoker_t = typename _::iter_invoker<typename std::decay<Func>::type, Components...>;
auto ctx = FLECS_NEW(invoker_t)(std::forward<Func>(func));
create_system(_::iter_invoker<Func, Components...>::run, false); create_system(invoker_t::run, false);
EcsContext ctx_value = {ctx}; EcsContext ctx_value = {ctx};
ecs_set_ptr(m_world, m_id, EcsContext, &ctx_value); 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 /* Each is similar to action, but accepts a function that operates on a
* single entity */ * single entity */
template <typename Func> template <typename Func>
system& each(Func func) { system& each(Func&& func) {
auto ctx = new _::each_invoker<Func, Components...>(func); using invoker_t = typename _::each_invoker<typename std::decay<Func>::type, Components...>;
auto ctx = FLECS_NEW(invoker_t)(std::forward<Func>(func));
create_system(_::each_invoker<Func, Components...>::run, true); create_system(invoker_t::run, true);
EcsContext ctx_value = {ctx}; EcsContext ctx_value = {ctx};
ecs_set_ptr(m_world, m_id, EcsContext, &ctx_value); ecs_set_ptr(m_world, m_id, EcsContext, &ctx_value);