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.

552 lines
25KB

  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using DBC.ECS;
  4. using Svelto.Common;
  5. using Svelto.DataStructures;
  6. namespace Svelto.ECS.Internal
  7. {
  8. public static class TypeSafeDictionaryMethods
  9. {
  10. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  11. public static void AddEntitiesToDictionary<Strategy1, Strategy2, Strategy3, TValue>(
  12. in SveltoDictionary<uint, TValue, Strategy1, Strategy2, Strategy3> fromDictionary
  13. , ITypeSafeDictionary<TValue> toDic
  14. #if SLOW_SVELTO_SUBMISSION
  15. , in EnginesRoot.EntityReferenceMap entityLocator
  16. #endif
  17. , ExclusiveGroupStruct toGroupID)
  18. where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>>
  19. where Strategy2 : struct, IBufferStrategy<TValue>
  20. where Strategy3 : struct, IBufferStrategy<int>
  21. where TValue : struct, _IInternalEntityComponent
  22. {
  23. foreach (var tuple in fromDictionary)
  24. {
  25. #if SLOW_SVELTO_SUBMISSION
  26. var egid = new EGID(tuple.key, toGroupID);
  27. if (SlowSubmissionInfo<TValue>.hasEgid)
  28. SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref tuple.value, egid);
  29. if (SlowSubmissionInfo<TValue>.hasReference)
  30. SetEGIDWithoutBoxing<TValue>.SetRefWithoutBoxing(
  31. ref tuple.value,
  32. entityLocator.GetEntityReference(egid));
  33. #endif
  34. try
  35. {
  36. toDic.Add(tuple.key, tuple.value);
  37. }
  38. catch (Exception e)
  39. {
  40. Console.LogException(
  41. e,
  42. "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString())
  43. .FastConcat(", group ").FastConcat(toGroupID.ToName()).FastConcat(", id ").FastConcat(tuple.key));
  44. throw;
  45. }
  46. #if PARANOID_CHECK && SLOW_SVELTO_SUBMISSION
  47. DBC.ECS.Check.Ensure(_hasEgid == false || ((INeedEGID)fromDictionary[egid.entityID]).ID == egid, "impossible situation happened during swap");
  48. #endif
  49. }
  50. }
  51. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  52. public static void ExecuteEnginesAddCallbacks<Strategy1, Strategy2, Strategy3, TValue>(
  53. ref SveltoDictionary<uint, TValue, Strategy1, Strategy2, Strategy3> fromDictionary
  54. , ITypeSafeDictionary<TValue> todic, ExclusiveGroupStruct togroup
  55. , FasterDictionary<ComponentID, FasterList<ReactEngineContainer<IReactOnAdd>>> entitycomponentenginesdb
  56. , in PlatformProfiler sampler)
  57. where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>>
  58. where Strategy2 : struct, IBufferStrategy<TValue>
  59. where Strategy3 : struct, IBufferStrategy<int>
  60. where TValue : struct, _IInternalEntityComponent
  61. {
  62. if (entitycomponentenginesdb.TryGetValue(
  63. ComponentTypeID<TValue>.id
  64. , out var entityComponentsEngines))
  65. {
  66. if (entityComponentsEngines.count == 0)
  67. return;
  68. var dictionaryKeyEnumerator = fromDictionary.unsafeKeys;
  69. var count = fromDictionary.count;
  70. for (var i = 0; i < count; ++i)
  71. try
  72. {
  73. var key = dictionaryKeyEnumerator[i].key;
  74. ref var entity = ref todic.GetValueByRef(key);
  75. var egid = new EGID(key, togroup);
  76. //get all the engines linked to TValue
  77. for (var j = 0; j < entityComponentsEngines.count; j++)
  78. using (sampler.Sample(entityComponentsEngines[j].name))
  79. {
  80. #pragma warning disable CS0612
  81. ((IReactOnAdd<TValue>)entityComponentsEngines[j].engine).Add(ref entity, egid);
  82. #pragma warning restore CS0612
  83. }
  84. }
  85. catch (Exception e)
  86. {
  87. Console.LogException(e, "Code crashed inside Add callback with Type ".FastConcat(TypeCache<TValue>.name));
  88. throw;
  89. }
  90. }
  91. }
  92. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  93. public static void ExecuteEnginesDisposeCallbacks_Group<Strategy1, Strategy2, Strategy3, TValue>(
  94. ref SveltoDictionary<uint, TValue, Strategy1, Strategy2, Strategy3> fromDictionary
  95. , FasterDictionary<ComponentID, FasterList<ReactEngineContainer<IReactOnDispose>>> allEngines
  96. , ExclusiveGroupStruct inGroup, in PlatformProfiler sampler)
  97. where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>>
  98. where Strategy2 : struct, IBufferStrategy<TValue>
  99. where Strategy3 : struct, IBufferStrategy<int>
  100. where TValue : struct, _IInternalEntityComponent
  101. {
  102. if (allEngines.TryGetValue(ComponentTypeID<TValue>.id, out var entityComponentsEngines)
  103. == false)
  104. return;
  105. for (var i = 0; i < entityComponentsEngines.count; i++)
  106. try
  107. {
  108. using (sampler.Sample(entityComponentsEngines[i].name))
  109. {
  110. foreach (var value in fromDictionary)
  111. {
  112. ref var entity = ref value.value;
  113. var egid = new EGID(value.key, inGroup);
  114. var reactOnRemove = (IReactOnDispose<TValue>)entityComponentsEngines[i].engine;
  115. reactOnRemove.Remove(ref entity, egid);
  116. }
  117. }
  118. }
  119. catch
  120. {
  121. Console.LogError("Code crashed inside Remove callback ".FastConcat(entityComponentsEngines[i].name));
  122. throw;
  123. }
  124. }
  125. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  126. public static void ExecuteEnginesRemoveCallbacks<Strategy1, Strategy2, Strategy3, TValue>(FasterList<(uint, string)> infostoprocess
  127. , ref SveltoDictionary<uint, TValue, Strategy1, Strategy2, Strategy3> fromDictionary
  128. , FasterDictionary<ComponentID, FasterList<ReactEngineContainer<IReactOnRemove>>> reactiveenginesremove
  129. , ExclusiveGroupStruct fromgroup, in PlatformProfiler profiler)
  130. where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>>
  131. where Strategy2 : struct, IBufferStrategy<TValue>
  132. where Strategy3 : struct, IBufferStrategy<int>
  133. where TValue : struct, _IInternalEntityComponent
  134. {
  135. if (reactiveenginesremove.TryGetValue(
  136. ComponentTypeID<TValue>.id
  137. , out var entityComponentsEngines))
  138. {
  139. if (entityComponentsEngines.count == 0)
  140. return;
  141. var iterations = infostoprocess.count;
  142. for (var i = 0; i < iterations; i++)
  143. {
  144. var (entityID, trace) = infostoprocess[i];
  145. try
  146. {
  147. ref var entity = ref fromDictionary.GetValueByRef(entityID);
  148. var egid = new EGID(entityID, fromgroup);
  149. for (var j = 0; j < entityComponentsEngines.count; j++)
  150. using (profiler.Sample(entityComponentsEngines[j].name))
  151. {
  152. #pragma warning disable CS0612
  153. ((IReactOnRemove<TValue>)entityComponentsEngines[j].engine).Remove(ref entity, egid);
  154. #pragma warning restore CS0612
  155. }
  156. }
  157. catch
  158. {
  159. var str = "Crash while executing Remove Entity callback on ".FastConcat(TypeCache<TValue>.name)
  160. .FastConcat(" from : ", trace);
  161. Console.LogError(str);
  162. throw;
  163. }
  164. }
  165. }
  166. }
  167. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  168. public static void ExecuteEnginesRemoveCallbacks_Group<Strategy1, Strategy2, Strategy3, TValue>(
  169. ref SveltoDictionary<uint, TValue, Strategy1, Strategy2, Strategy3> fromDictionary
  170. , ITypeSafeDictionary<TValue> typeSafeDictionary
  171. , FasterDictionary<ComponentID, FasterList<ReactEngineContainer<IReactOnRemove>>> reactiveenginesremove
  172. , FasterDictionary<ComponentID, FasterList<ReactEngineContainer<IReactOnRemoveEx>>> reactiveenginesremoveex
  173. , int count, IEntityIDs entityids, ExclusiveGroupStruct group, in PlatformProfiler sampler)
  174. where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>>
  175. where Strategy2 : struct, IBufferStrategy<TValue>
  176. where Strategy3 : struct, IBufferStrategy<int>
  177. where TValue : struct, _IInternalEntityComponent
  178. {
  179. if (reactiveenginesremove.TryGetValue(
  180. ComponentTypeID<TValue>.id
  181. , out var reactiveEnginesRemovePerType))
  182. {
  183. var enginesCount = reactiveEnginesRemovePerType.count;
  184. for (var i = 0; i < enginesCount; i++)
  185. try
  186. {
  187. foreach (var value in fromDictionary)
  188. {
  189. ref var entity = ref value.value;
  190. var egid = new EGID(value.key, group);
  191. using (sampler.Sample(reactiveEnginesRemovePerType[i].name))
  192. {
  193. #pragma warning disable CS0612
  194. ((IReactOnRemove<TValue>)reactiveEnginesRemovePerType[i].engine).Remove(
  195. #pragma warning restore CS0612
  196. ref entity, egid);
  197. }
  198. }
  199. }
  200. catch
  201. {
  202. Console.LogError("Code crashed inside Remove callback ".FastConcat(reactiveEnginesRemovePerType[i].name));
  203. throw;
  204. }
  205. }
  206. if (reactiveenginesremoveex.TryGetValue(
  207. ComponentTypeID<TValue>.id
  208. , out var reactiveEnginesRemoveExPerType))
  209. {
  210. var enginesCount = reactiveEnginesRemoveExPerType.count;
  211. for (var i = 0; i < enginesCount; i++)
  212. try
  213. {
  214. using (sampler.Sample(reactiveEnginesRemoveExPerType[i].name))
  215. {
  216. ((IReactOnRemoveEx<TValue>)reactiveEnginesRemoveExPerType[i].engine).Remove(
  217. (0, (uint)count)
  218. , new EntityCollection<TValue>(
  219. typeSafeDictionary.GetValues(out _), entityids
  220. , (uint)count), group);
  221. }
  222. }
  223. catch
  224. {
  225. Console.LogError("Code crashed inside Remove callback ".FastConcat(reactiveEnginesRemoveExPerType[i].name));
  226. throw;
  227. }
  228. }
  229. }
  230. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  231. public static void ExecuteEnginesSwapCallbacks<Strategy1, Strategy2, Strategy3, TValue>(FasterList<(uint, uint, string)> infostoprocess
  232. , ref SveltoDictionary<uint, TValue, Strategy1, Strategy2, Strategy3> fromDictionary
  233. , FasterList<ReactEngineContainer<IReactOnSwap>> reactiveenginesswap, ExclusiveGroupStruct togroup
  234. , ExclusiveGroupStruct fromgroup, in PlatformProfiler sampler)
  235. where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>>
  236. where Strategy2 : struct, IBufferStrategy<TValue>
  237. where Strategy3 : struct, IBufferStrategy<int>
  238. where TValue : struct, _IInternalEntityComponent
  239. {
  240. if (reactiveenginesswap.count == 0)
  241. return;
  242. var iterations = infostoprocess.count;
  243. for (var i = 0; i < iterations; i++)
  244. {
  245. var (fromEntityID, toEntityID, trace) = infostoprocess[i];
  246. try
  247. {
  248. ref var entityComponent = ref fromDictionary.GetValueByRef(fromEntityID);
  249. var newEgid = new EGID(toEntityID, togroup);
  250. for (var j = 0; j < reactiveenginesswap.count; j++)
  251. using (sampler.Sample(reactiveenginesswap[j].name))
  252. {
  253. #pragma warning disable CS0612
  254. ((IReactOnSwap<TValue>)reactiveenginesswap[j].engine).MovedTo(
  255. #pragma warning restore CS0612
  256. ref entityComponent, fromgroup, newEgid);
  257. }
  258. }
  259. catch
  260. {
  261. var str = "Crash while executing Swap Entity callback on ".FastConcat(TypeCache<TValue>.name)
  262. .FastConcat(" from : ", trace);
  263. Console.LogError(str);
  264. throw;
  265. }
  266. }
  267. }
  268. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  269. public static void ExecuteEnginesSwapCallbacks_Group<Strategy1, Strategy2, Strategy3, TValue>(
  270. ref SveltoDictionary<uint, TValue, Strategy1, Strategy2, Strategy3> fromDictionary
  271. , ITypeSafeDictionary<TValue> toDic, ExclusiveGroupStruct togroup, ExclusiveGroupStruct fromgroup
  272. , ITypeSafeDictionary<TValue> typeSafeDictionary
  273. , FasterDictionary<ComponentID, FasterList<ReactEngineContainer<IReactOnSwap>>> reactiveenginesswap
  274. , FasterDictionary<ComponentID, FasterList<ReactEngineContainer<IReactOnSwapEx>>> reactiveenginesswapex
  275. , int count, IEntityIDs entityids, in PlatformProfiler sampler)
  276. where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>>
  277. where Strategy2 : struct, IBufferStrategy<TValue>
  278. where Strategy3 : struct, IBufferStrategy<int>
  279. where TValue : struct, _IInternalEntityComponent
  280. {
  281. //get all the engines linked to TValue
  282. if (!reactiveenginesswap.TryGetValue(
  283. ComponentTypeID<TValue>.id
  284. , out var reactiveEnginesSwapPerType))
  285. return;
  286. var componentsEnginesCount = reactiveEnginesSwapPerType.count;
  287. for (var i = 0; i < componentsEnginesCount; i++)
  288. try
  289. {
  290. foreach (var value in fromDictionary)
  291. {
  292. ref var entityComponent = ref toDic.GetValueByRef(value.key);
  293. var newEgid = new EGID(value.key, togroup);
  294. using (sampler.Sample(reactiveEnginesSwapPerType[i].name))
  295. {
  296. #pragma warning disable CS0612
  297. ((IReactOnSwap<TValue>)reactiveEnginesSwapPerType[i].engine).MovedTo(
  298. #pragma warning restore CS0612
  299. ref entityComponent, fromgroup, newEgid);
  300. }
  301. }
  302. }
  303. catch (Exception)
  304. {
  305. Console.LogError("Code crashed inside MoveTo callback ".FastConcat(reactiveEnginesSwapPerType[i].name));
  306. throw;
  307. }
  308. if (reactiveenginesswapex.TryGetValue(
  309. ComponentTypeID<TValue>.id
  310. , out var reactiveEnginesRemoveExPerType))
  311. {
  312. var enginesCount = reactiveEnginesRemoveExPerType.count;
  313. for (var i = 0; i < enginesCount; i++)
  314. try
  315. {
  316. using (sampler.Sample(reactiveEnginesRemoveExPerType[i].name))
  317. {
  318. ((IReactOnSwapEx<TValue>)reactiveEnginesRemoveExPerType[i].engine).MovedTo(
  319. (0, (uint)count)
  320. , new EntityCollection<TValue>(
  321. typeSafeDictionary.GetValues(out _), entityids
  322. , (uint)count), fromgroup, togroup);
  323. }
  324. }
  325. catch
  326. {
  327. Console.LogError("Code crashed inside Remove callback ".FastConcat(reactiveEnginesRemoveExPerType[i].name));
  328. throw;
  329. }
  330. }
  331. }
  332. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  333. public static void RemoveEntitiesFromDictionary<Strategy1, Strategy2, Strategy3, TValue>(FasterList<(uint, string)> infostoprocess
  334. , ref SveltoDictionary<uint, TValue, Strategy1, Strategy2, Strategy3> fromDictionary
  335. , FasterList<uint> entityIDsAffectedByRemoval)
  336. where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>>
  337. where Strategy2 : struct, IBufferStrategy<TValue>
  338. where Strategy3 : struct, IBufferStrategy<int>
  339. where TValue : struct, _IInternalEntityComponent
  340. {
  341. var iterations = infostoprocess.count;
  342. for (var i = 0; i < iterations; i++)
  343. {
  344. var (id, trace) = infostoprocess[i];
  345. try
  346. {
  347. if (fromDictionary.Remove(id, out var index, out var value))
  348. {
  349. //Note I am doing this to be able to use a range of values even with the
  350. //remove Ex callbacks. Basically I am copying back the deleted value
  351. //at the end of the array, so I can use as range count, count + number of deleted entities
  352. //I need to swap the keys too to have matching EntityIDs
  353. fromDictionary.unsafeValues[(uint)fromDictionary.count] = value;
  354. fromDictionary.unsafeKeys[(uint)fromDictionary.count] = new SveltoDictionaryNode<uint>(ref id, 0);
  355. //when a component is removed from a component array, a remove swap back happens. This means
  356. //that not only we have to remove the index of the component of the entity deleted from the array
  357. //but we need also to update the index of the component that has been swapped in the cell
  358. //of the deleted component
  359. //entityIDsAffectedByRemoval tracks all the entitiesID of the components that need to be updated
  360. //in the filters because their indices in the array changed.
  361. entityIDsAffectedByRemoval.Add(fromDictionary.unsafeKeys[index].key);
  362. }
  363. }
  364. catch
  365. {
  366. var str = "Crash while executing Remove Entity operation on ".FastConcat(TypeCache<TValue>.name)
  367. .FastConcat(" from : ", trace);
  368. Console.LogError(str);
  369. throw;
  370. }
  371. }
  372. }
  373. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  374. public static void SwapEntitiesBetweenDictionaries<Strategy1, Strategy2, Strategy3, TValue>(FasterList<(uint, uint, string)> infostoprocess
  375. , ref SveltoDictionary<uint, TValue, Strategy1, Strategy2, Strategy3> fromDictionary
  376. , ITypeSafeDictionary<TValue> toDictionary, ExclusiveGroupStruct fromgroup, ExclusiveGroupStruct togroup
  377. , FasterList<uint> entityIDsAffectedByRemoval)
  378. where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>>
  379. where Strategy2 : struct, IBufferStrategy<TValue>
  380. where Strategy3 : struct, IBufferStrategy<int>
  381. where TValue : struct, _IInternalEntityComponent
  382. {
  383. var iterations = infostoprocess.count;
  384. for (var i = 0; i < iterations; i++)
  385. {
  386. var (fromID, toID, trace) = infostoprocess[i];
  387. try
  388. {
  389. var fromEntityGid = new EGID(fromID, fromgroup);
  390. var toEntityEgid = new EGID(toID, togroup);
  391. Check.Require(togroup.isInvalid == false, "Invalid To Group");
  392. if (fromDictionary.Remove(fromEntityGid.entityID, out var index, out var value))
  393. entityIDsAffectedByRemoval.Add(fromDictionary.unsafeKeys[index].key);
  394. else
  395. Check.Assert(false, "Swapping an entity that doesn't exist");
  396. #if SLOW_SVELTO_SUBMISSION
  397. if (SlowSubmissionInfo<TValue>.hasEgid)
  398. SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref value, toEntityEgid);
  399. #endif
  400. toDictionary.Add(toEntityEgid.entityID, value);
  401. #if PARANOID_CHECK
  402. DBC.ECS.Check.Ensure(_hasEgid == false || ((INeedEGID)toGroupCasted[toEntityEGID.entityID]).ID == toEntityEGID, "impossible situation happened during swap");
  403. #endif
  404. }
  405. catch
  406. {
  407. var str = "Crash while executing Swap Entity operation on ".FastConcat(TypeCache<TValue>.name)
  408. .FastConcat(" from : ", trace);
  409. Console.LogError(str);
  410. throw;
  411. }
  412. }
  413. }
  414. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  415. public static void ExecuteEnginesAddEntityCallbacksFast<TValue>(
  416. FasterDictionary<ComponentID, FasterList<ReactEngineContainer<IReactOnAddEx>>> fasterDictionary
  417. , ExclusiveGroupStruct groupId, (uint, uint) rangeTuple, IEntityIDs entityids
  418. , ITypeSafeDictionary<TValue> typeSafeDictionary, PlatformProfiler profiler)
  419. where TValue : struct, _IInternalEntityComponent
  420. {
  421. //get all the engines linked to TValue
  422. if (!fasterDictionary.TryGetValue(
  423. ComponentTypeID<TValue>.id
  424. , out var entityComponentsEngines))
  425. return;
  426. for (var i = 0; i < entityComponentsEngines.count; i++)
  427. try
  428. {
  429. using (profiler.Sample(entityComponentsEngines[i].name))
  430. {
  431. ((IReactOnAddEx<TValue>)entityComponentsEngines[i].engine).Add(
  432. rangeTuple
  433. , new EntityCollection<TValue>(typeSafeDictionary.GetValues(out var count), entityids, count)
  434. , groupId);
  435. }
  436. }
  437. catch (Exception e)
  438. {
  439. Console.LogException(e, "Code crashed inside Add callback ".FastConcat(entityComponentsEngines[i].name));
  440. throw;
  441. }
  442. }
  443. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  444. public static void ExecuteEnginesSwapCallbacksFast<TValue>(FasterList<ReactEngineContainer<IReactOnSwapEx>> fasterList,
  445. ExclusiveGroupStruct fromGroup
  446. , ExclusiveGroupStruct toGroup, IEntityIDs entityids, ITypeSafeDictionary<TValue> typeSafeDictionary
  447. , (uint, uint) rangeofsubmittedentitiesindicies, PlatformProfiler sampler)
  448. where TValue : struct, _IInternalEntityComponent
  449. {
  450. for (var i = 0; i < fasterList.count; i++)
  451. try
  452. {
  453. using (sampler.Sample(fasterList[i].name))
  454. {
  455. ((IReactOnSwapEx<TValue>)fasterList[i].engine).MovedTo(
  456. rangeofsubmittedentitiesindicies
  457. , new EntityCollection<TValue>(typeSafeDictionary.GetValues(out var count), entityids, count)
  458. , fromGroup, toGroup);
  459. }
  460. }
  461. catch (Exception e)
  462. {
  463. Console.LogException(e, "Code crashed inside Add callback ".FastConcat(fasterList[i].name));
  464. throw;
  465. }
  466. }
  467. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  468. public static void ExecuteEnginesRemoveCallbacksFast<TValue>(FasterList<ReactEngineContainer<IReactOnRemoveEx>> fasterList,
  469. ExclusiveGroupStruct exclusiveGroupStruct
  470. , (uint, uint) valueTuple, IEntityIDs entityids, ITypeSafeDictionary<TValue> typeSafeDictionary
  471. , PlatformProfiler sampler)
  472. where TValue : struct, _IInternalEntityComponent
  473. {
  474. for (var i = 0; i < fasterList.count; i++)
  475. try
  476. {
  477. using (sampler.Sample(fasterList[i].name))
  478. {
  479. ((IReactOnRemoveEx<TValue>)fasterList[i].engine).Remove(
  480. valueTuple
  481. , new EntityCollection<TValue>(typeSafeDictionary.GetValues(out var count), entityids, count)
  482. , exclusiveGroupStruct);
  483. }
  484. }
  485. catch (Exception e)
  486. {
  487. Console.LogException(e, "Code crashed inside Add callback ".FastConcat(fasterList[i].name));
  488. throw;
  489. }
  490. }
  491. }
  492. }