Files
5.24BuYu/Assets/MyGame/Scripts/Net/WebSocketReponseList.cs
T
2026-08-04 21:20:56 +08:00

1126 lines
49 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Assets.MyGame.Scripts.Net.API;
using Assets.MyGame.Scripts.Net.WebResponses;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Assets.MyGame.Scripts.Net
{
public class WebSocketReponseList
{
/// <summary>已加载(或正在加载)的游戏场景名集合,防止人机 RoomJoinNotice 触发重复 LoadSceneAsync</summary>
private static readonly HashSet<string> _loadedGameScenes = new HashSet<string>();
/// <summary>清除指定场景的加载标记(退出游戏时调用,下次进入可重新加载)</summary>
public static void ClearLoadedGameScene(string sceneName)
{
if (!string.IsNullOrEmpty(sceneName))
_loadedGameScenes.Remove(sceneName);
}
/// <summary>
/// 获取房间列表 room
/// </summary>
/// <param name="msg"></param>
public static void GetRoomListChineseChessResult(string msg)
{
var var1 = UIManager.Instance.Show<UIChineseChessDistrict>(UIPanel.MainGamePanelZGXQ, msg);
}
public static void GetGoldConfigSuccessChineseChessResult(string msg)
{
UIManager.Instance.Get<ChineseChessMainPanel>(UIPanel.MainGamePanelZGXQ).subUIManager.ShowAsync(UIPanel.ZGXQGameExplain);
}
/// <summary>
/// 获取房间列表 room
/// </summary>
/// <param name="msg"></param>
public static void GetRoomListResult(string msg)
{
var var1 = UIManager.Instance.Get<UIHomeMain>(UIPanel.HomeMainPanel);
var selectRoomPanel = var1.subUIManager.Get<UIHome_BY_SelectRoomLevelPanel>(UIPanel.SelectRoomLeavePenel);
if (selectRoomPanel != null && selectRoomPanel.isMySelectRoom)
{
// 手动选座:显示房间列表给玩家选
selectRoomPanel.panel_UIHome_BY_MySelectRoomPenel.Show(msg);
}
// 快速加入分支已迁移到 OnSelectRoom → /api/Room/QuickJoin,不再走 GetRoomList
}
/// <summary>
/// 加入房间 room
/// </summary>
/// <param name="msg"></param>
public static void RoomJoinNotice(string msg)
{
// ⭐ 记录金币流水:收到加钱信息(RoomJoinNotice 推送,尚未执行)—— 只记录自己
try
{
var notice = JsonConvert.DeserializeObject<WebSocketResponseBase<RoomJoinNotice>>(msg).arguments[0];
string myId = GameManager.Instance.user?.id;
if (!string.IsNullOrEmpty(myId) && notice.userId == myId)
{
GameManager.LogGoldFlowReceiveGain("RoomJoinNotice", $"推送金币={notice.gold} 钻石={notice.diamond} 炮台消耗={notice.cannonMultiple} 座位={notice.position}");
}
}
catch { }
string sceneName = "";
UIPanel uIPanel = UIPanel.None;
switch (GameManager.Instance.gameData.currGameType)
{
case GameType.JQQD:
break;
case GameType.defaultPanel:
break;
case GameType.FLZY:
sceneName = "FLZY";
uIPanel = UIPanel.MainGamePanelFLZY;
break;
case GameType.YYSC:
sceneName = "YYSC";
uIPanel = UIPanel.MainGamePanelYYSC;
break;
case GameType.ZGXQ:
break;
case GameType.XJMH:
sceneName = "XJMH";
uIPanel = UIPanel.MainGamePanelXJMH;
break;
case GameType.HHWK:
sceneName = "HHWK";
uIPanel = UIPanel.MainGamePanelHHWK;
break;
default:
break;
}
if (UIManager.Instance.Get<GameMainPanel_Base>(uIPanel)==null)
{
UIManager.Instance.ShowAsync(uIPanel, UIPanel.HomeMainPanel, msg);
// 卸载所有已加载的游戏地图场景(保证场景唯一性)
string[] gameScenes = { "FLZY", "YYSC", "XJMH", "HHWK" };
foreach (var sn in gameScenes)
{
// 不卸载即将加载的目标场景
if (sn == sceneName) continue;
var s = SceneManager.GetSceneByName(sn);
if (s.IsValid() && s.isLoaded)
{
SceneManager.UnloadSceneAsync(sn);
_loadedGameScenes.Remove(sn);
}
}
// 目标场景仅加载一次:标记已加载后不再重复 LoadSceneAsync(防止人机通知触发重复加载)
if (!string.IsNullOrEmpty(sceneName) && !_loadedGameScenes.Contains(sceneName))
{
_loadedGameScenes.Add(sceneName);
var targetScene = SceneManager.GetSceneByName(sceneName);
if (!targetScene.IsValid() || !targetScene.isLoaded)
SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
}
UIManager.Instance.Close(UIPanel.HomeMainPanel);
WebSocketManager.Instance.isGame = true;
}
else
{
// 面板已存在 → 新玩家加入,转发给现有面板激活座位
var panel = UIManager.Instance.Get<GameMainPanel_Base>(uIPanel);
if (panel is GameMainPanel_FishBase fishPanel)
{
var v = JsonConvert.DeserializeObject<WebSocketResponseBase<RoomJoinNotice>>(msg);
if (v.arguments.Count > 0)
{
var notice = v.arguments[0];
// 如果座位被bot占用则替换
var targetSit = fishPanel.list_Sits.Find(s => s.posID == notice.position);
if (targetSit != null && targetSit.isBot)
{
targetSit.isBot = false;
targetSit.ShowWaiting();
}
fishPanel.JoinGame(notice.position, notice.gold, notice.diamond, notice.cannonMultiple, 0, notice.userId, notice.cannonId, notice.wingsId);
// 后端动态加入的机器人也要标记
if (targetSit != null && !string.IsNullOrEmpty(notice.userId) && notice.userId.StartsWith("bot_"))
targetSit.isBot = true;
}
}
}
}
/// <summary>
/// 房间列表 room
/// </summary>
/// <param name="msg"></param>
public static void RoomInfo(string msg)
{
WebSocketManager.Instance.isGame = false;
var roomData = JsonConvert.DeserializeObject<WebSocketResponseBase<RoomChangeResponse>>(msg).arguments[0];
if (roomData != null)
{
GameManager.Instance.gameData.currRoomData = roomData;
}
else
{
Debug.LogError("roomdata是空:" + msg);
}
}
public static void RoomKillNotice(string msg)
{
// 离开倒计时期间不触发击杀/爆奖特效
var panel = UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelDefault)
?? UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelFLZY)
?? UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelYYSC)
?? UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelXJMH)
?? UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelHHWK);
if (panel != null && panel.IsLeavingCountdown()) return;
CreateFishManager.Instance.Ws_DieFish(msg);
}
public static void RoomFireNotice(string msg)
{
FireCannonSendData fireCannonSendData = JsonConvert.DeserializeObject<WebSocketResponseBase<FireCannonSendData>>(msg).arguments[0];
var syncGameData = fireCannonSendData.extra;
EventCenter.Instance.EventTrigger<FireCannonSendData>(ConstEventString.SyncGameData_FireBullet, fireCannonSendData);
}
public static void SyncPlayerGold(string msg)
{
var gold = JsonConvert.DeserializeObject<WebSocketResponseBase<long>>(msg).arguments[0];
switch (GameManager.Instance.gameData.currGameType)
{
case GameType.JQQD:
break;
case GameType.defaultPanel:
UpdateLocalPlayerGold(UIPanel.MainGamePanelDefault, gold);
break;
case GameType.FLZY:
UpdateLocalPlayerGold(UIPanel.MainGamePanelFLZY, gold);
break;
case GameType.YYSC:
UpdateLocalPlayerGold(UIPanel.MainGamePanelYYSC, gold);
break;
case GameType.ZGXQ:
break;
case GameType.XJMH:
UpdateLocalPlayerGold(UIPanel.MainGamePanelXJMH, gold);
break;
case GameType.HHWK:
UpdateLocalPlayerGold(UIPanel.MainGamePanelHHWK, gold);
break;
default:
break;
}
}
/// <summary>
/// 炮倍变更通知:服务端确认玩家炮倍切换后广播给房间内所有玩家。
/// 客户端收到后才应用新炮倍到 consumeGoldNum 和 UI,避免开火扣费不一致。
/// </summary>
public static void ChangeGunMultipleNotice(string msg)
{
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<ChangeGunMultipleNotice>>(msg).arguments[0];
var panel = UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelDefault)
?? UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelFLZY)
?? UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelYYSC)
?? UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelXJMH)
?? UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelHHWK);
if (panel == null) return;
panel.ApplyCannonMultiple(data.userId, data.multiple);
}
/// <summary>
/// 后端推送 SyncPlayerGold 时同步本地玩家金币(底层值 + UI 显示)。
/// 之前只更新 goldNum 不刷新 tMP_GoldNum.text,导致 UI 显示和后端 authoritative 值不同步,
/// 后续杀鱼累加基于修正值但 UI 显示旧值,差距越拉越大。
/// </summary>
private static void UpdateLocalPlayerGold(UIPanel panelType, long gold)
{
var panel = UIManager.Instance.Get<GameMainPanel_FishBase>(panelType);
if (panel == null || panel.currSitData == null) return;
long oldGold = panel.currSitData.goldNum;
long diff = gold - oldGold;
// ⭐ 记录金币流水:收到后端推送 SyncPlayerGold(尚未执行)
if (diff > 0)
GameManager.LogGoldFlowReceiveGain("SyncPlayerGold", $"推送金币={gold} 修正前金币={oldGold} 差值=+{diff} 座位={panel.currSitData.posID}");
else if (diff < 0)
GameManager.LogGoldFlowReceiveCost("SyncPlayerGold", $"推送金币={gold} 修正前金币={oldGold} 差值={diff} 座位={panel.currSitData.posID}");
panel.currSitData.goldNum = gold;
if (panel.currSitData.tMP_GoldNum != null)
panel.currSitData.tMP_GoldNum.text = gold.ToString();
// ⭐ 记录金币流水:已执行修正(本地已写入)
if (diff > 0)
GameManager.LogGoldFlowAppliedGain("SyncPlayerGold", $"已修正金币={gold} 修正前金币={oldGold} 增加金币=+{diff} 座位={panel.currSitData.posID}");
else if (diff < 0)
GameManager.LogGoldFlowAppliedCost("SyncPlayerGold", $"已修正金币={gold} 修正前金币={oldGold} 扣除金币={diff} 座位={panel.currSitData.posID}");
}
public static void GetRoomInfoResult(string msg)
{
//GameManager.Instance.StartGame();RoomChangeResponse
var roomData = JsonConvert.DeserializeObject<WebSocketResponseBase<RoomChangeResponse>>(msg).arguments[0];
// ⭐ 记录金币流水:收到加钱信息(RoomInfo 推送玩家金币,尚未执行)—— 只记录自己
if (roomData?.players != null)
{
string myId = GameManager.Instance.user?.id;
foreach (var p in roomData.players)
{
if (string.IsNullOrEmpty(myId) || p.userId != myId) continue;
GameManager.LogGoldFlowReceiveGain("RoomInfo", $"推送金币={p.gold} 钻石={p.diamond} 炮台消耗={p.cannonMultiple} 座位={p.position} 房间={roomData.roomId}");
}
}
UIHome_BY_SelectRoomLevelPanel.StartGame(roomData);
}
public static void SyncGameDataResult(string msg)
{
var syncGameData = JsonConvert.DeserializeObject<WebSocketResponseBase<SyncGameData>>(msg).arguments[0];
switch (syncGameData.frameType)
{
case FrameDataType.none:
break;
case FrameDataType.rotateTurrect:
EventCenter.Instance.EventTrigger<SyncGameData>(ConstEventString.SyncGameData_Rotate, syncGameData);
break;
case FrameDataType.fireBullet:
// 机器人角度同步:SyncFire_ForBot 发出的 SyncGameData(fireBullet)
// 与 RoomFireNotice(FireCannonSendData) 分开处理,仅用于存储角度到 botLastAngle
EventCenter.Instance.EventTrigger<SyncGameData>(ConstEventString.SyncGameData_BotFireAngle, syncGameData);
break;
// createFish=3 已废弃:鱼创建改由服务端 OnFishSpawn 事件下发
case FrameDataType.changeAutoTarget:
EventCenter.Instance.EventTrigger<SyncGameData>(ConstEventString.SyncGameData_ChangeTargetMonster, syncGameData);
break;
case FrameDataType.randomDieFish:
EventCenter.Instance.EventTrigger<SyncGameData>(ConstEventString.SyncGameData_RendromDieFish, syncGameData);
break;
case FrameDataType.freezeFish:
EventCenter.Instance.EventTrigger<SyncGameData>(ConstEventString.SyncGameData_FreezeFish, syncGameData);
break;
case FrameDataType.changeTurretState:
EventCenter.Instance.EventTrigger<SyncGameData>(ConstEventString.SyncGameData_TurretState, syncGameData);
break;
case FrameDataType.changeTurret:
EventCenter.Instance.EventTrigger<SyncGameData>(ConstEventString.SyncGameData_ChangeTurret, syncGameData);
break;
case FrameDataType.changeCannonMultiple:
EventCenter.Instance.EventTrigger<SyncGameData>(ConstEventString.SyncGameData_ChangeCannonMultiple, syncGameData);
break;
case FrameDataType.changeWing:
EventCenter.Instance.EventTrigger<SyncGameData>(ConstEventString.SyncGameData_ChangeWing, syncGameData);
break;
// requestSyncFish=11 已废弃:鱼同步改由服务端 OnFishSpawn(type=sync) 下发
case FrameDataType.baoJiangEnd:
EventCenter.Instance.EventTrigger<SyncGameData>(ConstEventString.BaoJiangEnd, syncGameData);
break;
default:
break;
}
}
// ================================================================
// 新协议事件分发 (OnFishSpawn / PlayerAfk / FishFrozen / KillNotice 等)
// ================================================================
/// <summary>
/// 服务端出鱼事件 OnFishSpawn 处理。
/// 破坏性替代旧 createFish 帧同步:服务端统一出鱼,客户端只接收并生成。
/// </summary>
public static void OnFishSpawn(string msg)
{
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<OnFishSpawnData>>(msg).arguments[0];
if (data == null) return;
EventCenter.Instance.EventTrigger<OnFishSpawnData>(ConstEventString.OnFishSpawn, data);
NetLog.Log("出鱼:", msg);
}
/// <summary>鱼列表刷新</summary>
public static void OnFishList(string msg)
{
var fishList = JsonConvert.DeserializeObject<WebSocketResponseBase<object[]>>(msg).arguments[0];
EventCenter.Instance.EventTrigger<object[]>(ConstEventString.OnFishList, fishList);
}
/// <summary>
/// 冰冻道具(40003)生效事件 FishFrozen。
/// 鱼存活时间延长 15 秒,客户端写死 15s 倒计时。
/// </summary>
public static void FishFrozen(string msg)
{
EventCenter.Instance.EventTrigger(ConstEventString.FishFrozen);
}
/// <summary>玩家挂机通知</summary>
public static void PlayerAfkNotice(string msg)
{
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<PlayerAfkNoticeData>>(msg).arguments[0];
if (data == null) return;
EventCenter.Instance.EventTrigger<PlayerAfkNoticeData>(ConstEventString.PlayerAfkNotice, data);
}
/// <summary>玩家取消挂机通知</summary>
public static void PlayerCancelAfkNotice(string msg)
{
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<PlayerAfkNoticeData>>(msg).arguments[0];
if (data == null) return;
EventCenter.Instance.EventTrigger<PlayerAfkNoticeData>(ConstEventString.PlayerCancelAfkNotice, data);
}
/// <summary>
/// 全局击杀公告 KillNotice。
/// 真人击杀/机器人模拟,机器人模拟时额外带 fishName/multiplier/sceneName。
/// </summary>
public static void KillNotice(string msg)
{
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<KillNoticeData>>(msg).arguments[0];
if (data == null) return;
EventCenter.Instance.EventTrigger<KillNoticeData>(ConstEventString.KillNotice, data);
}
/// <summary>机器人退出房间通知</summary>
public static void BotLeaveRoomNotice(string msg)
{
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<BotLeaveRoomNoticeData>>(msg).arguments[0];
if (data == null) return;
WebSocketReponseList.BotLeaveRoom(data.userId);
}
/// <summary>房主断线</summary>
public static void HostDisconnectNotice(string msg)
{
EventCenter.Instance.EventTrigger(ConstEventString.HostDisconnect);
WebSocketReponseList.HostDisconnect();
}
/// <summary>
/// 座位变更 OnSeatChange。
/// 数据: { type, userId, position, showWaiting? }
/// </summary>
public static void OnSeatChange(string msg)
{
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<Dictionary<string, object>>>(msg).arguments[0];
if (data == null) return;
// JSON 字段可能是 long/double/string/bool 等任意类型,统一 ToString()+TryParse 容错
int seatType = data.TryGetValue("type", out var t) && int.TryParse(t?.ToString(), out var st) ? st : 0;
string userId = data.TryGetValue("userId", out var u) ? u?.ToString() : "";
int position = data.TryGetValue("position", out var p) && int.TryParse(p?.ToString(), out var pos) ? pos : -1;
bool showWaiting = data.TryGetValue("showWaiting", out var sw) && (sw is bool b ? b : (sw != null && bool.TryParse(sw.ToString(), out var bv) && bv));
WebSocketReponseList.SeatChange(seatType, userId, position, showWaiting);
}
/// <summary>
/// 房主控制权移交 ChangeControllerUser。
/// 仅维护 SitPosData.roomOwnerSit,不再设置 fangzhu(鱼创建已由服务端统一出鱼)。
/// </summary>
public static void ChangeControllerUser(string msg)
{
var userId = JsonConvert.DeserializeObject<WebSocketResponseBase<string>>(msg).arguments[0];
if (userId == GameManager.Instance.user.id)
{
// 本地玩家成为房主:同步本地房主引用
if (SitPosData.localPlayerSit != null)
SitPosData.roomOwnerSit = SitPosData.localPlayerSit;
}
else if (userId != null && userId.StartsWith("bot_"))
{
// 房主是人机:人机无法真正发包,由本地首个非人机玩家代行房主显示职责
SitPosData.TransferRoomOwner();
}
// 其他真人玩家成为房主时无需本地操作(roomOwnerSit 由 TransferRoomOwner/ShowPlayer 维护)
}
public static void LeaveRoomResult(string msg)
{
//WebSocketManager.Instance.isGame = false;
//GameManager.Instance.StartGame();RoomChangeResponse
var response = JsonConvert.DeserializeObject<WebSocketResponseBase<string>>(msg);
// ⭐ 数据保护:arguments 为空直接返回,避免抛异常
if (response?.arguments == null || response.arguments.Count == 0)
{
#if UNITY_EDITOR
Debug.LogWarning("[LeaveRoomResult] arguments 为空,忽略");
#endif
return;
}
var userid = response.arguments[0];
switch (GameManager.Instance.gameData.currGameType)
{
case GameType.JQQD:
break;
case GameType.defaultPanel:
break;
case GameType.FLZY:
{
var panel = UIManager.Instance.Get<GameMainPanel_FLZY>(UIPanel.MainGamePanelFLZY);
if (panel == null || panel.currSitData == null) break;
if (panel.currSitData.user == userid)
{
ExitFishingGame(UIPanel.MainGamePanelFLZY);
break;
}
foreach (var item in panel.list_Sits)
{
if (item.user == userid)
{
item.ShowWaiting();
}
}
}
break;
case GameType.YYSC:
{
var panel = UIManager.Instance.Get<GameMainPanel_YYSC>(UIPanel.MainGamePanelYYSC);
if (panel == null || panel.currSitData == null) break;
if (panel.currSitData.user == userid)
{
ExitFishingGame(UIPanel.MainGamePanelYYSC);
break;
}
foreach (var item in panel.list_Sits)
{
if (item.user == userid)
{
item.ShowWaiting();
}
}
}
break;
case GameType.ZGXQ:
break;
case GameType.XJMH:
{
var panel = UIManager.Instance.Get<GameMainPanel_XJMH>(UIPanel.MainGamePanelXJMH);
if (panel == null || panel.currSitData == null) break;
if (panel.currSitData.user == userid)
{
ExitFishingGame(UIPanel.MainGamePanelXJMH);
break;
}
foreach (var item in panel.list_Sits)
{
Debug.Log(item.user + "---" + userid);
if (item.user == userid)
{
Debug.Log("推出星际迷航");
item.ShowWaiting();
}
}
}
break;
case GameType.HHWK:
{
var panel = UIManager.Instance.Get<GameMainPanel_HHWK>(UIPanel.MainGamePanelHHWK);
if (panel == null || panel.currSitData == null) break;
if (panel.currSitData.user == userid)
{
ExitFishingGame(UIPanel.MainGamePanelHHWK);
break;
}
foreach (var item in panel.list_Sits)
{
if (item.user == userid)
{
item.ShowWaiting();
}
}
}
break;
default:
break;
}
}
/// <summary>
/// 统一退出捕鱼游戏流程:
/// 1. 关闭 game socket(自己的离房通知已收到,可以正常关闭)
/// 2. 拉取最新用户数据并触发 OnUserDataChanged 事件,同步大厅金币/钻石/等级等 UI
/// 3. 通知游戏面板 socket 已返回 → 解除进度条 0.9 的等待 → 走完剩余进度后自动切回 home
/// </summary>
private static void ExitFishingGame(UIPanel gamePanel)
{
// 标记游戏已结束
WebSocketManager.Instance.isGame = false;
// ⭐ 记录金币流水:退出房间
GameManager.LogGoldFlow("ExitRoom", $"退出房间 房间={GameManager.Instance.gameData?.currRoomData?.roomId}");
// 关闭游戏连接
WebSocketManager.Instance.Close(SocketType.game);
// 拉取最新用户数据,触发 UI 同步更新(金币/钻石/等级/VIP等)
APIRequestList.GetUser((user) =>
{
GameManager.Instance.UpdataUserData(user);
});
// ⭐ 通知面板 socket 已返回 → 进度条从 0.9 走到 1.0 → 切回 home
var panel = UIManager.Instance.Get<GameMainPanel_FishBase>(gamePanel);
if (panel != null)
{
panel.OnLeaveRoomResultReceived(gamePanel);
}
else
{
// 兜底:拿不到面板直接切换
UIManager.Instance.ShowAsync(UIPanel.HomeMainPanel, gamePanel);
}
}
// ---------------- LoginHub ----------------
/// <summary>
/// LoginHub Broadcast — 大厅公告广播
/// </summary>
public static void LoginBroadcast(string msg)
{
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<LoginBroadcastData>>(msg).arguments[0];
UIManager.Instance.ShowBoardGongGao(data.message);
}
/// <summary>
/// LoginHub AssetUpdated — 资产变动通知
/// </summary>
public static void AssetUpdated(string msg)
{
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<LoginAssetData>>(msg).arguments[0];
// 触发用户数据刷新
var userId = GameManager.Instance?.user?.id;
if (!string.IsNullOrEmpty(userId))
APIRequestList.GetUser(userId, a => GameManager.Instance.UpdataUserData(a));
}
/// <summary>
/// LoginHub BackpackUpdated — 背包变动通知
/// </summary>
public static void BackpackUpdated(string msg)
{
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<LoginBackpackData>>(msg).arguments[0];
// 刷新用户数据(道具列表在 GetUser 返回中)
var userId = GameManager.Instance?.user?.id;
if (!string.IsNullOrEmpty(userId))
APIRequestList.GetUser(userId, a => GameManager.Instance.UpdataUserData(a));
}
/// <summary>
/// LoginHub NewEmail — 新邮件通知
/// </summary>
public static void NewEmail(string msg)
{
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<LoginNewEmailData>>(msg).arguments[0];
UIManager.Instance.ShowBoardError($"收到新邮件:{data.title}");
}
/// <summary>
/// LoginHub KillAnnouncement — 全局击杀公告(特殊 Boss 击杀全服推送)
/// </summary>
public static void KillAnnouncement(string msg)
{
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<KillAnnouncementData>>(msg).arguments[0];
// 服务端已拼好 text(格式:恭喜玩家*****在风雷之渊 高级场捕获 人鱼公主*2000倍 150000000大奖)
if (data != null && !string.IsNullOrEmpty(data.text))
UIManager.Instance.ShowBoardGongGao(data.text);
}
/// <summary>
/// LoginHub LevelChanged — 后台修改玩家等级
/// 直接调用 GetUser 刷新用户数据,触发 OnUserDataChanged 同步 UI
/// </summary>
public static void LevelChanged(string msg)
{
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<LevelChangedData>>(msg).arguments[0];
var userId = GameManager.Instance?.user?.id;
if (!string.IsNullOrEmpty(userId))
APIRequestList.GetUser(userId, a => GameManager.Instance.UpdataUserData(a));
}
// ---------------- LoginHub 断线房间 ----------------
/// <summary>当前是否有待处理的断线房间弹窗(防止重复弹)</summary>
private static bool _pendingRoomActive;
/// <summary>
/// LoginHub PendingRoomNotice — 登录后检测到有未完成的断线游戏
/// 弹窗提示用户选择"返回房间"(调 Reconnect)或"离开"(调 DismissPendingRoom)
/// 60 秒无操作自动离开
/// </summary>
public static void PendingRoomNotice(string msg)
{
if (_pendingRoomActive) return; // 防重复
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<PendingRoomData>>(msg).arguments[0];
if (data == null) return;
_pendingRoomActive = true;
var userId = GameManager.Instance?.user?.id;
if (string.IsNullOrEmpty(userId)) return;
// 创建 TiShi 弹窗(直接 Instantiate 拿引用,方便 60 秒后销毁)
var prefab = UIManager.Instance.TiShiParefab;
if (prefab == null)
{
Debug.LogWarning("[PendingRoomNotice] TiShiParefab 未赋值,自动离开");
WebSocketSendList.DismissPendingRoom(userId);
_pendingRoomActive = false;
return;
}
var obj = UnityEngine.Object.Instantiate(prefab, UIManager.Instance.uiRoot);
obj.transform.SetAsLastSibling();
var tishi = obj.GetComponent<TiShi>();
if (tishi == null)
{
UnityEngine.Object.Destroy(obj);
_pendingRoomActive = false;
return;
}
// 标记用户是否已操作
bool acted = false;
// 确认 = 返回房间
tishi.Init($"检测到您在 {data.gameType} {data.roomName} 有未完成的游戏,是否返回房间?", () =>
{
acted = true;
WebSocketSendList.Reconnect(userId);
});
// 关闭 = 离开(额外给 btn_Close 加监听;TiShi.Close 本身只销毁不触发 action)
if (tishi.btn_Close != null)
{
tishi.btn_Close.onClick.AddListener(() =>
{
if (acted) return;
acted = true;
WebSocketSendList.DismissPendingRoom(userId);
});
}
// 60 秒无操作自动离开
WebSocketManager.Instance.StartCoroutine(PendingRoomCountdown(
60f, () => acted, () =>
{
acted = true;
WebSocketSendList.DismissPendingRoom(userId);
if (obj != null) UnityEngine.Object.Destroy(obj);
_pendingRoomActive = false;
}));
}
/// <summary>断线房间弹窗倒计时协程(到时检查是否已操作,未操作则触发回调)</summary>
private static System.Collections.IEnumerator PendingRoomCountdown(
float seconds, System.Func<bool> hasActed, System.Action onTimeout)
{
yield return new UnityEngine.WaitForSeconds(seconds);
if (!hasActed())
onTimeout?.Invoke();
}
/// <summary>
/// LoginHub PendingRoomNotice 数据
/// </summary>
[Serializable]
public class PendingRoomData
{
public string roomId;
public string gameType;
public string roomName;
}
/// <summary>
/// LoginHub AccountKicked — 账号被顶通知
/// 该账号在其他设备登录时,旧连接收到此事件。
/// 前端应提示"账号已在其他地方登录"并清空房间 UI、断开游戏连接。
/// </summary>
public static void AccountKicked(string msg)
{
try
{
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<AccountKickedData>>(msg).arguments[0];
var userId = GameManager.Instance?.user?.id;
// 只处理当前登录用户被顶的情况
if (data != null && !string.IsNullOrEmpty(data.userId)
&& !string.IsNullOrEmpty(userId) && data.userId == userId)
{
UIManager.Instance.ShowTiShi("账号已在其他地方登录", () =>
{
GameManager.Instance.Logout();
return;
}, showClose: false);
}
}
catch (Exception e)
{
Debug.LogWarning("[AccountKicked] 解析失败: " + e.Message);
}
}
/// <summary>
/// LoginHub AccountKicked 数据
/// </summary>
[Serializable]
public class AccountKickedData
{
public string userId;
}
/// <summary>
/// LoginHub ChangeConfig — 后台活动配置或游戏配置保存发布后广播
/// changeType: 1=活动配置更新 2=游戏配置更新
/// 前端按 changeType 重新请求对应配置
/// </summary>
public static void ChangeConfig(string msg)
{
try
{
var changeType = JsonConvert.DeserializeObject<WebSocketResponseBase<int>>(msg).arguments[0];
switch (changeType)
{
case 1:
// 活动配置更新
APIRequestList.GetActivityConfig(res =>
{
if (res != null) GameManager.Instance.activityConfig = res;
});
break;
case 2:
// 游戏配置更新
APIManager.Response<GameConfig>(APIType.GameConfig_Get, res =>
{
if (res != null)
{
GameManager.Instance.gameConfig = res;
var v = GameManager.Instance.dic_fishTypeConfigs;
if (v != null)
{
v.Clear();
foreach (var item in res.fishTypeConfigs)
v[item.fishNumber] = item;
}
}
});
break;
}
}
catch (Exception e)
{
Debug.LogWarning("[ChangeConfig] 解析失败: " + e.Message);
}
}
/// <summary>
/// LoginHub KickToMainPage — 强制退出到主页面
/// 当前账号连接已失效(被顶号/用户不匹配),前端应立即清除所有游戏 UI 并导航到主页面/登录页
/// </summary>
public static void KickToMainPage(string msg)
{
try
{
var data = JsonConvert.DeserializeObject<WebSocketResponseBase<KickToMainPageData>>(msg).arguments[0];
var userId = GameManager.Instance?.user?.id;
// 只处理当前登录用户被踢的情况
if (data != null && !string.IsNullOrEmpty(data.userId)
&& !string.IsNullOrEmpty(userId) && data.userId != userId)
return;
}
catch (Exception e)
{
Debug.LogWarning("[KickToMainPage] 解析失败: " + e.Message);
}
UIManager.Instance.ShowTiShi("连接已失效,请重新登录", () =>
{
GameManager.Instance.Logout();
}, showClose: false);
}
/// <summary>
/// LoginHub KickToMainPage 数据
/// </summary>
[Serializable]
public class KickToMainPageData
{
public string userId;
}
// ---------------- FishGameHub 补充 ----------------
/// <summary>
/// FishGameHub BotLeaveRoomNotice — 机器人退出房间
/// 仅触发对应座位显示等待,不影响本地玩家(自己退出走 LeaveRoomResult)
/// </summary>
public static void BotLeaveRoom(string botUserId)
{
if (string.IsNullOrEmpty(botUserId)) return;
switch (GameManager.Instance.gameData.currGameType)
{
case GameType.FLZY:
ReleaseBotSit(UIPanel.MainGamePanelFLZY, botUserId);
break;
case GameType.YYSC:
ReleaseBotSit(UIPanel.MainGamePanelYYSC, botUserId);
break;
case GameType.XJMH:
ReleaseBotSit(UIPanel.MainGamePanelXJMH, botUserId);
break;
case GameType.HHWK:
ReleaseBotSit(UIPanel.MainGamePanelHHWK, botUserId);
break;
default:
break;
}
}
/// <summary>释放机器人座位(显示等待界面,标记为 bot)</summary>
private static void ReleaseBotSit(UIPanel panelName, string botUserId)
{
var panel = UIManager.Instance.Get<GameMainPanel_FishBase>(panelName);
if (panel == null) return;
foreach (var sit in panel.list_Sits)
{
if (sit.user == botUserId)
{
sit.isBot = true;
sit.ShowWaiting();
break;
}
}
}
/// <summary>
/// FishGameHub HostDisconnect — 房主断线
/// 其他玩家可接受机器人控制权。本地房主引用若已断线则转移给本地首个非人机玩家。
/// </summary>
public static void HostDisconnect()
{
// 房主断线 → 触发房主转移(SitPosData 已封装好转移逻辑)
SitPosData.TransferRoomOwner();
}
/// <summary>
/// FishGameHub OnSeatChange — 座位变更
/// { type, userId, position, showWaiting? }
/// type 字段后端未明确定义,先按 showWaiting 决定显示状态
/// </summary>
public static void SeatChange(int seatType, string userId, int position, bool showWaiting)
{
if (string.IsNullOrEmpty(userId) || position < 0) return;
GameMainPanel_FishBase panel = null;
switch (GameManager.Instance.gameData.currGameType)
{
case GameType.FLZY:
panel = UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelFLZY);
break;
case GameType.YYSC:
panel = UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelYYSC);
break;
case GameType.XJMH:
panel = UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelXJMH);
break;
case GameType.HHWK:
panel = UIManager.Instance.Get<GameMainPanel_FishBase>(UIPanel.MainGamePanelHHWK);
break;
default:
break;
}
if (panel == null) return;
var targetSit = panel.list_Sits.Find(s => s.posID == position);
if (targetSit == null) return;
if (showWaiting)
{
// 显示等待界面(玩家离开该座位)
targetSit.ShowWaiting();
}
else
{
// 玩家进入该座位
targetSit.isBot = !string.IsNullOrEmpty(userId) && userId.StartsWith("bot_");
}
}
/// <summary>
/// FishRoomHub RoomChange — 房间变化通知
/// 房间列表界面收到时刷新显示。当前仅在房间列表 UI 开启时更新数据,
/// 具体刷新逻辑由 UIHome_BY_SelectRoomLevelPanel / 手动选座面板按需实现。
/// </summary>
public static void RoomChangeNotice(string msg)
{
try
{
var roomData = JsonConvert.DeserializeObject<WebSocketResponseBase<RoomChangeResponse>>(msg).arguments[0];
if (roomData == null) return;
// 通知手动选座面板刷新(若开启)
var homeMain = UIManager.Instance.Get<UIHomeMain>(UIPanel.HomeMainPanel);
if (homeMain == null) return;
var selectRoomPanel = homeMain.subUIManager.Get<UIHome_BY_SelectRoomLevelPanel>(UIPanel.SelectRoomLeavePenel);
if (selectRoomPanel != null && selectRoomPanel.isMySelectRoom)
{
selectRoomPanel.panel_UIHome_BY_MySelectRoomPenel?.OnRoomChange(roomData);
}
}
catch (Exception e)
{
Debug.LogWarning("[RoomChangeNotice] 解析失败: " + e.Message);
}
}
/// <summary>
/// LoginHub LevelChanged 数据(字段名对齐后端大写首字母)
/// </summary>
[Serializable]
public class LevelChangedData
{
public int NewLevel;
public long NewExp;
public long NewGold;
public int NewDiamond;
public string Source;
public string Remark;
}
// ---------------- Customer ----------------
/// <summary>
/// 发起会话回调(用户端)
/// isOk=false 时弹错误提示;isOk=true 时自动加入会话房间
/// </summary>
public static void Customer_OnConversationStarted(CustomerConversationStartedResult result)
{
if (!result.isOk)
{
UIManager.Instance.ShowBoardError(result.msg);
return;
}
// 加入会话房间,之后才能收发消息
//WebSocketSendList.Customer_JoinConversation(result.conversation.id);
// TODO: 通知客服 UI 显示会话(替换为你的实际 Panel 调用)
UIManager.Instance.Get<UIHomeMain>(UIPanel.HomeMainPanel).subUIManager.Get<UIHome_BY_ServicePanel>(UIPanel.ServicePanel)?.OnConversationStarted(result);
}
/// <summary>
/// 消息接收回调(用户端 & 客服端)
/// </summary>
public static void Customer_OnMessageReceived(CustomerMessageReceived msg)
{
// TODO: 通知客服 UI 追加消息(替换为你的实际 Panel 调用)
UIManager.Instance.Get<UIHomeMain>(UIPanel.HomeMainPanel).subUIManager.Get<UIHome_BY_ServicePanel>(UIPanel.ServicePanel)?.OnMessageReceived(msg);
Debug.Log($"[Customer] 收到消息 from {msg.senderName}({msg.senderType}): {msg.content}");
}
/// <summary>
/// 新会话通知(客服端)
/// </summary>
public static void Customer_OnNewConversation(CustomerNewConversationNotice notice)
{
// 客服端自动加入会话房间
WebSocketSendList.Customer_JoinConversation(notice.conversationId);
// TODO: 通知客服端 UI 显示新会话(替换为你的实际 Panel 调用)
// UIManager.Instance.Get<UICustomerServicePanel>(UIPanel.CustomerServicePanel)?.OnNewConversation(notice);
Debug.Log($"[Customer] 新会话来了,userId={notice.userId},userName={notice.userName}");
}
/// <summary>
/// LoginHub Broadcast 数据
/// </summary>
[Serializable]
public class LoginBroadcastData
{
public string userName;
public string avatarUrl;
public int level;
public int vipLevel;
public string message;
public long time;
}
/// <summary>
/// LoginHub KillAnnouncement 数据(全局击杀公告)
/// </summary>
[Serializable]
public class KillAnnouncementData
{
/// <summary>公告文本(服务端已拼接:恭喜玩家*****在风雷之渊 高级场捕获 人鱼公主*2000倍 150000000大奖)</summary>
public string text;
/// <summary>玩家名(已脱敏,全部替换为*)</summary>
public string userName;
/// <summary>游戏类型名(如:风雷之渊)</summary>
public string gameTypeName;
/// <summary>房间名(如:高级场)</summary>
public string roomName;
/// <summary>鱼名称(如:人鱼公主)</summary>
public string fishName;
/// <summary>炮台倍数</summary>
public int multiple;
/// <summary>获得金币</summary>
public long gold;
/// <summary>时间戳(Unix秒)</summary>
public long time;
}
/// <summary>
/// LoginHub AssetUpdated 数据
/// </summary>
[Serializable]
public class LoginAssetData
{
public int currencyType;
public int changeType;
public long amount;
public long newGold;
public int newDiamond;
public string source;
public string remark;
}
/// <summary>
/// LoginHub BackpackUpdated 数据
/// </summary>
[Serializable]
public class LoginBackpackData
{
public string action;
public string propNumber;
public int count;
public List<object> props;
}
/// <summary>
/// LoginHub NewEmail 数据
/// </summary>
[Serializable]
public class LoginNewEmailData
{
public string title;
public string emailId;
}
/// <summary>
/// FishGameHub Exception 数据({code, message})
/// code: 0=未知错误
/// 1001=房间已满 1002=金币不足 1003=房间不存在 1004=玩家不在房间
/// 1005=签名错误 1006=请求已过期 1007=鱼配置不存在 1008=子弹参数无效
/// 1009=金币参数超出范围 1011=用户不存在 1012=道具配置不存在
/// 1013=道具数量不足 1014=钻石不足 1015=炮倍参数无效
/// </summary>
[Serializable]
public class FishGameExceptionData
{
public int code;
public string message;
}
}
}
// end namespace