Compare commits

...

30 commits

Author SHA1 Message Date
f19bce1a57 Remove ECSEmscripten as Emscripten version is now supported in LDC (+fix compile scripts) 2025-05-14 13:26:10 +02:00
d77317c816 Make it possible to use custom runtime (such as numem) 2025-04-10 14:30:07 +02:00
76a23aa4ed Fix typo 2025-04-10 14:00:20 +02:00
9b75772039 Add missing export visibility attributes 2025-04-10 13:59:21 +02:00
50fa2ce19c Merge branch '2-empty-entity-crash' into 'master'
Fix crash in commit() when all components were removed from entity

Closes #2

See merge request Mergul/bubel-ecs!27
2023-04-27 10:30:22 +00:00
8ac9fa5dbd Fix crash in commit() when all components were removed from entity
Fix crash from issue #2 and add unittest case for it. Also implements general "empty" entity support
2023-04-24 23:46:16 +02:00
beb1837c43 Merge branch 'fixes' into 'master'
Fix unregisterSystem function

See merge request Mergul/bubel-ecs!26
2023-03-09 11:02:53 +00:00
0702b007d1 Fix unregisterSystem function
and add function to get m_system_pointer from System
2023-03-09 11:04:12 +01:00
9bf6f84d45 Update README
Add information about how to use library with DUB package manager
2023-01-18 15:09:25 +01:00
65c6ea4489 Fix demos compilation (using Meson) 2023-01-18 14:53:26 +01:00
ed0603a675 Update copyright notice 2023-01-18 14:40:59 +01:00
6bf8837e8f Update codecov token 2022-11-12 11:41:08 +00:00
6af3028070 Merge branch 'unregister_system' into 'master'
Add unregisterSystem functionality

See merge request Mergul/bubel-ecs!25
2022-11-12 11:15:31 +00:00
c2ba4c632a Fixes
-Assert if callEntitiesFunction is called for system which is not alive
-cleanup and formatting
2022-11-12 12:10:09 +01:00
5f4ba90b3e Add unregisterSystem functionality 2022-11-10 10:54:23 +01:00
881d6d113b Merge branch 'export_id' into 'master'
Export id generated by becsID

See merge request Mergul/bubel-ecs!24
2022-11-10 09:28:34 +00:00
7a614686c8 Export id generated by becsID 2022-11-08 22:05:57 +01:00
6c4109d86c Merge branch 'improve_component_init' into 'master'
Syntax '*ptr = Structure.init' requires opAssign operator, get rid of this...

See merge request Mergul/bubel-ecs!23
2022-10-31 18:35:26 +00:00
c0246ce2af Syntax '*ptr = Structure.init' requires opAssign operator, get rid of this requirement by using malloc.
This is useful when component is defined in another shared library and we use '-fvisibility=hidden', in that case opAssign is hidden and we will get 'undefined symbol'
2022-10-27 23:29:50 +02:00
a8d48f1218 Merge branch 'wasm_fixes' into 'master'
Don't use ForeachType as it don't work if foreach type is not copyable.

See merge request Mergul/bubel-ecs!22
2022-10-06 17:01:50 +00:00
ce47bfc65a Fix wasm compilation 2022-10-05 18:39:07 +02:00
56ce8c3f9c Don't use ForeachType as it don't work if foreach type is not copyable.
This fixes compilation error:
include/d/std/traits.d:8024: Error: Generating an `inout` copy constructor for `struct game.graphic.manager.Graphic` failed, therefore instances of it are uncopyable
2022-10-04 22:18:45 +02:00
bd2afce19a Merge branch 'small-fixes' into 'master'
Small fixes

See merge request Mergul/bubel-ecs!21
2022-07-09 20:27:01 +00:00
24a07a05e5 Small fixes 2022-07-09 20:27:01 +00:00
014e9cee8d Merge branch 'improve_meson' into 'master'
Allow simpler use of ecs from parent project

See merge request Mergul/bubel-ecs!20
2022-06-25 08:42:11 +00:00
d01ebd960a Allow simpler use of ecs from parent project
This allows parent project to find subproject by simple:
decs_dep = dependency('decs')
2022-06-19 14:31:49 +02:00
4f416c7557 Merge branch 'small-fixes' into 'master'
Fixed bug with addComponents template

See merge request Mergul/bubel-ecs!19
2021-11-17 14:32:43 +00:00
85e1f8a76e Fixed bug with addComponents template
-deprecated Comonent.ref_ function
-remover Comonent.ref_ usage from addComponents template
-updated dub.json
2021-11-17 15:03:25 +01:00
54b210346d Merge branch 'fixes' into 'master'
Minor fixes

See merge request Mergul/bubel-ecs!18
2021-05-09 16:46:11 +00:00
cc097dddf0 Minor fixes 2021-05-09 16:46:11 +00:00
27 changed files with 575 additions and 418 deletions

