From 3c1c67efd0ebf6d501e7dfc229f172db3e984776 Mon Sep 17 00:00:00 2001 From: Mergul Date: Sun, 3 Jan 2021 13:05:48 +0100 Subject: [PATCH 1/4] -added filterEntity callback (used to filter EntityInfos for system, better control than simply @optional) -removed some redundant code (two times same code) -added some common functions --- source/bubel/ecs/entity.d | 9 ++++ source/bubel/ecs/manager.d | 63 +++++++++++++--------- source/bubel/ecs/system.d | 2 + tests/basic.d | 106 ++++++++++++++++++++++++++++++++++++- 4 files changed, 155 insertions(+), 25 deletions(-) diff --git a/source/bubel/ecs/entity.d b/source/bubel/ecs/entity.d index f713c78..72131b5 100644 --- a/source/bubel/ecs/entity.d +++ b/source/bubel/ecs/entity.d @@ -128,6 +128,15 @@ export struct EntityTemplate if(T.component_id >= info.tmpl_deltas.length || info.tmpl_deltas[T.component_id] == ushort.max)return null; return cast(T*)(entity_data.ptr + info.tmpl_deltas[T.component_id]); } + + /************************************************************************************************************************ + 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 + { + 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/manager.d b/source/bubel/ecs/manager.d index 1514cbc..e82add3 100644 --- a/source/bubel/ecs/manager.d +++ b/source/bubel/ecs/manager.d @@ -1110,6 +1110,32 @@ export struct EntityManager } } + static void catchEntityFilterFunction(string func_name, RetType = void)(void** member) + { + static if (hasMember!(Sys, func_name)) + { + foreach (func; __traits(getOverloads, Sys, func_name)) + { + static if ((Parameters!(func)).length == 1 + && is(Parameters!(func)[0] == EntityInfo*) + && is(ReturnType!(func) == RetType)) + { + static RetType callFunc(void* system_pointer, EntityInfo* info) + { + Sys* s = cast(Sys*) system_pointer; + static if (is(RetTyp == void)) + mixin("s." ~ func_name ~ "(info)"); + else + return mixin("s." ~ func_name ~ "(info)"); + } + + *member = cast(void*)&callFunc; + break; + } + } + } + } + catchFunction!("onEnable")(&system.m_enable); catchFunction!("onDisable")(&system.m_disable); catchFunction!("onCreate")(&system.m_create); @@ -1121,6 +1147,8 @@ export struct EntityManager catchEntityFunction!("onRemoveEntity")(&system.m_remove_entity); catchEntityFunction!("onChangeEntity")(&system.m_change_entity); + catchEntityFilterFunction!("filterEntity", bool)(&system.m_filter_entity); + system.m_system_pointer = cast(void*) Mallocator.make!Sys; system.m_priority = priority; //(cast(Sys*) system.m_system_pointer).__ecsInitialize(); @@ -2035,6 +2063,9 @@ export struct EntityManager is_: } + ///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; + entity.systems[system_id] = true; } @@ -2077,30 +2108,8 @@ export struct EntityManager { System* system = &systems[system_id]; - if (system.m_excluded_components) - { - foreach (id; system.m_excluded_components) - { - foreach (id2; info.components) - { - if (id == id2) - return; - } - } - } - - foreach (id; system.m_components) - { - foreach (i2, id2; info.components) - { - if (id2 == id) - goto is_; - } - return; - is_: - } - - info.systems[system_id] = true; + connectListenerToEntityInfo(info, system_id); + if(!info.systems[system_id])return; uint index = 0; for (; index < passes[system.m_pass].system_callers.length; index++) @@ -3483,6 +3492,12 @@ export struct EntityManager return new_info; } + export bool hasComponent(ushort component_id) + { + if(component_id >= deltas.length || !deltas[component_id])return false; + return true; + } + export ~this() @nogc nothrow { if (components) diff --git a/source/bubel/ecs/system.d b/source/bubel/ecs/system.d index cd8d2e3..e571e8b 100644 --- a/source/bubel/ecs/system.d +++ b/source/bubel/ecs/system.d @@ -194,6 +194,8 @@ package: void* m_remove_entity; void* m_change_entity; + void* m_filter_entity; + //void function(ref EntityManager.CallData data) m_initialize; //void function(ref EntityManager.CallData data) m_deinitilize; void* m_initialize; diff --git a/tests/basic.d b/tests/basic.d index 59f88e2..4a9dc6f 100644 --- a/tests/basic.d +++ b/tests/basic.d @@ -139,6 +139,7 @@ void afterEveryTest() unittest { EntityTemplate* tmpl_ = gEM.allocateTemplate([CInt.component_id, CFloat.component_id, CFlag.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_); Entity* entity = gEM.addEntity(tmpl_); EntityMeta meta = entity.getMeta(); assert(meta.hasComponent(CInt.component_id)); @@ -1615,4 +1616,107 @@ unittest 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 +} + + +@("CustomFilter") +unittest +{ + struct TestSystem + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + @optional CInt[] int_; + @optional CLong[] long_; + @optional CFloat[] float_; + @optional CDouble[] double_; + } + + bool filterEntity(EntityManager.EntityInfo* info) + { + if(!info.hasComponent(CInt.component_id))return false; + int one_from = 0; + if(info.hasComponent(CLong.component_id))one_from++; + if(info.hasComponent(CFloat.component_id))one_from++; + if(info.hasComponent(CDouble.component_id))one_from++; + if(one_from == 1)return true; + return false; + } + + void onUpdate(EntitiesData entities) + { + updates++; + } + + uint updates = 0; + } + + struct TestSystem2 + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + @optional CInt[] int_; + @optional CLong[] long_; + @optional CFloat[] float_; + @optional CDouble[] double_; + } + + bool filterEntity(EntityManager.EntityInfo* info) + { + if(info.hasComponent(CInt.component_id) && info.hasComponent(CFloat.component_id) && !info.hasComponent(CLong.component_id) && !info.hasComponent(CDouble.component_id))return true; + if(info.hasComponent(CLong.component_id) && info.hasComponent(CDouble.component_id) && !info.hasComponent(CInt.component_id) && !info.hasComponent(CFloat.component_id))return true; + return false; + } + + void onUpdate(EntitiesData entities) + { + updates++; + } + + uint updates = 0; + } + + gEM.beginRegister(); + + gEM.registerSystem!TestSystem(0); + gEM.registerSystem!TestSystem2(1); + + gEM.endRegister(); + + + EntityTemplate* tmpl_ = gEM.allocateTemplate([CInt.component_id, CLong.component_id, CFloat.component_id, CDouble.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_); + EntityTemplate* tmpl_2 = gEM.allocateTemplate([CInt.component_id, CFloat.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_2); + EntityTemplate* tmpl_3 = gEM.allocateTemplate([CLong.component_id, CDouble.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_3); + EntityTemplate* tmpl_4 = gEM.allocateTemplate([CInt.component_id, CLong.component_id, CDouble.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_4); + EntityTemplate* tmpl_5 = gEM.allocateTemplate([CInt.component_id, CDouble.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_5); + EntityTemplate* tmpl_6 = gEM.allocateTemplate([CDouble.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_6); + + gEM.addEntity(tmpl_); + gEM.addEntity(tmpl_2); + gEM.addEntity(tmpl_3); + gEM.addEntity(tmpl_4); + gEM.addEntity(tmpl_5); + gEM.addEntity(tmpl_6); + + TestSystem* test_system = gEM.getSystem!TestSystem; + TestSystem2* test_system2 = gEM.getSystem!TestSystem2; + + gEM.begin(); + gEM.update(); + gEM.end(); + + assert(test_system.updates == 2); + assert(test_system2.updates == 2); +} From 84ba5f9eb57d85b48c50ce46df87d64c43ad55f2 Mon Sep 17 00:00:00 2001 From: Mergul Date: Sun, 3 Jan 2021 13:08:29 +0100 Subject: [PATCH 2/4] -fixed emscripten compilation --- .../android/bindbc/loader/sharedlib.d | 53 ------------------- demos/external/sources/cimgui/cimgui.d | 10 +++- .../wasm_imports/bindbc/sdl/bind/sdllog.d | 6 ++- demos/source/demos/brick_breaker.d | 2 + demos/utils/source/ecs_utils/gfx/renderer.d | 30 +++++------ demos/utils/source/ecs_utils/utils.d | 6 ++- 6 files changed, 36 insertions(+), 71 deletions(-) diff --git a/demos/external/android/bindbc/loader/sharedlib.d b/demos/external/android/bindbc/loader/sharedlib.d index ab6c444..ca3c654 100644 --- a/demos/external/android/bindbc/loader/sharedlib.d +++ b/demos/external/android/bindbc/loader/sharedlib.d @@ -213,8 +213,6 @@ void addErr(const(char)* errstr, const(char)* message) version(Windows) { import core.sys.windows.windows; - extern(Windows) @nogc nothrow alias pSetDLLDirectory = BOOL function(const(char)*); - pSetDLLDirectory setDLLDirectory; void* loadLib(const(char)* name) { @@ -255,57 +253,6 @@ version(Windows) } else strncpy(buf, "Unknown Error\0", len); } - - /** - Adds a path to the default search path on Windows, replacing the path set in a previous - call to the same function. - - Any path added to this function will be added to the default DLL search path as documented at - https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setdlldirectoryw. - - Generally, when loading DLLs on a path that is not on the search path, e.g., from a subdirectory - of the application, the path should be prepended to the DLL name passed to the load function, - e.g., "dlls\\SDL2.dll". If `setCustomLoaderSearchPath(".\\dlls")` is called first, then the subdirectory - will become part of the DLL search path and the path may be omitted from the load function. (Be - aware that ".\\dlls" is relative to the current working directory, which may not be the application - directory, so the path should be built appropriately.) - - Some DLLs may depend on other DLLs, perhaps even attempting to load them dynamically at run time - (e.g., SDL2_image only loads dependencies such as libpng if it is initialized at run time with - PNG support). In this case, if the DLL and its dependencies are placed in a subdirectory and - loaded as e.g., "dlls\\SDL2_image.dll", then it will not be able to find its dependencies; the - system loader will look for them on the regular DLL search path. When that happens, the solution - is to call `setCustomLoaderSearchPath` with the subdirectory before initializing the library. - - Calling this function with `null` as the argument will reset the default search path. - - When the function returns `false`, the relevant `ErrorInfo` is added to the global error list and can - be retrieved by looping through the array returned by the `errors` function. - - When placing DLLs in a subdirectory of the application, it should be considered good practice to - call `setCustomLoaderSearchPath` to ensure all DLLs load properly. It should also be considered good - practice to reset the default search path once all DLLs are loaded. - - This function is only available on Windows, so any usage of it should be preceded with - `version(Windows)`. - - Params: - path = the path to add to the DLL search path, or `null` to reset the default. - - Returns: - `true` if the path was successfully added to the DLL search path, otherwise `false`. - */ - public - bool setCustomLoaderSearchPath(const(char)* path) - { - if(!setDLLDirectory) { - auto lib = load("Kernel32.dll"); - if(lib == invalidHandle) return false; - lib.bindSymbol(cast(void**)&setDLLDirectory, "SetDllDirectoryA"); - if(!setDLLDirectory) return false; - } - return setDLLDirectory(path) != 0; - } } else version(Posix) { import core.sys.posix.dlfcn; diff --git a/demos/external/sources/cimgui/cimgui.d b/demos/external/sources/cimgui/cimgui.d index 507f616..1fc119f 100644 --- a/demos/external/sources/cimgui/cimgui.d +++ b/demos/external/sources/cimgui/cimgui.d @@ -2,9 +2,17 @@ //based on imgui.h file version "1.73" from Dear ImGui https://github.com/ocornut/imgui module cimgui.cimgui; -import core.stdc.stdarg; +// import core.stdc.stdarg; //import core.stdc.stdio; +version(WebAssembly) +{ + alias va_list = char*; + pragma(LDC_va_start) + void va_start(T)(out va_list ap, ref T parmn) @nogc; +} +else import core.stdc.stdarg; + extern (C): //alias ImU64 = ulong; diff --git a/demos/external/wasm_imports/bindbc/sdl/bind/sdllog.d b/demos/external/wasm_imports/bindbc/sdl/bind/sdllog.d index 3ff5b4c..36bf76d 100644 --- a/demos/external/wasm_imports/bindbc/sdl/bind/sdllog.d +++ b/demos/external/wasm_imports/bindbc/sdl/bind/sdllog.d @@ -6,7 +6,11 @@ module bindbc.sdl.bind.sdllog; -import core.stdc.stdarg : va_list; +version(WebAssembly) +{ + alias va_list = char*; +} +else import core.stdc.stdarg : va_list; import bindbc.sdl.config; enum SDL_MAX_LOG_MESSAGE = 4096; diff --git a/demos/source/demos/brick_breaker.d b/demos/source/demos/brick_breaker.d index c15c503..a170076 100644 --- a/demos/source/demos/brick_breaker.d +++ b/demos/source/demos/brick_breaker.d @@ -416,6 +416,8 @@ void brickBreakerStart() launcher.gui_manager.addComponent(CInput(), "Velocity"); launcher.gui_manager.addComponent(CDamping(), "Damping"); launcher.gui_manager.addComponent(CBall(), "Ball"); + launcher.gui_manager.addComponent(CBVH(), "BVH"); + launcher.gui_manager.addComponent(CAABB(), "AABB"); launcher.gui_manager.addSystem(MoveSystem.system_id, "Move System"); launcher.gui_manager.addSystem(EdgeCollisionSystem.system_id, "Edge Collision System"); diff --git a/demos/utils/source/ecs_utils/gfx/renderer.d b/demos/utils/source/ecs_utils/gfx/renderer.d index ef53543..b2091cf 100644 --- a/demos/utils/source/ecs_utils/gfx/renderer.d +++ b/demos/utils/source/ecs_utils/gfx/renderer.d @@ -187,6 +187,20 @@ struct Renderer } } + struct DrawData + { + Texture texture; + vec2 position; + vec2 size; + vec4 coords; + short depth = 0; + uint color = uint.max; + float angle = 0; + uint material_id = 0; + uint mesh_id = 0; + uint thread_id = 0; + } + struct Thread { //Vector!VertexBlock block; @@ -430,20 +444,6 @@ struct Renderer } - struct DrawData - { - Texture texture; - vec2 position; - vec2 size; - vec4 coords; - short depth = 0; - uint color = uint.max; - float angle = 0; - uint material_id = 0; - uint mesh_id = 0; - uint thread_id = 0; - } - //void draw(Texture tex, vec2 pos, vec2 size, vec4 coords, short depth = 0, uint color = uint.max, float angle = 0, uint material_id = 0, uint mesh_id = 0, uint thread_id = 0) void draw(scope ref const(DrawData) data) { @@ -570,7 +570,7 @@ struct Renderer memcpy(ptr+16,pos.data.ptr,8); memcpy(ptr+32,coords.data.ptr,16);*/ - short[] verts = cast(short[])block.batch_vertices; + short[] verts = (cast(short*)block.batch_vertices.ptr)[0..block.batch_vertices.length>>1]; uint item_id = block.items; uint mesh_id = data.mesh_id; diff --git a/demos/utils/source/ecs_utils/utils.d b/demos/utils/source/ecs_utils/utils.d index 5073d4e..1896856 100644 --- a/demos/utils/source/ecs_utils/utils.d +++ b/demos/utils/source/ecs_utils/utils.d @@ -77,7 +77,11 @@ version(GNU) else { extern(C) int printf(scope const char* format, ...) @nogc nothrow @system; - public import std.array : staticArray; + // public import std.array : staticArray; + pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a) + { + return a; + } } extern(C) int rand() nothrow @nogc @trusted; From edaa2286f436380e3cb3d244523268485b2bda12 Mon Sep 17 00:00:00 2001 From: Mergul Date: Sat, 9 Jan 2021 14:30:56 +0100 Subject: [PATCH 3/4] -fix some memory leaks in unittests -change name in dub.json ecs->bubel_ecs -add dependeny in meson.build --- dub.json | 2 +- meson.build | 2 ++ tests/basic.d | 4 ++++ tests/tests.d | 1 + 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/dub.json b/dub.json index ba935ee..4eca570 100755 --- a/dub.json +++ b/dub.json @@ -1,5 +1,5 @@ { - "name": "ecs", + "name": "bubel_ecs", "authors": [ "Michał Masiukiewicz", "Dawid Masiukiewicz" ], diff --git a/meson.build b/meson.build index 2a71fc7..4951584 100644 --- a/meson.build +++ b/meson.build @@ -67,6 +67,8 @@ ecs_lib = library('ecs', src, include_directories : [tests_inc, inc], d_args: ar executable('tests', tests_src, include_directories : [tests_inc, inc], d_args: args, link_args: link_args, link_with: ecs_lib) +bubel_ecs_dep = declare_dependency(include_directories : [inc], link_with : ecs_lib) + if BuildDemos_opt subdir('demos/utils') subdir('demos') diff --git a/tests/basic.d b/tests/basic.d index 4a9dc6f..bcea918 100644 --- a/tests/basic.d +++ b/tests/basic.d @@ -158,6 +158,7 @@ unittest unittest { EntityTemplate* tmpl_ = gEM.allocateTemplate([CInt.component_id, CFloat.component_id, CFlag.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_); assert(tmpl_.info.components.length == 3); assert(tmpl_.info.size == (CInt.sizeof + CFloat.sizeof + EntityID.sizeof)); assert(tmpl_.getComponent!CInt); @@ -346,6 +347,7 @@ unittest assert(*tmpl_7.getComponent!CLong == 10); gEM.freeTemplate(tmpl_d); + gEM.freeTemplate(tmpl_cp); gEM.freeTemplate(tmpl_); gEM.freeTemplate(tmpl_2); gEM.freeTemplate(tmpl_3); @@ -369,6 +371,8 @@ unittest assert(*tmpl_.getComponent!CInt == 1); assert(*tmpl_.getComponent!CFloat == 2.0); assert(tmpl_.info == tmpl_2.info); + gEM.freeTemplate(tmpl_); + gEM.freeTemplate(tmpl_2); } @("MultiRegister") diff --git a/tests/tests.d b/tests/tests.d index 9f69987..d539f89 100644 --- a/tests/tests.d +++ b/tests/tests.d @@ -1014,6 +1014,7 @@ else: writeEntityComponents(gEM.getEntity(entity)); //import std.stdio; ////writeln((cast(uint*)tmpl.info.first_block)[0..48]); + gEM.freeTemplate(tmpl_empty); gEM.freeTemplate(tmpl); gEM.freeTemplate(tmpl2); gEM.freeTemplate(copy_tempalte); From 9cef882faf3cd182af49893a50dfc0cfb7ee220f Mon Sep 17 00:00:00 2001 From: Mergul Date: Sat, 9 Jan 2021 15:13:53 +0100 Subject: [PATCH 4/4] -change targetName to "ecs" in order to fix CI guild (need to be renamed in CI in future) --- dub.json | 1 + 1 file changed, 1 insertion(+) diff --git a/dub.json b/dub.json index 4eca570..1a99040 100755 --- a/dub.json +++ b/dub.json @@ -1,5 +1,6 @@ { "name": "bubel_ecs", + "targetName" : "ecs", "authors": [ "Michał Masiukiewicz", "Dawid Masiukiewicz" ],