start implementing world transfer
							parent
							
								
									f7d4fcdee7
								
							
						
					
					
						commit
						227e3421cd
					
				|  | @ -103,7 +103,17 @@ namespace MoonTools.ECS | |||
| 
 | ||||
| 		public void CreateMissingStorages(ComponentDepot other) | ||||
| 		{ | ||||
| 			for (var i = 0; i < ComponentTypeIndices.Count; i += 1) | ||||
| 			while (other.ComponentTypeIndices.Count >= storages.Length) | ||||
| 			{ | ||||
| 				Array.Resize(ref storages, storages.Length * 2); | ||||
| 			} | ||||
| 
 | ||||
| 			while (other.ComponentTypeIndices.Count >= other.storages.Length) | ||||
| 			{ | ||||
| 				Array.Resize(ref other.storages, other.storages.Length * 2); | ||||
| 			} | ||||
| 
 | ||||
| 			for (var i = 0; i < other.ComponentTypeIndices.Count; i += 1) | ||||
| 			{ | ||||
| 				if (storages[i] == null && other.storages[i] != null) | ||||
| 				{ | ||||
|  |  | |||
|  | @ -20,6 +20,11 @@ namespace MoonTools.ECS | |||
| 			World = world; | ||||
| 		} | ||||
| 
 | ||||
| 		protected string GetTag(in Entity entity) | ||||
| 		{ | ||||
| 			return World.GetTag(entity); | ||||
| 		} | ||||
| 
 | ||||
| 		protected ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : unmanaged | ||||
| 		{ | ||||
| 			return ComponentDepot.ReadComponents<TComponent>(); | ||||
|  |  | |||
|  | @ -15,17 +15,24 @@ namespace MoonTools.ECS | |||
| 
 | ||||
| 		public int Count => nextID - availableIDs.Count; | ||||
| 
 | ||||
| 		public Entity Create() | ||||
| 		public Dictionary<int, string> Tags = new Dictionary<int, string>(); | ||||
| 
 | ||||
| 		public Entity Create(string tag) | ||||
| 		{ | ||||
| 			var entity = new Entity(NextID()); | ||||
| 
 | ||||
| 			if (!EntityToComponentTypeIndices.ContainsKey(entity.ID)) | ||||
| 			{ | ||||
| 				EntityToComponentTypeIndices.Add(entity.ID, new HashSet<int>()); | ||||
| 			} | ||||
| 
 | ||||
| 			if (!EntityToRelationTypeIndices.ContainsKey(entity.ID)) | ||||
| 			{ | ||||
| 				EntityToRelationTypeIndices.Add(entity.ID, new HashSet<int>()); | ||||
| 			} | ||||
| 
 | ||||
| 			Tags[entity.ID] = tag; | ||||
| 
 | ||||
| 			return entity; | ||||
| 		} | ||||
| 
 | ||||
|  | @ -34,10 +41,16 @@ namespace MoonTools.ECS | |||
| 			return Taken(entity.ID); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Tag(in Entity entity, string tag) | ||||
| 		{ | ||||
| 			Tags[entity.ID] = tag; | ||||
| 		} | ||||
| 
 | ||||
| 		public void Destroy(in Entity entity) | ||||
| 		{ | ||||
| 			EntityToComponentTypeIndices[entity.ID].Clear(); | ||||
| 			EntityToRelationTypeIndices[entity.ID].Clear(); | ||||
| 			Tags.Remove(entity.ID); | ||||
| 			Release(entity.ID); | ||||
| 		} | ||||
| 
 | ||||
|  | @ -68,6 +81,11 @@ namespace MoonTools.ECS | |||
| 			EntityToRelationTypeIndices[entityId].Remove(relationIndex); | ||||
| 		} | ||||
| 
 | ||||
| 		public string Tag(int entityID) | ||||
| 		{ | ||||
| 			return Tags[entityID]; | ||||
| 		} | ||||
| 
 | ||||
| 		public HashSet<int> ComponentTypeIndices(int entityID) | ||||
| 		{ | ||||
| 			return EntityToComponentTypeIndices[entityID]; | ||||
|  |  | |||
|  | @ -6,7 +6,8 @@ namespace MoonTools.ECS | |||
| 		{ | ||||
| 		} | ||||
| 
 | ||||
| 		protected Entity CreateEntity() => World.CreateEntity(); | ||||
| 		protected Entity CreateEntity(string tag = "") => World.CreateEntity(tag); | ||||
| 		protected void Tag(Entity entity, string tag) => World.Tag(entity, tag); | ||||
| 		protected void Set<TComponent>(in Entity entity, in TComponent component) where TComponent : unmanaged => World.Set<TComponent>(entity, component); | ||||
| 		protected void Remove<TComponent>(in Entity entity) where TComponent : unmanaged => World.Remove<TComponent>(entity); | ||||
| 		protected void Relate<TRelationKind>(in Entity entityA, in Entity entityB, TRelationKind relationData) where TRelationKind : unmanaged => World.Relate(entityA, entityB, relationData); | ||||
|  |  | |||
|  | @ -156,7 +156,17 @@ namespace MoonTools.ECS | |||
| 
 | ||||
| 		public void CreateMissingStorages(RelationDepot other) | ||||
| 		{ | ||||
| 			for (var i = 0; i < RelationTypeIndices.Count; i += 1) | ||||
| 			while (other.RelationTypeIndices.Count >= storages.Length) | ||||
| 			{ | ||||
| 				Array.Resize(ref storages, storages.Length * 2); | ||||
| 			} | ||||
| 
 | ||||
| 			while (other.RelationTypeIndices.Count >= other.storages.Length) | ||||
| 			{ | ||||
| 				Array.Resize(ref other.storages, other.storages.Length * 2); | ||||
| 			} | ||||
| 
 | ||||
| 			for (var i = 0; i < other.RelationTypeIndices.Count; i += 1) | ||||
| 			{ | ||||
| 				if (storages[i] == null && other.storages[i] != null) | ||||
| 				{ | ||||
|  |  | |||
							
								
								
									
										122
									
								
								src/Snapshot.cs
								
								
								
								
							
							
						
						
									
										122
									
								
								src/Snapshot.cs
								
								
								
								
							|  | @ -1,122 +0,0 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
| 	public class Snapshot | ||||
| 	{ | ||||
| 		private World World; | ||||
| 		private Filter? Filter; | ||||
| 
 | ||||
| 		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 unsafe void Take(Filter filter) | ||||
| 		{ | ||||
| 			Clear(); | ||||
| 			Filter = filter; | ||||
| 			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 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 relationStorageIndex = World.RelationDepot.GetStorageIndex(relationTypeIndex, worldEntity, otherEntityID); | ||||
| 						var otherSnapshotID = WorldToSnapshotID[otherEntityID]; | ||||
| 						SnapshotEntityStorage.AddRelationKind(otherSnapshotID, relationTypeIndex); | ||||
| 						SnapshotRelationDepot.Set(snapshotEntityID, otherSnapshotID, relationTypeIndex, World.RelationDepot.Get(relationTypeIndex, relationStorageIndex)); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		public unsafe void Restore() | ||||
| 		{ | ||||
| 			if (Filter == null) | ||||
| 			{ | ||||
| 				return; | ||||
| 			} | ||||
| 
 | ||||
| 			foreach (var entity in Filter.Entities) | ||||
| 			{ | ||||
| 				World.Destroy(entity); | ||||
| 			} | ||||
| 
 | ||||
| 			for (var i = 0; i < SnapshotEntityStorage.Count; i += 1) | ||||
| 			{ | ||||
| 				var entity = World.CreateEntity(); | ||||
| 				SnapshotToWorldID.Add(entity.ID); | ||||
| 
 | ||||
| 				foreach (var componentTypeIndex in SnapshotEntityStorage.ComponentTypeIndices(i)) | ||||
| 				{ | ||||
| 					World.EntityStorage.SetComponent(entity.ID, componentTypeIndex); | ||||
| 					World.FilterStorage.Check(entity.ID, componentTypeIndex); | ||||
| 					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 in SnapshotRelationDepot.OutRelations(i, relationTypeIndex)) | ||||
| 					{ | ||||
| 						var relationStorageIndex = SnapshotRelationDepot.GetStorageIndex(relationTypeIndex, i, otherEntityID); | ||||
| 						var otherEntityWorldID = SnapshotToWorldID[otherEntityID]; | ||||
| 						World.RelationDepot.Set(worldID, otherEntityWorldID, relationTypeIndex, SnapshotRelationDepot.Get(relationTypeIndex, relationStorageIndex)); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		private void Clear() | ||||
| 		{ | ||||
| 			SnapshotEntityStorage.Clear(); | ||||
| 			SnapshotComponentDepot.Clear(); | ||||
| 			SnapshotRelationDepot.Clear(); | ||||
| 			SnapshotToWorldID.Clear(); | ||||
| 			WorldToSnapshotID.Clear(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										94
									
								
								src/World.cs
								
								
								
								
							
							
						
						
									
										94
									
								
								src/World.cs
								
								
								
								
							|  | @ -1,4 +1,5 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
|  | @ -23,9 +24,19 @@ namespace MoonTools.ECS | |||
| 			TemplateComponentDepot = new ComponentDepot(ComponentTypeIndices); | ||||
| 		} | ||||
| 
 | ||||
| 		public Entity CreateEntity() | ||||
| 		public Entity CreateEntity(string tag = "") | ||||
| 		{ | ||||
| 			return EntityStorage.Create(); | ||||
| 			return EntityStorage.Create(tag); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Tag(Entity entity, string tag) | ||||
| 		{ | ||||
| 			EntityStorage.Tag(entity, tag); | ||||
| 		} | ||||
| 
 | ||||
| 		public string GetTag(Entity entity) | ||||
| 		{ | ||||
| 			return EntityStorage.Tag(entity); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Set<TComponent>(Entity entity, in TComponent component) where TComponent : unmanaged | ||||
|  | @ -45,6 +56,17 @@ namespace MoonTools.ECS | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// untyped version for Transfer | ||||
| 		internal unsafe void Set(Entity entity, int componentTypeIndex, void* component) | ||||
| 		{ | ||||
| 			ComponentDepot.Set(entity.ID, componentTypeIndex, component); | ||||
| 
 | ||||
| 			if (EntityStorage.SetComponent(entity.ID, componentTypeIndex)) | ||||
| 			{ | ||||
| 				FilterStorage.Check(entity.ID, componentTypeIndex); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		public void Remove<TComponent>(in Entity entity) where TComponent : unmanaged | ||||
| 		{ | ||||
| 			if (EntityStorage.RemoveComponent(entity.ID, ComponentTypeIndices.GetIndex<TComponent>())) | ||||
|  | @ -63,6 +85,14 @@ namespace MoonTools.ECS | |||
| 			EntityStorage.AddRelationKind(entityB.ID, relationTypeIndex); | ||||
| 		} | ||||
| 
 | ||||
| 		// untyped version for Transfer | ||||
| 		internal unsafe void Relate(Entity entityA, Entity entityB, int relationTypeIndex, void* relationData) | ||||
| 		{ | ||||
| 			RelationDepot.Set(entityA, entityB, relationTypeIndex, relationData); | ||||
| 			EntityStorage.AddRelationKind(entityA.ID, relationTypeIndex); | ||||
| 			EntityStorage.AddRelationKind(entityB.ID, relationTypeIndex); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Unrelate<TRelationKind>(in Entity entityA, in Entity entityB) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			var (aEmpty, bEmpty) = RelationDepot.Remove<TRelationKind>(entityA, entityB); | ||||
|  | @ -118,9 +148,65 @@ namespace MoonTools.ECS | |||
| 			MessageDepot.Clear(); | ||||
| 		} | ||||
| 
 | ||||
| 		public Snapshot CreateSnapshot() | ||||
| 		private Dictionary<int, int> WorldToTransferID = new Dictionary<int, int>(); | ||||
| 
 | ||||
| 		// FIXME: this API sucks | ||||
| 		public unsafe void Transfer(World other, Filter filter, Filter otherFilter) | ||||
| 		{ | ||||
| 			return new Snapshot(this); | ||||
| 			WorldToTransferID.Clear(); | ||||
| 			other.ComponentDepot.CreateMissingStorages(ComponentDepot); | ||||
| 			other.RelationDepot.CreateMissingStorages(RelationDepot); | ||||
| 
 | ||||
| 			// destroy all entities matching the filter | ||||
| 			foreach (var entity in otherFilter.Entities) | ||||
| 			{ | ||||
| 				other.Destroy(entity); | ||||
| 			} | ||||
| 
 | ||||
| 			// create entities and set their components | ||||
| 			foreach (var entity in filter.Entities) | ||||
| 			{ | ||||
| 				TransferEntity(other, entity); | ||||
| 			} | ||||
| 
 | ||||
| 			// set relations | ||||
| 			// FIXME: something is going wrong here, the transfer back isn't working | ||||
| 			foreach (var entity in filter.Entities) | ||||
| 			{ | ||||
| 				var otherWorldEntityA = WorldToTransferID[entity.ID]; | ||||
| 
 | ||||
| 				foreach (var relationTypeIndex in EntityStorage.RelationTypeIndices(entity.ID)) | ||||
| 				{ | ||||
| 					foreach (var entityB in RelationDepot.OutRelations(entity.ID, relationTypeIndex)) | ||||
| 					{ | ||||
| 						var storageIndex = RelationDepot.GetStorageIndex(relationTypeIndex, entity.ID, entityB); | ||||
| 
 | ||||
| 						int otherWorldEntityB; | ||||
| 						if (WorldToTransferID.TryGetValue(entityB, out otherWorldEntityB)) | ||||
| 						{ | ||||
| 							other.Relate(otherWorldEntityA, otherWorldEntityB, relationTypeIndex, RelationDepot.Get(relationTypeIndex, storageIndex)); | ||||
| 						} | ||||
| 						else | ||||
| 						{ | ||||
| 							// related entity is not in the filter | ||||
| 							throw new Exception($"Missing transfer entity! {EntityStorage.Tag(entity.ID)} related to {EntityStorage.Tag(entityB.ID)}"); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		private unsafe int TransferEntity(World other, Entity entity) | ||||
| 		{ | ||||
| 			var otherWorldEntity = other.CreateEntity(GetTag(entity)); | ||||
| 			WorldToTransferID.Add(entity.ID, otherWorldEntity.ID); | ||||
| 
 | ||||
| 			foreach (var componentTypeIndex in EntityStorage.ComponentTypeIndices(entity.ID)) | ||||
| 			{ | ||||
| 				other.Set(otherWorldEntity, componentTypeIndex, ComponentDepot.UntypedGet(entity.ID, componentTypeIndex)); | ||||
| 			} | ||||
| 
 | ||||
| 			return otherWorldEntity.ID; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue