public static IEnumerator BuildResAsync(IList <string> assets, IEditorWorkProgressShower winprog, IList <IResBuilderEx> runOnceExBuilder) { bool isDefaultBuild = assets == null; var logger = new EditorWorkProgressLogger() { Shower = winprog }; bool shouldCreateBuildingParams = BuildingParams == null; BuildingParams = BuildingParams ?? ResBuilderParams.Create(); var timetoken = BuildingParams.timetoken; var makezip = BuildingParams.makezip; string outputDir = "Latest"; if (!isDefaultBuild) { outputDir = timetoken + "/build"; } outputDir = "EditorOutput/Build/" + outputDir; System.IO.StreamWriter swlog = null; try { System.IO.Directory.CreateDirectory(outputDir + "/log/"); swlog = new System.IO.StreamWriter(outputDir + "/log/ResBuildLog.txt", false, System.Text.Encoding.UTF8); } catch (Exception e) { Debug.Log(e); } EditorApplication.LockReloadAssemblies(); List <IResBuilderEx> allExBuilders = new List <IResBuilderEx>(ResBuilderEx); if (runOnceExBuilder != null) { allExBuilders.AddRange(runOnceExBuilder); } Application.LogCallback LogToFile = (message, stack, logtype) => { swlog.WriteLine(message); swlog.Flush(); }; if (swlog != null) { Application.logMessageReceived += LogToFile; } for (int i = 0; i < allExBuilders.Count; ++i) { allExBuilders[i].Prepare(outputDir); } bool cleanupDone = false; Action BuilderCleanup = () => { if (!cleanupDone) { logger.Log("(Phase) Build Res Cleaup."); cleanupDone = true; for (int i = 0; i < allExBuilders.Count; ++i) { allExBuilders[i].Cleanup(); } logger.Log("(Done) Build Res Cleaup."); if (swlog != null) { Application.logMessageReceived -= LogToFile; swlog.Flush(); swlog.Dispose(); if (isDefaultBuild) { var logdir = "EditorOutput/Build/" + timetoken + "/log/"; System.IO.Directory.CreateDirectory(logdir); System.IO.File.Copy(outputDir + "/log/ResBuildLog.txt", logdir + "ResBuildLog.txt", true); } } if (shouldCreateBuildingParams) { BuildingParams = null; } EditorApplication.UnlockReloadAssemblies(); } }; if (winprog != null) { winprog.OnQuit += BuilderCleanup; } try { logger.Log("(Start) Build Res."); if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } //logger.Log("(Phase) Ex Full Build System."); //for (int i = 0; i < allExBuilders.Count; ++i) //{ // IEnumerator exwork = allExBuilders[i].CustomBuild(); // if (exwork != null) // { // while (exwork.MoveNext()) // { // if (winprog != null) // { // yield return exwork.Current; // } // } // } // if (winprog != null && AsyncWorkTimer.Check()) yield return null; //} // Generate Build Work Dictionary <string, CapsResBuildWork> works = new Dictionary <string, CapsResBuildWork>(); var work = GenerateBuildWorkAsync(works, assets, winprog, runOnceExBuilder); while (work.MoveNext()) { if (winprog != null) { yield return(work.Current); } } logger.Log("(Phase) Write Manifest."); var managermod = CapsEditorUtils.__MOD__; var manidir = "Assets/Mods/" + managermod + "/Build/"; System.IO.Directory.CreateDirectory(manidir); List <AssetBundleBuild> listManiBuilds = new List <AssetBundleBuild>(); HashSet <string> maniFileNames = new HashSet <string>(); foreach (var kvp in works) { foreach (var mani in kvp.Value.Manifests) { var mod = mani.MFlag; var dist = mani.DFlag; if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } logger.Log("Mod " + mod + "; Dist " + dist); var dmani = CapsResManifest.Save(mani); var filename = "m-" + mod + "-d-" + dist; var manipath = manidir + filename + ".m.asset"; AssetDatabase.CreateAsset(dmani, manipath); maniFileNames.Add(filename.ToLower()); listManiBuilds.Add(new AssetBundleBuild() { assetBundleName = filename + ".m.ab", assetNames = new[] { manipath } }); } } logger.Log("(Phase) Build Manifest."); if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } var buildopt = BuildAssetBundleOptions.DeterministicAssetBundle | BuildAssetBundleOptions.ChunkBasedCompression; BuildTarget buildtar = EditorUserBuildSettings.activeBuildTarget; var outmanidir = outputDir + "/res/mani"; System.IO.Directory.CreateDirectory(outmanidir); BuildPipeline.BuildAssetBundles(outmanidir, listManiBuilds.ToArray(), buildopt, buildtar); logger.Log("(Phase) Delete Unused Manifest."); if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } var manifiles = PlatDependant.GetAllFiles(outmanidir); for (int i = 0; i < manifiles.Length; ++i) { var file = manifiles[i]; if (file.EndsWith(".m.ab")) { var filename = file.Substring(outmanidir.Length + 1, file.Length - outmanidir.Length - 1 - ".m.ab".Length); if (!maniFileNames.Contains(filename)) { PlatDependant.DeleteFile(file); PlatDependant.DeleteFile(file + ".manifest"); } } } logger.Log("(Phase) Real Build."); foreach (var kvp in works) { var mod = kvp.Key; var abs = kvp.Value.ABs; logger.Log("Mod " + mod); if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } var dest = outputDir + "/res"; if (!string.IsNullOrEmpty(mod)) { dest += "/mod/" + mod; } System.IO.Directory.CreateDirectory(dest); // delete the unused ab HashSet <string> buildFiles = new HashSet <string>(); for (int i = 0; i < abs.Length; ++i) { if (!kvp.Value.ForceRefreshABs.Contains(i)) { if (string.IsNullOrEmpty(abs[i].assetBundleVariant)) { buildFiles.Add(abs[i].assetBundleName.ToLower()); } else { buildFiles.Add(abs[i].assetBundleName.ToLower() + "." + abs[i].assetBundleVariant.ToLower()); } } } var files = System.IO.Directory.GetFiles(dest); for (int i = 0; i < files.Length; ++i) { var file = files[i]; if (!file.EndsWith(".ab")) { var sub = System.IO.Path.GetFileName(file); var split = sub.LastIndexOf(".ab."); if (split < 0) { continue; } var ext = sub.Substring(split + ".ab.".Length); if (ext.Contains(".")) { continue; } if (ext == "manifest") { continue; } } { var fileName = System.IO.Path.GetFileName(file); if (!buildFiles.Contains(fileName)) { PlatDependant.DeleteFile(file); PlatDependant.DeleteFile(file + ".manifest"); } } } BuildPipeline.BuildAssetBundles(dest, abs, buildopt, buildtar); } logger.Log("(Phase) Delete Mod Folder Not Built."); var outmoddir = outputDir + "/res/mod/"; if (System.IO.Directory.Exists(outmoddir)) { var builtMods = new HashSet <string>(works.Keys); var allModFolders = System.IO.Directory.GetDirectories(outmoddir); int deletedModFolderCnt = 0; for (int i = 0; i < allModFolders.Length; ++i) { if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } var modfolder = allModFolders[i]; logger.Log(modfolder); var mod = modfolder.Substring(outmoddir.Length); if (!builtMods.Contains(mod)) { System.IO.Directory.Delete(modfolder, true); ++deletedModFolderCnt; } } if (deletedModFolderCnt == allModFolders.Length) { System.IO.Directory.Delete(outmoddir, true); } } if (isDefaultBuild) { logger.Log("(Phase) Write Version."); var outverdir = outputDir + "/res/version.txt"; int version = GetResVersion(); System.IO.File.WriteAllText(outverdir, version.ToString()); } logger.Log("(Phase) Copy."); var outresdir = outputDir + "/res/"; var allbuildfiles = PlatDependant.GetAllFiles(outresdir); if (System.IO.Directory.Exists("Assets/StreamingAssets/res/")) { logger.Log("Delete old."); var allexistfiles = PlatDependant.GetAllFiles("Assets/StreamingAssets/res/"); for (int i = 0; i < allexistfiles.Length; ++i) { if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } PlatDependant.DeleteFile(allexistfiles[i]); } } for (int i = 0; i < allbuildfiles.Length; ++i) { if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } var srcfile = allbuildfiles[i]; if (srcfile.EndsWith(".manifest")) { continue; } var part = srcfile.Substring(outresdir.Length); if (part == "mani/mani") { continue; } logger.Log(part); var destfile = "Assets/StreamingAssets/res/" + part; PlatDependant.CreateFolder(System.IO.Path.GetDirectoryName(destfile)); System.IO.File.Copy(srcfile, destfile); } if (System.IO.Directory.Exists("Assets/StreamingAssets/res/mod/")) { logger.Log("(Phase) Delete StreamingAssets Mod Folder Not Built."); var builtMods = new HashSet <string>(works.Keys); var allModFolders = System.IO.Directory.GetDirectories("Assets/StreamingAssets/res/mod/"); int deletedModFolderCnt = 0; for (int i = 0; i < allModFolders.Length; ++i) { if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } var modfolder = allModFolders[i]; logger.Log(modfolder); var mod = modfolder.Substring("Assets/StreamingAssets/res/mod/".Length); if (!builtMods.Contains(mod)) { System.IO.Directory.Delete(modfolder, true); ++deletedModFolderCnt; } } if (deletedModFolderCnt == allModFolders.Length) { System.IO.Directory.Delete("Assets/StreamingAssets/res/mod/", true); } } if (isDefaultBuild && makezip) { logger.Log("(Phase) Zip."); List <Pack <string, string, IList <string> > > zips = new List <Pack <string, string, IList <string> > >(); var outzipdir = "EditorOutput/Build/" + timetoken + "/whole/res/"; System.IO.Directory.CreateDirectory(outzipdir); foreach (var kvp in works) { var mod = kvp.Key; var manis = kvp.Value.Manifests; for (int i = 0; i < manis.Length; ++i) { var opmod = manis[i].MFlag; var dist = manis[i].DFlag; if (winprog != null && AsyncWorkTimer.Check()) { yield return(null); } logger.Log("Mod " + opmod + "; Dist " + dist); List <string> entries = new List <string>(); // abs var abdir = outputDir + "/res"; if (!string.IsNullOrEmpty(mod)) { abdir += "/mod/" + mod; } if (System.IO.Directory.Exists(abdir)) { try { var files = System.IO.Directory.GetFiles(abdir); for (int j = 0; j < files.Length; ++j) { var file = files[j]; if (!file.EndsWith(".ab")) { var sub = System.IO.Path.GetFileName(file); var split = sub.LastIndexOf(".ab."); if (split < 0) { continue; } var ext = sub.Substring(split + ".ab.".Length); if (ext.Contains(".")) { continue; } if (ext == "manifest") { continue; } } { var bundle = file.Substring(abdir.Length + 1); if (IsBundleInModAndDist(bundle, opmod, dist)) { var entry = file.Substring(outputDir.Length + 1); entries.Add(entry); entries.Add(entry + ".manifest"); } } } } catch (Exception e) { logger.Log("(Error)(Not Critical)"); logger.Log(e.ToString()); } } if (entries.Count > 0) { // unity build mani var umani = abdir + "/" + (string.IsNullOrEmpty(mod) ? "res" : mod); umani = umani.Substring(outputDir.Length + 1); entries.Add(umani); entries.Add(umani + ".manifest"); // mani var mani = "m-" + opmod.ToLower() + "-d-" + dist.ToLower() + ".m.ab"; mani = "res/mani/" + mani; entries.Add(mani); entries.Add(mani + ".manifest"); entries.Add("res/mani/mani"); entries.Add("res/mani/mani.manifest"); // version entries.Add("res/version.txt"); var zipfile = outzipdir + "m-" + opmod + "-d-" + dist + ".zip"; zips.Add(new Pack <string, string, IList <string> >(zipfile, outputDir, entries)); //var workz = MakeZipAsync(zipfile, outputDir, entries, winprog); //while (workz.MoveNext()) //{ // if (winprog != null) // { // yield return workz.Current; // } //} } } } if (zips.Count > 0) { var workz = CapsResBuilder.MakeZipsBackground(zips, winprog); while (workz.MoveNext()) { if (winprog != null) { yield return(workz.Current); } } } } for (int i = 0; i < allExBuilders.Count; ++i) { allExBuilders[i].OnSuccess(); } } finally { BuilderCleanup(); logger.Log("(Done) Build Res."); } }