Compare commits

..

1 commit

Author SHA1 Message Date
0281fd5c1d Remove unnecessary exports
If given type is not used across library interface it's methods can stay private.
As of now this is only test as I am not sure if it doesn't cause problems in wasm build.
2022-10-27 23:35:24 +02:00
25 changed files with 403 additions and 543 deletions

View file

@ -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 df87b1d8-85f4-4584-96e3-1315d27ec2c5
- bash <(curl -s https://codecov.io/bash) -s reports -t 1a0c0169-a721-4085-8252-fed4755dcd8c
wasm:
stage: build_wasm

View file

@ -10,13 +10,6 @@ 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-emscripten -betterC --output-bc --od=.bc --singleobj --checkaction=C --of=' + output + ' '
ldc_cmd = 'ldc2 ' + shared_flags + ldc_flags + '-oq -mtriple=wasm32-unknown-unknown-wasm -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 = ''
ldc_flags = '--d-version=ECSEmscripten '
import_paths = ['source','tests']
build_tests = 0

View file

@ -1,6 +1,7 @@
import os
import ntpath
import sys
import imp
def compile(sources, output):
files = []
@ -13,7 +14,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-emscripten -betterC --output-bc --od=.bc --singleobj --checkaction=C --of=' + output + ' '
ldc_cmd = compiler + shared_flags + ldc_flags + '-oq -mtriple=wasm32-unknown-unknown-wasm -betterC --output-bc --od=.bc --singleobj --checkaction=C --of=' + output + ' '
for path in sources:
ldc_cmd += '-I' + path + ' '
@ -40,7 +41,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=SDL_209 --d-version=BindSDL_Static --d-version=BindSDL_Image --d-version=MM_USE_POSIX_THREADS '
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']
for arg in sys.argv[1:]:
@ -120,9 +121,8 @@ 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'
pack_cmd = os.path.expandvars('$EMSDK/upstream/emscripten') + '/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'
print('Packafing files: ' + pack_cmd)
os.system(pack_cmd)

View file

@ -27,7 +27,7 @@ executable('BubelECSDemos', [demos_src, external_src],
bindbc_loader_dep,
bindbc_sdl_dep,
cimgui_dep,
bubel_ecs_dep,
decs_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('ECSUtils', utils_src,
ecs_utils_lib = library('ecs_utils', utils_src,
include_directories : [demos_inc, external_inc, utils_inc],
link_args : link_args,
d_module_versions : versions,
dependencies : [
bubel_ecs_dep,
decs_dep,
bindbc_loader_dep,
bindbc_sdl_dep,
]

View file

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

View file

@ -58,7 +58,7 @@ threads_dep = dependency('threads')
d_versions = []
deps = []
if host_machine.cpu_family() == 'wasm32'
d_versions += 'ECSEmscripten'
else
# meson incorectly adds "-s USE_PTHREADS=1" to ldc2 invocation whe pthreads is added as dependency
# add it for non wasm builds

View file

@ -4,10 +4,129 @@ 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-2023, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.atomic;
public import core.atomic;
version (Emscripten) version = ECSEmscripten;
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-2023, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2019, 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-2023, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2019, 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-2023, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.core;

View file

@ -1,7 +1,7 @@
/************************************************************************************************************************
Entity module.
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2019, 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);
}
export void* getComponent(ushort component_id) const
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);
}
export bool hasComponent(ushort component_id) const
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;
}
export EntityMeta getMeta() const
EntityMeta getMeta() const
{
EntityMeta meta;
meta.block = gEntityManager.getMetaData(&this);
@ -85,7 +85,7 @@ struct EntityMeta
return cast(T*)getComponent(becsID!T);
}
export void* getComponent(ushort component_id) const
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);
}
export bool hasComponent(ushort component_id) const
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.
*/
export void* getComponent(ushort component_id) const nothrow @nogc
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,5 +1,5 @@
/************************************************************************************************************************
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.events;

View file

@ -1,5 +1,5 @@
/************************************************************************************************************************
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.hash_map;
@ -28,7 +28,7 @@ export ulong defaultHashFunc(T)(auto ref T t) nothrow @nogc
}
}
export ulong hash(byte[] array) nothrow @nogc
ulong hash(byte[] array) nothrow @nogc
{
ulong hash = 0;
@ -72,31 +72,31 @@ nothrow:
size_t length; // Used to compute loadFactor
size_t markerdDeleted;
export void clear()
void clear()
{
elements.clear();
length = 0;
markerdDeleted = 0;
}
export void reset()
void reset()
{
elements.reset();
length = 0;
markerdDeleted = 0;
}
export bool isIn(ref Key el)
bool isIn(ref Key el)
{
return getIndex(el) != getIndexEmptyValue;
}
export bool isIn(Key el)
bool isIn(Key el)
{
return getIndex(el) != getIndexEmptyValue;
}
export Value* getPtr()(auto ref Key k)
Value* getPtr()(auto ref Key k)
{
size_t index = getIndex(k);
if (index == getIndexEmptyValue)
@ -109,20 +109,20 @@ nothrow:
}
}
export ref Value get()(auto ref Key k)
ref Value get()(auto ref Key k)
{
size_t index = getIndex(k);
assert(index != getIndexEmptyValue);
return elements[index].keyValue.value;
}
deprecated("Use get with second parameter.") export auto ref Value getDefault()(
deprecated("Use get with second parameter.") auto ref Value getDefault()(
auto ref Key k, auto ref Value defaultValue)
{
return get(k, defaultValue);
}
export auto ref Value get()(auto ref Key k, auto ref Value defaultValue)
auto ref Value get()(auto ref Key k, auto ref Value defaultValue)
{
size_t index = getIndex(k);
if (index == getIndexEmptyValue)
@ -135,7 +135,7 @@ nothrow:
}
}
export ref Value getInsertDefault()(auto ref Key k, auto ref Value defaultValue)
ref Value getInsertDefault()(auto ref Key k, auto ref Value defaultValue)
{
size_t index = getIndex(k);
if (index == getIndexEmptyValue)
@ -148,7 +148,7 @@ nothrow:
}
export bool tryRemove(Key el)
bool tryRemove(Key el)
{
size_t index = getIndex(el);
if (index == getIndexEmptyValue)
@ -161,23 +161,23 @@ nothrow:
return true;
}
export void remove(Key el)
void remove(Key el)
{
bool ok = tryRemove(el);
assert(ok);
}
export ref Value opIndex()(auto ref Key key)
ref Value opIndex()(auto ref Key key)
{
return get(key);
}
export void opIndexAssign()(auto ref Value value, auto ref Key key)
void opIndexAssign()(auto ref Value value, auto ref Key key)
{
add(key, value);
}
export void add()(auto ref Key key, auto ref Value value)
void add()(auto ref Key key, auto ref Value value)
{
size_t index = getIndex(key);
if (index != getIndexEmptyValue)
@ -221,12 +221,12 @@ nothrow:
//int numA;
//int numB;
export size_t getIndex(Key el)
size_t getIndex(Key el)
{
return getIndex(el);
}
export size_t getIndex(ref Key el)
size_t getIndex(ref Key el)
{
mixin(doNotInline);
@ -260,7 +260,7 @@ nothrow:
}
export float getLoadFactor(size_t forElementsNum)
float getLoadFactor(size_t forElementsNum)
{
if (elements.length == 0)
{
@ -269,7 +269,7 @@ nothrow:
return cast(float) forElementsNum / (elements.length);
}
export void rehash()()
void rehash()()
{
mixin(doNotInline);
// Get all elements
@ -303,7 +303,7 @@ nothrow:
}
// foreach support
export int opApply(DG)(scope DG dg)
int opApply(DG)(scope DG dg)
{
int result;
foreach (ref Bucket gr; elements)
@ -336,36 +336,36 @@ nothrow:
return result;
}
export int byKey(scope int delegate(ref Key k) dg)
int byKey(scope int delegate(ref Key k) dg)
{
int result;
foreach (ref Key k; this)
{
result = (cast(int delegate(ref Key k) nothrow @nogc)dg)(k);
result = (cast(int delegate(ref Key k) nothrow)dg)(k);
if (result)
break;
}
return result;
}
export int byValue(scope int delegate(ref Value v) dg)
int byValue(scope int delegate(ref Value v) dg)
{
int result;
foreach (ref Value v; this)
{
result = (cast(int delegate(ref Value v) nothrow @nogc)dg)(v);
result = (cast(int delegate(ref Value v) nothrow)dg)(v);
if (result)
break;
}
return result;
}
export int byKeyValue(scope int delegate(ref Key k, ref Value v) dg)
int byKeyValue(scope int delegate(ref Key k, ref Value v) dg)
{
int result;
foreach (ref Key k, ref Value v; this)
{
result = (cast(int delegate(ref Key k, ref Value v) nothrow @nogc)dg)(k, v);
result = (cast(int delegate(ref Key k, ref Value v) nothrow)dg)(k, v);
if (result)
break;
}

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

@ -2,14 +2,16 @@
It's internal code!
This module contain implementation of standard functionality.
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2019, 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 (Emscripten)
version (ECSEmscripten)
{
extern (C) struct pthread_mutex_t
{
@ -27,6 +29,10 @@ version (Emscripten)
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;
@ -54,7 +60,7 @@ else
public import core.stdc.stdlib : qsort;
}
version (Emscripten)
version (ECSEmscripten)
{
}
else version (Windows)
@ -83,7 +89,7 @@ else version (Posix)
import core.sys.posix.stdlib : posix_memalign;
}
version (Emscripten)
version (ECSEmscripten)
{
private const uint max_alloca = 10000;
private __gshared byte[max_alloca] alloca_array;
@ -288,7 +294,7 @@ static struct Mallocator
posix_memalign(&ret, alignment, length); //ret = aligned_alloc(alignment, length);
else version (Windows)
ret = _aligned_malloc(length, alignment);
else version (Emscripten)
else version (ECSEmscripten)
posix_memalign(&ret, alignment, length); //malloc(length);
else
static assert(0, "Unimplemented platform!");
@ -335,7 +341,7 @@ static struct Mallocator
free(cast(void*) object);
else version (Windows)
_aligned_free(cast(void*) object);
else version (Emscripten)
else version (ECSEmscripten)
free(cast(void*) object);
else
static assert(0, "Unimplemented platform!");
@ -345,7 +351,7 @@ static struct Mallocator
struct Mutex
{
version (Emscripten)
version (ECSEmscripten)
{
void initialize() nothrow @nogc
{

View file

@ -1,7 +1,7 @@
/************************************************************************************************************************
System module.
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2019, 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(EntitiesData);
* void onUpdate(EntitesData);
* 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(EntitiesData) - called for every entity which are assigned to system (by adding new entity or changing its components)
* void onAddEntity(EntitesData) - 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,100 +89,38 @@ 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:
///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
void destroy()
{
import bubel.ecs.std : Mallocator;
disable();
if (m_destroy)
{
(cast(void function(void*) nothrow @nogc) m_destroy)(m_system_pointer);
m_destroy = null;
}
(cast(void function(void*)) m_destroy)(m_system_pointer);
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,5 +1,5 @@
/************************************************************************************************************************
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.traits;
@ -11,11 +11,8 @@ import std.traits;
*/
ref ushort becsID(T)()
{
/// Embed id in struct so export can be added to variable definition
static struct LocalStruct {
export __gshared ushort id = ushort.max;
}
return LocalStruct.id;
__gshared ushort id = ushort.max;
return id;
}
/************************************************************************************************************************

View file

@ -1,5 +1,5 @@
/************************************************************************************************************************
Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
Copyright: Copyright © 2018-2019, Dawid Masiukiewicz, Michał Masiukiewicz
License: BSD 3-clause, see LICENSE file in project root folder.
*/
module bubel.ecs.vector;
@ -14,13 +14,13 @@ import bubel.ecs.std;
import std.conv : emplace;
import std.traits : hasMember, isCopyable, TemplateOf, Unqual;
export @nogc @safe nothrow pure size_t nextPow2(size_t num)
@nogc @safe nothrow pure size_t nextPow2(size_t num)
{
return 1 << bsr(num) + 1;
}
export __gshared size_t gVectorsCreated = 0;
export __gshared size_t gVectorsDestroyed = 0;
__gshared size_t gVectorsCreated = 0;
__gshared size_t gVectorsDestroyed = 0;
struct Vector(T)
{
@ -28,19 +28,19 @@ struct Vector(T)
size_t used;
public:
export this()(T t)
this()(T t)
{
add(t);
}
export this(X)(X[] t) if (is(Unqual!X == Unqual!T))
this(X)(X[] t) if (is(Unqual!X == Unqual!T))
{
add(t);
}
/*static if (isCopyable!T) {
export this(this) {
this(this) {
T[] tmp = array[0 .. used];
array = null;
used = 0;
@ -52,17 +52,17 @@ public:
@disable this(this);
export ~this()
~this()
{
clear();
}
export void clear()
void clear()
{
removeAll();
}
export void removeAll()
void removeAll()
{
if (array !is null)
{
@ -77,17 +77,17 @@ public:
used = 0;
}
export bool empty() const
bool empty() const
{
return (used == 0);
}
export size_t length() const
size_t length() const
{
return used;
}
export void length(size_t newLength)
void length(size_t newLength)
{
if (newLength > used)
{
@ -111,12 +111,12 @@ public:
used = newLength;
}
export void reset()
void reset()
{
used = 0;
}
export void reserve(size_t numElements)
void reserve(size_t numElements)
{
if (numElements > array.length)
{
@ -124,12 +124,12 @@ public:
}
}
export size_t capacity()
size_t capacity()
{
return array.length - used;
}
export void extend(size_t newNumOfElements)
void extend(size_t newNumOfElements)
{
auto oldArray = manualExtend(array, newNumOfElements);
if (oldArray !is null)
@ -138,14 +138,14 @@ public:
}
}
export @nogc void freeData(void[] data)
@nogc void freeData(void[] data)
{
// 0x0F probably invalid value for pointers and other types
memset(data.ptr, 0x0F, data.length); // Makes bugs show up xD
free(data.ptr);
}
export static void[] manualExtend(ref T[] array, size_t newNumOfElements = 0)
static void[] manualExtend(ref T[] array, size_t newNumOfElements = 0)
{
if (newNumOfElements == 0)
newNumOfElements = 2;
@ -161,7 +161,7 @@ public:
return (cast(void*) oldArray.ptr)[0 .. oldArray.length * T.sizeof];
}
export Vector!T copy()()
Vector!T copy()()
{
Vector!T duplicate;
duplicate.reserve(used);
@ -169,12 +169,12 @@ public:
return duplicate;
}
/*export bool canAddWithoutRealloc(uint elemNum = 1)
/*bool canAddWithoutRealloc(uint elemNum = 1)
{
return used + elemNum <= array.length;
}*/
export void add()(T t)
void add()(T t)
{
if (used >= array.length)
{
@ -185,7 +185,7 @@ public:
}
/// Add element at given position moving others
export void add()(T t, size_t pos)
void add()(T t, size_t pos)
{
assert(pos <= used);
if (used >= array.length)
@ -201,7 +201,7 @@ public:
used++;
}
export void add(X)(X[] t) if (is(Unqual!X == Unqual!T))
void add(X)(X[] t) if (is(Unqual!X == Unqual!T))
{
if (used + t.length > array.length)
{
@ -214,7 +214,7 @@ public:
used += t.length;
}
export void remove(size_t elemNum)
void remove(size_t elemNum)
{
//destroy(array[elemNum]);
static if (__traits(hasMember, T, "__xdtor"))
@ -226,7 +226,7 @@ public:
used--;
}
export void removeStable()(size_t elemNum)
void removeStable()(size_t elemNum)
{
used--;
foreach (i; 0 .. used)
@ -235,7 +235,7 @@ public:
}
}
export bool tryRemoveElement()(T elem)
bool tryRemoveElement()(T elem)
{
foreach (i, ref el; array[0 .. used])
{
@ -248,65 +248,65 @@ public:
return false;
}
export void removeElement()(T elem)
void removeElement()(T elem)
{
bool ok = tryRemoveElement(elem);
assert(ok, "There is no such an element in vector");
}
export ref T opIndex(size_t elemNum) const
ref T opIndex(size_t elemNum) const
{
//debug assert(elemNum < used, "Range violation [index]");
return *cast(T*)&array.ptr[elemNum];
}
export auto opSlice()
auto opSlice()
{
return array.ptr[0 .. used];
}
export T[] opSlice(size_t x, size_t y)
T[] opSlice(size_t x, size_t y)
{
assert(y <= used);
return array.ptr[x .. y];
}
export size_t opDollar()
size_t opDollar()
{
return used;
}
export void opAssign(X)(X[] slice)
void opAssign(X)(X[] slice)
{
reset();
this ~= slice;
}
export void opOpAssign(string op)(T obj)
void opOpAssign(string op)(T obj)
{
//static assert(op == "~");
add(obj);
}
export void opOpAssign(string op, X)(X[] obj)
void opOpAssign(string op, X)(X[] obj)
{
//static assert(op == "~");
add(obj);
}
export void opIndexAssign()(T obj, size_t elemNum)
void opIndexAssign()(T obj, size_t elemNum)
{
assert(elemNum < used, "Range viloation");
array[elemNum] = obj;
}
export void opSliceAssign()(T[] obj, size_t a, size_t b)
void opSliceAssign()(T[] obj, size_t a, size_t b)
{
assert(b <= used && a <= b, "Range viloation");
array.ptr[a .. b] = obj;
}
export bool opEquals()(auto ref const Vector!(T) r) const
bool opEquals()(auto ref const Vector!(T) r) const
{
return used == r.used && array.ptr[0 .. used] == r.array.ptr[0 .. r.used];
}

View file

@ -113,31 +113,6 @@ 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;
@ -268,82 +243,7 @@ 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

@ -142,34 +142,5 @@ 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();
}