TH1/Unity/Assets/Scripts/TH1_Logic/Steam/GameNetSender.cs
2026-05-21 17:37:38 +08:00

373 lines
14 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.

/*
* @Author: 白哉
* @Description:
* @Date: 2025年09月08日 星期一 17:09:18
* @Modify:
*/
using Logic;
using Logic.Action;
using Logic.AI;
using Logic.CrashSight;
using RuntimeData;
using Steamworks;
using TH1_Logic.Chat;
using TH1_Logic.Core;
using TH1_Logic.Net;
namespace TH1_Logic.Steam
{
public class GameNetSender
{
public static GameNetSender Instance { get; } = new GameNetSender();
private static float RecordTime;
private const float RequestLobbyDataCooldown = 1f;
private const float RequestForceUpdateCooldown = 5f;
private float _lastRequestLobbyDataTime = -RequestLobbyDataCooldown;
private float _lastRequestForceUpdateTime = -RequestForceUpdateCooldown;
private bool _needLobbyDataFromHost;
// 发送消息给房主
public bool SendMessage(BaseMessage message)
{
if (LobbyManager.Instance.Lobby.IsLobbyOwner()) return false;
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;
}
// 发送消息给指定人
public bool SendMessageToPlayer(ulong memberId, BaseMessage message)
{
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;
}
// 房主广播消息给所有成员
public bool BroadcastMessage(BaseMessage message)
{
if (!LobbyManager.Instance.Lobby.IsLobbyOwner()) return false;
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;
}
// 房主广播消息给所有成员
public bool SendMessageToAllPlayer(BaseMessage message)
{
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;
}
// 广播字符串
public void BroadcastString(string str)
{
var data = new StringMessage();
data.Content = str;
SendMessageToAllPlayer(data);
}
// 游戏开始
public bool GameStart()
{
if (!TryGetValidMultiMapForBroadcast("GameStart", out var mapData)) return false;
var data = new GameStartMessage();
data.MapData = mapData;
return BroadcastMessage(data);
}
// 请求行为 (成员 => 房主)
public bool ActionConfirm(CommonActionId id, CommonActionParams param)
{
var actionData = new ActionNetData();
actionData.Version = Main.MapData.Net.GetActionVersion();
actionData.MapHash = NetData.GetMapDataHash(Main.MapData);
actionData.Param = param;
actionData.ActionId = id;
var data = new ActionConfirmMessage();
data.ActionData = actionData;
return SendMessage(data);
}
// 行为执行 (房主 => 所有成员)
public bool ActionExecute(CommonActionId id, CommonActionParams param)
{
var actionData = new ActionNetData();
actionData.Version = Main.MapData.Net.GetActionVersion();
actionData.MapHash = NetData.GetMapDataHash(Main.MapData);
actionData.Param = param;
actionData.ActionId = id;
var data = new ActionExcuteMessage();
data.ActionData = actionData;
// // TODO 这里是测试,每帧最多只发送一次
// if (RecordTime < Time.time)
// {
// data.Map = Main.MapData;
// RecordTime = Time.time + 0.2f;
// }
return BroadcastMessage(data);
}
// 请求回合结束 (成员 => 房主)
public void TurnEndConfirm()
{
var data = new TurnEndMessage();
data.PlayerId = Main.MapData.PlayerMap.SelfPlayerId;
SendMessage(data);
}
// 游戏数据心跳校验 (成员 => 房主)
public void MapConfirm()
{
var data = new MapConfirmMessage();
data.MemberId = LobbyManager.Instance.Lobby.GetSelfMemberId();
data.PlayerId = Main.MapData.PlayerMap.SelfPlayerId;
data.Index = Main.MapData.Net.Actions.Count;
if (data.Index > 0) data.ActionData = Main.MapData.Net.Actions[^1];
SendMessage(data);
}
// 游戏数据心跳校验 (房主 => 所有成员)
public void BroadcastMapConfirm()
{
var data = new MapConfirmMessage();
data.MemberId = LobbyManager.Instance.Lobby.GetSelfMemberId();
data.PlayerId = Main.MapData.PlayerMap.SelfPlayerId;
data.Index = Main.MapData.Net.Actions.Count;
if (data.Index > 0) data.ActionData = Main.MapData.Net.Actions[^1];
BroadcastMessage(data);
}
// 强制更新 (房主 => 单成员)
public void ForceUpdate(ulong memberId)
{
if (memberId == 0) return;
if (!TryGetValidMultiMapForBroadcast("ForceUpdate", out var mapData)) return;
var data = new ForceUpdateMessage();
data.MapData = mapData;
SendMessageToPlayer(memberId, data);
}
// 强制更新 (房主 => 所有成员)
public void BroadcastForceUpdate()
{
if (!TryGetValidMultiMapForBroadcast("BroadcastForceUpdate", out var mapData)) return;
var data = new ForceUpdateMessage();
data.MapData = mapData;
BroadcastMessage(data);
}
private bool TryGetValidMultiMapForBroadcast(string context, out MapData mapData)
{
mapData = Main.MapData;
if (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)
{
LogSystem.LogError($"{context}: 无效多人地图数据,取消发送");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.InvalidNetworkMapData);
mapData = null;
return false;
}
if (!mapData.Net.RefreshPlayerNet(mapData))
{
LogSystem.LogError($"{context}: 玩家网络映射失败,取消发送");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkPlayerMappingFailed);
mapData = null;
return false;
}
return true;
}
// 修改成员房间配置(阵营/准备状态,成员 => 房主)
public bool ChangeCiv(MemberCiv memberCiv)
{
if (memberCiv == null)
{
LogSystem.LogError($"Get Self MemberCiv Error ");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyMemberConfigFailed);
return false;
}
var data = new ChangeCivMessage();
data.Civ = memberCiv;
if (!SendMessage(data)) return false;
MarkLobbyDataSyncRequired();
return true;
}
// 更新房间配置 (房主 => 所有成员)
public void UpdateLobbyData(MapConfig config)
{
if (!LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
var data = new UpdateLobbyDataMessage();
data.Config = config;
BroadcastMessage(data);
}
// 请求更新房间配置 (单成员 => 房主)
public bool RequestLobbyData(bool force = false)
{
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)))
{
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyDataRequestFailed);
return false;
}
var now = UnityEngine.Time.time;
if (!force && now - _lastRequestLobbyDataTime < RequestLobbyDataCooldown) return false;
_lastRequestLobbyDataTime = now;
var data = new RequestLobbyDataMessage();
data.MemberId = LobbyManager.Instance.Lobby.GetSelfMemberId();
if (SendMessage(data)) return true;
_lastRequestLobbyDataTime = now - RequestLobbyDataCooldown + 0.2f;
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyDataRequestFailed);
return false;
}
public void MarkLobbyDataSyncRequired()
{
_needLobbyDataFromHost = true;
_lastRequestLobbyDataTime = -RequestLobbyDataCooldown;
}
public void MarkLobbyDataSyncedFromHost()
{
_needLobbyDataFromHost = false;
}
public void ClearLobbyDataSyncState()
{
_needLobbyDataFromHost = false;
_lastRequestLobbyDataTime = -RequestLobbyDataCooldown;
}
public bool NeedsLobbyDataFromHost()
{
return _needLobbyDataFromHost;
}
// 更新房间配置 (房主 => 单成员)
public void SendLobbyData(MapConfig config, ulong memberId)
{
if (!LobbyManager.Instance.Lobby.IsLobbyOwner() || memberId == 0) return;
var data = new UpdateLobbyDataMessage();
data.Config = config;
SendMessageToPlayer(memberId, data);
}
// 心跳 (单成员 => 房主) 成员的心跳包
public void SendHeartbeat()
{
var confirm = Main.Instance.ConfirmMap.GetPlayerConfirm(LobbyManager.Instance.Lobby.GetLobbyOwnerId());
// 发送
var data = new HeartbeatMessage();
data.MemberId = LobbyManager.Instance.Lobby.GetSelfMemberId();
data.State = Main.Instance.GameLogic.GetCurState();
if (SendMessage(data))
{
// 确认发送记录
confirm.OnSendHeartbeat();
}
}
// 成员状态下发 (房主 => 成员) 房主的心跳包
public void SendMemberStateSync()
{
if (Main.MapData == null) return;
// 确认发送记录:遍历 lobby 所有成员,保证每个客机的 confirm 都写入 PingRecordTime
// (字典中没有该成员条目时通过 GetPlayerConfirm 懒加载创建,避免首次发送时字典为空导致 ping 永远 = 0
var selfId = LobbyManager.Instance.Lobby.GetSelfMemberId();
foreach (var memberId in LobbyManager.Instance.Lobby.GetAllMemberIds())
{
if (memberId == selfId) continue;
Main.Instance.ConfirmMap.GetPlayerConfirm(memberId).OnSendHeartbeat();
}
// 发送
var data = new MemberStateSyncMessage();
data.State = Main.Instance.GameLogic.GetCurState();
data.MemberId = LobbyManager.Instance.Lobby.GetSelfMemberId();
data.PlayerConfirm = Main.Instance.ConfirmMap.PlayerConfirm;
BroadcastMessage(data);
}
// 请求重连 (单成员 => 房主)
public void SendRequestForceUpdate()
{
if (Main.Instance.GameLogic.GetCurState() == GameState.ForceUpdating) return;
var now = UnityEngine.Time.time;
if (now - _lastRequestForceUpdateTime < RequestForceUpdateCooldown)
{
LogSystem.LogWarning($"客户端请求重连冷却中: SendRequestForceUpdate, remain={RequestForceUpdateCooldown - (now - _lastRequestForceUpdateTime):F1}s");
return;
}
_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);
}
}
// 心跳包回复 (任意成员 => 任意成员)
public void SendHeartbeatReply(ulong memberId)
{
var data = new HeartbeatReplyMessage();
data.MemberId = LobbyManager.Instance.Lobby.GetSelfMemberId();
SendMessageToPlayer(memberId, data);
}
// 发送聊天内容 (单成员 => 房主)
public void SendChatMessage(ChatItem item)
{
var data = new ChatMessage();
data.Item = item;
SendMessage(data);
}
// 发送聊天内容 (房主 => 所有成员)
public void BroadcastChatMessage(ChatItem item)
{
var data = new ChatMessage();
data.Item = item;
BroadcastMessage(data);
}
}
}