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.

551 lines
20KB

  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using Svelto.Common;
  4. using Svelto.DataStructures;
  5. using Svelto.DataStructures.Native;
  6. using Svelto.ECS.Hybrid;
  7. namespace Svelto.ECS.Internal
  8. {
  9. sealed class TypeSafeDictionary<TValue> : ITypeSafeDictionary<TValue> where TValue : struct, IEntityComponent
  10. {
  11. static readonly Type _type = typeof(TValue);
  12. static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type);
  13. static readonly bool _hasReference = typeof(INeedEntityReference).IsAssignableFrom(_type);
  14. internal static readonly bool isUnmanaged =
  15. _type.IsUnmanagedEx() && (typeof(IEntityViewComponent).IsAssignableFrom(_type) == false);
  16. public TypeSafeDictionary(uint size)
  17. {
  18. if (isUnmanaged)
  19. implUnmgd =
  20. new SveltoDictionary<uint, TValue, NativeStrategy<SveltoDictionaryNode<uint>>,
  21. NativeStrategy<TValue>, NativeStrategy<int>>(size, Allocator.Persistent);
  22. else
  23. {
  24. implMgd =
  25. new SveltoDictionary<uint, TValue, ManagedStrategy<SveltoDictionaryNode<uint>>,
  26. ManagedStrategy<TValue>, ManagedStrategy<int>>(size, Allocator.Managed);
  27. }
  28. }
  29. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  30. public void Add(uint egidEntityId, in TValue entityComponent)
  31. {
  32. if (isUnmanaged)
  33. implUnmgd.Add(egidEntityId, entityComponent);
  34. else
  35. implMgd.Add(egidEntityId, entityComponent);
  36. }
  37. /// todo: Is this really needed, cannot I just use AddEntitiesFromDictionary? Needs to be checked
  38. public void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup)
  39. {
  40. if (isUnmanaged)
  41. {
  42. var valueIndex = implUnmgd.GetIndex(fromEntityGid.entityID);
  43. DBC.ECS.Check.Require(toGroup != null
  44. , "Invalid To Group"); //todo check this, if it's right merge GetIndex
  45. {
  46. var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
  47. ref var entity = ref implUnmgd.GetDirectValueByRef(valueIndex);
  48. if (_hasEgid)
  49. SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID);
  50. toGroupCasted.Add(toEntityID.entityID, entity);
  51. }
  52. }
  53. else
  54. {
  55. var valueIndex = implMgd.GetIndex(fromEntityGid.entityID);
  56. DBC.ECS.Check.Require(toGroup != null
  57. , "Invalid To Group"); //todo check this, if it's right merge GetIndex
  58. {
  59. var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
  60. ref var entity = ref implMgd.GetDirectValueByRef(valueIndex);
  61. if (_hasEgid)
  62. SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID);
  63. toGroupCasted.Add(toEntityID.entityID, entity);
  64. }
  65. }
  66. }
  67. /// <summary>
  68. /// Add entities from external typeSafeDictionary (todo: add use case)
  69. /// </summary>
  70. /// <param name="entitiesToSubmit"></param>
  71. /// <param name="groupId"></param>
  72. /// <param name="enginesRoot"></param>
  73. /// <exception cref="TypeSafeDictionaryException"></exception>
  74. public void AddEntitiesFromDictionary
  75. (ITypeSafeDictionary entitiesToSubmit, ExclusiveGroupStruct groupId, EnginesRoot enginesRoot)
  76. {
  77. var safeDictionary = (entitiesToSubmit as TypeSafeDictionary<TValue>);
  78. if (isUnmanaged)
  79. {
  80. var typeSafeDictionary = safeDictionary.implUnmgd;
  81. foreach (var tuple in typeSafeDictionary)
  82. try
  83. {
  84. var egid = new EGID(tuple.Key, groupId);
  85. if (_hasEgid)
  86. SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref tuple.Value, egid);
  87. if (_hasReference)
  88. SetEGIDWithoutBoxing<TValue>.SetRefWithoutBoxing(
  89. ref tuple.Value, enginesRoot.entityLocator.GetEntityReference(egid));
  90. implUnmgd.Add(tuple.Key, tuple.Value);
  91. }
  92. catch (Exception e)
  93. {
  94. Console.LogException(
  95. e, "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId.ToName()).FastConcat(", id ").FastConcat(tuple.Key));
  96. throw;
  97. }
  98. }
  99. else
  100. {
  101. var typeSafeDictionary = safeDictionary.implMgd;
  102. foreach (var tuple in typeSafeDictionary)
  103. try
  104. {
  105. if (_hasEgid)
  106. SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(
  107. ref tuple.Value, new EGID(tuple.Key, groupId));
  108. implMgd.Add(tuple.Key, tuple.Value);
  109. }
  110. catch (Exception e)
  111. {
  112. Console.LogException(
  113. e, "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId.ToName()).FastConcat(", id ").FastConcat(tuple.Key));
  114. throw;
  115. }
  116. }
  117. }
  118. public void ExecuteEnginesAddOrSwapCallbacks
  119. (FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer>> entityComponentEnginesDB
  120. , ITypeSafeDictionary realDic, ExclusiveGroupStruct? fromGroup, ExclusiveGroupStruct toGroup
  121. , in PlatformProfiler profiler)
  122. {
  123. if (isUnmanaged)
  124. {
  125. var typeSafeDictionary = realDic as ITypeSafeDictionary<TValue>;
  126. //this can be optimized, should pass all the entities and not restart the process for each one
  127. foreach (var value in implUnmgd)
  128. ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(entityComponentEnginesDB
  129. , ref typeSafeDictionary[value.Key], fromGroup
  130. , in profiler, new EGID(value.Key, toGroup));
  131. }
  132. else
  133. {
  134. var typeSafeDictionary = realDic as ITypeSafeDictionary<TValue>;
  135. //this can be optimized, should pass all the entities and not restart the process for each one
  136. foreach (var value in implMgd)
  137. ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(entityComponentEnginesDB
  138. , ref typeSafeDictionary[value.Key], fromGroup
  139. , in profiler, new EGID(value.Key, toGroup));
  140. }
  141. }
  142. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  143. public void Clear()
  144. {
  145. if (isUnmanaged)
  146. {
  147. implUnmgd.Clear();
  148. }
  149. else
  150. {
  151. implMgd.Clear();
  152. }
  153. }
  154. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  155. public void FastClear()
  156. {
  157. if (isUnmanaged)
  158. {
  159. implUnmgd.FastClear();
  160. }
  161. else
  162. {
  163. implMgd.FastClear();
  164. }
  165. }
  166. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  167. public bool ContainsKey(uint egidEntityId)
  168. {
  169. if (isUnmanaged)
  170. {
  171. return implUnmgd.ContainsKey(egidEntityId);
  172. }
  173. else
  174. {
  175. return implMgd.ContainsKey(egidEntityId);
  176. }
  177. }
  178. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  179. public ITypeSafeDictionary Create()
  180. {
  181. return TypeSafeDictionaryFactory<TValue>.Create(1);
  182. }
  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. public void Dispose()
  415. {
  416. if (isUnmanaged)
  417. implUnmgd.Dispose();
  418. else
  419. implMgd.Dispose();
  420. GC.SuppressFinalize(this);
  421. }
  422. void ExecuteEnginesAddOrSwapCallbacksOnSingleEntity
  423. (FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer>> engines, ref TValue entity
  424. , ExclusiveGroupStruct? previousGroup, in PlatformProfiler profiler, EGID egid)
  425. {
  426. //get all the engines linked to TValue
  427. if (!engines.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines))
  428. return;
  429. if (previousGroup == null)
  430. {
  431. for (var i = 0; i < entityComponentsEngines.count; i++)
  432. try
  433. {
  434. using (profiler.Sample(entityComponentsEngines[i].name))
  435. {
  436. (entityComponentsEngines[i].engine as IReactOnAddAndRemove<TValue>).Add(ref entity, egid);
  437. }
  438. }
  439. catch
  440. {
  441. Console.LogError("Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()));
  442. throw;
  443. }
  444. }
  445. else
  446. {
  447. for (var i = 0; i < entityComponentsEngines.count; i++)
  448. try
  449. {
  450. using (profiler.Sample(entityComponentsEngines[i].name))
  451. {
  452. (entityComponentsEngines[i].engine as IReactOnSwap<TValue>).MovedTo(
  453. ref entity, previousGroup.Value, egid);
  454. }
  455. }
  456. catch (Exception)
  457. {
  458. Console.LogError("Code crashed inside MoveTo callback ".FastConcat(typeof(TValue).ToString()));
  459. throw;
  460. }
  461. }
  462. }
  463. static void ExecuteEnginesRemoveCallbackOnSingleEntity
  464. (FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer>> engines, ref TValue entity
  465. , in PlatformProfiler profiler, EGID egid)
  466. {
  467. if (!engines.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines))
  468. return;
  469. for (var i = 0; i < entityComponentsEngines.count; i++)
  470. try
  471. {
  472. using (profiler.Sample(entityComponentsEngines[i].name))
  473. {
  474. (entityComponentsEngines[i].engine as IReactOnAddAndRemove<TValue>).Remove(ref entity, egid);
  475. }
  476. }
  477. catch
  478. {
  479. Console.LogError("Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()));
  480. throw;
  481. }
  482. }
  483. internal SveltoDictionary<uint, TValue, ManagedStrategy<SveltoDictionaryNode<uint>>, ManagedStrategy<TValue>,
  484. ManagedStrategy<int>> implMgd;
  485. //used directly by native methods
  486. internal SveltoDictionary<uint, TValue, NativeStrategy<SveltoDictionaryNode<uint>>, NativeStrategy<TValue>,
  487. NativeStrategy<int>> implUnmgd;
  488. }
  489. }