diff --git a/.gitignore b/.gitignore index eae1286..e0e6fff 100644 --- a/.gitignore +++ b/.gitignore @@ -12,8 +12,4 @@ !compile_wasm.py !compile_android.py !.gitlab-ci.yml -!LICENSE -!c-api -!c-api/*.h -!c-api/*.d -!c-api/*.c \ No newline at end of file +!LICENSE \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 86ccda1..f1d8ecc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -23,7 +23,7 @@ build_code: test_dmd_debug: stage: test - image: frolvlad/alpine-glibc + image: debian:buster-slim script: - binaries/dmd_debug_unittest artifacts: @@ -31,7 +31,7 @@ test_dmd_debug: junit: test_report.xml test_dmd: stage: test - image: frolvlad/alpine-glibc + image: debian:buster-slim script: - binaries/dmd_release_unittest artifacts: @@ -39,7 +39,7 @@ test_dmd: junit: test_report.xml test_dmd_betterC: stage: test - image: frolvlad/alpine-glibc + image: debian:buster-slim script: - binaries/dmd_debug_unittest_bc artifacts: diff --git a/README.md b/README.md index 3f52b93..14e6d75 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,13 @@ Bubel ECS was tested on Linux, Windows, Android and WASM. **Currently library is in beta stage so some significant API changes can appear.** +Package is available on [DUB package repository](https://code.dlang.org/packages/bubel-ecs). Usage: +``` + "dependencies": { + "bubel-ecs": "~>0.1.1" + } +``` + ## Design For core information about Entity-Component-System architectural pattern please read definition described at [Wikipedia](https://en.wikipedia.org/wiki/Entity_component_system). diff --git a/c-api/becs.h b/c-api/becs.h deleted file mode 100644 index 2a2bca0..0000000 --- a/c-api/becs.h +++ /dev/null @@ -1,207 +0,0 @@ -#ifndef __BECS__ -#define __BECS__ - -#include -#include -#include -#include - -typedef struct BECSSystem -{ - -}BECSSystem; - -typedef struct BECSArray -{ - size_t size; - void* ptr; -}BECSArray; - -typedef struct EntityID -{ - ///Index to entity in IDManager. - uint32_t id; - ///Counter required for reusing ID. - uint32_t counter; -}EntityID; - -typedef struct Entity -{ - ///Entity ID. - EntityID id; -}Entity; - -typedef struct ComponentRef -{ - ///pointer to component - void* ptr; - ///component index - uint16_t id; -}ComponentRef; - -typedef ComponentRef EventRef; - -typedef struct BECSComponentInfo -{ - ///Component size - uint16_t size; - ///Component data alignment - uint16_t alignment; - ///Initialization data - BECSArray init_data; - ///Pointer to component destroy callback - void* destroy_callback; - ///Pointer to component create callback - void* create_callback; - //void* create_callback; -}BECSComponentInfo; - -typedef struct BECSEventRegisterInfo -{ - ///Component size - uint16_t size; - ///Component data alignment - uint16_t alignment; -}BECSEventRegisterInfo; - -typedef struct EntityTemplate -{ - /*///Entity components data - BECSArray entity_data; - ///Pointer to entity type info. - void* info;*/ -}EntityTemplate; - -enum BECSSystemComponentAttribute -{ - required = 0, - optional = 1, - excluded = 2 -}; - -enum BECSSystemComponentAccess -{ - writable = 0, - readonly = 1 -}; - -typedef struct BECSSystemComponentInfo -{ - uint16_t id; - enum BECSSystemComponentAttribute attribute; - enum BECSSystemComponentAccess access; -}BECSSystemComponentInfo; - -///C-API structure conatin only subset of real data which can be accessed directly. Component arrays can be retrived using function calls. -typedef struct BECSListenerCallData -{ - uint32_t count; - void* context; -}BECSListenerCallData; - -///C-API structure conatin only subset of real data which can be accessed directly. Component arrays can be retrived using function calls. -typedef struct BECSSystemCallData -{ - uint32_t count; - uint32_t thread_id; - uint32_t job_id; - void* context; -}BECSSystemCallData; - -///C-API structure conatin only subset of real data which can be accessed directly. Component arrays can be retrived using function calls. -typedef struct BECSEventCallData -{ - Entity* entity; - void* event; - void* context; -}BECSEventCallData; - -typedef struct BECSEventCallback -{ - uint16_t id; - void (*callback)(BECSEventCallData* data); -}BECSEventCallback; - -typedef struct BECSSystemRegisterInfo -{ - uint32_t pass_id; - int32_t priority; - uint32_t max_jobs; - - size_t components_count; - BECSSystemComponentInfo* components; - - size_t event_handlers_count; - BECSEventCallback* event_handlers; - - size_t system_size; - void* init_data; - - void (*on_update)(BECSSystemCallData* call_data); - void (*on_create)(void* system_pointer); - void (*on_destroy)(void* system_pointer); - void (*on_enable)(void* system_pointer); - void (*on_disable)(void* system_pointer); - char (*on_begin)(void* system_pointer); - void (*on_end)(void* system_pointer); - void (*on_add_entity)(BECSListenerCallData*); - void (*on_remove_entity)(BECSListenerCallData*); - void (*on_change_entity)(BECSListenerCallData*); - void (*filter_entity)(void* system_pointer, void* info); -}BECSSystemRegisterInfo; - -void becsInitialize(); -void becsDestroy(); - -void becsBeginRegister(); -void becsEndRegister(); - -void becsBegin(); -void becsEnd(); -void becsCommit(); - -void becsUpdate(uint16_t pass_id); -void becsUpdateMT(uint16_t pass_id); - -uint16_t becsRegisterComponent(const char* name, BECSComponentInfo info); -uint16_t becsRegisterEvent(const char* name, BECSEventRegisterInfo info); -uint16_t becsRegisterSystem(const char* name, BECSSystemRegisterInfo info); -uint16_t becsRegisterPass(const char* name); - -Entity* becsAddEntity(EntityTemplate* template); -Entity* becsAddEntityCopy(EntityID id); -void becsRemoveEntity(EntityID id); - -Entity* becsGetEntity(EntityID id); - -void becsAddComponents(const EntityID entity_id, size_t length, ComponentRef* comps); -void becsRemoveComponents(const EntityID entity_id, size_t length, uint16_t* comps); - -EntityTemplate* becsAllocateTemplate(uint32_t count, uint16_t* components); -EntityTemplate* becsAllocateTemplateFromEntity(EntityID id, uint8_t fill_default); -EntityTemplate* becsAllocateTemplateCopy(EntityTemplate* tmpl); -EntityTemplate* becsAllocateTemplateFromTemplate(EntityTemplate* tmpl, size_t new_count, uint16_t* components, size_t remove_count, uint16_t* remove_components); -void becsFreeTemplate(EntityTemplate* tmpl); - -void* becsEntityGetComponent(Entity* entity, uint16_t component_id); - -void* becsSystemCallDataGetComponentArray(BECSSystemCallData* data, uint16_t component_id); -Entity* becsSystemCallDataGetEntitiesArray(BECSSystemCallData* data); - -void* becsListenerCallDataGetComponentArray(BECSListenerCallData* data, uint16_t component_id); -Entity* becsListenerCallDataGetEntitiesArray(BECSListenerCallData* data); - -void becsCallEntitiesFunction(uint16_t system_id, void (*on_update)(BECSSystemCallData* call_data), void* context); - -void becsSendEvent(EntityID id, EventRef event); - -#define BECS_REGISTER_COMPONENT(comp) \ - ({ \ - void* mem_ptr = malloc(sizeof(comp)); \ - memcpy(mem_ptr, &comp, sizeof(comp)); \ - becsRegisterComponent(#comp, (BECSComponentInfo){sizeof(comp), alignof(comp), (BECSArray){sizeof(comp), mem_ptr}, 0, 0}); \ - }) - - //BECSComponentInfo(sizeof(comp), 4, 0, 0, 0)); - -#endif //__BECS__ \ No newline at end of file diff --git a/c-api/manager.d b/c-api/manager.d deleted file mode 100644 index e109f1d..0000000 --- a/c-api/manager.d +++ /dev/null @@ -1,306 +0,0 @@ -module manager; - -import bubel.ecs.manager; -import bubel.ecs.entity; -import bubel.ecs.events; -import bubel.ecs.system; -import bubel.ecs.std; - -import core.stdc.string; - -extern (C): -/* -struct BECSComponentInfo -{ - ///Component size - ushort size; - ///Component data alignment - ushort alignment; - ///Initialization data - ubyte[] init_data; - ///Pointer to component destroy callback - void* destroy_callback; - ///Pointer to component create callback - void* create_callback; - //void* create_callback; -}*/ - -alias BECSComponentInfo = EntityManager.ComponentInfo; -// alias BECSEventCallData = EntityManager.EventCallData; - -enum BECSSystemComponentAttribute -{ - required = 0, - optional = 1, - excluded = 2 -} - -enum BECSSystemComponentAccess -{ - writable = 0, - readonly = 1 -} - -struct BECSSystemComponentInfo -{ - ushort id; - BECSSystemComponentAttribute attribute; - BECSSystemComponentAccess access; -} - -struct BECSEventCallback -{ - ushort id; - void function(EntityManager.EventCallData* data) callback; -} - -struct BECSSystemRegisterInfo -{ - uint pass_id; - int priority; - uint max_jobs; - - BECSSystemComponentInfo[] components; - BECSEventCallback[] event_handlers; - - byte[] init_data; - - void function(EntityManager.SystemCallData* call_data) on_update; - void function(void* system_pointer) on_create; - void function(void* system_pointer) on_destroy; - void function(void* system_pointer) on_enable; - void function(void* system_pointer) on_disable; - bool function(void* system_pointer) on_begin; - void function(void* system_pointer) on_end; - void function(EntityManager.ListenerCallData*) on_add_entity; - void function(EntityManager.ListenerCallData*) on_remove_entity; - void function(EntityManager.ListenerCallData*) on_change_entity; - void function(void* system_pointer, EntityManager.EntityInfo* info) filter_entity; -} - -alias BECSEventRegisterInfo = EntityManager.EventRegisterInfo; - -void becsInitialize() -{ - EntityManager.initialize(); -} - -void becsDestroy() -{ - EntityManager.destroy(); -} - -void becsBeginRegister() -{ - gEntityManager.beginRegister(); -} - -void becsEndRegister() -{ - gEntityManager.endRegister(); -} - -void becsBegin() -{ - gEntityManager.begin(); -} - -void becsEnd() -{ - gEntityManager.end(); -} - -void becsCommit() -{ - gEntityManager.commit(); -} - -void becsUpdate(ushort pass_id) -{ - gEntityManager.update(pass_id); -} - -void becsUpdateMT(ushort pass_id) -{ - gEntityManager.update(pass_id); -} - -ushort becsRegisterPass(const (char)* name) -{ - return gEntityManager.registerPass(name[0 .. strlen(name)]); -} - -ushort becsRegisterComponent(const(char)* name, BECSComponentInfo info) -{ - return gEntityManager.registerComponent(name[0 .. strlen(name)], info); -} - -ushort becsRegisterEvent(const(char)* name, BECSEventRegisterInfo info) -{ - return gEntityManager.registerEvent(name[0 .. strlen(name)], info); -} - -ushort becsRegisterSystem(const(char)* name, BECSSystemRegisterInfo info) -{ - System system; - - if(info.init_data.length) - { - system.m_system_pointer = malloc(info.init_data.length); - memcpy(system.m_system_pointer, info.init_data.ptr, info.init_data.length); - } - - if(info.event_handlers.length) - { - system.m_event_callers = Mallocator.makeArray!(System.EventCaller)(info.event_handlers.length); - foreach(i, BECSEventCallback callback; info.event_handlers) - { - system.m_event_callers[i].id = callback.id; - system.m_event_callers[i].callback = callback.callback; - } - } - - system.m_update = info.on_update; - system.m_create = info.on_create; - system.m_destroy = info.on_destroy; - system.m_enable = info.on_enable; - system.m_disable = info.on_disable; - system.m_begin = info.on_begin; - system.m_end = info.on_end; - system.m_add_entity = info.on_add_entity; - system.m_remove_entity = info.on_remove_entity; - system.m_change_entity = info.on_change_entity; - system.m_filter_entity = info.filter_entity; - - if(info.components.length) - { - uint req; - uint opt; - foreach(comp; info.components) - { - if(comp.attribute == BECSSystemComponentAttribute.required)req++; - else if(comp.attribute == BECSSystemComponentAttribute.optional)opt++; - } - if(req)system.m_components = Mallocator.makeArray!ushort(req); - if(opt)system.m_optional_components = Mallocator.makeArray!ushort(opt); - req = 0; - opt = 0; - foreach(comp; info.components) - { - if(comp.attribute == BECSSystemComponentAttribute.required) - { - system.m_components[req++] = comp.id; - } - else if(comp.attribute == BECSSystemComponentAttribute.optional) - { - system.m_optional_components[opt++] = comp.id; - } - } - } - else - { - system.m_empty = true; - } - return gEntityManager.registerSystem(name[0 .. strlen(name)], system); -} - -EntityTemplate* becsAllocateTemplate(uint count, ushort* components) -{ - return gEntityManager.allocateTemplate(components[0 .. count]); -} - -EntityTemplate* becsAllocateTemplateFromEntity(EntityID id, bool fill_default) -{ - return gEntityManager.allocateTemplate(id, fill_default); -} - -EntityTemplate* becsAllocateTemplateCopy(EntityTemplate* tmpl) -{ - return gEntityManager.allocateTemplate(tmpl); -} - -EntityTemplate* becsAllocateTemplateFromTemplate(EntityTemplate* tmpl, size_t new_count, ushort* components, size_t remove_count, ushort* remove_components) -{ - return gEntityManager.allocateTemplate(tmpl, components[0 .. new_count], remove_components[0 .. remove_count]); -} - -void becsFreeTemplate(EntityTemplate* tmpl) -{ - gEntityManager.freeTemplate(tmpl); -} - -Entity* becsAddEntity(EntityTemplate* tmpl) -{ - return gEntityManager.addEntity(tmpl); -} - -Entity* becsAddEntityCopy(EntityID id) -{ - return gEntityManager.addEntityCopy(id); -} - -void becsRemoveEntity(EntityID id) -{ - gEntityManager.removeEntity(id); -} - -Entity* becsGetEntity(EntityID id) -{ - return gEntityManager.getEntity(id); -} - -void* becsEntityGetComponent(Entity* entity, ushort component_id) -{ - return entity.getComponent(component_id); -} - -void* becsSystemCallDataGetComponentArray(EntityManager.SystemCallData* data, ushort component_id) -{ - if(data.info.deltas.length <= component_id || data.info.deltas[component_id] == 0)return null; - return cast(void*)data.block + data.info.deltas[component_id]; -} - -Entity* becsSystemCallDataGetEntitiesArray(EntityManager.SystemCallData* data) -{ - return cast(Entity*)data.block.dataBegin(); -} - -void* becsListenerCallDataGetComponentArray(EntityManager.ListenerCallData* data, ushort component_id) -{ - if(data.info.deltas.length <= component_id || data.info.deltas[component_id] == 0)return null; - return cast(void*)data.block + data.info.deltas[component_id]; -} - -Entity* becsListenerCallDataGetEntitiesArray(EntityManager.ListenerCallData* data) -{ - return cast(Entity*)data.block.dataBegin(); -} - -void becsCallEntitiesFunction(ushort system_id, void function(EntityManager.SystemCallData*) callback, void* context) -{ - System* system = gEntityManager.getSystem(system_id); - - if (!system.m_any_system_caller) - return; - - foreach (info; system.m_any_system_caller.infos) - { - EntityManager.CallData data = EntityManager.CallData(system.id, system, info, context, cast(void*)callback); - data.update(); - } -} - -void becsAddComponents(const EntityID entity_id, ComponentRef[] comps) -{ - gEntityManager.addComponents(entity_id, comps); -} - -void becsRemoveComponents(const EntityID entity_id, ushort[] comps) -{ - gEntityManager.removeComponents(entity_id, comps); -} - -void becsSendEvent(EntityID id, EventRef event) -{ - gEntityManager.sendEvent(id, event); -} \ No newline at end of file diff --git a/c-api/meson.build b/c-api/meson.build deleted file mode 100644 index 5e1e2cf..0000000 --- a/c-api/meson.build +++ /dev/null @@ -1,7 +0,0 @@ -src += files( - 'manager.d' -) - -c_src = files( - 'test.c' -) \ No newline at end of file diff --git a/c-api/test.c b/c-api/test.c deleted file mode 100644 index c6b1246..0000000 --- a/c-api/test.c +++ /dev/null @@ -1,1051 +0,0 @@ -#include "becs.h" -#include -#include - -uint32_t getID() -{ - return 0; -} - -long getUSecTime() -{ - //time_t time; - struct timespec spec; - - clock_gettime(CLOCK_REALTIME, &spec); - - //time = spec.tv_sec; - return spec.tv_sec * 1000000 + spec.tv_nsec / 1000; //time / 1000_000; -} - -typedef struct TestEvent -{ - int32_t a; -}TestEvent; -static uint16_t TestEventID; - -typedef struct TestEvent2 -{ - float a; -}TestEvent2; -static uint16_t TestEvent2ID; - -typedef struct -{ - float x,y; -}CLocation; -static uint16_t CLocationID; - -typedef struct -{ - float x,y; -}CVelocity; -static uint16_t CVelocityID; - -typedef struct TestComp -{ - int32_t a;// = 1; - uint64_t b;// = 2; -}TestComp; -static uint16_t TestCompID; - -typedef struct TestComp2 -{ - int32_t b;// = 3; - int32_t a;// = 4; -}TestComp2; -static uint16_t TestComp2ID; - -typedef struct TestComp3 -{ - uint32_t gg;// = 5; //good game - uint32_t bg;// = 6; //bad game -}TestComp3; -static uint16_t TestComp3ID; - -typedef struct TestComp4 -{ - uint32_t gg;// = 7; //good game - uint32_t bg;// = 8; //bad game - uint64_t a;// = 9; - uint64_t b;// = 10; - uint64_t c;// = 11; - uint64_t g;// = 12; -}TestComp4; -static uint16_t TestComp4ID; - -typedef struct TestComp5 -{ - uint32_t gg;// = 7; //good game - uint32_t bg;// = 8; //bad game - uint64_t a;// = 9; - uint64_t b;// = 10; - uint64_t c;// = 11; - uint64_t g;// = 12; -}TestComp5; -static uint16_t TestComp5ID; - -typedef struct CPosition -{ - float x; - float y; -}CPosition; -static uint16_t CPositionID; - -void onUpdateTest(BECSSystemCallData* call_data) -{ - CLocation* location = (CLocation*)becsSystemCallDataGetComponentArray(call_data, CLocationID); - CVelocity* velocity = (CVelocity*)becsSystemCallDataGetComponentArray(call_data, CVelocityID); - //if(velocity != NULL)exit(0); - for(int i=0; icount; i++) - { - location[i].y += 10; - if(velocity)velocity[i].x += 1; - } - printf("To jest printf z systemu!\n"); -} -/* -struct EverySystem -{ - struct EntitiesData - { - uint length; - Entity[] entity; - CPosition[] pos; - } - - -};*/ - -void EverySystemFree(BECSSystemCallData* call_data) -{ - Entity* entity = becsSystemCallDataGetEntitiesArray(call_data); - for(int i=0; icount; i++) - { - becsRemoveEntity(entity[i].id); - } -} - -void EverySystemAddOne(BECSSystemCallData* call_data) -{ - Entity* entity = becsSystemCallDataGetEntitiesArray(call_data); - TestComp2 comp2 = {3, 4}; - ComponentRef ref = {&comp2, TestComp2ID}; - - for(int i=0; icount; i++) - { - becsAddComponents(entity[i].id, 1, &ref); - ///gEntityManager.addComponents(data.entity[i].id, TestComp2()); - } -} - -void EverySystemOnUpdate(BECSSystemCallData* call_data) -{ - //CLocation* location = (CLocation*)becsSystemCallDataGetComponentArray(call_data, CLocationID); - CPosition* pos = (CPosition*)becsSystemCallDataGetComponentArray(call_data, CPositionID); - - for(int i=0; icount; i++) - { - pos[i].x++; - pos[i].y++; - } -}; - -void EverySystemIterate(BECSSystemCallData* call_data) -{ - //CLocation* location = (CLocation*)becsSystemCallDataGetComponentArray(call_data, CLocationID); - CPosition* pos = (CPosition*)becsSystemCallDataGetComponentArray(call_data, CPositionID); - - for(int i=0; icount; i++) - { - pos[i].x++; - pos[i].y++; - } -}; - -/* -struct ChangeTestSystem -{ - mixin ECS.System!16; //__gshared ushort system_id; - - static struct EntitiesData - { - size_t length; - const(Entity)[] entites; - TestComp4[] test4; - @optional TestComp5[] test5; - } - - -}*/ - -void ChangeTestSystemOnCreate() -{ - //writeln("On Change Test System create."); - printf("On Change Test System create.\n"); -} - -void ChangeTestSystemOnDestroy() -{ - //writeln("On Change Test System destroy."); - printf("On Change Test System destroy.\n"); -} - -void ChangeTestSystemOnAddEntity(BECSListenerCallData* call_data) -{ - Entity* entities = becsListenerCallDataGetEntitiesArray(call_data); - //printf("Entity added! ID: "); - for(int i=0; icount; i++) - printf("Entity added! ID: %u\n",(uint32_t) entities[i].id.id); - ////writeln("Entity added! ID: ", entities[i].id); -} - -void ChangeTestSystemOnRemoveEntity(BECSListenerCallData* call_data) -{ - Entity* entities = becsListenerCallDataGetEntitiesArray(call_data); - ////writeln("Entity removed! ID: ", entities[0].id); - printf("Entity removed! ID: %u\n",(uint32_t) entities[0].id.id); -} - -void ChangeTestSystemOnChangeEntity(BECSListenerCallData* call_data) -{ - Entity* entities = becsListenerCallDataGetEntitiesArray(call_data); - ////writeln("Entity changed! ID: ", entities[0].id); - printf("Entity changed! ID: %u\n",(uint32_t) entities[0].id.id); -} - -char ChangeTestSystemOnBegin(void* context) -{ - ////writeln("On Test System begin."); - return 1; -} - -void ChangeTestSystemOnEnd(void* context) -{ - ////writeln("On Test System end."); -} - -void ChangeTestSystemOnUpdate(BECSSystemCallData* call_data) -{ - /*for(int i=0; icount; i++) - { - - }*/ -} - -uint16_t ChangeTestSystemID; - - -typedef struct TestSystem -{ - uint32_t print; - - /* - - void initialize(ref Entity entity, ref TestComp comp) - { - - } - - static struct EntitiesData - { - size_t length; - const(Entity)[] entites; - TestComp[] test; - TestComp2[] test2; - @readonly @optional const(TestComp3)[] test3; - //@excluded TestComp4[] test4; - } - - void handleEvent(TestEvent event, ref TestComp test, ref TestComp2 test2, TestComp3* test3) - { - - }*/ -}TestSystem; - -void TestSystemOnCreate(void* context) -{ - //writeln("On Test System create."); - printf("On Change Test System create.\n"); -} - -void TestSystemOnDestroy(void* context) -{ - //writeln("On Test System destroy."); - printf("On Change Test System destroy.\n"); -} - -void TestSystemOnAddEntity(BECSListenerCallData* call_data) -{ - //foreach(i;0..data.length) - ////writeln("Entity added ID: ",data.entites[i].id.id); -} - -void TestSystemOnRemoveEntity(BECSListenerCallData* call_data) -{ - ////writeln("Entity destroyed ID: ",data.entites[0].id); -} - -char TestSystemOnBegin(void* context) -{ - TestSystem* system = (TestSystem*)context; - if(system->print)printf("On Test System begin.\n"); - return 1; -} - -void TestSystemOnEnd(void* context) -{ - TestSystem* system = (TestSystem*)context; - if(system->print)printf("On Test System end.\n"); - system->print = 0; -} - -void TestSystemOnUpdate(BECSSystemCallData* call_data) -{ - TestComp* test = (TestComp*)becsSystemCallDataGetComponentArray(call_data, TestCompID); - TestComp2* test2 = (TestComp2*)becsSystemCallDataGetComponentArray(call_data, TestComp2ID); - for(int i=0; icount; i++) - { - test[i].a += 1000; - test[i].b += 2000; - test2[i].b += 2; - test2[i].a = 8; - } -} - -uint16_t TestSystemID; - -/* -struct TestSystemWithHighPriority -{ - mixin ECS.System!16; //__gshared ushort system_id; - - static struct EntitiesData - { - TestComp[] test; - } - - void initialize(ref Entity entity, ref TestComp comp) - { - int o = 1; - } -} -*/ - -void TestSystemWithHighPriorityOnUpdate(BECSSystemCallData* call_data) -{ - -} - -uint16_t TestSystemWithHighPriorityID; - -/* -struct Sys1 -{ - mixin ECS.System; - - struct EntitiesData - { - TestComp[] comp; - } -}*/ - -void Sys1OnAddEntity(BECSListenerCallData* call_data) -{ - -} - -uint16_t Sys1ID; -/* -struct Sys2 -{ - mixin ECS.System; - - struct EntitiesData - { - TestComp[] comp; - } -}*/ - -void Sys2OnAddEntity(BECSListenerCallData* call_data) -{ - -} - -uint16_t Sys2ID; -/* -struct Sys3 -{ - mixin ECS.System; - - struct EntitiesData - { - TestComp[] comp; - } -}*/ - -void Sys3OnAddEntity(BECSListenerCallData* call_data) -{ - -} - -void Sys3OnUpdate(BECSSystemCallData* call_data) -{ - -} - -uint16_t Sys3ID; - - -typedef struct EmptyEventSystem -{ - char handled; -}EmptyEventSystem; - -void EmptyEventSystemHandleEvent(BECSEventCallData* call_data) -{ - //TestEvent* event = (TestEvent*)call_data->event; - EmptyEventSystem* context = (EmptyEventSystem*)call_data->context; - if (!context->handled) - { - printf("EmptyEventSystem.handleEvent() called!\n"); - context->handled = 1; - } - exit(0);//assert(0, "this shouldn't be called!"); -} - -uint16_t EmptyEventSystemID; - - -typedef struct EventSystem -{ - char handled; - - /*struct EntitiesData - { - uint thread_id; - TestComp[] comp; - }*/ -}EventSystem; - -void EventSystemHandleTestEvent(BECSEventCallData* call_data) -{ - //TestEvent* event = (TestEvent*)call_data->event; - EmptyEventSystem* context = (EmptyEventSystem*)call_data->context; - if (!context->handled) - { - printf("EventSystem.handleEvent() called!\n"); - context->handled = 1; - } -} - -uint16_t EventSystemID; - -/* -struct EmptySystem -{ - mixin ECS.System; - - struct EntitiesData - { - uint thread_id; - } -}*/ - -void EmptySystemOnUpdate(BECSSystemCallData* call_data) -{ - printf("EmptySystem.onUpdate() - this should be called once per update\n"); -} - -uint16_t EmptySystemID; - -/* -import std.meta; - -struct TestSystem2 -{ - mixin ECS.System!16; //__gshared ushort system_id; - - //alias ExcludedComponents = AliasSeq!("TestComp", "TestComp4"); - - static struct EntitiesData - { - short length; - const(Entity)[] entity; - TestComp3[] test; - //@excluded TestComp[] testt; - } - - static struct EventInput - { - Entity* entity; - TestComp3* test; - //TestComp* tt; - } - - void initialize(ref Entity entity, ref TestComp comp) - { - - } - - void lateUpdate(ref BECSSystemCallData* call_data) - { - for(int i=0;icount;i++) - { - data.test[i].gg -= 1; - //gEntityManager.sendSelfEvent!(TestEvent)(data.entity[i].id, TestEvent()); - } - } - -} -*/ - -void TestSystem2HandleEvent(BECSEventCallData* call_data) -{ - Entity* entity = call_data->entity; - TestEvent* event = (TestEvent*)call_data->event; - EmptyEventSystem* context = (EmptyEventSystem*)call_data->context; - - TestComp3* test = (TestComp3*)becsEntityGetComponent(entity, TestComp3ID); - test->bg = event->a; - TestEvent2 event2; - event2.a = event->a + 8; - becsSendEvent(entity->id, (EventRef){&event2, TestEvent2ID}); -} - -void TestSystem2HandleEvent2(BECSEventCallData* call_data) -{ - Entity* entity = call_data->entity; - TestEvent2* event = (TestEvent2*)call_data->event; - EmptyEventSystem* context = (EmptyEventSystem*)call_data->context; - - TestComp3* test = (TestComp3*)becsEntityGetComponent(entity, TestComp3ID); - test->gg =(uint32_t) event->a; -} - -void TestSystem2OnEnable() -{ - - //writeln("TestSystem2 enabled"); - printf("TestSystem2 enabled\n"); -} - -void TestSystem2OnDisable() -{ - - //writeln("TestSystem2 disabled"); - printf("TestSystem2 disabled\n"); -} - -void TestSystem2OnUpdate(BECSSystemCallData* call_data) -{ - TestComp3* test = (TestComp3*)becsSystemCallDataGetComponentArray(call_data, TestComp3ID); - Entity* entity = becsSystemCallDataGetEntitiesArray(call_data); - for(int i=0;icount;i++) - { - test[i].gg += 14; - TestEvent event; - event.a = test[i].gg + 4; - becsSendEvent(entity[i].id, (EventRef){&event, TestEventID}); - //gEntityManager.sendEvent!(TestEvent)(data.entity[i].id, event); - //gEntityManager.sendSelfEvent!(TestEvent)(data.entity[i].id, TestEvent()); - } -} - -uint16_t TestSystem2ID; - -typedef struct ExternalUpdateCallTest -{ - int print_count;// = 3 -}ExternalUpdateCallTest; - -void ExternalUpdateCallTestUpdate(BECSSystemCallData* call_data) -{ - TestComp3* test = becsSystemCallDataGetComponentArray(call_data, TestComp3ID); - ExternalUpdateCallTest* context = (ExternalUpdateCallTest*)call_data->context; - - if (context->print_count > 0) - { - context->print_count--; - printf("ExternalUpdateCallTest %u %u\n", test[0].gg,(uint32_t) call_data->count); - } -} - -static uint16_t EverySystemID; - -void writeEntityComponents(Entity* entity) -{ - - printf("EntityID(%u, %u)", (uint32_t) entity->id.id, (uint32_t) entity->id.counter); - //write(entity->id); - TestComp* test_comp = (TestComp*)becsEntityGetComponent(entity, TestCompID);//.getComponent!TestComp; - if (test_comp) - printf("TestComp(%u, %u)", (uint32_t) test_comp->a, (uint32_t) test_comp->b); //write(*test_comp); - TestComp2* test_comp2 = (TestComp2*)becsEntityGetComponent(entity, TestComp2ID);//.getComponent!TestComp2; - if (test_comp2) - printf("TestComp2(%u, %u)", (uint32_t) test_comp2->b, (uint32_t) test_comp2->a); //write(*test_comp2); - TestComp3* test_comp3 = (TestComp3*)becsEntityGetComponent(entity, TestComp3ID);//.getComponent!TestComp3; - if (test_comp3) - printf("TestComp3(%u, %u)", (uint32_t) test_comp3->gg, (uint32_t) test_comp3->bg); //write(*test_comp3); - TestComp4* test_comp4 = (TestComp4*)becsEntityGetComponent(entity, TestComp4ID);//.getComponent!TestComp4; - if (test_comp4) - printf("TestComp4(%u, %u, %u, %u, %u, %u)", test_comp4->gg, test_comp4->bg, - (uint32_t) test_comp4->a, (uint32_t) test_comp4->b, - (uint32_t) test_comp4->c, (uint32_t) test_comp4->g); //write(*test_comp4); - printf("\n"); - //writeln(); - ////writeln(((uint32_t*) pp)[0 .. 14], " ", pp); -} - -int main() -{ - becsInitialize(); - //gEntityManager.setMultithreadingCallbacks(&dispatch, &getID); - - becsBeginRegister(); - uint16_t fixed_pass = becsRegisterPass("fixed"); - - long time = getUSecTime(); - - TestComp2 test_comp2 = {3,4}; - TestComp2ID = BECS_REGISTER_COMPONENT(test_comp2); - TestComp4 test_comp4 = {7,8,9,10,11,12}; - TestComp4ID = BECS_REGISTER_COMPONENT(test_comp4); - TestComp test_comp = {1,2}; - TestCompID = BECS_REGISTER_COMPONENT(test_comp); - TestComp3 test_comp3 = {5,6}; - TestComp3ID = BECS_REGISTER_COMPONENT(test_comp3); - TestComp5 test_comp5 = {7,8,9,10,11,12}; - TestComp5ID = BECS_REGISTER_COMPONENT(test_comp5); - CPosition position = {0,0}; - CPositionID = BECS_REGISTER_COMPONENT(position); - - CLocation loc = {10,12.2}; - CLocationID = BECS_REGISTER_COMPONENT(loc); - CVelocity vel = {1,2.6}; - CVelocityID = BECS_REGISTER_COMPONENT(vel); - - TestEventID = becsRegisterEvent("TestEvent", (BECSEventRegisterInfo){sizeof(TestEvent), alignof(TestEvent)}); - TestEvent2ID = becsRegisterEvent("TestEvent2", (BECSEventRegisterInfo){sizeof(TestEvent2), alignof(TestEvent2)}); - - printf("Components register: %f usecs\n", (float)(getUSecTime() - time)); - time = getUSecTime(); - - /*{ - BECSSystemComponentInfo array[] = {(BECSSystemComponentInfo){0}}; - BECSSystemRegisterInfo register_info; - register_info.components_count = 1; - register_info.components = array; - register_info.on_update = onUpdateTest; - becsRegisterSystem("System", register_info); - }*/ - - { - BECSSystemComponentInfo array[] = {(BECSSystemComponentInfo){TestCompID}}; - BECSSystemRegisterInfo register_info = {}; - register_info.priority = 100; - register_info.components_count = 1; - register_info.components = array; - register_info.pass_id = fixed_pass; - register_info.on_update = TestSystemWithHighPriorityOnUpdate; - TestSystemWithHighPriorityID = becsRegisterSystem("TestSystemWithHighPriority", register_info); - } - - { - BECSSystemComponentInfo array[] = { - (BECSSystemComponentInfo){TestCompID}, - (BECSSystemComponentInfo){TestComp2ID}, - (BECSSystemComponentInfo){TestComp3ID,optional}}; - BECSSystemRegisterInfo register_info = {}; - register_info.priority = 0; - register_info.system_size = sizeof(TestSystem); - register_info.init_data = &(TestSystem){1}; - register_info.components_count = 3; - register_info.components = array; - register_info.on_create = TestSystemOnCreate; - register_info.on_destroy = TestSystemOnDestroy; - register_info.on_add_entity = TestSystemOnAddEntity; - register_info.on_update = TestSystemOnUpdate; - register_info.on_begin = TestSystemOnBegin; - register_info.on_end = TestSystemOnEnd; - TestSystemID = becsRegisterSystem("TestSystem", register_info); - } - - { - BECSSystemComponentInfo array[] = { - (BECSSystemComponentInfo){TestComp4ID}, - (BECSSystemComponentInfo){TestComp5ID,optional}}; - BECSSystemRegisterInfo register_info = {}; - register_info.priority = 0; - register_info.components_count = 2; - register_info.components = array; - register_info.on_update = ChangeTestSystemOnUpdate; - register_info.on_create = ChangeTestSystemOnCreate; - register_info.on_destroy = ChangeTestSystemOnDestroy; - register_info.on_add_entity = ChangeTestSystemOnAddEntity; - register_info.on_remove_entity = ChangeTestSystemOnRemoveEntity; - register_info.on_change_entity = ChangeTestSystemOnChangeEntity; - register_info.on_begin = ChangeTestSystemOnBegin; - register_info.on_end = ChangeTestSystemOnEnd; - ChangeTestSystemID = becsRegisterSystem("ChangeTestSystem", register_info); - } - - { - BECSSystemComponentInfo array[] = {(BECSSystemComponentInfo){TestCompID}}; - BECSSystemRegisterInfo register_info= {}; - register_info.priority = 10; - register_info.components_count = 1; - register_info.components = array; - register_info.on_add_entity = Sys1OnAddEntity; - Sys1ID = becsRegisterSystem("Sys1", register_info); - } - - { - BECSSystemComponentInfo array[] = {(BECSSystemComponentInfo){TestCompID}}; - BECSSystemRegisterInfo register_info= {}; - register_info.priority = -100; - register_info.components_count = 1; - register_info.components = array; - register_info.on_add_entity = Sys2OnAddEntity; - Sys2ID = becsRegisterSystem("Sys2", register_info); - } - - { - BECSSystemComponentInfo array[] = {(BECSSystemComponentInfo){TestCompID}}; - BECSSystemRegisterInfo register_info= {}; - register_info.priority = -2; - register_info.components_count = 1; - register_info.components = array; - register_info.on_update = Sys3OnUpdate; - register_info.on_add_entity = Sys3OnAddEntity; - Sys3ID = becsRegisterSystem("Sys3", register_info); - } - - { - BECSSystemRegisterInfo register_info= {}; - register_info.priority = 2; - register_info.on_update = EmptySystemOnUpdate; - EmptySystemID = becsRegisterSystem("EmptySystem", register_info); - } - - { - BECSEventCallback event_array[] = {(BECSEventCallback){TestEventID, EmptyEventSystemHandleEvent}}; - BECSSystemRegisterInfo register_info= {}; - register_info.priority = 2; - register_info.system_size = sizeof(EmptyEventSystem); - register_info.init_data = &(EmptyEventSystem){}; - register_info.event_handlers_count = 1; - register_info.event_handlers = event_array; - EmptyEventSystemID = becsRegisterSystem("EmptyEventSystem", register_info); - } - - { - BECSEventCallback event_array[] = {(BECSEventCallback){TestEventID, EventSystemHandleTestEvent}}; - BECSSystemComponentInfo array[] = {(BECSSystemComponentInfo){TestCompID}}; - BECSSystemRegisterInfo register_info= {}; - register_info.priority = 2; - register_info.system_size = sizeof(EventSystem); - register_info.init_data = &(EventSystem){}; - register_info.components_count = 1; - register_info.components = array; - register_info.event_handlers_count = 1; - register_info.event_handlers = event_array; - EventSystemID = becsRegisterSystem("EventSystem", register_info); - } - - { - BECSSystemComponentInfo array[] = {(BECSSystemComponentInfo){CPositionID}}; - BECSSystemRegisterInfo register_info= {}; - register_info.priority = 0; - register_info.components_count = 1; - register_info.components = array; - register_info.on_update = EverySystemOnUpdate; - EverySystemID = becsRegisterSystem("EverySystem", register_info); - } - - becsEndRegister(); - - printf("Systems register: %f usecs\n", (float)(getUSecTime() - time)); - time = getUSecTime(); - - uint16_t ids[2] = {TestComp2ID, TestCompID}; - EntityTemplate* tmpl = becsAllocateTemplate(2, ids); - - uint16_t ids2[2] = {TestComp3ID, TestCompID}; - EntityTemplate* tmpl2 = becsAllocateTemplate(2, ids2); - - printf("Template allocating: %f usecs\n", (float)(getUSecTime() - time)); - time = getUSecTime(); - - uint16_t empty_ids[1] = {CPositionID}; - EntityTemplate* tmpl_empty = becsAllocateTemplate(1, empty_ids); - - becsCommit(); - - time = getUSecTime(); - for(int i = 0;i < 4000000; i++)becsAddEntity(tmpl_empty); - becsCommit(); - for(int i = 0;i < 4000000; i++)becsAddEntity(tmpl_empty); - becsCommit(); - for(int i = 0;i < 2000000; i++)becsAddEntity(tmpl_empty); - becsCommit(); - - printf("Adding 1M entities: %f usecs\n", (float)(getUSecTime() - time)); - - becsCommit(); - time = getUSecTime(); - becsCallEntitiesFunction(EverySystemID, EverySystemIterate, NULL); - printf("Iterate 1M entities: %f usecs\n", (float)(getUSecTime() - time)); - - becsBegin(); - time = getUSecTime(); - becsUpdate(0); - printf("Iterate 1M entities (update): %f usecs\n", (float)(getUSecTime() - time)); - becsEnd(); - - time = getUSecTime(); - becsCallEntitiesFunction(EverySystemID,EverySystemFree, NULL); - becsCommit(); - printf("Deleting 1M entities: %f usecs\n", (float)(getUSecTime() - time)); - - time = getUSecTime(); - for(int i = 0;i < 4000000; i++)becsAddEntity(tmpl_empty); - becsCommit(); - for(int i = 0;i < 4000000; i++)becsAddEntity(tmpl_empty); - becsCommit(); - for(int i = 0;i < 2000000; i++)becsAddEntity(tmpl_empty); - becsCommit(); - - printf("Adding 1M entities (prealloc): %f usecs\n", (float)(getUSecTime() - time)); - - becsCommit(); - time = getUSecTime(); - becsCallEntitiesFunction(EverySystemID, EverySystemAddOne, NULL); - becsCommit(); - printf("Adding 1M component: %f usecs\n", (float)(getUSecTime() - time)); - - becsCommit(); - becsCallEntitiesFunction(EverySystemID,EverySystemFree, NULL); - becsCommit(); - - time = getUSecTime(); - - EntityID entity; - - { - entity = becsAddEntity(tmpl)->id; - writeEntityComponents(becsGetEntity(entity)); - /*EntityManager.EntitiesBlock* block = gEntityManager.getMetaData( - gEntityManager.getEntity(entity)); - EntityManager.EntityInfo* info = block.type_info;*/ - //writeln(info.add_listeners); - //if(info)assert(0); - } - - time = getUSecTime(); - - //EntityID[] idss = Mallocator.makeArray!EntityID(5000); //[5000] - EntityID* idss = (EntityID*)malloc(sizeof(EntityID) * 5000); - - for (int i=0;i<200;i++) - { - becsBegin(); - for (int j=0;j<5000;j++) - idss[j] = becsAddEntity(tmpl)->id; - for (int j=0;j<5000;j++) - becsRemoveEntity(idss[j]); - becsEnd(); - } - becsCommit(); - - printf("Entities adding: %f usecs\n", (float)(getUSecTime() - time)); - free(idss); - time = getUSecTime(); - - uint32_t blocks = 0; - /*foreach (info; &gEntityManager.entities_infos.byValue) - { - EntityManager.EntitiesBlock* block = info.first_block; - while (block !is null) - { - block = block.next_block; - blocks++; - } - }*/ - //writeln("Entities blocks: ", blocks); - printf("Entities blocks: %u\n", blocks); - - becsBeginRegister(); - { - BECSEventCallback event_array[] = { - (BECSEventCallback){TestEventID, TestSystem2HandleEvent}, - (BECSEventCallback){TestEvent2ID, TestSystem2HandleEvent2}}; - BECSSystemComponentInfo array[] = {(BECSSystemComponentInfo){TestComp3ID}}; - BECSSystemRegisterInfo register_info; - register_info.priority = 0; - register_info.components_count = 1; - register_info.components = array; - register_info.event_handlers_count = 2; - register_info.event_handlers = event_array; - register_info.on_enable = TestSystem2OnEnable; - register_info.on_disable = TestSystem2OnDisable; - register_info.on_update = TestSystem2OnUpdate; - TestSystem2ID = becsRegisterSystem("TestSystem2", register_info); - } - becsEndRegister(); - - EntityID entity2; - - time = getUSecTime(); - - EntityID* entities = (EntityID*)malloc(sizeof(EntityID) * 1000000); - for (int i=0;i< 500000;i++) - { - entity2 = becsAddEntity(tmpl)->id; - entities[i * 2] = entity2; - entities[i * 2 + 1] = becsAddEntity(tmpl2)->id; - } - - becsCommit(); - - printf("Entities adding2: %f usecs\n", (float)(getUSecTime() - time)); - time = getUSecTime(); - - for (int i=0;i< 1000000;i++) - { - becsAddComponents(entities[i], 1, &(ComponentRef){&(TestComp5){7, 8, 9, 10, 11, 12}, TestComp5ID}); - if ((i & 0x00FFFF) == 0) - becsCommit(); - } - - becsCommit(); - - printf("Components adding: %f usecs\n", (float)(getUSecTime() - time)); - time = getUSecTime(); - - for (int i=0;i< 1000000;i++) - { - becsRemoveComponents(entities[i], 1, &TestComp5ID); - //if((i & 0x00FFFF) == 0)gEntityManager.commit(); - } - - becsCommit(); - printf("Components removing: %f usecs\n", (float)(getUSecTime() - time)); - time = getUSecTime(); - - free(entities); - - time = getUSecTime(); - - becsBegin(); - becsUpdate(0); - becsEnd(); - - printf("Update: %f usecs\n", (float)(getUSecTime() - time)); - - writeEntityComponents(becsGetEntity(entity2)); - - time = getUSecTime(); - - becsBegin(); - becsUpdateMT(0); - becsEnd(); - - printf("Update: %f usecs\n", (float)(getUSecTime() - time)); - - writeEntityComponents(becsGetEntity(entity2)); - - time = getUSecTime(); - - becsBegin(); - becsUpdate(0); - becsEnd(); - - printf("Update: %f usecs\n", (float)(getUSecTime() - time)); - - writeEntityComponents(becsGetEntity(entity2)); - - entity = becsAddEntity(tmpl)->id; - - becsBegin(); - becsUpdate(0); - becsEnd(); - - writeEntityComponents(becsGetEntity(entity)); - - becsAddEntity(tmpl); - writeEntityComponents(becsGetEntity(entity)); - writeEntityComponents(becsAddEntityCopy(entity)); - EntityTemplate* copy_tempalte = becsAllocateTemplateFromEntity(entity, 0); - writeEntityComponents(becsAddEntity(copy_tempalte)); - EntityTemplate* copy_default_tempalte = becsAllocateTemplateFromEntity(entity, 1); - writeEntityComponents(becsAddEntity(copy_default_tempalte)); - - becsAddComponents(entity, 1, &(ComponentRef){&(TestComp4){7,8,9,10,11,12},TestComp4ID}); - becsAddComponents(entity, 1, &(ComponentRef){&(TestComp3){5,6},TestComp3ID}); - - becsBegin(); - becsUpdate(0); - becsEnd(); - - writeEntityComponents(becsGetEntity(entity)); - - becsRemoveComponents(entity, 1, &TestCompID); - becsAddComponents(entity, 1, &(ComponentRef){&(TestComp){1,2},TestCompID}); - becsAddComponents(entity, 1, &(ComponentRef){&(TestComp5){7,8,9,10,11,12},TestComp5ID}); - - becsBegin(); - becsUpdate(0); - becsUpdate(fixed_pass); - becsEnd(); - - becsRemoveComponents(entity, 1, &TestComp4ID); - - becsCommit(); - - // BECSSystem* sys = (BECSSystem*)becsGetSystem(TestSystem2ID); - - ExternalUpdateCallTest external_update_test = {3}; - - becsCallEntitiesFunction(TestSystem2ID, ExternalUpdateCallTestUpdate, &external_update_test); - - printf("pre end\n"); - - writeEntityComponents(becsGetEntity(entity)); - becsFreeTemplate(tmpl_empty); - becsFreeTemplate(tmpl); - becsFreeTemplate(tmpl2); - becsFreeTemplate(copy_tempalte); - becsFreeTemplate(copy_default_tempalte); - becsDestroy(); - - printf("end\n"); - -/* - unsigned short components[1] = {CLocationID}; - EntityTemplate* template = becsAllocateTemplate(1, components); - unsigned short components2[2] = {CVelocityID, CLocationID}; - EntityTemplate* template2 = becsAllocateTemplate(2, components2); - Entity* entity = becsAddEntity(template); - Entity* entity2 = becsAddEntity(template2); - Entity* entity3 = becsAddEntity(template2); - printf("%lu\n", (unsigned long)entity); - entity = becsGetEntity(entity->id); - printf("%lu\n", (unsigned long)entity); - CLocation* location = (CLocation*)becsEntityGetComponent(entity, CLocationID); - printf("comp %f %f\n", location->x, location->y); - - entity2 = becsGetEntity(entity2->id); - CVelocity* velocity = (CVelocity*)becsEntityGetComponent(entity2, CVelocityID); - printf("compv %f %f\n", velocity->x, velocity->y); - - entity3 = becsGetEntity(entity3->id); - location = (CLocation*)becsEntityGetComponent(entity3, CLocationID); - printf("comp %f %f\n", location->x, location->y); - - becsBegin(); - becsUpdate(0); - becsEnd(); - printf("comp %f %f\n", location->x, location->y);*/ - return 0; -} \ No newline at end of file diff --git a/compile_wasm.py b/compile_wasm.py index 3bd1afd..7214801 100644 --- a/compile_wasm.py +++ b/compile_wasm.py @@ -13,7 +13,7 @@ def compile(sources, output): if file_extension == '.d' and filename != 'package': files.append(os.path.join(r, file)) - ldc_cmd = 'ldc2 ' + shared_flags + ldc_flags + '-oq -mtriple=wasm32-unknown-unknown-wasm -betterC --output-bc --od=.bc --singleobj --checkaction=C --of=' + output + ' ' + ldc_cmd = 'ldc2 ' + shared_flags + ldc_flags + '-oq -mtriple=wasm32-unknown-emscripten -betterC --output-bc --od=.bc --singleobj --checkaction=C --of=' + output + ' ' for path in sources: ldc_cmd += '-I' + path + ' ' @@ -33,7 +33,7 @@ def compile(sources, output): shared_flags = '' clean = 0 emc_flags = '' -ldc_flags = '--d-version=ECSEmscripten ' +ldc_flags = '' import_paths = ['source','tests'] build_tests = 0 diff --git a/demos/compile_wasm.py b/demos/compile_wasm.py index 336221a..b178260 100644 --- a/demos/compile_wasm.py +++ b/demos/compile_wasm.py @@ -1,7 +1,6 @@ import os import ntpath import sys -import imp def compile(sources, output): files = [] @@ -14,7 +13,7 @@ def compile(sources, output): if file_extension == '.d' and filename != 'package': files.append(os.path.join(r, file)) - ldc_cmd = compiler + shared_flags + ldc_flags + '-oq -mtriple=wasm32-unknown-unknown-wasm -betterC --output-bc --od=.bc --singleobj --checkaction=C --of=' + output + ' ' + ldc_cmd = compiler + shared_flags + ldc_flags + '-oq -mtriple=wasm32-unknown-emscripten -betterC --output-bc --od=.bc --singleobj --checkaction=C --of=' + output + ' ' for path in sources: ldc_cmd += '-I' + path + ' ' @@ -41,7 +40,7 @@ only_bc = 0 multi = 0 sources = ['tests', 'source'] emc_flags = '-s USE_SDL=2 -s USE_SDL_IMAGE=2 -s SDL2_IMAGE_FORMATS="[\'png\']" ' -ldc_flags = '--d-version=ECSEmscripten --d-version=SDL_209 --d-version=BindSDL_Static --d-version=BindSDL_Image --d-version=MM_USE_POSIX_THREADS ' +ldc_flags = '--d-version=SDL_209 --d-version=BindSDL_Static --d-version=BindSDL_Image --d-version=MM_USE_POSIX_THREADS ' import_paths = ['external/sources', 'external/imports', 'external/wasm_imports', '../source', 'utils/source', 'simple/source'] for arg in sys.argv[1:]: @@ -75,7 +74,7 @@ for arg in sys.argv[1:]: elif(arg == '-opt'): shared_flags += '-O3 ' ldc_flags += '-release -enable-inlining ' - emc_flags += '--llvm-lto 3 ' + emc_flags += '--llvm-lto 3 -s SIMD=1 ' elif(arg == '-quiet'): emc_flags += "-Wl,--no-check-features " elif(arg == '--clean'): @@ -121,8 +120,9 @@ emcc_cmd += 'demo.bc ' os.system("mkdir build") -emscripten = imp.load_source('', os.path.expanduser("~") + '/.emscripten') -pack_cmd = emscripten.EMSCRIPTEN_ROOT + '/tools/file_packager.py build/assets.data --preload assets --js-output=build/assets.js' +# emscripten = imp.load_source('', os.path.expanduser("~") + '/.emscripten') +# pack_cmd = emscripten.EMSCRIPTEN_ROOT + '/tools/file_packager.py build/assets.data --preload assets --js-output=build/assets.js' +pack_cmd = os.path.expandvars('$EMSDK/upstream/emscripten') + '/tools/file_packager.py build/assets.data --preload assets --js-output=build/assets.js' print('Packafing files: ' + pack_cmd) os.system(pack_cmd) diff --git a/demos/meson.build b/demos/meson.build index 20df64c..8a3642d 100644 --- a/demos/meson.build +++ b/demos/meson.build @@ -19,7 +19,7 @@ sdl2_image_dep = dependency('SDL2_image') subdir('utils') # Utils library -executable('decs-demos', [demos_src, external_src], +executable('BubelECSDemos', [demos_src, external_src], include_directories : [demos_inc, external_inc], d_module_versions : versions, link_with : [ecs_lib, ecs_utils_lib], @@ -27,7 +27,7 @@ executable('decs-demos', [demos_src, external_src], bindbc_loader_dep, bindbc_sdl_dep, cimgui_dep, - decs_dep, + bubel_ecs_dep, ecs_utils_dep, sdl2_dep, sdl2_image_dep, diff --git a/demos/utils/meson.build b/demos/utils/meson.build index de9e958..708fc85 100644 --- a/demos/utils/meson.build +++ b/demos/utils/meson.build @@ -5,12 +5,12 @@ subdir('source/ecs_utils') utils_inc = include_directories('source/') # Dependencies -ecs_utils_lib = library('ecs_utils', utils_src, +ecs_utils_lib = library('ECSUtils', utils_src, include_directories : [demos_inc, external_inc, utils_inc], link_args : link_args, d_module_versions : versions, dependencies : [ - decs_dep, + bubel_ecs_dep, bindbc_loader_dep, bindbc_sdl_dep, ] diff --git a/dub.json b/dub.json index 8e2a1b3..602117d 100755 --- a/dub.json +++ b/dub.json @@ -1,11 +1,11 @@ { - "name": "bubel_ecs", - "targetName" : "bubel_ecs", + "name": "bubel-ecs", + "targetName" : "BubelECS", "authors": [ "Michał Masiukiewicz", "Dawid Masiukiewicz" ], "description": "Dynamic Entity Component System", - "copyright": "Copyright © 2018-2019, Michał Masiukiewicz, Dawid Masiukiewicz", + "copyright": "Copyright © 2018-2023, Michał Masiukiewicz, Dawid Masiukiewicz", "license": "BSD 3-clause", "sourcePaths" : ["source\/"], "excludedSourceFiles":[ @@ -63,7 +63,7 @@ ], "dflags": [ "-unittest", - "-cov" + "-cov=ctfe" ] }, { diff --git a/meson.build b/meson.build index 4dd7212..141cb25 100644 --- a/meson.build +++ b/meson.build @@ -1,17 +1,15 @@ -project('decs', 'd', version : '0.1.0') +project('bubel-ecs', 'd', version : '0.5.0') # Options betterC_opt = get_option('betterC') BuildDemos_opt = get_option('BuildDemos') BuildTests_opt = get_option('BuildTests') LTO_otp = get_option('LTO') -C_API_opt = get_option('C-API') summary('betterC enabled', betterC_opt) summary('build demos', BuildDemos_opt) summary('build tests', BuildTests_opt) summary('LTO enabled', LTO_otp) -summary('C-API enabled', C_API_opt) meson_minimum_version = '>=0.57.1' assert(meson.version().version_compare(meson_minimum_version), 'Newer verson of meson required, current version: @0@, required: @1@'.format(meson.version(), meson_minimum_version)) @@ -20,14 +18,7 @@ assert(meson.version().version_compare(meson_minimum_version), 'Newer verson of src = files() subdir('source') -inc = [include_directories('source/')] - -#C API files -if C_API_opt - c_src = files() - subdir('c-api') - inc += include_directories('c-api/') -endif +inc = include_directories('source/') # Arguments args = [] @@ -52,55 +43,45 @@ if betterC_opt if comp_id == 'gcc' args += ['-fno-druntime'] link_args += ['-fno-druntime'] - else + else args += '-betterC' link_args += '-betterC' endif endif -if comp_id == 'gcc' - args+='-pthread' - link_args+='-pthread' -endif - add_project_arguments(args, language : 'd') add_project_link_arguments(link_args, language : 'd') # Dependencies threads_dep = dependency('threads') -ecs_lib = library('decs', +d_versions = [] +deps = [] +if host_machine.cpu_family() == 'wasm32' + +else + # meson incorectly adds "-s USE_PTHREADS=1" to ldc2 invocation whe pthreads is added as dependency + # add it for non wasm builds + deps += threads_dep +endif + +ecs_lib = library('BubelECS', src, + d_module_versions : d_versions, include_directories : [inc], ) -decs_dep = declare_dependency( +bubel_ecs_dep = declare_dependency( include_directories : [inc], link_with : ecs_lib, - dependencies : threads_dep, + dependencies : deps, ) +meson.override_dependency('bubel-ecs', bubel_ecs_dep) + # Tests if BuildTests_opt subdir('tests') - - executable('d-api-tests', - ['tests/tests.d'], - include_directories : [inc], - d_args : args, - link_args : link_args, - dependencies : decs_dep, - ) - - if C_API_opt - add_languages('c') - executable('c-api-tests', - ['c-api/test.c'], - include_directories : [inc], - dependencies : decs_dep, - ) - endif - endif # Demos diff --git a/meson_options.txt b/meson_options.txt index 30f9c3d..335f32b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -2,4 +2,3 @@ option('betterC', type: 'boolean', value: false) option('BuildDemos', type: 'boolean', value: false) option('BuildTests', type: 'boolean', value: false) option('LTO', type: 'boolean', value: false) -option('C-API', type: 'boolean', value: false) diff --git a/source/bubel/ecs/atomic.d b/source/bubel/ecs/atomic.d index 8ea43df..d8995ae 100644 --- a/source/bubel/ecs/atomic.d +++ b/source/bubel/ecs/atomic.d @@ -4,129 +4,10 @@ It's internal code. Can be used for atomics if emscripten backend will be used. This module contain atomic operations which include support for emscripten atomics functions. Emscripten functions are contained in API similar to druntime. -Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz License: BSD 3-clause, see LICENSE file in project root folder. */ module bubel.ecs.atomic; -version (Emscripten) version = ECSEmscripten; +public import core.atomic; -version (ECSEmscripten) -{ - import std.traits; - - enum MemoryOrder - { - acq, - acq_rel, - raw, - rel, - seq - } - - extern (C) ubyte emscripten_atomic_cas_u8(void* addr, ubyte oldVal, ubyte newVal) @nogc nothrow pure; - extern (C) ushort emscripten_atomic_cas_u16(void* addr, ushort oldVal, ushort newVal) @nogc nothrow pure; - extern (C) uint emscripten_atomic_cas_u32(void* addr, uint oldVal, uint newVal) @nogc nothrow pure; - - extern (C) ubyte emscripten_atomic_load_u8(const void* addr) @nogc nothrow pure; - extern (C) ushort emscripten_atomic_load_u16(const void* addr) @nogc nothrow pure; - extern (C) uint emscripten_atomic_load_u32(const void* addr) @nogc nothrow pure; - - extern (C) ubyte emscripten_atomic_store_u8(void* addr, ubyte val) @nogc nothrow pure; - extern (C) ushort emscripten_atomic_store_u16(void* addr, ushort val) @nogc nothrow pure; - extern (C) uint emscripten_atomic_store_u32(void* addr, uint val) @nogc nothrow pure; - - extern (C) ubyte emscripten_atomic_add_u8(void* addr, ubyte val) @nogc nothrow pure; - extern (C) ushort emscripten_atomic_add_u16(void* addr, ushort val) @nogc nothrow pure; - extern (C) uint emscripten_atomic_add_u32(void* addr, uint val) @nogc nothrow pure; - - extern (C) ubyte emscripten_atomic_sub_u8(void* addr, ubyte val) @nogc nothrow pure; - extern (C) ushort emscripten_atomic_sub_u16(void* addr, ushort val) @nogc nothrow pure; - extern (C) uint emscripten_atomic_sub_u32(void* addr, uint val) @nogc nothrow pure; - - public pure nothrow @nogc Unqual!T atomicOp(string op, T, V1)(ref shared T val, V1 mod) - { - static if (op == "+=") - { - static if (is(T == byte) || is(T == ubyte)) - return cast(Unqual!T)(emscripten_atomic_add_u8(cast(void*)&val, - cast(Unqual!T) mod) + 1); - else static if (is(T == short) || is(T == ushort)) - return cast(Unqual!T)(emscripten_atomic_add_u16(cast(void*)&val, - cast(Unqual!T) mod) + 1); - else static if (is(T == int) || is(T == uint)) - return cast(Unqual!T)(emscripten_atomic_add_u32(cast(void*)&val, - cast(Unqual!T) mod) + 1); - else - static assert(0); - } - else static if (op == "-=") - { - static if (is(T == byte) || is(T == ubyte)) - return cast(Unqual!T)(emscripten_atomic_sub_u8(cast(void*)&val, - cast(Unqual!T) mod) - 1); - else static if (is(T == short) || is(T == ushort)) - return cast(Unqual!T)(emscripten_atomic_sub_u16(cast(void*)&val, - cast(Unqual!T) mod) - 1); - else static if (is(T == int) || is(T == uint)) - return cast(Unqual!T)(emscripten_atomic_sub_u32(cast(void*)&val, - cast(Unqual!T) mod) - 1); - else - static assert(0); - } - } - - public pure nothrow @nogc @trusted void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val, - V newval) - { - alias UT = Unqual!T; - static if (is(UT == bool) || is(UT == byte) || is(UT == ubyte)) - emscripten_atomic_store_u8(cast(void*)&val, cast(UT) newval); - else static if (is(UT == short) || is(UT == ushort)) - emscripten_atomic_store_u16(cast(void*)&val, cast(UT) newval); - else static if (is(UT == int) || is(UT == uint)) - emscripten_atomic_store_u32(cast(void*)&val, cast(UT) newval); - else - static assert(0); - } - - public pure nothrow @nogc @trusted T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)( - ref const T val) - { - alias UT = Unqual!T; - static if (is(UT == bool)) - return emscripten_atomic_load_u8(cast(const void*)&val) != 0; - else static if (is(UT == byte) || is(UT == ubyte)) - return emscripten_atomic_load_u8(cast(const void*)&val); - else static if (is(UT == short) || is(UT == ushort)) - return emscripten_atomic_load_u16(cast(const void*)&val); - else static if (is(UT == int) || is(UT == uint)) - return emscripten_atomic_load_u32(cast(const void*)&val); - else - static assert(0); - } - - public pure nothrow @nogc @trusted bool cas(MemoryOrder succ = MemoryOrder.seq, - MemoryOrder fail = MemoryOrder.seq, T, V1, V2)(T* here, V1 ifThis, V2 writeThis) - { - alias UT = Unqual!T; - static if (is(UT == bool)) - return emscripten_atomic_cas_u8(cast(void*) here, - cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis; - else static if (is(UT == byte) || is(UT == ubyte)) - return emscripten_atomic_cas_u8(cast(void*) here, - cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis; - else static if (is(UT == short) || is(UT == ushort)) - return emscripten_atomic_cas_u16(cast(void*) here, - cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis; - else static if (is(UT == int) || is(UT == uint)) - return emscripten_atomic_cas_u32(cast(void*) here, - cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis; - else - static assert(0); - } -} -else -{ - public import core.atomic; -} diff --git a/source/bubel/ecs/attributes.d b/source/bubel/ecs/attributes.d index d094aad..1a041d4 100644 --- a/source/bubel/ecs/attributes.d +++ b/source/bubel/ecs/attributes.d @@ -17,7 +17,7 @@ Struct EntitiesData } --- -Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz License: BSD 3-clause, see LICENSE file in project root folder. */ module bubel.ecs.attributes; diff --git a/source/bubel/ecs/block_allocator.d b/source/bubel/ecs/block_allocator.d index 6894a17..0ab735e 100644 --- a/source/bubel/ecs/block_allocator.d +++ b/source/bubel/ecs/block_allocator.d @@ -3,7 +3,7 @@ It's internal code. Module contain memory allocator. -Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz License: BSD 3-clause, see LICENSE file in project root folder. */ module bubel.ecs.block_allocator; diff --git a/source/bubel/ecs/core.d b/source/bubel/ecs/core.d index f8511ea..c796423 100644 --- a/source/bubel/ecs/core.d +++ b/source/bubel/ecs/core.d @@ -67,7 +67,7 @@ Struct System1 } --- -Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz License: BSD 3-clause, see LICENSE file in project root folder. */ module bubel.ecs.core; diff --git a/source/bubel/ecs/entity.d b/source/bubel/ecs/entity.d index cb3f752..76a8a2f 100644 --- a/source/bubel/ecs/entity.d +++ b/source/bubel/ecs/entity.d @@ -1,7 +1,7 @@ /************************************************************************************************************************ Entity module. -Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz License: BSD 3-clause, see LICENSE file in project root folder. */ module bubel.ecs.entity; @@ -44,7 +44,7 @@ struct Entity return cast(T*)getComponent(becsID!T); } - void* getComponent(ushort component_id) const + export void* getComponent(ushort component_id) const { EntityManager.EntitiesBlock* block = gEntityManager.getMetaData(&this); EntityManager.EntityInfo* info = block.type_info; @@ -54,7 +54,7 @@ struct Entity return (cast(void*)block + info.deltas[component_id] + block.entityIndex(&this) * gEntityManager.components[component_id].size); } - bool hasComponent(ushort component_id) const + export bool hasComponent(ushort component_id) const { EntityManager.EntitiesBlock* block = gEntityManager.getMetaData(&this); EntityManager.EntityInfo* info = block.type_info; @@ -62,7 +62,7 @@ struct Entity return true; } - EntityMeta getMeta() const + export EntityMeta getMeta() const { EntityMeta meta; meta.block = gEntityManager.getMetaData(&this); @@ -85,7 +85,7 @@ struct EntityMeta return cast(T*)getComponent(becsID!T); } - void* getComponent(ushort component_id) const + export void* getComponent(ushort component_id) const { const (EntityManager.EntityInfo)* info = block.type_info; @@ -95,7 +95,7 @@ struct EntityMeta return (cast(void*)block + info.deltas[component_id] + index * gEntityManager.components[component_id].size); } - bool hasComponent(ushort component_id) const + export bool hasComponent(ushort component_id) const { const EntityManager.EntityInfo* info = block.type_info; if (component_id >= info.deltas.length || info.deltas[component_id] == 0)return false; @@ -133,7 +133,7 @@ export struct EntityTemplate /************************************************************************************************************************ Get specified component. If component doesn't exist function return null. Returned pointer is valid during EntityTemplate lifetime. */ - void* getComponent(ushort component_id) const nothrow @nogc + export void* getComponent(ushort component_id) const nothrow @nogc { if(component_id >= info.tmpl_deltas.length || info.tmpl_deltas[component_id] == ushort.max)return null; return cast(void*)(entity_data.ptr + info.tmpl_deltas[component_id]); diff --git a/source/bubel/ecs/events.d b/source/bubel/ecs/events.d index fdf02dc..11816a1 100644 --- a/source/bubel/ecs/events.d +++ b/source/bubel/ecs/events.d @@ -1,3 +1,7 @@ +/************************************************************************************************************************ +Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. +*/ module bubel.ecs.events; import bubel.ecs.block_allocator; @@ -8,8 +12,6 @@ import bubel.ecs.traits : becsID; import std.algorithm.comparison : max; -alias EventRef = ComponentRef; - package struct EventManager { @@ -32,17 +34,12 @@ package struct EventManager } export void sendEvent(Ev)(EntityID id, Ev event, uint thread_id = 0) nothrow @nogc - { - sendEvent(id, EventRef(&event, becsID!Ev), thread_id); - } - - export void sendEvent(EntityID id, EventRef event, uint thread_id = 0) nothrow @nogc { uint block_id = current_index + thread_id; - EventData* data = &events[event.component_id]; + EventData* data = &events[becsID!Ev]; EventBlock* block = data.blocks[block_id]; - EntityManager.EventInfo* info = &manager.events[event.component_id]; + //EntityManager.EventInfo* info = &manager.events[Ev.event_id]; //event.entity_id = id; if (block is null) @@ -68,11 +65,10 @@ package struct EventManager data.blocks[block_id] = block; } - uint size = info.size + EntityID.sizeof; + uint size = Ev.sizeof + EntityID.sizeof; void* ptr = cast(void*) block + data.data_offset + block.count * size; *cast(EntityID*)ptr = id; - memcpy(ptr + EntityID.sizeof, event.ptr, info.size); - //*cast(Ev*)(ptr + EntityID.sizeof) = event; + *cast(Ev*)(ptr + EntityID.sizeof) = event; //Ev* event_array = cast(Ev*)(cast(void*) block + data.data_offset); //event_array[block.count] = event; block.count++; diff --git a/source/bubel/ecs/hash_map.d b/source/bubel/ecs/hash_map.d index 764374c..7ba6137 100755 --- a/source/bubel/ecs/hash_map.d +++ b/source/bubel/ecs/hash_map.d @@ -1,4 +1,8 @@ -module bubel.ecs.hash_map; +/************************************************************************************************************************ +Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. +*/ +module bubel.ecs.hash_map; import std.traits; @@ -24,7 +28,7 @@ export ulong defaultHashFunc(T)(auto ref T t) nothrow @nogc } } -ulong hash(byte[] array) nothrow @nogc +export ulong hash(byte[] array) nothrow @nogc { ulong hash = 0; @@ -332,36 +336,36 @@ nothrow: return result; } - export int byKey(scope int delegate(Key k) nothrow dg) + export int byKey(scope int delegate(ref Key k) dg) { int result; foreach (ref Key k; this) { - result = dg(k); + result = (cast(int delegate(ref Key k) nothrow @nogc)dg)(k); if (result) break; } return result; } - export int byValue(scope int delegate(ref Value k) nothrow dg) + export int byValue(scope int delegate(ref Value v) dg) { int result; foreach (ref Value v; this) { - result = dg(v); + result = (cast(int delegate(ref Value v) nothrow @nogc)dg)(v); if (result) break; } return result; } - export int byKeyValue(scope int delegate(ref Key k, ref Value v) nothrow dg) + export int byKeyValue(scope int delegate(ref Key k, ref Value v) dg) { int result; foreach (ref Key k, ref Value v; this) { - result = dg(k, v); + result = (cast(int delegate(ref Key k, ref Value v) nothrow @nogc)dg)(k, v); if (result) break; } diff --git a/source/bubel/ecs/id_manager.d b/source/bubel/ecs/id_manager.d index 86a611e..2054cbb 100644 --- a/source/bubel/ecs/id_manager.d +++ b/source/bubel/ecs/id_manager.d @@ -1,3 +1,7 @@ +/************************************************************************************************************************ +Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. +*/ module bubel.ecs.id_manager; import bubel.ecs.entity; diff --git a/source/bubel/ecs/manager.d b/source/bubel/ecs/manager.d index 6bc2339..b5220f4 100644 --- a/source/bubel/ecs/manager.d +++ b/source/bubel/ecs/manager.d @@ -1,7 +1,7 @@ /************************************************************************************************************************ Most important module. Almost every function is called from EntityManager. -Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz License: BSD 3-clause, see LICENSE file in project root folder. */ module bubel.ecs.manager; @@ -10,10 +10,6 @@ import std.algorithm : max; import std.conv : to; import std.traits; -//import core.atomic; -//import core.stdc.stdlib : qsort; -//import core.stdc.string; - import bubel.ecs.system; //not ordered as forward reference bug workaround import bubel.ecs.block_allocator; import bubel.ecs.entity; @@ -31,6 +27,8 @@ alias SerializeVector = bubel.ecs.vector.Vector!ubyte; ///Global EntityManager used for everything. export __gshared EntityManager* gEntityManager = null; +version(D_BetterC) version = NoDRuntime; + /************************************************************************************************************************ Entity manager is responsible for everything. @@ -69,21 +67,18 @@ export struct EntityManager Initialize ECS. */ export static void initialize(uint threads_count = 1, uint page_size = 32768, - uint block_pages_count = 128) + uint block_pages_count = 128) { if (gEntityManager is null) { - //gEntityManager = Mallocator.make!EntityManager(threads_count); gEntityManager = Mallocator.make!EntityManager(threads_count, page_size, block_pages_count); with (gEntityManager) { UpdatePass* pass = Mallocator.make!UpdatePass; pass.name = Mallocator.makeArray(cast(char[]) "update"); - //pass.name = Mallocator.makeArray!char("update".length); - //pass.name[0..$] = "update"; passes.add(pass); - passes_map.add(cast(string) pass.name, cast(ushort)(passes.length - 1)); + passes_map.add(cast(const(char)[]) pass.name, cast(ushort)(passes.length - 1)); } } } @@ -91,7 +86,7 @@ export struct EntityManager /************************************************************************************************************************ Deinitialize and destroy ECS. This function release whole memory. */ - export static void destroy() + export static void destroy() nothrow @nogc { if (gEntityManager is null) return; @@ -105,6 +100,8 @@ export struct EntityManager foreach (EntityInfo* info; &entities_infos.byValue) { + //if(info.components)Mallocator.dispose(info.components); + Mallocator.dispose(info); } @@ -172,6 +169,8 @@ export struct EntityManager foreach (ref system; systems) { + if (system.isAlive() == false) + continue; if (system.m_empty) { if (system.m_update) @@ -181,7 +180,7 @@ export struct EntityManager if (system.m_update is null) { if (system.m_add_entity || system.m_remove_entity - || system.m_change_entity || system.m_event_callers.length) + || system.m_change_entity || system.m_event_callers.length) { foreach (info; &entities_infos.byValue) { @@ -236,6 +235,8 @@ export struct EntityManager foreach (ref system; systems) { + if (system.isAlive() == false) + continue; foreach (caller; system.m_event_callers) { event_callers[caller.id]++; @@ -252,6 +253,8 @@ export struct EntityManager foreach (ref system; systems) { + if (system.isAlive() == false) + continue; foreach (caller; system.m_event_callers) { events[caller.id].callers[event_callers[caller.id]].callback = caller.callback; @@ -275,7 +278,7 @@ export struct EntityManager foreach (ref event; events) { qsort(event.callers.ptr, event.callers.length, - EventCaller.sizeof, &comapreEventCaller); + EventCaller.sizeof, &comapreEventCaller); } //qsort(event_callers.ptr, event_callers.length, EventInfo.sizeof, &compareUShorts); @@ -295,7 +298,6 @@ export struct EntityManager if (threads_count == 0) threads_count = 1; threads = Mallocator.makeArray!ThreadData(threads_count); - //foreach(ref thread;threads)thread = ThreadData().init; m_page_size = page_size; m_pages_in_block = block_pages_count; @@ -329,13 +331,37 @@ export struct EntityManager allocator.freeMemory(); } + /************************************************************************************************************************ + Unregister given system form EntityManager. + */ + void unregisterSystem(Sys)() + { + ushort system_id = becsID!Sys; + System* system = getSystem(system_id); + + unregisterSystem(system); + } + + /************************************************************************************************************************ + Unregister given system form EntityManager. + */ + export void unregisterSystem(System* system) nothrow @nogc + { + assert(register_state, "unregisterSystem must be called between beginRegister() and endRegister()."); + assert(system !is null, "System was not registered"); + assert(system.isAlive, "System already unregistered"); + + //disable, destroy and dispose user created system but keep name and other data + system.destroySystemData(); + } + /************************************************************************************************************************ Same as "void registerSystem(Sys)(int priority, int pass = 0)" but use pass name instead of id. */ void registerSystem(Sys)(int priority, const(char)[] pass_name) { ushort pass = passes_map.get(pass_name, ushort.max); - version (D_BetterC) + version (NoDRuntime) assert(pass != ushort.max, "Update pass doesn't exist."); else assert(pass != ushort.max, "Update pass (Name " ~ pass_name ~ ") doesn't exist."); @@ -356,18 +382,23 @@ export struct EntityManager //alias STC = ParameterStorageClass; assert(register_state, - "registerSystem must be called between beginRegister() and endRegister()."); - version (D_BetterC) + "registerSystem must be called between beginRegister() and endRegister()."); + version (NoDRuntime) assert(pass < passes.length, "Update pass doesn't exist."); else assert(pass < passes.length, "Update pass (ID " ~ pass.to!string ~ ") doesn't exist."); + // enum SystemName = fullyQualifiedName!Sys; enum SystemName = fullName!Sys; + //enum SystemName = Sys.stringof; - System system; + System system = System(); system.m_pass = pass; - alias SystemData = SystemEntityData!Sys; + // static if (!(hasMember!(Sys, "system_id")) || !is(typeof(Sys.system_id) == ushort)) + // { + // static assert(0, "Add \"mixin ECS.System;\" in top of system structure;"); + // } static if (!(hasMember!(Sys, "EntitiesData"))) { @@ -398,9 +429,10 @@ export struct EntityManager { alias EventParamType = Params[1]; enum EventName = fullName!(Unqual!(EventParamType)); + // enum EventName = fullyQualifiedName!(Unqual!(EventParamType));//.stringof; ushort evt = events_map.get(cast(char[]) EventName, ushort.max); assert(evt != ushort.max, - "Can't register system \"" ~ SystemName + "Can't register system \"" ~ SystemName ~ "\" due to non existing event \"" ~ EventName ~ "\"."); callers[i].callback = cast(void*)&callEventHandler!(EventParamType); @@ -418,10 +450,222 @@ export struct EntityManager } } - alias components_info = SystemData.components_info; + static struct CompInfo + { + string name; + string type; + } - static void allocateSystemComponents/*(ComponentsIndices!component_counts components_info)*/( - ref System system) + static struct ComponentsCounts + { + //one more than should be to prevent null arrays (zero length arrays) + uint readonly = 1; + uint mutable = 1; + uint excluded = 1; + uint optional = 1; + uint req = 1; + uint readonly_dep = 1; + uint writable_dep = 1; + } + + static ComponentsCounts getComponentsCounts() + { + ComponentsCounts components_counts; + + bool checkExcludedComponentsSomething(Sys)() + { + return __traits(compiles, allSameType!(string, typeof(Sys.ExcludedComponents))) && allSameType!(string, + typeof(Sys.ExcludedComponents)) && isExpressions!(Sys.ExcludedComponents); + } + + foreach (member; __traits(allMembers, Sys.EntitiesData)) + { + alias MemberType = typeof(__traits(getMember, Sys.EntitiesData, member)); + if (member == "length" || member == "thread_id" || member == "job_id" + || is(MemberType == Entity[]) || is(MemberType == const(Entity)[])) + { + //continue; + } + else + { + string name; + static if (isArray!MemberType) + { // Workaround. This code is never called with: not an array type, but compiler prints an error + // name = fullyQualifiedName!(Unqual!(ForeachType!MemberType));//.stringof; + name = fullName!(Unqual!(typeof(MemberType.init[0]))); + } + + bool is_optional; + bool is_read_only; + + if (is(CopyConstness!(ForeachType!(MemberType), int) == const(int))) + { + is_read_only = true; + } + + foreach (att; __traits(getAttributes, __traits(getMember, + Sys.EntitiesData, member))) + { + if (att == "optional") + { + is_optional = true; + } + if (att == "readonly") + { + is_read_only = true; + } + } + if (is_read_only) + { + components_counts.readonly++; + } + else + { + components_counts.mutable++; + } + if (is_optional) + { + components_counts.optional++; + } + else + { + components_counts.req++; + } + } + } + + static if (__traits(hasMember, Sys, "ExcludedComponents")) + { + static if (is(Sys.ExcludedComponents == enum)) + { + foreach (str; __traits(allMembers, Sys.ExcludedComponents)) + { + components_counts.excluded++; + } + } + else //static if (checkExcludedComponentsSomething!Sys) + { + foreach (str; Sys.ExcludedComponents) + { + components_counts.excluded++; + } + } + } + + static if (__traits(hasMember, Sys, "ReadOnlyDependencies")) + { + foreach (str; Sys.ReadOnlyDependencies) + { + components_counts.readonly_dep++; + } + } + + static if (__traits(hasMember, Sys, "WritableDependencies")) + { + foreach (str; Sys.WritableDependencies) + { + components_counts.writable_dep++; + } + } + + return components_counts; + } + + enum ComponentsCounts component_counts = getComponentsCounts(); + + static struct ComponentsIndices(ComponentsCounts counts) + { + CompInfo[] readonly() + { + return m_readonly[0 .. m_readonly_counter]; + } + + CompInfo[] mutable() + { + return m_mutable[0 .. m_mutable_counter]; + } + + CompInfo[] excluded() + { + return m_excluded[0 .. m_excluded_counter]; + } + + CompInfo[] optional() + { + return m_optional[0 .. m_optional_counter]; + } + + CompInfo[] req() + { + return m_req[0 .. m_req_counter]; + } + + CompInfo[] readonlyDeps() + { + return m_readonly_dep[0 .. m_readonly_dep_counter]; + } + + CompInfo[] writableDeps() + { + return m_writable_dep[0 .. m_writable_dep_counter]; + } + + void addReadonly(CompInfo info) + { + m_readonly[m_readonly_counter++] = info; + } + + void addMutable(CompInfo info) + { + m_mutable[m_mutable_counter++] = info; + } + + void addExcluded(CompInfo info) + { + m_excluded[m_excluded_counter++] = info; + } + + void addOptional(CompInfo info) + { + m_optional[m_optional_counter++] = info; + } + + void addReq(CompInfo info) + { + m_req[m_req_counter++] = info; + } + + void addReadonlyDep(CompInfo info) + { + m_readonly_dep[m_readonly_dep_counter++] = info; + } + + void addWritableDep(CompInfo info) + { + m_writable_dep[m_writable_dep_counter++] = info; + } + + CompInfo[counts.readonly] m_readonly; + CompInfo[counts.mutable] m_mutable; + CompInfo[counts.excluded] m_excluded; + CompInfo[counts.optional] m_optional; + CompInfo[counts.req] m_req; + CompInfo[counts.readonly_dep] m_readonly_dep; + CompInfo[counts.writable_dep] m_writable_dep; + + uint m_readonly_counter; + uint m_mutable_counter; + uint m_excluded_counter; + uint m_optional_counter; + uint m_req_counter; + uint m_readonly_dep_counter; + uint m_writable_dep_counter; + + string entites_array; + } + + static void allocateSystemComponents(ComponentsIndices!component_counts components_info)( + ref System system) { size_t req = components_info.req.length; size_t opt = components_info.optional.length; @@ -445,8 +689,119 @@ export struct EntityManager system.m_readonly_dependencies = Mallocator.makeArray!ushort(read_only_deps); if (writable_deps > 0) system.m_writable_dependencies = Mallocator.makeArray!ushort(writable_deps); + } + static ComponentsIndices!component_counts getComponentsInfo() + { + ComponentsIndices!component_counts components_info; + + bool checkExcludedComponentsSomething(Sys)() + { + return __traits(compiles, allSameType!(string, typeof(Sys.ExcludedComponents))) && allSameType!(string, + typeof(Sys.ExcludedComponents)) && isExpressions!(Sys.ExcludedComponents); + } + + foreach (member; __traits(allMembers, Sys.EntitiesData)) + { + alias MemberType = typeof(__traits(getMember, Sys.EntitiesData, member)); + if (member == "length" || member == "thread_id" || member == "job_id" + || is(MemberType == Entity[]) || is(MemberType == const(Entity)[])) + { + if (is(MemberType == Entity[]) || is(MemberType == const(Entity)[])) + components_info.entites_array = member; + //continue; + } + else + { + string name; + static if (isArray!MemberType) + { // Workaround. This code is never called with: not an array type, but compiler prints an error + // name = fullyQualifiedName!(Unqual!(ForeachType!MemberType)); + name = fullName!(Unqual!(typeof(MemberType.init[0]))); + //name = Unqual!(ForeachType!MemberType).stringof; + } + + bool is_optional; + bool is_read_only; + + if (is(CopyConstness!(ForeachType!(MemberType), int) == const(int))) + { + is_read_only = true; + } + + foreach (att; __traits(getAttributes, __traits(getMember, + Sys.EntitiesData, member))) + { + if (att == "optional") + { + is_optional = true; + } + if (att == "readonly") + { + is_read_only = true; + } + } + if (is_read_only) + { + components_info.addReadonly(CompInfo(member, name)); + } + else + { + components_info.addMutable(CompInfo(member, name)); + } + if (is_optional) + { + components_info.addOptional(CompInfo(member, name)); + } + else + { + components_info.addReq(CompInfo(member, name)); + } + } + } + + static if (__traits(hasMember, Sys, "ExcludedComponents")) + { + static if (is(Sys.ExcludedComponents == enum)) + { + foreach (str; __traits(allMembers, Sys.ExcludedComponents)) + { + components_info.addExcluded(CompInfo(str, str)); + } + } + else //static if (checkExcludedComponentsSomething!Sys) + { + foreach (str; Sys.ExcludedComponents) + { + components_info.addExcluded(CompInfo(str.stringof, fullName!str)); + // components_info.addExcluded(CompInfo(str.stringof, str.stringof)); + } + + } + } + + static if (__traits(hasMember, Sys, "ReadOnlyDependencies")) + { + foreach (str; Sys.ReadOnlyDependencies) + { + components_info.addReadonlyDep(CompInfo(str, str)); + } + } + + static if (__traits(hasMember, Sys, "WritableDependencies")) + { + foreach (str; Sys.WritableDependencies) + { + components_info.addWritableDep(CompInfo(str, str)); + } + } + + return components_info; + } + + enum ComponentsIndices!component_counts components_info = getComponentsInfo(); + static void genCompList(ref System system, ref HashMap!(char[], ushort) components_map) { @@ -459,37 +814,37 @@ export struct EntityManager else static if (member == "length") { static assert(isIntegral!(MemberType), - "EntitiesData 'length' member must be integral type."); + "EntitiesData 'length' member must be integral type."); static assert(MemberType.sizeof > 1, - "EntitiesData 'length' member can't be byte or ubyte."); + "EntitiesData 'length' member can't be byte or ubyte."); } else static if (member == "thread_id") { static assert(isIntegral!(MemberType), - "EntitiesData 'thread_id' member must be integral type."); + "EntitiesData 'thread_id' member must be integral type."); static assert(MemberType.sizeof > 1, - "EntitiesData 'thread_id' member can't be byte or ubyte."); + "EntitiesData 'thread_id' member can't be byte or ubyte."); } else static if (member == "job_id") { static assert(isIntegral!(MemberType), - "EntitiesData 'job_id' member must be integral type."); + "EntitiesData 'job_id' member must be integral type."); static assert(MemberType.sizeof > 1, - "EntitiesData 'job_id' member can't be byte or ubyte."); + "EntitiesData 'job_id' member can't be byte or ubyte."); } else static if (!(isArray!(MemberType))) static assert(0, "EntitiesData members should be arrays of elements!"); } //enum ComponentsIndices components_info = getComponentsInfo(); - allocateSystemComponents(system); + allocateSystemComponents!(components_info)(system); foreach (iii, comp_info; components_info.req) { ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max); - version (D_BetterC) + version (NoDRuntime) assert(comp != ushort.max, - "Can't register system \"" ~ SystemName + "Can't register system \"" ~ SystemName ~ "\" due to non existing component."); else assert(comp != ushort.max, "Can't register system \"" ~ SystemName @@ -499,9 +854,9 @@ export struct EntityManager foreach (iii, comp_info; components_info.excluded) { ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max); - version (D_BetterC) + version (NoDRuntime) assert(comp != ushort.max, - "Can't register system \"" ~ SystemName + "Can't register system \"" ~ SystemName ~ "\" due to non existing component."); else assert(comp != ushort.max, "Can't register system \"" ~ SystemName @@ -511,9 +866,9 @@ export struct EntityManager foreach (iii, comp_info; components_info.optional) { ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max); - version (D_BetterC) + version (NoDRuntime) assert(comp != ushort.max, - "Can't register system \"" ~ SystemName + "Can't register system \"" ~ SystemName ~ "\" due to non existing component."); else assert(comp != ushort.max, "Can't register system \"" ~ SystemName @@ -523,9 +878,9 @@ export struct EntityManager foreach (iii, comp_info; components_info.readonly) { ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max); - version (D_BetterC) + version (NoDRuntime) assert(comp != ushort.max, - "Can't register system \"" ~ SystemName + "Can't register system \"" ~ SystemName ~ "\" due to non existing component."); else assert(comp != ushort.max, "Can't register system \"" ~ SystemName @@ -535,9 +890,9 @@ export struct EntityManager foreach (iii, comp_info; components_info.mutable) { ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max); - version (D_BetterC) + version (NoDRuntime) assert(comp != ushort.max, - "Can't register system \"" ~ SystemName + "Can't register system \"" ~ SystemName ~ "\" due to non existing component."); else assert(comp != ushort.max, "Can't register system \"" ~ SystemName @@ -546,6 +901,67 @@ export struct EntityManager } } + static void fillInputData(ref Sys.EntitiesData input_data, EntityInfo* info, + EntitiesBlock* block, uint offset, uint entities_count, System* system) + { + //enum ComponentsIndices components_info = getComponentsInfo(); + + static if (components_info.entites_array) + { + __traits(getMember, input_data, components_info.entites_array) = ( + cast(Entity*) block.dataBegin())[offset .. entities_count]; + } + + static if (hasMember!(Sys.EntitiesData, "length")) + { + input_data.length = cast(typeof(input_data.length))(entities_count - offset); + } + + /*static if (hasMember!(Sys.EntitiesData, "thread_id")) + { + input_data.thread_id = cast(typeof(input_data.thread_id))threadID(); + }//*/ + + ///FIXME: should be "components_info.req()" but it's not compile with GCC + static foreach (iii, comp_info; components_info.m_req[0 + .. components_info.m_req_counter]) + { + __traits(getMember, input_data, comp_info.name) = ( + cast(typeof( + (typeof(__traits(getMember, Sys.EntitiesData, comp_info.name))).init[0] + )*)(cast(void*) block + info.deltas[system.m_components[iii]]) + )[offset .. entities_count]; + } + + static foreach (iii, comp_info; components_info.m_optional[0 + .. components_info.m_optional_counter]) + { + if (system.m_optional_components[iii] < info.deltas.length + && info.deltas[system.m_optional_components[iii]] != 0) + { + __traits(getMember, input_data, comp_info.name) = (cast(ForeachType!(typeof(__traits(getMember, + Sys.EntitiesData, comp_info.name)))*)(cast( + void*) block + info.deltas[system.m_optional_components[iii]]))[offset + .. entities_count]; + + } + } + } + + /*bool checkOnUpdateParams() + { + bool ret = false; + foreach (func; __traits(getOverloads, Sys, "onUpdate")) + { + if ((Parameters!(func)).length == 1 && is(Parameters!(func)[0] == Sys.EntitiesData)) + { + ret = true; + break; + } + } + return ret; + }*/ + int getOnUpdateOverload()() { int ret = -1; @@ -573,26 +989,98 @@ export struct EntityManager static if (OnUpdateOverloadNum != -1) { - static void callUpdate(SystemCallData* data) + static if (!IsEmpty) { - Sys* s = cast(Sys*) data.context; - - Sys.EntitiesData input_data; - - static if (!IsEmpty)SystemData.fillInputData(input_data, data.info, data.block, data.begin, data.count);//, system); - - static if (hasMember!(Sys.EntitiesData, "thread_id")) + static void callUpdate(ref CallData data) { - input_data.thread_id = cast( - typeof(input_data.thread_id)) data.thread_id; - } + Sys* s = cast(Sys*) data.system.m_system_pointer; - static if (hasMember!(Sys.EntitiesData, "job_id")) + Sys.EntitiesData input_data; + EntityInfo* info = data.info; //block.type_info; + System* system = data.system; + + EntitiesBlock* block; + if (data.first_block) + block = data.first_block; + else + block = info.first_block; + + uint offset = data.begin; + uint entities_count; + uint blocks; + if (data.blocks) + blocks = data.blocks; + else + blocks = uint.max; + + while (block !is null && blocks > 0) + { + if (blocks == 1) + { + if (data.end) + entities_count = data.end; + else + entities_count = block.entities_count; + } + else + entities_count = block.entities_count; + + if (entities_count > 0) + { + assert(entities_count <= block.entities_count + && offset < block.entities_count); + assert(entities_count > offset); + + fillInputData(input_data, info, block, offset, entities_count, system); + + static if (hasMember!(Sys.EntitiesData, "thread_id")) + { + input_data.thread_id = cast( + typeof(input_data.thread_id)) data.thread_id; + } + + static if (hasMember!(Sys.EntitiesData, "job_id")) + { + input_data.job_id = cast(typeof(input_data.job_id)) data.job_id; + } + + //s.onUpdate(input_data); + (cast(typeof(&__traits(getOverloads, s, + "onUpdate")[OnUpdateOverloadNum])) data.update_delegate)( + input_data); + } + block = block.next_block; + offset = 0; + blocks--; + } + } + } + else + { + static void callUpdate(ref CallData data) { - input_data.job_id = cast(typeof(input_data.job_id)) data.job_id; - } + Sys* s = cast(Sys*) data.system.m_system_pointer; - s.onUpdate(input_data); + Sys.EntitiesData input_data; + + /*static if (hasMember!(Sys.EntitiesData, "length")) + { + input_data.length = 0; + }//*/ + + static if (hasMember!(Sys.EntitiesData, "thread_id")) + { + input_data.thread_id = cast(typeof(input_data.thread_id)) data.thread_id; + } + + static if (hasMember!(Sys.EntitiesData, "job_id")) + { + input_data.job_id = cast(typeof(input_data.job_id)) data.job_id; + } + + (cast(typeof(&__traits(getOverloads, s, + "onUpdate")[OnUpdateOverloadNum])) data.update_delegate)(input_data); + } } system.m_update = &callUpdate; @@ -629,15 +1117,15 @@ export struct EntityManager foreach (func; __traits(getOverloads, Sys, func_name)) { static if ((Parameters!(func)).length == 1 - && is(Parameters!(func)[0] == Sys.EntitiesData) - && is(ReturnType!(func) == RetType)) + && is(Parameters!(func)[0] == Sys.EntitiesData) + && is(ReturnType!(func) == RetType)) { static RetType callFunc(ref ListenerCallData data) { Sys* s = cast(Sys*) data.system.m_system_pointer; Sys.EntitiesData input_data; - SystemData.fillInputData(input_data, data.block.type_info, - data.block, data.begin, data.count);//, data.system); + fillInputData(input_data, data.block.type_info, + data.block, data.begin, data.end, data.system); static if (is(RetTyp == void)) mixin("s." ~ func_name ~ "(input_data)"); else @@ -658,8 +1146,8 @@ export struct EntityManager foreach (func; __traits(getOverloads, Sys, func_name)) { static if ((Parameters!(func)).length == 1 - && is(Parameters!(func)[0] == EntityInfo*) - && is(ReturnType!(func) == RetType)) + && is(Parameters!(func)[0] == EntityInfo*) + && is(ReturnType!(func) == RetType)) { static RetType callFunc(void* system_pointer, EntityInfo* info) { @@ -692,19 +1180,29 @@ export struct EntityManager system.m_system_pointer = cast(void*) Mallocator.make!Sys; system.m_priority = priority; + //(cast(Sys*) system.m_system_pointer).__ecsInitialize(); + //system.jobs = (cast(Sys*) system.m_system_pointer)._ecs_jobs; + static if (__traits(hasMember, Sys, "__becs_jobs_count")) + system.jobs = Mallocator.makeArray!(Job)(Sys.__becs_jobs_count); + else + system.jobs = Mallocator.makeArray!(Job)(32); - static if(__traits(hasMember, Sys ,"__becs_jobs_count"))system.jobs = Mallocator.makeArray!(Job)(Sys.__becs_jobs_count); - else system.jobs = Mallocator.makeArray!(Job)(32); + static if (OnUpdateOverloadNum != -1) + { + Sys* s = cast(Sys*) system.m_system_pointer; + system.m_update_delegate = cast(void delegate())&__traits(getOverloads, + s, "onUpdate")[OnUpdateOverloadNum]; + } genCompList(system, components_map); foreach (iii, comp_info; components_info.readonlyDeps) { ushort comp = external_dependencies_map.get(cast(const(char)[]) comp_info.type, - ushort.max); - version (D_BetterC) + ushort.max); + version (NoDRuntime) assert(comp != ushort.max, - "Can't register system \"" ~ SystemName + "Can't register system \"" ~ SystemName ~ "\" due to non existing dependency."); else assert(comp != ushort.max, "Can't register system \"" ~ SystemName @@ -715,9 +1213,9 @@ export struct EntityManager foreach (iii, comp_info; components_info.writableDeps) { ushort comp = external_dependencies_map.get(cast(char[]) comp_info.type, ushort.max); - version (D_BetterC) + version (NoDRuntime) assert(comp != ushort.max, - "Can't register system \"" ~ SystemName + "Can't register system \"" ~ SystemName ~ "\" due to non existing dependency."); else assert(comp != ushort.max, "Can't register system \"" ~ SystemName @@ -758,43 +1256,9 @@ export struct EntityManager becsID!Sys = system.id; } - export ushort registerSystem(const (char)[] name, System system) - { - ushort sys_id = systems_map.get(cast(char[]) name, ushort.max); - if (sys_id < systems.length) - { - system.m_name = systems[sys_id].m_name; - systems[sys_id].m_name = null; - systems[sys_id].destroy(); - - if (system.m_create) - (cast(void function(void*)) system.m_create)(system.m_system_pointer); - - system.enable(); - - system.m_id = sys_id; - systems[sys_id] = system; - } - else - { - system.m_name = Mallocator.makeArray(cast(char[]) name); - - systems_map.add(system.m_name, cast(ushort) systems.length); - - system.m_id = cast(ushort)(systems.length); - - systems.add(system); - - if (system.m_create) - (cast(void function(void*)) system.m_create)(system.m_system_pointer); - - systems[$ - 1].enable(); - } - return system.m_id; - } - /************************************************************************************************************************ - Return system ECS api by id + Return system ECS api by id. + System pointer might become invalid after registration process. It should be get once again after new systems are registered. */ export System* getSystem(ushort id) nothrow @nogc { @@ -834,11 +1298,18 @@ export struct EntityManager { ComponentInfo info; + // enum ComponentName = fullyQualifiedName!Comp; enum ComponentName = fullName!Comp; + // enum ComponentName = Comp.stringof; + + // static if (!(hasMember!(Comp, "component_id")) || !is(typeof(Comp.component_id) == ushort)) + // { + // static assert(0, "Add \"mixin ECS.Component;\" in top of component structure;"); + // } static if (hasMember!(Comp, "onDestroy") && isFunction!(Comp.onDestroy) - && is(ReturnType!(Comp.onDestroy) == void) - && Parameters!(Comp.onDestroy).length == 0) + && is(ReturnType!(Comp.onDestroy) == void) + && Parameters!(Comp.onDestroy).length == 0) { static void callDestroy(void* pointer) nothrow @nogc { @@ -849,7 +1320,7 @@ export struct EntityManager } static if (hasMember!(Comp, "onCreate") && isFunction!(Comp.onCreate) - && is(ReturnType!(Comp.onCreate) == void) && Parameters!(Comp.onCreate).length == 0) + && is(ReturnType!(Comp.onCreate) == void) && Parameters!(Comp.onCreate).length == 0) { static void callCreate(void* pointer) nothrow @nogc { @@ -865,17 +1336,13 @@ export struct EntityManager info.size = Comp.sizeof; info.alignment = Comp.alignof; //8; info.init_data = Mallocator.makeArray!ubyte(Comp.sizeof); - *cast(Comp*) info.init_data.ptr = Comp.init; // = Comp(); + __gshared Comp init_memory; + memcpy(info.init_data.ptr, &init_memory, Comp.sizeof); - becsID!Comp = registerComponent(ComponentName, info); - } - - ushort registerComponent(const (char)[] name, ComponentInfo info) - { - ushort comp_id = components_map.get(cast(char[]) name, ushort.max); + ushort comp_id = components_map.get(cast(char[]) ComponentName, ushort.max); if (comp_id < components.length) { - // becsID!Comp = comp_id; + becsID!Comp = comp_id; if (components[comp_id].init_data) Mallocator.dispose(components[comp_id].init_data); components[comp_id] = info; @@ -883,12 +1350,10 @@ export struct EntityManager else { components.add(info); - // becsID!Comp = cast(ushort)(components.length - 1); - comp_id = cast(ushort)(components.length - 1); - char[] name_copy = Mallocator.makeArray(cast(char[]) name); - components_map.add(name_copy, cast(ushort)(components.length - 1)); + becsID!Comp = cast(ushort)(components.length - 1); + char[] name = Mallocator.makeArray(cast(char[]) ComponentName); + components_map.add(name, cast(ushort)(components.length - 1)); } - return comp_id; } void registerEvent(Ev)() @@ -901,7 +1366,7 @@ export struct EntityManager // } static if (hasMember!(Ev, "onDestroy") && isFunction!(Ev.onDestroy) - && is(ReturnType!(Ev.onDestroy) == void) && Parameters!(Ev.onDestroy).length == 0) + && is(ReturnType!(Ev.onDestroy) == void) && Parameters!(Ev.onDestroy).length == 0) { static void callDestroy(void* pointer) { @@ -929,32 +1394,16 @@ export struct EntityManager } } - export ushort registerEvent(const (char)[] name, EventRegisterInfo refister_info) - { - EventInfo info; - info.size = refister_info.size; - info.alignment = refister_info.alignment; - - ushort event_id = events_map.get(name, ushort.max); - if (event_id >= events.length) - { - events.add(info); - event_id = cast(ushort)(events.length - 1); - events_map.add(name, cast(ushort)(events.length - 1)); - } - return event_id; - } - export void callEntitiesFunction(Sys, T)(T func) { //TODO: check if onUpdate function is good Sys* s; static assert(isDelegate!func, "Function must be delegate."); static assert(__traits(hasMember, Sys, "EntitiesData"), - "Can't call function with system which hasn't EntitesData structure."); + "Can't call function with system which hasn't EntitesData structure."); ///TODO: make possibly to call function to group without system with onUpdate function static assert(__traits(hasMember, Sys, "onUpdate"), - "Can't call function with system which hasn't onUpdate function callback."); + "Can't call function with system which hasn't onUpdate function callback."); // static assert(is(SetFunctionAttributes!(T, functionLinkage!(s.onUpdate), // functionAttributes!(s.onUpdate)) == typeof(&s.onUpdate)), // "Function must match system update function."); FIXME: It's lead to crash on android build @@ -962,50 +1411,15 @@ export struct EntityManager System* system = getSystem(becsID!Sys); assert(system != null, - "System must be registered in EntityManager before any funcion can be called."); + "System must be registered in EntityManager before any funcion can be called."); + assert(system.isAlive(), "System must be alive (registered) in order to call entities function on its entities"); + if (!system.m_any_system_caller) return; - /* struct Delegate - { - void* context; - void* func; - }*/ - - /*static void fillInputData_dyn(ref Sys.EntitiesData input_data, EntityInfo* info, - EntitiesBlock* block, uint offset, uint entities_count) - { - System* system = gEntityManager.getSystem(becsID!Sys); - input_data.length = cast(typeof(input_data.length))entities_count; - // input_data.thread_id = 0; - foreach(size_t i, ushort component; system.m_components) - { - *(&block.data_pointer + i) = cast(void*) block + info.deltas[component] + offset * gEntityManager.components[component].size; - } - - foreach(size_t i, ushort component; system.m_optional_components) - { - if(component < info.deltas.length && info.deltas[component] != 0)*(&input_data.data_pointer + i + system.m_components.length) = cast(void*) block + info.deltas[component] + offset * gEntityManager.components[component].size; - } - }*/ - - static void callUpdate(SystemCallData* data) - { - //Sys* s = cast(Sys*) data.system_pointer; - Sys.EntitiesData input_data; - EntityInfo* info = data.info; - - alias SystemData = SystemEntityData!Sys; - SystemData.fillInputData(input_data, info, data.block, data.begin, data.count); - //fillInputData(input_data, info, data.block, data.begin, data.end - data.begin); - //fillInputData_dyn(input_data, info, data.block, data.begin, data.end - data.begin); - - (*cast(void delegate(Sys.EntitiesData)*)data.context)(input_data); - } - foreach (info; system.m_any_system_caller.infos) { - CallData data = CallData(system.id, system, info, cast(void*)&func, cast(void*)&callUpdate); + CallData data = CallData(system.id, system, info, cast(void delegate()) func); data.update(); } } @@ -1030,19 +1444,18 @@ export struct EntityManager foreach (caller; passes[pass].system_callers) { System* sys = &systems[caller.system_id]; - assert(sys !is null); if (sys.enabled && sys.willExecute) { if (sys.m_empty) { - CallData data = CallData(caller.system_id, sys, null, sys.m_system_pointer, sys.m_update); + CallData data = CallData(caller.system_id, sys, null, sys.m_update_delegate); data.update(); } else foreach (info; caller.infos) { CallData data = CallData(caller.system_id, sys, info, - sys.m_system_pointer, sys.m_update); + sys.m_update_delegate); data.update(); } } @@ -1064,7 +1477,7 @@ export struct EntityManager assert(!register_state); assert(pass < passes.length); assert(m_dispatch_jobs, - "Can't update with multithreading without JobDispatch function. Please use setJobDispatchFunc()."); + "Can't update with multithreading without JobDispatch function. Please use setJobDispatchFunc()."); Vector!CallData tmp_datas; tmp_datas.reserve(8); @@ -1077,7 +1490,7 @@ export struct EntityManager void nextJob() { CallData[] callers = m_call_data_allocator.getCallData( - cast(uint) tmp_datas.length); + cast(uint) tmp_datas.length); //callers[0 .. $] = tmp_datas[0 .. $]; memcpy(callers.ptr, &tmp_datas[0], CallData.sizeof * tmp_datas.length); tmp_datas.clear(); @@ -1088,7 +1501,7 @@ export struct EntityManager if (sys.m_empty) { - tmp_datas.add(CallData(caller.system_id, sys, null, sys.m_system_pointer, sys.m_update)); + tmp_datas.add(CallData(caller.system_id, sys, null, sys.m_update_delegate)); nextJob(); caller.job_group.jobs = sys.jobs[0 .. 1]; (cast(void delegate(JobGroup) nothrow @nogc) m_dispatch_jobs)(caller.job_group); @@ -1132,7 +1545,7 @@ export struct EntityManager //if this info will fill job if ((blocks_count - 1) * info.max_entities + entities_count - + info.last_block.entities_count - first_elem >= entities_per_job) + + info.last_block.entities_count - first_elem >= entities_per_job) { int reamaining_entities = (entities_per_job - entities_count - ( first_block.entities_count - first_elem)); @@ -1153,9 +1566,9 @@ export struct EntityManager assert(entities_per_job == full_blocks_count * info.max_entities + entities_count + ( first_block.entities_count - first_elem)); CallData data = CallData(caller.system_id, sys, - info, sys.m_system_pointer, sys.m_update, first_block, - cast(ushort)(full_blocks_count + 1), - cast(ushort) first_elem, 0); + info, sys.m_update_delegate, first_block, + cast(ushort)(full_blocks_count + 1), + cast(ushort) first_elem, 0); tmp_datas.add(data); first_elem = 0; blocks_count -= full_blocks_count + 1; @@ -1164,14 +1577,14 @@ export struct EntityManager else { entities_count += full_blocks_count * info.max_entities + ( - first_block.entities_count - first_elem); // - first_elem; + first_block.entities_count - first_elem); // - first_elem; uint last_elem = entities_per_job - entities_count; // + first_elem - 1; assert(last_elem > 0); assert(last_elem <= block.entities_count); CallData data = CallData(caller.system_id, sys, - info, sys.m_system_pointer, sys.m_update, first_block, - cast(ushort)(full_blocks_count + 2), - cast(ushort) first_elem, cast(ushort) last_elem); + info, sys.m_update_delegate, first_block, + cast(ushort)(full_blocks_count + 2), + cast(ushort) first_elem, cast(ushort) last_elem); tmp_datas.add(data); first_elem = last_elem; blocks_count -= full_blocks_count + 1; @@ -1188,8 +1601,8 @@ export struct EntityManager uint last_elem = entities_per_job - entities_count; assert(last_elem > 0); CallData data = CallData(caller.system_id, sys, - info, sys.m_system_pointer, sys.m_update, first_block, 1, - cast(ushort) first_elem, cast(ushort)(first_elem + last_elem)); + info, sys.m_update_delegate, first_block, 1, + cast(ushort) first_elem, cast(ushort)(first_elem + last_elem)); tmp_datas.add(data); first_elem += last_elem; assert(first_elem <= first_block.entities_count); @@ -1208,8 +1621,8 @@ export struct EntityManager else { //take whole info blocks - CallData data = CallData(caller.system_id, sys, info, sys.m_system_pointer, sys.m_update, - first_block, cast(ushort) blocks_count, cast(ushort) first_elem); + CallData data = CallData(caller.system_id, sys, info, sys.m_update_delegate, + first_block, cast(ushort) blocks_count, cast(ushort) first_elem); tmp_datas.add(data); entities_count += (blocks_count - 1) * info.max_entities + info.last_block.entities_count - first_elem; @@ -1224,7 +1637,7 @@ export struct EntityManager } export void setMultithreadingCallbacks(void delegate(JobGroup) dispatch_callback, - uint delegate() get_id_callback) + uint delegate() get_id_callback) { m_dispatch_jobs = cast(void delegate(JobGroup jobs) nothrow @nogc) dispatch_callback; m_thread_id_func = cast(uint delegate() nothrow @nogc) get_id_callback; @@ -1289,7 +1702,7 @@ export struct EntityManager if (components[comp].size == 0) continue; memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], - components[comp].init_data.ptr, components[comp].size); + components[comp].init_data.ptr, components[comp].size); } } else @@ -1300,8 +1713,8 @@ export struct EntityManager if (components[comp].size == 0) continue; memcpy(cast(void*) temp.entity_data.ptr + info.tmpl_deltas[comp], - cast(void*) block + info.deltas[comp] + components[comp].size * index, - components[comp].size); + cast(void*) block + info.deltas[comp] + components[comp].size * index, + components[comp].size); } } @@ -1350,7 +1763,7 @@ export struct EntityManager if (components[comp].size == 0) continue; memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], - components[comp].init_data.ptr, components[comp].size); + components[comp].init_data.ptr, components[comp].size); } return temp; @@ -1366,18 +1779,18 @@ export struct EntityManager remove_components_ids = array of components to remove from base template */ export EntityTemplate* allocateTemplate(EntityTemplate* base_tmpl, - ushort[] components_ids, ushort[] remove_components_ids = null) + ushort[] components_ids, ushort[] remove_components_ids = null) { size_t len = base_tmpl.info.components.length + components_ids.length; ushort[] ids = (cast(ushort*) alloca(ushort.sizeof * len))[0 .. len]; memcpy(ids.ptr, base_tmpl.info.components.ptr, - ushort.sizeof * base_tmpl.info.components.length); + ushort.sizeof * base_tmpl.info.components.length); memcpy(ids.ptr + base_tmpl.info.components.length, components_ids.ptr, - ushort.sizeof * components_ids.length); + ushort.sizeof * components_ids.length); qsort(ids.ptr, ids.length, ushort.sizeof, &compareUShorts); qsort(remove_components_ids.ptr, remove_components_ids.length, - ushort.sizeof, &compareUShorts); + ushort.sizeof, &compareUShorts); { uint k = 0; uint j = 1; @@ -1417,20 +1830,20 @@ export struct EntityManager foreach (comp; info.components) { if (comp < base_tmpl.info.tmpl_deltas.length - && base_tmpl.info.tmpl_deltas[comp] != ushort.max) //copy data from base component - { + && base_tmpl.info.tmpl_deltas[comp] != ushort.max) //copy data from base component + { if (components[comp].size == 0) continue; memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], - base_tmpl.entity_data.ptr + base_tmpl.info.tmpl_deltas[comp], - components[comp].size); + base_tmpl.entity_data.ptr + base_tmpl.info.tmpl_deltas[comp], + components[comp].size); } else //fill with default data { if (components[comp].size == 0) continue; memcpy(temp.entity_data.ptr + info.tmpl_deltas[comp], - components[comp].init_data.ptr, components[comp].size); + components[comp].init_data.ptr, components[comp].size); } } @@ -1445,6 +1858,7 @@ export struct EntityManager */ export EntityTemplate* allocateTemplate(EntityTemplate* copy_tmpl) { + assert(copy_tmpl, "copy_tmpl can't be null"); EntityTemplate* tmpl = Mallocator.make!EntityTemplate; tmpl.info = copy_tmpl.info; tmpl.entity_data = Mallocator.makeArray(copy_tmpl.entity_data); @@ -1459,76 +1873,86 @@ export struct EntityManager */ export EntityInfo* getEntityInfo(ushort[] ids) { + if(ids.length == 0)ids = null; EntityInfo* info = entities_infos.get(ids, null); if (info is null) { info = Mallocator.make!EntityInfo; - info.components = Mallocator.makeArray(ids); - /*info.components = Mallocator.makeArray!ushort(ids.length); - info.components[0 .. $] = ids[0 .. $];*/ - info.deltas = Mallocator.makeArray!ushort(ids[$ - 1] + 1); - info.size = EntityID.sizeof; info.alignment = EntityID.alignof; - info.tmpl_deltas = Mallocator.makeArray!ushort(ids[$ - 1] + 1, ushort.max); - uint components_size = EntityID.sizeof; - - foreach (i, id; ids) + if(ids is null) { - 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; + uint block_memory = cast(uint)( + m_page_size - EntitiesBlock.sizeof - info.size); + uint entites_in_block = block_memory / info.size; + info.max_entities = cast(ushort) entites_in_block; } - alignNum(info.size, info.alignment); - - uint block_memory = cast(uint)( - m_page_size - EntitiesBlock.sizeof - (info.size - components_size)); - //uint entity_comps_size = EntityID.sizeof; - uint mem_begin = EntitiesBlock.sizeof; - - 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) + else { - if (current_delta == 0) - current_delta = ushort.max; - alignNum(current_delta, components[id].alignment); - info.deltas[id] = cast(ushort) current_delta; - current_delta += entites_in_block * components[id].size; + uint components_size = EntityID.sizeof; + + info.components = Mallocator.makeArray(ids); + info.deltas = Mallocator.makeArray!ushort(ids[$ - 1] + 1); + info.tmpl_deltas = Mallocator.makeArray!ushort(ids[$ - 1] + 1, ushort.max); + + 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)( + m_page_size - EntitiesBlock.sizeof - (info.size - components_size)); + //uint entity_comps_size = EntityID.sizeof; + uint mem_begin = EntitiesBlock.sizeof; + + 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) + { + if (current_delta == 0) + current_delta = ushort.max; + alignNum(current_delta, components[id].alignment); + info.deltas[id] = cast(ushort) current_delta; + current_delta += entites_in_block * components[id].size; + } + + info.comp_add_info = Mallocator.makeArray!(EntityInfo*)(info.deltas.length); + info.comp_rem_info = Mallocator.makeArray!(EntityInfo*)(info.deltas.length); + + foreach (comp; info.components) + { + info.comp_add_info[comp] = info; + info.comp_rem_info[comp] = null; + } } info.systems = Mallocator.makeArray!bool(systems.length); foreach (i, ref system; systems) { + if (system.isAlive() == false) + continue; if (system.m_empty) continue; if (system.m_update is null) { if (system.m_add_entity || system.m_remove_entity - || system.m_change_entity || system.m_event_callers.length) + || system.m_change_entity || system.m_event_callers.length) connectListenerToEntityInfo(*info, cast(uint) i); continue; } addSystemCaller(*info, cast(uint) i); } - info.comp_add_info = Mallocator.makeArray!(EntityInfo*)(gEntityManager.components.length); - //info.comp_rem_info = Mallocator.makeArray!(EntityInfo*)(gEntityManager.components.length); - info.comp_rem_info = Mallocator.makeArray!(EntityInfo*)(info.deltas.length); - - foreach (comp; info.components) - { - info.comp_add_info[comp] = info; - info.comp_rem_info[comp] = null; - } - entities_infos.add(info.components, info); generateListeners(info); @@ -1679,7 +2103,9 @@ export struct EntityManager } ///call Custom Entity Filter test if function exists - if(system.m_filter_entity && !(cast(bool function(void* system_pointer, EntityInfo* info) @nogc nothrow)system.m_filter_entity)(system, &entity))return; + if (system.m_filter_entity && !(cast(bool function(void* system_pointer, EntityInfo* info) @nogc nothrow) system + .m_filter_entity)(system, &entity)) + return; entity.systems[system_id] = true; } @@ -1724,7 +2150,8 @@ export struct EntityManager System* system = &systems[system_id]; connectListenerToEntityInfo(info, system_id); - if(!info.systems[system_id])return; + if (!info.systems[system_id]) + return; uint index = 0; for (; index < passes[system.m_pass].system_callers.length; index++) @@ -1822,7 +2249,7 @@ export struct EntityManager { if (!new_info.systems[listener]) { - callRemoveEntityListener(&systems[listener], info, block, ind, 1); + callRemoveEntityListener(&systems[listener], info, block, ind, ind + 1); } } } @@ -1833,7 +2260,7 @@ export struct EntityManager if (comp_size == 0) continue; 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); + cast(void*) block + info.deltas[comp] + ind * comp_size, comp_size); } new_block.entities_count++; @@ -1847,7 +2274,7 @@ export struct EntityManager if (!info.systems[listener]) { callAddEntityListener(&systems[listener], new_info, new_block, - new_block.entities_count - 1, 1); + new_block.entities_count - 1, new_block.entities_count); } } } @@ -1859,7 +2286,7 @@ export struct EntityManager if (info.systems[listener]) { callChangeEntityListener(&systems[listener], new_info, new_block, - new_block.entities_count - 1, 1, del_ids); + new_block.entities_count - 1, new_block.entities_count, del_ids); } } } @@ -1965,7 +2392,7 @@ export struct EntityManager { if (!new_info.systems[listener]) { - callRemoveEntityListener(&systems[listener], info, block, ind, 1); + callRemoveEntityListener(&systems[listener], info, block, ind, ind + 1); } } } @@ -2010,7 +2437,7 @@ export struct EntityManager if (!info.systems[listener]) { callAddEntityListener(&systems[listener], new_info, new_block, - new_block.entities_count - 1, 1); + new_block.entities_count - 1, new_block.entities_count); } } } @@ -2022,7 +2449,7 @@ export struct EntityManager if (info.systems[listener]) { callChangeEntityListener(&systems[listener], new_info, new_block, - new_block.entities_count - 1, 1, new_ids); + new_block.entities_count - 1, new_block.entities_count, new_ids); } } } @@ -2065,7 +2492,7 @@ export struct EntityManager { if (components[ref_.component_id].size != 0) data.changeEntitiesList.add( - (cast(ubyte*) ref_.ptr)[0 .. components[ref_.component_id].size]); + (cast(ubyte*) ref_.ptr)[0 .. components[ref_.component_id].size]); } } @@ -2115,12 +2542,12 @@ export struct EntityManager ushort size = components[comp].size; if (size != 0) memcpy(cast(void*) new_block + info.deltas[comp] + new_id * size, - cast(void*) block + info.deltas[comp] + size * index, size); + cast(void*) block + info.deltas[comp] + size * index, size); if (components[comp].create_callback) { components[comp].create_callback( - cast(void*) new_block + info.deltas[comp] + new_id * size); + cast(void*) new_block + info.deltas[comp] + new_id * size); } } @@ -2139,7 +2566,7 @@ export struct EntityManager use you should save ID instead of pointer. Params: - tmpl = pointer entity template allocated by EntityManager. + tmpl = pointer entity template allocated by EntityManager. Can be null in which case empty entity would be added (entity without components) */ export Entity* addEntity(EntityTemplate* tmpl) { @@ -2151,12 +2578,14 @@ export struct EntityManager use you should save ID instead of pointer. Params: - tmpl = pointer entity template allocated by EntityManager. + tmpl = pointer entity template allocated by EntityManager. Can be null in which case empty entity would be added (entity without components) replacement = list of components references to used. Memory form list replace data from template inside new entity. Should be used only for data which vary between most entities (like 3D position etc.) */ export Entity* addEntity(EntityTemplate* tmpl, ComponentRef[] replacement) { - EntityInfo* info = tmpl.info; + EntityInfo* info = void; + if(tmpl)info = tmpl.info; + else info = getEntityInfo(null); ushort index = 0; EntitiesBlock* block; @@ -2177,7 +2606,7 @@ export struct EntityManager uint size = components[comp].size; if (size != 0) memcpy(cast(void*) block + info.deltas[comp] + size * id, - tmpl.entity_data.ptr + info.tmpl_deltas[comp], size); + tmpl.entity_data.ptr + info.tmpl_deltas[comp], size); } foreach (comp; replacement) @@ -2199,7 +2628,7 @@ export struct EntityManager if (components[comp].create_callback) { components[comp].create_callback( - cast(void*) block + info.deltas[comp] + id * components[comp].size); + cast(void*) block + info.deltas[comp] + id * components[comp].size); } } @@ -2325,7 +2754,7 @@ export struct EntityManager { uint pos = block.entityIndex(entity); - callRemoveEntityListeners(info, block, pos, 1); + callRemoveEntityListeners(info, block, pos, pos + 1); } id_manager.releaseID(id); //release id from manager @@ -2334,7 +2763,7 @@ export struct EntityManager } private void removeEntityNoID(Entity* entity, EntitiesBlock* block, - bool call_destructors = false) nothrow @nogc + bool call_destructors = false) nothrow @nogc { EntityInfo* info = block.type_info; @@ -2456,51 +2885,49 @@ export struct EntityManager return has_work; } - private void callAddEntityListeners(EntityInfo* info, EntitiesBlock* block, int begin, int count) @nogc nothrow + private void callAddEntityListeners(EntityInfo* info, EntitiesBlock* block, int begin, int end) @nogc nothrow { foreach (listener; info.add_listeners) { System* system = &systems[listener]; - callAddEntityListener(system, info, block, begin, count); + callAddEntityListener(system, info, block, begin, end); } } private static void callAddEntityListener(System* system, EntityInfo* info, - EntitiesBlock* block, int begin, int count) @nogc nothrow + EntitiesBlock* block, int begin, int end) @nogc nothrow { ListenerCallData data; data.system = system; data.block = block; data.begin = begin; - data.count = count; - data.info = block.type_info; + data.end = end; (cast(void function(ref ListenerCallData) nothrow @nogc) system.m_add_entity)(data); } private void callRemoveEntityListeners(EntityInfo* info, EntitiesBlock* block, int begin, - int count) @nogc nothrow + int end) @nogc nothrow { foreach (listener; info.remove_listeners) { System* system = &systems[listener]; - callRemoveEntityListener(system, info, block, begin, count); + callRemoveEntityListener(system, info, block, begin, end); } } private static void callRemoveEntityListener(System* system, - EntityInfo* info, EntitiesBlock* block, int begin, int count) @nogc nothrow + EntityInfo* info, EntitiesBlock* block, int begin, int end) @nogc nothrow { ListenerCallData data; data.system = system; data.block = block; data.begin = begin; - data.count = count; - data.info = block.type_info; + data.end = end; (cast(void function(ref ListenerCallData) nothrow @nogc) system.m_remove_entity)(data); } private void callChangeEntityListener(System* system, EntityInfo* info, - EntitiesBlock* block, int begin, int count, ushort[] ch_ids) @nogc nothrow + EntitiesBlock* block, int begin, int end, ushort[] ch_ids) @nogc nothrow { int i = 0; int j = 0; @@ -2532,8 +2959,7 @@ export struct EntityManager data.system = system; data.block = block; data.begin = begin; - data.count = count; - data.info = block.type_info; + data.end = end; (cast(void function(ref ListenerCallData) nothrow @nogc) system.m_change_entity)(data); } @@ -2576,7 +3002,7 @@ export struct EntityManager if (info.add_listeners) { - callAddEntityListeners(info, block, entities_count, block.entities_count - entities_count); + callAddEntityListeners(info, block, entities_count, block.entities_count); } } @@ -2655,7 +3081,7 @@ export struct EntityManager foreach (caller; events[i].callers) { if (call_data.block.type_info.systems[caller.system.m_id] == false - || !caller.system.enabled || !caller.system.willExecute) + || !caller.system.enabled || !caller.system.willExecute) continue; call_data.system_pointer = caller.system.m_system_pointer; (cast(void function(ref EventCallData) nothrow @nogc) caller @@ -2718,9 +3144,11 @@ export struct EntityManager foreach (ref system; systems) { + if (system.isAlive() == false) + continue; if (system.enabled && system.m_begin) system.m_execute = (cast(bool function(void*)) system.m_begin)( - system.m_system_pointer); + system.m_system_pointer); } } @@ -2732,6 +3160,9 @@ export struct EntityManager foreach (ref system; systems) { + if (system.isAlive() == false) + continue; + if (system.enabled && system.m_end) (cast(void function(void*)) system.m_end)(system.m_system_pointer); } @@ -2752,11 +3183,6 @@ export struct EntityManager event_manager.sendEvent(id, event, threadID); } - void sendEvent(EntityID id, EventRef event) nothrow @nogc - { - event_manager.sendEvent(id, event, threadID); - } - private void generateDependencies() nothrow @nogc { foreach (pass_id, pass; passes) @@ -2850,8 +3276,6 @@ export struct EntityManager if (index > 0) { caller.exclusion = Mallocator.makeArray(exclusion[0 .. index]); - /*caller.exclusion = Mallocator.makeArray!(SystemCaller*)(index); - caller.exclusion[0..$] = exclusion[0 .. index];*/ } else caller.exclusion = null; @@ -2877,7 +3301,7 @@ export struct EntityManager } qsort(pass.system_callers.array.ptr, pass.system_callers.length, - (SystemCaller*).sizeof, &compareSystems); + (SystemCaller*).sizeof, &compareSystems); foreach (i, caller; pass.system_callers) caller.job_group.id = cast(uint) i; @@ -2899,8 +3323,6 @@ export struct EntityManager if (index > 0) { caller.dependencies = Mallocator.makeArray(exclusion[0 .. index]); - /*caller.dependencies = Mallocator.makeArray!(SystemCaller*)(index); - caller.dependencies[0..$] = exclusion[0 .. index];*/ caller.job_group.dependencies = Mallocator.makeArray!(JobGroup*)(index); foreach (j, dep; caller.dependencies) @@ -2932,9 +3354,19 @@ export struct EntityManager */ struct ComponentInfo { - /*export ~this() nothrow @nogc + export ~this() nothrow @nogc { - }*/ + } + + export void opAssign(ComponentInfo c) + { + size = c.size; + alignment = c.alignment; + init_data = c.init_data; + destroy_callback = c.destroy_callback; + create_callback = c.create_callback; + } + ///Component size ushort size; ///Component data alignment @@ -2957,25 +3389,19 @@ export struct EntityManager struct EventCallData { - Entity* entity; - void* event; - void* system_pointer; - ushort id; EntitiesBlock* block; + void* system_pointer; + void* event; + Entity* entity; + ushort id; } struct EventInfo { ushort size; ushort alignment; - void function(void* pointer) nothrow @nogc destroy_callback; EventCaller[] callers; - } - - struct EventRegisterInfo - { - ushort size; - ushort alignment; + void function(void* pointer) nothrow @nogc destroy_callback; } /************************************************************************************************************************ @@ -3012,7 +3438,7 @@ export struct EntityManager if (comp_add_info.length <= id) { EntityInfo*[] new_infos = Mallocator.makeArray!(EntityInfo*)( - gEntityManager.components.length); + gEntityManager.components.length); if (comp_add_info !is null) { //new_infos[0 .. comp_add_info.length] = comp_add_info[0 .. $]; @@ -3046,7 +3472,7 @@ export struct EntityManager break; } } - if (id > components[$ - 1]) + if (components.length == 0 || id > components[$ - 1]) ids[len++] = id; assert(len == components.length + 1); @@ -3057,7 +3483,7 @@ export struct EntityManager return new_info; } - EntityInfo* getNewInfoRemove(ushort id) return + EntityInfo* getNewInfoRemove(ushort id) return { /*if (comp_rem_info.length <= id) { @@ -3100,7 +3526,8 @@ export struct EntityManager export bool hasComponent(ushort component_id) { - if(component_id >= deltas.length || !deltas[component_id])return false; + if (component_id >= deltas.length || !deltas[component_id]) + return false; return true; } @@ -3169,7 +3596,7 @@ export struct EntityManager struct EntitiesBlock { ///return pointer to first element in block - export void* dataBegin() nothrow @nogc pure return + export void* dataBegin() nothrow @nogc pure return { ushort dif = EntitiesBlock.sizeof; return cast(void*)&this + dif; @@ -3211,60 +3638,7 @@ export struct EntityManager { export void update() nothrow @nogc { - if(!system.m_empty) - { - assert(info !is null); - - EntitiesBlock* block; - if (first_block) - block = first_block; - else - block = info.first_block; - - uint offset = begin; - uint last_entity; - uint blocks; - if (blocks_count) - blocks = blocks_count; - else - blocks = uint.max; - - SystemCallData call_data = SystemCallData(0, thread_id, job_id, context, info); - - while (block !is null && blocks > 0) - { - if (blocks == 1) - { - if (end) - last_entity = end; - else - last_entity = block.entities_count; - } - else - last_entity = block.entities_count; - - if (last_entity > 0) - { - call_data.block = block; - call_data.begin = offset; - call_data.count = last_entity - offset; - - assert(last_entity <= block.entities_count - && offset < block.entities_count); - assert(last_entity > offset); - - (cast(void function(SystemCallData*) @nogc nothrow) update_func)(&call_data); - } - block = block.next_block; - offset = 0; - blocks--; - } - } - else - { - SystemCallData call_data = SystemCallData(0, thread_id, job_id, context, info); - (cast(void function(SystemCallData*) @nogc nothrow) update_func)(&call_data); - } + (cast(SytemFuncType) system.m_update)(this); } ///system ID. Used to update system pointer after system reload. @@ -3274,14 +3648,12 @@ export struct EntityManager ///poiner to Entity type info EntityManager.EntityInfo* info; ///delegate function to call (by default it's delegate to onUpdate call) - void* context; - void* update_func; - //void delegate() update_delegate; + void delegate() update_delegate; ///pointer to first block into process (if 0 then first block will be used) EntitiesBlock* first_block; ///number of blocks to update (if 0 then update all) - ushort blocks_count; + ushort blocks; ///index of first element in first block ushort begin; ///index of last element in last block @@ -3292,30 +3664,12 @@ export struct EntityManager uint job_id; } - /************************************************************************************************************************ - Structure with data used to call single System call. - - first_block, begin, end, blocks parameters are used - to call partial info update - */ - struct SystemCallData - { - uint count; - uint thread_id; - uint job_id; - void* context; - EntityInfo* info; - EntitiesBlock* block; - uint begin; - } - struct ListenerCallData { - uint count; System* system; - EntityInfo* info; EntitiesBlock* block; uint begin; + uint end; } struct Job @@ -3325,7 +3679,6 @@ export struct EntityManager export void execute() nothrow @nogc { - //gEntityManager.getThreadID(); foreach (ref caller; callers) { caller.thread_id = gEntityManager.threadID(); @@ -3370,32 +3723,32 @@ export struct EntityManager struct ThreadData { - ref Vector!EntityID entitesToRemove() @nogc nothrow return + ref Vector!EntityID entitesToRemove() @nogc nothrow return { return entities_to_remove[data_index]; } - ref SimpleVector changeEntitiesList() @nogc nothrow return + ref SimpleVector changeEntitiesList() @nogc nothrow return { return change_entities_list[data_index]; } - ref Vector!(EntityInfo*) infosToUpdate() @nogc nothrow return + ref Vector!(EntityInfo*) infosToUpdate() @nogc nothrow return { return infos_to_update[data_index]; } - ref Vector!EntityID entitiesToRemovePrev() @nogc nothrow return + ref Vector!EntityID entitiesToRemovePrev() @nogc nothrow return { return entities_to_remove[1 - data_index]; } - ref SimpleVector changeEntitiesListPrev() @nogc nothrow return + ref SimpleVector changeEntitiesListPrev() @nogc nothrow return { return change_entities_list[1 - data_index]; } - ref Vector!(EntityInfo*) infosToUpdatePrev() @nogc nothrow return + ref Vector!(EntityInfo*) infosToUpdatePrev() @nogc nothrow return { return infos_to_update[1 - data_index]; } @@ -3531,424 +3884,5 @@ export struct EntityManager return ret; } } - + } - -/*static void fillInputData(Sys)(ref Sys.EntitiesData input_data, EntityManager.EntityInfo* info, - EntityManager.EntitiesBlock* block, uint offset, uint entities_count)//, System* system) -{ - //enum ComponentsIndices components_info = getComponentsInfo(); - - static if (components_info.entites_array) - { - __traits(getMember, input_data, components_info.entites_array) = ( - cast(Entity*) block.dataBegin())[offset .. entities_count]; - } - - static if (hasMember!(Sys.EntitiesData, "length")) - { - input_data.length = cast(typeof(input_data.length))(entities_count - offset); - } - - // static if (hasMember!(Sys.EntitiesData, "thread_id")) - // { - // input_data.thread_id = cast(typeof(input_data.thread_id))threadID(); - // } - - ///FIXME: should be "components_info.req()" but it's not compile with GCC - static foreach(comp; __traits(Stru)) - static foreach (iii, comp_info; components_info.m_req[0 - .. components_info.m_req_counter]) - { - __traits(getMember, input_data, comp_info.name) = (cast(ForeachType!(typeof(__traits(getMember, - Sys.EntitiesData, comp_info.name)))*)( - cast(void*) block + info.deltas[becsID!(typeof(__traits(getMember, Sys.EntitiesData, comp_info.name)))]))[offset - .. entities_count]; - } - - static foreach (iii, comp_info; components_info.m_optional[0 - .. components_info.m_optional_counter]) - { - ushort system_id = becsID!(typeof(__traits(getMember, Sys.EntitiesData, comp_info.name))); - if (system_id < info.deltas.length - && info.deltas[system_id] != 0) - { - __traits(getMember, input_data, comp_info.name) = (cast(ForeachType!(typeof(__traits(getMember, - Sys.EntitiesData, comp_info.name)))*)(cast( - void*) block + info.deltas[system_id]))[offset - .. entities_count]; - - } - } -}*/ - -struct SystemEntityData(Sys) -{ - static struct CompInfo - { - string name; - string type; - } - - static struct ComponentsCounts - { - //one more than should be to prevent null arrays (zero length arrays) - uint readonly = 1; - uint mutable = 1; - uint excluded = 1; - uint optional = 1; - uint req = 1; - uint readonly_dep = 1; - uint writable_dep = 1; - } - - static ComponentsCounts getComponentsCounts() - { - ComponentsCounts components_counts; - - bool checkExcludedComponentsSomething(Sys)() - { - return __traits(compiles, allSameType!(string, typeof(Sys.ExcludedComponents))) && allSameType!(string, - typeof(Sys.ExcludedComponents)) && isExpressions!(Sys.ExcludedComponents); - } - - foreach (member; __traits(allMembers, Sys.EntitiesData)) - { - alias MemberType = typeof(__traits(getMember, Sys.EntitiesData, member)); - if (member == "length" || member == "thread_id" || member == "job_id" - || is(MemberType == Entity[]) || is(MemberType == const(Entity)[])) - { - //continue; - } - else - { - string name; - static if (isArray!MemberType) - { // Workaround. This code is never called with: not an array type, but compiler prints an error - name = fullName!(Unqual!(ForeachType!MemberType)); - } - - bool is_optional; - bool is_read_only; - - if (is(CopyConstness!(ForeachType!(MemberType), int) == const(int))) - { - is_read_only = true; - } - - foreach (att; __traits(getAttributes, __traits(getMember, - Sys.EntitiesData, member))) - { - if (att == "optional") - { - is_optional = true; - } - if (att == "readonly") - { - is_read_only = true; - } - } - if (is_read_only) - { - components_counts.readonly++; - } - else - { - components_counts.mutable++; - } - if (is_optional) - { - components_counts.optional++; - } - else - { - components_counts.req++; - } - } - } - - static if (__traits(hasMember, Sys, "ExcludedComponents")) - { - static if (is(Sys.ExcludedComponents == enum)) - { - foreach (str; __traits(allMembers, Sys.ExcludedComponents)) - { - components_counts.excluded++; - } - } - else //static if (checkExcludedComponentsSomething!Sys) - { - foreach (str; Sys.ExcludedComponents) - { - components_counts.excluded++; - } - } - } - - static if (__traits(hasMember, Sys, "ReadOnlyDependencies")) - { - foreach (str; Sys.ReadOnlyDependencies) - { - components_counts.readonly_dep++; - } - } - - static if (__traits(hasMember, Sys, "WritableDependencies")) - { - foreach (str; Sys.WritableDependencies) - { - components_counts.writable_dep++; - } - } - - return components_counts; - } - - enum ComponentsCounts component_counts = getComponentsCounts(); - - static struct ComponentsIndices(ComponentsCounts counts) - { - CompInfo[] readonly() - { - return m_readonly[0 .. m_readonly_counter]; - } - - CompInfo[] mutable() - { - return m_mutable[0 .. m_mutable_counter]; - } - - CompInfo[] excluded() - { - return m_excluded[0 .. m_excluded_counter]; - } - - CompInfo[] optional() - { - return m_optional[0 .. m_optional_counter]; - } - - CompInfo[] req() - { - return m_req[0 .. m_req_counter]; - } - - CompInfo[] readonlyDeps() - { - return m_readonly_dep[0 .. m_readonly_dep_counter]; - } - - CompInfo[] writableDeps() - { - return m_writable_dep[0 .. m_writable_dep_counter]; - } - - void addReadonly(CompInfo info) - { - m_readonly[m_readonly_counter++] = info; - } - - void addMutable(CompInfo info) - { - m_mutable[m_mutable_counter++] = info; - } - - void addExcluded(CompInfo info) - { - m_excluded[m_excluded_counter++] = info; - } - - void addOptional(CompInfo info) - { - m_optional[m_optional_counter++] = info; - } - - void addReq(CompInfo info) - { - m_req[m_req_counter++] = info; - } - - void addReadonlyDep(CompInfo info) - { - m_readonly_dep[m_readonly_dep_counter++] = info; - } - - void addWritableDep(CompInfo info) - { - m_writable_dep[m_writable_dep_counter++] = info; - } - - CompInfo[counts.readonly] m_readonly; - CompInfo[counts.mutable] m_mutable; - CompInfo[counts.excluded] m_excluded; - CompInfo[counts.optional] m_optional; - CompInfo[counts.req] m_req; - CompInfo[counts.readonly_dep] m_readonly_dep; - CompInfo[counts.writable_dep] m_writable_dep; - - uint m_readonly_counter; - uint m_mutable_counter; - uint m_excluded_counter; - uint m_optional_counter; - uint m_req_counter; - uint m_readonly_dep_counter; - uint m_writable_dep_counter; - - string entites_array; - } - - enum ComponentsIndices!component_counts components_info = getComponentsInfo(); - - static ComponentsIndices!component_counts getComponentsInfo() - { - ComponentsIndices!component_counts components_info; - - bool checkExcludedComponentsSomething(Sys)() - { - return __traits(compiles, allSameType!(string, typeof(Sys.ExcludedComponents))) && allSameType!(string, - typeof(Sys.ExcludedComponents)) && isExpressions!(Sys.ExcludedComponents); - } - - foreach (member; __traits(allMembers, Sys.EntitiesData)) - { - alias MemberType = typeof(__traits(getMember, Sys.EntitiesData, member)); - if (member == "length" || member == "thread_id" || member == "job_id" - || is(MemberType == Entity[]) || is(MemberType == const(Entity)[])) - { - if (is(MemberType == Entity[]) || is(MemberType == const(Entity)[])) - components_info.entites_array = member; - //continue; - } - else - { - string name; - static if (isArray!MemberType) - { // Workaround. This code is never called with: not an array type, but compiler prints an error - name = fullName!(Unqual!(ForeachType!MemberType)); - } - - bool is_optional; - bool is_read_only; - - if (is(CopyConstness!(ForeachType!(MemberType), int) == const(int))) - { - is_read_only = true; - } - - foreach (att; __traits(getAttributes, __traits(getMember, - Sys.EntitiesData, member))) - { - if (att == "optional") - { - is_optional = true; - } - if (att == "readonly") - { - is_read_only = true; - } - } - if (is_read_only) - { - components_info.addReadonly(CompInfo(member, name)); - } - else - { - components_info.addMutable(CompInfo(member, name)); - } - if (is_optional) - { - components_info.addOptional(CompInfo(member, name)); - } - else - { - components_info.addReq(CompInfo(member, name)); - } - } - } - - static if (__traits(hasMember, Sys, "ExcludedComponents")) - { - static if (is(Sys.ExcludedComponents == enum)) - { - foreach (str; __traits(allMembers, Sys.ExcludedComponents)) - { - components_info.addExcluded(CompInfo(str, str)); - } - } - else //static if (checkExcludedComponentsSomething!Sys) - { - foreach (str; Sys.ExcludedComponents) - { - components_info.addExcluded(CompInfo(str.stringof, fullName!str)); - // components_info.addExcluded(CompInfo(str.stringof, str.stringof)); - } - - } - } - - static if (__traits(hasMember, Sys, "ReadOnlyDependencies")) - { - foreach (str; Sys.ReadOnlyDependencies) - { - components_info.addReadonlyDep(CompInfo(str, str)); - } - } - - static if (__traits(hasMember, Sys, "WritableDependencies")) - { - foreach (str; Sys.WritableDependencies) - { - components_info.addWritableDep(CompInfo(str, str)); - } - } - - return components_info; - } - - static void fillInputData(ref Sys.EntitiesData input_data, EntityManager.EntityInfo* info, - EntityManager.EntitiesBlock* block, uint offset, uint entities_count)//, System* system) - { - //enum ComponentsIndices components_info = getComponentsInfo(); - uint last_entity = entities_count + offset; - - static if (components_info.entites_array) - { - __traits(getMember, input_data, components_info.entites_array) = ( - cast(Entity*) block.dataBegin())[offset .. last_entity]; - } - - static if (hasMember!(Sys.EntitiesData, "length")) - { - input_data.length = cast(typeof(input_data.length))(entities_count); - } - - // static if (hasMember!(Sys.EntitiesData, "thread_id")) - // { - // input_data.thread_id = cast(typeof(input_data.thread_id))threadID(); - // } - - ///FIXME: should be "components_info.req()" but it's not compile with GCC - static foreach (iii, comp_info; components_info.m_req[0 - .. components_info.m_req_counter]) - { - __traits(getMember, input_data, comp_info.name) = (cast(ForeachType!(typeof(__traits(getMember, - Sys.EntitiesData, comp_info.name)))*)( - cast(void*) block + info.deltas[becsID!(ForeachType!(typeof(__traits(getMember, Sys.EntitiesData, comp_info.name))))]))[offset - .. last_entity]; - } - - static foreach (iii, comp_info; components_info.m_optional[0 - .. components_info.m_optional_counter]) - { - if (becsID!(ForeachType!(typeof(__traits(getMember, Sys.EntitiesData, comp_info.name)))) < info.deltas.length - && info.deltas[becsID!(ForeachType!(typeof(__traits(getMember, Sys.EntitiesData, comp_info.name))))] != 0) - { - __traits(getMember, input_data, comp_info.name) = (cast(ForeachType!(typeof(__traits(getMember, - Sys.EntitiesData, comp_info.name)))*)(cast( - void*) block + info.deltas[becsID!(ForeachType!(typeof(__traits(getMember, Sys.EntitiesData, comp_info.name))))]))[offset - .. last_entity]; - - } - } - } -} \ No newline at end of file diff --git a/source/bubel/ecs/package.d b/source/bubel/ecs/package.d index 51da325..e2d8893 100644 --- a/source/bubel/ecs/package.d +++ b/source/bubel/ecs/package.d @@ -1,3 +1,7 @@ +/************************************************************************************************************************ +Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. +*/ module ecs; public import bubel.ecs.core; diff --git a/source/bubel/ecs/simple_vector.d b/source/bubel/ecs/simple_vector.d index acf01ee..2fdaf31 100644 --- a/source/bubel/ecs/simple_vector.d +++ b/source/bubel/ecs/simple_vector.d @@ -1,3 +1,7 @@ +/************************************************************************************************************************ +Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. +*/ module bubel.ecs.simple_vector; import bubel.ecs.std; diff --git a/source/bubel/ecs/std.d b/source/bubel/ecs/std.d index d99a05c..8a3e207 100644 --- a/source/bubel/ecs/std.d +++ b/source/bubel/ecs/std.d @@ -2,16 +2,14 @@ It's internal code! This module contain implementation of standard functionality. -Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz License: BSD 3-clause, see LICENSE file in project root folder. */ module bubel.ecs.std; -version (Emscripten) version = ECSEmscripten; - import std.traits; -version (ECSEmscripten) +version (Emscripten) { extern (C) struct pthread_mutex_t { @@ -29,10 +27,6 @@ version (ECSEmscripten) extern (C) int memcmp(const void* s1, const void* s2, size_t size); extern (C) void exit(int status) nothrow @nogc; - extern (C) void __assert(const(char)* msg, const(char)* file, uint line) - { - exit(-20); - } extern (C) void free(void*) @nogc nothrow @system; extern (C) void* malloc(size_t size) @nogc nothrow @system; @@ -60,7 +54,7 @@ else public import core.stdc.stdlib : qsort; } -version (ECSEmscripten) +version (Emscripten) { } else version (Windows) @@ -89,7 +83,7 @@ else version (Posix) import core.sys.posix.stdlib : posix_memalign; } -version (ECSEmscripten) +version (Emscripten) { private const uint max_alloca = 10000; private __gshared byte[max_alloca] alloca_array; @@ -294,7 +288,7 @@ static struct Mallocator posix_memalign(&ret, alignment, length); //ret = aligned_alloc(alignment, length); else version (Windows) ret = _aligned_malloc(length, alignment); - else version (ECSEmscripten) + else version (Emscripten) posix_memalign(&ret, alignment, length); //malloc(length); else static assert(0, "Unimplemented platform!"); @@ -341,7 +335,7 @@ static struct Mallocator free(cast(void*) object); else version (Windows) _aligned_free(cast(void*) object); - else version (ECSEmscripten) + else version (Emscripten) free(cast(void*) object); else static assert(0, "Unimplemented platform!"); @@ -351,7 +345,7 @@ static struct Mallocator struct Mutex { - version (ECSEmscripten) + version (Emscripten) { void initialize() nothrow @nogc { diff --git a/source/bubel/ecs/system.d b/source/bubel/ecs/system.d index 256841d..310b53a 100644 --- a/source/bubel/ecs/system.d +++ b/source/bubel/ecs/system.d @@ -1,7 +1,7 @@ /************************************************************************************************************************ System module. -Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz License: BSD 3-clause, see LICENSE file in project root folder. */ module bubel.ecs.system; @@ -13,14 +13,14 @@ import bubel.ecs.manager; System contain data required to proper glue EntityManager with Systems. System callbacks: $(LIST - * void onUpdate(EntitesData); + * void onUpdate(EntitiesData); * void onEnable() - called inside system.enable() function * void onDisable() - called inside system.disable() function * bool onBegin() - called inside manager.begin() * void onEnd() - called inside manager.end() * void onCreate() - called after registration inside registerSystem function * void onDestroy() - called during re-registration and inside manager destructor - * void onAddEntity(EntitesData) - called for every entity which are assigned to system (by adding new entity or changing its components) + * void onAddEntity(EntitiesData) - called for every entity which are assigned to system (by adding new entity or changing its components) * void onRemoveEntity(EntitiesData) - called for every entity removed from system update process * void onChangeEntity(EntitiesData) - called for every entity which components are changed but it was previously assigned to system * void handleEvent(Entity*, Event) - called for every event supported by system @@ -89,38 +89,100 @@ struct System return cast(const(char)[]) m_name; } -//package: + /************************************************************************************************************************ + Return false if system was unregistered, true otherwise. + */ + export bool isAlive() nothrow @nogc + { + return m_system_pointer != null; + } - void destroy() + /************************************************************************************************************************ + Return pointer to user side system object + */ + export void* ptr() nothrow @nogc + { + return m_system_pointer; + } + +package: + + ///destory system. Dispose all data + export void destroy() nothrow @nogc + { + import bubel.ecs.std : Mallocator; + + destroySystemData(); + + if (m_name) + { + Mallocator.dispose(m_name); + m_name = null; + } + } + + ///destroy all system data but keeps name which is used for case of system re-registration + void destroySystemData() nothrow @nogc { import bubel.ecs.std : Mallocator; disable(); if (m_destroy) - (cast(void function(void*)) m_destroy)(m_system_pointer); + { + (cast(void function(void*) nothrow @nogc) m_destroy)(m_system_pointer); + m_destroy = null; + } - if (m_name) - Mallocator.dispose(m_name); if (m_components) + { Mallocator.dispose(m_components); + m_components = null; + } if (m_excluded_components) + { Mallocator.dispose(m_excluded_components); + m_excluded_components = null; + } if (m_optional_components) + { Mallocator.dispose(m_optional_components); + m_optional_components = null; + } if (jobs) + { Mallocator.dispose(jobs); + jobs = null; + } if (m_read_only_components) + { Mallocator.dispose(m_read_only_components); + m_read_only_components = null; + } if (m_writable_components) + { Mallocator.dispose(m_writable_components); + m_writable_components = null; + } if (m_readonly_dependencies) + { Mallocator.dispose(m_readonly_dependencies); + m_readonly_dependencies = null; + } if (m_writable_dependencies) + { Mallocator.dispose(m_writable_dependencies); + m_writable_dependencies = null; + } if (m_event_callers) + { Mallocator.dispose(m_event_callers); + m_event_callers = null; + } if (m_system_pointer) + { Mallocator.dispose(m_system_pointer); + m_system_pointer = null; + } } struct EventCaller @@ -170,7 +232,7 @@ struct System //void function(ref EntityManager.CallData data) m_update; void* m_update; ///workaroud for DMD bug with upper line - //void delegate() m_update_delegate; + void delegate() m_update_delegate; //void function(void* system_pointer) m_enable; //void function(void* system_pointer) m_disable; @@ -198,6 +260,6 @@ struct System //void function(ref EntityManager.CallData data) m_initialize; //void function(ref EntityManager.CallData data) m_deinitilize; - // void* m_initialize; - // void* m_deinitilize; + void* m_initialize; + void* m_deinitilize; } diff --git a/source/bubel/ecs/traits.d b/source/bubel/ecs/traits.d index 849632a..79901db 100644 --- a/source/bubel/ecs/traits.d +++ b/source/bubel/ecs/traits.d @@ -1,3 +1,7 @@ +/************************************************************************************************************************ +Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. +*/ module bubel.ecs.traits; import std.traits; @@ -7,8 +11,11 @@ import std.traits; */ ref ushort becsID(T)() { - __gshared ushort id = ushort.max; - return id; + /// Embed id in struct so export can be added to variable definition + static struct LocalStruct { + export __gshared ushort id = ushort.max; + } + return LocalStruct.id; } /************************************************************************************************************************ diff --git a/source/bubel/ecs/vector.d b/source/bubel/ecs/vector.d index 019673b..6cf6274 100644 --- a/source/bubel/ecs/vector.d +++ b/source/bubel/ecs/vector.d @@ -1,3 +1,7 @@ +/************************************************************************************************************************ +Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. +*/ module bubel.ecs.vector; import core.bitop; diff --git a/tests/basic.d b/tests/basic.d index 3f08caa..ea54382 100644 --- a/tests/basic.d +++ b/tests/basic.d @@ -113,6 +113,31 @@ struct EmptySystem int count = 0; } +struct EntityCounterSystem +{ + mixin ECS.System!1; + + struct EntitiesData + { + int thread_id; + uint length; + Entity[] entity; + } + + bool onBegin() + { + count = 0; + return true; + } + + void onUpdate(EntitiesData data) + { + count += data.length; + } + + int count = 0; +} + void beforeEveryTest() { becsID!CUnregistered = ushort.max; @@ -243,7 +268,82 @@ unittest gEntityManager.commit(); entity3 = gEntityManager.getEntity(id); assert(!entity3.getComponent!CUnregistered); +} +@("AddEmptyEntity") +unittest +{ + struct OnAddRemoveChangeCounter + { + mixin ECS.System!1; + + struct EntitiesData + { + int thread_id; + uint length; + Entity[] entity; + } + + void onAddEntity(EntitiesData data) + { + add += data.length; + } + + void onRemoveEntity(EntitiesData data) + { + assert(0, "It's impossible to remove entity from being updated by system which accept empty entity"); + } + + int add = 0; + } + + gEntityManager.beginRegister(); + + gEntityManager.registerSystem!EntityCounterSystem(0); + gEntityManager.registerSystem!OnAddRemoveChangeCounter(1); + + gEntityManager.endRegister(); + + CLong long_component = CLong(3); + + Entity* entity = null; + EntityID entity_id = gEntityManager.addEntity(null).id; + + EntityCounterSystem* system = gEntityManager.getSystem!EntityCounterSystem; + assert(system !is null); + assert(system.count == 0); + + OnAddRemoveChangeCounter* add_remove_change_system = gEntityManager.getSystem!OnAddRemoveChangeCounter; + assert(add_remove_change_system !is null); + assert(add_remove_change_system.add == 0); + + gEntityManager.commit(); + assert(add_remove_change_system.add == 1); + + entity = gEntityManager.getEntity(entity_id); + assert(!entity.hasComponent(becsID!CLong)); + assert(entity.getComponent(becsID!CLong) is null); + + + gEntityManager.begin(); + gEntityManager.update(); + assert(system.count == 1); + gEntityManager.end(); + + gEntityManager.addEntityCopy(entity_id); + gEntityManager.addEntityCopy(entity_id); + gEntityManager.addComponents(entity_id, [ComponentRef(&long_component, becsID(long_component))].staticArray); + gEntityManager.commit(); + assert(add_remove_change_system.add == 3, "onAddEntity missed"); + + entity = gEntityManager.getEntity(entity_id); + assert(entity.hasComponent(becsID!CLong)); + assert(*entity.getComponent!CLong == 3); + + gEntityManager.begin(); + gEntityManager.update(); + assert(system.count == 3); + gEntityManager.end(); } //allocate templates @@ -650,8 +750,6 @@ unittest void onAddEntity(EntitiesData data) { - foreach(i; 0..data.length) - data.long_[i] += 1; add++; assert(add_order == 1); add_order++; diff --git a/tests/bugs.d b/tests/bugs.d index 1a3968c..10a3c62 100644 --- a/tests/bugs.d +++ b/tests/bugs.d @@ -142,5 +142,34 @@ unittest gEntityManager.update(); gEntityManager.end(); + gEntityManager.destroy(); +} + +@("2-empty-entity-crash") +unittest +{ + + gEntityManager.initialize(0); + + gEntityManager.beginRegister(); + + gEntityManager.registerComponent!CInt; + gEntityManager.registerComponent!CFloat; + + gEntityManager.endRegister(); + + + EntityTemplate* tmpl = gEntityManager.allocateTemplate([becsID!CInt, becsID!CFloat].staticArray); + scope(exit) gEntityManager.freeTemplate(tmpl); + + EntityID id = gEntityManager.addEntity(tmpl).id; + // EntityID id2 = gEntityManager.addEntity().id; + + gEntityManager.commit(); + + gEntityManager.removeComponents(id, [becsID!CInt, becsID!CFloat].staticArray); + + gEntityManager.commit(); + gEntityManager.destroy(); } \ No newline at end of file diff --git a/tests/meson.build b/tests/meson.build index cfccaa7..ff1a3a1 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -10,7 +10,7 @@ tests_src = files( 'vector.d' ) -exe = executable('decs-tests', +exe = executable('BubelECSTests', tests_src, include_directories : [inc, include_directories('..')], d_args : args, diff --git a/tests/tests.d b/tests/tests.d index 16986cf..164eb30 100644 --- a/tests/tests.d +++ b/tests/tests.d @@ -283,13 +283,13 @@ struct ChangeTestSystem bool onBegin() { - // writeln("On Test System begin."); + ////writeln("On Test System begin."); return true; } void onEnd() { - // writeln("On Test System end."); + ////writeln("On Test System end."); } void initialize(ref Entity entity, ref TestComp comp) @@ -307,10 +307,10 @@ struct ChangeTestSystem void onUpdate(EntitiesData data) { - /*foreach (i; 0 .. data.length) + foreach (i; 0 .. data.length) { - }*/ + } } } @@ -318,8 +318,6 @@ struct TestSystem { mixin ECS.System!16; //__gshared ushort system_id; - uint print = 1; - void onCreate() { //writeln("On Test System create."); @@ -345,14 +343,13 @@ struct TestSystem bool onBegin() { - if(print)printf("On Test System begin.\n"); + ////writeln("On Test System begin."); return true; } void onEnd() { - if(print)printf("On Test System end.\n"); - print = 0; + ////writeln("On Test System end."); } void initialize(ref Entity entity, ref TestComp comp) @@ -409,7 +406,7 @@ struct TestSystemWithHighPriority void initialize(ref Entity entity, ref TestComp comp) { - + int o = 1; } void onUpdate(EntitiesData data) @@ -847,7 +844,6 @@ else: //dur = (MonoTime.currTime - time).total!"usecs"; //writeln("Entities adding: ", dur, " usecs"); printf("Entities adding: %f usecs\n", cast(float)(Time.getUSecTime() - time)); - Mallocator.dispose(idss); time = Time.getUSecTime(); uint blocks = 0; @@ -1025,6 +1021,8 @@ else: gEntityManager.freeTemplate(copy_default_tempalte); EntityManager.destroy(); + Mallocator.dispose(idss); + printf("end\n"); //*/ return 0;