[+] 接入ET8服务端

[+] 接入ET8服务端
This commit is contained in:
ALEXTANG
2023-07-13 12:23:48 +08:00
parent e0be062006
commit 336d4b2eb9
1316 changed files with 130657 additions and 626 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 43270a1ae7e8422458e49f6855475e8c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,16 @@
using System;
namespace ET
{
public static class ActorHelper
{
public static IActorResponse CreateResponse(IActorRequest iActorRequest, int error)
{
Type responseType = OpcodeType.Instance.GetResponseType(iActorRequest.GetType());
IActorResponse response = (IActorResponse)ObjectPool.Instance.Fetch(responseType);
response.Error = error;
response.RpcId = iActorRequest.RpcId;
return response;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a98f0108855505546a19aa3fb9aeba48
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace ET
{
[ComponentOf(typeof(Scene))]
public class ActorInnerComponent: Entity, IAwake, IDestroy, IUpdate
{
public const long TIMEOUT_TIME = 40 * 1000;
public int RpcId;
public readonly Dictionary<int, ActorMessageSender> requestCallback = new();
public readonly List<ActorMessageInfo> list = new();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d74a2fe39353d9c498e360f72bca8080
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,193 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace ET
{
[EntitySystemOf(typeof(ActorInnerComponent))]
[FriendOf(typeof(ActorInnerComponent))]
public static partial class ActorInnerComponentSystem
{
[EntitySystem]
private static void Destroy(this ActorInnerComponent self)
{
Fiber fiber = self.Fiber();
ActorMessageQueue.Instance.RemoveQueue(fiber.Id);
}
[EntitySystem]
private static void Awake(this ActorInnerComponent self)
{
Fiber fiber = self.Fiber();
ActorMessageQueue.Instance.AddQueue(fiber.Id);
fiber.ActorInnerComponent = self;
}
[EntitySystem]
private static void Update(this ActorInnerComponent self)
{
self.list.Clear();
Fiber fiber = self.Fiber();
ActorMessageQueue.Instance.Fetch(fiber.Id, 1000, self.list);
ActorInnerComponent actorInnerComponent = fiber.Root.GetComponent<ActorInnerComponent>();
foreach (ActorMessageInfo actorMessageInfo in self.list)
{
if (actorMessageInfo.MessageObject is IActorResponse response)
{
actorInnerComponent.HandleIActorResponse(response);
continue;
}
ActorId actorId = actorMessageInfo.ActorId;
MessageObject message = actorMessageInfo.MessageObject;
MailBoxComponent mailBoxComponent = self.Fiber().Mailboxes.Get(actorId.InstanceId);
if (mailBoxComponent == null)
{
Log.Warning($"actor not found mailbox, from: {actorId} current: {fiber.Address} {message}");
if (message is IActorRequest request)
{
IActorResponse resp = ActorHelper.CreateResponse(request, ErrorCore.ERR_NotFoundActor);
actorInnerComponent.Reply(actorId.Address, resp);
}
return;
}
mailBoxComponent.Add(actorId.Address, message);
}
}
public static void HandleIActorResponse(this ActorInnerComponent self, IActorResponse response)
{
if (!self.requestCallback.Remove(response.RpcId, out ActorMessageSender actorMessageSender))
{
return;
}
Run(actorMessageSender, response);
}
private static void Run(ActorMessageSender self, IActorResponse response)
{
if (response.Error == ErrorCore.ERR_ActorTimeout)
{
self.Tcs.SetException(new Exception($"Rpc error: request, 注意Actor消息超时请注意查看是否死锁或者没有reply: actorId: {self.ActorId} {self.Request}, response: {response}"));
return;
}
if (self.NeedException && ErrorCore.IsRpcNeedThrowException(response.Error))
{
self.Tcs.SetException(new Exception($"Rpc error: actorId: {self.ActorId} request: {self.Request}, response: {response}"));
return;
}
self.Tcs.SetResult(response);
}
public static void Reply(this ActorInnerComponent self, Address fromAddress, IActorResponse message)
{
self.Send(new ActorId(fromAddress, 0), message);
}
public static void Send(this ActorInnerComponent self, ActorId actorId, IActorMessage message)
{
Fiber fiber = self.Fiber();
// 如果发向同一个进程,则扔到消息队列中
if (actorId.Process != fiber.Process)
{
throw new Exception($"actor inner process diff: {actorId.Process} {fiber.Process}");
}
ActorMessageQueue.Instance.Send(fiber.Address, actorId, (MessageObject)message);
}
private static void SendInner(this ActorInnerComponent self, ActorId actorId, MessageObject message)
{
Fiber fiber = self.Fiber();
ActorMessageQueue.Instance.Send(fiber.Address, actorId, message);
}
public static int GetRpcId(this ActorInnerComponent self)
{
return ++self.RpcId;
}
public static async ETTask<IActorResponse> Call(
this ActorInnerComponent self,
ActorId actorId,
IActorRequest request,
bool needException = true
)
{
request.RpcId = self.GetRpcId();
if (actorId == default)
{
throw new Exception($"actor id is 0: {request}");
}
return await self.Call(actorId, request.RpcId, request, needException);
}
public static async ETTask<IActorResponse> Call(
this ActorInnerComponent self,
ActorId actorId,
int rpcId,
IActorRequest iActorRequest,
bool needException = true
)
{
if (actorId == default)
{
throw new Exception($"actor id is 0: {iActorRequest}");
}
Fiber fiber = self.Fiber();
if (fiber.Process != actorId.Process)
{
throw new Exception($"actor inner process diff: {actorId.Process} {fiber.Process}");
}
var tcs = ETTask<IActorResponse>.Create(true);
self.requestCallback.Add(rpcId, new ActorMessageSender(actorId, iActorRequest, tcs, needException));
self.SendInner(actorId, iActorRequest as MessageObject);
async ETTask Timeout()
{
await fiber.TimerComponent.WaitAsync(ActorInnerComponent.TIMEOUT_TIME);
if (!self.requestCallback.Remove(rpcId, out ActorMessageSender action))
{
return;
}
if (needException)
{
action.Tcs.SetException(new Exception($"actor sender timeout: {iActorRequest}"));
}
else
{
IActorResponse response = ActorHelper.CreateResponse(iActorRequest, ErrorCore.ERR_Timeout);
action.Tcs.SetResult(response);
}
}
Timeout().Coroutine();
long beginTime = fiber.TimeInfo.ServerFrameTime();
IActorResponse response = await tcs;
long endTime = fiber.TimeInfo.ServerFrameTime();
long costTime = endTime - beginTime;
if (costTime > 200)
{
Log.Warning($"actor rpc time > 200: {costTime} {iActorRequest}");
}
return response;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ceb83d81c9c72a340887562f24d51629
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
using System.IO;
namespace ET
{
// 知道对方的instanceId使用这个类发actor消息
public readonly struct ActorMessageSender
{
public ActorId ActorId { get; }
public IActorRequest Request { get; }
public bool NeedException { get; }
public ETTask<IActorResponse> Tcs { get; }
public ActorMessageSender(ActorId actorId, IActorRequest iActorRequest, ETTask<IActorResponse> tcs, bool needException)
{
this.ActorId = actorId;
this.Request = iActorRequest;
this.Tcs = tcs;
this.NeedException = needException;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b51d51733a35791478b6a487b5196dd3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 25135ca97055fb64fa240b23a01794cb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
using System;
namespace ET
{
[EntitySystemOf(typeof(CoroutineLock))]
public static partial class CoroutineLockSystem
{
[EntitySystem]
private static void Awake(this CoroutineLock self, int type, long k, int count)
{
self.type = type;
self.key = k;
self.level = count;
}
[EntitySystem]
private static void Destroy(this CoroutineLock self)
{
self.Scene<CoroutineLockComponent>().RunNextCoroutine(self.type, self.key, self.level + 1);
self.type = CoroutineLockType.None;
self.key = 0;
self.level = 0;
}
}
[ChildOf(typeof(CoroutineLockQueue))]
public class CoroutineLock: Entity, IAwake<int, long, int>, IDestroy
{
public int type;
public long key;
public int level;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 92e2f035a00b77c498465fbfb5bc4f0d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
namespace ET
{
[EntitySystemOf(typeof(CoroutineLockComponent))]
public static partial class CoroutineLockComponentSystem
{
[EntitySystem]
public static void Awake(this CoroutineLockComponent self)
{
self.Fiber.CoroutineLockComponent = self;
}
[EntitySystem]
public static void Update(this CoroutineLockComponent self)
{
// 循环过程中会有对象继续加入队列
while (self.nextFrameRun.Count > 0)
{
(int coroutineLockType, long key, int count) = self.nextFrameRun.Dequeue();
self.Notify(coroutineLockType, key, count);
}
}
public static void RunNextCoroutine(this CoroutineLockComponent self, int coroutineLockType, long key, int level)
{
// 一个协程队列一帧处理超过100个,说明比较多了,打个warning,检查一下是否够正常
if (level == 100)
{
Log.Warning($"too much coroutine level: {coroutineLockType} {key}");
}
self.nextFrameRun.Enqueue((coroutineLockType, key, level));
}
public static async ETTask<CoroutineLock> Wait(this CoroutineLockComponent self, int coroutineLockType, long key, int time = 60000)
{
CoroutineLockQueueType coroutineLockQueueType = self.GetChild<CoroutineLockQueueType>(coroutineLockType) ?? self.AddChildWithId<CoroutineLockQueueType>(coroutineLockType);
return await coroutineLockQueueType.Wait(key, time);
}
private static void Notify(this CoroutineLockComponent self, int coroutineLockType, long key, int level)
{
CoroutineLockQueueType coroutineLockQueueType = self.GetChild<CoroutineLockQueueType>(coroutineLockType);
if (coroutineLockQueueType == null)
{
return;
}
coroutineLockQueueType.Notify(key, level);
}
}
[ComponentOf(typeof(Scene))]
public class CoroutineLockComponent: Entity, IAwake, IScene, IUpdate
{
public Fiber Fiber { get; set; }
public SceneType SceneType { get; set; }
public readonly Queue<(int, long, int)> nextFrameRun = new();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c025fcd3d7a7add45851a0c7b93c6674
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
namespace ET
{
[EntitySystemOf(typeof(CoroutineLockQueue))]
public static partial class CoroutineLockQueueSystem
{
[EntitySystem]
private static void Awake(this CoroutineLockQueue self, int type)
{
self.type = type;
}
[EntitySystem]
private static void Destroy(this CoroutineLockQueue self)
{
self.queue.Clear();
self.type = 0;
self.CurrentCoroutineLock = null;
}
public static async ETTask<CoroutineLock> Wait(this CoroutineLockQueue self, int time)
{
if (self.CurrentCoroutineLock == null)
{
self.CurrentCoroutineLock = self.AddChild<CoroutineLock, int, long, int>(self.type, self.Id, 1, true);
return self.CurrentCoroutineLock;
}
WaitCoroutineLock waitCoroutineLock = WaitCoroutineLock.Create();
self.queue.Enqueue(waitCoroutineLock);
if (time > 0)
{
long tillTime = self.Fiber().TimeInfo.ClientFrameTime() + time;
self.Root().GetComponent<TimerComponent>().NewOnceTimer(tillTime, TimerCoreInvokeType.CoroutineTimeout, waitCoroutineLock);
}
self.CurrentCoroutineLock = await waitCoroutineLock.Wait();
return self.CurrentCoroutineLock;
}
public static void Notify(this CoroutineLockQueue self, int level)
{
// 有可能WaitCoroutineLock已经超时抛出异常所以要找到一个未处理的WaitCoroutineLock
while (self.queue.Count > 0)
{
WaitCoroutineLock waitCoroutineLock = self.queue.Dequeue();
if (waitCoroutineLock.IsDisposed())
{
continue;
}
CoroutineLock coroutineLock = self.AddChild<CoroutineLock, int, long, int>(self.type, self.Id, level, true);
waitCoroutineLock.SetResult(coroutineLock);
break;
}
}
}
[ChildOf(typeof(CoroutineLockQueueType))]
public class CoroutineLockQueue: Entity, IAwake<int>, IDestroy
{
public int type;
private EntityRef<CoroutineLock> currentCoroutineLock;
public CoroutineLock CurrentCoroutineLock
{
get
{
return this.currentCoroutineLock;
}
set
{
this.currentCoroutineLock = value;
}
}
public Queue<WaitCoroutineLock> queue = new();
public int Count
{
get
{
return this.queue.Count;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 75bebf4862d839045a78140d260ecc11
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,57 @@
using System.Collections.Generic;
namespace ET
{
[EntitySystemOf(typeof(CoroutineLockQueueType))]
[FriendOf(typeof(CoroutineLockQueueType))]
public static partial class CoroutineLockQueueTypeSystem
{
[EntitySystem]
private static void Awake(this CoroutineLockQueueType self)
{
}
public static CoroutineLockQueue Get(this CoroutineLockQueueType self, long key)
{
return self.GetChild<CoroutineLockQueue>(key);
}
public static CoroutineLockQueue New(this CoroutineLockQueueType self, long key)
{
CoroutineLockQueue queue = self.AddChildWithId<CoroutineLockQueue, int>(key, (int)self.Id, true);
return queue;
}
public static void Remove(this CoroutineLockQueueType self, long key)
{
self.RemoveChild(key);
}
public static async ETTask<CoroutineLock> Wait(this CoroutineLockQueueType self, long key, int time)
{
CoroutineLockQueue queue = self.Get(key) ?? self.New(key);
return await queue.Wait(time);
}
public static void Notify(this CoroutineLockQueueType self, long key, int level)
{
CoroutineLockQueue queue = self.Get(key);
if (queue == null)
{
return;
}
if (queue.Count == 0)
{
self.Remove(key);
}
queue.Notify(level);
}
}
[ChildOf(typeof(CoroutineLockComponent))]
public class CoroutineLockQueueType: Entity, IAwake
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d57848ad9cf65e3478460a513cfb6434
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,16 @@
namespace ET
{
public static class CoroutineLockType
{
public const int None = 0;
public const int Location = 1; // location进程上使用
public const int ActorLocationSender = 2; // ActorLocationSender中队列消息
public const int Mailbox = 3; // Mailbox中队列
public const int UnitId = 4; // Map服务器上线下线时使用
public const int DB = 5;
public const int Resources = 6;
public const int ResourcesLoader = 7;
public const int Max = 100; // 这个必须最大
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d66a751e8de660f4eb57de33db77e33a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,62 @@
using System;
using System.Threading;
namespace ET
{
[Invoke(TimerCoreInvokeType.CoroutineTimeout)]
public class WaitCoroutineLockTimer: ATimer<WaitCoroutineLock>
{
protected override void Run(WaitCoroutineLock waitCoroutineLock)
{
if (waitCoroutineLock.IsDisposed())
{
return;
}
waitCoroutineLock.SetException(new Exception("coroutine is timeout!"));
}
}
public class WaitCoroutineLock
{
public static WaitCoroutineLock Create()
{
WaitCoroutineLock waitCoroutineLock = new WaitCoroutineLock();
waitCoroutineLock.tcs = ETTask<CoroutineLock>.Create(true);
return waitCoroutineLock;
}
private ETTask<CoroutineLock> tcs;
public void SetResult(CoroutineLock coroutineLock)
{
if (this.tcs == null)
{
throw new NullReferenceException("SetResult tcs is null");
}
var t = this.tcs;
this.tcs = null;
t.SetResult(coroutineLock);
}
public void SetException(Exception exception)
{
if (this.tcs == null)
{
throw new NullReferenceException("SetException tcs is null");
}
var t = this.tcs;
this.tcs = null;
t.SetException(exception);
}
public bool IsDisposed()
{
return this.tcs == null;
}
public async ETTask<CoroutineLock> Wait()
{
return await this.tcs;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 17b1d64d5899f124f9b5df1c2d73e687
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ebde9d58b893f434eaf40d782d2ef41a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
namespace ET
{
public class NavmeshComponent: Singleton<NavmeshComponent>, ISingletonAwake
{
public struct RecastFileLoader
{
public string Name { get; set; }
}
private readonly Dictionary<string, byte[]> navmeshs = new();
public void Awake()
{
}
public byte[] Get(string name)
{
lock (this)
{
if (this.navmeshs.TryGetValue(name, out byte[] bytes))
{
return bytes;
}
byte[] buffer =
EventSystem.Instance.Invoke<NavmeshComponent.RecastFileLoader, byte[]>(
new NavmeshComponent.RecastFileLoader() { Name = name });
if (buffer.Length == 0)
{
throw new Exception($"no nav data: {name}");
}
this.navmeshs[name] = buffer;
return buffer;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bae9eeb5e7ebfeb4fa7173090cdafaa2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 627f202f7d6eb9e4389a2b719f9edd03
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ET
{
public class FiberTaskScheduler: TaskScheduler
{
private readonly ConcurrentQueue<Task> queue = new();
public void Update()
{
while (true)
{
if (!this.queue.TryDequeue(out Task task))
{
return;
}
base.TryExecuteTask(task);
}
}
protected override IEnumerable<Task> GetScheduledTasks()
{
return this.queue;
}
protected override void QueueTask(Task task)
{
queue.Enqueue(task);
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
queue.Enqueue(task);
return true;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 54538c8374bfae744a9a242c227e48ad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3daeaaf059f885443a3575415c6b1ec8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,12 @@
namespace ET
{
public abstract class ATimer<T>: AInvokeHandler<TimerCallback> where T: class
{
public override void Handle(TimerCallback a)
{
this.Run(a.Args as T);
}
protected abstract void Run(T t);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d537ec4047c7d0c4e872df21040c1823
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,343 @@
using System.Collections.Generic;
namespace ET
{
public enum TimerClass
{
None,
OnceTimer,
OnceWaitTimer,
RepeatedTimer,
}
public class TimerAction
{
public static TimerAction Create(long id, TimerClass timerClass, long startTime, long time, int type, object obj)
{
TimerAction timerAction = ObjectPool.Instance.Fetch<TimerAction>();
timerAction.Id = id;
timerAction.TimerClass = timerClass;
timerAction.StartTime = startTime;
timerAction.Object = obj;
timerAction.Time = time;
timerAction.Type = type;
return timerAction;
}
public long Id;
public TimerClass TimerClass;
public object Object;
public long StartTime;
public long Time;
public int Type;
public void Recycle()
{
this.Id = 0;
this.Object = null;
this.StartTime = 0;
this.Time = 0;
this.TimerClass = TimerClass.None;
this.Type = 0;
ObjectPool.Instance.Recycle(this);
}
}
public struct TimerCallback
{
public object Args;
}
[EntitySystemOf(typeof(TimerComponent))]
public static partial class TimerComponentSystem
{
[EntitySystem]
private static void Awake(this TimerComponent self)
{
self.Fiber().TimerComponent = self;
}
[EntitySystem]
private static void Update(this TimerComponent self)
{
if (self.timeId.Count == 0)
{
return;
}
long timeNow = self.GetNow();
if (timeNow < self.minTime)
{
return;
}
using (var enumerator = self.timeId.GetEnumerator())
{
while (enumerator.MoveNext())
{
var kv = enumerator.Current;
long k = kv.Key;
if (k > timeNow)
{
self.minTime = k;
break;
}
self.timeOutTime.Enqueue(k);
}
}
while (self.timeOutTime.Count > 0)
{
long time = self.timeOutTime.Dequeue();
var list = self.timeId[time];
for (int i = 0; i < list.Count; ++i)
{
long timerId = list[i];
self.timeOutTimerIds.Enqueue(timerId);
}
self.timeId.Remove(time);
}
if (self.timeId.Count == 0)
{
self.minTime = long.MaxValue;
}
while (self.timeOutTimerIds.Count > 0)
{
long timerId = self.timeOutTimerIds.Dequeue();
if (!self.timerActions.Remove(timerId, out TimerAction timerAction))
{
continue;
}
self.Run(timerAction);
}
}
private static long GetId(this TimerComponent self)
{
return ++self.idGenerator;
}
private static long GetNow(this TimerComponent self)
{
return self.Fiber().TimeInfo.ClientFrameTime();
}
private static void Run(this TimerComponent self, TimerAction timerAction)
{
switch (timerAction.TimerClass)
{
case TimerClass.OnceTimer:
{
EventSystem.Instance.Invoke(timerAction.Type, new TimerCallback() { Args = timerAction.Object });
timerAction.Recycle();
break;
}
case TimerClass.OnceWaitTimer:
{
ETTask tcs = timerAction.Object as ETTask;
tcs.SetResult();
timerAction.Recycle();
break;
}
case TimerClass.RepeatedTimer:
{
long timeNow = self.GetNow();
timerAction.StartTime = timeNow;
self.AddTimer(timerAction);
EventSystem.Instance.Invoke(timerAction.Type, new TimerCallback() { Args = timerAction.Object });
break;
}
}
}
private static void AddTimer(this TimerComponent self, TimerAction timer)
{
long tillTime = timer.StartTime + timer.Time;
self.timeId.Add(tillTime, timer.Id);
self.timerActions.Add(timer.Id, timer);
if (tillTime < self.minTime)
{
self.minTime = tillTime;
}
}
public static bool Remove(this TimerComponent self, ref long id)
{
long i = id;
id = 0;
return self.Remove(i);
}
private static bool Remove(this TimerComponent self, long id)
{
if (id == 0)
{
return false;
}
if (!self.timerActions.Remove(id, out TimerAction timerAction))
{
return false;
}
timerAction.Recycle();
return true;
}
public static async ETTask WaitTillAsync(this TimerComponent self, long tillTime, ETCancellationToken cancellationToken = null)
{
long timeNow = self.GetNow();
if (timeNow >= tillTime)
{
return;
}
ETTask tcs = ETTask.Create(true);
TimerAction timer = TimerAction.Create(self.GetId(), TimerClass.OnceWaitTimer, timeNow, tillTime - timeNow, 0, tcs);
self.AddTimer(timer);
long timerId = timer.Id;
void CancelAction()
{
if (self.Remove(timerId))
{
tcs.SetResult();
}
}
try
{
cancellationToken?.Add(CancelAction);
await tcs;
}
finally
{
cancellationToken?.Remove(CancelAction);
}
}
public static async ETTask WaitFrameAsync(this TimerComponent self, ETCancellationToken cancellationToken = null)
{
await self.WaitAsync(1, cancellationToken);
}
public static async ETTask WaitAsync(this TimerComponent self, long time, ETCancellationToken cancellationToken = null)
{
if (time == 0)
{
return;
}
long timeNow = self.GetNow();
ETTask tcs = ETTask.Create(true);
TimerAction timer = TimerAction.Create(self.GetId(), TimerClass.OnceWaitTimer, timeNow, time, 0, tcs);
self.AddTimer(timer);
long timerId = timer.Id;
void CancelAction()
{
if (self.Remove(timerId))
{
tcs.SetResult();
}
}
try
{
cancellationToken?.Add(CancelAction);
await tcs;
}
finally
{
cancellationToken?.Remove(CancelAction);
}
}
// 用这个优点是可以热更缺点是回调式的写法逻辑不连贯。WaitTillAsync不能热更优点是逻辑连贯。
// wait时间短并且逻辑需要连贯的建议WaitTillAsync
// wait时间长不需要逻辑连贯的建议用NewOnceTimer
public static long NewOnceTimer(this TimerComponent self, long tillTime, int type, object args)
{
long timeNow = self.GetNow();
if (tillTime < timeNow)
{
Log.Error($"new once time too small: {tillTime}");
}
TimerAction timer = TimerAction.Create(self.GetId(), TimerClass.OnceTimer, timeNow, tillTime - timeNow, type, args);
self.AddTimer(timer);
return timer.Id;
}
public static long NewFrameTimer(this TimerComponent self, int type, object args)
{
#if DOTNET
return self.NewRepeatedTimerInner(100, type, args);
#else
return self.NewRepeatedTimerInner(0, type, args);
#endif
}
/// <summary>
/// 创建一个RepeatedTimer
/// </summary>
private static long NewRepeatedTimerInner(this TimerComponent self, long time, int type, object args)
{
#if DOTNET
if (time < 100)
{
throw new Exception($"repeated timer < 100, timerType: time: {time}");
}
#endif
long timeNow = self.GetNow();
TimerAction timer = TimerAction.Create(self.GetId(), TimerClass.RepeatedTimer, timeNow, time, type, args);
// 每帧执行的不用加到timerId中防止遍历
self.AddTimer(timer);
return timer.Id;
}
public static long NewRepeatedTimer(this TimerComponent self, long time, int type, object args)
{
if (time < 100)
{
Log.Error($"time too small: {time}");
return 0;
}
return self.NewRepeatedTimerInner(time, type, args);
}
}
[ComponentOf(typeof(Scene))]
public class TimerComponent: Entity, IAwake, IUpdate
{
/// <summary>
/// key: time, value: timer id
/// </summary>
public readonly MultiMap<long, long> timeId = new(1000);
public readonly Queue<long> timeOutTime = new();
public readonly Queue<long> timeOutTimerIds = new();
public readonly Dictionary<long, TimerAction> timerActions = new();
public long idGenerator;
// 记录最小时间不用每次都去MultiMap取第一个值
public long minTime = long.MaxValue;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2f5224b1db93f7847bb68beec0f232b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: