fix: improve minigame reconnect flow

This commit is contained in:
ud18010
2026-07-29 19:30:50 +08:00
parent 6a5e534d00
commit 42c3874d82
21 changed files with 979 additions and 52 deletions
@@ -0,0 +1,30 @@
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
public sealed class MatchWaitingPrefabLayoutTests
{
private const string PrefabPath = "Assets/Game/Art/UI/Prefab/UI_MatchWaiting.prefab";
[Test]
public void WaitingPanel_IsCenteredInSafeArea()
{
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(PrefabPath);
Assert.That(prefab, Is.Not.Null);
RectTransform root = prefab.GetComponent<RectTransform>();
Assert.That(root.anchorMin, Is.EqualTo(Vector2.zero));
Assert.That(root.anchorMax, Is.EqualTo(Vector2.one));
Assert.That(root.anchoredPosition, Is.EqualTo(Vector2.zero));
Assert.That(root.sizeDelta, Is.EqualTo(Vector2.zero));
Transform panel = prefab.transform.Find("SafeArea/Panel_Waiting");
Assert.That(panel, Is.Not.Null);
RectTransform rect = panel.GetComponent<RectTransform>();
Assert.That(rect.anchorMin, Is.EqualTo(new Vector2(0.5f, 0.5f)));
Assert.That(rect.anchorMax, Is.EqualTo(new Vector2(0.5f, 0.5f)));
Assert.That(rect.pivot, Is.EqualTo(new Vector2(0.5f, 0.5f)));
Assert.That(rect.anchoredPosition, Is.EqualTo(Vector2.zero));
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7b43c5afbe3e421488be033e25c0c28a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,5 +1,7 @@
using System.Reflection;
using NUnit.Framework;
using UnityEngine;
using XGame;
using XGame.MiniGame;
public sealed class MiniGameStageIsolationTests
@@ -96,6 +98,51 @@ public sealed class MiniGameStageIsolationTests
Assert.IsTrue(behaviour.enabled);
}
[Test]
public void SuspendAndResume_AutoCapturesCharacterSwitchUiRoot()
{
_root = new GameObject("root");
GameObject characterObject = new GameObject("character-switch");
characterObject.transform.SetParent(_root.transform);
GameObject characterUi = new GameObject("UI_CharacterSwitch");
characterUi.transform.SetParent(_root.transform);
var controller = characterObject.AddComponent<CharacterSwitchController>();
typeof(CharacterSwitchController)
.GetField("uiPanel", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(controller, characterUi);
Assert.AreSame(characterUi, controller.StageIsolationUiRoot);
var isolation = _root.AddComponent<MiniGameStageIsolation>();
isolation.SuspendLobbyForMiniGame();
Assert.IsFalse(characterUi.activeSelf);
isolation.ResumeLobbyAfterMiniGame();
Assert.IsTrue(characterUi.activeSelf);
}
[Test]
public void CharacterSwitchUiLoadedWhileSuspended_IsHidden()
{
_root = new GameObject("root");
var isolation = _root.AddComponent<MiniGameStageIsolation>();
isolation.SuspendLobbyForMiniGame();
GameObject characterObject = new GameObject("character-switch");
characterObject.transform.SetParent(_root.transform);
GameObject characterUi = new GameObject("UI_CharacterSwitch");
characterUi.transform.SetParent(_root.transform);
var controller = characterObject.AddComponent<CharacterSwitchController>();
typeof(CharacterSwitchController)
.GetField("uiPanel", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(controller, characterUi);
controller.ApplyStageIsolationState();
Assert.IsFalse(characterUi.activeSelf);
}
[Test]
public void SuspendAndResume_IgnoreNullEntries()
{
@@ -1,3 +1,4 @@
using System.IO;
using System.Reflection;
using NUnit.Framework;
using UnityEditor;
@@ -33,3 +34,84 @@ public sealed class XWorldJsonPrefabAutoConvertTests
return (bool)method.Invoke(null, null);
}
}
public sealed class XWorldLocalServerBuildTests
{
[Test]
public void LocalServerMenuUsesGatewayProjectInsteadOfPrebuiltDll()
{
string gatewayProjectPath = InvokeStringPath("GetMiniGameGatewayRunnerProjectPath");
Assert.That(gatewayProjectPath.Replace('\\', '/'), Does.EndWith("/Server/Gateway.Runner/Gateway.Runner.csproj"));
Assert.That(File.Exists(gatewayProjectPath), Is.True);
Assert.That(XWorldUtil.CanOpenLocalServer(), Is.True);
}
[Test]
public void CdnRunnerBuildMenuUsesCdnProject()
{
string cdnProjectPath = InvokeStringPath("GetMiniGameCdnRunnerProjectPath");
Assert.That(cdnProjectPath.Replace('\\', '/'), Does.EndWith("/Server/Cdn.Runner/Cdn.Runner.csproj"));
Assert.That(File.Exists(cdnProjectPath), Is.True);
}
[Test]
public void LocalServerBuildIsSkippedWhenGatewayIsAlreadyRunning()
{
Assert.That(InvokeShouldBuildLocalServer(false), Is.True);
Assert.That(InvokeShouldBuildLocalServer(true), Is.False);
}
[Test]
public void DotnetRunnerLaunchableRequiresRuntimeConfigNextToDll()
{
string tempDir = Path.Combine(Path.GetTempPath(), "XWorldRunnerTest-" + System.Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(tempDir);
try
{
string runnerPath = Path.Combine(tempDir, "Example.Runner.dll");
File.WriteAllText(runnerPath, string.Empty);
Assert.That(InvokeLaunchable(runnerPath), Is.False);
File.WriteAllText(Path.Combine(tempDir, "Example.Runner.runtimeconfig.json"), "{}");
Assert.That(InvokeLaunchable(runnerPath), Is.True);
}
finally
{
Directory.Delete(tempDir, true);
}
}
private static string InvokeStringPath(string methodName)
{
MethodInfo method = typeof(XWorldUtil).GetMethod(
methodName,
BindingFlags.Static | BindingFlags.NonPublic);
Assert.That(method, Is.Not.Null);
return (string)method.Invoke(null, null);
}
private static bool InvokeLaunchable(string runnerPath)
{
MethodInfo method = typeof(XWorldUtil).GetMethod(
"IsDotnetRunnerLaunchable",
BindingFlags.Static | BindingFlags.NonPublic);
Assert.That(method, Is.Not.Null);
return (bool)method.Invoke(null, new object[] { runnerPath });
}
private static bool InvokeShouldBuildLocalServer(bool isGatewayRunning)
{
MethodInfo method = typeof(XWorldUtil).GetMethod(
"ShouldBuildMiniGameLocalServer",
BindingFlags.Static | BindingFlags.NonPublic);
Assert.That(method, Is.Not.Null);
return (bool)method.Invoke(null, new object[] { isGatewayRunning });
}
}