update extension sample

pull/326/head
何冠峰 2024-07-07 00:52:50 +08:00
parent bafd15571a
commit b282515c39
22 changed files with 976 additions and 296 deletions

View File

@ -1,296 +0,0 @@
#if UNITY_WECHAT_GAME
using System;
using System.IO;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using YooAsset;
using WeChatWASM;
public static class WechatFileSystemCreater
{
public static FileSystemParameters CreateWechatFileSystemParameters(IRemoteServices remoteServices)
{
string fileSystemClass = $"{nameof(WechatFileSystem)},YooAsset.RuntimeExtension";
var fileSystemParams = new FileSystemParameters(fileSystemClass, null);
fileSystemParams.AddParameter("REMOTE_SERVICES", remoteServices);
fileSystemParams.AddParameter("DISABLE_UNITY_WEB_CACHE", true);
fileSystemParams.AddParameter("ALLOW_CROSS_ACCESS", true);
return fileSystemParams;
}
}
/// <summary>
/// 微信小游戏文件系统扩展
/// 参考https://wechat-miniprogram.github.io/minigame-unity-webgl-transform/Design/UsingAssetBundle.html
/// </summary>
internal partial class WechatFileSystem : DefaultWebFileSystem
{
private WXFileSystemManager _wxFileSystemMgr;
private readonly Dictionary<string, string> _wxFilePaths = new Dictionary<string, string>(10000);
private string _wxFileCacheRoot = string.Empty;
public override void OnCreate(string packageName, string rootDirectory)
{
base.OnCreate(packageName, rootDirectory);
_wxFileSystemMgr = WX.GetFileSystemManager();
_wxFileCacheRoot = WX.env.USER_DATA_PATH; //注意:如果有子目录,请修改此处!
}
/// <summary>
/// 重写资源文件下载方法
/// </summary>
public override FSDownloadFileOperation DownloadFileAsync(params object[] args)
{
PackageBundle bundle = args[0] as PackageBundle;
int failedTryAgain = (int)args[2];
int timeout = (int)args[3];
string mainURL = RemoteServices.GetRemoteMainURL(bundle.FileName);
string fallbackURL = RemoteServices.GetRemoteFallbackURL(bundle.FileName);
var operation = new WechatDownloadFileOperation(this, bundle, mainURL, fallbackURL, failedTryAgain, timeout);
OperationSystem.StartOperation(PackageName, operation);
return operation;
}
/// <summary>
/// 重写资源文件加载方法
/// </summary>
public override FSLoadBundleOperation LoadBundleFile(PackageBundle bundle)
{
var operation = new WechatLoadBundleOperation(this, bundle);
OperationSystem.StartOperation(PackageName, operation);
return operation;
}
/// <summary>
/// 重写资源文件卸载方法
/// </summary>
public override void UnloadBundleFile(PackageBundle bundle, object result)
{
AssetBundle assetBundle = result as AssetBundle;
if (assetBundle != null)
assetBundle.WXUnload(true);
}
/// <summary>
/// 重写查询方法
/// </summary>
public override bool Exists(PackageBundle bundle)
{
string filePath = GetWXFileLoadPath(bundle);
string result = _wxFileSystemMgr.AccessSync(filePath);
return result.Equals("access:ok");
}
#region 内部方法
private string GetWXFileLoadPath(PackageBundle bundle)
{
if (_wxFilePaths.TryGetValue(bundle.BundleGUID, out string filePath) == false)
{
filePath = PathUtility.Combine(_wxFileCacheRoot, bundle.FileName);
_wxFilePaths.Add(bundle.BundleGUID, filePath);
}
return filePath;
}
#endregion
}
internal partial class WechatFileSystem
{
internal class WechatLoadBundleOperation : FSLoadBundleOperation
{
private enum ESteps
{
None,
LoadBundleFile,
Done,
}
private readonly WechatFileSystem _fileSystem;
private readonly PackageBundle _bundle;
private UnityWebRequest _webRequest;
private ESteps _steps = ESteps.None;
internal WechatLoadBundleOperation(WechatFileSystem fileSystem, PackageBundle bundle)
{
_fileSystem = fileSystem;
_bundle = bundle;
}
internal override void InternalOnStart()
{
_steps = ESteps.LoadBundleFile;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadBundleFile)
{
if (_webRequest == null)
{
string mainURL = _fileSystem.RemoteServices.GetRemoteMainURL(_bundle.FileName);
_webRequest = WXAssetBundle.GetAssetBundle(mainURL);
_webRequest.SendWebRequest();
}
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes;
Progress = DownloadProgress;
if (_webRequest.isDone == false)
return;
if (CheckRequestResult())
{
_steps = ESteps.Done;
Result = (_webRequest.downloadHandler as DownloadHandlerWXAssetBundle).assetBundle;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
}
}
}
public override void WaitForAsyncComplete()
{
if (IsDone == false)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "WebGL platform not support sync load method !";
UnityEngine.Debug.LogError(Error);
}
}
public override void AbortDownloadOperation()
{
}
private bool CheckRequestResult()
{
#if UNITY_2020_3_OR_NEWER
if (_webRequest.result != UnityWebRequest.Result.Success)
{
Error = _webRequest.error;
return false;
}
else
{
return true;
}
#else
if (_webRequest.isNetworkError || _webRequest.isHttpError)
{
Error = _webRequest.error;
return false;
}
else
{
return true;
}
#endif
}
}
internal class WechatDownloadFileOperation : DefaultDownloadFileOperation
{
private WechatFileSystem _fileSystem;
private ESteps _steps = ESteps.None;
internal WechatDownloadFileOperation(WechatFileSystem fileSystem, PackageBundle bundle,
string mainURL, string fallbackURL, int failedTryAgain, int timeout)
: base(bundle, mainURL, fallbackURL, failedTryAgain, timeout)
{
_fileSystem = fileSystem;
}
internal override void InternalOnStart()
{
_steps = ESteps.CreateRequest;
}
internal override void InternalOnUpdate()
{
// 创建下载器
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()
{
_webRequest = WXAssetBundle.GetAssetBundle(_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,8 @@
fileFormatVersion: 2
guid: f0165bf1a62988a439ce3a00db043620
folderAsset: yes
DefaultImporter:
externalObjects: {}
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,102 @@
#if UNITY_WECHAT_GAME
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 InternalOnStart()
{
_steps = ESteps.CreateRequest;
}
internal override void InternalOnUpdate()
{
// 创建下载器
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()
{
_webRequest = WXAssetBundle.GetAssetBundle(_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_WECHAT_GAME
using YooAsset;
internal partial class WXFSInitializeOperation : FSInitializeFileSystemOperation
{
private readonly WechatFileSystem _fileSystem;
public WXFSInitializeOperation(WechatFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
internal override void InternalOnStart()
{
Status = EOperationStatus.Succeed;
}
internal override void InternalOnUpdate()
{
}
}
#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,103 @@
#if UNITY_WECHAT_GAME
using UnityEngine;
using UnityEngine.Networking;
using YooAsset;
using WeChatWASM;
internal class WXFSLoadBundleOperation : FSLoadBundleOperation
{
private enum ESteps
{
None,
LoadBundleFile,
Done,
}
private readonly WechatFileSystem _fileSystem;
private readonly PackageBundle _bundle;
private UnityWebRequest _webRequest;
private ESteps _steps = ESteps.None;
internal WXFSLoadBundleOperation(WechatFileSystem fileSystem, PackageBundle bundle)
{
_fileSystem = fileSystem;
_bundle = bundle;
}
internal override void InternalOnStart()
{
_steps = ESteps.LoadBundleFile;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadBundleFile)
{
if (_webRequest == null)
{
string mainURL = _fileSystem.RemoteServices.GetRemoteMainURL(_bundle.FileName);
_webRequest = WXAssetBundle.GetAssetBundle(mainURL);
_webRequest.SendWebRequest();
}
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes;
Progress = DownloadProgress;
if (_webRequest.isDone == false)
return;
if (CheckRequestResult())
{
_steps = ESteps.Done;
Result = (_webRequest.downloadHandler as DownloadHandlerWXAssetBundle).assetBundle;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
}
}
}
public override void WaitForAsyncComplete()
{
if (IsDone == false)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "WebGL platform not support sync load method !";
UnityEngine.Debug.LogError(Error);
}
}
public override void AbortDownloadOperation()
{
}
private bool CheckRequestResult()
{
#if UNITY_2020_3_OR_NEWER
if (_webRequest.result != UnityWebRequest.Result.Success)
{
Error = _webRequest.error;
return false;
}
else
{
return true;
}
#else
if (_webRequest.isNetworkError || _webRequest.isHttpError)
{
Error = _webRequest.error;
return false;
}
else
{
return true;
}
#endif
}
}
#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,88 @@
#if UNITY_WECHAT_GAME
using YooAsset;
internal class WXFSLoadPackageManifestOperation : FSLoadPackageManifestOperation
{
private enum ESteps
{
None,
RequestRemotePackageHash,
LoadRemotePackageManifest,
Done,
}
private readonly WechatFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly int _timeout;
private RequestWechatPackageHashOperation _requestRemotePackageHashOp;
private LoadWechatPackageManifestOperation _loadRemotePackageManifestOp;
private ESteps _steps = ESteps.None;
public WXFSLoadPackageManifestOperation(WechatFileSystem fileSystem, string packageVersion, int timeout)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
_timeout = timeout;
}
internal override void InternalOnStart()
{
_steps = ESteps.RequestRemotePackageHash;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.RequestRemotePackageHash)
{
if (_requestRemotePackageHashOp == null)
{
_requestRemotePackageHashOp = new RequestWechatPackageHashOperation(_fileSystem, _packageVersion, _timeout);
OperationSystem.StartOperation(_fileSystem.PackageName, _requestRemotePackageHashOp);
}
if (_requestRemotePackageHashOp.IsDone == false)
return;
if (_requestRemotePackageHashOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.LoadRemotePackageManifest;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _requestRemotePackageHashOp.Error;
}
}
if (_steps == ESteps.LoadRemotePackageManifest)
{
if (_loadRemotePackageManifestOp == null)
{
string packageHash = _requestRemotePackageHashOp.PackageHash;
_loadRemotePackageManifestOp = new LoadWechatPackageManifestOperation(_fileSystem, _packageVersion, packageHash, _timeout);
OperationSystem.StartOperation(_fileSystem.PackageName, _loadRemotePackageManifestOp);
}
Progress = _loadRemotePackageManifestOp.Progress;
if (_loadRemotePackageManifestOp.IsDone == false)
return;
if (_loadRemotePackageManifestOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.Done;
Manifest = _loadRemotePackageManifestOp.Manifest;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _loadRemotePackageManifestOp.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,60 @@
#if UNITY_WECHAT_GAME
using YooAsset;
internal class WXFSRequestPackageVersionOperation : FSRequestPackageVersionOperation
{
private enum ESteps
{
None,
RequestPackageVersion,
Done,
}
private readonly WechatFileSystem _fileSystem;
private readonly int _timeout;
private RequestWechatPackageVersionOperation _requestWebPackageVersionOp;
private ESteps _steps = ESteps.None;
internal WXFSRequestPackageVersionOperation(WechatFileSystem fileSystem, int timeout)
{
_fileSystem = fileSystem;
_timeout = timeout;
}
internal override void InternalOnStart()
{
_steps = ESteps.RequestPackageVersion;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.RequestPackageVersion)
{
if (_requestWebPackageVersionOp == null)
{
_requestWebPackageVersionOp = new RequestWechatPackageVersionOperation(_fileSystem, _timeout);
OperationSystem.StartOperation(_fileSystem.PackageName, _requestWebPackageVersionOp);
}
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,125 @@
#if UNITY_WECHAT_GAME
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 InternalOnStart()
{
_requestCount = WebRequestCounter.GetRequestFailedCount(_fileSystem.PackageName, nameof(LoadWechatPackageManifestOperation));
_steps = ESteps.RequestFileData;
}
internal override void InternalOnUpdate()
{
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);
OperationSystem.StartOperation(_fileSystem.PackageName, _webDataRequestOp);
}
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.BytesMD5(_webDataRequestOp.Result);
if (fileHash == _packageHash)
{
_steps = ESteps.LoadManifest;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Failed to verify wechat package manifest file!";
}
}
if (_steps == ESteps.LoadManifest)
{
if (_deserializer == null)
{
_deserializer = new DeserializeManifestOperation(_webDataRequestOp.Result);
OperationSystem.StartOperation(_fileSystem.PackageName, _deserializer);
}
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,90 @@
#if UNITY_WECHAT_GAME
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 InternalOnStart()
{
_requestCount = WebRequestCounter.GetRequestFailedCount(_fileSystem.PackageName, nameof(RequestWechatPackageHashOperation));
_steps = ESteps.RequestPackageHash;
}
internal override void InternalOnUpdate()
{
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);
OperationSystem.StartOperation(_fileSystem.PackageName, _webTextRequestOp);
}
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,88 @@
#if UNITY_WECHAT_GAME
using YooAsset;
internal class RequestWechatPackageVersionOperation : AsyncOperationBase
{
private enum ESteps
{
None,
RequestPackageVersion,
Done,
}
private readonly WechatFileSystem _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 RequestWechatPackageVersionOperation(WechatFileSystem fileSystem, int timeout)
{
_fileSystem = fileSystem;
_timeout = timeout;
}
internal override void InternalOnStart()
{
_requestCount = WebRequestCounter.GetRequestFailedCount(_fileSystem.PackageName, nameof(RequestWechatPackageVersionOperation));
_steps = ESteps.RequestPackageVersion;
}
internal override void InternalOnUpdate()
{
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);
OperationSystem.StartOperation(_fileSystem.PackageName, _webTextRequestOp);
}
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)
{
// 轮流返回请求地址
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: 758fbcd91469b3f4e843a9ea7e3deda9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,188 @@
#if UNITY_WECHAT_GAME
using System.Collections.Generic;
using UnityEngine;
using YooAsset;
using WeChatWASM;
public static class WechatFileSystemCreater
{
public static FileSystemParameters CreateWechatFileSystemParameters(IRemoteServices remoteServices)
{
string fileSystemClass = $"{nameof(WechatFileSystem)},YooAsset.RuntimeExtension";
var fileSystemParams = new FileSystemParameters(fileSystemClass, null);
fileSystemParams.AddParameter("REMOTE_SERVICES", remoteServices);
return fileSystemParams;
}
}
/// <summary>
/// 微信小游戏文件系统
/// 参考https://wechat-miniprogram.github.io/minigame-unity-webgl-transform/Design/UsingAssetBundle.html
/// </summary>
internal class WechatFileSystem : IFileSystem
{
private readonly Dictionary<string, string> _wxFilePaths = new Dictionary<string, string>(10000);
private WXFileSystemManager _wxFileSystemMgr;
private string _wxFileCacheRoot = string.Empty;
/// <summary>
/// 包裹名称
/// </summary>
public string PackageName { private set; get; }
/// <summary>
/// 文件访问权限
/// </summary>
public EFileAccess FileSystemAccess
{
get
{
return EFileAccess.ReadWrite;
}
}
/// <summary>
/// 文件根目录
/// </summary>
public string FileRoot
{
get
{
return _wxFileCacheRoot;
}
}
/// <summary>
/// 文件数量
/// </summary>
public int FileCount
{
get
{
return 0;
}
}
#region 自定义参数
/// <summary>
/// 自定义参数:远程服务接口
/// </summary>
public IRemoteServices RemoteServices { private set; get; } = null;
#endregion
public WechatFileSystem()
{
}
public virtual FSInitializeFileSystemOperation InitializeFileSystemAsync()
{
var operation = new WXFSInitializeOperation(this);
OperationSystem.StartOperation(PackageName, operation);
return operation;
}
public virtual FSLoadPackageManifestOperation LoadPackageManifestAsync(string packageVersion, int timeout)
{
var operation = new WXFSLoadPackageManifestOperation(this, packageVersion, timeout);
OperationSystem.StartOperation(PackageName, operation);
return operation;
}
public virtual FSRequestPackageVersionOperation RequestPackageVersionAsync(bool appendTimeTicks, int timeout)
{
var operation = new WXFSRequestPackageVersionOperation(this, timeout);
OperationSystem.StartOperation(PackageName, operation);
return operation;
}
public virtual FSClearAllBundleFilesOperation ClearAllBundleFilesAsync()
{
var operation = new FSClearAllBundleFilesCompleteOperation();
OperationSystem.StartOperation(PackageName, operation);
return operation;
}
public virtual FSClearUnusedBundleFilesOperation ClearUnusedBundleFilesAsync(PackageManifest manifest)
{
var operation = new FSClearUnusedBundleFilesCompleteOperation();
OperationSystem.StartOperation(PackageName, operation);
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);
OperationSystem.StartOperation(PackageName, operation);
return operation;
}
public virtual FSLoadBundleOperation LoadBundleFile(PackageBundle bundle)
{
var operation = new WXFSLoadBundleOperation(this, bundle);
OperationSystem.StartOperation(PackageName, operation);
return operation;
}
public virtual void UnloadBundleFile(PackageBundle bundle, object result)
{
AssetBundle assetBundle = result as AssetBundle;
if (assetBundle != null)
assetBundle.WXUnload(true);
}
public virtual void SetParameter(string name, object value)
{
if (name == "REMOTE_SERVICES")
{
RemoteServices = (IRemoteServices)value;
}
else
{
YooLogger.Warning($"Invalid parameter : {name}");
}
}
public virtual void OnCreate(string packageName, string rootDirectory)
{
PackageName = packageName;
_wxFileSystemMgr = WX.GetFileSystemManager();
_wxFileCacheRoot = WX.env.USER_DATA_PATH; //注意:如果有子目录,请修改此处!
}
public virtual void OnUpdate()
{
}
public virtual bool Belong(PackageBundle bundle)
{
return true;
}
public virtual bool Exists(PackageBundle bundle)
{
string filePath = GetWXFileLoadPath(bundle);
string result = _wxFileSystemMgr.AccessSync(filePath);
return result.Equals("access:ok");
}
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;
}
#region 内部方法
private string GetWXFileLoadPath(PackageBundle bundle)
{
if (_wxFilePaths.TryGetValue(bundle.BundleGUID, out string filePath) == false)
{
filePath = PathUtility.Combine(_wxFileCacheRoot, bundle.FileName);
_wxFilePaths.Add(bundle.BundleGUID, filePath);
}
return filePath;
}
#endregion
}
#endif