using AutoBuild.Helper; using Cysharp.Threading.Tasks; using HybridCLR.Editor; using HybridCLR.Editor.Commands; using HybridCLR.Editor.HotUpdate; using PhxhSDK; using PhxhSDK.AOT.VersionUpdate; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using Obfuz.Settings; using UnityEditor; using UnityEngine; using BuildTarget = UnityEditor.BuildTarget; using Debug = UnityEngine.Debug; using Directory = System.IO.Directory; using YamlDotNet.Serialization; using Obfuz4HybridCLR; /// /// Jenkins打包 /// internal class BatchBuild { /// /// 批处理更新设置 /// jenkins打包新包时使用该方法 /// public static void ApplyPlayerSetting() { InitChannelConfigFromYaml(); YooAssetBuildBase.Init(); } public static string _oldDefines; /// /// 只在DEBUG下有 /// private const string DEBUG = "DEBUG"; /// /// dev上传地址 /// private const string Dev_PostUrl = "http://dev_openapi.kedrgame.com/api/v1/"; /// /// Pre上传地址 /// private const string Pre_PostUrl = "http://pre_openapi.kedrgame.com/api/v1/"; /// /// 海外Release上传地址 /// private const string Oversea_Release_PostUrl = "http://intl_openapi.kedrgame.com/api/v1/"; /// /// Release上传地址 /// private const string Release_PostUrl = "http://openapi.kedrgame.com/api/v1/"; private static void WriteEncryptedJsonResource(string resourceFileNameWithoutExtension, string json) { var resourcesPath = "Assets/Resources"; var jsonPath = Path.Combine(resourcesPath, resourceFileNameWithoutExtension + ".json"); var bytesPath = Path.Combine(resourcesPath, resourceFileNameWithoutExtension + ".bytes"); if (!Directory.Exists(resourcesPath)) { Directory.CreateDirectory(resourcesPath); } if (File.Exists(jsonPath)) { File.Delete(jsonPath); var metaPath = jsonPath + ".meta"; if (File.Exists(metaPath)) { File.Delete(metaPath); } } var encryptedBytes = LocalInfoEncrypt.Encrypt(Encoding.UTF8.GetBytes(json)); File.WriteAllBytes(bytesPath, encryptedBytes); } public static void _RevertScriptDefines() { var currentTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget); PlayerSettings.SetScriptingDefineSymbolsForGroup(currentTargetGroup, _oldDefines); } /// /// 批处理构建Resources和Player /// jenkins打包新包时使用该方法 /// public static void BuildContentAndPlayer() { try { InitChannelConfigFromYaml(); bool enableHotUpdate = YooAssetBuildBase.Init(); string bundleVersionCode = GetBundleVersionCode(); // 写入版本信息到Resources/LocalInfo.json 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 = bundleVersionCode, pathRoot = "Build", phxhBiAccessKeyId = YooAssetBuildBase.phxhBiAccessKeyId, phxhBiScretKey = YooAssetBuildBase.phxhBiScretKey, wxPayAppId = YooAssetBuildBase.wxPayAppId, thinkingDataAppId = YooAssetBuildBase.thinkingDataAppId, thinkingDataServerUrl = YooAssetBuildBase.thinkingDataServerUrl, }; PackDataInst.CreateResourceJson(info); BuildTargetGroup currentTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget); string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(currentTargetGroup); // 写入打包信息到Yoo_Build_Cache bool isDebugBuild = false; #if DEVELOPMENT_BUILD || DEBUG isDebugBuild = true; #endif ExtBuildInfoHelper.CreateFirstExtBuildInfo(info.buildTicks, info.workSpace, info.version, info.buildCode, isDebugBuild, defines); BuildLauncher.SetEnableHotUpdate(enableHotUpdate); if (enableHotUpdate) BuildLauncher.BuildHotUpdateDlls(); YooAssetBuildBase.BuildContentAndPlayer(); // 写入打包信息到Yoo_Build_Cache ExtBuildInfoHelper.CreateVertifyBuildInfo(info.workSpace, info.version); // 还原宏 _RevertScriptDefines(); // 缓存打包时的文件 _CacheBuildFiles(info); PostLoginKey(info.version); // 上传Aot loginKey } catch (Exception e) { //Build Exception用于在日志文件中定位此次打包的异常 Debug.LogError($"[Build Exception]:{e.Message} \r\n {e.StackTrace}"); throw; } } private static void _CacheAotDlls(UpdateLocalInfo info) { string aotDllStorePath = GetAotDllStorePath(info.workSpace, info.version); string 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); } } private static void _CacheObfuzFiles(UpdateLocalInfo info) { // 备份 Assets/Obfuz/SymbolObfus 文件夹 // 备份 Library/Obfuz/{buildTarget} 文件夹 var obfuzSymbolPath = Application.dataPath + "/Obfuz/SymbolObfus"; var obfuzBuildPath = Application.dataPath + $"/../Library/Obfuz/{EditorUserBuildSettings.activeBuildTarget}"; var cacheObfuzPath = Application.dataPath + $"/../Yoo_Build_Cache/{info.workSpace}/{info.version}/CacheObfuz"; var cacheObfuzSymbolPath = cacheObfuzPath + "/SymbolObfus";; var cacheObfuzBuildPath = cacheObfuzPath + $"/{EditorUserBuildSettings.activeBuildTarget}"; if (Directory.Exists(cacheObfuzPath)) Directory.Delete(cacheObfuzPath, true); Directory.CreateDirectory(cacheObfuzPath); DirectoryCopyHelper.CopyDirectory(obfuzSymbolPath, cacheObfuzSymbolPath); DirectoryCopyHelper.CopyDirectory(obfuzBuildPath, cacheObfuzBuildPath); } private static void _CacheBuildFiles(UpdateLocalInfo info) { _CacheAotDlls(info); if (ObfuzSettings.Instance.buildPipelineSettings.enable) { _CacheObfuzFiles(info); } } /// /// 批处理增量更新资源 /// jenkins打包增量更新时使用该方法 /// public static void UpdatePreviousBuild() { try { DebugUtil.Log($"初始化渠道配置:{DateTime.Now}"); InitChannelConfigFromYaml(); DebugUtil.Log($"初始化渠道配置完成:{DateTime.Now}"); var enableHotUpdate = YooAssetBuildBase.Init(); DebugUtil.Log($"YooAssetBuildBase初始化设置完成:{DateTime.Now}"); ExtBuildInfoHelper.VertifyExitBuildInfo(YooAssetBuildBase.workSpace, YooAssetBuildBase.appVersion); DebugUtil.Log($"验证ExBuildInfo完成:{DateTime.Now}"); var skipCheck = true; if (enableHotUpdate) { if (skipCheck) { Debug.Log("跳过检查AOT版本"); BuildLauncher.BuildHotUpdateDlls(); } else { Debug.Log("开始检查AOT版本"); DebugUtil.Log($"开始检查AOT Dll:{DateTime.Now}"); if (CheckAotOutDate(YooAssetBuildBase.workSpace, YooAssetBuildBase.appVersion)) { Debug.Log("AOT版本检查通过"); DebugUtil.Log($"检查AOT Dll完成:{DateTime.Now}"); BuildLauncher.BuildHotUpdateDlls(); } else { DebugUtil.Log($"检查AOT Dll完成:{DateTime.Now}"); Debug.LogError("AOT版本不匹配,请重新打AOT包"); } } } else { Debug.Log("未开启热更"); } 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 DebugUtil.Log($"开始打包热更资源:{DateTime.Now}"); bool result = YooAssetBuildBase.BuildOnlyResUpdate(); DebugUtil.Log($"打包热更资源完成:{DateTime.Now}"); _RevertScriptDefines(); if (result) { ExtBuildInfoHelper.CreateVertifyBuildInfo(YooAssetBuildBase.workSpace, YooAssetBuildBase.appVersion); } else { Debug.LogError("打包资源失败"); throw new Exception("YooAsset打包资源失败"); } } catch (Exception e) { //Build Exception用于在日志文件中定位此次打包的异常 Debug.LogError($"[Build Exception]:{e.Message} \r\n {e.StackTrace}"); throw; } } /// /// jenkins检查AOT裁剪代码 /// /// /// public static bool CheckAotOutDate(string workSpace, string version) { Debug.Log($"开始检查aot版本:{version}"); string stripedDllPath = GetAotDllStorePath(workSpace, version); // 先编译dll // CompileDllCommand.CompileDllActiveBuildTarget(); PrebuildCommandExt.CompileAndObfuscateDll(); // 编译混淆dll // 再检查编译出来的dll是否满足以前的Aotdll MissingMetadataChecker checker = new(stripedDllPath, SettingsUtil.HotUpdateAssemblyNamesIncludePreserved); string hotUpdateDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(EditorUserBuildSettings.activeBuildTarget); foreach (string dll in SettingsUtil.HotUpdateAssemblyFilesExcludePreserved) { string dllPath = $"{hotUpdateDir}/{dll}"; bool notAnyMissing = checker.Check(dllPath); if (!notAnyMissing) throw new Exception($"AOT缺失Dll({dllPath}),请重新打AOT包"); } return true; } /// /// 获取缓存的AOT Dll路径 /// /// public static string GetAotDllStorePath(string workSpace, string version) { return Application.dataPath + $"/../Yoo_Build_Cache/{workSpace}/{version}/CacheAOTDll"; } public static string GetBundleVersionCode() { #if UNITY_IOS return PlayerSettings.iOS.buildNumber; #elif UNITY_ANDROID return PlayerSettings.Android.bundleVersionCode.ToString(); #else return ""; #endif } /// /// 上传登录key /// public static void PostLoginKey(string version) { string loginKey = HashUtils.MD5(version); // 服务端签名原文包含 platform="",按字母升序排列 Dictionary headData = PostHeader.PostHeaderInfo(new Dictionary { { "plaintext", version }, { "platform", "" }, { "tag", loginKey }, { "timestamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString() } }); LoginKeyData loginKeyData = new() { plaintext = version, tag = loginKey, }; try { if (PackDataInst.Inst.localInfo.isDev) { if (PackDataInst.Inst.localInfo.isOversea) HttpHelper.PostAsync($"{Oversea_Release_PostUrl}version", loginKeyData, headData).Forget(); // 暂时海外dev也使用Release else HttpHelper.PostAsync($"{Dev_PostUrl}version", loginKeyData, headData).Forget(); } else { if (PackDataInst.Inst.localInfo.isOversea) HttpHelper.PostAsync($"{Oversea_Release_PostUrl}version", loginKeyData, headData).Forget(); else { HttpHelper.PostAsync($"{Pre_PostUrl}version", loginKeyData, headData).Forget(); HttpHelper.PostAsync($"{Release_PostUrl}version", loginKeyData, headData).Forget(); } } } catch (Exception e) { Debug.LogError($"[PostLoginKey Exception]:{e.Message} \r\n {e.StackTrace}"); throw; } } /// /// 自动发布debug loginKey /// public static void PostDebugSendKey() { string version = PackDataInst.Inst.localInfo.version; string tag = HashUtils.MD5(version); // 服务端签名原文包含 platform="0",按字母升序排列 Dictionary dicHeadData = PostHeader.PostHeaderInfo(new Dictionary { { "plaintext", version }, { "platform", "0" }, { "tag", tag }, { "timestamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString() } }); SendVersionBody body = new() { plaintext = version, tag = tag, platform = "0" }; string url = $"{Dev_PostUrl}sendVersion"; Debug.Log($"[PostDebugSendKey] URL={url}, version={version}, tag(MD5)={tag}"); HttpHelper.PostAsync(url, body, dicHeadData).Forget(); } #if UNITY_EDITOR [UnityEditor.MenuItem("Test/Build/测试 Version (version=1.0.0)")] public static void TestPostDebugSendKey() { const string testVersion = "1.0.0"; string loginKey = HashUtils.MD5(testVersion); string timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(); // 服务端签名算法:MD5(排序参数字符串) + httpKey 再做 SHA256(64位) // 即 PostHeader.PostHeaderInfo 内部已实现的流程,参数需包含 platform="" Dictionary signParams = new Dictionary { { "plaintext", testVersion }, { "platform", "" }, { "tag", loginKey }, { "timestamp", timestamp } }; var sortedKeys = signParams.Keys.OrderBy(k => k).ToList(); string sortParamsStr = string.Join("&", sortedKeys.Select(k => $"{k}={signParams[k]}")); Debug.Log($"[TestPostLoginKey] 签名原文: {sortParamsStr}"); // 使用 PostHeaderInfo 生成 64 位 signature(MD5 + httpKey + SHA256) Dictionary headData = PostHeader.PostHeaderInfo(signParams); Debug.Log($"[TestPostLoginKey] signature: {headData["signature"]}"); LoginKeyData loginKeyData = new() { plaintext = testVersion, tag = loginKey, }; string bodyJson = JsonUtility.ToJson(loginKeyData); Debug.Log($"[TestPostLoginKey] 请求 Body: {bodyJson}"); string testUrl = $"{Dev_PostUrl}version"; try { Debug.Log($"[TestPostLoginKey] URL: {testUrl}"); HttpHelper.PostAsync(testUrl, loginKeyData, headData).Forget(); } catch (Exception e) { Debug.LogError($"[TestPostLoginKey Exception]:{e.Message} \r\n {e.StackTrace}"); throw; } } [UnityEditor.MenuItem("Test/Build/测试 SendVersion (version=1.0.0)")] public static void TestPostSendVersion() { const string testVersion = "1.0.0"; string loginKey = HashUtils.MD5(testVersion); string timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(); // sendVersion 签名参数包含 platform="0"(服务端用 strconv.Atoi 解析) Dictionary signParams = new Dictionary { { "plaintext", testVersion }, { "platform", "0" }, { "tag", loginKey }, { "timestamp", timestamp } }; var sortedKeys = signParams.Keys.OrderBy(k => k).ToList(); string sortParamsStr = string.Join("&", sortedKeys.Select(k => $"{k}={signParams[k]}")); Debug.Log($"[TestPostSendVersion] 签名原文: {sortParamsStr}"); Dictionary headData = PostHeader.PostHeaderInfo(signParams); Debug.Log($"[TestPostSendVersion] signature: {headData["signature"]}"); SendVersionBody body = new() { plaintext = testVersion, tag = loginKey, platform = "0" }; string bodyJson = JsonUtility.ToJson(body); Debug.Log($"[TestPostSendVersion] 请求 Body: {bodyJson}"); string testUrl = $"{Dev_PostUrl}sendVersion"; try { Debug.Log($"[TestPostSendVersion] URL: {testUrl}"); HttpHelper.PostAsync(testUrl, body, headData).Forget(); } catch (Exception e) { Debug.LogError($"[TestPostSendVersion Exception]:{e.Message} \r\n {e.StackTrace}"); throw; } } /// /// 从指定渠道目录读取 ChannelSetting.yaml,返回 ChannelConfig /// private static ChannelConfig LoadChannelConfig(string channelName) { // 路径:ProjectNLD/../Tool/Channels/{channelName}/ChannelSetting.yaml string projectRoot = Directory.GetParent(Application.dataPath)?.Parent?.FullName; string yamlPath = Path.Combine(projectRoot, "Tool", "Channels", channelName, "ChannelSetting.yaml"); Debug.Log($"[LoadChannelConfig] dataPath={Application.dataPath}"); Debug.Log($"[LoadChannelConfig] projectRoot={projectRoot}"); Debug.Log($"[LoadChannelConfig] yamlPath={yamlPath}"); if (!File.Exists(yamlPath)) throw new FileNotFoundException($"[LoadChannelConfig] 找不到渠道配置文件: {yamlPath}"); var deserializer = new DeserializerBuilder().IgnoreUnmatchedProperties().Build(); string yamlContent = File.ReadAllText(yamlPath); var config = deserializer.Deserialize(yamlContent); Debug.Log($"[LoadChannelConfig] 已加载渠道={channelName}, AppAccessKey={config.AppAccessKey}, Version={config.Version}"); return config; } [UnityEditor.MenuItem("Test/Build/测试 SendVersion (MarketTest_Official)")] public static void TestPostSendVersion_MarketTestOfficial() { var config = LoadChannelConfig("MarketTest_Official"); string testVersion = config.Version; string accessKey = config.AppAccessKey; string secretKey = config.AppKey; string tag = HashUtils.MD5(testVersion); string timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(); Dictionary signParams = new Dictionary { { "plaintext", testVersion }, { "platform", "0" }, { "tag", tag }, { "timestamp", timestamp } }; var sortedKeys = signParams.Keys.OrderBy(k => k).ToList(); string signRaw = string.Join("&", sortedKeys.Select(k => $"{k}={signParams[k]}")); Debug.Log($"[TestSendVersion_Official] version={testVersion}, tag(MD5)={tag}"); Debug.Log($"[TestSendVersion_Official] AppAccessKey={accessKey}, AppKey={secretKey}"); Debug.Log($"[TestSendVersion_Official] 签名原文: {signRaw}"); Dictionary headData = PostHeader.PostHeaderInfo(signParams, secretKey, accessKey); Debug.Log($"[TestSendVersion_Official] signature: {headData["signature"]}"); SendVersionBody body = new() { plaintext = testVersion, tag = tag, platform = "0" }; Debug.Log($"[TestSendVersion_Official] 请求 Body: {JsonUtility.ToJson(body)}"); string url = $"{Dev_PostUrl}sendVersion"; try { Debug.Log($"[TestSendVersion_Official] URL: {url}"); HttpHelper.PostAsync(url, body, headData).Forget(); } catch (Exception e) { Debug.LogError($"[TestSendVersion_Official Exception]:{e.Message} \r\n {e.StackTrace}"); throw; } } [UnityEditor.MenuItem("Test/Build/测试 Version (MarketTest_Official)")] public static void TestPostVersion_MarketTestOfficial() { var config = LoadChannelConfig("MarketTest_Official"); string testVersion = config.Version; string accessKey = config.AppAccessKey; string secretKey = config.AppKey; string tag = HashUtils.MD5(testVersion); string timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(); // /version 接口:签名参数 platform="" (与 /sendVersion 的 "0" 不同) Dictionary signParams = new Dictionary { { "plaintext", testVersion }, { "platform", "" }, { "tag", tag }, { "timestamp", timestamp } }; var sortedKeys = signParams.Keys.OrderBy(k => k).ToList(); string signRaw = string.Join("&", sortedKeys.Select(k => $"{k}={signParams[k]}")); Debug.Log($"[TestVersion_Official] version={testVersion}, tag(MD5)={tag}"); Debug.Log($"[TestVersion_Official] AppAccessKey={accessKey}, AppKey={secretKey}"); Debug.Log($"[TestVersion_Official] 签名原文: {signRaw}"); Dictionary headData = PostHeader.PostHeaderInfo(signParams, secretKey, accessKey); Debug.Log($"[TestVersion_Official] signature: {headData["signature"]}"); // /version Body 不含 platform 字段 LoginKeyData body = new() { plaintext = testVersion, tag = tag, }; Debug.Log($"[TestVersion_Official] 请求 Body: {JsonUtility.ToJson(body)}"); try { string url = $"{Dev_PostUrl}version"; Debug.Log($"[TestVersion_Official] URL: {url}"); HttpHelper.PostAsync(url, body, headData).Forget(); } catch (Exception e) { Debug.LogError($"[TestVersion_Official Exception]:{e.Message} \r\n {e.StackTrace}"); throw; } } #endif [System.Serializable] private class LoginKeyData { public string plaintext; public string tag; public string platform; } /// /// /sendVersion 接口 Body /// platform 服务端内部用 strconv.Atoi 解析,必须为可转整数的字符串(空字符串会报错) /// [System.Serializable] private class SendVersionBody { public string plaintext; public string tag; public string platform = "0"; // 服务端需要可转整数的字符串,"0" 表示未指定平台 } private class LoginKeyResData { public int code; public string message; public object data; } private class ChannelConfig { public List Define_list { get; set; } public string PackageName { get; set; } public bool IsOversea { get; set; } public int BuildCode { get; set; } public string ConfigAccessKeyId { get; set; } public string ConfigAccessKeySecret { get; set; } public string ConfigBucketName { get; set; } public string ConfigURL { get; set; } public string AppAccessKey { get; set; } public string AppKey { get; set; } public string Version { get; set; } public string PhxhBiAccessKeyId { get; set; } public string PhxhBiScretKey { get; set; } public string WxPayAppId { get; set; } public CrashSightConfig CrashSight_Android { get; set; } public CrashSightConfig CrashSight_iOS { get; set; } public TalkingDataConfig TalkingData { get; set; } public ThinkingDataConfig ThinkingData { get; set; } } private class CrashSightConfig { [YamlMember(Alias = "id")] public string Id { get; set; } [YamlMember(Alias = "key")] public string Key { get; set; } } private class TalkingDataConfig { [YamlMember(Alias = "appid")] public string appId { get; set; } [YamlMember(Alias = "channel")] public string channel { get; set; } } private class ThinkingDataConfig { [YamlMember(Alias = "appid")] public string appId { get; set; } [YamlMember(Alias = "serverurl")] public string serverUrl { get; set; } } private const string ChannelSettingYaml = "/../ChannelSetting.yaml"; /// /// 从YAML文件初始化渠道配置 /// private static void InitChannelConfigFromYaml() { List appendDefineList = new(); // 判断是否为开发模式 if (EditorUserBuildSettings.development) { appendDefineList.Add(DEBUG); Debug.Log("追加宏定义:" + DEBUG); } string yamlPath = Application.dataPath + ChannelSettingYaml; if (File.Exists(yamlPath)) { try { // 忽略 YAML 中多余的字段 var deserializer = new DeserializerBuilder() .IgnoreUnmatchedProperties() .Build(); string yamlContent = File.ReadAllText(yamlPath); var config = deserializer.Deserialize(yamlContent); // 处理宏定义 if (config.Define_list != null) { foreach (string define in config.Define_list) { string cleanDefine = define.Trim(); if (!string.IsNullOrEmpty(cleanDefine)) { appendDefineList.Add(cleanDefine); Debug.Log("追加宏定义:" + cleanDefine); } } } // 处理包名 if (!string.IsNullOrEmpty(config.PackageName)) { Debug.Log("设置新包名:" + config.PackageName); if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android) { PlayerSettings.applicationIdentifier = config.PackageName; } } YooAssetBuildBase.isOversea = config.IsOversea; // 处理BuildCode // 若命令行已通过 -buildCode 传入(由 version_code_manager.py 自动递增),优先使用命令行值,不用 YAML 覆盖 bool hasCmdBuildCode = System.Array.Exists( Environment.GetCommandLineArgs(), a => a == "-buildCode"); if (hasCmdBuildCode) { Debug.Log($"[BuildCode] 命令行已传入 -buildCode,跳过 YAML 中 BuildCode={config.BuildCode} 的覆盖"); } else if (config.BuildCode != 0) { Debug.Log("设置BuildCode:" + config.BuildCode); #if UNITY_IOS PlayerSettings.iOS.buildNumber = config.BuildCode.ToString(); #elif UNITY_ANDROID PlayerSettings.Android.bundleVersionCode = config.BuildCode; #endif } // 处理AppConfig上传id if (config.ConfigAccessKeyId != null) { YooAssetBuildBase.appConfigAccessKeyId = config.ConfigAccessKeyId; Debug.Log($"设置AppConfig上传id:{YooAssetBuildBase.appConfigAccessKeyId}"); } // 处理AppConfig上传key if (config.ConfigAccessKeySecret != null) { YooAssetBuildBase.appConfigAccessKeySecret = config.ConfigAccessKeySecret; Debug.Log($"设置AppConfig上传key:{YooAssetBuildBase.appConfigAccessKeySecret}"); } // 处理AppConfig上传桶名 if (config.ConfigBucketName != null) { YooAssetBuildBase.appConfigBucketName = config.ConfigBucketName; Debug.Log($"设置AppConfig上传桶名:{YooAssetBuildBase.appConfigBucketName}"); } // 处理AppConfig上传url if (config.ConfigURL != null) { YooAssetBuildBase.appConfigURL = config.ConfigURL; Debug.Log($"设置AppConfig上传url:{YooAssetBuildBase.appConfigURL}"); } // 处理http请求AccessKey if (config.AppAccessKey != null) { YooAssetBuildBase.httpAccessKey = config.AppAccessKey; Debug.Log($"设置http请求id:{YooAssetBuildBase.httpAccessKey}"); } // 处理http请求AppKey if (config.AppKey != null) { YooAssetBuildBase.httpKey = config.AppKey; Debug.Log($"设置http请求id:{YooAssetBuildBase.httpKey}"); } // 处理BI AccessKeyId if (config.PhxhBiAccessKeyId != null) { YooAssetBuildBase.phxhBiAccessKeyId = config.PhxhBiAccessKeyId; Debug.Log($"设置BI AccessKeyId:{YooAssetBuildBase.phxhBiAccessKeyId}"); } // 处理BI SecretKey if (config.PhxhBiScretKey != null) { YooAssetBuildBase.phxhBiScretKey = config.PhxhBiScretKey; Debug.Log($"设置BI SecretKey:{YooAssetBuildBase.phxhBiScretKey}"); } // 处理微信支付 AppID if (config.WxPayAppId != null) { YooAssetBuildBase.wxPayAppId = config.WxPayAppId; Debug.Log($"设置微信支付 AppID:{YooAssetBuildBase.wxPayAppId}"); } // 处理CrashSight_Android配置 if (config.CrashSight_Android != null && EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android) { var crashSightData = new PhxhSDK.Data.CrashSightData { id = config.CrashSight_Android.Id, key = config.CrashSight_Android.Key }; var json = JsonUtility.ToJson(crashSightData); WriteEncryptedJsonResource("CrashSightData", json); Debug.Log("CrashSight配置已保存:" + crashSightData.id + "|" + crashSightData.key); } // 处理CrashSight_iOS配置 if (config.CrashSight_iOS != null && EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS) { var crashSightData = new PhxhSDK.Data.CrashSightData { id = config.CrashSight_iOS.Id, key = config.CrashSight_iOS.Key }; var json = JsonUtility.ToJson(crashSightData); WriteEncryptedJsonResource("CrashSightData", json); Debug.Log("CrashSight配置已保存:" + crashSightData.id + "|" + crashSightData.key); } if (config.TalkingData != null) { var talkingDataInitTool = new TalkingDataInitTool { appID = config.TalkingData.appId, channel = config.TalkingData.channel }; var json = JsonUtility.ToJson(talkingDataInitTool); WriteEncryptedJsonResource("TalkingDataInitConfig", json); Debug.Log("TalkingData配置已保存:" + talkingDataInitTool.appID + "|" + talkingDataInitTool.channel); } // 处理数数科技(ThinkingData)配置 if (config.ThinkingData != null) { YooAssetBuildBase.thinkingDataAppId = config.ThinkingData.appId; YooAssetBuildBase.thinkingDataServerUrl = config.ThinkingData.serverUrl; Debug.Log($"设置数数科技 AppID:{YooAssetBuildBase.thinkingDataAppId}"); Debug.Log($"设置数数科技 ServerUrl:{YooAssetBuildBase.thinkingDataServerUrl}"); } // 更新宏定义 var currentTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget); var oldDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(currentTargetGroup); _oldDefines = oldDefines; if (appendDefineList.Count > 0) { 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)); } // 关闭Unity闪屏 PlayerSettings.SplashScreen.showUnityLogo = false; } catch (Exception e) { Debug.LogError($"解析YAML配置文件失败: {e.Message}\n{e.StackTrace}"); throw; } } else { Debug.LogError($"找不到渠道配置文件: {yamlPath}"); } } }