From afa11f85e28fb0fd5806667c4683365aa97dc98b Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Thu, 30 Mar 2023 23:51:13 +0200 Subject: [PATCH] Use ModManager2 --- Penumbra/Api/PenumbraApi.cs | 50 +++-- Penumbra/Collections/CollectionManager.cs | 13 +- Penumbra/Mods/Editor/ModBackup.cs | 2 +- Penumbra/Mods/Manager/ExportManager.cs | 17 +- Penumbra/Mods/Manager/Mod.Manager.BasePath.cs | 203 ------------------ Penumbra/Mods/Manager/Mod.Manager.Root.cs | 102 --------- Penumbra/Mods/Manager/Mod.Manager.cs | 174 +++------------ Penumbra/Mods/ModCacheManager.cs | 1 - Penumbra/Mods/ModFileSystem.cs | 16 +- Penumbra/PenumbraNew.cs | 6 +- Penumbra/Services/ConfigMigrationService.cs | 2 +- Penumbra/UI/AdvancedWindow/ItemSwapTab.cs | 4 +- Penumbra/UI/FileDialogService.cs | 17 +- Penumbra/UI/ModsTab/ModFileSystemSelector.cs | 40 ++-- Penumbra/UI/ModsTab/ModPanelEditTab.cs | 24 +-- Penumbra/UI/ModsTab/ModPanelSettingsTab.cs | 2 +- 16 files changed, 131 insertions(+), 542 deletions(-) delete mode 100644 Penumbra/Mods/Manager/Mod.Manager.BasePath.cs delete mode 100644 Penumbra/Mods/Manager/Mod.Manager.Root.cs diff --git a/Penumbra/Api/PenumbraApi.cs b/Penumbra/Api/PenumbraApi.cs index 5b5df883..8b6adafc 100644 --- a/Penumbra/Api/PenumbraApi.cs +++ b/Penumbra/Api/PenumbraApi.cs @@ -56,6 +56,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi { if (value == null) return; + CheckInitialized(); _communicator.CreatingCharacterBase.Event += new Action(value); } @@ -63,6 +64,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi { if (value == null) return; + CheckInitialized(); _communicator.CreatingCharacterBase.Event -= new Action(value); } @@ -74,6 +76,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi { if (value == null) return; + CheckInitialized(); _communicator.CreatedCharacterBase.Event += new Action(value); } @@ -81,6 +84,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi { if (value == null) return; + CheckInitialized(); _communicator.CreatedCharacterBase.Event -= new Action(value); } @@ -93,10 +97,10 @@ public class PenumbraApi : IDisposable, IPenumbraApi private Penumbra _penumbra; private Lumina.GameData? _lumina; - private ModManager _modManager; + private ModManager _modManager; private ResourceLoader _resourceLoader; private Configuration _config; - private CollectionManager _collectionManager; + private CollectionManager _collectionManager; private DalamudServices _dalamud; private TempCollectionManager _tempCollections; private TempModManager _tempMods; @@ -108,18 +112,18 @@ public class PenumbraApi : IDisposable, IPenumbraApi Configuration config, CollectionManager collectionManager, DalamudServices dalamud, TempCollectionManager tempCollections, TempModManager tempMods, ActorService actors, CollectionResolver collectionResolver, CutsceneService cutsceneService) { - _communicator = communicator; - _penumbra = penumbra; - _modManager = modManager; - _resourceLoader = resourceLoader; - _config = config; - _collectionManager = collectionManager; - _dalamud = dalamud; - _tempCollections = tempCollections; - _tempMods = tempMods; - _actors = actors; - _collectionResolver = collectionResolver; - _cutsceneService = cutsceneService; + _communicator = communicator; + _penumbra = penumbra; + _modManager = modManager; + _resourceLoader = resourceLoader; + _config = config; + _collectionManager = collectionManager; + _dalamud = dalamud; + _tempCollections = tempCollections; + _tempMods = tempMods; + _actors = actors; + _collectionResolver = collectionResolver; + _cutsceneService = cutsceneService; _lumina = (Lumina.GameData?)_dalamud.GameData.GetType() .GetField("gameData", BindingFlags.Instance | BindingFlags.NonPublic) @@ -129,13 +133,13 @@ public class PenumbraApi : IDisposable, IPenumbraApi _communicator.CollectionChange.Event += SubscribeToNewCollections; _resourceLoader.ResourceLoaded += OnResourceLoaded; - _modManager.ModPathChanged += ModPathChangeSubscriber; + _communicator.ModPathChanged.Event += ModPathChangeSubscriber; } public unsafe void Dispose() { if (!Valid) - return; + return; foreach (var collection in _collectionManager) { @@ -145,7 +149,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi _resourceLoader.ResourceLoaded -= OnResourceLoaded; _communicator.CollectionChange.Event -= SubscribeToNewCollections; - _modManager.ModPathChanged -= ModPathChangeSubscriber; + _communicator.ModPathChanged.Event -= ModPathChangeSubscriber; _lumina = null; _communicator = null!; _penumbra = null!; @@ -182,12 +186,12 @@ public class PenumbraApi : IDisposable, IPenumbraApi add { CheckInitialized(); - _modManager.ModDirectoryChanged += value; + _communicator.ModDirectoryChanged.Event += value; } remove { CheckInitialized(); - _modManager.ModDirectoryChanged -= value; + _communicator.ModDirectoryChanged.Event -= value; } } @@ -542,8 +546,8 @@ public class PenumbraApi : IDisposable, IPenumbraApi public unsafe (nint, string) GetDrawObjectInfo(nint drawObject) { - CheckInitialized(); - var data = _collectionResolver.IdentifyCollection((DrawObject*) drawObject, true); + CheckInitialized(); + var data = _collectionResolver.IdentifyCollection((DrawObject*)drawObject, true); return (data.AssociatedGameObject, data.ModCollection.Name); } @@ -592,7 +596,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi if (!_modManager.TryGetMod(modDirectory, modName, out var mod)) return PenumbraApiEc.ModMissing; - _modManager.ReloadMod(mod.Index); + _modManager.ReloadMod(mod); return PenumbraApiEc.Success; } @@ -613,7 +617,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi if (!_modManager.TryGetMod(modDirectory, modName, out var mod)) return PenumbraApiEc.NothingChanged; - _modManager.DeleteMod(mod.Index); + _modManager.DeleteMod(mod); return PenumbraApiEc.Success; } diff --git a/Penumbra/Collections/CollectionManager.cs b/Penumbra/Collections/CollectionManager.cs index c1510686..0f2694ba 100644 --- a/Penumbra/Collections/CollectionManager.cs +++ b/Penumbra/Collections/CollectionManager.cs @@ -9,7 +9,6 @@ using System.IO; using System.Linq; using Penumbra.Api; using Penumbra.GameData.Actors; -using Penumbra.Interop; using Penumbra.Interop.Services; using Penumbra.Services; using Penumbra.Util; @@ -71,10 +70,10 @@ public sealed partial class CollectionManager : IDisposable, IEnumerable _exportDirectory ?? _modManager.BasePath; - public ExportManager(Configuration config, ModManager modManager) + public ExportManager(Configuration config, CommunicatorService communicator, ModManager modManager) { - _config = config; - _modManager = modManager; + _config = config; + _communicator = communicator; + _modManager = modManager; UpdateExportDirectory(_config.ExportDirectory, false); - _modManager.ModPathChanged += OnModPathChange; + _communicator.ModPathChanged.Event += OnModPathChange; } /// @@ -73,7 +76,7 @@ public class ExportManager : IDisposable } public void Dispose() - => _modManager.ModPathChanged -= OnModPathChange; + => _communicator.ModPathChanged.Event -= OnModPathChange; /// Automatically migrate the backup file to the new name if any exists. private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory, diff --git a/Penumbra/Mods/Manager/Mod.Manager.BasePath.cs b/Penumbra/Mods/Manager/Mod.Manager.BasePath.cs deleted file mode 100644 index b6e50c80..00000000 --- a/Penumbra/Mods/Manager/Mod.Manager.BasePath.cs +++ /dev/null @@ -1,203 +0,0 @@ -using System; -using System.IO; -using System.Linq; - -namespace Penumbra.Mods; - -public partial class ModManager -{ - public delegate void ModPathChangeDelegate(ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory, - DirectoryInfo? newDirectory); - - public event ModPathChangeDelegate ModPathChanged; - - /// - /// Rename/Move a mod directory. - /// Updates all collection settings and sort order settings. - /// - public void MoveModDirectory(int idx, string newName) - { - var mod = this[idx]; - var oldName = mod.Name; - var oldDirectory = mod.ModPath; - - switch (NewDirectoryValid(oldDirectory.Name, newName, out var dir)) - { - case NewDirectoryState.NonExisting: - // Nothing to do - break; - case NewDirectoryState.ExistsEmpty: - try - { - Directory.Delete(dir!.FullName); - } - catch (Exception e) - { - Penumbra.Log.Error($"Could not delete empty directory {dir!.FullName} to move {mod.Name} to it:\n{e}"); - return; - } - - break; - // Should be caught beforehand. - case NewDirectoryState.ExistsNonEmpty: - case NewDirectoryState.ExistsAsFile: - case NewDirectoryState.ContainsInvalidSymbols: - // Nothing to do at all. - case NewDirectoryState.Identical: - default: - return; - } - - try - { - Directory.Move(oldDirectory.FullName, dir!.FullName); - } - catch (Exception e) - { - Penumbra.Log.Error($"Could not move {mod.Name} from {oldDirectory.Name} to {dir!.Name}:\n{e}"); - return; - } - - DataEditor.MoveDataFile(oldDirectory, dir); - - dir.Refresh(); - mod.ModPath = dir; - if (!mod.Reload(this, false, out var metaChange)) - { - Penumbra.Log.Error($"Error reloading moved mod {mod.Name}."); - return; - } - - ModPathChanged.Invoke(ModPathChangeType.Moved, mod, oldDirectory, dir); - if (metaChange != ModDataChangeType.None) - _communicator.ModDataChanged.Invoke(metaChange, mod, oldName); - } - - /// - /// Reload a mod without changing its base directory. - /// If the base directory does not exist anymore, the mod will be deleted. - /// - public void ReloadMod(int idx) - { - var mod = this[idx]; - var oldName = mod.Name; - - ModPathChanged.Invoke(ModPathChangeType.StartingReload, mod, mod.ModPath, mod.ModPath); - if (!mod.Reload(this, true, out var metaChange)) - { - Penumbra.Log.Warning(mod.Name.Length == 0 - ? $"Reloading mod {oldName} has failed, new name is empty. Deleting instead." - : $"Reloading mod {oldName} failed, {mod.ModPath.FullName} does not exist anymore or it ha. Deleting instead."); - - DeleteMod(idx); - return; - } - - ModPathChanged.Invoke(ModPathChangeType.Reloaded, mod, mod.ModPath, mod.ModPath); - if (metaChange != ModDataChangeType.None) - _communicator.ModDataChanged.Invoke(metaChange, mod, oldName); - } - - /// - /// Delete a mod by its index. The event is invoked before the mod is removed from the list. - /// Deletes from filesystem as well as from internal data. - /// Updates indices of later mods. - /// - public void DeleteMod(int idx) - { - var mod = this[idx]; - if (Directory.Exists(mod.ModPath.FullName)) - try - { - Directory.Delete(mod.ModPath.FullName, true); - Penumbra.Log.Debug($"Deleted directory {mod.ModPath.FullName} for {mod.Name}."); - } - catch (Exception e) - { - Penumbra.Log.Error($"Could not delete the mod {mod.ModPath.Name}:\n{e}"); - } - - ModPathChanged.Invoke(ModPathChangeType.Deleted, mod, mod.ModPath, null); - _mods.RemoveAt(idx); - foreach (var remainingMod in _mods.Skip(idx)) - --remainingMod.Index; - - Penumbra.Log.Debug($"Deleted mod {mod.Name}."); - } - - /// Load a new mod and add it to the manager if successful. - public void AddMod(DirectoryInfo modFolder) - { - if (_mods.Any(m => m.ModPath.Name == modFolder.Name)) - return; - - Mod.Creator.SplitMultiGroups(modFolder); - var mod = Mod.LoadMod(this, modFolder, true); - if (mod == null) - return; - - mod.Index = _mods.Count; - _mods.Add(mod); - ModPathChanged.Invoke(ModPathChangeType.Added, mod, null, mod.ModPath); - Penumbra.Log.Debug($"Added new mod {mod.Name} from {modFolder.FullName}."); - } - - public enum NewDirectoryState - { - NonExisting, - ExistsEmpty, - ExistsNonEmpty, - ExistsAsFile, - ContainsInvalidSymbols, - Identical, - Empty, - } - - /// Return the state of the new potential name of a directory. - public NewDirectoryState NewDirectoryValid(string oldName, string newName, out DirectoryInfo? directory) - { - directory = null; - if (newName.Length == 0) - return NewDirectoryState.Empty; - - if (oldName == newName) - return NewDirectoryState.Identical; - - var fixedNewName = Mod.Creator.ReplaceBadXivSymbols(newName); - if (fixedNewName != newName) - return NewDirectoryState.ContainsInvalidSymbols; - - directory = new DirectoryInfo(Path.Combine(BasePath.FullName, fixedNewName)); - if (File.Exists(directory.FullName)) - return NewDirectoryState.ExistsAsFile; - - if (!Directory.Exists(directory.FullName)) - return NewDirectoryState.NonExisting; - - if (directory.EnumerateFileSystemInfos().Any()) - return NewDirectoryState.ExistsNonEmpty; - - return NewDirectoryState.ExistsEmpty; - } - - - /// Add new mods to NewMods and remove deleted mods from NewMods. - private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory, - DirectoryInfo? newDirectory) - { - switch (type) - { - case ModPathChangeType.Added: - NewMods.Add(mod); - break; - case ModPathChangeType.Deleted: - NewMods.Remove(mod); - break; - case ModPathChangeType.Moved: - if (oldDirectory != null && newDirectory != null) - DataEditor.MoveDataFile(oldDirectory, newDirectory); - - break; - } - } -} diff --git a/Penumbra/Mods/Manager/Mod.Manager.Root.cs b/Penumbra/Mods/Manager/Mod.Manager.Root.cs deleted file mode 100644 index 575954d5..00000000 --- a/Penumbra/Mods/Manager/Mod.Manager.Root.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.IO; -using System.Threading.Tasks; - -namespace Penumbra.Mods; - -public sealed partial class ModManager -{ - public DirectoryInfo BasePath { get; private set; } = null!; - public bool Valid { get; private set; } - - public event Action? ModDiscoveryStarted; - public event Action? ModDiscoveryFinished; - public event Action ModDirectoryChanged; - - // Change the mod base directory and discover available mods. - public void DiscoverMods(string newDir) - { - SetBaseDirectory(newDir, false); - DiscoverMods(); - } - - // Set the mod base directory. - // If its not the first time, check if it is the same directory as before. - // Also checks if the directory is available and tries to create it if it is not. - private void SetBaseDirectory(string newPath, bool firstTime) - { - if (!firstTime && string.Equals(newPath, Penumbra.Config.ModDirectory, StringComparison.OrdinalIgnoreCase)) - return; - - if (newPath.Length == 0) - { - Valid = false; - BasePath = new DirectoryInfo("."); - if (Penumbra.Config.ModDirectory != BasePath.FullName) - ModDirectoryChanged.Invoke(string.Empty, false); - } - else - { - var newDir = new DirectoryInfo(newPath); - if (!newDir.Exists) - try - { - Directory.CreateDirectory(newDir.FullName); - newDir.Refresh(); - } - catch (Exception e) - { - Penumbra.Log.Error($"Could not create specified mod directory {newDir.FullName}:\n{e}"); - } - - BasePath = newDir; - Valid = Directory.Exists(newDir.FullName); - if (Penumbra.Config.ModDirectory != BasePath.FullName) - ModDirectoryChanged.Invoke(BasePath.FullName, Valid); - } - } - - private static void OnModDirectoryChange(string newPath, bool _) - { - Penumbra.Log.Information($"Set new mod base directory from {Penumbra.Config.ModDirectory} to {newPath}."); - Penumbra.Config.ModDirectory = newPath; - Penumbra.Config.Save(); - } - - // Discover new mods. - public void DiscoverMods() - { - NewMods.Clear(); - ModDiscoveryStarted?.Invoke(); - _mods.Clear(); - BasePath.Refresh(); - - if (Valid && BasePath.Exists) - { - var options = new ParallelOptions() - { - MaxDegreeOfParallelism = Environment.ProcessorCount / 2, - }; - var queue = new ConcurrentQueue(); - Parallel.ForEach(BasePath.EnumerateDirectories(), options, dir => - { - var mod = Mod.LoadMod(this, dir, false); - if (mod != null) - queue.Enqueue(mod); - }); - - foreach (var mod in queue) - { - mod.Index = _mods.Count; - _mods.Add(mod); - } - } - - ModDiscoveryFinished?.Invoke(); - Penumbra.Log.Information("Rediscovered mods."); - - if (MigrateModBackups) - ModBackup.MigrateZipToPmp(this); - } -} diff --git a/Penumbra/Mods/Manager/Mod.Manager.cs b/Penumbra/Mods/Manager/Mod.Manager.cs index febe8209..050c4ed1 100644 --- a/Penumbra/Mods/Manager/Mod.Manager.cs +++ b/Penumbra/Mods/Manager/Mod.Manager.cs @@ -1,15 +1,12 @@ using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; +using System.Collections.Concurrent; using System.IO; using System.Linq; -using System.Threading.Tasks; +using System.Threading.Tasks; using Penumbra.Services; -using Penumbra.Util; + namespace Penumbra.Mods; - + /// Describes the state of a potential move-target for a mod. public enum NewDirectoryState { @@ -20,25 +17,27 @@ public enum NewDirectoryState ContainsInvalidSymbols, Identical, Empty, -} +} -public sealed class ModManager2 : ModStorage +public sealed class ModManager : ModStorage { private readonly Configuration _config; - private readonly CommunicatorService _communicator; + private readonly CommunicatorService _communicator; public readonly ModDataEditor DataEditor; public readonly ModOptionEditor OptionEditor; public DirectoryInfo BasePath { get; private set; } = null!; - public bool Valid { get; private set; } + public bool Valid { get; private set; } - public ModManager2(Configuration config, CommunicatorService communicator, ModDataEditor dataEditor, ModOptionEditor optionEditor) + public ModManager(Configuration config, CommunicatorService communicator, ModDataEditor dataEditor, ModOptionEditor optionEditor) { _config = config; _communicator = communicator; DataEditor = dataEditor; - OptionEditor = optionEditor; + OptionEditor = optionEditor; + SetBaseDirectory(config.ModDirectory, true); + DiscoverMods(); } /// Change the mod base directory and discover available mods. @@ -75,7 +74,7 @@ public sealed class ModManager2 : ModStorage return; Mod.Creator.SplitMultiGroups(modFolder); - var mod = Mod.LoadMod(Penumbra.ModManager, modFolder, true); + var mod = Mod.LoadMod(this, modFolder, true); if (mod == null) return; @@ -133,16 +132,16 @@ public sealed class ModManager2 : ModStorage _communicator.ModPathChanged.Invoke(ModPathChangeType.Reloaded, mod, mod.ModPath, mod.ModPath); if (metaChange != ModDataChangeType.None) _communicator.ModDataChanged.Invoke(metaChange, mod, oldName); - } - - - /// + } + + + /// /// Rename/Move a mod directory. - /// Updates all collection settings and sort order settings. - /// + /// Updates all collection settings and sort order settings. + /// public void MoveModDirectory(Mod mod, string newName) { - var oldName = mod.Name; + var oldName = mod.Name; var oldDirectory = mod.ModPath; switch (NewDirectoryValid(oldDirectory.Name, newName, out var dir)) @@ -195,8 +194,8 @@ public sealed class ModManager2 : ModStorage _communicator.ModPathChanged.Invoke(ModPathChangeType.Moved, mod, oldDirectory, dir); if (metaChange != ModDataChangeType.None) _communicator.ModDataChanged.Invoke(metaChange, mod, oldName); - } - + } + /// Return the state of the new potential name of a directory. public NewDirectoryState NewDirectoryValid(string oldName, string newName, out DirectoryInfo? directory) { @@ -243,16 +242,16 @@ public sealed class ModManager2 : ModStorage break; } - } + } public void Dispose() { } - /// + /// /// Set the mod base directory. /// If its not the first time, check if it is the same directory as before. - /// Also checks if the directory is available and tries to create it if it is not. - /// + /// Also checks if the directory is available and tries to create it if it is not. + /// private void SetBaseDirectory(string newPath, bool firstTime) { if (!firstTime && string.Equals(newPath, _config.ModDirectory, StringComparison.OrdinalIgnoreCase)) @@ -260,7 +259,7 @@ public sealed class ModManager2 : ModStorage if (newPath.Length == 0) { - Valid = false; + Valid = false; BasePath = new DirectoryInfo("."); if (_config.ModDirectory != BasePath.FullName) TriggerModDirectoryChange(string.Empty, false); @@ -280,8 +279,8 @@ public sealed class ModManager2 : ModStorage } BasePath = newDir; - Valid = Directory.Exists(newDir.FullName); - if (_config.ModDirectory != BasePath.FullName) + Valid = Directory.Exists(newDir.FullName); + if (!firstTime && _config.ModDirectory != BasePath.FullName) TriggerModDirectoryChange(BasePath.FullName, Valid); } } @@ -295,10 +294,9 @@ public sealed class ModManager2 : ModStorage } - - /// + /// /// Iterate through available mods with multiple threads and queue their loads, - /// then add the mods from the queue. + /// then add the mods from the queue. /// private void ScanMods() { @@ -309,7 +307,7 @@ public sealed class ModManager2 : ModStorage var queue = new ConcurrentQueue(); Parallel.ForEach(BasePath.EnumerateDirectories(), options, dir => { - var mod = Mod.LoadMod(Penumbra.ModManager, dir, false); + var mod = Mod.LoadMod(this, dir, false); if (mod != null) queue.Enqueue(mod); }); @@ -321,111 +319,3 @@ public sealed class ModManager2 : ModStorage } } } - -public sealed partial class ModManager : IReadOnlyList, IDisposable -{ - // Set when reading Config and migrating from v4 to v5. - public static bool MigrateModBackups = false; - - // An easily accessible set of new mods. - // Mods are added when they are created or imported. - // Mods are removed when they are deleted or when they are toggled in any collection. - // Also gets cleared on mod rediscovery. - public readonly HashSet NewMods = new(); - - private readonly List _mods = new(); - - public Mod this[int idx] - => _mods[idx]; - - public Mod this[Index idx] - => _mods[idx]; - - public int Count - => _mods.Count; - - public IEnumerator GetEnumerator() - => _mods.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() - => GetEnumerator(); - - private readonly Configuration _config; - private readonly CommunicatorService _communicator; - public readonly ModDataEditor DataEditor; - public readonly ModOptionEditor OptionEditor; - - public ModManager(StartTracker time, Configuration config, CommunicatorService communicator, ModDataEditor dataEditor, - ModOptionEditor optionEditor) - { - using var timer = time.Measure(StartTimeType.Mods); - _config = config; - _communicator = communicator; - DataEditor = dataEditor; - OptionEditor = optionEditor; - ModDirectoryChanged += OnModDirectoryChange; - SetBaseDirectory(config.ModDirectory, true); - _communicator.ModOptionChanged.Event += OnModOptionChange; - ModPathChanged += OnModPathChange; - DiscoverMods(); - } - - public void Dispose() - { - _communicator.ModOptionChanged.Event -= OnModOptionChange; - } - - - // Try to obtain a mod by its directory name (unique identifier, preferred), - // or the first mod of the given name if no directory fits. - public bool TryGetMod(string modDirectory, string modName, [NotNullWhen(true)] out Mod? mod) - { - mod = null; - foreach (var m in _mods) - { - if (string.Equals(m.ModPath.Name, modDirectory, StringComparison.OrdinalIgnoreCase)) - { - mod = m; - return true; - } - - if (m.Name == modName) - mod ??= m; - } - - return mod != null; - } - - private static void OnModOptionChange(ModOptionChangeType type, Mod mod, int groupIdx, int _, int _2) - { - if (type == ModOptionChangeType.PrepareChange) - return; - - bool ComputeChangedItems() - { - mod.ComputeChangedItems(); - return true; - } - - // State can not change on adding groups, as they have no immediate options. - var unused = type switch - { - ModOptionChangeType.GroupAdded => ComputeChangedItems() & mod.SetCounts(), - ModOptionChangeType.GroupDeleted => ComputeChangedItems() & mod.SetCounts(), - ModOptionChangeType.GroupMoved => false, - ModOptionChangeType.GroupTypeChanged => mod.HasOptions = mod.Groups.Any(o => o.IsOption), - ModOptionChangeType.PriorityChanged => false, - ModOptionChangeType.OptionAdded => ComputeChangedItems() & mod.SetCounts(), - ModOptionChangeType.OptionDeleted => ComputeChangedItems() & mod.SetCounts(), - ModOptionChangeType.OptionMoved => false, - ModOptionChangeType.OptionFilesChanged => ComputeChangedItems() - & (0 < (mod.TotalFileCount = mod.AllSubMods.Sum(s => s.Files.Count))), - ModOptionChangeType.OptionSwapsChanged => ComputeChangedItems() - & (0 < (mod.TotalSwapCount = mod.AllSubMods.Sum(s => s.FileSwaps.Count))), - ModOptionChangeType.OptionMetaChanged => ComputeChangedItems() - & (0 < (mod.TotalManipulations = mod.AllSubMods.Sum(s => s.Manipulations.Count))), - ModOptionChangeType.DisplayChange => false, - _ => false, - }; - } -} diff --git a/Penumbra/Mods/ModCacheManager.cs b/Penumbra/Mods/ModCacheManager.cs index 69787644..632c5a31 100644 --- a/Penumbra/Mods/ModCacheManager.cs +++ b/Penumbra/Mods/ModCacheManager.cs @@ -20,7 +20,6 @@ public class ModCacheManager : IDisposable, IReadOnlyList private readonly List _cache = new(); - // TODO ModManager2 public ModCacheManager(CommunicatorService communicator, IdentifierService identifier, ModManager modManager) { _communicator = communicator; diff --git a/Penumbra/Mods/ModFileSystem.cs b/Penumbra/Mods/ModFileSystem.cs index c16c172e..42ff4381 100644 --- a/Penumbra/Mods/ModFileSystem.cs +++ b/Penumbra/Mods/ModFileSystem.cs @@ -12,7 +12,7 @@ namespace Penumbra.Mods; public sealed class ModFileSystem : FileSystem, IDisposable, ISavable { - private readonly ModManager _modManager; + private readonly ModManager _modManager; private readonly CommunicatorService _communicator; private readonly FilenameService _files; @@ -23,17 +23,17 @@ public sealed class ModFileSystem : FileSystem, IDisposable, ISavable _communicator = communicator; _files = files; Reload(); - Changed += OnChange; - _modManager.ModDiscoveryFinished += Reload; - _communicator.ModDataChanged.Event += OnDataChange; - _modManager.ModPathChanged += OnModPathChange; + Changed += OnChange; + _communicator.ModDiscoveryFinished.Event += Reload; + _communicator.ModDataChanged.Event += OnDataChange; + _communicator.ModPathChanged.Event += OnModPathChange; } public void Dispose() { - _modManager.ModPathChanged -= OnModPathChange; - _modManager.ModDiscoveryFinished -= Reload; - _communicator.ModDataChanged.Event -= OnDataChange; + _communicator.ModPathChanged.Event -= OnModPathChange; + _communicator.ModDiscoveryFinished.Event -= Reload; + _communicator.ModDataChanged.Event -= OnDataChange; } public struct ImportDate : ISortMode diff --git a/Penumbra/PenumbraNew.cs b/Penumbra/PenumbraNew.cs index 73c642ac..23bfefe5 100644 --- a/Penumbra/PenumbraNew.cs +++ b/Penumbra/PenumbraNew.cs @@ -94,7 +94,8 @@ public class PenumbraNew .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton(); + .AddSingleton() + .AddSingleton(); // Add Resource services services.AddSingleton() @@ -150,8 +151,7 @@ public class PenumbraNew .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() - .AddSingleton(); + .AddSingleton(); // Add API services.AddSingleton() diff --git a/Penumbra/Services/ConfigMigrationService.cs b/Penumbra/Services/ConfigMigrationService.cs index a2f8b55f..c2d64f92 100644 --- a/Penumbra/Services/ConfigMigrationService.cs +++ b/Penumbra/Services/ConfigMigrationService.cs @@ -113,7 +113,7 @@ public class ConfigMigrationService if (_config.Version != 4) return; - ModManager.MigrateModBackups = true; + ModBackup.MigrateModBackups = true; _config.Version = 5; } diff --git a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs index 9a0af228..c50449a6 100644 --- a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs +++ b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs @@ -270,9 +270,9 @@ public class ItemSwapTab : IDisposable, ITab _modManager.DataEditor.CreateMeta(newDir, _newModName, _config.DefaultModAuthor, CreateDescription(), "1.0", string.Empty); Mod.Creator.CreateDefaultFiles(newDir); _modManager.AddMod(newDir); - if (!_swapData.WriteMod(_modManager, _modManager.Last(), + if (!_swapData.WriteMod(_modManager, _modManager[^1], _useFileSwaps ? ItemSwapContainer.WriteType.UseSwaps : ItemSwapContainer.WriteType.NoSwaps)) - _modManager.DeleteMod(_modManager.Count - 1); + _modManager.DeleteMod(_modManager[^1]); } private void CreateOption() diff --git a/Penumbra/UI/FileDialogService.cs b/Penumbra/UI/FileDialogService.cs index 061f53ad..c6a7d451 100644 --- a/Penumbra/UI/FileDialogService.cs +++ b/Penumbra/UI/FileDialogService.cs @@ -9,23 +9,22 @@ using Dalamud.Interface.ImGuiFileDialog; using Dalamud.Utility; using ImGuiNET; using OtterGui; -using Penumbra.Mods; +using Penumbra.Services; namespace Penumbra.UI; public class FileDialogService : IDisposable { - private readonly ModManager _mods; + private readonly CommunicatorService _communicator; private readonly FileDialogManager _manager; private readonly ConcurrentDictionary _startPaths = new(); private bool _isOpen; - public FileDialogService(ModManager mods, Configuration config) + public FileDialogService(CommunicatorService communicator, Configuration config) { - _mods = mods; - _manager = SetupFileManager(config.ModDirectory); - - _mods.ModDirectoryChanged += OnModDirectoryChange; + _communicator = communicator; + _manager = SetupFileManager(config.ModDirectory); + _communicator.ModDirectoryChanged.Event += OnModDirectoryChange; } public void OpenFilePicker(string title, string filters, Action> callback, int selectionCountMax, string? startPath, @@ -72,7 +71,7 @@ public class FileDialogService : IDisposable { _startPaths.Clear(); _manager.Reset(); - _mods.ModDirectoryChanged -= OnModDirectoryChange; + _communicator.ModDirectoryChanged.Event -= OnModDirectoryChange; } private string? GetStartPath(string title, string? startPath, bool forceStartPath) @@ -87,7 +86,7 @@ public class FileDialogService : IDisposable { return (valid, list) => { - _isOpen = false; + _isOpen = false; var loc = HandleRoot(GetCurrentLocation()); _startPaths[title] = loc; callback(valid, list.Select(HandleRoot).ToList()); diff --git a/Penumbra/UI/ModsTab/ModFileSystemSelector.cs b/Penumbra/UI/ModsTab/ModFileSystemSelector.cs index d1ec76ad..0ea3b734 100644 --- a/Penumbra/UI/ModsTab/ModFileSystemSelector.cs +++ b/Penumbra/UI/ModsTab/ModFileSystemSelector.cs @@ -25,20 +25,20 @@ namespace Penumbra.UI.ModsTab; public sealed partial class ModFileSystemSelector : FileSystemSelector { - private readonly CommunicatorService _communicator; - private readonly ChatService _chat; - private readonly Configuration _config; - private readonly FileDialogService _fileDialog; - private readonly ModManager _modManager; - private readonly CollectionManager _collectionManager; - private readonly TutorialService _tutorial; - private readonly ModEditor _modEditor; + private readonly CommunicatorService _communicator; + private readonly ChatService _chat; + private readonly Configuration _config; + private readonly FileDialogService _fileDialog; + private readonly ModManager _modManager; + private readonly CollectionManager _collectionManager; + private readonly TutorialService _tutorial; + private readonly ModEditor _modEditor; private TexToolsImporter? _import; public ModSettings SelectedSettings { get; private set; } = ModSettings.Empty; public ModCollection SelectedSettingCollection { get; private set; } = ModCollection.Empty; - private uint _infoPopupId = 0; + private uint _infoPopupId = 0; public ModFileSystemSelector(CommunicatorService communicator, ModFileSystem fileSystem, ModManager modManager, CollectionManager collectionManager, Configuration config, TutorialService tutorial, FileDialogService fileDialog, ChatService chat, @@ -81,16 +81,16 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector.Lexicographical).OfType().Select(l => { // Any mod handled here should not stay new. - _modManager.NewMods.Remove(l.Value); + _modManager.SetKnown(l.Value); return l.Value; }); @@ -428,7 +428,7 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector Penumbra.ChatService.NotificationMessage(e.Message, "Failure", NotificationType.Warning); + => Penumbra.ChatService.NotificationMessage(e.Message, "Failure", NotificationType.Warning); #endregion @@ -618,7 +618,7 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector Only get the text color for a mod if no filters are set. private ColorId GetTextColor(Mod mod, ModSettings? settings, ModCollection collection) { - if (Penumbra.ModManager.NewMods.Contains(mod)) + if (_modManager.IsNew(mod)) return ColorId.NewMod; if (settings == null) @@ -638,7 +638,7 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector A text input for the new directory name and a button to apply the move. private static class MoveDirectory { - private static string? _currentModDirectory; - private static ModManager.NewDirectoryState _state = ModManager.NewDirectoryState.Identical; + private static string? _currentModDirectory; + private static NewDirectoryState _state = NewDirectoryState.Identical; public static void Reset() { _currentModDirectory = null; - _state = ModManager.NewDirectoryState.Identical; + _state = NewDirectoryState.Identical; } public static void Draw(ModManager modManager, Mod mod, Vector2 buttonSize) @@ -276,20 +276,20 @@ public class ModPanelEditTab : ITab var (disabled, tt) = _state switch { - ModManager.NewDirectoryState.Identical => (true, "Current directory name is identical to new one."), - ModManager.NewDirectoryState.Empty => (true, "Please enter a new directory name first."), - ModManager.NewDirectoryState.NonExisting => (false, $"Move mod from {mod.ModPath.Name} to {_currentModDirectory}."), - ModManager.NewDirectoryState.ExistsEmpty => (false, $"Move mod from {mod.ModPath.Name} to {_currentModDirectory}."), - ModManager.NewDirectoryState.ExistsNonEmpty => (true, $"{_currentModDirectory} already exists and is not empty."), - ModManager.NewDirectoryState.ExistsAsFile => (true, $"{_currentModDirectory} exists as a file."), - ModManager.NewDirectoryState.ContainsInvalidSymbols => (true, + NewDirectoryState.Identical => (true, "Current directory name is identical to new one."), + NewDirectoryState.Empty => (true, "Please enter a new directory name first."), + NewDirectoryState.NonExisting => (false, $"Move mod from {mod.ModPath.Name} to {_currentModDirectory}."), + NewDirectoryState.ExistsEmpty => (false, $"Move mod from {mod.ModPath.Name} to {_currentModDirectory}."), + NewDirectoryState.ExistsNonEmpty => (true, $"{_currentModDirectory} already exists and is not empty."), + NewDirectoryState.ExistsAsFile => (true, $"{_currentModDirectory} exists as a file."), + NewDirectoryState.ContainsInvalidSymbols => (true, $"{_currentModDirectory} contains invalid symbols for FFXIV."), _ => (true, "Unknown error."), }; ImGui.SameLine(); if (ImGuiUtil.DrawDisabledButton("Rename Mod Directory", buttonSize, tt, disabled) && _currentModDirectory != null) { - modManager.MoveModDirectory(mod.Index, _currentModDirectory); + modManager.MoveModDirectory(mod, _currentModDirectory); Reset(); } diff --git a/Penumbra/UI/ModsTab/ModPanelSettingsTab.cs b/Penumbra/UI/ModsTab/ModPanelSettingsTab.cs index 72dbe9fa..26702c5a 100644 --- a/Penumbra/UI/ModsTab/ModPanelSettingsTab.cs +++ b/Penumbra/UI/ModsTab/ModPanelSettingsTab.cs @@ -125,7 +125,7 @@ public class ModPanelSettingsTab : ITab if (!ImGui.Checkbox("Enabled", ref enabled)) return; - _modManager.NewMods.Remove(_selector.Selected!); + _modManager.SetKnown(_selector.Selected!); _collectionManager.Current.SetModState(_selector.Selected!.Index, enabled); }