This commit is contained in:
wuwenbo 2026-05-21 17:37:38 +08:00
parent 5abe3d3b93
commit 6342e1496d
17 changed files with 596 additions and 21 deletions

View File

@ -0,0 +1,136 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3ebbbbba88fe43409c16e83b934b878c, type: 3}
m_Name: NetworkPlayerTipDataAssets
m_EditorClassIdentifier:
DefaultCooldownSeconds: 3
NetworkPlayerTipInfoList:
- TipType: 1
Title: "Steam\u8FDE\u63A5\u4E0D\u53EF\u7528"
Message: "\u672A\u68C0\u6D4B\u5230\u53EF\u7528\u7684Steam\u8FDE\u63A5\uFF0C\u8BF7\u786E\u8BA4Steam\u5DF2\u542F\u52A8\u5E76\u5DF2\u767B\u5F55\u3002"
CooldownSeconds: 5
- TipType: 2
Title: "\u9700\u8981\u767B\u5F55Steam"
Message: "\u5F53\u524DSteam\u8D26\u53F7\u672A\u767B\u5F55\uFF0C\u767B\u5F55\u540E\u518D\u5C1D\u8BD5\u8054\u673A\u3002"
CooldownSeconds: 5
- TipType: 3
Title: "Steam\u7248\u672C\u8FC7\u65E7"
Message: "Steam\u5BA2\u6237\u7AEF\u7248\u672C\u8FC7\u65E7\uFF0C\u8BF7\u5148\u5728Steam\u4E2D\u68C0\u67E5\u66F4\u65B0\u3002"
CooldownSeconds: 10
- TipType: 4
Title: "Steam\u8FDE\u63A5\u5DF2\u65AD\u5F00"
Message: "Steam\u8054\u673A\u4F1A\u8BDD\u5DF2\u65AD\u5F00\uFF0C\u5C06\u9000\u51FA\u5F53\u524D\u8054\u673A\u5BF9\u5C40\u6216\u623F\u95F4\u3002"
CooldownSeconds: 5
- TipType: 5
Title: "\u521B\u5EFA\u623F\u95F4\u5931\u8D25"
Message: "\u65E0\u6CD5\u521B\u5EFA\u8054\u673A\u623F\u95F4\uFF0C\u8BF7\u68C0\u67E5Steam\u8FDE\u63A5\u540E\u91CD\u8BD5\u3002"
CooldownSeconds: 3
- TipType: 6
Title: "\u52A0\u5165\u623F\u95F4\u5931\u8D25"
Message: "\u65E0\u6CD5\u52A0\u5165\u8BE5\u623F\u95F4\uFF0C\u623F\u95F4\u53EF\u80FD\u5DF2\u7ED3\u675F\u6216\u7F51\u7EDC\u8FDE\u63A5\u5F02\u5E38\u3002"
CooldownSeconds: 3
- TipType: 7
Title: "\u623F\u95F4\u64CD\u4F5C\u5931\u8D25"
Message: "\u5F53\u524D\u623F\u95F4\u64CD\u4F5C\u6CA1\u6709\u6210\u529F\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002"
CooldownSeconds: 2
- TipType: 8
Title: "\u4ECD\u6709\u6210\u5458\u672A\u51C6\u5907"
Message: "\u623F\u95F4\u5185\u8FD8\u6709\u6210\u5458\u672A\u51C6\u5907\uFF0C\u6240\u6709\u6210\u5458\u51C6\u5907\u540E\u624D\u80FD\u5F00\u59CB\u3002"
CooldownSeconds: 2
- TipType: 9
Title: "\u623F\u95F4\u4FE1\u606F\u540C\u6B65\u4E2D"
Message: "\u623F\u95F4\u6210\u5458\u4FE1\u606F\u5C1A\u672A\u540C\u6B65\u5B8C\u6210\uFF0C\u8BF7\u7B49\u5F85\u623F\u4E3B\u6570\u636E\u5237\u65B0\u3002"
CooldownSeconds: 3
- TipType: 10
Title: "\u623F\u95F4\u914D\u7F6E\u540C\u6B65\u5931\u8D25"
Message: "\u623F\u95F4\u6210\u5458\u6216\u9635\u8425\u914D\u7F6E\u672A\u80FD\u540C\u6B65\uFF0C\u8BF7\u91CD\u65B0\u5C1D\u8BD5\u3002"
CooldownSeconds: 3
- TipType: 11
Title: "\u623F\u4E3B\u8FDE\u63A5\u5DF2\u65AD\u5F00"
Message: "\u623F\u4E3B\u5DF2\u79BB\u5F00\u6216\u8FDE\u63A5\u4E2D\u65AD\uFF0C\u5F53\u524D\u8054\u673A\u5C06\u9000\u51FA\u3002"
CooldownSeconds: 5
- TipType: 12
Title: "\u8054\u673A\u8FDE\u63A5\u5931\u8D25"
Message: "\u65E0\u6CD5\u5EFA\u7ACBP2P\u8FDE\u63A5\uFF0C\u8BF7\u786E\u8BA4\u53CC\u65B9Steam\u5728\u7EBF\u5E76\u7A0D\u540E\u91CD\u8BD5\u3002"
CooldownSeconds: 3
- TipType: 13
Title: "\u8054\u673A\u8FDE\u63A5\u8D85\u65F6"
Message: "\u4E0E\u5BF9\u65B9\u7684P2P\u8FDE\u63A5\u8D85\u65F6\uFF0C\u53EF\u80FD\u662F\u7F51\u7EDC\u6216\u9632\u706B\u5899\u5BFC\u81F4\u3002"
CooldownSeconds: 5
- TipType: 14
Title: "\u7F51\u7EDC\u6D88\u606F\u53D1\u9001\u5931\u8D25"
Message: "\u8054\u673A\u6D88\u606F\u672A\u80FD\u9001\u8FBE\u5BF9\u65B9\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5\u3002"
CooldownSeconds: 3
- TipType: 15
Title: "\u5E7F\u64AD\u6D88\u606F\u5931\u8D25"
Message: "\u623F\u4E3B\u672A\u80FD\u628A\u5173\u952E\u6D88\u606F\u53D1\u7ED9\u6240\u6709\u6210\u5458\uFF0C\u64CD\u4F5C\u5DF2\u53D6\u6D88\u3002"
CooldownSeconds: 3
- TipType: 16
Title: "\u7F51\u7EDC\u6D88\u606F\u961F\u5217\u7E41\u5FD9"
Message: "\u5F53\u524D\u8054\u673A\u6D88\u606F\u79EF\u538B\u8FC7\u591A\uFF0C\u8BF7\u7A0D\u7B49\u7247\u523B\u540E\u518D\u8BD5\u3002"
CooldownSeconds: 5
- TipType: 17
Title: "\u7F51\u7EDC\u6D88\u606F\u63A5\u6536\u5F02\u5E38"
Message: "\u63A5\u6536\u5230\u7684\u8054\u673A\u6D88\u606F\u4E0D\u5B8C\u6574\u6216\u987A\u5E8F\u5F02\u5E38\uFF0C\u8FDE\u63A5\u5C06\u5C1D\u8BD5\u6062\u590D\u3002"
CooldownSeconds: 3
- TipType: 18
Title: "\u7F51\u7EDC\u6D88\u606F\u89E3\u6790\u5931\u8D25"
Message: "\u8054\u673A\u6D88\u606F\u89E3\u6790\u5931\u8D25\uFF0C\u672C\u6B21\u6D88\u606F\u5DF2\u5FFD\u7565\u3002"
CooldownSeconds: 5
- TipType: 19
Title: "\u8054\u673A\u5730\u56FE\u6570\u636E\u65E0\u6548"
Message: "\u6536\u5230\u7684\u8054\u673A\u5730\u56FE\u6570\u636E\u4E0D\u5B8C\u6574\uFF0C\u5DF2\u62D2\u7EDD\u5E94\u7528\u3002"
CooldownSeconds: 5
- TipType: 20
Title: "\u73A9\u5BB6\u5E2D\u4F4D\u540C\u6B65\u5931\u8D25"
Message: "\u623F\u95F4\u6210\u5458\u4E0E\u5BF9\u5C40\u73A9\u5BB6\u5E2D\u4F4D\u65E0\u6CD5\u5BF9\u5E94\uFF0C\u8BF7\u91CD\u65B0\u540C\u6B65\u623F\u95F4\u4FE1\u606F\u3002"
CooldownSeconds: 5
- TipType: 21
Title: "\u5F00\u59CB\u8054\u673A\u6E38\u620F\u5931\u8D25"
Message: "\u8054\u673A\u5BF9\u5C40\u672A\u80FD\u6210\u529F\u5F00\u59CB\uFF0C\u8BF7\u68C0\u67E5\u623F\u95F4\u548C\u7F51\u7EDC\u72B6\u6001\u3002"
CooldownSeconds: 3
- TipType: 22
Title: "\u6B63\u5728\u8BF7\u6C42\u91CD\u8FDE"
Message: "\u68C0\u6D4B\u5230\u540C\u6B65\u5F02\u5E38\uFF0C\u6B63\u5728\u5411\u623F\u4E3B\u8BF7\u6C42\u6700\u65B0\u5BF9\u5C40\u6570\u636E\u3002"
CooldownSeconds: 5
- TipType: 23
Title: "\u6B63\u5728\u6062\u590D\u8054\u673A\u6570\u636E"
Message: "\u6B63\u5728\u5E94\u7528\u623F\u4E3B\u53D1\u6765\u7684\u6700\u65B0\u5BF9\u5C40\u6570\u636E\u3002"
CooldownSeconds: 5
- TipType: 24
Title: "\u91CD\u8FDE\u6062\u590D\u5931\u8D25"
Message: "\u672A\u80FD\u5E94\u7528\u623F\u4E3B\u7684\u6700\u65B0\u5BF9\u5C40\u6570\u636E\uFF0C\u8BF7\u91CD\u65B0\u52A0\u5165\u6216\u8BA9\u623F\u4E3B\u518D\u6B21\u540C\u6B65\u3002"
CooldownSeconds: 5
- TipType: 25
Title: "\u91CD\u8FDE\u6062\u590D\u5B8C\u6210"
Message: "\u5BF9\u5C40\u6570\u636E\u5DF2\u6062\u590D\uFF0C\u53EF\u4EE5\u7EE7\u7EED\u6E38\u620F\u3002"
CooldownSeconds: 5
- TipType: 26
Title: "\u64CD\u4F5C\u540C\u6B65\u5F02\u5E38"
Message: "\u6536\u5230\u7684\u64CD\u4F5C\u4E0E\u5F53\u524D\u56DE\u5408\u6216\u7248\u672C\u4E0D\u4E00\u81F4\uFF0C\u672C\u6B21\u64CD\u4F5C\u5DF2\u62D2\u7EDD\u3002"
CooldownSeconds: 3
- TipType: 27
Title: "\u5730\u56FE\u540C\u6B65\u5F02\u5E38"
Message: "\u68C0\u6D4B\u5230\u623F\u4E3B\u548C\u6210\u5458\u7684\u5730\u56FE\u72B6\u6001\u4E0D\u4E00\u81F4\uFF0C\u5C06\u5C1D\u8BD5\u91CD\u8FDE\u6062\u590D\u3002"
CooldownSeconds: 3
- TipType: 28
Title: "\u64CD\u4F5C\u53D1\u9001\u5931\u8D25"
Message: "\u672C\u6B21\u64CD\u4F5C\u672A\u80FD\u540C\u6B65\u5230\u8054\u673A\u6210\u5458\uFF0C\u5DF2\u53D6\u6D88\u6267\u884C\u3002"
CooldownSeconds: 2
- TipType: 29
Title: "\u623F\u95F4\u4FE1\u606F\u8BF7\u6C42\u5931\u8D25"
Message: "\u6682\u65F6\u65E0\u6CD5\u5411\u623F\u4E3B\u8BF7\u6C42\u623F\u95F4\u4FE1\u606F\uFF0C\u6B63\u5728\u7B49\u5F85P2P\u8FDE\u63A5\u5C31\u7EEA\u3002"
CooldownSeconds: 3
- TipType: 30
Title: "\u9080\u8BF7\u7248\u672C\u4E0D\u4E00\u81F4"
Message: "\u8BE5\u9080\u8BF7\u6765\u81EA\u4E0D\u540C\u7248\u672C\u7684\u6E38\u620F\uFF0C\u8BF7\u786E\u8BA4\u53CC\u65B9\u5DF2\u66F4\u65B0\u5230\u76F8\u540C\u7248\u672C\u3002"
CooldownSeconds: 5

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 75aa47d936a644d08c7b390386992e90
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -48,8 +48,6 @@ namespace RuntimeData
[MemoryPackable]
public partial class MapConfig
{
public const int NoTeamId = 0;
public uint Id;
public uint Width;
public uint Height;
@ -86,7 +84,9 @@ namespace RuntimeData
// 水域类型,默认为 Pangea
public Logic.MapWaterType WaterType = Logic.MapWaterType.Pangea;
public const int NoTeamId = 0;
[MemoryPackConstructor]
public MapConfig()
{
@ -236,6 +236,7 @@ namespace RuntimeData
if (slot == null)
{
LogSystem.LogError($"房间人数超过玩家槽位数量: member={kv.Key}, playerCount={PlayerCount}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyMembersNotSynced);
continue;
}
@ -460,10 +461,15 @@ namespace RuntimeData
if (civ.MemberId != selfMemberId)
{
LogSystem.LogError($"客户端只能修改自己的阵营: self={selfMemberId}, target={civ.MemberId}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyMemberConfigFailed);
return false;
}
if (!GameNetSender.Instance.ChangeCiv(civ)) return false;
if (!GameNetSender.Instance.ChangeCiv(civ))
{
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyMemberConfigFailed);
return false;
}
ApplyMemberCivLocal(civ);
return true;
}
@ -471,6 +477,7 @@ namespace RuntimeData
if (LobbyManager.Instance.Lobby.IsInLobby() && !LobbyManager.Instance.Lobby.IsMemberInLobby(civ.MemberId))
{
LogSystem.LogError($"不能修改不在房间内的成员阵营: target={civ.MemberId}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyMemberConfigFailed);
return false;
}
@ -679,6 +686,7 @@ namespace RuntimeData
{
LogSystem.LogError($"联机模式指定地图中找不到指定的阵营信息, 启动游戏失败, " +
$"地图 ID : {Id} 文明 ID : {member.CivId} 势力 ID : {member.ForceId}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkPlayerMappingFailed);
return false;
}
}
@ -742,15 +750,15 @@ namespace RuntimeData
[MemoryPackable]
public partial class MemberCiv
{
public int Index;
public ulong MemberId;
public uint PlayerId;
public uint CivId;
public uint ForceId;
public bool IsReady;
public int Index;
public int TeamId;
public bool IsAI;
public bool IsCivFixed;
public bool IsReady;
}

View File

@ -274,14 +274,26 @@ namespace RuntimeData
public bool RefreshPlayerNet(MapData mapData)
{
if (Mode != NetMode.Multi) return true;
if (mapData?.PlayerMap?.PlayerDataList == null) return false;
if (mapData?.PlayerMap?.PlayerDataList == null)
{
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.InvalidNetworkMapData);
return false;
}
var lobby = LobbyManager.Instance.Lobby;
if (lobby == null || !lobby.IsInLobby()) return false;
if (lobby == null || !lobby.IsInLobby())
{
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkPlayerMappingFailed);
return false;
}
Players ??= new Dictionary<ulong, uint>();
var lobbyMemberIds = lobby.GetAllMemberIds();
if (lobbyMemberIds == null || lobbyMemberIds.Count == 0) return false;
if (lobbyMemberIds == null || lobbyMemberIds.Count == 0)
{
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyMembersNotSynced);
return false;
}
var lobbyMemberSet = new HashSet<ulong>(lobbyMemberIds);
if (lobby.IsLobbyOwner())
{
@ -337,6 +349,7 @@ namespace RuntimeData
|| !mapData.PlayerMap.GetPlayerDataByPlayerID(playerId, out _))
{
LogSystem.LogWarning($"RefreshPlayerNet missing PlayerId mapping for lobby member: member={memberId}, player={playerId}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkPlayerMappingFailed);
return false;
}
}
@ -348,12 +361,14 @@ namespace RuntimeData
if (kv.Value == 0 || !mapData.PlayerMap.GetPlayerDataByPlayerID(kv.Value, out _))
{
LogSystem.LogWarning($"RefreshPlayerNet invalid PlayerId mapping: member={kv.Key}, player={kv.Value}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkPlayerMappingFailed);
return false;
}
if (!seenPlayerIds.Add(kv.Value))
{
LogSystem.LogWarning($"RefreshPlayerNet duplicate PlayerId mapping: player={kv.Value}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkPlayerMappingFailed);
return false;
}
}
@ -368,6 +383,7 @@ namespace RuntimeData
else
{
LogSystem.LogWarning($"RefreshPlayerNet could not find PlayerId for self member: {selfMemberId}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkPlayerMappingFailed);
return false;
}
}

View File

@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using Logic.Multilingual;
using UnityEngine;
public enum NetworkPlayerTipType
{
None,
SteamUnavailable,
SteamLoginRequired,
SteamClientOutdated,
SteamSessionLost,
LobbyCreateFailed,
LobbyJoinFailed,
LobbyOperationFailed,
LobbyMembersNotReady,
LobbyMembersNotSynced,
LobbyMemberConfigFailed,
HostDisconnected,
P2PConnectionFailed,
P2PConnectionTimeout,
P2PMessageSendFailed,
P2PBroadcastFailed,
P2PQueueFull,
P2PReceiveFailed,
NetworkMessageParseFailed,
InvalidNetworkMapData,
NetworkPlayerMappingFailed,
GameStartFailed,
ReconnectRequested,
ReconnectStarted,
ReconnectFailed,
ReconnectSucceeded,
ActionSyncMismatch,
MapSyncMismatch,
ActionSendFailed,
LobbyDataRequestFailed,
InviteVersionMismatch
}
[Serializable]
[CreateAssetMenu(fileName = "NetworkPlayerTipDataAssets", menuName = "TH1 Game Data/Network Player Tip Data Asset")]
public class NetworkPlayerTipDataAssets : ScriptableObject
{
public float DefaultCooldownSeconds = 3f;
public List<NetworkPlayerTipInfo> NetworkPlayerTipInfoList = new List<NetworkPlayerTipInfo>();
[NonSerialized]
private Dictionary<NetworkPlayerTipType, NetworkPlayerTipInfo> _tipInfoDict;
public bool GetNetworkPlayerTipInfo(NetworkPlayerTipType tipType, out NetworkPlayerTipInfo info)
{
EnsureDict();
return _tipInfoDict.TryGetValue(tipType, out info);
}
private void EnsureDict()
{
if (_tipInfoDict != null && _tipInfoDict.Count == NetworkPlayerTipInfoList.Count) return;
_tipInfoDict = new Dictionary<NetworkPlayerTipType, NetworkPlayerTipInfo>();
foreach (var info in NetworkPlayerTipInfoList)
{
if (info == null || info.TipType == NetworkPlayerTipType.None) continue;
_tipInfoDict[info.TipType] = info;
}
}
}
[Serializable]
public class NetworkPlayerTipInfo
{
public NetworkPlayerTipType TipType;
[MultilingualField] public string Title;
[MultilingualField] public string Message;
public float CooldownSeconds = 3f;
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3ebbbbba88fe43409c16e83b934b878c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -100,6 +100,7 @@ public class Table
public MusicDataAssets MusicDataAssets;
public StaffDataAssets StaffDataAssets;
public WikiData WikiData;
public NetworkPlayerTipDataAssets NetworkPlayerTipDataAssets;
//public AchievementAsset AchievementAsset;
@ -152,6 +153,9 @@ public class Table
CultureCardDataAssets = Resources.Load<CultureCardDataAssets>("Export/CultureCardDataAssets");
MusicDataAssets = Resources.Load<MusicDataAssets>("Export/MusicDataAssets");
StaffDataAssets = Resources.Load<StaffDataAssets>("Export/StaffDataAssets");
NetworkPlayerTipDataAssets = Resources.Load<NetworkPlayerTipDataAssets>("Export/NetworkPlayerTipDataAssets");
if (!NetworkPlayerTipDataAssets)
NetworkPlayerTipDataAssets = Resources.Load<NetworkPlayerTipDataAssets>("DataAssets/NetworkPlayerTipDataAssets");
//不用多语言导表

View File

@ -1111,6 +1111,7 @@ namespace Logic.Action
&& actionParams.MapData.CurPlayer != null && actionParams.MapData.CurPlayer.Id != actionParams.PlayerId)
{
LogSystem.LogError($"CompleteExecute Player 不一致 {ActionId.GetStringLog()}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ActionSyncMismatch);
return false;
}
@ -1128,6 +1129,7 @@ namespace Logic.Action
if (!GameNetSender.Instance.ActionExecute(_actionId, actionParams))
{
LogSystem.LogError($"ActionExecute broadcast failed, abort owner execute: {ActionId.GetStringLog()}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ActionSendFailed);
return false;
}
}
@ -1136,6 +1138,7 @@ namespace Logic.Action
if (!GameNetSender.Instance.ActionConfirm(_actionId, actionParams))
{
LogSystem.LogError($"ActionConfirm send failed, abort local execute: {ActionId.GetStringLog()}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ActionSendFailed);
return false;
}
if (ActionId.ActionType == CommonActionType.TurnEnd) return false;

View File

@ -425,6 +425,7 @@ namespace Logic
public override void Update()
{
if (Time.time < _limitTime) return;
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ReconnectFailed);
_gameLogic.ChangeState(GameState.Menu);
}
}

View File

@ -331,6 +331,7 @@ namespace TH1_Logic.Core
if (!newMapData.Net.RefreshPlayerNet(newMapData))
{
LogSystem.LogError("MainMemberStartMatch: 玩家网络映射失败");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkPlayerMappingFailed);
return false;
}
MapData = newMapData;
@ -354,6 +355,7 @@ namespace TH1_Logic.Core
if (mapRecord == null)
{
LogSystem.LogError($"StartMatch: 未能加载自定义地图 {MapConfig.MapName}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.GameStartFailed);
AbortHostMultiStart("MainMemberStartMatch: 自定义地图加载失败", previousMap, previousInput, previousInteraction, previousGenerator);
return false;
}
@ -417,15 +419,21 @@ namespace TH1_Logic.Core
{
//step #1 读取存档的map
var resumeMap = MapData.GetMapData(isMulti: true);
if (resumeMap == null) return false;
if (resumeMap == null)
{
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.GameStartFailed);
return false;
}
if (resumeMap.Net == null || resumeMap.Net.Mode != NetMode.Multi)
{
LogSystem.LogError("MainMemberResumeMatch: 读取到的不是有效多人存档");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.InvalidNetworkMapData);
return false;
}
if (!resumeMap.Net.RefreshPlayerNet(resumeMap))
{
LogSystem.LogError("MainMemberResumeMatch: 玩家网络映射失败");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkPlayerMappingFailed);
return false;
}
MapData = resumeMap;
@ -475,6 +483,7 @@ namespace TH1_Logic.Core
MapInteraction previousInteraction, MapGenerator previousGenerator)
{
LogSystem.LogError(reason);
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.GameStartFailed);
Timer.Instance.CancelByMessage("Main_CenterMessage_Anim");
MapGeneratorLogic = previousGenerator;
RestoreNetworkMatchState(previousMap, previousInput, previousInteraction, reason);
@ -529,6 +538,7 @@ namespace TH1_Logic.Core
catch (System.Exception e)
{
LogSystem.LogError($"NetStartGame failed: {e}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.GameStartFailed);
RestoreNetworkMatchState(previousMap, previousInput, previousInteraction, "NetStartGame");
return false;
}
@ -573,6 +583,7 @@ namespace TH1_Logic.Core
catch (System.Exception e)
{
LogSystem.LogError($"NetResumeMatch failed: {e}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ReconnectFailed);
RestoreNetworkMatchState(previousMap, previousInput, previousInteraction, "NetResumeMatch");
return false;
}
@ -589,18 +600,23 @@ namespace TH1_Logic.Core
|| map.UnitMap == null)
{
LogSystem.LogError($"{context}: map is invalid");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.InvalidNetworkMapData);
return false;
}
if (!map.Net.RefreshPlayerNet(map))
{
LogSystem.LogError($"{context}: 玩家网络映射失败");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkPlayerMappingFailed);
return false;
}
if (map.PlayerMap.SelfPlayerData == null)
{
LogSystem.LogError($"{context}: SelfPlayerData is invalid");
NetworkPlayerTipManager.Instance.Request(context == "NetResumeMatch"
? NetworkPlayerTipType.ReconnectFailed
: NetworkPlayerTipType.GameStartFailed);
return false;
}

