[+] 接入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: 663974eab9143cd4aa7740221e48e4f2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,179 @@
using System.Collections.Generic;
using Unity.Mathematics;
namespace ET.Server
{
[EntitySystemOf(typeof(AOIEntity))]
[FriendOf(typeof(AOIEntity))]
public static partial class AOIEntitySystem2
{
[EntitySystem]
private static void Awake(this AOIEntity self, int distance, float3 pos)
{
self.ViewDistance = distance;
self.Scene().GetComponent<AOIManagerComponent>().Add(self, pos.x, pos.z);
}
[EntitySystem]
private static void Destroy(this AOIEntity self)
{
self.Scene().GetComponent<AOIManagerComponent>()?.Remove(self);
self.ViewDistance = 0;
self.SeeUnits.Clear();
self.SeePlayers.Clear();
self.BeSeePlayers.Clear();
self.BeSeeUnits.Clear();
self.SubEnterCells.Clear();
self.SubLeaveCells.Clear();
}
}
[FriendOf(typeof(AOIEntity))]
[FriendOf(typeof(Cell))]
public static partial class AOIEntitySystem
{
// 获取在自己视野中的对象
public static Dictionary<long, AOIEntity> GetSeeUnits(this AOIEntity self)
{
return self.SeeUnits;
}
public static Dictionary<long, AOIEntity> GetBeSeePlayers(this AOIEntity self)
{
return self.BeSeePlayers;
}
public static Dictionary<long, AOIEntity> GetSeePlayers(this AOIEntity self)
{
return self.SeePlayers;
}
// cell中的unit进入self的视野
public static void SubEnter(this AOIEntity self, Cell cell)
{
cell.SubsEnterEntities.Add(self.Id, self);
foreach (KeyValuePair<long, AOIEntity> kv in cell.AOIUnits)
{
if (kv.Key == self.Id)
{
continue;
}
self.EnterSight(kv.Value);
}
}
public static void UnSubEnter(this AOIEntity self, Cell cell)
{
cell.SubsEnterEntities.Remove(self.Id);
}
public static void SubLeave(this AOIEntity self, Cell cell)
{
cell.SubsLeaveEntities.Add(self.Id, self);
}
// cell中的unit离开self的视野
public static void UnSubLeave(this AOIEntity self, Cell cell)
{
foreach (KeyValuePair<long, AOIEntity> kv in cell.AOIUnits)
{
if (kv.Key == self.Id)
{
continue;
}
self.LeaveSight(kv.Value);
}
cell.SubsLeaveEntities.Remove(self.Id);
}
// enter进入self视野
public static void EnterSight(this AOIEntity self, AOIEntity enter)
{
// 有可能之前在Enter后来出了Enter还在LeaveCell这样仍然没有删除继续进来Enter这种情况不需要处理
if (self.SeeUnits.ContainsKey(enter.Id))
{
return;
}
if (!AOISeeCheckHelper.IsCanSee(self, enter))
{
return;
}
if (self.Unit.Type == UnitType.Player)
{
if (enter.Unit.Type == UnitType.Player)
{
self.SeeUnits.Add(enter.Id, enter);
enter.BeSeeUnits.Add(self.Id, self);
self.SeePlayers.Add(enter.Id, enter);
enter.BeSeePlayers.Add(self.Id, self);
}
else
{
self.SeeUnits.Add(enter.Id, enter);
enter.BeSeeUnits.Add(self.Id, self);
enter.BeSeePlayers.Add(self.Id, self);
}
}
else
{
if (enter.Unit.Type == UnitType.Player)
{
self.SeeUnits.Add(enter.Id, enter);
enter.BeSeeUnits.Add(self.Id, self);
self.SeePlayers.Add(enter.Id, enter);
}
else
{
self.SeeUnits.Add(enter.Id, enter);
enter.BeSeeUnits.Add(self.Id, self);
}
}
EventSystem.Instance.Publish(self.Scene(), new EventType.UnitEnterSightRange() { A = self, B = enter });
}
// leave离开self视野
public static void LeaveSight(this AOIEntity self, AOIEntity leave)
{
if (self.Id == leave.Id)
{
return;
}
if (!self.SeeUnits.ContainsKey(leave.Id))
{
return;
}
self.SeeUnits.Remove(leave.Id);
if (leave.Unit.Type == UnitType.Player)
{
self.SeePlayers.Remove(leave.Id);
}
leave.BeSeeUnits.Remove(self.Id);
if (self.Unit.Type == UnitType.Player)
{
leave.BeSeePlayers.Remove(self.Id);
}
EventSystem.Instance.Publish(self.Scene(), new EventType.UnitLeaveSightRange { A = self, B = leave });
}
/// <summary>
/// 是否在Unit视野范围内
/// </summary>
/// <param name="self"></param>
/// <param name="unitId"></param>
/// <returns></returns>
public static bool IsBeSee(this AOIEntity self, long unitId)
{
return self.BeSeePlayers.ContainsKey(unitId);
}
}
}

View File

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

View File

