From dfdb56d5017f333a73a1ce040a0143138cb69077 Mon Sep 17 00:00:00 2001 From: Mergul Date: Sat, 10 Aug 2019 15:35:36 +0000 Subject: [PATCH 1/6] -system name getter -added error checking for event handling in registerSystem -now only valid handleEvent() functions are taken by system during register --- source/ecs/manager.d | 41 ++++++++++++++++++++++++++--------------- source/ecs/system.d | 10 +++++++++- tests/tests.d | 9 ++++++++- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/source/ecs/manager.d b/source/ecs/manager.d index 19d7cd1..3318d9e 100644 --- a/source/ecs/manager.d +++ b/source/ecs/manager.d @@ -69,7 +69,7 @@ export class EntityManager if(system.m_components)Mallocator.instance.dispose(system.m_components); if(system.m_excluded_components)Mallocator.instance.dispose(system.m_excluded_components); if(system.m_optional_components)Mallocator.instance.dispose(system.m_optional_components); - if(system.name)Mallocator.instance.dispose(system.name); + if(system.m_name)Mallocator.instance.dispose(system.m_name); if(system.m_event_callers)Mallocator.instance.dispose(system.m_event_callers); if(system.m_system_pointer)Mallocator.instance.dispose(system.m_system_pointer); @@ -344,20 +344,31 @@ export class EntityManager data_system.handleEvent(input, *cast(Type*) data.event); } - static void setEventCallers(Sys)(ref System system) + void setEventCallers(Sys)(ref System system) { enum event_handlers_num = __traits(getOverloads, Sys, "handleEvent").length; - system.m_event_callers = Mallocator.instance.makeArray!( - System.EventCaller)(event_handlers_num); + System.EventCaller[] callers = (cast(System.EventCaller*)alloca(event_handlers_num * System.EventCaller.sizeof))[0..event_handlers_num]; + int i = 0; foreach (j, func; __traits(getOverloads, Sys, "handleEvent")) { - alias EventParamType = Parameters!(__traits(getOverloads, - Sys, "handleEvent")[j])[1]; - system.m_event_callers[j].callback = cast( - void*)&callEventHandler!(EventParamType); - system.m_event_callers[j].id = EventParamType.event_id; + alias Params = Parameters!(__traits(getOverloads, + Sys, "handleEvent")[j]); + static if(Params.length == 2 && is(Params[0] == __traits(getMember, Sys, "EventInput"))) + { + alias EventParamType = Params[1]; + enum EventName = Unqual!(EventParamType).stringof; + ushort evt = events_map.get(cast(char[]) EventName, ushort.max); + assert(evt != ushort.max, "Can't register system \""~Sys.stringof~"\" due to non existing event \""~EventName~"\"."); + + callers[i].callback = cast( + void*)&callEventHandler!(EventParamType); + callers[i].id = EventParamType.event_id; + i++; + } } + + system.m_event_callers = Mallocator.instance.makeArray(callers[0..i]); } static if (__traits(hasMember, Sys, "handleEvent")) @@ -736,8 +747,8 @@ export class EntityManager } else { - system.name = Mallocator.instance.makeArray(Sys.stringof); - systems_map.add(system.name, cast(ushort) systems.length); + system.m_name = Mallocator.instance.makeArray(Sys.stringof); + systems_map.add(system.m_name, cast(ushort) systems.length); system.m_id = cast(ushort)(systems.length); @@ -2302,9 +2313,9 @@ export class EntityManager caller.exclusion = null; /*import std.stdio; - write("Exclusive systems for system ", caller.system.name, ": "); + write("Exclusive systems for system ", caller.system.m_name, ": "); foreach (ex; exclusion[0 .. index]) - write(ex.system.name, " "); + write(ex.system.m_name, " "); writeln();*/ } @@ -2362,9 +2373,9 @@ export class EntityManager caller.dependencies = null; /*import std.stdio; - write("Dependencies for system ", caller.system.name, ": "); + write("Dependencies for system ", caller.system.m_name, ": "); foreach (ex; caller.dependencies) - write(ex.system.name, " "); + write(ex.system.m_name, " "); writeln();*/ } } diff --git a/source/ecs/system.d b/source/ecs/system.d index e033147..b4ad780 100644 --- a/source/ecs/system.d +++ b/source/ecs/system.d @@ -74,6 +74,14 @@ struct System return m_id; } + /************************************************************************************************************************ + *Get system name. + */ + export const (char)[] name() nothrow @nogc + { + return cast(const (char)[])m_name; + } + struct EventCaller { ushort id; @@ -97,7 +105,7 @@ package: int m_pass; ///system name - char[] name; + char[] m_name; ///required components ushort[] m_components; diff --git a/tests/tests.d b/tests/tests.d index edd8b16..3cf3697 100644 --- a/tests/tests.d +++ b/tests/tests.d @@ -376,6 +376,11 @@ struct TestSystem2 //TestComp* tt; } + void handleEvent(EventInput input) + { + + } + void handleEvent(EventInput input, ref TestEvent event) { input.test.bg = event.a; @@ -571,9 +576,11 @@ int main() //foreach(j; 0..1_000)gEM.addEntity(tmpl); gEM.beginRegister(); - //gEM.registerSystem!TestSystem2(0); + gEM.registerSystem!TestSystem2(0); gEM.endRegister(); + System* sys = EntityManager.instance.getSystem(TestSystem2.system_id); + //gEM.generateDependencies(); //assert(*(cast(EntityID*)(cast(void*)tmpl.info.first_block+24)) == EntityID(1,1)); From 8318d2efb49a5b476fee2e535fa246f4b8fd690e Mon Sep 17 00:00:00 2001 From: Mergul Date: Sat, 10 Aug 2019 15:44:01 +0000 Subject: [PATCH 2/6] -added support for optional components in event handling --- source/ecs/manager.d | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/ecs/manager.d b/source/ecs/manager.d index 3318d9e..b5636b3 100644 --- a/source/ecs/manager.d +++ b/source/ecs/manager.d @@ -330,7 +330,10 @@ export class EntityManager "optional"); static if (is_optional) { - event_field = null; + if(info.deltas[EventFieldType.component_id] != 0)event_field = cast(EventFieldType*)(cast(void*) data.block + + info.deltas[EventFieldType.component_id] + + data.id * EventFieldType.sizeof); + else event_field = null; } else { From 1be08eb5345a0b63d24f9221ce9e16f557cbf6d4 Mon Sep 17 00:00:00 2001 From: Mergul Date: Sat, 10 Aug 2019 16:20:50 +0000 Subject: [PATCH 3/6] -added pure annotation for some functions -addEntity now returns pointer instead of reference (it's has more sense) -added function addEntityCopy(EntityID) which adds copy of entity with it's whole data --- source/ecs/manager.d | 106 +++++++++++++++++++++++++++++-------------- tests/tests.d | 43 +++++++++--------- 2 files changed, 94 insertions(+), 55 deletions(-) diff --git a/source/ecs/manager.d b/source/ecs/manager.d index b5636b3..6d01d6d 100644 --- a/source/ecs/manager.d +++ b/source/ecs/manager.d @@ -1056,7 +1056,7 @@ export class EntityManager m_dispatch_jobs = func; } - static void alignNum(ref ushort num, ushort alignment) nothrow @nogc + static void alignNum(ref ushort num, ushort alignment) nothrow @nogc pure { num = cast(ushort)((num + alignment - 1) & (-cast(int) alignment)); //num += alignment - (num & (alignment - 1)); } @@ -1459,10 +1459,7 @@ export class EntityManager new_entity.id = entity.id; new_entity.updateID(); - static if (EntityID.sizeof == 8) - uint ind = cast(uint)((cast(void*) entity - block.dataBegin()) >> 3); - else - uint ind = cast(uint)((cast(void*) entity - block.dataBegin()) / EntityID.sizeof()); + uint ind = block.entityIndex(entity); if (info.remove_listeners) { @@ -1636,10 +1633,7 @@ export class EntityManager uint j = 0; uint k = 0; - static if (EntityID.sizeof == 8) - uint ind = cast(uint)((cast(void*) entity - block.dataBegin()) >> 3); - else - uint ind = cast(uint)((cast(void*) entity - block.dataBegin()) / EntityID.sizeof()); + uint ind = block.entityIndex(entity); if (info.remove_listeners) { @@ -1761,12 +1755,66 @@ export class EntityManager } /************************************************************************************************************************ - *Add entity to system. + *Add copy of entity to system and returns pointer to it. Added copy has same data as copied entity. Returen pointer is + *valid only before one from commit(), begin() or end() will be called. To save entity to further use you should save ID + *instead of pointer. * *Params: *tmpl = pointer entity template allocated by EntityManager. */ - export ref Entity addEntity(EntityTemplate* tmpl) + export Entity* addEntityCopy(EntityID id) + { + Entity* entity = getEntity(id); + EntitiesBlock* block = getMetaData(entity); + EntityInfo* info = block.type_info; + + ushort index = block.entityIndex(entity); + + ushort new_index = 0; + EntitiesBlock* new_block; + do + { + new_block = findBlockWithFreeSpaceMT(info); + new_index = new_block.added_count.atomicOp!"+="(1); + } + while (new_block.entities_count + new_index > info.max_entities); + + ushort new_id = cast(ushort)(new_block.entities_count + new_index - 1); + const void* data_begin = new_block.dataBegin(); + const void* start = data_begin + EntityID.sizeof * new_id; + + foreach (i, comp; info.components) + { + memcpy(cast(void*) new_block + info.deltas[comp] + components[comp].size * new_id, + cast(void*) block + info.deltas[comp] + components[comp].size * index, components[comp].size); + + if (components[comp].create_callback) + { + components[comp].create_callback( + cast(void*) block + info.deltas[comp] + new_id * components[comp].size); + } + } + + if (new_index == 1) + threads[thread_id].blocks_to_update.add(new_block); + + Entity* new_entity = cast(Entity*) start; + //add_mutex.lock_nothrow(); + new_entity.id = id_manager.getNewID(); + //add_mutex.unlock_nothrow(); + new_entity.updateID(); + + return new_entity; + } + + /************************************************************************************************************************ + *Add entity to system. Returen pointer is valid only before one from commit(), begin() or end() will be called. To save entity to further + *use you should save ID instead of pointer. + * + *Params: + *tmpl = pointer entity template allocated by EntityManager. + */ + export Entity* addEntity(EntityTemplate* tmpl) { EntityInfo* info = tmpl.info; @@ -1806,7 +1854,7 @@ export class EntityManager //add_mutex.unlock_nothrow(); entity.updateID(); - return *entity; + return entity; } /************************************************************************************************************************ @@ -1903,11 +1951,7 @@ export class EntityManager EntityInfo* info = block.type_info; if (info.remove_listeners) { - void* data_begin = block.dataBegin(); - static if (EntityID.sizeof == 8) - uint pos = cast(uint)((cast(void*) entity - data_begin) >> 3); - else - uint pos = cast(uint)((cast(void*) entity - data_begin) / EntityID.sizeof()); + uint pos = block.entityIndex(entity); callRemoveEntityListeners(info, block, pos, pos + 1); } @@ -1920,16 +1964,11 @@ export class EntityManager private void removeEntityNoID(Entity* entity, EntitiesBlock* block, bool call_destructors = false) nothrow @nogc { - //pos is Entity number in block - void* data_begin = block.dataBegin(); EntityInfo* info = block.type_info; info.last_block.entities_count--; - static if (EntityID.sizeof == 8) - uint pos = cast(uint)((cast(void*) entity - data_begin) >> 3); - else - uint pos = cast(uint)((cast(void*) entity - data_begin) / EntityID.sizeof()); + uint pos = block.entityIndex(entity); if (call_destructors) { @@ -2173,14 +2212,7 @@ export class EntityManager EntityID entity_id = *cast(EntityID*) event_pointer; Entity* entity = id_manager.getEntityPointer(entity_id); call_data.block = getMetaData(entity); - - static if (EntityID.sizeof == 8) - call_data.id = cast(ushort)( - (cast(void*) entity - call_data.block.dataBegin()) >> 3); - else - call_data.id = cast(ushort)( - (cast(void*) entity - call_data.block.dataBegin()) / EntityID - .sizeof); + call_data.id = call_data.block.entityIndex(entity); foreach (caller; events[i].callers) { @@ -2580,7 +2612,7 @@ export class EntityManager struct EntitiesBlock { ///return distance (in bytes) from begin of block to data - export uint dataDelta() nothrow @nogc + export uint dataDelta() nothrow @nogc pure { ushort dif = EntitiesBlock.sizeof; alignNum(dif, type_info.alignment); @@ -2588,12 +2620,20 @@ export class EntityManager } ///return pointer to first element in block - export void* dataBegin() nothrow @nogc + export void* dataBegin() nothrow @nogc pure { ushort dif = EntitiesBlock.sizeof; return cast(void*)&this + dif; } + export ushort entityIndex(Entity* entity) nothrow @nogc pure + { + static if (EntityID.sizeof == 8) + return cast(ushort)((cast(void*) entity - dataBegin()) >> 3); + else + return cast(ushort)((cast(void*) entity - dataBegin()) / EntityID.sizeof()); + } + ///pointer to Entity type info EntityInfo* type_info = null; ///number of entities in block diff --git a/tests/tests.d b/tests/tests.d index 3cf3697..1b7e47b 100644 --- a/tests/tests.d +++ b/tests/tests.d @@ -526,13 +526,13 @@ int main() dur = (MonoTime.currTime - time).total!"usecs"; writeln("Template allocating: ", dur, " usecs"); - Entity entity; + EntityID entity; { - entity = gEM.addEntity(tmpl); - writeEntityComponents(gEM.getEntity(entity.id)); + entity = gEM.addEntity(tmpl).id; + writeEntityComponents(gEM.getEntity(entity)); EntityManager.EntitiesBlock* block = EntityManager.instance.getMetaData( - gEM.getEntity(entity.id)); + gEM.getEntity(entity)); EntityManager.EntityInfo* info = block.type_info; writeln(info.add_listeners); //if(info)assert(0); @@ -586,15 +586,15 @@ int main() //assert(*(cast(EntityID*)(cast(void*)tmpl.info.first_block+24)) == EntityID(1,1)); //assert(*(cast(EntityID*)(cast(void*)tmpl.info.first_block+48)) == EntityID(1,1)); - Entity entity2; + EntityID entity2; time = MonoTime.currTime; EntityID[] entities = Mallocator.instance.makeArray!EntityID(1_000_000); foreach (i; 0 .. 500_000) { - entity2 = gEM.addEntity(tmpl); - entities[i*2] = entity2.id; + entity2 = gEM.addEntity(tmpl).id; + entities[i*2] = entity2; entities[i*2+1] = gEM.addEntity(tmpl2).id; } @@ -638,7 +638,7 @@ int main() dur = (MonoTime.currTime - time).total!"usecs"; writeln("Update: ", dur, " usecs"); - writeEntityComponents(gEM.getEntity(entity2.id)); + writeEntityComponents(gEM.getEntity(entity2)); time = MonoTime.currTime; @@ -650,7 +650,7 @@ int main() dur = (MonoTime.currTime - time).total!"usecs"; writeln("Update: ", dur, " usecs"); - writeEntityComponents(gEM.getEntity(entity2.id)); + writeEntityComponents(gEM.getEntity(entity2)); time = MonoTime.currTime; @@ -662,9 +662,9 @@ int main() dur = (MonoTime.currTime - time).total!"usecs"; writeln("Update: ", dur, " usecs"); - writeEntityComponents(gEM.getEntity(entity2.id)); + writeEntityComponents(gEM.getEntity(entity2)); - entity = gEM.addEntity(tmpl); + entity = gEM.addEntity(tmpl).id; gEM.begin(); gEM.update(); @@ -672,40 +672,39 @@ int main() //Entity* pp;// = gEM.getEntity(entity.id); //writeln((cast(uint*) pp)[0 .. 14], " ", pp); - writeEntityComponents(gEM.getEntity(entity.id)); + writeEntityComponents(gEM.getEntity(entity)); gEM.addEntity(tmpl); + gEM.addEntityCopy(entity); - gEM.addComponents(entity.id, TestComp4()); - gEM.addComponents(entity.id, TestComp3()); + gEM.addComponents(entity, TestComp4()); + gEM.addComponents(entity, TestComp3()); gEM.begin(); gEM.update(); gEM.end(); - writeEntityComponents(gEM.getEntity(entity.id)); + writeEntityComponents(gEM.getEntity(entity)); - gEM.removeComponents!(TestComp)(entity.id); - gEM.addComponents(entity.id, TestComp()); - gEM.addComponents(entity.id, TestComp5()); + gEM.removeComponents!(TestComp)(entity); + gEM.addComponents(entity, TestComp()); + gEM.addComponents(entity, TestComp5()); gEM.begin(); gEM.update(); gEM.update("fixed"); gEM.end(); - gEM.removeComponents!(TestComp4)(entity.id); + gEM.removeComponents!(TestComp4)(entity); gEM.commit();//*/ - writeEntityComponents(gEM.getEntity(entity.id)); + writeEntityComponents(gEM.getEntity(entity)); //import std.stdio; //writeln((cast(uint*)tmpl.info.first_block)[0..48]); gEM.freeTemplate(tmpl); gEM.freeTemplate(tmpl2); EntityManager.destroy(); - - return 0; } From 9402e917f239fbb18fb01dfc9b6a4a7210f6707d Mon Sep 17 00:00:00 2001 From: Mergul Date: Sat, 10 Aug 2019 16:32:57 +0000 Subject: [PATCH 4/6] -added function to create EntityTemplate form Entity --- source/ecs/manager.d | 39 +++++++++++++++++++++++++++++++++++++++ tests/tests.d | 10 +++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/source/ecs/manager.d b/source/ecs/manager.d index 6d01d6d..4523e4e 100644 --- a/source/ecs/manager.d +++ b/source/ecs/manager.d @@ -1073,6 +1073,45 @@ export class EntityManager return 1; } + /************************************************************************************************************************ + *Allocate EntityTemplate with all components from entity witch it's data and returns pointer to it. + * + *Params: + *id = ID of entity from which should be created template + *fill_default = if true, components will be filled with default data, instead entity data will be taken + */ + export EntityTemplate* allocateTemplate(EntityID entity_id, bool fill_default = false) + { + Entity* entity = getEntity(entity_id); + EntitiesBlock* block = getMetaData(entity); + EntityInfo* info = block.type_info; + + EntityTemplate* temp = Mallocator.instance.make!EntityTemplate; + temp.entity_data = Mallocator.instance.makeArray!ubyte(info.size); + temp.info = info; + + if(fill_default) + { + //fill components with default data + foreach (comp; info.components) + { + temp.entity_data[info.tmpl_deltas[comp] .. info.tmpl_deltas[comp] + components[comp].size] + = components[comp].init_data; + } + } + else + { + ushort index = block.entityIndex(entity); + foreach (comp; info.components) + { + memcpy(cast(void*) temp.entity_data + info.tmpl_deltas[comp], + cast(void*) block + info.deltas[comp] + components[comp].size * index, components[comp].size); + } + } + + return temp; + } + /************************************************************************************************************************ *Allocate EntityTemplate with specifed components and returns pointer to it. * diff --git a/tests/tests.d b/tests/tests.d index 1b7e47b..4c41220 100644 --- a/tests/tests.d +++ b/tests/tests.d @@ -674,8 +674,14 @@ int main() //writeln((cast(uint*) pp)[0 .. 14], " ", pp); writeEntityComponents(gEM.getEntity(entity)); + writeln("Entity, its copy, and template, and default filled tempalte"); gEM.addEntity(tmpl); - gEM.addEntityCopy(entity); + writeEntityComponents(gEM.getEntity(entity)); + writeEntityComponents(gEM.addEntityCopy(entity)); + EntityTemplate* copy_tempalte = gEM.allocateTemplate(entity); + writeEntityComponents(gEM.addEntity(copy_tempalte)); + EntityTemplate* copy_default_tempalte = gEM.allocateTemplate(entity,true); + writeEntityComponents(gEM.addEntity(copy_default_tempalte)); gEM.addComponents(entity, TestComp4()); gEM.addComponents(entity, TestComp3()); @@ -704,6 +710,8 @@ int main() //writeln((cast(uint*)tmpl.info.first_block)[0..48]); gEM.freeTemplate(tmpl); gEM.freeTemplate(tmpl2); + gEM.freeTemplate(copy_tempalte); + gEM.freeTemplate(copy_default_tempalte); EntityManager.destroy(); return 0; From beab56033c34b783df8e761abd9f69763b96c4e1 Mon Sep 17 00:00:00 2001 From: Mergul Date: Tue, 13 Aug 2019 11:13:26 +0000 Subject: [PATCH 5/6] -events are simplified to handleEvent(Entity*,Event) (use entity.getComponent to get entity components) --- source/ecs/manager.d | 25 +++++++++++++++++++------ tests/tests.d | 16 +++++++++------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/source/ecs/manager.d b/source/ecs/manager.d index 4523e4e..4c30c10 100644 --- a/source/ecs/manager.d +++ b/source/ecs/manager.d @@ -304,9 +304,9 @@ export class EntityManager { static void callEventHandler(Type)(ref EventCallData data) { - Sys.EventInput input; + //Sys.EventInput input; Sys* data_system = cast(Sys*) data.system_pointer; - EntityInfo* info = data.block.type_info; + /*EntityInfo* info = data.block.type_info; alias EventFields = Fields!(Sys.EventInput); foreach (ref event_field; input.tupleof) @@ -343,8 +343,9 @@ export class EntityManager } } - } - data_system.handleEvent(input, *cast(Type*) data.event); + }//*/ + Type* event = cast(Type*) data.event; + data_system.handleEvent(/*input, */gEM.getEntity(event.entity_id), *event); } void setEventCallers(Sys)(ref System system) @@ -357,7 +358,19 @@ export class EntityManager { alias Params = Parameters!(__traits(getOverloads, Sys, "handleEvent")[j]); - static if(Params.length == 2 && is(Params[0] == __traits(getMember, Sys, "EventInput"))) + /*static if(Params.length == 2 && is(Params[0] == __traits(getMember, Sys, "EventInput"))) + { + alias EventParamType = Params[1]; + enum EventName = Unqual!(EventParamType).stringof; + ushort evt = events_map.get(cast(char[]) EventName, ushort.max); + assert(evt != ushort.max, "Can't register system \""~Sys.stringof~"\" due to non existing event \""~EventName~"\"."); + + callers[i].callback = cast( + void*)&callEventHandler!(EventParamType); + callers[i].id = EventParamType.event_id; + i++; + }*/ + static if(Params.length == 2 && is(Params[0] == Entity*)) { alias EventParamType = Params[1]; enum EventName = Unqual!(EventParamType).stringof; @@ -389,9 +402,9 @@ export class EntityManager static struct ComponentsIndices { CompInfo[] readonly; + CompInfo[] mutable; CompInfo[] excluded; CompInfo[] optional; - CompInfo[] mutable; CompInfo[] req; string entites_array; } diff --git a/tests/tests.d b/tests/tests.d index 4c41220..30810e2 100644 --- a/tests/tests.d +++ b/tests/tests.d @@ -376,22 +376,24 @@ struct TestSystem2 //TestComp* tt; } - void handleEvent(EventInput input) + /*void handleEvent(EventInput input) { - } + }*/ - void handleEvent(EventInput input, ref TestEvent event) + void handleEvent(/*EventInput input, */Entity* entity, ref TestEvent event) { - input.test.bg = event.a; + TestComp3* test = entity.getComponent!TestComp3; + test.bg = event.a; TestEvent2 event2; event2.a = event.a + 8; - gEM.sendEvent(input.entity.id, event2); + gEM.sendEvent(entity.id, event2); } - void handleEvent(EventInput input, ref TestEvent2 event) + void handleEvent(/*EventInput input, */Entity* entity, ref TestEvent2 event) { - input.test.gg = cast(uint) event.a; + TestComp3* test = entity.getComponent!TestComp3; + test.gg = cast(uint) event.a; } void onEnable() From ebec25633ef09b79cdb11ceddb062d9aa7af59d8 Mon Sep 17 00:00:00 2001 From: Mergul Date: Tue, 13 Aug 2019 11:58:01 +0000 Subject: [PATCH 6/6] -now event handler system does not have to has EventInput structure --- source/ecs/manager.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/ecs/manager.d b/source/ecs/manager.d index 4c30c10..e7bbb8a 100644 --- a/source/ecs/manager.d +++ b/source/ecs/manager.d @@ -300,7 +300,7 @@ export class EntityManager static assert(0, "System should gave \"EntitiesData\" struct for input components"); } - static if (hasMember!(Sys, "EventInput")) + static if (hasMember!(Sys, "handleEvent")) { static void callEventHandler(Type)(ref EventCallData data) {