#include static void Iter(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); Position *p_parent = ecs_field(it, Position, 2); test_assert(!p_parent || !ecs_field_is_self(it, 2)); probe_iter(it); int i; for (i = 0; i < it->count; i ++) { p[i].x ++; p[i].y ++; if (p_parent) { p[i].x += p_parent->x; p[i].y += p_parent->y; } } } void SystemCascade_cascade_depth_1(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_ENTITY(world, e1, Position); ECS_ENTITY(world, e2, Position); ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, ?Position(parent|cascade)); ecs_system_init(world, &(ecs_system_desc_t){ .entity = Iter, .query.filter.instanced = true }); ecs_set(world, e1, Position, {1, 2}); ecs_set(world, e2, Position, {1, 2}); ecs_set(world, e3, Position, {1, 2}); ecs_set(world, e4, Position, {1, 2}); ecs_add_pair(world, e3, EcsChildOf, e1); ecs_add_pair(world, e4, EcsChildOf, e2); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); ecs_progress(world, 1); test_int(ctx.count, 4); test_int(ctx.invoked, 3); test_int(ctx.system, Iter); test_int(ctx.term_count, 2); test_null(ctx.param); probe_has_entity(&ctx, e1); probe_has_entity(&ctx, e2); probe_has_entity(&ctx, e3); probe_has_entity(&ctx, e4); test_int(ctx.c[0][0], ecs_id(Position)); test_int(ctx.s[0][0], 0); const Position *p = ecs_get(world, e1, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 3); p = ecs_get(world, e2, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 3); p = ecs_get(world, e3, Position); test_assert(p != NULL); test_int(p->x, 4); test_int(p->y, 6); p = ecs_get(world, e4, Position); test_assert(p != NULL); test_int(p->x, 4); test_int(p->y, 6); ecs_fini(world); } void SystemCascade_cascade_depth_2(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_ENTITY(world, e1, Position); ECS_ENTITY(world, e2, Position); ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); ECS_ENTITY(world, e5, Position); ECS_ENTITY(world, e6, Position); ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, ?Position(parent|cascade)); ecs_system_init(world, &(ecs_system_desc_t){ .entity = Iter, .query.filter.instanced = true }); ecs_set(world, e1, Position, {1, 2}); ecs_set(world, e2, Position, {1, 2}); ecs_set(world, e3, Position, {1, 2}); ecs_set(world, e4, Position, {1, 2}); ecs_set(world, e5, Position, {1, 2}); ecs_set(world, e6, Position, {1, 2}); ecs_add_pair(world, e3, EcsChildOf, e1); /* depth 1 */ ecs_add_pair(world, e4, EcsChildOf, e2); /* depth 1 */ ecs_add_pair(world, e5, EcsChildOf, e3); /* depth 2 */ ecs_add_pair(world, e6, EcsChildOf, e4); /* depth 2 */ Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); ecs_progress(world, 1); test_int(ctx.count, 6); test_int(ctx.invoked, 5); test_int(ctx.system, Iter); test_int(ctx.term_count, 2); test_null(ctx.param); test_assert((ctx.e[0] == e1 && ctx.e[1] == e2) || (ctx.e[0] == e2 && ctx.e[1] == e1)); test_assert((ctx.e[2] == e3 && ctx.e[3] == e4) || (ctx.e[2] == e4 && ctx.e[3] == e3)); test_assert((ctx.e[4] == e5 && ctx.e[5] == e6) || (ctx.e[4] == e6 && ctx.e[5] == e5)); test_int(ctx.c[0][0], ecs_id(Position)); test_int(ctx.s[0][0], 0); const Position *p = ecs_get(world, e1, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 3); p = ecs_get(world, e2, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 3); p = ecs_get(world, e3, Position); test_assert(p != NULL); test_int(p->x, 4); test_int(p->y, 6); p = ecs_get(world, e4, Position); test_assert(p != NULL); test_int(p->x, 4); test_int(p->y, 6); p = ecs_get(world, e5, Position); test_assert(p != NULL); test_int(p->x, 6); test_int(p->y, 9); p = ecs_get(world, e6, Position); test_assert(p != NULL); test_int(p->x, 6); test_int(p->y, 9); ecs_fini(world); } void SystemCascade_cascade_depth_2_new_syntax(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_ENTITY(world, e1, Position); ECS_ENTITY(world, e2, Position); ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); ECS_ENTITY(world, e5, Position); ECS_ENTITY(world, e6, Position); ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, ?Position(cascade(ChildOf))); ecs_system_init(world, &(ecs_system_desc_t){ .entity = Iter, .query.filter.instanced = true }); ecs_set(world, e1, Position, {1, 2}); ecs_set(world, e2, Position, {1, 2}); ecs_set(world, e3, Position, {1, 2}); ecs_set(world, e4, Position, {1, 2}); ecs_set(world, e5, Position, {1, 2}); ecs_set(world, e6, Position, {1, 2}); ecs_add_pair(world, e3, EcsChildOf, e1); /* depth 1 */ ecs_add_pair(world, e4, EcsChildOf, e2); /* depth 1 */ ecs_add_pair(world, e5, EcsChildOf, e3); /* depth 2 */ ecs_add_pair(world, e6, EcsChildOf, e4); /* depth 2 */ Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); ecs_progress(world, 1); test_int(ctx.count, 6); test_int(ctx.invoked, 5); test_int(ctx.system, Iter); test_int(ctx.term_count, 2); test_null(ctx.param); test_assert((ctx.e[0] == e1 && ctx.e[1] == e2) || (ctx.e[0] == e2 && ctx.e[1] == e1)); test_assert((ctx.e[2] == e3 && ctx.e[3] == e4) || (ctx.e[2] == e4 && ctx.e[3] == e3)); test_assert((ctx.e[4] == e5 && ctx.e[5] == e6) || (ctx.e[4] == e6 && ctx.e[5] == e5)); test_int(ctx.c[0][0], ecs_id(Position)); test_int(ctx.s[0][0], 0); const Position *p = ecs_get(world, e1, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 3); p = ecs_get(world, e2, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 3); p = ecs_get(world, e3, Position); test_assert(p != NULL); test_int(p->x, 4); test_int(p->y, 6); p = ecs_get(world, e4, Position); test_assert(p != NULL); test_int(p->x, 4); test_int(p->y, 6); p = ecs_get(world, e5, Position); test_assert(p != NULL); test_int(p->x, 6); test_int(p->y, 9); p = ecs_get(world, e6, Position); test_assert(p != NULL); test_int(p->x, 6); test_int(p->y, 9); ecs_fini(world); } static void AddParent(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); Position *p_parent = ecs_field(it, Position, 2); test_assert(!p_parent || !ecs_field_is_self(it, 2)); probe_iter(it); int i; for (i = 0; i < it->count; i ++) { if (p_parent) { p[i].x += p_parent->x; p[i].y += p_parent->y; } } } void SystemCascade_add_after_match(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_ENTITY(world, e1, Position); ECS_ENTITY(world, e2, Position); ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); ECS_SYSTEM(world, AddParent, EcsOnUpdate, Position, ?Position(parent|cascade)); ecs_system_init(world, &(ecs_system_desc_t){ .entity = AddParent, .query.filter.instanced = true }); ecs_entity_t parent = ecs_new(world, 0); ecs_set(world, e1, Position, {1, 2}); ecs_set(world, e2, Position, {1, 2}); ecs_set(world, e3, Position, {1, 2}); ecs_set(world, e4, Position, {1, 2}); ecs_add_pair(world, e3, EcsChildOf, parent); /* depth 1 */ ecs_add_pair(world, e4, EcsChildOf, parent); /* depth 1 */ Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); ecs_progress(world, 1); /* Before adding Position to parent, it wasn't being considered for the * column(parent|cascade), so tables could have been ordered randomly. Make sure * that queries can handle changes to depth after all tables are matched */ ecs_set(world, parent, Position, {1, 2}); ecs_os_zeromem(&ctx); ecs_progress(world, 1); test_int(ctx.count, 5); test_int(ctx.invoked, 3); test_int(ctx.system, AddParent); test_int(ctx.term_count, 2); test_null(ctx.param); test_assert(ctx.e[0] == e1 || ctx.e[1] == e1 || ctx.e[2] == e1); test_assert(ctx.e[0] == e2 || ctx.e[1] == e2 || ctx.e[2] == e2); test_assert(ctx.e[0] == parent || ctx.e[1] == parent || ctx.e[2] == parent); test_assert(ctx.e[3] == e3 || ctx.e[4] == e3); test_assert(ctx.e[3] == e4 || ctx.e[4] == e4); test_int(ctx.c[0][0], ecs_id(Position)); test_int(ctx.s[0][0], 0); const Position *p = ecs_get(world, e1, Position); test_assert(p != NULL); test_int(p->x, 1); test_int(p->y, 2); p = ecs_get(world, e2, Position); test_assert(p != NULL); test_int(p->x, 1); test_int(p->y, 2); p = ecs_get(world, e3, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 4); p = ecs_get(world, e4, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 4); ecs_fini(world); } void SystemCascade_adopt_after_match(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_ENTITY(world, e1, Position); ECS_ENTITY(world, e2, Position); ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); ECS_SYSTEM(world, AddParent, EcsOnUpdate, Position, ?Position(parent|cascade)); ecs_system_init(world, &(ecs_system_desc_t){ .entity = AddParent, .query.filter.instanced = true }); ecs_entity_t parent = ecs_set(world, 0, Position, {1, 2}); ecs_set(world, e1, Position, {1, 2}); ecs_set(world, e2, Position, {1, 2}); ecs_set(world, e3, Position, {1, 2}); ecs_set(world, e4, Position, {1, 2}); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); ecs_progress(world, 1); ecs_add_pair(world, e3, EcsChildOf, parent); ecs_add_pair(world, e4, EcsChildOf, parent); ecs_os_zeromem(&ctx); ecs_progress(world, 1); test_int(ctx.count, 5); test_int(ctx.invoked, 3); test_int(ctx.system, AddParent); 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], parent); test_int(ctx.e[3], e3); test_int(ctx.e[4], e4); test_int(ctx.c[0][0], ecs_id(Position)); test_int(ctx.s[0][0], 0); const Position *p = ecs_get(world, e1, Position); test_assert(p != NULL); test_int(p->x, 1); test_int(p->y, 2); p = ecs_get(world, e2, Position); test_assert(p != NULL); test_int(p->x, 1); test_int(p->y, 2); p = ecs_get(world, e3, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 4); p = ecs_get(world, e4, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 4); ecs_fini(world); } void SystemCascade_custom_relation_cascade_depth_1(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_ENTITY(world, Rel, Traversable); ECS_ENTITY(world, e1, Position); ECS_ENTITY(world, e2, Position); ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, ?Position(cascade(Rel))); ecs_system_init(world, &(ecs_system_desc_t){ .entity = Iter, .query.filter.instanced = true }); ecs_set(world, e1, Position, {1, 2}); ecs_set(world, e2, Position, {1, 2}); ecs_set(world, e3, Position, {1, 2}); ecs_set(world, e4, Position, {1, 2}); ecs_add_pair(world, e3, Rel, e1); ecs_add_pair(world, e4, Rel, e2); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); ecs_progress(world, 1); test_int(ctx.count, 4); test_int(ctx.invoked, 3); test_int(ctx.system, Iter); test_int(ctx.term_count, 2); test_null(ctx.param); probe_has_entity(&ctx, e1); probe_has_entity(&ctx, e2); probe_has_entity(&ctx, e3); probe_has_entity(&ctx, e4); test_int(ctx.c[0][0], ecs_id(Position)); test_int(ctx.s[0][0], 0); const Position *p = ecs_get(world, e1, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 3); p = ecs_get(world, e2, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 3); p = ecs_get(world, e3, Position); test_assert(p != NULL); test_int(p->x, 4); test_int(p->y, 6); p = ecs_get(world, e4, Position); test_assert(p != NULL); test_int(p->x, 4); test_int(p->y, 6); ecs_fini(world); } void SystemCascade_custom_relation_cascade_depth_2(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_ENTITY(world, Rel, Traversable); ECS_ENTITY(world, e1, Position); ECS_ENTITY(world, e2, Position); ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); ECS_ENTITY(world, e5, Position); ECS_ENTITY(world, e6, Position); ECS_SYSTEM(world, Iter, EcsOnUpdate, Position, ?Position(cascade(Rel))); ecs_system_init(world, &(ecs_system_desc_t){ .entity = Iter, .query.filter.instanced = true }); ecs_set(world, e1, Position, {1, 2}); ecs_set(world, e2, Position, {1, 2}); ecs_set(world, e3, Position, {1, 2}); ecs_set(world, e4, Position, {1, 2}); ecs_set(world, e5, Position, {1, 2}); ecs_set(world, e6, Position, {1, 2}); ecs_add_pair(world, e3, Rel, e1); /* depth 1 */ ecs_add_pair(world, e4, Rel, e2); /* depth 1 */ ecs_add_pair(world, e5, Rel, e3); /* depth 2 */ ecs_add_pair(world, e6, Rel, e4); /* depth 2 */ Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); ecs_progress(world, 1); test_int(ctx.count, 6); test_int(ctx.invoked, 5); test_int(ctx.system, Iter); test_int(ctx.term_count, 2); test_null(ctx.param); test_assert((ctx.e[0] == e1 && ctx.e[1] == e2) || (ctx.e[0] == e2 && ctx.e[1] == e1)); test_assert((ctx.e[2] == e3 && ctx.e[3] == e4) || (ctx.e[2] == e4 && ctx.e[3] == e3)); test_assert((ctx.e[4] == e5 && ctx.e[5] == e6) || (ctx.e[4] == e6 && ctx.e[5] == e5)); test_int(ctx.c[0][0], ecs_id(Position)); test_int(ctx.s[0][0], 0); const Position *p = ecs_get(world, e1, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 3); p = ecs_get(world, e2, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 3); p = ecs_get(world, e3, Position); test_assert(p != NULL); test_int(p->x, 4); test_int(p->y, 6); p = ecs_get(world, e4, Position); test_assert(p != NULL); test_int(p->x, 4); test_int(p->y, 6); p = ecs_get(world, e5, Position); test_assert(p != NULL); test_int(p->x, 6); test_int(p->y, 9); p = ecs_get(world, e6, Position); test_assert(p != NULL); test_int(p->x, 6); test_int(p->y, 9); ecs_fini(world); } void SystemCascade_custom_relation_add_after_match(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_ENTITY(world, Rel, Traversable); ECS_ENTITY(world, e1, Position); ECS_ENTITY(world, e2, Position); ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); ECS_SYSTEM(world, AddParent, EcsOnUpdate, Position, ?Position(cascade(Rel))); ecs_system_init(world, &(ecs_system_desc_t){ .entity = AddParent, .query.filter.instanced = true }); ecs_entity_t parent = ecs_new(world, 0); ecs_set(world, e1, Position, {1, 2}); ecs_set(world, e2, Position, {1, 2}); ecs_set(world, e3, Position, {1, 2}); ecs_set(world, e4, Position, {1, 2}); ecs_add_pair(world, e3, Rel, parent); /* depth 1 */ ecs_add_pair(world, e4, Rel, parent); /* depth 1 */ Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); ecs_progress(world, 1); /* Before adding Position to parent, it wasn't being considered for the * column(parent|cascade), so tables could have been ordered randomly. Make sure * that queries can handle changes to depth after all tables are matched */ ecs_set(world, parent, Position, {1, 2}); ecs_os_zeromem(&ctx); ecs_progress(world, 1); test_int(ctx.count, 5); test_int(ctx.invoked, 3); test_int(ctx.system, AddParent); test_int(ctx.term_count, 2); test_null(ctx.param); test_assert(ctx.e[0] == e1 || ctx.e[1] == e1 || ctx.e[2] == e1); test_assert(ctx.e[0] == e2 || ctx.e[1] == e2 || ctx.e[2] == e2); test_assert(ctx.e[0] == parent || ctx.e[1] == parent || ctx.e[2] == parent); test_assert(ctx.e[3] == e3 || ctx.e[4] == e3); test_assert(ctx.e[3] == e4 || ctx.e[4] == e4); test_int(ctx.c[0][0], ecs_id(Position)); test_int(ctx.s[0][0], 0); const Position *p = ecs_get(world, e1, Position); test_assert(p != NULL); test_int(p->x, 1); test_int(p->y, 2); p = ecs_get(world, e2, Position); test_assert(p != NULL); test_int(p->x, 1); test_int(p->y, 2); p = ecs_get(world, e3, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 4); p = ecs_get(world, e4, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 4); ecs_fini(world); } void SystemCascade_custom_relation_adopt_after_match(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_ENTITY(world, Rel, Traversable); ECS_ENTITY(world, e1, Position); ECS_ENTITY(world, e2, Position); ECS_ENTITY(world, e3, Position); ECS_ENTITY(world, e4, Position); ECS_SYSTEM(world, AddParent, EcsOnUpdate, Position, ?Position(cascade(Rel))); ecs_system_init(world, &(ecs_system_desc_t){ .entity = AddParent, .query.filter.instanced = true }); ecs_entity_t parent = ecs_set(world, 0, Position, {1, 2}); ecs_set(world, e1, Position, {1, 2}); ecs_set(world, e2, Position, {1, 2}); ecs_set(world, e3, Position, {1, 2}); ecs_set(world, e4, Position, {1, 2}); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); ecs_progress(world, 1); ecs_add_pair(world, e3, Rel, parent); ecs_add_pair(world, e4, Rel, parent); ecs_os_zeromem(&ctx); ecs_progress(world, 1); test_int(ctx.count, 5); test_int(ctx.invoked, 3); test_int(ctx.system, AddParent); 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], parent); test_int(ctx.e[3], e3); test_int(ctx.e[4], e4); test_int(ctx.c[0][0], ecs_id(Position)); test_int(ctx.s[0][0], 0); const Position *p = ecs_get(world, e1, Position); test_assert(p != NULL); test_int(p->x, 1); test_int(p->y, 2); p = ecs_get(world, e2, Position); test_assert(p != NULL); test_int(p->x, 1); test_int(p->y, 2); p = ecs_get(world, e3, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 4); p = ecs_get(world, e4, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 4); ecs_fini(world); }