World State Save and Load #2
			
				
			
		
		
		
	|  | @ -4,6 +4,7 @@ | |||
|     <TargetFramework>net6.0</TargetFramework> | ||||
|     <Nullable>enable</Nullable> | ||||
|     <Platforms>x64</Platforms> | ||||
|     <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||
|   </PropertyGroup> | ||||
| 
 | ||||
| </Project> | ||||
|  |  | |||
|  | @ -11,13 +11,13 @@ namespace MoonTools.ECS | |||
| 
 | ||||
| 		private Dictionary<Type, HashSet<FilterSignature>> typeToFilterSignatures = new Dictionary<Type, HashSet<FilterSignature>>(); | ||||
| 
 | ||||
| 		private Dictionary<int, HashSet<Type>> entityComponentMap = new Dictionary<int, HashSet<Type>>(); | ||||
| 
 | ||||
| #if DEBUG | ||||
| 		private Dictionary<Type, Filter> singleComponentFilters = new Dictionary<Type, Filter>(); | ||||
| #endif | ||||
| 
 | ||||
| 		internal void Register<TComponent>() where TComponent : struct | ||||
| 		private HashSet<Type> TypesWithDisabledSerialization = new HashSet<Type>(); | ||||
| 
 | ||||
| 		internal void Register<TComponent>() where TComponent : unmanaged | ||||
| 		{ | ||||
| 			if (!storages.ContainsKey(typeof(TComponent))) | ||||
| 			{ | ||||
|  | @ -33,19 +33,19 @@ namespace MoonTools.ECS | |||
| 			return storages[type]; | ||||
| 		} | ||||
| 
 | ||||
| 		private ComponentStorage<TComponent> Lookup<TComponent>() where TComponent : struct | ||||
| 		private ComponentStorage<TComponent> Lookup<TComponent>() where TComponent : unmanaged | ||||
| 		{ | ||||
| 			// TODO: is it possible to optimize this? | ||||
| 			Register<TComponent>(); | ||||
| 			return (ComponentStorage<TComponent>) storages[typeof(TComponent)]; | ||||
| 		} | ||||
| 
 | ||||
| 		public bool Some<TComponent>() where TComponent : struct | ||||
| 		public bool Some<TComponent>() where TComponent : unmanaged | ||||
| 		{ | ||||
| 			return Lookup<TComponent>().Any(); | ||||
| 		} | ||||
| 
 | ||||
| 		public bool Has<TComponent>(int entityID) where TComponent : struct | ||||
| 		public bool Has<TComponent>(int entityID) where TComponent : unmanaged | ||||
| 		{ | ||||
| 			return Lookup<TComponent>().Has(entityID); | ||||
| 		} | ||||
|  | @ -55,29 +55,22 @@ namespace MoonTools.ECS | |||
| 			return Lookup(type).Has(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		public ref readonly TComponent Get<TComponent>(int entityID) where TComponent : struct | ||||
| 		public ref readonly TComponent Get<TComponent>(int entityID) where TComponent : unmanaged | ||||
| 		{ | ||||
| 			return ref Lookup<TComponent>().Get(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		public ref readonly TComponent Get<TComponent>() where TComponent : struct | ||||
| 		public ref readonly TComponent Get<TComponent>() where TComponent : unmanaged | ||||
| 		{ | ||||
| 			return ref Lookup<TComponent>().Get(); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Set<TComponent>(int entityID, in TComponent component) where TComponent : struct | ||||
| 		public void Set<TComponent>(int entityID, in TComponent component) where TComponent : unmanaged | ||||
| 		{ | ||||
| 			Lookup<TComponent>().Set(entityID, component); | ||||
| 
 | ||||
| 			if (!entityComponentMap.ContainsKey(entityID)) | ||||
| 			{ | ||||
| 				entityComponentMap.Add(entityID, new HashSet<Type>()); | ||||
| 			} | ||||
| 
 | ||||
| 			var notFound = entityComponentMap[entityID].Add(typeof(TComponent)); | ||||
| 			var existed = Lookup<TComponent>().Set(entityID, component); | ||||
| 
 | ||||
| 			// update filters | ||||
| 			if (notFound) | ||||
| 			if (!existed) | ||||
| 			{ | ||||
| 				if (typeToFilterSignatures.TryGetValue(typeof(TComponent), out var filterSignatures)) | ||||
| 				{ | ||||
|  | @ -89,24 +82,22 @@ namespace MoonTools.ECS | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		public Entity GetSingletonEntity<TComponent>() where TComponent : struct | ||||
| 		public Entity GetSingletonEntity<TComponent>() where TComponent : unmanaged | ||||
| 		{ | ||||
| 			return Lookup<TComponent>().FirstEntity(); | ||||
| 		} | ||||
| 
 | ||||
| 		public ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct | ||||
| 		public ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : unmanaged | ||||
| 		{ | ||||
| 			return Lookup<TComponent>().AllComponents(); | ||||
| 		} | ||||
| 
 | ||||
| 		private void Remove(Type type, int entityID) | ||||
| 		{ | ||||
| 			Lookup(type).Remove(entityID); | ||||
| 
 | ||||
| 			var found = entityComponentMap[entityID].Remove(type); | ||||
| 			var existed = Lookup(type).Remove(entityID); | ||||
| 
 | ||||
| 			// update filters | ||||
| 			if (found) | ||||
| 			if (existed) | ||||
| 			{ | ||||
| 				if (typeToFilterSignatures.TryGetValue(type, out var filterSignatures)) | ||||
| 				{ | ||||
|  | @ -118,14 +109,12 @@ namespace MoonTools.ECS | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		public void Remove<TComponent>(int entityID) where TComponent : struct | ||||
| 		public void Remove<TComponent>(int entityID) where TComponent : unmanaged | ||||
| 		{ | ||||
| 			Lookup<TComponent>().Remove(entityID); | ||||
| 
 | ||||
| 			var found = entityComponentMap[entityID].Remove(typeof(TComponent)); | ||||
| 			var existed = Lookup<TComponent>().Remove(entityID); | ||||
| 
 | ||||
| 			// update filters | ||||
| 			if (found) | ||||
| 			if (existed) | ||||
| 			{ | ||||
| 				if (typeToFilterSignatures.TryGetValue(typeof(TComponent), out var filterSignatures)) | ||||
| 				{ | ||||
|  | @ -137,16 +126,12 @@ namespace MoonTools.ECS | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// TODO: is there some way to optimize this without complicating serialization? | ||||
| 		public void OnEntityDestroy(int entityID) | ||||
| 		{ | ||||
| 			if (entityComponentMap.ContainsKey(entityID)) | ||||
| 			foreach (var type in storages.Keys) | ||||
| 			{ | ||||
| 				foreach (var type in entityComponentMap[entityID]) | ||||
| 				{ | ||||
| 					Remove(type, entityID); | ||||
| 				} | ||||
| 
 | ||||
| 				entityComponentMap.Remove(entityID); | ||||
| 				Remove(type, entityID); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  | @ -231,6 +216,53 @@ namespace MoonTools.ECS | |||
| 			filterSignatureToEntityIDs[filterSignature].Add(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		public void DisableSerialization<TComponent>() where TComponent : unmanaged | ||||
| 		{ | ||||
| 			TypesWithDisabledSerialization.Add(typeof(TComponent)); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Save(ComponentDepotState state) | ||||
| 		{ | ||||
| 			foreach (var (type, storage) in storages) | ||||
| 			{ | ||||
| 				if (!TypesWithDisabledSerialization.Contains(type)) | ||||
| 				{ | ||||
| 					if (!state.StorageStates.ContainsKey(type)) | ||||
| 					{ | ||||
| 						state.StorageStates.Add(type, storage.CreateState()); | ||||
| 					} | ||||
| 
 | ||||
| 					storage.Save(state.StorageStates[type]); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			foreach (var (signature, set) in filterSignatureToEntityIDs) | ||||
| 			{ | ||||
| 				// FIXME: we could cache this | ||||
| 				if (!signature.Included.Overlaps(TypesWithDisabledSerialization) && !signature.Excluded.Overlaps(TypesWithDisabledSerialization)) | ||||
| 				{ | ||||
| 					if (!state.FilterStates.ContainsKey(signature)) | ||||
| 					{ | ||||
| 						state.FilterStates[signature] = new IndexableSetState<int>(set.Count); | ||||
| 					} | ||||
| 					set.Save(state.FilterStates[signature]); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		public void Load(ComponentDepotState state) | ||||
| 		{ | ||||
| 			foreach (var (type, storageState) in state.StorageStates) | ||||
| 			{ | ||||
| 				storages[type].Load(storageState); | ||||
| 			} | ||||
| 
 | ||||
| 			foreach (var (signature, setState) in state.FilterStates) | ||||
| 			{ | ||||
| 				filterSignatureToEntityIDs[signature].Load(setState); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| #if DEBUG | ||||
| 		public IEnumerable<object> Debug_GetAllComponents(int entityID) | ||||
| 		{ | ||||
|  |  | |||
|  | @ -1,17 +1,20 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Runtime.InteropServices; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
| 	internal abstract class ComponentStorage | ||||
| 	{ | ||||
| 		public abstract bool Has(int entityID); | ||||
| 		public abstract void Remove(int entityID); | ||||
| 		public abstract bool Remove(int entityID); | ||||
| 		public abstract object Debug_Get(int entityID); | ||||
| 		public abstract ComponentStorageState CreateState(); | ||||
| 		public abstract void Save(ComponentStorageState state); | ||||
| 		public abstract void Load(ComponentStorageState state); | ||||
| 	} | ||||
| 
 | ||||
| 	// FIXME: we can probably get rid of this weird entity storage system by using filters | ||||
| 	internal class ComponentStorage<TComponent> : ComponentStorage where TComponent : struct | ||||
| 	internal class ComponentStorage<TComponent> : ComponentStorage where TComponent : unmanaged | ||||
| 	{ | ||||
| 		private int nextID; | ||||
| 		private readonly Dictionary<int, int> entityIDToStorageIndex = new Dictionary<int, int>(16); | ||||
|  | @ -49,8 +52,11 @@ namespace MoonTools.ECS | |||
| 			return ref components[0]; | ||||
| 		} | ||||
| 
 | ||||
| 		public void Set(int entityID, in TComponent component) | ||||
| 		// Returns true if the entity already had this component. | ||||
| 		public bool Set(int entityID, in TComponent component) | ||||
| 		{ | ||||
| 			bool result = true; | ||||
| 
 | ||||
| 			if (!entityIDToStorageIndex.ContainsKey(entityID)) | ||||
| 			{ | ||||
| 				var index = nextID; | ||||
|  | @ -64,12 +70,17 @@ namespace MoonTools.ECS | |||
| 
 | ||||
| 				entityIDToStorageIndex[entityID] = index; | ||||
| 				entityIDs[index] = entityID; | ||||
| 
 | ||||
| 				result = false; | ||||
| 			} | ||||
| 
 | ||||
| 			components[entityIDToStorageIndex[entityID]] = component; | ||||
| 
 | ||||
| 			return result; | ||||
| 		} | ||||
| 
 | ||||
| 		public override void Remove(int entityID) | ||||
| 		// Returns true if the entity had this component. | ||||
| 		public override bool Remove(int entityID) | ||||
| 		{ | ||||
| 			if (entityIDToStorageIndex.ContainsKey(entityID)) | ||||
| 			{ | ||||
|  | @ -88,7 +99,11 @@ namespace MoonTools.ECS | |||
| 				} | ||||
| 
 | ||||
| 				nextID -= 1; | ||||
| 
 | ||||
| 				return true; | ||||
| 			} | ||||
| 
 | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		public void Clear() | ||||
|  | @ -106,5 +121,44 @@ namespace MoonTools.ECS | |||
| 		{ | ||||
| 			return new Entity(entityIDs[0]); | ||||
| 		} | ||||
| 
 | ||||
| 		public override ComponentStorageState CreateState() | ||||
| 		{ | ||||
| 			return ComponentStorageState.Create<TComponent>(nextID); | ||||
| 		} | ||||
| 
 | ||||
| 		public override void Save(ComponentStorageState state) | ||||
| 		{ | ||||
| 			ReadOnlySpan<byte> entityIDBytes = MemoryMarshal.Cast<int, byte>(new ReadOnlySpan<int>(entityIDs, 0, nextID)); | ||||
| 
 | ||||
| 			if (entityIDBytes.Length > state.EntityIDs.Length) | ||||
| 			{ | ||||
| 				Array.Resize(ref state.EntityIDs, entityIDBytes.Length); | ||||
| 			} | ||||
| 			entityIDBytes.CopyTo(state.EntityIDs); | ||||
| 
 | ||||
| 			ReadOnlySpan<byte> componentBytes = MemoryMarshal.Cast<TComponent, byte>(AllComponents()); | ||||
| 			if (componentBytes.Length > state.Components.Length) | ||||
| 			{ | ||||
| 				Array.Resize(ref state.Components, componentBytes.Length); | ||||
| 			} | ||||
| 			componentBytes.CopyTo(state.Components); | ||||
| 
 | ||||
| 			state.Count = nextID; | ||||
| 		} | ||||
| 
 | ||||
| 		public override void Load(ComponentStorageState state) | ||||
| 		{ | ||||
| 			state.EntityIDs.CopyTo(MemoryMarshal.Cast<int, byte>(entityIDs)); | ||||
| 			state.Components.CopyTo(MemoryMarshal.Cast<TComponent, byte>(components)); | ||||
| 
 | ||||
| 			entityIDToStorageIndex.Clear(); | ||||
| 			for (var i = 0; i < state.Count; i += 1) | ||||
| 			{ | ||||
| 				entityIDToStorageIndex[entityIDs[i]] = i; | ||||
| 			} | ||||
| 
 | ||||
| 			nextID = state.Count; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -5,52 +5,43 @@ namespace MoonTools.ECS | |||
| { | ||||
| 	public abstract class EntityComponentReader | ||||
| 	{ | ||||
| 		internal EntityStorage EntityStorage; | ||||
| 		internal ComponentDepot ComponentDepot; | ||||
| 		internal RelationDepot RelationDepot; | ||||
| 		protected readonly World World; | ||||
| 		internal EntityStorage EntityStorage => World.EntityStorage; | ||||
| 		internal ComponentDepot ComponentDepot => World.ComponentDepot; | ||||
| 		internal RelationDepot RelationDepot => World.RelationDepot; | ||||
| 		protected FilterBuilder FilterBuilder => new FilterBuilder(ComponentDepot); | ||||
| 
 | ||||
| 		internal void RegisterEntityStorage(EntityStorage entityStorage) | ||||
| 		public EntityComponentReader(World world) | ||||
| 		{ | ||||
| 			EntityStorage = entityStorage; | ||||
| 			World = world; | ||||
| 		} | ||||
| 
 | ||||
| 		internal void RegisterComponentDepot(ComponentDepot componentDepot) | ||||
| 		{ | ||||
| 			ComponentDepot = componentDepot; | ||||
| 		} | ||||
| 
 | ||||
| 		internal void RegisterRelationDepot(RelationDepot relationDepot) | ||||
| 		{ | ||||
| 			RelationDepot = relationDepot; | ||||
| 		} | ||||
| 
 | ||||
| 		protected ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct | ||||
| 		protected ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : unmanaged | ||||
| 		{ | ||||
| 			return ComponentDepot.ReadComponents<TComponent>(); | ||||
| 		} | ||||
| 
 | ||||
| 		protected bool Has<TComponent>(in Entity entity) where TComponent : struct | ||||
| 		protected bool Has<TComponent>(in Entity entity) where TComponent : unmanaged | ||||
| 		{ | ||||
| 			return ComponentDepot.Has<TComponent>(entity.ID); | ||||
| 		} | ||||
| 
 | ||||
| 		protected bool Some<TComponent>() where TComponent : struct | ||||
| 		protected bool Some<TComponent>() where TComponent : unmanaged | ||||
| 		{ | ||||
| 			return ComponentDepot.Some<TComponent>(); | ||||
| 		} | ||||
| 
 | ||||
| 		protected ref readonly TComponent Get<TComponent>(in Entity entity) where TComponent : struct | ||||
| 		protected ref readonly TComponent Get<TComponent>(in Entity entity) where TComponent : unmanaged | ||||
| 		{ | ||||
| 			return ref ComponentDepot.Get<TComponent>(entity.ID); | ||||
| 		} | ||||
| 
 | ||||
| 		protected ref readonly TComponent GetSingleton<TComponent>() where TComponent : struct | ||||
| 		protected ref readonly TComponent GetSingleton<TComponent>() where TComponent : unmanaged | ||||
| 		{ | ||||
| 			return ref ComponentDepot.Get<TComponent>(); | ||||
| 		} | ||||
| 
 | ||||
| 		protected Entity GetSingletonEntity<TComponent>() where TComponent : struct | ||||
| 		protected Entity GetSingletonEntity<TComponent>() where TComponent : unmanaged | ||||
| 		{ | ||||
| 			return ComponentDepot.GetSingletonEntity<TComponent>(); | ||||
| 		} | ||||
|  | @ -60,22 +51,22 @@ namespace MoonTools.ECS | |||
| 			return EntityStorage.Exists(entity); | ||||
| 		} | ||||
| 
 | ||||
| 		protected IEnumerable<(Entity, Entity, TRelationKind)> Relations<TRelationKind>() where TRelationKind : struct | ||||
| 		protected IEnumerable<(Entity, Entity, TRelationKind)> Relations<TRelationKind>() where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return RelationDepot.Relations<TRelationKind>(); | ||||
| 		} | ||||
| 
 | ||||
| 		protected bool Related<TRelationKind>(in Entity a, in Entity b) where TRelationKind : struct | ||||
| 		protected bool Related<TRelationKind>(in Entity a, in Entity b) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return RelationDepot.Related<TRelationKind>(a.ID, b.ID); | ||||
| 		} | ||||
| 
 | ||||
| 		protected IEnumerable<(Entity, TRelationKind)> RelatedToA<TRelationKind>(in Entity entity) where TRelationKind : struct | ||||
| 		protected IEnumerable<(Entity, TRelationKind)> RelatedToA<TRelationKind>(in Entity entity) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return RelationDepot.RelatedToA<TRelationKind>(entity.ID); | ||||
| 		} | ||||
| 
 | ||||
| 		protected IEnumerable<(Entity, TRelationKind)> RelatedToB<TRelationKind>(in Entity entity) where TRelationKind : struct | ||||
| 		protected IEnumerable<(Entity, TRelationKind)> RelatedToB<TRelationKind>(in Entity entity) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return RelationDepot.RelatedToB<TRelationKind>(entity.ID); | ||||
| 		} | ||||
|  |  | |||
|  | @ -1,22 +1,75 @@ | |||
| namespace MoonTools.ECS | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
| 	internal class EntityStorage | ||||
| 	{ | ||||
| 		public IDStorage idStorage = new IDStorage(); | ||||
| 		private int nextID = 0; | ||||
| 		private readonly Stack<int> availableIDs = new Stack<int>(); | ||||
| 		private readonly HashSet<int> availableIDHash = new HashSet<int>(); | ||||
| 
 | ||||
| 		public Entity Create() | ||||
| 		{ | ||||
| 			return new Entity(idStorage.NextID()); | ||||
| 			return new Entity(NextID()); | ||||
| 		} | ||||
| 
 | ||||
| 		public bool Exists(in Entity entity) | ||||
| 		{ | ||||
| 			return idStorage.Taken(entity.ID); | ||||
| 			return Taken(entity.ID); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Destroy(in Entity entity) | ||||
| 		{ | ||||
| 			idStorage.Release(entity.ID); | ||||
| 			Release(entity.ID); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Save(EntityStorageState state) | ||||
| 		{ | ||||
| 			state.NextID = nextID; | ||||
| 			state.availableIDs.Clear(); | ||||
| 			foreach (var id in availableIDs) | ||||
| 			{ | ||||
| 				state.availableIDs.Add(id); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		public void Load(EntityStorageState state) | ||||
| 		{ | ||||
| 			nextID = state.NextID; | ||||
| 			availableIDs.Clear(); | ||||
| 			availableIDHash.Clear(); | ||||
| 			foreach (var id in state.availableIDs) | ||||
| 			{ | ||||
| 				availableIDs.Push(id); | ||||
| 				availableIDHash.Add(id); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		private int NextID() | ||||
| 		{ | ||||
| 			if (availableIDs.Count > 0) | ||||
| 			{ | ||||
| 				var id = availableIDs.Pop(); | ||||
| 				availableIDHash.Remove(id); | ||||
| 				return id; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				var id = nextID; | ||||
| 				nextID += 1; | ||||
| 				return id; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		private bool Taken(int id) | ||||
| 		{ | ||||
| 			return !availableIDHash.Contains(id) && id < nextID; | ||||
| 		} | ||||
| 
 | ||||
| 		private void Release(int id) | ||||
| 		{ | ||||
| 			availableIDs.Push(id); | ||||
| 			availableIDHash.Add(id); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -23,14 +23,14 @@ namespace MoonTools.ECS | |||
| 			Excluded = excluded; | ||||
| 		} | ||||
| 
 | ||||
| 		public FilterBuilder Include<TComponent>() where TComponent : struct | ||||
| 		public FilterBuilder Include<TComponent>() where TComponent : unmanaged | ||||
| 		{ | ||||
| 			ComponentDepot.Register<TComponent>(); | ||||
| 			Included.Add(typeof(TComponent)); | ||||
| 			return new FilterBuilder(ComponentDepot, Included, Excluded); | ||||
| 		} | ||||
| 
 | ||||
| 		public FilterBuilder Exclude<TComponent>() where TComponent : struct | ||||
| 		public FilterBuilder Exclude<TComponent>() where TComponent : unmanaged | ||||
| 		{ | ||||
| 			ComponentDepot.Register<TComponent>(); | ||||
| 			Excluded.Add(typeof(TComponent)); | ||||
|  |  | |||
|  | @ -7,8 +7,8 @@ namespace MoonTools.ECS | |||
| 	{ | ||||
| 		private const int HASH_FACTOR = 97; | ||||
| 
 | ||||
| 		public HashSet<Type> Included; | ||||
| 		public HashSet<Type> Excluded; | ||||
| 		public readonly HashSet<Type> Included; | ||||
| 		public readonly HashSet<Type> Excluded; | ||||
| 
 | ||||
| 		public FilterSignature(HashSet<Type> included, HashSet<Type> excluded) | ||||
| 		{ | ||||
|  |  | |||
|  | @ -1,39 +0,0 @@ | |||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
| 	internal class IDStorage | ||||
| 	{ | ||||
| 		private int nextID = 0; | ||||
| 
 | ||||
| 		private readonly Stack<int> availableIDs = new Stack<int>(); | ||||
| 		private readonly HashSet<int> availableIDHash = new HashSet<int>(); | ||||
| 
 | ||||
| 		public int NextID() | ||||
| 		{ | ||||
| 			if (availableIDs.Count > 0) | ||||
| 			{ | ||||
| 				var id = availableIDs.Pop(); | ||||
| 				availableIDHash.Remove(id); | ||||
| 				return id; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				var id = nextID; | ||||
| 				nextID += 1; | ||||
| 				return id; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		public bool Taken(int id) | ||||
| 		{ | ||||
| 			return !availableIDHash.Contains(id) && id < nextID; | ||||
| 		} | ||||
| 
 | ||||
| 		public void Release(int id) | ||||
| 		{ | ||||
| 			availableIDs.Push(id); | ||||
| 			availableIDHash.Add(id); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -1,10 +1,11 @@ | |||
| using System; | ||||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
| using System.Runtime.InteropServices; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
| 	internal class IndexableSet<T> : IEnumerable<T> where T : notnull | ||||
| 	internal class IndexableSet<T> : IEnumerable<T> where T : unmanaged | ||||
| 	{ | ||||
| 		private Dictionary<T, int> indices; | ||||
| 		private T[] array; | ||||
|  | @ -78,5 +79,32 @@ namespace MoonTools.ECS | |||
| 				yield return array[i]; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		public void Save(IndexableSetState<T> state) | ||||
| 		{ | ||||
| 			ReadOnlySpan<byte> arrayBytes = MemoryMarshal.Cast<T, byte>(array); | ||||
| 
 | ||||
| 			if (arrayBytes.Length > state.Array.Length) | ||||
| 			{ | ||||
| 				Array.Resize(ref state.Array, arrayBytes.Length); | ||||
| 			} | ||||
| 
 | ||||
| 			arrayBytes.CopyTo(state.Array); | ||||
| 
 | ||||
| 			state.Count = Count; | ||||
| 		} | ||||
| 
 | ||||
| 		public void Load(IndexableSetState<T> state) | ||||
| 		{ | ||||
| 			state.Array.CopyTo(MemoryMarshal.Cast<T, byte>(array)); | ||||
| 
 | ||||
| 			indices.Clear(); | ||||
| 			for (var i = 0; i < state.Count; i += 1) | ||||
| 			{ | ||||
| 				indices[array[i]] = i; | ||||
| 			} | ||||
| 
 | ||||
| 			Count = state.Count; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ namespace MoonTools.ECS | |||
| 	{ | ||||
| 		private Dictionary<Type, RelationStorage> storages = new Dictionary<Type, RelationStorage>(); | ||||
| 
 | ||||
| 		private void Register<TRelationKind>() where TRelationKind : struct | ||||
| 		private void Register<TRelationKind>() where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			if (!storages.ContainsKey(typeof(TRelationKind))) | ||||
| 			{ | ||||
|  | @ -15,18 +15,18 @@ namespace MoonTools.ECS | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		private RelationStorage<TRelationKind> Lookup<TRelationKind>() where TRelationKind : struct | ||||
| 		private RelationStorage<TRelationKind> Lookup<TRelationKind>() where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			Register<TRelationKind>(); | ||||
| 			return (RelationStorage<TRelationKind>) storages[typeof(TRelationKind)]; | ||||
| 		} | ||||
| 
 | ||||
| 		public void Add<TRelationKind>(Relation relation, TRelationKind relationData) where TRelationKind : struct | ||||
| 		public void Set<TRelationKind>(Relation relation, TRelationKind relationData) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			Lookup<TRelationKind>().Add(relation, relationData); | ||||
| 			Lookup<TRelationKind>().Set(relation, relationData); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Remove<TRelationKind>(Relation relation) where TRelationKind : struct | ||||
| 		public void Remove<TRelationKind>(Relation relation) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			Lookup<TRelationKind>().Remove(relation); | ||||
| 		} | ||||
|  | @ -40,24 +40,45 @@ namespace MoonTools.ECS | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		public IEnumerable<(Entity, Entity, TRelationKind)> Relations<TRelationKind>() where TRelationKind : struct | ||||
| 		public IEnumerable<(Entity, Entity, TRelationKind)> Relations<TRelationKind>() where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return Lookup<TRelationKind>().All(); | ||||
| 		} | ||||
| 
 | ||||
| 		public bool Related<TRelationKind>(int idA, int idB) where TRelationKind : struct | ||||
| 		public bool Related<TRelationKind>(int idA, int idB) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return Lookup<TRelationKind>().Has(new Relation(idA, idB)); | ||||
| 		} | ||||
| 
 | ||||
| 		public IEnumerable<(Entity, TRelationKind)> RelatedToA<TRelationKind>(int entityID) where TRelationKind : struct | ||||
| 		public IEnumerable<(Entity, TRelationKind)> RelatedToA<TRelationKind>(int entityID) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return Lookup<TRelationKind>().RelatedToA(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		public IEnumerable<(Entity, TRelationKind)> RelatedToB<TRelationKind>(int entityID) where TRelationKind : struct | ||||
| 		public IEnumerable<(Entity, TRelationKind)> RelatedToB<TRelationKind>(int entityID) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return Lookup<TRelationKind>().RelatedToB(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Save(RelationDepotState state) | ||||
| 		{ | ||||
| 			foreach (var (type, storage) in storages) | ||||
| 			{ | ||||
| 				if (!state.StorageStates.ContainsKey(type)) | ||||
| 				{ | ||||
| 					state.StorageStates.Add(type, storage.CreateState()); | ||||
| 				} | ||||
| 
 | ||||
| 				storage.Save(state.StorageStates[type]); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		public void Load(RelationDepotState state) | ||||
| 		{ | ||||
| 			foreach (var (type, storageState) in state.StorageStates) | ||||
| 			{ | ||||
| 				storages[type].Load(storageState); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -1,32 +1,46 @@ | |||
| using System.Collections.Generic; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Runtime.InteropServices; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
| 	internal abstract class RelationStorage | ||||
| 	{ | ||||
| 		public abstract RelationStorageState CreateState(); | ||||
| 		public abstract void Save(RelationStorageState state); | ||||
| 		public abstract void Load(RelationStorageState state); | ||||
| 		public abstract void OnEntityDestroy(int entityID); | ||||
| 	} | ||||
| 
 | ||||
| 	// Relation is the two entities, A related to B. | ||||
| 	// TRelation is the data attached to the relation. | ||||
| 	internal class RelationStorage<TRelation> : RelationStorage where TRelation : struct | ||||
| 	internal class RelationStorage<TRelation> : RelationStorage where TRelation : unmanaged | ||||
| 	{ | ||||
| 		private Dictionary<Relation, TRelation> relations = new Dictionary<Relation, TRelation>(16); | ||||
| 		private int count = 0; | ||||
| 		private Dictionary<Relation, int> indices = new Dictionary<Relation, int>(16); | ||||
| 		private Relation[] relations = new Relation[16]; | ||||
| 		private TRelation[] relationDatas = new TRelation[16]; | ||||
| 		private Dictionary<int, HashSet<int>> entitiesRelatedToA = new Dictionary<int, HashSet<int>>(16); | ||||
| 		private Dictionary<int, HashSet<int>> entitiesRelatedToB = new Dictionary<int, HashSet<int>>(16); | ||||
| 		private Stack<HashSet<int>> listPool = new Stack<HashSet<int>>(); | ||||
| 
 | ||||
| 		public IEnumerable<(Entity, Entity, TRelation)> All() | ||||
| 		{ | ||||
| 			foreach (var relationData in relations) | ||||
| 			for (var i = 0; i < count; i += 1) | ||||
| 			{ | ||||
| 				yield return (relationData.Key.A, relationData.Key.B, relationData.Value); | ||||
| 				var relation = relations[i]; | ||||
| 				yield return (relation.A, relation.B, relationDatas[i]); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		public void Add(Relation relation, TRelation relationData) | ||||
| 		public void Set(Relation relation, TRelation relationData) | ||||
| 		{ | ||||
| 			if (relations.ContainsKey(relation)) { return; } | ||||
| 			if (indices.ContainsKey(relation)) | ||||
| 			{ | ||||
| 				var index = indices[relation]; | ||||
| 				relationDatas[index] = relationData; | ||||
| 				return; | ||||
| 			} | ||||
| 
 | ||||
| 			var idA = relation.A.ID; | ||||
| 			var idB = relation.B.ID; | ||||
|  | @ -43,12 +57,20 @@ namespace MoonTools.ECS | |||
| 			} | ||||
| 			entitiesRelatedToB[idB].Add(idA); | ||||
| 
 | ||||
| 			relations.Add(relation, relationData); | ||||
| 			if (count >= relationDatas.Length) | ||||
| 			{ | ||||
| 				Array.Resize(ref relationDatas, relationDatas.Length * 2); | ||||
| 			} | ||||
| 
 | ||||
| 			relations[count] = relation; | ||||
| 			relationDatas[count] = relationData; | ||||
| 			indices.Add(relation, count); | ||||
| 			count += 1; | ||||
| 		} | ||||
| 
 | ||||
| 		public bool Has(Relation relation) | ||||
| 		{ | ||||
| 			return relations.ContainsKey(relation); | ||||
| 			return indices.ContainsKey(relation); | ||||
| 		} | ||||
| 
 | ||||
| 		// FIXME: is there a more descriptive name for these? | ||||
|  | @ -59,7 +81,7 @@ namespace MoonTools.ECS | |||
| 				foreach (var id in entitiesRelatedToA[entityID]) | ||||
| 				{ | ||||
| 					var relation = new Relation(entityID, id); | ||||
| 					yield return (relation.B, relations[relation]); | ||||
| 					yield return (relation.B, relationDatas[indices[relation]]); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | @ -71,7 +93,7 @@ namespace MoonTools.ECS | |||
| 				foreach (var id in entitiesRelatedToB[entityID]) | ||||
| 				{ | ||||
| 					var relation = new Relation(id, entityID); | ||||
| 					yield return (relation.A, relations[relation]); | ||||
| 					yield return (relation.A, relationDatas[indices[relation]]); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | @ -88,7 +110,26 @@ namespace MoonTools.ECS | |||
| 				entitiesRelatedToB[relation.B.ID].Remove(relation.A.ID); | ||||
| 			} | ||||
| 
 | ||||
| 			return relations.Remove(relation); | ||||
| 			if (indices.ContainsKey(relation)) | ||||
| 			{ | ||||
| 				var index = indices[relation]; | ||||
| 				var lastElementIndex = count - 1; | ||||
| 
 | ||||
| 				// move an element into the hole | ||||
| 				if (index != lastElementIndex) | ||||
| 				{ | ||||
| 					var lastRelation = relations[lastElementIndex]; | ||||
| 					indices[lastRelation] = index; | ||||
| 					relationDatas[index] = relationDatas[lastElementIndex]; | ||||
| 					relations[index] = lastRelation; | ||||
| 				} | ||||
| 
 | ||||
| 				count -= 1; | ||||
| 				indices.Remove(relation); | ||||
| 				return true; | ||||
| 			} | ||||
| 
 | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		public override void OnEntityDestroy(int entityID) | ||||
|  | @ -131,5 +172,60 @@ namespace MoonTools.ECS | |||
| 			hashSet.Clear(); | ||||
| 			listPool.Push(hashSet); | ||||
| 		} | ||||
| 
 | ||||
| 		public override RelationStorageState CreateState() | ||||
| 		{ | ||||
| 			return RelationStorageState.Create<TRelation>(count); | ||||
| 		} | ||||
| 
 | ||||
| 		public override void Save(RelationStorageState state) | ||||
| 		{ | ||||
| 			ReadOnlySpan<byte> relationBytes = MemoryMarshal.Cast<Relation, byte>(relations); | ||||
| 
 | ||||
| 			if (relationBytes.Length > state.Relations.Length) | ||||
| 			{ | ||||
| 				Array.Resize(ref state.Relations, relationBytes.Length); | ||||
| 			} | ||||
| 			relationBytes.CopyTo(state.Relations); | ||||
| 
 | ||||
| 			ReadOnlySpan<byte> relationDataBytes = MemoryMarshal.Cast<TRelation, byte>(relationDatas); | ||||
| 
 | ||||
| 			if (relationDataBytes.Length > state.RelationDatas.Length) | ||||
| 			{ | ||||
| 				Array.Resize(ref state.RelationDatas, relationDataBytes.Length); | ||||
| 			} | ||||
| 			relationDataBytes.CopyTo(state.RelationDatas); | ||||
| 
 | ||||
| 			state.Count = count; | ||||
| 		} | ||||
| 
 | ||||
| 		public override void Load(RelationStorageState state) | ||||
| 		{ | ||||
| 			state.Relations.CopyTo(MemoryMarshal.Cast<Relation, byte>(relations)); | ||||
| 			state.RelationDatas.CopyTo(MemoryMarshal.Cast<TRelation, byte>(relationDatas)); | ||||
| 
 | ||||
| 			indices.Clear(); | ||||
| 			entitiesRelatedToA.Clear(); | ||||
| 			entitiesRelatedToB.Clear(); | ||||
| 			for (var i = 0; i < state.Count; i += 1) | ||||
| 			{ | ||||
| 				var relation = relations[i]; | ||||
| 				indices[relation] = i; | ||||
| 
 | ||||
| 				if (!entitiesRelatedToA.ContainsKey(relation.A.ID)) | ||||
| 				{ | ||||
| 					entitiesRelatedToA[relation.A.ID] = AcquireHashSetFromPool(); | ||||
| 				} | ||||
| 				entitiesRelatedToA[relation.A.ID].Add(relation.B.ID); | ||||
| 
 | ||||
| 				if (!entitiesRelatedToB.ContainsKey(relation.B.ID)) | ||||
| 				{ | ||||
| 					entitiesRelatedToB[relation.B.ID] = AcquireHashSetFromPool(); | ||||
| 				} | ||||
| 				entitiesRelatedToB[relation.B.ID].Add(relation.A.ID); | ||||
| 			} | ||||
| 
 | ||||
| 			count = state.Count; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -2,9 +2,6 @@ | |||
| { | ||||
| 	public abstract class Renderer : EntityComponentReader | ||||
| 	{ | ||||
| 		public Renderer(World world) | ||||
| 		{ | ||||
| 			world.AddRenderer(this); | ||||
| 		} | ||||
| 		public Renderer(World world) : base(world) { } | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,11 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
| 	internal class ComponentDepotState | ||||
| 	{ | ||||
| 		public Dictionary<Type, ComponentStorageState> StorageStates = new Dictionary<Type, ComponentStorageState>(); | ||||
| 		public Dictionary<FilterSignature, IndexableSetState<int>> FilterStates = new Dictionary<FilterSignature, IndexableSetState<int>>(); | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,27 @@ | |||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
|     internal class ComponentStorageState | ||||
|     { | ||||
| 		public int Count; | ||||
| 		public byte[] EntityIDs; | ||||
| 		public byte[] Components; | ||||
| 
 | ||||
|         public unsafe static ComponentStorageState Create<TComponent>(int count) where TComponent : unmanaged | ||||
|         { | ||||
| 			return new ComponentStorageState( | ||||
|                 count, | ||||
|                 count * sizeof(int), | ||||
|                 count * sizeof(TComponent) | ||||
|             ); | ||||
| 		} | ||||
| 
 | ||||
|         private ComponentStorageState(int count, int entityIDSize, int componentSize) | ||||
|         { | ||||
| 			Count = count; | ||||
| 			EntityIDs = new byte[entityIDSize]; | ||||
| 			Components = new byte[componentSize]; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,10 @@ | |||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
|     internal class EntityStorageState | ||||
|     { | ||||
| 		public int NextID; | ||||
| 		public List<int> availableIDs = new List<int>(); | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,16 @@ | |||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
|     internal class IndexableSetState<T> where T : unmanaged | ||||
|     { | ||||
| 		public int Count; | ||||
| 		public byte[] Array; | ||||
| 
 | ||||
|         public unsafe IndexableSetState(int count) | ||||
|         { | ||||
| 			Count = count; | ||||
| 			Array = new byte[sizeof(T) * count]; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,10 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
|     internal class RelationDepotState | ||||
|     { | ||||
| 		public Dictionary<Type, RelationStorageState> StorageStates = new Dictionary<Type, RelationStorageState>(); | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,27 @@ | |||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
| 	internal class RelationStorageState | ||||
| 	{ | ||||
| 		public int Count; | ||||
| 		public byte[] Relations; | ||||
| 		public byte[] RelationDatas; | ||||
| 
 | ||||
| 		public unsafe static RelationStorageState Create<TRelation>(int count) where TRelation : unmanaged | ||||
| 		{ | ||||
| 			return new RelationStorageState( | ||||
| 				count, | ||||
| 				count * sizeof(Relation), | ||||
| 				count * sizeof(TRelation) | ||||
| 			); | ||||
| 		} | ||||
| 
 | ||||
| 		private RelationStorageState(int count, int relationSize, int relationDataSize) | ||||
| 		{ | ||||
| 			Count = count; | ||||
| 			Relations = new byte[relationSize]; | ||||
| 			RelationDatas = new byte[relationDataSize]; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,16 @@ | |||
| namespace MoonTools.ECS | ||||
| { | ||||
|     public class WorldState | ||||
|     { | ||||
| 		internal readonly ComponentDepotState ComponentDepotState; | ||||
| 		internal readonly EntityStorageState EntityStorageState; | ||||
| 		internal readonly RelationDepotState RelationDepotState; | ||||
| 
 | ||||
| 		public WorldState() | ||||
|         { | ||||
| 			ComponentDepotState = new ComponentDepotState(); | ||||
| 			EntityStorageState = new EntityStorageState(); | ||||
| 			RelationDepotState = new RelationDepotState(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -5,17 +5,9 @@ namespace MoonTools.ECS | |||
| { | ||||
| 	public abstract class System : EntityComponentReader | ||||
| 	{ | ||||
| 		internal MessageDepot MessageDepot; | ||||
| 		internal MessageDepot MessageDepot => World.MessageDepot; | ||||
| 
 | ||||
| 		internal void RegisterMessageDepot(MessageDepot messageDepot) | ||||
| 		{ | ||||
| 			MessageDepot = messageDepot; | ||||
| 		} | ||||
| 
 | ||||
| 		public System(World world) | ||||
| 		{ | ||||
| 			world.AddSystem(this); | ||||
| 		} | ||||
| 		public System(World world) : base(world) { } | ||||
| 
 | ||||
| 		public abstract void Update(TimeSpan delta); | ||||
| 
 | ||||
|  | @ -24,7 +16,7 @@ namespace MoonTools.ECS | |||
| 			return EntityStorage.Create(); | ||||
| 		} | ||||
| 
 | ||||
| 		protected void Set<TComponent>(in Entity entity, in TComponent component) where TComponent : struct | ||||
| 		protected void Set<TComponent>(in Entity entity, in TComponent component) where TComponent : unmanaged | ||||
| 		{ | ||||
| #if DEBUG | ||||
| 			// check for use after destroy | ||||
|  | @ -36,52 +28,52 @@ namespace MoonTools.ECS | |||
| 			ComponentDepot.Set<TComponent>(entity.ID, component); | ||||
| 		} | ||||
| 
 | ||||
| 		protected void Remove<TComponent>(in Entity entity) where TComponent : struct | ||||
| 		protected void Remove<TComponent>(in Entity entity) where TComponent : unmanaged | ||||
| 		{ | ||||
| 			ComponentDepot.Remove<TComponent>(entity.ID); | ||||
| 		} | ||||
| 
 | ||||
| 		protected ReadOnlySpan<TMessage> ReadMessages<TMessage>() where TMessage : struct | ||||
| 		protected ReadOnlySpan<TMessage> ReadMessages<TMessage>() where TMessage : unmanaged | ||||
| 		{ | ||||
| 			return MessageDepot.All<TMessage>(); | ||||
| 		} | ||||
| 
 | ||||
| 		protected TMessage ReadMessage<TMessage>() where TMessage : struct | ||||
| 		protected TMessage ReadMessage<TMessage>() where TMessage : unmanaged | ||||
| 		{ | ||||
| 			return MessageDepot.First<TMessage>(); | ||||
| 		} | ||||
| 
 | ||||
| 		protected bool SomeMessage<TMessage>() where TMessage : struct | ||||
| 		protected bool SomeMessage<TMessage>() where TMessage : unmanaged | ||||
| 		{ | ||||
| 			return MessageDepot.Some<TMessage>(); | ||||
| 		} | ||||
| 
 | ||||
| 		protected IEnumerable<TMessage> ReadMessagesWithEntity<TMessage>(in Entity entity) where TMessage : struct, IHasEntity | ||||
| 		protected IEnumerable<TMessage> ReadMessagesWithEntity<TMessage>(in Entity entity) where TMessage : unmanaged, IHasEntity | ||||
| 		{ | ||||
| 			return MessageDepot.WithEntity<TMessage>(entity.ID); | ||||
| 		} | ||||
| 
 | ||||
| 		protected ref readonly TMessage ReadMessageWithEntity<TMessage>(in Entity entity) where TMessage : struct, IHasEntity | ||||
| 		protected ref readonly TMessage ReadMessageWithEntity<TMessage>(in Entity entity) where TMessage : unmanaged, IHasEntity | ||||
| 		{ | ||||
| 			return ref MessageDepot.FirstWithEntity<TMessage>(entity.ID); | ||||
| 		} | ||||
| 
 | ||||
| 		protected bool SomeMessageWithEntity<TMessage>(in Entity entity) where TMessage : struct, IHasEntity | ||||
| 		protected bool SomeMessageWithEntity<TMessage>(in Entity entity) where TMessage : unmanaged, IHasEntity | ||||
| 		{ | ||||
| 			return MessageDepot.SomeWithEntity<TMessage>(entity.ID); | ||||
| 		} | ||||
| 
 | ||||
| 		protected void Send<TMessage>(in TMessage message) where TMessage : struct | ||||
| 		protected void Send<TMessage>(in TMessage message) where TMessage : unmanaged | ||||
| 		{ | ||||
| 			MessageDepot.Add(message); | ||||
| 		} | ||||
| 
 | ||||
| 		protected void Relate<TRelationKind>(in Entity entityA, in Entity entityB, TRelationKind relationData) where TRelationKind : struct | ||||
| 		protected void Relate<TRelationKind>(in Entity entityA, in Entity entityB, TRelationKind relationData) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			RelationDepot.Add<TRelationKind>(new Relation(entityA, entityB), relationData); | ||||
| 			RelationDepot.Set<TRelationKind>(new Relation(entityA, entityB), relationData); | ||||
| 		} | ||||
| 
 | ||||
| 		protected void Unrelate<TRelationKind>(in Entity entityA, in Entity entityB) where TRelationKind : struct | ||||
| 		protected void Unrelate<TRelationKind>(in Entity entityA, in Entity entityB) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			RelationDepot.Remove<TRelationKind>(new Relation(entityA, entityB)); | ||||
| 		} | ||||
|  |  | |||
							
								
								
									
										51
									
								
								src/World.cs
								
								
								
								
							
							
						
						
									
										51
									
								
								src/World.cs
								
								
								
								
							|  | @ -2,37 +2,22 @@ | |||
| { | ||||
| 	public class World | ||||
| 	{ | ||||
| 		private readonly EntityStorage EntityStorage = new EntityStorage(); | ||||
| 		private readonly ComponentDepot ComponentDepot = new ComponentDepot(); | ||||
| 		private readonly MessageDepot MessageDepot = new MessageDepot(); | ||||
| 		private readonly RelationDepot RelationDepot = new RelationDepot(); | ||||
| 
 | ||||
| 		internal void AddSystem(System system) | ||||
| 		{ | ||||
| 			system.RegisterEntityStorage(EntityStorage); | ||||
| 			system.RegisterComponentDepot(ComponentDepot); | ||||
| 			system.RegisterMessageDepot(MessageDepot); | ||||
| 			system.RegisterRelationDepot(RelationDepot); | ||||
| 		} | ||||
| 
 | ||||
| 		internal void AddRenderer(Renderer renderer) | ||||
| 		{ | ||||
| 			renderer.RegisterEntityStorage(EntityStorage); | ||||
| 			renderer.RegisterComponentDepot(ComponentDepot); | ||||
| 			renderer.RegisterRelationDepot(RelationDepot); | ||||
| 		} | ||||
| 		internal readonly EntityStorage EntityStorage = new EntityStorage(); | ||||
| 		internal readonly ComponentDepot ComponentDepot = new ComponentDepot(); | ||||
| 		internal readonly MessageDepot MessageDepot = new MessageDepot(); | ||||
| 		internal readonly RelationDepot RelationDepot = new RelationDepot(); | ||||
| 
 | ||||
| 		public Entity CreateEntity() | ||||
| 		{ | ||||
| 			return EntityStorage.Create(); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Set<TComponent>(Entity entity, in TComponent component) where TComponent : struct | ||||
| 		public void Set<TComponent>(Entity entity, in TComponent component) where TComponent : unmanaged | ||||
| 		{ | ||||
| 			ComponentDepot.Set(entity.ID, component); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Send<TMessage>(in TMessage message) where TMessage : struct | ||||
| 		public void Send<TMessage>(in TMessage message) where TMessage : unmanaged | ||||
| 		{ | ||||
| 			MessageDepot.Add(message); | ||||
| 		} | ||||
|  | @ -41,5 +26,29 @@ | |||
| 		{ | ||||
| 			MessageDepot.Clear(); | ||||
| 		} | ||||
| 
 | ||||
| 		public void DisableSerialization<TComponent>() where TComponent : unmanaged | ||||
| 		{ | ||||
| 			ComponentDepot.DisableSerialization<TComponent>(); | ||||
| 		} | ||||
| 
 | ||||
| 		public WorldState CreateState() | ||||
| 		{ | ||||
| 			return new WorldState(); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Save(WorldState state) | ||||
| 		{ | ||||
| 			ComponentDepot.Save(state.ComponentDepotState); | ||||
| 			EntityStorage.Save(state.EntityStorageState); | ||||
| 			RelationDepot.Save(state.RelationDepotState); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Load(WorldState state) | ||||
| 		{ | ||||
| 			ComponentDepot.Load(state.ComponentDepotState); | ||||
| 			EntityStorage.Load(state.EntityStorageState); | ||||
| 			RelationDepot.Load(state.RelationDepotState); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue