fix: improve minigame reconnect flow
This commit is contained in:
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using XGame.MiniGame;
|
||||
using UObject = UnityEngine.Object;
|
||||
|
||||
namespace XGame
|
||||
@@ -35,6 +36,11 @@ namespace XGame
|
||||
get { return currentConfig == null ? string.Empty : currentConfig.id; }
|
||||
}
|
||||
|
||||
public GameObject StageIsolationUiRoot
|
||||
{
|
||||
get { return uiPanel; }
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
LoadConfig();
|
||||
@@ -178,6 +184,15 @@ namespace XGame
|
||||
|
||||
CacheUI();
|
||||
BuildCharacterButtons();
|
||||
ApplyStageIsolationState();
|
||||
}
|
||||
|
||||
public void ApplyStageIsolationState()
|
||||
{
|
||||
if (uiPanel != null && MiniGameStageIsolation.HasSuspendedLobby())
|
||||
{
|
||||
uiPanel.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void CacheUI()
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using XGame.MiniGame;
|
||||
using XWorld.Framework;
|
||||
using XWorld.Framework.Protocol;
|
||||
using UObject = UnityEngine.Object;
|
||||
|
||||
@@ -37,13 +38,19 @@ namespace XGame
|
||||
private XWebSocket socket;
|
||||
private MiniGameNetChannel lobbyNet;
|
||||
private MiniGameHost gameHost;
|
||||
private MiniGameStageIsolation stageIsolation;
|
||||
private LobbyWorldController lobbyWorld;
|
||||
private WorldChatModule worldChat;
|
||||
private readonly List<GameInfoMsg> games = new List<GameInfoMsg>();
|
||||
private readonly List<NetMessage> pendingGameMessages = new List<NetMessage>();
|
||||
private readonly HashSet<int> canceledMatchRequestIds = new HashSet<int>();
|
||||
private bool lobbyActive;
|
||||
private bool openingLogin;
|
||||
private bool openingLobby;
|
||||
private bool openingWaiting;
|
||||
private bool matchRequestActive;
|
||||
private int nextMatchRequestId;
|
||||
private int currentMatchRequestId;
|
||||
private string matchMessage = string.Empty;
|
||||
private string pendingGameId = string.Empty;
|
||||
private int pendingGameVersion;
|
||||
@@ -409,6 +416,7 @@ namespace XGame
|
||||
|
||||
socket = sock;
|
||||
lobbyNet = new MiniGameNetChannel(sock);
|
||||
lobbyNet.OnGameMessage = OnLobbyGameMessage;
|
||||
lobbyNet.OnFrameworkFrame = OnLobbyFrameworkFrame;
|
||||
lobbyActive = true;
|
||||
SetLobbyStatus("Lobby connected.");
|
||||
@@ -455,6 +463,9 @@ namespace XGame
|
||||
|
||||
private void OnMatchClicked(string gameId)
|
||||
{
|
||||
matchRequestActive = true;
|
||||
currentMatchRequestId = ++nextMatchRequestId;
|
||||
canceledMatchRequestIds.Remove(currentMatchRequestId);
|
||||
matchMessage = "Matching...";
|
||||
pendingGameId = string.Empty;
|
||||
pendingGameVersion = 0;
|
||||
@@ -468,7 +479,7 @@ namespace XGame
|
||||
|
||||
if (string.IsNullOrEmpty(gameId))
|
||||
{
|
||||
lobbyNet.SendFramework(FrameworkOpcode.RandomMatchRequest, Array.Empty<byte>());
|
||||
lobbyNet.SendFramework(FrameworkOpcode.RandomMatchRequest, new MatchControlMsg { RequestId = currentMatchRequestId }.Encode());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -483,7 +494,7 @@ namespace XGame
|
||||
}
|
||||
pendingGameId = gameId;
|
||||
pendingGameVersion = version;
|
||||
lobbyNet.SendFramework(FrameworkOpcode.MatchRequest, new MatchRequestMsg { GameId = gameId, Version = version }.Encode());
|
||||
lobbyNet.SendFramework(FrameworkOpcode.MatchRequest, new MatchRequestMsg { GameId = gameId, Version = version, RequestId = currentMatchRequestId }.Encode());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,6 +541,8 @@ namespace XGame
|
||||
|
||||
private void OnCloseWaiting()
|
||||
{
|
||||
CancelCurrentMatchRequest();
|
||||
matchRequestActive = false;
|
||||
matchMessage = string.Empty;
|
||||
pendingGameId = string.Empty;
|
||||
pendingGameVersion = 0;
|
||||
@@ -537,6 +550,34 @@ namespace XGame
|
||||
OpenLobby();
|
||||
}
|
||||
|
||||
private void CancelPendingMatch()
|
||||
=> CancelPendingMatch(currentMatchRequestId);
|
||||
|
||||
private void CancelCurrentMatchRequest()
|
||||
{
|
||||
if (currentMatchRequestId != 0)
|
||||
{
|
||||
canceledMatchRequestIds.Add(currentMatchRequestId);
|
||||
}
|
||||
CancelPendingMatch(currentMatchRequestId);
|
||||
}
|
||||
|
||||
private void CancelPendingMatch(int requestId)
|
||||
{
|
||||
if (lobbyNet == null || !lobbyNet.IsConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lobbyNet.SendFramework(FrameworkOpcode.MatchCancel, new MatchControlMsg { RequestId = requestId }.Encode());
|
||||
}
|
||||
|
||||
private bool IsCurrentMatchResponse(int requestId)
|
||||
=> matchRequestActive && (requestId == 0 || requestId == currentMatchRequestId);
|
||||
|
||||
private bool IsCanceledMatchResponse(int requestId)
|
||||
=> requestId != 0 && canceledMatchRequestIds.Contains(requestId);
|
||||
|
||||
private void OnLobbyFrameworkFrame(Frame frame)
|
||||
{
|
||||
switch ((FrameworkOpcode)frame.Opcode)
|
||||
@@ -549,6 +590,16 @@ namespace XGame
|
||||
break;
|
||||
case FrameworkOpcode.MatchAssigned:
|
||||
MatchAssignedMsg assigned = MatchAssignedMsg.Decode(frame.Payload);
|
||||
if (IsCanceledMatchResponse(assigned.RequestId))
|
||||
{
|
||||
Debug.Log("[CSharpClientApp] ignore canceled match assigned req=" + assigned.RequestId);
|
||||
break;
|
||||
}
|
||||
if (!IsCurrentMatchResponse(assigned.RequestId))
|
||||
{
|
||||
Debug.Log("[CSharpClientApp] ignore stale match assigned req=" + assigned.RequestId);
|
||||
break;
|
||||
}
|
||||
pendingGameId = assigned.GameId;
|
||||
pendingGameVersion = assigned.Version;
|
||||
Debug.Log("[CSharpClientApp] match assigned " + pendingGameId + "@" + pendingGameVersion);
|
||||
@@ -556,10 +607,31 @@ namespace XGame
|
||||
break;
|
||||
case FrameworkOpcode.MatchFound:
|
||||
MatchFoundMsg found = MatchFoundMsg.Decode(frame.Payload);
|
||||
if (IsCanceledMatchResponse(found.RequestId))
|
||||
{
|
||||
Debug.Log("[CSharpClientApp] ignore canceled match found req=" + found.RequestId);
|
||||
CancelPendingMatch(found.RequestId);
|
||||
break;
|
||||
}
|
||||
if (!matchRequestActive && !string.IsNullOrEmpty(found.GameId))
|
||||
{
|
||||
Debug.Log("[CSharpClientApp] resume game room=" + found.RoomId + " game=" + found.GameId + "@" + found.Version);
|
||||
EnterAssignedGame(found.GameId, found.Version, found);
|
||||
break;
|
||||
}
|
||||
if (!IsCurrentMatchResponse(found.RequestId))
|
||||
{
|
||||
Debug.Log("[CSharpClientApp] ignore stale match found req=" + found.RequestId);
|
||||
if (found.RequestId != 0)
|
||||
{
|
||||
CancelPendingMatch(found.RequestId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
Debug.Log("[CSharpClientApp] match found room=" + found.RoomId + " self=" + found.SelfPlayerId);
|
||||
if (string.IsNullOrEmpty(pendingGameId))
|
||||
{
|
||||
SetWaitingStatus("Room ready, but no game was assigned.", true);
|
||||
AbortMatchAndReturnLobby("Match expired, please retry.", found.RequestId);
|
||||
break;
|
||||
}
|
||||
SetWaitingStatus("Room ready.", false);
|
||||
@@ -567,6 +639,16 @@ namespace XGame
|
||||
break;
|
||||
case FrameworkOpcode.Error:
|
||||
ErrorMsg err = ErrorMsg.Decode(frame.Payload);
|
||||
if (IsCanceledMatchResponse(err.RequestId))
|
||||
{
|
||||
Debug.Log("[CSharpClientApp] ignore canceled match error req=" + err.RequestId);
|
||||
break;
|
||||
}
|
||||
if (!IsCurrentMatchResponse(err.RequestId))
|
||||
{
|
||||
Debug.Log("[CSharpClientApp] ignore stale match error req=" + err.RequestId);
|
||||
break;
|
||||
}
|
||||
pendingGameId = string.Empty;
|
||||
pendingGameVersion = 0;
|
||||
SetWaitingStatus("Server error " + err.Code + ": " + err.Message, true);
|
||||
@@ -590,11 +672,28 @@ namespace XGame
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLobbyGameMessage(NetMessage message)
|
||||
{
|
||||
if (gameHost != null)
|
||||
{
|
||||
gameHost.QueueInitialMessage(message);
|
||||
return;
|
||||
}
|
||||
|
||||
pendingGameMessages.Add(message);
|
||||
if (pendingGameMessages.Count > 16)
|
||||
{
|
||||
pendingGameMessages.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnterAssignedGame(string gameId, int version, MatchFoundMsg found)
|
||||
{
|
||||
matchRequestActive = false;
|
||||
pendingGameId = string.Empty;
|
||||
pendingGameVersion = 0;
|
||||
lobbyActive = false;
|
||||
SuspendLobbyStage();
|
||||
lobbyWorld?.SendLeave();
|
||||
CloseWorldChat();
|
||||
lobbyNet = null;
|
||||
@@ -607,17 +706,73 @@ namespace XGame
|
||||
}
|
||||
gameHost.OnGameExited = () =>
|
||||
{
|
||||
ResumeLobbyStage();
|
||||
matchMessage = "Returned to lobby.";
|
||||
lobbyNet = new MiniGameNetChannel(socket);
|
||||
lobbyNet.OnGameMessage = OnLobbyGameMessage;
|
||||
lobbyNet.OnFrameworkFrame = OnLobbyFrameworkFrame;
|
||||
lobbyActive = true;
|
||||
OpenLobby();
|
||||
JoinLobbyWorld();
|
||||
RequestGameList();
|
||||
};
|
||||
gameHost.EnterGame(ResolvedCdnBase(), gameId, version, socket, false);
|
||||
// 大厅链路 MatchFound 由大厅通道消费,宿主收不到,须显式注入房间信息(结算弹框判胜负用)
|
||||
gameHost.SetMatchInfo(found.RoomId, found.SelfPlayerId);
|
||||
gameHost.SetMatchInfo(found.RoomId, found.SelfPlayerId, found.Players);
|
||||
gameHost.EnterGame(ResolvedCdnBase(), gameId, version, socket, false);
|
||||
for (int i = 0; i < pendingGameMessages.Count; i++)
|
||||
{
|
||||
gameHost.QueueInitialMessage(pendingGameMessages[i]);
|
||||
}
|
||||
pendingGameMessages.Clear();
|
||||
}
|
||||
|
||||
private void AbortMatchAndReturnLobby(string message, int requestId)
|
||||
{
|
||||
int cancelRequestId = requestId != 0 ? requestId : currentMatchRequestId;
|
||||
if (cancelRequestId != 0)
|
||||
{
|
||||
canceledMatchRequestIds.Add(cancelRequestId);
|
||||
}
|
||||
CancelPendingMatch(cancelRequestId);
|
||||
matchRequestActive = false;
|
||||
pendingGameId = string.Empty;
|
||||
pendingGameVersion = 0;
|
||||
CloseWaiting();
|
||||
OpenLobby();
|
||||
SetLobbyStatus(message);
|
||||
}
|
||||
|
||||
private MiniGameStageIsolation ResolveStageIsolation()
|
||||
{
|
||||
if (stageIsolation != null)
|
||||
{
|
||||
return stageIsolation;
|
||||
}
|
||||
|
||||
stageIsolation = GetComponent<MiniGameStageIsolation>();
|
||||
if (stageIsolation == null)
|
||||
{
|
||||
stageIsolation = gameObject.AddComponent<MiniGameStageIsolation>();
|
||||
}
|
||||
return stageIsolation;
|
||||
}
|
||||
|
||||
private void SuspendLobbyStage()
|
||||
{
|
||||
MiniGameStageIsolation isolation = ResolveStageIsolation();
|
||||
if (isolation != null)
|
||||
{
|
||||
isolation.SuspendLobbyForMiniGame();
|
||||
}
|
||||
}
|
||||
|
||||
private void ResumeLobbyStage()
|
||||
{
|
||||
MiniGameStageIsolation isolation = ResolveStageIsolation();
|
||||
if (isolation != null)
|
||||
{
|
||||
isolation.ResumeLobbyAfterMiniGame();
|
||||
}
|
||||
}
|
||||
|
||||
private string ResolvedCdnBase()
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace XGame.MiniGame
|
||||
private GameClientCtx _ctx;
|
||||
private MiniGameAssetLoader _assets;
|
||||
private RoomPlayerService _players;
|
||||
private readonly List<NetMessage> _queuedInitialMessages = new List<NetMessage>();
|
||||
private bool _running;
|
||||
private bool _waitingForResultConfirm;
|
||||
private string _gameId;
|
||||
@@ -38,10 +39,22 @@ namespace XGame.MiniGame
|
||||
public void EnterGame(string cdnBase, string gameId, int version, XWebSocket sock, bool sendMatchRequest)
|
||||
{
|
||||
_gameId = gameId; _version = version;
|
||||
_queuedInitialMessages.Clear();
|
||||
// FIX: XWCoroutine.StartCo is a static method (not Instance.StartCo)
|
||||
XWCoroutine.StartCo(CoEnter(cdnBase, gameId, version, sock, sendMatchRequest));
|
||||
}
|
||||
|
||||
public void QueueInitialMessage(NetMessage message)
|
||||
{
|
||||
if (_client != null)
|
||||
{
|
||||
SafeCall(() => _client.OnNetMessage(message), "OnNetMessage");
|
||||
return;
|
||||
}
|
||||
|
||||
_queuedInitialMessages.Add(message);
|
||||
}
|
||||
|
||||
// 大厅链路:MatchFound 由大厅通道消费(sendMatchRequest=false 时宿主收不到),
|
||||
// 由外部把房间信息注入,否则结算弹框无法判断胜负(_selfPlayerId 恒 0)。
|
||||
public void SetMatchInfo(string roomId, int selfPlayerId)
|
||||
@@ -122,6 +135,7 @@ namespace XGame.MiniGame
|
||||
try
|
||||
{
|
||||
_client.OnEnter(_ctx);
|
||||
FlushQueuedInitialMessages();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -165,6 +179,7 @@ namespace XGame.MiniGame
|
||||
public void ExitGame()
|
||||
{
|
||||
if (!_running && _client == null) return;
|
||||
_queuedInitialMessages.Clear();
|
||||
DestroyResultDialog();
|
||||
_waitingForResultConfirm = false;
|
||||
_running = false;
|
||||
@@ -183,6 +198,7 @@ namespace XGame.MiniGame
|
||||
|
||||
private void FailEnterAndExit(string reason)
|
||||
{
|
||||
_queuedInitialMessages.Clear();
|
||||
DestroyResultDialog();
|
||||
_waitingForResultConfirm = false;
|
||||
_running = false;
|
||||
@@ -443,6 +459,18 @@ namespace XGame.MiniGame
|
||||
return button;
|
||||
}
|
||||
|
||||
private void FlushQueuedInitialMessages()
|
||||
{
|
||||
if (_client == null || _queuedInitialMessages.Count == 0) return;
|
||||
NetMessage[] messages = _queuedInitialMessages.ToArray();
|
||||
_queuedInitialMessages.Clear();
|
||||
for (int i = 0; i < messages.Length; i++)
|
||||
{
|
||||
NetMessage message = messages[i];
|
||||
SafeCall(() => _client.OnNetMessage(message), "OnNetMessage");
|
||||
}
|
||||
}
|
||||
|
||||
private static void SafeCall(Action a, string where)
|
||||
{
|
||||
try { a(); }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using XGame;
|
||||
|
||||
namespace XGame.MiniGame
|
||||
{
|
||||
@@ -17,6 +18,19 @@ namespace XGame.MiniGame
|
||||
|
||||
public bool IsSuspended { get; private set; }
|
||||
|
||||
public static bool HasSuspendedLobby()
|
||||
{
|
||||
MiniGameStageIsolation[] isolations = FindObjectsOfType<MiniGameStageIsolation>(true);
|
||||
for (int i = 0; i < isolations.Length; i++)
|
||||
{
|
||||
if (isolations[i] != null && isolations[i].IsSuspended)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SuspendLobbyForMiniGame()
|
||||
{
|
||||
if (IsSuspended)
|
||||
@@ -77,6 +91,7 @@ namespace XGame.MiniGame
|
||||
_behaviourStates.Clear();
|
||||
_canvasGroupStates.Clear();
|
||||
CaptureGameObjects(LobbyUiRoots);
|
||||
CaptureCharacterSwitchUiRoots();
|
||||
CaptureGameObjects(LobbySceneRoots);
|
||||
CaptureBehaviours(LobbyInputBehaviours);
|
||||
CaptureBehaviours(LobbyTickBehaviours);
|
||||
@@ -86,6 +101,7 @@ namespace XGame.MiniGame
|
||||
private void ApplySuspendedState()
|
||||
{
|
||||
SetGameObjectsActive(LobbyUiRoots, false);
|
||||
SetCharacterSwitchUiRootsActive(false);
|
||||
SetGameObjectsActive(LobbySceneRoots, false);
|
||||
SetBehavioursEnabled(LobbyInputBehaviours, false);
|
||||
SetBehavioursEnabled(LobbyTickBehaviours, false);
|
||||
@@ -105,6 +121,19 @@ namespace XGame.MiniGame
|
||||
}
|
||||
}
|
||||
|
||||
private void CaptureCharacterSwitchUiRoots()
|
||||
{
|
||||
CharacterSwitchController[] controllers = FindObjectsOfType<CharacterSwitchController>(true);
|
||||
for (int i = 0; i < controllers.Length; i++)
|
||||
{
|
||||
GameObject target = controllers[i] != null ? controllers[i].StageIsolationUiRoot : null;
|
||||
if (target != null)
|
||||
{
|
||||
_gameObjectStates.Add(new GameObjectState(target, target.activeSelf));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CaptureBehaviours(Behaviour[] targets)
|
||||
{
|
||||
if (targets == null) return;
|
||||
@@ -143,6 +172,19 @@ namespace XGame.MiniGame
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetCharacterSwitchUiRootsActive(bool active)
|
||||
{
|
||||
CharacterSwitchController[] controllers = FindObjectsOfType<CharacterSwitchController>(true);
|
||||
for (int i = 0; i < controllers.Length; i++)
|
||||
{
|
||||
GameObject target = controllers[i] != null ? controllers[i].StageIsolationUiRoot : null;
|
||||
if (target != null)
|
||||
{
|
||||
target.SetActive(active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetBehavioursEnabled(Behaviour[] targets, bool enabled)
|
||||
{
|
||||
if (targets == null) return;
|
||||
|
||||
Reference in New Issue
Block a user