diff --git a/.gitignore b/.gitignore index 8a5c5c8..e595637 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ !README.md !./dub.json !.gitignore +!codecov.yml +!skeleton.html !meson.build !meson_options.txt !compile_wasm.py \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 25b0820..14ba23c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,9 +1,91 @@ -test_dmd: - stage: test - image: mmcomando/ecs_enviroment +stages: + - build + - test + - testcov + - build_emscripten + - deploy + +build_code: + stage: build + image: "registry.gitlab.com/mergul/bubel-ecs:latest" script: - - source $(/script/dlang/install.sh dmd -a) && dmd --version - - source $(/script/dlang/install.sh dmd -a) && dub -c unittest-runner -b unittest + - mkdir build + - /bin/bash /compile_ecs.sh + - cp artifacts/* build/ + - cp -r public build/ + artifacts: + expire_in: 1h + paths: + - build + rules: + - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"' + when: always + - when: always + allow_failure: true + +test_dmd_debug: + stage: test + image: frolvlad/alpine-glibc + script: + - build/dmd_debug_unittest artifacts: reports: junit: test_report.xml +test_dmd: + stage: test + image: frolvlad/alpine-glibc + script: + - build/dmd_release_unittest + artifacts: + reports: + junit: test_report.xml +test_dmd_betterC: + stage: test + image: frolvlad/alpine-glibc + script: + - build/dmd_release_unittest_bc + artifacts: + reports: + junit: test_report.xml + +coverage_test_dmd: + stage: testcov + image: "registry.gitlab.com/mergul/bubel-ecs/curl:latest" + needs: ["test_dmd_debug", "build_code"] + dependencies: + - build_code + script: + - mkdir reports + - build/dmd_unittest_cov + after_script: + - bash <(curl -s https://codecov.io/bash) -s reports -t 1a0c0169-a721-4085-8252-fed4755dcd8c + +emscripten: + stage: build_emscripten + image: "registry.gitlab.com/mergul/bubel-ecs/emscripten:latest" + dependencies: + - build_code + script: + - /bin/bash /build.sh + rules: + - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"' + when: always + artifacts: + expire_in: 1h + paths: + - wasm + +pages: + stage: deploy + image: frolvlad/alpine-glibc + script: + - mkdir public + - cp -r wasm/* public/ + - cp -r build/public/* public/ + rules: + - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"' + when: always + artifacts: + expire_in: 1h + paths: + - public \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ede0cb3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2020, Dawid Masiukiewicz +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 08cab68..d3f6816 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # Dynamic Entity Component System +[![pipeline status](https://gitlab.com/Mergul/bubel-ecs/badges/master/pipeline.svg)](https://gitlab.com/Mergul/bubel-ecs/-/commits/master) +[![codecov](https://codecov.io/gl/Mergul/bubel-ecs/branch/master/graph/badge.svg?token=Unm0TJhFoW)](https://codecov.io/gl/Mergul/bubel-ecs) -Entity-Component-System implementation in D language. \ No newline at end of file +Entity-Component-System implementation in D language. diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..a6afdbc --- /dev/null +++ b/codecov.yml @@ -0,0 +1,3 @@ +ignore: + - "tests/*" + - "**/traits*" \ No newline at end of file diff --git a/demos/.gitignore b/demos/.gitignore index 30c9e9c..1ac2a4a 100644 --- a/demos/.gitignore +++ b/demos/.gitignore @@ -12,4 +12,6 @@ !.gitignore !compile_wasm.py !cimgui.bc +!emscripten_shell.html +!emscripten_multi_shell.html .dub \ No newline at end of file diff --git a/demos/compile_wasm.py b/demos/compile_wasm.py index b1f3a7e..06d31ac 100644 --- a/demos/compile_wasm.py +++ b/demos/compile_wasm.py @@ -1,6 +1,7 @@ import os import ntpath import sys +import imp def compile(sources, output): files = [] @@ -36,8 +37,10 @@ compiler = 'ldc2 ' shared_flags = '' clean = 0 demo = 0 +only_bc = 0 +multi = 0 sources = ['tests', 'source'] -emc_flags = '-s USE_SDL=2 -s USE_SDL_IMAGE=2 -s SDL2_IMAGE_FORMATS="[\'png\']" --preload-file assets ' +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 ' import_paths = ['external/sources', 'external/imports', 'external/wasm_imports', '../source', 'utils/source', 'simple/source'] @@ -60,6 +63,8 @@ for arg in sys.argv[1:]: shared_flags += '-Oz ' elif(arg == '-g'): shared_flags += '-g ' + elif(arg == '--multi'): + multi = 1 elif(arg == '-g4'): ldc_flags += '-g ' emc_flags += '-g4 --source-map-base ./ ' @@ -79,6 +84,8 @@ for arg in sys.argv[1:]: emc_flags += '-s USE_PTHREADS=1 ' elif(arg == '--demo=simple'): demo = 0 + elif(arg == '--only-bc'): + only_bc = 1 else: print('unknown argument: ' + arg) exit() @@ -92,8 +99,17 @@ compile(['source'], 'demo.bc') if clean or os.path.exists('../ecs.bc') == 0 or os.path.isfile('../ecs.bc') == 0: compile(['../source'], '../ecs.bc') -emcc_cmd = 'emcc -v ' + shared_flags + emc_flags + '-s ERROR_ON_UNDEFINED_SYMBOLS=0 -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 -s ALLOW_MEMORY_GROWTH=1 -s MINIFY_HTML=0 -s WASM_MEM_MAX=2048MB -s MALLOC=dlmalloc -s WASM=1 -o ecs_demo.html --shell-file emscripten_shell.html ' +if only_bc: + exit() + +if multi: + emcc_cmd = 'emcc ' + shared_flags + emc_flags + '--pre-js build/assets.js -s FORCE_FILESYSTEM=1 -s MAX_WEBGL_VERSION=2 --emrun -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 -s ALLOW_MEMORY_GROWTH=1 -s WASM_MEM_MAX=2048MB -s MALLOC=dlmalloc -s WASM=1 {0} -o {1} --shell-file emscripten_multi_shell.html ' +else: + emcc_cmd = 'emcc ' + shared_flags + emc_flags + '--pre-js build/assets.js -s FORCE_FILESYSTEM=1 -s MAX_WEBGL_VERSION=2 --emrun -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 -s ALLOW_MEMORY_GROWTH=1 -s WASM_MEM_MAX=2048MB -s MALLOC=dlmalloc -s WASM=1 -o build/ecs_demo.html --shell-file emscripten_shell.html ' + +#emcc_cmd = 'emcc -v ' + shared_flags + emc_flags + '-s MAX_WEBGL_VERSION=2 --emrun -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 -s ALLOW_MEMORY_GROWTH=1 -s WASM_MEM_MAX=2048MB -s MALLOC=dlmalloc -s WASM=1 -o build/ecs_demo.html --shell-file emscripten_shell.html ' #-s ALLOW_MEMORY_GROWTH=1 -s PROXY_TO_PTHREAD=1 -Wl,--no-check-features -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s TOTAL_MEMORY=512MB +#-s MAX_WEBGL_VERSION=2 emcc_cmd += '../ecs.bc ' emcc_cmd += 'utils.bc ' @@ -103,6 +119,24 @@ emcc_cmd += 'cimgui.bc ' emcc_cmd += 'mmutils.bc ' emcc_cmd += 'demo.bc ' -print(emcc_cmd) +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' +print('Packafing files: ' + pack_cmd) + +os.system(pack_cmd) + +if multi: + final_cmd = emcc_cmd.format('','build/ecs_demo.html') + print(final_cmd) + os.system(final_cmd) + final_cmd = emcc_cmd.format('-s USE_PTHREADS=1','build/ecs_demo_mt.js') + print(final_cmd) + os.system(final_cmd) +else: + print(emcc_cmd) + os.system(emcc_cmd) + +os.system('rm build/assets.js') -os.system(emcc_cmd) diff --git a/demos/emscripten_multi_shell.html b/demos/emscripten_multi_shell.html new file mode 100644 index 0000000..c0b5598 --- /dev/null +++ b/demos/emscripten_multi_shell.html @@ -0,0 +1,170 @@ + + + + + + ECS Demo + + + + +
emscripten
+
Downloading...
+
+ +
+
+ +
+ + +
+ +
+ + + + diff --git a/demos/emscripten_shell.html b/demos/emscripten_shell.html new file mode 100644 index 0000000..a657766 --- /dev/null +++ b/demos/emscripten_shell.html @@ -0,0 +1,154 @@ + + + + + + ECS Demo + + + + +
emscripten
+
Downloading...
+
+ +
+
+ +
+ + +
+ +
+ + {{{ SCRIPT }}} + + diff --git a/demos/external/sources/mmutils/thread_pool.d b/demos/external/sources/mmutils/thread_pool.d index 445cd19..374fbc1 100644 --- a/demos/external/sources/mmutils/thread_pool.d +++ b/demos/external/sources/mmutils/thread_pool.d @@ -376,7 +376,8 @@ version (MM_USE_POSIX_THREADS) { threadStart = dg; int ok = pthread_create(&handle, null, &threadRunFunc, cast(void*)&this); - assert(ok == 0); + if(!ok)handle = pthread_t(); + //assert(ok == 0); } void join() @@ -461,7 +462,8 @@ else version(D_BetterC) { threadStart = dg; int ok = pthread_create(&handle, null, &threadRunFunc, cast(void*)&this); - assert(ok == 0); + if(!ok)handle = pthread_t(); + //assert(ok == 0); } void join() @@ -569,7 +571,7 @@ else version(D_BetterC) threadStart = dg; handle = cast(HANDLE) _beginthreadex( null, 0, &threadRunFunc, cast(void*)&this, 0, null ); //int ok = pthread_create(&handle, null, &threadRunFunc, cast(void*)&this); - assert(handle != null); + //assert(handle != null); } void join() diff --git a/demos/utils/source/ecs_utils/gfx/texture.d b/demos/utils/source/ecs_utils/gfx/texture.d index 4407123..bb4c62a 100644 --- a/demos/utils/source/ecs_utils/gfx/texture.d +++ b/demos/utils/source/ecs_utils/gfx/texture.d @@ -55,6 +55,7 @@ struct Texture data.data[0..$] = (cast(ubyte*)surf.pixels)[0..data.data.length]; glGenTextures(1, &data.gl_handle); + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D,data.gl_handle); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); diff --git a/dub.json b/dub.json index 1709ae0..c23a4e6 100755 --- a/dub.json +++ b/dub.json @@ -5,7 +5,7 @@ ], "description": "Dynamic Entity Component System", "copyright": "Copyright © 2018-2019, Michał Masiukiewicz, Dawid Masiukiewicz", - "license": "BSD", + "license": "BSD 3-clause", "sourcePaths" : ["source\/"], "excludedSourceFiles":[ "source\/ecs\/traits.d" diff --git a/skeleton.html b/skeleton.html new file mode 100644 index 0000000..02f57a2 --- /dev/null +++ b/skeleton.html @@ -0,0 +1,36 @@ + + + + BubelECS + + + + + + + + + + +
+
+
+ +
+ + + diff --git a/source/ecs/atomic.d b/source/ecs/atomic.d index 91cc3d4..5e5f447 100644 --- a/source/ecs/atomic.d +++ b/source/ecs/atomic.d @@ -1,8 +1,11 @@ /************************************************************************************************************************ -*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. +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 +License: BSD 3-clause, see LICENSE file in project root folder. */ module ecs.atomic; diff --git a/source/ecs/attributes.d b/source/ecs/attributes.d index 2b24178..20d0f60 100644 --- a/source/ecs/attributes.d +++ b/source/ecs/attributes.d @@ -1,17 +1,24 @@ /************************************************************************************************************************ -*This module contain attributes used to mark components. -*Currently only two attributes are supported: -* - optional: mark component as optional for system update -* - readonly: mark component access as read only (used for multithreading) -* -*By default components are required and mutable. "const" attribute can be used insteac od readonly mark. -*ex. -*Struct EntitiesData -*{ -* Comp1[] cmp; //mutable required component -* @readonly @optional Comp2[] cmp2; //optional read only component -* @optional const (Comp3)[] cmp3; //same as cmp2 -*} +This module contain attributes used to mark components. +Currently only two attributes are supported: +$(LIST + * optional: mark component as optional for system update + * readonly: mark component access as read only (used for multithreading) +) + +By default components are required and mutable. "const" attribute can be used insteac od readonly mark. + +--- +Struct EntitiesData +{ + Comp1[] cmp; //mutable required component + @readonly @optional Comp2[] cmp2; //optional read only component + @optional const (Comp3)[] cmp3; //same as cmp2 +} +--- + +Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. */ module ecs.attributes; diff --git a/source/ecs/block_allocator.d b/source/ecs/block_allocator.d index 5db4dab..d9f08ca 100644 --- a/source/ecs/block_allocator.d +++ b/source/ecs/block_allocator.d @@ -1,7 +1,10 @@ /************************************************************************************************************************ -*It's internal code. -* -*Module contain memory allocator. +It's internal code. + +Module contain memory allocator. + +Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. */ module ecs.block_allocator; @@ -9,14 +12,14 @@ import ecs.manager; import ecs.std; /************************************************************************************************************************ -*Allocator allocate large blocks and return smaller blocks. When there is no more blocks then next large block is allocated. -*By default freeing memory only returns it to allocator. To free large memory chunks freeMemory function is used. -*freeMemory function return to system memory even if chunk blocks wasn't freed. +Allocator allocate large blocks and return smaller blocks. When there is no more blocks then next large block is allocated. +By default freeing memory only returns it to allocator. To free large memory chunks freeMemory function is used. +freeMemory function return to system memory even if chunk blocks wasn't freed. */ struct BlockAllocator { /************************************************************************************************************************ - *Get new block. Allocator automatically allocate next memory chunk if needed. + Get new block. Allocator automatically allocate next memory chunk if needed. */ void* getBlock() nothrow @nogc { @@ -28,7 +31,7 @@ struct BlockAllocator } /************************************************************************************************************************ - *Return block to allocator for further use. + Return block to allocator for further use. */ void freeBlock(void* block) nothrow @nogc { @@ -37,7 +40,7 @@ struct BlockAllocator } /************************************************************************************************************************ - *Free whole used memory. This function return to system all memory chunks even if not every black was freed. + Free whole used memory. This function return to system all memory chunks even if not every black was freed. */ void freeMemory() nothrow @nogc { diff --git a/source/ecs/core.d b/source/ecs/core.d index 91c9065..c346c9f 100644 --- a/source/ecs/core.d +++ b/source/ecs/core.d @@ -1,34 +1,53 @@ /************************************************************************************************************************ -*This module contain main templates for user. -*There are three structure templates (mixins) which should be added on top of structure: -* - System: make system structure -* - Component: make component structure -* - Event: make event structure -* -*ex. -*Struct System1 -*{ -* mixin!ECS.System; -*} -* -*Struct System2 -*{ -* mixin!ECS.System(16);//set number of jobs generated for system by multithreaded update -*} -* -*Struct Component1 -*{ -* mixin!ECS.Component; -*} -* -*Struct Event1 -*{ -* mixin!ECS.Event; -*} -* -*There is also template for generating list of excluded components "ExcludedComponets(T...)". -*This template takes component structure types and making list of excluded components used in "registerSystem" function. -* +This module contain main templates for user. +There are three structure templates (mixins) which should be added on top of structure: +$(LIST + * System: make system structure + * Component: make component structure + * Event: make event structure +) + +--- +Struct System1 +{ + mixin!ECS.System; +} + +Struct System2 +{ + mixin!ECS.System(16);//set number of jobs generated for system by multithreaded update +} + +Struct Component1 +{ + mixin!ECS.Component; +} + +Struct Event1 +{ + mixin!ECS.Event; +} +--- + +There is also template for generating list of excluded components "ExcludedComponets(T...)". +This template takes component structure types and making list of excluded components used in "registerSystem" function. + +--- +Struct System1 +{ + mixin!ECS.System; + + struct EntitiesData + { + ... //used components + } + + ExcludedComponets!(Comp1, Comp2); +} +--- + +Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. */ module ecs.core; @@ -36,12 +55,12 @@ public import ecs.manager; public import ecs.entity; /************************************************************************************************************************ -*Main struct used as namespace for templates. +Main struct used as namespace for templates. */ static struct ECS { /************************************************************************************************************************ - *Mark structure as System. Should be added on top of structure (before any data). + Mark structure as System. Should be added on top of structure (before any data). */ mixin template System(uint jobs_count = 32) { @@ -50,7 +69,7 @@ static struct ECS } /************************************************************************************************************************ - *Mark structure as Component. Should be added on top of structure (before any data). + Mark structure as Component. Should be added on top of structure (before any data). */ mixin template Component() { @@ -58,7 +77,7 @@ static struct ECS } /************************************************************************************************************************ - *Mark structure as Event. Should be added on top of structure (before any data). + Mark structure as Event. Should be added on top of structure (before any data). */ mixin template Event() { @@ -67,7 +86,7 @@ static struct ECS } /************************************************************************************************************************ - *Make list of excluded components. This template get structure types as argument. Should be added inside System structure. + Make list of excluded components. This template get structure types as argument. Should be added inside System structure. */ mixin template ExcludedComponents(T...) { diff --git a/source/ecs/entity.d b/source/ecs/entity.d index f3a8d97..0bd2dea 100644 --- a/source/ecs/entity.d +++ b/source/ecs/entity.d @@ -1,5 +1,8 @@ /************************************************************************************************************************ -*Entity module. +Entity module. + +Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. */ module ecs.entity; @@ -7,7 +10,7 @@ import ecs.system; import ecs.manager; /************************************************************************************************************************ -*Entity ID structure. Used as reference to Entity. Pointer to entity should be ever used to store entity reference! +Entity ID structure. Used as reference to Entity. Pointer to entity should be ever used to store entity reference! */ struct EntityID { @@ -18,7 +21,7 @@ struct EntityID } /************************************************************************************************************************ -*Structure of Entity. It's have only ID, but have full konwlege to get to components memory (If pointer to it is proper). +Structure of Entity. It's have only ID, but have full konwlege to get to components memory (If pointer to it is proper). */ struct Entity { @@ -26,8 +29,8 @@ struct Entity EntityID id; /************************************************************************************************************************ - *Get specified component. If component doesn't exist function retun null. Pointer is valid only before next "commit()", "begin()" or "end()" - *function is called. Returned pointer shouldn't be used to store reference to entity data. + Get specified component. If component doesn't exist function retun null. Pointer is valid only before next "commit()", "begin()" or "end()" + function is called. Returned pointer shouldn't be used to store reference to entity data. */ T* getComponent(T)() const { @@ -45,13 +48,15 @@ struct Entity } /************************************************************************************************************************ -*Entity template structure. -*Entity contain whole information needed to create new entity. Allocating EntityTemplate is considered as more expensive operation -*than adding entity. Whole components memory is stored in EntityTemplate and is copyied to newly added entity. -*If you want to place several entity with small difference in data then you should take pointer to component and change it before every -*entity addition. -*There is no restriction about number of allocated templates. Single template can be used from multiple threads, but if you -*want to changes some components data before add entity (entity position for example) it's better to use multiple templates. +Entity template structure. + +Entity contain whole information needed to create new entity. Allocating EntityTemplate is considered as more expensive operation +than adding entity. Whole components memory is stored in EntityTemplate and is copyied to newly added entity. +If you want to place several entity with small difference in data then you should take pointer to component and change it before every +entity addition. + +There is no restriction about number of allocated templates. Single template can be used from multiple threads, but if you +want to changes some components data before add entity (entity position for example) it's better to use multiple templates. */ export struct EntityTemplate { @@ -61,7 +66,7 @@ export struct EntityTemplate EntityManager.EntityInfo* info; /************************************************************************************************************************ - *Get specified component. If component doesn't exist function return null. Returned pointer is valid during EntityTemplate lifetime. + Get specified component. If component doesn't exist function return null. Returned pointer is valid during EntityTemplate lifetime. */ T* getComponent(T)() nothrow @nogc { diff --git a/source/ecs/events.d b/source/ecs/events.d index 317b2f0..ac7e185 100644 --- a/source/ecs/events.d +++ b/source/ecs/events.d @@ -34,7 +34,7 @@ package struct EventManager EventData* data = &events[Ev.event_id]; EventBlock* block = data.blocks[block_id]; - //EntityManager.EventInfo* info = &gEM.events[Ev.event_id]; + //EntityManager.EventInfo* info = &manager.events[Ev.event_id]; event.entity_id = id; if(block is null) @@ -119,15 +119,15 @@ package struct EventManager void allocateData(uint threads_count) nothrow @nogc { disposeData(); - events = Mallocator.makeArray!EventData(gEM.events.length); + events = Mallocator.makeArray!EventData(manager.events.length); foreach(i,ref event;events) { event.blocks = Mallocator.makeArray!(EventBlock*)(threads_count*2); event.first_blocks = Mallocator.makeArray!(EventBlock*)(threads_count*2); - event.data_offset = EventBlock.sizeof;//gEM.events[i]. - gEM.alignNum(event.data_offset, gEM.events[i].alignment); + event.data_offset = EventBlock.sizeof;//manager.events[i]. + manager.alignNum(event.data_offset, manager.events[i].alignment); - event.max_events = cast(ushort)((events_block_size - event.data_offset) / gEM.events[i].size); + event.max_events = cast(ushort)((events_block_size - event.data_offset) / manager.events[i].size); } } diff --git a/source/ecs/id_manager.d b/source/ecs/id_manager.d index 4f5a949..1ab8465 100644 --- a/source/ecs/id_manager.d +++ b/source/ecs/id_manager.d @@ -8,12 +8,12 @@ import ecs.atomic; import core.stdc.string : memcpy; /************************************************************************************************************************ -*IDManager is responsible for assignment and removing IDs. IDs are unique throughtout a whole duration of the program. +IDManager is responsible for assignment and removing IDs. IDs are unique throughtout a whole duration of the program. */ struct IDManager { /************************************************************************************************************************ - *Get new ID. + Get new ID. */ pragma(inline, false) EntityID getNewID() nothrow @nogc { @@ -74,7 +74,7 @@ struct IDManager } /************************************************************************************************************************ - *Release ID. + Release ID. */ void releaseID(EntityID id) nothrow @nogc { @@ -91,7 +91,7 @@ struct IDManager } /************************************************************************************************************************ - *Update pointer to entity. The purpose of this function is to ensure that pointer to entity is always correct. + Update pointer to entity. The purpose of this function is to ensure that pointer to entity is always correct. */ void update(ref Entity entity) nothrow @nogc { @@ -107,7 +107,7 @@ struct IDManager } /************************************************************************************************************************ - *Returns pointer to entity. + Returns pointer to entity. */ export Entity* getEntityPointer(EntityID id) nothrow @nogc { @@ -132,7 +132,7 @@ struct IDManager } /************************************************************************************************************************ - *Check if entity with specified ID exist. + Check if entity with specified ID exist. */ export bool isExist(EntityID id) nothrow @nogc { @@ -143,7 +143,7 @@ struct IDManager } /************************************************************************************************************************ - *Initialize manager. + Initialize manager. */ void initialize() nothrow @nogc { @@ -162,7 +162,7 @@ struct IDManager } /************************************************************************************************************************ - *Free manager memory. + Free manager memory. */ void deinitialize() @trusted @nogc nothrow { @@ -185,7 +185,7 @@ struct IDManager } /************************************************************************************************************************ - *Optimize memory. Must be called if any ID was added and some ID will be removed. + Optimize memory. Must be called if any ID was added and some ID will be removed. */ void optimize() nothrow @nogc { diff --git a/source/ecs/manager.d b/source/ecs/manager.d index 9f5dceb..aca7f53 100644 --- a/source/ecs/manager.d +++ b/source/ecs/manager.d @@ -1,5 +1,8 @@ /************************************************************************************************************************ -*Most important module. Almost every function is called from EntityManager. +Most important module. Almost every function is called from EntityManager. + +Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. */ module ecs.manager; @@ -28,37 +31,38 @@ export alias gEntityManager = EntityManager.instance; alias SerializeVector = ecs.vector.Vector!ubyte; /************************************************************************************************************************ -*Entity manager is responsible for everything. -* -*Entity manager can be in three states: -* - registration: time between beginRegister() and endRegister() calls. -* - update: time between being() and end() calls. -* - default: when it's not in registration or update time -* -*Manager can be only in one state simultaneously. -* -*Manager must be initialized before use. There is global instance of EntityManager: EntityManager.instance or gEM as alias. -* -*Registration process consist of registration of passes, systems, entities and events. -* -*Pass is group of system which should be used inside one update() call. Passes are added as name (string) and can be referenced by name or id.
-*System is structure responsible for update of specified group of entities. System consist of EntitiesData structure which contain components used -*by system and some callback. Main callback is onUpdate() which is called by update() entity manager function. Other callbacks are used as listeners for -*adding entites, tracking system lifetime and events handling.
-*Component is basicly small fraction of data which is considered to be used as whole. In best scenario every byte of component is used when it's refered to. -*In practice sometimes it's better to join data into one component even if it's can be accessed separetly.
-*Events are structures with data used to handle events. Event can contain for exmaple one floating point number used as damage dealt to entity.
-*Entity is group of components. In memory entity is only ID which makes it's possible to take it's components. Components are grouped into chunks, and -*grouped by component type so entity can be fracted in big memory chunk.
-* -*There is two types of update: -*
- update(): function used to call update pass. -*
- updateMT(): function used to call update pass multithreaded. This call only generates jobs which must be called by user. +Entity manager is responsible for everything. + +Entity manager can be in three states: + - registration: time between beginRegister() and endRegister() calls. + - update: time between being() and end() calls. + - default: when it's not in registration or update time + +Manager can be only in one state simultaneously. + +Manager must be initialized before use. There is global instance of EntityManager: EntityManager.instance or gEM as alias. + +Registration process consist of registration of passes, systems, entities and events. + +Pass is group of system which should be used inside one update() call. Passes are added as name (string) and can be referenced by name or id. +System is structure responsible for update of specified group of entities. System consist of EntitiesData structure which contain components used +by system and some callback. Main callback is onUpdate() which is called by update() entity manager function. Other callbacks are used as listeners for +adding entites, tracking system lifetime and events handling. + +Component is basicly small fraction of data which is considered to be used as whole. In best scenario every byte of component is used when it's refered to. +In practice sometimes it's better to join data into one component even if it's can be accessed separetly. +Events are structures with data used to handle events. Event can contain for exmaple one floating point number used as damage dealt to entity. +Entity is group of components. In memory entity is only ID which makes it's possible to take it's components. Components are grouped into chunks, and +grouped by component type so entity can be fracted in big memory chunk. + +There is two types of update: + - update(): function used to call update pass. + - updateMT(): function used to call update pass multithreaded. This call only generates jobs which must be called by user. */ export struct EntityManager { /************************************************************************************************************************ - *Initialize ECS. + Initialize ECS. */ export static void initialize(uint threads_count, uint page_size = 32768, uint block_pages_count = 128) @@ -81,7 +85,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Deinitialize and destroy ECS. This function release whole memory. + Deinitialize and destroy ECS. This function release whole memory. */ export static void destroy() { @@ -154,7 +158,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Begin registering process. Every register function should be called between beginRegister() and endRegister(). + Begin registering process. Every register function should be called between beginRegister() and endRegister(). */ export void beginRegister() nothrow @nogc { @@ -172,7 +176,7 @@ export struct EntityManager } /************************************************************************************************************************ - *End registering process. Every register function should be called between beginRegister() and endRegister(). + End registering process. Every register function should be called between beginRegister() and endRegister(). */ export void endRegister() { @@ -346,7 +350,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Same as "void registerSystem(Sys)(int priority, int pass = 0)" but use pass name instead of id. + 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) { @@ -359,13 +363,13 @@ export struct EntityManager } /************************************************************************************************************************ - *Register new System into EntityManager. This funcion generate glue between EntityManager and System. - *Systems can be registered from external dynamic library, and can be registered after adding entities too. - *System mustn't be registered before components which system want to use, in this case functions call assertion. - * - *Params: - *priority = system priority. Priority determines order of execution of systems updates - *pass = index of UpdatePass which sholud call system update + Register new System into EntityManager. This funcion generate glue between EntityManager and System. + Systems can be registered from external dynamic library, and can be registered after adding entities too. + System mustn't be registered before components which system want to use, in this case functions call assertion. + + Params: + priority = system priority. Priority determines order of execution of systems updates + pass = index of UpdatePass which sholud call system update */ void registerSystem(Sys)(int priority, ushort pass = 0) { @@ -442,11 +446,12 @@ export struct EntityManager static struct ComponentsCounts { - uint readonly; - uint mutable; - uint excluded; - uint optional; - uint req; + //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; } static ComponentsCounts getComponentsCounts()() @@ -1083,7 +1088,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Return system ECS api by id + Return system ECS api by id */ export System* getSystem(ushort id) nothrow @nogc { @@ -1093,7 +1098,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Return pointer to system registered in manager + Return pointer to system registered in manager */ Sys* getSystem(Sys)() nothrow @nogc { @@ -1114,7 +1119,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Register component into EntityManager. + Register component into EntityManager. */ void registerComponent(Comp)() { @@ -1229,7 +1234,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Same as "void update(int pass = 0)" but use pass name instead of id. + Same as "void update(int pass = 0)" but use pass name instead of id. */ export void update(const(char)[] pass_name) nothrow @nogc { @@ -1239,7 +1244,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Update systems. Should be called only between begin() and end(). + Update systems. Should be called only between begin() and end(). */ export void update(ushort pass = 0) nothrow @nogc { @@ -1267,7 +1272,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Same as "void updateMT(int pass = 0)" but use pass name instead of id. + Same as "void updateMT(int pass = 0)" but use pass name instead of id. */ export void updateMT(const(char)[] pass_name) nothrow @nogc { @@ -1430,7 +1435,7 @@ export struct EntityManager }*/ /************************************************************************************************************************ - *Return size of single page (block). Every entity data block has size of page. + Return size of single page (block). Every entity data block has size of page. */ uint pageSize() { @@ -1438,8 +1443,8 @@ export struct EntityManager } /************************************************************************************************************************ - *Return number of pages in single block allocation. Library allocate defined number of pages at once and assign it's - *for entities. + Return number of pages in single block allocation. Library allocate defined number of pages at once and assign it's + for entities. */ uint pagesInBlock() { @@ -1464,11 +1469,11 @@ export struct EntityManager } /************************************************************************************************************************ - *Allocate EntityTemplate with all components from entity witch it's data and returns pointer to it. - * - *Params: - *entity_id = ID of entity from which should be created template - *fill_default = if true, components will be filled with default data, instead entity data will be taken + Allocate EntityTemplate with all components from entity witch it's data and returns pointer to it. + + Params: + entity_id = ID of entity from which should be created template + fill_default = if true, components will be filled with default data, instead entity data will be taken */ export EntityTemplate* allocateTemplate(EntityID entity_id, bool fill_default = false) { @@ -1504,10 +1509,10 @@ export struct EntityManager } /************************************************************************************************************************ - *Allocate EntityTemplate with specifed components and returns pointer to it. - * - *Params: - *components_ids = array of components allocated with template + Allocate EntityTemplate with specifed components and returns pointer to it. + + Params: + components_ids = array of components allocated with template */ export EntityTemplate* allocateTemplate(ushort[] components_ids) { @@ -1550,13 +1555,13 @@ export struct EntityManager } /************************************************************************************************************************ - *Allocate EntityTemplate from basic Template with modifications by adding and removing some components and returns pointer to it. - *Arrays of components needen't to be checked for repeated components, as function itself check if components exist in base template. - * - *Params: - *base_tmpl = template from which components sould be copied - *components_ids = array of new components to add - *remove_components_ids = array of components to remove from base template + Allocate EntityTemplate from basic Template with modifications by adding and removing some components and returns pointer to it. + Arrays of components needen't to be checked for repeated components, as function itself check if components exist in base template. + + Params: + base_tmpl = template from which components sould be copied + components_ids = array of new components to add + 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) @@ -1626,10 +1631,10 @@ export struct EntityManager } /************************************************************************************************************************ - *Returns entity type info. - * - *Params: - *ids = array of components + Returns entity type info. + + Params: + ids = array of components */ export EntityInfo* getEntityInfo(ushort[] ids) { @@ -1932,10 +1937,10 @@ export struct EntityManager } /************************************************************************************************************************ - *Returns pointer to entity. - * - *Params: - *id = ID of entity + Returns pointer to entity. + + Params: + id = ID of entity */ export Entity* getEntity(EntityID id) nothrow @nogc { @@ -1943,11 +1948,11 @@ export struct EntityManager } /************************************************************************************************************************ - *Remove components from entity by IDs. Components will be removed on end of frame. - * - *Params: - *entity_id = ID of entity - *del_ids = array of components IDs + Remove components from entity by IDs. Components will be removed on end of frame. + + Params: + entity_id = ID of entity + del_ids = array of components IDs */ export void removeComponents(EntityID entity_id, ushort[] del_ids) nothrow @nogc { @@ -2053,11 +2058,11 @@ export struct EntityManager } /************************************************************************************************************************ - *Remove coponents from entity. - * - *Params: - *Components = components types to remove - *entity_id = ID of entity + Remove coponents from entity. + + Params: + Components = components types to remove + entity_id = ID of entity */ void removeComponents(Components...)(EntityID entity_id) { @@ -2206,11 +2211,11 @@ export struct EntityManager } /************************************************************************************************************************ - *Add components to entity. Components will be added on end of frame. - * - *Params: - *entity_id = ID of entity to remove - *comps = components to add + Add components to entity. Components will be added on end of frame. + + Params: + entity_id = ID of entity to remove + comps = components to add */ void addComponents(Components...)(const EntityID entity_id, Components comps) nothrow @nogc { @@ -2241,10 +2246,10 @@ export struct EntityManager } /************************************************************************************************************************ - *Free template memory. - * - *Params: - *template_ = pointer entity template allocated by EntityManager. + Free template memory. + + Params: + template_ = pointer entity template allocated by EntityManager. */ export void freeTemplate(EntityTemplate* template_) { @@ -2253,9 +2258,9 @@ export struct EntityManager } /************************************************************************************************************************ - *Add copy of entity to system and returns pointer to it. Added copy has same data as copied entity. Returen pointer is - *valid only before one from commit(), begin() or end() will be called. To save entity to further use you should save ID - *instead of pointer. + Add copy of entity to system and returns pointer to it. Added copy has same data as copied entity. Returen pointer is + valid only before one from commit(), begin() or end() will be called. To save entity to further use you should save ID + instead of pointer. * *Params: *id = ID of entity to be copyied. @@ -2307,11 +2312,11 @@ export struct EntityManager } /************************************************************************************************************************ - *Add entity to system. Returen pointer is valid only before one from commit(), begin() or end() will be called. To save entity to further - *use you should save ID instead of pointer. - * - *Params: - *tmpl = pointer entity template allocated by EntityManager. + Add entity to system. Returen pointer is valid only before one from commit(), begin() or end() will be called. To save entity to further + use you should save ID instead of pointer. + + Params: + tmpl = pointer entity template allocated by EntityManager. */ export Entity* addEntity(EntityTemplate* tmpl) { @@ -2357,7 +2362,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Return block with free space for selected EntityInfo. + Return block with free space for selected EntityInfo. */ private EntitiesBlock* findBlockWithFreeSpace(EntityInfo* info) nothrow @nogc { @@ -2385,7 +2390,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Return block with free space for selected EntityInfo. Additional this function is multithread safe. + Return block with free space for selected EntityInfo. Additional this function is multithread safe. */ private EntitiesBlock* findBlockWithFreeSpaceMT(EntityInfo* info) { @@ -2429,10 +2434,10 @@ export struct EntityManager } /************************************************************************************************************************ - *Remove entity by ID. Entity will be removed on frame end. - * - *Params: - *id = id of entity to remove + Remove entity by ID. Entity will be removed on frame end. + + Params: + id = id of entity to remove */ export void removeEntity(EntityID id) { @@ -2515,10 +2520,10 @@ export struct EntityManager } /************************************************************************************************************************ - *functions return MetaData of page. - * - *Params: - *pointer = pointer to any data of entity (i.e. component data pointer) + functions return MetaData of page. + + Params: + pointer = pointer to any data of entity (i.e. component data pointer) */ export EntitiesBlock* getMetaData(const void* pointer) nothrow @nogc { @@ -2752,7 +2757,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Begin of update process. Should be called before any update is called. + Begin of update process. Should be called before any update is called. */ export void begin() { @@ -2769,7 +2774,7 @@ export struct EntityManager } /************************************************************************************************************************ - *End of update process. Should be called after every update function. + End of update process. Should be called after every update function. */ export void end() { @@ -2919,7 +2924,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Component info; + Component info; */ struct ComponentInfo { @@ -2963,7 +2968,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Entity type info. + Entity type info. */ struct EntityInfo { @@ -3135,7 +3140,7 @@ export struct EntityManager } /************************************************************************************************************************ - *Meta data of every block of entities (contained at the begining of block). + Meta data of every block of entities (contained at the begining of block). */ struct EntitiesBlock { @@ -3182,10 +3187,10 @@ export struct EntityManager } /************************************************************************************************************************ - *Structure with data used to calling System calls. - * - *first_block, begin, end, blocks parameters are used - *to call partial info update + Structure with data used to calling System calls. + + first_block, begin, end, blocks parameters are used + to call partial info update */ struct CallData { diff --git a/source/ecs/std.d b/source/ecs/std.d index 6077ede..c027fd5 100644 --- a/source/ecs/std.d +++ b/source/ecs/std.d @@ -1,6 +1,9 @@ /************************************************************************************************************************ -*It's internal code! -*This module contain implementation of standard functionality. +It's internal code! +This module contain implementation of standard functionality. + +Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. */ module ecs.std; diff --git a/source/ecs/system.d b/source/ecs/system.d index 35eb996..22a3955 100644 --- a/source/ecs/system.d +++ b/source/ecs/system.d @@ -1,5 +1,8 @@ /************************************************************************************************************************ -*System module. +System module. + +Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz +License: BSD 3-clause, see LICENSE file in project root folder. */ module ecs.system; @@ -7,25 +10,27 @@ import ecs.entity; import ecs.manager; /************************************************************************************************************************ -*System contain data required to proper glue EntityManager with Systems. -*System callbacks: -*
-void onUpdate(EntitesData); -*
-void onEnable() -*
-void onDisable(); -*
-bool onBegin(); -*
-void onEnd(); -*
-void onCreate() -*
-void onDestroy(); -*
-void onAddEntity(EntitesData); -*
-void onRemoveEntity(EntitiesData); -*
-void onChangeEntity(EntitiesData); -*
-void handleEvent(Entity*, Event); +System contain data required to proper glue EntityManager with Systems. +System callbacks: +$(LIST + * void onUpdate(EntitesData); + * void onEnable() + * void onDisable(); + * bool onBegin(); + * void onEnd(); + * void onCreate() + * void onDestroy(); + * void onAddEntity(EntitesData); + * void onRemoveEntity(EntitiesData); + * void onChangeEntity(EntitiesData); + * void handleEvent(Entity*, Event); +) */ struct System { /************************************************************************************************************************ - *Check if system is enabled. + Check if system is enabled. */ export bool enabled() nothrow @nogc { @@ -33,7 +38,7 @@ struct System } /************************************************************************************************************************ - *Enable system. If actually it is enabled function do nothing. + Enable system. If actually it is enabled function do nothing. */ export void enable() nothrow @nogc { @@ -43,7 +48,7 @@ struct System } /************************************************************************************************************************ - *Disable system. If actually it is disabled function do nothing. + Disable system. If actually it is disabled function do nothing. */ export void disable() nothrow @nogc { @@ -53,7 +58,7 @@ struct System } /************************************************************************************************************************ - *Get system priority. + Get system priority. */ export int priority() nothrow @nogc { @@ -61,7 +66,7 @@ struct System } /************************************************************************************************************************ - *Get system priority. + Get system priority. */ export bool execute() nothrow @nogc { @@ -69,7 +74,7 @@ struct System } /************************************************************************************************************************ - *Get system priority. + Get system priority. */ export ushort id() nothrow @nogc { @@ -77,7 +82,7 @@ struct System } /************************************************************************************************************************ - *Get system name. + Get system name. */ export const(char)[] name() nothrow @nogc { diff --git a/source/ecs/traits.d b/source/ecs/traits.d index b270b06..d19259f 100644 --- a/source/ecs/traits.d +++ b/source/ecs/traits.d @@ -15,7 +15,7 @@ unittest } /************************************************************************************************************************ -* Returns index of Component/Entity array in System's EntitiesData struct + Returns index of Component/Entity array in System's EntitiesData struct */ static long getIndexOfTypeInEntitiesData(EntitiesData, Type)() {