using Assets.MyGame.Scripts.Net; using Assets.MyGame.Scripts.Net.WebResponses; using Assets.MyGame.Scripts.Net.WebResponses.ChineseChess; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Text; using Unity.VisualScripting; using UnityEngine; public partial class WebSocketSendList { public static string MakeKillFishSign( string roomId, string userId, string fishNumber, string fishId, long gold, long timespan, string bulletId, long timestamp, string signKey ) { string rawStr = $"{roomId}|{userId}|{fishNumber}|{fishId}|{gold}|{timespan}|{bulletId}|{timestamp}"; return Sign(rawStr, signKey); } public static string MakeFireCannonSign(string roomId, string userId, long gold, int count, long timestamp, string signKey) { string rawStr = $"{roomId}|{userId}|{gold}|{count}|{timestamp}"; Debug.Log($"生成签名的原始字符串:{rawStr}"); return Sign(rawStr, signKey); } public static string Sign(string rawStr, string signKey) { string input = rawStr + "|" + signKey; using (var md5 = System.Security.Cryptography.MD5.Create()) { byte[] bytes = Encoding.UTF8.GetBytes(input); byte[] hash = md5.ComputeHash(bytes); // 转成 hex 字符串(前 8 位) StringBuilder sb = new StringBuilder(); for (int i = 0; i < hash.Length; i++) { sb.Append(hash[i].ToString("x2")); } return sb.ToString().Substring(0, 8); } } /// /// 获取房间信息 /// public static void GetRoomListFish(int leave,int district) { WebSocketManager.Instance.Send(SocketType.room, "{\"type\":1,\"target\":\"GetRoomList\",\"invocationId\":\"1\",\"arguments\":[\"" + GameManager.Instance.user.id + "\"," + (int)GameManager.Instance.gameData.currGameType + "," + leave + ","+ district + "]}"); } /// /// 打中鱼 /// /// /// /// 配置id /// 唯一id public static void KillFish( string roomId, string userId, string fishNumber, string fishId, int paoBei, string bulletId, string signKey ) { long timespan = BeijingTimeManager.Instance.CurrentTimestampMs; // 毫秒 long timestamp = timespan / 1000; // 秒级 // count 固定为 1 string sign = MakeKillFishSign(roomId, userId, fishNumber, fishId, paoBei, timespan, bulletId, timestamp, signKey); var sendObj = new WebSocketSendList( "KillFish", roomId, userId, fishNumber, fishId, paoBei, timespan, bulletId, timestamp, sign ); WebSocketManager.Instance.Send(SocketType.game, JsonConvert.SerializeObject(sendObj)); } /// /// 真人玩家发射子弹(对接后端最新签名) /// 服务端方法:Task> FireCannon(roomId, userId, gold, count, timestamp, sign, extra) /// 返回值 List 为服务端生成的 bulletIds,通过 RoomFireNotice 广播给房间所有人 /// /// 房间ID /// 玩家ID /// 单价 /// 数量 /// 秒级时间戳 /// 签名(MD5 前8位) /// 附加数据(SyncGameData) public static void FireCannon( string roomId, string userId, long gold, int count, long timestamp, string sign, object extra = null ) { var sendObj = new WebSocketSendList( "FireCannon", roomId, userId, gold, count, timestamp, sign, extra ); WebSocketManager.Instance.Send(SocketType.game, JsonConvert.SerializeObject(sendObj)); } /// /// 发射子弹 /// /// /// /// 单价 /// 数量 public static void ShootBullet(string roomId, string userId, int gold, int count) { var sendObj = new WebSocketSendList("ShootBullet", roomId, userId, gold, count); WebSocketManager.Instance.Send(SocketType.game, JsonConvert.SerializeObject(sendObj)); } /// /// 链接登录界面ws /// /// public static void Bind(string userId) { var sendObj = new WebSocketSendList("Bind", userId); WebSocketManager.Instance.Send(SocketType.login, JsonConvert.SerializeObject(sendObj)); } /// /// 断线重连(必须先调用,重新加入房间前调用) /// 对应服务端 FishGameHub.Reconnect(string userId) /// public static void Reconnect(string userId) { var sendObj = new WebSocketSendList("Reconnect", userId); WebSocketManager.Instance.Send(SocketType.game, JsonConvert.SerializeObject(sendObj)); } /// /// 获取玩家信息(结果通过 SendPlayerInfoResult 事件返回) /// 对应服务端 FishGameHub.GetPlayerInfo(string roomId, string userId) /// public static void GetPlayerInfo(string roomId, string userId) { var sendObj = new WebSocketSendList("GetPlayerInfo", roomId, userId); WebSocketManager.Instance.Send(SocketType.game, JsonConvert.SerializeObject(sendObj)); } /// /// 加入房间 /// /// /// /// 房间等级 public static void JoinRoom(string roomId, string userId, int sitID) { var sendObj = new WebSocketSendList("JoinRoom", roomId, userId, sitID); WebSocketManager.Instance.Send(SocketType.game, JsonConvert.SerializeObject(sendObj)); } /// /// 获取房间信息 /// /// 房间id public static void GetRoomInfo(string roomId) { var sendObj = new WebSocketSendList("GetRoomInfo", roomId); WebSocketManager.Instance.Send(SocketType.room, JsonConvert.SerializeObject(sendObj)); } /// /// 使用道具 /// /// /// /// 道具id /// 道具消耗 public static void UseProp(string roomId, string userId, string propNumber, int diamond) { var sendObj = new WebSocketSendList("UseProp", roomId, userId, propNumber, diamond); WebSocketManager.Instance.Send(SocketType.game, JsonConvert.SerializeObject(sendObj)); } /// /// 离开房间 /// /// /// public static void LeaveRoom(string roomId, string userId) { WebSocketManager.Instance.isGame=false; var sendObj = new WebSocketSendList("LeaveRoom", roomId, userId); WebSocketManager.Instance.Send(SocketType.game, JsonConvert.SerializeObject(sendObj)); } /// /// 更改炮倍 /// /// /// /// public static void ChangeGunMultiple(string roomId, string userId,int multiple) { var sendObj = new WebSocketSendList("ChangeGunMultiple", roomId, userId, multiple); WebSocketManager.Instance.Send(SocketType.game, JsonConvert.SerializeObject(sendObj)); } /// /// 同步等级常量(秒) /// public const float SYNC_LV1 = 0.2f; public const float SYNC_LV2 = 0.5f; public const float SYNC_LV3 = 1f; // 队列 private static float _syncLv1Timer; private static float _syncLv2Timer; private static float _syncLv3Timer; private static Dictionary _syncLv1Cover = new Dictionary(); private static List _syncLv1Append = new List(); private static List _syncLv2Append = new List(); private static List _syncLv3Append = new List(); /// 数据同步(自动按类型分级入队) public static void SyncGameData(object obj) { var sgd = obj as SyncGameData; if (sgd == null) { SendImmediate(JsonConvert.SerializeObject(obj)); return; } switch (sgd.frameType) { case FrameDataType.fireBullet: case FrameDataType.rotateTurrect: // ⭐ 开火/旋转实时发送,和开炮一样立即广播,保证炮台旋转流畅 SendImmediate(BuildJson(sgd)); return; case FrameDataType.changeAutoTarget: _syncLv1Cover[sgd.sitPos] = sgd; return; case FrameDataType.randomDieFish: _syncLv1Append.Add(sgd); return; case FrameDataType.createFish: case FrameDataType.changeTurretState: case FrameDataType.changeTurret: case FrameDataType.freezeFish: _syncLv2Append.Add(sgd); return; case FrameDataType.requestSyncFish: // ⭐ 新玩家加载完成的"请求同步鱼":立即发送,房主尽快响应广播 SendImmediate(BuildJson(sgd)); return; default: _syncLv3Append.Add(sgd); return; } } /// 每帧调用,由 GameMainPanel.Update 驱动 public static void FlushSyncQueues(float dt) { _syncLv1Timer += dt; if (_syncLv1Timer >= SYNC_LV1) { _syncLv1Timer = 0f; FlushLv1(); } _syncLv2Timer += dt; if (_syncLv2Timer >= SYNC_LV2) { _syncLv2Timer = 0f; FlushLv2(); } _syncLv3Timer += dt; if (_syncLv3Timer >= SYNC_LV3) { _syncLv3Timer = 0f; FlushLv3(); } } private static void FlushLv1() { foreach (var kv in _syncLv1Cover) SendImmediate(BuildJson(kv.Value)); foreach (var sgd in _syncLv1Append) SendImmediate(BuildJson(sgd)); _syncLv1Cover.Clear(); _syncLv1Append.Clear(); } private static void FlushLv2() { foreach (var sgd in _syncLv2Append) SendImmediate(BuildJson(sgd)); _syncLv2Append.Clear(); } private static void FlushLv3() { foreach (var sgd in _syncLv3Append) SendImmediate(BuildJson(sgd)); _syncLv3Append.Clear(); } private static void SendImmediate(string json) { WebSocketManager.Instance.Send(SocketType.game, json); } private static string BuildJson(object obj) { var sendObj = new WebSocketSendList { type = 1, invocationId = "1", target = "SyncGameData", arguments = new System.Collections.Generic.List { GameManager.Instance.gameData.currRoomData.roomId, obj } }; return JsonConvert.SerializeObject(sendObj); } }