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:
@@ -0,0 +1,17 @@
|
||||
namespace ET
|
||||
{
|
||||
public static partial class ErrorCode
|
||||
{
|
||||
public const int ERR_Success = 0;
|
||||
|
||||
// 1-11004 是SocketError请看SocketError定义
|
||||
//-----------------------------------
|
||||
// 100000-109999是Core层的错误
|
||||
|
||||
// 110000以下的错误请看ErrorCore.cs
|
||||
|
||||
// 这里配置逻辑层的错误码
|
||||
// 110000 - 200000是抛异常的错误
|
||||
// 200001以上不抛异常
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c926ebadd533a344d9b622ba3398dd9a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace ET
|
||||
{
|
||||
public interface IMHandler
|
||||
{
|
||||
void Handle(Session session, object message);
|
||||
Type GetMessageType();
|
||||
|
||||
Type GetResponseType();
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d9d71d94c994b64daecdede5f1a889c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,100 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace ET
|
||||
{
|
||||
public class MessageDispatcherInfo
|
||||
{
|
||||
public SceneType SceneType { get; }
|
||||
public IMHandler IMHandler { get; }
|
||||
|
||||
public MessageDispatcherInfo(SceneType sceneType, IMHandler imHandler)
|
||||
{
|
||||
this.SceneType = sceneType;
|
||||
this.IMHandler = imHandler;
|
||||
}
|
||||
}
|
||||
|
||||
public class MessageDispatcherComponent: SingletonLock<MessageDispatcherComponent>, ISingletonAwake
|
||||
{
|
||||
private readonly Dictionary<ushort, List<MessageDispatcherInfo>> handlers = new();
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
HashSet<Type> types = EventSystem.Instance.GetTypes(typeof (MessageHandlerAttribute));
|
||||
|
||||
foreach (Type type in types)
|
||||
{
|
||||
IMHandler iMHandler = Activator.CreateInstance(type) as IMHandler;
|
||||
if (iMHandler == null)
|
||||
{
|
||||
Log.Error($"message handle {type.Name} 需要继承 IMHandler");
|
||||
continue;
|
||||
}
|
||||
|
||||
object[] attrs = type.GetCustomAttributes(typeof(MessageHandlerAttribute), true);
|
||||
|
||||
foreach (object attr in attrs)
|
||||
{
|
||||
MessageHandlerAttribute messageHandlerAttribute = attr as MessageHandlerAttribute;
|
||||
|
||||
Type messageType = iMHandler.GetMessageType();
|
||||
|
||||
ushort opcode = OpcodeType.Instance.GetOpcode(messageType);
|
||||
if (opcode == 0)
|
||||
{
|
||||
Log.Error($"消息opcode为0: {messageType.Name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
MessageDispatcherInfo messageDispatcherInfo = new (messageHandlerAttribute.SceneType, iMHandler);
|
||||
this.RegisterHandler(opcode, messageDispatcherInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Load()
|
||||
{
|
||||
World.Instance.AddSingleton<MessageDispatcherComponent>(true);
|
||||
}
|
||||
|
||||
private void RegisterHandler(ushort opcode, MessageDispatcherInfo handler)
|
||||
{
|
||||
if (!this.handlers.ContainsKey(opcode))
|
||||
{
|
||||
this.handlers.Add(opcode, new List<MessageDispatcherInfo>());
|
||||
}
|
||||
|
||||
this.handlers[opcode].Add(handler);
|
||||
}
|
||||
|
||||
public void Handle(Session session, object message)
|
||||
{
|
||||
List<MessageDispatcherInfo> actions;
|
||||
ushort opcode = OpcodeType.Instance.GetOpcode(message.GetType());
|
||||
if (!this.handlers.TryGetValue(opcode, out actions))
|
||||
{
|
||||
Log.Error($"消息没有处理: {opcode} {message}");
|
||||
return;
|
||||
}
|
||||
|
||||
SceneType sceneType = session.IScene.SceneType;
|
||||
foreach (MessageDispatcherInfo ev in actions)
|
||||
{
|
||||
if (!ev.SceneType.HasSameFlag(sceneType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ev.IMHandler.Handle(session, message);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ede21855857ffe4aae0f5d80047cc80
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
|
||||
namespace ET
|
||||
{
|
||||
public abstract class MessageHandler<Message>: IMHandler where Message : class
|
||||
{
|
||||
protected abstract ETTask Run(Session session, Message message);
|
||||
|
||||
public void Handle(Session session, object msg)
|
||||
{
|
||||
Message message = msg as Message;
|
||||
if (message == null)
|
||||
{
|
||||
Log.Error($"消息类型转换错误: {msg.GetType().FullName} to {typeof (Message).Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (session.IsDisposed)
|
||||
{
|
||||
Log.Error($"session disconnect {msg}");
|
||||
return;
|
||||
}
|
||||
|
||||
this.Run(session, message).Coroutine();
|
||||
}
|
||||
|
||||
public Type GetMessageType()
|
||||
{
|
||||
return typeof (Message);
|
||||
}
|
||||
|
||||
public Type GetResponseType()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c40ee71fc1a2f144bf4d2ff2cf7c65d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace ET
|
||||
{
|
||||
public class MessageHandlerAttribute: BaseAttribute
|
||||
{
|
||||
public SceneType SceneType { get; }
|
||||
|
||||
public MessageHandlerAttribute(SceneType sceneType)
|
||||
{
|
||||
this.SceneType = sceneType;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61a55441f76423340b0da6e5c2af7c66
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
185
Assets/GameScripts/DotNet/Model/Share/Module/Message/Session.cs
Normal file
185
Assets/GameScripts/DotNet/Model/Share/Module/Message/Session.cs
Normal file
@@ -0,0 +1,185 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
namespace ET
|
||||
{
|
||||
public readonly struct RpcInfo
|
||||
{
|
||||
public readonly IRequest Request;
|
||||
public readonly ETTask<IResponse> Tcs;
|
||||
|
||||
public RpcInfo(IRequest request)
|
||||
{
|
||||
this.Request = request;
|
||||
this.Tcs = ETTask<IResponse>.Create(true);
|
||||
}
|
||||
}
|
||||
|
||||
[EntitySystemOf(typeof(Session))]
|
||||
[FriendOf(typeof(Session))]
|
||||
public static partial class SessionSystem
|
||||
{
|
||||
[EntitySystem]
|
||||
private static void Awake(this Session self, AService aService)
|
||||
{
|
||||
self.AService = aService;
|
||||
long timeNow = self.Fiber().TimeInfo.ClientNow();
|
||||
self.LastRecvTime = timeNow;
|
||||
self.LastSendTime = timeNow;
|
||||
|
||||
self.requestCallbacks.Clear();
|
||||
|
||||
Log.Info($"session create: zone: {self.Zone()} id: {self.Id} {timeNow} ");
|
||||
}
|
||||
|
||||
[EntitySystem]
|
||||
private static void Destroy(this Session self)
|
||||
{
|
||||
self.AService.Remove(self.Id, self.Error);
|
||||
|
||||
foreach (RpcInfo responseCallback in self.requestCallbacks.Values.ToArray())
|
||||
{
|
||||
responseCallback.Tcs.SetException(new RpcException(self.Error, $"session dispose: {self.Id} {self.RemoteAddress}"));
|
||||
}
|
||||
|
||||
Log.Info($"session dispose: {self.RemoteAddress} id: {self.Id} ErrorCode: {self.Error}, please see ErrorCode.cs! {self.Fiber().TimeInfo.ClientNow()}");
|
||||
|
||||
self.requestCallbacks.Clear();
|
||||
}
|
||||
|
||||
public static void OnResponse(this Session self, IResponse response)
|
||||
{
|
||||
if (!self.requestCallbacks.Remove(response.RpcId, out var action))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ErrorCore.IsRpcNeedThrowException(response.Error))
|
||||
{
|
||||
action.Tcs.SetException(new Exception($"Rpc error, request: {action.Request} response: {response}"));
|
||||
return;
|
||||
}
|
||||
action.Tcs.SetResult(response);
|
||||
}
|
||||
|
||||
public static async ETTask<IResponse> Call(this Session self, IRequest request, ETCancellationToken cancellationToken)
|
||||
{
|
||||
int rpcId = ++Session.RpcId;
|
||||
RpcInfo rpcInfo = new RpcInfo(request);
|
||||
self.requestCallbacks[rpcId] = rpcInfo;
|
||||
request.RpcId = rpcId;
|
||||
|
||||
self.Send(request);
|
||||
|
||||
void CancelAction()
|
||||
{
|
||||
if (!self.requestCallbacks.TryGetValue(rpcId, out RpcInfo action))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self.requestCallbacks.Remove(rpcId);
|
||||
Type responseType = OpcodeType.Instance.GetResponseType(action.Request.GetType());
|
||||
IResponse response = (IResponse) Activator.CreateInstance(responseType);
|
||||
response.Error = ErrorCore.ERR_Cancel;
|
||||
action.Tcs.SetResult(response);
|
||||
}
|
||||
|
||||
IResponse ret;
|
||||
try
|
||||
{
|
||||
cancellationToken?.Add(CancelAction);
|
||||
ret = await rpcInfo.Tcs;
|
||||
}
|
||||
finally
|
||||
{
|
||||
cancellationToken?.Remove(CancelAction);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static async ETTask<IResponse> Call(this Session self, IRequest request, int time = 0)
|
||||
{
|
||||
int rpcId = ++Session.RpcId;
|
||||
RpcInfo rpcInfo = new(request);
|
||||
self.requestCallbacks[rpcId] = rpcInfo;
|
||||
request.RpcId = rpcId;
|
||||
self.Send(request);
|
||||
|
||||
if (time > 0)
|
||||
{
|
||||
async ETTask Timeout()
|
||||
{
|
||||
await self.Fiber().TimerComponent.WaitAsync(time);
|
||||
if (!self.requestCallbacks.TryGetValue(rpcId, out RpcInfo action))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self.requestCallbacks.Remove(rpcId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
action.Tcs.SetException(new Exception($"session call timeout: {request} {time}"));
|
||||
}
|
||||
|
||||
Timeout().Coroutine();
|
||||
}
|
||||
|
||||
return await rpcInfo.Tcs;
|
||||
}
|
||||
|
||||
public static void Send(this Session self, IMessage message)
|
||||
{
|
||||
self.Send(default, message);
|
||||
}
|
||||
|
||||
public static void Send(this Session self, ActorId actorId, IMessage message)
|
||||
{
|
||||
self.LastSendTime = self.Fiber().TimeInfo.ClientNow();
|
||||
Log.Debug(message.ToString());
|
||||
self.AService.Send(self.Id, actorId, message as MessageObject);
|
||||
}
|
||||
}
|
||||
|
||||
[ChildOf]
|
||||
public sealed class Session: Entity, IAwake<AService>, IDestroy
|
||||
{
|
||||
public AService AService { get; set; }
|
||||
|
||||
public static int RpcId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public readonly Dictionary<int, RpcInfo> requestCallbacks = new();
|
||||
|
||||
public long LastRecvTime
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public long LastSendTime
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int Error
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public IPEndPoint RemoteAddress
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f666df5ed1cec4d40bd2986c28507634
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,9 @@
|
||||
namespace ET
|
||||
{
|
||||
// 刚accept的session只持续5秒,必须通过验证,否则断开
|
||||
[ComponentOf(typeof(Session))]
|
||||
public class SessionAcceptTimeoutComponent: Entity, IAwake, IDestroy
|
||||
{
|
||||
public long Timer;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37f2d187ca934374393b7b32d87b91f3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
namespace ET
|
||||
{
|
||||
[ComponentOf(typeof(Session))]
|
||||
public class SessionIdleCheckerComponent: Entity, IAwake, IDestroy
|
||||
{
|
||||
public long RepeatedTimer;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d309c1ef46c0eb4bb58c14b6716c032
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user