mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-14 16:51:28 +00:00
[+] 接入ET8服务端
[+] 接入ET8服务端
This commit is contained in:
8
Assets/GameScripts/DotNet/Hotfix/Server/Module/AOI.meta
Normal file
8
Assets/GameScripts/DotNet/Hotfix/Server/Module/AOI.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 663974eab9143cd4aa7740221e48e4f2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 652ae649b0d965d44b15b66a2b2a15b2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37db3276e119c254295c2b6e57aeee57
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba627657b5b945f42879a685aa491be8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,10 @@
|
||||
namespace ET.Server
|
||||
{
|
||||
public static partial class AOISeeCheckHelper
|
||||
{
|
||||
public static bool IsCanSee(AOIEntity a, AOIEntity b)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13b8112b4a2e587479d6fe24b8c39c1a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1286ef0bf56acc14dad6dd9a105a2bfc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 73a856bb588c88d40b0b0f2131b27027
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9c5b5f290f9eb049a98efe57de93c0e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c78fb4596a5212845bde1ec4757efa39
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1366e4041bf79d1468b108c582082181
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dfa61d7b9671c944c8edbcfffc94b189
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4a35bb25143640b1a80652d551b4ce9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 898ea95a00c4c6442bd0bd8322094277
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f6c2757c99f36b459abe314a571260c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c7646f47e2c7394ba566e5fda9cd3dc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4be989f429e198044aec36d3fbfef1f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9d16c6c9a7661cc4389e5b14d72ff25d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 77b92c4f020a77b4b98b215b660cfe3b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Hotfix/Server/Module/DB.meta
Normal file
8
Assets/GameScripts/DotNet/Hotfix/Server/Module/DB.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f3a62c696459cb4894764862b609a46
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f19e3330918fd704a90558bf305e8be6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46065c60cd29636488a1ef3c436ce6bc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Hotfix/Server/Module/Http.meta
Normal file
8
Assets/GameScripts/DotNet/Hotfix/Server/Module/Http.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd3c768fde72d464094ec6d867788cf7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70f2ab9b9fd4f114795f68e7e8311ac5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34561c39c6a10754abfb53da3becd29b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c88c8f9b180ab7f4b8c92e3cbdd3260f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd158674e3aca934cb0bf40359f60577
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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 session,channelId是进程号
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5950c6f1f1733d846864c5f87cc48038
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1dbffb76761f34144a9e283ec16a499a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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});
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 40d0bddfefa8ce34a8349977968fc0cd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b4dd22515474c148af1637fe0a85045
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e9246ca7a67d1d74283089a148b9ab16
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6633f9840ed3f84c84cd67e5f4912b6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34e94a083d078d541976cd51eb64b8c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3fc65a37e69f17e43b4c2371e7f71ee6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e0e953d25e2c6e46960acc2bc16755f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c074f6c8ded9714399f7c1a900c657f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce88e327d4106944f82129077d984047
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 295da4f243f6ef64eaf46a00c63fa964
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 17f639ceedd49c044aa375dee0e96c0f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user