module tests.basic; import ecs.core; import ecs.manager; import ecs.system; import ecs.attributes; 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 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() { gEM.initialize(1); gEM.beginRegister(); gEM.registerComponent!CInt; gEM.registerComponent!CFloat; gEM.registerComponent!CDouble; gEM.registerComponent!CLong; gEM.registerComponent!CShort; gEM.endRegister(); } void afterEveryTest() { gEM.destroy(); } @("AddEntity") unittest { ushort[2] ids = [CInt.component_id, CFloat.component_id]; EntityTemplate* tmpl_ = gEM.allocateTemplate(ids); assert(tmpl_.info.components.length == 2); assert(tmpl_.getComponent!CInt); assert(tmpl_.getComponent!CFloat); 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); } //allocate templates @("AllocateTemplates") unittest { //basic template allocation ushort[2] ids = [CInt.component_id, CFloat.component_id]; EntityTemplate* tmpl_ = gEM.allocateTemplate(ids); 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); *tmpl_.getComponent!CInt = 4; *tmpl_.getComponent!CFloat = 5.0; //allocate template from template with additional component ushort[1] ids2 = [CDouble.component_id]; EntityTemplate* tmpl_2 = gEM.allocateTemplate(tmpl_, ids2); assert(tmpl_2.info.components.length == 3); assert(tmpl_2.getComponent!CInt); assert(tmpl_2.getComponent!CFloat); assert(tmpl_2.getComponent!CDouble); assert(*tmpl_2.getComponent!CInt == 4); assert(*tmpl_2.getComponent!CFloat == 5.0); assert(*tmpl_2.getComponent!CDouble == 3.0); Entity* entity = gEM.addEntity(tmpl_); gEM.addComponents(entity.id, CDouble(8.0)); //apply entity changes gEM.commit(); //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 two additional component ushort[2] ids3 = [CDouble.component_id, CLong.component_id]; EntityTemplate* tmpl_5 = gEM.allocateTemplate(tmpl_, ids3); assert(tmpl_5.info.components.length == 4); assert(tmpl_5.getComponent!CInt); assert(tmpl_5.getComponent!CFloat); assert(tmpl_5.getComponent!CDouble); assert(tmpl_5.getComponent!CLong); 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); //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 == 3); 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_); 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() { (*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(); } @("CustomPass") unittest { gEM.beginRegister(); gEM.registerPass("custom"); gEM.registerSystem!LongAddSystem(-1,"custom"); gEM.endRegister(); 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 { struct TestSystem { mixin ECS.System!16; mixin ECS.ExcludedComponents!(CShort); struct EntitiesData { int length; CLong[] long_; @optional CInt[] int_; } void onAddEntity(EntitiesData data) { add++; } void onRemoveEntity(EntitiesData data) { remove++; } void onChangeEntity(EntitiesData data) { change++; } void onUpdate(EntitiesData data) { } int add = 0; int remove = 0; int change = 0; } gEM.beginRegister(); gEM.registerSystem!TestSystem(0); gEM.endRegister(); TestSystem* system = gEM.getSystem!TestSystem; assert(system !is null); assert(system.add == 0); assert(system.remove == 0); assert(system.change == 0); ushort[2] ids = [CLong.component_id,CFloat.component_id]; EntityTemplate* tmpl = gEM.allocateTemplate(ids); scope (exit) gEM.freeTemplate(tmpl); EntityID id0 = gEM.addEntity(tmpl).id; gEM.commit(); assert(system.add == 1); ushort[2] ids2 = [CLong.component_id, CInt.component_id]; EntityTemplate* tmpl2 = gEM.allocateTemplate(ids2); scope (exit) gEM.freeTemplate(tmpl2); EntityID id1 = gEM.addEntity(tmpl2).id; gEM.commit(); assert(system.add == 2); ushort[2] ids3 = [CLong.component_id, CShort.component_id]; EntityTemplate* tmpl3 = gEM.allocateTemplate(ids3); scope (exit) gEM.freeTemplate(tmpl3); EntityID id2 = gEM.addEntity(tmpl3).id; gEM.commit(); assert(system.add == 2); gEM.commit(); }