308 lines
8.5 KiB
C#
308 lines
8.5 KiB
C#
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
|
|||
|
namespace ET
|
|||
|
{
|
|||
|
public enum TimerClass
|
|||
|
{
|
|||
|
None,
|
|||
|
OnceWaitTimer,
|
|||
|
OnceTimer,
|
|||
|
RepeatedTimer,
|
|||
|
}
|
|||
|
|
|||
|
[ObjectSystem]
|
|||
|
public class TimerActionAwakeSystem: AwakeSystem<TimerAction, TimerClass, long, object>
|
|||
|
{
|
|||
|
public override void Awake(TimerAction self, TimerClass timerClass, long time, object callback)
|
|||
|
{
|
|||
|
self.TimerClass = timerClass;
|
|||
|
self.Callback = callback;
|
|||
|
self.Time = time;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[ObjectSystem]
|
|||
|
public class TimerActionDestroySystem: DestroySystem<TimerAction>
|
|||
|
{
|
|||
|
public override void Destroy(TimerAction self)
|
|||
|
{
|
|||
|
self.Callback = null;
|
|||
|
self.Time = 0;
|
|||
|
self.TimerClass = TimerClass.None;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public class TimerAction: Entity
|
|||
|
{
|
|||
|
public TimerClass TimerClass;
|
|||
|
|
|||
|
public object Callback;
|
|||
|
|
|||
|
public long Time;
|
|||
|
}
|
|||
|
|
|||
|
[ObjectSystem]
|
|||
|
public class TimerComponentAwakeSystem: AwakeSystem<TimerComponent>
|
|||
|
{
|
|||
|
public override void Awake(TimerComponent self)
|
|||
|
{
|
|||
|
TimerComponent.Instance = self;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[ObjectSystem]
|
|||
|
public class TimerComponentUpdateSystem: UpdateSystem<TimerComponent>
|
|||
|
{
|
|||
|
public override void Update(TimerComponent self)
|
|||
|
{
|
|||
|
self.Update();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public class TimerComponent: Entity
|
|||
|
{
|
|||
|
public static TimerComponent Instance
|
|||
|
{
|
|||
|
get;
|
|||
|
set;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// key: time, value: timer id
|
|||
|
/// </summary>
|
|||
|
private readonly MultiMap<long, long> TimeId = new MultiMap<long, long>();
|
|||
|
|
|||
|
private readonly Queue<long> timeOutTime = new Queue<long>();
|
|||
|
|
|||
|
private readonly Queue<long> timeOutTimerIds = new Queue<long>();
|
|||
|
|
|||
|
// 记录最小时间,不用每次都去MultiMap取第一个值
|
|||
|
private long minTime;
|
|||
|
|
|||
|
public void Update()
|
|||
|
{
|
|||
|
if (this.TimeId.Count == 0)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
long timeNow = TimeHelper.ServerNow();
|
|||
|
|
|||
|
if (timeNow < this.minTime)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
foreach (KeyValuePair<long, List<long>> kv in this.TimeId)
|
|||
|
{
|
|||
|
long k = kv.Key;
|
|||
|
if (k > timeNow)
|
|||
|
{
|
|||
|
minTime = k;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
this.timeOutTime.Enqueue(k);
|
|||
|
}
|
|||
|
|
|||
|
while (this.timeOutTime.Count > 0)
|
|||
|
{
|
|||
|
long time = this.timeOutTime.Dequeue();
|
|||
|
foreach (long timerId in this.TimeId[time])
|
|||
|
{
|
|||
|
this.timeOutTimerIds.Enqueue(timerId);
|
|||
|
}
|
|||
|
|
|||
|
this.TimeId.Remove(time);
|
|||
|
}
|
|||
|
|
|||
|
while (this.timeOutTimerIds.Count > 0)
|
|||
|
{
|
|||
|
long timerId = this.timeOutTimerIds.Dequeue();
|
|||
|
|
|||
|
TimerAction timerAction = this.GetChild<TimerAction>(timerId);
|
|||
|
if (timerAction == null)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
Run(timerAction);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void Run(TimerAction timerAction)
|
|||
|
{
|
|||
|
switch (timerAction.TimerClass)
|
|||
|
{
|
|||
|
case TimerClass.OnceWaitTimer:
|
|||
|
{
|
|||
|
ETTaskCompletionSource<bool> tcs = timerAction.Callback as ETTaskCompletionSource<bool>;
|
|||
|
this.Remove(timerAction.Id);
|
|||
|
tcs.SetResult(true);
|
|||
|
break;
|
|||
|
}
|
|||
|
case TimerClass.OnceTimer:
|
|||
|
{
|
|||
|
Action action = timerAction.Callback as Action;
|
|||
|
this.Remove(timerAction.Id);
|
|||
|
action?.Invoke();
|
|||
|
break;
|
|||
|
}
|
|||
|
case TimerClass.RepeatedTimer:
|
|||
|
{
|
|||
|
Action action = timerAction.Callback as Action;
|
|||
|
long tillTime = TimeHelper.ServerNow() + timerAction.Time;
|
|||
|
this.AddTimer(tillTime, timerAction);
|
|||
|
action?.Invoke();
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void AddTimer(long tillTime, TimerAction timer)
|
|||
|
{
|
|||
|
this.TimeId.Add(tillTime, timer.Id);
|
|||
|
if (tillTime < this.minTime)
|
|||
|
{
|
|||
|
this.minTime = tillTime;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public async ETTask<bool> WaitTillAsync(long tillTime, ETCancellationToken cancellationToken = null)
|
|||
|
{
|
|||
|
if (TimeHelper.ServerNow() >= tillTime)
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
ETTaskCompletionSource<bool> tcs = new ETTaskCompletionSource<bool>();
|
|||
|
TimerAction timer = EntityFactory.CreateWithParent<TimerAction, TimerClass, long, object>(this, TimerClass.OnceWaitTimer, 0, tcs, true);
|
|||
|
this.AddTimer(tillTime, timer);
|
|||
|
long timerId = timer.Id;
|
|||
|
|
|||
|
void CancelAction()
|
|||
|
{
|
|||
|
if (this.Remove(timerId))
|
|||
|
{
|
|||
|
tcs.SetResult(false);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
bool ret;
|
|||
|
try
|
|||
|
{
|
|||
|
cancellationToken?.Add(CancelAction);
|
|||
|
ret = await tcs.Task;
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
cancellationToken?.Remove(CancelAction);
|
|||
|
}
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
public async ETTask<bool> WaitFrameAsync(ETCancellationToken cancellationToken = null)
|
|||
|
{
|
|||
|
return await WaitAsync(1, cancellationToken);
|
|||
|
}
|
|||
|
|
|||
|
public async ETTask<bool> WaitAsync(long time, ETCancellationToken cancellationToken = null)
|
|||
|
{
|
|||
|
if (time == 0)
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
long tillTime = TimeHelper.ServerNow() + time;
|
|||
|
|
|||
|
ETTaskCompletionSource<bool> tcs = new ETTaskCompletionSource<bool>();
|
|||
|
|
|||
|
TimerAction timer = EntityFactory.CreateWithParent<TimerAction, TimerClass, long, object>(this, TimerClass.OnceWaitTimer, 0, tcs, true);
|
|||
|
this.AddTimer(tillTime, timer);
|
|||
|
long timerId = timer.Id;
|
|||
|
|
|||
|
void CancelAction()
|
|||
|
{
|
|||
|
if (this.Remove(timerId))
|
|||
|
{
|
|||
|
tcs.SetResult(false);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
bool ret;
|
|||
|
try
|
|||
|
{
|
|||
|
cancellationToken?.Add(CancelAction);
|
|||
|
ret = await tcs.Task;
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
cancellationToken?.Remove(CancelAction);
|
|||
|
}
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
public long NewFrameTimer(Action action)
|
|||
|
{
|
|||
|
#if NOT_CLIENT
|
|||
|
return NewRepeatedTimerInner(100, action);
|
|||
|
#else
|
|||
|
return NewRepeatedTimerInner(1, action);
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 创建一个RepeatedTimer
|
|||
|
/// </summary>
|
|||
|
private long NewRepeatedTimerInner(long time, Action action)
|
|||
|
{
|
|||
|
#if NOT_CLIENT
|
|||
|
if (time < 100)
|
|||
|
{
|
|||
|
throw new Exception($"repeated timer < 100, timerType: time: {time}");
|
|||
|
}
|
|||
|
#endif
|
|||
|
long tillTime = TimeHelper.ServerNow() + time;
|
|||
|
TimerAction timer = EntityFactory.CreateWithParent<TimerAction, TimerClass, long, object>(this, TimerClass.RepeatedTimer, time, action, true);
|
|||
|
this.AddTimer(tillTime, timer);
|
|||
|
return timer.Id;
|
|||
|
}
|
|||
|
|
|||
|
public long NewRepeatedTimer(long time, Action action)
|
|||
|
{
|
|||
|
return NewRepeatedTimerInner(time, action);
|
|||
|
}
|
|||
|
|
|||
|
public void Remove(ref long id)
|
|||
|
{
|
|||
|
this.Remove(id);
|
|||
|
id = 0;
|
|||
|
}
|
|||
|
|
|||
|
public bool Remove(long id)
|
|||
|
{
|
|||
|
if (id == 0)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
TimerAction timerAction = this.GetChild<TimerAction>(id);
|
|||
|
if (timerAction == null)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
timerAction.Dispose();
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
public long NewOnceTimer(long tillTime, Action action)
|
|||
|
{
|
|||
|
if (tillTime < TimeHelper.ServerNow())
|
|||
|
{
|
|||
|
Log.Error($"new once time too small: {tillTime}");
|
|||
|
}
|
|||
|
TimerAction timer = EntityFactory.CreateWithParent<TimerAction, TimerClass, long, object>(this, TimerClass.OnceTimer, 0, action, true);
|
|||
|
this.AddTimer(tillTime, timer);
|
|||
|
return timer.Id;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|