YooAsset Extension程序集 因为MIniGame的FileSystem是写死了这个程序集

YooAsset Extension程序集 因为MIniGame的FileSystem是写死了这个程序集
This commit is contained in:
Alex-Rachel
2025-03-20 10:29:49 +08:00
parent 0ba9d1a8b7
commit 14bbb993f8
80 changed files with 43 additions and 2 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5d7171aa14ba0e649900533bfeed0edb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,16 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using YooAsset;
public static class AssetHandleExtension
{
/// <summary>
/// 等待异步执行完毕
/// </summary>
public static AssetHandle WaitForAsyncOperationComplete(this AssetHandle thisHandle)
{
thisHandle.WaitForAsyncComplete();
return thisHandle;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c0893ac05d109894e9acb6deeb688ee5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,12 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using YooAsset;
public static class HandleBaseExtension
{
public static bool IsSucceed(this HandleBase thisHandle)
{
return thisHandle.IsDone && thisHandle.Status == EOperationStatus.Succeed;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 03c367406c5062c41ba3290201be20c2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5280dfac6a481ee429c769ba5688c9d2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ccb22c5c90bfe2f43a0f83f873dd6646
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 879f2b393f883554899ab3345f0dc998
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,64 @@
#if UNITY_WEBGL && DOUYINMINIGAME
using System.IO;
using UnityEngine;
using UnityEngine.SceneManagement;
using TTSDK;
namespace YooAsset
{
internal class TTAssetBundleResult : BundleResult
{
private readonly IFileSystem _fileSystem;
private readonly PackageBundle _packageBundle;
private readonly AssetBundle _assetBundle;
public TTAssetBundleResult(IFileSystem fileSystem, PackageBundle packageBundle, AssetBundle assetBundle)
{
_fileSystem = fileSystem;
_packageBundle = packageBundle;
_assetBundle = assetBundle;
}
public override void UnloadBundleFile()
{
if (_assetBundle != null)
{
_assetBundle.TTUnload(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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 80ce0631d38cf74458c902fb325afb81
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8cba5e0b26df8ee40a93d697f889e33d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,101 @@
#if UNITY_WEBGL && DOUYINMINIGAME
using UnityEngine;
using UnityEngine.Networking;
using YooAsset;
internal class TTFSDownloadFileOperation : DefaultDownloadFileOperation
{
private TiktokFileSystem _fileSystem;
private ESteps _steps = ESteps.None;
internal TTFSDownloadFileOperation(TiktokFileSystem fileSystem, PackageBundle bundle, DownloadParam param) : base(bundle, param)
{
_fileSystem = fileSystem;
}
internal override void InternalStart()
{
_steps = ESteps.CreateRequest;
}
internal override void InternalUpdate()
{
// 创建下载器
if (_steps == ESteps.CreateRequest)
{
// 获取请求地址
_requestURL = GetRequestURL();
// 重置变量
ResetRequestFiled();
// 创建下载器
CreateWebRequest();
_steps = ESteps.CheckRequest;
}
// 检测下载结果
if (_steps == ESteps.CheckRequest)
{
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes;
Progress = DownloadProgress;
if (_webRequest.isDone == false)
{
CheckRequestTimeout();
return;
}
// 检查网络错误
if (CheckRequestResult())
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.TryAgain;
}
// 注意:最终释放请求器
DisposeWebRequest();
}
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
if (FailedTryAgain <= 0)
{
Status = EOperationStatus.Failed;
_steps = ESteps.Done;
YooLogger.Error(Error);
return;
}
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
FailedTryAgain--;
_steps = ESteps.CreateRequest;
YooLogger.Warning(Error);
}
}
}
private void CreateWebRequest()
{
//TODO : 抖音小游戏没有找到预下载方法
_webRequest = UnityWebRequest.Get(_requestURL);
_webRequest.disposeDownloadHandlerOnDispose = true;
_webRequest.SendWebRequest();
}
private void DisposeWebRequest()
{
if (_webRequest != null)
{
//注意引擎底层会自动调用Abort方法
_webRequest.Dispose();
_webRequest = null;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0bdf53359f9e17b41b08982a2be2098c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
#if UNITY_WEBGL && DOUYINMINIGAME
using YooAsset;
internal partial class TTFSInitializeOperation : FSInitializeFileSystemOperation
{
private readonly TiktokFileSystem _fileSystem;
public TTFSInitializeOperation(TiktokFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
internal override void InternalStart()
{
Status = EOperationStatus.Succeed;
}
internal override void InternalUpdate()
{
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 614227bf558da7149a7eb6de7dfa1a21
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,98 @@
#if UNITY_WEBGL && DOUYINMINIGAME
using UnityEngine;
using UnityEngine.Networking;
using YooAsset;
internal class TTFSLoadBundleOperation : FSLoadBundleOperation
{
private enum ESteps
{
None,
DownloadAssetBundle,
Done,
}
private readonly TiktokFileSystem _fileSystem;
private readonly PackageBundle _bundle;
private DownloadAssetBundleOperation _downloadAssetBundleOp;
private ESteps _steps = ESteps.None;
internal TTFSLoadBundleOperation(TiktokFileSystem fileSystem, PackageBundle bundle)
{
_fileSystem = fileSystem;
_bundle = bundle;
}
internal override void InternalStart()
{
_steps = ESteps.DownloadAssetBundle;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.DownloadAssetBundle)
{
if (_downloadAssetBundleOp == null)
{
DownloadParam downloadParam = new DownloadParam(int.MaxValue, 60);
downloadParam.MainURL = _fileSystem.RemoteServices.GetRemoteMainURL(_bundle.FileName); ;
downloadParam.FallbackURL = _fileSystem.RemoteServices.GetRemoteFallbackURL(_bundle.FileName);
if (_bundle.Encrypted)
{
_downloadAssetBundleOp = new DownloadWebEncryptAssetBundleOperation(false, _fileSystem.DecryptionServices, _bundle, downloadParam);
_downloadAssetBundleOp.StartOperation();
AddChildOperation(_downloadAssetBundleOp);
}
else
{
_downloadAssetBundleOp = new DownloadTiktokAssetBundleOperation(_bundle, downloadParam);
_downloadAssetBundleOp.StartOperation();
AddChildOperation(_downloadAssetBundleOp);
}
}
_downloadAssetBundleOp.UpdateOperation();
DownloadProgress = _downloadAssetBundleOp.DownloadProgress;
DownloadedBytes = (long)_downloadAssetBundleOp.DownloadedBytes;
Progress = DownloadProgress;
if (_downloadAssetBundleOp.IsDone == false)
return;
if (_downloadAssetBundleOp.Status == EOperationStatus.Succeed)
{
var assetBundle = _downloadAssetBundleOp.Result;
if (assetBundle == null)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"{nameof(DownloadAssetBundleOperation)} loaded asset bundle is null !";
}
else
{
_steps = ESteps.Done;
Result = new TTAssetBundleResult(_fileSystem, _bundle, assetBundle);
Status = EOperationStatus.Succeed;
}
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _downloadAssetBundleOp.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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 19997de7b89b54445961e8b973f43ab3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,92 @@
#if UNITY_WEBGL && DOUYINMINIGAME
using YooAsset;
internal class TTFSLoadPackageManifestOperation : FSLoadPackageManifestOperation
{
private enum ESteps
{
None,
RequestPackageHash,
LoadPackageManifest,
Done,
}
private readonly TiktokFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly int _timeout;
private RequestTiktokPackageHashOperation _requestPackageHashOp;
private LoadTiktokPackageManifestOperation _loadPackageManifestOp;
private ESteps _steps = ESteps.None;
public TTFSLoadPackageManifestOperation(TiktokFileSystem 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 RequestTiktokPackageHashOperation(_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 LoadTiktokPackageManifestOperation(_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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2850c0b0e742f824f9d1f12b7d68b806
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,62 @@
#if UNITY_WEBGL && DOUYINMINIGAME
using YooAsset;
internal class TTFSRequestPackageVersionOperation : FSRequestPackageVersionOperation
{
private enum ESteps
{
None,
RequestPackageVersion,
Done,
}
private readonly TiktokFileSystem _fileSystem;
private readonly int _timeout;
private RequestTiktokPackageVersionOperation _requestPackageVersionOp;
private ESteps _steps = ESteps.None;
internal TTFSRequestPackageVersionOperation(TiktokFileSystem fileSystem, int timeout)
{
_fileSystem = fileSystem;
_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 (_requestPackageVersionOp == null)
{
_requestPackageVersionOp = new RequestTiktokPackageVersionOperation(_fileSystem, _timeout);
_requestPackageVersionOp.StartOperation();
AddChildOperation(_requestPackageVersionOp);
}
_requestPackageVersionOp.UpdateOperation();
Progress = _requestPackageVersionOp.Progress;
if (_requestPackageVersionOp.IsDone == false)
return;
if (_requestPackageVersionOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.Done;
PackageVersion = _requestPackageVersionOp.PackageVersion;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _requestPackageVersionOp.Error;
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8210185faba92a3479bf6713abab1f19
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 101c97835f86e9147993961f514caf2c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,126 @@
#if UNITY_WEBGL && DOUYINMINIGAME
using UnityEngine;
using TTSDK;
namespace YooAsset
{
internal class DownloadTiktokAssetBundleOperation : DownloadAssetBundleOperation
{
private ESteps _steps = ESteps.None;
internal DownloadTiktokAssetBundleOperation(PackageBundle bundle, DownloadParam param) : base(bundle, param)
{
}
internal override void InternalStart()
{
_steps = ESteps.CreateRequest;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
// 创建下载器
if (_steps == ESteps.CreateRequest)
{
// 获取请求地址
_requestURL = GetRequestURL();
// 重置变量
ResetRequestFiled();
// 创建下载器
CreateWebRequest();
_steps = ESteps.CheckRequest;
}
// 检测下载结果
if (_steps == ESteps.CheckRequest)
{
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes;
Progress = DownloadProgress;
if (_webRequest.isDone == false)
{
//TODO 需要验证抖音插件请求器的下载进度
//CheckRequestTimeout();
return;
}
// 检查网络错误
if (CheckRequestResult())
{
var downloadHanlder = (DownloadHandlerTTAssetBundle)_webRequest.downloadHandler;
AssetBundle assetBundle = downloadHanlder.assetBundle;
if (assetBundle == null)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Download handler asset bundle object is null !";
}
else
{
_steps = ESteps.Done;
Result = assetBundle;
Status = EOperationStatus.Succeed;
//TODO 需要验证抖音插件请求器的下载进度
DownloadProgress = 1f;
DownloadedBytes = Bundle.FileSize;
Progress = 1f;
}
}
else
{
_steps = ESteps.TryAgain;
}
// 注意:最终释放请求器
DisposeWebRequest();
}
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
if (FailedTryAgain <= 0)
{
Status = EOperationStatus.Failed;
_steps = ESteps.Done;
YooLogger.Error(Error);
return;
}
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
FailedTryAgain--;
_steps = ESteps.CreateRequest;
YooLogger.Warning(Error);
}
}
}
internal override void InternalAbort()
{
_steps = ESteps.Done;
DisposeWebRequest();
}
private void CreateWebRequest()
{
_webRequest = TTAssetBundle.GetAssetBundle(_requestURL);
_webRequest.disposeDownloadHandlerOnDispose = true;
_webRequest.SendWebRequest();
}
private void DisposeWebRequest()
{
if (_webRequest != null)
{
//注意引擎底层会自动调用Abort方法
_webRequest.Dispose();
_webRequest = null;
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fa5e1bc536118b14ba56f53930539e38
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,129 @@
#if UNITY_WEBGL && DOUYINMINIGAME
using YooAsset;
internal class LoadTiktokPackageManifestOperation : AsyncOperationBase
{
private enum ESteps
{
None,
RequestFileData,
VerifyFileData,
LoadManifest,
Done,
}
private readonly TiktokFileSystem _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;
/// <summary>
/// 包裹清单
/// </summary>
public PackageManifest Manifest { private set; get; }
internal LoadTiktokPackageManifestOperation(TiktokFileSystem 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(LoadTiktokPackageManifestOperation));
_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(LoadTiktokPackageManifestOperation));
}
}
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(_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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b648d648fa0c4e44d811b42b80891543
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,92 @@
#if UNITY_WEBGL && DOUYINMINIGAME
using YooAsset;
internal class RequestTiktokPackageHashOperation : AsyncOperationBase
{
private enum ESteps
{
None,
RequestPackageHash,
Done,
}
private readonly TiktokFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly int _timeout;
private UnityWebTextRequestOperation _webTextRequestOp;
private int _requestCount = 0;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹哈希值
/// </summary>
public string PackageHash { private set; get; }
public RequestTiktokPackageHashOperation(TiktokFileSystem fileSystem, string packageVersion, int timeout)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
_timeout = timeout;
}
internal override void InternalStart()
{
_requestCount = WebRequestCounter.GetRequestFailedCount(_fileSystem.PackageName, nameof(RequestTiktokPackageHashOperation));
_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 = $"Wechat 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(RequestTiktokPackageHashOperation));
}
}
}
private string GetRequestURL(string fileName)
{
// 轮流返回请求地址
if (_requestCount % 2 == 0)
return _fileSystem.RemoteServices.GetRemoteMainURL(fileName);
else
return _fileSystem.RemoteServices.GetRemoteFallbackURL(fileName);
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 51a0e40e248b49a4783b3dca33a23cf1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,90 @@
#if UNITY_WEBGL && DOUYINMINIGAME
using YooAsset;
internal class RequestTiktokPackageVersionOperation : AsyncOperationBase
{
private enum ESteps
{
None,
RequestPackageVersion,
Done,
}
private readonly TiktokFileSystem _fileSystem;
private readonly int _timeout;
private UnityWebTextRequestOperation _webTextRequestOp;
private int _requestCount = 0;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹版本
/// </summary>
public string PackageVersion { private set; get; }
public RequestTiktokPackageVersionOperation(TiktokFileSystem fileSystem, int timeout)
{
_fileSystem = fileSystem;
_timeout = timeout;
}
internal override void InternalStart()
{
_requestCount = WebRequestCounter.GetRequestFailedCount(_fileSystem.PackageName, nameof(RequestTiktokPackageVersionOperation));
_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 = $"Wechat 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(RequestTiktokPackageVersionOperation));
}
}
}
private string GetRequestURL(string fileName)
{
// 轮流返回请求地址
if (_requestCount % 2 == 0)
return _fileSystem.RemoteServices.GetRemoteMainURL(fileName);
else
return _fileSystem.RemoteServices.GetRemoteFallbackURL(fileName);
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a04c463fb00f60f499541fbb98e4fdc4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,266 @@
#if UNITY_WEBGL && DOUYINMINIGAME
using System.Collections.Generic;
using UnityEngine;
using YooAsset;
using TTSDK;
using System.Linq;
using System;
public static class TiktokFileSystemCreater
{
public static FileSystemParameters CreateFileSystemParameters(string packageRoot, IRemoteServices remoteServices)
{
string fileSystemClass = $"{nameof(TiktokFileSystem)},YooAsset.RuntimeExtension";
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(TiktokFileSystem)},YooAsset.RuntimeExtension";
var fileSystemParams = new FileSystemParameters(fileSystemClass, packageRoot);
fileSystemParams.AddParameter(FileSystemParametersDefine.REMOTE_SERVICES, remoteServices);
fileSystemParams.AddParameter(FileSystemParametersDefine.DECRYPTION_SERVICES, decryptionServices);
return fileSystemParams;
}
}
/// <summary>
/// 抖音小游戏文件系统
/// 参考https://developer.open-douyin.com/docs/resource/zh-CN/mini-game/develop/guide/know
/// </summary>
internal class TiktokFileSystem : IFileSystem
{
private class WebRemoteServices : IRemoteServices
{
private readonly string _webPackageRoot;
protected readonly Dictionary<string, string> _mapping = new Dictionary<string, string>(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<string, string> _cacheFilePathMapping = new Dictionary<string, string>(10000);
private TTFileSystemManager _fileSystemMgr;
private string _ttCacheRoot = string.Empty;
/// <summary>
/// 包裹名称
/// </summary>
public string PackageName { private set; get; }
/// <summary>
/// 文件根目录
/// </summary>
public string FileRoot
{
get
{
return _ttCacheRoot;
}
}
/// <summary>
/// 文件数量
/// </summary>
public int FileCount
{
get
{
return 0;
}
}
#region
/// <summary>
/// 自定义参数:远程服务接口
/// </summary>
public IRemoteServices RemoteServices { private set; get; } = null;
/// <summary>
/// 自定义参数:解密方法类
/// </summary>
public IWebDecryptionServices DecryptionServices { private set; get; }
#endregion
public TiktokFileSystem()
{
}
public virtual FSInitializeFileSystemOperation InitializeFileSystemAsync()
{
var operation = new TTFSInitializeOperation(this);
return operation;
}
public virtual FSLoadPackageManifestOperation LoadPackageManifestAsync(string packageVersion, int timeout)
{
var operation = new TTFSLoadPackageManifestOperation(this, packageVersion, timeout);
return operation;
}
public virtual FSRequestPackageVersionOperation RequestPackageVersionAsync(bool appendTimeTicks, int timeout)
{
var operation = new TTFSRequestPackageVersionOperation(this, timeout);
return operation;
}
public virtual FSClearCacheFilesOperation ClearCacheFilesAsync(PackageManifest manifest, string clearMode, object clearParam)
{
var operation = new FSClearCacheFilesCompleteOperation();
return operation;
}
public virtual FSDownloadFileOperation DownloadFileAsync(PackageBundle bundle, DownloadParam param)
{
param.MainURL = RemoteServices.GetRemoteMainURL(bundle.FileName);
param.FallbackURL = RemoteServices.GetRemoteFallbackURL(bundle.FileName);
var operation = new TTFSDownloadFileOperation(this, bundle, param);
return operation;
}
public virtual FSLoadBundleOperation LoadBundleFile(PackageBundle bundle)
{
if (bundle.BundleType == (int)EBuildBundleType.AssetBundle)
{
var operation = new TTFSLoadBundleOperation(this, bundle);
return operation;
}
else
{
string error = $"{nameof(TiktokFileSystem)} 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
{
YooLogger.Warning($"Invalid parameter : {name}");
}
}
public virtual void OnCreate(string packageName, string rootDirectory)
{
PackageName = packageName;
_ttCacheRoot = rootDirectory;
if (string.IsNullOrEmpty(_ttCacheRoot))
{
throw new System.Exception("请配置抖音小游戏的缓存根目录!");
}
// 注意CDN服务未启用的情况下使用抖音WEB服务器
if (RemoteServices == null)
{
string webRoot = PathUtility.Combine(Application.streamingAssetsPath, YooAssetSettingsData.Setting.DefaultYooFolderName, packageName);
RemoteServices = new WebRemoteServices(webRoot);
}
_fileSystemMgr = TT.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<byte>();
}
}
public virtual string ReadBundleFileText(PackageBundle bundle)
{
if (CheckCacheFileExist(bundle))
{
string filePath = GetCacheFileLoadPath(bundle);
return _fileSystemMgr.ReadFileSync(filePath, "utf8");
}
else
{
return string.Empty;
}
}
#region
public TTFileSystemManager GetFileSystemMgr()
{
return _fileSystemMgr;
}
public bool CheckCacheFileExist(PackageBundle bundle)
{
string url = RemoteServices.GetRemoteMainURL(bundle.FileName);
return _fileSystemMgr.IsUrlCached(url);
}
private string GetCacheFileLoadPath(PackageBundle bundle)
{
if (_cacheFilePathMapping.TryGetValue(bundle.BundleGUID, out string filePath) == false)
{
filePath = _fileSystemMgr.GetLocalCachedPathForUrl(bundle.FileName);
_cacheFilePathMapping.Add(bundle.BundleGUID, filePath);
}
return filePath;
}
#endregion
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e6b4324eddc60f045aa271bc5bb50cc9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f0165bf1a62988a439ce3a00db043620
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b4c0dec5e8fae814eb7ac0299187e8f4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,64 @@
#if UNITY_WEBGL && WEIXINMINIGAME
using System.IO;
using UnityEngine;
using UnityEngine.SceneManagement;
using WeChatWASM;
namespace YooAsset
{
internal class WXAssetBundleResult : BundleResult
{
private readonly IFileSystem _fileSystem;
private readonly PackageBundle _packageBundle;
private readonly AssetBundle _assetBundle;
public WXAssetBundleResult(IFileSystem fileSystem, PackageBundle packageBundle, AssetBundle assetBundle)
{
_fileSystem = fileSystem;
_packageBundle = packageBundle;
_assetBundle = assetBundle;
}
public override void UnloadBundleFile()
{
if (_assetBundle != null)
{
_assetBundle.WXUnload(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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 750c122b5d921aa4a9c882e21982ebd5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 85ec3f4c43303f74a9b9fa14288f9201
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,57 @@
#if UNITY_WEBGL && WEIXINMINIGAME
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using YooAsset;
using WeChatWASM;
internal class WXFSClearAllBundleFilesOperation : FSClearCacheFilesOperation
{
private enum ESteps
{
None,
ClearAllCacheFiles,
WaitResult,
Done,
}
private readonly WechatFileSystem _fileSystem;
private ESteps _steps = ESteps.None;
internal WXFSClearAllBundleFilesOperation(WechatFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
internal override void InternalStart()
{
_steps = ESteps.ClearAllCacheFiles;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.ClearAllCacheFiles)
{
_steps = ESteps.WaitResult;
WX.CleanAllFileCache((bool isOk) =>
{
if (isOk)
{
YooLogger.Log("微信缓存清理成功!");
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
YooLogger.Log("微信缓存清理失败!");
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "微信缓存清理失败!";
}
});
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d48600eb90915544586c0108b94cfd02
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,98 @@
#if UNITY_WEBGL && WEIXINMINIGAME
using System.Collections.Generic;
using System.IO;
using YooAsset;
using WeChatWASM;
internal class WXFSClearUnusedBundleFilesAsync : FSClearCacheFilesOperation
{
private enum ESteps
{
None,
GetUnusedCacheFiles,
WaitingSearch,
ClearUnusedCacheFiles,
Done,
}
private readonly WechatFileSystem _fileSystem;
private readonly PackageManifest _manifest;
private List<string> _unusedCacheFiles;
private int _unusedFileTotalCount = 0;
private ESteps _steps = ESteps.None;
internal WXFSClearUnusedBundleFilesAsync(WechatFileSystem fileSystem, PackageManifest manifest)
{
_fileSystem = fileSystem;
_manifest = manifest;
}
internal override void InternalStart()
{
_steps = ESteps.GetUnusedCacheFiles;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.GetUnusedCacheFiles)
{
_steps = ESteps.WaitingSearch;
var fileSystemMgr = _fileSystem.GetFileSystemMgr();
var statOption = new WXStatOption();
statOption.path = _fileSystem.FileRoot;
statOption.recursive = true;
statOption.success = (WXStatResponse response) =>
{
foreach (var fileStat in response.stats)
{
// 注意存储文件必须按照Bundle文件哈希值存储
string bundleGUID = Path.GetFileNameWithoutExtension(fileStat.path);
if (_manifest.TryGetPackageBundleByBundleGUID(bundleGUID, out PackageBundle value) == false)
{
string fullPath = WX.GetCachePath(fileStat.path);
if (_unusedCacheFiles.Contains(fullPath) == false)
_unusedCacheFiles.Add(fullPath);
}
}
_steps = ESteps.ClearUnusedCacheFiles;
_unusedFileTotalCount = _unusedCacheFiles.Count;
YooLogger.Log($"Found unused cache files count : {_unusedFileTotalCount}");
};
statOption.fail = (WXStatResponse response) =>
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = response.errMsg;
};
fileSystemMgr.Stat(statOption);
}
if (_steps == ESteps.ClearUnusedCacheFiles)
{
for (int i = _unusedCacheFiles.Count - 1; i >= 0; i--)
{
string filePath = _unusedCacheFiles[i];
_unusedCacheFiles.RemoveAt(i);
WX.RemoveFile(filePath, null);
if (OperationSystem.IsBusy)
break;
}
if (_unusedFileTotalCount == 0)
Progress = 1.0f;
else
Progress = 1.0f - (_unusedCacheFiles.Count / _unusedFileTotalCount);
if (_unusedCacheFiles.Count == 0)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e4938d8a5a9d7f24db37f8df6b32501f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,110 @@
#if UNITY_WEBGL && WEIXINMINIGAME
using UnityEngine;
using UnityEngine.Networking;
using YooAsset;
using WeChatWASM;
internal class WXFSDownloadFileOperation : DefaultDownloadFileOperation
{
private WechatFileSystem _fileSystem;
private ESteps _steps = ESteps.None;
internal WXFSDownloadFileOperation(WechatFileSystem fileSystem, PackageBundle bundle, DownloadParam param) : base(bundle, param)
{
_fileSystem = fileSystem;
}
internal override void InternalStart()
{
_steps = ESteps.CreateRequest;
}
internal override void InternalUpdate()
{
// 创建下载器
if (_steps == ESteps.CreateRequest)
{
// 获取请求地址
_requestURL = GetRequestURL();
// 重置变量
ResetRequestFiled();
// 创建下载器
CreateWebRequest();
_steps = ESteps.CheckRequest;
}
// 检测下载结果
if (_steps == ESteps.CheckRequest)
{
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes;
Progress = DownloadProgress;
if (_webRequest.isDone == false)
{
//TODO 由于微信小游戏插件的问题,暂时不能判定超时!
// Issue : https://github.com/wechat-miniprogram/minigame-unity-webgl-transform/issues/108#
//CheckRequestTimeout();
return;
}
// 检查网络错误
if (CheckRequestResult())
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
//TODO 解决微信小游戏插件问题
// Issue : https://github.com/wechat-miniprogram/minigame-unity-webgl-transform/issues/108#
DownloadProgress = 1f;
DownloadedBytes = Bundle.FileSize;
Progress = 1f;
}
else
{
_steps = ESteps.TryAgain;
}
// 注意:最终释放请求器
DisposeWebRequest();
}
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
if (FailedTryAgain <= 0)
{
Status = EOperationStatus.Failed;
_steps = ESteps.Done;
YooLogger.Error(Error);
return;
}
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
FailedTryAgain--;
_steps = ESteps.CreateRequest;
YooLogger.Warning(Error);
}
}
}
private void CreateWebRequest()
{
_webRequest = UnityWebRequest.Get(_requestURL);
_webRequest.SetRequestHeader("wechatminigame-preload", "1");
_webRequest.disposeDownloadHandlerOnDispose = true;
_webRequest.SendWebRequest();
}
private void DisposeWebRequest()
{
if (_webRequest != null)
{
//注意引擎底层会自动调用Abort方法
_webRequest.Dispose();
_webRequest = null;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e58c7876bd106e04380cbf4c00e8d584
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
#if UNITY_WEBGL && WEIXINMINIGAME
using YooAsset;
internal partial class WXFSInitializeOperation : FSInitializeFileSystemOperation
{
private readonly WechatFileSystem _fileSystem;
public WXFSInitializeOperation(WechatFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
internal override void InternalStart()
{
Status = EOperationStatus.Succeed;
}
internal override void InternalUpdate()
{
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a0c5566de97a44245b840bf388cdfbe0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,96 @@
#if UNITY_WEBGL && WEIXINMINIGAME
using YooAsset;
internal class WXFSLoadBundleOperation : FSLoadBundleOperation
{
private enum ESteps
{
None,
DownloadAssetBundle,
Done,
}
private readonly WechatFileSystem _fileSystem;
private readonly PackageBundle _bundle;
private DownloadAssetBundleOperation _downloadAssetBundleOp;
private ESteps _steps = ESteps.None;
internal WXFSLoadBundleOperation(WechatFileSystem fileSystem, PackageBundle bundle)
{
_fileSystem = fileSystem;
_bundle = bundle;
}
internal override void InternalStart()
{
_steps = ESteps.DownloadAssetBundle;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.DownloadAssetBundle)
{
if (_downloadAssetBundleOp == null)
{
DownloadParam downloadParam = new DownloadParam(int.MaxValue, 60);
downloadParam.MainURL = _fileSystem.RemoteServices.GetRemoteMainURL(_bundle.FileName); ;
downloadParam.FallbackURL = _fileSystem.RemoteServices.GetRemoteFallbackURL(_bundle.FileName);
if (_bundle.Encrypted)
{
_downloadAssetBundleOp = new DownloadWebEncryptAssetBundleOperation(false, _fileSystem.DecryptionServices, _bundle, downloadParam);
_downloadAssetBundleOp.StartOperation();
AddChildOperation(_downloadAssetBundleOp);
}
else
{
_downloadAssetBundleOp = new DownloadWechatAssetBundleOperation(_bundle, downloadParam);
_downloadAssetBundleOp.StartOperation();
AddChildOperation(_downloadAssetBundleOp);
}
}
_downloadAssetBundleOp.UpdateOperation();
DownloadProgress = _downloadAssetBundleOp.DownloadProgress;
DownloadedBytes = (long)_downloadAssetBundleOp.DownloadedBytes;
Progress = DownloadProgress;
if (_downloadAssetBundleOp.IsDone == false)
return;
if (_downloadAssetBundleOp.Status == EOperationStatus.Succeed)
{
var assetBundle = _downloadAssetBundleOp.Result;
if (assetBundle == null)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"{nameof(DownloadAssetBundleOperation)} loaded asset bundle is null !";
}
else
{
_steps = ESteps.Done;
Result = new WXAssetBundleResult(_fileSystem, _bundle, assetBundle);
Status = EOperationStatus.Succeed;
}
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _downloadAssetBundleOp.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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7277b7d1ed5616b41986cad60c3fe97c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,92 @@
#if UNITY_WEBGL && WEIXINMINIGAME
using YooAsset;
internal class WXFSLoadPackageManifestOperation : FSLoadPackageManifestOperation
{
private enum ESteps
{
None,
RequestPackageHash,
LoadPackageManifest,
Done,
}
private readonly WechatFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly int _timeout;
private RequestWechatPackageHashOperation _requestPackageHashOp;
private LoadWechatPackageManifestOperation _loadPackageManifestOp;
private ESteps _steps = ESteps.None;
public WXFSLoadPackageManifestOperation(WechatFileSystem 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 RequestWechatPackageHashOperation(_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 LoadWechatPackageManifestOperation(_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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cfbe2efa84cb0524f9e3482e6b895f41
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,64 @@
#if UNITY_WEBGL && WEIXINMINIGAME
using YooAsset;
internal class WXFSRequestPackageVersionOperation : FSRequestPackageVersionOperation
{
private enum ESteps
{
None,
RequestPackageVersion,
Done,
}
private readonly WechatFileSystem _fileSystem;
private readonly bool _appendTimeTicks;
private readonly int _timeout;
private RequestWechatPackageVersionOperation _requestWebPackageVersionOp;
private ESteps _steps = ESteps.None;
internal WXFSRequestPackageVersionOperation(WechatFileSystem 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 RequestWechatPackageVersionOperation(_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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4353a9a0cbf86cc43acf3320458e3ac8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3fd307b5063003545a663ccbec226a23
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,128 @@
#if UNITY_WEBGL && WEIXINMINIGAME
using UnityEngine;
using WeChatWASM;
namespace YooAsset
{
internal class DownloadWechatAssetBundleOperation : DownloadAssetBundleOperation
{
private ESteps _steps = ESteps.None;
internal DownloadWechatAssetBundleOperation(PackageBundle bundle, DownloadParam param) : base(bundle, param)
{
}
internal override void InternalStart()
{
_steps = ESteps.CreateRequest;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
// 创建下载器
if (_steps == ESteps.CreateRequest)
{
// 获取请求地址
_requestURL = GetRequestURL();
// 重置变量
ResetRequestFiled();
// 创建下载器
CreateWebRequest();
_steps = ESteps.CheckRequest;
}
// 检测下载结果
if (_steps == ESteps.CheckRequest)
{
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes;
Progress = DownloadProgress;
if (_webRequest.isDone == false)
{
//TODO 由于微信小游戏插件的问题,暂时不能判定超时!
// Issue : https://github.com/wechat-miniprogram/minigame-unity-webgl-transform/issues/108#
//CheckRequestTimeout();
return;
}
// 检查网络错误
if (CheckRequestResult())
{
var downloadHanlder = (DownloadHandlerWXAssetBundle)_webRequest.downloadHandler;
AssetBundle assetBundle = downloadHanlder.assetBundle;
if (assetBundle == null)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Download handler asset bundle object is null !";
}
else
{
_steps = ESteps.Done;
Result = assetBundle;
Status = EOperationStatus.Succeed;
//TODO 解决微信小游戏插件问题
// Issue : https://github.com/wechat-miniprogram/minigame-unity-webgl-transform/issues/108#
DownloadProgress = 1f;
DownloadedBytes = Bundle.FileSize;
Progress = 1f;
}
}
else
{
_steps = ESteps.TryAgain;
}
// 注意:最终释放请求器
DisposeWebRequest();
}
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
if (FailedTryAgain <= 0)
{
Status = EOperationStatus.Failed;
_steps = ESteps.Done;
YooLogger.Error(Error);
return;
}
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
FailedTryAgain--;
_steps = ESteps.CreateRequest;
YooLogger.Warning(Error);
}
}
}
internal override void InternalAbort()
{
_steps = ESteps.Done;
DisposeWebRequest();
}
private void CreateWebRequest()
{
_webRequest = WXAssetBundle.GetAssetBundle(_requestURL);
_webRequest.disposeDownloadHandlerOnDispose = true;
_webRequest.SendWebRequest();
}
private void DisposeWebRequest()
{
if (_webRequest != null)
{
//注意引擎底层会自动调用Abort方法
_webRequest.Dispose();
_webRequest = null;
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e556be3f274429e4ca6c542fff90e6ab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,129 @@
#if UNITY_WEBGL && WEIXINMINIGAME
using YooAsset;
internal class LoadWechatPackageManifestOperation : AsyncOperationBase
{
private enum ESteps
{
None,
RequestFileData,
VerifyFileData,
LoadManifest,
Done,
}
private readonly WechatFileSystem _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;
/// <summary>
/// 包裹清单
/// </summary>
public PackageManifest Manifest { private set; get; }
internal LoadWechatPackageManifestOperation(WechatFileSystem 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(LoadWechatPackageManifestOperation));
_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(LoadWechatPackageManifestOperation));
}
}
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(_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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c2153284d246964fb2146f9fdda311c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,92 @@
#if UNITY_WEBGL && WEIXINMINIGAME
using YooAsset;
internal class RequestWechatPackageHashOperation : AsyncOperationBase
{
private enum ESteps
{
None,
RequestPackageHash,
Done,
}
private readonly WechatFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly int _timeout;
private UnityWebTextRequestOperation _webTextRequestOp;
private int _requestCount = 0;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹哈希值
/// </summary>
public string PackageHash { private set; get; }
public RequestWechatPackageHashOperation(WechatFileSystem fileSystem, string packageVersion, int timeout)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
_timeout = timeout;
}
internal override void InternalStart()
{
_requestCount = WebRequestCounter.GetRequestFailedCount(_fileSystem.PackageName, nameof(RequestWechatPackageHashOperation));
_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 = $"Wechat 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(RequestWechatPackageHashOperation));
}
}
}
private string GetRequestURL(string fileName)
{
// 轮流返回请求地址
if (_requestCount % 2 == 0)
return _fileSystem.RemoteServices.GetRemoteMainURL(fileName);
else
return _fileSystem.RemoteServices.GetRemoteFallbackURL(fileName);
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3f8269507a575884f935f9fbc71396ea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,100 @@
#if UNITY_WEBGL && WEIXINMINIGAME
using YooAsset;
internal class RequestWechatPackageVersionOperation : AsyncOperationBase
{
private enum ESteps
{
None,
RequestPackageVersion,
Done,
}
private readonly WechatFileSystem _fileSystem;
private readonly int _timeout;
private readonly bool _appendTimeTicks;
private UnityWebTextRequestOperation _webTextRequestOp;
private int _requestCount = 0;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹版本
/// </summary>
public string PackageVersion { private set; get; }
public RequestWechatPackageVersionOperation(WechatFileSystem fileSystem, bool appendTimeTicks, int timeout)
{
_fileSystem = fileSystem;
_appendTimeTicks = appendTimeTicks;
_timeout = timeout;
}
internal override void InternalStart()
{
_requestCount = WebRequestCounter.GetRequestFailedCount(_fileSystem.PackageName, nameof(RequestWechatPackageVersionOperation));
_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 = $"Wechat 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(RequestWechatPackageVersionOperation));
}
}
}
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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 758fbcd91469b3f4e843a9ea7e3deda9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,285 @@
#if UNITY_WEBGL && WEIXINMINIGAME
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Scripting;
using YooAsset;
using WeChatWASM;
[Preserve]
public static class WechatFileSystemCreater
{
public static FileSystemParameters CreateFileSystemParameters(string packageRoot, IRemoteServices remoteServices)
{
string fileSystemClass = $"{nameof(WechatFileSystem)},YooAsset.RuntimeExtension";
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(WechatFileSystem)},YooAsset.RuntimeExtension";
var fileSystemParams = new FileSystemParameters(fileSystemClass, packageRoot);
fileSystemParams.AddParameter(FileSystemParametersDefine.REMOTE_SERVICES, remoteServices);
fileSystemParams.AddParameter(FileSystemParametersDefine.DECRYPTION_SERVICES, decryptionServices);
return fileSystemParams;
}
}
/// <summary>
/// 微信小游戏文件系统
/// 参考https://wechat-miniprogram.github.io/minigame-unity-webgl-transform/Design/UsingAssetBundle.html
/// </summary>
[Preserve]
internal class WechatFileSystem : IFileSystem
{
private class WebRemoteServices : IRemoteServices
{
private readonly string _webPackageRoot;
protected readonly Dictionary<string, string> _mapping = new Dictionary<string, string>(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<string, string> _cacheFilePathMapping = new Dictionary<string, string>(10000);
private WXFileSystemManager _fileSystemMgr;
private string _wxCacheRoot = string.Empty;
/// <summary>
/// 包裹名称
/// </summary>
public string PackageName { private set; get; }
private readonly string _packageRoot = YooAssetSettingsData.Setting.DefaultYooFolderName;
/// <summary>
/// 文件根目录
/// </summary>
public string FileRoot
{
get
{
return _wxCacheRoot;
}
}
/// <summary>
/// 文件数量
/// </summary>
public int FileCount
{
get
{
return 0;
}
}
#region
/// <summary>
/// 自定义参数:远程服务接口
/// </summary>
public IRemoteServices RemoteServices { private set; get; } = null;
/// <summary>
/// 自定义参数:解密方法类
/// </summary>
public IWebDecryptionServices DecryptionServices { private set; get; }
#endregion
public WechatFileSystem()
{
}
public virtual FSInitializeFileSystemOperation InitializeFileSystemAsync()
{
var operation = new WXFSInitializeOperation(this);
return operation;
}
public virtual FSLoadPackageManifestOperation LoadPackageManifestAsync(string packageVersion, int timeout)
{
var operation = new WXFSLoadPackageManifestOperation(this, packageVersion, timeout);
return operation;
}
public virtual FSRequestPackageVersionOperation RequestPackageVersionAsync(bool appendTimeTicks, int timeout)
{
var operation = new WXFSRequestPackageVersionOperation(this, appendTimeTicks, timeout);
return operation;
}
public virtual FSClearCacheFilesOperation ClearCacheFilesAsync(PackageManifest manifest, string clearMode, object clearParam)
{
if (clearMode == EFileClearMode.ClearAllBundleFiles.ToString())
{
var operation = new WXFSClearAllBundleFilesOperation(this);
return operation;
}
else if (clearMode == EFileClearMode.ClearUnusedBundleFiles.ToString())
{
var operation = new WXFSClearUnusedBundleFilesAsync(this, manifest);
return operation;
}
else
{
string error = $"Invalid clear mode : {clearMode}";
var operation = new FSClearCacheFilesCompleteOperation(error);
return operation;
}
}
public virtual FSDownloadFileOperation DownloadFileAsync(PackageBundle bundle, DownloadParam param)
{
param.MainURL = RemoteServices.GetRemoteMainURL(bundle.FileName);
param.FallbackURL = RemoteServices.GetRemoteFallbackURL(bundle.FileName);
var operation = new WXFSDownloadFileOperation(this, bundle, param);
return operation;
}
public virtual FSLoadBundleOperation LoadBundleFile(PackageBundle bundle)
{
if (bundle.BundleType == (int)EBuildBundleType.AssetBundle)
{
var operation = new WXFSLoadBundleOperation(this, bundle);
return operation;
}
else
{
string error = $"{nameof(WechatFileSystem)} 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
{
YooLogger.Warning($"Invalid parameter : {name}");
}
}
public virtual void OnCreate(string packageName, string packageRoot)
{
PackageName = packageName;
_wxCacheRoot = packageRoot;
if (string.IsNullOrEmpty(_wxCacheRoot))
{
throw new System.Exception("请配置微信小游戏缓存根目录!");
}
if (_wxCacheRoot.StartsWith(WX.PluginCachePath) == false)
{
_wxCacheRoot = PathUtility.Combine(WX.PluginCachePath, _wxCacheRoot);
}
// 注意CDN服务未启用的情况下使用微信WEB服务器
if (RemoteServices == null)
{
string webRoot = PathUtility.Combine(Application.streamingAssetsPath, YooAssetSettingsData.Setting.DefaultYooFolderName, packageName);
RemoteServices = new WebRemoteServices(webRoot);
}
_fileSystemMgr = WX.GetFileSystemManager();
}
public virtual void OnDestroy()
{
}
public virtual bool Belong(PackageBundle bundle)
{
return true;
}
public virtual bool Exists(PackageBundle bundle)
{
string filePath = GetCacheFileLoadPath(bundle);
return CheckCacheFileExist(filePath);
}
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)
{
string filePath = GetCacheFileLoadPath(bundle);
if (CheckCacheFileExist(filePath))
return _fileSystemMgr.ReadFileSync(filePath);
else
return Array.Empty<byte>();
}
public virtual string ReadBundleFileText(PackageBundle bundle)
{
string filePath = GetCacheFileLoadPath(bundle);
if (CheckCacheFileExist(filePath))
return _fileSystemMgr.ReadFileSync(filePath, "utf8");
else
return string.Empty;
}
#region
public WXFileSystemManager GetFileSystemMgr()
{
return _fileSystemMgr;
}
public bool CheckCacheFileExist(string filePath)
{
string result = WX.GetCachePath(filePath);
if (string.IsNullOrEmpty(result))
return false;
else
return true;
}
public string GetCacheFileLoadPath(PackageBundle bundle)
{
if (_cacheFilePathMapping.TryGetValue(bundle.BundleGUID, out string filePath) == false)
{
filePath = PathUtility.Combine(_wxCacheRoot, bundle.FileName);
_cacheFilePathMapping.Add(bundle.BundleGUID, filePath);
}
return filePath;
}
#endregion
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 97f01c24ae799f34393a4362f33a0e1f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f63bfdaa2e1fd3d48b604787fd1a8c99
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,157 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using YooAsset;
/// <summary>
/// 拷贝内置清单文件到沙盒目录
/// </summary>
public class CopyBuildinManifestOperation : GameAsyncOperation
{
private enum ESteps
{
None,
CheckHashFile,
UnpackHashFile,
CheckManifestFile,
UnpackManifestFile,
Done,
}
private readonly string _packageName;
private readonly string _packageVersion;
private ESteps _steps = ESteps.None;
private UnityWebFileRequestOperation _hashFileRequestOp;
private UnityWebFileRequestOperation _manifestFileRequestOp;
public CopyBuildinManifestOperation(string packageName, string packageVersion)
{
_packageName = packageName;
_packageVersion = packageVersion;
}
protected override void OnStart()
{
_steps = ESteps.CheckHashFile;
}
protected override void OnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckHashFile)
{
string hashFilePath = GetCacheHashFilePath();
if (File.Exists(hashFilePath))
{
_steps = ESteps.CheckManifestFile;
return;
}
_steps = ESteps.UnpackHashFile;
}
if (_steps == ESteps.UnpackHashFile)
{
if(_hashFileRequestOp == null)
{
string sourcePath = GetBuildinHashFilePath();
string destPath = GetCacheHashFilePath();
string url = DownloadSystemHelper.ConvertToWWWPath(sourcePath);
_hashFileRequestOp = new UnityWebFileRequestOperation(url, destPath);
OperationSystem.StartOperation(_packageName, _hashFileRequestOp);
}
if (_hashFileRequestOp.IsDone == false)
return;
if (_hashFileRequestOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.CheckManifestFile;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _hashFileRequestOp.Error;
}
}
if (_steps == ESteps.CheckManifestFile)
{
string manifestFilePath = GetCacheManifestFilePath();
if (File.Exists(manifestFilePath))
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
return;
}
_steps = ESteps.UnpackManifestFile;
}
if (_steps == ESteps.UnpackManifestFile)
{
if (_manifestFileRequestOp == null)
{
string sourcePath = GetBuildinManifestFilePath();
string destPath = GetCacheManifestFilePath();
string url = DownloadSystemHelper.ConvertToWWWPath(sourcePath);
_manifestFileRequestOp = new UnityWebFileRequestOperation(url, destPath);
OperationSystem.StartOperation(_packageName, _manifestFileRequestOp);
}
if (_manifestFileRequestOp.IsDone == false)
return;
if (_manifestFileRequestOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _manifestFileRequestOp.Error;
}
}
}
protected override void OnAbort()
{
}
private string GetBuildinYooRoot()
{
return YooAssetSettingsData.GetYooDefaultBuildinRoot();
}
private string GetBuildinHashFilePath()
{
string fileRoot = GetBuildinYooRoot();
string fileName = YooAssetSettingsData.GetPackageHashFileName(_packageName, _packageVersion);
return PathUtility.Combine(fileRoot, _packageName, fileName);
}
private string GetBuildinManifestFilePath()
{
string fileRoot = GetBuildinYooRoot();
string fileName = YooAssetSettingsData.GetManifestBinaryFileName(_packageName, _packageVersion);
return PathUtility.Combine(fileRoot, _packageName, fileName);
}
private string GetCacheYooRoot()
{
return YooAssetSettingsData.GetYooDefaultCacheRoot();
}
private string GetCacheHashFilePath()
{
string fileRoot = GetCacheYooRoot();
string fileName = YooAssetSettingsData.GetPackageHashFileName(_packageName, _packageVersion);
return PathUtility.Combine(fileRoot, _packageName, fileName);
}
private string GetCacheManifestFilePath()
{
string fileRoot = GetCacheYooRoot();
string fileName = YooAssetSettingsData.GetManifestBinaryFileName(_packageName, _packageVersion);
return PathUtility.Combine(fileRoot, _packageName, fileName);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 41ab464f315df234fb40b0c24e97ee23
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using YooAsset;
/// <summary>
/// 获取沙盒目录里缓存文件大小
/// </summary>
public class GetCacheBundleSizeOperation : GameAsyncOperation
{
private enum ESteps
{
None,
GetCacheFiles,
Done,
}
private readonly string _packageName;
private ESteps _steps = ESteps.None;
/// <summary>
/// 总大小(单位:字节)
/// </summary>
public long TotalSize = 0;
public GetCacheBundleSizeOperation(string packageName)
{
_packageName = packageName;
}
protected override void OnStart()
{
_steps = ESteps.GetCacheFiles;
}
protected override void OnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.GetCacheFiles)
{
long totalSize = 0;
string directoryRoot = GetCacheDirectoryRoot();
var directoryInfo = new DirectoryInfo(directoryRoot);
if (directoryInfo.Exists)
{
FileInfo[] fileInfos = directoryInfo.GetFiles("*", SearchOption.AllDirectories);
foreach (FileInfo fileInfo in fileInfos)
{
totalSize += fileInfo.Length;
}
}
TotalSize = totalSize;
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
protected override void OnAbort()
{
}
private string GetCacheDirectoryRoot()
{
string rootDirectory = YooAssetSettingsData.GetYooDefaultCacheRoot();
string packageRoot = PathUtility.Combine(rootDirectory, _packageName);
return PathUtility.Combine(packageRoot, DefaultCacheFileSystemDefine.BundleFilesFolderName);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8790bf5eb17db6843b696018a2b1ce6a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,117 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using YooAsset;
public class LoadAssetsByTagOperation<TObject> : GameAsyncOperation where TObject : UnityEngine.Object
{
private enum ESteps
{
None,
LoadAssets,
CheckResult,
Done,
}
private readonly string _tag;
private ESteps _steps = ESteps.None;
private List<AssetHandle> _handles;
/// <summary>
/// 资源对象集合
/// </summary>
public List<TObject> AssetObjects { private set; get; }
public LoadAssetsByTagOperation(string tag)
{
_tag = tag;
}
protected override void OnStart()
{
_steps = ESteps.LoadAssets;
}
protected override void OnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadAssets)
{
AssetInfo[] assetInfos = YooAssets.GetAssetInfos(_tag);
_handles = new List<AssetHandle>(assetInfos.Length);
foreach (var assetInfo in assetInfos)
{
var handle = YooAssets.LoadAssetAsync(assetInfo);
_handles.Add(handle);
}
_steps = ESteps.CheckResult;
}
if (_steps == ESteps.CheckResult)
{
int index = 0;
foreach (var handle in _handles)
{
if (handle.IsDone == false)
{
Progress = (float)index / _handles.Count;
return;
}
index++;
}
AssetObjects = new List<TObject>(_handles.Count);
foreach (var handle in _handles)
{
if (handle.Status == EOperationStatus.Succeed)
{
var assetObject = handle.AssetObject as TObject;
if (assetObject != null)
{
AssetObjects.Add(assetObject);
}
else
{
string error = $"资源类型转换失败:{handle.AssetObject.name}";
Debug.LogError($"{error}");
AssetObjects.Clear();
SetFinish(false, error);
return;
}
}
else
{
Debug.LogError($"{handle.LastError}");
AssetObjects.Clear();
SetFinish(false, handle.LastError);
return;
}
}
SetFinish(true);
}
}
protected override void OnAbort()
{
}
private void SetFinish(bool succeed, string error = "")
{
Error = error;
Status = succeed ? EOperationStatus.Succeed : EOperationStatus.Failed;
_steps = ESteps.Done;
}
/// <summary>
/// 释放资源句柄
/// </summary>
public void ReleaseHandle()
{
foreach (var handle in _handles)
{
handle.Release();
}
_handles.Clear();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 35cd25a83a43dfb4093399183b31a0d0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,102 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using YooAsset;
public static class YooAssetsExtension
{
public static LoadGameObjectOperation LoadGameObjectAsync(this ResourcePackage resourcePackage, string location, Vector3 position, Quaternion rotation, Transform parent, bool destroyGoOnRelease = false)
{
var operation = new LoadGameObjectOperation(location, position, rotation, parent, destroyGoOnRelease);
YooAssets.StartOperation(operation);
return operation;
}
}
public class LoadGameObjectOperation : GameAsyncOperation
{
private enum ESteps
{
None,
LoadAsset,
Done,
}
private readonly string _location;
private readonly Vector3 _positon;
private readonly Quaternion _rotation;
private readonly Transform _parent;
private readonly bool _destroyGoOnRelease;
private AssetHandle _handle;
private ESteps _steps = ESteps.None;
/// <summary>
/// 加载的游戏对象
/// </summary>
public GameObject Go { private set; get; }
public LoadGameObjectOperation(string location, Vector3 position, Quaternion rotation, Transform parent, bool destroyGoOnRelease = false)
{
_location = location;
_positon = position;
_rotation = rotation;
_parent = parent;
_destroyGoOnRelease = destroyGoOnRelease;
}
protected override void OnStart()
{
_steps = ESteps.LoadAsset;
}
protected override void OnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadAsset)
{
if (_handle == null)
{
_handle = YooAssets.LoadAssetAsync<GameObject>(_location);
}
Progress = _handle.Progress;
if (_handle.IsDone == false)
return;
if (_handle.Status != EOperationStatus.Succeed)
{
Error = _handle.LastError;
Status = EOperationStatus.Failed;
_steps = ESteps.Done;
}
else
{
Go = _handle.InstantiateSync(_positon, _rotation, _parent);
Status = EOperationStatus.Succeed;
_steps = ESteps.Done;
}
}
}
protected override void OnAbort()
{
}
/// <summary>
/// 释放资源句柄
/// </summary>
public void ReleaseHandle()
{
if (_handle != null)
{
_handle.Release();
if (_destroyGoOnRelease)
{
if (Go != null)
GameObject.Destroy(Go);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 55478908ad5dc5541af1529a3faccee7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,73 @@
using UnityEngine;
using YooAsset;
#if UNITY_EDITOR
[UnityEditor.CustomEditor(typeof(GameObjectAssetReference), true)]
public class GameObjectAssetReferenceInspector : UnityEditor.Editor
{
private bool _init = false;
private GameObject _cacheObject;
public override void OnInspectorGUI()
{
GameObjectAssetReference mono = (GameObjectAssetReference)target;
if (_init == false)
{
_init = true;
if (string.IsNullOrEmpty(mono.AssetGUID) == false)
{
string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(mono.AssetGUID);
if (string.IsNullOrEmpty(assetPath) == false)
{
_cacheObject = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
}
}
}
GameObject go = (GameObject)UnityEditor.EditorGUILayout.ObjectField(_cacheObject, typeof(GameObject), false);
if (go != _cacheObject)
{
_cacheObject = go;
string assetPath = UnityEditor.AssetDatabase.GetAssetPath(go);
mono.AssetGUID = UnityEditor.AssetDatabase.AssetPathToGUID(assetPath);
UnityEditor.EditorUtility.SetDirty(target);
}
UnityEditor.EditorGUILayout.LabelField("Asset GUID", mono.AssetGUID);
}
}
#endif
public class GameObjectAssetReference : MonoBehaviour
{
[HideInInspector]
public string AssetGUID = "";
private AssetHandle _handle;
public void Start()
{
var package = YooAssets.GetPackage("DefaultPackage");
var assetInfo = package.GetAssetInfoByGUID(AssetGUID);
_handle = package.LoadAssetAsync(assetInfo);
_handle.Completed += Handle_Completed;
}
public void OnDestroy()
{
if (_handle != null)
{
_handle.Release();
_handle = null;
}
}
private void Handle_Completed(AssetHandle handle)
{
if (handle.Status == EOperationStatus.Succeed)
{
handle.InstantiateSync(this.transform);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1534f1a1b207ad542bf1fc73da8b4316
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
{
"name": "YooAsset.RuntimeExtension",
"rootNamespace": "",
"references": [
"GUID:e34a5702dd353724aa315fb8011f08c3",
"GUID:5efd170ecd8084500bed5692932fe14e",
"GUID:bb21d6197862c4c3e863390dec9859a7"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: d7fc4d49301802c4d997d63a24928ab3
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
<linker>
<assembly fullname="YooAsset.RuntimeExtension" preserve="all"/>
</linker>

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: ae37d193392b87649b9b6fa7c9628e7e
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: