using XGame.Framework.Asset; using System.Collections.Generic; using System.IO; using System.Linq; using XGame.Framework.Json; namespace XGame.Editor.Build.AssetBundles { internal class AssetBundleGroupsTask { public class AssetbundleGroupsManifest { //public UnityEditor.AssetBundleBuild[] bundleBuilds; public AssetbundleGroup[] bundleGroups; } public class AssetbundleGroup { public int groupId; public AssetbundleData[] bundles; } public class AssetbundleData { public uint bundleId; public int buildIndex; public uint[] dependencies; } public enum AbGroupMode { Normal = 0, Dependencies, } private string _outputPath; private AbGroupMode _groupMode; //public AssetBundleGroupsTask() //{ // _outputPath = string.Empty; // _groupMode = AbGroupMode.Dependencies; //} public AssetBundleGroupsTask(string outputPath, AbGroupMode groupMode) { _outputPath = outputPath; _groupMode = groupMode; if (string.IsNullOrEmpty(outputPath)) return; var dirPath = Path.GetDirectoryName(outputPath); if (!Directory.Exists(dirPath)) { Directory.CreateDirectory(dirPath); BuildLog.Log($"AssetBundleGroupsTask CreateDirectory: {dirPath}"); } } public bool Excute() { XBuildPipeline.GenerateBundleManifests(); var manifest = AssetBundleUtils.LoadManifest(); if (manifest == null || manifest.bundleInfos == null) return false; if (!BundleGroup(manifest.bundleInfos, out var bundleGroupMap)) { return false; } return Export(manifest.bundleInfos, bundleGroupMap); } internal bool BundleGroup(AssetBundleInfo[] bundleInfos, out Dictionary bundleGroupMap) { if (_groupMode == AbGroupMode.Dependencies) { return BundleGroupByDependencies(bundleInfos, out bundleGroupMap); } return BundleGroupNormal(bundleInfos, out bundleGroupMap); } private bool BundleGroupNormal(AssetBundleInfo[] bundleInfos, out Dictionary bundleGroupMap) { bundleGroupMap = new Dictionary(); var groupIndex = 0; var bundleIds = new HashSet(); var groups = new HashSet(); foreach (var bundleInfo in bundleInfos) { //先找出该bundle和依赖的所有groupId var min = -1; if (bundleGroupMap.TryGetValue(bundleInfo.bundleId, out var groupId)) { min = groupId; groups.Add(groupId); } if (bundleInfo.dependencies != null) { foreach (var dependence in bundleInfo.dependencies) { if (bundleGroupMap.TryGetValue(dependence, out groupId)) { groups.Add(groupId); if (min == -1 || groupId < min) min = groupId; } } } if (groups.Count > 0) { groups.Remove(min); if (groups.Count > 0) { //关联的bundle也必须同步修改groupId foreach (var key in bundleIds) { var val = bundleGroupMap[key]; if (groups.Contains(val)) { bundleGroupMap[key] = min; } } } //取groupId最小值 groupId = min; } else { //新增一个Group groupId = groupIndex++; } //设置GroupId if (!bundleGroupMap.ContainsKey(bundleInfo.bundleId)) { bundleGroupMap[bundleInfo.bundleId] = groupId; bundleIds.Add(bundleInfo.bundleId); } if (bundleInfo.dependencies != null) { foreach (var dependence in bundleInfo.dependencies) { if (!bundleGroupMap.ContainsKey(dependence)) { bundleGroupMap[dependence] = groupId; bundleIds.Add(dependence); } } } //清除缓存 groups.Clear(); } return true; } private bool BundleGroupByDependencies(AssetBundleInfo[] bundleInfos, out Dictionary bundleGroupMap) { bundleGroupMap = new Dictionary(); var bundlesMap = new Dictionary(); var bundleIds = new List(); foreach (var bundleInfo in bundleInfos) { bundlesMap.Add(bundleInfo.bundleId, bundleInfo); if (bundleInfo.DependenciesCount == 0) { bundleGroupMap.Add(bundleInfo.bundleId, 0); } else { bundleIds.Add(bundleInfo.bundleId); } } var groupId = 1; var tempIds = new List(); while (bundleIds.Count > 0) { foreach (var bundleId in bundleIds) { var bundleInfo = bundlesMap[bundleId]; var isTrue = true; foreach (var dep in bundleInfo.dependencies) { if (!bundleGroupMap.ContainsKey(dep)) { isTrue = false; break; } } if (isTrue) { tempIds.Add(bundleId); } } if (tempIds.Count > 0) { foreach (var bundleId in tempIds) { bundleIds.Remove(bundleId); bundleGroupMap.Add(bundleId, groupId); } groupId += 1; tempIds.Clear(); } else { Log.Error($"Assetbundle分组失败。已分数量:{bundleGroupMap.Count} 剩余:{bundleIds.Count} Group:{groupId}"); return false; } } return true; } private bool Export(AssetBundleInfo[] bundleInfos, Dictionary bundleGroupMap) { var outputPath = _outputPath; var abCount = bundleInfos.Length; var manifest = new AssetbundleGroupsManifest() { //bundleBuilds = new UnityEditor.AssetBundleBuild[abCount], }; var bundleDataMap = new Dictionary(); var abIndex = 0; foreach (var bundleInfo in bundleInfos) { var bundleId = bundleInfo.bundleId; bundleDataMap[bundleId] = new AssetbundleData() { bundleId = bundleId, buildIndex = abIndex, dependencies = bundleInfo.dependencies, }; //manifest.bundleBuilds[abIndex] = bundleBuildMap[bundleId].ToUniBuild(); abIndex++; } var bundleGroups = bundleGroupMap.GroupBy(item => item.Value); var groupCount = bundleGroups.Count(); var abGroups = new List(); foreach (var bundleGroup in bundleGroups) { var count = bundleGroup.Count(); var abGroup = new AssetbundleGroup() { groupId = bundleGroup.Key, bundles = new AssetbundleData[count], }; var bundleIdx = 0; foreach (var item in bundleGroup) { abGroup.bundles[bundleIdx++] = bundleDataMap[item.Key]; } abGroups.Add(abGroup); } abGroups.Sort((a, b) => a.groupId - b.groupId); manifest.bundleGroups = abGroups.ToArray(); var text = XJson.ToJson(manifest); File.WriteAllText(outputPath, text, System.Text.Encoding.UTF8); BuildLog.Log($"GenAssetbundleGroups finish. GroupCount:{groupCount} Path:{outputPath}"); return true; } } }