AlicizaX/Client/Packages/com.alicizax.unity.timer/Runtime/Timer/TimerManager.cs
陈思海 eb38f67131 init
2025-01-23 19:06:48 +08:00

330 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using AlicizaX.Runtime;
using UnityEngine;
namespace AlicizaX.Timer.Runtime
{
public struct TimerInfo
{
public float Interval;
public int Repeat;
public float Elapsed;
}
/// <summary>
/// 定时器管理器
/// </summary>
[UnityEngine.Scripting.Preserve]
internal sealed class TimerManager : ITimerManager
{
class TimerItem
{
public float Interval;
public int Repeat;
public Action<object> Callback;
public object Param;
public float Elapsed;
public bool Deleted;
public void Set(float interval, int repeat, Action<object> callback, object param)
{
this.Interval = interval;
this.Repeat = repeat;
this.Callback = callback;
this.Param = param;
}
}
private readonly Dictionary<Action<object>, TimerItem> _items = new Dictionary<Action<object>, TimerItem>();
private readonly Dictionary<Action<object>, TimerItem> _toAdd = new Dictionary<Action<object>, TimerItem>();
private readonly List<TimerItem> _toRemove = new List<TimerItem>();
private readonly List<TimerItem> _pool = new List<TimerItem>(100);
private static readonly object Locker = new object();
/// <summary>
/// 是否触发回调异常
/// </summary>
public static bool CatchCallbackExceptions = false;
void IModuleUpdate.Update(float elapseSeconds, float realElapseSeconds)
{
lock (Locker)
{
Dictionary<Action<object>, TimerItem>.Enumerator enumerator;
if (_items.Count > 0)
{
enumerator = _items.GetEnumerator();
while (enumerator.MoveNext())
{
TimerItem timerItem = enumerator.Current.Value;
if (timerItem.Deleted)
{
_toRemove.Add(timerItem);
continue;
}
timerItem.Elapsed += realElapseSeconds;
if (timerItem.Elapsed < timerItem.Interval)
{
continue;
}
timerItem.Elapsed -= timerItem.Interval;
if (timerItem.Elapsed < 0 || timerItem.Elapsed > 0.03f)
timerItem.Elapsed = 0;
if (timerItem.Repeat > 0)
{
timerItem.Repeat--;
if (timerItem.Repeat == 0)
{
timerItem.Deleted = true;
_toRemove.Add(timerItem);
}
}
// var repeat = i.Repeat;
if (timerItem.Callback != null)
{
if (CatchCallbackExceptions)
{
try
{
timerItem.Callback(timerItem.Param);
}
catch (Exception e)
{
timerItem.Deleted = true;
Debug.LogWarning("Timer timer(internal=" + timerItem.Interval + ", repeat=" + timerItem.Repeat + ") callback error > " + e.Message);
}
}
else
{
timerItem.Callback(timerItem.Param);
}
}
}
enumerator.Dispose();
}
int len = _toRemove.Count;
if (len > 0)
{
for (int k = 0; k < len; k++)
{
TimerItem i = _toRemove[k];
if (i.Deleted && i.Callback != null)
{
_items.Remove(i.Callback);
ReturnToPool(i);
}
}
_toRemove.Clear();
}
if (_toAdd.Count > 0)
{
enumerator = _toAdd.GetEnumerator();
while (enumerator.MoveNext())
{
_items.Add(enumerator.Current.Key, enumerator.Current.Value);
}
enumerator.Dispose();
_toAdd.Clear();
}
}
}
void IModule.Dispose()
{
lock (Locker)
{
this._toRemove.Clear();
this._toAdd.Clear();
this._items.Clear();
}
}
/// <summary>
/// 添加一个定时调用的任务
/// </summary>
/// <param name="interval">间隔时间(以秒为单位)</param>
/// <param name="repeat">重复次数0 表示无限重复)</param>
/// <param name="callback">要执行的回调函数</param>
/// <param name="callbackParam">回调函数的参数(可选)</param>
public void Add(float interval, int repeat, Action<object> callback, object callbackParam = null)
{
lock (Locker)
{
if (callback == null)
{
Debug.LogWarning("timer callback is null, " + interval + "," + repeat);
return;
}
if (_items.TryGetValue(callback, out var t))
{
t.Set(interval, repeat, callback, callbackParam);
t.Elapsed = 0;
t.Deleted = false;
return;
}
if (_toAdd.TryGetValue(callback, out t))
{
t.Set(interval, repeat, callback, callbackParam);
return;
}
t = GetFromPool();
t.Interval = interval;
t.Repeat = repeat;
t.Callback = callback;
t.Param = callbackParam;
_toAdd[callback] = t;
}
}
/// <summary>
/// 添加一个只执行一次的任务
/// </summary>
/// <param name="interval">间隔时间(以秒为单位)</param>
/// <param name="callback">要执行的回调函数</param>
/// <param name="callbackParam">回调函数的参数(可选)</param>
public void AddOnce(float interval, Action<object> callback, object callbackParam = null)
{
Add(interval, 1, callback, callbackParam);
}
/// <summary>
/// 添加一个每帧更新执行的任务
/// </summary>
/// <param name="callback">要执行的回调函数</param>
public void AddUpdate(Action<object> callback)
{
Add(0.001f, 0, callback);
}
/// <summary>
/// 添加一个每帧更新执行的任务
/// </summary>
/// <param name="callback">要执行的回调函数</param>
/// <param name="callbackParam">回调函数的参数</param>
public void AddUpdate(Action<object> callback, object callbackParam)
{
Add(0.001f, 0, callback, callbackParam);
}
/// <summary>
/// 检查指定的任务是否存在
/// </summary>
/// <param name="callback">要检查的回调函数</param>
/// <returns>存在返回 true不存在返回 false</returns>
public bool Exists(Action<object> callback)
{
lock (Locker)
{
if (_toAdd.ContainsKey(callback))
{
return true;
}
if (_items.TryGetValue(callback, out var at))
{
return !at.Deleted;
}
return false;
}
}
/// <summary>
/// 移除指定的任务
/// </summary>
/// <param name="callback">要移除的回调函数</param>
public void Remove(Action<object> callback)
{
lock (Locker)
{
if (_toAdd.TryGetValue(callback, out var t))
{
_toAdd.Remove(callback);
ReturnToPool(t);
}
if (_items.TryGetValue(callback, out t))
{
t.Deleted = true;
}
}
}
private TimerItem GetFromPool()
{
TimerItem t;
int cnt = _pool.Count;
if (cnt > 0)
{
t = _pool[cnt - 1];
_pool.RemoveAt(cnt - 1);
t.Deleted = false;
t.Elapsed = 0;
}
else
{
t = new TimerItem();
}
return t;
}
private void ReturnToPool(TimerItem t)
{
t.Callback = null;
_pool.Add(t);
}
public int Priority
{
get => 0;
}
/// <summary>
/// 获取所有正在运行的定时器的信息
/// </summary>
/// <returns>一个列表,其中包含每个定时器的信息</returns>
public List<TimerInfo> GetTimerInfos()
{
List<TimerInfo> timerInfos = new List<TimerInfo>();
lock (Locker)
{
foreach (var timerItem in _items.Values)
{
if (!timerItem.Deleted) // 确保定时器未被删除
{
timerInfos.Add(new TimerInfo
{
Interval = timerItem.Interval,
Repeat = timerItem.Repeat,
Elapsed = timerItem.Elapsed,
});
}
}
}
return timerInfos;
}
}
}