View File

@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using Logic.Multilingual;
using UnityEngine;
namespace TH1_Logic.Net
{
public class NetworkPlayerTipPayload
{
public NetworkPlayerTipType TipType;
public NetworkPlayerTipInfo TipInfo;
public string Title;
public string Message;
}
public class NetworkPlayerTipManager
{
public static NetworkPlayerTipManager Instance { get; } = new NetworkPlayerTipManager();
public event Action<NetworkPlayerTipPayload> OnTipRequested;
private readonly Dictionary<NetworkPlayerTipType, float> _nextTipTimes =
new Dictionary<NetworkPlayerTipType, float>();
private NetworkPlayerTipDataAssets _dataAssets;
public bool Request(NetworkPlayerTipType tipType)
{
if (tipType == NetworkPlayerTipType.None) return false;
var info = GetTipInfo(tipType);
var cooldownSeconds = GetCooldownSeconds(info);
var now = Time.unscaledTime;
if (_nextTipTimes.TryGetValue(tipType, out var nextTipTime) && now < nextTipTime) return false;
_nextTipTimes[tipType] = now + cooldownSeconds;
OnTipRequested?.Invoke(new NetworkPlayerTipPayload
{
TipType = tipType,
TipInfo = info,
Title = ResolveText(info?.Title, tipType.ToString()),
Message = ResolveText(info?.Message, tipType.ToString())
});
return true;
}
public bool TryGetTipInfo(NetworkPlayerTipType tipType, out NetworkPlayerTipInfo info)
{
info = GetTipInfo(tipType);
return info != null;
}
public string GetTitle(NetworkPlayerTipType tipType)
{
var info = GetTipInfo(tipType);
return ResolveText(info?.Title, tipType.ToString());
}
public string GetMessage(NetworkPlayerTipType tipType)
{
var info = GetTipInfo(tipType);
return ResolveText(info?.Message, tipType.ToString());
}
public void ClearCooldown(NetworkPlayerTipType tipType)
{
if (tipType == NetworkPlayerTipType.None) return;
_nextTipTimes.Remove(tipType);
}
public void ClearAllCooldowns()
{
_nextTipTimes.Clear();
}
private NetworkPlayerTipInfo GetTipInfo(NetworkPlayerTipType tipType)
{
var dataAssets = GetDataAssets();
if (!dataAssets) return null;
return dataAssets.GetNetworkPlayerTipInfo(tipType, out var info) ? info : null;
}
private float GetCooldownSeconds(NetworkPlayerTipInfo info)
{
if (info != null && info.CooldownSeconds > 0f) return info.CooldownSeconds;
var dataAssets = GetDataAssets();
if (dataAssets && dataAssets.DefaultCooldownSeconds > 0f) return dataAssets.DefaultCooldownSeconds;
return 3f;
}
private NetworkPlayerTipDataAssets GetDataAssets()
{
if (_dataAssets) return _dataAssets;
if (Table.Instance != null) _dataAssets = Table.Instance.NetworkPlayerTipDataAssets;
if (!_dataAssets) _dataAssets = Resources.Load<NetworkPlayerTipDataAssets>("Export/NetworkPlayerTipDataAssets");
if (!_dataAssets) _dataAssets = Resources.Load<NetworkPlayerTipDataAssets>("DataAssets/NetworkPlayerTipDataAssets");
return _dataAssets;
}
private static string ResolveText(string text, string fallback)
{
if (string.IsNullOrEmpty(text)) return fallback;
if (uint.TryParse(text, out var id)) return MultilingualManager.Instance.GetMultilingualText(id);
return text;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4b12b9e1df7b48bd95b2c5c4e22c5dad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -44,6 +44,7 @@ namespace TH1_Logic.Steam
catch (System.Exception e)
{
LogSystem.LogError($"OnMessageReceived 处理失败, bytes: {data?.Length ?? 0}, error: {e}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
}
}
@ -53,6 +54,7 @@ namespace TH1_Logic.Steam
if (message == null)
{
LogSystem.LogError($"消息解析失败: OnReceivedString");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
return;
}
@ -66,12 +68,14 @@ namespace TH1_Logic.Steam
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;
@ -89,10 +93,12 @@ namespace TH1_Logic.Steam
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);
}
}
@ -102,19 +108,26 @@ namespace TH1_Logic.Steam
if (message == null)
{
LogSystem.LogError($"消息解析失败: OnReceivedActionConfirm");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
return;
}
if (!LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
if (message.ActionData == null) 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;
}
@ -126,6 +139,7 @@ namespace TH1_Logic.Steam
&& Main.MapData.CurPlayer != null && Main.MapData.CurPlayer.Id != message.ActionData.Param.PlayerId)
{
LogSystem.LogError($"OnReceivedActionConfirm Player 不一致");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ActionSyncMismatch);
return;
}
@ -133,6 +147,7 @@ namespace TH1_Logic.Steam
if (action == null)
{
LogSystem.LogError($"OnReceivedActionConfirm 找不到对应 Action");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ActionSyncMismatch);
return;
}
action.CompleteExecute(message.ActionData.Param);
@ -144,22 +159,33 @@ namespace TH1_Logic.Steam
if (message == null)
{
LogSystem.LogError($"消息解析失败: OnReceivedActionExcute");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
return;
}
if (LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
if (message.ActionData == null) 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()) 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;
}
@ -171,12 +197,14 @@ namespace TH1_Logic.Steam
&& 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);
@ -188,6 +216,7 @@ namespace TH1_Logic.Steam
if (message == null)
{
LogSystem.LogError($"消息解析失败: OnReceivedTurnEnd");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
return;
}
if (!LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
@ -200,6 +229,7 @@ namespace TH1_Logic.Steam
if (message == null)
{
LogSystem.LogError($"消息解析失败: OnReceivedMapConfirm");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
return;
}
@ -213,6 +243,7 @@ namespace TH1_Logic.Steam
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)
@ -220,17 +251,20 @@ namespace TH1_Logic.Steam
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
@ -245,16 +279,19 @@ namespace TH1_Logic.Steam
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();
}
}
@ -266,12 +303,14 @@ namespace TH1_Logic.Steam
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
@ -279,16 +318,19 @@ namespace TH1_Logic.Steam
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
{
@ -305,6 +347,7 @@ namespace TH1_Logic.Steam
{
if (!Main.Instance.NetResumeMatch(message.MapData))
{
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ReconnectFailed);
Main.Instance.GameLogic.ChangeState(previousState);
return;
}
@ -312,10 +355,12 @@ namespace TH1_Logic.Steam
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, () =>
@ -346,10 +391,15 @@ namespace TH1_Logic.Steam
if (message == null)
{
LogSystem.LogError($"消息解析失败: OnReceivedChangeCiv");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
return;
}
if (!LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
if (message.Civ == null) return;
if (message.Civ == null)
{
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyMemberConfigFailed);
return;
}
if (Main.Instance.MapConfig.UpdateMemberCiv(message.Civ))
Main.Instance.MapConfig.CheckMapConfigChanged();
}
@ -360,10 +410,15 @@ namespace TH1_Logic.Steam
if (message == null)
{
LogSystem.LogError($"消息解析失败: OnReceivedUpdateLobbyData");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
return;
}
if (LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
if (message.Config == null) return;
if (message.Config == null)
{
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyMembersNotSynced);
return;
}
Main.Instance.MapConfig = message.Config;
if (Main.Instance.MapConfig.HasSameLobbyMembers(LobbyManager.Instance.Lobby.GetAllMemberInfo()))
{
@ -371,6 +426,7 @@ namespace TH1_Logic.Steam
}
else
{
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyMembersNotSynced);
GameNetSender.Instance.MarkLobbyDataSyncRequired();
GameNetSender.Instance.RequestLobbyData();
}
@ -383,6 +439,7 @@ namespace TH1_Logic.Steam
if (message == null)
{
LogSystem.LogError($"消息解析失败: OnReceivedRequestLobbyData");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
return;
}
if (!LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
@ -397,6 +454,7 @@ namespace TH1_Logic.Steam
if (message == null)
{
LogSystem.LogError($"消息解析失败: OnReceivedHeartbeat");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
return;
}
@ -429,6 +487,7 @@ namespace TH1_Logic.Steam
if (message == null)
{
LogSystem.LogError($"消息解析失败: OnReceivedMemberStateSync");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
return;
}
@ -453,6 +512,7 @@ namespace TH1_Logic.Steam
{
// TODO 此时说明房主端我的状态有误,可选申请重连
LogSystem.LogWarning($"OnReceivedMemberStateSync 检测状态错误");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.MapSyncMismatch);
GameNetSender.Instance.SendRequestForceUpdate();
}
}
@ -463,6 +523,7 @@ namespace TH1_Logic.Steam
if (message == null)
{
LogSystem.LogError($"消息解析失败: OnReceivedRequestForceUpdate");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
return;
}
@ -477,6 +538,7 @@ namespace TH1_Logic.Steam
if (message == null)
{
LogSystem.LogError($"消息解析失败: OnReceivedHeartbeatReply");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
return;
}
@ -490,6 +552,7 @@ namespace TH1_Logic.Steam
if (message == null)
{
LogSystem.LogError($"消息解析失败: OnReceivedChatMessage");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
return;
}
@ -502,6 +565,7 @@ namespace TH1_Logic.Steam
if (message == null)
{
LogSystem.LogError($"消息解析失败: OnReceivedChatMessage");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
return;
}
@ -514,6 +578,7 @@ namespace TH1_Logic.Steam
if (message.LobbyInfo.Version != ConfigManager.Instance.VersionCfg.CurVersionInfo.Version)
{
LogSystem.LogInfo($"版本不一致!!!");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.InviteVersionMismatch);
return;
}

