| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 | using System;using System.Collections.Generic;using System.IO;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", "Assets/Res/MD5" };        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();                    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 outputPath = Settings.PlatformBuildPath;            if (bundles.Count <= 0) return;            var manifest = BuildPipeline.BuildAssetBundles(outputPath, 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))            {                CreateEncryptAssets(outputPath, outputPath, manifest, GFGGame.LauncherConfig.resKey);            }            AfterBuildBundles(bundles, manifest);        }        /// <summary>        /// 创建加密的AssetBundle        /// </summary>        public static void CreateEncryptAssets(string bundlePackagePath, string encryptAssetPath, AssetBundleManifest manifest, string secretKey)        {            string[] assetBundles = manifest.GetAllAssetBundles();            foreach (string assetBundle in assetBundles)            {                string bundlePath = Path.Combine(bundlePackagePath, assetBundle);                byte[] encryptBytes = EncryptHelper.CreateEncryptData(bundlePath, secretKey);                if (!Directory.Exists(encryptAssetPath))                {                    Directory.CreateDirectory(encryptAssetPath);                }                using (FileStream fs = new FileStream(Path.Combine(bundlePackagePath, assetBundle), 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);                        var file = Settings.GetBuildPath(assetBundle);                        if (File.Exists(file))                            using (var stream = File.OpenRead(file))                            {                                manifestBundle.size = stream.Length;                                manifestBundle.crc = Utility.ComputeCRC32(stream);                            }                        else                            Debug.LogErrorFormat("File not found: {0}", file);                    }                    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);                    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);        }    }}
 |