Files
TuanTuan-Engine/Assets/GameFramework/Runtime/Patch/PatchOperation.cs
2025-11-17 10:51:40 +08:00

396 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Cysharp.Threading.Tasks;
using System.Threading.Tasks;
using UnityEngine;
using YooAsset;
using static YooAsset.DownloaderOperation;
namespace Tuan.GameFramework
{
public class PatchOperationData
{
public string packageName;
public EPlayMode playMode;
public bool useBuildinFileSystem;
public DownloadError downloadError;
public DownloaderFinish downloadFinish;
public DownloadUpdate downloadUpdate;
public int downloadingMaxNum = 10;
public int failedTryAgain = 3;
}
public class PatchOperation
{
public PatchOperationData data;
public ResourcePackage package;
public ResourceDownloaderOperation downloader;
public string packageVersion;
public string PkgVersionKey;
public PatchOperation(PatchOperationData data)
{
this.data = data;
PkgVersionKey = $"{Application.productName}_{data.packageName}";
#if !UNITY_EDITOR
PkgVersionKey = $"Editor_{Application.productName}_{data.packageName}";
#endif
}
#region
public async UniTask<bool> InitializePackage()
{
package = YooAssets.TryGetPackage(data.packageName);
if (package == null)
package = YooAssets.CreatePackage(data.packageName);
InitializationOperation initializationOperation = null;
switch (data.playMode)
{
case EPlayMode.EditorSimulateMode:
initializationOperation = InitializeEditorMode(package);
break;
case EPlayMode.OfflinePlayMode:
initializationOperation = InitializeOfflineMode(package);
break;
case EPlayMode.HostPlayMode:
initializationOperation = InitializeHostMode(package);
break;
case EPlayMode.WebPlayMode:
initializationOperation = InitializeWebPlayMode(package);
break;
case EPlayMode.CustomPlayMode:
break;
default:
break;
}
await initializationOperation;
if (initializationOperation.Status != EOperationStatus.Succeed)
{
MessageBox.Show()
.SetTitle($"{data.packageName}初始化")
.SetContent($"{initializationOperation.Error}")
.AddButton("退出", (box) =>
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
});
}
else
{
PatchEvent.UpdateStatus($"初始化成功{data.packageName}");
Debug.Log($"初始化成功{data.packageName}");
}
return initializationOperation.Status == EOperationStatus.Succeed;
}
private InitializationOperation InitializeEditorMode(ResourcePackage package)
{
var buildResult = EditorSimulateModeHelper.SimulateBuild(data.packageName);
var packageRoot = buildResult.PackageRootDirectory;
var createParameters = new EditorSimulateModeParameters();
createParameters.EditorFileSystemParameters = FileSystemParameters.CreateDefaultEditorFileSystemParameters(packageRoot);
return package.InitializeAsync(createParameters);
}
private InitializationOperation InitializeOfflineMode(ResourcePackage package)
{
var createParameters = new OfflinePlayModeParameters();
createParameters.BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters();
return package.InitializeAsync(createParameters);
}
private InitializationOperation InitializeHostMode(ResourcePackage package)
{
FileSystemParameters buildinFileSystemParams = null;
if (data.useBuildinFileSystem)
{
// 注意设置参数COPY_BUILDIN_PACKAGE_MANIFEST可以初始化的时候拷贝内置清单到沙盒目录
buildinFileSystemParams = FileSystemParameters.CreateDefaultBuildinFileSystemParameters();
buildinFileSystemParams.AddParameter(FileSystemParametersDefine.COPY_BUILDIN_PACKAGE_MANIFEST, true);
}
string defaultHostServer = GetHostServerURL();
string fallbackHostServer = GetHostServerURL();
IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
// 注意设置参数INSTALL_CLEAR_MODE可以解决覆盖安装的时候将拷贝的内置清单文件清理的问题。
var cacheFileSystemParams = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices);
cacheFileSystemParams.AddParameter(FileSystemParametersDefine.INSTALL_CLEAR_MODE, EOverwriteInstallClearMode.ClearAllManifestFiles);
var createParameters = new HostPlayModeParameters();
createParameters.BuildinFileSystemParameters = buildinFileSystemParams;
createParameters.CacheFileSystemParameters = cacheFileSystemParams;
return package.InitializeAsync(createParameters);
}
private InitializationOperation InitializeWebPlayMode(ResourcePackage package)
{
#if UNITY_WEBGL && WEIXINMINIGAME && !UNITY_EDITOR
var createParameters = new WebPlayModeParameters();
string defaultHostServer = GetHostServerURL();
string fallbackHostServer = GetHostServerURL();
string packageRoot = $"{WeChatWASM.WX.env.USER_DATA_PATH}/__GAME_FILE_CACHE"; //注意:如果有子目录,请修改此处!
IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
createParameters.WebServerFileSystemParameters = WechatFileSystemCreater.CreateFileSystemParameters(packageRoot, remoteServices);
return package.InitializeAsync(createParameters);
#else
var createParameters = new WebPlayModeParameters();
createParameters.WebServerFileSystemParameters = FileSystemParameters.CreateDefaultWebServerFileSystemParameters();
return package.InitializeAsync(createParameters);
#endif
}
private string GetHostServerURL()
{
string hostServerIP = $"https://home.gtuantuan.online:9444/{Application.productName}";
string appVersion = "v1";
#if UNITY_EDITOR
if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.Android)
// return $"{hostServerIP}/CDN/Android/{packageName}/{appVersion}";
return $"{hostServerIP}/CDN/PC/{data.packageName}/{appVersion}";
else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.iOS)
return $"{hostServerIP}/CDN/IPhone/{data.packageName}/{appVersion}";
else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.WebGL)
return $"{hostServerIP}/CDN/WebGL/{data.packageName}/{appVersion}";
else
return $"{hostServerIP}/CDN/PC/{data.packageName}/{appVersion}";
#else
if (Application.platform == RuntimePlatform.Android)
return $"{hostServerIP}/CDN/Android/{data.packageName}/{appVersion}";
else if (Application.platform == RuntimePlatform.IPhonePlayer)
return $"{hostServerIP}/CDN/IPhone/{data.packageName}/{appVersion}";
else if (Application.platform == RuntimePlatform.WebGLPlayer)
return $"{hostServerIP}/CDN/WebGL/{data.packageName}/{appVersion}";
else
return $"{hostServerIP}/CDN/PC/{data.packageName}/{appVersion}";
#endif
}
private class RemoteServices : IRemoteServices
{
private readonly string _defaultHostServer;
private readonly string _fallbackHostServer;
public RemoteServices(string defaultHostServer, string fallbackHostServer)
{
_defaultHostServer = defaultHostServer;
_fallbackHostServer = fallbackHostServer;
}
string IRemoteServices.GetRemoteMainURL(string fileName)
{
return $"{_defaultHostServer}/{fileName}";
}
string IRemoteServices.GetRemoteFallbackURL(string fileName)
{
return $"{_fallbackHostServer}/{fileName}";
}
}
#endregion
#region
public async UniTask<bool> RequestPackageVersion(bool showBox = true)
{
var operation = package.RequestPackageVersionAsync(true, 5);
await operation;
if (operation.Status != EOperationStatus.Succeed)
{
if (showBox)
{
if (await CheckUseLocalVersion(operation))
{
string cachedVersion = GetCachedPackageVersion();
if (!string.IsNullOrEmpty(cachedVersion))
{
packageVersion = cachedVersion;
PatchEvent.UpdateStatus($"获取缓存版本成功{data.packageName}");
Debug.Log($"获取缓存版本成功{data.packageName}{packageVersion}");
return true;
}
}
}
}
else
{
packageVersion = operation.PackageVersion;
PatchEvent.UpdateStatus($"获取远端版本成功{data.packageName}");
Debug.Log($"获取远端版本成功{data.packageName}{packageVersion}");
}
return operation.Status == EOperationStatus.Succeed;
}
public async UniTask<bool> CheckUseLocalVersion(RequestPackageVersionOperation operation)
{
var completionSource = new UniTaskCompletionSource<bool>();
MessageBox.Show()
.SetTitle($"{data.packageName}请求版本")
.SetContent($"{operation.Error}")
.AddButton("继续", (box) =>
{
completionSource.TrySetResult(true);
})
.AddButton("退出", (box) =>
{
completionSource.TrySetResult(false);
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
});
bool shouldContinue = await completionSource.Task;
return shouldContinue;
}
public async Task<string> GetBuildinPackageVersion()
{
var operation = new GetBuildinPackageVersionOperation(data.packageName);
YooAssets.StartOperation(operation);
await operation;
if (operation.Status == EOperationStatus.Succeed)
{
return operation.PackageVersion;
}
else
{
return null;
}
}
public string GetCachedPackageVersion()
{
if (PlayerPrefs.HasKey(PkgVersionKey))
{
return PlayerPrefs.GetString(PkgVersionKey);
}
return null;
}
public void SaveVersionToCache()
{
PlayerPrefs.SetString(PkgVersionKey, packageVersion);
PlayerPrefs.Save();
PatchEvent.UpdateStatus($"更新完成{data.packageName}");
Debug.Log($"更新{data.packageName}完成,版本号{packageVersion}");
}
public async UniTask<bool> UpdatePackageManifest(bool showBox = true)
{
var operation = package.UpdatePackageManifestAsync(packageVersion, 10);
await operation;
if (operation.Status != EOperationStatus.Succeed)
{
if (showBox)
{
MessageBox.Show()
.SetTitle($"{data.packageName}更新清单")
.SetContent($"{operation.Error}")
.AddButton("退出", (box) =>
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
});
}
}
else
{
PatchEvent.UpdateStatus($"获取资源清单成功{data.packageName}");
Debug.Log($"获取资源清单成功{data.packageName}");
}
return operation.Status == EOperationStatus.Succeed;
}
#endregion
#region
public bool CreateDownloader()
{
downloader = package.CreateResourceDownloader(data.downloadingMaxNum, data.failedTryAgain);
downloader.DownloadErrorCallback = data.downloadError;
downloader.DownloadFinishCallback = data.downloadFinish;
downloader.DownloadUpdateCallback = data.downloadUpdate;
if (downloader.TotalDownloadCount == 0) return false;
return true;
}
public async UniTask<bool> CheckDownloadOrSkip()
{
var completionSource = new UniTaskCompletionSource<bool>();
MessageBox.Show()
.SetTitle($"{data.packageName}发现更新")
.SetContent($"发现资源更新\n{GetCachedPackageVersion()}=>{packageVersion}: {FormatFileSize(downloader.TotalDownloadBytes)}")
.AddButton("下载", async (box) =>
{
bool success = await DownloadPackageFiles();
completionSource.TrySetResult(success);
})
.AddButton("跳过", async (box) =>
{
downloader.CancelDownload();
packageVersion = GetCachedPackageVersion();
await UpdatePackageManifest();
completionSource.TrySetResult(true);
})
.AddButton("退出", (box) =>
{
downloader.CancelDownload();
completionSource.TrySetResult(false);
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
});
bool shouldContinue = await completionSource.Task;
return shouldContinue;
}
public async UniTask<bool> DownloadPackageFiles(bool showBox = true)
{
if (downloader.TotalDownloadCount == 0)
return true;
Debug.Log($"{data.packageName} DownloadPackageFiles {downloader.TotalDownloadCount}");
downloader.BeginDownload();
await downloader;
if (downloader.Status != EOperationStatus.Succeed)
{
if (showBox)
{
MessageBox.Show()
.SetTitle($"{data.packageName}下载文件")
.SetContent($"{downloader.Error}")
.AddButton("退出", (box) =>
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
});
}
return false;
}
return downloader.Status == EOperationStatus.Succeed;
}
public async UniTask<bool> ClearCacheBundle(bool showBox = true)
{
var operation = package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedBundleFiles);
await operation;
if (operation.Status != EOperationStatus.Succeed)
{
if (showBox)
{
MessageBox.Show()
.SetTitle($"{data.packageName}清除缓存")
.SetContent($"{operation.Error}")
.AddButton("退出", (box) =>
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
});
}
}
return operation.Status == EOperationStatus.Succeed;
}
public string FormatFileSize(long size)
{
if (size < 1024 * 1024)
return $"{(size / 1024f):F1}KB";
else
return $"{size / 1024f / 1024f:F1}MB";
}
#endregion
}
}