512 lines
20 KiB
C#
512 lines
20 KiB
C#
using Aliyun.OSS;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using VersionFlow.Runtime;
|
|
using static UnityEditor.ObjectChangeEventStream;
|
|
|
|
namespace VersionFlow.Editors
|
|
{
|
|
[CustomEditor(typeof(BuilderConfig))]
|
|
public class BuilderConfigEditor : Editor
|
|
{
|
|
static private BundleManifest RemoteVersion;
|
|
static private string UpdateVersion;
|
|
|
|
static public BundleManifest Report;
|
|
static private List<BundleCompare> CompareResult;
|
|
static private string outputPath;
|
|
static private Dictionary<string, BundleExtraInfo> bundleExtraInfo;
|
|
static bool FoldOutGroup;
|
|
|
|
private static void ResetState()
|
|
{
|
|
RemoteVersion = null;
|
|
UpdateVersion = null;
|
|
|
|
Report = null;
|
|
CompareResult = null;
|
|
outputPath = null;
|
|
bundleExtraInfo = null;
|
|
}
|
|
|
|
public override void OnInspectorGUI()
|
|
{
|
|
base.OnInspectorGUI();
|
|
|
|
DrawUploader();
|
|
|
|
var builder = target as BuilderConfig;
|
|
|
|
EditorGUILayout.Space(20);
|
|
BuilderConfig.ShowBuilderMarker = EditorGUILayout.Toggle("在Project窗口中显示打包标记", BuilderConfig.ShowBuilderMarker);
|
|
if (FoldOutGroup = EditorGUILayout.BeginFoldoutHeaderGroup(FoldOutGroup, "打包配置"))
|
|
{
|
|
DrawGroupItems();
|
|
EditorGUILayout.EndFoldoutHeaderGroup();
|
|
}
|
|
|
|
if (GUILayout.Button("获取远端版本号"))
|
|
{
|
|
ResetState();
|
|
var (remoteVersion, updateVersion) = FetchRemoteVersion(builder);
|
|
RemoteVersion = remoteVersion;
|
|
UpdateVersion = updateVersion;
|
|
}
|
|
|
|
if (RemoteVersion != null)
|
|
{
|
|
EditorGUILayout.LabelField($"远端版本号:{RemoteVersion.Version}");
|
|
if (GUILayout.Button("将远端版本号复制到剪切板"))
|
|
{
|
|
EditorGUIUtility.systemCopyBuffer = RemoteVersion.Version;
|
|
}
|
|
UpdateVersion = EditorGUILayout.TextField($"上传版本号", UpdateVersion);
|
|
|
|
if (CompareResult == null && GUILayout.Button("Build"))
|
|
{
|
|
var bc = (BuilderConfig)target;
|
|
bc.OnCalcBundleHash = BuilderConfigEditor.CalcBundleHash;
|
|
Report = bc.Build(UpdateVersion, out outputPath, out bundleExtraInfo);
|
|
CompareResult = bc.CompareBundleManifest(Report, RemoteVersion);
|
|
}
|
|
|
|
if (CompareResult != null && Report != null && outputPath != null && bundleExtraInfo != null)
|
|
{
|
|
DrawCompareResult(CompareResult, bundleExtraInfo);
|
|
if (GUILayout.Button("UPLOAD"))
|
|
{
|
|
StartUpLoad(CompareResult, Report, outputPath, builder.GetPatchLoader());
|
|
|
|
ResetState();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawUploader()
|
|
{
|
|
var builder = target as BuilderConfig;
|
|
|
|
var uploaderTypes = builder.GetAllPatcherType();
|
|
|
|
var options = uploaderTypes.Select(t => $"{t.Namespace}.{t.Name}").ToList();
|
|
|
|
var selectIndex = options.IndexOf(builder.uploaderClassName);
|
|
|
|
|
|
selectIndex = EditorGUILayout.Popup("选择上传器", selectIndex, options.Select(op=>PatchUploaderUtility.GetPatcherPrettyName(op)).ToArray());
|
|
if (selectIndex != -1)
|
|
{
|
|
var selectClassName = options[selectIndex];
|
|
builder.uploaderClassName = selectClassName;
|
|
}
|
|
|
|
var patchLoader = builder.GetPatchLoader();
|
|
if (patchLoader != null)
|
|
{
|
|
Editor.CreateEditor(patchLoader).OnInspectorGUI();
|
|
string json = patchLoader.GetCfgJson();
|
|
builder.uploaderCfgJson = json;
|
|
}
|
|
|
|
if (GUI.changed)
|
|
{
|
|
EditorUtility.SetDirty(target);
|
|
patchLoader.CfgChanged(builder, builder.uploaderCfgJson);
|
|
}
|
|
}
|
|
|
|
private void DrawGroupItems()
|
|
{
|
|
Undo.RecordObject(target, target.name);
|
|
|
|
EditorGUILayout.BeginVertical();
|
|
|
|
var builder = target as BuilderConfig;
|
|
builder.DuplicateBundleBeInstallBundle = EditorGUILayout.Toggle("重复依赖资源的bundle作为随包资源", builder.DuplicateBundleBeInstallBundle);
|
|
builder.ShaderBundleBeInstallBundle = EditorGUILayout.Toggle("shaderbundle作为随包资源", builder.ShaderBundleBeInstallBundle);
|
|
|
|
foreach (var group in builder.Groups)
|
|
{
|
|
var temp = GUI.color;
|
|
|
|
GUI.color = GetGroupColor(group, out var errorMsg);
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
{
|
|
EditorGUILayout.LabelField(errorMsg);
|
|
//Name And Optionnal
|
|
EditorGUILayout.BeginHorizontal();
|
|
{
|
|
group.MarkColor = EditorGUILayout.ColorField("标记颜色", group.MarkColor);
|
|
group.GroupName = EditorGUILayout.TextField(group.GroupName, GUILayout.Width(100));
|
|
|
|
var groupDeleteColor = GUI.color;
|
|
GUI.color = Color.red;
|
|
if (GUILayout.Button("X", GUILayout.Width(30)))
|
|
{
|
|
builder.Groups.Remove(group);
|
|
break;
|
|
}
|
|
GUI.color = groupDeleteColor;
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
group.OptionBundle = EditorGUILayout.Toggle($"可选下载包", group.OptionBundle);
|
|
group.InstallReady = EditorGUILayout.Toggle($"跟随主包", group.InstallReady);
|
|
|
|
//buildMode
|
|
EditorGUILayout.BeginHorizontal();
|
|
{
|
|
var EnumType = typeof(BuildEntity.EnumBuildMode);
|
|
var values = Enum.GetValues(EnumType);
|
|
foreach (BuildEntity.EnumBuildMode modeValue in values)
|
|
{
|
|
var modeName = Enum.GetName(EnumType, modeValue);
|
|
|
|
var tempColor = GUI.color;
|
|
if (modeValue == group.BuildMode) GUI.color = Color.green;
|
|
else GUI.color = Color.white;
|
|
if (GUILayout.Button(modeName))
|
|
{
|
|
group.BuildMode = modeValue;
|
|
}
|
|
GUI.color = tempColor;
|
|
}
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.BeginVertical(new GUIStyle { padding = new RectOffset(5, 5, 5, 5) });
|
|
{
|
|
for (int i = 0; i < group.FolderList.Count; i++)
|
|
{
|
|
EditorGUILayout.BeginHorizontal();
|
|
{
|
|
var foldAsset = group.FolderList[i];
|
|
var deleteColor = GUI.color;
|
|
GUI.color = Color.red;
|
|
if (GUILayout.Button("-", GUILayout.Width(20)))
|
|
{
|
|
group.FolderList.RemoveAt(i);
|
|
break;
|
|
}
|
|
GUI.color = deleteColor;
|
|
group.FolderList[i] = (DefaultAsset)EditorGUILayout.ObjectField(foldAsset, typeof(DefaultAsset), false);
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
|
|
//draw add Folder
|
|
var addFolderColor = GUI.color;
|
|
GUI.color = Color.yellow;
|
|
if (GUILayout.Button("添加资源目录"))
|
|
{
|
|
group.FolderList.Add(null);
|
|
}
|
|
GUI.color = addFolderColor;
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
GUI.color = temp;
|
|
}
|
|
|
|
//Draw Add Group Button
|
|
var addColor = GUI.color;
|
|
GUI.color = Color.yellow;
|
|
if (GUILayout.Button("添加组"))
|
|
{
|
|
builder.Groups.Add(new BuildEntity());
|
|
}
|
|
GUI.color = addColor;
|
|
|
|
EditorGUILayout.EndVertical();
|
|
|
|
if (GUI.changed)
|
|
{
|
|
MarkInBundleFiles.RefreshProjectBundleMark();
|
|
EditorUtility.SetDirty(target);
|
|
}
|
|
|
|
Color GetGroupColor(BuildEntity group, out string errorMsg)
|
|
{
|
|
errorMsg = null;
|
|
|
|
if (string.IsNullOrWhiteSpace(group.GroupName))
|
|
{
|
|
errorMsg = "非法名称";
|
|
return Color.red;
|
|
}
|
|
if (builder.Groups.Count(item => item.GroupName == group.GroupName) > 1)
|
|
{
|
|
errorMsg = "名称重复";
|
|
return Color.red;
|
|
}
|
|
if (group.FolderList.Count == 0)
|
|
{
|
|
errorMsg = "未包含任何目录";
|
|
return Color.red;
|
|
}
|
|
if (group.FolderList.Count(f => f == null) > 0)
|
|
{
|
|
errorMsg = "存在无效目录";
|
|
return Color.red;
|
|
}
|
|
|
|
if (group.OptionBundle) return Color.cyan;
|
|
|
|
return Color.white;
|
|
}
|
|
}
|
|
|
|
private void DrawCompareResult(List<BundleCompare> compareResult, Dictionary<string, BundleExtraInfo> bundleExtraInfo)
|
|
{
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
foreach (var item in compareResult.OrderBy(c => c.Result))
|
|
{
|
|
if (item.Result == BundleCompare.EnumCompare.Same) continue;
|
|
EditorGUILayout.BeginHorizontal();
|
|
string drawStr = null;
|
|
Color drawColor = Color.white;
|
|
|
|
switch (item.Result)
|
|
{
|
|
case BundleCompare.EnumCompare.Same:
|
|
drawStr = "=";
|
|
drawColor = Color.black;
|
|
break;
|
|
case BundleCompare.EnumCompare.Modified:
|
|
drawColor = Color.yellow;
|
|
drawStr = "*";
|
|
break;
|
|
case BundleCompare.EnumCompare.Add:
|
|
drawColor = Color.green;
|
|
drawStr = "+";
|
|
break;
|
|
case BundleCompare.EnumCompare.Delete:
|
|
drawColor = Color.red;
|
|
drawStr = "-";
|
|
break;
|
|
}
|
|
|
|
drawStr += item.ToString();
|
|
|
|
var temp = GUI.color;
|
|
GUI.color = drawColor;
|
|
EditorGUILayout.LabelField($"{drawStr}");
|
|
EditorGUILayout.EndHorizontal();
|
|
GUI.color = temp;
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
public static (BundleManifest remoteVersion, string updateVersion) FetchRemoteVersion(BuilderConfig builder)
|
|
{
|
|
BundleManifest remoteVersion = null;
|
|
string updateVersion = null;
|
|
|
|
var bc = builder;
|
|
var patchLoader = bc.GetPatchLoader();
|
|
|
|
var remoteManifestData = patchLoader.DownloadFile($"Bundles/{VersionFlowX.PlatformFoldName}/PatchManifest.json");
|
|
if (remoteManifestData == null)
|
|
{
|
|
remoteVersion = new BundleManifest();
|
|
remoteVersion.Version = "N/A";
|
|
var now = DateTime.Now;
|
|
updateVersion = $"1.0.0.{now.Year:00}{now.Month:00}{now.Day:00}{now.Hour:00}{now.Minute:00}{now.Second:00}";
|
|
}
|
|
else
|
|
{
|
|
var remoteJson = Encoding.ASCII.GetString(remoteManifestData);
|
|
var remoteManifest = BundleManifest.FromJson(remoteJson);
|
|
remoteVersion = remoteManifest;
|
|
var temp = remoteVersion.Version.Substring(0, remoteVersion.Version.LastIndexOf('.'));
|
|
var now = DateTime.Now;
|
|
updateVersion = $"{temp}.{now.Year:00}{now.Month:00}{now.Day:00}{now.Hour:00}{now.Minute:00}{now.Second:00}";
|
|
}
|
|
|
|
return (remoteVersion, updateVersion);
|
|
}
|
|
|
|
public static void StartUpLoad(List<BundleCompare> compareResult, BundleManifest localManifest, string outputPath, PatchUploader uploader)
|
|
{
|
|
List<Bundle> bundlesToUpload = new List<Bundle>();
|
|
List<Bundle> bundlesToDelete = new List<Bundle>();
|
|
foreach (var item in compareResult)
|
|
{
|
|
if (item.Result == BundleCompare.EnumCompare.Same) continue;
|
|
|
|
if (item.Result == BundleCompare.EnumCompare.Delete)
|
|
{
|
|
bundlesToDelete.Add(item.Old);
|
|
}
|
|
else
|
|
{
|
|
bundlesToUpload.Add(item.New);
|
|
}
|
|
}
|
|
|
|
var tips = "****!!!确认上传信息!!!*****\n";
|
|
|
|
if (bundlesToUpload.Count == 0 && bundlesToDelete.Count == 0)
|
|
{
|
|
bool confirm = EditorUtility.DisplayDialog("FBI WARNING", $"CDN上没有需要更新的Bundle,将只更新版本号\n{tips}", "ok", "cancel");
|
|
if (confirm)
|
|
{
|
|
var newVersionFile = new MemoryStream(Encoding.ASCII.GetBytes(localManifest.ToJson()));
|
|
uploader.UploadFile($"Bundles/{VersionFlowX.PlatformFoldName}/PatchManifest.json", newVersionFile);
|
|
var cdnPerformer = uploader as ICDNPerformer;
|
|
cdnPerformer?.CDNRefresh(new string[] { "PatchManifest.json" });
|
|
}
|
|
return;
|
|
}
|
|
|
|
var totalSize = bundlesToUpload.Sum(b => b.Size);
|
|
var totalSizeStr = $"{totalSize}b";
|
|
if (totalSize > 1024 * 1024)
|
|
{
|
|
totalSizeStr = $"{totalSize / 1024f / 1024f:.00}mb";
|
|
}
|
|
else if (totalSize > 1024)
|
|
{
|
|
totalSizeStr = $"{totalSize / 1024f:.00}kb";
|
|
}
|
|
|
|
bool res = EditorUtility.DisplayDialog("FBI WARNING", $"本次需上传{bundlesToUpload.Count}个Bundle\n总大小为{totalSizeStr}\n{tips}", "ok", "cancel");
|
|
if (!res) return;
|
|
|
|
int max = bundlesToUpload.Count + 1;
|
|
int step = 0;
|
|
foreach (var bundle in bundlesToUpload)
|
|
{
|
|
EditorUtility.DisplayProgressBar("上传", $"{bundle.BundleName}", step * 1f / max);
|
|
var bundleFilePath = $"{outputPath}/{bundle.BundleName}";
|
|
string remoteFilePath = null;
|
|
|
|
var fileSt = new MemoryStream(File.ReadAllBytes(bundleFilePath));
|
|
|
|
var ext = Path.GetExtension(bundleFilePath);
|
|
var fileName = Path.GetFileNameWithoutExtension(bundleFilePath);
|
|
|
|
remoteFilePath = $"Bundles/{VersionFlowX.PlatformFoldName}/{fileName}.{bundle.Hash}{ext}";
|
|
|
|
uploader.UploadFile(remoteFilePath, fileSt);
|
|
|
|
step++;
|
|
}
|
|
|
|
EditorUtility.DisplayProgressBar("上传", $"上传PatchManifest.json", step * 1f / max);
|
|
var st = new MemoryStream(Encoding.ASCII.GetBytes(localManifest.ToJson()));
|
|
uploader.UploadFile($"Bundles/{VersionFlowX.PlatformFoldName}/PatchManifest.json", st);
|
|
(uploader as ICDNPerformer)?.CDNRefresh(new string[] { $"Bundles/{VersionFlowX.PlatformFoldName}/PatchManifest.json" });
|
|
|
|
if (bundlesToDelete.Count > 0)
|
|
{
|
|
max = bundlesToDelete.Count + 1;
|
|
step = 0;
|
|
foreach (var bundle in bundlesToDelete)
|
|
{
|
|
EditorUtility.DisplayProgressBar("移除冗余bundle", $"{bundle.BundleName}", step * 1f / max);
|
|
|
|
var temp = Path.GetFileNameWithoutExtension(bundle.BundleName);
|
|
var ext = Path.GetExtension(bundle.BundleName);
|
|
string bundlePath = $"Bundles/{VersionFlowX.PlatformFoldName}/{temp}.{bundle.Hash}{ext}";
|
|
uploader.DeleteFile(bundlePath);
|
|
step++;
|
|
}
|
|
}
|
|
|
|
EditorUtility.ClearProgressBar();
|
|
}
|
|
|
|
public static void CalcBundleHash(string bundleOutputPath, List<AssetBundleBuild> buildInfoList, Dictionary<string, BundleExtraInfo> bundleExtraInfo, AssetBundleManifest report)
|
|
{
|
|
var bundleNames = report.GetAllAssetBundles();
|
|
int total = bundleNames.Length;
|
|
|
|
int step = 0;
|
|
foreach (var bundleName in bundleNames)
|
|
{
|
|
var hash = report.GetAssetBundleHash(bundleName);
|
|
bundleExtraInfo[bundleName].BundleHash = hash;
|
|
|
|
step++;
|
|
|
|
EditorUtility.DisplayProgressBar("算hash", $"{step}/{total}", step / total * 1f);
|
|
}
|
|
|
|
//var procedu = Parallel.ForEach(bundleNames, item =>
|
|
//{
|
|
// var fullPath = $"{bundleOutputPath}/{item}";
|
|
// var hash = AssetBundleParser.ParserHash(fullPath);
|
|
// lock (bundleExtraInfo)
|
|
// {
|
|
// bundleExtraInfo[item].BundleHash = hash;
|
|
// }
|
|
// step++;
|
|
//});
|
|
|
|
//while (!procedu.IsCompleted)
|
|
//{
|
|
// EditorUtility.DisplayProgressBar("算hash", $"{step}/{total}", step / total * 1f);
|
|
//}
|
|
|
|
EditorUtility.ClearProgressBar();
|
|
}
|
|
|
|
public static void SetInstallBundles(BuilderConfig builder, bool force)
|
|
{
|
|
if (Directory.Exists($"{Application.streamingAssetsPath}/Bundles"))
|
|
Directory.Delete($"{Application.streamingAssetsPath}/Bundles", true);
|
|
|
|
//获取oss上的bundle清单
|
|
var remoteInfo = FetchRemoteVersion(builder);
|
|
|
|
//收集定义为随包资源的bundle
|
|
List<Bundle> installBundles = new List<Bundle>();
|
|
foreach (var bundle in remoteInfo.remoteVersion.Bundles)
|
|
{
|
|
if (force) installBundles.Add(bundle);
|
|
else if (bundle.InstallBundle) installBundles.Add(bundle);
|
|
}
|
|
|
|
if (installBundles.Count > 0)
|
|
{
|
|
Directory.CreateDirectory($"{Application.streamingAssetsPath}/Bundles");
|
|
BundleManifest installManifest = new BundleManifest();
|
|
installManifest.Version = remoteInfo.remoteVersion.Version;
|
|
int i = 0;
|
|
|
|
var patchLoader = builder.GetPatchLoader();
|
|
|
|
foreach (var bundle in installBundles)
|
|
{
|
|
EditorUtility.DisplayProgressBar("处理随包资源", $"{bundle.BundleName}", i * 1f / installBundles.Count);
|
|
|
|
var ext = Path.GetExtension(bundle.BundleName);
|
|
var fileName = Path.GetFileNameWithoutExtension(bundle.BundleName);
|
|
var fileNameInOSS = $"{fileName}.{bundle.Hash}{ext}";
|
|
|
|
var bundleData = patchLoader.DownloadFile($"Bundles/{VersionFlowX.PlatformFoldName}/{fileNameInOSS}");
|
|
|
|
File.WriteAllBytes($"{Application.streamingAssetsPath}/Bundles/{bundle.BundleName}", bundleData);
|
|
installManifest.UpdateBundleData(bundle);
|
|
i++;
|
|
}
|
|
|
|
File.WriteAllText($"{Application.streamingAssetsPath}/Bundles/InstallPatchManifest.json", installManifest.ToJson());
|
|
}
|
|
|
|
EditorUtility.ClearProgressBar();
|
|
|
|
AssetDatabase.Refresh();
|
|
}
|
|
}
|
|
}
|