From 311882948a39288f029b7773a6302ea11681291e Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Wed, 22 Jun 2022 17:02:31 +0200 Subject: [PATCH] Add last IPC tests, fix some problems with them, increment API Version to 5. --- Penumbra/Api/IPenumbraApi.cs | 7 +- Penumbra/Api/IpcTester.cs | 99 +++++++++++++++++-- Penumbra/Api/PenumbraApi.cs | 32 +++--- Penumbra/Api/PenumbraIpc.cs | 8 +- Penumbra/Api/TempModManager.cs | 2 +- .../Interop/Resolver/PathResolver.Data.cs | 12 ++- 6 files changed, 126 insertions(+), 34 deletions(-) diff --git a/Penumbra/Api/IPenumbraApi.cs b/Penumbra/Api/IPenumbraApi.cs index 01ecc868..1fa7b576 100644 --- a/Penumbra/Api/IPenumbraApi.cs +++ b/Penumbra/Api/IPenumbraApi.cs @@ -68,7 +68,7 @@ public interface IPenumbraApi : IPenumbraApiBase public string ResolvePath( string gamePath, string characterName ); // Reverse resolves a given modded local path into its replacement in form of all applicable game path for given character - public IList ReverseResolvePath( string moddedPath, string characterName ); + public IList< string > ReverseResolvePath( string moddedPath, string characterName ); // Try to load a given gamePath with the resolved path from Penumbra. public T? GetFile< T >( string gamePath ) where T : FileResource; @@ -144,13 +144,12 @@ public interface IPenumbraApi : IPenumbraApiBase // Set a temporary mod with the given paths, manipulations and priority and the name tag to all collections. // Can return Okay, InvalidGamePath, or InvalidManipulation. - public PenumbraApiEc AddTemporaryModAll( string tag, IReadOnlyDictionary< string, string > paths, IReadOnlySet< string > manipCodes, + public PenumbraApiEc AddTemporaryModAll( string tag, Dictionary< string, string > paths, HashSet< string > manipCodes, int priority ); // Set a temporary mod with the given paths, manipulations and priority and the name tag to the collection with the given name, which can be temporary. // Can return Okay, MissingCollection InvalidGamePath, or InvalidManipulation. - public PenumbraApiEc AddTemporaryMod( string tag, string collectionName, IReadOnlyDictionary< string, string > paths, - IReadOnlySet< string > manipCodes, + public PenumbraApiEc AddTemporaryMod( string tag, string collectionName, Dictionary< string, string > paths, HashSet< string > manipCodes, int priority ); // Remove the temporary mod with the given tag and priority from the temporary mods applying to all collections, if it exists. diff --git a/Penumbra/Api/IpcTester.cs b/Penumbra/Api/IpcTester.cs index 069d4c3e..30059913 100644 --- a/Penumbra/Api/IpcTester.cs +++ b/Penumbra/Api/IpcTester.cs @@ -483,8 +483,8 @@ public class IpcTester : IDisposable private bool _settingsEnabled = false; private int _settingsPriority = 0; private IDictionary< string, (IList< string >, SelectType) >? _availableSettings; - private IDictionary< string, IList< string > >? _currentSettings = null; - private PenumbraApiEc _lastError = PenumbraApiEc.Success; + private IDictionary< string, IList< string > >? _currentSettings = null; + private PenumbraApiEc _lastSettingsError = PenumbraApiEc.Success; private void DrawSetting() @@ -506,7 +506,7 @@ public class IpcTester : IDisposable return; } - DrawIntro( "Last Error", _lastError.ToString() ); + DrawIntro( "Last Error", _lastSettingsError.ToString() ); DrawIntro( PenumbraIpc.LabelProviderGetAvailableModSettings, "Get Available Settings" ); if( ImGui.Button( "Get##Available" ) ) @@ -514,7 +514,7 @@ public class IpcTester : IDisposable _availableSettings = _pi .GetIpcSubscriber< string, string, IDictionary< string, (IList< string >, SelectType) >? >( PenumbraIpc.LabelProviderGetAvailableModSettings ).InvokeFunc( _settingsModDirectory, _settingsModName ); - _lastError = _availableSettings == null ? PenumbraApiEc.ModMissing : PenumbraApiEc.Success; + _lastSettingsError = _availableSettings == null ? PenumbraApiEc.ModMissing : PenumbraApiEc.Success; } DrawIntro( PenumbraIpc.LabelProviderGetCurrentModSettings, "Get Current Settings" ); @@ -524,7 +524,7 @@ public class IpcTester : IDisposable .GetIpcSubscriber< string, string, string, bool, (PenumbraApiEc, (bool, int, IDictionary< string, IList< string > >, bool)?) >( PenumbraIpc.LabelProviderGetCurrentModSettings ).InvokeFunc( _settingsCollection, _settingsModDirectory, _settingsModName, _settingsAllowInheritance ); - _lastError = ret.Item1; + _lastSettingsError = ret.Item1; if( ret.Item1 == PenumbraApiEc.Success ) { _settingsEnabled = ret.Item2?.Item1 ?? false; @@ -543,7 +543,7 @@ public class IpcTester : IDisposable ImGui.SameLine(); if( ImGui.Button( "Set##Inherit" ) ) { - _lastError = _pi.GetIpcSubscriber< string, string, string, bool, PenumbraApiEc >( PenumbraIpc.LabelProviderTryInheritMod ) + _lastSettingsError = _pi.GetIpcSubscriber< string, string, string, bool, PenumbraApiEc >( PenumbraIpc.LabelProviderTryInheritMod ) .InvokeFunc( _settingsCollection, _settingsModDirectory, _settingsModName, _settingsInherit ); } @@ -552,7 +552,7 @@ public class IpcTester : IDisposable ImGui.SameLine(); if( ImGui.Button( "Set##Enabled" ) ) { - _lastError = _pi.GetIpcSubscriber< string, string, string, bool, PenumbraApiEc >( PenumbraIpc.LabelProviderTrySetMod ) + _lastSettingsError = _pi.GetIpcSubscriber< string, string, string, bool, PenumbraApiEc >( PenumbraIpc.LabelProviderTrySetMod ) .InvokeFunc( _settingsCollection, _settingsModDirectory, _settingsModName, _settingsEnabled ); } @@ -562,7 +562,8 @@ public class IpcTester : IDisposable ImGui.SameLine(); if( ImGui.Button( "Set##Priority" ) ) { - _lastError = _pi.GetIpcSubscriber< string, string, string, int, PenumbraApiEc >( PenumbraIpc.LabelProviderTrySetModPriority ) + _lastSettingsError = _pi + .GetIpcSubscriber< string, string, string, int, PenumbraApiEc >( PenumbraIpc.LabelProviderTrySetModPriority ) .InvokeFunc( _settingsCollection, _settingsModDirectory, _settingsModName, _settingsPriority ); } @@ -615,25 +616,37 @@ public class IpcTester : IDisposable { if( type == SelectType.Single ) { - _lastError = _pi + _lastSettingsError = _pi .GetIpcSubscriber< string, string, string, string, string, PenumbraApiEc >( PenumbraIpc.LabelProviderTrySetModSetting ).InvokeFunc( _settingsCollection, _settingsModDirectory, _settingsModName, group, current.Count > 0 ? current[ 0 ] : string.Empty ); } else { - _lastError = _pi + _lastSettingsError = _pi .GetIpcSubscriber< string, string, string, string, IReadOnlyList< string >, PenumbraApiEc >( PenumbraIpc.LabelProviderTrySetModSettings ).InvokeFunc( _settingsCollection, _settingsModDirectory, _settingsModName, group, current.ToArray() ); } } + ImGui.SameLine(); ImGui.TextUnformatted( group ); } } } + private string _tempCollectionName = string.Empty; + private string _tempCharacterName = string.Empty; + private bool _forceOverwrite = true; + private string _tempModName = string.Empty; + private PenumbraApiEc _lastTempError = PenumbraApiEc.Success; + private string _lastCreatedCollectionName = string.Empty; + private string _tempGamePath = "test/game/path.mtrl"; + private string _tempFilePath = "test/success.mtrl"; + private string _tempManipulation = string.Empty; + + private void DrawTemp() { using var _ = ImRaii.TreeNode( "Temp IPC" ); @@ -641,6 +654,72 @@ public class IpcTester : IDisposable { return; } + + ImGui.InputTextWithHint( "##tempCollection", "Collection Name...", ref _tempCollectionName, 128 ); + ImGui.InputTextWithHint( "##tempCollectionChar", "Collection Character...", ref _tempCharacterName, 32 ); + ImGui.InputTextWithHint( "##tempMod", "Temporary Mod Name...", ref _tempModName, 32 ); + ImGui.InputTextWithHint( "##tempGame", "Game Path...", ref _tempGamePath, 256 ); + ImGui.InputTextWithHint( "##tempFile", "File Path...", ref _tempFilePath, 256 ); + ImGui.InputTextWithHint( "##tempManip", "Manipulation Base64 String...", ref _tempManipulation, 256 ); + ImGui.Checkbox( "Force Character Collection Overwrite", ref _forceOverwrite ); + + using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); + if( !table ) + { + return; + } + + DrawIntro( "Last Error", _lastTempError.ToString() ); + DrawIntro( "Last Created Collection", _lastCreatedCollectionName ); + DrawIntro( PenumbraIpc.LabelProviderCreateTemporaryCollection, "Create Temporary Collection" ); + if( ImGui.Button( "Create##Collection" ) ) + { + ( _lastTempError, _lastCreatedCollectionName ) = + _pi.GetIpcSubscriber< string, string, bool, (PenumbraApiEc, string) >( PenumbraIpc.LabelProviderCreateTemporaryCollection ) + .InvokeFunc( _tempCollectionName, _tempCharacterName, _forceOverwrite ); + } + + DrawIntro( PenumbraIpc.LabelProviderRemoveTemporaryCollection, "Remove Temporary Collection from Character" ); + if( ImGui.Button( "Delete##Collection" ) ) + { + _lastTempError = _pi.GetIpcSubscriber< string, PenumbraApiEc >( PenumbraIpc.LabelProviderRemoveTemporaryCollection ) + .InvokeFunc( _tempCharacterName ); + } + + DrawIntro( PenumbraIpc.LabelProviderAddTemporaryMod, "Add Temporary Mod to specific Collection" ); + if( ImGui.Button( "Add##Mod" ) ) + { + _lastTempError = _pi + .GetIpcSubscriber< string, string, IReadOnlyDictionary< string, string >, IReadOnlySet< string >, int, PenumbraApiEc >( + PenumbraIpc.LabelProviderAddTemporaryMod ) + .InvokeFunc( _tempModName, _tempCollectionName, + new Dictionary< string, string > { { _tempGamePath, _tempFilePath } }, + _tempManipulation.Length > 0 ? new HashSet< string > { _tempManipulation } : new HashSet< string >(), int.MaxValue ); + } + + DrawIntro( PenumbraIpc.LabelProviderAddTemporaryModAll, "Add Temporary Mod to all Collections" ); + if( ImGui.Button( "Add##All" ) ) + { + _lastTempError = _pi + .GetIpcSubscriber< string, IReadOnlyDictionary< string, string >, IReadOnlySet< string >, int, PenumbraApiEc >( + PenumbraIpc.LabelProviderAddTemporaryModAll ) + .InvokeFunc( _tempModName, new Dictionary< string, string > { { _tempGamePath, _tempFilePath } }, + _tempManipulation.Length > 0 ? new HashSet< string > { _tempManipulation } : new HashSet< string >(), int.MaxValue ); + } + + DrawIntro( PenumbraIpc.LabelProviderRemoveTemporaryMod, "Remove Temporary Mod from specific Collection" ); + if( ImGui.Button( "Remove##Mod" ) ) + { + _lastTempError = _pi.GetIpcSubscriber< string, string, int, PenumbraApiEc >( PenumbraIpc.LabelProviderRemoveTemporaryMod ) + .InvokeFunc( _tempModName, _tempCollectionName, int.MaxValue ); + } + + DrawIntro( PenumbraIpc.LabelProviderRemoveTemporaryModAll, "Remove Temporary Mod from all Collections" ); + if( ImGui.Button( "Remove##ModAll" ) ) + { + _lastTempError = _pi.GetIpcSubscriber< string, int, PenumbraApiEc >( PenumbraIpc.LabelProviderRemoveTemporaryModAll ) + .InvokeFunc( _tempModName, int.MaxValue ); + } } private void DrawTempCollections() diff --git a/Penumbra/Api/PenumbraApi.cs b/Penumbra/Api/PenumbraApi.cs index 0cbf419e..2efeaaa9 100644 --- a/Penumbra/Api/PenumbraApi.cs +++ b/Penumbra/Api/PenumbraApi.cs @@ -4,8 +4,6 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; -using Dalamud.Configuration; -using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Logging; using Lumina.Data; using Newtonsoft.Json; @@ -22,7 +20,7 @@ namespace Penumbra.Api; public class PenumbraApi : IDisposable, IPenumbraApi { public int ApiVersion - => 4; + => 5; private Penumbra? _penumbra; private Lumina.GameData? _lumina; @@ -342,6 +340,12 @@ public class PenumbraApi : IDisposable, IPenumbraApi public (PenumbraApiEc, string) CreateTemporaryCollection( string tag, string character, bool forceOverwriteCharacter ) { CheckInitialized(); + + if( character.Length is 0 or > 32 || tag.Length == 0 ) + { + return ( PenumbraApiEc.InvalidArgument, string.Empty ); + } + if( !forceOverwriteCharacter && Penumbra.CollectionManager.Characters.ContainsKey( character ) || Penumbra.TempMods.Collections.ContainsKey( character ) ) { @@ -364,8 +368,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi return PenumbraApiEc.Success; } - public PenumbraApiEc AddTemporaryModAll( string tag, IReadOnlyDictionary< string, string > paths, IReadOnlySet< string > manipCodes, - int priority ) + public PenumbraApiEc AddTemporaryModAll( string tag, Dictionary< string, string > paths, HashSet< string > manipCodes, int priority ) { CheckInitialized(); if( !ConvertPaths( paths, out var p ) ) @@ -385,11 +388,11 @@ public class PenumbraApi : IDisposable, IPenumbraApi }; } - public PenumbraApiEc AddTemporaryMod( string tag, string collectionName, IReadOnlyDictionary< string, string > paths, - IReadOnlySet< string > manipCodes, int priority ) + public PenumbraApiEc AddTemporaryMod( string tag, string collectionName, Dictionary< string, string > paths, HashSet< string > manipCodes, + int priority ) { CheckInitialized(); - if( !Penumbra.TempMods.Collections.TryGetValue( collectionName, out var collection ) + if( !Penumbra.TempMods.Collections.Values.FindFirst( c => c.Name == collectionName, out var collection ) && !Penumbra.CollectionManager.ByName( collectionName, out collection ) ) { return PenumbraApiEc.CollectionMissing; @@ -426,7 +429,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi public PenumbraApiEc RemoveTemporaryMod( string tag, string collectionName, int priority ) { CheckInitialized(); - if( !Penumbra.TempMods.Collections.TryGetValue( collectionName, out var collection ) + if( !Penumbra.TempMods.Collections.Values.FindFirst( c => c.Name == collectionName, out var collection ) && !Penumbra.CollectionManager.ByName( collectionName, out collection ) ) { return PenumbraApiEc.CollectionMissing; @@ -530,16 +533,19 @@ public class PenumbraApi : IDisposable, IPenumbraApi manips = new HashSet< MetaManipulation >( manipStrings.Count ); foreach( var m in manipStrings ) { - if( Functions.FromCompressedBase64< MetaManipulation >( m, out var manip ) != MetaManipulation.CurrentVersion ) + if( Functions.FromCompressedBase64< MetaManipulation[] >( m, out var manipArray ) != MetaManipulation.CurrentVersion ) { manips = null; return false; } - if( !manips.Add( manip ) ) + foreach( var manip in manipArray! ) { - manips = null; - return false; + if( !manips.Add( manip ) ) + { + manips = null; + return false; + } } } diff --git a/Penumbra/Api/PenumbraIpc.cs b/Penumbra/Api/PenumbraIpc.cs index d146f0c3..600dcf9e 100644 --- a/Penumbra/Api/PenumbraIpc.cs +++ b/Penumbra/Api/PenumbraIpc.cs @@ -520,10 +520,10 @@ public partial class PenumbraIpc internal ICallGateProvider< string, string, bool, (PenumbraApiEc, string) >? ProviderCreateTemporaryCollection; internal ICallGateProvider< string, PenumbraApiEc >? ProviderRemoveTemporaryCollection; - internal ICallGateProvider< string, IReadOnlyDictionary< string, string >, IReadOnlySet< string >, int, PenumbraApiEc >? + internal ICallGateProvider< string, Dictionary< string, string >, HashSet< string >, int, PenumbraApiEc >? ProviderAddTemporaryModAll; - internal ICallGateProvider< string, string, IReadOnlyDictionary< string, string >, IReadOnlySet< string >, int, PenumbraApiEc >? + internal ICallGateProvider< string, string, Dictionary< string, string >, HashSet< string >, int, PenumbraApiEc >? ProviderAddTemporaryMod; internal ICallGateProvider< string, int, PenumbraApiEc >? ProviderRemoveTemporaryModAll; @@ -556,7 +556,7 @@ public partial class PenumbraIpc try { ProviderAddTemporaryModAll = - pi.GetIpcProvider< string, IReadOnlyDictionary< string, string >, IReadOnlySet< string >, int, PenumbraApiEc >( + pi.GetIpcProvider< string, Dictionary< string, string >, HashSet< string >, int, PenumbraApiEc >( LabelProviderAddTemporaryModAll ); ProviderAddTemporaryModAll.RegisterFunc( Api.AddTemporaryModAll ); } @@ -568,7 +568,7 @@ public partial class PenumbraIpc try { ProviderAddTemporaryMod = - pi.GetIpcProvider< string, string, IReadOnlyDictionary< string, string >, IReadOnlySet< string >, int, PenumbraApiEc >( + pi.GetIpcProvider< string, string, Dictionary< string, string >, HashSet< string >, int, PenumbraApiEc >( LabelProviderAddTemporaryMod ); ProviderAddTemporaryMod.RegisterFunc( Api.AddTemporaryMod ); } diff --git a/Penumbra/Api/TempModManager.cs b/Penumbra/Api/TempModManager.cs index b3be6577..cebc6de9 100644 --- a/Penumbra/Api/TempModManager.cs +++ b/Penumbra/Api/TempModManager.cs @@ -117,7 +117,7 @@ public class TempModManager return RedirectResult.NotRegistered; } - var removed = _modsForAllCollections.RemoveAll( m => + var removed = list.RemoveAll( m => { if( m.Name != tag || priority != null && m.Priority != priority.Value ) { diff --git a/Penumbra/Interop/Resolver/PathResolver.Data.cs b/Penumbra/Interop/Resolver/PathResolver.Data.cs index 14a876b4..8614bbc4 100644 --- a/Penumbra/Interop/Resolver/PathResolver.Data.cs +++ b/Penumbra/Interop/Resolver/PathResolver.Data.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Dalamud.Hooking; using Dalamud.Logging; @@ -293,7 +294,8 @@ public unsafe partial class PathResolver { // Early return if we prefer the actors own name over its owner. actorName = new Utf8String( gameObject->Name ).ToString(); - if( actorName.Length > 0 && Penumbra.CollectionManager.Characters.TryGetValue( actorName, out var actorCollection ) ) + if( actorName.Length > 0 + && CollectionByActorName( actorName, out var actorCollection ) ) { return actorCollection; } @@ -313,7 +315,7 @@ public unsafe partial class PathResolver ?? GetOwnerName( gameObject ) ?? actorName ?? new Utf8String( gameObject->Name ).ToString(); // First check temporary character collections, then the own configuration. - return Penumbra.TempMods.Collections.TryGetValue( actualName, out var c ) ? c : Penumbra.CollectionManager.Character( actualName ); + return CollectionByActorName( actualName, out var c ) ? c : Penumbra.CollectionManager.Default; } catch( Exception e ) { @@ -322,6 +324,12 @@ public unsafe partial class PathResolver } } + // Check both temporary and permanent character collections. Temporary first. + private static bool CollectionByActorName( string name, [NotNullWhen( true )] out ModCollection? collection ) + => Penumbra.TempMods.Collections.TryGetValue( name, out collection ) + || Penumbra.CollectionManager.Characters.TryGetValue( name, out collection ); + + // Update collections linked to Game/DrawObjects due to a change in collection configuration. private void CheckCollections( ModCollection.Type type, ModCollection? _1, ModCollection? _2, string? name ) {