Add ResolvePlayerPaths.

This commit is contained in:
Ottermandias 2023-01-31 16:12:57 +01:00
parent 58c74e839c
commit fe561f39c2
6 changed files with 126 additions and 37 deletions

@ -1 +1 @@
Subproject commit b65f0a4e2a761a3142a07587a6c0f8657f1361ee Subproject commit 97dc16ba32bf78c4d3a4d210a08010cd6d4eec3c

View file

@ -678,6 +678,32 @@ public class IpcTester : IDisposable
} }
} }
} }
DrawIntro( Ipc.ResolvePlayerPaths.Label, "Resolved Paths (Player)" );
if( _currentResolvePath.Length > 0 || _currentReversePath.Length > 0 )
{
var forwardArray = _currentResolvePath.Length > 0 ? new[] { _currentResolvePath } : Array.Empty< string >();
var reverseArray = _currentReversePath.Length > 0 ? new[] { _currentReversePath } : Array.Empty< string >();
var ret = Ipc.ResolvePlayerPaths.Subscriber( _pi ).Invoke( forwardArray, reverseArray );
var text = string.Empty;
if( ret.Item1.Length > 0 )
{
if( ret.Item2.Length > 0 )
{
text = $"Forward: {ret.Item1[ 0 ]} | Reverse: {string.Join( "; ", ret.Item2[ 0 ] )}.";
}
else
{
text = $"Forward: {ret.Item1[ 0 ]}.";
}
}
else if( ret.Item2.Length > 0 )
{
text = $"Reverse: {string.Join( "; ", ret.Item2[ 0 ] )}.";
}
ImGui.TextUnformatted( text );
}
} }
} }
@ -685,10 +711,10 @@ public class IpcTester : IDisposable
{ {
private readonly DalamudPluginInterface _pi; private readonly DalamudPluginInterface _pi;
private int _objectIdx = 0; private int _objectIdx = 0;
private string _collectionName = string.Empty; private string _collectionName = string.Empty;
private bool _allowCreation = true; private bool _allowCreation = true;
private bool _allowDeletion = true; private bool _allowDeletion = true;
private ApiCollectionType _type = ApiCollectionType.Current; private ApiCollectionType _type = ApiCollectionType.Current;
private string _characterCollectionName = string.Empty; private string _characterCollectionName = string.Empty;
@ -709,7 +735,7 @@ public class IpcTester : IDisposable
return; return;
} }
ImGuiUtil.GenericEnumCombo( "Collection Type", 200, _type, out _type, t => ((CollectionType)t).ToName() ); ImGuiUtil.GenericEnumCombo( "Collection Type", 200, _type, out _type, t => ( ( CollectionType )t ).ToName() );
ImGui.InputInt( "Object Index##Collections", ref _objectIdx, 0, 0 ); ImGui.InputInt( "Object Index##Collections", ref _objectIdx, 0, 0 );
ImGui.InputText( "Collection Name##Collections", ref _collectionName, 64 ); ImGui.InputText( "Collection Name##Collections", ref _collectionName, 64 );
ImGui.Checkbox( "Allow Assignment Creation", ref _allowCreation ); ImGui.Checkbox( "Allow Assignment Creation", ref _allowCreation );
@ -766,8 +792,11 @@ public class IpcTester : IDisposable
{ {
( _returnCode, _oldCollection ) = Ipc.SetCollectionForObject.Subscriber( _pi ).Invoke( _objectIdx, _collectionName, _allowCreation, _allowDeletion ); ( _returnCode, _oldCollection ) = Ipc.SetCollectionForObject.Subscriber( _pi ).Invoke( _objectIdx, _collectionName, _allowCreation, _allowDeletion );
} }
if( _returnCode == PenumbraApiEc.NothingChanged && _oldCollection.IsNullOrEmpty() ) if( _returnCode == PenumbraApiEc.NothingChanged && _oldCollection.IsNullOrEmpty() )
{
_oldCollection = null; _oldCollection = null;
}
DrawIntro( Ipc.GetChangedItems.Label, "Changed Item List" ); DrawIntro( Ipc.GetChangedItems.Label, "Changed Item List" );
ImGui.SetNextItemWidth( 200 * ImGuiHelpers.GlobalScale ); ImGui.SetNextItemWidth( 200 * ImGuiHelpers.GlobalScale );

View file

