diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem.meta b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem.meta
new file mode 100644
index 00000000..0ec24d20
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 7d2280a83472bd14bb033f506647284d
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/AlipayFileSystem.cs b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/AlipayFileSystem.cs
new file mode 100644
index 00000000..bf49db77
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/AlipayFileSystem.cs
@@ -0,0 +1,280 @@
+#if UNITY_WEBGL && UNITY_ALIMINIGAME
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+using YooAsset;
+using AlipaySdk;
+
+public static class AlipayFileSystemCreater
+{
+ public static FileSystemParameters CreateFileSystemParameters(string packageRoot, IRemoteServices remoteServices)
+ {
+ string fileSystemClass = $"{nameof(AlipayFileSystem)},YooAsset.MiniGame";
+ var fileSystemParams = new FileSystemParameters(fileSystemClass, packageRoot);
+ fileSystemParams.AddParameter(FileSystemParametersDefine.REMOTE_SERVICES, remoteServices);
+ return fileSystemParams;
+ }
+
+ public static FileSystemParameters CreateFileSystemParameters(string packageRoot, IRemoteServices remoteServices, IWebDecryptionServices decryptionServices)
+ {
+ string fileSystemClass = $"{nameof(AlipayFileSystem)},YooAsset.MiniGame";
+ var fileSystemParams = new FileSystemParameters(fileSystemClass, packageRoot);
+ fileSystemParams.AddParameter(FileSystemParametersDefine.REMOTE_SERVICES, remoteServices);
+ fileSystemParams.AddParameter(FileSystemParametersDefine.DECRYPTION_SERVICES, decryptionServices);
+ return fileSystemParams;
+ }
+}
+
+///
+/// 支付宝小游戏文件系统
+/// 参考:https://opendocs.alipay.com/mini-game/0ftleg
+///
+internal class AlipayFileSystem : IFileSystem
+{
+ private class WebRemoteServices : IRemoteServices
+ {
+ private readonly string _webPackageRoot;
+ protected readonly Dictionary _mapping = new Dictionary(10000);
+
+ public WebRemoteServices(string buildinPackRoot)
+ {
+ _webPackageRoot = buildinPackRoot;
+ }
+ string IRemoteServices.GetRemoteMainURL(string fileName)
+ {
+ return GetFileLoadURL(fileName);
+ }
+ string IRemoteServices.GetRemoteFallbackURL(string fileName)
+ {
+ return GetFileLoadURL(fileName);
+ }
+
+ private string GetFileLoadURL(string fileName)
+ {
+ if (_mapping.TryGetValue(fileName, out string url) == false)
+ {
+ string filePath = PathUtility.Combine(_webPackageRoot, fileName);
+ url = DownloadSystemHelper.ConvertToWWWPath(filePath);
+ _mapping.Add(fileName, url);
+ }
+ return url;
+ }
+ }
+
+ private readonly Dictionary _cacheFilePathMapping = new Dictionary(10000);
+ private AlipayFSManager _fileSystemMgr;
+ private string _aliCacheRoot = string.Empty;
+
+ ///
+ /// 包裹名称
+ ///
+ public string PackageName { private set; get; }
+
+ ///
+ /// 文件根目录
+ ///
+ public string FileRoot
+ {
+ get
+ {
+ return _aliCacheRoot;
+ }
+ }
+
+ ///
+ /// 文件数量
+ ///
+ public int FileCount
+ {
+ get
+ {
+ return 0;
+ }
+ }
+
+ #region 自定义参数
+ ///
+ /// 自定义参数:远程服务接口
+ ///
+ public IRemoteServices RemoteServices { private set; get; } = null;
+
+ ///
+ /// 自定义参数:解密方法类
+ ///
+ public IWebDecryptionServices DecryptionServices { private set; get; }
+
+ ///
+ /// 自定义参数:资源清单服务类
+ ///
+ public IManifestRestoreServices ManifestServices { private set; get; }
+ #endregion
+
+
+ public AlipayFileSystem()
+ {
+ }
+ public virtual FSInitializeFileSystemOperation InitializeFileSystemAsync()
+ {
+ var operation = new APFSInitializeOperation(this);
+ return operation;
+ }
+ public virtual FSLoadPackageManifestOperation LoadPackageManifestAsync(string packageVersion, int timeout)
+ {
+ var operation = new APFSLoadPackageManifestOperation(this, packageVersion, timeout);
+ return operation;
+ }
+ public virtual FSRequestPackageVersionOperation RequestPackageVersionAsync(bool appendTimeTicks, int timeout)
+ {
+ var operation = new APFSRequestPackageVersionOperation(this, appendTimeTicks, timeout);
+ return operation;
+ }
+ public virtual FSClearCacheFilesOperation ClearCacheFilesAsync(PackageManifest manifest, ClearCacheFilesOptions options)
+ {
+ var operation = new FSClearCacheFilesCompleteOperation();
+ return operation;
+ }
+ public virtual FSDownloadFileOperation DownloadFileAsync(PackageBundle bundle, DownloadFileOptions options)
+ {
+ string mainURL = RemoteServices.GetRemoteMainURL(bundle.FileName);
+ string fallbackURL = RemoteServices.GetRemoteFallbackURL(bundle.FileName);
+ options.SetURL(mainURL, fallbackURL);
+ var operation = new APFSDownloadFileOperation(this, bundle, options);
+ return operation;
+ }
+ public virtual FSLoadBundleOperation LoadBundleFile(PackageBundle bundle)
+ {
+ if (bundle.BundleType == (int)EBuildBundleType.AssetBundle)
+ {
+ var operation = new APFSLoadBundleOperation(this, bundle);
+ return operation;
+ }
+ else
+ {
+ string error = $"{nameof(AlipayFileSystem)} not support load bundle type : {bundle.BundleType}";
+ var operation = new FSLoadBundleCompleteOperation(error);
+ return operation;
+ }
+ }
+
+ public virtual void SetParameter(string name, object value)
+ {
+ if (name == FileSystemParametersDefine.REMOTE_SERVICES)
+ {
+ RemoteServices = (IRemoteServices)value;
+ }
+ else if (name == FileSystemParametersDefine.DECRYPTION_SERVICES)
+ {
+ DecryptionServices = (IWebDecryptionServices)value;
+ }
+ else if (name == FileSystemParametersDefine.MANIFEST_SERVICES)
+ {
+ ManifestServices = (IManifestRestoreServices)value;
+ }
+ else
+ {
+ YooLogger.Warning($"Invalid parameter : {name}");
+ }
+ }
+ public virtual void OnCreate(string packageName, string rootDirectory)
+ {
+ PackageName = packageName;
+ _aliCacheRoot = rootDirectory;
+
+ if (string.IsNullOrEmpty(_aliCacheRoot))
+ {
+ throw new System.Exception("请配置支付宝小游戏的缓存根目录!");
+ }
+
+ // 注意:CDN服务未启用的情况下,使用支付宝WEB服务器
+ if (RemoteServices == null)
+ {
+ string webRoot = PathUtility.Combine(Application.streamingAssetsPath, YooAssetSettingsData.Setting.DefaultYooFolderName, packageName);
+ RemoteServices = new WebRemoteServices(webRoot);
+ }
+
+ _fileSystemMgr = AlipaySDK.API.GetFileSystemManager();
+ }
+ public virtual void OnDestroy()
+ {
+ }
+
+ public virtual bool Belong(PackageBundle bundle)
+ {
+ return true;
+ }
+ public virtual bool Exists(PackageBundle bundle)
+ {
+ return CheckCacheFileExist(bundle);
+ }
+ public virtual bool NeedDownload(PackageBundle bundle)
+ {
+ if (Belong(bundle) == false)
+ return false;
+
+ return Exists(bundle) == false;
+ }
+ public virtual bool NeedUnpack(PackageBundle bundle)
+ {
+ return false;
+ }
+ public virtual bool NeedImport(PackageBundle bundle)
+ {
+ return false;
+ }
+
+ public virtual string GetBundleFilePath(PackageBundle bundle)
+ {
+ return GetCacheFileLoadPath(bundle);
+ }
+ public virtual byte[] ReadBundleFileData(PackageBundle bundle)
+ {
+ if (CheckCacheFileExist(bundle))
+ {
+ string filePath = GetCacheFileLoadPath(bundle);
+ return _fileSystemMgr.ReadFileSync(filePath);
+ }
+ else
+ {
+ return Array.Empty();
+ }
+ }
+ public virtual string ReadBundleFileText(PackageBundle bundle)
+ {
+ if (CheckCacheFileExist(bundle))
+ {
+ string filePath = GetCacheFileLoadPath(bundle);
+ return _fileSystemMgr.ReadFileSync(filePath, "utf8");
+ }
+ else
+ {
+ return string.Empty;
+ }
+ }
+
+ #region 内部方法
+ public AlipayFSManager GetFileSystemMgr()
+ {
+ return _fileSystemMgr;
+ }
+ public bool CheckCacheFileExist(PackageBundle bundle)
+ {
+ //TODO : 效率极低
+ /*
+ string filePath = GetCacheFileLoadPath(bundle);
+ string result = _fileSystemMgr.AccessSync(filePath);
+ return result.Equals("access:ok", StringComparison.Ordinal);
+ */
+ return false;
+ }
+ private string GetCacheFileLoadPath(PackageBundle bundle)
+ {
+ if (_cacheFilePathMapping.TryGetValue(bundle.BundleGUID, out string filePath) == false)
+ {
+ filePath = PathUtility.Combine(_aliCacheRoot, bundle.FileName);
+ _cacheFilePathMapping.Add(bundle.BundleGUID, filePath);
+ }
+ return filePath;
+ }
+ #endregion
+}
+#endif
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/AlipayFileSystem.cs.meta b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/AlipayFileSystem.cs.meta
new file mode 100644
index 00000000..ecb2e1e7
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/AlipayFileSystem.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 61be9965c7f7aae468a34b3ed38116b5
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/BundleResult.meta b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/BundleResult.meta
new file mode 100644
index 00000000..9359b1d5
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/BundleResult.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 44b9746ed3270394eb54d85b27a2ef04
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/BundleResult/APAssetBundleResult.cs b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/BundleResult/APAssetBundleResult.cs
new file mode 100644
index 00000000..6a53aebe
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/BundleResult/APAssetBundleResult.cs
@@ -0,0 +1,67 @@
+#if UNITY_WEBGL && UNITY_ALIMINIGAME
+using System.IO;
+using UnityEngine;
+using UnityEngine.SceneManagement;
+using AlipaySdk;
+
+namespace YooAsset
+{
+ internal class APAssetBundleResult : BundleResult
+ {
+ private readonly IFileSystem _fileSystem;
+ private readonly PackageBundle _packageBundle;
+ private readonly AssetBundle _assetBundle;
+
+ public APAssetBundleResult(IFileSystem fileSystem, PackageBundle packageBundle, AssetBundle assetBundle)
+ {
+ _fileSystem = fileSystem;
+ _packageBundle = packageBundle;
+ _assetBundle = assetBundle;
+ }
+
+ public override void UnloadBundleFile()
+ {
+ if (_assetBundle != null)
+ {
+ if (_packageBundle.Encrypted)
+ _assetBundle.Unload(true);
+ else
+ _assetBundle.APUnload(true);
+ }
+ }
+ public override string GetBundleFilePath()
+ {
+ return _fileSystem.GetBundleFilePath(_packageBundle);
+ }
+ public override byte[] ReadBundleFileData()
+ {
+ return _fileSystem.ReadBundleFileData(_packageBundle);
+ }
+ public override string ReadBundleFileText()
+ {
+ return _fileSystem.ReadBundleFileText(_packageBundle);
+ }
+
+ public override FSLoadAssetOperation LoadAssetAsync(AssetInfo assetInfo)
+ {
+ var operation = new AssetBundleLoadAssetOperation(_packageBundle, _assetBundle, assetInfo);
+ return operation;
+ }
+ public override FSLoadAllAssetsOperation LoadAllAssetsAsync(AssetInfo assetInfo)
+ {
+ var operation = new AssetBundleLoadAllAssetsOperation(_packageBundle, _assetBundle, assetInfo);
+ return operation;
+ }
+ public override FSLoadSubAssetsOperation LoadSubAssetsAsync(AssetInfo assetInfo)
+ {
+ var operation = new AssetBundleLoadSubAssetsOperation(_packageBundle, _assetBundle, assetInfo);
+ return operation;
+ }
+ public override FSLoadSceneOperation LoadSceneOperation(AssetInfo assetInfo, LoadSceneParameters loadParams, bool suspendLoad)
+ {
+ var operation = new AssetBundleLoadSceneOperation(assetInfo, loadParams, suspendLoad);
+ return operation;
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/BundleResult/APAssetBundleResult.cs.meta b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/BundleResult/APAssetBundleResult.cs.meta
new file mode 100644
index 00000000..3cdbc4d1
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/BundleResult/APAssetBundleResult.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1de437726bd89584da56aaddfdb1a07c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation.meta b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation.meta
new file mode 100644
index 00000000..46ba23ce
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 18ae5147de9585045ab40941d945696b
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSDownloadFileOperation.cs b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSDownloadFileOperation.cs
new file mode 100644
index 00000000..3e49047b
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSDownloadFileOperation.cs
@@ -0,0 +1,111 @@
+#if UNITY_WEBGL && UNITY_ALIMINIGAME
+using UnityEngine;
+using YooAsset;
+
+internal class APFSDownloadFileOperation : FSDownloadFileOperation
+{
+ protected enum ESteps
+ {
+ None,
+ CreateRequest,
+ CheckRequest,
+ TryAgain,
+ Done,
+ }
+
+ private readonly AlipayFileSystem _fileSystem;
+ private readonly DownloadFileOptions _options;
+ private UnityWebCacheRequestOperation _webCacheRequestOp;
+ private int _requestCount = 0;
+ private float _tryAgainTimer;
+ private int _failedTryAgain;
+ private ESteps _steps = ESteps.None;
+
+ internal APFSDownloadFileOperation(AlipayFileSystem fileSystem, PackageBundle bundle, DownloadFileOptions options) : base(bundle)
+ {
+ _fileSystem = fileSystem;
+ _options = options;
+ }
+ internal override void InternalStart()
+ {
+ _steps = ESteps.CreateRequest;
+ }
+ internal override void InternalUpdate()
+ {
+ // 创建下载器
+ if (_steps == ESteps.CreateRequest)
+ {
+ string url = GetRequestURL();
+ _webCacheRequestOp = new UnityWebCacheRequestOperation(url);
+ _webCacheRequestOp.StartOperation();
+ AddChildOperation(_webCacheRequestOp);
+ _steps = ESteps.CheckRequest;
+ }
+
+ // 检测下载结果
+ if (_steps == ESteps.CheckRequest)
+ {
+ _webCacheRequestOp.UpdateOperation();
+ Progress = _webCacheRequestOp.Progress;
+ DownloadProgress = _webCacheRequestOp.DownloadProgress;
+ DownloadedBytes = _webCacheRequestOp.DownloadedBytes;
+ if (_webCacheRequestOp.IsDone == false)
+ return;
+
+ if (_webCacheRequestOp.Status == EOperationStatus.Succeed)
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Succeed;
+
+ //TODO 需要验证插件请求器的下载进度
+ DownloadProgress = 1f;
+ DownloadedBytes = Bundle.FileSize;
+ Progress = 1f;
+ }
+ else
+ {
+ if (_failedTryAgain > 0)
+ {
+ _steps = ESteps.TryAgain;
+ YooLogger.Warning($"Failed download : {_webCacheRequestOp.URL} Try again !");
+ }
+ else
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Failed;
+ Error = _webCacheRequestOp.Error;
+ YooLogger.Error(Error);
+ }
+ }
+ }
+
+ // 重新尝试下载
+ if (_steps == ESteps.TryAgain)
+ {
+ _tryAgainTimer += Time.unscaledDeltaTime;
+ if (_tryAgainTimer > 1f)
+ {
+ _tryAgainTimer = 0f;
+ _failedTryAgain--;
+ Progress = 0f;
+ DownloadProgress = 0f;
+ DownloadedBytes = 0;
+ _steps = ESteps.CreateRequest;
+ }
+ }
+ }
+
+ ///
+ /// 获取网络请求地址
+ ///
+ private string GetRequestURL()
+ {
+ // 轮流返回请求地址
+ _requestCount++;
+ if (_requestCount % 2 == 0)
+ return _options.FallbackURL;
+ else
+ return _options.MainURL;
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSDownloadFileOperation.cs.meta b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSDownloadFileOperation.cs.meta
new file mode 100644
index 00000000..93a790f6
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSDownloadFileOperation.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: bc47317a416e9f542b7ec6b77485a8b7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSInitializeOperation.cs b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSInitializeOperation.cs
new file mode 100644
index 00000000..181b87ca
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSInitializeOperation.cs
@@ -0,0 +1,20 @@
+#if UNITY_WEBGL && UNITY_ALIMINIGAME
+using YooAsset;
+
+internal partial class APFSInitializeOperation : FSInitializeFileSystemOperation
+{
+ private readonly AlipayFileSystem _fileSystem;
+
+ public APFSInitializeOperation(AlipayFileSystem fileSystem)
+ {
+ _fileSystem = fileSystem;
+ }
+ internal override void InternalStart()
+ {
+ Status = EOperationStatus.Succeed;
+ }
+ internal override void InternalUpdate()
+ {
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSInitializeOperation.cs.meta b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSInitializeOperation.cs.meta
new file mode 100644
index 00000000..86f090da
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSInitializeOperation.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 71a99a94d7563fe4a9d56ea0e15aa8d1
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSLoadBundleOperation.cs b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSLoadBundleOperation.cs
new file mode 100644
index 00000000..ca289564
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSLoadBundleOperation.cs
@@ -0,0 +1,88 @@
+#if UNITY_WEBGL && UNITY_ALIMINIGAME
+using YooAsset;
+
+internal class APFSLoadBundleOperation : FSLoadBundleOperation
+{
+ private enum ESteps
+ {
+ None,
+ LoadAssetBundle,
+ Done,
+ }
+
+ private readonly AlipayFileSystem _fileSystem;
+ private readonly PackageBundle _bundle;
+ private LoadWebAssetBundleOperation _loadWebAssetBundleOp;
+ private ESteps _steps = ESteps.None;
+
+ internal APFSLoadBundleOperation(AlipayFileSystem fileSystem, PackageBundle bundle)
+ {
+ _fileSystem = fileSystem;
+ _bundle = bundle;
+ }
+ internal override void InternalStart()
+ {
+ _steps = ESteps.LoadAssetBundle;
+ }
+ internal override void InternalUpdate()
+ {
+ if (_steps == ESteps.None || _steps == ESteps.Done)
+ return;
+
+ if (_steps == ESteps.LoadAssetBundle)
+ {
+ if (_loadWebAssetBundleOp == null)
+ {
+ string mainURL = _fileSystem.RemoteServices.GetRemoteMainURL(_bundle.FileName);
+ string fallbackURL = _fileSystem.RemoteServices.GetRemoteFallbackURL(_bundle.FileName);
+ DownloadFileOptions options = new DownloadFileOptions(int.MaxValue);
+ options.SetURL(mainURL, fallbackURL);
+
+ if (_bundle.Encrypted)
+ {
+ _loadWebAssetBundleOp = new LoadWebEncryptAssetBundleOperation(_bundle, options, _fileSystem.DecryptionServices);
+ _loadWebAssetBundleOp.StartOperation();
+ AddChildOperation(_loadWebAssetBundleOp);
+ }
+ else
+ {
+ _loadWebAssetBundleOp = new LoadAlipayAssetBundleOperation(_bundle, options);
+ _loadWebAssetBundleOp.StartOperation();
+ AddChildOperation(_loadWebAssetBundleOp);
+ }
+ }
+
+ _loadWebAssetBundleOp.UpdateOperation();
+ Progress = _loadWebAssetBundleOp.Progress;
+ DownloadProgress = _loadWebAssetBundleOp.DownloadProgress;
+ DownloadedBytes = _loadWebAssetBundleOp.DownloadedBytes;
+ if (_loadWebAssetBundleOp.IsDone == false)
+ return;
+
+ if (_loadWebAssetBundleOp.Status == EOperationStatus.Succeed)
+ {
+ var assetBundle = _loadWebAssetBundleOp.Result;
+ _steps = ESteps.Done;
+ Result = new APAssetBundleResult(_fileSystem, _bundle, assetBundle);
+ Status = EOperationStatus.Succeed;
+ }
+ else
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Failed;
+ Error = _loadWebAssetBundleOp.Error;
+ }
+ }
+ }
+ internal override void InternalWaitForAsyncComplete()
+ {
+ if (_steps != ESteps.Done)
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Failed;
+ Error = "WebGL platform not support sync load method !";
+ UnityEngine.Debug.LogError(Error);
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSLoadBundleOperation.cs.meta b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSLoadBundleOperation.cs.meta
new file mode 100644
index 00000000..5b3fd077
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSLoadBundleOperation.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f71fc4fc468d41441b00be64938b2fe9
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSLoadPackageManifestOperation.cs b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSLoadPackageManifestOperation.cs
new file mode 100644
index 00000000..4b0b3783
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSLoadPackageManifestOperation.cs
@@ -0,0 +1,92 @@
+#if UNITY_WEBGL && UNITY_ALIMINIGAME
+using YooAsset;
+
+internal class APFSLoadPackageManifestOperation : FSLoadPackageManifestOperation
+{
+ private enum ESteps
+ {
+ None,
+ RequestPackageHash,
+ LoadPackageManifest,
+ Done,
+ }
+
+ private readonly AlipayFileSystem _fileSystem;
+ private readonly string _packageVersion;
+ private readonly int _timeout;
+ private RequestAlipayPackageHashOperation _requestPackageHashOp;
+ private LoadAlipayPackageManifestOperation _loadPackageManifestOp;
+ private ESteps _steps = ESteps.None;
+
+
+ public APFSLoadPackageManifestOperation(AlipayFileSystem fileSystem, string packageVersion, int timeout)
+ {
+ _fileSystem = fileSystem;
+ _packageVersion = packageVersion;
+ _timeout = timeout;
+ }
+ internal override void InternalStart()
+ {
+ _steps = ESteps.RequestPackageHash;
+ }
+ internal override void InternalUpdate()
+ {
+ if (_steps == ESteps.None || _steps == ESteps.Done)
+ return;
+
+ if (_steps == ESteps.RequestPackageHash)
+ {
+ if (_requestPackageHashOp == null)
+ {
+ _requestPackageHashOp = new RequestAlipayPackageHashOperation(_fileSystem, _packageVersion, _timeout);
+ _requestPackageHashOp.StartOperation();
+ AddChildOperation(_requestPackageHashOp);
+ }
+
+ _requestPackageHashOp.UpdateOperation();
+ if (_requestPackageHashOp.IsDone == false)
+ return;
+
+ if (_requestPackageHashOp.Status == EOperationStatus.Succeed)
+ {
+ _steps = ESteps.LoadPackageManifest;
+ }
+ else
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Failed;
+ Error = _requestPackageHashOp.Error;
+ }
+ }
+
+ if (_steps == ESteps.LoadPackageManifest)
+ {
+ if (_loadPackageManifestOp == null)
+ {
+ string packageHash = _requestPackageHashOp.PackageHash;
+ _loadPackageManifestOp = new LoadAlipayPackageManifestOperation(_fileSystem, _packageVersion, packageHash, _timeout);
+ _loadPackageManifestOp.StartOperation();
+ AddChildOperation(_loadPackageManifestOp);
+ }
+
+ _loadPackageManifestOp.UpdateOperation();
+ Progress = _loadPackageManifestOp.Progress;
+ if (_loadPackageManifestOp.IsDone == false)
+ return;
+
+ if (_loadPackageManifestOp.Status == EOperationStatus.Succeed)
+ {
+ _steps = ESteps.Done;
+ Manifest = _loadPackageManifestOp.Manifest;
+ Status = EOperationStatus.Succeed;
+ }
+ else
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Failed;
+ Error = _loadPackageManifestOp.Error;
+ }
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSLoadPackageManifestOperation.cs.meta b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSLoadPackageManifestOperation.cs.meta
new file mode 100644
index 00000000..77ab0ab4
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSLoadPackageManifestOperation.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 335885fb4e1977a4ca5c26263eda03d7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSRequestPackageVersionOperation.cs b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSRequestPackageVersionOperation.cs
new file mode 100644
index 00000000..ba5b117f
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSRequestPackageVersionOperation.cs
@@ -0,0 +1,64 @@
+#if UNITY_WEBGL && UNITY_ALIMINIGAME
+using YooAsset;
+
+internal class APFSRequestPackageVersionOperation : FSRequestPackageVersionOperation
+{
+ private enum ESteps
+ {
+ None,
+ RequestPackageVersion,
+ Done,
+ }
+
+ private readonly AlipayFileSystem _fileSystem;
+ private readonly bool _appendTimeTicks;
+ private readonly int _timeout;
+ private RequestAlipayPackageVersionOperation _requestWebPackageVersionOp;
+ private ESteps _steps = ESteps.None;
+
+
+ internal APFSRequestPackageVersionOperation(AlipayFileSystem fileSystem, bool appendTimeTicks, int timeout)
+ {
+ _fileSystem = fileSystem;
+ _appendTimeTicks = appendTimeTicks;
+ _timeout = timeout;
+ }
+ internal override void InternalStart()
+ {
+ _steps = ESteps.RequestPackageVersion;
+ }
+ internal override void InternalUpdate()
+ {
+ if (_steps == ESteps.None || _steps == ESteps.Done)
+ return;
+
+ if (_steps == ESteps.RequestPackageVersion)
+ {
+ if (_requestWebPackageVersionOp == null)
+ {
+ _requestWebPackageVersionOp = new RequestAlipayPackageVersionOperation(_fileSystem, _appendTimeTicks, _timeout);
+ _requestWebPackageVersionOp.StartOperation();
+ AddChildOperation(_requestWebPackageVersionOp);
+ }
+
+ _requestWebPackageVersionOp.UpdateOperation();
+ Progress = _requestWebPackageVersionOp.Progress;
+ if (_requestWebPackageVersionOp.IsDone == false)
+ return;
+
+ if (_requestWebPackageVersionOp.Status == EOperationStatus.Succeed)
+ {
+ _steps = ESteps.Done;
+ PackageVersion = _requestWebPackageVersionOp.PackageVersion;
+ Status = EOperationStatus.Succeed;
+ }
+ else
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Failed;
+ Error = _requestWebPackageVersionOp.Error;
+ }
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSRequestPackageVersionOperation.cs.meta b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSRequestPackageVersionOperation.cs.meta
new file mode 100644
index 00000000..cc074ca7
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/APFSRequestPackageVersionOperation.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a879fd6a506b37345b4b9ea6c5de20bc
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal.meta b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal.meta
new file mode 100644
index 00000000..5f9812d9
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b94d7bcfa19151046a1f0ed8e089a474
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/LoadAlipayAssetBundleOperation.cs b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/LoadAlipayAssetBundleOperation.cs
new file mode 100644
index 00000000..8174fdf9
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/LoadAlipayAssetBundleOperation.cs
@@ -0,0 +1,115 @@
+#if UNITY_WEBGL && UNITY_ALIMINIGAME
+using UnityEngine;
+using AlipaySdk;
+
+namespace YooAsset
+{
+ internal class LoadAlipayAssetBundleOperation : LoadWebAssetBundleOperation
+ {
+ protected enum ESteps
+ {
+ None,
+ CreateRequest,
+ CheckRequest,
+ TryAgain,
+ Done,
+ }
+
+ private readonly PackageBundle _bundle;
+ private readonly DownloadFileOptions _options;
+ private UnityAlipayAssetBundleRequestOperation _unityAssetBundleRequestOp;
+
+ private int _requestCount = 0;
+ private float _tryAgainTimer;
+ private int _failedTryAgain;
+ private ESteps _steps = ESteps.None;
+
+
+ internal LoadAlipayAssetBundleOperation(PackageBundle bundle, DownloadFileOptions options)
+ {
+ _bundle = bundle;
+ _options = options;
+ }
+ internal override void InternalStart()
+ {
+ _steps = ESteps.CreateRequest;
+ }
+ internal override void InternalUpdate()
+ {
+ if (_steps == ESteps.None || _steps == ESteps.Done)
+ return;
+
+ // 创建下载器
+ if (_steps == ESteps.CreateRequest)
+ {
+ string url = GetRequestURL();
+ _unityAssetBundleRequestOp = new UnityAlipayAssetBundleRequestOperation(_bundle, url);
+ _unityAssetBundleRequestOp.StartOperation();
+ AddChildOperation(_unityAssetBundleRequestOp);
+ _steps = ESteps.CheckRequest;
+ }
+
+ // 检测下载结果
+ if (_steps == ESteps.CheckRequest)
+ {
+ _unityAssetBundleRequestOp.UpdateOperation();
+ Progress = _unityAssetBundleRequestOp.Progress;
+ DownloadProgress = _unityAssetBundleRequestOp.DownloadProgress;
+ DownloadedBytes = _unityAssetBundleRequestOp.DownloadedBytes;
+ if (_unityAssetBundleRequestOp.IsDone == false)
+ return;
+
+ if (_unityAssetBundleRequestOp.Status == EOperationStatus.Succeed)
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Succeed;
+ Result = _unityAssetBundleRequestOp.Result;
+ }
+ else
+ {
+ if (_failedTryAgain > 0)
+ {
+ _steps = ESteps.TryAgain;
+ YooLogger.Warning($"Failed download : {_unityAssetBundleRequestOp.URL} Try again !");
+ }
+ else
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Failed;
+ Error = _unityAssetBundleRequestOp.Error;
+ YooLogger.Error(Error);
+ }
+ }
+ }
+
+ // 重新尝试下载
+ if (_steps == ESteps.TryAgain)
+ {
+ _tryAgainTimer += Time.unscaledDeltaTime;
+ if (_tryAgainTimer > 1f)
+ {
+ _tryAgainTimer = 0f;
+ _failedTryAgain--;
+ Progress = 0f;
+ DownloadProgress = 0f;
+ DownloadedBytes = 0;
+ _steps = ESteps.CreateRequest;
+ }
+ }
+ }
+
+ ///
+ /// 获取网络请求地址
+ ///
+ private string GetRequestURL()
+ {
+ // 轮流返回请求地址
+ _requestCount++;
+ if (_requestCount % 2 == 0)
+ return _options.FallbackURL;
+ else
+ return _options.MainURL;
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/LoadAlipayAssetBundleOperation.cs.meta b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/LoadAlipayAssetBundleOperation.cs.meta
new file mode 100644
index 00000000..041efb4a
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/LoadAlipayAssetBundleOperation.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 62249d32580e2ef48a33afb58515312a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/LoadAlipayPackageManifestOperation.cs b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/LoadAlipayPackageManifestOperation.cs
new file mode 100644
index 00000000..de343777
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/LoadAlipayPackageManifestOperation.cs
@@ -0,0 +1,129 @@
+#if UNITY_WEBGL && UNITY_ALIMINIGAME
+using YooAsset;
+
+internal class LoadAlipayPackageManifestOperation : AsyncOperationBase
+{
+ private enum ESteps
+ {
+ None,
+ RequestFileData,
+ VerifyFileData,
+ LoadManifest,
+ Done,
+ }
+
+ private readonly AlipayFileSystem _fileSystem;
+ private readonly string _packageVersion;
+ private readonly string _packageHash;
+ private readonly int _timeout;
+ private UnityWebDataRequestOperation _webDataRequestOp;
+ private DeserializeManifestOperation _deserializer;
+ private int _requestCount = 0;
+ private ESteps _steps = ESteps.None;
+
+ ///
+ /// 包裹清单
+ ///
+ public PackageManifest Manifest { private set; get; }
+
+
+ internal LoadAlipayPackageManifestOperation(AlipayFileSystem fileSystem, string packageVersion, string packageHash, int timeout)
+ {
+ _fileSystem = fileSystem;
+ _packageVersion = packageVersion;
+ _packageHash = packageHash;
+ _timeout = timeout;
+ }
+ internal override void InternalStart()
+ {
+ _requestCount = WebRequestCounter.GetRequestFailedCount(_fileSystem.PackageName, nameof(LoadAlipayPackageManifestOperation));
+ _steps = ESteps.RequestFileData;
+ }
+ internal override void InternalUpdate()
+ {
+ if (_steps == ESteps.None || _steps == ESteps.Done)
+ return;
+
+ if (_steps == ESteps.RequestFileData)
+ {
+ if (_webDataRequestOp == null)
+ {
+ string fileName = YooAssetSettingsData.GetManifestBinaryFileName(_fileSystem.PackageName, _packageVersion);
+ string url = GetRequestURL(fileName);
+ _webDataRequestOp = new UnityWebDataRequestOperation(url, _timeout);
+ _webDataRequestOp.StartOperation();
+ AddChildOperation(_webDataRequestOp);
+ }
+
+ _webDataRequestOp.UpdateOperation();
+ Progress = _webDataRequestOp.Progress;
+ if (_webDataRequestOp.IsDone == false)
+ return;
+
+ if (_webDataRequestOp.Status == EOperationStatus.Succeed)
+ {
+ _steps = ESteps.VerifyFileData;
+ }
+ else
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Failed;
+ Error = _webDataRequestOp.Error;
+ WebRequestCounter.RecordRequestFailed(_fileSystem.PackageName, nameof(LoadAlipayPackageManifestOperation));
+ }
+ }
+
+ if (_steps == ESteps.VerifyFileData)
+ {
+ string fileHash = HashUtility.BytesCRC32(_webDataRequestOp.Result);
+ if (fileHash == _packageHash)
+ {
+ _steps = ESteps.LoadManifest;
+ }
+ else
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Failed;
+ Error = "Failed to verify package manifest file!";
+ }
+ }
+
+ if (_steps == ESteps.LoadManifest)
+ {
+ if (_deserializer == null)
+ {
+ _deserializer = new DeserializeManifestOperation(_fileSystem.ManifestServices, _webDataRequestOp.Result);
+ _deserializer.StartOperation();
+ AddChildOperation(_deserializer);
+ }
+
+ _deserializer.UpdateOperation();
+ Progress = _deserializer.Progress;
+ if (_deserializer.IsDone == false)
+ return;
+
+ if (_deserializer.Status == EOperationStatus.Succeed)
+ {
+ _steps = ESteps.Done;
+ Manifest = _deserializer.Manifest;
+ Status = EOperationStatus.Succeed;
+ }
+ else
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Failed;
+ Error = _deserializer.Error;
+ }
+ }
+ }
+
+ private string GetRequestURL(string fileName)
+ {
+ // 轮流返回请求地址
+ if (_requestCount % 2 == 0)
+ return _fileSystem.RemoteServices.GetRemoteMainURL(fileName);
+ else
+ return _fileSystem.RemoteServices.GetRemoteFallbackURL(fileName);
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/LoadAlipayPackageManifestOperation.cs.meta b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/LoadAlipayPackageManifestOperation.cs.meta
new file mode 100644
index 00000000..77d0ee62
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/LoadAlipayPackageManifestOperation.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c802e6ad51754e144bb4e08942da9592
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/RequestAlipayPackageHashOperation.cs b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/RequestAlipayPackageHashOperation.cs
new file mode 100644
index 00000000..617e627a
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/RequestAlipayPackageHashOperation.cs
@@ -0,0 +1,92 @@
+#if UNITY_WEBGL && UNITY_ALIMINIGAME
+using YooAsset;
+
+internal class RequestAlipayPackageHashOperation : AsyncOperationBase
+{
+ private enum ESteps
+ {
+ None,
+ RequestPackageHash,
+ Done,
+ }
+
+ private readonly AlipayFileSystem _fileSystem;
+ private readonly string _packageVersion;
+ private readonly int _timeout;
+ private UnityWebTextRequestOperation _webTextRequestOp;
+ private int _requestCount = 0;
+ private ESteps _steps = ESteps.None;
+
+ ///
+ /// 包裹哈希值
+ ///
+ public string PackageHash { private set; get; }
+
+
+ public RequestAlipayPackageHashOperation(AlipayFileSystem fileSystem, string packageVersion, int timeout)
+ {
+ _fileSystem = fileSystem;
+ _packageVersion = packageVersion;
+ _timeout = timeout;
+ }
+ internal override void InternalStart()
+ {
+ _requestCount = WebRequestCounter.GetRequestFailedCount(_fileSystem.PackageName, nameof(RequestAlipayPackageHashOperation));
+ _steps = ESteps.RequestPackageHash;
+ }
+ internal override void InternalUpdate()
+ {
+ if (_steps == ESteps.None || _steps == ESteps.Done)
+ return;
+
+ if (_steps == ESteps.RequestPackageHash)
+ {
+ if (_webTextRequestOp == null)
+ {
+ string fileName = YooAssetSettingsData.GetPackageHashFileName(_fileSystem.PackageName, _packageVersion);
+ string url = GetRequestURL(fileName);
+ _webTextRequestOp = new UnityWebTextRequestOperation(url, _timeout);
+ _webTextRequestOp.StartOperation();
+ AddChildOperation(_webTextRequestOp);
+ }
+
+ _webTextRequestOp.UpdateOperation();
+ Progress = _webTextRequestOp.Progress;
+ if (_webTextRequestOp.IsDone == false)
+ return;
+
+ if (_webTextRequestOp.Status == EOperationStatus.Succeed)
+ {
+ PackageHash = _webTextRequestOp.Result;
+ if (string.IsNullOrEmpty(PackageHash))
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Failed;
+ Error = $"Web package hash file content is empty !";
+ }
+ else
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Succeed;
+ }
+ }
+ else
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Failed;
+ Error = _webTextRequestOp.Error;
+ WebRequestCounter.RecordRequestFailed(_fileSystem.PackageName, nameof(RequestAlipayPackageHashOperation));
+ }
+ }
+ }
+
+ private string GetRequestURL(string fileName)
+ {
+ // 轮流返回请求地址
+ if (_requestCount % 2 == 0)
+ return _fileSystem.RemoteServices.GetRemoteMainURL(fileName);
+ else
+ return _fileSystem.RemoteServices.GetRemoteFallbackURL(fileName);
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/RequestAlipayPackageHashOperation.cs.meta b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/RequestAlipayPackageHashOperation.cs.meta
new file mode 100644
index 00000000..9559c2a2
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/RequestAlipayPackageHashOperation.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3b4f8cf6fe34a7e419f8e3a0063f591b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/RequestAlipayPackageVersionOperation.cs b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/RequestAlipayPackageVersionOperation.cs
new file mode 100644
index 00000000..69874c5b
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/RequestAlipayPackageVersionOperation.cs
@@ -0,0 +1,100 @@
+#if UNITY_WEBGL && UNITY_ALIMINIGAME
+using YooAsset;
+
+internal class RequestAlipayPackageVersionOperation : AsyncOperationBase
+{
+ private enum ESteps
+ {
+ None,
+ RequestPackageVersion,
+ Done,
+ }
+
+ private readonly AlipayFileSystem _fileSystem;
+ private readonly bool _appendTimeTicks;
+ private readonly int _timeout;
+ private UnityWebTextRequestOperation _webTextRequestOp;
+ private int _requestCount = 0;
+ private ESteps _steps = ESteps.None;
+
+ ///
+ /// 包裹版本
+ ///
+ public string PackageVersion { private set; get; }
+
+
+ public RequestAlipayPackageVersionOperation(AlipayFileSystem fileSystem, bool appendTimeTicks, int timeout)
+ {
+ _fileSystem = fileSystem;
+ _appendTimeTicks = appendTimeTicks;
+ _timeout = timeout;
+ }
+ internal override void InternalStart()
+ {
+ _requestCount = WebRequestCounter.GetRequestFailedCount(_fileSystem.PackageName, nameof(RequestAlipayPackageVersionOperation));
+ _steps = ESteps.RequestPackageVersion;
+ }
+ internal override void InternalUpdate()
+ {
+ if (_steps == ESteps.None || _steps == ESteps.Done)
+ return;
+
+ if (_steps == ESteps.RequestPackageVersion)
+ {
+ if (_webTextRequestOp == null)
+ {
+ string fileName = YooAssetSettingsData.GetPackageVersionFileName(_fileSystem.PackageName);
+ string url = GetRequestURL(fileName);
+ _webTextRequestOp = new UnityWebTextRequestOperation(url, _timeout);
+ _webTextRequestOp.StartOperation();
+ AddChildOperation(_webTextRequestOp);
+ }
+
+ _webTextRequestOp.UpdateOperation();
+ Progress = _webTextRequestOp.Progress;
+ if (_webTextRequestOp.IsDone == false)
+ return;
+
+ if (_webTextRequestOp.Status == EOperationStatus.Succeed)
+ {
+ PackageVersion = _webTextRequestOp.Result;
+ if (string.IsNullOrEmpty(PackageVersion))
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Failed;
+ Error = $"Web package version file content is empty !";
+ }
+ else
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Succeed;
+ }
+ }
+ else
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Failed;
+ Error = _webTextRequestOp.Error;
+ WebRequestCounter.RecordRequestFailed(_fileSystem.PackageName, nameof(RequestAlipayPackageVersionOperation));
+ }
+ }
+ }
+
+ private string GetRequestURL(string fileName)
+ {
+ string url;
+
+ // 轮流返回请求地址
+ if (_requestCount % 2 == 0)
+ url = _fileSystem.RemoteServices.GetRemoteMainURL(fileName);
+ else
+ url = _fileSystem.RemoteServices.GetRemoteFallbackURL(fileName);
+
+ // 在URL末尾添加时间戳
+ if (_appendTimeTicks)
+ return $"{url}?{System.DateTime.UtcNow.Ticks}";
+ else
+ return url;
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/RequestAlipayPackageVersionOperation.cs.meta b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/RequestAlipayPackageVersionOperation.cs.meta
new file mode 100644
index 00000000..08ed6918
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/RequestAlipayPackageVersionOperation.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0ef3d88ce51a6fa47be1b157316a740c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/UnityAlipayAssetBundleRequestOperation.cs b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/UnityAlipayAssetBundleRequestOperation.cs
new file mode 100644
index 00000000..867b79a1
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/UnityAlipayAssetBundleRequestOperation.cs
@@ -0,0 +1,95 @@
+#if UNITY_WEBGL && UNITY_ALIMINIGAME
+using UnityEngine.Networking;
+using UnityEngine;
+using AlipaySdk;
+
+namespace YooAsset
+{
+ internal class UnityAlipayAssetBundleRequestOperation : UnityWebRequestOperation
+ {
+ protected enum ESteps
+ {
+ None,
+ CreateRequest,
+ Download,
+ Done,
+ }
+
+ private readonly PackageBundle _packageBundle;
+ private UnityWebRequestAsyncOperation _requestOperation;
+ private ESteps _steps = ESteps.None;
+
+ ///
+ /// 请求结果
+ ///
+ public AssetBundle Result { private set; get; }
+
+ internal UnityAlipayAssetBundleRequestOperation(PackageBundle bundle, string url) : base(url)
+ {
+ _packageBundle = bundle;
+ }
+ internal override void InternalStart()
+ {
+ _steps = ESteps.CreateRequest;
+ }
+ internal override void InternalUpdate()
+ {
+ if (_steps == ESteps.None || _steps == ESteps.Done)
+ return;
+
+ if (_steps == ESteps.CreateRequest)
+ {
+ CreateWebRequest();
+ _steps = ESteps.Download;
+ }
+
+ if (_steps == ESteps.Download)
+ {
+ DownloadProgress = _webRequest.downloadProgress;
+ DownloadedBytes = (long)_webRequest.downloadedBytes;
+ Progress = _requestOperation.progress;
+ if (_requestOperation.isDone == false)
+ return;
+
+ if (CheckRequestResult())
+ {
+ var downloadHanlder = (DownloadHandlerAPAssetBundle)_webRequest.downloadHandler;
+ AssetBundle assetBundle = downloadHanlder.assetBundle;
+ if (assetBundle == null)
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Failed;
+ Error = $"URL : {_requestURL} Download handler asset bundle object is null !";
+ }
+ else
+ {
+ _steps = ESteps.Done;
+ Result = assetBundle;
+ Status = EOperationStatus.Succeed;
+
+ //TODO 需要验证插件请求器的下载进度
+ DownloadProgress = 1f;
+ DownloadedBytes = _packageBundle.FileSize;
+ Progress = 1f;
+ }
+ }
+ else
+ {
+ _steps = ESteps.Done;
+ Status = EOperationStatus.Failed;
+ }
+
+ // 注意:最终释放请求器
+ DisposeRequest();
+ }
+ }
+
+ private void CreateWebRequest()
+ {
+ _webRequest = APAssetBundle.GetAssetBundle(_requestURL);
+ _webRequest.disposeDownloadHandlerOnDispose = true;
+ _requestOperation = _webRequest.SendWebRequest();
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/UnityAlipayAssetBundleRequestOperation.cs.meta b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/UnityAlipayAssetBundleRequestOperation.cs.meta
new file mode 100644
index 00000000..a64ab9f5
--- /dev/null
+++ b/Assets/YooAsset/Samples~/Mini Game/Runtime/AlipayFileSystem/Operation/internal/UnityAlipayAssetBundleRequestOperation.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a2e7a4e37ac067f4ba0a43ebea0aa3f6
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: