module ecs.manager; import std.algorithm : max; import std.conv : to; import std.experimental.allocator; import std.experimental.allocator.mallocator : AlignedMallocator, Mallocator; import std.traits; import core.stdc.stdlib; import core.stdc.string; import ecs.entity; import ecs.block_allocator; import ecs.hash_map; import ecs.id_manager; import ecs.system; import ecs.vector; import ecs.events; alias gEM = EntityManager.instance; alias SerializeVector = ecs.vector.Vector!ubyte; class EntityManager { export static void initialize() { if (instance is null) instance = Mallocator.instance.make!EntityManager; } export static void destroy() { if (instance is null) return; foreach (ref system; instance.systems) { system.disable(); } foreach (ref system; instance.systems) { if (system.m_destroy) system.m_destroy(system.m_system_pointer); } Mallocator.instance.dispose(instance); instance = null; } this() { //event_manager = EventManager(this); //event_manager.manager = this; } void registerSystem(Sys)(int priority) { alias STC = ParameterStorageClass; System system; static if (!(hasMember!(Sys, "system_id")) || !is(typeof(Sys.system_id) == ushort)) { static assert(0, "System should have \"__gshared ushort system_id"); } static if (hasMember!(Sys, "update")) { alias types = Parameters!(Sys.update); alias storages = ParameterStorageClassTuple!(Sys.update); static string genCall()() { string ret; version (LinearLayout) { { uint i = 0; uint opt = 0; static foreach (param; (Parameters!(Sys.update))[1 .. $]) { i++; if (isPointer!param) { ret ~= "if(opt_array" ~ opt.to!string ~ " !is null)opt_ptr" ~ opt.to!string ~ " = &opt_array" ~ opt.to!string ~ "[i];"; opt++; } } } } /*foreach (i; 1 .. (Parameters!(Sys.update)).length) { ret ~= "*cast(types[" ~ i.to!string ~ "]*)(pointers[" ~ (i - 1) .to!string ~ "]),"; }*/ uint i = 0; uint req = 0; uint opt = 0; version (LinearLayout) { ret ~= "s.update(id_array[i],"; static foreach (param; (Parameters!(Sys.update))[1 .. $]) { i++; if (isPointer!param) { ret ~= "opt_ptr" ~ (opt++).to!string ~ ","; } else { ret ~= "array" ~ (req++).to!string ~ "[i],"; } } } else { ret ~= "s.update(*cast(Entity*)data_pointer,"; static foreach (param; (Parameters!(Sys.update))[1 .. $]) { i++; if (isPointer!param) { ret ~= "cast(types[" ~ i.to!string ~ "])(optional_pointers[" ~ (opt++) .to!string ~ "]),"; } else { ret ~= "*cast(types[" ~ i.to!string ~ "]*)(pointers[" ~ (req++) .to!string ~ "]),"; } } } ret ~= ");"; return ret; } static string genArrays()() { string ret; uint i = 0; uint req = 0; uint opt = 0; static foreach (param; (Parameters!(Sys.update))[1 .. $]) { i++; if (isPointer!param) { ret ~= "PointerTarget!(types[" ~ i.to!string ~ "])[] opt_array" ~ opt.to!string ~ " = null;"; ret ~= "if(info.deltas[types[" ~ i.to!string ~ "].component_id] != 0)opt_array" ~ opt.to!string ~ " = (cast(types[" ~ i.to!string ~ "])(cast(void*)block + info.deltas[types[" ~ i.to!string ~ "].component_id]))[0..block.entities_count];"; ret ~= "types[" ~ i.to!string ~ "] opt_ptr" ~ opt.to!string ~ ";"; opt++; } else { ret ~= "types[" ~ i.to!string ~ "][] array" ~ req.to!string ~ " = (cast(types[" ~ i.to!string ~ "]*)(cast(void*)block + info.deltas[types[" ~ i.to!string ~ "].component_id]))[0..block.entities_count];"; req++; } } return ret; } static string genCompList()() { string ret = "ushort comp;uint req;uint opt;"; foreach (i; 1 .. (Parameters!(Sys.update)).length) { ret ~= " static if(isPointer!(types[" ~ i.to!string ~ "]))opt++; else static if(storages[" ~ i.to!string ~ "] == STC.ref_)req++;\n else static assert(0,\"Can't register system \\\"" ~ Sys.stringof ~ "\\\". Unsupported parameter type \\\"\"~types[" ~ i.to!string ~ "].stringof~\"\\\".\");"; } ret ~= "system.m_components = Mallocator.instance.makeArray!ushort(req);"; ret ~= "system.m_optional_components = Mallocator.instance.makeArray!ushort(opt);"; ret ~= "opt = 0;req = 0;"; foreach (i; 1 .. (Parameters!(Sys.update)).length) { ret ~= " static if(isPointer!(types[" ~ i.to!string ~ "])) { comp = components_map.get(PointerTarget!(types[" ~ i.to!string ~ "]).stringof, ushort.max);\n if(comp == ushort.max)assert(0,\"Can't register system \\\"" ~ Sys.stringof ~ "\\\" due to non existing component \\\"\"~types[" ~ i.to!string ~ "].stringof~\"\\\".\"); system.m_optional_components[opt++] = comp; } else static if(storages[" ~ i.to!string ~ "] == STC.ref_) { comp = components_map.get(types[" ~ i.to!string ~ "].stringof, ushort.max);\n if(comp == ushort.max)assert(0,\"Can't register system \\\"" ~ Sys.stringof ~ "\\\" due to non existing component \\\"\"~types[" ~ i.to!string ~ "].stringof~\"\\\".\"); system.m_components[req++] = comp; }"; } return ret; } static void callUpdate(ref CallData data, void* entity) { static if (hasMember!(Sys, "update")) { Sys* s = cast(Sys*) data.system.m_system_pointer; void*[] pointers = (cast(void**) alloca(data.system.m_components.length * (void*) .sizeof))[0 .. data.system.m_components.length]; void*[] optional_pointers = (cast(void**) alloca( data.system.m_optional_components.length * (void*).sizeof))[0 .. data.system.m_optional_components.length]; EntitiesBlock* block = data.info.first_block; while (block !is null) { version (LinearLayout) { //uint size = block.type_info.size; /*void* data_pointer = block.dataBegin(); foreach (i, ref pointer; pointers) { pointer = cast(void*) block + data.deltas[i]; } foreach (i, ref pointer; optional_pointers) { uint ind = cast(uint)(i + pointers.length); if (data.deltas[ind] != uint.max) pointer = cast(void*) block + data.deltas[ind]; else pointer = null; }*/ EntityInfo* info = block.type_info; Entity[] id_array = (cast(Entity*) block.dataBegin())[0 .. block.entities_count]; mixin(genArrays()); foreach (i; 0 .. block.entities_count) { mixin(genCall()); //data_pointer += EntityID.sizeof; //data.info.size; /*foreach (ref pointer; pointers) pointer += size; foreach (ref pointer; optional_pointers) if (pointer != null) pointer += size;*/ } } else { uint size = block.type_info.size; void* data_pointer = block.dataBegin(); foreach (i, ref pointer; pointers) { pointer = data_pointer + data.deltas[i]; } foreach (i, ref pointer; optional_pointers) { uint ind = cast(uint)(i + pointers.length); if (data.deltas[ind] != 0) pointer = data_pointer + data.deltas[ind]; else pointer = null; } foreach (i; 0 .. block.entities_count) { mixin(genCall()); data_pointer += size; //data.info.size; foreach (ref pointer; pointers) pointer += size; foreach (ref pointer; optional_pointers) if (pointer != null) pointer += size; } } block = block.next_block; } } } system.m_update = &callUpdate; } static string catchFunc()(string member, string func) { string ret = "static if (hasMember!(Sys, \"" ~ func ~ "\")) { static void call" ~ func ~ "(void* system_pointer) { Sys* s = cast(Sys*) system_pointer; s." ~ func ~ "(); } system." ~ member ~ " = &call" ~ func ~ "; }"; return ret; } mixin(catchFunc("m_enable", "onEnable")); mixin(catchFunc("m_disable", "onDisable")); mixin(catchFunc("m_create", "onCreate")); mixin(catchFunc("m_destroy", "onDestroy")); mixin(catchFunc("m_begin", "onBegin")); mixin(catchFunc("m_end", "onEnd")); system.m_system_pointer = cast(void*) Mallocator.instance.make!Sys; system.m_priority = priority; //system.m_components = Mallocator.instance.makeArray!uint(types.length - 1); static if (hasMember!(Sys, "update")) { mixin(genCompList()); } ushort sys_id = systems_map.get(Sys.stringof, ushort.max); if (sys_id < systems.length) { system.enable(); /*if (systems[sys_id].m_destroy) systems[sys_id].m_destroy(systems[sys_id].m_system_pointer);*/ if (system.m_create) system.m_create(system.m_system_pointer); systems[sys_id] = system; Sys.system_id = sys_id; } else { string name = Mallocator.instance.makeArray(Sys.stringof); systems_map.add(name, cast(ushort) systems.length); systems.add(system); if (system.m_create) system.m_create(system.m_system_pointer); systems[$ - 1].enable(); Sys.system_id = cast(ushort)(systems.length - 1); foreach (info; &entities_infos.byValue) { addEntityCaller(*info, cast(uint) systems.length - 1); } } updateEntityCallers(); } System* getSystem(ushort id) { return &systems[id]; } Sys* getSystem(Sys)() { return cast(Sys*) systems[Sys.system_id].m_system_pointer; } void registerComponent(Comp)() { ComponentInfo info; static if (!(hasMember!(Comp, "component_id")) || !is(typeof(Comp.component_id) == ushort)) { static assert(0, "Component should have \"__gshared ushort component_id"); } static if (hasMember!(Comp, "onDestroy") && isFunction!(Comp.onDestroy) && is(ReturnType!(Comp.onDestroy) == void) && Parameters!(Comp.onDestroy).length == 0) { static void callDestroy(void* pointer) { (cast(Comp*) pointer).onDestroy(); } info.destroy_callback = &callDestroy; } info.size = Comp.sizeof; info.alignment = Comp.alignof; //8; info.init_data = Mallocator.instance.makeArray!ubyte(Comp.sizeof); *cast(Comp*) info.init_data.ptr = Comp.init; // = Comp(); ushort comp_id = components_map.get(Comp.stringof, ushort.max); if (comp_id < components.length) { Comp.component_id = comp_id; components[comp_id] = info; } else { components.add(info); Comp.component_id = cast(ushort)(components.length - 1); string name = Mallocator.instance.makeArray(Comp.stringof); components_map.add(name, cast(ushort)(components.length - 1)); } } void registerEvent(Ev)() { EventInfo info; static if (!(hasMember!(Ev, "event_id")) || !is(typeof(Ev.event_id) == ushort)) { static assert(0, "Event should have \"__gshared ushort event_id"); } static if (hasMember!(Ev, "onDestroy") && isFunction!(Ev.onDestroy) && is(ReturnType!(Ev.onDestroy) == void) && Parameters!(Ev.onDestroy).length == 0) { static void callDestroy(void* pointer) { (cast(Ev*) pointer).onDestroy(); } info.destroy_callback = &callDestroy; } info.size = Ev.sizeof; info.alignment = Ev.alignof; ushort event_id = events_map.get(Ev.stringof, ushort.max); if (event_id < events.length) { Ev.event_id = event_id; } else { events.add(info); Ev.event_id = cast(ushort)(events.length - 1); events_map.add(Ev.stringof, cast(ushort)(events.length - 1)); } } export void update() { foreach (info; &entities_infos.byValue) { foreach (data; info.callers) { if (data.system.enabled) (cast(SytemFuncType) data.system.m_update)(data, null); //caller(call_data,null); } } } static void alignNum(ref ushort num, ushort alignment) { num = cast(ushort)((num + alignment - 1) & (-cast(int) alignment)); //num += alignment - (num & (alignment - 1)); } static ushort alignedNum(ushort num, ushort alignment) { return cast(ushort)((num + alignment - 1) & (-cast(int) alignment)); } extern (C) static int compareUShorts(const void* a, const void* b) { ushort _a = *cast(ushort*) a; ushort _b = *cast(ushort*) b; if (_a < _b) return -1; else if (_a == _b) return 0; else return 1; } export EntityTemplate* allocateTemplate(ushort[] components_ids) { ushort[] ids = (cast(ushort*) alloca(ushort.sizeof * components_ids.length))[0 .. components_ids.length]; ids[0 .. $] = components_ids[]; qsort(ids.ptr, ids.length, ushort.sizeof, &compareUShorts); { uint j = 1; foreach (i; 1 .. ids.length) { if (ids[i] != ids[j - 1]) { ids[j] = ids[i]; j++; } else debug assert(0, "Duplicated components in template!!!"); } ids = ids[0 .. j]; } EntityInfo* info = getEntityInfo(ids); EntityTemplate* temp = Mallocator.instance.make!EntityTemplate; temp.entity_data = Mallocator.instance.makeArray!ubyte(info.size); temp.info = info; //fill components with default data version (LinearLayout) { foreach (comp; info.components) { temp.entity_data[info.tmpl_deltas[comp] .. info.tmpl_deltas[comp] + components[comp].size] = components[comp].init_data; } } else { foreach (comp; info.components) { temp.entity_data[info.deltas[comp] .. info.deltas[comp] + components[comp].size] = components[comp].init_data; } } return temp; } export EntityInfo* getEntityInfo(ushort[] ids) { EntityInfo* info = entities_infos.get(ids, null); if (info is null) { info = Mallocator.instance.make!EntityInfo; info.components = Mallocator.instance.makeArray(ids); info.deltas = Mallocator.instance.makeArray!ushort(ids[$ - 1] + 1); info.size = EntityID.sizeof; info.alignment = EntityID.alignof; version (LinearLayout) { info.tmpl_deltas = Mallocator.instance.makeArray!ushort(ids[$ - 1] + 1); uint components_size = EntityID.sizeof; foreach (i, id; ids) { info.alignment = max(info.alignment, components[id].alignment); alignNum(info.size, components[id].alignment); info.tmpl_deltas[id] = info.size; info.size += components[id].size; components_size += components[id].size; } alignNum(info.size, info.alignment); /**/ uint block_memory = cast(uint)( page_size - EntitiesBlock.sizeof - (info.size - components_size)); //uint entity_comps_size = EntityID.sizeof; uint mem_begin = EntitiesBlock.sizeof; /*foreach (id; ids) { entity_comps_size += components[id].size; }*/ uint entites_in_block = block_memory / info.size; //entity_comps_size; info.max_entities = cast(ushort) entites_in_block; ushort current_delta = cast(ushort)(mem_begin + entites_in_block * EntityID.sizeof); foreach (i, id; ids) { alignNum(current_delta, components[id].alignment); info.deltas[id] = cast(ushort) current_delta; current_delta += entites_in_block * components[id].size; } } else { foreach (i, id; ids) { info.alignment = max(info.alignment, components[id].alignment); alignNum(info.size, components[id].alignment); info.deltas[id] = info.size; info.size += components[id].size; } alignNum(info.size, info.alignment); info.max_entities = (page_size - EntitiesBlock.sizeof) / info.size; } foreach (uint i, ref system; systems) { if (system.m_update is null) continue; addEntityCaller(*info, i); } updateEntityCallers(); entities_infos.add(info.components, info); } return info; } export void updateEntityCallers() { foreach (entity; &entities_infos.byValue) { foreach (ref caller; entity.callers) { caller.system = &systems[caller.system_id]; } } } export void addEntityCaller(ref EntityInfo entity, uint system_id) { System* system = &systems[system_id]; CallData call_data = CallData(system_id, system, &entity, null); uint num = cast(uint)(system.m_components.length + system.m_optional_components.length); ushort[] deltas = (cast(ushort*) alloca(num * ushort.sizeof))[0 .. num]; uint delta_id = 0; foreach (id; system.m_components) { deltas[delta_id] = ushort.max; foreach (i2, id2; entity.components) { if (id2 == id) { deltas[delta_id] = entity.deltas[id2]; break; } } if (deltas[delta_id] == ushort.max) { deltas = null; break; } delta_id++; } if (deltas is null) return; foreach (id; system.m_optional_components) { deltas[delta_id] = ushort.max; foreach (i2, id2; entity.components) { if (id2 == id) { deltas[delta_id] = entity.deltas[id2]; break; } } /*if (deltas[delta_id] == ushort.max) { deltas = null; break; }*/ delta_id++; } call_data.deltas = Mallocator.instance.makeArray(deltas); //Mallocator.instance.makeArray!ushort(system.m_components.length); uint index = 0; 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 Entity* getEntity(EntityID id) { return cast(Entity*) id_manager.getEntityPointer(id); } export void removeComponents(EntityID entity_id, ushort[] del_ids) { uint num = cast(uint) del_ids.length; change_entities_list.add(0); change_entities_list.add((cast(ubyte*)&entity_id)[0 .. 8]); change_entities_list.add((cast(ubyte*)&num)[0 .. 4]); change_entities_list.add(cast(ubyte[]) del_ids); } private void __removeComponents(EntityID entity_id, ushort[] del_ids) { Entity* entity = id_manager.getEntityPointer(entity_id); if (!entity) return; EntitiesBlock* block = getMetaData(entity); EntityInfo* info = block.type_info; qsort(del_ids.ptr, del_ids.length, ushort.sizeof, &compareUShorts); ushort[] ids = (cast(ushort*) alloca(ushort.sizeof * (info.components.length)))[0 .. info.components.length]; uint j = 0; uint k = 0; foreach (id; info.components) { while (k < del_ids.length && del_ids[k] < id) k++; if (k >= del_ids.length) { ids[j++] = id; } else if (del_ids[k] == info.components[j]) { k++; } else ids[j++] = id; } if (j == info.components.length) return; EntityInfo* new_info = getEntityInfo(ids[0 .. j]); EntitiesBlock* new_block = findBlockWithFreeSpace(new_info); version (LinearLayout) void* start = new_block.dataBegin() + new_block.entities_count * EntityID.sizeof; else void* start = new_block.dataBegin() + new_block.entities_count * new_info.size; Entity* new_entity = cast(Entity*) start; new_entity.id = entity.id; new_entity.updateID(); version (LinearLayout) { 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()); foreach (comp; new_info.components) { uint comp_size = components[comp].size; memcpy(cast(void*) new_block + new_info.deltas[comp] + new_block.entities_count * comp_size, cast(void*) block + info.deltas[comp] + ind * comp_size, comp_size); } } else { foreach (comp; new_info.components) { memcpy(cast(void*) new_entity + new_info.deltas[comp], cast(void*) entity + info.deltas[comp], components[comp].size); } } new_block.entities_count++; removeEntityNoID(entity, block); } void removeComponents(Components...)(EntityID entity_id) { const uint num = Components.length; ushort[num] del_ids; static foreach (i, comp; Components) { del_ids[i] = comp.component_id; } /*change_entities_list.add(0); change_entities_list.add((cast(ubyte*)&entity_id)[0..8]); change_entities_list.add((cast(ubyte*)&num)[0..4]); change_entities_list.add(cast(ubyte[])del_ids);*/ removeComponents(entity_id, del_ids); } private void __addComponents(EntityID entity_id, ushort[] new_ids, void*[] data_pointers) { uint num = cast(uint) new_ids.length; Entity* entity = id_manager.getEntityPointer(entity_id); if (!entity) return; EntitiesBlock* block = getMetaData(entity); EntityInfo* info = block.type_info; ushort[] ids = (cast(ushort*) alloca(ushort.sizeof * (info.components.length + num)))[0 .. info.components.length + num]; /*ushort[num] new_ids; static foreach (i, comp; Components) { new_ids[i] = comp.component_id; }*/ /*void*[num] pointers; static foreach (i, comp; comps) { pointers[i] = ∁ }*/ foreach (int i; 0 .. num) { ushort min = new_ids[i]; int pos = i; foreach (int j; i .. num) { if (new_ids[j] < min) { min = new_ids[j]; pos = j; } } if (pos != i) { ushort id = new_ids[i]; new_ids[i] = new_ids[pos]; new_ids[pos] = id; void* ptr = data_pointers[i]; data_pointers[i] = data_pointers[pos]; data_pointers[pos] = ptr; } } uint j = 0; uint k = 0; foreach (ref id; ids) { if (k >= new_ids.length) { id = info.components[j++]; continue; } if (j >= info.components.length) { id = new_ids[k++]; continue; } debug if (new_ids[k] == info.components[j]) assert(0, "Trying to add already existing component!"); if (new_ids[k] < info.components[j]) { id = new_ids[k++]; } else id = info.components[j++]; } EntityInfo* new_info = getEntityInfo(ids); EntitiesBlock* new_block = findBlockWithFreeSpace(new_info); //removeEntityNoID(entity, block); version (LinearLayout) void* start = new_block.dataBegin() + new_block.entities_count * EntityID.sizeof; else void* start = new_block.dataBegin() + new_block.entities_count * new_info.size; Entity* new_entity = cast(Entity*) start; new_entity.id = entity.id; new_entity.updateID(); j = 0; k = 0; version (LinearLayout) { 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()); foreach (ref id; ids) { void* dst = cast(void*) new_block + new_info.deltas[id] + ( new_block.entities_count + new_block.added_count) * components[id].size; uint size = components[id].size; if (k >= new_ids.length) { memcpy(dst, cast(void*) block + info.deltas[id] + ind * size, size); j++; } else if (j >= info.components.length) { memcpy(dst, data_pointers[k], size); k++; } else if (id == new_ids[k]) { memcpy(dst, data_pointers[k], size); k++; } else { memcpy(dst, cast(void*) block + info.deltas[id] + ind * size, size); j++; } } } else { foreach (ref id; ids) { void* dst = cast(void*) new_entity + new_info.deltas[id]; uint size = components[id].size; if (k >= new_ids.length) { memcpy(dst, cast(void*) entity + info.deltas[id], size); j++; } else if (j >= info.components.length) { memcpy(dst, data_pointers[k], size); k++; } else if (id == new_ids[k]) { memcpy(dst, data_pointers[k], size); k++; } else { memcpy(dst, cast(void*) entity + info.deltas[id], size); j++; } } } new_block.entities_count++; removeEntityNoID(entity, block); } void addComponents(Components...)(EntityID entity_id, Components comps) { const uint num = Components.length; Entity* entity = id_manager.getEntityPointer(entity_id); EntitiesBlock* block = getMetaData(entity); EntityInfo* info = block.type_info; ushort[] ids = (cast(ushort*) alloca(ushort.sizeof * (info.components.length + num)))[0 .. info.components.length + num]; ushort[num] new_ids; static foreach (i, comp; Components) { new_ids[i] = comp.component_id; } /*void*[num] pointers; static foreach (i, comp; comps) { pointers[i] = ∁ }*/ change_entities_list.add(1); change_entities_list.add((cast(ubyte*)&entity_id)[0 .. 8]); change_entities_list.add((cast(ubyte*)&num)[0 .. 4]); change_entities_list.add(cast(ubyte[]) new_ids); static foreach (i, comp; comps) { change_entities_list.add((cast(ubyte*)&comp)[0 .. comp.sizeof]); } //__addComponents(entity_id, new_ids, pointers); } export void freeTemplate(EntityTemplate* template_) { Mallocator.instance.dispose(template_.entity_data); Mallocator.instance.dispose(template_); } export ref Entity addEntity(EntityTemplate* tmpl) { EntitiesBlock* block = findBlockWithFreeSpace(tmpl.info); uint id = (block.entities_count + block.added_count); EntityInfo* info = tmpl.info; version (LinearLayout) { void* data_begin = block.dataBegin(); void* start = data_begin + EntityID.sizeof * id; //memcpy(data_begin + EntityID.sizeof * id, tmpl.entity_data.ptr, EntityID.sizeof); foreach (i, comp; info.components) { memcpy(cast(void*) block + info.deltas[comp] + components[comp].size * id, tmpl.entity_data.ptr + info.tmpl_deltas[comp], components[comp].size); } } else { void* start = block.dataBegin() + id * info.size; memcpy(start, tmpl.entity_data.ptr, info.size); } if (!block.added_count) blocks_to_update.add(block); Entity* entity = cast(Entity*) start; entity.id = id_manager.getNewID(); entity.updateID(); block.added_count++; //block.entities_count++; return *entity; } private EntitiesBlock* findBlockWithFreeSpace(EntityInfo* info) { EntitiesBlock* previous_block; EntitiesBlock* block = info.first_with_free_space; while (1) { if (block is null) { block = cast(EntitiesBlock*) allocator.getBlock(); //AlignedMallocator.instance.alignedAllocate(page_size, page_size); *block = EntitiesBlock(info); if (previous_block is null) { info.first_block = block; block.id = 0; } else { previous_block.next_block = block; block.prev_block = previous_block; block.id = cast(ushort)(previous_block.id + 1); } info.first_with_free_space = block; break; // new block certainly has free space } // check if there is enought space /*if (block.dataDelta() + ( block.entities_count + block.added_count + 1) * info.size > page_size)*/ if (block.entities_count + block.added_count >= block.type_info.max_entities) { previous_block = block; block = block.next_block; continue; } info.first_with_free_space = block; break; // block exists and bounds check passed } return block; } export void removeEntity(EntityID id) { entities_to_remove.add(id); } private void __removeEntity(EntityID id) { //get entity and block meta data pointers Entity* entity = id_manager.getEntityPointer(id); if (entity is null) return; //return if entity doesn't exist EntitiesBlock* block = getMetaData(entity); id_manager.releaseID(id); //release id from manager removeEntityNoID(entity, block, true); } private void removeEntityNoID(Entity* entity, EntitiesBlock* block, bool call_destructors = false) { //pos is Entity number in block 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; version (LinearLayout) { 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()); if (call_destructors) { //void* data = data_begin + pos * info.size; foreach (comp; info.components) { if (components[comp].destroy_callback) { //components[comp].destroy_callback(data + info.deltas[comp]); components[comp].destroy_callback(cast( void*) block + info.deltas[comp] + pos * components[comp].size); } } } } else { uint pos = cast(int)(cast(void*) entity - data_begin) / info.size; if (call_destructors) { void* data = data_begin + pos * info.size; foreach (comp; info.components) { if (components[comp].destroy_callback) { components[comp].destroy_callback(data + info.deltas[comp]); } } } } if (block.entities_count == 0) { 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; } if (block.prev_block) { block.prev_block.next_block = block.next_block; } if (block.next_block) { block.next_block.prev_block = block.prev_block; } allocator.freeBlock(block); return; } if (pos == block.entities_count) return; version (LinearLayout) { 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); } else { //copy memory of last entity to position of removed entity void* src = data_begin + block.entities_count * info.size; void* dst = data_begin + pos * info.size; memcpy(dst, src, info.size); } //update pointer for moved entity ID //entity = cast(Entity*) dst; entity.updateID(); } /************************************************************************************************************************ *functions return MetaData of page. * *params: *pointer = pointer to any data of entity (i.e. component data pointer) */ export EntitiesBlock* getMetaData(void* pointer) { return cast(EntitiesBlock*)(cast(size_t) pointer & (~cast(size_t)(page_size - 1))); } private void changeEntites() { uint index = 0; uint len = cast(uint) change_entities_list.length; while (index < len) { if (!change_entities_list[index++]) { EntityID id = *cast(EntityID*)&change_entities_list[index]; index += EntityID.sizeof; uint num = *cast(uint*)&change_entities_list[index]; index += uint.sizeof; ushort[] ids = (cast(ushort*) alloca(num * ushort.sizeof))[0 .. num]; ids[0 .. $] = (cast(ushort*)&change_entities_list[index])[0 .. num]; index += ushort.sizeof * num; __removeComponents(id, ids); } else { EntityID id = *cast(EntityID*)&change_entities_list[index]; index += EntityID.sizeof; uint num = *cast(uint*)&change_entities_list[index]; index += uint.sizeof; ushort[] ids = (cast(ushort*) alloca(num * ushort.sizeof))[0 .. num]; ids[0 .. $] = (cast(ushort*)&change_entities_list[index])[0 .. num]; index += ushort.sizeof * num; void*[] pointers = (cast(void**) alloca(num * (void*).sizeof))[0 .. num]; foreach (i; 0 .. num) { pointers[i] = &change_entities_list[index]; index += components[ids[i]].size; } __addComponents(id, ids, pointers); } } change_entities_list.clear(); } private void updateBlocks() { foreach (block; blocks_to_update) { block.entities_count += block.added_count; block.added_count = 0; } blocks_to_update.clear(); } private void removeEntities() { foreach (id; entities_to_remove) { __removeEntity(id); } entities_to_remove.clear(); } export void begin() { updateBlocks(); removeEntities(); changeEntites(); foreach (ref system; instance.systems) { if (system.m_begin) system.m_begin(system.m_system_pointer); } } export void end() { foreach (ref system; instance.systems) { if (system.m_end) system.m_end(system.m_system_pointer); } updateBlocks(); removeEntities(); changeEntites(); //clearEvents(); } struct ComponentInfo { ushort size; ushort alignment; ubyte[] init_data; void function(void* pointer) destroy_callback; } struct EventInfo { ushort size; ushort alignment; void function(void* pointer) destroy_callback; } /************************************************************************************************************************ *Entity type info. */ struct EntityInfo { ///entity components ushort[] components; version (LinearLayout) { ///deltas in memory for components in EntitiesBlock ushort[] deltas; ///deltas in memory for components in EntityTemplate ushort[] tmpl_deltas; } else { ///deltas in memory for components ushort[] deltas; } ///alignment of whole entity ushort alignment; //unused in linear-layout ///size of entity (with alignment respect) ushort size; ///max number of entities in block ushort max_entities; ///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 ///array of CallData. Contain data for System calls. Vector!(CallData) callers; } /************************************************************************************************************************ *Meta data of every block of entities (contained at the begining of block). */ struct EntitiesBlock { ///return distance (in bytes) from begin of block to data uint dataDelta() { ushort dif = EntitiesBlock.sizeof; alignNum(dif, type_info.alignment); return dif; } ///return pointer to first element in block export void* dataBegin() { ushort dif = EntitiesBlock.sizeof; version (LinearLayout) { } else alignNum(dif, type_info.alignment); return cast(void*)&this + dif; } ///pointer to Entity type info EntityInfo* type_info = null; ///number of entities in block ushort entities_count = 0; ///number of new entities in block ushort added_count = 0; ///block id ushort id = 0; ///maximum number of entities in block //ushort max_entities = 0; ///pointer to next block/page EntitiesBlock* next_block = null; ///pointer to next block/page EntitiesBlock* prev_block = null; //there is a loooot of data (some kB of memory, pure magic) } /************************************************************************************************************************ *Structure with data used to calling System calls. */ struct CallData { ///system ID. Used to update system pointer after system reload. uint system_id; ///pointer to used system System* system; ///poiner to Entity type info EntityManager.EntityInfo* info; ///deltas for components ushort[] deltas; //unused in linear-layout } alias SytemFuncType = void function(ref EntityManager.CallData data, void* entity); //alias sendSelfEvent = instance.event_manager.sendSelfEvent; //alias event_manager this; ///Single page size. Must be power of two. enum page_size = 32768; //4096; ///Number of pages in block. enum pages_in_block = 128; IDManager id_manager; BlockAllocator!(page_size, pages_in_block) allocator; //EventManager event_manager; mixin EventManagerCode; Vector!EntityID entities_to_remove; Vector!(EntitiesBlock*) blocks_to_update; Vector!ubyte change_entities_list; HashMap!(ushort[], EntityInfo*) entities_infos; HashMap!(string, ushort) systems_map; HashMap!(string, ushort) components_map; HashMap!(string, ushort) events_map; Vector!System systems; Vector!ComponentInfo components; Vector!EventInfo events; __gshared EntityManager instance = null; } /* static ulong defaultHashFunc(T)(auto ref T t) { ulong ret = 0; foreach(id;t) { ret = ret } }*/