Add ReverseResolvePlayerPathsAsync.

This commit is contained in:
Ottermandias 2023-10-22 15:36:47 +02:00
parent f2ef0e15d3
commit f910dcf1e0
6 changed files with 115 additions and 63 deletions

@ -1 +1 @@
Subproject commit 839cc8f270abb6a938d71596bef05b4a9b3ab0ea Subproject commit eb9e6d65d51db9c8ed11d74332f8390d5d813727

View file

@ -16,6 +16,7 @@ using Penumbra.Services;
using Penumbra.UI; using Penumbra.UI;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using ImGuiScene;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
namespace Penumbra.Api; namespace Penumbra.Api;
@ -566,10 +567,11 @@ public class IpcTester : IDisposable
{ {
private readonly DalamudPluginInterface _pi; private readonly DalamudPluginInterface _pi;
private string _currentResolvePath = string.Empty; private string _currentResolvePath = string.Empty;
private string _currentResolveCharacter = string.Empty; private string _currentResolveCharacter = string.Empty;
private string _currentReversePath = string.Empty; private string _currentReversePath = string.Empty;
private int _currentReverseIdx = 0; private int _currentReverseIdx = 0;
private Task<(string[], string[][])> _task = Task.FromException<(string[], string[][])>(new Exception());
public Resolve(DalamudPluginInterface pi) public Resolve(DalamudPluginInterface pi)
=> _pi = pi; => _pi = pi;
@ -645,37 +647,55 @@ public class IpcTester : IDisposable
} }
} }
DrawIntro(Ipc.ResolvePlayerPaths.Label, "Resolved Paths (Player)"); var forwardArray = _currentResolvePath.Length > 0
if (_currentResolvePath.Length > 0 || _currentReversePath.Length > 0) ? new[]
{
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) _currentResolvePath,
text = $"Forward: {ret.Item1[0]} | Reverse: {string.Join("; ", ret.Item2[0])}.";
else
text = $"Forward: {ret.Item1[0]}.";
} }
else if (ret.Item2.Length > 0) : Array.Empty<string>();
var reverseArray = _currentReversePath.Length > 0
? new[]
{ {
text = $"Reverse: {string.Join("; ", ret.Item2[0])}."; _currentReversePath,
}
: Array.Empty<string>();
string ConvertText((string[], string[][]) data)
{
var text = string.Empty;
if (data.Item1.Length > 0)
{
if (data.Item2.Length > 0)
text = $"Forward: {data.Item1[0]} | Reverse: {string.Join("; ", data.Item2[0])}.";
else
text = $"Forward: {data.Item1[0]}.";
}
else if (data.Item2.Length > 0)
{
text = $"Reverse: {string.Join("; ", data.Item2[0])}.";
} }
ImGui.TextUnformatted(text); return text;
} }
;
DrawIntro(Ipc.ResolvePlayerPaths.Label, "Resolved Paths (Player)");
if (forwardArray.Length > 0 || reverseArray.Length > 0)
{
var ret = Ipc.ResolvePlayerPaths.Subscriber(_pi).Invoke(forwardArray, reverseArray);
ImGui.TextUnformatted(ConvertText(ret));
}
DrawIntro(Ipc.ResolvePlayerPathsAsync.Label, "Resolved Paths Async (Player)");
if (ImGui.Button("Start"))
_task = Ipc.ResolvePlayerPathsAsync.Subscriber(_pi).Invoke(forwardArray, reverseArray);
var hovered = ImGui.IsItemHovered();
ImGui.SameLine();
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(_task.Status.ToString());
if ((hovered || ImGui.IsItemHovered()) && _task.IsCompletedSuccessfully)
ImGui.SetTooltip(ConvertText(_task.Result));
} }
} }
@ -1442,12 +1462,12 @@ public class IpcTester : IDisposable
DrawIntro(Ipc.GetGameObjectResourcePaths.Label, "Get GameObject resource paths"); DrawIntro(Ipc.GetGameObjectResourcePaths.Label, "Get GameObject resource paths");
if (ImGui.Button("Get##GameObjectResourcePaths")) if (ImGui.Button("Get##GameObjectResourcePaths"))
{ {
var gameObjects = GetSelectedGameObjects(); var gameObjects = GetSelectedGameObjects();
var subscriber = Ipc.GetGameObjectResourcePaths.Subscriber(_pi); var subscriber = Ipc.GetGameObjectResourcePaths.Subscriber(_pi);
_stopwatch.Restart(); _stopwatch.Restart();
var resourcePaths = subscriber.Invoke(gameObjects); var resourcePaths = subscriber.Invoke(gameObjects);
_lastCallDuration = _stopwatch.Elapsed; _lastCallDuration = _stopwatch.Elapsed;
_lastGameObjectResourcePaths = gameObjects _lastGameObjectResourcePaths = gameObjects
.Select(i => GameObjectToString(i)) .Select(i => GameObjectToString(i))
.Zip(resourcePaths) .Zip(resourcePaths)
@ -1459,11 +1479,11 @@ public class IpcTester : IDisposable
DrawIntro(Ipc.GetPlayerResourcePaths.Label, "Get local player resource paths"); DrawIntro(Ipc.GetPlayerResourcePaths.Label, "Get local player resource paths");
if (ImGui.Button("Get##PlayerResourcePaths")) if (ImGui.Button("Get##PlayerResourcePaths"))
{ {
var subscriber = Ipc.GetPlayerResourcePaths.Subscriber(_pi); var subscriber = Ipc.GetPlayerResourcePaths.Subscriber(_pi);
_stopwatch.Restart(); _stopwatch.Restart();
var resourcePaths = subscriber.Invoke(); var resourcePaths = subscriber.Invoke();
_lastCallDuration = _stopwatch.Elapsed; _lastCallDuration = _stopwatch.Elapsed;
_lastPlayerResourcePaths = resourcePaths _lastPlayerResourcePaths = resourcePaths
.Select(pair => (GameObjectToString(pair.Key), (IReadOnlyDictionary<string, string[]>?)pair.Value)) .Select(pair => (GameObjectToString(pair.Key), (IReadOnlyDictionary<string, string[]>?)pair.Value))
.ToArray(); .ToArray();
@ -1474,12 +1494,12 @@ public class IpcTester : IDisposable
DrawIntro(Ipc.GetGameObjectResourcesOfType.Label, "Get GameObject resources of type"); DrawIntro(Ipc.GetGameObjectResourcesOfType.Label, "Get GameObject resources of type");
if (ImGui.Button("Get##GameObjectResourcesOfType")) if (ImGui.Button("Get##GameObjectResourcesOfType"))
{ {
var gameObjects = GetSelectedGameObjects(); var gameObjects = GetSelectedGameObjects();
var subscriber = Ipc.GetGameObjectResourcesOfType.Subscriber(_pi); var subscriber = Ipc.GetGameObjectResourcesOfType.Subscriber(_pi);
_stopwatch.Restart(); _stopwatch.Restart();
var resourcesOfType = subscriber.Invoke(_type, _withUIData, gameObjects); var resourcesOfType = subscriber.Invoke(_type, _withUIData, gameObjects);
_lastCallDuration = _stopwatch.Elapsed; _lastCallDuration = _stopwatch.Elapsed;
_lastGameObjectResourcesOfType = gameObjects _lastGameObjectResourcesOfType = gameObjects
.Select(i => GameObjectToString(i)) .Select(i => GameObjectToString(i))
.Zip(resourcesOfType) .Zip(resourcesOfType)
@ -1491,11 +1511,11 @@ public class IpcTester : IDisposable
DrawIntro(Ipc.GetPlayerResourcesOfType.Label, "Get local player resources of type"); DrawIntro(Ipc.GetPlayerResourcesOfType.Label, "Get local player resources of type");
if (ImGui.Button("Get##PlayerResourcesOfType")) if (ImGui.Button("Get##PlayerResourcesOfType"))
{ {
var subscriber = Ipc.GetPlayerResourcesOfType.Subscriber(_pi); var subscriber = Ipc.GetPlayerResourcesOfType.Subscriber(_pi);
_stopwatch.Restart(); _stopwatch.Restart();
var resourcesOfType = subscriber.Invoke(_type, _withUIData); var resourcesOfType = subscriber.Invoke(_type, _withUIData);
_lastCallDuration = _stopwatch.Elapsed; _lastCallDuration = _stopwatch.Elapsed;
_lastPlayerResourcesOfType = resourcesOfType _lastPlayerResourcesOfType = resourcesOfType
.Select(pair => (GameObjectToString(pair.Key), (IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>?)pair.Value)) .Select(pair => (GameObjectToString(pair.Key), (IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>?)pair.Value))
.ToArray(); .ToArray();
@ -1504,10 +1524,10 @@ public class IpcTester : IDisposable
} }
DrawPopup(nameof(Ipc.GetGameObjectResourcePaths), ref _lastGameObjectResourcePaths, DrawResourcePaths, _lastCallDuration); DrawPopup(nameof(Ipc.GetGameObjectResourcePaths), ref _lastGameObjectResourcePaths, DrawResourcePaths, _lastCallDuration);
DrawPopup(nameof(Ipc.GetPlayerResourcePaths), ref _lastPlayerResourcePaths, DrawResourcePaths, _lastCallDuration); DrawPopup(nameof(Ipc.GetPlayerResourcePaths), ref _lastPlayerResourcePaths, DrawResourcePaths, _lastCallDuration);
DrawPopup(nameof(Ipc.GetGameObjectResourcesOfType), ref _lastGameObjectResourcesOfType, DrawResourcesOfType, _lastCallDuration); DrawPopup(nameof(Ipc.GetGameObjectResourcesOfType), ref _lastGameObjectResourcesOfType, DrawResourcesOfType, _lastCallDuration);
DrawPopup(nameof(Ipc.GetPlayerResourcesOfType), ref _lastPlayerResourcesOfType, DrawResourcesOfType, _lastCallDuration); DrawPopup(nameof(Ipc.GetPlayerResourcesOfType), ref _lastPlayerResourcesOfType, DrawResourcesOfType, _lastCallDuration);
} }
private static void DrawPopup<T>(string popupId, ref T? result, Action<T> drawResult, TimeSpan duration) where T : class private static void DrawPopup<T>(string popupId, ref T? result, Action<T> drawResult, TimeSpan duration) where T : class
@ -1573,7 +1593,7 @@ public class IpcTester : IDisposable
return; return;
ImGui.TableSetupColumn("Actual Path", ImGuiTableColumnFlags.WidthStretch, 0.6f); ImGui.TableSetupColumn("Actual Path", ImGuiTableColumnFlags.WidthStretch, 0.6f);
ImGui.TableSetupColumn("Game Paths", ImGuiTableColumnFlags.WidthStretch, 0.4f); ImGui.TableSetupColumn("Game Paths", ImGuiTableColumnFlags.WidthStretch, 0.4f);
ImGui.TableHeadersRow(); ImGui.TableHeadersRow();
foreach (var (actualPath, gamePaths) in paths) foreach (var (actualPath, gamePaths) in paths)
@ -1596,7 +1616,7 @@ public class IpcTester : IDisposable
return; return;
ImGui.TableSetupColumn("Resource Handle", ImGuiTableColumnFlags.WidthStretch, 0.15f); ImGui.TableSetupColumn("Resource Handle", ImGuiTableColumnFlags.WidthStretch, 0.15f);
ImGui.TableSetupColumn("Actual Path", ImGuiTableColumnFlags.WidthStretch, _withUIData ? 0.55f : 0.85f); ImGui.TableSetupColumn("Actual Path", ImGuiTableColumnFlags.WidthStretch, _withUIData ? 0.55f : 0.85f);
if (_withUIData) if (_withUIData)
ImGui.TableSetupColumn("Icon & Name", ImGuiTableColumnFlags.WidthStretch, 0.3f); ImGui.TableSetupColumn("Icon & Name", ImGuiTableColumnFlags.WidthStretch, 0.3f);
ImGui.TableHeadersRow(); ImGui.TableHeadersRow();
@ -1626,8 +1646,8 @@ public class IpcTester : IDisposable
private ushort[] GetSelectedGameObjects() private ushort[] GetSelectedGameObjects()
=> _gameObjectIndices.Split(',') => _gameObjectIndices.Split(',')
.SelectWhere(index => (ushort.TryParse(index.Trim(), out var i), i)) .SelectWhere(index => (ushort.TryParse(index.Trim(), out var i), i))
.ToArray(); .ToArray();
private unsafe string GameObjectToString(ObjectIndex gameObjectIndex) private unsafe string GameObjectToString(ObjectIndex gameObjectIndex)
{ {

View file

@ -388,6 +388,30 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return (resolved, reverseResolved.Select(a => a.Select(p => p.ToString()).ToArray()).ToArray()); return (resolved, reverseResolved.Select(a => a.Select(p => p.ToString()).ToArray()).ToArray());
} }
public async Task<(string[], string[][])> ResolvePlayerPathsAsync(string[] forward, string[] reverse)
{
CheckInitialized();
if (!_config.EnableMods)
return (forward, reverse.Select(p => new[]
{
p,
}).ToArray());
return await Task.Run(async () =>
{
var playerCollection = await _dalamud.Framework.RunOnFrameworkThread(_collectionResolver.PlayerCollection).ConfigureAwait(false);
var forwardTask = Task.Run(() =>
{
var forwardRet = new string[forward.Length];
Parallel.For(0, forward.Length, idx => forwardRet[idx] = ResolvePath(forward[idx], _modManager, playerCollection));
return forwardRet;
}).ConfigureAwait(false);
var reverseTask = Task.Run(() => playerCollection.ReverseResolvePaths(reverse)).ConfigureAwait(false);
var reverseResolved = (await reverseTask).Select(a => a.Select(p => p.ToString()).ToArray()).ToArray();
return (await forwardTask, reverseResolved);
});
}
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));

View file

@ -53,15 +53,16 @@ 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; internal readonly FuncProvider<string[], string[], (string[], string[][])> ResolvePlayerPaths;
internal readonly FuncProvider<string[], string[], Task<(string[], string[][])>> ResolvePlayerPathsAsync;
// Collections // Collections
internal readonly FuncProvider<IList<string>> GetCollections; internal readonly FuncProvider<IList<string>> GetCollections;
@ -119,10 +120,15 @@ public class PenumbraIpcProviders : IDisposable
internal readonly FuncProvider<string, string, int, PenumbraApiEc> RemoveTemporaryMod; internal readonly FuncProvider<string, string, int, PenumbraApiEc> RemoveTemporaryMod;
// Resource Tree // Resource Tree
internal readonly FuncProvider<ushort[], IReadOnlyDictionary<string, string[]>?[]> GetGameObjectResourcePaths; internal readonly FuncProvider<ushort[], IReadOnlyDictionary<string, string[]>?[]> GetGameObjectResourcePaths;
internal readonly FuncProvider<IReadOnlyDictionary<ushort, IReadOnlyDictionary<string, string[]>>> GetPlayerResourcePaths; internal readonly FuncProvider<IReadOnlyDictionary<ushort, IReadOnlyDictionary<string, string[]>>> GetPlayerResourcePaths;
internal readonly FuncProvider<ResourceType, bool, ushort[], IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>?[]> GetGameObjectResourcesOfType;
internal readonly FuncProvider<ResourceType, bool, IReadOnlyDictionary<ushort, IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>>> GetPlayerResourcesOfType; internal readonly FuncProvider<ResourceType, bool, ushort[], IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>?[]>
GetGameObjectResourcesOfType;
internal readonly
FuncProvider<ResourceType, bool, IReadOnlyDictionary<ushort, IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>>>
GetPlayerResourcesOfType;
public PenumbraIpcProviders(DalamudServices dalamud, IPenumbraApi api, ModManager modManager, CollectionManager collections, public PenumbraIpcProviders(DalamudServices dalamud, IPenumbraApi api, ModManager modManager, CollectionManager collections,
TempModManager tempMods, TempCollectionManager tempCollections, SaveService saveService, Configuration config) TempModManager tempMods, TempCollectionManager tempCollections, SaveService saveService, Configuration config)
@ -184,6 +190,7 @@ public class PenumbraIpcProviders : IDisposable
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); ResolvePlayerPaths = Ipc.ResolvePlayerPaths.Provider(pi, Api.ResolvePlayerPaths);
ResolvePlayerPathsAsync = Ipc.ResolvePlayerPathsAsync.Provider(pi, Api.ResolvePlayerPathsAsync);
// Collections // Collections
GetCollections = Ipc.GetCollections.Provider(pi, Api.GetCollections); GetCollections = Ipc.GetCollections.Provider(pi, Api.GetCollections);
@ -301,6 +308,7 @@ public class PenumbraIpcProviders : IDisposable
ReverseResolveGameObjectPath.Dispose(); ReverseResolveGameObjectPath.Dispose();
ReverseResolvePlayerPath.Dispose(); ReverseResolvePlayerPath.Dispose();
ResolvePlayerPaths.Dispose(); ResolvePlayerPaths.Dispose();
ResolvePlayerPathsAsync.Dispose();
// Collections // Collections
GetCollections.Dispose(); GetCollections.Dispose();

View file

@ -23,7 +23,7 @@ public class CollectionCache : IDisposable
private readonly ModCollection _collection; private readonly ModCollection _collection;
public readonly CollectionModData ModData = new(); public readonly CollectionModData ModData = new();
public readonly SortedList<string, (SingleArray<IMod>, object?)> _changedItems = new(); public readonly SortedList<string, (SingleArray<IMod>, object?)> _changedItems = new();
public readonly Dictionary<Utf8GamePath, ModPath> ResolvedFiles = new(); public readonly ConcurrentDictionary<Utf8GamePath, ModPath> ResolvedFiles = new();
public readonly MetaCache Meta; public readonly MetaCache Meta;
public readonly Dictionary<IMod, SingleArray<ModConflicts>> _conflicts = new(); public readonly Dictionary<IMod, SingleArray<ModConflicts>> _conflicts = new();
@ -146,7 +146,7 @@ public class CollectionCache : IDisposable
ModData.RemovePath(modPath.Mod, path); ModData.RemovePath(modPath.Mod, path);
if (fullPath.FullName.Length > 0) if (fullPath.FullName.Length > 0)
{ {
ResolvedFiles.Add(path, new ModPath(Mod.ForcedFiles, fullPath)); ResolvedFiles.TryAdd(path, new ModPath(Mod.ForcedFiles, fullPath));
InvokeResolvedFileChange(_collection, ResolvedFileChanged.Type.Replaced, path, fullPath, modPath.Path, InvokeResolvedFileChange(_collection, ResolvedFileChanged.Type.Replaced, path, fullPath, modPath.Path,
Mod.ForcedFiles); Mod.ForcedFiles);
} }
@ -157,7 +157,7 @@ public class CollectionCache : IDisposable
} }
else if (fullPath.FullName.Length > 0) else if (fullPath.FullName.Length > 0)
{ {
ResolvedFiles.Add(path, new ModPath(Mod.ForcedFiles, fullPath)); ResolvedFiles.TryAdd(path, new ModPath(Mod.ForcedFiles, fullPath));
InvokeResolvedFileChange(_collection, ResolvedFileChanged.Type.Added, path, fullPath, FullPath.Empty, Mod.ForcedFiles); InvokeResolvedFileChange(_collection, ResolvedFileChanged.Type.Added, path, fullPath, FullPath.Empty, Mod.ForcedFiles);
} }
} }

View file

@ -56,7 +56,7 @@ public partial class ModCollection
} }
internal IReadOnlyDictionary<Utf8GamePath, ModPath> ResolvedFiles internal IReadOnlyDictionary<Utf8GamePath, ModPath> ResolvedFiles
=> _cache?.ResolvedFiles ?? new Dictionary<Utf8GamePath, ModPath>(); => _cache?.ResolvedFiles ?? new ConcurrentDictionary<Utf8GamePath, ModPath>();
internal IReadOnlyDictionary<string, (SingleArray<IMod>, object?)> ChangedItems internal IReadOnlyDictionary<string, (SingleArray<IMod>, object?)> ChangedItems
=> _cache?.ChangedItems ?? new Dictionary<string, (SingleArray<IMod>, object?)>(); => _cache?.ChangedItems ?? new Dictionary<string, (SingleArray<IMod>, object?)>();