From 173b4d7306b662ccbfae466ac50d488ebf0c700e Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Tue, 12 Dec 2023 21:05:08 +0100 Subject: [PATCH 01/14] Respect ascii setting for group names. --- Penumbra/Mods/Editor/DuplicateManager.cs | 6 ++-- Penumbra/Mods/Manager/ModMigration.cs | 4 +-- Penumbra/Mods/Manager/ModOptionEditor.cs | 44 +++++++++++++----------- Penumbra/Mods/ModCreator.cs | 29 +++++++++------- Penumbra/Mods/Subclasses/IModGroup.cs | 18 ++++++---- Penumbra/Mods/TemporaryMod.cs | 2 +- Penumbra/Services/FilenameService.cs | 8 ++--- Penumbra/Services/SaveService.cs | 6 ++-- Penumbra/UI/Changelog.cs | 3 +- Penumbra/UI/ModsTab/ModPanelEditTab.cs | 8 ++--- 10 files changed, 70 insertions(+), 58 deletions(-) diff --git a/Penumbra/Mods/Editor/DuplicateManager.cs b/Penumbra/Mods/Editor/DuplicateManager.cs index 7df0389e..47c34ce5 100644 --- a/Penumbra/Mods/Editor/DuplicateManager.cs +++ b/Penumbra/Mods/Editor/DuplicateManager.cs @@ -7,15 +7,17 @@ namespace Penumbra.Mods.Editor; public class DuplicateManager { + private readonly Configuration _config; private readonly SaveService _saveService; private readonly ModManager _modManager; private readonly SHA256 _hasher = SHA256.Create(); private readonly List<(FullPath[] Paths, long Size, byte[] Hash)> _duplicates = new(); - public DuplicateManager(ModManager modManager, SaveService saveService) + public DuplicateManager(ModManager modManager, SaveService saveService, Configuration config) { _modManager = modManager; _saveService = saveService; + _config = config; } public IReadOnlyList<(FullPath[] Paths, long Size, byte[] Hash)> Duplicates @@ -82,7 +84,7 @@ public class DuplicateManager { var sub = (SubMod)subMod; sub.FileData = dict; - _saveService.ImmediateSaveSync(new ModSaveGroup(mod, groupIdx)); + _saveService.ImmediateSaveSync(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); } } diff --git a/Penumbra/Mods/Manager/ModMigration.cs b/Penumbra/Mods/Manager/ModMigration.cs index 452da366..8b73cae5 100644 --- a/Penumbra/Mods/Manager/ModMigration.cs +++ b/Penumbra/Mods/Manager/ModMigration.cs @@ -83,7 +83,7 @@ public static partial class ModMigration creator.IncorporateMetaChanges(mod.Default, mod.ModPath, true); foreach (var (_, index) in mod.Groups.WithIndex()) - saveService.ImmediateSave(new ModSaveGroup(mod, index)); + saveService.ImmediateSave(new ModSaveGroup(mod, index, creator.Config.ReplaceNonAsciiOnImport)); // Delete meta files. foreach (var file in seenMetaFiles.Where(f => f.Exists)) @@ -111,7 +111,7 @@ public static partial class ModMigration } fileVersion = 1; - saveService.ImmediateSave(new ModSaveGroup(mod, -1)); + saveService.ImmediateSave(new ModSaveGroup(mod, -1, creator.Config.ReplaceNonAsciiOnImport)); return true; } diff --git a/Penumbra/Mods/Manager/ModOptionEditor.cs b/Penumbra/Mods/Manager/ModOptionEditor.cs index 0a3034fc..73cb80cc 100644 --- a/Penumbra/Mods/Manager/ModOptionEditor.cs +++ b/Penumbra/Mods/Manager/ModOptionEditor.cs @@ -33,13 +33,15 @@ public enum ModOptionChangeType public class ModOptionEditor { + private readonly Configuration _config; private readonly CommunicatorService _communicator; private readonly SaveService _saveService; - public ModOptionEditor(CommunicatorService communicator, SaveService saveService) + public ModOptionEditor(CommunicatorService communicator, SaveService saveService, Configuration config) { _communicator = communicator; _saveService = saveService; + _config = config; } /// Change the type of a group given by mod and index to type, if possible. @@ -50,7 +52,7 @@ public class ModOptionEditor return; mod.Groups[groupIdx] = group.Convert(type); - _saveService.QueueSave(new ModSaveGroup(mod, groupIdx)); + _saveService.QueueSave(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupTypeChanged, mod, groupIdx, -1, -1); } @@ -62,7 +64,7 @@ public class ModOptionEditor return; group.DefaultSettings = defaultOption; - _saveService.QueueSave(new ModSaveGroup(mod, groupIdx)); + _saveService.QueueSave(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.DefaultOptionChanged, mod, groupIdx, -1, -1); } @@ -74,7 +76,7 @@ public class ModOptionEditor if (oldName == newName || !VerifyFileName(mod, group, newName, true)) return; - _saveService.ImmediateDelete(new ModSaveGroup(mod, groupIdx)); + _saveService.ImmediateDelete(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); var _ = group switch { SingleModGroup s => s.Name = newName, @@ -82,7 +84,7 @@ public class ModOptionEditor _ => newName, }; - _saveService.ImmediateSave(new ModSaveGroup(mod, groupIdx)); + _saveService.ImmediateSave(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupRenamed, mod, groupIdx, -1, -1); } @@ -105,7 +107,7 @@ public class ModOptionEditor Name = newName, Priority = maxPriority, }); - _saveService.ImmediateSave(new ModSaveGroup(mod, mod.Groups.Count - 1)); + _saveService.ImmediateSave(new ModSaveGroup(mod, mod.Groups.Count - 1, _config.ReplaceNonAsciiOnImport)); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupAdded, mod, mod.Groups.Count - 1, -1, -1); } @@ -129,7 +131,7 @@ public class ModOptionEditor _communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, groupIdx, -1, -1); mod.Groups.RemoveAt(groupIdx); UpdateSubModPositions(mod, groupIdx); - _saveService.SaveAllOptionGroups(mod, false); + _saveService.SaveAllOptionGroups(mod, false, _config.ReplaceNonAsciiOnImport); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupDeleted, mod, groupIdx, -1, -1); } @@ -140,7 +142,7 @@ public class ModOptionEditor return; UpdateSubModPositions(mod, Math.Min(groupIdxFrom, groupIdxTo)); - _saveService.SaveAllOptionGroups(mod, false); + _saveService.SaveAllOptionGroups(mod, false, _config.ReplaceNonAsciiOnImport); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupMoved, mod, groupIdxFrom, -1, groupIdxTo); } @@ -157,7 +159,7 @@ public class ModOptionEditor MultiModGroup m => m.Description = newDescription, _ => newDescription, }; - _saveService.QueueSave(new ModSaveGroup(mod, groupIdx)); + _saveService.QueueSave(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, mod, groupIdx, -1, -1); } @@ -170,7 +172,7 @@ public class ModOptionEditor return; s.Description = newDescription; - _saveService.QueueSave(new ModSaveGroup(mod, groupIdx)); + _saveService.QueueSave(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, mod, groupIdx, optionIdx, -1); } @@ -187,7 +189,7 @@ public class ModOptionEditor MultiModGroup m => m.Priority = newPriority, _ => newPriority, }; - _saveService.QueueSave(new ModSaveGroup(mod, groupIdx)); + _saveService.QueueSave(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.PriorityChanged, mod, groupIdx, -1, -1); } @@ -204,7 +206,7 @@ public class ModOptionEditor return; m.PrioritizedOptions[optionIdx] = (m.PrioritizedOptions[optionIdx].Mod, newPriority); - _saveService.QueueSave(new ModSaveGroup(mod, groupIdx)); + _saveService.QueueSave(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.PriorityChanged, mod, groupIdx, optionIdx, -1); return; } @@ -230,7 +232,7 @@ public class ModOptionEditor break; } - _saveService.QueueSave(new ModSaveGroup(mod, groupIdx)); + _saveService.QueueSave(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, mod, groupIdx, optionIdx, -1); } @@ -250,7 +252,7 @@ public class ModOptionEditor break; } - _saveService.QueueSave(new ModSaveGroup(mod, groupIdx)); + _saveService.QueueSave(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionAdded, mod, groupIdx, group.Count - 1, -1); } @@ -296,7 +298,7 @@ public class ModOptionEditor break; } - _saveService.QueueSave(new ModSaveGroup(mod, groupIdx)); + _saveService.QueueSave(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionAdded, mod, groupIdx, group.Count - 1, -1); } @@ -317,7 +319,7 @@ public class ModOptionEditor } group.UpdatePositions(optionIdx); - _saveService.QueueSave(new ModSaveGroup(mod, groupIdx)); + _saveService.QueueSave(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionDeleted, mod, groupIdx, optionIdx, -1); } @@ -328,7 +330,7 @@ public class ModOptionEditor if (!group.MoveOption(optionIdxFrom, optionIdxTo)) return; - _saveService.QueueSave(new ModSaveGroup(mod, groupIdx)); + _saveService.QueueSave(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMoved, mod, groupIdx, optionIdxFrom, optionIdxTo); } @@ -342,7 +344,7 @@ public class ModOptionEditor _communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, groupIdx, optionIdx, -1); subMod.ManipulationData.SetTo(manipulations); - _saveService.QueueSave(new ModSaveGroup(mod, groupIdx)); + _saveService.QueueSave(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, mod, groupIdx, optionIdx, -1); } @@ -355,7 +357,7 @@ public class ModOptionEditor _communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, groupIdx, optionIdx, -1); subMod.FileData.SetTo(replacements); - _saveService.QueueSave(new ModSaveGroup(mod, groupIdx)); + _saveService.QueueSave(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionFilesChanged, mod, groupIdx, optionIdx, -1); } @@ -367,7 +369,7 @@ public class ModOptionEditor subMod.FileData.AddFrom(additions); if (oldCount != subMod.FileData.Count) { - _saveService.QueueSave(new ModSaveGroup(mod, groupIdx)); + _saveService.QueueSave(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionFilesAdded, mod, groupIdx, optionIdx, -1); } } @@ -381,7 +383,7 @@ public class ModOptionEditor _communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, groupIdx, optionIdx, -1); subMod.FileSwapData.SetTo(swaps); - _saveService.QueueSave(new ModSaveGroup(mod, groupIdx)); + _saveService.QueueSave(new ModSaveGroup(mod, groupIdx, _config.ReplaceNonAsciiOnImport)); _communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionSwapsChanged, mod, groupIdx, optionIdx, -1); } diff --git a/Penumbra/Mods/ModCreator.cs b/Penumbra/Mods/ModCreator.cs index 31fa64ab..9a31cf46 100644 --- a/Penumbra/Mods/ModCreator.cs +++ b/Penumbra/Mods/ModCreator.cs @@ -16,16 +16,18 @@ using Penumbra.String.Classes; namespace Penumbra.Mods; -public partial class ModCreator(SaveService _saveService, Configuration _config, ModDataEditor _dataEditor, MetaFileManager _metaFileManager, +public partial class ModCreator(SaveService _saveService, Configuration config, ModDataEditor _dataEditor, MetaFileManager _metaFileManager, IGamePathParser _gamePathParser) { + public readonly Configuration Config = config; + /// Creates directory and files necessary for a new mod without adding it to the manager. public DirectoryInfo? CreateEmptyMod(DirectoryInfo basePath, string newName, string description = "") { try { - var newDir = CreateModFolder(basePath, newName, _config.ReplaceNonAsciiOnImport, true); - _dataEditor.CreateMeta(newDir, newName, _config.DefaultModAuthor, description, "1.0", string.Empty); + var newDir = CreateModFolder(basePath, newName, Config.ReplaceNonAsciiOnImport, true); + _dataEditor.CreateMeta(newDir, newName, Config.DefaultModAuthor, description, "1.0", string.Empty); CreateDefaultFiles(newDir); return newDir; } @@ -86,7 +88,8 @@ public partial class ModCreator(SaveService _saveService, Configuration _config, if (group != null && mod.Groups.All(g => g.Name != group.Name)) { changes = changes - || _saveService.FileNames.OptionGroupFile(mod.ModPath.FullName, mod.Groups.Count, group.Name) != file.FullName; + || _saveService.FileNames.OptionGroupFile(mod.ModPath.FullName, mod.Groups.Count, group.Name, true) + != Path.Combine(file.DirectoryName!, ReplaceBadXivSymbols(file.Name, true)); mod.Groups.Add(group); } else @@ -96,13 +99,13 @@ public partial class ModCreator(SaveService _saveService, Configuration _config, } if (changes) - _saveService.SaveAllOptionGroups(mod, true); + _saveService.SaveAllOptionGroups(mod, true, Config.ReplaceNonAsciiOnImport); } /// Load the default option for a given mod. public void LoadDefaultOption(Mod mod) { - var defaultFile = _saveService.FileNames.OptionGroupFile(mod, -1); + var defaultFile = _saveService.FileNames.OptionGroupFile(mod, -1, Config.ReplaceNonAsciiOnImport); mod.Default.SetPosition(-1, 0); try { @@ -161,8 +164,8 @@ public partial class ModCreator(SaveService _saveService, Configuration _config, if (!changes) return; - _saveService.SaveAllOptionGroups(mod, false); - _saveService.ImmediateSaveSync(new ModSaveGroup(mod.ModPath, mod.Default)); + _saveService.SaveAllOptionGroups(mod, false, Config.ReplaceNonAsciiOnImport); + _saveService.ImmediateSaveSync(new ModSaveGroup(mod.ModPath, mod.Default, Config.ReplaceNonAsciiOnImport)); } @@ -188,7 +191,7 @@ public partial class ModCreator(SaveService _saveService, Configuration _config, continue; var meta = new TexToolsMeta(_metaFileManager, _gamePathParser, File.ReadAllBytes(file.FullName), - _config.KeepDefaultMetaChanges); + Config.KeepDefaultMetaChanges); Penumbra.Log.Verbose( $"Incorporating {file} as Metadata file of {meta.MetaManipulations.Count} manipulations {deleteString}"); deleteList.Add(file.FullName); @@ -201,7 +204,7 @@ public partial class ModCreator(SaveService _saveService, Configuration _config, continue; var rgsp = TexToolsMeta.FromRgspFile(_metaFileManager, file.FullName, File.ReadAllBytes(file.FullName), - _config.KeepDefaultMetaChanges); + Config.KeepDefaultMetaChanges); Penumbra.Log.Verbose( $"Incorporating {file} as racial scaling file of {rgsp.MetaManipulations.Count} manipulations {deleteString}"); deleteList.Add(file.FullName); @@ -246,7 +249,7 @@ public partial class ModCreator(SaveService _saveService, Configuration _config, DefaultSettings = defaultSettings, }; group.PrioritizedOptions.AddRange(subMods.OfType().Select((s, idx) => (s, idx))); - _saveService.ImmediateSaveSync(new ModSaveGroup(baseFolder, group, index)); + _saveService.ImmediateSaveSync(new ModSaveGroup(baseFolder, group, index, Config.ReplaceNonAsciiOnImport)); break; } case GroupType.Single: @@ -259,7 +262,7 @@ public partial class ModCreator(SaveService _saveService, Configuration _config, DefaultSettings = defaultSettings, }; group.OptionData.AddRange(subMods.OfType()); - _saveService.ImmediateSaveSync(new ModSaveGroup(baseFolder, group, index)); + _saveService.ImmediateSaveSync(new ModSaveGroup(baseFolder, group, index, Config.ReplaceNonAsciiOnImport)); break; } } @@ -306,7 +309,7 @@ public partial class ModCreator(SaveService _saveService, Configuration _config, } IncorporateMetaChanges(mod.Default, directory, true); - _saveService.ImmediateSaveSync(new ModSaveGroup(mod, -1)); + _saveService.ImmediateSaveSync(new ModSaveGroup(mod, -1, Config.ReplaceNonAsciiOnImport)); } /// Return the name of a new valid directory based on the base directory and the given name. diff --git a/Penumbra/Mods/Subclasses/IModGroup.cs b/Penumbra/Mods/Subclasses/IModGroup.cs index 957fe21d..ea5f176c 100644 --- a/Penumbra/Mods/Subclasses/IModGroup.cs +++ b/Penumbra/Mods/Subclasses/IModGroup.cs @@ -39,8 +39,9 @@ public readonly struct ModSaveGroup : ISavable private readonly IModGroup? _group; private readonly int _groupIdx; private readonly ISubMod? _defaultMod; + private readonly bool _onlyAscii; - public ModSaveGroup(Mod mod, int groupIdx) + public ModSaveGroup(Mod mod, int groupIdx, bool onlyAscii) { _basePath = mod.ModPath; _groupIdx = groupIdx; @@ -48,24 +49,27 @@ public readonly struct ModSaveGroup : ISavable _defaultMod = mod.Default; else _group = mod.Groups[_groupIdx]; + _onlyAscii = onlyAscii; } - public ModSaveGroup(DirectoryInfo basePath, IModGroup group, int groupIdx) + public ModSaveGroup(DirectoryInfo basePath, IModGroup group, int groupIdx, bool onlyAscii) { - _basePath = basePath; - _group = group; - _groupIdx = groupIdx; + _basePath = basePath; + _group = group; + _groupIdx = groupIdx; + _onlyAscii = onlyAscii; } - public ModSaveGroup(DirectoryInfo basePath, ISubMod @default) + public ModSaveGroup(DirectoryInfo basePath, ISubMod @default, bool onlyAscii) { _basePath = basePath; _groupIdx = -1; _defaultMod = @default; + _onlyAscii = onlyAscii; } public string ToFilename(FilenameService fileNames) - => fileNames.OptionGroupFile(_basePath.FullName, _groupIdx, _group?.Name ?? string.Empty); + => fileNames.OptionGroupFile(_basePath.FullName, _groupIdx, _group?.Name ?? string.Empty, _onlyAscii); public void Save(StreamWriter writer) { diff --git a/Penumbra/Mods/TemporaryMod.cs b/Penumbra/Mods/TemporaryMod.cs index dc73b451..52159258 100644 --- a/Penumbra/Mods/TemporaryMod.cs +++ b/Penumbra/Mods/TemporaryMod.cs @@ -86,7 +86,7 @@ public class TemporaryMod : IMod foreach (var manip in collection.MetaCache?.Manipulations ?? Array.Empty()) defaultMod.ManipulationData.Add(manip); - saveService.ImmediateSave(new ModSaveGroup(dir, defaultMod)); + saveService.ImmediateSave(new ModSaveGroup(dir, defaultMod, config.ReplaceNonAsciiOnImport)); modManager.AddMod(dir); Penumbra.Log.Information($"Successfully generated mod {mod.Name} at {mod.ModPath.FullName} for collection {collection.Name}."); } diff --git a/Penumbra/Services/FilenameService.cs b/Penumbra/Services/FilenameService.cs index cf99c6c8..52881b9e 100644 --- a/Penumbra/Services/FilenameService.cs +++ b/Penumbra/Services/FilenameService.cs @@ -60,14 +60,14 @@ public class FilenameService(DalamudPluginInterface pi) => Path.Combine(modDirectory, "meta.json"); /// Obtain the path of the file describing a given option group by its index and the mod. If the index is < 0, return the path for the default mod file. - public string OptionGroupFile(Mod mod, int index) - => OptionGroupFile(mod.ModPath.FullName, index, index >= 0 ? mod.Groups[index].Name : string.Empty); + public string OptionGroupFile(Mod mod, int index, bool onlyAscii) + => OptionGroupFile(mod.ModPath.FullName, index, index >= 0 ? mod.Groups[index].Name : string.Empty, onlyAscii); /// Obtain the path of the file describing a given option group by its index, name and basepath. If the index is < 0, return the path for the default mod file. - public string OptionGroupFile(string basePath, int index, string name) + public string OptionGroupFile(string basePath, int index, string name, bool onlyAscii) { var fileName = index >= 0 - ? $"group_{index + 1:D3}_{name.RemoveInvalidPathSymbols().ToLowerInvariant()}.json" + ? $"group_{index + 1:D3}_{ModCreator.ReplaceBadXivSymbols(name.ToLowerInvariant(), onlyAscii)}.json" : "default_mod.json"; return Path.Combine(basePath, fileName); } diff --git a/Penumbra/Services/SaveService.cs b/Penumbra/Services/SaveService.cs index 3f54160d..40dc4107 100644 --- a/Penumbra/Services/SaveService.cs +++ b/Penumbra/Services/SaveService.cs @@ -18,7 +18,7 @@ public sealed class SaveService : SaveServiceBase { } /// Immediately delete all existing option group files for a mod and save them anew. - public void SaveAllOptionGroups(Mod mod, bool backup) + public void SaveAllOptionGroups(Mod mod, bool backup, bool onlyAscii) { foreach (var file in FileNames.GetOptionGroupFiles(mod)) { @@ -37,7 +37,7 @@ public sealed class SaveService : SaveServiceBase } for (var i = 0; i < mod.Groups.Count - 1; ++i) - ImmediateSave(new ModSaveGroup(mod, i)); - ImmediateSaveSync(new ModSaveGroup(mod, mod.Groups.Count - 1)); + ImmediateSave(new ModSaveGroup(mod, i, onlyAscii)); + ImmediateSaveSync(new ModSaveGroup(mod, mod.Groups.Count - 1, onlyAscii)); } } diff --git a/Penumbra/UI/Changelog.cs b/Penumbra/UI/Changelog.cs index d172cfb9..d5b77bf4 100644 --- a/Penumbra/UI/Changelog.cs +++ b/Penumbra/UI/Changelog.cs @@ -53,7 +53,8 @@ public class PenumbraChangelog private static void Add8_3_0(Changelog log) => log.NextVersion("Version 0.8.3.0") .RegisterHighlight("Improved the UI for the On-Screen tabs with highlighting of used paths, filtering and more selections. (by Ny)") - .RegisterEntry("Added an option to replace non-ASCII symbols with underscores for folder paths on mod import since this causes problems on some WINE systems. This option is off by default.") + .RegisterEntry( + "Added an option to replace non-ASCII symbols with underscores for folder paths on mod import since this causes problems on some WINE systems. This option is off by default.") .RegisterEntry( "Added support for the Changed Item Icons to load modded icons, but this depends on a not-yet-released Dalamud update.") .RegisterEntry( diff --git a/Penumbra/UI/ModsTab/ModPanelEditTab.cs b/Penumbra/UI/ModsTab/ModPanelEditTab.cs index 18d0e613..ed7a6a67 100644 --- a/Penumbra/UI/ModsTab/ModPanelEditTab.cs +++ b/Penumbra/UI/ModsTab/ModPanelEditTab.cs @@ -86,7 +86,7 @@ public class ModPanelEditTab : ITab _modManager.DataEditor.ChangeModTag(_mod, tagIdx, editedTag); UiHelpers.DefaultLineSpace(); - AddOptionGroup.Draw(_filenames, _modManager, _mod); + AddOptionGroup.Draw(_filenames, _modManager, _mod, _config.ReplaceNonAsciiOnImport); UiHelpers.DefaultLineSpace(); for (var groupIdx = 0; groupIdx < _mod.Groups.Count; ++groupIdx) @@ -235,13 +235,13 @@ public class ModPanelEditTab : ITab public static void Reset() => _newGroupName = string.Empty; - public static void Draw(FilenameService filenames, ModManager modManager, Mod mod) + public static void Draw(FilenameService filenames, ModManager modManager, Mod mod, bool onlyAscii) { using var spacing = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(UiHelpers.ScaleX3)); ImGui.SetNextItemWidth(UiHelpers.InputTextMinusButton3); ImGui.InputTextWithHint("##newGroup", "Add new option group...", ref _newGroupName, 256); ImGui.SameLine(); - var defaultFile = filenames.OptionGroupFile(mod, -1); + var defaultFile = filenames.OptionGroupFile(mod, -1, onlyAscii); var fileExists = File.Exists(defaultFile); var tt = fileExists ? "Open the default option json file in the text editor of your choice." @@ -438,7 +438,7 @@ public class ModPanelEditTab : ITab _delayedActions.Enqueue(() => DescriptionEdit.OpenPopup(_mod, groupIdx)); ImGui.SameLine(); - var fileName = _filenames.OptionGroupFile(_mod, groupIdx); + var fileName = _filenames.OptionGroupFile(_mod, groupIdx, _config.ReplaceNonAsciiOnImport); var fileExists = File.Exists(fileName); tt = fileExists ? $"Open the {group.Name} json file in the text editor of your choice." From 0514e72d47a271892d9533f7e02eba4c3a076475 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Wed, 13 Dec 2023 20:47:18 +0100 Subject: [PATCH 02/14] Update sizing for option groups. --- Penumbra/UI/ModsTab/ModPanelEditTab.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Penumbra/UI/ModsTab/ModPanelEditTab.cs b/Penumbra/UI/ModsTab/ModPanelEditTab.cs index ed7a6a67..20da8fde 100644 --- a/Penumbra/UI/ModsTab/ModPanelEditTab.cs +++ b/Penumbra/UI/ModsTab/ModPanelEditTab.cs @@ -475,10 +475,11 @@ public class ModPanelEditTab : ITab if (!table) return; - ImGui.TableSetupColumn("idx", ImGuiTableColumnFlags.WidthFixed, 60 * UiHelpers.Scale); + var maxWidth = ImGui.CalcTextSize("Option #88.").X; + ImGui.TableSetupColumn("idx", ImGuiTableColumnFlags.WidthFixed, maxWidth); ImGui.TableSetupColumn("default", ImGuiTableColumnFlags.WidthFixed, ImGui.GetFrameHeight()); ImGui.TableSetupColumn("name", ImGuiTableColumnFlags.WidthFixed, - UiHelpers.InputTextWidth.X - 72 * UiHelpers.Scale - ImGui.GetFrameHeight() - UiHelpers.IconButtonSize.X); + UiHelpers.InputTextWidth.X - maxWidth - 12 * UiHelpers.Scale - ImGui.GetFrameHeight() - UiHelpers.IconButtonSize.X); ImGui.TableSetupColumn("description", ImGuiTableColumnFlags.WidthFixed, UiHelpers.IconButtonSize.X); ImGui.TableSetupColumn("delete", ImGuiTableColumnFlags.WidthFixed, UiHelpers.IconButtonSize.X); ImGui.TableSetupColumn("priority", ImGuiTableColumnFlags.WidthFixed, 50 * UiHelpers.Scale); @@ -644,7 +645,7 @@ public class ModPanelEditTab : ITab _ => "Unknown", }; - ImGui.SetNextItemWidth(UiHelpers.InputTextWidth.X - 3 * (UiHelpers.IconButtonSize.X - 4 * UiHelpers.Scale)); + ImGui.SetNextItemWidth(UiHelpers.InputTextWidth.X - 2 * UiHelpers.IconButtonSize.X - 2 * ImGui.GetStyle().ItemSpacing.X); using var combo = ImRaii.Combo("##GroupType", GroupTypeName(group.Type)); if (!combo) return; From 330525048213693cf4d2ad578d2795619537b618 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Thu, 14 Dec 2023 14:24:05 +0100 Subject: [PATCH 03/14] Misc. --- OtterGui | 2 +- Penumbra/Services/BackupService.cs | 6 +----- Penumbra/Services/ValidityChecker.cs | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/OtterGui b/OtterGui index 7098e957..5f0eec50 160000 --- a/OtterGui +++ b/OtterGui @@ -1 +1 @@ -Subproject commit 7098e9577117a3555f5f6181edae6cd306a4b5d4 +Subproject commit 5f0eec50ea7f7a4727ceab056bc3756f0ed58a30 diff --git a/Penumbra/Services/BackupService.cs b/Penumbra/Services/BackupService.cs index 7b8ace29..0059cf9f 100644 --- a/Penumbra/Services/BackupService.cs +++ b/Penumbra/Services/BackupService.cs @@ -14,11 +14,7 @@ public class BackupService Backup.CreateAutomaticBackup(logger, new DirectoryInfo(fileNames.ConfigDirectory), files); } - public static void CreatePermanentBackup(FilenameService fileNames) - => Backup.CreatePermanentBackup(Penumbra.Log, new DirectoryInfo(fileNames.ConfigDirectory), PenumbraFiles(fileNames), - "pre_ephemeral_config"); - - // Collect all relevant files for penumbra configuration. + /// Collect all relevant files for penumbra configuration. private static IReadOnlyList PenumbraFiles(FilenameService fileNames) { var list = fileNames.CollectionFiles.ToList(); diff --git a/Penumbra/Services/ValidityChecker.cs b/Penumbra/Services/ValidityChecker.cs index 749da5b9..0688850b 100644 --- a/Penumbra/Services/ValidityChecker.cs +++ b/Penumbra/Services/ValidityChecker.cs @@ -26,7 +26,7 @@ public class ValidityChecker IsNotInstalledPenumbra = CheckIsNotInstalled(pi); IsValidSourceRepo = CheckSourceRepo(pi); - var assembly = Assembly.GetExecutingAssembly(); + var assembly = GetType().Assembly; Version = assembly.GetName().Version?.ToString() ?? string.Empty; CommitHash = assembly.GetCustomAttribute()?.InformationalVersion ?? "Unknown"; } From 7d612df95156841dcfe0c780be246a9227a98a22 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sun, 17 Dec 2023 11:51:24 +0100 Subject: [PATCH 04/14] Update for changed GameData. --- OtterGui | 2 +- Penumbra.GameData | 2 +- Penumbra/Api/PenumbraApi.cs | 18 ++-- Penumbra/Collections/Cache/CollectionCache.cs | 24 ++--- .../Cache/CollectionCacheManager.cs | 4 +- .../Collections/Manager/ActiveCollections.cs | 10 +- .../Manager/IndividualCollections.Access.cs | 33 +++---- .../Manager/IndividualCollections.Files.cs | 40 ++++---- .../Manager/IndividualCollections.cs | 81 +++++++--------- .../Manager/TempCollectionManager.cs | 8 +- Penumbra/CommandHandler.cs | 4 +- Penumbra/Import/Structs/MetaFileInfo.cs | 3 +- Penumbra/Import/TexToolsMeta.cs | 3 +- .../PathResolving/CollectionResolver.cs | 16 ++-- .../Interop/PathResolving/CutsceneService.cs | 5 +- .../Interop/ResourceTree/ResolveContext.cs | 26 +++-- .../ResourceTree/ResourceTreeFactory.cs | 32 ++++--- .../Interop/ResourceTree/TreeBuildCache.cs | 40 +++----- Penumbra/Interop/Services/RedrawService.cs | 2 +- Penumbra/Meta/MetaFileManager.cs | 5 +- Penumbra/Mods/ItemSwap/EquipmentSwap.cs | 14 +-- Penumbra/Mods/ItemSwap/ItemSwapContainer.cs | 19 ++-- Penumbra/Mods/Manager/ModCacheManager.cs | 31 +++--- Penumbra/Mods/ModCreator.cs | 4 +- Penumbra/Penumbra.cs | 26 ++--- Penumbra/Services/BackupService.cs | 4 +- Penumbra/Services/ServiceManager.cs | 40 ++++++-- Penumbra/Services/ServiceWrapper.cs | 30 +----- Penumbra/Services/StainService.cs | 11 +-- Penumbra/Services/Wrappers.cs | 34 ------- Penumbra/UI/AdvancedWindow/ItemSwapTab.cs | 32 +++---- .../ModEditWindow.ShaderPackages.cs | 1 + .../AdvancedWindow/ModEditWindow.ShpkTab.cs | 1 + Penumbra/UI/CollectionTab/CollectionPanel.cs | 8 +- .../CollectionTab/IndividualAssignmentUi.cs | 61 ++++++------ .../UI/ResourceWatcher/ResourceWatcher.cs | 16 ++-- Penumbra/UI/Tabs/CollectionsTab.cs | 5 +- Penumbra/UI/Tabs/ConfigTabBar.cs | 1 + Penumbra/UI/Tabs/{ => Debug}/DebugTab.cs | 95 ++++++++++++------- Penumbra/UI/WindowSystem.cs | 2 +- Penumbra/Util/PerformanceType.cs | 33 ------- Penumbra/packages.lock.json | 3 +- 42 files changed, 374 insertions(+), 455 deletions(-) delete mode 100644 Penumbra/Services/Wrappers.cs rename Penumbra/UI/Tabs/{ => Debug}/DebugTab.cs (94%) diff --git a/OtterGui b/OtterGui index 5f0eec50..bde59c34 160000 --- a/OtterGui +++ b/OtterGui @@ -1 +1 @@ -Subproject commit 5f0eec50ea7f7a4727ceab056bc3756f0ed58a30 +Subproject commit bde59c34f7108520002c21cdbf21e8ee5b586944 diff --git a/Penumbra.GameData b/Penumbra.GameData index ffdb966f..afc56d9f 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit ffdb966fec5a657893289e655c641ceb3af1d59f +Subproject commit afc56d9f07a2a54ab791a4c85cf627b6f884aec2 diff --git a/Penumbra/Api/PenumbraApi.cs b/Penumbra/Api/PenumbraApi.cs index 5ce28510..97e69089 100644 --- a/Penumbra/Api/PenumbraApi.cs +++ b/Penumbra/Api/PenumbraApi.cs @@ -95,7 +95,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi private DalamudServices _dalamud; private TempCollectionManager _tempCollections; private TempModManager _tempMods; - private ActorService _actors; + private ActorManager _actors; private CollectionResolver _collectionResolver; private CutsceneService _cutsceneService; private ModImportManager _modImportManager; @@ -108,7 +108,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi public unsafe PenumbraApi(CommunicatorService communicator, ModManager modManager, ResourceLoader resourceLoader, Configuration config, CollectionManager collectionManager, DalamudServices dalamud, TempCollectionManager tempCollections, - TempModManager tempMods, ActorService actors, CollectionResolver collectionResolver, CutsceneService cutsceneService, + TempModManager tempMods, ActorManager actors, CollectionResolver collectionResolver, CutsceneService cutsceneService, ModImportManager modImportManager, CollectionEditor collectionEditor, RedrawService redrawService, ModFileSystem modFileSystem, ConfigWindow configWindow, TextureManager textureManager, ResourceTreeFactory resourceTreeFactory) { @@ -889,13 +889,10 @@ public class PenumbraApi : IDisposable, IPenumbraApi { CheckInitialized(); - if (!_actors.Valid) - return PenumbraApiEc.SystemDisposed; - if (actorIndex < 0 || actorIndex >= _dalamud.Objects.Length) return PenumbraApiEc.InvalidArgument; - var identifier = _actors.AwaitedService.FromObject(_dalamud.Objects[actorIndex], false, false, true); + var identifier = _actors.FromObject(_dalamud.Objects[actorIndex], false, false, true); if (!identifier.IsValid) return PenumbraApiEc.InvalidArgument; @@ -1143,11 +1140,11 @@ public class PenumbraApi : IDisposable, IPenumbraApi [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private unsafe ActorIdentifier AssociatedIdentifier(int gameObjectIdx) { - if (gameObjectIdx < 0 || gameObjectIdx >= _dalamud.Objects.Length || !_actors.Valid) + if (gameObjectIdx < 0 || gameObjectIdx >= _dalamud.Objects.Length) return ActorIdentifier.Invalid; var ptr = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)_dalamud.Objects.GetObjectAddress(gameObjectIdx); - return _actors.AwaitedService.FromObject(ptr, out _, false, true, true); + return _actors.FromObject(ptr, out _, false, true, true); } // Resolve a path given by string for a specific collection. @@ -1241,12 +1238,9 @@ public class PenumbraApi : IDisposable, IPenumbraApi // TODO: replace all usages with ActorIdentifier stuff when incrementing API private ActorIdentifier NameToIdentifier(string name, ushort worldId) { - if (!_actors.Valid) - return ActorIdentifier.Invalid; - // Verified to be valid name beforehand. var b = ByteString.FromStringUnsafe(name, false); - return _actors.AwaitedService.CreatePlayer(b, worldId); + return _actors.CreatePlayer(b, worldId); } private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, int _1, int _2, bool inherited) diff --git a/Penumbra/Collections/Cache/CollectionCache.cs b/Penumbra/Collections/Cache/CollectionCache.cs index 3761424a..a6e5fef5 100644 --- a/Penumbra/Collections/Cache/CollectionCache.cs +++ b/Penumbra/Collections/Cache/CollectionCache.cs @@ -22,10 +22,10 @@ public class CollectionCache : IDisposable private readonly CollectionCacheManager _manager; private readonly ModCollection _collection; public readonly CollectionModData ModData = new(); - public readonly SortedList, object?)> _changedItems = new(); + private readonly SortedList, object?)> _changedItems = []; public readonly ConcurrentDictionary ResolvedFiles = new(); public readonly MetaCache Meta; - public readonly Dictionary> _conflicts = new(); + public readonly Dictionary> ConflictDict = []; public int Calculating = -1; @@ -33,10 +33,10 @@ public class CollectionCache : IDisposable => _collection.AnonymizedName; public IEnumerable> AllConflicts - => _conflicts.Values; + => ConflictDict.Values; public SingleArray Conflicts(IMod mod) - => _conflicts.TryGetValue(mod, out var c) ? c : new SingleArray(); + => ConflictDict.TryGetValue(mod, out SingleArray c) ? c : new SingleArray(); private int _changedItemsSaveCounter = -1; @@ -195,7 +195,7 @@ public class CollectionCache : IDisposable $"Invalid mod state, removing {mod.Name} and associated manipulation {manipulation} returned current mod {mp.Name}."); } - _conflicts.Remove(mod); + ConflictDict.Remove(mod); foreach (var conflict in conflicts) { if (conflict.HasPriority) @@ -206,9 +206,9 @@ public class CollectionCache : IDisposable { var newConflicts = Conflicts(conflict.Mod2).Remove(c => c.Mod2 == mod); if (newConflicts.Count > 0) - _conflicts[conflict.Mod2] = newConflicts; + ConflictDict[conflict.Mod2] = newConflicts; else - _conflicts.Remove(conflict.Mod2); + ConflictDict.Remove(conflict.Mod2); } } @@ -336,9 +336,9 @@ public class CollectionCache : IDisposable return false; }); if (changedConflicts.Count == 0) - _conflicts.Remove(mod); + ConflictDict.Remove(mod); else - _conflicts[mod] = changedConflicts; + ConflictDict[mod] = changedConflicts; } // Add a new conflict between the added mod and the existing mod. @@ -373,9 +373,9 @@ public class CollectionCache : IDisposable { // Add the same conflict list to both conflict directions. var conflictList = new List { data }; - _conflicts[addedMod] = addedConflicts.Append(new ModConflicts(existingMod, conflictList, existingPriority < addedPriority, + ConflictDict[addedMod] = addedConflicts.Append(new ModConflicts(existingMod, conflictList, existingPriority < addedPriority, existingPriority != addedPriority)); - _conflicts[existingMod] = existingConflicts.Append(new ModConflicts(addedMod, conflictList, + ConflictDict[existingMod] = existingConflicts.Append(new ModConflicts(addedMod, conflictList, existingPriority >= addedPriority, existingPriority != addedPriority)); } @@ -426,7 +426,7 @@ public class CollectionCache : IDisposable _changedItems.Clear(); // Skip IMCs because they would result in far too many false-positive items, // since they are per set instead of per item-slot/item/variant. - var identifier = _manager.MetaFileManager.Identifier.AwaitedService; + var identifier = _manager.MetaFileManager.Identifier; var items = new SortedList(512); void AddItems(IMod mod) diff --git a/Penumbra/Collections/Cache/CollectionCacheManager.cs b/Penumbra/Collections/Cache/CollectionCacheManager.cs index a24eb2fa..7d4a5722 100644 --- a/Penumbra/Collections/Cache/CollectionCacheManager.cs +++ b/Penumbra/Collections/Cache/CollectionCacheManager.cs @@ -159,7 +159,7 @@ public class CollectionCacheManager : IDisposable null); cache.ResolvedFiles.Clear(); cache.Meta.Reset(); - cache._conflicts.Clear(); + cache.ConflictDict.Clear(); // Add all forced redirects. foreach (var tempMod in _tempMods.ModsForAllCollections @@ -372,7 +372,7 @@ public class CollectionCacheManager : IDisposable { collection._cache!.ResolvedFiles.Clear(); collection._cache!.Meta.Reset(); - collection._cache!._conflicts.Clear(); + collection._cache!.ConflictDict.Clear(); } } diff --git a/Penumbra/Collections/Manager/ActiveCollections.cs b/Penumbra/Collections/Manager/ActiveCollections.cs index 0814da90..38679612 100644 --- a/Penumbra/Collections/Manager/ActiveCollections.cs +++ b/Penumbra/Collections/Manager/ActiveCollections.cs @@ -28,9 +28,9 @@ public class ActiveCollections : ISavable, IDisposable private readonly CommunicatorService _communicator; private readonly SaveService _saveService; private readonly ActiveCollectionData _data; - private readonly ActorService _actors; + private readonly ActorManager _actors; - public ActiveCollections(Configuration config, CollectionStorage storage, ActorService actors, CommunicatorService communicator, + public ActiveCollections(Configuration config, CollectionStorage storage, ActorManager actors, CommunicatorService communicator, SaveService saveService, ActiveCollectionData data) { _storage = storage; @@ -475,7 +475,7 @@ public class ActiveCollections : ISavable, IDisposable { case IdentifierType.Player when id.HomeWorld != ushort.MaxValue: { - var global = ByType(CollectionType.Individual, _actors.AwaitedService.CreatePlayer(id.PlayerName, ushort.MaxValue)); + var global = ByType(CollectionType.Individual, _actors.CreatePlayer(id.PlayerName, ushort.MaxValue)); return global?.Index == checkAssignment.Index ? "Assignment is redundant due to an identical Any-World assignment existing.\nYou can remove it." : string.Empty; @@ -484,12 +484,12 @@ public class ActiveCollections : ISavable, IDisposable if (id.HomeWorld != ushort.MaxValue) { var global = ByType(CollectionType.Individual, - _actors.AwaitedService.CreateOwned(id.PlayerName, ushort.MaxValue, id.Kind, id.DataId)); + _actors.CreateOwned(id.PlayerName, ushort.MaxValue, id.Kind, id.DataId)); if (global?.Index == checkAssignment.Index) return "Assignment is redundant due to an identical Any-World assignment existing.\nYou can remove it."; } - var unowned = ByType(CollectionType.Individual, _actors.AwaitedService.CreateNpc(id.Kind, id.DataId)); + var unowned = ByType(CollectionType.Individual, _actors.CreateNpc(id.Kind, id.DataId)); return unowned?.Index == checkAssignment.Index ? "Assignment is redundant due to an identical unowned NPC assignment existing.\nYou can remove it." : string.Empty; diff --git a/Penumbra/Collections/Manager/IndividualCollections.Access.cs b/Penumbra/Collections/Manager/IndividualCollections.Access.cs index 78eff98c..785f0013 100644 --- a/Penumbra/Collections/Manager/IndividualCollections.Access.cs +++ b/Penumbra/Collections/Manager/IndividualCollections.Access.cs @@ -1,6 +1,7 @@ using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.Types; using Penumbra.GameData.Actors; +using Penumbra.GameData.Enums; using Penumbra.String; namespace Penumbra.Collections.Manager; @@ -36,7 +37,7 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa return true; if (identifier.Retainer is not ActorIdentifier.RetainerType.Mannequin && _config.UseOwnerNameForCharacterCollection) - return CheckWorlds(_actorService.AwaitedService.GetCurrentPlayer(), out collection); + return CheckWorlds(_actors.GetCurrentPlayer(), out collection); break; } @@ -46,7 +47,7 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa return true; // Handle generic NPC - var npcIdentifier = _actorService.AwaitedService.CreateIndividualUnchecked(IdentifierType.Npc, ByteString.Empty, + var npcIdentifier = _actors.CreateIndividualUnchecked(IdentifierType.Npc, ByteString.Empty, ushort.MaxValue, identifier.Kind, identifier.DataId); if (npcIdentifier.IsValid && _individuals.TryGetValue(npcIdentifier, out collection)) @@ -56,7 +57,7 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa if (!_config.UseOwnerNameForCharacterCollection) return false; - identifier = _actorService.AwaitedService.CreateIndividualUnchecked(IdentifierType.Player, identifier.PlayerName, + identifier = _actors.CreateIndividualUnchecked(IdentifierType.Player, identifier.PlayerName, identifier.HomeWorld.Id, ObjectKind.None, uint.MaxValue); return CheckWorlds(identifier, out collection); @@ -89,37 +90,37 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa if (identifier.Type != IdentifierType.Special) return (identifier, SpecialResult.Invalid); - if (_actorService.AwaitedService.ResolvePartyBannerPlayer(identifier.Special, out var id)) + if (_actors.ResolvePartyBannerPlayer(identifier.Special, out var id)) return _config.UseCharacterCollectionsInCards ? (id, SpecialResult.PartyBanner) : (identifier, SpecialResult.Invalid); - if (_actorService.AwaitedService.ResolvePvPBannerPlayer(identifier.Special, out id)) + if (_actors.ResolvePvPBannerPlayer(identifier.Special, out id)) return _config.UseCharacterCollectionsInCards ? (id, SpecialResult.PvPBanner) : (identifier, SpecialResult.Invalid); - if (_actorService.AwaitedService.ResolveMahjongPlayer(identifier.Special, out id)) + if (_actors.ResolveMahjongPlayer(identifier.Special, out id)) return _config.UseCharacterCollectionsInCards ? (id, SpecialResult.Mahjong) : (identifier, SpecialResult.Invalid); switch (identifier.Special) { case ScreenActor.CharacterScreen when _config.UseCharacterCollectionInMainWindow: - return (_actorService.AwaitedService.GetCurrentPlayer(), SpecialResult.CharacterScreen); + return (_actors.GetCurrentPlayer(), SpecialResult.CharacterScreen); case ScreenActor.FittingRoom when _config.UseCharacterCollectionInTryOn: - return (_actorService.AwaitedService.GetCurrentPlayer(), SpecialResult.FittingRoom); + return (_actors.GetCurrentPlayer(), SpecialResult.FittingRoom); case ScreenActor.DyePreview when _config.UseCharacterCollectionInTryOn: - return (_actorService.AwaitedService.GetCurrentPlayer(), SpecialResult.DyePreview); + return (_actors.GetCurrentPlayer(), SpecialResult.DyePreview); case ScreenActor.Portrait when _config.UseCharacterCollectionsInCards: - return (_actorService.AwaitedService.GetCurrentPlayer(), SpecialResult.Portrait); + return (_actors.GetCurrentPlayer(), SpecialResult.Portrait); case ScreenActor.ExamineScreen: { - identifier = _actorService.AwaitedService.GetInspectPlayer(); + identifier = _actors.GetInspectPlayer(); if (identifier.IsValid) return (_config.UseCharacterCollectionInInspect ? identifier : ActorIdentifier.Invalid, SpecialResult.Inspect); - identifier = _actorService.AwaitedService.GetCardPlayer(); + identifier = _actors.GetCardPlayer(); if (identifier.IsValid) return (_config.UseCharacterCollectionInInspect ? identifier : ActorIdentifier.Invalid, SpecialResult.Card); return _config.UseCharacterCollectionInTryOn - ? (_actorService.AwaitedService.GetGlamourPlayer(), SpecialResult.Glamour) + ? (_actors.GetGlamourPlayer(), SpecialResult.Glamour) : (identifier, SpecialResult.Invalid); } default: return (identifier, SpecialResult.Invalid); @@ -127,10 +128,10 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa } public bool TryGetCollection(GameObject? gameObject, out ModCollection? collection) - => TryGetCollection(_actorService.AwaitedService.FromObject(gameObject, true, false, false), out collection); + => TryGetCollection(_actors.FromObject(gameObject, true, false, false), out collection); public unsafe bool TryGetCollection(FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* gameObject, out ModCollection? collection) - => TryGetCollection(_actorService.AwaitedService.FromObject(gameObject, out _, true, false, false), out collection); + => TryGetCollection(_actors.FromObject(gameObject, out _, true, false, false), out collection); private bool CheckWorlds(ActorIdentifier identifier, out ModCollection? collection) { @@ -143,7 +144,7 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa if (_individuals.TryGetValue(identifier, out collection)) return true; - identifier = _actorService.AwaitedService.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, ushort.MaxValue, + identifier = _actors.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, ushort.MaxValue, identifier.Kind, identifier.DataId); if (identifier.IsValid && _individuals.TryGetValue(identifier, out collection)) diff --git a/Penumbra/Collections/Manager/IndividualCollections.Files.cs b/Penumbra/Collections/Manager/IndividualCollections.Files.cs index 45a1d98c..dc20da1e 100644 --- a/Penumbra/Collections/Manager/IndividualCollections.Files.cs +++ b/Penumbra/Collections/Manager/IndividualCollections.Files.cs @@ -3,6 +3,8 @@ using Dalamud.Interface.Internal.Notifications; using Newtonsoft.Json.Linq; using OtterGui.Classes; using Penumbra.GameData.Actors; +using Penumbra.GameData.DataContainers.Bases; +using Penumbra.GameData.Structs; using Penumbra.Services; using Penumbra.String; @@ -26,23 +28,20 @@ public partial class IndividualCollections public bool ReadJObject(SaveService saver, ActiveCollections parent, JArray? obj, CollectionStorage storage) { - if (_actorService.Valid) + if (_actors.Awaiter.IsCompletedSuccessfully) { var ret = ReadJObjectInternal(obj, storage); return ret; } - void Func() + Penumbra.Log.Debug("[Collections] Delayed reading individual assignments until actor service is ready..."); + _actors.Awaiter.ContinueWith(_ => { if (ReadJObjectInternal(obj, storage)) saver.ImmediateSave(parent); IsLoaded = true; Loaded.Invoke(); - _actorService.FinishedCreation -= Func; - } - - Penumbra.Log.Debug("[Collections] Delayed reading individual assignments until actor service is ready..."); - _actorService.FinishedCreation += Func; + }); return false; } @@ -60,7 +59,7 @@ public partial class IndividualCollections { try { - var identifier = _actorService.AwaitedService.FromJson(data as JObject); + var identifier = _actors.FromJson(data as JObject); var group = GetGroup(identifier); if (group.Length == 0 || group.Any(i => !i.IsValid)) { @@ -101,10 +100,10 @@ public partial class IndividualCollections internal void Migrate0To1(Dictionary old) { - static bool FindDataId(string name, IReadOnlyDictionary data, out uint dataId) + static bool FindDataId(string name, NameDictionary data, out NpcId dataId) { var kvp = data.FirstOrDefault(kvp => kvp.Value.Equals(name, StringComparison.OrdinalIgnoreCase), - new KeyValuePair(uint.MaxValue, string.Empty)); + new KeyValuePair(uint.MaxValue, string.Empty)); dataId = kvp.Key; return kvp.Value.Length > 0; } @@ -114,22 +113,22 @@ public partial class IndividualCollections var kind = ObjectKind.None; var lowerName = name.ToLowerInvariant(); // Prefer matching NPC names, fewer false positives than preferring players. - if (FindDataId(lowerName, _actorService.AwaitedService.Data.Companions, out var dataId)) + if (FindDataId(lowerName, _actors.Data.Companions, out var dataId)) kind = ObjectKind.Companion; - else if (FindDataId(lowerName, _actorService.AwaitedService.Data.Mounts, out dataId)) + else if (FindDataId(lowerName, _actors.Data.Mounts, out dataId)) kind = ObjectKind.MountType; - else if (FindDataId(lowerName, _actorService.AwaitedService.Data.BNpcs, out dataId)) + else if (FindDataId(lowerName, _actors.Data.BNpcs, out dataId)) kind = ObjectKind.BattleNpc; - else if (FindDataId(lowerName, _actorService.AwaitedService.Data.ENpcs, out dataId)) + else if (FindDataId(lowerName, _actors.Data.ENpcs, out dataId)) kind = ObjectKind.EventNpc; - var identifier = _actorService.AwaitedService.CreateNpc(kind, dataId); + var identifier = _actors.CreateNpc(kind, dataId); if (identifier.IsValid) { // If the name corresponds to a valid npc, add it as a group. If this fails, notify users. var group = GetGroup(identifier); var ids = string.Join(", ", group.Select(i => i.DataId.ToString())); - if (Add($"{_actorService.AwaitedService.Data.ToName(kind, dataId)} ({kind.ToName()})", group, collection)) + if (Add($"{_actors.Data.ToName(kind, dataId)} ({kind.ToName()})", group, collection)) Penumbra.Log.Information($"Migrated {name} ({kind.ToName()}) to NPC Identifiers [{ids}]."); else Penumbra.Messager.NotificationMessage( @@ -137,15 +136,12 @@ public partial class IndividualCollections NotificationType.Error); } // If it is not a valid NPC name, check if it can be a player name. - else if (ActorManager.VerifyPlayerName(name)) + else if (ActorIdentifierFactory.VerifyPlayerName(name)) { - identifier = _actorService.AwaitedService.CreatePlayer(ByteString.FromStringUnsafe(name, false), ushort.MaxValue); + identifier = _actors.CreatePlayer(ByteString.FromStringUnsafe(name, false), ushort.MaxValue); var shortName = string.Join(" ", name.Split().Select(n => $"{n[0]}.")); // Try to migrate the player name without logging full names. - if (Add($"{name} ({_actorService.AwaitedService.Data.ToWorldName(identifier.HomeWorld)})", new[] - { - identifier, - }, collection)) + if (Add($"{name} ({_actors.Data.ToWorldName(identifier.HomeWorld)})", [identifier], collection)) Penumbra.Log.Information($"Migrated {shortName} ({collection.AnonymizedName}) to Player Identifier."); else Penumbra.Messager.NotificationMessage( diff --git a/Penumbra/Collections/Manager/IndividualCollections.cs b/Penumbra/Collections/Manager/IndividualCollections.cs index 31695a94..67ab0b21 100644 --- a/Penumbra/Collections/Manager/IndividualCollections.cs +++ b/Penumbra/Collections/Manager/IndividualCollections.cs @@ -1,7 +1,9 @@ using Dalamud.Game.ClientState.Objects.Enums; using OtterGui.Filesystem; using Penumbra.GameData.Actors; -using Penumbra.Services; +using Penumbra.GameData.DataContainers.Bases; +using Penumbra.GameData.Enums; +using Penumbra.GameData.Structs; using Penumbra.String; namespace Penumbra.Collections.Manager; @@ -11,9 +13,9 @@ public sealed partial class IndividualCollections public record struct IndividualAssignment(string DisplayName, IReadOnlyList Identifiers, ModCollection Collection); private readonly Configuration _config; - private readonly ActorService _actorService; - private readonly Dictionary _individuals = new(); - private readonly List _assignments = new(); + private readonly ActorManager _actors; + private readonly Dictionary _individuals = []; + private readonly List _assignments = []; public event Action Loaded; public bool IsLoaded { get; private set; } @@ -21,12 +23,12 @@ public sealed partial class IndividualCollections public IReadOnlyList Assignments => _assignments; - public IndividualCollections(ActorService actorService, Configuration config, bool temporary) + public IndividualCollections(ActorManager actors, Configuration config, bool temporary) { - _config = config; - _actorService = actorService; - IsLoaded = temporary; - Loaded += () => Penumbra.Log.Information($"{_assignments.Count} Individual Assignments loaded after delay."); + _config = config; + _actors = actors; + IsLoaded = temporary; + Loaded += () => Penumbra.Log.Information($"{_assignments.Count} Individual Assignments loaded after delay."); } public enum AddResult @@ -69,44 +71,34 @@ public sealed partial class IndividualCollections return set ? AddResult.AlreadySet : AddResult.Valid; } - public AddResult CanAdd(IdentifierType type, string name, ushort homeWorld, ObjectKind kind, IEnumerable dataIds, + public AddResult CanAdd(IdentifierType type, string name, WorldId homeWorld, ObjectKind kind, IEnumerable dataIds, out ActorIdentifier[] identifiers) { - identifiers = Array.Empty(); + identifiers = []; - var manager = _actorService.AwaitedService; switch (type) { case IdentifierType.Player: if (!ByteString.FromString(name, out var playerName)) return AddResult.Invalid; - identifiers = new[] - { - manager.CreatePlayer(playerName, homeWorld), - }; + identifiers = [_actors.CreatePlayer(playerName, homeWorld)]; break; case IdentifierType.Retainer: if (!ByteString.FromString(name, out var retainerName)) return AddResult.Invalid; - identifiers = new[] - { - manager.CreateRetainer(retainerName, ActorIdentifier.RetainerType.Both), - }; + identifiers = [_actors.CreateRetainer(retainerName, ActorIdentifier.RetainerType.Both)]; break; case IdentifierType.Owned: if (!ByteString.FromString(name, out var ownerName)) return AddResult.Invalid; - identifiers = dataIds.Select(id => manager.CreateOwned(ownerName, homeWorld, kind, id)).ToArray(); + identifiers = dataIds.Select(id => _actors.CreateOwned(ownerName, homeWorld, kind, id)).ToArray(); break; case IdentifierType.Npc: identifiers = dataIds - .Select(id => manager.CreateIndividual(IdentifierType.Npc, ByteString.Empty, ushort.MaxValue, kind, id)).ToArray(); - break; - default: - identifiers = Array.Empty(); + .Select(id => _actors.CreateIndividual(IdentifierType.Npc, ByteString.Empty, ushort.MaxValue, kind, id)).ToArray(); break; } @@ -116,12 +108,22 @@ public sealed partial class IndividualCollections public ActorIdentifier[] GetGroup(ActorIdentifier identifier) { if (!identifier.IsValid) - return Array.Empty(); + return []; + + return identifier.Type switch + { + IdentifierType.Player => [identifier.CreatePermanent()], + IdentifierType.Special => [identifier], + IdentifierType.Retainer => [identifier.CreatePermanent()], + IdentifierType.Owned => CreateNpcs(_actors, identifier.CreatePermanent()), + IdentifierType.Npc => CreateNpcs(_actors, identifier), + _ => [], + }; static ActorIdentifier[] CreateNpcs(ActorManager manager, ActorIdentifier identifier) { var name = manager.Data.ToName(identifier.Kind, identifier.DataId); - var table = identifier.Kind switch + NameDictionary table = identifier.Kind switch { ObjectKind.BattleNpc => manager.Data.BNpcs, ObjectKind.EventNpc => manager.Data.ENpcs, @@ -134,25 +136,6 @@ public sealed partial class IndividualCollections .Select(kvp => manager.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, identifier.HomeWorld.Id, identifier.Kind, kvp.Key)).ToArray(); } - - return identifier.Type switch - { - IdentifierType.Player => new[] - { - identifier.CreatePermanent(), - }, - IdentifierType.Special => new[] - { - identifier, - }, - IdentifierType.Retainer => new[] - { - identifier.CreatePermanent(), - }, - IdentifierType.Owned => CreateNpcs(_actorService.AwaitedService, identifier.CreatePermanent()), - IdentifierType.Npc => CreateNpcs(_actorService.AwaitedService, identifier), - _ => Array.Empty(), - }; } internal bool Add(ActorIdentifier[] identifiers, ModCollection collection) @@ -241,12 +224,12 @@ public sealed partial class IndividualCollections { return identifier.Type switch { - IdentifierType.Player => $"{identifier.PlayerName} ({_actorService.AwaitedService.Data.ToWorldName(identifier.HomeWorld)})", + IdentifierType.Player => $"{identifier.PlayerName} ({_actors.Data.ToWorldName(identifier.HomeWorld)})", IdentifierType.Retainer => $"{identifier.PlayerName} (Retainer)", IdentifierType.Owned => - $"{identifier.PlayerName} ({_actorService.AwaitedService.Data.ToWorldName(identifier.HomeWorld)})'s {_actorService.AwaitedService.Data.ToName(identifier.Kind, identifier.DataId)}", + $"{identifier.PlayerName} ({_actors.Data.ToWorldName(identifier.HomeWorld)})'s {_actors.Data.ToName(identifier.Kind, identifier.DataId)}", IdentifierType.Npc => - $"{_actorService.AwaitedService.Data.ToName(identifier.Kind, identifier.DataId)} ({identifier.Kind.ToName()})", + $"{_actors.Data.ToName(identifier.Kind, identifier.DataId)} ({identifier.Kind.ToName()})", _ => string.Empty, }; } diff --git a/Penumbra/Collections/Manager/TempCollectionManager.cs b/Penumbra/Collections/Manager/TempCollectionManager.cs index d0edf19b..5d9de13d 100644 --- a/Penumbra/Collections/Manager/TempCollectionManager.cs +++ b/Penumbra/Collections/Manager/TempCollectionManager.cs @@ -14,10 +14,10 @@ public class TempCollectionManager : IDisposable private readonly CommunicatorService _communicator; private readonly CollectionStorage _storage; - private readonly ActorService _actors; + private readonly ActorManager _actors; private readonly Dictionary _customCollections = new(); - public TempCollectionManager(Configuration config, CommunicatorService communicator, ActorService actors, CollectionStorage storage) + public TempCollectionManager(Configuration config, CommunicatorService communicator, ActorManager actors, CollectionStorage storage) { _communicator = communicator; _actors = actors; @@ -111,7 +111,7 @@ public class TempCollectionManager : IDisposable if (!ByteString.FromString(characterName, out var byteString, false)) return false; - var identifier = _actors.AwaitedService.CreatePlayer(byteString, worldId); + var identifier = _actors.CreatePlayer(byteString, worldId); if (!identifier.IsValid) return false; @@ -123,7 +123,7 @@ public class TempCollectionManager : IDisposable if (!ByteString.FromString(characterName, out var byteString, false)) return false; - var identifier = _actors.AwaitedService.CreatePlayer(byteString, worldId); + var identifier = _actors.CreatePlayer(byteString, worldId); return Collections.TryGetValue(identifier, out var collection) && RemoveTemporaryCollection(collection.Name); } } diff --git a/Penumbra/CommandHandler.cs b/Penumbra/CommandHandler.cs index c151e7e4..537b08da 100644 --- a/Penumbra/CommandHandler.cs +++ b/Penumbra/CommandHandler.cs @@ -32,7 +32,7 @@ public class CommandHandler : IDisposable public CommandHandler(IFramework framework, ICommandManager commandManager, IChatGui chat, RedrawService redrawService, Configuration config, - ConfigWindow configWindow, ModManager modManager, CollectionManager collectionManager, ActorService actors, Penumbra penumbra, + ConfigWindow configWindow, ModManager modManager, CollectionManager collectionManager, ActorManager actors, Penumbra penumbra, CollectionEditor collectionEditor) { _commandManager = commandManager; @@ -41,7 +41,7 @@ public class CommandHandler : IDisposable _configWindow = configWindow; _modManager = modManager; _collectionManager = collectionManager; - _actors = actors.AwaitedService; + _actors = actors; _chat = chat; _penumbra = penumbra; _collectionEditor = collectionEditor; diff --git a/Penumbra/Import/Structs/MetaFileInfo.cs b/Penumbra/Import/Structs/MetaFileInfo.cs index f7c9b419..693c77b1 100644 --- a/Penumbra/Import/Structs/MetaFileInfo.cs +++ b/Penumbra/Import/Structs/MetaFileInfo.cs @@ -1,5 +1,6 @@ using Penumbra.GameData.Enums; using Penumbra.GameData; +using Penumbra.GameData.Data; namespace Penumbra.Import.Structs; @@ -47,7 +48,7 @@ public partial struct MetaFileInfo _ => false, }; - public MetaFileInfo(IGamePathParser parser, string fileName) + public MetaFileInfo(GamePathParser parser, string fileName) { // Set the primary type from the gamePath start. PrimaryType = parser.PathToObjectType(fileName); diff --git a/Penumbra/Import/TexToolsMeta.cs b/Penumbra/Import/TexToolsMeta.cs index 1108c965..83b430fb 100644 --- a/Penumbra/Import/TexToolsMeta.cs +++ b/Penumbra/Import/TexToolsMeta.cs @@ -1,4 +1,5 @@ using Penumbra.GameData; +using Penumbra.GameData.Data; using Penumbra.Import.Structs; using Penumbra.Meta; using Penumbra.Meta.Manipulations; @@ -28,7 +29,7 @@ public partial class TexToolsMeta private readonly MetaFileManager _metaFileManager; - public TexToolsMeta(MetaFileManager metaFileManager, IGamePathParser parser, byte[] data, bool keepDefault) + public TexToolsMeta(MetaFileManager metaFileManager, GamePathParser parser, byte[] data, bool keepDefault) { _metaFileManager = metaFileManager; _keepDefault = keepDefault; diff --git a/Penumbra/Interop/PathResolving/CollectionResolver.cs b/Penumbra/Interop/PathResolving/CollectionResolver.cs index ecd4eb2e..fe51a5cd 100644 --- a/Penumbra/Interop/PathResolving/CollectionResolver.cs +++ b/Penumbra/Interop/PathResolving/CollectionResolver.cs @@ -3,7 +3,7 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using Penumbra.Collections; using Penumbra.Collections.Manager; using Penumbra.GameData.Actors; -using Penumbra.GameData.Data; +using Penumbra.GameData.DataContainers; using Penumbra.GameData.Enums; using Penumbra.Services; using Penumbra.Util; @@ -21,7 +21,7 @@ public unsafe class CollectionResolver private readonly IClientState _clientState; private readonly IGameGui _gameGui; - private readonly ActorService _actors; + private readonly ActorManager _actors; private readonly CutsceneService _cutscenes; private readonly Configuration _config; @@ -30,7 +30,7 @@ public unsafe class CollectionResolver private readonly DrawObjectState _drawObjectState; public CollectionResolver(PerformanceTracker performance, IdentifiedCollectionCache cache, IClientState clientState, IGameGui gameGui, - ActorService actors, CutsceneService cutscenes, Configuration config, CollectionManager collectionManager, + ActorManager actors, CutsceneService cutscenes, Configuration config, CollectionManager collectionManager, TempCollectionManager tempCollections, DrawObjectState drawObjectState, HumanModelList humanModels) { _performance = performance; @@ -58,7 +58,7 @@ public unsafe class CollectionResolver return _collectionManager.Active.ByType(CollectionType.Yourself) ?? _collectionManager.Active.Default; - var player = _actors.AwaitedService.GetCurrentPlayer(); + var player = _actors.GetCurrentPlayer(); var _ = false; return CollectionByIdentifier(player) ?? CheckYourself(player, gameObject) @@ -147,7 +147,7 @@ public unsafe class CollectionResolver return false; } - var player = _actors.AwaitedService.GetCurrentPlayer(); + var player = _actors.GetCurrentPlayer(); var notYetReady = false; var collection = (player.IsValid ? CollectionByIdentifier(player) : null) ?? _collectionManager.Active.ByType(CollectionType.Yourself) @@ -163,7 +163,7 @@ public unsafe class CollectionResolver /// private ResolveData DefaultState(GameObject* gameObject) { - var identifier = _actors.AwaitedService.FromObject(gameObject, out var owner, true, false, false); + var identifier = _actors.FromObject(gameObject, out var owner, true, false, false); if (identifier.Type is IdentifierType.Special) { (identifier, var type) = _collectionManager.Active.Individuals.ConvertSpecialIdentifier(identifier); @@ -193,7 +193,7 @@ public unsafe class CollectionResolver { if (actor->ObjectIndex == 0 || _cutscenes.GetParentIndex(actor->ObjectIndex) == 0 - || identifier.Equals(_actors.AwaitedService.GetCurrentPlayer())) + || identifier.Equals(_actors.GetCurrentPlayer())) return _collectionManager.Active.ByType(CollectionType.Yourself); return null; @@ -242,7 +242,7 @@ public unsafe class CollectionResolver if (identifier.Type != IdentifierType.Owned || !_config.UseOwnerNameForCharacterCollection || owner == null) return null; - var id = _actors.AwaitedService.CreateIndividualUnchecked(IdentifierType.Player, identifier.PlayerName, identifier.HomeWorld.Id, + var id = _actors.CreateIndividualUnchecked(IdentifierType.Player, identifier.PlayerName, identifier.HomeWorld.Id, ObjectKind.None, uint.MaxValue); return CheckYourself(id, owner) diff --git a/Penumbra/Interop/PathResolving/CutsceneService.cs b/Penumbra/Interop/PathResolving/CutsceneService.cs index add121b6..18c016b9 100644 --- a/Penumbra/Interop/PathResolving/CutsceneService.cs +++ b/Penumbra/Interop/PathResolving/CutsceneService.cs @@ -1,6 +1,6 @@ using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game.Character; -using Penumbra.GameData.Actors; +using Penumbra.GameData.Enums; using Penumbra.Interop.Services; namespace Penumbra.Interop.PathResolving; @@ -45,6 +45,9 @@ public class CutsceneService : IDisposable /// Return the currently set index of a parent or -1 if none is set or the index is invalid. public int GetParentIndex(int idx) + => GetParentIndex((ushort)idx); + + public short GetParentIndex(ushort idx) { if (idx is >= CutsceneStartIdx and < CutsceneEndIdx) return _copiedCharacters[idx - CutsceneStartIdx]; diff --git a/Penumbra/Interop/ResourceTree/ResolveContext.cs b/Penumbra/Interop/ResourceTree/ResolveContext.cs index 5a5ecdd9..d03ee508 100644 --- a/Penumbra/Interop/ResourceTree/ResolveContext.cs +++ b/Penumbra/Interop/ResourceTree/ResolveContext.cs @@ -7,6 +7,7 @@ using OtterGui; using Penumbra.Api.Enums; using Penumbra.Collections; using Penumbra.GameData; +using Penumbra.GameData.Data; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using Penumbra.String; @@ -19,7 +20,7 @@ using ModelType = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.CharacterBase.M namespace Penumbra.Interop.ResourceTree; -internal record GlobalResolveContext(IObjectIdentifier Identifier, ModCollection Collection, TreeBuildCache TreeBuildCache, bool WithUiData) +internal record GlobalResolveContext(ObjectIdentification Identifier, ModCollection Collection, TreeBuildCache TreeBuildCache, bool WithUiData) { public readonly Dictionary<(Utf8GamePath, nint), ResourceNode> Nodes = new(128); @@ -28,8 +29,13 @@ internal record GlobalResolveContext(IObjectIdentifier Identifier, ModCollection => new(this, characterBase, slotIndex, slot, equipment, weaponType); } -internal partial record ResolveContext(GlobalResolveContext Global, Pointer CharacterBase, uint SlotIndex, - EquipSlot Slot, CharacterArmor Equipment, WeaponType WeaponType) +internal partial record ResolveContext( + GlobalResolveContext Global, + Pointer CharacterBase, + uint SlotIndex, + EquipSlot Slot, + CharacterArmor Equipment, + WeaponType WeaponType) { private static readonly ByteString ShpkPrefix = ByteString.FromSpanUnsafe("shader/sm5/shpk"u8, true, true, true); @@ -152,6 +158,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, PointerModelResourceHandle == null) return null; + var mdlResource = mdl->ModelResourceHandle; var path = ResolveModelPath(); @@ -224,6 +231,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer "L: ", _ => string.Empty, } - + item.Name.ToString(); + + item.Name; return new ResourceNode.UiData(name, ChangedItemDrawer.GetCategoryIcon(item.Name, item)); } - var dataFromPath = GuessUIDataFromPath(gamePath); + var dataFromPath = GuessUiDataFromPath(gamePath); if (dataFromPath.Name != null) return dataFromPath; @@ -337,7 +345,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer tree.FlatNodes.Add(node)); @@ -161,9 +163,9 @@ public class ResourceTreeFactory var gamePath = node.PossibleGamePaths[0]; node.SetUiData(node.Type switch { - ResourceType.Imc => node.ResolveContext!.GuessModelUIData(gamePath).PrependName("IMC: "), - ResourceType.Mdl => node.ResolveContext!.GuessModelUIData(gamePath), - _ => node.ResolveContext!.GuessUIDataFromPath(gamePath), + ResourceType.Imc => node.ResolveContext!.GuessModelUiData(gamePath).PrependName("IMC: "), + ResourceType.Mdl => node.ResolveContext!.GuessModelUiData(gamePath), + _ => node.ResolveContext!.GuessUiDataFromPath(gamePath), }); } @@ -215,7 +217,7 @@ public class ResourceTreeFactory private unsafe (string Name, bool PlayerRelated) GetCharacterName(Dalamud.Game.ClientState.Objects.Types.Character character, TreeBuildCache cache) { - var identifier = _actors.AwaitedService.FromObject((GameObject*)character.Address, out var owner, true, false, false); + var identifier = _actors.FromObject((GameObject*)character.Address, out var owner, true, false, false); switch (identifier.Type) { case IdentifierType.Player: return (identifier.PlayerName.ToString(), true); diff --git a/Penumbra/Interop/ResourceTree/TreeBuildCache.cs b/Penumbra/Interop/ResourceTree/TreeBuildCache.cs index 9614e9aa..7582c753 100644 --- a/Penumbra/Interop/ResourceTree/TreeBuildCache.cs +++ b/Penumbra/Interop/ResourceTree/TreeBuildCache.cs @@ -1,36 +1,26 @@ using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Plugin.Services; using Penumbra.GameData.Actors; +using Penumbra.GameData.Enums; using Penumbra.GameData.Files; using Penumbra.GameData.Structs; -using Penumbra.Services; using Penumbra.String; using Penumbra.String.Classes; namespace Penumbra.Interop.ResourceTree; -internal readonly struct TreeBuildCache +internal readonly struct TreeBuildCache(IObjectTable objects, IDataManager dataManager, ActorManager actors) { - private readonly IDataManager _dataManager; - private readonly ActorService _actors; - private readonly Dictionary _shaderPackages = new(); - private readonly IObjectTable _objects; - - public TreeBuildCache(IObjectTable objects, IDataManager dataManager, ActorService actors) - { - _dataManager = dataManager; - _objects = objects; - _actors = actors; - } + private readonly Dictionary _shaderPackages = []; public unsafe bool IsLocalPlayerRelated(Character character) { - var player = _objects[0]; + var player = objects[0]; if (player == null) return false; var gameObject = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)character.Address; - var parent = _actors.AwaitedService.ToCutsceneParent(gameObject->ObjectIndex); + var parent = actors.ToCutsceneParent(gameObject->ObjectIndex); var actualIndex = parent >= 0 ? (ushort)parent : gameObject->ObjectIndex; return actualIndex switch { @@ -41,38 +31,38 @@ internal readonly struct TreeBuildCache } public IEnumerable GetCharacters() - => _objects.OfType(); + => objects.OfType(); public IEnumerable GetLocalPlayerRelatedCharacters() { - var player = _objects[0]; + var player = objects[0]; if (player == null) yield break; yield return (Character)player; - var minion = _objects[1]; + var minion = objects[1]; if (minion != null) yield return (Character)minion; var playerId = player.ObjectId; for (var i = 2; i < ObjectIndex.CutsceneStart.Index; i += 2) { - if (_objects[i] is Character owned && owned.OwnerId == playerId) + if (objects[i] is Character owned && owned.OwnerId == playerId) yield return owned; } for (var i = ObjectIndex.CutsceneStart.Index; i < ObjectIndex.CharacterScreen.Index; ++i) { - var character = _objects[i] as Character; + var character = objects[i] as Character; if (character == null) continue; - var parent = _actors.AwaitedService.ToCutsceneParent(i); + var parent = actors.ToCutsceneParent(i); if (parent < 0) continue; - if (parent is 0 or 1 || _objects[parent]?.OwnerId == playerId) + if (parent is 0 or 1 || objects[parent]?.OwnerId == playerId) yield return character; } } @@ -85,11 +75,11 @@ internal readonly struct TreeBuildCache private unsafe bool GetOwnedId(ByteString playerName, uint playerId, int idx, [NotNullWhen(true)] out Character? character) { - character = _objects[idx] as Character; + character = objects[idx] as Character; if (character == null) return false; - var actorId = _actors.AwaitedService.FromObject(character, out var owner, true, true, true); + var actorId = actors.FromObject(character, out var owner, true, true, true); if (!actorId.IsValid) return false; if (owner != null && owner->OwnerID != playerId) @@ -102,7 +92,7 @@ internal readonly struct TreeBuildCache /// Try to read a shpk file from the given path and cache it on success. public ShpkFile? ReadShaderPackage(FullPath path) - => ReadFile(_dataManager, path, _shaderPackages, bytes => new ShpkFile(bytes)); + => ReadFile(dataManager, path, _shaderPackages, bytes => new ShpkFile(bytes)); private static T? ReadFile(IDataManager dataManager, FullPath path, Dictionary cache, Func parseFile) where T : class diff --git a/Penumbra/Interop/Services/RedrawService.cs b/Penumbra/Interop/Services/RedrawService.cs index 7a73857a..e2e57b1c 100644 --- a/Penumbra/Interop/Services/RedrawService.cs +++ b/Penumbra/Interop/Services/RedrawService.cs @@ -9,7 +9,7 @@ using FFXIVClientStructs.Interop; using Penumbra.Api; using Penumbra.Api.Enums; using Penumbra.GameData; -using Penumbra.GameData.Actors; +using Penumbra.GameData.Enums; using Penumbra.Interop.Structs; using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character; diff --git a/Penumbra/Meta/MetaFileManager.cs b/Penumbra/Meta/MetaFileManager.cs index 9c42b9fc..5283f77e 100644 --- a/Penumbra/Meta/MetaFileManager.cs +++ b/Penumbra/Meta/MetaFileManager.cs @@ -5,6 +5,7 @@ using OtterGui.Compression; using Penumbra.Collections; using Penumbra.Collections.Manager; using Penumbra.GameData; +using Penumbra.GameData.Data; using Penumbra.Import; using Penumbra.Interop.Services; using Penumbra.Interop.Structs; @@ -24,11 +25,11 @@ public unsafe class MetaFileManager internal readonly IDataManager GameData; internal readonly ActiveCollectionData ActiveCollections; internal readonly ValidityChecker ValidityChecker; - internal readonly IdentifierService Identifier; + internal readonly ObjectIdentification Identifier; internal readonly FileCompactor Compactor; public MetaFileManager(CharacterUtility characterUtility, ResidentResourceManager residentResources, IDataManager gameData, - ActiveCollectionData activeCollections, Configuration config, ValidityChecker validityChecker, IdentifierService identifier, + ActiveCollectionData activeCollections, Configuration config, ValidityChecker validityChecker, ObjectIdentification identifier, FileCompactor compactor, IGameInteropProvider interop) { CharacterUtility = characterUtility; diff --git a/Penumbra/Mods/ItemSwap/EquipmentSwap.cs b/Penumbra/Mods/ItemSwap/EquipmentSwap.cs index 3d8ab1b6..d634349e 100644 --- a/Penumbra/Mods/ItemSwap/EquipmentSwap.cs +++ b/Penumbra/Mods/ItemSwap/EquipmentSwap.cs @@ -1,5 +1,4 @@ using Penumbra.Api.Enums; -using Penumbra.GameData; using Penumbra.GameData.Data; using Penumbra.GameData.Enums; using Penumbra.GameData.Files; @@ -41,7 +40,7 @@ public static class EquipmentSwap : Array.Empty(); } - public static EquipItem[] CreateTypeSwap(MetaFileManager manager, IObjectIdentifier identifier, List swaps, + public static EquipItem[] CreateTypeSwap(MetaFileManager manager, ObjectIdentification identifier, List swaps, Func redirections, Func manips, EquipSlot slotFrom, EquipItem itemFrom, EquipSlot slotTo, EquipItem itemTo) { @@ -99,7 +98,7 @@ public static class EquipmentSwap return affectedItems; } - public static EquipItem[] CreateItemSwap(MetaFileManager manager, IObjectIdentifier identifier, List swaps, + public static EquipItem[] CreateItemSwap(MetaFileManager manager, ObjectIdentification identifier, List swaps, Func redirections, Func manips, EquipItem itemFrom, EquipItem itemTo, bool rFinger = true, bool lFinger = true) { @@ -247,7 +246,7 @@ public static class EquipmentSwap variant = i.Variant; } - private static (ImcFile, Variant[], EquipItem[]) GetVariants(MetaFileManager manager, IObjectIdentifier identifier, EquipSlot slotFrom, + private static (ImcFile, Variant[], EquipItem[]) GetVariants(MetaFileManager manager, ObjectIdentification identifier, EquipSlot slotFrom, SetId idFrom, SetId idTo, Variant variantFrom) { var entry = new ImcManipulation(slotFrom, variantFrom.Id, idFrom, default); @@ -256,11 +255,8 @@ public static class EquipmentSwap Variant[] variants; if (idFrom == idTo) { - items = identifier.Identify(idFrom, variantFrom, slotFrom).ToArray(); - variants = new[] - { - variantFrom, - }; + items = identifier.Identify(idFrom, 0, variantFrom, slotFrom).ToArray(); + variants = [variantFrom]; } else { diff --git a/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs b/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs index 1db890ed..9ca02c4e 100644 --- a/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs +++ b/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs @@ -1,5 +1,5 @@ -using Lumina.Excel.GeneratedSheets; using Penumbra.Collections; +using Penumbra.GameData.Data; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using Penumbra.Meta.Manipulations; @@ -7,17 +7,16 @@ using Penumbra.String.Classes; using Penumbra.Meta; using Penumbra.Mods.Manager; using Penumbra.Mods.Subclasses; -using Penumbra.Services; namespace Penumbra.Mods.ItemSwap; public class ItemSwapContainer { - private readonly MetaFileManager _manager; - private readonly IdentifierService _identifier; + private readonly MetaFileManager _manager; + private readonly ObjectIdentification _identifier; - private Dictionary _modRedirections = new(); - private HashSet _modManipulations = new(); + private Dictionary _modRedirections = []; + private HashSet _modManipulations = []; public IReadOnlyDictionary ModRedirections => _modRedirections; @@ -25,7 +24,7 @@ public class ItemSwapContainer public IReadOnlySet ModManipulations => _modManipulations; - public readonly List Swaps = new(); + public readonly List Swaps = []; public bool Loaded { get; private set; } @@ -107,7 +106,7 @@ public class ItemSwapContainer } } - public ItemSwapContainer(MetaFileManager manager, IdentifierService identifier) + public ItemSwapContainer(MetaFileManager manager, ObjectIdentification identifier) { _manager = manager; _identifier = identifier; @@ -130,7 +129,7 @@ public class ItemSwapContainer { Swaps.Clear(); Loaded = false; - var ret = EquipmentSwap.CreateItemSwap(_manager, _identifier.AwaitedService, Swaps, PathResolver(collection), MetaResolver(collection), + var ret = EquipmentSwap.CreateItemSwap(_manager, _identifier, Swaps, PathResolver(collection), MetaResolver(collection), from, to, useRightRing, useLeftRing); Loaded = true; return ret; @@ -140,7 +139,7 @@ public class ItemSwapContainer { Swaps.Clear(); Loaded = false; - var ret = EquipmentSwap.CreateTypeSwap(_manager, _identifier.AwaitedService, Swaps, PathResolver(collection), MetaResolver(collection), + var ret = EquipmentSwap.CreateTypeSwap(_manager, _identifier, Swaps, PathResolver(collection), MetaResolver(collection), slotFrom, from, slotTo, to); Loaded = true; return ret; diff --git a/Penumbra/Mods/Manager/ModCacheManager.cs b/Penumbra/Mods/Manager/ModCacheManager.cs index afd42f95..0af78431 100644 --- a/Penumbra/Mods/Manager/ModCacheManager.cs +++ b/Penumbra/Mods/Manager/ModCacheManager.cs @@ -1,5 +1,4 @@ using Penumbra.Communication; -using Penumbra.GameData; using Penumbra.GameData.Data; using Penumbra.GameData.Enums; using Penumbra.Meta.Manipulations; @@ -9,11 +8,12 @@ namespace Penumbra.Mods.Manager; public class ModCacheManager : IDisposable { - private readonly CommunicatorService _communicator; - private readonly IdentifierService _identifier; - private readonly ModStorage _modManager; + private readonly CommunicatorService _communicator; + private readonly ObjectIdentification _identifier; + private readonly ModStorage _modManager; + private bool _updatingItems = false; - public ModCacheManager(CommunicatorService communicator, IdentifierService identifier, ModStorage modStorage) + public ModCacheManager(CommunicatorService communicator, ObjectIdentification identifier, ModStorage modStorage) { _communicator = communicator; _identifier = identifier; @@ -23,8 +23,7 @@ public class ModCacheManager : IDisposable _communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.ModCacheManager); _communicator.ModDataChanged.Subscribe(OnModDataChange, ModDataChanged.Priority.ModCacheManager); _communicator.ModDiscoveryFinished.Subscribe(OnModDiscoveryFinished, ModDiscoveryFinished.Priority.ModCacheManager); - if (!identifier.Valid) - identifier.FinishedCreation += OnIdentifierCreation; + identifier.Awaiter.ContinueWith(_ => OnIdentifierCreation()); OnModDiscoveryFinished(); } @@ -37,7 +36,7 @@ public class ModCacheManager : IDisposable } /// Compute the items changed by a given meta manipulation and put them into the changedItems dictionary. - public static void ComputeChangedItems(IObjectIdentifier identifier, IDictionary changedItems, MetaManipulation manip) + public static void ComputeChangedItems(ObjectIdentification identifier, IDictionary changedItems, MetaManipulation manip) { switch (manip.ManipulationType) { @@ -155,10 +154,7 @@ public class ModCacheManager : IDisposable => Parallel.ForEach(_modManager, Refresh); private void OnIdentifierCreation() - { - Parallel.ForEach(_modManager, UpdateChangedItems); - _identifier.FinishedCreation -= OnIdentifierCreation; - } + => Parallel.ForEach(_modManager, UpdateChangedItems); private static void UpdateFileCount(Mod mod) => mod.TotalFileCount = mod.AllSubMods.Sum(s => s.Files.Count); @@ -177,18 +173,23 @@ public class ModCacheManager : IDisposable private void UpdateChangedItems(Mod mod) { + if (_updatingItems) + return; + + _updatingItems = true; var changedItems = (SortedList)mod.ChangedItems; changedItems.Clear(); - if (!_identifier.Valid) + if (!_identifier.Awaiter.IsCompletedSuccessfully) return; foreach (var gamePath in mod.AllSubMods.SelectMany(m => m.Files.Keys.Concat(m.FileSwaps.Keys))) - _identifier.AwaitedService.Identify(changedItems, gamePath.ToString()); + _identifier.Identify(changedItems, gamePath.ToString()); foreach (var manip in mod.AllSubMods.SelectMany(m => m.Manipulations)) - ComputeChangedItems(_identifier.AwaitedService, changedItems, manip); + ComputeChangedItems(_identifier, changedItems, manip); mod.LowerChangedItemsString = string.Join("\0", mod.ChangedItems.Keys.Select(k => k.ToLowerInvariant())); + _updatingItems = false; } private static void UpdateCounts(Mod mod) diff --git a/Penumbra/Mods/ModCreator.cs b/Penumbra/Mods/ModCreator.cs index 9a31cf46..042c98b4 100644 --- a/Penumbra/Mods/ModCreator.cs +++ b/Penumbra/Mods/ModCreator.cs @@ -5,7 +5,7 @@ using OtterGui; using OtterGui.Classes; using OtterGui.Filesystem; using Penumbra.Api.Enums; -using Penumbra.GameData; +using Penumbra.GameData.Data; using Penumbra.Import; using Penumbra.Import.Structs; using Penumbra.Meta; @@ -17,7 +17,7 @@ using Penumbra.String.Classes; namespace Penumbra.Mods; public partial class ModCreator(SaveService _saveService, Configuration config, ModDataEditor _dataEditor, MetaFileManager _metaFileManager, - IGamePathParser _gamePathParser) + GamePathParser _gamePathParser) { public readonly Configuration Config = config; diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs index bcf94ed1..9be40e66 100644 --- a/Penumbra/Penumbra.cs +++ b/Penumbra/Penumbra.cs @@ -1,4 +1,5 @@ using Dalamud.Plugin; +using Dalamud.Plugin.Services; using ImGuiNET; using Lumina.Excel.GeneratedSheets; using Microsoft.Extensions.DependencyInjection; @@ -6,7 +7,6 @@ using OtterGui; using OtterGui.Log; using Penumbra.Api; using Penumbra.Api.Enums; -using Penumbra.Util; using Penumbra.Collections; using Penumbra.Collections.Cache; using Penumbra.Interop.ResourceLoading; @@ -19,6 +19,7 @@ using Penumbra.UI.Tabs; using ChangedItemClick = Penumbra.Communication.ChangedItemClick; using ChangedItemHover = Penumbra.Communication.ChangedItemHover; using OtterGui.Tasks; +using Penumbra.GameData.Data; using Penumbra.GameData.Enums; using Penumbra.Interop.Structs; using Penumbra.UI; @@ -31,7 +32,7 @@ public class Penumbra : IDalamudPlugin public string Name => "Penumbra"; - public static readonly Logger Log = new(); + public static readonly Logger Log = new(); public static MessageService Messager { get; private set; } = null!; private readonly ValidityChecker _validityChecker; @@ -53,10 +54,8 @@ public class Penumbra : IDalamudPlugin { try { - var startTimer = new StartTracker(); - using var timer = startTimer.Measure(StartTimeType.Total); - _services = ServiceManager.CreateProvider(this, pluginInterface, Log, startTimer); - Messager = _services.GetRequiredService(); + _services = ServiceManager.CreateProvider(this, pluginInterface, Log); + Messager = _services.GetRequiredService(); _validityChecker = _services.GetRequiredService(); var startup = _services.GetRequiredService().GetDalamudConfig(DalamudServices.WaitingForPluginsOption, out bool s) ? s.ToString() @@ -74,14 +73,11 @@ public class Penumbra : IDalamudPlugin _tempCollections = _services.GetRequiredService(); _redrawService = _services.GetRequiredService(); _communicatorService = _services.GetRequiredService(); - _services.GetRequiredService(); // Initialize because not required anywhere else. - _services.GetRequiredService(); // Initialize because not required anywhere else. + _services.GetRequiredService(); // Initialize because not required anywhere else. + _services.GetRequiredService(); // Initialize because not required anywhere else. _services.GetRequiredService(); // Initialize because not required anywhere else. _collectionManager.Caches.CreateNecessaryCaches(); - using (var t = _services.GetRequiredService().Measure(StartTimeType.PathResolver)) - { - _services.GetRequiredService(); - } + _services.GetRequiredService(); _services.GetRequiredService(); @@ -108,8 +104,7 @@ public class Penumbra : IDalamudPlugin private void SetupApi() { - using var timer = _services.GetRequiredService().Measure(StartTimeType.Api); - var api = _services.GetRequiredService(); + var api = _services.GetRequiredService(); _services.GetRequiredService(); _communicatorService.ChangedItemHover.Subscribe(it => { @@ -128,8 +123,7 @@ public class Penumbra : IDalamudPlugin { AsyncTask.Run(() => { - using var tInterface = _services.GetRequiredService().Measure(StartTimeType.Interface); - var system = _services.GetRequiredService(); + var system = _services.GetRequiredService(); system.Window.Setup(this, _services.GetRequiredService()); _services.GetRequiredService(); if (!_disposed) diff --git a/Penumbra/Services/BackupService.cs b/Penumbra/Services/BackupService.cs index 0059cf9f..e8684f9d 100644 --- a/Penumbra/Services/BackupService.cs +++ b/Penumbra/Services/BackupService.cs @@ -1,15 +1,13 @@ using Newtonsoft.Json.Linq; using OtterGui.Classes; using OtterGui.Log; -using Penumbra.Util; namespace Penumbra.Services; public class BackupService { - public BackupService(Logger logger, StartTracker timer, FilenameService fileNames) + public BackupService(Logger logger, FilenameService fileNames) { - using var t = timer.Measure(StartTimeType.Backup); var files = PenumbraFiles(fileNames); Backup.CreateAutomaticBackup(logger, new DirectoryInfo(fileNames.ConfigDirectory), files); } diff --git a/Penumbra/Services/ServiceManager.cs b/Penumbra/Services/ServiceManager.cs index 73be8834..049ab328 100644 --- a/Penumbra/Services/ServiceManager.cs +++ b/Penumbra/Services/ServiceManager.cs @@ -7,7 +7,10 @@ using Penumbra.Api; using Penumbra.Collections.Cache; using Penumbra.Collections.Manager; using Penumbra.GameData; +using Penumbra.GameData.Actors; using Penumbra.GameData.Data; +using Penumbra.GameData.DataContainers; +using Penumbra.GameData.DataContainers.Bases; using Penumbra.Import.Textures; using Penumbra.Interop.PathResolving; using Penumbra.Interop.ResourceLoading; @@ -24,17 +27,17 @@ using Penumbra.UI.Classes; using Penumbra.UI.ModsTab; using Penumbra.UI.ResourceWatcher; using Penumbra.UI.Tabs; +using Penumbra.UI.Tabs.Debug; using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager; namespace Penumbra.Services; public static class ServiceManager { - public static ServiceProvider CreateProvider(Penumbra penumbra, DalamudPluginInterface pi, Logger log, StartTracker startTimer) + public static ServiceProvider CreateProvider(Penumbra penumbra, DalamudPluginInterface pi, Logger log) { var services = new ServiceCollection() .AddSingleton(log) - .AddSingleton(startTimer) .AddSingleton(penumbra) .AddDalamud(pi) .AddMeta() @@ -47,7 +50,9 @@ public static class ServiceManager .AddResolvers() .AddInterface() .AddModEditor() - .AddApi(); + .AddApi() + .AddDataContainers() + .AddAsyncServices(); return services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true }); } @@ -59,6 +64,22 @@ public static class ServiceManager return services; } + private static IServiceCollection AddDataContainers(this IServiceCollection services) + { + foreach (var type in typeof(IDataContainer).Assembly.GetExportedTypes() + .Where(t => t is { IsAbstract: false, IsInterface: false } && t.IsAssignableTo(typeof(IDataContainer)))) + services.AddSingleton(type); + return services; + } + + private static IServiceCollection AddAsyncServices(this IServiceCollection services) + { + foreach (var type in typeof(IDataContainer).Assembly.GetExportedTypes() + .Where(t => t is { IsAbstract: false, IsInterface: false } && t.IsAssignableTo(typeof(IAsyncService)))) + services.AddSingleton(type); + return services; + } + private static IServiceCollection AddMeta(this IServiceCollection services) => services.AddSingleton() .AddSingleton() @@ -71,17 +92,19 @@ public static class ServiceManager private static IServiceCollection AddGameData(this IServiceCollection services) - => services.AddSingleton() - .AddSingleton() + => services.AddSingleton() .AddSingleton() - .AddSingleton() - .AddSingleton() .AddSingleton(); private static IServiceCollection AddInterop(this IServiceCollection services) => services.AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton(p => + { + var cutsceneService = p.GetRequiredService(); + return new CutsceneResolver(cutsceneService.GetParentIndex); + }) .AddSingleton() .AddSingleton() .AddSingleton() @@ -173,7 +196,8 @@ public static class ServiceManager .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton(); + .AddSingleton() + .AddSingleton(p => new Diagnostics(p)); private static IServiceCollection AddModEditor(this IServiceCollection services) => services.AddSingleton() diff --git a/Penumbra/Services/ServiceWrapper.cs b/Penumbra/Services/ServiceWrapper.cs index 67ddb63d..37acdfd0 100644 --- a/Penumbra/Services/ServiceWrapper.cs +++ b/Penumbra/Services/ServiceWrapper.cs @@ -1,5 +1,4 @@ using OtterGui.Tasks; -using Penumbra.Util; namespace Penumbra.Services; @@ -12,10 +11,9 @@ public abstract class SyncServiceWrapper : IDisposable public bool Valid => !_isDisposed; - protected SyncServiceWrapper(string name, StartTracker tracker, StartTimeType type, Func factory) + protected SyncServiceWrapper(string name, Func factory) { Name = name; - using var timer = tracker.Measure(type); Service = factory(); Penumbra.Log.Verbose($"[{Name}] Created."); } @@ -54,32 +52,6 @@ public abstract class AsyncServiceWrapper : IDisposable private bool _isDisposed; - protected AsyncServiceWrapper(string name, StartTracker tracker, StartTimeType type, Func factory) - { - Name = name; - _task = TrackedTask.Run(() => - { - using var timer = tracker.Measure(type); - var service = factory(); - if (_isDisposed) - { - if (service is IDisposable d) - d.Dispose(); - } - else - { - Service = service; - Penumbra.Log.Verbose($"[{Name}] Created."); - _task = null; - } - }); - _task.ContinueWith((t, x) => - { - if (!_isDisposed) - FinishedCreation?.Invoke(); - }, null); - } - protected AsyncServiceWrapper(string name, Func factory) { Name = name; diff --git a/Penumbra/Services/StainService.cs b/Penumbra/Services/StainService.cs index a0bca570..d11e4d64 100644 --- a/Penumbra/Services/StainService.cs +++ b/Penumbra/Services/StainService.cs @@ -4,7 +4,7 @@ using Dalamud.Plugin; using Dalamud.Plugin.Services; using ImGuiNET; using OtterGui.Widgets; -using Penumbra.GameData.Data; +using Penumbra.GameData.DataContainers; using Penumbra.GameData.Files; using Penumbra.UI.AdvancedWindow; using Penumbra.Util; @@ -71,17 +71,16 @@ public class StainService : IDisposable } } - public readonly StainData StainData; + public readonly DictStains StainData; public readonly FilterComboColors StainCombo; public readonly StmFile StmFile; public readonly StainTemplateCombo TemplateCombo; - public StainService(StartTracker timer, DalamudPluginInterface pluginInterface, IDataManager dataManager, IPluginLog dalamudLog) + public StainService(DalamudPluginInterface pluginInterface, IDataManager dataManager, IPluginLog dalamudLog) { - using var t = timer.Measure(StartTimeType.Stains); - StainData = new StainData(pluginInterface, dataManager, dataManager.Language, dalamudLog); + StainData = new DictStains(pluginInterface, dalamudLog, dataManager); StainCombo = new FilterComboColors(140, - () => StainData.Data.Prepend(new KeyValuePair(0, ("None", 0, false))).ToList(), + () => StainData.Value.Prepend(new KeyValuePair(0, ("None", 0, false))).ToList(), Penumbra.Log); StmFile = new StmFile(dataManager); TemplateCombo = new StainTemplateCombo(StainCombo, StmFile); diff --git a/Penumbra/Services/Wrappers.cs b/Penumbra/Services/Wrappers.cs deleted file mode 100644 index b1f17d4d..00000000 --- a/Penumbra/Services/Wrappers.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Dalamud.Game; -using Dalamud.Plugin; -using Dalamud.Plugin.Services; -using Penumbra.GameData; -using Penumbra.GameData.Actors; -using Penumbra.GameData.Data; -using Penumbra.Interop.PathResolving; -using Penumbra.Util; - -namespace Penumbra.Services; - -public sealed class IdentifierService : AsyncServiceWrapper -{ - public IdentifierService(StartTracker tracker, DalamudPluginInterface pi, IDataManager data, ItemService items, IPluginLog log) - : base(nameof(IdentifierService), tracker, StartTimeType.Identifier, - () => GameData.GameData.GetIdentifier(pi, data, items.AwaitedService, log)) - { } -} - -public sealed class ItemService : AsyncServiceWrapper -{ - public ItemService(StartTracker tracker, DalamudPluginInterface pi, IDataManager gameData, IPluginLog log) - : base(nameof(ItemService), tracker, StartTimeType.Items, () => new ItemData(pi, gameData, gameData.Language, log)) - { } -} - -public sealed class ActorService : AsyncServiceWrapper -{ - public ActorService(StartTracker tracker, DalamudPluginInterface pi, IObjectTable objects, IClientState clientState, - IFramework framework, IDataManager gameData, IGameGui gui, CutsceneService cutscene, IPluginLog log, IGameInteropProvider interop) - : base(nameof(ActorService), tracker, StartTimeType.Actors, - () => new ActorManager(pi, objects, clientState, framework, interop, gameData, gui, idx => (short)cutscene.GetParentIndex(idx), log)) - { } -} diff --git a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs index 5347208e..0ec11542 100644 --- a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs +++ b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs @@ -1,8 +1,5 @@ -using Dalamud.Interface; using Dalamud.Interface.Internal.Notifications; -using Dalamud.Utility; using ImGuiNET; -using Lumina.Excel.GeneratedSheets; using OtterGui; using OtterGui.Classes; using OtterGui.Raii; @@ -11,6 +8,7 @@ using Penumbra.Api.Enums; using Penumbra.Collections; using Penumbra.Collections.Manager; using Penumbra.Communication; +using Penumbra.GameData.Data; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using Penumbra.Meta; @@ -27,16 +25,14 @@ public class ItemSwapTab : IDisposable, ITab { private readonly Configuration _config; private readonly CommunicatorService _communicator; - private readonly ItemService _itemService; private readonly CollectionManager _collectionManager; private readonly ModManager _modManager; private readonly MetaFileManager _metaFileManager; - public ItemSwapTab(CommunicatorService communicator, ItemService itemService, CollectionManager collectionManager, - ModManager modManager, IdentifierService identifier, MetaFileManager metaFileManager, Configuration config) + public ItemSwapTab(CommunicatorService communicator, ItemData itemService, CollectionManager collectionManager, + ModManager modManager, ObjectIdentification identifier, MetaFileManager metaFileManager, Configuration config) { _communicator = communicator; - _itemService = itemService; _collectionManager = collectionManager; _modManager = modManager; _metaFileManager = metaFileManager; @@ -46,15 +42,15 @@ public class ItemSwapTab : IDisposable, ITab _selectors = new Dictionary { // @formatter:off - [SwapType.Hat] = (new ItemSelector(_itemService, FullEquipType.Head), new ItemSelector(_itemService, FullEquipType.Head), "Take this Hat", "and put it on this one" ), - [SwapType.Top] = (new ItemSelector(_itemService, FullEquipType.Body), new ItemSelector(_itemService, FullEquipType.Body), "Take this Top", "and put it on this one" ), - [SwapType.Gloves] = (new ItemSelector(_itemService, FullEquipType.Hands), new ItemSelector(_itemService, FullEquipType.Hands), "Take these Gloves", "and put them on these" ), - [SwapType.Pants] = (new ItemSelector(_itemService, FullEquipType.Legs), new ItemSelector(_itemService, FullEquipType.Legs), "Take these Pants", "and put them on these" ), - [SwapType.Shoes] = (new ItemSelector(_itemService, FullEquipType.Feet), new ItemSelector(_itemService, FullEquipType.Feet), "Take these Shoes", "and put them on these" ), - [SwapType.Earrings] = (new ItemSelector(_itemService, FullEquipType.Ears), new ItemSelector(_itemService, FullEquipType.Ears), "Take these Earrings", "and put them on these" ), - [SwapType.Necklace] = (new ItemSelector(_itemService, FullEquipType.Neck), new ItemSelector(_itemService, FullEquipType.Neck), "Take this Necklace", "and put it on this one" ), - [SwapType.Bracelet] = (new ItemSelector(_itemService, FullEquipType.Wrists), new ItemSelector(_itemService, FullEquipType.Wrists), "Take these Bracelets", "and put them on these" ), - [SwapType.Ring] = (new ItemSelector(_itemService, FullEquipType.Finger), new ItemSelector(_itemService, FullEquipType.Finger), "Take this Ring", "and put it on this one" ), + [SwapType.Hat] = (new ItemSelector(itemService, FullEquipType.Head), new ItemSelector(itemService, FullEquipType.Head), "Take this Hat", "and put it on this one" ), + [SwapType.Top] = (new ItemSelector(itemService, FullEquipType.Body), new ItemSelector(itemService, FullEquipType.Body), "Take this Top", "and put it on this one" ), + [SwapType.Gloves] = (new ItemSelector(itemService, FullEquipType.Hands), new ItemSelector(itemService, FullEquipType.Hands), "Take these Gloves", "and put them on these" ), + [SwapType.Pants] = (new ItemSelector(itemService, FullEquipType.Legs), new ItemSelector(itemService, FullEquipType.Legs), "Take these Pants", "and put them on these" ), + [SwapType.Shoes] = (new ItemSelector(itemService, FullEquipType.Feet), new ItemSelector(itemService, FullEquipType.Feet), "Take these Shoes", "and put them on these" ), + [SwapType.Earrings] = (new ItemSelector(itemService, FullEquipType.Ears), new ItemSelector(itemService, FullEquipType.Ears), "Take these Earrings", "and put them on these" ), + [SwapType.Necklace] = (new ItemSelector(itemService, FullEquipType.Neck), new ItemSelector(itemService, FullEquipType.Neck), "Take this Necklace", "and put it on this one" ), + [SwapType.Bracelet] = (new ItemSelector(itemService, FullEquipType.Wrists), new ItemSelector(itemService, FullEquipType.Wrists), "Take these Bracelets", "and put them on these" ), + [SwapType.Ring] = (new ItemSelector(itemService, FullEquipType.Finger), new ItemSelector(itemService, FullEquipType.Finger), "Take this Ring", "and put it on this one" ), // @formatter:on }; @@ -131,8 +127,8 @@ public class ItemSwapTab : IDisposable, ITab private class ItemSelector : FilterComboCache { - public ItemSelector(ItemService data, FullEquipType type) - : base(() => data.AwaitedService[type], Penumbra.Log) + public ItemSelector(ItemData data, FullEquipType type) + : base(() => data.ByType[type], Penumbra.Log) { } protected override string ToString(EquipItem obj) diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.ShaderPackages.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.ShaderPackages.cs index 804feae1..82fc78c0 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.ShaderPackages.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.ShaderPackages.cs @@ -8,6 +8,7 @@ using OtterGui.Classes; using Penumbra.GameData; using Penumbra.GameData.Data; using Penumbra.GameData.Files; +using Penumbra.GameData.Interop; using Penumbra.String; using static Penumbra.GameData.Files.ShpkFile; diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.ShpkTab.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.ShpkTab.cs index 7f14165c..12b8d761 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.ShpkTab.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.ShpkTab.cs @@ -3,6 +3,7 @@ using Lumina.Misc; using OtterGui; using Penumbra.GameData.Data; using Penumbra.GameData.Files; +using Penumbra.GameData.Interop; namespace Penumbra.UI.AdvancedWindow; diff --git a/Penumbra/UI/CollectionTab/CollectionPanel.cs b/Penumbra/UI/CollectionTab/CollectionPanel.cs index bd37a484..8f90750f 100644 --- a/Penumbra/UI/CollectionTab/CollectionPanel.cs +++ b/Penumbra/UI/CollectionTab/CollectionPanel.cs @@ -22,7 +22,7 @@ public sealed class CollectionPanel : IDisposable private readonly CollectionStorage _collections; private readonly ActiveCollections _active; private readonly CollectionSelector _selector; - private readonly ActorService _actors; + private readonly ActorManager _actors; private readonly ITargetManager _targets; private readonly IndividualAssignmentUi _individualAssignmentUi; private readonly InheritanceUi _inheritanceUi; @@ -37,7 +37,7 @@ public sealed class CollectionPanel : IDisposable private int _draggedIndividualAssignment = -1; public CollectionPanel(DalamudPluginInterface pi, CommunicatorService communicator, CollectionManager manager, - CollectionSelector selector, ActorService actors, ITargetManager targets, ModStorage mods) + CollectionSelector selector, ActorManager actors, ITargetManager targets, ModStorage mods) { _collections = manager.Storage; _active = manager.Active; @@ -382,11 +382,11 @@ public sealed class CollectionPanel : IDisposable } private void DrawCurrentCharacter(Vector2 width) - => DrawIndividualButton("Current Character", width, string.Empty, 'c', _actors.AwaitedService.GetCurrentPlayer()); + => DrawIndividualButton("Current Character", width, string.Empty, 'c', _actors.GetCurrentPlayer()); private void DrawCurrentTarget(Vector2 width) => DrawIndividualButton("Current Target", width, string.Empty, 't', - _actors.AwaitedService.FromObject(_targets.Target, false, true, true)); + _actors.FromObject(_targets.Target, false, true, true)); private void DrawNewPlayer(Vector2 width) => DrawIndividualButton("New Player", width, _individualAssignmentUi.PlayerTooltip, 'p', diff --git a/Penumbra/UI/CollectionTab/IndividualAssignmentUi.cs b/Penumbra/UI/CollectionTab/IndividualAssignmentUi.cs index 5f463c43..d3e4ab5e 100644 --- a/Penumbra/UI/CollectionTab/IndividualAssignmentUi.cs +++ b/Penumbra/UI/CollectionTab/IndividualAssignmentUi.cs @@ -5,6 +5,9 @@ using Penumbra.Collections; using Penumbra.Collections.Manager; using Penumbra.Communication; using Penumbra.GameData.Actors; +using Penumbra.GameData.Enums; +using Penumbra.GameData.Gui; +using Penumbra.GameData.Structs; using Penumbra.Services; namespace Penumbra.UI.CollectionTab; @@ -12,7 +15,7 @@ namespace Penumbra.UI.CollectionTab; public class IndividualAssignmentUi : IDisposable { private readonly CommunicatorService _communicator; - private readonly ActorService _actorService; + private readonly ActorManager _actors; private readonly CollectionManager _collectionManager; private WorldCombo _worldCombo = null!; @@ -24,16 +27,13 @@ public class IndividualAssignmentUi : IDisposable private bool _ready; - public IndividualAssignmentUi(CommunicatorService communicator, ActorService actors, CollectionManager collectionManager) + public IndividualAssignmentUi(CommunicatorService communicator, ActorManager actors, CollectionManager collectionManager) { _communicator = communicator; - _actorService = actors; + _actors = actors; _collectionManager = collectionManager; _communicator.CollectionChange.Subscribe(UpdateIdentifiers, CollectionChange.Priority.IndividualAssignmentUi); - if (_actorService.Valid) - SetupCombos(); - else - _actorService.FinishedCreation += SetupCombos; + _actors.Awaiter.ContinueWith(_ => SetupCombos()); } public string PlayerTooltip { get; private set; } = NewPlayerTooltipEmpty; @@ -91,10 +91,10 @@ public class IndividualAssignmentUi : IDisposable // Input Selections. private string _newCharacterName = string.Empty; private ObjectKind _newKind = ObjectKind.BattleNpc; - private ActorIdentifier[] _playerIdentifiers = Array.Empty(); - private ActorIdentifier[] _retainerIdentifiers = Array.Empty(); - private ActorIdentifier[] _npcIdentifiers = Array.Empty(); - private ActorIdentifier[] _ownedIdentifiers = Array.Empty(); + private ActorIdentifier[] _playerIdentifiers = []; + private ActorIdentifier[] _retainerIdentifiers = []; + private ActorIdentifier[] _npcIdentifiers = []; + private ActorIdentifier[] _ownedIdentifiers = []; private const string NewPlayerTooltipEmpty = "Please enter a valid player name and choose an available world or 'Any World'."; private const string NewRetainerTooltipEmpty = "Please enter a valid retainer name."; @@ -126,14 +126,13 @@ public class IndividualAssignmentUi : IDisposable /// Create combos when ready. private void SetupCombos() { - _worldCombo = new WorldCombo(_actorService.AwaitedService.Data.Worlds, Penumbra.Log); - _mountCombo = new NpcCombo("##mountCombo", _actorService.AwaitedService.Data.Mounts, Penumbra.Log); - _companionCombo = new NpcCombo("##companionCombo", _actorService.AwaitedService.Data.Companions, Penumbra.Log); - _ornamentCombo = new NpcCombo("##ornamentCombo", _actorService.AwaitedService.Data.Ornaments, Penumbra.Log); - _bnpcCombo = new NpcCombo("##bnpcCombo", _actorService.AwaitedService.Data.BNpcs, Penumbra.Log); - _enpcCombo = new NpcCombo("##enpcCombo", _actorService.AwaitedService.Data.ENpcs, Penumbra.Log); - _ready = true; - _actorService.FinishedCreation -= SetupCombos; + _worldCombo = new WorldCombo(_actors.Data.Worlds, Penumbra.Log, WorldId.AnyWorld); + _mountCombo = new NpcCombo("##mountCombo", _actors.Data.Mounts, Penumbra.Log); + _companionCombo = new NpcCombo("##companionCombo", _actors.Data.Companions, Penumbra.Log); + _ornamentCombo = new NpcCombo("##ornamentCombo", _actors.Data.Ornaments, Penumbra.Log); + _bnpcCombo = new NpcCombo("##bnpcCombo", _actors.Data.BNpcs, Penumbra.Log); + _enpcCombo = new NpcCombo("##enpcCombo", _actors.Data.ENpcs, Penumbra.Log); + _ready = true; } private void UpdateIdentifiers(CollectionType type, ModCollection? _1, ModCollection? _2, string _3) @@ -146,22 +145,22 @@ public class IndividualAssignmentUi : IDisposable { var combo = GetNpcCombo(_newKind); PlayerTooltip = _collectionManager.Active.Individuals.CanAdd(IdentifierType.Player, _newCharacterName, - _worldCombo.CurrentSelection.Key, ObjectKind.None, - Array.Empty(), out _playerIdentifiers) switch + _worldCombo.CurrentSelection.Key, ObjectKind.None, [], out _playerIdentifiers) switch { _ when _newCharacterName.Length == 0 => NewPlayerTooltipEmpty, IndividualCollections.AddResult.Invalid => NewPlayerTooltipInvalid, IndividualCollections.AddResult.AlreadySet => AlreadyAssigned, _ => string.Empty, }; - RetainerTooltip = _collectionManager.Active.Individuals.CanAdd(IdentifierType.Retainer, _newCharacterName, 0, ObjectKind.None, - Array.Empty(), out _retainerIdentifiers) switch - { - _ when _newCharacterName.Length == 0 => NewRetainerTooltipEmpty, - IndividualCollections.AddResult.Invalid => NewRetainerTooltipInvalid, - IndividualCollections.AddResult.AlreadySet => AlreadyAssigned, - _ => string.Empty, - }; + RetainerTooltip = + _collectionManager.Active.Individuals.CanAdd(IdentifierType.Retainer, _newCharacterName, 0, ObjectKind.None, [], + out _retainerIdentifiers) switch + { + _ when _newCharacterName.Length == 0 => NewRetainerTooltipEmpty, + IndividualCollections.AddResult.Invalid => NewRetainerTooltipInvalid, + IndividualCollections.AddResult.AlreadySet => AlreadyAssigned, + _ => string.Empty, + }; if (combo.CurrentSelection.Ids != null) { NpcTooltip = _collectionManager.Active.Individuals.CanAdd(IdentifierType.Npc, string.Empty, ushort.MaxValue, _newKind, @@ -184,8 +183,8 @@ public class IndividualAssignmentUi : IDisposable { NpcTooltip = NewNpcTooltipEmpty; OwnedTooltip = NewNpcTooltipEmpty; - _npcIdentifiers = Array.Empty(); - _ownedIdentifiers = Array.Empty(); + _npcIdentifiers = []; + _ownedIdentifiers = []; } } } diff --git a/Penumbra/UI/ResourceWatcher/ResourceWatcher.cs b/Penumbra/UI/ResourceWatcher/ResourceWatcher.cs index 0ac3b9a0..d5ff1abd 100644 --- a/Penumbra/UI/ResourceWatcher/ResourceWatcher.cs +++ b/Penumbra/UI/ResourceWatcher/ResourceWatcher.cs @@ -6,16 +6,16 @@ using OtterGui.Widgets; using Penumbra.Api.Enums; using Penumbra.Collections; using Penumbra.GameData.Actors; +using Penumbra.GameData.Enums; using Penumbra.Interop.ResourceLoading; using Penumbra.Interop.Structs; -using Penumbra.Services; using Penumbra.String; using Penumbra.String.Classes; using Penumbra.UI.Classes; namespace Penumbra.UI.ResourceWatcher; -public class ResourceWatcher : IDisposable, ITab +public sealed class ResourceWatcher : IDisposable, ITab { public const int DefaultMaxEntries = 1024; public const RecordType AllRecords = RecordType.Request | RecordType.ResourceLoad | RecordType.FileLoad | RecordType.Destruction; @@ -24,15 +24,15 @@ public class ResourceWatcher : IDisposable, ITab private readonly EphemeralConfig _ephemeral; private readonly ResourceService _resources; private readonly ResourceLoader _loader; - private readonly ActorService _actors; - private readonly List _records = new(); - private readonly ConcurrentQueue _newRecords = new(); + private readonly ActorManager _actors; + private readonly List _records = []; + private readonly ConcurrentQueue _newRecords = []; private readonly ResourceWatcherTable _table; private string _logFilter = string.Empty; private Regex? _logRegex; private int _newMaxEntries; - public unsafe ResourceWatcher(ActorService actors, Configuration config, ResourceService resources, ResourceLoader loader) + public unsafe ResourceWatcher(ActorManager actors, Configuration config, ResourceService resources, ResourceLoader loader) { _actors = actors; _config = config; @@ -266,12 +266,12 @@ public class ResourceWatcher : IDisposable, ITab public unsafe string Name(ResolveData resolve, string none = "") { - if (resolve.AssociatedGameObject == IntPtr.Zero || !_actors.Valid) + if (resolve.AssociatedGameObject == IntPtr.Zero || !_actors.Awaiter.IsCompletedSuccessfully) return none; try { - var id = _actors.AwaitedService.FromObject((GameObject*)resolve.AssociatedGameObject, out _, false, true, true); + var id = _actors.FromObject((GameObject*)resolve.AssociatedGameObject, out _, false, true, true); if (id.IsValid) { if (id.Type is not (IdentifierType.Player or IdentifierType.Owned)) diff --git a/Penumbra/UI/Tabs/CollectionsTab.cs b/Penumbra/UI/Tabs/CollectionsTab.cs index 2d30f9cf..3c6a3ed9 100644 --- a/Penumbra/UI/Tabs/CollectionsTab.cs +++ b/Penumbra/UI/Tabs/CollectionsTab.cs @@ -7,6 +7,7 @@ using OtterGui; using OtterGui.Raii; using OtterGui.Widgets; using Penumbra.Collections.Manager; +using Penumbra.GameData.Actors; using Penumbra.Mods.Manager; using Penumbra.Services; using Penumbra.UI.Classes; @@ -14,7 +15,7 @@ using Penumbra.UI.CollectionTab; namespace Penumbra.UI.Tabs; -public class CollectionsTab : IDisposable, ITab +public sealed class CollectionsTab : IDisposable, ITab { private readonly EphemeralConfig _config; private readonly CollectionSelector _selector; @@ -40,7 +41,7 @@ public class CollectionsTab : IDisposable, ITab } public CollectionsTab(DalamudPluginInterface pi, Configuration configuration, CommunicatorService communicator, - CollectionManager collectionManager, ModStorage modStorage, ActorService actors, ITargetManager targets, TutorialService tutorial) + CollectionManager collectionManager, ModStorage modStorage, ActorManager actors, ITargetManager targets, TutorialService tutorial) { _config = configuration.Ephemeral; _tutorial = tutorial; diff --git a/Penumbra/UI/Tabs/ConfigTabBar.cs b/Penumbra/UI/Tabs/ConfigTabBar.cs index 1cc29d88..ad3fdb3d 100644 --- a/Penumbra/UI/Tabs/ConfigTabBar.cs +++ b/Penumbra/UI/Tabs/ConfigTabBar.cs @@ -3,6 +3,7 @@ using OtterGui.Widgets; using Penumbra.Api.Enums; using Penumbra.Mods; using Penumbra.Services; +using Penumbra.UI.Tabs.Debug; using Watcher = Penumbra.UI.ResourceWatcher.ResourceWatcher; namespace Penumbra.UI.Tabs; diff --git a/Penumbra/UI/Tabs/DebugTab.cs b/Penumbra/UI/Tabs/Debug/DebugTab.cs similarity index 94% rename from Penumbra/UI/Tabs/DebugTab.cs rename to Penumbra/UI/Tabs/Debug/DebugTab.cs index 4f554ac5..57ca68fc 100644 --- a/Penumbra/UI/Tabs/DebugTab.cs +++ b/Penumbra/UI/Tabs/Debug/DebugTab.cs @@ -1,24 +1,30 @@ using Dalamud.Interface; using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Group; using FFXIVClientStructs.FFXIV.Client.Game.Object; +using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; using FFXIVClientStructs.FFXIV.Client.System.Resource; using FFXIVClientStructs.FFXIV.Client.UI.Agent; using ImGuiNET; +using Microsoft.Extensions.DependencyInjection; using OtterGui; using OtterGui.Classes; using OtterGui.Widgets; using Penumbra.Api; using Penumbra.Collections.Manager; using Penumbra.GameData.Actors; +using Penumbra.GameData.DataContainers; +using Penumbra.GameData.DataContainers.Bases; using Penumbra.GameData.Files; using Penumbra.Import.Structs; using Penumbra.Import.Textures; -using Penumbra.Interop.ResourceLoading; using Penumbra.Interop.PathResolving; +using Penumbra.Interop.ResourceLoading; +using Penumbra.Interop.Services; using Penumbra.Interop.Structs; using Penumbra.Mods; using Penumbra.Mods.Manager; @@ -31,22 +37,39 @@ using CharacterBase = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.CharacterBa using CharacterUtility = Penumbra.Interop.Services.CharacterUtility; using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind; using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager; -using Penumbra.Interop.Services; -using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; using ImGuiClip = OtterGui.ImGuiClip; -namespace Penumbra.UI.Tabs; +namespace Penumbra.UI.Tabs.Debug; + +public class Diagnostics(IServiceProvider provider) +{ + public void DrawDiagnostics() + { + if (!ImGui.CollapsingHeader("Diagnostics")) + return; + + using var table = ImRaii.Table("##data", 4, ImGuiTableFlags.RowBg); + foreach (var type in typeof(IAsyncDataContainer).Assembly.GetTypes() + .Where(t => t is { IsAbstract: false, IsInterface: false } && t.IsAssignableTo(typeof(IAsyncDataContainer)))) + { + var container = (IAsyncDataContainer) provider.GetRequiredService(type); + ImGuiUtil.DrawTableColumn(container.Name); + ImGuiUtil.DrawTableColumn(container.Time.ToString()); + ImGuiUtil.DrawTableColumn(Functions.HumanReadableSize(container.Memory)); + ImGuiUtil.DrawTableColumn(container.TotalCount.ToString()); + } + } +} public class DebugTab : Window, ITab { - private readonly StartTracker _timer; private readonly PerformanceTracker _performance; private readonly Configuration _config; private readonly CollectionManager _collectionManager; private readonly ModManager _modManager; private readonly ValidityChecker _validityChecker; private readonly HttpApi _httpApi; - private readonly ActorService _actorService; + private readonly ActorManager _actors; private readonly DalamudServices _dalamud; private readonly StainService _stains; private readonly CharacterUtility _characterUtility; @@ -64,16 +87,17 @@ public class DebugTab : Window, ITab private readonly FrameworkManager _framework; private readonly TextureManager _textureManager; private readonly SkinFixer _skinFixer; - private readonly IdentifierService _identifier; private readonly RedrawService _redraws; + private readonly DictEmotes _emotes; + private readonly Diagnostics _diagnostics; - public DebugTab(StartTracker timer, PerformanceTracker performance, Configuration config, CollectionManager collectionManager, - ValidityChecker validityChecker, ModManager modManager, HttpApi httpApi, ActorService actorService, + public DebugTab(PerformanceTracker performance, Configuration config, CollectionManager collectionManager, + ValidityChecker validityChecker, ModManager modManager, HttpApi httpApi, ActorManager actors, DalamudServices dalamud, StainService stains, CharacterUtility characterUtility, ResidentResourceManager residentResources, ResourceManagerService resourceManager, PenumbraIpcProviders ipc, CollectionResolver collectionResolver, DrawObjectState drawObjectState, PathState pathState, SubfileHelper subfileHelper, IdentifiedCollectionCache identifiedCollectionCache, CutsceneService cutsceneService, ModImportManager modImporter, ImportPopup importPopup, FrameworkManager framework, - TextureManager textureManager, SkinFixer skinFixer, IdentifierService identifier, RedrawService redraws) + TextureManager textureManager, SkinFixer skinFixer, RedrawService redraws, DictEmotes emotes, Diagnostics diagnostics) : base("Penumbra Debug Window", ImGuiWindowFlags.NoCollapse) { IsOpen = true; @@ -82,14 +106,13 @@ public class DebugTab : Window, ITab MinimumSize = new Vector2(200, 200), MaximumSize = new Vector2(2000, 2000), }; - _timer = timer; _performance = performance; _config = config; _collectionManager = collectionManager; _validityChecker = validityChecker; _modManager = modManager; _httpApi = httpApi; - _actorService = actorService; + _actors = actors; _dalamud = dalamud; _stains = stains; _characterUtility = characterUtility; @@ -107,8 +130,9 @@ public class DebugTab : Window, ITab _framework = framework; _textureManager = textureManager; _skinFixer = skinFixer; - _identifier = identifier; _redraws = redraws; + _emotes = emotes; + _diagnostics = diagnostics; } public ReadOnlySpan Label @@ -130,6 +154,7 @@ public class DebugTab : Window, ITab return; DrawDebugTabGeneral(); + _diagnostics.DrawDiagnostics(); DrawPerformanceTab(); ImGui.NewLine(); DrawPathResolverDebug(); @@ -357,7 +382,6 @@ public class DebugTab : Window, ITab ImGuiUtil.DrawTableColumn(name); ImGui.TableNextColumn(); } - } } } @@ -372,10 +396,7 @@ public class DebugTab : Window, ITab using (var start = TreeNode("Startup Performance", ImGuiTreeNodeFlags.DefaultOpen)) { if (start) - { - _timer.Draw("##startTimer", TimingExtensions.ToName); ImGui.NewLine(); - } } _performance.Draw("##performance", "Enable Runtime Performance Tracking", TimingExtensions.ToName); @@ -391,22 +412,10 @@ public class DebugTab : Window, ITab if (!table) return; - void DrawSpecial(string name, ActorIdentifier id) - { - if (!id.IsValid) - return; - - ImGuiUtil.DrawTableColumn(name); - ImGuiUtil.DrawTableColumn(string.Empty); - ImGuiUtil.DrawTableColumn(string.Empty); - ImGuiUtil.DrawTableColumn(_actorService.AwaitedService.ToString(id)); - ImGuiUtil.DrawTableColumn(string.Empty); - } - - DrawSpecial("Current Player", _actorService.AwaitedService.GetCurrentPlayer()); - DrawSpecial("Current Inspect", _actorService.AwaitedService.GetInspectPlayer()); - DrawSpecial("Current Card", _actorService.AwaitedService.GetCardPlayer()); - DrawSpecial("Current Glamour", _actorService.AwaitedService.GetGlamourPlayer()); + DrawSpecial("Current Player", _actors.GetCurrentPlayer()); + DrawSpecial("Current Inspect", _actors.GetInspectPlayer()); + DrawSpecial("Current Card", _actors.GetCardPlayer()); + DrawSpecial("Current Glamour", _actors.GetGlamourPlayer()); foreach (var obj in _dalamud.Objects) { @@ -415,11 +424,25 @@ public class DebugTab : Window, ITab ImGuiUtil.DrawTableColumn(obj.Address == nint.Zero ? string.Empty : $"0x{(nint)((Character*)obj.Address)->GameObject.GetDrawObject():X}"); - var identifier = _actorService.AwaitedService.FromObject(obj, false, true, false); - ImGuiUtil.DrawTableColumn(_actorService.AwaitedService.ToString(identifier)); + var identifier = _actors.FromObject(obj, false, true, false); + ImGuiUtil.DrawTableColumn(_actors.ToString(identifier)); var id = obj.ObjectKind == ObjectKind.BattleNpc ? $"{identifier.DataId} | {obj.DataId}" : identifier.DataId.ToString(); ImGuiUtil.DrawTableColumn(id); } + + return; + + void DrawSpecial(string name, ActorIdentifier id) + { + if (!id.IsValid) + return; + + ImGuiUtil.DrawTableColumn(name); + ImGuiUtil.DrawTableColumn(string.Empty); + ImGuiUtil.DrawTableColumn(string.Empty); + ImGuiUtil.DrawTableColumn(_actors.ToString(id)); + ImGuiUtil.DrawTableColumn(string.Empty); + } } /// @@ -616,7 +639,7 @@ public class DebugTab : Window, ITab return; var skips = ImGuiClip.GetNecessarySkips(ImGui.GetTextLineHeightWithSpacing()); - var dummy = ImGuiClip.FilteredClippedDraw(_identifier.AwaitedService.Emotes, skips, + var dummy = ImGuiClip.FilteredClippedDraw(_emotes, skips, p => p.Key.Contains(_emoteSearchFile, StringComparison.OrdinalIgnoreCase) && (_emoteSearchName.Length == 0 || p.Value.Any(s => s.Name.ToDalamudString().TextValue.Contains(_emoteSearchName, StringComparison.OrdinalIgnoreCase))), diff --git a/Penumbra/UI/WindowSystem.cs b/Penumbra/UI/WindowSystem.cs index 25d91644..62ad5a6e 100644 --- a/Penumbra/UI/WindowSystem.cs +++ b/Penumbra/UI/WindowSystem.cs @@ -2,7 +2,7 @@ using Dalamud.Interface; using Dalamud.Interface.Windowing; using Dalamud.Plugin; using Penumbra.UI.AdvancedWindow; -using Penumbra.UI.Tabs; +using Penumbra.UI.Tabs.Debug; namespace Penumbra.UI; diff --git a/Penumbra/Util/PerformanceType.cs b/Penumbra/Util/PerformanceType.cs index 932072f0..b84813cc 100644 --- a/Penumbra/Util/PerformanceType.cs +++ b/Penumbra/Util/PerformanceType.cs @@ -1,23 +1,7 @@ -global using StartTracker = OtterGui.Classes.StartTimeTracker; global using PerformanceTracker = OtterGui.Classes.PerformanceTracker; namespace Penumbra.Util; -public enum StartTimeType -{ - Total, - Identifier, - Stains, - Items, - Actors, - Backup, - Mods, - Collections, - PathResolver, - Interface, - Api, -} - public enum PerformanceType { UiMainWindow, @@ -48,23 +32,6 @@ public enum PerformanceType public static class TimingExtensions { - public static string ToName(this StartTimeType type) - => type switch - { - StartTimeType.Total => "Total Construction", - StartTimeType.Identifier => "Identification Data", - StartTimeType.Stains => "Stain Data", - StartTimeType.Items => "Item Data", - StartTimeType.Actors => "Actor Data", - StartTimeType.Backup => "Checking Backups", - StartTimeType.Mods => "Loading Mods", - StartTimeType.Collections => "Loading Collections", - StartTimeType.Api => "Setting Up API", - StartTimeType.Interface => "Setting Up Interface", - StartTimeType.PathResolver => "Setting Up Path Resolver", - _ => $"Unknown {(int)type}", - }; - public static string ToName(this PerformanceType type) => type switch { diff --git a/Penumbra/packages.lock.json b/Penumbra/packages.lock.json index eed5d7c8..9172bb60 100644 --- a/Penumbra/packages.lock.json +++ b/Penumbra/packages.lock.json @@ -81,7 +81,8 @@ "penumbra.gamedata": { "type": "Project", "dependencies": { - "Penumbra.Api": "[1.0.8, )", + "OtterGui": "[1.0.0, )", + "Penumbra.Api": "[1.0.13, )", "Penumbra.String": "[1.0.4, )" } }, From b494892d62630f1e446c255d5b19abbce0dd64fb Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Mon, 18 Dec 2023 16:50:52 +0100 Subject: [PATCH 05/14] Update for changed GameData. --- Penumbra.GameData | 2 +- Penumbra/Mods/ItemSwap/CustomizationSwap.cs | 2 +- Penumbra/UI/AdvancedWindow/ItemSwapTab.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Penumbra.GameData b/Penumbra.GameData index afc56d9f..0cb10c3d 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit afc56d9f07a2a54ab791a4c85cf627b6f884aec2 +Subproject commit 0cb10c3d915fd9c589195e24d44db3e2ca57841a diff --git a/Penumbra/Mods/ItemSwap/CustomizationSwap.cs b/Penumbra/Mods/ItemSwap/CustomizationSwap.cs index acd6eae9..36237e47 100644 --- a/Penumbra/Mods/ItemSwap/CustomizationSwap.cs +++ b/Penumbra/Mods/ItemSwap/CustomizationSwap.cs @@ -46,7 +46,7 @@ public static class CustomizationSwap SetId idFrom, SetId idTo, byte variant, ref string fileName, ref bool dataWasChanged) { - variant = slot is BodySlot.Face or BodySlot.Zear ? byte.MaxValue : variant; + variant = slot is BodySlot.Face or BodySlot.Ear ? byte.MaxValue : variant; var mtrlFromPath = GamePaths.Character.Mtrl.Path(race, slot, idFrom, fileName, out var gameRaceFrom, out var gameSetIdFrom, variant); var mtrlToPath = GamePaths.Character.Mtrl.Path(race, slot, idTo, fileName, out var gameRaceTo, out var gameSetIdTo, variant); diff --git a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs index 0ec11542..c2910b51 100644 --- a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs +++ b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs @@ -221,7 +221,7 @@ public class ItemSwapTab : IDisposable, ITab _useCurrentCollection ? _collectionManager.Active.Current : null); break; case SwapType.Ears when _targetId > 0 && _sourceId > 0: - _swapData.LoadCustomization(_metaFileManager, BodySlot.Zear, Names.CombinedRace(_currentGender, ModelRace.Viera), + _swapData.LoadCustomization(_metaFileManager, BodySlot.Ear, Names.CombinedRace(_currentGender, ModelRace.Viera), (SetId)_sourceId, (SetId)_targetId, _useCurrentCollection ? _collectionManager.Active.Current : null); From 6dc5916f2b711faa489ac00fe67c352c923c9d61 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Mon, 18 Dec 2023 17:02:08 +0100 Subject: [PATCH 06/14] Fix some issues. --- OtterGui | 2 +- Penumbra.GameData | 2 +- Penumbra/Services/ServiceManager.cs | 5 +++-- Penumbra/Services/StainService.cs | 5 +++-- Penumbra/UI/Tabs/Debug/DebugTab.cs | 4 ++-- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/OtterGui b/OtterGui index bde59c34..ac88abfe 160000 --- a/OtterGui +++ b/OtterGui @@ -1 +1 @@ -Subproject commit bde59c34f7108520002c21cdbf21e8ee5b586944 +Subproject commit ac88abfe9eb0bb5d03aec092dc8f4db642ecbd6a diff --git a/Penumbra.GameData b/Penumbra.GameData index 0cb10c3d..c53e578e 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit 0cb10c3d915fd9c589195e24d44db3e2ca57841a +Subproject commit c53e578e750d26f83a4e81aca1681c5b01d25a5a diff --git a/Penumbra/Services/ServiceManager.cs b/Penumbra/Services/ServiceManager.cs index 049ab328..30f58701 100644 --- a/Penumbra/Services/ServiceManager.cs +++ b/Penumbra/Services/ServiceManager.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using OtterGui.Classes; using OtterGui.Compression; using OtterGui.Log; +using OtterGui.Services; using Penumbra.Api; using Penumbra.Collections.Cache; using Penumbra.Collections.Manager; @@ -74,8 +75,8 @@ public static class ServiceManager private static IServiceCollection AddAsyncServices(this IServiceCollection services) { - foreach (var type in typeof(IDataContainer).Assembly.GetExportedTypes() - .Where(t => t is { IsAbstract: false, IsInterface: false } && t.IsAssignableTo(typeof(IAsyncService)))) + foreach (var type in typeof(ActorManager).Assembly.GetExportedTypes() + .Where(t => t is { IsAbstract: false, IsInterface: false } && t.IsAssignableTo(typeof(IService)))) services.AddSingleton(type); return services; } diff --git a/Penumbra/Services/StainService.cs b/Penumbra/Services/StainService.cs index d11e4d64..714576b2 100644 --- a/Penumbra/Services/StainService.cs +++ b/Penumbra/Services/StainService.cs @@ -3,6 +3,7 @@ using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin; using Dalamud.Plugin.Services; using ImGuiNET; +using OtterGui.Log; using OtterGui.Widgets; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Files; @@ -76,9 +77,9 @@ public class StainService : IDisposable public readonly StmFile StmFile; public readonly StainTemplateCombo TemplateCombo; - public StainService(DalamudPluginInterface pluginInterface, IDataManager dataManager, IPluginLog dalamudLog) + public StainService(DalamudPluginInterface pluginInterface, IDataManager dataManager, Logger logger) { - StainData = new DictStains(pluginInterface, dalamudLog, dataManager); + StainData = new DictStains(pluginInterface, logger, dataManager); StainCombo = new FilterComboColors(140, () => StainData.Value.Prepend(new KeyValuePair(0, ("None", 0, false))).ToList(), Penumbra.Log); diff --git a/Penumbra/UI/Tabs/Debug/DebugTab.cs b/Penumbra/UI/Tabs/Debug/DebugTab.cs index 57ca68fc..34628c60 100644 --- a/Penumbra/UI/Tabs/Debug/DebugTab.cs +++ b/Penumbra/UI/Tabs/Debug/DebugTab.cs @@ -13,12 +13,12 @@ using ImGuiNET; using Microsoft.Extensions.DependencyInjection; using OtterGui; using OtterGui.Classes; +using OtterGui.Services; using OtterGui.Widgets; using Penumbra.Api; using Penumbra.Collections.Manager; using Penumbra.GameData.Actors; using Penumbra.GameData.DataContainers; -using Penumbra.GameData.DataContainers.Bases; using Penumbra.GameData.Files; using Penumbra.Import.Structs; using Penumbra.Import.Textures; @@ -49,7 +49,7 @@ public class Diagnostics(IServiceProvider provider) return; using var table = ImRaii.Table("##data", 4, ImGuiTableFlags.RowBg); - foreach (var type in typeof(IAsyncDataContainer).Assembly.GetTypes() + foreach (var type in typeof(ActorManager).Assembly.GetTypes() .Where(t => t is { IsAbstract: false, IsInterface: false } && t.IsAssignableTo(typeof(IAsyncDataContainer)))) { var container = (IAsyncDataContainer) provider.GetRequiredService(type); From 5d28904bdf7e343f9df883c205cfcd2b10ec8ed3 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Wed, 20 Dec 2023 16:39:26 +0100 Subject: [PATCH 07/14] Update for GameData changes. --- Penumbra.GameData | 2 +- Penumbra/Collections/Cache/EqdpCache.cs | 2 +- Penumbra/Collections/Cache/EstCache.cs | 6 +-- Penumbra/Collections/Cache/MetaCache.cs | 10 ++--- .../ResolveContext.PathResolution.cs | 14 +++---- .../Interop/ResourceTree/ResolveContext.cs | 6 +-- Penumbra/Meta/Files/EqdpFile.cs | 22 +++++----- Penumbra/Meta/Files/EqpGmpFile.cs | 30 ++++++------- Penumbra/Meta/Files/EstFile.cs | 12 +++--- .../Meta/Manipulations/EqdpManipulation.cs | 4 +- .../Meta/Manipulations/EqpManipulation.cs | 4 +- .../Meta/Manipulations/EstManipulation.cs | 5 ++- .../Meta/Manipulations/GmpManipulation.cs | 6 +-- .../Meta/Manipulations/ImcManipulation.cs | 12 +++--- .../Meta/Manipulations/MetaManipulation.cs | 20 ++++++++- Penumbra/Mods/ItemSwap/CustomizationSwap.cs | 6 +-- Penumbra/Mods/ItemSwap/EquipmentSwap.cs | 42 +++++++++---------- Penumbra/Mods/ItemSwap/ItemSwap.cs | 10 ++--- Penumbra/Mods/ItemSwap/ItemSwapContainer.cs | 2 +- Penumbra/Mods/Manager/ModCacheManager.cs | 40 ++++++++++++------ Penumbra/Mods/Subclasses/SubMod.cs | 6 +-- Penumbra/Penumbra.csproj | 5 ++- Penumbra/UI/AdvancedWindow/ItemSwapTab.cs | 16 +++---- Penumbra/UI/ModsTab/ModFileSystemSelector.cs | 1 + 24 files changed, 160 insertions(+), 123 deletions(-) diff --git a/Penumbra.GameData b/Penumbra.GameData index c53e578e..bee73fbe 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit c53e578e750d26f83a4e81aca1681c5b01d25a5a +Subproject commit bee73fbe1e263d81067029ad97c8e4a263c88147 diff --git a/Penumbra/Collections/Cache/EqdpCache.cs b/Penumbra/Collections/Cache/EqdpCache.cs index 3937fa72..6b4982a7 100644 --- a/Penumbra/Collections/Cache/EqdpCache.cs +++ b/Penumbra/Collections/Cache/EqdpCache.cs @@ -45,7 +45,7 @@ public readonly struct EqdpCache : IDisposable foreach (var file in _eqdpFiles.OfType()) { var relevant = CharacterUtility.RelevantIndices[file.Index.Value]; - file.Reset(_eqdpManipulations.Where(m => m.FileIndex() == relevant).Select(m => (SetId)m.SetId)); + file.Reset(_eqdpManipulations.Where(m => m.FileIndex() == relevant).Select(m => (PrimaryId)m.SetId)); } _eqdpManipulations.Clear(); diff --git a/Penumbra/Collections/Cache/EstCache.cs b/Penumbra/Collections/Cache/EstCache.cs index 9e2cdef9..2552cd4a 100644 --- a/Penumbra/Collections/Cache/EstCache.cs +++ b/Penumbra/Collections/Cache/EstCache.cs @@ -74,12 +74,12 @@ public struct EstCache : IDisposable }; } - internal ushort GetEstEntry(MetaFileManager manager, EstManipulation.EstType type, GenderRace genderRace, SetId setId) + internal ushort GetEstEntry(MetaFileManager manager, EstManipulation.EstType type, GenderRace genderRace, PrimaryId primaryId) { var file = GetEstFile(type); return file != null - ? file[genderRace, setId.Id] - : EstFile.GetDefault(manager, type, genderRace, setId); + ? file[genderRace, primaryId.Id] + : EstFile.GetDefault(manager, type, genderRace, primaryId); } public void Reset() diff --git a/Penumbra/Collections/Cache/MetaCache.cs b/Penumbra/Collections/Cache/MetaCache.cs index d5acf249..650bd536 100644 --- a/Penumbra/Collections/Cache/MetaCache.cs +++ b/Penumbra/Collections/Cache/MetaCache.cs @@ -187,17 +187,17 @@ public class MetaCache : IDisposable, IEnumerable _imcCache.GetImcFile(path, out file); - internal EqdpEntry GetEqdpEntry(GenderRace race, bool accessory, SetId setId) + internal EqdpEntry GetEqdpEntry(GenderRace race, bool accessory, PrimaryId primaryId) { var eqdpFile = _eqdpCache.EqdpFile(race, accessory); if (eqdpFile != null) - return setId.Id < eqdpFile.Count ? eqdpFile[setId] : default; + return primaryId.Id < eqdpFile.Count ? eqdpFile[primaryId] : default; else - return Meta.Files.ExpandedEqdpFile.GetDefault(_manager, race, accessory, setId); + return Meta.Files.ExpandedEqdpFile.GetDefault(_manager, race, accessory, primaryId); } - internal ushort GetEstEntry(EstManipulation.EstType type, GenderRace genderRace, SetId setId) - => _estCache.GetEstEntry(_manager, type, genderRace, setId); + internal ushort GetEstEntry(EstManipulation.EstType type, GenderRace genderRace, PrimaryId primaryId) + => _estCache.GetEstEntry(_manager, type, genderRace, primaryId); /// Use this when CharacterUtility becomes ready. private void ApplyStoredManipulations() diff --git a/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs b/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs index 1c9dfaa1..f2059253 100644 --- a/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs +++ b/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs @@ -37,7 +37,7 @@ internal partial record ResolveContext private unsafe GenderRace ResolveModelRaceCode() => ResolveEqdpRaceCode(Slot, Equipment.Set); - private unsafe GenderRace ResolveEqdpRaceCode(EquipSlot slot, SetId setId) + private unsafe GenderRace ResolveEqdpRaceCode(EquipSlot slot, PrimaryId primaryId) { var slotIndex = slot.ToIndex(); if (slotIndex >= 10 || ModelType != ModelType.Human) @@ -55,7 +55,7 @@ internal partial record ResolveContext if (metaCache == null) return GenderRace.MidlanderMale; - var entry = metaCache.GetEqdpEntry(characterRaceCode, accessory, setId); + var entry = metaCache.GetEqdpEntry(characterRaceCode, accessory, primaryId); if (entry.ToBits(slot).Item2) return characterRaceCode; @@ -63,7 +63,7 @@ internal partial record ResolveContext if (fallbackRaceCode == GenderRace.MidlanderMale) return GenderRace.MidlanderMale; - entry = metaCache.GetEqdpEntry(fallbackRaceCode, accessory, setId); + entry = metaCache.GetEqdpEntry(fallbackRaceCode, accessory, primaryId); if (entry.ToBits(slot).Item2) return fallbackRaceCode; @@ -229,7 +229,7 @@ internal partial record ResolveContext return Utf8GamePath.FromString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty; } - private unsafe (GenderRace RaceCode, string Slot, SetId Set) ResolveHumanSkeletonData(uint partialSkeletonIndex) + private unsafe (GenderRace RaceCode, string Slot, PrimaryId Set) ResolveHumanSkeletonData(uint partialSkeletonIndex) { var human = (Human*)CharacterBase.Value; var characterRaceCode = (GenderRace)human->RaceSexId; @@ -262,17 +262,17 @@ internal partial record ResolveContext } } - private unsafe (GenderRace RaceCode, string Slot, SetId Set) ResolveHumanEquipmentSkeletonData(EquipSlot slot, EstManipulation.EstType type) + private unsafe (GenderRace RaceCode, string Slot, PrimaryId Set) ResolveHumanEquipmentSkeletonData(EquipSlot slot, EstManipulation.EstType type) { var human = (Human*)CharacterBase.Value; var equipment = ((CharacterArmor*)&human->Head)[slot.ToIndex()]; return ResolveHumanExtraSkeletonData(ResolveEqdpRaceCode(slot, equipment.Set), type, equipment.Set); } - private unsafe (GenderRace RaceCode, string Slot, SetId Set) ResolveHumanExtraSkeletonData(GenderRace raceCode, EstManipulation.EstType type, SetId set) + private unsafe (GenderRace RaceCode, string Slot, PrimaryId Set) ResolveHumanExtraSkeletonData(GenderRace raceCode, EstManipulation.EstType type, PrimaryId primary) { var metaCache = Global.Collection.MetaCache; - var skeletonSet = metaCache == null ? default : metaCache.GetEstEntry(type, raceCode, set); + var skeletonSet = metaCache == null ? default : metaCache.GetEstEntry(type, raceCode, primary); return (raceCode, EstManipulation.ToName(type), skeletonSet); } diff --git a/Penumbra/Interop/ResourceTree/ResolveContext.cs b/Penumbra/Interop/ResourceTree/ResolveContext.cs index d03ee508..431f1ac0 100644 --- a/Penumbra/Interop/ResourceTree/ResolveContext.cs +++ b/Penumbra/Interop/ResourceTree/ResolveContext.cs @@ -25,8 +25,8 @@ internal record GlobalResolveContext(ObjectIdentification Identifier, ModCollect public readonly Dictionary<(Utf8GamePath, nint), ResourceNode> Nodes = new(128); public unsafe ResolveContext CreateContext(CharacterBase* characterBase, uint slotIndex = 0xFFFFFFFFu, - EquipSlot slot = EquipSlot.Unknown, CharacterArmor equipment = default, WeaponType weaponType = default) - => new(this, characterBase, slotIndex, slot, equipment, weaponType); + EquipSlot slot = EquipSlot.Unknown, CharacterArmor equipment = default, SecondaryId secondaryId = default) + => new(this, characterBase, slotIndex, slot, equipment, secondaryId); } internal partial record ResolveContext( @@ -35,7 +35,7 @@ internal partial record ResolveContext( uint SlotIndex, EquipSlot Slot, CharacterArmor Equipment, - WeaponType WeaponType) + SecondaryId SecondaryId) { private static readonly ByteString ShpkPrefix = ByteString.FromSpanUnsafe("shader/sm5/shpk"u8, true, true, true); diff --git a/Penumbra/Meta/Files/EqdpFile.cs b/Penumbra/Meta/Files/EqdpFile.cs index 8a99225f..c76c4efd 100644 --- a/Penumbra/Meta/Files/EqdpFile.cs +++ b/Penumbra/Meta/Files/EqdpFile.cs @@ -38,7 +38,7 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile public int Count => (Length - DataOffset) / EqdpEntrySize; - public EqdpEntry this[SetId id] + public EqdpEntry this[PrimaryId id] { get { @@ -79,7 +79,7 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile MemoryUtility.MemSet(myDataPtr, 0, Length - (int)((byte*)myDataPtr - Data)); } - public void Reset(IEnumerable entries) + public void Reset(IEnumerable entries) { foreach (var entry in entries) this[entry] = GetDefault(entry); @@ -101,18 +101,18 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile Reset(); } - public EqdpEntry GetDefault(SetId setId) - => GetDefault(Manager, Index, setId); + public EqdpEntry GetDefault(PrimaryId primaryId) + => GetDefault(Manager, Index, primaryId); - public static EqdpEntry GetDefault(MetaFileManager manager, CharacterUtility.InternalIndex idx, SetId setId) - => GetDefault((byte*)manager.CharacterUtility.DefaultResource(idx).Address, setId); + public static EqdpEntry GetDefault(MetaFileManager manager, CharacterUtility.InternalIndex idx, PrimaryId primaryId) + => GetDefault((byte*)manager.CharacterUtility.DefaultResource(idx).Address, primaryId); - public static EqdpEntry GetDefault(byte* data, SetId setId) + public static EqdpEntry GetDefault(byte* data, PrimaryId primaryId) { var blockSize = *(ushort*)(data + IdentifierSize); var totalBlockCount = *(ushort*)(data + IdentifierSize + 2); - var blockIdx = setId.Id / blockSize; + var blockIdx = primaryId.Id / blockSize; if (blockIdx >= totalBlockCount) return 0; @@ -121,9 +121,9 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile return 0; var blockData = (ushort*)(data + IdentifierSize + PreambleSize + totalBlockCount * 2 + block * 2); - return (EqdpEntry)(*(blockData + setId.Id % blockSize)); + return (EqdpEntry)(*(blockData + primaryId.Id % blockSize)); } - public static EqdpEntry GetDefault(MetaFileManager manager, GenderRace raceCode, bool accessory, SetId setId) - => GetDefault(manager, CharacterUtility.ReverseIndices[(int)CharacterUtilityData.EqdpIdx(raceCode, accessory)], setId); + public static EqdpEntry GetDefault(MetaFileManager manager, GenderRace raceCode, bool accessory, PrimaryId primaryId) + => GetDefault(manager, CharacterUtility.ReverseIndices[(int)CharacterUtilityData.EqdpIdx(raceCode, accessory)], primaryId); } diff --git a/Penumbra/Meta/Files/EqpGmpFile.cs b/Penumbra/Meta/Files/EqpGmpFile.cs index 6e9fd010..97f57703 100644 --- a/Penumbra/Meta/Files/EqpGmpFile.cs +++ b/Penumbra/Meta/Files/EqpGmpFile.cs @@ -24,7 +24,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile public ulong ControlBlock => *(ulong*)Data; - protected ulong GetInternal(SetId idx) + protected ulong GetInternal(PrimaryId idx) { return idx.Id switch { @@ -34,7 +34,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile }; } - protected void SetInternal(SetId idx, ulong value) + protected void SetInternal(PrimaryId idx, ulong value) { idx = idx.Id switch { @@ -81,13 +81,13 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile Reset(); } - protected static ulong GetDefaultInternal(MetaFileManager manager, CharacterUtility.InternalIndex fileIndex, SetId setId, ulong def) + protected static ulong GetDefaultInternal(MetaFileManager manager, CharacterUtility.InternalIndex fileIndex, PrimaryId primaryId, ulong def) { var data = (byte*)manager.CharacterUtility.DefaultResource(fileIndex).Address; - if (setId == 0) - setId = 1; + if (primaryId == 0) + primaryId = 1; - var blockIdx = setId.Id / BlockSize; + var blockIdx = primaryId.Id / BlockSize; if (blockIdx >= NumBlocks) return def; @@ -97,7 +97,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile return def; var count = BitOperations.PopCount(control & (blockBit - 1)); - var idx = setId.Id % BlockSize; + var idx = primaryId.Id % BlockSize; var ptr = (ulong*)data + BlockSize * count + idx; return *ptr; } @@ -112,15 +112,15 @@ public sealed class ExpandedEqpFile : ExpandedEqpGmpBase, IEnumerable : base(manager, false) { } - public EqpEntry this[SetId idx] + public EqpEntry this[PrimaryId idx] { get => (EqpEntry)GetInternal(idx); set => SetInternal(idx, (ulong)value); } - public static EqpEntry GetDefault(MetaFileManager manager, SetId setIdx) - => (EqpEntry)GetDefaultInternal(manager, InternalIndex, setIdx, (ulong)Eqp.DefaultEntry); + public static EqpEntry GetDefault(MetaFileManager manager, PrimaryId primaryIdx) + => (EqpEntry)GetDefaultInternal(manager, InternalIndex, primaryIdx, (ulong)Eqp.DefaultEntry); protected override unsafe void SetEmptyBlock(int idx) { @@ -130,7 +130,7 @@ public sealed class ExpandedEqpFile : ExpandedEqpGmpBase, IEnumerable *ptr = (ulong)Eqp.DefaultEntry; } - public void Reset(IEnumerable entries) + public void Reset(IEnumerable entries) { foreach (var entry in entries) this[entry] = GetDefault(Manager, entry); @@ -155,16 +155,16 @@ public sealed class ExpandedGmpFile : ExpandedEqpGmpBase, IEnumerable : base(manager, true) { } - public GmpEntry this[SetId idx] + public GmpEntry this[PrimaryId idx] { get => (GmpEntry)GetInternal(idx); set => SetInternal(idx, (ulong)value); } - public static GmpEntry GetDefault(MetaFileManager manager, SetId setIdx) - => (GmpEntry)GetDefaultInternal(manager, InternalIndex, setIdx, (ulong)GmpEntry.Default); + public static GmpEntry GetDefault(MetaFileManager manager, PrimaryId primaryIdx) + => (GmpEntry)GetDefaultInternal(manager, InternalIndex, primaryIdx, (ulong)GmpEntry.Default); - public void Reset(IEnumerable entries) + public void Reset(IEnumerable entries) { foreach (var entry in entries) this[entry] = GetDefault(Manager, entry); diff --git a/Penumbra/Meta/Files/EstFile.cs b/Penumbra/Meta/Files/EstFile.cs index 2c7409b4..af441b22 100644 --- a/Penumbra/Meta/Files/EstFile.cs +++ b/Penumbra/Meta/Files/EstFile.cs @@ -167,21 +167,21 @@ public sealed unsafe class EstFile : MetaBaseFile public ushort GetDefault(GenderRace genderRace, ushort setId) => GetDefault(Manager, Index, genderRace, setId); - public static ushort GetDefault(MetaFileManager manager, CharacterUtility.InternalIndex index, GenderRace genderRace, SetId setId) + public static ushort GetDefault(MetaFileManager manager, CharacterUtility.InternalIndex index, GenderRace genderRace, PrimaryId primaryId) { var data = (byte*)manager.CharacterUtility.DefaultResource(index).Address; var count = *(int*)data; var span = new ReadOnlySpan(data + 4, count); - var (idx, found) = FindEntry(span, genderRace, setId.Id); + var (idx, found) = FindEntry(span, genderRace, primaryId.Id); if (!found) return 0; return *(ushort*)(data + 4 + count * EntryDescSize + idx * EntrySize); } - public static ushort GetDefault(MetaFileManager manager, MetaIndex metaIndex, GenderRace genderRace, SetId setId) - => GetDefault(manager, CharacterUtility.ReverseIndices[(int)metaIndex], genderRace, setId); + public static ushort GetDefault(MetaFileManager manager, MetaIndex metaIndex, GenderRace genderRace, PrimaryId primaryId) + => GetDefault(manager, CharacterUtility.ReverseIndices[(int)metaIndex], genderRace, primaryId); - public static ushort GetDefault(MetaFileManager manager, EstManipulation.EstType estType, GenderRace genderRace, SetId setId) - => GetDefault(manager, (MetaIndex)estType, genderRace, setId); + public static ushort GetDefault(MetaFileManager manager, EstManipulation.EstType estType, GenderRace genderRace, PrimaryId primaryId) + => GetDefault(manager, (MetaIndex)estType, genderRace, primaryId); } diff --git a/Penumbra/Meta/Manipulations/EqdpManipulation.cs b/Penumbra/Meta/Manipulations/EqdpManipulation.cs index df7ed2e4..0426dfce 100644 --- a/Penumbra/Meta/Manipulations/EqdpManipulation.cs +++ b/Penumbra/Meta/Manipulations/EqdpManipulation.cs @@ -18,13 +18,13 @@ public readonly struct EqdpManipulation : IMetaManipulation [JsonConverter(typeof(StringEnumConverter))] public ModelRace Race { get; private init; } - public SetId SetId { get; private init; } + public PrimaryId SetId { get; private init; } [JsonConverter(typeof(StringEnumConverter))] public EquipSlot Slot { get; private init; } [JsonConstructor] - public EqdpManipulation(EqdpEntry entry, EquipSlot slot, Gender gender, ModelRace race, SetId setId) + public EqdpManipulation(EqdpEntry entry, EquipSlot slot, Gender gender, ModelRace race, PrimaryId setId) { Gender = gender; Race = race; diff --git a/Penumbra/Meta/Manipulations/EqpManipulation.cs b/Penumbra/Meta/Manipulations/EqpManipulation.cs index 4373e8e9..d59938b6 100644 --- a/Penumbra/Meta/Manipulations/EqpManipulation.cs +++ b/Penumbra/Meta/Manipulations/EqpManipulation.cs @@ -15,13 +15,13 @@ public readonly struct EqpManipulation : IMetaManipulation [JsonConverter(typeof(ForceNumericFlagEnumConverter))] public EqpEntry Entry { get; private init; } - public SetId SetId { get; private init; } + public PrimaryId SetId { get; private init; } [JsonConverter(typeof(StringEnumConverter))] public EquipSlot Slot { get; private init; } [JsonConstructor] - public EqpManipulation(EqpEntry entry, EquipSlot slot, SetId setId) + public EqpManipulation(EqpEntry entry, EquipSlot slot, PrimaryId setId) { Slot = slot; SetId = setId; diff --git a/Penumbra/Meta/Manipulations/EstManipulation.cs b/Penumbra/Meta/Manipulations/EstManipulation.cs index 455c39ff..d3c92ad3 100644 --- a/Penumbra/Meta/Manipulations/EstManipulation.cs +++ b/Penumbra/Meta/Manipulations/EstManipulation.cs @@ -36,13 +36,14 @@ public readonly struct EstManipulation : IMetaManipulation [JsonConverter(typeof(StringEnumConverter))] public ModelRace Race { get; private init; } - public SetId SetId { get; private init; } + public PrimaryId SetId { get; private init; } [JsonConverter(typeof(StringEnumConverter))] public EstType Slot { get; private init; } + [JsonConstructor] - public EstManipulation(Gender gender, ModelRace race, EstType slot, SetId setId, ushort entry) + public EstManipulation(Gender gender, ModelRace race, EstType slot, PrimaryId setId, ushort entry) { Entry = entry; Gender = gender; diff --git a/Penumbra/Meta/Manipulations/GmpManipulation.cs b/Penumbra/Meta/Manipulations/GmpManipulation.cs index 928b6f55..ee58295d 100644 --- a/Penumbra/Meta/Manipulations/GmpManipulation.cs +++ b/Penumbra/Meta/Manipulations/GmpManipulation.cs @@ -8,11 +8,11 @@ namespace Penumbra.Meta.Manipulations; [StructLayout(LayoutKind.Sequential, Pack = 1)] public readonly struct GmpManipulation : IMetaManipulation { - public GmpEntry Entry { get; private init; } - public SetId SetId { get; private init; } + public GmpEntry Entry { get; private init; } + public PrimaryId SetId { get; private init; } [JsonConstructor] - public GmpManipulation(GmpEntry entry, SetId setId) + public GmpManipulation(GmpEntry entry, PrimaryId setId) { Entry = entry; SetId = setId; diff --git a/Penumbra/Meta/Manipulations/ImcManipulation.cs b/Penumbra/Meta/Manipulations/ImcManipulation.cs index 391daacc..a1c4b5bf 100644 --- a/Penumbra/Meta/Manipulations/ImcManipulation.cs +++ b/Penumbra/Meta/Manipulations/ImcManipulation.cs @@ -12,10 +12,10 @@ namespace Penumbra.Meta.Manipulations; [StructLayout(LayoutKind.Sequential, Pack = 1)] public readonly struct ImcManipulation : IMetaManipulation { - public ImcEntry Entry { get; private init; } - public SetId PrimaryId { get; private init; } - public SetId SecondaryId { get; private init; } - public Variant Variant { get; private init; } + public ImcEntry Entry { get; private init; } + public PrimaryId PrimaryId { get; private init; } + public PrimaryId SecondaryId { get; private init; } + public Variant Variant { get; private init; } [JsonConverter(typeof(StringEnumConverter))] public ObjectType ObjectType { get; private init; } @@ -26,7 +26,7 @@ public readonly struct ImcManipulation : IMetaManipulation [JsonConverter(typeof(StringEnumConverter))] public BodySlot BodySlot { get; private init; } - public ImcManipulation(EquipSlot equipSlot, ushort variant, SetId primaryId, ImcEntry entry) + public ImcManipulation(EquipSlot equipSlot, ushort variant, PrimaryId primaryId, ImcEntry entry) { Entry = entry; PrimaryId = primaryId; @@ -42,7 +42,7 @@ public readonly struct ImcManipulation : IMetaManipulation // so we change the unused value to something nonsensical in that case, just so they do not compare equal, // and clamp the variant to 255. [JsonConstructor] - internal ImcManipulation(ObjectType objectType, BodySlot bodySlot, SetId primaryId, SetId secondaryId, ushort variant, + internal ImcManipulation(ObjectType objectType, BodySlot bodySlot, PrimaryId primaryId, PrimaryId secondaryId, ushort variant, EquipSlot equipSlot, ImcEntry entry) { Entry = entry; diff --git a/Penumbra/Meta/Manipulations/MetaManipulation.cs b/Penumbra/Meta/Manipulations/MetaManipulation.cs index 94b45cdf..e057d1a4 100644 --- a/Penumbra/Meta/Manipulations/MetaManipulation.cs +++ b/Penumbra/Meta/Manipulations/MetaManipulation.cs @@ -269,5 +269,23 @@ public readonly struct MetaManipulation : IEquatable, ICompara Type.Gmp => $"{Gmp.Entry.Value}", Type.Rsp => $"{Rsp.Entry}", _ => string.Empty, - }; + }; + + public static bool operator ==(MetaManipulation left, MetaManipulation right) + => left.Equals(right); + + public static bool operator !=(MetaManipulation left, MetaManipulation right) + => !(left == right); + + public static bool operator <(MetaManipulation left, MetaManipulation right) + => left.CompareTo(right) < 0; + + public static bool operator <=(MetaManipulation left, MetaManipulation right) + => left.CompareTo(right) <= 0; + + public static bool operator >(MetaManipulation left, MetaManipulation right) + => left.CompareTo(right) > 0; + + public static bool operator >=(MetaManipulation left, MetaManipulation right) + => left.CompareTo(right) >= 0; } diff --git a/Penumbra/Mods/ItemSwap/CustomizationSwap.cs b/Penumbra/Mods/ItemSwap/CustomizationSwap.cs index 36237e47..fc32df0c 100644 --- a/Penumbra/Mods/ItemSwap/CustomizationSwap.cs +++ b/Penumbra/Mods/ItemSwap/CustomizationSwap.cs @@ -12,7 +12,7 @@ public static class CustomizationSwap { /// The .mdl file for customizations is unique per racecode, slot and id, thus the .mdl redirection itself is independent of the mode. public static FileSwap CreateMdl(MetaFileManager manager, Func redirections, BodySlot slot, GenderRace race, - SetId idFrom, SetId idTo) + PrimaryId idFrom, PrimaryId idTo) { if (idFrom.Id > byte.MaxValue) throw new Exception($"The Customization ID {idFrom} is too large for {slot}."); @@ -43,7 +43,7 @@ public static class CustomizationSwap } public static FileSwap CreateMtrl(MetaFileManager manager, Func redirections, BodySlot slot, GenderRace race, - SetId idFrom, SetId idTo, byte variant, + PrimaryId idFrom, PrimaryId idTo, byte variant, ref string fileName, ref bool dataWasChanged) { variant = slot is BodySlot.Face or BodySlot.Ear ? byte.MaxValue : variant; @@ -79,7 +79,7 @@ public static class CustomizationSwap } public static FileSwap CreateTex(MetaFileManager manager, Func redirections, BodySlot slot, GenderRace race, - SetId idFrom, ref MtrlFile.Texture texture, + PrimaryId idFrom, ref MtrlFile.Texture texture, ref bool dataWasChanged) { var path = texture.Path; diff --git a/Penumbra/Mods/ItemSwap/EquipmentSwap.cs b/Penumbra/Mods/ItemSwap/EquipmentSwap.cs index d634349e..f7f82a59 100644 --- a/Penumbra/Mods/ItemSwap/EquipmentSwap.cs +++ b/Penumbra/Mods/ItemSwap/EquipmentSwap.cs @@ -185,13 +185,13 @@ public static class EquipmentSwap } public static MetaSwap? CreateEqdp(MetaFileManager manager, Func redirections, - Func manips, EquipSlot slot, GenderRace gr, SetId idFrom, - SetId idTo, byte mtrlTo) + Func manips, EquipSlot slot, GenderRace gr, PrimaryId idFrom, + PrimaryId idTo, byte mtrlTo) => CreateEqdp(manager, redirections, manips, slot, slot, gr, idFrom, idTo, mtrlTo); public static MetaSwap? CreateEqdp(MetaFileManager manager, Func redirections, - Func manips, EquipSlot slotFrom, EquipSlot slotTo, GenderRace gr, SetId idFrom, - SetId idTo, byte mtrlTo) + Func manips, EquipSlot slotFrom, EquipSlot slotTo, GenderRace gr, PrimaryId idFrom, + PrimaryId idTo, byte mtrlTo) { var (gender, race) = gr.Split(); var eqdpFrom = new EqdpManipulation(ExpandedEqdpFile.GetDefault(manager, gr, slotFrom.IsAccessory(), idFrom), slotFrom, gender, @@ -214,11 +214,11 @@ public static class EquipmentSwap } public static FileSwap CreateMdl(MetaFileManager manager, Func redirections, EquipSlot slot, GenderRace gr, - SetId idFrom, SetId idTo, byte mtrlTo) + PrimaryId idFrom, PrimaryId idTo, byte mtrlTo) => CreateMdl(manager, redirections, slot, slot, gr, idFrom, idTo, mtrlTo); public static FileSwap CreateMdl(MetaFileManager manager, Func redirections, EquipSlot slotFrom, EquipSlot slotTo, - GenderRace gr, SetId idFrom, SetId idTo, byte mtrlTo) + GenderRace gr, PrimaryId idFrom, PrimaryId idTo, byte mtrlTo) { var mdlPathFrom = slotFrom.IsAccessory() ? GamePaths.Accessory.Mdl.Path(idFrom, gr, slotFrom) @@ -236,7 +236,7 @@ public static class EquipmentSwap return mdl; } - private static void LookupItem(EquipItem i, out EquipSlot slot, out SetId modelId, out Variant variant) + private static void LookupItem(EquipItem i, out EquipSlot slot, out PrimaryId modelId, out Variant variant) { slot = i.Type.ToSlot(); if (!slot.IsEquipmentPiece()) @@ -247,7 +247,7 @@ public static class EquipmentSwap } private static (ImcFile, Variant[], EquipItem[]) GetVariants(MetaFileManager manager, ObjectIdentification identifier, EquipSlot slotFrom, - SetId idFrom, SetId idTo, Variant variantFrom) + PrimaryId idFrom, PrimaryId idTo, Variant variantFrom) { var entry = new ImcManipulation(slotFrom, variantFrom.Id, idFrom, default); var imc = new ImcFile(manager, entry); @@ -270,8 +270,8 @@ public static class EquipmentSwap return (imc, variants, items); } - public static MetaSwap? CreateGmp(MetaFileManager manager, Func manips, EquipSlot slot, SetId idFrom, - SetId idTo) + public static MetaSwap? CreateGmp(MetaFileManager manager, Func manips, EquipSlot slot, PrimaryId idFrom, + PrimaryId idTo) { if (slot is not EquipSlot.Head) return null; @@ -283,12 +283,12 @@ public static class EquipmentSwap public static MetaSwap CreateImc(MetaFileManager manager, Func redirections, Func manips, EquipSlot slot, - SetId idFrom, SetId idTo, Variant variantFrom, Variant variantTo, ImcFile imcFileFrom, ImcFile imcFileTo) + PrimaryId idFrom, PrimaryId idTo, Variant variantFrom, Variant variantTo, ImcFile imcFileFrom, ImcFile imcFileTo) => CreateImc(manager, redirections, manips, slot, slot, idFrom, idTo, variantFrom, variantTo, imcFileFrom, imcFileTo); public static MetaSwap CreateImc(MetaFileManager manager, Func redirections, Func manips, - EquipSlot slotFrom, EquipSlot slotTo, SetId idFrom, SetId idTo, + EquipSlot slotFrom, EquipSlot slotTo, PrimaryId idFrom, PrimaryId idTo, Variant variantFrom, Variant variantTo, ImcFile imcFileFrom, ImcFile imcFileTo) { var entryFrom = imcFileFrom.GetEntry(ImcFile.PartIndex(slotFrom), variantFrom); @@ -322,7 +322,7 @@ public static class EquipmentSwap // Example: Abyssos Helm / Body - public static FileSwap? CreateAvfx(MetaFileManager manager, Func redirections, SetId idFrom, SetId idTo, byte vfxId) + public static FileSwap? CreateAvfx(MetaFileManager manager, Func redirections, PrimaryId idFrom, PrimaryId idTo, byte vfxId) { if (vfxId == 0) return null; @@ -340,8 +340,8 @@ public static class EquipmentSwap return avfx; } - public static MetaSwap? CreateEqp(MetaFileManager manager, Func manips, EquipSlot slot, SetId idFrom, - SetId idTo) + public static MetaSwap? CreateEqp(MetaFileManager manager, Func manips, EquipSlot slot, PrimaryId idFrom, + PrimaryId idTo) { if (slot.IsAccessory()) return null; @@ -353,13 +353,13 @@ public static class EquipmentSwap return new MetaSwap(manips, eqpFrom, eqpTo); } - public static FileSwap? CreateMtrl(MetaFileManager manager, Func redirections, EquipSlot slot, SetId idFrom, - SetId idTo, byte variantTo, ref string fileName, + public static FileSwap? CreateMtrl(MetaFileManager manager, Func redirections, EquipSlot slot, PrimaryId idFrom, + PrimaryId idTo, byte variantTo, ref string fileName, ref bool dataWasChanged) => CreateMtrl(manager, redirections, slot, slot, idFrom, idTo, variantTo, ref fileName, ref dataWasChanged); public static FileSwap? CreateMtrl(MetaFileManager manager, Func redirections, EquipSlot slotFrom, EquipSlot slotTo, - SetId idFrom, SetId idTo, byte variantTo, ref string fileName, + PrimaryId idFrom, PrimaryId idTo, byte variantTo, ref string fileName, ref bool dataWasChanged) { var prefix = slotTo.IsAccessory() ? 'a' : 'e'; @@ -397,13 +397,13 @@ public static class EquipmentSwap return mtrl; } - public static FileSwap CreateTex(MetaFileManager manager, Func redirections, char prefix, SetId idFrom, SetId idTo, + public static FileSwap CreateTex(MetaFileManager manager, Func redirections, char prefix, PrimaryId idFrom, PrimaryId idTo, ref MtrlFile.Texture texture, ref bool dataWasChanged) => CreateTex(manager, redirections, prefix, EquipSlot.Unknown, EquipSlot.Unknown, idFrom, idTo, ref texture, ref dataWasChanged); public static FileSwap CreateTex(MetaFileManager manager, Func redirections, char prefix, EquipSlot slotFrom, - EquipSlot slotTo, SetId idFrom, - SetId idTo, ref MtrlFile.Texture texture, ref bool dataWasChanged) + EquipSlot slotTo, PrimaryId idFrom, + PrimaryId idTo, ref MtrlFile.Texture texture, ref bool dataWasChanged) { var path = texture.Path; var addedDashes = false; diff --git a/Penumbra/Mods/ItemSwap/ItemSwap.cs b/Penumbra/Mods/ItemSwap/ItemSwap.cs index 90bee553..b269d89c 100644 --- a/Penumbra/Mods/ItemSwap/ItemSwap.cs +++ b/Penumbra/Mods/ItemSwap/ItemSwap.cs @@ -150,7 +150,7 @@ public static class ItemSwap /// metaChanges is not manipulated, but IReadOnlySet does not support TryGetValue. public static MetaSwap? CreateEst(MetaFileManager manager, Func redirections, Func manips, EstManipulation.EstType type, - GenderRace genderRace, SetId idFrom, SetId idTo, bool ownMdl) + GenderRace genderRace, PrimaryId idFrom, PrimaryId idTo, bool ownMdl) { if (type == 0) return null; @@ -195,7 +195,7 @@ public static class ItemSwap } } - public static string ReplaceAnyId(string path, char idType, SetId id, bool condition = true) + public static string ReplaceAnyId(string path, char idType, PrimaryId id, bool condition = true) => condition ? Regex.Replace(path, $"{idType}\\d{{4}}", $"{idType}{id.Id:D4}") : path; @@ -203,10 +203,10 @@ public static class ItemSwap public static string ReplaceAnyRace(string path, GenderRace to, bool condition = true) => ReplaceAnyId(path, 'c', (ushort)to, condition); - public static string ReplaceAnyBody(string path, BodySlot slot, SetId to, bool condition = true) + public static string ReplaceAnyBody(string path, BodySlot slot, PrimaryId to, bool condition = true) => ReplaceAnyId(path, slot.ToAbbreviation(), to, condition); - public static string ReplaceId(string path, char type, SetId idFrom, SetId idTo, bool condition = true) + public static string ReplaceId(string path, char type, PrimaryId idFrom, PrimaryId idTo, bool condition = true) => condition ? path.Replace($"{type}{idFrom.Id:D4}", $"{type}{idTo.Id:D4}") : path; @@ -219,7 +219,7 @@ public static class ItemSwap public static string ReplaceRace(string path, GenderRace from, GenderRace to, bool condition = true) => ReplaceId(path, 'c', (ushort)from, (ushort)to, condition); - public static string ReplaceBody(string path, BodySlot slot, SetId idFrom, SetId idTo, bool condition = true) + public static string ReplaceBody(string path, BodySlot slot, PrimaryId idFrom, PrimaryId idTo, bool condition = true) => ReplaceId(path, slot.ToAbbreviation(), idFrom, idTo, condition); public static string AddSuffix(string path, string ext, string suffix, bool condition = true) diff --git a/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs b/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs index 9ca02c4e..ff722945 100644 --- a/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs +++ b/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs @@ -145,7 +145,7 @@ public class ItemSwapContainer return ret; } - public bool LoadCustomization(MetaFileManager manager, BodySlot slot, GenderRace race, SetId from, SetId to, + public bool LoadCustomization(MetaFileManager manager, BodySlot slot, GenderRace race, PrimaryId from, PrimaryId to, ModCollection? collection = null) { var pathResolver = PathResolver(collection); diff --git a/Penumbra/Mods/Manager/ModCacheManager.cs b/Penumbra/Mods/Manager/ModCacheManager.cs index 0af78431..a95567ce 100644 --- a/Penumbra/Mods/Manager/ModCacheManager.cs +++ b/Penumbra/Mods/Manager/ModCacheManager.cs @@ -139,7 +139,7 @@ public class ModCacheManager : IDisposable { case ModPathChangeType.Added: case ModPathChangeType.Reloaded: - Refresh(mod); + RefreshWithChangedItems(mod); break; } } @@ -151,10 +151,28 @@ public class ModCacheManager : IDisposable } private void OnModDiscoveryFinished() - => Parallel.ForEach(_modManager, Refresh); + { + if (!_identifier.Awaiter.IsCompletedSuccessfully || _updatingItems) + { + Parallel.ForEach(_modManager, RefreshWithoutChangedItems); + } + else + { + _updatingItems = true; + Parallel.ForEach(_modManager, RefreshWithChangedItems); + _updatingItems = false; + } + } private void OnIdentifierCreation() - => Parallel.ForEach(_modManager, UpdateChangedItems); + { + if (_updatingItems) + return; + + _updatingItems = true; + Parallel.ForEach(_modManager, UpdateChangedItems); + _updatingItems = false; + } private static void UpdateFileCount(Mod mod) => mod.TotalFileCount = mod.AllSubMods.Sum(s => s.Files.Count); @@ -173,15 +191,8 @@ public class ModCacheManager : IDisposable private void UpdateChangedItems(Mod mod) { - if (_updatingItems) - return; - - _updatingItems = true; var changedItems = (SortedList)mod.ChangedItems; changedItems.Clear(); - if (!_identifier.Awaiter.IsCompletedSuccessfully) - return; - foreach (var gamePath in mod.AllSubMods.SelectMany(m => m.Files.Keys.Concat(m.FileSwaps.Keys))) _identifier.Identify(changedItems, gamePath.ToString()); @@ -189,7 +200,6 @@ public class ModCacheManager : IDisposable ComputeChangedItems(_identifier, changedItems, manip); mod.LowerChangedItemsString = string.Join("\0", mod.ChangedItems.Keys.Select(k => k.ToLowerInvariant())); - _updatingItems = false; } private static void UpdateCounts(Mod mod) @@ -210,10 +220,16 @@ public class ModCacheManager : IDisposable } } - private void Refresh(Mod mod) + private void RefreshWithChangedItems(Mod mod) { UpdateTags(mod); UpdateCounts(mod); UpdateChangedItems(mod); } + + private void RefreshWithoutChangedItems(Mod mod) + { + UpdateTags(mod); + UpdateCounts(mod); + } } diff --git a/Penumbra/Mods/Subclasses/SubMod.cs b/Penumbra/Mods/Subclasses/SubMod.cs index 542b14d2..a081f7bc 100644 --- a/Penumbra/Mods/Subclasses/SubMod.cs +++ b/Penumbra/Mods/Subclasses/SubMod.cs @@ -30,9 +30,9 @@ public sealed class SubMod : ISubMod public bool IsDefault => GroupIdx < 0; - public Dictionary FileData = new(); - public Dictionary FileSwapData = new(); - public HashSet ManipulationData = new(); + public Dictionary FileData = []; + public Dictionary FileSwapData = []; + public HashSet ManipulationData = []; public SubMod(IMod parentMod) => ParentMod = parentMod; diff --git a/Penumbra/Penumbra.csproj b/Penumbra/Penumbra.csproj index ec433113..54f7a16f 100644 --- a/Penumbra/Penumbra.csproj +++ b/Penumbra/Penumbra.csproj @@ -91,8 +91,9 @@ - - + + + diff --git a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs index c2910b51..d31a08ae 100644 --- a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs +++ b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs @@ -210,26 +210,26 @@ public class ItemSwapTab : IDisposable, ITab break; case SwapType.Hair when _targetId > 0 && _sourceId > 0: _swapData.LoadCustomization(_metaFileManager, BodySlot.Hair, Names.CombinedRace(_currentGender, _currentRace), - (SetId)_sourceId, - (SetId)_targetId, + (PrimaryId)_sourceId, + (PrimaryId)_targetId, _useCurrentCollection ? _collectionManager.Active.Current : null); break; case SwapType.Face when _targetId > 0 && _sourceId > 0: _swapData.LoadCustomization(_metaFileManager, BodySlot.Face, Names.CombinedRace(_currentGender, _currentRace), - (SetId)_sourceId, - (SetId)_targetId, + (PrimaryId)_sourceId, + (PrimaryId)_targetId, _useCurrentCollection ? _collectionManager.Active.Current : null); break; case SwapType.Ears when _targetId > 0 && _sourceId > 0: _swapData.LoadCustomization(_metaFileManager, BodySlot.Ear, Names.CombinedRace(_currentGender, ModelRace.Viera), - (SetId)_sourceId, - (SetId)_targetId, + (PrimaryId)_sourceId, + (PrimaryId)_targetId, _useCurrentCollection ? _collectionManager.Active.Current : null); break; case SwapType.Tail when _targetId > 0 && _sourceId > 0: _swapData.LoadCustomization(_metaFileManager, BodySlot.Tail, Names.CombinedRace(_currentGender, _currentRace), - (SetId)_sourceId, - (SetId)_targetId, + (PrimaryId)_sourceId, + (PrimaryId)_targetId, _useCurrentCollection ? _collectionManager.Active.Current : null); break; case SwapType.Weapon: break; diff --git a/Penumbra/UI/ModsTab/ModFileSystemSelector.cs b/Penumbra/UI/ModsTab/ModFileSystemSelector.cs index c307f6f4..cf65901e 100644 --- a/Penumbra/UI/ModsTab/ModFileSystemSelector.cs +++ b/Penumbra/UI/ModsTab/ModFileSystemSelector.cs @@ -169,6 +169,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector Date: Wed, 20 Dec 2023 16:43:30 +0100 Subject: [PATCH 08/14] Move signatures and add Footsteps. --- Penumbra.GameData | 2 +- .../PathResolving/AnimationHookService.cs | 26 ++++++++++++++----- .../Structs/ModelResourceHandleUtility.cs | 3 ++- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/Penumbra.GameData b/Penumbra.GameData index bee73fbe..4d3570fd 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit bee73fbe1e263d81067029ad97c8e4a263c88147 +Subproject commit 4d3570fd47d78dbc49cf5e41fd3545a533ef9e81 diff --git a/Penumbra/Interop/PathResolving/AnimationHookService.cs b/Penumbra/Interop/PathResolving/AnimationHookService.cs index 7fa0ed35..d13ef7f2 100644 --- a/Penumbra/Interop/PathResolving/AnimationHookService.cs +++ b/Penumbra/Interop/PathResolving/AnimationHookService.cs @@ -55,6 +55,7 @@ public unsafe class AnimationHookService : IDisposable _unkParasolAnimationHook.Enable(); _dismountHook.Enable(); _apricotListenerSoundPlayHook.Enable(); + _footStepHook.Enable(); } public bool HandleFiles(ResourceType type, Utf8GamePath _, out ResolveData resolveData) @@ -113,6 +114,7 @@ public unsafe class AnimationHookService : IDisposable _unkParasolAnimationHook.Dispose(); _dismountHook.Dispose(); _apricotListenerSoundPlayHook.Dispose(); + _footStepHook.Dispose(); } /// Characters load some of their voice lines or whatever with this function. @@ -324,7 +326,7 @@ public unsafe class AnimationHookService : IDisposable private delegate void UnkMountAnimationDelegate(DrawObject* drawObject, uint unk1, byte unk2, uint unk3); - [Signature("48 89 5C 24 ?? 48 89 6C 24 ?? 89 54 24", DetourName = nameof(UnkMountAnimationDetour))] + [Signature(Sigs.UnkMountAnimation, DetourName = nameof(UnkMountAnimationDetour))] private readonly Hook _unkMountAnimationHook = null!; private void UnkMountAnimationDetour(DrawObject* drawObject, uint unk1, byte unk2, uint unk3) @@ -335,10 +337,9 @@ public unsafe class AnimationHookService : IDisposable _animationLoadData.Value = last; } - private delegate void UnkParasolAnimationDelegate(DrawObject* drawObject, int unk1); - [Signature("48 89 5C 24 ?? 48 89 74 24 ?? 89 54 24 ?? 57 48 83 EC ?? 48 8B F9", DetourName = nameof(UnkParasolAnimationDetour))] + [Signature(Sigs.UnkParasolAnimation, DetourName = nameof(UnkParasolAnimationDetour))] private readonly Hook _unkParasolAnimationHook = null!; private void UnkParasolAnimationDetour(DrawObject* drawObject, int unk1) @@ -347,9 +348,9 @@ public unsafe class AnimationHookService : IDisposable _animationLoadData.Value = _collectionResolver.IdentifyCollection(drawObject, true); _unkParasolAnimationHook.Original(drawObject, unk1); _animationLoadData.Value = last; - } + } - [Signature("E8 ?? ?? ?? ?? F6 43 ?? ?? 74 ?? 48 8B CB", DetourName = nameof(DismountDetour))] + [Signature(Sigs.Dismount, DetourName = nameof(DismountDetour))] private readonly Hook _dismountHook = null!; private delegate void DismountDelegate(nint a1, nint a2); @@ -375,7 +376,7 @@ public unsafe class AnimationHookService : IDisposable _animationLoadData.Value = last; } - [Signature("48 89 6C 24 ?? 41 54 41 56 41 57 48 81 EC", DetourName = nameof(ApricotListenerSoundPlayDetour))] + [Signature(Sigs.ApricotListenerSoundPlay, DetourName = nameof(ApricotListenerSoundPlayDetour))] private readonly Hook _apricotListenerSoundPlayHook = null!; private delegate nint ApricotListenerSoundPlayDelegate(nint a1, nint a2, nint a3, nint a4, nint a5, nint a6); @@ -406,4 +407,17 @@ public unsafe class AnimationHookService : IDisposable _animationLoadData.Value = last; return ret; } + + private delegate void FootStepDelegate(GameObject* gameObject, int id, int unk); + + [Signature(Sigs.FootStepSound, DetourName = nameof(FootStepDetour))] + private readonly Hook _footStepHook = null!; + + private void FootStepDetour(GameObject* gameObject, int id, int unk) + { + var last = _animationLoadData.Value; + _animationLoadData.Value = _collectionResolver.IdentifyCollection(gameObject, true); + _footStepHook.Original(gameObject, id, unk); + _animationLoadData.Value = last; + } } diff --git a/Penumbra/Interop/Structs/ModelResourceHandleUtility.cs b/Penumbra/Interop/Structs/ModelResourceHandleUtility.cs index 008cd59a..bcfa2fa2 100644 --- a/Penumbra/Interop/Structs/ModelResourceHandleUtility.cs +++ b/Penumbra/Interop/Structs/ModelResourceHandleUtility.cs @@ -1,6 +1,7 @@ using Dalamud.Plugin.Services; using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; +using Penumbra.GameData; namespace Penumbra.Interop.Structs; @@ -10,7 +11,7 @@ public class ModelResourceHandleUtility public ModelResourceHandleUtility(IGameInteropProvider interop) => interop.InitializeFromAttributes(this); - [Signature("E8 ?? ?? ?? ?? 44 8B CD 48 89 44 24")] + [Signature(Sigs.GetMaterialFileNameBySlot)] private static nint _getMaterialFileNameBySlot = nint.Zero; public static unsafe byte* GetMaterialFileNameBySlot(ModelResourceHandle* handle, uint slot) From f022d2be6480f459e2163f8dc8bfff50c288636f Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Wed, 20 Dec 2023 18:47:30 +0100 Subject: [PATCH 09/14] Rework DalamudServices, --- OtterGui | 2 +- Penumbra/Api/IpcTester.cs | 85 ++++++++++--------- Penumbra/Api/PenumbraApi.cs | 47 +++++----- Penumbra/Api/PenumbraIpcProviders.cs | 8 +- Penumbra/Import/Textures/TexFileParser.cs | 6 +- .../ResourceLoading/CreateFileWHook.cs | 6 +- Penumbra/Penumbra.cs | 60 +++++++------ Penumbra/Penumbra.json | 2 +- Penumbra/Services/DalamudServices.cs | 81 +++++++----------- .../{ServiceManager.cs => ServiceManagerA.cs} | 74 ++++++---------- Penumbra/Services/ValidityChecker.cs | 2 +- .../ModEditWindow.Materials.MtrlTab.cs | 8 +- .../ModEditWindow.QuickImport.cs | 2 +- .../AdvancedWindow/ModEditWindow.Textures.cs | 2 +- Penumbra/UI/AdvancedWindow/ModEditWindow.cs | 20 +++-- Penumbra/UI/ModsTab/ModFileSystemSelector.cs | 4 +- Penumbra/UI/Tabs/Debug/DebugTab.cs | 24 +++--- Penumbra/UI/Tabs/SettingsTab.cs | 42 +++++---- Penumbra/packages.lock.json | 5 +- 19 files changed, 230 insertions(+), 250 deletions(-) rename Penumbra/Services/{ServiceManager.cs => ServiceManagerA.cs} (72%) diff --git a/OtterGui b/OtterGui index ac88abfe..197d23ee 160000 --- a/OtterGui +++ b/OtterGui @@ -1 +1 @@ -Subproject commit ac88abfe9eb0bb5d03aec092dc8f4db642ecbd6a +Subproject commit 197d23eee167c232000f22ef40a7a2bded913b6c diff --git a/Penumbra/Api/IpcTester.cs b/Penumbra/Api/IpcTester.cs index f7b740b9..aea95156 100644 --- a/Penumbra/Api/IpcTester.cs +++ b/Penumbra/Api/IpcTester.cs @@ -40,23 +40,24 @@ public class IpcTester : IDisposable private readonly Temporary _temporary; private readonly ResourceTree _resourceTree; - public IpcTester(Configuration config, DalamudServices dalamud, PenumbraIpcProviders ipcProviders, ModManager modManager, - CollectionManager collections, TempModManager tempMods, TempCollectionManager tempCollections, SaveService saveService) + public IpcTester(Configuration config, DalamudPluginInterface pi, IObjectTable objects, IClientState clientState, + PenumbraIpcProviders ipcProviders, ModManager modManager, CollectionManager collections, TempModManager tempMods, + TempCollectionManager tempCollections, SaveService saveService) { _ipcProviders = ipcProviders; - _pluginState = new PluginState(dalamud.PluginInterface); - _ipcConfiguration = new IpcConfiguration(dalamud.PluginInterface); - _ui = new Ui(dalamud.PluginInterface); - _redrawing = new Redrawing(dalamud); - _gameState = new GameState(dalamud.PluginInterface); - _resolve = new Resolve(dalamud.PluginInterface); - _collections = new Collections(dalamud.PluginInterface); - _meta = new Meta(dalamud.PluginInterface); - _mods = new Mods(dalamud.PluginInterface); - _modSettings = new ModSettings(dalamud.PluginInterface); - _editing = new Editing(dalamud.PluginInterface); - _temporary = new Temporary(dalamud.PluginInterface, modManager, collections, tempMods, tempCollections, saveService, config); - _resourceTree = new ResourceTree(dalamud.PluginInterface, dalamud.Objects); + _pluginState = new PluginState(pi); + _ipcConfiguration = new IpcConfiguration(pi); + _ui = new Ui(pi); + _redrawing = new Redrawing(pi, objects, clientState); + _gameState = new GameState(pi); + _resolve = new Resolve(pi); + _collections = new Collections(pi); + _meta = new Meta(pi); + _mods = new Mods(pi); + _modSettings = new ModSettings(pi); + _editing = new Editing(pi); + _temporary = new Temporary(pi, modManager, collections, tempMods, tempCollections, saveService, config); + _resourceTree = new ResourceTree(pi, objects); UnsubscribeEvents(); } @@ -398,17 +399,21 @@ public class IpcTester : IDisposable private class Redrawing { - private readonly DalamudServices _dalamud; + private readonly DalamudPluginInterface _pi; + private readonly IClientState _clientState; + private readonly IObjectTable _objects; public readonly EventSubscriber Redrawn; private string _redrawName = string.Empty; private int _redrawIndex = 0; private string _lastRedrawnString = "None"; - public Redrawing(DalamudServices dalamud) + public Redrawing(DalamudPluginInterface pi, IObjectTable objects, IClientState clientState) { - _dalamud = dalamud; - Redrawn = Ipc.GameObjectRedrawn.Subscriber(_dalamud.PluginInterface, SetLastRedrawn); + _pi = pi; + _objects = objects; + _clientState = clientState; + Redrawn = Ipc.GameObjectRedrawn.Subscriber(_pi, SetLastRedrawn); } public void Draw() @@ -426,25 +431,25 @@ public class IpcTester : IDisposable ImGui.InputTextWithHint("##redrawName", "Name...", ref _redrawName, 32); ImGui.SameLine(); if (ImGui.Button("Redraw##Name")) - Ipc.RedrawObjectByName.Subscriber(_dalamud.PluginInterface).Invoke(_redrawName, RedrawType.Redraw); + Ipc.RedrawObjectByName.Subscriber(_pi).Invoke(_redrawName, RedrawType.Redraw); DrawIntro(Ipc.RedrawObject.Label, "Redraw Player Character"); - if (ImGui.Button("Redraw##pc") && _dalamud.ClientState.LocalPlayer != null) - Ipc.RedrawObject.Subscriber(_dalamud.PluginInterface).Invoke(_dalamud.ClientState.LocalPlayer, RedrawType.Redraw); + if (ImGui.Button("Redraw##pc") && _clientState.LocalPlayer != null) + Ipc.RedrawObject.Subscriber(_pi).Invoke(_clientState.LocalPlayer, RedrawType.Redraw); DrawIntro(Ipc.RedrawObjectByIndex.Label, "Redraw by Index"); var tmp = _redrawIndex; ImGui.SetNextItemWidth(100 * UiHelpers.Scale); - if (ImGui.DragInt("##redrawIndex", ref tmp, 0.1f, 0, _dalamud.Objects.Length)) - _redrawIndex = Math.Clamp(tmp, 0, _dalamud.Objects.Length); + if (ImGui.DragInt("##redrawIndex", ref tmp, 0.1f, 0, _objects.Length)) + _redrawIndex = Math.Clamp(tmp, 0, _objects.Length); ImGui.SameLine(); if (ImGui.Button("Redraw##Index")) - Ipc.RedrawObjectByIndex.Subscriber(_dalamud.PluginInterface).Invoke(_redrawIndex, RedrawType.Redraw); + Ipc.RedrawObjectByIndex.Subscriber(_pi).Invoke(_redrawIndex, RedrawType.Redraw); DrawIntro(Ipc.RedrawAll.Label, "Redraw All"); if (ImGui.Button("Redraw##All")) - Ipc.RedrawAll.Subscriber(_dalamud.PluginInterface).Invoke(RedrawType.Redraw); + Ipc.RedrawAll.Subscriber(_pi).Invoke(RedrawType.Redraw); DrawIntro(Ipc.GameObjectRedrawn.Label, "Last Redrawn Object:"); ImGui.TextUnformatted(_lastRedrawnString); @@ -453,12 +458,12 @@ public class IpcTester : IDisposable private void SetLastRedrawn(IntPtr address, int index) { if (index < 0 - || index > _dalamud.Objects.Length + || index > _objects.Length || address == IntPtr.Zero - || _dalamud.Objects[index]?.Address != address) + || _objects[index]?.Address != address) _lastRedrawnString = "Invalid"; - _lastRedrawnString = $"{_dalamud.Objects[index]!.Name} (0x{address:X}, {index})"; + _lastRedrawnString = $"{_objects[index]!.Name} (0x{address:X}, {index})"; } } @@ -1529,7 +1534,7 @@ public class IpcTester : IDisposable if (ImGui.Button("Get##GameObjectResourceTrees")) { var gameObjects = GetSelectedGameObjects(); - var subscriber = Ipc.GetGameObjectResourceTrees.Subscriber(_pi); + var subscriber = Ipc.GetGameObjectResourceTrees.Subscriber(_pi); _stopwatch.Restart(); var trees = subscriber.Invoke(_withUIData, gameObjects); @@ -1563,7 +1568,7 @@ public class IpcTester : IDisposable DrawPopup(nameof(Ipc.GetGameObjectResourcesOfType), ref _lastGameObjectResourcesOfType, DrawResourcesOfType, _lastCallDuration); DrawPopup(nameof(Ipc.GetPlayerResourcesOfType), ref _lastPlayerResourcesOfType, DrawResourcesOfType, _lastCallDuration); - DrawPopup(nameof(Ipc.GetGameObjectResourceTrees), ref _lastGameObjectResourceTrees, DrawResourceTrees, _lastCallDuration); + DrawPopup(nameof(Ipc.GetGameObjectResourceTrees), ref _lastGameObjectResourceTrees, DrawResourceTrees, _lastCallDuration); DrawPopup(nameof(Ipc.GetPlayerResourceTrees), ref _lastPlayerResourceTrees, DrawResourceTrees!, _lastCallDuration); } @@ -1695,9 +1700,10 @@ public class IpcTester : IDisposable { ImGui.TableSetupColumn("Type", ImGuiTableColumnFlags.WidthStretch, 0.5f); } - ImGui.TableSetupColumn("Game Path", ImGuiTableColumnFlags.WidthStretch, 0.5f); - ImGui.TableSetupColumn("Actual Path", ImGuiTableColumnFlags.WidthStretch, 0.5f); - ImGui.TableSetupColumn("Object Address", ImGuiTableColumnFlags.WidthStretch, 0.2f); + + ImGui.TableSetupColumn("Game Path", ImGuiTableColumnFlags.WidthStretch, 0.5f); + ImGui.TableSetupColumn("Actual Path", ImGuiTableColumnFlags.WidthStretch, 0.5f); + ImGui.TableSetupColumn("Object Address", ImGuiTableColumnFlags.WidthStretch, 0.2f); ImGui.TableSetupColumn("Resource Handle", ImGuiTableColumnFlags.WidthStretch, 0.2f); ImGui.TableHeadersRow(); @@ -1707,10 +1713,10 @@ public class IpcTester : IDisposable ImGui.TableNextColumn(); var hasChildren = node.Children.Any(); using var treeNode = ImRaii.TreeNode( - $"{(_withUIData ? (node.Name ?? "Unknown") : node.Type)}##{node.ObjectAddress:X8}", - hasChildren ? - ImGuiTreeNodeFlags.SpanFullWidth : - (ImGuiTreeNodeFlags.SpanFullWidth | ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.NoTreePushOnOpen)); + $"{(_withUIData ? node.Name ?? "Unknown" : node.Type)}##{node.ObjectAddress:X8}", + hasChildren + ? ImGuiTreeNodeFlags.SpanFullWidth + : ImGuiTreeNodeFlags.SpanFullWidth | ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.NoTreePushOnOpen); if (_withUIData) { ImGui.TableNextColumn(); @@ -1718,6 +1724,7 @@ public class IpcTester : IDisposable ImGui.TableNextColumn(); TextUnformattedMono(node.Icon.ToString()); } + ImGui.TableNextColumn(); ImGui.TextUnformatted(node.GamePath ?? "Unknown"); ImGui.TableNextColumn(); @@ -1728,10 +1735,8 @@ public class IpcTester : IDisposable TextUnformattedMono($"0x{node.ResourceHandle:X8}"); if (treeNode) - { foreach (var child in node.Children) DrawNode(child); - } } foreach (var node in tree.Nodes) diff --git a/Penumbra/Api/PenumbraApi.cs b/Penumbra/Api/PenumbraApi.cs index 97e69089..b7a46ae2 100644 --- a/Penumbra/Api/PenumbraApi.cs +++ b/Penumbra/Api/PenumbraApi.cs @@ -1,4 +1,5 @@ using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Plugin.Services; using Lumina.Data; using Newtonsoft.Json; using OtterGui; @@ -88,11 +89,13 @@ public class PenumbraApi : IDisposable, IPenumbraApi private CommunicatorService _communicator; private Lumina.GameData? _lumina; + private IDataManager _gameData; + private IFramework _framework; + private IObjectTable _objects; private ModManager _modManager; private ResourceLoader _resourceLoader; private Configuration _config; private CollectionManager _collectionManager; - private DalamudServices _dalamud; private TempCollectionManager _tempCollections; private TempModManager _tempMods; private ActorManager _actors; @@ -106,18 +109,20 @@ public class PenumbraApi : IDisposable, IPenumbraApi private TextureManager _textureManager; private ResourceTreeFactory _resourceTreeFactory; - public unsafe PenumbraApi(CommunicatorService communicator, ModManager modManager, ResourceLoader resourceLoader, - Configuration config, CollectionManager collectionManager, DalamudServices dalamud, TempCollectionManager tempCollections, - TempModManager tempMods, ActorManager actors, CollectionResolver collectionResolver, CutsceneService cutsceneService, - ModImportManager modImportManager, CollectionEditor collectionEditor, RedrawService redrawService, ModFileSystem modFileSystem, - ConfigWindow configWindow, TextureManager textureManager, ResourceTreeFactory resourceTreeFactory) + public unsafe PenumbraApi(CommunicatorService communicator, IDataManager gameData, IFramework framework, IObjectTable objects, + ModManager modManager, ResourceLoader resourceLoader, Configuration config, CollectionManager collectionManager, + TempCollectionManager tempCollections, TempModManager tempMods, ActorManager actors, CollectionResolver collectionResolver, + CutsceneService cutsceneService, ModImportManager modImportManager, CollectionEditor collectionEditor, RedrawService redrawService, + ModFileSystem modFileSystem, ConfigWindow configWindow, TextureManager textureManager, ResourceTreeFactory resourceTreeFactory) { _communicator = communicator; + _gameData = gameData; + _framework = framework; + _objects = objects; _modManager = modManager; _resourceLoader = resourceLoader; _config = config; _collectionManager = collectionManager; - _dalamud = dalamud; _tempCollections = tempCollections; _tempMods = tempMods; _actors = actors; @@ -130,7 +135,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi _configWindow = configWindow; _textureManager = textureManager; _resourceTreeFactory = resourceTreeFactory; - _lumina = _dalamud.GameData.GameData; + _lumina = gameData.GameData; _resourceLoader.ResourceLoaded += OnResourceLoaded; _communicator.ModPathChanged.Subscribe(ModPathChangeSubscriber, ModPathChanged.Priority.Api); @@ -153,7 +158,6 @@ public class PenumbraApi : IDisposable, IPenumbraApi _resourceLoader = null!; _config = null!; _collectionManager = null!; - _dalamud = null!; _tempCollections = null!; _tempMods = null!; _actors = null!; @@ -166,6 +170,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi _configWindow = null!; _textureManager = null!; _resourceTreeFactory = null!; + _framework = null!; } public event ChangedItemClick? ChangedItemClicked @@ -399,7 +404,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi return await Task.Run(async () => { - var playerCollection = await _dalamud.Framework.RunOnFrameworkThread(_collectionResolver.PlayerCollection).ConfigureAwait(false); + var playerCollection = await _framework.RunOnFrameworkThread(_collectionResolver.PlayerCollection).ConfigureAwait(false); var forwardTask = Task.Run(() => { var forwardRet = new string[forward.Length]; @@ -851,7 +856,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi { CheckInitialized(); - if (!ActorManager.VerifyPlayerName(character.AsSpan()) || tag.Length == 0) + if (!ActorIdentifierFactory.VerifyPlayerName(character.AsSpan()) || tag.Length == 0) return (PenumbraApiEc.InvalidArgument, string.Empty); var identifier = NameToIdentifier(character, ushort.MaxValue); @@ -889,10 +894,10 @@ public class PenumbraApi : IDisposable, IPenumbraApi { CheckInitialized(); - if (actorIndex < 0 || actorIndex >= _dalamud.Objects.Length) + if (actorIndex < 0 || actorIndex >= _objects.Length) return PenumbraApiEc.InvalidArgument; - var identifier = _actors.FromObject(_dalamud.Objects[actorIndex], false, false, true); + var identifier = _actors.FromObject(_objects[actorIndex], false, false, true); if (!identifier.IsValid) return PenumbraApiEc.InvalidArgument; @@ -1037,7 +1042,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi public IReadOnlyDictionary?[] GetGameObjectResourcePaths(ushort[] gameObjects) { - var characters = gameObjects.Select(index => _dalamud.Objects[index]).OfType(); + var characters = gameObjects.Select(index => _objects[index]).OfType(); var resourceTrees = _resourceTreeFactory.FromCharacters(characters, 0); var pathDictionaries = ResourceTreeApiHelper.GetResourcePathDictionaries(resourceTrees); @@ -1055,7 +1060,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi public IReadOnlyDictionary?[] GetGameObjectResourcesOfType(ResourceType type, bool withUIData, params ushort[] gameObjects) { - var characters = gameObjects.Select(index => _dalamud.Objects[index]).OfType(); + var characters = gameObjects.Select(index => _objects[index]).OfType(); var resourceTrees = _resourceTreeFactory.FromCharacters(characters, withUIData ? ResourceTreeFactory.Flags.WithUiData : 0); var resDictionaries = ResourceTreeApiHelper.GetResourcesOfType(resourceTrees, type); @@ -1074,7 +1079,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi public Ipc.ResourceTree?[] GetGameObjectResourceTrees(bool withUIData, params ushort[] gameObjects) { - var characters = gameObjects.Select(index => _dalamud.Objects[index]).OfType(); + var characters = gameObjects.Select(index => _objects[index]).OfType(); var resourceTrees = _resourceTreeFactory.FromCharacters(characters, withUIData ? ResourceTreeFactory.Flags.WithUiData : 0); var resDictionary = ResourceTreeApiHelper.EncapsulateResourceTrees(resourceTrees); @@ -1126,10 +1131,10 @@ public class PenumbraApi : IDisposable, IPenumbraApi private unsafe bool AssociatedCollection(int gameObjectIdx, out ModCollection collection) { collection = _collectionManager.Active.Default; - if (gameObjectIdx < 0 || gameObjectIdx >= _dalamud.Objects.Length) + if (gameObjectIdx < 0 || gameObjectIdx >= _objects.Length) return false; - var ptr = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)_dalamud.Objects.GetObjectAddress(gameObjectIdx); + var ptr = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)_objects.GetObjectAddress(gameObjectIdx); var data = _collectionResolver.IdentifyCollection(ptr, false); if (data.Valid) collection = data.ModCollection; @@ -1140,10 +1145,10 @@ public class PenumbraApi : IDisposable, IPenumbraApi [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private unsafe ActorIdentifier AssociatedIdentifier(int gameObjectIdx) { - if (gameObjectIdx < 0 || gameObjectIdx >= _dalamud.Objects.Length) + if (gameObjectIdx < 0 || gameObjectIdx >= _objects.Length) return ActorIdentifier.Invalid; - var ptr = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)_dalamud.Objects.GetObjectAddress(gameObjectIdx); + var ptr = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)_objects.GetObjectAddress(gameObjectIdx); return _actors.FromObject(ptr, out _, false, true, true); } @@ -1168,7 +1173,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi if (Path.IsPathRooted(resolvedPath)) return _lumina?.GetFileFromDisk(resolvedPath); - return _dalamud.GameData.GetFile(resolvedPath); + return _gameData.GetFile(resolvedPath); } catch (Exception e) { diff --git a/Penumbra/Api/PenumbraIpcProviders.cs b/Penumbra/Api/PenumbraIpcProviders.cs index a564588b..1df90dd9 100644 --- a/Penumbra/Api/PenumbraIpcProviders.cs +++ b/Penumbra/Api/PenumbraIpcProviders.cs @@ -15,7 +15,6 @@ using CurrentSettings = ValueTuple GetGameObjectResourceTrees; internal readonly FuncProvider> GetPlayerResourceTrees; - public PenumbraIpcProviders(DalamudServices dalamud, IPenumbraApi api, ModManager modManager, CollectionManager collections, + public PenumbraIpcProviders(DalamudPluginInterface pi, IPenumbraApi api, ModManager modManager, CollectionManager collections, TempModManager tempMods, TempCollectionManager tempCollections, SaveService saveService, Configuration config) { - var pi = dalamud.PluginInterface; Api = api; // Plugin State @@ -260,15 +258,11 @@ public class PenumbraIpcProviders : IDisposable GetGameObjectResourceTrees = Ipc.GetGameObjectResourceTrees.Provider(pi, Api.GetGameObjectResourceTrees); GetPlayerResourceTrees = Ipc.GetPlayerResourceTrees.Provider(pi, Api.GetPlayerResourceTrees); - Tester = new IpcTester(config, dalamud, this, modManager, collections, tempMods, tempCollections, saveService); - Initialized.Invoke(); } public void Dispose() { - Tester.Dispose(); - // Plugin State Initialized.Dispose(); ApiVersion.Dispose(); diff --git a/Penumbra/Import/Textures/TexFileParser.cs b/Penumbra/Import/Textures/TexFileParser.cs index 629cdff5..6f854022 100644 --- a/Penumbra/Import/Textures/TexFileParser.cs +++ b/Penumbra/Import/Textures/TexFileParser.cs @@ -44,8 +44,8 @@ public static class TexFileParser if (offset == 0) return i; - var requiredSize = width * height * bits / 8; - if (offset + requiredSize > data.Length) + var Size = width * height * bits / 8; + if (offset + Size > data.Length) return i; var diff = offset - lastOffset; @@ -55,7 +55,7 @@ public static class TexFileParser width = Math.Max(width / 2, minSize); height = Math.Max(height / 2, minSize); lastOffset = offset; - lastSize = requiredSize; + lastSize = Size; } return 13; diff --git a/Penumbra/Interop/ResourceLoading/CreateFileWHook.cs b/Penumbra/Interop/ResourceLoading/CreateFileWHook.cs index b77ac1e0..7d94b1d5 100644 --- a/Penumbra/Interop/ResourceLoading/CreateFileWHook.cs +++ b/Penumbra/Interop/ResourceLoading/CreateFileWHook.cs @@ -13,7 +13,7 @@ namespace Penumbra.Interop.ResourceLoading; /// public unsafe class CreateFileWHook : IDisposable { - public const int RequiredSize = 28; + public const int Size = 28; public CreateFileWHook(IGameInteropProvider interop) { @@ -57,8 +57,8 @@ public unsafe class CreateFileWHook : IDisposable ptr[22] = (byte)(l >> 16); ptr[24] = (byte)(l >> 24); - ptr[RequiredSize - 2] = 0; - ptr[RequiredSize - 1] = 0; + ptr[Size - 2] = 0; + ptr[Size - 1] = 0; } public void Dispose() diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs index 9be40e66..900d2770 100644 --- a/Penumbra/Penumbra.cs +++ b/Penumbra/Penumbra.cs @@ -1,10 +1,9 @@ using Dalamud.Plugin; -using Dalamud.Plugin.Services; using ImGuiNET; using Lumina.Excel.GeneratedSheets; -using Microsoft.Extensions.DependencyInjection; using OtterGui; using OtterGui.Log; +using OtterGui.Services; using Penumbra.Api; using Penumbra.Api.Enums; using Penumbra.Collections; @@ -19,7 +18,6 @@ using Penumbra.UI.Tabs; using ChangedItemClick = Penumbra.Communication.ChangedItemClick; using ChangedItemHover = Penumbra.Communication.ChangedItemHover; using OtterGui.Tasks; -using Penumbra.GameData.Data; using Penumbra.GameData.Enums; using Penumbra.Interop.Structs; using Penumbra.UI; @@ -48,40 +46,40 @@ public class Penumbra : IDalamudPlugin private PenumbraWindowSystem? _windowSystem; private bool _disposed; - private readonly ServiceProvider _services; + private readonly ServiceManager _services; public Penumbra(DalamudPluginInterface pluginInterface) { try { - _services = ServiceManager.CreateProvider(this, pluginInterface, Log); - Messager = _services.GetRequiredService(); - _validityChecker = _services.GetRequiredService(); - var startup = _services.GetRequiredService().GetDalamudConfig(DalamudServices.WaitingForPluginsOption, out bool s) + _services = ServiceManagerA.CreateProvider(this, pluginInterface, Log); + Messager = _services.GetService(); + _validityChecker = _services.GetService(); + var startup = _services.GetService().GetDalamudConfig(DalamudConfigService.WaitingForPluginsOption, out bool s) ? s.ToString() : "Unknown"; Log.Information( $"Loading Penumbra Version {_validityChecker.Version}, Commit #{_validityChecker.CommitHash} with Waiting For Plugins: {startup}..."); - _services.GetRequiredService(); // Initialize because not required anywhere else. - _config = _services.GetRequiredService(); - _characterUtility = _services.GetRequiredService(); - _tempMods = _services.GetRequiredService(); - _residentResources = _services.GetRequiredService(); - _services.GetRequiredService(); // Initialize because not required anywhere else. - _modManager = _services.GetRequiredService(); - _collectionManager = _services.GetRequiredService(); - _tempCollections = _services.GetRequiredService(); - _redrawService = _services.GetRequiredService(); - _communicatorService = _services.GetRequiredService(); - _services.GetRequiredService(); // Initialize because not required anywhere else. - _services.GetRequiredService(); // Initialize because not required anywhere else. - _services.GetRequiredService(); // Initialize because not required anywhere else. + _services.GetService(); // Initialize because not required anywhere else. + _config = _services.GetService(); + _characterUtility = _services.GetService(); + _tempMods = _services.GetService(); + _residentResources = _services.GetService(); + _services.GetService(); // Initialize because not required anywhere else. + _modManager = _services.GetService(); + _collectionManager = _services.GetService(); + _tempCollections = _services.GetService(); + _redrawService = _services.GetService(); + _communicatorService = _services.GetService(); + _services.GetService(); // Initialize because not required anywhere else. + _services.GetService(); // Initialize because not required anywhere else. + _services.GetService(); // Initialize because not required anywhere else. _collectionManager.Caches.CreateNecessaryCaches(); - _services.GetRequiredService(); + _services.GetService(); - _services.GetRequiredService(); + _services.GetService(); - _services.GetRequiredService(); // Initialize before Interface. + _services.GetService(); // Initialize before Interface. SetupInterface(); SetupApi(); @@ -104,8 +102,8 @@ public class Penumbra : IDalamudPlugin private void SetupApi() { - var api = _services.GetRequiredService(); - _services.GetRequiredService(); + var api = _services.GetService(); + _services.GetService(); _communicatorService.ChangedItemHover.Subscribe(it => { if (it is (Item, FullEquipType)) @@ -123,9 +121,9 @@ public class Penumbra : IDalamudPlugin { AsyncTask.Run(() => { - var system = _services.GetRequiredService(); - system.Window.Setup(this, _services.GetRequiredService()); - _services.GetRequiredService(); + var system = _services.GetService(); + system.Window.Setup(this, _services.GetService()); + _services.GetService(); if (!_disposed) _windowSystem = system; else @@ -194,7 +192,7 @@ public class Penumbra : IDalamudPlugin sb.Append($"> **`Auto-Deduplication: `** {_config.AutoDeduplicateOnImport}\n"); sb.Append($"> **`Debug Mode: `** {_config.DebugMode}\n"); sb.Append( - $"> **`Synchronous Load (Dalamud): `** {(_services.GetRequiredService().GetDalamudConfig(DalamudServices.WaitingForPluginsOption, out bool v) ? v.ToString() : "Unknown")}\n"); + $"> **`Synchronous Load (Dalamud): `** {(_services.GetService().GetDalamudConfig(DalamudConfigService.WaitingForPluginsOption, out bool v) ? v.ToString() : "Unknown")}\n"); sb.Append( $"> **`Logging: `** Log: {_config.Ephemeral.EnableResourceLogging}, Watcher: {_config.Ephemeral.EnableResourceWatcher} ({_config.MaxResourceWatcherRecords})\n"); sb.Append($"> **`Use Ownership: `** {_config.UseOwnerNameForCharacterCollection}\n"); diff --git a/Penumbra/Penumbra.json b/Penumbra/Penumbra.json index d1682985..28dbc90d 100644 --- a/Penumbra/Penumbra.json +++ b/Penumbra/Penumbra.json @@ -9,7 +9,7 @@ "Tags": [ "modding" ], "DalamudApiLevel": 9, "LoadPriority": 69420, - "LoadRequiredState": 2, + "LoadState": 2, "LoadSync": true, "IconUrl": "https://raw.githubusercontent.com/xivdev/Penumbra/master/images/icon.png" } diff --git a/Penumbra/Services/DalamudServices.cs b/Penumbra/Services/DalamudServices.cs index 99539c39..51fb1192 100644 --- a/Penumbra/Services/DalamudServices.cs +++ b/Penumbra/Services/DalamudServices.cs @@ -5,15 +5,15 @@ using Dalamud.IoC; using Dalamud.Plugin; using Dalamud.Interface.DragDrop; using Dalamud.Plugin.Services; -using Microsoft.Extensions.DependencyInjection; +using OtterGui.Services; // ReSharper disable AutoPropertyCanBeMadeGetOnly.Local namespace Penumbra.Services; -public class DalamudServices +public class DalamudConfigService { - public DalamudServices(DalamudPluginInterface pluginInterface) + public DalamudConfigService(DalamudPluginInterface pluginInterface) { pluginInterface.Inject(this); try @@ -52,55 +52,6 @@ public class DalamudServices } } - public void AddServices(IServiceCollection services) - { - services.AddSingleton(PluginInterface); - services.AddSingleton(Commands); - services.AddSingleton(GameData); - services.AddSingleton(ClientState); - services.AddSingleton(Chat); - services.AddSingleton(Framework); - services.AddSingleton(Conditions); - services.AddSingleton(Targets); - services.AddSingleton(Objects); - services.AddSingleton(TitleScreenMenu); - services.AddSingleton(GameGui); - services.AddSingleton(KeyState); - services.AddSingleton(SigScanner); - services.AddSingleton(this); - services.AddSingleton(UiBuilder); - services.AddSingleton(DragDropManager); - services.AddSingleton(TextureProvider); - services.AddSingleton(TextureSubstitutionProvider); - services.AddSingleton(Interop); - services.AddSingleton(Log); - } - - // TODO remove static - // @formatter:off - [PluginService][RequiredVersion("1.0")] public DalamudPluginInterface PluginInterface { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public ICommandManager Commands { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public IDataManager GameData { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public IClientState ClientState { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public IChatGui Chat { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public IFramework Framework { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public ICondition Conditions { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public ITargetManager Targets { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public IObjectTable Objects { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public ITitleScreenMenu TitleScreenMenu { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public IGameGui GameGui { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public IKeyState KeyState { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public ISigScanner SigScanner { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public IDragDropManager DragDropManager { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public ITextureProvider TextureProvider { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public ITextureSubstitutionProvider TextureSubstitutionProvider { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public IGameInteropProvider Interop { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public IPluginLog Log { get; private set; } = null!; - // @formatter:on - - public UiBuilder UiBuilder - => PluginInterface.UiBuilder; - public const string WaitingForPluginsOption = "IsResumeGameAfterPluginLoad"; private readonly object? _dalamudConfig; @@ -164,3 +115,29 @@ public class DalamudServices } } } + +public static class DalamudServices +{ + public static void AddServices(ServiceManager services, DalamudPluginInterface pi) + { + services.AddExistingService(pi); + services.AddExistingService(pi.UiBuilder); + services.AddDalamudService(pi); + services.AddDalamudService(pi); + services.AddDalamudService(pi); + services.AddDalamudService(pi); + services.AddDalamudService(pi); + services.AddDalamudService(pi); + services.AddDalamudService(pi); + services.AddDalamudService(pi); + services.AddDalamudService(pi); + services.AddDalamudService(pi); + services.AddDalamudService(pi); + services.AddDalamudService(pi); + services.AddDalamudService(pi); + services.AddDalamudService(pi); + services.AddDalamudService(pi); + services.AddDalamudService(pi); + services.AddDalamudService(pi); + } +} diff --git a/Penumbra/Services/ServiceManager.cs b/Penumbra/Services/ServiceManagerA.cs similarity index 72% rename from Penumbra/Services/ServiceManager.cs rename to Penumbra/Services/ServiceManagerA.cs index 30f58701..410acfb9 100644 --- a/Penumbra/Services/ServiceManager.cs +++ b/Penumbra/Services/ServiceManagerA.cs @@ -7,11 +7,10 @@ using OtterGui.Services; using Penumbra.Api; using Penumbra.Collections.Cache; using Penumbra.Collections.Manager; -using Penumbra.GameData; using Penumbra.GameData.Actors; using Penumbra.GameData.Data; using Penumbra.GameData.DataContainers; -using Penumbra.GameData.DataContainers.Bases; +using Penumbra.GameData.Structs; using Penumbra.Import.Textures; using Penumbra.Interop.PathResolving; using Penumbra.Interop.ResourceLoading; @@ -33,14 +32,13 @@ using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManage namespace Penumbra.Services; -public static class ServiceManager +public static class ServiceManagerA { - public static ServiceProvider CreateProvider(Penumbra penumbra, DalamudPluginInterface pi, Logger log) + public static ServiceManager CreateProvider(Penumbra penumbra, DalamudPluginInterface pi, Logger log) { - var services = new ServiceCollection() - .AddSingleton(log) - .AddSingleton(penumbra) - .AddDalamud(pi) + var services = new ServiceManager(log) + .AddExistingService(log) + .AddExistingService(penumbra) .AddMeta() .AddGameData() .AddInterop() @@ -51,37 +49,15 @@ public static class ServiceManager .AddResolvers() .AddInterface() .AddModEditor() - .AddApi() - .AddDataContainers() - .AddAsyncServices(); - - return services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true }); - } - - private static IServiceCollection AddDalamud(this IServiceCollection services, DalamudPluginInterface pi) - { - var dalamud = new DalamudServices(pi); - dalamud.AddServices(services); + .AddApi(); + services.AddIServices(typeof(EquipItem).Assembly); + services.AddIServices(typeof(Penumbra).Assembly); + DalamudServices.AddServices(services, pi); + services.CreateProvider(); return services; } - private static IServiceCollection AddDataContainers(this IServiceCollection services) - { - foreach (var type in typeof(IDataContainer).Assembly.GetExportedTypes() - .Where(t => t is { IsAbstract: false, IsInterface: false } && t.IsAssignableTo(typeof(IDataContainer)))) - services.AddSingleton(type); - return services; - } - - private static IServiceCollection AddAsyncServices(this IServiceCollection services) - { - foreach (var type in typeof(ActorManager).Assembly.GetExportedTypes() - .Where(t => t is { IsAbstract: false, IsInterface: false } && t.IsAssignableTo(typeof(IService)))) - services.AddSingleton(type); - return services; - } - - private static IServiceCollection AddMeta(this IServiceCollection services) + private static ServiceManager AddMeta(this ServiceManager services) => services.AddSingleton() .AddSingleton() .AddSingleton() @@ -89,15 +65,16 @@ public static class ServiceManager .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton(); + .AddSingleton() + .AddSingleton(); - private static IServiceCollection AddGameData(this IServiceCollection services) + private static ServiceManager AddGameData(this ServiceManager services) => services.AddSingleton() .AddSingleton() .AddSingleton(); - private static IServiceCollection AddInterop(this IServiceCollection services) + private static ServiceManager AddInterop(this ServiceManager services) => services.AddSingleton() .AddSingleton() .AddSingleton() @@ -117,12 +94,12 @@ public static class ServiceManager .AddSingleton() .AddSingleton(); - private static IServiceCollection AddConfiguration(this IServiceCollection services) - => services.AddTransient() + private static ServiceManager AddConfiguration(this ServiceManager services) + => services.AddSingleton() .AddSingleton() .AddSingleton(); - private static IServiceCollection AddCollections(this IServiceCollection services) + private static ServiceManager AddCollections(this ServiceManager services) => services.AddSingleton() .AddSingleton() .AddSingleton() @@ -132,7 +109,7 @@ public static class ServiceManager .AddSingleton() .AddSingleton(); - private static IServiceCollection AddMods(this IServiceCollection services) + private static ServiceManager AddMods(this ServiceManager services) => services.AddSingleton() .AddSingleton() .AddSingleton() @@ -144,14 +121,14 @@ public static class ServiceManager .AddSingleton() .AddSingleton(s => (ModStorage)s.GetRequiredService()); - private static IServiceCollection AddResources(this IServiceCollection services) + private static ServiceManager AddResources(this ServiceManager services) => services.AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton(); - private static IServiceCollection AddResolvers(this IServiceCollection services) + private static ServiceManager AddResolvers(this ServiceManager services) => services.AddSingleton() .AddSingleton() .AddSingleton() @@ -162,7 +139,7 @@ public static class ServiceManager .AddSingleton() .AddSingleton(); - private static IServiceCollection AddInterface(this IServiceCollection services) + private static ServiceManager AddInterface(this ServiceManager services) => services.AddSingleton() .AddSingleton() .AddSingleton() @@ -200,7 +177,7 @@ public static class ServiceManager .AddSingleton() .AddSingleton(p => new Diagnostics(p)); - private static IServiceCollection AddModEditor(this IServiceCollection services) + private static ServiceManager AddModEditor(this ServiceManager services) => services.AddSingleton() .AddSingleton() .AddSingleton() @@ -212,10 +189,11 @@ public static class ServiceManager .AddSingleton() .AddSingleton(); - private static IServiceCollection AddApi(this IServiceCollection services) + private static ServiceManager AddApi(this ServiceManager services) => services.AddSingleton() .AddSingleton(x => x.GetRequiredService()) .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton(); } diff --git a/Penumbra/Services/ValidityChecker.cs b/Penumbra/Services/ValidityChecker.cs index 0688850b..7287938c 100644 --- a/Penumbra/Services/ValidityChecker.cs +++ b/Penumbra/Services/ValidityChecker.cs @@ -15,7 +15,7 @@ public class ValidityChecker public readonly bool IsNotInstalledPenumbra; public readonly bool IsValidSourceRepo; - public readonly List ImcExceptions = new(); + public readonly List ImcExceptions = []; public readonly string Version; public readonly string CommitHash; diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.MtrlTab.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.MtrlTab.cs index 954e9b0f..09e22d67 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.MtrlTab.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.MtrlTab.cs @@ -103,7 +103,7 @@ public partial class ModEditWindow LoadedShpkPath = path; var data = LoadedShpkPath.IsRooted ? File.ReadAllBytes(LoadedShpkPath.FullName) - : _edit._dalamud.GameData.GetFile(LoadedShpkPath.InternalName.ToString())?.Data; + : _edit._gameData.GetFile(LoadedShpkPath.InternalName.ToString())?.Data; AssociatedShpk = data?.Length > 0 ? new ShpkFile(data) : throw new Exception("Failure to load file data."); LoadedShpkPathName = path.ToPath(); } @@ -457,13 +457,13 @@ public partial class ModEditWindow var foundMaterials = new HashSet(); foreach (var materialInfo in instances) { - var material = materialInfo.GetDrawObjectMaterial(_edit._dalamud.Objects); + var material = materialInfo.GetDrawObjectMaterial(_edit._objects); if (foundMaterials.Contains((nint)material)) continue; try { - MaterialPreviewers.Add(new LiveMaterialPreviewer(_edit._dalamud.Objects, materialInfo)); + MaterialPreviewers.Add(new LiveMaterialPreviewer(_edit._objects, materialInfo)); foundMaterials.Add((nint)material); } catch (InvalidOperationException) @@ -481,7 +481,7 @@ public partial class ModEditWindow { try { - ColorTablePreviewers.Add(new LiveColorTablePreviewer(_edit._dalamud.Objects, _edit._dalamud.Framework, materialInfo)); + ColorTablePreviewers.Add(new LiveColorTablePreviewer(_edit._objects, _edit._framework, materialInfo)); } catch (InvalidOperationException) { diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.QuickImport.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.QuickImport.cs index 64457c25..c9cd3d06 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.QuickImport.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.QuickImport.cs @@ -71,7 +71,7 @@ public partial class ModEditWindow } else { - var file = _dalamud.GameData.GetFile(path); + var file = _gameData.GetFile(path); writable = file == null ? null : new RawGameFileWritable(file); } diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Textures.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Textures.cs index a3b17848..e9facdf4 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Textures.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Textures.cs @@ -219,7 +219,7 @@ public partial class ModEditWindow if (tex.Path != path) return; - _dalamud.Framework.RunOnFrameworkThread(() => tex.Reload(_textures)); + _framework.RunOnFrameworkThread(() => tex.Reload(_textures)); }); } diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs index db9201ca..9b127fe4 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs @@ -36,7 +36,6 @@ public partial class ModEditWindow : Window, IDisposable private readonly ModEditor _editor; private readonly Configuration _config; private readonly ItemSwapTab _itemSwapTab; - private readonly DalamudServices _dalamud; private readonly MetaFileManager _metaFileManager; private readonly ActiveCollections _activeCollections; private readonly StainService _stainService; @@ -44,6 +43,9 @@ public partial class ModEditWindow : Window, IDisposable private readonly CommunicatorService _communicator; private readonly IDragDropManager _dragDropManager; private readonly GameEventManager _gameEvents; + private readonly IDataManager _gameData; + private readonly IFramework _framework; + private readonly IObjectTable _objects; private Mod? _mod; private Vector2 _iconSize = Vector2.Zero; @@ -562,37 +564,41 @@ public partial class ModEditWindow : Window, IDisposable public ModEditWindow(PerformanceTracker performance, FileDialogService fileDialog, ItemSwapTab itemSwapTab, IDataManager gameData, Configuration config, ModEditor editor, ResourceTreeFactory resourceTreeFactory, MetaFileManager metaFileManager, - StainService stainService, ActiveCollections activeCollections, DalamudServices dalamud, ModMergeTab modMergeTab, + StainService stainService, ActiveCollections activeCollections, ModMergeTab modMergeTab, CommunicatorService communicator, TextureManager textures, IDragDropManager dragDropManager, GameEventManager gameEvents, - ChangedItemDrawer changedItemDrawer) + ChangedItemDrawer changedItemDrawer, IObjectTable objects, IFramework framework) : base(WindowBaseLabel) { _performance = performance; _itemSwapTab = itemSwapTab; + _gameData = gameData; _config = config; _editor = editor; _metaFileManager = metaFileManager; _stainService = stainService; _activeCollections = activeCollections; - _dalamud = dalamud; _modMergeTab = modMergeTab; _communicator = communicator; _dragDropManager = dragDropManager; _textures = textures; _fileDialog = fileDialog; _gameEvents = gameEvents; + _objects = objects; + _framework = framework; _materialTab = new FileEditor(this, gameData, config, _editor.Compactor, _fileDialog, "Materials", ".mtrl", () => PopulateIsOnPlayer(_editor.Files.Mtrl, ResourceType.Mtrl), DrawMaterialPanel, () => _mod?.ModPath.FullName ?? string.Empty, (bytes, path, writable) => new MtrlTab(this, new MtrlFile(bytes), path, writable)); _modelTab = new FileEditor(this, gameData, config, _editor.Compactor, _fileDialog, "Models", ".mdl", - () => PopulateIsOnPlayer(_editor.Files.Mdl, ResourceType.Mdl), DrawModelPanel, () => _mod?.ModPath.FullName ?? string.Empty, (bytes, _, _) => new MdlFile(bytes)); + () => PopulateIsOnPlayer(_editor.Files.Mdl, ResourceType.Mdl), DrawModelPanel, () => _mod?.ModPath.FullName ?? string.Empty, + (bytes, _, _) => new MdlFile(bytes)); _shaderPackageTab = new FileEditor(this, gameData, config, _editor.Compactor, _fileDialog, "Shaders", ".shpk", - () => PopulateIsOnPlayer(_editor.Files.Shpk, ResourceType.Shpk), DrawShaderPackagePanel, () => _mod?.ModPath.FullName ?? string.Empty, + () => PopulateIsOnPlayer(_editor.Files.Shpk, ResourceType.Shpk), DrawShaderPackagePanel, + () => _mod?.ModPath.FullName ?? string.Empty, (bytes, _, _) => new ShpkTab(_fileDialog, bytes)); _center = new CombinedTexture(_left, _right); _textureSelectCombo = new TextureDrawer.PathSelectCombo(textures, editor, () => GetPlayerResourcesOfType(ResourceType.Tex)); _resourceTreeFactory = resourceTreeFactory; - _quickImportViewer = + _quickImportViewer = new ResourceTreeViewer(_config, resourceTreeFactory, changedItemDrawer, 2, OnQuickImportRefresh, DrawQuickImportActions); _communicator.ModPathChanged.Subscribe(OnModPathChanged, ModPathChanged.Priority.ModEditWindow); } diff --git a/Penumbra/UI/ModsTab/ModFileSystemSelector.cs b/Penumbra/UI/ModsTab/ModFileSystemSelector.cs index cf65901e..8f12afbb 100644 --- a/Penumbra/UI/ModsTab/ModFileSystemSelector.cs +++ b/Penumbra/UI/ModsTab/ModFileSystemSelector.cs @@ -190,9 +190,9 @@ public sealed class ModFileSystemSelector : FileSystemSelector Label @@ -417,7 +421,7 @@ public class DebugTab : Window, ITab DrawSpecial("Current Card", _actors.GetCardPlayer()); DrawSpecial("Current Glamour", _actors.GetGlamourPlayer()); - foreach (var obj in _dalamud.Objects) + foreach (var obj in _objects) { ImGuiUtil.DrawTableColumn($"{((GameObject*)obj.Address)->ObjectIndex}"); ImGuiUtil.DrawTableColumn($"0x{obj.Address:X}"); @@ -827,7 +831,7 @@ public class DebugTab : Window, ITab /// Draw information about the models, materials and resources currently loaded by the local player. private unsafe void DrawPlayerModelInfo() { - var player = _dalamud.ClientState.LocalPlayer; + var player = _clientState.LocalPlayer; var name = player?.Name.ToString() ?? "NULL"; if (!ImGui.CollapsingHeader($"Player Model Info: {name}##Draw") || player == null) return; @@ -952,11 +956,11 @@ public class DebugTab : Window, ITab { if (!ImGui.CollapsingHeader("IPC")) { - _ipc.Tester.UnsubscribeEvents(); + _ipcTester.UnsubscribeEvents(); return; } - _ipc.Tester.Draw(); + _ipcTester.Draw(); } /// Helper to print a property and its value in a 2-column table. diff --git a/Penumbra/UI/Tabs/SettingsTab.cs b/Penumbra/UI/Tabs/SettingsTab.cs index 104f8d91..a03e7b87 100644 --- a/Penumbra/UI/Tabs/SettingsTab.cs +++ b/Penumbra/UI/Tabs/SettingsTab.cs @@ -1,5 +1,7 @@ using Dalamud.Interface; using Dalamud.Interface.Components; +using Dalamud.Plugin; +using Dalamud.Plugin.Services; using Dalamud.Utility; using ImGuiNET; using OtterGui; @@ -33,19 +35,23 @@ public class SettingsTab : ITab private readonly ModFileSystemSelector _selector; private readonly CharacterUtility _characterUtility; private readonly ResidentResourceManager _residentResources; - private readonly DalamudServices _dalamud; private readonly HttpApi _httpApi; private readonly DalamudSubstitutionProvider _dalamudSubstitutionProvider; private readonly FileCompactor _compactor; + private readonly DalamudConfigService _dalamudConfig; + private readonly DalamudPluginInterface _pluginInterface; + private readonly IDataManager _gameData; private int _minimumX = int.MaxValue; private int _minimumY = int.MaxValue; - public SettingsTab(Configuration config, FontReloader fontReloader, TutorialService tutorial, Penumbra penumbra, - FileDialogService fileDialog, ModManager modManager, ModFileSystemSelector selector, CharacterUtility characterUtility, - ResidentResourceManager residentResources, DalamudServices dalamud, ModExportManager modExportManager, HttpApi httpApi, - DalamudSubstitutionProvider dalamudSubstitutionProvider, FileCompactor compactor) + public SettingsTab(DalamudPluginInterface pluginInterface, Configuration config, FontReloader fontReloader, TutorialService tutorial, + Penumbra penumbra, FileDialogService fileDialog, ModManager modManager, ModFileSystemSelector selector, + CharacterUtility characterUtility, ResidentResourceManager residentResources, ModExportManager modExportManager, HttpApi httpApi, + DalamudSubstitutionProvider dalamudSubstitutionProvider, FileCompactor compactor, DalamudConfigService dalamudConfig, + IDataManager gameData) { + _pluginInterface = pluginInterface; _config = config; _fontReloader = fontReloader; _tutorial = tutorial; @@ -55,11 +61,12 @@ public class SettingsTab : ITab _selector = selector; _characterUtility = characterUtility; _residentResources = residentResources; - _dalamud = dalamud; _modExportManager = modExportManager; _httpApi = httpApi; _dalamudSubstitutionProvider = dalamudSubstitutionProvider; _compactor = compactor; + _dalamudConfig = dalamudConfig; + _gameData = gameData; if (_compactor.CanCompact) _compactor.Enabled = _config.UseFileSystemCompression; } @@ -164,14 +171,14 @@ public class SettingsTab : ITab if (IsSubPathOf(programFiles, newName) || IsSubPathOf(programFilesX86, newName)) return ("Path is not allowed to be in ProgramFiles.", false); - var dalamud = _dalamud.PluginInterface.ConfigDirectory.Parent!.Parent!; + var dalamud = _pluginInterface.ConfigDirectory.Parent!.Parent!; if (IsSubPathOf(dalamud.FullName, newName)) return ("Path is not allowed to be inside your Dalamud directories.", false); if (Functions.GetDownloadsFolder(out var downloads) && IsSubPathOf(downloads, newName)) return ("Path is not allowed to be inside your Downloads folder.", false); - var gameDir = _dalamud.GameData.GameData.DataPath.Parent!.Parent!.FullName; + var gameDir = _gameData.GameData.DataPath.Parent!.Parent!.FullName; if (IsSubPathOf(gameDir, newName)) return ("Path is not allowed to be inside your game folder.", false); @@ -368,21 +375,21 @@ public class SettingsTab : ITab v => { _config.HideUiWhenUiHidden = v; - _dalamud.UiBuilder.DisableUserUiHide = !v; + _pluginInterface.UiBuilder.DisableUserUiHide = !v; }); Checkbox("Hide Config Window when in Cutscenes", "Hide the Penumbra main window when you are currently watching a cutscene.", _config.HideUiInCutscenes, v => { - _config.HideUiInCutscenes = v; - _dalamud.UiBuilder.DisableCutsceneUiHide = !v; + _config.HideUiInCutscenes = v; + _pluginInterface.UiBuilder.DisableCutsceneUiHide = !v; }); Checkbox("Hide Config Window when in GPose", "Hide the Penumbra main window when you are currently in GPose mode.", _config.HideUiInGPose, v => { - _config.HideUiInGPose = v; - _dalamud.UiBuilder.DisableGposeUiHide = !v; + _config.HideUiInGPose = v; + _pluginInterface.UiBuilder.DisableGposeUiHide = !v; }); } @@ -847,7 +854,7 @@ public class SettingsTab : ITab /// Draw a checkbox that toggles the dalamud setting to wait for plugins on open. private void DrawWaitForPluginsReflection() { - if (!_dalamud.GetDalamudConfig(DalamudServices.WaitingForPluginsOption, out bool value)) + if (!_dalamudConfig.GetDalamudConfig(DalamudConfigService.WaitingForPluginsOption, out bool value)) { using var disabled = ImRaii.Disabled(); Checkbox("Wait for Plugins on Startup (Disabled, can not access Dalamud Configuration)", string.Empty, false, v => { }); @@ -855,9 +862,12 @@ public class SettingsTab : ITab else { Checkbox("Wait for Plugins on Startup", - "Some mods need to change files that are loaded once when the game starts and never afterwards.\nThis can cause issues with Penumbra loading after the files are already loaded.\nThis setting causes the game to wait until certain plugins have finished loading, making those mods work (in the base collection).\n\nThis changes a setting in the Dalamud Configuration found at /xlsettings -> General.", + "Some mods need to change files that are loaded once when the game starts and never afterwards.\n" + + "This can cause issues with Penumbra loading after the files are already loaded.\n" + + "This setting causes the game to wait until certain plugins have finished loading, making those mods work (in the base collection).\n\n" + + "This changes a setting in the Dalamud Configuration found at /xlsettings -> General.", value, - v => _dalamud.SetDalamudConfig(DalamudServices.WaitingForPluginsOption, v, "doWaitForPluginsOnStartup")); + v => _dalamudConfig.SetDalamudConfig(DalamudConfigService.WaitingForPluginsOption, v, "doWaitForPluginsOnStartup")); } } diff --git a/Penumbra/packages.lock.json b/Penumbra/packages.lock.json index 9172bb60..16353828 100644 --- a/Penumbra/packages.lock.json +++ b/Penumbra/packages.lock.json @@ -73,7 +73,10 @@ } }, "ottergui": { - "type": "Project" + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[7.0.0, )" + } }, "penumbra.api": { "type": "Project" From c138c39c068ef8eb10da401d73583db3995bf223 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Thu, 21 Dec 2023 15:12:41 +0100 Subject: [PATCH 10/14] Change CharacterWeapon names. --- Penumbra.GameData | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Penumbra.GameData b/Penumbra.GameData index 4d3570fd..fe0bf13f 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit 4d3570fd47d78dbc49cf5e41fd3545a533ef9e81 +Subproject commit fe0bf13f1ae47b77684ccb4bb9e9f44e430acfbf From 2051197c65e823142bfa31667120cbf0984f0241 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Thu, 21 Dec 2023 15:20:39 +0100 Subject: [PATCH 11/14] Change again. --- Penumbra.GameData | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Penumbra.GameData b/Penumbra.GameData index fe0bf13f..3787e82d 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit fe0bf13f1ae47b77684ccb4bb9e9f44e430acfbf +Subproject commit 3787e82d1b84d2542b6e4238060d75383a4b12a1 From 4aa19e49d59ac3a31f9ba05d7ba37243f8fb2d0f Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Fri, 22 Dec 2023 14:22:03 +0100 Subject: [PATCH 12/14] Add filtering mods by changed item categories. --- Penumbra.GameData | 2 +- Penumbra/Mods/ItemSwap/EquipmentSwap.cs | 2 +- Penumbra/UI/ChangedItemDrawer.cs | 92 ++++++++++++++----- .../CollectionTab/IndividualAssignmentUi.cs | 2 +- Penumbra/UI/ModsTab/ModFileSystemSelector.cs | 25 +++-- 5 files changed, 89 insertions(+), 34 deletions(-) diff --git a/Penumbra.GameData b/Penumbra.GameData index 3787e82d..58a3e794 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit 3787e82d1b84d2542b6e4238060d75383a4b12a1 +Subproject commit 58a3e7947c207452f5fa0d328c47c5ed6bdd9a0f diff --git a/Penumbra/Mods/ItemSwap/EquipmentSwap.cs b/Penumbra/Mods/ItemSwap/EquipmentSwap.cs index f7f82a59..516df251 100644 --- a/Penumbra/Mods/ItemSwap/EquipmentSwap.cs +++ b/Penumbra/Mods/ItemSwap/EquipmentSwap.cs @@ -242,7 +242,7 @@ public static class EquipmentSwap if (!slot.IsEquipmentPiece()) throw new ItemSwap.InvalidItemTypeException(); - modelId = i.ModelId; + modelId = i.PrimaryId; variant = i.Variant; } diff --git a/Penumbra/UI/ChangedItemDrawer.cs b/Penumbra/UI/ChangedItemDrawer.cs index 0a1d58f9..638afef0 100644 --- a/Penumbra/UI/ChangedItemDrawer.cs +++ b/Penumbra/UI/ChangedItemDrawer.cs @@ -43,8 +43,71 @@ public class ChangedItemDrawer : IDisposable Emote = 0x01_00_00, } - public const ChangedItemIcon AllFlags = (ChangedItemIcon)0x01FFFF; - public const ChangedItemIcon DefaultFlags = AllFlags & ~ChangedItemIcon.Offhand; + private static readonly ChangedItemIcon[] Order = + [ + ChangedItemIcon.Head, + ChangedItemIcon.Body, + ChangedItemIcon.Hands, + ChangedItemIcon.Legs, + ChangedItemIcon.Feet, + ChangedItemIcon.Ears, + ChangedItemIcon.Neck, + ChangedItemIcon.Wrists, + ChangedItemIcon.Finger, + ChangedItemIcon.Mainhand, + ChangedItemIcon.Offhand, + ChangedItemIcon.Customization, + ChangedItemIcon.Action, + ChangedItemIcon.Emote, + ChangedItemIcon.Monster, + ChangedItemIcon.Demihuman, + ChangedItemIcon.Unknown, + ]; + + private static readonly string[] LowerNames = Order.Select(f => ToDescription(f).ToLowerInvariant()).ToArray(); + + public static bool TryParseIndex(ReadOnlySpan input, out ChangedItemIcon slot) + { + // Handle numeric cases before TryParse because numbers + // are not logical otherwise. + if (int.TryParse(input, out var idx)) + { + // We assume users will use 1-based index, but if they enter 0, just use the first. + if (idx == 0) + { + slot = Order[0]; + return true; + } + + // Use 1-based index. + --idx; + if (idx >= 0 && idx < Order.Length) + { + slot = Order[idx]; + return true; + } + } + + slot = 0; + return false; + } + + public static bool TryParsePartial(string lowerInput, out ChangedItemIcon slot) + { + if (TryParseIndex(lowerInput, out slot)) + return true; + + slot = 0; + foreach (var (item, flag) in LowerNames.Zip(Order)) + if (item.Contains(lowerInput, StringComparison.Ordinal)) + slot |= flag; + + return slot != 0; + } + + public const ChangedItemIcon AllFlags = (ChangedItemIcon)0x01FFFF; + public static readonly int NumCategories = Order.Length; + public const ChangedItemIcon DefaultFlags = AllFlags & ~ChangedItemIcon.Offhand; private readonly Configuration _config; private readonly ExcelSheet _items; @@ -163,26 +226,7 @@ public class ChangedItemDrawer : IDisposable using var _ = ImRaii.PushId("ChangedItemIconFilter"); var size = TypeFilterIconSize; using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero); - var order = new[] - { - ChangedItemIcon.Head, - ChangedItemIcon.Body, - ChangedItemIcon.Hands, - ChangedItemIcon.Legs, - ChangedItemIcon.Feet, - ChangedItemIcon.Ears, - ChangedItemIcon.Neck, - ChangedItemIcon.Wrists, - ChangedItemIcon.Finger, - ChangedItemIcon.Mainhand, - ChangedItemIcon.Offhand, - ChangedItemIcon.Customization, - ChangedItemIcon.Action, - ChangedItemIcon.Emote, - ChangedItemIcon.Monster, - ChangedItemIcon.Demihuman, - ChangedItemIcon.Unknown, - }; + bool DrawIcon(ChangedItemIcon type, ref ChangedItemIcon typeFilter) { @@ -217,13 +261,13 @@ public class ChangedItemDrawer : IDisposable return ret; } - foreach (var iconType in order) + foreach (var iconType in Order) { ret |= DrawIcon(iconType, ref typeFilter); ImGui.SameLine(); } - ImGui.SetCursorPos(new(ImGui.GetContentRegionMax().X - size.X, ImGui.GetCursorPosY() + yOffset)); + ImGui.SetCursorPos(new Vector2(ImGui.GetContentRegionMax().X - size.X, ImGui.GetCursorPosY() + yOffset)); ImGui.Image(_icons[AllFlags].ImGuiHandle, size, Vector2.Zero, Vector2.One, typeFilter == 0 ? new Vector4(0.6f, 0.3f, 0.3f, 1f) : typeFilter == AllFlags ? new Vector4(0.75f, 0.75f, 0.75f, 1f) : new Vector4(0.5f, 0.5f, 1f, 1f)); diff --git a/Penumbra/UI/CollectionTab/IndividualAssignmentUi.cs b/Penumbra/UI/CollectionTab/IndividualAssignmentUi.cs index d3e4ab5e..a0e35cff 100644 --- a/Penumbra/UI/CollectionTab/IndividualAssignmentUi.cs +++ b/Penumbra/UI/CollectionTab/IndividualAssignmentUi.cs @@ -126,7 +126,7 @@ public class IndividualAssignmentUi : IDisposable /// Create combos when ready. private void SetupCombos() { - _worldCombo = new WorldCombo(_actors.Data.Worlds, Penumbra.Log, WorldId.AnyWorld); + _worldCombo = new WorldCombo(_actors.Data.Worlds, Penumbra.Log); _mountCombo = new NpcCombo("##mountCombo", _actors.Data.Mounts, Penumbra.Log); _companionCombo = new NpcCombo("##companionCombo", _actors.Data.Companions, Penumbra.Log); _ornamentCombo = new NpcCombo("##ornamentCombo", _actors.Data.Ornaments, Penumbra.Log); diff --git a/Penumbra/UI/ModsTab/ModFileSystemSelector.cs b/Penumbra/UI/ModsTab/ModFileSystemSelector.cs index 8f12afbb..c42b1018 100644 --- a/Penumbra/UI/ModsTab/ModFileSystemSelector.cs +++ b/Penumbra/UI/ModsTab/ModFileSystemSelector.cs @@ -12,6 +12,8 @@ using Penumbra.Api.Enums; using Penumbra.Collections; using Penumbra.Collections.Manager; using Penumbra.Communication; +using Penumbra.GameData.Enums; +using Penumbra.GameData.Structs; using Penumbra.Mods; using Penumbra.Mods.Manager; using Penumbra.Mods.Subclasses; @@ -190,7 +192,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector filterValue.Length == 2 ? (LowerString.Empty, -1) : ParseFilter(filterValue, 3), 't' => filterValue.Length == 2 ? (LowerString.Empty, -1) : ParseFilter(filterValue, 4), 'T' => filterValue.Length == 2 ? (LowerString.Empty, -1) : ParseFilter(filterValue, 4), + 's' => filterValue.Length == 2 ? (LowerString.Empty, -1) : ParseFilter(filterValue, 5), + 'S' => filterValue.Length == 2 ? (LowerString.Empty, -1) : ParseFilter(filterValue, 5), _ => (new LowerString(filterValue), 0), }, _ => (new LowerString(filterValue), 0), @@ -549,10 +555,13 @@ public sealed class ModFileSystemSelector : FileSystemSelector !mod.Author.Contains(_modFilter), 3 => !mod.LowerChangedItemsString.Contains(_modFilter.Lower), 4 => !mod.AllTagsLower.Contains(_modFilter.Lower), + 5 => mod.ChangedItems.All(p => (ChangedItemDrawer.GetCategoryIcon(p.Key, p.Value) & _slotFilter) == 0), 2 + EmptyOffset => !mod.Author.IsEmpty, 3 + EmptyOffset => mod.LowerChangedItemsString.Length > 0, 4 + EmptyOffset => mod.AllTagsLower.Length > 0, + 5 + EmptyOffset => mod.ChangedItems.Count == 0, _ => false, // Should never happen }; } From dc583cb8e24338e9370ee1caab1875edffd65a17 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Fri, 22 Dec 2023 14:24:41 +0100 Subject: [PATCH 13/14] Update gamedata. --- Penumbra.GameData | 2 +- .../Interop/ResourceTree/ResolveContext.PathResolution.cs | 5 ++--- Penumbra/Interop/ResourceTree/ResourceTree.cs | 2 +- Penumbra/UI/Tabs/Debug/DebugTab.cs | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Penumbra.GameData b/Penumbra.GameData index 58a3e794..ed37f834 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit 58a3e7947c207452f5fa0d328c47c5ed6bdd9a0f +Subproject commit ed37f83424c11a5a601e74f4660cd52ebd68a7b3 diff --git a/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs b/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs index f2059253..0ab1e0e3 100644 --- a/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs +++ b/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs @@ -1,4 +1,3 @@ -using Dalamud.Game.ClientState.Objects.Enums; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; using Penumbra.GameData.Data; @@ -239,8 +238,8 @@ internal partial record ResolveContext return (characterRaceCode, "base", 1); case 1: var faceId = human->FaceId; - var tribe = human->Customize[(int)CustomizeIndex.Tribe]; - var modelType = human->Customize[(int)CustomizeIndex.ModelType]; + var tribe = human->Customize[(int)Dalamud.Game.ClientState.Objects.Enums.CustomizeIndex.Tribe]; + var modelType = human->Customize[(int)Dalamud.Game.ClientState.Objects.Enums.CustomizeIndex.ModelType]; if (faceId < 201) { faceId -= tribe switch diff --git a/Penumbra/Interop/ResourceTree/ResourceTree.cs b/Penumbra/Interop/ResourceTree/ResourceTree.cs index bd994242..24112a9f 100644 --- a/Penumbra/Interop/ResourceTree/ResourceTree.cs +++ b/Penumbra/Interop/ResourceTree/ResourceTree.cs @@ -6,9 +6,9 @@ using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; using Penumbra.GameData.Data; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; -using Penumbra.String.Classes; using Penumbra.UI; using CustomizeData = FFXIVClientStructs.FFXIV.Client.Game.Character.CustomizeData; +using CustomizeIndex = Dalamud.Game.ClientState.Objects.Enums.CustomizeIndex; namespace Penumbra.Interop.ResourceTree; diff --git a/Penumbra/UI/Tabs/Debug/DebugTab.cs b/Penumbra/UI/Tabs/Debug/DebugTab.cs index 599832f4..66b93b04 100644 --- a/Penumbra/UI/Tabs/Debug/DebugTab.cs +++ b/Penumbra/UI/Tabs/Debug/DebugTab.cs @@ -88,7 +88,7 @@ public class DebugTab : Window, ITab private readonly TextureManager _textureManager; private readonly SkinFixer _skinFixer; private readonly RedrawService _redraws; - private readonly DictEmotes _emotes; + private readonly DictEmote _emotes; private readonly Diagnostics _diagnostics; private readonly IObjectTable _objects; private readonly IClientState _clientState; @@ -99,7 +99,7 @@ public class DebugTab : Window, ITab ResourceManagerService resourceManager, PenumbraIpcProviders ipc, CollectionResolver collectionResolver, DrawObjectState drawObjectState, PathState pathState, SubfileHelper subfileHelper, IdentifiedCollectionCache identifiedCollectionCache, CutsceneService cutsceneService, ModImportManager modImporter, ImportPopup importPopup, FrameworkManager framework, - TextureManager textureManager, SkinFixer skinFixer, RedrawService redraws, DictEmotes emotes, Diagnostics diagnostics, IpcTester ipcTester) + TextureManager textureManager, SkinFixer skinFixer, RedrawService redraws, DictEmote emotes, Diagnostics diagnostics, IpcTester ipcTester) : base("Penumbra Debug Window", ImGuiWindowFlags.NoCollapse) { IsOpen = true; From 697b5fac6531d5dfc549f18f5c22540a1bb54033 Mon Sep 17 00:00:00 2001 From: Actions User Date: Fri, 29 Dec 2023 17:20:11 +0000 Subject: [PATCH 14/14] [CI] Updating repo.json for testing_0.8.3.2 --- repo.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/repo.json b/repo.json index 173f6592..dbe1fe87 100644 --- a/repo.json +++ b/repo.json @@ -5,7 +5,7 @@ "Description": "Runtime mod loader and manager.", "InternalName": "Penumbra", "AssemblyVersion": "0.8.3.1", - "TestingAssemblyVersion": "0.8.3.1", + "TestingAssemblyVersion": "0.8.3.2", "RepoUrl": "https://github.com/xivdev/Penumbra", "ApplicableVersion": "any", "DalamudApiLevel": 9, @@ -17,7 +17,7 @@ "LoadRequiredState": 2, "LoadSync": true, "DownloadLinkInstall": "https://github.com/xivdev/Penumbra/releases/download/0.8.3.1/Penumbra.zip", - "DownloadLinkTesting": "https://github.com/xivdev/Penumbra/releases/download/0.8.3.1/Penumbra.zip", + "DownloadLinkTesting": "https://github.com/xivdev/Penumbra/releases/download/testing_0.8.3.2/Penumbra.zip", "DownloadLinkUpdate": "https://github.com/xivdev/Penumbra/releases/download/0.8.3.1/Penumbra.zip", "IconUrl": "https://raw.githubusercontent.com/xivdev/Penumbra/master/images/icon.png" }