using System; using System.Buffers; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.AccessControl; using System.Threading; using kcp2k; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Unity.Mathematics; namespace BITKit.Entities { public static class EntitiesServiceExtensions { internal static T GetRequiredServiceWithId(this IServiceProvider serviceProvider, int id) { return serviceProvider.GetRequiredService(); } } public class EntitiesService : IEntitiesService, IDisposable { private static int _count; private readonly ILogger _logger; private readonly ITicker _ticker; private readonly ConcurrentQueue _onAddQueue = new(); private readonly HashSet _addingEntities = new(); private readonly Dictionary> _typeCaches = new(); private readonly Dictionary> _typeInstances = new(); public EntitiesService(ILogger logger, ITicker ticker) { _logger = logger; _ticker = ticker; _pool = new(ObjectGenerator, null, 64); _count++; logger.LogInformation($"已创建EntitiesService,当前有:{_count}个实例"); _ticker?.Add(OnTick); } private void OnTick(float obj) { while (_onAddQueue.TryDequeue(out var entity)) { if(_addingEntities.Remove(entity) is false)continue; OnAdd?.Invoke(entity); MakeCache(entity); } _addingEntities.Clear(); } private void MakeCache(IEntity entity) { foreach (var serviceDescriptor in entity.ServiceCollection) { var typeHash = serviceDescriptor.ServiceType.GetHashCode(); var hashSet = _typeCaches.GetOrCreate(typeHash); hashSet.Add(entity.Id); } } private readonly Dictionary _entitiesInternal = new(); public event Action OnAdd; public event Action OnRemove; IReadOnlyDictionary IEntitiesService.Entities => _entitiesInternal; private readonly Pool> _pool; private HashSet ObjectGenerator() { return new HashSet(math.max(8192, _typeCaches.Count * 2)); } public bool Register(IEntity entity) { if (!_entitiesInternal.TryAdd(entity.Id, entity)) return false; if (_ticker is not null) { _addingEntities.Add(entity); _onAddQueue.Enqueue(entity); } else { OnAdd?.Invoke(entity); MakeCache(entity); } return true; } public bool UnRegister(IEntity entity) { if (!_entitiesInternal.TryRemove(entity.Id)) return false; _addingEntities.Remove(entity); foreach (var serviceDescriptor in entity.ServiceCollection) { var typeHash = serviceDescriptor.ServiceType.GetHashCode(); var hashSet = _typeCaches.GetOrCreate(typeHash); hashSet.Remove(entity.Id); } _typeInstances.TryRemove(entity.Id); OnRemove?.Invoke(entity); return true; } public CancellationToken CancellationToken => _cancellationTokenSource.Token; private readonly CancellationTokenSource _cancellationTokenSource = new(); public IEntity Get(int id) { return _entitiesInternal[id]; } public bool TryGetEntity(int id, out IEntity entity) { return _entitiesInternal.TryGetValue(id, out entity); } public IEntity GetOrAdd(int id, Func factory) { if (_entitiesInternal.TryGetValue(id, out var current)) { _entitiesInternal.TryAdd(id, current = factory.Invoke(id)); } return current; } public Span QueryComponents() where T : class { var pool = ArrayPool.Shared; var hashset = _typeCaches.GetOrCreate(typeof(T).GetHashCode()); var array = pool.Rent(hashset.Count); var collection = _pool.Take(); collection.Clear(); collection.UnionWith(hashset); var i = 0; foreach (var id in collection) { var instances = _typeInstances.GetOrCreate(id); var h0 = typeof(T).GetHashCode(); if (instances.TryGetValue(h0, out var v0) is false) { instances[h0] = v0 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } array[i] = Unsafe.As(v0); i++; } try { return new Span(array, 0, i); } finally { pool.Return(array); _pool.Return(collection); } } public Span<(T, T1)> QueryComponents() where T : class where T1 : class { var pool = ArrayPool<(T, T1)>.Shared; var hashset = _typeCaches.GetOrCreate(typeof(T).GetHashCode()); var t1Set = _typeCaches.GetOrCreate(typeof(T1).GetHashCode()); var count = math.max(hashset.Count, t1Set.Count); var array = pool.Rent(count); var collection = _pool.Take(); collection.Clear(); collection.UnionWith(hashset); collection.IntersectWith(t1Set); var i = 0; foreach (var id in collection) { var instances = _typeInstances.GetOrCreate(id); var h0 = typeof(T).GetHashCode(); var h1 = typeof(T1).GetHashCode(); if (instances.TryGetValue(h0, out var v0) is false) { instances[h0] = v0 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h1, out var v1) is false) { instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } array[i] = (Unsafe.As(v0), Unsafe.As(v1)); i++; } try { return new Span<(T, T1)>(array, 0, i); } finally { pool.Return(array); _pool.Return(collection); } } public Span<(T, T1, T2)> QueryComponents() where T : class where T1 : class where T2 : class { var pool = ArrayPool<(T, T1, T2)>.Shared; var hashset = _typeCaches.GetOrCreate(typeof(T).GetHashCode()); var t1Set = _typeCaches.GetOrCreate(typeof(T1).GetHashCode()); var t2Set = _typeCaches.GetOrCreate(typeof(T2).GetHashCode()); var count = math.max(hashset.Count, math.max(t1Set.Count, t2Set.Count)); var array = pool.Rent(count); var collection = _pool.Take(); collection.Clear(); collection.UnionWith(hashset); collection.IntersectWith(t1Set); collection.IntersectWith(t2Set); var i = 0; foreach (var id in collection) { var instances = _typeInstances.GetOrCreate(id); var h0 = typeof(T).GetHashCode(); var h1 = typeof(T1).GetHashCode(); var h2 = typeof(T2).GetHashCode(); if (instances.TryGetValue(h0, out var v0) is false) { instances[h0] = v0 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h1, out var v1) is false) { instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h2, out var v2) is false) { instances[h2] = v2 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } array[i] = (Unsafe.As(v0), Unsafe.As(v1), Unsafe.As(v2)); i++; } try { return new Span<(T, T1, T2)>(array, 0, i); } finally { pool.Return(array); _pool.Return(collection); } } public Span<(T, T1, T2, T3)> QueryComponents() where T : class where T1 : class where T2 : class where T3 : class { var pool = ArrayPool<(T, T1, T2, T3)>.Shared; var hashset = _typeCaches.GetOrCreate(typeof(T).GetHashCode()); var t1Set = _typeCaches.GetOrCreate(typeof(T1).GetHashCode()); var t2Set = _typeCaches.GetOrCreate(typeof(T2).GetHashCode()); var t3Set = _typeCaches.GetOrCreate(typeof(T3).GetHashCode()); var count = math.max(hashset.Count, math.max(t1Set.Count, math.max(t2Set.Count, t3Set.Count))); var array = pool.Rent(count); var collection = _pool.Take(); collection.Clear(); collection.UnionWith(hashset); collection.IntersectWith(t1Set); collection.IntersectWith(t2Set); collection.IntersectWith(t3Set); var i = 0; foreach (var id in collection) { var instances = _typeInstances.GetOrCreate(id); var h0 = typeof(T).GetHashCode(); var h1 = typeof(T1).GetHashCode(); var h2 = typeof(T2).GetHashCode(); var h3 = typeof(T3).GetHashCode(); if (instances.TryGetValue(h0, out var v0) is false) { instances[h0] = v0 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h1, out var v1) is false) { instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h2, out var v2) is false) { instances[h2] = v2 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h3, out var v3) is false) { instances[h3] = v3 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } array[i] = (Unsafe.As(v0), Unsafe.As(v1), Unsafe.As(v2), Unsafe.As(v3)); i++; } try { return new Span<(T, T1, T2, T3)>(array, 0, i); } finally { pool.Return(array); _pool.Return(collection); } } public Span<(T, T1, T2, T3, T4)> QueryComponents() where T : class where T1 : class where T2 : class where T3 : class where T4 : class { var pool = ArrayPool<(T, T1, T2, T3,T4)>.Shared; var hashset = _typeCaches.GetOrCreate(typeof(T).GetHashCode()); var t1Set = _typeCaches.GetOrCreate(typeof(T1).GetHashCode()); var t2Set = _typeCaches.GetOrCreate(typeof(T2).GetHashCode()); var t3Set = _typeCaches.GetOrCreate(typeof(T3).GetHashCode()); var t4Set = _typeCaches.GetOrCreate(typeof(T4).GetHashCode()); var count = math.max(hashset.Count, math.max(t1Set.Count, math.max(t2Set.Count,math.max(t3Set.Count,t4Set.Count)))); var array = pool.Rent(count); var collection = _pool.Take(); collection.Clear(); collection.UnionWith(hashset); collection.IntersectWith(t1Set); collection.IntersectWith(t2Set); collection.IntersectWith(t3Set); var i = 0; foreach (var id in collection) { var instances = _typeInstances.GetOrCreate(id); var h0 = typeof(T).GetHashCode(); var h1 = typeof(T1).GetHashCode(); var h2 = typeof(T2).GetHashCode(); var h3 = typeof(T3).GetHashCode(); var h4 = typeof(T4).GetHashCode(); if (instances.TryGetValue(h0, out var v0) is false) { instances[h0] = v0 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h1, out var v1) is false) { instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h2, out var v2) is false) { instances[h2] = v2 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h3, out var v3) is false) { instances[h3] = v3 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h4, out var v4) is false) { instances[h4] = v4 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } array[i] = (Unsafe.As(v0), Unsafe.As(v1), Unsafe.As(v2), Unsafe.As(v3),Unsafe.As(v4)); i++; } try { return new Span<(T, T1, T2, T3,T4)>(array, 0, i); } finally { pool.Return(array); _pool.Return(collection); } } public Span<(T, T1, T2, T3, T4, T5)> QueryComponents() where T : class where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class { var pool = ArrayPool<(T, T1, T2, T3, T4, T5)>.Shared; var hashset = _typeCaches.GetOrCreate(typeof(T).GetHashCode()); var t1Set = _typeCaches.GetOrCreate(typeof(T1).GetHashCode()); var t2Set = _typeCaches.GetOrCreate(typeof(T2).GetHashCode()); var t3Set = _typeCaches.GetOrCreate(typeof(T3).GetHashCode()); var t4Set = _typeCaches.GetOrCreate(typeof(T4).GetHashCode()); var t5Set = _typeCaches.GetOrCreate(typeof(T5).GetHashCode()); var count = math.max(hashset.Count, math.max(t1Set.Count, math.max(t2Set.Count, math.max(t3Set.Count, math.max(t4Set.Count, t5Set.Count))))); var array = pool.Rent(count); var collection = _pool.Take(); collection.Clear(); collection.UnionWith(hashset); collection.IntersectWith(t1Set); collection.IntersectWith(t2Set); collection.IntersectWith(t3Set); collection.IntersectWith(t4Set); collection.IntersectWith(t5Set); var i = 0; foreach (var id in collection) { var instances = _typeInstances.GetOrCreate(id); var h0 = typeof(T).GetHashCode(); var h1 = typeof(T1).GetHashCode(); var h2 = typeof(T2).GetHashCode(); var h3 = typeof(T3).GetHashCode(); var h4 = typeof(T4).GetHashCode(); var h5 = typeof(T5).GetHashCode(); if (instances.TryGetValue(h0, out var v0) is false) { instances[h0] = v0 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h1, out var v1) is false) { instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h2, out var v2) is false) { instances[h2] = v2 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h3, out var v3) is false) { instances[h3] = v3 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h4, out var v4) is false) { instances[h4] = v4 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h5, out var v5) is false) { instances[h5] = v5 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } array[i] = (Unsafe.As(v0), Unsafe.As(v1), Unsafe.As(v2), Unsafe.As(v3), Unsafe.As(v4), Unsafe.As(v5)); i++; } pool.Return(array); _pool.Return(collection); return new Span<(T, T1, T2, T3, T4, T5)>(array, 0, i); } public Span<(T, T1, T2, T3, T4, T5, T6)> QueryComponents() where T : class where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class { var pool = ArrayPool<(T, T1, T2, T3, T4, T5, T6)>.Shared; var hashset = _typeCaches.GetOrCreate(typeof(T).GetHashCode()); var t1Set = _typeCaches.GetOrCreate(typeof(T1).GetHashCode()); var t2Set = _typeCaches.GetOrCreate(typeof(T2).GetHashCode()); var t3Set = _typeCaches.GetOrCreate(typeof(T3).GetHashCode()); var t4Set = _typeCaches.GetOrCreate(typeof(T4).GetHashCode()); var t5Set = _typeCaches.GetOrCreate(typeof(T5).GetHashCode()); var t6Set = _typeCaches.GetOrCreate(typeof(T6).GetHashCode()); var count = math.max(hashset.Count, math.max(t1Set.Count, math.max(t2Set.Count, math.max(t3Set.Count, math.max(t4Set.Count, math.max(t5Set.Count, t6Set.Count)))))); var array = pool.Rent(count); var collection = _pool.Take(); collection.Clear(); collection.UnionWith(hashset); collection.IntersectWith(t1Set); collection.IntersectWith(t2Set); collection.IntersectWith(t3Set); collection.IntersectWith(t4Set); collection.IntersectWith(t5Set); collection.IntersectWith(t6Set); var i = 0; foreach (var id in collection) { var instances = _typeInstances.GetOrCreate(id); var h0 = typeof(T).GetHashCode(); var h1 = typeof(T1).GetHashCode(); var h2 = typeof(T2).GetHashCode(); var h3 = typeof(T3).GetHashCode(); var h4 = typeof(T4).GetHashCode(); var h5 = typeof(T5).GetHashCode(); var h6 = typeof(T6).GetHashCode(); if (instances.TryGetValue(h0, out var v0) is false) { instances[h0] = v0 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h1, out var v1) is false) { instances[h1] = v1 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h2, out var v2) is false) { instances[h2] = v2 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h3, out var v3) is false) { instances[h3] = v3 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h4, out var v4) is false) { instances[h4] = v4 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h5, out var v5) is false) { instances[h5] = v5 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } if (instances.TryGetValue(h6, out var v6) is false) { instances[h6] = v6 = _entitiesInternal[id].ServiceProvider.GetRequiredService(); } array[i] = (Unsafe.As(v0), Unsafe.As(v1), Unsafe.As(v2), Unsafe.As(v3), Unsafe.As(v4), Unsafe.As(v5), Unsafe.As(v6)); i++; } try { return new Span<(T, T1, T2, T3, T4, T5, T6)>(array, 0, i); } finally { pool.Return(array); _pool.Return(collection); } } public void Dispose() { _count--; if (_count <= 0) { _entitiesInternal.Clear(); } _logger.LogInformation($"已释放,还剩{_count}个实例"); _cancellationTokenSource?.Dispose(); _ticker?.Remove(OnTick); } } }