You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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之间的状态同步核心逻辑:

  1. Netcode依赖connectQueue/disconnectQueue感知连接/断开事件,但你没在PlayFab的连接回调里往这些队列推送事件
  2. 本地玩家(服务器host或客户端)的ClientID映射完全没建立,Netcode不知道本地客户端的身份
  3. 服务器启动和客户端连接时,缺少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感知到已连接服务器
  • 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 08:38:09