diff --git a/distr/flecs.c b/distr/flecs.c index c69928476..875292213 100644 --- a/distr/flecs.c +++ b/distr/flecs.c @@ -14977,7 +14977,10 @@ ecs_observer_t* flecs_observer_init( impl->term_index = desc->term_index_; impl->flags = desc->flags_; - bool yield_on_create = false; + ecs_check(!(desc->yield_existing && + (desc->flags_ & (EcsObserverYieldOnCreate|EcsObserverYieldOnDelete))), + ECS_INVALID_PARAMETER, + "cannot set yield_existing and YieldOn* flags at the same time"); /* Check if observer is monitor. Monitors are created as multi observers * since they require pre/post checking of the filter to test if the @@ -14998,8 +15001,8 @@ ecs_observer_t* flecs_observer_init( o->event_count ++; impl->flags |= EcsObserverIsMonitor; if (desc->yield_existing) { + impl->flags |= EcsObserverYieldOnCreate; impl->flags |= EcsObserverYieldOnDelete; - yield_on_create = true; } } else { o->events[i] = event; @@ -15007,7 +15010,7 @@ ecs_observer_t* flecs_observer_init( if (event == EcsOnRemove) { impl->flags |= EcsObserverYieldOnDelete; } else { - yield_on_create = true; + impl->flags |= EcsObserverYieldOnCreate; } } } @@ -15043,7 +15046,7 @@ ecs_observer_t* flecs_observer_init( } } - if (yield_on_create) { + if (impl->flags & EcsObserverYieldOnCreate) { flecs_observer_yield_existing(world, o, false); } diff --git a/distr/flecs.h b/distr/flecs.h index 964c88708..5c19788a7 100644 --- a/distr/flecs.h +++ b/distr/flecs.h @@ -482,6 +482,7 @@ extern "C" { #define EcsQueryHasTableThisVar (1u << 26u) /* Does query have $this table var */ #define EcsQueryCacheYieldEmptyTables (1u << 27u) /* Does query cache empty tables */ + //////////////////////////////////////////////////////////////////////////////// //// Term flags (used by ecs_term_t::flags_) //////////////////////////////////////////////////////////////////////////////// @@ -511,7 +512,9 @@ extern "C" { #define EcsObserverIsDisabled (1u << 3u) /* Is observer entity disabled */ #define EcsObserverIsParentDisabled (1u << 4u) /* Is module parent of observer disabled */ #define EcsObserverBypassQuery (1u << 5u) /* Don't evaluate query for multi-component observer*/ -#define EcsObserverYieldOnDelete (1u << 6u) /* Yield matching entities when deleting observer */ +#define EcsObserverYieldOnCreate (1u << 6u) /* Yield matching entities when creating observer */ +#define EcsObserverYieldOnDelete (1u << 7u) /* Yield matching entities when deleting observer */ + //////////////////////////////////////////////////////////////////////////////// //// Table flags (used by ecs_table_t::flags) diff --git a/include/flecs/private/api_flags.h b/include/flecs/private/api_flags.h index b89ea1b8e..e056ad0c7 100644 --- a/include/flecs/private/api_flags.h +++ b/include/flecs/private/api_flags.h @@ -160,6 +160,7 @@ extern "C" { #define EcsQueryHasTableThisVar (1u << 26u) /* Does query have $this table var */ #define EcsQueryCacheYieldEmptyTables (1u << 27u) /* Does query cache empty tables */ + //////////////////////////////////////////////////////////////////////////////// //// Term flags (used by ecs_term_t::flags_) //////////////////////////////////////////////////////////////////////////////// @@ -189,7 +190,9 @@ extern "C" { #define EcsObserverIsDisabled (1u << 3u) /* Is observer entity disabled */ #define EcsObserverIsParentDisabled (1u << 4u) /* Is module parent of observer disabled */ #define EcsObserverBypassQuery (1u << 5u) /* Don't evaluate query for multi-component observer*/ -#define EcsObserverYieldOnDelete (1u << 6u) /* Yield matching entities when deleting observer */ +#define EcsObserverYieldOnCreate (1u << 6u) /* Yield matching entities when creating observer */ +#define EcsObserverYieldOnDelete (1u << 7u) /* Yield matching entities when deleting observer */ + //////////////////////////////////////////////////////////////////////////////// //// Table flags (used by ecs_table_t::flags) diff --git a/src/observer.c b/src/observer.c index 9e774540d..8465b3ae6 100644 --- a/src/observer.c +++ b/src/observer.c @@ -961,7 +961,10 @@ ecs_observer_t* flecs_observer_init( impl->term_index = desc->term_index_; impl->flags = desc->flags_; - bool yield_on_create = false; + ecs_check(!(desc->yield_existing && + (desc->flags_ & (EcsObserverYieldOnCreate|EcsObserverYieldOnDelete))), + ECS_INVALID_PARAMETER, + "cannot set yield_existing and YieldOn* flags at the same time"); /* Check if observer is monitor. Monitors are created as multi observers * since they require pre/post checking of the filter to test if the @@ -982,8 +985,8 @@ ecs_observer_t* flecs_observer_init( o->event_count ++; impl->flags |= EcsObserverIsMonitor; if (desc->yield_existing) { + impl->flags |= EcsObserverYieldOnCreate; impl->flags |= EcsObserverYieldOnDelete; - yield_on_create = true; } } else { o->events[i] = event; @@ -991,7 +994,7 @@ ecs_observer_t* flecs_observer_init( if (event == EcsOnRemove) { impl->flags |= EcsObserverYieldOnDelete; } else { - yield_on_create = true; + impl->flags |= EcsObserverYieldOnCreate; } } } @@ -1027,7 +1030,7 @@ ecs_observer_t* flecs_observer_init( } } - if (yield_on_create) { + if (impl->flags & EcsObserverYieldOnCreate) { flecs_observer_yield_existing(world, o, false); } diff --git a/test/core/project.json b/test/core/project.json index bceedca94..d93562be3 100644 --- a/test/core/project.json +++ b/test/core/project.json @@ -1531,6 +1531,9 @@ "on_remove_yield_existing_wildcard_multi", "on_remove_yield_existing_wildcard_multi_w_wildcard_pivot", "on_add_remove_yield_existing", + "on_add_remove_yield_existing_flags", + "on_add_remove_no_on_add_yield_existing", + "on_add_remove_no_on_remove_yield_existing", "observer_superset_wildcard", "observer_superset_wildcard_add_isa", "observer_superset_wildcard_add_isa_at_offset", diff --git a/test/core/src/Observer.c b/test/core/src/Observer.c index 90caf138c..736b4c63b 100644 --- a/test/core/src/Observer.c +++ b/test/core/src/Observer.c @@ -4187,6 +4187,150 @@ void Observer_on_add_remove_yield_existing(void) { ecs_fini(world); } +void Observer_on_add_remove_yield_existing_flags(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ Tag }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = &ctx, + .flags_ = EcsObserverYieldOnCreate|EcsObserverYieldOnDelete + }); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, Tag); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], Tag); + + ecs_os_zeromem(&ctx); + + ecs_delete(world, t); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.event_id, Tag); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], Tag); + + ecs_fini(world); +} + +void Observer_on_add_remove_no_on_add_yield_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ Tag }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = &ctx, + .flags_ = EcsObserverYieldOnDelete + }); + + test_int(ctx.invoked, 0); + + ecs_delete(world, t); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnRemove); + test_int(ctx.event_id, Tag); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], Tag); + + ecs_fini(world); +} + +void Observer_on_add_remove_no_on_remove_yield_existing(void) { + ecs_world_t *world = ecs_mini(); + + ECS_TAG(world, Tag); + + /* Create entities before trigger */ + ecs_entity_t e1 = ecs_new_w(world, Tag); + ecs_entity_t e2 = ecs_new_w(world, Tag); + ecs_entity_t e3 = ecs_new_w(world, Tag); + + test_assert(e1 != 0); + test_assert(e2 != 0); + test_assert(e3 != 0); + + Probe ctx = {0}; + ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){ + .query.terms = {{ Tag }}, + .events = {EcsOnAdd, EcsOnRemove}, + .callback = Observer, + .ctx = &ctx, + .flags_ = EcsObserverYieldOnCreate + }); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 3); + test_int(ctx.system, t); + test_int(ctx.event, EcsOnAdd); + test_int(ctx.event_id, Tag); + test_int(ctx.term_count, 1); + test_null(ctx.param); + + test_int(ctx.e[0], e1); + test_int(ctx.e[1], e2); + test_int(ctx.e[2], e3); + test_int(ctx.c[0][0], Tag); + + ecs_os_zeromem(&ctx); + + ecs_delete(world, t); + + test_int(ctx.invoked, 0); + + ecs_fini(world); +} + void Observer_observer_superset_wildcard(void) { ecs_world_t *world = ecs_mini(); diff --git a/test/core/src/main.c b/test/core/src/main.c index 117b49528..2b82d02fc 100644 --- a/test/core/src/main.c +++ b/test/core/src/main.c @@ -1470,6 +1470,9 @@ void Observer_on_remove_yield_existing_wildcard(void); void Observer_on_remove_yield_existing_wildcard_multi(void); void Observer_on_remove_yield_existing_wildcard_multi_w_wildcard_pivot(void); void Observer_on_add_remove_yield_existing(void); +void Observer_on_add_remove_yield_existing_flags(void); +void Observer_on_add_remove_no_on_add_yield_existing(void); +void Observer_on_add_remove_no_on_remove_yield_existing(void); void Observer_observer_superset_wildcard(void); void Observer_observer_superset_wildcard_add_isa(void); void Observer_observer_superset_wildcard_add_isa_at_offset(void); @@ -7934,6 +7937,18 @@ bake_test_case Observer_testcases[] = { "on_add_remove_yield_existing", Observer_on_add_remove_yield_existing }, + { + "on_add_remove_yield_existing_flags", + Observer_on_add_remove_yield_existing_flags + }, + { + "on_add_remove_no_on_add_yield_existing", + Observer_on_add_remove_no_on_add_yield_existing + }, + { + "on_add_remove_no_on_remove_yield_existing", + Observer_on_add_remove_no_on_remove_yield_existing + }, { "observer_superset_wildcard", Observer_observer_superset_wildcard @@ -11165,7 +11180,7 @@ static bake_test_suite suites[] = { "Observer", NULL, NULL, - 216, + 219, Observer_testcases }, {