894 lines
41 KiB
C#
894 lines
41 KiB
C#
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)
|
||
};
|
||
var path = GetTempPath();
|
||
if (!Directory.Exists(path))
|
||
Directory.CreateDirectory(path);
|
||
var versionManifest = BuildPipeline.BuildAssetBundles(path,
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
} |