module tests.basic; import bubel.ecs.core; import bubel.ecs.manager; import bubel.ecs.system; import bubel.ecs.attributes; version(GNU) { pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a) { return a; } } else import std.array : staticArray; struct CInt { mixin ECS.Component; alias value this; int value = 1; } struct CFloat { mixin ECS.Component; alias value this; float value = 2.0; } struct CDouble { mixin ECS.Component; alias value this; double value = 3.0; } struct CLong { mixin ECS.Component; alias value this; long value = 10; } struct CShort { mixin ECS.Component; alias value this; short value = 12; } struct CUnregistered { mixin ECS.Component; alias value this; short value = 12; } struct CFlag { mixin ECS.Component; } struct LongAddSystem { mixin ECS.System; struct EntitiesData { int length; CLong[] long_; } void onUpdate(EntitiesData data) { updates_count++; foreach(i;0..data.length) { data.long_[i] += 1; } } int updates_count = 0; } struct EmptySystem { mixin ECS.System!16; struct EntitiesData { int thread_id; } void onUpdate(EntitiesData data) { count++; } int count = 0; } void beforeEveryTest() { CUnregistered.component_id = ushort.max; gEM.initialize(0); gEM.beginRegister(); gEM.registerComponent!CInt; gEM.registerComponent!CFloat; gEM.registerComponent!CDouble; gEM.registerComponent!CLong; gEM.registerComponent!CShort; gEM.registerComponent!CFlag; gEM.endRegister(); } void afterEveryTest() { gEM.destroy(); } @("EntityMeta") unittest { EntityTemplate* tmpl_ = gEM.allocateTemplate([CInt.component_id, CFloat.component_id, CFlag.component_id].staticArray); Entity* entity = gEM.addEntity(tmpl_); EntityMeta meta = entity.getMeta(); assert(meta.hasComponent(CInt.component_id)); assert(meta.getComponent!CInt); assert(meta.hasComponent(CFloat.component_id)); assert(meta.getComponent!CFloat); assert(!meta.getComponent!CLong); assert(!meta.hasComponent(CLong.component_id)); assert(!meta.getComponent!CUnregistered); assert(!meta.hasComponent(CUnregistered.component_id)); assert(*meta.getComponent!CInt == 1); assert(*meta.getComponent!CFloat == 2.0); } @("AddEntity") unittest { EntityTemplate* tmpl_ = gEM.allocateTemplate([CInt.component_id, CFloat.component_id, CFlag.component_id].staticArray); assert(tmpl_.info.components.length == 3); assert(tmpl_.info.size == (CInt.sizeof + CFloat.sizeof + EntityID.sizeof)); assert(tmpl_.getComponent!CInt); assert(tmpl_.getComponent!CFloat); assert(tmpl_.getComponent!CFlag); assert(!tmpl_.getComponent!CLong); assert(!tmpl_.getComponent!CUnregistered); assert(*tmpl_.getComponent!CInt == 1); assert(*tmpl_.getComponent!CFloat == 2.0); Entity* entity = gEM.addEntity(tmpl_); assert(entity.getComponent!CInt); assert(entity.getComponent!CFloat); assert(*entity.getComponent!CInt == 1); assert(*entity.getComponent!CFloat == 2.0); *entity.getComponent!CInt = 2; Entity* entity2 = gEM.addEntityCopy(entity.id); assert(entity2.getComponent!CInt); assert(entity2.getComponent!CFloat); assert(*entity2.getComponent!CInt == 2); assert(*entity2.getComponent!CFloat == 2.0); //CInt cint = CInt(10); //CLong clong; //Entity* entity3 = gEM.addEntity(tmpl_, [cint.ref_, clong.ref_].staticArray); Entity* entity3 = gEM.addEntity(tmpl_, [CInt(10).ref_, CLong().ref_, CFlag().ref_].staticArray); EntityID id = entity3.id; assert(entity3.hasComponent(CInt.component_id)); assert(entity3.hasComponent(CFloat.component_id)); assert(*entity3.getComponent!CInt == 10); assert(*entity3.getComponent!CFloat == 2.0); gEM.addComponents(entity3.id, [CFlag().ref_,CShort(2).ref_].staticArray); gEM.commit(); entity3 = gEM.getEntity(id); assert(entity3.getComponent!CInt); assert(entity3.getComponent!CFloat); assert(entity3.getComponent!CFlag); assert(entity3.getComponent!CShort); assert(*entity3.getComponent!CInt == 10); assert(*entity3.getComponent!CFloat == 2.0); assert(*entity3.getComponent!CShort == 2); gEM.removeComponents(entity3.id, [CFlag().component_id,CShort(2).component_id].staticArray); gEM.commit(); entity3 = gEM.getEntity(id); assert(entity3.getComponent!CInt); assert(entity3.getComponent!CFloat); assert(!entity3.getComponent!CFlag); assert(!entity3.getComponent!CShort); assert(*entity3.getComponent!CInt == 10); assert(*entity3.getComponent!CFloat == 2.0); gEM.addComponents(entity3.id, [CFlag().ref_,CShort(2).ref_].staticArray); gEM.removeComponents(entity3.id, [CUnregistered.component_id].staticArray); gEM.commit(); entity3 = gEM.getEntity(id); assert(entity3.getComponent!CInt); assert(entity3.getComponent!CFloat); assert(entity3.getComponent!CFlag); assert(entity3.getComponent!CShort); assert(*entity3.getComponent!CInt == 10); assert(*entity3.getComponent!CFloat == 2.0); assert(*entity3.getComponent!CShort == 2); gEM.beginRegister(); gEM.registerComponent!CUnregistered; gEM.endRegister(); gEM.addComponents(entity3.id, [CUnregistered(4).ref_].staticArray); gEM.commit(); entity3 = gEM.getEntity(id); assert(entity3.getComponent!CUnregistered); assert(*entity3.getComponent!CUnregistered == 4); gEM.removeComponents(entity3.id, [CUnregistered.component_id].staticArray); gEM.commit(); entity3 = gEM.getEntity(id); assert(!entity3.getComponent!CUnregistered); } //allocate templates @("AllocateTemplates") unittest { //basic template allocation ushort[2] ids = [CInt.component_id, CFloat.component_id]; EntityTemplate* tmpl_ = gEM.allocateTemplate(ids); EntityTemplate* tmpl_d = gEM.allocateTemplate([CFloat.component_id, CInt.component_id, CFloat.component_id].staticArray); EntityTemplate* tmpl_cp = gEM.allocateTemplate(tmpl_); assert(tmpl_d.info == tmpl_.info); assert(tmpl_cp.info == tmpl_cp.info); assert(tmpl_.info.components.length == 2); assert(tmpl_.getComponent!CInt); assert(tmpl_.getComponent!CFloat); assert(*tmpl_.getComponent!CInt == 1); assert(*tmpl_.getComponent!CFloat == 2.0); assert(tmpl_cp.getComponent!CFloat); assert(tmpl_cp.getComponent!CInt); assert(tmpl_.getComponent!CInt != tmpl_cp.getComponent!CInt); assert(tmpl_.getComponent!CFloat != tmpl_cp.getComponent!CFloat); assert(*tmpl_.getComponent!CInt == *tmpl_cp.getComponent!CInt); assert(*tmpl_.getComponent!CFloat == *tmpl_cp.getComponent!CFloat); *tmpl_.getComponent!CInt = 4; *tmpl_.getComponent!CFloat = 5.0; //allocate template from template with additional components ushort[2] ids2 = [CDouble.component_id,CFlag.component_id]; EntityTemplate* tmpl_2 = gEM.allocateTemplate(tmpl_, ids2); assert(tmpl_2.info.components.length == 4); assert(tmpl_2.getComponent!CInt); assert(tmpl_2.getComponent!CFloat); assert(tmpl_2.getComponent!CDouble); assert(tmpl_2.getComponent!CFlag); assert(*tmpl_2.getComponent!CInt == 4); assert(*tmpl_2.getComponent!CFloat == 5.0); assert(*tmpl_2.getComponent!CDouble == 3.0); assert(tmpl_.info.blocksCount() == 0); Entity* entity = gEM.addEntity(tmpl_); gEM.addComponents(entity.id, CFloat(100)); gEM.addComponents(entity.id, CDouble(8.0), CFloat(100)); assert(tmpl_.info.blocksCount() == 1); //apply entity changes gEM.commit(); assert(tmpl_.info.blocksCount() == 0); //allocate template as entity copy EntityTemplate* tmpl_3 = gEM.allocateTemplate(entity.id); assert(tmpl_3.info.components.length == 3); assert(tmpl_3.getComponent!CInt); assert(tmpl_3.getComponent!CFloat); assert(tmpl_3.getComponent!CDouble); assert(*tmpl_3.getComponent!CInt == 4); assert(*tmpl_3.getComponent!CFloat == 5.0); assert(*tmpl_3.getComponent!CDouble == 8.0); //allocate template with entity data but default values EntityTemplate* tmpl_4 = gEM.allocateTemplate(entity.id, true); assert(tmpl_4.info.components.length == 3); assert(tmpl_4.getComponent!CInt); assert(tmpl_4.getComponent!CFloat); assert(tmpl_4.getComponent!CDouble); assert(*tmpl_4.getComponent!CInt == 1); assert(*tmpl_4.getComponent!CFloat == 2.0); assert(*tmpl_4.getComponent!CDouble == 3.0); //allocate template from template with three additional component ushort[3] ids3 = [CDouble.component_id, CLong.component_id, CShort.component_id]; EntityTemplate* tmpl_5 = gEM.allocateTemplate(tmpl_2, ids3); assert(tmpl_5.info.components.length == 6); assert(tmpl_5.getComponent!CInt); assert(tmpl_5.getComponent!CFloat); assert(tmpl_5.getComponent!CDouble); assert(tmpl_5.getComponent!CLong); assert(tmpl_5.getComponent!CShort); assert(*tmpl_5.getComponent!CInt == 4); assert(*tmpl_5.getComponent!CFloat == 5.0); assert(*tmpl_5.getComponent!CDouble == 3.0); assert(*tmpl_5.getComponent!CLong == 10); assert(*tmpl_5.getComponent!CShort == 12); //allocate template from template without one component ushort[1] rem_ids = [CFloat.component_id]; EntityTemplate* tmpl_6 = gEM.allocateTemplate(tmpl_, null, rem_ids); assert(tmpl_6.info.components.length == 1); assert(tmpl_6.getComponent!CInt); assert(*tmpl_6.getComponent!CInt == 4); //allocate template from template without one component and two additional EntityTemplate* tmpl_7 = gEM.allocateTemplate(tmpl_, ids3, rem_ids); assert(tmpl_7.info.components.length == 4); assert(tmpl_7.getComponent!CInt); assert(tmpl_7.getComponent!CDouble); assert(tmpl_7.getComponent!CLong); assert(*tmpl_7.getComponent!CInt == 4); assert(*tmpl_7.getComponent!CDouble == 3.0); assert(*tmpl_7.getComponent!CLong == 10); gEM.freeTemplate(tmpl_d); gEM.freeTemplate(tmpl_); gEM.freeTemplate(tmpl_2); gEM.freeTemplate(tmpl_3); gEM.freeTemplate(tmpl_4); gEM.freeTemplate(tmpl_5); gEM.freeTemplate(tmpl_6); gEM.freeTemplate(tmpl_7); } @("UnsortedComponentIDs") unittest { //basic template allocation ushort[2] ids = [CFloat.component_id, CInt.component_id]; ushort[2] ids2 = [CInt.component_id, CFloat.component_id]; EntityTemplate* tmpl_ = gEM.allocateTemplate(ids); EntityTemplate* tmpl_2 = gEM.allocateTemplate(ids2); assert(tmpl_.info.components.length == 2); assert(tmpl_.getComponent!CInt); assert(tmpl_.getComponent!CFloat); assert(*tmpl_.getComponent!CInt == 1); assert(*tmpl_.getComponent!CFloat == 2.0); assert(tmpl_.info == tmpl_2.info); } @("MultiRegister") unittest { gEM.beginRegister(); gEM.endRegister(); gEM.beginRegister(); gEM.registerComponent!CLong; gEM.registerComponent!CShort; gEM.endRegister(); } @("EmptySystem") unittest { gEM.beginRegister(); gEM.registerSystem!EmptySystem(0); gEM.endRegister(); EmptySystem* system = gEM.getSystem!EmptySystem; assert(system !is null); assert(system.count == 0); System* ecs_system = gEM.getSystem(EmptySystem.system_id); assert(ecs_system !is null); assert(ecs_system.id == EmptySystem.system_id); assert(ecs_system.name == "EmptySystem"); gEM.begin(); gEM.update(); gEM.end(); assert(system.count == 1); } @("SystemCallbacks") unittest { struct TestSystem { mixin ECS.System!16; mixin ECS.ExcludedComponents!(CShort); struct EntitiesData { int length; CLong[] long_; @optional CInt[] int_; } void onCreate() { create++; } void onDestroy() { if(destroy)(*destroy)++; } void onEnable() { enable++; } void onDisable() { disable++; } bool onBegin() { begin++; update = 0; return pass; } void onEnd() { end++; } void onUpdate(EntitiesData data) { update++; } int create = 0; int* destroy; int update = 0; int begin = 0; int end = 0; int enable = 0; int disable = 0; bool pass = true; } gEM.beginRegister(); gEM.registerSystem!TestSystem(0); gEM.endRegister(); TestSystem* system = gEM.getSystem!TestSystem; int destroy = 0; system.destroy = &destroy; gEM.beginRegister(); gEM.registerSystem!TestSystem(0); gEM.endRegister(); system = gEM.getSystem!TestSystem; system.destroy = &destroy; assert(system !is null); assert(system.create == 1); assert(system.begin == 0); assert(system.end == 0); assert(system.enable == 1); assert(system.disable == 0); //FIXME: currently destroy is only called with Manager.destory which is bug, but there is no workaround for this by now //assert(destroy == 1); System* ecs_system = gEM.getSystem(system.system_id); ecs_system.enable(); assert(system.enable == 1); ecs_system.disable(); ecs_system.disable(); ecs_system.enable(); assert(system.enable == 2); assert(system.disable == 1); ushort[2] ids = [CLong.component_id,CFloat.component_id]; EntityTemplate* tmpl = gEM.allocateTemplate(ids); scope (exit) gEM.freeTemplate(tmpl); gEM.addEntity(tmpl); gEM.begin(); assert(system.begin == 1); gEM.update(); assert(system.update == 1); gEM.end(); assert(system.end == 1); ushort[2] ids2 = [CLong.component_id, CInt.component_id]; EntityTemplate* tmpl2 = gEM.allocateTemplate(ids2); scope (exit) gEM.freeTemplate(tmpl2); gEM.addEntity(tmpl2); gEM.addEntity(tmpl2); gEM.begin(); assert(system.begin == 2); gEM.update(); assert(system.update == 2);//system is updated number of entity blocks times gEM.end(); assert(system.end == 2); ushort[2] ids3 = [CLong.component_id, CShort.component_id]; EntityTemplate* tmpl3 = gEM.allocateTemplate(ids3); scope (exit) gEM.freeTemplate(tmpl3); gEM.addEntity(tmpl3); //entity with excluded component shouldn't be updated gEM.begin(); assert(system.begin == 3); gEM.update(); assert(system.update == 2); gEM.end(); assert(system.end == 3); //system can be disable form update in onBegin() callback, onEnd() callback is called system.pass = false; gEM.begin(); assert(system.begin == 4); gEM.update(); assert(system.update == 0); gEM.end(); assert(system.end == 4); system.pass = true; //disabled system is't called ecs_system.disable(); gEM.begin(); assert(system.begin == 4); gEM.update(); assert(system.update == 0); gEM.end(); assert(system.end == 4); ecs_system.enable(); system.destroy = null; } @("CustomPass") unittest { gEM.beginRegister(); gEM.registerPass("custom"); gEM.registerSystem!LongAddSystem(-1,"custom"); gEM.endRegister(); assert(gEM.getPass("custom")); assert(!gEM.getPass("custommm")); LongAddSystem* system = gEM.getSystem!LongAddSystem; assert(system !is null); assert(system.updates_count == 0); System* ecs_system = gEM.getSystem(LongAddSystem.system_id); assert(ecs_system !is null); assert(ecs_system.id == LongAddSystem.system_id); assert(ecs_system.priority == -1); assert(ecs_system.name == "LongAddSystem"); ushort[1] ids = [CLong.component_id]; EntityTemplate* tmpl = gEM.allocateTemplate(ids); scope (exit) gEM.freeTemplate(tmpl); gEM.addEntity(tmpl); gEM.begin(); gEM.update(); assert(system.updates_count == 0); gEM.update("custom"); assert(system.updates_count == 1); gEM.end(); } @("SystemEntityCallbacks") unittest { static int add_order = 0; static int rem_order = 0; static int change_order = 0; struct TestSystem { mixin ECS.System!16; mixin ECS.ExcludedComponents!(CShort); struct EntitiesData { int length; CLong[] long_; @optional CInt[] int_; } void onAddEntity(EntitiesData data) { add++; assert(add_order == 1); add_order++; } void onRemoveEntity(EntitiesData data) { remove++; assert(rem_order == 1); rem_order++; } void onChangeEntity(EntitiesData data) { change++; assert(change_order == 1); change_order++; } void onUpdate(EntitiesData data) { } int add = 0; int remove = 0; int change = 0; } struct TestSystem2 { mixin ECS.System!16; mixin ECS.ExcludedComponents!(CShort); struct EntitiesData { int length; CLong[] long_; @optional CInt[] int_; } void onAddEntity(EntitiesData data) { assert(add_order == 2); add_order = 0; } void onRemoveEntity(EntitiesData data) { assert(rem_order == 2); rem_order = 0 ; } void onChangeEntity(EntitiesData data) { assert(change_order == 2); change_order = 0; } void onUpdate(EntitiesData data) { } } struct TestSystem3 { mixin ECS.System!16; mixin ECS.ExcludedComponents!(CShort); struct EntitiesData { int length; CLong[] long_; @optional CInt[] int_; } void onAddEntity(EntitiesData data) { assert(add_order == 0); add_order++; } void onRemoveEntity(EntitiesData data) { assert(rem_order == 0); rem_order++; } void onChangeEntity(EntitiesData data) { assert(change_order == 0); change_order++; } void onUpdate(EntitiesData data) { } } gEM.beginRegister(); gEM.registerSystem!TestSystem3(-1); gEM.registerSystem!TestSystem(0); gEM.registerSystem!TestSystem2(1); gEM.endRegister(); TestSystem* system = gEM.getSystem!TestSystem; assert(system !is null); assert(system.add == 0); assert(system.remove == 0); assert(system.change == 0); EntityTemplate* tmpl = gEM.allocateTemplate([CLong.component_id,CFloat.component_id].staticArray); scope (exit) gEM.freeTemplate(tmpl); EntityID id0 = gEM.addEntity(tmpl).id; gEM.commit(); assert(system.add == 1); EntityTemplate* tmpl2 = gEM.allocateTemplate([CLong.component_id, CInt.component_id].staticArray); scope (exit) gEM.freeTemplate(tmpl2); EntityID id1 = gEM.addEntity(tmpl2).id; gEM.commit(); assert(system.add == 2); EntityTemplate* tmpl3 = gEM.allocateTemplate([CLong.component_id, CShort.component_id].staticArray); scope (exit) gEM.freeTemplate(tmpl3); EntityID id2 = gEM.addEntity(tmpl3).id; gEM.commit(); assert(system.add == 2); gEM.beginRegister(); gEM.endRegister(); gEM.removeComponents(id0, [CFloat.component_id].staticArray); gEM.commit(); assert(system.add == 2); assert(system.remove == 0); assert(system.change == 0); gEM.removeComponents(id1, [CInt.component_id].staticArray); gEM.commit(); assert(system.add == 2); assert(system.remove == 0); assert(system.change == 1); gEM.removeComponents(id2, [CShort.component_id].staticArray); gEM.commit(); assert(system.add == 3); assert(system.remove == 0); assert(system.change == 1); gEM.addComponents(id2, CShort(1)); gEM.commit(); assert(system.add == 3); assert(system.remove == 1); assert(system.change == 1); gEM.removeEntity(id0); gEM.commit(); assert(system.add == 3); assert(system.remove == 2); assert(system.change == 1); gEM.addComponents(id1, CInt(1)); gEM.commit(); assert(system.add == 3); assert(system.remove == 2); assert(system.change == 2); gEM.addComponents(id0, CFloat(1)); gEM.commit(); assert(system.add == 3); assert(system.remove == 2); assert(system.change == 2); gEM.removeEntity(id1); gEM.commit(); assert(system.add == 3); assert(system.remove == 3); assert(system.change == 2); gEM.removeEntity(id2); gEM.commit(); assert(system.add == 3); assert(system.remove == 3); assert(system.change == 2); } @("TemplateCoverage") unittest { struct TestSystem { mixin ECS.System; struct EntitiesData { int length; Entity[] entity; @readonly CLong[] long_; @optional CInt[] int_; @readonly CFloat[] float_; } mixin ECS.ExcludedComponents!(CUnregistered); void onUpdate(EntitiesData data) { } } gEM.beginRegister(); gEM.registerComponent!CUnregistered; gEM.registerSystem!TestSystem(0); gEM.endRegister(); } @("UnregisteredSystem") unittest { struct TestSystem { mixin ECS.System; struct EntitiesData { int length; Entity[] entity; @readonly CLong[] long_; @optional CInt[] int_; @readonly CFloat[] float_; } void onUpdate(EntitiesData data) { } } assert(gEM.getSystem!TestSystem is null); assert(gEM.getSystem(TestSystem.system_id) is null); } @("MultithreadedUpdate") unittest { struct TestSystem { mixin ECS.System; struct EntitiesData { int length; Entity[] entity; @readonly CLong[] long_; @optional CInt[] int_; @readonly CFloat[] float_; } void onUpdate(EntitiesData data) { update++; entities += data.length; } int update = 0; int entities = 0; } struct TestEmptySystem { mixin ECS.System; struct EntitiesData { int length; } void onUpdate(EntitiesData data) { update++; } int update = 0; } void dispatch(EntityManager.JobGroup grp) { foreach(job; grp.jobs) { job.execute(); } } uint getID() { return 0; } gEM.setMultithreadingCallbacks(&dispatch, &getID); gEM.beginRegister(); gEM.registerPass("custom"); gEM.registerSystem!TestSystem(-1,"custom"); gEM.registerSystem!TestEmptySystem(1,"custom"); gEM.endRegister(); TestSystem* system = gEM.getSystem!TestSystem; TestEmptySystem* empty_system = gEM.getSystem!TestEmptySystem; ushort[2] ids = [CLong.component_id,CFloat.component_id]; EntityTemplate* tmpl = gEM.allocateTemplate(ids); scope (exit) gEM.freeTemplate(tmpl); EntityTemplate* tmpl2 = gEM.allocateTemplate([CLong.component_id,CInt.component_id,CShort.component_id,CFloat.component_id].staticArray); scope (exit) gEM.freeTemplate(tmpl2); gEM.begin(); gEM.updateMT("custom"); gEM.end(); assert(system.update == 0); assert(system.entities == 0); assert(empty_system.update == 1); gEM.addEntity(tmpl); gEM.begin(); gEM.updateMT("custom"); gEM.end(); assert(system.update == 1); assert(system.entities == 1); assert(empty_system.update == 2); system.entities = 0; foreach(i;0..2000)gEM.addEntity(tmpl); gEM.begin(); gEM.updateMT("custom"); gEM.end(); assert(system.update > 2); assert(system.entities == 2001); assert(empty_system.update == 3); system.entities = 0; foreach(i;0..10000)gEM.addEntity(tmpl); gEM.begin(); gEM.updateMT("custom"); gEM.end(); assert(system.entities == 12001); } unittest { assert(gEM.pageSize == 32768); assert(gEM.pagesInBlock == 128); } @("AddRemoveEntities") unittest { ushort[3] ids = [CLong.component_id,CFloat.component_id,CShort.component_id]; EntityTemplate* tmpl = gEM.allocateTemplate(ids); scope (exit) gEM.freeTemplate(tmpl); EntityID[5000] entities; foreach(i;0..4) { foreach(j;0..5000) { entities[j] = gEM.addEntity(tmpl).id; } gEM.commit(); foreach(j;0..5000) { gEM.removeEntity(entities[j]); } gEM.commit(); } } @("ChangeEntityComponents") unittest { gEM.beginRegister(); gEM.registerComponent!CUnregistered; gEM.endRegister(); ushort[1] ids = [CLong.component_id]; EntityTemplate* tmpl = gEM.allocateTemplate(ids); scope (exit) gEM.freeTemplate(tmpl); EntityID id = gEM.addEntity(tmpl).id; gEM.commit(); Entity* entity = gEM.getEntity(id); assert(entity.id == id); assert(entity.getComponent!CLong !is null); assert(entity.getComponent!CFloat is null); assert(entity.getComponent!CUnregistered is null); assert(entity.getComponent!CShort is null); assert(entity.getComponent!CInt is null); assert(*entity.getComponent!CLong == 10); gEM.addComponents(id, CShort(15), CFloat(13)); gEM.commit(); entity = gEM.getEntity(id); assert(entity.id == id); assert(entity.getComponent!CLong !is null); assert(entity.getComponent!CFloat !is null); assert(entity.getComponent!CUnregistered is null); assert(entity.getComponent!CShort !is null); assert(entity.getComponent!CInt is null); assert(*entity.getComponent!CLong == 10); assert(*entity.getComponent!CShort == 15); assert(*entity.getComponent!CFloat == 13); ushort[3] ids2 = [CFloat.component_id, CLong.component_id, CUnregistered.component_id]; gEM.removeComponents(id, ids2); gEM.commit(); entity = gEM.getEntity(id); assert(entity.id == id); assert(entity.getComponent!CLong is null); assert(entity.getComponent!CFloat is null); assert(entity.getComponent!CUnregistered is null); assert(entity.getComponent!CShort !is null); assert(entity.getComponent!CInt is null); assert(*entity.getComponent!CShort == 15); gEM.removeComponents(id, ids2); gEM.addComponents(id, CShort(11), CLong(2)); //wrong order of components gEM.commit(); entity = gEM.getEntity(id); assert(entity.id == id); assert(entity.getComponent!CLong !is null); assert(entity.getComponent!CFloat is null); assert(entity.getComponent!CUnregistered is null); assert(entity.getComponent!CShort !is null); assert(entity.getComponent!CInt is null); assert(*entity.getComponent!CLong == 2); assert(*entity.getComponent!CShort == 15); gEM.removeEntity(id); entity = gEM.getEntity(id); assert(entity !is null); assert(entity.id == id); gEM.commit(); entity = gEM.getEntity(id); assert(entity is null); } @("EventCallbacks") unittest { struct ETest { mixin ECS.Event; } struct ETest2 { mixin ECS.Event; void onDestroy() { destory++; } int super_liczba = 0; static int destory = 0; } struct TestSystem { mixin ECS.System; struct EntitiesData { int length; Entity[] entity; @readonly CLong[] long_; @optional CInt[] int_; } void onUpdate(EntitiesData data) { } void handleEvent(Entity* entity, ETest event) { CLong* long_ = entity.getComponent!CLong; CInt* int_ = entity.getComponent!CInt; *long_ += 16; if(int_)*int_ += 6; } void handleEvent(Entity* entity, ETest2 event) { CLong* long_ = entity.getComponent!CLong; CInt* int_ = entity.getComponent!CInt; *long_ += event.super_liczba * 2; if(int_)*int_ += event.super_liczba * 4; } } struct TestSystem2 { mixin ECS.System; struct EntitiesData { int length; Entity[] entity; CShort[] short_; @optional CInt[] int_; } void handleEvent(Entity* entity, ETest event) { CShort* short_ = entity.getComponent!CShort; CInt* int_ = entity.getComponent!CInt; *short_ += 8; if(int_)*int_ += 2; } void handleEvent(Entity* entity, ETest2 event) { CShort* short_ = entity.getComponent!CShort; CInt* int_ = entity.getComponent!CInt; *short_ += event.super_liczba; if(int_)*int_ *= event.super_liczba; } } gEM.beginRegister(); gEM.registerEvent!ETest; gEM.registerEvent!ETest2; gEM.registerEvent!ETest; gEM.registerEvent!ETest2; gEM.registerSystem!TestSystem2(1); gEM.registerSystem!TestSystem(0); gEM.endRegister(); ushort[1] ids = [CLong.component_id]; EntityTemplate* tmpl = gEM.allocateTemplate(ids); scope (exit) gEM.freeTemplate(tmpl); ushort[1] ids2 = [CShort.component_id]; EntityTemplate* tmpl2 = gEM.allocateTemplate(ids2); scope (exit) gEM.freeTemplate(tmpl2); Entity* entity = gEM.addEntity(tmpl); EntityID id = entity.id; assert(*entity.getComponent!CLong == 10); Entity* entity2 = gEM.addEntity(tmpl2); EntityID id2 = entity2.id; assert(*entity2.getComponent!CShort == 12); gEM.sendEvent(id,ETest()); gEM.sendEvent(id,ETest2(10)); gEM.sendEvent(id2,ETest()); gEM.sendEvent(id2,ETest2(12)); gEM.commit(); assert(ETest2.destory == 2); entity = gEM.getEntity(id); entity2 = gEM.getEntity(id2); assert(*entity.getComponent!CLong == 46); assert(*entity2.getComponent!CShort == 32); gEM.addComponents(id, CInt(2), CShort(1)); gEM.sendEvent(id,ETest()); gEM.sendEvent(id,ETest2(2)); gEM.commit(); assert(ETest2.destory == 3); entity = gEM.getEntity(id); assert(*entity.getComponent!CLong == 66); assert(*entity.getComponent!CInt == 2);//36); //test for multiple event blocks long result = *entity.getComponent!CLong; foreach(i;0..10000) { gEM.sendEvent(id,ETest()); gEM.sendEvent(id,ETest2(4)); result += 16; result += 8; } gEM.commit(); assert(ETest2.destory == 10003); entity = gEM.getEntity(id); assert(*entity.getComponent!CLong == result); //cover funcion to clearEvents before destroying manager gEM.sendEvent(id,ETest()); } @("EntitiesFunction") unittest { struct TestSystem { mixin ECS.System; struct EntitiesData { uint length; CInt[] int_; } void onUpdate(EntitiesData entities) { } } void func1(TestSystem.EntitiesData entities) { foreach(i;0 .. entities.length) { entities.int_[i] += entities.int_[i] / 2; } } void func2(TestSystem.EntitiesData entities) { foreach(i;0 .. entities.length) { entities.int_[i] += 8; } } gEM.beginRegister(); gEM.registerSystem!TestSystem(1); gEM.endRegister(); EntityTemplate* tmpl = gEM.allocateTemplate([CInt.component_id].staticArray); scope (exit) gEM.freeTemplate(tmpl); EntityID id1 = gEM.addEntity(tmpl).id; EntityTemplate* tmpl2 = gEM.allocateTemplate([CInt.component_id, CLong.component_id].staticArray); scope (exit) gEM.freeTemplate(tmpl2); EntityID id2 = gEM.addEntity(tmpl2).id; gEM.begin(); Entity* entity1 = gEM.getEntity(id1); Entity* entity2 = gEM.getEntity(id2); assert(*entity1.getComponent!CInt == 1); assert(*entity2.getComponent!CInt == 1); gEM.callEntitiesFunction!TestSystem(&func2); assert(*entity1.getComponent!CInt == 9); assert(*entity2.getComponent!CInt == 9); gEM.callEntitiesFunction!TestSystem(&func1); assert(*entity1.getComponent!CInt == 13); assert(*entity2.getComponent!CInt == 13); gEM.end(); } @("SystemDependencies") unittest { struct TestSystem { mixin ECS.System; struct EntitiesData { uint length; @readonly CInt[] int_; } void onUpdate(EntitiesData entities) { } } struct TestSystem2 { mixin ECS.System; struct EntitiesData { uint length; CInt[] int_; } void onUpdate(EntitiesData entities) { } } struct TestSystem3 { mixin ECS.System; struct EntitiesData { uint length; @readonly CInt[] int_; } void onUpdate(EntitiesData entities) { } } struct TestSystem4 { mixin ECS.System; struct EntitiesData { uint length; CInt[] int_; CLong[] long_; } void onUpdate(EntitiesData entities) { } } struct TestSystem5 { mixin ECS.System; struct EntitiesData { uint length; @readonly CLong[] int_; } void onUpdate(EntitiesData entities) { } } gEM.beginRegister(); gEM.registerSystem!TestSystem(0); gEM.registerSystem!TestSystem2(1); gEM.registerSystem!TestSystem3(2); gEM.registerSystem!TestSystem4(3); gEM.registerSystem!TestSystem5(4); gEM.endRegister(); const (EntityManager.UpdatePass)* pass = gEM.getPass("update"); assert(pass != null); assert(pass.system_callers.length == 5); assert(pass.system_callers[0].system_id == TestSystem.system_id); assert(pass.system_callers[1].system_id == TestSystem2.system_id); assert(pass.system_callers[2].system_id == TestSystem3.system_id); assert(pass.system_callers[3].system_id == TestSystem4.system_id); assert(pass.system_callers[4].system_id == TestSystem5.system_id); assert(pass.system_callers[0].dependencies.length == 0); assert(pass.system_callers[1].dependencies.length == 1); assert(pass.system_callers[2].dependencies.length == 1); assert(pass.system_callers[3].dependencies.length == 3); assert(pass.system_callers[4].dependencies.length == 1); assert(pass.system_callers[1].dependencies[0].system_id == TestSystem.system_id); assert(pass.system_callers[2].dependencies[0].system_id == TestSystem2.system_id); assert(pass.system_callers[3].dependencies[0].system_id == TestSystem.system_id); assert(pass.system_callers[3].dependencies[1].system_id == TestSystem2.system_id); assert(pass.system_callers[3].dependencies[2].system_id == TestSystem3.system_id); assert(pass.system_callers[4].dependencies[0].system_id == TestSystem4.system_id); } @("ExternalSystemDependencies") unittest { enum TestDependency = "TestDepencency"; struct TestSystem { mixin ECS.System; mixin ECS.ReadOnlyDependencies!(TestDependency); struct EntitiesData { uint length; @readonly CInt[] int_; } void onUpdate(EntitiesData entities) { } } struct TestSystem2 { mixin ECS.System; mixin ECS.WritableDependencies!(TestDependency); struct EntitiesData { uint length; @readonly CInt[] int_; } void onUpdate(EntitiesData entities) { } } struct TestSystem3 { mixin ECS.System; mixin ECS.ReadOnlyDependencies!(TestDependency); struct EntitiesData { uint thread_id; } void onUpdate(EntitiesData entities) { } } struct TestSystem4 { mixin ECS.System; mixin ECS.WritableDependencies!(TestDependency); struct EntitiesData { uint length; @readonly CInt[] int_; } void onUpdate(EntitiesData entities) { } } struct TestSystem5 { mixin ECS.System; mixin ECS.ReadOnlyDependencies!(TestDependency); struct EntitiesData { uint length; @readonly CLong[] int_; } void onUpdate(EntitiesData entities) { } } gEM.beginRegister(); gEM.registerDependency(TestDependency); gEM.registerSystem!TestSystem(0); gEM.registerSystem!TestSystem2(1); gEM.registerSystem!TestSystem3(2); gEM.registerSystem!TestSystem4(3); gEM.registerSystem!TestSystem5(4); gEM.endRegister(); const (EntityManager.UpdatePass)* pass = gEM.getPass("update"); assert(pass != null); assert(pass.system_callers.length == 5); assert(pass.system_callers[0].system_id == TestSystem.system_id); assert(pass.system_callers[1].system_id == TestSystem2.system_id); assert(pass.system_callers[2].system_id == TestSystem3.system_id); assert(pass.system_callers[3].system_id == TestSystem4.system_id); assert(pass.system_callers[4].system_id == TestSystem5.system_id); assert(pass.system_callers[0].dependencies.length == 0); assert(pass.system_callers[1].dependencies.length == 1); assert(pass.system_callers[2].dependencies.length == 1); assert(pass.system_callers[3].dependencies.length == 3); assert(pass.system_callers[4].dependencies.length == 2); assert(pass.system_callers[1].dependencies[0].system_id == TestSystem.system_id); assert(pass.system_callers[2].dependencies[0].system_id == TestSystem2.system_id); assert(pass.system_callers[3].dependencies[0].system_id == TestSystem.system_id); assert(pass.system_callers[3].dependencies[1].system_id == TestSystem2.system_id); assert(pass.system_callers[3].dependencies[2].system_id == TestSystem3.system_id); assert(pass.system_callers[4].dependencies[0].system_id == TestSystem2.system_id); assert(pass.system_callers[4].dependencies[1].system_id == TestSystem4.system_id); }