using System; using System.Collections.Generic; using System.IO; using System.Reflection; using AutoBuild.Helper; using HybridCLR.Editor; using HybridCLR.Editor.Commands; using HybridCLR.Editor.Installer; using HybridCLR.Editor.Link; using HybridCLR.Editor.Meta; using Obfuz; using Obfuz.Settings; using Obfuz.Unity; using Obfuz4HybridCLR; using PhxhSDK.AOT.VersionUpdate; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; /// /// 编辑器下打包 /// class BuildLauncher { private const string HybirdCLRGenerateDir = "/HybridCLRGenerate"; private const string HybirdCLRGenerateLinkPath = "Assets/HybridCLRGenerate/link.xml"; private const string GameLauncherSceneName = "Assets/Scenes/GameLauncher.unity"; private static bool enableHotUpdate; /// /// 开启热更 /// public static void SetEnableHotUpdate(bool isEnable) { Debug.Log($"是否开启热更新:{isEnable}"); var gameLauncherScene = EditorSceneManager.OpenScene(GameLauncherSceneName, OpenSceneMode.Additive); if (!gameLauncherScene.IsValid()) { Debug.LogError($"无法加载场景{GameLauncherSceneName}"); return; } var gameLauncher = UnityEngine.Object.FindObjectOfType(); if (gameLauncher == null) { Debug.LogError("找不到GameLauncher"); return; } gameLauncher.enableHotUpdate = isEnable; EditorSceneManager.MarkSceneDirty(gameLauncherScene); EditorSceneManager.SaveScene(gameLauncherScene); EditorSceneManager.CloseScene(gameLauncherScene, false); HybridCLR.Editor.SettingsUtil.Enable = isEnable; if (!isEnable) { //删除HybirdCLR生成的link文件 AssetDatabase.DeleteAsset(HybirdCLRGenerateLinkPath); } } /// /// 构建热更 /// public static void BuildHotUpdateDlls() { var controller = new InstallerController(); bool isObfuzEnabled = ObfuzSettings.Instance.buildPipelineSettings.enable; // 非混淆构建时强制重新安装 HybridCLR: // 混淆构建的 GeneratePolymorphicCodes() 会修改 LocalIl2CppData/hybridclr/metadata/PolymorphicDefs.h, // 将 kFormatVariantVersion 写为随机非零值并持久化到磁盘。 // 若非混淆构建不重装,该文件残留,导致编译出的 APK native 库要求 polymorphic 格式 DLL, // 而实际装载的是标准 DLL,运行时抛出 BadImageFormatException: LoadImageErrorCode:8 (UNMATCH_FORMAT_VARIANT)。 bool needInstall = !controller.HasInstalledHybridCLR() || controller.InstalledLibil2cppVersion != controller.PackageVersion || !isObfuzEnabled; if (needInstall) { Debug.Log($"[BuildHotUpdateDlls] Reinstalling HybridCLR: installed='{controller.InstalledLibil2cppVersion}' package='{controller.PackageVersion}' isObfuzEnabled={isObfuzEnabled}"); controller.InstallDefaultHybridCLR(); } BuildUtils.SetFileReadable(Application.dataPath + HybirdCLRGenerateDir); // 清空 HybridCLRData/HotUpdateDlls/ 源目录,防止残留上次构建(混淆/非混淆)的 DLL 污染本次产物 var hotUpdateDllSourceDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(EditorUserBuildSettings.activeBuildTarget); if (Directory.Exists(hotUpdateDllSourceDir)) { Directory.Delete(hotUpdateDllSourceDir, true); Debug.Log($"[BuildHotUpdateDlls] Cleared hotupdate dll source dir: {hotUpdateDllSourceDir}"); } if (ObfuzSettings.Instance.buildPipelineSettings.enable) { ObfuzMenu.GenerateEncryptionVM(); ObfuzMenu.SaveSecretFile(); var kedrType = Type.GetType("KedrExtensionMenu, Assembly-CSharp-Editor"); if (kedrType != null) { var method = kedrType.GetMethod("GenerateAll", BindingFlags.Public | BindingFlags.Static); method?.Invoke(null, null); } else { Debug.LogWarning("[Obfuz] KedrExtensionMenu not found, falling back to PrebuildCommandExt"); PrebuildCommandExt.GenerateAll(); } HotUpadeEditorHelper.CopyObfuscatedDllToTarget(); } else { PrebuildCommand.GenerateAll(); HotUpadeEditorHelper.CopyHotUpdateDll(); } HotUpadeEditorHelper.CopyMetaDataDll(); if (ObfuzSettings.Instance.polymorphicDllSettings.enable) { HotUpadeEditorHelper.ConvertOriginalDllToPolymorphicDll(); } } /// /// 打包面板初始化 /// public static void Init(BuildTarget buildTarget, string workSpace, string appVersion, string profileName, bool developmentBuild, bool hotUpdate) { EditorUserBuildSettings.development = developmentBuild; YooAssetBuildBase.BuildTargetPlatform = buildTarget; YooAssetBuildBase.profileName = profileName; YooAssetBuildBase.JudgmentPlatform(buildTarget.ToString()); YooAssetBuildBase.workSpace = string.IsNullOrEmpty(workSpace) ? "WorkSpace" : workSpace; PlayerSettings.bundleVersion = appVersion; YooAssetBuildBase.appVersion = appVersion; enableHotUpdate = hotUpdate; if (profileName.Equals("AllLocal")) { List appendDefineList = new() { "AllLocal" }; var currentTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget); var oldDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(currentTargetGroup); BatchBuild._oldDefines = oldDefines; var newDefineList = new List(); newDefineList.AddRange(oldDefines.Split(';')); for (var i = 0; i < newDefineList.Count; i++) { var define = newDefineList[i]; if (string.IsNullOrEmpty(define)) continue; if (appendDefineList.Contains(define)) appendDefineList.Remove(define); } newDefineList.AddRange(appendDefineList); PlayerSettings.SetScriptingDefineSymbolsForGroup(currentTargetGroup, string.Join(";", newDefineList)); } } /// /// 编辑器下构建资源 /// public static void BuildResource() { UpdateLocalInfo info = new() { workSpace = YooAssetBuildBase.workSpace, accessKeyId = YooAssetBuildBase.appConfigAccessKeyId, accessKeySecret = YooAssetBuildBase.appConfigAccessKeySecret, bucketName = YooAssetBuildBase.appConfigBucketName, configOriginalUrl = YooAssetBuildBase.appConfigURL, //httpUrl = YooAssetBuildBase.httpURL, httpAccessKey = YooAssetBuildBase.httpAccessKey, httpKey = YooAssetBuildBase.httpKey, version = YooAssetBuildBase.appVersion, isDev = YooAssetBuildBase.isDev, isOversea = YooAssetBuildBase.isOversea, buildTicks = DateTime.Now.Ticks, buildCode = BatchBuild.GetBundleVersionCode(), }; PackDataInst.CreateResourceJson(info); BuildTargetGroup currentTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget); string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(currentTargetGroup); #if DEVELOPMENT_BUILD || DEBUG ExtBuildInfoHelper.CreateFirstExtBuildInfo(info.buildTicks, info.workSpace, info.version, info.buildCode, true, defines); #else ExtBuildInfoHelper.CreateFirstExtBuildInfo(info.buildTicks, info.workSpace, info.version, false, defines); #endif SetEnableHotUpdate(enableHotUpdate); if (enableHotUpdate) { BuildHotUpdateDlls(); } YooAssetBuildBase.BuildResourceInEditor(); } /// /// 编辑器下构建资源和Player /// public static void BuildResourceAndPlayer() { UpdateLocalInfo info = new() { workSpace = YooAssetBuildBase.workSpace, accessKeyId = YooAssetBuildBase.appConfigAccessKeyId, accessKeySecret = YooAssetBuildBase.appConfigAccessKeySecret, bucketName = YooAssetBuildBase.appConfigBucketName, configOriginalUrl = YooAssetBuildBase.appConfigURL, //httpUrl = YooAssetBuildBase.httpURL, httpAccessKey = YooAssetBuildBase.httpAccessKey, httpKey = YooAssetBuildBase.httpKey, version = YooAssetBuildBase.appVersion, isDev = YooAssetBuildBase.isDev, isOversea = YooAssetBuildBase.isOversea, buildTicks = DateTime.Now.Ticks, buildCode = BatchBuild.GetBundleVersionCode(), }; PackDataInst.CreateResourceJson(info); BuildTargetGroup currentTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget); string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(currentTargetGroup); #if DEVELOPMENT_BUILD || DEBUG ExtBuildInfoHelper.CreateFirstExtBuildInfo(info.buildTicks, info.workSpace, info.version, info.buildCode, true, defines); #else ExtBuildInfoHelper.CreateFirstExtBuildInfo(info.buildTicks, info.workSpace, info.version, false, defines); #endif SetEnableHotUpdate(enableHotUpdate); if (enableHotUpdate) { BuildHotUpdateDlls(); } YooAssetBuildBase.BuildContentAndPlayerInEditor(); ExtBuildInfoHelper.CreateVertifyBuildInfo(info.workSpace, info.version); if (!string.IsNullOrEmpty(BatchBuild._oldDefines)) BatchBuild._RevertScriptDefines(); var aotDllStorePath = BatchBuild.GetAotDllStorePath(info.workSpace, info.version); var aotDllPath = SettingsUtil.GetAssembliesPostIl2CppStripDir(EditorUserBuildSettings.activeBuildTarget); DirectoryInfo dir = new(aotDllPath); if (Directory.Exists(aotDllStorePath)) Directory.Delete(aotDllStorePath, true); Directory.CreateDirectory(aotDllStorePath); FileInfo[] files = dir.GetFiles(); foreach (FileInfo file in files) { file.CopyTo(Path.Combine(aotDllStorePath, file.Name), false); } } /// /// 只构建Player /// public static void BuildPlayer() { YooAssetBuildBase.BuildPlayerInEditor(); } /// /// 编辑器下热更 /// public static void UpdatePreviousBuild(string workSpace, string version, BuildTarget buildTarget) { ExtBuildInfoHelper.VertifyExitBuildInfo(YooAssetBuildBase.workSpace, YooAssetBuildBase.appVersion); Debug.Log("开始检查AOT版本"); if (BatchBuild.CheckAotOutDate(YooAssetBuildBase.workSpace, YooAssetBuildBase.appVersion)) { Debug.Log("AOT版本检查通过"); BuildHotUpdateDlls(); } else { Debug.LogError("AOT版本不匹配,请重新打AOT包"); } BuildTargetGroup currentTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget); string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(currentTargetGroup); // 写入打包信息到Yoo_Build_Cache #if DEVELOPMENT_BUILD || DEBUG ExtBuildInfoHelper.UpdateExtBuildInfo(YooAssetBuildBase.workSpace, YooAssetBuildBase.appVersion, true, defines); #else ExtBuildInfoHelper.UpdateExtBuildInfo(YooAssetBuildBase.workSpace, YooAssetBuildBase.appVersion, false, defines); #endif YooAssetBuildBase.BuildOnlyResUpdate(); ExtBuildInfoHelper.CreateVertifyBuildInfo(YooAssetBuildBase.workSpace, YooAssetBuildBase.appVersion); YooAssetBuildBase.UploadToServerYooAsset(workSpace, version, buildTarget); /*try { } catch (Exception e) { DebugUtil.LogError($"[Build Exception]:{e.Message} \r\n {e.StackTrace}"); throw; }*/ } public static void UploadToServer(string workSpace, string version, BuildTarget buildTarget) { YooAssetBuildBase.UploadToServerYooAsset(workSpace, version, buildTarget); } public static void GenerateObfuzLinkXml() { //CompileDllCommand.CompileDllActiveBuildTarget(); PrebuildCommandExt.CompileAndObfuscateDll(); BuildTarget target = EditorUserBuildSettings.activeBuildTarget; ObfuzSettings obfuzSettings = ObfuzSettings.Instance; List assemblySearchDirs = new() { SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target), }; ObfuscatorBuilder builder = ObfuscatorBuilder.FromObfuzSettings(obfuzSettings, target, true); builder.InsertTopPriorityAssemblySearchPaths(assemblySearchDirs); Obfuscator obfuz = builder.Build(); obfuz.Run(); List hotfixAssemblies = SettingsUtil.HotUpdateAssemblyNamesExcludePreserved; var analyzer = new Analyzer(new PathAssemblyResolver(builder.CoreSettingsFacade.obfuscatedAssemblyOutputPath)); var refTypes = analyzer.CollectRefs(hotfixAssemblies); // HyridCLR中 LinkXmlWritter不是public的,在其他程序集无法访问,只能通过反射操作 var linkXmlWriter = typeof(SettingsUtil).Assembly.GetType("HybridCLR.Editor.Link.LinkXmlWriter"); var writeMethod = linkXmlWriter.GetMethod("Write", BindingFlags.Public | BindingFlags.Instance); var instance = Activator.CreateInstance(linkXmlWriter); string linkXmlOutputPath = $"{Application.dataPath}/Obfuz/link.xml"; writeMethod.Invoke(instance, new object[] { linkXmlOutputPath, refTypes }); Debug.Log($"[GenerateLinkXmlForObfuscatedAssembly] output:{linkXmlOutputPath}"); AssetDatabase.Refresh(); } }