module ecs.ecs; import std.stdio; version(Design): alias SytemFuncType = void function(ref SystemCallData data, void* componentsStart); struct HasComponentsStore { ulong[4] bits; //256 components bool has(HasComponentsStore components) { return true; } bool notIn(HasComponentsStore components) { return true; } int length() { assert(0); } } // Informacje o kompnencie struct ComponentInfo { int size; int aligment; SerializeJSON funsSerJ; SerializeBiN funcSerB; } struct System { HasComponentsStore requiredComponents; HasComponentsStore absenComponents; HasComponentsStore maybeComponents; bool enabled; int priority; SytemFuncType func; } // Informacje o systemie dla konkretnego entitiesa struct SystemCallData { System* system; int[] componentsDt; } // Informacje o entitiesie danego typu struct EntityTypeData { HasComponentsStore components; int[] deltas; int totalSize; int totalAligment = 8; SystemCallData[] systems; } struct EntitiesBlock { EntityTypeData* typeData; Entity* freeEntitySlot; EntitiesBlock* nextBlock; } struct EntityID { ulong id = ulong.max; static immutable notUsedValue = EntityID(ulong.max); } // Dane konkretnego Entitiesa struct Entity { EntityID entityID = EntityID.notUsedValue; union { string name; Entity* nextFreeSlot; } //string eventOnDestroy; uint group; EntityID entityID; //ubyte[XX] thereIsComponentsMemory; } struct Template { HasComponentsStore hasComp; Entity* entity; } struct Manager { EntityAllocator entityArrayAllcoator; ComponentInfo[] components; System[] systems; HashMap!(HasComponentsStore, EntitiesBlock*) entitiesDatas; HashMapTwoWays!(string, Entity*) nameMap; HashMapTwoWays!(EntityID, Entity*) idMap; EntitiesBlock* getEntitiesBlock(HasComponentsStore hasComponents) { EntitiesBlock* block = entitiesDatas.get(hasComponents, null); if (block is null) { // If such component combination was never present, add it block = addNewBlock(hasComponents, block); return block; } // Iterate over list of components until free slot is found or lists ends do { if (block.freeEntitySlot !is null) { return block; } if (block.nextBlock is null) { block = addNewBlock(hasComponents); return block; } block = block.nextBlock; } while (block.nextBlock !is null); } EntitiesBlock* addNewBlock(HasComponentsStore hasComponents, EntitiesBlock* firstBlock) { // Get last block so order of blocks is preserved, and first blocks are filled first EntitiesBlock* lastBlock = firstBlock; if (lastBlock !is null) { while (lastBlock.nextBlock !is null) { lastBlock = lastBlock.nextBlock; } } assert(lastBlock is null || lastBlock.nextBlock is null); ubyte[] memory = new ubyte[](4096); EntitiesBlock* block = cast(EntitiesBlock*) memory.ptr; if (lastBlock is null) { EntityTypeData* entityTypeData = newEntityTypeData(hasComponents); block.typeData = entityTypeData; block.nextBlock = null; entitiesDatas.add(hasComponents, block); } else { lastBlock.nextBlock = block; block.typeData = lastBlock.typeData; block.nextBlock = null; } } void alignNum(ref int num, int aligment) { int reminder = num % aligment; if (reminder != 0) { num += aligment - reminder; } } EntityTypeData* newEntityTypeData(HasComponentsStore hasComponents) { EntityTypeData* typeData = new EntityTypeData(); typeData.components = hasComponents; ComponentInfo[] components = getComponentsInfo(hasComponents); typeData.deltas.length = hasComponents.length; foreach (i, comp; components) { typeData.deltas[i] = typeData.totalSize; typeData.totalAligment.max(comp.aligment); typeData.totalSize += comp.size; alignNum(typeData.totalSize, comp.aligment); } alignNum(typeData.totalSize, typeData.totalAligment); foreach (sys; systems) { if (!typeData.hasComp.has(sys.requiredComponents) || !typeData.hasComp.notIn(sys.absenComponents)) { continue; } entTypeData.systems ~= sys; } return typeData; } void addEntity(Template* templ) { EntitiesBlock* block = getEntitiesBlock(templ.hasComp); Entity* newEntity = block.freeEntitySlot; block.freeEntitySlot = newEntity.nextFreeSlot; // from to size memcpy(temp.entity, newEntity, block.typeData.totalSize); } void addSystem(Func)(int priority) { HasComponentsStore requiredComponents; HasComponentsStore absenComponents; HasComponentsStore maybeComponents; void systemCaller(ref SystemCallData data, void * componentsStart) { Func(cast(FUnc.par1Type)(componentsStart + data.componentsDt[0]), cast(FUnc.par1Type)(componentsStart + data.componentsDt[1])/*...*/); } System* system = new System(&systemCaller, entTypeData); systems ~= system; foreach (ref entTypeData; entitiesDatas) { if (!entTypeData.hasComp.has(requiredComponents) || !entTypeData.hasComp.notIn(absenComponents)) { continue; } entTypeData.systems ~= system; } } } void someSystem(CompA a, CompB b, CompC* c) { } void main() { writeln("Edit source/app.d to start your project."); } class System { void start() { } void end() { } void update(ref ObjRend a) { } void useEvent(EventData evvv, ref ObjRend a) { } } alias SerializeVector = ubyte[]; __gshared EntityManager gEntityManager; unittest { struct ComponentA { __gshared static int component_id; int a; ulong b; static void serializeComponent(ref ComponentA comp, SerializeVector output) { } static void deerializeComponent(ref ComponentA comp, ubyte[] data) { } } gEM.addComponet!ComponentA(); assert(ComponentA.component_id == 0); ComponentData* ccc = &gEM.componnets[ComponentA.component_id]; assert(ccc.totalAligment == 8); assert(ccc.totalSize == 8); HasComponentsStore hasComponents; hasComponents.addComponet(ComponentA.component_id); EntityTempalte* tmpl = gEM.allocateTemplate(hasComponents); ComponentA* comp = tmpl.getComponent!ComponentA(ComponentA.component_id); comp.a = 111; comp.b = 222; gEM.addEntity(tmpl); struct SystemAdd { void update(ref ComponentA a) { a.a+=1000; b.b+=2000; } void handleEvent(EventData evvv, ref ComponentA a) { } } int priority=10; gEM.registerSystem!(SystemAdd)(priority); gEM.updateStepAll(); foreach(EntityID id; gEM.IterateByAllEntiteis){ assert(id.getComponent(ComponentA.component_id)); ComponentA* ccc=id.getComponent(ComponentA.component_id); assert(ccc.a==1111); assert(ccc.b==2222); } }