Merge branch 'refs/heads/WorkingRobot/master'

This commit is contained in:
Ottermandias 2023-12-01 14:31:00 +01:00
commit 222a0e5f77
5 changed files with 164 additions and 2 deletions

@ -1 +1 @@
Subproject commit 80f9793ef2ddaa50246b7112fde4d9b2098d8823 Subproject commit e2f578a903f4e2de6c5967eb92f1b5a0a413d287

View file

@ -16,8 +16,8 @@ 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;
using Penumbra.GameData.Enums;
namespace Penumbra.Api; namespace Penumbra.Api;
@ -1437,6 +1437,8 @@ public class IpcTester : IDisposable
private (string, IReadOnlyDictionary<string, string[]>?)[]? _lastPlayerResourcePaths; private (string, IReadOnlyDictionary<string, string[]>?)[]? _lastPlayerResourcePaths;
private (string, IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>?)[]? _lastGameObjectResourcesOfType; private (string, IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>?)[]? _lastGameObjectResourcesOfType;
private (string, IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>?)[]? _lastPlayerResourcesOfType; private (string, IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>?)[]? _lastPlayerResourcesOfType;
private (string, Ipc.ResourceTree?)[]? _lastGameObjectResourceTrees;
private (string, Ipc.ResourceTree)[]? _lastPlayerResourceTrees;
private TimeSpan _lastCallDuration; private TimeSpan _lastCallDuration;
public ResourceTree(DalamudPluginInterface pi, IObjectTable objects) public ResourceTree(DalamudPluginInterface pi, IObjectTable objects)
@ -1523,11 +1525,46 @@ public class IpcTester : IDisposable
ImGui.OpenPopup(nameof(Ipc.GetPlayerResourcesOfType)); ImGui.OpenPopup(nameof(Ipc.GetPlayerResourcesOfType));
} }
DrawIntro(Ipc.GetGameObjectResourceTrees.Label, "Get GameObject resource trees");
if (ImGui.Button("Get##GameObjectResourceTrees"))
{
var gameObjects = GetSelectedGameObjects();
var subscriber = Ipc.GetGameObjectResourceTrees.Subscriber(_pi);
_stopwatch.Restart();
var trees = subscriber.Invoke(_withUIData, gameObjects);
_lastCallDuration = _stopwatch.Elapsed;
_lastGameObjectResourceTrees = gameObjects
.Select(i => GameObjectToString(i))
.Zip(trees)
.ToArray();
ImGui.OpenPopup(nameof(Ipc.GetGameObjectResourceTrees));
}
DrawIntro(Ipc.GetPlayerResourceTrees.Label, "Get local player resource trees");
if (ImGui.Button("Get##PlayerResourceTrees"))
{
var subscriber = Ipc.GetPlayerResourceTrees.Subscriber(_pi);
_stopwatch.Restart();
var trees = subscriber.Invoke(_withUIData);
_lastCallDuration = _stopwatch.Elapsed;
_lastPlayerResourceTrees = trees
.Select(pair => (GameObjectToString(pair.Key), pair.Value))
.ToArray();
ImGui.OpenPopup(nameof(Ipc.GetPlayerResourceTrees));
}
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);
DrawPopup(nameof(Ipc.GetGameObjectResourceTrees), ref _lastGameObjectResourceTrees, DrawResourceTrees, _lastCallDuration);
DrawPopup(nameof(Ipc.GetPlayerResourceTrees), ref _lastPlayerResourceTrees, DrawResourceTrees!, _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
@ -1638,6 +1675,70 @@ public class IpcTester : IDisposable
}); });
} }
private void DrawResourceTrees((string, Ipc.ResourceTree?)[] result)
{
DrawWithHeaders(result, tree =>
{
ImGui.TextUnformatted($"Name: {tree.Name}\nRaceCode: {(GenderRace)tree.RaceCode}");
using var table = ImRaii.Table(string.Empty, _withUIData ? 7 : 5, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.Resizable);
if (!table)
return;
if (_withUIData)
{
ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthStretch, 0.5f);
ImGui.TableSetupColumn("Type", ImGuiTableColumnFlags.WidthStretch, 0.1f);
ImGui.TableSetupColumn("Icon", ImGuiTableColumnFlags.WidthStretch, 0.15f);
}
else
{
ImGui.TableSetupColumn("Type", ImGuiTableColumnFlags.WidthStretch, 0.5f);
}
ImGui.TableSetupColumn("Game Path", ImGuiTableColumnFlags.WidthStretch, 0.5f);
ImGui.TableSetupColumn("Actual Path", ImGuiTableColumnFlags.WidthStretch, 0.5f);
ImGui.TableSetupColumn("Object Address", ImGuiTableColumnFlags.WidthStretch, 0.2f);
ImGui.TableSetupColumn("Resource Handle", ImGuiTableColumnFlags.WidthStretch, 0.2f);
ImGui.TableHeadersRow();
void DrawNode(Ipc.ResourceNode node)
{
ImGui.TableNextRow();
ImGui.TableNextColumn();
var hasChildren = node.Children.Any();
using var treeNode = ImRaii.TreeNode(
$"{(_withUIData ? (node.Name ?? "Unknown") : node.Type)}##{node.ObjectAddress:X8}",
hasChildren ?
ImGuiTreeNodeFlags.SpanFullWidth :
(ImGuiTreeNodeFlags.SpanFullWidth | ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.NoTreePushOnOpen));
if (_withUIData)
{
ImGui.TableNextColumn();
TextUnformattedMono(node.Type.ToString());
ImGui.TableNextColumn();
TextUnformattedMono(node.Icon.ToString());
}
ImGui.TableNextColumn();
ImGui.TextUnformatted(node.GamePath ?? "Unknown");
ImGui.TableNextColumn();
ImGui.TextUnformatted(node.ActualPath);
ImGui.TableNextColumn();
TextUnformattedMono($"0x{node.ObjectAddress:X8}");
ImGui.TableNextColumn();
TextUnformattedMono($"0x{node.ResourceHandle:X8}");
if (treeNode)
{
foreach (var child in node.Children)
DrawNode(child);
}
}
foreach (var node in tree.Nodes)
DrawNode(node);
});
}
private static void TextUnformattedMono(string text) private static void TextUnformattedMono(string text)
{ {
using var _ = ImRaii.PushFont(UiBuilder.MonoFont); using var _ = ImRaii.PushFont(UiBuilder.MonoFont);

View file

@ -1075,6 +1075,23 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return resDictionaries.AsReadOnly(); return resDictionaries.AsReadOnly();
} }
public Ipc.ResourceTree?[] GetGameObjectResourceTrees(bool withUIData, params ushort[] gameObjects)
{
var characters = gameObjects.Select(index => _dalamud.Objects[index]).OfType<Character>();
var resourceTrees = _resourceTreeFactory.FromCharacters(characters, withUIData ? ResourceTreeFactory.Flags.WithUiData : 0);
var resDictionary = ResourceTreeApiHelper.EncapsulateResourceTrees(resourceTrees);
return Array.ConvertAll(gameObjects, obj => resDictionary.TryGetValue(obj, out var nodes) ? nodes : null);
}
public IReadOnlyDictionary<ushort, Ipc.ResourceTree> GetPlayerResourceTrees(bool withUIData)
{
var resourceTrees = _resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly
| (withUIData ? ResourceTreeFactory.Flags.WithUiData : 0));
var resDictionary = ResourceTreeApiHelper.EncapsulateResourceTrees(resourceTrees);
return resDictionary.AsReadOnly();
}
// TODO: cleanup when incrementing API // TODO: cleanup when incrementing API
public string GetMetaManipulations(string characterName) public string GetMetaManipulations(string characterName)

