NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/HotUpdate/GameLauncher.cs

432 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using HotUpdate.UI;
using Cysharp.Threading.Tasks;
using PhxhSDK.AOT;
using PhxhSDK.AOT.SimpleLoader;
using PhxhSDK.AOT.VersionUpdate;
using System;
#if UNITY_IOS && !UNITY_EDITOR
using System.Runtime.InteropServices;
#endif
using PhxhSDK.AOT.PreConfig;
using UnityEngine;
using UnityEngine.SceneManagement;
using Assets.PhxhSDK.AOT.BI;
#if USE_OBFUZ
using Obfuz.EncryptionVM;
using Obfuz;
#endif
using static ProgressBarData;
#if PLATFORM_ANDROID || UNITY_EDITOR
using UnityEngine.Android;
#endif
public class GameLauncher : MonoBehaviour
{
#if USE_OBFUZ
private static bool _isSetUpStaticSecret = false;
/// <summary>
/// 初始化EncryptionService后被混淆的代码才能正常运行
/// 因此尽可能地早地初始化它。
/// </summary>
[ObfuzIgnore]
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
private static void _SetUpStaticSecretKey()
{
if (_isSetUpStaticSecret)
{
return;
}
_isSetUpStaticSecret = true;
Debug.Log("[Obfz] SetUpStaticSecret begin");
var loadedAsset = Resources.Load<TextAsset>("us5af47s54fsa1z90sf");
if (loadedAsset == null)
{
Debug.LogError("[Obfz] SetUpStaticSecret failed: could not load static secret asset");
return;
}
EncryptionService<DefaultStaticEncryptionScope>.Encryptor = new GeneratedEncryptionVirtualMachine(loadedAsset.bytes);
Debug.Log("[Obfz] SetUpStaticSecret end");
}
private static void _SetUpDynamicSecret(byte[] bytes)
{
Debug.Log("[Obfz] SetUpDynamicSecret begin");
EncryptionService<DefaultDynamicEncryptionScope>.Encryptor = new GeneratedEncryptionVirtualMachine(bytes);
Debug.Log("[Obfz] SetUpDynamicSecret end");
}
#endif
private const string START_SCENE_NAME = "Assets/Scenes/StartScene.unity";
#if USE_OBFUZ
private const string DYNAMIC_SECRET_PATH = "Assets/Config/Obfuz/defaultDynamicSecretKey.bytes";
#endif
//最大能容忍的下载卡住时长,一旦超过则重新开始下载
// private const float DOWNLOAD_STUCK_TIME_MAX = 10;
#region 进度条优化
/// <summary>
/// 进度条
/// </summary>
private ProgressBarData progressBarData;
/// <summary>
/// 假进度
/// </summary>
private float fakeProgess;
/// <summary>
/// 真进度
/// </summary>
private float realProgess;
/// <summary>
/// 假进度计时
/// </summary>
private float fakeProgessTimer;
/// <summary>
/// 假进度下一次增长的值
/// </summary>
private float fakeNextAddValue;
#endregion
/// <summary>
/// 最终进度
/// </summary>
public float FinalProgess { get { return Mathf.Max(realProgess, fakeProgess); } }
#if USE_HCLR
private HybridCLRHelper _hybridCLRHelper;
#endif
private VersionUpdateHandle _versionUpdateHandle;
private ISimpleLoader _simpleLoader;
private IVersionUpdateUI versionUpdateUI;
public bool enableHotUpdate = true;
private void Awake()
{
#if USE_OBFUZ
_SetUpStaticSecretKey();
#endif
Screen.sleepTimeout = SleepTimeout.NeverSleep; // 保持屏幕常亮
fakeProgess = 0f;
realProgess = 0f;
fakeProgessTimer = 0f;
progressBarData = Resources.Load<ProgressBarData>("ProgressBarData");
}
private void Start()
{
#if SDK_ADJUST
var YourAppToken = "shw96lfwisqo"; // 替换为你的Adjust App Token
AdjustInitTool.Instance.InitAdjustSDK(YourAppToken);
#endif
#if SDK_FIREBASE
Firebase.Analytics.FirebaseAnalytics.LogEvent("First_open");
#endif
versionUpdateUI = new TempUIHandle();
versionUpdateUI.HideUpdateInfo();
bool agreement = PlayerPrefs.GetInt("PrivacyAgreement", 0) == 1;
//BIToolinAOT.Instance.TrackEventInAOT("app_open", null);
if (!agreement) // 弹隐私弹窗
{
TempUpdateUI.Instance.PrivacyAgreeCallback = () =>
{
TempUpdateUI.Instance.m_ProgressObj.SetActive(true);
Launch();
};
TempUpdateUI.Instance.m_PrivacyDialogObj.SetActive(true);
}
else
{
TempUpdateUI.Instance.m_ProgressObj.SetActive(true);
Launch();
}
}
private void Update()
{
if (null == versionUpdateUI)
return;
versionUpdateUI.UpdateProgress(FinalProgess);
if (realProgess >= 1f)
return;
if (null == _versionUpdateHandle)
return;
if (!_versionUpdateHandle.IsUpdating)
return;
if (null == progressBarData)
return;
if (0 >= progressBarData.ListProgressDatas.Count) // 没有配置数量
return;
ProgressData lastProgressData = progressBarData.ListProgressDatas[^1];
if (fakeProgess >= lastProgressData.Target) // 进度满了
return;
if (fakeProgessTimer <= 0f)
{
foreach (ProgressData progressData in progressBarData.ListProgressDatas)
{
if (fakeProgess >= progressData.Target)
continue;
fakeProgessTimer += UnityEngine.Random.Range(progressData.MinInterval, progressData.MaxInterval);
fakeNextAddValue = UnityEngine.Random.Range(progressData.MinAddValue, progressData.MaxAddValue);
break;
}
}
fakeProgessTimer -= Time.deltaTime;
if (fakeProgessTimer <= 0f)
{
fakeProgess += fakeNextAddValue;
if (fakeProgess > lastProgressData.Target)
fakeProgess = lastProgressData.Target;
}
}
private async void Launch()
{
#if UNITY_EDITOR
enableHotUpdate = false;
#endif
#if PLATFORM_ANDROID && !SDK_BILIBILI //B站在隐私协议之后才申请权限
if (!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite))
{
Debug.Log("申请权限: " + Permission.ExternalStorageWrite);
Permission.RequestUserPermission(Permission.ExternalStorageWrite);
}
#endif
realProgess = 0f;
versionUpdateUI.UpdateProgressText(LocalizeMono.Instance.GetLocalizeText("NetworkConnection")); // 尝试网络连接中...
_versionUpdateHandle = new VersionUpdateHandle(versionUpdateUI, SetRealProgress);
VersionUpdateInfo versionUpdateInfo = _versionUpdateHandle.GetVersionInfo();
PackDataInst.CreateInst();
UpdateLocalInfo localInfo = PackDataInst.Inst.localInfo;
string acquiring = LocalizeMono.Instance.GetLocalizeText("Acquiring"); // 获取中...
string displayLocalVersion = VersionDisplayFormatter.Format(localInfo.localFullVersion, "[LTRes]GameLauncher");
Debug.Log("[LTRes]GameLauncher - ShowVersion关键日志 stage=LaunchInit, fullOldVersion=" + localInfo.localFullVersion + ", fullNewVersion=" + acquiring + ", displayOldVersion=" + displayLocalVersion + ", displayNewVersion=" + acquiring + ", serverName=" + acquiring);
versionUpdateUI.ShowVersion(Application.version, localInfo.workSpace, displayLocalVersion, acquiring, acquiring);
BIToolinAOT.Instance.Init();//TalkingData初始化
try
{
DeviceIdHandle.Instance.Initialize();//设备ID获取初始化
#if UNITY_IOS && !UNITY_EDITOR
Debug.Log("开始获取iOS标识");
DeviceIdHandle.Instance.IDFV = FetchIDFV();
FetchIDFA();
Debug.Log("结束获取iOS标识");
#endif
}
catch (Exception e)
{
Debug.LogError($"DeviceIdHandle Initialize Error:{e}");
}
//PlayerPrefs.SetString("BI_Platform", TalkingDataInitTool.channel);
#region 更新AppConfig
bool downloadResult;
do
{
try
{
versionUpdateUI.UpdateProgressText(LocalizeMono.Instance.GetLocalizeText("RequestConfiguration")); // 开始请求APPConfig
BIToolinAOT.Instance.TrackEventInAOT("app_config_request", null);
downloadResult = await PreConfig.instance.DownloadConfig();
versionUpdateUI.UpdateProgressText(LocalizeMono.Instance.GetLocalizeText("RequestConfigurationComplete")); // 请求APPConfig结束
string displayLocalVersionAfterConfig = VersionDisplayFormatter.Format(localInfo.localFullVersion, "[LTRes]GameLauncher");
Debug.Log("[LTRes]GameLauncher - ShowVersion关键日志 stage=ConfigReady, fullOldVersion=" + localInfo.localFullVersion + ", fullNewVersion=" + acquiring + ", displayOldVersion=" + displayLocalVersionAfterConfig + ", displayNewVersion=" + acquiring + ", serverName=" + PreConfig.instance.ServerName);
versionUpdateUI.ShowVersion(Application.version, localInfo.workSpace, displayLocalVersionAfterConfig, acquiring, PreConfig.instance.ServerName);
BIToolinAOT.Instance.TrackEventInAOT("app_config_response", new System.Collections.Generic.Dictionary<string, object>(){
{"result", downloadResult? 0 : 1 }
});
}
catch (Exception)
{
downloadResult = false;
}
}
while (!downloadResult && await versionUpdateUI.ShowDialog(LocalizeMono.Instance.GetLocalizeText("Tip"), LocalizeMono.Instance.GetLocalizeText("DownloadConfigErrorRetry"))); // 下载配置文件失败,是否重试?
if (!downloadResult)
TempUpdateUI.Instance.OnClickCancel();
#endregion
if (!PreConfig.instance.isConfigValid)
{
// 配置错误,没有读取到ResUrl
Debug.LogError("配置文件错误");
await versionUpdateUI.ShowDialog(LocalizeMono.Instance.GetLocalizeText("Tip"), LocalizeMono.Instance.GetLocalizeText("ConfigError")); // 配置文件错误
TempUpdateUI.Instance.OnClickCancel();
return;
}
await YooAssetHelper.Init();
_simpleLoader = new SimpleLoader_YooAsset();
Debug.Log($"Launch Game! enableHotUpdate:{enableHotUpdate}");
bool result = await _versionUpdateHandle.CheckUpdate();//这里会打点资源检测更新事件
if (!result)
{
Debug.LogError("更新失败!,无法进入游戏");
return;
}
if (enableHotUpdate)
{
#if USE_HCLR
#if USE_OBFUZ
byte[] dynamicSecret = await _simpleLoader.LoadDllBytes(DYNAMIC_SECRET_PATH);
_SetUpDynamicSecret(dynamicSecret);
await _simpleLoader.UnloadDll(DYNAMIC_SECRET_PATH);
#endif
Debug.Log("需要热更新,开始加载程序集");
_hybridCLRHelper = new HybridCLRHelper();
_hybridCLRHelper.SetDllList(AOTGenericReferences.PatchedAOTAssemblyList);
_hybridCLRHelper.RegisterLoader(_simpleLoader);
await _hybridCLRHelper.LoadAssemblies();
Debug.Log("加载程序集结束!");
#endif
}
await _simpleLoader.WarmUpShader();
await EnterGame();
}
private async UniTask EnterGame()
{
Debug.Log("开始加载游戏场景");
SimulationProgress(versionUpdateUI.GetCurProgress());
try
{
Scene scene = await _simpleLoader.LoadSceneAsync(START_SCENE_NAME, LoadSceneMode.Additive);
foreach (GameObject go in scene.GetRootGameObjects())
{
if ("EventSystem".Equals(go.name))
{
Destroy(go);
break;
}
}
}
catch (Exception e)
{
Debug.LogError(e);
}
finally
{
await LoadOver();
Debug.Log("加载游戏场景结束");
}
}
private async void SimulationProgress(float startProgress)
{
versionUpdateUI.UpdateProgressText(LocalizeMono.Instance.GetLocalizeText("InitGaming")); // 初始化游戏...
realProgess = startProgress;
float step = (1f - startProgress) * 0.02f;
while (startProgress + step <= 0.99f)
{
await UniTask.Delay(20);
startProgress += step;
realProgess = startProgress;
}
}
private async UniTask LoadOver()
{
while (true)
{
await UniTask.Yield();
// Debug.Log($"校验是否加载完:{IVersionUpdateUI.IsLoadOver}");
if (StartSceneLoadingManager.IsLoadOver)
{
realProgess = 1f;
versionUpdateUI.UpdateProgressText(LocalizeMono.Instance.GetLocalizeText("Complete")); // 完成
await UniTask.Delay(200);
versionUpdateUI.CloseAll();
await SceneManager.UnloadSceneAsync(0);
return;
}
}
}
/// <summary>
/// 设置进度
/// </summary>
/// <param name="progress"></param>
private void SetRealProgress(float progress)
{
realProgess = progress;
}
#region iOS_DeviceHelper
#if UNITY_IOS && !UNITY_EDITOR
[DllImport("__Internal")]
private static extern void RequestIDFA();
[DllImport("__Internal")]
private static extern string GetIDFV();
#endif
public Action<string> OnIDFAReceived;
// 调用这个方法请求 IDFAiOS 14+ 会弹授权)
// 调用 FetchIDFA() → 弹授权框 → 回调给 Unity
public void FetchIDFA(Action<string> callback = null)
{
#if UNITY_IOS && !UNITY_EDITOR
OnIDFAReceived = callback;
RequestIDFA(); // 调用 Xcode 端方法
#else
callback?.Invoke("Editor_IDFA");
#endif
}
// 调用这个方法直接获取 IDFV
public string FetchIDFV()
{
#if UNITY_IOS && !UNITY_EDITOR
var idfv = GetIDFV();
Debug.Log("ios 获取到 IDFV: " + idfv);
return idfv;
#else
return "Editor_IDFV";
#endif
}
// Xcode 会通过 UnitySendMessage 回调这个方法
public void OnIDFAResult(string idfa)
{
Debug.Log("ios 获取到 IDFA: " + idfa);
DeviceIdHandle.Instance.IDFA = idfa;
OnIDFAReceived?.Invoke(idfa);
}
#endregion
}