using System; using System.Collections.Generic; using System.Runtime.InteropServices; using Logic.CrashSight; using Steamworks; using TH1_Logic.Net; using UnityEngine; namespace TH1_Logic.Steam { public class SimpleP2P { public static SimpleP2P Instance { get; } = new SimpleP2P(); // 推荐的虚拟端口范围 private static readonly int TargetPort = 1214; // 连接映射表:SteamID -> 连接句柄 private readonly Dictionary _connections = new Dictionary(); // 连接超时追踪 private readonly Dictionary _connectionTimeouts = new Dictionary(); private const float ConnectionTimeout = 30f; // 30秒超时 // 重连尝试计数 private readonly Dictionary _retryCount = new Dictionary(); private const int MaxRetryCount = 3; // 监听套接字 private float _socketRecord; private HSteamListenSocket _listenSocket; // 回调 private Callback _cbConnectionStatusChanged; // 事件委托 public event Action OnPeerConnectedEvent; public event Action OnPeerDisconnectedEvent; public event Action OnMessageReceivedEvent; public event Action OnConnectionErrorEvent; public bool IsInitialized => _listenSocket != HSteamListenSocket.Invalid; // 初始化 public void Initialize() { _socketRecord = 3; _listenSocket = HSteamListenSocket.Invalid; _cbConnectionStatusChanged = Callback.Create(OnConnectionStatusChanged); } // 创建监听套接字 private void CreateListenSocket() { ForceCleanupPort(TargetPort); LogSystem.LogInfo($"尝试虚拟端口 {TargetPort}..."); _listenSocket = SteamNetworkingSockets.CreateListenSocketP2P(TargetPort, 0, null); if (_listenSocket != HSteamListenSocket.Invalid) { LogSystem.LogInfo($"成功在虚拟端口 {TargetPort} 创建监听套接字: {_listenSocket}"); } } // 定时刷新套接字创建 private void RefreshListenSocket() { _socketRecord += Time.deltaTime; if (_listenSocket == HSteamListenSocket.Invalid && _socketRecord > 3f) { _socketRecord = 0; SteamNetworkingUtils.GetRelayNetworkStatus(out var relayStatus); if (relayStatus.m_eAvail == ESteamNetworkingAvailability.k_ESteamNetworkingAvailability_Current) { CreateListenSocket(); } } } private void ForceCleanupPort(int port) { LogSystem.LogInfo($"强制清理虚拟端口 {port}"); // 关闭所有现有连接 DisconnectAll(); // 关闭当前监听套接字 SteamNetworkingSockets.CloseListenSocket(_listenSocket); _listenSocket = HSteamListenSocket.Invalid; LogSystem.LogInfo("已关闭现有监听套接字"); // 强制清理特定端口的所有连接 SteamNetworkingSockets.RunCallbacks(); } // 连接到指定玩家 public bool ConnectToPeer(CSteamID steamID) { if (LobbyManager.Instance.Lobby.IsLobbyOwner()) return false; if (_connections.ContainsKey(steamID)) { LogSystem.LogInfo($"Already connected to {steamID}"); return true; } // 先进行连接前检查 if (!PreConnectionCheck(steamID)) { return false; } // 创建连接配置 var options = new SteamNetworkingConfigValue_t[1]; options[0] = new SteamNetworkingConfigValue_t(); options[0].m_eValue = ESteamNetworkingConfigValue.k_ESteamNetworkingConfig_TimeoutInitial; options[0].m_eDataType = ESteamNetworkingConfigDataType.k_ESteamNetworkingConfig_Int32; options[0].m_val.m_int32 = 15000; // 15秒超时 // 创建到目标玩家的连接 var identity = new SteamNetworkingIdentity(); identity.SetSteamID(steamID); var connection = SteamNetworkingSockets.ConnectP2P(ref identity, TargetPort, options.Length, options); if (connection == HSteamNetConnection.Invalid) { OnConnectionErrorEvent?.Invoke($"Failed to connect to {steamID}"); return false; } _connections[steamID] = connection; _connectionTimeouts[steamID] = Time.time + ConnectionTimeout; LogSystem.LogInfo($"Connecting to peer: {steamID}"); DiagnoseNetworkStatus(steamID); return true; } // 连接前检查 private bool PreConnectionCheck(CSteamID targetSteamID) { // 检查Steam是否已登录 if (!SteamUser.BLoggedOn()) { LogSystem.LogError("Steam not logged in"); return false; } // 检查目标用户是否在线 var friendState = SteamFriends.GetFriendPersonaState(targetSteamID); if (friendState == EPersonaState.k_EPersonaStateOffline) { LogSystem.LogWarning($"Target user {targetSteamID} appears offline"); } // 检查当前lobby信息 LogSystem.LogInfo("Checking lobby status..."); return true; } private void DiagnoseNetworkStatus(CSteamID targetSteamID) { LogSystem.LogInfo("=== P2P 连接诊断 ==="); // Steam 状态 LogSystem.LogInfo($"Steam 登录状态: {SteamUser.BLoggedOn()}"); LogSystem.LogInfo($"本地 Steam ID: {SteamUser.GetSteamID()}"); // 目标用户状态 var friendState = SteamFriends.GetFriendPersonaState(targetSteamID); LogSystem.LogInfo($"目标用户状态: {friendState}"); // 检查是否是好友 var relationShip = SteamFriends.GetFriendRelationship(targetSteamID); LogSystem.LogInfo($"好友关系: {relationShip}"); // Steam 网络状态 SteamNetworkingUtils.GetRelayNetworkStatus(out var relayStatus); LogSystem.LogInfo($"Steam 中继网络: {relayStatus.m_eAvail}"); LogSystem.LogInfo($"中继网络调试信息: {relayStatus.m_debugMsg}"); if (relayStatus.m_eAvail != ESteamNetworkingAvailability.k_ESteamNetworkingAvailability_Current) { LogSystem.LogWarning($"Steam 中继网络问题: {relayStatus.m_debugMsg}"); } } // 更新方法 - 需要在主循环中调用 public void Update() { RefreshListenSocket(); CheckConnectionTimeouts(); } // 检查连接超时 private void CheckConnectionTimeouts() { var currentTime = Time.time; var timeoutList = new List(); foreach (var kvp in _connectionTimeouts) { if (currentTime > kvp.Value) { timeoutList.Add(kvp.Key); } } foreach (var steamID in timeoutList) { LogSystem.LogError($"Connection to {steamID} timed out"); _connectionTimeouts.Remove(steamID); if (_connections.TryGetValue(steamID, out var conn)) { SteamNetworkingSockets.CloseConnection(conn, 0, "Connection timeout", false); _connections.Remove(steamID); } OnConnectionErrorEvent?.Invoke($"Connection to {steamID} timed out"); } } // 断开与指定玩家的连接 public void DisconnectFromPeer(CSteamID steamID) { if (!_connections.TryGetValue(steamID, out var connection)) return; SteamNetworkingSockets.CloseConnection(connection, 0, "Disconnected by user", false); _connections.Remove(steamID); _connectionTimeouts.Remove(steamID); LogSystem.LogInfo($"Disconnected from peer: {steamID}"); } // 断开所有连接 public void DisconnectAll() { foreach (var kvp in _connections) { SteamNetworkingSockets.CloseConnection(kvp.Value, 0, "Disconnecting all", false); } _connections.Clear(); _connectionTimeouts.Clear(); _retryCount.Clear(); LogSystem.LogInfo("Disconnected from all peers"); } // 连接状态变化回调 private void OnConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t info) { var state = info.m_info.m_eState; // 获取连接信息 SteamNetworkingSockets.GetConnectionInfo(info.m_hConn, out var connectionInfo); var remote = connectionInfo.m_identityRemote.GetSteamID(); // 清除超时计时器(如果连接状态有变化) if (_connectionTimeouts.ContainsKey(remote) && state != ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_Connecting) { _connectionTimeouts.Remove(remote); } // 记录详细的连接信息用于诊断 LogSystem.LogInfo($"Connection details - Remote: {remote}, State: {state}"); LogSystem.LogInfo($"End reason: {info.m_info.m_eEndReason}"); switch (state) { case ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_Connecting: LogSystem.LogInfo($"Connecting to {remote}..."); // 检查这是否是传入连接(对方主动连接我们) // 如果我们的连接字典中没有这个连接,说明是对方主动连接的 if (!_connections.ContainsKey(remote)) { LogSystem.LogInfo($"检测到来自 {remote} 的传入连接请求"); // 检查是否应该接受这个连接 if (ShouldAcceptIncomingConnection(remote)) { LogSystem.LogInfo($"接受来自 {remote} 的连接"); var result = SteamNetworkingSockets.AcceptConnection(info.m_hConn); if (result != EResult.k_EResultOK) { LogSystem.LogError($"无法接受连接: {result}"); SteamNetworkingSockets.CloseConnection(info.m_hConn, 0, "Failed to accept", false); return; } _connections[remote] = info.m_hConn; } else { LogSystem.LogInfo($"拒绝来自 {remote} 的连接"); SteamNetworkingSockets.CloseConnection(info.m_hConn, 0, "Connection rejected", false); } } else { LogSystem.LogInfo($"这是我们主动发起的连接到 {remote}"); } break; case ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_FindingRoute: LogSystem.LogInfo($"Finding route to {remote}..."); // 这个状态通常意味着正在通过Steam中继寻找路由 LogSystem.LogInfo("正在通过Steam中继网络寻找连接路径..."); break; case ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_Connected: LogSystem.LogInfo($"Successfully connected to {remote}"); if (!_connections.ContainsKey(remote)) { _connections[remote] = info.m_hConn; } // 清除超时和重试计数 _connectionTimeouts.Remove(remote); _retryCount.Remove(remote); OnPeerConnectedEvent?.Invoke(remote); break; case ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_ClosedByPeer: LogSystem.LogInfo($"Connection closed by peer: {remote}"); HandleDisconnection(remote, info.m_hConn); break; case ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_ProblemDetectedLocally: LogSystem.LogWarning($"Connection problem detected locally: {remote}"); LogSystem.LogWarning($"Problem details: {info.m_info.m_szEndDebug}"); HandleDisconnection(remote, info.m_hConn); break; case ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_None: // 检查具体的失败原因 var endReason = info.m_info.m_eEndReason; LogSystem.LogError($"Connection failed - Reason: {endReason}"); // 提供更详细的错误信息 switch (endReason) { case (int)ESteamNetConnectionEnd.k_ESteamNetConnectionEnd_Misc_Timeout: LogSystem.LogError("连接超时 - 可能的原因:1.目标用户不在线 2.网络问题 3.防火墙阻止"); break; case (int)ESteamNetConnectionEnd.k_ESteamNetConnectionEnd_Misc_InternalError: LogSystem.LogError("内部错误 - Steam网络服务可能有问题"); break; case (int)ESteamNetConnectionEnd.k_ESteamNetConnectionEnd_AppException_Generic: LogSystem.LogError("应用程序异常 - 目标应用可能没有正确处理连接"); break; case (int)ESteamNetConnectionEnd.k_ESteamNetConnectionEnd_Remote_Timeout: LogSystem.LogError("远程超时 - 目标用户网络问题"); break; default: if (endReason >= 1000 && endReason < 2000) { LogSystem.LogError($"应用层拒绝连接 - 错误码: {endReason},可能原因:1.对方未创建监听套接字 2.对方主动拒绝 3.对方游戏未运行"); } else { LogSystem.LogError($"未知连接失败原因: {endReason}"); } break; } HandleDisconnection(remote, info.m_hConn); break; } } // 检查是否应该接受这个连接 private bool ShouldAcceptIncomingConnection(CSteamID steamID) { // 基本检查:确保是有效的Steam ID if (!steamID.IsValid()) { LogSystem.LogWarning($"接收到无效的Steam ID连接请求: {steamID}"); return false; } // 检查是否超过最大连接数 if (_connections.Count >= 10) // 假设最大10个连接 { LogSystem.LogWarning("已达到最大连接数,拒绝新连接"); return false; } // 检查是否是好友(可选) var relationship = SteamFriends.GetFriendRelationship(steamID); LogSystem.LogInfo($"连接请求来自: {steamID}, 关系: {relationship}"); return true; // 默认接受所有连接 } // 处理断开连接 private void HandleDisconnection(CSteamID steamID, HSteamNetConnection connection) { if (_connections.ContainsKey(steamID)) { _connections.Remove(steamID); OnPeerDisconnectedEvent?.Invoke(steamID); } _connectionTimeouts.Remove(steamID); SteamNetworkingSockets.CloseConnection(connection, 0, "", false); } // 发送消息到指定玩家 public bool SendTo(CSteamID target, byte[] data, bool reliable = true, bool ordered = true) { if (data == null || data.Length == 0) { LogSystem.LogWarning("Trying to send null or empty data"); return false; } if (!_connections.TryGetValue(target, out var conn)) { LogSystem.LogWarning($"No connection to {target}"); return false; } // 可靠传输=8,不可靠传输=0 int flags = reliable ? 8 : 0; // 如果需要有序传输 k_nSteamNetworkingSend_NoDelay (确保按序处理) if (ordered) flags |= 1; IntPtr ptr = IntPtr.Zero; try { ptr = Marshal.AllocHGlobal(data.Length); Marshal.Copy(data, 0, ptr, data.Length); var result = SteamNetworkingSockets.SendMessageToConnection(conn, ptr, (uint)data.Length, flags, out _); if (result != EResult.k_EResultOK) { LogSystem.LogError($"Failed to send message to {target}: {result}"); return false; } return true; } catch (Exception e) { LogSystem.LogError($"Exception while sending message: {e.Message}"); return false; } finally { if (ptr != IntPtr.Zero) Marshal.FreeHGlobal(ptr); } } // 广播消息给所有连接的玩家 public void Broadcast(byte[] data, bool reliable = true) { foreach (var steamID in _connections.Keys) { SendTo(steamID, data, reliable); } } // 轮询接收消息 public void PollMessages() { // 为每个连接轮询消息 foreach (var kvp in _connections) { var steamID = kvp.Key; var connection = kvp.Value; PollMessagesForConnection(steamID, connection); } } // 为特定连接轮询消息 private void PollMessagesForConnection(CSteamID steamID, HSteamNetConnection connection) { IntPtr[] messages = new IntPtr[32]; // 一次最多处理32条消息 int messageCount = SteamNetworkingSockets.ReceiveMessagesOnConnection(connection, messages, 32); for (int i = 0; i < messageCount; i++) { var messagePtr = messages[i]; if (messagePtr == IntPtr.Zero) continue; // try // { // // 获取消息结构 // var message = Marshal.PtrToStructure(messagePtr); // // // 复制数据 // byte[] data = new byte[message.m_cbSize]; // Marshal.Copy(message.m_pData, data, 0, message.m_cbSize); // // // 触发接收事件 // GameNetReceiver.Instance.OnMessageReceived(data); // } // catch (Exception e) // { // LogSystem.LogError($"Error processing message from {steamID}: {e.Message}"); // } // finally // { // // 释放消息 - 这是关键,必须释放消息内存 // SteamNetworkingMessage_t.Release(messagePtr); // } // 获取消息结构 var message = Marshal.PtrToStructure(messagePtr); // 复制数据 byte[] data = new byte[message.m_cbSize]; Marshal.Copy(message.m_pData, data, 0, message.m_cbSize); // 触发接收事件 GameNetReceiver.Instance.OnMessageReceived(data); // 释放消息 - 这是关键,必须释放消息内存 SteamNetworkingMessage_t.Release(messagePtr); } } // 获取连接状态 public bool IsConnectedTo(CSteamID steamID) { return _connections.ContainsKey(steamID); } // 获取所有连接的玩家 public IEnumerable GetConnectedPeers() { return _connections.Keys; } // 获取连接数量 public int GetConnectionCount() { return _connections.Count; } // 清理资源 public void Cleanup() { DisconnectAll(); if (_listenSocket != HSteamListenSocket.Invalid) { SteamNetworkingSockets.CloseListenSocket(_listenSocket); _listenSocket = HSteamListenSocket.Invalid; } _cbConnectionStatusChanged?.Dispose(); _cbConnectionStatusChanged = null; } // 获取详细的连接信息用于调试 public void LogDetailedConnectionInfo(CSteamID steamID) { if (!_connections.TryGetValue(steamID, out var connection)) { LogSystem.LogWarning($"No connection found for {steamID}"); return; } if (SteamNetworkingSockets.GetConnectionInfo(connection, out var info)) { LogSystem.LogInfo("=== 详细连接信息 ==="); LogSystem.LogInfo($"连接状态: {info.m_eState}"); LogSystem.LogInfo($"结束原因: {info.m_eEndReason}"); LogSystem.LogInfo($"用户数据: {info.m_nUserData}"); LogSystem.LogInfo($"监听套接字: {info.m_hListenSocket}"); LogSystem.LogInfo($"远程地址: {info.m_addrRemote}"); LogSystem.LogInfo($"连接描述: {info.m_szConnectionDescription}"); LogSystem.LogInfo($"结束调试信息: {info.m_szEndDebug}"); LogSystem.LogInfo("==================="); } } } }