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