759 lines
36 KiB
C#
759 lines
36 KiB
C#
using Logic;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using Logic.Action;
|
||
using Logic.AI;
|
||
using Logic.CrashSight;
|
||
using RuntimeData;
|
||
using TH1_Core.Events;
|
||
using TH1_Core.Managers;
|
||
using TH1_Logic.Config;
|
||
using TH1_Logic.Core;
|
||
using TH1_Logic.Net;
|
||
using UnityEngine;
|
||
|
||
namespace TH1_Logic.Steam
|
||
{
|
||
public class GameNetReceiver
|
||
{
|
||
public static GameNetReceiver Instance { get; } = new GameNetReceiver();
|
||
|
||
|
||
public void OnMessageReceived(byte[] data)
|
||
{
|
||
try
|
||
{
|
||
var message = MemoryPack.MemoryPackSerializer.Deserialize<BaseMessage>(data);
|
||
if (message == null) return;
|
||
if (message.MessageType == P2PMsgType.NetworkStress) return;
|
||
|
||
if (message.MessageType == P2PMsgType.String) OnReceivedString((StringMessage)message);
|
||
if (message.MessageType == P2PMsgType.GameStart) OnReceivedGameStart((GameStartMessage)message);
|
||
if (message.MessageType == P2PMsgType.ActionConfirm) OnReceivedActionConfirm((ActionConfirmMessage)message);
|
||
if (message.MessageType == P2PMsgType.ActionExcute) OnReceivedActionExcute((ActionExcuteMessage)message);
|
||
if (message.MessageType == P2PMsgType.TurnEnd) OnReceivedTurnEnd((TurnEndMessage)message);
|
||
if (message.MessageType == P2PMsgType.MapConfirm) OnReceivedMapConfirm((MapConfirmMessage)message);
|
||
if (message.MessageType == P2PMsgType.ForceUpdate) OnReceivedForceUpdate((ForceUpdateMessage)message);
|
||
if (message.MessageType == P2PMsgType.ChangeCiv) OnReceivedChangeCiv((ChangeCivMessage)message);
|
||
if (message.MessageType == P2PMsgType.UpdateLobbyData) OnReceivedUpdateLobbyData((UpdateLobbyDataMessage)message);
|
||
if (message.MessageType == P2PMsgType.RequestLobbyData) OnReceivedRequestLobbyData((RequestLobbyDataMessage)message);
|
||
if (message.MessageType == P2PMsgType.Heartbeat) OnReceivedHeartbeat((HeartbeatMessage)message);
|
||
if (message.MessageType == P2PMsgType.MemberStateSync) OnReceivedMemberStateSync((MemberStateSyncMessage)message);
|
||
if (message.MessageType == P2PMsgType.RequestForceUpdate) OnReceivedRequestForceUpdate((RequestForceUpdateMessage)message);
|
||
if (message.MessageType == P2PMsgType.HeartbeatReply) OnReceivedHeartbeatReply((HeartbeatReplyMessage)message);
|
||
if (message.MessageType == P2PMsgType.ChatMessage) OnReceivedChatMessage((ChatMessage)message);
|
||
if (message.MessageType == P2PMsgType.InviteMessage) OnReceivedInviteMessage((InviteMessage)message);
|
||
if (message.MessageType == P2PMsgType.LobbyReport) OnReceivedLobbyReport((LobbyReportMessage)message);
|
||
if (message.MessageType == P2PMsgType.MapDataDebug) OnReceivedMapDataDebug((MapDataDebugMessage)message);
|
||
}
|
||
catch (System.Exception e)
|
||
{
|
||
LogSystem.LogError($"OnMessageReceived 处理失败, bytes: {data?.Length ?? 0}, error: {e}");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
}
|
||
}
|
||
|
||
// 基础字符串消息
|
||
private void OnReceivedString(StringMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedString");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
|
||
if (string.IsNullOrEmpty(message.Content)) return;
|
||
LogSystem.LogInfo($"收到消息 : {message.Content}");
|
||
}
|
||
|
||
// 只有玩家会收到
|
||
private void OnReceivedGameStart(GameStartMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedGameStart");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
|
||
if (!IsValidIncomingMultiMap(message.MapData))
|
||
{
|
||
LogSystem.LogError("OnReceivedGameStart 收到无效多人地图数据");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.InvalidNetworkMapData);
|
||
return;
|
||
}
|
||
if (LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
|
||
LogSystem.LogWarning($"OnReceivedGameStart : {NetData.GetMapDataHash(message.MapData)}");
|
||
|
||
//客户端开启游戏
|
||
//如果loading失败 直接开始游戏并关闭当前页面
|
||
UIManager.Instance.ShowLoading();
|
||
// 只有真正进局成功后才关闭多人面板;失败时保留房间 UI 方便重试或提示。
|
||
var started = false;
|
||
try
|
||
{
|
||
started = Main.Instance.NetStartGame(message.MapData);
|
||
}
|
||
catch (System.Exception e)
|
||
{
|
||
LogSystem.LogError($"[GameNetReceiver] NetStartGame threw, fallback closing multiplay UI: {e}");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.GameStartFailed);
|
||
}
|
||
finally
|
||
{
|
||
if (started) EventManager.Publish(new HideUIOutsideMultiplay(){});
|
||
else NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.GameStartFailed);
|
||
}
|
||
}
|
||
|
||
// 只有房主会收到
|
||
private void OnReceivedActionConfirm(ActionConfirmMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedActionConfirm");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
|
||
if (!LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
|
||
if (message.ActionData == null)
|
||
{
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
if (message.ActionData.Version != Main.MapData.Net.GetActionVersion())
|
||
{
|
||
LogSystem.LogError($"OnReceivedActionConfirm Version 不一致");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ActionSyncMismatch);
|
||
return;
|
||
}
|
||
if (NetData.GetMapDataHash(Main.MapData) != message.ActionData.MapHash)
|
||
{
|
||
LogSystem.LogError($"OnReceivedActionConfirm MapHash 不一致,拒绝执行");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.MapSyncMismatch);
|
||
return;
|
||
}
|
||
|
||
message.ActionData.Param.MapData = Main.MapData;
|
||
message.ActionData.Param.RefreshParams();
|
||
|
||
if (message.ActionData.ActionId.ActionType != CommonActionType.TurnStart
|
||
&& message.ActionData.ActionId.ActionType != CommonActionType.PlayerSurrender
|
||
&& Main.MapData.CurPlayer != null && Main.MapData.CurPlayer.Id != message.ActionData.Param.PlayerId)
|
||
{
|
||
LogSystem.LogError($"OnReceivedActionConfirm Player 不一致");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ActionSyncMismatch);
|
||
return;
|
||
}
|
||
|
||
var action = ActionLogicFactory.GetActionLogic(message.ActionData.ActionId);
|
||
if (action == null)
|
||
{
|
||
LogSystem.LogError($"OnReceivedActionConfirm 找不到对应 Action");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ActionSyncMismatch);
|
||
return;
|
||
}
|
||
action.CompleteExecute(message.ActionData.Param);
|
||
}
|
||
|
||
// 只有玩家会收到
|
||
private void OnReceivedActionExcute(ActionExcuteMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedActionExcute");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
if (LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
|
||
if (message.ActionData == null)
|
||
{
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
if (Main.MapData == null) return;
|
||
// 这里感觉可以不用了,游戏内心跳包会校验这种情况,先放着
|
||
if (message.ActionData.Version > Main.MapData.Net.GetActionVersion())
|
||
{
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ActionSyncMismatch);
|
||
GameNetSender.Instance.SendRequestForceUpdate();
|
||
return;
|
||
}
|
||
if (message.ActionData.Version != Main.MapData.Net.GetActionVersion())
|
||
{
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ActionSyncMismatch);
|
||
return;
|
||
}
|
||
|
||
if (NetData.GetMapDataHash(Main.MapData) != message.ActionData.MapHash)
|
||
{
|
||
LogSystem.LogError($"OnReceivedActionExcute MapHash 不一致,拒绝执行");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.MapSyncMismatch);
|
||
if (message.Map != null) Main.MapData.FindDifferences(message.Map);
|
||
return;
|
||
}
|
||
|
||
message.ActionData.Param.MapData = Main.MapData;
|
||
message.ActionData.Param.RefreshParams();
|
||
if (message.ActionData.ActionId.ActionType != CommonActionType.TurnStart
|
||
&& message.ActionData.ActionId.ActionType != CommonActionType.PlayerSurrender
|
||
&& Main.MapData.CurPlayer != null && Main.MapData.CurPlayer.Id != message.ActionData.Param.PlayerId)
|
||
{
|
||
LogSystem.LogError($"OnReceivedActionExcute Player 不一致");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ActionSyncMismatch);
|
||
return;
|
||
}
|
||
var action = ActionLogicFactory.GetActionLogic(message.ActionData.ActionId);
|
||
if (action == null)
|
||
{
|
||
LogSystem.LogError($"OnReceivedActionConfirm 找不到对应 Action");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ActionSyncMismatch);
|
||
return;
|
||
}
|
||
action.NetCompleteExecute(message.ActionData.Param);
|
||
}
|
||
|
||
// 只有房主会收到
|
||
private void OnReceivedTurnEnd(TurnEndMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedTurnEnd");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
if (!LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
|
||
Main.PlayerLogic.EndPlayerTurn(Main.MapData, message.PlayerId);
|
||
}
|
||
|
||
// 房主或者成员收到心跳校验
|
||
private void OnReceivedMapConfirm(MapConfirmMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedMapConfirm");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
|
||
if (Main.MapData == null) return;
|
||
if (Main.MapData.Net?.Actions == null) return;
|
||
// 房主收到校验信息时校验状态
|
||
if (LobbyManager.Instance.Lobby.IsLobbyOwner())
|
||
{
|
||
if (Main.MapData.Net.Actions.Count == 0 || message.Index == 0) return;
|
||
var confirm = Main.Instance.ConfirmMap.GetPlayerConfirm(message.MemberId);
|
||
if (message.Index > Main.MapData.Net.Actions.Count)
|
||
{
|
||
LogSystem.LogError($"房主端:message.Index > Main.MapData.Net.Actions.Count");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.MapSyncMismatch);
|
||
confirm.State = MemberNetState.Error;
|
||
}
|
||
else if (message.Index < Main.MapData.Net.Actions.Count)
|
||
{
|
||
if (Time.time - Main.MapData.Net.Actions[^1].Time > 8f)
|
||
{
|
||
LogSystem.LogError($"房主端:message.Index < Main.MapData.Net.Actions.Count");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.MapSyncMismatch);
|
||
confirm.State = MemberNetState.Error;
|
||
}
|
||
}
|
||
else if (message.ActionData == null)
|
||
{
|
||
LogSystem.LogError($"房主端:message.ActionData == null");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.MapSyncMismatch);
|
||
confirm.State = MemberNetState.Error;
|
||
}
|
||
else if (!Main.MapData.Net.Actions[message.Index - 1].IsEqual(message.ActionData))
|
||
{
|
||
LogSystem.LogError($"房主端:!Main.MapData.Net.Actions[message.Index - 1].IsEqual(message.ActionData)");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.MapSyncMismatch);
|
||
confirm.State = MemberNetState.Error;
|
||
}
|
||
else
|
||
{
|
||
confirm.State = MemberNetState.OK;
|
||
}
|
||
}
|
||
// 成员收到校验信息时校验状态
|
||
else
|
||
{
|
||
if (Main.MapData.Net.Actions.Count == 0 || message.Index == 0) return;
|
||
if (message.Index > Main.MapData.Net.Actions.Count)
|
||
{
|
||
LogSystem.LogError($"成员端: message.Index > Main.MapData.Net.Actions.Count");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.MapSyncMismatch);
|
||
GameNetSender.Instance.SendRequestForceUpdate();
|
||
}
|
||
else if (message.ActionData == null)
|
||
{
|
||
LogSystem.LogError($"成员端: message.ActionData == null");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.MapSyncMismatch);
|
||
GameNetSender.Instance.SendRequestForceUpdate();
|
||
}
|
||
else if (!Main.MapData.Net.Actions[message.Index - 1].IsEqual(message.ActionData))
|
||
{
|
||
LogSystem.LogError($"成员端: !Main.MapData.Net.Actions[message.Index - 1].IsEqual(message.ActionData)");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.MapSyncMismatch);
|
||
GameNetSender.Instance.SendRequestForceUpdate();
|
||
}
|
||
}
|
||
}
|
||
|
||
// 只有玩家会收到
|
||
private void OnReceivedForceUpdate(ForceUpdateMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedForceUpdate");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
if (LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
|
||
if (!IsValidIncomingMultiMap(message.MapData))
|
||
{
|
||
LogSystem.LogError("OnReceivedForceUpdate 收到无效多人地图数据");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.InvalidNetworkMapData);
|
||
return;
|
||
}
|
||
try
|
||
{
|
||
if (!message.MapData.Net.RefreshPlayerNet(message.MapData))
|
||
{
|
||
LogSystem.LogError("OnReceivedForceUpdate 玩家网络映射失败");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkPlayerMappingFailed);
|
||
return;
|
||
}
|
||
}
|
||
catch (System.Exception e)
|
||
{
|
||
LogSystem.LogError($"OnReceivedForceUpdate RefreshPlayerNet failed: {e}");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkPlayerMappingFailed);
|
||
return;
|
||
}
|
||
var confirm = Main.Instance.ConfirmMap.GetPlayerConfirm(LobbyManager.Instance.Lobby.GetSelfMemberId());
|
||
LogSystem.LogError($"触发断线重连, 触发原因: {confirm?.State}");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ReconnectStarted);
|
||
|
||
try
|
||
{
|
||
Main.MapData?.FindDifferences(message.MapData);
|
||
}
|
||
catch (System.Exception e)
|
||
{
|
||
LogSystem.LogError($"OnReceivedForceUpdate FindDifferences failed: {e}");
|
||
}
|
||
|
||
var previousState = Main.Instance.GameLogic.GetCurState();
|
||
Main.Instance.GameLogic.ChangeState(GameState.ForceUpdating);
|
||
try
|
||
{
|
||
if (!Main.Instance.NetResumeMatch(message.MapData))
|
||
{
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ReconnectFailed);
|
||
Main.Instance.GameLogic.ChangeState(previousState);
|
||
return;
|
||
}
|
||
}
|
||
catch (System.Exception e)
|
||
{
|
||
LogSystem.LogError($"OnReceivedForceUpdate NetResumeMatch failed: {e}");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ReconnectFailed);
|
||
Main.Instance.GameLogic.ChangeState(previousState);
|
||
return;
|
||
}
|
||
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ReconnectSucceeded);
|
||
EventManager.Publish(new HideUIOutsideAll());
|
||
EventManager.Publish(new HideUIOutsideMultiplay());
|
||
Timer.Instance.TimerRegister(this, () =>
|
||
{
|
||
//TODO Hint重做
|
||
//UIManager.Instance.GameUI.NetHint.SetActive(false);
|
||
},2f,"NetHint Wrong");
|
||
|
||
|
||
// Main.MapData = message.MapData;
|
||
}
|
||
|
||
private void OnReceivedMapDataDebug(MapDataDebugMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedMapDataDebug");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
|
||
var localMap = Main.MapData;
|
||
var remoteMap = message.MapData;
|
||
var lobby = LobbyManager.Instance.Lobby;
|
||
var selfMemberId = lobby?.GetSelfMemberId() ?? 0;
|
||
var selfRole = lobby != null && lobby.IsLobbyOwner() ? "房主" : "成员";
|
||
var localHash = SafeGetMapDataHash(localMap);
|
||
var remoteHash = string.IsNullOrEmpty(message.MapHash) ? SafeGetMapDataHash(remoteMap) : message.MapHash;
|
||
var localActionCount = localMap?.Net?.Actions?.Count ?? 0;
|
||
var remoteActionCount = remoteMap?.Net?.Actions?.Count ?? message.ActionIndex;
|
||
|
||
if (localMap == null || remoteMap == null)
|
||
{
|
||
LogSystem.LogError(
|
||
$"[MapDataDebug] 无法比较: localMap null={localMap == null}, remoteMap null={remoteMap == null}, " +
|
||
$"fromMember={message.SenderMemberId}, selfMember={selfMemberId}");
|
||
return;
|
||
}
|
||
|
||
var differences = BuildMapDataDebugDifferences(localMap, remoteMap);
|
||
var sb = new StringBuilder(8192);
|
||
sb.AppendLine($"[MapDataDebug] {selfRole}收到 MapData 诊断包");
|
||
sb.AppendLine($"fromMember={message.SenderMemberId}, fromPlayer={message.SenderPlayerId}, selfMember={selfMemberId}");
|
||
sb.AppendLine($"note={message.Note}");
|
||
sb.AppendLine($"localHash={localHash}, remoteHash={remoteHash}");
|
||
sb.AppendLine($"localActions={localActionCount}, remoteActions={remoteActionCount}");
|
||
if (!IsValidIncomingMultiMap(remoteMap))
|
||
sb.AppendLine("remoteMap=无效多人地图数据");
|
||
|
||
if (differences.Count == 0)
|
||
{
|
||
sb.AppendLine("result=一致");
|
||
LogSystem.LogInfo(sb.ToString());
|
||
return;
|
||
}
|
||
|
||
sb.AppendLine($"result=不一致, diffCount={differences.Count}");
|
||
AppendFirstActionMismatch(sb, localMap, remoteMap);
|
||
var logCount = Math.Min(differences.Count, 200);
|
||
for (var i = 0; i < logCount; i++)
|
||
sb.AppendLine($"diff[{i}] {differences[i]}");
|
||
if (differences.Count > logCount)
|
||
sb.AppendLine($"diff 过多,仅打印前 {logCount} 条,剩余 {differences.Count - logCount} 条");
|
||
LogSystem.LogError(sb.ToString());
|
||
}
|
||
|
||
private static List<string> BuildMapDataDebugDifferences(MapData localMap, MapData remoteMap)
|
||
{
|
||
var differences = new List<string>();
|
||
try
|
||
{
|
||
MapData.CompareComponent(localMap.MapConfig, remoteMap.MapConfig, "MapConfig", differences);
|
||
MapData.CompareComponent(localMap.GridMap, remoteMap.GridMap, "GridMap", differences);
|
||
MapData.CompareComponent(localMap.PlayerMap, remoteMap.PlayerMap, "PlayerMap", differences);
|
||
MapData.CompareComponent(localMap.PlayerMap?.PlayerDataList, remoteMap.PlayerMap?.PlayerDataList,
|
||
"PlayerMap.PlayerDataList", differences);
|
||
MapData.CompareComponent(localMap.CityMap, remoteMap.CityMap, "CityMap", differences);
|
||
MapData.CompareComponent(localMap.UnitMap, remoteMap.UnitMap, "UnitMap", differences);
|
||
MapData.CompareComponent(localMap.Net, remoteMap.Net, "Net", differences);
|
||
MapData.CompareComponent(localMap.CityToPlayerDict, remoteMap.CityToPlayerDict, "CityToPlayerDict", differences);
|
||
MapData.CompareComponent(localMap.UnitToCityDict, remoteMap.UnitToCityDict, "UnitToCityDict", differences);
|
||
MapData.CompareComponent(localMap.UnitToGridDict, remoteMap.UnitToGridDict, "UnitToGridDict", differences);
|
||
MapData.CompareComponent(localMap.CityToGridDict, remoteMap.CityToGridDict, "CityToGridDict", differences);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
differences.Add($"component comparison failed: {e.Message}");
|
||
}
|
||
|
||
try
|
||
{
|
||
differences.AddRange(MapData.FindDifferences(localMap, remoteMap));
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
differences.Add($"FindDifferences failed: {e.Message}");
|
||
}
|
||
|
||
return differences.Where(diff => !string.IsNullOrEmpty(diff)).Distinct().ToList();
|
||
}
|
||
|
||
private static void AppendFirstActionMismatch(StringBuilder sb, MapData localMap, MapData remoteMap)
|
||
{
|
||
var localActions = localMap.Net?.Actions;
|
||
var remoteActions = remoteMap.Net?.Actions;
|
||
if (localActions == null || remoteActions == null) return;
|
||
|
||
var minCount = Math.Min(localActions.Count, remoteActions.Count);
|
||
for (var i = 0; i < minCount; i++)
|
||
{
|
||
var localAction = localActions[i];
|
||
var remoteAction = remoteActions[i];
|
||
if (localAction == null && remoteAction == null) continue;
|
||
if (localAction != null && localAction.IsEqual(remoteAction)) continue;
|
||
|
||
sb.AppendLine($"firstActionMismatchIndex={i}");
|
||
sb.AppendLine($"localAction={GetActionDebugLine(localAction)}");
|
||
sb.AppendLine($"remoteAction={GetActionDebugLine(remoteAction)}");
|
||
return;
|
||
}
|
||
|
||
if (localActions.Count == remoteActions.Count) return;
|
||
sb.AppendLine($"firstActionMismatchIndex={minCount}");
|
||
sb.AppendLine($"localActionCount={localActions.Count}, remoteActionCount={remoteActions.Count}");
|
||
}
|
||
|
||
private static string GetActionDebugLine(ActionNetData action)
|
||
{
|
||
if (action == null) return "null";
|
||
var actionText = action.ActionId == null ? "null" : action.ActionId.GetStringLog().Replace("\n", " | ");
|
||
return $"version={action.Version}, mapHash={action.MapHash}, action={actionText}";
|
||
}
|
||
|
||
private static string SafeGetMapDataHash(MapData mapData)
|
||
{
|
||
if (mapData?.Net == null) return "null";
|
||
try
|
||
{
|
||
return NetData.GetMapDataHash(mapData);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
return $"hash_error:{e.Message}";
|
||
}
|
||
}
|
||
|
||
private bool IsValidIncomingMultiMap(MapData mapData)
|
||
{
|
||
return mapData?.Net != null
|
||
&& !mapData.DeserializedMissingCriticalData
|
||
&& mapData.Net.Mode == NetMode.Multi
|
||
&& mapData.MapConfig != null
|
||
&& mapData.GridMap != null
|
||
&& mapData.PlayerMap != null
|
||
&& mapData.CityMap != null
|
||
&& mapData.UnitMap != null;
|
||
}
|
||
|
||
// 成员房间配置变更(阵营/准备状态),只有房主会收到
|
||
private void OnReceivedChangeCiv(ChangeCivMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedChangeCiv");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
if (!LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
|
||
if (message.Civ == null)
|
||
{
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyMemberConfigFailed);
|
||
return;
|
||
}
|
||
if (Main.Instance.MapConfig.UpdateMemberCiv(message.Civ))
|
||
Main.Instance.MapConfig.CheckMapConfigChanged();
|
||
}
|
||
|
||
// 只有玩家会收到
|
||
private void OnReceivedUpdateLobbyData(UpdateLobbyDataMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedUpdateLobbyData");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
if (LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
|
||
if (message.Config == null)
|
||
{
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyMembersNotSynced);
|
||
return;
|
||
}
|
||
var selfMemberId = LobbyManager.Instance.Lobby.GetSelfMemberId();
|
||
var wasReady = Main.Instance.MapConfig?.IsMemberReady(selfMemberId) ?? false;
|
||
Main.Instance.MapConfig = message.Config;
|
||
var isReady = Main.Instance.MapConfig.IsMemberReady(selfMemberId);
|
||
if (wasReady && !isReady)
|
||
EventManager.Publish(new ShowUIOutsideMultiplayLobbyNotify { UINotifyCommonType = UINotifyCommonType.OutsideMultiplayRoomAutoCancelReady });
|
||
|
||
if (Main.Instance.MapConfig.HasSameLobbyMembers(LobbyManager.Instance.Lobby.GetAllMemberInfo()))
|
||
{
|
||
GameNetSender.Instance.MarkLobbyDataSyncedFromHost();
|
||
}
|
||
else
|
||
{
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyMembersNotSynced);
|
||
GameNetSender.Instance.MarkLobbyDataSyncRequired();
|
||
GameNetSender.Instance.RequestLobbyData();
|
||
}
|
||
EventManager.Publish(new UpdateUIOutsideMultiplayRoomSetting());
|
||
}
|
||
|
||
// 收到请求更新房间配置 只有房主会收到
|
||
private void OnReceivedRequestLobbyData(RequestLobbyDataMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedRequestLobbyData");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
if (!LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
|
||
if (Main.Instance.MapConfig.UpdateLobbyMember(LobbyManager.Instance.Lobby.GetAllMemberInfo()))
|
||
Main.Instance.MapConfig.CheckMapConfigChanged();
|
||
GameNetSender.Instance.SendLobbyData(Main.Instance.MapConfig, message.MemberId);
|
||
}
|
||
|
||
// 成员发过来的心跳包 只有房主会收到
|
||
private void OnReceivedHeartbeat(HeartbeatMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedHeartbeat");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
|
||
if (!LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
|
||
|
||
// 心跳回复
|
||
GameNetSender.Instance.SendHeartbeatReply(message.MemberId);
|
||
|
||
// 常规记录
|
||
var confirm = Main.Instance.ConfirmMap.GetPlayerConfirm(message.MemberId);
|
||
confirm.OnReceiveHeartbeat();
|
||
|
||
// 做一次状态错误判断
|
||
if (Main.MapData == null) return;
|
||
if (Main.MapData.Net.GameStartTime == 0 || Time.time - Main.MapData.Net.GameStartTime < 5f) return;
|
||
if (message.State != GameState.Finished && message.State != GameState.Menu) return;
|
||
|
||
var playerID = Main.MapData.Net.GetPlayerId(message.MemberId);
|
||
var player = Main.MapData.PlayerMap.GetPlayerData(playerID);
|
||
if (player == null) return;
|
||
if (!player.IsSurvival) return;
|
||
if (Main.MapData.CheckIfGameEnd(out _)) return;
|
||
confirm.State = MemberNetState.Disconnected;
|
||
GameNetSender.Instance.SendMemberStateSync();
|
||
}
|
||
|
||
// 房主心跳包 只有成员会收到 每 2s 收到一次
|
||
private void OnReceivedMemberStateSync(MemberStateSyncMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedMemberStateSync");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
|
||
// 心跳回复
|
||
GameNetSender.Instance.SendHeartbeatReply(message.MemberId);
|
||
|
||
// 判断是否游戏已经结束而我还在等待
|
||
if (Main.MapData != null && !Main.MapData.CheckIfGameEnd(out _) && message.State == GameState.Menu)
|
||
{
|
||
EventManager.Publish(new ExecuteUIBottomBottomBarQuit());
|
||
return;
|
||
}
|
||
|
||
// 判断是否需要重连
|
||
if (Main.MapData != null && Main.MapData.CheckIfGameEnd(out _)) return;
|
||
if (message.State == GameState.Finished || message.State == GameState.Menu) return;
|
||
if(!LobbyManager.Instance.Lobby.IsInLobby()) return;
|
||
if (LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
|
||
Main.Instance.ConfirmMap.UpdatePlayerConfirm(message.PlayerConfirm);
|
||
var confirm = Main.Instance.ConfirmMap.GetPlayerConfirm(LobbyManager.Instance.Lobby.GetSelfMemberId());
|
||
if (confirm.State != MemberNetState.OK)
|
||
{
|
||
// TODO 此时说明房主端我的状态有误,可选申请重连
|
||
LogSystem.LogWarning($"OnReceivedMemberStateSync 检测状态错误");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.MapSyncMismatch);
|
||
GameNetSender.Instance.SendRequestForceUpdate();
|
||
}
|
||
}
|
||
|
||
// 只有房主会收到
|
||
private void OnReceivedRequestForceUpdate(RequestForceUpdateMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedRequestForceUpdate");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
|
||
if (!LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
|
||
if (Main.MapData == null) return;
|
||
GameNetSender.Instance.ForceUpdate(message.MemberId);
|
||
}
|
||
|
||
// 心跳回复包,所有成员都能收到
|
||
private void OnReceivedHeartbeatReply(HeartbeatReplyMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedHeartbeatReply");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
|
||
var confirm = Main.Instance.ConfirmMap.GetPlayerConfirm(message.MemberId);
|
||
confirm.OnReceiveHeartbeatReply();
|
||
}
|
||
|
||
// 聊天消息(ReceiveChatItem 内部会触发 OnChatUpdated → ChatArea 自动刷新)
|
||
private void OnReceivedChatMessage(ChatMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedChatMessage");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
|
||
TH1_Logic.Chat.ChatManager.Instance.ReceiveChatItem(message.Item);
|
||
}
|
||
|
||
// 收到邀请消息
|
||
private void OnReceivedInviteMessage(InviteMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedChatMessage");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
|
||
if (LobbyManager.Instance.Lobby.IsInLobby())
|
||
{
|
||
var lobbyId = LobbyManager.Instance.Lobby.GetShareableLobbyId();
|
||
if (lobbyId == message.LobbyInfo.LobbyId) return;
|
||
}
|
||
|
||
if (message.LobbyInfo.Version != ConfigManager.Instance.VersionCfg.CurVersionInfo.Version)
|
||
{
|
||
LogSystem.LogInfo($"版本不一致!!!");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.InviteVersionMismatch);
|
||
return;
|
||
}
|
||
|
||
if (Main.Instance.GameLogic.GetCurState() != GameState.Menu)
|
||
{
|
||
EventManager.Publish(new ShowUITopInvited(){LobbyId = message.LobbyInfo.LobbyId});
|
||
}
|
||
else if (Main.Instance.GameLogic.GetCurState() == GameState.Menu)
|
||
{
|
||
EventManager.Publish(new ShowUIOutsideInvited(){LobbyId = message.LobbyInfo.LobbyId});
|
||
//LobbyManager.Instance.Lobby.JoinLobbyById(message.LobbyInfo.LobbyId);
|
||
}
|
||
|
||
}
|
||
|
||
// 房间外举报消息,只有被举报房间的房主处理
|
||
private void OnReceivedLobbyReport(LobbyReportMessage message)
|
||
{
|
||
if (message == null)
|
||
{
|
||
LogSystem.LogError($"消息解析失败: OnReceivedLobbyReport");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
|
||
return;
|
||
}
|
||
|
||
if (LobbyManager.Instance.Lobby is SteamLobbyManager steamLobby)
|
||
steamLobby.OnReceivedLobbyReport(message);
|
||
}
|
||
}
|
||
}
|