using Ghost.Core; using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections; using System.Runtime.CompilerServices; namespace Ghost.Entities; public partial class World { private static List s_worlds = new(4); private static Queue> s_freeWorldSlots = new(); internal static Identifier EmptyArchetypeID => new Identifier(0); public static int WorldCount => s_worlds.Count - s_freeWorldSlots.Count; public static World Create(int entityCapacity = 16) { lock (s_worlds) { if (s_freeWorldSlots.TryDequeue(out var index)) { s_worlds[index.value] = new World(index, entityCapacity); } else { index = new Identifier(s_worlds.Count); s_worlds.Add(new World(index, entityCapacity)); } return s_worlds[index.value]!; } } public static void Destroy(Identifier id) { lock (s_worlds) { if (id.value < 0 || id.value >= s_worlds.Count) { return; } var world = s_worlds[id.value]; if (world is not null) { world.Dispose(); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Result GetWorld(Identifier id) { if (id.value < 0 || id.value >= s_worlds.Count) { return Result.Create(default(World)!, ResultStatus.NotFound); } var world = s_worlds[id.value]; if (world is null) { return Result.Create(default(World)!, ResultStatus.NotFound); } return Result.Create(world, ResultStatus.Success); } } public partial class World : IIdentifierType, IDisposable, IEquatable { private readonly Identifier _id; private EntityManager _entityManager; private EntityCommandBuffer _entityCommandBuffer; private UnsafeList _archetypes; private UnsafeList _entityQueries; private UnsafeHashMap> _archetypeLookup; // Signature Hash to Archetype ID private UnsafeHashMap> _querieLookup; // Query Mask Hash to Query ID private bool _disposed = false; internal int ArchetypeCount => _archetypes.Count; public Identifier ID => _id; public EntityManager EntityManager => _entityManager; public EntityCommandBuffer EntityCommandBuffer => _entityCommandBuffer; private World(Identifier id, int entityCapacity) { _id = id; _archetypes = new UnsafeList(16, Allocator.Persistent); _entityQueries = new UnsafeList(16, Allocator.Persistent); _archetypeLookup = new UnsafeHashMap>(16, Allocator.Persistent); _querieLookup = new UnsafeHashMap>(16, Allocator.Persistent); _entityManager = new EntityManager(this, entityCapacity); _entityCommandBuffer = new EntityCommandBuffer(_entityManager); // Create the empty archetype CreateArchetype(ReadOnlySpan>.Empty, 0); } ~World() { Dispose(); } internal Identifier CreateArchetype(ReadOnlySpan> componentTypeIDs, int signatureHash) { var arcID = new Identifier(_archetypes.Count); _archetypes.Add(new Archetype(arcID, _id, componentTypeIDs)); _archetypeLookup.Add(signatureHash, arcID); for (int i = 0; i < _entityQueries.Count; i++) { ref var query = ref _entityQueries[i]; query.AddArchetypeIfMatch(_archetypes[arcID.value]); } return arcID; } internal Identifier GetArchetypeIDBySignatureHash(int signatureHash) { if (_archetypeLookup.TryGetValue(signatureHash, out var arcID)) { return arcID; } return Identifier.Invalid; } internal ref Archetype GetArchetypeReference(Identifier id) { return ref _archetypes[id.value]; } internal Identifier CreateEntityQuery(EntityQueryMask mask) { var queryID = new Identifier(_entityQueries.Count); _entityQueries.Add(new EntityQuery(_id, mask)); _querieLookup.Add(mask.GetHashCode(), queryID); return queryID; } internal Identifier GetEntityQueryIDByMaskHash(int maskHash) { if (_querieLookup.TryGetValue(maskHash, out var queryID)) { return queryID; } return Identifier.Invalid; } public ref EntityQuery GetEntityQueryReference(Identifier id) { return ref _entityQueries[id.value]; } public bool Equals(World? other) { return other is not null && _id == other._id; } public override int GetHashCode() { return _id.GetHashCode(); } public override bool Equals(object? obj) { return obj is World other && Equals(other); } public static bool operator ==(World? left, World? right) { return left?.Equals(right) ?? right is null; } public static bool operator !=(World? left, World? right) { return !(left == right); } public void Dispose() { if (_disposed) { return; } _entityManager.Dispose(); _entityCommandBuffer.Dispose(); _archetypes.Dispose(); _entityQueries.Dispose(); _archetypeLookup.Dispose(); _querieLookup.Dispose(); s_freeWorldSlots.Enqueue(_id); s_worlds[_id] = null; _disposed = true; GC.SuppressFinalize(this); } }