using System.Collections; using System.Collections.Generic; using UnityEngine; using TinaX.VFSKitInternal.Utils; using TinaX.IO; using System.IO; using TinaX.VFSKit.Const; using TinaX.VFSKitInternal; namespace TinaX.VFSKit { public class VFSGroup : IGroup { public string GroupName { get; set; } public GroupHandleMode HandleMode => mOption.GroupAssetsHandleMode; /// /// 扩展的组 /// public bool ExtensionGroup => mOption.ExtensionGroup; /// /// Obfuscate Directory Structure 混淆目录结构 /// public bool ObfuscateDirectoryStructure => mOption.ObfuscateDirectoryStructure; public long ExtensionGroup_MainPackageVersionLimit => mOption.Extension_VFSPackageMinVersion; /// /// 储存FolderPath, 格式:Assets/xx/xxx/ ,必须以斜线“/”结尾。 /// public List FolderPaths { get; private set; } = new List(); public List FolderPathsLower { get; private set; } = new List(); //同样必须以斜线结尾 /// /// Group中的直接Assets名单,格式: Assets/xxx/xx.xxx /// public List AssetPaths { get; private set; } = new List(); public List AssetPathsLower { get; private set; } = new List(); /// /// 当前组的忽略子目录,格式Assets/xxx/xxx/ 必须以斜线“/”结束 /// public List IgnoreSubpath { get; private set; } = new List(); public List IgnoreSubpathLower { get; private set; } = new List(); /// /// 忽略后缀名,小写,开头加点 /// public List IgnoreExtensionLower { get; private set; } = new List(); public List SpecialFolderBuildRules { get; private set; } = new List(); public List SpecialFolderBuildRulesLower { get; private set; } = new List(); public XAssetBundleManifest AssetBundleManifest { get { if (Manifest_Remote != null) return Manifest_Remote; if (Manifest_VirtualDisk != null) return Manifest_VirtualDisk; return Manifest_StreamingAssets; } } public XAssetBundleManifest Manifest_StreamingAssets { get; protected internal set; } public XAssetBundleManifest Manifest_VirtualDisk { get; protected internal set; } public XAssetBundleManifest Manifest_Remote { get; protected internal set; } public FilesHashBook FilesHash_StreamingAssets { get; protected internal set; } public FilesHashBook FilesHash_VirtualDisk { get; protected internal set; } public FilesHashBook FilesHash_Remote { get; protected internal set; } public FilesHashBook FilesHash { get { if (FilesHash_Remote != null) return FilesHash_Remote; if (FilesHash_VirtualDisk != null) return FilesHash_VirtualDisk; return FilesHash_StreamingAssets; } } public FilesHashBook FileHash_Local { get { if (FilesHash_VirtualDisk != null) return FilesHash_VirtualDisk; return FilesHash_StreamingAssets; } } protected VFSGroupOption mOption; internal Loader.IAssetBundleLoader ABLoader; public VFSGroup(VFSGroupOption option) { this.SetOptions(option); ABLoader = new Loader.AssetBundleLoader(); } public void SetOptions(VFSGroupOption option) { mOption = option; GroupName = option.GroupName; foreach(var path in option.FolderPaths) { if (!path.EndsWith("/")) { string _path = path+"/"; FolderPaths.Add(_path); FolderPathsLower.Add(_path.ToLower()); } else { FolderPaths.Add(path); FolderPathsLower.Add(path.ToLower()); } } foreach(var path in option.AssetPaths) { AssetPaths.Add(path); AssetPathsLower.Add(path.ToLower()); } //忽略子目录,子目录必须是FolderPaths的子目录,这里初始化的时候过滤一下无效的配置,节省后面的运算 foreach(var path in option.IgnoreSubPath) { string _path = (path.EndsWith("/")) ? path : path + "/"; string path_lower = _path.ToLower(); foreach(var folder in FolderPathsLower) { if(VFSUtil.IsSubpath(path_lower, folder, false)) { IgnoreSubpath.Add(_path); IgnoreSubpathLower.Add(path_lower); break; } } } //忽略后缀名 foreach(var ext in option.IngnoreExtName) { IgnoreExtensionLower.Add(ext.StartsWith(".") ? ext.ToLower() : "." + ext.ToLower()); } //特殊打包规则 foreach(var rule in option.FolderSpecialBuildRules) { bool flag = true; if (rule.DevType == FolderBuildDevelopType.normal && rule.BuildType == FolderBuildType.normal) flag = false;//这是条没必要的规则 if (rule.FolderPath.IsNullOrEmpty() || rule.FolderPath.IsNullOrWhiteSpace()) flag = false; string _folder_path = (rule.FolderPath.EndsWith("/")) ? rule.FolderPath : rule.FolderPath + "/"; string _folder_lower = _folder_path.ToLower(); if (!IsSubfolderOfFolderList(_folder_path)) flag = false; if (flag) { var _rule = rule; _rule.FolderPath = _folder_path; var lower_rule = rule; lower_rule.FolderPath = _folder_lower; SpecialFolderBuildRules.Add(_rule); SpecialFolderBuildRulesLower.Add(lower_rule); } } } /// /// 传入一个Asset的Path,检查是否在当前Group的规则内 /// /// /// public bool IsAssetPathMatch(string assetPath) { string path_lower = assetPath.ToLower(); //后缀名过滤 foreach(var ext in IgnoreExtensionLower) { if (path_lower.EndsWith(ext)) { return false; } } bool whitelist = false; //在Asset白名单里? if (AssetPathsLower.Contains(path_lower)) whitelist = true; //在folder里? if (!whitelist) { foreach(var path in FolderPathsLower) { if (_IsAssetPathMatchFolder(ref path_lower, path)) { whitelist = true; break; } } } if (!whitelist) return false; //检查是否在Group的黑名单里 foreach(var ignorePath in IgnoreSubpathLower) { if(_IsAssetPathMatchFolder(ref path_lower, ignorePath)) { //命中忽略名单 return false; } } return true; } //给定的资源path是否包含在Folder中 private bool _IsAssetPathMatchFolder(ref string assetPath, string folderPath) { if (assetPath.Length < folderPath.Length) return false; return assetPath.StartsWith(folderPath); } /// /// 【返回值不含AssetBundle后缀!】传入一个 Asset的Path,根据“特殊文件打包规则”推测它的AssetBundle名 /// /// /// public string GetAssetBundleNameOfAsset(string assetPath,out FolderBuildType buildType, out FolderBuildDevelopType devType) { string assetPath_lower = assetPath.ToLower(); //是否命中特殊目录 foreach(var rule in SpecialFolderBuildRulesLower) { if(_IsAssetPathMatchFolder(ref assetPath_lower, rule.FolderPath)) { buildType = rule.BuildType; devType = rule.DevType; switch (rule.BuildType) { case FolderBuildType.normal: //return assetPath_lower; return this.ObfuscateDirectoryStructure ? this.GetObfuscatedAssetBundleName(assetPath_lower) : assetPath_lower; case FolderBuildType.sub_dir: string subs_path = assetPath_lower.Substring(rule.FolderPath.Length, assetPath_lower.Length - rule.FolderPath.Length); int sub_index = subs_path.IndexOf('/'); if(sub_index == -1) { //路径里没有子目录。 return this.ObfuscateDirectoryStructure ? this.GetObfuscatedAssetBundleName(assetPath_lower) : assetPath_lower; //return assetPath_lower; } else { //有子目录,取到子目录那一层 //return rule.FolderPath + subs_path.Substring(0, sub_index); return this.ObfuscateDirectoryStructure ? this.GetObfuscatedAssetBundleName(rule.FolderPath + subs_path.Substring(0, sub_index)) : (rule.FolderPath + subs_path.Substring(0, sub_index)); } case FolderBuildType.whole: //return rule.FolderPath; return this.ObfuscateDirectoryStructure ? this.GetObfuscatedAssetBundleName(RemoveLastSlashIfExist(rule.FolderPath)) : RemoveLastSlashIfExist(rule.FolderPath); } } } //没有命中 buildType = FolderBuildType.normal; devType = FolderBuildDevelopType.normal; return this.ObfuscateDirectoryStructure ? this.GetObfuscatedAssetBundleName(assetPath_lower) : assetPath_lower; } /// /// 【返回值不含AssetBundle后缀!】传入一个 Asset的Path,根据“特殊文件打包规则”推测它的AssetBundle名 /// /// /// public string GetAssetBundleNameOfAsset(string assetPath) { return this.GetAssetBundleNameOfAsset(assetPath, out _, out _); } /// /// 检查组内的文件夹冲突,并将存在冲突的内容返回,如果没有冲突则返回值的Count = 0 /// /// public List CheckFolderConflict() { List result = new List(); for (int i = 0; i < FolderPaths.Count; i++) { if (i > 0) { for (int j = 0; j < i; j++) { if (VFSUtil.IsSameOrSubPath(FolderPaths[i], FolderPaths[j], true)) { if (!result.Contains(FolderPaths[i])) { result.Add(FolderPaths[i]); } } } } } return result; } /// /// 检查组内的文件夹是否冲突,如果冲突,返回true,冲突的文件夹路径会放在out参数里 /// /// 简化检测,如果有发现冲突直接返回false结果,而不遍历所有数据进行判断 /// /// public bool CheckFolderConflict(out List paths, bool simplify = false) { List result = new List(); for (int i = 0; i < FolderPaths.Count; i++) { if (i > 0) { for (int j = 0; j < i; j++) { if (VFSUtil.IsSameOrSubPath(FolderPaths[i], FolderPaths[j], true)) { if (!result.Contains(FolderPaths[i])) { result.Add(FolderPaths[i]); } if (simplify) { paths = result; return true; } } } } } paths = result; return (result.Count > 0); } /// /// 检查给定的文件夹路径是否与组内的文件夹配置冲突(相同或者互为子路径),如果冲突,返回true /// /// /// public bool CheckFolderConflict(string folderPath) { foreach(var path in FolderPaths) { if (VFSUtil.IsSameOrSubPath(folderPath, path, true)) { return true; } } return false; } /// /// AssetBundle是否在这个组里 /// /// /// public bool IsAssetBundleBelongGroup(string assetbundleName) { return FilesHash.IsPathExist(assetbundleName); } /// /// 获取Manifest的文件地址 /// /// /// public string GetManifestFilePath(string packages_root_path) { if (this.ExtensionGroup) return VFSUtil.GetExtensionGroups_AssetBundleManifests_FilePath(packages_root_path, this.GroupName); else return Path.Combine(packages_root_path, VFSConst.VFS_FOLDER_DATA, VFSConst.MainPackage_AssetBundleManifests_Folder, this.GroupName.GetMD5(true, true) + ".json"); } public string GetAssetBundleHashsFilePath(string packages_root_path) { if (this.ExtensionGroup) return Path.Combine(packages_root_path, VFSConst.VFS_FOLDER_EXTENSION, this.GroupName, VFSConst.AssetBundleFilesHash_FileName); else return Path.Combine(packages_root_path, VFSConst.VFS_FOLDER_DATA, VFSConst.MainPackage_AssetBundle_Hash_Files_Folder, this.GroupName.GetMD5(true, true) + ".json"); } /// /// 在给定的目录中获取AssetBundle的路径 /// /// /// /// public string GetAssetBundlePath(string packages_root_path,string assetbundleName) { if (this.ExtensionGroup) return VFSUtil.GetAssetBundlePathFromPackages(this.ExtensionGroup, packages_root_path, assetbundleName,this.GroupName); else return VFSUtil.GetAssetBundlePathFromPackages(this.ExtensionGroup, packages_root_path, assetbundleName); } public string GetBuildInfoPath(string packages_root_path) { if (this.ExtensionGroup) return VFSUtil.GetExtensionGroup_BuildInfo_Path(packages_root_path, this.GroupName); else return VFSUtil.GetMainPackage_BuildInfo_Path(packages_root_path); } /// /// 是否是FolderPathsLower的子目录 /// /// private bool IsSubfolderOfFolderList(string path) { string path_lower = path.ToLower(); foreach(var folder in FolderPathsLower) { if (path_lower.StartsWith(folder)) return true; } return false; } private string GetObfuscatedAssetBundleName(string sourceAssetbundleName) { string md5_32 = sourceAssetbundleName.GetMD5(true, false); return md5_32.Substring(0, 2) + "/" + md5_32.Substring(8,16); } /// /// 如果给定的字符串最后一位是斜杠的话,就去掉 /// /// private string RemoveLastSlashIfExist(string str) { if (str.EndsWith("/") || str.EndsWith("\\")) return str.Substring(0, str.Length - 1); else return str; } } }