From f910dcf1e0c90b6eb330166416e1fcb3e353ea85 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sun, 22 Oct 2023 15:36:47 +0200 Subject: [PATCH] Add ReverseResolvePlayerPathsAsync. --- Penumbra.Api | 2 +- Penumbra/Api/IpcTester.cs | 110 +++++++++++------- Penumbra/Api/PenumbraApi.cs | 24 ++++ Penumbra/Api/PenumbraIpcProviders.cs | 34 +++--- Penumbra/Collections/Cache/CollectionCache.cs | 6 +- .../Collections/ModCollection.Cache.Access.cs | 2 +- 6 files changed, 115 insertions(+), 63 deletions(-) diff --git a/Penumbra.Api b/Penumbra.Api index 839cc8f2..eb9e6d65 160000 --- a/Penumbra.Api +++ b/Penumbra.Api @@ -1 +1 @@ -Subproject commit 839cc8f270abb6a938d71596bef05b4a9b3ab0ea +Subproject commit eb9e6d65d51db9c8ed11d74332f8390d5d813727 diff --git a/Penumbra/Api/IpcTester.cs b/Penumbra/Api/IpcTester.cs index dd800f0a..675a61a3 100644 --- a/Penumbra/Api/IpcTester.cs +++ b/Penumbra/Api/IpcTester.cs @@ -16,6 +16,7 @@ using Penumbra.Services; using Penumbra.UI; using Penumbra.Collections.Manager; using Dalamud.Plugin.Services; +using ImGuiScene; using Penumbra.GameData.Structs; namespace Penumbra.Api; @@ -566,10 +567,11 @@ public class IpcTester : IDisposable { private readonly DalamudPluginInterface _pi; - private string _currentResolvePath = string.Empty; - private string _currentResolveCharacter = string.Empty; - private string _currentReversePath = string.Empty; - private int _currentReverseIdx = 0; + private string _currentResolvePath = string.Empty; + private string _currentResolveCharacter = string.Empty; + private string _currentReversePath = string.Empty; + private int _currentReverseIdx = 0; + private Task<(string[], string[][])> _task = Task.FromException<(string[], string[][])>(new Exception()); public Resolve(DalamudPluginInterface pi) => _pi = pi; @@ -645,37 +647,55 @@ 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(); - var reverseArray = _currentReversePath.Length > 0 - ? new[] - { - _currentReversePath, - } - : Array.Empty(); - var ret = Ipc.ResolvePlayerPaths.Subscriber(_pi).Invoke(forwardArray, reverseArray); - var text = string.Empty; - if (ret.Item1.Length > 0) + var forwardArray = _currentResolvePath.Length > 0 + ? new[] { - if (ret.Item2.Length > 0) - text = $"Forward: {ret.Item1[0]} | Reverse: {string.Join("; ", ret.Item2[0])}."; - else - text = $"Forward: {ret.Item1[0]}."; + _currentResolvePath, } - else if (ret.Item2.Length > 0) + : Array.Empty(); + var reverseArray = _currentReversePath.Length > 0 + ? new[] { - text = $"Reverse: {string.Join("; ", ret.Item2[0])}."; + _currentReversePath, + } + : Array.Empty(); + + 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"); if (ImGui.Button("Get##GameObjectResourcePaths")) { - var gameObjects = GetSelectedGameObjects(); - var subscriber = Ipc.GetGameObjectResourcePaths.Subscriber(_pi); + var gameObjects = GetSelectedGameObjects(); + var subscriber = Ipc.GetGameObjectResourcePaths.Subscriber(_pi); _stopwatch.Restart(); var resourcePaths = subscriber.Invoke(gameObjects); - _lastCallDuration = _stopwatch.Elapsed; + _lastCallDuration = _stopwatch.Elapsed; _lastGameObjectResourcePaths = gameObjects .Select(i => GameObjectToString(i)) .Zip(resourcePaths) @@ -1459,11 +1479,11 @@ public class IpcTester : IDisposable DrawIntro(Ipc.GetPlayerResourcePaths.Label, "Get local player resource paths"); if (ImGui.Button("Get##PlayerResourcePaths")) { - var subscriber = Ipc.GetPlayerResourcePaths.Subscriber(_pi); + var subscriber = Ipc.GetPlayerResourcePaths.Subscriber(_pi); _stopwatch.Restart(); var resourcePaths = subscriber.Invoke(); - _lastCallDuration = _stopwatch.Elapsed; + _lastCallDuration = _stopwatch.Elapsed; _lastPlayerResourcePaths = resourcePaths .Select(pair => (GameObjectToString(pair.Key), (IReadOnlyDictionary?)pair.Value)) .ToArray(); @@ -1474,12 +1494,12 @@ public class IpcTester : IDisposable DrawIntro(Ipc.GetGameObjectResourcesOfType.Label, "Get GameObject resources of type"); if (ImGui.Button("Get##GameObjectResourcesOfType")) { - var gameObjects = GetSelectedGameObjects(); - var subscriber = Ipc.GetGameObjectResourcesOfType.Subscriber(_pi); + var gameObjects = GetSelectedGameObjects(); + var subscriber = Ipc.GetGameObjectResourcesOfType.Subscriber(_pi); _stopwatch.Restart(); var resourcesOfType = subscriber.Invoke(_type, _withUIData, gameObjects); - _lastCallDuration = _stopwatch.Elapsed; + _lastCallDuration = _stopwatch.Elapsed; _lastGameObjectResourcesOfType = gameObjects .Select(i => GameObjectToString(i)) .Zip(resourcesOfType) @@ -1491,11 +1511,11 @@ public class IpcTester : IDisposable DrawIntro(Ipc.GetPlayerResourcesOfType.Label, "Get local player resources of type"); if (ImGui.Button("Get##PlayerResourcesOfType")) { - var subscriber = Ipc.GetPlayerResourcesOfType.Subscriber(_pi); + var subscriber = Ipc.GetPlayerResourcesOfType.Subscriber(_pi); _stopwatch.Restart(); var resourcesOfType = subscriber.Invoke(_type, _withUIData); - _lastCallDuration = _stopwatch.Elapsed; + _lastCallDuration = _stopwatch.Elapsed; _lastPlayerResourcesOfType = resourcesOfType .Select(pair => (GameObjectToString(pair.Key), (IReadOnlyDictionary?)pair.Value)) .ToArray(); @@ -1504,10 +1524,10 @@ public class IpcTester : IDisposable } 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.GetPlayerResourcesOfType), ref _lastPlayerResourcesOfType, DrawResourcesOfType, _lastCallDuration); + DrawPopup(nameof(Ipc.GetPlayerResourcesOfType), ref _lastPlayerResourcesOfType, DrawResourcesOfType, _lastCallDuration); } private static void DrawPopup(string popupId, ref T? result, Action drawResult, TimeSpan duration) where T : class @@ -1573,7 +1593,7 @@ public class IpcTester : IDisposable return; 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(); foreach (var (actualPath, gamePaths) in paths) @@ -1596,7 +1616,7 @@ public class IpcTester : IDisposable return; 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) ImGui.TableSetupColumn("Icon & Name", ImGuiTableColumnFlags.WidthStretch, 0.3f); ImGui.TableHeadersRow(); @@ -1626,8 +1646,8 @@ public class IpcTester : IDisposable private ushort[] GetSelectedGameObjects() => _gameObjectIndices.Split(',') - .SelectWhere(index => (ushort.TryParse(index.Trim(), out var i), i)) - .ToArray(); + .SelectWhere(index => (ushort.TryParse(index.Trim(), out var i), i)) + .ToArray(); private unsafe string GameObjectToString(ObjectIndex gameObjectIndex) { diff --git a/Penumbra/Api/PenumbraApi.cs b/Penumbra/Api/PenumbraApi.cs index 10b5b6bd..0ae4fcca 100644 --- a/Penumbra/Api/PenumbraApi.cs +++ b/Penumbra/Api/PenumbraApi.cs @@ -388,6 +388,30 @@ public class PenumbraApi : IDisposable, IPenumbraApi 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(string gamePath) where T : FileResource => GetFileIntern(ResolveDefaultPath(gamePath)); diff --git a/Penumbra/Api/PenumbraIpcProviders.cs b/Penumbra/Api/PenumbraIpcProviders.cs index 87de6a0c..b72073fb 100644 --- a/Penumbra/Api/PenumbraIpcProviders.cs +++ b/Penumbra/Api/PenumbraIpcProviders.cs @@ -53,15 +53,16 @@ public class PenumbraIpcProviders : IDisposable internal readonly EventProvider GameObjectResourcePathResolved; // Resolve - internal readonly FuncProvider ResolveDefaultPath; - internal readonly FuncProvider ResolveInterfacePath; - internal readonly FuncProvider ResolvePlayerPath; - internal readonly FuncProvider ResolveGameObjectPath; - internal readonly FuncProvider ResolveCharacterPath; - internal readonly FuncProvider ReverseResolvePath; - internal readonly FuncProvider ReverseResolveGameObjectPath; - internal readonly FuncProvider ReverseResolvePlayerPath; - internal readonly FuncProvider ResolvePlayerPaths; + internal readonly FuncProvider ResolveDefaultPath; + internal readonly FuncProvider ResolveInterfacePath; + internal readonly FuncProvider ResolvePlayerPath; + internal readonly FuncProvider ResolveGameObjectPath; + internal readonly FuncProvider ResolveCharacterPath; + internal readonly FuncProvider ReverseResolvePath; + internal readonly FuncProvider ReverseResolveGameObjectPath; + internal readonly FuncProvider ReverseResolvePlayerPath; + internal readonly FuncProvider ResolvePlayerPaths; + internal readonly FuncProvider> ResolvePlayerPathsAsync; // Collections internal readonly FuncProvider> GetCollections; @@ -119,10 +120,15 @@ public class PenumbraIpcProviders : IDisposable internal readonly FuncProvider RemoveTemporaryMod; // Resource Tree - internal readonly FuncProvider?[]> GetGameObjectResourcePaths; - internal readonly FuncProvider>> GetPlayerResourcePaths; - internal readonly FuncProvider?[]> GetGameObjectResourcesOfType; - internal readonly FuncProvider>> GetPlayerResourcesOfType; + internal readonly FuncProvider?[]> GetGameObjectResourcePaths; + internal readonly FuncProvider>> GetPlayerResourcePaths; + + internal readonly FuncProvider?[]> + GetGameObjectResourcesOfType; + + internal readonly + FuncProvider>> + GetPlayerResourcesOfType; public PenumbraIpcProviders(DalamudServices dalamud, IPenumbraApi api, ModManager modManager, CollectionManager collections, TempModManager tempMods, TempCollectionManager tempCollections, SaveService saveService, Configuration config) @@ -184,6 +190,7 @@ public class PenumbraIpcProviders : IDisposable ReverseResolveGameObjectPath = Ipc.ReverseResolveGameObjectPath.Provider(pi, Api.ReverseResolveGameObjectPath); ReverseResolvePlayerPath = Ipc.ReverseResolvePlayerPath.Provider(pi, Api.ReverseResolvePlayerPath); ResolvePlayerPaths = Ipc.ResolvePlayerPaths.Provider(pi, Api.ResolvePlayerPaths); + ResolvePlayerPathsAsync = Ipc.ResolvePlayerPathsAsync.Provider(pi, Api.ResolvePlayerPathsAsync); // Collections GetCollections = Ipc.GetCollections.Provider(pi, Api.GetCollections); @@ -301,6 +308,7 @@ public class PenumbraIpcProviders : IDisposable ReverseResolveGameObjectPath.Dispose(); ReverseResolvePlayerPath.Dispose(); ResolvePlayerPaths.Dispose(); + ResolvePlayerPathsAsync.Dispose(); // Collections GetCollections.Dispose(); diff --git a/Penumbra/Collections/Cache/CollectionCache.cs b/Penumbra/Collections/Cache/CollectionCache.cs index 80539d96..3761424a 100644 --- a/Penumbra/Collections/Cache/CollectionCache.cs +++ b/Penumbra/Collections/Cache/CollectionCache.cs @@ -23,7 +23,7 @@ public class CollectionCache : IDisposable private readonly ModCollection _collection; public readonly CollectionModData ModData = new(); public readonly SortedList, object?)> _changedItems = new(); - public readonly Dictionary ResolvedFiles = new(); + public readonly ConcurrentDictionary ResolvedFiles = new(); public readonly MetaCache Meta; public readonly Dictionary> _conflicts = new(); @@ -146,7 +146,7 @@ public class CollectionCache : IDisposable ModData.RemovePath(modPath.Mod, path); 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, Mod.ForcedFiles); } @@ -157,7 +157,7 @@ public class CollectionCache : IDisposable } 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); } } diff --git a/Penumbra/Collections/ModCollection.Cache.Access.cs b/Penumbra/Collections/ModCollection.Cache.Access.cs index 36e0fd98..a695c463 100644 --- a/Penumbra/Collections/ModCollection.Cache.Access.cs +++ b/Penumbra/Collections/ModCollection.Cache.Access.cs @@ -56,7 +56,7 @@ public partial class ModCollection } internal IReadOnlyDictionary ResolvedFiles - => _cache?.ResolvedFiles ?? new Dictionary(); + => _cache?.ResolvedFiles ?? new ConcurrentDictionary(); internal IReadOnlyDictionary, object?)> ChangedItems => _cache?.ChangedItems ?? new Dictionary, object?)>();