mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-14 16:51:28 +00:00
[+] UpdateNetwork Module
[+] UpdateNetwork Module
This commit is contained in:
@@ -0,0 +1,166 @@
|
|||||||
|
using TEngine;
|
||||||
|
|
||||||
|
namespace GameLogic
|
||||||
|
{
|
||||||
|
public enum ClientConnectWatcherStatus
|
||||||
|
{
|
||||||
|
StatusInit,
|
||||||
|
StatusReconnectAuto,
|
||||||
|
StatusReconnectConfirm,
|
||||||
|
StatusWaitExit
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ClientConnectWatcher
|
||||||
|
{
|
||||||
|
private readonly GameClient _client;
|
||||||
|
private ClientConnectWatcherStatus _status;
|
||||||
|
private float _statusTime;
|
||||||
|
private int _reconnectCnt = 0;
|
||||||
|
private int _disconnectReason = 0;
|
||||||
|
|
||||||
|
private bool _enable = false;
|
||||||
|
|
||||||
|
public bool Enable
|
||||||
|
{
|
||||||
|
get => _enable;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_enable != value)
|
||||||
|
{
|
||||||
|
_enable = value;
|
||||||
|
if (_enable)
|
||||||
|
{
|
||||||
|
OnEnable();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnDisable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientConnectWatcherStatus Status
|
||||||
|
{
|
||||||
|
get => _status;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_status == value) return;
|
||||||
|
_status = value;
|
||||||
|
_statusTime = NowTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float NowTime => GameTime.unscaledTime;
|
||||||
|
|
||||||
|
public ClientConnectWatcher(GameClient client)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
_statusTime = NowTime;
|
||||||
|
_status = ClientConnectWatcherStatus.StatusInit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
if (!_enable)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_client.IsEntered)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (_status)
|
||||||
|
{
|
||||||
|
case ClientConnectWatcherStatus.StatusInit:
|
||||||
|
UpdateOnInitStatus();
|
||||||
|
break;
|
||||||
|
case ClientConnectWatcherStatus.StatusReconnectAuto:
|
||||||
|
UpdateOnReconnectAuto();
|
||||||
|
break;
|
||||||
|
case ClientConnectWatcherStatus.StatusReconnectConfirm:
|
||||||
|
UpdateOnReconnectConfirm();
|
||||||
|
break;
|
||||||
|
case ClientConnectWatcherStatus.StatusWaitExit:
|
||||||
|
UpdateOnWaitExit();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnReConnect()
|
||||||
|
{
|
||||||
|
if (_status == ClientConnectWatcherStatus.StatusReconnectConfirm)
|
||||||
|
{
|
||||||
|
Status = ClientConnectWatcherStatus.StatusReconnectAuto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateOnInitStatus()
|
||||||
|
{
|
||||||
|
int autoReconnectMaxCount = 4;
|
||||||
|
if (_reconnectCnt <= autoReconnectMaxCount)
|
||||||
|
{
|
||||||
|
if (_reconnectCnt == 0)
|
||||||
|
{
|
||||||
|
_disconnectReason = _client.LastNetErrCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status = ClientConnectWatcherStatus.StatusReconnectAuto;
|
||||||
|
_reconnectCnt++;
|
||||||
|
|
||||||
|
//重连
|
||||||
|
_client.Reconnect();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Status = ClientConnectWatcherStatus.StatusReconnectConfirm;
|
||||||
|
_reconnectCnt++;
|
||||||
|
// UISys.Mgr.ShowUI(GAME_UI_TYPE.Tip_NetDisconn, UISys.Mgr.GetUIWindowParam().SetParam("errCode", m_disconnectReason));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateOnReconnectAuto()
|
||||||
|
{
|
||||||
|
if (_client.IsEntered)
|
||||||
|
{
|
||||||
|
Status = ClientConnectWatcherStatus.StatusInit;
|
||||||
|
_reconnectCnt = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float nowTime = NowTime;
|
||||||
|
var timeoutTime = 10f;
|
||||||
|
|
||||||
|
if (_statusTime + timeoutTime < nowTime)
|
||||||
|
{
|
||||||
|
Log.Error("UpdateOnReconnectAuto timeout: {0}", timeoutTime);
|
||||||
|
|
||||||
|
//切换到默认的,下一帧继续判断是否需要自动还是手动
|
||||||
|
Status = ClientConnectWatcherStatus.StatusInit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateOnReconnectConfirm()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateOnWaitExit()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDisable()
|
||||||
|
{
|
||||||
|
Status = ClientConnectWatcherStatus.StatusInit;
|
||||||
|
_reconnectCnt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
Status = ClientConnectWatcherStatus.StatusInit;
|
||||||
|
_reconnectCnt = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3e4e637f3da340dd9512150c3e2ff087
|
||||||
|
timeCreated: 1684334948
|
374
Assets/GameScripts/HotFix/GameLogic/Network/GameClient.cs
Normal file
374
Assets/GameScripts/HotFix/GameLogic/Network/GameClient.cs
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
using GameBase;
|
||||||
|
using GameProto;
|
||||||
|
using TEngine;
|
||||||
|
using CSPkg = GameProto.CSPkg;
|
||||||
|
|
||||||
|
namespace GameLogic
|
||||||
|
{
|
||||||
|
public enum GameClientStatus
|
||||||
|
{
|
||||||
|
StatusInit, //初始化
|
||||||
|
StatusReconnect, //重新连接
|
||||||
|
StatusClose, //断开连接
|
||||||
|
StatusLogin, //登录中
|
||||||
|
StatusEnter, //AccountLogin成功,进入服务器了
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum CsMsgResult
|
||||||
|
{
|
||||||
|
NoError = 0,
|
||||||
|
NetworkError = 1,
|
||||||
|
InternalError = 2,
|
||||||
|
MsgTimeOut = 3,
|
||||||
|
PingTimeOut = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
//定义消息回报的回调接口
|
||||||
|
public delegate void CsMsgDelegate(CsMsgResult result, CSPkg msg);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 统计网络协议的接口
|
||||||
|
/// </summary>
|
||||||
|
public delegate void CsMsgStatDelegate(int cmdID, int pkgSize);
|
||||||
|
|
||||||
|
public class GameClient : Singleton<GameClient>
|
||||||
|
{
|
||||||
|
public INetworkChannel Channel;
|
||||||
|
|
||||||
|
private GameClientStatus m_status = GameClientStatus.StatusInit;
|
||||||
|
|
||||||
|
private MsgDispatcher m_dispatcher;
|
||||||
|
|
||||||
|
private ClientConnectWatcher m_connectWatcher;
|
||||||
|
|
||||||
|
private float m_lastLogDisconnectErrTime = 0f;
|
||||||
|
|
||||||
|
private int m_lastNetErrCode = 0;
|
||||||
|
|
||||||
|
public int LastNetErrCode => m_lastNetErrCode;
|
||||||
|
|
||||||
|
public GameClientStatus Status
|
||||||
|
{
|
||||||
|
get => m_status;
|
||||||
|
set => m_status = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsEntered => m_status == GameClientStatus.StatusEnter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连续心跳超时
|
||||||
|
/// </summary>
|
||||||
|
private int m_heatBeatTimeoutNum = 0;
|
||||||
|
|
||||||
|
private int m_ping = -1;
|
||||||
|
|
||||||
|
private float NowTime => GameTime.unscaledTime;
|
||||||
|
|
||||||
|
private string _lastHost = null;
|
||||||
|
private int _lastPort = 0;
|
||||||
|
|
||||||
|
public GameClient()
|
||||||
|
{
|
||||||
|
m_connectWatcher = new ClientConnectWatcher(this);
|
||||||
|
m_dispatcher = new MsgDispatcher();
|
||||||
|
m_dispatcher.SetTimeout(5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Connect(string host, int port, bool reconnect = false)
|
||||||
|
{
|
||||||
|
ResetParam();
|
||||||
|
if (!reconnect)
|
||||||
|
{
|
||||||
|
SetWatchReconnect(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reconnect)
|
||||||
|
{
|
||||||
|
// GameEvent.Get<ICommUI>().ShowWaitUITip(WaitUISeq.LOGINWORLD_SEQID, G.R(TextDefine.ID_TIPS_RECONNECTING));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// GameEvent.Get<ICommUI>().ShowWaitUI(WaitUISeq.LOGINWORLD_SEQID);
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastHost = host;
|
||||||
|
_lastPort = port;
|
||||||
|
|
||||||
|
Channel = TEngine.Network.CreateNetworkChannel("GameClient", ServiceType.Tcp, new NetworkChannelHelper());
|
||||||
|
Channel.Connect(host, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reconnect()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_lastHost) || _lastPort <= 0)
|
||||||
|
{
|
||||||
|
// GameModule.UI.ShowTipMsg("Invalid reconnect param");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_connectWatcher.OnReConnect();
|
||||||
|
Connect(_lastHost, _lastPort, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Shutdown()
|
||||||
|
{
|
||||||
|
Channel.Close();
|
||||||
|
m_status = GameClientStatus.StatusInit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SendCsMsg(CSPkg reqPkg)
|
||||||
|
{
|
||||||
|
if (!IsStatusCanSendMsg(reqPkg.Head.MsgId))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DoSendData(reqPkg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsStatusCanSendMsg(uint msgId)
|
||||||
|
{
|
||||||
|
bool canSend = false;
|
||||||
|
if (m_status == GameClientStatus.StatusLogin)
|
||||||
|
{
|
||||||
|
canSend = (msgId == (uint)CSMsgID.CsCmdActLoginReq);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_status == GameClientStatus.StatusEnter)
|
||||||
|
{
|
||||||
|
canSend = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!canSend)
|
||||||
|
{
|
||||||
|
float nowTime = NowTime;
|
||||||
|
if (m_lastLogDisconnectErrTime + 5 < nowTime)
|
||||||
|
{
|
||||||
|
Log.Error("GameClient not connected, send msg failed, msgId[{0}]", msgId);
|
||||||
|
m_lastLogDisconnectErrTime = nowTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
//UISys.Mgr.ShowTipMsg(TextDefine.ID_ERR_NETWORKD_DISCONNECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return canSend;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SendCsMsg(CSPkg reqPkg, uint resCmd, CsMsgDelegate resHandler = null, bool needShowWaitUI = true)
|
||||||
|
{
|
||||||
|
if (!IsStatusCanSendMsg(reqPkg.Head.MsgId))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret = DoSendData(reqPkg);
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
if (resHandler != null)
|
||||||
|
{
|
||||||
|
resHandler(CsMsgResult.InternalError, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dispatcher.NotifyCmdError(resCmd, CsMsgResult.InternalError);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//注册消息
|
||||||
|
if (resHandler != null)
|
||||||
|
{
|
||||||
|
m_dispatcher.RegSeqHandle(reqPkg.Head.Echo, resCmd, resHandler);
|
||||||
|
if (reqPkg.Head.Echo > 0 && IsWaitingCmd(resCmd) && needShowWaitUI)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
// GameEvent.Get<ICommUI>().ShowWaitUI(reqPkg.Head.Echo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DoSendData(CSPkg reqPkg)
|
||||||
|
{
|
||||||
|
if (!IsIgnoreLog(reqPkg.Head.MsgId))
|
||||||
|
{
|
||||||
|
Log.Debug("[c-s] CmdId[{0}]\n{1}", reqPkg.Head.MsgId, reqPkg.Body.ToString());
|
||||||
|
}
|
||||||
|
var sendRet = Channel.Send(reqPkg);
|
||||||
|
return sendRet;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsIgnoreLog(uint msgId)
|
||||||
|
{
|
||||||
|
bool ignoreLog = false;
|
||||||
|
switch (msgId)
|
||||||
|
{
|
||||||
|
case (uint)CSMsgID.CsCmdHeatbeatReq:
|
||||||
|
case (uint)CSMsgID.CsCmdHeatbeatRes:
|
||||||
|
ignoreLog = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ignoreLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsWaitingCmd(uint msgId)
|
||||||
|
{
|
||||||
|
//心跳包不需要读条等待
|
||||||
|
if (msgId == (uint)CSMsgID.CsCmdHeatbeatRes)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetParam()
|
||||||
|
{
|
||||||
|
m_lastLogDisconnectErrTime = 0f;
|
||||||
|
m_heatBeatTimeoutNum = 0;
|
||||||
|
_lastHbTime = 0f;
|
||||||
|
m_ping = -1;
|
||||||
|
m_lastNetErrCode = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnUpdate()
|
||||||
|
{
|
||||||
|
m_dispatcher.Update();
|
||||||
|
TickHeartBeat();
|
||||||
|
CheckHeatBeatTimeout();
|
||||||
|
m_connectWatcher.Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置加密密钥
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
public void SetEncryptKey(string key)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置是否需要监控网络重连。
|
||||||
|
/// 登录成功后,开启监控,可以自动重连或者提示玩家重连。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="needWatch"></param>
|
||||||
|
public void SetWatchReconnect(bool needWatch)
|
||||||
|
{
|
||||||
|
m_connectWatcher.Enable = needWatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsNetworkOkAndLogin()
|
||||||
|
{
|
||||||
|
return m_status == GameClientStatus.StatusEnter;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region 心跳处理
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最近一次心跳的时间
|
||||||
|
/// </summary>
|
||||||
|
private float _lastHbTime = 0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 心跳间隔
|
||||||
|
/// </summary>
|
||||||
|
private readonly float _heartBeatDurTime = 5;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 心跳超时的最大次数
|
||||||
|
/// </summary>
|
||||||
|
private const int HeatBeatTimeoutMaxCount = 2;
|
||||||
|
|
||||||
|
private bool CheckHeatBeatTimeout()
|
||||||
|
{
|
||||||
|
if (m_heatBeatTimeoutNum >= HeatBeatTimeoutMaxCount)
|
||||||
|
{
|
||||||
|
//断开连接
|
||||||
|
Shutdown();
|
||||||
|
|
||||||
|
//准备重连
|
||||||
|
m_heatBeatTimeoutNum = 0;
|
||||||
|
Status = GameClientStatus.StatusClose;
|
||||||
|
Log.Error("heat beat detect timeout");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TickHeartBeat()
|
||||||
|
{
|
||||||
|
if (Status != GameClientStatus.StatusEnter)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nowTime = NowTime;
|
||||||
|
if (_lastHbTime + _heartBeatDurTime < nowTime)
|
||||||
|
{
|
||||||
|
_lastHbTime = nowTime;
|
||||||
|
|
||||||
|
CSPkg heatPkg = ProtobufUtility.BuildCsMsg((int)CSMsgID.CsCmdHeatbeatReq);
|
||||||
|
heatPkg.Body.HeatBeatReq = new CSHeatBeatReq { HeatEchoTime = _lastHbTime };
|
||||||
|
SendCsMsg(heatPkg, (int)CSMsgID.CsCmdHeatbeatRes, HandleHeatBeatRes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleHeatBeatRes(CsMsgResult result, CSPkg msg)
|
||||||
|
{
|
||||||
|
if (result != CsMsgResult.NoError)
|
||||||
|
{
|
||||||
|
//如果是超时了,则标记最近收到包的次数
|
||||||
|
if (result == CsMsgResult.MsgTimeOut)
|
||||||
|
{
|
||||||
|
m_heatBeatTimeoutNum++;
|
||||||
|
Log.Warning("heat beat timeout: {0}", m_heatBeatTimeoutNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var resBody = msg.Body.HeatBeatRes;
|
||||||
|
float diffTime = NowTime - resBody.HeatEchoTime;
|
||||||
|
m_ping = (int)(diffTime * 1000);
|
||||||
|
m_heatBeatTimeoutNum = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Ping值
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ping值
|
||||||
|
/// </summary>
|
||||||
|
public int Ping
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (IsPingValid())
|
||||||
|
{
|
||||||
|
return m_ping / 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsPingValid()
|
||||||
|
{
|
||||||
|
if (IsNetworkOkAndLogin())
|
||||||
|
{
|
||||||
|
return m_ping >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8c5441725c9f4d98a7790dc76a6a0c48
|
||||||
|
timeCreated: 1684331687
|
@@ -1,19 +0,0 @@
|
|||||||
using System;
|
|
||||||
using ProtoBuf;
|
|
||||||
|
|
||||||
namespace GameLogic
|
|
||||||
{
|
|
||||||
[Serializable, ProtoContract(Name = @"HeartBeat")]
|
|
||||||
public class HeartBeat : PacketBase
|
|
||||||
{
|
|
||||||
public HeartBeat()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int Id => 1;
|
|
||||||
|
|
||||||
public override void Clear()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: d8b9b60d2abf409ca9dd2ce00506ac79
|
|
||||||
timeCreated: 1682046644
|
|
264
Assets/GameScripts/HotFix/GameLogic/Network/MsgDispatcher.cs
Normal file
264
Assets/GameScripts/HotFix/GameLogic/Network/MsgDispatcher.cs
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using GameProto;
|
||||||
|
using TEngine;
|
||||||
|
using CSPkg = GameProto.CSPkg;
|
||||||
|
|
||||||
|
namespace GameLogic
|
||||||
|
{
|
||||||
|
internal class MsgHandleDataToRmv
|
||||||
|
{
|
||||||
|
public uint m_msgCmd;
|
||||||
|
public CsMsgDelegate m_handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MsgDispatcher
|
||||||
|
{
|
||||||
|
const int CHECK_TIMEOUT_PERFRAME = 10;
|
||||||
|
const int MAX_MSG_HANDLE = 256;
|
||||||
|
|
||||||
|
CsMsgDelegate[] m_aMsgHandles = new CsMsgDelegate[MAX_MSG_HANDLE];
|
||||||
|
float[] m_fMsgRegTime = new float[MAX_MSG_HANDLE];
|
||||||
|
UInt32[] m_adwMsgRegSeq = new UInt32[MAX_MSG_HANDLE]; //因为m_aMsgHandles存储的是hash,不能保证一定seqid一样,所以这儿存储下,用来校验
|
||||||
|
private uint[] m_aiMsgRegResCmdID = new uint[MAX_MSG_HANDLE];
|
||||||
|
|
||||||
|
UInt32 m_dwLastCheckIndex = 0;
|
||||||
|
|
||||||
|
Dictionary<uint, List<CsMsgDelegate>> m_mapCmdHandle = new Dictionary<uint, List<CsMsgDelegate>>();
|
||||||
|
List<CsMsgStatDelegate> m_listStatHandle = new List<CsMsgStatDelegate>();
|
||||||
|
|
||||||
|
//防止在处理消息的时候又删除了消息映射,所以这儿加了个队列来做个保护
|
||||||
|
private List<MsgHandleDataToRmv> m_rmvList = new List<MsgHandleDataToRmv>();
|
||||||
|
private bool m_isInHandleLoop = false;
|
||||||
|
|
||||||
|
private float m_timeout = 5;
|
||||||
|
|
||||||
|
// 清理所有的网络消息
|
||||||
|
public void CleanAllNetMsg()
|
||||||
|
{
|
||||||
|
m_mapCmdHandle.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTimeout(float timeout)
|
||||||
|
{
|
||||||
|
m_timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegSeqHandle(UInt32 dwMsgSeqID, uint iResCmdID, CsMsgDelegate msgDelegate)
|
||||||
|
{
|
||||||
|
UInt32 hashIndex = dwMsgSeqID % MAX_MSG_HANDLE;
|
||||||
|
if (m_aMsgHandles[hashIndex] != null)
|
||||||
|
{
|
||||||
|
OnCallSeqHandle(m_adwMsgRegSeq[hashIndex], m_aiMsgRegResCmdID[hashIndex]);
|
||||||
|
NotifyTimeout(m_aMsgHandles[hashIndex]);
|
||||||
|
RmvReg((int)hashIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_aMsgHandles[hashIndex] = msgDelegate;
|
||||||
|
m_fMsgRegTime[hashIndex] = NowTime;
|
||||||
|
m_adwMsgRegSeq[hashIndex] = dwMsgSeqID;
|
||||||
|
m_aiMsgRegResCmdID[hashIndex] = iResCmdID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegCmdHandle(uint iCmdID, CsMsgDelegate msgDelegate)
|
||||||
|
{
|
||||||
|
List<CsMsgDelegate> listHandle;
|
||||||
|
if (!m_mapCmdHandle.TryGetValue(iCmdID, out listHandle))
|
||||||
|
{
|
||||||
|
listHandle = new List<CsMsgDelegate>();
|
||||||
|
m_mapCmdHandle[iCmdID] = listHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listHandle != null)
|
||||||
|
{
|
||||||
|
if (listHandle.Contains(msgDelegate))
|
||||||
|
{
|
||||||
|
Log.Error("-------------repeat RegCmdHandle:{0}-----------", iCmdID);
|
||||||
|
}
|
||||||
|
|
||||||
|
listHandle.Add(msgDelegate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册统计处理接口
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handler"></param>
|
||||||
|
public void RegCmdStatHandle(CsMsgStatDelegate handler)
|
||||||
|
{
|
||||||
|
m_listStatHandle.Add(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DispatchCmdStat(int cmdID, int pkgSize)
|
||||||
|
{
|
||||||
|
foreach (CsMsgStatDelegate handle in m_listStatHandle)
|
||||||
|
{
|
||||||
|
handle(cmdID, pkgSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RmvCmdHandle(uint iCmdID, CsMsgDelegate msgDelegate)
|
||||||
|
{
|
||||||
|
if (m_isInHandleLoop)
|
||||||
|
{
|
||||||
|
MsgHandleDataToRmv toRmvData = new MsgHandleDataToRmv();
|
||||||
|
toRmvData.m_msgCmd = iCmdID;
|
||||||
|
toRmvData.m_handle = msgDelegate;
|
||||||
|
|
||||||
|
m_rmvList.Add(toRmvData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CsMsgDelegate> listHandle;
|
||||||
|
if (!m_mapCmdHandle.TryGetValue(iCmdID, out listHandle))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listHandle != null)
|
||||||
|
{
|
||||||
|
listHandle.Remove(msgDelegate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotifyCmdError(uint iCmdID, CsMsgResult result)
|
||||||
|
{
|
||||||
|
NotifyCmdHandle(iCmdID, result, default(CSPkg));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool NotifyCmdHandle(uint cmdID, CsMsgResult result, CSPkg pkg)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (m_mapCmdHandle.TryGetValue(cmdID, out var listHandle))
|
||||||
|
{
|
||||||
|
m_isInHandleLoop = true;
|
||||||
|
|
||||||
|
var rmvList = m_rmvList;
|
||||||
|
rmvList.Clear();
|
||||||
|
foreach (CsMsgDelegate handle in listHandle)
|
||||||
|
{
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
TProfiler.BeginSample("handle");
|
||||||
|
handle(result, pkg);
|
||||||
|
TProfiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_isInHandleLoop = false;
|
||||||
|
|
||||||
|
//再统一删除掉
|
||||||
|
int rmvCnt = rmvList.Count;
|
||||||
|
for (int i = 0; i < rmvCnt; i++)
|
||||||
|
{
|
||||||
|
var rmvItem = rmvList[i];
|
||||||
|
Log.Error("-------------remove cmd handle on loop:{0}-----------", rmvItem.m_msgCmd);
|
||||||
|
RmvCmdHandle(rmvItem.m_msgCmd, rmvItem.m_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void OnCallSeqHandle(UInt32 echoSeq, uint resCmdID)
|
||||||
|
{
|
||||||
|
if (echoSeq > 0)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
// GameEvent.Get<ICommUI>().FinWaitUI(echoSeq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void NotifyTimeout(CsMsgDelegate msgHandler)
|
||||||
|
{
|
||||||
|
msgHandler(CsMsgResult.MsgTimeOut, default(CSPkg));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotifySeqError(UInt32 dwSeqID, CsMsgResult result)
|
||||||
|
{
|
||||||
|
UInt32 hashIndex = dwSeqID % MAX_MSG_HANDLE;
|
||||||
|
|
||||||
|
//先判断是否有注册的指定消息
|
||||||
|
if (m_aMsgHandles[hashIndex] != null &&
|
||||||
|
m_adwMsgRegSeq[hashIndex] == dwSeqID)
|
||||||
|
{
|
||||||
|
OnCallSeqHandle(dwSeqID, m_aiMsgRegResCmdID[hashIndex]);
|
||||||
|
m_aMsgHandles[hashIndex](result, null);
|
||||||
|
|
||||||
|
RmvReg((int)hashIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCmdFilterNoLog(int cmdID)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
/*switch (cmdID)
|
||||||
|
{
|
||||||
|
case netMacros.CS_CMD_HEATBEAT_RES:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotifyMsg(CSPkg msg)
|
||||||
|
{
|
||||||
|
UInt32 dwSeq = msg.Head.Echo;
|
||||||
|
UInt32 hashIndex = dwSeq % MAX_MSG_HANDLE;
|
||||||
|
//判断是否有固定的消息处理流程
|
||||||
|
bool bHaveHandle = NotifyCmdHandle(msg.Head.MsgId, CsMsgResult.NoError, msg);
|
||||||
|
|
||||||
|
//再判断是否有注册的指定消息
|
||||||
|
if (m_aMsgHandles[hashIndex] != null &&
|
||||||
|
m_adwMsgRegSeq[hashIndex] == dwSeq &&
|
||||||
|
m_aiMsgRegResCmdID[hashIndex] == (int)msg.Head.MsgId)
|
||||||
|
{
|
||||||
|
OnCallSeqHandle(m_adwMsgRegSeq[hashIndex], m_aiMsgRegResCmdID[hashIndex]);
|
||||||
|
m_aMsgHandles[hashIndex](CsMsgResult.NoError, msg);
|
||||||
|
RmvReg((int)hashIndex);
|
||||||
|
bHaveHandle = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bHaveHandle)
|
||||||
|
{
|
||||||
|
//todo..临时改为debug
|
||||||
|
Log.Debug("there is no handle for Msg[{0}]", msg.Head.MsgId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float NowTime => GameTime.unscaledTime;
|
||||||
|
|
||||||
|
//定时检查是否请求超时了
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
float timeout = m_timeout;
|
||||||
|
float nowTime = NowTime;
|
||||||
|
for (int i = 0; i < CHECK_TIMEOUT_PERFRAME; i++)
|
||||||
|
{
|
||||||
|
m_dwLastCheckIndex = (m_dwLastCheckIndex + 1) % MAX_MSG_HANDLE;
|
||||||
|
if (m_aMsgHandles[m_dwLastCheckIndex] != null)
|
||||||
|
{
|
||||||
|
if (m_fMsgRegTime[m_dwLastCheckIndex] + timeout < nowTime)
|
||||||
|
{
|
||||||
|
Log.Error("msg timeout, resCmdID[{0}], reqSeq[{1}]", m_aiMsgRegResCmdID[m_dwLastCheckIndex],
|
||||||
|
m_adwMsgRegSeq[m_dwLastCheckIndex]);
|
||||||
|
|
||||||
|
OnCallSeqHandle(m_adwMsgRegSeq[m_dwLastCheckIndex], m_aiMsgRegResCmdID[m_dwLastCheckIndex]);
|
||||||
|
NotifyTimeout(m_aMsgHandles[m_dwLastCheckIndex]);
|
||||||
|
|
||||||
|
RmvReg((int)m_dwLastCheckIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RmvReg(int index)
|
||||||
|
{
|
||||||
|
m_aMsgHandles[index] = null;
|
||||||
|
m_adwMsgRegSeq[index] = 0;
|
||||||
|
m_aiMsgRegResCmdID[index] = 0;
|
||||||
|
m_fMsgRegTime[index] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7d667fb84fed4c5f93a06c464585512f
|
||||||
|
timeCreated: 1684333223
|
@@ -1,10 +1,9 @@
|
|||||||
using ProtoBuf;
|
using System;
|
||||||
using ProtoBuf.Meta;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using GameProto;
|
||||||
using TEngine;
|
using TEngine;
|
||||||
|
|
||||||
namespace GameLogic
|
namespace GameLogic
|
||||||
@@ -29,31 +28,6 @@ namespace GameLogic
|
|||||||
{
|
{
|
||||||
_networkChannel = networkChannel;
|
_networkChannel = networkChannel;
|
||||||
|
|
||||||
// 反射注册包和包处理函数。
|
|
||||||
Type packetBaseType = typeof(ProtoPacket);
|
|
||||||
Assembly assembly = Assembly.GetExecutingAssembly();
|
|
||||||
Type[] types = assembly.GetTypes();
|
|
||||||
for (int i = 0; i < types.Length; i++)
|
|
||||||
{
|
|
||||||
if (!types[i].IsClass || types[i].IsAbstract)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (types[i].BaseType == packetBaseType)
|
|
||||||
{
|
|
||||||
PacketBase packetBase = (PacketBase)Activator.CreateInstance(types[i]);
|
|
||||||
Type packetType = GetServerToClientPacketType(packetBase.Id);
|
|
||||||
if (packetType != null)
|
|
||||||
{
|
|
||||||
Log.Warning("Already exist packet type '{0}', check '{1}' or '{2}'?.", packetBase.Id.ToString(), packetType.Name, packetBase.GetType().Name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_serverToClientPacketTypes.Add(packetBase.Id, types[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GameEvent.AddEventListener<INetworkChannel, object>(NetworkEvent.NetworkConnectedEvent, OnNetworkConnected);
|
GameEvent.AddEventListener<INetworkChannel, object>(NetworkEvent.NetworkConnectedEvent, OnNetworkConnected);
|
||||||
GameEvent.AddEventListener<INetworkChannel>(NetworkEvent.NetworkClosedEvent, OnNetworkClosed);
|
GameEvent.AddEventListener<INetworkChannel>(NetworkEvent.NetworkClosedEvent, OnNetworkClosed);
|
||||||
GameEvent.AddEventListener<INetworkChannel, int>(NetworkEvent.NetworkMissHeartBeatEvent, OnNetworkMissHeartBeat);
|
GameEvent.AddEventListener<INetworkChannel, int>(NetworkEvent.NetworkMissHeartBeatEvent, OnNetworkMissHeartBeat);
|
||||||
@@ -84,13 +58,16 @@ namespace GameLogic
|
|||||||
_networkChannel.Socket.SendBufferSize = 1024 * 64;
|
_networkChannel.Socket.SendBufferSize = 1024 * 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CSPkg HeartBeatPack = new CSPkg { Head = new CSPkgHead(), Body = new CSPkgBody() };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 发送心跳消息包。
|
/// 发送心跳消息包。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>是否发送心跳消息包成功。</returns>
|
/// <returns>是否发送心跳消息包成功。</returns>
|
||||||
public bool SendHeartBeat()
|
public bool SendHeartBeat()
|
||||||
{
|
{
|
||||||
_networkChannel.Send(MemoryPool.Acquire<HeartBeat>());
|
HeartBeatPack.Head.MsgId = (uint)CSMsgID.CsCmdHeatbeatReq;
|
||||||
|
_networkChannel.Send(HeartBeatPack);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,10 +78,9 @@ namespace GameLogic
|
|||||||
/// <param name="packet">要序列化的消息包。</param>
|
/// <param name="packet">要序列化的消息包。</param>
|
||||||
/// <param name="destination">要序列化的目标流。</param>
|
/// <param name="destination">要序列化的目标流。</param>
|
||||||
/// <returns>是否序列化成功。</returns>
|
/// <returns>是否序列化成功。</returns>
|
||||||
public bool Serialize<T>(T packet, Stream destination) where T : Packet
|
public bool Serialize(CSPkg packet, Stream destination)
|
||||||
{
|
{
|
||||||
PacketBase packetImpl = packet as PacketBase;
|
if (packet == null)
|
||||||
if (packetImpl == null)
|
|
||||||
{
|
{
|
||||||
Log.Warning("Packet is invalid.");
|
Log.Warning("Packet is invalid.");
|
||||||
return false;
|
return false;
|
||||||
@@ -113,12 +89,7 @@ namespace GameLogic
|
|||||||
_cachedStream.SetLength(_cachedStream.Capacity); // 此行防止 Array.Copy 的数据无法写入
|
_cachedStream.SetLength(_cachedStream.Capacity); // 此行防止 Array.Copy 的数据无法写入
|
||||||
_cachedStream.Position = 0L;
|
_cachedStream.Position = 0L;
|
||||||
|
|
||||||
PacketHeader packetHeader = MemoryPool.Acquire<PacketHeader>();
|
global::ProtobufUtility.ToStream(packet,destination);
|
||||||
Serializer.Serialize(_cachedStream, packetHeader);
|
|
||||||
MemoryPool.Release(packetHeader);
|
|
||||||
|
|
||||||
Serializer.SerializeWithLengthPrefix(_cachedStream, packet, PrefixStyle.Fixed32);
|
|
||||||
MemoryPool.Release((IMemory)packet);
|
|
||||||
|
|
||||||
_cachedStream.WriteTo(destination);
|
_cachedStream.WriteTo(destination);
|
||||||
return true;
|
return true;
|
||||||
@@ -132,9 +103,10 @@ namespace GameLogic
|
|||||||
/// <returns>反序列化后的消息包头。</returns>
|
/// <returns>反序列化后的消息包头。</returns>
|
||||||
public IPacketHeader DeserializePacketHeader(Stream source, out object customErrorData)
|
public IPacketHeader DeserializePacketHeader(Stream source, out object customErrorData)
|
||||||
{
|
{
|
||||||
|
// TODO
|
||||||
// 注意:此函数并不在主线程调用!
|
// 注意:此函数并不在主线程调用!
|
||||||
customErrorData = null;
|
customErrorData = null;
|
||||||
return (IPacketHeader)RuntimeTypeModel.Default.Deserialize(source, MemoryPool.Acquire<PacketHeader>(), typeof(PacketHeader));
|
return null; //(IPacketHeader)RuntimeTypeModel.Default.Deserialize(source, MemoryPool.Acquire<PacketHeader>(), typeof(PacketHeader));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -144,7 +116,7 @@ namespace GameLogic
|
|||||||
/// <param name="source">要反序列化的来源流。</param>
|
/// <param name="source">要反序列化的来源流。</param>
|
||||||
/// <param name="customErrorData">用户自定义错误数据。</param>
|
/// <param name="customErrorData">用户自定义错误数据。</param>
|
||||||
/// <returns>反序列化后的消息包。</returns>
|
/// <returns>反序列化后的消息包。</returns>
|
||||||
public Packet DeserializePacket(IPacketHeader packetHeader, Stream source, out object customErrorData)
|
public CSPkg DeserializePacket(IPacketHeader packetHeader, Stream source, out object customErrorData)
|
||||||
{
|
{
|
||||||
// 注意:此函数并不在主线程调用!
|
// 注意:此函数并不在主线程调用!
|
||||||
customErrorData = null;
|
customErrorData = null;
|
||||||
@@ -156,13 +128,13 @@ namespace GameLogic
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Packet packet = null;
|
CSPkg csPkg = null;
|
||||||
if (scPacketHeader.IsValid)
|
if (scPacketHeader.IsValid)
|
||||||
{
|
{
|
||||||
Type packetType = GetServerToClientPacketType(scPacketHeader.Id);
|
Type packetType = GetServerToClientPacketType(scPacketHeader.Id);
|
||||||
if (packetType != null)
|
if (packetType != null)
|
||||||
{
|
{
|
||||||
packet = (Packet)RuntimeTypeModel.Default.DeserializeWithLengthPrefix(source, MemoryPool.Acquire(packetType), packetType, PrefixStyle.Fixed32, 0);
|
csPkg = global::ProtobufUtility.Deserialize<CSPkg>(((MemoryStream)source).GetBuffer());;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -175,7 +147,7 @@ namespace GameLogic
|
|||||||
}
|
}
|
||||||
|
|
||||||
MemoryPool.Release(scPacketHeader);
|
MemoryPool.Release(scPacketHeader);
|
||||||
return packet;
|
return csPkg;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Type GetServerToClientPacketType(int id)
|
private Type GetServerToClientPacketType(int id)
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
using GameProto;
|
||||||
|
using Google.Protobuf;
|
||||||
|
|
||||||
namespace TEngine
|
namespace TEngine
|
||||||
{
|
{
|
||||||
@@ -163,7 +165,7 @@ namespace TEngine
|
|||||||
/// <typeparam name="T">消息包类型。</typeparam>
|
/// <typeparam name="T">消息包类型。</typeparam>
|
||||||
/// <param name="packet">要发送的消息包。</param>
|
/// <param name="packet">要发送的消息包。</param>
|
||||||
/// <returns>消息包是否发送成功。</returns>
|
/// <returns>消息包是否发送成功。</returns>
|
||||||
bool Send<T>(T packet) where T : Packet;
|
bool Send(CSPkg packet);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 向远程主机发送消息包并注册消息回调。
|
/// 向远程主机发送消息包并注册消息回调。
|
||||||
@@ -173,6 +175,6 @@ namespace TEngine
|
|||||||
/// <param name="resHandler">要注册的回调。</param>
|
/// <param name="resHandler">要注册的回调。</param>
|
||||||
/// <param name="needShowWaitUI">是否需要等待UI。</param>
|
/// <param name="needShowWaitUI">是否需要等待UI。</param>
|
||||||
/// <returns>消息包是否发送成功。</returns>
|
/// <returns>消息包是否发送成功。</returns>
|
||||||
bool Send<T>(T packet, CsMsgDelegate resHandler, bool needShowWaitUI = false) where T : Packet;
|
bool Send(CSPkg packet, CsMsgDelegate resHandler, bool needShowWaitUI = false);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
|
using GameProto;
|
||||||
|
|
||||||
namespace TEngine
|
namespace TEngine
|
||||||
{
|
{
|
||||||
@@ -44,7 +45,7 @@ namespace TEngine
|
|||||||
/// <param name="packet">要序列化的消息包。</param>
|
/// <param name="packet">要序列化的消息包。</param>
|
||||||
/// <param name="destination">要序列化的目标流。</param>
|
/// <param name="destination">要序列化的目标流。</param>
|
||||||
/// <returns>是否序列化成功。</returns>
|
/// <returns>是否序列化成功。</returns>
|
||||||
bool Serialize<T>(T packet, Stream destination) where T : Packet;
|
bool Serialize(CSPkg packet, Stream destination);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 反序列化消息包头。
|
/// 反序列化消息包头。
|
||||||
@@ -61,6 +62,6 @@ namespace TEngine
|
|||||||
/// <param name="source">要反序列化的来源流。</param>
|
/// <param name="source">要反序列化的来源流。</param>
|
||||||
/// <param name="customErrorData">用户自定义错误数据。</param>
|
/// <param name="customErrorData">用户自定义错误数据。</param>
|
||||||
/// <returns>反序列化后的消息包。</returns>
|
/// <returns>反序列化后的消息包。</returns>
|
||||||
Packet DeserializePacket(IPacketHeader packetHeader, Stream source, out object customErrorData);
|
CSPkg DeserializePacket(IPacketHeader packetHeader, Stream source, out object customErrorData);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -2,6 +2,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
using GameProto;
|
||||||
|
using Google.Protobuf;
|
||||||
|
|
||||||
namespace TEngine
|
namespace TEngine
|
||||||
{
|
{
|
||||||
@@ -15,7 +17,7 @@ namespace TEngine
|
|||||||
private const float DefaultHeartBeatInterval = 30f;
|
private const float DefaultHeartBeatInterval = 30f;
|
||||||
|
|
||||||
private readonly string _name;
|
private readonly string _name;
|
||||||
protected readonly Queue<Packet> SendPacketPool;
|
protected readonly Queue<CSPkg> SendPacketPool;
|
||||||
protected readonly INetworkChannelHelper NetworkChannelHelper;
|
protected readonly INetworkChannelHelper NetworkChannelHelper;
|
||||||
protected AddressFamily MAddressFamily;
|
protected AddressFamily MAddressFamily;
|
||||||
protected bool MResetHeartBeatElapseSecondsWhenReceivePacket;
|
protected bool MResetHeartBeatElapseSecondsWhenReceivePacket;
|
||||||
@@ -48,7 +50,7 @@ namespace TEngine
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 消息包缓存堆栈。
|
/// 消息包缓存堆栈。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly Queue<Packet> _packsQueue = new Queue<Packet>();
|
private readonly Queue<CSPkg> _packsQueue = new Queue<CSPkg>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化网络频道基类的新实例。
|
/// 初始化网络频道基类的新实例。
|
||||||
@@ -58,7 +60,7 @@ namespace TEngine
|
|||||||
public NetworkChannelBase(string name, INetworkChannelHelper networkChannelHelper)
|
public NetworkChannelBase(string name, INetworkChannelHelper networkChannelHelper)
|
||||||
{
|
{
|
||||||
_name = name ?? string.Empty;
|
_name = name ?? string.Empty;
|
||||||
SendPacketPool = new Queue<Packet>();
|
SendPacketPool = new Queue<CSPkg>();
|
||||||
NetworkChannelHelper = networkChannelHelper;
|
NetworkChannelHelper = networkChannelHelper;
|
||||||
MAddressFamily = AddressFamily.Unknown;
|
MAddressFamily = AddressFamily.Unknown;
|
||||||
MResetHeartBeatElapseSecondsWhenReceivePacket = false;
|
MResetHeartBeatElapseSecondsWhenReceivePacket = false;
|
||||||
@@ -411,9 +413,8 @@ namespace TEngine
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 向远程主机发送消息包。
|
/// 向远程主机发送消息包。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">消息包类型。</typeparam>
|
|
||||||
/// <param name="packet">要发送的消息包。</param>
|
/// <param name="packet">要发送的消息包。</param>
|
||||||
public bool Send<T>(T packet) where T : Packet
|
public bool Send(CSPkg packet)
|
||||||
{
|
{
|
||||||
if (MSocket == null)
|
if (MSocket == null)
|
||||||
{
|
{
|
||||||
@@ -466,9 +467,9 @@ namespace TEngine
|
|||||||
/// <param name="resHandler">要注册的回调。</param>
|
/// <param name="resHandler">要注册的回调。</param>
|
||||||
/// <param name="needShowWaitUI">是否需要等待UI。</param>
|
/// <param name="needShowWaitUI">是否需要等待UI。</param>
|
||||||
/// <returns>消息包是否发送成功。</returns>
|
/// <returns>消息包是否发送成功。</returns>
|
||||||
public bool Send<T>(T packet, CsMsgDelegate resHandler, bool needShowWaitUI = false) where T : Packet
|
public bool Send(CSPkg packet, CsMsgDelegate resHandler, bool needShowWaitUI = false)
|
||||||
{
|
{
|
||||||
RegisterMsgHandler(packet.Id,resHandler,false);
|
RegisterMsgHandler((int)packet.Head.MsgId,resHandler,false);
|
||||||
return Send(packet);
|
return Send(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,16 +512,16 @@ namespace TEngine
|
|||||||
|
|
||||||
while (SendPacketPool.Count > 0)
|
while (SendPacketPool.Count > 0)
|
||||||
{
|
{
|
||||||
Packet packet = null;
|
CSPkg csPkg = null;
|
||||||
lock (SendPacketPool)
|
lock (SendPacketPool)
|
||||||
{
|
{
|
||||||
packet = SendPacketPool.Dequeue();
|
csPkg = SendPacketPool.Dequeue();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool serializeResult = false;
|
bool serializeResult = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
serializeResult = NetworkChannelHelper.Serialize(packet, MSendState.Stream);
|
serializeResult = NetworkChannelHelper.Serialize(csPkg, MSendState.Stream);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
@@ -614,22 +615,22 @@ namespace TEngine
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Packet packet = NetworkChannelHelper.DeserializePacket(MReceiveState.PacketHeader, MReceiveState.Stream, out var customErrorData);
|
CSPkg csPkg = NetworkChannelHelper.DeserializePacket(MReceiveState.PacketHeader, MReceiveState.Stream, out var customErrorData);
|
||||||
|
|
||||||
if (customErrorData != null && NetworkChannelCustomError != null)
|
if (customErrorData != null && NetworkChannelCustomError != null)
|
||||||
{
|
{
|
||||||
NetworkChannelCustomError(this, customErrorData);
|
NetworkChannelCustomError(this, customErrorData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packet != null)
|
if (csPkg != null)
|
||||||
{
|
{
|
||||||
lock (_cacheHandlerQueue)
|
lock (_cacheHandlerQueue)
|
||||||
{
|
{
|
||||||
if (_msgHandlerMap.TryGetValue((int)packet.Id, out var listHandle))
|
if (_msgHandlerMap.TryGetValue((int)csPkg.Head.MsgId, out var listHandle))
|
||||||
{
|
{
|
||||||
_cacheHandlerQueue.Enqueue(listHandle);
|
_cacheHandlerQueue.Enqueue(listHandle);
|
||||||
|
|
||||||
_packsQueue.Enqueue(packet);
|
_packsQueue.Enqueue(csPkg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,18 +1,20 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
using GameBase;
|
||||||
|
using GameProto;
|
||||||
|
|
||||||
namespace TEngine
|
namespace TEngine
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 网络消息委托。
|
/// 网络消息委托。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public delegate void CsMsgDelegate(Packet packet);
|
public delegate void CsMsgDelegate(CSPkg csPkg);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 网络管理器。
|
/// 网络管理器。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed partial class NetworkManager : GameFrameworkModule, INetworkManager
|
internal sealed partial class NetworkManager : Singleton<NetworkManager>, INetworkManager
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, NetworkChannelBase> _networkChannels;
|
private readonly Dictionary<string, NetworkChannelBase> _networkChannels;
|
||||||
|
|
||||||
@@ -90,7 +92,7 @@ namespace TEngine
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="elapseSeconds">逻辑流逝时间,以秒为单位。</param>
|
/// <param name="elapseSeconds">逻辑流逝时间,以秒为单位。</param>
|
||||||
/// <param name="realElapseSeconds">真实流逝时间,以秒为单位。</param>
|
/// <param name="realElapseSeconds">真实流逝时间,以秒为单位。</param>
|
||||||
internal override void Update(float elapseSeconds, float realElapseSeconds)
|
public void Update(float elapseSeconds, float realElapseSeconds)
|
||||||
{
|
{
|
||||||
foreach (KeyValuePair<string, NetworkChannelBase> networkChannel in _networkChannels)
|
foreach (KeyValuePair<string, NetworkChannelBase> networkChannel in _networkChannels)
|
||||||
{
|
{
|
||||||
@@ -101,7 +103,7 @@ namespace TEngine
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 关闭并清理网络管理器。
|
/// 关闭并清理网络管理器。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal override void Shutdown()
|
public void Shutdown()
|
||||||
{
|
{
|
||||||
foreach (KeyValuePair<string, NetworkChannelBase> networkChannel in _networkChannels)
|
foreach (KeyValuePair<string, NetworkChannelBase> networkChannel in _networkChannels)
|
||||||
{
|
{
|
||||||
@@ -183,7 +185,8 @@ namespace TEngine
|
|||||||
/// <param name="serviceType">网络服务类型。</param>
|
/// <param name="serviceType">网络服务类型。</param>
|
||||||
/// <param name="networkChannelHelper">网络频道辅助器。</param>
|
/// <param name="networkChannelHelper">网络频道辅助器。</param>
|
||||||
/// <returns>要创建的网络频道。</returns>
|
/// <returns>要创建的网络频道。</returns>
|
||||||
public INetworkChannel CreateNetworkChannel(string name, ServiceType serviceType, INetworkChannelHelper networkChannelHelper)
|
public INetworkChannel CreateNetworkChannel(string name, ServiceType serviceType,
|
||||||
|
INetworkChannelHelper networkChannelHelper)
|
||||||
{
|
{
|
||||||
if (networkChannelHelper == null)
|
if (networkChannelHelper == null)
|
||||||
{
|
{
|
||||||
@@ -197,7 +200,8 @@ namespace TEngine
|
|||||||
|
|
||||||
if (HasNetworkChannel(name))
|
if (HasNetworkChannel(name))
|
||||||
{
|
{
|
||||||
throw new GameFrameworkException(Utility.Text.Format("Already exist network channel '{0}'.", name ?? string.Empty));
|
throw new GameFrameworkException(Utility.Text.Format("Already exist network channel '{0}'.",
|
||||||
|
name ?? string.Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkChannelBase networkChannel = null;
|
NetworkChannelBase networkChannel = null;
|
||||||
@@ -220,7 +224,8 @@ namespace TEngine
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new GameFrameworkException(Utility.Text.Format("Not supported service type '{0}'.", serviceType));
|
throw new GameFrameworkException(Utility.Text.Format("Not supported service type '{0}'.",
|
||||||
|
serviceType));
|
||||||
}
|
}
|
||||||
|
|
||||||
networkChannel.NetworkChannelConnected += OnNetworkChannelConnected;
|
networkChannel.NetworkChannelConnected += OnNetworkChannelConnected;
|
||||||
@@ -249,6 +254,7 @@ namespace TEngine
|
|||||||
networkChannel.Shutdown();
|
networkChannel.Shutdown();
|
||||||
return name != null && _networkChannels.Remove(name);
|
return name != null && _networkChannels.Remove(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,7 +291,8 @@ namespace TEngine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnNetworkChannelError(NetworkChannelBase networkChannel, NetworkErrorCode errorCode, SocketError socketErrorCode, string errorMessage)
|
private void OnNetworkChannelError(NetworkChannelBase networkChannel, NetworkErrorCode errorCode,
|
||||||
|
SocketError socketErrorCode, string errorMessage)
|
||||||
{
|
{
|
||||||
if (_networkErrorEventHandler != null)
|
if (_networkErrorEventHandler != null)
|
||||||
{
|
{
|
@@ -1,24 +0,0 @@
|
|||||||
using TEngine;
|
|
||||||
|
|
||||||
namespace GameLogic
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 网络消息包基类。
|
|
||||||
/// </summary>
|
|
||||||
public abstract class PacketBase : Packet
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 网络消息包Id。
|
|
||||||
/// </summary>
|
|
||||||
public int ProtoId;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 网络消息包包体。
|
|
||||||
/// </summary>
|
|
||||||
public byte[] ProtoBody;
|
|
||||||
|
|
||||||
public void Close()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: acea4283f57644b6b4452354d23f2803
|
|
||||||
timeCreated: 1682045887
|
|
@@ -1,15 +0,0 @@
|
|||||||
namespace GameLogic
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 网络消息包。
|
|
||||||
/// </summary>
|
|
||||||
public partial class ProtoPacket : PacketBase
|
|
||||||
{
|
|
||||||
public override int Id => 1;
|
|
||||||
|
|
||||||
public override void Clear()
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: da70995486db4799baba570b1767ab86
|
|
||||||
timeCreated: 1682045865
|
|
@@ -19,6 +19,16 @@ public partial class ProtobufUtility
|
|||||||
((IMessage)message).WriteTo(stream);
|
((IMessage)message).WriteTo(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 消息压入内存流。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <param name="stream"></param>
|
||||||
|
public static void ToStream(object message, Stream stream)
|
||||||
|
{
|
||||||
|
((IMessage)message).WriteTo(stream);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 比特流解析。
|
/// 比特流解析。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 972ca4545003463d8710de956f0fde66
|
guid: 26e2c268a764bad4eb7412ad65f822e2
|
||||||
timeCreated: 1682047511
|
timeCreated: 1682047511
|
@@ -1,18 +0,0 @@
|
|||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 网络消息包基类。
|
|
||||||
/// </summary>
|
|
||||||
public abstract class Packet : IMemory
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 获取类型编号。
|
|
||||||
/// </summary>
|
|
||||||
public abstract int Id { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 清理引用。
|
|
||||||
/// </summary>
|
|
||||||
public abstract void Clear();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: b840df90fc73484d94cf06e87190f9e2
|
|
||||||
timeCreated: 1681994166
|
|
Reference in New Issue
Block a user