aphla 测试 1
Some checks failed
Plugin Library CI / publish (00.BuildOriginality) (push) Successful in 4s
Plugin Library CI / publish (00.StaryEvo) (push) Successful in 4s
Plugin Library CI / publish (00.StaryEvoTools) (push) Failing after 10s
Plugin Library CI / publish (01.HybridCLR) (push) Successful in 4s
Plugin Library CI / publish (02.InformationSave) (push) Successful in 3s
Plugin Library CI / publish (03.YooAsset) (push) Successful in 32s
Plugin Library CI / publish (04.AudioCore) (push) Successful in 2s
Plugin Library CI / publish (05.TableTextConversion) (push) Successful in 3s
Plugin Library CI / publish (06.UIFarme) (push) Successful in 16s
Plugin Library CI / publish (07.RKTools) (push) Successful in 3s
Plugin Library CI / publish (08.UniTask) (push) Successful in 3s
Plugin Library CI / publish (09.CodeChecker) (push) Successful in 16s
Plugin Library CI / publish (11.PointCloudTools) (push) Successful in 2s
Plugin Library CI / publish (10.StoryEditor) (push) Successful in 3s
Plugin Library CI / publish (10.XNode) (push) Successful in 3s
Some checks failed
Plugin Library CI / publish (00.BuildOriginality) (push) Successful in 4s
Plugin Library CI / publish (00.StaryEvo) (push) Successful in 4s
Plugin Library CI / publish (00.StaryEvoTools) (push) Failing after 10s
Plugin Library CI / publish (01.HybridCLR) (push) Successful in 4s
Plugin Library CI / publish (02.InformationSave) (push) Successful in 3s
Plugin Library CI / publish (03.YooAsset) (push) Successful in 32s
Plugin Library CI / publish (04.AudioCore) (push) Successful in 2s
Plugin Library CI / publish (05.TableTextConversion) (push) Successful in 3s
Plugin Library CI / publish (06.UIFarme) (push) Successful in 16s
Plugin Library CI / publish (07.RKTools) (push) Successful in 3s
Plugin Library CI / publish (08.UniTask) (push) Successful in 3s
Plugin Library CI / publish (09.CodeChecker) (push) Successful in 16s
Plugin Library CI / publish (11.PointCloudTools) (push) Successful in 2s
Plugin Library CI / publish (10.StoryEditor) (push) Successful in 3s
Plugin Library CI / publish (10.XNode) (push) Successful in 3s
This commit is contained in:
@@ -15,70 +15,168 @@ namespace Stary.Evo.Editor
|
||||
|
||||
private static PLayerMode _pLayerMode;
|
||||
|
||||
// 主模式菜单路径
|
||||
private const string EditorSimulateMode = "Evo/Schema/ChangePlayer/EditorSimulateMode(编辑器调试模式)";
|
||||
private const string HostPlayMode = "Evo/Schema/ChangePlayer/HostPlayMode(联机运行模式)";
|
||||
private const string LocalPlayMode = "Evo/Schema/ChangePlayer/LocalPlayMode(本地运行模式)";
|
||||
private const string WebPlayMode = "Evo/Schema/ChangePlayer/WebPlayMode(Web运行模式)";
|
||||
|
||||
// Web 子模式菜单路径(作为 WebPlayMode 的子菜单)
|
||||
private const string WebNormalSubMode = "Evo/Schema/ChangePlayer/WebPlayMode(Web运行模式)/WebGL 普通模式";
|
||||
private const string WechatMiniSubMode = "Evo/Schema/ChangePlayer/WebPlayMode(Web运行模式)/微信小程序模式";
|
||||
|
||||
[MenuItem(EditorSimulateMode, false,3)]
|
||||
#region 主模式菜单
|
||||
|
||||
[MenuItem(EditorSimulateMode, false, 3)]
|
||||
private static void SetEditorMode() => SetPlayerMode(PLayerMode.EDITOR_SIMULATEMODE);
|
||||
[MenuItem(HostPlayMode, false,3)]
|
||||
|
||||
[MenuItem(HostPlayMode, false, 3)]
|
||||
private static void SetHostMode() => SetPlayerMode(PLayerMode.HOST_PLAYMODE);
|
||||
[MenuItem(LocalPlayMode, false,3)]
|
||||
|
||||
[MenuItem(LocalPlayMode, false, 3)]
|
||||
private static void SetLocalMode() => SetPlayerMode(PLayerMode.LOCAL_PLAYMODE);
|
||||
// [MenuItem(WebPlayMode)]
|
||||
// private static void SetWebMode() => SetPlayerMode(PLayerMode.WEB_PLAYMODE);
|
||||
|
||||
|
||||
|
||||
[MenuItem(EditorSimulateMode, true,3)]
|
||||
private static bool ValidateModeMenu()
|
||||
#endregion
|
||||
|
||||
#region Web 子模式菜单
|
||||
|
||||
[MenuItem(WebNormalSubMode, false, 4)]
|
||||
private static void SetWebNormalMode() => SetWebSubMode(WebSubMode.WEB_NORMAL);
|
||||
|
||||
[MenuItem(WechatMiniSubMode, false, 4)]
|
||||
private static void SetWechatMiniMode() => SetWebSubMode(WebSubMode.WECHAT_MINIGAME);
|
||||
|
||||
#endregion
|
||||
|
||||
#region 菜单验证
|
||||
|
||||
[MenuItem(EditorSimulateMode, true, 3)]
|
||||
[MenuItem(HostPlayMode, true, 3)]
|
||||
[MenuItem(LocalPlayMode, true, 3)]
|
||||
private static bool ValidateMainModeMenu()
|
||||
{
|
||||
string platform = CustomEditorPrefs.GetString("ChangePlayerSchema");
|
||||
Menu.SetChecked(EditorSimulateMode, platform == PLayerMode.EDITOR_SIMULATEMODE.ToString());
|
||||
Menu.SetChecked(HostPlayMode, platform == PLayerMode.HOST_PLAYMODE.ToString());
|
||||
Menu.SetChecked(LocalPlayMode, platform == PLayerMode.LOCAL_PLAYMODE.ToString());
|
||||
//Menu.SetChecked(WebPlayMode, platform == PLayerMode.WEB_PLAYMODE.ToString());
|
||||
Debug.Log($"ChangePlayerSchema:{platform}");
|
||||
return true;
|
||||
}
|
||||
|
||||
[MenuItem(WebNormalSubMode, true)]
|
||||
private static bool ValidateWebNormalMenu()
|
||||
{
|
||||
// 只在 WebPlayMode 下启用子菜单
|
||||
bool isWebMode = _pLayerMode == PLayerMode.WEB_PLAYMODE;
|
||||
string subMode = CustomEditorPrefs.GetString("WebSubMode", WebSubMode.WEB_NORMAL.ToString());
|
||||
Menu.SetChecked(WebNormalSubMode, subMode == WebSubMode.WEB_NORMAL.ToString());
|
||||
return isWebMode;
|
||||
}
|
||||
|
||||
[MenuItem(WechatMiniSubMode, true)]
|
||||
private static bool ValidateWechatMiniMenu()
|
||||
{
|
||||
bool isWebMode = _pLayerMode == PLayerMode.WEB_PLAYMODE;
|
||||
string subMode = CustomEditorPrefs.GetString("WebSubMode", WebSubMode.WEB_NORMAL.ToString());
|
||||
Menu.SetChecked(WechatMiniSubMode, subMode == WebSubMode.WECHAT_MINIGAME.ToString());
|
||||
return isWebMode;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static void SetPlayerMode(PLayerMode mode)
|
||||
{
|
||||
// 清除所有旧模式定义
|
||||
var currentTarget = EditorUserBuildSettings.selectedBuildTargetGroup;
|
||||
if (currentTarget == BuildTargetGroup.Unknown) return;
|
||||
|
||||
// 获取当前宏定义并清理所有相关的模式定义(包括主模式和子模式)
|
||||
var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(currentTarget)
|
||||
.Split(';')
|
||||
.Where(d => !string.IsNullOrEmpty(d))
|
||||
.Where(d => !Enum.GetNames(typeof(PLayerMode)).Contains(d))
|
||||
.Where(d => !Enum.GetNames(typeof(WebSubMode)).Contains(d))
|
||||
.ToList();
|
||||
|
||||
// 添加新模式
|
||||
defines.Add(mode.ToString());
|
||||
PlayerSettings.SetScriptingDefineSymbolsForGroup(currentTarget, string.Join(";", defines));
|
||||
|
||||
_pLayerMode = mode;
|
||||
CustomEditorPrefs.SetString("ChangePlayerSchema", _pLayerMode.ToString());
|
||||
ValidateModeMenu();
|
||||
|
||||
// 如果切换到 Web 模式,恢复之前保存的子模式或默认使用普通模式
|
||||
if (mode == PLayerMode.WEB_PLAYMODE)
|
||||
{
|
||||
string savedSubMode = CustomEditorPrefs.GetString("WebSubMode", WebSubMode.WEB_NORMAL.ToString());
|
||||
defines.Add(savedSubMode);
|
||||
}
|
||||
|
||||
ApplyDefines(defines);
|
||||
Debug.Log($"[ChangePlayerSchema] 切换至主模式: {mode}");
|
||||
}
|
||||
|
||||
public static void SetWebSubMode(WebSubMode subMode)
|
||||
{
|
||||
if (_pLayerMode != PLayerMode.WEB_PLAYMODE)
|
||||
{
|
||||
Debug.LogWarning("[ChangePlayerSchema] 请先切换到 WebPlayMode 再设置子模式");
|
||||
return;
|
||||
}
|
||||
|
||||
var currentTarget = EditorUserBuildSettings.selectedBuildTargetGroup;
|
||||
|
||||
// 清理现有子模式宏,保留其他所有宏
|
||||
var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(currentTarget)
|
||||
.Split(';')
|
||||
.Where(d => !string.IsNullOrEmpty(d))
|
||||
.Where(d => !Enum.GetNames(typeof(WebSubMode)).Contains(d))
|
||||
.ToList();
|
||||
|
||||
// 添加新的子模式宏
|
||||
defines.Add(subMode.ToString());
|
||||
CustomEditorPrefs.SetString("WebSubMode", subMode.ToString());
|
||||
|
||||
ApplyDefines(defines);
|
||||
Debug.Log($"[ChangePlayerSchema] Web 子模式切换至: {subMode}");
|
||||
}
|
||||
|
||||
private static void ApplyDefines(System.Collections.Generic.List<string> defines)
|
||||
{
|
||||
var currentTarget = EditorUserBuildSettings.selectedBuildTargetGroup;
|
||||
PlayerSettings.SetScriptingDefineSymbolsForGroup(currentTarget, string.Join(";", defines));
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
// 添加解决方案文件重新生成逻辑
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
//EditorApplication.ExecuteMenuItem("Assets/Open C# Project");
|
||||
UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
|
||||
Debug.Log($"当前编译符号: {string.Join(";", defines)}"); // 添加调试日志
|
||||
Debug.Log($"[ChangePlayerSchema] 当前编译符号: {string.Join(";", defines)}");
|
||||
};
|
||||
}
|
||||
|
||||
// 初始化时恢复上次保存的模式(可在 Editor 初始化时调用)
|
||||
[InitializeOnLoadMethod]
|
||||
private static void RestoreMode()
|
||||
{
|
||||
string savedMode = CustomEditorPrefs.GetString("ChangePlayerSchema", PLayerMode.EDITOR_SIMULATEMODE.ToString());
|
||||
if (Enum.TryParse<PLayerMode>(savedMode, out var mode))
|
||||
{
|
||||
_pLayerMode = mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 主模式枚举
|
||||
public enum PLayerMode
|
||||
{
|
||||
//编辑仿真模式,
|
||||
EDITOR_SIMULATEMODE,
|
||||
|
||||
//联机运行模式
|
||||
HOST_PLAYMODE,
|
||||
//本地运行模式
|
||||
LOCAL_PLAYMODE,
|
||||
// //web运行模式
|
||||
// WEB_PLAYMODE
|
||||
EDITOR_SIMULATEMODE, // 编辑器调试模式
|
||||
HOST_PLAYMODE, // 联机运行模式
|
||||
LOCAL_PLAYMODE, // 本地运行模式
|
||||
WEB_PLAYMODE // Web 运行模式(包含子模式)
|
||||
}
|
||||
|
||||
// Web 子模式枚举
|
||||
public enum WebSubMode
|
||||
{
|
||||
WEB_NORMAL, // WebGL 普通模式
|
||||
WECHAT_MINIGAME // 微信小程序模式
|
||||
}
|
||||
}
|
||||
8
Assets/00.StaryEvoTools/Plugins.meta
Normal file
8
Assets/00.StaryEvoTools/Plugins.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2dc17bc4d9faa2a41ab9270d35fee250
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/00.StaryEvoTools/Plugins/ICSharpCode.SharpZipLib.dll
Normal file
BIN
Assets/00.StaryEvoTools/Plugins/ICSharpCode.SharpZipLib.dll
Normal file
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92cce7f762d252b40853ec8cfca85b7c
|
||||
timeCreated: 1453461718
|
||||
licenseType: Pro
|
||||
PluginImporter:
|
||||
serializedVersion: 1
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
isPreloaded: 0
|
||||
platformData:
|
||||
Any:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
Editor:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
WindowsStoreApps:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -77,23 +77,23 @@ namespace Stary.Evo
|
||||
if (isInitSuccess)
|
||||
await UpdateLocalPackage(packageRawFile);
|
||||
await LoadHotfixSettings();
|
||||
#elif WEB_PLAYMODE
|
||||
// IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
|
||||
// var webServerFileSystemParams = FileSystemParameters.CreateDefaultWebServerFileSystemParameters();
|
||||
// var webRemoteFileSystemParams =
|
||||
// FileSystemParameters.CreateDefaultWebRemoteFileSystemParameters(remoteServices); //支持跨域下载
|
||||
//
|
||||
// var initParameters = new WebPlayModeParameters();
|
||||
// initParameters.WebServerFileSystemParameters = webServerFileSystemParams;
|
||||
// initParameters.WebRemoteFileSystemParameters = webRemoteFileSystemParams;
|
||||
//
|
||||
// var initOperation = package.InitializeAsync(initParameters);
|
||||
// await initOperation;
|
||||
//
|
||||
// if (initOperation.Status == EOperationStatus.Succeed)
|
||||
// Debug.Log("UnityEvo:资源包初始化成功!");
|
||||
// else
|
||||
// Debug.LogError($"UnityEvo:资源包初始化失败:{initOperation.Error}");
|
||||
#elif WEB_NORMAL
|
||||
IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
|
||||
var webServerFileSystemParams = FileSystemParameters.CreateDefaultWebServerFileSystemParameters();
|
||||
var webRemoteFileSystemParams =
|
||||
FileSystemParameters.CreateDefaultWebRemoteFileSystemParameters(remoteServices); //支持跨域下载
|
||||
|
||||
var initParameters = new WebPlayModeParameters();
|
||||
initParameters.WebServerFileSystemParameters = webServerFileSystemParams;
|
||||
initParameters.WebRemoteFileSystemParameters = webRemoteFileSystemParams;
|
||||
|
||||
var initOperation = package.InitializeAsync(initParameters);
|
||||
await initOperation;
|
||||
|
||||
if (initOperation.Status == EOperationStatus.Succeed)
|
||||
Debug.Log("UnityEvo:资源包初始化成功!");
|
||||
else
|
||||
Debug.LogError($"UnityEvo:资源包初始化失败:{initOperation.Error}");
|
||||
|
||||
Debug.LogError($"UnityEvo:暂不支持");
|
||||
#endif
|
||||
@@ -221,7 +221,7 @@ namespace Stary.Evo
|
||||
// 将StreamingAssets下指定的package拷贝到目标路径
|
||||
string sourcePath = Path.Combine(Application.streamingAssetsPath,
|
||||
YooAssetSettingsData.GetDefaultYooFolderName(), packageDomainName);
|
||||
string loadPath = Path.Combine(Application.temporaryCachePath, "DownloadedContent",
|
||||
string loadPath = Path.Combine(Application.persistentDataPath, "DownloadedContent",
|
||||
packageDomainName);
|
||||
|
||||
// 创建目标目录(如果不存在)
|
||||
@@ -408,7 +408,7 @@ namespace Stary.Evo
|
||||
{
|
||||
// 在任意MonoBehaviour或DomainBase派生类中
|
||||
//string loadPath = Path.Combine(Application.temporaryCachePath, "DownloadedContent", AppConfig.PackageDomainName);
|
||||
string loadPath = Path.Combine(Application.temporaryCachePath, "DownloadedContent");
|
||||
string loadPath = Path.Combine(Application.persistentDataPath, "DownloadedContent");
|
||||
//string loadPath = $"{Application.persistentDataPath}/DownloadedContent/{AppConfig.PackageDomainName}";
|
||||
// if (Directory.Exists(loadPath))
|
||||
// {
|
||||
|
||||
318
Assets/00.StaryEvoTools/Runtime/Tools/CrossPlatformUnzip.cs
Normal file
318
Assets/00.StaryEvoTools/Runtime/Tools/CrossPlatformUnzip.cs
Normal file
@@ -0,0 +1,318 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
#if WECHAT_MINIGAME
|
||||
using WeChatWASM;
|
||||
#else
|
||||
using System.IO; // 仅在非微信平台使用
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
#endif
|
||||
|
||||
namespace Stary.Evo.Unzip
|
||||
{
|
||||
public static class CrossPlatformUnzip
|
||||
{
|
||||
public static async UniTask UnzipAsync(string zipPath, string extractPath,
|
||||
Action<float> progressCallback = null, bool deleteAfterExtract = true)
|
||||
{
|
||||
#if WECHAT_MINIGAME && UNITY_WEBGL
|
||||
// 微信小程序:使用原生 WX API,零 System.IO
|
||||
await UnzipWeChatNativeAsync(zipPath, extractPath, progressCallback, deleteAfterExtract);
|
||||
#elif UNITY_WEBGL && !WECHAT_MINIGAME
|
||||
// WebGL:使用 SharpZipLib(虚拟文件系统)
|
||||
await UnzipWebGLAsync(zipPath, extractPath, progressCallback, deleteAfterExtract);
|
||||
#else
|
||||
// PC/移动端:标准 System.IO
|
||||
await UnzipStandardAsync(zipPath, extractPath, progressCallback, deleteAfterExtract);
|
||||
#endif
|
||||
}
|
||||
|
||||
#region 1. 微信小程序实现(零 System.IO)
|
||||
|
||||
#if WECHAT_MINIGAME
|
||||
private static async UniTask UnzipWeChatNativeAsync(string zipPath, string extractPath,
|
||||
Action<float> progressCallback, bool deleteAfterExtract)
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource();
|
||||
var fs = WX.GetFileSystemManager();
|
||||
|
||||
// 检查 ZIP 存在(使用微信 API)
|
||||
bool fileExists = false;
|
||||
fs.Access(new AccessOption()
|
||||
{
|
||||
path = zipPath,
|
||||
success = (res) => fileExists = true,
|
||||
fail = (err) => { }
|
||||
});
|
||||
|
||||
await UniTask.Delay(50); // 等待异步结果
|
||||
if (!fileExists)
|
||||
throw new Exception($"ZIP 不存在: {zipPath}");
|
||||
|
||||
// 清理目标目录(递归删除)
|
||||
try
|
||||
{
|
||||
fs.RmdirSync(extractPath, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 目录不存在或已删除
|
||||
}
|
||||
|
||||
// 创建目标目录
|
||||
fs.MkdirSync(extractPath, true);
|
||||
|
||||
// 模拟进度(WX.Unzip 无细粒度进度)
|
||||
float reportedProgress = 0f;
|
||||
var progressTask = SimulateProgressAsync(progressCallback, () => reportedProgress >= 1f);
|
||||
|
||||
// 微信原生解压(性能最佳,C++ 实现)
|
||||
WX.Unzip(new UnzipOption()
|
||||
{
|
||||
zipFilePath = zipPath,
|
||||
targetPath = extractPath,
|
||||
success = (res) =>
|
||||
{
|
||||
reportedProgress = 1f;
|
||||
progressCallback?.Invoke(1f);
|
||||
tcs.TrySetResult();
|
||||
},
|
||||
fail = (err) =>
|
||||
{
|
||||
tcs.TrySetException(new Exception($"微信解压失败: {err.errMsg}"));
|
||||
}
|
||||
});
|
||||
|
||||
await tcs.Task;
|
||||
await progressTask;
|
||||
|
||||
// 删除 ZIP 释放空间(微信 200MB 限制严格)
|
||||
if (deleteAfterExtract)
|
||||
{
|
||||
DeleteWeChatFileSafe(fs, zipPath);
|
||||
}
|
||||
}
|
||||
|
||||
private static async UniTask SimulateProgressAsync(Action<float> callback, Func<bool> isCompleted)
|
||||
{
|
||||
if (callback == null) return;
|
||||
|
||||
float progress = 0f;
|
||||
while (!isCompleted())
|
||||
{
|
||||
progress = Mathf.Min(progress + UnityEngine.Random.Range(0.02f, 0.08f), 0.95f);
|
||||
callback.Invoke(progress);
|
||||
await UniTask.Delay(200);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeleteWeChatFileSafe(FileSystemManager fs, string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
fs.UnlinkSync(path);
|
||||
Debug.Log($"[微信] 已删除 ZIP");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"[微信] 删除失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取微信缓存路径(字符串拼接,不使用 Path.Combine)
|
||||
/// </summary>
|
||||
public static string GetWeChatPath(string relativePath)
|
||||
{
|
||||
// 手动拼接路径,避免使用 System.IO.Path
|
||||
string basePath = WX.env.USER_DATA_PATH;
|
||||
if (relativePath.StartsWith("/"))
|
||||
return basePath + relativePath;
|
||||
return basePath + "/" + relativePath;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region 2. WebGL 实现(SharpZipLib + 虚拟文件系统)
|
||||
|
||||
#if UNITY_WEBGL && !WECHAT_MINIGAME
|
||||
private static async UniTask UnzipWebGLAsync(string zipPath, string extractPath,
|
||||
Action<float> progressCallback, bool deleteAfterExtract)
|
||||
{
|
||||
// WebGL 使用 Unity 的虚拟文件系统(基于 IndexedDB)
|
||||
// 注意:虽然是 FileStream,但在 WebGL 中实际是虚拟 IO
|
||||
|
||||
if (!File.Exists(zipPath))
|
||||
throw new FileNotFoundException($"ZIP 不存在: {zipPath}");
|
||||
|
||||
string fullExtractPath = Path.GetFullPath(extractPath);
|
||||
|
||||
// 清理目录
|
||||
if (Directory.Exists(extractPath))
|
||||
Directory.Delete(extractPath, true);
|
||||
Directory.CreateDirectory(extractPath);
|
||||
|
||||
using (FileStream fs = File.OpenRead(zipPath))
|
||||
using (ZipFile zipFile = new ZipFile(fs))
|
||||
{
|
||||
int total = 0;
|
||||
var validEntries = new List<ZipEntry>();
|
||||
|
||||
foreach (ZipEntry entry in zipFile)
|
||||
{
|
||||
if (entry.IsFile && !string.IsNullOrEmpty(entry.Name))
|
||||
{
|
||||
validEntries.Add(entry);
|
||||
total++;
|
||||
}
|
||||
}
|
||||
|
||||
// WebGL 分帧解压,避免卡死浏览器
|
||||
int processed = 0;
|
||||
const int FILES_PER_FRAME = 3;
|
||||
|
||||
foreach (var entry in validEntries)
|
||||
{
|
||||
// 安全检查
|
||||
string fullPath = Path.GetFullPath(Path.Combine(extractPath, entry.Name));
|
||||
if (!fullPath.StartsWith(fullExtractPath))
|
||||
{
|
||||
Debug.LogWarning($"跳过非法路径: {entry.Name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 创建目录
|
||||
string dir = Path.GetDirectoryName(fullPath);
|
||||
if (!string.IsNullOrEmpty(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
// 解压单文件
|
||||
using (Stream input = zipFile.GetInputStream(entry))
|
||||
using (FileStream output = File.Create(fullPath))
|
||||
{
|
||||
byte[] buffer = new byte[4096]; // WebGL 使用更小缓冲区
|
||||
int read;
|
||||
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
|
||||
{
|
||||
output.Write(buffer, 0, read);
|
||||
}
|
||||
}
|
||||
|
||||
processed++;
|
||||
|
||||
// 每 N 个文件让出一帧,保持浏览器响应
|
||||
if (processed % FILES_PER_FRAME == 0)
|
||||
{
|
||||
progressCallback?.Invoke((float)processed / total);
|
||||
await UniTask.Yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteAfterExtract)
|
||||
{
|
||||
File.Delete(zipPath);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region 3. PC/移动端实现(标准 System.IO)
|
||||
|
||||
private static async UniTask UnzipStandardAsync(string zipPath, string extractPath,
|
||||
Action<float> progressCallback, bool deleteAfterExtract)
|
||||
{
|
||||
await UniTask.SwitchToThreadPool();
|
||||
|
||||
try
|
||||
{
|
||||
if (!File.Exists(zipPath))
|
||||
throw new FileNotFoundException($"ZIP 不存在: {zipPath}");
|
||||
|
||||
string fullExtractPath = Path.GetFullPath(extractPath);
|
||||
|
||||
if (Directory.Exists(extractPath))
|
||||
Directory.Delete(extractPath, true);
|
||||
Directory.CreateDirectory(extractPath);
|
||||
|
||||
using (FileStream fs = File.OpenRead(zipPath))
|
||||
using (ZipFile zipFile = new ZipFile(fs))
|
||||
{
|
||||
int total = 0;
|
||||
foreach (ZipEntry entry in zipFile)
|
||||
if (entry.IsFile) total++;
|
||||
|
||||
int processed = 0;
|
||||
foreach (ZipEntry entry in zipFile)
|
||||
{
|
||||
if (!entry.IsFile) continue;
|
||||
|
||||
string fullPath = Path.GetFullPath(Path.Combine(extractPath, entry.Name));
|
||||
if (!fullPath.StartsWith(fullExtractPath)) continue;
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
|
||||
|
||||
using (Stream input = zipFile.GetInputStream(entry))
|
||||
using (FileStream output = File.Create(fullPath))
|
||||
{
|
||||
await input.CopyToAsync(output);
|
||||
}
|
||||
|
||||
processed++;
|
||||
|
||||
await UniTask.SwitchToMainThread();
|
||||
progressCallback?.Invoke((float)processed / total);
|
||||
await UniTask.SwitchToThreadPool();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
await UniTask.SwitchToMainThread();
|
||||
if (deleteAfterExtract && File.Exists(zipPath))
|
||||
File.Delete(zipPath);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 4. 公共工具(平台特定)
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存路径(平台特定实现)
|
||||
/// </summary>
|
||||
public static string GetCachePath(string relativePath)
|
||||
{
|
||||
#if WECHAT_MINIGAME
|
||||
return GetWeChatPath(relativePath);
|
||||
#else
|
||||
return Path.Combine(Application.persistentDataPath, relativePath);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查文件存在(平台特定)
|
||||
/// </summary>
|
||||
public static bool FileExists(string path)
|
||||
{
|
||||
#if WECHAT_MINIGAME
|
||||
bool exists = false;
|
||||
WX.GetFileSystemManager().Access(new AccessOption()
|
||||
{
|
||||
path = path,
|
||||
success = (res) => exists = true,
|
||||
fail = (err) => { }
|
||||
});
|
||||
return exists;
|
||||
#else
|
||||
return File.Exists(path);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a27d5c7ab782498b90e6d3a8879410d0
|
||||
timeCreated: 1776267030
|
||||
@@ -19,8 +19,8 @@ namespace Stary.Evo
|
||||
{
|
||||
var initParameters = new OfflinePlayModeParameters();
|
||||
var buildinFileSystemParams = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(null,
|
||||
$"{Application.temporaryCachePath}/DownloadedContent/{packageName}");
|
||||
Debug.Log($"UnityEvo:Host InitializeParameterPath: 【{Application.temporaryCachePath}/DownloadedContent/{packageName}】");
|
||||
$"{Application.persistentDataPath}/DownloadedContent/{packageName}");
|
||||
Debug.Log($"UnityEvo:Host InitializeParameterPath: 【{Application.persistentDataPath}/DownloadedContent/{packageName}】");
|
||||
buildinFileSystemParams.AddParameter(FileSystemParametersDefine.APPEND_FILE_EXTENSION, true);
|
||||
buildinFileSystemParams.AddParameter(FileSystemParametersDefine.COPY_BUILDIN_PACKAGE_MANIFEST, true);
|
||||
initParameters.BuildinFileSystemParameters = buildinFileSystemParams;
|
||||
|
||||
@@ -4,36 +4,86 @@ using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Stary.Evo.Unzip;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
using WeChatWASM;
|
||||
|
||||
namespace Stary.Evo
|
||||
{
|
||||
public class ZipTool
|
||||
{
|
||||
private static SemaphoreSlim unzipLock = new(1, 1);
|
||||
|
||||
// 新增:带进度回调的下载解压方法
|
||||
public static async UniTask DownloadAndUnzipAsync(string fildId, string extractPath,
|
||||
Action<float> downloadProgress = null, Action<float> unzipProgress = null)
|
||||
{
|
||||
string tempPath = Path.Combine(Application.persistentDataPath, "temp.zip");
|
||||
|
||||
try
|
||||
{
|
||||
string url = $"{AppConfig.IpConfig}/FileLoad/Download/{fildId}";
|
||||
await WebRequestSystem.GetFile(url, tempPath,downloadProgress);
|
||||
|
||||
|
||||
#if !WEIXINMINIGAME
|
||||
string tempPath = Path.Combine(Application.persistentDataPath, "temp.zip");
|
||||
await WebRequestSystem.GetFile(url, tempPath, downloadProgress);
|
||||
#else
|
||||
bool isCompleted = false;
|
||||
string tempPath = WX.env.USER_DATA_PATH + "/temp.zip"; // 指定保存路径
|
||||
var downloadTask = WX.DownloadFile(new DownloadFileOption()
|
||||
{
|
||||
url = url,
|
||||
filePath = WX.env.USER_DATA_PATH + "/temp.zip", // 指定保存路径
|
||||
success = (res) => {
|
||||
isCompleted = true;
|
||||
},
|
||||
fail = (err) => {
|
||||
Debug.LogError("下载失败:" + err.errMsg);
|
||||
isCompleted = true;
|
||||
}
|
||||
});
|
||||
// 注册进度回调(关键代码)
|
||||
downloadTask.OnProgressUpdate((res) =>
|
||||
{
|
||||
// res.progress: 下载进度 0-100
|
||||
// res.totalBytesExpectedToWrite: 预期总字节数
|
||||
// res.totalBytesWritten: 已下载字节数
|
||||
downloadProgress?.Invoke((float) res.progress);
|
||||
Debug.Log($"下载进度:{res.progress:F1}% | " +
|
||||
$"已下载:{res.totalBytesWritten / 1024}KB / " +
|
||||
$"总计:{res.totalBytesExpectedToWrite / 1024}KB");
|
||||
});
|
||||
// 等待完成或被取消
|
||||
await new WaitUntil(() => isCompleted);
|
||||
// 等待下载完成,同时可以更新 UI
|
||||
// while (!isCompleted)
|
||||
// {
|
||||
// // 在这里更新 Unity UI 进度条
|
||||
// UpdateProgressUI(currentProgress);
|
||||
// yield return null;
|
||||
// }
|
||||
// WX.GetFileSystemManager().Unzip(new UnzipOption()
|
||||
// {
|
||||
// zipFilePath = tempPath,
|
||||
// targetPath = extractPath,
|
||||
// success = (suc) =>
|
||||
// {
|
||||
// Debug.Log("解压成功");
|
||||
// File.Delete(tempPath);
|
||||
// },
|
||||
// fail = (err) =>
|
||||
// {
|
||||
// Debug.LogError($"解压失败:{err.errMsg}");
|
||||
// }
|
||||
// });
|
||||
#endif
|
||||
|
||||
// 解压下载的文件
|
||||
await UnzipAsync(tempPath, extractPath, unzipProgress);
|
||||
File.Delete(tempPath);
|
||||
await CrossPlatformUnzip.UnzipAsync(tempPath, extractPath, unzipProgress);
|
||||
}
|
||||
finally
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 清理临时文件
|
||||
if (File.Exists(tempPath))
|
||||
{
|
||||
File.Delete(tempPath);
|
||||
}
|
||||
Debug.LogError($"解压失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +110,7 @@ namespace Stary.Evo
|
||||
Debug.LogWarning($"跳过不安全的文件路径: {entry.FullName}");
|
||||
continue;
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
|
||||
|
||||
if (!string.IsNullOrEmpty(entry.Name))
|
||||
@@ -67,7 +118,7 @@ namespace Stary.Evo
|
||||
using (Stream inputStream = entry.Open())
|
||||
using (FileStream outputStream = File.Create(filePath))
|
||||
{
|
||||
await inputStream.CopyToAsync(outputStream,81920);
|
||||
await inputStream.CopyToAsync(outputStream, 81920);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +127,7 @@ namespace Stary.Evo
|
||||
progressCallback?.Invoke(progress);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"解压完成: {extractPath}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -85,6 +137,7 @@ namespace Stary.Evo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 自定义网络请求器需要登录
|
||||
/// </summary>
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
"Informationsave.runtime",
|
||||
"DOTween.Modules",
|
||||
"com.stary.buildoriginality.runtime",
|
||||
"YooAsset"
|
||||
"YooAsset",
|
||||
"Wx"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "com.staryevo.tools",
|
||||
"version": "1.4.4",
|
||||
"version": "1.4.4_alpha",
|
||||
"displayName": "00.StaryEvo.Tools",
|
||||
"description": "This is an Framework package(后台服务器版本,端口9527)",
|
||||
"unity": "2021.3",
|
||||
|
||||
Reference in New Issue
Block a user