View File

@ -36,6 +36,7 @@ namespace TH1_Logic.Steam
byte[] messageBytes = MemoryPack.MemoryPackSerializer.Serialize(message);
if (LobbyManager.Instance.Lobby.SendMessageToPeer(LobbyManager.Instance.Lobby.GetLobbyOwnerId(), messageBytes)) return true;
LogSystem.LogError($"{message?.GetType().Name}: 发送给房主失败");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
return false;
}
@ -45,6 +46,7 @@ namespace TH1_Logic.Steam
byte[] messageBytes = MemoryPack.MemoryPackSerializer.Serialize(message);
if (LobbyManager.Instance.Lobby.SendMessageToPeer(memberId, messageBytes)) return true;
LogSystem.LogError($"{message?.GetType().Name}: 发送给成员失败 memberId={memberId}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
return false;
}
@ -55,6 +57,7 @@ namespace TH1_Logic.Steam
byte[] messageBytes = MemoryPack.MemoryPackSerializer.Serialize(message);
if (LobbyManager.Instance.Lobby.BroadcastMessage(messageBytes)) return true;
LogSystem.LogError($"{message?.GetType().Name}: 房主广播失败");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PBroadcastFailed);
return false;
}
@ -64,6 +67,7 @@ namespace TH1_Logic.Steam
byte[] messageBytes = MemoryPack.MemoryPackSerializer.Serialize(message);
if (LobbyManager.Instance.Lobby.BroadcastMessage(messageBytes)) return true;
LogSystem.LogError($"{message?.GetType().Name}: 广播给所有成员失败");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PBroadcastFailed);
return false;
}
@ -181,6 +185,7 @@ namespace TH1_Logic.Steam
|| mapData.UnitMap == null)
{
LogSystem.LogError($"{context}: 无效多人地图数据,取消发送");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.InvalidNetworkMapData);
mapData = null;
return false;
}
@ -188,6 +193,7 @@ namespace TH1_Logic.Steam
if (!mapData.Net.RefreshPlayerNet(mapData))
{
LogSystem.LogError($"{context}: 玩家网络映射失败,取消发送");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkPlayerMappingFailed);
mapData = null;
return false;
}
@ -201,6 +207,7 @@ namespace TH1_Logic.Steam
if (memberCiv == null)
{
LogSystem.LogError($"Get Self MemberCiv Error ");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyMemberConfigFailed);
return false;
}
@ -226,7 +233,11 @@ namespace TH1_Logic.Steam
if (LobbyManager.Instance.Lobby.IsLobbyOwner()) return false;
if (!LobbyManager.Instance.Lobby.IsInLobby()) return false;
var hostId = LobbyManager.Instance.Lobby.GetLobbyOwnerId();
if (hostId == 0 || !SimpleP2P.Instance.IsConnectedTo(new CSteamID(hostId))) return false;
if (hostId == 0 || !SimpleP2P.Instance.IsConnectedTo(new CSteamID(hostId)))
{
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyDataRequestFailed);
return false;
}
var now = UnityEngine.Time.time;
if (!force && now - _lastRequestLobbyDataTime < RequestLobbyDataCooldown) return false;
@ -237,6 +248,7 @@ namespace TH1_Logic.Steam
if (SendMessage(data)) return true;
_lastRequestLobbyDataTime = now - RequestLobbyDataCooldown + 0.2f;
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyDataRequestFailed);
return false;
}
@ -323,10 +335,12 @@ namespace TH1_Logic.Steam
_lastRequestForceUpdateTime = now;
LogSystem.LogError($"客户端请求重连: SendRequestForceUpdate");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ReconnectRequested);
var data = new RequestForceUpdateMessage();
data.MemberId = LobbyManager.Instance.Lobby.GetSelfMemberId();
if (SendMessage(data))
{
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ReconnectStarted);
Main.Instance.GameLogic.ChangeState(GameState.ForceUpdating);
}
}

