diff --git a/dub.json b/dub.json index bacb5fc..c4303f2 100755 --- a/dub.json +++ b/dub.json @@ -10,6 +10,9 @@ "dflags-posix-ldc": [ "-defaultlib=phobos2-ldc,druntime-ldc" ], + "versions": [ + "UpdateBySystems" + ], "configurations" : [ { "name" : "library", diff --git a/source/ecs/manager.d b/source/ecs/manager.d index 95a31d6..168b6a1 100644 --- a/source/ecs/manager.d +++ b/source/ecs/manager.d @@ -89,28 +89,30 @@ class EntityManager { static if (isFunction!(__traits(getMember, Sys.EntitiesData, member))) static assert(0, "EntitiesData can't have any function!"); - else static if(member == "length") + else static if (member == "length") { - static assert(isIntegral!(typeof(__traits(getMember,Sys.EntitiesData, member))),"EntitiesData 'length' member must be integral type."); - static assert(typeof(__traits(getMember,Sys.EntitiesData, member)).sizeof > 1,"EntitiesData 'length' member can't be byte or ubyte."); + static assert(isIntegral!(typeof(__traits(getMember, Sys.EntitiesData, + member))), "EntitiesData 'length' member must be integral type."); + static assert(typeof(__traits(getMember, Sys.EntitiesData, member)) + .sizeof > 1, "EntitiesData 'length' member can't be byte or ubyte."); } else static if (!(isArray!(typeof(__traits(getMember, Sys.EntitiesData, member))))) static assert(0, "EntitiesData members should be arrays of elements!"); } - string ret;// = "ushort comp;uint req;uint opt;uint absent;"; - + string ret; // = "ushort comp;uint req;uint opt;uint absent;"; + uint req; uint opt; uint absent; foreach (member; __traits(allMembers, Sys.EntitiesData)) { - if (member == "length" || is(typeof(__traits(getMember, Sys.EntitiesData, - member)) == Entity[]) || is(typeof(__traits(getMember, + if (member == "length" || is(typeof(__traits(getMember, + Sys.EntitiesData, member)) == Entity[]) || is(typeof(__traits(getMember, Sys.EntitiesData, member)) == const(Entity)[])) { - + } else { @@ -134,52 +136,66 @@ class EntityManager break; } } - if (!has_att)req++; - //ret ~= "req++;"; + if (!has_att) + req++; + //ret ~= "req++;"; } } } - static if(__traits(hasMember, Sys, "AbsentComponents")) + static if (__traits(hasMember, Sys, "AbsentComponents")) { - static if(is(Sys.AbsentComponents == enum)) + static if (is(Sys.AbsentComponents == enum)) { - absent += (Fields!(Sys.AbsentComponents)).length;//static assert(0,"Enum AbsentComponents are not implemented yet."); + absent += (Fields!(Sys.AbsentComponents)).length; //static assert(0,"Enum AbsentComponents are not implemented yet."); } - else static if(__traits(compiles,allSameType!(string,typeof(Sys.AbsentComponents))) && allSameType!(string,typeof(Sys.AbsentComponents)) && - isExpressions!(Sys.AbsentComponents)) + else static if (__traits(compiles, allSameType!(string, typeof(Sys.AbsentComponents))) + && allSameType!(string, typeof(Sys.AbsentComponents)) + && isExpressions!(Sys.AbsentComponents)) { absent += Sys.AbsentComponents.length; } } - if(req > 0)ret ~= "system.m_components = Mallocator.instance.makeArray!ushort("~req.to!string~");"; - if(opt > 0)ret ~= "system.m_optional_components = Mallocator.instance.makeArray!ushort("~opt.to!string~");"; - if(absent > 0)ret ~= "system.m_absent_components = Mallocator.instance.makeArray!ushort("~absent.to!string~");"; - ret ~= "ushort comp;";//uint opt = 0;uint req = 0;uint absent = 0;"; + if (req > 0) + ret ~= "system.m_components = Mallocator.instance.makeArray!ushort(" + ~ req.to!string ~ ");"; + if (opt > 0) + ret ~= "system.m_optional_components = Mallocator.instance.makeArray!ushort(" + ~ opt.to!string ~ ");"; + if (absent > 0) + ret ~= "system.m_absent_components = Mallocator.instance.makeArray!ushort(" + ~ absent.to!string ~ ");"; + ret ~= "ushort comp;"; //uint opt = 0;uint req = 0;uint absent = 0;"; opt = 0; req = 0; absent = 0; - static if(__traits(hasMember, Sys, "AbsentComponents")) + static if (__traits(hasMember, Sys, "AbsentComponents")) { - static if(is(Sys.AbsentComponents == enum)) + static if (is(Sys.AbsentComponents == enum)) { //static assert(0,"Enum AbsentComponents are not implemented yet."); - foreach(str;Fields!(Sys.AbsentComponents))ret ~= "system.m_absent_components["~(absent++).to!string~"] = components_map.get(\""~str.stringof~"\", ushort.max);"; + foreach (str; Fields!(Sys.AbsentComponents)) + ret ~= "system.m_absent_components[" ~ (absent++) + .to!string ~ "] = components_map.get(\"" + ~ str.stringof ~ "\", ushort.max);"; } - else static if(__traits(compiles,allSameType!(string,typeof(Sys.AbsentComponents))) && allSameType!(string,typeof(Sys.AbsentComponents)) && - isExpressions!(Sys.AbsentComponents)) + else static if (__traits(compiles, allSameType!(string, typeof(Sys.AbsentComponents))) + && allSameType!(string, typeof(Sys.AbsentComponents)) + && isExpressions!(Sys.AbsentComponents)) { - foreach(str;Sys.AbsentComponents)ret ~= "system.m_absent_components["~(absent++).to!string~"] = components_map.get(\""~str~"\", ushort.max);"; + foreach (str; Sys.AbsentComponents) + ret ~= "system.m_absent_components[" ~ (absent++) + .to!string ~ "] = components_map.get(\"" ~ str ~ "\", ushort.max);"; } } foreach (member; __traits(allMembers, Sys.EntitiesData)) { - if (member == "length" || is(typeof(__traits(getMember, Sys.EntitiesData, - member)) == Entity[]) || is(typeof(__traits(getMember, + if (member == "length" || is(typeof(__traits(getMember, + Sys.EntitiesData, member)) == Entity[]) || is(typeof(__traits(getMember, Sys.EntitiesData, member)) == const(Entity)[])) { @@ -204,20 +220,22 @@ class EntityManager { if (att == "optional") { - ret ~= "system.m_optional_components["~(opt++).to!string~"] = comp;}"; + ret ~= "system.m_optional_components[" ~ (opt++) + .to!string ~ "] = comp;}"; has_att = true; break; } else if (att == "absent") { - ret ~= "system.m_absent_components["~(absent++).to!string~"] = comp;}"; + ret ~= "system.m_absent_components[" ~ (absent++) + .to!string ~ "] = comp;}"; has_att = true; break; } } if (!has_att) { - ret ~= "system.m_components["~(req++).to!string~"] = comp;}"; + ret ~= "system.m_components[" ~ (req++).to!string ~ "] = comp;}"; } } } @@ -256,10 +274,9 @@ class EntityManager ret ~= "input_data." ~ member ~ " = (cast(Entity*) block.dataBegin())[0 .. block.entities_count];"; } - else if(member == "length") + else if (member == "length") { - ret ~= "input_data." ~ member - ~ " = block.entities_count;"; + ret ~= "input_data." ~ member ~ " = block.entities_count;"; } else { @@ -392,6 +409,28 @@ class EntityManager addEntityCaller(*info, cast(uint) systems.length - 1); } } + + version (UpdateBySystems) + { + bool added = false; + foreach (i, ref caller; system_callers) + { + if (systems[caller.system_id].priority > priority) + { + SystemCaller* sys_caller = Mallocator.instance.make!SystemCaller; + sys_caller.system_id = Sys.system_id; + system_callers.add(sys_caller, i); + added = true; + break; + } + } + if (!added) + { + SystemCaller* sys_caller = Mallocator.instance.make!SystemCaller; + sys_caller.system_id = Sys.system_id; + system_callers.add(sys_caller); + } + } } updateEntityCallers(); @@ -492,12 +531,34 @@ class EntityManager */ export void update() { - foreach (info; &entities_infos.byValue) + version (UpdateBySystems) { - foreach (data; info.callers) + foreach (caller; system_callers) { - if (data.system.enabled) - (cast(SytemFuncType) data.system.m_update)(data); //caller(call_data,null); + System* sys = &systems[caller.system_id]; + if (sys.enabled) + { + if (sys.m_begin) + sys.m_begin(sys.m_system_pointer); + foreach (info; caller.infos) + { + CallData data = CallData(caller.system_id, sys, info); + (cast(SytemFuncType) data.system.m_update)(data); + } + if (sys.m_end) + sys.m_end(sys.m_system_pointer); + } + } + } + else + { + foreach (info; &entities_infos.byValue) + { + foreach (data; info.callers) + { + if (data.system.enabled) + (cast(SytemFuncType) data.system.m_update)(data); //caller(call_data,null); + } } } } @@ -630,6 +691,16 @@ class EntityManager addEntityCaller(*info, i); } + version (UpdateBySystems) + { + foreach (uint i, ref system; systems) + { + if (system.m_update is null) + continue; + addSystemCaller(*info, i); + } + } + updateEntityCallers(); entities_infos.add(info.components, info); @@ -648,18 +719,19 @@ class EntityManager } } - export void addEntityCaller(ref EntityInfo entity, uint system_id) + export void addSystemCaller(ref EntityInfo entity, uint system_id) { System* system = &systems[system_id]; - CallData call_data = CallData(system_id, system, &entity); + //CallData call_data = CallData(system_id, system, &entity); - if(system.m_absent_components) + if (system.m_absent_components) { foreach (id; system.m_absent_components) { foreach (id2; entity.components) { - if(id == id2)return; + if (id == id2) + return; } } } @@ -668,10 +740,56 @@ class EntityManager { foreach (i2, id2; entity.components) { - if (id2 == id)goto is_; + if (id2 == id) + goto is_; } return; - is_: + is_: + } + + uint index = 0; + for (; index < system_callers.length; index++) + { + if (system_callers[index].system_id == system_id) + break; + } + + system_callers[index].infos.add(&entity); + /*for (; index < entity.callers.length; index++) + { + CallData* caller = &entity.callers[index]; + if (caller.system.priority >= call_data.system.priority) + break; + } + entity.callers.add(call_data, index);*/ + } + + export void addEntityCaller(ref EntityInfo entity, uint system_id) + { + System* system = &systems[system_id]; + CallData call_data = CallData(system_id, system, &entity); + + if (system.m_absent_components) + { + foreach (id; system.m_absent_components) + { + foreach (id2; entity.components) + { + if (id == id2) + return; + } + } + } + + foreach (id; system.m_components) + { + foreach (i2, id2; entity.components) + { + if (id2 == id) + goto is_; + } + return; + is_: } uint index = 0; @@ -1002,7 +1120,7 @@ class EntityManager private EntitiesBlock* findBlockWithFreeSpace(EntityInfo* info) { EntitiesBlock* previous_block; - EntitiesBlock* block = info.first_with_free_space; + EntitiesBlock* block = info.last_block; while (1) { @@ -1021,7 +1139,7 @@ class EntityManager block.prev_block = previous_block; block.id = cast(ushort)(previous_block.id + 1); } - info.first_with_free_space = block; + info.last_block = block; break; // new block certainly has free space } // check if there is enought space @@ -1034,7 +1152,6 @@ class EntityManager continue; } - info.first_with_free_space = block; break; // block exists and bounds check passed } @@ -1071,11 +1188,7 @@ class EntityManager void* data_begin = block.dataBegin(); EntityInfo* info = block.type_info; - block.entities_count--; - - //set "first_with_free_space" if should it be - if (info.first_with_free_space.id > block.id) - info.first_with_free_space = block; + info.last_block.entities_count--; static if (EntityID.sizeof == 8) uint pos = cast(uint)((cast(void*) entity - data_begin) >> 3); @@ -1096,43 +1209,36 @@ class EntityManager } } + if(block !is info.last_block || pos != block.entities_count) + { + foreach (comp; info.components) + { + void* src = cast(void*) info.last_block + info.deltas[comp]; + void* dst = cast(void*) block + info.deltas[comp]; + uint size = components[comp].size; + memcpy(dst + pos * size, src + info.last_block.entities_count * size, size); + } + + block = info.last_block; + entity.id = *cast(EntityID*)(block.dataBegin() + block.entities_count * EntityID.sizeof); + + entity.updateID(); + } + + block = info.last_block; if (block.entities_count == 0) { + info.last_block = block.prev_block; if (info.first_block is block) { - info.first_block = block.next_block; - } - if (info.first_with_free_space is block) - { - info.first_with_free_space = block.next_block; //info.first_block; + info.first_block = null; } if (block.prev_block) { - block.prev_block.next_block = block.next_block; - } - if (block.next_block) - { - block.next_block.prev_block = block.prev_block; - } + block.prev_block.next_block = null; + } allocator.freeBlock(block); - return; } - - if (pos == block.entities_count) - return; - - foreach (comp; info.components) - { - void* ptr = cast(void*) block + info.deltas[comp]; - uint size = components[comp].size; - memcpy(ptr + pos * size, ptr + block.entities_count * size, size); - } - - entity.id = *cast(EntityID*)(data_begin + block.entities_count * EntityID.sizeof); - - //update pointer for moved entity ID - //entity = cast(Entity*) dst; - entity.updateID(); } /************************************************************************************************************************ @@ -1211,10 +1317,18 @@ class EntityManager updateBlocks(); removeEntities(); changeEntites(); - foreach (ref system; instance.systems) + + version (UpdateBySystems) { - if (system.m_begin) - system.m_begin(system.m_system_pointer); + + } + else + { + foreach (ref system; instance.systems) + { + if (system.m_begin) + system.m_begin(system.m_system_pointer); + } } } @@ -1223,11 +1337,19 @@ class EntityManager */ export void end() { - foreach (ref system; instance.systems) + version (UpdateBySystems) { - if (system.m_end) - system.m_end(system.m_system_pointer); + } + else + { + foreach (ref system; instance.systems) + { + if (system.m_end) + system.m_end(system.m_system_pointer); + } + } + updateBlocks(); removeEntities(); changeEntites(); @@ -1262,6 +1384,12 @@ class EntityManager */ struct EntityInfo { + ///Returns number of blocks + uint blocksCount() + { + return last_block.id; + } + ///entity components ushort[] components; @@ -1279,8 +1407,8 @@ class EntityManager ///pointer to first block/page EntitiesBlock* first_block; - ///a hint for allocations - EntitiesBlock* first_with_free_space; // a hint for allocations, should have empty space in it but doesn't have to + ///pointer to last block + EntitiesBlock* last_block; ///array of CallData. Contain data for System calls. Vector!(CallData) callers; } @@ -1335,6 +1463,15 @@ class EntityManager EntityManager.EntityInfo* info; } + struct SystemCaller + { + uint system_id; + System* system; + Vector!(EntityInfo*) infos; + } + + Vector!(SystemCaller*) system_callers; + alias SytemFuncType = void function(ref EntityManager.CallData data); //alias sendSelfEvent = instance.event_manager.sendSelfEvent; @@ -1357,7 +1494,7 @@ class EntityManager Vector!ubyte change_entities_list; HashMap!(ushort[], EntityInfo*) entities_infos; - HashMap!(const (char)[], ushort) systems_map; + HashMap!(const(char)[], ushort) systems_map; HashMap!(string, ushort) components_map; HashMap!(string, ushort) events_map; Vector!System systems; diff --git a/tests/tests.d b/tests/tests.d index 86c48fb..79a76a6 100644 --- a/tests/tests.d +++ b/tests/tests.d @@ -324,14 +324,14 @@ import std.meta; //foreach(i; 0..1_000_000)gEM.removeEntity(gEM.addEntity(tmpl).id); - EntityID[1000] idss; + EntityID[5000] idss; - foreach (i; 0 .. 1_000) + foreach (i; 0 .. 200) { gEM.begin(); - foreach (j; 0 .. 1_000) + foreach (j; 0 .. 5_000) idss[j] = gEM.addEntity(tmpl).id; - foreach (j; 0 .. 1_000) + foreach (j; 0 .. 5_000) gEM.removeEntity(idss[j]); gEM.end(); }