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.

549 lines
20KB

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