Initial commit

This commit is contained in:
hevinci
2022-03-01 10:44:12 +08:00
parent 1d54164bfb
commit e66853fd46
237 changed files with 14462 additions and 2 deletions
@@ -0,0 +1,194 @@
using System;
using System.Linq;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace YooAsset.Editor
{
public class TaskBuilding : IBuildTask
{
public class UnityManifestContext : IContextObject
{
public AssetBundleManifest UnityManifest;
}
void IBuildTask.Run(BuildContext context)
{
var buildParametersContext = context.GetContextObject<AssetBundleBuilder.BuildParametersContext>();
var buildMapContext = context.GetContextObject<TaskGetBuildMap.BuildMapContext>();
Debug.Log($"开始构建......");
BuildAssetBundleOptions opt = buildParametersContext.GetPipelineBuildOptions();
AssetBundleManifest unityManifest = BuildPipeline.BuildAssetBundles(buildParametersContext.PipelineOutputDirectory, buildMapContext.GetPipelineBuilds(), opt, buildParametersContext.Parameters.BuildTarget);
if (unityManifest == null)
throw new Exception("构建过程中发生错误!");
UnityManifestContext unityManifestContext = new UnityManifestContext();
unityManifestContext.UnityManifest = unityManifest;
context.SetContextObject(unityManifestContext);
// 拷贝原生文件
foreach (var bundleInfo in buildMapContext.BundleInfos)
{
if (bundleInfo.IsRawFile)
{
string dest = $"{buildParametersContext.PipelineOutputDirectory}/{bundleInfo.BundleName}";
foreach(var buildAsset in bundleInfo.Assets)
{
if(buildAsset.IsRawAsset)
EditorTools.CopyFile(buildAsset.AssetPath, dest, true);
}
}
}
// 验证构建结果
if (buildParametersContext.Parameters.IsVerifyBuildingResult)
{
VerifyingBuildingResult(context, unityManifest);
}
}
/// <summary>
/// 验证构建结果
/// </summary>
private void VerifyingBuildingResult(BuildContext context, AssetBundleManifest unityManifest)
{
var buildParameters = context.GetContextObject<AssetBundleBuilder.BuildParametersContext>();
var buildMapContext = context.GetContextObject<TaskGetBuildMap.BuildMapContext>();
string[] buildedBundles = unityManifest.GetAllAssetBundles();
// 1. 过滤掉原生Bundle
List<BuildBundleInfo> expectBundles = new List<BuildBundleInfo>(buildedBundles.Length);
foreach(var bundleInfo in buildMapContext.BundleInfos)
{
if (bundleInfo.IsRawFile == false)
expectBundles.Add(bundleInfo);
}
// 2. 验证数量
if (buildedBundles.Length != expectBundles.Count)
{
Debug.LogWarning($"构建过程中可能存在无效的资源,导致和预期构建的Bundle数量不一致!");
}
// 3. 正向验证Bundle
foreach (var bundleName in buildedBundles)
{
if (buildMapContext.IsContainsBundle(bundleName) == false)
{
throw new Exception($"Should never get here !");
}
}
// 4. 反向验证Bundle
bool isPass = true;
foreach (var expectBundle in expectBundles)
{
bool isMatch = false;
foreach (var buildedBundle in buildedBundles)
{
if (buildedBundle == expectBundle.BundleName)
{
isMatch = true;
break;
}
}
if (isMatch == false)
{
isPass = false;
Debug.LogWarning($"没有找到预期构建的Bundle文件 : {expectBundle.BundleName}");
}
}
if(isPass == false)
{
throw new Exception("构建结果验证没有通过,请参考警告日志!");
}
// 5. 验证Asset
int progressValue = 0;
foreach (var buildedBundle in buildedBundles)
{
string filePath = $"{buildParameters.PipelineOutputDirectory}/{buildedBundle}";
string[] allBuildinAssetPaths = GetAssetBundleAllAssets(filePath);
string[] expectBuildinAssetPaths = buildMapContext.GetBuildinAssetPaths(buildedBundle);
if (expectBuildinAssetPaths.Length != allBuildinAssetPaths.Length)
{
Debug.LogWarning($"构建的Bundle文件内的资源对象数量和预期不匹配 : {buildedBundle}");
isPass = false;
continue;
}
foreach (var buildinAssetPath in allBuildinAssetPaths)
{
var guid = AssetDatabase.AssetPathToGUID(buildinAssetPath);
if (string.IsNullOrEmpty(guid))
{
Debug.LogWarning($"无效的资源路径,请检查路径是否带有特殊符号或中文:{buildinAssetPath}");
isPass = false;
continue;
}
bool isMatch = false;
foreach (var exceptBuildAssetPath in expectBuildinAssetPaths)
{
var guidExcept = AssetDatabase.AssetPathToGUID(exceptBuildAssetPath);
if (guid == guidExcept)
{
isMatch = true;
break;
}
}
if (isMatch == false)
{
Debug.LogWarning($"在构建的Bundle文件里发现了没有匹配的资源对象:{buildinAssetPath}");
isPass = false;
continue;
}
}
EditorTools.DisplayProgressBar("验证构建结果", ++progressValue, buildedBundles.Length);
}
EditorTools.ClearProgressBar();
if (isPass == false)
{
throw new Exception("构建结果验证没有通过,请参考警告日志!");
}
// 卸载所有加载的Bundle
Debug.Log("构建结果验证成功!");
}
/// <summary>
/// 解析.manifest文件并获取资源列表
/// </summary>
private string[] GetAssetBundleAllAssets(string filePath)
{
string manifestFilePath = $"{filePath}.manifest";
List<string> assetLines = new List<string>();
using (StreamReader reader = File.OpenText(manifestFilePath))
{
string content;
bool findTarget = false;
while (null != (content = reader.ReadLine()))
{
if (content.StartsWith("Dependencies:"))
break;
if (findTarget == false && content.StartsWith("Assets:"))
findTarget = true;
if (findTarget)
{
if (content.StartsWith("- "))
{
string assetPath = content.TrimStart("- ".ToCharArray());
assetLines.Add(assetPath);
}
}
}
}
return assetLines.ToArray();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9466e826c135e994c84961e4b921ca5f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,64 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace YooAsset.Editor
{
/// <summary>
/// 拷贝内置文件到StreamingAssets
/// </summary>
public class TaskCopyBuildinFiles : IBuildTask
{
void IBuildTask.Run(BuildContext context)
{
// 注意:我们只有在强制重建的时候才会拷贝
var buildParameters = context.GetContextObject<AssetBundleBuilder.BuildParametersContext>();
if(buildParameters.Parameters.IsForceRebuild)
{
// 清空流目录
AssetBundleBuilderHelper.ClearStreamingAssetsFolder();
// 拷贝内置文件
var pipelineOutputDirectory = buildParameters.PipelineOutputDirectory;
CopyBuildinFilesToStreaming(pipelineOutputDirectory);
}
}
private void CopyBuildinFilesToStreaming(string pipelineOutputDirectory)
{
// 加载补丁清单
PatchManifest patchManifest = AssetBundleBuilderHelper.LoadPatchManifestFile(pipelineOutputDirectory);
// 拷贝文件列表
foreach (var patchBundle in patchManifest.BundleList)
{
if (patchBundle.IsBuildin == false)
continue;
string sourcePath = $"{pipelineOutputDirectory}/{patchBundle.BundleName}";
string destPath = $"{Application.dataPath}/StreamingAssets/{patchBundle.Hash}";
Debug.Log($"拷贝内置文件到流目录:{patchBundle.BundleName}");
EditorTools.CopyFile(sourcePath, destPath, true);
}
// 拷贝清单文件
{
string sourcePath = $"{pipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestFileName}";
string destPath = $"{Application.dataPath}/StreamingAssets/{ResourceSettingData.Setting.PatchManifestFileName}";
EditorTools.CopyFile(sourcePath, destPath, true);
}
// 拷贝清单哈希文件
{
string sourcePath = $"{pipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestHashFileName}";
string destPath = $"{Application.dataPath}/StreamingAssets/{ResourceSettingData.Setting.PatchManifestHashFileName}";
EditorTools.CopyFile(sourcePath, destPath, true);
}
// 刷新目录
AssetDatabase.Refresh();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5c77e17c3a3a57548a218f1cd26f5a55
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,155 @@
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using YooAsset.Utility;
namespace YooAsset.Editor
{
/// <summary>
/// 创建补丁清单文件
/// </summary>
public class TaskCreatePatchManifest : IBuildTask
{
void IBuildTask.Run(BuildContext context)
{
var buildParameters = context.GetContextObject<AssetBundleBuilder.BuildParametersContext>();
var encryptionContext = context.GetContextObject<TaskEncryption.EncryptionContext>();
var buildMapContext = context.GetContextObject<TaskGetBuildMap.BuildMapContext>();
CreatePatchManifestFile(buildParameters, buildMapContext, encryptionContext);
}
/// <summary>
/// 创建补丁清单文件到输出目录
/// </summary>
private void CreatePatchManifestFile(AssetBundleBuilder.BuildParametersContext buildParameters,
TaskGetBuildMap.BuildMapContext buildMapContext, TaskEncryption.EncryptionContext encryptionContext)
{
// 创建新补丁清单
PatchManifest patchManifest = new PatchManifest();
patchManifest.ResourceVersion = buildParameters.Parameters.BuildVersion;
patchManifest.BuildinTags = buildParameters.Parameters.BuildinTags;
patchManifest.BundleList = GetAllPatchBundle(buildParameters, buildMapContext, encryptionContext);
patchManifest.AssetList = GetAllPatchAsset(buildMapContext, patchManifest.BundleList);
// 创建补丁清单文件
string manifestFilePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestFileName}";
UnityEngine.Debug.Log($"创建补丁清单文件:{manifestFilePath}");
PatchManifest.Serialize(manifestFilePath, patchManifest);
// 创建补丁清单哈希文件
string manifestHashFilePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestHashFileName}";
string manifestHash = HashUtility.FileMD5(manifestFilePath);
UnityEngine.Debug.Log($"创建补丁清单哈希文件:{manifestHashFilePath}");
FileUtility.CreateFile(manifestHashFilePath, manifestHash);
}
/// <summary>
/// 获取资源包列表
/// </summary>
private List<PatchBundle> GetAllPatchBundle(AssetBundleBuilder.BuildParametersContext buildParameters,
TaskGetBuildMap.BuildMapContext buildMapContext, TaskEncryption.EncryptionContext encryptionContext)
{
List<PatchBundle> result = new List<PatchBundle>(1000);
// 内置标记列表
List<string> buildinTags = buildParameters.Parameters.GetBuildinTags();
// 加载旧补丁清单
PatchManifest oldPatchManifest = null;
if (buildParameters.Parameters.IsForceRebuild == false)
{
oldPatchManifest = AssetBundleBuilderHelper.LoadPatchManifestFile(buildParameters.PipelineOutputDirectory);
}
foreach (var bundleInfo in buildMapContext.BundleInfos)
{
var bundleName = bundleInfo.BundleName;
string filePath = $"{buildParameters.PipelineOutputDirectory}/{bundleName}";
string hash = HashUtility.FileMD5(filePath);
string crc = HashUtility.FileCRC32(filePath);
long size = FileUtility.GetFileSize(filePath);
int version = buildParameters.Parameters.BuildVersion;
string[] tags = buildMapContext.GetAssetTags(bundleName);
bool isEncrypted = encryptionContext.IsEncryptFile(bundleName);
bool isBuildin = IsBuildinBundle(tags, buildinTags);
bool isRawFile = bundleInfo.IsRawFile;
// 附加文件扩展名
if (buildParameters.Parameters.AppendFileExtension)
{
hash += bundleInfo.GetAppendExtension();
}
// 注意:如果文件没有变化使用旧版本号
if (oldPatchManifest != null && oldPatchManifest.Bundles.TryGetValue(bundleName, out PatchBundle value))
{
if (value.Hash == hash)
version = value.Version;
}
PatchBundle patchBundle = new PatchBundle(bundleName, hash, crc, size, version, tags);
patchBundle.SetFlagsValue(isEncrypted, isBuildin, isRawFile);
result.Add(patchBundle);
}
return result;
}
private bool IsBuildinBundle(string[] bundleTags, List<string> buildinTags)
{
// 注意:没有任何标记的Bundle文件默认为内置文件
if (bundleTags.Length == 0)
return true;
foreach (var tag in bundleTags)
{
if (buildinTags.Contains(tag))
return true;
}
return false;
}
/// <summary>
/// 获取资源列表
/// </summary>
private List<PatchAsset> GetAllPatchAsset(TaskGetBuildMap.BuildMapContext buildMapContext, List<PatchBundle> bundleList)
{
List<PatchAsset> result = new List<PatchAsset>(1000);
foreach (var bundleInfo in buildMapContext.BundleInfos)
{
var assetInfos = bundleInfo.GetCollectAssetInfos();
foreach (var assetInfo in assetInfos)
{
PatchAsset patchAsset = new PatchAsset();
patchAsset.AssetPath = assetInfo.AssetPath;
patchAsset.BundleID = GetAssetBundleID(assetInfo.GetBundleName(), bundleList);
patchAsset.DependIDs = GetAssetBundleDependIDs(assetInfo, bundleList);
result.Add(patchAsset);
}
}
return result;
}
private int[] GetAssetBundleDependIDs(BuildAssetInfo assetInfo, List<PatchBundle> bundleList)
{
List<int> result = new List<int>();
foreach (var dependAssetInfo in assetInfo.AllDependAssetInfos)
{
if (dependAssetInfo.CheckBundleNameValid() == false)
continue;
int bundleID = GetAssetBundleID(dependAssetInfo.GetBundleName(), bundleList);
if (result.Contains(bundleID) == false)
result.Add(bundleID);
}
return result.ToArray();
}
private int GetAssetBundleID(string bundleName, List<PatchBundle> bundleList)
{
for (int index = 0; index < bundleList.Count; index++)
{
if (bundleList[index].BundleName == bundleName)
return index;
}
throw new Exception($"Not found bundle name : {bundleName}");
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 566c37f5a59f2e84397a9527981a7310
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,83 @@
using System.Collections;
using System.Collections.Generic;
namespace YooAsset.Editor
{
/// <summary>
/// 制作补丁包
/// </summary>
public class TaskCreatePatchPackage : IBuildTask
{
void IBuildTask.Run(BuildContext context)
{
var buildParameters = context.GetContextObject<AssetBundleBuilder.BuildParametersContext>();
CopyPatchFiles(buildParameters);
}
/// <summary>
/// 拷贝补丁文件到补丁包目录
/// </summary>
private void CopyPatchFiles(AssetBundleBuilder.BuildParametersContext buildParameters)
{
string packageDirectory = buildParameters.GetPackageDirectory();
UnityEngine.Debug.Log($"开始拷贝补丁文件到补丁包目录:{packageDirectory}");
// 拷贝Readme文件
{
string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.ReadmeFileName}";
string destPath = $"{packageDirectory}/{ResourceSettingData.Setting.ReadmeFileName}";
EditorTools.CopyFile(sourcePath, destPath, true);
UnityEngine.Debug.Log($"拷贝Readme文件到:{destPath}");
}
// 拷贝PatchManifest文件
{
string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestFileName}";
string destPath = $"{packageDirectory}/{ResourceSettingData.Setting.PatchManifestFileName}";
EditorTools.CopyFile(sourcePath, destPath, true);
UnityEngine.Debug.Log($"拷贝PatchManifest文件到:{destPath}");
}
// 拷贝PatchManifest哈希文件
{
string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestHashFileName}";
string destPath = $"{packageDirectory}/{ResourceSettingData.Setting.PatchManifestHashFileName}";
EditorTools.CopyFile(sourcePath, destPath, true);
UnityEngine.Debug.Log($"拷贝PatchManifest哈希文件到:{destPath}");
}
// 拷贝UnityManifest序列化文件
{
string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.UnityManifestFileName}";
string destPath = $"{packageDirectory}/{ResourceSettingData.Setting.UnityManifestFileName}";
EditorTools.CopyFile(sourcePath, destPath, true);
UnityEngine.Debug.Log($"拷贝UnityManifest文件到:{destPath}");
}
// 拷贝UnityManifest文本文件
{
string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.UnityManifestFileName}.manifest";
string destPath = $"{packageDirectory}/{ResourceSettingData.Setting.UnityManifestFileName}.manifest";
EditorTools.CopyFile(sourcePath, destPath, true);
}
// 拷贝所有补丁文件
// 注意:拷贝的补丁文件都是需要玩家热更新的文件
int progressValue = 0;
PatchManifest patchManifest = AssetBundleBuilderHelper.LoadPatchManifestFile(buildParameters.PipelineOutputDirectory);
int patchFileTotalCount = patchManifest.BundleList.Count;
foreach (var patchBundle in patchManifest.BundleList)
{
if (patchBundle.Version == buildParameters.Parameters.BuildVersion)
{
string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{patchBundle.BundleName}";
string destPath = $"{packageDirectory}/{patchBundle.Hash}";
EditorTools.CopyFile(sourcePath, destPath, true);
UnityEngine.Debug.Log($"拷贝补丁文件到补丁包:{patchBundle.BundleName}");
EditorTools.DisplayProgressBar("拷贝补丁文件", ++progressValue, patchFileTotalCount);
}
}
EditorTools.ClearProgressBar();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d110092f543e3fe40a4d3882e1a718e8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,205 @@
using System;
using System.IO;
using System.Text;
using System.Collections;
using System.Collections.Generic;
namespace YooAsset.Editor
{
/// <summary>
/// 创建说明文件
/// </summary>
public class TaskCreateReadme : IBuildTask
{
void IBuildTask.Run(BuildContext context)
{
var buildParameters = context.GetContextObject<AssetBundleBuilder.BuildParametersContext>();
var buildMapContext = context.GetContextObject<TaskGetBuildMap.BuildMapContext>();
CreateReadmeFile(buildParameters, buildMapContext);
}
/// <summary>
/// 创建Readme文件到输出目录
/// </summary>
private void CreateReadmeFile(AssetBundleBuilder.BuildParametersContext buildParameters, TaskGetBuildMap.BuildMapContext buildMapContext)
{
PatchManifest patchManifest = AssetBundleBuilderHelper.LoadPatchManifestFile(buildParameters.PipelineOutputDirectory);
// 删除旧文件
string filePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.ReadmeFileName}";
if (File.Exists(filePath))
File.Delete(filePath);
UnityEngine.Debug.Log($"创建说明文件:{filePath}");
StringBuilder content = new StringBuilder();
AppendData(content, $"构建时间:{DateTime.Now}");
AppendData(content, $"构建平台:{buildParameters.Parameters.BuildTarget}");
AppendData(content, $"构建版本:{buildParameters.Parameters.BuildVersion}");
AppendData(content, $"冗余机制:{buildParameters.Parameters.ApplyRedundancy}");
AppendData(content, "");
AppendData(content, $"--着色器--");
AppendData(content, $"IsCollectAllShaders:{AssetBundleCollectorSettingData.Setting.IsCollectAllShaders}");
AppendData(content, $"ShadersBundleName:{AssetBundleCollectorSettingData.Setting.ShadersBundleName}");
AppendData(content, "");
AppendData(content, $"--配置信息--");
for (int i = 0; i < AssetBundleCollectorSettingData.Setting.Collectors.Count; i++)
{
AssetBundleCollectorSetting.Collector wrapper = AssetBundleCollectorSettingData.Setting.Collectors[i];
AppendData(content, wrapper.ToString());
}
AppendData(content, "");
AppendData(content, $"--构建参数--");
AppendData(content, $"CompressOption:{buildParameters.Parameters.CompressOption}");
AppendData(content, $"IsForceRebuild:{buildParameters.Parameters.IsForceRebuild}");
AppendData(content, $"BuildinTags:{buildParameters.Parameters.BuildinTags}");
AppendData(content, $"IsAppendHash:{buildParameters.Parameters.IsAppendHash}");
AppendData(content, $"IsDisableWriteTypeTree:{buildParameters.Parameters.IsDisableWriteTypeTree}");
AppendData(content, $"IsIgnoreTypeTreeChanges:{buildParameters.Parameters.IsIgnoreTypeTreeChanges}");
AppendData(content, $"IsDisableLoadAssetByFileName : {buildParameters.Parameters.IsDisableLoadAssetByFileName}");
AppendData(content, "");
AppendData(content, $"--构建信息--");
AppendData(content, $"参与构建的资源总数:{buildMapContext.GetAllAssets().Count}");
GetBundleFileCountAndTotalSize(patchManifest, out int fileCount1, out long fileTotalSize1);
AppendData(content, $"构建的资源包总数:{fileCount1} 文件总大小:{fileTotalSize1 / (1024 * 1024)}MB");
GetBuildinFileCountAndTotalSize(patchManifest, out int fileCount2, out long fileTotalSize2);
AppendData(content, $"内置的资源包总数:{fileCount2} 文件总大小:{fileTotalSize2 / (1024 * 1024)}MB");
GetNotBuildinFileCountAndTotalSize(patchManifest, out int fileCount3, out long fileTotalSize3);
AppendData(content, $"非内置的资源包总数:{fileCount3} 文件总大小:{fileTotalSize3 / (1024 * 1024)}MB");
GetEncryptedFileCountAndTotalSize(patchManifest, out int fileCount4, out long fileTotalSize4);
AppendData(content, $"加密的资源包总数:{fileCount4} 文件总大小:{fileTotalSize4 / (1024 * 1024)}MB");
GetRawFileCountAndTotalSize(patchManifest, out int fileCount5, out long fileTotalSize5);
AppendData(content, $"原生的资源包总数:{fileCount5} 文件总大小:{fileTotalSize5 / (1024 * 1024)}MB");
AppendData(content, "");
AppendData(content, $"--冗余列表--");
for (int i = 0; i < buildMapContext.RedundancyList.Count; i++)
{
string redundancyAssetPath = buildMapContext.RedundancyList[i];
AppendData(content, redundancyAssetPath);
}
AppendData(content, "");
AppendData(content, $"--构建列表--");
for (int i = 0; i < buildMapContext.BundleInfos.Count; i++)
{
string bundleName = buildMapContext.BundleInfos[i].BundleName;
AppendData(content, bundleName);
}
AppendData(content, "");
AppendData(content, $"--内置文件列表--");
foreach (var patchBundle in patchManifest.BundleList)
{
if (patchBundle.IsBuildin)
{
AppendData(content, patchBundle.BundleName);
}
}
AppendData(content, "");
AppendData(content, $"--非内置文件列表--");
foreach (var patchBundle in patchManifest.BundleList)
{
if (patchBundle.IsBuildin == false)
{
AppendData(content, patchBundle.BundleName);
}
}
AppendData(content, "");
AppendData(content, $"--加密文件列表--");
foreach (var patchBundle in patchManifest.BundleList)
{
if (patchBundle.IsEncrypted)
{
AppendData(content, patchBundle.BundleName);
}
}
AppendData(content, "");
AppendData(content, $"--原生文件列表--");
foreach (var patchBundle in patchManifest.BundleList)
{
if (patchBundle.IsRawFile)
{
AppendData(content, patchBundle.BundleName);
}
}
// 创建新文件
File.WriteAllText(filePath, content.ToString(), Encoding.UTF8);
}
private void AppendData(StringBuilder sb, string data)
{
sb.Append(data);
sb.Append("\r\n");
}
private void GetBundleFileCountAndTotalSize(PatchManifest patchManifest, out int fileCount, out long fileBytes)
{
fileCount = patchManifest.BundleList.Count;
fileBytes = 0;
foreach (var patchBundle in patchManifest.BundleList)
{
fileBytes += patchBundle.SizeBytes;
}
}
private void GetBuildinFileCountAndTotalSize(PatchManifest patchManifest, out int fileCount, out long fileBytes)
{
fileCount = 0;
fileBytes = 0;
foreach (var patchBundle in patchManifest.BundleList)
{
if (patchBundle.IsBuildin)
{
fileCount++;
fileBytes += patchBundle.SizeBytes;
}
}
}
private void GetNotBuildinFileCountAndTotalSize(PatchManifest patchManifest, out int fileCount, out long fileBytes)
{
fileCount = 0;
fileBytes = 0;
foreach (var patchBundle in patchManifest.BundleList)
{
if (patchBundle.IsBuildin == false)
{
fileCount++;
fileBytes += patchBundle.SizeBytes;
}
}
}
private void GetEncryptedFileCountAndTotalSize(PatchManifest patchManifest, out int fileCount, out long fileBytes)
{
fileCount = 0;
fileBytes = 0;
foreach (var patchBundle in patchManifest.BundleList)
{
if (patchBundle.IsEncrypted)
{
fileCount++;
fileBytes += patchBundle.SizeBytes;
}
}
}
private void GetRawFileCountAndTotalSize(PatchManifest patchManifest, out int fileCount, out long fileBytes)
{
fileCount = 0;
fileBytes = 0;
foreach (var patchBundle in patchManifest.BundleList)
{
if (patchBundle.IsRawFile)
{
fileCount++;
fileBytes += patchBundle.SizeBytes;
}
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1660a082a6449f4429fcca15e4383f0b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,94 @@
using System;
using System.Linq;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using YooAsset.Utility;
namespace YooAsset.Editor
{
public class TaskEncryption : IBuildTask
{
public class EncryptionContext : IContextObject
{
public List<string> EncryptList;
/// <summary>
/// 检测是否为加密文件
/// </summary>
public bool IsEncryptFile(string bundleName)
{
return EncryptList.Contains(bundleName);
}
}
void IBuildTask.Run(BuildContext context)
{
var buildParameters = context.GetContextObject<AssetBundleBuilder.BuildParametersContext>();
var buildMapContext = context.GetContextObject<TaskGetBuildMap.BuildMapContext>();
var encrypter = CreateAssetEncrypter();
List<string> encryptList = EncryptFiles(encrypter, buildParameters, buildMapContext);
EncryptionContext encryptionContext = new EncryptionContext();
encryptionContext.EncryptList = encryptList;
context.SetContextObject(encryptionContext);
}
/// <summary>
/// 创建加密类
/// </summary>
/// <returns>如果没有定义类型,则返回NULL</returns>
private IAssetEncrypter CreateAssetEncrypter()
{
var types = AssemblyUtility.GetAssignableTypes(AssemblyUtility.UnityDefaultAssemblyEditorName, typeof(IAssetEncrypter));
if (types.Count == 0)
return null;
if (types.Count != 1)
throw new Exception($"Found more {nameof(IAssetEncrypter)} types. We only support one.");
UnityEngine.Debug.Log($"创建实例类 : {types[0].FullName}");
return (IAssetEncrypter)Activator.CreateInstance(types[0]);
}
/// <summary>
/// 加密文件
/// </summary>
private List<string> EncryptFiles(IAssetEncrypter encrypter, AssetBundleBuilder.BuildParametersContext buildParameters, TaskGetBuildMap.BuildMapContext buildMapContext)
{
// 加密资源列表
List<string> encryptList = new List<string>();
// 如果没有设置加密类
if (encrypter == null)
return encryptList;
UnityEngine.Debug.Log($"开始加密资源文件");
int progressValue = 0;
foreach (var bundleInfo in buildMapContext.BundleInfos)
{
var bundleName = bundleInfo.BundleName;
string filePath = $"{buildParameters.PipelineOutputDirectory}/{bundleName}";
if (encrypter.Check(filePath))
{
encryptList.Add(bundleName);
// 注意:通过判断文件合法性,规避重复加密一个文件
byte[] fileData = File.ReadAllBytes(filePath);
if (EditorTools.CheckBundleFileValid(fileData))
{
byte[] bytes = encrypter.Encrypt(fileData);
File.WriteAllBytes(filePath, bytes);
UnityEngine.Debug.Log($"文件加密完成:{filePath}");
}
}
// 进度条
EditorTools.DisplayProgressBar("加密资源包", ++progressValue, buildMapContext.BundleInfos.Count);
}
EditorTools.ClearProgressBar();
return encryptList;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c78d471226b9c8d429a2d962370f480b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,331 @@
using System;
using System.IO;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using YooAsset.Utility;
namespace YooAsset.Editor
{
public class TaskGetBuildMap : IBuildTask
{
public class BuildMapContext : IContextObject
{
/// <summary>
/// 资源包列表
/// </summary>
public readonly List<BuildBundleInfo> BundleInfos = new List<BuildBundleInfo>(1000);
/// <summary>
/// 冗余的资源列表
/// </summary>
public readonly List<string> RedundancyList = new List<string>(1000);
/// <summary>
/// 添加一个打包资源
/// </summary>
public void PackAsset(BuildAssetInfo assetInfo)
{
if (TryGetBundleInfo(assetInfo.GetBundleName(), out BuildBundleInfo bundleInfo))
{
bundleInfo.PackAsset(assetInfo);
}
else
{
BuildBundleInfo newBundleInfo = new BuildBundleInfo(assetInfo.BundleLabel, assetInfo.BundleVariant);
newBundleInfo.PackAsset(assetInfo);
BundleInfos.Add(newBundleInfo);
}
}
/// <summary>
/// 获取所有的打包资源
/// </summary>
public List<BuildAssetInfo> GetAllAssets()
{
List<BuildAssetInfo> result = new List<BuildAssetInfo>(BundleInfos.Count);
foreach (var bundleInfo in BundleInfos)
{
result.AddRange(bundleInfo.Assets);
}
return result;
}
/// <summary>
/// 获取AssetBundle内包含的标记列表
/// </summary>
public string[] GetAssetTags(string bundleFullName)
{
if (TryGetBundleInfo(bundleFullName, out BuildBundleInfo bundleInfo))
{
return bundleInfo.GetAssetTags();
}
throw new Exception($"Not found {nameof(BuildBundleInfo)} : {bundleFullName}");
}
/// <summary>
/// 获取AssetBundle内收集的资源路径列表
/// </summary>
public string[] GetCollectAssetPaths(string bundleFullName)
{
if (TryGetBundleInfo(bundleFullName, out BuildBundleInfo bundleInfo))
{
return bundleInfo.GetCollectAssetPaths();
}
throw new Exception($"Not found {nameof(BuildBundleInfo)} : {bundleFullName}");
}
/// <summary>
/// 获取AssetBundle内构建的资源路径列表
/// </summary>
public string[] GetBuildinAssetPaths(string bundleFullName)
{
if (TryGetBundleInfo(bundleFullName, out BuildBundleInfo bundleInfo))
{
return bundleInfo.GetBuildinAssetPaths();
}
throw new Exception($"Not found {nameof(BuildBundleInfo)} : {bundleFullName}");
}
/// <summary>
/// 获取构建管线里需要的数据
/// </summary>
public UnityEditor.AssetBundleBuild[] GetPipelineBuilds()
{
List<AssetBundleBuild> builds = new List<AssetBundleBuild>(BundleInfos.Count);
foreach (var bundleInfo in BundleInfos)
{
if (bundleInfo.IsRawFile == false)
builds.Add(bundleInfo.CreatePipelineBuild());
}
return builds.ToArray();
}
/// <summary>
/// 检测是否包含BundleName
/// </summary>
public bool IsContainsBundle(string bundleFullName)
{
return TryGetBundleInfo(bundleFullName, out BuildBundleInfo bundleInfo);
}
private bool TryGetBundleInfo(string bundleFullName, out BuildBundleInfo result)
{
foreach (var bundleInfo in BundleInfos)
{
if (bundleInfo.BundleName == bundleFullName)
{
result = bundleInfo;
return true;
}
}
result = null;
return false;
}
}
void IBuildTask.Run(BuildContext context)
{
var buildParametersContext = context.GetContextObject<AssetBundleBuilder.BuildParametersContext>();
BuildMapContext buildMapContext = new BuildMapContext();
context.SetContextObject(buildMapContext);
SetupBuildMap(buildMapContext, buildParametersContext);
// 检测构建结果
CheckBuildMapContent(buildMapContext);
}
/// <summary>
/// 组织构建的资源包
/// </summary>
private void SetupBuildMap(BuildMapContext buildMapContext, AssetBundleBuilder.BuildParametersContext buildParameters)
{
Dictionary<string, BuildAssetInfo> buildAssets = new Dictionary<string, BuildAssetInfo>();
// 1. 获取主动收集的资源
List<AssetCollectInfo> allCollectInfos = AssetBundleCollectorSettingData.GetAllCollectAssets();
// 2. 对收集的资源进行依赖分析
int progressValue = 0;
foreach (AssetCollectInfo collectInfo in allCollectInfos)
{
string mainAssetPath = collectInfo.AssetPath;
// 获取所有依赖资源
List<BuildAssetInfo> depends = GetAllDependencies(mainAssetPath);
for (int i = 0; i < depends.Count; i++)
{
string assetPath = depends[i].AssetPath;
// 如果已经存在,则增加该资源的依赖计数
if (buildAssets.ContainsKey(assetPath))
{
buildAssets[assetPath].DependCount++;
}
else
{
buildAssets.Add(assetPath, depends[i]);
}
// 添加资源标记
buildAssets[assetPath].AddAssetTags(collectInfo.AssetTags);
// 注意:检测是否为主动收集资源
if (assetPath == mainAssetPath)
{
buildAssets[mainAssetPath].IsCollectAsset = true;
buildAssets[mainAssetPath].IsRawAsset = collectInfo.IsRawAsset;
}
}
// 添加所有的依赖资源列表
// 注意:不包括自己
var allDependAssetInfos = new List<BuildAssetInfo>(depends.Count);
for (int i = 0; i < depends.Count; i++)
{
string assetPath = depends[i].AssetPath;
if (assetPath != mainAssetPath)
allDependAssetInfos.Add(buildAssets[assetPath]);
}
buildAssets[mainAssetPath].SetAllDependAssetInfos(allDependAssetInfos);
EditorTools.DisplayProgressBar("依赖文件分析", ++progressValue, allCollectInfos.Count);
}
EditorTools.ClearProgressBar();
// 3. 移除零依赖的资源
var redundancy = CreateAssetRedundancy();
List<BuildAssetInfo> undependentAssets = new List<BuildAssetInfo>();
foreach (KeyValuePair<string, BuildAssetInfo> pair in buildAssets)
{
var buildAssetInfo = pair.Value;
if (buildAssetInfo.IsCollectAsset)
continue;
if (buildAssetInfo.DependCount == 0)
{
undependentAssets.Add(buildAssetInfo);
continue;
}
// 冗余扩展
if(redundancy != null)
{
if(redundancy.Check(buildAssetInfo.AssetPath))
{
undependentAssets.Add(buildAssetInfo);
buildMapContext.RedundancyList.Add(buildAssetInfo.AssetPath);
continue;
}
}
// 冗余机制
if (buildParameters.Parameters.ApplyRedundancy)
{
if (AssetBundleCollectorSettingData.HasCollector(buildAssetInfo.AssetPath) == false)
{
undependentAssets.Add(buildAssetInfo);
buildMapContext.RedundancyList.Add(buildAssetInfo.AssetPath);
}
}
}
foreach (var assetInfo in undependentAssets)
{
buildAssets.Remove(assetInfo.AssetPath);
}
// 4. 设置资源包名
progressValue = 0;
foreach (KeyValuePair<string, BuildAssetInfo> pair in buildAssets)
{
var assetInfo = pair.Value;
var bundleLabel = AssetBundleCollectorSettingData.GetBundleLabel(assetInfo.AssetPath);
if (assetInfo.IsRawAsset)
assetInfo.SetBundleLabelAndVariant(bundleLabel, ResourceSettingData.Setting.RawFileVariant);
else
assetInfo.SetBundleLabelAndVariant(bundleLabel, ResourceSettingData.Setting.AssetBundleFileVariant);
EditorTools.DisplayProgressBar("设置资源包名", ++progressValue, buildAssets.Count);
}
EditorTools.ClearProgressBar();
// 4. 构建资源包
var allAssets = buildAssets.Values.ToList();
if (allAssets.Count == 0)
throw new Exception("构建的资源列表不能为空");
UnityEngine.Debug.Log($"构建的资源列表里总共有{allAssets.Count}个资源");
foreach (var assetInfo in allAssets)
{
buildMapContext.PackAsset(assetInfo);
}
}
/// <summary>
/// 获取指定资源依赖的所有资源列表
/// 注意:返回列表里已经包括主资源自己
/// </summary>
private List<BuildAssetInfo> GetAllDependencies(string mainAssetPath)
{
List<BuildAssetInfo> result = new List<BuildAssetInfo>();
string[] depends = AssetDatabase.GetDependencies(mainAssetPath, true);
foreach (string assetPath in depends)
{
if (AssetBundleCollectorSettingData.IsValidateAsset(assetPath))
{
BuildAssetInfo assetInfo = new BuildAssetInfo(assetPath);
result.Add(assetInfo);
}
}
return result;
}
/// <summary>
/// 检测构建结果
/// </summary>
private void CheckBuildMapContent(BuildMapContext buildMapContext)
{
foreach (var bundleInfo in buildMapContext.BundleInfos)
{
// 注意:原生文件资源包只能包含一个原生文件
bool isRawFile = bundleInfo.IsRawFile;
if (isRawFile)
{
if (bundleInfo.Assets.Count != 1)
throw new Exception("The bundle does not support multiple raw asset : {bundleInfo.BundleName}");
continue;
}
// 注意:原生文件不能被其它资源文件依赖
foreach (var assetInfo in bundleInfo.Assets)
{
if (assetInfo.AllDependAssetInfos != null)
{
foreach (var dependAssetInfo in assetInfo.AllDependAssetInfos)
{
if (dependAssetInfo.IsRawAsset)
throw new Exception($"{assetInfo.AssetPath} can not depend raw asset : {dependAssetInfo.AssetPath}");
}
}
}
}
}
/// <summary>
/// 创建冗余类
/// </summary>
/// <returns>如果没有定义类型,则返回NULL</returns>
private IAssetRedundancy CreateAssetRedundancy()
{
var types = AssemblyUtility.GetAssignableTypes(AssemblyUtility.UnityDefaultAssemblyEditorName, typeof(IAssetRedundancy));
if (types.Count == 0)
return null;
if (types.Count != 1)
throw new Exception($"Found more {nameof(IAssetRedundancy)} types. We only support one.");
UnityEngine.Debug.Log($"创建实例类 : {types[0].FullName}");
return (IAssetRedundancy)Activator.CreateInstance(types[0]);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: baee18c5c7b76584b90413bf20fdae9a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,72 @@
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
namespace YooAsset.Editor
{
public class TaskPrepare : IBuildTask
{
void IBuildTask.Run(BuildContext context)
{
var buildParameters = context.GetContextObject<AssetBundleBuilder.BuildParametersContext>();
// 检测构建平台是否合法
if (buildParameters.Parameters.BuildTarget == BuildTarget.NoTarget)
throw new Exception("请选择目标平台");
// 检测构建版本是否合法
if (buildParameters.Parameters.BuildVersion <= 0)
throw new Exception("请先设置版本号");
// 检测输出目录是否为空
if (string.IsNullOrEmpty(buildParameters.PipelineOutputDirectory))
throw new Exception("输出目录不能为空");
// 检测资源收集配置文件
if (AssetBundleCollectorSettingData.GetCollecterCount() == 0)
throw new Exception("配置的资源收集路径为空");
// 增量更新时候的必要检测
if (buildParameters.Parameters.IsForceRebuild == false)
{
// 检测历史版本是否存在
if (AssetBundleBuilderHelper.HasAnyPackageVersion(buildParameters.Parameters.BuildTarget, buildParameters.Parameters.OutputRoot) == false)
throw new Exception("没有发现任何历史版本,请尝试强制重建");
// 检测构建版本是否合法
int maxPackageVersion = AssetBundleBuilderHelper.GetMaxPackageVersion(buildParameters.Parameters.BuildTarget, buildParameters.Parameters.OutputRoot);
if (buildParameters.Parameters.BuildVersion <= maxPackageVersion)
throw new Exception("构建版本不能小于历史版本");
// 检测补丁包是否已经存在
string packageDirectory = buildParameters.GetPackageDirectory();
if (Directory.Exists(packageDirectory))
throw new Exception($"补丁包已经存在:{packageDirectory}");
// 检测内置资源标记是否一致
PatchManifest oldPatchManifest = AssetBundleBuilderHelper.LoadPatchManifestFile(buildParameters.PipelineOutputDirectory);
if (buildParameters.Parameters.BuildinTags != oldPatchManifest.BuildinTags)
throw new Exception($"增量更新时内置资源标记必须一致:{buildParameters.Parameters.BuildinTags} != {oldPatchManifest.BuildinTags}");
}
// 如果是强制重建
if (buildParameters.Parameters.IsForceRebuild)
{
// 删除平台总目录
string platformDirectory = $"{buildParameters.Parameters.OutputRoot}/{buildParameters.Parameters.BuildTarget}";
if (EditorTools.DeleteDirectory(platformDirectory))
{
UnityEngine.Debug.Log($"删除平台总目录:{platformDirectory}");
}
}
// 如果输出目录不存在
if (EditorTools.CreateDirectory(buildParameters.PipelineOutputDirectory))
{
UnityEngine.Debug.Log($"创建输出目录:{buildParameters.PipelineOutputDirectory}");
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0dee755cccd608d48b6581ec33c99b39
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: