Init TEngine4.0.0

Init TEngine4.0.0
This commit is contained in:
ALEXTANG
2023-10-08 15:21:33 +08:00
parent 4c8c37ffd8
commit 8c3d6308b9
3773 changed files with 49313 additions and 150734 deletions

View File

@@ -0,0 +1,14 @@
namespace GameMain
{
public abstract class ProcedureBase : TEngine.ProcedureBase
{
/// <summary>
/// 获取流程是否使用原生对话框
/// 在一些特殊的流程(如游戏逻辑对话框资源更新完成前的流程)中,可以考虑调用原生对话框进行消息提示行为
/// </summary>
public abstract bool UseNativeDialog
{
get;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 531a832bf8dfd734395de52f4141082f
timeCreated: 1528026158
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,34 @@
using TEngine;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureManager>;
namespace GameMain
{
/// <summary>
/// 流程 => 清理缓存。
/// </summary>
public class ProcedureClearCache:ProcedureBase
{
public override bool UseNativeDialog { get; }
private ProcedureOwner _procedureOwner;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
_procedureOwner = procedureOwner;
Log.Info("清理未使用的缓存文件!");
UILoadMgr.Show(UIDefine.UILoadUpdate,$"清理未使用的缓存文件...");
var operation = GameModule.Resource.ClearUnusedCacheFilesAsync();
operation.Completed += Operation_Completed;
}
private void Operation_Completed(YooAsset.AsyncOperationBase obj)
{
UILoadMgr.Show(UIDefine.UILoadUpdate,$"清理完成 即将进入游戏...");
ChangeState<ProcedureLoadAssembly>(_procedureOwner);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b9e990ba01f24ac584014438abc7f3b7
timeCreated: 1679635352

View File

@@ -0,0 +1,201 @@
using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using TEngine;
using UnityEngine;
using UnityEngine.Networking;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureManager>;
using Utility = TEngine.Utility;
namespace GameMain
{
public class ProcedureCreateDownloader : ProcedureBase
{
private int _curTryCount;
private const int MaxTryCount = 3;
public override bool UseNativeDialog { get; }
private ProcedureOwner _procedureOwner;
private ResourceDownloaderOperation _downloader;
private int _totalDownloadCount;
private string _totalSizeMb;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
_procedureOwner = procedureOwner;
Log.Info("创建补丁下载器");
UILoadMgr.Show(UIDefine.UILoadUpdate,$"创建补丁下载器...");
CreateDownloader().Forget();
}
private async UniTaskVoid CreateDownloader()
{
await UniTask.Delay(TimeSpan.FromSeconds(0.5f));
_downloader = GameModule.Resource.CreateResourceDownloader();
if (_downloader.TotalDownloadCount == 0)
{
Log.Info("Not found any download files !");
ChangeState<ProcedureDownloadOver>(_procedureOwner);
}
else
{
//A total of 10 files were found that need to be downloaded
Log.Info($"Found total {_downloader.TotalDownloadCount} files that need download ");
// 发现新更新文件后,挂起流程系统
// 注意:开发者需要在下载前检测磁盘空间不足
_totalDownloadCount = _downloader.TotalDownloadCount;
long totalDownloadBytes = _downloader.TotalDownloadBytes;
float sizeMb = totalDownloadBytes / 1048576f;
sizeMb = Mathf.Clamp(sizeMb, 0.1f, float.MaxValue);
_totalSizeMb = sizeMb.ToString("f1");
if (!SettingsUtils.EnableUpdateData())
{
UILoadTip.ShowMessageBox($"Found update patch files, Total count {_totalDownloadCount} Total size {_totalSizeMb}MB", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_StartUpdate_Notice
, StartDownFile, Application.Quit);
}
else
{
RequestUpdateData().Forget();
}
}
}
void StartDownFile()
{
ChangeState<ProcedureDownloadFile>(_procedureOwner);
}
/// <summary>
/// 请求更新配置数据。
/// </summary>
private async UniTaskVoid RequestUpdateData()
{
Log.Warning("On RequestVersion");
_curTryCount++;
if (_curTryCount > MaxTryCount)
{
UILoadTip.ShowMessageBox(LoadText.Instance.Label_Net_Error, MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry,
() =>
{
_curTryCount = 0;
RequestUpdateData().Forget();
}, Application.Quit);
return;
}
var checkVersionUrl = SettingsUtils.GetUpdateDataUrl();
UILoadMgr.Show(UIDefine.UILoadUpdate, string.Format(LoadText.Instance.Label_Load_Checking, _curTryCount));
if (string.IsNullOrEmpty(checkVersionUrl))
{
Log.Error("LoadMgr.RequestVersion, remote url is empty or null");
UILoadTip.ShowMessageBox(LoadText.Instance.Label_RemoteUrlisNull, MessageShowType.OneButton,
LoadStyle.StyleEnum.Style_QuitApp,
Application.Quit);
return;
}
Log.Info("RequestUpdateData, proxy:" + checkVersionUrl);
var updateDataStr = await Utility.Http.Get(checkVersionUrl);
try
{
UpdateData updateData = Utility.Json.ToObject<UpdateData>(updateDataStr);
ShowUpdateType(updateData);
}
catch (Exception e)
{
Log.Fatal(e);
throw;
}
}
/// <summary>
/// 显示更新方式
/// </summary>
/// <returns></returns>
private void ShowUpdateType(UpdateData data)
{
UILoadMgr.Show(UIDefine.UILoadUpdate, LoadText.Instance.Label_Load_Checked);
//底包更新
if (data.UpdateType == UpdateType.PackageUpdate)
{
UILoadTip.ShowMessageBox(LoadText.Instance.Label_Load_Package, MessageShowType.OneButton,
LoadStyle.StyleEnum.Style_DownLoadApk,
() =>
{
Log.Info("自定义下载APK");
Application.Quit();
});
}
//资源更新
else if (data.UpdateType == UpdateType.ResourceUpdate)
{
//强制
if (data.UpdateStyle == UpdateStyle.Force)
{
//提示
if (data.UpdateNotice == UpdateNotice.Notice)
{
NetworkReachability networkReachability = Application.internetReachability;
string desc = LoadText.Instance.Label_Load_Force_WIFI;
if (networkReachability == NetworkReachability.ReachableViaCarrierDataNetwork)
{
desc = LoadText.Instance.Label_Load_Force_NO_WIFI;
}
desc = string.Format(desc, $"{_totalSizeMb}MB");
UILoadTip.ShowMessageBox(desc, MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_StartUpdate_Notice,
StartDownFile, Application.Quit);
}
//不提示
else if (data.UpdateNotice == UpdateNotice.NoNotice)
{
StartDownFile();
}
}
//非强制
else if (data.UpdateStyle == UpdateStyle.Optional)
{
//提示
if (data.UpdateNotice == UpdateNotice.Notice)
{
UILoadTip.ShowMessageBox(string.Format(LoadText.Instance.Label_Load_Notice,$"{_totalSizeMb}MB"), MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_StartUpdate_Notice,
StartDownFile, () =>
{
ChangeState<ProcedureLoadAssembly>(_procedureOwner);
});
}
//不提示
else if (data.UpdateNotice == UpdateNotice.NoNotice)
{
StartDownFile();
}
}
else
{
Log.Error("LoadMgr._CheckUpdate, style is error,code:" + data.UpdateStyle);
}
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2323d6b64e5a4a1195b9ed1795a64bdd
timeCreated: 1679634708

View File

@@ -0,0 +1,83 @@
using System;
using Cysharp.Threading.Tasks;
using TEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureManager>;
using Utility = TEngine.Utility;
namespace GameMain
{
public class ProcedureDownloadFile:ProcedureBase
{
public override bool UseNativeDialog { get; }
private ProcedureOwner _procedureOwner;
private float _currentDownloadTime;
private float CurrentSpeed =>
(GameModule.Resource.Downloader.TotalDownloadBytes -
GameModule.Resource.Downloader.CurrentDownloadBytes) / _currentDownloadTime;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
_procedureOwner = procedureOwner;
Log.Info("开始下载更新文件!");
UILoadMgr.Show(UIDefine.UILoadUpdate,$"开始下载更新文件...");
BeginDownload().Forget();
}
private async UniTaskVoid BeginDownload()
{
var downloader = GameModule.Resource.Downloader;
// 注册下载回调
downloader.OnDownloadErrorCallback = OnDownloadErrorCallback;
downloader.OnDownloadProgressCallback = OnDownloadProgressCallback;
downloader.BeginDownload();
await downloader;
// 检测下载结果
if (downloader.Status != EOperationStatus.Succeed)
return;
ChangeState<ProcedureDownloadOver>(_procedureOwner);
}
private void OnDownloadErrorCallback(string fileName, string error)
{
UILoadTip.ShowMessageBox($"Failed to download file : {fileName}", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Default
, () => { ChangeState<ProcedureCreateDownloader>(_procedureOwner); }, UnityEngine.Application.Quit);
}
private void OnDownloadProgressCallback(int totalDownloadCount, int currentDownloadCount, long totalDownloadBytes, long currentDownloadBytes)
{
string currentSizeMb = (currentDownloadBytes / 1048576f).ToString("f1");
string totalSizeMb = (totalDownloadBytes / 1048576f).ToString("f1");
// UILoadMgr.Show(UIDefine.UILoadUpdate,$"{currentDownloadCount}/{totalDownloadCount} {currentSizeMb}MB/{totalSizeMb}MB");
string descriptionText = Utility.Text.Format("正在更新,已更新{0},总更新{1},已更新大小{2},总更新大小{3},更新进度{4},当前网速{5}/s",
currentDownloadCount.ToString(),
totalDownloadCount.ToString(),
Utility.File.GetByteLengthString(currentDownloadBytes),
Utility.File.GetByteLengthString(totalDownloadBytes),
GameModule.Resource.Downloader.Progress,
Utility.File.GetByteLengthString((int)CurrentSpeed));
LoadUpdateLogic.Instance.DownProgressAction?.Invoke(GameModule.Resource.Downloader.Progress);
UILoadMgr.Show(UIDefine.UILoadUpdate,descriptionText);
int needTime = 0;
if (CurrentSpeed > 0)
{
needTime = (int)((totalDownloadBytes - currentDownloadBytes) / CurrentSpeed);
}
TimeSpan ts = new TimeSpan(0, 0, needTime);
string timeStr = ts.ToString(@"mm\:ss");
string updateProgress = Utility.Text.Format("剩余时间 {0}({1}/s)", timeStr, Utility.File.GetLengthString((int)CurrentSpeed));
Log.Info(updateProgress);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 19b2799aad474898b96d8551856e6b40
timeCreated: 1679642638

View File

@@ -0,0 +1,31 @@
using TEngine;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureManager>;
namespace GameMain
{
public class ProcedureDownloadOver:ProcedureBase
{
public override bool UseNativeDialog { get; }
private bool _needClearCache;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
Log.Info("下载完成!!!");
UILoadMgr.Show(UIDefine.UILoadUpdate,$"下载完成...");
}
protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)
{
if (_needClearCache)
{
ChangeState<ProcedureClearCache>(procedureOwner);
}
else
{
ChangeState<ProcedureLoadAssembly>(procedureOwner);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b3998cede0874b49afc170062e554c34
timeCreated: 1679635148

View File

@@ -0,0 +1,158 @@
using System;
using Cysharp.Threading.Tasks;
using TEngine;
using UnityEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureManager>;
using Utility = TEngine.Utility;
namespace GameMain
{
/// <summary>
/// 流程 => 初始化Package。
/// </summary>
public class ProcedureInitPackage : ProcedureBase
{
public override bool UseNativeDialog { get; }
private ProcedureOwner _procedureOwner;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
base.OnEnter(procedureOwner);
_procedureOwner = procedureOwner;
//Fire Forget立刻触发UniTask初始化Package
InitPackage(procedureOwner).Forget();
}
private async UniTaskVoid InitPackage(ProcedureOwner procedureOwner)
{
if (GameModule.Resource.PlayMode == EPlayMode.HostPlayMode)
{
if (SettingsUtils.EnableUpdateData())
{
UpdateData updateData = await RequestUpdateData();
if (updateData != null)
{
if (!string.IsNullOrEmpty(updateData.HostServerURL))
{
SettingsUtils.FrameworkGlobalSettings.HostServerURL = updateData.HostServerURL;
}
if (!string.IsNullOrEmpty(updateData.FallbackHostServerURL))
{
SettingsUtils.FrameworkGlobalSettings.FallbackHostServerURL = updateData.FallbackHostServerURL;
}
}
}
}
var initializationOperation = GameModule.Resource.InitPackage();
await UniTask.Delay(TimeSpan.FromSeconds(1f));
await initializationOperation.ToUniTask();
if (initializationOperation.Status == EOperationStatus.Succeed)
{
//热更新阶段文本初始化
LoadText.Instance.InitConfigData(null);
//热更新UI初始化
UILoadMgr.Initialize();
EPlayMode playMode = GameModule.Resource.PlayMode;
// 编辑器模式。
if (playMode == EPlayMode.EditorSimulateMode)
{
Log.Info("Editor resource mode detected.");
ChangeState<ProcedurePreload>(procedureOwner);
}
// 单机模式。
else if (playMode == EPlayMode.OfflinePlayMode)
{
Log.Info("Package resource mode detected.");
ChangeState<ProcedureInitResources>(procedureOwner);
}
// 可更新模式。
else if (playMode == EPlayMode.HostPlayMode)
{
// 打开启动UI。
UILoadMgr.Show(UIDefine.UILoadUpdate);
Log.Info("Updatable resource mode detected.");
ChangeState<ProcedureUpdateVersion>(procedureOwner);
}
// 可更新模式。
else if (playMode == EPlayMode.WebPlayMode)
{
Log.Info("WebPlayMode resource mode detected.");
ChangeState<ProcedurePreload>(procedureOwner);
}
else
{
Log.Error("UnKnow resource mode detected Please check???");
}
}
else
{
// 打开启动UI。
UILoadMgr.Show(UIDefine.UILoadUpdate);
Log.Error($"{initializationOperation.Error}");
// 打开启动UI。
UILoadMgr.Show(UIDefine.UILoadUpdate, $"资源初始化失败!");
UILoadTip.ShowMessageBox($"资源初始化失败!点击确认重试 \n \n <color=#FF0000>原因{initializationOperation.Error}</color>", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry
, () => { Retry(procedureOwner); }, UnityEngine.Application.Quit);
}
}
private void Retry(ProcedureOwner procedureOwner)
{
// 打开启动UI。
UILoadMgr.Show(UIDefine.UILoadUpdate, $"重新初始化资源中...");
InitPackage(procedureOwner).Forget();
}
/// <summary>
/// 请求更新配置数据。
/// </summary>
private async UniTask<UpdateData> RequestUpdateData()
{
// 打开启动UI。
UILoadMgr.Show(UIDefine.UILoadUpdate);
var checkVersionUrl = SettingsUtils.GetUpdateDataUrl();
if (string.IsNullOrEmpty(checkVersionUrl))
{
Log.Error("LoadMgr.RequestVersion, remote url is empty or null");
return null;
}
Log.Info("RequestUpdateData, proxy:" + checkVersionUrl);
try
{
var updateDataStr = await Utility.Http.Get(checkVersionUrl);
UpdateData updateData = Utility.Json.ToObject<UpdateData>(updateDataStr);
return updateData;
}
catch (Exception e)
{
// 打开启动UI。
UILoadTip.ShowMessageBox("请求配置数据失败!点击确认重试", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry
, () => { InitPackage(_procedureOwner).Forget(); }, Application.Quit);
Log.Warning(e);
return null;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0fc41b358bdc498681a63f72ad8942c8
timeCreated: 1679639147

View File

@@ -0,0 +1,46 @@
using System;
using Cysharp.Threading.Tasks;
using TEngine;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureManager>;
namespace GameMain
{
public class ProcedureInitResources : ProcedureBase
{
private bool m_InitResourcesComplete = false;
public override bool UseNativeDialog => true;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
base.OnEnter(procedureOwner);
m_InitResourcesComplete = false;
UILoadMgr.Show(UIDefine.UILoadUpdate,"初始化资源中...");
// 注意:使用单机模式并初始化资源前,需要先构建 AssetBundle 并复制到 StreamingAssets 中,否则会产生 HTTP 404 错误
OnInitResourcesComplete().Forget();
}
protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
if (!m_InitResourcesComplete)
{
// 初始化资源未完成则继续等待
return;
}
ChangeState<ProcedurePreload>(procedureOwner);
}
private async UniTaskVoid OnInitResourcesComplete()
{
await UniTask.Delay(TimeSpan.FromSeconds(1f));
m_InitResourcesComplete = true;
Log.Info("Init resources complete.");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 49285f361d08436aab23bdf729c10ea0
timeCreated: 1679635748

View File

@@ -0,0 +1,81 @@
using TEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureManager>;
namespace GameMain
{
/// <summary>
/// 流程 => 启动器。
/// </summary>
public class ProcedureLaunch : ProcedureBase
{
public override bool UseNativeDialog => true;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
base.OnEnter(procedureOwner);
// 语言配置:设置当前使用的语言,如果不设置,则默认使用操作系统语言
InitLanguageSettings();
// 声音配置:根据用户配置数据,设置即将使用的声音选项
InitSoundSettings();
}
protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
// 运行一帧即切换到 Splash 展示流程
ChangeState<ProcedureSplash>(procedureOwner);
}
private void InitLanguageSettings()
{
if (GameModule.Resource.PlayMode == EPlayMode.EditorSimulateMode && GameModule.Base.EditorLanguage == Language.Unspecified)
{
// 编辑器资源模式直接使用 Inspector 上设置的语言
return;
}
Language language = GameModule.Localization.Language;
if (GameModule.Setting.HasSetting(Constant.Setting.Language))
{
try
{
string languageString = GameModule.Setting.GetString(Constant.Setting.Language);
language = (Language)System.Enum.Parse(typeof(Language), languageString);
}
catch(System.Exception exception)
{
Log.Error("Init language error, reason {0}",exception.ToString());
}
}
if (language != Language.English
&& language != Language.ChineseSimplified
&& language != Language.ChineseTraditional)
{
// 若是暂不支持的语言,则使用英语
language = Language.English;
GameModule.Setting.SetString(Constant.Setting.Language, language.ToString());
GameModule.Setting.Save();
}
GameModule.Localization.Language = language;
Log.Info("Init language settings complete, current language is '{0}'.", language.ToString());
}
private void InitSoundSettings()
{
GameModule.Audio.MusicEnable = !GameModule.Setting.GetBool(Constant.Setting.MusicMuted, false);
GameModule.Audio.MusicVolume = GameModule.Setting.GetFloat(Constant.Setting.MusicVolume, 1f);
GameModule.Audio.SoundEnable = !GameModule.Setting.GetBool(Constant.Setting.SoundMuted, false);
GameModule.Audio.SoundVolume = GameModule.Setting.GetFloat(Constant.Setting.SoundVolume, 1f);
GameModule.Audio.UISoundEnable = !GameModule.Setting.GetBool(Constant.Setting.UISoundMuted, false);
GameModule.Audio.UISoundVolume = GameModule.Setting.GetFloat(Constant.Setting.UISoundVolume, 1f);
Log.Info("Init sound settings complete.");
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d6c3d65d0c3a2104383e82700116b3d1
timeCreated: 1528026159
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,286 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using HybridCLR;
using UnityEngine;
using TEngine;
using System.Reflection;
using YooAsset;
namespace GameMain
{
/// <summary>
/// 流程加载器 - 代码初始化
/// </summary>
public class ProcedureLoadAssembly : ProcedureBase
{
/// <summary>
/// 是否需要加载热更新DLL
/// </summary>
public bool NeedLoadDll => (int)GameModule.Resource.PlayMode > (int)EPlayMode.EditorSimulateMode;
private bool m_enableAddressable = true;
public override bool UseNativeDialog => true;
private int m_LoadAssetCount;
private int m_LoadMetadataAssetCount;
private int m_FailureAssetCount;
private int m_FailureMetadataAssetCount;
private bool m_LoadAssemblyComplete;
private bool m_LoadMetadataAssemblyComplete;
private bool m_LoadAssemblyWait;
#pragma warning disable CS0414
private bool m_LoadMetadataAssemblyWait;
#pragma warning restore CS0414
private Assembly m_MainLogicAssembly;
private List<Assembly> m_HotfixAssemblys;
private IFsm<IProcedureManager> m_procedureOwner;
protected override void OnEnter(IFsm<IProcedureManager> procedureOwner)
{
base.OnEnter(procedureOwner);
Log.Debug("HyBridCLR ProcedureLoadAssembly OnEnter");
m_procedureOwner = procedureOwner;
m_LoadAssemblyComplete = false;
m_HotfixAssemblys = new List<Assembly>();
//AOT Assembly加载原始metadata
if (SettingsUtils.HybridCLRCustomGlobalSettings.Enable)
{
#if !UNITY_EDITOR
m_LoadMetadataAssemblyComplete = false;
LoadMetadataForAOTAssembly();
#else
m_LoadMetadataAssemblyComplete = true;
#endif
}
else
{
m_LoadMetadataAssemblyComplete = true;
}
if (!NeedLoadDll || GameModule.Resource.PlayMode == EPlayMode.EditorSimulateMode)
{
m_MainLogicAssembly = GetMainLogicAssembly();
}
else
{
if (SettingsUtils.HybridCLRCustomGlobalSettings.Enable)
{
foreach (string hotUpdateDllName in SettingsUtils.HybridCLRCustomGlobalSettings.HotUpdateAssemblies)
{
var assetLocation = hotUpdateDllName;
if (!m_enableAddressable)
{
assetLocation = Utility.Path.GetRegularPath(
Path.Combine(
"Assets",
SettingsUtils.HybridCLRCustomGlobalSettings.AssemblyTextAssetPath,
$"{hotUpdateDllName}{SettingsUtils.HybridCLRCustomGlobalSettings.AssemblyTextAssetExtension}"));
}
Log.Debug($"LoadAsset: [ {assetLocation} ]");
m_LoadAssetCount++;
GameModule.Resource.LoadAssetAsync<TextAsset>(assetLocation,LoadAssetSuccess);
}
m_LoadAssemblyWait = true;
}
else
{
m_MainLogicAssembly = GetMainLogicAssembly();
}
}
if (m_LoadAssetCount == 0)
{
m_LoadAssemblyComplete = true;
}
}
protected override void OnUpdate(IFsm<IProcedureManager> procedureOwner, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
if (!m_LoadAssemblyComplete)
{
return;
}
if (!m_LoadMetadataAssemblyComplete)
{
return;
}
AllAssemblyLoadComplete();
}
private void AllAssemblyLoadComplete()
{
ChangeState<ProcedureStartGame>(m_procedureOwner);
#if UNITY_EDITOR
m_MainLogicAssembly = AppDomain.CurrentDomain.GetAssemblies().
First(assembly => $"{assembly.GetName().Name}.dll" == SettingsUtils.HybridCLRCustomGlobalSettings.LogicMainDllName);
#endif
if (m_MainLogicAssembly == null)
{
Log.Fatal($"Main logic assembly missing.");
return;
}
var appType = m_MainLogicAssembly.GetType("GameApp");
if (appType == null)
{
Log.Fatal($"Main logic type 'GameMain' missing.");
return;
}
var entryMethod = appType.GetMethod("Entrance");
if (entryMethod == null)
{
Log.Fatal($"Main logic entry method 'Entrance' missing.");
return;
}
object[] objects = new object[] { new object[] { m_HotfixAssemblys } };
entryMethod.Invoke(appType, objects);
}
private Assembly GetMainLogicAssembly()
{
Assembly mainLogicAssembly = null;
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (string.Compare(SettingsUtils.HybridCLRCustomGlobalSettings.LogicMainDllName, $"{assembly.GetName().Name}.dll",
StringComparison.Ordinal) == 0)
{
mainLogicAssembly = assembly;
}
foreach (var hotUpdateDllName in SettingsUtils.HybridCLRCustomGlobalSettings.HotUpdateAssemblies)
{
if (hotUpdateDllName == $"{assembly.GetName().Name}.dll")
{
m_HotfixAssemblys.Add(assembly);
}
}
if (mainLogicAssembly != null && m_HotfixAssemblys.Count == SettingsUtils.HybridCLRCustomGlobalSettings.HotUpdateAssemblies.Count)
{
break;
}
}
return mainLogicAssembly;
}
/// <summary>
/// 加载代码资源成功回调。
/// </summary>
/// <param name="assetOperationHandle">资源操作句柄。</param>
private void LoadAssetSuccess(AssetOperationHandle assetOperationHandle)
{
m_LoadAssetCount--;
var assetName = assetOperationHandle.AssetObject.name;
Log.Debug($"LoadAssetSuccess, assetName: [ {assetName} ]");
var textAsset = assetOperationHandle.AssetObject as TextAsset;
if (textAsset == null)
{
Log.Warning($"Load text asset [ {assetName} ] failed.");
return;
}
try
{
var assembly = Assembly.Load(textAsset.bytes);
if (string.Compare(SettingsUtils.HybridCLRCustomGlobalSettings.LogicMainDllName, assetName, StringComparison.Ordinal) == 0)
{
m_MainLogicAssembly = assembly;
}
m_HotfixAssemblys.Add(assembly);
Log.Debug($"Assembly [ {assembly.GetName().Name} ] loaded");
}
catch (Exception e)
{
m_FailureAssetCount++;
Log.Fatal(e);
throw;
}
finally
{
m_LoadAssemblyComplete = m_LoadAssemblyWait && 0 == m_LoadAssetCount;
}
assetOperationHandle.Dispose();
}
/// <summary>
/// 为Aot Assembly加载原始metadata 这个代码放Aot或者热更新都行。
/// 一旦加载后如果AOT泛型函数对应native实现不存在则自动替换为解释模式执行。
/// </summary>
public void LoadMetadataForAOTAssembly()
{
// 可以加载任意aot assembly的对应的dll。但要求dll必须与unity build过程中生成的裁剪后的dll一致而不能直接使用原始dll。
// 我们在BuildProcessor_xxx里添加了处理代码这些裁剪后的dll在打包时自动被复制到 {项目目录}/HybridCLRData/AssembliesPostIl2CppStrip/{Target} 目录。
// 注意补充元数据是给AOT dll补充元数据而不是给热更新dll补充元数据。
// 热更新dll不缺元数据不需要补充如果调用LoadMetadataForAOTAssembly会返回错误
if (SettingsUtils.HybridCLRCustomGlobalSettings.AOTMetaAssemblies.Count == 0)
{
m_LoadMetadataAssemblyComplete = true;
return;
}
foreach (string aotDllName in SettingsUtils.HybridCLRCustomGlobalSettings.AOTMetaAssemblies)
{
var assetLocation = aotDllName;
if (!m_enableAddressable)
{
assetLocation = Utility.Path.GetRegularPath(
Path.Combine(
"Assets",
SettingsUtils.HybridCLRCustomGlobalSettings.AssemblyTextAssetPath,
$"{aotDllName}{SettingsUtils.HybridCLRCustomGlobalSettings.AssemblyTextAssetExtension}"));
}
Log.Debug($"LoadMetadataAsset: [ {assetLocation} ]");
m_LoadMetadataAssetCount++;
GameModule.Resource.LoadAssetAsync<TextAsset>(assetLocation,LoadMetadataAssetSuccess);
}
m_LoadMetadataAssemblyWait = true;
}
/// <summary>
/// 加载元数据资源成功回调。
/// </summary>
/// <param name="assetOperationHandle">资源操作句柄。</param>
private unsafe void LoadMetadataAssetSuccess(AssetOperationHandle assetOperationHandle)
{
m_LoadMetadataAssetCount--;
string assetName = assetOperationHandle.AssetObject.name;
Log.Debug($"LoadMetadataAssetSuccess, assetName: [ {assetName} ]");
var textAsset = assetOperationHandle.AssetObject as TextAsset;
if (null == textAsset)
{
Log.Debug($"LoadMetadataAssetSuccess:Load text asset [ {assetName} ] failed.");
return;
}
try
{
byte[] dllBytes = textAsset.bytes;
fixed (byte* ptr = dllBytes)
{
// 加载assembly对应的dll会自动为它hook。一旦Aot泛型函数的native函数不存在用解释器版本代码
HomologousImageMode mode = HomologousImageMode.SuperSet;
LoadImageErrorCode err = (LoadImageErrorCode)HybridCLR.RuntimeApi.LoadMetadataForAOTAssembly(dllBytes,mode);
Log.Warning($"LoadMetadataForAOTAssembly:{assetName}. mode:{mode} ret:{err}");
}
}
catch (Exception e)
{
m_FailureMetadataAssetCount++;
Log.Fatal(e.Message);
throw;
}
finally
{
m_LoadMetadataAssemblyComplete = m_LoadMetadataAssemblyWait && 0 == m_LoadMetadataAssetCount;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 38e1de092fdb401c955859d7d60d1b5a
timeCreated: 1675668724

View File

@@ -0,0 +1,156 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using TEngine;
using UnityEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureManager>;
namespace GameMain
{
/// <summary>
/// 预加载流程
/// </summary>
public class ProcedurePreload : ProcedureBase
{
private float _progress = 0f;
private Dictionary<string, bool> m_LoadedFlag = new Dictionary<string, bool>();
public override bool UseNativeDialog => true;
private bool m_needProLoadConfig = false;
private bool m_InitConfigXml = false;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
base.OnEnter(procedureOwner);
m_LoadedFlag.Clear();
if (GameModule.Resource.PlayMode == EPlayMode.EditorSimulateMode)
{
m_InitConfigXml = true;
}
UILoadMgr.Show(UIDefine.UILoadUpdate, Utility.Text.Format(LoadText.Instance.Label_Load_Load_Progress, 0));
GameEvent.Send("UILoadUpdate.RefreshVersion");
PreloadResources().Forget();
}
protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
var totalCount = m_LoadedFlag.Count <= 0 ? 1 : m_LoadedFlag.Count;
var loadCount = m_LoadedFlag.Count <= 0 ? 1 : 0;
foreach (KeyValuePair<string, bool> loadedFlag in m_LoadedFlag)
{
if (!loadedFlag.Value)
{
break;
}
else
{
loadCount++;
}
}
if (m_LoadedFlag.Count != 0)
{
UILoadMgr.Show(UIDefine.UILoadUpdate, Utility.Text.Format(LoadText.Instance.Label_Load_Load_Progress, (float)loadCount / totalCount * 100));
}
else
{
LoadUpdateLogic.Instance.DownProgressAction?.Invoke(_progress);
string progressStr = $"{_progress * 100:f1}";
if (Math.Abs(_progress - 1f) < 0.001f)
{
UILoadMgr.Show(UIDefine.UILoadUpdate, LoadText.Instance.Label_Load_Load_Complete);
}
else
{
UILoadMgr.Show(UIDefine.UILoadUpdate, Utility.Text.Format(LoadText.Instance.Label_Load_Load_Progress, progressStr));
}
}
if (loadCount < totalCount)
{
return;
}
if (m_InitConfigXml == false)
{
return;
}
ChangeState<ProcedureLoadAssembly>(procedureOwner);
}
public IEnumerator SmoothValue(float value, float duration, Action callback = null)
{
float time = 0f;
while (time < duration)
{
time += GameTime.deltaTime;
var result = Mathf.Lerp(0, value, time / duration);
_progress = result;
yield return new WaitForEndOfFrame();
}
_progress = value;
callback?.Invoke();
}
private async UniTaskVoid PreloadResources()
{
await SmoothValue(1f, 1.2f).ToUniTask(GameModule.Procedure);
await UniTask.Delay(TimeSpan.FromSeconds(2.5f));
if (m_needProLoadConfig)
{
LoadAllConfig();
}
else
{
m_InitConfigXml = true;
}
}
private void LoadAllConfig()
{
if (GameModule.Resource.PlayMode == EPlayMode.EditorSimulateMode)
{
m_InitConfigXml = true;
return;
}
}
private void LoadConfig(string configName)
{
m_LoadedFlag.Add(configName, false);
GameModule.Resource.LoadAssetAsync<TextAsset>(configName, OnLoadSuccess);
}
private void OnLoadSuccess(AssetOperationHandle assetOperationHandle)
{
if (assetOperationHandle == null)
{
return;
}
var name = assetOperationHandle.GetAssetInfo().Address;
m_LoadedFlag[name] = true;
Log.Info("Load config '{0}' OK.", name);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3510902f458c4bd48422c40d10138549
timeCreated: 1665578910

View File

@@ -0,0 +1,27 @@
using UnityEngine;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureManager>;
namespace GameMain
{
/// <summary>
/// 流程 => 闪屏。
/// </summary>
public class ProcedureSplash : ProcedureBase
{
public override bool UseNativeDialog => true;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
base.OnEnter(procedureOwner);
}
protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
// 播放 Splash 动画
//Splash.Active(splashTime:3f);
//初始化资源包
ChangeState<ProcedureInitPackage>(procedureOwner);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 139ec26f4972a8147a39d4bb34d5fcaa
timeCreated: 1528026159
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
using System;
using Cysharp.Threading.Tasks;
using TEngine;
namespace GameMain
{
public class ProcedureStartGame : ProcedureBase
{
public override bool UseNativeDialog { get; }
protected override void OnEnter(IFsm<IProcedureManager> procedureOwner)
{
base.OnEnter(procedureOwner);
StartGame().Forget();
}
private async UniTaskVoid StartGame()
{
await UniTask.Yield();
UILoadMgr.HideAll();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: db61ddc0f5f34d639fae224363a3ac49
timeCreated: 1665628052

View File

@@ -0,0 +1,50 @@
using System;
using Cysharp.Threading.Tasks;
using TEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureManager>;
namespace GameMain
{
/// <summary>
/// 流程 => 用户尝试更新清单
/// </summary>
public class ProcedureUpdateManifest: ProcedureBase
{
public override bool UseNativeDialog { get; }
protected override void OnEnter(ProcedureOwner procedureOwner)
{
Log.Info("更新资源清单!!!");
UILoadMgr.Show(UIDefine.UILoadUpdate,$"更新清单文件...");
UpdateManifest(procedureOwner).Forget();
}
private async UniTaskVoid UpdateManifest(ProcedureOwner procedureOwner)
{
await UniTask.Delay(TimeSpan.FromSeconds(0.5f));
var operation = GameModule.Resource.UpdatePackageManifestAsync(GameModule.Resource.PackageVersion);
await operation.ToUniTask();
if(operation.Status == EOperationStatus.Succeed)
{
//更新成功
//注意:保存资源版本号作为下次默认启动的版本!
operation.SavePackageVersion();
ChangeState<ProcedureCreateDownloader>(procedureOwner);
}
else
{
Log.Error(operation.Error);
UILoadTip.ShowMessageBox($"用户尝试更新清单失败!点击确认重试 \n \n <color=#FF0000>原因{operation.Error}</color>", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry
, () => { ChangeState<ProcedureUpdateManifest>(procedureOwner); }, UnityEngine.Application.Quit);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3f0ff55ed6ee4711a65cc396946bb43f
timeCreated: 1679634395

View File

@@ -0,0 +1,71 @@
using System;
using Cysharp.Threading.Tasks;
using UnityEngine;
using TEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureManager>;
namespace GameMain
{
/// <summary>
/// 流程 => 用户尝试更新静态版本
/// </summary>
public class ProcedureUpdateVersion : ProcedureBase
{
public override bool UseNativeDialog => true;
private ProcedureOwner _procedureOwner;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
_procedureOwner = procedureOwner;
base.OnEnter(procedureOwner);
UILoadMgr.Show(UIDefine.UILoadUpdate,$"更新静态版本文件...");
//检查设备是否能够访问互联网
if (Application.internetReachability == NetworkReachability.NotReachable)
{
Log.Warning("The device is not connected to the network");
UILoadMgr.Show(UIDefine.UILoadUpdate, LoadText.Instance.Label_Net_UnReachable);
UILoadTip.ShowMessageBox(LoadText.Instance.Label_Net_UnReachable, MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry,
GetStaticVersion().Forget,
() => { ChangeState<ProcedureInitResources>(procedureOwner); });
}
UILoadMgr.Show(UIDefine.UILoadUpdate, LoadText.Instance.Label_RequestVersionIng);
// 用户尝试更新静态版本。
GetStaticVersion().Forget();
}
/// <summary>
/// 向用户尝试更新静态版本。
/// </summary>
private async UniTaskVoid GetStaticVersion()
{
await UniTask.Delay(TimeSpan.FromSeconds(0.5f));
var operation = GameModule.Resource.UpdatePackageVersionAsync();
await operation.ToUniTask();
if (operation.Status == EOperationStatus.Succeed)
{
//线上最新版本operation.PackageVersion
GameModule.Resource.PackageVersion = operation.PackageVersion;
Log.Debug($"Updated package Version : from {GameModule.Resource.GetPackageVersion()} to {operation.PackageVersion}");
ChangeState<ProcedureUpdateManifest>(_procedureOwner);
}
else
{
Log.Error(operation.Error);
UILoadTip.ShowMessageBox($"用户尝试更新静态版本失败!点击确认重试 \n \n <color=#FF0000>原因{operation.Error}</color>", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry
, () => { ChangeState<ProcedureUpdateVersion>(_procedureOwner); }, UnityEngine.Application.Quit);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6f0ff5df84e44adcb8064f46662a1b10
timeCreated: 1665651883