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
相关产品推荐
相关产品推荐

