TEngine 6

This commit is contained in:
Alex-Rachel
2025-03-07 23:09:46 +08:00
parent aad8ff3ee5
commit 551727687f
1988 changed files with 46223 additions and 94880 deletions

View File

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

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,35 @@
using Launcher;
using TEngine;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
namespace Procedure
{
/// <summary>
/// 流程 => 清理缓存。
/// </summary>
public class ProcedureClearCache : ProcedureBase
{
public override bool UseNativeDialog { get; }
private ProcedureOwner _procedureOwner;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
_procedureOwner = procedureOwner;
Log.Info("清理未使用的缓存文件!");
LauncherMgr.Show(UIDefine.UILoadUpdate, $"清理未使用的缓存文件...");
var operation = _resourceModule.ClearCacheFilesAsync();
operation.Completed += Operation_Completed;
}
private void Operation_Completed(YooAsset.AsyncOperationBase obj)
{
LauncherMgr.Show(UIDefine.UILoadUpdate, $"清理完成 即将进入游戏...");
ChangeState<ProcedurePreload>(_procedureOwner);
}
}
}

View File

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

View File

@@ -0,0 +1,77 @@
using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using Launcher;
using TEngine;
using UnityEngine;
using UnityEngine.Networking;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
using Utility = TEngine.Utility;
namespace Procedure
{
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("创建补丁下载器");
LauncherMgr.Show(UIDefine.UILoadUpdate, $"创建补丁下载器...");
CreateDownloader().Forget();
}
private async UniTaskVoid CreateDownloader()
{
await UniTask.Delay(TimeSpan.FromSeconds(0.5f));
_downloader = _resourceModule.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");
LauncherMgr.ShowMessageBox($"Found update patch files, Total count {_totalDownloadCount} Total size {_totalSizeMb}MB", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_StartUpdate_Notice
, StartDownFile, Application.Quit);
}
}
void StartDownFile()
{
ChangeState<ProcedureDownloadFile>(_procedureOwner);
}
}
}

View File

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

View File

@@ -0,0 +1,106 @@
using System;
using Cysharp.Threading.Tasks;
using Launcher;
using TEngine;
using UnityEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
using Utility = TEngine.Utility;
namespace Procedure
{
public class ProcedureDownloadFile : ProcedureBase
{
public override bool UseNativeDialog { get; }
private ProcedureOwner _procedureOwner;
private float _lastUpdateDownloadedSize;
private float _totalSpeed;
private int _speedSampleCount;
private float CurrentSpeed
{
get
{
float interval = Math.Max(Time.deltaTime, 0.01f); // 防止deltaTime过小
var sizeDiff = _resourceModule.Downloader.CurrentDownloadBytes - _lastUpdateDownloadedSize;
_lastUpdateDownloadedSize = _resourceModule.Downloader.CurrentDownloadBytes;
var speed = sizeDiff / interval;
// 使用滑动窗口计算平均速度
_totalSpeed += speed;
_speedSampleCount++;
return _totalSpeed / _speedSampleCount;
}
}
protected override void OnEnter(ProcedureOwner procedureOwner)
{
_procedureOwner = procedureOwner;
Log.Info("开始下载更新文件!");
LauncherMgr.Show(UIDefine.UILoadUpdate, "开始下载更新文件...");
BeginDownload().Forget();
}
private async UniTaskVoid BeginDownload()
{
var downloader = _resourceModule.Downloader;
// 注册下载回调
downloader.DownloadErrorCallback = OnDownloadErrorCallback;
downloader.DownloadUpdateCallback = OnDownloadProgressCallback;
downloader.BeginDownload();
await downloader;
// 检测下载结果
if (downloader.Status != EOperationStatus.Succeed)
return;
ChangeState<ProcedureDownloadOver>(_procedureOwner);
}
private void OnDownloadErrorCallback(DownloadErrorData downloadErrorData)
{
LauncherMgr.ShowMessageBox($"Failed to download file : {downloadErrorData.FileName}",
MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Default,
() => { ChangeState<ProcedureCreateDownloader>(_procedureOwner); }, UnityEngine.Application.Quit);
}
private void OnDownloadProgressCallback(DownloadUpdateData downloadUpdateData)
{
string currentSizeMb = (downloadUpdateData.CurrentDownloadBytes / 1048576f).ToString("f1");
string totalSizeMb = (downloadUpdateData.TotalDownloadBytes / 1048576f).ToString("f1");
float progressPercentage = _resourceModule.Downloader.Progress * 100;
string speed = Utility.File.GetLengthString((int)CurrentSpeed);
string line1 = Utility.Text.Format("正在更新,已更新 {0}/{1} ({2:F2}%)", downloadUpdateData.CurrentDownloadCount,
downloadUpdateData.TotalDownloadCount, progressPercentage);
string line2 = Utility.Text.Format("已更新大小 {0}MB/{1}MB", currentSizeMb, totalSizeMb);
string line3 = Utility.Text.Format("当前网速 {0}/s剩余时间 {1}", speed,
GetRemainingTime(downloadUpdateData.TotalDownloadBytes, downloadUpdateData.CurrentDownloadBytes,
CurrentSpeed));
LauncherMgr.UpdateUIProgress(_resourceModule.Downloader.Progress);
LauncherMgr.Show(UIDefine.UILoadUpdate, $"{line1}\n{line2}\n{line3}");
Log.Info($"{line1} {line2} {line3}");
}
private string GetRemainingTime(long totalBytes, long currentBytes, float speed)
{
int needTime = 0;
if (speed > 0)
{
needTime = (int)((totalBytes - currentBytes) / speed);
}
TimeSpan ts = new TimeSpan(0, 0, needTime);
return ts.ToString(@"mm\:ss");
}
}
}

