using System; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; using BFEditor.Resource; using BF; namespace BFEditor.Build { public static class BuildProjectTools { static string AssetBundleITPath = Application.dataPath + "/../Build/output/{0}"; static string AssetBundleCachePath = Application.dataPath + "/../HistoryAssetBundles"; static BuildProjectTools() { BuildAndroidUtils.EnsureJavaHome(); } /// /// 提供给neo出包接口 /// public static void BuildBFPlayer() { var dict = BFEditorUtils.GetCommandLineArgs(); if (dict.TryGetValue("data", out string dataStr)) { Debug.Log("BuildBFPlayer " + dataStr); var json = "{\"infos\":" + dataStr + "}"; var buildInfoCollection = JsonUtility.FromJson(json); if (!BuildBFPlayer(buildInfoCollection.GetFirstInfo())) { throw new Exception(); } } else { throw new Exception(); } } public static bool BuildBFPlayer(BuildInfo buildInfo) { // assetbundles if (!BuildResources(buildInfo, Application.streamingAssetsPath, true)) { return false; } // 只打ab包 if (buildInfo.onlyAssetBundle) { AssetDatabase.Refresh(); return true; } // 删除不进包的多语言 // if (!DeleteOverLanguageAb(buildInfo)) // { // return false; // } // 打包 if (!BuildPlayer(buildInfo)) { Debug.LogError("[bferror]buildPlayer失败"); return false; } AssetDatabase.Refresh(); return true; } static bool BuildPlayer(BuildInfo buildInfo) { Debug.Log("[bfinfo]开始打包, bundleName : " + buildInfo.bundleName + " version : " + buildInfo.version + " mode : " + buildInfo.mode); var watch = new System.Diagnostics.Stopwatch(); watch.Start(); var result = false; if (BuildAndroidUtils.BuildAndroidPlayer(buildInfo)) { result = true; } if (result) { watch.Stop(); Debug.Log("[bfinfo]打包完成,耗时: " + BFEditorUtils.GetTimeStr(watch.ElapsedMilliseconds)); } return result; } /// /// 提供给neo打热更接口 /// public static void BuildResources() { var dict = BFEditorUtils.GetCommandLineArgs(); if (dict.TryGetValue("data", out string dataStr)) { Debug.Log("BuildResources " + dataStr); var json = "{\"infos\":" + dataStr + "}"; var buildInfoCollection = JsonUtility.FromJson(json); var bulidInfo = buildInfoCollection.GetFirstInfo(); var itPath = string.Format(AssetBundleITPath, bulidInfo.mode); if (!BuildResources(bulidInfo, itPath, false)) { throw new Exception(); } } else { throw new Exception(); } } public static bool BuildResources(BuildInfo buildInfo, string outputPath, bool needCopyToITPath) { if (Directory.Exists(outputPath)) { Directory.Delete(outputPath, true); } // 首先尝试从缓存获取 // if (TryGetAbFromCache(buildInfo, outputPath)) // { // // 拷贝资源到it上传路径 // // if (needCopyToITPath) // // { // // CopyResToITPath(buildInfo, outputPath); // // } // return true; // } var watch = new System.Diagnostics.Stopwatch(); watch.Start(); var isRelease = true; Debug.Log("[bfinfo]开始打包资源, bundleName : " + buildInfo.bundleName + " version : " + buildInfo.version + " mode : " + buildInfo.mode); // // 检查平台 // if (EditorUserBuildSettings.activeBuildTarget != buildTarget) // { // Debug.LogError("[bferror]当前没有在对应平台"); // return false; // } // 更新atlas MainAtlasTools.UpdateMainAtlas(); // LanguageAtlasTools.UpdateLanguageAtlas(); // 生成tmp字体 TMPTools.GenFormalFontAsset(false); // 清理fbx默认材质 MaterialTools.ClearFbxDefaultMaterial(); // 清理shader依赖资源 ShaderDependenciesTools.ClearAllShaderDependencies(); // 收集shader变体 ShaderVariantsTools.GenerateVariantCollection(); // ios因为环境问题,先不编译,打版本前提前编译好 if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android) { // 编译并且加密lua代码 if (!CompileScriptsUtils.CompileAndEncryptLua(!isRelease)) { Debug.LogError("[bferror]lua代码编译失败"); return false; } } // 设置abName AssetBundleUtils.SetAllAssetsBundleName(); // 打ab资源 if (!AssetBundleUtils.BuildAssetBundles(EditorUserBuildSettings.activeBuildTarget, outputPath, isRelease, BuildAssetBundleOptions.ChunkBasedCompression, buildInfo.GetLanguageInfo(), buildInfo.version)) { Debug.LogError("[bferror]打ab失败"); return false; } // 检查循环依赖 if (!ABCycleDependUtils.CheckABCycleDepend(outputPath)) { Debug.LogError("[bferror]打ab失败,ab存在循环依赖"); return false; } // 本地缓存ab if (buildInfo.IsPublish()) { Debug.Log("[bfinfo]正在缓存assetbundles..."); BFEditorUtils.CopyDirWithIgnore(outputPath, Path.Combine(AssetBundleCachePath, buildInfo.version), new List { ".meta" }); } // 拷贝资源到it上传路径 // if (needCopyToITPath) // { // CopyResToITPath(buildInfo, outputPath); // } watch.Stop(); Debug.Log("[bfinfo]资源打包完成,耗时: " + BFEditorUtils.GetTimeStr(watch.ElapsedMilliseconds)); return true; } /// /// 尝试从缓存中获取ab,会进行md5校验 /// static bool TryGetAbFromCache(BuildInfo buildInfo, string outputPath) { var cachePath = Path.Combine(AssetBundleCachePath, buildInfo.version); if (!Directory.Exists(cachePath)) { return false; } if (CheckCacheAbMd5(cachePath, buildInfo)) { Debug.Log(string.Format("[bfinfo]正在拷贝本地缓存资源 {0} ---> {1}", cachePath, outputPath)); BFEditorUtils.CopyDirWithIgnore(cachePath, outputPath, new List { ".meta" }); } return true; } public static void CopyResToITPath(BuildInfo buildInfo, string outputPath) { Debug.Log("[bfinfo]正在拷贝assetbundles到运维上传路径..."); var itPath = string.Format(AssetBundleITPath, buildInfo.mode); if (Directory.Exists(itPath)) { Directory.Delete(itPath, true); } BFEditorUtils.CopyDirWithIgnore(outputPath, itPath, new List { ".meta" }); } /// /// 校验缓存资源 /// static bool CheckCacheAbMd5(string abPath, BuildInfo buildInfo) { Debug.Log("[bfinfo]正在校验缓存资源..."); var abccPath = Path.Combine(abPath, "ab_config.bytes"); if (!File.Exists(abccPath)) { Debug.LogError("[bferror]校验缓存资源失败 ab_config.bytes不存在"); return false; } var bytes = File.ReadAllBytes(abccPath); var json = AssetBundleConfigCollection.Decompress(bytes); var abcc = AssetBundleConfigCollection.Create(json); foreach (var abc in abcc.mainConfigs) { var path = Path.Combine(abPath, abc.assetBundlePath); if (!File.Exists(path)) { Debug.LogError(string.Format("[bferror]校验缓存资源失败 {0} 文件丢失", abc.assetBundlePath)); return false; } var md5 = GameLaunchUtils.GetFileMD5(path); if (md5 != abc.md5) { Debug.LogError(string.Format("[bferror]校验缓存资源失败 {0} 文件md5不一致", abc.assetBundlePath)); return false; } } // 只有版本号不一样, 则修改abConfig的版本号 if (abcc.version != buildInfo.version) { Debug.Log(string.Format("[bfinfo]资源版本号不一样,修改版本号,缓存资源版本{0}, 目标版本{1}", abcc.version, buildInfo.version)); abcc.version = buildInfo.version; var newJson = abcc.ToJson(); var newBytes = AssetBundleConfigCollection.Compress(newJson); File.WriteAllBytes(abccPath, newBytes); } Debug.Log("[bfinfo]缓存资源校验完成"); return true; } /// /// 剔除冗余的多语言资源 /// static bool DeleteOverLanguageAb(BuildInfo buildInfo) { var bundleName = buildInfo.bundleName; var languageInfo = BFPlatform.GetLanguageInfo(bundleName); if (languageInfo == null) { Debug.LogError("[bferror]没有对应的多语言info " + bundleName); return false; } foreach (var languagePath in AssetBundleUtils.languagePathList) { var languageAbDirPath = Application.streamingAssetsPath + "/" + languagePath; var subLanguageDirs = Directory.GetDirectories(languageAbDirPath); foreach (var dir in subLanguageDirs) { var dirInfo = new DirectoryInfo(dir); var name = dirInfo.Name; if (languageInfo.Contains(name)) { continue; } Debug.Log("[bfinfo]剔除不进包的多语言资源 " + dirInfo.FullName); dirInfo.Delete(true); } } return true; } } }