#include void Commands_defer_new(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_defer_begin(world); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_assert(!ecs_has(world, e, Position)); ecs_defer_end(world); test_assert(!ecs_has(world, e, Position)); ecs_defer_end(world); test_assert(ecs_has(world, e, Position)); ecs_frame_end(world); test_assert(ecs_has(world, e, Position)); ecs_fini(world); } void Commands_defer_bulk_new(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_defer_begin(world); const ecs_entity_t *temp_ids = ecs_bulk_new(world, Position, 3); ecs_entity_t ids[3]; memcpy(ids, temp_ids, sizeof(ecs_entity_t) * 3); test_assert(!ecs_has(world, ids[0], Position)); test_assert(!ecs_has(world, ids[1], Position)); test_assert(!ecs_has(world, ids[2], Position)); ecs_defer_end(world); test_assert(!ecs_has(world, ids[0], Position)); test_assert(!ecs_has(world, ids[1], Position)); test_assert(!ecs_has(world, ids[2], Position)); ecs_defer_end(world); test_assert(ecs_has(world, ids[0], Position)); test_assert(ecs_has(world, ids[1], Position)); test_assert(ecs_has(world, ids[2], Position)); ecs_frame_end(world); test_assert(ecs_has(world, ids[0], Position)); test_assert(ecs_has(world, ids[1], Position)); test_assert(ecs_has(world, ids[2], Position)); ecs_fini(world); } void Commands_defer_add(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, 0); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_defer_begin(world); ecs_add(world, e, Position); test_assert(!ecs_has(world, e, Position)); ecs_defer_end(world); test_assert(!ecs_has(world, e, Position)); ecs_defer_end(world); test_assert(ecs_has(world, e, Position)); ecs_frame_end(world); test_assert(ecs_has(world, e, Position)); ecs_fini(world); } void Commands_defer_add_two(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e = ecs_new(world, 0); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_defer_begin(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); test_assert(!ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); ecs_defer_end(world); test_assert(!ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); ecs_defer_end(world); test_assert(ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Velocity)); ecs_frame_end(world); test_assert(ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Velocity)); ecs_fini(world); } void Commands_defer_remove(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, Position); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_defer_begin(world); ecs_remove(world, e, Position); test_assert(ecs_has(world, e, Position)); ecs_defer_end(world); test_assert(ecs_has(world, e, Position)); ecs_defer_end(world); test_assert(!ecs_has(world, e, Position)); ecs_frame_end(world); test_assert(!ecs_has(world, e, Position)); ecs_fini(world); } void Commands_defer_remove_two(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e = ecs_new_id(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_defer_begin(world); ecs_remove(world, e, Position); ecs_remove(world, e, Velocity); test_assert(ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Velocity)); ecs_defer_end(world); test_assert(ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Velocity)); ecs_defer_end(world); test_assert(!ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); ecs_frame_end(world); test_assert(!ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); ecs_fini(world); } void Commands_defer_set(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, Position); ecs_set(world, e, Position, {1, 2}); const Position *p = ecs_get(world, e, Position); test_assert(p != NULL); test_int(p->x, 1); test_int(p->y, 2); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_defer_begin(world); /* set will not be deferred if the component exists */ ecs_set(world, e, Position, {3, 4}); test_assert(ecs_has(world, e, Position)); p = ecs_get(world, e, Position); test_assert(p != NULL); test_int(p->x, 3); test_int(p->y, 4); ecs_defer_end(world); test_assert(ecs_has(world, e, Position)); p = ecs_get(world, e, Position); test_assert(p != NULL); test_int(p->x, 3); test_int(p->y, 4); ecs_defer_end(world); test_assert(ecs_has(world, e, Position)); p = ecs_get(world, e, Position); test_assert(p != NULL); test_int(p->x, 3); test_int(p->y, 4); ecs_frame_end(world); test_assert(ecs_has(world, e, Position)); p = ecs_get(world, e, Position); test_assert(p != NULL); test_int(p->x, 3); test_int(p->y, 4); ecs_fini(world); } void Commands_defer_delete(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, Position); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_defer_begin(world); ecs_delete(world, e); test_assert(ecs_is_alive(world, e)); ecs_defer_end(world); test_assert(ecs_is_alive(world, e)); ecs_defer_end(world); test_assert(!ecs_is_alive(world, e)); ecs_frame_end(world); test_assert(!ecs_is_alive(world, e)); ecs_fini(world); } void Commands_defer_twice(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e = ecs_new(world, Position); ecs_defer_begin(world); ecs_defer_begin(world); ecs_set(world, e, Velocity, {1, 2}); ecs_defer_end(world); test_assert(!ecs_has(world, e, Velocity)); ecs_defer_end(world); test_assert(ecs_has(world, e, Velocity)); const Velocity *v = ecs_get(world, e, Velocity); test_assert(v != NULL); test_int(v->x, 1); test_int(v->y, 2); ecs_fini(world); } void Commands_defer_twice_in_progress(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e = ecs_new(world, Position); ecs_frame_begin(world, 0); ecs_readonly_begin(world); ecs_world_t *stage = ecs_get_stage(world, 0); ecs_defer_begin(stage); ecs_defer_begin(stage); ecs_set(stage, e, Velocity, {1, 2}); ecs_defer_end(stage); test_assert(!ecs_has(stage, e, Velocity)); ecs_defer_end(stage); test_assert(!ecs_has(stage, e, Velocity)); ecs_readonly_end(world); test_assert(ecs_has(world, e, Velocity)); ecs_frame_end(world); test_assert(ecs_has(world, e, Velocity)); const Velocity *v = ecs_get(world, e, Velocity); test_assert(v != NULL); test_int(v->x, 1); test_int(v->y, 2); ecs_fini(world); } static void AddVelocity(ecs_iter_t *it) { ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); ecs_defer_begin(it->world); int i; for (i = 0; i < it->count; i ++) { ecs_add(it->world, it->entities[i], Velocity); } ecs_defer_end(it->world); } void Commands_run_w_defer(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_SYSTEM(world, AddVelocity, 0, Position, [in] Velocity()); ecs_entity_t e = ecs_new(world, Position); ecs_run(world, AddVelocity, 0, NULL); test_assert(ecs_has(world, e, Velocity)); ecs_fini(world); } void Commands_system_in_progress_w_defer(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_SYSTEM(world, AddVelocity, EcsOnUpdate, Position, [in] Velocity()); ecs_entity_t e = ecs_new(world, Position); ecs_progress(world, 0); test_assert(ecs_has(world, e, Velocity)); ecs_fini(world); } static bool on_set_invoked = 0; static void OnSetTestInvoked(ecs_iter_t *it) { on_set_invoked = 1; } void Commands_defer_get_mut_no_modify(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_OBSERVER(world, OnSetTestInvoked, EcsOnSet, Velocity); ecs_entity_t e = ecs_new(world, Position); ecs_defer_begin(world); Velocity *v = ecs_get_mut(world, e, Velocity); v->x = 1; v->y = 2; test_assert(!on_set_invoked); ecs_defer_end(world); test_assert(!on_set_invoked); test_assert(ecs_has(world, e, Velocity)); const Velocity *vptr = ecs_get(world, e, Velocity); test_int(vptr->x, 1); test_int(vptr->y, 2); ecs_fini(world); } void Commands_defer_get_mut_w_modify(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_OBSERVER(world, OnSetTestInvoked, EcsOnSet, Velocity); ecs_entity_t e = ecs_new(world, Position); ecs_defer_begin(world); Velocity *v = ecs_get_mut(world, e, Velocity); v->x = 1; v->y = 2; test_assert(!on_set_invoked); ecs_modified(world, e, Velocity); test_assert(!on_set_invoked); ecs_defer_end(world); test_assert(on_set_invoked); test_assert(ecs_has(world, e, Velocity)); const Velocity *vptr = ecs_get(world, e, Velocity); test_int(vptr->x, 1); test_int(vptr->y, 2); ecs_fini(world); } void Commands_defer_modify(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Velocity); ECS_OBSERVER(world, OnSetTestInvoked, EcsOnSet, Velocity); ecs_entity_t e = ecs_new(world, Velocity); ecs_defer_begin(world); ecs_modified(world, e, Velocity); test_assert(!on_set_invoked); ecs_defer_end(world); test_assert(on_set_invoked); ecs_fini(world); } void Commands_defer_set_pair(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e = ecs_new(world, 0); ecs_defer_begin(world); ecs_set_pair(world, e, Velocity, ecs_id(Position), {1, 2}); ecs_defer_end(world); test_assert(ecs_has_pair(world, e, ecs_id(Velocity), ecs_id(Position))); ecs_fini(world); } void Commands_defer_clear(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, Position); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_defer_begin(world); ecs_clear(world, e); test_assert(ecs_has(world, e, Position)); ecs_defer_end(world); test_assert(ecs_has(world, e, Position)); ecs_defer_end(world); test_assert(!ecs_has(world, e, Position)); ecs_frame_end(world); test_assert(!ecs_has(world, e, Position)); ecs_fini(world); } void Commands_defer_add_after_delete(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e = ecs_new(world, Position); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_delete(world, e); ecs_add(world, e, Velocity); test_assert(ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); test_assert(ecs_is_alive(world, e)); ecs_defer_end(world); test_assert(!ecs_is_alive(world, e)); ecs_frame_end(world); test_assert(!ecs_is_alive(world, e)); test_int(ecs_count(world, Position), 0); test_int(ecs_count(world, Velocity), 0); ecs_fini(world); } void Commands_defer_set_after_delete(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e = ecs_new(world, Position); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_delete(world, e); ecs_set(world, e, Velocity, {1, 2}); test_assert(ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); test_assert(ecs_is_alive(world, e)); ecs_defer_end(world); test_assert(!ecs_is_alive(world, e)); ecs_frame_end(world); test_assert(!ecs_is_alive(world, e)); test_int(ecs_count(world, Position), 0); test_int(ecs_count(world, Velocity), 0); ecs_fini(world); } void Commands_defer_get_mut_after_delete(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e = ecs_new(world, Position); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_delete(world, e); Velocity *v = ecs_get_mut(world, e, Velocity); v->x = 1; v->y = 2; test_assert(ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); test_assert(ecs_is_alive(world, e)); ecs_defer_end(world); test_assert(!ecs_is_alive(world, e)); ecs_frame_end(world); test_assert(!ecs_is_alive(world, e)); test_int(ecs_count(world, Position), 0); test_int(ecs_count(world, Velocity), 0); ecs_fini(world); } void Commands_defer_get_mut_after_delete_2nd_to_last(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e = ecs_new(world, Position); /* Create 2nd position. This will cause deletion of the entity in the sparse * set to take a different path since it's not the last. */ ecs_new(world, Position); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_delete(world, e); Velocity *v = ecs_get_mut(world, e, Velocity); v->x = 1; v->y = 2; test_assert(ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); test_assert(ecs_is_alive(world, e)); ecs_defer_end(world); test_assert(!ecs_is_alive(world, e)); ecs_frame_end(world); test_assert(!ecs_is_alive(world, e)); test_int(ecs_count(world, Position), 1); test_int(ecs_count(world, Velocity), 0); ecs_fini(world); } void Commands_defer_add_child_to_deleted_parent(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t parent = ecs_new(world, 0); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); ecs_add(world, child, Velocity); ecs_delete(world, parent); ecs_defer_end(world); ecs_frame_end(world); test_assert(!ecs_is_alive(world, parent)); test_assert(!ecs_is_alive(world, child)); ecs_fini(world); } void Commands_recreate_deleted_entity_while_deferred(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e_old = ecs_new(world, 0); test_assert(e_old != 0); ecs_delete(world, e_old); test_assert(!ecs_is_alive(world, e_old)); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_defer_begin(world); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_assert(e != e_old); test_assert((e & ECS_ENTITY_MASK) == (e_old & ECS_ENTITY_MASK)); test_assert(!ecs_has(world, e, Position)); ecs_defer_end(world); test_assert(!ecs_has(world, e, Position)); ecs_defer_end(world); test_assert(ecs_has(world, e, Position)); ecs_frame_end(world); test_assert(ecs_has(world, e, Position)); ecs_fini(world); } void Commands_defer_add_to_recycled_id(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t id = ecs_new_id(world); test_assert(id != 0); ecs_delete(world, id); ecs_entity_t id_2 = ecs_new_id(world); test_assert(id != id_2); test_assert((int32_t)id == (int32_t)id_2); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_entity_t child = ecs_new_w_id(world, id_2); ecs_add(world, child, Velocity); ecs_defer_end(world); ecs_frame_end(world); test_assert(!ecs_is_alive(world, id)); test_assert(ecs_is_alive(world, id_2)); test_assert(ecs_is_alive(world, child)); test_assert(ecs_has_id(world, child, id_2)); ecs_fini(world); } void Commands_defer_add_to_recycled_id_w_role(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t id = ecs_new_id(world); test_assert(id != 0); ecs_delete(world, id); ecs_entity_t id_2 = ecs_new_id(world); test_assert(id != id_2); test_assert((int32_t)id == (int32_t)id_2); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_entity_t child = ecs_new_w_id(world, ECS_TOGGLE | id_2); ecs_add(world, child, Velocity); ecs_defer_end(world); ecs_frame_end(world); test_assert(!ecs_is_alive(world, id)); test_assert(ecs_is_alive(world, id_2)); test_assert(ecs_is_alive(world, child)); test_assert(ecs_has_id(world, child, ECS_TOGGLE | id_2)); ecs_fini(world); } void Commands_defer_add_to_recycled_relation(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t target = ecs_new_id(world); test_assert(target != 0); ecs_entity_t rel = ecs_new_id(world); test_assert(rel != 0); ecs_delete(world, rel); ecs_entity_t rel_2 = ecs_new_id(world); test_assert(rel != rel_2); test_assert((int32_t)rel == (int32_t)rel_2); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_entity_t child = ecs_new_w_pair(world, rel_2, target); ecs_add(world, child, Velocity); ecs_defer_end(world); ecs_frame_end(world); test_assert(ecs_is_alive(world, target)); test_assert(!ecs_is_alive(world, rel)); test_assert(ecs_is_alive(world, rel_2)); test_assert(ecs_is_alive(world, child)); test_assert(ecs_has_pair(world, child, rel_2, target)); ecs_fini(world); } void Commands_defer_add_to_recycled_object(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_TAG(world, Rel); ecs_entity_t target = ecs_new(world, 0); test_assert(target != 0); ecs_delete(world, target); ecs_entity_t object_2 = ecs_new_id(world); test_assert(target != object_2); test_assert((int32_t)target == (int32_t)object_2); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_entity_t child = ecs_new_w_pair(world, Rel, object_2); ecs_add(world, child, Velocity); ecs_defer_end(world); ecs_frame_end(world); test_assert(!ecs_is_alive(world, target)); test_assert(ecs_is_alive(world, object_2)); test_assert(ecs_is_alive(world, child)); test_assert(ecs_has_pair(world, child, Rel, object_2)); ecs_fini(world); } void Commands_defer_add_to_recycled_object_childof(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t parent = ecs_new(world, 0); test_assert(parent != 0); ecs_delete(world, parent); ecs_entity_t parent_2 = ecs_new_id(world); test_assert(parent != parent_2); test_assert((int32_t)parent == (int32_t)parent_2); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent_2); ecs_add(world, child, Velocity); ecs_defer_end(world); ecs_frame_end(world); test_assert(!ecs_is_alive(world, parent)); test_assert(ecs_is_alive(world, parent_2)); test_assert(ecs_is_alive(world, child)); test_assert(ecs_has_pair(world, child, EcsChildOf, parent_2)); ecs_fini(world); } void Commands_defer_add_to_deleted_id(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t id = ecs_new_id(world); test_assert(id != 0); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_delete(world, id); ecs_entity_t child = ecs_new_w_id(world, id); ecs_add(world, child, Velocity); ecs_defer_end(world); ecs_frame_end(world); test_assert(!ecs_is_alive(world, id)); test_assert(ecs_is_alive(world, child)); test_assert(!ecs_has_id(world, child, id)); ecs_fini(world); } void Commands_defer_add_to_deleted_id_w_role(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t id = ecs_new_id(world); test_assert(id != 0); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_delete(world, id); ecs_entity_t child = ecs_new_w_id(world, ECS_TOGGLE | id); ecs_add(world, child, Velocity); ecs_defer_end(world); ecs_frame_end(world); test_assert(!ecs_is_alive(world, id)); test_assert(ecs_is_alive(world, child)); test_assert(!ecs_has_id(world, child, ECS_TOGGLE | id)); ecs_fini(world); } void Commands_defer_add_to_deleted_relation(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t target = ecs_new_id(world); test_assert(target != 0); ecs_entity_t rel = ecs_new_id(world); test_assert(rel != 0); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_delete(world, rel); ecs_entity_t child = ecs_new_w_pair(world, rel, target); ecs_add(world, child, Velocity); ecs_defer_end(world); ecs_frame_end(world); test_assert(ecs_is_alive(world, target)); test_assert(!ecs_is_alive(world, rel)); test_assert(ecs_is_alive(world, child)); test_assert(!ecs_has_pair(world, child, rel, target)); ecs_fini(world); } void Commands_defer_add_to_deleted_object(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_TAG(world, Rel); ecs_entity_t target = ecs_new(world, 0); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_delete(world, target); ecs_entity_t child = ecs_new_w_pair(world, Rel, target); ecs_add(world, child, Velocity); ecs_defer_end(world); ecs_frame_end(world); test_assert(!ecs_is_alive(world, target)); test_assert(ecs_is_alive(world, child)); test_assert(!ecs_has_pair(world, child, Rel, target)); ecs_fini(world); } void Commands_defer_add_to_deleted_object_childof(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t parent = ecs_new(world, 0); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_delete(world, parent); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); ecs_add(world, child, Velocity); ecs_defer_end(world); ecs_frame_end(world); test_assert(!ecs_is_alive(world, parent)); test_assert(!ecs_is_alive(world, child)); ecs_fini(world); } void Commands_defer_delete_added_id(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t id = ecs_new_id(world); test_assert(id != 0); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_entity_t child = ecs_new_w_id(world, id); ecs_add(world, child, Velocity); ecs_delete(world, id); ecs_defer_end(world); ecs_frame_end(world); test_assert(!ecs_is_alive(world, id)); test_assert(ecs_is_alive(world, child)); test_assert(!ecs_has_id(world, child, id)); ecs_fini(world); } void Commands_defer_delete_added_id_w_role(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t id = ecs_new_id(world); test_assert(id != 0); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_entity_t child = ecs_new_w_id(world, ECS_TOGGLE | id); ecs_add(world, child, Velocity); ecs_delete(world, id); ecs_defer_end(world); ecs_frame_end(world); test_assert(!ecs_is_alive(world, id)); test_assert(ecs_is_alive(world, child)); test_assert(!ecs_has_id(world, child, ECS_TOGGLE | id)); ecs_fini(world); } void Commands_defer_delete_added_relation(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t target = ecs_new_id(world); test_assert(target != 0); ecs_entity_t rel = ecs_new_id(world); test_assert(rel != 0); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_entity_t child = ecs_new_w_pair(world, rel, target); ecs_add(world, child, Velocity); ecs_delete(world, rel); ecs_defer_end(world); ecs_frame_end(world); test_assert(ecs_is_alive(world, target)); test_assert(!ecs_is_alive(world, rel)); test_assert(ecs_is_alive(world, child)); test_assert(!ecs_has_pair(world, child, rel, target)); ecs_fini(world); } void Commands_defer_delete_added_object(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_TAG(world, Rel); ecs_entity_t target = ecs_new(world, 0); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_entity_t child = ecs_new_w_pair(world, Rel, target); ecs_add(world, child, Velocity); ecs_delete(world, target); ecs_defer_end(world); ecs_frame_end(world); test_assert(!ecs_is_alive(world, target)); test_assert(ecs_is_alive(world, child)); test_assert(!ecs_has_pair(world, child, Rel, target)); ecs_fini(world); } void Commands_defer_delete_added_object_childof(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t parent = ecs_new(world, 0); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); ecs_add(world, child, Velocity); ecs_delete(world, parent); ecs_defer_end(world); ecs_frame_end(world); test_assert(!ecs_is_alive(world, parent)); test_assert(!ecs_is_alive(world, child)); ecs_fini(world); } void Commands_discard_add(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, 0); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_defer_begin(world); ecs_delete(world, e); test_assert(ecs_is_alive(world, e)); ecs_add(world, e, Position); test_assert(ecs_is_alive(world, e)); test_assert(!ecs_has(world, e, Position)); ecs_defer_end(world); test_assert(ecs_is_alive(world, e)); test_assert(!ecs_has(world, e, Position)); ecs_defer_end(world); test_assert(!ecs_is_alive(world, e)); ecs_frame_end(world); test_assert(!ecs_is_alive(world, e)); ecs_fini(world); } void Commands_discard_remove(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, Position); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_defer_begin(world); ecs_delete(world, e); test_assert(ecs_is_alive(world, e)); ecs_remove(world, e, Position); test_assert(ecs_is_alive(world, e)); test_assert(ecs_has(world, e, Position)); ecs_defer_end(world); test_assert(ecs_is_alive(world, e)); test_assert(ecs_has(world, e, Position)); ecs_defer_end(world); test_assert(!ecs_is_alive(world, e)); ecs_frame_end(world); test_assert(!ecs_is_alive(world, e)); ecs_fini(world); } void Commands_discard_add_two(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e = ecs_new_id(world); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_defer_begin(world); ecs_delete(world, e); test_assert(ecs_is_alive(world, e)); ecs_add(world, e, Position); ecs_add(world, e, Velocity); test_assert(!ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); test_assert(ecs_is_alive(world, e)); ecs_defer_end(world); test_assert(!ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); test_assert(ecs_is_alive(world, e)); ecs_defer_end(world); test_assert(!ecs_is_alive(world, e)); ecs_frame_end(world); test_assert(!ecs_is_alive(world, e)); ecs_fini(world); } void Commands_discard_remove_two(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e = ecs_new_id(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_defer_begin(world); ecs_delete(world, e); test_assert(ecs_is_alive(world, e)); ecs_remove(world, e, Position); ecs_remove(world, e, Velocity); test_assert(ecs_is_alive(world, e)); test_assert(ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Velocity)); ecs_defer_end(world); test_assert(ecs_is_alive(world, e)); test_assert(ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Velocity)); ecs_defer_end(world); test_assert(!ecs_is_alive(world, e)); ecs_frame_end(world); test_assert(!ecs_is_alive(world, e)); ecs_fini(world); } void Commands_discard_child(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_defer_begin(world); ecs_entity_t e = ecs_new(world, 0); test_assert(e != 0); test_assert(!ecs_has(world, e, Position)); ecs_delete(world, e); test_assert(ecs_is_alive(world, e)); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, e); test_assert(child != 0); test_assert(!ecs_has_pair(world, child, EcsChildOf, e)); ecs_defer_end(world); test_assert(ecs_is_alive(world, e)); test_assert(ecs_is_alive(world, child)); test_assert(!ecs_has(world, e, Position)); test_assert(!ecs_has_pair(world, child, EcsChildOf, e)); ecs_defer_end(world); test_assert(!ecs_is_alive(world, e)); test_assert(!ecs_is_alive(world, child)); ecs_frame_end(world); test_assert(!ecs_is_alive(world, e)); test_assert(!ecs_is_alive(world, child)); ecs_fini(world); } void Commands_discard_child_w_add(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_frame_begin(world, 1); ecs_defer_begin(world); ecs_defer_begin(world); ecs_entity_t e = ecs_new(world, 0); test_assert(e != 0); test_assert(!ecs_has(world, e, Position)); ecs_delete(world, e); test_assert(ecs_is_alive(world, e)); ecs_entity_t child = ecs_new(world, 0); test_assert(child != 0); ecs_add_pair(world, child, EcsChildOf, e); test_assert(!ecs_has_pair(world, child, EcsChildOf, e)); ecs_defer_end(world); test_assert(ecs_is_alive(world, e)); test_assert(ecs_is_alive(world, child)); ecs_defer_end(world); test_assert(!ecs_is_alive(world, e)); test_assert(!ecs_is_alive(world, child)); ecs_frame_end(world); test_assert(!ecs_is_alive(world, e)); test_assert(!ecs_is_alive(world, child)); ecs_fini(world); } void Commands_defer_return_value(void) { ecs_world_t *world = ecs_mini(); test_bool(ecs_defer_begin(world), true); test_bool(ecs_defer_begin(world), false); test_bool(ecs_defer_end(world), false); test_bool(ecs_defer_end(world), true); ecs_fini(world); } void Commands_defer_get_mut_pair(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_TAG(world, Pair); ecs_entity_t e = ecs_new_id(world); ecs_defer_begin(world); Position *p = ecs_get_mut_pair(world, e, Position, Pair); test_assert(p != NULL); p->x = 10; p->y = 20; ecs_defer_end(world); const Position *pc = ecs_get_pair(world, e, Position, Pair); test_assert(pc != NULL); test_int(pc->x, 10); test_int(pc->y, 20); ecs_fini(world); } void Commands_async_stage_add(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new_id(world); ecs_world_t *async = ecs_async_stage_new(world); ecs_add(async, e, Position); test_assert(!ecs_has(world, e, Position)); ecs_merge(async); test_assert(ecs_has(world, e, Position)); ecs_async_stage_free(async); ecs_fini(world); } void Commands_async_stage_add_twice(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e = ecs_new_id(world); ecs_world_t *async = ecs_async_stage_new(world); ecs_add(async, e, Position); test_assert(!ecs_has(world, e, Position)); ecs_merge(async); test_assert(ecs_has(world, e, Position)); ecs_add(async, e, Velocity); test_assert(!ecs_has(world, e, Velocity)); ecs_merge(async); test_assert(ecs_has(world, e, Velocity)); ecs_async_stage_free(async); ecs_fini(world); } void Commands_async_stage_remove(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, Position); ecs_world_t *async = ecs_async_stage_new(world); ecs_remove(async, e, Position); test_assert(ecs_has(world, e, Position)); ecs_merge(async); test_assert(!ecs_has(world, e, Position)); ecs_async_stage_free(async); ecs_fini(world); } void Commands_async_stage_clear(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e = ecs_new(world, Position); ecs_add(world, e, Velocity); ecs_world_t *async = ecs_async_stage_new(world); ecs_clear(async, e); test_assert(ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Velocity)); ecs_merge(async); test_assert(!ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); ecs_async_stage_free(async); ecs_fini(world); } void Commands_async_stage_delete(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e = ecs_new(world, Position); ecs_add(world, e, Velocity); ecs_world_t *async = ecs_async_stage_new(world); ecs_delete(async, e); test_assert(ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Velocity)); test_assert(ecs_is_alive(world, e)); ecs_merge(async); test_assert(!ecs_is_alive(world, e)); ecs_async_stage_free(async); ecs_fini(world); } void Commands_async_stage_new(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_world_t *async = ecs_async_stage_new(world); ecs_entity_t e = ecs_new(async, 0); ecs_add(async, e, Position); ecs_add(async, e, Velocity); test_assert(!ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); ecs_merge(async); test_assert(ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Velocity)); test_assert(ecs_is_alive(world, e)); ecs_async_stage_free(async); ecs_fini(world); } void Commands_async_stage_no_get(void) { install_test_abort(); ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, Position); ecs_world_t *async = ecs_async_stage_new(world); test_expect_abort(); ecs_get(async, e, Position); } void Commands_async_stage_readonly(void) { ecs_world_t *world = ecs_mini(); ecs_world_t *async = ecs_async_stage_new(world); test_assert(!ecs_stage_is_readonly(async)); ecs_async_stage_free(async); ecs_fini(world); } void Commands_async_stage_is_async(void) { ecs_world_t *world = ecs_mini(); ecs_world_t *async = ecs_async_stage_new(world); test_assert(ecs_stage_is_async(async)); ecs_async_stage_free(async); ecs_fini(world); } static void RegisterComponent(ecs_iter_t *it) { ecs_entity_t ecs_id(Position) = ecs_component_init(it->world, &(ecs_component_desc_t){ .entity = ecs_entity(it->world, {.name = "Position"}), .type.size = ECS_SIZEOF(Position), .type.alignment = ECS_ALIGNOF(Position) }); test_assert(ecs_id(Position) != 0); } void Commands_register_component_while_in_progress(void) { ecs_world_t *world = ecs_init(); ECS_SYSTEM(world, RegisterComponent, EcsOnUpdate, 0); ecs_progress(world, 0); ecs_entity_t ecs_id(Position) = ecs_lookup(world, "Position"); test_assert(ecs_id(Position) != 0); test_assert(ecs_has(world, ecs_id(Position), EcsComponent)); ecs_entity_t e = ecs_new_id(world); ecs_set(world, e, Position, {10, 20}); test_assert(ecs_has_id(world, e, ecs_id(Position))); const Position *p = ecs_get(world, e, Position); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); ecs_fini(world); } void Commands_register_component_while_staged(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); ecs_entity_t e = ecs_new_id(world); ecs_entity_t canary = ecs_new_id(world); ecs_readonly_begin(world); ecs_world_t *stage = ecs_get_stage(world, 0); ecs_add_id(stage, canary, Tag); ecs_entity_t ecs_id(Position) = ecs_component_init(stage, &(ecs_component_desc_t){ .entity = ecs_entity(stage, {.name = "Position"}), .type.size = ECS_SIZEOF(Position), .type.alignment = ECS_ALIGNOF(Position) }); test_assert(ecs_id(Position) != 0); ecs_set(stage, e, Position, {10, 20}); test_assert(!ecs_has_id(world, e, ecs_id(Position))); test_assert(!ecs_has_id(world, canary, Tag)); ecs_readonly_end(world); test_assert(ecs_has_id(world, e, ecs_id(Position))); const Position *p = ecs_get(world, e, Position); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); ecs_fini(world); } void Commands_register_component_while_deferred(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); ecs_entity_t e = ecs_new_id(world); ecs_entity_t canary = ecs_new_id(world); ecs_defer_begin(world); ecs_add_id(world, canary, Tag); ecs_entity_t ecs_id(Position) = ecs_component_init(world, &(ecs_component_desc_t){ .entity = ecs_entity(world, {.name = "Position"}), .type.size = ECS_SIZEOF(Position), .type.alignment = ECS_ALIGNOF(Position) }); test_assert(ecs_id(Position) != 0); ecs_set(world, e, Position, {10, 20}); test_assert(!ecs_has_id(world, e, ecs_id(Position))); test_assert(!ecs_has_id(world, canary, Tag)); ecs_defer_end(world); ecs_fini(world); } void Commands_defer_enable(void) { ecs_world_t *world = ecs_mini(); ecs_entity_t e = ecs_new_id(world); ecs_defer_begin(world); ecs_enable(world, e, false); test_assert(!ecs_has_id(world, e, EcsDisabled)); ecs_defer_end(world); test_assert(ecs_has_id(world, e, EcsDisabled)); ecs_fini(world); } void Commands_defer_disable(void) { ecs_world_t *world = ecs_mini(); ecs_entity_t e = ecs_new_w_id(world, EcsDisabled); ecs_defer_begin(world); ecs_enable(world, e, true); test_assert(ecs_has_id(world, e, EcsDisabled)); ecs_defer_end(world); test_assert(!ecs_has_id(world, e, EcsDisabled)); ecs_fini(world); } void Commands_defer_delete_with(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); ecs_entity_t e_1 = ecs_new(world, TagA); ecs_entity_t e_2 = ecs_new(world, TagA); ecs_entity_t e_3 = ecs_new(world, TagA); ecs_add(world, e_1, TagB); ecs_defer_begin(world); ecs_delete_with(world, TagA); test_assert(ecs_is_alive(world, e_1)); test_assert(ecs_is_alive(world, e_2)); test_assert(ecs_is_alive(world, e_3)); ecs_defer_end(world); test_assert(!ecs_is_alive(world, e_1)); test_assert(!ecs_is_alive(world, e_2)); test_assert(!ecs_is_alive(world, e_3)); ecs_fini(world); } void Commands_defer_remove_all(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); ecs_entity_t e_1 = ecs_new(world, TagA); ecs_entity_t e_2 = ecs_new(world, TagA); ecs_entity_t e_3 = ecs_new(world, TagA); ecs_add(world, e_1, TagB); ecs_defer_begin(world); ecs_remove_all(world, TagA); test_assert(ecs_is_alive(world, e_1)); test_assert(ecs_is_alive(world, e_2)); test_assert(ecs_is_alive(world, e_3)); ecs_defer_end(world); test_assert(ecs_is_alive(world, e_1)); test_assert(ecs_is_alive(world, e_2)); test_assert(ecs_is_alive(world, e_3)); test_assert(!ecs_has_id(world, e_1, TagA)); test_assert(!ecs_has_id(world, e_2, TagA)); test_assert(!ecs_has_id(world, e_3, TagA)); test_assert(ecs_has_id(world, e_1, TagB)); ecs_fini(world); } void Commands_deferred_modified_after_remove(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(Position), .events = { EcsOnSet }, .callback = OnSetTestInvoked }); ecs_entity_t e = ecs_new(world, Position); ecs_modified(world, e, Position); test_int(on_set_invoked, 1); on_set_invoked = 0; ecs_defer_begin(world); ecs_remove(world, e, Position); test_assert(ecs_has(world, e, Position)); ecs_modified(world, e, Position); ecs_defer_end(world); test_int(on_set_invoked, 0); ecs_fini(world); } typedef struct { int value; } Counter; ECS_COMPONENT_DECLARE(Counter); static int update_counter_invoked = 0; static int remove_counter_invoked = 0; static void update_counter(ecs_iter_t *it) { ecs_world_t *world = it->world; for (int i = 0; i < it->count; i ++) { ecs_entity_t e = it->entities[i]; ecs_entity_t parent = ecs_get_target(world, e, EcsChildOf, 0); test_assert(parent != 0); Counter *ptr = ecs_get_mut(world, parent, Counter); test_assert(ptr != NULL); if (it->event == EcsOnAdd) { ptr->value ++; } else if (it->event == EcsOnRemove) { ptr->value --; } update_counter_invoked ++; } } static void remove_counter(ecs_iter_t *it) { Counter *ptr = ecs_field(it, Counter, 1); for (int i = 0; i < it->count; i ++) { test_int(ptr[i].value, 0); remove_counter_invoked ++; } } void Commands_merge_cleanup_ops_before_delete(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT_DEFINE(world, Counter); ECS_TAG(world, Tag); ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = Tag, .events = {EcsOnAdd, EcsOnRemove}, .callback = update_counter }); ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = ecs_id(Counter), .src.flags = EcsSelf }, .events = {EcsOnRemove}, .callback = remove_counter }); ecs_entity_t parent = ecs_new_id(world); ecs_set(world, parent, Counter, {0}); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); ecs_add(world, child, Tag); test_int(update_counter_invoked, 1); const Counter *ptr = ecs_get(world, parent, Counter); test_assert(ptr != NULL); test_assert(ptr->value == 1); ecs_delete(world, parent); test_int(update_counter_invoked, 2); test_int(remove_counter_invoked, 1); ecs_fini(world); } void Commands_merge_nested_cleanup_ops_before_delete(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT_DEFINE(world, Counter); ECS_TAG(world, Tag); ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = Tag, .src.flags = EcsSelf }, .events = {EcsOnAdd, EcsOnRemove}, .callback = update_counter }); ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = ecs_id(Counter), .src.flags = EcsSelf }, .events = {EcsOnRemove}, .callback = remove_counter }); ecs_entity_t parent = ecs_new_id(world); ecs_set(world, parent, Counter, {0}); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); ecs_add(world, child, Tag); test_int(update_counter_invoked, 1); ecs_set(world, child, Counter, {0}); ecs_entity_t grand_child = ecs_new_w_pair(world, EcsChildOf, child); ecs_add(world, grand_child, Tag); test_int(update_counter_invoked, 2); const Counter *ptr = ecs_get(world, parent, Counter); test_assert(ptr != NULL); test_assert(ptr->value == 1); ptr = ecs_get(world, child, Counter); test_assert(ptr != NULL); test_assert(ptr->value == 1); ecs_delete(world, parent); test_int(update_counter_invoked, 4); test_int(remove_counter_invoked, 2); ecs_fini(world); } void Commands_defer_suspend_resume(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); ecs_entity_t e = ecs_new_id(world); ecs_defer_begin(world); ecs_add(world, e, TagA); test_assert(!ecs_has(world, e, TagA)); ecs_defer_suspend(world); ecs_add(world, e, TagB); test_assert(!ecs_has(world, e, TagA)); test_assert(ecs_has(world, e, TagB)); ecs_defer_resume(world); test_assert(!ecs_has(world, e, TagA)); test_assert(ecs_has(world, e, TagB)); ecs_defer_end(world); test_assert(ecs_has(world, e, TagA)); test_assert(ecs_has(world, e, TagB)); ecs_fini(world); } static void System(ecs_iter_t *it) { probe_system_w_ctx(it, it->ctx); } static int system_2_invoked = 0; static void System2(ecs_iter_t *it) { probe_system_w_ctx(it, it->ctx); system_2_invoked ++; } void Commands_create_observer_while_deferred(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ecs_defer_begin(world); Probe ctx = {0}; ecs_entity_t observer = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms = {{ .id = TagA }}, .events = {EcsOnAdd}, .callback = System, .ctx = &ctx }); ecs_defer_end(world); test_assert(observer != 0); test_int(ctx.invoked, 0); ecs_new(world, TagA); test_int(ctx.invoked, 1); ecs_fini(world); } void Commands_create_query_while_deferred(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ecs_defer_begin(world); ecs_query_t *query = ecs_query_init(world, &(ecs_query_desc_t){ .filter.terms = {{ .id = TagA }} }); ecs_defer_end(world); test_assert(query != 0); ecs_entity_t e = ecs_new(world, TagA); ecs_iter_t it = ecs_query_iter(world, query); test_bool(true, ecs_query_next(&it)); test_int(1, it.count); test_uint(e, it.entities[0]); test_bool(false, ecs_query_next(&it)); ecs_fini(world); } void Commands_update_observer_while_deferred(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ecs_defer_begin(world); Probe ctx = {0}; ecs_entity_t observer = ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms = {{ .id = TagA }}, .events = {EcsOnAdd}, .callback = System, .ctx = &ctx }); ecs_defer_end(world); test_assert(observer != 0); test_int(ctx.invoked, 0); ecs_new(world, TagA); test_int(ctx.invoked, 1); test_int(system_2_invoked, 0); ecs_defer_begin(world); ecs_observer_init(world, &(ecs_observer_desc_t){ .entity = observer, .callback = System2 }); ecs_defer_end(world); ecs_new(world, TagA); test_int(ctx.invoked, 2); test_int(system_2_invoked, 1); ecs_fini(world); } typedef struct { int16_t arr[8096]; } LargeComponent; void Commands_defer_set_large_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, LargeComponent); ecs_entity_t e = ecs_new_id(world); ecs_defer_begin(world); LargeComponent *ptr = ecs_get_mut(world, e, LargeComponent); test_assert(ptr != NULL); for (int i = 0; i < 8096; i ++) { ptr->arr[i] = i; } test_assert(!ecs_has(world, e, LargeComponent)); ecs_defer_end(world); ptr = ecs_get_mut(world, e, LargeComponent); test_assert(ptr != NULL); for (int i = 0; i < 8096; i ++) { test_int(ptr->arr[i], i); } ecs_fini(world); } static ECS_COMPONENT_DECLARE(Position); static void CreatePosition(ecs_iter_t *it) { ecs_entity_t e = ecs_new_id(it->world); ecs_set(it->world, e, Position, {999, 1000}); } void Commands_defer_while_suspend_readonly(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT_DEFINE(world, Position); /* Create observer on EcsComponent which will defer a command while readonly * mode is suspended */ ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(EcsComponent), .events = { EcsOnAdd }, .callback = CreatePosition }); ecs_world_t *s = ecs_get_stage(world, 0); ecs_readonly_begin(world); /* Component creation suspends readonly mode so that they can be used right * after they have been registered */ ECS_COMPONENT(s, Velocity); ecs_entity_t e = ecs_set(s, 0, Position, {10, 20}); ecs_set(s, e, Velocity, {1, 2}); test_assert(!ecs_has(s, e, Position)); test_assert(!ecs_has(s, e, Velocity)); ecs_readonly_end(world); test_assert(ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Velocity)); const Position *p = ecs_get(world, e, Position); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); const Velocity *v = ecs_get(world, e, Velocity); test_assert(v != NULL); test_int(v->x, 1); test_int(v->y, 2); test_int(2, ecs_count(world, Position)); ecs_fini(world); } void Commands_defer_while_suspend_readonly_w_existing_commands(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT_DEFINE(world, Position); /* Create observer on EcsComponent which will defer a command while readonly * mode is suspended */ ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0].id = ecs_id(EcsComponent), .events = { EcsOnAdd }, .callback = CreatePosition }); ecs_world_t *s = ecs_get_stage(world, 0); ecs_readonly_begin(world); ecs_entity_t e1 = ecs_set(s, 0, Position, {10, 20}); test_assert(!ecs_has(s, e1, Position)); /* Component creation suspends readonly mode so that they can be used right * after they have been registered */ ECS_COMPONENT(s, Velocity); ecs_entity_t e2 = ecs_set(s, 0, Position, {20, 30}); ecs_set(s, e2, Velocity, {1, 2}); test_assert(!ecs_has(s, e2, Position)); test_assert(!ecs_has(s, e2, Velocity)); ecs_readonly_end(world); test_assert(ecs_has(world, e1, Position)); test_assert(ecs_has(world, e2, Position)); test_assert(ecs_has(world, e2, Velocity)); const Position *p = ecs_get(world, e1, Position); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); p = ecs_get(world, e2, Position); test_assert(p != NULL); test_int(p->x, 20); test_int(p->y, 30); const Velocity *v = ecs_get(world, e2, Velocity); test_assert(v != NULL); test_int(v->x, 1); test_int(v->y, 2); test_int(3, ecs_count(world, Position)); ecs_fini(world); } void Commands_defer_add_union_relationship(void) { ecs_world_t *world = ecs_mini(); ECS_ENTITY(world, Rel, Union); ECS_TAG(world, Tgt); ecs_entity_t e = ecs_new_id(world); ecs_defer_begin(world); ecs_add_pair(world, e, Rel, Tgt); test_assert(!ecs_has_pair(world, e, Rel, Tgt)); ecs_defer_end(world); test_assert(ecs_has_pair(world, e, Rel, Tgt)); ecs_fini(world); } void Commands_defer_add_existing_union_relationship(void) { ecs_world_t *world = ecs_mini(); ECS_ENTITY(world, Rel, Union); ECS_TAG(world, TgtA); ECS_TAG(world, TgtB); ecs_entity_t e = ecs_new_id(world); ecs_add_pair(world, e, Rel, TgtA); test_assert(ecs_has_pair(world, e, Rel, TgtA)); ecs_defer_begin(world); ecs_add_pair(world, e, Rel, TgtB); test_assert(ecs_has_pair(world, e, Rel, TgtA)); test_assert(!ecs_has_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)); ecs_fini(world); } void Commands_defer_add_union_relationship_2_ops(void) { ecs_world_t *world = ecs_mini(); ECS_ENTITY(world, Rel, Union); ECS_TAG(world, Tgt); ECS_TAG(world, Tag); ecs_entity_t e = ecs_new_id(world); ecs_defer_begin(world); ecs_add_pair(world, e, Rel, Tgt); ecs_add(world, e, Tag); test_assert(!ecs_has_pair(world, e, Rel, Tgt)); test_assert(!ecs_has(world, e, Tag)); ecs_defer_end(world); test_assert(ecs_has_pair(world, e, Rel, Tgt)); test_assert(ecs_has(world, e, Tag)); ecs_fini(world); } void Commands_defer_add_existing_union_relationship_2_ops(void) { ecs_world_t *world = ecs_mini(); ECS_ENTITY(world, Rel, Union); ECS_TAG(world, TgtA); ECS_TAG(world, TgtB); ECS_TAG(world, Tag); ecs_entity_t e = ecs_new_id(world); ecs_add_pair(world, e, Rel, TgtA); test_assert(ecs_has_pair(world, e, Rel, TgtA)); ecs_defer_begin(world); ecs_add_pair(world, e, Rel, TgtB); ecs_add(world, e, Tag); test_assert(ecs_has_pair(world, e, Rel, TgtA)); test_assert(!ecs_has_pair(world, e, Rel, TgtB)); test_assert(!ecs_has(world, e, Tag)); ecs_defer_end(world); test_assert(!ecs_has_pair(world, e, Rel, TgtA)); test_assert(ecs_has_pair(world, e, Rel, TgtB)); test_assert(ecs_has(world, e, Tag)); ecs_fini(world); } void Commands_defer_remove_after_set(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new_id(world); ecs_defer_begin(world); ecs_set(world, e, Position, {10, 20}); test_assert(!ecs_has(world, e, Position)); ecs_remove(world, e, Position); test_assert(!ecs_has(world, e, Position)); ecs_defer_end(world); test_assert(!ecs_has(world, e, Position)); ecs_fini(world); } static int position_observer_invoked = 0; static void PositionObserver(ecs_iter_t *it) { position_observer_invoked ++; } void Commands_defer_remove_after_set_w_observer(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_OBSERVER(world, PositionObserver, EcsOnSet, Position); ecs_entity_t e = ecs_new_id(world); ecs_defer_begin(world); ecs_set(world, e, Position, {10, 20}); test_assert(!ecs_has(world, e, Position)); ecs_remove(world, e, Position); test_assert(!ecs_has(world, e, Position)); ecs_defer_end(world); test_int(position_observer_invoked, 0); test_assert(!ecs_has(world, e, Position)); ecs_fini(world); } void Commands_defer_override_after_remove(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); ecs_set(world, inst, Position, {20, 30}); ecs_defer_begin(world); ecs_remove(world, inst, Position); ecs_add(world, inst, Position); ecs_defer_end(world); const Position *p = ecs_get(world, inst, Position); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); test_assert(p != ecs_get(world, base, Position)); ecs_fini(world); } void Commands_defer_override_after_remove_3_ops(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_TAG(world, Tag); ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); ecs_set(world, inst, Position, {20, 30}); ecs_defer_begin(world); ecs_remove(world, inst, Position); ecs_add(world, inst, Position); ecs_defer_end(world); const Position *p = ecs_get(world, inst, Position); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); test_assert(p != ecs_get(world, base, Position)); ecs_fini(world); } void Commands_flush_stage_to_deferred_world(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); ecs_entity_t e = ecs_new_id(world); ecs_world_t *async = ecs_async_stage_new(world); ecs_add(async, e, Tag); test_assert(!ecs_has(world, e, Tag)); ecs_defer_begin(world); ecs_merge(async); test_assert(!ecs_has(world, e, Tag)); ecs_defer_end(world); test_assert(ecs_has(world, e, Tag)); ecs_async_stage_free(async); ecs_fini(world); } static void AddPosition(ecs_iter_t *it) { test_int(it->count, 1); ecs_set(it->world, it->entities[0], Position, {10, 20}); } void Commands_add_in_observer_during_merge(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT_DEFINE(world, Position); ECS_TAG(world, TagA); ECS_OBSERVER(world, AddPosition, EcsOnAdd, TagA); ecs_entity_t e = ecs_new_id(world); ecs_defer_begin(world); ecs_add(world, e, TagA); test_assert(!ecs_has(world, e, TagA)); test_assert(!ecs_has(world, e, Position)); ecs_defer_end(world); test_assert(ecs_has(world, e, TagA)); test_assert(ecs_has(world, e, Position)); const Position *ptr = ecs_get(world, e, Position); test_assert(ptr != NULL); test_int(ptr->x, 10); test_int(ptr->y, 20); ecs_fini(world); } void Commands_add_in_observer_during_merge_2_commands(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT_DEFINE(world, Position); ECS_TAG(world, TagA); ECS_TAG(world, TagB); ECS_OBSERVER(world, AddPosition, EcsOnAdd, TagB); ecs_entity_t e = ecs_new_id(world); ecs_log_set_level(0); ecs_defer_begin(world); ecs_add(world, e, TagA); ecs_add(world, e, TagB); test_assert(!ecs_has(world, e, TagA)); test_assert(!ecs_has(world, e, TagB)); test_assert(!ecs_has(world, e, Position)); ecs_defer_end(world); test_assert(ecs_has(world, e, TagA)); test_assert(ecs_has(world, e, TagB)); test_assert(ecs_has(world, e, Position)); const Position *ptr = ecs_get(world, e, Position); test_assert(ptr != NULL); test_int(ptr->x, 10); test_int(ptr->y, 20); ecs_log_set_level(-1); ecs_fini(world); } static ECS_DECLARE(TagB); static ECS_DECLARE(TagC); static void AddTwoTags(ecs_iter_t *it) { test_int(it->count, 1); ecs_add(it->world, it->entities[0], TagB); ecs_add(it->world, it->entities[0], TagC); } void Commands_add_2_in_observer_while_on_remove_for_delete(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG_DEFINE(world, TagB); ECS_TAG_DEFINE(world, TagC); ECS_OBSERVER(world, AddTwoTags, EcsOnRemove, TagA); ecs_entity_t e = ecs_new(world, TagA); ecs_delete(world, e); test_assert(!ecs_is_alive(world, e)); ecs_fini(world); } void Commands_add_2_in_observer_while_on_remove_for_delete_child(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG_DEFINE(world, TagB); ECS_TAG_DEFINE(world, TagC); ECS_OBSERVER(world, AddTwoTags, EcsOnRemove, TagA); ecs_entity_t parent = ecs_new_id(world); ecs_entity_t e = ecs_new(world, TagA); ecs_add_pair(world, e, EcsChildOf, parent); ecs_delete(world, e); test_assert(!ecs_is_alive(world, e)); ecs_fini(world); } void Commands_add_2_in_observer_while_on_remove_for_delete_recycled_id(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG_DEFINE(world, TagB); ECS_TAG_DEFINE(world, TagC); ECS_OBSERVER(world, AddTwoTags, EcsOnRemove, TagA); ecs_delete(world, ecs_new_id(world)); ecs_entity_t e = ecs_new(world, TagA); ecs_delete(world, e); test_assert(!ecs_is_alive(world, e)); ecs_fini(world); } void Commands_add_2_in_observer_while_on_remove_for_deferred_delete_recycled_id(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG_DEFINE(world, TagB); ECS_TAG_DEFINE(world, TagC); ECS_OBSERVER(world, AddTwoTags, EcsOnRemove, TagA); ecs_delete(world, ecs_new_id(world)); ecs_entity_t e = ecs_new(world, TagA); ecs_defer_begin(world); ecs_delete(world, e); ecs_defer_end(world); test_assert(!ecs_is_alive(world, e)); ecs_fini(world); } void Commands_defer_add_after_clear(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_TAG(world, Tag); ecs_entity_t e = ecs_new_id(world); ecs_defer_begin(world); ecs_clear(world, e); ecs_add(world, e, Position); ecs_defer_end(world); test_assert(ecs_has(world, e, Position)); ecs_fini(world); } void Commands_defer_cmd_after_modified(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ecs_entity_t e1 = ecs_new_id(world); ecs_entity_t e2 = ecs_new_id(world); ecs_defer_begin(world); ecs_set(world, e1, Position, {10, 20}); ecs_modified(world, e2, Position); ecs_set(world, e2, Position, {20, 30}); test_assert(!ecs_has(world, e1, Position)); test_assert(!ecs_has(world, e2, Position)); ecs_defer_end(world); test_assert(ecs_has(world, e1, Position)); test_assert(ecs_has(world, e2, Position)); const Position *p = ecs_get(world, e1, Position); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); p = ecs_get(world, e2, Position); test_assert(p != NULL); test_int(p->x, 20); test_int(p->y, 30); ecs_fini(world); } void Commands_defer_remove_after_emplace_different_id(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_TAG(world, Tag); ecs_entity_t e = ecs_new_id(world); ecs_add(world, e, Tag); ecs_defer_begin(world); Position *p = ecs_emplace(world, e, Position); p->x = 10; p->y = 20; ecs_remove(world, e, Tag); test_assert(!ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Tag)); ecs_defer_end(world); test_assert(ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Tag)); const Position *ptr = ecs_get(world, e, Position); test_assert(ptr != NULL); test_int(ptr->x, 10); test_int(ptr->y, 20); ecs_fini(world); } void Commands_defer_remove_after_set_and_emplace_different_id(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_TAG(world, Tag); ecs_entity_t e = ecs_new_id(world); ecs_add(world, e, Tag); ecs_defer_begin(world); Position *p = ecs_emplace(world, e, Position); p->x = 10; p->y = 20; ecs_set(world, e, Velocity, {1, 2}); ecs_remove(world, e, Tag); test_assert(!ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); test_assert(ecs_has(world, e, Tag)); ecs_defer_end(world); test_assert(ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Velocity)); test_assert(!ecs_has(world, e, Tag)); const Position *ptr = ecs_get(world, e, Position); test_assert(ptr != NULL); test_int(ptr->x, 10); test_int(ptr->y, 20); const Velocity *v = ecs_get(world, e, Velocity); test_assert(v != NULL); test_int(v->x, 1); test_int(v->y, 2); ecs_fini(world); } void Commands_clear_after_add_to_nonempty(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); ecs_entity_t e = ecs_new(world, TagA); ecs_defer_begin(world); ecs_add(world, e, TagB); ecs_clear(world, e); test_assert(ecs_has(world, e, TagA)); test_assert(!ecs_has(world, e, TagB)); ecs_defer_end(world); test_assert(!ecs_has(world, e, TagA)); test_assert(!ecs_has(world, e, TagB)); ecs_fini(world); } void Commands_remove_after_add_to_nonempty(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); ecs_entity_t e = ecs_new(world, TagA); ecs_defer_begin(world); ecs_add(world, e, TagB); ecs_remove(world, e, TagA); ecs_remove(world, e, TagB); test_assert(ecs_has(world, e, TagA)); test_assert(!ecs_has(world, e, TagB)); ecs_defer_end(world); test_assert(!ecs_has(world, e, TagA)); test_assert(!ecs_has(world, e, TagB)); ecs_fini(world); } void Commands_register_while_deferred_with_n_stages(void) { ecs_world_t *world = ecs_mini(); ecs_set_stage_count(world, 2); ecs_defer_begin(world); ECS_COMPONENT(world, Position); ecs_defer_end(world); test_assert(ecs_id(Position)); ecs_fini(world); } static int position_velocity_observer_invoked = 0; static void PositionVelocityObserver(ecs_iter_t *it) { position_velocity_observer_invoked ++; test_int(it->count, 1); Position *p = ecs_field(it, Position, 1); Velocity *v = ecs_field(it, Velocity, 2); test_assert(p != NULL); test_assert(v != NULL); test_int(p->x, 10); test_int(p->y, 20); test_int(v->x, 1); test_int(v->y, 2); } void Commands_defer_2_sets_w_multi_observer(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_observer(world, { .filter.terms = {{ ecs_id(Position) }, { ecs_id(Velocity) }}, .callback = PositionVelocityObserver, .events = { EcsOnSet } }); ecs_entity_t e = ecs_new_id(world); ecs_defer_begin(world); ecs_set(world, e, Position, {10, 20}); ecs_set(world, e, Velocity, {1, 2}); ecs_defer_end(world); test_int(position_velocity_observer_invoked, 2); const Position *p = ecs_get(world, e, Position); test_int(p->x, 10); test_int(p->y, 20); const Velocity *v = ecs_get(world, e, Velocity); test_int(v->x, 1); test_int(v->y, 2); ecs_fini(world); } void Commands_defer_2_get_muts_w_multi_observer(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_observer(world, { .filter.terms = {{ ecs_id(Position) }, { ecs_id(Velocity) }}, .callback = PositionVelocityObserver, .events = { EcsOnSet } }); ecs_entity_t e = ecs_new_id(world); ecs_defer_begin(world); { Position *p = ecs_get_mut(world, e, Position); p->x = 10; p->y = 20; ecs_modified(world, e, Position); Velocity *v = ecs_get_mut(world, e, Velocity); v->x = 1; v->y = 2; ecs_modified(world, e, Velocity); } ecs_defer_end(world); test_int(position_velocity_observer_invoked, 2); const Position *p = ecs_get(world, e, Position); test_int(p->x, 10); test_int(p->y, 20); const Velocity *v = ecs_get(world, e, Velocity); test_int(v->x, 1); test_int(v->y, 2); ecs_fini(world); } void Commands_defer_2_get_muts_no_modified_w_multi_observer(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_observer(world, { .filter.terms = {{ ecs_id(Position) }, { ecs_id(Velocity) }}, .callback = PositionVelocityObserver, .events = { EcsOnSet } }); ecs_entity_t e = ecs_new_id(world); ecs_defer_begin(world); { Position *p = ecs_get_mut(world, e, Position); p->x = 10; p->y = 20; Velocity *v = ecs_get_mut(world, e, Velocity); v->x = 1; v->y = 2; } ecs_defer_end(world); test_int(position_velocity_observer_invoked, 0); const Position *p = ecs_get(world, e, Position); test_int(p->x, 10); test_int(p->y, 20); const Velocity *v = ecs_get(world, e, Velocity); test_int(v->x, 1); test_int(v->y, 2); ecs_fini(world); } void Commands_exists_remove_set(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, 0); ecs_set(world, e, Position, {1, 2}); ecs_defer_begin(world); ecs_remove(world, e, Position); ecs_set(world, e, Position, {5, 6}); ecs_defer_end(world); const Position *p = ecs_get(world, e, Position); test_assert(p != NULL); test_int(p->x, 5); test_int(p->y, 6); ecs_fini(world); } void Commands_absent_remove_set(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, 0); ecs_defer_begin(world); ecs_remove(world, e, Position); ecs_set(world, e, Position, {5, 6}); ecs_defer_end(world); const Position *p = ecs_get(world, e, Position); test_assert(p != NULL); test_int(p->x, 5); test_int(p->y, 6); ecs_fini(world); } void Commands_exists_set_remove(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, 0); ecs_set(world, e, Position, {1, 2}); ecs_defer_begin(world); ecs_set(world, e, Position, {5, 6}); ecs_remove(world, e, Position); ecs_defer_end(world); const Position *p = ecs_get(world, e, Position); test_assert(p == NULL); ecs_fini(world); } void Commands_absent_set_remove(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, 0); ecs_defer_begin(world); ecs_set(world, e, Position, {5, 6}); ecs_remove(world, e, Position); ecs_defer_end(world); const Position *p = ecs_get(world, e, Position); test_assert(p == NULL); ecs_fini(world); } void Commands_exists_set_w_get_mut(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, 0); ecs_set(world, e, Position, {1, 2}); ecs_defer_begin(world); { ecs_set(world, e, Position, {5, 6}); Position *p = ecs_get_mut(world, e, Position); p->x = 11; } ecs_defer_end(world); const Position *p = ecs_get(world, e, Position); test_assert(p != NULL); test_int(p->x, 11); test_int(p->y, 6); ecs_fini(world); } void Commands_exists_remove_get_mut(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, 0); ecs_set(world, e, Position, {1, 2}); ecs_defer_begin(world); ecs_remove(world, e, Position); { Position *p = ecs_get_mut(world, e, Position); test_assert(p != NULL); p->x = 5; p->y = 6; } ecs_defer_end(world); const Position *p = ecs_get(world, e, Position); test_assert(p != NULL); test_int(p->x, 5); test_int(p->y, 6); ecs_fini(world); } void Commands_absent_remove_get_mut(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, 0); ecs_defer_begin(world); ecs_remove(world, e, Position); { Position *p = ecs_get_mut(world, e, Position); test_assert(p != NULL); p->x = 5; p->y = 6; } test_assert(!ecs_has(world, e, Position)); ecs_defer_end(world); const Position *p = ecs_get(world, e, Position); test_assert(p != NULL); test_int(p->x, 5); test_int(p->y, 6); ecs_fini(world); } void Commands_exists_get_mut_remove(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, 0); ecs_set(world, e, Position, {1, 2}); ecs_defer_begin(world); { Position *p = ecs_get_mut(world, e, Position); test_assert(p != NULL); p->x = 5; p->y = 6; } ecs_remove(world, e, Position); ecs_defer_end(world); const Position *p = ecs_get(world, e, Position); test_assert(p == NULL); ecs_fini(world); } void Commands_absent_get_mut_remove(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, 0); ecs_defer_begin(world); { Position *p = ecs_get_mut(world, e, Position); test_assert(p != NULL); p->x = 5; p->y = 6; } test_assert(!ecs_has(world, e, Position)); ecs_remove(world, e, Position); ecs_defer_end(world); const Position *p = ecs_get(world, e, Position); test_assert(p == NULL); ecs_fini(world); } void Commands_absent_set_invoke_on_set(void) { ecs_world_t *world = ecs_mini(); on_set_invoked = 0; ECS_COMPONENT(world, Position); ECS_OBSERVER(world, OnSetTestInvoked, EcsOnSet, Position); ecs_entity_t e = ecs_new(world, 0); /* create the component */ ecs_defer_begin(world); ecs_set(world, e, Position, {1, 2}); /* OnSet should not be run until defer has completed */ test_int(on_set_invoked, 0); ecs_defer_end(world); const Position *p = ecs_get(world, e, Position); test_int(on_set_invoked, 1); test_int(p->x, 1); test_int(p->y, 2); ecs_fini(world); } void Commands_exists_set_invoke_on_set(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_OBSERVER(world, OnSetTestInvoked, EcsOnSet, Position); ecs_entity_t e = ecs_new(world, 0); ecs_set(world, e, Position, {1, 2}); on_set_invoked = 0; /* create the component */ ecs_defer_begin(world); ecs_set(world, e, Position, {5, 6}); /* OnSet should not be run until defer has completed */ test_int(on_set_invoked, 0); ecs_defer_end(world); const Position *p = ecs_get(world, e, Position); test_int(on_set_invoked, 1); test_int(p->x, 5); test_int(p->y, 6); ecs_fini(world); } void Commands_defer_get_mut_no_on_set(void) { ecs_world_t *world = ecs_mini(); on_set_invoked = 0; ECS_COMPONENT(world, Position); ECS_OBSERVER(world, OnSetTestInvoked, EcsOnSet, Position); ecs_entity_t e = ecs_new(world, 0); /* create the component */ ecs_defer_begin(world); { Position *p = ecs_get_mut(world, e, Position); p->x = 1; p->y = 2; } test_assert(!ecs_has(world, e, Position)); test_int(on_set_invoked, 0); ecs_defer_end(world); test_int(on_set_invoked, 0); const Position *p = ecs_get(world, e, Position); test_int(p->x, 1); test_int(p->y, 2); ecs_fini(world); } void Commands_defer_existing_get_mut_no_on_set(void) { ecs_world_t *world = ecs_mini(); on_set_invoked = 0; ECS_COMPONENT(world, Position); ECS_OBSERVER(world, OnSetTestInvoked, EcsOnSet, Position); ecs_entity_t e = ecs_new(world, Position); /* create the component */ ecs_defer_begin(world); { Position *p = ecs_get_mut(world, e, Position); p->x = 1; p->y = 2; } test_assert(ecs_has(world, e, Position)); test_int(on_set_invoked, 0); ecs_defer_end(world); test_int(on_set_invoked, 0); const Position *p = ecs_get(world, e, Position); test_int(p->x, 1); test_int(p->y, 2); ecs_fini(world); } void Commands_get_mut_override(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t base = ecs_set(world, 0, Position, {1, 2}); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); ecs_defer_begin(world); { Position *p = ecs_get_mut(world, e, Position); test_assert(p != NULL); test_int(p->x, 1); test_int(p->y, 2); p->x = 5; p->y = 6; } test_assert(ecs_has(world, e, Position)); test_assert(!ecs_owns(world, e, Position)); ecs_defer_end(world); test_assert(ecs_has(world, e, Position)); test_assert(ecs_owns(world, e, Position)); const Position *p = ecs_get(world, e, Position); test_int(p->x, 5); test_int(p->y, 6); const Position *bp = ecs_get(world, base, Position); test_assert(p != bp); test_int(bp->x, 1); test_int(bp->y, 2); ecs_fini(world); } void Commands_set_override(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_entity_t base = ecs_set(world, 0, Position, {1, 2}); ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); ecs_defer_begin(world); { ecs_set(world, e, Position, {5, 6}); } test_assert(ecs_has(world, e, Position)); test_assert(!ecs_owns(world, e, Position)); ecs_defer_end(world); test_assert(ecs_has(world, e, Position)); test_assert(ecs_owns(world, e, Position)); const Position *p = ecs_get(world, e, Position); test_int(p->x, 5); test_int(p->y, 6); const Position *bp = ecs_get(world, base, Position); test_assert(p != bp); test_int(bp->x, 1); test_int(bp->y, 2); ecs_fini(world); } void Commands_absent_get_mut_for_entity_w_tag(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, Tag); test_assert(e != 0); ecs_defer_begin(world); { Position *p = ecs_get_mut(world, e, Position); test_assert(p != NULL); p->x = 10; p->y = 20; } ecs_defer_end(world); { const Position *p = ecs_get(world, e, Position); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); } ecs_fini(world); } static int set_position_invoked = 0; static int set_velocity_invoked = 0; static int add_tag_invoked = 0; static void set_position_hook(ecs_iter_t *it) { set_position_invoked ++; } static void set_velocity_hook(ecs_iter_t *it) { set_velocity_invoked ++; } static void add_tag(ecs_iter_t *it) { test_int(set_position_invoked, 1); add_tag_invoked ++; } void Commands_on_set_hook_before_on_add_for_existing_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_TAG(world, TagA); ECS_TAG(world, TagB); ecs_set_hooks(world, Position, { .on_set = set_position_hook }); ecs_observer(world, { .filter.terms[0].id = TagA, .events = { EcsOnAdd }, .callback = add_tag }); ecs_entity_t e = ecs_new(world, Position); ecs_defer_begin(world); ecs_add(world, e, TagB); /* 2 add commands, to trigger batching */ ecs_modified(world, e, Position); ecs_add(world, e, TagA); test_int(set_position_invoked, 0); test_int(add_tag_invoked, 0); ecs_defer_end(world); test_assert(set_position_invoked != 0); test_int(add_tag_invoked, 1); ecs_fini(world); } void Commands_defer_2_sets_w_observer_same_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_observer(world, { .filter.terms[0].id = ecs_id(Position), .events = { EcsOnSet }, .callback = set_position_hook }); ecs_entity_t e = ecs_new(world, Position); ecs_defer_begin(world); ecs_set(world, e, Position, {10, 20}); ecs_set(world, e, Position, {20, 30}); ecs_defer_end(world); test_assert(set_position_invoked != 0); const Position *p = ecs_get(world, e, Position); test_assert(p != NULL); test_int(p->x, 20); test_int(p->y, 30); ecs_fini(world); } void Commands_defer_2_sets_w_observer_other_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_observer(world, { .filter.terms[0].id = ecs_id(Position), .events = { EcsOnSet }, .callback = set_position_hook }); ecs_observer(world, { .filter.terms[0].id = ecs_id(Velocity), .events = { EcsOnSet }, .callback = set_velocity_hook }); ecs_entity_t e = ecs_new(world, Position); ecs_defer_begin(world); ecs_set(world, e, Position, {10, 20}); ecs_set(world, e, Velocity, {1, 2}); ecs_defer_end(world); test_assert(set_position_invoked != 0); test_assert(set_velocity_invoked != 0); const Position *p = ecs_get(world, e, Position); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); const Velocity *v = ecs_get(world, e, Velocity); test_assert(v != NULL); test_int(v->x, 1); test_int(v->y, 2); ecs_fini(world); } static int remove_tag_invoked = 0; static void remove_tag(ecs_iter_t *it) { remove_tag_invoked ++; } void Commands_on_remove_after_deferred_clear_and_add(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); ECS_TAG(world, TagC); ecs_observer(world, { .filter.terms[0].id = TagA, .events = { EcsOnRemove }, .callback = remove_tag }); ecs_entity_t e = ecs_new_id(world); ecs_add(world, e, TagA); ecs_add(world, e, TagB); ecs_defer_begin(world); ecs_clear(world, e); ecs_add(world, e, TagC); test_int(remove_tag_invoked, 0); ecs_defer_end(world); test_int(remove_tag_invoked, 1); test_assert(!ecs_has(world, e, TagA)); test_assert(!ecs_has(world, e, TagB)); test_assert(ecs_has(world, e, TagC)); ecs_fini(world); } static void Recycle(ecs_iter_t *it) { ecs_new_id(it->world); *(ecs_entity_t*)it->ctx = ecs_new_id(it->world); } void Commands_defer_delete_recycle_same_id(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Foo); ECS_TAG(world, Bar); ecs_entity_t parent = ecs_new_id(world); ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, parent); ecs_entity_t e2 = ecs_new_id(world); ecs_observer(world, { .filter.terms = { { .id = ecs_id(Foo) } }, .events = { EcsOnAdd }, .callback = Recycle, .ctx = &e1 }); ecs_defer_begin(world); ecs_delete(world, parent); ecs_add(world, e2, Foo); ecs_add(world, e1, Bar); ecs_defer_end(world); test_assert(!ecs_is_alive(world, parent)); test_assert(ecs_is_alive(world, e2)); test_assert(ecs_is_alive(world, e1)); test_assert((uint32_t)e1 != e1); test_assert(ecs_has(world, e2, Foo)); ecs_fini(world); } static ECS_COMPONENT_DECLARE(Velocity); static void AddWhileSuspended(ecs_iter_t *it) { for (int i = 0; i < it->count; i ++) { ecs_add(it->world, it->entities[i], Velocity); } } void Commands_observer_while_defer_suspended(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT_DEFINE(world, Velocity); ecs_observer(world, { .filter.terms = { { .id = ecs_id(Position) } }, .events = { EcsOnAdd }, .callback = AddWhileSuspended, }); ecs_defer_begin(world); ecs_defer_suspend(world); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_assert(ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); ecs_defer_resume(world); ecs_defer_end(world); test_assert(ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Velocity)); ecs_fini(world); } void Commands_on_add_hook_while_defer_suspended(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT_DEFINE(world, Velocity); ecs_set_hooks(world, Position, { .on_add = AddWhileSuspended }); ecs_defer_begin(world); ecs_defer_suspend(world); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_assert(ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); ecs_defer_resume(world); ecs_defer_end(world); test_assert(ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Velocity)); ecs_fini(world); } void Commands_on_set_hook_while_defer_suspended(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT_DEFINE(world, Velocity); ecs_set_hooks(world, Position, { .on_set = AddWhileSuspended }); ecs_defer_begin(world); ecs_defer_suspend(world); ecs_entity_t e = ecs_set(world, 0, Position, {10, 20}); test_assert(e != 0); test_assert(ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); ecs_defer_resume(world); ecs_defer_end(world); test_assert(ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Velocity)); ecs_fini(world); } void Commands_on_remove_hook_while_defer_suspended(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT_DEFINE(world, Velocity); ecs_set_hooks(world, Position, { .on_remove = AddWhileSuspended }); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_assert(ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); ecs_defer_begin(world); ecs_defer_suspend(world); test_assert(ecs_has(world, e, Position)); ecs_remove(world, e, Position); test_assert(!ecs_has(world, e, Position)); test_assert(!ecs_has(world, e, Velocity)); ecs_defer_resume(world); ecs_defer_end(world); test_assert(!ecs_has(world, e, Position)); test_assert(ecs_has(world, e, Velocity)); ecs_fini(world); }