protected override void ProcessRecord() { using (RepositoryParameter container = UseOrDiscoverRepository(Repository)) { String[] paths = ArrayUtil.Combine(Path, LiteralPath); if (paths != null) { foreach (String path in paths) { FileStatus state = container.Repository.RetrieveStatus(path); WriteObject(new GitFileSystemStatusEntry(container.Repository.Info.WorkingDirectory, SessionState.Path.CurrentFileSystemLocation.Path, path, state)); } } else { RepositoryStatus status = container.Repository.RetrieveStatus(); foreach (StatusEntry entry in status) { WriteObject(new GitFileSystemStatusEntry(container.Repository.Info.WorkingDirectory, SessionState.Path.CurrentFileSystemLocation.Path, entry.FilePath, entry.State)); } } } }
protected override void ProcessRecord() { String[] addPaths = ArrayUtil.Combine(Path, LiteralPath); if (addPaths == null && !All && !Update) { throw new ArgumentException("You must specify paths to add using -Path or -LiteralPath, or use the -All or -Update parameter"); } else if (All && Update) { throw new ArgumentException("You cannot specify both the -All and -Update parameters"); } using (RepositoryParameter container = UseOrDiscoverRepository(Repository)) { if (addPaths != null) { foreach (String path in addPaths) { String repoRelativePath = FileSystemUtil.MakeRelative(path, container.Repository.Info.WorkingDirectory); WriteVerbose(String.Format("Adding {0}", repoRelativePath)); container.Repository.Stage(path); WriteObject(new GitFileSystemStatusEntry(container.Repository.Info.WorkingDirectory, SessionState.Path.CurrentFileSystemLocation.Path, path, container.Repository.RetrieveStatus(path))); } } else { foreach (StatusEntry statusEntry in container.Repository.RetrieveStatus()) { if ( (statusEntry.State == FileStatus.Untracked && All) || (statusEntry.State == FileStatus.Missing) || (statusEntry.State == FileStatus.Modified) ) { String repoRelativePath = FileSystemUtil.MakeRelative(statusEntry.FilePath, container.Repository.Info.WorkingDirectory); WriteVerbose(String.Format("Adding {0}", statusEntry.FilePath)); container.Repository.Stage(statusEntry.FilePath); WriteObject(new GitFileSystemStatusEntry(container.Repository.Info.WorkingDirectory, SessionState.Path.CurrentFileSystemLocation.Path, statusEntry.FilePath, container.Repository.RetrieveStatus(statusEntry.FilePath))); } } } } }
protected override void ProcessRecord() { String[] removePaths = ArrayUtil.Combine(pathParameters.Path, pathParameters.LiteralPath); if (removePaths == null) { throw new ArgumentException("You must specify paths to add using -Path or -LiteralPath"); } using (RepositoryParameter container = UseOrDiscoverRepository(Repository)) { /* Sanity check input, ensure it's in the index */ foreach (String path in removePaths) { FileStatus state = container.Repository.Index.RetrieveStatus(path); if (state == FileStatus.Nonexistent || state == FileStatus.Untracked) { throw new ArgumentException(String.Format("The item {0} is not tracked", path)); } } foreach (String path in removePaths) { if (!Cached) { File.Delete(path); } String repoRelativePath = FileSystemUtil.MakeRelative(path, container.Repository.Info.WorkingDirectory); WriteVerbose(String.Format("Removing {0}", repoRelativePath)); container.Repository.Index.Unstage(path); WriteObject(new GitFileSystemStatusEntry(container.Repository.Info.WorkingDirectory, SessionState.Path.CurrentFileSystemLocation.Path, path, container.Repository.Index.RetrieveStatus(path))); } } }
private void OnGUI() { GUILayout.BeginVertical(); GUILayout.BeginHorizontal(style_head); GUILayout.Label("Profile:", GUILayout.Width(55)); if (xprofiles == null || (select_xprofile - 1) > xprofiles.Length) { refreshXprofilesCacheData(); } select_xprofile = EditorGUILayout.Popup(select_xprofile, xprofiles); GUILayout.EndHorizontal(); //数据 string cur_xprofile_name = xprofiles[select_xprofile]; if (mCurProfileRecord == null || xprofiles[select_xprofile] != mCurProfileRecord.ProfileName) { mCurProfileRecord = VFSManagerEditor.GetProfileRecord(xprofiles[select_xprofile]); addConfiguredValueToCache(); } // 工具栏 GUILayout.Space(5); mToolbar_Index = GUILayout.Toolbar(mToolbar_Index, toolbar_str); if (mToolbar_Index == 0) { #region Group列表 GUILayout.BeginVertical(style_body); v2_body_groups_scrollview = EditorGUILayout.BeginScrollView(v2_body_groups_scrollview); //表头 EditorGUILayout.BeginHorizontal(); GUILayout.Label("Group", GUILayout.Width(165)); GUILayout.Label("|", GUILayout.Width(10)); GUILayout.Label(IsChinese ? "资源存储位置" : "Assets Storage Location", GUILayout.Width(150)); GUILayout.Label("|", GUILayout.Width(10)); GUILayout.Label("Disable", GUILayout.Width(50)); EditorGUILayout.EndHorizontal(); if (groups == null) { groups = VFSManagerEditor.GetGroups(); //groupNames = VFSManagerEditor.GetGroupNames(); } foreach (var group in groups) { GUILayout.BeginHorizontal(); //GroupName GUILayout.Label(group.GroupName, style_txt_group_item, GUILayout.Width(170)); GUILayout.Space(5); //资源存储位置 GroupHandleMode handleMode = group.HandleMode; if (handleMode == GroupHandleMode.LocalAndUpdatable || handleMode == GroupHandleMode.LocalOrRemote) { //可以主动设置资源位置 if (!assetLocation_cache.ContainsKey(cur_xprofile_name)) { assetLocation_cache.Add(cur_xprofile_name, new Dictionary <string, ProfileRecord.E_GroupAssetsLocation>()); } if (!assetLocation_cache[cur_xprofile_name].ContainsKey(group.GroupName)) { assetLocation_cache[cur_xprofile_name].Add(group.GroupName, ProfileRecord.E_GroupAssetsLocation.Local); } assetLocation_cache[cur_xprofile_name][group.GroupName] = (ProfileRecord.E_GroupAssetsLocation)EditorGUILayout.EnumPopup(assetLocation_cache[cur_xprofile_name][group.GroupName], GUILayout.Width(150)); GUILayout.Space(2); } else { //写死资源位置 if (handleMode == GroupHandleMode.LocalOnly) { setAssetLocationCacheValue(cur_xprofile_name, group.GroupName, ProfileRecord.E_GroupAssetsLocation.Local); GUILayout.Label($"[{ProfileRecord.E_GroupAssetsLocation.Local.ToString()}]", GUILayout.Width(150)); } else if (handleMode == GroupHandleMode.RemoteOnly) { setAssetLocationCacheValue(cur_xprofile_name, group.GroupName, ProfileRecord.E_GroupAssetsLocation.Remote); GUILayout.Label($"[{ProfileRecord.E_GroupAssetsLocation.Remote.ToString()}]", GUILayout.Width(150)); } } GUILayout.Space(10); //Disable if (group.ExtensionGroup) { //可以主动设置group disable if (!disable_expansionGroup_cache.ContainsKey(cur_xprofile_name)) { disable_expansionGroup_cache.Add(cur_xprofile_name, new Dictionary <string, bool>()); } if (!disable_expansionGroup_cache[cur_xprofile_name].ContainsKey(group.GroupName)) { disable_expansionGroup_cache[cur_xprofile_name].Add(group.GroupName, false); } GUILayout.Space(10); disable_expansionGroup_cache[cur_xprofile_name][group.GroupName] = EditorGUILayout.Toggle(disable_expansionGroup_cache[cur_xprofile_name][group.GroupName], GUILayout.Width(50)); } else { GUILayout.Label("[-]", style_label_center, GUILayout.Width(36)); setGroupDisableCacheValue(cur_xprofile_name, group.GroupName, false); } GUILayout.EndHorizontal(); } EditorGUILayout.EndScrollView(); GUILayout.EndVertical(); #endregion } else if (mToolbar_Index == 1) { #region WebVFS URLs if (mWebVFS_Net_Config == null) { mWebVFS_Net_Config = XConfig.CreateConfigIfNotExists <WebVFSNetworkConfig>(TinaX.VFSKit.Const.VFSConst.Config_WebVFS_URLs); } if (mWebVFS_Net_Config_serialized == null) { mWebVFS_Net_Config_serialized = new SerializedObject(mWebVFS_Net_Config); } if (mCur_WebVFS_profileNmae == null || mCur_WebVFS_profileNmae != cur_xprofile_name) { if (mWebVFS_Net_Config.Configs == null || !mWebVFS_Net_Config.Configs.Any(item => item.ProfileName == cur_xprofile_name)) { ArrayUtil.Combine(ref mWebVFS_Net_Config.Configs, new WebVFSNetworkConfig.NetworkConfig[] { new WebVFSNetworkConfig.NetworkConfig() { ProfileName = cur_xprofile_name } }); mWebVFS_Net_Config_serialized.UpdateIfRequiredOrScript(); } for (int i = 0; i < mWebVFS_Net_Config.Configs.Length; i++) { if (mWebVFS_Net_Config.Configs[i].ProfileName == cur_xprofile_name) { mCur_ProfileIndex_InWebVFS = i; } } SerializedProperty cur_config = mWebVFS_Net_Config_serialized.FindProperty("Configs").GetArrayElementAtIndex(mCur_ProfileIndex_InWebVFS); SerializedProperty cur_urls = cur_config.FindPropertyRelative("Urls"); reorderableList_urls = new ReorderableList( mWebVFS_Net_Config_serialized, cur_urls, true, true, true, true); reorderableList_urls.drawElementCallback = (rect, index, selected, focused) => { SerializedProperty itemData = reorderableList_urls.serializedProperty.GetArrayElementAtIndex(index); SerializedProperty mode = itemData.FindPropertyRelative("NetworkMode"); SerializedProperty url = itemData.FindPropertyRelative("BaseUrl"); SerializedProperty helloUrl = itemData.FindPropertyRelative("HelloUrl"); rect.y += 2; rect.height = EditorGUIUtility.singleLineHeight * 1; var rect_mode = rect; EditorGUI.PropertyField(rect_mode, mode, new GUIContent(IsChinese ? "网络模式" : "Network Mode")); //-------------------------------------------------------------------------------------------- var rect_url_label = rect; rect_url_label.y += EditorGUIUtility.singleLineHeight + 2; rect_url_label.width = 58; GUI.Label(rect_url_label, IsChinese ? "基础Url" : "Base Url"); var rect_url = rect; rect_url.y += EditorGUIUtility.singleLineHeight + 2; rect_url.width -= 62; rect_url.x += 62; EditorGUI.PropertyField(rect_url, url, GUIContent.none); //--------------------------------------------------------------------------------------------------- var rect_hellourl_label = rect; rect_hellourl_label.y += EditorGUIUtility.singleLineHeight * 2 + 4; rect_hellourl_label.width = 58; GUI.Label(rect_hellourl_label, IsChinese ? "HelloUrl" : "Hello Url"); var rect_helloUrl = rect; rect_helloUrl.y += EditorGUIUtility.singleLineHeight * 2 + 4; rect_helloUrl.width -= 62; rect_helloUrl.x += 62; EditorGUI.PropertyField(rect_helloUrl, helloUrl, GUIContent.none); }; reorderableList_urls.elementHeightCallback = (index) => { return(EditorGUIUtility.singleLineHeight * 3 + 10); }; reorderableList_urls.drawHeaderCallback = (rect) => { GUI.Label(rect, IsChinese?"Web Urls":"Web Urls"); }; mCur_WebVFS_profileNmae = cur_xprofile_name; } GUILayout.BeginVertical(style_body); v2_body_webvfs_scrollview = EditorGUILayout.BeginScrollView(v2_body_webvfs_scrollview); reorderableList_urls.DoLayoutList(); //EditorGUILayout.PropertyField(cur_urls); EditorGUILayout.EndScrollView(); GUILayout.EndVertical(); #endregion } #region 保存和重置值的两个按钮 GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); //包存cache的值 if (GUILayout.Button(IsChinese ? "保存设置" : "Save", GUILayout.Width(100))) { #region Group Profile foreach (var item in assetLocation_cache) { //item.key: profile, var profile = VFSManagerEditor.GetProfileRecord(item.Key); //因为获取到的是class,所以直接修改它就行了 //item.value 是一个字典,key = groupName, value = value foreach (var item2 in item.Value) { //接下来要在结构体上操作了,所以不能拉过来操作了,只能找到下标之后直接去修改 if (!profile.GroupProfileRecords.Any(gpr => gpr.GroupName == item2.Key)) { ArrayUtil.Combine(ref profile.GroupProfileRecords, new ProfileRecord.S_GroupProfileRecord[] { new ProfileRecord.S_GroupProfileRecord() { GroupName = item2.Key, } }); } for (var i = 0; i < profile.GroupProfileRecords.Length; i++) { if (profile.GroupProfileRecords[i].GroupName == item2.Key) { profile.GroupProfileRecords[i].Location = item2.Value; break; } } } VFSManagerEditor.AddProfileIfNotExists(profile); } foreach (var item in disable_expansionGroup_cache) { //item.key: profile, var profile = VFSManagerEditor.GetProfileRecord(item.Key); //因为获取到的是class,所以直接修改它就行了 //item.value 是一个字典,key = groupName, value = value foreach (var item2 in item.Value) { //接下来要在结构体上操作了,所以不能拉过来操作了,只能找到下标之后直接去修改 if (!profile.GroupProfileRecords.Any(gpr => gpr.GroupName == item2.Key)) { ArrayUtil.Combine(ref profile.GroupProfileRecords, new ProfileRecord.S_GroupProfileRecord[] { new ProfileRecord.S_GroupProfileRecord() { GroupName = item2.Key, } }); } for (var i = 0; i < profile.GroupProfileRecords.Length; i++) { if (profile.GroupProfileRecords[i].GroupName == item2.Key) { profile.GroupProfileRecords[i].DisableGroup = item2.Value; break; } } } VFSManagerEditor.AddProfileIfNotExists(profile); } //foreach (var item in developMode_cache) //{ // //item.key: profile, // var profile = VFSManagerEditor.GetProfileRecord(item.Key); //因为获取到的是class,所以直接修改它就行了 // //item.value 是 是否为开发模式的value // profile.DevelopMode = item.Value; // VFSManagerEditor.AddProfileIfNotExists(profile); //} //通知VFSManager保存到disk VFSManagerEditor.SaveProfileRecord(); #endregion #region WebVFS URL mWebVFS_Net_Config_serialized?.ApplyModifiedPropertiesWithoutUndo(); //删掉可能多余的内容 string[] profiles = XCoreEditor.GetXProfileNames(); if (mWebVFS_Net_Config.Configs != null && mWebVFS_Net_Config.Configs.Length > 0) { List <WebVFSNetworkConfig.NetworkConfig> list_temp = new List <WebVFSNetworkConfig.NetworkConfig>(mWebVFS_Net_Config.Configs); for (var i = list_temp.Count - 1; i >= 0; i--) { if (!profiles.Contains(list_temp[i].ProfileName)) { list_temp.RemoveAt(i); } } if (list_temp.Count != mWebVFS_Net_Config.Configs.Length) { mWebVFS_Net_Config.Configs = list_temp.ToArray(); } } AssetDatabase.SaveAssets(); #endregion } //重置cache的已记录值 if (GUILayout.Button(IsChinese ? "重置设置" : "Reset modify", GUILayout.Width(100))) { assetLocation_cache?.Clear(); disable_expansionGroup_cache?.Clear(); addConfiguredValueToCache(); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); #endregion GUILayout.Space(5); GUILayout.EndVertical(); }
public void RefreshAssetBundleSign() { /* * Unity目前提供的API对应的打包方法是: * 1. 给全局的文件加上assetbundle标记 * 2. 整体打包 */ //获取到所有组的文件白名单目录 refreshProfileInfos(); if (EnableTipsGUI) { EditorUtility.DisplayProgressBar("VFS Builder", "Handle AssetBundle signs ...", 0f); } var _whiteLists_folder = VFSManagerEditor.GetAllFolderPaths(); string ab_extension = Config.AssetBundleFileExtension; //没有点开头的后缀名 if (ab_extension.StartsWith(".")) { ab_extension = ab_extension.Substring(1, ab_extension.Length - 1); } ab_extension = ab_extension.ToLower(); List <string> _whiteLists_folder_list_temp = new List <string>(); foreach (var path in _whiteLists_folder) { if (!IsAssetFolderExists(path)) { Debug.LogError("[TinaX][VFS]Folder path in vfs config is not exists: \"" + path + "\""); } else { if (path.EndsWith("/")) { _whiteLists_folder_list_temp.Add(path.Substring(0, path.Length - 1)); } else { _whiteLists_folder_list_temp.Add(path); } } } var _whitelist_folder_temp = _whiteLists_folder_list_temp.ToArray(); string[] guids = AssetDatabase.FindAssets("", _whitelist_folder_temp); string[] asset_paths = VFSManagerEditor.GetAllWithelistAssetsPaths(); List <string> asset_guids = new List <string>(); foreach (var item in asset_paths) { var myguid = AssetDatabase.AssetPathToGUID(item); if (!myguid.IsNullOrEmpty()) { asset_guids.Add(myguid); } } if (asset_guids.Count > 0) { ArrayUtil.Combine(ref guids, asset_guids.ToArray()); } ArrayUtil.RemoveDuplicationElements(ref guids); asset_paths = null; asset_guids = null; asset_hash_book.Clear(); dict_asset_hash_book.Clear(); int counter = 0; int counter_t = 0; int totalLength = guids.Length; foreach (var guid in guids) { string cur_asset_path = AssetDatabase.GUIDToAssetPath(guid); if (VFSManagerEditor.QueryAsset(cur_asset_path, Config, out AssetsStatusQueryResult result, true)) { //查询到了信息,但是并不是所有情况都需要设置assetbundle记录, bool sign_flag = true; if (result.DevType == FolderBuildDevelopType.editor_only) { sign_flag = false; //该资源仅在编辑器下被加载,不参与打包。 } if (result.DevType == FolderBuildDevelopType.develop_mode_only) { if (!mDevelopMode) { sign_flag = false; //资源应该仅在develop模式下使用,但是当前Profile的设置并不是develop } } if (result.ExtensionGroup) { if (curProfile.IsDisabledGroup(result.GroupName)) { sign_flag = false; } } if (sign_flag) { var importer = AssetImporter.GetAtPath(cur_asset_path); if (!XPath.IsFolder(cur_asset_path) && !result.AssetBundleFileNameWithoutExtension.IsNullOrEmpty()) { string assetBundleName_without_extension = result.AssetBundleFileNameWithoutExtension; void InvokePipeline(BuilderPipelineContext ctx) { if (ctx != null && ctx.Handler != null) { bool b = ctx.Handler.BeforeSetAssetBundleSign(ref assetBundleName_without_extension, ref result); if (b && ctx.Next != null) { InvokePipeline(ctx.Next); } } } if (mUsePipeline) { InvokePipeline(mPipeline.First); } //正式设置AssetBundle importer.SetAssetBundleNameAndVariant(assetBundleName_without_extension, ab_extension); //记录Asset原始hash(用于补丁版本检索) if (result.ExtensionGroup) { //记录到字典里 if (!dict_asset_hash_book.ContainsKey(result.GroupName)) { dict_asset_hash_book.Add(result.GroupName, new List <FilesHashBook.FileHash>()); } dict_asset_hash_book[result.GroupName].Add(new FilesHashBook.FileHash() { p = cur_asset_path, h = XFile.GetMD5(cur_asset_path, true) }); } else { asset_hash_book.Add(new FilesHashBook.FileHash() { p = cur_asset_path, h = XFile.GetMD5(cur_asset_path, true) }); } //记录AssetBundle名 string final_ab_name = assetBundleName_without_extension + "." + ab_extension; if (!mDict_Group_AssetBundleNames.ContainsKey(result.GroupName)) { mDict_Group_AssetBundleNames.Add(result.GroupName, new List <string>()); } if (!mDict_Group_AssetBundleNames[result.GroupName].Contains(final_ab_name)) { mDict_Group_AssetBundleNames[result.GroupName].Add(final_ab_name); } } } } if (EnableTipsGUI) { counter++; if (totalLength < 100) { EditorUtility.DisplayProgressBar("VFS Builder", $"Handle AssetBundle signs : {counter} / {totalLength}", counter / totalLength); } else { counter_t++; if (counter_t > 50) { counter_t = 0; EditorUtility.DisplayProgressBar($"VFS Builder", "Handle AssetBundle signs : {counter} / {totalLength}", counter / totalLength); } } } } AssetDatabase.SaveAssets(); if (EnableTipsGUI) { EditorUtility.ClearProgressBar(); } }
/// <summary> /// 配置规范化 /// </summary> /// <param name="config"></param> public static void NormalizationConfig(IVFSConfig config) { if (config == null) { return; } //全局配置 //后缀名配置 补全缺失的“框架内部定义的必须存在的”配置项 #region 全局 后缀名 内部配置项 if (config.PGlobalVFS_Ignore_ExtName == null) { config.PGlobalVFS_Ignore_ExtName = new string[0]; } List <string> ext_list = new List <string>(); foreach (var item in TinaX.VFSKitInternal.InternalVFSConfig.GlobalIgnoreExtName) { if (!config.PGlobalVFS_Ignore_ExtName.Contains(item)) { ext_list.Add(item); } } if (ext_list.Count > 0) { config.PGlobalVFS_Ignore_ExtName = ArrayUtil.Combine(config.PGlobalVFS_Ignore_ExtName, ext_list.ToArray()); } #endregion //后缀名配置必须要以点开头,并小写 #region 全局 后缀名 格式规范 if (config.PGlobalVFS_Ignore_ExtName != null && config.PGlobalVFS_Ignore_ExtName.Length > 0) { for (var i = 0; i < config.PGlobalVFS_Ignore_ExtName.Length; i++) { if (!config.PGlobalVFS_Ignore_ExtName[i].StartsWith(".")) { config.PGlobalVFS_Ignore_ExtName[i] = "." + config.PGlobalVFS_Ignore_ExtName[i]; } config.PGlobalVFS_Ignore_ExtName[i] = config.PGlobalVFS_Ignore_ExtName[i].ToLower(); } } #endregion //后缀名配置 重复项 #region 全局 后缀名 重复项 if (config.PGlobalVFS_Ignore_ExtName != null && config.PGlobalVFS_Ignore_ExtName.Length > 0) { List <string> list = new List <string>(config.PGlobalVFS_Ignore_ExtName).Distinct().ToList(); if (list.Count != config.PGlobalVFS_Ignore_ExtName.Length) { config.PGlobalVFS_Ignore_ExtName = list.ToArray(); } } #endregion #region 全局 忽略 路径 item //补全内部设定 if (config.PGlobalVFS_Ignore_Path_Item == null) { config.PGlobalVFS_Ignore_Path_Item = new string[0]; } List <string> tmp_path_item_list = new List <string>(config.PGlobalVFS_Ignore_Path_Item); foreach (var item in TinaX.VFSKitInternal.InternalVFSConfig.GlobalIgnorePathItem) { if (!tmp_path_item_list.Contains(item)) { tmp_path_item_list.Add(item); } } List <string> temp_2 = new List <string>(); //查重和空白项目 for (int i = tmp_path_item_list.Count - 1; i >= 0; i--) { if (!tmp_path_item_list[i].IsNullOrEmpty()) { if (!temp_2.Any(item => item.ToLower() == tmp_path_item_list[i].ToLower())) { temp_2.Add(tmp_path_item_list[i]); } } } config.PGlobalVFS_Ignore_Path_Item = temp_2.ToArray(); #endregion #region 全局 Assetbundle细节 if (config.PAssetBundleFileExtension.IsNullOrEmpty() || config.PAssetBundleFileExtension.IsNullOrWhiteSpace()) { config.PAssetBundleFileExtension = InternalVFSConfig.default_AssetBundle_ExtName; } if (!config.PAssetBundleFileExtension.StartsWith(".")) { config.PAssetBundleFileExtension = "." + config.PAssetBundleFileExtension; } config.PAssetBundleFileExtension = config.PAssetBundleFileExtension.ToLower(); #endregion #region WebVFS if (config.PDefaultWebVFSBaseUrl.IsNullOrEmpty() || config.PDefaultWebVFSBaseUrl.IsNullOrWhiteSpace()) { config.PDefaultWebVFSBaseUrl = VFSKit.VFSKit.DefaultDownloadWebAssetUrl; } if (!config.PDefaultWebVFSBaseUrl.EndsWith("/")) { config.PDefaultWebVFSBaseUrl += "/"; } #endregion #region 至少包含一个Group if (config.PGroups == null || config.PGroups.Length == 0) { config.PGroups = new VFSGroupOption[] { VFSGroupOption.New() }; } #endregion }