257 lines
8.5 KiB
C#
257 lines
8.5 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using TH1_Logic.Hotfix;
|
|
using TH1Resource;
|
|
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
using YooAsset;
|
|
|
|
namespace TH1_AOT
|
|
{
|
|
[DisallowMultipleComponent]
|
|
public sealed class AotMainBootstrap : MonoBehaviour
|
|
{
|
|
private const string GameSceneAssetRoot = "Assets/BundleResources/";
|
|
|
|
[Header("Game Scene")]
|
|
public string GameSceneLocation = "Scenes/SampleScene.unity";
|
|
public bool LoadGameSceneFromYooAsset = true;
|
|
|
|
[Header("Debug Param")]
|
|
public bool NoAI = false;
|
|
public bool FullSight = true;
|
|
public float AIActionTime = 0.2f;
|
|
public bool AIAllTech = true;
|
|
public bool AIMoreMoney = true;
|
|
public float LandThreshold = -1f;
|
|
public float AnimationSpeed = 1f;
|
|
public bool DebugMode = false;
|
|
public bool DebugHideCenterMessage = false;
|
|
|
|
[Header("Play Settings")]
|
|
public int cityCount;
|
|
public int unitCount;
|
|
public int turn;
|
|
public int renko;
|
|
public bool IsNetActionExecuting;
|
|
|
|
[Header("RenderObject")]
|
|
public GameObject ROMapRenderer;
|
|
|
|
[Header("SuperBank")]
|
|
public Material URPDefaultMat;
|
|
|
|
private bool _started;
|
|
private static bool _runtimeStarted;
|
|
private static SceneHandle _gameSceneHandle;
|
|
|
|
private static readonly string[] ForwardedFieldNames =
|
|
{
|
|
nameof(GameSceneLocation),
|
|
nameof(LoadGameSceneFromYooAsset),
|
|
nameof(NoAI),
|
|
nameof(FullSight),
|
|
nameof(AIActionTime),
|
|
nameof(AIAllTech),
|
|
nameof(AIMoreMoney),
|
|
nameof(LandThreshold),
|
|
nameof(AnimationSpeed),
|
|
nameof(DebugMode),
|
|
nameof(DebugHideCenterMessage),
|
|
nameof(cityCount),
|
|
nameof(unitCount),
|
|
nameof(turn),
|
|
nameof(renko),
|
|
nameof(IsNetActionExecuting),
|
|
nameof(ROMapRenderer),
|
|
nameof(URPDefaultMat),
|
|
};
|
|
|
|
private IEnumerator Start()
|
|
{
|
|
if (_started) yield break;
|
|
_started = true;
|
|
|
|
if (_runtimeStarted)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
_runtimeStarted = true;
|
|
|
|
Application.runInBackground = true;
|
|
DontDestroyOnLoad(gameObject);
|
|
|
|
Debug.Log("[TH1.AOT] Bootstrap start.");
|
|
|
|
var hotfixAssembly = ResolveHotfixAssembly();
|
|
if (hotfixAssembly == null)
|
|
{
|
|
Debug.LogError("[TH1.AOT] Hotfix assembly is not available.");
|
|
yield break;
|
|
}
|
|
|
|
HotfixBootstrap.EnsureAotMetadataLoaded();
|
|
HotfixBootstrap.InvokeHotfixEntry(hotfixAssembly);
|
|
HotfixBootstrap.RefreshRuntimeTypeCaches();
|
|
Debug.Log($"[TH1.AOT] Hotfix assembly ready: {hotfixAssembly.GetName().Name}");
|
|
|
|
yield return ResourceManager.InitializeCoroutine();
|
|
|
|
var sceneBootstrap = this;
|
|
if (LoadGameSceneFromYooAsset && !IsGameSceneLoaded(GameSceneLocation))
|
|
{
|
|
var sceneHandle = ResourceManager.LoadSceneAsync(GameSceneLocation, LoadSceneMode.Single);
|
|
yield return sceneHandle;
|
|
|
|
if (sceneHandle.Status != EOperationStatus.Succeed)
|
|
{
|
|
Debug.LogError($"[TH1.AOT] Load game scene failed: {GameSceneLocation}, {sceneHandle.LastError}");
|
|
_runtimeStarted = false;
|
|
yield break;
|
|
}
|
|
|
|
_gameSceneHandle = sceneHandle;
|
|
sceneHandle.ActivateScene();
|
|
sceneBootstrap = FindBootstrapInScene(sceneHandle.SceneObject) ?? this;
|
|
if (sceneBootstrap != this)
|
|
{
|
|
sceneBootstrap.enabled = false;
|
|
}
|
|
|
|
Debug.Log($"[TH1.AOT] Loaded game scene from YooAsset: {GameSceneLocation}");
|
|
}
|
|
|
|
var hotfixMainType = hotfixAssembly.GetType(HotfixManifest.RuntimeMainTypeName, false);
|
|
if (hotfixMainType == null)
|
|
{
|
|
Debug.LogError($"[TH1.AOT] Hotfix Main type not found: {HotfixManifest.RuntimeMainTypeName}");
|
|
yield break;
|
|
}
|
|
|
|
if (!typeof(MonoBehaviour).IsAssignableFrom(hotfixMainType))
|
|
{
|
|
Debug.LogError($"[TH1.AOT] Hotfix Main type is not a MonoBehaviour: {hotfixMainType.FullName}");
|
|
yield break;
|
|
}
|
|
|
|
var mainHost = sceneBootstrap != null ? sceneBootstrap.gameObject : gameObject;
|
|
var hotfixMain = mainHost.GetComponent(hotfixMainType);
|
|
if (hotfixMain == null)
|
|
{
|
|
hotfixMain = mainHost.AddComponent(hotfixMainType);
|
|
}
|
|
|
|
ForwardSceneFields(sceneBootstrap, hotfixMain);
|
|
|
|
Debug.Log($"[TH1.AOT] Started hotfix Main from {hotfixAssembly.GetName().Name}.");
|
|
}
|
|
|
|
private static Assembly ResolveHotfixAssembly()
|
|
{
|
|
#if UNITY_EDITOR
|
|
var editorAssembly = FindLoadedAssembly(HotfixManifest.HotfixAssemblyName);
|
|
if (editorAssembly != null)
|
|
{
|
|
return editorAssembly;
|
|
}
|
|
#endif
|
|
|
|
if (HotfixBootstrap.LoadedAssembly != null)
|
|
{
|
|
return HotfixBootstrap.LoadedAssembly;
|
|
}
|
|
|
|
if (HotfixBootstrap.InitializeFromStreamingAssets(true))
|
|
{
|
|
return HotfixBootstrap.LoadedAssembly;
|
|
}
|
|
|
|
return FindLoadedAssembly(HotfixManifest.HotfixAssemblyName);
|
|
}
|
|
|
|
private static Assembly FindLoadedAssembly(string assemblyName)
|
|
{
|
|
return AppDomain.CurrentDomain.GetAssemblies()
|
|
.FirstOrDefault(assembly => assembly.GetName().Name == assemblyName);
|
|
}
|
|
|
|
private bool IsGameSceneLoaded(string location)
|
|
{
|
|
var expectedPath = NormalizeGameSceneAssetPath(location);
|
|
for (var i = 0; i < SceneManager.sceneCount; i++)
|
|
{
|
|
var scene = SceneManager.GetSceneAt(i);
|
|
if (!scene.isLoaded) continue;
|
|
|
|
var scenePath = scene.path.Replace('\\', '/');
|
|
if (scenePath.Equals(expectedPath, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static string NormalizeGameSceneAssetPath(string location)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(location))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
var normalized = location.Replace('\\', '/').TrimStart('/');
|
|
return normalized.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase)
|
|
? normalized
|
|
: GameSceneAssetRoot + normalized;
|
|
}
|
|
|
|
private AotMainBootstrap FindBootstrapInScene(Scene scene)
|
|
{
|
|
if (scene.IsValid() && scene.isLoaded)
|
|
{
|
|
foreach (var root in scene.GetRootGameObjects())
|
|
{
|
|
var bootstrap = root.GetComponentsInChildren<AotMainBootstrap>(true)
|
|
.FirstOrDefault(item => item != null && item != this);
|
|
if (bootstrap != null)
|
|
{
|
|
return bootstrap;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FindObjectsOfType<AotMainBootstrap>(true)
|
|
.FirstOrDefault(item => item != null && item != this);
|
|
}
|
|
|
|
private static void ForwardSceneFields(AotMainBootstrap source, Component hotfixMain)
|
|
{
|
|
if (source == null || hotfixMain == null) return;
|
|
|
|
var sourceType = source.GetType();
|
|
var targetType = hotfixMain.GetType();
|
|
const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
|
|
|
foreach (var fieldName in ForwardedFieldNames)
|
|
{
|
|
var sourceField = sourceType.GetField(fieldName, flags);
|
|
var targetField = targetType.GetField(fieldName, flags);
|
|
if (sourceField == null || targetField == null) continue;
|
|
|
|
try
|
|
{
|
|
targetField.SetValue(hotfixMain, sourceField.GetValue(source));
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogWarning($"[TH1.AOT] Forward field failed: {fieldName}, {e.Message}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|