[+] 接入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,134 @@
using System;
using System.Collections.Generic;
namespace ET
{
public class EntitySystem
{
private readonly Queue<EntityRef<Entity>>[] queues = new Queue<EntityRef<Entity>>[InstanceQueueIndex.Max];
public EntitySystem()
{
for (int i = 0; i < this.queues.Length; i++)
{
this.queues[i] = new Queue<EntityRef<Entity>>();
}
}
public virtual void RegisterSystem(Entity component)
{
Type type = component.GetType();
TypeSystems.OneTypeSystems oneTypeSystems = EntitySystemSingleton.Instance.TypeSystems.GetOneTypeSystems(type);
if (oneTypeSystems == null)
{
return;
}
for (int i = 0; i < oneTypeSystems.QueueFlag.Length; ++i)
{
if (!oneTypeSystems.QueueFlag[i])
{
continue;
}
this.queues[i].Enqueue(component);
}
}
public void Update()
{
Queue<EntityRef<Entity>> queue = this.queues[InstanceQueueIndex.Update];
int count = queue.Count;
while (count-- > 0)
{
Entity component = queue.Dequeue();
if (component == null)
{
continue;
}
if (component.IsDisposed)
{
continue;
}
if (component is not IUpdate)
{
continue;
}
try
{
List<object> iUpdateSystems = EntitySystemSingleton.Instance.TypeSystems.GetSystems(component.GetType(), typeof (IUpdateSystem));
if (iUpdateSystems == null)
{
continue;
}
queue.Enqueue(component);
foreach (IUpdateSystem iUpdateSystem in iUpdateSystems)
{
try
{
iUpdateSystem.Run(component);
}
catch (Exception e)
{
Log.Error(e);
}
}
}
catch (Exception e)
{
throw new Exception($"entity system update fail: {component.GetType().FullName}", e);
}
}
}
public void LateUpdate()
{
Queue<EntityRef<Entity>> queue = this.queues[InstanceQueueIndex.LateUpdate];
int count = queue.Count;
while (count-- > 0)
{
Entity component = queue.Dequeue();
if (component == null)
{
continue;
}
if (component.IsDisposed)
{
continue;
}
if (component is not ILateUpdate)
{
continue;
}
List<object> iLateUpdateSystems = EntitySystemSingleton.Instance.TypeSystems.GetSystems(component.GetType(), typeof (ILateUpdateSystem));
if (iLateUpdateSystems == null)
{
continue;
}
queue.Enqueue(component);
foreach (ILateUpdateSystem iLateUpdateSystem in iLateUpdateSystems)
{
try
{
iLateUpdateSystem.Run(component);
}
catch (Exception e)
{
Log.Error(e);
}
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
using System.Threading;
namespace ET
{
public static class FiberHelper
{
public static ActorId GetActorId(this Entity self)
{
Fiber root = self.Fiber();
return new ActorId(root.Process, root.Id, self.InstanceId);
}
}
public class Fiber: IDisposable
{
public bool IsDisposed;
public int Id;
public int Zone;
public Scene Root { get; }
public Address Address
{
get
{
return new Address(this.Process, this.Id);
}
}
public int Process { get; }
public EntitySystem EntitySystem { get; }
public TimeInfo TimeInfo { get; }
public IdGenerater IdGenerater { get; private set; }
public Mailboxes Mailboxes { get; private set; }
public ThreadSynchronizationContext ThreadSynchronizationContext { get; }
private EntityRef<TimerComponent> timerCompnent;
public TimerComponent TimerComponent
{
get
{
return this.timerCompnent;
}
set
{
this.timerCompnent = value;
}
}
private EntityRef<CoroutineLockComponent> coroutineLockComponent;
public CoroutineLockComponent CoroutineLockComponent
{
get
{
return this.coroutineLockComponent;
}
set
{
this.coroutineLockComponent = value;
}
}
private EntityRef<ActorInnerComponent> actorInnerComponent;
public ActorInnerComponent ActorInnerComponent
{
get
{
return this.actorInnerComponent;
}
set
{
this.actorInnerComponent = value;
}
}
private readonly Queue<ETTask> frameFinishTasks = new();
internal Fiber(int id, int process, int zone, SceneType sceneType, string name)
{
this.Id = id;
this.Process = process;
this.Zone = zone;
this.EntitySystem = new EntitySystem();
this.TimeInfo = new TimeInfo();
this.IdGenerater = new IdGenerater(process, this.TimeInfo);
this.Mailboxes = new Mailboxes();
this.ThreadSynchronizationContext = new ThreadSynchronizationContext();
this.Root = new Scene(this, id, 1, sceneType, name);
}
internal void Update()
{
try
{
this.TimeInfo.Update();
this.EntitySystem.Update();
}
catch (Exception e)
{
Log.Error(e);
}
}
internal void LateUpdate()
{
try
{
this.EntitySystem.LateUpdate();
FrameFinishUpdate();
this.ThreadSynchronizationContext.Update();
}
catch (Exception e)
{
Log.Error(e);
}
}
public async ETTask WaitFrameFinish()
{
ETTask task = ETTask.Create(true);
this.frameFinishTasks.Enqueue(task);
await task;
}
private void FrameFinishUpdate()
{
while (this.frameFinishTasks.Count > 0)
{
ETTask task = this.frameFinishTasks.Dequeue();
task.SetResult();
}
}
public void Dispose()
{
if (this.IsDisposed)
{
return;
}
this.IsDisposed = true;
FiberManager.Instance.RemoveReal(this.Id);
this.Root.Dispose();
}
}
}

View File

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

View File

@@ -0,0 +1,155 @@
using System;
using System.Runtime.InteropServices;
namespace ET
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct IdStruct
{
public short Process; // 14bit
public uint Time; // 30bit
public uint Value; // 20bit
public long ToLong()
{
ulong result = 0;
result |= (ushort) this.Process;
result <<= 14;
result |= this.Time;
result <<= 30;
result |= this.Value;
return (long) result;
}
public IdStruct(uint time, short process, uint value)
{
this.Process = process;
this.Time = time;
this.Value = value;
}
public IdStruct(long id)
{
ulong result = (ulong) id;
this.Value = (ushort) (result & IdGenerater.Mask20bit);
result >>= 20;
this.Time = (uint) result & IdGenerater.Mask30bit;
result >>= 30;
this.Process = (short) (result & IdGenerater.Mask14bit);
}
public override string ToString()
{
return $"process: {this.Process}, time: {this.Time}, value: {this.Value}";
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct InstanceIdStruct
{
public uint Time; // 32bit
public uint Value; // 32bit
public long ToLong()
{
ulong result = 0;
result |= this.Time;
result <<= 32;
result |= this.Value;
return (long) result;
}
public InstanceIdStruct(uint time, uint value)
{
this.Time = time;
this.Value = value;
}
public InstanceIdStruct(long id)
{
ulong result = (ulong) id;
this.Value = (uint)(result & uint.MaxValue);
result >>= 32;
this.Time = (uint)(result & uint.MaxValue);
}
public override string ToString()
{
return $"time: {this.Time}, value: {this.Value}";
}
}
public class IdGenerater
{
public const int MaxZone = 1024;
public const int Mask14bit = 0x3fff;
public const int Mask30bit = 0x3fffffff;
public const int Mask20bit = 0xfffff;
private readonly long epoch2022;
private uint value;
private uint lastIdTime;
private readonly TimeInfo timeInfo;
private readonly int process;
private uint instanceIdValue;
public IdGenerater(int process, TimeInfo timeInfo)
{
this.process = process;
this.timeInfo = timeInfo;
long epoch1970tick = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks / 10000;
this.epoch2022 = new DateTime(2022, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks / 10000 - epoch1970tick;
this.lastIdTime = TimeSince2022();
if (this.lastIdTime <= 0)
{
Log.Warning($"lastIdTime less than 0: {this.lastIdTime}");
this.lastIdTime = 1;
}
}
private uint TimeSince2022()
{
uint a = (uint)((this.timeInfo.FrameTime - this.epoch2022) / 1000);
return a;
}
public long GenerateId()
{
uint time = TimeSince2022();
// 时间不会倒退
if (time > this.lastIdTime)
{
this.lastIdTime = time;
}
this.value = IdValueGenerater.Instance.Value;
IdStruct idStruct = new(this.lastIdTime, (short)this.process, value);
return idStruct.ToLong();
}
public long GenerateInstanceId()
{
uint time = this.TimeSince2022();
// 时间不会倒退
if (time > this.lastIdTime)
{
this.lastIdTime = time;
}
++this.instanceIdValue;
if (this.instanceIdValue >= int.MaxValue)
{
this.instanceIdValue = 0;
}
InstanceIdStruct instanceIdStruct = new(this.lastIdTime, this.instanceIdValue);
return instanceIdStruct.ToLong();
}
}
}

View File

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

View File

@@ -0,0 +1,50 @@
namespace ET
{
[EntitySystemOf(typeof(MailBoxComponent))]
[FriendOf(typeof(MailBoxComponent))]
public static partial class MailBoxComponentSystem
{
[EntitySystem]
private static void Awake(this MailBoxComponent self, MailBoxType mailBoxType)
{
Fiber fiber = self.Fiber();
self.MailBoxType = mailBoxType;
self.ParentInstanceId = self.Parent.InstanceId;
fiber.Mailboxes.Add(self);
self.CoroutineLockComponent = fiber.CoroutineLockComponent;
}
[EntitySystem]
private static void Destroy(this MailBoxComponent self)
{
self.Fiber().Mailboxes.Remove(self.ParentInstanceId);
}
// 加到mailbox
public static void Add(this MailBoxComponent self, Address fromAddress, MessageObject messageObject)
{
// 根据mailboxType进行分发处理
EventSystem.Instance.Invoke((long)self.MailBoxType, new MailBoxInvoker() {MailBoxComponent = self, MessageObject = messageObject, FromAddress = fromAddress});
}
}
public struct MailBoxInvoker
{
public Address FromAddress;
public MessageObject MessageObject;
public MailBoxComponent MailBoxComponent;
}
/// <summary>
/// 挂上这个组件表示该Entity是一个Actor,接收的消息将会队列处理
/// </summary>
[ComponentOf]
public class MailBoxComponent: Entity, IAwake<MailBoxType>, IDestroy
{
public long ParentInstanceId { get; set; }
// Mailbox的类型
public MailBoxType MailBoxType { get; set; }
public CoroutineLockComponent CoroutineLockComponent { get; set; }
}
}

View File

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

View File

@@ -0,0 +1,9 @@
namespace ET
{
public enum MailBoxType
{
OrderedMessage = 1,
UnOrderedMessage,
GateSession,
}
}

View File

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

View File

@@ -0,0 +1,25 @@
using System.Collections.Generic;
namespace ET
{
public class Mailboxes
{
private readonly Dictionary<long, MailBoxComponent> mailboxes = new();
public void Add(MailBoxComponent mailBoxComponent)
{
this.mailboxes.Add(mailBoxComponent.Parent.InstanceId, mailBoxComponent);
}
public void Remove(long instanceId)
{
this.mailboxes.Remove(instanceId);
}
public MailBoxComponent Get(long instanceId)
{
this.mailboxes.TryGetValue(instanceId, out MailBoxComponent entity);
return entity;
}
}
}

View File

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

View File

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

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:

View File

@@ -0,0 +1,75 @@
using System;
namespace ET
{
public class TimeInfo
{
private int timeZone;
public int TimeZone
{
get
{
return this.timeZone;
}
set
{
this.timeZone = value;
dt = dt1970.AddHours(TimeZone);
}
}
private DateTime dt1970;
private DateTime dt;
public long ServerMinusClientTime { private get; set; }
public long FrameTime;
public TimeInfo()
{
this.dt1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
this.dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
this.FrameTime = this.ClientNow();
}
public void Update()
{
this.FrameTime = this.ClientNow();
}
/// <summary>
/// 根据时间戳获取时间
/// </summary>
public DateTime ToDateTime(long timeStamp)
{
return dt.AddTicks(timeStamp * 10000);
}
// 线程安全
public long ClientNow()
{
return (DateTime.UtcNow.Ticks - this.dt1970.Ticks) / 10000;
}
public long ServerNow()
{
return ClientNow() + this.ServerMinusClientTime;
}
public long ClientFrameTime()
{
return this.FrameTime;
}
public long ServerFrameTime()
{
return this.FrameTime + this.ServerMinusClientTime;
}
public long Transition(DateTime d)
{
return (d.Ticks - dt.Ticks) / 10000;
}
}
}

View File

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