VersionFlow/Assets/VersionFlow/Editor/BuilderConfigEditor.cs

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();
}
}
}