Update WIP

This commit is contained in:
Mikhail 2023-02-05 19:59:45 +08:00
parent 80458d2e70
commit 491e8069c3
41 changed files with 2674 additions and 0 deletions

14
DragonECS.asmdef Normal file
View File

@ -0,0 +1,14 @@
{
"name": "DCFApixels.DragonECS",
"rootNamespace": "DCFApixels",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

7
DragonECS.asmdef.meta Normal file
View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: abb125fa67fff1e45914d0825236f608
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
src.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0192fd952d3f4a24d8057af65f35d8e1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

13
src/EcsEntityTable.cs Normal file
View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DCFApixels.DragonECS
{
public interface IEcsEntityTable
{
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1d395d007b9c7c24c9f1f4c09f16e04c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DCFApixels.DragonECS
{
public class EcsEntityTableManager
{
private int _count;
private IEcsFieldPool[] _fieldPools;
private int _idIncrement;
private Dictionary<IDKey, int> _ids;
public EcsEntityTableManager(int capacity)
{
_fieldPools = new IEcsFieldPool[capacity];
_ids = new Dictionary<IDKey, int>(capacity);
_count = 0;
}
public EcsFieldPool<T> GetFieldPool<T>(int id)
{
if(id < _count)
return (EcsFieldPool<T>)_fieldPools[id];
_count++;
if(_fieldPools.Length < _count)
{
Array.Resize(ref _fieldPools, _fieldPools.Length << 1);
}
EcsFieldPool<T> newPool = new EcsFieldPool<T>(7);
_fieldPools[id] = newPool;
return newPool;
}
public void ResizeFieldPool(int id)
{
}
public int GetFieldID(string name, int index)
{
IDKey key = new IDKey(name, index);
if (_ids.TryGetValue(key, out int id))
return id;
id = _idIncrement++;
_ids.Add(key, id);
return id;
}
private struct IDKey
{
public string name;
public int index;
public IDKey(string name, int index)
{
this.name = name;
this.index = index;
}
public override bool Equals(object obj)
{
return obj is IDKey key &&
name == key.name &&
index == key.index;
}
public override int GetHashCode()
{
return HashCode.Combine(name, index);
}
public override string ToString()
{
return name + "_" + index;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f568bc9f583414c4188526dfade20358
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

17
src/EcsField.cs Normal file
View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DCFApixels.DragonECS
{
public struct EcsField<T>
{
private EcsFieldPool<T> _pool;
public ref T this[int index]
{
get => ref _pool[index];
}
}
}

11
src/EcsField.cs.meta Normal file
View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f2fa97ad86c494a40939307a2cfaca36
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

51
src/EcsFieldPool.cs Normal file
View File

@ -0,0 +1,51 @@
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace DCFApixels.DragonECS
{
public interface IEcsFieldPool
{
public bool Has(int index);
public void Add(int index);
}
public class EcsFieldPool<T> : IEcsFieldPool
{
private SparseSet _sparseSet;
private T[] _denseItems;
public EcsFieldPool(int capacity)
{
_denseItems = new T[capacity];
_sparseSet = new SparseSet(capacity);
}
public ref T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _denseItems[_sparseSet[index]];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Add(int index)
{
_sparseSet.Add(index);
_sparseSet.Normalize(ref _denseItems);
return ref _denseItems[_sparseSet.IndexOf(index)];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(int index)
{
return _sparseSet.Contains(index);
}
#region IEcsFieldPool
void IEcsFieldPool.Add(int index)
{
Add(index);
}
#endregion
}
}

11
src/EcsFieldPool.cs.meta Normal file
View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2b523e51b8d5f4c4c969e0ffec1b8b6f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

119
src/EcsSession.cs Normal file
View File

@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DCFApixels.DragonECS
{
public class EcsSession
{
private List<IEcsSystem> _allSystems;
private ReadOnlyCollection<IEcsSystem> _ecsSystemsSealed;
private bool _isInit = false;
private bool _isDestoryed = false;
private int _worldIdIncrement;
private Dictionary<string, EcsWorld> _worldsDict = new Dictionary<string, EcsWorld>();
private List<EcsWorld> _worlds = new List<EcsWorld>();
private Dictionary<Type, IEcsSystemsRunner> _runners;
private Dictionary<Type, IEcsSystemsMessenger> _messengers;
private EcsSystemsRunner<_Run> _runRunnerCache;
#region Properties
public ReadOnlyCollection<IEcsSystem> AllSystems => _ecsSystemsSealed;
#endregion
#region React Runners/Messengers
public EcsSystemsRunner<TDoTag> GetRunner<TDoTag>()
where TDoTag : IEcsDoTag
{
Type type = typeof(TDoTag);
if (_runners.TryGetValue(type, out IEcsSystemsRunner result))
{
return (EcsSystemsRunner<TDoTag>)result;
}
result = new EcsSystemsRunner<TDoTag>(this);
_runners.Add(type, result);
return (EcsSystemsRunner<TDoTag>)result;
}
public EcsSystemsMessenger<TMessege> GetMessenger<TMessege>()
where TMessege : IEcsMessage
{
Type type = typeof(TMessege);
if (_messengers.TryGetValue(type, out IEcsSystemsMessenger result))
{
return (EcsSystemsMessenger<TMessege>)result;
}
result = new EcsSystemsMessenger<TMessege>(this);
_messengers.Add(type, result);
return (EcsSystemsMessenger<TMessege>)result;
}
#endregion
#region Configuration
public EcsSession Add(IEcsSystem system)
{
CheckInitForMethod(nameof(AddWorld));
_allSystems.Add(system);
return this;
}
public EcsSession AddWorld(string name)
{
CheckInitForMethod(nameof(AddWorld));
//_worlds.Add(new EcsWorld(_worldIdIncrement++));
return this;
}
#endregion
#region LifeCycle
public void Init()
{
CheckInitForMethod(nameof(Init));
_ecsSystemsSealed = _allSystems.AsReadOnly();
_isInit = true;
GetRunner<_PreInit>().Run();
GetRunner<_Init>().Run();
_runRunnerCache = GetRunner<_Run>();
}
public void Run()
{
CheckDestroyForMethod(nameof(Run));
_runRunnerCache.Run();
}
public void Destroy()
{
CheckDestroyForMethod(nameof(Run));
_isDestoryed = true;
GetRunner<_Destroy>().Run();
GetRunner<_PostDestroy>().Run();
}
#endregion
#region StateChecks
private void CheckInitForMethod(string methodName)
{
if (_isInit)
throw new MethodAccessException($"Запрещено вызывать метод {methodName}, после инициализации {nameof(EcsSession)}");
}
private void CheckDestroyForMethod(string methodName)
{
if (_isInit)
throw new MethodAccessException($"Запрещено вызывать метод {methodName}, после уничтожения {nameof(EcsSession)}");
}
#endregion
}
}

11
src/EcsSession.cs.meta Normal file
View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2ae31658c78bbf04cb755ac72be367dd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

83
src/EcsWorld.cs Normal file
View File

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DCFApixels.DragonECS
{
public class EcsWorld
{
public const int MAX_WORLDS = byte.MaxValue; //Номер последнего мира 254
public const int DEAD_WORLD_ID = byte.MaxValue; //Зарезервированный номер мира для мертвых сущьностей
private byte _id = DEAD_WORLD_ID;
private Dictionary<Type, IEcsFieldPool> _pools;
private SparseSet _entities = new SparseSet();
private short[] _gens;
private byte[] _components;
//private Dictionary<Type, IEcsEntityTable> _tables;
#region Properties
public int ID => _id;
public bool IsAlive => _id != DEAD_WORLD_ID;
#endregion
#region Constructors
public EcsWorld()
{
_pools = new Dictionary<Type, IEcsFieldPool>();
_entities = new SparseSet();
}
#endregion
#region ID
internal void SetId(byte id)
{
_id = id;
}
#endregion
#region GetPool
public EcsFieldPool<T> GetPool<T>()
{
Type type = typeof(T);
if (_pools.TryGetValue(type, out IEcsFieldPool pool))
{
return (EcsFieldPool<T>)pool;
}
//pool = new EcsPool<T>();
_pools.Add(type, pool);
return (EcsFieldPool<T>)pool;
}
#endregion
#region NewEntity
public ent NewEntity()
{
int entityID = _entities.GetFree();
_entities.Normalize(ref _gens);
_entities.Normalize(ref _components);
_gens[entityID]++;
return new ent(entityID, _gens[entityID], _id, _components[entityID]);
}
#endregion
#region Destroy
public void Destroy()
{
_id = Consts.DEAD_WORLD_ID;
}
#endregion
private void Resize()
{
}
}
}

11
src/EcsWorld.cs.meta Normal file
View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b045d6a8b5bcf654f9c2be8012a526f6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

2
src/INFO.txt Normal file
View File

@ -0,0 +1,2 @@
Мервтый мир - значение byte 255, зарезервированный адишник мира, все что ссылается на мертвый мир считается так же мертвым.

7
src/INFO.txt.meta Normal file
View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 39030d83d78d46048982adceaa08bfbd
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
src/Interfaces.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4ed6f2439b576f34eaefee5b9d0978bf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,29 @@
namespace DCFApixels.DragonECS
{
public interface IEcsSystem { }
public interface IEcsDoTag { }
public struct _PreInit : IEcsDoTag { }
public struct _Init : IEcsDoTag { }
public struct _Run : IEcsDoTag { }
public struct _Destroy : IEcsDoTag { }
public struct _PostDestroy : IEcsDoTag { }
public interface IEcsDo<TTag> : IEcsSystem
where TTag : IEcsDoTag
{
public void Do(EcsSession engine);
}
public interface IEcsMessage { }
public interface IEcsDoMessege<TMessage> : IEcsSystem
where TMessage : IEcsMessage
{
public void Do(EcsSession engine, in TMessage message);
}
public interface IEcsSimpleCycleSystem :
IEcsDo<_Init>,
IEcsDo<_Run>,
IEcsDo<_Destroy>
{ }
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c440e40c9802ce54fb7a84fda930b22d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
src/React.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c0c409d38416ea840b358d614c3e62a4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,41 @@
using System.Collections.Generic;
namespace DCFApixels.DragonECS
{
public interface IEcsSystemsMessenger
{
public EcsSession Source { get; }
}
public class EcsSystemsMessenger<TMessage> : IEcsSystemsMessenger
where TMessage : IEcsMessage
{
private EcsSession _source;
private IEcsDoMessege<TMessage>[] _systems;
public EcsSession Source => _source;
public IReadOnlyList<IEcsDoMessege<TMessage>> Systems => _systems;
internal EcsSystemsMessenger(EcsSession source)
{
_source = source;
List<IEcsDoMessege<TMessage>> list = new List<IEcsDoMessege<TMessage>>();
foreach (var item in _source.AllSystems)
{
if (item is IEcsDoMessege<TMessage> targetItem)
{
list.Add(targetItem);
}
}
_systems = list.ToArray();
}
public void Send(in TMessage message)
{
foreach (var item in _systems)
{
item.Do(_source, message);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dc514cbb93c3f3049adf666ad237c6ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,42 @@
using System.Collections.Generic;
namespace DCFApixels.DragonECS
{
public interface IEcsSystemsRunner
{
public EcsSession Source { get; }
public void Run();
}
public class EcsSystemsRunner<TDoTag> : IEcsSystemsRunner
where TDoTag : IEcsDoTag
{
private EcsSession _source;
private IEcsDo<TDoTag>[] _systems;
public EcsSession Source => _source;
public IReadOnlyList<IEcsDo<TDoTag>> Systems => _systems;
internal EcsSystemsRunner(EcsSession source)
{
_source = source;
List<IEcsDo<TDoTag>> list = new List<IEcsDo<TDoTag>>();
foreach (var item in _source.AllSystems)
{
if (item is IEcsDo<TDoTag> targetItem)
{
list.Add(targetItem);
}
}
_systems = list.ToArray();
}
public void Run()
{
foreach (var item in _systems)
{
item.Do(_source);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 37fabd05090af0843a67e6c8046ad374
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
src/Utils.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 16b79c30e1b668e47bd35de518796871
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,12 @@
namespace DCFApixels.DragonECS
{
public class ComponentTypeID
{
protected static int _incerement = 0;
}
public class TypeID<T> : ComponentTypeID
where T : struct
{
public static readonly int id = _incerement++;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b5af477721d789f498a0de809e753321
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,264 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
namespace DCFApixels
{
public class GrowingSparseCollection<TValue>
{
private const int EMPTY = -1;
private int[] _buckets = Array.Empty<int>();
private Entry[] _entries = Array.Empty<Entry>();
private int _capacity;
private int _count;
#region Properties
public TValue this[int key]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _entries[FindEntry(key)].value;
}
#endregion
#region Add
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(int key, TValue value)
{
#if DEBUG
if (Contains(key))
throw new ArgumentException("Contains(key) is true");
#endif
Insert(key, value);
}
#endregion
#region Getter
public bool TryGetValue(int key, out TValue value)
{
int index = IndexOfKey(key);
if (index < 0)
{
value = default;
return false;
}
value = _entries[index].value;
return true;
}
#endregion
#region Constructors
public GrowingSparseCollection(int capacity)
{
Initialize(capacity);
}
#endregion
#region Initialize
private void Initialize(int capacity)
{
_capacity = HashHelpers.GetPrime(capacity);
_buckets = new int[_capacity];
for (int i = 0; i < _capacity; i++)
_buckets[i] = EMPTY;
_entries = new Entry[_capacity];
}
#endregion
#region Clear
public void Clear()
{
if (_count > 0)
{
for (int i = 0; i < _buckets.Length; i++)
{
_buckets[i] = -1;
}
Array.Clear(_entries, 0, _count);
_count = 0;
}
}
#endregion
#region Contains
public bool Contains(int key)
{
return IndexOfKey(key) >= 0;
}
#endregion
#region IndexOfKey/Find/Insert
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOfKey(int key)
{
key &= ~int.MinValue;
for (int i = _buckets[key % _capacity]; i >= 0; i = _entries[i].next)
{
if (_entries[i].key == key)
return i;
}
return -1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int FindEntry(int key)
{
key &= ~int.MinValue;
for (int i = _buckets[key % _capacity]; i >= 0; i = _entries[i].next)
{
if (_entries[i].key == key)
return i;
}
throw new KeyNotFoundException();
}
private void Insert(int key, TValue value)
{
key &= ~int.MinValue;
int targetBucket = key % _capacity;
for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next)
{
if (_entries[i].key == key)
{
_entries[i].value = value;
return;
}
}
if (_count >= _entries.Length)
{
Resize();
targetBucket = key % _capacity;
}
int index = _count;
_count++;
_entries[index].next = _buckets[targetBucket];
_entries[index].key = key;
_entries[index].value = value;
_buckets[targetBucket] = index;
}
#endregion
#region Resize
private void Resize()
{
Resize(HashHelpers.ExpandPrime(_count), false);
}
private void Resize(int newSize, bool forceNewHashCodes)
{
_capacity = newSize;
Contract.Assert(newSize >= _entries.Length);
int[] newBuckets = new int[newSize];
for (int i = 0; i < newBuckets.Length; i++)
{
newBuckets[i] = EMPTY;
}
Entry[] newEntries = new Entry[newSize];
Array.Copy(_entries, 0, newEntries, 0, _count);
if (forceNewHashCodes)
{
for (int i = 0; i < _count; i++)
{
if (newEntries[i].key != -1)
{
newEntries[i].key = newEntries[i].key;
}
}
}
for (int i = 0; i < _count; i++)
{
if (newEntries[i].key >= 0)
{
int bucket = newEntries[i].key % newSize;
newEntries[i].next = newBuckets[bucket];
newBuckets[bucket] = i;
}
}
_buckets = newBuckets;
_entries = newEntries;
}
#endregion
#region Utils
private struct Entry
{
public int next; // Index of next entry, -1 if last
public int key; // key & hash
public TValue value;
}
#endregion
}
#region HashHelpers
internal static class HashHelpers
{
public const int MaxPrimeArrayLength = 0x7FEFFFFD;
public static readonly int[] primes = {
3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,
17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,
187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,
1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369};
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
public static bool IsPrime(int candidate)
{
if ((candidate & 1) != 0)
{
int limit = (int)Math.Sqrt(candidate);
for (int divisor = 3; divisor <= limit; divisor += 2)
{
if ((candidate % divisor) == 0)
return false;
}
return true;
}
return (candidate == 2);
}
public static int ExpandPrime(int oldSize)
{
int newSize = 2 * oldSize;
if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
{
Contract.Assert(MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
return MaxPrimeArrayLength;
}
return GetPrime(newSize);
}
internal const int HashtableHashPrime = 101;
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
public static int GetPrime(int min)
{
if (min < 0)
{
throw new ArgumentException("min < 0"); //TODO
}
Contract.EndContractBlock();
for (int i = 0; i < primes.Length; i++)
{
int prime = primes[i];
if (prime >= min)
return prime;
}
for (int i = (min | 1); i < int.MaxValue; i += 2)
{
if (IsPrime(i) && ((i - 1) % HashtableHashPrime != 0))
return i;
}
return min;
}
}
#endregion
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0862221477782744a98863ae86648660
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

1036
src/Utils/IntSet.cs Normal file

File diff suppressed because it is too large Load Diff

11
src/Utils/IntSet.cs.meta Normal file
View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9cad30d5b37df1d48bc2abe5d1743649
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

489
src/Utils/SparseSet.cs Normal file
View File

@ -0,0 +1,489 @@
// _sparse[value] == index
// _dense[index] == value
//
// int[] _dense => |2|4|1|_|_|
// int[] _sparse => |_|2|0|_|1|
//
// indexator => [0]2, [1]4, [2]1
//
// can use foreach
// implements IEnumerable<int>
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
namespace DCFApixels.DragonECS
{
public class SparseSet : IEnumerable<int>, ICollection<int>, IReadOnlyCollection<int>
{
public const int DEFAULT_CAPACITY = 16;
private int[] _dense;
private int[] _sparse;
private int _count;
#region Properties
public int Count => _count;
public int Capacity => _dense.Length;
public int this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#if DEBUG
get
{
ThrowHalper.CheckOutOfRange(this, index);
return _dense[index];
}
#else
get => _dense[index];
#endif
}
public IndexesCollection Indexes => new IndexesCollection(_sparse);
#endregion
#region Constructors
public SparseSet() : this(DEFAULT_CAPACITY) { }
public SparseSet(int capacity)
{
#if DEBUG
ThrowHalper.CheckCapacity(capacity);
#endif
_dense = new int[capacity];
_sparse = new int[capacity];
for (int i = 0; i < _sparse.Length; i++)
{
_dense[i] = i;
_sparse[i] = i;
}
_count = 0;
}
#endregion
#region Add/AddRange/GetFree
public void Add<T>(int value, ref T[] normalizedArray)
{
Add(value);
Normalize(ref normalizedArray);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(int value)
{
#if DEBUG
ThrowHalper.CheckValueIsPositive(value);
ThrowHalper.CheckValueNotContained(this, value);
#endif
int neadedSpace = _dense.Length;
while (value >= neadedSpace)
neadedSpace <<= 1;
if (neadedSpace != _dense.Length)
Resize(neadedSpace);
if (Contains(value))
{
return;
}
Swap(value, _count++);
}
public bool TryAdd<T>(int value, ref T[] normalizedArray)
{
if (Contains(value))
return false;
Add(value, ref normalizedArray);
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryAdd(int value)
{
if (Contains(value))
return false;
Add(value);
return true;
}
public void AddRange<T>(IEnumerable<int> range, ref T[] normalizedArray)
{
foreach (var item in range)
{
if (Contains(item))
continue;
Add(item);
}
Normalize(ref normalizedArray);
}
public void AddRange(IEnumerable<int> range)
{
foreach (var item in range)
{
if (Contains(item))
continue;
Add(item);
}
}
/// <summary>
/// Adds a value between 0 and Capacity to the array and returns it.
/// </summary>
/// <returns>Value between 0 and Capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetFree<T>(ref T[] normalizedArray)
{
int result = GetFree();
Normalize(ref normalizedArray);
return result;
}
/// <summary>
/// Adds a value between 0 and Capacity to the array and returns it.
/// </summary>
/// <returns>Value between 0 and Capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetFree()
{
if (++_count >= _dense.Length)
AddSpaces();
return _dense[_count - 1];
}
#endregion
#region Contains
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(int value)
{
return value >= 0 && value < Capacity && _sparse[value] < _count;
}
#endregion
#region Remove
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Remove(int value)
{
#if DEBUG
ThrowHalper.CheckValueContained(this, value);
#endif
Swap(_sparse[value], --_count);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryRemove(int value)
{
if (!Contains(value))
return false;
Remove(value);
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAt(int index)
{
#if DEBUG
ThrowHalper.CheckOutOfRange(this, index);
#endif
Remove(_dense[index]);
}
#endregion
#region Other
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Normalize<T>(ref T[] array)
{
if (array.Length != _dense.Length)
Array.Resize(ref array, _dense.Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOf(int value)
{
if (value < 0 || !Contains(value))
return -1;
return _sparse[value];
}
public void Sort()
{
int increment = 0;
for (int i = 0; i < Capacity; i++)
{
if (_sparse[i] < _count)
{
_sparse[i] = increment;
_dense[increment++] = i;
}
}
}
public void HardSort()
{
int inc = 0;
int inc2 = _count;
for (int i = 0; i < Capacity; i++)
{
if (_sparse[i] < _count)
{
_sparse[i] = inc;
_dense[inc++] = i;
}
else
{
_sparse[i] = inc2;
_dense[inc2++] = i;
}
}
}
public void CopyTo(SparseSet other)
{
other._count = _count;
if (Capacity != other.Capacity)
{
other.Resize(Capacity);
}
_dense.CopyTo(other._dense, 0);
_sparse.CopyTo(other._sparse, 0);
}
public void CopyTo(int[] array, int arrayIndex)
{
#if DEBUG
if (arrayIndex < 0)
throw new ArgumentException("arrayIndex is less than 0");
if (arrayIndex + _count >= array.Length)
throw new ArgumentException("The number of elements in the source List<T> is greater than the available space from arrayIndex to the end of the destination array.");
#endif
for (int i = 0; i < _count; i++, arrayIndex++)
{
array[arrayIndex] = this[i];
}
}
#endregion
#region Clear/Reset
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
_count = 0;
}
public void Reset()
{
Clear();
for (int i = 0; i < _dense.Length; i++)
{
_dense[i] = i;
_sparse[i] = i;
}
}
public void Reset(int newCapacity)
{
#if DEBUG
ThrowHalper.CheckCapacity(newCapacity);
#endif
Reset();
Resize(newCapacity);
}
#endregion
#region AddSpace/Resize
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AddSpaces() => Resize(_count << 1);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Resize(int newSpace)
{
int oldspace = _dense.Length;
Array.Resize(ref _dense, newSpace);
Array.Resize(ref _sparse, newSpace);
for (int i = oldspace; i < newSpace; i++)
{
_dense[i] = i;
_sparse[i] = i;
}
}
#endregion
#region Swap
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Swap(int fromIndex, int toIndex)
{
int value = _dense[toIndex];
int oldValue = _dense[fromIndex];
_dense[toIndex] = oldValue;
_dense[fromIndex] = value;
_sparse[_dense[fromIndex]] = fromIndex;
_sparse[_dense[toIndex]] = toIndex;
}
#endregion
#region Enumerator
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public RefEnumerator GetEnumerator() => new RefEnumerator(_dense, _count);
public ref struct RefEnumerator
{
private readonly int[] _dense;
private readonly int _count;
private int _index;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public RefEnumerator(int[] values, int count)
{
_dense = values;
_count = count;
_index = -1;
}
public int Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _dense[_index];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose() { }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext() => ++_index < _count;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset() => _index = -1;
}
IEnumerator<int> IEnumerable<int>.GetEnumerator() => new Enumerator(_dense, _count);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_dense, _count);
public struct Enumerator : IEnumerator<int> //to implement the IEnumerable interface and use the ref structure, 2 Enumerators were created.
{
private readonly int[] _dense;
private readonly int _count;
private int _index;
public Enumerator(int[] values, int count)
{
_dense = values;
_count = count;
_index = -1;
}
public int Current => _dense[_index];
object IEnumerator.Current => _dense[_index];
public void Dispose() { }
public bool MoveNext() => ++_index < _count;
public void Reset() => _index = -1;
}
#endregion
#region Utils
public ref struct IndexesCollection
{
private readonly int[] _indexes;
public IndexesCollection(int[] indexes)
{
_indexes = indexes;
}
public int this[int value]
{
get => _indexes[value];
}
}
#endregion
#region ICollection
bool ICollection<int>.IsReadOnly => false;
bool ICollection<int>.Remove(int value) => TryRemove(value);
#endregion
#region Debug
public string Log()
{
StringBuilder logbuild = new StringBuilder();
for (int i = 0; i < Capacity; i++)
{
logbuild.Append(_dense[i] + ", ");
}
logbuild.Append("\n\r");
for (int i = 0; i < Capacity; i++)
{
logbuild.Append(_sparse[i] + ", ");
}
logbuild.Append("\n\r --------------------------");
logbuild.Append("\n\r");
for (int i = 0; i < Capacity; i++)
{
logbuild.Append((i < _count ? _dense[i].ToString() : "_") + ", ");
}
logbuild.Append("\n\r");
for (int i = 0; i < Capacity; i++)
{
logbuild.Append((_sparse[i] < _count ? _sparse[i].ToString() : "_") + ", ");
}
logbuild.Append("\n\r Count: " + _count);
logbuild.Append("\n\r Capacity: " + Capacity);
logbuild.Append("\n\r IsValide: " + IsValide_Debug());
logbuild.Append("\n\r");
return logbuild.ToString();
}
public bool IsValide_Debug()
{
bool isPass = true;
for (int index = 0; index < Capacity; index++)
{
int value = _dense[index];
isPass = isPass && _sparse[value] == index;
}
return isPass;
}
#if DEBUG
private static class ThrowHalper
{
public static void CheckCapacity(int capacity)
{
if (capacity < 0)
throw new ArgumentException("Capacity cannot be a negative number");
}
public static void CheckValueIsPositive(int value)
{
if (value < 0)
throw new ArgumentException("The SparseSet can only contain positive numbers");
}
public static void CheckValueContained(SparseSet source, int value)
{
if (!source.Contains(value))
throw new ArgumentException($"Value {value} is not contained");
}
public static void CheckValueNotContained(SparseSet source, int value)
{
if (source.Contains(value))
throw new ArgumentException($"Value {value} is already contained");
}
public static void CheckOutOfRange(SparseSet source, int index)
{
if (index < 0 || index >= source.Count)
throw new ArgumentOutOfRangeException($"Index {index} was out of range. Must be non-negative and less than the size of the collection.");
}
}
#endif
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d46dabbefb72a224bbf8e74eb12fc455
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

113
src/ent.cs Normal file
View File

@ -0,0 +1,113 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace DCFApixels.DragonECS
{
[StructLayout(LayoutKind.Sequential, Pack = 0, Size = 8)]
public readonly struct ent : IEquatable<long>, IEquatable<ent>
{
//private const int ID_BITS = 32;
//private const int GEN_BITS = 16;
//private const int WORLD_BITS = 8;
//private const int COM_BITS = 8;
public readonly long _full;
[EditorBrowsable(EditorBrowsableState.Never)]
public int id
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (int)(_full >> 32);
}
[EditorBrowsable(EditorBrowsableState.Never)]
public short gen
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (short)((_full << 32) >> 48);
}
[EditorBrowsable(EditorBrowsableState.Never)]
public byte world
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (byte)((_full << 48) >> 56);
}
[EditorBrowsable(EditorBrowsableState.Never)]
public byte com
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (byte)((_full << 56) >> 56);
}
[EditorBrowsable(EditorBrowsableState.Never)]
public ent(int id, short gen, byte world, byte com)
{
_full = ((long)id) << 32;
_full += ((long)gen) << 16;
_full += ((long)world) << 8;
_full += com;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ent(long value)
{
_full = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TypeCode GetTypeCode()
{
return TypeCode.Int64;
}
#region GetHashCode
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return unchecked((int)(_full)) ^ (int)(_full >> 32);
}
#endregion
#region Equals
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(in ent other)
{
return _full == other._full;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
return obj is ent other && Equals(in other);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ent other)
{
return _full == other._full;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(long other)
{
return _full == other;
}
#endregion
#region operators
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(in ent left, in ent right) => left.Equals(in right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(in ent left, in ent right) => !left.Equals(in right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator long(in ent eent) => eent._full;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ent(in long value) => new ent(value);
#endregion
}
}

11
src/ent.cs.meta Normal file
View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 24f0d55c3815f3c429a749511f5d3837
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
test.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3776f76149b634c48a0bf7a7f98ee0c5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

23
test/TestSystem.cs Normal file
View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DCFApixels.DragonECS
{
public class TestSystem : IEcsDo<_Init>, IEcsDo<_Run>, IEcsDo<_Destroy>
{
void IEcsDo<_Init>.Do(EcsSession engine)
{
}
void IEcsDo<_Run>.Do(EcsSession engine)
{
}
void IEcsDo<_Destroy>.Do(EcsSession engine)
{
}
}
}

11
test/TestSystem.cs.meta Normal file
View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: affa952a9e445864ebfe2ca2388e76de
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

12
test/TransformTable.cs Normal file
View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DCFApixels.DragonECS
{
public struct TransformTable
{
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d6c6320184f942444b499e3f027a72a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: