mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-14 16:51:28 +00:00
[+] 接入ET8服务端
[+] 接入ET8服务端
This commit is contained in:
8
Assets/GameScripts/DotNet/Core/Fiber/Module/Actor.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Fiber/Module/Actor.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43270a1ae7e8422458e49f6855475e8c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a98f0108855505546a19aa3fb9aeba48
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d74a2fe39353d9c498e360f72bca8080
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ceb83d81c9c72a340887562f24d51629
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b51d51733a35791478b6a487b5196dd3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25135ca97055fb64fa240b23a01794cb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92e2f035a00b77c498465fbfb5bc4f0d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c025fcd3d7a7add45851a0c7b93c6674
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 75bebf4862d839045a78140d260ecc11
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d57848ad9cf65e3478460a513cfb6434
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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; // 这个必须最大
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d66a751e8de660f4eb57de33db77e33a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 17b1d64d5899f124f9b5df1c2d73e687
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/Fiber/Module/Navmesh.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Fiber/Module/Navmesh.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ebde9d58b893f434eaf40d782d2ef41a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bae9eeb5e7ebfeb4fa7173090cdafaa2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 627f202f7d6eb9e4389a2b719f9edd03
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 54538c8374bfae744a9a242c227e48ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/Fiber/Module/Timer.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Fiber/Module/Timer.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3daeaaf059f885443a3575415c6b1ec8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
12
Assets/GameScripts/DotNet/Core/Fiber/Module/Timer/ATimer.cs
Normal file
12
Assets/GameScripts/DotNet/Core/Fiber/Module/Timer/ATimer.cs
Normal 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);
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d537ec4047c7d0c4e872df21040c1823
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f5224b1db93f7847bb68beec0f232b1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user