DragonECS/src/EcsWorld.cs

406 lines
14 KiB
C#
Raw Normal View History

2023-04-20 10:59:55 +08:00
using System;
2023-03-02 14:42:44 +08:00
using System.Collections.Generic;
using System.Runtime.CompilerServices;
2023-04-01 20:45:37 +08:00
using System.Runtime.InteropServices;
2023-03-02 14:42:44 +08:00
namespace DCFApixels.DragonECS
{
2023-04-18 19:35:42 +08:00
public interface IEcsWorld : IEcsTable
2023-03-02 14:42:44 +08:00
{
#region Properties
2023-04-18 19:35:42 +08:00
public int UniqueID { get; }
2023-04-01 20:45:37 +08:00
public EcsPipeline Pipeline { get; }
2023-03-02 14:42:44 +08:00
#endregion
2023-04-06 23:40:47 +08:00
#region Entities
2023-04-08 05:50:44 +08:00
public EcsEntity NewEntity();
public void DelEntity(EcsEntity entity);
2023-03-26 11:19:03 +08:00
public bool EntityIsAlive(int entityID, short gen);
2023-04-18 19:35:42 +08:00
public EcsEntity GetEcsEntity(int entityID);
2023-04-08 21:29:18 +08:00
#endregion
2023-03-02 14:42:44 +08:00
}
public abstract class EcsWorld : IEcsWorld
2023-03-02 14:42:44 +08:00
{
2023-04-23 17:19:52 +08:00
private const short GEN_BITS = 0x7fff;
private const short DEATH_GEN_BIT = short.MinValue;
public static EcsWorld[] Worlds = new EcsWorld[8];
2023-04-07 15:11:48 +08:00
private static IntDispenser _worldIdDispenser = new IntDispenser(0);
2023-04-18 19:35:42 +08:00
public readonly short uniqueID;
2023-04-20 11:37:27 +08:00
private const int DEL_ENT_BUFFER_SIZE_OFFSET = 2;
private int _worldArchetypeID;
2023-04-18 19:35:42 +08:00
private IntDispenser _entityDispenser;
private int _entitiesCount;
2023-04-18 19:35:42 +08:00
private int _entitesCapacity;
private short[] _gens; //старший бит указывает на то жива ли сущьность.
2023-03-26 11:19:03 +08:00
//private short[] _componentCounts; //TODO
2023-04-20 11:37:27 +08:00
private EcsGroup _allEntites;
2023-04-20 18:23:23 +08:00
//буфер удаления откладывает освобождение андишников сущьностей.
//Нужен для того чтобы запускать некоторые процесыы связанные с удалением сущьности не по одному при каждом удалении, а пачкой
2023-04-24 00:19:07 +08:00
//В теории такой подход частично улучшает ситуацию с переполнением поколений
2023-04-20 18:23:23 +08:00
private int[] _delEntBuffer;
2023-04-20 11:37:27 +08:00
private int _delEntBufferCount;
2023-04-18 19:35:42 +08:00
2023-04-21 03:16:05 +08:00
private EcsPoolBase[] _pools;
2023-03-13 04:32:24 +08:00
private EcsNullPool _nullPool;
2023-03-02 14:42:44 +08:00
2023-04-17 22:58:52 +08:00
private EcsQueryBase[] _queries;
2023-03-02 14:42:44 +08:00
2023-04-01 20:45:37 +08:00
private EcsPipeline _pipeline;
2023-04-17 22:58:52 +08:00
private List<WeakReference<EcsGroup>> _groups;
2023-04-18 19:35:42 +08:00
private Stack<EcsGroup> _groupsPool = new Stack<EcsGroup>(64);
2023-04-01 20:45:37 +08:00
private IEcsEntityCreate _entityCreate;
private IEcsEntityDestroy _entityDestry;
#region GetterMethods
2023-04-21 03:16:05 +08:00
public ReadOnlySpan<EcsPoolBase> GetAllPools() => new ReadOnlySpan<EcsPoolBase>(_pools);
2023-04-20 19:23:58 +08:00
public int GetComponentID<T>() => WorldMetaStorage.GetComponentId<T>(_worldArchetypeID);////ComponentType<TWorldArchetype>.uniqueID;
#endregion
2023-03-02 14:42:44 +08:00
#region Properties
public abstract Type Archetype { get; }
2023-04-18 19:35:42 +08:00
public int UniqueID => uniqueID;
public int Count => _entitiesCount;
public int Capacity => _entitesCapacity; //_denseEntities.Length;
2023-04-01 20:45:37 +08:00
public EcsPipeline Pipeline => _pipeline;
2023-04-09 02:52:39 +08:00
public EcsReadonlyGroup Entities => _allEntites.Readonly;
2023-03-02 14:42:44 +08:00
#endregion
2023-04-23 22:55:13 +08:00
#region Internal Properties
internal EcsPoolBase[] Pools
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _pools;
}
#endregion
2023-04-20 20:10:16 +08:00
#region Constructors/Destroy
2023-04-20 10:59:55 +08:00
public EcsWorld(EcsPipeline pipline)
{
2023-04-23 17:19:52 +08:00
_entitesCapacity = 512;
uniqueID = (short)_worldIdDispenser.GetFree();
if (uniqueID >= Worlds.Length)
Array.Resize(ref Worlds, Worlds.Length << 1);
Worlds[uniqueID] = this;
_worldArchetypeID = WorldMetaStorage.GetWorldId(Archetype);
2023-04-01 20:45:37 +08:00
_pipeline = pipline ?? EcsPipeline.Empty;
if (!_pipeline.IsInit) pipline.Init();
2023-04-07 15:11:48 +08:00
_entityDispenser = new IntDispenser(0);
2023-04-06 23:40:47 +08:00
_nullPool = EcsNullPool.instance;
2023-04-21 03:16:05 +08:00
_pools = new EcsPoolBase[512];
2023-04-08 00:47:35 +08:00
ArrayUtility.Fill(_pools, _nullPool);
2023-04-01 20:45:37 +08:00
2023-04-23 17:19:52 +08:00
_gens = new short[_entitesCapacity];
ArrayUtility.Fill(_gens, DEATH_GEN_BIT);
2023-04-20 11:37:27 +08:00
_delEntBufferCount = 0;
2023-04-23 17:19:52 +08:00
_delEntBuffer = new int[_entitesCapacity >> DEL_ENT_BUFFER_SIZE_OFFSET];
2023-04-18 19:35:42 +08:00
2023-04-17 22:58:52 +08:00
_groups = new List<WeakReference<EcsGroup>>();
2023-04-20 10:59:55 +08:00
_allEntites = GetGroupFromPool();
2023-04-23 04:10:54 +08:00
_queries = new EcsQueryBase[128];
2023-04-01 20:45:37 +08:00
_entityCreate = _pipeline.GetRunner<IEcsEntityCreate>();
_entityDestry = _pipeline.GetRunner<IEcsEntityDestroy>();
_pipeline.GetRunner<IEcsInject<EcsWorld>>().Inject(this);
2023-04-01 20:45:37 +08:00
_pipeline.GetRunner<IEcsWorldCreate>().OnWorldCreate(this);
2023-03-02 14:42:44 +08:00
}
2023-04-20 20:10:16 +08:00
public void Destroy()
{
2023-04-20 20:10:16 +08:00
_entityDispenser = null;
//_denseEntities = null;
_gens = null;
_pools = null;
_nullPool = null;
_queries = null;
Worlds[uniqueID] = null;
_worldIdDispenser.Release(uniqueID);
}
2023-04-20 20:10:16 +08:00
public void DestryWithPipeline()
{
Destroy();
_pipeline.Destroy();
}
2023-03-02 14:42:44 +08:00
#endregion
#region GetPool
2023-04-22 23:40:09 +08:00
public TPool GetPool<TComponent, TPool>() where TComponent : struct where TPool : EcsPoolBase<TComponent>, new()
2023-03-02 14:42:44 +08:00
{
2023-04-20 18:23:23 +08:00
int uniqueID = WorldMetaStorage.GetComponentId<TComponent>(_worldArchetypeID);
2023-03-02 14:42:44 +08:00
if (uniqueID >= _pools.Length)
{
2023-03-13 04:32:24 +08:00
int oldCapacity = _pools.Length;
2023-04-10 22:22:17 +08:00
Array.Resize(ref _pools, _pools.Length << 1);
2023-04-08 00:47:35 +08:00
ArrayUtility.Fill(_pools, _nullPool, oldCapacity, oldCapacity - _pools.Length);
2023-03-02 14:42:44 +08:00
}
2023-03-13 04:32:24 +08:00
if (_pools[uniqueID] == _nullPool)
2023-04-21 03:16:05 +08:00
{
var pool = new TPool();
_pools[uniqueID] = pool;
pool.InvokeInit(this);
//EcsDebug.Print(pool.GetType().FullName);
}
2023-04-20 18:23:23 +08:00
2023-04-21 03:16:05 +08:00
return (TPool)_pools[uniqueID];
2023-03-02 14:42:44 +08:00
}
#endregion
2023-04-20 11:37:27 +08:00
#region Queries
2023-04-21 16:03:50 +08:00
public TQuery Where<TQuery>(out TQuery query) where TQuery : EcsQuery
2023-04-20 10:59:55 +08:00
{
query = Select<TQuery>();
2023-04-22 23:40:09 +08:00
query.ExecuteWhere();
2023-04-20 10:59:55 +08:00
return query;
}
public TQuery Select<TQuery>() where TQuery : EcsQueryBase
2023-03-12 20:45:18 +08:00
{
2023-04-20 18:23:23 +08:00
int uniqueID = WorldMetaStorage.GetQueryId<TQuery>(_worldArchetypeID);
if (uniqueID >= _queries.Length)
Array.Resize(ref _queries, _queries.Length << 1);
if (_queries[uniqueID] == null)
2023-04-17 22:58:52 +08:00
_queries[uniqueID] = EcsQueryBase.Builder.Build<TQuery>(this);
2023-04-20 10:59:55 +08:00
return (TQuery)_queries[uniqueID];
}
2023-03-02 14:42:44 +08:00
#endregion
2023-04-15 00:23:46 +08:00
#region IsMaskCompatible
2023-04-08 00:47:35 +08:00
public bool IsMaskCompatible(EcsComponentMask mask, int entityID)
2023-03-02 14:42:44 +08:00
{
2023-04-01 20:45:37 +08:00
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
2023-04-20 19:23:58 +08:00
if (mask.WorldArchetype != Archetype)
2023-04-08 00:47:35 +08:00
throw new EcsFrameworkException("mask.WorldArchetypeType != typeof(TTableArhetype)");
2023-03-13 01:39:04 +08:00
#endif
2023-04-07 18:21:52 +08:00
for (int i = 0, iMax = mask.Inc.Length; i < iMax; i++)
2023-03-02 14:42:44 +08:00
{
2023-04-08 00:47:35 +08:00
if (!_pools[mask.Inc[i]].Has(entityID))
2023-03-02 14:42:44 +08:00
return false;
}
2023-04-07 18:21:52 +08:00
for (int i = 0, iMax = mask.Exc.Length; i < iMax; i++)
2023-03-02 14:42:44 +08:00
{
2023-04-08 00:47:35 +08:00
if (_pools[mask.Exc[i]].Has(entityID))
2023-03-02 14:42:44 +08:00
return false;
}
return true;
}
#endregion
2023-03-26 11:19:03 +08:00
#region Entity
2023-04-08 05:50:44 +08:00
public EcsEntity NewEntity()
2023-03-02 14:42:44 +08:00
{
2023-03-26 11:19:03 +08:00
int entityID = _entityDispenser.GetFree();
2023-04-18 19:35:42 +08:00
_entitiesCount++;
2023-04-01 20:45:37 +08:00
if (_gens.Length <= entityID)
{
2023-03-26 11:19:03 +08:00
Array.Resize(ref _gens, _gens.Length << 1);
2023-04-23 17:19:52 +08:00
ArrayUtility.Fill(_gens, DEATH_GEN_BIT, _entitesCapacity);
2023-04-18 19:35:42 +08:00
_entitesCapacity = _gens.Length;
2023-04-23 17:19:52 +08:00
2023-04-17 22:58:52 +08:00
for (int i = 0; i < _groups.Count; i++)
{
if (_groups[i].TryGetTarget(out EcsGroup group))
{
group.OnWorldResize(_gens.Length);
}
else
{
int last = _groups.Count - 1;
_groups[i--] = _groups[last];
_groups.RemoveAt(last);
}
}
2023-04-01 20:45:37 +08:00
foreach (var item in _pools)
2023-04-21 03:16:05 +08:00
item.InvokeOnWorldResize(_gens.Length);
2023-04-01 20:45:37 +08:00
}
2023-04-23 17:19:52 +08:00
_gens[entityID] &= GEN_BITS;
EcsEntity entity = new EcsEntity(entityID, ++_gens[entityID], uniqueID);
2023-04-23 22:55:13 +08:00
// UnityEngine.Debug.Log($"{entityID} {_gens[entityID]} {uniqueID}");
2023-04-01 20:45:37 +08:00
_entityCreate.OnEntityCreate(entity);
2023-04-09 02:52:39 +08:00
_allEntites.Add(entityID);
2023-04-01 20:45:37 +08:00
return entity;
2023-03-26 11:19:03 +08:00
}
2023-04-08 05:50:44 +08:00
public void DelEntity(EcsEntity entity)
2023-03-26 11:19:03 +08:00
{
2023-04-09 02:52:39 +08:00
_allEntites.Remove(entity.id);
2023-04-20 11:37:27 +08:00
_delEntBuffer[_delEntBufferCount++] = entity.id;
2023-04-23 17:19:52 +08:00
_gens[entity.id] |= DEATH_GEN_BIT;
_entitiesCount--;
2023-04-01 20:45:37 +08:00
_entityDestry.OnEntityDestroy(entity);
2023-04-20 11:37:27 +08:00
if (_delEntBufferCount >= _delEntBuffer.Length)
2023-04-20 11:37:27 +08:00
ReleaseDelEntBuffer();
}
private void ReleaseDelEntBuffer()//TODO проверить что буфер удаления работает нормально
{
for (int i = 0; i < _delEntBufferCount; i++)
_entityDispenser.Release(_delEntBuffer[i]);
_delEntBufferCount = 0;
2023-03-02 14:42:44 +08:00
}
2023-04-01 20:45:37 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-04-18 19:35:42 +08:00
public EcsEntity GetEcsEntity(int entityID)
2023-03-13 04:32:24 +08:00
{
2023-04-18 19:35:42 +08:00
return new EcsEntity(entityID, _gens[entityID], uniqueID);
2023-03-13 04:32:24 +08:00
}
2023-03-26 11:19:03 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-04-23 04:10:54 +08:00
public bool EntityIsAlive(int entityID, short gen) //TODO пофиксить EntityIsAlive
2023-03-26 11:19:03 +08:00
{
return _gens[entityID] == gen;
2023-03-26 11:19:03 +08:00
}
2023-03-02 14:42:44 +08:00
#endregion
2023-04-18 19:35:42 +08:00
#region Groups
void IEcsTable.RegisterGroup(EcsGroup group) => RegisterGroup(group);
internal void RegisterGroup(EcsGroup group)
{
2023-04-17 22:58:52 +08:00
_groups.Add(new WeakReference<EcsGroup>(group));
}
2023-04-20 10:59:55 +08:00
EcsGroup IEcsTable.GetGroupFromPool() => GetGroupFromPool();
2023-04-18 19:35:42 +08:00
internal EcsGroup GetGroupFromPool()
{
if (_groupsPool.Count <= 0)
return new EcsGroup(this);
return _groupsPool.Pop();
}
void IEcsTable.ReleaseGroup(EcsGroup group) => ReleaseGroup(group);
internal void ReleaseGroup(EcsGroup group)
2023-04-18 19:35:42 +08:00
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (group.World != this)
2023-04-20 18:23:23 +08:00
throw new ArgumentException("groupFilter.WorldIndex != this");
2023-04-18 19:35:42 +08:00
#endif
group.Clear();
_groupsPool.Push(group);
}
#endregion
2023-04-01 20:45:37 +08:00
}
2023-04-08 21:29:18 +08:00
public abstract class EcsWorld<TWorldArchetype> : EcsWorld
where TWorldArchetype : EcsWorld<TWorldArchetype>
{
public override Type Archetype => typeof(TWorldArchetype);
public EcsWorld(EcsPipeline pipline) : base(pipline) { }
}
2023-04-18 19:35:42 +08:00
#region Utils
2023-04-01 20:45:37 +08:00
[StructLayout(LayoutKind.Sequential, Pack = 8, Size = 24)]
2023-04-20 18:23:23 +08:00
internal readonly struct PoolRunners
2023-03-30 01:14:43 +08:00
{
2023-04-01 20:45:37 +08:00
public readonly IEcsComponentAdd add;
public readonly IEcsComponentWrite write;
public readonly IEcsComponentDel del;
2023-04-20 18:23:23 +08:00
public PoolRunners(EcsPipeline pipeline)
2023-03-30 01:14:43 +08:00
{
2023-04-01 20:45:37 +08:00
add = pipeline.GetRunner<IEcsComponentAdd>();
write = pipeline.GetRunner<IEcsComponentWrite>();
del = pipeline.GetRunner<IEcsComponentDel>();
2023-03-30 01:14:43 +08:00
}
}
2023-04-20 18:23:23 +08:00
public static class WorldMetaStorage
2023-04-10 22:22:17 +08:00
{
private static List<Resizer> resizer = new List<Resizer>();
private static int tokenCount = 0;
private static int[] componentCounts = new int[0];
2023-04-20 18:23:23 +08:00
private static int[] queryCounts = new int[0];
2023-04-20 19:23:58 +08:00
private static Dictionary<Type, int> _worldIds = new Dictionary<Type, int>();
2023-04-20 18:23:23 +08:00
private static class WorldIndex<TWorldArchetype>
2023-04-10 22:22:17 +08:00
{
2023-04-20 19:23:58 +08:00
public static int id = GetWorldId(typeof(TWorldArchetype));
2023-04-10 22:22:17 +08:00
}
private static int GetToken()
{
tokenCount++;
Array.Resize(ref componentCounts, tokenCount);
2023-04-20 18:23:23 +08:00
Array.Resize(ref queryCounts, tokenCount);
2023-04-10 22:22:17 +08:00
foreach (var item in resizer)
item.Resize(tokenCount);
return tokenCount - 1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-04-20 19:23:58 +08:00
public static int GetWorldId(Type archetype)
{
if(_worldIds.TryGetValue(archetype, out int id) == false)
{
id = GetToken();
_worldIds.Add(archetype, id);
}
return id;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-04-20 18:23:23 +08:00
public static int GetWorldId<TWorldArchetype>() => WorldIndex<TWorldArchetype>.id;
2023-04-10 22:22:17 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-04-20 18:23:23 +08:00
public static int GetComponentId<T>(int worldID) => Component<T>.Get(worldID);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetQueryId<T>(int worldID) => Query<T>.Get(worldID);
2023-04-10 22:22:17 +08:00
private abstract class Resizer
{
public abstract void Resize(int size);
}
private sealed class Resizer<T> : Resizer
{
2023-04-20 18:23:23 +08:00
public override void Resize(int size)
{
Array.Resize(ref Component<T>.ids, size);
Array.Resize(ref Query<T>.ids, size);
}
2023-04-10 22:22:17 +08:00
}
2023-04-20 18:23:23 +08:00
private static class Component<T>
2023-04-10 22:22:17 +08:00
{
public static int[] ids;
static Component()
{
ids = new int[tokenCount];
for (int i = 0; i < ids.Length; i++)
ids[i] = -1;
2023-04-20 18:23:23 +08:00
resizer.Add(new Resizer<T>());
2023-04-10 22:22:17 +08:00
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Get(int token)
{
ref int id = ref ids[token];
if (id < 0)
id = componentCounts[token]++;
return id;
}
}
2023-04-20 18:23:23 +08:00
private static class Query<T>
{
public static int[] ids;
static Query()
{
ids = new int[tokenCount];
for (int i = 0; i < ids.Length; i++)
ids[i] = -1;
resizer.Add(new Resizer<T>());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Get(int token)
{
ref int id = ref ids[token];
if (id < 0)
id = queryCounts[token]++;
return id;
}
}
2023-04-10 22:22:17 +08:00
}
2023-04-18 19:35:42 +08:00
#endregion
2023-03-02 14:42:44 +08:00
}