@ -13,6 +13,7 @@ using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.String; using Penumbra.String;
@ -23,7 +24,7 @@ namespace Penumbra.Api;
public class PenumbraApi : IDisposable, IPenumbraApi public class PenumbraApi : IDisposable, IPenumbraApi
{ {
public (int, int) ApiVersion public (int, int) ApiVersion
=> ( 4, 18 ); => ( 4, 19 );
private Penumbra? _penumbra; private Penumbra? _penumbra;
private Lumina.GameData? _lumina; private Lumina.GameData? _lumina;
@ -271,6 +272,20 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return ret.Select( r => r.ToString() ).ToArray(); return ret.Select( r => r.ToString() ).ToArray();
} }
public (string[], string[][]) ResolvePlayerPaths( string[] forward, string[] reverse )
{
CheckInitialized();
if( !Penumbra.Config.EnableMods )
{
return ( forward, reverse.Select( p => new[] { p } ).ToArray() );
}
var playerCollection = PathResolver.PlayerCollection();
var resolved = forward.Select( p => ResolvePath( p, Penumbra.ModManager, playerCollection ) ).ToArray();
var reverseResolved = playerCollection.ReverseResolvePaths( reverse );
return ( resolved, reverseResolved.Select( a => a.Select( p => p.ToString() ).ToArray() ).ToArray() );
}
public T? GetFile< T >( string gamePath ) where T : FileResource public T? GetFile< T >( string gamePath ) where T : FileResource
=> GetFileIntern< T >( ResolveDefaultPath( gamePath ) ); => GetFileIntern< T >( ResolveDefaultPath( gamePath ) );
@ -306,17 +321,21 @@ public class PenumbraApi : IDisposable, IPenumbraApi
{ {
CheckInitialized(); CheckInitialized();
if( !Enum.IsDefined( type ) ) if( !Enum.IsDefined( type ) )
{
return string.Empty; return string.Empty;
}
var collection = Penumbra.CollectionManager.ByType( ( CollectionType )type ); var collection = Penumbra.CollectionManager.ByType( ( CollectionType )type );
return collection?.Name ?? string.Empty; return collection?.Name ?? string.Empty;
} }
public (PenumbraApiEc, string OldCollection) SetCollectionForType( Enums.ApiCollectionType type, string collectionName, bool allowCreateNew, bool allowDelete ) public (PenumbraApiEc, string OldCollection) SetCollectionForType( ApiCollectionType type, string collectionName, bool allowCreateNew, bool allowDelete )
{ {
CheckInitialized(); CheckInitialized();
if( !Enum.IsDefined( type ) ) if( !Enum.IsDefined( type ) )
{
return ( PenumbraApiEc.InvalidArgument, string.Empty ); return ( PenumbraApiEc.InvalidArgument, string.Empty );
}
var oldCollection = Penumbra.CollectionManager.ByType( ( CollectionType )type )?.Name ?? string.Empty; var oldCollection = Penumbra.CollectionManager.ByType( ( CollectionType )type )?.Name ?? string.Empty;
@ -327,18 +346,18 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return ( PenumbraApiEc.NothingChanged, oldCollection ); return ( PenumbraApiEc.NothingChanged, oldCollection );
} }
if( !allowDelete || type is Enums.ApiCollectionType.Current or Enums.ApiCollectionType.Default or Enums.ApiCollectionType.Interface ) if( !allowDelete || type is ApiCollectionType.Current or ApiCollectionType.Default or ApiCollectionType.Interface )
{ {
return ( PenumbraApiEc.AssignmentDeletionDisallowed, oldCollection ); return ( PenumbraApiEc.AssignmentDeletionDisallowed, oldCollection );
} }
Penumbra.CollectionManager.RemoveSpecialCollection( (CollectionType) type ); Penumbra.CollectionManager.RemoveSpecialCollection( ( CollectionType )type );
return ( PenumbraApiEc.Success, oldCollection ); return ( PenumbraApiEc.Success, oldCollection );
} }
if( !Penumbra.CollectionManager.ByName( collectionName, out var collection ) ) if( !Penumbra.CollectionManager.ByName( collectionName, out var collection ) )
{ {
return (PenumbraApiEc.CollectionMissing, oldCollection); return ( PenumbraApiEc.CollectionMissing, oldCollection );
} }
if( oldCollection.Length == 0 ) if( oldCollection.Length == 0 )
@ -355,7 +374,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return ( PenumbraApiEc.NothingChanged, oldCollection ); return ( PenumbraApiEc.NothingChanged, oldCollection );
} }
Penumbra.CollectionManager.SetCollection( collection, (CollectionType) type ); Penumbra.CollectionManager.SetCollection( collection, ( CollectionType )type );
return ( PenumbraApiEc.Success, oldCollection ); return ( PenumbraApiEc.Success, oldCollection );
} }
@ -392,28 +411,29 @@ public class PenumbraApi : IDisposable, IPenumbraApi
{ {
if( oldCollection.Length == 0 ) if( oldCollection.Length == 0 )
{ {
return (PenumbraApiEc.NothingChanged, oldCollection); return ( PenumbraApiEc.NothingChanged, oldCollection );
} }
if( !allowDelete ) if( !allowDelete )
{ {
return (PenumbraApiEc.AssignmentDeletionDisallowed, oldCollection); return ( PenumbraApiEc.AssignmentDeletionDisallowed, oldCollection );
} }
var idx = Penumbra.CollectionManager.Individuals.Index( id ); var idx = Penumbra.CollectionManager.Individuals.Index( id );
Penumbra.CollectionManager.RemoveIndividualCollection(idx ); Penumbra.CollectionManager.RemoveIndividualCollection( idx );
return (PenumbraApiEc.Success, oldCollection); return ( PenumbraApiEc.Success, oldCollection );
} }
if( !Penumbra.CollectionManager.ByName( collectionName, out var collection ) ) if( !Penumbra.CollectionManager.ByName( collectionName, out var collection ) )
{ {
return (PenumbraApiEc.CollectionMissing, oldCollection); return ( PenumbraApiEc.CollectionMissing, oldCollection );
} }
if( oldCollection.Length == 0 ) if( oldCollection.Length == 0 )
{ {
if( !allowCreateNew ) if( !allowCreateNew )
{ {
return (PenumbraApiEc.AssignmentCreationDisallowed, oldCollection); return ( PenumbraApiEc.AssignmentCreationDisallowed, oldCollection );
} }
var ids = Penumbra.CollectionManager.Individuals.GetGroup( id ); var ids = Penumbra.CollectionManager.Individuals.GetGroup( id );
@ -421,11 +441,11 @@ public class PenumbraApi : IDisposable, IPenumbraApi
} }
else if( oldCollection == collection.Name ) else if( oldCollection == collection.Name )
{ {
return (PenumbraApiEc.NothingChanged, oldCollection); return ( PenumbraApiEc.NothingChanged, oldCollection );
} }
Penumbra.CollectionManager.SetCollection( collection, CollectionType.Individual, Penumbra.CollectionManager.Individuals.Index( id ) ); Penumbra.CollectionManager.SetCollection( collection, CollectionType.Individual, Penumbra.CollectionManager.Individuals.Index( id ) );
return (PenumbraApiEc.Success, oldCollection); return ( PenumbraApiEc.Success, oldCollection );
} }
public IList< string > GetCollections() public IList< string > GetCollections()
@ -983,6 +1003,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
=> ChangedItemClicked?.Invoke( button, it ); => ChangedItemClicked?.Invoke( button, it );
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
private void CheckInitialized() private void CheckInitialized()
{ {
if( !Valid ) if( !Valid )
@ -993,6 +1014,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
// Return the collection associated to a current game object. If it does not exist, return the default collection. // Return the collection associated to a current game object. If it does not exist, return the default collection.
// If the index is invalid, returns false and the default collection. // If the index is invalid, returns false and the default collection.
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
private static unsafe bool AssociatedCollection( int gameObjectIdx, out ModCollection collection ) private static unsafe bool AssociatedCollection( int gameObjectIdx, out ModCollection collection )
{ {
collection = Penumbra.CollectionManager.Default; collection = Penumbra.CollectionManager.Default;
@ -1011,17 +1033,20 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return true; return true;
} }
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
private static unsafe ActorIdentifier AssociatedIdentifier( int gameObjectIdx ) private static unsafe ActorIdentifier AssociatedIdentifier( int gameObjectIdx )
{ {
if( gameObjectIdx < 0 || gameObjectIdx >= Dalamud.Objects.Length ) if( gameObjectIdx < 0 || gameObjectIdx >= Dalamud.Objects.Length )
{ {
return ActorIdentifier.Invalid; return ActorIdentifier.Invalid;
} }
var ptr = ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )Dalamud.Objects.GetObjectAddress( gameObjectIdx ); var ptr = ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )Dalamud.Objects.GetObjectAddress( gameObjectIdx );
return Penumbra.Actors.FromObject( ptr, out _, false, true ); return Penumbra.Actors.FromObject( ptr, out _, false, true );
} }
// Resolve a path given by string for a specific collection. // Resolve a path given by string for a specific collection.
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
private static string ResolvePath( string path, Mod.Manager _, ModCollection collection ) private static string ResolvePath( string path, Mod.Manager _, ModCollection collection )
{ {
if( !Penumbra.Config.EnableMods ) if( !Penumbra.Config.EnableMods )

View file

@ -49,26 +49,27 @@ public class PenumbraIpcProviders : IDisposable
internal readonly EventProvider< nint, string, string > GameObjectResourcePathResolved; internal readonly EventProvider< nint, string, string > GameObjectResourcePathResolved;
// Resolve // Resolve
internal readonly FuncProvider< string, string > ResolveDefaultPath; internal readonly FuncProvider< string, string > ResolveDefaultPath;
internal readonly FuncProvider< string, string > ResolveInterfacePath; internal readonly FuncProvider< string, string > ResolveInterfacePath;
internal readonly FuncProvider< string, string > ResolvePlayerPath; internal readonly FuncProvider< string, string > ResolvePlayerPath;
internal readonly FuncProvider< string, int, string > ResolveGameObjectPath; internal readonly FuncProvider< string, int, string > ResolveGameObjectPath;
internal readonly FuncProvider< string, string, string > ResolveCharacterPath; internal readonly FuncProvider< string, string, string > ResolveCharacterPath;
internal readonly FuncProvider< string, string, string[] > ReverseResolvePath; internal readonly FuncProvider< string, string, string[] > ReverseResolvePath;
internal readonly FuncProvider< string, int, string[] > ReverseResolveGameObjectPath; internal readonly FuncProvider< string, int, string[] > ReverseResolveGameObjectPath;
internal readonly FuncProvider< string, string[] > ReverseResolvePlayerPath; internal readonly FuncProvider< string, string[] > ReverseResolvePlayerPath;
internal readonly FuncProvider< string[], string[], (string[], string[][]) > ResolvePlayerPaths;
// Collections // Collections
internal readonly FuncProvider< IList< string > > GetCollections; internal readonly FuncProvider< IList< string > > GetCollections;
internal readonly FuncProvider< string > GetCurrentCollectionName; internal readonly FuncProvider< string > GetCurrentCollectionName;
internal readonly FuncProvider< string > GetDefaultCollectionName; internal readonly FuncProvider< string > GetDefaultCollectionName;
internal readonly FuncProvider< string > GetInterfaceCollectionName; internal readonly FuncProvider< string > GetInterfaceCollectionName;
internal readonly FuncProvider< string, (string, bool) > GetCharacterCollectionName; internal readonly FuncProvider< string, (string, bool) > GetCharacterCollectionName;
internal readonly FuncProvider< ApiCollectionType, string > GetCollectionForType; internal readonly FuncProvider< ApiCollectionType, string > GetCollectionForType;
internal readonly FuncProvider< ApiCollectionType, string, bool, bool, (PenumbraApiEc, string) > SetCollectionForType; internal readonly FuncProvider< ApiCollectionType, string, bool, bool, (PenumbraApiEc, string) > SetCollectionForType;
internal readonly FuncProvider< int, (bool, bool, string) > GetCollectionForObject; internal readonly FuncProvider< int, (bool, bool, string) > GetCollectionForObject;
internal readonly FuncProvider< int, string, bool, bool, (PenumbraApiEc, string) > SetCollectionForObject; internal readonly FuncProvider< int, string, bool, bool, (PenumbraApiEc, string) > SetCollectionForObject;
internal readonly FuncProvider< string, IReadOnlyDictionary< string, object? > > GetChangedItems; internal readonly FuncProvider< string, IReadOnlyDictionary< string, object? > > GetChangedItems;
// Meta // Meta
internal readonly FuncProvider< string > GetPlayerMetaManipulations; internal readonly FuncProvider< string > GetPlayerMetaManipulations;
@ -160,6 +161,7 @@ public class PenumbraIpcProviders : IDisposable
ReverseResolvePath = Ipc.ReverseResolvePath.Provider( pi, Api.ReverseResolvePath ); ReverseResolvePath = Ipc.ReverseResolvePath.Provider( pi, Api.ReverseResolvePath );
ReverseResolveGameObjectPath = Ipc.ReverseResolveGameObjectPath.Provider( pi, Api.ReverseResolveGameObjectPath ); ReverseResolveGameObjectPath = Ipc.ReverseResolveGameObjectPath.Provider( pi, Api.ReverseResolveGameObjectPath );
ReverseResolvePlayerPath = Ipc.ReverseResolvePlayerPath.Provider( pi, Api.ReverseResolvePlayerPath ); ReverseResolvePlayerPath = Ipc.ReverseResolvePlayerPath.Provider( pi, Api.ReverseResolvePlayerPath );
ResolvePlayerPaths = Ipc.ResolvePlayerPaths.Provider( pi, Api.ResolvePlayerPaths );
// Collections // Collections
GetCollections = Ipc.GetCollections.Provider( pi, Api.GetCollections ); GetCollections = Ipc.GetCollections.Provider( pi, Api.GetCollections );
@ -263,6 +265,7 @@ public class PenumbraIpcProviders : IDisposable
ReverseResolvePath.Dispose(); ReverseResolvePath.Dispose();
ReverseResolveGameObjectPath.Dispose(); ReverseResolveGameObjectPath.Dispose();
ReverseResolvePlayerPath.Dispose(); ReverseResolvePlayerPath.Dispose();
ResolvePlayerPaths.Dispose();
// Collections // Collections
GetCollections.Dispose(); GetCollections.Dispose();

View file

@ -4,6 +4,7 @@ using Penumbra.Meta.Manager;
using Penumbra.Mods; using Penumbra.Mods;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using Penumbra.Interop; using Penumbra.Interop;
@ -68,6 +69,9 @@ public partial class ModCollection
public IEnumerable< Utf8GamePath > ReverseResolvePath( FullPath path ) public IEnumerable< Utf8GamePath > ReverseResolvePath( FullPath path )
=> _cache?.ReverseResolvePath( path ) ?? Array.Empty< Utf8GamePath >(); => _cache?.ReverseResolvePath( path ) ?? Array.Empty< Utf8GamePath >();
public HashSet< Utf8GamePath >[] ReverseResolvePaths( string[] paths )
=> _cache?.ReverseResolvePaths( paths ) ?? paths.Select( _ => new HashSet< Utf8GamePath >() ).ToArray();
public FullPath? ResolvePath( Utf8GamePath path ) public FullPath? ResolvePath( Utf8GamePath path )
=> _cache?.ResolvePath( path ); => _cache?.ResolvePath( path );

View file

@ -5,6 +5,7 @@ using Penumbra.Meta.Manipulations;
using Penumbra.Mods; using Penumbra.Mods;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.String.Classes; using Penumbra.String.Classes;
@ -104,6 +105,33 @@ public partial class ModCollection
return iterator; return iterator;
} }
// Reverse resolve multiple paths at once for efficiency.
public HashSet< Utf8GamePath >[] ReverseResolvePaths( IReadOnlyCollection< string > fullPaths )
{
if( fullPaths.Count == 0 )
return Array.Empty< HashSet< Utf8GamePath > >();
var ret = new HashSet< Utf8GamePath >[fullPaths.Count];
var dict = new Dictionary< FullPath, int >( fullPaths.Count );
foreach( var (path, idx) in fullPaths.WithIndex() )
{
dict[ new FullPath(path) ] = idx;
ret[ idx ] = !Path.IsPathRooted( path ) && Utf8GamePath.FromString( path, out var utf8 )
? new HashSet< Utf8GamePath > { utf8 }
: new HashSet< Utf8GamePath >();
}
foreach( var (game, full) in ResolvedFiles )
{
if( dict.TryGetValue( full.Path, out var idx ) )
{
ret[ idx ].Add( game );
}
}
return ret;
}
private void OnModSettingChange( ModSettingChange type, int modIdx, int oldValue, int groupIdx, bool _ ) private void OnModSettingChange( ModSettingChange type, int modIdx, int oldValue, int groupIdx, bool _ )
{ {
switch( type ) switch( type )
@ -474,7 +502,7 @@ public partial class ModCollection
// Skip IMCs because they would result in far too many false-positive items, // 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. // since they are per set instead of per item-slot/item/variant.
var identifier = Penumbra.Identifier; var identifier = Penumbra.Identifier;
var items = new SortedList< string, object? >(512); var items = new SortedList< string, object? >( 512 );
void AddItems( IMod mod ) void AddItems( IMod mod )
{ {