[增加]1. 增加网络组件

This commit is contained in:
AlianBlank 2024-04-10 18:25:06 +08:00
parent 2c9e2d4d42
commit 0e536bee95
111 changed files with 4601 additions and 1 deletions

8
Editor.meta Normal file
View File

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

View File

@ -0,0 +1,19 @@
{
"name": "GameFrameX.Network.Editor",
"references": [
"GameFrameX.Network.Runtime",
"GameFrameX.Editor",
"GameFrameX.Runtime"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1bf417993ca661a44b4e2c55c4908e7b
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Editor/Inspector.meta Normal file
View File

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

View File

@ -0,0 +1,71 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using GameFrameX.Editor;
using GameFrameX.Network.Runtime;
using UnityEditor;
using UnityEngine;
namespace GameFrameX.Network.Editor
{
[CustomEditor(typeof(NetworkComponent))]
internal sealed class NetworkComponentInspector : GameFrameworkInspector
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (!EditorApplication.isPlaying)
{
EditorGUILayout.HelpBox("Available during runtime only.", MessageType.Info);
return;
}
NetworkComponent t = (NetworkComponent)target;
if (IsPrefabInHierarchy(t.gameObject))
{
EditorGUILayout.LabelField("Network Channel Count", t.NetworkChannelCount.ToString());
INetworkChannel[] networkChannels = t.GetAllNetworkChannels();
foreach (INetworkChannel networkChannel in networkChannels)
{
DrawNetworkChannel(networkChannel);
}
}
Repaint();
}
private void DrawNetworkChannel(INetworkChannel networkChannel)
{
EditorGUILayout.BeginVertical("box");
{
EditorGUILayout.LabelField(networkChannel.Name, networkChannel.Connected ? "Connected" : "Disconnected");
// EditorGUILayout.LabelField("Service Type", networkChannel.ServiceType.ToString());
EditorGUILayout.LabelField("Address Family", networkChannel.AddressFamily.ToString());
EditorGUILayout.LabelField("Local Address", networkChannel.Connected ? networkChannel.Socket.LocalEndPoint.ToString() : "Unavailable");
EditorGUILayout.LabelField("Remote Address", networkChannel.Connected ? networkChannel.Socket.RemoteEndPoint.ToString() : "Unavailable");
EditorGUILayout.LabelField("Send Packet", Utility.Text.Format("{0} / {1}", networkChannel.SendPacketCount, networkChannel.SentPacketCount));
EditorGUILayout.LabelField("Receive Packet", Utility.Text.Format("{0} / {1}", networkChannel.ReceivePacketCount, networkChannel.ReceivedPacketCount));
EditorGUILayout.LabelField("Miss Heart Beat Count", networkChannel.MissHeartBeatCount.ToString());
EditorGUILayout.LabelField("Heart Beat", Utility.Text.Format("{0:F2} / {1:F2}", networkChannel.HeartBeatElapseSeconds, networkChannel.HeartBeatInterval));
EditorGUI.BeginDisabledGroup(!networkChannel.Connected);
{
if (GUILayout.Button("Disconnect"))
{
networkChannel.Close();
}
}
EditorGUI.EndDisabledGroup();
}
EditorGUILayout.EndVertical();
EditorGUILayout.Separator();
}
}
}

View File

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

