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

Unity玩家用Alt+F4退出时未从Lobby移除的问题咨询

解决方案:Alt+F4强制退出后Lobby仍显示已满的问题

核心问题分析

当前代码存在三个关键问题导致强制退出后Lobby状态未更新:

  • Client ID与Lobby Player ID不匹配:你直接将NetworkManager的clientId.ToString()作为玩家ID传入移除逻辑,但Lobby中的Player.Id是玩家的认证ID(如Unity Authentication生成的ID),并非NetworkManager的客户端ID,导致找不到目标玩家。
  • 异步方法未正确等待:DisconnectPlayer使用async void修饰,在服务器断开回调中调用时不会等待异步操作完成,后续更新逻辑可能基于旧数据执行。
  • Lobby本地缓存未及时同步:即使调用了GetLobbyAsync,本地缓存的joinedLobby可能未及时刷新,导致可用插槽计算错误。

具体修复步骤

1. 建立Client ID到Lobby Player ID的映射

服务器需要维护客户端ID与玩家Lobby ID的对应关系,确保断开时能找到正确的玩家:

// 在MultiplayerManager中添加映射字典
private Dictionary<ulong, string> _clientToPlayerIdMap = new Dictionary<ulong, string>();

// 修改连接审批逻辑,记录映射关系
private void NetworkManager_ConnectionApprovalCallback(NetworkManager.ConnectionApprovalRequest request, NetworkManager.ConnectionApprovalResponse response)
{
    // 从请求载荷中获取玩家的Lobby ID(需在客户端连接时传入,示例代码需根据你的实际流程调整)
    string playerId = request.Payload.GetString("playerId");
    _clientToPlayerIdMap[request.ClientNetworkId] = playerId;

    response.Approved = true;
}

// 更新断开回调,使用正确的Player ID
private async void NetworkManager_Server_OnClientDisconnectCallback(ulong clientId)
{
    if (IsServer && _clientToPlayerIdMap.TryGetValue(clientId, out string playerId))
    {
        Debug.Log($"Client {clientId} (Player {playerId}) disconnected.");
        if (GameLobby.Instance != null)
        {
            await GameLobby.Instance.DisconnectPlayer(playerId);
        }
        _clientToPlayerIdMap.Remove(clientId); // 清理映射
    }
}

2. 修正异步方法的调用逻辑

将DisconnectPlayer改为async Task类型,确保异步操作能被正确等待:

public async Task DisconnectPlayer(string playerId)
{
    try
    {
        if (joinedLobby != null)
        {
            var targetPlayer = joinedLobby.Players.FirstOrDefault(p => p.Id == playerId);
            if (targetPlayer != null)
            {
                await LobbyService.Instance.RemovePlayerAsync(joinedLobby.Id, playerId);
                Debug.Log($"Player {playerId} removed from lobby.");
            }
            else
            {
                Debug.Log($"Player {playerId} not found in lobby, refreshing cache.");
                // 直接刷新Lobby缓存,避免旧数据干扰
                joinedLobby = await LobbyService.Instance.GetLobbyAsync(joinedLobby.Id);
            }

            // 刷新缓存后再更新插槽状态
            await UpdateLobbySlots();
        }
    }
    catch (LobbyServiceException e)
    {
        Debug.LogError($"Failed to remove player: {e.Message}");
    }
}

3. 确保插槽更新使用最新Lobby数据

在UpdateLobbySlots中先刷新Lobby缓存,再计算并更新可用插槽:

private async Task UpdateLobbySlots()
{
    try
    {
        // 先获取最新的Lobby状态
        joinedLobby = await LobbyService.Instance.GetLobbyAsync(joinedLobby.Id);
        int availableSlots = MultiplayerManager.MAX_PLAYER_AMOUNT - joinedLobby.Players.Count;

        await LobbyService.Instance.UpdateLobbyAsync(joinedLobby.Id, new UpdateLobbyOptions
        {
            Data = new Dictionary<string, DataObject>
            {
                { "availableSlots", new DataObject(DataObject.VisibilityOptions.Public, availableSlots.ToString()) }
            }
        });

        Debug.Log($"Lobby slots updated: {availableSlots} available.");
    }
    catch (LobbyServiceException e)
    {
        Debug.LogError($"Failed to update lobby slots: {e.Message}");
    }
}

4. 启用Lobby自动清理(可选)

Unity Lobby支持自动移除长时间无响应的玩家,创建Lobby时可添加超时设置:

// 创建Lobby时配置自动清理
var createOptions = new CreateLobbyOptions
{
    PlayerTimeoutInSeconds = 30, // 30秒无交互自动移除玩家
    MaxPlayers = MultiplayerManager.MAX_PLAYER_AMOUNT
};
joinedLobby = await LobbyService.Instance.CreateLobbyAsync("Your Lobby Name", createOptions);

内容的提问来源于stack exchange,提问作者michal czuluk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 22:09:55