using System; using System.Collections.Generic; using System.Linq; using Svelto.ECS; using Svelto.Tasks; using Svelto.Tasks.Lean; using TechbloxModdingAPI.Common; using TechbloxModdingAPI.Tasks; namespace TechbloxModdingAPI.Utility.ECS { public static partial class NativeApiExtensions { /// /// Attempts to query an entity and returns an optional that contains the result if succeeded. /// This overload does not take initializer data into account. /// /// The entities DB /// The EGID to query /// The component type to query /// An optional that contains the result on success or is empty if not found public static OptionalRef QueryEntityOptional(this EntitiesDB entitiesDB, EGID egid) where T : unmanaged, IEntityComponent { return entitiesDB.TryQueryEntitiesAndIndex(egid, out uint index, out var array) ? new OptionalRef(array, index) : new OptionalRef(); } /// /// Attempts to query an entity and returns the result in an optional reference. /// /// The entities DB to query from /// The ECS object to query /// The group of the entity if the object can have multiple /// The component to query /// A reference to the component or a dummy value public static OptionalRef QueryEntityOptional(this EntitiesDB entitiesDB, EcsObjectBase obj, ExclusiveGroupStruct group = default) where T : unmanaged, IEntityComponent { EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group); var opt = QueryEntityOptional(entitiesDB, id); return opt ? opt : new OptionalRef(obj, true); } /// /// Attempts to query an entity and returns the result or a dummy value that can be modified. /// /// The entities DB to query from /// The ECS object to query /// The group of the entity if the object can have multiple /// The component to query /// A reference to the component or a dummy value public static ref T QueryEntityOrDefault(this EntitiesDB entitiesDB, EcsObjectBase obj, ExclusiveGroupStruct group = default) where T : unmanaged, IEntityComponent { EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group); var opt = QueryEntityOptional(entitiesDB, id); if (opt) return ref opt.Get(); // If initializing the entity, check if the component is allowed by the descriptor, otherwise it could cause // issues in the game with Add() calls running unexpectedly if (obj.InitData.Valid && obj.AllowedEntityComponents.Contains(typeof(T))) return ref obj.InitData.Initializer(id).GetOrAdd(); return ref opt.Get(); //Default value } /// /// Publishes an entity change, ignoring duplicate publishes and delaying changes as necessary. /// It will only publish in the next frame. /// /// The entities DB to publish to /// The ECS object that got changed /// The component that changed public static void PublishEntityChangeDelayed(this EntitiesDB entitiesDB, EGID id) where T : unmanaged, IEntityComponent { PublishChanges(entitiesDB, id).RunOn(Scheduler.leanRunner); } private static IEnumerator PublishChanges(EntitiesDB entitiesDB, EGID id) where T : unmanaged, IEntityComponent { yield return Yield.It; var entityStream = entitiesDB.GetEntityStream(); if (entityStream is null) yield break; // There is no entity stream for this type var consumers = entityStream.GetConsumers(); if (consumers == null) { Console.WriteLine("Consumers is null"); yield break; } bool waitForConsumers; do { waitForConsumers = false; for (int i = 0; i < consumers.count; i++) { var buffer = consumers[i].GetRingBuffer(); if (buffer.Count + 1 <= buffer.Capacity) continue; waitForConsumers = true; break; } if (waitForConsumers) yield return Yield.It; } while (waitForConsumers); entitiesDB.PublishEntityChange(id); } /// /// Query entities as OptionalRefs. The elements always exist, it's just a nice way to encapsulate the data. /// /// /// /// /// /// /// public static RefCollection QueryEntitiesOptional(this EntitiesDB entitiesDB, ExclusiveGroupStruct group) where T : unmanaged, IEntityComponent { var (buffer, ids, count) = entitiesDB.QueryEntities(group); return new RefCollection(count, buffer, ids, group); } } }