using HybridCLR.Editor; using HybridCLR.Editor.Commands; using System; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEditor; using UnityEditor.Compilation; using UnityEngine; using Debug = UnityEngine.Debug; namespace ET { public enum PlatformType { None, Android, IOS, Windows, MacOS, Linux } public enum BuildType { Development, Release, } public class BuildEditor : EditorWindow { private PlatformType activePlatform; private PlatformType platformType; private bool clearFolder = true; private bool isBuildExe = true; private bool isContainAB = false; private CodeOptimization codeOptimization = CodeOptimization.Release; private BuildOptions buildOptions; private BuildAssetBundleOptions buildAssetBundleOptions = BuildAssetBundleOptions.None; public static string YooAssetCodeDll; public static string BattlePluginDir = "./Assets/Plugins/BattlePlugin"; private GlobalConfig globalConfig; [MenuItem("ET/Build Tool")] public static void ShowWindow() { GetWindow(DockDefine.Types); } private void OnEnable() { YooAssetCodeDll = Application.dataPath + "/Res/CodeDll/"; globalConfig = Resources.Load("GlobalConfig"); #if UNITY_ANDROID activePlatform = PlatformType.Android; #elif UNITY_IOS activePlatform = PlatformType.IOS; #elif UNITY_STANDALONE_WIN activePlatform = PlatformType.Windows; #elif UNITY_STANDALONE_OSX activePlatform = PlatformType.MacOS; #elif UNITY_STANDALONE_LINUX activePlatform = PlatformType.Linux; #else activePlatform = PlatformType.None; #endif platformType = activePlatform; } private void OnGUI() { this.platformType = (PlatformType)EditorGUILayout.EnumPopup(platformType); this.clearFolder = EditorGUILayout.Toggle("clean folder? ", clearFolder); this.isBuildExe = EditorGUILayout.Toggle("build exe?", this.isBuildExe); this.isContainAB = false; this.codeOptimization = (CodeOptimization)EditorGUILayout.EnumPopup("CodeOptimization ", this.codeOptimization); //EditorGUILayout.LabelField("BuildAssetBundleOptions "); //this.buildAssetBundleOptions = (BuildAssetBundleOptions)EditorGUILayout.EnumFlagsField(this.buildAssetBundleOptions); switch (this.codeOptimization) { case CodeOptimization.None: case CodeOptimization.Debug: this.buildOptions = BuildOptions.Development | BuildOptions.ConnectWithProfiler; break; case CodeOptimization.Release: this.buildOptions = BuildOptions.None; break; } GUILayout.Space(5); if (GUILayout.Button("BuildPackage")) { if (this.platformType == PlatformType.None) { ShowNotification(new GUIContent("please select platform!")); return; } var globalConfig = Resources.Load("GlobalConfig"); if (globalConfig.CodeMode == CodeMode.Server) { ShowNotification(new GUIContent("not supoort CodeMode: Server!")); return; } if (globalConfig.PlayMode == YooAsset.YooAssets.EPlayMode.EditorSimulateMode) { ShowNotification(new GUIContent("YooAsset.PlayMode not support EditorSimulateMode!")); return; } if (platformType != activePlatform) { switch (EditorUtility.DisplayDialogComplex("Warning!", $"current platform is {activePlatform}, if change to {platformType}, may be take a long time", "change", "cancel", "no change")) { case 0: activePlatform = platformType; break; case 1: return; case 2: platformType = activePlatform; break; } } RemoveBattlePluginDlls(); BuildHelper.Build(this.platformType, this.buildAssetBundleOptions, this.buildOptions, this.isBuildExe, this.isContainAB, this.clearFolder); } GUILayout.Label(""); GUILayout.Label("Build & copy AOTMetadlls"); if (GUILayout.Button("Hybrid-GenerateAll & copy2CodeDll")) { //CompileDll时,HotUpdateDll目录的model.dll会被清理掉,生成link.xml会报错 //所以在compileDll后,增加了BuildModel & copy动作 //PrebuildCommand.GenerateAll(); //删除battle相关dll RemoveBattlePluginDlls(); BuildTarget target = EditorUserBuildSettings.activeBuildTarget; CompileDllCommand.CompileDll(target); //生成最新的mode.dll & hotfix.dll,然后copy到hotupdate目录,供linkxml用 BuildModelAndHotfix(this.codeOptimization); CopyHotfixdllToHybridDir(); Il2CppDefGeneratorCommand.GenerateIl2CppDef(); // 这几个生成依赖HotUpdateDlls LinkGeneratorCommand.GenerateLinkXml(target); // 生成裁剪后的aot dll StripAOTDllCommand.GenerateStripedAOTDlls(target, EditorUserBuildSettings.selectedBuildTargetGroup); // 桥接函数生成依赖于AOT dll,必须保证已经build过,生成AOT dll MethodBridgeGeneratorCommand.GenerateMethodBridge(target); ReversePInvokeWrapperGeneratorCommand.GenerateReversePInvokeWrapper(target); AOTReferenceGeneratorCommand.GenerateAOTGenericReference(target); string aotDllDir = SettingsUtil.GetAssembliesPostIl2CppStripDir(target); ConfigStringList manifest = AssetDatabase.LoadAssetAtPath("Assets/Res/Config/HotUpdateAOTDlls.asset", typeof(ConfigStringList)) as ConfigStringList; if (manifest == null) { throw new Exception($"Assets/Res/Config/HotUpdateAOTDlls 配置不存在"); } List AOTMetaAssemblies = (manifest.List ?? Array.Empty()).ToList(); foreach (var dll in AOTMetaAssemblies) { string dllPath = $"{aotDllDir}/{dll}"; if (!File.Exists(dllPath)) { Debug.LogError($"ab中添加AOT补充元数据dll:{dllPath} 时发生错误,文件不存在。裁剪后的AOT dll在BuildPlayer时才能生成,因此需要你先构建一次游戏App后再打包。"); continue; } string dllBytesPath = $"{YooAssetCodeDll}/{dll}.bytes"; File.Copy(dllPath, dllBytesPath, true); Debug.Log($"copy AOT dll {dllPath} -> {dllBytesPath}"); } } GUILayout.Label(""); GUILayout.Label("Build热更dll & ab:"); this.globalConfig.CodeMode = CodeMode.Client;// (CodeMode)EditorGUILayout.EnumPopup("CodeMode: ", this.globalConfig.CodeMode); if (GUILayout.Button("BuildModelAndHotfix")) { BuildModelAndHotfix(this.codeOptimization); //用yooAsset打ab, 不用你了 //AfterCompiling(); ShowNotification("Build Model And Hotfix Success!"); } if (GUILayout.Button("BuildAB by YooAsset")) { var target = EditorUserBuildSettings.activeBuildTarget; BuildHelper.BuildAB(target, null); } GUILayout.Label(""); GUILayout.Label("Config Tools: ======================="); if (GUILayout.Button("ExcelExporter")) { //Directory.Delete("Assets/Bundles/Config", true); ToolsEditor.ExcelExporter(); } if (GUILayout.Button("Proto2CS")) { ToolsEditor.Proto2CS(); } GUILayout.Space(5); } //在打包前删除战斗相关dll //因为战斗dll是需要支持热更的,放在plugin目录的话,所有程序集都会自动引用 //为了排除战斗dll被主工程打进包带来的各种问题,删除之以防万一 private static void RemoveBattlePluginDlls() { var list = AssetDatabase.LoadAssetAtPath("Assets/Res/Config/HotupdateBattleDlls.asset", typeof(ConfigStringList)) as ConfigStringList; if (list == null) { throw new Exception($"Assets/Res/Config/HotupdateBattleDlls 配置不存在"); } foreach (var dll in list.List) { var file = Path.Combine(BattlePluginDir, dll); File.Delete(file); Debug.Log($"delete dll {file}"); } AssetDatabase.Refresh(); } private static void CopyHotfixdllToHybridDir() { var target = EditorUserBuildSettings.activeBuildTarget; ConfigKeyValueMap manifest = AssetDatabase.LoadAssetAtPath("Assets/Res/Config/HotupdateDlls.asset", typeof(ConfigKeyValueMap)) as ConfigKeyValueMap; if (manifest == null) { throw new Exception($"Assets/Res/Config/HotupdateDlls 配置不存在"); } foreach (var kvs in manifest.keyValueMaps) { var bytes = Path.Combine(YooAssetCodeDll, $"{kvs.Key}.bytes"); if (!File.Exists(bytes)) { Debug.LogError($"not exists: {bytes}"); } else { File.Copy(bytes, Path.Combine(SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target), kvs.Key), true); Debug.Log($"copy hot dll {bytes} -> to dir: HotUpdateDlls"); } } var list = AssetDatabase.LoadAssetAtPath("Assets/Res/Config/HotupdateBattleDlls.asset", typeof(ConfigStringList)) as ConfigStringList; if (list == null) { throw new Exception($"Assets/Res/Config/HotupdateBattleDlls 配置不存在"); } foreach (var dll in list.List) { var bytes = Path.Combine(YooAssetCodeDll, $"{dll}.bytes"); if (!File.Exists(bytes)) { Debug.LogError($"not exists: {bytes}"); } else { File.Copy(bytes, Path.Combine(SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target), dll), true); Debug.Log($"copy hot dll {bytes} -> to dir: HotUpdateDlls"); } } } public static void BuildModelAndHotfix(CodeOptimization codeOptimization = CodeOptimization.Release) { if (Define.EnableCodes) { throw new Exception("Remove macro 'ENABLE_CODES' first!"); } var globalConfig = Resources.Load("GlobalConfig"); if (globalConfig.CodeMode == CodeMode.Server) { throw new Exception("Don't compile server here, open 'ET.sln' to compile!"); } BuildAssembliesHelper.BuildModel(codeOptimization, globalConfig); BuildAssembliesHelper.BuildHotfix(codeOptimization, globalConfig); BuildAssembliesHelper.BuildMuteAssembly("Unity.Mono", new List() { "Assets/Scripts/Codes/Mono/" }, Array.Empty(), codeOptimization, globalConfig.CodeMode); File.Copy(Path.Combine(Define.BuildOutputDir, "Unity.Mono.dll"), Path.Combine(YooAssetCodeDll, $"Unity.Mono.dll.bytes"), true); File.Copy(Path.Combine(Define.BuildOutputDir, "Unity.Mono.pdb"), Path.Combine(YooAssetCodeDll, $"Unity.Mono.pdb.bytes"), true); Debug.Log("copy Unity.Mono.dll to Assets/Res/CodeDll success!"); } //将model.dll打包成ab /*private static void AfterCompiling() { Directory.CreateDirectory(BuildAssembliesHelper.CodeDir); // 设置ab包 AssetImporter assetImporter = AssetImporter.GetAtPath("Assets/Bundles/Code"); assetImporter.assetBundleName = "Code.unity3d"; AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.Log("build success!"); }*/ public static void ShowNotification(string tips) { EditorWindow game = EditorWindow.GetWindow(typeof(EditorWindow).Assembly.GetType("UnityEditor.GameView")); game?.ShowNotification(new GUIContent($"{tips}")); } } }