JJBB/Assets/Editor/AssetUpdate/AssetVersionWindow.cs

894 lines
41 KiB
C#
Raw Normal View History

2024-08-23 15:49:34 +08:00
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using BundleV2;
using Games.GlobeDefine;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;
namespace AssetUpdate
{
public class AssetVersionWindow : EditorWindow
{
public const int startVersion = 1;
public const string excelName = "AssetList";
public const string excelNameKey = "name";
public const string excelLevelKey = "level";
public const string excelImportanceKey = "importance";
public const BuildAssetBundleOptions buildOptions =
BuildAssetBundleOptions.DeterministicAssetBundle |
BuildAssetBundleOptions.ChunkBasedCompression;
private static IntSetting _currentVersion;
private static IntSetting _previousVersion;
private static string gameVersionKey
{
get { return AssetUtils.GetTextMd5(Application.dataPath.Open("GameVersion")); }
}
private static string lastVersionKey
{
get { return AssetUtils.GetTextMd5(Application.dataPath.Open("LastVersion")); }
}
public static string bundlePath
{
get { return Application.dataPath.MoveUp().Open("AssetBundle").Open(PlatformName.GetEditorPlatformName()); }
}
public static string excelToolPath { get; private set; }
public static string excelTextPath { get; private set; }
private void OnEnable()
{
if (_currentVersion == null)
_currentVersion = new IntSetting(gameVersionKey, startVersion);
if (_previousVersion == null)
_previousVersion = new IntSetting(lastVersionKey, startVersion);
if (string.IsNullOrEmpty(excelToolPath))
excelToolPath =
Application.dataPath.MoveUp().Open("ExcelExport").Open("ExcelToText.exe");
if (string.IsNullOrEmpty(excelTextPath))
excelTextPath =
Application.dataPath.MoveUp().Open("ExcelTexts");
}
private void OnGUI()
{
EditorGUILayout.LabelField("测试工具");
if (GUILayout.Button("仅仅编译版本记录"))
CreateAssetVersionBundle();
// var isLocalAsset = EngineManager.isLocalRes.current;
// var setLocalAsset = EditorGUILayout.Toggle("使用序列化导表", isLocalAsset);
// if (isLocalAsset != setLocalAsset)
// EngineManager.isLocalRes.Set(setLocalAsset);
// GUILayout.Space(10f);
// if (GUILayout.Button("仅仅复制Lua文件"))
// AssetBundleMarker.RedirectLua();
// GUILayout.Space(10f);
// if (GUILayout.Button("仅仅编译动态库"))
// {
// var assetPath = AssetBundleMarker.dllPath;
// var bundleName = AssetUtils.GetTextMd5(AssetConst.dllBundle);
// var bundleVariant = AssetConst.bundleVariant.Substring(1);
// var build = new AssetBundleBuild
// {
// assetNames = new[] {assetPath},
// addressableNames = new[] {Path.GetFileNameWithoutExtension(assetPath)},
// assetBundleName = bundleName,
// assetBundleVariant = bundleVariant
// };
// var manifest = BuildPipeline.BuildAssetBundles(bundlePath,
// new[] {build},
// buildOptions,
// EditorUserBuildSettings.activeBuildTarget);
// if (!manifest)
// ShowError("动态库资源编译失败!");
// else
// Debug.LogWarning(string.Format("动态库资源编译完成,包名{0}", bundleName));
// }
GUILayout.Space(15f);
EditorGUILayout.LabelField("资源包编辑工具");
// if (GUILayout.Button("创建Message Pack生成代码"))
// GenerateMessagePackCode();
// GUILayout.Space(10f);
// if (GUILayout.Button("编译GameConfig"))
// BuildGameConfig();
// GUILayout.Space(10f);
// if (GUILayout.Button("复制代码到Dll项目"))
// DllUtilities.CopyAllCodes();
GUILayout.Space(10f);
if (GUILayout.Button("标记资源包"))
ClassifyBundles.ClassifyAllChange_Additive();
// Debug.LogWarning("Start Mark " + DateTime.Now);
// ShaderVariantBuilder.BuildVariants();
// AssetDatabase.Refresh();
// var marker = new AssetBundleMarker();
// marker.MarkAllResources();
GUILayout.Space(10f);
GUILayout.Label("当前编译平台:" + EditorUserBuildSettings.activeBuildTarget);
if (GUILayout.Button("编辑资源包"))
BuildAssetBundles();
GUILayout.Space(15f);
EditorGUILayout.LabelField("游戏版本号");
GUILayout.Label("Apk版本号" + Application.version);
GUILayout.Label("上次打包资源版本号:" + _previousVersion.current);
GUILayout.Label("当前编译平台:" + EditorUserBuildSettings.activeBuildTarget);
GUILayout.Space(10f);
int version = _currentVersion;
var newVersion = EditorGUILayout.IntField("打包资源版本号", version);
if (newVersion != version)
_currentVersion.Set(newVersion);
GUILayout.Space(15f);
EditorGUILayout.LabelField("游戏资源路径工具");
CopyBundleType? copyType = null;
if (GUILayout.Button("按版本复制资源"))
copyType = CopyBundleType.UpdateOnly;
GUILayout.Space(10f);
if (GUILayout.Button("按版本复制资源 和 Apk资源"))
copyType = CopyBundleType.UpdateAndApk;
GUILayout.Space(10f);
if (GUILayout.Button("按版本复制资源 和 全部资源"))
copyType = CopyBundleType.UpdateAndFull;
GUILayout.Space(10f);
if (copyType != null)
{
var lastVersion = _previousVersion.current;
bool confirm;
if (version <= _previousVersion.current)
confirm = EditorUtility.DisplayDialog("警告",
string.Format("当前选择版本{0},等于或者低于上次处理版本{1}\n该操作可能导致版本资源混乱 或者 需要抛弃全部更高版本的资源!", version,
lastVersion),
"确定", "取消");
else if (version > _previousVersion.current + 1)
confirm = EditorUtility.DisplayDialog("警告",
string.Format("当前选择版本{0},高于上次处理版本{1} + 1\n该操作会跳过一个版本", version,
lastVersion),
"确定", "取消");
else
confirm = true;
if (confirm)
if (CopyAssetBundles(copyType.Value))
_previousVersion.Set(version);
}
// // 2020.04.08 iOS版本直接使用资源BundleName单一项目打包不排除动态库
// // 使用Unity内置方法排除重复资源
// GUILayout.Space(15f);
// EditorGUILayout.LabelField("iOS特殊操作流程");
// GUILayout.Space(10f);
// if (GUILayout.Button("创建iOS Link.xml文件"))
// iOSDllUtilities.CreateLinkXml();
// GUILayout.Space(10f);
// var iOSPath = iOSDllUtilities.GetiOSPath();
// if (GUILayout.Button("复制iOS代码"))
// iOSDllUtilities.SyncCodeToiOS(iOSPath);
// if (GUILayout.Button("设置资源BundleName"))
// MarkFileOnImporters();
// GUILayout.Space(10f);
// if (GUILayout.Button("按BundleName编辑资源包"))
// BuildAssetBundlesByTag();
// GUILayout.Space(10f);
// if (GUILayout.Button("复制全部到Streaming"))
// if (EditorUtility.DisplayDialog("警告", "复制到Streaming的操作仅仅用于测试需要至少做一次按版本复制资源才能正常运作", "确定", "取消"))
// CopyAllAssetToPath(false);
// GUILayout.Space(10f);
// if (GUILayout.Button("复制全部到Persist"))
// if (EditorUtility.DisplayDialog("警告", "复制到Persist的操作仅仅用于测试需要至少做一次按版本复制资源才能正常运作", "确定", "取消"))
// CopyAllAssetToPath(true);
}
private static AssetBundleManifest CreateAssetVersionBundle()
{
var versionFile =
GlobeVar.testConfigPath.Open(AssetConst.versionFile + AssetConst.bytesExtension);
AssetDatabase.Refresh();
var bundleName = AssetUtils.GetTextMd5(AssetConst.versionFile);
var build = new AssetBundleBuild
{
assetNames = new[] {EditorCommonUtility.FileToAssetPath(versionFile)},
addressableNames = new[] {AssetConst.versionFile},
assetBundleName = bundleName,
assetBundleVariant = AssetConst.bundleVariant.Substring(1)
};
2024-09-03 19:56:21 +08:00
var path = GetTempPath();
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
var versionManifest = BuildPipeline.BuildAssetBundles(path,
2024-08-23 15:49:34 +08:00
new[] {build},
buildOptions,
EditorUserBuildSettings.activeBuildTarget);
return versionManifest;
}
[MenuItem("Bundle V2/Asset Version Window")]
public static void OpenWindow()
{
GetWindow<AssetVersionWindow>("游戏版本管理器");
}
// 已经过时在AssetImporter标记太费时间
// private static void BuildAssetBundlesByTag()
// {
// var startTime = DateTime.Now;
// if (!Directory.Exists(bundlePath))
// Directory.CreateDirectory(bundlePath);
// var manifest = BuildPipeline.BuildAssetBundles(bundlePath,
// buildOptions,
// EditorUserBuildSettings.activeBuildTarget);
// var endTime = DateTime.Now;
// if (!manifest)
// ShowError("Asset Bundle 编译失败!");
// else
// Debug.LogWarning("Asset Bundle 编译成功于:" + endTime + ",共耗时:" + (endTime - startTime));
// }
// 已经过时在AssetImporter标记太费时间
// private static void MarkFileOnImporters()
// {
// var startTime = DateTime.Now;
// // 监视当前已经配置部分
// var current = new Dictionary<string, string>();
// var bundleNames = AssetDatabase.GetAllAssetBundleNames();
// foreach (var bundleName in bundleNames)
// {
// var noExtension = bundleName.RemoveExtension();
// var assetPaths = AssetDatabase.GetAssetPathsFromAssetBundle(bundleName);
// foreach (var assetPath in assetPaths)
// current[assetPath] = noExtension;
// }
//
// // 监视当前目标配置部分
// var target = new Dictionary<string, string>();
// var plans = LoadAssetMarkFile();
// foreach (var plan in plans)
// foreach (var assetPath in plan.assetPaths)
// target[assetPath] = plan.bundleName;
// // 对比获得移除部分
// var removeNames = new HashSet<string>();
// // 对比获得修改部分
// var addNames = new Dictionary<string, string>();
// foreach (var key in current.Keys)
// if (!target.ContainsKey(key))
// removeNames.Add(key);
// foreach (var keyValue in target)
// {
// string bundleName;
// if (!current.TryGetValue(keyValue.Key, out bundleName) ||
// bundleName != keyValue.Value)
// addNames[keyValue.Key] = keyValue.Value;
// }
//
// if (EditorUtility.DisplayDialog("Bundle Plan", string.Format(
// "Total current {0}, total target {1}.\nRemove Bundle {2}.\nReset Bundle {3}.",
// current.Count,
// target.Count,
// removeNames.Count,
// addNames.Count), "Ok", "Cancel"))
// {
// var finished = 0;
// var total = removeNames.Count;
// foreach (var assetPath in removeNames)
// {
// EditorUtility.DisplayProgressBar("Remove Bundle Names",
// string.Format("{0} / {1}", finished + 1, total), (float) finished / total);
// finished++;
// var importer = AssetImporter.GetAtPath(assetPath);
// if (importer)
// {
// importer.assetBundleName = string.Empty;
// importer.assetBundleVariant = string.Empty;
// importer.SaveAndReimport();
// }
// else
// {
// Debug.LogError(string.Format("Failed to get assetImporter at path {0}!", assetPath));
// }
// }
//
// finished = 0;
// total = target.Count;
// // 移除前面的点
// var variant = AssetConst.bundleVariant.Substring(1);
// foreach (var keyValue in target)
// {
// EditorUtility.DisplayProgressBar("Remove Bundle Names",
// string.Format("{0} / {1}", finished + 1, total), (float) finished / total);
// finished++;
// var importer = AssetImporter.GetAtPath(keyValue.Key);
// if (importer)
// {
// importer.assetBundleName = keyValue.Value;
// importer.assetBundleVariant = variant;
// importer.SaveAndReimport();
// }
// else
// {
// Debug.LogError(string.Format("Failed to get assetImporter at path {0}!", keyValue.Key));
// }
// }
//
// EditorUtility.ClearProgressBar();
// AssetDatabase.RemoveUnusedAssetBundleNames();
// var endTime = DateTime.Now;
// Debug.LogWarning("Mark Asset 标记到Importers" + endTime + ",共耗时:" + (endTime - startTime));
// }
// }
private static void BuildAssetBundles()
{
// 首先独立打包导出场景资源
// 然后打包其他类型带依赖资源
// 最后将两份资源信息合并到统一的清单之上
var startTime = DateTime.Now;
Debug.LogWarning("Asset Bundle 编译开始于:" + startTime);
// 注bundleVariant 会自己加个点,因此跳过一个点。
if (!Directory.Exists(bundlePath))
Directory.CreateDirectory(bundlePath);
var manifest = BuildPipeline.BuildAssetBundles(bundlePath,
buildOptions,
EditorUserBuildSettings.activeBuildTarget);
var endTime = DateTime.Now;
if (!manifest)
ShowError("Asset Bundle 编译失败!");
else
Debug.LogWarning("Asset Bundle 编译成功于:" + endTime + ",共耗时:" + (endTime - startTime));
}
private static List<AssetBundlePlan> LoadAssetMarkFile()
{
var result = new List<AssetBundlePlan>();
var bundleNames = AssetDatabase.GetAllAssetBundleNames();
foreach (var bundleName in bundleNames)
{
var assets = AssetDatabase.GetAssetPathsFromAssetBundle(bundleName);
if (assets.Length > 0)
{
var bundlePlan = new AssetBundlePlan(bundleName.RemoveExtension());
result.Add(bundlePlan);
foreach (var asset in assets)
bundlePlan.AddAsset(asset);
}
}
return result;
}
private static bool CopyAssetBundles(CopyBundleType type)
{
var result = false;
var versionPath = GetCurrentVersionPath();
var currentPath = versionPath.Open(_currentVersion.current.ToString());
var manifestPath = bundlePath.Open(PlatformName.GetEditorPlatformName());
AssetBundleManifest manifest = null;
if (File.Exists(manifestPath))
{
var assetBundle = AssetBundle.LoadFromFile(manifestPath);
if (assetBundle)
{
manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
assetBundle.Unload(false);
}
}
if (!manifest)
{
ShowError("无法加载上次编译的AssetBundleManifest请检查编译是否正常完成路径" + manifestPath);
return result;
}
var itemList = InitDependencyByExcel(manifest);
if (itemList == null)
{
ShowError("由Excel初始化资源等级失败");
}
// 按照当前版本初始化全部资源信息
else
{
var error = false;
var finalItemList = new List<AssetDependencyItem>();
// 灌注全部物品信息
// ReSharper disable once PossibleNullReferenceException
var allAssets = manifest.GetAllAssetBundles()
.Where(a => a != EditorAssetConst.dummyBundle + AssetConst.bundleVariant)
.Select(a => a.RemoveExtension()).ToArray();
for (var i = 0; i < allAssets.Length; i++)
{
var asset = allAssets[i];
EditorUtility.DisplayProgressBar("构造资源信息", string.Format("{0} / {1}", i + 1, allAssets.Length),
(float) i / allAssets.Length);
var item = itemList.Find(a => a.name == asset) ?? new AssetDependencyItem
{
name = asset
};
finalItemList.Add(item);
item.dependencies = manifest.GetAllDependencies(item.name + AssetConst.bundleVariant)
.Select(a => a.RemoveExtension()).ToArray();
}
// 提升具有特殊权限的物品
// ReSharper disable once ForCanBeConvertedToForeach
for (var i = 0; i < finalItemList.Count; i++)
{
EditorUtility.DisplayProgressBar("修改资源权限信息",
string.Format("{0} / {1}", i + 1, finalItemList.Count), (float) i / finalItemList.Count);
var item = finalItemList[i];
foreach (var dependency in item.dependencies)
{
var dependItem = finalItemList.Find(a => a.name == dependency);
if (dependItem == null)
{
error = true;
Debug.LogError(string.Format(
"Cannot find item {0}! Some problem in AssetBundle Manifest!", dependency));
}
else
{
dependItem.level = Mathf.Max(dependItem.level, item.level);
dependItem.importance = Mathf.Min(dependItem.importance, item.importance);
}
}
}
// 灌注资源文件信息
for (var i = 0; i < finalItemList.Count; i++)
{
EditorUtility.DisplayProgressBar("增加资源文件信息",
string.Format("{0} / {1}", i + 1, finalItemList.Count), (float) i / finalItemList.Count);
var item = finalItemList[i];
var path = bundlePath.Open(item.name + AssetConst.bundleVariant);
var fileInfo = new FileInfo(path);
if (!fileInfo.Exists)
{
error = true;
Debug.LogError(string.Format("无法获得文件{0}!检查是否打包中途出现故障!", path));
}
else
{
// 单包不可能超过2G不需要用int64做长度
item.fileSize = (int) fileInfo.Length;
item.md5 = AssetUtils.GetFileMd5(path);
item.version = _currentVersion;
}
}
// 获得之前版本的信息
var oldFiles = new List<AssetDependencyInfo>();
if (error)
{
ShowError("文件信息构造流程出现问题,详见之前的报错!");
}
else
{
if (!Directory.Exists(versionPath))
Directory.CreateDirectory(versionPath);
// 仅仅对比最近10个版本的资源减少对比负担
// 注这个流程仅仅会不重用10个版本内都不使用的部分实际未变动资源仍然可以逐版本继承上来
var versionDirs =
(from path in Directory.GetDirectories(versionPath, "*", SearchOption.TopDirectoryOnly)
// ReSharper disable once AssignNullToNotNullAttribute
let version = int.Parse(Path.GetFileNameWithoutExtension(path))
where _currentVersion - version < 10
select path).ToArray();
for (var i = 0; i < versionDirs.Length; i++)
{
EditorUtility.DisplayProgressBar("加载旧版本清单",
string.Format("{0} / {1}", i + 1, versionDirs.Length),
(float) i / versionDirs.Length);
var dir = versionDirs[i];
int oldVersion;
if (!int.TryParse(Path.GetFileNameWithoutExtension(dir), out oldVersion))
{
Debug.LogError(string.Format("路径并非版本号结尾,于{0}", dir));
}
else if (oldVersion < _currentVersion)
{
var filePath = dir.Open(AssetUtils.GetTextMd5(AssetConst.versionFile) +
AssetConst.bundleVariant);
if (!File.Exists(filePath))
{
ShowError(string.Format("无法获得旧版本文件{0}!", filePath));
error = true;
}
else
{
var info = AssetDependencyInfo.Create(filePath);
if (info == null)
{
ShowError(string.Format("无法解析旧版本文件{0}!", filePath));
error = true;
}
else
{
oldFiles.Add(info);
}
}
}
}
}
// 试图降级当前打包出来的文件
if (!error && oldFiles.Count > 0)
{
// 从大到小排列,策略是认为版本间有改动的资源数量不多
oldFiles.Sort((a, b) => -a.version.CompareTo(b.version));
// 对全部资源进行最小版本对比
for (var i = 0; i < finalItemList.Count; i++)
{
var item = finalItemList[i];
EditorUtility.DisplayProgressBar("检查文件是否重用老版本",
string.Format("{0} / {1}", i + 1, finalItemList.Count),
(float) i / finalItemList.Count);
foreach (var oldFile in oldFiles)
{
var oldItem = oldFile.assetList.FirstOrDefault(a => a.name == item.name);
if (oldItem != null && oldItem.md5 == item.md5)
{
item.version = Mathf.Min(item.version, oldItem.version);
break;
}
}
}
}
if (!error)
{
// 当前版本的资源清单
var dependencyInfo = new AssetDependencyInfo
{
version = _currentVersion,
assetList = finalItemList.ToArray()
};
// 输出资源包文件
var currentFiles = dependencyInfo.assetList.Where(a => a.version == _currentVersion).ToArray();
// if (currentFiles.Length == 0)
// {
// EditorUtility.DisplayDialog("当前版本无新资源", "所有资源同之前版本匹配,不需要更新资源!", "确定");
// }
// else
{
EditorUtility.DisplayProgressBar("输出资源清单",
string.Format("{0} / {1}", 0, 1),
0f);
// 输出资源清单文件
var builder = new StringBuilder();
dependencyInfo.Write(builder);
var bytes = Encoding.UTF8.GetBytes(builder.ToString());
var versionFile =
GlobeVar.testConfigPath.Open(AssetConst.versionFile + AssetConst.bytesExtension);
File.WriteAllBytes(versionFile, bytes);
var versionManifest = CreateAssetVersionBundle();
if (!versionManifest)
{
Debug.LogError(string.Format("无法建立{0}的AssetBundle", AssetConst.versionFile));
error = true;
return !error;
}
if (Directory.Exists(currentPath))
Directory.Delete(currentPath, true);
Directory.CreateDirectory(currentPath);
var bundleName = AssetUtils.GetTextMd5(AssetConst.versionFile);
File.Copy(GetTempPath().Open(bundleName + AssetConst.bundleVariant),
currentPath.Open(bundleName + AssetConst.bundleVariant), true);
for (var i = 0; i < currentFiles.Length; i++)
{
EditorUtility.DisplayProgressBar("复制资源服务器文件",
string.Format("{0} / {1}", i + 1, currentFiles.Length),
(float) i / currentFiles.Length);
var file = currentFiles[i];
var filePath = bundlePath.Open(file.name + AssetConst.bundleVariant);
var targetPath = currentPath.Open(file.name + AssetConst.bundleVariant);
EnsureDirectory(targetPath);
File.Copy(filePath, targetPath);
}
var apkFiles = type == CopyBundleType.UpdateAndFull
? dependencyInfo.assetList
: dependencyInfo.assetList.Where(a => a.level == AssetLevel.inApk).ToArray();
if (type > CopyBundleType.UpdateOnly)
{
var apkPath = GetCurrentApkPath();
if (Directory.Exists(apkPath))
EditorCommonUtility.DeleteAllFiles(apkPath);
else
Directory.CreateDirectory(apkPath);
for (var i = 0; i < apkFiles.Length; i++)
{
var file = apkFiles[i];
EditorUtility.DisplayProgressBar("复制首包文件",
string.Format("{0} / {1}", i + 1, apkFiles.Length),
(float) i / apkFiles.Length);
var fileName = file.name + AssetConst.bundleVariant;
var filePath = bundlePath.Open(fileName);
var targetPath =
apkPath.Open(fileName);
EnsureDirectory(targetPath);
File.Copy(filePath, targetPath);
}
// 构造当前版本数字文件
var currentVersionPath =
AssetConst.csAssetHeader.Open("Resources").Open(AssetConst.currentVersionFile) +
AssetConst.textExtension;
File.WriteAllText(EditorCommonUtility.AssetToFilePath(currentVersionPath), _currentVersion.ToString());
}
// 输出全部资源清单
var streamingPathDict = new Dictionary<string, string>();
foreach (var file in apkFiles)
streamingPathDict.Add(file.name, file.md5);
var dataPath = Application.dataPath.MoveUp().Open(AssetConst.csAssetHeader)
.Open("Resources")
.Open(AssetConst.assetPathDataPath)
.Open(AssetConst.pathFile + AssetConst.bytesExtension);
var formatter = new BinaryFormatter();
using (var fs = File.Create(dataPath))
{
formatter.Serialize(fs, streamingPathDict);
}
}
result = !error;
}
}
EditorUtility.ClearProgressBar();
return result;
}
private static void EnsureDirectory(string filePath)
{
var dir = Path.GetDirectoryName(filePath);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
}
private static string GetTempPath()
{
var rootPath = Application.dataPath.MoveUp();
var tempPath = rootPath.Open("AssetBundleVersioned").Open("Temp");
return tempPath;
}
private static string GetCurrentApkPath()
{
var rootPath = Application.dataPath.MoveUp();
var buildTarget = GetBuildTargetDescription();
var apkFilePath = rootPath.Open("AssetBundleVersioned").Open("ApkFile").Open(buildTarget);
return apkFilePath;
}
private static string GetCurrentVersionPath()
{
var rootPath = Application.dataPath.MoveUp();
var buildTarget = GetBuildTargetDescription();
var versionPath = rootPath.Open("AssetBundleVersioned").Open(buildTarget);
return versionPath;
}
private static string GetBuildTargetDescription()
{
var buildTarget = EditorUserBuildSettings.activeBuildTarget.ToString();
var dotIndex = buildTarget.LastIndexOf('.');
if (dotIndex >= 0)
buildTarget = buildTarget.Substring(dotIndex);
return buildTarget;
}
private static List<AssetDependencyItem> InitDependencyByExcel(AssetBundleManifest manifest)
{
var allBundles = manifest.GetAllAssetBundles().Select(a => a.RemoveExtension()).ToArray();
List<AssetDependencyItem> itemList = null;
var readers = GetExcelText();
if (readers == null || readers.Count < 1)
{
ShowError("读取Excel文件失败");
}
else
{
// 读取Excel文件确定资源等级
var reader = readers.Find(a => a.name == excelName);
if (reader == null)
{
ShowError(string.Format("无法获得Excel表单{0}", excelName));
}
else
{
var nameId = -1;
var levelId = -1;
var importanceId = -1;
for (var i = 0; i < reader.titles.Length; i++)
{
var title = reader.titles[i];
// ReSharper disable once SwitchStatementMissingSomeCases
switch (title)
{
case excelNameKey:
nameId = i;
break;
case excelLevelKey:
levelId = i;
break;
case excelImportanceKey:
importanceId = i;
break;
}
}
if (nameId < 0 || levelId < 0 || importanceId < 0)
{
ShowError(string.Format("Excel表单{0}表头不包含全部标题{1}{2}和{3}", excelName, excelNameKey,
excelLevelKey, excelImportanceKey));
}
else
{
var error = false;
// 需要执行不完整对比,因此不能直接使用字典
// 先写入Excel中的特例
var activeList = allBundles.Where(a => !a.StartsWith(ClassifyBundles.dependBundleHeader))
.ToArray();
itemList = new List<AssetDependencyItem>(activeList.Length);
// 默认等级配置到于后台下载级
foreach (var active in activeList)
itemList.Add(new AssetDependencyItem
{
name = active,
level = AssetLevel.onRunning,
importance = 0
});
for (var i = 0; i < reader.contentLines.Count; i++)
{
var line = reader.contentLines[i];
// 初始化每行Excel的状态
var bundleFeature = line[nameId].ToUrl();
var level = 0;
var importance = 0;
if (!string.IsNullOrEmpty(line[levelId]) && !int.TryParse(line[levelId], out level))
{
Debug.LogError(string.Format("Asset {0} in Excel has no proper level!", bundleFeature));
error = true;
}
else if (!string.IsNullOrEmpty(line[importanceId]) &&
!int.TryParse(line[importanceId], out importance))
{
Debug.LogError(string.Format("Asset {0} in Excel has no proper importance!",
bundleFeature));
error = true;
}
if (!error)
{
EditorUtility.DisplayProgressBar("更新资源等级",
string.Format("{0} / {1}: {2}", i, reader.contentLines.Count, bundleFeature),
(float) i / reader.contentLines.Count);
var group = itemList.Where(a => a.name.StartsWith(bundleFeature));
foreach (var item in group)
{
item.level = level;
item.importance = importance;
}
}
if (error)
break;
}
if (error)
itemList = null;
}
}
}
return itemList;
}
// private static bool CheckInPlan(string feature, AssetBundlePlan buildPlan)
// {
// return buildPlan.bundleName.StartsWith(feature);
// }
private static List<TextReader> GetExcelText()
{
List<TextReader> readers = null;
var error = true;
var process = Process.Start(excelToolPath);
if (process == null)
{
Debug.LogError(string.Format("Path Error: Failed to start process at path {0}!", excelToolPath));
}
else
{
while (!process.HasExited)
{
}
var code = process.ExitCode;
if (code != 0)
Debug.LogError(string.Format("Process Error: Process didn't exit properly with exit code {0}!",
code));
else
error = false;
}
if (!error)
{
readers = new List<TextReader>();
var files = Directory.GetFiles(excelTextPath, "*.txt", SearchOption.TopDirectoryOnly);
foreach (var file in files)
try
{
var reader = new TextReader(file);
if (reader.error)
error = true;
else
readers.Add(reader);
}
catch (Exception e)
{
Debug.LogError(e);
}
}
return error ? null : readers;
}
private static void ShowError(string content)
{
EditorUtility.DisplayDialog("错误", content, "确定");
}
// private string[] GetLastAssetBundles()
// {
// string[] result = null;
// AssetBundleManifest manifest = null;
// var manifestPath = bundlePath.Open("AssetBundle");
// if (File.Exists(manifestPath))
// {
// var assetBundle = AssetBundle.LoadFromFile(manifestPath);
// if (assetBundle)
// {
// manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
// assetBundle.Unload(false);
// }
// }
//
// if (!manifest)
// ShowError(string.Format("无法获得AssetBundle Manifest于路径{0}", manifestPath));
// else
// // ReSharper disable once PossibleNullReferenceException
// result = manifest.GetAllAssetBundles().Where(a => a != EditorAssetConst.dummyBundle).ToArray();
// return result;
// }
private enum CopyBundleType
{
UpdateOnly, // 仅仅复制资源服务器文件
UpdateAndApk, // 服务器文件 和 最小Apk
UpdateAndFull // 服务器文件 和 全部文件到Apk
}
public class AssetBundlePlan
{
public readonly List<string> assetPaths = new List<string>();
public readonly string bundleName;
public AssetBundlePlan(string bundle)
{
bundleName = bundle;
}
public void AddAsset(string assetPath)
{
assetPaths.Add(assetPath);
}
}
}
}