diff --git a/Penumbra/Configuration.cs b/Penumbra/Configuration.cs index 7a7d2524..9b902f03 100644 --- a/Penumbra/Configuration.cs +++ b/Penumbra/Configuration.cs @@ -25,6 +25,7 @@ namespace Penumbra public int WaitFrames { get; set; } = 30; public string ModDirectory { get; set; } = string.Empty; + public string TempDirectory { get; set; } = string.Empty; public string CurrentCollection { get; set; } = "Default"; public string DefaultCollection { get; set; } = "Default"; diff --git a/Penumbra/Meta/MetaManager.cs b/Penumbra/Meta/MetaManager.cs index 1ca8fa39..423438b3 100644 --- a/Penumbra/Meta/MetaManager.cs +++ b/Penumbra/Meta/MetaManager.cs @@ -116,15 +116,12 @@ namespace Penumbra.Meta private void ClearDirectory() => ClearDirectory( _dir ); - public static void ClearBaseDirectory( DirectoryInfo modDir ) - => ClearDirectory( new DirectoryInfo( Path.Combine( modDir.FullName, TmpDirectory ) ) ); - - public MetaManager( string name, Dictionary< GamePath, FileInfo > resolvedFiles, DirectoryInfo modDir ) + public MetaManager( string name, Dictionary< GamePath, FileInfo > resolvedFiles, DirectoryInfo tempDir ) { _resolvedFiles = resolvedFiles; _default = Service< MetaDefaults >.Get(); _resourceManagement = Service< GameResourceManagement >.Get(); - _dir = new DirectoryInfo( Path.Combine( modDir.FullName, TmpDirectory, name.ReplaceBadXivSymbols() ) ); + _dir = new DirectoryInfo( Path.Combine( tempDir.FullName, name.ReplaceBadXivSymbols() ) ); ClearDirectory(); } diff --git a/Penumbra/Mods/CollectionManager.cs b/Penumbra/Mods/CollectionManager.cs index eac6e7e7..4baf9b1e 100644 --- a/Penumbra/Mods/CollectionManager.cs +++ b/Penumbra/Mods/CollectionManager.cs @@ -36,9 +36,15 @@ namespace Penumbra.Mods public void RecreateCaches() { + if( !_manager.TempWritable ) + { + PluginLog.Error( "No temporary directory available." ); + return; + } + foreach( var collection in Collections.Values.Where( c => c.Cache != null ) ) { - collection.CreateCache( _manager.BasePath, _manager.StructuredMods.AllMods(_manager.Config.SortFoldersFirst) ); + collection.CreateCache( _manager.TempPath, _manager.StructuredMods.AllMods( _manager.Config.SortFoldersFirst ) ); } } @@ -137,9 +143,15 @@ namespace Penumbra.Mods private void AddCache( ModCollection collection ) { + if( !_manager.TempWritable ) + { + PluginLog.Error( "No tmp directory available." ); + return; + } + if( collection.Cache == null && collection.Name != string.Empty ) { - collection.CreateCache( _manager.BasePath, _manager.StructuredMods.AllMods(_manager.Config.SortFoldersFirst) ); + collection.CreateCache( _manager.TempPath, _manager.StructuredMods.AllMods( _manager.Config.SortFoldersFirst ) ); } } diff --git a/Penumbra/Mods/ModCollectionCache.cs b/Penumbra/Mods/ModCollectionCache.cs index e733644e..c50e4dd3 100644 --- a/Penumbra/Mods/ModCollectionCache.cs +++ b/Penumbra/Mods/ModCollectionCache.cs @@ -19,8 +19,8 @@ namespace Penumbra.Mods public readonly Dictionary< GamePath, GamePath > SwappedFiles = new(); public readonly MetaManager MetaManipulations; - public ModCollectionCache( string collectionName, DirectoryInfo modDir ) - => MetaManipulations = new MetaManager( collectionName, ResolvedFiles, modDir ); + public ModCollectionCache( string collectionName, DirectoryInfo tempDir ) + => MetaManipulations = new MetaManager( collectionName, ResolvedFiles, tempDir ); private void AddFiles( Dictionary< GamePath, Mod.Mod > registeredFiles, Mod.Mod mod ) { diff --git a/Penumbra/Mods/ModManager.cs b/Penumbra/Mods/ModManager.cs index ecd77002..2b8b3be2 100644 --- a/Penumbra/Mods/ModManager.cs +++ b/Penumbra/Mods/ModManager.cs @@ -16,6 +16,7 @@ namespace Penumbra.Mods { private readonly Plugin _plugin; public DirectoryInfo BasePath { get; private set; } = null!; + public DirectoryInfo TempPath { get; private set; } = null!; public Dictionary< string, ModData > Mods { get; } = new(); public ModFolder StructuredMods { get; } = ModFileSystem.Root; @@ -23,39 +24,157 @@ namespace Penumbra.Mods public CollectionManager Collections { get; } public bool Valid { get; private set; } + public bool TempWritable { get; private set; } public Configuration Config => _plugin.Configuration; - private void SetBaseDirectory( string basePath ) + public void DiscoverMods( string newDir ) { - if( basePath.Any() ) + SetBaseDirectory( newDir, false ); + DiscoverMods(); + } + + private void ClearOldTmpDir() + { + TempPath.Refresh(); + if( TempWritable && TempPath.Exists ) { - BasePath = new DirectoryInfo( basePath ); - Valid = Path.IsPathRooted( basePath ); + try + { + TempPath.Delete( true ); + } + catch( Exception e ) + { + PluginLog.Error( $"Could not delete temporary directory {TempPath.FullName}:\n{e}" ); + } + } + } + + private static bool CheckTmpDir( string newPath, out DirectoryInfo tmpDir ) + { + tmpDir = new DirectoryInfo( Path.Combine( newPath, MetaManager.TmpDirectory ) ); + try + { + if( tmpDir.Exists ) + { + tmpDir.Delete( true ); + tmpDir.Refresh(); + } + + Directory.CreateDirectory( tmpDir.FullName ); + tmpDir.Refresh(); + return true; + } + catch( Exception e ) + { + PluginLog.Error( $"Could not create temporary directory {tmpDir.FullName}:\n{e}" ); + return false; + } + } + + private void SetBaseDirectory( string newPath, bool firstTime ) + { + if( !firstTime && string.Equals( newPath, Config.ModDirectory, StringComparison.InvariantCultureIgnoreCase ) ) + { + return; + } + + if( !newPath.Any() ) + { + Valid = false; + BasePath = new DirectoryInfo( "." ); } else { - BasePath = new DirectoryInfo( "." ); - Valid = false; + var newDir = new DirectoryInfo( newPath ); + if( !newDir.Exists ) + { + try + { + Directory.CreateDirectory( newDir.FullName ); + newDir.Refresh(); + } + catch( Exception e ) + { + PluginLog.Error( $"Could not create specified mod directory {newDir.FullName}:\n{e}" ); + } + } + + BasePath = newDir; + Valid = true; + if( Config.ModDirectory != BasePath.FullName ) + { + Config.ModDirectory = BasePath.FullName; + Config.Save(); + } + + if( !Config.TempDirectory.Any() ) + { + if( CheckTmpDir( BasePath.FullName, out var newTmpDir ) ) + { + if( !firstTime ) + { + ClearOldTmpDir(); + } + + TempPath = newTmpDir; + TempWritable = true; + } + else + { + TempWritable = false; + } + } } } + private void SetTempDirectory( string newPath, bool firstTime ) + { + if( !Valid || !firstTime && string.Equals( newPath, Config.TempDirectory, StringComparison.InvariantCultureIgnoreCase ) ) + { + return; + } + + if( !newPath.Any() && CheckTmpDir( BasePath.FullName, out var newTmpDir ) + || newPath.Any() && CheckTmpDir( newPath, out newTmpDir ) ) + { + if( !firstTime ) + { + ClearOldTmpDir(); + } + + TempPath = newTmpDir; + TempWritable = true; + var newName = newPath.Any() ? TempPath.Parent!.FullName : string.Empty; + if( Config.TempDirectory != newName ) + { + Config.TempDirectory = newName; + Config.Save(); + } + + if( !firstTime ) + { + Collections.RecreateCaches(); + } + } + else + { + TempWritable = false; + } + } + + public void SetTempDirectory( string newPath ) + => SetTempDirectory( newPath, false ); + public ModManager( Plugin plugin ) { _plugin = plugin; - SetBaseDirectory( plugin.Configuration.ModDirectory ); - MetaManager.ClearBaseDirectory( BasePath! ); - + SetBaseDirectory( Config.ModDirectory, true ); + SetTempDirectory( Config.TempDirectory, true ); Collections = new CollectionManager( plugin, this ); } - public void DiscoverMods( string basePath ) - { - SetBaseDirectory( basePath ); - DiscoverMods(); - } - private bool SetSortOrderPath( ModData mod, string path ) { mod.Move( path ); @@ -101,21 +220,11 @@ namespace Penumbra.Mods public void DiscoverMods() { Mods.Clear(); - if( Valid && !BasePath.Exists ) - { - PluginLog.Debug( "The mod directory {Directory} does not exist.", BasePath.FullName ); - try - { - Directory.CreateDirectory( BasePath.FullName ); - } - catch( Exception e ) - { - PluginLog.Error( $"The mod directory {BasePath.FullName} does not exist and could not be created:\n{e}" ); - Valid = false; - } - } + BasePath.Refresh(); - if( Valid ) + StructuredMods.SubFolders.Clear(); + StructuredMods.Mods.Clear(); + if( Valid && BasePath.Exists ) { foreach( var modFolder in BasePath.EnumerateDirectories() ) { diff --git a/Penumbra/Mods/ModManagerEditExtensions.cs b/Penumbra/Mods/ModManagerEditExtensions.cs index b1bc3435..06b8ebde 100644 --- a/Penumbra/Mods/ModManagerEditExtensions.cs +++ b/Penumbra/Mods/ModManagerEditExtensions.cs @@ -206,7 +206,7 @@ namespace Penumbra.Mods manager.Collections.SaveCollection( collection ); if( collection.Cache != null && settings.Enabled ) { - collection.CalculateEffectiveFileList( manager.BasePath, mod.Resources.MetaManipulations.Count > 0, + collection.CalculateEffectiveFileList( manager.TempPath, mod.Resources.MetaManipulations.Count > 0, collection == manager.Collections.ActiveCollection ); } } diff --git a/Penumbra/Plugin.cs b/Penumbra/Plugin.cs index 6deac9ae..5257681e 100644 --- a/Penumbra/Plugin.cs +++ b/Penumbra/Plugin.cs @@ -85,9 +85,9 @@ namespace Penumbra CreateWebServer(); } - if( Configuration.EnableActorWatch && Configuration.IsEnabled ) + if( !Configuration.EnableActorWatch || !Configuration.IsEnabled ) { - PlayerWatcher.Enable(); + PlayerWatcher.Disable(); } PlayerWatcher.ActorChanged += a => diff --git a/Penumbra/UI/MenuTabs/TabDebug.cs b/Penumbra/UI/MenuTabs/TabDebug.cs index 60b94e17..39a33236 100644 --- a/Penumbra/UI/MenuTabs/TabDebug.cs +++ b/Penumbra/UI/MenuTabs/TabDebug.cs @@ -144,6 +144,11 @@ namespace Penumbra.UI PrintValue( "Mod Manager BasePath IsRooted", Path.IsPathRooted( _plugin.Configuration.ModDirectory ).ToString() ); PrintValue( "Mod Manager BasePath Exists", Directory.Exists( manager.BasePath.FullName ).ToString() ); PrintValue( "Mod Manager Valid", manager.Valid.ToString() ); + PrintValue( "Mod Manager Temp Path", manager.TempPath.FullName ); + PrintValue( "Mod Manager Temp Path IsRooted", + ( !_plugin.Configuration.TempDirectory.Any() || Path.IsPathRooted( _plugin.Configuration.TempDirectory ) ).ToString() ); + PrintValue( "Mod Manager Temp Path Exists", Directory.Exists( manager.TempPath.FullName ).ToString() ); + PrintValue( "Mod Manager Temp Path IsWritable", manager.TempWritable.ToString() ); ImGui.EndTable(); } diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs index 48ba1c78..8c48b16d 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs @@ -435,7 +435,7 @@ namespace Penumbra.UI foreach( var collection in _modManager.Collections.Collections.Values .Where( c => c.Cache != null && c.Settings[ Mod!.Data.BasePath.Name ].Enabled ) ) { - collection.CalculateEffectiveFileList( _modManager.BasePath, false, + collection.CalculateEffectiveFileList( _modManager.TempPath, false, collection == _modManager.Collections.ActiveCollection ); } diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs index 35ccfb2e..191e2491 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs @@ -539,7 +539,7 @@ namespace Penumbra.UI var collection = _modManager.Collections.CurrentCollection; if( collection.Cache != null ) { - collection.CalculateEffectiveFileList( _modManager.BasePath, metaManips, + collection.CalculateEffectiveFileList( _modManager.TempPath, metaManips, collection == _modManager.Collections.ActiveCollection ); } @@ -563,6 +563,7 @@ namespace Penumbra.UI { folder.Merge( folder.Parent! ); } + _newFolderName = string.Empty; } diff --git a/Penumbra/UI/MenuTabs/TabSettings.cs b/Penumbra/UI/MenuTabs/TabSettings.cs index c0a3260d..9feaebe1 100644 --- a/Penumbra/UI/MenuTabs/TabSettings.cs +++ b/Penumbra/UI/MenuTabs/TabSettings.cs @@ -17,8 +17,10 @@ namespace Penumbra.UI { private const string LabelTab = "Settings"; private const string LabelRootFolder = "Root Folder"; + private const string LabelTempFolder = "Temporary Folder"; private const string LabelRediscoverButton = "Rediscover Mods"; private const string LabelOpenFolder = "Open Mods Folder"; + private const string LabelOpenTempFolder = "Open Temporary Folder"; private const string LabelEnabled = "Enable Mods"; private const string LabelEnabledPlayerWatch = "Enable automatic Character Redraws"; private const string LabelWaitFrames = "Wait Frames"; @@ -47,10 +49,39 @@ namespace Penumbra.UI if( ImGui.InputText( LabelRootFolder, ref basePath, 255, ImGuiInputTextFlags.EnterReturnsTrue ) && _config.ModDirectory != basePath ) { - _config.ModDirectory = basePath; - _configChanged = true; - _base.ReloadMods(); _base._menu.InstalledTab.Selector.ClearSelection(); + _base._modManager.DiscoverMods( basePath ); + _base._menu.InstalledTab.Selector.Cache.TriggerListReset(); + } + } + + private void DrawTempFolder() + { + var tempPath = _config.TempDirectory; + ImGui.SetNextItemWidth( 400 ); + if( ImGui.InputText( LabelTempFolder, ref tempPath, 255, ImGuiInputTextFlags.EnterReturnsTrue ) + && _config.TempDirectory != tempPath ) + { + _base._modManager.SetTempDirectory( tempPath ); + } + + if( ImGui.IsItemHovered() ) + { + ImGui.SetTooltip( "The folder used to store temporary meta manipulation files.\n" + + "Leave this blank if you have no reason not to.\n" + + "A folder 'penumbrametatmp' will be created as a subdirectory to the specified directory.\n" + + "If none is specified (i.e. this is blank) this folder will be created in the root folder instead." ); + } + + ImGui.SameLine(); + if( ImGui.Button( LabelOpenTempFolder ) ) + { + if( !Directory.Exists( _base._modManager.TempPath.FullName ) || !_base._modManager.TempWritable ) + { + return; + } + + Process.Start( _base._modManager.TempPath.FullName ); } } @@ -58,8 +89,9 @@ namespace Penumbra.UI { if( ImGui.Button( LabelRediscoverButton ) ) { - _base.ReloadMods(); _base._menu.InstalledTab.Selector.ClearSelection(); + _base._modManager.DiscoverMods(); + _base._menu.InstalledTab.Selector.Cache.TriggerListReset(); } } @@ -108,7 +140,7 @@ namespace Penumbra.UI { _config.SortFoldersFirst = foldersFirst; _base._menu.InstalledTab.Selector.Cache.TriggerListReset(); - _configChanged = true; + _configChanged = true; } } @@ -224,6 +256,7 @@ namespace Penumbra.UI private void DrawAdvancedSettings() { + DrawTempFolder(); DrawLogLoadedFilesBox(); DrawDisableNotificationsBox(); DrawEnableHttpApiBox(); diff --git a/Penumbra/UI/SettingsInterface.cs b/Penumbra/UI/SettingsInterface.cs index b75ef5d0..66d921b6 100644 --- a/Penumbra/UI/SettingsInterface.cs +++ b/Penumbra/UI/SettingsInterface.cs @@ -74,7 +74,7 @@ namespace Penumbra.UI var current = _modManager.Collections.CurrentCollection; if( current.Cache != null ) { - current.CalculateEffectiveFileList( _modManager.BasePath, recalculateMeta, + current.CalculateEffectiveFileList( _modManager.TempPath, recalculateMeta, current == _modManager.Collections.ActiveCollection ); _menu.InstalledTab.Selector.Cache.TriggerFilterReset(); }