#include static void OnPosition(ecs_iter_t *it) { probe_iter(it); } void Monitor_1_comp(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_OBSERVER(world, OnPosition, EcsOnAdd, Position); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); ecs_entity_t e = ecs_new(world, Position); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, OnPosition); 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_run_aperiodic(world, 0); test_int(ctx.invoked, 0); ecs_add(world, e, Velocity); test_int(ctx.invoked, 0); ecs_fini(world); } void Monitor_2_comps(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_OBSERVER(world, OnPosition, EcsOnAdd, 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_add(world, e, Velocity); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, OnPosition); 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, Position); test_int(ctx.invoked, 0); ecs_add(world, e, Position); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, OnPosition); 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); } void Monitor_1_comp_1_not(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_OBSERVER(world, OnPosition, EcsOnAdd, Position, !Velocity); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); ecs_entity_t e = ecs_new(world, Position); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, OnPosition); 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_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, OnPosition); 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); } void Monitor_1_parent(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_OBSERVER(world, OnPosition, EcsOnAdd, Position(parent)); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); ecs_entity_t parent = ecs_new(world, Position); test_int(ctx.invoked, 0); ecs_entity_t e = ecs_new(world, 0); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsChildOf, parent); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.system, OnPosition); 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], parent); ecs_fini(world); } void Monitor_1_comp_1_parent(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_OBSERVER(world, OnPosition, EcsMonitor, Position, Position(up(ChildOf))); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); ecs_entity_t parent = ecs_new(world, Position); test_int(ctx.invoked, 0); ecs_entity_t e = ecs_new(world, Position); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsChildOf, parent); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.event, EcsOnAdd); test_int(ctx.system, OnPosition); 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(Position)); test_int(ctx.s[0][1], parent); ecs_os_zeromem(&ctx); ecs_add(world, e, Velocity); 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.event, EcsOnRemove); ecs_os_zeromem(&ctx); ecs_add(world, e, Position); test_int(ctx.invoked, 1); test_int(ctx.count, 1); test_int(ctx.event, EcsOnAdd); test_int(ctx.system, OnPosition); 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(Position)); test_int(ctx.s[0][1], parent); ecs_fini(world); } void Monitor_1_comp_prefab_new(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_OBSERVER(world, OnPosition, EcsOnAdd, Position); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); ECS_PREFAB(world, Prefab, Position); test_int(ctx.invoked, 0); ecs_run_aperiodic(world, 0); test_int(ctx.invoked, 0); ecs_add(world, Prefab, Velocity); test_int(ctx.invoked, 0); ecs_fini(world); } void Monitor_1_comp_prefab_add(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_OBSERVER(world, OnPosition, EcsOnAdd, Position); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); ecs_entity_t e = ecs_new_w_id(world, EcsPrefab); ecs_add(world, e, Position); test_int(ctx.invoked, 0); ecs_run_aperiodic(world, 0); test_int(ctx.invoked, 0); ecs_add(world, e, Velocity); test_int(ctx.invoked, 0); ecs_fini(world); } void Monitor_monitor_w_and(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); ECS_OBSERVER(world, OnPosition, EcsMonitor, Position, Velocity); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_add(world, e, Velocity); test_int(ctx.invoked, 1); // First match, trigger OnAdd test_int(ctx.event, EcsOnAdd); ecs_os_zeromem(&ctx); ecs_remove(world, e, Velocity); test_int(ctx.invoked, 1); // First unmatch, trigger OnRemove test_int(ctx.event, EcsOnRemove); ecs_os_zeromem(&ctx); ecs_remove(world, e, Position); test_int(ctx.invoked, 0); // Second unmatch, no trigger ecs_add(world, e, Velocity); test_int(ctx.invoked, 0); ecs_add(world, e, Position); test_int(ctx.invoked, 1); // First match, trigger OnAdd test_int(ctx.event, EcsOnAdd); ecs_fini(world); } void Monitor_monitor_w_or(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); ECS_OBSERVER(world, OnPosition, EcsMonitor, Position, Velocity || Mass); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); // No match, filter doesn't match yet ecs_add(world, e, Velocity); test_int(ctx.invoked, 1); // First match, triggers OnAdd test_int(ctx.event, EcsOnAdd); ecs_os_zeromem(&ctx); ecs_add(world, e, Mass); test_int(ctx.invoked, 0); // Second match, no trigger ecs_remove(world, e, Position); test_int(ctx.invoked, 1); // First non-match, triggers OnRemove test_int(ctx.event, EcsOnRemove); ecs_os_zeromem(&ctx); ecs_add(world, e, Position); // First match, trigger test_int(ctx.invoked, 1); test_int(ctx.event, EcsOnAdd); ecs_fini(world); } void Monitor_monitor_w_not(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); ECS_OBSERVER(world, OnPosition, EcsMonitor, Position, Mass, !Velocity); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_add(world, e, Mass); test_int(ctx.invoked, 1); // First match, trigger OnAdd test_int(ctx.event, EcsOnAdd); ecs_os_zeromem(&ctx); ecs_add(world, e, Velocity); test_int(ctx.invoked, 1); // First unmatch, trigger OnRemove test_int(ctx.event, EcsOnRemove); ecs_os_zeromem(&ctx); ecs_remove(world, e, Velocity); test_int(ctx.invoked, 1); // First match, trigger OnAdd test_int(ctx.event, EcsOnAdd); ecs_fini(world); } void Monitor_monitor_w_optional(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); ECS_OBSERVER(world, OnPosition, EcsMonitor, Position, ?Velocity); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_int(ctx.invoked, 1); // First match, trigger OnAdd test_int(ctx.event, EcsOnAdd); ecs_os_zeromem(&ctx); ecs_add(world, e, Velocity); test_int(ctx.invoked, 0); // Second match, no trigger ecs_remove(world, e, Position); test_int(ctx.invoked, 1); // First unmatch, trigger OnRemove test_int(ctx.event, EcsOnRemove); ecs_os_zeromem(&ctx); ecs_add(world, e, Position); test_int(ctx.invoked, 1); // First match, trigger OnAdd test_int(ctx.event, EcsOnAdd); ecs_os_zeromem(&ctx); ecs_remove(world, e, Velocity); test_int(ctx.invoked, 0); // Second match, no trigger ecs_fini(world); } void Monitor_monitor_w_superset(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); ECS_OBSERVER(world, OnPosition, EcsMonitor, Position, Velocity(up)); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); ecs_entity_t base_1 = ecs_new(world, Velocity); test_assert(base_1 != 0); test_int(ctx.invoked, 0); ecs_entity_t base_2 = ecs_new(world, Velocity); test_assert(base_2 != 0); test_int(ctx.invoked, 0); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsIsA, base_1); test_int(ctx.invoked, 1); // First match, trigger test_int(ctx.event, EcsOnAdd); ecs_os_zeromem(&ctx); ecs_add_pair(world, e, EcsIsA, base_2); test_int(ctx.invoked, 0); // Second match, no trigger ecs_remove_pair(world, e, EcsIsA, base_2); test_int(ctx.invoked, 0); ecs_remove_pair(world, e, EcsIsA, base_1); test_int(ctx.invoked, 1); // First unmatch, trigger OnRemove test_int(ctx.event, EcsOnRemove); ecs_os_zeromem(&ctx); ecs_remove(world, e, Position); test_int(ctx.invoked, 0); // Second unmatch, no trigger ecs_fini(world); } void Monitor_monitor_w_self_superset(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); ECS_OBSERVER(world, OnPosition, EcsMonitor, Position, Velocity(self|up)); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); ecs_entity_t base_1 = ecs_new(world, Velocity); test_assert(base_1 != 0); test_int(ctx.invoked, 0); ecs_entity_t base_2 = ecs_new(world, Velocity); test_assert(base_2 != 0); test_int(ctx.invoked, 0); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_add_pair(world, e, EcsIsA, base_1); test_int(ctx.invoked, 1); // First match, trigger test_int(ctx.event, EcsOnAdd); ecs_os_zeromem(&ctx); ecs_add_pair(world, e, EcsIsA, base_2); test_int(ctx.invoked, 0); // Second match, no trigger ecs_add(world, e, Velocity); test_int(ctx.invoked, 0); // Third match, no trigger ecs_remove_pair(world, e, EcsIsA, base_2); test_int(ctx.invoked, 0); ecs_remove_pair(world, e, EcsIsA, base_1); test_int(ctx.invoked, 0); ecs_remove(world, e, Velocity); test_int(ctx.invoked, 1); // First unmatch, trigger OnRemove test_int(ctx.event, EcsOnRemove); ecs_os_zeromem(&ctx); ecs_add(world, e, Velocity); test_int(ctx.invoked, 1); // First match, trigger test_int(ctx.event, EcsOnAdd); ecs_os_zeromem(&ctx); ecs_remove(world, e, Position); test_int(ctx.invoked, 1); // First unmatch, trigger OnRemove test_int(ctx.event, EcsOnRemove); ecs_fini(world); } void Monitor_monitor_w_wildcard(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_TAG(world, Eats); ECS_TAG(world, Apples); ECS_TAG(world, Pears); ECS_OBSERVER(world, OnPosition, EcsMonitor, Position, (Eats, *)); Probe ctx = { 0 }; ecs_set_ctx(world, &ctx, NULL); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_int(ctx.invoked, 0); ecs_add_pair(world, e, Eats, Apples); test_int(ctx.invoked, 1); // First match, trigger OnAdd test_int(ctx.event, EcsOnAdd); ecs_os_zeromem(&ctx); ecs_add_pair(world, e, Eats, Pears); test_int(ctx.invoked, 0); // Second match, no trigger ecs_remove(world, e, Position); test_int(ctx.invoked, 1); // First unmatch, trigger OnRemove test_int(ctx.event, EcsOnRemove); ecs_os_zeromem(&ctx); ecs_add(world, e, Position); test_int(ctx.invoked, 1); // First match, trigger OnAdd test_int(ctx.event, EcsOnAdd); ecs_os_zeromem(&ctx); ecs_remove_pair(world, e, Eats, Apples); test_int(ctx.invoked, 0); ecs_remove_pair(world, e, Eats, Pears); test_int(ctx.invoked, 1); // First unmatch, trigger OnRemove test_int(ctx.event, EcsOnRemove); ecs_fini(world); } static void Monitor(ecs_iter_t *it) { probe_system_w_ctx(it, it->ctx); } void Monitor_monitor_at_fini(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); Probe ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms = {{ TagA }}, .events = {EcsMonitor}, .callback = Monitor, .ctx = &ctx }); test_int(ctx.invoked, 0); ecs_entity_t e = ecs_new_id(world); ecs_add(world, e, TagA); test_int(ctx.invoked, 1); test_int(ctx.event, EcsOnAdd); ecs_os_zeromem(&ctx); ecs_fini(world); test_int(ctx.invoked, 1); test_int(ctx.event, EcsOnRemove); } typedef struct { ecs_entity_t event; ecs_table_t *table; ecs_table_t *other_table; int32_t invoked; } check_table_t; static void CheckTable(ecs_iter_t *it) { check_table_t *ctx = it->ctx; test_assert(it->event == ctx->event); test_assert(it->table == ctx->table); test_assert(it->other_table == ctx->other_table); ctx->invoked ++; } void Monitor_monitor_other_table(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, X); ECS_TAG(world, Y); ecs_entity_t x = ecs_new(world, X); ecs_entity_t xy = ecs_new(world, X); ecs_add(world, xy, Y); check_table_t ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms = { { X }, { Y, .oper = EcsNot } }, .events = {EcsMonitor}, .callback = CheckTable, .ctx = &ctx }); ctx.table = ecs_get_table(world, x); ctx.other_table = NULL; ctx.event = EcsOnAdd; ecs_entity_t e = ecs_new(world, X); test_int(ctx.invoked, 1); ctx.table = ecs_get_table(world, xy); ctx.other_table = ecs_get_table(world, x); ctx.event = EcsOnRemove; ecs_add(world, e, Y); test_int(ctx.invoked, 2); ctx.table = ecs_get_table(world, xy); ctx.other_table = ecs_get_table(world, x); ctx.event = EcsOnAdd; ecs_remove(world, e, Y); test_int(ctx.invoked, 3); ctx.table = ecs_get_table(world, x); ctx.other_table = NULL; ctx.event = EcsOnRemove; ecs_fini(world); } typedef struct { ecs_entity_t event; Position *result; int32_t invoked; } check_component_t; static void CheckComponent(ecs_iter_t *it) { check_component_t *ctx = it->ctx; test_assert(it->event == ctx->event); ctx->result = ecs_field(it, Position, 1); ctx->invoked ++; } void Monitor_monitor_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_TAG(world, Tag); check_component_t ctx = {0}; ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms = { { ecs_id(Position) }, { Tag, .oper = EcsNot } }, .events = {EcsMonitor}, .callback = CheckComponent, .ctx = &ctx }); ctx.event = EcsOnAdd; ecs_entity_t e = ecs_new(world, Position); const Position *expect = ecs_get(world, e, Position); test_int(ctx.invoked, 1); test_assert(ctx.result == expect); ctx.event = EcsOnRemove; ecs_add(world, e, Tag); expect = ecs_get(world, e, Position); test_int(ctx.invoked, 2); test_assert(ctx.result == expect); ctx.event = EcsOnAdd; ecs_remove(world, e, Tag); test_int(ctx.invoked, 3); test_assert(ctx.result == expect); expect = ecs_get(world, e, Position); ctx.event = EcsOnRemove; ecs_fini(world); test_int(ctx.invoked, 4); test_assert(ctx.result == expect); }