饭太稀
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Pool;
|
||||
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
namespace Fantasy.Async
|
||||
{
|
||||
/// <summary>
|
||||
/// 协程锁专用的对象池
|
||||
/// </summary>
|
||||
public sealed class CoroutineLockPool : PoolCore<CoroutineLock>
|
||||
{
|
||||
/// <summary>
|
||||
/// 协程锁专用的对象池的构造函数
|
||||
/// </summary>
|
||||
public CoroutineLockPool() : base(2000) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 协程锁
|
||||
/// </summary>
|
||||
public sealed class CoroutineLock : IPool, IDisposable
|
||||
{
|
||||
private Scene _scene;
|
||||
private CoroutineLockComponent _coroutineLockComponent;
|
||||
private readonly Dictionary<long, CoroutineLockQueue> _queue = new Dictionary<long, CoroutineLockQueue>();
|
||||
/// <summary>
|
||||
/// 表示是否是对象池中创建的
|
||||
/// </summary>
|
||||
private bool _isPool;
|
||||
/// <summary>
|
||||
/// 协程锁的类型
|
||||
/// </summary>
|
||||
public long CoroutineLockType { get; private set; }
|
||||
|
||||
internal void Initialize(CoroutineLockComponent coroutineLockComponent, ref long coroutineLockType)
|
||||
{
|
||||
_scene = coroutineLockComponent.Scene;
|
||||
CoroutineLockType = coroutineLockType;
|
||||
_coroutineLockComponent = coroutineLockComponent;
|
||||
}
|
||||
/// <summary>
|
||||
/// 销毁协程锁,如果调用了该方法,所有使用当前协程锁等待的逻辑会按照顺序释放锁。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var (_, coroutineLockQueue) in _queue)
|
||||
{
|
||||
while (TryCoroutineLockQueueDequeue(coroutineLockQueue)) { }
|
||||
}
|
||||
|
||||
_queue.Clear();
|
||||
_scene = null;
|
||||
CoroutineLockType = 0;
|
||||
_coroutineLockComponent = null;
|
||||
}
|
||||
/// <summary>
|
||||
/// 等待上一个任务完成
|
||||
/// </summary>
|
||||
/// <param name="coroutineLockQueueKey">需要等待的Id</param>
|
||||
/// <param name="tag">用于查询协程锁的标记,可不传入,只有在超时的时候排查是哪个锁超时时使用</param>
|
||||
/// <param name="timeOut">等待多久会超时,当到达设定的时候会把当前锁给按照超时处理</param>
|
||||
/// <returns></returns>
|
||||
public async FTask<WaitCoroutineLock> Wait(long coroutineLockQueueKey, string tag = null, int timeOut = 30000)
|
||||
{
|
||||
var waitCoroutineLock = _coroutineLockComponent.WaitCoroutineLockPool.Rent(this, ref coroutineLockQueueKey, tag, timeOut);
|
||||
|
||||
if (!_queue.TryGetValue(coroutineLockQueueKey, out var queue))
|
||||
{
|
||||
queue = _coroutineLockComponent.CoroutineLockQueuePool.Rent();
|
||||
_queue.Add(coroutineLockQueueKey, queue);
|
||||
return waitCoroutineLock;
|
||||
}
|
||||
|
||||
queue.Enqueue(waitCoroutineLock);
|
||||
return await waitCoroutineLock.Tcs;
|
||||
}
|
||||
/// <summary>
|
||||
/// 按照先入先出的顺序,释放最早的一个协程锁
|
||||
/// </summary>
|
||||
/// <param name="coroutineLockQueueKey"></param>
|
||||
public void Release(long coroutineLockQueueKey)
|
||||
{
|
||||
if (!_queue.TryGetValue(coroutineLockQueueKey, out var coroutineLockQueue))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryCoroutineLockQueueDequeue(coroutineLockQueue))
|
||||
{
|
||||
_queue.Remove(coroutineLockQueueKey);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryCoroutineLockQueueDequeue(CoroutineLockQueue coroutineLockQueue)
|
||||
{
|
||||
if (!coroutineLockQueue.TryDequeue(out var waitCoroutineLock))
|
||||
{
|
||||
_coroutineLockComponent.CoroutineLockQueuePool.Return(coroutineLockQueue);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (waitCoroutineLock.TimerId != 0)
|
||||
{
|
||||
_scene.TimerComponent.Net.Remove(waitCoroutineLock.TimerId);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 放到下一帧执行,如果不这样会导致逻辑的顺序不正常。
|
||||
_scene.ThreadSynchronizationContext.Post(waitCoroutineLock.SetResult);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"Error in disposing CoroutineLock: {e}");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Entitas;
|
||||
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
|
||||
namespace Fantasy.Async
|
||||
{
|
||||
/// <summary>
|
||||
/// 协程锁组件
|
||||
/// </summary>
|
||||
public class CoroutineLockComponent : Entity
|
||||
{
|
||||
private long _lockId;
|
||||
private CoroutineLockPool _coroutineLockPool;
|
||||
internal WaitCoroutineLockPool WaitCoroutineLockPool { get; private set; }
|
||||
internal CoroutineLockQueuePool CoroutineLockQueuePool { get; private set; }
|
||||
private readonly Dictionary<long, CoroutineLock> _coroutineLocks = new Dictionary<long, CoroutineLock>();
|
||||
internal CoroutineLockComponent Initialize()
|
||||
{
|
||||
_coroutineLockPool = new CoroutineLockPool();
|
||||
CoroutineLockQueuePool = new CoroutineLockQueuePool();
|
||||
WaitCoroutineLockPool = new WaitCoroutineLockPool(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
internal long LockId => ++_lockId;
|
||||
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
public override void Dispose()
|
||||
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_lockId = 0;
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的协程锁
|
||||
/// 使用这个方法创建的协程锁,需要手动释放管理CoroutineLock。
|
||||
/// 不会再CoroutineLockComponent理进行管理。
|
||||
/// </summary>
|
||||
/// <param name="coroutineLockType"></param>
|
||||
/// <returns></returns>
|
||||
public CoroutineLock Create(long coroutineLockType)
|
||||
{
|
||||
var coroutineLock = _coroutineLockPool.Rent();
|
||||
coroutineLock.Initialize(this, ref coroutineLockType);
|
||||
return coroutineLock;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求一个协程锁。
|
||||
/// 使用这个方法创建的协程锁,会自动释放CoroutineLockQueueType。
|
||||
/// </summary>
|
||||
/// <param name="coroutineLockType">锁类型</param>
|
||||
/// <param name="coroutineLockQueueKey">锁队列Id</param>
|
||||
/// <param name="tag">当某些锁超时,需要一个标记来方便排查问题,正常的情况下这个默认为null就可以。</param>
|
||||
/// <param name="time">设置锁的超时时间,让超过设置的时间会触发超时,保证锁不会因为某一个锁一直不解锁导致卡住的问题。</param>
|
||||
/// <returns>
|
||||
/// 返回的WaitCoroutineLock通过Dispose来解除这个锁、建议用using来保住这个锁。
|
||||
/// 也可以返回的WaitCoroutineLock通过CoroutineLockComponent.UnLock来解除这个锁。
|
||||
/// </returns>
|
||||
public FTask<WaitCoroutineLock> Wait(long coroutineLockType, long coroutineLockQueueKey, string tag = null, int time = 30000)
|
||||
{
|
||||
if (!_coroutineLocks.TryGetValue(coroutineLockType, out var coroutineLock))
|
||||
{
|
||||
coroutineLock = _coroutineLockPool.Rent();
|
||||
coroutineLock.Initialize(this, ref coroutineLockType);
|
||||
_coroutineLocks.Add(coroutineLockType, coroutineLock);
|
||||
}
|
||||
|
||||
return coroutineLock.Wait(coroutineLockQueueKey, tag, time);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解除一个协程锁。
|
||||
/// </summary>
|
||||
/// <param name="coroutineLockType"></param>
|
||||
/// <param name="coroutineLockQueueKey"></param>
|
||||
public void Release(int coroutineLockType, long coroutineLockQueueKey)
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_coroutineLocks.TryGetValue(coroutineLockType, out var coroutineLock))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
coroutineLock.Release(coroutineLockQueueKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Pool;
|
||||
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
namespace Fantasy.Async
|
||||
{
|
||||
internal sealed class CoroutineLockQueuePool : PoolCore<CoroutineLockQueue>
|
||||
{
|
||||
public CoroutineLockQueuePool() : base(2000) { }
|
||||
}
|
||||
|
||||
internal sealed class CoroutineLockQueue : Queue<WaitCoroutineLock>, IPool
|
||||
{
|
||||
private bool _isPool;
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using Fantasy.Event;
|
||||
using Fantasy.Pool;
|
||||
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
namespace Fantasy.Async
|
||||
{
|
||||
internal sealed class WaitCoroutineLockPool : PoolCore<WaitCoroutineLock>
|
||||
{
|
||||
private readonly Scene _scene;
|
||||
private readonly CoroutineLockComponent _coroutineLockComponent;
|
||||
|
||||
public WaitCoroutineLockPool(CoroutineLockComponent coroutineLockComponent) : base(2000)
|
||||
{
|
||||
_scene = coroutineLockComponent.Scene;
|
||||
_coroutineLockComponent = coroutineLockComponent;
|
||||
}
|
||||
|
||||
public WaitCoroutineLock Rent(CoroutineLock coroutineLock, ref long coroutineLockQueueKey, string tag = null, int timeOut = 30000)
|
||||
{
|
||||
var timerId = 0L;
|
||||
var lockId = _coroutineLockComponent.LockId;
|
||||
var waitCoroutineLock = _coroutineLockComponent.WaitCoroutineLockPool.Rent();
|
||||
|
||||
if (timeOut > 0)
|
||||
{
|
||||
timerId = _scene.TimerComponent.Net.OnceTimer(timeOut, new CoroutineLockTimeout(ref lockId, waitCoroutineLock));
|
||||
}
|
||||
|
||||
waitCoroutineLock.Initialize(coroutineLock, this, ref coroutineLockQueueKey, ref timerId, ref lockId, tag);
|
||||
return waitCoroutineLock;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct CoroutineLockTimeout
|
||||
{
|
||||
public readonly long LockId;
|
||||
public readonly WaitCoroutineLock WaitCoroutineLock;
|
||||
|
||||
public CoroutineLockTimeout(ref long lockId, WaitCoroutineLock waitCoroutineLock)
|
||||
{
|
||||
LockId = lockId;
|
||||
WaitCoroutineLock = waitCoroutineLock;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class OnCoroutineLockTimeout : EventSystem<CoroutineLockTimeout>
|
||||
{
|
||||
protected override void Handler(CoroutineLockTimeout self)
|
||||
{
|
||||
var selfWaitCoroutineLock = self.WaitCoroutineLock;
|
||||
|
||||
if (self.LockId != selfWaitCoroutineLock.LockId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Error($"coroutine lock timeout CoroutineLockQueueType:{selfWaitCoroutineLock.CoroutineLock.CoroutineLockType} Key:{selfWaitCoroutineLock.CoroutineLockQueueKey} Tag:{selfWaitCoroutineLock.Tag}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 一个协程锁的实例,用户可以用过这个手动释放锁
|
||||
/// </summary>
|
||||
public sealed class WaitCoroutineLock : IPool, IDisposable
|
||||
{
|
||||
private bool _isPool;
|
||||
internal string Tag { get; private set; }
|
||||
internal long LockId { get; private set; }
|
||||
internal long TimerId { get; private set; }
|
||||
internal long CoroutineLockQueueKey { get; private set; }
|
||||
internal CoroutineLock CoroutineLock { get; private set; }
|
||||
|
||||
private bool _isSetResult;
|
||||
private FTask<WaitCoroutineLock> _tcs;
|
||||
private WaitCoroutineLockPool _waitCoroutineLockPool;
|
||||
internal void Initialize(CoroutineLock coroutineLock, WaitCoroutineLockPool waitCoroutineLockPool, ref long coroutineLockQueueKey, ref long timerId, ref long lockId, string tag)
|
||||
{
|
||||
Tag = tag;
|
||||
LockId = lockId;
|
||||
TimerId = timerId;
|
||||
CoroutineLock = coroutineLock;
|
||||
CoroutineLockQueueKey = coroutineLockQueueKey;
|
||||
_waitCoroutineLockPool = waitCoroutineLockPool;
|
||||
}
|
||||
/// <summary>
|
||||
/// 释放协程锁
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (LockId == 0)
|
||||
{
|
||||
Log.Error("WaitCoroutineLock is already disposed");
|
||||
return;
|
||||
}
|
||||
|
||||
CoroutineLock.Release(CoroutineLockQueueKey);
|
||||
|
||||
_tcs = null;
|
||||
Tag = null;
|
||||
LockId = 0;
|
||||
TimerId = 0;
|
||||
_isSetResult = false;
|
||||
CoroutineLockQueueKey = 0;
|
||||
_waitCoroutineLockPool.Return(this);
|
||||
CoroutineLock = null;
|
||||
_waitCoroutineLockPool = null;
|
||||
}
|
||||
|
||||
internal FTask<WaitCoroutineLock> Tcs
|
||||
{
|
||||
get { return _tcs ??= FTask<WaitCoroutineLock>.Create(); }
|
||||
}
|
||||
|
||||
internal void SetResult()
|
||||
{
|
||||
if (_isSetResult)
|
||||
{
|
||||
Log.Error("WaitCoroutineLock is already SetResult");
|
||||
return;
|
||||
}
|
||||
|
||||
_isSetResult = true;
|
||||
Tcs.SetResult(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,471 @@
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
using Fantasy.Assembly;
|
||||
using Fantasy.Async;
|
||||
using Fantasy.DataStructure.Collection;
|
||||
using Fantasy.Entitas;
|
||||
using Fantasy.Entitas.Interface;
|
||||
using Fantasy.Helper;
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
#pragma warning disable CS8765 // Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes).
|
||||
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
namespace Fantasy.Entitas
|
||||
{
|
||||
internal sealed class UpdateQueueInfo
|
||||
{
|
||||
public bool IsStop;
|
||||
public readonly Type Type;
|
||||
public readonly long RunTimeId;
|
||||
|
||||
public UpdateQueueInfo(Type type, long runTimeId)
|
||||
{
|
||||
Type = type;
|
||||
IsStop = false;
|
||||
RunTimeId = runTimeId;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class FrameUpdateQueueInfo
|
||||
{
|
||||
public readonly Type Type;
|
||||
public readonly long RunTimeId;
|
||||
|
||||
public FrameUpdateQueueInfo(Type type, long runTimeId)
|
||||
{
|
||||
Type = type;
|
||||
RunTimeId = runTimeId;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct CustomEntitiesSystemKey : IEquatable<CustomEntitiesSystemKey>
|
||||
{
|
||||
public int CustomEventType { get; }
|
||||
public Type EntitiesType { get; }
|
||||
public CustomEntitiesSystemKey(int customEventType, Type entitiesType)
|
||||
{
|
||||
CustomEventType = customEventType;
|
||||
EntitiesType = entitiesType;
|
||||
}
|
||||
public bool Equals(CustomEntitiesSystemKey other)
|
||||
{
|
||||
return CustomEventType == other.CustomEventType && EntitiesType == other.EntitiesType;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is CustomEntitiesSystemKey other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(CustomEventType, EntitiesType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entity管理组件
|
||||
/// </summary>
|
||||
public sealed class EntityComponent : Entity, ISceneUpdate, IAssembly
|
||||
{
|
||||
private readonly OneToManyList<long, Type> _assemblyList = new();
|
||||
private readonly OneToManyList<long, Type> _assemblyHashCodes = new();
|
||||
|
||||
private readonly Dictionary<Type, IAwakeSystem> _awakeSystems = new();
|
||||
private readonly Dictionary<Type, IUpdateSystem> _updateSystems = new();
|
||||
private readonly Dictionary<Type, IDestroySystem> _destroySystems = new();
|
||||
private readonly Dictionary<Type, IDeserializeSystem> _deserializeSystems = new();
|
||||
private readonly Dictionary<Type, IFrameUpdateSystem> _frameUpdateSystem = new();
|
||||
|
||||
private readonly OneToManyList<long, CustomEntitiesSystemKey> _assemblyCustomSystemList = new();
|
||||
private readonly Dictionary<CustomEntitiesSystemKey, ICustomEntitiesSystem> _customEntitiesSystems = new Dictionary<CustomEntitiesSystemKey, ICustomEntitiesSystem>();
|
||||
|
||||
private readonly Dictionary<Type, long> _hashCodes = new Dictionary<Type, long>();
|
||||
private readonly Queue<UpdateQueueInfo> _updateQueue = new Queue<UpdateQueueInfo>();
|
||||
private readonly Queue<FrameUpdateQueueInfo> _frameUpdateQueue = new Queue<FrameUpdateQueueInfo>();
|
||||
private readonly Dictionary<long, UpdateQueueInfo> _updateQueueDic = new Dictionary<long, UpdateQueueInfo>();
|
||||
|
||||
internal async FTask<EntityComponent> Initialize()
|
||||
{
|
||||
await AssemblySystem.Register(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
#region Assembly
|
||||
|
||||
public FTask Load(long assemblyIdentity)
|
||||
{
|
||||
var task = FTask.Create(false);
|
||||
Scene?.ThreadSynchronizationContext.Post(() =>
|
||||
{
|
||||
LoadInner(assemblyIdentity);
|
||||
task.SetResult();
|
||||
});
|
||||
return task;
|
||||
}
|
||||
|
||||
public FTask ReLoad(long assemblyIdentity)
|
||||
{
|
||||
var task = FTask.Create(false);
|
||||
Scene?.ThreadSynchronizationContext.Post(() =>
|
||||
{
|
||||
OnUnLoadInner(assemblyIdentity);
|
||||
LoadInner(assemblyIdentity);
|
||||
task.SetResult();
|
||||
});
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
public FTask OnUnLoad(long assemblyIdentity)
|
||||
{
|
||||
var task = FTask.Create(false);
|
||||
Scene?.ThreadSynchronizationContext.Post(() =>
|
||||
{
|
||||
OnUnLoadInner(assemblyIdentity);
|
||||
task.SetResult();
|
||||
});
|
||||
return task;
|
||||
}
|
||||
|
||||
private void LoadInner(long assemblyIdentity)
|
||||
{
|
||||
foreach (var entityType in AssemblySystem.ForEach(assemblyIdentity, typeof(IEntity)))
|
||||
{
|
||||
_hashCodes.Add(entityType, HashCodeHelper.ComputeHash64(entityType.FullName));
|
||||
_assemblyHashCodes.Add(assemblyIdentity, entityType);
|
||||
}
|
||||
|
||||
foreach (var entitiesSystemType in AssemblySystem.ForEach(assemblyIdentity, typeof(IEntitiesSystem)))
|
||||
{
|
||||
Type entitiesType = null;
|
||||
var entity = Activator.CreateInstance(entitiesSystemType);
|
||||
|
||||
switch (entity)
|
||||
{
|
||||
case IAwakeSystem iAwakeSystem:
|
||||
{
|
||||
entitiesType = iAwakeSystem.EntitiesType();
|
||||
_awakeSystems.Add(entitiesType, iAwakeSystem);
|
||||
break;
|
||||
}
|
||||
case IDestroySystem iDestroySystem:
|
||||
{
|
||||
entitiesType = iDestroySystem.EntitiesType();
|
||||
_destroySystems.Add(entitiesType, iDestroySystem);
|
||||
break;
|
||||
}
|
||||
case IDeserializeSystem iDeserializeSystem:
|
||||
{
|
||||
entitiesType = iDeserializeSystem.EntitiesType();
|
||||
_deserializeSystems.Add(entitiesType, iDeserializeSystem);
|
||||
break;
|
||||
}
|
||||
case IUpdateSystem iUpdateSystem:
|
||||
{
|
||||
entitiesType = iUpdateSystem.EntitiesType();
|
||||
_updateSystems.Add(entitiesType, iUpdateSystem);
|
||||
break;
|
||||
}
|
||||
case IFrameUpdateSystem iFrameUpdateSystem:
|
||||
{
|
||||
entitiesType = iFrameUpdateSystem.EntitiesType();
|
||||
_frameUpdateSystem.Add(entitiesType, iFrameUpdateSystem);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Log.Error($"IEntitiesSystem not support type {entitiesSystemType}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_assemblyList.Add(assemblyIdentity, entitiesType);
|
||||
}
|
||||
|
||||
foreach (var customEntitiesSystemType in AssemblySystem.ForEach(assemblyIdentity, typeof(ICustomEntitiesSystem)))
|
||||
{
|
||||
var entity = (ICustomEntitiesSystem)Activator.CreateInstance(customEntitiesSystemType);
|
||||
var customEntitiesSystemKey = new CustomEntitiesSystemKey(entity.CustomEventType, entity.EntitiesType());
|
||||
_customEntitiesSystems.Add(customEntitiesSystemKey, entity);
|
||||
_assemblyCustomSystemList.Add(assemblyIdentity, customEntitiesSystemKey);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUnLoadInner(long assemblyIdentity)
|
||||
{
|
||||
if (_assemblyHashCodes.TryGetValue(assemblyIdentity, out var entityType))
|
||||
{
|
||||
foreach (var type in entityType)
|
||||
{
|
||||
_hashCodes.Remove(type);
|
||||
}
|
||||
|
||||
_assemblyHashCodes.RemoveByKey(assemblyIdentity);
|
||||
}
|
||||
|
||||
if (_assemblyList.TryGetValue(assemblyIdentity, out var assembly))
|
||||
{
|
||||
foreach (var type in assembly)
|
||||
{
|
||||
_awakeSystems.Remove(type);
|
||||
_updateSystems.Remove(type);
|
||||
_destroySystems.Remove(type);
|
||||
_deserializeSystems.Remove(type);
|
||||
_frameUpdateSystem.Remove(type);
|
||||
}
|
||||
|
||||
_assemblyList.RemoveByKey(assemblyIdentity);
|
||||
}
|
||||
|
||||
if (_assemblyCustomSystemList.TryGetValue(assemblyIdentity, out var customSystemAssembly))
|
||||
{
|
||||
foreach (var customEntitiesSystemKey in customSystemAssembly)
|
||||
{
|
||||
_customEntitiesSystems.Remove(customEntitiesSystemKey);
|
||||
}
|
||||
|
||||
_assemblyCustomSystemList.RemoveByKey(assemblyIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event
|
||||
|
||||
/// <summary>
|
||||
/// 触发实体的唤醒方法
|
||||
/// </summary>
|
||||
/// <param name="entity">实体对象</param>
|
||||
public void Awake(Entity entity)
|
||||
{
|
||||
if (!_awakeSystems.TryGetValue(entity.Type, out var awakeSystem))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
awakeSystem.Invoke(entity);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"{entity.Type.FullName} Error {e}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发实体的销毁方法
|
||||
/// </summary>
|
||||
/// <param name="entity">实体对象</param>
|
||||
public void Destroy(Entity entity)
|
||||
{
|
||||
if (!_destroySystems.TryGetValue(entity.Type, out var system))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
system.Invoke(entity);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"{entity.Type.FullName} Destroy Error {e}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发实体的反序列化方法
|
||||
/// </summary>
|
||||
/// <param name="entity">实体对象</param>
|
||||
public void Deserialize(Entity entity)
|
||||
{
|
||||
if (!_deserializeSystems.TryGetValue(entity.Type, out var system))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
system.Invoke(entity);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"{entity.Type.FullName} Deserialize Error {e}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CustomEvent
|
||||
|
||||
public void CustomSystem(Entity entity, int customEventType)
|
||||
{
|
||||
var customEntitiesSystemKey = new CustomEntitiesSystemKey(customEventType, entity.Type);
|
||||
|
||||
if (!_customEntitiesSystems.TryGetValue(customEntitiesSystemKey, out var system))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
system.Invoke(entity);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"{entity.Type.FullName} CustomSystem Error {e}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Update
|
||||
|
||||
/// <summary>
|
||||
/// 将实体加入更新队列,准备进行更新
|
||||
/// </summary>
|
||||
/// <param name="entity">实体对象</param>
|
||||
public void StartUpdate(Entity entity)
|
||||
{
|
||||
var type = entity.Type;
|
||||
var entityRuntimeId = entity.RuntimeId;
|
||||
|
||||
if (_updateSystems.ContainsKey(type))
|
||||
{
|
||||
var updateQueueInfo = new UpdateQueueInfo(type, entityRuntimeId);
|
||||
_updateQueue.Enqueue(updateQueueInfo);
|
||||
_updateQueueDic.Add(entityRuntimeId, updateQueueInfo);
|
||||
}
|
||||
|
||||
if (_frameUpdateSystem.ContainsKey(type))
|
||||
{
|
||||
_frameUpdateQueue.Enqueue(new FrameUpdateQueueInfo(type, entityRuntimeId));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止实体进行更新
|
||||
/// </summary>
|
||||
/// <param name="entity">实体对象</param>
|
||||
public void StopUpdate(Entity entity)
|
||||
{
|
||||
if (!_updateQueueDic.Remove(entity.RuntimeId, out var updateQueueInfo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
updateQueueInfo.IsStop = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行实体系统的更新逻辑
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
var updateQueueCount = _updateQueue.Count;
|
||||
|
||||
while (updateQueueCount-- > 0)
|
||||
{
|
||||
var updateQueueStruct = _updateQueue.Dequeue();
|
||||
|
||||
if (updateQueueStruct.IsStop)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_updateSystems.TryGetValue(updateQueueStruct.Type, out var updateSystem))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var entity = Scene.GetEntity(updateQueueStruct.RunTimeId);
|
||||
|
||||
if (entity == null || entity.IsDisposed)
|
||||
{
|
||||
_updateQueueDic.Remove(updateQueueStruct.RunTimeId);
|
||||
continue;
|
||||
}
|
||||
|
||||
_updateQueue.Enqueue(updateQueueStruct);
|
||||
|
||||
try
|
||||
{
|
||||
updateSystem.Invoke(entity);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"{updateQueueStruct.Type.FullName} Update Error {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行实体系统的帧更新逻辑
|
||||
/// </summary>
|
||||
public void FrameUpdate()
|
||||
{
|
||||
var count = _frameUpdateQueue.Count;
|
||||
|
||||
while (count-- > 0)
|
||||
{
|
||||
var frameUpdateQueueStruct = _frameUpdateQueue.Dequeue();
|
||||
|
||||
if (!_frameUpdateSystem.TryGetValue(frameUpdateQueueStruct.Type, out var frameUpdateSystem))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var entity = Scene.GetEntity(frameUpdateQueueStruct.RunTimeId);
|
||||
|
||||
if (entity == null || entity.IsDisposed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_frameUpdateQueue.Enqueue(frameUpdateQueueStruct);
|
||||
|
||||
try
|
||||
{
|
||||
frameUpdateSystem.Invoke(entity);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"{frameUpdateQueueStruct.Type.FullName} FrameUpdate Error {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public long GetHashCode(Type type)
|
||||
{
|
||||
return _hashCodes[type];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放实体系统管理器资源
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
_updateQueue.Clear();
|
||||
_frameUpdateQueue.Clear();
|
||||
|
||||
_assemblyList.Clear();
|
||||
_awakeSystems.Clear();
|
||||
_updateSystems.Clear();
|
||||
_destroySystems.Clear();
|
||||
_deserializeSystems.Clear();
|
||||
_frameUpdateSystem.Clear();
|
||||
|
||||
AssemblySystem.UnRegister(this);
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Fantasy.Assembly;
|
||||
using Fantasy.Async;
|
||||
using Fantasy.DataStructure.Collection;
|
||||
using Fantasy.Entitas;
|
||||
|
||||
// ReSharper disable PossibleMultipleEnumeration
|
||||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
// ReSharper disable MethodOverloadWithOptionalParameter
|
||||
|
||||
namespace Fantasy.Event
|
||||
{
|
||||
internal sealed class EventCache
|
||||
{
|
||||
public readonly Type EnventType;
|
||||
public readonly object Obj;
|
||||
public EventCache(Type enventType, object obj)
|
||||
{
|
||||
EnventType = enventType;
|
||||
Obj = obj;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class EventComponent : Entity, IAssembly
|
||||
{
|
||||
private readonly OneToManyList<Type, IEvent> _events = new();
|
||||
private readonly OneToManyList<Type, IAsyncEvent> _asyncEvents = new();
|
||||
private readonly OneToManyList<long, EventCache> _assemblyEvents = new();
|
||||
private readonly OneToManyList<long, EventCache> _assemblyAsyncEvents = new();
|
||||
|
||||
internal async FTask<EventComponent> Initialize()
|
||||
{
|
||||
await AssemblySystem.Register(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
#region Assembly
|
||||
|
||||
public async FTask Load(long assemblyIdentity)
|
||||
{
|
||||
var tcs = FTask.Create(false);
|
||||
Scene?.ThreadSynchronizationContext.Post(() =>
|
||||
{
|
||||
LoadInner(assemblyIdentity);
|
||||
tcs.SetResult();
|
||||
});
|
||||
await tcs;
|
||||
}
|
||||
|
||||
public async FTask ReLoad(long assemblyIdentity)
|
||||
{
|
||||
var tcs = FTask.Create(false);
|
||||
Scene?.ThreadSynchronizationContext.Post(() =>
|
||||
{
|
||||
OnUnLoadInner(assemblyIdentity);
|
||||
LoadInner(assemblyIdentity);
|
||||
tcs.SetResult();
|
||||
});
|
||||
await tcs;
|
||||
}
|
||||
|
||||
public async FTask OnUnLoad(long assemblyIdentity)
|
||||
{
|
||||
var tcs = FTask.Create(false);
|
||||
Scene?.ThreadSynchronizationContext.Post(() =>
|
||||
{
|
||||
OnUnLoadInner(assemblyIdentity);
|
||||
tcs.SetResult();
|
||||
});
|
||||
await tcs;
|
||||
}
|
||||
|
||||
private void LoadInner(long assemblyIdentity)
|
||||
{
|
||||
foreach (var type in AssemblySystem.ForEach(assemblyIdentity, typeof(IEvent)))
|
||||
{
|
||||
var @event = (IEvent)Activator.CreateInstance(type);
|
||||
|
||||
if (@event == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var eventType = @event.EventType();
|
||||
_events.Add(eventType, @event);
|
||||
_assemblyEvents.Add(assemblyIdentity, new EventCache(eventType, @event));
|
||||
}
|
||||
|
||||
foreach (var type in AssemblySystem.ForEach(assemblyIdentity, typeof(IAsyncEvent)))
|
||||
{
|
||||
var @event = (IAsyncEvent)Activator.CreateInstance(type);
|
||||
|
||||
if (@event == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var eventType = @event.EventType();
|
||||
_asyncEvents.Add(eventType, @event);
|
||||
_assemblyAsyncEvents.Add(assemblyIdentity, new EventCache(eventType, @event));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUnLoadInner(long assemblyIdentity)
|
||||
{
|
||||
if (_assemblyEvents.TryGetValue(assemblyIdentity, out var events))
|
||||
{
|
||||
foreach (var @event in events)
|
||||
{
|
||||
_events.RemoveValue(@event.EnventType, (IEvent)@event.Obj);
|
||||
}
|
||||
|
||||
_assemblyEvents.RemoveByKey(assemblyIdentity);
|
||||
}
|
||||
|
||||
if (_assemblyAsyncEvents.TryGetValue(assemblyIdentity, out var asyncEvents))
|
||||
{
|
||||
foreach (var @event in asyncEvents)
|
||||
{
|
||||
_asyncEvents.RemoveValue(@event.EnventType, (IAsyncEvent)@event.Obj);
|
||||
}
|
||||
|
||||
_assemblyAsyncEvents.RemoveByKey(assemblyIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Publish
|
||||
|
||||
/// <summary>
|
||||
/// 发布一个值类型的事件数据。
|
||||
/// </summary>
|
||||
/// <typeparam name="TEventData">事件数据类型(值类型)。</typeparam>
|
||||
/// <param name="eventData">事件数据实例。</param>
|
||||
public void Publish<TEventData>(TEventData eventData) where TEventData : struct
|
||||
{
|
||||
if (!_events.TryGetValue(typeof(TEventData), out var list))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var @event in list)
|
||||
{
|
||||
try
|
||||
{
|
||||
@event.Invoke(eventData);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发布一个继承自 Entity 的事件数据。
|
||||
/// </summary>
|
||||
/// <typeparam name="TEventData">事件数据类型(继承自 Entity)。</typeparam>
|
||||
/// <param name="eventData">事件数据实例。</param>
|
||||
/// <param name="isDisposed">是否释放事件数据。</param>
|
||||
public void Publish<TEventData>(TEventData eventData, bool isDisposed = true) where TEventData : Entity
|
||||
{
|
||||
if (!_events.TryGetValue(typeof(TEventData), out var list))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var @event in list)
|
||||
{
|
||||
try
|
||||
{
|
||||
@event.Invoke(eventData);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (isDisposed)
|
||||
{
|
||||
eventData.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步发布一个值类型的事件数据。
|
||||
/// </summary>
|
||||
/// <typeparam name="TEventData">事件数据类型(值类型)。</typeparam>
|
||||
/// <param name="eventData">事件数据实例。</param>
|
||||
/// <returns>表示异步操作的任务。</returns>
|
||||
public async FTask PublishAsync<TEventData>(TEventData eventData) where TEventData : struct
|
||||
{
|
||||
if (!_asyncEvents.TryGetValue(typeof(TEventData), out var list))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var tasks = ListPool<FTask>.Create();
|
||||
|
||||
foreach (var @event in list)
|
||||
{
|
||||
tasks.Add(@event.InvokeAsync(eventData));
|
||||
}
|
||||
|
||||
await FTask.WaitAll(tasks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步发布一个继承自 Entity 的事件数据。
|
||||
/// </summary>
|
||||
/// <typeparam name="TEventData">事件数据类型(继承自 Entity)。</typeparam>
|
||||
/// <param name="eventData">事件数据实例。</param>
|
||||
/// <param name="isDisposed">是否释放事件数据。</param>
|
||||
/// <returns>表示异步操作的任务。</returns>
|
||||
public async FTask PublishAsync<TEventData>(TEventData eventData, bool isDisposed = true) where TEventData : Entity
|
||||
{
|
||||
if (!_asyncEvents.TryGetValue(eventData.GetType(), out var list))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var tasks = ListPool<FTask>.Create();
|
||||
|
||||
foreach (var @event in list)
|
||||
{
|
||||
tasks.Add(@event.InvokeAsync(eventData));
|
||||
}
|
||||
|
||||
await FTask.WaitAll(tasks);
|
||||
|
||||
if (isDisposed)
|
||||
{
|
||||
eventData.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_events.Clear();
|
||||
_asyncEvents.Clear();
|
||||
_assemblyEvents.Clear();
|
||||
_assemblyAsyncEvents.Clear();
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using Fantasy.Async;
|
||||
|
||||
namespace Fantasy.Event
|
||||
{
|
||||
/// <summary>
|
||||
/// 事件的接口
|
||||
/// </summary>
|
||||
public interface IEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// 用于指定事件的Type
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Type EventType();
|
||||
/// <summary>
|
||||
/// 时间内部使用的入口
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
void Invoke(object self);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步事件的接口
|
||||
/// </summary>
|
||||
public interface IAsyncEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IEvent.EventType"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Type EventType();
|
||||
/// <summary>
|
||||
/// <see cref="IEvent.Invoke"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
FTask InvokeAsync(object self);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 事件的抽象类,要使用事件必须要继承这个抽象接口。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要监听的事件泛型类型</typeparam>
|
||||
public abstract class EventSystem<T> : IEvent
|
||||
{
|
||||
private readonly Type _selfType = typeof(T);
|
||||
/// <summary>
|
||||
/// <see cref="IEvent.EventType"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Type EventType()
|
||||
{
|
||||
return _selfType;
|
||||
}
|
||||
/// <summary>
|
||||
/// 事件调用的方法,要在这个方法里编写事件发生的逻辑
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
protected abstract void Handler(T self);
|
||||
/// <summary>
|
||||
/// <see cref="IEvent.Invoke"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void Invoke(object self)
|
||||
{
|
||||
try
|
||||
{
|
||||
Handler((T) self);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"{_selfType.Name} Error {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 异步事件的抽象类,要使用事件必须要继承这个抽象接口。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要监听的事件泛型类型</typeparam>
|
||||
public abstract class AsyncEventSystem<T> : IAsyncEvent
|
||||
{
|
||||
private readonly Type _selfType = typeof(T);
|
||||
/// <summary>
|
||||
/// <see cref="IEvent.EventType"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Type EventType()
|
||||
{
|
||||
return _selfType;
|
||||
}
|
||||
/// <summary>
|
||||
/// 事件调用的方法,要在这个方法里编写事件发生的逻辑
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
protected abstract FTask Handler(T self);
|
||||
/// <summary>
|
||||
/// <see cref="IEvent.Invoke"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async FTask InvokeAsync(object self)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Handler((T) self);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"{_selfType.Name} Error {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Fantasy.DataStructure.Collection;
|
||||
using Fantasy.Entitas;
|
||||
using Fantasy.Pool;
|
||||
using Fantasy.Serialize;
|
||||
|
||||
namespace Fantasy.Entitas
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息的对象池组件
|
||||
/// </summary>
|
||||
public sealed class MessagePoolComponent : Entity
|
||||
{
|
||||
private int _poolCount;
|
||||
private const int MaxCapacity = ushort.MaxValue;
|
||||
private readonly OneToManyQueue<Type, AMessage> _poolQueue = new OneToManyQueue<Type, AMessage>();
|
||||
private readonly Dictionary<Type, Func<AMessage>> _typeCheckCache = new Dictionary<Type, Func<AMessage>>();
|
||||
/// <summary>
|
||||
/// 销毁组件
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
_poolCount = 0;
|
||||
_poolQueue.Clear();
|
||||
_typeCheckCache.Clear();
|
||||
base.Dispose();
|
||||
}
|
||||
/// <summary>
|
||||
/// 从对象池里获取一个消息,如果没有就创建一个新的
|
||||
/// </summary>
|
||||
/// <typeparam name="T">消息的泛型类型</typeparam>
|
||||
/// <returns></returns>
|
||||
public T Rent<T>() where T : AMessage, new()
|
||||
{
|
||||
if (!_poolQueue.TryDequeue(typeof(T), out var queue))
|
||||
{
|
||||
var instance = new T();
|
||||
instance.SetScene(Scene);
|
||||
instance.SetIsPool(true);
|
||||
return instance;
|
||||
}
|
||||
|
||||
queue.SetIsPool(true);
|
||||
_poolCount--;
|
||||
return (T)queue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="Rent"/>
|
||||
/// </summary>
|
||||
/// <param name="type">消息的类型</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotSupportedException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public AMessage Rent(Type type)
|
||||
{
|
||||
if (!_poolQueue.TryDequeue(type, out var queue))
|
||||
{
|
||||
if (!_typeCheckCache.TryGetValue(type, out var createInstance))
|
||||
{
|
||||
if (!typeof(AMessage).IsAssignableFrom(type))
|
||||
{
|
||||
throw new NotSupportedException($"{this.GetType().FullName} Type:{type.FullName} must inherit from IPool");
|
||||
}
|
||||
else
|
||||
{
|
||||
createInstance = CreateInstance.CreateMessage(type);
|
||||
_typeCheckCache[type] = createInstance;
|
||||
}
|
||||
}
|
||||
|
||||
var instance = createInstance();
|
||||
instance.SetScene(Scene);
|
||||
instance.SetIsPool(true);
|
||||
return instance;
|
||||
}
|
||||
|
||||
queue.SetIsPool(true);
|
||||
_poolCount--;
|
||||
return queue;
|
||||
}
|
||||
/// <summary>
|
||||
/// 返还一个消息到对象池中
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
public void Return(AMessage obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!obj.IsPool())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_poolCount >= MaxCapacity)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_poolCount++;
|
||||
obj.SetIsPool(false);
|
||||
_poolQueue.Enqueue(obj.GetType(), obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="Return"/>
|
||||
/// </summary>
|
||||
/// <param name="obj">返还的消息</param>
|
||||
/// <typeparam name="T">返还的消息泛型类型</typeparam>
|
||||
public void Return<T>(T obj) where T : AMessage
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!obj.IsPool())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_poolCount >= MaxCapacity)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_poolCount++;
|
||||
obj.SetIsPool(false);
|
||||
_poolQueue.Enqueue(typeof(T), obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// ReSharper disable SuspiciousTypeConversion.Global
|
||||
|
||||
using Fantasy.Assembly;
|
||||
using Fantasy.Async;
|
||||
using Fantasy.DataStructure.Collection;
|
||||
using Fantasy.Entitas;
|
||||
using Fantasy.Entitas.Interface;
|
||||
using Fantasy.Helper;
|
||||
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
#if FANTASY_NET
|
||||
namespace Fantasy.SingleCollection
|
||||
{
|
||||
/// <summary>
|
||||
/// 用于处理Entity下的实体进行数据库分表存储的组件
|
||||
/// </summary>
|
||||
public sealed class SingleCollectionComponent : Entity, IAssembly
|
||||
{
|
||||
private CoroutineLock _coroutineLock;
|
||||
private readonly OneToManyHashSet<Type, string> _collection = new OneToManyHashSet<Type, string>();
|
||||
|
||||
private readonly OneToManyList<long, SingleCollectionInfo> _assemblyCollections =
|
||||
new OneToManyList<long, SingleCollectionInfo>();
|
||||
|
||||
private sealed class SingleCollectionInfo(Type rootType, string collectionName)
|
||||
{
|
||||
public readonly Type RootType = rootType;
|
||||
public readonly string CollectionName = collectionName;
|
||||
}
|
||||
|
||||
internal async FTask<SingleCollectionComponent> Initialize()
|
||||
{
|
||||
var coroutineLockType = HashCodeHelper.ComputeHash64(GetType().FullName);
|
||||
_coroutineLock = Scene.CoroutineLockComponent.Create(coroutineLockType);
|
||||
await AssemblySystem.Register(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
#region Assembly
|
||||
|
||||
public async FTask Load(long assemblyIdentity)
|
||||
{
|
||||
var tcs = FTask.Create(false);
|
||||
Scene?.ThreadSynchronizationContext.Post(() =>
|
||||
{
|
||||
LoadInner(assemblyIdentity);
|
||||
tcs.SetResult();
|
||||
});
|
||||
await tcs;
|
||||
}
|
||||
|
||||
public async FTask ReLoad(long assemblyIdentity)
|
||||
{
|
||||
var tcs = FTask.Create(false);
|
||||
Scene?.ThreadSynchronizationContext.Post(() =>
|
||||
{
|
||||
OnUnLoadInner(assemblyIdentity);
|
||||
LoadInner(assemblyIdentity);
|
||||
tcs.SetResult();
|
||||
});
|
||||
await tcs;
|
||||
}
|
||||
|
||||
public async FTask OnUnLoad(long assemblyIdentity)
|
||||
{
|
||||
var tcs = FTask.Create(false);
|
||||
Scene?.ThreadSynchronizationContext.Post(() =>
|
||||
{
|
||||
OnUnLoadInner(assemblyIdentity);
|
||||
tcs.SetResult();
|
||||
});
|
||||
await tcs;
|
||||
}
|
||||
|
||||
private void LoadInner(long assemblyIdentity)
|
||||
{
|
||||
foreach (var type in AssemblySystem.ForEach(assemblyIdentity, typeof(ISupportedSingleCollection)))
|
||||
{
|
||||
var customAttributes = type.GetCustomAttributes(typeof(SingleCollectionAttribute), false);
|
||||
if (customAttributes.Length == 0)
|
||||
{
|
||||
Log.Error(
|
||||
$"type {type.FullName} Implemented the interface of ISingleCollection, requiring the implementation of SingleCollectionAttribute");
|
||||
continue;
|
||||
}
|
||||
|
||||
var singleCollectionAttribute = (SingleCollectionAttribute)customAttributes[0];
|
||||
var rootType = singleCollectionAttribute.RootType;
|
||||
var collectionName = singleCollectionAttribute.CollectionName;
|
||||
_collection.Add(rootType, collectionName);
|
||||
_assemblyCollections.Add(assemblyIdentity, new SingleCollectionInfo(rootType, collectionName));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUnLoadInner(long assemblyIdentity)
|
||||
{
|
||||
if (!_assemblyCollections.TryGetValue(assemblyIdentity, out var types))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var singleCollectionInfo in types)
|
||||
{
|
||||
_collection.RemoveValue(singleCollectionInfo.RootType, singleCollectionInfo.CollectionName);
|
||||
}
|
||||
|
||||
_assemblyCollections.RemoveByKey(assemblyIdentity);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Collections
|
||||
|
||||
/// <summary>
|
||||
/// 通过数据库获取某一个实体类型下所有的分表数据到当前实体下,并且会自动建立父子关系。
|
||||
/// </summary>
|
||||
/// <param name="entity">实体实例</param>
|
||||
/// <typeparam name="T">实体泛型类型</typeparam>
|
||||
public async FTask GetCollections<T>(T entity) where T : Entity, ISingleCollectionRoot
|
||||
{
|
||||
if (!_collection.TryGetValue(typeof(T), out var collections))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var worldDateBase = Scene.World.DataBase;
|
||||
|
||||
using (await _coroutineLock.Wait(entity.Id))
|
||||
{
|
||||
foreach (var collectionName in collections)
|
||||
{
|
||||
var singleCollection = await worldDateBase.QueryNotLock<Entity>(entity.Id, true, collectionName);
|
||||
entity.AddComponent(singleCollection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 存储当前实体下支持分表的组件到数据中,包括存储实体本身。
|
||||
/// </summary>
|
||||
/// <param name="entity">实体实例</param>
|
||||
/// <typeparam name="T">实体泛型类型</typeparam>
|
||||
public async FTask SaveCollections<T>(T entity) where T : Entity, ISingleCollectionRoot
|
||||
{
|
||||
using var collections = ListPool<Entity>.Create();
|
||||
|
||||
foreach (var treeEntity in entity.ForEachSingleCollection)
|
||||
{
|
||||
if (treeEntity is not ISupportedSingleCollection)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
collections.Add(treeEntity);
|
||||
}
|
||||
|
||||
collections.Add(entity);
|
||||
await entity.Scene.World.DataBase.Save(entity.Id, collections);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
using Fantasy.Event;
|
||||
|
||||
namespace Fantasy.Timer
|
||||
{
|
||||
/// <summary>
|
||||
/// 计时器抽象类,提供了一个基础框架,用于创建处理计时器事件的具体类。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">事件的类型参数</typeparam>
|
||||
public abstract class TimerHandler<T> : EventSystem<T> { }
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
// #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
// namespace Fantasy
|
||||
// {
|
||||
// public sealed class ScheduledTaskPool : PoolCore<ScheduledTask>
|
||||
// {
|
||||
// public ScheduledTaskPool() : base(2000) { }
|
||||
//
|
||||
// public ScheduledTask Rent(Action action, ref int rounds, ref int finalSlot)
|
||||
// {
|
||||
// var scheduledTask = Rent();
|
||||
// scheduledTask.Rounds = rounds;
|
||||
// scheduledTask.Action = action;
|
||||
// scheduledTask.FinalSlot = finalSlot;
|
||||
// return scheduledTask;
|
||||
// }
|
||||
//
|
||||
// public override void Return(ScheduledTask item)
|
||||
// {
|
||||
// base.Return(item);
|
||||
// item.Dispose();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public sealed class ScheduledTask : IPool, IDisposable
|
||||
// {
|
||||
// public int Rounds;
|
||||
// public int FinalSlot;
|
||||
// public Action Action;
|
||||
// public LinkedListNode<ScheduledTask> Node;
|
||||
//
|
||||
// public bool IsPool { get; set; }
|
||||
// public ScheduledTask() { }
|
||||
// public ScheduledTask(Action action, ref int rounds, ref int finalSlot)
|
||||
// {
|
||||
// Action = action;
|
||||
// Rounds = rounds;
|
||||
// FinalSlot = finalSlot;
|
||||
// }
|
||||
//
|
||||
// public void Dispose()
|
||||
// {
|
||||
// Rounds = 0;
|
||||
// FinalSlot = 0;
|
||||
// Action = null;
|
||||
// Node = null;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,134 @@
|
||||
// using System.Runtime.CompilerServices;
|
||||
// // ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
// #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
//
|
||||
// namespace Fantasy
|
||||
// {
|
||||
// public sealed class TimeWheel
|
||||
// {
|
||||
// private int _currentIndex;
|
||||
// private ScheduledTaskPool _scheduledTaskPool;
|
||||
//
|
||||
// private readonly Scene _scene;
|
||||
// private readonly int _wheelSize;
|
||||
// private readonly int _tickDuration;
|
||||
// private readonly TimeWheel _upperLevelWheel;
|
||||
// private readonly LinkedList<ScheduledTask>[] _wheel;
|
||||
// private readonly Queue<ScheduledTask> _tasksToReschedule = new Queue<ScheduledTask>();
|
||||
// private readonly Dictionary<long, ScheduledTask> _taskDictionary = new Dictionary<long, ScheduledTask>();
|
||||
//
|
||||
// public TimeWheel(TimerComponent timerComponent, int wheelSize, int tickDuration, TimeWheel upperLevelWheel = null)
|
||||
// {
|
||||
// _scene = timerComponent.Scene;
|
||||
// _wheelSize = wheelSize;
|
||||
// _tickDuration = tickDuration;
|
||||
// _upperLevelWheel = upperLevelWheel;
|
||||
// _scheduledTaskPool = timerComponent.ScheduledTaskPool;
|
||||
// _wheel = new LinkedList<ScheduledTask>[_wheelSize];
|
||||
// for (var i = 0; i < wheelSize; i++)
|
||||
// {
|
||||
// _wheel[i] = new LinkedList<ScheduledTask>();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public long Schedule(Action action, int delay)
|
||||
// {
|
||||
// var ticks = delay / _tickDuration;
|
||||
// var futureIndex = ticks + _currentIndex;
|
||||
// var rounds = futureIndex / _wheelSize;
|
||||
// var slot = futureIndex % _wheelSize;
|
||||
//
|
||||
// if (slot == 0)
|
||||
// {
|
||||
// slot = _wheelSize - 1;
|
||||
// rounds--;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// slot--;
|
||||
// }
|
||||
//
|
||||
// var taskId = _scene.RuntimeIdFactory.Create;
|
||||
// var task = _scheduledTaskPool.Rent(action, ref rounds, ref slot);
|
||||
// task.Node = _wheel[slot].AddLast(task);
|
||||
// _taskDictionary.Add(taskId, task);
|
||||
// Console.WriteLine($"Schedule rounds:{rounds} slot:{slot} _currentIndex:{_currentIndex}");
|
||||
// return taskId;
|
||||
// }
|
||||
//
|
||||
// public bool Remove(int taskId)
|
||||
// {
|
||||
// if (!_taskDictionary.TryGetValue(taskId, out var task))
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// _taskDictionary.Remove(taskId);
|
||||
// _wheel[task.FinalSlot].Remove(task.Node);
|
||||
// _scheduledTaskPool.Return(task);
|
||||
// Console.WriteLine("找到已经删除了任务");
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// public void Tick(object? state)
|
||||
// {
|
||||
// var currentWheel = _wheel[_currentIndex];
|
||||
//
|
||||
// if (currentWheel.Count == 0)
|
||||
// {
|
||||
// AdvanceIndex();
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// var currentNode = currentWheel.First;
|
||||
//
|
||||
// while (currentNode != null)
|
||||
// {
|
||||
// var nextNode = currentNode.Next;
|
||||
// var task = currentNode.Value;
|
||||
//
|
||||
// if (task.Rounds <= 0 && task.FinalSlot == _currentIndex)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// task.Action.Invoke();
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Log.Error($"Exception during task execution: {ex.Message}");
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// task.Rounds--;
|
||||
// _tasksToReschedule.Enqueue(task);
|
||||
// }
|
||||
//
|
||||
// currentWheel.Remove(currentNode);
|
||||
// currentNode = nextNode;
|
||||
// }
|
||||
//
|
||||
// RescheduleTasks();
|
||||
// AdvanceIndex();
|
||||
// }
|
||||
//
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// private void AdvanceIndex()
|
||||
// {
|
||||
// _currentIndex = (_currentIndex + 1) % _wheelSize;
|
||||
// if (_currentIndex == 0 && _upperLevelWheel != null)
|
||||
// {
|
||||
// _upperLevelWheel.Tick(null);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// private void RescheduleTasks()
|
||||
// {
|
||||
// while (_tasksToReschedule.TryDequeue(out var task))
|
||||
// {
|
||||
// _wheel[task.FinalSlot].AddLast(task);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8618
|
||||
|
||||
namespace Fantasy.Timer
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct TimerAction
|
||||
{
|
||||
public long TimerId;
|
||||
public long StartTime;
|
||||
public long TriggerTime;
|
||||
public readonly object Callback;
|
||||
public readonly TimerType TimerType;
|
||||
public TimerAction(long timerId, TimerType timerType, long startTime, long triggerTime, object callback)
|
||||
{
|
||||
TimerId = timerId;
|
||||
Callback = callback;
|
||||
TimerType = timerType;
|
||||
StartTime = startTime;
|
||||
TriggerTime = triggerTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// ReSharper disable ForCanBeConvertedToForeach
|
||||
|
||||
using Fantasy.Entitas;
|
||||
using Fantasy.Entitas.Interface;
|
||||
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
#if FANTASY_UNITY
|
||||
using UnityEngine;
|
||||
#endif
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
namespace Fantasy.Timer
|
||||
{
|
||||
public sealed class TimerComponentUpdateSystem : UpdateSystem<TimerComponent>
|
||||
{
|
||||
protected override void Update(TimerComponent self)
|
||||
{
|
||||
self.Update();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 时间调度组件
|
||||
/// </summary>
|
||||
public sealed class TimerComponent : Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 使用系统时间创建的计时器核心。
|
||||
/// </summary>
|
||||
public TimerSchedulerNet Net { get; private set; }
|
||||
#if FANTASY_UNITY
|
||||
/// <summary>
|
||||
/// 使用 Unity 时间创建的计时器核心。
|
||||
/// </summary>
|
||||
public TimerSchedulerNetUnity Unity { get; private set; }
|
||||
#endif
|
||||
internal TimerComponent Initialize()
|
||||
{
|
||||
Net = new TimerSchedulerNet(Scene);
|
||||
#if FANTASY_UNITY
|
||||
Unity = new TimerSchedulerNetUnity(Scene);
|
||||
#endif
|
||||
return this;
|
||||
}
|
||||
public void Update()
|
||||
{
|
||||
Net.Update();
|
||||
#if FANTASY_UNITY
|
||||
Unity.Update();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Async;
|
||||
using Fantasy.DataStructure.Collection;
|
||||
using Fantasy.Helper;
|
||||
// ReSharper disable UnusedParameter.Global
|
||||
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
|
||||
namespace Fantasy.Timer
|
||||
{
|
||||
/// <summary>
|
||||
/// 基于系统事件的任务调度系统
|
||||
/// </summary>
|
||||
public sealed class TimerSchedulerNet
|
||||
{
|
||||
private readonly Scene _scene;
|
||||
private long _idGenerator;
|
||||
private long _minTime; // 最小时间
|
||||
private readonly Queue<long> _timeOutTime = new Queue<long>();
|
||||
private readonly Queue<long> _timeOutTimerIds = new Queue<long>();
|
||||
private readonly Dictionary<long, TimerAction> _timerActions = new Dictionary<long, TimerAction>();
|
||||
private readonly SortedOneToManyList<long, long> _timeId = new(); // 时间与计时器ID的有序一对多列表
|
||||
private long GetId => ++_idGenerator;
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="scene">当前的Scene</param>
|
||||
public TimerSchedulerNet(Scene scene)
|
||||
{
|
||||
_scene = scene;
|
||||
}
|
||||
|
||||
private long Now()
|
||||
{
|
||||
return TimeHelper.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 驱动方法,只有调用这个方法任务系统才会正常运转。
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
if (_timeId.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var currentTime = Now();
|
||||
|
||||
if (currentTime < _minTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 遍历时间ID列表,查找超时的计时器任务
|
||||
foreach (var (key, _) in _timeId)
|
||||
{
|
||||
if (key > currentTime)
|
||||
{
|
||||
_minTime = key;
|
||||
break;
|
||||
}
|
||||
|
||||
_timeOutTime.Enqueue(key);
|
||||
}
|
||||
|
||||
// 处理超时的计时器任务
|
||||
while (_timeOutTime.TryDequeue(out var time))
|
||||
{
|
||||
var timerIds = _timeId[time];
|
||||
for (var i = 0; i < timerIds.Count; ++i)
|
||||
{
|
||||
_timeOutTimerIds.Enqueue(timerIds[i]);
|
||||
}
|
||||
|
||||
_timeId.Remove(time);
|
||||
// _timeId.RemoveKey(time);
|
||||
}
|
||||
|
||||
if (_timeId.Count == 0)
|
||||
{
|
||||
_minTime = long.MaxValue;
|
||||
}
|
||||
|
||||
// 执行超时的计时器任务的回调操作
|
||||
while (_timeOutTimerIds.TryDequeue(out var timerId))
|
||||
{
|
||||
if (!_timerActions.Remove(timerId, out var timerAction))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 根据计时器类型执行不同的操作
|
||||
switch (timerAction.TimerType)
|
||||
{
|
||||
case TimerType.OnceWaitTimer:
|
||||
{
|
||||
var tcs = (FTask<bool>)timerAction.Callback;
|
||||
tcs.SetResult(true);
|
||||
break;
|
||||
}
|
||||
case TimerType.OnceTimer:
|
||||
{
|
||||
if (timerAction.Callback is not Action action)
|
||||
{
|
||||
Log.Error($"timerAction {timerAction.ToJson()}");
|
||||
break;
|
||||
}
|
||||
|
||||
action();
|
||||
break;
|
||||
}
|
||||
case TimerType.RepeatedTimer:
|
||||
{
|
||||
if (timerAction.Callback is not Action action)
|
||||
{
|
||||
Log.Error($"timerAction {timerAction.ToJson()}");
|
||||
break;
|
||||
}
|
||||
|
||||
timerAction.StartTime = Now();
|
||||
AddTimer(ref timerAction);
|
||||
action();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddTimer(ref TimerAction timer)
|
||||
{
|
||||
var tillTime = timer.StartTime + timer.TriggerTime;
|
||||
_timeId.Add(tillTime, timer.TimerId);
|
||||
_timerActions.Add(timer.TimerId, timer);
|
||||
|
||||
if (tillTime < _minTime)
|
||||
{
|
||||
_minTime = tillTime;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待指定时间。
|
||||
/// </summary>
|
||||
/// <param name="time">等待的时间长度。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>等待是否成功。</returns>
|
||||
public async FTask<bool> WaitAsync(long time, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
if (time <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var now = Now();
|
||||
var timerId = GetId;
|
||||
var tcs = FTask<bool>.Create();
|
||||
var timerAction = new TimerAction(timerId, TimerType.OnceWaitTimer, now, time, tcs);
|
||||
|
||||
void CancelActionVoid()
|
||||
{
|
||||
if (Remove(timerId))
|
||||
{
|
||||
tcs.SetResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool result;
|
||||
|
||||
try
|
||||
{
|
||||
cancellationToken?.Add(CancelActionVoid);
|
||||
AddTimer(ref timerAction);
|
||||
result = await tcs;
|
||||
}
|
||||
finally
|
||||
{
|
||||
cancellationToken?.Remove(CancelActionVoid);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待直到指定时间。
|
||||
/// </summary>
|
||||
/// <param name="tillTime">等待的目标时间。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>等待是否成功。</returns>
|
||||
public async FTask<bool> WaitTillAsync(long tillTime, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
var now = Now();
|
||||
|
||||
if (now >= tillTime)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var timerId = GetId;
|
||||
var tcs = FTask<bool>.Create();
|
||||
var timerAction = new TimerAction(timerId, TimerType.OnceWaitTimer, now, tillTime - now, tcs);
|
||||
|
||||
void CancelActionVoid()
|
||||
{
|
||||
if (Remove(timerId))
|
||||
{
|
||||
tcs.SetResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool result;
|
||||
|
||||
try
|
||||
{
|
||||
cancellationToken?.Add(CancelActionVoid);
|
||||
AddTimer(ref timerAction);
|
||||
result = await tcs;
|
||||
}
|
||||
finally
|
||||
{
|
||||
cancellationToken?.Remove(CancelActionVoid);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待一帧时间。
|
||||
/// </summary>
|
||||
/// <returns>等待是否成功。</returns>
|
||||
public async FTask WaitFrameAsync()
|
||||
{
|
||||
#if FANTASY_NET
|
||||
await WaitAsync(100);
|
||||
#else
|
||||
await WaitAsync(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间
|
||||
/// </summary>
|
||||
/// <param name="time">计时器执行的目标时间。</param>
|
||||
/// <param name="action">计时器回调方法。</param>
|
||||
/// <returns></returns>
|
||||
public long OnceTimer(long time, Action action)
|
||||
{
|
||||
var now = Now();
|
||||
var timerId = GetId;
|
||||
var timerAction = new TimerAction(timerId, TimerType.OnceTimer, now, time, action);
|
||||
AddTimer(ref timerAction);
|
||||
return timerId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间。
|
||||
/// </summary>
|
||||
/// <param name="tillTime">计时器执行的目标时间。</param>
|
||||
/// <param name="action">计时器回调方法。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long OnceTillTimer(long tillTime, Action action)
|
||||
{
|
||||
var now = Now();
|
||||
|
||||
if (tillTime < now)
|
||||
{
|
||||
Log.Error($"new once time too small tillTime:{tillTime} Now:{now}");
|
||||
}
|
||||
|
||||
var timerId = GetId;
|
||||
var timerAction = new TimerAction(timerId, TimerType.OnceTimer, now, tillTime - now, action);
|
||||
AddTimer(ref timerAction);
|
||||
return timerId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,用于发布指定类型的事件。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">事件类型。</typeparam>
|
||||
/// <param name="time">计时器执行的延迟时间。</param>
|
||||
/// <param name="timerHandlerType">事件处理器类型。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long OnceTimer<T>(long time, T timerHandlerType) where T : struct
|
||||
{
|
||||
void OnceTimerVoid()
|
||||
{
|
||||
_scene.EventComponent.Publish(timerHandlerType);
|
||||
}
|
||||
|
||||
return OnceTimer(time, OnceTimerVoid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间,用于发布指定类型的事件。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">事件类型。</typeparam>
|
||||
/// <param name="tillTime">计时器执行的目标时间。</param>
|
||||
/// <param name="timerHandlerType">事件处理器类型。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long OnceTillTimer<T>(long tillTime, T timerHandlerType) where T : struct
|
||||
{
|
||||
void OnceTillTimerVoid()
|
||||
{
|
||||
_scene.EventComponent.Publish(timerHandlerType);
|
||||
}
|
||||
|
||||
return OnceTillTimer(tillTime, OnceTillTimerVoid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个帧任务
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
public long FrameTimer(Action action)
|
||||
{
|
||||
#if FANTASY_NET
|
||||
return RepeatedTimerInner(100, action);
|
||||
#else
|
||||
return RepeatedTimerInner(0, action);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个重复执行的计时器。
|
||||
/// </summary>
|
||||
/// <param name="time">计时器重复间隔的时间。</param>
|
||||
/// <param name="action">计时器回调方法。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long RepeatedTimer(long time, Action action)
|
||||
{
|
||||
if (time < 0)
|
||||
{
|
||||
Log.Error($"time too small: {time}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return RepeatedTimerInner(time, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个重复执行的计时器,用于发布指定类型的事件。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">事件类型。</typeparam>
|
||||
/// <param name="time">计时器重复间隔的时间。</param>
|
||||
/// <param name="timerHandlerType">事件处理器类型。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long RepeatedTimer<T>(long time, T timerHandlerType) where T : struct
|
||||
{
|
||||
void RepeatedTimerVoid()
|
||||
{
|
||||
_scene.EventComponent.Publish(timerHandlerType);
|
||||
}
|
||||
|
||||
return RepeatedTimer(time, RepeatedTimerVoid);
|
||||
}
|
||||
|
||||
private long RepeatedTimerInner(long time, Action action)
|
||||
{
|
||||
var now = Now();
|
||||
var timerId = GetId;
|
||||
var timerAction = new TimerAction(timerId, TimerType.RepeatedTimer, now, time, action);
|
||||
AddTimer(ref timerAction);
|
||||
return timerId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定 ID 的计时器。
|
||||
/// </summary>
|
||||
/// <param name="timerId"></param>
|
||||
/// <returns></returns>
|
||||
public bool Remove(ref long timerId)
|
||||
{
|
||||
var id = timerId;
|
||||
timerId = 0;
|
||||
return Remove(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定 ID 的计时器。
|
||||
/// </summary>
|
||||
/// <param name="timerId">计时器的 ID。</param>
|
||||
public bool Remove(long timerId)
|
||||
{
|
||||
return timerId != 0 && _timerActions.Remove(timerId, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
#if FANTASY_UNITY
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Async;
|
||||
using Fantasy.DataStructure.Collection;
|
||||
using Fantasy.Helper;
|
||||
using UnityEngine;
|
||||
namespace Fantasy.Timer
|
||||
{
|
||||
public sealed class TimerSchedulerNetUnity
|
||||
{
|
||||
private readonly Scene _scene;
|
||||
private long _idGenerator;
|
||||
private long _minTime; // 最小时间
|
||||
private readonly Queue<long> _timeOutTime = new Queue<long>();
|
||||
private readonly Queue<long> _timeOutTimerIds = new Queue<long>();
|
||||
private readonly Dictionary<long, TimerAction> _timerActions = new Dictionary<long, TimerAction>();
|
||||
private readonly SortedOneToManyList<long, long> _timeId = new(); // 时间与计时器ID的有序一对多列表
|
||||
private long GetId => ++_idGenerator;
|
||||
public TimerSchedulerNetUnity(Scene scene)
|
||||
{
|
||||
_scene = scene;
|
||||
}
|
||||
|
||||
private long Now()
|
||||
{
|
||||
return (long)(Time.time * 1000);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (_timeId.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var currentTime = Now();
|
||||
|
||||
if (currentTime < _minTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 遍历时间ID列表,查找超时的计时器任务
|
||||
foreach (var (key, _) in _timeId)
|
||||
{
|
||||
if (key > currentTime)
|
||||
{
|
||||
_minTime = key;
|
||||
break;
|
||||
}
|
||||
|
||||
_timeOutTime.Enqueue(key);
|
||||
}
|
||||
|
||||
// 处理超时的计时器任务
|
||||
while (_timeOutTime.TryDequeue(out var time))
|
||||
{
|
||||
var timerIds = _timeId[time];
|
||||
for (var i = 0; i < timerIds.Count; ++i)
|
||||
{
|
||||
_timeOutTimerIds.Enqueue(timerIds[i]);
|
||||
}
|
||||
|
||||
_timeId.Remove(time);
|
||||
// _timeId.RemoveKey(time);
|
||||
}
|
||||
|
||||
if (_timeId.Count == 0)
|
||||
{
|
||||
_minTime = long.MaxValue;
|
||||
}
|
||||
|
||||
// 执行超时的计时器任务的回调操作
|
||||
while (_timeOutTimerIds.TryDequeue(out var timerId))
|
||||
{
|
||||
if (!_timerActions.Remove(timerId, out var timerAction))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 根据计时器类型执行不同的操作
|
||||
switch (timerAction.TimerType)
|
||||
{
|
||||
case TimerType.OnceWaitTimer:
|
||||
{
|
||||
var tcs = (FTask<bool>)timerAction.Callback;
|
||||
tcs.SetResult(true);
|
||||
break;
|
||||
}
|
||||
case TimerType.OnceTimer:
|
||||
{
|
||||
if (timerAction.Callback is not Action action)
|
||||
{
|
||||
Log.Error($"timerAction {timerAction.ToJson()}");
|
||||
break;
|
||||
}
|
||||
|
||||
action();
|
||||
break;
|
||||
}
|
||||
case TimerType.RepeatedTimer:
|
||||
{
|
||||
if (timerAction.Callback is not Action action)
|
||||
{
|
||||
Log.Error($"timerAction {timerAction.ToJson()}");
|
||||
break;
|
||||
}
|
||||
|
||||
timerAction.StartTime = Now();
|
||||
AddTimer(ref timerAction);
|
||||
action();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddTimer(ref TimerAction timer)
|
||||
{
|
||||
var tillTime = timer.StartTime + timer.TriggerTime;
|
||||
_timeId.Add(tillTime, timer.TimerId);
|
||||
_timerActions.Add(timer.TimerId, timer);
|
||||
|
||||
if (tillTime < _minTime)
|
||||
{
|
||||
_minTime = tillTime;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待指定时间。
|
||||
/// </summary>
|
||||
/// <param name="time">等待的时间长度。</param>
|
||||
/// <param name="cancellationToken">可选的取消令牌。</param>
|
||||
/// <returns>等待是否成功。</returns>
|
||||
public async FTask<bool> WaitAsync(long time, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
if (time <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var now = Now();
|
||||
var timerId = GetId;
|
||||
var tcs = FTask<bool>.Create();
|
||||
var timerAction = new TimerAction(timerId, TimerType.OnceWaitTimer, now, time, tcs);
|
||||
|
||||
|
||||
void CancelActionVoid()
|
||||
{
|
||||
if (Remove(timerId))
|
||||
{
|
||||
tcs.SetResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool result;
|
||||
|
||||
try
|
||||
{
|
||||
cancellationToken?.Add(CancelActionVoid);
|
||||
AddTimer(ref timerAction);
|
||||
result =await tcs;
|
||||
}
|
||||
finally
|
||||
{
|
||||
cancellationToken?.Remove(CancelActionVoid);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待直到指定时间。
|
||||
/// </summary>
|
||||
/// <param name="tillTime">等待的目标时间。</param>
|
||||
/// <param name="cancellationToken">可选的取消令牌。</param>
|
||||
/// <returns>等待是否成功。</returns>
|
||||
public async FTask<bool> WaitTillAsync(long tillTime, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
var now = Now();
|
||||
|
||||
if (now >= tillTime)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var timerId = GetId;
|
||||
var tcs = FTask<bool>.Create();
|
||||
var timerAction = new TimerAction(timerId, TimerType.OnceWaitTimer, now, tillTime - now, tcs);
|
||||
|
||||
void CancelActionVoid()
|
||||
{
|
||||
if (Remove(timerId))
|
||||
{
|
||||
tcs.SetResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool result;
|
||||
|
||||
try
|
||||
{
|
||||
cancellationToken?.Add(CancelActionVoid);
|
||||
AddTimer(ref timerAction);
|
||||
result = await tcs;
|
||||
}
|
||||
finally
|
||||
{
|
||||
cancellationToken?.Remove(CancelActionVoid);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待一帧时间。
|
||||
/// </summary>
|
||||
/// <returns>等待是否成功。</returns>
|
||||
public async FTask WaitFrameAsync(FCancellationToken cancellationToken = null)
|
||||
{
|
||||
await WaitAsync(1, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间
|
||||
/// </summary>
|
||||
/// <param name="time">计时器执行的目标时间。</param>
|
||||
/// <param name="action">计时器回调方法。</param>
|
||||
/// <returns></returns>
|
||||
public long OnceTimer(long time, Action action)
|
||||
{
|
||||
var now = Now();
|
||||
var timerId = GetId;
|
||||
var timerAction = new TimerAction(timerId, TimerType.OnceTimer, now, time, action);
|
||||
AddTimer(ref timerAction);
|
||||
return timerId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间。
|
||||
/// </summary>
|
||||
/// <param name="tillTime">计时器执行的目标时间。</param>
|
||||
/// <param name="action">计时器回调方法。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long OnceTillTimer(long tillTime, Action action)
|
||||
{
|
||||
var now = Now();
|
||||
|
||||
if (tillTime < now)
|
||||
{
|
||||
Log.Error($"new once time too small tillTime:{tillTime} Now:{now}");
|
||||
}
|
||||
|
||||
var timerId = GetId;
|
||||
var timerAction = new TimerAction(timerId, TimerType.OnceTimer, now, tillTime - now, action);
|
||||
AddTimer(ref timerAction);
|
||||
return timerId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,用于发布指定类型的事件。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">事件类型。</typeparam>
|
||||
/// <param name="time">计时器执行的延迟时间。</param>
|
||||
/// <param name="timerHandlerType">事件处理器类型。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long OnceTimer<T>(long time, T timerHandlerType) where T : struct
|
||||
{
|
||||
void OnceTimerVoid()
|
||||
{
|
||||
_scene.EventComponent.Publish(timerHandlerType);
|
||||
}
|
||||
|
||||
return OnceTimer(time, OnceTimerVoid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间,用于发布指定类型的事件。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">事件类型。</typeparam>
|
||||
/// <param name="tillTime">计时器执行的目标时间。</param>
|
||||
/// <param name="timerHandlerType">事件处理器类型。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long OnceTillTimer<T>(long tillTime, T timerHandlerType) where T : struct
|
||||
{
|
||||
void OnceTillTimerVoid()
|
||||
{
|
||||
_scene.EventComponent.Publish(timerHandlerType);
|
||||
}
|
||||
|
||||
return OnceTillTimer(tillTime, OnceTillTimerVoid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个帧任务
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
public long FrameTimer(Action action)
|
||||
{
|
||||
return RepeatedTimerInner(1, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个重复执行的计时器。
|
||||
/// </summary>
|
||||
/// <param name="time">计时器重复间隔的时间。</param>
|
||||
/// <param name="action">计时器回调方法。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long RepeatedTimer(long time, Action action)
|
||||
{
|
||||
if (time < 0)
|
||||
{
|
||||
Log.Error($"time too small: {time}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return RepeatedTimerInner(time, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个重复执行的计时器,用于发布指定类型的事件。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">事件类型。</typeparam>
|
||||
/// <param name="time">计时器重复间隔的时间。</param>
|
||||
/// <param name="timerHandlerType">事件处理器类型。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long RepeatedTimer<T>(long time, T timerHandlerType) where T : struct
|
||||
{
|
||||
void RepeatedTimerVoid()
|
||||
{
|
||||
_scene.EventComponent.Publish(timerHandlerType);
|
||||
}
|
||||
|
||||
return RepeatedTimer(time, RepeatedTimerVoid);
|
||||
}
|
||||
|
||||
private long RepeatedTimerInner(long time, Action action)
|
||||
{
|
||||
var now = Now();
|
||||
var timerId = GetId;
|
||||
var timerAction = new TimerAction(timerId, TimerType.RepeatedTimer, now, time, action);
|
||||
AddTimer(ref timerAction);
|
||||
return timerId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定 ID 的计时器。
|
||||
/// </summary>
|
||||
/// <param name="timerId"></param>
|
||||
/// <returns></returns>
|
||||
public bool Remove(ref long timerId)
|
||||
{
|
||||
var id = timerId;
|
||||
timerId = 0;
|
||||
return Remove(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定 ID 的计时器。
|
||||
/// </summary>
|
||||
/// <param name="timerId">计时器的 ID。</param>
|
||||
public bool Remove(long timerId)
|
||||
{
|
||||
return timerId != 0 && _timerActions.Remove(timerId, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace Fantasy.Timer
|
||||
{
|
||||
/// <summary>
|
||||
/// 枚举对象TimerType
|
||||
/// </summary>
|
||||
public enum TimerType
|
||||
{
|
||||
/// <summary>
|
||||
/// None
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// 一次等待定时器
|
||||
/// </summary>
|
||||
OnceWaitTimer,
|
||||
/// <summary>
|
||||
/// 一次性定时器
|
||||
/// </summary>
|
||||
OnceTimer,
|
||||
/// <summary>
|
||||
/// 重复定时器
|
||||
/// </summary>
|
||||
RepeatedTimer
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user