View file

@ -130,6 +130,9 @@ public class PenumbraIpcProviders : IDisposable
FuncProvider<ResourceType, bool, IReadOnlyDictionary<ushort, IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>>> FuncProvider<ResourceType, bool, IReadOnlyDictionary<ushort, IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>>>
GetPlayerResourcesOfType; GetPlayerResourcesOfType;
internal readonly FuncProvider<bool, ushort[], Ipc.ResourceTree?[]> GetGameObjectResourceTrees;
internal readonly FuncProvider<bool, IReadOnlyDictionary<ushort, Ipc.ResourceTree>> GetPlayerResourceTrees;
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)
{ {
@ -254,6 +257,8 @@ public class PenumbraIpcProviders : IDisposable
GetPlayerResourcePaths = Ipc.GetPlayerResourcePaths.Provider(pi, Api.GetPlayerResourcePaths); GetPlayerResourcePaths = Ipc.GetPlayerResourcePaths.Provider(pi, Api.GetPlayerResourcePaths);
GetGameObjectResourcesOfType = Ipc.GetGameObjectResourcesOfType.Provider(pi, Api.GetGameObjectResourcesOfType); GetGameObjectResourcesOfType = Ipc.GetGameObjectResourcesOfType.Provider(pi, Api.GetGameObjectResourcesOfType);
GetPlayerResourcesOfType = Ipc.GetPlayerResourcesOfType.Provider(pi, Api.GetPlayerResourcesOfType); GetPlayerResourcesOfType = Ipc.GetPlayerResourcesOfType.Provider(pi, Api.GetPlayerResourcesOfType);
GetGameObjectResourceTrees = Ipc.GetGameObjectResourceTrees.Provider(pi, Api.GetGameObjectResourceTrees);
GetPlayerResourceTrees = Ipc.GetPlayerResourceTrees.Provider(pi, Api.GetPlayerResourceTrees);
Tester = new IpcTester(config, dalamud, this, modManager, collections, tempMods, tempCollections, saveService); Tester = new IpcTester(config, dalamud, this, modManager, collections, tempMods, tempCollections, saveService);
@ -370,6 +375,8 @@ public class PenumbraIpcProviders : IDisposable
GetPlayerResourcePaths.Dispose(); GetPlayerResourcePaths.Dispose();
GetGameObjectResourcesOfType.Dispose(); GetGameObjectResourcesOfType.Dispose();
GetPlayerResourcesOfType.Dispose(); GetPlayerResourcesOfType.Dispose();
GetGameObjectResourceTrees.Dispose();
GetPlayerResourceTrees.Dispose();
Disposed.Invoke(); Disposed.Invoke();
Disposed.Dispose(); Disposed.Dispose();

View file

@ -1,5 +1,7 @@
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using Penumbra.Api;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.String.Classes;
using Penumbra.UI; using Penumbra.UI;
namespace Penumbra.Interop.ResourceTree; namespace Penumbra.Interop.ResourceTree;
@ -71,4 +73,39 @@ internal static class ResourceTreeApiHelper
return resDictionaries.ToDictionary(pair => pair.Key, return resDictionaries.ToDictionary(pair => pair.Key,
pair => (IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>)pair.Value.AsReadOnly()); pair => (IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>)pair.Value.AsReadOnly());
} }
public static Dictionary<ushort, Ipc.ResourceTree> EncapsulateResourceTrees(IEnumerable<(Character, ResourceTree)> resourceTrees)
{
static Ipc.ResourceNode GetIpcNode(ResourceNode node) =>
new()
{
Type = node.Type,
Icon = ChangedItemDrawer.ToApiIcon(node.Icon),
Name = node.Name,
GamePath = node.GamePath.Equals(Utf8GamePath.Empty) ? null : node.GamePath.ToString(),
ActualPath = node.FullPath.ToString(),
ObjectAddress = node.ObjectAddress,
ResourceHandle = node.ResourceHandle,
Children = node.Children.Select(GetIpcNode).ToList(),
};
static Ipc.ResourceTree GetIpcTree(ResourceTree tree) =>
new()
{
Name = tree.Name,
RaceCode = (ushort)tree.RaceCode,
Nodes = tree.Nodes.Select(GetIpcNode).ToList(),
};
var resDictionary = new Dictionary<ushort, Ipc.ResourceTree>(4);
foreach (var (gameObject, resourceTree) in resourceTrees)
{
if (resDictionary.ContainsKey(gameObject.ObjectIndex))
continue;
resDictionary.Add(gameObject.ObjectIndex, GetIpcTree(resourceTree));
}
return resDictionary;
}
} }