/**************************************************** 文件:BuildAssetWindow.cs 作者:xosmo_ 邮箱: 日期:2025/3/10 10:43:20 功能: *****************************************************/ using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Threading.Tasks; using EditorFramework; using HybridCLR.Editor; using HybridCLR.Editor.Commands; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Sirenix.OdinInspector; using Sirenix.OdinInspector.Editor; using UnityEditor; using UnityEditorInternal; using UnityEngine; using UnityEngine.Serialization; using YooAsset; using YooAsset.Editor; namespace Stary.Evo.Editor { public class BuildAssetWindow : OdinEditorWindow { public static OdinEditorWindow window; [MenuItem("Evo/资源打包工具")] static void ShowWindows() { if (CreatAssetWindow.GetCreatDomainAll().Count <= 0) { EditorUtility.DisplayDialog("提示", "不存在Domain元素,无法打开此面板,请先创建Domain元素", "确定"); return; } window = (BuildAssetWindow)EditorWindow.GetWindow(typeof(BuildAssetWindow)); window.Show(); } protected override void OnDisable() { base.OnDisable(); EditorUtility.ClearProgressBar(); } protected override async void Initialize() { base.Initialize(); GetBuildPackageNames(); UpdateMessage(false); if (string.IsNullOrEmpty(ip)) { buildAssetType = BuildAssetType.Login; return; } bool isValidateToken = await WebRequestSystem.GetValidateToken(ip + "/Authentication/validateToken"); if (isValidateToken) { buildAssetType = BuildAssetType.Build; return; } buildAssetType = BuildAssetType.Login; } public static void RemoveBuildAssetWindow() { if (window != null) window.Close(); } [EnumToggleButtons, HideLabel] public BuildAssetType buildAssetType; #region BuildAssetLogin [ShowIf("@ buildAssetType== BuildAssetType.Login")] [BoxGroup("Login", showLabel: false)] public string ip, userName, password; [ShowIf("@ buildAssetType== BuildAssetType.Login")] [BoxGroup("Login", showLabel: false)] [Button("登录", ButtonSizes.Large)] [InfoBox("@ message", InfoMessageType.Info)] public async void LoginButton() { PlayerPrefs.SetString("LoginIp", ip); PlayerPrefs.SetString("LoginUserName", userName); PlayerPrefs.SetString("LoginPassword", password); string url = ip + "/Authentication/login"; EditorUtility.DisplayProgressBar("提示", $"登陆中~", 0f); bool islogin = await WebRequestSystem.Login(url, userName, password); float progress = 0f; while (progress>=1f) { progress += 0.1f; EditorUtility.DisplayProgressBar("提示", $"登陆中~", progress); await Task.Delay(TimeSpan.FromSeconds(0.2f)); } EditorUtility.ClearProgressBar(); UpdateMessage(islogin); } private string message; public async void UpdateMessage(bool islogin) { message = "当前登录状态为:" + islogin; ip = PlayerPrefs.GetString("LoginIp"); userName = PlayerPrefs.GetString("LoginUserName"); password = PlayerPrefs.GetString("LoginPassword"); if (islogin) { buildAssetType = BuildAssetType.Build; } } #endregion protected void OnSelectionChange() { EditorUtility.SetDirty(BuildAssetDataSetting); AssetDatabase.SaveAssets(); //AssetDatabase.Refresh(); } [BoxGroup("Build", showLabel: false)] [ShowIf("@ buildAssetType== BuildAssetType.Build")] [Title("包裹列表", titleAlignment: TitleAlignments.Centered)] [HorizontalGroup("Build/BuildPipeline"), HideLabel] [ValueDropdown("GetBuildPackageNames")] [OnValueChanged("SetBuildPackageNames")] public string selectedPackageNames; [BoxGroup("Build", showLabel: false)] [ShowIf("@ buildAssetType== BuildAssetType.Build")] [Title("当前打包平台", titleAlignment: TitleAlignments.Centered)] [ReadOnly] [HorizontalGroup("Build/BuildPipeline"), HideLabel] public string buildTarget; public static string packageName; #region HyBridCLRBuild [BoxGroup("Build", showLabel: false)] [HideIf("@ packageName== \"Main\" || buildAssetType!= BuildAssetType.Build")] [Title("打包dll(子包仅打一次)", titleAlignment: TitleAlignments.Centered)] [HorizontalGroup("Build/HyBridClrBuildEntity"), HideLabel] public BuildAssetEntity hyBridClrBuildEntity = new BuildAssetEntity("HyBridCLRBuild", "DllBuildTarget", () => { //PrebuildCommand.GenerateAll(); CompileDllCommand.CompileDllActiveBuildTarget(); }); #endregion #region MarkAsset [BoxGroup("Build", showLabel: false)] [ShowIf("@ buildAssetType== BuildAssetType.Build")] [Title("标记资源", titleAlignment: TitleAlignments.Centered)] [HorizontalGroup("Build/HyBridClrBuildEntity"), HideLabel] public BuildAssetEntity markAssetEntity = new BuildAssetEntity("MarkAsset", "MarkAsset", MarkAdressable.AddMark); #endregion #region BuildAsset [BoxGroup("Build", showLabel: false)] [ShowIf("@ buildAssetType== BuildAssetType.Build")] [InlineEditor(InlineEditorObjectFieldModes.CompletelyHidden)] [HideLabel] public BuildAssetDataSetting BuildAssetDataSetting; /// /// 检测当前所有包裹 /// /// private List GetBuildPackageNames() { List result = new List(); foreach (var name in CreatAssetWindow.GetCreatDomainAllName()) { result.Add(name); } if (selectedPackageNames.IsNullOrEmpty()) { // string buildPackageName = DataSetting.packageName; // if (result.Exists(p => p == buildPackageName)) // { // selectedPackageNames = buildPackageName; // } // else // { selectedPackageNames = result[0]; //} } SetBuildPackageNames(); return result; } /// /// 设置当前包裹 /// /// private void SetBuildPackageNames() { //PlayerPrefs.SetString("BuildPackageNames", selectedPackageNames); // DataSetting.packageName = selectedPackageNames; packageName = selectedPackageNames; // DataSetting = Resources.Load("BuildAssetDataSetting"); string buildAssetDataSettingPath = $"Assets/Domain/{selectedPackageNames}/Conf/BuildAssetDataSetting.asset"; BuildAssetDataSetting buildAssetDataSetting = AssetDatabase.LoadAssetAtPath(buildAssetDataSettingPath); if (buildAssetDataSetting != null) { BuildAssetDataSetting = buildAssetDataSetting; Init(); } else { Debug.LogError($"UnityEvo:BuildAssetDataSetting 不存在,请检查路径{buildAssetDataSettingPath}"); } } /// /// 获取当前包裹 /// /// public static string GetBuildPackageName() { return packageName; } #endregion #region Update [BoxGroup("Build", showLabel: false)] [ShowIf("@ buildAssetType== BuildAssetType.Build")] [Title("打包本地资源", titleAlignment: TitleAlignments.Centered)] [HideLabel] public BuildAssetEntity onBuildPipelineEntity; // [BoxGroup("Build", showLabel: false)] // [TitleGroup("Build/UpdateAssetButton", alignment: TitleAlignments.Centered)] // // [ShowIf("@ BuildAssetDataSetting.environmentType== EnvironmentType.Release ", true)] // [ReadOnly] // [InfoBox("服务器资源盘位未存在", InfoMessageType.Error, "@ _isCheckDriveExist==false")] // [InfoBox("1、映射网络驱动器\n2、添加http://192.168.31.100:5005/alist/HotRefresh地址\n3、设置盘符为Z盘", InfoMessageType.Info, // "@ _isCheckDriveExist==false")] // [HideIf( // "@ selectedPackageNames==\"Main\" ||BuildAssetDataSetting.environmentType!= EnvironmentType.Release || buildAssetType!= BuildAssetType.Build")] // public string updateServerPath; [BoxGroup("Build", showLabel: false)] [Title("上传资源", titleAlignment: TitleAlignments.Centered)] [HideLabel] [ShowIf( "@ BuildAssetDataSetting.environmentType== EnvironmentType.Release&&selectedPackageNames!=\"Main\" && buildAssetType== BuildAssetType.Build")] public BuildAssetEntity onUpdateBuildPipelineEntity; private void OnBuildPipeline() { if (EditorUtility.DisplayDialog("提示", $"开始构建资源包[{selectedPackageNames}]!", "Yes", "No")) { EditorTools.ClearUnityConsole(); EditorApplication.delayCall += BuildAssetDataSetting.viewer.ExecuteBuild; } else { Debug.LogWarning("[Build] 打包已经取消"); } } private async void OnUpdateBuildPipeline() { if (EditorUtility.DisplayDialog("提示", $"开始上传至服务器[{selectedPackageNames}]!", "Yes", "No")) { // 新增:打包为zip的逻辑 string zipFilePath = BuildZip(BuildAssetDataSetting); await UpdateFileDataResDomain(BuildAssetDataSetting, zipFilePath); await Task.Delay(1000); EditorUtility.ClearProgressBar(); } else { EditorUtility.ClearProgressBar(); EditorUtility.DisplayDialog("提示", "Update] 上传已经取消", "确定"); Debug.LogWarning("[Update] 上传已经取消"); } } public static string BuildZip(BuildAssetDataSetting setting) { EditorUtility.DisplayProgressBar("提示", $"开始上传{setting.packageName}(打包zip)", 0.0f); // 新增:打包为zip的逻辑 string zipFileName = $"{setting.packageName}_{setting.packageVersion}.zip"; var outputPackageDirectory = $"{AssetBundleBuilderHelper.GetDefaultBuildOutputRoot()}/{EditorUserBuildSettings.activeBuildTarget}/{setting.packageName}"; //拷贝目录 string outFilePath = $"{outputPackageDirectory}/{setting.packageVersion}"; DefaultBuildinFileSystemBuild.CreateBuildinCatalogFile(setting.packageName, outFilePath); //输出目录 string zipFilePath = Path.Combine(outputPackageDirectory, zipFileName); try { using (FileStream zipStream = new FileStream(zipFilePath, FileMode.Create)) using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create)) { // 指定需要压缩的后缀(例如:只压缩.json和.dll文件) //string[] targetExtensions = { ".bundle" }; // 遍历目录下所有文件 // foreach (string filePath in Directory.GetFiles(outFilePath, "*.*", SearchOption.AllDirectories) // .Where(f => targetExtensions.Contains(Path.GetExtension(f), // StringComparer.OrdinalIgnoreCase))) foreach (string filePath in Directory.GetFiles(outFilePath, "*.*", SearchOption.AllDirectories)) { // 获取文件在压缩包中的相对路径 string entryName = Path.GetRelativePath(outFilePath, filePath); // 创建zip条目 ZipArchiveEntry entry = archive.CreateEntry(entryName, System.IO.Compression.CompressionLevel.Optimal); // 写入文件内容 using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) using (Stream entryStream = entry.Open()) { fileStream.CopyTo(entryStream); } } } Debug.Log($"成功打包为zip:{zipFilePath}"); EditorUtility.DisplayProgressBar("提示", $"开始上传{setting.packageName}(打包zip)", 0.25f); return zipFilePath; } catch (Exception ex) { Debug.LogError($"打包zip失败:{ex.Message}"); EditorUtility.ClearProgressBar(); return default; } } public static async Task UpdateFileDataResDomain(BuildAssetDataSetting setting, string zipFilePath) { string ip = PlayerPrefs.GetString("LoginIp"); var messageEntity = await WebRequestSystem.PostFile(ip + "/FileLoad/UpLoadFile", new[] { zipFilePath }); EditorUtility.DisplayProgressBar("提示", $"开始上传{setting.packageName}(上传zip文件)", 0.5f); if (messageEntity.code == 200) { List resultMessageEntities = JsonConvert.DeserializeObject>(messageEntity.data.ToString()); if (resultMessageEntities.Count > 0) { EditorUtility.DisplayProgressBar("提示", "开始上传(更新数据库)", 0.75f); foreach (var resultMessageEntity in resultMessageEntities) { var data = JsonConvert.DeserializeObject(resultMessageEntity.data.ToString()) as JObject; string fileId = data["id"].ToString(); ResDmainAddRequst resDmainAddRequst = new ResDmainAddRequst() { ProductName = Application.productName, DomainName = setting.packageName, Platform = EditorUserBuildSettings.activeBuildTarget.ToString(), PackageVersion = setting.packageVersion, DocumentFileId = fileId }; var resResultMessage = await WebRequestSystem.Post(ip + "/ResDomain/AddResDomain", JsonConvert.SerializeObject(resDmainAddRequst)); //如果低于服务器版本,更新版本号 if (resResultMessage.code == 1009) { ResDmainVersionResponse dmainVersionResponse = JsonConvert.DeserializeObject(resResultMessage.data .ToString()); string[] versions = dmainVersionResponse.PackageVersion.Split('.'); setting.packageVersionX = int.Parse(versions[0]); setting.packageVersionY = int.Parse(versions[1]); setting.packageVersionZ = int.Parse(versions[2]); EditorUtility.DisplayDialog("提示", $"{resResultMessage.message + $"\n{resResultMessage.data.ToString()}"}", "确定"); } setting.GetBuildPackageVersion(true); } EditorUtility.DisplayProgressBar("提示", $"开始上传{setting.packageName}(更新数据库)", 1f); } } else { EditorUtility.DisplayProgressBar("提示", $"{messageEntity.message}", 1f); } await Task.Delay(1000); EditorUtility.ClearProgressBar(); return; } #endregion protected void Init() { //Update buildTarget = EditorUserBuildSettings.activeBuildTarget.ToString(); onBuildPipelineEntity = new BuildAssetEntity("打包", $"打包资源包【版本:{BuildAssetDataSetting.packageVersion}】", OnBuildPipeline); onUpdateBuildPipelineEntity = new BuildAssetEntity("更新", $"更新至服务器【版本:{BuildAssetDataSetting.packageVersion}】", OnUpdateBuildPipeline); } private Vector2 scroll; protected override void DrawEditor(int index) { scroll = GUILayout.BeginScrollView(scroll); { base.DrawEditor(index); } GUILayout.EndScrollView(); BuildServerPath(); UpdateBuildPipelineButtonName(); } public void UpdateBuildPipelineButtonName() { onBuildPipelineEntity.SetButtonName($"打包资源包【版本:{BuildAssetDataSetting.packageVersion}】"); onUpdateBuildPipelineEntity.SetButtonName($"更新至服务器【版本:{BuildAssetDataSetting.packageVersion}】"); } /// /// 生成服务器路径 /// private void BuildServerPath() { // if (!_isCheckDriveExist) // { // return; // } // // updateServerPath = // $"Z:/{Application.productName}/{BuildAssetDataSetting.packageName}/{EditorUserBuildSettings.activeBuildTarget}/{BuildAssetDataSetting.packageVersion}"; // if (!Directory.Exists(updateServerPath)) // Directory.CreateDirectory(updateServerPath); //更新Button文字 onBuildPipelineEntity.SetButtonName($"打包资源包【版本:{BuildAssetDataSetting.packageVersion}】"); onUpdateBuildPipelineEntity.SetButtonName($"更新至服务器【版本:{BuildAssetDataSetting.packageVersion}】"); } } public enum BuildAssetType { Login, Build, } }