diff --git a/.gitignore b/.gitignore index 68b8c91..f203f51 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,8 @@ perf.data.old *.obj *.exe *.lib -*.so \ No newline at end of file +*.so +*.def +*.lib +*.dll +*.exp \ No newline at end of file diff --git a/dub.json b/dub.json index 1106532..2312e33 100755 --- a/dub.json +++ b/dub.json @@ -16,6 +16,18 @@ "name" : "tests", "sourcePaths" : ["source\/","tests\/"], "targetType" : "executable" + }, + { + "name" : "dynlib", + "targetType" : "dynamicLibrary" + }, + { + "name" : "sources", + "targetType" : "dynamicLibrary", + "dflags": [ + "-Hdimport/", + "-op" + ] } ] } \ No newline at end of file diff --git a/import/ecs/ecs.di b/import/ecs/ecs.di new file mode 100644 index 0000000..13041de --- /dev/null +++ b/import/ecs/ecs.di @@ -0,0 +1,112 @@ +// D import file generated from 'source\ecs\ecs.d' +module ecs.ecs; +import std.stdio; +version (Design) +{ + alias SytemFuncType = void function(ref SystemCallData data, void* componentsStart); + struct HasComponentsStore + { + ulong[4] bits; + bool has(HasComponentsStore components); + bool notIn(HasComponentsStore components); + int length(); + } + struct ComponentInfo + { + int size; + int aligment; + SerializeJSON funsSerJ; + SerializeBiN funcSerB; + } + struct System + { + HasComponentsStore requiredComponents; + HasComponentsStore absenComponents; + HasComponentsStore maybeComponents; + bool enabled; + int priority; + SytemFuncType func; + } + struct SystemCallData + { + System* system; + int[] componentsDt; + } + struct EntityTypeData + { + HasComponentsStore components; + int[] deltas; + int totalSize; + int totalAligment = 8; + SystemCallData[] systems; + } + struct EntitiesBlock + { + EntityTypeData* typeData; + Entity* freeEntitySlot; + EntitiesBlock* nextBlock; + } + struct EntityID + { + ulong id = (ulong).max; + static immutable notUsedValue = EntityID((ulong).max); + } + struct Entity + { + EntityID entityID = EntityID.notUsedValue; + union + { + string name; + Entity* nextFreeSlot; + } + uint group; + EntityID entityID; + } + struct Template + { + HasComponentsStore hasComp; + Entity* entity; + } + struct Manager + { + EntityAllocator entityArrayAllcoator; + ComponentInfo[] components; + System[] systems; + HashMap!(HasComponentsStore, EntitiesBlock*) entitiesDatas; + HashMapTwoWays!(string, Entity*) nameMap; + HashMapTwoWays!(EntityID, Entity*) idMap; + EntitiesBlock* getEntitiesBlock(HasComponentsStore hasComponents); + EntitiesBlock* addNewBlock(HasComponentsStore hasComponents, EntitiesBlock* firstBlock); + void alignNum(ref int num, int aligment); + EntityTypeData* newEntityTypeData(HasComponentsStore hasComponents); + void addEntity(Template* templ); + void addSystem(Func)(int priority) + { + HasComponentsStore requiredComponents; + HasComponentsStore absenComponents; + HasComponentsStore maybeComponents; + void systemCaller(ref SystemCallData data, void* componentsStart); + System* system = new System(&systemCaller, entTypeData); + systems ~= system; + foreach (ref entTypeData; entitiesDatas) + { + if (!entTypeData.hasComp.has(requiredComponents) || !entTypeData.hasComp.notIn(absenComponents)) + { + continue; + } + entTypeData.systems ~= system; + } + } + } + void someSystem(CompA a, CompB b, CompC* c); + void main(); + class System + { + void start(); + void end(); + void update(ref ObjRend a); + void useEvent(EventData evvv, ref ObjRend a); + } + alias SerializeVector = ubyte[]; + __gshared EntityManager gEntityManager; +} diff --git a/import/ecs/entity.di b/import/ecs/entity.di new file mode 100644 index 0000000..db6208b --- /dev/null +++ b/import/ecs/entity.di @@ -0,0 +1,30 @@ +// D import file generated from 'source\ecs\entity.d' +module ecs.entity; +import ecs.manager; +struct EntityID +{ + uint id; + uint counter; +} +struct Entity +{ + EntityID id; + void updateID(); + T* getComponent(T)() + { + EntityManager.EntitiesBlock* block = gEM.getMetaData(&this); + EntityManager.EntityInfo* info = block.type_data; + if (T.component_id >= info.deltas.length || info.deltas[T.component_id] == 0) + return null; + return cast(T*)(cast(void*)&this + info.deltas[T.component_id]); + } +} +export struct EntityTemplate +{ + ubyte[] entity_data; + EntityManager.EntityInfo* info; + T* getComponent(T)() + { + return cast(T*)(entity_data.ptr + info.deltas[T.component_id]); + } +} diff --git a/import/ecs/entity_allocator.di b/import/ecs/entity_allocator.di new file mode 100644 index 0000000..66472fd --- /dev/null +++ b/import/ecs/entity_allocator.di @@ -0,0 +1,11 @@ +// D import file generated from 'source\ecs\entity_allocator.d' +module ecs.entity_allocator; +import ecs.manager; +import std.experimental.allocator; +import std.experimental.allocator.mallocator : AlignedMallocator, Mallocator; +struct EntityAllocator +{ + void* next_block; + void* getBlock(); + private void allocBlock(); +} diff --git a/import/ecs/events.di b/import/ecs/events.di new file mode 100644 index 0000000..73085c6 --- /dev/null +++ b/import/ecs/events.di @@ -0,0 +1,9 @@ +// D import file generated from 'source\ecs\events.d' +module ecs.events; +struct Event +{ + uint type; +} +class EventManager +{ +} diff --git a/import/ecs/hash_map.di b/import/ecs/hash_map.di new file mode 100644 index 0000000..d621ad8 --- /dev/null +++ b/import/ecs/hash_map.di @@ -0,0 +1,328 @@ +// D import file generated from 'source\ecs\hash_map.d' +module ecs.hash_map; +import std.traits; +import ecs.vector; +import ecs.traits; +enum doNotInline = "version(DigitalMars)pragma(inline,false);version(LDC)pragma(LDC_never_inline);"; +private enum HASH_EMPTY = 0; +private enum HASH_DELETED = 1; +private enum HASH_FILLED_MARK = ulong(1) << 8 * (ulong).sizeof - 1; +export ulong defaultHashFunc(T)(auto ref T t) +{ + static if (isIntegral!T) + { + return hashInt(t); + } + else + { + return hashInt(t.hashOf); + } +} +export nothrow @nogc @safe ulong hashInt(ulong x); +struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) +{ + alias Key = KeyPar; + alias Value = ValuePar; + enum rehashFactor = 0.75; + enum size_t getIndexEmptyValue = size_t.max; + static struct KeyVal + { + Key key; + Value value; + } + static struct Bucket + { + ulong hash; + KeyVal keyValue; + } + Vector!Bucket elements; + size_t length; + size_t markerdDeleted; + export void clear() + { + elements.clear(); + length = 0; + markerdDeleted = 0; + } + export void reset() + { + elements.reset(); + length = 0; + markerdDeleted = 0; + } + export bool isIn(ref Key el) + { + return getIndex(el) != getIndexEmptyValue; + } + export bool isIn(Key el) + { + return getIndex(el) != getIndexEmptyValue; + } + export Value* getPtr()(auto ref Key k) + { + size_t index = getIndex(k); + if (index == getIndexEmptyValue) + { + return null; + } + else + { + return &elements[index].keyValue.value; + } + } + export 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()(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) + { + size_t index = getIndex(k); + if (index == getIndexEmptyValue) + { + return defaultValue; + } + else + { + return elements[index].keyValue.value; + } + } + export ref Value getInsertDefault()(auto ref Key k, auto ref Value defaultValue) + { + size_t index = getIndex(k); + if (index == getIndexEmptyValue) + { + add(k, defaultValue); + } + index = getIndex(k); + assert(index != getIndexEmptyValue); + return elements[index].keyValue.value; + } + export bool tryRemove(Key el) + { + size_t index = getIndex(el); + if (index == getIndexEmptyValue) + { + return false; + } + length--; + elements[index].hash = HASH_DELETED; + markerdDeleted++; + return true; + } + export void remove(Key el) + { + bool ok = tryRemove(el); + assert(ok); + } + export ref Value opIndex()(auto ref Key key) + { + return get(key); + } + export void opIndexAssign()(auto ref Value value, auto ref Key key) + { + add(key, value); + } + export void add()(auto ref Key key, auto ref Value value) + { + size_t index = getIndex(key); + if (index != getIndexEmptyValue) + { + elements[index].keyValue.value = value; + return ; + } + if (getLoadFactor(length + 1) > rehashFactor || getLoadFactor(length + markerdDeleted) > rehashFactor) + { + rehash(); + } + length++; + immutable ulong hash = hashFunc(key) | HASH_FILLED_MARK; + immutable size_t rotateMask = elements.length - 1; + index = hash & rotateMask; + while (true) + { + Bucket* gr = &elements[index]; + if ((gr.hash & HASH_FILLED_MARK) == 0) + { + if (gr.hash == HASH_DELETED) + { + markerdDeleted--; + } + gr.hash = hash; + gr.keyValue.key = key; + gr.keyValue.value = value; + return ; + } + index++; + index = index & rotateMask; + } + } + export size_t getIndex(Key el) + { + return getIndex(el); + } + export size_t getIndex(ref Key el) + { + mixin(doNotInline); + immutable size_t groupsLength = elements.length; + if (groupsLength == 0) + { + return getIndexEmptyValue; + } + immutable ulong hash = hashFunc(el) | HASH_FILLED_MARK; + immutable size_t rotateMask = groupsLength - 1; + size_t index = hash & rotateMask; + while (true) + { + Bucket* gr = &elements[index]; + if (gr.hash == hash && (gr.keyValue.key == el)) + { + return index; + } + if (gr.hash == HASH_EMPTY) + { + return getIndexEmptyValue; + } + index++; + index = index & rotateMask; + } + } + export float getLoadFactor(size_t forElementsNum) + { + if (elements.length == 0) + { + return 1; + } + return cast(float)forElementsNum / elements.length; + } + export void rehash() + { + mixin(doNotInline); + Vector!KeyVal allElements; + allElements.reserve(elements.length); + foreach (ref Bucket el; elements) + { + if ((el.hash & HASH_FILLED_MARK) == 0) + { + el.hash = HASH_EMPTY; + continue; + } + el.hash = HASH_EMPTY; + allElements ~= el.keyValue; + } + if (getLoadFactor(length + 1) > rehashFactor) + { + elements.length = (elements.length ? elements.length : 4) << 1; + } + foreach (i, ref el; allElements) + { + add(el.key, el.value); + } + length = allElements.length; + markerdDeleted = 0; + } + export int opApply(DG)(scope DG dg) + { + int result; + foreach (ref Bucket gr; elements) + { + if ((gr.hash & HASH_FILLED_MARK) == 0) + { + continue; + } + static if (isForeachDelegateWithTypes!(DG, Key)) + { + result = dg(gr.keyValue.key); + } + else + { + static if (isForeachDelegateWithTypes!(DG, Value)) + { + result = dg(gr.keyValue.value); + } + else + { + static if (isForeachDelegateWithTypes!(DG, Key, Value)) + { + result = dg(gr.keyValue.key, gr.keyValue.value); + } + else + { + static assert(0); + } + } + } + if (result) + break; + } + return result; + } + export int byKey(scope int delegate(Key k) dg) + { + int result; + foreach (ref Key k; this) + { + result = dg(k); + if (result) + break; + } + return result; + } + export int byValue(scope int delegate(ref Value k) dg) + { + int result; + foreach (ref Value v; this) + { + result = dg(v); + if (result) + break; + } + return result; + } + 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); + if (result) + break; + } + return result; + } + import std.format : FormatSpec, formatValue; + export void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) + { + formatValue(sink, '[', fmt); + foreach (ref k, ref v; &byKeyValue) + { + formatValue(sink, k, fmt); + formatValue(sink, ':', fmt); + formatValue(sink, v, fmt); + formatValue(sink, ", ", fmt); + } + formatValue(sink, ']', fmt); + } +} +static void dumpHashMapToJson(T)(ref T map, string path = "HashMapDump.json") +{ + Vector!char data; + import std.file; + import mutils.serializer.json; + JSONSerializer.instance.serialize!(Load.no)(map, data); + std.file.write(path, data[]); +} +static void printHashMap(T)(ref T map) +{ + import std.stdio; + writeln(T.stringof, " dump:\x0a"); + foreach (k, v; &map.byKeyValue) + { + writefln("%20s : %20s", k, v); + } +} diff --git a/import/ecs/id_manager.di b/import/ecs/id_manager.di new file mode 100644 index 0000000..55a00ef --- /dev/null +++ b/import/ecs/id_manager.di @@ -0,0 +1,20 @@ +// D import file generated from 'source\ecs\id_manager.d' +module ecs.id_manager; +import ecs.entity; +import ecs.vector; +struct IDManager +{ + EntityID getNewID(); + void releaseID(EntityID id); + void update(ref Entity entity); + Entity* getEntityPointer(EntityID id); + bool isExist(EntityID id); + struct Data + { + uint counter = 0; + uint next_id = (uint).max; + Entity* entity = null; + } + private uint m_next_id = 0; + Vector!Data m_ids_array; +} diff --git a/import/ecs/manager.di b/import/ecs/manager.di new file mode 100644 index 0000000..801bc1d --- /dev/null +++ b/import/ecs/manager.di @@ -0,0 +1,287 @@ +// D import file generated from 'source\ecs\manager.d' +module ecs.manager; +import std.algorithm : max; +import std.conv : to; +import std.experimental.allocator; +import std.experimental.allocator.mallocator : AlignedMallocator, Mallocator; +import std.traits; +import core.stdc.stdlib; +import core.stdc.string; +import ecs.entity; +import ecs.entity_allocator; +import ecs.hash_map; +import ecs.id_manager; +import ecs.system; +import ecs.vector; +alias gEM = EntityManager.instance; +alias SerializeVector = ecs.vector.Vector!ubyte; +class EntityManager +{ + export static void initialize(); + export static void destroy(); + export void registerSystem(Sys)(int priority) + { + alias types = Parameters!(Sys.update); + alias storages = ParameterStorageClassTuple!(Sys.update); + alias STC = ParameterStorageClass; + System system; + static string genCall()() + { + string ret = "s.update(*cast(Entity*)data_pointer,"; + uint i = 0; + uint req = 0; + uint opt = 0; + static foreach (param; Parameters!(Sys.update)[1..$]) + { + i++; + if (isPointer!param) + { + ret ~= "cast(types[" ~ i.to!string ~ "])(optional_pointers[" ~ opt++.to!string ~ "]),"; + } + else + { + ret ~= "*cast(types[" ~ i.to!string ~ "]*)(pointers[" ~ req++.to!string ~ "]),"; + } + } + ret ~= ");"; + return ret; + } + static string genCompList()() + { + string ret = "ushort comp;uint req;uint opt;"; + foreach (i; 1 .. (Parameters!(Sys.update)).length) + { + ret ~= "\x0a static if(isPointer!(types[" ~ i.to!string ~ "]))opt++;\x0a else static if(storages[" ~ i.to!string ~ "] == STC.ref_)req++;\x0a\x0a else static assert(0,\"Can't register system \\\"" ~ Sys.stringof ~ "\\\". Unsupported parameter type \\\"\"~types[" ~ i.to!string ~ "].stringof~\"\\\".\");"; + } + ret ~= "system.m_components = Mallocator.instance.makeArray!ushort(req);"; + ret ~= "system.m_optional_components = Mallocator.instance.makeArray!ushort(opt);"; + ret ~= "opt = 0;req = 0;"; + foreach (i; 1 .. (Parameters!(Sys.update)).length) + { + ret ~= "\x0a static if(isPointer!(types[" ~ i.to!string ~ "]))\x0a {\x0a comp = components_map.get(PointerTarget!(types[" ~ i.to!string ~ "]).stringof, ushort.max);\x0a\x0a if(comp == ushort.max)assert(0,\"Can't register system \\\"" ~ Sys.stringof ~ "\\\" due to non existing component \\\"\"~types[" ~ i.to!string ~ "].stringof~\"\\\".\");\x0a system.m_optional_components[opt++] = comp;\x0a }\x0a else static if(storages[" ~ i.to!string ~ "] == STC.ref_)\x0a {\x0a comp = components_map.get(types[" ~ i.to!string ~ "].stringof, ushort.max);\x0a\x0a if(comp == ushort.max)assert(0,\"Can't register system \\\"" ~ Sys.stringof ~ "\\\" due to non existing component \\\"\"~types[" ~ i.to!string ~ "].stringof~\"\\\".\");\x0a system.m_components[req++] = comp;\x0a }"; + } + return ret; + } + static string catchFunc()(string member, string func) + { + string ret = "static if (hasMember!(Sys, \"" ~ func ~ "\"))\x0a {\x0a static void call" ~ func ~ "(void* system_pointer)\x0a {\x0a\x0a Sys* s = cast(Sys*) system_pointer;\x0a s." ~ func ~ "();\x0a }\x0a\x0a system." ~ member ~ " = &call" ~ func ~ ";\x0a }"; + return ret; + } + static if (hasMember!(Sys, "update")) + { + static void callUpdate(ref CallData data, void* entity) + { + static if (hasMember!(Sys, "update")) + { + Sys* s = cast(Sys*) data.system.m_system_pointer; + + void*[] pointers = (cast(void**) alloca(data.system.m_components.length * (void*) + .sizeof))[0 .. data.system.m_components.length]; + void*[] optional_pointers = (cast(void**) alloca( + data.system.m_optional_components.length * (void*).sizeof))[0 + .. data.system.m_optional_components.length]; + + EntitiesBlock* block = data.info.first_block; + while (block !is null) + { + uint size = block.type_data.size; + void* data_pointer = block.dataBegin(); + foreach (i, ref pointer; pointers) + { + pointer = data_pointer + data.deltas[i]; + } + foreach (i, ref pointer; optional_pointers) + { + uint ind = cast(uint)(i + pointers.length); + if (data.deltas[ind] != uint.max) + pointer = data_pointer + data.deltas[ind]; + else + pointer = null; + } + foreach (i; 0 .. block.entities_count) + { + mixin(genCall()); + data_pointer += size; //data.info.size; + foreach (ref pointer; pointers) + pointer += size; + foreach (ref pointer; optional_pointers) + if (pointer != null) + pointer += size; + } + block = block.next_block; + } + + } + } + system.m_update = &callUpdate; + } + + mixin(catchFunc("m_enable", "onEnable")); + mixin(catchFunc("m_disable", "onDisable")); + mixin(catchFunc("m_create", "onCreate")); + mixin(catchFunc("m_destroy", "onDestroy")); + mixin(catchFunc("m_begin", "onBegin")); + mixin(catchFunc("m_end", "onEnd")); + system.m_system_pointer = cast(void*)Mallocator.instance.make!Sys; + system.m_priority = priority; + mixin(genCompList()); + ushort sys_id = systems_map.get(Sys.stringof, (ushort).max); + if (sys_id < systems.length) + { + system.enable(); + systems[sys_id] = system; + } + else + { + systems_map.add(Sys.stringof, cast(ushort)systems.length); + systems.add(system); + if (system.m_create) + system.m_create(system.m_system_pointer); + systems[$ - 1].enable(); + foreach (info; &entities_infos.byValue) + { + addEntityCaller(*info, cast(uint)systems.length - 1); + } + } + updateEntityCallers(); + } + void registerComponent(Comp)() + { + ComponentInfo info; + static if (!hasMember!(Comp, "component_id") || !is(typeof(Comp.component_id) == ushort)) + { + static assert(0, "Component should have \"__gshared ushort component_id"); + } + + static if (hasMember!(Comp, "onDestroy") && isFunction!(Comp.onDestroy) && is(ReturnType!(Comp.onDestroy) == void) && (Parameters!(Comp.onDestroy).length == 0)) + { + static void callDestroy(void* pointer) + { + (cast(Comp*) pointer).onDestroy(); + } + info.destroy_callback = &callDestroy; + } + + info.size = Comp.sizeof; + info.aligment = Comp.alignof; + info.init_data = Mallocator.instance.makeArray!ubyte(Comp.sizeof); + *cast(Comp*)info.init_data.ptr = Comp.init; + ushort comp_id = components_map.get(Comp.stringof, (ushort).max); + if (comp_id < components.length) + { + Comp.component_id = comp_id; + } + else + { + components.add(info); + Comp.component_id = cast(ushort)(components.length - 1); + components_map.add(Comp.stringof, cast(ushort)(components.length - 1)); + } + } + export void update(); + static void alignNum(ref ushort num, ushort aligment); + extern (C) static int compareUShorts(const void* a, const void* b); + export EntityTemplate* allocateTemplate(ushort[] components_ids); + export EntityInfo* getEntityInfo(ushort[] ids); + export void updateEntityCallers(); + export void addEntityCaller(ref EntityInfo entity, uint system_id); + export Entity* getEntity(EntityID id); + export void removeComponents(EntityID entity_id, ushort[] del_ids); + private void __removeComponents(EntityID entity_id, ushort[] del_ids); + void removeComponents(Components...)(EntityID entity_id) + { + const uint num = Components.length; + ushort[num] del_ids; + static foreach (i, comp; Components) + { + del_ids[i] = comp.component_id; + } + removeComponents(entity_id, del_ids); + } + private void __addComponents(EntityID entity_id, ushort[] new_ids, void*[] data_pointers); + void addComponents(Components...)(EntityID entity_id, Components comps) + { + const uint num = Components.length; + Entity* entity = id_manager.getEntityPointer(entity_id); + EntitiesBlock* block = getMetaData(entity); + EntityInfo* info = block.type_data; + ushort[] ids = (cast(ushort*)alloca((ushort).sizeof * (info.components.length + num)))[0..info.components.length + num]; + ushort[num] new_ids; + static foreach (i, comp; Components) + { + new_ids[i] = comp.component_id; + } + change_entities_list.add(1); + change_entities_list.add((cast(ubyte*)&entity_id)[0..8]); + change_entities_list.add((cast(ubyte*)&num)[0..4]); + change_entities_list.add(cast(ubyte[])new_ids); + static foreach (i, comp; comps) + { + change_entities_list.add((cast(ubyte*)&comp)[0..comp.sizeof]); + } + } + export void freeTemplate(EntityTemplate* template_); + export ref Entity addEntity(EntityTemplate* tmpl); + private EntitiesBlock* findBlockWithFreeSpace(EntityInfo* info); + export void removeEntity(EntityID id); + private void __removeEntity(EntityID id); + private void removeEntityNoID(Entity* entity, EntitiesBlock* block, bool call_destructors = false); + export EntitiesBlock* getMetaData(void* pointer); + private void changeEntites(); + private void updateBlocks(); + private void removeEntities(); + export void begin(); + export void end(); + struct ComponentInfo + { + ushort size; + ushort aligment; + ubyte[] init_data; + void function(void* pointer) destroy_callback; + } + struct EntityInfo + { + ushort[] components; + ushort[] deltas; + ushort alignment; + ushort size; + EntitiesBlock* first_block; + EntitiesBlock* first_with_free_space; + Vector!CallData callers; + } + struct EntitiesBlock + { + uint dataDelta(); + void* dataBegin(); + EntityInfo* type_data; + ushort entities_count; + ushort added_count; + uint id; + EntitiesBlock* next_block; + } + struct CallData + { + uint system_id; + System* system; + EntityManager.EntityInfo* info; + ushort[] deltas; + } + alias SytemFuncType = void function(ref EntityManager.CallData data, void* entity); + enum page_size = 4096; + enum pages_in_block = 128; + IDManager id_manager; + EntityAllocator allocator; + Vector!EntityID entities_to_remove; + Vector!(EntitiesBlock*) blocks_to_update; + Vector!ubyte change_entities_list; + HashMap!(ushort[], EntityInfo*) entities_infos; + HashMap!(string, ushort) systems_map; + HashMap!(string, ushort) components_map; + Vector!System systems; + Vector!ComponentInfo components; + __gshared EntityManager instance; +} +version (Windows) +{ + extern (Windows) bool DllMain(void* hInstance, uint ulReason, void*); +} diff --git a/import/ecs/package.di b/import/ecs/package.di new file mode 100644 index 0000000..41d9943 --- /dev/null +++ b/import/ecs/package.di @@ -0,0 +1,7 @@ +// D import file generated from 'source\ecs\package.d' +module ecs; +public import ecs.manager; +public import ecs.entity; +public import ecs.system; +import ecs.id_manager; +import ecs.events; diff --git a/import/ecs/string_intern.di b/import/ecs/string_intern.di new file mode 100644 index 0000000..665bfc5 --- /dev/null +++ b/import/ecs/string_intern.di @@ -0,0 +1,31 @@ +// D import file generated from 'source\ecs\string_intern.d' +module ecs.string_intern; +import ecs.hash_map; +import ecs.traits : isForeachDelegateWithI; +import std.experimental.allocator; +import std.experimental.allocator.mallocator; +import std.traits : Parameters; +private static __gshared HashMap!(const(char)[], StringIntern) gStringInterns; +struct StringIntern +{ + private const(char)* strPtr; + this(const(char)[] fromStr) + { + opAssign(fromStr); + } + void reset(); + size_t length(); + const(char)[] str(); + const(char)[] cstr(); + bool opEquals()(auto ref const StringIntern s) + { + return strPtr == s.strPtr; + } + bool opEquals()(auto ref const(char[]) s) + { + return str() == s; + } + void opAssign(const(char)[] fromStr); + const(char)[] opSlice(); + private const(char)[] allocStr(const(char)[] fromStr); +} diff --git a/import/ecs/system.di b/import/ecs/system.di new file mode 100644 index 0000000..fd28e8a --- /dev/null +++ b/import/ecs/system.di @@ -0,0 +1,26 @@ +// D import file generated from 'source\ecs\system.d' +module ecs.system; +import ecs.entity; +import ecs.manager; +struct System +{ + export bool enabled(); + export void enable(); + export void disable(); + export int priority(); + package + { + bool m_enabled = false; + int m_priority; + void* m_system_pointer; + ushort[] m_components; + ushort[] m_optional_components; + void* m_update; + void function(void* system_pointer) m_enable; + void function(void* system_pointer) m_disable; + void function(void* system_pointer) m_create; + void function(void* system_pointer) m_destroy; + void function(void* system_pointer) m_begin; + void function(void* system_pointer) m_end; + } +} diff --git a/import/ecs/traits.di b/import/ecs/traits.di new file mode 100644 index 0000000..7eef028 --- /dev/null +++ b/import/ecs/traits.di @@ -0,0 +1,20 @@ +// D import file generated from 'source\ecs\traits.d' +module ecs.traits; +import std.traits; +bool isForeachDelegateWithI(DG)() +{ + return is(DG == delegate) && is(ReturnType!DG == int) && (Parameters!DG.length == 2) && isIntegral!(Parameters!DG[0]); +} +bool isForeachDelegateWithoutI(DG)() +{ + return is(DG == delegate) && is(ReturnType!DG == int) && (Parameters!DG.length == 1); +} +bool isForeachDelegateWithTypes(DG, Types...)() +{ + return is(DG == delegate) && is(ReturnType!DG == int) && is(Parameters!DG == Types); +} +auto assumeNoGC(T)(T t) if (isFunctionPointer!T || isDelegate!T) +{ + enum attrs = functionAttributes!T | FunctionAttribute.nogc; + return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs))t; +} diff --git a/import/ecs/vector.di b/import/ecs/vector.di new file mode 100644 index 0000000..cb0917e --- /dev/null +++ b/import/ecs/vector.di @@ -0,0 +1,274 @@ +// D import file generated from 'source\ecs\vector.d' +module ecs.vector; +import core.bitop; +import core.stdc.stdlib : free, malloc; +import core.stdc.string : memcpy, memset; +import std.algorithm : swap; +import std.conv : emplace; +import std.traits : hasMember, isCopyable, TemplateOf, Unqual; +export pure nothrow @nogc @safe size_t nextPow2(size_t num); +__gshared size_t gVectorsCreated = 0; +__gshared size_t gVectorsDestroyed = 0; +struct Vector(T) +{ + T[] array; + size_t used; + public + { + export this()(T t) + { + add(t); + } + export this(X)(X[] t) if (is(Unqual!X == Unqual!T)) + { + add(t); + } + static if (isCopyable!T) + { + export this(this) + { + T[] tmp = array[0..used]; + array = null; + used = 0; + add(tmp); + } + } + else + { + @disable this(this); + } + export ~this() + { + clear(); + } + export void clear() + { + removeAll(); + } + export void removeAll() + { + if (array !is null) + { + foreach (ref el; array[0..used]) + { + destroy(el); + } + freeData(cast(void[])array); + gVectorsDestroyed++; + } + array = null; + used = 0; + } + export bool empty() + { + return used == 0; + } + export size_t length() + { + return used; + } + export void length(size_t newLength) + { + if (newLength > used) + { + reserve(newLength); + foreach (ref el; array[used..newLength]) + { + emplace(&el); + } + } + else + { + foreach (ref el; array[newLength..used]) + { + destroy(el); + } + } + used = newLength; + } + export void reset() + { + used = 0; + } + export void reserve(size_t numElements) + { + if (numElements > array.length) + { + extend(numElements); + } + } + export size_t capacity() + { + return array.length - used; + } + export void extend(size_t newNumOfElements) + { + auto oldArray = manualExtend(array, newNumOfElements); + if (oldArray !is null) + { + freeData(oldArray); + } + } + export @nogc void freeData(void[] data) + { + memset(data.ptr, 15, data.length); + free(data.ptr); + } + export static void[] manualExtend(ref T[] array, size_t newNumOfElements = 0) + { + if (newNumOfElements == 0) + newNumOfElements = 2; + if (array.length == 0) + gVectorsCreated++; + T[] oldArray = array; + size_t oldSize = oldArray.length * T.sizeof; + size_t newSize = newNumOfElements * T.sizeof; + T* memory = cast(T*)malloc(newSize); + memcpy(cast(void*)memory, cast(void*)oldArray.ptr, oldSize); + array = memory[0..newNumOfElements]; + return cast(void[])oldArray; + } + export Vector!T copy()() + { + Vector!T duplicate; + duplicate.reserve(used); + duplicate ~= array[0..used]; + return duplicate; + } + export bool canAddWithoutRealloc(uint elemNum = 1) + { + return used + elemNum <= array.length; + } + export void add()(T t) + { + if (used >= array.length) + { + extend(nextPow2(used + 1)); + } + emplace(&array[used], t); + used++; + } + export void add()(T t, size_t pos) + { + assert(pos <= used); + if (used >= array.length) + { + extend(array.length * 2); + } + foreach_reverse (size_t i; pos .. used) + { + array[i + 1] = array[i]; + } + emplace(&array[pos], t); + used++; + } + export void add(X)(X[] t) if (is(Unqual!X == Unqual!T)) + { + if (used + t.length > array.length) + { + extend(nextPow2(used + t.length)); + } + foreach (i; 0 .. t.length) + { + emplace(&array[used + i], t[i]); + } + used += t.length; + } + export void remove(size_t elemNum) + { + destroy(array[elemNum]); + swap(array[elemNum], array[used - 1]); + used--; + } + export void removeStable()(size_t elemNum) + { + used--; + foreach (i; 0 .. used) + { + array[i] = array[i + 1]; + } + } + export bool tryRemoveElement()(T elem) + { + foreach (i, ref el; array[0..used]) + { + if (el == elem) + { + remove(i); + return true; + } + } + return false; + } + export 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) + { + assert(elemNum < used, "Range violation [index]"); + return array.ptr[elemNum]; + } + export auto opSlice() + { + return array.ptr[0..used]; + } + export T[] opSlice(size_t x, size_t y) + { + assert(y <= used); + return array.ptr[x..y]; + } + export size_t opDollar() + { + return used; + } + export void opAssign(X)(X[] slice) + { + reset(); + this ~= slice; + } + export void opOpAssign(string op)(T obj) + { + static assert(op == "~"); + add(obj); + } + export void opOpAssign(string op, X)(X[] obj) + { + static assert(op == "~"); + add(obj); + } + export 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) + { + assert(b <= used && (a <= b), "Range viloation"); + array.ptr[a..b] = obj; + } + export const bool opEquals()(auto ref const Vector!T r) + { + return used == r.used && (array.ptr[0..used] == r.array.ptr[0..r.used]); + } + export const nothrow @trusted size_t toHash() + { + return hashOf(cast(Unqual!T[])array.ptr[0..used]); + } + import std.format : FormatSpec, formatValue; + export void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) + { + static if (__traits(compiles, formatValue(sink, array[0..used], fmt))) + { + formatValue(sink, array[0..used], fmt); + } + + } + } +} +private pure nothrow @nogc @safe T[n] s(T, size_t n)(auto ref T[n] array) +{ + return array; +} +enum string checkVectorAllocations = "\x0a//assert(gVectorsCreated==gVectorsDestroyed);\x0agVectorsCreated=gVectorsDestroyed=0;\x0ascope(exit){if(gVectorsCreated!=gVectorsDestroyed){\x09\x0a\x09import std.stdio : writefln;\x0a\x09writefln(\"created==destroyed %s==%s\", gVectorsCreated, gVectorsDestroyed);\x0a\x09assert(gVectorsCreated==gVectorsDestroyed, \"Vector memory leak\");\x0a}}\x0a"; diff --git a/source/ecs/entity.d b/source/ecs/entity.d index 847f565..3b8f59c 100644 --- a/source/ecs/entity.d +++ b/source/ecs/entity.d @@ -26,7 +26,7 @@ struct Entity } } -struct EntityTemplate +export struct EntityTemplate { ubyte[] entity_data; EntityManager.EntityInfo* info; diff --git a/source/ecs/hash_map.d b/source/ecs/hash_map.d index 7935511..2a62201 100755 --- a/source/ecs/hash_map.d +++ b/source/ecs/hash_map.d @@ -11,7 +11,7 @@ private enum HASH_EMPTY = 0; private enum HASH_DELETED = 0x1; private enum HASH_FILLED_MARK = ulong(1) << 8 * ulong.sizeof - 1; -ulong defaultHashFunc(T)(auto ref T t) { +export ulong defaultHashFunc(T)(auto ref T t) { static if (isIntegral!(T)) { return hashInt(t); } else { @@ -20,7 +20,7 @@ ulong defaultHashFunc(T)(auto ref T t) { } // Can turn bad hash function to good one -ulong hashInt(ulong x) nothrow @nogc @safe { +export ulong hashInt(ulong x) nothrow @nogc @safe { x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9; x = (x ^ (x >> 27)) * 0x94d049bb133111eb; x = x ^ (x >> 31); @@ -48,27 +48,27 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { size_t length; // Used to compute loadFactor size_t markerdDeleted; - void clear() { + export void clear() { elements.clear(); length = 0; markerdDeleted = 0; } - void reset() { + export void reset() { elements.reset(); length = 0; markerdDeleted = 0; } - bool isIn(ref Key el) { + export bool isIn(ref Key el) { return getIndex(el) != getIndexEmptyValue; } - bool isIn(Key el) { + export bool isIn(Key el) { return getIndex(el) != getIndexEmptyValue; } - Value* getPtr()(auto ref Key k) { + export Value* getPtr()(auto ref Key k) { size_t index = getIndex(k); if (index == getIndexEmptyValue) { return null; @@ -77,18 +77,18 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { } } - ref Value get()(auto ref Key k) { + export 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.") auto ref Value getDefault()( + deprecated("Use get with second parameter.") export auto ref Value getDefault()( auto ref Key k, auto ref Value defaultValue) { return get(k, defaultValue); } - auto ref Value get()(auto ref Key k, auto ref Value defaultValue) { + export auto ref Value get()(auto ref Key k, auto ref Value defaultValue) { size_t index = getIndex(k); if (index == getIndexEmptyValue) { return defaultValue; @@ -97,7 +97,7 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { } } - ref Value getInsertDefault()(auto ref Key k, auto ref Value defaultValue) { + export ref Value getInsertDefault()(auto ref Key k, auto ref Value defaultValue) { size_t index = getIndex(k); if (index == getIndexEmptyValue) { add(k, defaultValue); @@ -108,7 +108,7 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { } - bool tryRemove(Key el) { + export bool tryRemove(Key el) { size_t index = getIndex(el); if (index == getIndexEmptyValue) { return false; @@ -119,20 +119,20 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { return true; } - void remove(Key el) { + export void remove(Key el) { bool ok = tryRemove(el); assert(ok); } - ref Value opIndex()(auto ref Key key) { + export ref Value opIndex()(auto ref Key key) { return get(key); } - void opIndexAssign()(auto ref Value value, auto ref Key key) { + export void opIndexAssign()(auto ref Value value, auto ref Key key) { add(key, value); } - void add()(auto ref Key key, auto ref Value value) { + export void add()(auto ref Key key, auto ref Value value) { size_t index = getIndex(key); if (index != getIndexEmptyValue) { elements[index].keyValue.value = value; @@ -170,11 +170,11 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { //int numA; //int numB; - size_t getIndex(Key el) { + export size_t getIndex(Key el) { return getIndex(el); } - size_t getIndex(ref Key el) { + export size_t getIndex(ref Key el) { mixin(doNotInline); immutable size_t groupsLength = elements.length; @@ -203,14 +203,14 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { } - float getLoadFactor(size_t forElementsNum) { + export float getLoadFactor(size_t forElementsNum) { if (elements.length == 0) { return 1; } return cast(float) forElementsNum / (elements.length); } - void rehash() { + export void rehash() { mixin(doNotInline); // Get all elements Vector!KeyVal allElements; @@ -239,7 +239,7 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { } // foreach support - int opApply(DG)(scope DG dg) { + export int opApply(DG)(scope DG dg) { int result; foreach (ref Bucket gr; elements) { if ((gr.hash & HASH_FILLED_MARK) == 0) { @@ -262,7 +262,7 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { return result; } - int byKey(scope int delegate(Key k) dg) { + export int byKey(scope int delegate(Key k) dg) { int result; foreach (ref Key k; this) { result = dg(k); @@ -272,7 +272,7 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { return result; } - int byValue(scope int delegate(ref Value k) dg) { + export int byValue(scope int delegate(ref Value k) dg) { int result; foreach (ref Value v; this) { result = dg(v); @@ -282,7 +282,7 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { return result; } - int byKeyValue(scope int delegate(ref Key k, ref Value v) 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); @@ -297,7 +297,7 @@ struct HashMap(KeyPar, ValuePar, alias hashFunc = defaultHashFunc) { /** * Preety print */ - void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) { + export void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) { formatValue(sink, '[', fmt); foreach (ref k, ref v; &byKeyValue) { formatValue(sink, k, fmt); diff --git a/source/ecs/id_manager.d b/source/ecs/id_manager.d index 5fee718..c5b521b 100644 --- a/source/ecs/id_manager.d +++ b/source/ecs/id_manager.d @@ -33,7 +33,7 @@ struct IDManager m_ids_array[entity.id.id].entity = &entity; } - Entity* getEntityPointer(EntityID id) + export Entity* getEntityPointer(EntityID id) { Data* data = &m_ids_array[id.id]; if (data.counter != id.counter) @@ -42,7 +42,7 @@ struct IDManager return data.entity; } - bool isExist(EntityID id) + export bool isExist(EntityID id) { Data* data = &m_ids_array[id.id]; return data.counter == id.counter; diff --git a/source/ecs/manager.d b/source/ecs/manager.d index f302a81..4b7b295 100644 --- a/source/ecs/manager.d +++ b/source/ecs/manager.d @@ -22,12 +22,12 @@ alias SerializeVector = ecs.vector.Vector!ubyte; class EntityManager { - static void initialize() + export static void initialize() { instance = Mallocator.instance.make!EntityManager; } - static void destroy() + export static void destroy() { foreach (ref system; instance.systems) @@ -204,16 +204,27 @@ class EntityManager //system.m_components = Mallocator.instance.makeArray!uint(types.length - 1); mixin(genCompList()); - systems.add(system); - - if (system.m_create) - system.m_create(system.m_system_pointer); - - systems[$ - 1].enable(); - - foreach (info; &entities_infos.byValue) + ushort sys_id = systems_map.get(Sys.stringof, ushort.max); + if(sys_id < systems.length) { - addEntityCaller(*info, cast(uint) systems.length - 1); + system.enable(); + systems[sys_id] = system; + } + else + { + systems_map.add(Sys.stringof,cast(ushort)systems.length); + + systems.add(system); + + if (system.m_create) + system.m_create(system.m_system_pointer); + + systems[$ - 1].enable(); + + foreach (info; &entities_infos.byValue) + { + addEntityCaller(*info, cast(uint) systems.length - 1); + } } updateEntityCallers(); @@ -246,12 +257,20 @@ class EntityManager info.init_data = Mallocator.instance.makeArray!ubyte(Comp.sizeof); *cast(Comp*) info.init_data.ptr = Comp.init; // = Comp(); - components.add(info); - Comp.component_id = cast(ushort)(components.length - 1); - components_map.add(Comp.stringof, cast(ushort)(components.length - 1)); + ushort comp_id = components_map.get(Comp.stringof, ushort.max); + if(comp_id < components.length) + { + Comp.component_id = comp_id; + } + else + { + components.add(info); + Comp.component_id = cast(ushort)(components.length - 1); + components_map.add(Comp.stringof, cast(ushort)(components.length - 1)); + } } - void update() + export void update() { foreach (info; &entities_infos.byValue) { @@ -280,7 +299,7 @@ class EntityManager return 1; } - EntityTemplate* allocateTemplate(ushort[] components_ids) + export EntityTemplate* allocateTemplate(ushort[] components_ids) { ushort[] ids = (cast(ushort*) alloca(ushort.sizeof * components_ids.length))[0 @@ -318,7 +337,7 @@ class EntityManager return temp; } - EntityInfo* getEntityInfo(ushort[] ids) + export EntityInfo* getEntityInfo(ushort[] ids) { EntityInfo* info = entities_infos.get(ids, null); if (info is null) @@ -354,7 +373,7 @@ class EntityManager return info; } - void updateEntityCallers() + export void updateEntityCallers() { foreach (entity; &entities_infos.byValue) { @@ -365,7 +384,7 @@ class EntityManager } } - void addEntityCaller(ref EntityInfo entity, uint system_id) + export void addEntityCaller(ref EntityInfo entity, uint system_id) { System* system = &systems[system_id]; CallData call_data = CallData(system_id, system, &entity, null); @@ -412,12 +431,12 @@ class EntityManager entity.callers.add(call_data, index); } - Entity* getEntity(EntityID id) + export Entity* getEntity(EntityID id) { return cast(Entity*) id_manager.getEntityPointer(id); } - void removeComponents(EntityID entity_id, ushort[] del_ids) + export void removeComponents(EntityID entity_id, ushort[] del_ids) { uint num = cast(uint) del_ids.length; change_entities_list.add(0); @@ -426,7 +445,7 @@ class EntityManager change_entities_list.add(cast(ubyte[]) del_ids); } - void __removeComponents(EntityID entity_id, ushort[] del_ids) + private void __removeComponents(EntityID entity_id, ushort[] del_ids) { Entity* entity = id_manager.getEntityPointer(entity_id); EntitiesBlock* block = getMetaData(entity); @@ -493,7 +512,7 @@ class EntityManager removeComponents(entity_id, del_ids); } - void __addComponents(EntityID entity_id, ushort[] new_ids, void*[] data_pointers) + private void __addComponents(EntityID entity_id, ushort[] new_ids, void*[] data_pointers) { uint num = cast(uint) new_ids.length; Entity* entity = id_manager.getEntityPointer(entity_id); @@ -644,13 +663,13 @@ class EntityManager //__addComponents(entity_id, new_ids, pointers); } - void freeTemplate(EntityTemplate* template_) + export void freeTemplate(EntityTemplate* template_) { Mallocator.instance.dispose(template_.entity_data); Mallocator.instance.dispose(template_); } - ref Entity addEntity(EntityTemplate* tmpl) + export ref Entity addEntity(EntityTemplate* tmpl) { EntitiesBlock* block = findBlockWithFreeSpace(tmpl.info); @@ -669,7 +688,7 @@ class EntityManager return *entity; } - EntitiesBlock* findBlockWithFreeSpace(EntityInfo* info) + private EntitiesBlock* findBlockWithFreeSpace(EntityInfo* info) { EntitiesBlock* previous_block; EntitiesBlock* block = info.first_with_free_space; @@ -709,12 +728,12 @@ class EntityManager return block; } - void removeEntity(EntityID id) + export void removeEntity(EntityID id) { entities_to_remove.add(id); } - void __removeEntity(EntityID id) + private void __removeEntity(EntityID id) { //get entity and block meta data pointers Entity* entity = id_manager.getEntityPointer(id); @@ -771,12 +790,12 @@ class EntityManager *params: *pointer = pointer to any data of entity (i.e. component data pointer) */ - EntitiesBlock* getMetaData(void* pointer) + export EntitiesBlock* getMetaData(void* pointer) { return cast(EntitiesBlock*)(cast(size_t) pointer & (~cast(size_t)(page_size - 1))); } - void changeEntites() + private void changeEntites() { uint index = 0; uint len = cast(uint) change_entities_list.length; @@ -814,7 +833,7 @@ class EntityManager change_entities_list.clear(); } - void updateBlocks() + private void updateBlocks() { foreach (block; blocks_to_update) { @@ -824,7 +843,7 @@ class EntityManager blocks_to_update.clear(); } - void removeEntities() + private void removeEntities() { foreach (id; entities_to_remove) { @@ -833,7 +852,7 @@ class EntityManager entities_to_remove.clear(); } - void begin() + export void begin() { updateBlocks(); changeEntites(); @@ -845,7 +864,7 @@ class EntityManager } } - void end() + export void end() { foreach (ref system; instance.systems) { @@ -900,7 +919,7 @@ class EntityManager } ///return pointer to first element in block - void* dataBegin() + export void* dataBegin() { ushort dif = EntitiesBlock.sizeof; alignNum(dif, type_data.alignment); @@ -950,12 +969,39 @@ class EntityManager Vector!ubyte change_entities_list; HashMap!(ushort[], EntityInfo*) entities_infos; + HashMap!(string, ushort) systems_map; HashMap!(string, ushort) components_map; Vector!System systems; Vector!ComponentInfo components; __gshared EntityManager instance; } + +version(Windows) +extern(Windows) bool DllMain(void* hInstance, uint ulReason, void*) { +import core.sys.windows.windows; +import core.sys.windows.dll; + switch (ulReason) +{ + default: assert(0); +case DLL_PROCESS_ATTACH: + dll_process_attach( hInstance, true ); + break; + +case DLL_PROCESS_DETACH: + dll_process_detach( hInstance, true ); + break; + +case DLL_THREAD_ATTACH: + dll_thread_attach( true, true ); + break; + +case DLL_THREAD_DETACH: + dll_thread_detach( true, true ); + break; + } + return true; +} /* static ulong defaultHashFunc(T)(auto ref T t) { diff --git a/source/ecs/system.d b/source/ecs/system.d index 95136dc..83bcd7d 100644 --- a/source/ecs/system.d +++ b/source/ecs/system.d @@ -5,26 +5,26 @@ import ecs.manager; struct System { - bool enabled() + export bool enabled() { return m_enabled; } - void enable() + export void enable() { if (!m_enabled && m_enable) m_enable(m_system_pointer); m_enabled = true; } - void disable() + export void disable() { if (m_enabled && m_disable) m_disable(m_system_pointer); m_enabled = false; } - int priority() + export int priority() { return m_priority; } diff --git a/source/ecs/vector.d b/source/ecs/vector.d index e0b6f2d..aeb0a9d 100644 --- a/source/ecs/vector.d +++ b/source/ecs/vector.d @@ -7,7 +7,7 @@ import std.algorithm : swap; import std.conv : emplace; import std.traits : hasMember, isCopyable, TemplateOf, Unqual; -@nogc @safe nothrow pure size_t nextPow2(size_t num) { +export @nogc @safe nothrow pure size_t nextPow2(size_t num) { return 1 << bsr(num) + 1; } @@ -19,17 +19,17 @@ struct Vector(T) { size_t used; public: - this()(T t) { + export this()(T t) { add(t); } - this(X)(X[] t) if (is(Unqual!X == Unqual!T)) { + export this(X)(X[] t) if (is(Unqual!X == Unqual!T)) { add(t); } static if (isCopyable!T) { - this(this) { + export this(this) { T[] tmp = array[0 .. used]; array = null; used = 0; @@ -39,15 +39,15 @@ public: @disable this(this); } - ~this() { + export ~this() { clear(); } - void clear() { + export void clear() { removeAll(); } - void removeAll() { + export void removeAll() { if (array !is null) { foreach (ref el; array[0 .. used]) { destroy(el); @@ -59,15 +59,15 @@ public: used = 0; } - bool empty() { + export bool empty() { return (used == 0); } - size_t length() { + export size_t length() { return used; } - void length(size_t newLength) { + export void length(size_t newLength) { if (newLength > used) { reserve(newLength); foreach (ref el; array[used .. newLength]) { @@ -81,34 +81,34 @@ public: used = newLength; } - void reset() { + export void reset() { used = 0; } - void reserve(size_t numElements) { + export void reserve(size_t numElements) { if (numElements > array.length) { extend(numElements); } } - size_t capacity() { + export size_t capacity() { return array.length - used; } - void extend(size_t newNumOfElements) { + export void extend(size_t newNumOfElements) { auto oldArray = manualExtend(array, newNumOfElements); if (oldArray !is null) { freeData(oldArray); } } - @nogc void freeData(void[] data) { + export @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); } - static void[] manualExtend(ref T[] array, size_t newNumOfElements = 0) { + export static void[] manualExtend(ref T[] array, size_t newNumOfElements = 0) { if (newNumOfElements == 0) newNumOfElements = 2; if (array.length == 0) @@ -122,18 +122,18 @@ public: return cast(void[]) oldArray; } - Vector!T copy()() { + export Vector!T copy()() { Vector!T duplicate; duplicate.reserve(used); duplicate ~= array[0 .. used]; return duplicate; } - bool canAddWithoutRealloc(uint elemNum = 1) { + export bool canAddWithoutRealloc(uint elemNum = 1) { return used + elemNum <= array.length; } - void add()(T t) { + export void add()(T t) { if (used >= array.length) { extend(nextPow2(used + 1)); } @@ -142,7 +142,7 @@ public: } /// Add element at given position moving others - void add()(T t, size_t pos) { + export void add()(T t, size_t pos) { assert(pos <= used); if (used >= array.length) { extend(array.length * 2); @@ -155,7 +155,7 @@ public: used++; } - void add(X)(X[] t) if (is(Unqual!X == Unqual!T)) { + export void add(X)(X[] t) if (is(Unqual!X == Unqual!T)) { if (used + t.length > array.length) { extend(nextPow2(used + t.length)); } @@ -165,20 +165,20 @@ public: used += t.length; } - void remove(size_t elemNum) { + export void remove(size_t elemNum) { destroy(array[elemNum]); swap(array[elemNum], array[used - 1]); used--; } - void removeStable()(size_t elemNum) { + export void removeStable()(size_t elemNum) { used--; foreach (i; 0 .. used) { array[i] = array[i + 1]; } } - bool tryRemoveElement()(T elem) { + export bool tryRemoveElement()(T elem) { foreach (i, ref el; array[0 .. used]) { if (el == elem) { remove(i); @@ -188,59 +188,59 @@ public: return false; } - void removeElement()(T elem) { + export void removeElement()(T elem) { bool ok = tryRemoveElement(elem); assert(ok, "There is no such an element in vector"); } - ref T opIndex(size_t elemNum) { + export ref T opIndex(size_t elemNum) { assert(elemNum < used, "Range violation [index]"); return array.ptr[elemNum]; } - auto opSlice() { + export auto opSlice() { return array.ptr[0 .. used]; } - T[] opSlice(size_t x, size_t y) { + export T[] opSlice(size_t x, size_t y) { assert(y <= used); return array.ptr[x .. y]; } - size_t opDollar() { + export size_t opDollar() { return used; } - void opAssign(X)(X[] slice) { + export void opAssign(X)(X[] slice) { reset(); this ~= slice; } - void opOpAssign(string op)(T obj) { + export void opOpAssign(string op)(T obj) { static assert(op == "~"); add(obj); } - void opOpAssign(string op, X)(X[] obj) { + export void opOpAssign(string op, X)(X[] obj) { static assert(op == "~"); add(obj); } - void opIndexAssign()(T obj, size_t elemNum) { + export void opIndexAssign()(T obj, size_t elemNum) { assert(elemNum < used, "Range viloation"); array[elemNum] = obj; } - void opSliceAssign()(T[] obj, size_t a, size_t b) { + export void opSliceAssign()(T[] obj, size_t a, size_t b) { assert(b <= used && a <= b, "Range viloation"); array.ptr[a .. b] = obj; } - bool opEquals()(auto ref const Vector!(T) r) const { + export bool opEquals()(auto ref const Vector!(T) r) const { return used == r.used && array.ptr[0 .. used] == r.array.ptr[0 .. r.used]; } - size_t toHash() const nothrow @trusted { + export size_t toHash() const nothrow @trusted { return hashOf(cast(Unqual!(T)[]) array.ptr[0 .. used]); } @@ -249,7 +249,7 @@ public: /** * Preety print */ - void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) { + export void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) { static if (__traits(compiles, formatValue(sink, array[0 .. used], fmt))) { formatValue(sink, array[0 .. used], fmt); }