[+] 接入ET8服务端

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

View File

@@ -0,0 +1,12 @@
namespace ET.Client
{
[ActorMessageHandler(SceneType.NetClient)]
public class A2NetClient_MessageHandler: ActorMessageHandler<Scene, A2NetClient_Message>
{
protected override async ETTask Run(Scene root, A2NetClient_Message message)
{
root.GetComponent<SessionComponent>().Session.Send(message.MessageObject);
await ETTask.CompletedTask;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0f70391ff9eb499db68bae992a9c85b6
timeCreated: 1688540041

View File

@@ -0,0 +1,11 @@
namespace ET.Client
{
[ActorMessageHandler(SceneType.NetClient)]
public class A2NetClient_RequestHandler: ActorMessageHandler<Scene, A2NetClient_Request, A2NetClient_Response>
{
protected override async ETTask Run(Scene root, A2NetClient_Request request, A2NetClient_Response response)
{
response.MessageObject = await root.GetComponent<SessionComponent>().Session.Call(request.MessageObject);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f3ac2d21dada4f2b8b51b26e0efe891e
timeCreated: 1688540239

View File

@@ -0,0 +1,16 @@
namespace ET.Client
{
[Invoke((long)SceneType.NetClient)]
public class FiberInit_NetClient: 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>();
await ETTask.CompletedTask;
}
}
}

View File

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

View File

@@ -0,0 +1,17 @@
namespace ET.Client
{
public static class LoginHelper
{
public static async ETTask Login(Scene root, string account, string password)
{
root.RemoveComponent<ClientSenderCompnent>();
ClientSenderCompnent clientSenderCompnent = root.AddComponent<ClientSenderCompnent>();
long playerId = await clientSenderCompnent.LoginAsync(account, password);
root.GetComponent<PlayerComponent>().MyId = playerId;
await EventSystem.Instance.PublishAsync(root, new EventType.LoginFinish());
}
}
}

View File

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

View File

@@ -0,0 +1,43 @@
using System.Net;
using System.Net.Sockets;
namespace ET.Client
{
[ActorMessageHandler(SceneType.NetClient)]
public class Main2NetClient_LoginHandler: ActorMessageHandler<Scene, Main2NetClient_Login, NetClient2Main_Login>
{
protected override async ETTask Run(Scene root, Main2NetClient_Login request, NetClient2Main_Login response)
{
string account = request.Account;
string password = request.Password;
// 创建一个ETModel层的Session
root.RemoveComponent<RouterAddressComponent>();
// 获取路由跟realmDispatcher地址
RouterAddressComponent routerAddressComponent = root.GetComponent<RouterAddressComponent>();
if (routerAddressComponent == null)
{
routerAddressComponent =
root.AddComponent<RouterAddressComponent, string, int>(ConstValue.RouterHttpHost, ConstValue.RouterHttpPort);
await routerAddressComponent.Init();
root.AddComponent<NetClientComponent, AddressFamily>(routerAddressComponent.RouterManagerIPAddress.AddressFamily);
}
IPEndPoint realmAddress = routerAddressComponent.GetRealmAddress(account);
R2C_Login r2CLogin;
using (Session session = await RouterHelper.CreateRouterSession(root, realmAddress))
{
r2CLogin = (R2C_Login)await session.Call(new C2R_Login() { Account = account, Password = password });
}
// 创建一个gate Session,并且保存到SessionComponent中
Session gateSession = await RouterHelper.CreateRouterSession(root, NetworkHelper.ToIPEndPoint(r2CLogin.Address));
gateSession.AddComponent<ClientSessionErrorComponent>();
root.AddComponent<SessionComponent>().Session = gateSession;
G2C_LoginGate g2CLoginGate = (G2C_LoginGate)await gateSession.Call(new C2G_LoginGate() { Key = r2CLogin.Key, GateId = r2CLogin.GateId });
Log.Debug("登陆gate成功!");
response.PlayerId = g2CLoginGate.PlayerId;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 02d16f101211408496a7e86b273ed882
timeCreated: 1688536630

View File

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

View File

@@ -0,0 +1,64 @@
using System;
namespace ET.Client
{
[EntitySystemOf(typeof(PingComponent))]
public static partial class PingComponentSystem
{
[EntitySystem]
private static void Awake(this PingComponent self)
{
self.PingAsync().Coroutine();
}
[EntitySystem]
private static void Destroy(this PingComponent self)
{
self.Ping = default;
}
private static async ETTask PingAsync(this PingComponent self)
{
Session session = self.GetParent<Session>();
long instanceId = self.InstanceId;
Fiber fiber = self.Fiber();
while (true)
{
if (self.InstanceId != instanceId)
{
return;
}
long time1 = self.Fiber().TimeInfo.ClientNow();
try
{
C2G_Ping c2GPing = C2G_Ping.Create(true);
using G2C_Ping response = await session.Call(c2GPing) as G2C_Ping;
if (self.InstanceId != instanceId)
{
return;
}
long time2 = self.Fiber().TimeInfo.ClientNow();
self.Ping = time2 - time1;
fiber.TimeInfo.ServerMinusClientTime = response.Time + (time2 - time1) / 2 - time2;
await fiber.TimerComponent.WaitAsync(2000);
}
catch (RpcException e)
{
// session断开导致ping rpc报错记录一下即可不需要打成error
Log.Info($"ping error: {self.Id} {e.Error}");
return;
}
catch (Exception e)
{
Log.Error($"ping error: \n{e}");
}
}
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,25 @@
using System;
using System.IO;
using System.Net.Http;
using System.Threading;
namespace ET.Client
{
public static partial class HttpClientHelper
{
public static async ETTask<string> Get(string link)
{
try
{
using HttpClient httpClient = new();
HttpResponseMessage response = await httpClient.GetAsync(link);
string result = await response.Content.ReadAsStringAsync();
return result;
}
catch (Exception e)
{
throw new Exception($"http request fail: {link.Substring(0,link.IndexOf('?'))}\n{e}");
}
}
}
}

View File

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

View File

@@ -0,0 +1,82 @@
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
namespace ET.Client
{
[EntitySystemOf(typeof(RouterAddressComponent))]
[FriendOf(typeof(RouterAddressComponent))]
public static partial class RouterAddressComponentSystem
{
[EntitySystem]
private static void Awake(this RouterAddressComponent self, string address, int port)
{
self.RouterManagerHost = address;
self.RouterManagerPort = port;
}
public static async ETTask Init(this RouterAddressComponent self)
{
self.RouterManagerIPAddress = NetworkHelper.GetHostAddress(self.RouterManagerHost);
await self.GetAllRouter();
}
private static async ETTask GetAllRouter(this RouterAddressComponent self)
{
string url = $"http://{self.RouterManagerHost}:{self.RouterManagerPort}/get_router?v={RandomGenerator.RandUInt32()}";
Log.Debug($"start get router info: {url}");
string routerInfo = await HttpClientHelper.Get(url);
Log.Debug($"recv router info: {routerInfo}");
HttpGetRouterResponse httpGetRouterResponse = MongoHelper.FromJson<HttpGetRouterResponse>(routerInfo);
self.Info = httpGetRouterResponse;
Log.Debug($"start get router info finish: {MongoHelper.ToJson(httpGetRouterResponse)}");
// 打乱顺序
RandomGenerator.BreakRank(self.Info.Routers);
self.WaitTenMinGetAllRouter().Coroutine();
}
// 等10分钟再获取一次
public static async ETTask WaitTenMinGetAllRouter(this RouterAddressComponent self)
{
await self.Fiber().TimerComponent.WaitAsync(5 * 60 * 1000);
if (self.IsDisposed)
{
return;
}
await self.GetAllRouter();
}
public static IPEndPoint GetAddress(this RouterAddressComponent self)
{
if (self.Info.Routers.Count == 0)
{
return null;
}
string address = self.Info.Routers[self.RouterIndex++ % self.Info.Routers.Count];
string[] ss = address.Split(':');
IPAddress ipAddress = IPAddress.Parse(ss[0]);
if (self.RouterManagerIPAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
ipAddress = ipAddress.MapToIPv6();
}
return new IPEndPoint(ipAddress, int.Parse(ss[1]));
}
public static IPEndPoint GetRealmAddress(this RouterAddressComponent self, string account)
{
int v = account.Mode(self.Info.Realms.Count);
string address = self.Info.Realms[v];
string[] ss = address.Split(':');
IPAddress ipAddress = IPAddress.Parse(ss[0]);
//if (self.IPAddress.AddressFamily == AddressFamily.InterNetworkV6)
//{
// ipAddress = ipAddress.MapToIPv6();
//}
return new IPEndPoint(ipAddress, int.Parse(ss[1]));
}
}
}

View File

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

View File

@@ -0,0 +1,71 @@
using System;
using System.Net;
namespace ET.Client
{
[EntitySystemOf(typeof(RouterCheckComponent))]
public static partial class RouterCheckComponentSystem
{
[EntitySystem]
private static void Awake(this RouterCheckComponent self)
{
self.CheckAsync().Coroutine();
}
private static async ETTask CheckAsync(this RouterCheckComponent self)
{
Session session = self.GetParent<Session>();
long instanceId = self.InstanceId;
Fiber fiber = self.Fiber();
Scene root = fiber.Root;
while (true)
{
if (self.InstanceId != instanceId)
{
return;
}
await fiber.TimerComponent.WaitAsync(1000);
if (self.InstanceId != instanceId)
{
return;
}
long time = self.Fiber().TimeInfo.ClientFrameTime();
if (time - session.LastRecvTime < 7 * 1000)
{
continue;
}
try
{
long sessionId = session.Id;
(uint localConn, uint remoteConn) = session.AService.GetChannelConn(sessionId);
IPEndPoint realAddress = self.GetParent<Session>().RemoteAddress;
Log.Info($"get recvLocalConn start: {root.Id} {realAddress} {localConn} {remoteConn}");
(uint recvLocalConn, IPEndPoint routerAddress) = await RouterHelper.GetRouterAddress(root, realAddress, localConn, remoteConn);
if (recvLocalConn == 0)
{
Log.Error($"get recvLocalConn fail: {root.Id} {routerAddress} {realAddress} {localConn} {remoteConn}");
continue;
}
Log.Info($"get recvLocalConn ok: {root.Id} {routerAddress} {realAddress} {recvLocalConn} {localConn} {remoteConn}");
session.LastRecvTime = self.Fiber().TimeInfo.ClientNow();
session.AService.ChangeAddress(sessionId, routerAddress);
}
catch (Exception e)
{
Log.Error(e);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,109 @@
using System;
using System.Net;
using System.Net.Sockets;
namespace ET.Client
{
public static partial class RouterHelper
{
// 注册router
public static async ETTask<Session> CreateRouterSession(Scene root, IPEndPoint address)
{
(uint recvLocalConn, IPEndPoint routerAddress) = await GetRouterAddress(root, address, 0, 0);
if (recvLocalConn == 0)
{
throw new Exception($"get router fail: {root.Id} {address}");
}
Log.Info($"get router: {recvLocalConn} {routerAddress}");
Session routerSession = root.GetComponent<NetClientComponent>().Create(routerAddress, address, recvLocalConn);
routerSession.AddComponent<PingComponent>();
routerSession.AddComponent<RouterCheckComponent>();
return routerSession;
}
public static async ETTask<(uint, IPEndPoint)> GetRouterAddress(Scene root, IPEndPoint address, uint localConn, uint remoteConn)
{
Log.Info($"start get router address: {root.Id} {address} {localConn} {remoteConn}");
//return (RandomHelper.RandUInt32(), address);
RouterAddressComponent routerAddressComponent = root.GetComponent<RouterAddressComponent>();
IPEndPoint routerInfo = routerAddressComponent.GetAddress();
uint recvLocalConn = await Connect(root, routerInfo, address, localConn, remoteConn);
Log.Info($"finish get router address: {root.Id} {address} {localConn} {remoteConn} {recvLocalConn} {routerInfo}");
return (recvLocalConn, routerInfo);
}
// 向router申请
private static async ETTask<uint> Connect(Scene root, IPEndPoint routerAddress, IPEndPoint realAddress, uint localConn, uint remoteConn)
{
uint connectId = RandomGenerator.RandUInt32();
using Socket socket = new Socket(routerAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
int count = 20;
byte[] sendCache = new byte[512];
byte[] recvCache = new byte[512];
uint synFlag = localConn == 0? KcpProtocalType.RouterSYN : KcpProtocalType.RouterReconnectSYN;
sendCache.WriteTo(0, synFlag);
sendCache.WriteTo(1, localConn);
sendCache.WriteTo(5, remoteConn);
sendCache.WriteTo(9, connectId);
byte[] addressBytes = realAddress.ToString().ToByteArray();
Array.Copy(addressBytes, 0, sendCache, 13, addressBytes.Length);
Log.Info($"router connect: {connectId} {localConn} {remoteConn} {routerAddress} {realAddress}");
EndPoint recvIPEndPoint = new IPEndPoint(IPAddress.Any, 0);
long lastSendTimer = 0;
Fiber fiber = root.Fiber;
while (true)
{
long timeNow = fiber.TimeInfo.ClientFrameTime();
if (timeNow - lastSendTimer > 300)
{
if (--count < 0)
{
Log.Error($"router connect timeout fail! {localConn} {remoteConn} {routerAddress} {realAddress}");
return 0;
}
lastSendTimer = timeNow;
// 发送
socket.SendTo(sendCache, 0, addressBytes.Length + 13, SocketFlags.None, routerAddress);
}
await fiber.TimerComponent.WaitFrameAsync();
// 接收
if (socket.Available > 0)
{
int messageLength = socket.ReceiveFrom(recvCache, ref recvIPEndPoint);
if (messageLength != 9)
{
Log.Error($"router connect error1: {connectId} {messageLength} {localConn} {remoteConn} {routerAddress} {realAddress}");
continue;
}
byte flag = recvCache[0];
if (flag != KcpProtocalType.RouterReconnectACK && flag != KcpProtocalType.RouterACK)
{
Log.Error($"router connect error2: {connectId} {synFlag} {flag} {localConn} {remoteConn} {routerAddress} {realAddress}");
continue;
}
uint recvRemoteConn = BitConverter.ToUInt32(recvCache, 1);
uint recvLocalConn = BitConverter.ToUInt32(recvCache, 5);
Log.Info($"router connect finish: {connectId} {recvRemoteConn} {recvLocalConn} {localConn} {remoteConn} {routerAddress} {realAddress}");
return recvLocalConn;
}
}
}
}
}

View File

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