diff --git a/Assets/TEngine/Scripts/Editor/Helper/ProtoGenTools.meta b/Assets/TEngine/Scripts/Editor/Helper/ProtoGenTools.meta new file mode 100644 index 00000000..2e886838 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/Helper/ProtoGenTools.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4843da953596d1546bc7ccc26fb5e3b3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/Helper/ProtoGenTools/ProtoGenTools.cs b/Assets/TEngine/Scripts/Editor/Helper/ProtoGenTools/ProtoGenTools.cs new file mode 100644 index 00000000..16bf9d33 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/Helper/ProtoGenTools/ProtoGenTools.cs @@ -0,0 +1,291 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TEngine.Runtime; +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace TEngine.Editor +{ + internal class OpcodeInfo + { + public string Name; + public int Opcode; + } + + public static class ProtoGenTools + { +#if UNITY_EDITOR + [MenuItem("TEngine/生成Proto|Gen Proto", false, 10)] +#endif + public static void Export() + { + InnerProto2CS.Proto2CS(); + Log.Info("proto2cs succeed!"); + } + } + + public static class InnerProto2CS + { + private static string ProtoPath = UnityEngine.Application.dataPath + "\\TEngine\\Tools~\\Protobuf\\Proto\\"; + + private static string OutPutPath = + UnityEngine.Application.dataPath + "\\TEngine\\Tools~\\Protobuf\\Proto_CSharp\\"; + + private static readonly char[] splitChars = { ' ', '\t' }; + private static readonly List msgOpcode = new List(); + + public static void Proto2CS() + { + msgOpcode.Clear(); + Proto2CS("TEngineProto", "TEngineProto.proto", OutPutPath,10001,false); + } + + public static void Proto2CS(string ns, string protoName, string outputPath, int startOpcode,bool useMemoryPool = false) + { + if (!Directory.Exists(outputPath)) + { + Directory.CreateDirectory(outputPath); + } + + msgOpcode.Clear(); + string proto = Path.Combine(ProtoPath, protoName); + string csPath = Path.Combine(outputPath, Path.GetFileNameWithoutExtension(proto) + ".cs"); + + string s = File.ReadAllText(proto); + + StringBuilder sb = new StringBuilder(); + sb.Append("using ProtoBuf;\n"); + sb.Append("using TEngine.Runtime;\n"); + sb.Append("using System.Collections.Generic;\n"); + sb.Append($"namespace {ns}\n"); + sb.Append("{\n"); + + bool isMsgStart = false; + bool isEnumStart = false; + foreach (string line in s.Split('\n')) + { + string newline = line.Trim(); + + if (newline == "") + { + continue; + } + + if (newline.StartsWith("//ResponseType")) + { + string responseType = line.Split(' ')[1].TrimEnd('\r', '\n'); + sb.AppendLine($"\t[ResponseType(nameof({responseType}))]"); + continue; + } + + if (newline.StartsWith("//")) + { + sb.Append($"{newline}\n"); + continue; + } + + if (newline.StartsWith("message")) + { + string parentClass = ""; + isMsgStart = true; + string msgName = newline.Split(splitChars, StringSplitOptions.RemoveEmptyEntries)[1]; + string[] ss = newline.Split(new[] { "//" }, StringSplitOptions.RemoveEmptyEntries); + + if (ss.Length == 2) + { + parentClass = ss[1].Trim(); + } + + msgOpcode.Add(new OpcodeInfo() { Name = msgName, Opcode = ++startOpcode }); + sb.Append($"\t[global::ProtoBuf.ProtoContract()]\n"); + if (useMemoryPool) + { + sb.Append($"\tpublic partial class {msgName}: IMemory"); + } + else + { + sb.Append($"\tpublic partial class {msgName}"); + } + if (parentClass != "") + { + sb.Append($", {parentClass}\n"); + } + else + { + sb.Append("\n"); + } + + continue; + } + + if (isMsgStart) + { + if (newline == "{") + { + sb.Append("\t{\n"); + continue; + } + + if (newline == "}") + { + isMsgStart = false; + sb.Append("\t}\n\n"); + continue; + } + + if (newline.Trim().StartsWith("//")) + { + sb.AppendLine(newline); + continue; + } + + if (newline.Trim() != "" && newline != "}") + { + if (newline.StartsWith("repeated")) + { + Repeated(sb, ns, newline); + } + else + { + Members(sb, newline, true); + } + } + } + + + if (newline.StartsWith("enum")) + { + isEnumStart = true; + string enumName = newline.Split(splitChars, StringSplitOptions.RemoveEmptyEntries)[1]; + + sb.Append($"\t[global::ProtoBuf.ProtoContract()]\n"); + sb.Append($"\tpublic enum {enumName}"); + sb.Append("\n"); + continue; + } + + if (isEnumStart) + { + if (newline == "{") + { + sb.Append("\t{\n"); + continue; + } + + if (newline == "}") + { + isEnumStart = false; + sb.Append("\t}\n\n"); + continue; + } + + if (newline.Trim().StartsWith("//")) + { + sb.AppendLine(newline); + continue; + } + + int index = newline.IndexOf(";"); + newline = newline.Remove(index); + sb.Append($"\t\t{newline},\n\n"); + } + } + + sb.Append("}\n"); + using (FileStream txt = new FileStream(csPath, FileMode.Create, FileAccess.ReadWrite)) + { + using (StreamWriter sw = new StreamWriter(txt)) + { + Log.Debug(sb.ToString()); + sw.Write(sb.ToString()); + } + } + } + + private static void Repeated(StringBuilder sb, string ns, string newline) + { + try + { + int index = newline.IndexOf(";"); + newline = newline.Remove(index); + string[] ss = newline.Split(splitChars, StringSplitOptions.RemoveEmptyEntries); + string type = ss[1]; + type = ConvertType(type); + string name = ss[2]; + int n = int.Parse(ss[4]); + + sb.Append($"\t\t[global::ProtoBuf.ProtoMember({n})]\n"); + sb.Append($"\t\tpublic List<{type}> {name} = new List<{type}>();\n\n"); + } + catch (Exception e) + { + Console.WriteLine($"{newline}\n {e}"); + } + } + + private static string ConvertType(string type) + { + string typeCs = ""; + switch (type) + { + case "int16": + typeCs = "short"; + break; + case "int32": + typeCs = "int"; + break; + case "bytes": + typeCs = "byte[]"; + break; + case "uint32": + typeCs = "uint"; + break; + case "long": + typeCs = "long"; + break; + case "int64": + typeCs = "long"; + break; + case "uint64": + typeCs = "ulong"; + break; + case "uint16": + typeCs = "ushort"; + break; + default: + typeCs = type; + break; + } + + return typeCs; + } + + private static void Members(StringBuilder sb, string newline, bool isRequired) + { + try + { + int index = newline.IndexOf(";"); + newline = newline.Remove(index); + string[] ss = newline.Split(splitChars, StringSplitOptions.RemoveEmptyEntries); + string type = ss[0]; + string name = ss[1]; + int n = int.Parse(ss[3]); + string typeCs = ConvertType(type); + + sb.Append($"\t\t[global::ProtoBuf.ProtoMember({n})]\n"); + if (string.Equals(type,"string")) + { + sb.Append($"\t\t[global::System.ComponentModel.DefaultValue(\"\")]\n"); + } + sb.Append($"\t\tpublic {typeCs} {name} {{ get; set; }}\n\n"); + } + catch (Exception e) + { + Console.WriteLine($"{newline}\n {e}"); + } + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Editor/Helper/ProtoGenTools/ProtoGenTools.cs.meta b/Assets/TEngine/Scripts/Editor/Helper/ProtoGenTools/ProtoGenTools.cs.meta new file mode 100644 index 00000000..c5eda390 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/Helper/ProtoGenTools/ProtoGenTools.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9035b84a2db6d8142a2e10604836e302 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/Inspector/NetworkInspector.cs b/Assets/TEngine/Scripts/Editor/Inspector/NetworkInspector.cs index ccbc3f2d..f12eb5b3 100644 --- a/Assets/TEngine/Scripts/Editor/Inspector/NetworkInspector.cs +++ b/Assets/TEngine/Scripts/Editor/Inspector/NetworkInspector.cs @@ -1,67 +1,67 @@ -// using UnityEditor; -// using UnityEngine; -// using TEngine.Runtime; -// -// namespace TEngine.Editor -// { -// [CustomEditor(typeof(TEngine.Runtime.Network))] -// internal sealed class NetworkComponentInspector : TEngineInspector -// { -// public override void OnInspectorGUI() -// { -// base.OnInspectorGUI(); -// -// if (!EditorApplication.isPlaying) -// { -// EditorGUILayout.HelpBox("Available during runtime only.", MessageType.Info); -// return; -// } -// -// TEngine.Runtime.Network t = (TEngine.Runtime.Network)target; -// -// if (IsPrefabInHierarchy(t.gameObject)) -// { -// EditorGUILayout.LabelField("Network Channel Count", t.NetworkChannelCount.ToString()); -// -// INetworkChannel[] networkChannels = t.GetAllNetworkChannels(); -// foreach (INetworkChannel networkChannel in networkChannels) -// { -// DrawNetworkChannel(networkChannel); -// } -// } -// -// Repaint(); -// } -// -// private void OnEnable() -// { -// } -// -// private void DrawNetworkChannel(INetworkChannel networkChannel) -// { -// EditorGUILayout.BeginVertical("box"); -// { -// EditorGUILayout.LabelField(networkChannel.Name, networkChannel.Connected ? "Connected" : "Disconnected"); -// EditorGUILayout.LabelField("Service Type", networkChannel.ServiceType.ToString()); -// EditorGUILayout.LabelField("Address Family", networkChannel.AddressFamily.ToString()); -// EditorGUILayout.LabelField("Local Address", networkChannel.Connected ? networkChannel.Socket.LocalEndPoint.ToString() : "Unavailable"); -// EditorGUILayout.LabelField("Remote Address", networkChannel.Connected ? networkChannel.Socket.RemoteEndPoint.ToString() : "Unavailable"); -// EditorGUILayout.LabelField("Send Packet", Utility.Text.Format("{0} / {1}", networkChannel.SendPacketCount.ToString(), networkChannel.SentPacketCount.ToString())); -// EditorGUILayout.LabelField("Receive Packet", Utility.Text.Format("{0} / {1}", networkChannel.ReceivePacketCount.ToString(), networkChannel.ReceivedPacketCount.ToString())); -// EditorGUILayout.LabelField("Miss Heart Beat Count", networkChannel.MissHeartBeatCount.ToString()); -// EditorGUILayout.LabelField("Heart Beat", Utility.Text.Format("{0} / {1}", networkChannel.HeartBeatElapseSeconds.ToString("F2"), networkChannel.HeartBeatInterval.ToString("F2"))); -// EditorGUI.BeginDisabledGroup(!networkChannel.Connected); -// { -// if (GUILayout.Button("Disconnect")) -// { -// networkChannel.Close(); -// } -// } -// EditorGUI.EndDisabledGroup(); -// } -// EditorGUILayout.EndVertical(); -// -// EditorGUILayout.Separator(); -// } -// } -// } +using UnityEditor; +using UnityEngine; +using TEngine.Runtime; + +namespace TEngine.Editor +{ + [CustomEditor(typeof(TEngine.Runtime.Network))] + internal sealed class NetworkComponentInspector : TEngineInspector + { + public override void OnInspectorGUI() + { + base.OnInspectorGUI(); + + if (!EditorApplication.isPlaying) + { + EditorGUILayout.HelpBox("Available during runtime only.", MessageType.Info); + return; + } + + TEngine.Runtime.Network t = (TEngine.Runtime.Network)target; + + if (IsPrefabInHierarchy(t.gameObject)) + { + EditorGUILayout.LabelField("Network Channel Count", t.NetworkChannelCount.ToString()); + + INetworkChannel[] networkChannels = t.GetAllNetworkChannels(); + foreach (INetworkChannel networkChannel in networkChannels) + { + DrawNetworkChannel(networkChannel); + } + } + + Repaint(); + } + + private void OnEnable() + { + } + + private void DrawNetworkChannel(INetworkChannel networkChannel) + { + EditorGUILayout.BeginVertical("box"); + { + EditorGUILayout.LabelField(networkChannel.Name, networkChannel.Connected ? "Connected" : "Disconnected"); + EditorGUILayout.LabelField("Service Type", networkChannel.ServiceType.ToString()); + EditorGUILayout.LabelField("Address Family", networkChannel.AddressFamily.ToString()); + EditorGUILayout.LabelField("Local Address", networkChannel.Connected ? networkChannel.Socket.LocalEndPoint.ToString() : "Unavailable"); + EditorGUILayout.LabelField("Remote Address", networkChannel.Connected ? networkChannel.Socket.RemoteEndPoint.ToString() : "Unavailable"); + EditorGUILayout.LabelField("Send Packet", Utility.Text.Format("{0} / {1}", networkChannel.SendPacketCount.ToString(), networkChannel.SentPacketCount.ToString())); + EditorGUILayout.LabelField("Receive Packet", Utility.Text.Format("{0} / {1}", networkChannel.ReceivedPacketCount.ToString(), networkChannel.ReceivedPacketCount.ToString())); + EditorGUILayout.LabelField("Miss Heart Beat Count", networkChannel.MissHeartBeatCount.ToString()); + EditorGUILayout.LabelField("Heart Beat", Utility.Text.Format("{0} / {1}", networkChannel.HeartBeatElapseSeconds.ToString("F2"), networkChannel.HeartBeatInterval.ToString("F2"))); + EditorGUI.BeginDisabledGroup(!networkChannel.Connected); + { + if (GUILayout.Button("Disconnect")) + { + networkChannel.Close(); + } + } + EditorGUI.EndDisabledGroup(); + } + EditorGUILayout.EndVertical(); + + EditorGUILayout.Separator(); + } + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/Base/Debugger/Imp/DebuggerComponent.NetworkInformationWindow.cs b/Assets/TEngine/Scripts/Runtime/Core/Base/Debugger/Imp/DebuggerComponent.NetworkInformationWindow.cs new file mode 100644 index 00000000..fda15b77 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/Base/Debugger/Imp/DebuggerComponent.NetworkInformationWindow.cs @@ -0,0 +1,61 @@ +using UnityEngine; + +namespace TEngine.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class NetworkInformationWindow : ScrollableDebuggerWindowBase + { + private Network m_NetworkComponent = null; + + public override void Initialize(params object[] args) + { + m_NetworkComponent = Network.Instance; + if (m_NetworkComponent == null) + { + Log.Fatal("Network component is invalid."); + return; + } + } + + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Network Information"); + GUILayout.BeginVertical("box"); + { + DrawItem("Network Channel Count", m_NetworkComponent.NetworkChannelCount.ToString()); + } + GUILayout.EndVertical(); + INetworkChannel[] networkChannels = m_NetworkComponent.GetAllNetworkChannels(); + for (int i = 0; i < networkChannels.Length; i++) + { + DrawNetworkChannel(networkChannels[i]); + } + } + + private void DrawNetworkChannel(INetworkChannel networkChannel) + { + GUILayout.Label(Utility.Text.Format("Network Channel: {0} ({1})", networkChannel.Name, networkChannel.Connected ? "Connected" : "Disconnected")); + GUILayout.BeginVertical("box"); + { + DrawItem("Service Type", networkChannel.ServiceType.ToString()); + DrawItem("Address Family", networkChannel.AddressFamily.ToString()); + DrawItem("Local Address", networkChannel.Connected ? networkChannel.Socket.LocalEndPoint.ToString() : "Unavailable"); + DrawItem("Remote Address", networkChannel.Connected ? networkChannel.Socket.RemoteEndPoint.ToString() : "Unavailable"); + DrawItem("Send Packet", Utility.Text.Format("{0} / {1}", networkChannel.SendPacketCount.ToString(), networkChannel.SentPacketCount.ToString())); + DrawItem("Receive Packet", Utility.Text.Format("{0} / {1}", networkChannel.ReceivedPacketCount.ToString(), networkChannel.ReceivedPacketCount.ToString())); + DrawItem("Miss Heart Beat Count", networkChannel.MissHeartBeatCount.ToString()); + DrawItem("Heart Beat", Utility.Text.Format("{0} / {1}", networkChannel.HeartBeatElapseSeconds.ToString("F2"), networkChannel.HeartBeatInterval.ToString("F2"))); + if (networkChannel.Connected) + { + if (GUILayout.Button("Disconnect", GUILayout.Height(30f))) + { + networkChannel.Close(); + } + } + } + GUILayout.EndVertical(); + } + } + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/Base/Debugger/Imp/DebuggerComponent.NetworkInformationWindow.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/Base/Debugger/Imp/DebuggerComponent.NetworkInformationWindow.cs.meta new file mode 100644 index 00000000..1d902b54 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/Base/Debugger/Imp/DebuggerComponent.NetworkInformationWindow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 499fb65f1b784d4c912050bf3c1b54d3 +timeCreated: 1661930992 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/Base/Debugger/Imp/DebuggerComponent.cs b/Assets/TEngine/Scripts/Runtime/Core/Base/Debugger/Imp/DebuggerComponent.cs index aea0798e..bfa8d29b 100644 --- a/Assets/TEngine/Scripts/Runtime/Core/Base/Debugger/Imp/DebuggerComponent.cs +++ b/Assets/TEngine/Scripts/Runtime/Core/Base/Debugger/Imp/DebuggerComponent.cs @@ -189,7 +189,7 @@ namespace TEngine.Runtime private RuntimeMemoryInformationWindow m_RuntimeMemoryScriptableObjectInformationWindow = new RuntimeMemoryInformationWindow(); private MemoryPoolInformationWindow m_MemoryPoolInformationWindow = new MemoryPoolInformationWindow(); - // private NetworkInformationWindow m_NetworkInformationWindow = new NetworkInformationWindow(); + private NetworkInformationWindow m_NetworkInformationWindow = new NetworkInformationWindow(); private SettingsWindow m_SettingsWindow = new SettingsWindow(); #endregion @@ -229,7 +229,7 @@ namespace TEngine.Runtime RegisterDebuggerWindow("Profiler/Memory/ScriptableObject", m_RuntimeMemoryScriptableObjectInformationWindow); RegisterDebuggerWindow("Profiler/Memory Pool", m_MemoryPoolInformationWindow); - // RegisterDebuggerWindow("Profiler/Network", m_NetworkInformationWindow); + RegisterDebuggerWindow("Profiler/Network", m_NetworkInformationWindow); RegisterDebuggerWindow("Other/Settings", m_SettingsWindow); diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork.meta new file mode 100644 index 00000000..4283a259 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 38e14d7bbaa74254198afb9c1df69a30 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core.meta new file mode 100644 index 00000000..d0b4ddd1 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b9fbba9291a686a4485a94f19febffd1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/AddressFamily.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/AddressFamily.cs new file mode 100644 index 00000000..fdc3f8fb --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/AddressFamily.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/AddressFamily.cs.meta new file mode 100644 index 00000000..54d65ab7 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/INetworkChannel.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/INetworkChannel.cs new file mode 100644 index 00000000..783aa2e5 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/INetworkChannel.cs @@ -0,0 +1,144 @@ +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 ReceivedPacketCount + { + get; + } + + /// + /// 获取或设置当收到消息包时是否重置心跳流逝时间。 + /// + bool ResetHeartBeatElapseSecondsWhenReceivePacket + { + get; + set; + } + + /// + /// 获取丢失心跳的次数。 + /// + int MissHeartBeatCount + { + get; + } + + /// + /// 获取或设置心跳间隔时长,以秒为单位。 + /// + float HeartBeatInterval + { + get; + set; + } + + /// + /// 获取心跳等待时长,以秒为单位。 + /// + float HeartBeatElapseSeconds + { + get; + } + + /// + /// 注册网络消息包处理函数。 + /// + /// + /// + void RegisterHandler(int actionId, CsMsgDelegate msgDelegate); + + /// + /// 连接到远程主机。 + /// + /// 远程主机的 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/Core/INetworkChannel.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/INetworkChannel.cs.meta new file mode 100644 index 00000000..474bacd3 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/INetworkChannelHelper.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/INetworkChannelHelper.cs new file mode 100644 index 00000000..40c855e5 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/INetworkChannelHelper.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/INetworkChannelHelper.cs.meta new file mode 100644 index 00000000..495fc074 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/INetworkManager.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/INetworkManager.cs new file mode 100644 index 00000000..b3ab01b2 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/INetworkManager.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Net.Sockets; + +namespace TEngine.Runtime +{ + /// + /// 网络管理器接口。 + /// + public interface INetworkManager + { + /// + /// 获取网络频道数量。 + /// + int NetworkChannelCount + { + get; + } + + /// + /// 网络连接成功事件。 + /// + event Action NetworkConnected; + + /// + /// 网络连接关闭事件。 + /// + event Action NetworkClosed; + + /// + /// 网络心跳包丢失事件。 + /// + event Action NetworkMissHeartBeat; + + /// + /// 网络错误事件。 + /// + event Action NetworkError; + + /// + /// 用户自定义网络错误事件。 + /// + event Action 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/Core/INetworkManager.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/INetworkManager.cs.meta new file mode 100644 index 00000000..b8132e5a --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/IPacketHeader.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/IPacketHeader.cs new file mode 100644 index 00000000..48877019 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/IPacketHeader.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/IPacketHeader.cs.meta new file mode 100644 index 00000000..888bf894 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/NetWorkEventId.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetWorkEventId.cs new file mode 100644 index 00000000..2a165c7a --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetWorkEventId.cs @@ -0,0 +1,11 @@ +namespace TEngine.Runtime +{ + public static class NetWorkEventId + { + public static int NetworkConnectedEvent = StringId.StringToHash("NetWorkEventId.NetworkConnectedEvent"); + public static int NetworkClosedEvent = StringId.StringToHash("NetWorkEventId.NetworkClosedEvent"); + public static int NetworkMissHeartBeatEvent = StringId.StringToHash("NetWorkEventId.NetworkMissHeartBeatEvent"); + public static int NetworkErrorEvent = StringId.StringToHash("NetWorkEventId.NetworkErrorEvent"); + public static int NetworkCustomErrorEvent = StringId.StringToHash("NetWorkEventId.NetworkCustomErrorEvent"); + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetWorkEventId.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetWorkEventId.cs.meta new file mode 100644 index 00000000..a1e3f651 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetWorkEventId.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 007c9ae2d35f4d6181f53cf3e5738884 +timeCreated: 1661930441 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/Network.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/Network.cs new file mode 100644 index 00000000..b156d585 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/Network.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace TEngine.Runtime +{ + /// + /// 网络组件。 + /// + [DisallowMultipleComponent] + [AddComponentMenu("TEngine/Network")] + public class Network : UnitySingleton + { + private INetworkManager m_NetworkManager = null; + + public NetworkManager NetworkManager + { + private set; + get; + } + + /// + /// 获取网络频道数量。 + /// + public int NetworkChannelCount + { + get + { + if (m_NetworkManager == null) + { + return 0; + } + return m_NetworkManager.NetworkChannelCount; + } + } + + protected override void OnLoad() + { + base.OnLoad(); + m_NetworkManager = new NetworkManager(); + NetworkManager = m_NetworkManager as 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; + } + + private void Update() + { + if (m_NetworkManager != null) + { + NetworkManager.Update(Time.deltaTime, Time.unscaledDeltaTime);; + } + } + + protected override void OnDestroy() + { + base.OnDestroy(); + if (NetworkManager != null) + { + NetworkManager.Shutdown(); + } + } + + /// + /// 检查是否存在网络频道。 + /// + /// 网络频道名称。 + /// 是否存在网络频道。 + 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(INetworkChannel channel, object obj) + { + GameEventMgr.Instance.Send(NetWorkEventId.NetworkConnectedEvent,channel, obj); + } + + private void OnNetworkClosed(INetworkChannel channel) + { + GameEventMgr.Instance.Send(NetWorkEventId.NetworkClosedEvent,channel); + } + + private void OnNetworkMissHeartBeat(INetworkChannel channel, int missCount) + { + GameEventMgr.Instance.Send(NetWorkEventId.NetworkMissHeartBeatEvent,channel,missCount); + } + + private void OnNetworkError(INetworkChannel channel, NetworkErrorCode errorCode, string message) + { + GameEventMgr.Instance.Send(NetWorkEventId.NetworkErrorEvent,channel,errorCode,message); + } + + private void OnNetworkCustomError(INetworkChannel channel,object message) + { + GameEventMgr.Instance.Send(NetWorkEventId.NetworkCustomErrorEvent,channel,message); + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/Network.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/Network.cs.meta new file mode 100644 index 00000000..1760a35a --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/NetworkErrorCode.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkErrorCode.cs new file mode 100644 index 00000000..2ceade3c --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/NetworkErrorCode.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkErrorCode.cs.meta new file mode 100644 index 00000000..35a0f13b --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/NetworkManager.ConnectState.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkManager.ConnectState.cs new file mode 100644 index 00000000..3bd4b7f6 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/NetworkManager.ConnectState.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkManager.ConnectState.cs.meta new file mode 100644 index 00000000..ccd0558f --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/NetworkManager.HeartBeatState.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkManager.HeartBeatState.cs new file mode 100644 index 00000000..5220221e --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/NetworkManager.HeartBeatState.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkManager.HeartBeatState.cs.meta new file mode 100644 index 00000000..1ba94069 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/NetworkManager.NetworkChannelBase.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkManager.NetworkChannelBase.cs new file mode 100644 index 00000000..4c9ee08e --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkManager.NetworkChannelBase.cs @@ -0,0 +1,762 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using TEngineProto; + +namespace TEngine.Runtime +{ + public delegate void CsMsgDelegate(MainPack mainPack); + + public sealed partial class NetworkManager + { + /// + /// 网络频道基类。 + /// + private abstract class NetworkChannelBase : INetworkChannel, IDisposable + { + private const float DefaultHeartBeatInterval = 30f; + private const int MAX_MSG_HANDLE = 256; + + private readonly string m_Name; + protected readonly Queue m_SendPacketPool; + 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; + + /// + /// 委托注册队列 + /// + protected Dictionary> m_MapCmdHandle = new Dictionary>(); + /// + /// 委托缓存堆栈 + /// + protected Queue> m_CachelistHandle = new Queue>(); + + /// + /// 消息包缓存堆栈 + /// + protected Queue m_QueuepPacks = new Queue(); + + /// + /// 初始化网络频道基类的新实例。 + /// + /// 网络频道名称。 + /// 网络频道辅助器。 + public NetworkChannelBase(string name, INetworkChannelHelper networkChannelHelper) + { + m_Name = name ?? string.Empty; + m_SendPacketPool = new Queue(); + 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 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; + } + + HandleCsMsgOnUpdate(); + CheckCsMsgTimeOut(); + ProcessSend(); + ProcessReceive(); + if (m_Socket == null || !m_Active) + { + return; + } + + 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_NetworkChannelHelper.Shutdown(); + } + + /// + /// 注册网络消息包处理函数。 + /// + /// + /// + /// + public void RegisterHandler(int actionId, CsMsgDelegate msgDelegate) + { + if (msgDelegate == null) + { + throw new Exception("Packet handler is invalid."); + } + + List listHandle; + if (!m_MapCmdHandle.TryGetValue(actionId, out listHandle)) + { + listHandle = new List(); + m_MapCmdHandle[actionId] = listHandle; + } + + if (listHandle != null) + { + if (!listHandle.Contains(msgDelegate)) + { + listHandle.Add(msgDelegate); + } + else + { + Log.Warning("-------------repeat RegCmdHandle ActionCode:{0}-----------", (ActionCode)actionId); + } + } + } + + /// + /// 移除消息处理函数 + /// + /// + /// + public void RmvHandler(int actionId, CsMsgDelegate msgDelegate) + { + List listHandle; + if (!m_MapCmdHandle.TryGetValue(actionId, out listHandle)) + { + return; + } + + if (listHandle != null) + { + listHandle.Remove(msgDelegate); + } + } + + /// + /// 连接到远程主机。 + /// + /// 远程主机的 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(); + } + + 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) + { + HandleResponse(packet as MainPack); + } + + 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; + } + + /// + /// 网络消息回调,非主线程 + /// + /// + public void HandleResponse(MainPack pack) + { + lock (m_CachelistHandle) + { + List listHandle; + + if (m_MapCmdHandle.TryGetValue((int)pack.actioncode, out listHandle)) + { + m_CachelistHandle.Enqueue(listHandle); + + m_QueuepPacks.Enqueue(pack); + } + } + } + + /// + /// 主线程从消息包缓存堆栈/委托缓存堆栈中出列 + /// + private void HandleCsMsgOnUpdate() + { + if (m_CachelistHandle.Count <= 0 || m_QueuepPacks.Count <= 0) + { + return; + } + try + { + foreach (CsMsgDelegate handle in m_CachelistHandle.Dequeue()) + { + var pack = m_QueuepPacks.Peek(); + + if (pack != null) + { + handle(pack); + + UInt32 hashIndex = (uint)pack.actioncode % MAX_MSG_HANDLE; + + RmvCheckCsMsg((int)hashIndex); + } + } + m_QueuepPacks.Dequeue(); + } + catch (Exception e) + { + TLogger.LogError(e.Message); + } + } + + #region 超时检测 + private const int CHECK_TIMEOUT_PERFRAME = 10; + UInt32 m_dwLastCheckIndex = 0; + CsMsgDelegate[] m_aMsgHandles = new CsMsgDelegate[MAX_MSG_HANDLE]; + float[] m_fMsgRegTime = new float[MAX_MSG_HANDLE]; + private float m_timeout = 15; + private readonly MainPack _timeOutPack = new MainPack { returncode = ReturnCode.MsgTimeOut }; + public void RmvCheckCsMsg(int index) + { + m_aMsgHandles[index] = null; + m_fMsgRegTime[index] = 0; + } + private void RegTimeOutHandle(uint actionCode, CsMsgDelegate resHandler) + { + uint hashIndex = actionCode % MAX_MSG_HANDLE; + if (m_aMsgHandles[hashIndex] != null) + { + //NotifyTimeout(m_aMsgHandles[hashIndex]); + RmvCheckCsMsg((int)hashIndex); + } + m_aMsgHandles[hashIndex] = resHandler; + m_fMsgRegTime[hashIndex] = UnityEngine.Time.time; + } + + protected void NotifyTimeout(CsMsgDelegate msgHandler) + { + msgHandler(_timeOutPack); + } + + private void CheckCsMsgTimeOut() + { + float nowTime = UnityEngine.Time.time; + for (int i = 0; i < CHECK_TIMEOUT_PERFRAME; i++) + { + m_dwLastCheckIndex = (m_dwLastCheckIndex + 1) % MAX_MSG_HANDLE; + if (m_aMsgHandles[m_dwLastCheckIndex] != null) + { + if (m_fMsgRegTime[m_dwLastCheckIndex] + m_timeout < nowTime) + { + TLogger.LogError("msg timeout, resCmdID[{0}]", m_aMsgHandles[m_dwLastCheckIndex]); + + NotifyTimeout(m_aMsgHandles[m_dwLastCheckIndex]); + + RmvCheckCsMsg((int)m_dwLastCheckIndex); + } + } + } + } + + #endregion + } + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkManager.NetworkChannelBase.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkManager.NetworkChannelBase.cs.meta new file mode 100644 index 00000000..a096d5c6 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/NetworkManager.ReceiveState.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkManager.ReceiveState.cs new file mode 100644 index 00000000..478b492b --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/NetworkManager.ReceiveState.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkManager.ReceiveState.cs.meta new file mode 100644 index 00000000..5d6a2077 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/NetworkManager.SendState.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkManager.SendState.cs new file mode 100644 index 00000000..9a573301 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/NetworkManager.SendState.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkManager.SendState.cs.meta new file mode 100644 index 00000000..1ee0277c --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/NetworkManager.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkManager.cs new file mode 100644 index 00000000..426ca1c6 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkManager.cs @@ -0,0 +1,340 @@ +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 Action m_NetworkConnectedEventHandler; + private Action m_NetworkClosedEventHandler; + private Action m_NetworkMissHeartBeatEventHandler; + private Action m_NetworkErrorEventHandler; + private Action 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 Action NetworkConnected + { + add + { + m_NetworkConnectedEventHandler += value; + } + remove + { + m_NetworkConnectedEventHandler -= value; + } + } + + /// + /// 网络连接关闭事件。 + /// + public event Action NetworkClosed + { + add + { + m_NetworkClosedEventHandler += value; + } + remove + { + m_NetworkClosedEventHandler -= value; + } + } + + /// + /// 网络心跳包丢失事件。 + /// + public event Action NetworkMissHeartBeat + { + add + { + m_NetworkMissHeartBeatEventHandler += value; + } + remove + { + m_NetworkMissHeartBeatEventHandler -= value; + } + } + + /// + /// 网络错误事件。 + /// + public event Action NetworkError + { + add + { + m_NetworkErrorEventHandler += value; + } + remove + { + m_NetworkErrorEventHandler -= value; + } + } + + /// + /// 用户自定义网络错误事件。 + /// + public event Action 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) + { + m_NetworkConnectedEventHandler(networkChannel,userData); + } + } + } + + private void OnNetworkChannelClosed(NetworkChannelBase networkChannel) + { + if (m_NetworkClosedEventHandler != null) + { + lock (m_NetworkClosedEventHandler) + { + m_NetworkClosedEventHandler(networkChannel); + } + } + } + + private void OnNetworkChannelMissHeartBeat(NetworkChannelBase networkChannel, int missHeartBeatCount) + { + if (m_NetworkMissHeartBeatEventHandler != null) + { + lock (m_NetworkMissHeartBeatEventHandler) + { + m_NetworkMissHeartBeatEventHandler(networkChannel,missHeartBeatCount); + } + } + } + + private void OnNetworkChannelError(NetworkChannelBase networkChannel, NetworkErrorCode errorCode, SocketError socketErrorCode, string errorMessage) + { + if (m_NetworkErrorEventHandler != null) + { + lock (m_NetworkErrorEventHandler) + { + m_NetworkErrorEventHandler(networkChannel,errorCode,errorMessage); + } + } + } + + private void OnNetworkChannelCustomError(NetworkChannelBase networkChannel, object customErrorData) + { + if (m_NetworkCustomErrorEventHandler != null) + { + lock (m_NetworkCustomErrorEventHandler) + { + m_NetworkCustomErrorEventHandler(networkChannel,customErrorData); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkManager.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/NetworkManager.cs.meta new file mode 100644 index 00000000..97a987d3 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/Packet.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/Packet.cs new file mode 100644 index 00000000..73ab0c3a --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/Packet.cs @@ -0,0 +1,10 @@ +namespace TEngine.Runtime +{ + /// + /// 网络消息包基类。 + /// + public abstract class Packet:IMemory + { + public abstract void Clear(); + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/Packet.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/Packet.cs.meta new file mode 100644 index 00000000..8435f3b5 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/ServiceType.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/ServiceType.cs new file mode 100644 index 00000000..bca19f41 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Core/ServiceType.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/ServiceType.cs.meta new file mode 100644 index 00000000..04b3f41a --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Core/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/Helper.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper.meta new file mode 100644 index 00000000..2410227b --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9de71dee8e9fb74469064a1e1cf7a77b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/CSPacketHeader.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/CSPacketHeader.cs new file mode 100644 index 00000000..2e92199e --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/CSPacketHeader.cs @@ -0,0 +1,11 @@ +using System; +using ProtoBuf; + +namespace TEngine.Runtime +{ + [Serializable, ProtoContract(Name = @"CSPacketHeader")] + public sealed class CSPacketHeader : PacketHeaderBase + { + + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/CSPacketHeader.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/CSPacketHeader.cs.meta new file mode 100644 index 00000000..00eff30f --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/CSPacketHeader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 383e04755715b054f9e57b1ab1d322af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/NetworkChannelHelper.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/NetworkChannelHelper.cs new file mode 100644 index 00000000..708f7029 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/NetworkChannelHelper.cs @@ -0,0 +1,207 @@ +using System; +using System.IO; +using ProtoBuf; +using ProtoBuf.Meta; +using TEngineProto; + +namespace TEngine.Runtime +{ + public class NetworkChannelHelper : INetworkChannelHelper + { + private readonly MemoryStream m_CachedStream = new MemoryStream(1024 * 8); + private INetworkChannel m_NetworkChannel = null; + + /// + /// 获取消息包头长度。 + /// + public int PacketHeaderLength => sizeof(int); + + /// + /// 初始化网络频道辅助器。 + /// + /// 网络频道。 + public void Initialize(INetworkChannel networkChannel) + { + m_NetworkChannel = networkChannel; + + GameEventMgr.Instance.AddEventListener(NetWorkEventId.NetworkConnectedEvent,OnNetworkConnected); + GameEventMgr.Instance.AddEventListener(NetWorkEventId.NetworkClosedEvent,OnNetworkClosed); + GameEventMgr.Instance.AddEventListener(NetWorkEventId.NetworkMissHeartBeatEvent,OnNetworkMissHeartBeat); + GameEventMgr.Instance.AddEventListener(NetWorkEventId.NetworkErrorEvent,OnNetworkError); + GameEventMgr.Instance.AddEventListener(NetWorkEventId.NetworkCustomErrorEvent,OnNetworkCustomError); + + m_NetworkChannel.RegisterHandler((int)ActionCode.HeartBeat,HandleHeartBeat); + } + + private void HandleHeartBeat(MainPack mainPack) + { + + } + + /// + /// 准备进行连接。 + /// + public void PrepareForConnecting() + { + m_NetworkChannel.Socket.ReceiveBufferSize = 1024 * 64; + m_NetworkChannel.Socket.SendBufferSize = 1024 * 64; + } + + /// + /// 发送心跳消息包。 + /// + /// 是否发送心跳消息包成功。 + public bool SendHeartBeat() + { + m_NetworkChannel.Send(CSHeartBeatHandler.AllocHeartBeatPack()); + return true; + } + + /// + /// 序列化消息包。 + /// + /// 消息包类型。 + /// 要序列化的消息包。 + /// 要序列化的目标流。 + /// 是否序列化成功。 + public bool Serialize(T packet, Stream destination) where T : Packet + { + m_CachedStream.SetLength(m_CachedStream.Capacity); // 此行防止 Array.Copy 的数据无法写入 + m_CachedStream.Position = 0L; + + CSPacketHeader packetHeader = MemoryPool.Acquire(); + Serializer.Serialize(m_CachedStream, packetHeader); + MemoryPool.Release(packetHeader); + + Serializer.SerializeWithLengthPrefix(m_CachedStream, packet, PrefixStyle.Fixed32); + MemoryPool.Release((IMemory)packet); + + m_CachedStream.WriteTo(destination); + return true; + } + + /// + /// 反序列化消息包。 + /// + /// 消息包头。 + /// 要反序列化的来源流。 + /// 用户自定义错误数据。 + /// 反序列化后的消息包。 + public Packet DeserializePacket(IPacketHeader packetHeader, Stream source, out object customErrorData) + { + // 注意:此函数并不在主线程调用! + customErrorData = null; + + CSPacketHeader csPacketHeader = packetHeader as CSPacketHeader; + if (csPacketHeader == null) + { + Log.Warning("Packet header is invalid."); + return null; + } + + Packet packet = null; + if (csPacketHeader.IsValid) + { + Type packetType = typeof(MainPack); + if (packetType != null) + { + packet = (Packet)RuntimeTypeModel.Default.DeserializeWithLengthPrefix(source, MemoryPool.Acquire(packetType), packetType, PrefixStyle.Fixed32, 0); + } + else + { + Log.Warning("Can not deserialize packet for packet id '{0}'.", csPacketHeader.Id.ToString()); + } + } + else + { + Log.Warning("Packet header is invalid."); + } + + MemoryPool.Release(csPacketHeader); + return packet; + } + + public IPacketHeader DeserializePacketHeader(Stream source, out object customErrorData) + { + // 注意:此函数并不在主线程调用! + customErrorData = null; + return (IPacketHeader)RuntimeTypeModel.Default.Deserialize(source, MemoryPool.Acquire(), typeof(CSPacketHeader)); + } + + /// + /// 关闭并清理网络频道辅助器。 + /// + public void Shutdown() + { + GameEventMgr.Instance.RemoveEventListener(NetWorkEventId.NetworkConnectedEvent,OnNetworkConnected); + GameEventMgr.Instance.RemoveEventListener(NetWorkEventId.NetworkClosedEvent,OnNetworkClosed); + GameEventMgr.Instance.RemoveEventListener(NetWorkEventId.NetworkMissHeartBeatEvent,OnNetworkMissHeartBeat); + GameEventMgr.Instance.RemoveEventListener(NetWorkEventId.NetworkErrorEvent,OnNetworkError); + GameEventMgr.Instance.RemoveEventListener(NetWorkEventId.NetworkCustomErrorEvent,OnNetworkCustomError); + m_NetworkChannel = null; + } + + #region Handle + private void OnNetworkConnected(INetworkChannel channel,object userData) + { + if (channel != m_NetworkChannel) + { + return; + } + + if (channel.Socket == null) + { + return; + } + Log.Info("Network channel '{0}' connected, local address '{1}', remote address '{2}'.", channel.Name, channel.Socket.LocalEndPoint.ToString(), channel.Socket.RemoteEndPoint.ToString()); + } + + private void OnNetworkClosed(INetworkChannel channel) + { + if (channel != m_NetworkChannel) + { + return; + } + Log.Warning("Network channel '{0}' closed.", channel.Name); + } + + private void OnNetworkMissHeartBeat(INetworkChannel channel,int missCount) + { + if (channel != m_NetworkChannel) + { + return; + } + + Log.Warning("Network channel '{0}' miss heart beat '{1}' times.", channel.Name, missCount.ToString()); + + if (missCount < 2) + { + return; + } + channel.Close(); + } + + private void OnNetworkError(INetworkChannel channel,NetworkErrorCode errorCode,string errorMessage) + { + if (channel != m_NetworkChannel) + { + return; + } + + Log.Fatal("Network channel '{0}' error, error code is '{1}', error message is '{2}'.", channel.Name, errorCode.ToString(), errorMessage); + + channel.Close(); + } + + private void OnNetworkCustomError(INetworkChannel channel,object customErrorData) + { + if (channel != m_NetworkChannel) + { + return; + } + + Log.Fatal("Network channel '{0}' error, error code is '{1}', error message is '{2}'.", channel.Name, customErrorData.ToString()); + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/NetworkChannelHelper.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/NetworkChannelHelper.cs.meta new file mode 100644 index 00000000..0a65b0f1 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/NetworkChannelHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 02d761e658b2dcd4ea9ebff4ce324968 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/PacketHandler.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/PacketHandler.meta new file mode 100644 index 00000000..9c57d0f7 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/PacketHandler.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0396e29af93d424fb252e3cf5a948fa6 +timeCreated: 1661850708 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/PacketHandler/SCHeartBeatHandler.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/PacketHandler/SCHeartBeatHandler.cs new file mode 100644 index 00000000..e96afb6f --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/PacketHandler/SCHeartBeatHandler.cs @@ -0,0 +1,28 @@ +using TEngineProto; + +namespace TEngine.Runtime +{ + /// + /// 心跳Handler + /// + public class CSHeartBeatHandler + { + public static MainPack AllocHeartBeatPack() + { + var mainPack = MemoryPool.Acquire(); + mainPack.actioncode = ActionCode.HeartBeat; + mainPack.requestcode = RequestCode.Heart; + return mainPack; + } + + public static void Handler(MainPack mainPack) + { + if (mainPack == null) + { + Log.Fatal("Receive CSHeartBeat Failed !!!"); + return; + } + Log.Info("Receive packet '{0}'.", mainPack.ToString()); + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/PacketHandler/SCHeartBeatHandler.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/PacketHandler/SCHeartBeatHandler.cs.meta new file mode 100644 index 00000000..571942fb --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/PacketHandler/SCHeartBeatHandler.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 553b96ca58c647d39802bed84a356608 +timeCreated: 1661850718 \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/PacketHeaderBase.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/PacketHeaderBase.cs new file mode 100644 index 00000000..e99d169c --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/PacketHeaderBase.cs @@ -0,0 +1,31 @@ +namespace TEngine.Runtime +{ + public abstract class PacketHeaderBase : IPacketHeader, IMemory + { + public int Id + { + get; + set; + } + + public int PacketLength + { + get; + set; + } + + public bool IsValid + { + get + { + return Id > 0 && PacketLength >= 0; + } + } + + public void Clear() + { + Id = 0; + PacketLength = 0; + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/PacketHeaderBase.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/PacketHeaderBase.cs.meta new file mode 100644 index 00000000..d280b59c --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/PacketHeaderBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 279045bf4ec72144aa03880102a04057 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/ProtoUtils.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/ProtoUtils.cs new file mode 100644 index 00000000..d21d6413 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/ProtoUtils.cs @@ -0,0 +1,49 @@ +using System.IO; + +namespace TEngine.Runtime +{ + public class ProtoUtils + { + /// + /// 序列化 MainPack -> byte[] + /// + /// + /// + /// + public static byte[] Serialize(T mainPack) where T : class + { + try + { + using (var stream = new System.IO.MemoryStream()) + { + ProtoBuf.Serializer.Serialize(stream, mainPack); + return stream.ToArray(); + } + } + catch (IOException ex) + { + Log.Error($"[Serialize] Error:{ex.Message}, {ex.Data["StackTrace"]}"); + return null; + } + } + + /// + /// 反序列化 + /// + /// + /// + /// + public static T DeSerialize(byte[] buffer) where T : class + { + try + { + return ProtoBuf.Serializer.Deserialize(typeof(T), new System.IO.MemoryStream(buffer)) as T; + } + catch (IOException ex) + { + Log.Error(($"[DeSerialize] Error:{ex.Message}, {ex.Data["StackTrace"]}")); + return null; + } + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/ProtoUtils.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/ProtoUtils.cs.meta new file mode 100644 index 00000000..ebbdc4f6 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Helper/ProtoUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2ab82c8abf14856489102fe60cc78926 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Proto.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Proto.meta new file mode 100644 index 00000000..f36a9046 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Proto.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9bb2521a45da9104489c2f22445d39fb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Proto/MainPackExt.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Proto/MainPackExt.cs new file mode 100644 index 00000000..b9149a92 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Proto/MainPackExt.cs @@ -0,0 +1,14 @@ +using TEngine.Runtime; + +namespace TEngineProto +{ + public partial class MainPack:Packet + { + public override void Clear() + { + requestcode = RequestCode.RequestNone; + actioncode = ActionCode.ActionNone; + returncode = ReturnCode.ReturnNone; + } + } +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Proto/MainPackExt.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Proto/MainPackExt.cs.meta new file mode 100644 index 00000000..6a02f710 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Proto/MainPackExt.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9a93b14384cc75e47b78cb84e7cfb9c1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Proto/TEngineProto.cs b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Proto/TEngineProto.cs new file mode 100644 index 00000000..9e973732 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Proto/TEngineProto.cs @@ -0,0 +1,192 @@ +using ProtoBuf; +using TEngine.Runtime; +using System.Collections.Generic; +namespace TEngineProto +{ + [global::ProtoBuf.ProtoContract()] + public enum RequestCode + { + RequestNone = 0, + + Heart = 1, + + User = 2, + + Room = 3, + + Game = 4, + + } + + [global::ProtoBuf.ProtoContract()] + public enum ActionCode + { + ActionNone = 0, + + HeartBeat = 1, + + Register = 1000, + + Login = 1001, + + CreateRoom = 1002, + + FindRoom = 1003, + + GetPlayers = 1004, + + JoinRoom = 1005, + + ExitRoom = 1006, + + Chat = 2000, + + } + + [global::ProtoBuf.ProtoContract()] + public enum ReturnCode + { + ReturnNone = 0, + + Success = 1, + + Fail = 2, + + MsgTimeOut = 3, + + } + + [global::ProtoBuf.ProtoContract()] + public partial class MainPack + { + [global::ProtoBuf.ProtoMember(1)] + public RequestCode requestcode { get; set; } + + [global::ProtoBuf.ProtoMember(2)] + public ActionCode actioncode { get; set; } + + [global::ProtoBuf.ProtoMember(3)] + public ReturnCode returncode { get; set; } + + [global::ProtoBuf.ProtoMember(4)] + public LoginPack loginPack { get; set; } + + [global::ProtoBuf.ProtoMember(5)] + [global::System.ComponentModel.DefaultValue("")] + public string extstr { get; set; } + + [global::ProtoBuf.ProtoMember(6)] + public List roompack = new List(); + + [global::ProtoBuf.ProtoMember(7)] + public PlayerPack playerpack { get; set; } + + [global::ProtoBuf.ProtoMember(8)] + public long HeatEchoTime { get; set; } + } + + [global::ProtoBuf.ProtoContract()] + public partial class LoginPack + { + [global::ProtoBuf.ProtoMember(1)] + [global::System.ComponentModel.DefaultValue("")] + public string username { get; set; } + + [global::ProtoBuf.ProtoMember(2)] + [global::System.ComponentModel.DefaultValue("")] + public string password { get; set; } + + } + + [global::ProtoBuf.ProtoContract()] + public partial class RoomPack + { + [global::ProtoBuf.ProtoMember(1)] + [global::System.ComponentModel.DefaultValue("")] + public string roomname { get; set; } + + [global::ProtoBuf.ProtoMember(2)] + public int maxnum { get; set; } + + [global::ProtoBuf.ProtoMember(3)] + public int curnum { get; set; } + + [global::ProtoBuf.ProtoMember(4)] + public int state { get; set; } + + [global::ProtoBuf.ProtoMember(6)] + public int roomID { get; set; } + + [global::ProtoBuf.ProtoMember(9)] + public int visable { get; set; } + + [global::ProtoBuf.ProtoMember(10)] + public int usePassword { get; set; } + + [global::ProtoBuf.ProtoMember(11)] + [global::System.ComponentModel.DefaultValue("")] + public string password { get; set; } + + [global::ProtoBuf.ProtoMember(12)] + public List playerpack = new List(); + + } + + [global::ProtoBuf.ProtoContract()] + public partial class PlayerPack + { + [global::ProtoBuf.ProtoMember(1)] + [global::System.ComponentModel.DefaultValue("")] + public string playerName { get; set; } + + [global::ProtoBuf.ProtoMember(2)] + [global::System.ComponentModel.DefaultValue("")] + public string playerID { get; set; } + + [global::ProtoBuf.ProtoMember(3)] + public int hp { get; set; } + + [global::ProtoBuf.ProtoMember(4)] + public PosPack posPack { get; set; } + + } + + [global::ProtoBuf.ProtoContract()] + public partial class PosPack + { + [global::ProtoBuf.ProtoMember(1)] + public float PosX { get; set; } + + [global::ProtoBuf.ProtoMember(2)] + public float PosY { get; set; } + + [global::ProtoBuf.ProtoMember(3)] + public float PosZ { get; set; } + + [global::ProtoBuf.ProtoMember(4)] + public float RotaX { get; set; } + + [global::ProtoBuf.ProtoMember(5)] + public float RotaY { get; set; } + + [global::ProtoBuf.ProtoMember(6)] + public float RotaZ { get; set; } + + [global::ProtoBuf.ProtoMember(8)] + public int Animation { get; set; } + + [global::ProtoBuf.ProtoMember(9)] + public int Direction { get; set; } + + [global::ProtoBuf.ProtoMember(10)] + public float MoveX { get; set; } + + [global::ProtoBuf.ProtoMember(11)] + public float MoveY { get; set; } + + [global::ProtoBuf.ProtoMember(12)] + public float MoveZ { get; set; } + + } + +} diff --git a/Assets/TEngine/Scripts/Runtime/Core/NetWork/Proto/TEngineProto.cs.meta b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Proto/TEngineProto.cs.meta new file mode 100644 index 00000000..bf365d9c --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Proto/TEngineProto.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cbec49a00bdc11543a0608119cf63810 +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..20ca88f0 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Tcp/NetworkManager.TcpNetworkChannel.cs @@ -0,0 +1,281 @@ +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(); + } + + 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..86550d5e --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Tcp/NetworkManager.TcpWithSyncReceiveNetworkChannel.cs @@ -0,0 +1,257 @@ +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(); + } + + 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..f33fb428 --- /dev/null +++ b/Assets/TEngine/Scripts/Runtime/Core/NetWork/Udp/UdpNetworkChannel.cs @@ -0,0 +1,274 @@ +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(); + } + + 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: diff --git a/Assets/TEngine/Tools~/Localize/ExcelConfig/Localization.json b/Assets/TEngine/Tools~/Localize/ExcelConfig/Localization.json new file mode 100644 index 00000000..7a73a41b --- /dev/null +++ b/Assets/TEngine/Tools~/Localize/ExcelConfig/Localization.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Assets/TEngine/Tools~/Localize/ExcelConfig/Localization.xlsx b/Assets/TEngine/Tools~/Localize/ExcelConfig/Localization.xlsx new file mode 100644 index 00000000..feaf36f1 Binary files /dev/null and b/Assets/TEngine/Tools~/Localize/ExcelConfig/Localization.xlsx differ diff --git a/Assets/TEngine/Tools~/Protobuf/Proto/TEngineProto.proto b/Assets/TEngine/Tools~/Protobuf/Proto/TEngineProto.proto new file mode 100644 index 00000000..48868cc6 --- /dev/null +++ b/Assets/TEngine/Tools~/Protobuf/Proto/TEngineProto.proto @@ -0,0 +1,83 @@ +syntax = "proto3"; +package TEngineProto; + +enum RequestCode +{ + RequestNone = 0; + Heart = 1; //心跳 + User = 2; //用户 + Room = 3; //房间 + Game = 4; //游戏 +} + +enum ActionCode +{ + ActionNone = 0; + HeartBeat = 1; //心跳 + Register = 1000; //注册 + Login = 1001; //登录 + CreateRoom = 1002; //创建房间 + FindRoom = 1003; //查找房间 + GetPlayers = 1004; //获取玩家列表 + JoinRoom = 1005; //加入房间 + ExitRoom = 1006; //离开房间 + Chat = 2000; //聊天 +} + +enum ReturnCode +{ + ReturnNone = 0; + Success = 1; //成功 + Fail = 2; //失败 + MsgTimeOut = 3; //消息超时 +} + +message MainPack +{ + RequestCode requestcode = 1; + ActionCode actioncode = 2; + ReturnCode returncode = 3; + LoginPack loginPack = 4; + string extstr = 5; + repeated RoomPack roompack = 6; //房间包,repeated加上为list + PlayerPack playerpack = 7; + long HeatEchoTime = 8; //心跳包回包时间 +} + +message LoginPack +{ + string username = 1; //用户名 + string password = 2; //密码 +} + +message RoomPack +{ + string roomname = 1; //房间名 + int32 maxnum = 2; //房间最大人数 + int32 curnum = 3; //房间当前人数 + int32 roomID = 6; + repeated PlayerPack playerpack = 12; +} + +message PlayerPack +{ + string playerName = 1; //玩家名称 + string playerID = 2; //玩家ID + int32 hp = 3; //玩家血量 + PosPack posPack = 4; //位置信息 +} + +message PosPack +{ + float PosX = 1; + float PosY = 2; + float PosZ = 3; + float RotaX = 4; + float RotaY = 5; + float RotaZ = 6; + int32 Animation = 8; + int32 Direction = 9; + float MoveX = 10; + float MoveY = 11; + float MoveZ = 12; +} \ No newline at end of file diff --git a/Assets/TEngine/Tools~/Protobuf/Proto/msg.proto b/Assets/TEngine/Tools~/Protobuf/Proto/msg.proto new file mode 100644 index 00000000..8571cbda --- /dev/null +++ b/Assets/TEngine/Tools~/Protobuf/Proto/msg.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +package msg; + +message Msg { + string msgId = 1; + bytes msgContent = 2; +} \ No newline at end of file diff --git a/Assets/TEngine/Tools~/Protobuf/Proto_CSharp/TEngineProto.cs b/Assets/TEngine/Tools~/Protobuf/Proto_CSharp/TEngineProto.cs new file mode 100644 index 00000000..68913498 --- /dev/null +++ b/Assets/TEngine/Tools~/Protobuf/Proto_CSharp/TEngineProto.cs @@ -0,0 +1,180 @@ +using ProtoBuf; +using TEngine.Runtime; +using System.Collections.Generic; +namespace TEngineProto +{ + [global::ProtoBuf.ProtoContract()] + public enum RequestCode + { + RequestNone = 0, + + Heart = 1, + + User = 2, + + Room = 3, + + Game = 4, + + } + + [global::ProtoBuf.ProtoContract()] + public enum ActionCode + { + ActionNone = 0, + + HeartBeat = 1, + + Register = 1000, + + Login = 1001, + + CreateRoom = 1002, + + FindRoom = 1003, + + GetPlayers = 1004, + + JoinRoom = 1005, + + ExitRoom = 1006, + + Chat = 2000, + + } + + [global::ProtoBuf.ProtoContract()] + public enum ReturnCode + { + ReturnNone = 0, + + Success = 1, + + Fail = 2, + + MsgTimeOut = 3, + + } + + [global::ProtoBuf.ProtoContract()] + public partial class MainPack + { + [global::ProtoBuf.ProtoMember(1)] + public RequestCode requestcode { get; set; } + + [global::ProtoBuf.ProtoMember(2)] + public ActionCode actioncode { get; set; } + + [global::ProtoBuf.ProtoMember(3)] + public ReturnCode returncode { get; set; } + + [global::ProtoBuf.ProtoMember(4)] + public LoginPack loginPack { get; set; } + + [global::ProtoBuf.ProtoMember(5)] + [global::System.ComponentModel.DefaultValue("")] + public string extstr { get; set; } + + [global::ProtoBuf.ProtoMember(6)] + public List roompack = new List(); + + [global::ProtoBuf.ProtoMember(7)] + public PlayerPack playerpack { get; set; } + + [global::ProtoBuf.ProtoMember(8)] + public long HeatEchoTime { get; set; } + + } + + [global::ProtoBuf.ProtoContract()] + public partial class LoginPack + { + [global::ProtoBuf.ProtoMember(1)] + [global::System.ComponentModel.DefaultValue("")] + public string username { get; set; } + + [global::ProtoBuf.ProtoMember(2)] + [global::System.ComponentModel.DefaultValue("")] + public string password { get; set; } + + } + + [global::ProtoBuf.ProtoContract()] + public partial class RoomPack + { + [global::ProtoBuf.ProtoMember(1)] + [global::System.ComponentModel.DefaultValue("")] + public string roomname { get; set; } + + [global::ProtoBuf.ProtoMember(2)] + public int maxnum { get; set; } + + [global::ProtoBuf.ProtoMember(3)] + public int curnum { get; set; } + + [global::ProtoBuf.ProtoMember(6)] + public int roomID { get; set; } + + [global::ProtoBuf.ProtoMember(12)] + public List playerpack = new List(); + + } + + [global::ProtoBuf.ProtoContract()] + public partial class PlayerPack + { + [global::ProtoBuf.ProtoMember(1)] + [global::System.ComponentModel.DefaultValue("")] + public string playerName { get; set; } + + [global::ProtoBuf.ProtoMember(2)] + [global::System.ComponentModel.DefaultValue("")] + public string playerID { get; set; } + + [global::ProtoBuf.ProtoMember(3)] + public int hp { get; set; } + + [global::ProtoBuf.ProtoMember(4)] + public PosPack posPack { get; set; } + + } + + [global::ProtoBuf.ProtoContract()] + public partial class PosPack + { + [global::ProtoBuf.ProtoMember(1)] + public float PosX { get; set; } + + [global::ProtoBuf.ProtoMember(2)] + public float PosY { get; set; } + + [global::ProtoBuf.ProtoMember(3)] + public float PosZ { get; set; } + + [global::ProtoBuf.ProtoMember(4)] + public float RotaX { get; set; } + + [global::ProtoBuf.ProtoMember(5)] + public float RotaY { get; set; } + + [global::ProtoBuf.ProtoMember(6)] + public float RotaZ { get; set; } + + [global::ProtoBuf.ProtoMember(8)] + public int Animation { get; set; } + + [global::ProtoBuf.ProtoMember(9)] + public int Direction { get; set; } + + [global::ProtoBuf.ProtoMember(10)] + public float MoveX { get; set; } + + [global::ProtoBuf.ProtoMember(11)] + public float MoveY { get; set; } + + [global::ProtoBuf.ProtoMember(12)] + public float MoveZ { get; set; } + + } + +} diff --git a/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/gen_all.bat b/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/gen_all.bat new file mode 100644 index 00000000..166a37c4 --- /dev/null +++ b/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/gen_all.bat @@ -0,0 +1,19 @@ +@echo off +setlocal enabledelayedexpansion +::Proto文件路径 +set SOURCE_PATH=..\Proto + +::C#文件生成路径 +set TARGET_PATH=..\Proto_CSharp + +::删除之前创建的文件 +del %TARGET_PATH%\*.cs /f /s /q + +echo ------------------------------------------------------------- + +for /R %SOURCE_PATH% %%f in (*.proto) do ( + set "FILE_PATH=%%~nxf" + echo handle file: !FILE_PATH! + protogen --proto_path=%SOURCE_PATH% --csharp_out=%TARGET_PATH% !FILE_PATH! +) +pause \ No newline at end of file diff --git a/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protobuf-net.Reflection.dll b/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protobuf-net.Reflection.dll new file mode 100644 index 00000000..1bcc729e Binary files /dev/null and b/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protobuf-net.Reflection.dll differ diff --git a/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protobuf-net.Reflection.xml b/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protobuf-net.Reflection.xml new file mode 100644 index 00000000..bb8df418 --- /dev/null +++ b/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protobuf-net.Reflection.xml @@ -0,0 +1,829 @@ + + + + protobuf-net.Reflection + + + + + Abstract root for a general purpose code-generator + + + + + The logical name of this code generator + + + + + Get a string representation of the instance + + + + + Execute the code generator against a FileDescriptorSet, yielding a sequence of files + + + + + Execute the code generator against a FileDescriptorSet, yielding a sequence of files + + + + + Eexecute this code generator against a code file + + + + + Eexecute this code generator against a set of code file + + + + + Abstract base class for a code generator that uses a visitor pattern + + + + + Obtain the access of an item, accounting for the model's hierarchy + + + + + Get the language version for this language from a schema + + + + + Obtain the access of an item, accounting for the model's hierarchy + + + + + Obtain the access of an item, accounting for the model's hierarchy + + + + + Obtain the access of an item, accounting for the model's hierarchy + + + + + Get the textual name of a given access level + + + + + The indentation used by this code generator + + + + + The file extension of the files generatred by this generator + + + + + Should case-sensitivity be used when computing conflicts? + + + + + Handle keyword escaping in the language of this code generator + + + + + + + Execute the code generator against a FileDescriptorSet, yielding a sequence of files + + + + + Emits the code for a file in a descriptor-set + + + + + Emit code representing an extension field + + + + + Emit code preceeding a set of extension fields + + + + + Emit code following a set of extension fields + + + + + Emit code preceeding a set of extension fields + + + + + Emit code following a set of extension fields + + + + + Emit code representing a service + + + + + Emit code following a set of service methods + + + + + Emit code representing a service method + + + + + Emit code following preceeding a set of service methods + + + + + Check whether a particular message should be suppressed - for example because it represents a map + + + + + Emit code representing a message type + + + + + Emit code terminating a constructor, if one is required + + + + + Emit code initializing field values inside a constructor, if one is required + + + + + Emit code beginning a constructor, if one is required + + true if a constructor is required + + + + Emit code representing a message field + + + + + Emit code following a set of message fields + + + + + Emit code preceeding a set of message fields + + + + + Emit code representing an enum type + + + + + Emit code representing 'oneof' elements as an enum discriminator + + + + + Emit code preceeding a set of enum values + + + + + Emit code representing an enum value + + + + + Emit code following a set of enum values + + + + + Emit code at the start of a file + + + + + Emit code at the end of a file + + + + + Emit the start of an enum declaration for 'oneof' groups, including the 0/None element + + + + + Emit a field-based entry for a 'oneof' groups's enum + + + + + Emit the end of an enum declaration for 'oneof' groups + + + + + Emit the discriminator accessor for 'oneof' groups + + + + + Convention-based suffix for 'oneof' enums + + + + + Convention-based suffix for 'oneof' discriminators + + + + + Represents the state of a code-generation invocation + + + + + The file being processed + + + + + The token to use for indentation + + + + + The current indent level + + + + + The mechanism to use for name normalization + + + + + The output for this code generation + + + + + The effective syntax of this code-generation cycle, defaulting to "proto2" if not explicity specified + + + + + Whether to emit enums and discriminators for oneof groups + + + + + Create a new GeneratorContext instance + + + + + Gets the value of an OPTION/VALUE pair provided to the system + + + + + Should default value initializers be emitted even for required values? + + + + + The specified language version (null if not specified) + + + + + Ends the current line + + + + + Appends a value and ends the current line + + + + + Appends a value to the current line + + + + + Increases the indentation level + + + + + Decreases the indentation level + + + + + Try to find a descriptor of the type specified by T with the given full name + + + + + Represents the union summary of a one-of declaration + + + + + The underlying descriptor + + + + + The effective index of this stub + + + + + A code generator that writes C# + + + + + Reusable code-generator instance + + + + + Create a new CSharpCodeGenerator instance + + + + + Returns the language name + + + + + Returns the default file extension + + + + + Escapes language keywords + + + + + Get the language version for this language from a schema + + + + + Start a file + + + + + End a file + + + + + Start an enum + + + + + End an enum + + + + + Write an enum value + + + + + End a message + + + + + Start a message + + + + + Get the language specific keyword representing an access level + + + + + Emit code beginning a constructor, if one is required + + true if a constructor is required + + + + Emit code terminating a constructor, if one is required + + + + + Emit code initializing field values inside a constructor, if one is required + + + + + Write a field + + + + + Starts an extgensions block + + + + + Ends an extgensions block + + + + + Starts an extensions block + + + + + Ends an extensions block + + + + + Write an extension + + + + + Emit the start of an enum declaration for 'oneof' groups, including the 0/None element + + + + + Emit the end of an enum declaration for 'oneof' groups + + + + + Emit a field-based entry for a 'oneof' groups's enum + + + + + Emit the discriminator accessor for 'oneof' groups + + + + + Provides general purpose name suggestions + + + + + Suggest a name with idiomatic pluralization + + + + + Suggest a name with idiomatic pluralization + + + + + Suggest a name with idiomatic name capitalization + + + + + Suggest a name with idiomatic pluralization + + + + + Name normalizer with default protobuf-net behaviour, using .NET idioms + + + + + Name normalizer that passes through all identifiers without any changes + + + + + Suggest a normalized identifier + + + + + Suggest a name with idiomatic pluralization + + + + + Suggest a normalized identifier + + + + + Suggest a normalized identifier + + + + + Suggest a normalized identifier + + + + + Suggest a normalized identifier + + + + + Suggest a normalized identifier + + + + + Suggest a normalized identifier + + + + + Obtain a set of all names defined for a message + + + + + Get the preferred name for an element + + + + + Describes a generated file + + + + + Get a string representation of this instance + + + + + + Create a new CodeFile instance + + + + + The name (including path if necessary) of this file + + + + + The contents of this file + + + + + Represents the overall result of a compilation process + + + + + The errors from this execution + + + + + The output files from this execution + + + + + Describes an error that occurred during processing + + + + + Parse an error from a PROTOC error message + + + + + Get a text representation of this instance + + + + + + True if this instance represents a non-fatal warning + + + + + True if this instance represents a fatal error + + + + + The file in which this error was identified + + + + + The source text relating to this error + + + + + The error message + + + + + The entire line contents in the source in which this error was located + + + + + The line number in which this error was located + + + + + The column number in which this error was located + + + + + A code generator that writes VB + + + + + Reusable code-generator instance + + + + + Should case-sensitivity be used when computing conflicts? + + + + + Create a new VBCodeGenerator instance + + + + + Returns the language name + + + + + Get the language version for this language from a schema + + + + + Returns the default file extension + + + + + Escapes language keywords + + + + + Start a file + + + + + End a file + + + + + Start an enum + + + + + End an enum + + + + + Write an enum value + + + + + Emit the discriminator accessor for 'oneof' groups + + + + + Emit the end of an enum declaration for 'oneof' groups + + + + + Emit the start of an enum declaration for 'oneof' groups, including the 0/None element + + + + + Emit a field-based entry for a 'oneof' groups's enum + + + + + End a message + + + + + Start a message + + + + + Get the language specific keyword representing an access level + + + + + Write a field + + + + + Starts an extgensions block + + + + + Ends an extgensions block + + + + + Starts an extensions block + + + + + Ends an extensions block + + + + + Write an extension + + + + + When processing Foo/Bar/Some.proto, should Foo/Bar be treated as an implicit import location? + + + + + Default package to use when none is specified; can use #FILE# and #DIR# tokens + + + + diff --git a/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protobuf-net.dll b/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protobuf-net.dll new file mode 100644 index 00000000..3562db52 Binary files /dev/null and b/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protobuf-net.dll differ diff --git a/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protobuf-net.xml b/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protobuf-net.xml new file mode 100644 index 00000000..f9d4c315 --- /dev/null +++ b/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protobuf-net.xml @@ -0,0 +1,3550 @@ + + + + protobuf-net + + + + + Provides support for common .NET types that do not have a direct representation + in protobuf, using the definitions from bcl.proto + + + + + Creates a new instance of the specified type, bypassing the constructor. + + The type to create + The new instance + If the platform does not support constructor-skipping + + + + The default value for dates that are following google.protobuf.Timestamp semantics + + + + + Writes a TimeSpan to a protobuf stream using protobuf-net's own representation, bcl.TimeSpan + + + + + Parses a TimeSpan from a protobuf stream using protobuf-net's own representation, bcl.TimeSpan + + + + + Parses a TimeSpan from a protobuf stream using the standardized format, google.protobuf.Duration + + + + + Writes a TimeSpan to a protobuf stream using the standardized format, google.protobuf.Duration + + + + + Parses a DateTime from a protobuf stream using the standardized format, google.protobuf.Timestamp + + + + + Writes a DateTime to a protobuf stream using the standardized format, google.protobuf.Timestamp + + + + + Parses a DateTime from a protobuf stream + + + + + Writes a DateTime to a protobuf stream, excluding the Kind + + + + + Writes a DateTime to a protobuf stream, including the Kind + + + + + Parses a decimal from a protobuf stream + + + + + Writes a decimal to a protobuf stream + + + + + Writes a Guid to a protobuf stream + + + + + Parses a Guid from a protobuf stream + + + + + Optional behaviours that introduce .NET-specific functionality + + + + + No special behaviour + + + + + Enables full object-tracking/full-graph support. + + + + + Embeds the type information into the stream, allowing usage with types not known in advance. + + + + + If false, the constructor for the type is bypassed during deserialization, meaning any field initializers + or other initialization code is skipped. + + + + + Should the object index be reserved, rather than creating an object promptly + + + + + Reads an *implementation specific* bundled .NET object, including (as options) type-metadata, identity/re-use, etc. + + + + + Writes an *implementation specific* bundled .NET object, including (as options) type-metadata, identity/re-use, etc. + + + + + Provides a simple buffer-based implementation of an extension object. + + + + + https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/gcallowverylargeobjects-element + + + + Specifies a method on the root-contract in an hierarchy to be invoked before serialization. + + + Specifies a method on the root-contract in an hierarchy to be invoked after serialization. + + + Specifies a method on the root-contract in an hierarchy to be invoked before deserialization. + + + Specifies a method on the root-contract in an hierarchy to be invoked after deserialization. + + + + Pushes a null reference onto the stack. Note that this should only + be used to return a null (or set a variable to null); for null-tests + use BranchIfTrue / BranchIfFalse. + + + + + Creates a new "using" block (equivalent) around a variable; + the variable must exist, and note that (unlike in C#) it is + the variables *final* value that gets disposed. If you need + *original* disposal, copy your variable first. + + It is the callers responsibility to ensure that the variable's + scope fully-encapsulates the "using"; if not, the variable + may be re-used (and thus re-assigned) unexpectedly. + + + + + Sub-format to use when serializing/deserializing data + + + + + Uses the default encoding for the data-type. + + + + + When applied to signed integer-based data (including Decimal), this + indicates that zigzag variant encoding will be used. This means that values + with small magnitude (regardless of sign) take a small amount + of space to encode. + + + + + When applied to signed integer-based data (including Decimal), this + indicates that two's-complement variant encoding will be used. + This means that any -ve number will take 10 bytes (even for 32-bit), + so should only be used for compatibility. + + + + + When applied to signed integer-based data (including Decimal), this + indicates that a fixed amount of space will be used. + + + + + When applied to a sub-message, indicates that the value should be treated + as group-delimited. + + + + + When applied to members of types such as DateTime or TimeSpan, specifies + that the "well known" standardized representation should be use; DateTime uses Timestamp, + + + + + Represent multiple types as a union; this is used as part of OneOf - + note that it is the caller's responsbility to only read/write the value as the same type + + + The value typed as Object + + + Indicates whether the specified discriminator is assigned + + + Create a new discriminated union value + + + Reset a value if the specified discriminator is assigned + + + The discriminator value + + + Represent multiple types as a union; this is used as part of OneOf - + note that it is the caller's responsbility to only read/write the value as the same type + + + The value typed as Int64 + + + The value typed as UInt64 + + + The value typed as Int32 + + + The value typed as UInt32 + + + The value typed as Boolean + + + The value typed as Single + + + The value typed as Double + + + The value typed as DateTime + + + The value typed as TimeSpan + + + Indicates whether the specified discriminator is assigned + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Reset a value if the specified discriminator is assigned + + + The discriminator value + + + Represent multiple types as a union; this is used as part of OneOf - + note that it is the caller's responsbility to only read/write the value as the same type + + + The value typed as Int64 + + + The value typed as UInt64 + + + The value typed as Int32 + + + The value typed as UInt32 + + + The value typed as Boolean + + + The value typed as Single + + + The value typed as Double + + + The value typed as DateTime + + + The value typed as TimeSpan + + + The value typed as Guid + + + The value typed as Object + + + Indicates whether the specified discriminator is assigned + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Reset a value if the specified discriminator is assigned + + + The discriminator value + + + Represent multiple types as a union; this is used as part of OneOf - + note that it is the caller's responsbility to only read/write the value as the same type + + + The value typed as Int64 + + + The value typed as UInt64 + + + The value typed as Int32 + + + The value typed as UInt32 + + + The value typed as Boolean + + + The value typed as Single + + + The value typed as Double + + + The value typed as DateTime + + + The value typed as TimeSpan + + + The value typed as Guid + + + Indicates whether the specified discriminator is assigned + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Reset a value if the specified discriminator is assigned + + + The discriminator value + + + Represent multiple types as a union; this is used as part of OneOf - + note that it is the caller's responsbility to only read/write the value as the same type + + + The value typed as Int64 + + + The value typed as UInt64 + + + The value typed as Int32 + + + The value typed as UInt32 + + + The value typed as Boolean + + + The value typed as Single + + + The value typed as Double + + + The value typed as DateTime + + + The value typed as TimeSpan + + + The value typed as Object + + + Indicates whether the specified discriminator is assigned + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Reset a value if the specified discriminator is assigned + + + The discriminator value + + + Represent multiple types as a union; this is used as part of OneOf - + note that it is the caller's responsbility to only read/write the value as the same type + + + The value typed as Int32 + + + The value typed as UInt32 + + + The value typed as Boolean + + + The value typed as Single + + + Indicates whether the specified discriminator is assigned + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Reset a value if the specified discriminator is assigned + + + The discriminator value + + + Represent multiple types as a union; this is used as part of OneOf - + note that it is the caller's responsbility to only read/write the value as the same type + + + The value typed as Int32 + + + The value typed as UInt32 + + + The value typed as Boolean + + + The value typed as Single + + + The value typed as Object + + + Indicates whether the specified discriminator is assigned + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Reset a value if the specified discriminator is assigned + + + The discriminator value + + + + Simple base class for supporting unexpected fields allowing + for loss-less round-tips/merge, even if the data is not understod. + The additional fields are (by default) stored in-memory in a buffer. + + As an example of an alternative implementation, you might + choose to use the file system (temporary files) as the back-end, tracking + only the paths [such an object would ideally be IDisposable and use + a finalizer to ensure that the files are removed]. + + + + + Retrieves the extension object for the current + instance, optionally creating it if it does not already exist. + + Should a new extension object be + created if it does not already exist? + The extension object if it exists (or was created), or null + if the extension object does not exist or is not available. + The createIfMissing argument is false during serialization, + and true during deserialization upon encountering unexpected fields. + + + + Provides a simple, default implementation for extension support, + optionally creating it if it does not already exist. Designed to be called by + classes implementing . + + Should a new extension object be + created if it does not already exist? + The extension field to check (and possibly update). + The extension object if it exists (or was created), or null + if the extension object does not exist or is not available. + The createIfMissing argument is false during serialization, + and true during deserialization upon encountering unexpected fields. + + + + Appends the value as an additional (unexpected) data-field for the instance. + Note that for non-repeated sub-objects, this equates to a merge operation; + for repeated sub-objects this adds a new instance to the set; for simple + values the new value supercedes the old value. + + Note that appending a value does not remove the old value from + the stream; avoid repeatedly appending values for the same field. + The type of the value to append. + The extensible object to append the value to. + The field identifier; the tag should not be defined as a known data-field for the instance. + The value to append. + + + + Appends the value as an additional (unexpected) data-field for the instance. + Note that for non-repeated sub-objects, this equates to a merge operation; + for repeated sub-objects this adds a new instance to the set; for simple + values the new value supercedes the old value. + + Note that appending a value does not remove the old value from + the stream; avoid repeatedly appending values for the same field. + The data-type of the field. + The data-format to use when encoding the value. + The extensible object to append the value to. + The field identifier; the tag should not be defined as a known data-field for the instance. + The value to append. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + The value returned is the composed value after merging any duplicated content; if the + value is "repeated" (a list), then use GetValues instead. + + The data-type of the field. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + The effective value of the field, or the default value if not found. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + The value returned is the composed value after merging any duplicated content; if the + value is "repeated" (a list), then use GetValues instead. + + The data-type of the field. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + The data-format to use when decoding the value. + The effective value of the field, or the default value if not found. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + The value returned (in "value") is the composed value after merging any duplicated content; + if the value is "repeated" (a list), then use GetValues instead. + + The data-type of the field. + The effective value of the field, or the default value if not found. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + True if data for the field was present, false otherwise. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + The value returned (in "value") is the composed value after merging any duplicated content; + if the value is "repeated" (a list), then use GetValues instead. + + The data-type of the field. + The effective value of the field, or the default value if not found. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + The data-format to use when decoding the value. + True if data for the field was present, false otherwise. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + The value returned (in "value") is the composed value after merging any duplicated content; + if the value is "repeated" (a list), then use GetValues instead. + + The data-type of the field. + The effective value of the field, or the default value if not found. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + The data-format to use when decoding the value. + Allow tags that are present as part of the definition; for example, to query unknown enum values. + True if data for the field was present, false otherwise. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + Each occurrence of the field is yielded separately, making this usage suitable for "repeated" + (list) fields. + + The extended data is processed lazily as the enumerator is iterated. + The data-type of the field. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + An enumerator that yields each occurrence of the field. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + Each occurrence of the field is yielded separately, making this usage suitable for "repeated" + (list) fields. + + The extended data is processed lazily as the enumerator is iterated. + The data-type of the field. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + The data-format to use when decoding the value. + An enumerator that yields each occurrence of the field. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + The value returned (in "value") is the composed value after merging any duplicated content; + if the value is "repeated" (a list), then use GetValues instead. + + The data-type of the field. + The model to use for configuration. + The effective value of the field, or the default value if not found. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + The data-format to use when decoding the value. + Allow tags that are present as part of the definition; for example, to query unknown enum values. + True if data for the field was present, false otherwise. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + Each occurrence of the field is yielded separately, making this usage suitable for "repeated" + (list) fields. + + The extended data is processed lazily as the enumerator is iterated. + The model to use for configuration. + The data-type of the field. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + The data-format to use when decoding the value. + An enumerator that yields each occurrence of the field. + + + + Appends the value as an additional (unexpected) data-field for the instance. + Note that for non-repeated sub-objects, this equates to a merge operation; + for repeated sub-objects this adds a new instance to the set; for simple + values the new value supercedes the old value. + + Note that appending a value does not remove the old value from + the stream; avoid repeatedly appending values for the same field. + The model to use for configuration. + The data-format to use when encoding the value. + The extensible object to append the value to. + The field identifier; the tag should not be defined as a known data-field for the instance. + The value to append. + + + + This class acts as an internal wrapper allowing us to do a dynamic + methodinfo invoke; an't put into Serializer as don't want on public + API; can't put into Serializer<T> since we need to invoke + across classes + + + + + All this does is call GetExtendedValuesTyped with the correct type for "instance"; + this ensures that we don't get issues with subclasses declaring conflicting types - + the caller must respect the fields defined for the type they pass in. + + + + + All this does is call GetExtendedValuesTyped with the correct type for "instance"; + this ensures that we don't get issues with subclasses declaring conflicting types - + the caller must respect the fields defined for the type they pass in. + + + + + Not all frameworks are created equal (fx1.1 vs fx2.0, + micro-framework, compact-framework, + silverlight, etc). This class simply wraps up a few things that would + otherwise make the real code unnecessarily messy, providing fallback + implementations if necessary. + + + + + Intended to be a direct map to regular TypeCode, but: + - with missing types + - existing on WinRT + + + + + Indicates that the implementing type has support for protocol-buffer + extensions. + + Can be implemented by deriving from Extensible. + + + + Retrieves the extension object for the current + instance, optionally creating it if it does not already exist. + + Should a new extension object be + created if it does not already exist? + The extension object if it exists (or was created), or null + if the extension object does not exist or is not available. + The createIfMissing argument is false during serialization, + and true during deserialization upon encountering unexpected fields. + + + + Provides addition capability for supporting unexpected fields during + protocol-buffer serialization/deserialization. This allows for loss-less + round-trip/merge, even when the data is not fully understood. + + + + + Requests a stream into which any unexpected fields can be persisted. + + A new stream suitable for storing data. + + + + Indicates that all unexpected fields have now been stored. The + implementing class is responsible for closing the stream. If + "commit" is not true the data may be discarded. + + The stream originally obtained by BeginAppend. + True if the append operation completed successfully. + + + + Requests a stream of the unexpected fields previously stored. + + A prepared stream of the unexpected fields. + + + + Indicates that all unexpected fields have now been read. The + implementing class is responsible for closing the stream. + + The stream originally obtained by BeginQuery. + + + + Requests the length of the raw binary stream; this is used + when serializing sub-entities to indicate the expected size. + + The length of the binary stream representing unexpected data. + + + + Provides the ability to remove all existing extension data + + + + + Remove all existing extension data + + + + + Specifies the method used to infer field tags for members of the type + under consideration. Tags are deduced using the invariant alphabetic + sequence of the members' names; this makes implicit field tags very brittle, + and susceptible to changes such as field names (normally an isolated + change). + + + + + No members are serialized implicitly; all members require a suitable + attribute such as [ProtoMember]. This is the recmomended mode for + most scenarios. + + + + + Public properties and fields are eligible for implicit serialization; + this treats the public API as a contract. Ordering beings from ImplicitFirstTag. + + + + + Public and non-public fields are eligible for implicit serialization; + this acts as a state/implementation serializer. Ordering beings from ImplicitFirstTag. + + + + + Represents the set of serialization callbacks to be used when serializing/deserializing a type. + + + + Called before serializing an instance + + + Called before deserializing an instance + + + Called after serializing an instance + + + Called after deserializing an instance + + + + True if any callback is set, else False + + + + + Represents a type at runtime for use with protobuf, allowing the field mappings (etc) to be defined + + + + + Get the name of the type being represented + + + + + Gets the base-type for this type + + + + + When used to compile a model, should public serialization/deserialzation methods + be included for this type? + + + + + Should this type be treated as a reference by default? + + + + + Adds a known sub-type to the inheritance model + + + + + Adds a known sub-type to the inheritance model + + + + + Indicates whether the current type has defined callbacks + + + + + Indicates whether the current type has defined subtypes + + + + + Returns the set of callbacks defined for this type + + + + + Assigns the callbacks to use during serialiation/deserialization. + + The method (or null) called before serialization begins. + The method (or null) called when serialization is complete. + The method (or null) called before deserialization begins (or when a new instance is created during deserialization). + The method (or null) called when deserialization is complete. + The set of callbacks. + + + + Assigns the callbacks to use during serialiation/deserialization. + + The name of the method (or null) called before serialization begins. + The name of the method (or null) called when serialization is complete. + The name of the method (or null) called before deserialization begins (or when a new instance is created during deserialization). + The name of the method (or null) called when deserialization is complete. + The set of callbacks. + + + + Gets or sets the name of this contract. + + + + + Designate a factory-method to use to create instances of this type + + + + + Designate a factory-method to use to create instances of this type + + + + + Throws an exception if the type has been made immutable + + + + + The runtime type that the meta-type represents + + + + + Adds a member (by name) to the MetaType + + + + + Adds a member (by name) to the MetaType, returning the ValueMember rather than the fluent API. + This is otherwise identical to Add. + + + + + Gets or sets whether the type should use a parameterless constructor (the default), + or whether the type should skip the constructor completely. This option is not supported + on compact-framework. + + + + + The concrete type to create when a new instance of this type is needed; this may be useful when dealing + with dynamic proxies, or with interface-based APIs + + + + + Adds a member (by name) to the MetaType + + + + + Performs serialization of this type via a surrogate; all + other serialization options are ignored and handled + by the surrogate's configuration. + + + + + Adds a set of members (by name) to the MetaType + + + + + Adds a member (by name) to the MetaType + + + + + Adds a member (by name) to the MetaType, including an itemType and defaultType for representing lists + + + + + Adds a member (by name) to the MetaType, including an itemType and defaultType for representing lists, returning the ValueMember rather than the fluent API. + This is otherwise identical to Add. + + + + + Returns the ValueMember that matchs a given field number, or null if not found + + + + + Returns the ValueMember that matchs a given member (property/field), or null if not found + + + + + Returns the ValueMember instances associated with this type + + + + + Returns the SubType instances associated with this type + + + + + Compiles the serializer for this type; this is *not* a full + standalone compile, but can significantly boost performance + while allowing additional types to be added. + + An in-place compile can access non-public types / members + + + + Gets or sets a value indicating that an enum should be treated directly as an int/short/etc, rather + than enforcing .proto enum rules. This is useful *in particul* for [Flags] enums. + + + + + Gets or sets a value indicating that this type should NOT be treated as a list, even if it has + familiar list-like characteristics (enumerable, add, etc) + + + + + Indicates whether this type should always be treated as a "group" (rather than a string-prefixed sub-message) + + + + + Indiate the variant of the protobuf .proto DSL syntax to use + + + + + https://developers.google.com/protocol-buffers/docs/proto + + + + + https://developers.google.com/protocol-buffers/docs/proto3 + + + + + Provides protobuf serialization support for a number of types that can be defined at runtime + + + + + Global default that + enables/disables automatic tag generation based on the existing name / order + of the defined members. See + for usage and important warning / explanation. + You must set the global default before attempting to serialize/deserialize any + impacted type. + + + + + Global default that determines whether types are considered serializable + if they have [DataContract] / [XmlType]. With this enabled, ONLY + types marked as [ProtoContract] are added automatically. + + + + + Global switch that enables or disables the implicit + handling of "zero defaults"; meanning: if no other default is specified, + it assumes bools always default to false, integers to zero, etc. + + If this is disabled, no such assumptions are made and only *explicit* + default values are processed. This is enabled by default to + preserve similar logic to v1. + + + + + Global switch that determines whether types with a .ToString() and a Parse(string) + should be serialized as strings. + + + + + Global switch that determines whether DateTime serialization should include the Kind of the date/time. + + + + + Global switch that determines whether a single instance of the same string should be used during deserialization. + + Note this does not use the global .NET string interner + + + + Should the Kind be included on date/time values? + + + + + The default model, used to support ProtoBuf.Serializer + + + + + Returns a sequence of the Type instances that can be + processed by this model. + + + + + Suggest a .proto definition for the given type + + The type to generate a .proto definition for, or null to generate a .proto that represents the entire model + The .proto definition as a string + The .proto syntax to use + + + + Obtains the MetaType associated with a given Type for the current model, + allowing additional configuration. + + + + + Adds support for an additional type in this model, optionally + applying inbuilt patterns. If the type is already known to the + model, the existing type is returned **without** applying + any additional behaviour. + + Inbuilt patterns include: + [ProtoContract]/[ProtoMember(n)] + [DataContract]/[DataMember(Order=n)] + [XmlType]/[XmlElement(Order=n)] + [On{Des|S}erializ{ing|ed}] + ShouldSerialize*/*Specified + + The type to be supported + Whether to apply the inbuilt configuration patterns (via attributes etc), or + just add the type with no additional configuration (the type must then be manually configured). + The MetaType representing this type, allowing + further configuration. + + + + Should serializers be compiled on demand? It may be useful + to disable this for debugging purposes. + + + + + Should support for unexpected types be added automatically? + If false, an exception is thrown when unexpected types + are encountered. + + + + + Verifies that the model is still open to changes; if not, an exception is thrown + + + + + Prevents further changes to this model + + + + + Provides the key that represents a given type in the current model. + + + + + Writes a protocol-buffer representation of the given instance to the supplied stream. + + Represents the type (including inheritance) to consider. + The existing instance to be serialized (cannot be null). + The destination stream to write to. + + + + Applies a protocol-buffer stream to an existing instance (which may be null). + + Represents the type (including inheritance) to consider. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Compiles the serializers individually; this is *not* a full + standalone compile, but can significantly boost performance + while allowing additional types to be added. + + An in-place compile can access non-public types / members + + + + Fully compiles the current model into a static-compiled model instance + + A full compilation is restricted to accessing public types / members + An instance of the newly created compiled type-model + + + + Represents configuration options for compiling a model to + a standalone assembly. + + + + + Import framework options from an existing type + + + + + The TargetFrameworkAttribute FrameworkName value to burn into the generated assembly + + + + + The TargetFrameworkAttribute FrameworkDisplayName value to burn into the generated assembly + + + + + The name of the TypeModel class to create + + + + + The path for the new dll + + + + + The runtime version for the generated assembly + + + + + The runtime version for the generated assembly + + + + + The acecssibility of the generated serializer + + + + + Type accessibility + + + + + Available to all callers + + + + + Available to all callers in the same assembly, or assemblies specified via [InternalsVisibleTo(...)] + + + + + Fully compiles the current model into a static-compiled serialization dll + (the serialization dll still requires protobuf-net for support services). + + A full compilation is restricted to accessing public types / members + The name of the TypeModel class to create + The path for the new dll + An instance of the newly created compiled type-model + + + + Fully compiles the current model into a static-compiled serialization dll + (the serialization dll still requires protobuf-net for support services). + + A full compilation is restricted to accessing public types / members + An instance of the newly created compiled type-model + + + + The amount of time to wait if there are concurrent metadata access operations + + + + + If a lock-contention is detected, this event signals the *owner* of the lock responsible for the blockage, indicating + what caused the problem; this is only raised if the lock-owning code successfully completes. + + + + + Designate a factory-method to use to create instances of any type; note that this only affect types seen by the serializer *after* setting the factory. + + + + + Contains the stack-trace of the owning code when a lock-contention scenario is detected + + + + + The stack-trace of the code that owned the lock when a lock-contention scenario occurred + + + + + Event-type that is raised when a lock-contention scenario is detected + + + + + Represents an inherited type in a type hierarchy. + + + + + The field-number that is used to encapsulate the data (as a nested + message) for the derived dype. + + + + + The sub-type to be considered. + + + + + Creates a new SubType instance. + + The field-number that is used to encapsulate the data (as a nested + message) for the derived dype. + The sub-type to be considered. + Specific encoding style to use; in particular, Grouped can be used to avoid buffering, but is not the default. + + + + Event arguments needed to perform type-formatting functions; this could be resolving a Type to a string suitable for serialization, or could + be requesting a Type from a string. If no changes are made, a default implementation will be used (from the assembly-qualified names). + + + + + The type involved in this map; if this is initially null, a Type is expected to be provided for the string in FormattedName. + + + + + The formatted-name involved in this map; if this is initially null, a formatted-name is expected from the type in Type. + + + + + Delegate type used to perform type-formatting functions; the sender originates as the type-model. + + + + + Provides protobuf serialization support for a number of types + + + + + Should the Kind be included on date/time values? + + + + + Resolve a System.Type to the compiler-specific type + + + + + Resolve a System.Type to the compiler-specific type + + + + + This is the more "complete" version of Serialize, which handles single instances of mapped types. + The value is written as a complete field, including field-header and (for sub-objects) a + length-prefix + In addition to that, this provides support for: + - basic values; individual int / string / Guid / etc + - IEnumerable sequences of any type handled by TrySerializeAuxiliaryType + + + + + + Writes a protocol-buffer representation of the given instance to the supplied stream. + + The existing instance to be serialized (cannot be null). + The destination stream to write to. + + + + Writes a protocol-buffer representation of the given instance to the supplied stream. + + The existing instance to be serialized (cannot be null). + The destination stream to write to. + Additional information about this serialization operation. + + + + Writes a protocol-buffer representation of the given instance to the supplied writer. + + The existing instance to be serialized (cannot be null). + The destination writer to write to. + + + + Applies a protocol-buffer stream to an existing instance (or null), using length-prefixed + data - useful with network IO. + + The type being merged. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + How to encode the length prefix. + The tag used as a prefix to each record (only used with base-128 style prefixes). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Applies a protocol-buffer stream to an existing instance (or null), using length-prefixed + data - useful with network IO. + + The type being merged. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + How to encode the length prefix. + The tag used as a prefix to each record (only used with base-128 style prefixes). + Used to resolve types on a per-field basis. + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Applies a protocol-buffer stream to an existing instance (or null), using length-prefixed + data - useful with network IO. + + The type being merged. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + How to encode the length prefix. + The tag used as a prefix to each record (only used with base-128 style prefixes). + Used to resolve types on a per-field basis. + Returns the number of bytes consumed by this operation (includes length-prefix overheads and any skipped data). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Applies a protocol-buffer stream to an existing instance (or null), using length-prefixed + data - useful with network IO. + + The type being merged. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + How to encode the length prefix. + The tag used as a prefix to each record (only used with base-128 style prefixes). + Used to resolve types on a per-field basis. + Returns the number of bytes consumed by this operation (includes length-prefix overheads and any skipped data). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Reads a sequence of consecutive length-prefixed items from a stream, using + either base-128 or fixed-length prefixes. Base-128 prefixes with a tag + are directly comparable to serializing multiple items in succession + (use the tag to emulate the implicit behavior + when serializing a list/array). When a tag is + specified, any records with different tags are silently omitted. The + tag is ignored. The tag is ignores for fixed-length prefixes. + + The binary stream containing the serialized records. + The prefix style used in the data. + The tag of records to return (if non-positive, then no tag is + expected and all records are returned). + On a field-by-field basis, the type of object to deserialize (can be null if "type" is specified). + The type of object to deserialize (can be null if "resolver" is specified). + The sequence of deserialized objects. + + + + Reads a sequence of consecutive length-prefixed items from a stream, using + either base-128 or fixed-length prefixes. Base-128 prefixes with a tag + are directly comparable to serializing multiple items in succession + (use the tag to emulate the implicit behavior + when serializing a list/array). When a tag is + specified, any records with different tags are silently omitted. The + tag is ignored. The tag is ignores for fixed-length prefixes. + + The binary stream containing the serialized records. + The prefix style used in the data. + The tag of records to return (if non-positive, then no tag is + expected and all records are returned). + On a field-by-field basis, the type of object to deserialize (can be null if "type" is specified). + The type of object to deserialize (can be null if "resolver" is specified). + The sequence of deserialized objects. + Additional information about this serialization operation. + + + + Reads a sequence of consecutive length-prefixed items from a stream, using + either base-128 or fixed-length prefixes. Base-128 prefixes with a tag + are directly comparable to serializing multiple items in succession + (use the tag to emulate the implicit behavior + when serializing a list/array). When a tag is + specified, any records with different tags are silently omitted. The + tag is ignored. The tag is ignores for fixed-length prefixes. + + The type of object to deserialize. + The binary stream containing the serialized records. + The prefix style used in the data. + The tag of records to return (if non-positive, then no tag is + expected and all records are returned). + The sequence of deserialized objects. + + + + Reads a sequence of consecutive length-prefixed items from a stream, using + either base-128 or fixed-length prefixes. Base-128 prefixes with a tag + are directly comparable to serializing multiple items in succession + (use the tag to emulate the implicit behavior + when serializing a list/array). When a tag is + specified, any records with different tags are silently omitted. The + tag is ignored. The tag is ignores for fixed-length prefixes. + + The type of object to deserialize. + The binary stream containing the serialized records. + The prefix style used in the data. + The tag of records to return (if non-positive, then no tag is + expected and all records are returned). + The sequence of deserialized objects. + Additional information about this serialization operation. + + + + Writes a protocol-buffer representation of the given instance to the supplied stream, + with a length-prefix. This is useful for socket programming, + as DeserializeWithLengthPrefix can be used to read the single object back + from an ongoing stream. + + The type being serialized. + The existing instance to be serialized (cannot be null). + How to encode the length prefix. + The destination stream to write to. + The tag used as a prefix to each record (only used with base-128 style prefixes). + + + + Writes a protocol-buffer representation of the given instance to the supplied stream, + with a length-prefix. This is useful for socket programming, + as DeserializeWithLengthPrefix can be used to read the single object back + from an ongoing stream. + + The type being serialized. + The existing instance to be serialized (cannot be null). + How to encode the length prefix. + The destination stream to write to. + The tag used as a prefix to each record (only used with base-128 style prefixes). + Additional information about this serialization operation. + + + + Applies a protocol-buffer stream to an existing instance (which may be null). + + The type (including inheritance) to consider. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Applies a protocol-buffer stream to an existing instance (which may be null). + + The type (including inheritance) to consider. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + Additional information about this serialization operation. + + + + Applies a protocol-buffer stream to an existing instance (which may be null). + + The type (including inheritance) to consider. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The number of bytes to consume. + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Applies a protocol-buffer stream to an existing instance (which may be null). + + The type (including inheritance) to consider. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The number of bytes to consume. + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Applies a protocol-buffer stream to an existing instance (which may be null). + + The type (including inheritance) to consider. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The number of bytes to consume (or -1 to read to the end of the stream). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + Additional information about this serialization operation. + + + + Applies a protocol-buffer stream to an existing instance (which may be null). + + The type (including inheritance) to consider. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The number of bytes to consume (or -1 to read to the end of the stream). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + Additional information about this serialization operation. + + + + Applies a protocol-buffer reader to an existing instance (which may be null). + + The type (including inheritance) to consider. + The existing instance to be modified (can be null). + The reader to apply to the instance (cannot be null). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + This is the more "complete" version of Deserialize, which handles single instances of mapped types. + The value is read as a complete field, including field-header and (for sub-objects) a + length-prefix..kmc + + In addition to that, this provides support for: + - basic values; individual int / string / Guid / etc + - IList sets of any type handled by TryDeserializeAuxiliaryType + + + + + Creates a new runtime model, to which the caller + can add support for a range of types. A model + can be used "as is", or can be compiled for + optimal performance. + + + + + Applies common proxy scenarios, resolving the actual type to consider + + + + + Indicates whether the supplied type is explicitly modelled by the model + + + + + Provides the key that represents a given type in the current model. + The type is also normalized for proxies at the same time. + + + + + Advertise that a type's key can have changed + + + + + Provides the key that represents a given type in the current model. + + + + + Writes a protocol-buffer representation of the given instance to the supplied stream. + + Represents the type (including inheritance) to consider. + The existing instance to be serialized (cannot be null). + The destination stream to write to. + + + + Applies a protocol-buffer stream to an existing instance (which may be null). + + Represents the type (including inheritance) to consider. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Indicates the type of callback to be used + + + + + Invoked before an object is serialized + + + + + Invoked after an object is serialized + + + + + Invoked before an object is deserialized (or when a new instance is created) + + + + + Invoked after an object is deserialized + + + + + Create a deep clone of the supplied instance; any sub-items are also cloned. + + + + + Indicates that while an inheritance tree exists, the exact type encountered was not + specified in that hierarchy and cannot be processed. + + + + + Indicates that the given type was not expected, and cannot be processed. + + + + + Indicates that the given type cannot be constructed; it may still be possible to + deserialize into existing instances. + + + + + Returns true if the type supplied is either a recognised contract type, + or a *list* of a recognised contract type. + + Note that primitives always return false, even though the engine + will, if forced, try to serialize such + True if this type is recognised as a serializable entity, else false + + + + Returns true if the type supplied is a basic type with inbuilt handling, + a recognised contract type, or a *list* of a basic / contract type. + + + + + Returns true if the type supplied is a basic type with inbuilt handling, + or a *list* of a basic type with inbuilt handling + + + + + Suggest a .proto definition for the given type + + The type to generate a .proto definition for, or null to generate a .proto that represents the entire model + The .proto definition as a string + + + + Suggest a .proto definition for the given type + + The type to generate a .proto definition for, or null to generate a .proto that represents the entire model + The .proto definition as a string + The .proto syntax to use for the operation + + + + Used to provide custom services for writing and parsing type names when using dynamic types. Both parsing and formatting + are provided on a single API as it is essential that both are mapped identically at all times. + + + + + Creates a new IFormatter that uses protocol-buffer [de]serialization. + + A new IFormatter to be used during [de]serialization. + The type of object to be [de]deserialized by the formatter. + + + + Represents a member (property/field) that is mapped to a protobuf field + + + + + The number that identifies this member in a protobuf stream + + + + + Gets the member (field/property) which this member relates to. + + + + + Gets the backing member (field/property) which this member relates to + + + + + Within a list / array / etc, the type of object for each item in the list (especially useful with ArrayList) + + + + + The underlying type of the member + + + + + For abstract types (IList etc), the type of concrete object to create (if required) + + + + + The type the defines the member + + + + + The default value of the item (members with this value will not be serialized) + + + + + Creates a new ValueMember instance + + + + + Creates a new ValueMember instance + + + + + Specifies the rules used to process the field; this is used to determine the most appropriate + wite-type, but also to describe subtypes within that wire-type (such as SignedVariant) + + + + + Indicates whether this field should follow strict encoding rules; this means (for example) that if a "fixed32" + is encountered when "variant" is defined, then it will fail (throw an exception) when parsing. Note that + when serializing the defined type is always used. + + + + + Indicates whether this field should use packed encoding (which can save lots of space for repeated primitive values). + This option only applies to list/array data of primitive types (int, double, etc). + + + + + Indicates whether this field should *repace* existing values (the default is false, meaning *append*). + This option only applies to list/array data. + + + + + Indicates whether this field is mandatory. + + + + + Enables full object-tracking/full-graph support. + + + + + Embeds the type information into the stream, allowing usage with types not known in advance. + + + + + Indicates that the member should be treated as a protobuf Map + + + + + Specifies the data-format that should be used for the key, when IsMap is enabled + + + + + Specifies the data-format that should be used for the value, when IsMap is enabled + + + + + Specifies methods for working with optional data members. + + Provides a method (null for none) to query whether this member should + be serialized; it must be of the form "bool {Method}()". The member is only serialized if the + method returns true. + Provides a method (null for none) to indicate that a member was + deserialized; it must be of the form "void {Method}(bool)", and will be called with "true" + when data is found. + + + + Gets the logical name for this member in the schema (this is not critical for binary serialization, but may be used + when inferring a schema). + + + + + Should lists have extended support for null values? Note this makes the serialization less efficient. + + + + + Specifies the type of prefix that should be applied to messages. + + + + + No length prefix is applied to the data; the data is terminated only be the end of the stream. + + + + + A base-128 length prefix is applied to the data (efficient for short messages). + + + + + A fixed-length (little-endian) length prefix is applied to the data (useful for compatibility). + + + + + A fixed-length (big-endian) length prefix is applied to the data (useful for compatibility). + + + + + Indicates that a type is defined for protocol-buffer serialization. + + + + + Gets or sets the defined name of the type. + + + + + Gets or sets the fist offset to use with implicit field tags; + only uesd if ImplicitFields is set. + + + + + If specified, alternative contract markers (such as markers for XmlSerailizer or DataContractSerializer) are ignored. + + + + + If specified, do NOT treat this type as a list, even if it looks like one. + + + + + Gets or sets the mechanism used to automatically infer field tags + for members. This option should be used in advanced scenarios only. + Please review the important notes against the ImplicitFields enumeration. + + + + + Enables/disables automatic tag generation based on the existing name / order + of the defined members. This option is not used for members marked + with ProtoMemberAttribute, as intended to provide compatibility with + WCF serialization. WARNING: when adding new fields you must take + care to increase the Order for new elements, otherwise data corruption + may occur. + + If not explicitly specified, the default is assumed from Serializer.GlobalOptions.InferTagFromName. + + + + Has a InferTagFromName value been explicitly set? if not, the default from the type-model is assumed. + + + + + Specifies an offset to apply to [DataMember(Order=...)] markers; + this is useful when working with mex-generated classes that have + a different origin (usually 1 vs 0) than the original data-contract. + + This value is added to the Order of each member. + + + + + If true, the constructor for the type is bypassed during deserialization, meaning any field initializers + or other initialization code is skipped. + + + + + Should this type be treated as a reference by default? Please also see the implications of this, + as recorded on ProtoMemberAttribute.AsReference + + + + + Indicates whether this type should always be treated as a "group" (rather than a string-prefixed sub-message) + + + + + Applies only to enums (not to DTO classes themselves); gets or sets a value indicating that an enum should be treated directly as an int/short/etc, rather + than enforcing .proto enum rules. This is useful *in particul* for [Flags] enums. + + + + + Allows to define a surrogate type used for serialization/deserialization purpose. + + + + + Has a EnumPassthru value been explicitly set? + + + + + Indicates that a static member should be considered the same as though + were an implicit / explicit conversion operator; in particular, this + is useful for conversions that operator syntax does not allow, such as + to/from interface types. + + + + + Used to define protocol-buffer specific behavior for + enumerated values. + + + + + Gets or sets the specific value to use for this enum during serialization. + + + + + Indicates whether this instance has a customised value mapping + + true if a specific value is set + + + + Gets or sets the defined name of the enum, as used in .proto + (this name is not used during serialization). + + + + + Indicates an error during serialization/deserialization of a proto stream. + + + + Creates a new ProtoException instance. + + + Creates a new ProtoException instance. + + + Creates a new ProtoException instance. + + + Creates a new ProtoException instance. + + + + Indicates that a member should be excluded from serialization; this + is only normally used when using implict fields. + + + + + Indicates that a member should be excluded from serialization; this + is only normally used when using implict fields. This allows + ProtoIgnoreAttribute usage + even for partial classes where the individual members are not + under direct control. + + + + + Creates a new ProtoPartialIgnoreAttribute instance. + + Specifies the member to be ignored. + + + + The name of the member to be ignored. + + + + + Indicates the known-types to support for an individual + message. This serializes each level in the hierarchy as + a nested message to retain wire-compatibility with + other protocol-buffer implementations. + + + + + Creates a new instance of the ProtoIncludeAttribute. + + The unique index (within the type) that will identify this data. + The additional type to serialize/deserialize. + + + + Creates a new instance of the ProtoIncludeAttribute. + + The unique index (within the type) that will identify this data. + The additional type to serialize/deserialize. + + + + Gets the unique index (within the type) that will identify this data. + + + + + Gets the additional type to serialize/deserialize. + + + + + Gets the additional type to serialize/deserialize. + + + + + Specifies whether the inherited sype's sub-message should be + written with a length-prefix (default), or with group markers. + + + + + Controls the formatting of elements in a dictionary, and indicates that + "map" rules should be used: duplicates *replace* earlier values, rather + than throwing an exception + + + + + Describes the data-format used to store the key + + + + + Describes the data-format used to store the value + + + + + Disables "map" handling; dictionaries will use ".Add(key,value)" instead of "[key] = value", + which means duplicate keys will cause an exception (instead of retaining the final value); if + a proto schema is emitted, it will be produced using "repeated" instead of "map" + + + + + Declares a member to be used in protocol-buffer serialization, using + the given Tag. A DataFormat may be used to optimise the serialization + format (for instance, using zigzag encoding for negative numbers, or + fixed-length encoding for large values. + + + + + Compare with another ProtoMemberAttribute for sorting purposes + + + + + Compare with another ProtoMemberAttribute for sorting purposes + + + + + Creates a new ProtoMemberAttribute instance. + + Specifies the unique tag used to identify this member within the type. + + + + Gets or sets the original name defined in the .proto; not used + during serialization. + + + + + Gets or sets the data-format to be used when encoding this value. + + + + + Gets the unique tag used to identify this member within the type. + + + + + Gets or sets a value indicating whether this member is mandatory. + + + + + Gets a value indicating whether this member is packed. + This option only applies to list/array data of primitive types (int, double, etc). + + + + + Indicates whether this field should *repace* existing values (the default is false, meaning *append*). + This option only applies to list/array data. + + + + + Enables full object-tracking/full-graph support. + + + + + Embeds the type information into the stream, allowing usage with types not known in advance. + + + + + Gets or sets a value indicating whether this member is packed (lists/arrays). + + + + + Additional (optional) settings that control serialization of members + + + + + Default; no additional options + + + + + Indicates that repeated elements should use packed (length-prefixed) encoding + + + + + Indicates that the given item is required + + + + + Enables full object-tracking/full-graph support + + + + + Embeds the type information into the stream, allowing usage with types not known in advance + + + + + Indicates whether this field should *repace* existing values (the default is false, meaning *append*). + This option only applies to list/array data. + + + + + Determines whether the types AsReferenceDefault value is used, or whether this member's AsReference should be used + + + + + Declares a member to be used in protocol-buffer serialization, using + the given Tag and MemberName. This allows ProtoMemberAttribute usage + even for partial classes where the individual members are not + under direct control. + A DataFormat may be used to optimise the serialization + format (for instance, using zigzag encoding for negative numbers, or + fixed-length encoding for large values. + + + + + Creates a new ProtoMemberAttribute instance. + + Specifies the unique tag used to identify this member within the type. + Specifies the member to be serialized. + + + + The name of the member to be serialized. + + + + + A stateful reader, used to read a protobuf stream. Typical usage would be (sequentially) to call + ReadFieldHeader and (after matching the field) an appropriate Read* method. + + + + + Gets the number of the field being processed. + + + + + Indicates the underlying proto serialization format on the wire. + + + + + Creates a new reader against a stream + + The source stream + The model to use for serialization; this can be null, but this will impair the ability to deserialize sub-objects + Additional context about this serialization operation + + + + Gets / sets a flag indicating whether strings should be checked for repetition; if + true, any repeated UTF-8 byte sequence will result in the same String instance, rather + than a second instance of the same string. Enabled by default. Note that this uses + a custom interner - the system-wide string interner is not used. + + + + + Creates a new reader against a stream + + The source stream + The model to use for serialization; this can be null, but this will impair the ability to deserialize sub-objects + Additional context about this serialization operation + The number of bytes to read, or -1 to read until the end of the stream + + + + Creates a new reader against a stream + + The source stream + The model to use for serialization; this can be null, but this will impair the ability to deserialize sub-objects + Additional context about this serialization operation + The number of bytes to read, or -1 to read until the end of the stream + + + + Addition information about this deserialization operation. + + + + + Releases resources used by the reader, but importantly does not Dispose the + underlying stream; in many typical use-cases the stream is used for different + processes, so it is assumed that the consumer will Dispose their stream separately. + + + + + Reads an unsigned 32-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Returns the position of the current reader (note that this is not necessarily the same as the position + in the underlying stream, if multiple readers are used on the same stream) + + + + + Returns the position of the current reader (note that this is not necessarily the same as the position + in the underlying stream, if multiple readers are used on the same stream) + + + + + Reads a signed 16-bit integer from the stream: Variant, Fixed32, Fixed64, SignedVariant + + + + + Reads an unsigned 16-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Reads an unsigned 8-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Reads a signed 8-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant + + + + + Reads a signed 32-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant + + + + + Reads a signed 64-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant + + + + + Reads a string from the stream (using UTF8); supported wire-types: String + + + + + Throws an exception indication that the given value cannot be mapped to an enum. + + + + + Reads a double-precision number from the stream; supported wire-types: Fixed32, Fixed64 + + + + + Reads (merges) a sub-message from the stream, internally calling StartSubItem and EndSubItem, and (in between) + parsing the message in accordance with the model associated with the reader + + + + + Makes the end of consuming a nested message in the stream; the stream must be either at the correct EndGroup + marker, or all fields of the sub-message must have been consumed (in either case, this means ReadFieldHeader + should return zero) + + + + + Begins consuming a nested message in the stream; supported wire-types: StartGroup, String + + The token returned must be help and used when callining EndSubItem + + + + Reads a field header from the stream, setting the wire-type and retuning the field number. If no + more fields are available, then 0 is returned. This methods respects sub-messages. + + + + + Looks ahead to see whether the next field in the stream is what we expect + (typically; what we've just finished reading - for example ot read successive list items) + + + + + Get the TypeModel associated with this reader + + + + + Compares the streams current wire-type to the hinted wire-type, updating the reader if necessary; for example, + a Variant may be updated to SignedVariant. If the hinted wire-type is unrelated then no change is made. + + + + + Verifies that the stream's current wire-type is as expected, or a specialized sub-type (for example, + SignedVariant) - in which case the current wire-type is updated. Otherwise an exception is thrown. + + + + + Discards the data for the current field. + + + + + Reads an unsigned 64-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Reads a single-precision number from the stream; supported wire-types: Fixed32, Fixed64 + + + + + Reads a boolean value from the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + + Reads a byte-sequence from the stream, appending them to an existing byte-sequence (which can be null); supported wire-types: String + + + + + Reads the length-prefix of a message from a stream without buffering additional data, allowing a fixed-length + reader to be created. + + + + + Reads a little-endian encoded integer. An exception is thrown if the data is not all available. + + + + + Reads a big-endian encoded integer. An exception is thrown if the data is not all available. + + + + + Reads a varint encoded integer. An exception is thrown if the data is not all available. + + + + + Reads a string (of a given lenth, in bytes) directly from the source into a pre-existing buffer. An exception is thrown if the data is not all available. + + + + + Reads a given number of bytes directly from the source. An exception is thrown if the data is not all available. + + + + + Reads a string (of a given lenth, in bytes) directly from the source. An exception is thrown if the data is not all available. + + + + + Reads the length-prefix of a message from a stream without buffering additional data, allowing a fixed-length + reader to be created. + + + + + Reads the length-prefix of a message from a stream without buffering additional data, allowing a fixed-length + reader to be created. + + + + The number of bytes consumed; 0 if no data available + + + + Copies the current field into the instance as extension data + + + + + Indicates whether the reader still has data remaining in the current sub-item, + additionally setting the wire-type for the next field if there is more data. + This is used when decoding packed data. + + + + + Utility method, not intended for public use; this helps maintain the root object is complex scenarios + + + + + Reads a Type from the stream, using the model's DynamicTypeFormatting if appropriate; supported wire-types: String + + + + + Merge two objects using the details from the current reader; this is used to change the type + of objects when an inheritance relationship is discovered later than usual during deserilazation. + + + + + Creates a new reader against a stream + + The source stream + The model to use for serialization; this can be null, but this will impair the ability to deserialize sub-objects + Additional context about this serialization operation + The number of bytes to read, or -1 to read until the end of the stream + + + + Represents an output stream for writing protobuf data. + + Why is the API backwards (static methods with writer arguments)? + See: http://marcgravell.blogspot.com/2010/03/last-will-be-first-and-first-will-be.html + + + + + Write an encapsulated sub-object, using the supplied unique key (reprasenting a type). + + The object to write. + The key that uniquely identifies the type within the model. + The destination. + + + + Write an encapsulated sub-object, using the supplied unique key (reprasenting a type) - but the + caller is asserting that this relationship is non-recursive; no recursion check will be + performed. + + The object to write. + The key that uniquely identifies the type within the model. + The destination. + + + + Writes a field-header, indicating the format of the next data we plan to write. + + + + + Writes a byte-array to the stream; supported wire-types: String + + + + + Writes a byte-array to the stream; supported wire-types: String + + + + + Indicates the start of a nested record. + + The instance to write. + The destination. + A token representing the state of the stream; this token is given to EndSubItem. + + + + Indicates the end of a nested record. + + The token obtained from StartubItem. + The destination. + + + + Creates a new writer against a stream + + The destination stream + The model to use for serialization; this can be null, but this will impair the ability to serialize sub-objects + Additional context about this serialization operation + + + + Creates a new writer against a stream + + The destination stream + The model to use for serialization; this can be null, but this will impair the ability to serialize sub-objects + Additional context about this serialization operation + + + + Addition information about this serialization operation. + + + + + Flushes data to the underlying stream, and releases any resources. The underlying stream is *not* disposed + by this operation. + + + + + Get the TypeModel associated with this writer + + + + + Writes any buffered data (if possible) to the underlying stream. + + The writer to flush + It is not always possible to fully flush, since some sequences + may require values to be back-filled into the byte-stream. + + + + Writes an unsigned 32-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Writes a string to the stream; supported wire-types: String + + + + + Writes an unsigned 64-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Writes a signed 64-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant + + + + + Writes an unsigned 16-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Writes a signed 16-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant + + + + + Writes an unsigned 16-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Writes an unsigned 8-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Writes a signed 8-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant + + + + + Writes a signed 32-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant + + + + + Writes a double-precision number to the stream; supported wire-types: Fixed32, Fixed64 + + + + + Writes a single-precision number to the stream; supported wire-types: Fixed32, Fixed64 + + + + + Throws an exception indicating that the given enum cannot be mapped to a serialized value. + + + + + Writes a boolean to the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Copies any extension data stored for the instance to the underlying stream + + + + + Used for packed encoding; indicates that the next field should be skipped rather than + a field header written. Note that the field number must match, else an exception is thrown + when the attempt is made to write the (incorrect) field. The wire-type is taken from the + subsequent call to WriteFieldHeader. Only primitive types can be packed. + + + + + Used for packed encoding; explicitly reset the packed field marker; this is not required + if using StartSubItem/EndSubItem + + + + + Used for packed encoding; writes the length prefix using fixed sizes rather than using + buffering. Only valid for fixed-32 and fixed-64 encoding. + + + + + Specifies a known root object to use during reference-tracked serialization + + + + + Writes a Type to the stream, using the model's DynamicTypeFormatting if appropriate; supported wire-types: String + + + + + Additional information about a serialization operation + + + + + Gets or sets a user-defined object containing additional information about this serialization/deserialization operation. + + + + + A default SerializationContext, with minimal information. + + + + + Gets or sets the source or destination of the transmitted data. + + + + + Convert a SerializationContext to a StreamingContext + + + + + Convert a StreamingContext to a SerializationContext + + + + + Provides protocol-buffer serialization capability for concrete, attributed types. This + is a *default* model, but custom serializer models are also supported. + + + Protocol-buffer serialization is a compact binary format, designed to take + advantage of sparse data and knowledge of specific data types; it is also + extensible, allowing a type to be deserialized / merged even if some data is + not recognised. + + + + + Suggest a .proto definition for the given type + + The type to generate a .proto definition for + The .proto definition as a string + + + + Suggest a .proto definition for the given type + + The type to generate a .proto definition for + The .proto definition as a string + + + + Create a deep clone of the supplied instance; any sub-items are also cloned. + + + + + Applies a protocol-buffer stream to an existing instance. + + The type being merged. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Creates a new instance from a protocol-buffer stream + + The type to be created. + The binary stream to apply to the new instance (cannot be null). + A new, initialized instance. + + + + Creates a new instance from a protocol-buffer stream + + The type to be created. + The binary stream to apply to the new instance (cannot be null). + A new, initialized instance. + + + + Writes a protocol-buffer representation of the given instance to the supplied stream. + + The existing instance to be serialized (cannot be null). + The destination stream to write to. + + + + Serializes a given instance and deserializes it as a different type; + this can be used to translate between wire-compatible objects (where + two .NET types represent the same data), or to promote/demote a type + through an inheritance hierarchy. + + No assumption of compatibility is made between the types. + The type of the object being copied. + The type of the new object to be created. + The existing instance to use as a template. + A new instane of type TNewType, with the data from TOldType. + + + + Writes a protocol-buffer representation of the given instance to the supplied SerializationInfo. + + The type being serialized. + The existing instance to be serialized (cannot be null). + The destination SerializationInfo to write to. + + + + Writes a protocol-buffer representation of the given instance to the supplied SerializationInfo. + + The type being serialized. + The existing instance to be serialized (cannot be null). + The destination SerializationInfo to write to. + Additional information about this serialization operation. + + + + Writes a protocol-buffer representation of the given instance to the supplied XmlWriter. + + The type being serialized. + The existing instance to be serialized (cannot be null). + The destination XmlWriter to write to. + + + + Applies a protocol-buffer from an XmlReader to an existing instance. + + The type being merged. + The existing instance to be modified (cannot be null). + The XmlReader containing the data to apply to the instance (cannot be null). + + + + Applies a protocol-buffer from a SerializationInfo to an existing instance. + + The type being merged. + The existing instance to be modified (cannot be null). + The SerializationInfo containing the data to apply to the instance (cannot be null). + + + + Applies a protocol-buffer from a SerializationInfo to an existing instance. + + The type being merged. + The existing instance to be modified (cannot be null). + The SerializationInfo containing the data to apply to the instance (cannot be null). + Additional information about this serialization operation. + + + + Precompiles the serializer for a given type. + + + + + Creates a new IFormatter that uses protocol-buffer [de]serialization. + + The type of object to be [de]deserialized by the formatter. + A new IFormatter to be used during [de]serialization. + + + + Reads a sequence of consecutive length-prefixed items from a stream, using + either base-128 or fixed-length prefixes. Base-128 prefixes with a tag + are directly comparable to serializing multiple items in succession + (use the tag to emulate the implicit behavior + when serializing a list/array). When a tag is + specified, any records with different tags are silently omitted. The + tag is ignored. The tag is ignored for fixed-length prefixes. + + The type of object to deserialize. + The binary stream containing the serialized records. + The prefix style used in the data. + The tag of records to return (if non-positive, then no tag is + expected and all records are returned). + The sequence of deserialized objects. + + + + Creates a new instance from a protocol-buffer stream that has a length-prefix + on data (to assist with network IO). + + The type to be created. + The binary stream to apply to the new instance (cannot be null). + How to encode the length prefix. + A new, initialized instance. + + + + Creates a new instance from a protocol-buffer stream that has a length-prefix + on data (to assist with network IO). + + The type to be created. + The binary stream to apply to the new instance (cannot be null). + How to encode the length prefix. + The expected tag of the item (only used with base-128 prefix style). + A new, initialized instance. + + + + Applies a protocol-buffer stream to an existing instance, using length-prefixed + data - useful with network IO. + + The type being merged. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + How to encode the length prefix. + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Writes a protocol-buffer representation of the given instance to the supplied stream, + with a length-prefix. This is useful for socket programming, + as DeserializeWithLengthPrefix/MergeWithLengthPrefix can be used to read the single object back + from an ongoing stream. + + The type being serialized. + The existing instance to be serialized (cannot be null). + How to encode the length prefix. + The destination stream to write to. + + + + Writes a protocol-buffer representation of the given instance to the supplied stream, + with a length-prefix. This is useful for socket programming, + as DeserializeWithLengthPrefix/MergeWithLengthPrefix can be used to read the single object back + from an ongoing stream. + + The type being serialized. + The existing instance to be serialized (cannot be null). + How to encode the length prefix. + The destination stream to write to. + The tag used as a prefix to each record (only used with base-128 style prefixes). + + + Indicates the number of bytes expected for the next message. + The stream containing the data to investigate for a length. + The algorithm used to encode the length. + The length of the message, if it could be identified. + True if a length could be obtained, false otherwise. + + + Indicates the number of bytes expected for the next message. + The buffer containing the data to investigate for a length. + The offset of the first byte to read from the buffer. + The number of bytes to read from the buffer. + The algorithm used to encode the length. + The length of the message, if it could be identified. + True if a length could be obtained, false otherwise. + + + + The field number that is used as a default when serializing/deserializing a list of objects. + The data is treated as repeated message with field number 1. + + + + + Provides non-generic access to the default serializer. + + + + + Create a deep clone of the supplied instance; any sub-items are also cloned. + + + + + Writes a protocol-buffer representation of the given instance to the supplied stream. + + The existing instance to be serialized (cannot be null). + The destination stream to write to. + + + + Creates a new instance from a protocol-buffer stream + + The type to be created. + The binary stream to apply to the new instance (cannot be null). + A new, initialized instance. + + + Applies a protocol-buffer stream to an existing instance. + The existing instance to be modified (cannot be null). + The binary stream to apply to the instance (cannot be null). + The updated instance + + + + Writes a protocol-buffer representation of the given instance to the supplied stream, + with a length-prefix. This is useful for socket programming, + as DeserializeWithLengthPrefix/MergeWithLengthPrefix can be used to read the single object back + from an ongoing stream. + + The existing instance to be serialized (cannot be null). + How to encode the length prefix. + The destination stream to write to. + The tag used as a prefix to each record (only used with base-128 style prefixes). + + + + Applies a protocol-buffer stream to an existing instance (or null), using length-prefixed + data - useful with network IO. + + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + How to encode the length prefix. + Used to resolve types on a per-field basis. + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Indicates whether the supplied type is explicitly modelled by the model + + + + + Precompiles the serializer for a given type. + + + + + Global switches that change the behavior of protobuf-net + + + + + + + + + + Maps a field-number to a type + + + + + Releases any internal buffers that have been reserved for efficiency; this does not affect any serialization + operations; simply: it can be used (optionally) to release the buffers for garbage collection (at the expense + of having to re-allocate a new buffer for the next operation, rather than re-use prior buffers). + + + + + The type that this serializer is intended to work for. + + + + + Perform the steps necessary to serialize this data. + + The value to be serialized. + The writer entity that is accumulating the output data. + + + + Perform the steps necessary to deserialize this data. + + The current value, if appropriate. + The reader providing the input data. + The updated / replacement value. + + + + Indicates whether a Read operation replaces the existing value, or + extends the value. If false, the "value" parameter to Read is + discarded, and should be passed in as null. + + + + + Now all Read operations return a value (although most do); if false no + value should be expected. + + + + Emit the IL necessary to perform the given actions + to serialize this data. + + Details and utilities for the method being generated. + The source of the data to work against; + If the value is only needed once, then LoadValue is sufficient. If + the value is needed multiple times, then note that a "null" + means "the top of the stack", in which case you should create your + own copy - GetLocalWithValue. + + + + Emit the IL necessary to perform the given actions to deserialize this data. + + Details and utilities for the method being generated. + For nested values, the instance holding the values; note + that this is not always provided - a null means not supplied. Since this is always + a variable or argument, it is not necessary to consume this value. + + + + Uses protocol buffer serialization on the specified operation; note that this + must be enabled on both the client and server. + + + + + Configuration element to swap out DatatContractSerilaizer with the XmlProtoSerializer for a given endpoint. + + + + + + Creates a new ProtoBehaviorExtension instance. + + + + + Gets the type of behavior. + + + + + Creates a behavior extension based on the current configuration settings. + + The behavior extension. + + + + Behavior to swap out DatatContractSerilaizer with the XmlProtoSerializer for a given endpoint. + + Add the following to the server and client app.config in the system.serviceModel section: + + + + + + + + + + + + + + Configure your endpoints to have a behaviorConfiguration as follows: + + + + + + + + + + + + + Describes a WCF operation behaviour that can perform protobuf serialization + + + + + Create a new ProtoOperationBehavior instance + + + + + The type-model that should be used with this behaviour + + + + + Creates a protobuf serializer if possible (falling back to the default WCF serializer) + + + + + An xml object serializer that can embed protobuf data in a base-64 hunk (looking like a byte[]) + + + + + Attempt to create a new serializer for the given model and type + + A new serializer instance if the type is recognised by the model; null otherwise + + + + Creates a new serializer for the given model and type + + + + + Ends an object in the output + + + + + Begins an object in the output + + + + + Writes the body of an object in the output + + + + + Indicates whether this is the start of an object we are prepared to handle + + + + + Reads the body of an object + + + + + Used to hold particulars relating to nested objects. This is opaque to the caller - simply + give back the token you are given at the end of an object. + + + + + Indicates the encoding used to represent an individual value in a protobuf stream + + + + + Represents an error condition + + + + + Base-128 variant-length encoding + + + + + Fixed-length 8-byte encoding + + + + + Length-variant-prefixed encoding + + + + + Indicates the start of a group + + + + + Indicates the end of a group + + + + + Fixed-length 4-byte encoding + 10 + + + + This is not a formal wire-type in the "protocol buffers" spec, but + denotes a variant integer that should be interpreted using + zig-zag semantics (so -ve numbers aren't a significant overhead) + + + + diff --git a/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protogen.exe b/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protogen.exe new file mode 100644 index 00000000..c875ab45 Binary files /dev/null and b/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protogen.exe differ diff --git a/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protogen.xml b/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protogen.xml new file mode 100644 index 00000000..c9482089 --- /dev/null +++ b/Assets/TEngine/Tools~/Protobuf/ProtobufGenCsharp/protogen.xml @@ -0,0 +1,8 @@ + + + + protogen + + + +