module ecs.manager; import std.experimental.allocator.mallocator : Mallocator, AlignedMallocator; import std.experimental.allocator; import std.traits; import std.algorithm : max; import std.conv : to; import core.stdc.string; import ecs.system; import ecs.entity; import ecs.vector; import ecs.hash_map; alias gEM = EntityManager.instance; class EntityManager { static void initialize() { instance = Mallocator.instance.make!EntityManager; } void registerSystem(Sys)(int priority) { alias types = Parameters!(Sys.update); static string genCall()() { string ret = "s.update("; foreach(i,param;Parameters!(Sys.update)) { ret ~= "*cast(types["~i.to!string~"]*)(data_pointer + data.deltas["~i.to!string~"]),"; } ret ~= ");"; return ret; } static string genCompList()() { string ret; foreach(i,param;Parameters!(Sys.update)) { ret ~= "system.components["~i.to!string~"] = components_map.get(types["~i.to!string~"].stringof);\n"; } return ret; } static void callUpdate(ref CallData data, void* entity) { static if (hasMember!(Sys, "update")) { Sys* s = cast(Sys*) data.system.system_pointer; EntitiesBlock* block = data.info.first_block; void* data_pointer = block.dataBegin(); mixin(genCall()); } } System system; static if (hasMember!(Sys, "update")) { system.update = &callUpdate; } system.system_pointer = cast(void*) Mallocator.instance.make!Sys; system.components = Mallocator.instance.makeArray!uint(types.length); mixin(genCompList()); if (systems is null) { systems = Mallocator.instance.makeArray!System(1); systems[0] = system; } else { Mallocator.instance.expandArray(systems, 1); systems[$ - 1] = system; } } void registerComponent(Comp)() { ushort size = Comp.sizeof; ComponentInfo info; info.size = size; info.aligment = Comp.alignof;//8; if (components is null) { components = Mallocator.instance.makeArray!ComponentInfo(1); components[0] = info; } else { Mallocator.instance.expandArray(components, 1); components[$ - 1] = info; } components_map.add(Comp.stringof,cast(uint)(components.length-1)); } void update() { foreach(info;&entities_infos.byValue) { foreach(data;info.callers) { data.system.update(data,null);//caller(call_data,null); } } } static void alignNum(ref ushort num, ushort aligment) { int reminder = num % aligment; if (reminder != 0) { num += aligment - reminder; } } EntityTemplate* allocateTemplate(ushort[] components_ids) { EntityInfo* info = entities_infos.get(components_ids, null); if (info is null) { info = Mallocator.instance.make!EntityInfo; info.components = Mallocator.instance.makeArray(components_ids); info.deltas = Mallocator.instance.makeArray!ushort(components_ids.length); info.size = EntityID.sizeof; info.alignment = EntityID.alignof; foreach (i, id; components_ids) { info.alignment = max(info.alignment,components[id].aligment); alignNum(info.size, components[id].aligment); info.deltas[i] = info.size; info.size += components[id].size; } alignNum(info.size, info.alignment); foreach(ref system;systems) { if(system.update is null)continue; CallData call_data = CallData(&system,info, null); call_data.deltas = Mallocator.instance.makeArray!ushort(system.components.length); foreach(i,id;system.components) { foreach(i2,id2;info.components) { if(id2 == id) { call_data.deltas[i] = info.deltas[i2]; break; } } } info.callers.add(call_data); } entities_infos.add(info.components,info); } EntityTemplate* temp = Mallocator.instance.make!EntityTemplate; temp.entity_data = Mallocator.instance.makeArray!ubyte(info.size); temp.info = info; return temp; } void freeTemplate(EntityTemplate* template_) { Mallocator.instance.dispose(template_.entity_data); Mallocator.instance.dispose(template_); } void addEntity(EntityTemplate* tmpl) { if(tmpl.info.first_block is null) { tmpl.info.first_block = cast(EntitiesBlock*) AlignedMallocator.instance.alignedAllocate(4096,4096); *tmpl.info.first_block = EntitiesBlock(tmpl.info); } void* start = tmpl.info.first_block.dataBegin() + tmpl.info.first_block.entities_count * tmpl.info.size; memcpy(start, tmpl.entity_data.ptr, tmpl.info.size); tmpl.info.first_block.entities_count++; } struct CallData { System* system; EntityInfo* info; ushort[] deltas; } struct ComponentInfo { ushort size; ushort aligment; } struct EntityInfo { ushort[] components; ushort[] deltas; ushort alignment; ushort size; EntitiesBlock* first_block; Vector!(CallData) callers; } struct EntitiesBlock { void* dataBegin() { ushort dif = EntitiesBlock.sizeof; alignNum(dif,type_data.alignment); return cast(void*)&this + dif; } EntityInfo* type_data; uint entities_count; EntitiesBlock* next_block; ///there is a loooot of data (4kB, pure magic) } alias SytemFuncType = void function(ref EntityManager.CallData data, void* entity); HashMap!(ushort[], EntityInfo*) entities_infos; HashMap!(string,uint) components_map; System[] systems; ComponentInfo[] components; __gshared EntityManager instance; }