View File

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

View File

@@ -0,0 +1,36 @@
using Launcher;
using TEngine;
using UnityEngine;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
namespace Procedure
{
public class ProcedureDownloadOver : ProcedureBase
{
public override bool UseNativeDialog { get; }
private bool _needClearCache;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
Log.Info("下载完成!!!");
LauncherMgr.Show(UIDefine.UILoadUpdate, $"下载完成...");
// 下载完成之后再保存本地版本。
PlayerPrefs.SetString("GAME_VERSION", _resourceModule.PackageVersion);
}
protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)
{
if (_needClearCache)
{
ChangeState<ProcedureClearCache>(procedureOwner);
}
else
{
ChangeState<ProcedurePreload>(procedureOwner);
}
}
}
}

View File

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

View File

@@ -0,0 +1,123 @@
using System;
using Cysharp.Threading.Tasks;
using Launcher;
using TEngine;
using UnityEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
using Utility = TEngine.Utility;
namespace Procedure
{
/// <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)
{
try
{
var initializationOperation = await _resourceModule.InitPackage(_resourceModule.DefaultPackageName);
if (initializationOperation.Status == EOperationStatus.Succeed)
{
//热更新阶段文本初始化
LoadText.Instance.InitConfigData(null);
EPlayMode playMode = _resourceModule.PlayMode;
// 编辑器模式。
if (playMode == EPlayMode.EditorSimulateMode)
{
Log.Info("Editor resource mode detected.");
ChangeState<ProcedureInitResources>(procedureOwner);
}
// 单机模式。
else if (playMode == EPlayMode.OfflinePlayMode)
{
Log.Info("Package resource mode detected.");
ChangeState<ProcedureInitResources>(procedureOwner);
}
// 可更新模式。
else if (playMode == EPlayMode.HostPlayMode ||
playMode == EPlayMode.WebPlayMode)
{
// 打开启动UI。
LauncherMgr.Show(UIDefine.UILoadUpdate);
Log.Info("Updatable resource mode detected.");
ChangeState<ProcedureUpdateVersion>(procedureOwner);
}
else
{
Log.Error("UnKnow resource mode detected Please check???");
}
}
else
{
// 打开启动UI。
LauncherMgr.Show(UIDefine.UILoadUpdate);
Log.Error($"{initializationOperation.Error}");
// 打开启动UI。
LauncherMgr.Show(UIDefine.UILoadUpdate, $"资源初始化失败!");
LauncherMgr.ShowMessageBox(
$"资源初始化失败!点击确认重试 \n \n <color=#FF0000>原因{initializationOperation.Error}</color>",
MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry
, () => { Retry(procedureOwner); }, UnityEngine.Application.Quit);
}
}
catch (Exception e)
{
OnInitPackageFailed(procedureOwner, e.Message);
}
}
private void OnInitPackageFailed(ProcedureOwner procedureOwner, string message)
{
// 打开启动UI。
LauncherMgr.Show(UIDefine.UILoadUpdate);
Log.Error($"{message}");
// 打开启动UI。
LauncherMgr.Show(UIDefine.UILoadUpdate, $"资源初始化失败!");
if (message.Contains("PackageManifest_DefaultPackage.version Error : HTTP/1.1 404 Not Found"))
{
message = "请检查StreamingAssets/package/DefaultPackage/PackageManifest_DefaultPackage.version是否存在";
}
LauncherMgr.ShowMessageBox($"资源初始化失败!点击确认重试 \n \n <color=#FF0000>原因{message}</color>", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry
, () => { Retry(procedureOwner); },
Application.Quit);
}
private void Retry(ProcedureOwner procedureOwner)
{
// 打开启动UI。
LauncherMgr.Show(UIDefine.UILoadUpdate, $"重新初始化资源中...");
InitPackage(procedureOwner).Forget();
}
}
}

