Improved and fixed publish queue detection and block test
- Made the PublishEntityChangesDelayed() method use the internals of Svelto.ECS to determine if it should wait -- I had this code for a while but it used too much reflection to my liking -- Now I made the reflection code nicer and a bit safer - Fixed float comparisons: last time I didn't actually used abs() for float3 but I found this even better method
This commit is contained in:
parent
b3b1e9b9e7
commit
67f32b8810
12 changed files with 106 additions and 47 deletions
|
@ -5,7 +5,6 @@ using System.Reflection;
|
||||||
|
|
||||||
using DataLoader;
|
using DataLoader;
|
||||||
using Svelto.Tasks;
|
using Svelto.Tasks;
|
||||||
using Svelto.Tasks.Enumerators;
|
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Tests;
|
using TechbloxModdingAPI.Tests;
|
||||||
|
@ -86,28 +85,44 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
//Includes specialised block properties
|
//Includes specialised block properties
|
||||||
if (property.SetMethod == null) continue;
|
if (property.SetMethod == null) continue;
|
||||||
var testValues = new (Type, object, Predicate<object>)[]
|
|
||||||
|
bool3 Float3Compare(float3 a, float3 b)
|
||||||
|
{ // From Unity reference code
|
||||||
|
return math.abs(b - a) < math.max(
|
||||||
|
0.000001f * math.max(math.abs(a), math.abs(b)),
|
||||||
|
float.Epsilon * 8
|
||||||
|
);
|
||||||
|
}
|
||||||
|
bool4 Float4Compare(float4 a, float4 b)
|
||||||
|
{ // From Unity reference code
|
||||||
|
return math.abs(b - a) < math.max(
|
||||||
|
0.000001f * math.max(math.abs(a), math.abs(b)),
|
||||||
|
float.Epsilon * 8
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var testValues = new (Type, object, Predicate<(object Value, object Default)>)[]
|
||||||
{
|
{
|
||||||
//(type, default value, predicate or null for equality)
|
//(type, default value, predicate or null for equality)
|
||||||
(typeof(long), 3, null),
|
(typeof(long), 3, null),
|
||||||
(typeof(int), 4, null),
|
(typeof(int), 4, null),
|
||||||
(typeof(double), 5.2f, obj => Math.Abs((double) obj - 5.2f) < float.Epsilon),
|
(typeof(double), 5.2f, t => Math.Abs((double) t.Value - (double) t.Default) < float.Epsilon),
|
||||||
(typeof(float), 5.2f, obj => Math.Abs((float) obj - 5.2f) < float.Epsilon),
|
(typeof(float), 5.2f, t => Math.Abs((float) t.Value - (float) t.Default) < float.Epsilon),
|
||||||
(typeof(bool), true, obj => (bool) obj),
|
(typeof(bool), true, t => (bool) t.Value),
|
||||||
(typeof(string), "Test", obj => (string) obj == "Test"), //String equality check
|
(typeof(string), "Test", t => (string) t.Value == "Test"), //String equality check
|
||||||
(typeof(float3), (float3) 2, obj => math.all((float3) obj - 2 < (float3) float.Epsilon)),
|
(typeof(float3), (float3) 20, t => math.all(Float3Compare((float3)t.Value, (float3)t.Default))),
|
||||||
(typeof(BlockColor), new BlockColor(BlockColors.Aqua, 2), null),
|
(typeof(BlockColor), new BlockColor(BlockColors.Aqua, 2), null),
|
||||||
(typeof(float4), (float4) 5, obj => math.all((float4) obj - 5 < (float4) float.Epsilon))
|
(typeof(float4), (float4) 5, t => math.all(Float4Compare((float4)t.Value, (float4)t.Default)))
|
||||||
};
|
};
|
||||||
var propType = property.PropertyType;
|
var propType = property.PropertyType;
|
||||||
if (!propType.IsValueType) continue;
|
if (!propType.IsValueType) continue;
|
||||||
(object valueToUse, Predicate<object> predicateToUse) = (null, null);
|
(object valueToUse, Predicate<(object Value, object Default)> predicateToUse) = (null, null);
|
||||||
foreach (var (type, value, predicate) in testValues)
|
foreach (var (type, value, predicate) in testValues)
|
||||||
{
|
{
|
||||||
if (type.IsAssignableFrom(propType))
|
if (type.IsAssignableFrom(propType))
|
||||||
{
|
{
|
||||||
valueToUse = value;
|
valueToUse = value;
|
||||||
predicateToUse = predicate ?? (obj => Equals(obj, value));
|
predicateToUse = predicate ?? (t => Equals(t.Value, t.Default));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +131,7 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
var values = propType.GetEnumValues();
|
var values = propType.GetEnumValues();
|
||||||
valueToUse = values.GetValue(values.Length / 2);
|
valueToUse = values.GetValue(values.Length / 2);
|
||||||
predicateToUse = val => Equals(val, valueToUse);
|
predicateToUse = t => Equals(t.Value, t.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valueToUse == null)
|
if (valueToUse == null)
|
||||||
|
@ -144,7 +159,7 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var attr = property.GetCustomAttribute<TestValueAttribute>();
|
var attr = property.GetCustomAttribute<TestValueAttribute>();
|
||||||
if (!predicateToUse(got) && (attr == null || !Equals(attr.PossibleValue, got)))
|
if (!predicateToUse((got, valueToUse)) && (attr == null || !Equals(attr.PossibleValue, got)))
|
||||||
{
|
{
|
||||||
Assert.Fail($"Property {block.GetType().Name}.{property.Name} value {got} does not equal {valueToUse} for block {block}.");
|
Assert.Fail($"Property {block.GetType().Name}.{property.Name} value {got} does not equal {valueToUse} for block {block}.");
|
||||||
yield break;
|
yield break;
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/*/// <summary> - TODO: Internal struct access
|
||||||
/// Gets or sets the Engine's On property. May not be saved.
|
/// Gets or sets the Engine's On property. May not be saved.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool On
|
public bool On
|
||||||
|
@ -377,6 +377,6 @@ namespace TechbloxModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).manualToAutoGearCoolOffTime = value;
|
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).manualToAutoGearCoolOffTime = value;
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ using Unity.Mathematics;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
using PrefabsID = RobocraftX.Common.PrefabsID;
|
using PrefabsID = RobocraftX.Common.PrefabsID;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks.Engines
|
namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
|
@ -125,7 +126,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
var skew = entitiesDB.QueryEntity<SkewComponent>(id);
|
var skew = entitiesDB.QueryEntity<SkewComponent>(id);
|
||||||
entitiesDB.QueryEntity<RenderingDataStruct>(id).matrix =
|
entitiesDB.QueryEntity<RenderingDataStruct>(id).matrix =
|
||||||
math.mul(float4x4.TRS(pos.position, rot.rotation, scale.scale), skew.skewMatrix);
|
math.mul(float4x4.TRS(pos.position, rot.rotation, scale.scale), skew.skewMatrix);
|
||||||
entitiesDB.PublishEntityChangeDelayed<GFXPrefabEntityStructGPUI>(id, 30); // Signal a prefab change so it updates the render buffers
|
entitiesDB.PublishEntityChangeDelayed<GFXPrefabEntityStructGPUI>(id); // Signal a prefab change so it updates the render buffers
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void UpdatePrefab(Block block, byte material, bool flipped)
|
internal void UpdatePrefab(Block block, byte material, bool flipped)
|
||||||
|
@ -146,8 +147,8 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
entitiesDB.QueryEntityOrDefault<GFXPrefabEntityStructGPUI>(block).prefabID = prefabId;
|
entitiesDB.QueryEntityOrDefault<GFXPrefabEntityStructGPUI>(block).prefabID = prefabId;
|
||||||
if (block.Exists)
|
if (block.Exists)
|
||||||
{
|
{
|
||||||
entitiesDB.PublishEntityChangeDelayed<CubeMaterialStruct>(block.Id, 30);
|
entitiesDB.PublishEntityChangeDelayed<CubeMaterialStruct>(block.Id);
|
||||||
entitiesDB.PublishEntityChangeDelayed<GFXPrefabEntityStructGPUI>(block.Id, 30);
|
entitiesDB.PublishEntityChangeDelayed<GFXPrefabEntityStructGPUI>(block.Id);
|
||||||
|
|
||||||
ref BuildingActionComponent local =
|
ref BuildingActionComponent local =
|
||||||
ref entitiesDB.QueryEntity<BuildingActionComponent>(BuildingDroneUtility
|
ref entitiesDB.QueryEntity<BuildingActionComponent>(BuildingDroneUtility
|
||||||
|
@ -161,7 +162,7 @@ namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
|
|
||||||
public void UpdateBlockColor(EGID id)
|
public void UpdateBlockColor(EGID id)
|
||||||
{
|
{
|
||||||
entitiesDB.PublishEntityChangeDelayed<ColourParameterEntityStruct>(id, 30);
|
entitiesDB.PublishEntityChangeDelayed<ColourParameterEntityStruct>(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool BlockExists(EGID blockID)
|
public bool BlockExists(EGID blockID)
|
||||||
|
|
|
@ -21,6 +21,7 @@ using Svelto.ECS.Serialization;
|
||||||
using Techblox.Blocks.Connections;
|
using Techblox.Blocks.Connections;
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
using Unity.Collections;
|
using Unity.Collections;
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
|
@ -7,6 +7,7 @@ using Unity.Transforms;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks.Engines
|
namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,6 +16,7 @@ using Unity.Mathematics;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks.Engines
|
namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,6 +7,7 @@ using UnityEngine;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks.Engines
|
namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,6 +6,7 @@ using Svelto.ECS;
|
||||||
|
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Blocks.Engines
|
namespace TechbloxModdingAPI.Blocks.Engines
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,7 @@ using Techblox.Character;
|
||||||
using TechbloxModdingAPI.Engines;
|
using TechbloxModdingAPI.Engines;
|
||||||
using TechbloxModdingAPI.Input;
|
using TechbloxModdingAPI.Input;
|
||||||
using TechbloxModdingAPI.Utility;
|
using TechbloxModdingAPI.Utility;
|
||||||
|
using TechbloxModdingAPI.Utility.ECS;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Players
|
namespace TechbloxModdingAPI.Players
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.Hybrid;
|
using Svelto.ECS.Hybrid;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Utility
|
namespace TechbloxModdingAPI.Utility.ECS
|
||||||
{
|
{
|
||||||
public static class ManagedApiExtensions
|
public static class ManagedApiExtensions
|
||||||
{
|
{
|
|
@ -0,0 +1,56 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using HarmonyLib;
|
||||||
|
using Svelto.DataStructures;
|
||||||
|
using Svelto.ECS;
|
||||||
|
|
||||||
|
namespace TechbloxModdingAPI.Utility.ECS
|
||||||
|
{
|
||||||
|
public static partial class NativeApiExtensions
|
||||||
|
{
|
||||||
|
[SuppressMessage("ReSharper", "StaticMemberInGenericType")]
|
||||||
|
private static class EntitiesDBHelper<T> where T : unmanaged, IEntityComponent
|
||||||
|
{ // Each type gets a new set of fields here (that's what the ReSharper warning is about too)
|
||||||
|
public static readonly Lazy<MethodInfo> EntityStream =
|
||||||
|
new(() => AccessTools.PropertyGetter(typeof(EntitiesDB), "_entityStream"));
|
||||||
|
|
||||||
|
public static readonly Lazy<FieldInfo> Streams = new(() =>
|
||||||
|
AccessTools.Field(EntityStream.Value.ReturnType, "_streams"));
|
||||||
|
|
||||||
|
public static readonly Lazy<FieldInfo> Consumers = new(() =>
|
||||||
|
AccessTools.Field(typeof(EntityStream<T>), "_consumers"));
|
||||||
|
|
||||||
|
public static readonly Lazy<MethodInfo> TryGetValue =
|
||||||
|
new(AccessTools.Method(Streams.Value.FieldType, "TryGetValue"));
|
||||||
|
|
||||||
|
public static readonly Lazy<FieldInfo> RingBuffer =
|
||||||
|
new(() => AccessTools.Field(typeof(Consumer<T>), "_ringBuffer"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static EntityStream<T> GetEntityStream<T>(this EntitiesDB entitiesDB) where T : unmanaged, IEntityComponent
|
||||||
|
{
|
||||||
|
// EntitiesStreams (internal)
|
||||||
|
var entitiesStreams = EntitiesDBHelper<T>.EntityStream.Value.Invoke(entitiesDB, Array.Empty<object>());
|
||||||
|
// FasterDictionary<RefWrapperType, ITypeSafeStream> (interface is internal)
|
||||||
|
var streams = EntitiesDBHelper<T>.Streams.Value.GetValue(entitiesStreams);
|
||||||
|
|
||||||
|
var parameters = new object[] { TypeRefWrapper<T>.wrapper, null };
|
||||||
|
var success = EntitiesDBHelper<T>.TryGetValue.Value.Invoke(streams, parameters);
|
||||||
|
if (!(bool)success)
|
||||||
|
return null; // There is no entity stream for this type
|
||||||
|
return (EntityStream<T>)parameters[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ThreadSafeFasterList<Consumer<T>> GetConsumers<T>(this EntityStream<T> stream) where T : unmanaged, IEntityComponent
|
||||||
|
{
|
||||||
|
return (ThreadSafeFasterList<Consumer<T>>)EntitiesDBHelper<T>.Consumers.Value.GetValue(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RingBuffer<(T, EGID)> GetRingBuffer<T>(this Consumer<T> consumer) where T : unmanaged, IEntityComponent
|
||||||
|
{
|
||||||
|
return (RingBuffer<(T, EGID)>)EntitiesDBHelper<T>.RingBuffer.Value.GetValue(consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,14 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
|
||||||
using HarmonyLib;
|
|
||||||
using Svelto.DataStructures;
|
using Svelto.DataStructures;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.Tasks;
|
using Svelto.Tasks;
|
||||||
using Svelto.Tasks.Lean;
|
using Svelto.Tasks.Lean;
|
||||||
using TechbloxModdingAPI.Tasks;
|
using TechbloxModdingAPI.Tasks;
|
||||||
|
|
||||||
namespace TechbloxModdingAPI.Utility
|
namespace TechbloxModdingAPI.Utility.ECS
|
||||||
{
|
{
|
||||||
public static class NativeApiExtensions
|
public static partial class NativeApiExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to query an entity and returns an optional that contains the result if succeeded.
|
/// Attempts to query an entity and returns an optional that contains the result if succeeded.
|
||||||
|
@ -69,36 +67,27 @@ namespace TechbloxModdingAPI.Utility
|
||||||
return ref opt.Get(); //Default value
|
return ref opt.Get(); //Default value
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Dictionary<Type, (int PublishedCount, HashSet<EGID> Changes)> ChangesToPublish = new();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Publishes an entity change, ignoring duplicate publishes and delaying changes as necessary.
|
/// Publishes an entity change, ignoring duplicate publishes and delaying changes as necessary.
|
||||||
/// It will only publish in the next frame.
|
/// It will only publish in the next frame.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="entitiesDB">The entities DB to publish to</param>
|
/// <param name="entitiesDB">The entities DB to publish to</param>
|
||||||
/// <param name="id">The ECS object that got changed</param>
|
/// <param name="id">The ECS object that got changed</param>
|
||||||
/// <param name="limit">Limits how many changes to publish - should be no more than the consumers' capacity that process this component</param>
|
|
||||||
/// <typeparam name="T">The component that changed</typeparam>
|
/// <typeparam name="T">The component that changed</typeparam>
|
||||||
public static void PublishEntityChangeDelayed<T>(this EntitiesDB entitiesDB, EGID id, int limit = 80)
|
public static void PublishEntityChangeDelayed<T>(this EntitiesDB entitiesDB, EGID id)
|
||||||
where T : unmanaged, IEntityComponent
|
where T : unmanaged, IEntityComponent
|
||||||
{
|
{
|
||||||
if (!ChangesToPublish.ContainsKey(typeof(T)))
|
PublishChanges<T>(entitiesDB, id).RunOn(Scheduler.leanRunner);
|
||||||
ChangesToPublish.Add(typeof(T), (0, new HashSet<EGID>()));
|
|
||||||
var changes = ChangesToPublish[typeof(T)].Changes;
|
|
||||||
if (changes.Contains(id)) return;
|
|
||||||
changes.Add(id);
|
|
||||||
PublishChanges<T>(entitiesDB, id, limit).RunOn(Scheduler.leanRunner);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerator<TaskContract> PublishChanges<T>(EntitiesDB entitiesDB, EGID id, int limit)
|
private static IEnumerator<TaskContract> PublishChanges<T>(EntitiesDB entitiesDB, EGID id)
|
||||||
where T : unmanaged, IEntityComponent
|
where T : unmanaged, IEntityComponent
|
||||||
{
|
{
|
||||||
yield return Yield.It;
|
yield return Yield.It;
|
||||||
while (ChangesToPublish[typeof(T)].PublishedCount >= limit)
|
var entityStream = entitiesDB.GetEntityStream<T>();
|
||||||
yield return Yield.It;
|
if (entityStream is null)
|
||||||
if (!entitiesDB._entityStream._streams.TryGetValue(TypeRefWrapper<T>.wrapper, out var result))
|
|
||||||
yield break; // There is no entity stream for this type
|
yield break; // There is no entity stream for this type
|
||||||
var consumers = (result as EntityStream<T>)?._consumers;
|
var consumers = entityStream.GetConsumers();
|
||||||
if (consumers == null)
|
if (consumers == null)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Consumers is null");
|
Console.WriteLine("Consumers is null");
|
||||||
|
@ -111,21 +100,15 @@ namespace TechbloxModdingAPI.Utility
|
||||||
waitForConsumers = false;
|
waitForConsumers = false;
|
||||||
for (int i = 0; i < consumers.count; i++)
|
for (int i = 0; i < consumers.count; i++)
|
||||||
{
|
{
|
||||||
var buffer = consumers[i]._ringBuffer;
|
var buffer = consumers[i].GetRingBuffer();
|
||||||
if (buffer.Count + 1 <= buffer.Capacity) continue;
|
if (buffer.Count + 1 <= buffer.Capacity) continue;
|
||||||
waitForConsumers = true;
|
waitForConsumers = true;
|
||||||
Console.WriteLine($"Gonna have to wait for a consumer (capacity: {buffer.Capacity} count: {buffer.Count}");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (waitForConsumers) yield return Yield.It;
|
if (waitForConsumers) yield return Yield.It;
|
||||||
} while (waitForConsumers);
|
} while (waitForConsumers);
|
||||||
entitiesDB.PublishEntityChange<T>(id);
|
entitiesDB.PublishEntityChange<T>(id);
|
||||||
var (count, changes) = ChangesToPublish[typeof(T)];
|
|
||||||
changes.Remove(id);
|
|
||||||
ChangesToPublish[typeof(T)] = (count + 1, changes);
|
|
||||||
yield return Yield.It;
|
|
||||||
ChangesToPublish[typeof(T)] = (Math.Max(ChangesToPublish[typeof(T)].PublishedCount - 1, 0), changes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
Loading…
Reference in a new issue