@@ -0,0 +1,41 @@
using System.Collections.Generic;
namespace ET.Server
{
[FriendOf(typeof(AOIEntity))]
public static partial class AOIHelper
{
public static long CreateCellId(int x, int y)
{
return (long) ((ulong) x << 32) | (uint) y;
}
public static void CalcEnterAndLeaveCell(AOIEntity aoiEntity, int cellX, int cellY, HashSet<long> enterCell, HashSet<long> leaveCell)
{
enterCell.Clear();
leaveCell.Clear();
int r = (aoiEntity.ViewDistance - 1) / AOIManagerComponent.CellSize + 1;
int leaveR = r;
if (aoiEntity.Unit.Type == UnitType.Player)
{
leaveR += 1;
}
for (int i = cellX - leaveR; i <= cellX + leaveR; ++i)
{
for (int j = cellY - leaveR; j <= cellY + leaveR; ++j)
{
long cellId = CreateCellId(i, j);
leaveCell.Add(cellId);
if (i > cellX + r || i < cellX - r || j > cellY + r || j < cellY - r)
{
continue;
}
enterCell.Add(cellId);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,187 @@
using System.Collections.Generic;
namespace ET.Server
{
[FriendOf(typeof(AOIManagerComponent))]
[FriendOf(typeof(AOIEntity))]
[FriendOf(typeof(Cell))]
public static partial class AOIManagerComponentSystem
{
public static void Add(this AOIManagerComponent self, AOIEntity aoiEntity, float x, float y)
{
int cellX = (int)(x * 1000) / AOIManagerComponent.CellSize;
int cellY = (int)(y * 1000) / AOIManagerComponent.CellSize;
if (aoiEntity.ViewDistance == 0)
{
aoiEntity.ViewDistance = 1;
}
AOIHelper.CalcEnterAndLeaveCell(aoiEntity, cellX, cellY, aoiEntity.SubEnterCells, aoiEntity.SubLeaveCells);
// 遍历EnterCell
foreach (long cellId in aoiEntity.SubEnterCells)
{
Cell cell = self.GetCell(cellId);
aoiEntity.SubEnter(cell);
}
// 遍历LeaveCell
foreach (long cellId in aoiEntity.SubLeaveCells)
{
Cell cell = self.GetCell(cellId);
aoiEntity.SubLeave(cell);
}
// 自己加入的Cell
Cell selfCell = self.GetCell(AOIHelper.CreateCellId(cellX, cellY));
aoiEntity.Cell = selfCell;
selfCell.Add(aoiEntity);
// 通知订阅该Cell Enter的Unit
foreach (KeyValuePair<long, AOIEntity> kv in selfCell.SubsEnterEntities)
{
kv.Value.EnterSight(aoiEntity);
}
}
public static void Remove(this AOIManagerComponent self, AOIEntity aoiEntity)
{
if (aoiEntity.Cell == null)
{
return;
}
// 通知订阅该Cell Leave的Unit
aoiEntity.Cell.Remove(aoiEntity);
foreach (KeyValuePair<long, AOIEntity> kv in aoiEntity.Cell.SubsLeaveEntities)
{
kv.Value.LeaveSight(aoiEntity);
}
// 通知自己订阅的Enter Cell清理自己
foreach (long cellId in aoiEntity.SubEnterCells)
{
Cell cell = self.GetCell(cellId);
aoiEntity.UnSubEnter(cell);
}
foreach (long cellId in aoiEntity.SubLeaveCells)
{
Cell cell = self.GetCell(cellId);
aoiEntity.UnSubLeave(cell);
}
// 检查
if (aoiEntity.SeeUnits.Count > 1)
{
Log.Error($"aoiEntity has see units: {aoiEntity.SeeUnits.Count}");
}
if (aoiEntity.BeSeeUnits.Count > 1)
{
Log.Error($"aoiEntity has beSee units: {aoiEntity.BeSeeUnits.Count}");
}
}
private static Cell GetCell(this AOIManagerComponent self, long cellId)
{
Cell cell = self.GetChild<Cell>(cellId);
if (cell == null)
{
cell = self.AddChildWithId<Cell>(cellId);
}
return cell;
}
public static void Move(AOIEntity aoiEntity, Cell newCell, Cell preCell)
{
aoiEntity.Cell = newCell;
preCell.Remove(aoiEntity);
newCell.Add(aoiEntity);
// 通知订阅该newCell Enter的Unit
foreach (KeyValuePair<long, AOIEntity> kv in newCell.SubsEnterEntities)
{
if (kv.Value.SubEnterCells.Contains(preCell.Id))
{
continue;
}
kv.Value.EnterSight(aoiEntity);
}
// 通知订阅preCell leave的Unit
foreach (KeyValuePair<long, AOIEntity> kv in preCell.SubsLeaveEntities)
{
// 如果新的cell仍然在对方订阅的subleave中
if (kv.Value.SubLeaveCells.Contains(newCell.Id))
{
continue;
}
kv.Value.LeaveSight(aoiEntity);
}
}
public static void Move(this AOIManagerComponent self, AOIEntity aoiEntity, int cellX, int cellY)
{
long newCellId = AOIHelper.CreateCellId(cellX, cellY);
if (aoiEntity.Cell.Id == newCellId) // cell没有变化
{
return;
}
// 自己加入新的Cell
Cell newCell = self.GetCell(newCellId);
Move(aoiEntity, newCell, aoiEntity.Cell);
AOIHelper.CalcEnterAndLeaveCell(aoiEntity, cellX, cellY, aoiEntity.enterHashSet, aoiEntity.leaveHashSet);
// 算出自己leave新Cell
foreach (long cellId in aoiEntity.leaveHashSet)
{
if (aoiEntity.SubLeaveCells.Contains(cellId))
{
continue;
}
Cell cell = self.GetCell(cellId);
aoiEntity.SubLeave(cell);
}
// 算出需要通知离开的Cell
aoiEntity.SubLeaveCells.ExceptWith(aoiEntity.leaveHashSet);
foreach (long cellId in aoiEntity.SubLeaveCells)
{
Cell cell = self.GetCell(cellId);
aoiEntity.UnSubLeave(cell);
}
// 这里交换两个HashSet,提高性能
ObjectHelper.Swap(ref aoiEntity.SubLeaveCells, ref aoiEntity.leaveHashSet);
// 算出自己看到的新Cell
foreach (long cellId in aoiEntity.enterHashSet)
{
if (aoiEntity.SubEnterCells.Contains(cellId))
{
continue;
}
Cell cell = self.GetCell(cellId);
aoiEntity.SubEnter(cell);
}
// 离开的Enter
aoiEntity.SubEnterCells.ExceptWith(aoiEntity.enterHashSet);
foreach (long cellId in aoiEntity.SubEnterCells)
{
Cell cell = self.GetCell(cellId);
aoiEntity.UnSubEnter(cell);
}
// 这里交换两个HashSet,提高性能
ObjectHelper.Swap(ref aoiEntity.SubEnterCells, ref aoiEntity.enterHashSet);
}
}
}

View File

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

View File

@@ -0,0 +1,10 @@
namespace ET.Server
{
public static partial class AOISeeCheckHelper
{
public static bool IsCanSee(AOIEntity a, AOIEntity b)
{
return true;
}
}
}

View File

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

View File

@@ -0,0 +1,56 @@
using System.Collections.Generic;
using System.Text;
namespace ET.Server
{
[EntitySystemOf(typeof(Cell))]
[FriendOf(typeof(Cell))]
public static partial class CellSystem
{
[EntitySystem]
private static void Awake(this ET.Server.Cell self)
{
}
[EntitySystem]
private static void Destroy(this Cell self)
{
self.AOIUnits.Clear();
self.SubsEnterEntities.Clear();
self.SubsLeaveEntities.Clear();
}
public static void Add(this Cell self, AOIEntity aoiEntity)
{
self.AOIUnits.Add(aoiEntity.Id, aoiEntity);
}
public static void Remove(this Cell self, AOIEntity aoiEntity)
{
self.AOIUnits.Remove(aoiEntity.Id);
}
public static string CellIdToString(this long cellId)
{
int y = (int)(cellId & 0xffffffff);
int x = (int)((ulong)cellId >> 32);
return $"{x}:{y}";
}
public static string CellIdToString(this HashSet<long> cellIds)
{
StringBuilder sb = new StringBuilder();
foreach (long cellId in cellIds)
{
sb.Append(cellId.CellIdToString());
sb.Append(",");
}
return sb.ToString();
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,310 @@
using System;
using System.IO;
using MongoDB.Bson;
namespace ET.Server
{
[EntitySystemOf(typeof(ActorLocationSenderOneType))]
[FriendOf(typeof(ActorLocationSenderOneType))]
[FriendOf(typeof(ActorLocationSender))]
public static partial class ActorLocationSenderComponentSystem
{
[Invoke(TimerInvokeType.ActorLocationSenderChecker)]
public class ActorLocationSenderChecker: ATimer<ActorLocationSenderOneType>
{
protected override void Run(ActorLocationSenderOneType self)
{
try
{
self.Check();
}
catch (Exception e)
{
Log.Error($"move timer error: {self.Id}\n{e}");
}
}
}
[EntitySystem]
private static void Awake(this ActorLocationSenderOneType self, int locationType)
{
self.LocationType = locationType;
// 每10s扫描一次过期的actorproxy进行回收,过期时间是2分钟
// 可能由于bug或者进程挂掉导致ActorLocationSender发送的消息没有确认结果无法自动删除每一分钟清理一次这种ActorLocationSender
self.CheckTimer = self.Fiber().TimerComponent.NewRepeatedTimer(10 * 1000, TimerInvokeType.ActorLocationSenderChecker, self);
}
[EntitySystem]
private static void Destroy(this ActorLocationSenderOneType self)
{
self.Fiber().TimerComponent?.Remove(ref self.CheckTimer);
}
private static void Check(this ActorLocationSenderOneType self)
{
using (ListComponent<long> list = ListComponent<long>.Create())
{
long timeNow = self.Fiber().TimeInfo.ServerNow();
foreach ((long key, Entity value) in self.Children)
{
ActorLocationSender actorLocationMessageSender = (ActorLocationSender) value;
if (timeNow > actorLocationMessageSender.LastSendOrRecvTime + ActorLocationSenderOneType.TIMEOUT_TIME)
{
list.Add(key);
}
}
foreach (long id in list)
{
self.Remove(id);
}
}
}
private static ActorLocationSender GetOrCreate(this ActorLocationSenderOneType self, long id)
{
if (id == 0)
{
throw new Exception($"actor id is 0");
}
if (self.Children.TryGetValue(id, out Entity actorLocationSender))
{
return (ActorLocationSender) actorLocationSender;
}
actorLocationSender = self.AddChildWithId<ActorLocationSender>(id);
return (ActorLocationSender) actorLocationSender;
}
// 有需要主动删除actorMessageSender的需求比如断线重连玩家登录了不同的Gate这时候需要通知map删掉之前的actorMessageSender
// 然后重新创建新的重新请求新的ActorId
public static void Remove(this ActorLocationSenderOneType self, long id)
{
if (!self.Children.TryGetValue(id, out Entity actorMessageSender))
{
return;
}
actorMessageSender.Dispose();
}
// 发给不会改变位置的actorlocation用这个这种actor消息不会阻塞发送队列性能更高
// 发送过去找不到actor不会重试,用此方法你得保证actor提前注册好了location
public static void Send(this ActorLocationSenderOneType self, long entityId, IActorMessage message)
{
self.SendInner(entityId, message).Coroutine();
}
private static async ETTask SendInner(this ActorLocationSenderOneType self, long entityId, IActorMessage message)
{
ActorLocationSender actorLocationSender = self.GetOrCreate(entityId);
Scene root = self.Root();
if (actorLocationSender.ActorId != default)
{
actorLocationSender.LastSendOrRecvTime = self.Fiber().TimeInfo.ServerNow();
root.GetComponent<ActorSenderComponent>().Send(actorLocationSender.ActorId, message);
return;
}
long instanceId = actorLocationSender.InstanceId;
int coroutineLockType = (self.LocationType << 16) | CoroutineLockType.ActorLocationSender;
using (await root.Fiber.CoroutineLockComponent.Wait(coroutineLockType, entityId))
{
if (actorLocationSender.InstanceId != instanceId)
{
throw new RpcException(ErrorCore.ERR_ActorTimeout, $"{message}");
}
if (actorLocationSender.ActorId == default)
{
actorLocationSender.ActorId = await root.GetComponent<LocationProxyComponent>().Get(self.LocationType, actorLocationSender.Id);
if (actorLocationSender.InstanceId != instanceId)
{
throw new RpcException(ErrorCore.ERR_ActorLocationSenderTimeout2, $"{message}");
}
}
actorLocationSender.LastSendOrRecvTime = self.Fiber().TimeInfo.ServerNow();
root.GetComponent<ActorSenderComponent>().Send(actorLocationSender.ActorId, message);
}
}
// 发给不会改变位置的actorlocation用这个这种actor消息不会阻塞发送队列性能更高发送过去找不到actor不会重试
// 发送过去找不到actor不会重试,用此方法你得保证actor提前注册好了location
public static async ETTask<IActorResponse> Call(this ActorLocationSenderOneType self, long entityId, IActorRequest request)
{
ActorLocationSender actorLocationSender = self.GetOrCreate(entityId);
Scene root = self.Root();
if (actorLocationSender.ActorId != default)
{
actorLocationSender.LastSendOrRecvTime = self.Fiber().TimeInfo.ServerNow();
return await root.GetComponent<ActorSenderComponent>().Call(actorLocationSender.ActorId, request);
}
long instanceId = actorLocationSender.InstanceId;
int coroutineLockType = (self.LocationType << 16) | CoroutineLockType.ActorLocationSender;
using (await root.Fiber.CoroutineLockComponent.Wait(coroutineLockType, entityId))
{
if (actorLocationSender.InstanceId != instanceId)
{
throw new RpcException(ErrorCore.ERR_ActorTimeout, $"{request}");
}
if (actorLocationSender.ActorId == default)
{
actorLocationSender.ActorId = await root.GetComponent<LocationProxyComponent>().Get(self.LocationType, actorLocationSender.Id);
if (actorLocationSender.InstanceId != instanceId)
{
throw new RpcException(ErrorCore.ERR_ActorLocationSenderTimeout2, $"{request}");
}
}
}
actorLocationSender.LastSendOrRecvTime = self.Fiber().TimeInfo.ServerNow();
return await root.GetComponent<ActorSenderComponent>().Call(actorLocationSender.ActorId, request);
}
public static void Send(this ActorLocationSenderOneType self, long entityId, IActorLocationMessage message)
{
self.Call(entityId, message).Coroutine();
}
public static async ETTask<IActorResponse> Call(this ActorLocationSenderOneType self, long entityId, IActorLocationRequest iActorRequest)
{
ActorLocationSender actorLocationSender = self.GetOrCreate(entityId);
Scene root = self.Root();
int rpcId = root.GetComponent<ActorSenderComponent>().GetRpcId();
iActorRequest.RpcId = rpcId;
long actorLocationSenderInstanceId = actorLocationSender.InstanceId;
int coroutineLockType = (self.LocationType << 16) | CoroutineLockType.ActorLocationSender;
using (await root.Fiber.CoroutineLockComponent.Wait(coroutineLockType, entityId))
{
if (actorLocationSender.InstanceId != actorLocationSenderInstanceId)
{
throw new RpcException(ErrorCore.ERR_ActorTimeout, $"{iActorRequest}");
}
// 队列中没处理的消息返回跟上个消息一样的报错
if (actorLocationSender.Error == ErrorCore.ERR_NotFoundActor)
{
return ActorHelper.CreateResponse(iActorRequest, actorLocationSender.Error);
}
try
{
return await self.CallInner(actorLocationSender, rpcId, iActorRequest);
}
catch (RpcException)
{
self.Remove(actorLocationSender.Id);
throw;
}
catch (Exception e)
{
self.Remove(actorLocationSender.Id);
throw new Exception($"{iActorRequest}", e);
}
}
}
private static async ETTask<IActorResponse> CallInner(this ActorLocationSenderOneType self, ActorLocationSender actorLocationSender, int rpcId, IActorRequest iActorRequest)
{
int failTimes = 0;
long instanceId = actorLocationSender.InstanceId;
actorLocationSender.LastSendOrRecvTime = self.Fiber().TimeInfo.ServerNow();
Scene root = self.Root();
while (true)
{
if (actorLocationSender.ActorId == default)
{
actorLocationSender.ActorId = await root.GetComponent<LocationProxyComponent>().Get(self.LocationType, actorLocationSender.Id);
if (actorLocationSender.InstanceId != instanceId)
{
throw new RpcException(ErrorCore.ERR_ActorLocationSenderTimeout2, $"{iActorRequest}");
}
}
if (actorLocationSender.ActorId == default)
{
actorLocationSender.Error = ErrorCore.ERR_NotFoundActor;
return ActorHelper.CreateResponse(iActorRequest, ErrorCore.ERR_NotFoundActor);
}
IActorResponse response = await root.GetComponent<ActorSenderComponent>().Call(actorLocationSender.ActorId, rpcId, iActorRequest, needException: false);
if (actorLocationSender.InstanceId != instanceId)
{
throw new RpcException(ErrorCore.ERR_ActorLocationSenderTimeout3, $"{iActorRequest}");
}
switch (response.Error)
{
case ErrorCore.ERR_NotFoundActor:
{
// 如果没找到Actor,重试
++failTimes;
if (failTimes > 20)
{
Log.Debug($"actor send message fail, actorid: {actorLocationSender.Id} {iActorRequest}");
actorLocationSender.Error = ErrorCore.ERR_NotFoundActor;
// 这里不能删除actor要让后面等待发送的消息也返回ERR_NotFoundActor直到超时删除
return response;
}
// 等待0.5s再发送
await root.Fiber.TimerComponent.WaitAsync(500);
if (actorLocationSender.InstanceId != instanceId)
{
throw new RpcException(ErrorCore.ERR_ActorLocationSenderTimeout4, $"{iActorRequest}");
}
actorLocationSender.ActorId = default;
continue;
}
case ErrorCore.ERR_ActorTimeout:
{
throw new RpcException(response.Error, $"{iActorRequest}");
}
}
if (ErrorCore.IsRpcNeedThrowException(response.Error))
{
throw new RpcException(response.Error, $"Message: {response.Message} Request: {iActorRequest}");
}
return response;
}
}
}
[EntitySystemOf(typeof(ActorLocationSenderComponent))]
[FriendOf(typeof (ActorLocationSenderComponent))]
public static partial class ActorLocationSenderManagerComponentSystem
{
[EntitySystem]
private static void Awake(this ActorLocationSenderComponent self)
{
for (int i = 0; i < self.ActorLocationSenderComponents.Length; ++i)
{
self.ActorLocationSenderComponents[i] = self.AddChild<ActorLocationSenderOneType, int>(i);
}
}
public static ActorLocationSenderOneType Get(this ActorLocationSenderComponent self, int locationType)
{
return self.ActorLocationSenderComponents[locationType];
}
}
}

View File

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

View File

@@ -0,0 +1,26 @@
using System;
namespace ET.Server
{
[EntitySystemOf(typeof(ActorLocationSender))]
[FriendOf(typeof(ActorLocationSender))]
public static partial class ActorLocationSenderSystem
{
[EntitySystem]
private static void Awake(this ActorLocationSender self)
{
self.LastSendOrRecvTime = self.Fiber().TimeInfo.ServerNow();
self.ActorId = default;
self.Error = 0;
}
[EntitySystem]
private static void Destroy(this ActorLocationSender self)
{
Log.Debug($"actor location remove: {self.Id}");
self.LastSendOrRecvTime = 0;
self.ActorId = default;
self.Error = 0;
}
}
}

View File

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

View File

@@ -0,0 +1,96 @@
using System;
namespace ET.Server
{
[EnableClass]
public abstract class ActorMessageLocationHandler<E, Message>: IMActorHandler where E : Entity where Message : class, IActorLocationMessage
{
protected abstract ETTask Run(E entity, Message message);
public async ETTask Handle(Entity entity, Address fromAddress, MessageObject actorMessage)
{
if (actorMessage is not Message message)
{
Log.Error($"消息类型转换错误: {actorMessage.GetType().FullName} to {typeof (Message).Name}");
return;
}
if (entity is not E e)
{
Log.Error($"Actor类型转换错误: {entity.GetType().FullName} to {typeof (E).FullName} --{typeof (Message).FullName}");
return;
}
ActorResponse response = new() {RpcId = message.RpcId};
entity.Root().GetComponent<ActorInnerComponent>().Reply(fromAddress, response);
await this.Run(e, message);
}
public Type GetRequestType()
{
return typeof (Message);
}
public Type GetResponseType()
{
return typeof (ActorResponse);
}
}
[EnableClass]
public abstract class ActorMessageLocationHandler<E, Request, Response>: IMActorHandler where E : Entity where Request : MessageObject, IActorLocationRequest where Response : MessageObject, IActorLocationResponse
{
protected abstract ETTask Run(E unit, Request request, Response response);
public async ETTask Handle(Entity entity, Address fromAddress, MessageObject actorMessage)
{
try
{
if (actorMessage is not Request request)
{
Log.Error($"消息类型转换错误: {actorMessage.GetType().FullName} to {typeof (Request).Name}");
return;
}
if (entity is not E ee)
{
Log.Error($"Actor类型转换错误: {entity.GetType().FullName} to {typeof (E).FullName} --{typeof (Request).FullName}");
return;
}
int rpcId = request.RpcId;
Response response = ObjectPool.Instance.Fetch<Response>();
try
{
await this.Run(ee, request, response);
}
catch (Exception exception)
{
Log.Error(exception);
response.Error = ErrorCore.ERR_RpcFail;
response.Message = exception.ToString();
}
response.RpcId = rpcId;
entity.Root().GetComponent<ActorInnerComponent>().Reply(fromAddress, response);
}
catch (Exception e)
{
throw new Exception($"解释消息失败: {actorMessage.GetType().FullName}", e);
}
}
public Type GetRequestType()
{
return typeof (Request);
}
public Type GetResponseType()
{
return typeof (Response);
}
}
}

View File

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

View File

@@ -0,0 +1,21 @@
using System.Net;
namespace ET.Server
{
[Invoke((long)SceneType.Location)]
public class FiberInit_Location: AInvokeHandler<FiberInit, ETTask>
{
public override async ETTask Handle(FiberInit fiberInit)
{
Scene root = fiberInit.Fiber.Root;
root.AddComponent<MailBoxComponent, MailBoxType>(MailBoxType.UnOrderedMessage);
root.AddComponent<TimerComponent>();
root.AddComponent<CoroutineLockComponent>();
root.AddComponent<ActorInnerComponent>();
root.AddComponent<ActorSenderComponent>();
root.AddComponent<LocationManagerComoponent>();
await ETTask.CompletedTask;
}
}
}

View File

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

View File

@@ -0,0 +1,137 @@
using System;
namespace ET.Server
{
[EntitySystemOf(typeof(LockInfo))]
[FriendOf(typeof(LockInfo))]
public static partial class LockInfoSystem
{
[EntitySystem]
private static void Awake(this LockInfo self, ActorId lockActorId, CoroutineLock coroutineLock)
{
self.CoroutineLock = coroutineLock;
self.LockActorId = lockActorId;
}
[EntitySystem]
private static void Destroy(this LockInfo self)
{
self.CoroutineLock.Dispose();
self.LockActorId = default;
}
}
[EntitySystemOf(typeof(LocationOneType))]
[FriendOf(typeof(LocationOneType))]
[FriendOf(typeof(LockInfo))]
public static partial class LocationOneTypeSystem
{
[EntitySystem]
private static void Awake(this LocationOneType self, int locationType)
{
self.LocationType = locationType;
}
public static async ETTask Add(this LocationOneType self, long key, ActorId instanceId)
{
int coroutineLockType = (self.LocationType << 16) | CoroutineLockType.Location;
using (await self.Fiber().CoroutineLockComponent.Wait(coroutineLockType, key))
{
self.locations[key] = instanceId;
Log.Info($"location add key: {key} instanceId: {instanceId}");
}
}
public static async ETTask Remove(this LocationOneType self, long key)
{
int coroutineLockType = (self.LocationType << 16) | CoroutineLockType.Location;
using (await self.Fiber().CoroutineLockComponent.Wait(coroutineLockType, key))
{
self.locations.Remove(key);
Log.Info($"location remove key: {key}");
}
}
public static async ETTask Lock(this LocationOneType self, long key, ActorId actorId, int time = 0)
{
int coroutineLockType = (self.LocationType << 16) | CoroutineLockType.Location;
CoroutineLock coroutineLock = await self.Fiber().CoroutineLockComponent.Wait(coroutineLockType, key);
LockInfo lockInfo = self.AddChild<LockInfo, ActorId, CoroutineLock>(actorId, coroutineLock);
self.lockInfos.Add(key, lockInfo);
Log.Info($"location lock key: {key} instanceId: {actorId}");
if (time > 0)
{
async ETTask TimeWaitAsync()
{
long lockInfoInstanceId = lockInfo.InstanceId;
await self.Fiber().TimerComponent.WaitAsync(time);
if (lockInfo.InstanceId != lockInfoInstanceId)
{
return;
}
Log.Info($"location timeout unlock key: {key} instanceId: {actorId} newInstanceId: {actorId}");
self.UnLock(key, actorId, actorId);
}
TimeWaitAsync().Coroutine();
}
}
public static void UnLock(this LocationOneType self, long key, ActorId oldActorId, ActorId newInstanceId)
{
if (!self.lockInfos.TryGetValue(key, out LockInfo lockInfo))
{
Log.Error($"location unlock not found key: {key} {oldActorId}");
return;
}
if (oldActorId != lockInfo.LockActorId)
{
Log.Error($"location unlock oldInstanceId is different: {key} {oldActorId}");
return;
}
Log.Info($"location unlock key: {key} instanceId: {oldActorId} newInstanceId: {newInstanceId}");
self.locations[key] = newInstanceId;
self.lockInfos.Remove(key);
// 解锁
lockInfo.Dispose();
}
public static async ETTask<ActorId> Get(this LocationOneType self, long key)
{
int coroutineLockType = (self.LocationType << 16) | CoroutineLockType.Location;
using (await self.Fiber().CoroutineLockComponent.Wait(coroutineLockType, key))
{
self.locations.TryGetValue(key, out ActorId actorId);
Log.Info($"location get key: {key} actorId: {actorId}");
return actorId;
}
}
}
[EntitySystemOf(typeof(LocationManagerComoponent))]
[FriendOf(typeof (LocationManagerComoponent))]
public static partial class LocationComoponentSystem
{
[EntitySystem]
private static void Awake(this LocationManagerComoponent self)
{
for (int i = 0; i < self.LocationOneTypes.Length; ++i)
{
self.LocationOneTypes[i] = self.AddChild<LocationOneType, int>(i);
}
}
public static LocationOneType Get(this LocationManagerComoponent self, int locationType)
{
return self.LocationOneTypes[locationType];
}
}
}

View File

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

View File

@@ -0,0 +1,64 @@
using System;
namespace ET.Server
{
public static partial class LocationProxyComponentSystem
{
private static ActorId GetLocationSceneId(long key)
{
return StartSceneConfigCategory.Instance.LocationConfig.ActorId;
}
public static async ETTask Add(this LocationProxyComponent self, int type, long key, ActorId actorId)
{
Log.Info($"location proxy add {key}, {actorId} {self.Fiber().TimeInfo.ServerNow()}");
await self.Root().GetComponent<ActorSenderComponent>().Call(GetLocationSceneId(key),
new ObjectAddRequest() { Type = type, Key = key, ActorId = actorId });
}
public static async ETTask Lock(this LocationProxyComponent self, int type, long key, ActorId actorId, int time = 60000)
{
Log.Info($"location proxy lock {key}, {actorId} {self.Fiber().TimeInfo.ServerNow()}");
await self.Root().GetComponent<ActorSenderComponent>().Call(GetLocationSceneId(key),
new ObjectLockRequest() { Type = type, Key = key, ActorId = actorId, Time = time });
}
public static async ETTask UnLock(this LocationProxyComponent self, int type, long key, ActorId oldActorId, ActorId newActorId)
{
Log.Info($"location proxy unlock {key}, {newActorId} {self.Fiber().TimeInfo.ServerNow()}");
await self.Root().GetComponent<ActorSenderComponent>().Call(GetLocationSceneId(key),
new ObjectUnLockRequest() { Type = type, Key = key, OldActorId = oldActorId, NewActorId = newActorId });
}
public static async ETTask Remove(this LocationProxyComponent self, int type, long key)
{
Log.Info($"location proxy add {key}, {self.Fiber().TimeInfo.ServerNow()}");
await self.Root().GetComponent<ActorSenderComponent>().Call(GetLocationSceneId(key),
new ObjectRemoveRequest() { Type = type, Key = key });
}
public static async ETTask<ActorId> Get(this LocationProxyComponent self, int type, long key)
{
if (key == 0)
{
throw new Exception($"get location key 0");
}
// location server配置到共享区一个大战区可以配置N多个location server,这里暂时为1
ObjectGetResponse response =
(ObjectGetResponse) await self.Root().GetComponent<ActorSenderComponent>().Call(GetLocationSceneId(key),
new ObjectGetRequest() { Type = type, Key = key });
return response.ActorId;
}
public static async ETTask AddLocation(this Entity self, int type)
{
await self.Root().GetComponent<LocationProxyComponent>().Add(type, self.Id, self.GetActorId());
}
public static async ETTask RemoveLocation(this Entity self, int type)
{
await self.Root().GetComponent<LocationProxyComponent>().Remove(type, self.Id);
}
}
}

View File

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

View File

@@ -0,0 +1,13 @@
using System;
namespace ET.Server
{
[ActorMessageHandler(SceneType.Location)]
public class ObjectAddRequestHandler: ActorMessageHandler<Scene, ObjectAddRequest, ObjectAddResponse>
{
protected override async ETTask Run(Scene scene, ObjectAddRequest request, ObjectAddResponse response)
{
await scene.GetComponent<LocationManagerComoponent>().Get(request.Type).Add(request.Key, request.ActorId);
}
}
}

View File

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

View File

@@ -0,0 +1,13 @@
using System;
namespace ET.Server
{
[ActorMessageHandler(SceneType.Location)]
public class ObjectGetRequestHandler: ActorMessageHandler<Scene, ObjectGetRequest, ObjectGetResponse>
{
protected override async ETTask Run(Scene scene, ObjectGetRequest request, ObjectGetResponse response)
{
response.ActorId = await scene.GetComponent<LocationManagerComoponent>().Get(request.Type).Get(request.Key);
}
}
}

View File

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

View File

@@ -0,0 +1,13 @@
using System;
namespace ET.Server
{
[ActorMessageHandler(SceneType.Location)]
public class ObjectLockRequestHandler: ActorMessageHandler<Scene, ObjectLockRequest, ObjectLockResponse>
{
protected override async ETTask Run(Scene scene, ObjectLockRequest request, ObjectLockResponse response)
{
await scene.GetComponent<LocationManagerComoponent>().Get(request.Type).Lock(request.Key, request.ActorId, request.Time);
}
}
}

View File

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

View File

@@ -0,0 +1,13 @@
using System;
namespace ET.Server
{
[ActorMessageHandler(SceneType.Location)]
public class ObjectRemoveRequestHandler: ActorMessageHandler<Scene, ObjectRemoveRequest, ObjectRemoveResponse>
{
protected override async ETTask Run(Scene scene, ObjectRemoveRequest request, ObjectRemoveResponse response)
{
await scene.GetComponent<LocationManagerComoponent>().Get(request.Type).Remove(request.Key);
}
}
}

View File

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

View File

@@ -0,0 +1,15 @@
using System;
namespace ET.Server
{
[ActorMessageHandler(SceneType.Location)]
public class ObjectUnLockRequestHandler: ActorMessageHandler<Scene, ObjectUnLockRequest, ObjectUnLockResponse>
{
protected override async ETTask Run(Scene scene, ObjectUnLockRequest request, ObjectUnLockResponse response)
{
scene.GetComponent<LocationManagerComoponent>().Get(request.Type).UnLock(request.Key, request.OldActorId, request.NewActorId);
await ETTask.CompletedTask;
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,251 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using MongoDB.Driver;
namespace ET.Server
{
[EntitySystemOf(typeof(DBComponent))]
[FriendOf(typeof(DBComponent))]
public static partial class DBComponentSystem
{
[EntitySystem]
private static void Awake(this DBComponent self, string dbConnection, string dbName, int zone)
{
self.mongoClient = new MongoClient(dbConnection);
self.database = self.mongoClient.GetDatabase(dbName);
}
private static IMongoCollection<T> GetCollection<T>(this DBComponent self, string collection = null)
{
return self.database.GetCollection<T>(collection ?? typeof (T).Name);
}
private static IMongoCollection<Entity> GetCollection(this DBComponent self, string name)
{
return self.database.GetCollection<Entity>(name);
}
#region Query
public static async ETTask<T> Query<T>(this DBComponent self, long id, string collection = null) where T : Entity
{
using (await self.Fiber().CoroutineLockComponent.Wait(CoroutineLockType.DB, id % DBComponent.TaskCount))
{
IAsyncCursor<T> cursor = await self.GetCollection<T>(collection).FindAsync(d => d.Id == id);
return await cursor.FirstOrDefaultAsync();
}
}
public static async ETTask<List<T>> Query<T>(this DBComponent self, Expression<Func<T, bool>> filter, string collection = null)
where T : Entity
{
using (await self.Fiber().CoroutineLockComponent.Wait(CoroutineLockType.DB, RandomGenerator.RandInt64() % DBComponent.TaskCount))
{
IAsyncCursor<T> cursor = await self.GetCollection<T>(collection).FindAsync(filter);
return await cursor.ToListAsync();
}
}
public static async ETTask<List<T>> Query<T>(this DBComponent self, long taskId, Expression<Func<T, bool>> filter, string collection = null)
where T : Entity
{
using (await self.Fiber().CoroutineLockComponent.Wait(CoroutineLockType.DB, taskId % DBComponent.TaskCount))
{
IAsyncCursor<T> cursor = await self.GetCollection<T>(collection).FindAsync(filter);
return await cursor.ToListAsync();
}
}
public static async ETTask Query(this DBComponent self, long id, List<string> collectionNames, List<Entity> result)
{
if (collectionNames == null || collectionNames.Count == 0)
{
return;
}
using (await self.Fiber().CoroutineLockComponent.Wait(CoroutineLockType.DB, id % DBComponent.TaskCount))
{
foreach (string collectionName in collectionNames)
{
IAsyncCursor<Entity> cursor = await self.GetCollection(collectionName).FindAsync(d => d.Id == id);
Entity e = await cursor.FirstOrDefaultAsync();
if (e == null)
{
continue;
}
result.Add(e);
}
}
}
public static async ETTask<List<T>> QueryJson<T>(this DBComponent self, string json, string collection = null) where T : Entity
{
using (await self.Fiber().CoroutineLockComponent.Wait(CoroutineLockType.DB, RandomGenerator.RandInt64() % DBComponent.TaskCount))
{
FilterDefinition<T> filterDefinition = new JsonFilterDefinition<T>(json);
IAsyncCursor<T> cursor = await self.GetCollection<T>(collection).FindAsync(filterDefinition);
return await cursor.ToListAsync();
}
}
public static async ETTask<List<T>> QueryJson<T>(this DBComponent self, long taskId, string json, string collection = null) where T : Entity
{
using (await self.Fiber().CoroutineLockComponent.Wait(CoroutineLockType.DB, RandomGenerator.RandInt64() % DBComponent.TaskCount))
{
FilterDefinition<T> filterDefinition = new JsonFilterDefinition<T>(json);
IAsyncCursor<T> cursor = await self.GetCollection<T>(collection).FindAsync(filterDefinition);
return await cursor.ToListAsync();
}
}
#endregion
#region Insert
public static async ETTask InsertBatch<T>(this DBComponent self, IEnumerable<T> list, string collection = null) where T: Entity
{
if (collection == null)
{
collection = typeof (T).Name;
}
using (await self.Fiber().CoroutineLockComponent.Wait(CoroutineLockType.DB, RandomGenerator.RandInt64() % DBComponent.TaskCount))
{
await self.GetCollection(collection).InsertManyAsync(list);
}
}
#endregion
#region Save
public static async ETTask Save<T>(this DBComponent self, T entity, string collection = null) where T : Entity
{
if (entity == null)
{
Log.Error($"save entity is null: {typeof (T).Name}");
return;
}
if (collection == null)
{
collection = entity.GetType().FullName;
}
using (await self.Fiber().CoroutineLockComponent.Wait(CoroutineLockType.DB, entity.Id % DBComponent.TaskCount))
{
await self.GetCollection(collection).ReplaceOneAsync(d => d.Id == entity.Id, entity, new ReplaceOptions { IsUpsert = true });
}
}
public static async ETTask Save<T>(this DBComponent self, long taskId, T entity, string collection = null) where T : Entity
{
if (entity == null)
{
Log.Error($"save entity is null: {typeof (T).Name}");
return;
}
if (collection == null)
{
collection = entity.GetType().FullName;
}
using (await self.Fiber().CoroutineLockComponent.Wait(CoroutineLockType.DB, taskId % DBComponent.TaskCount))
{
await self.GetCollection(collection).ReplaceOneAsync(d => d.Id == entity.Id, entity, new ReplaceOptions { IsUpsert = true });
}
}
public static async ETTask Save(this DBComponent self, long id, List<Entity> entities)
{
if (entities == null)
{
Log.Error($"save entity is null");
return;
}
using (await self.Fiber().CoroutineLockComponent.Wait(CoroutineLockType.DB, id % DBComponent.TaskCount))
{
foreach (Entity entity in entities)
{
if (entity == null)
{
continue;
}
await self.GetCollection(entity.GetType().FullName)
.ReplaceOneAsync(d => d.Id == entity.Id, entity, new ReplaceOptions { IsUpsert = true });
}
}
}
public static async ETTask SaveNotWait<T>(this DBComponent self, T entity, long taskId = 0, string collection = null) where T : Entity
{
if (taskId == 0)
{
await self.Save(entity, collection);
return;
}
await self.Save(taskId, entity, collection);
}
#endregion
#region Remove
public static async ETTask<long> Remove<T>(this DBComponent self, long id, string collection = null) where T : Entity
{
using (await self.Fiber().CoroutineLockComponent.Wait(CoroutineLockType.DB, id % DBComponent.TaskCount))
{
DeleteResult result = await self.GetCollection<T>(collection).DeleteOneAsync(d => d.Id == id);
return result.DeletedCount;
}
}
public static async ETTask<long> Remove<T>(this DBComponent self, long taskId, long id, string collection = null) where T : Entity
{
using (await self.Fiber().CoroutineLockComponent.Wait(CoroutineLockType.DB, taskId % DBComponent.TaskCount))
{
DeleteResult result = await self.GetCollection<T>(collection).DeleteOneAsync(d => d.Id == id);
return result.DeletedCount;
}
}
public static async ETTask<long> Remove<T>(this DBComponent self, Expression<Func<T, bool>> filter, string collection = null) where T : Entity
{
using (await self.Fiber().CoroutineLockComponent.Wait(CoroutineLockType.DB, RandomGenerator.RandInt64() % DBComponent.TaskCount))
{
DeleteResult result = await self.GetCollection<T>(collection).DeleteManyAsync(filter);
return result.DeletedCount;
}
}
public static async ETTask<long> Remove<T>(this DBComponent self, long taskId, Expression<Func<T, bool>> filter, string collection = null)
where T : Entity
{
using (await self.Fiber().CoroutineLockComponent.Wait(CoroutineLockType.DB, taskId % DBComponent.TaskCount))
{
DeleteResult result = await self.GetCollection<T>(collection).DeleteManyAsync(filter);
return result.DeletedCount;
}
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,27 @@
using System;
namespace ET.Server
{
[FriendOf(typeof(DBManagerComponent))]
public static partial class DBManagerComponentSystem
{
public static DBComponent GetZoneDB(this DBManagerComponent self, int zone)
{
DBComponent dbComponent = self.DBComponents[zone];
if (dbComponent != null)
{
return dbComponent;
}
StartZoneConfig startZoneConfig = StartZoneConfigCategory.Instance.Get(zone);
if (startZoneConfig.DBConnection == "")
{
throw new Exception($"zone: {zone} not found mongo connect string");
}
dbComponent = self.AddChild<DBComponent, string, string, int>(startZoneConfig.DBConnection, startZoneConfig.DBName, zone);
self.DBComponents[zone] = dbComponent;
return dbComponent;
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Net;
namespace ET.Server
{
[EntitySystemOf(typeof(HttpComponent))]
[FriendOf(typeof(HttpComponent))]
public static partial class HttpComponentSystem
{
[EntitySystem]
private static void Awake(this HttpComponent self, string address)
{
try
{
self.Listener = new HttpListener();
foreach (string s in address.Split(';'))
{
if (s.Trim() == "")
{
continue;
}
self.Listener.Prefixes.Add(s);
}
self.Listener.Start();
self.Accept().Coroutine();
}
catch (HttpListenerException e)
{
throw new Exception($"请先在cmd中运行: netsh http add urlacl url=http://*:你的address中的端口/ user=Everyone, address: {address}", e);
}
}
[EntitySystem]
private static void Destroy(this HttpComponent self)
{
self.Listener.Stop();
self.Listener.Close();
}
private static async ETTask Accept(this HttpComponent self)
{
long instanceId = self.InstanceId;
while (self.InstanceId == instanceId)
{
try
{
HttpListenerContext context = await self.Listener.GetContextAsync();
self.Handle(context).Coroutine();
}
catch (ObjectDisposedException)
{
}
catch (Exception e)
{
Log.Error(e);
}
}
}
private static async ETTask Handle(this HttpComponent self, HttpListenerContext context)
{
try
{
IHttpHandler handler = HttpDispatcher.Instance.Get(self.IScene.SceneType, context.Request.Url.AbsolutePath);
await handler.Handle(self.Scene(), context);
}
catch (Exception e)
{
Log.Error(e);
}
context.Request.InputStream.Dispose();
context.Response.OutputStream.Dispose();
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace ET.Server
{
[ActorMessageHandler(SceneType.NetInner)]
public class A2NetInner_MessageHandler: ActorMessageHandler<Scene, A2NetInner_Message>
{
protected override async ETTask Run(Scene root, A2NetInner_Message innerMessage)
{
root.GetComponent<ActorOuterComponent>().Send(innerMessage.ActorId, innerMessage.MessageObject);
await ETTask.CompletedTask;
}
}
}

View File

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

View File

@@ -0,0 +1,13 @@
using System.Collections.Generic;
namespace ET.Server
{
[ActorMessageHandler(SceneType.NetInner)]
public class A2NetInner_RequestHandler: ActorMessageHandler<Scene, A2NetInner_Request, A2NetInner_Response>
{
protected override async ETTask Run(Scene root, A2NetInner_Request request, A2NetInner_Response response)
{
response.MessageObject = await root.GetComponent<ActorOuterComponent>().Call(request.ActorId, request.MessageObject, request.NeedException);
}
}
}

View File

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

View File

@@ -0,0 +1,246 @@
using System;
using System.Net;
namespace ET.Server
{
[EntitySystemOf(typeof(ActorOuterComponent))]
[FriendOf(typeof(ActorOuterComponent))]
public static partial class ActorOuterComponentSystem
{
[EntitySystem]
private static void Awake(this ActorOuterComponent self, IPEndPoint address)
{
switch (self.InnerProtocol)
{
case NetworkProtocol.TCP:
{
self.AService = new TService(address, ServiceType.Inner);
break;
}
case NetworkProtocol.KCP:
{
self.AService = new KService(address, ServiceType.Inner);
break;
}
}
self.AService.AcceptCallback = self.OnAccept;
self.AService.ReadCallback = self.OnRead;
self.AService.ErrorCallback = self.OnError;
}
[EntitySystem]
private static void Update(this ActorOuterComponent self)
{
self.AService.Update();
}
[EntitySystem]
private static void Destroy(this ActorOuterComponent self)
{
self.AService.Dispose();
}
private static void OnRead(this ActorOuterComponent self, long channelId, ActorId actorId, object message)
{
Session session = self.GetChild<Session>(channelId);
if (session == null)
{
return;
}
session.LastRecvTime = self.Fiber().TimeInfo.ClientFrameTime();
self.HandleMessage(actorId, message).Coroutine();
}
private static async ETTask HandleMessage(this ActorOuterComponent self, ActorId actorId, object message)
{
Fiber fiber = self.Fiber();
int fromProcess = actorId.Process;
actorId.Process = fiber.Process;
switch (message)
{
case IActorResponse iActorResponse:
{
self.HandleIActorResponse(iActorResponse);
return;
}
case IActorLocationRequest iActorRequest:
{
IActorResponse response = await fiber.ActorInnerComponent.Call(actorId, iActorRequest, false);
actorId.Process = fromProcess;
self.Send(actorId, response);
break;
}
case IActorRequest iActorRequest:
{
IActorResponse response = await fiber.ActorInnerComponent.Call(actorId, iActorRequest);
actorId.Process = fromProcess;
self.Send(actorId, response);
break;
}
default:
{
ActorMessageQueue.Instance.Send(actorId, (MessageObject)message);
break;
}
}
}
private static void OnError(this ActorOuterComponent self, long channelId, int error)
{
Session session = self.GetChild<Session>(channelId);
if (session == null)
{
return;
}
session.Error = error;
session.Dispose();
}
// 这个channelId是由CreateAcceptChannelId生成的
private static void OnAccept(this ActorOuterComponent self, long channelId, IPEndPoint ipEndPoint)
{
Session session = self.AddChildWithId<Session, AService>(channelId, self.AService);
session.RemoteAddress = ipEndPoint;
//session.AddComponent<SessionIdleCheckerComponent, int, int, int>(NetThreadComponent.checkInteral, NetThreadComponent.recvMaxIdleTime, NetThreadComponent.sendMaxIdleTime);
}
private static Session CreateInner(this ActorOuterComponent self, long channelId, IPEndPoint ipEndPoint)
{
Session session = self.AddChildWithId<Session, AService>(channelId, self.AService);
session.RemoteAddress = ipEndPoint;
self.AService.Create(channelId, ipEndPoint);
//session.AddComponent<InnerPingComponent>();
//session.AddComponent<SessionIdleCheckerComponent, int, int, int>(NetThreadComponent.checkInteral, NetThreadComponent.recvMaxIdleTime, NetThreadComponent.sendMaxIdleTime);
return session;
}
// 内网actor sessionchannelId是进程号
private static Session Get(this ActorOuterComponent self, long channelId)
{
Session session = self.GetChild<Session>(channelId);
if (session != null)
{
return session;
}
IPEndPoint ipEndPoint = StartSceneConfigCategory.Instance.Get((int) channelId).InnerIPPort;
session = self.CreateInner(channelId, ipEndPoint);
return session;
}
public static void HandleIActorResponse(this ActorOuterComponent 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 Send(this ActorOuterComponent self, ActorId actorId, IActorMessage message)
{
self.SendInner(actorId, message as MessageObject);
}
private static void SendInner(this ActorOuterComponent self, ActorId actorId, MessageObject message)
{
if (actorId == default)
{
throw new Exception($"actor id is 0: {message}");
}
Fiber fiber = self.Fiber();
// 如果发向同一个进程,则报错
if (actorId.Process == fiber.Process)
{
throw new Exception($"actor is the same process: {fiber.Process} {actorId.Process}");
}
StartSceneConfig startSceneConfig = StartSceneConfigCategory.Instance.NetInners[actorId.Process];
Session session = self.Get(startSceneConfig.Id);
actorId.Process = fiber.Process;
session.Send(actorId, message);
}
private static int GetRpcId(this ActorOuterComponent self)
{
return ++self.RpcId;
}
public static async ETTask<IActorResponse> Call(this ActorOuterComponent self, ActorId actorId, IActorRequest iActorRequest, bool needException = true)
{
if (actorId == default)
{
throw new Exception($"actor id is 0: {iActorRequest}");
}
Fiber fiber = self.Fiber();
int rpcId = self.GetRpcId();
var tcs = ETTask<IActorResponse>.Create(true);
self.requestCallback.Add(self.RpcId, new ActorMessageSender(actorId, iActorRequest, tcs, needException));
self.SendInner(actorId, iActorRequest as MessageObject);
async ETTask Timeout()
{
await fiber.TimerComponent.WaitAsync(ActorOuterComponent.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: 5950c6f1f1733d846864c5f87cc48038
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace ET
{
[FriendOf(typeof(ActorSenderComponent))]
public static partial class ActorSenderComponentSystem
{
public static void Send(this ActorSenderComponent self, ActorId actorId, IActorMessage message)
{
Fiber fiber = self.Fiber();
// 如果发向同一个进程,则扔到消息队列中
if (actorId.Process == fiber.Process)
{
ActorMessageQueue.Instance.Send(fiber.Address, actorId, (MessageObject)message);
return;
}
// 发给NetInner纤程
A2NetInner_Message a2NetInnerMessage = A2NetInner_Message.Create();
a2NetInnerMessage.FromAddress = fiber.Address;
a2NetInnerMessage.ActorId = actorId;
a2NetInnerMessage.MessageObject = message;
StartSceneConfig startSceneConfig = StartSceneConfigCategory.Instance.NetInners[fiber.Process];
ActorMessageQueue.Instance.Send(startSceneConfig.ActorId, a2NetInnerMessage);
}
public static int GetRpcId(this ActorSenderComponent self)
{
return ++self.RpcId;
}
public static async ETTask<IActorResponse> Call(
this ActorSenderComponent 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 ActorSenderComponent 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)
{
return await fiber.Root.GetComponent<ActorInnerComponent>().Call(actorId, rpcId, iActorRequest, needException);
}
// 发给NetInner纤程
A2NetInner_Request a2NetInner_Request = A2NetInner_Request.Create();
a2NetInner_Request.ActorId = actorId;
a2NetInner_Request.MessageObject = iActorRequest;
a2NetInner_Request.NeedException = needException;
StartSceneConfig startSceneConfig = StartSceneConfigCategory.Instance.NetInners[fiber.Process];
A2NetInner_Response response = await fiber.Root.GetComponent<ActorSenderComponent>().Call(
startSceneConfig.ActorId, a2NetInner_Request, needException: a2NetInner_Request.NeedException) as A2NetInner_Response;
return response.MessageObject;
}
}
}

View File

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

View File

@@ -0,0 +1,72 @@
using System.Net;
using MongoDB.Bson;
namespace ET.Server
{
[EntitySystemOf(typeof(NetServerComponent))]
[FriendOf(typeof(NetServerComponent))]
public static partial class NetServerComponentSystem
{
[EntitySystem]
private static void Awake(this NetServerComponent self, IPEndPoint address)
{
self.AService = new KService(address, ServiceType.Outer);
self.AService.AcceptCallback = self.OnAccept;
self.AService.ReadCallback = self.OnRead;
self.AService.ErrorCallback = self.OnError;
}
[EntitySystem]
private static void Update(this NetServerComponent self)
{
self.AService.Update();
}
[EntitySystem]
private static void Destroy(this NetServerComponent self)
{
self.AService.Dispose();
}
private static void OnError(this NetServerComponent self, long channelId, int error)
{
Session session = self.GetChild<Session>(channelId);
if (session == null)
{
return;
}
session.Error = error;
session.Dispose();
}
// 这个channelId是由CreateAcceptChannelId生成的
private static void OnAccept(this NetServerComponent self, long channelId, IPEndPoint ipEndPoint)
{
Session session = self.AddChildWithId<Session, AService>(channelId, self.AService);
session.RemoteAddress = ipEndPoint;
if (self.IScene.SceneType != SceneType.BenchmarkServer)
{
// 挂上这个组件5秒就会删除session所以客户端验证完成要删除这个组件。该组件的作用就是防止外挂一直连接不发消息也不进行权限验证
session.AddComponent<SessionAcceptTimeoutComponent>();
// 客户端连接2秒检查一次recv消息10秒没有消息则断开
session.AddComponent<SessionIdleCheckerComponent>();
}
}
private static void OnRead(this NetServerComponent self, long channelId, ActorId actorId, object message)
{
Session session = self.GetChild<Session>(channelId);
if (session == null)
{
return;
}
session.LastRecvTime = self.Fiber().TimeInfo.ClientNow();
Log.Debug(message.ToJson());
EventSystem.Instance.Publish(self.Scene(), new NetServerComponentOnRead() {Session = session, Message = message});
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,22 @@
using System.Net;
namespace ET.Server
{
[Invoke((long)SceneType.NetInner)]
public class FiberInit_NetInner: AInvokeHandler<FiberInit, ETTask>
{
public override async ETTask Handle(FiberInit fiberInit)
{
Scene root = fiberInit.Fiber.Root;
root.AddComponent<MailBoxComponent, MailBoxType>(MailBoxType.UnOrderedMessage);
root.AddComponent<TimerComponent>();
root.AddComponent<CoroutineLockComponent>();
StartSceneConfig startSceneConfig = StartSceneConfigCategory.Instance.Get(fiberInit.Fiber.Id);
root.AddComponent<ActorOuterComponent, IPEndPoint>(startSceneConfig.InnerIPPort);
root.AddComponent<ActorInnerComponent>();
//root.AddComponent<ActorSenderComponent>();
await ETTask.CompletedTask;
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,24 @@
namespace ET.Server
{
// 这里为什么能定义class呢因为这里只有逻辑热重载后新的handler替换旧的仍然没有问题
[EnableClass]
public abstract class ARobotCase: AInvokeHandler<RobotInvokeArgs, ETTask>
{
protected abstract ETTask Run(RobotCase robotCase);
public override async ETTask Handle(RobotInvokeArgs a)
{
using RobotCase robotCase = await a.Fiber.Root.GetComponent<RobotCaseComponent>().New();
try
{
await this.Run(robotCase);
}
catch (System.Exception e)
{
Log.Error($"{robotCase.Zone()} {e}");
RobotLog.Console($"RobotCase Error {this.GetType().FullName}:\n\t{e}");
}
}
}
}

View File

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

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
namespace ET.Server
{
[FriendOf(typeof(RobotCaseComponent))]
public static partial class RobotCaseComponentSystem
{
public static int GetN(this RobotCaseComponent self)
{
return ++self.N;
}
public static async ETTask<RobotCase> New(this RobotCaseComponent self)
{
await ETTask.CompletedTask;
RobotCase robotCase = self.AddChild<RobotCase>();
return robotCase;
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,19 @@
using System.Net;
namespace ET.Server
{
[Invoke((long)SceneType.Router)]
public class FiberInit_Router: AInvokeHandler<FiberInit, ETTask>
{
public override async ETTask Handle(FiberInit fiberInit)
{
Scene root = fiberInit.Fiber.Root;
StartSceneConfig startSceneConfig = StartSceneConfigCategory.Instance.Get((int)root.Id);
// 开发期间使用OuterIPPort云服务器因为本机没有OuterIP所以要改成InnerIPPort然后在云防火墙中端口映射到InnerIPPort
root.AddComponent<RouterComponent, IPEndPoint, string>(startSceneConfig.OuterIPPort, startSceneConfig.StartProcessConfig.InnerIP);
await ETTask.CompletedTask;
}
}
}

View File

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

View File

@@ -0,0 +1,17 @@
using System.Net;
namespace ET.Server
{
[Invoke((long)SceneType.RouterManager)]
public class FiberInit_RouterManager: AInvokeHandler<FiberInit, ETTask>
{
public override async ETTask Handle(FiberInit fiberInit)
{
Scene root = fiberInit.Fiber.Root;
StartSceneConfig startSceneConfig = StartSceneConfigCategory.Instance.Get((int)root.Id);
root.AddComponent<HttpComponent, string>($"http://*:{startSceneConfig.Port}/");
await ETTask.CompletedTask;
}
}
}

View File

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

View File

@@ -0,0 +1,646 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
namespace ET.Server
{
[EntitySystemOf(typeof(RouterComponent))]
[FriendOf(typeof (RouterComponent))]
[FriendOf(typeof (RouterNode))]
public static partial class RouterComponentSystem
{
[EntitySystem]
private static void Awake(this RouterComponent self, IPEndPoint outerAddress, string innerIP)
{
self.OuterSocket = new Socket(outerAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
self.OuterSocket.Bind(outerAddress);
if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
self.OuterSocket.SendBufferSize = 16 * Kcp.OneM;
self.OuterSocket.ReceiveBufferSize = 16 * Kcp.OneM;
}
self.InnerSocket = new Socket(outerAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
self.InnerSocket.Bind(new IPEndPoint(IPAddress.Parse(innerIP), 0));
if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
self.InnerSocket.SendBufferSize = 16 * Kcp.OneM;
self.InnerSocket.ReceiveBufferSize = 16 * Kcp.OneM;
}
NetworkHelper.SetSioUdpConnReset(self.OuterSocket);
NetworkHelper.SetSioUdpConnReset(self.InnerSocket);
}
[EntitySystem]
private static void Destroy(this RouterComponent self)
{
self.OuterSocket.Dispose();
self.InnerSocket.Dispose();
self.OuterNodes.Clear();
self.IPEndPoint = null;
}
[EntitySystem]
private static void Update(this RouterComponent self)
{
long timeNow = self.Fiber().TimeInfo.ClientNow();
self.RecvOuter(timeNow);
self.RecvInner(timeNow);
// 每秒钟检查一次
if (timeNow - self.LastCheckTime > 1000)
{
self.CheckConnectTimeout(timeNow);
self.LastCheckTime = timeNow;
}
}
private static IPEndPoint CloneAddress(this RouterComponent self)
{
IPEndPoint ipEndPoint = (IPEndPoint) self.IPEndPoint;
return new IPEndPoint(ipEndPoint.Address, ipEndPoint.Port);
}
// 接收udp消息
private static void RecvOuter(this RouterComponent self, long timeNow)
{
while (self.OuterSocket != null && self.OuterSocket.Available > 0)
{
try
{
int messageLength = self.OuterSocket.ReceiveFrom(self.Cache, ref self.IPEndPoint);
self.RecvOuterHandler(messageLength, timeNow);
}
catch (Exception e)
{
Log.Error(e);
}
}
}
private static void CheckConnectTimeout(this RouterComponent self, long timeNow)
{
// 检查连接过程超时
using (ListComponent<long> listComponent = ListComponent<long>.Create())
{
foreach (var kv in self.ConnectIdNodes)
{
if (timeNow < kv.Value.LastRecvOuterTime + 10 * 1000)
{
continue;
}
listComponent.Add(kv.Value.Id);
}
foreach (long id in listComponent)
{
self.OnError(id, ErrorCore.ERR_KcpRouterConnectFail);
}
}
// 外网消息超时就断开内网因为会一直重发没有重连之前内网连接一直存在会导致router一直收到内网消息
using (ListComponent<long> listComponent = ListComponent<long>.Create())
{
foreach (var kv in self.OuterNodes)
{
// 比session超时应该多10秒钟
if (timeNow < kv.Value.LastRecvOuterTime + ConstValue.SessionTimeoutTime + 10 * 1000)
{
continue;
}
listComponent.Add(kv.Value.Id);
}
foreach (long id in listComponent)
{
self.OnError(id, ErrorCore.ERR_KcpRouterTimeout);
}
}
}
private static void RecvInner(this RouterComponent self, long timeNow)
{
while (self.InnerSocket != null && self.InnerSocket.Available > 0)
{
try
{
int messageLength = self.InnerSocket.ReceiveFrom(self.Cache, ref self.IPEndPoint);
self.RecvInnerHandler(messageLength, timeNow);
}
catch (Exception e)
{
Log.Error(e);
}
}
}
private static void RecvOuterHandler(this RouterComponent self, int messageLength, long timeNow)
{
// 长度小于1不是正常的消息
if (messageLength < 1)
{
return;
}
// accept
byte flag = self.Cache[0];
switch (flag)
{
case KcpProtocalType.RouterReconnectSYN:
{
if (messageLength < 13)
{
break;
}
uint outerConn = BitConverter.ToUInt32(self.Cache, 1);
uint innerConn = BitConverter.ToUInt32(self.Cache, 5);
uint connectId = BitConverter.ToUInt32(self.Cache, 9);
string realAddress = self.Cache.ToStr(13, messageLength - 13);
RouterNode routerNode;
// RouterAck之后ConnectIdNodes会删除加入到OuterNodes中来
if (!self.OuterNodes.TryGetValue(outerConn, out routerNode))
{
self.ConnectIdNodes.TryGetValue(connectId, out routerNode);
if (routerNode == null)
{
Log.Info($"router create reconnect: {self.IPEndPoint} {realAddress} {connectId} {outerConn} {innerConn}");
routerNode = self.New(realAddress, connectId, outerConn, innerConn, self.CloneAddress());
// self.OuterNodes 这里不能add因为还没验证完成,要在RouterAck中加入
}
}
if (routerNode.ConnectId != connectId)
{
Log.Warning($"kcp router router reconnect connectId diff1: {routerNode.SyncIpEndPoint} {(IPEndPoint) self.IPEndPoint}");
break;
}
// 不是自己的outerConn冲突, 直接break,也就是说这个软路由上有个跟自己outerConn冲突的连接就不能连接了
// 这个路由连接不上,客户端会换个软路由,所以没关系
if (routerNode.InnerConn != innerConn)
{
Log.Warning($"kcp router router reconnect inner conn diff1: {routerNode.SyncIpEndPoint} {(IPEndPoint) self.IPEndPoint}");
break;
}
if (routerNode.OuterConn != outerConn)
{
Log.Warning($"kcp router router reconnect outer conn diff1: {routerNode.SyncIpEndPoint} {(IPEndPoint) self.IPEndPoint}");
break;
}
// 校验ip连接过程中ip不能变化
if (!Equals(routerNode.SyncIpEndPoint, self.IPEndPoint))
{
Log.Warning($"kcp router syn ip is diff1: {routerNode.SyncIpEndPoint} {(IPEndPoint) self.IPEndPoint}");
break;
}
// 校验内网地址
if (routerNode.InnerAddress != realAddress)
{
Log.Warning($"router sync error2: {routerNode.OuterConn} {routerNode.InnerAddress} {outerConn} {realAddress}");
break;
}
if (++routerNode.RouterSyncCount > 40)
{
self.OnError(routerNode.Id, ErrorCore.ERR_KcpRouterRouterSyncCountTooMuchTimes);
break;
}
// 转发到内网
self.Cache.WriteTo(0, KcpProtocalType.RouterReconnectSYN);
self.Cache.WriteTo(1, outerConn);
self.Cache.WriteTo(5, innerConn);
self.Cache.WriteTo(9, connectId);
self.InnerSocket.SendTo(self.Cache, 0, 13, SocketFlags.None, routerNode.InnerIpEndPoint);
if (!routerNode.CheckOuterCount(timeNow))
{
self.OnError(routerNode.Id, ErrorCore.ERR_KcpRouterTooManyPackets);
}
break;
}
case KcpProtocalType.RouterSYN:
{
if (messageLength < 13)
{
break;
}
uint outerConn = BitConverter.ToUInt32(self.Cache, 1);
uint innerConn = BitConverter.ToUInt32(self.Cache, 5);
uint connectId = BitConverter.ToUInt32(self.Cache, 9);
string realAddress = self.Cache.ToStr(13, messageLength - 13);
RouterNode routerNode;
self.ConnectIdNodes.TryGetValue(connectId, out routerNode);
if (routerNode == null)
{
outerConn = NetServices.Instance.CreateConnectChannelId();
routerNode = self.New(realAddress, connectId, outerConn, innerConn, self.CloneAddress());
Log.Info($"router create: {realAddress} {connectId} {outerConn} {innerConn} {routerNode.SyncIpEndPoint}");
self.OuterNodes.Add(routerNode.OuterConn, routerNode);
}
if (++routerNode.RouterSyncCount > 40)
{
self.OnError(routerNode.Id, ErrorCore.ERR_KcpRouterRouterSyncCountTooMuchTimes);
break;
}
// 校验ip连接过程中ip不能变化
if (!Equals(routerNode.SyncIpEndPoint, self.IPEndPoint))
{
Log.Warning($"kcp router syn ip is diff1: {routerNode.SyncIpEndPoint} {self.IPEndPoint}");
break;
}
// 校验内网地址
if (routerNode.InnerAddress != realAddress)
{
Log.Warning($"router sync error2: {routerNode.OuterConn} {routerNode.InnerAddress} {outerConn} {realAddress}");
break;
}
self.Cache.WriteTo(0, KcpProtocalType.RouterACK);
self.Cache.WriteTo(1, routerNode.InnerConn);
self.Cache.WriteTo(5, routerNode.OuterConn);
self.OuterSocket.SendTo(self.Cache, 0, 9, SocketFlags.None, routerNode.SyncIpEndPoint);
if (!routerNode.CheckOuterCount(timeNow))
{
self.OnError(routerNode.Id, ErrorCore.ERR_KcpRouterTooManyPackets);
}
break;
}
case KcpProtocalType.SYN:
{
// 长度!=13不是accpet消息
if (messageLength != 9)
{
break;
}
uint outerConn = BitConverter.ToUInt32(self.Cache, 1); // remote
uint innerConn = BitConverter.ToUInt32(self.Cache, 5);
if (!self.OuterNodes.TryGetValue(outerConn, out RouterNode kcpRouter))
{
Log.Warning($"kcp router syn not found outer nodes: {outerConn} {innerConn}");
break;
}
if (++kcpRouter.SyncCount > 20)
{
self.OnError(kcpRouter.Id, ErrorCore.ERR_KcpRouterSyncCountTooMuchTimes);
break;
}
// 校验ip连接过程中ip不能变化
IPEndPoint ipEndPoint = (IPEndPoint) self.IPEndPoint;
if (!Equals(kcpRouter.SyncIpEndPoint.Address, ipEndPoint.Address))
{
Log.Warning($"kcp router syn ip is diff3: {kcpRouter.SyncIpEndPoint.Address} {ipEndPoint.Address}");
break;
}
// 发了syn过来那么RouterSyn就成功了可以删除ConnectId
self.ConnectIdNodes.Remove(kcpRouter.ConnectId);
kcpRouter.LastRecvOuterTime = timeNow;
kcpRouter.OuterIpEndPoint = self.CloneAddress();
// 转发到内网, 带上客户端的地址
self.Cache.WriteTo(0, KcpProtocalType.SYN);
self.Cache.WriteTo(1, outerConn);
self.Cache.WriteTo(5, innerConn);
byte[] addressBytes = ipEndPoint.ToString().ToByteArray();
Array.Copy(addressBytes, 0, self.Cache, 9, addressBytes.Length);
Log.Info($"kcp router syn: {outerConn} {innerConn} {kcpRouter.InnerIpEndPoint} {kcpRouter.OuterIpEndPoint}");
self.InnerSocket.SendTo(self.Cache, 0, 9 + addressBytes.Length, SocketFlags.None, kcpRouter.InnerIpEndPoint);
if (!kcpRouter.CheckOuterCount(timeNow))
{
self.OnError(kcpRouter.Id, ErrorCore.ERR_KcpRouterTooManyPackets);
}
break;
}
case KcpProtocalType.FIN: // 断开
{
// 长度!=13不是DisConnect消息
if (messageLength != 13)
{
break;
}
uint outerConn = BitConverter.ToUInt32(self.Cache, 1);
uint innerConn = BitConverter.ToUInt32(self.Cache, 5);
if (!self.OuterNodes.TryGetValue(outerConn, out RouterNode kcpRouter))
{
Log.Warning($"kcp router outer fin not found outer nodes: {outerConn} {innerConn}");
break;
}
// 比对innerConn
if (kcpRouter.InnerConn != innerConn)
{
Log.Warning($"router node innerConn error: {innerConn} {outerConn} {kcpRouter.Status}");
break;
}
kcpRouter.LastRecvOuterTime = timeNow;
Log.Info($"kcp router outer fin: {outerConn} {innerConn} {kcpRouter.InnerIpEndPoint}");
self.InnerSocket.SendTo(self.Cache, 0, messageLength, SocketFlags.None, kcpRouter.InnerIpEndPoint);
if (!kcpRouter.CheckOuterCount(timeNow))
{
self.OnError(kcpRouter.Id, ErrorCore.ERR_KcpRouterTooManyPackets);
}
break;
}
case KcpProtocalType.MSG:
{
// 长度<9不是Msg消息
if (messageLength < 9)
{
break;
}
// 处理chanel
uint outerConn = BitConverter.ToUInt32(self.Cache, 1); // remote
uint innerConn = BitConverter.ToUInt32(self.Cache, 5); // local
if (!self.OuterNodes.TryGetValue(outerConn, out RouterNode kcpRouter))
{
Log.Warning($"kcp router msg not found outer nodes: {outerConn} {innerConn}");
break;
}
if (kcpRouter.Status != RouterStatus.Msg)
{
Log.Warning($"router node status error: {innerConn} {outerConn} {kcpRouter.Status}");
break;
}
// 比对innerConn
if (kcpRouter.InnerConn != innerConn)
{
Log.Warning($"router node innerConn error: {innerConn} {outerConn} {kcpRouter.Status}");
break;
}
// 重连的时候没有经过syn阶段可能没有设置OuterIpEndPoint重连请求Router的Socket跟发送消息的Socket不是同一个所以udp出来的公网地址可能会变化
if (!Equals(kcpRouter.OuterIpEndPoint, self.IPEndPoint))
{
kcpRouter.OuterIpEndPoint = self.CloneAddress();
}
kcpRouter.LastRecvOuterTime = timeNow;
self.InnerSocket.SendTo(self.Cache, 0, messageLength, SocketFlags.None, kcpRouter.InnerIpEndPoint);
if (!kcpRouter.CheckOuterCount(timeNow))
{
self.OnError(kcpRouter.Id, ErrorCore.ERR_KcpRouterTooManyPackets);
}
break;
}
}
}
private static void RecvInnerHandler(this RouterComponent self, int messageLength, long timeNow)
{
// 长度小于1不是正常的消息
if (messageLength < 1)
{
return;
}
// accept
byte flag = self.Cache[0];
switch (flag)
{
case KcpProtocalType.RouterReconnectACK:
{
uint innerConn = BitConverter.ToUInt32(self.Cache, 1);
uint outerConn = BitConverter.ToUInt32(self.Cache, 5);
uint connectId = BitConverter.ToUInt32(self.Cache, 9);
if (!self.ConnectIdNodes.TryGetValue(connectId, out RouterNode kcpRouterNode))
{
Log.Warning($"router node error: {innerConn} {connectId}");
break;
}
// 必须校验innerConn防止伪造
if (innerConn != kcpRouterNode.InnerConn)
{
Log.Warning(
$"router node innerConn error: {innerConn} {kcpRouterNode.InnerConn} {outerConn} {kcpRouterNode.OuterConn} {kcpRouterNode.Status}");
break;
}
// 必须校验outerConn防止伪造
if (outerConn != kcpRouterNode.OuterConn)
{
Log.Warning(
$"router node outerConn error: {innerConn} {kcpRouterNode.InnerConn} {outerConn} {kcpRouterNode.OuterConn} {kcpRouterNode.Status}");
break;
}
kcpRouterNode.Status = RouterStatus.Msg;
kcpRouterNode.LastRecvInnerTime = timeNow;
// 校验成功才加到outerNodes中, 如果这里有冲突,外网将连接失败,不过几率极小
if (!self.OuterNodes.ContainsKey(outerConn))
{
self.OuterNodes.Add(outerConn, kcpRouterNode);
self.ConnectIdNodes.Remove(connectId);
}
// 转发出去
self.Cache.WriteTo(0, KcpProtocalType.RouterReconnectACK);
self.Cache.WriteTo(1, kcpRouterNode.InnerConn);
self.Cache.WriteTo(5, kcpRouterNode.OuterConn);
Log.Info($"kcp router RouterAck: {outerConn} {innerConn} {kcpRouterNode.SyncIpEndPoint}");
self.OuterSocket.SendTo(self.Cache, 0, 9, SocketFlags.None, kcpRouterNode.SyncIpEndPoint);
break;
}
case KcpProtocalType.ACK:
{
uint innerConn = BitConverter.ToUInt32(self.Cache, 1); // remote
uint outerConn = BitConverter.ToUInt32(self.Cache, 5); // local
if (!self.OuterNodes.TryGetValue(outerConn, out RouterNode kcpRouterNode))
{
Log.Warning($"kcp router ack not found outer nodes: {outerConn} {innerConn}");
break;
}
kcpRouterNode.Status = RouterStatus.Msg;
kcpRouterNode.InnerConn = innerConn;
kcpRouterNode.LastRecvInnerTime = timeNow;
// 转发出去
Log.Info($"kcp router ack: {outerConn} {innerConn} {kcpRouterNode.OuterIpEndPoint}");
self.OuterSocket.SendTo(self.Cache, 0, messageLength, SocketFlags.None, kcpRouterNode.OuterIpEndPoint);
break;
}
case KcpProtocalType.FIN: // 断开
{
// 长度!=13不是DisConnect消息
if (messageLength != 13)
{
break;
}
uint innerConn = BitConverter.ToUInt32(self.Cache, 1);
uint outerConn = BitConverter.ToUInt32(self.Cache, 5);
if (!self.OuterNodes.TryGetValue(outerConn, out RouterNode kcpRouterNode))
{
Log.Warning($"kcp router inner fin not found outer nodes: {outerConn} {innerConn}");
break;
}
// 比对innerConn
if (kcpRouterNode.InnerConn != innerConn)
{
Log.Warning($"router node innerConn error: {innerConn} {outerConn} {kcpRouterNode.Status}");
break;
}
// 重连,这个字段可能为空,需要客户端发送消息上来才能设置
if (kcpRouterNode.OuterIpEndPoint == null)
{
break;
}
kcpRouterNode.LastRecvInnerTime = timeNow;
Log.Info($"kcp router inner fin: {outerConn} {innerConn} {kcpRouterNode.OuterIpEndPoint}");
self.OuterSocket.SendTo(self.Cache, 0, messageLength, SocketFlags.None, kcpRouterNode.OuterIpEndPoint);
break;
}
case KcpProtocalType.MSG:
{
// 长度<9不是Msg消息
if (messageLength < 9)
{
break;
}
// 处理chanel
uint innerConn = BitConverter.ToUInt32(self.Cache, 1); // remote
uint outerConn = BitConverter.ToUInt32(self.Cache, 5); // local
if (!self.OuterNodes.TryGetValue(outerConn, out RouterNode kcpRouterNode))
{
Log.Warning($"kcp router inner msg not found outer nodes: {outerConn} {innerConn}");
break;
}
// 比对innerConn
if (kcpRouterNode.InnerConn != innerConn)
{
Log.Warning($"router node innerConn error: {innerConn} {outerConn} {kcpRouterNode.Status}");
break;
}
// 重连,这个字段可能为空,需要客户端发送消息上来才能设置
if (kcpRouterNode.OuterIpEndPoint == null)
{
break;
}
kcpRouterNode.LastRecvInnerTime = timeNow;
self.OuterSocket.SendTo(self.Cache, 0, messageLength, SocketFlags.None, kcpRouterNode.OuterIpEndPoint);
break;
}
}
}
public static RouterNode Get(this RouterComponent self, uint outerConn)
{
RouterNode routerNode = null;
self.OuterNodes.TryGetValue(outerConn, out routerNode);
return routerNode;
}
private static RouterNode New(this RouterComponent self, string innerAddress, uint connectId, uint outerConn, uint innerConn, IPEndPoint syncEndPoint)
{
RouterNode routerNode = self.AddChild<RouterNode>();
routerNode.ConnectId = connectId;
routerNode.OuterConn = outerConn;
routerNode.InnerConn = innerConn;
routerNode.InnerIpEndPoint = NetworkHelper.ToIPEndPoint(innerAddress);
routerNode.SyncIpEndPoint = syncEndPoint;
routerNode.InnerAddress = innerAddress;
routerNode.LastRecvInnerTime = self.Fiber().TimeInfo.ClientNow();
self.ConnectIdNodes.Add(connectId, routerNode);
routerNode.Status = RouterStatus.Sync;
Log.Info($"router new: outerConn: {outerConn} innerConn: {innerConn} {syncEndPoint}");
return routerNode;
}
public static void OnError(this RouterComponent self, long id, int error)
{
RouterNode routerNode = self.GetChild<RouterNode>(id);
if (routerNode == null)
{
return;
}
Log.Info($"router node remove: {routerNode.OuterConn} {routerNode.InnerConn} {error}");
self.Remove(id);
}
private static void Remove(this RouterComponent self, long id)
{
RouterNode routerNode = self.GetChild<RouterNode>(id);
if (routerNode == null)
{
return;
}
self.OuterNodes.Remove(routerNode.OuterConn);
RouterNode connectRouterNode;
if (self.ConnectIdNodes.TryGetValue(routerNode.ConnectId, out connectRouterNode))
{
if (connectRouterNode.Id == routerNode.Id)
{
self.ConnectIdNodes.Remove(routerNode.ConnectId);
}
}
Log.Info($"router remove: {routerNode.Id} outerConn: {routerNode.OuterConn} innerConn: {routerNode.InnerConn}");
routerNode.Dispose();
}
}
}

View File

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

View File

@@ -0,0 +1,56 @@
namespace ET.Server
{
[EntitySystemOf(typeof(RouterNode))]
[FriendOf(typeof(RouterNode))]
public static partial class RouterNodeSystem
{
[EntitySystem]
private static void Awake(this RouterNode self)
{
long timeNow = self.Fiber().TimeInfo.ServerNow();
self.LastRecvInnerTime = timeNow;
self.LastRecvOuterTime = timeNow;
self.OuterIpEndPoint = null;
self.InnerIpEndPoint = null;
self.RouterSyncCount = 0;
self.OuterConn = 0;
self.InnerConn = 0;
}
[EntitySystem]
private static void Destroy(this RouterNode self)
{
self.OuterConn = 0;
self.InnerConn = 0;
self.LastRecvInnerTime = 0;
self.LastRecvOuterTime = 0;
self.OuterIpEndPoint = null;
self.InnerIpEndPoint = null;
self.InnerAddress = null;
self.RouterSyncCount = 0;
self.SyncCount = 0;
}
public static bool CheckOuterCount(this RouterNode self, long timeNow)
{
if (self.LastCheckTime == 0)
{
self.LastCheckTime = timeNow;
}
if (timeNow - self.LastCheckTime > 1000)
{
//Log.Debug($"router recv packet per second: {self.LimitCountPerSecond}");
self.LimitCountPerSecond = 0;
self.LastCheckTime = timeNow;
}
if (++self.LimitCountPerSecond > 1000)
{
return false;
}
return true;
}
}
}

View File

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