Compare commits

..

3 commits

Author SHA1 Message Date
cd1f81127a Workaround for issue with hasMember and opDispatch 2023-04-23 21:52:18 +02:00
3a3a9e0341 Add @nogc UDA, fix some small issues, xmake changes
-add @nogc to some functions where it was missing
-fix compilation issue in mallocator
-fix Meson build
-some work on xmake build
2023-03-23 22:31:20 +01:00
5e123d96b3 Add xmake 2023-03-09 14:08:42 +01:00
16 changed files with 340 additions and 253 deletions

1
.gitignore vendored
View file

@ -6,6 +6,7 @@
!.gitignore
!codecov.yml
!skeleton.html
!**/xmake.lua
!**/meson.build
!**/*.wrap
!meson_options.txt

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

@ -7,9 +7,9 @@
"description": "Dynamic Entity Component System",
"copyright": "Copyright © 2018-2023, Michał Masiukiewicz, Dawid Masiukiewicz",
"license": "BSD 3-clause",
"sourcePaths" : ["source\/"],
"sourcePaths" : ["source/"],
"excludedSourceFiles":[
"source\/ecs\/traits.d"
"source/ecs/traits.d"
],
"configurations" : [
{
@ -21,7 +21,7 @@
"sourceFiles" : ["tests/tests.d"],
"targetType" : "executable",
"excludedSourceFiles":[
"source\/win_dll.d"
"source/win_dll.d"
]
},
{
@ -45,7 +45,7 @@
"sourcePaths": ["source/","tests/"],
"mainSourceFile":"tests/runner.d",
"excludedSourceFiles":[
"source\/win_dll.d",
"source/win_dll.d",
"tests/tests.d"
],
"dflags": [
@ -58,7 +58,7 @@
"sourcePaths": ["source/","tests/"],
"mainSourceFile":"tests/runner.d",
"excludedSourceFiles":[
"source\/win_dll.d",
"source/win_dll.d",
"tests/tests.d"
],
"dflags": [
@ -70,7 +70,7 @@
"name" : "library-betterC",
"targetType" : "library",
"excludedSourceFiles":[
"source\/win_dll.d"
"source/win_dll.d"
],
"dflags": [
"-betterC",
@ -101,7 +101,7 @@
"targetType" : "executable",
"sourceFiles" : ["tests/tests.d"],
"excludedSourceFiles":[
"source\/win_dll.d"
"source/win_dll.d"
],
"dflags": [
"-betterC"
@ -125,7 +125,7 @@
"sourcePaths": ["source/","tests/"],
"mainSourceFile":"tests/runner.d",
"excludedSourceFiles":[
"source\/win_dll.d",
"source/win_dll.d",
"tests/tests.d"
]
}

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

@ -9,5 +9,124 @@ 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

@ -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 nothrow @nogc
{
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 nothrow @nogc
{
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 nothrow @nogc
{
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 nothrow @nogc
{
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 nothrow @nogc
{
const EntityManager.EntityInfo* info = block.type_info;
if (component_id >= info.deltas.length || info.deltas[component_id] == 0)return false;
@ -124,7 +124,7 @@ export struct EntityTemplate
/************************************************************************************************************************
Get specified component. If component doesn't exist function return null. Returned pointer is valid during EntityTemplate lifetime.
*/
T* getComponent(T)() nothrow @nogc
T* getComponent(T)()
{
if(becsID!T >= info.tmpl_deltas.length || info.tmpl_deltas[becsID!T] == ushort.max)return null;
return cast(T*)(entity_data.ptr + info.tmpl_deltas[becsID!T]);
@ -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

@ -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;

View file

@ -27,8 +27,6 @@ alias SerializeVector = bubel.ecs.vector.Vector!ubyte;
///Global EntityManager used for everything.
export __gshared EntityManager* gEntityManager = null;
version(D_BetterC) version = NoDRuntime;
/************************************************************************************************************************
Entity manager is responsible for everything.
@ -361,7 +359,7 @@ export struct EntityManager
void registerSystem(Sys)(int priority, const(char)[] pass_name)
{
ushort pass = passes_map.get(pass_name, ushort.max);
version (NoDRuntime)
version (D_BetterC)
assert(pass != ushort.max, "Update pass doesn't exist.");
else
assert(pass != ushort.max, "Update pass (Name " ~ pass_name ~ ") doesn't exist.");
@ -383,7 +381,7 @@ export struct EntityManager
assert(register_state,
"registerSystem must be called between beginRegister() and endRegister().");
version (NoDRuntime)
version (D_BetterC)
assert(pass < passes.length, "Update pass doesn't exist.");
else
assert(pass < passes.length, "Update pass (ID " ~ pass.to!string ~ ") doesn't exist.");
@ -392,7 +390,7 @@ export struct EntityManager
enum SystemName = fullName!Sys;
//enum SystemName = Sys.stringof;
System system = System();
System system;
system.m_pass = pass;
// static if (!(hasMember!(Sys, "system_id")) || !is(typeof(Sys.system_id) == ushort))
@ -842,7 +840,7 @@ export struct EntityManager
foreach (iii, comp_info; components_info.req)
{
ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max);
version (NoDRuntime)
version (D_BetterC)
assert(comp != ushort.max,
"Can't register system \"" ~ SystemName
~ "\" due to non existing component.");
@ -854,7 +852,7 @@ export struct EntityManager
foreach (iii, comp_info; components_info.excluded)
{
ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max);
version (NoDRuntime)
version (D_BetterC)
assert(comp != ushort.max,
"Can't register system \"" ~ SystemName
~ "\" due to non existing component.");
@ -866,7 +864,7 @@ export struct EntityManager
foreach (iii, comp_info; components_info.optional)
{
ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max);
version (NoDRuntime)
version (D_BetterC)
assert(comp != ushort.max,
"Can't register system \"" ~ SystemName
~ "\" due to non existing component.");
@ -878,7 +876,7 @@ export struct EntityManager
foreach (iii, comp_info; components_info.readonly)
{
ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max);
version (NoDRuntime)
version (D_BetterC)
assert(comp != ushort.max,
"Can't register system \"" ~ SystemName
~ "\" due to non existing component.");
@ -890,7 +888,7 @@ export struct EntityManager
foreach (iii, comp_info; components_info.mutable)
{
ushort comp = components_map.get(cast(char[]) comp_info.type, ushort.max);
version (NoDRuntime)
version (D_BetterC)
assert(comp != ushort.max,
"Can't register system \"" ~ SystemName
~ "\" due to non existing component.");
@ -1200,7 +1198,7 @@ export struct EntityManager
{
ushort comp = external_dependencies_map.get(cast(const(char)[]) comp_info.type,
ushort.max);
version (NoDRuntime)
version (D_BetterC)
assert(comp != ushort.max,
"Can't register system \"" ~ SystemName
~ "\" due to non existing dependency.");
@ -1213,7 +1211,7 @@ export struct EntityManager
foreach (iii, comp_info; components_info.writableDeps)
{
ushort comp = external_dependencies_map.get(cast(char[]) comp_info.type, ushort.max);
version (NoDRuntime)
version (D_BetterC)
assert(comp != ushort.max,
"Can't register system \"" ~ SystemName
~ "\" due to non existing dependency.");
@ -1307,7 +1305,7 @@ export struct EntityManager
// static assert(0, "Add \"mixin ECS.Component;\" in top of component structure;");
// }
static if (hasMember!(Comp, "onDestroy") && isFunction!(Comp.onDestroy)
static if (__traits(hasMember, Comp.init, "onDestroy") && isFunction!(Comp.onDestroy)
&& is(ReturnType!(Comp.onDestroy) == void)
&& Parameters!(Comp.onDestroy).length == 0)
{
@ -1319,7 +1317,7 @@ export struct EntityManager
info.destroy_callback = &callDestroy;
}
static if (hasMember!(Comp, "onCreate") && isFunction!(Comp.onCreate)
static if (__traits(hasMember, Comp.init, "onCreate") && isFunction!(Comp.onCreate)
&& is(ReturnType!(Comp.onCreate) == void) && Parameters!(Comp.onCreate).length == 0)
{
static void callCreate(void* pointer) nothrow @nogc
@ -1858,7 +1856,6 @@ export struct EntityManager
*/
export EntityTemplate* allocateTemplate(EntityTemplate* copy_tmpl)
{
assert(copy_tmpl, "copy_tmpl can't be null");
EntityTemplate* tmpl = Mallocator.make!EntityTemplate;
tmpl.info = copy_tmpl.info;
tmpl.entity_data = Mallocator.makeArray(copy_tmpl.entity_data);
@ -1873,66 +1870,46 @@ export struct EntityManager
*/
export EntityInfo* getEntityInfo(ushort[] ids)
{
if(ids.length == 0)ids = null;
EntityInfo* info = entities_infos.get(ids, null);
if (info is null)
{
info = Mallocator.make!EntityInfo;
info.components = Mallocator.makeArray(ids);
info.deltas = Mallocator.makeArray!ushort(ids[$ - 1] + 1);
info.size = EntityID.sizeof;
info.alignment = EntityID.alignof;
if(ids is null)
info.tmpl_deltas = Mallocator.makeArray!ushort(ids[$ - 1] + 1, ushort.max);
uint components_size = EntityID.sizeof;
foreach (i, id; ids)
{
uint block_memory = cast(uint)(
m_page_size - EntitiesBlock.sizeof - info.size);
uint entites_in_block = block_memory / info.size;
info.max_entities = cast(ushort) entites_in_block;
info.alignment = max(info.alignment, components[id].alignment);
alignNum(info.size, components[id].alignment);
info.tmpl_deltas[id] = info.size;
info.size += components[id].size;
components_size += components[id].size;
}
else
alignNum(info.size, info.alignment);
uint block_memory = cast(uint)(
m_page_size - EntitiesBlock.sizeof - (info.size - components_size));
//uint entity_comps_size = EntityID.sizeof;
uint mem_begin = EntitiesBlock.sizeof;
uint entites_in_block = block_memory / info.size; //entity_comps_size;
info.max_entities = cast(ushort) entites_in_block;
ushort current_delta = cast(ushort)(mem_begin + entites_in_block * EntityID.sizeof);
foreach (i, id; ids)
{
uint components_size = EntityID.sizeof;
info.components = Mallocator.makeArray(ids);
info.deltas = Mallocator.makeArray!ushort(ids[$ - 1] + 1);
info.tmpl_deltas = Mallocator.makeArray!ushort(ids[$ - 1] + 1, ushort.max);
foreach (i, id; ids)
{
info.alignment = max(info.alignment, components[id].alignment);
alignNum(info.size, components[id].alignment);
info.tmpl_deltas[id] = info.size;
info.size += components[id].size;
components_size += components[id].size;
}
alignNum(info.size, info.alignment);
uint block_memory = cast(uint)(
m_page_size - EntitiesBlock.sizeof - (info.size - components_size));
//uint entity_comps_size = EntityID.sizeof;
uint mem_begin = EntitiesBlock.sizeof;
uint entites_in_block = block_memory / info.size; //entity_comps_size;
info.max_entities = cast(ushort) entites_in_block;
ushort current_delta = cast(ushort)(mem_begin + entites_in_block * EntityID.sizeof);
foreach (i, id; ids)
{
if (current_delta == 0)
current_delta = ushort.max;
alignNum(current_delta, components[id].alignment);
info.deltas[id] = cast(ushort) current_delta;
current_delta += entites_in_block * components[id].size;
}
info.comp_add_info = Mallocator.makeArray!(EntityInfo*)(info.deltas.length);
info.comp_rem_info = Mallocator.makeArray!(EntityInfo*)(info.deltas.length);
foreach (comp; info.components)
{
info.comp_add_info[comp] = info;
info.comp_rem_info[comp] = null;
}
if (current_delta == 0)
current_delta = ushort.max;
alignNum(current_delta, components[id].alignment);
info.deltas[id] = cast(ushort) current_delta;
current_delta += entites_in_block * components[id].size;
}
info.systems = Mallocator.makeArray!bool(systems.length);
@ -1953,6 +1930,15 @@ export struct EntityManager
addSystemCaller(*info, cast(uint) i);
}
info.comp_add_info = Mallocator.makeArray!(EntityInfo*)(info.deltas.length);
info.comp_rem_info = Mallocator.makeArray!(EntityInfo*)(info.deltas.length);
foreach (comp; info.components)
{
info.comp_add_info[comp] = info;
info.comp_rem_info[comp] = null;
}
entities_infos.add(info.components, info);
generateListeners(info);
@ -2516,7 +2502,7 @@ export struct EntityManager
*Params:
*id = ID of entity to be copyied.
*/
export Entity* addEntityCopy(EntityID id)
export Entity* addEntityCopy(EntityID id) nothrow @nogc
{
Entity* entity = getEntity(id);
EntitiesBlock* block = getMetaData(entity);
@ -2566,9 +2552,9 @@ export struct EntityManager
use you should save ID instead of pointer.
Params:
tmpl = pointer entity template allocated by EntityManager. Can be null in which case empty entity would be added (entity without components)
tmpl = pointer entity template allocated by EntityManager.
*/
export Entity* addEntity(EntityTemplate* tmpl)
export Entity* addEntity(EntityTemplate* tmpl) nothrow @nogc
{
return addEntity(tmpl, null);
}
@ -2578,14 +2564,12 @@ export struct EntityManager
use you should save ID instead of pointer.
Params:
tmpl = pointer entity template allocated by EntityManager. Can be null in which case empty entity would be added (entity without components)
tmpl = pointer entity template allocated by EntityManager.
replacement = list of components references to used. Memory form list replace data from template inside new entity. Should be used only for data which vary between most entities (like 3D position etc.)
*/
export Entity* addEntity(EntityTemplate* tmpl, ComponentRef[] replacement)
export Entity* addEntity(EntityTemplate* tmpl, ComponentRef[] replacement) nothrow @nogc
{
EntityInfo* info = void;
if(tmpl)info = tmpl.info;
else info = getEntityInfo(null);
EntityInfo* info = tmpl.info;
ushort index = 0;
EntitiesBlock* block;
@ -2680,7 +2664,7 @@ export struct EntityManager
/************************************************************************************************************************
Return block with free space for selected EntityInfo. Additional this function is multithread safe.
*/
private EntitiesBlock* findBlockWithFreeSpaceMT(EntityInfo* info)
private EntitiesBlock* findBlockWithFreeSpaceMT(EntityInfo* info) nothrow @nogc
{
EntitiesBlock* block = info.last_block;
@ -2734,7 +2718,7 @@ export struct EntityManager
Params:
id = id of entity to remove
*/
export void removeEntity(EntityID id)
export void removeEntity(EntityID id) nothrow @nogc
{
threads[threadID].entitesToRemove.add(id);
}
@ -3357,16 +3341,6 @@ export struct EntityManager
export ~this() nothrow @nogc
{
}
export void opAssign(ComponentInfo c)
{
size = c.size;
alignment = c.alignment;
init_data = c.init_data;
destroy_callback = c.destroy_callback;
create_callback = c.create_callback;
}
///Component size
ushort size;
///Component data alignment
@ -3472,7 +3446,7 @@ export struct EntityManager
break;
}
}
if (components.length == 0 || id > components[$ - 1])
if (id > components[$ - 1])
ids[len++] = id;
assert(len == components.length + 1);

View file

@ -7,9 +7,11 @@ 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;
@ -273,7 +279,7 @@ static struct Mallocator
static if (__traits(isPOD, T))
{
__gshared immutable T init = T.init;
__gshared T init = T.init;
memcpy(ret, &init, T.sizeof);
}
else static if (is(T == struct))
@ -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

@ -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
@ -108,7 +108,7 @@ struct System
package:
///destory system. Dispose all data
export void destroy() nothrow @nogc
void destroy() nothrow @nogc
{
import bubel.ecs.std : Mallocator;

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

@ -144,32 +144,3 @@ unittest
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

@ -13,9 +13,9 @@ tests_src = files(
exe = executable('BubelECSTests',
tests_src,
include_directories : [inc, include_directories('..')],
d_args : args,
link_args : link_args,
dependencies : decs_dep,
# d_args : args,
# link_args : link_args,
dependencies : bubel_ecs_dep,
)
test('basic-tests', exe)

View file

@ -645,7 +645,7 @@ version (unittest)
else:
extern (C) int main()
{
version (D_BetterC)printf("BetterC\n");
void dispatch(EntityManager.JobGroup jobs) nothrow @nogc
{
foreach (job; jobs.jobs)

116
xmake.lua Normal file
View file

@ -0,0 +1,116 @@
add_rules("mode.debug", "mode.release")
if get_config("toolchain") == "ldc" then
add_ldflags("-linker=lld", {force = true})
end
option("betterC")
set_default(false)
if get_config("toolchain") == "gdc" then
add_dcflags("-fno-druntime")
elseif get_config("toolchain") == "dmd" then
add_dcflags("-betterC") -- DMD doesn't need -betterC flag during linking!?
add_ldflags("-betterC", {force = true})
add_shflags("-betterC", {force = true})
else
add_ldflags("-betterC", {force = true}) -- -betterC flag isn't accepted for LDC for some reason
add_dcflags("-betterC", {force = true})
add_shflags("-betterC", {force = true})
end
target("bubel-ecs")
set_kind("shared")
set_basename("BubelECS")
add_files("source/bubel/**.d")
add_includedirs("source", {public = true})
set_options("betterC")
target("tests")
set_kind("binary")
set_basename("BubelECSTests")
add_ldflags("-L-rpath=.", {force = true})
add_files("tests/*.d|tests.d")
add_includedirs(".", {public = true})
set_options("betterC")
add_deps("bubel-ecs")
add_dcflags("-unittest")
target("test")
set_kind("binary")
set_basename("BubelECSTest")
-- add_rpathdirs(".") -- this doesn't work completely
add_ldflags("-L-rpath=.", {force = true})
add_files("tests/tests.d")
add_includedirs(".", {public = true})
set_options("betterC")
add_deps("bubel-ecs")
--
-- If you want to known more usage about xmake, please see https://xmake.io
--
-- ## FAQ
--
-- You can enter the project directory firstly before building project.
--
-- $ cd projectdir
--
-- 1. How to build project?
--
-- $ xmake
--
-- 2. How to configure project?
--
-- $ xmake f -p [macosx|linux|iphoneos ..] -a [x86_64|i386|arm64 ..] -m [debug|release]
--
-- 3. Where is the build output directory?
--
-- The default output directory is `./build` and you can configure the output directory.
--
-- $ xmake f -o outputdir
-- $ xmake
--
-- 4. How to run and debug target after building project?
--
-- $ xmake run [targetname]
-- $ xmake run -d [targetname]
--
-- 5. How to install target to the system directory or other output directory?
--
-- $ xmake install
-- $ xmake install -o installdir
--
-- 6. Add some frequently-used compilation flags in xmake.lua
--
-- @code
-- -- add debug and release modes
-- add_rules("mode.debug", "mode.release")
--
-- -- add macro defination
-- add_defines("NDEBUG", "_GNU_SOURCE=1")
--
-- -- set warning all as error
-- set_warnings("all", "error")
--
-- -- set language: c99, c++11
-- set_languages("c99", "c++11")
--
-- -- set optimization: none, faster, fastest, smallest
-- set_optimize("fastest")
--
-- -- add include search directories
-- add_includedirs("/usr/include", "/usr/local/include")
--
-- -- add link libraries and search directories
-- add_links("tbox")
-- add_linkdirs("/usr/local/lib", "/usr/lib")
--
-- -- add system link libraries
-- add_syslinks("z", "pthread")
--
-- -- add compilation and link flags
-- add_cxflags("-stdnolib", "-fno-strict-aliasing")
-- add_ldflags("-L/usr/local/lib", "-lpthread", {force = true})
--
-- @endcode
--