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));
}
}
}