201
LICENSE.md Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [2023] [ALianBlank of copyright owner][alianblank@outlook.com][https://alianblank.com/]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

7
LICENSE.md.meta Normal file
View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 572d9c8c818718f47a23fb9689821082
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1 +1,19 @@
# com.alianblank.gameframex.unity.network
## HOMEPAGE
GameFrameX 的 Network 长连接网络组件
**Network 长连接网络组件 (Network Component)** - 提供长连接网络组件相关的接口。
# 使用文档(文档编写于GPT4)
## 注意事项
# 使用方式(任选其一)
1. 直接在 `manifest.json` 的文件中的 `dependencies` 节点下添加以下内容
```json
{"com.alianblank.gameframex.network": "https://github.com/AlianBlank/com.alianblank.gameframex.unity.network.git"}
```
2. 在Unity 的`Packages Manager` 中使用`Git URL` 的方式添加库,地址为https://github.com/AlianBlank/com.alianblank.gameframex.unity.network.git
3. 直接下载仓库放置到Unity 项目的`Packages` 目录下。会自动加载识别

7
README.md.meta Normal file
View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: b4c88c16e8f340f43b01d901b61d9fd0
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Runtime.meta Normal file
View File

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

3
Runtime/EventArgs.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 689f25156dd5479bbc9e048eed1f05c3
timeCreated: 1712727663

View File

@ -0,0 +1,63 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using GameFrameX.Event.Runtime;
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络连接关闭事件。
/// </summary>
public sealed class NetworkClosedEventArgs : GameEventArgs
{
/// <summary>
/// 网络连接关闭事件编号。
/// </summary>
public static readonly string EventId = typeof(NetworkClosedEventArgs).FullName;
/// <summary>
/// 获取网络连接关闭事件编号。
/// </summary>
public override string Id
{
get { return EventId; }
}
/// <summary>
/// 初始化网络连接关闭事件的新实例。
/// </summary>
public NetworkClosedEventArgs()
{
NetworkChannel = null;
}
/// <summary>
/// 获取网络频道。
/// </summary>
public INetworkChannel NetworkChannel { get; private set; }
/// <summary>
/// 创建网络连接关闭事件。
/// </summary>
/// <param name="networkChannel">网络频道。</param>
/// <returns>创建的网络连接关闭事件。</returns>
public static NetworkClosedEventArgs Create(INetworkChannel networkChannel)
{
NetworkClosedEventArgs networkClosedEventArgs = ReferencePool.Acquire<NetworkClosedEventArgs>();
networkClosedEventArgs.NetworkChannel = networkChannel;
return networkClosedEventArgs;
}
/// <summary>
/// 清理网络连接关闭事件。
/// </summary>
public override void Clear()
{
NetworkChannel = null;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 73bf0753fd824f3c9b5a519141eac1e7
timeCreated: 1712727663

View File

@ -0,0 +1,72 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using GameFrameX.Event.Runtime;
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络连接成功事件。
/// </summary>
public sealed class NetworkConnectedEventArgs : GameEventArgs
{
/// <summary>
/// 网络连接成功事件编号。
/// </summary>
public static readonly string EventId = typeof(NetworkConnectedEventArgs).FullName;
/// <summary>
/// 获取网络连接成功事件编号。
/// </summary>
public override string Id
{
get { return EventId; }
}
/// <summary>
/// 初始化网络连接成功事件的新实例。
/// </summary>
public NetworkConnectedEventArgs()
{
NetworkChannel = null;
UserData = null;
}
/// <summary>
/// 获取网络频道。
/// </summary>
public INetworkChannel NetworkChannel { get; private set; }
/// <summary>
/// 获取用户自定义数据。
/// </summary>
public object UserData { get; private set; }
/// <summary>
/// 创建网络连接成功事件。
/// </summary>
/// <param name="networkChannel">网络频道。</param>
/// <param name="userData">用户自定义数据。</param>
/// <returns>创建的网络连接成功事件。</returns>
public static NetworkConnectedEventArgs Create(INetworkChannel networkChannel, object userData)
{
NetworkConnectedEventArgs networkConnectedEventArgs = ReferencePool.Acquire<NetworkConnectedEventArgs>();
networkConnectedEventArgs.NetworkChannel = networkChannel;
networkConnectedEventArgs.UserData = userData;
return networkConnectedEventArgs;
}
/// <summary>
/// 清理网络连接成功事件。
/// </summary>
public override void Clear()
{
NetworkChannel = null;
UserData = null;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 624ae69be674436a963446040fc8332d
timeCreated: 1712727663

View File

@ -0,0 +1,72 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using GameFrameX.Event.Runtime;
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 用户自定义网络错误事件。
/// </summary>
public sealed class NetworkCustomErrorEventArgs : GameEventArgs
{
/// <summary>
/// 用户自定义网络错误事件编号。
/// </summary>
public static readonly string EventId = typeof(NetworkCustomErrorEventArgs).FullName;
/// <summary>
/// 获取用户自定义网络错误事件编号。
/// </summary>
public override string Id
{
get { return EventId; }
}
/// <summary>
/// 初始化用户自定义网络错误事件的新实例。
/// </summary>
public NetworkCustomErrorEventArgs()
{
NetworkChannel = null;
CustomErrorData = null;
}
/// <summary>
/// 获取网络频道。
/// </summary>
public INetworkChannel NetworkChannel { get; private set; }
/// <summary>
/// 获取用户自定义错误数据。
/// </summary>
public object CustomErrorData { get; private set; }
/// <summary>
/// 创建用户自定义网络错误事件。
/// </summary>
/// <param name="networkChannel">网络频道。</param>
/// <param name="customErrorData">用户自定义错误数据。</param>
/// <returns>创建的用户自定义网络错误事件。</returns>
public static NetworkCustomErrorEventArgs Create(INetworkChannel networkChannel, object customErrorData)
{
NetworkCustomErrorEventArgs networkCustomErrorEventArgs = ReferencePool.Acquire<NetworkCustomErrorEventArgs>();
networkCustomErrorEventArgs.NetworkChannel = networkChannel;
networkCustomErrorEventArgs.CustomErrorData = customErrorData;
return networkCustomErrorEventArgs;
}
/// <summary>
/// 清理用户自定义网络错误事件。
/// </summary>
public override void Clear()
{
NetworkChannel = null;
CustomErrorData = null;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: edb2b3ba582d4fe9b20eb761b38c127e
timeCreated: 1712727663

View File

@ -0,0 +1,91 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using System.Net.Sockets;
using GameFrameX.Event.Runtime;
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络错误事件。
/// </summary>
public sealed class NetworkErrorEventArgs : GameEventArgs
{
/// <summary>
/// 网络错误事件编号。
/// </summary>
public static readonly string EventId = typeof(NetworkErrorEventArgs).FullName;
/// <summary>
/// 获取网络错误事件编号。
/// </summary>
public override string Id
{
get { return EventId; }
}
/// <summary>
/// 初始化网络错误事件的新实例。
/// </summary>
public NetworkErrorEventArgs()
{
NetworkChannel = null;
ErrorCode = NetworkErrorCode.Unknown;
SocketErrorCode = SocketError.Success;
ErrorMessage = null;
}
/// <summary>
/// 获取网络频道。
/// </summary>
public INetworkChannel NetworkChannel { get; private set; }
/// <summary>
/// 获取错误码。
/// </summary>
public NetworkErrorCode ErrorCode { get; private set; }
/// <summary>
/// 获取 Socket 错误码。
/// </summary>
public SocketError SocketErrorCode { get; private set; }
/// <summary>
/// 获取错误信息。
/// </summary>
public string ErrorMessage { get; private set; }
/// <summary>
/// 创建网络错误事件。
/// </summary>
/// <param name="networkChannel">网络频道。</param>
/// <param name="errorCode">错误码。</param>
/// <param name="socketErrorCode">Socket 错误码。</param>
/// <param name="errorMessage">错误信息。</param>
/// <returns>创建的网络错误事件。</returns>
public static NetworkErrorEventArgs Create(INetworkChannel networkChannel, NetworkErrorCode errorCode, SocketError socketErrorCode, string errorMessage)
{
NetworkErrorEventArgs networkErrorEventArgs = ReferencePool.Acquire<NetworkErrorEventArgs>();
networkErrorEventArgs.NetworkChannel = networkChannel;
networkErrorEventArgs.ErrorCode = errorCode;
networkErrorEventArgs.SocketErrorCode = socketErrorCode;
networkErrorEventArgs.ErrorMessage = errorMessage;
return networkErrorEventArgs;
}
/// <summary>
/// 清理网络错误事件。
/// </summary>
public override void Clear()
{
NetworkChannel = null;
ErrorCode = NetworkErrorCode.Unknown;
SocketErrorCode = SocketError.Success;
ErrorMessage = null;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 67320bf31d774067b0f7784c80d5958c
timeCreated: 1712727663

View File

@ -0,0 +1,72 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using GameFrameX.Event.Runtime;
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络心跳包丢失事件。
/// </summary>
public sealed class NetworkMissHeartBeatEventArgs : GameEventArgs
{
/// <summary>
/// 网络心跳包丢失事件编号。
/// </summary>
public static readonly string EventId = typeof(NetworkMissHeartBeatEventArgs).FullName;
/// <summary>
/// 获取网络心跳包丢失事件编号。
/// </summary>
public override string Id
{
get { return EventId; }
}
/// <summary>
/// 初始化网络心跳包丢失事件的新实例。
/// </summary>
public NetworkMissHeartBeatEventArgs()
{
NetworkChannel = null;
MissCount = 0;
}
/// <summary>
/// 获取网络频道。
/// </summary>
public INetworkChannel NetworkChannel { get; private set; }
/// <summary>
/// 获取心跳包已丢失次数。
/// </summary>
public int MissCount { get; private set; }
/// <summary>
/// 创建网络心跳包丢失事件。
/// </summary>
/// <param name="networkChannel">网络频道。</param>
/// <param name="missCount">心跳包已丢失次数。</param>
/// <returns>创建的网络心跳包丢失事件。</returns>
public static NetworkMissHeartBeatEventArgs Create(INetworkChannel networkChannel, int missCount)
{
NetworkMissHeartBeatEventArgs networkMissHeartBeatEventArgs = ReferencePool.Acquire<NetworkMissHeartBeatEventArgs>();
networkMissHeartBeatEventArgs.NetworkChannel = networkChannel;
networkMissHeartBeatEventArgs.MissCount = missCount;
return networkMissHeartBeatEventArgs;
}
/// <summary>
/// 清理网络心跳包丢失事件。
/// </summary>
public override void Clear()
{
NetworkChannel = null;
MissCount = 0;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 59bbd0035c1c4775b0d18cb3d6eba036
timeCreated: 1712727663

View File

@ -0,0 +1,30 @@
{
"name": "GameFrameX.Network.Runtime",
"references": [
"GameFrameX.Runtime",
"GameFrameX.Event.Runtime",
"UnityWebSocket.Runtime",
"ProtoBuffer.Runtime"
],
"rootNamespace": "GameFrameX.Network.Runtime",
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.alianblank.unitywebsocket",
"expression": "",
"define": "ENABLE_GAME_FRAME_X_WEB_SOCKET"
},
{
"name": "com.google.protobuf",
"expression": "",
"define": "ENABLE_PROTOBUF"
}
],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: bd1d88a7cb020ac4f8e204d3f5769b3c
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,48 @@
using UnityEngine;
namespace GameFrameX.Network.Runtime
{
public class GameFrameXNetworkCroppingHelper : MonoBehaviour
{
private void Start()
{
_ = typeof(DefaultNetworkChannelHelper);
_ = typeof(AddressFamily);
_ = typeof(DefaultPacketReceiveBodyHandler);
_ = typeof(DefaultPacketReceiveHeaderHandler);
_ = typeof(DefaultPacketSendBodyHandler);
_ = typeof(DefaultPacketSendHeaderHandler);
_ = typeof(INetworkChannel);
_ = typeof(INetworkChannelHelper);
_ = typeof(INetworkManager);
_ = typeof(INetworkSocket);
_ = typeof(IPacketHandler);
_ = typeof(IPacketHeartBeatHandler);
_ = typeof(IPacketReceiveBodyHandler);
_ = typeof(IPacketReceiveHeaderHandler);
_ = typeof(IPacketSendBodyHandler);
_ = typeof(IPacketSendHeaderHandler);
_ = typeof(IRequestMessage);
_ = typeof(IResponseMessage);
_ = typeof(MessagePackageObjectAttribute);
_ = typeof(MessageTypeHandlerAttribute);
_ = typeof(NetworkClosedEventArgs);
_ = typeof(NetworkConnectedEventArgs);
_ = typeof(NetworkCustomErrorEventArgs);
_ = typeof(NetworkErrorCode);
_ = typeof(NetworkErrorEventArgs);
_ = typeof(NetworkManager);
_ = typeof(NetworkManager.ConnectState);
_ = typeof(NetworkManager.HeartBeatState);
_ = typeof(NetworkManager.NetworkChannelBase);
_ = typeof(NetworkManager.ReceiveState);
_ = typeof(NetworkManager.SendState);
_ = typeof(NetworkMissHeartBeatEventArgs);
_ = typeof(Packet);
_ = typeof(PacketBase);
_ = typeof(ProtoMessageIdHandler);
_ = typeof(ServiceType);
_ = typeof(NetworkComponent);
}
}
}

View File

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

3
Runtime/Network.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7cb8a335950b4bd79eef61c85dd3c800
timeCreated: 1712727663

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2459d700cd854ff796dc0ad4bd1ab228
timeCreated: 1712727663

View File

@ -0,0 +1,9 @@
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 客户端发送给服务器的消息的基类接口
/// </summary>
public interface IRequestMessage
{
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cfff529910fc4bbe843b6598106526a8
timeCreated: 1712727663

View File

@ -0,0 +1,9 @@
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 服务器返回给客户端的消息基类接口
/// </summary>
public interface IResponseMessage
{
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 48caa362979748cda8b0dde9dad75c1a
timeCreated: 1712727663

View File

@ -0,0 +1,15 @@
#if ENABLE_PROTOBUF
using ProtoBuf;
#endif
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 消息基类
/// </summary>
#if ENABLE_PROTOBUF
[ProtoContract]
#endif
public class MessageObject
{
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3b92cbe17894435bbe4b5d2780c8ff0c
timeCreated: 1712727663

View File

@ -0,0 +1,35 @@
using System;
namespace GameFrameX.Network.Runtime
{
#if ENABLE_MESSAGEPACK
/// <summary>
/// 消息对象标签
/// </summary>
public class MessagePackageObjectAttribute : MessagePack.MessagePackObjectAttribute
{
public MessagePackageObjectAttribute() : base(true)
{
}
}
#elif ENABLE_PROTOBUF
/// <summary>
/// 消息对象标签
/// </summary>
public class MessagePackageObjectAttribute : Attribute
{
public MessagePackageObjectAttribute() : base()
{
}
}
#else
/// <summary>
/// 消息对象标签
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)]
public class MessagePackageObjectAttribute
{
public MessagePackageObjectAttribute() { }
}
#endif
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e16e4772b48a4d8eb6ced977051c7f58
timeCreated: 1712727663

View File

@ -0,0 +1,25 @@
using System;
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络消息处理器
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class MessageTypeHandlerAttribute : Attribute
{
/// <summary>
/// 消息ID,不能重复
/// </summary>
public int MessageId { get; }
/// <summary>
/// 网络消息处理器
/// </summary>
/// <param name="messageId">消息ID,不能重复</param>
public MessageTypeHandlerAttribute(int messageId)
{
MessageId = messageId;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6491314f168a45e68a5cafe5e6ad7ee5
timeCreated: 1712727663

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f15e40650d3847a59dde940a6daf5cad
timeCreated: 1712727663

View File

@ -0,0 +1,16 @@
using System.Buffers;
using GameFrameX.Runtime;
using ProtoBuf;
namespace GameFrameX.Network.Runtime
{
public sealed class DefaultPacketReceiveBodyHandler : IPacketReceiveBodyHandler, IPacketHandler
{
public bool Handler<T>(object source, int messageId, out T messageObject) where T : MessageObject
{
var messageType = ProtoMessageIdHandler.GetRespTypeById(messageId);
messageObject = (T)SerializerHelper.Deserialize(source as byte[], messageType);
return true;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 67720f87151c431c9b7b2b732dbc5718
timeCreated: 1712727663

View File

@ -0,0 +1,52 @@
using System;
using System.Buffers;
using GameFrameX.Runtime;
namespace GameFrameX.Network.Runtime
{
public sealed class DefaultPacketReceiveHeaderHandler : IPacketReceiveHeaderHandler, IPacketHandler
{
public int PacketLength { get; private set; }
public int Id { get; private set; }
public bool Handler(object source)
{
byte[] reader = source as byte[];
if (reader == null)
{
return false;
}
// packetLength
int offest = 0;
int packetLength = reader.ReadInt(ref offest); //4
PacketLength = packetLength;
// timestamp
long timestamp = reader.ReadLong(ref offest); //8
// MsgId
int msgId = reader.ReadInt(ref offest); //4
Id = msgId;
return true;
}
/// <summary>
/// 网络包长度
/// </summary>
private const int NetPacketLength = 4;
// 消息码
private const int NetCmdIdLength = 4;
// 消息时间戳
private const int NetTicketLength = 8;
public DefaultPacketReceiveHeaderHandler()
{
PacketHeaderLength = NetPacketLength + NetTicketLength + NetCmdIdLength;
}
public int PacketHeaderLength { get; }
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: abd509e066aa48f3822f2048efb12af9
timeCreated: 1712727663

View File

@ -0,0 +1,14 @@
using System.IO;
namespace GameFrameX.Network.Runtime
{
public sealed class DefaultPacketSendBodyHandler : IPacketSendBodyHandler, IPacketHandler
{
public bool Handler(byte[] messageBodyBuffer, MemoryStream cachedStream, Stream destination)
{
cachedStream.Write(messageBodyBuffer, 0, messageBodyBuffer.Length);
cachedStream.WriteTo(destination);
return true;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b96f0f08c4364b86ade3333d44fbcf35
timeCreated: 1712727663

View File

@ -0,0 +1,75 @@
using System.IO;
using GameFrameX.Runtime;
using ProtoBuf;
namespace GameFrameX.Network.Runtime
{
public sealed class DefaultPacketSendHeaderHandler : IPacketSendHeaderHandler, IPacketHandler
{
/// <summary>
/// 网络包长度
/// </summary>
private const int NetPacketLength = 4;
// 消息码
private const int NetCmdIdLength = 4;
// 排序
private const int NetOrderLength = 4;
// 消息时间戳
private const int NetTicketLength = 8;
public DefaultPacketSendHeaderHandler()
{
// 4 + 8 + 4 + 4
PacketHeaderLength = NetPacketLength + NetTicketLength + NetOrderLength + NetCmdIdLength;
_cachedByte = new byte[PacketHeaderLength];
}
/// <summary>
/// 消息包头长度
/// </summary>
public int PacketHeaderLength { get; }
/// <summary>
/// 获取网络消息包协议编号。
/// </summary>
public int Id { get; private set; }
/// <summary>
/// 获取网络消息包长度。
/// </summary>
public int PacketLength { get; private set; }
private const int Magic = 0x1234;
int count = 0;
private readonly byte[] _cachedByte;
public bool Handler<T>(T messageObject, MemoryStream cachedStream, out byte[] messageBodyBuffer) where T : MessageObject
{
cachedStream.Seek(0, SeekOrigin.Begin);
cachedStream.SetLength(0);
messageBodyBuffer = SerializerHelper.Serialize(messageObject);
var messageType = messageObject.GetType();
Id = ProtoMessageIdHandler.GetReqMessageIdByType(messageType);
var messageLength = messageBodyBuffer.Length;
PacketLength = PacketHeaderLength + messageLength;
int magic = Magic + ++count;
magic ^= Magic << 8;
magic ^= PacketLength;
int offset = 0;
_cachedByte.WriteInt(PacketLength, ref offset);
_cachedByte.WriteLong(GameTimeHelper.UnixTimeMilliseconds(), ref offset);
_cachedByte.WriteInt(Id, ref offset);
_cachedByte.WriteInt(messageLength, ref offset);
cachedStream.Write(_cachedByte, 0, PacketHeaderLength);
return true;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7fc1e6db3167413fbde9df4ce2f75d3d
timeCreated: 1712727663

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4558070d78b34c3ca6c144261757f1fa
timeCreated: 1712727663

View File

@ -0,0 +1,30 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络地址类型。
/// </summary>
public enum AddressFamily : byte
{
/// <summary>
/// 未知。
/// </summary>
Unknown = 0,
/// <summary>
/// IP 版本 4。
/// </summary>
IPv4,
/// <summary>
/// IP 版本 6。
/// </summary>
IPv6
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9a2b7b3d3d394ace93aa66899c567726
timeCreated: 1712727663

View File

@ -0,0 +1,160 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using System;
using System.Net;
using System.Net.Sockets;
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络频道接口。
/// </summary>
public interface INetworkChannel
{
/// <summary>
/// 获取网络频道名称。
/// </summary>
string Name { get; }
/// <summary>
/// 获取网络频道所使用的 Socket。
/// </summary>
INetworkSocket Socket { get; }
/// <summary>
/// 获取是否已连接。
/// </summary>
bool Connected { get; }
/// <summary>
/// 获取网络地址类型。
/// </summary>
AddressFamily AddressFamily { get; }
/// <summary>
/// 获取要发送的消息包数量。
/// </summary>
int SendPacketCount { get; }
/// <summary>
/// 获取累计发送的消息包数量。
/// </summary>
int SentPacketCount { get; }
/// <summary>
/// 获取已接收未处理的消息包数量。
/// </summary>
int ReceivePacketCount { get; }
/// <summary>
/// 获取累计已接收的消息包数量。
/// </summary>
int ReceivedPacketCount { get; }
/// <summary>
/// 获取或设置当收到消息包时是否重置心跳流逝时间。
/// </summary>
bool ResetHeartBeatElapseSecondsWhenReceivePacket { get; set; }
/// <summary>
/// 获取丢失心跳的次数。
/// </summary>
int MissHeartBeatCount { get; }
/// <summary>
/// 获取或设置心跳间隔时长,以秒为单位。
/// </summary>
float HeartBeatInterval { get; set; }
/// <summary>
/// 获取心跳等待时长,以秒为单位。
/// </summary>
float HeartBeatElapseSeconds { get; }
/// <summary>
/// 消息发送包头处理器
/// </summary>
IPacketSendHeaderHandler PacketSendHeaderHandler { get; }
/// <summary>
/// 消息发送内容处理器
/// </summary>
IPacketSendBodyHandler PacketSendBodyHandler { get; }
/// <summary>
/// 心跳消息处理器
/// </summary>
IPacketHeartBeatHandler PacketHeartBeatHandler { get; }
/// <summary>
/// 消息接收包头处理器
/// </summary>
IPacketReceiveHeaderHandler PacketReceiveHeaderHandler { get; }
/// <summary>
/// 消息接收内容处理器
/// </summary>
IPacketReceiveBodyHandler PacketReceiveBodyHandler { get; }
/// <summary>
/// 注册网络消息包处理函数。
/// </summary>
/// <param name="handler">要注册的网络消息包处理函数。</param>
void RegisterHandler(IPacketSendHeaderHandler handler);
/// <summary>
/// 注册网络消息包处理函数。
/// </summary>
/// <param name="handler">要注册的网络消息包处理函数。</param>
void RegisterHandler(IPacketSendBodyHandler handler);
/// <summary>
/// 注册网络消息包处理函数。
/// </summary>
/// <param name="handler">要注册的网络消息包处理函数。</param>
void RegisterHandler(IPacketReceiveHeaderHandler handler);
/// <summary>
/// 注册网络消息包处理函数。
/// </summary>
/// <param name="handler">要注册的网络消息包处理函数。</param>
void RegisterHandler(IPacketReceiveBodyHandler handler);
/// <summary>
/// 注册网络消息心跳处理函数,用于处理心跳消息
/// </summary>
/// <param name="handler">要注册的网络消息包处理函数</param>
void RegisterHandler(IPacketHeartBeatHandler handler);
/// <summary>
/// 设置默认事件处理函数。
/// </summary>
/// <param name="handler">要设置的默认事件处理函数。</param>
void SetDefaultHandler(EventHandler<Packet> handler);
/// <summary>
/// 连接到远程主机。
/// </summary>
/// <param name="ipAddress">远程主机的 IP 地址。</param>
/// <param name="port">远程主机的端口号。</param>
/// <param name="userData">用户自定义数据。</param>
void Connect(IPAddress ipAddress, int port, object userData = null);
/// <summary>
/// 关闭网络频道。
/// </summary>
void Close();
/// <summary>
/// 向远程主机发送消息包。
/// </summary>
/// <typeparam name="T">消息包类型。</typeparam>
/// <param name="messageObject">要发送的消息包。</param>
void Send<T>(T messageObject) where T : MessageObject;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d09e24362c0c4247b2e3bf09f3d3fb15
timeCreated: 1712727663

View File

@ -0,0 +1,75 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using System.Buffers;
using System.IO;
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络频道辅助器接口。
/// </summary>
public interface INetworkChannelHelper
{
/// <summary>
/// 初始化网络频道辅助器。
/// </summary>
/// <param name="networkChannel">网络频道。</param>
void Initialize(INetworkChannel networkChannel);
/// <summary>
/// 关闭并清理网络频道辅助器。
/// </summary>
void Shutdown();
/// <summary>
/// 准备进行连接。
/// </summary>
void PrepareForConnecting();
/// <summary>
/// 发送心跳消息包。
/// </summary>
/// <returns>是否发送心跳消息包成功。</returns>
bool SendHeartBeat();
/// <summary>
/// 序列化消息头
/// </summary>
/// <param name="messageObject"></param>
/// <param name="destination"></param>
/// <param name="messageBodyBuffer"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
bool SerializePacketHeader<T>(T messageObject, Stream destination, out byte[] messageBodyBuffer) where T : MessageObject;
/// <summary>
/// 序列化消息包。
/// </summary>
/// <param name="messageBodyBuffer">要序列化的消息包。</param>
/// <param name="destination">要序列化的目标流。</param>
/// <returns>是否序列化成功。</returns>
bool SerializePacketBody(byte[] messageBodyBuffer, Stream destination);
/// <summary>
/// 反序列化消息包头。
/// </summary>
/// <param name="source">要反序列化的来源流。</param>
/// <returns>反序列化后的消息包头。</returns>
bool DeserializePacketHeader(object source);
/// <summary>
/// 反序列化消息包。
/// </summary>
/// <param name="source">要反序列化的来源流。</param>
/// <param name="messageId">消息ID</param>
/// <param name="messageObject"></param>
/// <returns>反序列化后的消息包。</returns>
bool DeserializePacketBody(object source, int messageId, out MessageObject messageObject);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: af2db2c8580c4cbfbb3b0bdd2847c4c9
timeCreated: 1712727663

View File

@ -0,0 +1,89 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using System;
using System.Collections.Generic;
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络管理器接口。
/// </summary>
public interface INetworkManager
{
/// <summary>
/// 获取网络频道数量。
/// </summary>
int NetworkChannelCount { get; }
/// <summary>
/// 网络连接成功事件。
/// </summary>
event EventHandler<NetworkConnectedEventArgs> NetworkConnected;
/// <summary>
/// 网络连接关闭事件。
/// </summary>
event EventHandler<NetworkClosedEventArgs> NetworkClosed;
/// <summary>
/// 网络心跳包丢失事件。
/// </summary>
event EventHandler<NetworkMissHeartBeatEventArgs> NetworkMissHeartBeat;
/// <summary>
/// 网络错误事件。
/// </summary>
event EventHandler<NetworkErrorEventArgs> NetworkError;
/// <summary>
/// 用户自定义网络错误事件。
/// </summary>
event EventHandler<NetworkCustomErrorEventArgs> NetworkCustomError;
/// <summary>
/// 检查是否存在网络频道。
/// </summary>
/// <param name="channelName">网络频道名称。</param>
/// <returns>是否存在网络频道。</returns>
bool HasNetworkChannel(string channelName);
/// <summary>
/// 获取网络频道。
/// </summary>
/// <param name="channelName">网络频道名称。</param>
/// <returns>要获取的网络频道。</returns>
INetworkChannel GetNetworkChannel(string channelName);
/// <summary>
/// 获取所有网络频道。
/// </summary>
/// <returns>所有网络频道。</returns>
INetworkChannel[] GetAllNetworkChannels();
/// <summary>
/// 获取所有网络频道。
/// </summary>
/// <param name="results">所有网络频道。</param>
void GetAllNetworkChannels(List<INetworkChannel> results);
/// <summary>
/// 创建网络频道。
/// </summary>
/// <param name="channelName">网络频道名称。</param>
/// <param name="networkChannelHelper">网络频道辅助器。</param>
/// <returns>要创建的网络频道。</returns>
INetworkChannel CreateNetworkChannel(string channelName, INetworkChannelHelper networkChannelHelper);
/// <summary>
/// 销毁网络频道。
/// </summary>
/// <param name="channelName">网络频道名称。</param>
/// <returns>是否销毁网络频道成功。</returns>
bool DestroyNetworkChannel(string channelName);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6e5038d2f7c2458183a1982de7e8297f
timeCreated: 1712727663

View File

@ -0,0 +1,46 @@
using System.Net;
using System.Net.Sockets;
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络套接字接口。
/// </summary>
public interface INetworkSocket
{
/// <summary>
/// 获取是否已连接。
/// </summary>
bool IsConnected { get; }
/// <summary>
/// 获取本地终结点。
/// </summary>
EndPoint LocalEndPoint { get; }
/// <summary>
/// 获取远程终结点。
/// </summary>
EndPoint RemoteEndPoint { get; }
/// <summary>
/// 获取或设置接收缓冲区大小。
/// </summary>
int ReceiveBufferSize { get; set; }
/// <summary>
/// 获取或设置发送缓冲区大小。
/// </summary>
int SendBufferSize { get; set; }
/// <summary>
/// 关闭网络套接字。
/// </summary>
void Shutdown();
/// <summary>
/// 关闭网络套接字并释放资源。
/// </summary>
void Close();
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9ff2740bbabd40bda79fe61c1d59802f
timeCreated: 1712727663

View File

@ -0,0 +1,6 @@
namespace GameFrameX.Network.Runtime
{
public interface IPacketHandler
{
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1e27d90703174b8fa72a5ea5292b772b
timeCreated: 1712727663

View File

@ -0,0 +1,24 @@
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络消息心跳包接口。
/// </summary>
public interface IPacketHeartBeatHandler
{
/// <summary>
/// 每次心跳的间隔
/// </summary>
float HeartBeatInterval { get; }
/// <summary>
/// 几次心跳丢失。触发断开网络
/// </summary>
int MissHeartBeatCountByClose { get; }
/// <summary>
/// 处理器
/// </summary>
/// <returns></returns>
MessageObject Handler();
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ebf5176432074099ae1dca6c555f6332
timeCreated: 1712727663

View File

@ -0,0 +1,18 @@
using System.Buffers;
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络消息包处理器接口。
/// </summary>
public interface IPacketReceiveBodyHandler
{
/// <summary>
/// 网络消息包处理函数。
/// </summary>
/// <param name="source">网络消息包源。</param>
/// <param name="messageId">消息包标识,用于映射消息包</param>
/// <param name="messageObject"></param>
bool Handler<T>(object source,int messageId, out T messageObject) where T : MessageObject;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f8089c34f2c54e9cb9fd90b75d487c3c
timeCreated: 1712727663

View File

@ -0,0 +1,34 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using System.Buffers;
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络消息包头接口。
/// </summary>
public interface IPacketReceiveHeaderHandler
{
/// <summary>
/// 消息包头长度
/// </summary>
int PacketHeaderLength { get; }
/// <summary>
/// 获取网络消息包长度。
/// </summary>
int PacketLength { get; }
/// <summary>
/// 获取网络消息包协议编号。
/// </summary>
int Id { get; }
bool Handler(object source);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 095ef321684f44c4947309c5a156ad03
timeCreated: 1712727663

View File

@ -0,0 +1,26 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using System.IO;
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络消息包处理器接口。
/// </summary>
public interface IPacketSendBodyHandler
{
/// <summary>
///
/// </summary>
/// <param name="messageBodyBuffer"></param>
/// <param name="cachedStream"></param>
/// <param name="destination"></param>
/// <returns></returns>
bool Handler(byte[] messageBodyBuffer, MemoryStream cachedStream, Stream destination);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f78783fd0f4941e0a8e3aa2991e1aa67
timeCreated: 1712727663

View File

@ -0,0 +1,35 @@
using System.IO;
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络消息包头接口。
/// </summary>
public interface IPacketSendHeaderHandler
{
/// <summary>
/// 消息包头长度
/// </summary>
int PacketHeaderLength { get; }
/// <summary>
/// 获取网络消息包协议编号。
/// </summary>
int Id { get; }
/// <summary>
/// 获取网络消息包长度。
/// </summary>
int PacketLength { get; }
/// <summary>
/// 处理消息
/// </summary>
/// <param name="messageObject">消息对象</param>
/// <param name="cachedStream">缓存流</param>
/// <param name="messageBodyBuffer">消息序列化完的二进制数组</param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
bool Handler<T>(T messageObject, MemoryStream cachedStream, out byte[] messageBodyBuffer) where T : MessageObject;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ec256791814648aa82526abaf98189a9
timeCreated: 1712727663

View File

@ -0,0 +1,218 @@
using System;
using System.Buffers;
using System.IO;
using System.Reflection;
using GameFrameX;
using GameFrameX.Event;
using GameFrameX.Event.Runtime;
using GameFrameX.Network;
using GameFrameX.Runtime;
namespace GameFrameX.Network.Runtime
{
public class DefaultNetworkChannelHelper : INetworkChannelHelper, IReference
{
private MemoryStream _cachedStream;
private INetworkChannel _netChannel;
public DefaultNetworkChannelHelper()
{
// _s2cPacketTypes = new Dictionary<int, Type>();
_cachedStream = new MemoryStream(1024);
_netChannel = null;
}
/// <summary>
/// 获取事件组件。
/// </summary>
public EventComponent Event
{
get
{
if (_event == null)
{
_event = GameEntry.GetComponent<EventComponent>();
}
return _event;
}
}
private static EventComponent _event;
public void Initialize(INetworkChannel netChannel)
{
_netChannel = netChannel;
// 反射注册包和包处理函数。
var packetReceiveHeaderHandlerBaseType = typeof(IPacketReceiveHeaderHandler);
var packetReceiveBodyHandlerBaseType = typeof(IPacketReceiveBodyHandler);
var packetSendHeaderHandlerBaseType = typeof(IPacketSendHeaderHandler);
var packetSendBodyHandlerBaseType = typeof(IPacketSendBodyHandler);
var packetHeartBeatHandlerBaseType = typeof(IPacketHeartBeatHandler);
var packetHandlerBaseType = typeof(IPacketHandler);
var assembly = Assembly.GetExecutingAssembly();
var types = assembly.GetTypes();
foreach (var type in types)
{
if (!type.IsClass || type.IsAbstract)
{
continue;
}
if (!type.IsImplWithInterface(packetHandlerBaseType))
{
continue;
}
if (type.IsImplWithInterface(packetReceiveHeaderHandlerBaseType))
{
var packetHandler = (IPacketReceiveHeaderHandler)Activator.CreateInstance(type);
_netChannel.RegisterHandler(packetHandler);
}
else if (type.IsImplWithInterface(packetReceiveBodyHandlerBaseType))
{
var packetHandler = (IPacketReceiveBodyHandler)Activator.CreateInstance(type);
_netChannel.RegisterHandler(packetHandler);
}
else if (type.IsImplWithInterface(packetSendHeaderHandlerBaseType))
{
var packetHandler = (IPacketSendHeaderHandler)Activator.CreateInstance(type);
_netChannel.RegisterHandler(packetHandler);
}
else if (type.IsImplWithInterface(packetSendBodyHandlerBaseType))
{
var packetHandler = (IPacketSendBodyHandler)Activator.CreateInstance(type);
_netChannel.RegisterHandler(packetHandler);
}
else if (type.IsImplWithInterface(packetHeartBeatHandlerBaseType))
{
var packetHandler = (IPacketHeartBeatHandler)Activator.CreateInstance(type);
_netChannel.RegisterHandler(packetHandler);
}
}
Event.Subscribe(NetworkConnectedEventArgs.EventId, OnNetConnected);
Event.Subscribe(NetworkClosedEventArgs.EventId, OnNetClosed);
Event.Subscribe(NetworkMissHeartBeatEventArgs.EventId, OnNetMissHeartBeat);
Event.Subscribe(NetworkErrorEventArgs.EventId, OnNetError);
Event.Subscribe(NetworkConnectedEventArgs.EventId, OnNetCustomError);
}
public void Shutdown()
{
Event.Unsubscribe(NetworkConnectedEventArgs.EventId, OnNetConnected);
Event.Unsubscribe(NetworkClosedEventArgs.EventId, OnNetClosed);
Event.Unsubscribe(NetworkMissHeartBeatEventArgs.EventId, OnNetMissHeartBeat);
Event.Unsubscribe(NetworkErrorEventArgs.EventId, OnNetError);
Event.Unsubscribe(NetworkConnectedEventArgs.EventId, OnNetCustomError);
_netChannel = null;
}
public void PrepareForConnecting()
{
_netChannel.Socket.ReceiveBufferSize = 1024 * 1024 * 5;
_netChannel.Socket.SendBufferSize = 1024 * 1024 * 5;
}
public bool SendHeartBeat()
{
var message = _netChannel.PacketHeartBeatHandler.Handler();
_netChannel.Send(message);
return true;
}
public bool SerializePacketHeader<T>(T messageObject, Stream destination, out byte[] messageBodyBuffer) where T : MessageObject
{
GameFrameworkGuard.NotNull(_netChannel, nameof(_netChannel));
GameFrameworkGuard.NotNull(_netChannel.PacketSendHeaderHandler, nameof(_netChannel.PacketSendHeaderHandler));
GameFrameworkGuard.NotNull(messageObject, nameof(messageObject));
GameFrameworkGuard.NotNull(destination, nameof(destination));
return _netChannel.PacketSendHeaderHandler.Handler(messageObject, _cachedStream, out messageBodyBuffer);
}
public bool SerializePacketBody(byte[] messageBodyBuffer, Stream destination)
{
GameFrameworkGuard.NotNull(_netChannel, nameof(_netChannel));
GameFrameworkGuard.NotNull(_netChannel.PacketSendHeaderHandler, nameof(_netChannel.PacketSendHeaderHandler));
GameFrameworkGuard.NotNull(_netChannel.PacketSendBodyHandler, nameof(_netChannel.PacketSendBodyHandler));
GameFrameworkGuard.NotNull(messageBodyBuffer, nameof(messageBodyBuffer));
GameFrameworkGuard.NotNull(destination, nameof(destination));
return _netChannel.PacketSendBodyHandler.Handler(messageBodyBuffer, _cachedStream, destination);
}
public bool DeserializePacketHeader(object source)
{
GameFrameworkGuard.NotNull(source, nameof(source));
return _netChannel.PacketReceiveHeaderHandler.Handler(source);
}
public bool DeserializePacketBody(object source, int messageId, out MessageObject messageObject)
{
GameFrameworkGuard.NotNull(source, nameof(source));
return _netChannel.PacketReceiveBodyHandler.Handler(source, messageId, out messageObject);
}
public void Clear()
{
_cachedStream = null;
_netChannel = null;
}
private void OnNetConnected(object sender, GameEventArgs e)
{
var ne = e as NetworkConnectedEventArgs;
if (ne == null || ne.NetworkChannel != _netChannel)
{
return;
}
Log.Info("网络连接成功......");
}
private void OnNetClosed(object sender, GameEventArgs e)
{
var ne = e as NetworkClosedEventArgs;
if (ne == null || ne.NetworkChannel != _netChannel)
{
return;
}
Log.Info("网络连接关闭......");
}
private void OnNetMissHeartBeat(object sender, GameEventArgs e)
{
var ne = e as NetworkMissHeartBeatEventArgs;
if (ne == null || ne.NetworkChannel != _netChannel) return;
Log.Warning(Utility.Text.Format("Network channel '{0}' miss heart beat '{1}' times.", ne.NetworkChannel.Name, ne.MissCount));
// if (ne.MissCount < 2) return;
// ne.NetChannel.Close();
}
private void OnNetError(object sender, GameEventArgs e)
{
var ne = e as NetworkErrorEventArgs;
if (ne == null || ne.NetworkChannel != _netChannel)
{
return;
}
Log.Error(Utility.Text.Format("Network channel '{0}' error, error code is '{1}', error message is '{2}'.", ne.NetworkChannel.Name, ne.ErrorCode, ne.ErrorMessage));
//ne.NetworkChannel.Close();
}
private void OnNetCustomError(object sender, GameEventArgs e)
{
var ne = e as NetworkCustomErrorEventArgs;
if (ne == null || ne.NetworkChannel != _netChannel)
{
return;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 87137372edf04fdfaf39110a047ac72f
timeCreated: 1712727663

View File

@ -0,0 +1,60 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络错误码。
/// </summary>
public enum NetworkErrorCode : byte
{
/// <summary>
/// 未知错误。
/// </summary>
Unknown = 0,
/// <summary>
/// 地址族错误。
/// </summary>
AddressFamilyError,
/// <summary>
/// Socket 错误。
/// </summary>
SocketError,
/// <summary>
/// 连接错误。
/// </summary>
ConnectError,
/// <summary>
/// 发送错误。
/// </summary>
SendError,
/// <summary>
/// 接收错误。
/// </summary>
ReceiveError,
/// <summary>
/// 序列化错误。
/// </summary>
SerializeError,
/// <summary>
/// 反序列化消息包头错误。
/// </summary>
DeserializePacketHeaderError,
/// <summary>
/// 反序列化消息包错误。
/// </summary>
DeserializePacketError
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 356d13a0a41447c38f37eaa891861ce7
timeCreated: 1712727663

View File

@ -0,0 +1,40 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
namespace GameFrameX.Network.Runtime
{
public sealed partial class NetworkManager
{
public sealed class ConnectState
{
private readonly INetworkSocket _socket;
private readonly object _userData;
public ConnectState(INetworkSocket socket, object userData)
{
_socket = socket;
_userData = userData;
}
/// <summary>
/// Socket
/// </summary>
public INetworkSocket Socket
{
get { return _socket; }
}
/// <summary>
/// 用户自定义数据
/// </summary>
public object UserData
{
get { return _userData; }
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2ffa6d8aff2d402f9a993390ea8995de
timeCreated: 1712727663

View File

@ -0,0 +1,59 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
namespace GameFrameX.Network.Runtime
{
public sealed partial class NetworkManager
{
/// <summary>
/// 心跳状态
/// </summary>
public sealed class HeartBeatState
{
private float _heartBeatElapseSeconds;
private int _missHeartBeatCount;
public HeartBeatState()
{
_heartBeatElapseSeconds = 0f;
_missHeartBeatCount = 0;
}
/// <summary>
/// 心跳间隔时长
/// </summary>
public float HeartBeatElapseSeconds
{
get => _heartBeatElapseSeconds;
set => _heartBeatElapseSeconds = value;
}
/// <summary>
/// 心跳丢失次数
/// </summary>
public int MissHeartBeatCount
{
get => _missHeartBeatCount;
set => _missHeartBeatCount = value;
}
/// <summary>
/// 重置心跳数据=>保活
/// </summary>
/// <param name="resetHeartBeatElapseSeconds">是否重置心跳流逝时长</param>
public void Reset(bool resetHeartBeatElapseSeconds)
{
if (resetHeartBeatElapseSeconds)
{
_heartBeatElapseSeconds = 0f;
}
_missHeartBeatCount = 0;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: db7880af581d48f2a75f72e65ce55eda
timeCreated: 1712727663

View File

@ -0,0 +1,784 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using GameFrameX.Runtime;
namespace GameFrameX.Network.Runtime
{
public sealed partial class NetworkManager
{
/// <summary>
/// 网络频道基类。
/// </summary>
public abstract class NetworkChannelBase : INetworkChannel, IDisposable
{
private const float DefaultHeartBeatInterval = 30f;
private const int DefaultMissHeartBeatCountByClose = 10;
private readonly string _name;
protected readonly Queue<MessageObject> PSendPacketPool;
protected readonly EventPool<Packet> PReceivePacketPool;
protected readonly INetworkChannelHelper PNetworkChannelHelper;
protected AddressFamily PAddressFamily;
protected bool PResetHeartBeatElapseSecondsWhenReceivePacket;
protected float PHeartBeatInterval;
protected int MissHeartBeatCountByClose;
protected INetworkSocket PSocket;
protected readonly SendState PSendState;
protected readonly ReceiveState PReceiveState;
protected readonly HeartBeatState PHeartBeatState;
protected int PSentPacketCount;
protected int PReceivedPacketCount;
protected bool PActive
{
get => _pActive;
set
{
_pActive = value;
Log.Debug(value);
}
}
private bool _disposed;
private IPacketSendHeaderHandler _packetSendHeaderHandler;
private IPacketSendBodyHandler _packetSendBodyHandler;
private IPacketReceiveHeaderHandler _packetReceiveHeaderHandler;
private IPacketReceiveBodyHandler _packetReceiveBodyHandler;
private IPacketHeartBeatHandler _packetHeartBeatHandler;
public Action<NetworkChannelBase, object> NetworkChannelConnected;
public Action<NetworkChannelBase> NetworkChannelClosed;
public Action<NetworkChannelBase, int> NetworkChannelMissHeartBeat;
public Action<NetworkChannelBase, NetworkErrorCode, SocketError, string> NetworkChannelError;
public Action<NetworkChannelBase, object> NetworkChannelCustomError;
private bool _pActive;
/// <summary>
/// 初始化网络频道基类的新实例。
/// </summary>
/// <param name="name">网络频道名称。</param>
/// <param name="networkChannelHelper">网络频道辅助器。</param>
public NetworkChannelBase(string name, INetworkChannelHelper networkChannelHelper)
{
_name = name ?? string.Empty;
PSendPacketPool = new Queue<MessageObject>(128);
PReceivePacketPool = new EventPool<Packet>(EventPoolMode.Default);
PNetworkChannelHelper = networkChannelHelper;
PAddressFamily = AddressFamily.Unknown;
PResetHeartBeatElapseSecondsWhenReceivePacket = false;
PHeartBeatInterval = DefaultHeartBeatInterval;
MissHeartBeatCountByClose = DefaultMissHeartBeatCountByClose;
PSocket = null;
PSendState = new SendState();
PReceiveState = new ReceiveState();
PHeartBeatState = new HeartBeatState();
PSentPacketCount = 0;
PReceivedPacketCount = 0;
PActive = false;
_disposed = false;
NetworkChannelConnected = null;
NetworkChannelClosed = null;
NetworkChannelMissHeartBeat = null;
NetworkChannelError = null;
NetworkChannelCustomError = null;
networkChannelHelper.Initialize(this);
}
#region
/// <summary>
/// 获取网络频道名称。
/// </summary>
public string Name
{
get { return _name; }
}
/// <summary>
/// 获取网络频道所使用的 Socket。
/// </summary>
public INetworkSocket Socket
{
get { return PSocket; }
}
/// <summary>
/// 获取是否已连接。
/// </summary>
public bool Connected
{
get
{
if (PSocket != null)
{
return PSocket.IsConnected;
}
return false;
}
}
/// <summary>
/// 获取网络地址类型。
/// </summary>
public AddressFamily AddressFamily
{
get { return PAddressFamily; }
}
/// <summary>
/// 获取要发送的消息包数量。
/// </summary>
public int SendPacketCount
{
get
{
lock (PSendPacketPool)
{
return PSendPacketPool.Count;
}
}
}
/// <summary>
/// 获取累计发送的消息包数量。
/// </summary>
public int SentPacketCount
{
get { return PSentPacketCount; }
}
/// <summary>
/// 获取已接收未处理的消息包数量。
/// </summary>
public int ReceivePacketCount
{
get { return PReceivePacketPool.EventCount; }
}
/// <summary>
/// 获取累计已接收的消息包数量。
/// </summary>
public int ReceivedPacketCount
{
get { return PReceivedPacketCount; }
}
/// <summary>
/// 获取或设置当收到消息包时是否重置心跳流逝时间。
/// </summary>
public bool ResetHeartBeatElapseSecondsWhenReceivePacket
{
get { return PResetHeartBeatElapseSecondsWhenReceivePacket; }
set { PResetHeartBeatElapseSecondsWhenReceivePacket = value; }
}
/// <summary>
/// 获取丢失心跳的次数。
/// </summary>
public int MissHeartBeatCount
{
get
{
lock (PHeartBeatState)
{
return PHeartBeatState.MissHeartBeatCount;
}
}
}
/// <summary>
/// 获取或设置心跳间隔时长,以秒为单位。
/// </summary>
public float HeartBeatInterval
{
get { return PHeartBeatInterval; }
set { PHeartBeatInterval = value; }
}
/// <summary>
/// 获取心跳等待时长,以秒为单位。
/// </summary>
public float HeartBeatElapseSeconds
{
get
{
lock (PHeartBeatState)
{
return PHeartBeatState.HeartBeatElapseSeconds;
}
}
}
/// <summary>
/// 消息发送包头处理器
/// </summary>
public IPacketSendHeaderHandler PacketSendHeaderHandler
{
get { return _packetSendHeaderHandler; }
}
/// <summary>
/// 消息发送内容处理器
/// </summary>
public IPacketSendBodyHandler PacketSendBodyHandler
{
get { return _packetSendBodyHandler; }
}
/// <summary>
/// 消息接收包头处理器
/// </summary>
public IPacketReceiveHeaderHandler PacketReceiveHeaderHandler
{
get { return _packetReceiveHeaderHandler; }
}
/// <summary>
/// 心跳消息处理器
/// </summary>
public IPacketHeartBeatHandler PacketHeartBeatHandler
{
get { return _packetHeartBeatHandler; }
}
/// <summary>
/// 消息接收内容处理器
/// </summary>
public IPacketReceiveBodyHandler PacketReceiveBodyHandler
{
get { return _packetReceiveBodyHandler; }
}
#endregion
/// <summary>
/// 网络频道轮询。
/// </summary>
/// <param name="elapseSeconds">逻辑流逝时间,以秒为单位。</param>
/// <param name="realElapseSeconds">真实流逝时间,以秒为单位。</param>
public virtual void Update(float elapseSeconds, float realElapseSeconds)
{
if (PSocket == null || !PActive)
{
return;
}
ProcessSend();
ProcessReceive();
if (PSocket == null || !PActive)
{
return;
}
PReceivePacketPool.Update(elapseSeconds, realElapseSeconds);
ProcessHeartBeat(realElapseSeconds);
}
/// <summary>
/// 处理心跳
/// </summary>
/// <param name="realElapseSeconds"></param>
private void ProcessHeartBeat(float realElapseSeconds)
{
if (PHeartBeatInterval > 0f)
{
bool sendHeartBeat = false;
int missHeartBeatCount = 0;
lock (PHeartBeatState)
{
if (PSocket == null || !PActive)
{
return;
}
PHeartBeatState.HeartBeatElapseSeconds += realElapseSeconds;
if (PHeartBeatState.HeartBeatElapseSeconds >= PHeartBeatInterval)
{
sendHeartBeat = true;
missHeartBeatCount = PHeartBeatState.MissHeartBeatCount;
PHeartBeatState.HeartBeatElapseSeconds = 0f;
PHeartBeatState.MissHeartBeatCount++;
}
if (sendHeartBeat && PNetworkChannelHelper.SendHeartBeat())
{
if (missHeartBeatCount > 0 && NetworkChannelMissHeartBeat != null)
{
NetworkChannelMissHeartBeat(this, missHeartBeatCount);
}
// PHeartBeatState.Reset(this.ResetHeartBeatElapseSecondsWhenReceivePacket);
return;
}
if (PHeartBeatState.MissHeartBeatCount > MissHeartBeatCountByClose)
{
// 心跳丢失达到上线。触发断开
Close();
}
}
}
}
/// <summary>
/// 关闭网络频道。
/// </summary>
public virtual void Shutdown()
{
Close();
PSendState.Reset();
PReceivePacketPool.Shutdown();
PNetworkChannelHelper.Shutdown();
}
/// <summary>
/// 注册网络消息包处理函数。
/// </summary>
/// <param name="handler">要注册的网络消息包处理函数。</param>
public void RegisterHandler(IPacketSendHeaderHandler handler)
{
GameFrameworkGuard.NotNull(handler, nameof(handler));
_packetSendHeaderHandler = handler;
}
/// <summary>
/// 注册网络消息包处理函数。
/// </summary>
/// <param name="handler">要注册的网络消息包处理函数。</param>
public void RegisterHandler(IPacketSendBodyHandler handler)
{
GameFrameworkGuard.NotNull(handler, nameof(handler));
_packetSendBodyHandler = handler;
}
/// <summary>
/// 注册网络消息包处理函数。
/// </summary>
/// <param name="handler">要注册的网络消息包处理函数。</param>
public void RegisterHandler(IPacketReceiveHeaderHandler handler)
{
GameFrameworkGuard.NotNull(handler, nameof(handler));
_packetReceiveHeaderHandler = handler;
}
/// <summary>
/// 注册网络消息包处理函数。
/// </summary>
/// <param name="handler">要注册的网络消息包处理函数。</param>
public void RegisterHandler(IPacketReceiveBodyHandler handler)
{
GameFrameworkGuard.NotNull(handler, nameof(handler));
_packetReceiveBodyHandler = handler;
}
/// <summary>
/// 注册网络消息心跳处理函数,用于处理心跳消息
/// </summary>
/// <param name="handler">要注册的网络消息包处理函数</param>
public void RegisterHandler(IPacketHeartBeatHandler handler)
{
GameFrameworkGuard.NotNull(handler, nameof(handler));
_packetHeartBeatHandler = handler;
if (handler.HeartBeatInterval > 0)
{
PHeartBeatInterval = handler.HeartBeatInterval;
}
if (handler.MissHeartBeatCountByClose > 0)
{
MissHeartBeatCountByClose = handler.MissHeartBeatCountByClose;
}
}
/// <summary>
/// 设置默认事件处理函数。
/// </summary>
/// <param name="handler">要设置的默认事件处理函数。</param>
public void SetDefaultHandler(EventHandler<Packet> handler)
{
GameFrameworkGuard.NotNull(handler, nameof(handler));
PReceivePacketPool.SetDefaultHandler(handler);
}
/// <summary>
/// 连接到远程主机。
/// </summary>
/// <param name="ipAddress">远程主机的 IP 地址。</param>
/// <param name="port">远程主机的端口号。</param>
/// <param name="userData">用户自定义数据。</param>
public virtual void Connect(IPAddress ipAddress, int port, object userData = null)
{
if (PSocket != null)
{
Close();
PSocket = null;
}
switch (ipAddress.AddressFamily)
{
case System.Net.Sockets.AddressFamily.InterNetwork:
PAddressFamily = AddressFamily.IPv4;
break;
case System.Net.Sockets.AddressFamily.InterNetworkV6:
PAddressFamily = AddressFamily.IPv6;
break;
default:
string errorMessage = Utility.Text.Format("Not supported address family '{0}'.", ipAddress.AddressFamily);
if (NetworkChannelError != null)
{
NetworkChannelError(this, NetworkErrorCode.AddressFamilyError, SocketError.Success, errorMessage);
return;
}
throw new GameFrameworkException(errorMessage);
}
PSendState.Reset();
PReceiveState.PrepareForPacketHeader(0);
}
/// <summary>
/// 关闭连接并释放所有相关资源。
/// </summary>
public virtual void Close()
{
lock (this)
{
if (PSocket == null)
{
return;
}
PActive = false;
try
{
PSocket.Shutdown();
}
catch
{
}
finally
{
PSocket.Close();
PSocket = null;
if (NetworkChannelClosed != null)
{
NetworkChannelClosed(this);
}
}
PSentPacketCount = 0;
PReceivedPacketCount = 0;
lock (PSendPacketPool)
{
PSendPacketPool.Clear();
}
PReceivePacketPool.Clear();
lock (PHeartBeatState)
{
PHeartBeatState.Reset(true);
}
}
}
/// <summary>
/// 向远程主机发送消息包。
/// </summary>
/// <typeparam name="T">消息包类型。</typeparam>
/// <param name="messageObject">要发送的消息包。</param>
public void Send<T>(T messageObject) where T : MessageObject
{
if (PSocket == null)
{
const string errorMessage = "You must connect first.";
if (NetworkChannelError != null)
{
NetworkChannelError(this, NetworkErrorCode.SendError, SocketError.Success, errorMessage);
return;
}
throw new GameFrameworkException(errorMessage);
}
if (!PActive)
{
const string errorMessage = "Socket is not active.";
if (NetworkChannelError != null)
{
NetworkChannelError(this, NetworkErrorCode.SendError, SocketError.Success, errorMessage);
return;
}
throw new GameFrameworkException(errorMessage);
}
if (messageObject == null)
{
const string errorMessage = "Packet is invalid.";
if (NetworkChannelError != null)
{
NetworkChannelError(this, NetworkErrorCode.SendError, SocketError.Success, errorMessage);
return;
}
throw new GameFrameworkException(errorMessage);
}
lock (PSendPacketPool)
{
PSendPacketPool.Enqueue(messageObject);
}
}
/// <summary>
/// 释放资源。
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// 释放资源。
/// </summary>
/// <param name="disposing">释放资源标记。</param>
private void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
Close();
PSendState.Dispose();
PReceiveState.Dispose();
}
_disposed = true;
}
/// <summary>
/// 处理发送消息对象
/// </summary>
/// <param name="messageObject">消息对象</param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
protected virtual bool ProcessSendMessage(MessageObject messageObject)
{
bool serializeResult = PNetworkChannelHelper.SerializePacketHeader(messageObject, PSendState.Stream, out var messageBodyBuffer);
if (serializeResult)
{
serializeResult = PNetworkChannelHelper.SerializePacketBody(messageBodyBuffer, PSendState.Stream);
}
else
{
const string errorMessage = "Serialized packet failure.";
throw new InvalidOperationException(errorMessage);
}
return serializeResult;
}
/// <summary>
/// 处理消息发送
/// </summary>
/// <returns></returns>
/// <exception cref="GameFrameworkException"></exception>
protected virtual bool ProcessSend()
{
lock (PSendPacketPool)
{
if (PSendState.Stream.Length > 0 || PSendPacketPool.Count <= 0)
{
return false;
}
while (PSendPacketPool.Count > 0)
{
var messageObject = PSendPacketPool.Dequeue();
bool serializeResult = false;
try
{
serializeResult = ProcessSendMessage(messageObject);
#if UNITY_EDITOR
Log.Debug($"发送消息 ID:[{PacketSendHeaderHandler.Id}] ==>消息类型:{messageObject.GetType()} 消息内容:{Utility.Json.ToJson(messageObject)}");
#endif
}
catch (Exception exception)
{
PActive = false;
if (NetworkChannelError != null)
{
SocketException socketException = exception as SocketException;
NetworkChannelError(this, NetworkErrorCode.SerializeError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
return false;
}
throw;
}
if (!serializeResult)
{
const string errorMessage = "Serialized packet failure.";
if (NetworkChannelError != null)
{
NetworkChannelError(this, NetworkErrorCode.SerializeError, SocketError.Success, errorMessage);
return false;
}
throw new GameFrameworkException(errorMessage);
}
PSendState.Stream.SetLength(0);
}
PSendState.Stream.Position = 0L;
return true;
}
}
protected virtual void ProcessReceive()
{
}
/*
/// <summary>
/// 处理发送消息对象
/// </summary>
/// <param name="messageObject">消息对象</param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
protected virtual bool ProcessReceiveMessage(MessageObject messageObject)
{
bool serializeResult = PNetworkChannelHelper.SerializePacketHeader(messageObject, PSendState.Stream, out var messageBodyBuffer);
if (serializeResult)
{
serializeResult = PNetworkChannelHelper.SerializePacketBody(messageBodyBuffer, PSendState.Stream);
}
else
{
const string errorMessage = "Serialized packet failure.";
throw new InvalidOperationException(errorMessage);
}
return serializeResult;
}*/
protected virtual bool ProcessPacketHeader()
{
try
{
// var packetHeader = PNetworkChannelHelper.DeserializePacketHeader(PReceiveState.Stream, out var customErrorData);
//
// if (customErrorData != null && NetworkChannelCustomError != null)
// {
// NetworkChannelCustomError(this, customErrorData);
// }
//
// if (packetHeader == null)
// {
// string errorMessage = "Packet header is invalid.";
// if (NetworkChannelError != null)
// {
// NetworkChannelError(this, NetworkErrorCode.DeserializePacketHeaderError, SocketError.Success, errorMessage);
// return false;
// }
//
// throw new GameFrameworkException(errorMessage);
// }
//
// PReceiveState.PrepareForPacket(PacketReceiveHeaderHandler, PacketReceiveBodyHandler);
// if (packetHeader.PacketLength <= 0)
// {
// bool processSuccess = ProcessPacket();
// PReceivedPacketCount++;
// return processSuccess;
// }
}
catch (Exception exception)
{
PActive = false;
if (NetworkChannelError != null)
{
SocketException socketException = exception as SocketException;
NetworkChannelError(this, NetworkErrorCode.DeserializePacketHeaderError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
return false;
}
throw;
}
return true;
}
protected virtual bool ProcessPacket()
{
lock (PHeartBeatState)
{
PHeartBeatState.Reset(PResetHeartBeatElapseSecondsWhenReceivePacket);
}
try
{
var packet = PNetworkChannelHelper.DeserializePacketBody(PReceiveState.Stream, 0, out var customErrorData);
if (customErrorData != null && NetworkChannelCustomError != null)
{
NetworkChannelCustomError(this, customErrorData);
}
if (packet)
{
PReceivePacketPool.Fire(this, null);
}
PReceiveState.PrepareForPacketHeader(0);
}
catch (Exception exception)
{
PActive = false;
if (NetworkChannelError != null)
{
SocketException socketException = exception as SocketException;
NetworkChannelError(this, NetworkErrorCode.DeserializePacketError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
return false;
}
throw;
}
return true;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8eb9cef7207c44569ba604eeb5d32899
timeCreated: 1712727663

View File

@ -0,0 +1,105 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using System;
using System.IO;
namespace GameFrameX.Network.Runtime
{
public sealed partial class NetworkManager
{
public sealed class ReceiveState : IDisposable
{
private const int DefaultBufferLength = 1024 * 64;
private MemoryStream _mStream;
private IPacketReceiveHeaderHandler _packetReceiveHeaderHandler;
private IPacketReceiveBodyHandler _packetReceiveBodyHandler;
private bool _disposed;
public ReceiveState()
{
_mStream = new MemoryStream(DefaultBufferLength);
_packetReceiveHeaderHandler = null;
_packetReceiveBodyHandler = null;
_disposed = false;
}
public MemoryStream Stream
{
get { return _mStream; }
}
public IPacketReceiveHeaderHandler PacketHeaderHandler
{
get { return _packetReceiveHeaderHandler; }
}
public IPacketReceiveBodyHandler PacketBodyHandler
{
get { return _packetReceiveBodyHandler; }
}
public void PrepareForPacketHeader(int packetHeaderLength)
{
Reset(packetHeaderLength, null, null);
}
public void PrepareForPacket(IPacketReceiveHeaderHandler packetHeader, IPacketReceiveBodyHandler packetBody)
{
if (packetHeader == null)
{
throw new ArgumentNullException(nameof(packetHeader), "Packet header is invalid.");
}
if (packetBody == null)
{
throw new ArgumentNullException(nameof(packetBody), "Packet body is invalid.");
}
Reset(packetHeader.PacketLength, packetHeader, packetBody);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
if (_mStream != null)
{
_mStream.Dispose();
_mStream = null;
}
}
_disposed = true;
}
private void Reset(int targetLength, IPacketReceiveHeaderHandler packetReceiveHeaderHandler, IPacketReceiveBodyHandler packetReceiveBodyHandler)
{
if (targetLength < 0)
{
throw new GameFrameworkException("Target length is invalid.");
}
_mStream.Position = 0L;
_mStream.SetLength(targetLength);
_packetReceiveHeaderHandler = packetReceiveHeaderHandler;
_packetReceiveBodyHandler = packetReceiveBodyHandler;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d8f189af0c3b40328bec4eff82a654b7
timeCreated: 1712727663

View File

@ -0,0 +1,64 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using System;
using System.IO;
namespace GameFrameX.Network.Runtime
{
public sealed partial class NetworkManager
{
public sealed class SendState : IDisposable
{
private const int DefaultBufferLength = 1024 * 64;
private MemoryStream _stream;
private bool _disposed;
public SendState()
{
_stream = new MemoryStream(DefaultBufferLength);
_disposed = false;
}
public MemoryStream Stream
{
get { return _stream; }
}
public void Reset()
{
_stream.Position = 0L;
_stream.SetLength(0L);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
if (_stream != null)
{
_stream.Dispose();
_stream = null;
}
}
_disposed = true;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ca29f173c8354c0a829aa16e1dca3430
timeCreated: 1712727663

View File

@ -0,0 +1,109 @@
/*using System;
using System.Net;
using System.Net.Sockets;
namespace GameFrameX.Network.Runtime
{
public partial class NetworkManager
{
private sealed class SystemNetSocket : INetworkSocket
{
private readonly Socket _socket;
public SystemNetSocket(System.Net.Sockets.AddressFamily ipAddressAddressFamily, SocketType socketType, ProtocolType protocolType)
{
_socket = new Socket(ipAddressAddressFamily, socketType, protocolType);
}
public bool IsConnected
{
get { return _socket.Connected; }
}
public Socket Socket
{
get { return _socket; }
}
public EndPoint LocalEndPoint
{
get { return _socket.LocalEndPoint; }
}
public EndPoint RemoteEndPoint
{
get { return _socket.RemoteEndPoint; }
}
public int Available
{
get { return _socket.Available; }
}
public int ReceiveBufferSize
{
get { return _socket.ReceiveBufferSize; }
set
{
if (value <= 0)
{
throw new ArgumentException("Receive buffer size is invalid.", nameof(value));
}
_socket.ReceiveBufferSize = value;
}
}
public int SendBufferSize
{
get { return _socket.SendBufferSize; }
set
{
if (value <= 0)
{
throw new ArgumentException("Send buffer size is invalid.", nameof(value));
}
_socket.SendBufferSize = value;
}
}
public void Shutdown()
{
_socket.Shutdown(SocketShutdown.Both);
}
public void Close()
{
_socket.Close();
}
public void BeginSend(byte[] getBuffer, int streamPosition, int streamLength, SocketFlags none, AsyncCallback mSendCallback, INetworkSocket mSocket)
{
_socket.BeginSend(getBuffer, streamPosition, streamLength, none, mSendCallback, mSocket);
}
public void BeginReceive(byte[] getBuffer, int streamPosition, int streamLength, SocketFlags none, AsyncCallback mReceiveCallback, INetworkSocket mSocket)
{
_socket.BeginReceive(getBuffer, streamPosition, streamLength, none, mReceiveCallback, mSocket);
}
public void BeginConnect(IPAddress ipAddress, int port, AsyncCallback mConnectCallback, ConnectState connectState)
{
_socket.BeginConnect(ipAddress, port, mConnectCallback, connectState);
}
public void EndConnect(IAsyncResult ar)
{
_socket.EndConnect(ar);
}
public int Receive(byte[] getBuffer, int streamPosition, int streamLength, SocketFlags none)
{
return _socket.Receive(getBuffer, streamPosition, streamLength, none);
}
}
}
}*/

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a97b60f62dc6440f88f8bc6e6b0cbd12
timeCreated: 1712727663

View File

@ -0,0 +1,279 @@
/*//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using System;
using System.Net;
using System.Net.Sockets;
namespace GameFrameX.Network.Runtime
{
public sealed partial class NetworkManager
{
/// <summary>
/// TCP 网络频道。
/// </summary>
private sealed class TcpNetworkChannel : NetworkChannelBase
{
private readonly AsyncCallback _connectCallback;
private readonly AsyncCallback _sendCallback;
private readonly AsyncCallback _receiveCallback;
/// <summary>
/// 初始化网络频道的新实例。
/// </summary>
/// <param name="name">网络频道名称。</param>
/// <param name="networkChannelHelper">网络频道辅助器。</param>
public TcpNetworkChannel(string name, INetworkChannelHelper networkChannelHelper)
: base(name, networkChannelHelper)
{
_connectCallback = ConnectCallback;
_sendCallback = SendCallback;
_receiveCallback = ReceiveCallback;
}
/// <summary>
/// 连接到远程主机。
/// </summary>
/// <param name="ipAddress">远程主机的 IP 地址。</param>
/// <param name="port">远程主机的端口号。</param>
/// <param name="userData">用户自定义数据。</param>
public override void Connect(IPAddress ipAddress, int port, object userData = null)
{
base.Connect(ipAddress, port, userData);
PSocket = new SystemNetSocket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
if (PSocket == null)
{
string errorMessage = "Initialize network channel failure.";
if (NetworkChannelError != null)
{
NetworkChannelError(this, NetworkErrorCode.SocketError, SocketError.Success, errorMessage);
return;
}
throw new GameFrameworkException(errorMessage);
}
PNetworkChannelHelper.PrepareForConnecting();
ConnectAsync(ipAddress, port, userData);
}
protected override bool ProcessSend()
{
if (base.ProcessSend())
{
SendAsync();
return true;
}
return false;
}
private void ConnectAsync(IPAddress ipAddress, int port, object userData)
{
try
{
((SystemNetSocket)PSocket).BeginConnect(ipAddress, port, _connectCallback, new ConnectState(PSocket, userData));
}
catch (Exception exception)
{
if (NetworkChannelError != null)
{
SocketException socketException = exception as SocketException;
NetworkChannelError(this, NetworkErrorCode.ConnectError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
return;
}
throw;
}
}
private void ConnectCallback(IAsyncResult ar)
{
ConnectState socketUserData = (ConnectState)ar.AsyncState;
try
{
((SystemNetSocket)socketUserData.Socket).EndConnect(ar);
}
catch (ObjectDisposedException)
{
return;
}
catch (Exception exception)
{
PActive = false;
if (NetworkChannelError != null)
{
SocketException socketException = exception as SocketException;
NetworkChannelError(this, NetworkErrorCode.ConnectError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
return;
}
throw;
}
PSentPacketCount = 0;
PReceivedPacketCount = 0;
lock (PSendPacketPool)
{
PSendPacketPool.Clear();
}
PReceivePacketPool.Clear();
lock (PHeartBeatState)
{
PHeartBeatState.Reset(true);
}
if (NetworkChannelConnected != null)
{
NetworkChannelConnected(this, socketUserData.UserData);
}
PActive = true;
ReceiveAsync();
}
private void SendAsync()
{
try
{
((SystemNetSocket)PSocket).BeginSend(PSendState.Stream.GetBuffer(), (int)PSendState.Stream.Position, (int)(PSendState.Stream.Length - PSendState.Stream.Position), SocketFlags.None, _sendCallback, PSocket);
}
catch (Exception exception)
{
PActive = false;
if (NetworkChannelError != null)
{
SocketException socketException = exception as SocketException;
NetworkChannelError(this, NetworkErrorCode.SendError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
return;
}
throw;
}
}
private void SendCallback(IAsyncResult ar)
{
Socket socket = ((SystemNetSocket)ar.AsyncState).Socket;
if (!socket.Connected)
{
return;
}
int bytesSent = 0;
try
{
bytesSent = socket.EndSend(ar);
}
catch (Exception exception)
{
PActive = false;
if (NetworkChannelError != null)
{
SocketException socketException = exception as SocketException;
NetworkChannelError(this, NetworkErrorCode.SendError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
return;
}
throw;
}
PSendState.Stream.Position += bytesSent;
if (PSendState.Stream.Position < PSendState.Stream.Length)
{
SendAsync();
return;
}
PSentPacketCount++;
PSendState.Reset();
}
private void ReceiveAsync()
{
try
{
((SystemNetSocket)PSocket).BeginReceive(PReceiveState.Stream.GetBuffer(), (int)PReceiveState.Stream.Position, (int)(PReceiveState.Stream.Length - PReceiveState.Stream.Position), SocketFlags.None, _receiveCallback, PSocket);
}
catch (Exception exception)
{
PActive = false;
if (NetworkChannelError != null)
{
SocketException socketException = exception as SocketException;
NetworkChannelError(this, NetworkErrorCode.ReceiveError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
return;
}
throw;
}
}
private void ReceiveCallback(IAsyncResult ar)
{
Socket socket = ((INetworkSocket)ar.AsyncState).Socket;
if (!socket.Connected)
{
return;
}
int bytesReceived = 0;
try
{
bytesReceived = socket.EndReceive(ar);
}
catch (Exception exception)
{
PActive = false;
if (NetworkChannelError != null)
{
SocketException socketException = exception as SocketException;
NetworkChannelError(this, NetworkErrorCode.ReceiveError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
return;
}
throw;
}
if (bytesReceived <= 0)
{
Close();
return;
}
PReceiveState.Stream.Position += bytesReceived;
if (PReceiveState.Stream.Position < PReceiveState.Stream.Length)
{
ReceiveAsync();
return;
}
PReceiveState.Stream.Position = 0L;
bool processSuccess = false;
if (PReceiveState.PacketHeaderHandler != null)
{
processSuccess = ProcessPacket();
PReceivedPacketCount++;
}
else
{
processSuccess = ProcessPacketHeader();
}
if (processSuccess)
{
ReceiveAsync();
return;
}
}
}
}
}*/

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3e1854e4c28b4ade9bb03f60cda5d650
timeCreated: 1712727663

View File

@ -0,0 +1,99 @@
#if ENABLE_GAME_FRAME_X_WEB_SOCKET
using System;
using System.Net;
using System.Threading.Tasks;
using UnityWebSocket;
namespace GameFrameX.Network.Runtime
{
public partial class NetworkManager
{
private sealed class WebSocketClientNetSocket : INetworkSocket
{
private readonly IWebSocket _client;
/// <summary>
/// 是否是加密协议
/// </summary>
private readonly bool IsSSL = false;
TaskCompletionSource<bool> _connectTask = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
private Action<byte[]> _action;
public WebSocketClientNetSocket(IPAddress ipAddress, int port, Action<byte[]> action)
{
_client = new WebSocket("ws://" + ipAddress + ":" + port + "/" + (IsSSL ? "wss" : "ws"));
_action = action;
_client.OnOpen += OnOpen;
_client.OnError += OnError;
_client.OnClose += OnClose;
_client.OnMessage += OnMessage;
}
private void OnMessage(object sender, MessageEventArgs e)
{
if (e.IsBinary)
{
_action.Invoke(e.RawData);
}
}
private void OnClose(object sender, CloseEventArgs e)
{
}
private void OnError(object sender, ErrorEventArgs e)
{
_connectTask.TrySetResult(false);
}
private void OnOpen(object sender, OpenEventArgs e)
{
_connectTask.TrySetResult(true);
}
public async Task ConnectAsync()
{
_connectTask = new TaskCompletionSource<bool>();
_client.ConnectAsync();
await _connectTask.Task;
}
public IWebSocket Client
{
get { return _client; }
}
public bool IsConnected
{
get { return _client.IsConnected; }
}
public EndPoint LocalEndPoint
{
get { return null; }
}
public EndPoint RemoteEndPoint
{
get { return null; }
}
public int ReceiveBufferSize { get; set; }
public int SendBufferSize { get; set; }
public void Shutdown()
{
_client.CloseAsync();
}
public void Close()
{
_client.CloseAsync();
}
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7fca9918ccef459e80f58cee2259ceac
timeCreated: 1712727663

View File

@ -0,0 +1,289 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
#if ENABLE_GAME_FRAME_X_WEB_SOCKET
using System;
using System.Buffers;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using GameFrameX.Runtime;
namespace GameFrameX.Network.Runtime
{
public sealed partial class NetworkManager
{
/// <summary>
/// TCP 网络频道。
/// </summary>
private sealed class WebSocketNetworkChannel : NetworkChannelBase
{
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
/// <summary>
/// 初始化网络频道的新实例。
/// </summary>
/// <param name="name">网络频道名称。</param>
/// <param name="networkChannelHelper">网络频道辅助器。</param>
public WebSocketNetworkChannel(string name, INetworkChannelHelper networkChannelHelper)
: base(name, networkChannelHelper)
{
}
/// <summary>
/// 连接到远程主机。
/// </summary>
/// <param name="ipAddress">远程主机的 IP 地址。</param>
/// <param name="port">远程主机的端口号。</param>
/// <param name="userData">用户自定义数据。</param>
public override void Connect(IPAddress ipAddress, int port, object userData = null)
{
base.Connect(ipAddress, port, userData);
PSocket = new WebSocketClientNetSocket(ipAddress, port, ReceiveCallback);
if (PSocket == null)
{
const string errorMessage = "Initialize network channel failure.";
if (NetworkChannelError != null)
{
NetworkChannelError(this, NetworkErrorCode.SocketError, SocketError.Success, errorMessage);
return;
}
throw new GameFrameworkException(errorMessage);
}
PNetworkChannelHelper.PrepareForConnecting();
ConnectAsync(userData);
}
public override void Close()
{
base.Close();
_cancellationTokenSource.Cancel();
}
private bool IsClose()
{
return _cancellationTokenSource.IsCancellationRequested;
}
/// <summary>
/// 处理发送消息对象
/// </summary>
/// <param name="messageObject">消息对象</param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
protected override bool ProcessSendMessage(MessageObject messageObject)
{
if (IsClose())
{
PActive = false;
if (NetworkChannelError != null)
{
NetworkChannelError(this, NetworkErrorCode.SocketError, SocketError.Disconnecting, "Network channel is closing.");
}
return false;
}
bool serializeResult = base.ProcessSendMessage(messageObject);
if (serializeResult)
{
// TODO 效率不高
var tcpClientNetSocket = (WebSocketClientNetSocket)PSocket;
byte[] buffer = new byte[PSendState.Stream.Length];
PSendState.Stream.Seek(0, SeekOrigin.Begin);
_ = PSendState.Stream.Read(buffer, 0, buffer.Length);
tcpClientNetSocket.Client.SendAsync(buffer);
}
else
{
const string errorMessage = "Serialized packet failure.";
throw new InvalidOperationException(errorMessage);
}
return true;
}
private async void ConnectAsync(object userData)
{
try
{
var socketClient = (WebSocketClientNetSocket)PSocket;
await socketClient.ConnectAsync();
ConnectCallback(new ConnectState(PSocket, userData));
}
catch (Exception exception)
{
if (NetworkChannelError != null)
{
SocketException socketException = exception as SocketException;
NetworkChannelError(this, NetworkErrorCode.ConnectError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
return;
}
throw;
}
}
private void ConnectCallback(ConnectState connectState)
{
try
{
var socketUserData = (WebSocketClientNetSocket)PSocket;
if (!socketUserData.IsConnected)
{
throw new SocketException((int)NetworkErrorCode.ConnectError);
}
}
catch (ObjectDisposedException)
{
return;
}
catch (Exception exception)
{
PActive = false;
if (NetworkChannelError != null)
{
SocketException socketException = exception as SocketException;
NetworkChannelError(this, NetworkErrorCode.ConnectError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
return;
}
throw;
}
PSentPacketCount = 0;
PReceivedPacketCount = 0;
lock (PSendPacketPool)
{
PSendPacketPool.Clear();
}
PReceivePacketPool.Clear();
lock (PHeartBeatState)
{
PHeartBeatState.Reset(true);
}
if (NetworkChannelConnected != null)
{
NetworkChannelConnected(this, connectState.UserData);
}
PActive = true;
}
private async void ReceiveCallback(byte[] buffer)
{
// PReceiveState.Stream.Seek(0, SeekOrigin.Begin);
// PReceiveState.Stream.Write(buffer, 0, buffer.Length);
// if (PReceiveState.Stream.Position < PReceiveState.Stream.Length)
// {
// return;
// }
// PReceiveState.Stream.Position = 0L;
ProcessReceiveMessage(ref buffer);
// bool processSuccess = false;
// if (PReceiveState.PacketHeaderHandler != null)
// {
// processSuccess = ProcessPacket();
// PReceivedPacketCount++;
// }
// else
// {
// processSuccess = ProcessPacketHeader();
// }
}
private bool ProcessReceiveMessage(ref byte[] buffer)
{
try
{
lock (PHeartBeatState)
{
PHeartBeatState.Reset(PResetHeartBeatElapseSecondsWhenReceivePacket);
}
PReceivedPacketCount++;
if (buffer.Length < PacketReceiveHeaderHandler.PacketHeaderLength)
{
return false;
}
// var header = buffer.Slice(buffer.Start, PacketReceiveHeaderHandler.PacketHeaderLength);
// buffer = buffer.Slice(PacketReceiveHeaderHandler.PacketHeaderLength);
var result = PNetworkChannelHelper.DeserializePacketHeader(buffer);
if (result)
{
var bodyLength = PacketReceiveHeaderHandler.PacketLength - PacketReceiveHeaderHandler.PacketHeaderLength;
if (buffer.Length < bodyLength)
{
return false;
}
var body = new byte[bodyLength - 4];
Array.Copy(buffer, PacketReceiveHeaderHandler.PacketHeaderLength, body, 0, bodyLength-4); //buffer.Slice(buffer.Start, bodyLength);
// var bodyFirst = body.First.ToArray();
StringBuilder stringBuilder = new StringBuilder();
// foreach (byte b in bodyFirst)
// {
// stringBuilder.Append(b + " ");
// }
// buffer = buffer.Slice(bodyLength);
result = PNetworkChannelHelper.DeserializePacketBody(body, PacketReceiveHeaderHandler.Id, out var messageObject);
#if UNITY_EDITOR
Log.Debug($"收到消息 ID:[{PacketReceiveHeaderHandler.Id}] ==>消息类型:{messageObject.GetType()} 消息内容:{Utility.Json.ToJson(messageObject)}");
#endif
if (!result)
{
if (NetworkChannelError != null)
{
NetworkChannelError(this, NetworkErrorCode.DeserializePacketError, SocketError.Success, "Packet body is invalid.");
return false;
}
}
PacketBase packetBase = ReferencePool.Acquire<PacketBase>();
packetBase.MessageObject = messageObject;
packetBase.MessageId = PacketReceiveHeaderHandler.Id;
PReceivePacketPool.Fire(this, packetBase);
}
else
{
if (NetworkChannelError != null)
{
NetworkChannelError(this, NetworkErrorCode.DeserializePacketHeaderError, SocketError.Success, "Packet header is invalid.");
return false;
}
}
return result;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8de67fed18384b41a07efc1737296356
timeCreated: 1712727663

View File

@ -0,0 +1,295 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Net.Sockets;
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络管理器。
/// </summary>
public sealed partial class NetworkManager : GameFrameworkModule, INetworkManager
{
private readonly Dictionary<string, NetworkChannelBase> m_NetworkChannels;
private EventHandler<NetworkConnectedEventArgs> m_NetworkConnectedEventHandler;
private EventHandler<NetworkClosedEventArgs> m_NetworkClosedEventHandler;
private EventHandler<NetworkMissHeartBeatEventArgs> m_NetworkMissHeartBeatEventHandler;
private EventHandler<NetworkErrorEventArgs> m_NetworkErrorEventHandler;
private EventHandler<NetworkCustomErrorEventArgs> m_NetworkCustomErrorEventHandler;
/// <summary>
/// 初始化网络管理器的新实例。
/// </summary>
public NetworkManager()
{
m_NetworkChannels = new Dictionary<string, NetworkChannelBase>(StringComparer.Ordinal);
m_NetworkConnectedEventHandler = null;
m_NetworkClosedEventHandler = null;
m_NetworkMissHeartBeatEventHandler = null;
m_NetworkErrorEventHandler = null;
m_NetworkCustomErrorEventHandler = null;
}
/// <summary>
/// 获取网络频道数量。
/// </summary>
public int NetworkChannelCount => m_NetworkChannels.Count;
/// <summary>
/// 网络连接成功事件。
/// </summary>
public event EventHandler<NetworkConnectedEventArgs> NetworkConnected
{
add => m_NetworkConnectedEventHandler += value;
remove => m_NetworkConnectedEventHandler -= value;
}
/// <summary>
/// 网络连接关闭事件。
/// </summary>
public event EventHandler<NetworkClosedEventArgs> NetworkClosed
{
add => m_NetworkClosedEventHandler += value;
remove => m_NetworkClosedEventHandler -= value;
}
/// <summary>
/// 网络心跳包丢失事件。
/// </summary>
public event EventHandler<NetworkMissHeartBeatEventArgs> NetworkMissHeartBeat
{
add => m_NetworkMissHeartBeatEventHandler += value;
remove => m_NetworkMissHeartBeatEventHandler -= value;
}
/// <summary>
/// 网络错误事件。
/// </summary>
public event EventHandler<NetworkErrorEventArgs> NetworkError
{
add => m_NetworkErrorEventHandler += value;
remove => m_NetworkErrorEventHandler -= value;
}
/// <summary>
/// 用户自定义网络错误事件。
/// </summary>
public event EventHandler<NetworkCustomErrorEventArgs> NetworkCustomError
{
add => m_NetworkCustomErrorEventHandler += value;
remove => m_NetworkCustomErrorEventHandler -= value;
}
/// <summary>
/// 网络管理器轮询。
/// </summary>
/// <param name="elapseSeconds">逻辑流逝时间,以秒为单位。</param>
/// <param name="realElapseSeconds">真实流逝时间,以秒为单位。</param>
protected override void Update(float elapseSeconds, float realElapseSeconds)
{
foreach (var networkChannel in m_NetworkChannels)
{
networkChannel.Value.Update(elapseSeconds, realElapseSeconds);
}
}
/// <summary>
/// 关闭并清理网络管理器。
/// </summary>
protected override void Shutdown()
{
foreach (var networkChannel in m_NetworkChannels)
{
NetworkChannelBase networkChannelBase = networkChannel.Value;
networkChannelBase.NetworkChannelConnected -= OnNetworkChannelConnected;
networkChannelBase.NetworkChannelClosed -= OnNetworkChannelClosed;
networkChannelBase.NetworkChannelMissHeartBeat -= OnNetworkChannelMissHeartBeat;
networkChannelBase.NetworkChannelError -= OnNetworkChannelError;
networkChannelBase.NetworkChannelCustomError -= OnNetworkChannelCustomError;
networkChannelBase.Shutdown();
}
m_NetworkChannels.Clear();
}
/// <summary>
/// 检查是否存在网络频道。
/// </summary>
/// <param name="channelName">网络频道名称。</param>
/// <returns>是否存在网络频道。</returns>
public bool HasNetworkChannel(string channelName)
{
return m_NetworkChannels.ContainsKey(channelName ?? string.Empty);
}
/// <summary>
/// 获取网络频道。
/// </summary>
/// <param name="channelName">网络频道名称。</param>
/// <returns>要获取的网络频道。</returns>
public INetworkChannel GetNetworkChannel(string channelName)
{
if (m_NetworkChannels.TryGetValue(channelName ?? string.Empty, out var networkChannel))
{
return networkChannel;
}
return null;
}
/// <summary>
/// 获取所有网络频道。
/// </summary>
/// <returns>所有网络频道。</returns>
public INetworkChannel[] GetAllNetworkChannels()
{
var index = 0;
var results = new INetworkChannel[m_NetworkChannels.Count];
foreach (var networkChannel in m_NetworkChannels)
{
results[index++] = networkChannel.Value;
}
return results;
}
/// <summary>
/// 获取所有网络频道。
/// </summary>
/// <param name="results">所有网络频道。</param>
public void GetAllNetworkChannels(List<INetworkChannel> results)
{
GameFrameworkGuard.NotNull(results, nameof(results));
results.Clear();
foreach (var networkChannel in m_NetworkChannels)
{
results.Add(networkChannel.Value);
}
}
/// <summary>
/// 创建网络频道。
/// </summary>
/// <param name="channelName">网络频道名称。</param>
/// <param name="networkChannelHelper">网络频道辅助器。</param>
/// <returns>要创建的网络频道。</returns>
public INetworkChannel CreateNetworkChannel(string channelName, INetworkChannelHelper networkChannelHelper)
{
GameFrameworkGuard.NotNull(networkChannelHelper, nameof(networkChannelHelper));
if (HasNetworkChannel(channelName))
{
throw new GameFrameworkException(Utility.Text.Format("Already exist network channel '{0}'.", channelName ?? string.Empty));
}
#if ENABLE_GAME_FRAME_X_WEB_SOCKET
NetworkChannelBase networkChannel = new WebSocketNetworkChannel(channelName, networkChannelHelper);
#else
NetworkChannelBase networkChannel = new SocketNetworkChannel(channelName, networkChannelHelper);
#endif
networkChannel.NetworkChannelConnected += OnNetworkChannelConnected;
networkChannel.NetworkChannelClosed += OnNetworkChannelClosed;
networkChannel.NetworkChannelMissHeartBeat += OnNetworkChannelMissHeartBeat;
networkChannel.NetworkChannelError += OnNetworkChannelError;
networkChannel.NetworkChannelCustomError += OnNetworkChannelCustomError;
m_NetworkChannels.Add(channelName, networkChannel);
return networkChannel;
}
/// <summary>
/// 销毁网络频道。
/// </summary>
/// <param name="channelName">网络频道名称。</param>
/// <returns>是否销毁网络频道成功。</returns>
public bool DestroyNetworkChannel(string channelName)
{
if (m_NetworkChannels.TryGetValue(channelName ?? string.Empty, out var networkChannel))
{
networkChannel.NetworkChannelConnected -= OnNetworkChannelConnected;
networkChannel.NetworkChannelClosed -= OnNetworkChannelClosed;
networkChannel.NetworkChannelMissHeartBeat -= OnNetworkChannelMissHeartBeat;
networkChannel.NetworkChannelError -= OnNetworkChannelError;
networkChannel.NetworkChannelCustomError -= OnNetworkChannelCustomError;
networkChannel.Shutdown();
if (channelName != null)
{
return m_NetworkChannels.Remove(channelName);
}
}
return false;
}
private void OnNetworkChannelConnected(NetworkChannelBase networkChannel, object userData)
{
if (m_NetworkConnectedEventHandler != null)
{
lock (m_NetworkConnectedEventHandler)
{
NetworkConnectedEventArgs networkConnectedEventArgs = NetworkConnectedEventArgs.Create(networkChannel, userData);
m_NetworkConnectedEventHandler(this, networkConnectedEventArgs);
// ReferencePool.Release(networkConnectedEventArgs);
}
}
}
private void OnNetworkChannelClosed(NetworkChannelBase networkChannel)
{
if (m_NetworkClosedEventHandler != null)
{
lock (m_NetworkClosedEventHandler)
{
NetworkClosedEventArgs networkClosedEventArgs = NetworkClosedEventArgs.Create(networkChannel);
m_NetworkClosedEventHandler(this, networkClosedEventArgs);
// ReferencePool.Release(networkClosedEventArgs);
}
}
}
private void OnNetworkChannelMissHeartBeat(NetworkChannelBase networkChannel, int missHeartBeatCount)
{
if (m_NetworkMissHeartBeatEventHandler != null)
{
lock (m_NetworkMissHeartBeatEventHandler)
{
NetworkMissHeartBeatEventArgs networkMissHeartBeatEventArgs = NetworkMissHeartBeatEventArgs.Create(networkChannel, missHeartBeatCount);
m_NetworkMissHeartBeatEventHandler(this, networkMissHeartBeatEventArgs);
// ReferencePool.Release(networkMissHeartBeatEventArgs);
}
}
}
private void OnNetworkChannelError(NetworkChannelBase networkChannel, NetworkErrorCode errorCode, SocketError socketErrorCode, string errorMessage)
{
if (m_NetworkErrorEventHandler != null)
{
lock (m_NetworkErrorEventHandler)
{
NetworkErrorEventArgs networkErrorEventArgs = NetworkErrorEventArgs.Create(networkChannel, errorCode, socketErrorCode, errorMessage);
m_NetworkErrorEventHandler(this, networkErrorEventArgs);
// ReferencePool.Release(networkErrorEventArgs);
}
}
}
private void OnNetworkChannelCustomError(NetworkChannelBase networkChannel, object customErrorData)
{
if (m_NetworkCustomErrorEventHandler != null)
{
lock (m_NetworkCustomErrorEventHandler)
{
NetworkCustomErrorEventArgs networkCustomErrorEventArgs = NetworkCustomErrorEventArgs.Create(networkChannel, customErrorData);
m_NetworkCustomErrorEventHandler(this, networkCustomErrorEventArgs);
// ReferencePool.Release(networkCustomErrorEventArgs);
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b730bf25813d4ea295e6a07d9985941c
timeCreated: 1712727663

View File

@ -0,0 +1,17 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 网络消息包基类。
/// </summary>
public abstract class Packet : BaseEventArgs
{
// public abstract int PacketSize();
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3c8aec49e4884375a94d4e9efbeb96b4
timeCreated: 1712727663

View File

@ -0,0 +1,23 @@
namespace GameFrameX.Network.Runtime
{
public class PacketBase : Packet
{
public MessageObject MessageObject { get; set; }
public int MessageId { get; set; }
public override void Clear()
{
MessageId = 0;
}
public override string Id
{
get { return MessageId.ToString(); }
}
public override string ToString()
{
return "Packet ID: " + MessageId + ", Message: " + MessageObject;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c7d814943d7941bc91b634f39730d506
timeCreated: 1712727663

View File

@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace GameFrameX.Network.Runtime
{
/// <summary>
/// 协议消息处理器
/// </summary>
public static class ProtoMessageIdHandler
{
private static readonly BidirectionalDictionary<int, Type> ReqDictionary = new BidirectionalDictionary<int, Type>();
private static readonly BidirectionalDictionary<int, Type> RespDictionary = new BidirectionalDictionary<int, Type>();
/// <summary>
/// 根据消息ID获取请求的类型
/// </summary>
/// <param name="messageId">消息ID</param>
/// <returns>请求的类型</returns>
public static Type GetReqTypeById(int messageId)
{
ReqDictionary.TryGetValue(messageId, out var value);
return value;
}
/// <summary>
/// 根据类型获取请求消息ID
/// </summary>
/// <param name="type">类型</param>
/// <returns>请求消息ID</returns>
public static int GetReqMessageIdByType(Type type)
{
ReqDictionary.TryGetKey(type, out var value);
return value;
}
/// <summary>
/// 根据消息ID获取响应的类型
/// </summary>
/// <param name="messageId">消息ID</param>
/// <returns>响应的类型</returns>
public static Type GetRespTypeById(int messageId)
{
RespDictionary.TryGetValue(messageId, out var value);
return value;
}
/// <summary>
/// 根据类型获取响应消息ID
/// </summary>
/// <param name="type">类型</param>
/// <returns>响应消息ID</returns>
public static int GetRespMessageIdByType(Type type)
{
RespDictionary.TryGetKey(type, out var value);
return value;
}
/// <summary>
/// 初始化所有协议对象
/// </summary>
public static void Init(Assembly assembly)
{
ReqDictionary.Clear();
RespDictionary.Clear();
var types = assembly.GetTypes();
StringBuilder stringBuilder = new StringBuilder(1024);
foreach (var type in types)
{
var attribute = type.GetCustomAttribute(typeof(MessageTypeHandlerAttribute));
if (attribute == null)
{
continue;
}
stringBuilder.AppendLine(type.FullName);
if (attribute is MessageTypeHandlerAttribute messageIdHandler)
{
if (type.IsImplWithInterface(typeof(IRequestMessage)))
{
// 请求
if (!ReqDictionary.TryAdd(messageIdHandler.MessageId, type))
{
ReqDictionary.TryGetValue(messageIdHandler.MessageId, out var value);
throw new GameFrameworkException($"请求Id重复==>当前ID:{messageIdHandler.MessageId},已有ID类型:{value.FullName}");
}
}
else if (type.IsImplWithInterface(typeof(IResponseMessage)))
{
// 返回
if (!RespDictionary.TryAdd(messageIdHandler.MessageId, type))
{
RespDictionary.TryGetValue(messageIdHandler.MessageId, out var value);
throw new GameFrameworkException($"返回Id重复==>当前ID:{messageIdHandler.MessageId},已有ID类型:{value.FullName}");
}
}
}
}
GameFrameworkLog.Debug(" 注册消息ID类型: " + stringBuilder);
GameFrameworkLog.Info(" 注册消息ID类型: 结束");
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9e5dff838bff4fdb84ed874cf3de295a
timeCreated: 1712727663

Some files were not shown because too many files have changed in this diff Show More