using Cysharp.Threading.Tasks; using HybridCLR; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEditor; using UnityEngine; using YooAsset; using FairyGUI; namespace AOT { /// /// 用于启动游戏,执行版本检查,版本更新,加载程序集,进入游戏 /// 错误处理是用的打印日志,正式上线的话需要一个错误处理系统来给玩家显示错误信息 /// public class GameLaunch : MonoBehaviour { #region Inner Class [Serializable] public class MethodExecutionInfo { public string assemblyName; public string typeName; public string methodName; public int sequence; public MethodExecutionInfo(string assemblyName, string typeName, string methodName, int sequence) { this.assemblyName = assemblyName; this.typeName = typeName; this.methodName = methodName; this.sequence = sequence; } public void Execute() { var assembly = FindObjectOfType().GetAssembly(assemblyName); if (assembly == null) { Debug.LogError($"cant find assembly,name:{assemblyName}"); return; } var type = assembly.GetType(typeName); if (type == null) { Debug.LogError($"cant find type,name:{typeName}"); return; } var method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { Debug.LogError($"cant find method,name:{methodName}"); return; } method.Invoke(null, null); } } [Serializable] public class RuntimeInitializeOnLoadMethodCollection { public List methodExecutionInfos = new List(); } #endregion const string START_SCENE_NAME = "Assets/Art/Scenes/main.unity"; public static string META_DATA_DLL_PATH { get { switch (Application.platform) { case RuntimePlatform.WindowsPlayer: case RuntimePlatform.WindowsEditor: case RuntimePlatform.OSXPlayer: case RuntimePlatform.OSXEditor: case RuntimePlatform.LinuxPlayer: case RuntimePlatform.LinuxEditor: return "Assets/Scripts/Dlls/PC/HotUpdateDlls/MetaDataDll/"; case RuntimePlatform.Android: return "Assets/Scripts/Dlls/Android/HotUpdateDlls/MetaDataDll/"; default: Debug.LogWarning($"Unsupported platform: {Application.platform}. Using PC path as fallback."); return "Assets/Scripts/Dlls/PC/HotUpdateDlls/MetaDataDll/"; } } } //const string META_DATA_DLL_PATH = "Assets/Scripts/Dlls/HotUpdateDlls/MetaDataDll/"; //const string HOT_UPDATE_DLL_PATH = "Assets/Scripts/Dlls/HotUpdateDlls/HotUpdateDll/"; //public const string META_DATA_DLLS_TO_LOAD_PATH = "Assets/Scripts/Dlls/HotUpdateDlls/MetaDataDllToLoad.txt"; //public const string RUN_TIME_INITIALIZE_ON_LOAD_METHOD_COLLECTION_PATH = "Assets/Scripts/Dlls/HotUpdateDlls/RuntimeInitializeOnLoadMethodCollection.txt"; // 定义静态属性,根据平台返回不同的 HOT_UPDATE_DLL_PATH public static string HOT_UPDATE_DLL_PATH { get { switch (Application.platform) { case RuntimePlatform.WindowsPlayer: case RuntimePlatform.WindowsEditor: case RuntimePlatform.OSXPlayer: case RuntimePlatform.OSXEditor: case RuntimePlatform.LinuxPlayer: case RuntimePlatform.LinuxEditor: return "Assets/Scripts/Dlls/PC/HotUpdateDlls/HotUpdateDll/"; case RuntimePlatform.Android: return "Assets/Scripts/Dlls/Android/HotUpdateDlls/HotUpdateDll/"; default: Debug.LogWarning($"Unsupported platform: {Application.platform}. Using PC path as fallback."); return "Assets/Scripts/Dlls/PC/HotUpdateDlls/HotUpdateDll/"; } } } // 定义静态属性,根据平台返回不同的 META_DATA_DLLS_TO_LOAD_PATH public static string META_DATA_DLLS_TO_LOAD_PATH { get { switch (Application.platform) { case RuntimePlatform.WindowsPlayer: case RuntimePlatform.WindowsEditor: case RuntimePlatform.OSXPlayer: case RuntimePlatform.OSXEditor: case RuntimePlatform.LinuxPlayer: case RuntimePlatform.LinuxEditor: return "Assets/Scripts/Dlls/PC/HotUpdateDlls/MetaDataDllToLoad.txt"; case RuntimePlatform.Android: return "Assets/Scripts/Dlls/Android/HotUpdateDlls/MetaDataDllToLoad.txt"; default: Debug.LogWarning($"Unsupported platform: {Application.platform}. Using PC path as fallback."); return "Assets/Scripts/Dlls/PC/HotUpdateDlls/MetaDataDllToLoad.txt"; } } } // 定义静态属性,根据平台返回不同的 RUN_TIME_INITIALIZE_ON_LOAD_METHOD_COLLECTION_PATH public static string RUN_TIME_INITIALIZE_ON_LOAD_METHOD_COLLECTION_PATH { get { switch (Application.platform) { case RuntimePlatform.WindowsPlayer: case RuntimePlatform.WindowsEditor: case RuntimePlatform.OSXPlayer: case RuntimePlatform.OSXEditor: case RuntimePlatform.LinuxPlayer: case RuntimePlatform.LinuxEditor: return "Assets/Scripts/Dlls/PC/HotUpdateDlls/RuntimeInitializeOnLoadMethodCollection.txt"; case RuntimePlatform.Android: return "Assets/Scripts/Dlls/Android/HotUpdateDlls/RuntimeInitializeOnLoadMethodCollection.txt"; default: Debug.LogWarning($"Unsupported platform: {Application.platform}. Using PC path as fallback."); return "Assets/Scripts/Dlls/PC/HotUpdateDlls/RuntimeInitializeOnLoadMethodCollection.txt"; } } } const string GAMEPLAY_DLL_NAME = "HotUpdate.dll"; public const string META_DATA_DLL_SEPARATOR = "_"; private Dictionary _allHotUpdateAssemblies = new Dictionary(); ///GamePlay程序集依赖的热更程序集,这些程序集要先于gameplay程序集加载,需要手动填写 private readonly List _gamePlayDependencyDlls = new List() { }; /// /// 资源系统运行模式 /// public EPlayMode playMode = EPlayMode.EditorSimulateMode; //DeviceInfoManager deviceInfo = new DeviceInfoManager(); public _UI_VersionUpdate version; private void Awake() { //SetFullScreen(version.transform.parent as RectTransform); } public void SetFullScreen(RectTransform full) { //var full = transform.Find("full") as RectTransform; //这里写让full的Top自适应到安全屏幕。 //这是一个竖屏的App,Top就是顶部屏幕,有刘海的位置, if (full != null) { // 获取安全区域 Rect safeArea = Screen.safeArea; // 计算顶部偏移量(屏幕高度减去安全区域顶部的y坐标) float topOffset = Screen.height - safeArea.yMax; if (topOffset > 0) { //LogYellow($"不安全区域像素:{topOffset}"); if (topOffset < 0) { topOffset = 0; } } // 获取full的offsetMax Vector2 offsetMax = full.offsetMax; // 设置顶部偏移量 offsetMax.y = -topOffset; // 应用新的offsetMax full.offsetMax = offsetMax; } } async void Start() { //deviceInfo.InitDeviceInfo(); await Launch(); } private async UniTask Launch() { InitFairyGUI(); await RequestVersionInfo(); //await Framework_AOT.Init(this.gameObject); //暂时注释 //_ui_versionUpdate.gameObject.SetActive(true); //出现更新界面 //if (playMode == EPlayMode.HostPlayMode) //{ // LogRed($"开始zip下载流程"); // //ZIP包补丁更新需要在YooAsset初始化之前 // //await XPatchManager.Instance.Init(m_DownloadManager, m_ZipManager); // //LogRed($"XPatchManager初始化结束"); // //解锁帧率 // Application.targetFrameRate = -1; // var updateSucceed = await XPatchManager.Instance.StartUpdateResourcesAsync(); // LogRed($"zip下载流程结束"); // if (!updateSucceed) // { // LogRed($"补丁更新失败\n"); // //清空此次更新下载的资源 // XPatchManager.Instance.ClearUpdateResources(); // //TODO 要么弹窗让玩家重试,要么退出游戏 // return; // } // Application.targetFrameRate = 60; //} YooAssetManager_AOT.instance.onStart = LoadAssemblies; YooAssetManager_AOT.instance.onFinish = EnterGame; await YooAssetManager_AOT.YooInit(playMode); //启动游戏加载字体 //await yooManager.LoadAssetAsync("Assets/Art/Font/hpqdqxj.ttf"); Debug.Log("YooInit 完成"); //await LoadAssemblies(); //EnterGame(); } private void InitFairyGUI() { // 1. 初始化UI根节点 GRoot.inst.SetContentScaleFactor((int)AppConst_AOT.screenSize_Width, (int)AppConst_AOT.screenSize_Height, UIContentScaler.ScreenMatchMode.MatchWidthOrHeight); // 2. 加载Loading界面 UIPackage.AddPackage("Loading"); GComponent view = UIPackage.CreateObject("Loading", "mainbg").asCom; GRoot.inst.AddChild(view); var versionUpdate = new _UI_VersionUpdate(view); } /// /// 请求版本文件 /// assets_version_info.json /// private async UniTask RequestVersionInfo() { AOT.Event.PatchEventDefine.RequestVersionInfo.SendEventMessage("下载 assets_version_info.json"); var url = $"{AppConst_AOT.AssetsServerPath}/CDN/{GetPlatformName()}/{AppConst_AOT.AssetsVersion}/assets_version_info.json"; //Debug.Log($"## 请求版本文件 :{url}"); if (AppConst_AOT.UpdateMode) { AppConst_AOT.yoo_Package_Info = await Downloader.DownloadJSONAsync(url); //Debug.Log("## 版本文件下载完成"); } else { //编辑器模式读assets_version_info.json //后续改为从服务器下载版本信息。 AppConst_AOT.yoo_Package_Info = VersionUtils.GetYooPackageVersion(); } } public static string GetPlatformName() { #if UNITY_EDITOR switch (EditorUserBuildSettings.activeBuildTarget) { case BuildTarget.StandaloneWindows: case BuildTarget.StandaloneWindows64: case BuildTarget.StandaloneOSX: case BuildTarget.StandaloneLinux64: return "PC"; case BuildTarget.iOS: return "IPhone"; case BuildTarget.Android: return "Android"; default: return EditorUserBuildSettings.activeBuildTarget.ToString(); } #else switch (Application.platform) { case RuntimePlatform.WindowsPlayer: case RuntimePlatform.WindowsEditor: return "PC"; case RuntimePlatform.IPhonePlayer: return "IPhone"; case RuntimePlatform.Android: return "Android"; default: return Application.platform.ToString(); } #endif } private async UniTask LoadAssemblies() { //if (!enableHybridCLR) // return; #if !UNITY_EDITOR if (playMode == EPlayMode.EditorSimulateMode) return; Debug.Log("LoadAssemblies start!"); HybridCLROptimizer.OptimizeHybridCLR(); await LoadMetadataForAOTAssemblies(); await LoadGamePlayDependencyAssemblies(); await LoadGamePlayAssemblies(); ExecuteRuntimeInitializeOnLoadMethodAttribute(); await UniTask.Yield(); Debug.Log("LoadAssemblies finish!"); #endif } //补充元数据 private async UniTask LoadMetadataForAOTAssemblies() { var aotAssemblies = await GetMetaDataDllToLoad(); if (aotAssemblies == null) { return; } foreach (var aotDllName in aotAssemblies) { if (string.IsNullOrEmpty(aotDllName)) continue; Debug.Log($"补充元数据 {META_DATA_DLL_PATH}{aotDllName}.bytes"); var path = $"{META_DATA_DLL_PATH}{aotDllName}.bytes"; var asset = await ReadDllBytes(path); if (asset != null) { var err = HybridCLR.RuntimeApi.LoadMetadataForAOTAssembly(asset, HomologousImageMode.SuperSet); Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. ret:{err}"); } } Debug.Log("LoadMetadataForAOTAssemblies finish!"); } private async UniTask GetMetaDataDllToLoad() { string[] result = null; var metaDataToLoad = await YooAssetManager_AOT.LoadAssembliesAsset(META_DATA_DLLS_TO_LOAD_PATH); if (metaDataToLoad == null) { Debug.LogError($"cant load metaDataText,path:{META_DATA_DLLS_TO_LOAD_PATH}"); } else { var text = metaDataToLoad.text; Debug.Log($"下载到的dll名称:{text}"); result = text.Split(META_DATA_DLL_SEPARATOR); //yooManager.UnloadAsset(metaDataToLoad); } return result; } private async UniTask ReadDllBytes(string path) { var dllText = await YooAssetManager_AOT.LoadAssembliesAsset(path); return dllText.bytes; //_dllBytes = (dllText.AssetObject as TextAsset).bytes; } //加载GamePlay依赖的第三方程序集 private async UniTask LoadGamePlayDependencyAssemblies() { foreach (var dllName in _gamePlayDependencyDlls) await LoadSingleHotUpdateAssembly(dllName); Debug.Log("LoadGamePlayDependencyAssemblies finish!"); } //加载GamePlay程序集 private async UniTask LoadGamePlayAssemblies() { await LoadSingleHotUpdateAssembly(GAMEPLAY_DLL_NAME); Debug.Log("LoadGamePlayAssemblies finish!"); } private async UniTask OnLoadScene() { //StartCoroutine(YooAssetManager.ChangeScene()); transform.Find("EventSystem").gameObject.SetActive(false); await YooAssetManager_AOT.ChangeScene(START_SCENE_NAME); } private async void EnterGame() { if (YooAssetManager_AOT.instance.isFinish) await OnLoadScene(); else return; Debug.Log("EnterGame finish!"); } private async UniTask LoadSingleHotUpdateAssembly(string dllName) { var path = $"{HOT_UPDATE_DLL_PATH}{dllName}.bytes"; Debug.Log($"开始读取dll bytes:{path}"); var asset = await ReadDllBytes(path); if (asset != null) { var assembly = Assembly.Load(asset); _allHotUpdateAssemblies.Add(assembly.FullName, assembly); Debug.Log($"加载程序集成功, 程序集名称:{assembly.FullName}"); } await UniTask.Yield(); } /// /// 反射执行被RuntimeInitializeOnLoadMethod attribute标注的函数,HybridCLR不支持该attribute /// private async void ExecuteRuntimeInitializeOnLoadMethodAttribute() { //var runtimeInitializeOnLoadMethodCollection = await YooAssetManager_AOT.LoadAsset(RUN_TIME_INITIALIZE_ON_LOAD_METHOD_COLLECTION_PATH); //var json = runtimeInitializeOnLoadMethodCollection.text; //var collection = JsonUtility.FromJson(json); //foreach (var methodInfo in collection.methodExecutionInfos) // methodInfo.Execute(); //Debug.Log("execute RuntimeInitializeOnLoadMethod finish!"); } private Assembly GetAssembly(string assemblyName) { assemblyName = assemblyName.Replace(".dll", ""); IEnumerable allAssemblies; #if !UNITY_EDITOR allAssemblies = _allHotUpdateAssemblies.Values; #else allAssemblies = AppDomain.CurrentDomain.GetAssemblies(); #endif //enableHybridCLR ? _allHotUpdateAssemblies.Values : AppDomain.CurrentDomain.GetAssemblies(); return allAssemblies.First(assembly => assembly.FullName.Contains(assemblyName)); } } }