diff --git a/src/Builtin/Worlds.cs b/src/Builtin/Worlds.cs index 8662512..d98a1db 100644 --- a/src/Builtin/Worlds.cs +++ b/src/Builtin/Worlds.cs @@ -1,5 +1,13 @@ namespace DCFApixels.DragonECS { - public sealed class EcsDefaultWorld : EcsWorld { } - public sealed class EcsEventWorld : EcsWorld { } + public sealed class EcsDefaultWorld : EcsWorld + { + public EcsDefaultWorld() : base(null) { } + public EcsDefaultWorld(IEcsWorldConfig config) : base(config) { } + } + public sealed class EcsEventWorld : EcsWorld + { + public EcsEventWorld() : base(null) { } + public EcsEventWorld(IEcsWorldConfig config) : base(config) { } + } } diff --git a/src/DataInterfaces.cs b/src/DataInterfaces.cs index 4fed4d3..d200d3a 100644 --- a/src/DataInterfaces.cs +++ b/src/DataInterfaces.cs @@ -74,7 +74,7 @@ namespace DCFApixels.DragonECS static EcsComponentCopyHandler() { T def = default; - if(def is IEcsComponentCopy intrf) + if (def is IEcsComponentCopy intrf) { instance = intrf; } diff --git a/src/Debug/EcsDebug.cs b/src/Debug/EcsDebug.cs index bb7bbef..279404f 100644 --- a/src/Debug/EcsDebug.cs +++ b/src/Debug/EcsDebug.cs @@ -41,6 +41,11 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void PrintWarning(object v) => Print(EcsConsts.DEBUG_WARNING_TAG, v); public static void PrintError(object v) => Print(EcsConsts.DEBUG_ERROR_TAG, v); + public static void PrintErrorAndBreak(object v) + { + Print(EcsConsts.DEBUG_ERROR_TAG, v); + Break(); + } public static void PrintPass(object v) => Print(EcsConsts.DEBUG_PASS_TAG, v); public static void Print() { diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 4bc4059..572c208 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -3,12 +3,14 @@ using DCFApixels.DragonECS.Utils; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using static Leopotam.EcsLite.EcsWorld; namespace DCFApixels.DragonECS { public abstract partial class EcsWorld { public readonly short id; + private IEcsWorldConfig _config; private bool _isDestroyed; @@ -36,6 +38,11 @@ namespace DCFApixels.DragonECS private readonly PoolsMediator _poolsMediator; #region Properties + public IEcsWorldConfig Config + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _config; } + } public bool IsDestroyed { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -63,7 +70,7 @@ namespace DCFApixels.DragonECS get { return _isEnableAutoReleaseDelEntBuffer; } } - public EcsReadonlyGroup Entities + public EcsReadonlyGroup Entities { [MethodImpl(MethodImplOptions.AggressiveInlining)] get @@ -84,13 +91,15 @@ namespace DCFApixels.DragonECS #endregion #region Constructors/Destroy - public EcsWorld() : this(true) { } - internal EcsWorld(bool isIndexable) + public EcsWorld(IEcsWorldConfig config) : this(config, true) { } + private EcsWorld(IEcsWorldConfig config, bool isIndexable) { - const int POOLS_CAPACITY = 512; - _poolsMediator = new PoolsMediator(this); - - _entitesCapacity = 512; + if (config == null) + { + config = EmptyConfig.Instance; + } + config.Lock(); + _config = config; if (isIndexable) { @@ -102,13 +111,15 @@ namespace DCFApixels.DragonECS Worlds[id] = this; } + _poolsMediator = new PoolsMediator(this); _entityDispenser = new IntDispenser(0); - _pools = new IEcsPoolImplementation[POOLS_CAPACITY]; - _poolComponentCounts = new int[POOLS_CAPACITY]; - //_sortedPoolIds = new int[POOLS_CAPACITY]; - //_sortedPoolIdsMapping = new int[POOLS_CAPACITY]; + + int poolsCapacity = config.Get_PoolsCapacity(); + _pools = new IEcsPoolImplementation[poolsCapacity]; + _poolComponentCounts = new int[poolsCapacity]; ArrayUtility.Fill(_pools, _nullPool); + _entitesCapacity = config.Get_EntitiesCapacity(); _gens = new short[_entitesCapacity]; _componentCounts = new short[_entitesCapacity]; @@ -116,9 +127,11 @@ namespace DCFApixels.DragonECS _delEntBufferCount = 0; _delEntBuffer = new int[_entitesCapacity]; _entitiesComponentMasks = new int[_entitesCapacity][]; + + int maskLength = _pools.Length / 32 + 1; for (int i = 0; i < _entitesCapacity; i++) { - _entitiesComponentMasks[i] = new int[_pools.Length / 32 + 1]; + _entitiesComponentMasks[i] = new int[maskLength]; } _delEntBufferMinCount = Math.Max(_delEntBuffer.Length >> DEL_ENT_BUFFER_SIZE_OFFSET, DEL_ENT_BUFFER_MIN_SIZE); @@ -264,7 +277,9 @@ namespace DCFApixels.DragonECS _entitiesCount++; if (_gens.Length <= entityID) - Upsize(); + { + Upsize(_gens.Length << 1); + } _gens[entityID] &= GEN_BITS; _allEntites.Add(entityID); @@ -432,24 +447,31 @@ namespace DCFApixels.DragonECS #region Upsize [MethodImpl(MethodImplOptions.NoInlining)] - private void Upsize() + public void Upsize(int minSize) { - Array.Resize(ref _gens, _gens.Length << 1); - Array.Resize(ref _componentCounts, _gens.Length); - Array.Resize(ref _delEntBuffer, _gens.Length); - Array.Resize(ref _entitiesComponentMasks, _gens.Length); - for (int i = _entitesCapacity; i < _gens.Length; i++) + if (minSize < Capacity) + { + return; + } + + int newSize = 1 << (BitsUtility.GetHighBitNumber(minSize - 1) + 1); + + Array.Resize(ref _gens, newSize); + Array.Resize(ref _componentCounts, newSize); + Array.Resize(ref _delEntBuffer, newSize); + Array.Resize(ref _entitiesComponentMasks, newSize); + for (int i = _entitesCapacity; i < newSize; i++) _entitiesComponentMasks[i] = new int[_pools.Length / 32 + 1]; _delEntBufferMinCount = Math.Max(_delEntBuffer.Length >> DEL_ENT_BUFFER_SIZE_OFFSET, DEL_ENT_BUFFER_MIN_SIZE); ArrayUtility.Fill(_gens, DEATH_GEN_BIT, _entitesCapacity); - _entitesCapacity = _gens.Length; + _entitesCapacity = newSize; for (int i = 0; i < _groups.Count; i++) { if (_groups[i].TryGetTarget(out EcsGroup group)) { - group.OnWorldResize(_gens.Length); + group.OnWorldResize(newSize); } else { @@ -459,9 +481,11 @@ namespace DCFApixels.DragonECS } } foreach (var item in _pools) - item.OnWorldResize(_gens.Length); + { + item.OnWorldResize(newSize); + } - _listeners.InvokeOnWorldResize(_gens.Length); + _listeners.InvokeOnWorldResize(newSize); } #endregion @@ -538,6 +562,21 @@ namespace DCFApixels.DragonECS } } #endregion + + #region EmptyConfig + private class EmptyConfig : IEcsWorldConfig + { + public static readonly EmptyConfig Instance = new EmptyConfig(); + private EmptyConfig() { } + public bool IsLocked => true; + public T Get(string valueName) { return default; } + public bool Has(string valueName) { return false; } + public void Lock() { } + public void Remove(string valueName) { } + public void Set(string valueName, T value) { } + public bool TryGet(string valueName, out T value) { value = default; return false; } + } + #endregion } #region Callbacks Interface @@ -589,4 +628,4 @@ namespace DCFApixels.DragonECS public static entlong ToEntityLong(this int self, EcsWorld world) => world.GetEntityLong(self); } #endregion -} +} \ No newline at end of file diff --git a/src/EcsWorld.pools.cs b/src/EcsWorld.pools.cs index a9c371d..c20881f 100644 --- a/src/EcsWorld.pools.cs +++ b/src/EcsWorld.pools.cs @@ -25,7 +25,7 @@ namespace DCFApixels.DragonECS public bool IsComponentTypeDeclared(Type type) => _componentIds.Contains(EcsTypeCode.Get(type)); public bool IsComponentTypeDeclared(int componentTypeID) { - if(componentTypeID >= 0 && componentTypeID < _pools.Length) + if (componentTypeID >= 0 && componentTypeID < _pools.Length) { return _pools[componentTypeID] != _nullPool; } diff --git a/src/EcsWorld.static.cs b/src/EcsWorld.static.cs index af03e07..8803f44 100644 --- a/src/EcsWorld.static.cs +++ b/src/EcsWorld.static.cs @@ -22,14 +22,14 @@ namespace DCFApixels.DragonECS private const int DEL_ENT_BUFFER_MIN_SIZE = 64; private static EcsWorld[] Worlds = new EcsWorld[4]; - private static IdDispenser _worldIdDispenser = new IdDispenser(0); + private static IdDispenser _worldIdDispenser = new IdDispenser(4); private static List _dataReleaseres = new List(); //public static int Copacity => Worlds.Length; static EcsWorld() { - Worlds[0] = new EcsNullWorld(); + Worlds[0] = new NullWorld(); } private static void ReleaseData(int worldID) { @@ -121,9 +121,9 @@ namespace DCFApixels.DragonECS } } } - } - internal sealed class EcsNullWorld : EcsWorld - { - internal EcsNullWorld() : base(false) { } + private sealed class NullWorld : EcsWorld + { + internal NullWorld() : base(EmptyConfig.Instance, false) { } + } } } diff --git a/src/EcsWorldConfig.cs b/src/EcsWorldConfig.cs new file mode 100644 index 0000000..98522b9 --- /dev/null +++ b/src/EcsWorldConfig.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; + +namespace DCFApixels.DragonECS +{ + public interface IEcsWorldConfig + { + bool IsLocked { get; } + void Lock(); + void Set(string valueName, T value); + bool Has(string valueName); + T Get(string valueName); + bool TryGet(string valueName, out T value); + void Remove(string valueName); + } + public class EcsWorldConfig : IEcsWorldConfig + { + private Dictionary _storage = new Dictionary(); + private bool _isLocked = false; + public bool IsLocked { get { return _isLocked; } } + public void Lock() + { + _isLocked = true; + } + public T Get(string valueName) + { + return (T)_storage[valueName]; + } + public bool Has(string valueName) + { + return _storage.ContainsKey(valueName); + } + public void Remove(string valueName) + { + _storage.Remove(valueName); + } + public void Set(string valueName, T value) + { + if (_isLocked) + { + throw new InvalidOperationException(); + } + _storage[valueName] = value; + } + public bool TryGet(string valueName, out T value) + { + bool result = _storage.TryGetValue(valueName, out object rawValue); + value = rawValue == null ? default : (T)rawValue; + return result; + } + } + public static class EcsWorldConfigExtensions + { + public static T GetOrDefault(this IEcsWorldConfig self, string valueName, T defaultValue) + { + if (self.TryGet(valueName, out T value)) + { + return value; + } + return defaultValue; + } + + private const string ENTITIES_CAPACITY = nameof(ENTITIES_CAPACITY); + private const int ENTITIES_CAPACITY_DEFAULT = 512; + public static TConfig Set_EntitiesCapacity(this TConfig self, int value) + where TConfig : IEcsWorldConfig + { + self.Set(ENTITIES_CAPACITY, value); + return self; + } + public static int Get_EntitiesCapacity(this IEcsWorldConfig self) + { + return self.GetOrDefault(ENTITIES_CAPACITY, ENTITIES_CAPACITY_DEFAULT); + } + + //private const string RECYCLED_ENTITIES_CAPACITY = nameof(RECYCLED_ENTITIES_CAPACITY); + //public static void Set_RecycledEntitiesCapacity(this IEcsWorldConfig self, int value) + //{ + // self.Set(RECYCLED_ENTITIES_CAPACITY, value); + //} + //public static int Get_RecycledEntitiesCapacity(this IEcsWorldConfig self) + //{ + // return self.GetOrDefault(RECYCLED_ENTITIES_CAPACITY, self.Get_EntitiesCapacity() / 2); + //} + + private const string POOLS_CAPACITY = nameof(POOLS_CAPACITY); + private const int POOLS_CAPACITY_DEFAULT = 512; + public static TConfig Set_PoolsCapacity(this TConfig self, int value) + where TConfig : IEcsWorldConfig + { + self.Set(POOLS_CAPACITY, value); + return self; + } + public static int Get_PoolsCapacity(this IEcsWorldConfig self) + { + return self.GetOrDefault(POOLS_CAPACITY, POOLS_CAPACITY_DEFAULT); + } + + private const string COMPONENT_POOL_CAPACITY = nameof(COMPONENT_POOL_CAPACITY); + private const int COMPONENT_POOL_CAPACITY_DEFAULT = 512; + public static TConfig Set_PoolComponentsCapacity(this TConfig self, int value) + where TConfig : IEcsWorldConfig + { + self.Set(COMPONENT_POOL_CAPACITY, value); + return self; + } + public static int Get_PoolComponentsCapacity(this IEcsWorldConfig self) + { + return self.GetOrDefault(COMPONENT_POOL_CAPACITY, COMPONENT_POOL_CAPACITY_DEFAULT); + } + + private const string POOL_RECYCLED_COMPONENTS_CAPACITY = nameof(POOL_RECYCLED_COMPONENTS_CAPACITY); + public static TConfig Set_PoolRecycledComponentsCapacity(this TConfig self, int value) + where TConfig : IEcsWorldConfig + { + self.Set(POOL_RECYCLED_COMPONENTS_CAPACITY, value); + return self; + } + public static int Get_PoolRecycledComponentsCapacity(this IEcsWorldConfig self) + { + return self.GetOrDefault(POOL_RECYCLED_COMPONENTS_CAPACITY, self.Get_PoolComponentsCapacity() / 2); + } + } +} diff --git a/src/Pools/EcsPool.cs b/src/Pools/EcsPool.cs index 2f0755a..07a5a26 100644 --- a/src/Pools/EcsPool.cs +++ b/src/Pools/EcsPool.cs @@ -143,12 +143,10 @@ namespace DCFApixels.DragonECS _componentTypeID = componentTypeID; _maskBit = EcsMaskChunck.FromID(componentTypeID); - const int capacity = 512; - _mapping = new int[world.Capacity]; - _recycledItems = new int[128]; + _recycledItems = new int[world.Config.Get_PoolRecycledComponentsCapacity()]; _recycledItemsCount = 0; - _items = new T[capacity]; + _items = new T[world.Config.Get_PoolComponentsCapacity()]; _itemsCount = 0; } void IEcsPoolImplementation.OnWorldResize(int newSize) diff --git a/src/Utils/Exceptions.cs b/src/Utils/Exceptions.cs index b883552..243646f 100644 --- a/src/Utils/Exceptions.cs +++ b/src/Utils/Exceptions.cs @@ -81,6 +81,11 @@ namespace DCFApixels.DragonECS.Internal else throw new EcsFrameworkException($"The {entity} is not alive."); } + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void UndefinedException() + { + throw new Exception(); + } } } diff --git a/src/Utils/IntDispenser.cs b/src/Utils/IntDispenser.cs index 96fb120..df975e4 100644 --- a/src/Utils/IntDispenser.cs +++ b/src/Utils/IntDispenser.cs @@ -14,12 +14,7 @@ namespace DCFApixels.DragonECS.Utils #endregion #region Constructor - public IntDispenser() - { - _freeInts = new ConcurrentQueue(); - _increment = 0; - } - public IntDispenser(int startIncrement) + public IntDispenser(int startIncrement = 0) { _freeInts = new ConcurrentQueue(); _increment = startIncrement;