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.

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