snapshot refinements
							parent
							
								
									8061590195
								
							
						
					
					
						commit
						f628735025
					
				|  | @ -47,12 +47,6 @@ namespace MoonTools.ECS | |||
| 			return ref Lookup<TComponent>().Get(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		// used for debugging and template instantiation | ||||
| 		internal object UntypedGet(int entityID, int componentTypeIndex) | ||||
| 		{ | ||||
| 			return storages[componentTypeIndex].UntypedGet(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		public ref readonly TComponent GetFirst<TComponent>() where TComponent : unmanaged | ||||
| 		{ | ||||
| 			return ref Lookup<TComponent>().GetFirst(); | ||||
|  | @ -63,10 +57,7 @@ namespace MoonTools.ECS | |||
| 			Lookup<TComponent>().Set(entityID, component); | ||||
| 		} | ||||
| 
 | ||||
| 		internal void Set(int entityID, int componentTypeIndex, object component) | ||||
| 		{ | ||||
| 			storages[componentTypeIndex].Set(entityID, component); | ||||
| 		} | ||||
| 
 | ||||
| 
 | ||||
| 		public Entity GetSingletonEntity<TComponent>() where TComponent : unmanaged | ||||
| 		{ | ||||
|  | @ -99,8 +90,21 @@ namespace MoonTools.ECS | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// used to fill snapshot depot with correct storages | ||||
| 		public void FillMissingStorages(ComponentDepot other) | ||||
| 		// these methods used to implement snapshots, templates, and debugging | ||||
| 
 | ||||
| 		// FIXME: use unsafe pointers instead of object | ||||
| 		internal object UntypedGet(int entityID, int componentTypeIndex) | ||||
| 		{ | ||||
| 			return storages[componentTypeIndex].UntypedGet(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		// FIXME: use unsafe pointers instead of object | ||||
| 		internal void Set(int entityID, int componentTypeIndex, object component) | ||||
| 		{ | ||||
| 			storages[componentTypeIndex].Set(entityID, component); | ||||
| 		} | ||||
| 
 | ||||
| 		public void CreateMissingStorages(ComponentDepot other) | ||||
| 		{ | ||||
| 			for (var i = 0; i < ComponentTypeIndices.Count; i += 1) | ||||
| 			{ | ||||
|  |  | |||
|  | @ -52,7 +52,7 @@ namespace MoonTools.ECS | |||
| 			return EntityToComponentTypeIndices[entityID].Remove(componentTypeIndex); | ||||
| 		} | ||||
| 
 | ||||
| 		public void AddRelation(int entityID, int relationIndex) | ||||
| 		public void AddRelationKind(int entityID, int relationIndex) | ||||
| 		{ | ||||
| 			EntityToRelationTypeIndices[entityID].Add(relationIndex); | ||||
| 		} | ||||
|  |  | |||
|  | @ -24,11 +24,6 @@ namespace MoonTools.ECS | |||
| 			return Included.SetEquals(other.Included) && Excluded.SetEquals(other.Excluded); | ||||
| 		} | ||||
| 
 | ||||
| 		private int GuidToInt(Guid guid) | ||||
| 		{ | ||||
| 			return BitConverter.ToInt32(guid.ToByteArray()); | ||||
| 		} | ||||
| 
 | ||||
| 		public override int GetHashCode() | ||||
| 		{ | ||||
| 			var hashcode = 1; | ||||
|  |  | |||
|  | @ -94,6 +94,27 @@ namespace MoonTools.ECS | |||
| 			Check(entityID, ComponentTypeIndices.GetIndex<TComponent>()); | ||||
| 		} | ||||
| 
 | ||||
| 		public bool CheckSatisfied(int entityID, FilterSignature filterSignature) | ||||
| 		{ | ||||
| 			foreach (var type in filterSignature.Included) | ||||
| 			{ | ||||
| 				if (!EntityStorage.HasComponent(entityID, type)) | ||||
| 				{ | ||||
| 					return false; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			foreach (var type in filterSignature.Excluded) | ||||
| 			{ | ||||
| 				if (EntityStorage.HasComponent(entityID, type)) | ||||
| 				{ | ||||
| 					return false; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			return true; | ||||
| 		} | ||||
| 
 | ||||
| 		private void CheckFilter(int entityID, FilterSignature filterSignature) | ||||
| 		{ | ||||
| 			foreach (var type in filterSignature.Included) | ||||
|  |  | |||
|  | @ -51,11 +51,6 @@ namespace MoonTools.ECS | |||
| 			Lookup<TRelationKind>().UnrelateAll(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		public void UnrelateAll(int entityID, int relationStorageIndex) | ||||
| 		{ | ||||
| 			storages[relationStorageIndex].UnrelateAll(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		public IEnumerable<(Entity, Entity, TRelationKind)> Relations<TRelationKind>() where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return Lookup<TRelationKind>().All(); | ||||
|  | @ -105,5 +100,49 @@ namespace MoonTools.ECS | |||
| 		{ | ||||
| 			return Lookup<TRelationKind>().InRelationCount(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		// untyped methods used for destroying and snapshots | ||||
| 
 | ||||
| 		public void Set(int entityA, int entityB, int relationTypeIndex, object relationData) | ||||
| 		{ | ||||
| 			storages[relationTypeIndex].Set(entityA, entityB, relationData); | ||||
| 		} | ||||
| 
 | ||||
| 		public void UnrelateAll(int entityID, int relationTypeIndex) | ||||
| 		{ | ||||
| 			storages[relationTypeIndex].UnrelateAll(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		public IEnumerable<(int, object)> InRelations(int entityID, int relationTypeIndex) | ||||
| 		{ | ||||
| 			return storages[relationTypeIndex].UntypedInRelations(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		public IEnumerable<(int, object)> OutRelations(int entityID, int relationTypeIndex) | ||||
| 		{ | ||||
| 			return storages[relationTypeIndex].UntypedOutRelations(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Clear() | ||||
| 		{ | ||||
| 			for (var i = 0; i < RelationTypeIndices.Count; i += 1) | ||||
| 			{ | ||||
| 				if (storages[i] != null) | ||||
| 				{ | ||||
| 					storages[i].Clear(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		public void CreateMissingStorages(RelationDepot other) | ||||
| 		{ | ||||
| 			for (var i = 0; i < RelationTypeIndices.Count; i += 1) | ||||
| 			{ | ||||
| 				if (storages[i] == null && other.storages[i] != null) | ||||
| 				{ | ||||
| 					storages[i] = other.storages[i].CreateStorage(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -1,12 +1,17 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Runtime.InteropServices; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
| 	internal abstract class RelationStorage | ||||
| 	{ | ||||
| 		public abstract void Set(int entityA, int entityB, object relationData); | ||||
| 		public abstract void UnrelateAll(int entityID); | ||||
| 		public abstract IEnumerable<(int, object)> UntypedInRelations(int entityID); | ||||
| 		public abstract IEnumerable<(int, object)> UntypedOutRelations(int entityID); | ||||
| 		// used to create correctly typed storage on snapshot | ||||
| 		public abstract RelationStorage CreateStorage(); | ||||
| 		public abstract void Clear(); | ||||
| 	} | ||||
| 
 | ||||
| 	// Relation is the two entities, A related to B. | ||||
|  | @ -185,6 +190,29 @@ namespace MoonTools.ECS | |||
| 			return (aEmpty, bEmpty); | ||||
| 		} | ||||
| 
 | ||||
| 		private IndexableSet<int> AcquireHashSetFromPool() | ||||
| 		{ | ||||
| 			if (listPool.Count == 0) | ||||
| 			{ | ||||
| 				listPool.Push(new IndexableSet<int>()); | ||||
| 			} | ||||
| 
 | ||||
| 			return listPool.Pop(); | ||||
| 		} | ||||
| 
 | ||||
| 		private void ReturnHashSetToPool(IndexableSet<int> hashSet) | ||||
| 		{ | ||||
| 			hashSet.Clear(); | ||||
| 			listPool.Push(hashSet); | ||||
| 		} | ||||
| 
 | ||||
| 		// untyped methods used for internal implementation | ||||
| 
 | ||||
| 		public override void Set(int entityA, int entityB, object relationData) | ||||
| 		{ | ||||
| 			Set(new Relation(entityA, entityB), (TRelation) relationData); | ||||
| 		} | ||||
| 
 | ||||
| 		public override void UnrelateAll(int entityID) | ||||
| 		{ | ||||
| 			if (outRelations.ContainsKey(entityID)) | ||||
|  | @ -210,20 +238,43 @@ namespace MoonTools.ECS | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		private IndexableSet<int> AcquireHashSetFromPool() | ||||
| 		public override IEnumerable<(int, object)> UntypedInRelations(int entityID) | ||||
| 		{ | ||||
| 			if (listPool.Count == 0) | ||||
| 			foreach (var (entity, relationData) in InRelations(entityID)) | ||||
| 			{ | ||||
| 				listPool.Push(new IndexableSet<int>()); | ||||
| 				yield return (entity.ID, relationData); | ||||
| 			} | ||||
| 
 | ||||
| 			return listPool.Pop(); | ||||
| 		} | ||||
| 
 | ||||
| 		private void ReturnHashSetToPool(IndexableSet<int> hashSet) | ||||
| 		public override IEnumerable<(int, object)> UntypedOutRelations(int entityID) | ||||
| 		{ | ||||
| 			hashSet.Clear(); | ||||
| 			listPool.Push(hashSet); | ||||
| 			foreach (var (entity, relationData) in OutRelations(entityID)) | ||||
| 			{ | ||||
| 				yield return (entity.ID, relationData); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		public override RelationStorage<TRelation> CreateStorage() | ||||
| 		{ | ||||
| 			return new RelationStorage<TRelation>(); | ||||
| 		} | ||||
| 
 | ||||
| 		public override void Clear() | ||||
| 		{ | ||||
| 			count = 0; | ||||
| 			indices.Clear(); | ||||
| 
 | ||||
| 			foreach (var set in inRelations.Values) | ||||
| 			{ | ||||
| 				ReturnHashSetToPool(set); | ||||
| 			} | ||||
| 			inRelations.Clear(); | ||||
| 
 | ||||
| 			foreach (var set in outRelations.Values) | ||||
| 			{ | ||||
| 				ReturnHashSetToPool(set); | ||||
| 			} | ||||
| 			outRelations.Clear(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -1,3 +1,6 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
| 	public class Snapshot | ||||
|  | @ -7,29 +10,71 @@ namespace MoonTools.ECS | |||
| 
 | ||||
| 		private EntityStorage SnapshotEntityStorage; | ||||
| 		private ComponentDepot SnapshotComponentDepot; | ||||
| 		private RelationDepot SnapshotRelationDepot; | ||||
| 
 | ||||
| 		private List<int> SnapshotToWorldID = new List<int>(); | ||||
| 		private Dictionary<int, int> WorldToSnapshotID = new Dictionary<int, int>(); | ||||
| 
 | ||||
| 		internal Snapshot(World world) | ||||
| 		{ | ||||
| 			World = world; | ||||
| 			SnapshotEntityStorage = new EntityStorage(); | ||||
| 			SnapshotComponentDepot = new ComponentDepot(World.ComponentTypeIndices); | ||||
| 			SnapshotRelationDepot = new RelationDepot(World.RelationTypeIndices); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Take(Filter filter) | ||||
| 		{ | ||||
| 			Clear(); | ||||
| 			Filter = filter; | ||||
| 			SnapshotComponentDepot.FillMissingStorages(World.ComponentDepot); | ||||
| 			SnapshotComponentDepot.CreateMissingStorages(World.ComponentDepot); | ||||
| 			SnapshotRelationDepot.CreateMissingStorages(World.RelationDepot); | ||||
| 
 | ||||
| 			foreach (var worldEntity in filter.Entities) | ||||
| 			{ | ||||
| 				var snapshotEntity = SnapshotEntityStorage.Create(); | ||||
| 				WorldToSnapshotID.Add(worldEntity.ID, snapshotEntity.ID); | ||||
| 
 | ||||
| 				foreach (var componentTypeIndex in World.EntityStorage.ComponentTypeIndices(worldEntity.ID)) | ||||
| 				{ | ||||
| 					SnapshotEntityStorage.SetComponent(snapshotEntity.ID, componentTypeIndex); | ||||
| 					SnapshotComponentDepot.Set(snapshotEntity.ID, componentTypeIndex, World.ComponentDepot.UntypedGet(worldEntity.ID, componentTypeIndex)); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			foreach (var worldEntity in filter.Entities) | ||||
| 			{ | ||||
| 				var snapshotEntityID = WorldToSnapshotID[worldEntity.ID]; | ||||
| 
 | ||||
| 				foreach (var relationTypeIndex in World.EntityStorage.RelationTypeIndices(worldEntity.ID)) | ||||
| 				{ | ||||
| 					SnapshotEntityStorage.AddRelationKind(snapshotEntityID, relationTypeIndex); | ||||
| 
 | ||||
| 					foreach (var (otherEntityID, relationData) in World.RelationDepot.InRelations(worldEntity.ID, relationTypeIndex)) | ||||
| 					{ | ||||
| #if DEBUG | ||||
| 						if (!World.FilterStorage.CheckSatisfied(otherEntityID, Filter.Signature)) | ||||
| 						{ | ||||
| 							throw new InvalidOperationException($"Snapshot entity {worldEntity.ID} is related to non-snapshot entity {otherEntityID}!"); | ||||
| 						} | ||||
| #endif | ||||
| 						var otherSnapshotID = WorldToSnapshotID[otherEntityID]; | ||||
| 						SnapshotRelationDepot.Set(otherSnapshotID, snapshotEntityID, relationTypeIndex, relationData); | ||||
| 					} | ||||
| 
 | ||||
| 					foreach (var (otherEntityID, relationData) in World.RelationDepot.OutRelations(worldEntity.ID, relationTypeIndex)) | ||||
| 					{ | ||||
| #if DEBUG | ||||
| 						if (!World.FilterStorage.CheckSatisfied(otherEntityID, Filter.Signature)) | ||||
| 						{ | ||||
| 							throw new InvalidOperationException($"Snapshot entity {worldEntity.ID} is related to non-snapshot entity {otherEntityID}!"); | ||||
| 						} | ||||
| #endif | ||||
| 						var otherSnapshotID = WorldToSnapshotID[otherEntityID]; | ||||
| 						SnapshotRelationDepot.Set(snapshotEntityID, otherSnapshotID, relationTypeIndex, relationData); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		public void Restore() | ||||
|  | @ -47,6 +92,7 @@ namespace MoonTools.ECS | |||
| 			for (var i = 0; i < SnapshotEntityStorage.Count; i += 1) | ||||
| 			{ | ||||
| 				var entity = World.CreateEntity(); | ||||
| 				SnapshotToWorldID.Add(entity.ID); | ||||
| 
 | ||||
| 				foreach (var componentTypeIndex in SnapshotEntityStorage.ComponentTypeIndices(i)) | ||||
| 				{ | ||||
|  | @ -55,12 +101,37 @@ namespace MoonTools.ECS | |||
| 					World.ComponentDepot.Set(entity.ID, componentTypeIndex, SnapshotComponentDepot.UntypedGet(i, componentTypeIndex)); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			for (var i = 0; i < SnapshotEntityStorage.Count; i += 1) | ||||
| 			{ | ||||
| 				var worldID = SnapshotToWorldID[i]; | ||||
| 
 | ||||
| 				foreach (var relationTypeIndex in SnapshotEntityStorage.RelationTypeIndices(i)) | ||||
| 				{ | ||||
| 					World.EntityStorage.AddRelationKind(worldID, relationTypeIndex); | ||||
| 
 | ||||
| 					foreach (var (otherEntityID, relationData) in SnapshotRelationDepot.InRelations(i, relationTypeIndex)) | ||||
| 					{ | ||||
| 						var otherEntityWorldID = SnapshotToWorldID[otherEntityID]; | ||||
| 						World.RelationDepot.Set(otherEntityWorldID, worldID, relationTypeIndex, relationData); | ||||
| 					} | ||||
| 
 | ||||
| 					foreach (var (otherEntityID, relationData) in SnapshotRelationDepot.OutRelations(i, relationTypeIndex)) | ||||
| 					{ | ||||
| 						var otherEntityWorldID = SnapshotToWorldID[otherEntityID]; | ||||
| 						World.RelationDepot.Set(worldID, otherEntityWorldID, relationTypeIndex, relationData); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		private void Clear() | ||||
| 		{ | ||||
| 			SnapshotEntityStorage.Clear(); | ||||
| 			SnapshotComponentDepot.Clear(); | ||||
| 			SnapshotRelationDepot.Clear(); | ||||
| 			SnapshotToWorldID.Clear(); | ||||
| 			WorldToSnapshotID.Clear(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -67,8 +67,8 @@ namespace MoonTools.ECS | |||
| 		{ | ||||
| 			RelationDepot.Set<TRelationKind>(new Relation(entityA, entityB), relationData); | ||||
| 			var relationTypeIndex = RelationTypeIndices.GetIndex<TRelationKind>(); | ||||
| 			EntityStorage.AddRelation(entityA.ID, relationTypeIndex); | ||||
| 			EntityStorage.AddRelation(entityB.ID, relationTypeIndex); | ||||
| 			EntityStorage.AddRelationKind(entityA.ID, relationTypeIndex); | ||||
| 			EntityStorage.AddRelationKind(entityB.ID, relationTypeIndex); | ||||
| 		} | ||||
| 
 | ||||
| 		protected void Unrelate<TRelationKind>(in Entity entityA, in Entity entityB) where TRelationKind : unmanaged | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue