123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using GFGEditor;
- using UnityEditor;
- using UnityEngine;
- namespace VEngine.Editor.Builds
- {
- public class BuildTask
- {
- private readonly string[] EXCLUDE_EXTS = new string[] { ".meta" };
- private readonly string[] EXCLUDE_DIRS = new string[] { "Assets/Res/.svn", ImportArtResTool.Md5FilePath };
- private readonly BuildAssetBundleOptions buildAssetBundleOptions;
- private readonly List<Asset> bundledAssets = new List<Asset>();
- private readonly string bundleExtension;
- public readonly string name;
- private readonly Dictionary<string, Asset> pathWithAssets = new Dictionary<string, Asset>();
- public BuildTask()
- {
- name = nameof(Manifest);
- buildAssetBundleOptions = BuildAssetBundleOptions.ChunkBasedCompression |
- BuildAssetBundleOptions.AppendHashToAssetBundleName;
- bundleExtension = ".unity3d";
- }
- public Record record { get; private set; }
- private static string GetRecordsPath(string buildName)
- {
- return Settings.GetBuildPath($"build_records_for_{buildName}.json");
- }
- private static void WriteRecord(Record record)
- {
- var records = GetRecords(record.build);
- records.data.Insert(0, record);
- File.WriteAllText(GetRecordsPath(record.build), JsonUtility.ToJson(records));
- }
- private static Records GetRecords(string build)
- {
- var records = ScriptableObject.CreateInstance<Records>();
- var path = GetRecordsPath(build);
- if (File.Exists(path)) JsonUtility.FromJsonOverwrite(File.ReadAllText(path), records);
- return records;
- }
- private static void DisplayProgressBar(string title, string content, int index, int max)
- {
- EditorUtility.DisplayProgressBar($"{title}({index}/{max}) ", content,
- index * 1f / max);
- }
- public void BuildBundles()
- {
- var assetBundleNames = AssetDatabase.GetAllAssetBundleNames();
- for (var i = 0; i < assetBundleNames.Length; i++)
- {
- var assetBundleName = assetBundleNames[i];
- DisplayProgressBar("采集资源", assetBundleName, i, assetBundleNames.Length);
- var assetNames = AssetDatabase.GetAssetPathsFromAssetBundle(assetBundleName);
- bundledAssets.AddRange(Array.ConvertAll(assetNames, input => new Asset
- {
- path = input,
- bundle = assetBundleName
- }));
- }
- CheckAssets();
- EditorUtility.ClearProgressBar();
- FinishBuild();
- }
- public void BuildCustomBundles(string[] resRootDirNames, string setName)
- {
- foreach (var resRootDirName in resRootDirNames)
- {
- CreateBundles(resRootDirName, setName);
- }
- CheckAssets();
- EditorUtility.ClearProgressBar();
- FinishBuild();
- }
- private void CreateBundles(string resRootDirName, string setName)
- {
- var path = Path.Combine(Application.dataPath, resRootDirName);
- var buildSetting = GFGEditor.BuildSetting.GetBuildSetting(setName);
- var dirBundleList = buildSetting.dirBundleList;
- var excludeDirs = buildSetting.dirBundleList.GetRange(0, buildSetting.dirBundleList.Count);
- excludeDirs.AddRange(EXCLUDE_DIRS);
- GFGEditor.FileUtil.ForeachFileInDir(path, excludeDirs, (string file) =>
- {
- var ext = Path.GetExtension(file);
- if (Array.IndexOf(EXCLUDE_EXTS, ext) < 0)
- {
- file = file.Replace('\\', '/');
- string curDir = Environment.CurrentDirectory.Replace("\\", "/");
- string filePath = file.Replace(curDir + "/", "");
- EditorUtility.DisplayProgressBar("采集资源", filePath, 1f);
- var bundle = filePath.Replace($"Assets/{resRootDirName}/", "");
- bundle = bundle.Replace('/', '_');
- var i = bundle.IndexOf(".");
- bundle = bundle.Substring(0, i);
- bundle = bundle.ToLower();
- //以文件名标识分组
- for (int j = 0; j < buildSetting.dirTypeList.Count; j++)
- {
- string str = buildSetting.dirTypeList[j];
- if (bundle.IndexOf(str) >= 0)
- {
- bundle = str.Substring(0, str.Length - 1);
- break;
- }
- }
- bundledAssets.Add(new Asset
- {
- path = filePath,
- bundle = bundle
- });
- }
- });
- //以子文件夹为单位分组
- foreach (var dir in dirBundleList)
- {
- var dirPath = Path.Combine(Environment.CurrentDirectory, dir);
- if (!GFGEditor.FileUtil.CheckPathInParent(dirPath, path))
- {
- continue;
- }
- GFGEditor.FileUtil.ForeachDirInDir(dirPath, (string subDirPath) =>
- {
- var targetDirPath = subDirPath.Replace('\\', '/');
- string curDirPath = Environment.CurrentDirectory.Replace("\\", "/");
- string subDir = targetDirPath.Replace(curDirPath + "/", "");
- var bundle = subDir.Replace($"Assets/{resRootDirName}/", "");
- bundle = bundle.Replace('/', '_');
- bundle = bundle.ToLower();
- GFGEditor.FileUtil.ForeachFileInDir(subDirPath, null, (string file) =>
- {
- var ext = Path.GetExtension(file);
- file = file.Replace('\\', '/');
- string curDir = Environment.CurrentDirectory.Replace("\\", "/");
- string filePath = file.Replace(curDir + "/", "");
- EditorUtility.DisplayProgressBar("采集资源", filePath, 1f);
- bundledAssets.Add(new Asset
- {
- path = filePath,
- bundle = bundle
- });
- });
- });
- }
- }
- private void CheckAssets()
- {
- for (var i = 0; i < bundledAssets.Count; i++)
- {
- var asset = bundledAssets[i];
- //去重
- if (!pathWithAssets.TryGetValue(asset.path, out var ba))
- {
- pathWithAssets[asset.path] = asset;
- }
- else
- {
- bundledAssets.RemoveAt(i);
- i--;
- Debug.LogWarningFormat("{0} can't pack with {1}, because already pack to {2}", asset.path,
- asset.bundle, ba.bundle);
- }
- }
- }
- private void FinishBuild()
- {
- var bundles = new List<ManifestBundle>();
- var dictionary = new Dictionary<string, List<string>>();
- //分组
- foreach (var asset in bundledAssets)
- {
- if (!dictionary.TryGetValue(asset.bundle, out var assets))
- {
- assets = new List<string>();
- dictionary.Add(asset.bundle, assets);
- bundles.Add(new ManifestBundle
- {
- name = asset.bundle,
- assets = assets
- });
- }
- assets.Add(asset.path);
- }
- var unityBuildPath = Settings.PlatformBuildPath;
- if (!string.IsNullOrEmpty(GFGGame.LauncherConfig.resKey))
- {
- unityBuildPath = Settings.UnityBuildPath;
- }
- if (bundles.Count <= 0) return;
- var manifest = BuildPipeline.BuildAssetBundles(unityBuildPath, bundles.ConvertAll(bundle =>
- new AssetBundleBuild
- {
- assetNames = bundle.assets.ToArray(),
- assetBundleName = bundle.name
- }).ToArray(),
- buildAssetBundleOptions | BuildAssetBundleOptions.AppendHashToAssetBundleName,
- EditorUserBuildSettings.activeBuildTarget);
- if (manifest == null)
- {
- Debug.LogErrorFormat("Failed to build {0}.", name);
- return;
- }
- //if (!string.IsNullOrEmpty(GFGGame.LauncherConfig.resKey))
- //{
- // string buildPath = Settings.PlatformBuildPath;
- // CreateEncryptAssets(unityBuildPath, buildPath, manifest, GFGGame.LauncherConfig.resKey);
- //}
- AfterBuildBundles(bundles, manifest);
- }
- /// <summary>
- /// 创建加密的AssetBundle
- /// </summary>
- public static void CreateEncryptAssets(string bundlePackagePath, string encryptAssetPath, AssetBundleManifest manifest, string secretKey)
- {
- if (!Directory.Exists(encryptAssetPath))
- {
- Directory.CreateDirectory(encryptAssetPath);
- }
- string[] assetBundles = manifest.GetAllAssetBundles();
- foreach (string assetBundle in assetBundles)
- {
- string bundlePath = Path.Combine(bundlePackagePath, assetBundle);
- byte[] encryptBytes = EncryptHelper.CreateEncryptData(bundlePath, secretKey);
- using (FileStream fs = new FileStream(Path.Combine(encryptAssetPath, assetBundle), FileMode.OpenOrCreate))
- {
- fs.SetLength(0);
- fs.Write(encryptBytes, 0, encryptBytes.Length);
- }
- }
- }
- /// <summary>
- /// 创建加密的AssetBundle
- /// </summary>
- public static void CreateEncryptAsset(string unityBundlesPath, string bundlesPath, string unityBundle, string secretKey)
- {
- if (!Directory.Exists(bundlesPath))
- {
- Directory.CreateDirectory(bundlesPath);
- }
- string unityBundlePath = Path.Combine(unityBundlesPath, unityBundle);
- byte[] encryptBytes = EncryptHelper.CreateEncryptData(unityBundlePath, secretKey);
- using (FileStream fs = new FileStream(Path.Combine(bundlesPath, unityBundle), FileMode.OpenOrCreate))
- {
- fs.SetLength(0);
- fs.Write(encryptBytes, 0, encryptBytes.Length);
- }
- }
- private string GetOriginBundle(string assetBundle)
- {
- var pos = assetBundle.LastIndexOf("_", StringComparison.Ordinal) + 1;
- var hash = assetBundle.Substring(pos);
- if (!string.IsNullOrEmpty(bundleExtension)) hash = hash.Replace(bundleExtension, "");
- var originBundle = $"{assetBundle.Replace("_" + hash, "")}";
- return originBundle;
- }
- private void AfterBuildBundles(List<ManifestBundle> bundles,
- AssetBundleManifest manifest)
- {
- var nameWithBundles = new Dictionary<string, ManifestBundle>();
- for (var i = 0; i < bundles.Count; i++)
- {
- var bundle = bundles[i];
- bundle.id = i;
- nameWithBundles[bundle.name] = bundle;
- }
- if (manifest != null)
- {
- var assetBundles = manifest.GetAllAssetBundles();
- foreach (var assetBundle in assetBundles)
- {
- var originBundle = GetOriginBundle(assetBundle);
- var dependencies = Array.ConvertAll(manifest.GetAllDependencies(assetBundle), GetOriginBundle);
- if (nameWithBundles.TryGetValue(originBundle, out var manifestBundle))
- {
- manifestBundle.nameWithAppendHash = assetBundle;
- manifestBundle.dependencies = Array.ConvertAll(dependencies, input => nameWithBundles[input].id);
-
- }
- else
- {
- Debug.LogErrorFormat("Bundle not exist: {0}", originBundle);
- }
- }
- }
- CreateManifest(bundles);
- }
- private void CreateManifest(List<ManifestBundle> bundles)
- {
- var manifest = Settings.GetManifest();
- manifest.version++;
- manifest.appVersion = UnityEditor.PlayerSettings.bundleVersion;
- var getBundles = manifest.GetBundles();
- var newFiles = new List<string>();
- var newSize = 0L;
- foreach (var bundle in bundles)
- if (!getBundles.TryGetValue(bundle.name, out var value) ||
- value.nameWithAppendHash != bundle.nameWithAppendHash)
- {
- newFiles.Add(bundle.nameWithAppendHash);
- if (!string.IsNullOrEmpty(GFGGame.LauncherConfig.resKey))
- {
- string buildPath = Settings.PlatformBuildPath;
- CreateEncryptAsset(Settings.UnityBuildPath, buildPath, bundle.nameWithAppendHash, GFGGame.LauncherConfig.resKey);
- }
- var file = Settings.GetBuildPath(bundle.nameWithAppendHash);
- if (File.Exists(file))
- using (var stream = File.OpenRead(file))
- {
- bundle.size = stream.Length;
- bundle.crc = Utility.ComputeCRC32(stream);
- }
- else
- Debug.LogErrorFormat("File not found: {0}", file);
- newSize += bundle.size;
- }
- manifest.bundles = bundles;
- var newFilesSize = Utility.FormatBytes(newSize);
- newFiles.AddRange(WriteManifest(manifest));
- // write upload files
- var filename = Settings.GetBuildPath($"upload_files_for_{manifest.name}_{manifest.version}.txt");
- File.WriteAllText(filename, string.Join("\n", newFiles.ToArray()));
- record = new Record
- {
- build = name,
- version = manifest.version,
- files = newFiles,
- size = newSize,
- time = DateTime.Now.ToFileTime()
- };
- WriteRecord(record);
- Debug.LogFormat("Build bundles with {0}({1}) files with version {2} for {3}.", newFiles.Count, newFilesSize,
- manifest.version, manifest.name);
- }
- private static IEnumerable<string> WriteManifest(Manifest manifest)
- {
- var newFiles = new List<string>();
- var filename = $"{manifest.name}";
- var version = manifest.version;
- WriteJson(manifest, filename, newFiles);
- var path = Settings.GetBuildPath(filename);
- var crc = Utility.ComputeCRC32(path);
- var info = new FileInfo(path);
- WriteJson(manifest, $"{filename}_v{version}_{crc}", newFiles);
- // for version file
- var manifestVersion = ScriptableObject.CreateInstance<ManifestVersion>();
- manifestVersion.crc = crc;
- manifestVersion.size = info.Length;
- manifestVersion.version = version;
- manifestVersion.appVersion = manifest.appVersion;
- WriteJson(manifestVersion, Manifest.GetVersionFile(filename), newFiles);
- WriteJson(manifestVersion, $"{filename}_v{version}_{crc}.version", newFiles);
- return newFiles;
- }
- private static void WriteJson(ScriptableObject so, string file, List<string> newFiles)
- {
- newFiles.Add(file);
- var json = JsonUtility.ToJson(so);
- File.WriteAllText(Settings.GetBuildPath(file), json);
- }
- }
- }
|