A stable modding interface between Techblox and mods https://mod.exmods.org/
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.

102 lines
5.3KB

  1. using System;
  2. using System.Collections.Generic;
  3. using Svelto.ECS;
  4. using Svelto.Tasks;
  5. using Svelto.Tasks.Lean;
  6. using TechbloxModdingAPI.Tasks;
  7. namespace TechbloxModdingAPI.Utility
  8. {
  9. public static class NativeApiExtensions
  10. {
  11. /// <summary>
  12. /// Attempts to query an entity and returns an optional that contains the result if succeeded.
  13. /// <b>This overload does not take initializer data into account.</b>
  14. /// </summary>
  15. /// <param name="entitiesDB">The entities DB</param>
  16. /// <param name="egid">The EGID to query</param>
  17. /// <typeparam name="T">The component type to query</typeparam>
  18. /// <returns>An optional that contains the result on success or is empty if not found</returns>
  19. public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EGID egid)
  20. where T : unmanaged, IEntityComponent
  21. {
  22. return entitiesDB.TryQueryEntitiesAndIndex<T>(egid, out uint index, out var array)
  23. ? new OptionalRef<T>(array, index)
  24. : new OptionalRef<T>();
  25. }
  26. /// <summary>
  27. /// Attempts to query an entity and returns the result in an optional reference.
  28. /// </summary>
  29. /// <param name="entitiesDB">The entities DB to query from</param>
  30. /// <param name="obj">The ECS object to query</param>
  31. /// <param name="group">The group of the entity if the object can have multiple</param>
  32. /// <typeparam name="T">The component to query</typeparam>
  33. /// <returns>A reference to the component or a dummy value</returns>
  34. public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EcsObjectBase obj, ExclusiveGroupStruct group = default)
  35. where T : unmanaged, IEntityComponent
  36. {
  37. EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group);
  38. var opt = QueryEntityOptional<T>(entitiesDB, id);
  39. return opt ? opt : new OptionalRef<T>(obj, true);
  40. }
  41. /// <summary>
  42. /// Attempts to query an entity and returns the result or a dummy value that can be modified.
  43. /// </summary>
  44. /// <param name="entitiesDB">The entities DB to query from</param>
  45. /// <param name="obj">The ECS object to query</param>
  46. /// <param name="group">The group of the entity if the object can have multiple</param>
  47. /// <typeparam name="T">The component to query</typeparam>
  48. /// <returns>A reference to the component or a dummy value</returns>
  49. public static ref T QueryEntityOrDefault<T>(this EntitiesDB entitiesDB, EcsObjectBase obj, ExclusiveGroupStruct group = default)
  50. where T : unmanaged, IEntityComponent
  51. {
  52. EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group);
  53. var opt = QueryEntityOptional<T>(entitiesDB, id);
  54. if (opt) return ref opt.Get();
  55. if (obj.InitData.Valid) return ref obj.InitData.Initializer(id).GetOrAdd<T>();
  56. /*if (!obj.InitData.Valid) return ref opt.Get(); //Default value
  57. var init = obj.InitData.Initializer(id);
  58. // Do not create the component if missing, as that can trigger Add() listeners that, in some cases, may be
  59. // invalid if (ab)using the classes in an unusual way - TODO: Check entity descriptor or something
  60. if (init.Has<T>()) return ref init.Get<T>();*/
  61. return ref opt.Get(); //Default value
  62. }
  63. private static readonly Dictionary<Type, (int PublishedCount, HashSet<EGID> Changes)> ChangesToPublish = new();
  64. /// <summary>
  65. /// Publishes an entity change, ignoring duplicate publishes and delaying changes as necessary.
  66. /// It will only publish in the next frame.
  67. /// </summary>
  68. /// <param name="entitiesDB">The entities DB to publish to</param>
  69. /// <param name="id">The ECS object that got changed</param>
  70. /// <param name="limit">Limits how many changes to publish - should be no more than the consumers' capacity that process this component</param>
  71. /// <typeparam name="T">The component that changed</typeparam>
  72. public static void PublishEntityChangeDelayed<T>(this EntitiesDB entitiesDB, EGID id, int limit = 80)
  73. where T : unmanaged, IEntityComponent
  74. { //TODO: Doesn't seem to help
  75. if(!ChangesToPublish.ContainsKey(typeof(T)))
  76. ChangesToPublish.Add(typeof(T), (0, new HashSet<EGID>()));
  77. var changes = ChangesToPublish[typeof(T)].Changes;
  78. if (changes.Contains(id)) return;
  79. changes.Add(id);
  80. PublishChanges<T>(entitiesDB, id, limit).RunOn(Scheduler.leanRunner);
  81. }
  82. private static IEnumerator<TaskContract> PublishChanges<T>(EntitiesDB entitiesDB, EGID id, int limit)
  83. where T : unmanaged, IEntityComponent
  84. {
  85. yield return Yield.It;
  86. while (ChangesToPublish[typeof(T)].PublishedCount >= limit)
  87. yield return Yield.It;
  88. entitiesDB.PublishEntityChange<T>(id);
  89. var (count, changes) = ChangesToPublish[typeof(T)];
  90. changes.Remove(id);
  91. ChangesToPublish[typeof(T)] = (count + 1, changes);
  92. yield return Yield.It;
  93. ChangesToPublish[typeof(T)] = (0, changes);
  94. }
  95. }
  96. }