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.

538 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<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);
  25. else
  26. {
  27. implMgd = new SveltoDictionary<uint, TValue, NativeStrategy<SveltoDictionaryNode<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
  95. , ref typeSafeDictionary[value.Key], fromGroup
  96. , in profiler, new EGID(value.Key, toGroup));
  97. }
  98. else
  99. {
  100. var typeSafeDictionary = realDic as ITypeSafeDictionary<TValue>;
  101. //this can be optimized, should pass all the entities and not restart the process for each one
  102. foreach (var value in implMgd)
  103. ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(entityComponentEnginesDB
  104. , ref typeSafeDictionary[value.Key], fromGroup
  105. , in profiler, new EGID(value.Key, toGroup));
  106. }
  107. }
  108. public void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup)
  109. {
  110. if (IsUnmanaged)
  111. {
  112. var valueIndex = implUnmgd.GetIndex(fromEntityGid.entityID);
  113. DBC.ECS.Check.Require(toGroup != null
  114. , "Invalid To Group"); //todo check this, if it's right merge GetIndex
  115. {
  116. var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
  117. ref var entity = ref implUnmgd.GetDirectValueByRef(valueIndex);
  118. if (_hasEgid)
  119. SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID);
  120. toGroupCasted.Add(toEntityID.entityID, entity);
  121. }
  122. }
  123. else
  124. {
  125. var valueIndex = implMgd.GetIndex(fromEntityGid.entityID);
  126. DBC.ECS.Check.Require(toGroup != null
  127. , "Invalid To Group"); //todo check this, if it's right merge GetIndex
  128. {
  129. var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
  130. ref var entity = ref implMgd.GetDirectValueByRef(valueIndex);
  131. if (_hasEgid)
  132. SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID);
  133. toGroupCasted.Add(toEntityID.entityID, entity);
  134. }
  135. }
  136. }
  137. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  138. public void Clear()
  139. {
  140. if (IsUnmanaged)
  141. {
  142. implUnmgd.Clear();
  143. }
  144. else
  145. {
  146. implMgd.Clear();
  147. }
  148. }
  149. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  150. public void FastClear()
  151. {
  152. if (IsUnmanaged)
  153. {
  154. implUnmgd.FastClear();
  155. }
  156. else
  157. {
  158. implMgd.FastClear();
  159. }
  160. }
  161. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  162. public bool ContainsKey(uint egidEntityId)
  163. {
  164. if (IsUnmanaged)
  165. {
  166. return implUnmgd.ContainsKey(egidEntityId);
  167. }
  168. else
  169. {
  170. return implMgd.ContainsKey(egidEntityId);
  171. }
  172. }
  173. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  174. public ITypeSafeDictionary Create() { return TypeSafeDictionaryFactory<TValue>.Create(1); }
  175. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  176. public uint GetIndex(uint valueEntityId)
  177. {
  178. if (IsUnmanaged)
  179. {
  180. return this.implUnmgd.GetIndex(valueEntityId);
  181. }
  182. else
  183. {
  184. return this.implMgd.GetIndex(valueEntityId);
  185. }
  186. }
  187. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  188. public ref TValue GetOrCreate(uint idEntityId)
  189. {
  190. if (IsUnmanaged)
  191. {
  192. return ref this.implUnmgd.GetOrCreate(idEntityId);
  193. }
  194. else
  195. {
  196. return ref this.implMgd.GetOrCreate(idEntityId);
  197. }
  198. }
  199. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  200. public IBuffer<TValue> GetValues(out uint count)
  201. {
  202. if (IsUnmanaged)
  203. {
  204. return this.implUnmgd.GetValues(out count);
  205. }
  206. else
  207. {
  208. return this.implMgd.GetValues(out count);
  209. }
  210. }
  211. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  212. public ref TValue GetDirectValueByRef(uint key)
  213. {
  214. if (IsUnmanaged)
  215. {
  216. return ref this.implUnmgd.GetDirectValueByRef(key);
  217. }
  218. else
  219. {
  220. return ref this.implMgd.GetDirectValueByRef(key);
  221. }
  222. }
  223. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  224. public bool Has(uint key)
  225. {
  226. if (IsUnmanaged)
  227. {
  228. return this.implUnmgd.ContainsKey(key);
  229. }
  230. else
  231. {
  232. return this.implMgd.ContainsKey(key);
  233. }
  234. }
  235. public void ExecuteEnginesSwapOrRemoveCallbacks
  236. (EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup
  237. , FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, in PlatformProfiler profiler)
  238. {
  239. if (IsUnmanaged)
  240. {
  241. var valueIndex = this.implUnmgd.GetIndex(fromEntityGid.entityID);
  242. ref var entity = ref this.implUnmgd.GetDirectValueByRef(valueIndex);
  243. //move
  244. if (toGroup != null)
  245. {
  246. var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
  247. var previousGroup = fromEntityGid.groupID;
  248. if (_hasEgid)
  249. SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID.Value);
  250. var index = toGroupCasted.GetIndex(toEntityID.Value.entityID);
  251. ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(engines, ref toGroupCasted.GetDirectValueByRef(index)
  252. , previousGroup, in profiler, toEntityID.Value);
  253. }
  254. //remove
  255. else
  256. {
  257. ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref entity, in profiler, fromEntityGid);
  258. }
  259. }
  260. else
  261. {
  262. var valueIndex = this.implMgd.GetIndex(fromEntityGid.entityID);
  263. ref var entity = ref this.implMgd.GetDirectValueByRef(valueIndex);
  264. if (toGroup != null)
  265. {
  266. var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
  267. var previousGroup = fromEntityGid.groupID;
  268. if (_hasEgid)
  269. SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID.Value);
  270. var index = toGroupCasted.GetIndex(toEntityID.Value.entityID);
  271. ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(engines, ref toGroupCasted.GetDirectValueByRef(index)
  272. , previousGroup, in profiler, toEntityID.Value);
  273. }
  274. else
  275. {
  276. ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref entity, in profiler, fromEntityGid);
  277. }
  278. }
  279. }
  280. public void ExecuteEnginesRemoveCallbacks
  281. (FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, in PlatformProfiler profiler
  282. , ExclusiveGroupStruct group)
  283. {
  284. if (IsUnmanaged)
  285. {
  286. foreach (var value in implUnmgd)
  287. ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref implUnmgd.GetValueByRef(value.Key)
  288. , in profiler, new EGID(value.Key, group));
  289. }
  290. else
  291. {
  292. foreach (var value in implMgd)
  293. ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref implMgd.GetValueByRef(value.Key)
  294. , in profiler, new EGID(value.Key, group));
  295. }
  296. }
  297. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  298. public void RemoveEntityFromDictionary(EGID fromEntityGid)
  299. {
  300. if (IsUnmanaged)
  301. {
  302. this.implUnmgd.Remove(fromEntityGid.entityID);
  303. }
  304. else
  305. {
  306. this.implMgd.Remove(fromEntityGid.entityID);
  307. }
  308. }
  309. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  310. public void SetCapacity(uint size)
  311. {
  312. if (IsUnmanaged)
  313. {
  314. this.implUnmgd.SetCapacity(size);
  315. }
  316. else
  317. {
  318. this.implMgd.SetCapacity(size);
  319. }
  320. }
  321. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  322. public void Trim()
  323. {
  324. if (IsUnmanaged)
  325. {
  326. this.implUnmgd.Trim();
  327. }
  328. else
  329. {
  330. this.implMgd.Trim();
  331. }
  332. }
  333. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  334. public bool TryFindIndex(uint entityId, out uint index)
  335. {
  336. if (IsUnmanaged)
  337. {
  338. return implUnmgd.TryFindIndex(entityId, out index);
  339. }
  340. else
  341. {
  342. return implMgd.TryFindIndex(entityId, out index);
  343. }
  344. }
  345. public void KeysEvaluator(Action<uint> action)
  346. {
  347. if (IsUnmanaged)
  348. {
  349. foreach (var key in implUnmgd.keys)
  350. {
  351. action(key);
  352. }
  353. }
  354. else
  355. {
  356. foreach (var key in implMgd.keys)
  357. {
  358. action(key);
  359. }
  360. }
  361. }
  362. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  363. public bool TryGetValue(uint entityId, out TValue item)
  364. {
  365. if (IsUnmanaged)
  366. {
  367. return this.implUnmgd.TryGetValue(entityId, out item);
  368. }
  369. else
  370. {
  371. return this.implMgd.TryGetValue(entityId, out item);
  372. }
  373. }
  374. public uint count
  375. {
  376. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  377. get
  378. {
  379. if (IsUnmanaged)
  380. {
  381. return (uint) this.implUnmgd.count;
  382. }
  383. else
  384. {
  385. return (uint) this.implMgd.count;
  386. }
  387. }
  388. }
  389. public ref TValue this[uint idEntityId]
  390. {
  391. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  392. get
  393. {
  394. if (IsUnmanaged)
  395. {
  396. return ref this.implUnmgd.GetValueByRef(idEntityId);
  397. }
  398. else
  399. {
  400. return ref this.implMgd.GetValueByRef(idEntityId);
  401. }
  402. }
  403. }
  404. static void ExecuteEnginesRemoveCallbackOnSingleEntity
  405. (FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, ref TValue entity
  406. , in PlatformProfiler profiler, EGID egid)
  407. {
  408. if (!engines.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines))
  409. return;
  410. for (var i = 0; i < entityComponentsEngines.count; i++)
  411. try
  412. {
  413. using (profiler.Sample(entityComponentsEngines[i], _typeName))
  414. {
  415. (entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Remove(ref entity, egid);
  416. }
  417. }
  418. catch
  419. {
  420. Svelto.Console.LogError(
  421. "Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()));
  422. throw;
  423. }
  424. }
  425. void ExecuteEnginesAddOrSwapCallbacksOnSingleEntity
  426. (FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, ref TValue entity
  427. , ExclusiveGroupStruct? previousGroup, in PlatformProfiler profiler, EGID egid)
  428. {
  429. //get all the engines linked to TValue
  430. if (!engines.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines))
  431. return;
  432. if (previousGroup == null)
  433. {
  434. for (var i = 0; i < entityComponentsEngines.count; i++)
  435. try
  436. {
  437. using (profiler.Sample(entityComponentsEngines[i], _typeName))
  438. {
  439. (entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Add(ref entity, egid);
  440. }
  441. }
  442. catch
  443. {
  444. Svelto.Console.LogError(
  445. "Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()));
  446. throw;
  447. }
  448. }
  449. else
  450. {
  451. for (var i = 0; i < entityComponentsEngines.count; i++)
  452. try
  453. {
  454. using (profiler.Sample(entityComponentsEngines[i], _typeName))
  455. {
  456. (entityComponentsEngines[i] as IReactOnSwap<TValue>).MovedTo(
  457. ref entity, previousGroup.Value, egid);
  458. }
  459. }
  460. catch (Exception)
  461. {
  462. Svelto.Console.LogError(
  463. "Code crashed inside MoveTo callback ".FastConcat(typeof(TValue).ToString()));
  464. throw;
  465. }
  466. }
  467. }
  468. public void Dispose()
  469. {
  470. if (IsUnmanaged)
  471. implUnmgd.Dispose();
  472. else
  473. implMgd.Dispose();
  474. GC.SuppressFinalize(this);
  475. }
  476. }
  477. }