From 6079103505c3b0b99c26e041f59c26c41b13a543 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sat, 23 Aug 2025 14:46:19 +0200 Subject: [PATCH] Add collection PCP settings. --- Penumbra.Api | 2 +- Penumbra/Api/Api/ModsApi.cs | 2 +- Penumbra/Communication/PcpCreation.cs | 3 +- Penumbra/Configuration.cs | 19 ++++++---- Penumbra/Services/PcpService.cs | 51 ++++++++++++++++----------- Penumbra/UI/Tabs/SettingsTab.cs | 19 +++++++--- 6 files changed, 61 insertions(+), 35 deletions(-) diff --git a/Penumbra.Api b/Penumbra.Api index 0a970295..297941bc 160000 --- a/Penumbra.Api +++ b/Penumbra.Api @@ -1 +1 @@ -Subproject commit 0a970295b2398683b1e49c46fd613541e2486210 +Subproject commit 297941bc22300f4a8368f4d0177f62943eb69727 diff --git a/Penumbra/Api/Api/ModsApi.cs b/Penumbra/Api/Api/ModsApi.cs index 55f1e259..1f4f1cf4 100644 --- a/Penumbra/Api/Api/ModsApi.cs +++ b/Penumbra/Api/Api/ModsApi.cs @@ -108,7 +108,7 @@ public class ModsApi : IPenumbraApiMods, IApiService, IDisposable public event Action? ModAdded; public event Action? ModMoved; - public event Action? CreatingPcp + public event Action? CreatingPcp { add => _communicator.PcpCreation.Subscribe(value!, PcpCreation.Priority.ModsApi); remove => _communicator.PcpCreation.Unsubscribe(value!); diff --git a/Penumbra/Communication/PcpCreation.cs b/Penumbra/Communication/PcpCreation.cs index cb11b3c3..ca0cfcf6 100644 --- a/Penumbra/Communication/PcpCreation.cs +++ b/Penumbra/Communication/PcpCreation.cs @@ -8,9 +8,10 @@ namespace Penumbra.Communication; /// /// Parameter is the JObject that gets written to file. /// Parameter is the object index of the game object this is written for. +/// Parameter is the full path to the directory being set up for the PCP creation. /// /// -public sealed class PcpCreation() : EventWrapper(nameof(PcpCreation)) +public sealed class PcpCreation() : EventWrapper(nameof(PcpCreation)) { public enum Priority { diff --git a/Penumbra/Configuration.cs b/Penumbra/Configuration.cs index d9a9f5fe..f9cad217 100644 --- a/Penumbra/Configuration.cs +++ b/Penumbra/Configuration.cs @@ -18,6 +18,15 @@ using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; namespace Penumbra; +public record PcpSettings +{ + public bool CreateCollection { get; set; } = true; + public bool AssignCollection { get; set; } = true; + public bool AllowIpc { get; set; } = true; + public bool DisableHandling { get; set; } = false; + public string FolderName { get; set; } = "PCP"; +} + [Serializable] public class Configuration : IPluginConfiguration, ISavable, IService { @@ -68,11 +77,10 @@ public class Configuration : IPluginConfiguration, ISavable, IService public bool HideMachinistOffhandFromChangedItems { get; set; } = true; public bool DefaultTemporaryMode { get; set; } = false; public bool EnableCustomShapes { get; set; } = true; - public bool DisablePcpHandling { get; set; } = false; - public bool AllowPcpIpc { get; set; } = true; - public RenameField ShowRename { get; set; } = RenameField.BothDataPrio; - public ChangedItemMode ChangedItemDisplay { get; set; } = ChangedItemMode.GroupedCollapsed; - public int OptionGroupCollapsibleMin { get; set; } = 5; + public PcpSettings PcpSettings = new(); + public RenameField ShowRename { get; set; } = RenameField.BothDataPrio; + public ChangedItemMode ChangedItemDisplay { get; set; } = ChangedItemMode.GroupedCollapsed; + public int OptionGroupCollapsibleMin { get; set; } = 5; public Vector2 MinimumSize = new(Constants.MinimumSizeX, Constants.MinimumSizeY); @@ -90,7 +98,6 @@ public class Configuration : IPluginConfiguration, ISavable, IService public bool OpenFoldersByDefault { get; set; } = false; public int SingleGroupRadioMax { get; set; } = 2; public string DefaultImportFolder { get; set; } = string.Empty; - public string PcpFolderName { get; set; } = "PCP"; public string QuickMoveFolder1 { get; set; } = string.Empty; public string QuickMoveFolder2 { get; set; } = string.Empty; public string QuickMoveFolder3 { get; set; } = string.Empty; diff --git a/Penumbra/Services/PcpService.cs b/Penumbra/Services/PcpService.cs index 73c61cdb..b9d472aa 100644 --- a/Penumbra/Services/PcpService.cs +++ b/Penumbra/Services/PcpService.cs @@ -89,7 +89,7 @@ public class PcpService : IApiService, IDisposable private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory, DirectoryInfo? newDirectory) { - if (type is not ModPathChangeType.Added || _config.DisablePcpHandling || newDirectory is null) + if (type is not ModPathChangeType.Added || _config.PcpSettings.DisableHandling || newDirectory is null) return; try @@ -107,29 +107,37 @@ public class PcpService : IApiService, IDisposable } Penumbra.Log.Information($"[PCPService] Found a PCP file for {mod.Name}, applying."); - var text = File.ReadAllText(file); - var jObj = JObject.Parse(text); - var identifier = _actors.FromJson(jObj["Actor"] as JObject); - if (!identifier.IsValid) - return; + var text = File.ReadAllText(file); + var jObj = JObject.Parse(text); + var collection = ModCollection.Empty; + // Create collection. + if (_config.PcpSettings.CreateCollection) + { + var identifier = _actors.FromJson(jObj["Actor"] as JObject); + if (identifier.IsValid && jObj["Collection"]?.ToObject() is { } collectionName) + { + var name = $"PCP/{collectionName}"; + if (_collections.Storage.AddCollection(name, null)) + { + collection = _collections.Storage[^1]; + _collections.Editor.SetModState(collection, mod, true); - if (jObj["Collection"]?.ToObject() is not { } collectionName) - return; + // Assign collection. + if (_config.PcpSettings.AssignCollection) + { + var identifierGroup = _collections.Active.Individuals.GetGroup(identifier); + _collections.Active.SetCollection(collection, CollectionType.Individual, identifierGroup); + } + } + } + } - var name = $"PCP/{collectionName}"; - if (!_collections.Storage.AddCollection(name, null)) - return; - - var collection = _collections.Storage[^1]; - _collections.Editor.SetModState(collection, mod, true); - - var identifierGroup = _collections.Active.Individuals.GetGroup(identifier); - _collections.Active.SetCollection(collection, CollectionType.Individual, identifierGroup); + // Move to folder. if (_fileSystem.TryGetValue(mod, out var leaf)) { try { - var folder = _fileSystem.FindOrCreateAllFolders(_config.PcpFolderName); + var folder = _fileSystem.FindOrCreateAllFolders(_config.PcpSettings.FolderName); _fileSystem.Move(leaf, folder); } catch @@ -138,7 +146,8 @@ public class PcpService : IApiService, IDisposable } } - if (_config.AllowPcpIpc) + // Invoke IPC. + if (_config.PcpSettings.AllowIpc) _communicator.PcpParsing.Invoke(jObj, mod.Identifier, collection.Identity.Id); } catch (Exception ex) @@ -208,8 +217,8 @@ public class PcpService : IApiService, IDisposable }; if (note.Length > 0) cancel.ThrowIfCancellationRequested(); - if (_config.AllowPcpIpc) - await _framework.Framework.RunOnFrameworkThread(() => _communicator.PcpCreation.Invoke(jObj, index.Index)); + if (_config.PcpSettings.AllowIpc) + await _framework.Framework.RunOnFrameworkThread(() => _communicator.PcpCreation.Invoke(jObj, index.Index, directory.FullName)); var filePath = Path.Combine(directory.FullName, "character.json"); await using var file = File.Open(filePath, File.Exists(filePath) ? FileMode.Truncate : FileMode.CreateNew); await using var stream = new StreamWriter(file); diff --git a/Penumbra/UI/Tabs/SettingsTab.cs b/Penumbra/UI/Tabs/SettingsTab.cs index a6d03593..ded56bb1 100644 --- a/Penumbra/UI/Tabs/SettingsTab.cs +++ b/Penumbra/UI/Tabs/SettingsTab.cs @@ -602,7 +602,7 @@ public class SettingsTab : ITab, IUiService _config.AlwaysOpenDefaultImport, v => _config.AlwaysOpenDefaultImport = v); Checkbox("Handle PCP Files", "When encountering specific mods, usually but not necessarily denoted by a .pcp file ending, Penumbra will automatically try to create an associated collection and assign it to a specific character for this mod package. This can turn this behaviour off if unwanted.", - !_config.DisablePcpHandling, v => _config.DisablePcpHandling = !v); + !_config.PcpSettings.DisableHandling, v => _config.PcpSettings.DisableHandling = !v); var active = _config.DeleteModModifier.IsActive(); ImGui.SameLine(); @@ -612,14 +612,23 @@ public class SettingsTab : ITab, IUiService ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteModModifier} while clicking."); ImGui.SameLine(); - if (ImUtf8.ButtonEx("Delete all PCP Collections"u8, "Deletes all collections whose name starts with 'PCP/' from the collection list."u8, disabled: !active)) + if (ImUtf8.ButtonEx("Delete all PCP Collections"u8, "Deletes all collections whose name starts with 'PCP/' from the collection list."u8, + disabled: !active)) _pcpService.CleanPcpCollections(); if (!active) ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteModModifier} while clicking."); Checkbox("Allow Other Plugins Access to PCP Handling", "When creating or importing PCP files, other plugins can add and interpret their own data to the character.json file.", - _config.AllowPcpIpc, v => _config.AllowPcpIpc = v); + _config.PcpSettings.AllowIpc, v => _config.PcpSettings.AllowIpc = v); + + Checkbox("Create PCP Collections", + "When importing PCP files, create the associated collection.", + _config.PcpSettings.CreateCollection, v => _config.PcpSettings.CreateCollection = v); + + Checkbox("Assign PCP Collections", + "When importing PCP files and creating the associated collection, assign it to the associated character.", + _config.PcpSettings.AssignCollection, v => _config.PcpSettings.AssignCollection = v); DrawDefaultModImportPath(); DrawDefaultModAuthor(); DrawDefaultModImportFolder(); @@ -736,10 +745,10 @@ public class SettingsTab : ITab, IUiService /// Draw input for the default folder to sort put newly imported mods into. private void DrawPcpFolder() { - var tmp = _config.PcpFolderName; + var tmp = _config.PcpSettings.FolderName; ImGui.SetNextItemWidth(UiHelpers.InputTextWidth.X); if (ImUtf8.InputText("##pcpFolder"u8, ref tmp)) - _config.PcpFolderName = tmp; + _config.PcpSettings.FolderName = tmp; if (ImGui.IsItemDeactivatedAfterEdit()) _config.Save();