504 lines
19 KiB
C#
504 lines
19 KiB
C#
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
|
||
{
|
||
/// <summary>
|
||
/// 用于启动游戏,执行版本检查,版本更新,加载程序集,进入游戏
|
||
/// 错误处理是用的打印日志,正式上线的话需要一个错误处理系统来给玩家显示错误信息
|
||
/// </summary>
|
||
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<GameLaunch>().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<MethodExecutionInfo> methodExecutionInfos = new List<MethodExecutionInfo>();
|
||
}
|
||
|
||
#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<string, Assembly> _allHotUpdateAssemblies = new Dictionary<string, Assembly>();
|
||
|
||
///GamePlay程序集依赖的热更程序集,这些程序集要先于gameplay程序集加载,需要手动填写
|
||
private readonly List<string> _gamePlayDependencyDlls = new List<string>()
|
||
{
|
||
};
|
||
|
||
/// <summary>
|
||
/// 资源系统运行模式
|
||
/// </summary>
|
||
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<Font>("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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 请求版本文件
|
||
/// assets_version_info.json
|
||
/// </summary>
|
||
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<Yoo_Package_Info>(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<string[]> GetMetaDataDllToLoad()
|
||
{
|
||
string[] result = null;
|
||
var metaDataToLoad = await YooAssetManager_AOT.LoadAssembliesAsset<TextAsset>(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<byte[]> ReadDllBytes(string path)
|
||
{
|
||
var dllText = await YooAssetManager_AOT.LoadAssembliesAsset<TextAsset>(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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 反射执行被RuntimeInitializeOnLoadMethod attribute标注的函数,HybridCLR不支持该attribute
|
||
/// </summary>
|
||
private async void ExecuteRuntimeInitializeOnLoadMethodAttribute()
|
||
{
|
||
//var runtimeInitializeOnLoadMethodCollection = await YooAssetManager_AOT.LoadAsset<TextAsset>(RUN_TIME_INITIALIZE_ON_LOAD_METHOD_COLLECTION_PATH);
|
||
//var json = runtimeInitializeOnLoadMethodCollection.text;
|
||
//var collection = JsonUtility.FromJson<RuntimeInitializeOnLoadMethodCollection>(json);
|
||
//foreach (var methodInfo in collection.methodExecutionInfos)
|
||
// methodInfo.Execute();
|
||
|
||
//Debug.Log("execute RuntimeInitializeOnLoadMethod finish!");
|
||
}
|
||
|
||
private Assembly GetAssembly(string assemblyName)
|
||
{
|
||
assemblyName = assemblyName.Replace(".dll", "");
|
||
IEnumerable<Assembly> 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));
|
||
}
|
||
}
|
||
} |