View File

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

View File

@@ -0,0 +1,90 @@
using System;
using System.Collections;
using Cysharp.Threading.Tasks;
using Launcher;
using TEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
namespace Procedure
{
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;
LauncherMgr.Show(UIDefine.UILoadUpdate, "初始化资源中...");
// 注意:使用单机模式并初始化资源前,需要先构建 AssetBundle 并复制到 StreamingAssets 中,否则会产生 HTTP 404 错误
ModuleSystem.GetModule<IUpdateDriver>().StartCoroutine(InitResources(procedureOwner));
}
protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
if (!m_InitResourcesComplete)
{
// 初始化资源未完成则继续等待
return;
}
ChangeState<ProcedurePreload>(procedureOwner);
}
/// <summary>
/// 编辑器与单机保持相同流程。
/// </summary>
/// <returns></returns>
private IEnumerator InitResources(ProcedureOwner procedureOwner)
{
string packageVersion;
if (_resourceModule.PlayMode != EPlayMode.HostPlayMode)
{
// 2. 获取资源清单的版本信息
var operation1 = _resourceModule.RequestPackageVersionAsync();
yield return operation1;
if (operation1.Status != EOperationStatus.Succeed)
{
OnInitResourcesError(procedureOwner);
yield break;
}
packageVersion = operation1.PackageVersion;
}
else
{
packageVersion = _resourceModule.PackageVersion;
}
Log.Info($"Init resource package version : {packageVersion}");
// 3. 传入的版本信息更新资源清单
var operation = _resourceModule.UpdatePackageManifestAsync(packageVersion);
yield return operation;
if (operation.Status != EOperationStatus.Succeed)
{
OnInitResourcesError(procedureOwner);
yield break;
}
m_InitResourcesComplete = true;
}
private void OnInitResourcesError(ProcedureOwner procedureOwner)
{
LauncherMgr.ShowMessageBox($"初始化资源失败!点击确认重试</color>", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry
, () =>
{
ModuleSystem.GetModule<IUpdateDriver>().StartCoroutine(InitResources(procedureOwner));
}, UnityEngine.Application.Quit);
}
}
}

View File

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

View File

