diff --git a/distr/flecs.c b/distr/flecs.c index b5ccba168..f401fe4e5 100644 --- a/distr/flecs.c +++ b/distr/flecs.c @@ -14361,6 +14361,11 @@ void flecs_emit_forward( ecs_dbg_assert(r->table == elem->table, ECS_INTERNAL_ERROR, NULL); (void)r; + /* If entities already have the component, don't propagate */ + if (flecs_id_record_get_table(rc_idr, it->table)) { + continue; + } + ecs_event_id_record_t *iders[5] = {0}; int32_t ider_count = flecs_event_observers_get( er, rc_idr->id, iders); diff --git a/src/observable.c b/src/observable.c index bfc9bf95f..00db91c27 100644 --- a/src/observable.c +++ b/src/observable.c @@ -1096,6 +1096,11 @@ void flecs_emit_forward( ecs_dbg_assert(r->table == elem->table, ECS_INTERNAL_ERROR, NULL); (void)r; + /* If entities already have the component, don't propagate */ + if (flecs_id_record_get_table(rc_idr, it->table)) { + continue; + } + ecs_event_id_record_t *iders[5] = {0}; int32_t ider_count = flecs_event_observers_get( er, rc_idr->id, iders); diff --git a/test/core/project.json b/test/core/project.json index 09177dcc9..dab0a2283 100644 --- a/test/core/project.json +++ b/test/core/project.json @@ -1696,6 +1696,7 @@ "on_remove_multi_only_optional", "on_add_multi_observers_w_prefab_instance", "on_add_overlapping_multi_observers_w_prefab_instance", + "mask_propagated_component_after_reparent", "cache_test_1", "cache_test_2", "cache_test_3", diff --git a/test/core/src/Observer.c b/test/core/src/Observer.c index cf6c547de..287545205 100644 --- a/test/core/src/Observer.c +++ b/test/core/src/Observer.c @@ -5,6 +5,15 @@ void Observer(ecs_iter_t *it) { probe_system_w_ctx(it, it->ctx); } +static +void Observer_w_field(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); + + Position *p = ecs_field(it, Position, 0); + test_assert(p != NULL); + // data is uninitialized +} + static void Observer_w_value_1(ecs_iter_t *it) { probe_system_w_ctx(it, it->ctx); @@ -8405,10 +8414,8 @@ void Observer_cache_test_9(void) { ecs_clear(world, base_2); - /* Once for Position of base_2, twice because Position is reachable through - * two paths. */ - test_int(ctx.invoked, 2); - test_int(ctx.count, 2); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); test_int(ctx.e[0], inst); test_int(ctx.s[0][0], base_2); test_int(ctx.c[0][0], ecs_id(Position)); @@ -8418,10 +8425,8 @@ void Observer_cache_test_9(void) { ecs_clear(world, base_3); - /* Once for Position of base_2, twice because Position is reachable through - * two paths. */ - test_int(ctx.invoked, 2); - test_int(ctx.count, 2); + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); test_int(ctx.e[0], inst); test_int(ctx.s[0][0], base_3); test_int(ctx.c[0][0], ecs_id(Position)); @@ -9707,3 +9712,51 @@ void Observer_on_add_overlapping_multi_observers_w_prefab_instance(void) { ecs_fini(world); } + +void Observer_mask_propagated_component_after_reparent(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + Probe ctx = {0}; + + ecs_entity_t o = ecs_observer(world, { + .query.terms = { + { ecs_id(Position), .src.id = EcsUp }, + { ecs_id(Position) } + }, + .events = { EcsOnAdd }, + .callback = Observer_w_field, + .ctx = &ctx + }); + + ecs_entity_t root = ecs_new_w(world, Position); + ecs_entity_t parent = ecs_new_w(world, Position); + + test_int(ctx.invoked, 0); + + ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + ecs_add(world, child, Position); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], child); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_uint(ctx.s[0][0], parent); + test_uint(ctx.s[0][1], 0); + + ecs_os_zeromem(&ctx); + + ecs_add_pair(world, parent, EcsChildOf, root); + + test_int(ctx.invoked, 1); + test_int(ctx.count, 1); + test_int(ctx.e[0], parent); + test_int(ctx.system, o); + test_int(ctx.event, EcsOnAdd); + test_uint(ctx.s[0][0], root); + test_uint(ctx.s[0][1], 0); + + ecs_fini(world); +} diff --git a/test/core/src/main.c b/test/core/src/main.c index 8d0f06d35..01afcee47 100644 --- a/test/core/src/main.c +++ b/test/core/src/main.c @@ -1635,6 +1635,7 @@ void Observer_on_add_multi_only_optional(void); void Observer_on_remove_multi_only_optional(void); void Observer_on_add_multi_observers_w_prefab_instance(void); void Observer_on_add_overlapping_multi_observers_w_prefab_instance(void); +void Observer_mask_propagated_component_after_reparent(void); void Observer_cache_test_1(void); void Observer_cache_test_2(void); void Observer_cache_test_3(void); @@ -8674,6 +8675,10 @@ bake_test_case Observer_testcases[] = { "on_add_overlapping_multi_observers_w_prefab_instance", Observer_on_add_overlapping_multi_observers_w_prefab_instance }, + { + "mask_propagated_component_after_reparent", + Observer_mask_propagated_component_after_reparent + }, { "cache_test_1", Observer_cache_test_1 @@ -11565,7 +11570,7 @@ static bake_test_suite suites[] = { "Observer", NULL, NULL, - 231, + 232, Observer_testcases }, {