Mirror of Svelto.ECS because we're a fan of it
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

250 lines
13KB

  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using Svelto.DataStructures;
  4. using Svelto.ECS.Internal;
  5. using Svelto.ECS.Serialization;
  6. namespace Svelto.ECS
  7. {
  8. public partial class EnginesRoot
  9. {
  10. sealed class EntitySerialization : IEntitySerialization
  11. {
  12. public void SerializeEntity(EGID egid, ISerializationData serializationData, int serializationType)
  13. {
  14. var entitiesDb = _enginesRoot._entitiesDB;
  15. //needs to retrieve the meta data associated with the entity
  16. ref var serializableEntityComponent = ref entitiesDb.QueryEntity<SerializableEntityComponent>(egid);
  17. uint descriptorHash = serializableEntityComponent.descriptorHash;
  18. SerializationDescriptorMap serializationDescriptorMap = _enginesRoot._serializationDescriptorMap;
  19. ISerializableEntityDescriptor entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash);
  20. ISerializableComponentBuilder[] entityComponentsToSerialise = entityDescriptor.componentsToSerialize;
  21. var header =
  22. new SerializableEntityHeader(descriptorHash, egid, (byte)entityComponentsToSerialise.Length);
  23. header.Copy(serializationData);
  24. for (int index = 0; index < entityComponentsToSerialise.Length; index++)
  25. {
  26. var entityBuilder = entityComponentsToSerialise[index];
  27. serializationData.BeginNextEntityComponent();
  28. SerializeEntityComponent(egid, entityBuilder, serializationData, serializationType);
  29. }
  30. }
  31. public EntityInitializer DeserializeNewEntity(EGID egid, ISerializationData serializationData,
  32. int serializationType)
  33. {
  34. //todo: SerializableEntityHeader may be needed to be customizable
  35. var serializableEntityHeader = new SerializableEntityHeader(serializationData);
  36. uint descriptorHash = serializableEntityHeader.descriptorHash;
  37. SerializationDescriptorMap serializationDescriptorMap = _enginesRoot._serializationDescriptorMap;
  38. var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash);
  39. IDeserializationFactory factory = serializationDescriptorMap.GetSerializationFactory(descriptorHash);
  40. return factory.BuildDeserializedEntity(egid, serializationData, entityDescriptor, serializationType,
  41. this, _enginesRoot.GenerateEntityFactory(), _enginesRoot is SerializingEnginesRoot);
  42. }
  43. public void DeserializeEntity(ISerializationData serializationData, int serializationType)
  44. {
  45. var serializableEntityHeader = new SerializableEntityHeader(serializationData);
  46. EGID egid = serializableEntityHeader.egid;
  47. DeserializeEntityInternal(serializationData, egid, serializableEntityHeader, serializationType);
  48. }
  49. public void DeserializeEntity(EGID egid, ISerializationData serializationData, int serializationType)
  50. {
  51. var serializableEntityHeader = new SerializableEntityHeader(serializationData);
  52. DeserializeEntityInternal(serializationData, egid, serializableEntityHeader, serializationType);
  53. }
  54. public void DeserializeEntityComponents(ISerializationData serializationData,
  55. ISerializableEntityDescriptor entityDescriptor, ref EntityInitializer initializer,
  56. int serializationType)
  57. {
  58. foreach (var serializableEntityBuilder in entityDescriptor.componentsToSerialize)
  59. {
  60. serializationData.BeginNextEntityComponent();
  61. serializableEntityBuilder.Deserialize(serializationData, initializer, serializationType);
  62. }
  63. }
  64. /// <summary>
  65. /// Note this has been left undocumented and forgot over the months. The initial version was obviously
  66. /// wrong, as it wasn't looking for T but only assuming that T was the first component in the entity.
  67. /// It's also weird or at least must be revalidated, the fact that serializationData works only as
  68. /// a tape, so we need to reset datapos in case we do not want to forward the head.
  69. /// </summary>
  70. /// <param name="serializationData"></param>
  71. /// <param name="entityDescriptor"></param>
  72. /// <param name="serializationType"></param>
  73. /// <typeparam name="T"></typeparam>
  74. /// <returns></returns>
  75. public T DeserializeEntityComponent<T>(ISerializationData serializationData,
  76. ISerializableEntityDescriptor entityDescriptor, int serializationType)
  77. where T : unmanaged, IEntityComponent
  78. {
  79. var readPos = serializationData.dataPos;
  80. T entityComponent = default;
  81. foreach (var serializableEntityBuilder in entityDescriptor.componentsToSerialize)
  82. {
  83. if (serializableEntityBuilder is SerializableComponentBuilder<T> entityBuilder)
  84. {
  85. entityBuilder.Deserialize(serializationData, ref entityComponent, serializationType);
  86. break;
  87. }
  88. else
  89. serializationData.dataPos += (uint)serializableEntityBuilder.Size(serializationType);
  90. }
  91. serializationData.dataPos = readPos;
  92. return entityComponent;
  93. }
  94. public void DeserializeEntityToSwap(EGID fromEGID, EGID toEGID, [CallerMemberName] string caller = null)
  95. {
  96. EntitiesDB entitiesDb = _enginesRoot._entitiesDB;
  97. ref var serializableEntityComponent = ref entitiesDb.QueryEntity<SerializableEntityComponent>(fromEGID);
  98. SerializationDescriptorMap serializationDescriptorMap = _enginesRoot._serializationDescriptorMap;
  99. uint descriptorHash = serializableEntityComponent.descriptorHash;
  100. var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash);
  101. _enginesRoot.CheckRemoveEntityID(fromEGID, entityDescriptor.realType, caller);
  102. _enginesRoot.CheckAddEntityID(toEGID, entityDescriptor.realType, caller);
  103. /// Serializable Entity Descriptors can be extended so we need to use FindRealComponents
  104. _enginesRoot.QueueSwapEntityOperation(fromEGID, toEGID,
  105. _enginesRoot.FindRealComponents(fromEGID, entityDescriptor.componentsToBuild), caller);
  106. }
  107. public void DeserializeEntityToDelete(EGID egid, [CallerMemberName] string caller = null)
  108. {
  109. EntitiesDB entitiesDB = _enginesRoot._entitiesDB;
  110. ref var serializableEntityComponent = ref entitiesDB.QueryEntity<SerializableEntityComponent>(egid);
  111. uint descriptorHash = serializableEntityComponent.descriptorHash;
  112. SerializationDescriptorMap serializationDescriptorMap = _enginesRoot._serializationDescriptorMap;
  113. var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash);
  114. _enginesRoot.CheckRemoveEntityID(egid, entityDescriptor.realType, caller);
  115. try
  116. {
  117. /// Serializable Entity Descriptors can be extended so we need to use FindRealComponents
  118. _enginesRoot.QueueRemoveEntityOperation(egid,
  119. _enginesRoot.FindRealComponents(egid, entityDescriptor.componentsToBuild), caller);
  120. }
  121. catch
  122. {
  123. Console.LogError(
  124. $"something went wrong while deserializing entity {entityDescriptor.realType}");
  125. throw;
  126. }
  127. }
  128. public void SkipEntityDeserialization(ISerializationData serializationData, int serializationType,
  129. int numberOfEntities)
  130. {
  131. uint dataPositionBeforeHeader = serializationData.dataPos;
  132. var serializableEntityHeader = new SerializableEntityHeader(serializationData);
  133. uint headerSize = serializationData.dataPos - dataPositionBeforeHeader;
  134. uint descriptorHash = serializableEntityHeader.descriptorHash;
  135. SerializationDescriptorMap serializationDescriptorMap = _enginesRoot._serializationDescriptorMap;
  136. var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash);
  137. uint componentSizeTotal = 0;
  138. foreach (var serializableEntityBuilder in entityDescriptor.componentsToSerialize)
  139. {
  140. componentSizeTotal += (uint)serializableEntityBuilder.Size(serializationType);
  141. }
  142. //When constructing an SerializableEntityHeader the data position of the serializationData is incremented by the size of the header.
  143. //Since a header is needed to get the entity descriptor, we need to account for one less header than usual, since the data has already
  144. //been incremented once.
  145. var totalBytesToSkip = (uint)(headerSize * (numberOfEntities - 1)) +
  146. (uint)(componentSizeTotal * numberOfEntities);
  147. serializationData.dataPos += totalBytesToSkip;
  148. }
  149. public uint GetHashFromGroup(ExclusiveGroupStruct groupStruct)
  150. {
  151. return GroupHashMap.GetHashFromGroup(groupStruct);
  152. }
  153. public ExclusiveGroupStruct GetGroupFromHash(uint groupHash)
  154. {
  155. return GroupHashMap.GetGroupFromHash(groupHash);
  156. }
  157. public void RegisterSerializationFactory<T>(IDeserializationFactory deserializationFactory)
  158. where T : ISerializableEntityDescriptor, new()
  159. {
  160. SerializationDescriptorMap serializationDescriptorMap = _enginesRoot._serializationDescriptorMap;
  161. serializationDescriptorMap.RegisterSerializationFactory<T>(deserializationFactory);
  162. }
  163. internal EntitySerialization(EnginesRoot enginesRoot)
  164. {
  165. _root = new DataStructures.WeakReference<EnginesRoot>(enginesRoot);
  166. }
  167. void SerializeEntityComponent(EGID entityGID, ISerializableComponentBuilder componentBuilder,
  168. ISerializationData serializationData, int serializationType)
  169. {
  170. ExclusiveGroupStruct groupId = entityGID.groupID;
  171. Type entityType = componentBuilder.GetEntityComponentType();
  172. if (!_enginesRoot._entitiesDB.UnsafeQueryEntityDictionary(groupId, entityType, out var safeDictionary))
  173. {
  174. throw new Exception("Entity Serialization failed");
  175. }
  176. componentBuilder.Serialize(entityGID.entityID, safeDictionary, serializationData, serializationType);
  177. }
  178. void DeserializeEntityInternal(ISerializationData serializationData, EGID egid,
  179. SerializableEntityHeader serializableEntityHeader, int serializationType)
  180. {
  181. SerializationDescriptorMap descriptorMap = _enginesRoot._serializationDescriptorMap;
  182. var entityDescriptor = descriptorMap.GetDescriptorFromHash(serializableEntityHeader.descriptorHash);
  183. if (_enginesRoot._groupEntityComponentsDB.TryGetValue(egid.groupID, out var entitiesInGroupPerType) ==
  184. false)
  185. throw new Exception("Entity Serialization failed");
  186. foreach (var serializableEntityBuilder in entityDescriptor.componentsToSerialize)
  187. {
  188. entitiesInGroupPerType.TryGetValue(
  189. new RefWrapperType(serializableEntityBuilder.GetEntityComponentType()), out var safeDictionary);
  190. serializationData.BeginNextEntityComponent();
  191. serializableEntityBuilder.Deserialize(egid.entityID, safeDictionary, serializationData,
  192. serializationType);
  193. }
  194. }
  195. EnginesRoot _enginesRoot => _root.Target;
  196. readonly DataStructures.WeakReference<EnginesRoot> _root;
  197. }
  198. public IEntitySerialization GenerateEntitySerializer()
  199. {
  200. return new EntitySerialization(this);
  201. }
  202. }
  203. }