Compare commits
10 Commits
Author | SHA1 | Date |
---|---|---|
|
dc33abde46 | |
|
aae7b08dd1 | |
|
f70582af1a | |
|
f04d84bb93 | |
|
78693deed6 | |
|
f1a5965b4c | |
|
cd43d0775d | |
|
cf532a84ef | |
|
651b65d148 | |
|
895dde1cc8 |
|
@ -2,6 +2,27 @@
|
||||||
|
|
||||||
All notable changes to this package will be documented in this file.
|
All notable changes to this package will be documented in this file.
|
||||||
|
|
||||||
|
## [1.4.16] - 2023-06-14
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- 增加了自动分析冗余资源的开关
|
||||||
|
|
||||||
|
```c#
|
||||||
|
/// <summary>
|
||||||
|
/// 构建参数
|
||||||
|
/// </summary>
|
||||||
|
public class BuildParameters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 自动分析冗余资源
|
||||||
|
/// </summary>
|
||||||
|
public bool AutoAnalyzeRedundancy = true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 太空战机DEMO启用了新的内置资源查询机制。
|
||||||
|
|
||||||
## [1.4.15] - 2023-06-09
|
## [1.4.15] - 2023-06-09
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -275,6 +275,7 @@ namespace YooAsset.Editor
|
||||||
buildParameters.PackageName = AssetBundleBuilderSettingData.Setting.BuildPackage;
|
buildParameters.PackageName = AssetBundleBuilderSettingData.Setting.BuildPackage;
|
||||||
buildParameters.PackageVersion = _buildVersionField.value;
|
buildParameters.PackageVersion = _buildVersionField.value;
|
||||||
buildParameters.VerifyBuildingResult = true;
|
buildParameters.VerifyBuildingResult = true;
|
||||||
|
buildParameters.AutoAnalyzeRedundancy = true;
|
||||||
buildParameters.ShareAssetPackRule = new DefaultShareAssetPackRule();
|
buildParameters.ShareAssetPackRule = new DefaultShareAssetPackRule();
|
||||||
buildParameters.EncryptionServices = CreateEncryptionServicesInstance();
|
buildParameters.EncryptionServices = CreateEncryptionServicesInstance();
|
||||||
buildParameters.CompressOption = AssetBundleBuilderSettingData.Setting.CompressOption;
|
buildParameters.CompressOption = AssetBundleBuilderSettingData.Setting.CompressOption;
|
||||||
|
|
|
@ -183,5 +183,27 @@ namespace YooAsset.Editor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断是否为冗余资源
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRedundancyAsset()
|
||||||
|
{
|
||||||
|
if (CollectorType != ECollectorType.None)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (IsRawAsset)
|
||||||
|
throw new Exception("Should never get here !");
|
||||||
|
|
||||||
|
return _referenceBundleNames.Count > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取关联资源包的数量
|
||||||
|
/// </summary>
|
||||||
|
public int GetReferenceBundleCount()
|
||||||
|
{
|
||||||
|
return _referenceBundleNames.Count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,6 +10,11 @@ namespace YooAsset.Editor
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, BuildBundleInfo> _bundleInfoDic = new Dictionary<string, BuildBundleInfo>(10000);
|
private readonly Dictionary<string, BuildBundleInfo> _bundleInfoDic = new Dictionary<string, BuildBundleInfo>(10000);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 冗余的资源列表
|
||||||
|
/// </summary>
|
||||||
|
public readonly List<ReportRedundancyInfo> RedundancyInfos= new List<ReportRedundancyInfo>(1000);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 参与构建的资源总数
|
/// 参与构建的资源总数
|
||||||
/// 说明:包括主动收集的资源以及其依赖的所有资源
|
/// 说明:包括主动收集的资源以及其依赖的所有资源
|
||||||
|
|
|
@ -77,6 +77,11 @@ namespace YooAsset.Editor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool VerifyBuildingResult = false;
|
public bool VerifyBuildingResult = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自动分析冗余资源
|
||||||
|
/// </summary>
|
||||||
|
public bool AutoAnalyzeRedundancy = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 共享资源的打包规则
|
/// 共享资源的打包规则
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -27,6 +27,11 @@ namespace YooAsset.Editor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<ReportBundleInfo> BundleInfos = new List<ReportBundleInfo>();
|
public List<ReportBundleInfo> BundleInfos = new List<ReportBundleInfo>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 冗余的资源列表
|
||||||
|
/// </summary>
|
||||||
|
public List<ReportRedundancyInfo> RedundancyInfos = new List<ReportRedundancyInfo>();
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取资源包信息类
|
/// 获取资源包信息类
|
||||||
|
@ -61,7 +66,7 @@ namespace YooAsset.Editor
|
||||||
File.Delete(savePath);
|
File.Delete(savePath);
|
||||||
|
|
||||||
string json = JsonUtility.ToJson(buildReport, true);
|
string json = JsonUtility.ToJson(buildReport, true);
|
||||||
FileUtility.CreateFile(savePath, json);
|
FileUtility.WriteAllText(savePath, json);
|
||||||
}
|
}
|
||||||
public static BuildReport Deserialize(string jsonData)
|
public static BuildReport Deserialize(string jsonData)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace YooAsset.Editor
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public class ReportRedundancyInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 资源路径
|
||||||
|
/// </summary>
|
||||||
|
public string AssetPath;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源类型
|
||||||
|
/// </summary>
|
||||||
|
public string AssetType;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源GUID
|
||||||
|
/// 说明:Meta文件记录的GUID
|
||||||
|
/// </summary>
|
||||||
|
public string AssetGUID;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源文件大小
|
||||||
|
/// </summary>
|
||||||
|
public long FileSize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 冗余的资源包数量
|
||||||
|
/// </summary>
|
||||||
|
public int Number;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7fbb7b27f54d3b0439a951348fd9d785
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -22,7 +22,7 @@ namespace YooAsset.Editor
|
||||||
/// 构建时间
|
/// 构建时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string BuildDate;
|
public string BuildDate;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构建耗时(单位:秒)
|
/// 构建耗时(单位:秒)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -63,6 +63,16 @@ namespace YooAsset.Editor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UniqueBundleName;
|
public bool UniqueBundleName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自动分析冗余
|
||||||
|
/// </summary>
|
||||||
|
public bool AutoAnalyzeRedundancy;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 共享资源的打包类名称
|
||||||
|
/// </summary>
|
||||||
|
public string ShareAssetPackRuleClassName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 加密服务类名称
|
/// 加密服务类名称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -98,7 +98,7 @@ namespace YooAsset.Editor
|
||||||
{
|
{
|
||||||
string fileName = YooAssetSettingsData.GetPackageHashFileName(buildParameters.PackageName, buildParameters.PackageVersion);
|
string fileName = YooAssetSettingsData.GetPackageHashFileName(buildParameters.PackageName, buildParameters.PackageVersion);
|
||||||
string filePath = $"{packageOutputDirectory}/{fileName}";
|
string filePath = $"{packageOutputDirectory}/{fileName}";
|
||||||
FileUtility.CreateFile(filePath, packageHash);
|
FileUtility.WriteAllText(filePath, packageHash);
|
||||||
BuildLogger.Log($"创建补丁清单哈希文件:{filePath}");
|
BuildLogger.Log($"创建补丁清单哈希文件:{filePath}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ namespace YooAsset.Editor
|
||||||
{
|
{
|
||||||
string fileName = YooAssetSettingsData.GetPackageVersionFileName(buildParameters.PackageName);
|
string fileName = YooAssetSettingsData.GetPackageVersionFileName(buildParameters.PackageName);
|
||||||
string filePath = $"{packageOutputDirectory}/{fileName}";
|
string filePath = $"{packageOutputDirectory}/{fileName}";
|
||||||
FileUtility.CreateFile(filePath, buildParameters.PackageVersion);
|
FileUtility.WriteAllText(filePath, buildParameters.PackageVersion);
|
||||||
BuildLogger.Log($"创建补丁清单版本文件:{filePath}");
|
BuildLogger.Log($"创建补丁清单版本文件:{filePath}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
|
|
||||||
namespace YooAsset.Editor
|
namespace YooAsset.Editor
|
||||||
|
@ -46,6 +47,9 @@ namespace YooAsset.Editor
|
||||||
buildReport.Summary.BuildPackageVersion = buildParameters.PackageVersion;
|
buildReport.Summary.BuildPackageVersion = buildParameters.PackageVersion;
|
||||||
buildReport.Summary.EnableAddressable = buildMapContext.EnableAddressable;
|
buildReport.Summary.EnableAddressable = buildMapContext.EnableAddressable;
|
||||||
buildReport.Summary.UniqueBundleName = buildMapContext.UniqueBundleName;
|
buildReport.Summary.UniqueBundleName = buildMapContext.UniqueBundleName;
|
||||||
|
buildReport.Summary.AutoAnalyzeRedundancy = buildParameters.AutoAnalyzeRedundancy;
|
||||||
|
buildReport.Summary.ShareAssetPackRuleClassName = buildParameters.ShareAssetPackRule == null ?
|
||||||
|
"null" : buildParameters.ShareAssetPackRule.GetType().FullName;
|
||||||
buildReport.Summary.EncryptionServicesClassName = buildParameters.EncryptionServices == null ?
|
buildReport.Summary.EncryptionServicesClassName = buildParameters.EncryptionServices == null ?
|
||||||
"null" : buildParameters.EncryptionServices.GetType().FullName;
|
"null" : buildParameters.EncryptionServices.GetType().FullName;
|
||||||
|
|
||||||
|
@ -101,6 +105,9 @@ namespace YooAsset.Editor
|
||||||
buildReport.BundleInfos.Add(reportBundleInfo);
|
buildReport.BundleInfos.Add(reportBundleInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 冗余资源列表
|
||||||
|
buildReport.RedundancyInfos = new List<ReportRedundancyInfo>(buildMapContext.RedundancyInfos);
|
||||||
|
|
||||||
// 序列化文件
|
// 序列化文件
|
||||||
string fileName = YooAssetSettingsData.GetReportFileName(buildParameters.PackageName, buildParameters.PackageVersion);
|
string fileName = YooAssetSettingsData.GetReportFileName(buildParameters.PackageName, buildParameters.PackageVersion);
|
||||||
string filePath = $"{packageOutputDirectory}/{fileName}";
|
string filePath = $"{packageOutputDirectory}/{fileName}";
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace YooAsset.Editor
|
||||||
}
|
}
|
||||||
|
|
||||||
string filePath = $"{pipelineOutputDirectory}/{bundleInfo.BundleName}.encrypt";
|
string filePath = $"{pipelineOutputDirectory}/{bundleInfo.BundleName}.encrypt";
|
||||||
FileUtility.CreateFile(filePath, encryptResult.EncryptedData);
|
FileUtility.WriteAllBytes(filePath, encryptResult.EncryptedData);
|
||||||
bundleInfo.EncryptedFilePath = filePath;
|
bundleInfo.EncryptedFilePath = filePath;
|
||||||
bundleInfo.LoadMethod = encryptResult.LoadMethod;
|
bundleInfo.LoadMethod = encryptResult.LoadMethod;
|
||||||
BuildLogger.Log($"Bundle文件加密完成:{filePath}");
|
BuildLogger.Log($"Bundle文件加密完成:{filePath}");
|
||||||
|
|
|
@ -13,8 +13,7 @@ namespace YooAsset.Editor
|
||||||
void IBuildTask.Run(BuildContext context)
|
void IBuildTask.Run(BuildContext context)
|
||||||
{
|
{
|
||||||
var buildParametersContext = context.GetContextObject<BuildParametersContext>();
|
var buildParametersContext = context.GetContextObject<BuildParametersContext>();
|
||||||
var buildParameters = buildParametersContext.Parameters;
|
var buildMapContext = CreateBuildMap(buildParametersContext.Parameters);
|
||||||
var buildMapContext = CreateBuildMap(buildParameters.BuildMode, buildParameters.ShareAssetPackRule, buildParameters.PackageName);
|
|
||||||
context.SetContextObject(buildMapContext);
|
context.SetContextObject(buildMapContext);
|
||||||
BuildLogger.Log("构建内容准备完毕!");
|
BuildLogger.Log("构建内容准备完毕!");
|
||||||
|
|
||||||
|
@ -25,8 +24,13 @@ namespace YooAsset.Editor
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 资源构建上下文
|
/// 资源构建上下文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public BuildMapContext CreateBuildMap(EBuildMode buildMode, IShareAssetPackRule packRule, string packageName)
|
public BuildMapContext CreateBuildMap(BuildParameters buildParameters)
|
||||||
{
|
{
|
||||||
|
EBuildMode buildMode = buildParameters.BuildMode;
|
||||||
|
string packageName = buildParameters.PackageName;
|
||||||
|
IShareAssetPackRule sharePackRule = buildParameters.ShareAssetPackRule;
|
||||||
|
bool autoAnalyzeRedundancy = buildParameters.AutoAnalyzeRedundancy;
|
||||||
|
|
||||||
Dictionary<string, BuildAssetInfo> allBuildAssetInfoDic = new Dictionary<string, BuildAssetInfo>(1000);
|
Dictionary<string, BuildAssetInfo> allBuildAssetInfoDic = new Dictionary<string, BuildAssetInfo>(1000);
|
||||||
|
|
||||||
// 1. 检测配置合法性
|
// 1. 检测配置合法性
|
||||||
|
@ -99,10 +103,30 @@ namespace YooAsset.Editor
|
||||||
context.ShadersBundleName = collectResult.Command.ShadersBundleName;
|
context.ShadersBundleName = collectResult.Command.ShadersBundleName;
|
||||||
|
|
||||||
// 8. 计算共享的资源包名
|
// 8. 计算共享的资源包名
|
||||||
var command = collectResult.Command;
|
if (autoAnalyzeRedundancy)
|
||||||
foreach (var buildAssetInfo in allBuildAssetInfoDic.Values)
|
|
||||||
{
|
{
|
||||||
buildAssetInfo.CalculateShareBundleName(packRule, command.UniqueBundleName, command.PackageName, command.ShadersBundleName);
|
var command = collectResult.Command;
|
||||||
|
foreach (var buildAssetInfo in allBuildAssetInfoDic.Values)
|
||||||
|
{
|
||||||
|
buildAssetInfo.CalculateShareBundleName(sharePackRule, command.UniqueBundleName, command.PackageName, command.ShadersBundleName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 记录冗余资源
|
||||||
|
foreach (var buildAssetInfo in allBuildAssetInfoDic.Values)
|
||||||
|
{
|
||||||
|
if (buildAssetInfo.IsRedundancyAsset())
|
||||||
|
{
|
||||||
|
var redundancyInfo = new ReportRedundancyInfo();
|
||||||
|
redundancyInfo.AssetPath = buildAssetInfo.AssetPath;
|
||||||
|
redundancyInfo.AssetType = AssetDatabase.GetMainAssetTypeAtPath(buildAssetInfo.AssetPath).Name;
|
||||||
|
redundancyInfo.AssetGUID = AssetDatabase.AssetPathToGUID(buildAssetInfo.AssetPath);
|
||||||
|
redundancyInfo.FileSize = FileUtility.GetFileSize(buildAssetInfo.AssetPath);
|
||||||
|
redundancyInfo.Number = buildAssetInfo.GetReferenceBundleCount();
|
||||||
|
context.RedundancyInfos.Add(redundancyInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 9. 移除不参与构建的资源
|
// 9. 移除不参与构建的资源
|
||||||
|
|
|
@ -251,7 +251,7 @@ namespace YooAsset.Editor
|
||||||
// 检测原生文件是否合规
|
// 检测原生文件是否合规
|
||||||
if (isRawFilePackRule)
|
if (isRawFilePackRule)
|
||||||
{
|
{
|
||||||
string extension = StringUtility.RemoveFirstChar(System.IO.Path.GetExtension(assetPath));
|
string extension = EditorTools.RemoveFirstChar(System.IO.Path.GetExtension(assetPath));
|
||||||
if (extension == EAssetFileExtension.unity.ToString() || extension == EAssetFileExtension.prefab.ToString() ||
|
if (extension == EAssetFileExtension.unity.ToString() || extension == EAssetFileExtension.prefab.ToString() ||
|
||||||
extension == EAssetFileExtension.fbx.ToString() || extension == EAssetFileExtension.mat.ToString() ||
|
extension == EAssetFileExtension.fbx.ToString() || extension == EAssetFileExtension.mat.ToString() ||
|
||||||
extension == EAssetFileExtension.controller.ToString() || extension == EAssetFileExtension.anim.ToString() ||
|
extension == EAssetFileExtension.controller.ToString() || extension == EAssetFileExtension.anim.ToString() ||
|
||||||
|
|
|
@ -40,7 +40,7 @@ namespace YooAsset.Editor
|
||||||
{
|
{
|
||||||
PackRuleResult IPackRule.GetPackRuleResult(PackRuleData data)
|
PackRuleResult IPackRule.GetPackRuleResult(PackRuleData data)
|
||||||
{
|
{
|
||||||
string bundleName = StringUtility.RemoveExtension(data.AssetPath);
|
string bundleName = PathUtility.RemoveExtension(data.AssetPath);
|
||||||
PackRuleResult result = new PackRuleResult(bundleName, DefaultPackRule.AssetBundleFileExtension);
|
PackRuleResult result = new PackRuleResult(bundleName, DefaultPackRule.AssetBundleFileExtension);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ namespace YooAsset.Editor
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bundleName = StringUtility.RemoveExtension(collectPath);
|
bundleName = PathUtility.RemoveExtension(collectPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
PackRuleResult result = new PackRuleResult(bundleName, DefaultPackRule.AssetBundleFileExtension);
|
PackRuleResult result = new PackRuleResult(bundleName, DefaultPackRule.AssetBundleFileExtension);
|
||||||
|
|
|
@ -278,7 +278,7 @@ namespace YooAsset.Editor
|
||||||
|
|
||||||
string filePath = $"{resultPath}/{nameof(DebugReport)}_{_currentReport.FrameCount}.json";
|
string filePath = $"{resultPath}/{nameof(DebugReport)}_{_currentReport.FrameCount}.json";
|
||||||
string fileContent = JsonUtility.ToJson(_currentReport, true);
|
string fileContent = JsonUtility.ToJson(_currentReport, true);
|
||||||
FileUtility.CreateFile(filePath, fileContent);
|
FileUtility.WriteAllText(filePath, fileContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void OnSearchKeyWordChange(ChangeEvent<string> e)
|
private void OnSearchKeyWordChange(ChangeEvent<string> e)
|
||||||
|
|
|
@ -35,12 +35,18 @@ namespace YooAsset.Editor
|
||||||
/// 资源包视图
|
/// 资源包视图
|
||||||
/// </summary>
|
/// </summary>
|
||||||
BundleView,
|
BundleView,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 冗余资源试图
|
||||||
|
/// </summary>
|
||||||
|
Redundancy,
|
||||||
}
|
}
|
||||||
|
|
||||||
private ToolbarMenu _viewModeMenu;
|
private ToolbarMenu _viewModeMenu;
|
||||||
private ReporterSummaryViewer _summaryViewer;
|
private ReporterSummaryViewer _summaryViewer;
|
||||||
private ReporterAssetListViewer _assetListViewer;
|
private ReporterAssetListViewer _assetListViewer;
|
||||||
private ReporterBundleListViewer _bundleListViewer;
|
private ReporterBundleListViewer _bundleListViewer;
|
||||||
|
private ReporterRedundancyListViewer _redundancyListViewer;
|
||||||
|
|
||||||
private EViewMode _viewMode;
|
private EViewMode _viewMode;
|
||||||
private BuildReport _buildReport;
|
private BuildReport _buildReport;
|
||||||
|
@ -70,6 +76,7 @@ namespace YooAsset.Editor
|
||||||
_viewModeMenu.menu.AppendAction(EViewMode.Summary.ToString(), ViewModeMenuAction0, ViewModeMenuFun0);
|
_viewModeMenu.menu.AppendAction(EViewMode.Summary.ToString(), ViewModeMenuAction0, ViewModeMenuFun0);
|
||||||
_viewModeMenu.menu.AppendAction(EViewMode.AssetView.ToString(), ViewModeMenuAction1, ViewModeMenuFun1);
|
_viewModeMenu.menu.AppendAction(EViewMode.AssetView.ToString(), ViewModeMenuAction1, ViewModeMenuFun1);
|
||||||
_viewModeMenu.menu.AppendAction(EViewMode.BundleView.ToString(), ViewModeMenuAction2, ViewModeMenuFun2);
|
_viewModeMenu.menu.AppendAction(EViewMode.BundleView.ToString(), ViewModeMenuAction2, ViewModeMenuFun2);
|
||||||
|
_viewModeMenu.menu.AppendAction(EViewMode.Redundancy.ToString(), ViewModeMenuAction3, ViewModeMenuFun3);
|
||||||
|
|
||||||
// 搜索栏
|
// 搜索栏
|
||||||
var searchField = root.Q<ToolbarSearchField>("SearchField");
|
var searchField = root.Q<ToolbarSearchField>("SearchField");
|
||||||
|
@ -87,6 +94,10 @@ namespace YooAsset.Editor
|
||||||
_bundleListViewer = new ReporterBundleListViewer();
|
_bundleListViewer = new ReporterBundleListViewer();
|
||||||
_bundleListViewer.InitViewer();
|
_bundleListViewer.InitViewer();
|
||||||
|
|
||||||
|
// 加载试图
|
||||||
|
_redundancyListViewer = new ReporterRedundancyListViewer();
|
||||||
|
_redundancyListViewer.InitViewer();
|
||||||
|
|
||||||
// 显示视图
|
// 显示视图
|
||||||
_viewMode = EViewMode.Summary;
|
_viewMode = EViewMode.Summary;
|
||||||
_viewModeMenu.text = EViewMode.Summary.ToString();
|
_viewModeMenu.text = EViewMode.Summary.ToString();
|
||||||
|
@ -111,9 +122,10 @@ namespace YooAsset.Editor
|
||||||
_reportFilePath = selectFilePath;
|
_reportFilePath = selectFilePath;
|
||||||
string jsonData = FileUtility.ReadAllText(_reportFilePath);
|
string jsonData = FileUtility.ReadAllText(_reportFilePath);
|
||||||
_buildReport = BuildReport.Deserialize(jsonData);
|
_buildReport = BuildReport.Deserialize(jsonData);
|
||||||
|
_summaryViewer.FillViewData(_buildReport);
|
||||||
_assetListViewer.FillViewData(_buildReport, _searchKeyWord);
|
_assetListViewer.FillViewData(_buildReport, _searchKeyWord);
|
||||||
_bundleListViewer.FillViewData(_buildReport, _reportFilePath, _searchKeyWord);
|
_bundleListViewer.FillViewData(_buildReport, _reportFilePath, _searchKeyWord);
|
||||||
_summaryViewer.FillViewData(_buildReport);
|
_redundancyListViewer.FillViewData(_buildReport, _searchKeyWord);
|
||||||
}
|
}
|
||||||
private void OnSearchKeyWordChange(ChangeEvent<string> e)
|
private void OnSearchKeyWordChange(ChangeEvent<string> e)
|
||||||
{
|
{
|
||||||
|
@ -134,6 +146,7 @@ namespace YooAsset.Editor
|
||||||
_summaryViewer.AttachParent(root);
|
_summaryViewer.AttachParent(root);
|
||||||
_assetListViewer.DetachParent();
|
_assetListViewer.DetachParent();
|
||||||
_bundleListViewer.DetachParent();
|
_bundleListViewer.DetachParent();
|
||||||
|
_redundancyListViewer.DetachParent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void ViewModeMenuAction1(DropdownMenuAction action)
|
private void ViewModeMenuAction1(DropdownMenuAction action)
|
||||||
|
@ -146,6 +159,7 @@ namespace YooAsset.Editor
|
||||||
_summaryViewer.DetachParent();
|
_summaryViewer.DetachParent();
|
||||||
_assetListViewer.AttachParent(root);
|
_assetListViewer.AttachParent(root);
|
||||||
_bundleListViewer.DetachParent();
|
_bundleListViewer.DetachParent();
|
||||||
|
_redundancyListViewer.DetachParent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void ViewModeMenuAction2(DropdownMenuAction action)
|
private void ViewModeMenuAction2(DropdownMenuAction action)
|
||||||
|
@ -158,6 +172,20 @@ namespace YooAsset.Editor
|
||||||
_summaryViewer.DetachParent();
|
_summaryViewer.DetachParent();
|
||||||
_assetListViewer.DetachParent();
|
_assetListViewer.DetachParent();
|
||||||
_bundleListViewer.AttachParent(root);
|
_bundleListViewer.AttachParent(root);
|
||||||
|
_redundancyListViewer.DetachParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void ViewModeMenuAction3(DropdownMenuAction action)
|
||||||
|
{
|
||||||
|
if (_viewMode != EViewMode.Redundancy)
|
||||||
|
{
|
||||||
|
_viewMode = EViewMode.Redundancy;
|
||||||
|
VisualElement root = this.rootVisualElement;
|
||||||
|
_viewModeMenu.text = EViewMode.Redundancy.ToString();
|
||||||
|
_summaryViewer.DetachParent();
|
||||||
|
_assetListViewer.DetachParent();
|
||||||
|
_bundleListViewer.DetachParent();
|
||||||
|
_redundancyListViewer.AttachParent(root);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private DropdownMenuAction.Status ViewModeMenuFun0(DropdownMenuAction action)
|
private DropdownMenuAction.Status ViewModeMenuFun0(DropdownMenuAction action)
|
||||||
|
@ -181,6 +209,13 @@ namespace YooAsset.Editor
|
||||||
else
|
else
|
||||||
return DropdownMenuAction.Status.Normal;
|
return DropdownMenuAction.Status.Normal;
|
||||||
}
|
}
|
||||||
|
private DropdownMenuAction.Status ViewModeMenuFun3(DropdownMenuAction action)
|
||||||
|
{
|
||||||
|
if (_viewMode == EViewMode.Redundancy)
|
||||||
|
return DropdownMenuAction.Status.Checked;
|
||||||
|
else
|
||||||
|
return DropdownMenuAction.Status.Normal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
|
@ -0,0 +1,317 @@
|
||||||
|
#if UNITY_2019_4_OR_NEWER
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor.UIElements;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
|
namespace YooAsset.Editor
|
||||||
|
{
|
||||||
|
internal class ReporterRedundancyListViewer
|
||||||
|
{
|
||||||
|
private enum ESortMode
|
||||||
|
{
|
||||||
|
AssetPath,
|
||||||
|
AssetType,
|
||||||
|
FileSize,
|
||||||
|
Number,
|
||||||
|
}
|
||||||
|
|
||||||
|
private VisualTreeAsset _visualAsset;
|
||||||
|
private TemplateContainer _root;
|
||||||
|
|
||||||
|
private ToolbarButton _topBar1;
|
||||||
|
private ToolbarButton _topBar2;
|
||||||
|
private ToolbarButton _topBar3;
|
||||||
|
private ToolbarButton _topBar4;
|
||||||
|
private ListView _assetListView;
|
||||||
|
|
||||||
|
private BuildReport _buildReport;
|
||||||
|
private string _searchKeyWord;
|
||||||
|
private ESortMode _sortMode = ESortMode.AssetPath;
|
||||||
|
private bool _descendingSort = false;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化页面
|
||||||
|
/// </summary>
|
||||||
|
public void InitViewer()
|
||||||
|
{
|
||||||
|
// 加载布局文件
|
||||||
|
_visualAsset = UxmlLoader.LoadWindowUXML<ReporterRedundancyListViewer>();
|
||||||
|
if (_visualAsset == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_root = _visualAsset.CloneTree();
|
||||||
|
_root.style.flexGrow = 1f;
|
||||||
|
|
||||||
|
// 顶部按钮栏
|
||||||
|
_topBar1 = _root.Q<ToolbarButton>("TopBar1");
|
||||||
|
_topBar2 = _root.Q<ToolbarButton>("TopBar2");
|
||||||
|
_topBar3 = _root.Q<ToolbarButton>("TopBar3");
|
||||||
|
_topBar4 = _root.Q<ToolbarButton>("TopBar4");
|
||||||
|
_topBar1.clicked += TopBar1_clicked;
|
||||||
|
_topBar2.clicked += TopBar2_clicked;
|
||||||
|
_topBar3.clicked += TopBar3_clicked;
|
||||||
|
_topBar4.clicked += TopBar4_clicked;
|
||||||
|
|
||||||
|
// 资源列表
|
||||||
|
_assetListView = _root.Q<ListView>("TopListView");
|
||||||
|
_assetListView.makeItem = MakeAssetListViewItem;
|
||||||
|
_assetListView.bindItem = BindAssetListViewItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 填充页面数据
|
||||||
|
/// </summary>
|
||||||
|
public void FillViewData(BuildReport buildReport, string searchKeyWord)
|
||||||
|
{
|
||||||
|
_buildReport = buildReport;
|
||||||
|
_searchKeyWord = searchKeyWord;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
private void RefreshView()
|
||||||
|
{
|
||||||
|
_assetListView.Clear();
|
||||||
|
_assetListView.ClearSelection();
|
||||||
|
_assetListView.itemsSource = FilterAndSortViewItems();
|
||||||
|
_assetListView.Rebuild();
|
||||||
|
RefreshSortingSymbol();
|
||||||
|
}
|
||||||
|
private List<ReportRedundancyInfo> FilterAndSortViewItems()
|
||||||
|
{
|
||||||
|
List<ReportRedundancyInfo> result = new List<ReportRedundancyInfo>(_buildReport.RedundancyInfos.Count);
|
||||||
|
|
||||||
|
// 过滤列表
|
||||||
|
foreach (var redundancyInfo in _buildReport.RedundancyInfos)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_searchKeyWord) == false)
|
||||||
|
{
|
||||||
|
if (redundancyInfo.AssetPath.Contains(_searchKeyWord) == false)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result.Add(redundancyInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排序列表
|
||||||
|
if (_sortMode == ESortMode.AssetPath)
|
||||||
|
{
|
||||||
|
if (_descendingSort)
|
||||||
|
return result.OrderByDescending(a => a.AssetPath).ToList();
|
||||||
|
else
|
||||||
|
return result.OrderBy(a => a.AssetPath).ToList();
|
||||||
|
}
|
||||||
|
else if(_sortMode == ESortMode.AssetType)
|
||||||
|
{
|
||||||
|
if (_descendingSort)
|
||||||
|
return result.OrderByDescending(a => a.AssetType).ToList();
|
||||||
|
else
|
||||||
|
return result.OrderBy(a => a.AssetType).ToList();
|
||||||
|
}
|
||||||
|
else if (_sortMode == ESortMode.FileSize)
|
||||||
|
{
|
||||||
|
if (_descendingSort)
|
||||||
|
return result.OrderByDescending(a => a.FileSize).ToList();
|
||||||
|
else
|
||||||
|
return result.OrderBy(a => a.FileSize).ToList();
|
||||||
|
}
|
||||||
|
else if (_sortMode == ESortMode.Number)
|
||||||
|
{
|
||||||
|
if (_descendingSort)
|
||||||
|
return result.OrderByDescending(a => a.Number).ToList();
|
||||||
|
else
|
||||||
|
return result.OrderBy(a => a.Number).ToList();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void RefreshSortingSymbol()
|
||||||
|
{
|
||||||
|
_topBar1.text = $"Asset Path ({_assetListView.itemsSource.Count})";
|
||||||
|
_topBar2.text = "Asset Type";
|
||||||
|
_topBar3.text = "File Size";
|
||||||
|
_topBar4.text = "Redundancy Num";
|
||||||
|
|
||||||
|
if (_sortMode == ESortMode.AssetPath)
|
||||||
|
{
|
||||||
|
if (_descendingSort)
|
||||||
|
_topBar1.text = $"Asset Path ({_assetListView.itemsSource.Count}) ↓";
|
||||||
|
else
|
||||||
|
_topBar1.text = $"Asset Path ({_assetListView.itemsSource.Count}) ↑";
|
||||||
|
}
|
||||||
|
else if(_sortMode == ESortMode.AssetType)
|
||||||
|
{
|
||||||
|
if (_descendingSort)
|
||||||
|
_topBar2.text = "Asset Type ↓";
|
||||||
|
else
|
||||||
|
_topBar2.text = "Asset Type ↑";
|
||||||
|
}
|
||||||
|
else if (_sortMode == ESortMode.FileSize)
|
||||||
|
{
|
||||||
|
if (_descendingSort)
|
||||||
|
_topBar3.text = "File Size ↓";
|
||||||
|
else
|
||||||
|
_topBar3.text = "File Size ↑";
|
||||||
|
}
|
||||||
|
else if (_sortMode == ESortMode.Number)
|
||||||
|
{
|
||||||
|
if (_descendingSort)
|
||||||
|
_topBar4.text = "Redundancy Num ↓";
|
||||||
|
else
|
||||||
|
_topBar4.text = "Redundancy Num ↑";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 挂接到父类页面上
|
||||||
|
/// </summary>
|
||||||
|
public void AttachParent(VisualElement parent)
|
||||||
|
{
|
||||||
|
parent.Add(_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从父类页面脱离开
|
||||||
|
/// </summary>
|
||||||
|
public void DetachParent()
|
||||||
|
{
|
||||||
|
_root.RemoveFromHierarchy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 资源列表相关
|
||||||
|
private VisualElement MakeAssetListViewItem()
|
||||||
|
{
|
||||||
|
VisualElement element = new VisualElement();
|
||||||
|
element.style.flexDirection = FlexDirection.Row;
|
||||||
|
|
||||||
|
{
|
||||||
|
var label = new Label();
|
||||||
|
label.name = "Label1";
|
||||||
|
label.style.unityTextAlign = TextAnchor.MiddleLeft;
|
||||||
|
label.style.marginLeft = 3f;
|
||||||
|
label.style.flexGrow = 1f;
|
||||||
|
label.style.width = 280;
|
||||||
|
element.Add(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var label = new Label();
|
||||||
|
label.name = "Label2";
|
||||||
|
label.style.unityTextAlign = TextAnchor.MiddleLeft;
|
||||||
|
label.style.marginLeft = 3f;
|
||||||
|
label.style.flexGrow = 0;
|
||||||
|
label.style.width = 125;
|
||||||
|
element.Add(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var label = new Label();
|
||||||
|
label.name = "Label3";
|
||||||
|
label.style.unityTextAlign = TextAnchor.MiddleLeft;
|
||||||
|
label.style.marginLeft = 3f;
|
||||||
|
label.style.flexGrow = 0;
|
||||||
|
label.style.width = 125;
|
||||||
|
element.Add(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var label = new Label();
|
||||||
|
label.name = "Label4";
|
||||||
|
label.style.unityTextAlign = TextAnchor.MiddleLeft;
|
||||||
|
label.style.marginLeft = 3f;
|
||||||
|
label.style.flexGrow = 0;
|
||||||
|
label.style.width = 125;
|
||||||
|
element.Add(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
private void BindAssetListViewItem(VisualElement element, int index)
|
||||||
|
{
|
||||||
|
var sourceData = _assetListView.itemsSource as List<ReportRedundancyInfo>;
|
||||||
|
var redundancyInfo = sourceData[index];
|
||||||
|
|
||||||
|
// Asset Path
|
||||||
|
var label1 = element.Q<Label>("Label1");
|
||||||
|
label1.text = redundancyInfo.AssetPath;
|
||||||
|
|
||||||
|
// Asset Type
|
||||||
|
var label2 = element.Q<Label>("Label2");
|
||||||
|
label2.text = redundancyInfo.AssetType;
|
||||||
|
|
||||||
|
// File Size
|
||||||
|
var label3 = element.Q<Label>("Label3");
|
||||||
|
label3.text = EditorUtility.FormatBytes(redundancyInfo.FileSize);
|
||||||
|
|
||||||
|
// Number
|
||||||
|
var label4 = element.Q<Label>("Label4");
|
||||||
|
label4.text = redundancyInfo.Number.ToString();
|
||||||
|
}
|
||||||
|
private void TopBar1_clicked()
|
||||||
|
{
|
||||||
|
if (_sortMode != ESortMode.AssetPath)
|
||||||
|
{
|
||||||
|
_sortMode = ESortMode.AssetPath;
|
||||||
|
_descendingSort = false;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_descendingSort = !_descendingSort;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void TopBar2_clicked()
|
||||||
|
{
|
||||||
|
if (_sortMode != ESortMode.AssetType)
|
||||||
|
{
|
||||||
|
_sortMode = ESortMode.AssetType;
|
||||||
|
_descendingSort = false;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_descendingSort = !_descendingSort;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void TopBar3_clicked()
|
||||||
|
{
|
||||||
|
if (_sortMode != ESortMode.FileSize)
|
||||||
|
{
|
||||||
|
_sortMode = ESortMode.FileSize;
|
||||||
|
_descendingSort = false;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_descendingSort = !_descendingSort;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void TopBar4_clicked()
|
||||||
|
{
|
||||||
|
if (_sortMode != ESortMode.Number)
|
||||||
|
{
|
||||||
|
_sortMode = ESortMode.Number;
|
||||||
|
_descendingSort = false;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_descendingSort = !_descendingSort;
|
||||||
|
RefreshView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a72c4edf1a81c9942a9d43e9d2a77b53
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,11 @@
|
||||||
|
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
|
||||||
|
<ui:VisualElement name="TopGroup" style="flex-grow: 1; border-left-width: 1px; border-right-width: 1px; border-top-width: 1px; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-right-color: rgb(0, 0, 0); border-top-color: rgb(0, 0, 0); border-bottom-color: rgb(0, 0, 0); margin-left: 0; margin-right: 0; margin-top: 2px; margin-bottom: 1px; display: flex;">
|
||||||
|
<uie:Toolbar name="TopBar" style="height: 25px; margin-left: 1px; margin-right: 1px;">
|
||||||
|
<uie:ToolbarButton text="Asset Path" display-tooltip-when-elided="true" name="TopBar1" style="width: 280px; -unity-text-align: middle-left; flex-grow: 1;" />
|
||||||
|
<uie:ToolbarButton text="Asset Type" display-tooltip-when-elided="true" name="TopBar2" style="width: 125px; -unity-text-align: middle-left; flex-grow: 0; flex-shrink: 1;" />
|
||||||
|
<uie:ToolbarButton text="File Size" display-tooltip-when-elided="true" name="TopBar3" style="width: 125px; -unity-text-align: middle-left; flex-grow: 0; flex-shrink: 1;" />
|
||||||
|
<uie:ToolbarButton text="Redundancy Num" display-tooltip-when-elided="true" name="TopBar4" style="width: 125px; -unity-text-align: middle-left; flex-grow: 0; flex-shrink: 1;" />
|
||||||
|
</uie:Toolbar>
|
||||||
|
<ui:ListView focusable="true" name="TopListView" item-height="18" virtualization-method="DynamicHeight" style="flex-grow: 1; flex-basis: 60px;" />
|
||||||
|
</ui:VisualElement>
|
||||||
|
</ui:UXML>
|
|
@ -0,0 +1,10 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a5296d9c037ce3944b5c197cbdd78a8b
|
||||||
|
ScriptedImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
|
|
@ -68,7 +68,9 @@ namespace YooAsset.Editor
|
||||||
|
|
||||||
_items.Add(new ItemWrapper("启用可寻址资源定位", $"{buildReport.Summary.EnableAddressable}"));
|
_items.Add(new ItemWrapper("启用可寻址资源定位", $"{buildReport.Summary.EnableAddressable}"));
|
||||||
_items.Add(new ItemWrapper("资源包名唯一化", $"{buildReport.Summary.UniqueBundleName}"));
|
_items.Add(new ItemWrapper("资源包名唯一化", $"{buildReport.Summary.UniqueBundleName}"));
|
||||||
_items.Add(new ItemWrapper("加密服务类名称", $"{buildReport.Summary.EncryptionServicesClassName}"));
|
_items.Add(new ItemWrapper("自动分析冗余资源", $"{buildReport.Summary.AutoAnalyzeRedundancy}"));
|
||||||
|
_items.Add(new ItemWrapper("共享资源的打包类名称", buildReport.Summary.ShareAssetPackRuleClassName));
|
||||||
|
_items.Add(new ItemWrapper("加密服务类名称", buildReport.Summary.EncryptionServicesClassName));
|
||||||
|
|
||||||
_items.Add(new ItemWrapper(string.Empty, string.Empty));
|
_items.Add(new ItemWrapper(string.Empty, string.Empty));
|
||||||
_items.Add(new ItemWrapper("构建参数", string.Empty));
|
_items.Add(new ItemWrapper("构建参数", string.Empty));
|
||||||
|
|
|
@ -286,6 +286,18 @@ namespace YooAsset.Editor
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region StringUtility
|
#region StringUtility
|
||||||
|
public static string RemoveFirstChar(string str)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(str))
|
||||||
|
return str;
|
||||||
|
return str.Substring(1);
|
||||||
|
}
|
||||||
|
public static string RemoveLastChar(string str)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(str))
|
||||||
|
return str;
|
||||||
|
return str.Substring(0, str.Length - 1);
|
||||||
|
}
|
||||||
public static List<string> StringToStringList(string str, char separator)
|
public static List<string> StringToStringList(string str, char separator)
|
||||||
{
|
{
|
||||||
List<string> result = new List<string>();
|
List<string> result = new List<string>();
|
||||||
|
@ -303,7 +315,6 @@ namespace YooAsset.Editor
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T NameToEnum<T>(string name)
|
public static T NameToEnum<T>(string name)
|
||||||
{
|
{
|
||||||
if (Enum.IsDefined(typeof(T), name) == false)
|
if (Enum.IsDefined(typeof(T), name) == false)
|
||||||
|
|
|
@ -86,9 +86,7 @@ namespace YooAsset
|
||||||
if (IsValidWithWarning == false)
|
if (IsValidWithWarning == false)
|
||||||
return null;
|
return null;
|
||||||
string filePath = Provider.RawFilePath;
|
string filePath = Provider.RawFilePath;
|
||||||
if (File.Exists(filePath) == false)
|
return FileUtility.ReadAllText(filePath);
|
||||||
return null;
|
|
||||||
return File.ReadAllText(filePath, Encoding.UTF8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -246,7 +246,7 @@ namespace YooAsset
|
||||||
var result = CacheSystem.VerifyingRecordFile(MainBundleInfo.Bundle.PackageName, MainBundleInfo.Bundle.CacheGUID);
|
var result = CacheSystem.VerifyingRecordFile(MainBundleInfo.Bundle.PackageName, MainBundleInfo.Bundle.CacheGUID);
|
||||||
if (result != EVerifyResult.Succeed)
|
if (result != EVerifyResult.Succeed)
|
||||||
{
|
{
|
||||||
YooLogger.Error($"Found possibly corrupt file ! {MainBundleInfo.Bundle.CacheGUID}");
|
YooLogger.Error($"Found possibly corrupt file ! {MainBundleInfo.Bundle.CacheGUID} Verify result : {result}");
|
||||||
CacheSystem.DiscardFile(MainBundleInfo.Bundle.PackageName, MainBundleInfo.Bundle.CacheGUID);
|
CacheSystem.DiscardFile(MainBundleInfo.Bundle.PackageName, MainBundleInfo.Bundle.CacheGUID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace YooAsset
|
||||||
{
|
{
|
||||||
internal static class PersistentTools
|
internal static class PersistentTools
|
||||||
{
|
{
|
||||||
|
private const string SandboxFolderName = "Sandbox";
|
||||||
private const string CacheFolderName = "CacheFiles";
|
private const string CacheFolderName = "CacheFiles";
|
||||||
private const string CachedBundleFileFolder = "BundleFiles";
|
private const string CachedBundleFileFolder = "BundleFiles";
|
||||||
private const string CachedRawFileFolder = "RawFiles";
|
private const string CachedRawFileFolder = "RawFiles";
|
||||||
|
@ -32,29 +33,24 @@ namespace YooAsset
|
||||||
// 注意:为了方便调试查看,编辑器下把存储目录放到项目里
|
// 注意:为了方便调试查看,编辑器下把存储目录放到项目里
|
||||||
if (string.IsNullOrEmpty(_sandboxPath))
|
if (string.IsNullOrEmpty(_sandboxPath))
|
||||||
{
|
{
|
||||||
string directory = Path.GetDirectoryName(UnityEngine.Application.dataPath);
|
string projectPath = Path.GetDirectoryName(UnityEngine.Application.dataPath);
|
||||||
string projectPath = GetRegularPath(directory);
|
projectPath = PathUtility.RegularPath(projectPath);
|
||||||
_sandboxPath = StringUtility.Format("{0}/Sandbox", projectPath);
|
_sandboxPath = PathUtility.Combine(projectPath, SandboxFolderName);
|
||||||
}
|
}
|
||||||
#elif UNITY_STANDALONE
|
#elif UNITY_STANDALONE
|
||||||
if (string.IsNullOrEmpty(_sandboxPath))
|
if (string.IsNullOrEmpty(_sandboxPath))
|
||||||
{
|
{
|
||||||
_sandboxPath = StringUtility.Format("{0}/Sandbox", UnityEngine.Application.dataPath);
|
_sandboxPath = PathUtility.Combine(UnityEngine.Application.dataPath, SandboxFolderName);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (string.IsNullOrEmpty(_sandboxPath))
|
if (string.IsNullOrEmpty(_sandboxPath))
|
||||||
{
|
{
|
||||||
_sandboxPath = StringUtility.Format("{0}/Sandbox", UnityEngine.Application.persistentDataPath);
|
_sandboxPath = PathUtility.Combine(UnityEngine.Application.persistentDataPath, SandboxFolderName);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return _sandboxPath;
|
return _sandboxPath;
|
||||||
}
|
}
|
||||||
private static string GetRegularPath(string path)
|
|
||||||
{
|
|
||||||
return path.Replace('\\', '/').Replace("\\", "/"); //替换为Linux路径格式
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取基于流文件夹的加载路径
|
/// 获取基于流文件夹的加载路径
|
||||||
|
@ -63,9 +59,9 @@ namespace YooAsset
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(_buildinPath))
|
if (string.IsNullOrEmpty(_buildinPath))
|
||||||
{
|
{
|
||||||
_buildinPath = StringUtility.Format("{0}/{1}", UnityEngine.Application.streamingAssetsPath, YooAssetSettings.StreamingAssetsBuildinFolder);
|
_buildinPath = PathUtility.Combine(UnityEngine.Application.streamingAssetsPath, YooAssetSettings.StreamingAssetsBuildinFolder);
|
||||||
}
|
}
|
||||||
return StringUtility.Format("{0}/{1}", _buildinPath, path);
|
return PathUtility.Combine(_buildinPath, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -74,7 +70,17 @@ namespace YooAsset
|
||||||
public static string MakePersistentLoadPath(string path)
|
public static string MakePersistentLoadPath(string path)
|
||||||
{
|
{
|
||||||
string root = GetPersistentRootPath();
|
string root = GetPersistentRootPath();
|
||||||
return StringUtility.Format("{0}/{1}", root, path);
|
return PathUtility.Combine(root, path);
|
||||||
|
}
|
||||||
|
public static string MakePersistentLoadPath(string path1, string path2)
|
||||||
|
{
|
||||||
|
string root = GetPersistentRootPath();
|
||||||
|
return PathUtility.Combine(root, path1, path2);
|
||||||
|
}
|
||||||
|
public static string MakePersistentLoadPath(string path1, string path2, string path3)
|
||||||
|
{
|
||||||
|
string root = GetPersistentRootPath();
|
||||||
|
return PathUtility.Combine(root, path1, path2, path3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -101,7 +107,7 @@ namespace YooAsset
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void DeleteSandbox()
|
public static void DeleteSandbox()
|
||||||
{
|
{
|
||||||
string directoryPath = MakePersistentLoadPath(string.Empty);
|
string directoryPath = GetPersistentRootPath();
|
||||||
if (Directory.Exists(directoryPath))
|
if (Directory.Exists(directoryPath))
|
||||||
Directory.Delete(directoryPath, true);
|
Directory.Delete(directoryPath, true);
|
||||||
}
|
}
|
||||||
|
@ -135,8 +141,7 @@ namespace YooAsset
|
||||||
{
|
{
|
||||||
if (_cachedBundleFileFolder.TryGetValue(packageName, out string value) == false)
|
if (_cachedBundleFileFolder.TryGetValue(packageName, out string value) == false)
|
||||||
{
|
{
|
||||||
string root = MakePersistentLoadPath(CacheFolderName);
|
value = MakePersistentLoadPath(CacheFolderName, packageName, CachedBundleFileFolder);
|
||||||
value = $"{root}/{packageName}/{CachedBundleFileFolder}";
|
|
||||||
_cachedBundleFileFolder.Add(packageName, value);
|
_cachedBundleFileFolder.Add(packageName, value);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
@ -150,8 +155,7 @@ namespace YooAsset
|
||||||
{
|
{
|
||||||
if (_cachedRawFileFolder.TryGetValue(packageName, out string value) == false)
|
if (_cachedRawFileFolder.TryGetValue(packageName, out string value) == false)
|
||||||
{
|
{
|
||||||
string root = MakePersistentLoadPath(CacheFolderName);
|
value = MakePersistentLoadPath(CacheFolderName, packageName, CachedRawFileFolder);
|
||||||
value = $"{root}/{packageName}/{CachedRawFileFolder}";
|
|
||||||
_cachedRawFileFolder.Add(packageName, value);
|
_cachedRawFileFolder.Add(packageName, value);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
@ -171,7 +175,7 @@ namespace YooAsset
|
||||||
public static string GetCacheManifestFilePath(string packageName, string packageVersion)
|
public static string GetCacheManifestFilePath(string packageName, string packageVersion)
|
||||||
{
|
{
|
||||||
string fileName = YooAssetSettingsData.GetManifestBinaryFileName(packageName, packageVersion);
|
string fileName = YooAssetSettingsData.GetManifestBinaryFileName(packageName, packageVersion);
|
||||||
return MakePersistentLoadPath($"{ManifestFolderName}/{fileName}");
|
return MakePersistentLoadPath(ManifestFolderName, fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -180,7 +184,7 @@ namespace YooAsset
|
||||||
public static string GetCachePackageHashFilePath(string packageName, string packageVersion)
|
public static string GetCachePackageHashFilePath(string packageName, string packageVersion)
|
||||||
{
|
{
|
||||||
string fileName = YooAssetSettingsData.GetPackageHashFileName(packageName, packageVersion);
|
string fileName = YooAssetSettingsData.GetPackageHashFileName(packageName, packageVersion);
|
||||||
return MakePersistentLoadPath($"{ManifestFolderName}/{fileName}");
|
return MakePersistentLoadPath(ManifestFolderName, fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -189,7 +193,7 @@ namespace YooAsset
|
||||||
public static string GetCachePackageVersionFilePath(string packageName)
|
public static string GetCachePackageVersionFilePath(string packageName)
|
||||||
{
|
{
|
||||||
string fileName = YooAssetSettingsData.GetPackageVersionFileName(packageName);
|
string fileName = YooAssetSettingsData.GetPackageVersionFileName(packageName);
|
||||||
return MakePersistentLoadPath($"{ManifestFolderName}/{fileName}");
|
return MakePersistentLoadPath(ManifestFolderName, fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -198,7 +202,7 @@ namespace YooAsset
|
||||||
public static void SaveCachePackageVersionFile(string packageName, string version)
|
public static void SaveCachePackageVersionFile(string packageName, string version)
|
||||||
{
|
{
|
||||||
string filePath = GetCachePackageVersionFilePath(packageName);
|
string filePath = GetCachePackageVersionFilePath(packageName);
|
||||||
FileUtility.CreateFile(filePath, version);
|
FileUtility.WriteAllText(filePath, version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,7 +16,7 @@ namespace YooAsset
|
||||||
public static void SerializeToJson(string savePath, PackageManifest manifest)
|
public static void SerializeToJson(string savePath, PackageManifest manifest)
|
||||||
{
|
{
|
||||||
string json = JsonUtility.ToJson(manifest, true);
|
string json = JsonUtility.ToJson(manifest, true);
|
||||||
FileUtility.CreateFile(savePath, json);
|
FileUtility.WriteAllText(savePath, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -410,7 +410,7 @@ namespace YooAsset
|
||||||
_footPrint = Application.buildGUID;
|
_footPrint = Application.buildGUID;
|
||||||
#endif
|
#endif
|
||||||
string footPrintFilePath = PersistentTools.GetAppFootPrintFilePath();
|
string footPrintFilePath = PersistentTools.GetAppFootPrintFilePath();
|
||||||
FileUtility.CreateFile(footPrintFilePath, _footPrint);
|
FileUtility.WriteAllText(footPrintFilePath, _footPrint);
|
||||||
YooLogger.Log($"Save application foot print : {_footPrint}");
|
YooLogger.Log($"Save application foot print : {_footPrint}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,12 +75,13 @@ namespace YooAsset
|
||||||
if (IsRawFile)
|
if (IsRawFile)
|
||||||
{
|
{
|
||||||
string cacheRoot = PersistentTools.GetCachedRawFileFolderPath(PackageName);
|
string cacheRoot = PersistentTools.GetCachedRawFileFolderPath(PackageName);
|
||||||
_cachedDataFilePath = $"{cacheRoot}/{folderName}/{CacheGUID}/{YooAssetSettings.CacheBundleDataFileName}{_fileExtension}";
|
_cachedDataFilePath = PathUtility.Combine(cacheRoot, folderName, CacheGUID, YooAssetSettings.CacheBundleDataFileName);
|
||||||
|
_cachedDataFilePath += _fileExtension;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string cacheRoot = PersistentTools.GetCachedBundleFileFolderPath(PackageName);
|
string cacheRoot = PersistentTools.GetCachedBundleFileFolderPath(PackageName);
|
||||||
_cachedDataFilePath = $"{cacheRoot}/{folderName}/{CacheGUID}/{YooAssetSettings.CacheBundleDataFileName}";
|
_cachedDataFilePath = PathUtility.Combine(cacheRoot, folderName, CacheGUID, YooAssetSettings.CacheBundleDataFileName);
|
||||||
}
|
}
|
||||||
return _cachedDataFilePath;
|
return _cachedDataFilePath;
|
||||||
}
|
}
|
||||||
|
@ -101,12 +102,12 @@ namespace YooAsset
|
||||||
if (IsRawFile)
|
if (IsRawFile)
|
||||||
{
|
{
|
||||||
string cacheRoot = PersistentTools.GetCachedRawFileFolderPath(PackageName);
|
string cacheRoot = PersistentTools.GetCachedRawFileFolderPath(PackageName);
|
||||||
_cachedInfoFilePath = $"{cacheRoot}/{folderName}/{CacheGUID}/{YooAssetSettings.CacheBundleInfoFileName}";
|
_cachedInfoFilePath = PathUtility.Combine(cacheRoot, folderName, CacheGUID, YooAssetSettings.CacheBundleInfoFileName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string cacheRoot = PersistentTools.GetCachedBundleFileFolderPath(PackageName);
|
string cacheRoot = PersistentTools.GetCachedBundleFileFolderPath(PackageName);
|
||||||
_cachedInfoFilePath = $"{cacheRoot}/{folderName}/{CacheGUID}/{YooAssetSettings.CacheBundleInfoFileName}";
|
_cachedInfoFilePath = PathUtility.Combine(cacheRoot, folderName, CacheGUID, YooAssetSettings.CacheBundleInfoFileName);
|
||||||
}
|
}
|
||||||
return _cachedInfoFilePath;
|
return _cachedInfoFilePath;
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,7 +114,7 @@ namespace YooAsset
|
||||||
// 添加无后缀名路径的映射
|
// 添加无后缀名路径的映射
|
||||||
if (Path.HasExtension(location))
|
if (Path.HasExtension(location))
|
||||||
{
|
{
|
||||||
string locationWithoutExtension = StringUtility.RemoveExtension(location);
|
string locationWithoutExtension = PathUtility.RemoveExtension(location);
|
||||||
if (AssetPathMapping.ContainsKey(locationWithoutExtension))
|
if (AssetPathMapping.ContainsKey(locationWithoutExtension))
|
||||||
YooLogger.Warning($"AssetPath have existed : {locationWithoutExtension}");
|
YooLogger.Warning($"AssetPath have existed : {locationWithoutExtension}");
|
||||||
else
|
else
|
||||||
|
|
|
@ -7,13 +7,67 @@ using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace YooAsset
|
namespace YooAsset
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 路径工具类
|
||||||
|
/// </summary>
|
||||||
|
internal static class PathUtility
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 路径归一化
|
||||||
|
/// 注意:替换为Linux路径格式
|
||||||
|
/// </summary>
|
||||||
|
public static string RegularPath(string path)
|
||||||
|
{
|
||||||
|
return path.Replace('\\', '/').Replace("\\", "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除路径里的后缀名
|
||||||
|
/// </summary>
|
||||||
|
public static string RemoveExtension(string str)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(str))
|
||||||
|
return str;
|
||||||
|
|
||||||
|
int index = str.LastIndexOf(".");
|
||||||
|
if (index == -1)
|
||||||
|
return str;
|
||||||
|
else
|
||||||
|
return str.Remove(index); //"assets/config/test.unity3d" --> "assets/config/test"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 合并路径
|
||||||
|
/// </summary>
|
||||||
|
public static string Combine(string path1, string path2)
|
||||||
|
{
|
||||||
|
return StringUtility.Format("{0}/{1}", path1, path2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 合并路径
|
||||||
|
/// </summary>
|
||||||
|
public static string Combine(string path1, string path2, string path3)
|
||||||
|
{
|
||||||
|
return StringUtility.Format("{0}/{1}/{2}", path1, path2, path3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 合并路径
|
||||||
|
/// </summary>
|
||||||
|
public static string Combine(string path1, string path2, string path3, string path4)
|
||||||
|
{
|
||||||
|
return StringUtility.Format("{0}/{1}/{2}/{3}", path1, path2, path3, path4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 字符串工具类
|
/// 字符串工具类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class StringUtility
|
internal static class StringUtility
|
||||||
{
|
{
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static StringBuilder _cacheBuilder = new StringBuilder(1024);
|
private static StringBuilder _cacheBuilder = new StringBuilder(2048);
|
||||||
|
|
||||||
public static string Format(string format, object arg0)
|
public static string Format(string format, object arg0)
|
||||||
{
|
{
|
||||||
|
@ -54,30 +108,6 @@ namespace YooAsset
|
||||||
_cacheBuilder.AppendFormat(format, args);
|
_cacheBuilder.AppendFormat(format, args);
|
||||||
return _cacheBuilder.ToString();
|
return _cacheBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string RemoveFirstChar(string str)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(str))
|
|
||||||
return str;
|
|
||||||
return str.Substring(1);
|
|
||||||
}
|
|
||||||
public static string RemoveLastChar(string str)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(str))
|
|
||||||
return str;
|
|
||||||
return str.Substring(0, str.Length - 1);
|
|
||||||
}
|
|
||||||
public static string RemoveExtension(string str)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(str))
|
|
||||||
return str;
|
|
||||||
|
|
||||||
int index = str.LastIndexOf(".");
|
|
||||||
if (index == -1)
|
|
||||||
return str;
|
|
||||||
else
|
|
||||||
return str.Remove(index); //"assets/config/test.unity3d" --> "assets/config/test"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -106,46 +136,26 @@ namespace YooAsset
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建文件(如果已经存在则删除旧文件)
|
/// 写入文本数据(会覆盖指定路径的文件)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void CreateFile(string filePath, string content)
|
public static void WriteAllText(string filePath, string content)
|
||||||
{
|
{
|
||||||
// 删除旧文件
|
|
||||||
if (File.Exists(filePath))
|
|
||||||
File.Delete(filePath);
|
|
||||||
|
|
||||||
// 创建文件夹路径
|
// 创建文件夹路径
|
||||||
CreateFileDirectory(filePath);
|
CreateFileDirectory(filePath);
|
||||||
|
|
||||||
// 创建新文件
|
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes(content);
|
byte[] bytes = Encoding.UTF8.GetBytes(content);
|
||||||
using (FileStream fs = File.Create(filePath))
|
File.WriteAllBytes(filePath, bytes); //避免写入BOM标记
|
||||||
{
|
|
||||||
fs.Write(bytes, 0, bytes.Length);
|
|
||||||
fs.Flush();
|
|
||||||
fs.Close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建文件(如果已经存在则删除旧文件)
|
/// 写入字节数据(会覆盖指定路径的文件)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void CreateFile(string filePath, byte[] data)
|
public static void WriteAllBytes(string filePath, byte[] data)
|
||||||
{
|
{
|
||||||
// 删除旧文件
|
|
||||||
if (File.Exists(filePath))
|
|
||||||
File.Delete(filePath);
|
|
||||||
|
|
||||||
// 创建文件夹路径
|
// 创建文件夹路径
|
||||||
CreateFileDirectory(filePath);
|
CreateFileDirectory(filePath);
|
||||||
|
|
||||||
// 创建新文件
|
File.WriteAllBytes(filePath, data);
|
||||||
using (FileStream fs = File.Create(filePath))
|
|
||||||
{
|
|
||||||
fs.Write(data, 0, data.Length);
|
|
||||||
fs.Flush();
|
|
||||||
fs.Close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -92,41 +92,29 @@ internal class FsmInitialize : IStateNode
|
||||||
{
|
{
|
||||||
//string hostServerIP = "http://10.0.2.2"; //安卓模拟器地址
|
//string hostServerIP = "http://10.0.2.2"; //安卓模拟器地址
|
||||||
string hostServerIP = "http://127.0.0.1";
|
string hostServerIP = "http://127.0.0.1";
|
||||||
string gameVersion = "v1.0";
|
string appVersion = "v1.0";
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.Android)
|
if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.Android)
|
||||||
return $"{hostServerIP}/CDN/Android/{gameVersion}";
|
return $"{hostServerIP}/CDN/Android/{appVersion}";
|
||||||
else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.iOS)
|
else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.iOS)
|
||||||
return $"{hostServerIP}/CDN/IPhone/{gameVersion}";
|
return $"{hostServerIP}/CDN/IPhone/{appVersion}";
|
||||||
else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.WebGL)
|
else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.WebGL)
|
||||||
return $"{hostServerIP}/CDN/WebGL/{gameVersion}";
|
return $"{hostServerIP}/CDN/WebGL/{appVersion}";
|
||||||
else
|
else
|
||||||
return $"{hostServerIP}/CDN/PC/{gameVersion}";
|
return $"{hostServerIP}/CDN/PC/{appVersion}";
|
||||||
#else
|
#else
|
||||||
if (Application.platform == RuntimePlatform.Android)
|
if (Application.platform == RuntimePlatform.Android)
|
||||||
return $"{hostServerIP}/CDN/Android/{gameVersion}";
|
return $"{hostServerIP}/CDN/Android/{appVersion}";
|
||||||
else if (Application.platform == RuntimePlatform.IPhonePlayer)
|
else if (Application.platform == RuntimePlatform.IPhonePlayer)
|
||||||
return $"{hostServerIP}/CDN/IPhone/{gameVersion}";
|
return $"{hostServerIP}/CDN/IPhone/{appVersion}";
|
||||||
else if (Application.platform == RuntimePlatform.WebGLPlayer)
|
else if (Application.platform == RuntimePlatform.WebGLPlayer)
|
||||||
return $"{hostServerIP}/CDN/WebGL/{gameVersion}";
|
return $"{hostServerIP}/CDN/WebGL/{appVersion}";
|
||||||
else
|
else
|
||||||
return $"{hostServerIP}/CDN/PC/{gameVersion}";
|
return $"{hostServerIP}/CDN/PC/{appVersion}";
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 内置文件查询服务类
|
|
||||||
/// </summary>
|
|
||||||
private class GameQueryServices : IQueryServices
|
|
||||||
{
|
|
||||||
public bool QueryStreamingAssets(string fileName)
|
|
||||||
{
|
|
||||||
string buildinFolderName = YooAssets.GetStreamingAssetBuildinFolderName();
|
|
||||||
return StreamingAssetsHelper.FileExists($"{buildinFolderName}/{fileName}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 资源文件解密服务类
|
/// 资源文件解密服务类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
10
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/StreamingAssetsHelper/BuildinFileManifest.cs
vendored
Normal file
10
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/StreamingAssetsHelper/BuildinFileManifest.cs
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 内置资源清单
|
||||||
|
/// </summary>
|
||||||
|
public class BuildinFileManifest : ScriptableObject
|
||||||
|
{
|
||||||
|
public List<string> BuildinFiles = new List<string>();
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 71b02dfa7aa9d4545b3417a18477fbee
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -1,134 +1,97 @@
|
||||||
//-------------------------------------
|
using System.IO;
|
||||||
// 作者:Stark
|
|
||||||
//-------------------------------------
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using YooAsset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 内置文件查询服务类
|
||||||
|
/// </summary>
|
||||||
|
public class GameQueryServices : IQueryServices
|
||||||
|
{
|
||||||
|
public bool QueryStreamingAssets(string fileName)
|
||||||
|
{
|
||||||
|
// 注意:fileName包含文件格式
|
||||||
|
return StreamingAssetsHelper.FileExists(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// StreamingAssets目录下资源查询帮助类
|
/// StreamingAssets目录下资源查询帮助类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class StreamingAssetsHelper
|
public sealed class StreamingAssetsHelper
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<string, bool> _cacheData = new Dictionary<string, bool>(1000);
|
private static bool _isInit = false;
|
||||||
|
private static readonly HashSet<string> _cacheData = new HashSet<string>();
|
||||||
|
|
||||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
private static AndroidJavaClass _unityPlayerClass;
|
public static void Init() { _isInit = true; }
|
||||||
public static AndroidJavaClass UnityPlayerClass
|
public static bool FileExists(string fileName)
|
||||||
{
|
{
|
||||||
get
|
return File.Exists(System.IO.Path.Combine(Application.streamingAssetsPath, "BuildinFiles", fileName));
|
||||||
{
|
|
||||||
if (_unityPlayerClass == null)
|
|
||||||
_unityPlayerClass = new UnityEngine.AndroidJavaClass("com.unity3d.player.UnityPlayer");
|
|
||||||
return _unityPlayerClass;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
private static AndroidJavaObject _currentActivity;
|
/// <summary>
|
||||||
public static AndroidJavaObject CurrentActivity
|
/// 初始化
|
||||||
|
/// </summary>
|
||||||
|
public static void Init()
|
||||||
{
|
{
|
||||||
get
|
if (_isInit == false)
|
||||||
{
|
{
|
||||||
if (_currentActivity == null)
|
_isInit = true;
|
||||||
_currentActivity = UnityPlayerClass.GetStatic<AndroidJavaObject>("currentActivity");
|
var manifest = Resources.Load<BuildinFileManifest>("BuildinFileManifest");
|
||||||
return _currentActivity;
|
foreach (string fileName in manifest.BuildinFiles)
|
||||||
|
{
|
||||||
|
_cacheData.Add(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 利用安卓原生接口查询内置文件是否存在
|
/// 内置文件查询方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool FileExists(string filePath)
|
public static bool FileExists(string fileName)
|
||||||
{
|
{
|
||||||
if (_cacheData.TryGetValue(filePath, out bool result) == false)
|
if (_isInit == false)
|
||||||
{
|
Init();
|
||||||
result = CurrentActivity.Call<bool>("CheckAssetExist", filePath);
|
|
||||||
_cacheData.Add(filePath, result);
|
return _cacheData.Contains(fileName);
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
public static bool FileExists(string filePath)
|
|
||||||
{
|
|
||||||
if (_cacheData.TryGetValue(filePath, out bool result) == false)
|
|
||||||
{
|
|
||||||
result = System.IO.File.Exists(System.IO.Path.Combine(Application.streamingAssetsPath, filePath));
|
|
||||||
_cacheData.Add(filePath, result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
#if UNITY_ANDROID && UNITY_EDITOR
|
internal class PreprocessBuild : UnityEditor.Build.IPreprocessBuildWithReport
|
||||||
/// <summary>
|
|
||||||
/// 为Github对开发者的友好,采用自动补充UnityPlayerActivity.java文件的通用姿势满足各个开发者
|
|
||||||
/// </summary>
|
|
||||||
internal class AndroidPost : UnityEditor.Android.IPostGenerateGradleAndroidProject
|
|
||||||
{
|
{
|
||||||
public int callbackOrder => 99;
|
public int callbackOrder { get { return 0; } }
|
||||||
public void OnPostGenerateGradleAndroidProject(string path)
|
|
||||||
{
|
|
||||||
path = path.Replace("\\", "/");
|
|
||||||
string untityActivityFilePath = $"{path}/src/main/java/com/unity3d/player/UnityPlayerActivity.java";
|
|
||||||
var readContent = System.IO.File.ReadAllLines(untityActivityFilePath);
|
|
||||||
string postContent =
|
|
||||||
" //auto-gen-function \n" +
|
|
||||||
" public boolean CheckAssetExist(String filePath) \n" +
|
|
||||||
" { \n" +
|
|
||||||
" android.content.res.AssetManager assetManager = getAssets(); \n" +
|
|
||||||
" try \n" +
|
|
||||||
" { \n" +
|
|
||||||
" java.io.InputStream inputStream = assetManager.open(filePath); \n" +
|
|
||||||
" if (null != inputStream) \n" +
|
|
||||||
" { \n" +
|
|
||||||
" inputStream.close(); \n" +
|
|
||||||
" return true; \n" +
|
|
||||||
" } \n" +
|
|
||||||
" } \n" +
|
|
||||||
" catch(java.io.IOException e) \n" +
|
|
||||||
" { \n" +
|
|
||||||
" } \n" +
|
|
||||||
" return false; \n" +
|
|
||||||
" } \n" +
|
|
||||||
"}";
|
|
||||||
|
|
||||||
if (CheckFunctionExist(readContent) == false)
|
/// <summary>
|
||||||
readContent[readContent.Length - 1] = postContent;
|
/// 在构建应用程序前处理
|
||||||
System.IO.File.WriteAllLines(untityActivityFilePath, readContent);
|
/// </summary>
|
||||||
}
|
public void OnPreprocessBuild(UnityEditor.Build.Reporting.BuildReport report)
|
||||||
private bool CheckFunctionExist(string[] contents)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < contents.Length; i++)
|
var manifest = ScriptableObject.CreateInstance<BuildinFileManifest>();
|
||||||
|
|
||||||
|
string folderPath = $"{Application.dataPath}/StreamingAssets/BuildinFiles";
|
||||||
|
DirectoryInfo root = new DirectoryInfo(folderPath);
|
||||||
|
FileInfo[] files = root.GetFiles();
|
||||||
|
foreach (var fileInfo in files)
|
||||||
{
|
{
|
||||||
if (contents[i].Contains("CheckAssetExist"))
|
if (fileInfo.Extension == ".meta")
|
||||||
{
|
continue;
|
||||||
return true;
|
if (fileInfo.Name.StartsWith("PackageManifest_"))
|
||||||
}
|
continue;
|
||||||
|
manifest.BuildinFiles.Add(fileInfo.Name);
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
string saveFilePath = "Assets/Resources/BuildinFileManifest.asset";
|
||||||
|
if (File.Exists(saveFilePath))
|
||||||
|
File.Delete(saveFilePath);
|
||||||
|
if (Directory.Exists("Assets/Resources") == false)
|
||||||
|
Directory.CreateDirectory("Assets/Resources");
|
||||||
|
UnityEditor.AssetDatabase.CreateAsset(manifest, saveFilePath);
|
||||||
|
UnityEditor.AssetDatabase.SaveAssets();
|
||||||
|
UnityEditor.AssetDatabase.Refresh();
|
||||||
|
Debug.Log($"内置资源清单保存成功 : {saveFilePath}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
|
||||||
//auto-gen-function
|
|
||||||
public boolean CheckAssetExist(String filePath)
|
|
||||||
{
|
|
||||||
android.content.res.AssetManager assetManager = getAssets();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
java.io.InputStream inputStream = assetManager.open(filePath);
|
|
||||||
if(null != inputStream)
|
|
||||||
{
|
|
||||||
inputStream.close();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(java.io.IOException e)
|
|
||||||
{
|
|
||||||
//e.printStackTrace();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*/
|
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 358145d67e230b34883002b08b23cba3
|
guid: ca0617f5ec2b4504b923e3205dc77f54
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
|
|
150
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/StreamingAssetsHelper/StreamingAssetsHelperOLD.cs
vendored
Normal file
150
Assets/YooAsset/Samples~/Space Shooter/ThirdParty/StreamingAssetsHelper/StreamingAssetsHelperOLD.cs
vendored
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
//-------------------------------------
|
||||||
|
// 作者:Stark
|
||||||
|
//-------------------------------------
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using YooAsset;
|
||||||
|
|
||||||
|
/*
|
||||||
|
/// <summary>
|
||||||
|
/// 内置文件查询服务类
|
||||||
|
/// </summary>
|
||||||
|
public class GameQueryServices : IQueryServices
|
||||||
|
{
|
||||||
|
public bool QueryStreamingAssets(string fileName)
|
||||||
|
{
|
||||||
|
string buildinFolderName = YooAssets.GetStreamingAssetBuildinFolderName();
|
||||||
|
return StreamingAssetsHelper.FileExists($"{buildinFolderName}/{fileName}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// StreamingAssets目录下资源查询帮助类
|
||||||
|
/// </summary>
|
||||||
|
public sealed class StreamingAssetsHelper
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<string, bool> _cacheData = new Dictionary<string, bool>(1000);
|
||||||
|
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
private static AndroidJavaClass _unityPlayerClass;
|
||||||
|
public static AndroidJavaClass UnityPlayerClass
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_unityPlayerClass == null)
|
||||||
|
_unityPlayerClass = new UnityEngine.AndroidJavaClass("com.unity3d.player.UnityPlayer");
|
||||||
|
return _unityPlayerClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AndroidJavaObject _currentActivity;
|
||||||
|
public static AndroidJavaObject CurrentActivity
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_currentActivity == null)
|
||||||
|
_currentActivity = UnityPlayerClass.GetStatic<AndroidJavaObject>("currentActivity");
|
||||||
|
return _currentActivity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 利用安卓原生接口查询内置文件是否存在
|
||||||
|
/// </summary>
|
||||||
|
public static bool FileExists(string filePath)
|
||||||
|
{
|
||||||
|
if (_cacheData.TryGetValue(filePath, out bool result) == false)
|
||||||
|
{
|
||||||
|
result = CurrentActivity.Call<bool>("CheckAssetExist", filePath);
|
||||||
|
_cacheData.Add(filePath, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
public static bool FileExists(string filePath)
|
||||||
|
{
|
||||||
|
if (_cacheData.TryGetValue(filePath, out bool result) == false)
|
||||||
|
{
|
||||||
|
result = System.IO.File.Exists(System.IO.Path.Combine(Application.streamingAssetsPath, filePath));
|
||||||
|
_cacheData.Add(filePath, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if UNITY_ANDROID && UNITY_EDITOR
|
||||||
|
/// <summary>
|
||||||
|
/// 为Github对开发者的友好,采用自动补充UnityPlayerActivity.java文件的通用姿势满足各个开发者
|
||||||
|
/// </summary>
|
||||||
|
internal class AndroidPost : UnityEditor.Android.IPostGenerateGradleAndroidProject
|
||||||
|
{
|
||||||
|
public int callbackOrder => 99;
|
||||||
|
public void OnPostGenerateGradleAndroidProject(string path)
|
||||||
|
{
|
||||||
|
path = path.Replace("\\", "/");
|
||||||
|
string untityActivityFilePath = $"{path}/src/main/java/com/unity3d/player/UnityPlayerActivity.java";
|
||||||
|
var readContent = System.IO.File.ReadAllLines(untityActivityFilePath);
|
||||||
|
string postContent =
|
||||||
|
" //auto-gen-function \n" +
|
||||||
|
" public boolean CheckAssetExist(String filePath) \n" +
|
||||||
|
" { \n" +
|
||||||
|
" android.content.res.AssetManager assetManager = getAssets(); \n" +
|
||||||
|
" try \n" +
|
||||||
|
" { \n" +
|
||||||
|
" java.io.InputStream inputStream = assetManager.open(filePath); \n" +
|
||||||
|
" if (null != inputStream) \n" +
|
||||||
|
" { \n" +
|
||||||
|
" inputStream.close(); \n" +
|
||||||
|
" return true; \n" +
|
||||||
|
" } \n" +
|
||||||
|
" } \n" +
|
||||||
|
" catch(java.io.IOException e) \n" +
|
||||||
|
" { \n" +
|
||||||
|
" } \n" +
|
||||||
|
" return false; \n" +
|
||||||
|
" } \n" +
|
||||||
|
"}";
|
||||||
|
|
||||||
|
if (CheckFunctionExist(readContent) == false)
|
||||||
|
readContent[readContent.Length - 1] = postContent;
|
||||||
|
System.IO.File.WriteAllLines(untityActivityFilePath, readContent);
|
||||||
|
}
|
||||||
|
private bool CheckFunctionExist(string[] contents)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < contents.Length; i++)
|
||||||
|
{
|
||||||
|
if (contents[i].Contains("CheckAssetExist"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
//auto-gen-function
|
||||||
|
public boolean CheckAssetExist(String filePath)
|
||||||
|
{
|
||||||
|
android.content.res.AssetManager assetManager = getAssets();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
java.io.InputStream inputStream = assetManager.open(filePath);
|
||||||
|
if(null != inputStream)
|
||||||
|
{
|
||||||
|
inputStream.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(java.io.IOException e)
|
||||||
|
{
|
||||||
|
//e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*/
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 358145d67e230b34883002b08b23cba3
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "com.tuyoogame.yooasset",
|
"name": "com.tuyoogame.yooasset",
|
||||||
"displayName": "YooAsset",
|
"displayName": "YooAsset",
|
||||||
"version": "1.4.15",
|
"version": "1.4.16",
|
||||||
"unity": "2019.4",
|
"unity": "2019.4",
|
||||||
"description": "unity3d resources management system.",
|
"description": "unity3d resources management system.",
|
||||||
"author": {
|
"author": {
|
||||||
|
|
Loading…
Reference in New Issue