diff --git a/source/bubel/ecs/core.d b/source/bubel/ecs/core.d index ccdf3e2..0487665 100644 --- a/source/bubel/ecs/core.d +++ b/source/bubel/ecs/core.d @@ -92,4 +92,20 @@ static struct ECS { alias ExcludedComponents = T; } + + /************************************************************************************************************************ + Make list of readonly ependencies. This template get strings as arguments. Should be added inside System structure. + */ + mixin template ReadOnlyDependencies(T...) + { + alias ReadOnlyDependencies = T; + } + + /************************************************************************************************************************ + Make list of writable ependencies. This template get strings as arguments. Should be added inside System structure. + */ + mixin template WritableDependencies(T...) + { + alias WritableDependencies = T; + } } \ No newline at end of file diff --git a/source/bubel/ecs/manager.d b/source/bubel/ecs/manager.d index 1b896db..c1eb62a 100644 --- a/source/bubel/ecs/manager.d +++ b/source/bubel/ecs/manager.d @@ -452,6 +452,8 @@ export struct EntityManager uint excluded = 1; uint optional = 1; uint req = 1; + uint readonly_dep = 1; + uint writable_dep = 1; } static ComponentsCounts getComponentsCounts()() @@ -532,7 +534,22 @@ export struct EntityManager { 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++; } } @@ -568,6 +585,16 @@ export struct EntityManager 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; @@ -593,17 +620,31 @@ export struct EntityManager 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; } @@ -616,6 +657,8 @@ export struct EntityManager size_t excluded = components_info.excluded.length; size_t read_only = components_info.readonly.length; size_t modified = components_info.mutable.length; + size_t read_only_deps = components_info.readonlyDeps.length; + size_t writable_deps = components_info.writableDeps.length; if (req > 0) system.m_components = Mallocator.makeArray!ushort(req); @@ -627,6 +670,10 @@ export struct EntityManager system.m_read_only_components = Mallocator.makeArray!ushort(read_only); if (modified > 0) system.m_modified_components = Mallocator.makeArray!ushort(modified); + if (read_only_deps > 0) + system.m_readonly_dependencies = Mallocator.makeArray!ushort(read_only_deps); + if (writable_deps > 0) + system.m_writable_dependencies = Mallocator.makeArray!ushort(writable_deps); } @@ -714,6 +761,22 @@ export struct EntityManager } } + 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; } @@ -809,7 +872,6 @@ export struct EntityManager ~ "\" due to non existing component \"" ~ comp_info.type ~ "\"."); system.m_modified_components[iii] = comp; } - } static void fillInputData(ref Sys.EntitiesData input_data, EntityInfo* info, @@ -1059,6 +1121,32 @@ export struct EntityManager 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) + assert(comp != ushort.max, + "Can't register system \"" ~ Sys.stringof + ~ "\" due to non existing dependency."); + else + assert(comp != ushort.max, "Can't register system \"" ~ Sys.stringof + ~ "\" due to non existing dependency \"" ~ comp_info.type ~ "\"."); + system.m_readonly_dependencies[iii] = comp; + } + + foreach (iii, comp_info; components_info.writableDeps) + { + ushort comp = external_dependencies_map.get(cast(char[]) comp_info.type, ushort.max); + version (D_BetterC) + assert(comp != ushort.max, + "Can't register system \"" ~ Sys.stringof + ~ "\" due to non existing dependency."); + else + assert(comp != ushort.max, "Can't register system \"" ~ Sys.stringof + ~ "\" due to non existing dependency \"" ~ comp_info.type ~ "\"."); + system.m_writable_dependencies[iii] = comp; + } + ushort sys_id = systems_map.get(cast(char[]) Sys.stringof, ushort.max); if (sys_id < systems.length) { @@ -1118,6 +1206,11 @@ export struct EntityManager return cast(ushort)(passes.length - 1); } + export void registerDependency(const(char)[] name) + { + return external_dependencies_map.add(name, cast(ushort)external_dependencies_map.length); + } + /************************************************************************************************************************ Register component into EntityManager. */ @@ -2820,10 +2913,45 @@ export struct EntityManager foreach (caller; pass.system_callers) { index = 0; + ///gets systems which are excluding each other out_for: foreach (caller2; pass.system_callers) { if (caller is caller2) continue; + + ///check for external dependencies + foreach (cmp; caller.system.m_readonly_dependencies) + { + foreach (cmp2; caller2.system.m_writable_dependencies) + { + if (cmp == cmp2) + { + exclusion[index++] = caller2; + continue out_for; + } + } + } + foreach (cmp; caller.system.m_writable_dependencies) + { + foreach (cmp2; caller2.system.m_readonly_dependencies) + { + if (cmp == cmp2) + { + exclusion[index++] = caller2; + continue out_for; + } + } + foreach (cmp2; caller2.system.m_writable_dependencies) + { + if (cmp == cmp2) + { + exclusion[index++] = caller2; + continue out_for; + } + } + } + + ///check for component dependencies foreach (cmp; caller.system.m_read_only_components) { foreach (cmp2; caller2.system.m_modified_components) @@ -2923,6 +3051,18 @@ export struct EntityManager } } + const (UpdatePass)* getPass(const (char)[] name) + { + ushort id = getPassID(name); + if(id == ushort.max)return null; + return passes[id]; + } + + ushort getPassID(const (char)[] name) + { + return passes_map.get(name, ushort.max); + } + /************************************************************************************************************************ Component info; */ @@ -3350,6 +3490,7 @@ export struct EntityManager HashMap!(char[], ushort) components_map; HashMap!(const(char)[], ushort) events_map; HashMap!(const(char)[], ushort) passes_map; + HashMap!(const(char)[], ushort) external_dependencies_map; Vector!System systems; Vector!ComponentInfo components; Vector!EventInfo events; diff --git a/source/bubel/ecs/system.d b/source/bubel/ecs/system.d index 7343831..5bf750b 100644 --- a/source/bubel/ecs/system.d +++ b/source/bubel/ecs/system.d @@ -129,6 +129,9 @@ package: ushort[] m_read_only_components; ushort[] m_modified_components; + ushort[] m_readonly_dependencies; + ushort[] m_writable_dependencies; + EntityManager.SystemCaller* m_any_system_caller; EventCaller[] m_event_callers; diff --git a/source/bubel/ecs/vector.d b/source/bubel/ecs/vector.d index 3334be2..413bbce 100644 --- a/source/bubel/ecs/vector.d +++ b/source/bubel/ecs/vector.d @@ -62,11 +62,11 @@ public: used = 0; } - export bool empty() { + export bool empty() const { return (used == 0); } - export size_t length() { + export size_t length() const { return used; } @@ -197,9 +197,9 @@ public: assert(ok, "There is no such an element in vector"); } - export ref T opIndex(size_t elemNum) { + export ref T opIndex(size_t elemNum) const { //debug assert(elemNum < used, "Range violation [index]"); - return array.ptr[elemNum]; + return *cast(T*)&array.ptr[elemNum]; } export auto opSlice() { diff --git a/tests/basic.d b/tests/basic.d index 653575f..10d79b8 100644 --- a/tests/basic.d +++ b/tests/basic.d @@ -1220,4 +1220,279 @@ unittest assert(*entity2.getComponent!CInt == 13); gEM.end(); +} + +@("SystemDependencies") +unittest +{ + struct TestSystem + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + @readonly CInt[] int_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + struct TestSystem2 + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + CInt[] int_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + struct TestSystem3 + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + @readonly CInt[] int_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + struct TestSystem4 + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + CInt[] int_; + CLong[] long_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + struct TestSystem5 + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + @readonly CLong[] int_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + void func1(TestSystem.EntitiesData entities) + { + foreach(i;0 .. entities.length) + { + entities.int_[i] += entities.int_[i] / 2; + } + } + + void func2(TestSystem.EntitiesData entities) + { + foreach(i;0 .. entities.length) + { + entities.int_[i] += 8; + } + } + + gEM.beginRegister(); + + gEM.registerSystem!TestSystem(0); + gEM.registerSystem!TestSystem2(1); + gEM.registerSystem!TestSystem3(2); + gEM.registerSystem!TestSystem4(3); + gEM.registerSystem!TestSystem5(4); + + gEM.endRegister(); + + const (EntityManager.UpdatePass)* pass = gEM.getPass("update"); + assert(pass != null); + assert(pass.system_callers.length == 5); + assert(pass.system_callers[0].system_id == TestSystem.system_id); + assert(pass.system_callers[1].system_id == TestSystem2.system_id); + assert(pass.system_callers[2].system_id == TestSystem3.system_id); + assert(pass.system_callers[3].system_id == TestSystem4.system_id); + assert(pass.system_callers[4].system_id == TestSystem5.system_id); + assert(pass.system_callers[0].dependencies.length == 0); + assert(pass.system_callers[1].dependencies.length == 1); + assert(pass.system_callers[2].dependencies.length == 1); + assert(pass.system_callers[3].dependencies.length == 3); + assert(pass.system_callers[4].dependencies.length == 1); + assert(pass.system_callers[1].dependencies[0].system_id == TestSystem.system_id); + assert(pass.system_callers[2].dependencies[0].system_id == TestSystem2.system_id); + assert(pass.system_callers[3].dependencies[0].system_id == TestSystem.system_id); + assert(pass.system_callers[3].dependencies[1].system_id == TestSystem2.system_id); + assert(pass.system_callers[3].dependencies[2].system_id == TestSystem3.system_id); + assert(pass.system_callers[4].dependencies[0].system_id == TestSystem4.system_id); +} + +@("ExternalSystemDependencies") +unittest +{ + enum TestDependency = "TestDepencency"; + + struct TestSystem + { + mixin ECS.System; + + mixin ECS.ReadOnlyDependencies!(TestDependency); + + struct EntitiesData + { + uint length; + @readonly CInt[] int_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + struct TestSystem2 + { + mixin ECS.System; + + mixin ECS.WritableDependencies!(TestDependency); + + struct EntitiesData + { + uint length; + @readonly CInt[] int_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + struct TestSystem3 + { + mixin ECS.System; + + mixin ECS.ReadOnlyDependencies!(TestDependency); + + struct EntitiesData + { + uint thread_id; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + struct TestSystem4 + { + mixin ECS.System; + + mixin ECS.WritableDependencies!(TestDependency); + + struct EntitiesData + { + uint length; + @readonly CInt[] int_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + struct TestSystem5 + { + mixin ECS.System; + + mixin ECS.ReadOnlyDependencies!(TestDependency); + + struct EntitiesData + { + uint length; + @readonly CLong[] int_; + } + + void onUpdate(EntitiesData entities) + { + + } + } + + void func1(TestSystem.EntitiesData entities) + { + foreach(i;0 .. entities.length) + { + entities.int_[i] += entities.int_[i] / 2; + } + } + + void func2(TestSystem.EntitiesData entities) + { + foreach(i;0 .. entities.length) + { + entities.int_[i] += 8; + } + } + + gEM.beginRegister(); + + gEM.registerDependency(TestDependency); + + gEM.registerSystem!TestSystem(0); + gEM.registerSystem!TestSystem2(1); + gEM.registerSystem!TestSystem3(2); + gEM.registerSystem!TestSystem4(3); + gEM.registerSystem!TestSystem5(4); + + gEM.endRegister(); + + const (EntityManager.UpdatePass)* pass = gEM.getPass("update"); + assert(pass != null); + assert(pass.system_callers.length == 5); + assert(pass.system_callers[0].system_id == TestSystem.system_id); + assert(pass.system_callers[1].system_id == TestSystem2.system_id); + assert(pass.system_callers[2].system_id == TestSystem3.system_id); + assert(pass.system_callers[3].system_id == TestSystem4.system_id); + assert(pass.system_callers[4].system_id == TestSystem5.system_id); + assert(pass.system_callers[0].dependencies.length == 0); + assert(pass.system_callers[1].dependencies.length == 1); + assert(pass.system_callers[2].dependencies.length == 1); + assert(pass.system_callers[3].dependencies.length == 3); + assert(pass.system_callers[4].dependencies.length == 2); + assert(pass.system_callers[1].dependencies[0].system_id == TestSystem.system_id); + assert(pass.system_callers[2].dependencies[0].system_id == TestSystem2.system_id); + assert(pass.system_callers[3].dependencies[0].system_id == TestSystem.system_id); + assert(pass.system_callers[3].dependencies[1].system_id == TestSystem2.system_id); + assert(pass.system_callers[3].dependencies[2].system_id == TestSystem3.system_id); + assert(pass.system_callers[4].dependencies[0].system_id == TestSystem2.system_id); + assert(pass.system_callers[4].dependencies[1].system_id == TestSystem4.system_id); } \ No newline at end of file