Add support for external depencencies

This commit is contained in:
Mergul 2020-05-12 17:28:31 +02:00
parent b19fbb1528
commit 5e884352ba
5 changed files with 440 additions and 5 deletions

View file

@ -92,4 +92,20 @@ static struct ECS
{ {
alias ExcludedComponents = T; 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;
}
} }

View file

@ -452,6 +452,8 @@ export struct EntityManager
uint excluded = 1; uint excluded = 1;
uint optional = 1; uint optional = 1;
uint req = 1; uint req = 1;
uint readonly_dep = 1;
uint writable_dep = 1;
} }
static ComponentsCounts getComponentsCounts()() static ComponentsCounts getComponentsCounts()()
@ -532,7 +534,22 @@ export struct EntityManager
{ {
components_counts.excluded++; 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]; 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) void addReadonly(CompInfo info)
{ {
m_readonly[m_readonly_counter++] = info; m_readonly[m_readonly_counter++] = info;
@ -593,17 +620,31 @@ export struct EntityManager
m_req[m_req_counter++] = 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.readonly] m_readonly;
CompInfo[counts.mutable] m_mutable; CompInfo[counts.mutable] m_mutable;
CompInfo[counts.excluded] m_excluded; CompInfo[counts.excluded] m_excluded;
CompInfo[counts.optional] m_optional; CompInfo[counts.optional] m_optional;
CompInfo[counts.req] m_req; 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_readonly_counter;
uint m_mutable_counter; uint m_mutable_counter;
uint m_excluded_counter; uint m_excluded_counter;
uint m_optional_counter; uint m_optional_counter;
uint m_req_counter; uint m_req_counter;
uint m_readonly_dep_counter;
uint m_writable_dep_counter;
string entites_array; string entites_array;
} }
@ -616,6 +657,8 @@ export struct EntityManager
size_t excluded = components_info.excluded.length; size_t excluded = components_info.excluded.length;
size_t read_only = components_info.readonly.length; size_t read_only = components_info.readonly.length;
size_t modified = components_info.mutable.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) if (req > 0)
system.m_components = Mallocator.makeArray!ushort(req); system.m_components = Mallocator.makeArray!ushort(req);
@ -627,6 +670,10 @@ export struct EntityManager
system.m_read_only_components = Mallocator.makeArray!ushort(read_only); system.m_read_only_components = Mallocator.makeArray!ushort(read_only);
if (modified > 0) if (modified > 0)
system.m_modified_components = Mallocator.makeArray!ushort(modified); 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; return components_info;
} }
@ -809,7 +872,6 @@ export struct EntityManager
~ "\" due to non existing component \"" ~ comp_info.type ~ "\"."); ~ "\" due to non existing component \"" ~ comp_info.type ~ "\".");
system.m_modified_components[iii] = comp; system.m_modified_components[iii] = comp;
} }
} }
static void fillInputData(ref Sys.EntitiesData input_data, EntityInfo* info, static void fillInputData(ref Sys.EntitiesData input_data, EntityInfo* info,
@ -1059,6 +1121,32 @@ export struct EntityManager
genCompList(system, components_map); 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); ushort sys_id = systems_map.get(cast(char[]) Sys.stringof, ushort.max);
if (sys_id < systems.length) if (sys_id < systems.length)
{ {
@ -1118,6 +1206,11 @@ export struct EntityManager
return cast(ushort)(passes.length - 1); 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. Register component into EntityManager.
*/ */
@ -2820,10 +2913,45 @@ export struct EntityManager
foreach (caller; pass.system_callers) foreach (caller; pass.system_callers)
{ {
index = 0; index = 0;
///gets systems which are excluding each other
out_for: foreach (caller2; pass.system_callers) out_for: foreach (caller2; pass.system_callers)
{ {
if (caller is caller2) if (caller is caller2)
continue; 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 (cmp; caller.system.m_read_only_components)
{ {
foreach (cmp2; caller2.system.m_modified_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; Component info;
*/ */
@ -3350,6 +3490,7 @@ export struct EntityManager
HashMap!(char[], ushort) components_map; HashMap!(char[], ushort) components_map;
HashMap!(const(char)[], ushort) events_map; HashMap!(const(char)[], ushort) events_map;
HashMap!(const(char)[], ushort) passes_map; HashMap!(const(char)[], ushort) passes_map;
HashMap!(const(char)[], ushort) external_dependencies_map;
Vector!System systems; Vector!System systems;
Vector!ComponentInfo components; Vector!ComponentInfo components;
Vector!EventInfo events; Vector!EventInfo events;

View file

@ -129,6 +129,9 @@ package:
ushort[] m_read_only_components; ushort[] m_read_only_components;
ushort[] m_modified_components; ushort[] m_modified_components;
ushort[] m_readonly_dependencies;
ushort[] m_writable_dependencies;
EntityManager.SystemCaller* m_any_system_caller; EntityManager.SystemCaller* m_any_system_caller;
EventCaller[] m_event_callers; EventCaller[] m_event_callers;

View file

@ -62,11 +62,11 @@ public:
used = 0; used = 0;
} }
export bool empty() { export bool empty() const {
return (used == 0); return (used == 0);
} }
export size_t length() { export size_t length() const {
return used; return used;
} }
@ -197,9 +197,9 @@ public:
assert(ok, "There is no such an element in vector"); 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]"); //debug assert(elemNum < used, "Range violation [index]");
return array.ptr[elemNum]; return *cast(T*)&array.ptr[elemNum];
} }
export auto opSlice() { export auto opSlice() {

View file

@ -1221,3 +1221,278 @@ unittest
gEM.end(); 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);
}