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.

128 lines
5.9KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Svelto.ECS;
  5. using Svelto.Tasks;
  6. using Svelto.Tasks.Lean;
  7. using TechbloxModdingAPI.Common;
  8. using TechbloxModdingAPI.Tasks;
  9. namespace TechbloxModdingAPI.Utility.ECS
  10. {
  11. public static partial class NativeApiExtensions
  12. {
  13. /// <summary>
  14. /// Attempts to query an entity and returns an optional that contains the result if succeeded.
  15. /// <b>This overload does not take initializer data into account.</b>
  16. /// </summary>
  17. /// <param name="entitiesDB">The entities DB</param>
  18. /// <param name="egid">The EGID to query</param>
  19. /// <typeparam name="T">The component type to query</typeparam>
  20. /// <returns>An optional that contains the result on success or is empty if not found</returns>
  21. public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EGID egid)
  22. where T : unmanaged, IEntityComponent
  23. {
  24. return entitiesDB.TryQueryEntitiesAndIndex<T>(egid, out uint index, out var array)
  25. ? new OptionalRef<T>(array, index)
  26. : new OptionalRef<T>();
  27. }
  28. /// <summary>
  29. /// Attempts to query an entity and returns the result in an optional reference.
  30. /// </summary>
  31. /// <param name="entitiesDB">The entities DB to query from</param>
  32. /// <param name="obj">The ECS object to query</param>
  33. /// <param name="group">The group of the entity if the object can have multiple</param>
  34. /// <typeparam name="T">The component to query</typeparam>
  35. /// <returns>A reference to the component or a dummy value</returns>
  36. public static OptionalRef<T> QueryEntityOptional<T>(this EntitiesDB entitiesDB, EcsObjectBase obj,
  37. ExclusiveGroupStruct group = default)
  38. where T : unmanaged, IEntityComponent
  39. {
  40. EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group);
  41. var opt = QueryEntityOptional<T>(entitiesDB, id);
  42. return opt ? opt : new OptionalRef<T>(obj, true);
  43. }
  44. /// <summary>
  45. /// Attempts to query an entity and returns the result or a dummy value that can be modified.
  46. /// </summary>
  47. /// <param name="entitiesDB">The entities DB to query from</param>
  48. /// <param name="obj">The ECS object to query</param>
  49. /// <param name="group">The group of the entity if the object can have multiple</param>
  50. /// <typeparam name="T">The component to query</typeparam>
  51. /// <returns>A reference to the component or a dummy value</returns>
  52. public static ref T QueryEntityOrDefault<T>(this EntitiesDB entitiesDB, EcsObjectBase obj,
  53. ExclusiveGroupStruct group = default)
  54. where T : unmanaged, IEntityComponent
  55. {
  56. EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group);
  57. var opt = QueryEntityOptional<T>(entitiesDB, id);
  58. if (opt) return ref opt.Get();
  59. // If initializing the entity, check if the component is allowed by the descriptor, otherwise it could cause
  60. // issues in the game with Add() calls running unexpectedly
  61. if (obj.InitData.Valid && obj.AllowedEntityComponents.Contains(typeof(T)))
  62. return ref obj.InitData.Initializer(id).GetOrAdd<T>();
  63. return ref opt.Get(); //Default value
  64. }
  65. /// <summary>
  66. /// Publishes an entity change, ignoring duplicate publishes and delaying changes as necessary.
  67. /// It will only publish in the next frame.
  68. /// </summary>
  69. /// <param name="entitiesDB">The entities DB to publish to</param>
  70. /// <param name="id">The ECS object that got changed</param>
  71. /// <typeparam name="T">The component that changed</typeparam>
  72. public static void PublishEntityChangeDelayed<T>(this EntitiesDB entitiesDB, EGID id)
  73. where T : unmanaged, IEntityComponent
  74. {
  75. PublishChanges<T>(entitiesDB, id).RunOn(Scheduler.leanRunner);
  76. }
  77. private static IEnumerator<TaskContract> PublishChanges<T>(EntitiesDB entitiesDB, EGID id)
  78. where T : unmanaged, IEntityComponent
  79. {
  80. yield return Yield.It;
  81. var entityStream = entitiesDB.GetEntityStream<T>();
  82. if (entityStream is null)
  83. yield break; // There is no entity stream for this type
  84. var consumers = entityStream.GetConsumers();
  85. if (consumers == null)
  86. {
  87. Console.WriteLine("Consumers is null");
  88. yield break;
  89. }
  90. bool waitForConsumers;
  91. do
  92. {
  93. waitForConsumers = false;
  94. for (int i = 0; i < consumers.count; i++)
  95. {
  96. var buffer = consumers[i].GetRingBuffer();
  97. if (buffer.Count + 1 <= buffer.Capacity) continue;
  98. waitForConsumers = true;
  99. break;
  100. }
  101. if (waitForConsumers) yield return Yield.It;
  102. } while (waitForConsumers);
  103. entitiesDB.PublishEntityChange<T>(id);
  104. }
  105. /// <summary>
  106. /// Query entities as OptionalRefs. The elements always exist, it's just a nice way to encapsulate the data.
  107. /// </summary>
  108. /// <param name="entitiesDB"></param>
  109. /// <param name="group"></param>
  110. /// <param name="select"></param>
  111. /// <typeparam name="T"></typeparam>
  112. /// <typeparam name="TR"></typeparam>
  113. /// <returns></returns>
  114. public static RefCollection<T> QueryEntitiesOptional<T>(this EntitiesDB entitiesDB, ExclusiveGroupStruct group) where T : unmanaged, IEntityComponent
  115. {
  116. var (buffer, ids, count) = entitiesDB.QueryEntities<T>(group);
  117. return new RefCollection<T>(count, buffer, ids, group);
  118. }
  119. }
  120. }