Files

132 lines
8.3 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using HotUpdate.Gameplay;
using HotUpdate.Net;
using HotUpdate.UI;
using UnityEditor;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>Play Mode check that PrototypeArena loads Greenfield without bridges.</summary>
public static class VerifyBattlefield
{
const string Endpoint = "http://127.0.0.1:18081";
static PrototypeFlow Flow => UnityEngine.Object.FindObjectOfType<PrototypeFlow>();
static void Check(bool value, string message) { if (!value) throw new Exception(message); }
static T Get<T>(string name) => (T)typeof(PrototypeFlow).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(Flow);
static void Set(string name, object value) => typeof(PrototypeFlow).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic).SetValue(Flow, value);
static async Task Until(Func<bool> condition, int seconds = 25)
{
var end = DateTime.UtcNow.AddSeconds(seconds);
while (!condition())
{
Check(DateTime.UtcNow < end, "Battlefield timed out: " + Flow?.Page);
await Task.Delay(100);
}
}
static Button Button(string name) => UnityEngine.Object.FindObjectsOfType<Button>().Single(b => b.name == "Btn_" + name && b.gameObject.activeInHierarchy);
static void Click(string name)
{
var b = Button(name);
Check(b.interactable, "Disabled " + name);
Canvas.ForceUpdateCanvases();
var rect = b.GetComponent<RectTransform>();
var pointer = new PointerEventData(EventSystem.current) { position = RectTransformUtility.WorldToScreenPoint(null, rect.TransformPoint(rect.rect.center)), button = PointerEventData.InputButton.Left };
var hits = new List<RaycastResult>();
EventSystem.current.RaycastAll(pointer, hits);
Check(hits.Count > 0 && ExecuteEvents.GetEventHandler<IPointerClickHandler>(hits[0].gameObject) == b.gameObject, "Blocked " + name);
ExecuteEvents.Execute(b.gameObject, pointer, ExecuteEvents.pointerClickHandler);
}
static async Task Capture(string name)
{
await Task.Delay(200);
var type = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetType("Unity.Pipeline.Editor.Commands.Capture.CaptureCommands")).First(t => t != null);
type.GetMethod("CaptureGameView", BindingFlags.Public | BindingFlags.Static).Invoke(null, new object[] { 1080, 1920, null, "Screenshots/battlefield_" + name + ".png", false, 0, "screen" });
}
public static string Assets()
{
var arena = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Bundles/Scenes/Prototype/PrototypeArena.prefab");
Check(arena != null, "Missing PrototypeArena");
Check(arena.transform.Find("Greenfield") != null, "PrototypeArena missing Greenfield");
Check(arena.transform.Find("Greenfield/Ground/GroundMesh") != null, "Missing Greenfield ground");
Check(arena.transform.Find("Greenfield/Decorations/tree") != null, "Missing tree");
Check(arena.transform.Find("Greenfield/Decorations/rock") != null, "Missing rock");
Check(arena.transform.Find("Greenfield/Decorations/sign") != null, "Missing sign");
Check(arena.transform.Find("Greenfield/Decorations/shrub") != null, "Missing shrub");
Check(arena.transform.Find("Greenfield/Decorations/bridge") == null, "bridge still nested");
Check(arena.transform.Find("Greenfield/Decorations/bridgeLeft") == null, "bridgeLeft still nested");
Check(arena.transform.Find("Greenfield/Decorations/bridgeRight") == null, "bridgeRight still nested");
Check(arena.transform.Find("Heroes") != null && arena.transform.Find("BattleCamera") != null, "Gameplay nodes missing");
var ground = arena.transform.Find("Ground");
Check(ground == null || !ground.gameObject.activeSelf, "Primitive ground still active");
var badLayer = arena.transform.Find("Greenfield").GetComponentsInChildren<Renderer>(true).FirstOrDefault(r => r.gameObject.layer != 30);
Check(badLayer == null, "Greenfield renderer not on layer 30: " + badLayer?.name);
return "PASS authored PrototypeArena nests Greenfield without bridges.";
}
public static async Task<string> Play()
{
Check(EditorApplication.isPlaying, "Enter Play Mode first");
await Until(() => Flow?.Game != null && !Flow.IsBusy);
SessionState.SetString("AIGame.Battlefield.original", JsonUtility.ToJson(Flow.Game.State));
SessionState.SetString("AIGame.Battlefield.originalEndpoint", Get<string>("_httpBase"));
AuthResponse account = null; string error = null; bool done = false;
string user = "battlefield_" + Guid.NewGuid().ToString("N");
Directory.CreateDirectory(Path.Combine(Application.dataPath, "../../Tools/output/battlefield"));
File.WriteAllText(Path.Combine(Application.dataPath, "../../Tools/output/battlefield/account.txt"), user);
Flow.StartCoroutine(AuthApi.Register(Endpoint, user, "isolated-battlefield-password", (r, e) => { account = r; error = e; done = true; }));
await Until(() => done);
Check(account != null, "Isolated register failed " + error);
var session = new SessionData { Pid = account.pid, Token = account.token, Username = user };
UiManager.Instance.Get(ConfirmDialogView.PrefabName).GetComponent<ConfirmDialogView>().Hide();
Set("_session", new Func<SessionData>(() => session));
Set("_profile", new Func<long>(() => session.Pid));
Set("_httpBase", Endpoint);
Set("_boundProfile", 0L);
PlayerPrefs.DeleteKey("AIGame.G1.pending." + Uri.EscapeDataString(Endpoint) + "." + session.Pid);
Flow.Handle("Home");
await Until(() => Flow.Game != null && !Flow.IsBusy);
Check(Flow.Game.State.formation != null && Flow.Game.State.formation.Any(p => p >= 0), "Fresh account has empty formation");
Click("Adventure");
await Until(() => Flow.Page == "UI_PrototypeStages");
Click("Stage1");
await Until(() => Flow.Page == "UI_PrototypeFormation");
Click("StartBattle");
await Until(() => Flow.Page == "UI_PrototypeBattle" && Flow.Battle != null);
var arena = Get<GameObject>("_arena");
Check(arena != null, "Battle arena was not instantiated");
Check(arena.transform.Find("Greenfield/Ground/GroundMesh") != null, "Runtime Greenfield ground missing");
Check(arena.transform.Find("Greenfield/Decorations/tree") != null, "Runtime tree missing");
Check(arena.transform.Find("Greenfield/Decorations/bridge") == null, "Runtime bridge present");
Check(arena.transform.Find("Greenfield/Decorations/bridgeLeft") == null, "Runtime bridgeLeft present");
Check(arena.transform.Find("Greenfield/Decorations/bridgeRight") == null, "Runtime bridgeRight present");
var ground = arena.transform.Find("Ground");
Check(ground == null || !ground.gameObject.activeSelf, "Runtime primitive ground still active");
Click("Speed");
await Until(() => Flow.Battle.Time >= 8);
await Capture("play_battle");
return "PASS play battle loads Greenfield without bridges; screenshot battlefield_play_battle.png";
}
public static async Task<string> Cleanup()
{
var net = UnityEngine.Object.FindObjectOfType<GameNet>();
UiManager.Instance.Get(ConfirmDialogView.PrefabName).GetComponent<ConfirmDialogView>().Hide();
Set("_session", new Func<SessionData>(() => net.Session));
Set("_profile", new Func<long>(() => net.Session?.Pid ?? 0));
Set("_httpBase", SessionState.GetString("AIGame.Battlefield.originalEndpoint", "http://127.0.0.1:8080"));
Set("_boundProfile", 0L);
Flow.Handle("Home");
await Until(() => Flow.Game != null && !Flow.IsBusy);
var original = JsonUtility.FromJson<PrototypeSave>(SessionState.GetString("AIGame.Battlefield.original", ""));
Check(Flow.Game.State.coins == original.coins && Flow.Game.State.candy == original.candy && Flow.Game.State.shots == original.shots && Flow.Game.State.level == original.level, "Real account was changed by battlefield test");
return "PASS cleanup: real account restored; battlefield check used isolated :18081 only.";
}
}