Files
PixelDefense/engine/libs/flecs/test/api/src/Observer.c

4998 lines
124 KiB
C

#include <api.h>
static
void Observer(ecs_iter_t *it) {
probe_system_w_ctx(it, it->ctx);
}
static
void Observer_w_value(ecs_iter_t *it) {
probe_system_w_ctx(it, it->ctx);
test_int(it->count, 1);
test_assert(it->entities != NULL);
test_assert(it->entities[0] != 0);
Position *p = ecs_field(it, Position, 1);
test_int(p->x, 10);
test_int(p->y, 20);
if (it->field_count > 1) {
Velocity *v = ecs_field(it, Velocity, 2);
test_int(v->x, 1);
test_int(v->y, 2);
}
}
static
void Observer_w_1_value(ecs_iter_t *it) {
probe_system_w_ctx(it, it->ctx);
test_int(it->count, 1);
test_assert(it->entities != NULL);
test_assert(it->entities[0] != 0);
Position *p = ecs_field(it, Position, 1);
test_int(p->x, 10);
test_int(p->y, 20);
}
static
void Observer_w_filter_term(ecs_iter_t *it) {
probe_system_w_ctx(it, it->ctx);
test_int(it->count, 1);
test_assert(it->entities != NULL);
test_assert(it->entities[0] != 0);
/* Not guaranteed to be NULL */
// test_assert(it->ptrs == NULL);
}
static
void Observer_w_1_filter_term(ecs_iter_t *it) {
probe_system_w_ctx(it, it->ctx);
test_int(it->count, 1);
test_assert(it->entities != NULL);
test_assert(it->entities[0] != 0);
test_assert(it->ptrs != NULL);
/* Not guaranteed to be NULL */
// test_assert(it->ptrs[0] == NULL);
test_assert(it->ptrs[1] != NULL);
Velocity *v = ecs_field(it, Velocity, 2);
test_int(v->x, 1);
test_int(v->y, 2);
}
static
void Observer_w_2_filter_terms(ecs_iter_t *it) {
probe_system_w_ctx(it, it->ctx);
test_int(it->count, 1);
test_assert(it->entities != NULL);
test_assert(it->entities[0] != 0);
test_assert(it->ptrs != NULL);
test_assert(it->sizes != NULL);
/* Not guaranteed to be NULL */
// test_assert(it->ptrs[0] == NULL);
// test_assert(it->ptrs[1] == NULL);
test_assert(it->ptrs[2] != NULL);
Mass *m = ecs_field(it, Mass, 3);
test_int(m[0], 100);
}
static bool dummy_called = false;
static
void Dummy(ecs_iter_t *it) {
dummy_called = true;
}
void Observer_2_terms_w_on_add(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{TagA}, {TagB}},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, TagB);
test_int(ctx.invoked, 0);
ecs_add_id(world, e, TagA);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], TagA);
test_int(ctx.c[0][1], TagB);
ecs_fini(world);
}
void Observer_2_terms_w_on_remove(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{TagA}, {TagB}},
.events = {EcsOnRemove},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, TagA);
test_int(ctx.invoked, 0);
ecs_add_id(world, e, TagB);
test_int(ctx.invoked, 0);
ecs_remove_id(world, e, TagA);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnRemove);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], TagA);
test_int(ctx.c[0][1], TagB);
ecs_fini(world);
}
void Observer_2_terms_w_on_set_value(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ecs_id(Position)}, {ecs_id(Velocity)}},
.events = {EcsOnSet},
.callback = Observer_w_value,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_set(world, e, Velocity, {1, 2});
test_int(ctx.invoked, 0);
ecs_set(world, e, Position, {10, 20});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnSet);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.c[0][1], ecs_id(Velocity));
ecs_fini(world);
}
void Observer_2_terms_w_on_remove_value(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ecs_id(Position)}, {ecs_id(Velocity)}},
.events = {EcsOnRemove},
.callback = Observer_w_value,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_set(world, e, Velocity, {1, 2});
test_int(ctx.invoked, 0);
ecs_set(world, e, Position, {10, 20});
test_int(ctx.invoked, 0);
ecs_remove(world, e, Position);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnRemove);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.c[0][1], ecs_id(Velocity));
ecs_fini(world);
}
void Observer_2_terms_w_on_add_2nd(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{TagA}, {TagB}},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, TagA);
test_int(ctx.invoked, 0);
ecs_add_id(world, e, TagB);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], TagA);
test_int(ctx.c[0][1], TagB);
ecs_fini(world);
}
void Observer_2_terms_w_on_remove_2nd(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{TagA}, {TagB}},
.events = {EcsOnRemove},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, TagA);
test_int(ctx.invoked, 0);
ecs_add_id(world, e, TagB);
test_int(ctx.invoked, 0);
ecs_remove_id(world, e, TagB);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnRemove);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], TagA);
test_int(ctx.c[0][1], TagB);
ecs_fini(world);
}
void Observer_2_pair_terms_w_on_add(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, RelA);
ECS_TAG(world, RelB);
ECS_TAG(world, ObjA);
ECS_TAG(world, ObjB);
ecs_id_t pair_a = ecs_pair(RelA, ObjA);
ecs_id_t pair_b = ecs_pair(RelB, ObjB);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{pair_a}, {pair_b}},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_pair(world, e, RelA, ObjA);
test_int(ctx.invoked, 0);
ecs_add_pair(world, e, RelB, ObjB);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], pair_a);
test_int(ctx.c[0][1], pair_b);
ecs_fini(world);
}
void Observer_2_pair_terms_w_on_remove(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, RelA);
ECS_TAG(world, RelB);
ECS_TAG(world, ObjA);
ECS_TAG(world, ObjB);
ecs_id_t pair_a = ecs_pair(RelA, ObjA);
ecs_id_t pair_b = ecs_pair(RelB, ObjB);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{pair_a}, {pair_b}},
.events = {EcsOnRemove},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_pair(world, e, RelA, ObjA);
test_int(ctx.invoked, 0);
ecs_add_pair(world, e, RelB, ObjB);
test_int(ctx.invoked, 0);
ecs_remove_pair(world, e, RelB, ObjB);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnRemove);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], pair_a);
test_int(ctx.c[0][1], pair_b);
ecs_fini(world);
}
void Observer_2_wildcard_pair_terms_w_on_add(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, RelA);
ECS_TAG(world, RelB);
ECS_TAG(world, ObjA);
ECS_TAG(world, ObjB);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ecs_pair(RelA, ObjA)}, {ecs_pair(RelB, EcsWildcard)}},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_pair(world, e, RelA, ObjA);
test_int(ctx.invoked, 0);
ecs_add_pair(world, e, RelB, ObjB);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_pair(RelA, ObjA));
test_int(ctx.c[0][1], ecs_pair(RelB, ObjB));
ecs_fini(world);
}
void Observer_2_wildcard_pair_terms_w_on_add_2_matching(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, RelA);
ECS_TAG(world, RelB);
ECS_TAG(world, ObjA);
ECS_TAG(world, ObjB);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ecs_pair(RelA, ObjA)}, {ecs_pair(RelB, EcsWildcard)}},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_pair(world, e, RelA, ObjA);
test_int(ctx.invoked, 0);
ecs_add_pair(world, e, RelB, ObjA);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_pair(RelA, ObjA));
test_int(ctx.c[0][1], ecs_pair(RelB, ObjA));
ecs_os_zeromem(&ctx);
ecs_add_pair(world, e, RelB, ObjB);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_pair(RelA, ObjA));
test_int(ctx.c[0][1], ecs_pair(RelB, ObjB));
ecs_fini(world);
}
void Observer_2_wildcard_pair_terms_w_on_add_3_matching(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, RelA);
ECS_TAG(world, RelB);
ECS_TAG(world, ObjA);
ECS_TAG(world, ObjB);
ECS_TAG(world, ObjC);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ecs_pair(RelA, ObjA)}, {ecs_pair(RelB, EcsWildcard)}},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_pair(world, e, RelA, ObjA);
test_int(ctx.invoked, 0);
ecs_add_pair(world, e, RelB, ObjA);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_pair(RelA, ObjA));
test_int(ctx.c[0][1], ecs_pair(RelB, ObjA));
ecs_os_zeromem(&ctx);
ecs_add_pair(world, e, RelB, ObjC);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_pair(RelA, ObjA));
test_int(ctx.c[0][1], ecs_pair(RelB, ObjC));
ecs_os_zeromem(&ctx);
ecs_add_pair(world, e, RelB, ObjB);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_pair(RelA, ObjA));
test_int(ctx.c[0][1], ecs_pair(RelB, ObjB));
ecs_fini(world);
}
void Observer_2_wildcard_pair_terms_w_on_remove(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, RelA);
ECS_TAG(world, RelB);
ECS_TAG(world, ObjA);
ECS_TAG(world, ObjB);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ecs_pair(RelA, ObjA)}, {ecs_pair(RelB, EcsWildcard)}},
.events = {EcsOnRemove},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_pair(world, e, RelA, ObjA);
test_int(ctx.invoked, 0);
ecs_add_pair(world, e, RelB, ObjB);
test_int(ctx.invoked, 0);
ecs_remove_pair(world, e, RelB, ObjB);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnRemove);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_pair(RelA, ObjA));
test_int(ctx.c[0][1], ecs_pair(RelB, ObjB));
ecs_fini(world);
}
void Observer_on_set_n_entities(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Self);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ecs_id(Self)}},
.events = {EcsOnSet},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e_1 = ecs_new_id(world);
test_int(ctx.invoked, 0);
ecs_set(world, e_1, Self, {e_1});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnSet);
test_int(ctx.term_count, 1);
test_null(ctx.param);
test_int(ctx.e[0], e_1);
test_int(ctx.c[0][0], ecs_id(Self));
ecs_os_zeromem(&ctx);
ecs_entity_t e_2 = ecs_new_id(world);
test_int(ctx.invoked, 0);
ecs_set(world, e_2, Self, {e_2});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnSet);
test_int(ctx.term_count, 1);
test_null(ctx.param);
test_int(ctx.e[0], e_2);
test_int(ctx.c[0][0], ecs_id(Self));
ecs_os_zeromem(&ctx);
ecs_entity_t e_3 = ecs_new_id(world);
test_int(ctx.invoked, 0);
ecs_set(world, e_3, Self, {e_3});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnSet);
test_int(ctx.term_count, 1);
test_null(ctx.param);
test_int(ctx.e[0], e_3);
test_int(ctx.c[0][0], ecs_id(Self));
ecs_fini(world);
}
void Observer_on_set_n_entities_2_comp(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, Obj);
ECS_COMPONENT(world, Self);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ecs_id(Self)}, {ecs_pair(ecs_id(Self), EcsWildcard)}},
.events = {EcsOnSet},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e_1 = ecs_new_id(world);
ecs_set(world, e_1, Self, {e_1});
test_int(ctx.invoked, 0);
ecs_set_pair(world, e_1, Self, Obj, {e_1});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnSet);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e_1);
test_int(ctx.c[0][0], ecs_id(Self));
test_int(ctx.c[0][1], ecs_pair(ecs_id(Self), Obj));
ecs_os_zeromem(&ctx);
ecs_entity_t e_2 = ecs_new_id(world);
ecs_set(world, e_2, Self, {e_2});
test_int(ctx.invoked, 0);
ecs_set_pair(world, e_2, Self, Obj, {e_2});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnSet);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e_2);
test_int(ctx.c[0][0], ecs_id(Self));
test_int(ctx.c[0][1], ecs_pair(ecs_id(Self), Obj));
ecs_fini(world);
}
void Observer_wildcard_pair_w_pred_component(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, ObjA);
ECS_TAG(world, ObjB);
ECS_COMPONENT(world, Position);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ecs_pair(ecs_id(Position), EcsWildcard)}},
.events = {EcsOnSet},
.callback = Observer_w_value,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
test_int(ctx.invoked, 0);
ecs_set_pair(world, e, Position, ObjA, {10, 20});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnSet);
test_int(ctx.term_count, 1);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_pair(ecs_id(Position), ObjA));
/* Change existing component without triggering OnSet as the callback
* expects value {10, 20}, then add a new component with {10, 20} */
Position *p = ecs_get_mut_pair(world, e, Position, ObjA);
test_assert(p != NULL);
test_int(p->x, 10);
test_int(p->y, 20);
p->x = 30;
p->y = 40;
ecs_os_zeromem(&ctx);
ecs_set_pair(world, e, Position, ObjB, {10, 20});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnSet);
test_int(ctx.term_count, 1);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_pair(ecs_id(Position), ObjB));
ecs_fini(world);
}
void Observer_wildcard_pair_w_obj_component(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, RelA);
ECS_TAG(world, RelB);
ECS_COMPONENT(world, Position);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ ecs_pair(EcsWildcard, ecs_id(Position)) }},
.events = {EcsOnSet},
.callback = Observer_w_value,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
test_int(ctx.invoked, 0);
ecs_set_pair_object(world, e, RelA, Position, {10, 20});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnSet);
test_int(ctx.term_count, 1);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_pair(RelA, ecs_id(Position)));
/* Change existing component without triggering OnSet as the callback
* expects value {10, 20}, then add a new component with {10, 20} */
Position *p = ecs_get_mut_pair_second(world, e, RelA, Position);
test_assert(p != NULL);
test_int(p->x, 10);
test_int(p->y, 20);
p->x = 30;
p->y = 40;
ecs_os_zeromem(&ctx);
ecs_set_pair_object(world, e, RelB, Position, {10, 20});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnSet);
test_int(ctx.term_count, 1);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_pair(RelB, ecs_id(Position)));
ecs_fini(world);
}
void Observer_2_terms_1_not_w_on_add(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{TagA}, {TagB, .oper = EcsNot}},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, TagB);
test_int(ctx.invoked, 0);
ecs_add_id(world, e, TagA);
test_int(ctx.invoked, 0);
ecs_clear(world, e);
test_int(ctx.invoked, 0);
test_assert(!ecs_has(world, e, TagB));
ecs_add_id(world, e, TagA);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], TagA);
test_int(ctx.c[0][1], TagB);
ecs_fini(world);
}
void Observer_2_terms_1_not_w_on_remove(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{TagA}, {TagB, .oper = EcsNot}},
.events = {EcsOnRemove},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, TagB);
test_int(ctx.invoked, 0);
ecs_add_id(world, e, TagA);
test_int(ctx.invoked, 0);
ecs_remove_id(world, e, TagA);
test_int(ctx.invoked, 0);
ecs_clear(world, e);
test_assert(!ecs_has(world, e, TagB));
ecs_add_id(world, e, TagA);
test_int(ctx.invoked, 0);
ecs_remove_id(world, e, TagA);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnRemove);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], TagA);
test_int(ctx.c[0][1], TagB);
ecs_fini(world);
}
void Observer_2_terms_w_on_set(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ecs_id(Position)}, {TagB}},
.events = {EcsOnSet},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, TagB);
test_int(ctx.invoked, 0);
ecs_add(world, e, Position);
test_int(ctx.invoked, 0);
ecs_set(world, e, Position, {10, 20});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnSet);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.c[0][1], TagB);
ecs_fini(world);
}
void Observer_2_terms_w_un_set(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ecs_id(Position)}, {TagB}},
.events = {EcsUnSet},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, TagB);
test_int(ctx.invoked, 0);
ecs_add(world, e, Position);
test_int(ctx.invoked, 0);
ecs_set(world, e, Position, {10, 20});
test_int(ctx.invoked, 0);
ecs_remove(world, e, Position);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsUnSet);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.c[0][1], TagB);
ecs_fini(world);
}
void Observer_3_terms_2_or_on_add(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
ECS_TAG(world, TagC);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{TagA}, {TagB, .oper = EcsOr}, {TagC }},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, TagA);
test_int(ctx.invoked, 0);
ecs_add_id(world, e, TagB);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], TagA);
test_int(ctx.c[0][1], TagB);
ecs_os_zeromem(&ctx);
ecs_add_id(world, e, TagC);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], TagA);
test_int(ctx.c[0][1], TagC);
ecs_fini(world);
}
void Observer_3_terms_2_or_on_remove(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
ECS_TAG(world, TagC);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{TagA}, {TagB, .oper = EcsOr}, {TagC }},
.events = {EcsOnRemove},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, TagA);
test_int(ctx.invoked, 0);
ecs_add_id(world, e, TagB);
test_int(ctx.invoked, 0);
ecs_add_id(world, e, TagC);
test_int(ctx.invoked, 0);
ecs_remove_id(world, e, TagC);
test_int(ctx.invoked, 1);
ecs_remove_id(world, e, TagB);
test_int(ctx.invoked, 2);
ecs_remove_id(world, e, TagA);
test_int(ctx.invoked, 2);
ecs_fini(world);
}
void Observer_2_terms_w_from_entity_on_add(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
ecs_entity_t x = ecs_new_id(world);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{TagA}, {TagB, .src.id = x}},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, TagA);
test_int(ctx.invoked, 0);
ecs_add_id(world, e, TagB);
test_int(ctx.invoked, 0);
ecs_clear(world, e);
test_int(ctx.invoked, 0);
ecs_add_id(world, x, TagB);
test_int(ctx.invoked, 0);
ecs_add_id(world, e, TagA);
test_int(ctx.invoked, 1);
ecs_fini(world);
}
void Observer_2_terms_on_remove_on_clear(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{TagA}, {TagB}},
.events = {EcsOnRemove},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, TagA);
test_int(ctx.invoked, 0);
ecs_add_id(world, e, TagB);
test_int(ctx.invoked, 0);
ecs_clear(world, e);
test_int(ctx.invoked, 1);
ecs_fini(world);
}
void Observer_2_terms_on_remove_on_delete(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{TagA}, {TagB}},
.events = {EcsOnRemove},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, TagA);
test_int(ctx.invoked, 0);
ecs_add_id(world, e, TagB);
test_int(ctx.invoked, 0);
ecs_delete(world, e);
test_int(ctx.invoked, 1);
ecs_fini(world);
}
void Observer_add_after_delete_observer(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ecs_entity_t observer = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{.id = ecs_id(Position) }},
.events = {EcsOnAdd},
.callback = Dummy
});
ecs_entity_t e1 = ecs_new(world, Position);
test_assert(e1 != 0);
test_assert(ecs_has(world, e1, Position));
test_int(dummy_called, 1);
dummy_called = 0;
ecs_delete(world, observer);
test_int(dummy_called, 0);
ecs_entity_t e2 = ecs_new(world, Position);
test_assert(e2 != 0);
test_assert(ecs_has(world, e2, Position));
test_int(dummy_called, 0);
ecs_fini(world);
}
void Observer_remove_after_delete_observer(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ecs_entity_t observer = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{.id = ecs_id(Position) }},
.events = {EcsOnRemove},
.callback = Dummy
});
ecs_entity_t e1 = ecs_new(world, Position);
test_assert(e1 != 0);
test_assert(ecs_has(world, e1, Position));
test_int(dummy_called, 0);
ecs_remove(world, e1, Position);
test_int(dummy_called, 1);
dummy_called = 0;
ecs_delete(world, observer);
test_int(dummy_called, 0);
ecs_entity_t e2 = ecs_new(world, Position);
test_assert(e2 != 0);
test_assert(ecs_has(world, e2, Position));
test_int(dummy_called, 0);
ecs_remove(world, e2, Position);
test_int(dummy_called, 0);
ecs_fini(world);
}
static int ctx_value;
static
void ctx_free(void *ctx) {
test_assert(&ctx_value == ctx);
ctx_value ++;
}
static int binding_ctx_value;
static
void binding_ctx_free(void *ctx) {
test_assert(&binding_ctx_value == ctx);
binding_ctx_value ++;
}
static int ctx_value_2;
static
void ctx_free_2(void *ctx) {
test_assert(&ctx_value_2 == ctx);
ctx_value_2 ++;
}
static int binding_ctx_value_2;
static
void binding_ctx_free_2(void *ctx) {
test_assert(&binding_ctx_value_2 == ctx);
binding_ctx_value_2 ++;
}
void Observer_delete_observer_w_ctx(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, Tag);
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ Tag }},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx_value,
.ctx_free = ctx_free,
.binding_ctx = &binding_ctx_value,
.binding_ctx_free = binding_ctx_free
});
test_assert(o != 0);
test_assert(ecs_observer_get_ctx(world, o) == &ctx_value);
test_assert(ecs_observer_get_binding_ctx(world, o) == &binding_ctx_value);
ecs_delete(world, o);
test_int(ctx_value, 1);
test_int(binding_ctx_value, 1);
ecs_fini(world);
}
void Observer_update_ctx(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ecs_entity_t system = ecs_observer(world, {
.filter.terms = {{.id = Tag}},
.callback = Dummy,
.events = {EcsOnAdd},
.ctx = &ctx_value,
.ctx_free = ctx_free,
.binding_ctx = &binding_ctx_value,
.binding_ctx_free = binding_ctx_free
});
test_assert(system != 0);
test_assert(ecs_observer_get_ctx(world, system) == &ctx_value);
test_assert(ecs_observer_get_binding_ctx(world, system)
== &binding_ctx_value);
ecs_observer(world, {
.entity = system,
.ctx = &ctx_value,
.ctx_free = ctx_free,
.binding_ctx = &binding_ctx_value,
.binding_ctx_free = binding_ctx_free
});
test_int(ctx_value, 0);
test_int(binding_ctx_value, 0);
test_int(ctx_value_2, 0);
test_int(binding_ctx_value_2, 0);
ecs_observer(world, {
.entity = system,
.ctx = &ctx_value_2,
.ctx_free = ctx_free_2,
.binding_ctx = &binding_ctx_value_2,
.binding_ctx_free = binding_ctx_free_2
});
test_int(ctx_value, 1);
test_int(binding_ctx_value, 1);
test_int(ctx_value_2, 0);
test_int(binding_ctx_value_2, 0);
ecs_delete(world, system);
test_int(ctx_value, 1);
test_int(binding_ctx_value, 1);
test_int(ctx_value_2, 1);
test_int(binding_ctx_value_2, 1);
ecs_fini(world);
}
void Observer_filter_w_strings(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t o = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{.first.name = "TagA"}, {.first.name = "TagB"}},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, TagB);
test_int(ctx.invoked, 0);
ecs_add_id(world, e, TagA);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], TagA);
test_int(ctx.c[0][1], TagB);
ecs_fini(world);
}
static ecs_table_t *trigger_table;
void TypeObserver(ecs_iter_t *it) {
trigger_table = it->table;
}
void Observer_iter_type_set(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, Tag);
Probe ctx = {0};
ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ Tag }},
.events = {EcsOnAdd},
.callback = TypeObserver,
.ctx = &ctx
});
test_assert(t != 0);
ecs_new(world, Tag);
test_assert(trigger_table != NULL);
test_assert(trigger_table != NULL);
const ecs_type_t *type = ecs_table_get_type(trigger_table);
test_int(type->count, 1);
test_int(type->array[0], Tag);
ecs_fini(world);
}
void ObserverReadonly(ecs_iter_t *it) {
probe_system_w_ctx(it, it->ctx);
test_bool(ecs_field_is_readonly(it, 1), true);
}
void Observer_readonly_term(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
Probe ctx = {0};
ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ TagA, .inout = EcsIn }},
.events = {EcsOnAdd},
.callback = ObserverReadonly,
.ctx = &ctx
});
ecs_entity_t e = ecs_new_id(world);
test_assert(e != 0);
ecs_add(world, e, TagA);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, t);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.event_id, TagA);
test_int(ctx.term_count, 1);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], TagA);
ecs_fini(world);
}
void Observer_trigger_on_prefab(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
Probe ctx_1 = {0};
Probe ctx_2 = {0};
ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ TagA }},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx_1
});
ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ TagA }, { EcsPrefab, .oper = EcsOptional}},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx_2
});
ecs_new(world, TagA);
test_int(ctx_1.invoked, 1);
test_int(ctx_2.invoked, 1);
ecs_os_zeromem(&ctx_1);
ecs_os_zeromem(&ctx_2);
ecs_entity_t e = ecs_new_w_id(world, EcsPrefab);
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 0);
ecs_add(world, e, TagA);
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 1);
ecs_os_zeromem(&ctx_2);
e = ecs_new_w_id(world, EcsDisabled);
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 0);
ecs_add(world, e, TagA);
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 0);
ecs_fini(world);
}
void Observer_trigger_on_disabled(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
Probe ctx_1 = {0};
Probe ctx_2 = {0};
ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ TagA }},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx_1
});
ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ TagA }, { EcsDisabled, .oper = EcsOptional}},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx_2
});
ecs_new(world, TagA);
test_int(ctx_1.invoked, 1);
test_int(ctx_2.invoked, 1);
ecs_os_zeromem(&ctx_1);
ecs_os_zeromem(&ctx_2);
ecs_entity_t e = ecs_new_w_id(world, EcsPrefab);
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 0);
ecs_add(world, e, TagA);
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 0);
ecs_os_zeromem(&ctx_2);
e = ecs_new_w_id(world, EcsDisabled);
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 0);
ecs_add(world, e, TagA);
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 1);
ecs_fini(world);
}
static
void UnSet(ecs_iter_t *it) {
probe_iter(it);
}
static
void UnSetA(ecs_iter_t *it) { }
static
void UnSetB(ecs_iter_t *it) {
}
void Observer_unset_1_of_1(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_OBSERVER(world, UnSet, EcsUnSet, Position);
Probe ctx = { 0 };
ecs_set_ctx(world, &ctx, NULL);
ecs_entity_t e = ecs_new(world, Position);
test_int(ctx.invoked, 0);
ecs_set(world, e, Position, {10, 20});
test_int(ctx.invoked, 0);
ecs_set(world, e, Velocity, {1, 2});
test_int(ctx.invoked, 0);
ecs_remove(world, e, Velocity);
test_int(ctx.invoked, 0);
ecs_remove(world, e, Position);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, UnSet);
test_int(ctx.term_count, 1);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.s[0][0], 0);
ecs_os_zeromem(&ctx);
ecs_remove(world, e, Position);
test_int(ctx.invoked, 0);
ecs_os_zeromem(&ctx);
ecs_fini(world);
}
void Observer_unset_1_of_2(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_OBSERVER(world, UnSet, EcsUnSet, Position, Velocity);
Probe ctx = { 0 };
ecs_set_ctx(world, &ctx, NULL);
ecs_entity_t e = ecs_new(world, Position);
test_int(ctx.invoked, 0);
ecs_set(world, e, Position, {10, 20});
test_int(ctx.invoked, 0);
ecs_set(world, e, Velocity, {1, 2});
test_int(ctx.invoked, 0);
ecs_remove(world, e, Position);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, UnSet);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.s[0][0], 0);
test_int(ctx.c[0][1], ecs_id(Velocity));
test_int(ctx.s[0][1], 0);
ecs_os_zeromem(&ctx);
ecs_remove(world, e, Velocity);
test_int(ctx.invoked, 0);
ecs_os_zeromem(&ctx);
ecs_remove(world, e, Position);
test_int(ctx.invoked, 0);
ecs_os_zeromem(&ctx);
ecs_fini(world);
test_int(ctx.invoked, 0);
}
void Observer_unset_1_of_3(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_COMPONENT(world, Mass);
ECS_OBSERVER(world, UnSet, EcsUnSet, Position, Velocity, Mass);
Probe ctx = { 0 };
ecs_set_ctx(world, &ctx, NULL);
ecs_entity_t e = ecs_new(world, Position);
test_int(ctx.invoked, 0);
ecs_set(world, e, Position, {10, 20});
test_int(ctx.invoked, 0);
ecs_set(world, e, Velocity, {1, 2});
test_int(ctx.invoked, 0);
ecs_set(world, e, Mass, {1});
test_int(ctx.invoked, 0);
ecs_remove(world, e, Position);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, UnSet);
test_int(ctx.term_count, 3);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.s[0][0], 0);
test_int(ctx.c[0][1], ecs_id(Velocity));
test_int(ctx.s[0][1], 0);
test_int(ctx.c[0][2], ecs_id(Mass));
test_int(ctx.s[0][2], 0);
ecs_os_zeromem(&ctx);
ecs_remove(world, e, Mass);
test_int(ctx.invoked, 0);
ecs_os_zeromem(&ctx);
ecs_remove(world, e, Velocity);
test_int(ctx.invoked, 0);
ecs_os_zeromem(&ctx);
ecs_remove(world, e, Position);
test_int(ctx.invoked, 0);
ecs_os_zeromem(&ctx);
ecs_fini(world);
test_int(ctx.invoked, 0);
}
void Observer_unset_on_delete_1(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_OBSERVER(world, UnSet, EcsUnSet, Position);
Probe ctx = { 0 };
ecs_set_ctx(world, &ctx, NULL);
ecs_new(world, Position);
test_int(ctx.invoked, 0);
ecs_new(world, Position);
test_int(ctx.invoked, 0);
ecs_entity_t e = ecs_new(world, Position);
test_int(ctx.invoked, 0);
ecs_delete(world, e);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, UnSet);
test_int(ctx.term_count, 1);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.s[0][0], 0);
ecs_fini(world);
}
void Observer_unset_on_delete_2(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_OBSERVER(world, UnSet, EcsUnSet, Position, Velocity);
Probe ctx = { 0 };
ecs_set_ctx(world, &ctx, NULL);
ecs_entity_t e1 = ecs_new(world, Position);
ecs_add(world, e1, Velocity);
test_int(ctx.invoked, 0);
ecs_entity_t e2 = ecs_new(world, Position);
ecs_add(world, e2, Velocity);
test_int(ctx.invoked, 0);
ecs_entity_t e3 = ecs_new(world, Position);
ecs_add(world, e3, Velocity);
test_int(ctx.invoked, 0);
ecs_delete(world, e3);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, UnSet);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e3);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.s[0][0], 0);
test_int(ctx.c[0][1], ecs_id(Velocity));
test_int(ctx.s[0][1], 0);
ecs_fini(world);
}
void Observer_unset_on_delete_3(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_COMPONENT(world, Mass);
ECS_OBSERVER(world, UnSet, EcsUnSet, Position, Velocity, Mass);
Probe ctx = { 0 };
ecs_set_ctx(world, &ctx, NULL);
ecs_entity_t e1 = ecs_new(world, Position);
ecs_add(world, e1, Velocity);
ecs_add(world, e1, Mass);
test_int(ctx.invoked, 0);
ecs_entity_t e2 = ecs_new(world, Position);
ecs_add(world, e2, Velocity);
ecs_add(world, e2, Mass);
test_int(ctx.invoked, 0);
ecs_entity_t e3 = ecs_new(world, Position);
ecs_add(world, e3, Velocity);
ecs_add(world, e3, Mass);
test_int(ctx.invoked, 0);
ecs_delete(world, e3);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, UnSet);
test_int(ctx.term_count, 3);
test_null(ctx.param);
test_int(ctx.e[0], e3);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.s[0][0], 0);
test_int(ctx.c[0][1], ecs_id(Velocity));
test_int(ctx.s[0][1], 0);
test_int(ctx.c[0][2], ecs_id(Mass));
test_int(ctx.s[0][2], 0);
ecs_fini(world);
}
void Observer_unset_on_fini_1(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_OBSERVER(world, UnSet, EcsUnSet, Position);
Probe ctx = { 0 };
ecs_set_ctx(world, &ctx, NULL);
ecs_entity_t e1 = ecs_new(world, Position);
test_int(ctx.invoked, 0);
ecs_entity_t e2 = ecs_new(world, Position);
test_int(ctx.invoked, 0);
ecs_entity_t e3 = ecs_new(world, Position);
test_int(ctx.invoked, 0);
ecs_new(world, Velocity);
test_int(ctx.invoked, 0);
ecs_fini(world);
test_int(ctx.count, 3);
test_int(ctx.system, UnSet);
test_int(ctx.term_count, 1);
test_null(ctx.param);
test_int(ctx.e[0], e3);
test_int(ctx.e[1], e2);
test_int(ctx.e[2], e1);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.s[0][0], 0);
}
void Observer_unset_on_fini_2(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_OBSERVER(world, UnSet, EcsUnSet, Position, Velocity);
Probe ctx = { 0 };
ecs_set_ctx(world, &ctx, NULL);
ecs_entity_t e1 = ecs_new(world, Position);
ecs_add(world, e1, Velocity);
test_int(ctx.invoked, 0);
ecs_entity_t e2 = ecs_new(world, Position);
ecs_add(world, e2, Velocity);
test_int(ctx.invoked, 0);
ecs_entity_t e3 = ecs_new(world, Position);
ecs_add(world, e3, Velocity);
test_int(ctx.invoked, 0);
ecs_new(world, Position);
test_int(ctx.invoked, 0);
ecs_new(world, Velocity);
test_int(ctx.invoked, 0);
ecs_fini(world);
test_int(ctx.count, 3);
test_int(ctx.system, UnSet);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e3);
test_int(ctx.e[1], e2);
test_int(ctx.e[2], e1);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.s[0][0], 0);
test_int(ctx.c[0][1], ecs_id(Velocity));
test_int(ctx.s[0][1], 0);
}
void Observer_unset_on_fini_3(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_COMPONENT(world, Mass);
ECS_OBSERVER(world, UnSet, EcsUnSet, Position, Velocity, Mass);
Probe ctx = { 0 };
ecs_set_ctx(world, &ctx, NULL);
ecs_entity_t e1 = ecs_new(world, Position);
ecs_add(world, e1, Velocity);
ecs_add(world, e1, Mass);
test_int(ctx.invoked, 0);
ecs_entity_t e2 = ecs_new(world, Position);
ecs_add(world, e2, Velocity);
ecs_add(world, e2, Mass);
test_int(ctx.invoked, 0);
ecs_entity_t e3 = ecs_new(world, Position);
ecs_add(world, e3, Velocity);
ecs_add(world, e3, Mass);
test_int(ctx.invoked, 0);
ecs_new(world, Position);
test_int(ctx.invoked, 0);
ecs_new(world, Velocity);
test_int(ctx.invoked, 0);
ecs_new(world, Mass);
test_int(ctx.invoked, 0);
ecs_fini(world);
test_int(ctx.count, 3);
test_int(ctx.system, UnSet);
test_int(ctx.term_count, 3);
test_null(ctx.param);
test_int(ctx.e[0], e3);
test_int(ctx.e[1], e2);
test_int(ctx.e[2], e1);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.s[0][0], 0);
test_int(ctx.c[0][1], ecs_id(Velocity));
test_int(ctx.s[0][1], 0);
test_int(ctx.c[0][2], ecs_id(Mass));
test_int(ctx.s[0][2], 0);
}
void Observer_overlapping_unset_systems(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_COMPONENT(world, Mass);
ECS_OBSERVER(world, UnSetA, EcsUnSet, Position);
ECS_OBSERVER(world, UnSetB, EcsUnSet, Position, Velocity);
ECS_OBSERVER(world, UnSet, EcsUnSet, Position, Velocity);
Probe ctx = { 0 };
ecs_set_ctx(world, &ctx, NULL);
ecs_entity_t e = ecs_new(world, Position);
ecs_add(world, e, Velocity);
test_int(ctx.invoked, 0);
ecs_remove(world, e, Velocity);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, UnSet);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.s[0][0], 0);
test_int(ctx.c[0][1], ecs_id(Velocity));
test_int(ctx.s[0][1], 0);
ecs_fini(world);
}
static
void UnSet_TestComp(ecs_iter_t *it) {
if (!ecs_get_ctx(it->world)) {
return;
}
probe_iter(it);
test_int(it->count, 1);
Position *p = ecs_field(it, Position, 1);
test_assert(p != NULL);
test_int(p->x, 10);
test_int(p->y, 20);
}
void Observer_unset_move_to_nonempty_table(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_OBSERVER(world, UnSet_TestComp, EcsUnSet, Position);
Probe ctx = { 0 };
ecs_set_ctx(world, &ctx, NULL);
ECS_ENTITY(world, DummyA, Position, Velocity);
test_int(ctx.invoked, 0);
ECS_ENTITY(world, DummyB, Position, Velocity);
test_int(ctx.invoked, 0);
ECS_ENTITY(world, e, Position, Velocity);
ecs_set(world, e, Position, {10, 20});
ecs_set(world, e, Velocity, {20, 10});
test_int(ctx.invoked, 0);
ecs_entity_t e2 = ecs_new(world, Velocity);
ecs_set(world, e2, Velocity, {30, 40});
test_int(ctx.invoked, 0);
ecs_remove(world, e, Position);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, UnSet_TestComp);
test_int(ctx.term_count, 1);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.s[0][0], 0);
/* Prevent system from getting called by fini */
ecs_set_ctx(world, NULL, NULL);
ecs_fini(world);
}
static
void UnSet_WriteComp(ecs_iter_t *it) {
if (!ecs_get_ctx(it->world)) {
return;
}
probe_iter(it);
test_int(it->count, 1);
Position *p = ecs_field(it, Position, 1);
test_assert(p != NULL);
Velocity *v = ecs_field(it, Velocity, 2);
test_int(p->x, 10);
test_int(p->y, 20);
test_int(v->x, 1);
test_int(v->y, 2);
v->x = 2;
v->y = 3;
}
void Observer_write_in_unset(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_OBSERVER(world, UnSet_WriteComp, EcsUnSet, Position, Velocity);
ecs_entity_t e = ecs_set(world, 0, Position, {10, 20});
ecs_set(world, e, Velocity, {1, 2});
Probe ctx = { 0 };
ecs_set_ctx(world, &ctx, NULL);
ecs_remove(world, e, Position);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, UnSet_WriteComp);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.s[0][0], 0);
test_int(ctx.c[0][1], ecs_id(Velocity));
test_int(ctx.s[0][1], 0);
/* Prevent system from getting called by fini */
ecs_set_ctx(world, NULL, NULL);
const Velocity *v = ecs_get(world, e, Velocity);
test_int(v->x, 2);
test_int(v->y, 3);
ecs_fini(world);
}
void Observer_filter_term(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
Probe ctx = {0};
ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ .id = ecs_id(Position), .inout = EcsInOutNone }},
.events = {EcsOnSet},
.callback = Observer_w_filter_term,
.ctx = &ctx
});
/* Create entities before trigger */
ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20});
test_assert(e1 != 0);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, t);
test_int(ctx.event, EcsOnSet);
test_int(ctx.event_id, ecs_id(Position));
test_int(ctx.term_count, 1);
test_null(ctx.param);
test_int(ctx.e[0], e1);
test_int(ctx.c[0][0], ecs_id(Position));
ecs_fini(world);
}
void Observer_2_terms_1_filter(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
Probe ctx = {0};
ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {
{ .id = ecs_id(Position), .inout = EcsInOutNone },
{ .id = ecs_id(Velocity) }
},
.events = {EcsOnSet},
.callback = Observer_w_1_filter_term,
.ctx = &ctx
});
/* Create entities before trigger */
ecs_entity_t e1 = ecs_set(world, 0, Velocity, {1, 2});
test_assert(e1 != 0);
test_int(ctx.invoked, 0);
ecs_set(world, e1, Position, {10, 20});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, t);
test_int(ctx.event, EcsOnSet);
test_int(ctx.event_id, ecs_id(Position));
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e1);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.c[0][1], ecs_id(Velocity));
ecs_fini(world);
}
void Observer_3_terms_2_filter(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_COMPONENT(world, Mass);
Probe ctx = {0};
ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {
{ .id = ecs_id(Position), .inout = EcsInOutNone },
{ .id = ecs_id(Velocity), .inout = EcsInOutNone },
{ .id = ecs_id(Mass) }
},
.events = {EcsOnSet},
.callback = Observer_w_2_filter_terms,
.ctx = &ctx
});
/* Create entities before trigger */
ecs_entity_t e1 = ecs_set(world, 0, Velocity, {1, 2});
test_assert(e1 != 0);
test_int(ctx.invoked, 0);
ecs_set(world, e1, Mass, {100});
ecs_set(world, e1, Position, {10, 20});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, t);
test_int(ctx.event, EcsOnSet);
test_int(ctx.event_id, ecs_id(Position));
test_int(ctx.term_count, 3);
test_null(ctx.param);
test_int(ctx.e[0], e1);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.c[0][1], ecs_id(Velocity));
test_int(ctx.c[0][2], ecs_id(Mass));
ecs_fini(world);
}
void Observer_and_from(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
ECS_PREFAB(world, Type, TagA, TagB);
Probe ctx = {0};
ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {
{ ECS_AND | Type}
},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
ecs_entity_t e = ecs_new(world, 0);
test_int(ctx.invoked, 0);
ecs_add(world, e, TagA);
test_int(ctx.invoked, 0);
ecs_add(world, e, TagB);
test_int(ctx.invoked, 1);
ecs_remove(world, e, TagA);
test_int(ctx.invoked, 1);
ecs_add(world, e, TagA);
test_int(ctx.invoked, 2);
ecs_fini(world);
}
void Observer_or_from(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
ECS_PREFAB(world, Type, TagA, TagB);
Probe ctx = {0};
ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {
{ Type, .oper = EcsOrFrom }
},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
ecs_entity_t e = ecs_new(world, 0);
test_int(ctx.invoked, 0);
ecs_add(world, e, TagA);
test_int(ctx.invoked, 1);
ecs_add(world, e, TagB);
test_int(ctx.invoked, 2);
ecs_remove(world, e, TagA);
test_int(ctx.invoked, 2);
ecs_add(world, e, TagA);
test_int(ctx.invoked, 3);
ecs_fini(world);
}
static int invoke_count = 0;
static ecs_entity_t base_ent = 0;
static ecs_entity_t inst_ent_a = 0;
static ecs_entity_t inst_ent_b = 0;
static void TriggerTwice(ecs_iter_t *it) {
probe_system_w_ctx(it, it->ctx);
test_assert(base_ent != 0);
test_assert(inst_ent_a != 0);
test_assert(inst_ent_b != 0);
test_int(it->count, 1);
if (invoke_count == 0) {
test_assert(it->entities[0] == base_ent);
invoke_count ++;
} else if (invoke_count == 1) {
test_assert(it->entities[0] == inst_ent_b);
invoke_count ++;
} else {
test_int(invoke_count, 2);
test_assert(it->entities[0] == inst_ent_a);
invoke_count ++;
}
}
void Observer_notify_propagated_twice(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ TagA }},
.events = {EcsOnAdd},
.callback = TriggerTwice,
.ctx = &ctx
});
test_assert(t1 != 0);
base_ent = ecs_new_id(world);
inst_ent_a = ecs_new_w_pair(world, EcsIsA, base_ent);
ecs_add(world, inst_ent_a, TagB);
inst_ent_b = ecs_new_w_pair(world, EcsIsA, base_ent);
test_int(ctx.invoked, 0);
ecs_add(world, base_ent, TagA);
test_int(ctx.invoked, 3);
test_int(invoke_count, 3);
ecs_fini(world);
}
void Observer_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(world, Tag);
ecs_entity_t e2 = ecs_new(world, Tag);
ecs_entity_t e3 = ecs_new(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){
.filter.terms = {{ Tag }},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx,
.yield_existing = true
});
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);
// Ensure normal triggering also still works
ecs_os_zeromem(&ctx);
ecs_new(world, Tag);
test_int(ctx.invoked, 1);
ecs_fini(world);
}
void Observer_on_add_yield_existing_2_tables(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
/* Create entities before trigger */
ecs_entity_t e1 = ecs_new(world, TagA);
ecs_entity_t e2 = ecs_new(world, TagA);
ecs_entity_t e3 = ecs_new(world, TagA);
test_assert(e1 != 0);
test_assert(e2 != 0);
test_assert(e3 != 0);
ecs_add(world, e3, TagB);
Probe ctx = {0};
ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ TagA }},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx,
.yield_existing = true
});
test_int(ctx.invoked, 2);
test_int(ctx.count, 3);
test_int(ctx.system, t);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.event_id, TagA);
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], TagA);
// Ensure normal triggering also still works
ecs_os_zeromem(&ctx);
ecs_new(world, TagA);
test_int(ctx.invoked, 1);
ecs_fini(world);
}
void Observer_on_add_yield_existing_2_terms(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
ECS_TAG(world, TagC);
/* Create entities before trigger */
ecs_entity_t e1 = ecs_new(world, TagA);
ecs_entity_t e2 = ecs_new(world, TagA);
ecs_entity_t e3 = ecs_new(world, TagA);
ecs_entity_t e4 = ecs_new(world, TagA); /* no match */
ecs_entity_t e5 = ecs_new(world, TagB); /* no match */
test_assert(e1 != 0);
test_assert(e2 != 0);
test_assert(e3 != 0);
test_assert(e4 != 0);
test_assert(e5 != 0);
ecs_add(world, e1, TagB);
ecs_add(world, e2, TagB);
ecs_add(world, e3, TagB);
ecs_add(world, e3, TagC);
Probe ctx = {0};
ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ TagA }, { TagB }},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx,
.yield_existing = true
});
test_int(ctx.invoked, 2);
test_int(ctx.count, 3);
test_int(ctx.system, t);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 2);
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], TagA);
test_int(ctx.c[0][1], TagB);
test_int(ctx.c[1][0], TagA);
test_int(ctx.c[1][1], TagB);
// Ensure normal triggering also still works
ecs_os_zeromem(&ctx);
ecs_entity_t e6 = ecs_new(world, TagA);
test_int(ctx.invoked, 0);
ecs_add(world, e6, TagB);
test_int(ctx.invoked, 1);
ecs_fini(world);
}
void Observer_on_add_yield_existing_wildcard(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, Rel);
ECS_TAG(world, TgtA);
ECS_TAG(world, TgtB);
ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA);
ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB);
Probe ctx = {0};
ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ ecs_pair(Rel, EcsWildcard ) }},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx,
.yield_existing = true
});
test_int(ctx.invoked, 2);
test_int(ctx.count, 2);
test_int(ctx.system, t);
test_int(ctx.event, EcsOnAdd);
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.c[0][0], ecs_pair(Rel, TgtA));
test_int(ctx.c[1][0], ecs_pair(Rel, TgtB));
ecs_fini(world);
}
void Observer_on_add_yield_existing_wildcard_multi(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, Rel);
ECS_TAG(world, TgtA);
ECS_TAG(world, TgtB);
ECS_TAG(world, Tag);
ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA);
ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB);
ecs_new_w_pair(world, Rel, TgtB);
ecs_add(world, e1, Tag);
ecs_add(world, e2, Tag);
Probe ctx = {0};
ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ ecs_pair(Rel, EcsWildcard ) }, { Tag }},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx,
.yield_existing = true
});
test_int(ctx.invoked, 2);
test_int(ctx.count, 2);
test_int(ctx.system, t);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e1);
test_int(ctx.e[1], e2);
test_int(ctx.c[0][0], ecs_pair(Rel, TgtA));
test_int(ctx.c[0][1], Tag);
test_int(ctx.c[1][0], ecs_pair(Rel, TgtB));
test_int(ctx.c[1][1], Tag);
ecs_fini(world);
}
void Observer_on_add_yield_existing_wildcard_multi_w_wildcard_pivot(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, Rel);
ECS_TAG(world, TgtA);
ECS_TAG(world, TgtB);
ECS_TAG(world, Tag);
ecs_entity_t e1 = ecs_new_w_pair(world, Rel, TgtA);
ecs_entity_t e2 = ecs_new_w_pair(world, Rel, TgtB);
ecs_add(world, e1, Tag);
ecs_add(world, e2, Tag);
ecs_new(world, Tag);
Probe ctx = {0};
ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ ecs_pair(Rel, EcsWildcard ) }, { Tag }},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx,
.yield_existing = true
});
test_int(ctx.invoked, 2);
test_int(ctx.count, 2);
test_int(ctx.system, t);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 2);
test_null(ctx.param);
test_int(ctx.e[0], e1);
test_int(ctx.e[1], e2);
test_int(ctx.c[0][0], ecs_pair(Rel, TgtA));
test_int(ctx.c[0][1], Tag);
test_int(ctx.c[1][0], ecs_pair(Rel, TgtB));
test_int(ctx.c[1][1], Tag);
ecs_fini(world);
}
void Observer_observer_superset_wildcard(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, Rel);
ECS_TAG(world, ObjA);
ECS_TAG(world, ObjB);
ecs_entity_t base = ecs_new_id(world);
ecs_entity_t inst = ecs_new_id(world);
ecs_add_pair(world, inst, EcsIsA, base);
Probe ctx = {0};
ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ .id = ecs_pair(Rel, EcsWildcard), .src.flags = EcsUp }},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
test_assert(t != 0);
test_int(ctx.invoked, 0);
ecs_add_pair(world, base, Rel, ObjA);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, t);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.event_id, ecs_pair(Rel, ObjA));
test_int(ctx.term_count, 1);
test_null(ctx.param);
test_int(ctx.e[0], inst);
test_int(ctx.s[0][0], base);
ecs_os_zeromem(&ctx);
ecs_add_pair(world, base, Rel, ObjB);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, t);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.event_id, ecs_pair(Rel, ObjB));
test_int(ctx.term_count, 1);
test_null(ctx.param);
test_int(ctx.e[0], inst);
test_int(ctx.s[0][0], base);
ecs_fini(world);
}
void Observer_observer_superset_wildcard_add_isa(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, Rel);
ECS_TAG(world, ObjA);
ECS_TAG(world, ObjB);
ecs_entity_t base = ecs_new_id(world);
ecs_add_pair(world, base, Rel, ObjA);
ecs_add_pair(world, base, Rel, ObjB);
ecs_entity_t inst = ecs_new_id(world);
Probe ctx = {0};
ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ .id = ecs_pair(Rel, EcsWildcard), .src.flags = EcsUp }},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
test_assert(t != 0);
test_int(ctx.invoked, 0);
ecs_add_pair(world, inst, EcsIsA, base);
test_int(ctx.invoked, 2);
test_int(ctx.count, 2);
test_int(ctx.system, t);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 1);
test_null(ctx.param);
test_int(ctx.e[0], inst);
test_int(ctx.s[0][0], base);
ecs_fini(world);
}
void Observer_observer_superset_wildcard_add_isa_at_offset(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, Tag);
ECS_TAG(world, Rel);
ECS_TAG(world, ObjA);
ECS_TAG(world, ObjB);
ecs_entity_t base = ecs_new_id(world);
ecs_add(world, base, Tag);
ecs_add_pair(world, base, Rel, ObjA);
ecs_add_pair(world, base, Rel, ObjB);
ecs_entity_t inst = ecs_new_id(world);
Probe ctx = {0};
ecs_entity_t t = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ .id = ecs_pair(Rel, EcsWildcard), .src.flags = EcsUp }},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
test_assert(t != 0);
test_int(ctx.invoked, 0);
ecs_add_pair(world, inst, EcsIsA, base);
test_int(ctx.invoked, 2);
test_int(ctx.count, 2);
test_int(ctx.system, t);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.term_count, 1);
test_null(ctx.param);
test_int(ctx.e[0], inst);
test_int(ctx.s[0][0], base);
ecs_fini(world);
}
void Observer_on_set_w_tag(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
Probe ctx = {0};
ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ TagA }},
.events = {EcsOnSet},
.callback = Observer,
.ctx = &ctx
});
test_assert(t1 != 0);
ecs_entity_t e = ecs_new_id(world);
test_assert(e != 0);
ecs_add(world, e, TagA);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.event_id, TagA);
ecs_fini(world);
}
void Observer_mixed_on_set_w_tag(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_COMPONENT(world, Position);
Probe ctx = {0};
ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ TagA }, { ecs_id(Position) }},
.events = {EcsOnSet},
.callback = Observer,
.ctx = &ctx
});
test_assert(t1 != 0);
ecs_entity_t e1 = ecs_new_id(world);
test_assert(e1 != 0);
ecs_add(world, e1, TagA);
test_int(ctx.invoked, 0);
ecs_add(world, e1, Position);
test_int(ctx.invoked, 0);
ecs_set(world, e1, Position, {10, 20});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e1);
test_int(ctx.event, EcsOnSet);
test_int(ctx.event_id, ecs_id(Position));
ctx = (Probe){0};
ecs_entity_t e2 = ecs_new_id(world);
test_assert(e2 != 0);
ecs_set(world, e2, Position, {10, 20});
test_int(ctx.invoked, 0);
ecs_add(world, e2, TagA);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e2);
test_int(ctx.event, EcsOnAdd);
test_int(ctx.event_id, TagA);
ecs_fini(world);
}
void Observer_mixed_un_set_w_tag(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_COMPONENT(world, Position);
Probe ctx = {0};
ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ TagA }, { ecs_id(Position) }},
.events = {EcsUnSet},
.callback = Observer,
.ctx = &ctx
});
test_assert(t1 != 0);
ecs_entity_t e1 = ecs_new_id(world);
ecs_add(world, e1, TagA);
ecs_set(world, e1, Position, {10, 20});
test_int(ctx.invoked, 0);
ecs_remove(world, e1, TagA);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e1);
test_int(ctx.event, EcsOnRemove);
test_int(ctx.event_id, TagA);
ctx = (Probe){0};
ecs_entity_t e2 = ecs_new_id(world);
ecs_set(world, e2, Position, {10, 20});
ecs_add(world, e2, TagA);
test_int(ctx.invoked, 0);
ecs_remove(world, e2, Position);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e2);
test_int(ctx.event, EcsUnSet);
test_int(ctx.event_id, ecs_id(Position));
ecs_fini(world);
}
void Observer_match_base_w_id_at_offset(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
Probe ctx_1 = {0};
ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ ecs_id(Position) }},
.events = {EcsOnSet},
.callback = Observer,
.ctx = &ctx_1
});
test_assert(t1 != 0);
Probe ctx_2 = {0};
ecs_entity_t t2 = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ ecs_id(Velocity) }},
.events = {EcsOnSet},
.callback = Observer,
.ctx = &ctx_2
});
test_assert(t2 != 0);
ecs_entity_t base = ecs_new_w_id(world, EcsPrefab);
ecs_set(world, base, Position, {10, 20});
ecs_set(world, base, Velocity, {1, 2});
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 0);
ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base);
test_int(ctx_1.invoked, 1);
test_int(ctx_1.count, 1);
test_int(ctx_1.e[0], inst);
test_int(ctx_1.event, EcsOnSet);
test_int(ctx_1.event_id, ecs_id(Position));
test_int(ctx_2.invoked, 1);
test_int(ctx_2.count, 1);
test_int(ctx_2.e[0], inst);
test_int(ctx_2.event, EcsOnSet);
test_int(ctx_2.event_id, ecs_id(Velocity));
ecs_fini(world);
}
static int run_invoked = 0;
static int run_invoked_matched = 0;
static void Run(ecs_iter_t *it) {
if (ecs_observer_default_run_action(it)) {
run_invoked_matched ++;
}
run_invoked ++;
}
static void Run_w_iter_next(ecs_iter_t *it) {
run_invoked ++;
test_assert(it != NULL);
test_assert(it->next != NULL);
test_assert(it->callback != NULL);
while (ecs_iter_next(it)) {
it->callback(it);
}
}
void Observer_custom_run_action(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
Probe ctx = {0};
ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ TagA }},
.events = {EcsOnAdd},
.run = Run,
.callback = Observer,
.ctx = &ctx
});
test_assert(t1 != 0);
ecs_entity_t e = ecs_new(world, TagA);
test_int(run_invoked, 1);
test_int(run_invoked_matched, 1);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e);
test_int(ctx.event, EcsOnAdd);
ecs_fini(world);
}
void Observer_custom_run_action_w_iter_next(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
Probe ctx = {0};
ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ TagA }},
.events = {EcsOnAdd},
.run = Run_w_iter_next,
.callback = Observer,
.ctx = &ctx
});
test_assert(t1 != 0);
ecs_entity_t e = ecs_new(world, TagA);
test_int(run_invoked, 1);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e);
test_int(ctx.event, EcsOnAdd);
ecs_fini(world);
}
void Observer_custom_run_action_2_terms(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ TagA }, { TagB }},
.events = {EcsOnAdd},
.run = Run,
.callback = Observer,
.ctx = &ctx
});
test_assert(t1 != 0);
ecs_entity_t e = ecs_new(world, TagA);
test_int(run_invoked, 1);
run_invoked = 0;
ecs_add(world, e, TagB);
test_int(run_invoked, 1);
test_int(run_invoked_matched, 1);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e);
test_int(ctx.event, EcsOnAdd);
ecs_fini(world);
}
void Observer_custom_run_action_w_iter_next_2_terms(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t t1 = ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {{ TagA }, { TagB }},
.events = {EcsOnAdd},
.run = Run_w_iter_next,
.callback = Observer,
.ctx = &ctx
});
test_assert(t1 != 0);
ecs_entity_t e = ecs_new(world, TagA);
test_int(run_invoked, 1);
run_invoked = 0;
ecs_add(world, e, TagB);
test_int(run_invoked, 1);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e);
test_int(ctx.event, EcsOnAdd);
ecs_fini(world);
}
void Observer_read_in_on_remove_after_add_other_w_not(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_TAG(world, Tag);
ECS_TAG(world, O);
Probe ctx = {0};
ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {
{ .id = ecs_pair(ecs_id(Position), O)},
{ .id = Tag, .oper = EcsNot }
},
.events = {EcsOnRemove},
.callback = Observer_w_1_value,
.ctx = &ctx
});
ecs_entity_t e = ecs_set_pair(world, 0, Position, O, { 10, 20 });
test_int(ctx.invoked, 0);
ecs_add(world, e, Tag);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e);
test_int(ctx.event, EcsOnRemove);
ecs_fini(world);
}
void Observer_observer_w_short_notation(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, Foo);
Probe ctx = {0};
ecs_entity_t o = ecs_observer(world, {
.filter.terms = {{
.id = Foo
}},
.events = { EcsOnAdd },
.callback = Observer,
.ctx = &ctx
});
test_assert(o != 0);
ecs_entity_t e = ecs_new(world, Foo);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e);
test_int(ctx.event, EcsOnAdd);
ecs_fini(world);
}
void Observer_observer_w_filter_term(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {{ .id = TagA }, { .id = TagB, .src.flags = EcsFilter }},
.events = { EcsOnAdd },
.callback = Observer,
.ctx = &ctx
});
ecs_entity_t e = ecs_new_id(world);
test_int(ctx.invoked, 0);
ecs_add(world, e, TagB);
test_int(ctx.invoked, 0);
ecs_add(world, e, TagA);
test_int(ctx.invoked, 1);
ecs_remove(world, e, TagB);
ecs_add(world, e, TagB);
test_int(ctx.invoked, 1);
ecs_clear(world, e);
test_int(ctx.invoked, 1);
ecs_add(world, e, TagA);
test_int(ctx.invoked, 1);
ecs_fini(world);
}
static int free_ctx_invoked = 0;
static
void free_ctx(void *ctx) {
free_ctx_invoked ++;
ecs_os_free(ctx);
}
static int observer_w_ctx_invoked = 0;
static void Observer_w_ctx(ecs_iter_t *it) {
test_assert(it->ctx != NULL);
test_int(*(int*)it->ctx, 10);
observer_w_ctx_invoked ++;
}
void Observer_multi_observer_w_ctx_free(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
int *ctx = ecs_os_malloc_t(int);
*ctx = 10;
ecs_entity_t o = ecs_observer(world, {
.filter.terms = {{ .id = TagA }, { .id = TagB }},
.events = { EcsOnAdd },
.callback = Observer_w_ctx,
.ctx = ctx,
.ctx_free = free_ctx
});
test_int(observer_w_ctx_invoked, 0);
test_int(free_ctx_invoked, 0);
ecs_entity_t e = ecs_new(world, TagA);
test_int(observer_w_ctx_invoked, 0);
test_int(free_ctx_invoked, 0);
ecs_add(world, e, TagB);
test_int(observer_w_ctx_invoked, 1);
test_int(free_ctx_invoked, 0);
ecs_delete(world, o);
test_int(free_ctx_invoked, 1);
ecs_fini(world);
test_int(free_ctx_invoked, 1);
}
void Observer_propagate_after_on_delete_clear_action(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, Rel);
ecs_entity_t tgt = ecs_new_id(world);
ecs_entity_t parent = ecs_new_id(world);
ecs_add_pair(world, parent, Rel, tgt);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {{
.id = ecs_pair(Rel, EcsWildcard),
.src.flags = EcsUp,
.src.trav = EcsChildOf
}},
.events = { EcsOnRemove },
.callback = Observer,
.ctx = &ctx
});
ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent);
test_int(ctx.invoked, 0);
ecs_delete(world, tgt);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], child);
test_int(ctx.s[0][0], parent);
test_int(ctx.event, EcsOnRemove);
ecs_fini(world);
}
void Observer_on_add_after_batch_w_exclusive_adds(void) {
ecs_world_t *world = ecs_mini();
ECS_ENTITY(world, Rel, Exclusive);
ECS_TAG(world, TgtA);
ECS_TAG(world, TgtB);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {{
ecs_pair(Rel, EcsWildcard),
.src.flags = EcsSelf
}},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
ecs_entity_t e = ecs_new_id(world);
ecs_defer_begin(world);
ecs_add_pair(world, e, Rel, TgtA);
ecs_add_pair(world, e, Rel, TgtB);
ecs_defer_end(world);
test_assert(!ecs_has_pair(world, e, Rel, TgtA));
test_assert(ecs_has_pair(world, e, Rel, TgtB));
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e);
test_int(ctx.s[0][0], 0);
test_int(ctx.c[0][0], ecs_pair(Rel, TgtB));
test_int(ctx.event, EcsOnAdd);
ecs_fini(world);
}
void Observer_propagate_match_relationship_w_self_up(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {{ TagB, .src.flags = EcsSelf|EcsUp }},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
ecs_entity_t parent = ecs_new_id(world);
ecs_add(world, parent, TagA);
ecs_new_w_pair(world, EcsChildOf, parent);
test_int(ctx.invoked, 0);
ecs_add(world, parent, TagB);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], parent);
test_int(ctx.s[0][0], 0);
test_int(ctx.c[0][0], TagB);
test_int(ctx.event, EcsOnAdd);
ecs_fini(world);
}
void Observer_propagate_match_relationship_w_up(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {{ TagB, .src.flags = EcsUp }},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
ecs_entity_t parent = ecs_new_id(world);
ecs_add(world, parent, TagA);
ecs_new_w_pair(world, EcsChildOf, parent);
test_int(ctx.invoked, 0);
ecs_add(world, parent, TagB);
test_int(ctx.invoked, 0);
ecs_fini(world);
}
void Observer_observer_w_2_fixed_src(void) {
ecs_world_t *world = ecs_init();
ecs_entity_t Foo = ecs_new_id(world);
ecs_entity_t Bar = ecs_new_id(world);
ecs_entity_t e1 = ecs_new_id(world);
ecs_entity_t e2 = ecs_new_id(world);
Probe ctx = {0};
ecs_observer_init(world, &(ecs_observer_desc_t){
.filter.terms = {
// Comment out either of these and the callback succeeds.
{ Foo , .src.id = e1 },
{ Bar, .src.id = e2 },
},
.events = { EcsOnAdd },
.callback = Observer,
.ctx = &ctx
});
ecs_add_id(world, e1, Foo);
test_int(ctx.invoked, 0);
ecs_add_id(world, e2, Bar);
test_int(ctx.invoked, 1);
ecs_fini(world);
}
void Observer_emit_for_recreated_id_after_remove_all(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, Rel);
ECS_TAG(world, Tgt);
ecs_entity_t e1 = ecs_new(world, TagA);
ecs_add_pair(world, e1, Rel, Tgt);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {
{ TagA },
{ ecs_pair(Rel, Tgt) }
},
.events = { EcsOnAdd },
.callback = Observer,
.ctx = &ctx
});
ecs_remove_all(world, ecs_pair(Rel, Tgt));
test_assert(ecs_id_is_valid(world, ecs_pair(Rel, Tgt)));
test_assert(ecs_is_alive(world, e1));
test_int(0, ctx.invoked);
ecs_entity_t e2 = ecs_new(world, TagA);
ecs_add_pair(world, e2, Rel, Tgt);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e2);
test_int(ctx.s[0][0], 0);
test_int(ctx.c[0][0], TagA);
test_int(ctx.c[0][1], ecs_pair(Rel, Tgt));
test_int(ctx.event, EcsOnAdd);
ecs_fini(world);
}
void Observer_emit_for_recreated_id_after_remove_all_wildcard(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, Rel);
ECS_TAG(world, Tgt);
ecs_entity_t e1 = ecs_new(world, TagA);
ecs_add_pair(world, e1, Rel, Tgt);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {
{ TagA },
{ ecs_pair(Rel, Tgt) }
},
.events = { EcsOnAdd },
.callback = Observer,
.ctx = &ctx
});
ecs_remove_all(world, ecs_pair(Rel, EcsWildcard));
test_assert(ecs_id_is_valid(world, ecs_pair(Rel, Tgt)));
test_assert(ecs_is_alive(world, e1));
test_int(0, ctx.invoked);
ecs_entity_t e2 = ecs_new(world, TagA);
ecs_add_pair(world, e2, Rel, Tgt);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e2);
test_int(ctx.s[0][0], 0);
test_int(ctx.c[0][0], TagA);
test_int(ctx.c[0][1], ecs_pair(Rel, Tgt));
test_int(ctx.event, EcsOnAdd);
ecs_fini(world);
}
void Observer_emit_for_recreated_id_after_delete_with(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, Rel);
ECS_TAG(world, Tgt);
ecs_entity_t e1 = ecs_new(world, TagA);
ecs_add_pair(world, e1, Rel, Tgt);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {
{ TagA },
{ ecs_pair(Rel, Tgt) }
},
.events = { EcsOnAdd },
.callback = Observer,
.ctx = &ctx
});
ecs_delete_with(world, ecs_pair(Rel, Tgt));
test_assert(ecs_id_is_valid(world, ecs_pair(Rel, Tgt)));
test_assert(!ecs_is_alive(world, e1));
test_int(0, ctx.invoked);
ecs_entity_t e2 = ecs_new(world, TagA);
ecs_add_pair(world, e2, Rel, Tgt);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e2);
test_int(ctx.s[0][0], 0);
test_int(ctx.c[0][0], TagA);
test_int(ctx.c[0][1], ecs_pair(Rel, Tgt));
test_int(ctx.event, EcsOnAdd);
ecs_fini(world);
}
void Observer_emit_for_recreated_id_after_delete_with_wildcard(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, Rel);
ECS_TAG(world, Tgt);
ecs_entity_t e1 = ecs_new(world, TagA);
ecs_add_pair(world, e1, Rel, Tgt);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {
{ TagA },
{ ecs_pair(Rel, Tgt) }
},
.events = { EcsOnAdd },
.callback = Observer,
.ctx = &ctx
});
ecs_delete_with(world, ecs_pair(Rel, EcsWildcard));
test_assert(ecs_id_is_valid(world, ecs_pair(Rel, Tgt)));
test_assert(!ecs_is_alive(world, e1));
test_int(0, ctx.invoked);
ecs_entity_t e2 = ecs_new(world, TagA);
ecs_add_pair(world, e2, Rel, Tgt);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e2);
test_int(ctx.s[0][0], 0);
test_int(ctx.c[0][0], TagA);
test_int(ctx.c[0][1], ecs_pair(Rel, Tgt));
test_int(ctx.event, EcsOnAdd);
ecs_fini(world);
}
void Observer_delete_observed_id(void) {
install_test_abort();
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, Rel);
ECS_TAG(world, Tgt);
ecs_entity_t e1 = ecs_new(world, TagA);
ecs_add_pair(world, e1, Rel, Tgt);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {
{ TagA },
{ ecs_pair(Rel, Tgt) }
},
.events = { EcsOnAdd },
.callback = Observer,
.ctx = &ctx
});
test_expect_abort();
ecs_delete(world, TagA);
}
void Observer_delete_observed_rel(void) {
install_test_abort();
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, Rel);
ECS_TAG(world, Tgt);
ecs_entity_t e1 = ecs_new(world, TagA);
ecs_add_pair(world, e1, Rel, Tgt);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {
{ TagA },
{ ecs_pair(Rel, Tgt) }
},
.events = { EcsOnAdd },
.callback = Observer,
.ctx = &ctx
});
test_expect_abort();
ecs_delete(world, Rel);
}
void Observer_delete_observed_tgt(void) {
install_test_abort();
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, Rel);
ECS_TAG(world, Tgt);
ecs_entity_t e1 = ecs_new(world, TagA);
ecs_add_pair(world, e1, Rel, Tgt);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {
{ TagA },
{ ecs_pair(Rel, Tgt) }
},
.events = { EcsOnAdd },
.callback = Observer,
.ctx = &ctx
});
test_expect_abort();
ecs_delete(world, Tgt);
}
static int pair_x = 0;
static int32_t pair_column = 0;
static
void OnTagPair(ecs_iter_t *it) {
test_int(it->count, 1);
pair_column = it->columns[0];
probe_iter(it);
}
static
void OnPair(ecs_iter_t *it) {
test_int(it->count, 1);
Position *p = ecs_field(it, Position, 1);
pair_x = p->x;
pair_column = it->columns[0];
probe_iter(it);
}
void Observer_on_add_2_pairs_w_uni_observer(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, Rel);
ECS_TAG(world, TgtA);
ECS_TAG(world, TgtB);
ECS_TAG(world, TgtC);
ECS_TAG(world, Tag);
ECS_OBSERVER(world, OnTagPair, EcsOnAdd, (Rel, *));
Probe ctx = { 0 };
ecs_set_ctx(world, &ctx, NULL);
ecs_entity_t e = ecs_new_id(world);
ecs_add(world, e, Tag);
ecs_add_pair(world, e, Rel, TgtA);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, OnTagPair);
test_int(ctx.term_count, 1);
test_int(ctx.event_id, ecs_pair(Rel, TgtA));
test_int(pair_column, 2);
ecs_os_zeromem(&ctx);
ecs_add_pair(world, e, Rel, TgtB);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, OnTagPair);
test_int(ctx.term_count, 1);
test_int(ctx.event_id, ecs_pair(Rel, TgtB));
test_int(pair_column, 3);
ecs_os_zeromem(&ctx);
ecs_add_pair(world, e, Rel, TgtC);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, OnTagPair);
test_int(ctx.term_count, 1);
test_int(ctx.event_id, ecs_pair(Rel, TgtC));
test_int(pair_column, 4);
ecs_fini(world);
}
void Observer_on_add_2_pairs_w_multi_observer(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, Rel);
ECS_TAG(world, TgtA);
ECS_TAG(world, TgtB);
ECS_TAG(world, TgtC);
ECS_TAG(world, Tag);
ECS_OBSERVER(world, OnTagPair, EcsOnAdd, (Rel, *), Tag);
Probe ctx = { 0 };
ecs_set_ctx(world, &ctx, NULL);
ecs_entity_t e = ecs_new_id(world);
ecs_add(world, e, Tag);
ecs_add_pair(world, e, Rel, TgtA);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, OnTagPair);
test_int(ctx.term_count, 2);
test_int(ctx.event_id, ecs_pair(Rel, TgtA));
test_int(pair_column, 2);
ecs_os_zeromem(&ctx);
ecs_add_pair(world, e, Rel, TgtB);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, OnTagPair);
test_int(ctx.term_count, 2);
test_int(ctx.event_id, ecs_pair(Rel, TgtB));
test_int(pair_column, 3);
ecs_os_zeromem(&ctx);
ecs_add_pair(world, e, Rel, TgtC);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, OnTagPair);
test_int(ctx.term_count, 2);
test_int(ctx.event_id, ecs_pair(Rel, TgtC));
test_int(pair_column, 4);
ecs_fini(world);
}
void Observer_on_set_2_pairs_w_uni_observer(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_TAG(world, TgtA);
ECS_TAG(world, TgtB);
ECS_TAG(world, TgtC);
ECS_TAG(world, Tag);
ECS_OBSERVER(world, OnPair, EcsOnSet, (Position, *));
Probe ctx = { 0 };
ecs_set_ctx(world, &ctx, NULL);
ecs_entity_t e = ecs_new_id(world);
ecs_add(world, e, Tag);
ecs_set_pair(world, e, Position, TgtA, {1});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, OnPair);
test_int(ctx.term_count, 1);
test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtA));
test_int(pair_column, 2);
test_int(pair_x, 1);
ecs_os_zeromem(&ctx);
ecs_set_pair(world, e, Position, TgtB, {2});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, OnPair);
test_int(ctx.term_count, 1);
test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtB));
test_int(pair_column, 3);
test_int(pair_x, 2);
ecs_os_zeromem(&ctx);
ecs_set_pair(world, e, Position, TgtC, {3});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, OnPair);
test_int(ctx.term_count, 1);
test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtC));
test_int(pair_column, 4);
test_int(pair_x, 3);
ecs_fini(world);
}
void Observer_on_set_2_pairs_w_multi_observer(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_TAG(world, TgtA);
ECS_TAG(world, TgtB);
ECS_TAG(world, TgtC);
ECS_TAG(world, Tag);
ECS_OBSERVER(world, OnPair, EcsOnSet, (Position, *), Tag);
Probe ctx = { 0 };
ecs_set_ctx(world, &ctx, NULL);
ecs_entity_t e = ecs_new_id(world);
ecs_add(world, e, Tag);
ecs_set_pair(world, e, Position, TgtA, {1});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, OnPair);
test_int(ctx.term_count, 2);
test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtA));
test_int(pair_column, 2);
test_int(pair_x, 1);
ecs_os_zeromem(&ctx);
ecs_set_pair(world, e, Position, TgtB, {2});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, OnPair);
test_int(ctx.term_count, 2);
test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtB));
test_int(pair_column, 3);
test_int(pair_x, 2);
ecs_os_zeromem(&ctx);
ecs_set_pair(world, e, Position, TgtC, {3});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, OnPair);
test_int(ctx.term_count, 2);
test_int(ctx.event_id, ecs_pair(ecs_id(Position), TgtC));
test_int(pair_column, 4);
test_int(pair_x, 3);
ecs_fini(world);
}
void Observer_on_remove_target_from_base_at_offset(void) {
ecs_world_t *world = ecs_mini();
ecs_entity_t R = ecs_new_id(world);
ecs_entity_t T1 = ecs_new_id(world);
ecs_entity_t T2 = ecs_new_id(world);
ecs_entity_t C = ecs_new_id(world);
Probe ctx = { 0 };
ecs_entity_t o = ecs_observer(world, {
.filter.terms = {
{ .id = ecs_pair(R, EcsWildcard), .src.flags = EcsUp },
{ .id = C },
},
.events = { EcsOnRemove },
.callback = Observer,
.ctx = &ctx
});
ecs_entity_t base = ecs_new_id(world);
ecs_add_pair(world, base, R, T1);
ecs_add_pair(world, base, R, T2);
ecs_entity_t e = ecs_new_id(world);
ecs_add_pair(world, e, EcsIsA, base);
ecs_add_id(world, e, C);
test_int(ctx.invoked, 0);
ecs_delete(world, T2);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.term_count, 2);
test_int(ctx.event_id, ecs_pair(R, T2));
test_int(ctx.event, EcsOnRemove);
ecs_fini(world);
}
static void Observer_base_component(ecs_iter_t *it) {
probe_system_w_ctx(it, it->ctx);
Position *p = ecs_field(it, Position, 1);
test_assert(p != NULL);
test_int(p->x, 30);
test_int(p->y, 40);
}
void Observer_on_remove_target_component_from_base_at_offset(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ecs_entity_t T1 = ecs_new_id(world);
ecs_entity_t T2 = ecs_new_id(world);
ecs_entity_t C = ecs_new_id(world);
Probe ctx = { 0 };
ecs_entity_t o = ecs_observer(world, {
.filter.terms = {
{ .id = ecs_pair(ecs_id(Position), EcsWildcard), .src.flags = EcsUp },
{ .id = C },
},
.events = { EcsOnRemove },
.callback = Observer_base_component,
.ctx = &ctx
});
ecs_entity_t base = ecs_new_id(world);
ecs_set_pair(world, base, Position, T1, {10, 20});
ecs_set_pair(world, base, Position, T2, {30, 40});
ecs_entity_t e = ecs_new_id(world);
ecs_add_pair(world, e, EcsIsA, base);
ecs_add_id(world, e, C);
test_int(ctx.invoked, 0);
ecs_delete(world, T2);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.system, o);
test_int(ctx.term_count, 2);
test_int(ctx.e[0], e);
test_int(ctx.s[0][0], base);
test_int(ctx.s[0][1], 0);
test_int(ctx.event_id, ecs_pair(ecs_id(Position), T2));
test_int(ctx.event, EcsOnRemove);
ecs_delete(world, o);
ecs_fini(world);
}
static void Observer_w_other_table(ecs_iter_t *it) {
probe_system_w_ctx(it, it->ctx);
test_assert(it->table != NULL);
test_assert(it->other_table != NULL);
}
static void Observer_dummy(ecs_iter_t *it) {}
void Observer_wildcard_propagate_w_other_table(void) {
ecs_world_t *world = ecs_mini();
ecs_entity_t tag = ecs_new_id(world);
ecs_entity_t rel = ecs_new_id(world);
ecs_entity_t tgt_1 = ecs_new_id(world);
ecs_entity_t parent = ecs_new_w_id(world, tag);
ecs_new_w_pair(world, EcsChildOf, parent);
Probe ctx_parent = {0};
ecs_observer(world, {
.filter.terms = {{ ecs_pair(rel, EcsWildcard) }},
.events = {EcsOnAdd},
.callback = Observer_dummy
});
ecs_observer(world, {
.filter.terms = {{ ecs_pair(rel, EcsWildcard) }},
.events = {EcsWildcard},
.callback = Observer_w_other_table,
.ctx = &ctx_parent
});
ecs_observer(world, {
.filter.terms = {{ ecs_pair(rel, EcsWildcard), .src.flags = EcsUp, .src.trav = EcsChildOf }},
.events = {EcsWildcard},
.callback = Observer_dummy
});
ecs_add_pair(world, parent, rel, tgt_1);
test_int(ctx_parent.invoked, 1);
ecs_fini(world);
}
void Observer_cache_test_1(void) {
ecs_world_t *world = ecs_mini();
ecs_entity_t tag = ecs_new_id(world);
ecs_entity_t e1 = ecs_new_id(world);
ecs_entity_t e2 = ecs_new_id(world);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {{ tag, .src.flags = EcsUp, .src.trav = EcsChildOf }},
.events = {EcsOnAdd, EcsOnRemove},
.callback = Observer,
.ctx = &ctx
});
ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1));
ecs_add_id(world, e1, tag);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e2);
test_int(ctx.s[0][0], e1);
test_int(ctx.c[0][0], tag);
test_int(ctx.event, EcsOnAdd);
ecs_os_zeromem(&ctx);
ecs_entity_t e3 = ecs_new_id(world);
ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2));
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e3);
test_int(ctx.s[0][0], e1);
test_int(ctx.c[0][0], tag);
test_int(ctx.event, EcsOnAdd);
ecs_os_zeromem(&ctx);
ecs_delete(world, e3);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e3);
test_int(ctx.s[0][0], e1);
test_int(ctx.c[0][0], tag);
test_int(ctx.event, EcsOnRemove);
ecs_os_zeromem(&ctx);
ecs_remove_id(world, e1, tag);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e2);
test_int(ctx.s[0][0], e1);
test_int(ctx.c[0][0], tag);
test_int(ctx.event, EcsOnRemove);
ecs_os_zeromem(&ctx);
e3 = ecs_new_id(world);
ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2));
test_int(ctx.invoked, 0);
ecs_fini(world);
}
void Observer_cache_test_2(void) {
ecs_world_t *world = ecs_mini();
ecs_entity_t tag_1 = ecs_new_id(world);
ecs_entity_t tag_2 = ecs_new_id(world);
ecs_entity_t e1 = ecs_new_id(world);
ecs_entity_t e2 = ecs_new_id(world);
ecs_entity_t e3 = ecs_new_id(world);
Probe ctx_1 = {0};
Probe ctx_2 = {0};
ecs_observer(world, {
.filter.terms = {{ tag_1, .src.flags = EcsUp, .src.trav = EcsChildOf }},
.events = {EcsOnAdd, EcsOnRemove},
.callback = Observer,
.ctx = &ctx_1
});
ecs_observer(world, {
.filter.terms = {{ tag_2, .src.flags = EcsUp, .src.trav = EcsChildOf }},
.events = {EcsOnAdd, EcsOnRemove},
.callback = Observer,
.ctx = &ctx_2
});
ecs_add_id(world, e1, tag_1);
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 0);
ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1));
test_int(ctx_1.invoked, 1);
test_int(ctx_1.count, 1);
test_int(ctx_1.e[0], e2);
test_int(ctx_1.s[0][0], e1);
test_int(ctx_1.c[0][0], tag_1);
test_int(ctx_1.event, EcsOnAdd);
ecs_os_zeromem(&ctx_1);
test_int(ctx_2.invoked, 0);
ecs_add_id(world, e2, tag_2);
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 0);
ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2));
test_int(ctx_1.invoked, 1);
test_int(ctx_1.count, 1);
test_int(ctx_1.e[0], e3);
test_int(ctx_1.s[0][0], e1);
test_int(ctx_1.c[0][0], tag_1);
test_int(ctx_1.event, EcsOnAdd);
ecs_os_zeromem(&ctx_1);
test_int(ctx_2.invoked, 1);
test_int(ctx_2.count, 1);
test_int(ctx_2.e[0], e3);
test_int(ctx_2.s[0][0], e2);
test_int(ctx_2.c[0][0], tag_2);
test_int(ctx_2.event, EcsOnAdd);
ecs_os_zeromem(&ctx_2);
ecs_fini(world);
}
void Observer_cache_test_3(void) {
ecs_world_t *world = ecs_mini();
ecs_entity_t tag_1 = ecs_new_id(world);
ecs_entity_t tag_2 = ecs_new_id(world);
ecs_entity_t e1 = ecs_new_id(world);
ecs_entity_t e2 = ecs_new_id(world);
ecs_entity_t e3 = ecs_new_id(world);
Probe ctx_1 = {0};
Probe ctx_2 = {0};
ecs_observer(world, {
.filter.terms = {{ tag_1, .src.flags = EcsUp, .src.trav = EcsChildOf }},
.events = {EcsOnAdd, EcsOnRemove},
.callback = Observer,
.ctx = &ctx_1
});
ecs_observer(world, {
.filter.terms = {{ tag_2, .src.flags = EcsUp, .src.trav = EcsChildOf }},
.events = {EcsOnAdd, EcsOnRemove},
.callback = Observer,
.ctx = &ctx_2
});
ecs_add_id(world, e1, tag_1);
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 0);
ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1));
test_int(ctx_1.invoked, 1);
test_int(ctx_1.count, 1);
test_int(ctx_1.e[0], e2);
test_int(ctx_1.s[0][0], e1);
test_int(ctx_1.c[0][0], tag_1);
test_int(ctx_1.event, EcsOnAdd);
ecs_os_zeromem(&ctx_1);
test_int(ctx_2.invoked, 0);
ecs_add_id(world, e2, tag_1);
ecs_add_id(world, e2, tag_2);
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 0);
ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2));
test_int(ctx_1.invoked, 1);
test_int(ctx_1.count, 1);
test_int(ctx_1.e[0], e3);
test_int(ctx_1.s[0][0], e2);
test_int(ctx_1.c[0][0], tag_1);
test_int(ctx_1.event, EcsOnAdd);
ecs_os_zeromem(&ctx_1);
test_int(ctx_2.invoked, 1);
test_int(ctx_2.count, 1);
test_int(ctx_2.e[0], e3);
test_int(ctx_2.s[0][0], e2);
test_int(ctx_2.c[0][0], tag_2);
test_int(ctx_2.event, EcsOnAdd);
ecs_os_zeromem(&ctx_2);
ecs_fini(world);
}
void Observer_cache_test_4(void) {
ecs_world_t *world = ecs_mini();
ecs_entity_t tag_0 = ecs_new_id(world);
ecs_entity_t tag_1 = ecs_new_id(world);
ecs_entity_t tag_2 = ecs_new_id(world);
ecs_entity_t e0 = ecs_new_id(world);
ecs_entity_t e1 = ecs_new_id(world);
ecs_entity_t e2 = ecs_new_id(world);
ecs_entity_t e3 = ecs_new_id(world);
Probe *ctx_0 = ecs_os_calloc_t(Probe);
Probe *ctx_1 = ecs_os_calloc_t(Probe);
Probe *ctx_2 = ecs_os_calloc_t(Probe);
ecs_observer(world, {
.filter.terms = {{ tag_0, .src.flags = EcsUp, .src.trav = EcsChildOf }},
.events = {EcsOnAdd, EcsOnRemove},
.callback = Observer,
.ctx = ctx_0
});
ecs_observer(world, {
.filter.terms = {{ tag_1, .src.flags = EcsUp, .src.trav = EcsChildOf }},
.events = {EcsOnAdd, EcsOnRemove},
.callback = Observer,
.ctx = ctx_1
});
ecs_observer(world, {
.filter.terms = {{ tag_2, .src.flags = EcsUp, .src.trav = EcsChildOf }},
.events = {EcsOnAdd, EcsOnRemove},
.callback = Observer,
.ctx = ctx_2
});
ecs_add_id(world, e0, tag_0);
test_int(ctx_0->invoked, 0);
test_int(ctx_1->invoked, 0);
test_int(ctx_2->invoked, 0);
ecs_add_id(world, e1, ecs_pair(EcsChildOf, e0));
test_int(ctx_0->invoked, 1);
test_int(ctx_0->count, 1);
test_int(ctx_0->e[0], e1);
test_int(ctx_0->s[0][0], e0);
test_int(ctx_0->c[0][0], tag_0);
test_int(ctx_0->event, EcsOnAdd);
ecs_os_zeromem(ctx_0);
test_int(ctx_1->invoked, 0);
test_int(ctx_2->invoked, 0);
ecs_add_id(world, e1, tag_1);
test_int(ctx_0->invoked, 0);
test_int(ctx_1->invoked, 0);
test_int(ctx_2->invoked, 0);
ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1));
test_int(ctx_0->invoked, 1);
test_int(ctx_0->count, 1);
test_int(ctx_0->e[0], e2);
test_int(ctx_0->s[0][0], e0);
test_int(ctx_0->c[0][0], tag_0);
test_int(ctx_0->event, EcsOnAdd);
ecs_os_zeromem(ctx_0);
test_int(ctx_1->invoked, 1);
test_int(ctx_1->count, 1);
test_int(ctx_1->e[0], e2);
test_int(ctx_1->s[0][0], e1);
test_int(ctx_1->c[0][0], tag_1);
test_int(ctx_1->event, EcsOnAdd);
ecs_os_zeromem(ctx_1);
test_int(ctx_2->invoked, 0);
ecs_add_id(world, e2, tag_1);
ecs_add_id(world, e2, tag_2);
test_int(ctx_0->invoked, 0);
test_int(ctx_1->invoked, 0);
test_int(ctx_2->invoked, 0);
ecs_remove_id(world, e0, tag_0);
test_int(ctx_0->invoked, 2);
ecs_os_zeromem(ctx_0);
test_int(ctx_1->invoked, 0);
test_int(ctx_2->invoked, 0);
ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2));
test_int(ctx_0->invoked, 0);
test_int(ctx_1->invoked, 1);
test_int(ctx_1->count, 1);
test_int(ctx_1->e[0], e3);
test_int(ctx_1->s[0][0], e2);
test_int(ctx_1->c[0][0], tag_1);
test_int(ctx_1->event, EcsOnAdd);
ecs_os_zeromem(ctx_1);
test_int(ctx_2->invoked, 1);
test_int(ctx_2->count, 1);
test_int(ctx_2->e[0], e3);
test_int(ctx_2->s[0][0], e2);
test_int(ctx_2->c[0][0], tag_2);
test_int(ctx_2->event, EcsOnAdd);
ecs_os_zeromem(ctx_2);
ecs_fini(world);
ecs_os_free(ctx_0);
ecs_os_free(ctx_1);
ecs_os_free(ctx_2);
}
void Observer_cache_test_5(void) {
ecs_world_t *world = ecs_mini();
ecs_entity_t tag_0 = ecs_new_id(world);
ecs_entity_t tag_1 = ecs_new_id(world);
ecs_entity_t tag_2 = ecs_new_id(world);
ecs_entity_t e0 = ecs_new_id(world);
ecs_entity_t e1 = ecs_new_id(world);
ecs_entity_t e2 = ecs_new_id(world);
ecs_entity_t e3 = ecs_new_id(world);
ecs_entity_t e4 = ecs_new_id(world);
Probe ctx_1 = {0};
Probe ctx_2 = {0};
ecs_observer(world, {
.filter.terms = {{ tag_1, .src.flags = EcsUp, .src.trav = EcsChildOf }},
.events = {EcsOnAdd, EcsOnRemove},
.callback = Observer,
.ctx = &ctx_1
});
ecs_observer(world, {
.filter.terms = {{ tag_2, .src.flags = EcsUp, .src.trav = EcsChildOf }},
.events = {EcsOnAdd, EcsOnRemove},
.callback = Observer,
.ctx = &ctx_2
});
ecs_add_id(world, e0, tag_0);
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 0);
ecs_add_id(world, e1, ecs_pair(EcsChildOf, e0));
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 0);
ecs_add_id(world, e1, tag_1);
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 0);
ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1));
test_int(ctx_1.invoked, 1);
test_int(ctx_1.count, 1);
test_int(ctx_1.e[0], e2);
test_int(ctx_1.s[0][0], e1);
test_int(ctx_1.c[0][0], tag_1);
test_int(ctx_1.event, EcsOnAdd);
ecs_os_zeromem(&ctx_1);
test_int(ctx_2.invoked, 0);
ecs_add_id(world, e2, tag_1);
ecs_add_id(world, e2, tag_2);
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 0);
ecs_remove_id(world, e0, tag_0);
test_int(ctx_1.invoked, 0);
test_int(ctx_2.invoked, 0);
ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2));
test_int(ctx_1.invoked, 1);
test_int(ctx_1.count, 1);
test_int(ctx_1.e[0], e3);
test_int(ctx_1.s[0][0], e2);
test_int(ctx_1.c[0][0], tag_1);
test_int(ctx_1.event, EcsOnAdd);
ecs_os_zeromem(&ctx_1);
test_int(ctx_2.invoked, 1);
test_int(ctx_2.count, 1);
test_int(ctx_2.e[0], e3);
test_int(ctx_2.s[0][0], e2);
test_int(ctx_2.c[0][0], tag_2);
test_int(ctx_2.event, EcsOnAdd);
ecs_os_zeromem(&ctx_2);
ecs_add_id(world, e4, ecs_pair(EcsChildOf, e1));
test_int(ctx_1.invoked, 1);
test_int(ctx_1.count, 1);
test_int(ctx_1.e[0], e4);
test_int(ctx_1.s[0][0], e1);
test_int(ctx_1.c[0][0], tag_1);
test_int(ctx_1.event, EcsOnAdd);
ecs_os_zeromem(&ctx_1);
test_int(ctx_2.invoked, 0);
ecs_fini(world);
}
void Observer_cache_test_6(void) {
ecs_world_t *world = ecs_mini();
ecs_entity_t tag_1 = ecs_new_id(world);
ecs_entity_t e1 = ecs_new_id(world);
ecs_entity_t e2 = ecs_new_id(world);
ecs_entity_t e3 = ecs_new_id(world);
Probe ctx_1 = {0};
ecs_observer(world, {
.filter.terms = {{ tag_1, .src.flags = EcsUp, .src.trav = EcsChildOf }},
.events = {EcsOnAdd, EcsOnRemove},
.callback = Observer,
.ctx = &ctx_1
});
ecs_add_id(world, e1, tag_1);
test_int(ctx_1.invoked, 0);
ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1));
test_int(ctx_1.invoked, 1);
test_int(ctx_1.count, 1);
test_int(ctx_1.e[0], e2);
test_int(ctx_1.s[0][0], e1);
test_int(ctx_1.c[0][0], tag_1);
test_int(ctx_1.event, EcsOnAdd);
ecs_os_zeromem(&ctx_1);
ecs_remove_id(world, e1, tag_1);
test_int(ctx_1.invoked, 1);
test_int(ctx_1.count, 1);
test_int(ctx_1.e[0], e2);
test_int(ctx_1.s[0][0], e1);
test_int(ctx_1.c[0][0], tag_1);
test_int(ctx_1.event, EcsOnRemove);
ecs_os_zeromem(&ctx_1);
ecs_add_id(world, e3, ecs_pair(EcsChildOf, e1));
test_int(ctx_1.invoked, 0);
ecs_fini(world);
}
void Observer_cache_test_7(void) {
ecs_world_t *world = ecs_mini();
ecs_entity_t tag_1 = ecs_new_id(world);
ecs_entity_t tag_2 = ecs_new_id(world);
ecs_entity_t e1 = ecs_new_id(world);
ecs_entity_t e2 = ecs_new_id(world);
ecs_entity_t e3 = ecs_new_id(world);
Probe ctx_1 = {0};
ecs_observer(world, {
.filter.terms = {{ tag_1, .src.flags = EcsUp, .src.trav = EcsChildOf }},
.events = {EcsOnAdd, EcsOnRemove},
.callback = Observer,
.ctx = &ctx_1
});
ecs_add_id(world, e1, tag_1);
ecs_add_id(world, e1, tag_2);
test_int(ctx_1.invoked, 0);
ecs_add_id(world, e2, ecs_pair(EcsChildOf, e1));
test_int(ctx_1.invoked, 1);
test_int(ctx_1.count, 1);
test_int(ctx_1.e[0], e2);
test_int(ctx_1.s[0][0], e1);
test_int(ctx_1.c[0][0], tag_1);
test_int(ctx_1.event, EcsOnAdd);
ecs_os_zeromem(&ctx_1);
ecs_remove_id(world, e1, tag_1);
test_int(ctx_1.invoked, 1);
test_int(ctx_1.count, 1);
test_int(ctx_1.e[0], e2);
test_int(ctx_1.s[0][0], e1);
test_int(ctx_1.c[0][0], tag_1);
test_int(ctx_1.event, EcsOnRemove);
ecs_os_zeromem(&ctx_1);
ecs_add_id(world, e3, ecs_pair(EcsChildOf, e1));
test_int(ctx_1.invoked, 0);
ecs_fini(world);
}
void Observer_cache_test_8(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {{ TagB }},
.events = {EcsOnAdd},
.callback = Observer,
.ctx = &ctx
});
ecs_entity_t parent = ecs_new_id(world);
ecs_add(world, parent, TagA);
ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent);
ecs_new_w_pair(world, EcsChildOf, child);
test_int(ctx.invoked, 0);
ecs_add(world, parent, TagB);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], parent);
test_int(ctx.s[0][0], 0);
test_int(ctx.c[0][0], TagB);
test_int(ctx.event, EcsOnAdd);
ecs_fini(world);
}
void Observer_cache_test_9(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {{ ecs_id(Position), .src.flags = EcsUp }},
.events = { EcsOnRemove },
.callback = Observer,
.ctx = &ctx
});
ecs_entity_t base = ecs_new_w_id(world, EcsPrefab);
ecs_set(world, base, Position, {10, 20});
ecs_entity_t base_2 = ecs_new_w_id(world, EcsPrefab);
ecs_add_pair(world, base_2, EcsIsA, base);
ecs_set(world, base_2, Position, {20, 30});
ecs_entity_t base_3 = ecs_new_w_id(world, EcsPrefab);
ecs_add_pair(world, base_3, EcsIsA, base);
ecs_set(world, base_3, Position, {40, 50});
ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base_2);
ecs_add_pair(world, inst, EcsIsA, base_3);
ecs_clear(world, base_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));
test_int(ctx.event, EcsOnRemove);
ecs_os_zeromem(&ctx);
ecs_clear(world, base_3);
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));
test_int(ctx.event, EcsOnRemove);
ecs_fini(world);
}
void Observer_cache_test_10(void) {
ecs_world_t *world = ecs_mini();
ecs_entity_t tag = ecs_new_id(world);
ecs_entity_t ns = ecs_new_id(world);
ecs_add_id(world, ns, tag);
ecs_entity_t foo_parent = ecs_new_w_pair(world, EcsChildOf, ns);
ecs_entity_t foo = ecs_new_w_pair(world, EcsChildOf, foo_parent);
ecs_entity_t bar = ecs_new_id(world);
ecs_entity_t parent = ecs_new_w_id(world, foo);
ecs_entity_t e1 = ecs_new_id(world);
ecs_add_id(world, e1, foo);
ecs_add_pair(world, e1, EcsChildOf, parent);
ecs_add_id(world, e1, bar);
ecs_entity_t e2 = ecs_new_id(world);
ecs_add_id(world, e2, foo);
ecs_add_pair(world, e2, EcsChildOf, parent);
ecs_add_id(world, e2, bar);
ecs_entity_t c1 = ecs_new_id(world);
ecs_add_id(world, c1, foo);
ecs_add_pair(world, c1, EcsChildOf, e1);
ecs_entity_t c2 = ecs_new_id(world);
ecs_add_id(world, c2, foo);
ecs_add_pair(world, c2, EcsChildOf, e2);
ecs_delete(world, ns);
test_assert(!ecs_is_alive(world, ns));
test_assert(!ecs_is_alive(world, foo));
test_assert(!ecs_is_alive(world, foo_parent));
test_assert(!ecs_has_id(world, parent, foo));
test_assert(!ecs_has_id(world, e1, foo));
test_assert(!ecs_has_id(world, e2, foo));
test_assert(!ecs_has_id(world, c1, foo));
test_assert(!ecs_has_id(world, c2, foo));
ecs_delete(world, parent);
test_assert(!ecs_is_alive(world, parent));
test_assert(!ecs_is_alive(world, e1));
test_assert(!ecs_is_alive(world, e2));
test_assert(!ecs_is_alive(world, c1));
test_assert(!ecs_is_alive(world, c2));
ecs_fini(world);
}
void Observer_cache_test_11(void) {
ecs_world_t *world = ecs_mini();
ecs_entity_t ns = ecs_new_id(world);
ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, ns);
ecs_entity_t e2 = ecs_new_w_pair(world, EcsDependsOn, e1);
ecs_add_pair(world, e2, EcsChildOf, e1);
ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, ns);
ecs_add_id(world, e3, EcsModule);
ecs_entity_t e4 = ecs_new_w_pair(world, EcsChildOf, e3);
ecs_add_pair(world, e4, EcsDependsOn, e2);
ecs_add_id(world, e1, ecs_new_id(world));
ecs_run_aperiodic(world, 0);
test_assert(true); // ensure cache revalidation didn't assert
ecs_fini(world);
}
void Observer_cache_test_12(void) {
ecs_world_t *world = ecs_mini();
ecs_entity_t t0 = ecs_new_id(world);
ecs_entity_t t1 = ecs_new_id(world);
ecs_entity_t t2 = ecs_new_id(world);
ecs_entity_t e0 = ecs_new_id(world);
ecs_add_id(world, e0, t0);
ecs_add_id(world, e0, t1);
ecs_add_id(world, e0, t2);
ecs_entity_t e1 = ecs_new_id(world);
ecs_add_id(world, e1, t0);
ecs_add_id(world, e1, t1);
ecs_add_id(world, e1, t2);
ecs_new_w_pair(world, EcsChildOf, e1);
ecs_entity_t e3 = ecs_new_w_pair(world, EcsChildOf, e0);
ecs_add_id(world, e3, t0);
ecs_entity_t e4 = ecs_new_id(world);
ecs_new_w_pair(world, EcsChildOf, e4);
ecs_remove_all(world, t1);
ecs_new_w_pair(world, EcsChildOf, e3);
ecs_run_aperiodic(world, 0);
ecs_remove_all(world, t0);
test_assert(true); // ensure cache revalidation didn't assert
ecs_fini(world);
}
void Observer_cache_test_13(void) {
install_test_abort();
ecs_world_t *world = ecs_mini();
ecs_entity_t t0 = ecs_new_id(world);
ecs_entity_t e0 = ecs_new_id(world);
ecs_entity_t e1 = ecs_new_id(world);
ecs_add_id(world, e1, t0);
ecs_add_id(world, e1, ecs_pair(EcsChildOf, e0));
ecs_entity_t e2 = ecs_new_id(world);
ecs_add_id(world, e2, t0);
ecs_entity_t e3 = ecs_new_id(world);
ecs_add_id(world, e3, ecs_pair(EcsChildOf, e2));
ecs_delete_with(world, t0);
test_assert(!ecs_is_alive(world, e1));
test_assert(!ecs_is_alive(world, e2));
test_assert(!ecs_is_alive(world, e3));
ecs_entity_t e4 = ecs_new_id(world);
test_expect_abort();
ecs_add_id(world, e4, ecs_pair(EcsChildOf, e2));
}
void Observer_cache_test_14(void) {
ecs_world_t *world = ecs_mini();
ECS_TAG(world, Tag);
{
ecs_entity_t e0 = ecs_new_id(world);
ecs_add(world, e0, Tag);
ecs_entity_t e1 = ecs_new_id(world);
ecs_add_pair(world, e1, EcsChildOf, e0);
ecs_entity_t e2 = ecs_new_id(world);
ecs_add_pair(world, e2, EcsChildOf, e1);
ecs_delete_with(world, Tag);
test_assert(!ecs_is_alive(world, e0));
test_assert(!ecs_is_alive(world, e1));
test_assert(!ecs_is_alive(world, e2));
}
{
ecs_entity_t e0 = ecs_new_id(world);
ecs_add(world, e0, Tag);
ecs_entity_t e1 = ecs_new_id(world);
ecs_add_pair(world, e1, EcsChildOf, e0);
ecs_add(world, e1, Tag);
ecs_entity_t e2 = ecs_new_id(world);
ecs_add_pair(world, e2, EcsChildOf, e1);
ecs_remove_all(world, Tag);
test_assert(ecs_is_alive(world, e0));
test_assert(ecs_is_alive(world, e1));
test_assert(ecs_is_alive(world, e2));
test_assert(!ecs_has(world, e0, Tag));
}
ecs_fini(world);
}
void Observer_cache_test_15(void) {
ecs_world_t *world = ecs_mini();
ecs_entity_t r = ecs_new_id(world);
ecs_entity_t e0 = ecs_new_id(world);
ecs_entity_t e1 = ecs_new_id(world);
ecs_add_pair(world, e1, r, e0);
ecs_add_pair(world, e0, EcsChildOf, e1);
ecs_run_aperiodic(world, 0);
ecs_delete(world, e1);
test_assert(!ecs_is_alive(world, e1));
test_assert(!ecs_is_alive(world, e0));
test_assert(ecs_is_alive(world, r));
ecs_fini(world);
}
static int Observer_a_invoked = 0;
static int Observer_b_invoked = 0;
static void Observer_a(ecs_iter_t *it) {
Observer_a_invoked += it->count;
}
static void Observer_b(ecs_iter_t *it) {
Observer_b_invoked += it->count;
}
void Observer_filter_observer_after_observer(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_TAG(world, Tag);
ecs_observer(world, {
.filter.terms = {
{ .id = ecs_id(Position) },
{ .id = Tag }
},
.callback = Observer_a,
.events = { EcsOnAdd }
});
ecs_observer(world, {
.filter.terms = {
{ .id = ecs_id(Position), .inout = EcsInOutNone },
{ .id = Tag, .inout = EcsInOutNone}
},
.callback = Observer_b,
.events = { EcsOnAdd }
});
ecs_entity_t e = ecs_new(world, Tag);
ecs_add(world, e, Position);
test_int(Observer_a_invoked, 1);
test_int(Observer_b_invoked, 1);
ecs_fini(world);
}
void Observer_notify_after_defer_batched(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {
{ .id = ecs_id(Position) },
{ .id = ecs_id(Velocity) }
},
.callback = Observer,
.events = { EcsOnAdd },
.ctx = &ctx
});
ecs_defer_begin(world);
ecs_entity_t e1 = ecs_new_id(world);
ecs_set(world, e1, Position, {10, 20});
test_int(ctx.invoked, 0);
ecs_set(world, e1, Velocity, {1, 2});
ecs_defer_end(world);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e1);
ecs_fini(world);
}
void Observer_notify_after_defer_batched_2_entities_in_table(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {
{ .id = ecs_id(Position) },
{ .id = ecs_id(Velocity) }
},
.callback = Observer,
.events = { EcsOnAdd },
.ctx = &ctx
});
ecs_entity_t e1 = ecs_new_id(world);
ecs_set(world, e1, Position, {10, 20});
test_int(ctx.invoked, 0);
ecs_set(world, e1, Velocity, {1, 2});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e1);
ecs_os_zeromem(&ctx);
ecs_entity_t e2 = ecs_new_id(world);
ecs_defer_begin(world);
ecs_set(world, e2, Position, {30, 40});
ecs_set(world, e2, Velocity, {3, 4});
test_int(ctx.count, 0);
ecs_defer_end(world);
test_int(ctx.count, 1);
test_int(ctx.e[0], e2);
ecs_fini(world);
}
void Observer_notify_after_defer_batched_2_entities_in_table_w_tgt(void) {
ecs_world_t *world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {
{ .id = ecs_id(Position) },
{ .id = ecs_id(Velocity) }
},
.callback = Observer,
.events = { EcsOnAdd },
.ctx = &ctx
});
ecs_entity_t e1 = ecs_new_id(world);
ecs_set(world, e1, Position, {10, 20});
test_int(ctx.invoked, 0);
ecs_set(world, e1, Velocity, {1, 2});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e1);
ecs_os_zeromem(&ctx);
ecs_entity_t e2 = ecs_new_id(world);
ecs_entity_t e3 = ecs_new_id(world);
ecs_add_pair(world, e3, EcsChildOf, e2);
ecs_defer_begin(world);
ecs_set(world, e2, Position, {30, 40});
ecs_set(world, e2, Velocity, {3, 4});
test_int(ctx.count, 0);
ecs_defer_end(world);
test_int(ctx.count, 1);
test_int(ctx.e[0], e2);
ecs_fini(world);
}
void Observer_multi_observer_table_fill_w_singleton(void) {
ecs_world_t* world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
Probe ctx = {0};
ecs_observer(world, {
.filter.terms = {
{ .id = ecs_id(Position), .src.flags = EcsIsEntity, .src.id = ecs_id(Position) },
{ .id = ecs_id(Velocity) },
},
.filter.flags = EcsFilterNoData,
.callback = Observer,
.events = { EcsOnTableFill },
.ctx = &ctx
});
ecs_singleton_add(world, Position);
test_int(ctx.invoked, 0);
ecs_entity_t e = ecs_new_id(world);
ecs_add(world, e, Velocity);
test_int(ctx.invoked, 0);
ecs_run_aperiodic(world, 0);
test_int(ctx.invoked, 1);
ecs_fini(world);
}
ECS_COMPONENT_DECLARE(Velocity);
static
void AddVelocity(ecs_iter_t *it) {
for (int i = 0; i < it->count; i ++) {
ecs_add(it->world, it->entities[i], Velocity);
test_assert(!ecs_has(it->world, it->entities[i], Velocity));
}
}
void Observer_add_in_yield_existing(void) {
ecs_world_t* world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT_DEFINE(world, Velocity);
ecs_entity_t e1 = ecs_new(world, Position);
ecs_entity_t e2 = ecs_new(world, Position);
ecs_entity_t e3 = ecs_new(world, Position);
ecs_observer(world, {
.filter.terms = {
{ .id = ecs_id(Position) },
},
.callback = AddVelocity,
.events = { EcsOnAdd },
.yield_existing = true,
});
test_assert(ecs_has(world, e1, Position));
test_assert(ecs_has(world, e1, Velocity));
test_assert(ecs_has(world, e2, Position));
test_assert(ecs_has(world, e2, Velocity));
test_assert(ecs_has(world, e3, Position));
test_assert(ecs_has(world, e3, Velocity));
ecs_fini(world);
}
void Observer_add_in_yield_existing_multi(void) {
ecs_world_t* world = ecs_mini();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Mass);
ECS_COMPONENT_DEFINE(world, Velocity);
ecs_entity_t e1 = ecs_new(world, Position);
ecs_entity_t e2 = ecs_new(world, Position);
ecs_entity_t e3 = ecs_new(world, Position);
ecs_add(world, e1, Mass);
ecs_add(world, e2, Mass);
ecs_add(world, e3, Mass);
ecs_observer(world, {
.filter.terms = {
{ .id = ecs_id(Position) },
{ .id = ecs_id(Mass) },
},
.callback = AddVelocity,
.events = { EcsOnAdd },
.yield_existing = true,
});
test_assert(ecs_has(world, e1, Position));
test_assert(ecs_has(world, e1, Mass));
test_assert(ecs_has(world, e1, Velocity));
test_assert(ecs_has(world, e2, Position));
test_assert(ecs_has(world, e2, Mass));
test_assert(ecs_has(world, e2, Velocity));
test_assert(ecs_has(world, e3, Position));
test_assert(ecs_has(world, e3, Mass));
test_assert(ecs_has(world, e3, Velocity));
ecs_fini(world);
}
void Observer_emit_for_parent_w_prefab_child_and_instance(void) {
ecs_world_t* ecs = ecs_init();
ECS_COMPONENT(ecs, Position);
Probe ctx = {0};
ecs_observer(ecs, {
.filter.terms = {
{ .id = ecs_id(Position) }
},
.events = { EcsOnSet },
.callback = Observer,
.ctx = &ctx
});
ecs_entity_t e = ecs_new_id(ecs);
ecs_entity_t p = ecs_new_w_pair(ecs, EcsChildOf, e);
ecs_entity_t child_1 = ecs_new_w_pair(ecs, EcsChildOf, e);
ecs_add_pair(ecs, child_1, EcsIsA, p);
test_int(ctx.invoked, 0);
ecs_set(ecs, e, Position, {10, 20});
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e);
test_int(ctx.c[0][0], ecs_id(Position));
test_int(ctx.event, EcsOnSet);
ecs_fini(ecs);
}