diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..413e5fb --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +dub.userprefs +dub.selections.json +.dub +/.vscode +perf.data +perf.data.old +*.o +*.a +*.obj \ No newline at end of file diff --git a/README.md b/README.md index 5a003b6..50c3dfc 100644 --- a/README.md +++ b/README.md @@ -1,2 +1 @@ -# ecs - +# Dynamic Entity Component System diff --git a/dub.json b/dub.json new file mode 100755 index 0000000..547b6be --- /dev/null +++ b/dub.json @@ -0,0 +1,9 @@ +{ + "name": "dlps", + "authors": [ + "Michał Masiukiewicz", "Dawid Masiukiewicz" + ], + "description": "Dynamic Entity Component System", + "copyright": "Copyright © 2017-2018, Michał Masiukiewicz", + "license": "MIT" +} \ No newline at end of file diff --git a/source/ecs/ecs.d b/source/ecs/ecs.d new file mode 100644 index 0000000..d82add2 --- /dev/null +++ b/source/ecs/ecs.d @@ -0,0 +1,241 @@ +module ecs.ecs; + +import std.stdio; + + + +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){ + + } +} + diff --git a/source/ecs/entity_allocator.d b/source/ecs/entity_allocator.d new file mode 100644 index 0000000..bf0b560 --- /dev/null +++ b/source/ecs/entity_allocator.d @@ -0,0 +1,51 @@ +module ecs.entity_allocator; + +enum bucketSize = 4096; +enum bucketsInAllocation = 128; + +struct Bucket +{ + union + { + ubyte[bucketSize] memory; + Bucket* next; + } + +} + +struct BucketArray +{ + Bucket[bucketsInAllocation] buckets; +} + +struct EntityAllocator +{ + BucketArray[] arrays; + Bucket* lastEmptyBucket; + + void initBucketArray(ref BucketArray bArr) + { + + } + + void allocateBucketArray() + { + auto bucketArray = new BucketArray(); + assert(bucketArray.buckets[0].ptr % bucketSize == 0) arrays ~= bucketArray; + Bucket* lasBucket = initBucketArray(bucketArray); + lastEmptyBucket = lasBucket; + } + + ubyte[] getMemory() + { + if (lastEmptyBucket is null) + { + allocateBucket(); + } + auto bucketTmp = lastEmptyBucket; + lastEmptyBucket = bucketTmp.next; + + return bucketTmp.memory[]; + } + +}