mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-07 16:45:10 +00:00
[+] TEngineServer
[+] TEngineServer
This commit is contained in:
8
Assets/GameScripts/DotNet/Core/Network/Base.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Network/Base.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d6d37d54409f584d95bcfea9b9345b3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/Network/Base/Enum.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Network/Base/Enum.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 457e84e8ec81f254da550c11a78d4353
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,9 @@
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public enum NetworkProtocolType
|
||||
{
|
||||
None = 0,
|
||||
KCP = 1,
|
||||
TCP = 2
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74351b44f2b6d4a41969a8159e2808ab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,9 @@
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public enum NetworkTarget
|
||||
{
|
||||
None = 0,
|
||||
Outer = 1,
|
||||
Inner = 2
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf647911052fccc40b1d4601ac423b5b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,9 @@
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public enum NetworkType
|
||||
{
|
||||
None = 0,
|
||||
Client = 1,
|
||||
Server = 2
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8841f1e5b0a30eb4cad8a4e0a099adc8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/Network/Base/Helper.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Network/Base/Helper.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a19a0ba64b936934ba63f099234b941a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace TEngine.Core
|
||||
{
|
||||
public static class NetworkHelper
|
||||
{
|
||||
public static string[] GetAddressIPs()
|
||||
{
|
||||
var list = new List<string>();
|
||||
foreach (var networkInterface in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
if (networkInterface.NetworkInterfaceType != NetworkInterfaceType.Ethernet)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var add in networkInterface.GetIPProperties().UnicastAddresses)
|
||||
{
|
||||
list.Add(add.Address.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public static IPEndPoint ToIPEndPoint(string host, int port)
|
||||
{
|
||||
return new IPEndPoint(IPAddress.Parse(host), port);
|
||||
}
|
||||
|
||||
public static IPEndPoint ToIPEndPoint(string address)
|
||||
{
|
||||
var index = address.LastIndexOf(':');
|
||||
var host = address.Substring(0, index);
|
||||
var p = address.Substring(index + 1);
|
||||
var port = int.Parse(p);
|
||||
return ToIPEndPoint(host, port);
|
||||
}
|
||||
|
||||
public static string IPEndPointToStr(this IPEndPoint self)
|
||||
{
|
||||
return $"{self.Address}:{self.Port}";
|
||||
}
|
||||
|
||||
public static void SetSioUdpConnReset(Socket socket)
|
||||
{
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
目前这个问题只有Windows下才会出现。
|
||||
服务器端在发送数据时捕获到了一个异常,
|
||||
这个异常导致原因应该是远程客户端的UDP监听已停止导致数据发送出错。
|
||||
按理说UDP是无连接的,报这个异常是不合理的
|
||||
这个异常让整UDP的服务监听也停止了。
|
||||
这样就因为一个客户端的数据发送无法到达而导致了服务挂了,所有客户端都无法与服务器通信了
|
||||
想详细了解看下https://blog.csdn.net/sunzhen6251/article/details/124168805*/
|
||||
const uint IOC_IN = 0x80000000;
|
||||
const uint IOC_VENDOR = 0x18000000;
|
||||
const int SIO_UDP_CONNRESET = unchecked((int) (IOC_IN | IOC_VENDOR | 12));
|
||||
|
||||
socket.IOControl(SIO_UDP_CONNRESET, new[] {Convert.ToByte(false)}, null);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: adfef17cb18d2624c8b425b5004f9e75
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93c4a9920a37e1b4c9df005d5f1dfa54
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public abstract class AClientNetwork : ANetwork
|
||||
{
|
||||
public uint ChannelId { get; protected set; }
|
||||
public abstract event Action OnDispose;
|
||||
public abstract event Action<uint> OnChangeChannelId;
|
||||
public abstract event Action<APackInfo> OnReceiveMemoryStream;
|
||||
|
||||
protected AClientNetwork(Scene scene, NetworkType networkType, NetworkProtocolType networkProtocolType, NetworkTarget networkTarget) : base(scene, networkType, networkProtocolType, networkTarget) { }
|
||||
|
||||
public abstract uint Connect(IPEndPoint remoteEndPoint, Action onConnectComplete, Action onConnectFail, int connectTimeout = 5000);
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
ChannelId = 0;
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 513303d3661a378438e54a292c1c6d15
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using TEngine.Core;
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8618
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public struct MessageCacheInfo
|
||||
{
|
||||
public uint RpcId;
|
||||
public long RouteId;
|
||||
public long RouteTypeOpCode;
|
||||
public object Message;
|
||||
public MemoryStream MemoryStream;
|
||||
}
|
||||
|
||||
public abstract class ANetwork : IDisposable
|
||||
{
|
||||
public long Id { get; protected set; }
|
||||
public Scene Scene { get; protected set; }
|
||||
public bool IsDisposed { get; protected set; }
|
||||
public NetworkType NetworkType { get; private set; }
|
||||
public NetworkTarget NetworkTarget { get; private set; }
|
||||
public NetworkProtocolType NetworkProtocolType { get; private set; }
|
||||
public ANetworkMessageScheduler NetworkMessageScheduler { get; protected set; }
|
||||
|
||||
protected readonly Func<uint, long, long, MemoryStream, object, MemoryStream> Pack;
|
||||
private readonly LastMessageInfo _lastMessageInfo = new LastMessageInfo();
|
||||
|
||||
protected ANetwork(Scene scene, NetworkType networkType, NetworkProtocolType networkProtocolType, NetworkTarget networkTarget)
|
||||
{
|
||||
Scene = scene;
|
||||
NetworkType = networkType;
|
||||
NetworkTarget = networkTarget;
|
||||
NetworkProtocolType = networkProtocolType;
|
||||
Id = IdFactory.NextRunTimeId();
|
||||
#if TENGINE_NET
|
||||
if (networkTarget == NetworkTarget.Inner)
|
||||
{
|
||||
Pack = InnerPack;
|
||||
NetworkMessageScheduler = new InnerMessageScheduler();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
Pack = OuterPack;
|
||||
|
||||
switch (networkType)
|
||||
{
|
||||
case NetworkType.Client:
|
||||
{
|
||||
NetworkMessageScheduler = new ClientMessageScheduler();
|
||||
return;
|
||||
}
|
||||
case NetworkType.Server:
|
||||
{
|
||||
NetworkMessageScheduler = new OuterMessageScheduler();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if TENGINE_NET
|
||||
private MemoryStream InnerPack(uint rpcId, long routeTypeOpCode, long routeId, MemoryStream memoryStream, object message)
|
||||
{
|
||||
#if TENGINE_DEVELOP
|
||||
if (NetworkThread.Instance.ManagedThreadId != Thread.CurrentThread.ManagedThreadId)
|
||||
{
|
||||
Log.Error("not in NetworkThread!");
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
if (memoryStream != null)
|
||||
{
|
||||
return InnerPacketParser.Pack(rpcId, routeId, memoryStream);
|
||||
}
|
||||
|
||||
// 只针对服务器做缓存消息优化(例如群发消息等)、避免多次序列化
|
||||
if (ReferenceEquals(_lastMessageInfo.Message, message))
|
||||
{
|
||||
_lastMessageInfo.MemoryStream.Seek(0, SeekOrigin.Begin);
|
||||
return _lastMessageInfo.MemoryStream;
|
||||
}
|
||||
|
||||
memoryStream = InnerPacketParser.Pack(rpcId, routeId, message);
|
||||
_lastMessageInfo.MemoryStream = memoryStream;
|
||||
_lastMessageInfo.Message = message;
|
||||
return memoryStream;
|
||||
}
|
||||
#endif
|
||||
private MemoryStream OuterPack(uint rpcId, long routeTypeOpCode, long routeId, MemoryStream memoryStream, object message)
|
||||
{
|
||||
#if TENGINE_DEVELOP
|
||||
if (NetworkThread.Instance.ManagedThreadId != Thread.CurrentThread.ManagedThreadId)
|
||||
{
|
||||
Log.Error("not in NetworkThread!");
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
if (memoryStream != null)
|
||||
{
|
||||
return OuterPacketParser.Pack(rpcId, routeTypeOpCode, memoryStream);
|
||||
}
|
||||
|
||||
// 只针对服务器做缓存消息优化(例如群发消息等)、避免多次序列化
|
||||
// 客户端没有群发消息的功能、一般客户端都是自己缓存消息、如果这里做了缓存反而不好了
|
||||
#if TENGINE_NET
|
||||
if (ReferenceEquals(_lastMessageInfo.Message, message))
|
||||
{
|
||||
_lastMessageInfo.MemoryStream.Seek(0, SeekOrigin.Begin);
|
||||
return _lastMessageInfo.MemoryStream;
|
||||
}
|
||||
#endif
|
||||
memoryStream = OuterPacketParser.Pack(rpcId, routeTypeOpCode, message);
|
||||
#if TENGINE_NET
|
||||
_lastMessageInfo.MemoryStream = memoryStream;
|
||||
_lastMessageInfo.Message = message;
|
||||
#endif
|
||||
return memoryStream;
|
||||
}
|
||||
|
||||
public abstract void Send(uint channelId, uint rpcId, long routeTypeOpCode, long routeId, object message);
|
||||
public abstract void Send(uint channelId, uint rpcId, long routeTypeOpCode, long routeId, MemoryStream memoryStream);
|
||||
public abstract void RemoveChannel(uint channelId);
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
NetworkThread.Instance?.RemoveNetwork(Id);
|
||||
|
||||
Id = 0;
|
||||
Scene = null;
|
||||
IsDisposed = true;
|
||||
NetworkType = NetworkType.None;
|
||||
NetworkTarget = NetworkTarget.None;
|
||||
NetworkProtocolType = NetworkProtocolType.None;
|
||||
|
||||
_lastMessageInfo.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8e1b157f4cd9134b84dd17cbb6cb498
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8618
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public abstract class ANetworkChannel
|
||||
{
|
||||
public uint Id { get; private set; }
|
||||
public Scene Scene { get; protected set; }
|
||||
public long NetworkId { get; private set; }
|
||||
public bool IsDisposed { get; protected set; }
|
||||
public EndPoint RemoteEndPoint { get; protected set; }
|
||||
public APacketParser PacketParser { get; protected set; }
|
||||
|
||||
public uint LocalConn
|
||||
{
|
||||
get
|
||||
{
|
||||
return (uint)this.Id;
|
||||
}
|
||||
private set
|
||||
{
|
||||
this.Id = value;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract event Action OnDispose;
|
||||
public abstract event Action<APackInfo> OnReceiveMemoryStream;
|
||||
|
||||
protected ANetworkChannel(Scene scene, uint id, long networkId)
|
||||
{
|
||||
Id = id;
|
||||
Scene = scene;
|
||||
NetworkId = networkId;
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
NetworkThread.Instance.RemoveChannel(NetworkId, Id);
|
||||
|
||||
Id = 0;
|
||||
Scene = null;
|
||||
NetworkId = 0;
|
||||
IsDisposed = true;
|
||||
RemoteEndPoint = null;
|
||||
|
||||
if (PacketParser != null)
|
||||
{
|
||||
PacketParser.Dispose();
|
||||
PacketParser = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cef25b06be672c04993353b330cef248
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,7 @@
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public interface INetworkUpdate
|
||||
{
|
||||
void Update();
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7eb7bbbac8bc38f429bad42158a0eb39
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8618
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public class LastMessageInfo : IDisposable
|
||||
{
|
||||
public object Message;
|
||||
public MemoryStream MemoryStream;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Message = null;
|
||||
MemoryStream = null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9135b10424d287419b2b9b46caa803d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/Network/Base/Server.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Network/Base/Server.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9629d3993af22245b83309fcfee436e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,12 @@
|
||||
#if TENGINE_NET
|
||||
namespace TEngine;
|
||||
|
||||
public class MachineConfigInfo
|
||||
{
|
||||
public uint Id;
|
||||
public string OuterIP;
|
||||
public string OuterBindIP;
|
||||
public string InnerBindIP;
|
||||
public int ManagementPort;
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c1e5851b16c4a4d49b253c124b953fb3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,19 @@
|
||||
namespace TEngine
|
||||
{
|
||||
public class SceneConfigInfo
|
||||
{
|
||||
public Scene Scene;
|
||||
public long EntityId;
|
||||
|
||||
public uint Id;
|
||||
public string SceneType;
|
||||
public string Name;
|
||||
public string NetworkProtocol;
|
||||
public uint RouteId;
|
||||
public uint WorldId;
|
||||
public int OuterPort;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4fb765098d2e8f34894ec2696a260f48
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
173
Assets/GameScripts/DotNet/Core/Network/Base/Server/Server.cs
Normal file
173
Assets/GameScripts/DotNet/Core/Network/Base/Server/Server.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
#if TENGINE_NET
|
||||
using TEngine.Core;
|
||||
using TEngine.Core.Network;
|
||||
#pragma warning disable CS8603
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8618
|
||||
|
||||
namespace TEngine
|
||||
{
|
||||
public sealed class Server
|
||||
{
|
||||
public uint Id { get; private set; }
|
||||
public Scene Scene { get; private set; }
|
||||
private readonly Dictionary<uint, ConnectInfo> _sessions = new Dictionary<uint, ConnectInfo>();
|
||||
|
||||
private sealed class ConnectInfo : IDisposable
|
||||
{
|
||||
public Session Session;
|
||||
public Entity NetworkComponent;
|
||||
|
||||
public ConnectInfo(Session session, Entity networkComponent)
|
||||
{
|
||||
Session = session;
|
||||
NetworkComponent = networkComponent;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Session != null)
|
||||
{
|
||||
Session.Dispose();
|
||||
Session = null;
|
||||
}
|
||||
|
||||
if (NetworkComponent != null)
|
||||
{
|
||||
NetworkComponent.Dispose();
|
||||
NetworkComponent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Session GetSession(uint targetServerId)
|
||||
{
|
||||
if (_sessions.TryGetValue(targetServerId, out var connectInfo))
|
||||
{
|
||||
if (!connectInfo.Session.IsDisposed)
|
||||
{
|
||||
return connectInfo.Session;
|
||||
}
|
||||
|
||||
_sessions.Remove(targetServerId);
|
||||
}
|
||||
|
||||
// 同一个Server下、只需要内部走下消息派发就可以
|
||||
|
||||
if (Id == targetServerId)
|
||||
{
|
||||
var serverNetworkComponent = Scene.GetComponent<ServerNetworkComponent>();
|
||||
var session = Session.Create(serverNetworkComponent.Network);
|
||||
_sessions.Add(targetServerId, new ConnectInfo(session, null));
|
||||
return session;
|
||||
}
|
||||
|
||||
// 不同的Server下需要建立网络连接
|
||||
|
||||
var serverConfigInfo = ConfigTableManage.ServerConfig(targetServerId);
|
||||
|
||||
if (serverConfigInfo == null)
|
||||
{
|
||||
throw new Exception($"The server with ServerId {targetServerId} was not found in the configuration file");
|
||||
}
|
||||
|
||||
var machineConfigInfo = ConfigTableManage.MachineConfig(serverConfigInfo.MachineId);
|
||||
|
||||
if (machineConfigInfo == null)
|
||||
{
|
||||
throw new Exception($"Server.cs The specified MachineId was not found: {serverConfigInfo.MachineId}");
|
||||
}
|
||||
|
||||
var ipEndPoint = NetworkHelper.ToIPEndPoint($"{machineConfigInfo.InnerBindIP}:{serverConfigInfo.InnerPort}");
|
||||
var clientNetworkComponent = Entity.Create<ClientNetworkComponent>(Scene);
|
||||
clientNetworkComponent.Initialize(NetworkProtocolType.KCP, NetworkTarget.Inner);
|
||||
clientNetworkComponent.Connect(ipEndPoint,null, () =>
|
||||
{
|
||||
Log.Error($"Unable to connect to the target server sourceServerId:{Id} targetServerId:{targetServerId}");
|
||||
});
|
||||
_sessions.Add(targetServerId, new ConnectInfo(clientNetworkComponent.Session, clientNetworkComponent));
|
||||
return clientNetworkComponent.Session;
|
||||
}
|
||||
|
||||
public void RemoveSession(uint targetServerId)
|
||||
{
|
||||
if (!_sessions.Remove(targetServerId, out var connectInfo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
connectInfo.Dispose();
|
||||
}
|
||||
|
||||
#region Static
|
||||
|
||||
private static readonly Dictionary<uint, Server> Servers = new Dictionary<uint, Server>();
|
||||
|
||||
public static async FTask Create(uint routeId)
|
||||
{
|
||||
var serverConfigInfo = ConfigTableManage.ServerConfig(routeId);
|
||||
|
||||
if (serverConfigInfo == null)
|
||||
{
|
||||
Log.Error($"not found server by Id:{routeId}");
|
||||
return;
|
||||
}
|
||||
|
||||
var machineConfigInfo = ConfigTableManage.MachineConfig(serverConfigInfo.MachineId);
|
||||
|
||||
if (machineConfigInfo == null)
|
||||
{
|
||||
Log.Error($"not found machine by Id:{serverConfigInfo.MachineId}");
|
||||
return;
|
||||
}
|
||||
|
||||
var sceneInfos = Scene.GetSceneInfoByRouteId(routeId);
|
||||
await Create(routeId, machineConfigInfo.InnerBindIP, serverConfigInfo.InnerPort, machineConfigInfo.OuterBindIP, sceneInfos);
|
||||
// Log.Info($"ServerId:{routeId} is start complete");
|
||||
}
|
||||
|
||||
public static async FTask<Server> Create(uint routeId, string innerBindIp, int innerPort, string outerBindIp, List<SceneConfigInfo> sceneInfos)
|
||||
{
|
||||
if (Servers.TryGetValue(routeId, out var server))
|
||||
{
|
||||
return server;
|
||||
}
|
||||
|
||||
// 创建一个新的Server、创建一个临时Scene给Server当做Scene使用
|
||||
|
||||
server = new Server
|
||||
{
|
||||
Id = routeId
|
||||
};
|
||||
|
||||
server.Scene = await Scene.Create($"ServerScene{routeId}", server, new EntityIdStruct(routeId, 0, 0));
|
||||
|
||||
// 创建网络、Server下的网络只能是内部网络、外部网络是在Scene中定义
|
||||
|
||||
if (!string.IsNullOrEmpty(innerBindIp) && innerPort != 0)
|
||||
{
|
||||
var address = NetworkHelper.ToIPEndPoint(innerBindIp, innerPort);
|
||||
var serverNetworkComponent = server.Scene.AddComponent<ServerNetworkComponent>();
|
||||
serverNetworkComponent.Initialize(NetworkProtocolType.KCP, NetworkTarget.Inner, address);
|
||||
}
|
||||
|
||||
// 创建Server拥有所有的Scene
|
||||
|
||||
foreach (var sceneConfig in sceneInfos)
|
||||
{
|
||||
await Scene.Create(server, outerBindIp, sceneConfig);
|
||||
}
|
||||
|
||||
Servers.Add(routeId, server);
|
||||
return server;
|
||||
}
|
||||
|
||||
public static Server Get(uint routeId)
|
||||
{
|
||||
return Servers.TryGetValue(routeId, out var server) ? server : null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c108c5f2d1cfef844b1842be027fda0d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,10 @@
|
||||
#if TENGINE_NET
|
||||
namespace TEngine;
|
||||
|
||||
public class ServerConfigInfo
|
||||
{
|
||||
public uint Id;
|
||||
public uint MachineId;
|
||||
public int InnerPort;
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9d2ecc96f78db214fa45582d7b146b93
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,51 @@
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public static class SocketExtensions
|
||||
{
|
||||
public static void SetSocketBufferToOsLimit(this Socket socket)
|
||||
{
|
||||
socket.SetReceiveBufferToOSLimit();
|
||||
socket.SetSendBufferToOSLimit();
|
||||
}
|
||||
|
||||
// 100k attempts of 1 KB increases = default + 100 MB max
|
||||
public static void SetReceiveBufferToOSLimit(this Socket socket, int stepSize = 1024, int attempts = 100_000)
|
||||
{
|
||||
// setting a too large size throws a socket exception.
|
||||
// so let's keep increasing until we encounter it.
|
||||
for (int i = 0; i < attempts; ++i)
|
||||
{
|
||||
// increase in 1 KB steps
|
||||
try
|
||||
{
|
||||
socket.ReceiveBufferSize += stepSize;
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 100k attempts of 1 KB increases = default + 100 MB max
|
||||
public static void SetSendBufferToOSLimit(this Socket socket, int stepSize = 1024, int attempts = 100_000)
|
||||
{
|
||||
// setting a too large size throws a socket exception.
|
||||
// so let's keep increasing until we encounter it.
|
||||
for (var i = 0; i < attempts; ++i)
|
||||
{
|
||||
// increase in 1 KB steps
|
||||
try
|
||||
{
|
||||
socket.SendBufferSize += stepSize;
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4638b4ab890780c419b527a606f0b184
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/Network/Entity.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Network/Entity.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9ea43e4953ac874aa8ec1c44059c396
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8618
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public sealed class ClientNetworkComponent : Entity
|
||||
{
|
||||
private AClientNetwork Network { get; set; }
|
||||
public Session Session { get; private set; }
|
||||
|
||||
public void Initialize(NetworkProtocolType networkProtocolType, NetworkTarget networkTarget)
|
||||
{
|
||||
switch (networkProtocolType)
|
||||
{
|
||||
case NetworkProtocolType.KCP:
|
||||
{
|
||||
Network = new KCPClientNetwork(Scene, networkTarget);
|
||||
return;
|
||||
}
|
||||
case NetworkProtocolType.TCP:
|
||||
{
|
||||
Network = new TCPClientNetwork(Scene, networkTarget);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException($"Unsupported NetworkProtocolType:{networkProtocolType}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Connect(IPEndPoint remoteEndPoint, Action onConnectComplete, Action onConnectFail, int connectTimeout = 5000)
|
||||
{
|
||||
if (Network == null || Network.IsDisposed)
|
||||
{
|
||||
throw new NotSupportedException("Network is null or isDisposed");
|
||||
}
|
||||
|
||||
Network.Connect(remoteEndPoint, onConnectComplete, onConnectFail, connectTimeout);
|
||||
Session = Session.Create(Network);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (Network != null)
|
||||
{
|
||||
Network.Dispose();
|
||||
Network = null;
|
||||
}
|
||||
|
||||
Session = null;
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8de6a8ca9e15721489a435305757da53
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8618
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public sealed class ServerNetworkComponent : Entity, INotSupportedPool
|
||||
{
|
||||
public ANetwork Network { get; private set; }
|
||||
|
||||
public void Initialize(NetworkProtocolType networkProtocolType, NetworkTarget networkTarget, IPEndPoint address)
|
||||
{
|
||||
switch (networkProtocolType)
|
||||
{
|
||||
case NetworkProtocolType.KCP:
|
||||
{
|
||||
Network = new KCPServerNetwork(Scene, networkTarget, address);
|
||||
// Log.Info($"NetworkProtocol:KCP IPEndPoint:{address}");
|
||||
return;
|
||||
}
|
||||
case NetworkProtocolType.TCP:
|
||||
{
|
||||
Network = new TCPServerNetwork(Scene, networkTarget, address);
|
||||
// Log.Info($"NetworkProtocol:TCP IPEndPoint:{address}");
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException($"Unsupported NetworkProtocolType:{networkProtocolType}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (Network != null)
|
||||
{
|
||||
Network.Dispose();
|
||||
Network = null;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08a60481ef8b51540b31a673aa1837e3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 313e3a0020362b4408b2b68e276004e6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,49 @@
|
||||
using TEngine.Core;
|
||||
|
||||
#if TENGINE_NET
|
||||
namespace TEngine.Core.Network;
|
||||
|
||||
public sealed class ServerInnerSession : Session
|
||||
{
|
||||
public override void Send(object message, uint rpcId = 0, long routeId = 0)
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 序列化消息到流中
|
||||
var memoryStream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
InnerPacketParser.Serialize(message, memoryStream);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
// 分发消息
|
||||
var packInfo = InnerPackInfo.Create(rpcId, routeId, ((IMessage)message).OpCode(), 0, memoryStream);
|
||||
NetworkMessageScheduler.Scheduler(this, packInfo).Coroutine();
|
||||
}
|
||||
|
||||
public override void Send(IRouteMessage routeMessage, uint rpcId = 0, long routeId = 0)
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// 序列化消息到流中
|
||||
var memoryStream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
InnerPacketParser.Serialize(routeMessage, memoryStream);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
// 分发消息
|
||||
var packInfo = InnerPackInfo.Create(rpcId, routeId, routeMessage.OpCode(), routeMessage.RouteTypeOpCode(), memoryStream);
|
||||
NetworkMessageScheduler.Scheduler(this, packInfo).Coroutine();
|
||||
}
|
||||
|
||||
public override void Send(MemoryStream memoryStream, uint rpcId = 0, long routeTypeOpCode = 0, long routeId = 0)
|
||||
{
|
||||
throw new Exception("The use of this method is not supported");
|
||||
}
|
||||
|
||||
public override FTask<IResponse> Call(IRequest request, long routeId = 0)
|
||||
{
|
||||
throw new Exception("The use of this method is not supported");
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 787fbc8b90a182644abe477d98ed8939
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
212
Assets/GameScripts/DotNet/Core/Network/Entity/Session/Session.cs
Normal file
212
Assets/GameScripts/DotNet/Core/Network/Entity/Session/Session.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using TEngine.Core;
|
||||
#pragma warning disable CS8603
|
||||
#pragma warning disable CS8601
|
||||
#pragma warning disable CS8618
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public class Session : Entity, INotSupportedPool, ISupportedMultiEntity
|
||||
{
|
||||
private uint _rpcId;
|
||||
public long NetworkId { get; private set; }
|
||||
public uint ChannelId { get; private set; }
|
||||
public long LastReceiveTime { get; private set; }
|
||||
public ANetworkMessageScheduler NetworkMessageScheduler { get; private set;}
|
||||
private static readonly Dictionary<long, Session> Sessions = new ();
|
||||
public readonly Dictionary<long, FTask<IResponse>> RequestCallback = new();
|
||||
|
||||
public static void Create(ANetworkMessageScheduler networkMessageScheduler, ANetworkChannel channel)
|
||||
{
|
||||
#if TENGINE_DEVELOP
|
||||
if (ThreadSynchronizationContext.Main.ThreadId != Thread.CurrentThread.ManagedThreadId)
|
||||
{
|
||||
throw new NotSupportedException("Session Create not in MainThread");
|
||||
}
|
||||
#endif
|
||||
var session = Entity.Create<Session>(channel.Scene);
|
||||
session.ChannelId = channel.Id;
|
||||
session.NetworkId = channel.NetworkId;
|
||||
session.NetworkMessageScheduler = networkMessageScheduler;
|
||||
channel.OnDispose += session.Dispose;
|
||||
channel.OnReceiveMemoryStream += session.OnReceive;
|
||||
Sessions.Add(session.Id, session);
|
||||
}
|
||||
|
||||
public static Session Create(AClientNetwork network)
|
||||
{
|
||||
#if TENGINE_DEVELOP
|
||||
if (ThreadSynchronizationContext.Main.ThreadId != Thread.CurrentThread.ManagedThreadId)
|
||||
{
|
||||
throw new NotSupportedException("Session Create not in MainThread");
|
||||
}
|
||||
#endif
|
||||
var session = Entity.Create<Session>(network.Scene);
|
||||
session.ChannelId = network.ChannelId;
|
||||
session.NetworkId = network.Id;
|
||||
session.NetworkMessageScheduler = network.NetworkMessageScheduler;
|
||||
network.OnDispose += session.Dispose;
|
||||
network.OnChangeChannelId += session.OnChangeChannelId;
|
||||
network.OnReceiveMemoryStream += session.OnReceive;
|
||||
Sessions.Add(session.Id, session);
|
||||
return session;
|
||||
}
|
||||
#if TENGINE_NET
|
||||
public static ServerInnerSession Create(ANetwork network)
|
||||
{
|
||||
#if TENGINE_DEVELOP
|
||||
if (ThreadSynchronizationContext.Main.ThreadId != Thread.CurrentThread.ManagedThreadId)
|
||||
{
|
||||
throw new NotSupportedException("Session Create not in MainThread");
|
||||
}
|
||||
#endif
|
||||
var session = Entity.Create<ServerInnerSession>(network.Scene);
|
||||
session.NetworkMessageScheduler = network.NetworkMessageScheduler;
|
||||
Sessions.Add(session.Id, session);
|
||||
return session;
|
||||
}
|
||||
|
||||
public static ServerInnerSession CreateServerInner(Scene scene)
|
||||
{
|
||||
#if TENGINE_DEVELOP
|
||||
if (ThreadSynchronizationContext.Main.ThreadId != Thread.CurrentThread.ManagedThreadId)
|
||||
{
|
||||
throw new NotSupportedException("Session Create not in MainThread");
|
||||
}
|
||||
#endif
|
||||
var session = Entity.Create<ServerInnerSession>(scene, false);
|
||||
Sessions.Add(session.Id, session);
|
||||
return session;
|
||||
}
|
||||
#endif
|
||||
|
||||
public static bool TryGet(long id, out Session session)
|
||||
{
|
||||
return Sessions.TryGetValue(id, out session);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
#if TENGINE_DEVELOP
|
||||
if (ThreadSynchronizationContext.Main.ThreadId != Thread.CurrentThread.ManagedThreadId)
|
||||
{
|
||||
throw new NotSupportedException("Session Create not in MainThread");
|
||||
}
|
||||
#endif
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var id = Id;
|
||||
var networkId = NetworkId;
|
||||
var channelId = ChannelId;
|
||||
|
||||
foreach (var requestCallback in RequestCallback.Values.ToArray())
|
||||
{
|
||||
requestCallback.SetException(new Exception($"session is dispose: {Id}"));
|
||||
}
|
||||
|
||||
if (networkId != 0 && channelId != 0)
|
||||
{
|
||||
NetworkThread.Instance.SynchronizationContext.Post(() =>
|
||||
{
|
||||
NetworkThread.Instance?.RemoveChannel(networkId, channelId);
|
||||
});
|
||||
}
|
||||
|
||||
Sessions.Remove(id);
|
||||
#if NETDEBUG
|
||||
Log.Debug($"Sessions Dispose Count:{Sessions.Count}");
|
||||
#endif
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
public virtual void Send(object message, uint rpcId = 0, long routeId = 0)
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkThread.Instance.Send(NetworkId, ChannelId, rpcId, 0, routeId, message);
|
||||
}
|
||||
|
||||
public virtual void Send(IRouteMessage routeMessage, uint rpcId = 0, long routeId = 0)
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkThread.Instance.Send(NetworkId, ChannelId, rpcId, routeMessage.RouteTypeOpCode(), routeId, routeMessage);
|
||||
}
|
||||
|
||||
public virtual void Send(MemoryStream memoryStream, uint rpcId = 0, long routeTypeOpCode = 0, long routeId = 0)
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkThread.Instance.SendStream(NetworkId, ChannelId, rpcId, routeTypeOpCode, routeId, memoryStream);
|
||||
}
|
||||
|
||||
public virtual FTask<IResponse> Call(IRequest request, long routeId = 0)
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var requestCallback = FTask<IResponse>.Create();
|
||||
|
||||
unchecked
|
||||
{
|
||||
var rpcId = ++_rpcId;
|
||||
RequestCallback.Add(rpcId, requestCallback);
|
||||
|
||||
if (request is IRouteMessage iRouteMessage)
|
||||
{
|
||||
Send(iRouteMessage, rpcId, routeId);
|
||||
}
|
||||
else
|
||||
{
|
||||
Send(request, rpcId, routeId);
|
||||
}
|
||||
}
|
||||
|
||||
return requestCallback;
|
||||
}
|
||||
|
||||
private void OnReceive(APackInfo packInfo)
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LastReceiveTime = TimeHelper.Now;
|
||||
|
||||
try
|
||||
{
|
||||
NetworkMessageScheduler.Scheduler(this, packInfo).Coroutine();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// 如果解析失败,只有一种可能,那就是有人恶意发包。
|
||||
// 所以这里强制关闭了当前连接。不让对方一直发包。
|
||||
Dispose();
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnChangeChannelId(uint channelId)
|
||||
{
|
||||
ChannelId = channelId;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a7d60d383d04c84888fd48783813ed3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/Network/Exception.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Network/Exception.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b62e9f6a86604cf469ab9d9f47bddc5a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public class ScanException : Exception
|
||||
{
|
||||
public ScanException() { }
|
||||
|
||||
public ScanException(string msg) : base(msg) { }
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8258e475fd71ff040a40766c65bd34a4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/Network/Message.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Network/Message.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4192fecfffd49194086253fef2b074f7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab17a7b38d59bc84aafe2d54a2a6c834
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,104 @@
|
||||
#if TENGINE_NET
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public static class AddressableHelper
|
||||
{
|
||||
private static readonly List<SceneConfigInfo> AddressableScenes = new List<SceneConfigInfo>();
|
||||
|
||||
static AddressableHelper()
|
||||
{
|
||||
var sceneConfigInfos = ConfigTableManage.AllSceneConfig();
|
||||
|
||||
foreach (var sceneConfigInfo in sceneConfigInfos)
|
||||
{
|
||||
if (sceneConfigInfo.SceneType == "Addressable")
|
||||
{
|
||||
AddressableScenes.Add(sceneConfigInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async FTask AddAddressable(Scene scene, long addressableId, long routeId)
|
||||
{
|
||||
var addressableScene = AddressableScenes[(int)addressableId % AddressableScenes.Count];
|
||||
var response = await MessageHelper.CallInnerRoute(scene, addressableScene.EntityId,
|
||||
new I_AddressableAdd_Request
|
||||
{
|
||||
AddressableId = addressableId, RouteId = routeId
|
||||
});
|
||||
|
||||
if (response.ErrorCode != 0)
|
||||
{
|
||||
Log.Error($"AddAddressable error is {response.ErrorCode}");
|
||||
}
|
||||
}
|
||||
|
||||
public static async FTask<long> GetAddressableRouteId(Scene scene, long addressableId)
|
||||
{
|
||||
var addressableScene = AddressableScenes[(int)addressableId % AddressableScenes.Count];
|
||||
|
||||
var response = (I_AddressableGet_Response) await MessageHelper.CallInnerRoute(scene, addressableScene.EntityId,
|
||||
new I_AddressableGet_Request
|
||||
{
|
||||
AddressableId = addressableId
|
||||
});
|
||||
|
||||
if (response.ErrorCode == 0)
|
||||
{
|
||||
return response.RouteId;
|
||||
}
|
||||
|
||||
Log.Error($"GetAddressable error is {response.ErrorCode} addressableId:{addressableId}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static async FTask RemoveAddressable(Scene scene, long addressableId)
|
||||
{
|
||||
var addressableScene = AddressableScenes[(int)addressableId % AddressableScenes.Count];
|
||||
var response = await MessageHelper.CallInnerRoute(scene, addressableScene.EntityId,
|
||||
new I_AddressableRemove_Request
|
||||
{
|
||||
AddressableId = addressableId
|
||||
});
|
||||
|
||||
if (response.ErrorCode != 0)
|
||||
{
|
||||
Log.Error($"RemoveAddressable error is {response.ErrorCode}");
|
||||
}
|
||||
}
|
||||
|
||||
public static async FTask LockAddressable(Scene scene, long addressableId)
|
||||
{
|
||||
var addressableScene = AddressableScenes[(int)addressableId % AddressableScenes.Count];
|
||||
var response = await MessageHelper.CallInnerRoute(scene, addressableScene.EntityId,
|
||||
new I_AddressableLock_Request
|
||||
{
|
||||
AddressableId = addressableId
|
||||
});
|
||||
|
||||
if (response.ErrorCode != 0)
|
||||
{
|
||||
Log.Error($"LockAddressable error is {response.ErrorCode}");
|
||||
}
|
||||
}
|
||||
|
||||
public static async FTask UnLockAddressable(Scene scene, long addressableId, long routeId, string source)
|
||||
{
|
||||
var addressableScene = AddressableScenes[(int)addressableId % AddressableScenes.Count];
|
||||
var response = await MessageHelper.CallInnerRoute(scene, addressableScene.EntityId,
|
||||
new I_AddressableUnLock_Request
|
||||
{
|
||||
AddressableId = addressableId,
|
||||
RouteId = routeId,
|
||||
Source = source
|
||||
});
|
||||
|
||||
if (response.ErrorCode != 0)
|
||||
{
|
||||
Log.Error($"UnLockAddressable error is {response.ErrorCode}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8589a3e288b52f4e92a04053ba0c6bf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,62 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public sealed class AddressableManageComponent : Entity
|
||||
{
|
||||
private readonly Dictionary<long, long> _addressable = new();
|
||||
private readonly Dictionary<long, WaitCoroutineLock> _locks = new();
|
||||
private readonly CoroutineLockQueueType _addressableLock = new CoroutineLockQueueType("AddressableLock");
|
||||
|
||||
public async FTask Add(long addressableId, long routeId)
|
||||
{
|
||||
using (await _addressableLock.Lock(addressableId))
|
||||
{
|
||||
_addressable[addressableId] = routeId;
|
||||
Log.Debug($"AddressableManageComponent Add addressableId:{addressableId} routeId:{routeId}");
|
||||
}
|
||||
}
|
||||
|
||||
public async FTask<long> Get(long addressableId)
|
||||
{
|
||||
using (await _addressableLock.Lock(addressableId))
|
||||
{
|
||||
_addressable.TryGetValue(addressableId, out var routeId);
|
||||
return routeId;
|
||||
}
|
||||
}
|
||||
|
||||
public async FTask Remove(long addressableId)
|
||||
{
|
||||
using (await _addressableLock.Lock(addressableId))
|
||||
{
|
||||
_addressable.Remove(addressableId);
|
||||
}
|
||||
}
|
||||
|
||||
public async FTask Lock(long addressableId)
|
||||
{
|
||||
var waitCoroutineLock = await _addressableLock.Lock(addressableId);
|
||||
_locks.Add(addressableId, waitCoroutineLock);
|
||||
}
|
||||
|
||||
public void UnLock(long addressableId, long routeId, string source)
|
||||
{
|
||||
if (!_locks.Remove(addressableId, out var coroutineLock))
|
||||
{
|
||||
Log.Error($"Addressable unlock not found addressableId: {addressableId} Source:{source}");
|
||||
return;
|
||||
}
|
||||
|
||||
_addressable.TryGetValue(addressableId, out var oldAddressableId);
|
||||
|
||||
if (routeId != 0)
|
||||
{
|
||||
_addressable[addressableId] = routeId;
|
||||
}
|
||||
|
||||
coroutineLock.Dispose();
|
||||
Log.Debug($"Addressable UnLock key: {addressableId} oldAddressableId : {oldAddressableId} routeId: {routeId} Source:{source}");
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e946a97e041db3b4f89af34ac9914098
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,59 @@
|
||||
#if TENGINE_NET
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// 可寻址消息组件、挂载了这个组件可以接收Addressable消息
|
||||
/// </summary>
|
||||
public sealed class AddressableMessageComponent : Entity
|
||||
{
|
||||
public long AddressableId;
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (AddressableId != 0)
|
||||
{
|
||||
AddressableHelper.RemoveAddressable(Scene, AddressableId).Coroutine();
|
||||
AddressableId = 0;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
public FTask Register()
|
||||
{
|
||||
if (Parent == null)
|
||||
{
|
||||
throw new Exception("AddressableRouteComponent must be mounted under a component");
|
||||
}
|
||||
|
||||
AddressableId = Parent.Id;
|
||||
|
||||
if (AddressableId == 0)
|
||||
{
|
||||
throw new Exception("AddressableRouteComponent.Parent.Id is null");
|
||||
}
|
||||
|
||||
#if TENGINE_DEVELOP
|
||||
Log.Debug($"AddressableMessageComponent Register addressableId:{AddressableId} RouteId:{Parent.RuntimeId}");
|
||||
#endif
|
||||
return AddressableHelper.AddAddressable(Scene, AddressableId, Parent.RuntimeId);
|
||||
}
|
||||
|
||||
public FTask Lock()
|
||||
{
|
||||
#if TENGINE_DEVELOP
|
||||
Log.Debug($"AddressableMessageComponent Lock {Parent.Id}");
|
||||
#endif
|
||||
return AddressableHelper.LockAddressable(Scene, Parent.Id);
|
||||
}
|
||||
|
||||
public FTask UnLock(string source)
|
||||
{
|
||||
#if TENGINE_DEVELOP
|
||||
Log.Debug($"AddressableMessageComponent UnLock {Parent.Id} {Parent.RuntimeId}");
|
||||
#endif
|
||||
return AddressableHelper.UnLockAddressable(Scene, Parent.Id, Parent.RuntimeId, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be31fb4b0e977f347837b7b6b6b840d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,191 @@
|
||||
#pragma warning disable CS8603
|
||||
#pragma warning disable CS8600
|
||||
#if TENGINE_NET
|
||||
namespace TEngine.Core.Network;
|
||||
|
||||
public sealed class AddressableRouteComponentAwakeSystem : AwakeSystem<AddressableRouteComponent>
|
||||
{
|
||||
protected override void Awake(AddressableRouteComponent self)
|
||||
{
|
||||
self.Awake();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 可寻址消息组件、挂载了这个组件可以接收和发送Addressable消息
|
||||
/// </summary>
|
||||
public sealed class AddressableRouteComponent : Entity
|
||||
{
|
||||
private long _parentId;
|
||||
private long _addressableRouteId;
|
||||
public static readonly CoroutineLockQueueType AddressableRouteMessageLock = new CoroutineLockQueueType("AddressableRouteMessageLock");
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_parentId = 0;
|
||||
_addressableRouteId = 0;
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
if (Parent == null)
|
||||
{
|
||||
throw new Exception("AddressableRouteComponent must be mounted under a component");
|
||||
}
|
||||
|
||||
if (Parent.RuntimeId == 0)
|
||||
{
|
||||
throw new Exception("AddressableRouteComponent.Parent.RuntimeId is null");
|
||||
}
|
||||
|
||||
_parentId = Parent.Id;
|
||||
}
|
||||
|
||||
public void SetAddressableRouteId(long addressableRouteId)
|
||||
{
|
||||
_addressableRouteId = addressableRouteId;
|
||||
}
|
||||
|
||||
public void Send(IAddressableRouteMessage message)
|
||||
{
|
||||
Call(message).Coroutine();
|
||||
}
|
||||
|
||||
public void Send(long routeTypeOpCode, Type requestType, MemoryStream message)
|
||||
{
|
||||
Call(routeTypeOpCode, requestType, message).Coroutine();
|
||||
}
|
||||
|
||||
public async FTask<IResponse> Call(long routeTypeOpCode, Type requestType, MemoryStream request)
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return MessageDispatcherSystem.Instance.CreateResponse(requestType, CoreErrorCode.ErrNotFoundRoute);
|
||||
}
|
||||
|
||||
var failCount = 0;
|
||||
var runtimeId = RuntimeId;
|
||||
IResponse iRouteResponse = null;
|
||||
|
||||
using (await AddressableRouteMessageLock.Lock(_parentId, "AddressableRouteComponent Call MemoryStream"))
|
||||
{
|
||||
while (!IsDisposed)
|
||||
{
|
||||
if (_addressableRouteId == 0)
|
||||
{
|
||||
_addressableRouteId = await AddressableHelper.GetAddressableRouteId(Scene, _parentId);
|
||||
}
|
||||
|
||||
if (_addressableRouteId == 0)
|
||||
{
|
||||
return MessageDispatcherSystem.Instance.CreateResponse(requestType, CoreErrorCode.ErrNotFoundRoute);
|
||||
}
|
||||
|
||||
iRouteResponse = await MessageHelper.CallInnerRoute(Scene, _addressableRouteId, routeTypeOpCode, requestType, request);
|
||||
|
||||
if (runtimeId != RuntimeId)
|
||||
{
|
||||
iRouteResponse.ErrorCode = CoreErrorCode.ErrRouteTimeout;
|
||||
}
|
||||
|
||||
switch (iRouteResponse.ErrorCode)
|
||||
{
|
||||
case CoreErrorCode.ErrRouteTimeout:
|
||||
{
|
||||
return iRouteResponse;
|
||||
}
|
||||
case CoreErrorCode.ErrNotFoundRoute:
|
||||
{
|
||||
if (++failCount > 20)
|
||||
{
|
||||
Log.Error($"AddressableComponent.Call failCount > 20 route send message fail, routeId: {_addressableRouteId} AddressableRouteComponent:{Id}");
|
||||
return iRouteResponse;
|
||||
}
|
||||
|
||||
await TimerScheduler.Instance.Core.WaitAsync(500);
|
||||
|
||||
if (runtimeId != RuntimeId)
|
||||
{
|
||||
iRouteResponse.ErrorCode = CoreErrorCode.ErrRouteTimeout;
|
||||
}
|
||||
|
||||
_addressableRouteId = 0;
|
||||
continue;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return iRouteResponse;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iRouteResponse;
|
||||
}
|
||||
|
||||
public async FTask<IResponse> Call(IAddressableRouteMessage request)
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return MessageDispatcherSystem.Instance.CreateResponse(request, CoreErrorCode.ErrNotFoundRoute);
|
||||
}
|
||||
|
||||
var failCount = 0;
|
||||
var runtimeId = RuntimeId;
|
||||
|
||||
using (await AddressableRouteMessageLock.Lock(_parentId,"AddressableRouteComponent Call"))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (_addressableRouteId == 0)
|
||||
{
|
||||
_addressableRouteId = await AddressableHelper.GetAddressableRouteId(Scene, _parentId);
|
||||
}
|
||||
|
||||
if (_addressableRouteId == 0)
|
||||
{
|
||||
return MessageDispatcherSystem.Instance.CreateResponse(request, CoreErrorCode.ErrNotFoundRoute);
|
||||
}
|
||||
|
||||
var iRouteResponse = await MessageHelper.CallInnerRoute(Scene, _addressableRouteId, request);
|
||||
|
||||
if (runtimeId != RuntimeId)
|
||||
{
|
||||
iRouteResponse.ErrorCode = CoreErrorCode.ErrRouteTimeout;
|
||||
}
|
||||
|
||||
switch (iRouteResponse.ErrorCode)
|
||||
{
|
||||
case CoreErrorCode.ErrNotFoundRoute:
|
||||
{
|
||||
if (++failCount > 20)
|
||||
{
|
||||
Log.Error($"AddressableRouteComponent.Call failCount > 20 route send message fail, routeId: {_addressableRouteId} AddressableRouteComponent:{Id}");
|
||||
return iRouteResponse;
|
||||
}
|
||||
|
||||
await TimerScheduler.Instance.Core.WaitAsync(500);
|
||||
|
||||
if (runtimeId != RuntimeId)
|
||||
{
|
||||
iRouteResponse.ErrorCode = CoreErrorCode.ErrRouteTimeout;
|
||||
}
|
||||
|
||||
_addressableRouteId = 0;
|
||||
continue;
|
||||
}
|
||||
case CoreErrorCode.ErrRouteTimeout:
|
||||
{
|
||||
return iRouteResponse;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return iRouteResponse;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 897c9af3c45fd8e429f8a851eb1b8aab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09bf59c6e0e10e644aa18a17e8a305e8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,11 @@
|
||||
#if TENGINE_NET
|
||||
namespace TEngine.Core.Network;
|
||||
|
||||
public sealed class I_AddressableAddHandler : RouteRPC<Scene, I_AddressableAdd_Request, I_AddressableAdd_Response>
|
||||
{
|
||||
protected override async FTask Run(Scene scene, I_AddressableAdd_Request request, I_AddressableAdd_Response response, Action reply)
|
||||
{
|
||||
await scene.GetComponent<AddressableManageComponent>().Add(request.AddressableId, request.RouteId);
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 974e720fa811f47418b83d5b3ffdd41c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,11 @@
|
||||
#if TENGINE_NET
|
||||
namespace TEngine.Core.Network;
|
||||
|
||||
public sealed class I_AddressableGetHandler : RouteRPC<Scene, I_AddressableGet_Request, I_AddressableGet_Response>
|
||||
{
|
||||
protected override async FTask Run(Scene scene, I_AddressableGet_Request request, I_AddressableGet_Response response, Action reply)
|
||||
{
|
||||
response.RouteId = await scene.GetComponent<AddressableManageComponent>().Get(request.AddressableId);
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24a968aef62bae54f9fef9bc39b94959
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,11 @@
|
||||
#if TENGINE_NET
|
||||
namespace TEngine.Core.Network;
|
||||
|
||||
public sealed class I_AddressableLockHandler : RouteRPC<Scene, I_AddressableLock_Request, I_AddressableLock_Response>
|
||||
{
|
||||
protected override async FTask Run(Scene scene, I_AddressableLock_Request request, I_AddressableLock_Response response, Action reply)
|
||||
{
|
||||
await scene.GetComponent<AddressableManageComponent>().Lock(request.AddressableId);
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95e84c7e22ea5f047a3c524d8472e967
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,11 @@
|
||||
#if TENGINE_NET
|
||||
namespace TEngine.Core.Network;
|
||||
|
||||
public sealed class I_AddressableRemoveHandler : RouteRPC<Scene, I_AddressableRemove_Request, I_AddressableRemove_Response>
|
||||
{
|
||||
protected override async FTask Run(Scene scene, I_AddressableRemove_Request request, I_AddressableRemove_Response response, Action reply)
|
||||
{
|
||||
await scene.GetComponent<AddressableManageComponent>().Remove(request.AddressableId);
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 660cdf9f50994c94182e3a64c569da85
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,12 @@
|
||||
#if TENGINE_NET
|
||||
namespace TEngine.Core.Network;
|
||||
|
||||
public sealed class I_AddressableUnLockHandler : RouteRPC<Scene, I_AddressableUnLock_Request, I_AddressableUnLock_Response>
|
||||
{
|
||||
protected override async FTask Run(Scene scene, I_AddressableUnLock_Request request, I_AddressableUnLock_Response response, Action reply)
|
||||
{
|
||||
scene.GetComponent<AddressableManageComponent>().UnLock(request.AddressableId, request.RouteId, request.Source);
|
||||
await FTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4b465b4daeb9d7498cf33409ee72662
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 77c1999fe41f3b743a8df80748ece7e4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,265 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using TEngine.DataStructure;
|
||||
using TEngine.Core;
|
||||
using Type = System.Type;
|
||||
#pragma warning disable CS8602
|
||||
#pragma warning disable CS8600
|
||||
#pragma warning disable CS8618
|
||||
|
||||
// ReSharper disable PossibleNullReferenceException
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public sealed class HandlerInfo<T>
|
||||
{
|
||||
public T Obj;
|
||||
public Type Type;
|
||||
}
|
||||
|
||||
public sealed class MessageDispatcherSystem : Singleton<MessageDispatcherSystem>
|
||||
{
|
||||
private readonly Dictionary<Type, Type> _responseTypes = new Dictionary<Type, Type>();
|
||||
private readonly DoubleMapDictionary<uint, Type> _networkProtocols = new DoubleMapDictionary<uint, Type>();
|
||||
private readonly Dictionary<Type, IMessageHandler> _messageHandlers = new Dictionary<Type, IMessageHandler>();
|
||||
|
||||
private readonly OneToManyList<int, Type> _assemblyResponseTypes = new OneToManyList<int, Type>();
|
||||
private readonly OneToManyList<int, uint> _assemblyNetworkProtocols = new OneToManyList<int, uint>();
|
||||
private readonly OneToManyList<int, HandlerInfo<IMessageHandler>> _assemblyMessageHandlers = new OneToManyList<int, HandlerInfo<IMessageHandler>>();
|
||||
|
||||
#if TENGINE_NET
|
||||
private readonly Dictionary<Type, IRouteMessageHandler> _routeMessageHandlers = new Dictionary<Type, IRouteMessageHandler>();
|
||||
private readonly OneToManyList<int, HandlerInfo<IRouteMessageHandler>> _assemblyRouteMessageHandlers= new OneToManyList<int, HandlerInfo<IRouteMessageHandler>>();
|
||||
#endif
|
||||
private static readonly CoroutineLockQueueType ReceiveRouteMessageLock = new CoroutineLockQueueType("ReceiveRouteMessageLock");
|
||||
|
||||
protected override void OnLoad(int assemblyName)
|
||||
{
|
||||
foreach (var type in AssemblyManager.ForEach(assemblyName, typeof(IMessage)))
|
||||
{
|
||||
var obj = (IMessage) Activator.CreateInstance(type);
|
||||
var opCode = obj.OpCode();
|
||||
|
||||
_networkProtocols.Add(opCode, type);
|
||||
|
||||
var responseType = type.GetProperty("ResponseType");
|
||||
|
||||
if (responseType != null)
|
||||
{
|
||||
_responseTypes.Add(type, responseType.PropertyType);
|
||||
_assemblyResponseTypes.Add(assemblyName, type);
|
||||
}
|
||||
|
||||
_assemblyNetworkProtocols.Add(assemblyName, opCode);
|
||||
}
|
||||
|
||||
foreach (var type in AssemblyManager.ForEach(assemblyName, typeof(IMessageHandler)))
|
||||
{
|
||||
var obj = (IMessageHandler) Activator.CreateInstance(type);
|
||||
|
||||
if (obj == null)
|
||||
{
|
||||
throw new Exception($"message handle {type.Name} is null");
|
||||
}
|
||||
|
||||
var key = obj.Type();
|
||||
_messageHandlers.Add(key, obj);
|
||||
_assemblyMessageHandlers.Add(assemblyName, new HandlerInfo<IMessageHandler>()
|
||||
{
|
||||
Obj = obj, Type = key
|
||||
});
|
||||
}
|
||||
#if TENGINE_NET
|
||||
foreach (var type in AssemblyManager.ForEach(assemblyName, typeof(IRouteMessageHandler)))
|
||||
{
|
||||
var obj = (IRouteMessageHandler) Activator.CreateInstance(type);
|
||||
|
||||
if (obj == null)
|
||||
{
|
||||
throw new Exception($"message handle {type.Name} is null");
|
||||
}
|
||||
|
||||
var key = obj.Type();
|
||||
_routeMessageHandlers.Add(key, obj);
|
||||
_assemblyRouteMessageHandlers.Add(assemblyName, new HandlerInfo<IRouteMessageHandler>()
|
||||
{
|
||||
Obj = obj, Type = key
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void OnUnLoad(int assemblyName)
|
||||
{
|
||||
if (_assemblyResponseTypes.TryGetValue(assemblyName, out var removeResponseTypes))
|
||||
{
|
||||
foreach (var removeResponseType in removeResponseTypes)
|
||||
{
|
||||
_responseTypes.Remove(removeResponseType);
|
||||
}
|
||||
|
||||
_assemblyResponseTypes.RemoveByKey(assemblyName);
|
||||
}
|
||||
|
||||
if (_assemblyNetworkProtocols.TryGetValue(assemblyName, out var removeNetworkProtocols))
|
||||
{
|
||||
foreach (var removeNetworkProtocol in removeNetworkProtocols)
|
||||
{
|
||||
_networkProtocols.RemoveByKey(removeNetworkProtocol);
|
||||
}
|
||||
|
||||
_assemblyNetworkProtocols.RemoveByKey(assemblyName);
|
||||
}
|
||||
|
||||
if (_assemblyMessageHandlers.TryGetValue(assemblyName, out var removeMessageHandlers))
|
||||
{
|
||||
foreach (var removeMessageHandler in removeMessageHandlers)
|
||||
{
|
||||
_messageHandlers.Remove(removeMessageHandler.Type);
|
||||
}
|
||||
|
||||
_assemblyMessageHandlers.Remove(assemblyName);
|
||||
}
|
||||
#if TENGINE_NET
|
||||
if (_assemblyRouteMessageHandlers.TryGetValue(assemblyName, out var removeRouteMessageHandlers))
|
||||
{
|
||||
foreach (var removeRouteMessageHandler in removeRouteMessageHandlers)
|
||||
{
|
||||
_routeMessageHandlers.Remove(removeRouteMessageHandler.Type);
|
||||
}
|
||||
|
||||
_assemblyRouteMessageHandlers.Remove(assemblyName);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public void MessageHandler(Session session, Type type, object message, uint rpcId, uint protocolCode)
|
||||
{
|
||||
if (!_messageHandlers.TryGetValue(type, out var messageHandler))
|
||||
{
|
||||
Log.Warning($"Scene:{session.Scene.Id} Found Unhandled Message: {message.GetType()}");
|
||||
return;
|
||||
}
|
||||
|
||||
messageHandler.Handle(session, rpcId, protocolCode, message).Coroutine();
|
||||
}
|
||||
#if TENGINE_NET
|
||||
public async FTask RouteMessageHandler(Session session, Type type, Entity entity, object message, uint rpcId)
|
||||
{
|
||||
if (!_routeMessageHandlers.TryGetValue(type, out var routeMessageHandler))
|
||||
{
|
||||
Log.Warning($"Scene:{session.Scene.Id} Found Unhandled RouteMessage: {message.GetType()}");
|
||||
|
||||
if (message is IRouteRequest request)
|
||||
{
|
||||
FailResponse(session, request, CoreErrorCode.Error_NotFindEntity, rpcId);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var runtimeId = entity.RuntimeId;
|
||||
var sessionRuntimeId = session.RuntimeId;
|
||||
|
||||
if (entity is Scene)
|
||||
{
|
||||
// 如果是Scene的话、就不要加锁了、如果加锁很一不小心就可能会造成死锁
|
||||
await routeMessageHandler.Handle(session, entity, rpcId, message);
|
||||
return;
|
||||
}
|
||||
|
||||
using (await ReceiveRouteMessageLock.Lock(runtimeId))
|
||||
{
|
||||
if (sessionRuntimeId != session.RuntimeId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (runtimeId != entity.RuntimeId)
|
||||
{
|
||||
if (message is IRouteRequest request)
|
||||
{
|
||||
FailResponse(session, request, CoreErrorCode.Error_NotFindEntity, rpcId);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await routeMessageHandler.Handle(session, entity, rpcId, message);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
public void FailResponse(Session session, IRouteRequest iRouteRequest, int error, uint rpcId)
|
||||
{
|
||||
var response = CreateResponse(iRouteRequest, error);
|
||||
session.Send(response, rpcId);
|
||||
}
|
||||
|
||||
public IRouteResponse CreateRouteResponse()
|
||||
{
|
||||
return new RouteResponse();
|
||||
}
|
||||
|
||||
public IResponse CreateResponse(Type requestType, int error)
|
||||
{
|
||||
IResponse response;
|
||||
|
||||
if (_responseTypes.TryGetValue(requestType, out var responseType))
|
||||
{
|
||||
response = (IResponse) Activator.CreateInstance(responseType);
|
||||
}
|
||||
else
|
||||
{
|
||||
response = new Response();
|
||||
}
|
||||
|
||||
response.ErrorCode = error;
|
||||
return response;
|
||||
}
|
||||
|
||||
public IResponse CreateResponse(IRequest iRequest, int error)
|
||||
{
|
||||
IResponse response;
|
||||
|
||||
if (_responseTypes.TryGetValue(iRequest.GetType(), out var responseType))
|
||||
{
|
||||
response = (IResponse) Activator.CreateInstance(responseType);
|
||||
}
|
||||
else
|
||||
{
|
||||
response = new Response();
|
||||
}
|
||||
|
||||
response.ErrorCode = error;
|
||||
return response;
|
||||
}
|
||||
|
||||
public IRouteResponse CreateResponse(IRouteRequest iRouteRequest, int error)
|
||||
{
|
||||
IRouteResponse response;
|
||||
|
||||
if (_responseTypes.TryGetValue(iRouteRequest.GetType(), out var responseType))
|
||||
{
|
||||
response = (IRouteResponse) Activator.CreateInstance(responseType);
|
||||
}
|
||||
else
|
||||
{
|
||||
response = new RouteResponse();
|
||||
}
|
||||
|
||||
response.ErrorCode = error;
|
||||
return response;
|
||||
}
|
||||
|
||||
public uint GetOpCode(Type type)
|
||||
{
|
||||
return _networkProtocols.GetKeyByValue(type);
|
||||
}
|
||||
|
||||
public Type GetOpCodeType(uint code)
|
||||
{
|
||||
return _networkProtocols.GetValueByKey(code);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6c1581e1f459cc4295685cafaa63dd4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,50 @@
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public static class Opcode
|
||||
{
|
||||
// 外网消息
|
||||
public const uint OuterMessage = 100000000;
|
||||
public const uint OuterRequest = 110000000;
|
||||
// 内网消息
|
||||
public const uint InnerMessage = 120000000;
|
||||
public const uint InnerRequest = 130000000;
|
||||
// 内网Bson消息
|
||||
public const uint InnerBsonMessage = 140000000;
|
||||
public const uint InnerBsonRequest = 150000000;
|
||||
// 回复消息
|
||||
public const uint OuterResponse = 160000000;
|
||||
public const uint InnerResponse = 170000000;
|
||||
public const uint InnerBsonResponse = 180000000;
|
||||
// 外网路由消息
|
||||
public const uint OuterRouteMessage = 190000000;
|
||||
public const uint OuterRouteRequest = 200000000;
|
||||
// 内网路由消息
|
||||
public const uint InnerRouteMessage = 210000000;
|
||||
public const uint InnerRouteRequest = 220000000;
|
||||
// 内网Bson路由消息
|
||||
public const uint InnerBsonRouteMessage = 230000000;
|
||||
public const uint InnerBsonRouteRequest = 240000000;
|
||||
// 路由回复消息
|
||||
public const uint OuterRouteResponse = 250000000;
|
||||
public const uint InnerRouteResponse = 260000000;
|
||||
public const uint InnerBsonRouteResponse = 270000000;
|
||||
// 心跳消息
|
||||
public const uint PingRequest = 1;
|
||||
public const uint PingResponse = 2;
|
||||
// 默认回复消息
|
||||
public const uint DefaultResponse = 3;
|
||||
// Addressable可寻址消息
|
||||
public const uint AddressableAddRequest = InnerRouteRequest + 1;
|
||||
public const uint AddressableAddResponse = InnerRouteResponse + 1;
|
||||
public const uint AddressableGetRequest = InnerRouteRequest + 2;
|
||||
public const uint AddressableGetResponse = InnerRouteResponse + 2;
|
||||
public const uint AddressableRemoveRequest = InnerRouteRequest + 3;
|
||||
public const uint AddressableRemoveResponse = InnerRouteResponse + 3;
|
||||
public const uint AddressableLockRequest = InnerRouteRequest + 4;
|
||||
public const uint AddressableLockResponse = InnerRouteResponse + 4;
|
||||
public const uint AddressableUnLockRequest = InnerRouteRequest + 5;
|
||||
public const uint AddressableUnLockResponse = InnerRouteResponse + 5;
|
||||
// 默认的Route返回消息
|
||||
public const uint DefaultRouteResponse = InnerRouteResponse + 6;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b40c482be660ad4090888728af062ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,6 @@
|
||||
using ProtoBuf;
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8516b2b12ce77fb488b1b973adc680f0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e639a64f385d7164799d03a031c3fabb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db5ee92b41263e2478cfb3dbb35e0ccf
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,87 @@
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
using System;
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public interface IMessageHandler
|
||||
{
|
||||
public Type Type();
|
||||
FTask Handle(Session session, uint rpcId, uint messageTypeCode, object message);
|
||||
}
|
||||
|
||||
public abstract class Message<T> : IMessageHandler
|
||||
{
|
||||
public Type Type()
|
||||
{
|
||||
return typeof(T);
|
||||
}
|
||||
|
||||
public async FTask Handle(Session session, uint rpcId, uint messageTypeCode, object message)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Run(session, (T) message);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract FTask Run(Session session, T message);
|
||||
}
|
||||
|
||||
public abstract class MessageRPC<TRequest, TResponse> : IMessageHandler where TRequest : IRequest where TResponse : IResponse
|
||||
{
|
||||
public Type Type()
|
||||
{
|
||||
return typeof(TRequest);
|
||||
}
|
||||
|
||||
public async FTask Handle(Session session, uint rpcId, uint messageTypeCode, object message)
|
||||
{
|
||||
if (message is not TRequest request)
|
||||
{
|
||||
Log.Error($"消息类型转换错误: {message.GetType().Name} to {typeof(TRequest).Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
var response = Activator.CreateInstance<TResponse>();
|
||||
var isReply = false;
|
||||
|
||||
void Reply()
|
||||
{
|
||||
if (isReply)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
isReply = true;
|
||||
|
||||
if (session.IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
session.Send(response, rpcId);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Run(session, request, response, Reply);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
response.ErrorCode = CoreErrorCode.ErrRpcFail;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Reply();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract FTask Run(Session session, TRequest request, TResponse response, Action reply);
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0e1cb825e8847a479ac69acee8f0bb6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,222 @@
|
||||
#if TENGINE_NET
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public interface IRouteMessageHandler
|
||||
{
|
||||
public Type Type();
|
||||
FTask Handle(Session session, Entity entity, uint rpcId, object routeMessage);
|
||||
}
|
||||
|
||||
public abstract class Route<TEntity, TMessage> : IRouteMessageHandler where TEntity : Entity where TMessage : IRouteMessage
|
||||
{
|
||||
public Type Type()
|
||||
{
|
||||
return typeof(TMessage);
|
||||
}
|
||||
|
||||
public async FTask Handle(Session session, Entity entity, uint rpcId, object routeMessage)
|
||||
{
|
||||
if (routeMessage is not TMessage ruteMessage)
|
||||
{
|
||||
Log.Error($"Message type conversion error: {routeMessage.GetType().FullName} to {typeof(TMessage).Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity is not TEntity tEntity)
|
||||
{
|
||||
Log.Error($"Route type conversion error: {entity.GetType().Name} to {nameof(TEntity)}");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Run(tEntity, ruteMessage);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (entity is not Scene scene)
|
||||
{
|
||||
scene = entity.Scene;
|
||||
}
|
||||
|
||||
Log.Error($"SceneWorld:{session.Scene.World.Id} SceneRouteId:{scene.RouteId} SceneType:{scene.SceneInfo.SceneType} EntityId {tEntity.Id} : Error {e}");
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract FTask Run(TEntity entity, TMessage message);
|
||||
}
|
||||
|
||||
public abstract class RouteRPC<TEntity, TRouteRequest, TRouteResponse> : IRouteMessageHandler where TEntity : Entity where TRouteRequest : IRouteRequest where TRouteResponse : IRouteResponse
|
||||
{
|
||||
public Type Type()
|
||||
{
|
||||
return typeof(TRouteRequest);
|
||||
}
|
||||
|
||||
public async FTask Handle(Session session, Entity entity, uint rpcId, object routeMessage)
|
||||
{
|
||||
if (routeMessage is not TRouteRequest tRouteRequest)
|
||||
{
|
||||
Log.Error($"Message type conversion error: {routeMessage.GetType().FullName} to {typeof(TRouteRequest).Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity is not TEntity tEntity)
|
||||
{
|
||||
Log.Error($"Route type conversion error: {entity.GetType().Name} to {nameof(TEntity)}");
|
||||
return;
|
||||
}
|
||||
|
||||
var isReply = false;
|
||||
var response = Activator.CreateInstance<TRouteResponse>();
|
||||
|
||||
void Reply()
|
||||
{
|
||||
if (isReply)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
isReply = true;
|
||||
|
||||
if (session.IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
session.Send(response, rpcId);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Run(tEntity, tRouteRequest, response, Reply);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (entity is not Scene scene)
|
||||
{
|
||||
scene = entity.Scene;
|
||||
}
|
||||
|
||||
Log.Error($"SceneWorld:{session.Scene.World.Id} SceneRouteId:{scene.RouteId} SceneType:{scene.SceneInfo.SceneType} EntityId {tEntity.Id} : Error {e}");
|
||||
response.ErrorCode = CoreErrorCode.ErrRpcFail;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Reply();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract FTask Run(TEntity entity, TRouteRequest request, TRouteResponse response, Action reply);
|
||||
}
|
||||
|
||||
public abstract class Addressable<TEntity, TMessage> : IRouteMessageHandler where TEntity : Entity where TMessage : IAddressableRouteMessage
|
||||
{
|
||||
public Type Type()
|
||||
{
|
||||
return typeof(TMessage);
|
||||
}
|
||||
|
||||
public async FTask Handle(Session session, Entity entity, uint rpcId, object routeMessage)
|
||||
{
|
||||
if (routeMessage is not TMessage ruteMessage)
|
||||
{
|
||||
Log.Error($"Message type conversion error: {routeMessage.GetType().FullName} to {typeof(TMessage).Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity is not TEntity tEntity)
|
||||
{
|
||||
Log.Error($"Route type conversion error: {entity.GetType().Name} to {nameof(TEntity)}");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Run(tEntity, ruteMessage);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (entity is not Scene scene)
|
||||
{
|
||||
scene = entity.Scene;
|
||||
}
|
||||
|
||||
Log.Error($"SceneWorld:{session.Scene.World.Id} SceneRouteId:{scene.RouteId} SceneType:{scene.SceneInfo.SceneType} EntityId {tEntity.Id} : Error {e}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Send(MessageDispatcherSystem.Instance.CreateRouteResponse(), rpcId);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract FTask Run(TEntity entity, TMessage message);
|
||||
}
|
||||
|
||||
public abstract class AddressableRPC<TEntity, TRouteRequest, TRouteResponse> : IRouteMessageHandler where TEntity : Entity where TRouteRequest : IAddressableRouteRequest where TRouteResponse : IAddressableRouteResponse
|
||||
{
|
||||
public Type Type()
|
||||
{
|
||||
return typeof(TRouteRequest);
|
||||
}
|
||||
|
||||
public async FTask Handle(Session session, Entity entity, uint rpcId, object routeMessage)
|
||||
{
|
||||
if (routeMessage is not TRouteRequest tRouteRequest)
|
||||
{
|
||||
Log.Error($"Message type conversion error: {routeMessage.GetType().FullName} to {typeof(TRouteRequest).Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity is not TEntity tEntity)
|
||||
{
|
||||
Log.Error($"Route type conversion error: {entity.GetType().Name} to {nameof(TEntity)}");
|
||||
return;
|
||||
}
|
||||
|
||||
var isReply = false;
|
||||
var response = Activator.CreateInstance<TRouteResponse>();
|
||||
|
||||
void Reply()
|
||||
{
|
||||
if (isReply)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
isReply = true;
|
||||
|
||||
if (session.IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
session.Send(response, rpcId);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Run(tEntity, tRouteRequest, response, Reply);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (entity is not Scene scene)
|
||||
{
|
||||
scene = entity.Scene;
|
||||
}
|
||||
|
||||
Log.Error($"SceneWorld:{session.Scene.World.Id} SceneRouteId:{scene.RouteId} SceneType:{scene.SceneInfo.SceneType} EntityId {tEntity.Id} : Error {e}");
|
||||
response.ErrorCode = CoreErrorCode.ErrRpcFail;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Reply();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract FTask Run(TEntity entity, TRouteRequest request, TRouteResponse response, Action reply);
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6de532c5ce565747817a22793fb85c0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4767a374666197f4ab971d74378a5040
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,17 @@
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public interface IBsonMessage : IMessage
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public interface IBsonRequest : IBsonMessage, IRequest
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public interface IBsonResponse : IBsonMessage, IResponse
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb1d2907b6893e643b0f5c4644b412d1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,17 @@
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public interface IMessage
|
||||
{
|
||||
uint OpCode();
|
||||
}
|
||||
|
||||
public interface IRequest : IMessage
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public interface IResponse : IMessage
|
||||
{
|
||||
int ErrorCode { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0828643e6b72684fb0e4917cef34b97
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,26 @@
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
// 普通路由消息
|
||||
public interface IRouteMessage : IRequest
|
||||
{
|
||||
long RouteTypeOpCode();
|
||||
}
|
||||
public interface IRouteRequest : IRouteMessage { }
|
||||
public interface IRouteResponse : IResponse { }
|
||||
// 普通路由Bson消息
|
||||
public interface IBsonRouteMessage : IBsonMessage, IRouteMessage { }
|
||||
public interface IBsonRouteRequest : IBsonRouteMessage, IRouteRequest { }
|
||||
public interface IBsonRouteResponse : IBsonResponse, IRouteResponse { }
|
||||
// 可寻址协议
|
||||
public interface IAddressableRouteMessage : IRouteMessage { }
|
||||
public interface IAddressableRouteRequest : IRouteRequest { }
|
||||
public interface IAddressableRouteResponse : IRouteResponse { }
|
||||
// 可寻址Bson协议
|
||||
public interface IBsonAddressableRouteMessage : IBsonMessage, IAddressableRouteMessage { }
|
||||
public interface IBsonAddressableRouteRequest : IBsonRouteMessage, IAddressableRouteRequest { }
|
||||
public interface IBsonAddressableRouteResponse : IBsonResponse, IAddressableRouteResponse { }
|
||||
// 自定义Route协议
|
||||
public interface ICustomRouteMessage : IRouteMessage { }
|
||||
public interface ICustomRouteRequest : IRouteRequest { }
|
||||
public interface ICustomRouteResponse : IRouteResponse { }
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d836956c7e220f4d945807e0fd90c1c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8ff64952cfa44645a9410738b63d26b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using TEngine.Core;
|
||||
#pragma warning disable CS8600
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public abstract class ANetworkMessageScheduler
|
||||
{
|
||||
private readonly PingResponse _pingResponse = new PingResponse();
|
||||
|
||||
public async FTask Scheduler(Session session, APackInfo packInfo)
|
||||
{
|
||||
Type messageType = null;
|
||||
var packInfoMemoryStream = packInfo.MemoryStream;
|
||||
|
||||
try
|
||||
{
|
||||
if (session.IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (packInfo.ProtocolCode == Opcode.PingRequest)
|
||||
{
|
||||
_pingResponse.Now = TimeHelper.Now;
|
||||
session.Send(_pingResponse, packInfo.RpcId);
|
||||
return;
|
||||
}
|
||||
|
||||
messageType = MessageDispatcherSystem.Instance.GetOpCodeType(packInfo.ProtocolCode);
|
||||
|
||||
if (messageType == null)
|
||||
{
|
||||
throw new Exception($"可能遭受到恶意发包或没有协议定义ProtocolCode ProtocolCode:{packInfo.ProtocolCode}");
|
||||
}
|
||||
|
||||
switch (packInfo.ProtocolCode)
|
||||
{
|
||||
case >= Opcode.OuterRouteMessage:
|
||||
{
|
||||
await Handler(session, messageType, packInfo);
|
||||
return;
|
||||
}
|
||||
case < Opcode.OuterResponse:
|
||||
{
|
||||
var message = packInfo.Deserialize(messageType);
|
||||
MessageDispatcherSystem.Instance.MessageHandler(session, messageType, message, packInfo.RpcId, packInfo.ProtocolCode);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
var aResponse = (IResponse)packInfo.Deserialize(messageType);
|
||||
#if TENGINE_NET
|
||||
// 服务器之间发送消息因为走的是MessageHelper、所以接收消息的回调也应该放到MessageHelper里处理
|
||||
MessageHelper.ResponseHandler(packInfo.RpcId, aResponse);
|
||||
#else
|
||||
// 这个一般是客户端Session.Call发送时使用的、目前这个逻辑只有Unity客户端时使用
|
||||
|
||||
if (!session.RequestCallback.TryGetValue(packInfo.RpcId, out var action))
|
||||
{
|
||||
Log.Error($"not found rpc {packInfo.RpcId}, response message: {aResponse.GetType().Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
session.RequestCallback.Remove(packInfo.RpcId);
|
||||
action.SetResult(aResponse);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (packInfoMemoryStream.CanRead)
|
||||
{
|
||||
// ReSharper disable once MethodHasAsyncOverload
|
||||
packInfoMemoryStream.Dispose();
|
||||
}
|
||||
|
||||
Log.Error($"NetworkMessageScheduler error messageProtocolCode:{packInfo.ProtocolCode} messageType:{messageType} SessionId {session.Id} IsDispose {session.IsDisposed} {e}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
packInfo.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract FTask Handler(Session session, Type messageType, APackInfo packInfo);
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b71e62d5efece8a408fc90048164af53
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,21 @@
|
||||
// using System;
|
||||
// using System.IO;
|
||||
//
|
||||
// namespace TEngine.Core.Network
|
||||
// {
|
||||
// public enum NetworkMessageSchedulerHandlerType
|
||||
// {
|
||||
// None = 0,
|
||||
// ClientMessage = 1,
|
||||
// OuterMessageRoute = 2,
|
||||
// InnerMessage = 3,
|
||||
// ServerInnerMessage = 4
|
||||
// }
|
||||
//
|
||||
// public interface INetworkMessageSchedulerHandler
|
||||
// {
|
||||
// NetworkMessageSchedulerHandlerType HandlerType();
|
||||
//
|
||||
// FTask Handler(Session session, Type messageType, APackInfo packInfo);
|
||||
// }
|
||||
// }
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5653343ec7befa48a924bed253321ba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
196
Assets/GameScripts/DotNet/Core/Network/Message/MessageHelper.cs
Normal file
196
Assets/GameScripts/DotNet/Core/Network/Message/MessageHelper.cs
Normal file
@@ -0,0 +1,196 @@
|
||||
using TEngine.Core;
|
||||
#pragma warning disable CS8603
|
||||
|
||||
#if TENGINE_NET
|
||||
namespace TEngine.Core.Network;
|
||||
|
||||
public static class MessageHelper
|
||||
{
|
||||
private static uint _rpcId;
|
||||
public const long Timeout = 40000;
|
||||
public static readonly SortedDictionary<uint, MessageSender> RequestCallback = new();
|
||||
public static readonly Dictionary<uint, MessageSender> TimeoutRouteMessageSenders = new();
|
||||
|
||||
/// <summary>
|
||||
/// 定时检查过期的Call消息事件。
|
||||
/// </summary>
|
||||
public struct NetworkMessageUpdate { }
|
||||
|
||||
static MessageHelper()
|
||||
{
|
||||
TimerScheduler.Instance.Core.RepeatedTimer(10000, new NetworkMessageUpdate());
|
||||
}
|
||||
|
||||
public static void SendInnerServer(Scene scene, uint routeId, IMessage message)
|
||||
{
|
||||
scene.Server.GetSession(routeId).Send(message);
|
||||
}
|
||||
|
||||
public static void SendInnerRoute(Scene scene, long entityId, IRouteMessage message)
|
||||
{
|
||||
if (entityId == 0)
|
||||
{
|
||||
Log.Error($"SendInnerRoute appId == 0");
|
||||
return;
|
||||
}
|
||||
|
||||
EntityIdStruct entityIdStruct = entityId;
|
||||
var session = scene.Server.GetSession(entityIdStruct.RouteId);
|
||||
session.Send(message, 0, entityId);
|
||||
}
|
||||
|
||||
public static void SendInnerRoute(Scene scene, long entityId, long routeTypeOpCode, MemoryStream message)
|
||||
{
|
||||
if (entityId == 0)
|
||||
{
|
||||
Log.Error($"SendInnerRoute appId == 0");
|
||||
return;
|
||||
}
|
||||
|
||||
EntityIdStruct entityIdStruct = entityId;
|
||||
var session = scene.Server.GetSession(entityIdStruct.RouteId);
|
||||
session.Send(message, 0, routeTypeOpCode, entityId);
|
||||
}
|
||||
|
||||
public static void SendInnerRoute(Scene scene, ICollection<long> routeIdCollection, IRouteMessage message)
|
||||
{
|
||||
if (routeIdCollection.Count <= 0)
|
||||
{
|
||||
Log.Error($"SendInnerRoute routeId.Count <= 0");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var routeId in routeIdCollection)
|
||||
{
|
||||
SendInnerRoute(scene, routeId, message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SendAddressable(Scene scene, long addressableId, IRouteMessage message)
|
||||
{
|
||||
CallAddressable(scene, addressableId, message).Coroutine();
|
||||
}
|
||||
|
||||
public static async FTask<IResponse> CallInnerRoute(Scene scene, long entityId, long routeTypeOpCode, Type requestType, MemoryStream request)
|
||||
{
|
||||
if (entityId == 0)
|
||||
{
|
||||
Log.Error($"CallInnerRoute appId == 0");
|
||||
return null;
|
||||
}
|
||||
|
||||
EntityIdStruct entityIdStruct = entityId;
|
||||
var rpcId = ++_rpcId;
|
||||
var session = scene.Server.GetSession(entityIdStruct.RouteId);
|
||||
var requestCallback = FTask<IResponse>.Create(false);
|
||||
RequestCallback.Add(rpcId, MessageSender.Create(rpcId, requestType, requestCallback));
|
||||
session.Send(request, rpcId, routeTypeOpCode, entityId);
|
||||
return await requestCallback;
|
||||
}
|
||||
|
||||
public static async FTask<IResponse> CallInnerRoute(Scene scene, long entityId, IRouteMessage request)
|
||||
{
|
||||
if (entityId == 0)
|
||||
{
|
||||
Log.Error($"CallInnerRoute appId == 0");
|
||||
return null;
|
||||
}
|
||||
|
||||
EntityIdStruct entityIdStruct = entityId;
|
||||
var rpcId = ++_rpcId;
|
||||
var session = scene.Server.GetSession(entityIdStruct.RouteId);
|
||||
var requestCallback = FTask<IResponse>.Create(false);
|
||||
RequestCallback.Add(rpcId, MessageSender.Create(rpcId, request, requestCallback));
|
||||
session.Send(request, rpcId, entityId);
|
||||
return await requestCallback;
|
||||
}
|
||||
|
||||
public static async FTask<IResponse> CallInnerServer(Scene scene, uint targetServerId, IRequest request)
|
||||
{
|
||||
var rpcId = ++_rpcId;
|
||||
var session = scene.Server.GetSession(targetServerId);
|
||||
var requestCallback = FTask<IResponse>.Create(false);
|
||||
RequestCallback.Add(rpcId, MessageSender.Create(rpcId, request, requestCallback));
|
||||
session.Send(request, rpcId);
|
||||
return await requestCallback;
|
||||
}
|
||||
|
||||
public static async FTask<IResponse> CallAddressable(Scene scene, long addressableId, IRouteMessage request)
|
||||
{
|
||||
var failCount = 0;
|
||||
|
||||
using (await AddressableRouteComponent.AddressableRouteMessageLock.Lock(addressableId,"CallAddressable"))
|
||||
{
|
||||
var addressableRouteId = await AddressableHelper.GetAddressableRouteId(scene, addressableId);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (addressableRouteId == 0)
|
||||
{
|
||||
addressableRouteId = await AddressableHelper.GetAddressableRouteId(scene, addressableId);
|
||||
}
|
||||
|
||||
if (addressableRouteId == 0)
|
||||
{
|
||||
return MessageDispatcherSystem.Instance.CreateResponse(request, CoreErrorCode.ErrNotFoundRoute);
|
||||
}
|
||||
|
||||
var iRouteResponse = await MessageHelper.CallInnerRoute(scene, addressableRouteId, request);
|
||||
|
||||
switch (iRouteResponse.ErrorCode)
|
||||
{
|
||||
case CoreErrorCode.ErrNotFoundRoute:
|
||||
{
|
||||
if (++failCount > 20)
|
||||
{
|
||||
Log.Error($"AddressableComponent.Call failCount > 20 route send message fail, routeId: {addressableRouteId} AddressableMessageComponent:{addressableId}");
|
||||
return iRouteResponse;
|
||||
}
|
||||
|
||||
await TimerScheduler.Instance.Core.WaitAsync(500);
|
||||
addressableRouteId = 0;
|
||||
continue;
|
||||
}
|
||||
case CoreErrorCode.ErrRouteTimeout:
|
||||
{
|
||||
Log.Error($"CallAddressableRoute ErrorCode.ErrRouteTimeout Error:{iRouteResponse.ErrorCode} Message:{request}");
|
||||
return iRouteResponse;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return iRouteResponse;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ResponseHandler(uint rpcId, IResponse response)
|
||||
{
|
||||
if (!RequestCallback.Remove(rpcId, out var routeMessageSender))
|
||||
{
|
||||
throw new Exception($"not found rpc, response.RpcId:{rpcId} response message: {response.GetType().Name}");
|
||||
}
|
||||
|
||||
ResponseHandler(routeMessageSender, response);
|
||||
}
|
||||
|
||||
private static void ResponseHandler(MessageSender messageSender, IResponse response)
|
||||
{
|
||||
if (response.ErrorCode == CoreErrorCode.ErrRouteTimeout)
|
||||
{
|
||||
#if TENGINE_DEVELOP
|
||||
messageSender.Tcs.SetException(new Exception($"Rpc error: request, 注意RouteId消息超时,请注意查看是否死锁或者没有reply: RouteId: {messageSender.RouteId} {messageSender.Request.ToJson()}, response: {response}"));
|
||||
#else
|
||||
messageSender.Tcs.SetException(new Exception($"Rpc error: request, 注意RouteId消息超时,请注意查看是否死锁或者没有reply: RouteId: {messageSender.RouteId} {messageSender.Request}, response: {response}"));
|
||||
#endif
|
||||
messageSender.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
messageSender.Tcs.SetResult(response);
|
||||
messageSender.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2dce1a84fcf96e4a8dfbe49a155660a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using TEngine.Core;
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8618
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public sealed class MessageSender : IDisposable
|
||||
{
|
||||
public uint RpcId { get; private set; }
|
||||
public long RouteId { get; private set; }
|
||||
public long CreateTime { get; private set; }
|
||||
public Type MessageType { get; private set; }
|
||||
public IMessage Request { get; private set; }
|
||||
public FTask<IResponse> Tcs { get; private set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
RpcId = 0;
|
||||
RouteId = 0;
|
||||
CreateTime = 0;
|
||||
Tcs = null;
|
||||
Request = null;
|
||||
MessageType = null;
|
||||
Pool<MessageSender>.Return(this);
|
||||
}
|
||||
|
||||
public static MessageSender Create(uint rpcId, Type requestType, FTask<IResponse> tcs)
|
||||
{
|
||||
var routeMessageSender = Pool<MessageSender>.Rent();
|
||||
routeMessageSender.Tcs = tcs;
|
||||
routeMessageSender.RpcId = rpcId;
|
||||
routeMessageSender.MessageType = requestType;
|
||||
routeMessageSender.CreateTime = TimeHelper.Now;
|
||||
return routeMessageSender;
|
||||
}
|
||||
|
||||
public static MessageSender Create(uint rpcId, IRequest request, FTask<IResponse> tcs)
|
||||
{
|
||||
var routeMessageSender = Pool<MessageSender>.Rent();
|
||||
routeMessageSender.Tcs = tcs;
|
||||
routeMessageSender.RpcId = rpcId;
|
||||
routeMessageSender.Request = request;
|
||||
routeMessageSender.CreateTime = TimeHelper.Now;
|
||||
return routeMessageSender;
|
||||
}
|
||||
|
||||
public static MessageSender Create(uint rpcId, long routeId, IRouteMessage request, FTask<IResponse> tcs)
|
||||
{
|
||||
var routeMessageSender = Pool<MessageSender>.Rent();
|
||||
routeMessageSender.Tcs = tcs;
|
||||
routeMessageSender.RpcId = rpcId;
|
||||
routeMessageSender.RouteId = routeId;
|
||||
routeMessageSender.Request = request;
|
||||
routeMessageSender.CreateTime = TimeHelper.Now;
|
||||
return routeMessageSender;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39b99a8dcf9c7e946a72b1008bbfe1ae
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,71 @@
|
||||
#if TENGINE_NET
|
||||
using TEngine.Core;
|
||||
|
||||
namespace TEngine.Core.Network;
|
||||
|
||||
public sealed class OnNetworkMessageUpdateCheckTimeout : TimerHandler<MessageHelper.NetworkMessageUpdate>
|
||||
{
|
||||
public override void Handler(MessageHelper.NetworkMessageUpdate self)
|
||||
{
|
||||
var timeNow = TimeHelper.Now;
|
||||
|
||||
foreach (var (rpcId, value) in MessageHelper.RequestCallback)
|
||||
{
|
||||
if (timeNow < value.CreateTime + MessageHelper.Timeout)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
MessageHelper.TimeoutRouteMessageSenders.Add(rpcId, value);
|
||||
}
|
||||
|
||||
if (MessageHelper.TimeoutRouteMessageSenders.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (rpcId, routeMessageSender) in MessageHelper.TimeoutRouteMessageSenders)
|
||||
{
|
||||
uint responseRpcId = 0;
|
||||
|
||||
try
|
||||
{
|
||||
switch (routeMessageSender.Request)
|
||||
{
|
||||
case IRouteMessage iRouteMessage:
|
||||
{
|
||||
// var routeResponse = RouteMessageDispatcher.CreateResponse(iRouteMessage, ErrorCode.ErrRouteTimeout);
|
||||
// responseRpcId = routeResponse.RpcId;
|
||||
// routeResponse.RpcId = routeMessageSender.RpcId;
|
||||
// MessageHelper.ResponseHandler(routeResponse);
|
||||
break;
|
||||
}
|
||||
case IRequest iRequest:
|
||||
{
|
||||
var response = MessageDispatcherSystem.Instance.CreateResponse(iRequest, CoreErrorCode.ErrRpcFail);
|
||||
responseRpcId = routeMessageSender.RpcId;
|
||||
MessageHelper.ResponseHandler(responseRpcId, response);
|
||||
Log.Warning($"timeout rpcId:{rpcId} responseRpcId:{responseRpcId} {iRequest.ToJson()}");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Log.Error(routeMessageSender.Request != null
|
||||
? $"Unsupported protocol type {routeMessageSender.Request.GetType()} rpcId:{rpcId}"
|
||||
: $"Unsupported protocol type:{routeMessageSender.MessageType.FullName} rpcId:{rpcId}");
|
||||
|
||||
MessageHelper.RequestCallback.Remove(rpcId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"responseRpcId:{responseRpcId} routeMessageSender.RpcId:{routeMessageSender.RpcId} {e}");
|
||||
}
|
||||
}
|
||||
|
||||
MessageHelper.TimeoutRouteMessageSenders.Clear();
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3152b1d220a7acb45b874cf286c4a341
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1eb9f0783453cf7439a4f31708fde2d6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,160 @@
|
||||
using TEngine.Core.Network;
|
||||
using ProtoBuf;
|
||||
|
||||
namespace TEngine
|
||||
{
|
||||
[ProtoContract]
|
||||
public sealed class Response : AProto, IResponse
|
||||
{
|
||||
public uint OpCode()
|
||||
{
|
||||
return Opcode.DefaultResponse;
|
||||
}
|
||||
|
||||
[ProtoMember(90)] public long RpcId { get; set; }
|
||||
[ProtoMember(91, IsRequired = true)] public int ErrorCode { get; set; }
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public sealed class RouteResponse : AProto, IRouteResponse
|
||||
{
|
||||
public uint OpCode()
|
||||
{
|
||||
return Opcode.DefaultRouteResponse;
|
||||
}
|
||||
|
||||
[ProtoMember(90)] public long RpcId { get; set; }
|
||||
[ProtoMember(91, IsRequired = true)] public int ErrorCode { get; set; }
|
||||
}
|
||||
[ProtoContract]
|
||||
public class PingRequest : AProto, IRequest
|
||||
{
|
||||
public uint OpCode()
|
||||
{
|
||||
return Opcode.PingRequest;
|
||||
}
|
||||
|
||||
[ProtoIgnore] public PingResponse ResponseType { get; set; }
|
||||
[ProtoMember(90)] public long RpcId { get; set; }
|
||||
}
|
||||
|
||||
public class PingResponse : AProto, IResponse
|
||||
{
|
||||
public uint OpCode()
|
||||
{
|
||||
return Opcode.PingResponse;
|
||||
}
|
||||
|
||||
[ProtoMember(90)] public long RpcId { get; set; }
|
||||
[ProtoMember(91, IsRequired = true)] public int ErrorCode { get; set; }
|
||||
[ProtoMember(1)] public long Now;
|
||||
}
|
||||
/// <summary>
|
||||
/// 添加一个可寻址地址
|
||||
/// </summary>
|
||||
[ProtoContract]
|
||||
public partial class I_AddressableAdd_Request : AProto, IRouteRequest
|
||||
{
|
||||
[ProtoIgnore]
|
||||
public I_AddressableAdd_Response ResponseType { get; set; }
|
||||
public uint OpCode() { return Opcode.AddressableAddRequest; }
|
||||
public long RouteTypeOpCode() { return 1; }
|
||||
[ProtoMember(1)]
|
||||
public long AddressableId { get; set; }
|
||||
[ProtoMember(2)]
|
||||
public long RouteId { get; set; }
|
||||
}
|
||||
[ProtoContract]
|
||||
public partial class I_AddressableAdd_Response : AProto, IRouteResponse
|
||||
{
|
||||
public uint OpCode() { return Opcode.AddressableAddResponse; }
|
||||
[ProtoMember(91, IsRequired = true)]
|
||||
public int ErrorCode { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// 查询一个可寻址
|
||||
/// </summary>
|
||||
[ProtoContract]
|
||||
public partial class I_AddressableGet_Request : AProto, IRouteRequest
|
||||
{
|
||||
[ProtoIgnore]
|
||||
public I_AddressableGet_Response ResponseType { get; set; }
|
||||
public uint OpCode() { return Opcode.AddressableGetRequest; }
|
||||
public long RouteTypeOpCode() { return 1; }
|
||||
[ProtoMember(1)]
|
||||
public long AddressableId { get; set; }
|
||||
}
|
||||
[ProtoContract]
|
||||
public partial class I_AddressableGet_Response : AProto, IRouteResponse
|
||||
{
|
||||
public uint OpCode() { return Opcode.AddressableGetResponse; }
|
||||
[ProtoMember(91, IsRequired = true)]
|
||||
public int ErrorCode { get; set; }
|
||||
[ProtoMember(1)]
|
||||
public long RouteId { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// 删除一个可寻址
|
||||
/// </summary>
|
||||
[ProtoContract]
|
||||
public partial class I_AddressableRemove_Request : AProto, IRouteRequest
|
||||
{
|
||||
[ProtoIgnore]
|
||||
public I_AddressableRemove_Response ResponseType { get; set; }
|
||||
public uint OpCode() { return Opcode.AddressableRemoveRequest; }
|
||||
public long RouteTypeOpCode() { return 1; }
|
||||
[ProtoMember(1)]
|
||||
public long AddressableId { get; set; }
|
||||
}
|
||||
[ProtoContract]
|
||||
public partial class I_AddressableRemove_Response : AProto, IRouteResponse
|
||||
{
|
||||
public uint OpCode() { return Opcode.AddressableRemoveResponse; }
|
||||
[ProtoMember(91, IsRequired = true)]
|
||||
public int ErrorCode { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// 锁定一个可寻址
|
||||
/// </summary>
|
||||
[ProtoContract]
|
||||
public partial class I_AddressableLock_Request : AProto, IRouteRequest
|
||||
{
|
||||
[ProtoIgnore]
|
||||
public I_AddressableLock_Response ResponseType { get; set; }
|
||||
public uint OpCode() { return Opcode.AddressableLockRequest; }
|
||||
public long RouteTypeOpCode() { return 1; }
|
||||
[ProtoMember(1)]
|
||||
public long AddressableId { get; set; }
|
||||
}
|
||||
[ProtoContract]
|
||||
public partial class I_AddressableLock_Response : AProto, IRouteResponse
|
||||
{
|
||||
public uint OpCode() { return Opcode.AddressableLockResponse; }
|
||||
[ProtoMember(91, IsRequired = true)]
|
||||
public int ErrorCode { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// 解锁一个可寻址
|
||||
/// </summary>
|
||||
[ProtoContract]
|
||||
public partial class I_AddressableUnLock_Request : AProto, IRouteRequest
|
||||
{
|
||||
[ProtoIgnore]
|
||||
public I_AddressableUnLock_Response ResponseType { get; set; }
|
||||
public uint OpCode() { return Opcode.AddressableUnLockRequest; }
|
||||
public long RouteTypeOpCode() { return 1; }
|
||||
[ProtoMember(1)]
|
||||
public long AddressableId { get; set; }
|
||||
[ProtoMember(2)]
|
||||
public long RouteId { get; set; }
|
||||
[ProtoMember(3)]
|
||||
public string Source { get; set; }
|
||||
}
|
||||
[ProtoContract]
|
||||
public partial class I_AddressableUnLock_Response : AProto, IRouteResponse
|
||||
{
|
||||
public uint OpCode() { return Opcode.AddressableUnLockResponse; }
|
||||
[ProtoMember(91, IsRequired = true)]
|
||||
public int ErrorCode { get; set; }
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user