View File

@ -178,6 +178,7 @@ namespace TH1_Logic.Steam
if (connection == HSteamNetConnection.Invalid)
{
OnConnectionErrorEvent?.Invoke($"Failed to connect to {steamID}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PConnectionFailed);
return false;
}
@ -195,6 +196,7 @@ namespace TH1_Logic.Steam
if (!SteamUser.BLoggedOn())
{
LogSystem.LogError("Steam not logged in");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamLoginRequired);
return false;
}
@ -235,6 +237,7 @@ namespace TH1_Logic.Steam
if (relayStatus.m_eAvail != ESteamNetworkingAvailability.k_ESteamNetworkingAvailability_Current)
{
LogSystem.LogWarning($"Steam 中继网络问题: {relayStatus.m_debugMsg}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PConnectionFailed);
}
}
@ -266,6 +269,7 @@ namespace TH1_Logic.Steam
{
var reason = $"Connection to {steamID} timed out";
LogSystem.LogError(reason);
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PConnectionTimeout);
_connectionTimeouts.Remove(steamID);
if (_connections.TryGetValue(steamID, out var conn))
@ -360,6 +364,7 @@ namespace TH1_Logic.Steam
if (result != EResult.k_EResultOK)
{
LogSystem.LogError($"无法接受连接: {result}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PConnectionFailed);
SteamNetworkingSockets.CloseConnection(info.m_hConn, 0, "Failed to accept", false);
return;
}
@ -402,6 +407,7 @@ namespace TH1_Logic.Steam
case ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_ProblemDetectedLocally:
LogSystem.LogWarning($"Connection problem detected locally: {remote}");
LogSystem.LogWarning($"Problem details: {info.m_info.m_szEndDebug}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PConnectionFailed);
HandleDisconnection(remote, info.m_hConn);
break;
@ -409,6 +415,11 @@ namespace TH1_Logic.Steam
// 检查具体的失败原因
var endReason = info.m_info.m_eEndReason;
LogSystem.LogError($"Connection failed - Reason: {endReason}");
var isTimeout = endReason == (int)ESteamNetConnectionEnd.k_ESteamNetConnectionEnd_Misc_Timeout
|| endReason == (int)ESteamNetConnectionEnd.k_ESteamNetConnectionEnd_Remote_Timeout;
NetworkPlayerTipManager.Instance.Request(isTimeout
? NetworkPlayerTipType.P2PConnectionTimeout
: NetworkPlayerTipType.P2PConnectionFailed);
// 提供更详细的错误信息
switch (endReason)
@ -449,12 +460,14 @@ namespace TH1_Logic.Steam
if (!steamID.IsValid())
{
LogSystem.LogWarning($"接收到无效的Steam ID连接请求: {steamID}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PConnectionFailed);
return false;
}
if (!IsLobbyPeer(steamID))
{
LogSystem.LogWarning($"拒绝非当前房间成员的连接请求: {steamID}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PConnectionFailed);
return false;
}
@ -462,6 +475,7 @@ namespace TH1_Logic.Steam
if (_connections.Count >= 10) // 假设最大10个连接
{
LogSystem.LogWarning("已达到最大连接数,拒绝新连接");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PConnectionFailed);
return false;
}
@ -520,6 +534,7 @@ namespace TH1_Logic.Steam
if (data == null || data.Length == 0)
{
LogSystem.LogWarning("Trying to send null or empty data");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
return false;
}
if (data.Length > MaxLargeMessageBytes)
@ -527,12 +542,14 @@ namespace TH1_Logic.Steam
var reason = $"Trying to send oversized P2P message: target={target}, bytes={data.Length}, max={MaxLargeMessageBytes}";
LogSystem.LogError(reason);
OnMessageSendFailedEvent?.Invoke(target, reason);
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
return false;
}
if (!target.IsValid())
{
LogSystem.LogWarning($"Trying to send data to invalid Steam ID: {target}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
return false;
}
@ -541,6 +558,7 @@ namespace TH1_Logic.Steam
var reason = $"Skip sending message to non-lobby peer: {target}";
LogSystem.LogWarning(reason);
OnMessageSendFailedEvent?.Invoke(target, reason);
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
DisconnectFromPeer(target);
return false;
}
@ -550,6 +568,7 @@ namespace TH1_Logic.Steam
var reason = $"No connection to {target}";
LogSystem.LogWarning(reason);
OnMessageSendFailedEvent?.Invoke(target, reason);
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
return false;
}
@ -559,6 +578,7 @@ namespace TH1_Logic.Steam
var reason = $"Connection to {target} is not active for queueing. State: {connectionState}, bytes: {data.Length}";
LogSystem.LogWarning(reason);
OnMessageSendFailedEvent?.Invoke(target, reason);
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
HandleDisconnection(target, conn);
return false;
}
@ -685,16 +705,18 @@ namespace TH1_Logic.Steam
var result = SteamNetworkingSockets.SendMessageToConnection(conn, ptr, (uint)data.Length, flags, out _);
if (result != EResult.k_EResultOK)
{
if (logFailure)
LogSystem.LogError($"Failed to send message to {target}: {result}, bytes: {data.Length}, flags: {flags}, reliable: {reliable}");
return result;
}
if (logFailure)
LogSystem.LogError($"Failed to send message to {target}: {result}, bytes: {data.Length}, flags: {flags}, reliable: {reliable}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
return result;
}
return EResult.k_EResultOK;
}
catch (Exception e)
{
LogSystem.LogError($"Exception while sending message: {e.Message}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
return EResult.k_EResultFail;
}
finally
@ -710,6 +732,7 @@ namespace TH1_Logic.Steam
if (data == null || data.Length == 0)
{
LogSystem.LogWarning("Trying to send null or empty data");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
return false;
}
@ -731,6 +754,7 @@ namespace TH1_Logic.Steam
if (result != EResult.k_EResultOK)
{
LogSystem.LogError($"Failed to send message to {target}: {result}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
return false;
}
@ -739,6 +763,7 @@ namespace TH1_Logic.Steam
catch (Exception e)
{
LogSystem.LogError($"Exception while sending message: {e.Message}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
return false;
}
finally
@ -754,6 +779,7 @@ namespace TH1_Logic.Steam
if (data == null || data.Length == 0)
{
LogSystem.LogWarning("Trying to broadcast null or empty data");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PBroadcastFailed);
return false;
}
@ -766,6 +792,7 @@ namespace TH1_Logic.Steam
if (!IsLobbyPeer(steamID))
{
LogSystem.LogWarning($"Remove non-lobby peer before broadcast: {steamID}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PBroadcastFailed);
DisconnectFromPeer(steamID);
allSucceeded = false;
continue;
@ -774,6 +801,7 @@ namespace TH1_Logic.Steam
if (!SendTo(steamID, data, reliable))
{
allSucceeded = false;
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PBroadcastFailed);
OnMessageSendFailedEvent?.Invoke(steamID, $"Broadcast enqueue failed: bytes={data.Length}");
}
}
@ -831,6 +859,7 @@ namespace TH1_Logic.Steam
var reason = $"P2P outgoing queue limit exceeded: target={target}, bytes={data.Length}, peerQueued={queue.QueuedBytes}, totalQueued={_outgoingQueuedBytes}";
LogSystem.LogError(reason);
OnMessageSendFailedEvent?.Invoke(target, reason);
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PQueueFull);
if (createdQueue)
{
RemoveOutgoingMessages(target);
@ -925,6 +954,7 @@ namespace TH1_Logic.Steam
catch (Exception e)
{
LogSystem.LogError($"Error processing message from {steamID}: {e}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PReceiveFailed);
}
finally
{
@ -959,6 +989,7 @@ namespace TH1_Logic.Steam
catch (Exception e)
{
LogSystem.LogError($"Error processing invite message: {e}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PReceiveFailed);
}
finally
{
@ -996,6 +1027,7 @@ namespace TH1_Logic.Steam
if (sequence == 0 || payloadLength < 0 || payloadLength != data.Length - OrderedMessageHeaderSize)
{
LogSystem.LogWarning($"Invalid ordered P2P payload from {steamID}: sequence={sequence}, payload={payloadLength}, bytes={data.Length}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PReceiveFailed);
return true;
}
@ -1022,6 +1054,7 @@ namespace TH1_Logic.Steam
|| state.PendingBytes + payload.Length > MaxOrderedPendingBytesPerPeer)
{
LogSystem.LogError($"Ordered P2P pending buffer full from {steamID}: sequence={sequence}, expected={state.ExpectedSequence}, pending={state.PendingMessages.Count}, bytes={state.PendingBytes}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PReceiveFailed);
OnConnectionErrorEvent?.Invoke($"Ordered P2P pending buffer full from {steamID}");
if (_connections.TryGetValue(steamID, out var connection))
{
@ -1132,6 +1165,7 @@ namespace TH1_Logic.Steam
private bool RejectIncomingLargeMessage(CSteamID steamID, string reason)
{
LogSystem.LogWarning($"Reject incoming large P2P message from {steamID}: {reason}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PReceiveFailed);
OnConnectionErrorEvent?.Invoke($"Reject incoming large P2P message from {steamID}: {reason}");
if (_connections.TryGetValue(steamID, out var connection))
{
@ -1194,6 +1228,7 @@ namespace TH1_Logic.Steam
var nonLobbyReason = $"Drop queued P2P messages for non-lobby peer: {pending.Target}";
LogSystem.LogWarning(nonLobbyReason);
OnMessageSendFailedEvent?.Invoke(pending.Target, nonLobbyReason);
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
RemoveOutgoingMessages(pending.Target);
continue;
}
@ -1203,6 +1238,7 @@ namespace TH1_Logic.Steam
var missingConnectionReason = $"Drop queued P2P messages because connection is missing: {pending.Target}";
LogSystem.LogWarning(missingConnectionReason);
OnMessageSendFailedEvent?.Invoke(pending.Target, missingConnectionReason);
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
RemoveOutgoingMessages(pending.Target);
continue;
}
@ -1215,6 +1251,7 @@ namespace TH1_Logic.Steam
{
OnMessageSendFailedEvent?.Invoke(pending.Target,
$"Queued P2P message dropped because connection is not active: state={connectionState}, messageId: {pending.MessageId}, chunk: {pending.ChunkIndex + 1}/{pending.ChunkCount}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
HandleDisconnection(pending.Target, conn);
}
else
@ -1225,6 +1262,7 @@ namespace TH1_Logic.Steam
var timeoutReason = $"Queued P2P message waited too long for connected state to {pending.Target}: state={connectionState}, messageId: {pending.MessageId}, chunk: {pending.ChunkIndex + 1}/{pending.ChunkCount}, attempts: {pending.Attempts}";
LogSystem.LogError(timeoutReason);
OnMessageSendFailedEvent?.Invoke(pending.Target, timeoutReason);
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PConnectionTimeout);
HandleDisconnection(pending.Target, conn);
continue;
}
@ -1254,6 +1292,7 @@ namespace TH1_Logic.Steam
var timeoutReason = $"Queued P2P message exceeded retry limit to {pending.Target}: {result}, messageId: {pending.MessageId}, chunk: {pending.ChunkIndex + 1}/{pending.ChunkCount}, attempts: {pending.Attempts}";
LogSystem.LogError(timeoutReason);
OnMessageSendFailedEvent?.Invoke(pending.Target, timeoutReason);
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
HandleDisconnection(pending.Target, conn);
continue;
}
@ -1267,6 +1306,7 @@ namespace TH1_Logic.Steam
var reason = $"Failed to send queued P2P message to {pending.Target}: {result}, messageId: {pending.MessageId}, chunk: {pending.ChunkIndex + 1}/{pending.ChunkCount}";
LogSystem.LogError(reason);
OnMessageSendFailedEvent?.Invoke(pending.Target, reason);
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
HandleDisconnection(pending.Target, conn);
}
}
@ -1327,6 +1367,7 @@ namespace TH1_Logic.Steam
foreach (var peer in peersToDisconnect)
{
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PReceiveFailed);
OnConnectionErrorEvent?.Invoke($"Ordered P2P message gap timed out from {peer}");
if (_connections.TryGetValue(peer, out var connection))
{
@ -1358,6 +1399,7 @@ namespace TH1_Logic.Steam
{
peerMessages.Value.Remove(messageId);
LogSystem.LogWarning($"Large P2P message receive timed out from {peerMessages.Key}, messageId: {messageId}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PReceiveFailed);
}
if (messagesToRemove.Count > 0)
@ -1375,6 +1417,7 @@ namespace TH1_Logic.Steam
foreach (var peer in peersToDisconnect)
{
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PReceiveFailed);
OnConnectionErrorEvent?.Invoke($"Large P2P message receive timed out from {peer}");
if (_connections.TryGetValue(peer, out var connection))
{

View File

@ -179,13 +179,16 @@ namespace TH1_Logic.Steam
if (!_isSteamInitialized)
{
LogSystem.LogError($"Steam API初始化失败result={initResult}, msg={steamErrMsg}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamUnavailable);
switch (initResult)
{
case ESteamAPIInitResult.k_ESteamAPIInitResult_NoSteamClient:
LogSystem.LogError("→ Steam客户端未运行或未登录请先打开Steam并登录账号。");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamLoginRequired);
break;
case ESteamAPIInitResult.k_ESteamAPIInitResult_VersionMismatch:
LogSystem.LogError("→ Steam客户端版本过旧请在Steam中检查更新。");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamClientOutdated);
break;
case ESteamAPIInitResult.k_ESteamAPIInitResult_FailedGeneric:
LogSystem.LogError("→ 通用失败。常见原因当前Steam账号未拥有该AppID需在Steamworks后台授予权限或工作目录下steam_appid.txt位置错误或上一次进程未退干净。");
@ -229,11 +232,13 @@ namespace TH1_Logic.Steam
else
{
LogSystem.LogWarning("Steam用户未登录");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamLoginRequired);
}
}
catch (System.Exception e)
{
LogSystem.LogError($"检查用户登录状态异常: {e.Message}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamUnavailable);
}
}
@ -382,6 +387,7 @@ namespace TH1_Logic.Steam
_isHandlingSessionLoss = true;
LogSystem.LogWarning($"Local Steam session lost: {reason}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamSessionLost);
ForceQuitCurrentMultiplayerSession(reason);
}
@ -547,12 +553,14 @@ namespace TH1_Logic.Steam
if (!IsLobbyOwner())
{
LogSystem.LogError("Only lobby owner can kick members");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
return;
}
if (memberId == SteamUser.GetSteamID().m_SteamID)
{
LogSystem.LogError("Cannot kick yourself");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
return;
}
@ -593,6 +601,7 @@ namespace TH1_Logic.Steam
if (lobbyId == 0)
{
LogSystem.LogError("Invalid room code");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyJoinFailed);
return;
}
JoinLobbyById(lobbyId);
@ -635,12 +644,14 @@ namespace TH1_Logic.Steam
if (!CurrentLobby.IsValid())
{
LogSystem.LogError("Not in a lobby");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
return;
}
if (string.IsNullOrEmpty(code))
{
LogSystem.LogError("Invalid user code");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
return;
}
@ -648,6 +659,7 @@ namespace TH1_Logic.Steam
if (steamId == 0)
{
LogSystem.LogError("Invalid user code format");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
return;
}
@ -655,6 +667,7 @@ namespace TH1_Logic.Steam
if (!targetId.IsValid())
{
LogSystem.LogError("Invalid Steam ID");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
return;
}
@ -668,12 +681,14 @@ namespace TH1_Logic.Steam
if (!IsLobbyOwner())
{
LogSystem.LogError("Only lobby owner can change lobby type");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
return false;
}
if (!CurrentLobby.IsValid())
{
LogSystem.LogError("Not in a lobby");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
return false;
}
@ -685,6 +700,7 @@ namespace TH1_Logic.Steam
else
{
LogSystem.LogError($"Failed to change lobby type to: {lobbyType}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
}
return success;
}
@ -724,6 +740,7 @@ namespace TH1_Logic.Steam
if (!targetId.IsValid())
{
LogSystem.LogError($"Invalid target Steam ID: {targetSteamId}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
return false;
}
@ -731,6 +748,7 @@ namespace TH1_Logic.Steam
if (!isSucceed)
{
LogSystem.LogError($"Failed to send game invite to: {targetSteamId}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
return false;
}
LogSystem.LogInfo($"Game invite sent to: {targetSteamId}");
@ -907,6 +925,7 @@ namespace TH1_Logic.Steam
if (!IsLobbyOwner())
{
LogSystem.LogError("Only lobby owner can set lobby data");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
return;
}
@ -929,6 +948,7 @@ namespace TH1_Logic.Steam
{
CurrentState = LobbyState.None;
LogSystem.LogError($"Failed to create lobby: {data.m_eResult}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyCreateFailed);
return;
}
@ -970,6 +990,7 @@ namespace TH1_Logic.Steam
{
_steamServerConnected = false;
LogSystem.LogWarning($"Steam server connect failure: {data.m_eResult}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamSessionLost);
}
private void OnSteamShutdownCallback(SteamShutdown_t data)
@ -989,6 +1010,7 @@ namespace TH1_Logic.Steam
if (disbandedData == "true")
{
LogSystem.LogInfo($"Cannot join disbanded lobby");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyJoinFailed);
return;
}
@ -1004,6 +1026,7 @@ namespace TH1_Logic.Steam
CurrentState = LobbyState.None;
var errorMsg = $"Failed to enter lobby: {(EChatRoomEnterResponse)data.m_EChatRoomEnterResponse}";
LogSystem.LogError(errorMsg);
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyJoinFailed);
OnLobbyErrorEvent?.Invoke(errorMsg);
return;
}
@ -1098,6 +1121,7 @@ namespace TH1_Logic.Steam
}
LogSystem.LogWarning($"Host left lobby(oldHost={oldOwner}, newHost={newOwner}), forcing all members leave");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.HostDisconnected);
HandleLocalSteamSessionLost($"Host changed from {oldOwner} to {newOwner}");
}
@ -1184,12 +1208,14 @@ namespace TH1_Logic.Steam
private void OnP2PConnectionError(string error)
{
LogSystem.LogError($"P2P connection error: {error}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PConnectionFailed);
}
private void OnP2PMessageSendFailed(CSteamID steamID, string reason)
{
var error = $"P2P message send failed: target={steamID}, reason={reason}";
LogSystem.LogError(error);
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PMessageSendFailed);
if (_suppressP2PSendFailureLobbyErrors <= 0) OnLobbyErrorEvent?.Invoke(error);
}
@ -1218,6 +1244,7 @@ namespace TH1_Logic.Steam
{
OnLobbyErrorEvent?.Invoke("P2P broadcast failed: Not in lobby");
LogSystem.LogError("P2P broadcast failed: Not in lobby");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PBroadcastFailed);
return false;
}
@ -1225,6 +1252,7 @@ namespace TH1_Logic.Steam
{
OnLobbyErrorEvent?.Invoke("P2P broadcast failed: Trying to broadcast null or empty data");
LogSystem.LogError("P2P broadcast failed: Trying to broadcast null or empty data");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PBroadcastFailed);
return false;
}
@ -1237,6 +1265,19 @@ namespace TH1_Logic.Steam
}
if (targets.Count == 0) return true;
if (!SimpleP2P.Instance.CanQueueMessages(targets, data.Length, out var failedTarget, out var preflightReason))
{
var preflightError = $"P2P broadcast preflight failed: target={failedTarget}, reason={preflightReason}";
LogSystem.LogError(preflightError);
var isQueueLimit = preflightReason.IndexOf("queue", StringComparison.OrdinalIgnoreCase) >= 0
|| preflightReason.IndexOf("Queued", StringComparison.OrdinalIgnoreCase) >= 0;
NetworkPlayerTipManager.Instance.Request(isQueueLimit
? NetworkPlayerTipType.P2PQueueFull
: NetworkPlayerTipType.P2PBroadcastFailed);
OnLobbyErrorEvent?.Invoke(preflightError);
return false;
}
var successCount = 0;
var failedCount = 0;
_suppressP2PSendFailureLobbyErrors++;
@ -1262,12 +1303,17 @@ namespace TH1_Logic.Steam
if (successCount > 0)
{
if (failedCount > 0)
{
LogSystem.LogWarning($"P2P broadcast partially succeeded: success={successCount}, failed={failedCount}, bytes={data.Length}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PBroadcastFailed);
return false;
}
return true;
}
var error = $"P2P broadcast failed for all targets: targets={targets.Count}, bytes={data.Length}";
LogSystem.LogError(error);
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PBroadcastFailed);
OnLobbyErrorEvent?.Invoke(error);
return false;
}

View File

@ -642,6 +642,7 @@ namespace TH1_UI.View.Outside
if (!Main.Instance.MapConfig.AreAllLobbyMembersReady())
{
Debug.Log("Cannot start multiplayer game: not all members are ready");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyMembersNotReady);
return;
}
@ -658,6 +659,7 @@ namespace TH1_UI.View.Outside
int playerCount = (int)PlayerCount.SelectedIndex + 2;
if (members.Count > playerCount)
{
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyMembersNotSynced);
CantStartHint.SetActive(true);
CantStartHintText.text = MultilingualManager.Instance.GetMultilingualText(Table.Instance.TextDataAssets.OutsideMultiplayCantStartCount);
CantStartHintAnimancer.Play(ResourceCache.Instance.AnimCache.UICommonPanelFadeIn);
@ -961,11 +963,16 @@ namespace TH1_UI.View.Outside
return;
}
if (lobbyInfo.GameState != 0)
{
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyJoinFailed);
return;
}
//注册一次性加入结果监听
_lobby.OnLobbyEnteredEvent += OnLobbyJoinSuccess;
_lobby.OnLobbyErrorEvent += OnLobbyJoinFailed;
_lobby.RefreshLobbyListInfo();
if (lobbyInfo.GameState != 0) return;
LobbyManager.Instance.Lobby.JoinLobbyById(lobbyInfo.LobbyId);
}
@ -988,6 +995,7 @@ namespace TH1_UI.View.Outside
{
_lobby.OnLobbyEnteredEvent -= OnLobbyJoinSuccess;
_lobby.OnLobbyErrorEvent -= OnLobbyJoinFailed;
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyJoinFailed);
//显示加入失败提示