using System; using System.Collections.Generic; using Svelto.ECS; using Svelto.Tasks; using Svelto.Tasks.Lean; using TechbloxModdingAPI.Tasks; namespace TechbloxModdingAPI.Utility { public static 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 (obj.InitData.Valid) return ref obj.InitData.Initializer(id).GetOrAdd(); /*if (!obj.InitData.Valid) return ref opt.Get(); //Default value var init = obj.InitData.Initializer(id); // Do not create the component if missing, as that can trigger Add() listeners that, in some cases, may be // invalid if (ab)using the classes in an unusual way - TODO: Check entity descriptor or something if (init.Has()) return ref init.Get();*/ return ref opt.Get(); //Default value } private static readonly Dictionary Changes)> ChangesToPublish = new(); /// /// 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 /// Limits how many changes to publish - should be no more than the consumers' capacity that process this component /// The component that changed public static void PublishEntityChangeDelayed(this EntitiesDB entitiesDB, EGID id, int limit = 80) where T : unmanaged, IEntityComponent { //TODO: Doesn't seem to help if(!ChangesToPublish.ContainsKey(typeof(T))) ChangesToPublish.Add(typeof(T), (0, new HashSet())); var changes = ChangesToPublish[typeof(T)].Changes; if (changes.Contains(id)) return; changes.Add(id); PublishChanges(entitiesDB, id, limit).RunOn(Scheduler.leanRunner); } private static IEnumerator PublishChanges(EntitiesDB entitiesDB, EGID id, int limit) where T : unmanaged, IEntityComponent { yield return Yield.It; while (ChangesToPublish[typeof(T)].PublishedCount >= limit) yield return Yield.It; entitiesDB.PublishEntityChange(id); var (count, changes) = ChangesToPublish[typeof(T)]; changes.Remove(id); ChangesToPublish[typeof(T)] = (count + 1, changes); yield return Yield.It; ChangesToPublish[typeof(T)] = (0, changes); } } }