赞
踩
1.下载安装UnityWebSocket 插件
https://gitee.com/cambright/UnityWebSocket/
引入unity项目:
2.定义消息体结构:ExternalMessage和包结构Package:
- using ProtoBuf;
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- namespace UTNET
- {
-
- [ProtoContract]
- public class ExternalMessage
- {
- [ProtoMember(1)]
- // 请求命令类型: 0 心跳,1 业务
- public int cmdCode;
- // 协议开关,用于一些协议级别的开关控制,比如 安全加密校验等。 : 0 不校验
- [ProtoMember(2)]
- public int protocolSwitch;
- // 业务路由(高16为主, 低16为子)
- [ProtoMember(3)]
- public uint cmdMerge;
- // 响应码: 0:成功, 其他为有错误
- [ProtoMember(4,DataFormat = DataFormat.ZigZag)]
- public int responseStatus;
- // 验证信息(错误消息、异常消息),通常情况下 responseStatus == -1001 时, 会有值
- [ProtoMember(5)]
- public string validMsg;
- // 业务请求数据
- [ProtoMember(6)]
- public byte[] data;
- // 消息标记号;由前端请求时设置,服务器响应时会携带上;
- [ProtoMember(7, DataFormat = DataFormat.ZigZag)]
- public int msgId;
- }
-
-
- [ProtoContract]
- public class Package
- {
- [ProtoMember(1)]
- public uint packageType;
-
- [ProtoMember(2)]
- public string route = null;
-
- [ProtoMember(3)]
- public uint packID = 0;
-
- [ProtoMember(4)]
- public byte[] buff = null;
-
- [ProtoMember(5)]
- public string modelName = null;
- }
-
- //[ProtoContract]
- //public class Message<T>
- //{
- // [ProtoMember(1)]
- // public uint err;
- // [ProtoMember(2)]
- // public string errMsg = default;
- // [ProtoMember(3)]
- // public T data = default;
- // public Message() { }
- // public Message(uint err, string errMsg, T info)
- // {
- // this.err = err;
- // this.errMsg = errMsg;
- // this.data = info;
- // }
- //}
-
- [ProtoContract]
- public class HandShake
- {
- [ProtoMember(1)]
- public string token;
- }
-
- [ProtoContract]
- public class Heartbeat
- {
- [ProtoMember(1)]
- public uint heartbeat;
- }
- }
3.定义包协议结构:PackageProtocol
- using ProtoBuf;
- using System;
- using System.IO;
- using System.Text;
- using UnityEngine.XR;
-
- namespace UTNET
- {
- public enum PackageType
- {
- HEARTBEAT = 1,
- REQUEST = 2,
- PUSH = 3,
- KICK = 4,
- RESPONSE = 5,
- HANDSHAKE = 6,
- ERROR = 7,
- NOTIFY = 8
- }
-
- public class PackageProtocol
- {
- //获取cmd
- public static uint getCmd(uint merge)
- {
- return merge >> 16;
- }
- //获取subCmd
- public static uint getSubCmd(uint merge)
- {
- return merge & 0xFFFF;
- }
- //获取mergeCmd
- public static uint getMergeCmd(uint cmd, uint subCmd)
- {
- return (cmd << 16) + subCmd;
- }
- public static byte[] Encode(PackageType type)
- {
- Package sr = new Package()
- {
- packageType = (uint)type
- };
- return Serialize(sr);
- }
-
- public static byte[] Encode<T>(PackageType type, uint packID, string route, T info, string modelName = null)
- {
- Package sr = new Package()
- {
- packageType = (uint)type,
- packID = packID,
- route = route,
- buff = Serialize<T>(info),
- modelName = modelName
- };
- return Serialize(sr);
- }
-
-
- public static byte[] EncodeEx<T>(uint packID, uint cmdMerge, T info)
- {
- //Package sr = new Package()
- //{
- // packageType = (uint)type,
- // packID = packID,
- // route = route,
- // buff = Encoding.Default.GetBytes(SerializeEx<T>(info)),
- //};
- //return SerializeEx(sr);
-
- ExternalMessage sr = new ExternalMessage()
- {
- cmdCode = 100,
- protocolSwitch = 1,
- cmdMerge = cmdMerge,
- responseStatus = 0,
- validMsg = "",
- data = Encoding.UTF8.GetBytes(SerializeEx<T>(info)),
- };
- return Serialize(sr);
-
- }
-
-
-
- public static byte[] Encode<T>(uint packID, uint cmdMerge, T info)
- {
- ExternalMessage sr = new ExternalMessage()
- {
- cmdCode = 100,
- protocolSwitch = 1,
- cmdMerge = cmdMerge,
- responseStatus = 0,
- validMsg = "",
- data = Serialize<T>(info),
- msgId = (int)packID,
- };
- return Serialize(sr);
- }
-
- public static string SerializeEx<T>(T t)
- {
- using (MemoryStream ms = new MemoryStream())
- {
- Serializer.Serialize<T>(ms, t);
- return Encoding.UTF8.GetString(ms.ToArray());
- }
- }
-
-
- public static byte[] Serialize<T>(T info)
- {
- if (typeof(T) == typeof(int))
- {
- int value = (int)Convert.ChangeType(info, typeof(int));
- value = (value << 1);
- info = (T)Convert.ChangeType(info, typeof(T));
- }
-
- MemoryStream ms = new MemoryStream();
- Serializer.Serialize(ms, info);
- byte[] buff = ms.ToArray();
- ms.Close();
- return buff;
-
- //try
- //{
- // //涉及格式转换,需要用到流,将二进制序列化到流中
- // using (MemoryStream ms = new MemoryStream())
- // {
- // //使用ProtoBuf工具的序列化方法
- // Serializer.Serialize<T>(ms, info);
- // //定义二级制数组,保存序列化后的结果
- // byte[] result = new byte[ms.Length];
- // //将流的位置设为0,起始点
- // ms.Position = 0;
- // //将流中的内容读取到二进制数组中
- // ms.Read(result, 0, result.Length);
- // return result;
- // }
- //}
- //catch (Exception ex)
- //{
- // return null;
- //}
-
-
-
- }
-
- public static ExternalMessage Decode(byte[] buff)
- {
- //protobuf反序列化
- MemoryStream mem = new MemoryStream(buff);
- ExternalMessage rs = Serializer.Deserialize<ExternalMessage>(mem);
- mem.Close();
- return rs;
- }
- public static T DecodeInfo<T>(byte[] buff)
- {
- if (buff == null) return default;
- T rs;
-
- if (typeof(T) == typeof(int))
- {
- int value;
- using (var stream = new MemoryStream(buff))
- {
- value = Serializer.Deserialize<int>(stream);
- }//转zig zag
- value = (value >> 1) ^ -(value & 1);
-
- rs = (T)Convert.ChangeType(value, typeof(T));
- }
- else
- {
- MemoryStream mem = new MemoryStream(buff);
- rs = Serializer.Deserialize<T>(mem);
- mem.Close();
- }
-
- return rs;
- }
-
-
-
- //public static T MyMethod<T>(byte[] buff) where T : Object
- //{
- // return null;
- //}
-
-
-
- }
- }
4.引入UniTask插件:UniTask中文使用指南(一) - 知乎
项目地址:GitHub - Cysharp/UniTask: Provides an efficient allocation free async/await integration for Unity.
5.定义协议索引管理,添加长连接的协议:ProtoMaps
-
-
-
-
- using System;
- using System.Collections.Generic;
-
-
- namespace UTNET
- {
-
-
- public interface IProto
- {
-
- }
-
-
-
- //R ��������, S ��������
- //public class Proto<R, S> : IProto
- public class Proto : IProto
- {
- public string name; //������
- public uint mid { get; set; } //��id
- public uint sid { get; set; } //��id
-
- public uint mergeid;
- public int typ; //������
- //public R r;
- //public S s;
-
- public Proto(string name, uint mid, uint sid, int typ)
- {
- this.name = name;
- this.mid = mid;
- this.sid = sid;
- this.typ = typ;
- }
- }
-
- public class ProtoMaps
- {
-
- private static readonly ProtoMaps instance = new ProtoMaps();
-
-
- public static ProtoMaps Instance
- {
- get { return instance; }
- }
-
-
- //public Dictionary<string, IProto> protos = new Dictionary<string, IProto>();
-
- public Dictionary<string, Proto> protos = new Dictionary<string, Proto>();
-
- public Dictionary<uint, Proto> id2proto = new Dictionary<uint, Proto>();
-
-
- public void Add(string name, Proto pb)
- {
- protos.Add(name, pb);
- }
-
- public void SortById()
- {
- foreach (var item in protos)
- {
- var pb = item.Value;
- uint mergeid = PackageProtocol.getMergeCmd(pb.mid, pb.sid);
- pb.mergeid = mergeid;
- id2proto.Add(mergeid, pb);
- }
- }
-
-
- public ProtoMaps()
- {
- this.Add("login", new Proto("login", 1, 2, 1));
- this.Add("registerInfo", new Proto("registerInfo", 1, 1, 1));
- this.Add("enterRoom", new Proto("enterRoom", 1, 13, 3));
- this.Add("roleMove", new Proto("roleMove", 4, 3, 3));
- //this.Add("onNewRoleEnter", new Proto("onNewRoleEnter", 7, 1, 2));
- //服务器主动广播的角色退出
- this.Add("roleExitRoom", new Proto("roleExitRoom", 7, 2, 2));
- this.Add("talkMrg", new Proto("talkMrg", 1, 9, 3));
-
- //玩家点击的角色退出
- this.Add("playerExitRoom", new Proto("playerExitRoom", 1, 5, 3));
-
-
- this.Add("gameFrame", new Proto("gameFrame", 4, 2, 1));
- this.Add("gameObjUsed", new Proto("gameObjUsed", 4, 4, 3));
-
- this.Add("getOtherInfo", new Proto("getOtherInfo", 1, 23, 1));
-
- this.Add("heatBeat", new Proto("heatBeat", 1, 120, 1));
-
-
-
- SortById();
- }
-
- public Proto Name2Pb(string name)
- {
- //Proto pb = ProtoMaps.Instance.protos[name];
- if (protos.ContainsKey(name))
- {
- return protos[name];
- }
- return null;
- }
-
- internal Proto GetByMergeId(uint cmdMerge)
- {
- if (id2proto.ContainsKey(cmdMerge))
- {
- return id2proto[cmdMerge];
- }
- return null;
- }
- }
-
-
-
-
-
- }
6.定义心跳协议类:HeartBeatServiceGameObject
- using System;
- using UnityEngine;
- using UnityWebSocket;
- namespace UTNET
- {
- public class HeartBeatServiceGameObject : MonoBehaviour
- {
- public Action OnServerTimeout;
- private WebSocket socket;
- public float interval = 0;
-
- public long lastReceiveHeartbeatTime;
-
- void Start()
- {
-
- }
-
- static DateTime dt = new DateTime(1970, 1, 1);
- public static long GetTimestamp()
- {
- TimeSpan ts = DateTime.Now.ToUniversalTime() - dt;
- return (long)ts.TotalSeconds;
- }
-
- public float t;
-
- void Update()
- {
- t += Time.deltaTime;
- if (t > interval)
- {
- CheckAndSendHearbeat();
- t = 0;
- }
- }
-
- private void CheckAndSendHearbeat()
- {
- //檢查最後一次取得心跳包的時間是否小於客戶端心跳間隔時間
- long curTime = GetTimestamp();
- long intervalSec = curTime - lastReceiveHeartbeatTime;
- if (intervalSec > interval)
- {
- //Debug.Log(string.Format("XXXX CheckAndSendHearbeat:s1:{0} l:{1} s:{2}", curTime, lastReceiveHeartbeatTime, intervalSec));
- this.enabled = false;
- OnServerTimeout?.Invoke();
- }
- else
- {
- //Debug.Log(string.Format(" CheckAndSendHearbeat:s1:{0} l:{1} s:{2}", curTime, lastReceiveHeartbeatTime, intervalSec));
- this.enabled = true;
- SendHeartbeatPack();
- }
- }
-
- public void HitHole()
- {
- lastReceiveHeartbeatTime = GetTimestamp();
- }
-
- private void SendHeartbeatPack()
- {
- //lastSendHeartbeatPackTime = DateTime.Now;
- byte[] package = PackageProtocol.Encode(
- PackageType.HEARTBEAT);
- socket.SendAsync(package);//*/
- }
-
- internal void Setup(uint interval, Action onServerTimeout, WebSocket socket)
- {
- this.socket = socket;
- this.interval = (interval / 1000 )/2;
- this.OnServerTimeout = onServerTimeout;
- this.enabled = true;
- SendHeartbeatPack();
- }
-
- internal void ResetTimeout(uint interval)
- {
- this.enabled = true;
- this.interval = (interval / 1000) / 2;
- t = 0;
- //long s1 = GetTimestamp();
- //long s = (s1 - lastReceiveHeartbeatTime);
- //Debug.Log(string.Format("ResetTimeout: s1:{0} l:{1} s:{2} s > interval:{3}", s1, lastReceiveHeartbeatTime, s, s > interval));
- lastReceiveHeartbeatTime = GetTimestamp();
- SendHeartbeatPack();
- }
-
- internal void Stop()
- {
- this.enabled = false;
- t = 0;
- }
- }
- }
7.定义协议类Protocol:
- using Cysharp.Threading.Tasks;
- using ProtoBuf;
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Net;
- using System.Runtime.InteropServices;
- using System.Text;
- using UnityEditor;
- using UnityEngine;
- using UnityWebSocket;
-
- namespace UTNET
- {
- public class Protocol
- {
-
- Dictionary<uint, Action<ExternalMessage>> packAction = new Dictionary<uint, Action<ExternalMessage>>();
- UniTaskCompletionSource<bool> handshakeTcs;
- Dictionary<uint, UniTaskCompletionSource<ExternalMessage>> packTcs = new Dictionary<uint, UniTaskCompletionSource<ExternalMessage>>();
-
- //Dictionary<uint, Action<T>> attention = new Dictionary<uint, Action<T>>();
-
- WebSocket socket;
- public HeartBeatServiceGameObject heartBeatServiceGo;
- public Action OnReconected;
- public Action<string> OnError;
-
- public void SetSocket(WebSocket socket)
- {
- this.socket = socket;
- }
-
- public UniTask<bool> HandsharkAsync(string token)
- {
- handshakeTcs = new UniTaskCompletionSource<bool>();
- return handshakeTcs.Task;
- }
-
- internal void Notify<T>(string route, T info)
- {
- byte[] packBuff = PackageProtocol.Encode<T>(
- PackageType.NOTIFY,
- 0,
- route,
- info);
- socket.SendAsync(packBuff);
- }
-
-
- public UniTask<ExternalMessage> RequestAsync<T>(uint packID, uint route, T info = default, string modelName = null)
- {
- lock (packTcs)
- {
- UniTaskCompletionSource<ExternalMessage> pack = new UniTaskCompletionSource<ExternalMessage>();
- byte[] packBuff = PackageProtocol.Encode<T>(packID, route, info);
- packTcs.Add(packID, pack);
-
- //packTcs.Add(route, pack); //暂不支持,同一协议多次发送
- //byte[] ints = System.BitConverter.GetBytes(IPAddress.HostToNetworkOrder(testInt));
- //ByteBuffer bbBuffer = ByteBuffer.wrap(bs);
- //bbBuffer.order(ByteOrder.BIG_ENDIAN);
-
- socket.SendAsync(packBuff);
-
- return pack.Task;
- }
- }
-
- public void CanceledAllUTcs()
- {
- lock (packTcs)
- {
- foreach (var tcs in packTcs)
- {
- tcs.Value.TrySetCanceled();
- }
- packTcs.Clear();
- if(handshakeTcs!=null) handshakeTcs.TrySetCanceled();
- }
- }
-
- public async void OnReceive(byte[] bytes)
- {
-
- try
- {
- await UniTask.SwitchToMainThread();
- ExternalMessage package = PackageProtocol.Decode(bytes);
- uint mId = PackageProtocol.getCmd(package.cmdMerge);
- uint subId = PackageProtocol.getSubCmd(package.cmdMerge);
-
-
-
-
- Proto pb = ProtoMaps.Instance.GetByMergeId(package.cmdMerge);
- if (package.responseStatus != 0)
- {
- Debug.LogError("收到 cmd:" + mId + " &subcmd:" + subId);
- Debug.LogError("收到网络消息 " + package.cmdMerge);
-
- Debug.LogError("协议返回错误信息,查询errcode,后面可能根据弹窗处理: Errmsg->" + package.validMsg + " & Errcode->" + package.responseStatus );
- onShowNetError(package.responseStatus, mId, subId);
-
- return;
- }
-
- if (pb == null)
- {
- Debug.Log("收到未在 ProtoMaps 注册的 网络消息 " + package.cmdCode);
- return;
- }
-
-
- if (PackageProtocol.getCmd(package.cmdMerge) == 0)
- {
- Debug.Log("心跳数据 ");
- return;
- }
-
- ResponseHandler(package);
- PushHandler(package);
-
- //if (pb.typ == 1)
- //{
- //}
- //else if (pb.typ == 2)
- //{
- // PushHandler(package);
- //}
- //else
- //{
- // if (!ResponseHandler(package))
- // PushHandler(package);
- //}
-
-
- //Package package = PackageProtocol.Decode(bytes);
- //Debug.Log(package.packageType);
- //switch ((PackageType)package.packageType)
- //{
- // case PackageType.HEARTBEAT:
- // //Debug.LogWarning("get HEARTBEAT");
- // heartBeatServiceGo.HitHole();
- // break;
- // case PackageType.RESPONSE:
- // ResponseHandler(package);
- // break;
- // case PackageType.PUSH:
- // PushHandler(package);
- // break;
- // case PackageType.HANDSHAKE:
- // HandshakeHandler(package);
- // break;
- // case PackageType.KICK:
- // //HandleKick(package);
- // break;
- // case PackageType.ERROR:
- // ErrorHandler(package);
- // break;
- // default:
- // Debug.Log("No match packageType::" + package.packageType);
- // break;
- //}
- }
- catch (Exception e)
- {
- ExternalMessage package = PackageProtocol.Decode(bytes);
- Proto pb = ProtoMaps.Instance.GetByMergeId(package.cmdMerge);
- Debug.Log("错误协议-----" + JsonUtility.ToJson(pb));
- await UniTask.SwitchToMainThread();
- Debug.LogError(e);
- throw e;
- }
- }
-
- public void StopHeartbeat()
- {
- if (heartBeatServiceGo != null)
- {
- Debug.Log("Stop Heartbeat");
- heartBeatServiceGo.Stop();
- //heartBeatServiceGo = null;
- }
- }
-
- public void onShowNetError(int responseStatus,uint mid,uint subid) {
-
-
-
- }
- public void SetOnNet(uint route, Action<ExternalMessage> ac)
- {
- lock (packAction)
- {
- if (!packAction.ContainsKey(route))
- {
- packAction.Add(route, ac);
- }
- }
- }
-
- private void PushHandler(ExternalMessage pack)
- {
- lock (packAction)
- {
- uint route = pack.cmdMerge;
- if (packAction.ContainsKey(route))
- {
- #if SOCKET_DEBUG
- Debug.Log(string.Format("[Push] <<-- [{0}] {1}", pack.route, JsonUtility.ToJson(pack)));
- #endif
- packAction[route]?.Invoke(pack);
- //packAction.Remove(route); //监听事件不考虑删除由netmanager代理
- }
- }
- }
-
- private bool ResponseHandler(ExternalMessage package)
- {
- lock (packTcs)
- {
- uint msgId = (uint)package.msgId;
- //Debug.LogError("响应消息id" + msgId);
- if (packTcs.ContainsKey(msgId))
- {
- packTcs[msgId].TrySetResult(package);
- packTcs.Remove(msgId);
- return true;
- //if (packTcs.ContainsKey(package.cmdMerge))
- //{
- // packTcs.Remove(package.cmdMerge);
- // return true;
- //}
- }
-
- }
- return false;
- }
-
- //private void ResponseHandler(ExternalMessage package)
- //{
- // lock (packTcs)
- // {
- // packTcs[package.packID].TrySetResult(package);
- // if (packTcs.ContainsKey(package.packID))
- // {
- // packTcs.Remove(package.packID);
- // }
- // }
- //}
-
-
- private void HandshakeHandler(Package package)
- {
- Heartbeat msg = PackageProtocol.DecodeInfo<Heartbeat>(package.buff);
- if (msg.heartbeat > 0)
- {
- handshakeTcs.TrySetResult(false);
- return;
- }
-
- if (heartBeatServiceGo == null)
- {
-
- }
- else
- {
- OnReconected?.Invoke();
- heartBeatServiceGo.ResetTimeout(msg.heartbeat);
- }//*/
- handshakeTcs.TrySetResult(true);
- }
-
- private void ErrorHandler(Package package)
- {
-
- }
-
- private void OnServerTimeout()
- {
- if (socket.ReadyState == WebSocketState.Connecting)
- {
- socket.CloseAsync();
- }
- if (heartBeatServiceGo != null && socket.ReadyState != WebSocketState.Connecting && socket.ReadyState != WebSocketState.Open)
- {
- heartBeatServiceGo.Stop();
- }
- }
- }
- }
8.定义服务器对应的协议消息:TestProto.cs
-
- using ProtoBuf;
- using ProtoBuf.Meta;
- using System.Collections.Generic;
-
- [ProtoContract]
- public class RegisterInfo
- {
- [ProtoMember(1)]
- public string phoneNum;
- [ProtoMember(2)]
- public string account;
- [ProtoMember(3)]
- public string pwd;
- }
-
-
- [ProtoContract]
- public class LoginVerify
- {
-
- [ProtoMember(1)]
- public string account;
- [ProtoMember(2)]
- public string pwd;
- [ProtoMember(3, DataFormat = DataFormat.ZigZag)]
- public int loginBizCode;
- [ProtoMember(4, DataFormat = DataFormat.ZigZag)]
- public int spaceId;
- }
-
- [ProtoContract]
- public class LobbyTalkMsg
- {
- [ProtoMember(1, DataFormat = DataFormat.ZigZag)]
- public long id;
- [ProtoMember(2, DataFormat = DataFormat.ZigZag)]
- public long usrId;
- [ProtoMember(3)]
- public string nickName;
- [ProtoMember(4, DataFormat = DataFormat.ZigZag)]
- public int to_usrId;
- [ProtoMember(5)]
- public string msg;
- [ProtoMember(6, DataFormat = DataFormat.ZigZag)]
- public int msg_type;
- }
-
- [ProtoContract]
- public class RequestSuccess
- {
- [ProtoMember(1, DataFormat = DataFormat.ZigZag)]
- public int success;
-
- }
-
- [ProtoContract]
- public class GameFrame {
- [ProtoMember(1, DataFormat = DataFormat.ZigZag)]
- public long frameId;
- [ProtoMember(2)]
- public string opc;
- [ProtoMember(3)]
- public string world;
- [ProtoMember(4)]
- public TankLocation location;
-
- }
-
-
- [ProtoContract]
- public class UserInfo {
- [ProtoMember(1, DataFormat = DataFormat.ZigZag)]
- public long id;
- [ProtoMember(2)]
- public string nickName;
- [ProtoMember(3)]
- public string account;
- [ProtoMember(4)]
- public string gender;
-
- [ProtoMember(5)]
- public string headImgUrl;
-
- [ProtoMember(6, DataFormat = DataFormat.ZigZag)]
- public long avatarId;
-
- [ProtoMember(7)]
- public string avatar;
- [ProtoMember(8)]
- public string hold;
-
- [ProtoMember(9)]
- public string mtk;
-
- [ProtoMember(10)]
- public string ltk;
-
- [ProtoMember(11, DataFormat = DataFormat.ZigZag)]
- public long roomId;
-
-
- [ProtoMember(12, DataFormat = DataFormat.ZigZag)]
- public long team;
-
-
- [ProtoMember(13)]
- public TankLocation tankLocation;
-
- [ProtoMember(14, DataFormat = DataFormat.ZigZag)]
- public int speed;
- }
-
- [ProtoContract]
- public class TankLocation {
- [ProtoMember(1, DataFormat = DataFormat.ZigZag)]
- public float x;
- [ProtoMember(2, DataFormat = DataFormat.ZigZag)]
- public float y;
- [ProtoMember(3, DataFormat = DataFormat.ZigZag)]
- public float z;
- [ProtoMember(4, DataFormat = DataFormat.ZigZag)]
- public float dx;
- [ProtoMember(5, DataFormat = DataFormat.ZigZag)]
- public float dy;
- [ProtoMember(6, DataFormat = DataFormat.ZigZag)]
- public float dz;
- [ProtoMember(7, DataFormat = DataFormat.ZigZag)]
- public long playerId;
- [ProtoMember(8, DataFormat = DataFormat.ZigZag)]
- public long time;
-
- }
-
-
- [ProtoContract]
- public class TankPlayer {
- [ProtoMember(1, DataFormat = DataFormat.ZigZag)]
- public long id;
- [ProtoMember(2, DataFormat = DataFormat.ZigZag)]
- public int speed;
- [ProtoMember(3)]
- public TankLocation tankLocation;
- [ProtoMember(4)]
- public string nickname;
- [ProtoMember(5)]
- public string hold;
- // 其他属性: key: 子弹 id 1 玩具弹, 2 雪弹; value : 数量
- [ProtoMember(6)]
- public Dictionary<int, int> tankBulletMap;
- }
-
-
- [ProtoContract]
- public class TankEnterRoom {
- [ProtoMember(1, DataFormat = DataFormat.ZigZag)]
- public long roomId;
- [ProtoMember(2, DataFormat = DataFormat.ZigZag)]
- public long time;
- [ProtoMember(3, DataFormat = DataFormat.ZigZag)]
- public long team;
- [ProtoMember(4, DataFormat = DataFormat.ZigZag)]
- public long playerId;
-
- [ProtoMember(5)]
- public string sharetoken;
-
- [ProtoMember(6)]
- public string usertoken;
-
- [ProtoMember(7)]
- public string world;
-
- [ProtoMember(8)]
- public List<UserInfo> tankPlayerList;
-
- }
-
- [ProtoContract]
- public class UserIds {
- [ProtoMember(1, DataFormat = DataFormat.ZigZag)]
- public long[] userIds;
- }
-
- [ProtoContract]
- public class UserInfos
- {
- [ProtoMember(1, DataFormat = DataFormat.ZigZag)]
- public UserInfo[] users;
- }
-
- [ProtoContract]
- public class HeartBeat
- {
- [ProtoMember(1, DataFormat = DataFormat.ZigZag)]
- public int beat;
- }
-
-
-
-
9.定义客户端处理消息类:Client:
- using System;
- using UnityEngine;
- using Cysharp.Threading.Tasks;
- using System.Threading;
- using System.Collections;
- using UnityWebSocket;
-
- namespace UTNET
- {
- public enum NetWorkState
- {
- CONNECTING,
- CONNECTED,
- DISCONNECTED,
- TIMEOUT,
- ERROR,
- KICK
- }
-
- public class Client
- {
- private static int RqID = 0;
-
- //public NetWorkState state;
-
- public Action OnReconected, OnDisconnect, OnConnected;
- public Action<string> OnError;
- public uint retry;
- public bool isConnect = false;
- Protocol protocol;
- WebSocket socket;
- UniTaskCompletionSource<bool> utcs;
- private bool isForce;
- private bool isHeardbeat = false;
- private string host;
-
- public Client(string host)
- {
- Debug.Log("Client" + host);
- this.host = host;
- this.createSocket();
- }
-
- private void createSocket()
- {
- if (socket != null) {
-
- }
- Debug.Log("createSocket host=" + this.host);
- utcs = new UniTaskCompletionSource<bool>();
- if (socket != null) {
- socket.CloseAsync();
- socket.OnClose += ReCreateSocket;
- }
- else {
- socket = new WebSocket(this.host);
- socket.OnOpen += OnOpen;
- socket.OnMessage += OnMessage;
- socket.OnClose += OnClose;
- socket.OnError += OnErr;
- }
-
-
-
- }
-
- private void ReCreateSocket(object sender, CloseEventArgs e) {
- socket = new WebSocket(this.host);
- socket.OnOpen += OnOpen;
- socket.OnMessage += OnMessage;
- socket.OnClose += OnClose;
- socket.OnError += OnErr;
- }
-
- //断线重连逻辑
- public IEnumerator ReconectScoektAsync()
- {
- while (true)
- {
- yield return new WaitForSeconds(2);
- Debug.Log(" client : reconectScoekt");
- if (socket.ReadyState == WebSocketState.Connecting || socket.ReadyState == WebSocketState.Open) break;
- socket.ConnectAsync();
- }
- }
-
-
- //开始心跳逻辑
- public IEnumerator StartHeardBeat()
- {
- isHeardbeat = true;
- while (true)
- {
- Debug.Log("开始发送心跳");
- if (isHeardbeat == false) continue;
- yield return new WaitForSeconds(2);
- //发送心跳
- this.sendHeardbeat();
- }
- }
-
- private async void sendHeardbeat()
- {
- int u1 = await this.RequestAsync<int, int>("heartbeat", 21);
-
- }
-
-
- public UniTask<bool> ConnectAsync()
- {
- socket.ConnectAsync();
- return utcs.Task;
- }
-
- private void OnOpen(object sender, OpenEventArgs e)
- {
- if (protocol == null)
- protocol = new Protocol();
- protocol.SetSocket(socket);
- protocol.OnReconected = OnReconected;
- protocol.OnError = OnError;
- //bool isOK = await protocol.HandsharkAsync(this.token);
- Debug.Log("open:" + e);
- isConnect = true;
- utcs.TrySetResult(true);
- OnConnected?.Invoke();
- }
-
-
- private async void OnClose(object sender, CloseEventArgs e)
- {
- if (socket.ReadyState == UnityWebSocket.WebSocketState.Connecting || socket.ReadyState == UnityWebSocket.WebSocketState.Open) return;
- await UniTask.SwitchToMainThread();
- isConnect = false;
- Cancel();
- if (!isForce)
- {
- await UniTask.Delay(1000);
- //socket.Open();
- }
- OnDisconnect?.Invoke();
- }
-
- public void OnErr(object sender, ErrorEventArgs e)
- {
- utcs.TrySetResult(false);
- isConnect = false;
- //Debug.LogError(e.Exception.Message);
- }
-
- private void OnMessage(object sender, MessageEventArgs e)
- {
- protocol.OnReceive(e.RawData);
- isConnect = true;
- }
-
-
- public void OnNet(uint mid, uint sid, Action<ExternalMessage> cb)
- {
- uint merge = PackageProtocol.getMergeCmd(mid, sid);
- protocol.SetOnNet(merge, cb);
- }
-
- public void OnNetEx(string name, Action<ExternalMessage> cb)
- {
- Proto pb = ProtoMaps.Instance.protos[name] as Proto;
- uint merge = PackageProtocol.getMergeCmd(pb.mid, pb.sid);
- protocol.SetOnNet(merge, cb);
- }
-
- public void Notify<T>(string route, T info = default)
- {
- //uint rqID = (uint)Interlocked.Increment(ref RqID);
- try
- {
- #if SOCKET_DEBUG
- Debug.Log(string.Format("[Notify] -->> [{0}] {1}", route, JsonUtility.ToJson(info)));
- #endif
- protocol.Notify<T>(route, info);
- }
- catch (Exception e)
- {
- Debug.Log(string.Format("[Notify Exception]{0}", e.Message));
- throw e;
- }
- }
-
-
-
- public async UniTask<S> RequestAsyncEx<T, S>(uint mid, uint sid, T info = default, string modelName = null)
- {
-
- uint cmdMerge = PackageProtocol.getMergeCmd(mid, sid);
-
- uint rqID = (uint)Interlocked.Increment(ref RqID);
- try
- {
- #if SOCKET_DEBUG
- Debug.Log(string.Format("[{0}][Request] -->> [{1}] {2}", rqID, route, JsonUtility.ToJson(info)));
- #endif
-
- ExternalMessage pack = await protocol.RequestAsync<T>(rqID, cmdMerge, info, modelName);
-
- S msg = PackageProtocol.DecodeInfo<S>(pack.data);
-
-
- #if SOCKET_DEBUG
- Debug.Log(string.Format("[{0}][Request] <<-- [{1}] {2} {3}", rqID, route, JsonUtility.ToJson(msg), JsonUtility.ToJson(msg.info)));
- #endif
- return msg;
- }
- catch (Exception e)
- {
- //Debug.Log(string.Format("[{0}][RequestAsync Exception]{1}", rqID, e.Message));
- throw e;
- }
- }
-
-
- public async UniTask<S> RequestAsync<T, S>(string name, T info = default, string modelName = null)
- {
-
- // #if UNITY_WEBGL && !UNITY_EDITOR //只在WebGl下生效
- if (PlayerData.Instance.IsEditorMode) {
- S msg = PackageProtocol.DecodeInfo<S>(null);
- return msg;
- }
- //#endif
-
- //Debug.Log($"sendMsg {name}");
-
- Proto pb = ProtoMaps.Instance.Name2Pb(name);
-
- uint cmdMerge = PackageProtocol.getMergeCmd(pb.mid, pb.sid);
-
- uint rqID = (uint)Interlocked.Increment(ref RqID);
- //Debug.LogError("调试消息id" + rqID);
-
- try
- {
- #if SOCKET_DEBUG
- Debug.Log(string.Format("[{0}][Request] -->> [{1}] {2}", rqID, route, JsonUtility.ToJson(info)));
- #endif
-
- ExternalMessage pack = await protocol.RequestAsync<T>(rqID, cmdMerge, info, modelName);
-
-
- S msg = PackageProtocol.DecodeInfo<S>(pack.data);
- #if SOCKET_DEBUG
- Debug.Log(string.Format("[{0}][Request] <<-- [{1}] {2} {3}", rqID, route, JsonUtility.ToJson(msg), JsonUtility.ToJson(msg.info)));
- #endif
- return msg;
- }
- catch (Exception e)
- {
- //Debug.Log(string.Format("[{0}][RequestAsync Exception]{1}", rqID, e.Message));
- //Debug.Log("房间网络连接断开.......");
- S msg = PackageProtocol.DecodeInfo<S>(null);
- return msg;
- throw e;
- }
-
-
-
-
- }
-
- public void Cancel(bool isForce = false)
- {
- this.isForce = isForce;
- utcs.TrySetCanceled();
- if (socket.ReadyState != UnityWebSocket.WebSocketState.Closed)
- {
- socket.CloseAsync();
- }
- if (protocol != null)
- {
- protocol.StopHeartbeat();
- protocol.CanceledAllUTcs();
- }
- }
-
-
- public UnityWebSocket.WebSocketState getSocketState() {
-
- if (socket != null) {
-
- return socket.ReadyState;
- }
- return 0;
- }
-
-
- public void Close() {
- Debug.Log("UnityWebSocket......" + socket);
- Debug.Log("UnityWebSocket状态......"+ socket.ReadyState);
- if (socket!=null && socket.ReadyState == UnityWebSocket.WebSocketState.Open) {
- socket.CloseAsync();
- Debug.Log("强制玩家关闭连接......");
- }
- }
-
-
- void OnDestroy()
- {
- socket.CloseAsync();
- }
- }
- }
10.最终定义网络管理器:NetManager.cs
-
- using Cysharp.Threading.Tasks;
- using System;
- using System.Collections.Generic;
- using System.IO;
- using UnityEngine;
- using UnityEngine.Networking;
- using UTNET;
-
-
- public class NetMessage<T> {
- public uint mergeid;
- delegate void Message(object sender, T msg);
-
- }
-
- public class NetManager : MonoBehaviour {
-
- private NetManager() { }
-
- public static NetManager Instance;
-
-
- public string token, version;
- public Client client;
- public bool isConeccet = false;
- public string HaiMetaSpaceUrl = "*****";
-
- //192.144.138.221 /game.icewhale.net
- Dictionary<string, List<Action<ExternalMessage>>> actions = new Dictionary<string, List<Action<ExternalMessage>>>();
- private Coroutine rec;
-
-
-
- private void Awake() {
- Instance = this;
- }
-
- async void Start() {
- DontDestroyOnLoad(this);
- Screen.sleepTimeout = SleepTimeout.NeverSleep;
- pushBtn();
- }
-
- //public Client getNewClient() {
-
- // return newClient;
- //}
-
- //public void setNewClient(Client vNewClient) {
-
- // newClient = vNewClient;
- //}
-
-
- public void webOpenHaiMetaUrl(string param = null) {
-
- //string url = this.HaiMetaSpaceUrl+param;
- //UnityEngine.Application.ExternalEval("window.location.href = \"" + url + "\";");
- }
-
- public void webOpenNewSpace(string spaceUrl,string spaceId)
- {
-
- string url = spaceUrl + "?param={\"spaceId\":"+ spaceId + ",\"userName\":"+PlayerData.Instance.Name+",\"templateId\":"+ PlayerData.Instance.TemplateId + "}" + "&token=" + PlayerData.Instance.ltk + "&type=4"+ "&state=1";
- UnityEngine.Application.ExternalEval("window.location.href = \"" + url + "\";");
-
-
- }
-
- private void Update() {
- //if (client == null && newClient != null) {
-
- // client = getNewClient();
- //}
- }
-
- public async void CreateConeccetionBtn()
- {
-
- //string host = string.Format("wss://*****");
- Debug.Log("CreateConeccetionBtn" +Host.gameServer);
- client = new Client(Host.gameServer);
- client.OnDisconnect = OnDisconnect;
- client.OnReconected = OnReconected;
- client.OnError = OnError;
- client.OnConnected = OnConnected;
-
- bool isConeccet = await client.ConnectAsync();
- if (isConeccet)
- {
- Debug.Log("网络链接成功");
- }
-
- }
-
- //public void resetClient() {
- // setNewClient(client);
- //}
-
- private void OnConnected() {
- Debug.Log("OnConnected");
- if (rec != null)
- StopCoroutine(rec);
- this.TriggerEvent(EventName.SocketOpen, null);
-
- }
-
-
-
-
-
- private void OnError(string msg) {
- Debug.LogError(string.Format("err msg:{0}", msg));
- }
-
- private void OnReconected() {
- Debug.Log("OnReconect");
- }
-
- private void OnDisconnect() {
- Debug.Log("OnDisconnect");
- //client = null;
- rec = StartCoroutine(client.ReconectScoektAsync()); //断线重连逻辑
- }
-
- public void pushBtn() {
- LoadNetCofig();
- }
-
- void LoadNetCofig() {
- var configPath = Application.dataPath;
-
- #if UNITY_EDITOR
- var filepath = Path.Combine(Application.dataPath.Replace("Assets", ""), "config.txt");
- #else
- var filepath = Path.Combine(Application.dataPath, "config.txt");
- #endif
- Debug.Log("configPath" + filepath);
- filepath = filepath.Replace("\\", "/");
- LoadFileSetNetwork(filepath);
- }
-
-
-
- async void LoadFileSetNetwork(string filepath) {
- UnityWebRequest www = UnityWebRequest.Get(filepath);
-
-
- await www.SendWebRequest();
-
- if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError) {
- Debug.LogError(www.error);
- }
- else {
- string json = www.downloadHandler.text;
- var data = LitJson.JsonMapper.ToObject(json);
-
- if ((string)data["AssetBundleIP"] != string.Empty) {
- Host.AssetBundleIP = (string)data["AssetBundleIP"];
- }
- #if UNITY_EDITOR
- var serverMode = AppConst.serverMode;
- #else
- var serverMode = (ServerMode)int.Parse(HttpHelper.GetUrlParam("severAPI"));
- #endif
- AppConst.serverMode = serverMode;
- switch (serverMode) {
- case ServerMode.preview:
- Host.ApiHost = (string)data["preview"];
- break;
- case ServerMode.dev:
- Host.ApiHost = (string)data["dev"];
- break;
- }
-
-
-
-
- #if !UNITY_EDITOR
- var paramUrl = HttpHelper.GetUrlParam("param");
- JsonData paramdata = JsonMapper.ToObject(UtilsFunc.UnicodeToString(paramUrl));
- try {
- string spaceId = paramdata["spaceId"].ToJson();
- PlayerData.Instance.SpaceId = spaceId;
- PlayerData.Instance.ltk = HttpHelper.GetUrlParam("token");
-
-
- }
- catch (Exception e) {
- Debug.LogError(e.Message + '\n' + e.StackTrace);
- }
- #endif
- Debug.Log("LoadFileSetNetwork SpaceId" + PlayerData.Instance.SpaceId);
-
- //如果在编辑器下面,并且不填sapceId,那么则进入发布模式
-
- if (string.IsNullOrEmpty(PlayerData.Instance.SpaceId) == false) {
- await HttpHelper.Instance.GetSceneConfig();
- }
-
-
- #if !UNITY_EDITOR
- if (paramdata.ContainsKey("templateId")) {
-
- if (paramdata["templateId"] != null) {
- string tempStr = (string)paramdata["templateId"];
- bool isExist = int.TryParse(tempStr,out var tempId);
- if (isExist) PlayerData.Instance.TemplateId = tempId;
- }
-
- }
-
- string stateUrl = HttpHelper.GetUrlParam("state");
- #else
- string stateUrl = string.IsNullOrEmpty(PlayerData.Instance.SpaceId) == true ? AppConst.PublicMode : AppConst.ViewMode;
- #endif
- var arg = new SceneLoadActionArgs();
- arg.state = stateUrl;
-
-
-
-
-
- EventManager.Instance.TriggerEvent(EventName.LoadSceneAction, arg);
-
- if (stateUrl == AppConst.PublicMode) {
- PlayerData.Instance.IsEditorMode = true;
- PlayerData.Instance.IsPublicMode = true;
- EventManager.Instance.TriggerEvent(EventName.OnPublishEnter);
- HttpHelper.Instance.GetDefaultSpaceImg();
- return;
- }
-
- #if !UNITY_EDITOR
- await HttpHelper.Instance.GetServerConfig();
- #else
- Host.gameServer = (string)data["local"];
- #endif
- CreateConeccetionBtn();
-
- }
- }
-
- private async UniTaskVoid CreateConeccetion() {
- Debug.Log("开始...");
-
- bool isConeccet = await client.ConnectAsync();
-
- if (isConeccet) {
- Debug.Log("网络链接成功");
- // this.Register<RegisterInfo>("test", (int code) =>
- // {
- // Debug.Log("网络事件" +code);
- // });
- if (client != null) client.isConnect = true;
-
- //this.SendAPI();
-
- }
- else
- Debug.Log("多次链接仍让未成功");
- }
-
- public void AddSyncHandler(string name, Action<SyncWrapData> call) {
- NetManager.Instance.Register<GameFrame>("gameFrame", (ExternalMessage pack) => {
- GameFrame info = PackageProtocol.DecodeInfo<GameFrame>(pack.data);
- //if (info.location.playerId == PlayerData.Instance.PlayerId) {
- // return; //自己不用给自己同步
- //}
-
- var opc = info.opc;
- SyncWrapData dataBase = null;
- try {
-
- dataBase = JsonUtility.FromJson<SyncWrapData>(opc);
-
- if (dataBase.playerId != PlayerData.Instance.PlayerId.ToString()) { //只接受不同playerId的数据
- call.Invoke(dataBase);
- }
-
- }
- catch (Exception e) {
- //Debug.LogError(e);
- }
- });
- }
-
- public async void SendSyncData(SyncWrapData syncBase) {
- string jsonData = JsonUtility.ToJson(syncBase);
-
- if (NetManager.Instance.client == null) {
- Debug.LogError("NetManager.Instance.client == null");
- return;
- }
-
- var gameFrame = new GameFrame();
- gameFrame.opc = jsonData;
- await NetManager.Instance.client.RequestAsync<GameFrame, GameFrame>("gameFrame", gameFrame);
- }
-
- public async void SendSyncWorldData(SyncWorldData worldData) {
- string jsonData = LitJson.JsonMapper.ToJson(worldData);
-
- if (NetManager.Instance.client == null) {
- Debug.LogError("NetManager.Instance.client == null");
- return;
- }
- //Debug.Log("SendSyncWorldData worldData:" + jsonData);
- var gameFrame = new GameFrame();
- gameFrame.world = jsonData;
- await NetManager.Instance.client.RequestAsync<GameFrame, GameFrame>("gameFrame", gameFrame);
- }
-
- public async void SendFrameLocationData(SyncTransfomData syncPos,SyncWrapData wrap) {
-
- if (NetManager.Instance.client == null) {
- Debug.LogError("NetManager.Instance.client == null");
- return;
- }
-
- var gameFrame = new GameFrame();
-
- gameFrame.opc = JsonUtility.ToJson(wrap);
-
- gameFrame.location = new TankLocation();
- SyncTransfomUtil.ToTankLocation(ref gameFrame.location, syncPos);
- gameFrame.location.playerId = PlayerData.Instance.PlayerId;
-
- await NetManager.Instance.client.RequestAsync<GameFrame, GameFrame>("gameFrame", gameFrame);
- }
-
-
-
-
- public void Register<T>(string name, Action<ExternalMessage> ac) {
- //#if UNITY_WEBGL && !UNITY_EDITOR //只在WebGl下生效
- if (PlayerData.Instance.IsEditorMode) return;
- //#endif
- //加入事件列表
- Proto pb = ProtoMaps.Instance.Name2Pb(name);
-
- List<Action<ExternalMessage>> list = null;
- if (actions.ContainsKey(name)) {
- list = actions[name];
- }
- else {
- list = new List<Action<ExternalMessage>>();
- actions.Add(name, list);
- }
- list.Add(ac);
-
- client.OnNetEx(name, (ExternalMessage pack) => {
- T info = PackageProtocol.DecodeInfo<T>(pack.data);
- //Debug.Log("网络事件" + JsonUtility.ToJson(info));
- //遍历事件列表依次回调
- foreach (Action<ExternalMessage> cb in list) {
- cb?.Invoke(pack);
- }
-
- });
-
- }
-
-
- public async void SendAPI() {
-
- //请求注册
- //RegisterInfo register = new RegisterInfo();
- //register.account = "abc";
- //register.phoneNum = "abc";
- //register.pwd = "abc";
-
- //RegisterInfo a = await client.RequestAsync<RegisterInfo, RegisterInfo>("test", register);
-
- //Debug.Log(a.phoneNum);
-
-
- client.OnNetEx("login", (ExternalMessage pack) => {
-
- Debug.LogError("回调返回");
- //T info = PackageProtocol.DecodeInfo<T>(pack.data);
- Debug.Log("网络事件" + JsonUtility.ToJson(info));
- 遍历事件列表依次回调
- //foreach (Action<ExternalMessage> cb in list)
- //{
- // cb?.Invoke(pack);
- //}
-
- });
-
-
-
- LoginVerify loginVerify = new LoginVerify();
- loginVerify.account = "18060974935";
- loginVerify.pwd = "123456ab";
- loginVerify.loginBizCode = 1;
-
- UserInfo ret = await NetManager.Instance.client.RequestAsync<LoginVerify, UserInfo>("login", loginVerify);
- Debug.Log(ret.nickName);
-
-
- TankEnterRoom myEnterRoom = new TankEnterRoom();
-
- myEnterRoom.roomId = 1002;
- myEnterRoom.team = 1;
-
- TankEnterRoom ret2 = await NetManager.Instance.client.RequestAsync<TankEnterRoom, TankEnterRoom>("enterRoom", myEnterRoom);
- Debug.Log(ret2.time);
-
-
-
-
- //int code = await client.RequestAsync<String, int>("gameObjUsed", "ok");
-
-
- }
-
- public bool isConnected() {
- return isConeccet;
- }
-
- private void OnDestroy() {
- if (client != null) {
- client.Cancel();
- }
- }
- }
-
11.发送网络消息的应用:
UserInfo ret = await NetManager.Instance.client.RequestAsync<LoginVerify, UserInfo>("login", loginVer);
12.监听网络消息:
- NetManager.Instance.Register<TankEnterRoom>("enterRoom", (ExternalMessage pack) =>
- {
- Debug.Log("新玩家进去");
- this.onNewRoleEnter2(pack);
- });
- public void onNewRoleEnter2(ExternalMessage pack) {
- TankEnterRoom info = PackageProtocol.DecodeInfo<TankEnterRoom>(pack.data);
- List<UserInfo> roleList = info.tankPlayerList;
- Debug.Log("新进入房间玩家列表" + roleList.ToString());
- long roleId = info.playerId;
- Debug.Log("新进入房间玩家id" + roleId);
- foreach (UserInfo vRole in roleList)
- {
- Debug.Log("新进入房间玩家" + JsonUtility.ToJson(vRole));
- if (vRole.id == roleId && vRole.id!=PlayerData.Instance.PlayerId)
- {
- if (!RolesManager.Instance.isExistRoleById(vRole.id)) {
- GuestinfoDataModel.Instance.OnNewRoleIntoRoom(vRole);
- RolesManager.Instance.synPlayer(vRole, true);
- }
- }
- }
-
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。