@@ -0,0 +1,85 @@
using Launcher;
using TEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
namespace Procedure
{
/// <summary>
/// 流程 => 启动器。
/// </summary>
public class ProcedureLaunch : ProcedureBase
{
public override bool UseNativeDialog => true;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
base.OnEnter(procedureOwner);
//热更新UI初始化
LauncherMgr.Initialize();
// 语言配置:设置当前使用的语言,如果不设置,则默认使用操作系统语言
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,297 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
#if ENABLE_HYBRIDCLR
using HybridCLR;
#endif
using UnityEngine;
using TEngine;
using System.Reflection;
using YooAsset;
using Cysharp.Threading.Tasks;
namespace Procedure
{
/// <summary>
/// 流程加载器 - 代码初始化
/// </summary>
public class ProcedureLoadAssembly : ProcedureBase
{
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<IProcedureModule> m_procedureOwner;
private UpdateSetting m_Setting;
protected override void OnInit(IFsm<IProcedureModule> procedureOwner)
{
base.OnInit(procedureOwner);
m_Setting = Settings.UpdateSetting;
}
protected override void OnEnter(IFsm<IProcedureModule> procedureOwner)
{
base.OnEnter(procedureOwner);
Log.Debug("HyBridCLR ProcedureLoadAssembly OnEnter");
m_procedureOwner = procedureOwner;
LoadAssembly().Forget();
}
private async UniTaskVoid LoadAssembly()
{
m_LoadAssemblyComplete = false;
m_HotfixAssemblys = new List<Assembly>();
//AOT Assembly加载原始metadata
if (m_Setting.Enable)
{
#if !UNITY_EDITOR
m_LoadMetadataAssemblyComplete = false;
LoadMetadataForAOTAssembly();
#else
m_LoadMetadataAssemblyComplete = true;
#endif
}
else
{
m_LoadMetadataAssemblyComplete = true;
}
if (!m_Setting.Enable || _resourceModule.PlayMode == EPlayMode.EditorSimulateMode)
{
m_MainLogicAssembly = GetMainLogicAssembly();
}
else
{
if (m_Setting.Enable)
{
foreach (string hotUpdateDllName in m_Setting.HotUpdateAssemblies)
{
var assetLocation = hotUpdateDllName;
if (!m_enableAddressable)
{
assetLocation = Utility.Path.GetRegularPath(
Path.Combine(
"Assets",
m_Setting.AssemblyTextAssetPath,
$"{hotUpdateDllName}{m_Setting.AssemblyTextAssetExtension}"));
}
Log.Debug($"LoadAsset: [ {assetLocation} ]");
m_LoadAssetCount++;
var result = await _resourceModule.LoadAssetAsync<TextAsset>(assetLocation);
LoadAssetSuccess(result);
}
m_LoadAssemblyWait = true;
}
else
{
m_MainLogicAssembly = GetMainLogicAssembly();
}
}
if (m_LoadAssetCount == 0)
{
m_LoadAssemblyComplete = true;
}
}
protected override void OnUpdate(IFsm<IProcedureModule> 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 = GetMainLogicAssembly();
#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()
{
m_HotfixAssemblys.Clear();
Assembly mainLogicAssembly = null;
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (string.Compare(m_Setting.LogicMainDllName, $"{assembly.GetName().Name}.dll",
StringComparison.Ordinal) == 0)
{
mainLogicAssembly = assembly;
}
foreach (var hotUpdateDllName in m_Setting.HotUpdateAssemblies)
{
if (hotUpdateDllName == $"{assembly.GetName().Name}.dll")
{
m_HotfixAssemblys.Add(assembly);
}
}
if (mainLogicAssembly != null && m_HotfixAssemblys.Count == m_Setting.HotUpdateAssemblies.Count)
{
break;
}
}
return mainLogicAssembly;
}
/// <summary>
/// 加载代码资源成功回调。
/// </summary>
/// <param name="textAsset">代码资产。</param>
private void LoadAssetSuccess(TextAsset textAsset)
{
m_LoadAssetCount--;
if (textAsset == null)
{
Log.Warning($"Load Assembly failed.");
return;
}
var assetName = textAsset.name;
Log.Debug($"LoadAssetSuccess, assetName: [ {assetName} ]");
try
{
var assembly = Assembly.Load(textAsset.bytes);
if (string.Compare(m_Setting.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;
}
_resourceModule.UnloadAsset(textAsset);
}
/// <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 (m_Setting.AOTMetaAssemblies.Count == 0)
{
m_LoadMetadataAssemblyComplete = true;
return;
}
foreach (string aotDllName in m_Setting.AOTMetaAssemblies)
{
var assetLocation = aotDllName;
if (!m_enableAddressable)
{
assetLocation = Utility.Path.GetRegularPath(
Path.Combine(
"Assets",
m_Setting.AssemblyTextAssetPath,
$"{aotDllName}{m_Setting.AssemblyTextAssetExtension}"));
}
Log.Debug($"LoadMetadataAsset: [ {assetLocation} ]");
m_LoadMetadataAssetCount++;
_resourceModule.LoadAsset<TextAsset>(assetLocation, LoadMetadataAssetSuccess);
}
m_LoadMetadataAssemblyWait = true;
}
/// <summary>
/// 加载元数据资源成功回调。
/// </summary>
/// <param name="textAsset">代码资产。</param>
private void LoadMetadataAssetSuccess(TextAsset textAsset)
{
m_LoadMetadataAssetCount--;
if (null == textAsset)
{
Log.Debug($"LoadMetadataAssetSuccess:Load Metadata failed.");
return;
}
string assetName = textAsset.name;
Log.Debug($"LoadMetadataAssetSuccess, assetName: [ {assetName} ]");
try
{
byte[] dllBytes = textAsset.bytes;
#if ENABLE_HYBRIDCLR
// 加载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}");
#endif
}
catch (Exception e)
{
m_FailureMetadataAssetCount++;
Log.Fatal(e.Message);
throw;
}
finally
{
m_LoadMetadataAssemblyComplete = m_LoadMetadataAssemblyWait && 0 == m_LoadMetadataAssetCount;
}
_resourceModule.UnloadAsset(textAsset);
}
}
}

View File

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

View File

@@ -0,0 +1,175 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using Launcher;
using TEngine;
using UnityEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
namespace Procedure
{
/// <summary>
/// 预加载流程
/// </summary>
public class ProcedurePreload : ProcedureBase
{
private float _progress = 0f;
private readonly Dictionary<string, bool> _loadedFlag = new Dictionary<string, bool>();
public override bool UseNativeDialog => true;
private readonly bool _needProLoadConfig = true;
private ProcedureOwner _procedureOwner;
/// <summary>
/// 预加载回调。
/// </summary>
private LoadAssetCallbacks m_PreLoadAssetCallbacks;
protected override void OnInit(ProcedureOwner procedureOwner)
{
base.OnInit(procedureOwner);
_procedureOwner = procedureOwner;
m_PreLoadAssetCallbacks = new LoadAssetCallbacks(OnPreLoadAssetSuccess, OnPreLoadAssetFailure);
}
protected override void OnEnter(ProcedureOwner procedureOwner)
{
base.OnEnter(procedureOwner);
_loadedFlag.Clear();
LauncherMgr.Show(UIDefine.UILoadUpdate, Utility.Text.Format(LoadText.Instance.Label_Load_Load_Progress, 0));
GameEvent.Send("UILoadUpdate.RefreshVersion");
PreloadResources();
}
protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
var totalCount = _loadedFlag.Count <= 0 ? 1 : _loadedFlag.Count;
var loadCount = _loadedFlag.Count <= 0 ? 1 : 0;
foreach (KeyValuePair<string, bool> loadedFlag in _loadedFlag)
{
if (!loadedFlag.Value)
{
break;
}
else
{
loadCount++;
}
}
if (_loadedFlag.Count != 0)
{
LauncherMgr.Show(UIDefine.UILoadUpdate, Utility.Text.Format(LoadText.Instance.Label_Load_Load_Progress, (float)loadCount / totalCount * 100));
}
else
{
LauncherMgr.UpdateUIProgress(_progress);
string progressStr = $"{_progress * 100:f1}";
if (Math.Abs(_progress - 1f) < 0.001f)
{
LauncherMgr.Show(UIDefine.UILoadUpdate, LoadText.Instance.Label_Load_Load_Complete);
}
else
{
LauncherMgr.Show(UIDefine.UILoadUpdate, Utility.Text.Format(LoadText.Instance.Label_Load_Load_Progress, progressStr));
}
}
if (loadCount < totalCount)
{
return;
}
ChangeProcedureToLoadAssembly();
}
private async UniTaskVoid SmoothValue(float value, float duration, Action callback = null)
{
float time = 0f;
while (time < duration)
{
time += Time.deltaTime;
var result = Mathf.Lerp(0, value, time / duration);
_progress = result;
await UniTask.Yield();
}
_progress = value;
callback?.Invoke();
}
private void PreloadResources()
{
if (_needProLoadConfig)
{
LoadAllConfig();
}
}
private void LoadAllConfig()
{
if (_resourceModule.PlayMode == EPlayMode.EditorSimulateMode)
{
return;
}
AssetInfo[] assetInfos = _resourceModule.GetAssetInfos("PRELOAD");
foreach (var assetInfo in assetInfos)
{
PreLoad(assetInfo.Address);
}
#if UNITY_WEBGL
AssetInfo[] webAssetInfos = _resourceModule.GetAssetInfos("WEBGL_PRELOAD");
foreach (var assetInfo in webAssetInfos)
{
PreLoad(assetInfo.Address);
}
#endif
if (_loadedFlag.Count <= 0)
{
// SmoothValue(1, 1f, ChangeProcedureToLoadAssembly).Forget();
return;
}
}
private void PreLoad(string location)
{
_loadedFlag.Add(location, false);
_resourceModule.LoadAssetAsync(location, 100, m_PreLoadAssetCallbacks, null);
}
private void OnPreLoadAssetFailure(string assetName, LoadResourceStatus status, string errormessage, object userdata)
{
Log.Warning("Can not preload asset from '{0}' with error message '{1}'.", assetName, errormessage);
_loadedFlag[assetName] = true;
}
private void OnPreLoadAssetSuccess(string assetName, object asset, float duration, object userdata)
{
Log.Debug("Success preload asset from '{0}' duration '{1}'.", assetName, duration);
_loadedFlag[assetName] = true;
}
private void ChangeProcedureToLoadAssembly()
{
ChangeState<ProcedureLoadAssembly>(_procedureOwner);
}
}
}

View File

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

View File

@@ -0,0 +1,22 @@
using UnityEngine;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
namespace Procedure
{
/// <summary>
/// 流程 => 闪屏。
/// </summary>
public class ProcedureSplash : ProcedureBase
{
public override bool UseNativeDialog => true;
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,24 @@
using System;
using Cysharp.Threading.Tasks;
using Launcher;
using TEngine;
namespace Procedure
{
public class ProcedureStartGame : ProcedureBase
{
public override bool UseNativeDialog { get; }
protected override void OnEnter(IFsm<IProcedureModule> procedureOwner)
{
base.OnEnter(procedureOwner);
StartGame().Forget();
}
private async UniTaskVoid StartGame()
{
await UniTask.Yield();
LauncherMgr.HideAll();
}
}
}

View File

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

View File

@@ -0,0 +1,58 @@
using System;
using Cysharp.Threading.Tasks;
using Launcher;
using TEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
namespace Procedure
{
/// <summary>
/// 流程 => 用户尝试更新清单
/// </summary>
public class ProcedureUpdateManifest: ProcedureBase
{
public override bool UseNativeDialog { get; }
protected override void OnEnter(ProcedureOwner procedureOwner)
{
Log.Info("更新资源清单!!!");
LauncherMgr.Show(UIDefine.UILoadUpdate,$"更新清单文件...");
UpdateManifest(procedureOwner).Forget();
}
private async UniTaskVoid UpdateManifest(ProcedureOwner procedureOwner)
{
await UniTask.Delay(TimeSpan.FromSeconds(0.5f));
var operation = _resourceModule.UpdatePackageManifestAsync(_resourceModule.PackageVersion);
await operation.ToUniTask();
if(operation.Status == EOperationStatus.Succeed)
{
//更新成功
//注意:保存资源版本号作为下次默认启动的版本!
if (_resourceModule.PlayMode == EPlayMode.WebPlayMode ||
_resourceModule.UpdatableWhilePlaying)
{
// 边玩边下载还可以拓展首包支持。
ChangeState<ProcedurePreload>(procedureOwner);
return;
}
ChangeState<ProcedureCreateDownloader>(procedureOwner);
}
else
{
Log.Error(operation.Error);
LauncherMgr.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,127 @@
using System;
using Cysharp.Threading.Tasks;
using Launcher;
using UnityEngine;
using TEngine;
using YooAsset;
using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
namespace Procedure
{
/// <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);
LauncherMgr.Show(UIDefine.UILoadUpdate, $"更新静态版本文件...");
// 检查设备是否能够访问互联网。
if (Application.internetReachability == NetworkReachability.NotReachable)
{
if (!IsNeedUpdate())
{
return;
}
else
{
Log.Error("The device is not connected to the network");
LauncherMgr.ShowMessageBox(LoadText.Instance.Label_Net_UnReachable, MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry,
Application.Quit);
return;
}
}
LauncherMgr.Show(UIDefine.UILoadUpdate, LoadText.Instance.Label_RequestVersionIng);
// 用户尝试更新静态版本。
GetStaticVersion().Forget();
}
/// <summary>
/// 向用户尝试更新静态版本。
/// </summary>
private async UniTaskVoid GetStaticVersion()
{
await UniTask.Delay(TimeSpan.FromSeconds(0.5f));
var operation = _resourceModule.RequestPackageVersionAsync();
try
{
await operation.ToUniTask();
if (operation.Status == EOperationStatus.Succeed)
{
//线上最新版本operation.PackageVersion
_resourceModule.PackageVersion = operation.PackageVersion;
Log.Debug($"Updated package Version : from {_resourceModule.GetPackageVersion()} to {operation.PackageVersion}");
ChangeState<ProcedureUpdateManifest>(_procedureOwner);
}
else
{
OnGetStaticVersionError(operation.Error);
}
}
catch (Exception e)
{
OnGetStaticVersionError(e.Message);
}
}
private void OnGetStaticVersionError(string error)
{
Log.Error(error);
if (!IsNeedUpdate())
{
return;
}
LauncherMgr.ShowMessageBox($"用户尝试更新静态版本失败!点击确认重试 \n \n <color=#FF0000>原因{error}</color>", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry
, () => { ChangeState<ProcedureUpdateVersion>(_procedureOwner); }, UnityEngine.Application.Quit);
}
private bool IsNeedUpdate()
{
// 如果不能联网且当前游戏非强制(不更新可以进入游戏。)
if (Settings.UpdateSetting.UpdateStyle == UpdateStyle.Optional)
{
Log.Warning("The device is not connected to the network");
// 获取上次成功记录的版本
string packageVersion = PlayerPrefs.GetString("GAME_VERSION", string.Empty);
if(string.IsNullOrEmpty(packageVersion))
{
LauncherMgr.Show(UIDefine.UILoadUpdate, LoadText.Instance.Label_Net_UnReachable);
LauncherMgr.ShowMessageBox("没有找到本地版本记录,需要更新资源!", MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry,
GetStaticVersion().Forget,
Application.Quit);
return false;
}
_resourceModule.PackageVersion = packageVersion;
LauncherMgr.Show(UIDefine.UILoadUpdate, LoadText.Instance.Label_Net_UnReachable);
LauncherMgr.ShowMessageBox(LoadText.Instance.Label_Net_UnReachable, MessageShowType.TwoButton,
LoadStyle.StyleEnum.Style_Retry,
GetStaticVersion().Forget,
() => { ChangeState<ProcedureInitResources>(_procedureOwner); });
return false;
}
return true;
}
}
}

View File

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