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.

536 lines
19KB

  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using Svelto.Common;
  4. using Svelto.DataStructures;
  5. using Svelto.ECS.Hybrid;
  6. namespace Svelto.ECS.Internal
  7. {
  8. sealed class TypeSafeDictionary<TValue> : ITypeSafeDictionary<TValue> where TValue : struct, IEntityComponent
  9. {
  10. static readonly Type _type = typeof(TValue);
  11. static readonly string _typeName = _type.Name;
  12. static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type);
  13. internal static readonly bool IsUnmanaged =
  14. _type.IsUnmanagedEx() && (typeof(IEntityViewComponent).IsAssignableFrom(_type) == false);
  15. SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, ManagedStrategy<TValue>,
  16. ManagedStrategy<int>> implMgd;
  17. //used directly by native methods
  18. internal SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<TValue>,
  19. NativeStrategy<int>> implUnmgd;
  20. public TypeSafeDictionary(uint size)
  21. {
  22. if (IsUnmanaged)
  23. implUnmgd = new SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>,
  24. NativeStrategy<TValue>, NativeStrategy<int>>(size);
  25. else
  26. {
  27. implMgd = new SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>,
  28. ManagedStrategy<TValue>, ManagedStrategy<int>>(size);
  29. }
  30. }
  31. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  32. public void Add(uint egidEntityId, in TValue entityComponent)
  33. {
  34. if (IsUnmanaged)
  35. implUnmgd.Add(egidEntityId, entityComponent);
  36. else
  37. implMgd.Add(egidEntityId, entityComponent);
  38. }
  39. /// <summary>
  40. /// Add entities from external typeSafeDictionary
  41. /// </summary>
  42. /// <param name="entitiesToSubmit"></param>
  43. /// <param name="groupId"></param>
  44. /// <exception cref="TypeSafeDictionaryException"></exception>
  45. public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId)
  46. {
  47. if (IsUnmanaged)
  48. {
  49. var typeSafeDictionary = (entitiesToSubmit as TypeSafeDictionary<TValue>).implUnmgd;
  50. foreach (var tuple in typeSafeDictionary)
  51. try
  52. {
  53. if (_hasEgid)
  54. SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(
  55. ref tuple.Value, new EGID(tuple.Key, groupId));
  56. implUnmgd.Add(tuple.Key, tuple.Value);
  57. }
  58. catch (Exception e)
  59. {
  60. Console.LogException(
  61. e, "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key));
  62. throw;
  63. }
  64. }
  65. else
  66. {
  67. var typeSafeDictionary = (entitiesToSubmit as TypeSafeDictionary<TValue>).implMgd;
  68. foreach (var tuple in typeSafeDictionary)
  69. try
  70. {
  71. if (_hasEgid)
  72. SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(
  73. ref tuple.Value, new EGID(tuple.Key, groupId));
  74. implMgd.Add(tuple.Key, tuple.Value);
  75. }
  76. catch (Exception e)
  77. {
  78. Console.LogException(
  79. e, "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key));
  80. throw;
  81. }
  82. }
  83. }
  84. public void ExecuteEnginesAddOrSwapCallbacks
  85. (FasterDictionary<RefWrapperType, FasterList<IReactEngine>> entityComponentEnginesDB
  86. , ITypeSafeDictionary realDic, ExclusiveGroupStruct? fromGroup, ExclusiveGroupStruct toGroup
  87. , in PlatformProfiler profiler)
  88. {
  89. if (IsUnmanaged)
  90. {
  91. var typeSafeDictionary = realDic as ITypeSafeDictionary<TValue>;
  92. //this can be optimized, should pass all the entities and not restart the process for each one
  93. foreach (var value in implUnmgd)
  94. ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(entityComponentEnginesDB, ref typeSafeDictionary[value.Key]
  95. , fromGroup, in profiler, new EGID(value.Key, toGroup));
  96. }
  97. else
  98. {
  99. var typeSafeDictionary = realDic as ITypeSafeDictionary<TValue>;
  100. //this can be optimized, should pass all the entities and not restart the process for each one
  101. foreach (var value in implMgd)
  102. ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(entityComponentEnginesDB, ref typeSafeDictionary[value.Key]
  103. , fromGroup, in profiler, new EGID(value.Key, toGroup));
  104. }
  105. }
  106. public void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup)
  107. {
  108. if (IsUnmanaged)
  109. {
  110. var valueIndex = implUnmgd.GetIndex(fromEntityGid.entityID);
  111. DBC.ECS.Check.Require(toGroup != null
  112. , "Invalid To Group"); //todo check this, if it's right merge GetIndex
  113. {
  114. var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
  115. ref var entity = ref implUnmgd.GetDirectValueByRef(valueIndex);
  116. if (_hasEgid)
  117. SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID);
  118. toGroupCasted.Add(toEntityID.entityID, entity);
  119. }
  120. }
  121. else
  122. {
  123. var valueIndex = implMgd.GetIndex(fromEntityGid.entityID);
  124. DBC.ECS.Check.Require(toGroup != null
  125. , "Invalid To Group"); //todo check this, if it's right merge GetIndex
  126. {
  127. var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
  128. ref var entity = ref implMgd.GetDirectValueByRef(valueIndex);
  129. if (_hasEgid)
  130. SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID);
  131. toGroupCasted.Add(toEntityID.entityID, entity);
  132. }
  133. }
  134. }
  135. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  136. public void Clear()
  137. {
  138. if (IsUnmanaged)
  139. {
  140. implUnmgd.Clear();
  141. }
  142. else
  143. {
  144. implMgd.Clear();
  145. }
  146. }
  147. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  148. public void FastClear()
  149. {
  150. if (IsUnmanaged)
  151. {
  152. implUnmgd.FastClear();
  153. }
  154. else
  155. {
  156. implMgd.FastClear();
  157. }
  158. }
  159. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  160. public bool ContainsKey(uint egidEntityId)
  161. {
  162. if (IsUnmanaged)
  163. {
  164. return implUnmgd.ContainsKey(egidEntityId);
  165. }
  166. else
  167. {
  168. return implMgd.ContainsKey(egidEntityId);
  169. }
  170. }
  171. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  172. public ITypeSafeDictionary Create() { return TypeSafeDictionaryFactory<TValue>.Create(1); }
  173. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  174. public uint GetIndex(uint valueEntityId)
  175. {
  176. if (IsUnmanaged)
  177. {
  178. return this.implUnmgd.GetIndex(valueEntityId);
  179. }
  180. else
  181. {
  182. return this.implMgd.GetIndex(valueEntityId);
  183. }
  184. }
  185. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  186. public ref TValue GetOrCreate(uint idEntityId)
  187. {
  188. if (IsUnmanaged)
  189. {
  190. return ref this.implUnmgd.GetOrCreate(idEntityId);
  191. }
  192. else
  193. {
  194. return ref this.implMgd.GetOrCreate(idEntityId);
  195. }
  196. }
  197. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  198. public IBuffer<TValue> GetValues(out uint count)
  199. {
  200. if (IsUnmanaged)
  201. {
  202. return this.implUnmgd.GetValues(out count);
  203. }
  204. else
  205. {
  206. return this.implMgd.GetValues(out count);
  207. }
  208. }
  209. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  210. public ref TValue GetDirectValueByRef(uint key)
  211. {
  212. if (IsUnmanaged)
  213. {
  214. return ref this.implUnmgd.GetDirectValueByRef(key);
  215. }
  216. else
  217. {
  218. return ref this.implMgd.GetDirectValueByRef(key);
  219. }
  220. }
  221. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  222. public bool Has(uint key)
  223. {
  224. if (IsUnmanaged)
  225. {
  226. return this.implUnmgd.ContainsKey(key);
  227. }
  228. else
  229. {
  230. return this.implMgd.ContainsKey(key);
  231. }
  232. }
  233. public void ExecuteEnginesSwapOrRemoveCallbacks
  234. (EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup
  235. , FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, in PlatformProfiler profiler)
  236. {
  237. if (IsUnmanaged)
  238. {
  239. var valueIndex = this.implUnmgd.GetIndex(fromEntityGid.entityID);
  240. ref var entity = ref this.implUnmgd.GetDirectValueByRef(valueIndex);
  241. //move
  242. if (toGroup != null)
  243. {
  244. var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
  245. var previousGroup = fromEntityGid.groupID;
  246. if (_hasEgid)
  247. SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID.Value);
  248. var index = toGroupCasted.GetIndex(toEntityID.Value.entityID);
  249. ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(engines, ref toGroupCasted.GetDirectValueByRef(index)
  250. , previousGroup, in profiler, toEntityID.Value);
  251. }
  252. //remove
  253. else
  254. {
  255. ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref entity, in profiler, fromEntityGid);
  256. }
  257. }
  258. else
  259. {
  260. var valueIndex = this.implMgd.GetIndex(fromEntityGid.entityID);
  261. ref var entity = ref this.implMgd.GetDirectValueByRef(valueIndex);
  262. if (toGroup != null)
  263. {
  264. var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
  265. var previousGroup = fromEntityGid.groupID;
  266. if (_hasEgid)
  267. SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID.Value);
  268. var index = toGroupCasted.GetIndex(toEntityID.Value.entityID);
  269. ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(engines, ref toGroupCasted.GetDirectValueByRef(index)
  270. , previousGroup, in profiler, toEntityID.Value);
  271. }
  272. else
  273. {
  274. ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref entity, in profiler, fromEntityGid);
  275. }
  276. }
  277. }
  278. public void ExecuteEnginesRemoveCallbacks
  279. (FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, in PlatformProfiler profiler
  280. , ExclusiveGroupStruct group)
  281. {
  282. if (IsUnmanaged)
  283. {
  284. foreach (var value in implUnmgd)
  285. ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref implUnmgd.GetValueByRef(value.Key)
  286. , in profiler, new EGID(value.Key, group));
  287. }
  288. else
  289. {
  290. foreach (var value in implMgd)
  291. ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref implMgd.GetValueByRef(value.Key)
  292. , in profiler, new EGID(value.Key, group));
  293. }
  294. }
  295. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  296. public void RemoveEntityFromDictionary(EGID fromEntityGid)
  297. {
  298. if (IsUnmanaged)
  299. {
  300. this.implUnmgd.Remove(fromEntityGid.entityID);
  301. }
  302. else
  303. {
  304. this.implMgd.Remove(fromEntityGid.entityID);
  305. }
  306. }
  307. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  308. public void SetCapacity(uint size)
  309. {
  310. if (IsUnmanaged)
  311. {
  312. this.implUnmgd.SetCapacity(size);
  313. }
  314. else
  315. {
  316. this.implMgd.SetCapacity(size);
  317. }
  318. }
  319. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  320. public void Trim()
  321. {
  322. if (IsUnmanaged)
  323. {
  324. this.implUnmgd.Trim();
  325. }
  326. else
  327. {
  328. this.implMgd.Trim();
  329. }
  330. }
  331. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  332. public bool TryFindIndex(uint entityId, out uint index)
  333. {
  334. if (IsUnmanaged)
  335. {
  336. return implUnmgd.TryFindIndex(entityId, out index);
  337. }
  338. else
  339. {
  340. return implMgd.TryFindIndex(entityId, out index);
  341. }
  342. }
  343. public void KeysEvaluator(Action<uint> action)
  344. {
  345. if (IsUnmanaged)
  346. {
  347. foreach (var key in implUnmgd.keys)
  348. {
  349. action(key);
  350. }
  351. }
  352. else
  353. {
  354. foreach (var key in implMgd.keys)
  355. {
  356. action(key);
  357. }
  358. }
  359. }
  360. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  361. public bool TryGetValue(uint entityId, out TValue item)
  362. {
  363. if (IsUnmanaged)
  364. {
  365. return this.implUnmgd.TryGetValue(entityId, out item);
  366. }
  367. else
  368. {
  369. return this.implMgd.TryGetValue(entityId, out item);
  370. }
  371. }
  372. public uint count
  373. {
  374. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  375. get
  376. {
  377. if (IsUnmanaged)
  378. {
  379. return (uint) this.implUnmgd.count;
  380. }
  381. else
  382. {
  383. return (uint) this.implMgd.count;
  384. }
  385. }
  386. }
  387. public ref TValue this[uint idEntityId]
  388. {
  389. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  390. get
  391. {
  392. if (IsUnmanaged)
  393. {
  394. return ref this.implUnmgd.GetValueByRef(idEntityId);
  395. }
  396. else
  397. {
  398. return ref this.implMgd.GetValueByRef(idEntityId);
  399. }
  400. }
  401. }
  402. static void ExecuteEnginesRemoveCallbackOnSingleEntity
  403. (FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, ref TValue entity
  404. , in PlatformProfiler profiler, EGID egid)
  405. {
  406. if (!engines.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines))
  407. return;
  408. for (var i = 0; i < entityComponentsEngines.count; i++)
  409. try
  410. {
  411. using (profiler.Sample(entityComponentsEngines[i], _typeName))
  412. {
  413. (entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Remove(ref entity, egid);
  414. }
  415. }
  416. catch
  417. {
  418. Svelto.Console.LogError(
  419. "Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()));
  420. throw;
  421. }
  422. }
  423. void ExecuteEnginesAddOrSwapCallbacksOnSingleEntity
  424. (FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, ref TValue entity
  425. , ExclusiveGroupStruct? previousGroup, in PlatformProfiler profiler, EGID egid)
  426. {
  427. //get all the engines linked to TValue
  428. if (!engines.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines))
  429. return;
  430. if (previousGroup == null)
  431. {
  432. for (var i = 0; i < entityComponentsEngines.count; i++)
  433. try
  434. {
  435. using (profiler.Sample(entityComponentsEngines[i], _typeName))
  436. {
  437. (entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Add(ref entity, egid);
  438. }
  439. }
  440. catch
  441. {
  442. Svelto.Console.LogError(
  443. "Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()));
  444. throw;
  445. }
  446. }
  447. else
  448. {
  449. for (var i = 0; i < entityComponentsEngines.count; i++)
  450. try
  451. {
  452. using (profiler.Sample(entityComponentsEngines[i], _typeName))
  453. {
  454. (entityComponentsEngines[i] as IReactOnSwap<TValue>).MovedTo(
  455. ref entity, previousGroup.Value, egid);
  456. }
  457. }
  458. catch (Exception)
  459. {
  460. Svelto.Console.LogError(
  461. "Code crashed inside MoveTo callback ".FastConcat(typeof(TValue).ToString()));
  462. throw;
  463. }
  464. }
  465. }
  466. public void Dispose()
  467. {
  468. if (IsUnmanaged)
  469. implUnmgd.Dispose();
  470. else
  471. implMgd.Dispose();
  472. GC.SuppressFinalize(this);
  473. }
  474. }
  475. }