7
.gitignore vendored
View file

@ -1,9 +1,8 @@
*
!*/
!source/**
!tests/**
!README.md
!./dub.json
!dub.json
!.gitignore
!codecov.yml
!skeleton.html
@ -11,4 +10,6 @@
!**/*.wrap
!meson_options.txt
!compile_wasm.py
!compile_android.py
!compile_android.py
!.gitlab-ci.yml
!LICENSE

View file

@ -23,7 +23,7 @@ build_code:
test_dmd_debug:
stage: test
image: frolvlad/alpine-glibc
image: debian:buster-slim
script:
- binaries/dmd_debug_unittest
artifacts:
@ -31,7 +31,7 @@ test_dmd_debug:
junit: test_report.xml
test_dmd:
stage: test
image: frolvlad/alpine-glibc
image: debian:buster-slim
script:
- binaries/dmd_release_unittest
artifacts:
@ -39,7 +39,7 @@ test_dmd:
junit: test_report.xml
test_dmd_betterC:
stage: test
image: frolvlad/alpine-glibc
image: debian:buster-slim
script:
- binaries/dmd_debug_unittest_bc
artifacts:
@ -56,7 +56,7 @@ coverage_test_dmd:
- mkdir reports
- binaries/dmd_unittest_cov
after_script:
- bash <(curl -s https://codecov.io/bash) -s reports -t 1a0c0169-a721-4085-8252-fed4755dcd8c
- bash <(curl -s https://codecov.io/bash) -s reports -t df87b1d8-85f4-4584-96e3-1315d27ec2c5
wasm:
stage: build_wasm

View file

@ -10,6 +10,13 @@ Bubel ECS was tested on Linux, Windows, Android and WASM.
**Currently library is in beta stage so some significant API changes can appear.**
Package is available on [DUB package repository](https://code.dlang.org/packages/bubel-ecs). Usage:
```
"dependencies": {
"bubel-ecs": "~>0.1.1"
}
```
## Design
For core information about Entity-Component-System architectural pattern please read definition described at [Wikipedia](https://en.wikipedia.org/wiki/Entity_component_system).

View file

@ -13,7 +13,7 @@ def compile(sources, output):
if file_extension == '.d' and filename != 'package':
files.append(os.path.join(r, file))
ldc_cmd = 'ldc2 ' + shared_flags + ldc_flags + '-oq -mtriple=wasm32-unknown-unknown-wasm -betterC --output-bc --od=.bc --singleobj --checkaction=C --of=' + output + ' '
ldc_cmd = 'ldc2 ' + shared_flags + ldc_flags + '-oq -mtriple=wasm32-unknown-emscripten -betterC --output-bc --od=.bc --singleobj --checkaction=C --of=' + output + ' '
for path in sources:
ldc_cmd += '-I' + path + ' '
@ -33,7 +33,7 @@ def compile(sources, output):
shared_flags = ''
clean = 0
emc_flags = ''
ldc_flags = '--d-version=ECSEmscripten '
ldc_flags = ''
import_paths = ['source','tests']
build_tests = 0

View file

@ -1,7 +1,6 @@
import os
import ntpath
import sys
import imp
def compile(sources, output):
files = []
@ -14,7 +13,7 @@ def compile(sources, output):
if file_extension == '.d' and filename != 'package':
files.append(os.path.join(r, file))
ldc_cmd = compiler + shared_flags + ldc_flags + '-oq -mtriple=wasm32-unknown-unknown-wasm -betterC --output-bc --od=.bc --singleobj --checkaction=C --of=' + output + ' '
ldc_cmd = compiler + shared_flags + ldc_flags + '-oq -mtriple=wasm32-unknown-emscripten -betterC --output-bc --od=.bc --singleobj --checkaction=C --of=' + output + ' '
for path in sources:
ldc_cmd += '-I' + path + ' '
@ -41,7 +40,7 @@ only_bc = 0
multi = 0
sources = ['tests', 'source']
emc_flags = '-s USE_SDL=2 -s USE_SDL_IMAGE=2 -s SDL2_IMAGE_FORMATS="[\'png\']" '
ldc_flags = '--d-version=ECSEmscripten --d-version=SDL_209 --d-version=BindSDL_Static --d-version=BindSDL_Image --d-version=MM_USE_POSIX_THREADS '
ldc_flags = '--d-version=SDL_209 --d-version=BindSDL_Static --d-version=BindSDL_Image --d-version=MM_USE_POSIX_THREADS '
import_paths = ['external/sources', 'external/imports', 'external/wasm_imports', '../source', 'utils/source', 'simple/source']
for arg in sys.argv[1:]:
@ -121,8 +120,9 @@ emcc_cmd += 'demo.bc '
os.system("mkdir build")
emscripten = imp.load_source('', os.path.expanduser("~") + '/.emscripten')
pack_cmd = emscripten.EMSCRIPTEN_ROOT + '/tools/file_packager.py build/assets.data --preload assets --js-output=build/assets.js'
# emscripten = imp.load_source('', os.path.expanduser("~") + '/.emscripten')
# pack_cmd = emscripten.EMSCRIPTEN_ROOT + '/tools/file_packager.py build/assets.data --preload assets --js-output=build/assets.js'
pack_cmd = os.path.expandvars('$EMSDK/upstream/emscripten') + '/tools/file_packager.py build/assets.data --preload assets --js-output=build/assets.js'
print('Packafing files: ' + pack_cmd)
os.system(pack_cmd)

View file

@ -19,7 +19,7 @@ sdl2_image_dep = dependency('SDL2_image')
subdir('utils') # Utils library
executable('decs-demos', [demos_src, external_src],
executable('BubelECSDemos', [demos_src, external_src],
include_directories : [demos_inc, external_inc],
d_module_versions : versions,
link_with : [ecs_lib, ecs_utils_lib],
@ -27,7 +27,7 @@ executable('decs-demos', [demos_src, external_src],
bindbc_loader_dep,
bindbc_sdl_dep,
cimgui_dep,
decs_dep,
bubel_ecs_dep,
ecs_utils_dep,
sdl2_dep,
sdl2_image_dep,

View file

@ -5,12 +5,12 @@ subdir('source/ecs_utils')
utils_inc = include_directories('source/')
# Dependencies
ecs_utils_lib = library('ecs_utils', utils_src,
ecs_utils_lib = library('ECSUtils', utils_src,
include_directories : [demos_inc, external_inc, utils_inc],
link_args : link_args,
d_module_versions : versions,
dependencies : [
decs_dep,
bubel_ecs_dep,
bindbc_loader_dep,
bindbc_sdl_dep,
]

View file

@ -1,11 +1,11 @@
{
"name": "bubel_ecs",
"targetName" : "bubel_ecs",
"name": "bubel-ecs",
"targetName" : "BubelECS",
"authors": [
"Michał Masiukiewicz", "Dawid Masiukiewicz"
],
"description": "Dynamic Entity Component System",
"copyright": "Copyright © 2018-2019, Michał Masiukiewicz, Dawid Masiukiewicz",
"copyright": "Copyright © 2018-2023, Michał Masiukiewicz, Dawid Masiukiewicz",
"license": "BSD 3-clause",
"sourcePaths" : ["source\/"],
"excludedSourceFiles":[
@ -63,7 +63,7 @@
],
"dflags": [
"-unittest",
"-cov"
"-cov=ctfe"
]
},
{

View file

@ -1,4 +1,4 @@
project('decs', 'd', version : '0.5.0')
project('bubel-ecs', 'd', version : '0.5.0')
# Options
betterC_opt = get_option('betterC')
@ -43,29 +43,42 @@ if betterC_opt
if comp_id == 'gcc'
args += ['-fno-druntime']
link_args += ['-fno-druntime']
else
else
args += '-betterC'
link_args += '-betterC'
endif
endif
add_global_arguments(args, language : 'd')
add_global_link_arguments(link_args, language : 'd')
add_project_arguments(args, language : 'd')
add_project_link_arguments(link_args, language : 'd')
# Dependencies
threads_dep = dependency('threads')
ecs_lib = library('decs',
d_versions = []
deps = []
if host_machine.cpu_family() == 'wasm32'
else
# meson incorectly adds "-s USE_PTHREADS=1" to ldc2 invocation whe pthreads is added as dependency
# add it for non wasm builds
deps += threads_dep
endif
ecs_lib = library('BubelECS',
src,
d_module_versions : d_versions,
include_directories : [inc],
)
decs_dep = declare_dependency(
bubel_ecs_dep = declare_dependency(
include_directories : [inc],
link_with : ecs_lib,
dependencies : threads_dep,
dependencies : deps,
)
meson.override_dependency('bubel-ecs', bubel_ecs_dep)
# Tests
if BuildTests_opt
subdir('tests')

View file

@ -4,129 +4,10 @@ It's internal code. Can be used for atomics if emscripten backend will be used.
This module contain atomic operations which include support for emscripten atomics functions.
Emscripten functions are contained in API similar to druntime.
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.atomic;
version (Emscripten) version = ECSEmscripten;
public import core.atomic;
version (ECSEmscripten)
{
import std.traits;
enum MemoryOrder
{
acq,
acq_rel,
raw,
rel,
seq
}
extern (C) ubyte emscripten_atomic_cas_u8(void* addr, ubyte oldVal, ubyte newVal) @nogc nothrow pure;
extern (C) ushort emscripten_atomic_cas_u16(void* addr, ushort oldVal, ushort newVal) @nogc nothrow pure;
extern (C) uint emscripten_atomic_cas_u32(void* addr, uint oldVal, uint newVal) @nogc nothrow pure;
extern (C) ubyte emscripten_atomic_load_u8(const void* addr) @nogc nothrow pure;
extern (C) ushort emscripten_atomic_load_u16(const void* addr) @nogc nothrow pure;
extern (C) uint emscripten_atomic_load_u32(const void* addr) @nogc nothrow pure;
extern (C) ubyte emscripten_atomic_store_u8(void* addr, ubyte val) @nogc nothrow pure;
extern (C) ushort emscripten_atomic_store_u16(void* addr, ushort val) @nogc nothrow pure;
extern (C) uint emscripten_atomic_store_u32(void* addr, uint val) @nogc nothrow pure;
extern (C) ubyte emscripten_atomic_add_u8(void* addr, ubyte val) @nogc nothrow pure;
extern (C) ushort emscripten_atomic_add_u16(void* addr, ushort val) @nogc nothrow pure;
extern (C) uint emscripten_atomic_add_u32(void* addr, uint val) @nogc nothrow pure;
extern (C) ubyte emscripten_atomic_sub_u8(void* addr, ubyte val) @nogc nothrow pure;
extern (C) ushort emscripten_atomic_sub_u16(void* addr, ushort val) @nogc nothrow pure;
extern (C) uint emscripten_atomic_sub_u32(void* addr, uint val) @nogc nothrow pure;
public pure nothrow @nogc Unqual!T atomicOp(string op, T, V1)(ref shared T val, V1 mod)
{
static if (op == "+=")
{
static if (is(T == byte) || is(T == ubyte))
return cast(Unqual!T)(emscripten_atomic_add_u8(cast(void*)&val,
cast(Unqual!T) mod) + 1);
else static if (is(T == short) || is(T == ushort))
return cast(Unqual!T)(emscripten_atomic_add_u16(cast(void*)&val,
cast(Unqual!T) mod) + 1);
else static if (is(T == int) || is(T == uint))
return cast(Unqual!T)(emscripten_atomic_add_u32(cast(void*)&val,
cast(Unqual!T) mod) + 1);
else
static assert(0);
}
else static if (op == "-=")
{
static if (is(T == byte) || is(T == ubyte))
return cast(Unqual!T)(emscripten_atomic_sub_u8(cast(void*)&val,
cast(Unqual!T) mod) - 1);
else static if (is(T == short) || is(T == ushort))
return cast(Unqual!T)(emscripten_atomic_sub_u16(cast(void*)&val,
cast(Unqual!T) mod) - 1);
else static if (is(T == int) || is(T == uint))
return cast(Unqual!T)(emscripten_atomic_sub_u32(cast(void*)&val,
cast(Unqual!T) mod) - 1);
else
static assert(0);
}
}
public pure nothrow @nogc @trusted void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val,
V newval)
{
alias UT = Unqual!T;
static if (is(UT == bool) || is(UT == byte) || is(UT == ubyte))
emscripten_atomic_store_u8(cast(void*)&val, cast(UT) newval);
else static if (is(UT == short) || is(UT == ushort))
emscripten_atomic_store_u16(cast(void*)&val, cast(UT) newval);
else static if (is(UT == int) || is(UT == uint))
emscripten_atomic_store_u32(cast(void*)&val, cast(UT) newval);
else
static assert(0);
}
public pure nothrow @nogc @trusted T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(
ref const T val)
{
alias UT = Unqual!T;
static if (is(UT == bool))
return emscripten_atomic_load_u8(cast(const void*)&val) != 0;
else static if (is(UT == byte) || is(UT == ubyte))
return emscripten_atomic_load_u8(cast(const void*)&val);
else static if (is(UT == short) || is(UT == ushort))
return emscripten_atomic_load_u16(cast(const void*)&val);
else static if (is(UT == int) || is(UT == uint))
return emscripten_atomic_load_u32(cast(const void*)&val);
else
static assert(0);
}
public pure nothrow @nogc @trusted bool cas(MemoryOrder succ = MemoryOrder.seq,
MemoryOrder fail = MemoryOrder.seq, T, V1, V2)(T* here, V1 ifThis, V2 writeThis)
{
alias UT = Unqual!T;
static if (is(UT == bool))
return emscripten_atomic_cas_u8(cast(void*) here,
cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis;
else static if (is(UT == byte) || is(UT == ubyte))
return emscripten_atomic_cas_u8(cast(void*) here,
cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis;
else static if (is(UT == short) || is(UT == ushort))
return emscripten_atomic_cas_u16(cast(void*) here,
cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis;
else static if (is(UT == int) || is(UT == uint))
return emscripten_atomic_cas_u32(cast(void*) here,
cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis;
else
static assert(0);
}
}
else
{
public import core.atomic;
}

View file

@ -17,7 +17,7 @@ Struct EntitiesData
}
---
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.attributes;

View file

@ -3,7 +3,7 @@ It's internal code.
Module contain memory allocator.
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.block_allocator;

View file

@ -67,7 +67,7 @@ Struct System1
}
---
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.core;
@ -94,7 +94,7 @@ static struct ECS
*/
mixin template Component()
{
ComponentRef ref_() @nogc nothrow return
deprecated ComponentRef ref_() @nogc nothrow return
{
return ComponentRef(&this, becsID!(typeof(this)));
}

View file

@ -1,7 +1,7 @@
/************************************************************************************************************************
Entity module.
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.entity;
@ -44,7 +44,7 @@ struct Entity
return cast(T*)getComponent(becsID!T);
}
void* getComponent(ushort component_id) const
export void* getComponent(ushort component_id) const
{
EntityManager.EntitiesBlock* block = gEntityManager.getMetaData(&this);
EntityManager.EntityInfo* info = block.type_info;
@ -54,7 +54,7 @@ struct Entity
return (cast(void*)block + info.deltas[component_id] + block.entityIndex(&this) * gEntityManager.components[component_id].size);
}
bool hasComponent(ushort component_id) const
export bool hasComponent(ushort component_id) const
{
EntityManager.EntitiesBlock* block = gEntityManager.getMetaData(&this);
EntityManager.EntityInfo* info = block.type_info;
@ -62,7 +62,7 @@ struct Entity
return true;
}
EntityMeta getMeta() const
export EntityMeta getMeta() const
{
EntityMeta meta;
meta.block = gEntityManager.getMetaData(&this);
@ -85,7 +85,7 @@ struct EntityMeta
return cast(T*)getComponent(becsID!T);
}
void* getComponent(ushort component_id) const
export void* getComponent(ushort component_id) const
{
const (EntityManager.EntityInfo)* info = block.type_info;
@ -95,7 +95,7 @@ struct EntityMeta
return (cast(void*)block + info.deltas[component_id] + index * gEntityManager.components[component_id].size);
}
bool hasComponent(ushort component_id) const
export bool hasComponent(ushort component_id) const
{
const EntityManager.EntityInfo* info = block.type_info;
if (component_id >= info.deltas.length || info.deltas[component_id] == 0)return false;
@ -133,7 +133,7 @@ export struct EntityTemplate
/************************************************************************************************************************
Get specified component. If component doesn't exist function return null. Returned pointer is valid during EntityTemplate lifetime.
*/
void* getComponent(ushort component_id) const nothrow @nogc
export void* getComponent(ushort component_id) const nothrow @nogc
{
if(component_id >= info.tmpl_deltas.length || info.tmpl_deltas[component_id] == ushort.max)return null;
return cast(void*)(entity_data.ptr + info.tmpl_deltas[component_id]);

View file

@ -1,3 +1,7 @@
/************************************************************************************************************************
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.events;
import bubel.ecs.block_allocator;

View file

@ -1,4 +1,8 @@
module bubel.ecs.hash_map;
/************************************************************************************************************************
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.hash_map;
import std.traits;
@ -24,7 +28,7 @@ export ulong defaultHashFunc(T)(auto ref T t) nothrow @nogc
}
}
ulong hash(byte[] array) nothrow @nogc
export ulong hash(byte[] array) nothrow @nogc
{
ulong hash = 0;
@ -332,36 +336,36 @@ nothrow:
return result;
}
export int byKey(scope int delegate(Key k) nothrow dg)
export int byKey(scope int delegate(ref Key k) dg)
{
int result;
foreach (ref Key k; this)
{
result = dg(k);
result = (cast(int delegate(ref Key k) nothrow @nogc)dg)(k);
if (result)
break;
}
return result;
}
export int byValue(scope int delegate(ref Value k) nothrow dg)
export int byValue(scope int delegate(ref Value v) dg)
{
int result;
foreach (ref Value v; this)
{
result = dg(v);
result = (cast(int delegate(ref Value v) nothrow @nogc)dg)(v);
if (result)
break;
}
return result;
}
export int byKeyValue(scope int delegate(ref Key k, ref Value v) nothrow dg)
export int byKeyValue(scope int delegate(ref Key k, ref Value v) dg)
{
int result;
foreach (ref Key k, ref Value v; this)
{
result = dg(k, v);
result = (cast(int delegate(ref Key k, ref Value v) nothrow @nogc)dg)(k, v);
if (result)
break;
}

View file

@ -1,3 +1,7 @@
/************************************************************************************************************************
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.id_manager;
import bubel.ecs.entity;

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,7 @@
/************************************************************************************************************************
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module ecs;
public import bubel.ecs.core;

View file

@ -1,3 +1,7 @@
/************************************************************************************************************************
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.simple_vector;
import bubel.ecs.std;

View file

@ -2,16 +2,14 @@
It's internal code!
This module contain implementation of standard functionality.
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.std;
version (Emscripten) version = ECSEmscripten;
import std.traits;
version (ECSEmscripten)
version (Emscripten)
{
extern (C) struct pthread_mutex_t
{
@ -29,10 +27,6 @@ version (ECSEmscripten)
extern (C) int memcmp(const void* s1, const void* s2, size_t size);
extern (C) void exit(int status) nothrow @nogc;
extern (C) void __assert(const(char)* msg, const(char)* file, uint line)
{
exit(-20);
}
extern (C) void free(void*) @nogc nothrow @system;
extern (C) void* malloc(size_t size) @nogc nothrow @system;
@ -60,7 +54,7 @@ else
public import core.stdc.stdlib : qsort;
}
version (ECSEmscripten)
version (Emscripten)
{
}
else version (Windows)
@ -89,7 +83,7 @@ else version (Posix)
import core.sys.posix.stdlib : posix_memalign;
}
version (ECSEmscripten)
version (Emscripten)
{
private const uint max_alloca = 10000;
private __gshared byte[max_alloca] alloca_array;
@ -294,7 +288,7 @@ static struct Mallocator
posix_memalign(&ret, alignment, length); //ret = aligned_alloc(alignment, length);
else version (Windows)
ret = _aligned_malloc(length, alignment);
else version (ECSEmscripten)
else version (Emscripten)
posix_memalign(&ret, alignment, length); //malloc(length);
else
static assert(0, "Unimplemented platform!");
@ -341,7 +335,7 @@ static struct Mallocator
free(cast(void*) object);
else version (Windows)
_aligned_free(cast(void*) object);
else version (ECSEmscripten)
else version (Emscripten)
free(cast(void*) object);
else
static assert(0, "Unimplemented platform!");
@ -351,7 +345,7 @@ static struct Mallocator
struct Mutex
{
version (ECSEmscripten)
version (Emscripten)
{
void initialize() nothrow @nogc
{

View file

@ -1,7 +1,7 @@
/************************************************************************************************************************
System module.
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.system;
@ -13,14 +13,14 @@ import bubel.ecs.manager;
System contain data required to proper glue EntityManager with Systems.
System callbacks:
$(LIST
* void onUpdate(EntitesData);
* void onUpdate(EntitiesData);
* void onEnable() - called inside system.enable() function
* void onDisable() - called inside system.disable() function
* bool onBegin() - called inside manager.begin()
* void onEnd() - called inside manager.end()
* void onCreate() - called after registration inside registerSystem function
* void onDestroy() - called during re-registration and inside manager destructor
* void onAddEntity(EntitesData) - called for every entity which are assigned to system (by adding new entity or changing its components)
* void onAddEntity(EntitiesData) - called for every entity which are assigned to system (by adding new entity or changing its components)
* void onRemoveEntity(EntitiesData) - called for every entity removed from system update process
* void onChangeEntity(EntitiesData) - called for every entity which components are changed but it was previously assigned to system
* void handleEvent(Entity*, Event) - called for every event supported by system
@ -89,38 +89,100 @@ struct System
return cast(const(char)[]) m_name;
}
/************************************************************************************************************************
Return false if system was unregistered, true otherwise.
*/
export bool isAlive() nothrow @nogc
{
return m_system_pointer != null;
}
/************************************************************************************************************************
Return pointer to user side system object
*/
export void* ptr() nothrow @nogc
{
return m_system_pointer;
}
package:
void destroy()
///destory system. Dispose all data
export void destroy() nothrow @nogc
{
import bubel.ecs.std : Mallocator;
destroySystemData();
if (m_name)
{
Mallocator.dispose(m_name);
m_name = null;
}
}
///destroy all system data but keeps name which is used for case of system re-registration
void destroySystemData() nothrow @nogc
{
import bubel.ecs.std : Mallocator;
disable();
if (m_destroy)
(cast(void function(void*)) m_destroy)(m_system_pointer);
{
(cast(void function(void*) nothrow @nogc) m_destroy)(m_system_pointer);
m_destroy = null;
}
if (m_name)
Mallocator.dispose(m_name);
if (m_components)
{
Mallocator.dispose(m_components);
m_components = null;
}
if (m_excluded_components)
{
Mallocator.dispose(m_excluded_components);
m_excluded_components = null;
}
if (m_optional_components)
{
Mallocator.dispose(m_optional_components);
m_optional_components = null;
}
if (jobs)
{
Mallocator.dispose(jobs);
jobs = null;
}
if (m_read_only_components)
{
Mallocator.dispose(m_read_only_components);
m_read_only_components = null;
}
if (m_writable_components)
{
Mallocator.dispose(m_writable_components);
m_writable_components = null;
}
if (m_readonly_dependencies)
{
Mallocator.dispose(m_readonly_dependencies);
m_readonly_dependencies = null;
}
if (m_writable_dependencies)
{
Mallocator.dispose(m_writable_dependencies);
m_writable_dependencies = null;
}
if (m_event_callers)
{
Mallocator.dispose(m_event_callers);
m_event_callers = null;
}
if (m_system_pointer)
{
Mallocator.dispose(m_system_pointer);
m_system_pointer = null;
}
}
struct EventCaller

View file

@ -1,3 +1,7 @@
/************************************************************************************************************************
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.traits;
import std.traits;
@ -7,8 +11,11 @@ import std.traits;
*/
ref ushort becsID(T)()
{
__gshared ushort id = ushort.max;
return id;
/// Embed id in struct so export can be added to variable definition
static struct LocalStruct {
export __gshared ushort id = ushort.max;
}
return LocalStruct.id;
}
/************************************************************************************************************************
@ -66,7 +73,7 @@ static string attachParentName(alias T, string str)()
{
return attachParentName!(parent, parent_str[7 .. $] ~ '.' ~ str);
}
else return parent_str[8 .. $] ~ '.' ~ str;
else return parent_str[7 .. $] ~ '.' ~ str;
}
else static if(parent_str[0..8] == "package ")
{

View file

@ -1,3 +1,7 @@
/************************************************************************************************************************
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.vector;
import core.bitop;

View file

@ -16,7 +16,7 @@ else import std.array : staticArray;
struct CInt
{
mixin ECS.Component;
// mixin ECS.Component;
alias value this;
@ -25,7 +25,7 @@ struct CInt
struct CFloat
{
mixin ECS.Component;
// mixin ECS.Component;
alias value this;
@ -34,7 +34,7 @@ struct CFloat
struct CDouble
{
mixin ECS.Component;
// mixin ECS.Component;
alias value this;
@ -43,7 +43,7 @@ struct CDouble
struct CLong
{
mixin ECS.Component;
// mixin ECS.Component;
alias value this;
@ -52,7 +52,7 @@ struct CLong
struct CShort
{
mixin ECS.Component;
// mixin ECS.Component;
alias value this;
@ -61,7 +61,7 @@ struct CShort
struct CUnregistered
{
mixin ECS.Component;
// mixin ECS.Component;
alias value this;
@ -70,7 +70,7 @@ struct CUnregistered
struct CFlag
{
mixin ECS.Component;
// mixin ECS.Component;
}
struct LongAddSystem
@ -113,6 +113,31 @@ struct EmptySystem
int count = 0;
}
struct EntityCounterSystem
{
mixin ECS.System!1;
struct EntitiesData
{
int thread_id;
uint length;
Entity[] entity;
}
bool onBegin()
{
count = 0;
return true;
}
void onUpdate(EntitiesData data)
{
count += data.length;
}
int count = 0;
}
void beforeEveryTest()
{
becsID!CUnregistered = ushort.max;
@ -182,17 +207,18 @@ unittest
assert(*entity2.getComponent!CInt == 2);
assert(*entity2.getComponent!CFloat == 2.0);
//CInt cint = CInt(10);
//CLong clong;
//Entity* entity3 = gEntityManager.addEntity(tmpl_, [cint.ref_, clong.ref_].staticArray);
Entity* entity3 = gEntityManager.addEntity(tmpl_, [CInt(10).ref_, CLong().ref_, CFlag().ref_].staticArray);
CInt int1 = CInt(10);
CLong long1 = CLong();
CFlag flag1 = CFlag();
Entity* entity3 = gEntityManager.addEntity(tmpl_, [ComponentRef(&int1, becsID(int1)), ComponentRef(&long1, becsID(long1)), ComponentRef(&flag1, becsID(flag1))].staticArray);
EntityID id = entity3.id;
assert(entity3.hasComponent(becsID!CInt));
assert(entity3.hasComponent(becsID!CFloat));
assert(*entity3.getComponent!CInt == 10);
assert(*entity3.getComponent!CFloat == 2.0);
gEntityManager.addComponents(entity3.id, [CFlag().ref_,CShort(2).ref_].staticArray);
CShort short1 = CShort(2);
gEntityManager.addComponents(entity3.id, [ComponentRef(&flag1, becsID(flag1)),ComponentRef(&short1, becsID(short1))].staticArray);
gEntityManager.commit();
entity3 = gEntityManager.getEntity(id);
assert(entity3.getComponent!CInt);
@ -213,7 +239,7 @@ unittest
assert(*entity3.getComponent!CInt == 10);
assert(*entity3.getComponent!CFloat == 2.0);
gEntityManager.addComponents(entity3.id, [CFlag().ref_,CShort(2).ref_].staticArray);
gEntityManager.addComponents(entity3.id, [ComponentRef(&flag1, becsID(flag1)),ComponentRef(&short1, becsID(short1))].staticArray);
gEntityManager.removeComponents(entity3.id, [becsID!CUnregistered].staticArray);
gEntityManager.commit();
entity3 = gEntityManager.getEntity(id);
@ -231,7 +257,8 @@ unittest
gEntityManager.endRegister();
gEntityManager.addComponents(entity3.id, [CUnregistered(4).ref_].staticArray);
CUnregistered unregistered1 = CUnregistered(4);
gEntityManager.addComponents(entity3.id, [ComponentRef(&unregistered1, becsID(unregistered1))].staticArray);
gEntityManager.commit();
entity3 = gEntityManager.getEntity(id);
assert(entity3.getComponent!CUnregistered);
@ -241,7 +268,82 @@ unittest
gEntityManager.commit();
entity3 = gEntityManager.getEntity(id);
assert(!entity3.getComponent!CUnregistered);
}
@("AddEmptyEntity")
unittest
{
struct OnAddRemoveChangeCounter
{
mixin ECS.System!1;
struct EntitiesData
{
int thread_id;
uint length;
Entity[] entity;
}
void onAddEntity(EntitiesData data)
{
add += data.length;
}
void onRemoveEntity(EntitiesData data)
{
assert(0, "It's impossible to remove entity from being updated by system which accept empty entity");
}
int add = 0;
}
gEntityManager.beginRegister();
gEntityManager.registerSystem!EntityCounterSystem(0);
gEntityManager.registerSystem!OnAddRemoveChangeCounter(1);
gEntityManager.endRegister();
CLong long_component = CLong(3);
Entity* entity = null;
EntityID entity_id = gEntityManager.addEntity(null).id;
EntityCounterSystem* system = gEntityManager.getSystem!EntityCounterSystem;
assert(system !is null);
assert(system.count == 0);
OnAddRemoveChangeCounter* add_remove_change_system = gEntityManager.getSystem!OnAddRemoveChangeCounter;
assert(add_remove_change_system !is null);
assert(add_remove_change_system.add == 0);
gEntityManager.commit();
assert(add_remove_change_system.add == 1);
entity = gEntityManager.getEntity(entity_id);
assert(!entity.hasComponent(becsID!CLong));
assert(entity.getComponent(becsID!CLong) is null);
gEntityManager.begin();
gEntityManager.update();
assert(system.count == 1);
gEntityManager.end();
gEntityManager.addEntityCopy(entity_id);
gEntityManager.addEntityCopy(entity_id);
gEntityManager.addComponents(entity_id, [ComponentRef(&long_component, becsID(long_component))].staticArray);
gEntityManager.commit();
assert(add_remove_change_system.add == 3, "onAddEntity missed");
entity = gEntityManager.getEntity(entity_id);
assert(entity.hasComponent(becsID!CLong));
assert(*entity.getComponent!CLong == 3);
gEntityManager.begin();
gEntityManager.update();
assert(system.count == 3);
gEntityManager.end();
}
//allocate templates

View file

@ -119,8 +119,12 @@ unittest
gEntityManager.endRegister();
EntityTemplate* tmpl = gEntityManager.allocateTemplate([becsID!CInt, becsID!CLong].staticArray);
EntityID id = gEntityManager.addEntity(tmpl,[CLong(10).ref_, CInt(6).ref_].staticArray).id;
EntityID id2 = gEntityManager.addEntity(tmpl,[CInt(4).ref_].staticArray).id;
CLong clong = CLong(10);
CInt cint = CInt(6);
CInt cint2 = CInt(4);
EntityID id = gEntityManager.addEntity(tmpl,[ComponentRef(&clong, becsID(clong)), ComponentRef(&cint, becsID(cint))].staticArray).id;
EntityID id2 = gEntityManager.addEntity(tmpl,[ComponentRef(&cint2, becsID(cint2))].staticArray).id;
gEntityManager.freeTemplate(tmpl);
gEntityManager.commit();
@ -138,5 +142,34 @@ unittest
gEntityManager.update();
gEntityManager.end();
gEntityManager.destroy();
}
@("2-empty-entity-crash")
unittest
{
gEntityManager.initialize(0);
gEntityManager.beginRegister();
gEntityManager.registerComponent!CInt;
gEntityManager.registerComponent!CFloat;
gEntityManager.endRegister();
EntityTemplate* tmpl = gEntityManager.allocateTemplate([becsID!CInt, becsID!CFloat].staticArray);
scope(exit) gEntityManager.freeTemplate(tmpl);
EntityID id = gEntityManager.addEntity(tmpl).id;
// EntityID id2 = gEntityManager.addEntity().id;
gEntityManager.commit();
gEntityManager.removeComponents(id, [becsID!CInt, becsID!CFloat].staticArray);
gEntityManager.commit();
gEntityManager.destroy();
}

View file

@ -10,7 +10,7 @@ tests_src = files(
'vector.d'
)
exe = executable('decs-tests',
exe = executable('BubelECSTests',
tests_src,
include_directories : [inc, include_directories('..')],
d_args : args,