diff --git a/Assets/TEngine/Scripts/Runtime/Core/Base/DataStruct/LinkedListRange.cs b/Assets/TEngine/Scripts/Runtime/Core/Base/DataStruct/LinkedListRange.cs new file mode 100644 index 00000000..6016bf7e --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/Base/DataStruct/LinkedListRange.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace TEngine.Runtime +{ + /// + /// 游戏框架链表范围。 + /// + /// 指定链表范围的元素类型。 + [StructLayout(LayoutKind.Auto)] + public struct LinkedListRange : IEnumerable, IEnumerable + { + private readonly LinkedListNode m_First; + private readonly LinkedListNode m_Terminal; + + /// + /// 初始化游戏框架链表范围的新实例。 + /// + /// 链表范围的开始结点。 + /// 链表范围的终结标记结点。 + public LinkedListRange(LinkedListNode first, LinkedListNode terminal) + { + if (first == null || terminal == null || first == terminal) + { + throw new Exception("Range is invalid."); + } + + m_First = first; + m_Terminal = terminal; + } + + /// + /// 获取链表范围是否有效。 + /// + public bool IsValid + { + get + { + return m_First != null && m_Terminal != null && m_First != m_Terminal; + } + } + + /// + /// 获取链表范围的开始结点。 + /// + public LinkedListNode First + { + get + { + return m_First; + } + } + + /// + /// 获取链表范围的终结标记结点。 + /// + public LinkedListNode Terminal + { + get + { + return m_Terminal; + } + } + + /// + /// 获取链表范围的结点数量。 + /// + public int Count + { + get + { + if (!IsValid) + { + return 0; + } + + int count = 0; + for (LinkedListNode current = m_First; current != null && current != m_Terminal; current = current.Next) + { + count++; + } + + return count; + } + } + + /// + /// 检查是否包含指定值。 + /// + /// 要检查的值。 + /// 是否包含指定值。 + public bool Contains(T value) + { + for (LinkedListNode current = m_First; current != null && current != m_Terminal; current = current.Next) + { + if (current.Value.Equals(value)) + { + return true; + } + } + + return false; + } + + /// + /// 返回循环访问集合的枚举数。 + /// + /// 循环访问集合的枚举数。 + public Enumerator GetEnumerator() + { + return new Enumerator(this); + } + + /// + /// 返回循环访问集合的枚举数。 + /// + /// 循环访问集合的枚举数。 + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// 返回循环访问集合的枚举数。 + /// + /// 循环访问集合的枚举数。 + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// 循环访问集合的枚举数。 + /// + [StructLayout(LayoutKind.Auto)] + public struct Enumerator : IEnumerator, IEnumerator + { + private readonly LinkedListRange _mLinkedListRange; + private LinkedListNode m_Current; + private T m_CurrentValue; + + internal Enumerator(LinkedListRange range) + { + if (!range.IsValid) + { + throw new Exception("Range is invalid."); + } + + _mLinkedListRange = range; + m_Current = _mLinkedListRange.m_First; + m_CurrentValue = default(T); + } + + /// + /// 获取当前结点。 + /// + public T Current + { + get + { + return m_CurrentValue; + } + } + + /// + /// 获取当前的枚举数。 + /// + object IEnumerator.Current + { + get + { + return m_CurrentValue; + } + } + + /// + /// 清理枚举数。 + /// + public void Dispose() + { + } + + /// + /// 获取下一个结点。 + /// + /// 返回下一个结点。 + public bool MoveNext() + { + if (m_Current == null || m_Current == _mLinkedListRange.m_Terminal) + { + return false; + } + + m_CurrentValue = m_Current.Value; + m_Current = m_Current.Next; + return true; + } + + /// + /// 重置枚举数。 + /// + void IEnumerator.Reset() + { + m_Current = _mLinkedListRange.m_First; + m_CurrentValue = default(T); + } + } + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/Base/DataStruct/LinkedListRange.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/Base/DataStruct/LinkedListRange.cs.meta new file mode 100644 index 00000000..9e81a6f5 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/Base/DataStruct/LinkedListRange.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ae8aa6552d5da4247996a9f313b9cdc7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/Base/DataStruct/MultiDictionary.cs b/Assets/TEngine/Scripts/Runtime/Core/Base/DataStruct/MultiDictionary.cs new file mode 100644 index 00000000..0784fb35 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/Base/DataStruct/MultiDictionary.cs @@ -0,0 +1,277 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace TEngine.Runtime +{ + /// + /// 游戏框架多值字典类。 + /// + /// 指定多值字典的主键类型。 + /// 指定多值字典的值类型。 + public sealed class MultiDictionary : IEnumerable>>, IEnumerable + { + private readonly LinkedList m_LinkedList; + private readonly Dictionary> m_Dictionary; + + /// + /// 初始化游戏框架多值字典类的新实例。 + /// + public MultiDictionary() + { + m_LinkedList = new LinkedList(); + m_Dictionary = new Dictionary>(); + } + + /// + /// 获取多值字典中实际包含的主键数量。 + /// + public int Count + { + get + { + return m_Dictionary.Count; + } + } + + /// + /// 获取多值字典中指定主键的范围。 + /// + /// 指定的主键。 + /// 指定主键的范围。 + public LinkedListRange this[TKey key] + { + get + { + LinkedListRange range = default(LinkedListRange); + m_Dictionary.TryGetValue(key, out range); + return range; + } + } + + /// + /// 清理多值字典。 + /// + public void Clear() + { + m_Dictionary.Clear(); + m_LinkedList.Clear(); + } + + /// + /// 检查多值字典中是否包含指定主键。 + /// + /// 要检查的主键。 + /// 多值字典中是否包含指定主键。 + public bool Contains(TKey key) + { + return m_Dictionary.ContainsKey(key); + } + + /// + /// 检查多值字典中是否包含指定值。 + /// + /// 要检查的主键。 + /// 要检查的值。 + /// 多值字典中是否包含指定值。 + public bool Contains(TKey key, TValue value) + { + LinkedListRange range = default(LinkedListRange); + if (m_Dictionary.TryGetValue(key, out range)) + { + return range.Contains(value); + } + + return false; + } + + /// + /// 尝试获取多值字典中指定主键的范围。 + /// + /// 指定的主键。 + /// 指定主键的范围。 + /// 是否获取成功。 + public bool TryGetValue(TKey key, out LinkedListRange range) + { + return m_Dictionary.TryGetValue(key, out range); + } + + /// + /// 向指定的主键增加指定的值。 + /// + /// 指定的主键。 + /// 指定的值。 + public void Add(TKey key, TValue value) + { + LinkedListRange range = default(LinkedListRange); + if (m_Dictionary.TryGetValue(key, out range)) + { + m_LinkedList.AddBefore(range.Terminal, value); + } + else + { + LinkedListNode first = m_LinkedList.AddLast(value); + LinkedListNode terminal = m_LinkedList.AddLast(default(TValue)); + m_Dictionary.Add(key, new LinkedListRange(first, terminal)); + } + } + + /// + /// 从指定的主键中移除指定的值。 + /// + /// 指定的主键。 + /// 指定的值。 + /// 是否移除成功。 + public bool Remove(TKey key, TValue value) + { + LinkedListRange range = default(LinkedListRange); + if (m_Dictionary.TryGetValue(key, out range)) + { + for (LinkedListNode current = range.First; current != null && current != range.Terminal; current = current.Next) + { + if (current.Value.Equals(value)) + { + if (current == range.First) + { + LinkedListNode next = current.Next; + if (next == range.Terminal) + { + m_LinkedList.Remove(next); + m_Dictionary.Remove(key); + } + else + { + m_Dictionary[key] = new LinkedListRange(next, range.Terminal); + } + } + + m_LinkedList.Remove(current); + return true; + } + } + } + + return false; + } + + /// + /// 从指定的主键中移除所有的值。 + /// + /// 指定的主键。 + /// 是否移除成功。 + public bool RemoveAll(TKey key) + { + LinkedListRange range = default(LinkedListRange); + if (m_Dictionary.TryGetValue(key, out range)) + { + m_Dictionary.Remove(key); + + LinkedListNode current = range.First; + while (current != null) + { + LinkedListNode next = current != range.Terminal ? current.Next : null; + m_LinkedList.Remove(current); + current = next; + } + + return true; + } + + return false; + } + + /// + /// 返回循环访问集合的枚举数。 + /// + /// 循环访问集合的枚举数。 + public Enumerator GetEnumerator() + { + return new Enumerator(m_Dictionary); + } + + /// + /// 返回循环访问集合的枚举数。 + /// + /// 循环访问集合的枚举数。 + IEnumerator>> IEnumerable>>.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// 返回循环访问集合的枚举数。 + /// + /// 循环访问集合的枚举数。 + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// 循环访问集合的枚举数。 + /// + [StructLayout(LayoutKind.Auto)] + public struct Enumerator : IEnumerator>>, IEnumerator + { + private Dictionary>.Enumerator m_Enumerator; + + internal Enumerator(Dictionary> dictionary) + { + if (dictionary == null) + { + throw new Exception("Dictionary is invalid."); + } + + m_Enumerator = dictionary.GetEnumerator(); + } + + /// + /// 获取当前结点。 + /// + public KeyValuePair> Current + { + get + { + return m_Enumerator.Current; + } + } + + /// + /// 获取当前的枚举数。 + /// + object IEnumerator.Current + { + get + { + return m_Enumerator.Current; + } + } + + /// + /// 清理枚举数。 + /// + public void Dispose() + { + m_Enumerator.Dispose(); + } + + /// + /// 获取下一个结点。 + /// + /// 返回下一个结点。 + public bool MoveNext() + { + return m_Enumerator.MoveNext(); + } + + /// + /// 重置枚举数。 + /// + void IEnumerator.Reset() + { + ((IEnumerator>>)m_Enumerator).Reset(); + } + } + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/Base/DataStruct/MultiDictionary.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/Base/DataStruct/MultiDictionary.cs.meta new file mode 100644 index 00000000..fe81dd96 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/Base/DataStruct/MultiDictionary.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3eff6e3b9f51d4e48a6e454d268538de +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/Event/GameEvent.cs b/Assets/TEngine/Scripts/Runtime/Core/Event/GameEvent.cs index 995e392a..627479b2 100644 --- a/Assets/TEngine/Scripts/Runtime/Core/Event/GameEvent.cs +++ b/Assets/TEngine/Scripts/Runtime/Core/Event/GameEvent.cs @@ -690,5 +690,4 @@ namespace TEngine.Runtime } #endregion } - } \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork.meta new file mode 100644 index 00000000..7b259d95 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 69740261aa305b94aa6a2a717d82d8e4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/AddressFamily.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/AddressFamily.cs new file mode 100644 index 00000000..fdc3f8fb --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/AddressFamily.cs @@ -0,0 +1,23 @@ +namespace TEngine.Runtime +{ + /// + /// 网络地址类型。 + /// + public enum AddressFamily : byte + { + /// + /// 未知。 + /// + Unknown = 0, + + /// + /// IP 版本 4。 + /// + IPv4, + + /// + /// IP 版本 6。 + /// + IPv6 + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/AddressFamily.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/AddressFamily.cs.meta new file mode 100644 index 00000000..54d65ab7 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/AddressFamily.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6f0c907c7aeb4bd2a19ff78ad8e8b668 +timeCreated: 1661772419 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event.meta new file mode 100644 index 00000000..73250cfc --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2c4f37bbaa3a4ae45a5aa851ec78c3d0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs.meta new file mode 100644 index 00000000..5329bee4 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1b7b82bd3d86d874c978bebc6147b442 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetBaseEventArgs.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetBaseEventArgs.cs new file mode 100644 index 00000000..aed94370 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetBaseEventArgs.cs @@ -0,0 +1,19 @@ +using System; + +namespace TEngine.Runtime +{ + public abstract class NetBaseEventArgs : EventArgs, IMemory + { + /// + /// 初始化构造函数 + /// + public NetBaseEventArgs() + { + } + + /// + /// 清理引用。 + /// + public abstract void Clear(); + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetBaseEventArgs.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetBaseEventArgs.cs.meta new file mode 100644 index 00000000..5ea01426 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetBaseEventArgs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dd227169d1cedc64ba3c1454671bca7d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkClosedEventArgs.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkClosedEventArgs.cs new file mode 100644 index 00000000..0f7b5d7f --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkClosedEventArgs.cs @@ -0,0 +1,61 @@ +namespace TEngine.Runtime +{ + /// + /// 网络连接关闭事件。 + /// + public sealed class NetworkClosedEventArgs : GameEventArgs + { + /// + /// 网络连接关闭事件编号。 + /// + public static readonly int EventId = typeof(NetworkClosedEventArgs).GetHashCode(); + + /// + /// 初始化网络连接关闭事件的新实例。 + /// + public NetworkClosedEventArgs() + { + NetworkChannel = null; + } + + /// + /// 获取网络连接关闭事件编号。 + /// + public override int Id + { + get + { + return EventId; + } + } + + /// + /// 获取网络频道。 + /// + public INetworkChannel NetworkChannel + { + get; + private set; + } + + /// + /// 创建网络连接关闭事件。 + /// + /// 网络频道。 + /// 创建的网络连接关闭事件。 + public static NetworkClosedEventArgs Create(INetworkChannel networkChannel) + { + NetworkClosedEventArgs networkClosedEventArgs = MemoryPool.Acquire(); + networkClosedEventArgs.NetworkChannel = networkChannel; + return networkClosedEventArgs; + } + + /// + /// 清理网络连接关闭事件。 + /// + public override void Clear() + { + NetworkChannel = null; + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkClosedEventArgs.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkClosedEventArgs.cs.meta new file mode 100644 index 00000000..d654d3a4 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkClosedEventArgs.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6218d5320c6b4a328df908e68d135177 +timeCreated: 1661772187 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkConnectedEventArgs.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkConnectedEventArgs.cs new file mode 100644 index 00000000..aa764289 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkConnectedEventArgs.cs @@ -0,0 +1,74 @@ +namespace TEngine.Runtime +{ + /// + /// 网络连接成功事件。 + /// + public sealed class NetworkConnectedEventArgs : GameEventArgs + { + /// + /// 网络连接成功事件编号。 + /// + public static readonly int EventId = typeof(NetworkConnectedEventArgs).GetHashCode(); + + /// + /// 初始化网络连接成功事件的新实例。 + /// + public NetworkConnectedEventArgs() + { + NetworkChannel = null; + UserData = null; + } + + /// + /// 获取网络连接成功事件编号。 + /// + public override int Id + { + get + { + return EventId; + } + } + + /// + /// 获取网络频道。 + /// + public INetworkChannel NetworkChannel + { + get; + private set; + } + + /// + /// 获取用户自定义数据。 + /// + public object UserData + { + get; + private set; + } + + /// + /// 创建网络连接成功事件。 + /// + /// 网络频道。 + /// 用户自定义数据。 + /// 创建的网络连接成功事件。 + public static NetworkConnectedEventArgs Create(INetworkChannel networkChannel) + { + NetworkConnectedEventArgs networkConnectedEventArgs = MemoryPool.Acquire(); + networkConnectedEventArgs.NetworkChannel = networkChannel; + networkConnectedEventArgs.UserData = null; + return networkConnectedEventArgs; + } + + /// + /// 清理网络连接成功事件。 + /// + public override void Clear() + { + NetworkChannel = null; + UserData = null; + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkConnectedEventArgs.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkConnectedEventArgs.cs.meta new file mode 100644 index 00000000..9c4e82a7 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkConnectedEventArgs.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 64fffbe4a72442f0b3bbd81f78509b13 +timeCreated: 1661772223 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkCustomErrorEventArgs.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkCustomErrorEventArgs.cs new file mode 100644 index 00000000..b63a9126 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkCustomErrorEventArgs.cs @@ -0,0 +1,74 @@ +namespace TEngine.Runtime +{ + /// + /// 用户自定义网络错误事件。 + /// + public sealed class NetworkCustomErrorEventArgs : GameEventArgs + { + /// + /// 用户自定义网络错误事件编号。 + /// + public static readonly int EventId = typeof(NetworkCustomErrorEventArgs).GetHashCode(); + + /// + /// 初始化用户自定义网络错误事件的新实例。 + /// + public NetworkCustomErrorEventArgs() + { + NetworkChannel = null; + CustomErrorData = null; + } + + /// + /// 获取用户自定义网络错误事件编号。 + /// + public override int Id + { + get + { + return EventId; + } + } + + /// + /// 获取网络频道。 + /// + public INetworkChannel NetworkChannel + { + get; + private set; + } + + /// + /// 获取用户自定义错误数据。 + /// + public object CustomErrorData + { + get; + private set; + } + + /// + /// 创建用户自定义网络错误事件。 + /// + /// 网络频道。 + /// 用户自定义错误数据。 + /// 创建的用户自定义网络错误事件。 + public static NetworkCustomErrorEventArgs Create(INetworkChannel networkChannel, object customErrorData) + { + NetworkCustomErrorEventArgs networkCustomErrorEventArgs = MemoryPool.Acquire(); + networkCustomErrorEventArgs.NetworkChannel = networkChannel; + networkCustomErrorEventArgs.CustomErrorData = customErrorData; + return networkCustomErrorEventArgs; + } + + /// + /// 清理用户自定义网络错误事件。 + /// + public override void Clear() + { + NetworkChannel = null; + CustomErrorData = null; + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkCustomErrorEventArgs.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkCustomErrorEventArgs.cs.meta new file mode 100644 index 00000000..5a08e28a --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkCustomErrorEventArgs.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6947eca0814140a6a7fc564274cdb804 +timeCreated: 1661772258 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkErrorEventArgs.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkErrorEventArgs.cs new file mode 100644 index 00000000..cb3f073e --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkErrorEventArgs.cs @@ -0,0 +1,102 @@ +using System.Net.Sockets; + +namespace TEngine.Runtime +{ + /// + /// 网络错误事件。 + /// + public sealed class NetworkErrorEventArgs : GameEventArgs + { + /// + /// 网络错误事件编号。 + /// + public static readonly int EventId = typeof(NetworkErrorEventArgs).GetHashCode(); + + /// + /// 初始化网络错误事件的新实例。 + /// + public NetworkErrorEventArgs() + { + NetworkChannel = null; + ErrorCode = NetworkErrorCode.Unknown; + SocketErrorCode = SocketError.Success; + ErrorMessage = null; + } + + /// + /// 获取网络错误事件编号。 + /// + public override int Id + { + get + { + return EventId; + } + } + + /// + /// 获取网络频道。 + /// + public INetworkChannel NetworkChannel + { + get; + private set; + } + + /// + /// 获取错误码。 + /// + public NetworkErrorCode ErrorCode + { + get; + private set; + } + + /// + /// 获取 Socket 错误码。 + /// + public SocketError SocketErrorCode + { + get; + private set; + } + + /// + /// 获取错误信息。 + /// + public string ErrorMessage + { + get; + private set; + } + + /// + /// 创建网络错误事件。 + /// + /// 网络频道。 + /// 错误码。 + /// Socket 错误码。 + /// 错误信息。 + /// 创建的网络错误事件。 + public static NetworkErrorEventArgs Create(INetworkChannel networkChannel, NetworkErrorCode errorCode, SocketError socketErrorCode, string errorMessage) + { + NetworkErrorEventArgs networkErrorEventArgs = MemoryPool.Acquire(); + networkErrorEventArgs.NetworkChannel = networkChannel; + networkErrorEventArgs.ErrorCode = errorCode; + networkErrorEventArgs.SocketErrorCode = socketErrorCode; + networkErrorEventArgs.ErrorMessage = errorMessage; + return networkErrorEventArgs; + } + + /// + /// 清理网络错误事件。 + /// + public override void Clear() + { + NetworkChannel = null; + ErrorCode = NetworkErrorCode.Unknown; + SocketErrorCode = SocketError.Success; + ErrorMessage = null; + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkErrorEventArgs.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkErrorEventArgs.cs.meta new file mode 100644 index 00000000..8e0f5fbe --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkErrorEventArgs.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7f64b74fa985459b88f908ff182e8672 +timeCreated: 1661772293 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkMissHeartBeatEventArgs.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkMissHeartBeatEventArgs.cs new file mode 100644 index 00000000..bf189d6d --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkMissHeartBeatEventArgs.cs @@ -0,0 +1,74 @@ +namespace TEngine.Runtime +{ + /// + /// 网络心跳包丢失事件。 + /// + public sealed class NetworkMissHeartBeatEventArgs : GameEventArgs + { + /// + /// 网络心跳包丢失事件编号。 + /// + public static readonly int EventId = typeof(NetworkMissHeartBeatEventArgs).GetHashCode(); + + /// + /// 初始化网络心跳包丢失事件的新实例。 + /// + public NetworkMissHeartBeatEventArgs() + { + NetworkChannel = null; + MissCount = 0; + } + + /// + /// 获取网络心跳包丢失事件编号。 + /// + public override int Id + { + get + { + return EventId; + } + } + + /// + /// 获取网络频道。 + /// + public INetworkChannel NetworkChannel + { + get; + private set; + } + + /// + /// 获取心跳包已丢失次数。 + /// + public int MissCount + { + get; + private set; + } + + /// + /// 创建网络心跳包丢失事件。 + /// + /// 网络频道。 + /// 心跳包已丢失次数。 + /// 创建的网络心跳包丢失事件。 + public static NetworkMissHeartBeatEventArgs Create(INetworkChannel networkChannel, int missCount) + { + NetworkMissHeartBeatEventArgs networkMissHeartBeatEventArgs = MemoryPool.Acquire(); + networkMissHeartBeatEventArgs.NetworkChannel = networkChannel; + networkMissHeartBeatEventArgs.MissCount = missCount; + return networkMissHeartBeatEventArgs; + } + + /// + /// 清理网络心跳包丢失事件。 + /// + public override void Clear() + { + NetworkChannel = null; + MissCount = 0; + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkMissHeartBeatEventArgs.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkMissHeartBeatEventArgs.cs.meta new file mode 100644 index 00000000..6bf1598c --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventArgs/NetworkMissHeartBeatEventArgs.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c2dc35d1fb8047e3b86f4af42124aa06 +timeCreated: 1661772338 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventManager.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventManager.cs new file mode 100644 index 00000000..6dc69906 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventManager.cs @@ -0,0 +1,130 @@ +using System; + +namespace TEngine.Runtime +{ + /// + /// 事件管理器。 + /// + internal sealed class EventManager: IEventManager + { + private readonly EventPool m_EventPool; + + /// + /// 初始化事件管理器的新实例。 + /// + public EventManager() + { + m_EventPool = new EventPool(EventPoolMode.AllowNoHandler | EventPoolMode.AllowMultiHandler); + } + + /// + /// 获取事件处理函数的数量。 + /// + public int EventHandlerCount + { + get + { + return m_EventPool.EventHandlerCount; + } + } + + /// + /// 获取事件数量。 + /// + public int EventCount + { + get + { + return m_EventPool.EventCount; + } + } + + /// + /// 事件管理器轮询。 + /// + /// 逻辑流逝时间,以秒为单位。 + /// 真实流逝时间,以秒为单位。 + internal void Update(float elapseSeconds, float realElapseSeconds) + { + m_EventPool.Update(elapseSeconds, realElapseSeconds); + } + + /// + /// 关闭并清理事件管理器。 + /// + internal void Shutdown() + { + m_EventPool.Shutdown(); + } + + /// + /// 获取事件处理函数的数量。 + /// + /// 事件类型编号。 + /// 事件处理函数的数量。 + public int Count(int id) + { + return m_EventPool.Count(id); + } + + /// + /// 检查是否存在事件处理函数。 + /// + /// 事件类型编号。 + /// 要检查的事件处理函数。 + /// 是否存在事件处理函数。 + public bool Check(int id, EventHandler handler) + { + return m_EventPool.Check(id, handler); + } + + /// + /// 订阅事件处理函数。 + /// + /// 事件类型编号。 + /// 要订阅的事件处理函数。 + public void Subscribe(int id, EventHandler handler) + { + m_EventPool.Subscribe(id, handler); + } + + /// + /// 取消订阅事件处理函数。 + /// + /// 事件类型编号。 + /// 要取消订阅的事件处理函数。 + public void Unsubscribe(int id, EventHandler handler) + { + m_EventPool.Unsubscribe(id, handler); + } + + /// + /// 设置默认事件处理函数。 + /// + /// 要设置的默认事件处理函数。 + public void SetDefaultHandler(EventHandler handler) + { + m_EventPool.SetDefaultHandler(handler); + } + + /// + /// 抛出事件,这个操作是线程安全的,即使不在主线程中抛出,也可保证在主线程中回调事件处理函数,但事件会在抛出后的下一帧分发。 + /// + /// 事件源。 + /// 事件参数。 + public void Fire(object sender, GameEventArgs e) + { + m_EventPool.Fire(sender, e); + } + + /// + /// 抛出事件立即模式,这个操作不是线程安全的,事件会立刻分发。 + /// + /// 事件源。 + /// 事件参数。 + public void FireNow(object sender, GameEventArgs e) + { + m_EventPool.FireNow(sender, e); + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventManager.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventManager.cs.meta new file mode 100644 index 00000000..a8fa2d15 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5caf443f519944048edbf1bea889f6d2 +timeCreated: 1661775799 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool.meta new file mode 100644 index 00000000..d5f8af7e --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4b813480944c71f47a87c3facb84bce2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/BaseEventArgs.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/BaseEventArgs.cs new file mode 100644 index 00000000..e174a1b6 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/BaseEventArgs.cs @@ -0,0 +1,31 @@ +using System; + +namespace TEngine.Runtime +{ + /// + /// 事件基类。 + /// + public abstract class BaseEventArgs : EventArgs, IMemory + { + /// + /// 构造函数 + /// + public BaseEventArgs() + { + + } + + /// + /// 获取类型编号。 + /// + public abstract int Id + { + get; + } + + /// + /// 清理引用。 + /// + public abstract void Clear(); + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/BaseEventArgs.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/BaseEventArgs.cs.meta new file mode 100644 index 00000000..e3362be3 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/BaseEventArgs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 996eb2d764ee87b45a2d2aab7b88e148 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPool.Event.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPool.Event.cs new file mode 100644 index 00000000..2bfa63a0 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPool.Event.cs @@ -0,0 +1,50 @@ +namespace TEngine.Runtime +{ + internal sealed partial class EventPool where T : BaseEventArgs + { + /// + /// 事件结点。 + /// + private sealed class Event : IMemory + { + private object m_Sender; + private T m_EventArgs; + + public Event() + { + m_Sender = null; + m_EventArgs = null; + } + + public object Sender + { + get + { + return m_Sender; + } + } + + public T EventArgs + { + get + { + return m_EventArgs; + } + } + + public static Event Create(object sender, T e) + { + Event eventNode = MemoryPool.Acquire(); + eventNode.m_Sender = sender; + eventNode.m_EventArgs = e; + return eventNode; + } + + public void Clear() + { + m_Sender = null; + m_EventArgs = null; + } + } + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPool.Event.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPool.Event.cs.meta new file mode 100644 index 00000000..7613c093 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPool.Event.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 342958da5a55f9647a13c573988bdec5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPool.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPool.cs new file mode 100644 index 00000000..a7c20998 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPool.cs @@ -0,0 +1,278 @@ +using System; +using System.Collections.Generic; + +namespace TEngine.Runtime +{ + /// + /// 事件池。 + /// + /// 事件类型。 + internal sealed partial class EventPool where T : BaseEventArgs + { + private readonly MultiDictionary> m_EventHandlers; + private readonly Queue m_Events; + private readonly Dictionary>> m_CachedNodes; + private readonly Dictionary>> m_TempNodes; + private readonly EventPoolMode m_EventPoolMode; + private EventHandler m_DefaultHandler; + + /// + /// 初始化事件池的新实例。 + /// + /// 事件池模式。 + public EventPool(EventPoolMode mode) + { + m_EventHandlers = new MultiDictionary>(); + m_Events = new Queue(); + m_CachedNodes = new Dictionary>>(); + m_TempNodes = new Dictionary>>(); + m_EventPoolMode = mode; + m_DefaultHandler = null; + } + + /// + /// 获取事件处理函数的数量。 + /// + public int EventHandlerCount + { + get + { + return m_EventHandlers.Count; + } + } + + /// + /// 获取事件数量。 + /// + public int EventCount + { + get + { + return m_Events.Count; + } + } + + /// + /// 事件池轮询。 + /// + /// 逻辑流逝时间,以秒为单位。 + /// 真实流逝时间,以秒为单位。 + public void Update(float elapseSeconds, float realElapseSeconds) + { + lock (m_Events) + { + while (m_Events.Count > 0) + { + Event eventNode = m_Events.Dequeue(); + HandleEvent(eventNode.Sender, eventNode.EventArgs); + MemoryPool.Release(eventNode); + } + } + } + + /// + /// 关闭并清理事件池。 + /// + public void Shutdown() + { + Clear(); + m_EventHandlers.Clear(); + m_CachedNodes.Clear(); + m_TempNodes.Clear(); + m_DefaultHandler = null; + } + + /// + /// 清理事件。 + /// + public void Clear() + { + lock (m_Events) + { + m_Events.Clear(); + } + } + + /// + /// 获取事件处理函数的数量。 + /// + /// 事件类型编号。 + /// 事件处理函数的数量。 + public int Count(int id) + { + LinkedListRange> range = default(LinkedListRange>); + if (m_EventHandlers.TryGetValue(id, out range)) + { + return range.Count; + } + + return 0; + } + + /// + /// 检查是否存在事件处理函数。 + /// + /// 事件类型编号。 + /// 要检查的事件处理函数。 + /// 是否存在事件处理函数。 + public bool Check(int id, EventHandler handler) + { + if (handler == null) + { + throw new Exception("Event handler is invalid."); + } + + return m_EventHandlers.Contains(id, handler); + } + + /// + /// 订阅事件处理函数。 + /// + /// 事件类型编号。 + /// 要订阅的事件处理函数。 + public void Subscribe(int id, EventHandler handler) + { + if (handler == null) + { + throw new Exception("Event handler is invalid."); + } + + if (!m_EventHandlers.Contains(id)) + { + m_EventHandlers.Add(id, handler); + } + else if ((m_EventPoolMode & EventPoolMode.AllowMultiHandler) != EventPoolMode.AllowMultiHandler) + { + throw new Exception(Utility.Text.Format("Event '{0}' not allow multi handler.", id)); + } + else if ((m_EventPoolMode & EventPoolMode.AllowDuplicateHandler) != EventPoolMode.AllowDuplicateHandler && Check(id, handler)) + { + throw new Exception(Utility.Text.Format("Event '{0}' not allow duplicate handler.", id)); + } + else + { + m_EventHandlers.Add(id, handler); + } + } + + /// + /// 取消订阅事件处理函数。 + /// + /// 事件类型编号。 + /// 要取消订阅的事件处理函数。 + public void Unsubscribe(int id, EventHandler handler) + { + if (handler == null) + { + throw new Exception("Event handler is invalid."); + } + + if (m_CachedNodes.Count > 0) + { + foreach (KeyValuePair>> cachedNode in m_CachedNodes) + { + if (cachedNode.Value != null && cachedNode.Value.Value == handler) + { + m_TempNodes.Add(cachedNode.Key, cachedNode.Value.Next); + } + } + + if (m_TempNodes.Count > 0) + { + foreach (KeyValuePair>> cachedNode in m_TempNodes) + { + m_CachedNodes[cachedNode.Key] = cachedNode.Value; + } + + m_TempNodes.Clear(); + } + } + + if (!m_EventHandlers.Remove(id, handler)) + { + throw new Exception(Utility.Text.Format("Event '{0}' not exists specified handler.", id)); + } + } + + /// + /// 设置默认事件处理函数。 + /// + /// 要设置的默认事件处理函数。 + public void SetDefaultHandler(EventHandler handler) + { + m_DefaultHandler = handler; + } + + /// + /// 抛出事件,这个操作是线程安全的,即使不在主线程中抛出,也可保证在主线程中回调事件处理函数,但事件会在抛出后的下一帧分发。 + /// + /// 事件源。 + /// 事件参数。 + public void Fire(object sender, T e) + { + if (e == null) + { + throw new Exception("Event is invalid."); + } + + Event eventNode = Event.Create(sender, e); + lock (m_Events) + { + m_Events.Enqueue(eventNode); + } + } + + /// + /// 抛出事件立即模式,这个操作不是线程安全的,事件会立刻分发。 + /// + /// 事件源。 + /// 事件参数。 + public void FireNow(object sender, T e) + { + if (e == null) + { + throw new Exception("Event is invalid."); + } + + HandleEvent(sender, e); + } + + /// + /// 处理事件结点。 + /// + /// 事件源。 + /// 事件参数。 + private void HandleEvent(object sender, T e) + { + bool noHandlerException = false; + LinkedListRange> range = default(LinkedListRange>); + if (m_EventHandlers.TryGetValue(e.Id, out range)) + { + LinkedListNode> current = range.First; + while (current != null && current != range.Terminal) + { + m_CachedNodes[e] = current.Next != range.Terminal ? current.Next : null; + current.Value(sender, e); + current = m_CachedNodes[e]; + } + + m_CachedNodes.Remove(e); + } + else if (m_DefaultHandler != null) + { + m_DefaultHandler(sender, e); + } + else if ((m_EventPoolMode & EventPoolMode.AllowNoHandler) == 0) + { + noHandlerException = true; + } + + MemoryPool.Release(e); + + if (noHandlerException) + { + throw new Exception(Utility.Text.Format("Event '{0}' not allow no handler.", e.Id)); + } + } + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPool.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPool.cs.meta new file mode 100644 index 00000000..1e62e914 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d5b63a6b52c0c9e42bb270c6756aac26 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPoolMode.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPoolMode.cs new file mode 100644 index 00000000..00a98165 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPoolMode.cs @@ -0,0 +1,31 @@ +using System; + +namespace TEngine.Runtime +{ + /// + /// 事件池模式。 + /// + [Flags] + internal enum EventPoolMode : byte + { + /// + /// 默认事件池模式,即必须存在有且只有一个事件处理函数。 + /// + Default = 0, + + /// + /// 允许不存在事件处理函数。 + /// + AllowNoHandler = 1, + + /// + /// 允许存在多个事件处理函数。 + /// + AllowMultiHandler = 2, + + /// + /// 允许存在重复的事件处理函数。 + /// + AllowDuplicateHandler = 4 + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPoolMode.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPoolMode.cs.meta new file mode 100644 index 00000000..8aa349ba --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/EventPool/EventPoolMode.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c15df25d44d958f47a48a7ae0f26018c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/GameEventArgs.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/GameEventArgs.cs new file mode 100644 index 00000000..fb0ae382 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/GameEventArgs.cs @@ -0,0 +1,9 @@ +namespace TEngine.Runtime +{ + /// + /// 游戏逻辑事件基类。 + /// + public abstract class GameEventArgs : BaseEventArgs + { + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/GameEventArgs.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/GameEventArgs.cs.meta new file mode 100644 index 00000000..9a64959f --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/GameEventArgs.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: af3ac5182b9c4df9b51b8720629f19a8 +timeCreated: 1661775780 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/IEventManager.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/IEventManager.cs new file mode 100644 index 00000000..d6075ed4 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/IEventManager.cs @@ -0,0 +1,75 @@ +using System; + +namespace TEngine.Runtime +{ + /// + /// 事件管理器接口。 + /// + public interface IEventManager + { + /// + /// 获取事件处理函数的数量。 + /// + int EventHandlerCount + { + get; + } + + /// + /// 获取事件数量。 + /// + int EventCount + { + get; + } + + /// + /// 获取事件处理函数的数量。 + /// + /// 事件类型编号。 + /// 事件处理函数的数量。 + int Count(int id); + + /// + /// 检查是否存在事件处理函数。 + /// + /// 事件类型编号。 + /// 要检查的事件处理函数。 + /// 是否存在事件处理函数。 + bool Check(int id, EventHandler handler); + + /// + /// 订阅事件处理函数。 + /// + /// 事件类型编号。 + /// 要订阅的事件处理函数。 + void Subscribe(int id, EventHandler handler); + + /// + /// 取消订阅事件处理函数。 + /// + /// 事件类型编号。 + /// 要取消订阅的事件处理函数。 + void Unsubscribe(int id, EventHandler handler); + + /// + /// 设置默认事件处理函数。 + /// + /// 要设置的默认事件处理函数。 + void SetDefaultHandler(EventHandler handler); + + /// + /// 抛出事件,这个操作是线程安全的,即使不在主线程中抛出,也可保证在主线程中回调事件处理函数,但事件会在抛出后的下一帧分发。 + /// + /// 事件源。 + /// 事件参数。 + void Fire(object sender, GameEventArgs e); + + /// + /// 抛出事件立即模式,这个操作不是线程安全的,事件会立刻分发。 + /// + /// 事件源。 + /// 事件参数。 + void FireNow(object sender, GameEventArgs e); + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/IEventManager.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/IEventManager.cs.meta new file mode 100644 index 00000000..a652618c --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/IEventManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 056d52252266e8f419ec195f7908a878 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/NetEvent.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/NetEvent.cs new file mode 100644 index 00000000..b12813cf --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/NetEvent.cs @@ -0,0 +1,111 @@ +using System; + +namespace TEngine.Runtime +{ + public class NetEvent:BehaviourSingleton + { + private IEventManager m_EventManager = null; + + /// + /// 获取事件处理函数的数量。 + /// + public int EventHandlerCount + { + get + { + return m_EventManager.EventHandlerCount; + } + } + + /// + /// 获取事件数量。 + /// + public int EventCount + { + get + { + return m_EventManager.EventCount; + } + } + + public override void Awake() + { + base.Awake(); + + m_EventManager = new EventManager(); + if (m_EventManager == null) + { + Log.Fatal("Event manager is invalid."); + return; + } + }/// + /// 获取事件处理函数的数量。 + /// + /// 事件类型编号。 + /// 事件处理函数的数量。 + public int Count(int id) + { + return m_EventManager.Count(id); + } + + /// + /// 检查是否存在事件处理函数。 + /// + /// 事件类型编号。 + /// 要检查的事件处理函数。 + /// 是否存在事件处理函数。 + public bool Check(int id, EventHandler handler) + { + return m_EventManager.Check(id, handler); + } + + /// + /// 订阅事件处理回调函数。 + /// + /// 事件类型编号。 + /// 要订阅的事件处理回调函数。 + public void Subscribe(int id, EventHandler handler) + { + m_EventManager.Subscribe(id, handler); + } + + /// + /// 取消订阅事件处理回调函数。 + /// + /// 事件类型编号。 + /// 要取消订阅的事件处理回调函数。 + public void Unsubscribe(int id, EventHandler handler) + { + m_EventManager.Unsubscribe(id, handler); + } + + /// + /// 设置默认事件处理函数。 + /// + /// 要设置的默认事件处理函数。 + public void SetDefaultHandler(EventHandler handler) + { + m_EventManager.SetDefaultHandler(handler); + } + + /// + /// 抛出事件,这个操作是线程安全的,即使不在主线程中抛出,也可保证在主线程中回调事件处理函数,但事件会在抛出后的下一帧分发。 + /// + /// 事件发送者。 + /// 事件内容。 + public void Fire(object sender, GameEventArgs e) + { + m_EventManager.Fire(sender, e); + } + + /// + /// 抛出事件立即模式,这个操作不是线程安全的,事件会立刻分发。 + /// + /// 事件发送者。 + /// 事件内容。 + public void FireNow(object sender, GameEventArgs e) + { + m_EventManager.FireNow(sender, e); + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/NetEvent.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/NetEvent.cs.meta new file mode 100644 index 00000000..a9ef11ec --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Event/NetEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4e42aa2c6d194fd59c82dc3c597d9152 +timeCreated: 1661775598 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkChannel.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkChannel.cs new file mode 100644 index 00000000..b430dd2e --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkChannel.cs @@ -0,0 +1,157 @@ +using System; +using System.Net; +using System.Net.Sockets; + +namespace TEngine.Runtime +{ + /// + /// 网络频道接口。 + /// + public interface INetworkChannel + { + /// + /// 获取网络频道名称。 + /// + string Name + { + get; + } + + /// + /// 获取网络频道所使用的 Socket。 + /// + Socket Socket + { + get; + } + + /// + /// 获取是否已连接。 + /// + bool Connected + { + get; + } + + /// + /// 获取网络服务类型。 + /// + ServiceType ServiceType + { + get; + } + + /// + /// 获取网络地址类型。 + /// + AddressFamily AddressFamily + { + get; + } + + /// + /// 获取要发送的消息包数量。 + /// + int SendPacketCount + { + get; + } + + /// + /// 获取累计发送的消息包数量。 + /// + int SentPacketCount + { + get; + } + + /// + /// 获取已接收未处理的消息包数量。 + /// + int ReceivePacketCount + { + get; + } + + /// + /// 获取累计已接收的消息包数量。 + /// + int ReceivedPacketCount + { + get; + } + + /// + /// 获取或设置当收到消息包时是否重置心跳流逝时间。 + /// + bool ResetHeartBeatElapseSecondsWhenReceivePacket + { + get; + set; + } + + /// + /// 获取丢失心跳的次数。 + /// + int MissHeartBeatCount + { + get; + } + + /// + /// 获取或设置心跳间隔时长,以秒为单位。 + /// + float HeartBeatInterval + { + get; + set; + } + + /// + /// 获取心跳等待时长,以秒为单位。 + /// + float HeartBeatElapseSeconds + { + get; + } + + /// + /// 注册网络消息包处理函数。 + /// + /// 要注册的网络消息包处理函数。 + void RegisterHandler(IPacketHandler handler); + + /// + /// 设置默认事件处理函数。 + /// + /// 要设置的默认事件处理函数。 + void SetDefaultHandler(EventHandler handler); + + /// + /// 连接到远程主机。 + /// + /// 远程主机的 IP 地址。 + /// 远程主机的端口号。 + void Connect(IPAddress ipAddress, int port); + + /// + /// 连接到远程主机。 + /// + /// 远程主机的 IP 地址。 + /// 远程主机的端口号。 + /// 用户自定义数据。 + void Connect(IPAddress ipAddress, int port, object userData); + + /// + /// 关闭网络频道。 + /// + void Close(); + + /// + /// 向远程主机发送消息包。 + /// + /// 消息包类型。 + /// 要发送的消息包。 + void Send(T packet) where T : Packet; + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkChannel.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkChannel.cs.meta new file mode 100644 index 00000000..474bacd3 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkChannel.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c66ee24862094df49e089d63f7481285 +timeCreated: 1661771701 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkChannelHelper.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkChannelHelper.cs new file mode 100644 index 00000000..40c855e5 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkChannelHelper.cs @@ -0,0 +1,66 @@ +using System.IO; + +namespace TEngine.Runtime +{ + /// + /// 网络频道辅助器接口。 + /// + public interface INetworkChannelHelper + { + /// + /// 获取消息包头长度。 + /// + int PacketHeaderLength + { + get; + } + + /// + /// 初始化网络频道辅助器。 + /// + /// 网络频道。 + void Initialize(INetworkChannel networkChannel); + + /// + /// 关闭并清理网络频道辅助器。 + /// + void Shutdown(); + + /// + /// 准备进行连接。 + /// + void PrepareForConnecting(); + + /// + /// 发送心跳消息包。 + /// + /// 是否发送心跳消息包成功。 + bool SendHeartBeat(); + + /// + /// 序列化消息包。 + /// + /// 消息包类型。 + /// 要序列化的消息包。 + /// 要序列化的目标流。 + /// 是否序列化成功。 + bool Serialize(T packet, Stream destination) where T : Packet; + + /// + /// 反序列化消息包头。 + /// + /// 要反序列化的来源流。 + /// 用户自定义错误数据。 + /// 反序列化后的消息包头。 + IPacketHeader DeserializePacketHeader(Stream source, out object customErrorData); + + /// + /// 反序列化消息包。 + /// + /// 消息包头。 + /// 要反序列化的来源流。 + /// 用户自定义错误数据。 + /// 反序列化后的消息包。 + Packet DeserializePacket(IPacketHeader packetHeader, Stream source, out object customErrorData); + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkChannelHelper.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkChannelHelper.cs.meta new file mode 100644 index 00000000..495fc074 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkChannelHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: aeff0f53fdd54bd489d6a6141e79dca0 +timeCreated: 1661771934 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkManager.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkManager.cs new file mode 100644 index 00000000..600c32b8 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkManager.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; + +namespace TEngine.Runtime +{ + /// + /// 网络管理器接口。 + /// + public interface INetworkManager + { + /// + /// 获取网络频道数量。 + /// + int NetworkChannelCount + { + get; + } + + /// + /// 网络连接成功事件。 + /// + event EventHandler NetworkConnected; + + /// + /// 网络连接关闭事件。 + /// + event EventHandler NetworkClosed; + + /// + /// 网络心跳包丢失事件。 + /// + event EventHandler NetworkMissHeartBeat; + + /// + /// 网络错误事件。 + /// + event EventHandler NetworkError; + + /// + /// 用户自定义网络错误事件。 + /// + event EventHandler NetworkCustomError; + + /// + /// 检查是否存在网络频道。 + /// + /// 网络频道名称。 + /// 是否存在网络频道。 + bool HasNetworkChannel(string name); + + /// + /// 获取网络频道。 + /// + /// 网络频道名称。 + /// 要获取的网络频道。 + INetworkChannel GetNetworkChannel(string name); + + /// + /// 获取所有网络频道。 + /// + /// 所有网络频道。 + INetworkChannel[] GetAllNetworkChannels(); + + /// + /// 获取所有网络频道。 + /// + /// 所有网络频道。 + void GetAllNetworkChannels(List results); + + /// + /// 创建网络频道。 + /// + /// 网络频道名称。 + /// 网络服务类型。 + /// 网络频道辅助器。 + /// 要创建的网络频道。 + INetworkChannel CreateNetworkChannel(string name, ServiceType serviceType, INetworkChannelHelper networkChannelHelper); + + /// + /// 销毁网络频道。 + /// + /// 网络频道名称。 + /// 是否销毁网络频道成功。 + bool DestroyNetworkChannel(string name); + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkManager.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkManager.cs.meta new file mode 100644 index 00000000..b8132e5a --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/INetworkManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: aa9566de56cd4b579631a1725b89174e +timeCreated: 1661775465 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/IPacketHandler.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/IPacketHandler.cs new file mode 100644 index 00000000..de6d9a27 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/IPacketHandler.cs @@ -0,0 +1,23 @@ +namespace TEngine.Runtime +{ + /// + /// 网络消息包处理器接口。 + /// + public interface IPacketHandler + { + /// + /// 获取网络消息包协议编号。 + /// + int Id + { + get; + } + + /// + /// 网络消息包处理函数。 + /// + /// 网络消息包源。 + /// 网络消息包内容。 + void Handle(object sender, Packet packet); + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/IPacketHandler.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/IPacketHandler.cs.meta new file mode 100644 index 00000000..b1d1635b --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/IPacketHandler.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 49d9ce041dfb4dc98f954dd85e7b9015 +timeCreated: 1661771824 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/IPacketHeader.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/IPacketHeader.cs new file mode 100644 index 00000000..48877019 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/IPacketHeader.cs @@ -0,0 +1,16 @@ +namespace TEngine.Runtime +{ + /// + /// 网络消息包头接口。 + /// + public interface IPacketHeader + { + /// + /// 获取网络消息包长度。 + /// + int PacketLength + { + get; + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/IPacketHeader.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/IPacketHeader.cs.meta new file mode 100644 index 00000000..888bf894 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/IPacketHeader.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1a61d331daf74fe48bffbb083f016cb5 +timeCreated: 1661771898 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Network.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Network.cs new file mode 100644 index 00000000..f4b6a290 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Network.cs @@ -0,0 +1,139 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace TEngine.Runtime +{ + /// + /// 网络组件。 + /// + [DisallowMultipleComponent] + [AddComponentMenu("TEngine/Network")] + public class Network : UnitySingleton + { + private INetworkManager m_NetworkManager = null; + private NetEvent m_NetEvent = NetEvent.Instance; + + /// + /// 获取网络频道数量。 + /// + public int NetworkChannelCount + { + get + { + return m_NetworkManager.NetworkChannelCount; + } + } + + public override void Awake() + { + base.Awake(); + m_NetworkManager = new NetworkManager(); + if (m_NetworkManager == null) + { + Log.Fatal("Network manager is invalid."); + return; + } + + m_NetworkManager.NetworkConnected += OnNetworkConnected; + m_NetworkManager.NetworkClosed += OnNetworkClosed; + m_NetworkManager.NetworkMissHeartBeat += OnNetworkMissHeartBeat; + m_NetworkManager.NetworkError += OnNetworkError; + m_NetworkManager.NetworkCustomError += OnNetworkCustomError; + } + + protected override void OnLoad() + { + base.OnLoad(); + if (m_NetEvent == null) + { + Log.Fatal("Event component is invalid."); + return; + } + } + + /// + /// 检查是否存在网络频道。 + /// + /// 网络频道名称。 + /// 是否存在网络频道。 + public bool HasNetworkChannel(string name) + { + return m_NetworkManager.HasNetworkChannel(name); + } + + /// + /// 获取网络频道。 + /// + /// 网络频道名称。 + /// 要获取的网络频道。 + public INetworkChannel GetNetworkChannel(string name) + { + return m_NetworkManager.GetNetworkChannel(name); + } + + /// + /// 获取所有网络频道。 + /// + /// 所有网络频道。 + public INetworkChannel[] GetAllNetworkChannels() + { + return m_NetworkManager.GetAllNetworkChannels(); + } + + /// + /// 获取所有网络频道。 + /// + /// 所有网络频道。 + public void GetAllNetworkChannels(List results) + { + m_NetworkManager.GetAllNetworkChannels(results); + } + + /// + /// 创建网络频道。 + /// + /// 网络频道名称。 + /// 网络服务类型。 + /// 网络频道辅助器。 + /// 要创建的网络频道。 + public INetworkChannel CreateNetworkChannel(string name, ServiceType serviceType, INetworkChannelHelper networkChannelHelper) + { + return m_NetworkManager.CreateNetworkChannel(name, serviceType, networkChannelHelper); + } + + /// + /// 销毁网络频道。 + /// + /// 网络频道名称。 + /// 是否销毁网络频道成功。 + public bool DestroyNetworkChannel(string name) + { + return m_NetworkManager.DestroyNetworkChannel(name); + } + + private void OnNetworkConnected(object sender, NetworkConnectedEventArgs e) + { + m_NetEvent.Fire(this, e); + } + + private void OnNetworkClosed(object sender, NetworkClosedEventArgs e) + { + m_NetEvent.Fire(this, e); + } + + private void OnNetworkMissHeartBeat(object sender, NetworkMissHeartBeatEventArgs e) + { + m_NetEvent.Fire(this, e); + } + + private void OnNetworkError(object sender, NetworkErrorEventArgs e) + { + m_NetEvent.Fire(this, e); + } + + private void OnNetworkCustomError(object sender, NetworkCustomErrorEventArgs e) + { + m_NetEvent.Fire(this, e); + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Network.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Network.cs.meta new file mode 100644 index 00000000..1760a35a --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Network.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1dd2b00e963a5b54db7aae1b87628982 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkErrorCode.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkErrorCode.cs new file mode 100644 index 00000000..2ceade3c --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkErrorCode.cs @@ -0,0 +1,53 @@ +namespace TEngine.Runtime +{ + /// + /// 网络错误码。 + /// + public enum NetworkErrorCode : byte + { + /// + /// 未知错误。 + /// + Unknown = 0, + + /// + /// 地址族错误。 + /// + AddressFamilyError, + + /// + /// Socket 错误。 + /// + SocketError, + + /// + /// 连接错误。 + /// + ConnectError, + + /// + /// 发送错误。 + /// + SendError, + + /// + /// 接收错误。 + /// + ReceiveError, + + /// + /// 序列化错误。 + /// + SerializeError, + + /// + /// 反序列化消息包头错误。 + /// + DeserializePacketHeaderError, + + /// + /// 反序列化消息包错误。 + /// + DeserializePacketError + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkErrorCode.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkErrorCode.cs.meta new file mode 100644 index 00000000..35a0f13b --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkErrorCode.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 86fe9208ee30402ab1fcdc8cdcff1d89 +timeCreated: 1661771970 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.ConnectState.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.ConnectState.cs new file mode 100644 index 00000000..3bd4b7f6 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.ConnectState.cs @@ -0,0 +1,35 @@ +using System.Net.Sockets; + +namespace TEngine.Runtime +{ + public sealed partial class NetworkManager + { + private sealed class ConnectState + { + private readonly Socket m_Socket; + private readonly object m_UserData; + + public ConnectState(Socket socket, object userData) + { + m_Socket = socket; + m_UserData = userData; + } + + public Socket Socket + { + get + { + return m_Socket; + } + } + + public object UserData + { + get + { + return m_UserData; + } + } + } + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.ConnectState.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.ConnectState.cs.meta new file mode 100644 index 00000000..ccd0558f --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.ConnectState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 07167c4e7b24c2148acdfe52fbe0c817 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.HeartBeatState.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.HeartBeatState.cs new file mode 100644 index 00000000..5220221e --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.HeartBeatState.cs @@ -0,0 +1,51 @@ +namespace TEngine.Runtime +{ + public sealed partial class NetworkManager + { + private sealed class HeartBeatState + { + private float m_HeartBeatElapseSeconds; + private int m_MissHeartBeatCount; + + public HeartBeatState() + { + m_HeartBeatElapseSeconds = 0f; + m_MissHeartBeatCount = 0; + } + + public float HeartBeatElapseSeconds + { + get + { + return m_HeartBeatElapseSeconds; + } + set + { + m_HeartBeatElapseSeconds = value; + } + } + + public int MissHeartBeatCount + { + get + { + return m_MissHeartBeatCount; + } + set + { + m_MissHeartBeatCount = value; + } + } + + public void Reset(bool resetHeartBeatElapseSeconds) + { + if (resetHeartBeatElapseSeconds) + { + m_HeartBeatElapseSeconds = 0f; + } + + m_MissHeartBeatCount = 0; + } + } + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.HeartBeatState.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.HeartBeatState.cs.meta new file mode 100644 index 00000000..1ba94069 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.HeartBeatState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1beec3505229bd418c252c142a01152 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.NetworkChannelBase.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.NetworkChannelBase.cs new file mode 100644 index 00000000..0406ac3b --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.NetworkChannelBase.cs @@ -0,0 +1,629 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; + +namespace TEngine.Runtime +{ + public sealed partial class NetworkManager + { + /// + /// 网络频道基类。 + /// + private abstract class NetworkChannelBase : INetworkChannel, IDisposable + { + private const float DefaultHeartBeatInterval = 30f; + + private readonly string m_Name; + protected readonly Queue m_SendPacketPool; + protected readonly EventPool m_ReceivePacketPool; + protected readonly INetworkChannelHelper m_NetworkChannelHelper; + protected AddressFamily m_AddressFamily; + protected bool m_ResetHeartBeatElapseSecondsWhenReceivePacket; + protected float m_HeartBeatInterval; + protected Socket m_Socket; + protected readonly SendState m_SendState; + protected readonly ReceiveState m_ReceiveState; + protected readonly HeartBeatState m_HeartBeatState; + protected int m_SentPacketCount; + protected int m_ReceivedPacketCount; + protected bool m_Active; + private bool m_Disposed; + + public Action NetworkChannelConnected; + public Action NetworkChannelClosed; + public Action NetworkChannelMissHeartBeat; + public Action NetworkChannelError; + public Action NetworkChannelCustomError; + + /// + /// 初始化网络频道基类的新实例。 + /// + /// 网络频道名称。 + /// 网络频道辅助器。 + public NetworkChannelBase(string name, INetworkChannelHelper networkChannelHelper) + { + m_Name = name ?? string.Empty; + m_SendPacketPool = new Queue(); + m_ReceivePacketPool = new EventPool(EventPoolMode.Default); + m_NetworkChannelHelper = networkChannelHelper; + m_AddressFamily = AddressFamily.Unknown; + m_ResetHeartBeatElapseSecondsWhenReceivePacket = false; + m_HeartBeatInterval = DefaultHeartBeatInterval; + m_Socket = null; + m_SendState = new SendState(); + m_ReceiveState = new ReceiveState(); + m_HeartBeatState = new HeartBeatState(); + m_SentPacketCount = 0; + m_ReceivedPacketCount = 0; + m_Active = false; + m_Disposed = false; + + NetworkChannelConnected = null; + NetworkChannelClosed = null; + NetworkChannelMissHeartBeat = null; + NetworkChannelError = null; + NetworkChannelCustomError = null; + + networkChannelHelper.Initialize(this); + } + + /// + /// 获取网络频道名称。 + /// + public string Name + { + get + { + return m_Name; + } + } + + /// + /// 获取网络频道所使用的 Socket。 + /// + public Socket Socket + { + get + { + return m_Socket; + } + } + + /// + /// 获取是否已连接。 + /// + public bool Connected + { + get + { + if (m_Socket != null) + { + return m_Socket.Connected; + } + + return false; + } + } + + /// + /// 获取网络服务类型。 + /// + public abstract ServiceType ServiceType + { + get; + } + + /// + /// 获取网络地址类型。 + /// + public AddressFamily AddressFamily + { + get + { + return m_AddressFamily; + } + } + + /// + /// 获取要发送的消息包数量。 + /// + public int SendPacketCount + { + get + { + return m_SendPacketPool.Count; + } + } + + /// + /// 获取累计发送的消息包数量。 + /// + public int SentPacketCount + { + get + { + return m_SentPacketCount; + } + } + + /// + /// 获取已接收未处理的消息包数量。 + /// + public int ReceivePacketCount + { + get + { + return m_ReceivePacketPool.EventCount; + } + } + + /// + /// 获取累计已接收的消息包数量。 + /// + public int ReceivedPacketCount + { + get + { + return m_ReceivedPacketCount; + } + } + + /// + /// 获取或设置当收到消息包时是否重置心跳流逝时间。 + /// + public bool ResetHeartBeatElapseSecondsWhenReceivePacket + { + get + { + return m_ResetHeartBeatElapseSecondsWhenReceivePacket; + } + set + { + m_ResetHeartBeatElapseSecondsWhenReceivePacket = value; + } + } + + /// + /// 获取丢失心跳的次数。 + /// + public int MissHeartBeatCount + { + get + { + return m_HeartBeatState.MissHeartBeatCount; + } + } + + /// + /// 获取或设置心跳间隔时长,以秒为单位。 + /// + public float HeartBeatInterval + { + get + { + return m_HeartBeatInterval; + } + set + { + m_HeartBeatInterval = value; + } + } + + /// + /// 获取心跳等待时长,以秒为单位。 + /// + public float HeartBeatElapseSeconds + { + get + { + return m_HeartBeatState.HeartBeatElapseSeconds; + } + } + + /// + /// 网络频道轮询。 + /// + /// 逻辑流逝时间,以秒为单位。 + /// 真实流逝时间,以秒为单位。 + public virtual void Update(float elapseSeconds, float realElapseSeconds) + { + if (m_Socket == null || !m_Active) + { + return; + } + + ProcessSend(); + ProcessReceive(); + if (m_Socket == null || !m_Active) + { + return; + } + + m_ReceivePacketPool.Update(elapseSeconds, realElapseSeconds); + + if (m_HeartBeatInterval > 0f) + { + bool sendHeartBeat = false; + int missHeartBeatCount = 0; + lock (m_HeartBeatState) + { + if (m_Socket == null || !m_Active) + { + return; + } + + m_HeartBeatState.HeartBeatElapseSeconds += realElapseSeconds; + if (m_HeartBeatState.HeartBeatElapseSeconds >= m_HeartBeatInterval) + { + sendHeartBeat = true; + missHeartBeatCount = m_HeartBeatState.MissHeartBeatCount; + m_HeartBeatState.HeartBeatElapseSeconds = 0f; + m_HeartBeatState.MissHeartBeatCount++; + } + } + + if (sendHeartBeat && m_NetworkChannelHelper.SendHeartBeat()) + { + if (missHeartBeatCount > 0 && NetworkChannelMissHeartBeat != null) + { + NetworkChannelMissHeartBeat(this, missHeartBeatCount); + } + } + } + } + + /// + /// 关闭网络频道。 + /// + public virtual void Shutdown() + { + Close(); + m_ReceivePacketPool.Shutdown(); + m_NetworkChannelHelper.Shutdown(); + } + + /// + /// 注册网络消息包处理函数。 + /// + /// 要注册的网络消息包处理函数。 + public void RegisterHandler(IPacketHandler handler) + { + if (handler == null) + { + throw new Exception("Packet handler is invalid."); + } + + m_ReceivePacketPool.Subscribe(handler.Id, handler.Handle); + } + + /// + /// 设置默认事件处理函数。 + /// + /// 要设置的默认事件处理函数。 + public void SetDefaultHandler(EventHandler handler) + { + m_ReceivePacketPool.SetDefaultHandler(handler); + } + + /// + /// 连接到远程主机。 + /// + /// 远程主机的 IP 地址。 + /// 远程主机的端口号。 + public void Connect(IPAddress ipAddress, int port) + { + Connect(ipAddress, port, null); + } + + /// + /// 连接到远程主机。 + /// + /// 远程主机的 IP 地址。 + /// 远程主机的端口号。 + /// 用户自定义数据。 + public virtual void Connect(IPAddress ipAddress, int port, object userData) + { + if (m_Socket != null) + { + Close(); + m_Socket = null; + } + + switch (ipAddress.AddressFamily) + { + case System.Net.Sockets.AddressFamily.InterNetwork: + m_AddressFamily = AddressFamily.IPv4; + break; + + case System.Net.Sockets.AddressFamily.InterNetworkV6: + m_AddressFamily = 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 Exception(errorMessage); + } + + m_SendState.Reset(); + m_ReceiveState.PrepareForPacketHeader(m_NetworkChannelHelper.PacketHeaderLength); + } + + /// + /// 关闭连接并释放所有相关资源。 + /// + public void Close() + { + lock (this) + { + if (m_Socket == null) + { + return; + } + + m_Active = false; + + try + { + m_Socket.Shutdown(SocketShutdown.Both); + } + catch + { + } + finally + { + m_Socket.Close(); + m_Socket = null; + + if (NetworkChannelClosed != null) + { + NetworkChannelClosed(this); + } + } + + m_SentPacketCount = 0; + m_ReceivedPacketCount = 0; + + lock (m_SendPacketPool) + { + m_SendPacketPool.Clear(); + } + + m_ReceivePacketPool.Clear(); + + lock (m_HeartBeatState) + { + m_HeartBeatState.Reset(true); + } + } + } + + /// + /// 向远程主机发送消息包。 + /// + /// 消息包类型。 + /// 要发送的消息包。 + public void Send(T packet) where T : Packet + { + if (m_Socket == null) + { + string errorMessage = "You must connect first."; + if (NetworkChannelError != null) + { + NetworkChannelError(this, NetworkErrorCode.SendError, SocketError.Success, errorMessage); + return; + } + + throw new Exception(errorMessage); + } + + if (!m_Active) + { + string errorMessage = "Socket is not active."; + if (NetworkChannelError != null) + { + NetworkChannelError(this, NetworkErrorCode.SendError, SocketError.Success, errorMessage); + return; + } + + throw new Exception(errorMessage); + } + + if (packet == null) + { + string errorMessage = "Packet is invalid."; + if (NetworkChannelError != null) + { + NetworkChannelError(this, NetworkErrorCode.SendError, SocketError.Success, errorMessage); + return; + } + + throw new Exception(errorMessage); + } + + lock (m_SendPacketPool) + { + m_SendPacketPool.Enqueue(packet); + } + } + + /// + /// 释放资源。 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// 释放资源。 + /// + /// 释放资源标记。 + private void Dispose(bool disposing) + { + if (m_Disposed) + { + return; + } + + if (disposing) + { + Close(); + m_SendState.Dispose(); + m_ReceiveState.Dispose(); + } + + m_Disposed = true; + } + + protected virtual bool ProcessSend() + { + if (m_SendState.Stream.Length > 0 || m_SendPacketPool.Count <= 0) + { + return false; + } + + while (m_SendPacketPool.Count > 0) + { + Packet packet = null; + lock (m_SendPacketPool) + { + packet = m_SendPacketPool.Dequeue(); + } + + bool serializeResult = false; + try + { + serializeResult = m_NetworkChannelHelper.Serialize(packet, m_SendState.Stream); + } + catch (Exception exception) + { + m_Active = 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) + { + string errorMessage = "Serialized packet failure."; + if (NetworkChannelError != null) + { + NetworkChannelError(this, NetworkErrorCode.SerializeError, SocketError.Success, errorMessage); + return false; + } + + throw new Exception(errorMessage); + } + } + + m_SendState.Stream.Position = 0L; + return true; + } + + protected virtual void ProcessReceive() + { + } + + protected virtual bool ProcessPacketHeader() + { + try + { + object customErrorData = null; + IPacketHeader packetHeader = m_NetworkChannelHelper.DeserializePacketHeader(m_ReceiveState.Stream, out 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 Exception(errorMessage); + } + + m_ReceiveState.PrepareForPacket(packetHeader); + if (packetHeader.PacketLength <= 0) + { + bool processSuccess = ProcessPacket(); + m_ReceivedPacketCount++; + return processSuccess; + } + } + catch (Exception exception) + { + m_Active = 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 (m_HeartBeatState) + { + m_HeartBeatState.Reset(m_ResetHeartBeatElapseSecondsWhenReceivePacket); + } + + try + { + object customErrorData = null; + Packet packet = m_NetworkChannelHelper.DeserializePacket(m_ReceiveState.PacketHeader, m_ReceiveState.Stream, out customErrorData); + + if (customErrorData != null && NetworkChannelCustomError != null) + { + NetworkChannelCustomError(this, customErrorData); + } + + if (packet != null) + { + m_ReceivePacketPool.Fire(this, packet); + } + + m_ReceiveState.PrepareForPacketHeader(m_NetworkChannelHelper.PacketHeaderLength); + } + catch (Exception exception) + { + m_Active = 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; + } + } + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.NetworkChannelBase.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.NetworkChannelBase.cs.meta new file mode 100644 index 00000000..a096d5c6 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.NetworkChannelBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d124c3d2afc7006459e261c8d510711c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.ReceiveState.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.ReceiveState.cs new file mode 100644 index 00000000..478b492b --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.ReceiveState.cs @@ -0,0 +1,91 @@ +using System; +using System.IO; + +namespace TEngine.Runtime +{ + public sealed partial class NetworkManager + { + private sealed class ReceiveState : IDisposable + { + private const int DefaultBufferLength = 1024 * 64; + private MemoryStream m_Stream; + private IPacketHeader m_PacketHeader; + private bool m_Disposed; + + public ReceiveState() + { + m_Stream = new MemoryStream(DefaultBufferLength); + m_PacketHeader = null; + m_Disposed = false; + } + + public MemoryStream Stream + { + get + { + return m_Stream; + } + } + + public IPacketHeader PacketHeader + { + get + { + return m_PacketHeader; + } + } + + public void PrepareForPacketHeader(int packetHeaderLength) + { + Reset(packetHeaderLength, null); + } + + public void PrepareForPacket(IPacketHeader packetHeader) + { + if (packetHeader == null) + { + throw new Exception("Packet header is invalid."); + } + + Reset(packetHeader.PacketLength, packetHeader); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (m_Disposed) + { + return; + } + + if (disposing) + { + if (m_Stream != null) + { + m_Stream.Dispose(); + m_Stream = null; + } + } + + m_Disposed = true; + } + + private void Reset(int targetLength, IPacketHeader packetHeader) + { + if (targetLength < 0) + { + throw new Exception("Target length is invalid."); + } + + m_Stream.Position = 0L; + m_Stream.SetLength(targetLength); + m_PacketHeader = packetHeader; + } + } + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.ReceiveState.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.ReceiveState.cs.meta new file mode 100644 index 00000000..5d6a2077 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.ReceiveState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4a8a0b968c7b38442ae9bbf523e6f234 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.SendState.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.SendState.cs new file mode 100644 index 00000000..9a573301 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.SendState.cs @@ -0,0 +1,60 @@ +using System; +using System.IO; + +namespace TEngine.Runtime +{ + public sealed partial class NetworkManager + { + private sealed class SendState : IDisposable + { + private const int DefaultBufferLength = 1024 * 64; + private MemoryStream m_Stream; + private bool m_Disposed; + + public SendState() + { + m_Stream = new MemoryStream(DefaultBufferLength); + m_Disposed = false; + } + + public MemoryStream Stream + { + get + { + return m_Stream; + } + } + + public void Reset() + { + m_Stream.Position = 0L; + m_Stream.SetLength(0L); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (m_Disposed) + { + return; + } + + if (disposing) + { + if (m_Stream != null) + { + m_Stream.Dispose(); + m_Stream = null; + } + } + + m_Disposed = true; + } + } + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.SendState.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.SendState.cs.meta new file mode 100644 index 00000000..1ee0277c --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.SendState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 01a1e8d0d67bac34a9d5ea40a96bb0eb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.cs new file mode 100644 index 00000000..05ecfc53 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.cs @@ -0,0 +1,350 @@ +using System; +using System.Collections.Generic; +using System.Net.Sockets; + +namespace TEngine.Runtime +{ + /// + /// 网络管理器。 + /// + public sealed partial class NetworkManager : INetworkManager + { + private readonly Dictionary m_NetworkChannels; + + private EventHandler m_NetworkConnectedEventHandler; + private EventHandler m_NetworkClosedEventHandler; + private EventHandler m_NetworkMissHeartBeatEventHandler; + private EventHandler m_NetworkErrorEventHandler; + private EventHandler m_NetworkCustomErrorEventHandler; + + /// + /// 初始化网络管理器的新实例。 + /// + public NetworkManager() + { + m_NetworkChannels = new Dictionary(StringComparer.Ordinal); + m_NetworkConnectedEventHandler = null; + m_NetworkClosedEventHandler = null; + m_NetworkMissHeartBeatEventHandler = null; + m_NetworkErrorEventHandler = null; + m_NetworkCustomErrorEventHandler = null; + } + + /// + /// 获取网络频道数量。 + /// + public int NetworkChannelCount + { + get + { + return m_NetworkChannels.Count; + } + } + + /// + /// 网络连接成功事件。 + /// + public event EventHandler NetworkConnected + { + add + { + m_NetworkConnectedEventHandler += value; + } + remove + { + m_NetworkConnectedEventHandler -= value; + } + } + + /// + /// 网络连接关闭事件。 + /// + public event EventHandler NetworkClosed + { + add + { + m_NetworkClosedEventHandler += value; + } + remove + { + m_NetworkClosedEventHandler -= value; + } + } + + /// + /// 网络心跳包丢失事件。 + /// + public event EventHandler NetworkMissHeartBeat + { + add + { + m_NetworkMissHeartBeatEventHandler += value; + } + remove + { + m_NetworkMissHeartBeatEventHandler -= value; + } + } + + /// + /// 网络错误事件。 + /// + public event EventHandler NetworkError + { + add + { + m_NetworkErrorEventHandler += value; + } + remove + { + m_NetworkErrorEventHandler -= value; + } + } + + /// + /// 用户自定义网络错误事件。 + /// + public event EventHandler NetworkCustomError + { + add + { + m_NetworkCustomErrorEventHandler += value; + } + remove + { + m_NetworkCustomErrorEventHandler -= value; + } + } + + /// + /// 网络管理器轮询。 + /// + /// 逻辑流逝时间,以秒为单位。 + /// 真实流逝时间,以秒为单位。 + public void Update(float elapseSeconds, float realElapseSeconds) + { + foreach (KeyValuePair networkChannel in m_NetworkChannels) + { + networkChannel.Value.Update(elapseSeconds, realElapseSeconds); + } + } + + /// + /// 关闭并清理网络管理器。 + /// + public void Shutdown() + { + foreach (KeyValuePair 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(); + } + + /// + /// 检查是否存在网络频道。 + /// + /// 网络频道名称。 + /// 是否存在网络频道。 + public bool HasNetworkChannel(string name) + { + return m_NetworkChannels.ContainsKey(name ?? string.Empty); + } + + /// + /// 获取网络频道。 + /// + /// 网络频道名称。 + /// 要获取的网络频道。 + public INetworkChannel GetNetworkChannel(string name) + { + NetworkChannelBase networkChannel = null; + if (m_NetworkChannels.TryGetValue(name ?? string.Empty, out networkChannel)) + { + return networkChannel; + } + + return null; + } + + /// + /// 获取所有网络频道。 + /// + /// 所有网络频道。 + public INetworkChannel[] GetAllNetworkChannels() + { + int index = 0; + INetworkChannel[] results = new INetworkChannel[m_NetworkChannels.Count]; + foreach (KeyValuePair networkChannel in m_NetworkChannels) + { + results[index++] = networkChannel.Value; + } + + return results; + } + + /// + /// 获取所有网络频道。 + /// + /// 所有网络频道。 + public void GetAllNetworkChannels(List results) + { + if (results == null) + { + throw new Exception("Results is invalid."); + } + + results.Clear(); + foreach (KeyValuePair networkChannel in m_NetworkChannels) + { + results.Add(networkChannel.Value); + } + } + + /// + /// 创建网络频道。 + /// + /// 网络频道名称。 + /// 网络服务类型。 + /// 网络频道辅助器。 + /// 要创建的网络频道。 + public INetworkChannel CreateNetworkChannel(string name, ServiceType serviceType, INetworkChannelHelper networkChannelHelper) + { + if (networkChannelHelper == null) + { + throw new Exception("Network channel helper is invalid."); + } + + if (networkChannelHelper.PacketHeaderLength < 0) + { + throw new Exception("Packet header length is invalid."); + } + + if (HasNetworkChannel(name)) + { + throw new Exception(Utility.Text.Format("Already exist network channel '{0}'.", name ?? string.Empty)); + } + + NetworkChannelBase networkChannel = null; + switch (serviceType) + { + case ServiceType.Tcp: + networkChannel = new TcpNetworkChannel(name, networkChannelHelper); + break; + + case ServiceType.TcpWithSyncReceive: + networkChannel = new TcpWithSyncReceiveNetworkChannel(name, networkChannelHelper); + break; + + case ServiceType.Udp: + networkChannel = new UdpNetworkChannel(name, networkChannelHelper); + break; + + default: + throw new Exception(Utility.Text.Format("Not supported service type '{0}'.", serviceType)); + } + + networkChannel.NetworkChannelConnected += OnNetworkChannelConnected; + networkChannel.NetworkChannelClosed += OnNetworkChannelClosed; + networkChannel.NetworkChannelMissHeartBeat += OnNetworkChannelMissHeartBeat; + networkChannel.NetworkChannelError += OnNetworkChannelError; + networkChannel.NetworkChannelCustomError += OnNetworkChannelCustomError; + m_NetworkChannels.Add(name, networkChannel); + return networkChannel; + } + + /// + /// 销毁网络频道。 + /// + /// 网络频道名称。 + /// 是否销毁网络频道成功。 + public bool DestroyNetworkChannel(string name) + { + NetworkChannelBase networkChannel = null; + if (m_NetworkChannels.TryGetValue(name ?? string.Empty, out networkChannel)) + { + networkChannel.NetworkChannelConnected -= OnNetworkChannelConnected; + networkChannel.NetworkChannelClosed -= OnNetworkChannelClosed; + networkChannel.NetworkChannelMissHeartBeat -= OnNetworkChannelMissHeartBeat; + networkChannel.NetworkChannelError -= OnNetworkChannelError; + networkChannel.NetworkChannelCustomError -= OnNetworkChannelCustomError; + networkChannel.Shutdown(); + return m_NetworkChannels.Remove(name); + } + + return false; + } + + private void OnNetworkChannelConnected(NetworkChannelBase networkChannel, object userData) + { + if (m_NetworkConnectedEventHandler != null) + { + lock (m_NetworkConnectedEventHandler) + { + NetworkConnectedEventArgs networkConnectedEventArgs = NetworkConnectedEventArgs.Create(networkChannel); + m_NetworkConnectedEventHandler(this, networkConnectedEventArgs); + MemoryPool.Release(networkConnectedEventArgs); + } + } + } + + private void OnNetworkChannelClosed(NetworkChannelBase networkChannel) + { + if (m_NetworkClosedEventHandler != null) + { + lock (m_NetworkClosedEventHandler) + { + NetworkClosedEventArgs networkClosedEventArgs = NetworkClosedEventArgs.Create(networkChannel); + m_NetworkClosedEventHandler(this, networkClosedEventArgs); + MemoryPool.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); + MemoryPool.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); + MemoryPool.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); + MemoryPool.Release(networkCustomErrorEventArgs); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.cs.meta new file mode 100644 index 00000000..97a987d3 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/NetworkManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4ea28106108b496bb4b66fd8aa528797 +timeCreated: 1661772444 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Packet.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Packet.cs new file mode 100644 index 00000000..c4e1ee2e --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Packet.cs @@ -0,0 +1,10 @@ +namespace TEngine.Runtime +{ + /// + /// 网络消息包基类。 + /// + public abstract class Packet : BaseEventArgs + { + + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Packet.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Packet.cs.meta new file mode 100644 index 00000000..8435f3b5 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Packet.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4d54f03c4aed4afbab3b4fc45def97c5 +timeCreated: 1661771739 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/ServiceType.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/ServiceType.cs new file mode 100644 index 00000000..bca19f41 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/ServiceType.cs @@ -0,0 +1,28 @@ +namespace TEngine.Runtime +{ + /// + /// 网络服务类型。 + /// + public enum ServiceType : byte + { + /// + /// TCP 网络服务。 + /// + Tcp = 0, + + /// + /// 使用同步接收的 TCP 网络服务。 + /// + TcpWithSyncReceive, + + /// + /// Udp 网络服务。 + /// + Udp, + + /// + /// Kcp 网络服务。 + /// + Kcp, + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/ServiceType.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/ServiceType.cs.meta new file mode 100644 index 00000000..04b3f41a --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/ServiceType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 11929818b1dbde74ba521dc2c5d03f49 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Tcp.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Tcp.meta new file mode 100644 index 00000000..a5c3cd91 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Tcp.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bcf0e9cef581b5441ad604bfc2f771c4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Tcp/NetworkManager.TcpNetworkChannel.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Tcp/NetworkManager.TcpNetworkChannel.cs new file mode 100644 index 00000000..56b6e8e2 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Tcp/NetworkManager.TcpNetworkChannel.cs @@ -0,0 +1,283 @@ +using System; +using System.Net; +using System.Net.Sockets; + +namespace TEngine.Runtime +{ + public sealed partial class NetworkManager + { + /// + /// TCP 网络频道。 + /// + private sealed class TcpNetworkChannel : NetworkChannelBase + { + private readonly AsyncCallback m_ConnectCallback; + private readonly AsyncCallback m_SendCallback; + private readonly AsyncCallback m_ReceiveCallback; + + /// + /// 初始化网络频道的新实例。 + /// + /// 网络频道名称。 + /// 网络频道辅助器。 + public TcpNetworkChannel(string name, INetworkChannelHelper networkChannelHelper) + : base(name, networkChannelHelper) + { + m_ConnectCallback = ConnectCallback; + m_SendCallback = SendCallback; + m_ReceiveCallback = ReceiveCallback; + } + + /// + /// 获取网络服务类型。 + /// + public override ServiceType ServiceType + { + get + { + return ServiceType.Tcp; + } + } + + /// + /// 连接到远程主机。 + /// + /// 远程主机的 IP 地址。 + /// 远程主机的端口号。 + /// 用户自定义数据。 + public override void Connect(IPAddress ipAddress, int port, object userData) + { + base.Connect(ipAddress, port, userData); + m_Socket = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + if (m_Socket == null) + { + string errorMessage = "Initialize network channel failure."; + if (NetworkChannelError != null) + { + NetworkChannelError(this, NetworkErrorCode.SocketError, SocketError.Success, errorMessage); + return; + } + + throw new Exception(errorMessage); + } + + m_NetworkChannelHelper.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 + { + m_Socket.BeginConnect(ipAddress, port, m_ConnectCallback, new ConnectState(m_Socket, 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 + { + socketUserData.Socket.EndConnect(ar); + } + catch (ObjectDisposedException) + { + return; + } + catch (Exception exception) + { + m_Active = false; + if (NetworkChannelError != null) + { + SocketException socketException = exception as SocketException; + NetworkChannelError(this, NetworkErrorCode.ConnectError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString()); + return; + } + + throw; + } + + m_SentPacketCount = 0; + m_ReceivedPacketCount = 0; + + lock (m_SendPacketPool) + { + m_SendPacketPool.Clear(); + } + + m_ReceivePacketPool.Clear(); + + lock (m_HeartBeatState) + { + m_HeartBeatState.Reset(true); + } + + if (NetworkChannelConnected != null) + { + NetworkChannelConnected(this, socketUserData.UserData); + } + + m_Active = true; + ReceiveAsync(); + } + + private void SendAsync() + { + try + { + m_Socket.BeginSend(m_SendState.Stream.GetBuffer(), (int)m_SendState.Stream.Position, (int)(m_SendState.Stream.Length - m_SendState.Stream.Position), SocketFlags.None, m_SendCallback, m_Socket); + } + catch (Exception exception) + { + m_Active = 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 = (Socket)ar.AsyncState; + if (!socket.Connected) + { + return; + } + + int bytesSent = 0; + try + { + bytesSent = socket.EndSend(ar); + } + catch (Exception exception) + { + m_Active = false; + if (NetworkChannelError != null) + { + SocketException socketException = exception as SocketException; + NetworkChannelError(this, NetworkErrorCode.SendError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString()); + return; + } + + throw; + } + + m_SendState.Stream.Position += bytesSent; + if (m_SendState.Stream.Position < m_SendState.Stream.Length) + { + SendAsync(); + return; + } + + m_SentPacketCount++; + m_SendState.Reset(); + } + + private void ReceiveAsync() + { + try + { + m_Socket.BeginReceive(m_ReceiveState.Stream.GetBuffer(), (int)m_ReceiveState.Stream.Position, (int)(m_ReceiveState.Stream.Length - m_ReceiveState.Stream.Position), SocketFlags.None, m_ReceiveCallback, m_Socket); + } + catch (Exception exception) + { + m_Active = 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 = (Socket)ar.AsyncState; + if (!socket.Connected) + { + return; + } + + int bytesReceived = 0; + try + { + bytesReceived = socket.EndReceive(ar); + } + catch (Exception exception) + { + m_Active = 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; + } + + m_ReceiveState.Stream.Position += bytesReceived; + if (m_ReceiveState.Stream.Position < m_ReceiveState.Stream.Length) + { + ReceiveAsync(); + return; + } + + m_ReceiveState.Stream.Position = 0L; + + bool processSuccess = false; + if (m_ReceiveState.PacketHeader != null) + { + processSuccess = ProcessPacket(); + m_ReceivedPacketCount++; + } + else + { + processSuccess = ProcessPacketHeader(); + } + + if (processSuccess) + { + ReceiveAsync(); + return; + } + } + } + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Tcp/NetworkManager.TcpNetworkChannel.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Tcp/NetworkManager.TcpNetworkChannel.cs.meta new file mode 100644 index 00000000..0b1fbb8a --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Tcp/NetworkManager.TcpNetworkChannel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2df585ebfe0562642b66664dbf0feaaf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Tcp/NetworkManager.TcpWithSyncReceiveNetworkChannel.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Tcp/NetworkManager.TcpWithSyncReceiveNetworkChannel.cs new file mode 100644 index 00000000..16b9433f --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Tcp/NetworkManager.TcpWithSyncReceiveNetworkChannel.cs @@ -0,0 +1,259 @@ +using System; +using System.Net; +using System.Net.Sockets; + +namespace TEngine.Runtime +{ + public sealed partial class NetworkManager + { + /// + /// 使用同步接收的 TCP 网络频道。 + /// + private sealed class TcpWithSyncReceiveNetworkChannel : NetworkChannelBase + { + private readonly AsyncCallback m_ConnectCallback; + private readonly AsyncCallback m_SendCallback; + + /// + /// 初始化网络频道的新实例。 + /// + /// 网络频道名称。 + /// 网络频道辅助器。 + public TcpWithSyncReceiveNetworkChannel(string name, INetworkChannelHelper networkChannelHelper) + : base(name, networkChannelHelper) + { + m_ConnectCallback = ConnectCallback; + m_SendCallback = SendCallback; + } + + /// + /// 获取网络服务类型。 + /// + public override ServiceType ServiceType + { + get + { + return ServiceType.TcpWithSyncReceive; + } + } + + /// + /// 连接到远程主机。 + /// + /// 远程主机的 IP 地址。 + /// 远程主机的端口号。 + /// 用户自定义数据。 + public override void Connect(IPAddress ipAddress, int port, object userData) + { + base.Connect(ipAddress, port, userData); + m_Socket = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + if (m_Socket == null) + { + string errorMessage = "Initialize network channel failure."; + if (NetworkChannelError != null) + { + NetworkChannelError(this, NetworkErrorCode.SocketError, SocketError.Success, errorMessage); + return; + } + + throw new Exception(errorMessage); + } + + m_NetworkChannelHelper.PrepareForConnecting(); + ConnectAsync(ipAddress, port, userData); + } + + protected override bool ProcessSend() + { + if (base.ProcessSend()) + { + SendAsync(); + return true; + } + + return false; + } + + protected override void ProcessReceive() + { + base.ProcessReceive(); + while (m_Socket.Available > 0) + { + if (!ReceiveSync()) + { + break; + } + } + } + + private void ConnectAsync(IPAddress ipAddress, int port, object userData) + { + try + { + m_Socket.BeginConnect(ipAddress, port, m_ConnectCallback, new ConnectState(m_Socket, 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 + { + socketUserData.Socket.EndConnect(ar); + } + catch (ObjectDisposedException) + { + return; + } + catch (Exception exception) + { + m_Active = false; + if (NetworkChannelError != null) + { + SocketException socketException = exception as SocketException; + NetworkChannelError(this, NetworkErrorCode.ConnectError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString()); + return; + } + + throw; + } + + m_SentPacketCount = 0; + m_ReceivedPacketCount = 0; + + lock (m_SendPacketPool) + { + m_SendPacketPool.Clear(); + } + + m_ReceivePacketPool.Clear(); + + lock (m_HeartBeatState) + { + m_HeartBeatState.Reset(true); + } + + if (NetworkChannelConnected != null) + { + NetworkChannelConnected(this, socketUserData.UserData); + } + + m_Active = true; + } + + private void SendAsync() + { + try + { + m_Socket.BeginSend(m_SendState.Stream.GetBuffer(), (int)m_SendState.Stream.Position, (int)(m_SendState.Stream.Length - m_SendState.Stream.Position), SocketFlags.None, m_SendCallback, m_Socket); + } + catch (Exception exception) + { + m_Active = 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 = (Socket)ar.AsyncState; + if (!socket.Connected) + { + return; + } + + int bytesSent = 0; + try + { + bytesSent = socket.EndSend(ar); + } + catch (Exception exception) + { + m_Active = false; + if (NetworkChannelError != null) + { + SocketException socketException = exception as SocketException; + NetworkChannelError(this, NetworkErrorCode.SendError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString()); + return; + } + + throw; + } + + m_SendState.Stream.Position += bytesSent; + if (m_SendState.Stream.Position < m_SendState.Stream.Length) + { + SendAsync(); + return; + } + + m_SentPacketCount++; + m_SendState.Reset(); + } + + private bool ReceiveSync() + { + try + { + int bytesReceived = m_Socket.Receive(m_ReceiveState.Stream.GetBuffer(), (int)m_ReceiveState.Stream.Position, (int)(m_ReceiveState.Stream.Length - m_ReceiveState.Stream.Position), SocketFlags.None); + if (bytesReceived <= 0) + { + Close(); + return false; + } + + m_ReceiveState.Stream.Position += bytesReceived; + if (m_ReceiveState.Stream.Position < m_ReceiveState.Stream.Length) + { + return false; + } + + m_ReceiveState.Stream.Position = 0L; + + bool processSuccess = false; + if (m_ReceiveState.PacketHeader != null) + { + processSuccess = ProcessPacket(); + m_ReceivedPacketCount++; + } + else + { + processSuccess = ProcessPacketHeader(); + } + + return processSuccess; + } + catch (Exception exception) + { + m_Active = false; + if (NetworkChannelError != null) + { + SocketException socketException = exception as SocketException; + NetworkChannelError(this, NetworkErrorCode.ReceiveError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString()); + return false; + } + + throw; + } + } + } + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Tcp/NetworkManager.TcpWithSyncReceiveNetworkChannel.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Tcp/NetworkManager.TcpWithSyncReceiveNetworkChannel.cs.meta new file mode 100644 index 00000000..84d39b9d --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Tcp/NetworkManager.TcpWithSyncReceiveNetworkChannel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 19723c1417631894d90eef482924d87d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Udp.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Udp.meta new file mode 100644 index 00000000..74f97f87 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Udp.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 181b125a26149ad4bbc6f270738f37ce +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Udp/UdpNetworkChannel.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Udp/UdpNetworkChannel.cs new file mode 100644 index 00000000..0aa01271 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Udp/UdpNetworkChannel.cs @@ -0,0 +1,276 @@ +using System; +using System.Net; +using System.Net.Sockets; + +namespace TEngine.Runtime +{ + + public sealed partial class NetworkManager + { + private sealed class UdpNetworkChannel : NetworkChannelBase + { + /// + /// 获取网络服务类型。 + /// + public override ServiceType ServiceType => ServiceType.Udp; + + + private readonly AsyncCallback m_ConnectCallback; + private readonly AsyncCallback m_SendCallback; + private readonly AsyncCallback m_ReceiveCallback; + + /// + /// 初始化网络频道的新实例。 + /// + /// 网络频道名称。 + /// 网络频道辅助器。 + public UdpNetworkChannel(string name, INetworkChannelHelper networkChannelHelper) + : base(name, networkChannelHelper) + { + m_ConnectCallback = ConnectCallback; + m_SendCallback = SendCallback; + m_ReceiveCallback = ReceiveCallback; + } + + /// + /// 连接到远程主机。 + /// + /// 远程主机的 IP 地址。 + /// 远程主机的端口号。 + /// 用户自定义数据。 + public override void Connect(IPAddress ipAddress, int port, object userData) + { + base.Connect(ipAddress, port, userData); + m_Socket = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Udp); + if (m_Socket == null) + { + string errorMessage = "Initialize network channel failure."; + if (NetworkChannelError != null) + { + NetworkChannelError(this, NetworkErrorCode.SocketError, SocketError.Success, errorMessage); + return; + } + + throw new Exception(errorMessage); + } + + m_NetworkChannelHelper.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 + { + m_Socket.BeginConnect(ipAddress, port, m_ConnectCallback, new ConnectState(m_Socket, 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 + { + socketUserData.Socket.EndConnect(ar); + } + catch (ObjectDisposedException) + { + return; + } + catch (Exception exception) + { + m_Active = false; + if (NetworkChannelError != null) + { + SocketException socketException = exception as SocketException; + NetworkChannelError(this, NetworkErrorCode.ConnectError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString()); + return; + } + + throw; + } + + m_SentPacketCount = 0; + m_ReceivedPacketCount = 0; + + lock (m_SendPacketPool) + { + m_SendPacketPool.Clear(); + } + + m_ReceivePacketPool.Clear(); + + lock (m_HeartBeatState) + { + m_HeartBeatState.Reset(true); + } + + if (NetworkChannelConnected != null) + { + NetworkChannelConnected(this, socketUserData.UserData); + } + + m_Active = true; + ReceiveAsync(); + } + + private void SendAsync() + { + try + { + m_Socket.BeginSend(m_SendState.Stream.GetBuffer(), (int)m_SendState.Stream.Position, (int)(m_SendState.Stream.Length - m_SendState.Stream.Position), SocketFlags.None, m_SendCallback, m_Socket); + } + catch (Exception exception) + { + m_Active = 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 = (Socket)ar.AsyncState; + if (!socket.Connected) + { + return; + } + + int bytesSent = 0; + try + { + bytesSent = socket.EndSend(ar); + } + catch (Exception exception) + { + m_Active = false; + if (NetworkChannelError != null) + { + SocketException socketException = exception as SocketException; + NetworkChannelError(this, NetworkErrorCode.SendError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString()); + return; + } + + throw; + } + + m_SendState.Stream.Position += bytesSent; + if (m_SendState.Stream.Position < m_SendState.Stream.Length) + { + SendAsync(); + return; + } + + m_SentPacketCount++; + m_SendState.Reset(); + } + + private void ReceiveAsync() + { + try + { + m_Socket.BeginReceive(m_ReceiveState.Stream.GetBuffer(), (int)m_ReceiveState.Stream.Position, (int)(m_ReceiveState.Stream.Length - m_ReceiveState.Stream.Position), SocketFlags.None, m_ReceiveCallback, m_Socket); + } + catch (Exception exception) + { + m_Active = 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 = (Socket)ar.AsyncState; + if (!socket.Connected) + { + return; + } + + int bytesReceived = 0; + try + { + bytesReceived = socket.EndReceive(ar); + } + catch (Exception exception) + { + m_Active = 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; + } + + m_ReceiveState.Stream.Position += bytesReceived; + if (m_ReceiveState.Stream.Position < m_ReceiveState.Stream.Length) + { + ReceiveAsync(); + return; + } + + m_ReceiveState.Stream.Position = 0L; + + bool processSuccess = false; + if (m_ReceiveState.PacketHeader != null) + { + processSuccess = ProcessPacket(); + m_ReceivedPacketCount++; + } + else + { + processSuccess = ProcessPacketHeader(); + } + + if (processSuccess) + { + ReceiveAsync(); + return; + } + } + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Udp/UdpNetworkChannel.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Udp/UdpNetworkChannel.cs.meta new file mode 100644 index 00000000..8430dd16 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Udp/UdpNetworkChannel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 17b9e06584f10da45a8a3ca27fd5e654 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: