Unity Netcode自定义PlayFab Party传输层连接异常:PlayFab Party已连接但Netcode无连接
Unity Netcode自定义PlayFab Party传输层连接异常:PlayFab Party已连接但Netcode无连接
看起来你在给Unity Netcode实现PlayFab Party自定义传输层时遇到了典型的“底层网络通了但上层框架没感知”的问题——PlayFab Party已经成功建立网络,但Netcode那边完全没检测到连接状态对吧?我帮你梳理下代码里的关键疏漏和修复方案,都是实战中踩过的坑:
核心问题分析
你的代码已经订阅了PlayFab Party的基础回调,但缺少Netcode与PlayFab Party之间的状态同步核心逻辑:
- Netcode依赖
connectQueue/disconnectQueue感知连接/断开事件,但你没在PlayFab的连接回调里往这些队列推送事件 - 本地玩家(服务器host或客户端)的ClientID映射完全没建立,Netcode不知道本地客户端的身份
- 服务器启动和客户端连接时,缺少PlayFab网络的创建/加入逻辑,只是做了登录,没真正建立网络链路
修复后的完整代码
using System; using System.Collections.Generic; using System.Linq; using PlayFab; using PlayFab.ClientModels; using PlayFab.MultiplayerModels; using Unity.Netcode; using UnityEngine; using PlayFab.Party; public class PlayFabPartyTransport : NetworkTransport { public string hostDescriptor; private PlayFabPlayer localPlayer; private readonly Queue<(ulong, ArraySegment<byte>)> receiveQueue = new(); private readonly Queue<ulong> disconnectQueue = new(); private readonly Queue<ulong> connectQueue = new(); private readonly Dictionary<PlayFabPlayer, ulong> playerToClientId = new(); private readonly Dictionary<ulong, PlayFabPlayer> clientIdToPlayer = new(); private ulong nextClientId = 2; // 0 = server, 1 = host-client, 2+ = clients private bool IsServer { get; set; } public override ulong ServerClientId => 0; public override void Initialize(NetworkManager networkManager = null) { Debug.Log("[PlayFabPartyTransport] Initializing transport"); var manager = PlayFabMultiplayerManager.Get(); if (manager == null) { Debug.LogError("[PlayFabPartyTransport] PlayFabMultiplayerManager.Get() returned null in Initialize."); return; } // 防止重复订阅,先取消再添加 manager.OnDataMessageReceived -= OnDataReceived; manager.OnRemotePlayerJoined -= OnPlayerJoined; manager.OnRemotePlayerLeft -= OnPlayerLeft; manager.OnNetworkJoined -= OnNetworkJoined; manager.OnNetworkLeft -= OnNetworkLeft; manager.OnDataMessageReceived += OnDataReceived; manager.OnRemotePlayerJoined += OnPlayerJoined; manager.OnRemotePlayerLeft += OnPlayerLeft; manager.OnNetworkJoined += OnNetworkJoined; manager.OnNetworkLeft += OnNetworkLeft; } public override void Shutdown() { Debug.Log("[PlayFabPartyTransport] Shutting down transport"); var manager = PlayFabMultiplayerManager.Get(); if (manager != null) { manager.OnDataMessageReceived -= OnDataReceived; manager.OnRemotePlayerJoined -= OnPlayerJoined; manager.OnRemotePlayerLeft -= OnPlayerLeft; manager.OnNetworkJoined -= OnNetworkJoined; manager.OnNetworkLeft -= OnNetworkLeft; } playerToClientId.Clear(); clientIdToPlayer.Clear(); connectQueue.Clear(); disconnectQueue.Clear(); receiveQueue.Clear(); } public override bool StartServer() { Debug.Log("[PlayFabPartyTransport] Starting server..."); IsServer = true; // 重置状态 playerToClientId.Clear(); clientIdToPlayer.Clear(); connectQueue.Clear(); disconnectQueue.Clear(); receiveQueue.Clear(); nextClientId = 2; var request = new LoginWithCustomIDRequest { CustomId = $"{SystemInfo.deviceUniqueIdentifier}_0", CreateAccount = true }; PlayFabClientAPI.LoginWithCustomID(request, OnLoginSuccessServer, OnLoginFailureServer); return true; } private void OnLoginSuccessServer(LoginResult result) { Debug.Log("[PlayFabPartyTransport] Server login successful, creating network..."); // 创建PlayFab Party网络 var networkConfig = new PlayFabMultiplayerNetworkConfig { NetworkName = "MyGameNetwork_" + Guid.NewGuid(), IsServer = true }; PlayFabMultiplayerManager.Get().CreateAndJoinNetwork(networkConfig); } private void OnLoginFailureServer(PlayFabError error) { Debug.LogError($"[PlayFabPartyTransport] Server login failed: {error.ErrorMessage}"); } public override bool StartClient() { Debug.Log("[PlayFabPartyTransport] Starting client..."); IsServer = false; var request = new LoginWithCustomIDRequest { CustomId = $"{SystemInfo.deviceUniqueIdentifier}_{DateTime.UtcNow.Ticks}", CreateAccount = true }; PlayFabClientAPI.LoginWithCustomID(request, OnLoginSuccessClient, OnLoginFailureClient); return true; } private void OnLoginSuccessClient(LoginResult result) { Debug.Log("[PlayFabPartyTransport] Client login successful, joining network..."); if (string.IsNullOrEmpty(hostDescriptor)) { Debug.LogError("[PlayFabPartyTransport] hostDescriptor is empty, can't join network!"); return; } // 使用hostDescriptor加入服务器网络 PlayFabMultiplayerManager.Get().JoinNetwork(hostDescriptor); } private void OnLoginFailureClient(PlayFabError error) { Debug.LogError($"[PlayFabPartyTransport] Client login failed: {error.ErrorMessage}"); } public override void DisconnectLocalClient() { foreach (var kvp in playerToClientId) { disconnectQueue.Enqueue(kvp.Value); } playerToClientId.Clear(); clientIdToPlayer.Clear(); PlayFabMultiplayerManager.Get().LeaveNetwork(); } public override void DisconnectRemoteClient(ulong clientId) { if (clientIdToPlayer.TryGetValue(clientId, out var player) && player != null) { // 通知PlayFab踢人(如果是服务器) if (IsServer) { PlayFabMultiplayerManager.Get().RemovePlayerFromNetwork(player); } // 更新映射和断开队列 playerToClientId.Remove(player); clientIdToPlayer.Remove(clientId); disconnectQueue.Enqueue(clientId); } } public override void Send(ulong clientId, ArraySegment<byte> payload, NetworkDelivery delivery) { Debug.Log($"[PlayFabPartyTransport] Send - ClientId: {clientId}, Size: {payload.Count}"); var manager = PlayFabMultiplayerManager.Get(); if (manager == null) return; if (IsServer) { // 回环到host客户端 if (clientId == 1) { receiveQueue.Enqueue((1, payload)); return; } // 回环到服务器自己 if (clientId == ServerClientId) { receiveQueue.Enqueue((ServerClientId, payload)); return; } // 发送给远程客户端 if (clientIdToPlayer.TryGetValue(clientId, out var player)) { manager.SendDataMessage(payload.ToArray(), new[] { player }, MapDelivery(delivery)); } } else { // 客户端发送给服务器 if (clientIdToPlayer.TryGetValue(ServerClientId, out var serverPlayer)) { manager.SendDataMessage(payload.ToArray(), new[] { serverPlayer }, MapDelivery(delivery)); } } } public override NetworkEvent PollEvent(out ulong clientId, out ArraySegment<byte> payload, out float receiveTime) { receiveTime = Time.realtimeSinceStartup; payload = default; clientId = 0; // 处理连接事件 if (connectQueue.Count > 0) { clientId = connectQueue.Dequeue(); return NetworkEvent.Connect; } // 处理数据事件 if (receiveQueue.Count > 0) { var (cid, data) = receiveQueue.Dequeue(); clientId = cid; payload = data; return NetworkEvent.Data; } // 处理断开事件 if (disconnectQueue.Count > 0) { clientId = disconnectQueue.Dequeue(); return NetworkEvent.Disconnect; } return NetworkEvent.Nothing; } private void OnDataReceived(PlayFabPlayer sender, byte[] data) { Debug.Log($"[PlayFabPartyTransport] Data received from player: {sender.PlayerId}"); if (playerToClientId.TryGetValue(sender, out var clientId)) { receiveQueue.Enqueue((clientId, new ArraySegment<byte>(data))); } else { Debug.LogWarning($"[PlayFabPartyTransport] Received data from unknown player: {sender.PlayerId}"); } } private void OnPlayerJoined(PlayFabPlayer newPlayer) { Debug.Log($"[PlayFabPartyTransport] Remote player joined: {newPlayer.PlayerId}"); if (IsServer) { // 给新玩家分配ClientID var clientId = nextClientId++; playerToClientId[newPlayer] = clientId; clientIdToPlayer[clientId] = newPlayer; // 通知Netcode有新客户端连接 connectQueue.Enqueue(clientId); Debug.Log($"[PlayFabPartyTransport] Assigned ClientID {clientId} to player {newPlayer.PlayerId}"); } } private void OnPlayerLeft(PlayFabPlayer leftPlayer) { Debug.Log($"[PlayFabPartyTransport] Remote player left: {leftPlayer.PlayerId}"); if (playerToClientId.TryGetValue(leftPlayer, out var clientId)) { // 更新映射和断开队列 playerToClientId.Remove(leftPlayer); clientIdToPlayer.Remove(clientId); disconnectQueue.Enqueue(clientId); } } private void OnNetworkJoined() { Debug.Log("[PlayFabPartyTransport] Joined network successfully"); var manager = PlayFabMultiplayerManager.Get(); if (manager == null) return; localPlayer = manager.LocalPlayer; if (localPlayer == null) { Debug.LogError("[PlayFabPartyTransport] Local player is null after joining network!"); return; } if (IsServer) { // 服务器作为host,对应ClientID 1 playerToClientId[localPlayer] = 1; clientIdToPlayer[1] = localPlayer; // 通知Netcode host客户端已连接 connectQueue.Enqueue(1); // 保存网络描述符,给客户端用 hostDescriptor = manager.NetworkDescriptor; Debug.Log($"[PlayFabPartyTransport] Server is host, ClientID: 1, NetworkDescriptor: {hostDescriptor}"); } else { // 客户端需要映射服务器的ClientID 0到服务器玩家 var serverPlayer = manager.Network.RemotePlayers.FirstOrDefault(p => p.IsServer); if (serverPlayer != null) { playerToClientId[serverPlayer] = ServerClientId; clientIdToPlayer[ServerClientId] = serverPlayer; // 通知Netcode已连接到服务器 connectQueue.Enqueue(ServerClientId); Debug.Log($"[PlayFabPartyTransport] Client connected to server, Server ClientID: 0"); } else { Debug.LogError("[PlayFabPartyTransport] Could not find server player in network!"); } } } private void OnNetworkLeft() { Debug.Log("[PlayFabPartyTransport] Left network"); // 通知Netcode所有连接断开 foreach (var kvp in playerToClientId) { disconnectQueue.Enqueue(kvp.Value); } playerToClientId.Clear(); clientIdToPlayer.Clear(); } private PlayFabPartyDataMessagePriority MapDelivery(NetworkDelivery delivery) { return delivery switch { NetworkDelivery.Unreliable => PlayFabPartyDataMessagePriority.Unreliable, NetworkDelivery.UnreliableSequenced => PlayFabPartyDataMessagePriority.UnreliableSequenced, NetworkDelivery.Reliable => PlayFabPartyDataMessagePriority.Reliable, NetworkDelivery.ReliableSequenced => PlayFabPartyDataMessagePriority.ReliableSequenced, NetworkDelivery.ReliableFragmentedSequenced => PlayFabPartyDataMessagePriority.Reliable, _ => PlayFabPartyDataMessagePriority.Reliable }; } }
关键修复点解释
1. 补全登录后的网络逻辑
- 服务器登录成功后调用
CreateAndJoinNetwork真正创建PlayFab网络,同时生成hostDescriptor供客户端加入 - 客户端登录成功后用
hostDescriptor调用JoinNetwork,连接到指定服务器网络
2. 同步连接事件到Netcode
OnNetworkJoined中:- 服务器模式下,建立本地玩家(host)与ClientID 1的映射,并将1推入
connectQueue,让Netcode感知到host客户端的连接 - 客户端模式下,找到服务器玩家并建立ClientID 0的映射,将0推入
connectQueue,让Netcode感知到已连接服务器
- 服务器模式下,建立本地玩家(host)与ClientID 1的映射,并将1推入
OnPlayerJoined中:服务器给新玩家分配ClientID并推入connectQueue,Netcode就能检测到新客户端连接
3. 完善断开事件处理
OnPlayerLeft中更新映射并将ClientID推入disconnectQueue,Netcode会通过PollEvent检测到断开OnNetworkLeft中清空所有映射并推送断开事件,处理网络异常断开的情况
4. 修正数据发送逻辑
- 补全了PlayFabMultiplayerManager的空判断,避免空指针异常
- 完善了Netcode的
NetworkDelivery到PlayFab Party优先级的映射,确保传输可靠性对齐
额外注意事项
- 确保你已经在Unity中正确配置了PlayFab Multiplayer SDK,包括
PlayFabMultiplayerManager组件已添加到场景中 - 服务器启动后,需要把
hostDescriptor传递给客户端(比如通过PlayFab Lobby或者匹配服务),客户端才能用这个描述符加入网络 - 测试时先启动服务器,获取
hostDescriptor后再启动客户端,填入hostDescriptor字段
内容来源于stack exchange
相关产品推荐
相关产品推荐

