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 int _objectIdx = 0;
private string _collectionName = string.Empty;
private bool _allowCreation = true;
private bool _allowDeletion = true;
private int _objectIdx = 0;
private string _collectionName = string.Empty;
private bool _allowCreation = true;
private bool _allowDeletion = true;
private ApiCollectionType _type = ApiCollectionType.Current;
private string _characterCollectionName = string.Empty;
@ -709,7 +735,7 @@ public class IpcTester : IDisposable
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.InputText( "Collection Name##Collections", ref _collectionName, 64 );
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 );
}
if( _returnCode == PenumbraApiEc.NothingChanged && _oldCollection.IsNullOrEmpty() )
{
_oldCollection = null;
}
DrawIntro( Ipc.GetChangedItems.Label, "Changed Item List" );
ImGui.SetNextItemWidth( 200 * ImGuiHelpers.GlobalScale );

View file

@ -13,6 +13,7 @@ using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Penumbra.Api.Enums;
using Penumbra.GameData.Actors;
using Penumbra.String;
@ -23,7 +24,7 @@ namespace Penumbra.Api;
public class PenumbraApi : IDisposable, IPenumbraApi
{
public (int, int) ApiVersion
=> ( 4, 18 );
=> ( 4, 19 );
private Penumbra? _penumbra;
private Lumina.GameData? _lumina;
@ -271,6 +272,20 @@ public class PenumbraApi : IDisposable, IPenumbraApi
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
=> GetFileIntern< T >( ResolveDefaultPath( gamePath ) );
@ -306,17 +321,21 @@ public class PenumbraApi : IDisposable, IPenumbraApi
{
CheckInitialized();
if( !Enum.IsDefined( type ) )
{
return string.Empty;
}
var collection = Penumbra.CollectionManager.ByType( ( CollectionType )type );
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();
if( !Enum.IsDefined( type ) )
{
return ( PenumbraApiEc.InvalidArgument, 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 );
}
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 );
}
Penumbra.CollectionManager.RemoveSpecialCollection( (CollectionType) type );
Penumbra.CollectionManager.RemoveSpecialCollection( ( CollectionType )type );
return ( PenumbraApiEc.Success, oldCollection );
}
if( !Penumbra.CollectionManager.ByName( collectionName, out var collection ) )
{
return (PenumbraApiEc.CollectionMissing, oldCollection);
return ( PenumbraApiEc.CollectionMissing, oldCollection );
}
if( oldCollection.Length == 0 )
@ -355,7 +374,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return ( PenumbraApiEc.NothingChanged, oldCollection );
}
Penumbra.CollectionManager.SetCollection( collection, (CollectionType) type );
Penumbra.CollectionManager.SetCollection( collection, ( CollectionType )type );
return ( PenumbraApiEc.Success, oldCollection );
}
@ -392,28 +411,29 @@ public class PenumbraApi : IDisposable, IPenumbraApi
{
if( oldCollection.Length == 0 )
{
return (PenumbraApiEc.NothingChanged, oldCollection);
return ( PenumbraApiEc.NothingChanged, oldCollection );
}
if( !allowDelete )
{
return (PenumbraApiEc.AssignmentDeletionDisallowed, oldCollection);
return ( PenumbraApiEc.AssignmentDeletionDisallowed, oldCollection );
}
var idx = Penumbra.CollectionManager.Individuals.Index( id );
Penumbra.CollectionManager.RemoveIndividualCollection(idx );
return (PenumbraApiEc.Success, oldCollection);
Penumbra.CollectionManager.RemoveIndividualCollection( idx );
return ( PenumbraApiEc.Success, oldCollection );
}
if( !Penumbra.CollectionManager.ByName( collectionName, out var collection ) )
{
return (PenumbraApiEc.CollectionMissing, oldCollection);
return ( PenumbraApiEc.CollectionMissing, oldCollection );
}
if( oldCollection.Length == 0 )
{
if( !allowCreateNew )
{
return (PenumbraApiEc.AssignmentCreationDisallowed, oldCollection);
return ( PenumbraApiEc.AssignmentCreationDisallowed, oldCollection );
}
var ids = Penumbra.CollectionManager.Individuals.GetGroup( id );
@ -421,11 +441,11 @@ public class PenumbraApi : IDisposable, IPenumbraApi
}
else if( oldCollection == collection.Name )
{
return (PenumbraApiEc.NothingChanged, oldCollection);
return ( PenumbraApiEc.NothingChanged, oldCollection );
}
Penumbra.CollectionManager.SetCollection( collection, CollectionType.Individual, Penumbra.CollectionManager.Individuals.Index( id ) );
return (PenumbraApiEc.Success, oldCollection);
return ( PenumbraApiEc.Success, oldCollection );
}
public IList< string > GetCollections()
@ -983,6 +1003,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
=> ChangedItemClicked?.Invoke( button, it );
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
private void CheckInitialized()
{
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.
// 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 )
{
collection = Penumbra.CollectionManager.Default;
@ -1011,17 +1033,20 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return true;
}
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
private static unsafe ActorIdentifier AssociatedIdentifier( int gameObjectIdx )
{
if( gameObjectIdx < 0 || gameObjectIdx >= Dalamud.Objects.Length )
{
return ActorIdentifier.Invalid;
}
var ptr = ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )Dalamud.Objects.GetObjectAddress( gameObjectIdx );
return Penumbra.Actors.FromObject( ptr, out _, false, true );
}
// 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 )
{
if( !Penumbra.Config.EnableMods )

View file

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

View file

@ -4,6 +4,7 @@ using Penumbra.Meta.Manager;
using Penumbra.Mods;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using Penumbra.Interop;
@ -68,6 +69,9 @@ public partial class ModCollection
public IEnumerable< Utf8GamePath > ReverseResolvePath( FullPath path )
=> _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 )
=> _cache?.ResolvePath( path );

View file

@ -5,6 +5,7 @@ using Penumbra.Meta.Manipulations;
using Penumbra.Mods;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Penumbra.Api.Enums;
using Penumbra.String.Classes;
@ -104,6 +105,33 @@ public partial class ModCollection
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 _ )
{
switch( type )
@ -474,7 +502,7 @@ public partial class ModCollection
// 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 = Penumbra.Identifier;
var items = new SortedList< string, object? >(512);
var items = new SortedList< string, object? >( 512 );
void AddItems( IMod mod )
{