mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-02-21 23:37:47 +01:00
Rework API, use Collection ID in crash handler, use collection GUIDs in more places.
This commit is contained in:
parent
793ed4f0a7
commit
ba8999914f
88 changed files with 4193 additions and 3930 deletions
76
Penumbra/Api/Api/ApiHelpers.cs
Normal file
76
Penumbra/Api/Api/ApiHelpers.cs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
using OtterGui.Log;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Collections.Manager;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Penumbra.Interop.PathResolving;
|
||||
|
||||
namespace Penumbra.Api.Api;
|
||||
|
||||
public class ApiHelpers(
|
||||
CollectionManager collectionManager,
|
||||
ObjectManager objects,
|
||||
CollectionResolver collectionResolver,
|
||||
ActorManager actors) : IApiService
|
||||
{
|
||||
/// <summary> Return the associated identifier for an object given by its index. </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal ActorIdentifier AssociatedIdentifier(int gameObjectIdx)
|
||||
{
|
||||
if (gameObjectIdx < 0 || gameObjectIdx >= objects.TotalCount)
|
||||
return ActorIdentifier.Invalid;
|
||||
|
||||
var ptr = objects[gameObjectIdx];
|
||||
return actors.FromObject(ptr, out _, false, true, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal unsafe bool AssociatedCollection(int gameObjectIdx, out ModCollection collection)
|
||||
{
|
||||
collection = collectionManager.Active.Default;
|
||||
if (gameObjectIdx < 0 || gameObjectIdx >= objects.TotalCount)
|
||||
return false;
|
||||
|
||||
var ptr = objects[gameObjectIdx];
|
||||
var data = collectionResolver.IdentifyCollection(ptr.AsObject, false);
|
||||
if (data.Valid)
|
||||
collection = data.ModCollection;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal static PenumbraApiEc Return(PenumbraApiEc ec, LazyString args, [CallerMemberName] string name = "Unknown")
|
||||
{
|
||||
Penumbra.Log.Debug(
|
||||
$"[{name}] Called with {args}, returned {ec}.");
|
||||
return ec;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal static LazyString Args(params object[] arguments)
|
||||
{
|
||||
if (arguments.Length == 0)
|
||||
return new LazyString(() => "no arguments");
|
||||
|
||||
return new LazyString(() =>
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var i = 0; i < arguments.Length / 2; ++i)
|
||||
{
|
||||
sb.Append(arguments[2 * i]);
|
||||
sb.Append(" = ");
|
||||
sb.Append(arguments[2 * i + 1]);
|
||||
sb.Append(", ");
|
||||
}
|
||||
|
||||
return sb.ToString(0, sb.Length - 2);
|
||||
});
|
||||
}
|
||||
}
|
||||
141
Penumbra/Api/Api/CollectionApi.cs
Normal file
141
Penumbra/Api/Api/CollectionApi.cs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Collections.Manager;
|
||||
|
||||
namespace Penumbra.Api.Api;
|
||||
|
||||
public class CollectionApi(CollectionManager collections, ApiHelpers helpers) : IPenumbraApiCollection, IApiService
|
||||
{
|
||||
public Dictionary<Guid, string> GetCollections()
|
||||
=> collections.Storage.ToDictionary(c => c.Id, c => c.Name);
|
||||
|
||||
public Dictionary<string, object?> GetChangedItemsForCollection(Guid collectionId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!collections.Storage.ById(collectionId, out var collection))
|
||||
collection = ModCollection.Empty;
|
||||
|
||||
if (collection.HasCache)
|
||||
return collection.ChangedItems.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Item2);
|
||||
|
||||
Penumbra.Log.Warning($"Collection {collectionId} does not exist or is not loaded.");
|
||||
return [];
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error($"Could not obtain Changed Items for {collectionId}:\n{e}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public (Guid Id, string Name)? GetCollection(ApiCollectionType type)
|
||||
{
|
||||
if (!Enum.IsDefined(type))
|
||||
return null;
|
||||
|
||||
var collection = collections.Active.ByType((CollectionType)type);
|
||||
return collection == null ? null : (collection.Id, collection.Name);
|
||||
}
|
||||
|
||||
internal (Guid Id, string Name)? GetCollection(byte type)
|
||||
=> GetCollection((ApiCollectionType)type);
|
||||
|
||||
public (bool ObjectValid, bool IndividualSet, (Guid Id, string Name) EffectiveCollection) GetCollectionForObject(int gameObjectIdx)
|
||||
{
|
||||
var id = helpers.AssociatedIdentifier(gameObjectIdx);
|
||||
if (!id.IsValid)
|
||||
return (false, false, (collections.Active.Default.Id, collections.Active.Default.Name));
|
||||
|
||||
if (collections.Active.Individuals.TryGetValue(id, out var collection))
|
||||
return (true, true, (collection.Id, collection.Name));
|
||||
|
||||
helpers.AssociatedCollection(gameObjectIdx, out collection);
|
||||
return (true, false, (collection.Id, collection.Name));
|
||||
}
|
||||
|
||||
public Guid[] GetCollectionByName(string name)
|
||||
=> collections.Storage.Where(c => string.Equals(name, c.Name, StringComparison.OrdinalIgnoreCase)).Select(c => c.Id).ToArray();
|
||||
|
||||
public (PenumbraApiEc, (Guid Id, string Name)? OldCollection) SetCollection(ApiCollectionType type, Guid? collectionId,
|
||||
bool allowCreateNew, bool allowDelete)
|
||||
{
|
||||
if (!Enum.IsDefined(type))
|
||||
return (PenumbraApiEc.InvalidArgument, null);
|
||||
|
||||
var oldCollection = collections.Active.ByType((CollectionType)type);
|
||||
var old = oldCollection != null ? (oldCollection.Id, oldCollection.Name) : new ValueTuple<Guid, string>?();
|
||||
if (collectionId == null)
|
||||
{
|
||||
if (old == null)
|
||||
return (PenumbraApiEc.NothingChanged, old);
|
||||
|
||||
if (!allowDelete || type is ApiCollectionType.Current or ApiCollectionType.Default or ApiCollectionType.Interface)
|
||||
return (PenumbraApiEc.AssignmentDeletionDisallowed, old);
|
||||
|
||||
collections.Active.RemoveSpecialCollection((CollectionType)type);
|
||||
return (PenumbraApiEc.Success, old);
|
||||
}
|
||||
|
||||
if (!collections.Storage.ById(collectionId.Value, out var collection))
|
||||
return (PenumbraApiEc.CollectionMissing, old);
|
||||
|
||||
if (old == null)
|
||||
{
|
||||
if (!allowCreateNew)
|
||||
return (PenumbraApiEc.AssignmentCreationDisallowed, old);
|
||||
|
||||
collections.Active.CreateSpecialCollection((CollectionType)type);
|
||||
}
|
||||
else if (old.Value.Item1 == collection.Id)
|
||||
{
|
||||
return (PenumbraApiEc.NothingChanged, old);
|
||||
}
|
||||
|
||||
collections.Active.SetCollection(collection, (CollectionType)type);
|
||||
return (PenumbraApiEc.Success, old);
|
||||
}
|
||||
|
||||
public (PenumbraApiEc, (Guid Id, string Name)? OldCollection) SetCollectionForObject(int gameObjectIdx, Guid? collectionId,
|
||||
bool allowCreateNew, bool allowDelete)
|
||||
{
|
||||
var id = helpers.AssociatedIdentifier(gameObjectIdx);
|
||||
if (!id.IsValid)
|
||||
return (PenumbraApiEc.InvalidIdentifier, (collections.Active.Default.Id, collections.Active.Default.Name));
|
||||
|
||||
var oldCollection = collections.Active.Individuals.TryGetValue(id, out var c) ? c : null;
|
||||
var old = oldCollection != null ? (oldCollection.Id, oldCollection.Name) : new ValueTuple<Guid, string>?();
|
||||
if (collectionId == null)
|
||||
{
|
||||
if (old == null)
|
||||
return (PenumbraApiEc.NothingChanged, old);
|
||||
|
||||
if (!allowDelete)
|
||||
return (PenumbraApiEc.AssignmentDeletionDisallowed, old);
|
||||
|
||||
var idx = collections.Active.Individuals.Index(id);
|
||||
collections.Active.RemoveIndividualCollection(idx);
|
||||
return (PenumbraApiEc.Success, old);
|
||||
}
|
||||
|
||||
if (!collections.Storage.ById(collectionId.Value, out var collection))
|
||||
return (PenumbraApiEc.CollectionMissing, old);
|
||||
|
||||
if (old == null)
|
||||
{
|
||||
if (!allowCreateNew)
|
||||
return (PenumbraApiEc.AssignmentCreationDisallowed, old);
|
||||
|
||||
var ids = collections.Active.Individuals.GetGroup(id);
|
||||
collections.Active.CreateIndividualCollection(ids);
|
||||
}
|
||||
else if (old.Value.Item1 == collection.Id)
|
||||
{
|
||||
return (PenumbraApiEc.NothingChanged, old);
|
||||
}
|
||||
|
||||
collections.Active.SetCollection(collection, CollectionType.Individual, collections.Active.Individuals.Index(id));
|
||||
return (PenumbraApiEc.Success, old);
|
||||
}
|
||||
}
|
||||
40
Penumbra/Api/Api/EditingApi.cs
Normal file
40
Penumbra/Api/Api/EditingApi.cs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
using OtterGui.Services;
|
||||
using Penumbra.Import.Textures;
|
||||
using TextureType = Penumbra.Api.Enums.TextureType;
|
||||
|
||||
namespace Penumbra.Api.Api;
|
||||
|
||||
public class EditingApi(TextureManager textureManager) : IPenumbraApiEditing, IApiService
|
||||
{
|
||||
public Task ConvertTextureFile(string inputFile, string outputFile, TextureType textureType, bool mipMaps)
|
||||
=> textureType switch
|
||||
{
|
||||
TextureType.Png => textureManager.SavePng(inputFile, outputFile),
|
||||
TextureType.AsIsTex => textureManager.SaveAs(CombinedTexture.TextureSaveType.AsIs, mipMaps, true, inputFile, outputFile),
|
||||
TextureType.AsIsDds => textureManager.SaveAs(CombinedTexture.TextureSaveType.AsIs, mipMaps, false, inputFile, outputFile),
|
||||
TextureType.RgbaTex => textureManager.SaveAs(CombinedTexture.TextureSaveType.Bitmap, mipMaps, true, inputFile, outputFile),
|
||||
TextureType.RgbaDds => textureManager.SaveAs(CombinedTexture.TextureSaveType.Bitmap, mipMaps, false, inputFile, outputFile),
|
||||
TextureType.Bc3Tex => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC3, mipMaps, true, inputFile, outputFile),
|
||||
TextureType.Bc3Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC3, mipMaps, false, inputFile, outputFile),
|
||||
TextureType.Bc7Tex => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC7, mipMaps, true, inputFile, outputFile),
|
||||
TextureType.Bc7Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC7, mipMaps, false, inputFile, outputFile),
|
||||
_ => Task.FromException(new Exception($"Invalid input value {textureType}.")),
|
||||
};
|
||||
|
||||
// @formatter:off
|
||||
public Task ConvertTextureData(byte[] rgbaData, int width, string outputFile, TextureType textureType, bool mipMaps)
|
||||
=> textureType switch
|
||||
{
|
||||
TextureType.Png => textureManager.SavePng(new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
|
||||
TextureType.AsIsTex => textureManager.SaveAs(CombinedTexture.TextureSaveType.AsIs, mipMaps, true, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
|
||||
TextureType.AsIsDds => textureManager.SaveAs(CombinedTexture.TextureSaveType.AsIs, mipMaps, false, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
|
||||
TextureType.RgbaTex => textureManager.SaveAs(CombinedTexture.TextureSaveType.Bitmap, mipMaps, true, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
|
||||
TextureType.RgbaDds => textureManager.SaveAs(CombinedTexture.TextureSaveType.Bitmap, mipMaps, false, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
|
||||
TextureType.Bc3Tex => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC3, mipMaps, true, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
|
||||
TextureType.Bc3Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC3, mipMaps, false, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
|
||||
TextureType.Bc7Tex => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC7, mipMaps, true, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
|
||||
TextureType.Bc7Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC7, mipMaps, false, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
|
||||
_ => Task.FromException(new Exception($"Invalid input value {textureType}.")),
|
||||
};
|
||||
// @formatter:on
|
||||
}
|
||||
82
Penumbra/Api/Api/GameStateApi.cs
Normal file
82
Penumbra/Api/Api/GameStateApi.cs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Interop.PathResolving;
|
||||
using Penumbra.Interop.ResourceLoading;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Api.Api;
|
||||
|
||||
public class GameStateApi : IPenumbraApiGameState, IApiService, IDisposable
|
||||
{
|
||||
private readonly CommunicatorService _communicator;
|
||||
private readonly CollectionResolver _collectionResolver;
|
||||
private readonly CutsceneService _cutsceneService;
|
||||
private readonly ResourceLoader _resourceLoader;
|
||||
|
||||
public unsafe GameStateApi(CommunicatorService communicator, CollectionResolver collectionResolver, CutsceneService cutsceneService,
|
||||
ResourceLoader resourceLoader)
|
||||
{
|
||||
_communicator = communicator;
|
||||
_collectionResolver = collectionResolver;
|
||||
_cutsceneService = cutsceneService;
|
||||
_resourceLoader = resourceLoader;
|
||||
_resourceLoader.ResourceLoaded += OnResourceLoaded;
|
||||
_communicator.CreatedCharacterBase.Subscribe(OnCreatedCharacterBase, Communication.CreatedCharacterBase.Priority.Api);
|
||||
}
|
||||
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
_resourceLoader.ResourceLoaded -= OnResourceLoaded;
|
||||
_communicator.CreatedCharacterBase.Unsubscribe(OnCreatedCharacterBase);
|
||||
}
|
||||
|
||||
public event CreatedCharacterBaseDelegate? CreatedCharacterBase;
|
||||
public event GameObjectResourceResolvedDelegate? GameObjectResourceResolved;
|
||||
|
||||
public event CreatingCharacterBaseDelegate? CreatingCharacterBase
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value == null)
|
||||
return;
|
||||
|
||||
_communicator.CreatingCharacterBase.Subscribe(new Action<nint, Guid, nint, nint, nint>(value),
|
||||
Communication.CreatingCharacterBase.Priority.Api);
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value == null)
|
||||
return;
|
||||
|
||||
_communicator.CreatingCharacterBase.Unsubscribe(new Action<nint, Guid, nint, nint, nint>(value));
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe (nint GameObject, (Guid Id, string Name) Collection) GetDrawObjectInfo(nint drawObject)
|
||||
{
|
||||
var data = _collectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
||||
return (data.AssociatedGameObject, (data.ModCollection.Id, data.ModCollection.Name));
|
||||
}
|
||||
|
||||
public int GetCutsceneParentIndex(int actorIdx)
|
||||
=> _cutsceneService.GetParentIndex(actorIdx);
|
||||
|
||||
public PenumbraApiEc SetCutsceneParentIndex(int copyIdx, int newParentIdx)
|
||||
=> _cutsceneService.SetParentIndex(copyIdx, newParentIdx)
|
||||
? PenumbraApiEc.Success
|
||||
: PenumbraApiEc.InvalidArgument;
|
||||
|
||||
private unsafe void OnResourceLoaded(ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath, ResolveData resolveData)
|
||||
{
|
||||
if (resolveData.AssociatedGameObject != nint.Zero)
|
||||
GameObjectResourceResolved?.Invoke(resolveData.AssociatedGameObject, originalPath.ToString(),
|
||||
manipulatedPath?.ToString() ?? originalPath.ToString());
|
||||
}
|
||||
|
||||
private void OnCreatedCharacterBase(nint gameObject, ModCollection collection, nint drawObject)
|
||||
=> CreatedCharacterBase?.Invoke(gameObject, collection.Id, drawObject);
|
||||
}
|
||||
23
Penumbra/Api/Api/MetaApi.cs
Normal file
23
Penumbra/Api/Api/MetaApi.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
using OtterGui;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Interop.PathResolving;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
|
||||
namespace Penumbra.Api.Api;
|
||||
|
||||
public class MetaApi(CollectionResolver collectionResolver, ApiHelpers helpers) : IPenumbraApiMeta, IApiService
|
||||
{
|
||||
public string GetPlayerMetaManipulations()
|
||||
{
|
||||
var collection = collectionResolver.PlayerCollection();
|
||||
var set = collection.MetaCache?.Manipulations.ToArray() ?? [];
|
||||
return Functions.ToCompressedBase64(set, MetaManipulation.CurrentVersion);
|
||||
}
|
||||
|
||||
public string GetMetaManipulations(int gameObjectIdx)
|
||||
{
|
||||
helpers.AssociatedCollection(gameObjectIdx, out var collection);
|
||||
var set = collection.MetaCache?.Manipulations.ToArray() ?? [];
|
||||
return Functions.ToCompressedBase64(set, MetaManipulation.CurrentVersion);
|
||||
}
|
||||
}
|
||||
282
Penumbra/Api/Api/ModSettingsApi.cs
Normal file
282
Penumbra/Api/Api/ModSettingsApi.cs
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
using OtterGui;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Collections.Manager;
|
||||
using Penumbra.Communication;
|
||||
using Penumbra.Interop.PathResolving;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Mods.Editor;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
using Penumbra.Services;
|
||||
|
||||
namespace Penumbra.Api.Api;
|
||||
|
||||
public class ModSettingsApi : IPenumbraApiModSettings, IApiService, IDisposable
|
||||
{
|
||||
private readonly CollectionResolver _collectionResolver;
|
||||
private readonly ModManager _modManager;
|
||||
private readonly CollectionManager _collectionManager;
|
||||
private readonly CollectionEditor _collectionEditor;
|
||||
private readonly CommunicatorService _communicator;
|
||||
|
||||
public ModSettingsApi(CollectionResolver collectionResolver,
|
||||
ModManager modManager,
|
||||
CollectionManager collectionManager,
|
||||
CollectionEditor collectionEditor,
|
||||
CommunicatorService communicator)
|
||||
{
|
||||
_collectionResolver = collectionResolver;
|
||||
_modManager = modManager;
|
||||
_collectionManager = collectionManager;
|
||||
_collectionEditor = collectionEditor;
|
||||
_communicator = communicator;
|
||||
_communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.ApiModSettings);
|
||||
_communicator.ModSettingChanged.Subscribe(OnModSettingChange, Communication.ModSettingChanged.Priority.Api);
|
||||
_communicator.ModOptionChanged.Subscribe(OnModOptionEdited, ModOptionChanged.Priority.Api);
|
||||
_communicator.ModFileChanged.Subscribe(OnModFileChanged, ModFileChanged.Priority.Api);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_communicator.ModPathChanged.Unsubscribe(OnModPathChange);
|
||||
_communicator.ModSettingChanged.Unsubscribe(OnModSettingChange);
|
||||
_communicator.ModOptionChanged.Unsubscribe(OnModOptionEdited);
|
||||
_communicator.ModFileChanged.Unsubscribe(OnModFileChanged);
|
||||
}
|
||||
|
||||
public event ModSettingChangedDelegate? ModSettingChanged;
|
||||
|
||||
public AvailableModSettings? GetAvailableModSettings(string modDirectory, string modName)
|
||||
{
|
||||
if (!_modManager.TryGetMod(modDirectory, modName, out var mod))
|
||||
return null;
|
||||
|
||||
var dict = new Dictionary<string, (string[], int)>(mod.Groups.Count);
|
||||
foreach (var g in mod.Groups)
|
||||
dict.Add(g.Name, (g.Select(o => o.Name).ToArray(), (int)g.Type));
|
||||
return new AvailableModSettings(dict);
|
||||
}
|
||||
|
||||
public Dictionary<string, (string[], int)>? GetAvailableModSettingsBase(string modDirectory, string modName)
|
||||
=> _modManager.TryGetMod(modDirectory, modName, out var mod)
|
||||
? mod.Groups.ToDictionary(g => g.Name, g => (g.Select(o => o.Name).ToArray(), (int)g.Type))
|
||||
: null;
|
||||
|
||||
public (PenumbraApiEc, (bool, int, Dictionary<string, List<string>>, bool)?) GetCurrentModSettings(Guid collectionId, string modDirectory,
|
||||
string modName, bool ignoreInheritance)
|
||||
{
|
||||
if (!_modManager.TryGetMod(modDirectory, modName, out var mod))
|
||||
return (PenumbraApiEc.ModMissing, null);
|
||||
|
||||
if (!_collectionManager.Storage.ById(collectionId, out var collection))
|
||||
return (PenumbraApiEc.CollectionMissing, null);
|
||||
|
||||
var settings = collection.Id == Guid.Empty
|
||||
? null
|
||||
: ignoreInheritance
|
||||
? collection.Settings[mod.Index]
|
||||
: collection[mod.Index].Settings;
|
||||
if (settings == null)
|
||||
return (PenumbraApiEc.Success, null);
|
||||
|
||||
var (enabled, priority, dict) = settings.ConvertToShareable(mod);
|
||||
return (PenumbraApiEc.Success,
|
||||
(enabled, priority.Value, dict, collection.Settings[mod.Index] == null));
|
||||
}
|
||||
|
||||
public PenumbraApiEc TryInheritMod(Guid collectionId, string modDirectory, string modName, bool inherit)
|
||||
{
|
||||
var args = ApiHelpers.Args("CollectionId", collectionId, "ModDirectory", modDirectory, "ModName", modName, "Inherit",
|
||||
inherit.ToString());
|
||||
|
||||
if (collectionId == Guid.Empty)
|
||||
return ApiHelpers.Return(PenumbraApiEc.InvalidArgument, args);
|
||||
|
||||
if (!_collectionManager.Storage.ById(collectionId, out var collection))
|
||||
return ApiHelpers.Return(PenumbraApiEc.CollectionMissing, args);
|
||||
|
||||
if (!_modManager.TryGetMod(modDirectory, modName, out var mod))
|
||||
return ApiHelpers.Return(PenumbraApiEc.ModMissing, args);
|
||||
|
||||
var ret = _collectionEditor.SetModInheritance(collection, mod, inherit)
|
||||
? PenumbraApiEc.Success
|
||||
: PenumbraApiEc.NothingChanged;
|
||||
return ApiHelpers.Return(ret, args);
|
||||
}
|
||||
|
||||
public PenumbraApiEc TrySetMod(Guid collectionId, string modDirectory, string modName, bool enabled)
|
||||
{
|
||||
var args = ApiHelpers.Args("CollectionId", collectionId, "ModDirectory", modDirectory, "ModName", modName, "Enabled", enabled);
|
||||
if (!_collectionManager.Storage.ById(collectionId, out var collection))
|
||||
return ApiHelpers.Return(PenumbraApiEc.CollectionMissing, args);
|
||||
|
||||
if (!_modManager.TryGetMod(modDirectory, modName, out var mod))
|
||||
return ApiHelpers.Return(PenumbraApiEc.ModMissing, args);
|
||||
|
||||
var ret = _collectionEditor.SetModState(collection, mod, enabled)
|
||||
? PenumbraApiEc.Success
|
||||
: PenumbraApiEc.NothingChanged;
|
||||
return ApiHelpers.Return(ret, args);
|
||||
}
|
||||
|
||||
public PenumbraApiEc TrySetModPriority(Guid collectionId, string modDirectory, string modName, int priority)
|
||||
{
|
||||
var args = ApiHelpers.Args("CollectionId", collectionId, "ModDirectory", modDirectory, "ModName", modName, "Priority", priority);
|
||||
|
||||
if (!_collectionManager.Storage.ById(collectionId, out var collection))
|
||||
return ApiHelpers.Return(PenumbraApiEc.CollectionMissing, args);
|
||||
|
||||
if (!_modManager.TryGetMod(modDirectory, modName, out var mod))
|
||||
return ApiHelpers.Return(PenumbraApiEc.ModMissing, args);
|
||||
|
||||
var ret = _collectionEditor.SetModPriority(collection, mod, new ModPriority(priority))
|
||||
? PenumbraApiEc.Success
|
||||
: PenumbraApiEc.NothingChanged;
|
||||
return ApiHelpers.Return(ret, args);
|
||||
}
|
||||
|
||||
public PenumbraApiEc TrySetModSetting(Guid collectionId, string modDirectory, string modName, string optionGroupName, string optionName)
|
||||
{
|
||||
var args = ApiHelpers.Args("CollectionId", collectionId, "ModDirectory", modDirectory, "ModName", modName, "OptionGroupName",
|
||||
optionGroupName, "OptionName", optionName);
|
||||
|
||||
if (!_collectionManager.Storage.ById(collectionId, out var collection))
|
||||
return ApiHelpers.Return(PenumbraApiEc.CollectionMissing, args);
|
||||
|
||||
if (!_modManager.TryGetMod(modDirectory, modName, out var mod))
|
||||
return ApiHelpers.Return(PenumbraApiEc.ModMissing, args);
|
||||
|
||||
var groupIdx = mod.Groups.IndexOf(g => g.Name == optionGroupName);
|
||||
if (groupIdx < 0)
|
||||
return ApiHelpers.Return(PenumbraApiEc.OptionGroupMissing, args);
|
||||
|
||||
var optionIdx = mod.Groups[groupIdx].IndexOf(o => o.Name == optionName);
|
||||
if (optionIdx < 0)
|
||||
return ApiHelpers.Return(PenumbraApiEc.OptionMissing, args);
|
||||
|
||||
var setting = mod.Groups[groupIdx] switch
|
||||
{
|
||||
MultiModGroup => Setting.Multi(optionIdx),
|
||||
SingleModGroup => Setting.Single(optionIdx),
|
||||
_ => Setting.Zero,
|
||||
};
|
||||
var ret = _collectionEditor.SetModSetting(collection, mod, groupIdx, setting)
|
||||
? PenumbraApiEc.Success
|
||||
: PenumbraApiEc.NothingChanged;
|
||||
return ApiHelpers.Return(ret, args);
|
||||
}
|
||||
|
||||
public PenumbraApiEc TrySetModSettings(Guid collectionId, string modDirectory, string modName, string optionGroupName,
|
||||
IReadOnlyList<string> optionNames)
|
||||
{
|
||||
var args = ApiHelpers.Args("CollectionId", collectionId, "ModDirectory", modDirectory, "ModName", modName, "OptionGroupName",
|
||||
optionGroupName, "#optionNames", optionNames.Count);
|
||||
|
||||
if (!_collectionManager.Storage.ById(collectionId, out var collection))
|
||||
return ApiHelpers.Return(PenumbraApiEc.CollectionMissing, args);
|
||||
|
||||
if (!_modManager.TryGetMod(modDirectory, modName, out var mod))
|
||||
return ApiHelpers.Return(PenumbraApiEc.ModMissing, args);
|
||||
|
||||
var groupIdx = mod.Groups.IndexOf(g => g.Name == optionGroupName);
|
||||
if (groupIdx < 0)
|
||||
return ApiHelpers.Return(PenumbraApiEc.OptionGroupMissing, args);
|
||||
|
||||
var setting = Setting.Zero;
|
||||
switch (mod.Groups[groupIdx])
|
||||
{
|
||||
case SingleModGroup single:
|
||||
{
|
||||
var optionIdx = optionNames.Count == 0 ? -1 : single.IndexOf(o => o.Name == optionNames[^1]);
|
||||
if (optionIdx < 0)
|
||||
return ApiHelpers.Return(PenumbraApiEc.OptionMissing, args);
|
||||
|
||||
setting = Setting.Single(optionIdx);
|
||||
break;
|
||||
}
|
||||
case MultiModGroup multi:
|
||||
{
|
||||
foreach (var name in optionNames)
|
||||
{
|
||||
var optionIdx = multi.IndexOf(o => o.Name == name);
|
||||
if (optionIdx < 0)
|
||||
return ApiHelpers.Return(PenumbraApiEc.OptionMissing, args);
|
||||
|
||||
setting |= Setting.Multi(optionIdx);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var ret = _collectionEditor.SetModSetting(collection, mod, groupIdx, setting)
|
||||
? PenumbraApiEc.Success
|
||||
: PenumbraApiEc.NothingChanged;
|
||||
return ApiHelpers.Return(ret, args);
|
||||
}
|
||||
|
||||
public PenumbraApiEc CopyModSettings(Guid? collectionId, string modDirectoryFrom, string modDirectoryTo)
|
||||
{
|
||||
var args = ApiHelpers.Args("CollectionId", collectionId.HasValue ? collectionId.Value.ToString() : "NULL",
|
||||
"From", modDirectoryFrom, "To", modDirectoryTo);
|
||||
var sourceMod = _modManager.FirstOrDefault(m => string.Equals(m.ModPath.Name, modDirectoryFrom, StringComparison.OrdinalIgnoreCase));
|
||||
var targetMod = _modManager.FirstOrDefault(m => string.Equals(m.ModPath.Name, modDirectoryTo, StringComparison.OrdinalIgnoreCase));
|
||||
if (collectionId == null)
|
||||
foreach (var collection in _collectionManager.Storage)
|
||||
_collectionEditor.CopyModSettings(collection, sourceMod, modDirectoryFrom, targetMod, modDirectoryTo);
|
||||
else if (_collectionManager.Storage.ById(collectionId.Value, out var collection))
|
||||
_collectionEditor.CopyModSettings(collection, sourceMod, modDirectoryFrom, targetMod, modDirectoryTo);
|
||||
else
|
||||
return ApiHelpers.Return(PenumbraApiEc.CollectionMissing, args);
|
||||
|
||||
return ApiHelpers.Return(PenumbraApiEc.Success, args);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private void TriggerSettingEdited(Mod mod)
|
||||
{
|
||||
var collection = _collectionResolver.PlayerCollection();
|
||||
var (settings, parent) = collection[mod.Index];
|
||||
if (settings is { Enabled: true })
|
||||
ModSettingChanged?.Invoke(ModSettingChange.Edited, collection.Id, mod.Identifier, parent != collection);
|
||||
}
|
||||
|
||||
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? _1, DirectoryInfo? _2)
|
||||
{
|
||||
if (type == ModPathChangeType.Reloaded)
|
||||
TriggerSettingEdited(mod);
|
||||
}
|
||||
|
||||
private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, Setting _1, int _2, bool inherited)
|
||||
=> ModSettingChanged?.Invoke(type, collection.Id, mod?.ModPath.Name ?? string.Empty, inherited);
|
||||
|
||||
private void OnModOptionEdited(ModOptionChangeType type, Mod mod, int groupIndex, int optionIndex, int moveIndex)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ModOptionChangeType.GroupDeleted:
|
||||
case ModOptionChangeType.GroupMoved:
|
||||
case ModOptionChangeType.GroupTypeChanged:
|
||||
case ModOptionChangeType.PriorityChanged:
|
||||
case ModOptionChangeType.OptionDeleted:
|
||||
case ModOptionChangeType.OptionMoved:
|
||||
case ModOptionChangeType.OptionFilesChanged:
|
||||
case ModOptionChangeType.OptionFilesAdded:
|
||||
case ModOptionChangeType.OptionSwapsChanged:
|
||||
case ModOptionChangeType.OptionMetaChanged:
|
||||
TriggerSettingEdited(mod);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnModFileChanged(Mod mod, FileRegistry file)
|
||||
{
|
||||
if (file.CurrentUsage == 0)
|
||||
return;
|
||||
|
||||
TriggerSettingEdited(mod);
|
||||
}
|
||||
}
|
||||
132
Penumbra/Api/Api/ModsApi.cs
Normal file
132
Penumbra/Api/Api/ModsApi.cs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
using OtterGui.Compression;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Communication;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Services;
|
||||
|
||||
namespace Penumbra.Api.Api;
|
||||
|
||||
public class ModsApi : IPenumbraApiMods, IApiService, IDisposable
|
||||
{
|
||||
private readonly CommunicatorService _communicator;
|
||||
private readonly ModManager _modManager;
|
||||
private readonly ModImportManager _modImportManager;
|
||||
private readonly Configuration _config;
|
||||
private readonly ModFileSystem _modFileSystem;
|
||||
|
||||
public ModsApi(ModManager modManager, ModImportManager modImportManager, Configuration config, ModFileSystem modFileSystem,
|
||||
CommunicatorService communicator)
|
||||
{
|
||||
_modManager = modManager;
|
||||
_modImportManager = modImportManager;
|
||||
_config = config;
|
||||
_modFileSystem = modFileSystem;
|
||||
_communicator = communicator;
|
||||
_communicator.ModPathChanged.Subscribe(OnModPathChanged, ModPathChanged.Priority.ApiMods);
|
||||
}
|
||||
|
||||
private void OnModPathChanged(ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory, DirectoryInfo? newDirectory)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ModPathChangeType.Deleted when oldDirectory != null:
|
||||
ModDeleted?.Invoke(oldDirectory.Name);
|
||||
break;
|
||||
case ModPathChangeType.Added when newDirectory != null:
|
||||
ModAdded?.Invoke(newDirectory.Name);
|
||||
break;
|
||||
case ModPathChangeType.Moved when newDirectory != null && oldDirectory != null:
|
||||
ModMoved?.Invoke(oldDirectory.Name, newDirectory.Name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
=> _communicator.ModPathChanged.Unsubscribe(OnModPathChanged);
|
||||
|
||||
public Dictionary<string, string> GetModList()
|
||||
=> _modManager.ToDictionary(m => m.ModPath.Name, m => m.Name.Text);
|
||||
|
||||
public PenumbraApiEc InstallMod(string modFilePackagePath)
|
||||
{
|
||||
if (!File.Exists(modFilePackagePath))
|
||||
return ApiHelpers.Return(PenumbraApiEc.FileMissing, ApiHelpers.Args("ModFilePackagePath", modFilePackagePath));
|
||||
|
||||
_modImportManager.AddUnpack(modFilePackagePath);
|
||||
return ApiHelpers.Return(PenumbraApiEc.Success, ApiHelpers.Args("ModFilePackagePath", modFilePackagePath));
|
||||
}
|
||||
|
||||
public PenumbraApiEc ReloadMod(string modDirectory, string modName)
|
||||
{
|
||||
if (!_modManager.TryGetMod(modDirectory, modName, out var mod))
|
||||
return ApiHelpers.Return(PenumbraApiEc.ModMissing, ApiHelpers.Args("ModDirectory", modDirectory, "ModName", modName));
|
||||
|
||||
_modManager.ReloadMod(mod);
|
||||
return ApiHelpers.Return(PenumbraApiEc.Success, ApiHelpers.Args("ModDirectory", modDirectory, "ModName", modName));
|
||||
}
|
||||
|
||||
public PenumbraApiEc AddMod(string modDirectory)
|
||||
{
|
||||
var args = ApiHelpers.Args("ModDirectory", modDirectory);
|
||||
|
||||
var dir = new DirectoryInfo(Path.Join(_modManager.BasePath.FullName, Path.GetFileName(modDirectory)));
|
||||
if (!dir.Exists)
|
||||
return ApiHelpers.Return(PenumbraApiEc.FileMissing, args);
|
||||
|
||||
if (_modManager.BasePath.FullName != dir.Parent?.FullName)
|
||||
return ApiHelpers.Return(PenumbraApiEc.InvalidArgument, args);
|
||||
|
||||
_modManager.AddMod(dir);
|
||||
if (_config.UseFileSystemCompression)
|
||||
new FileCompactor(Penumbra.Log).StartMassCompact(dir.EnumerateFiles("*.*", SearchOption.AllDirectories),
|
||||
CompressionAlgorithm.Xpress8K);
|
||||
return ApiHelpers.Return(PenumbraApiEc.Success, args);
|
||||
}
|
||||
|
||||
public PenumbraApiEc DeleteMod(string modDirectory, string modName)
|
||||
{
|
||||
if (!_modManager.TryGetMod(modDirectory, modName, out var mod))
|
||||
return ApiHelpers.Return(PenumbraApiEc.NothingChanged, ApiHelpers.Args("ModDirectory", modDirectory, "ModName", modName));
|
||||
|
||||
_modManager.DeleteMod(mod);
|
||||
return ApiHelpers.Return(PenumbraApiEc.Success, ApiHelpers.Args("ModDirectory", modDirectory, "ModName", modName));
|
||||
}
|
||||
|
||||
public event Action<string>? ModDeleted;
|
||||
public event Action<string>? ModAdded;
|
||||
public event Action<string, string>? ModMoved;
|
||||
|
||||
public (PenumbraApiEc, string, bool, bool) GetModPath(string modDirectory, string modName)
|
||||
{
|
||||
if (!_modManager.TryGetMod(modDirectory, modName, out var mod)
|
||||
|| !_modFileSystem.FindLeaf(mod, out var leaf))
|
||||
return (PenumbraApiEc.ModMissing, string.Empty, false, false);
|
||||
|
||||
var fullPath = leaf.FullName();
|
||||
var isDefault = ModFileSystem.ModHasDefaultPath(mod, fullPath);
|
||||
var isNameDefault = isDefault || ModFileSystem.ModHasDefaultPath(mod, leaf.Name);
|
||||
return (PenumbraApiEc.Success, fullPath, !isDefault, !isNameDefault );
|
||||
}
|
||||
|
||||
public PenumbraApiEc SetModPath(string modDirectory, string modName, string newPath)
|
||||
{
|
||||
if (newPath.Length == 0)
|
||||
return PenumbraApiEc.InvalidArgument;
|
||||
|
||||
if (!_modManager.TryGetMod(modDirectory, modName, out var mod)
|
||||
|| !_modFileSystem.FindLeaf(mod, out var leaf))
|
||||
return PenumbraApiEc.ModMissing;
|
||||
|
||||
try
|
||||
{
|
||||
_modFileSystem.RenameAndMove(leaf, newPath);
|
||||
return PenumbraApiEc.Success;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return PenumbraApiEc.PathRenameFailed;
|
||||
}
|
||||
}
|
||||
}
|
||||
40
Penumbra/Api/Api/PenumbraApi.cs
Normal file
40
Penumbra/Api/Api/PenumbraApi.cs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
using OtterGui.Services;
|
||||
|
||||
namespace Penumbra.Api.Api;
|
||||
|
||||
public class PenumbraApi(
|
||||
CollectionApi collection,
|
||||
EditingApi editing,
|
||||
GameStateApi gameState,
|
||||
MetaApi meta,
|
||||
ModsApi mods,
|
||||
ModSettingsApi modSettings,
|
||||
PluginStateApi pluginState,
|
||||
RedrawApi redraw,
|
||||
ResolveApi resolve,
|
||||
ResourceTreeApi resourceTree,
|
||||
TemporaryApi temporary,
|
||||
UiApi ui) : IDisposable, IApiService, IPenumbraApi
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
Valid = false;
|
||||
}
|
||||
|
||||
public (int Breaking, int Feature) ApiVersion
|
||||
=> (5, 0);
|
||||
|
||||
public bool Valid { get; private set; } = true;
|
||||
public IPenumbraApiCollection Collection { get; } = collection;
|
||||
public IPenumbraApiEditing Editing { get; } = editing;
|
||||
public IPenumbraApiGameState GameState { get; } = gameState;
|
||||
public IPenumbraApiMeta Meta { get; } = meta;
|
||||
public IPenumbraApiMods Mods { get; } = mods;
|
||||
public IPenumbraApiModSettings ModSettings { get; } = modSettings;
|
||||
public IPenumbraApiPluginState PluginState { get; } = pluginState;
|
||||
public IPenumbraApiRedraw Redraw { get; } = redraw;
|
||||
public IPenumbraApiResolve Resolve { get; } = resolve;
|
||||
public IPenumbraApiResourceTree ResourceTree { get; } = resourceTree;
|
||||
public IPenumbraApiTemporary Temporary { get; } = temporary;
|
||||
public IPenumbraApiUi Ui { get; } = ui;
|
||||
}
|
||||
30
Penumbra/Api/Api/PluginStateApi.cs
Normal file
30
Penumbra/Api/Api/PluginStateApi.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
using Newtonsoft.Json;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Communication;
|
||||
using Penumbra.Services;
|
||||
|
||||
namespace Penumbra.Api.Api;
|
||||
|
||||
public class PluginStateApi(Configuration config, CommunicatorService communicator) : IPenumbraApiPluginState, IApiService
|
||||
{
|
||||
public string GetModDirectory()
|
||||
=> config.ModDirectory;
|
||||
|
||||
public string GetConfiguration()
|
||||
=> JsonConvert.SerializeObject(config, Formatting.Indented);
|
||||
|
||||
public event Action<string, bool>? ModDirectoryChanged
|
||||
{
|
||||
add => communicator.ModDirectoryChanged.Subscribe(value!, Communication.ModDirectoryChanged.Priority.Api);
|
||||
remove => communicator.ModDirectoryChanged.Unsubscribe(value!);
|
||||
}
|
||||
|
||||
public bool GetEnabledState()
|
||||
=> config.EnableMods;
|
||||
|
||||
public event Action<bool>? EnabledChange
|
||||
{
|
||||
add => communicator.EnabledChanged.Subscribe(value!, EnabledChanged.Priority.Api);
|
||||
remove => communicator.EnabledChanged.Unsubscribe(value!);
|
||||
}
|
||||
}
|
||||
27
Penumbra/Api/Api/RedrawApi.cs
Normal file
27
Penumbra/Api/Api/RedrawApi.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Interop.Services;
|
||||
|
||||
namespace Penumbra.Api.Api;
|
||||
|
||||
public class RedrawApi(RedrawService redrawService) : IPenumbraApiRedraw, IApiService
|
||||
{
|
||||
public void RedrawObject(int gameObjectIndex, RedrawType setting)
|
||||
=> redrawService.RedrawObject(gameObjectIndex, setting);
|
||||
|
||||
public void RedrawObject(string name, RedrawType setting)
|
||||
=> redrawService.RedrawObject(name, setting);
|
||||
|
||||
public void RedrawObject(GameObject? gameObject, RedrawType setting)
|
||||
=> redrawService.RedrawObject(gameObject, setting);
|
||||
|
||||
public void RedrawAll(RedrawType setting)
|
||||
=> redrawService.RedrawAll(setting);
|
||||
|
||||
public event GameObjectRedrawnDelegate? GameObjectRedrawn
|
||||
{
|
||||
add => redrawService.GameObjectRedrawn += value;
|
||||
remove => redrawService.GameObjectRedrawn -= value;
|
||||
}
|
||||
}
|
||||
101
Penumbra/Api/Api/ResolveApi.cs
Normal file
101
Penumbra/Api/Api/ResolveApi.cs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
using Dalamud.Plugin.Services;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Collections.Manager;
|
||||
using Penumbra.Interop.PathResolving;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Api.Api;
|
||||
|
||||
public class ResolveApi(
|
||||
ModManager modManager,
|
||||
CollectionManager collectionManager,
|
||||
Configuration config,
|
||||
CollectionResolver collectionResolver,
|
||||
ApiHelpers helpers,
|
||||
IFramework framework) : IPenumbraApiResolve, IApiService
|
||||
{
|
||||
public string ResolveDefaultPath(string gamePath)
|
||||
=> ResolvePath(gamePath, modManager, collectionManager.Active.Default);
|
||||
|
||||
public string ResolveInterfacePath(string gamePath)
|
||||
=> ResolvePath(gamePath, modManager, collectionManager.Active.Interface);
|
||||
|
||||
public string ResolveGameObjectPath(string gamePath, int gameObjectIdx)
|
||||
{
|
||||
helpers.AssociatedCollection(gameObjectIdx, out var collection);
|
||||
return ResolvePath(gamePath, modManager, collection);
|
||||
}
|
||||
|
||||
public string ResolvePlayerPath(string gamePath)
|
||||
=> ResolvePath(gamePath, modManager, collectionResolver.PlayerCollection());
|
||||
|
||||
public string[] ReverseResolveGameObjectPath(string moddedPath, int gameObjectIdx)
|
||||
{
|
||||
if (!config.EnableMods)
|
||||
return [moddedPath];
|
||||
|
||||
helpers.AssociatedCollection(gameObjectIdx, out var collection);
|
||||
var ret = collection.ReverseResolvePath(new FullPath(moddedPath));
|
||||
return ret.Select(r => r.ToString()).ToArray();
|
||||
}
|
||||
|
||||
public string[] ReverseResolvePlayerPath(string moddedPath)
|
||||
{
|
||||
if (!config.EnableMods)
|
||||
return [moddedPath];
|
||||
|
||||
var ret = collectionResolver.PlayerCollection().ReverseResolvePath(new FullPath(moddedPath));
|
||||
return ret.Select(r => r.ToString()).ToArray();
|
||||
}
|
||||
|
||||
public (string[], string[][]) ResolvePlayerPaths(string[] forward, string[] reverse)
|
||||
{
|
||||
if (!config.EnableMods)
|
||||
return (forward, reverse.Select(p => new[]
|
||||
{
|
||||
p,
|
||||
}).ToArray());
|
||||
|
||||
var playerCollection = collectionResolver.PlayerCollection();
|
||||
var resolved = forward.Select(p => ResolvePath(p, modManager, playerCollection)).ToArray();
|
||||
var reverseResolved = playerCollection.ReverseResolvePaths(reverse);
|
||||
return (resolved, reverseResolved.Select(a => a.Select(p => p.ToString()).ToArray()).ToArray());
|
||||
}
|
||||
|
||||
public async Task<(string[], string[][])> ResolvePlayerPathsAsync(string[] forward, string[] reverse)
|
||||
{
|
||||
if (!config.EnableMods)
|
||||
return (forward, reverse.Select(p => new[]
|
||||
{
|
||||
p,
|
||||
}).ToArray());
|
||||
|
||||
return await Task.Run(async () =>
|
||||
{
|
||||
var playerCollection = await 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);
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary> Resolve a path given by string for a specific collection. </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private string ResolvePath(string path, ModManager _, ModCollection collection)
|
||||
{
|
||||
if (!config.EnableMods)
|
||||
return path;
|
||||
|
||||
var gamePath = Utf8GamePath.FromString(path, out var p, true) ? p : Utf8GamePath.Empty;
|
||||
var ret = collection.ResolvePath(gamePath);
|
||||
return ret?.ToString() ?? path;
|
||||
}
|
||||
}
|
||||
63
Penumbra/Api/Api/ResourceTreeApi.cs
Normal file
63
Penumbra/Api/Api/ResourceTreeApi.cs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Penumbra.Interop.ResourceTree;
|
||||
|
||||
namespace Penumbra.Api.Api;
|
||||
|
||||
public class ResourceTreeApi(ResourceTreeFactory resourceTreeFactory, ObjectManager objects) : IPenumbraApiResourceTree, IApiService
|
||||
{
|
||||
public Dictionary<string, HashSet<string>>?[] GetGameObjectResourcePaths(params ushort[] gameObjects)
|
||||
{
|
||||
var characters = gameObjects.Select(index => objects.GetDalamudObject((int)index)).OfType<Character>();
|
||||
var resourceTrees = resourceTreeFactory.FromCharacters(characters, 0);
|
||||
var pathDictionaries = ResourceTreeApiHelper.GetResourcePathDictionaries(resourceTrees);
|
||||
|
||||
return Array.ConvertAll(gameObjects, obj => pathDictionaries.GetValueOrDefault(obj));
|
||||
}
|
||||
|
||||
public Dictionary<ushort, Dictionary<string, HashSet<string>>> GetPlayerResourcePaths()
|
||||
{
|
||||
var resourceTrees = resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly);
|
||||
return ResourceTreeApiHelper.GetResourcePathDictionaries(resourceTrees);
|
||||
}
|
||||
|
||||
public GameResourceDict?[] GetGameObjectResourcesOfType(ResourceType type, bool withUiData,
|
||||
params ushort[] gameObjects)
|
||||
{
|
||||
var characters = gameObjects.Select(index => objects.GetDalamudObject((int)index)).OfType<Character>();
|
||||
var resourceTrees = resourceTreeFactory.FromCharacters(characters, withUiData ? ResourceTreeFactory.Flags.WithUiData : 0);
|
||||
var resDictionaries = ResourceTreeApiHelper.GetResourcesOfType(resourceTrees, type);
|
||||
|
||||
return Array.ConvertAll(gameObjects, obj => resDictionaries.GetValueOrDefault(obj));
|
||||
}
|
||||
|
||||
public Dictionary<ushort, GameResourceDict> GetPlayerResourcesOfType(ResourceType type,
|
||||
bool withUiData)
|
||||
{
|
||||
var resourceTrees = resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly
|
||||
| (withUiData ? ResourceTreeFactory.Flags.WithUiData : 0));
|
||||
return ResourceTreeApiHelper.GetResourcesOfType(resourceTrees, type);
|
||||
}
|
||||
|
||||
public JObject?[] GetGameObjectResourceTrees(bool withUiData, params ushort[] gameObjects)
|
||||
{
|
||||
var characters = gameObjects.Select(index => objects.GetDalamudObject((int)index)).OfType<Character>();
|
||||
var resourceTrees = resourceTreeFactory.FromCharacters(characters, withUiData ? ResourceTreeFactory.Flags.WithUiData : 0);
|
||||
var resDictionary = ResourceTreeApiHelper.EncapsulateResourceTrees(resourceTrees);
|
||||
|
||||
return Array.ConvertAll(gameObjects, obj => resDictionary.GetValueOrDefault(obj));
|
||||
}
|
||||
|
||||
public Dictionary<ushort, JObject> GetPlayerResourceTrees(bool withUiData)
|
||||
{
|
||||
var resourceTrees = resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly
|
||||
| (withUiData ? ResourceTreeFactory.Flags.WithUiData : 0));
|
||||
var resDictionary = ResourceTreeApiHelper.EncapsulateResourceTrees(resourceTrees);
|
||||
|
||||
return resDictionary;
|
||||
}
|
||||
}
|
||||
190
Penumbra/Api/Api/TemporaryApi.cs
Normal file
190
Penumbra/Api/Api/TemporaryApi.cs
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
using OtterGui;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Collections.Manager;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Api.Api;
|
||||
|
||||
public class TemporaryApi(
|
||||
TempCollectionManager tempCollections,
|
||||
ObjectManager objects,
|
||||
ActorManager actors,
|
||||
CollectionManager collectionManager,
|
||||
TempModManager tempMods) : IPenumbraApiTemporary, IApiService
|
||||
{
|
||||
public Guid CreateTemporaryCollection(string name)
|
||||
=> tempCollections.CreateTemporaryCollection(name);
|
||||
|
||||
public PenumbraApiEc DeleteTemporaryCollection(Guid collectionId)
|
||||
=> tempCollections.RemoveTemporaryCollection(collectionId)
|
||||
? PenumbraApiEc.Success
|
||||
: PenumbraApiEc.CollectionMissing;
|
||||
|
||||
public PenumbraApiEc AssignTemporaryCollection(Guid collectionId, int actorIndex, bool forceAssignment)
|
||||
{
|
||||
var args = ApiHelpers.Args("CollectionId", collectionId, "ActorIndex", actorIndex, "Forced", forceAssignment);
|
||||
if (actorIndex < 0 || actorIndex >= objects.TotalCount)
|
||||
return ApiHelpers.Return(PenumbraApiEc.InvalidArgument, args);
|
||||
|
||||
var identifier = actors.FromObject(objects[actorIndex], out _, false, false, true);
|
||||
if (!identifier.IsValid)
|
||||
return ApiHelpers.Return(PenumbraApiEc.InvalidArgument, args);
|
||||
|
||||
if (!tempCollections.CollectionById(collectionId, out var collection))
|
||||
return ApiHelpers.Return(PenumbraApiEc.CollectionMissing, args);
|
||||
|
||||
if (forceAssignment)
|
||||
{
|
||||
if (tempCollections.Collections.ContainsKey(identifier) && !tempCollections.Collections.Delete(identifier))
|
||||
return ApiHelpers.Return(PenumbraApiEc.AssignmentDeletionFailed, args);
|
||||
}
|
||||
else if (tempCollections.Collections.ContainsKey(identifier)
|
||||
|| collectionManager.Active.Individuals.ContainsKey(identifier))
|
||||
{
|
||||
return ApiHelpers.Return(PenumbraApiEc.CharacterCollectionExists, args);
|
||||
}
|
||||
|
||||
var group = tempCollections.Collections.GetGroup(identifier);
|
||||
var ret = tempCollections.AddIdentifier(collection, group)
|
||||
? PenumbraApiEc.Success
|
||||
: PenumbraApiEc.UnknownError;
|
||||
return ApiHelpers.Return(ret, args);
|
||||
}
|
||||
|
||||
public PenumbraApiEc AddTemporaryModAll(string tag, Dictionary<string, string> paths, string manipString, int priority)
|
||||
{
|
||||
var args = ApiHelpers.Args("Tag", tag, "#Paths", paths.Count, "ManipString", manipString, "Priority", priority);
|
||||
if (!ConvertPaths(paths, out var p))
|
||||
return ApiHelpers.Return(PenumbraApiEc.InvalidGamePath, args);
|
||||
|
||||
if (!ConvertManips(manipString, out var m))
|
||||
return ApiHelpers.Return(PenumbraApiEc.InvalidManipulation, args);
|
||||
|
||||
var ret = tempMods.Register(tag, null, p, m, new ModPriority(priority)) switch
|
||||
{
|
||||
RedirectResult.Success => PenumbraApiEc.Success,
|
||||
_ => PenumbraApiEc.UnknownError,
|
||||
};
|
||||
return ApiHelpers.Return(ret, args);
|
||||
}
|
||||
|
||||
public PenumbraApiEc AddTemporaryMod(string tag, Guid collectionId, Dictionary<string, string> paths, string manipString, int priority)
|
||||
{
|
||||
var args = ApiHelpers.Args("Tag", tag, "CollectionId", collectionId, "#Paths", paths.Count, "ManipString",
|
||||
manipString, "Priority", priority);
|
||||
|
||||
if (collectionId == Guid.Empty)
|
||||
return ApiHelpers.Return(PenumbraApiEc.InvalidArgument, args);
|
||||
|
||||
if (!tempCollections.CollectionById(collectionId, out var collection)
|
||||
&& !collectionManager.Storage.ById(collectionId, out collection))
|
||||
return ApiHelpers.Return(PenumbraApiEc.CollectionMissing, args);
|
||||
|
||||
if (!ConvertPaths(paths, out var p))
|
||||
return ApiHelpers.Return(PenumbraApiEc.InvalidGamePath, args);
|
||||
|
||||
if (!ConvertManips(manipString, out var m))
|
||||
return ApiHelpers.Return(PenumbraApiEc.InvalidManipulation, args);
|
||||
|
||||
var ret = tempMods.Register(tag, collection, p, m, new ModPriority(priority)) switch
|
||||
{
|
||||
RedirectResult.Success => PenumbraApiEc.Success,
|
||||
_ => PenumbraApiEc.UnknownError,
|
||||
};
|
||||
return ApiHelpers.Return(ret, args);
|
||||
}
|
||||
|
||||
public PenumbraApiEc RemoveTemporaryModAll(string tag, int priority)
|
||||
{
|
||||
var ret = tempMods.Unregister(tag, null, new ModPriority(priority)) switch
|
||||
{
|
||||
RedirectResult.Success => PenumbraApiEc.Success,
|
||||
RedirectResult.NotRegistered => PenumbraApiEc.NothingChanged,
|
||||
_ => PenumbraApiEc.UnknownError,
|
||||
};
|
||||
return ApiHelpers.Return(ret, ApiHelpers.Args("Tag", tag, "Priority", priority));
|
||||
}
|
||||
|
||||
public PenumbraApiEc RemoveTemporaryMod(string tag, Guid collectionId, int priority)
|
||||
{
|
||||
var args = ApiHelpers.Args("Tag", tag, "CollectionId", collectionId, "Priority", priority);
|
||||
|
||||
if (!tempCollections.CollectionById(collectionId, out var collection)
|
||||
&& !collectionManager.Storage.ById(collectionId, out collection))
|
||||
return ApiHelpers.Return(PenumbraApiEc.CollectionMissing, args);
|
||||
|
||||
var ret = tempMods.Unregister(tag, collection, new ModPriority(priority)) switch
|
||||
{
|
||||
RedirectResult.Success => PenumbraApiEc.Success,
|
||||
RedirectResult.NotRegistered => PenumbraApiEc.NothingChanged,
|
||||
_ => PenumbraApiEc.UnknownError,
|
||||
};
|
||||
return ApiHelpers.Return(ret, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a dictionary of strings to a dictionary of game paths to full paths.
|
||||
/// Only returns true if all paths can successfully be converted and added.
|
||||
/// </summary>
|
||||
private static bool ConvertPaths(IReadOnlyDictionary<string, string> redirections,
|
||||
[NotNullWhen(true)] out Dictionary<Utf8GamePath, FullPath>? paths)
|
||||
{
|
||||
paths = new Dictionary<Utf8GamePath, FullPath>(redirections.Count);
|
||||
foreach (var (gString, fString) in redirections)
|
||||
{
|
||||
if (!Utf8GamePath.FromString(gString, out var path, false))
|
||||
{
|
||||
paths = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var fullPath = new FullPath(fString);
|
||||
if (!paths.TryAdd(path, fullPath))
|
||||
{
|
||||
paths = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert manipulations from a transmitted base64 string to actual manipulations.
|
||||
/// The empty string is treated as an empty set.
|
||||
/// Only returns true if all conversions are successful and distinct.
|
||||
/// </summary>
|
||||
private static bool ConvertManips(string manipString,
|
||||
[NotNullWhen(true)] out HashSet<MetaManipulation>? manips)
|
||||
{
|
||||
if (manipString.Length == 0)
|
||||
{
|
||||
manips = [];
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Functions.FromCompressedBase64<MetaManipulation[]>(manipString, out var manipArray) != MetaManipulation.CurrentVersion)
|
||||
{
|
||||
manips = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
manips = new HashSet<MetaManipulation>(manipArray!.Length);
|
||||
foreach (var manip in manipArray.Where(m => m.Validate()))
|
||||
{
|
||||
if (manips.Add(manip))
|
||||
continue;
|
||||
|
||||
Penumbra.Log.Warning($"Manipulation {manip} {manip.EntryToString()} is invalid and was skipped.");
|
||||
manips = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
101
Penumbra/Api/Api/UiApi.cs
Normal file
101
Penumbra/Api/Api/UiApi.cs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Communication;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.UI;
|
||||
|
||||
namespace Penumbra.Api.Api;
|
||||
|
||||
public class UiApi : IPenumbraApiUi, IApiService, IDisposable
|
||||
{
|
||||
private readonly CommunicatorService _communicator;
|
||||
private readonly ConfigWindow _configWindow;
|
||||
private readonly ModManager _modManager;
|
||||
|
||||
public UiApi(CommunicatorService communicator, ConfigWindow configWindow, ModManager modManager)
|
||||
{
|
||||
_communicator = communicator;
|
||||
_configWindow = configWindow;
|
||||
_modManager = modManager;
|
||||
_communicator.ChangedItemHover.Subscribe(OnChangedItemHover, ChangedItemHover.Priority.Default);
|
||||
_communicator.ChangedItemClick.Subscribe(OnChangedItemClick, ChangedItemClick.Priority.Default);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_communicator.ChangedItemHover.Unsubscribe(OnChangedItemHover);
|
||||
_communicator.ChangedItemClick.Unsubscribe(OnChangedItemClick);
|
||||
}
|
||||
|
||||
public event Action<ChangedItemType, uint>? ChangedItemTooltip;
|
||||
|
||||
public event Action<MouseButton, ChangedItemType, uint>? ChangedItemClicked;
|
||||
|
||||
public event Action<string, float, float>? PreSettingsTabBarDraw
|
||||
{
|
||||
add => _communicator.PreSettingsTabBarDraw.Subscribe(value!, Communication.PreSettingsTabBarDraw.Priority.Default);
|
||||
remove => _communicator.PreSettingsTabBarDraw.Unsubscribe(value!);
|
||||
}
|
||||
|
||||
public event Action<string>? PreSettingsPanelDraw
|
||||
{
|
||||
add => _communicator.PreSettingsPanelDraw.Subscribe(value!, Communication.PreSettingsPanelDraw.Priority.Default);
|
||||
remove => _communicator.PreSettingsPanelDraw.Unsubscribe(value!);
|
||||
}
|
||||
|
||||
public event Action<string>? PostEnabledDraw
|
||||
{
|
||||
add => _communicator.PostEnabledDraw.Subscribe(value!, Communication.PostEnabledDraw.Priority.Default);
|
||||
remove => _communicator.PostEnabledDraw.Unsubscribe(value!);
|
||||
}
|
||||
|
||||
public event Action<string>? PostSettingsPanelDraw
|
||||
{
|
||||
add => _communicator.PostSettingsPanelDraw.Subscribe(value!, Communication.PostSettingsPanelDraw.Priority.Default);
|
||||
remove => _communicator.PostSettingsPanelDraw.Unsubscribe(value!);
|
||||
}
|
||||
|
||||
public PenumbraApiEc OpenMainWindow(TabType tab, string modDirectory, string modName)
|
||||
{
|
||||
_configWindow.IsOpen = true;
|
||||
if (!Enum.IsDefined(tab))
|
||||
return PenumbraApiEc.InvalidArgument;
|
||||
|
||||
if (tab == TabType.Mods && (modDirectory.Length > 0 || modName.Length > 0))
|
||||
{
|
||||
if (_modManager.TryGetMod(modDirectory, modName, out var mod))
|
||||
_communicator.SelectTab.Invoke(tab, mod);
|
||||
else
|
||||
return PenumbraApiEc.ModMissing;
|
||||
}
|
||||
else if (tab != TabType.None)
|
||||
{
|
||||
_communicator.SelectTab.Invoke(tab, null);
|
||||
}
|
||||
|
||||
return PenumbraApiEc.Success;
|
||||
}
|
||||
|
||||
public void CloseMainWindow()
|
||||
=> _configWindow.IsOpen = false;
|
||||
|
||||
private void OnChangedItemClick(MouseButton button, object? data)
|
||||
{
|
||||
if (ChangedItemClicked == null)
|
||||
return;
|
||||
|
||||
var (type, id) = ChangedItemExtensions.ChangedItemToTypeAndId(data);
|
||||
ChangedItemClicked.Invoke(button, type, id);
|
||||
}
|
||||
|
||||
private void OnChangedItemHover(object? data)
|
||||
{
|
||||
if (ChangedItemTooltip == null)
|
||||
return;
|
||||
|
||||
var (type, id) = ChangedItemExtensions.ChangedItemToTypeAndId(data);
|
||||
ChangedItemTooltip.Invoke(type, id);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using Dalamud.Plugin.Services;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Collections.Manager;
|
||||
using Penumbra.Communication;
|
||||
|
|
@ -9,7 +10,7 @@ using Penumbra.String.Classes;
|
|||
|
||||
namespace Penumbra.Api;
|
||||
|
||||
public class DalamudSubstitutionProvider : IDisposable
|
||||
public class DalamudSubstitutionProvider : IDisposable, IApiService
|
||||
{
|
||||
private readonly ITextureSubstitutionProvider _substitution;
|
||||
private readonly ActiveCollectionData _activeCollectionData;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
using EmbedIO;
|
||||
using EmbedIO.Routing;
|
||||
using EmbedIO.WebApi;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Api;
|
||||
using Penumbra.Api.Enums;
|
||||
|
||||
namespace Penumbra.Api;
|
||||
|
||||
public class HttpApi : IDisposable
|
||||
public class HttpApi : IDisposable, IApiService
|
||||
{
|
||||
private partial class Controller : WebApiController
|
||||
{
|
||||
|
|
@ -67,7 +69,7 @@ public class HttpApi : IDisposable
|
|||
public partial object? GetMods()
|
||||
{
|
||||
Penumbra.Log.Debug($"[HTTP] {nameof(GetMods)} triggered.");
|
||||
return _api.GetModList();
|
||||
return _api.Mods.GetModList();
|
||||
}
|
||||
|
||||
public async partial Task Redraw()
|
||||
|
|
@ -75,17 +77,15 @@ public class HttpApi : IDisposable
|
|||
var data = await HttpContext.GetRequestDataAsync<RedrawData>();
|
||||
Penumbra.Log.Debug($"[HTTP] {nameof(Redraw)} triggered with {data}.");
|
||||
if (data.ObjectTableIndex >= 0)
|
||||
_api.RedrawObject(data.ObjectTableIndex, data.Type);
|
||||
else if (data.Name.Length > 0)
|
||||
_api.RedrawObject(data.Name, data.Type);
|
||||
_api.Redraw.RedrawObject(data.ObjectTableIndex, data.Type);
|
||||
else
|
||||
_api.RedrawAll(data.Type);
|
||||
_api.Redraw.RedrawAll(data.Type);
|
||||
}
|
||||
|
||||
public partial void RedrawAll()
|
||||
{
|
||||
Penumbra.Log.Debug($"[HTTP] {nameof(RedrawAll)} triggered.");
|
||||
_api.RedrawAll(RedrawType.Redraw);
|
||||
_api.Redraw.RedrawAll(RedrawType.Redraw);
|
||||
}
|
||||
|
||||
public async partial Task ReloadMod()
|
||||
|
|
@ -95,10 +95,10 @@ public class HttpApi : IDisposable
|
|||
// Add the mod if it is not already loaded and if the directory name is given.
|
||||
// AddMod returns Success if the mod is already loaded.
|
||||
if (data.Path.Length != 0)
|
||||
_api.AddMod(data.Path);
|
||||
_api.Mods.AddMod(data.Path);
|
||||
|
||||
// Reload the mod by path or name, which will also remove no-longer existing mods.
|
||||
_api.ReloadMod(data.Path, data.Name);
|
||||
_api.Mods.ReloadMod(data.Path, data.Name);
|
||||
}
|
||||
|
||||
public async partial Task InstallMod()
|
||||
|
|
@ -106,13 +106,13 @@ public class HttpApi : IDisposable
|
|||
var data = await HttpContext.GetRequestDataAsync<ModInstallData>();
|
||||
Penumbra.Log.Debug($"[HTTP] {nameof(InstallMod)} triggered with {data}.");
|
||||
if (data.Path.Length != 0)
|
||||
_api.InstallMod(data.Path);
|
||||
_api.Mods.InstallMod(data.Path);
|
||||
}
|
||||
|
||||
public partial void OpenWindow()
|
||||
{
|
||||
Penumbra.Log.Debug($"[HTTP] {nameof(OpenWindow)} triggered.");
|
||||
_api.OpenMainWindow(TabType.Mods, string.Empty, string.Empty);
|
||||
_api.Ui.OpenMainWindow(TabType.Mods, string.Empty, string.Empty);
|
||||
}
|
||||
|
||||
private record ModReloadData(string Path, string Name)
|
||||
|
|
|
|||
118
Penumbra/Api/IpcProviders.cs
Normal file
118
Penumbra/Api/IpcProviders.cs
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
using Dalamud.Plugin;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Api;
|
||||
using Penumbra.Api.Helpers;
|
||||
|
||||
namespace Penumbra.Api;
|
||||
|
||||
public sealed class IpcProviders : IDisposable, IApiService
|
||||
{
|
||||
private readonly List<IDisposable> _providers;
|
||||
|
||||
private readonly EventProvider _disposedProvider;
|
||||
private readonly EventProvider _initializedProvider;
|
||||
|
||||
public IpcProviders(DalamudPluginInterface pi, IPenumbraApi api)
|
||||
{
|
||||
_disposedProvider = IpcSubscribers.Disposed.Provider(pi);
|
||||
_initializedProvider = IpcSubscribers.Initialized.Provider(pi);
|
||||
_providers =
|
||||
[
|
||||
IpcSubscribers.GetCollections.Provider(pi, api.Collection),
|
||||
IpcSubscribers.GetChangedItemsForCollection.Provider(pi, api.Collection),
|
||||
IpcSubscribers.GetCollection.Provider(pi, api.Collection),
|
||||
IpcSubscribers.GetCollectionForObject.Provider(pi, api.Collection),
|
||||
IpcSubscribers.SetCollection.Provider(pi, api.Collection),
|
||||
IpcSubscribers.SetCollectionForObject.Provider(pi, api.Collection),
|
||||
|
||||
IpcSubscribers.ConvertTextureFile.Provider(pi, api.Editing),
|
||||
IpcSubscribers.ConvertTextureData.Provider(pi, api.Editing),
|
||||
|
||||
IpcSubscribers.GetDrawObjectInfo.Provider(pi, api.GameState),
|
||||
IpcSubscribers.GetCutsceneParentIndex.Provider(pi, api.GameState),
|
||||
IpcSubscribers.SetCutsceneParentIndex.Provider(pi, api.GameState),
|
||||
IpcSubscribers.CreatingCharacterBase.Provider(pi, api.GameState),
|
||||
IpcSubscribers.CreatedCharacterBase.Provider(pi, api.GameState),
|
||||
IpcSubscribers.GameObjectResourcePathResolved.Provider(pi, api.GameState),
|
||||
|
||||
IpcSubscribers.GetPlayerMetaManipulations.Provider(pi, api.Meta),
|
||||
IpcSubscribers.GetMetaManipulations.Provider(pi, api.Meta),
|
||||
|
||||
IpcSubscribers.GetModList.Provider(pi, api.Mods),
|
||||
IpcSubscribers.InstallMod.Provider(pi, api.Mods),
|
||||
IpcSubscribers.ReloadMod.Provider(pi, api.Mods),
|
||||
IpcSubscribers.AddMod.Provider(pi, api.Mods),
|
||||
IpcSubscribers.DeleteMod.Provider(pi, api.Mods),
|
||||
IpcSubscribers.ModDeleted.Provider(pi, api.Mods),
|
||||
IpcSubscribers.ModAdded.Provider(pi, api.Mods),
|
||||
IpcSubscribers.ModMoved.Provider(pi, api.Mods),
|
||||
IpcSubscribers.GetModPath.Provider(pi, api.Mods),
|
||||
IpcSubscribers.SetModPath.Provider(pi, api.Mods),
|
||||
|
||||
IpcSubscribers.GetAvailableModSettings.Provider(pi, api.ModSettings),
|
||||
IpcSubscribers.GetCurrentModSettings.Provider(pi, api.ModSettings),
|
||||
IpcSubscribers.TryInheritMod.Provider(pi, api.ModSettings),
|
||||
IpcSubscribers.TrySetMod.Provider(pi, api.ModSettings),
|
||||
IpcSubscribers.TrySetModPriority.Provider(pi, api.ModSettings),
|
||||
IpcSubscribers.TrySetModSetting.Provider(pi, api.ModSettings),
|
||||
IpcSubscribers.TrySetModSettings.Provider(pi, api.ModSettings),
|
||||
IpcSubscribers.ModSettingChanged.Provider(pi, api.ModSettings),
|
||||
IpcSubscribers.CopyModSettings.Provider(pi, api.ModSettings),
|
||||
|
||||
IpcSubscribers.ApiVersion.Provider(pi, api),
|
||||
IpcSubscribers.GetModDirectory.Provider(pi, api.PluginState),
|
||||
IpcSubscribers.GetConfiguration.Provider(pi, api.PluginState),
|
||||
IpcSubscribers.ModDirectoryChanged.Provider(pi, api.PluginState),
|
||||
IpcSubscribers.GetEnabledState.Provider(pi, api.PluginState),
|
||||
IpcSubscribers.EnabledChange.Provider(pi, api.PluginState),
|
||||
|
||||
IpcSubscribers.RedrawObject.Provider(pi, api.Redraw),
|
||||
IpcSubscribers.RedrawAll.Provider(pi, api.Redraw),
|
||||
IpcSubscribers.GameObjectRedrawn.Provider(pi, api.Redraw),
|
||||
|
||||
IpcSubscribers.ResolveDefaultPath.Provider(pi, api.Resolve),
|
||||
IpcSubscribers.ResolveInterfacePath.Provider(pi, api.Resolve),
|
||||
IpcSubscribers.ResolveGameObjectPath.Provider(pi, api.Resolve),
|
||||
IpcSubscribers.ResolvePlayerPath.Provider(pi, api.Resolve),
|
||||
IpcSubscribers.ReverseResolveGameObjectPath.Provider(pi, api.Resolve),
|
||||
IpcSubscribers.ReverseResolvePlayerPath.Provider(pi, api.Resolve),
|
||||
IpcSubscribers.ResolvePlayerPaths.Provider(pi, api.Resolve),
|
||||
IpcSubscribers.ResolvePlayerPathsAsync.Provider(pi, api.Resolve),
|
||||
|
||||
IpcSubscribers.GetGameObjectResourcePaths.Provider(pi, api.ResourceTree),
|
||||
IpcSubscribers.GetPlayerResourcePaths.Provider(pi, api.ResourceTree),
|
||||
IpcSubscribers.GetGameObjectResourcesOfType.Provider(pi, api.ResourceTree),
|
||||
IpcSubscribers.GetPlayerResourcesOfType.Provider(pi, api.ResourceTree),
|
||||
IpcSubscribers.GetGameObjectResourceTrees.Provider(pi, api.ResourceTree),
|
||||
IpcSubscribers.GetPlayerResourceTrees.Provider(pi, api.ResourceTree),
|
||||
|
||||
IpcSubscribers.CreateTemporaryCollection.Provider(pi, api.Temporary),
|
||||
IpcSubscribers.DeleteTemporaryCollection.Provider(pi, api.Temporary),
|
||||
IpcSubscribers.AssignTemporaryCollection.Provider(pi, api.Temporary),
|
||||
IpcSubscribers.AddTemporaryModAll.Provider(pi, api.Temporary),
|
||||
IpcSubscribers.AddTemporaryMod.Provider(pi, api.Temporary),
|
||||
IpcSubscribers.RemoveTemporaryModAll.Provider(pi, api.Temporary),
|
||||
IpcSubscribers.RemoveTemporaryMod.Provider(pi, api.Temporary),
|
||||
|
||||
IpcSubscribers.ChangedItemTooltip.Provider(pi, api.Ui),
|
||||
IpcSubscribers.ChangedItemClicked.Provider(pi, api.Ui),
|
||||
IpcSubscribers.PreSettingsTabBarDraw.Provider(pi, api.Ui),
|
||||
IpcSubscribers.PreSettingsPanelDraw.Provider(pi, api.Ui),
|
||||
IpcSubscribers.PostEnabledDraw.Provider(pi, api.Ui),
|
||||
IpcSubscribers.PostSettingsPanelDraw.Provider(pi, api.Ui),
|
||||
IpcSubscribers.OpenMainWindow.Provider(pi, api.Ui),
|
||||
IpcSubscribers.CloseMainWindow.Provider(pi, api.Ui),
|
||||
];
|
||||
_initializedProvider.Invoke();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var provider in _providers)
|
||||
provider.Dispose();
|
||||
_providers.Clear();
|
||||
_initializedProvider.Dispose();
|
||||
_disposedProvider.Invoke();
|
||||
_disposedProvider.Dispose();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
166
Penumbra/Api/IpcTester/CollectionsIpcTester.cs
Normal file
166
Penumbra/Api/IpcTester/CollectionsIpcTester.cs
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Plugin;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Api.IpcSubscribers;
|
||||
using Penumbra.Collections.Manager;
|
||||
using Penumbra.GameData.Enums;
|
||||
using ImGuiClip = OtterGui.ImGuiClip;
|
||||
|
||||
namespace Penumbra.Api.IpcTester;
|
||||
|
||||
public class CollectionsIpcTester(DalamudPluginInterface pi) : IUiService
|
||||
{
|
||||
private int _objectIdx;
|
||||
private string _collectionIdString = string.Empty;
|
||||
private Guid? _collectionId = null;
|
||||
private bool _allowCreation = true;
|
||||
private bool _allowDeletion = true;
|
||||
private ApiCollectionType _type = ApiCollectionType.Yourself;
|
||||
|
||||
private Dictionary<Guid, string> _collections = [];
|
||||
private (string, ChangedItemType, uint)[] _changedItems = [];
|
||||
private PenumbraApiEc _returnCode = PenumbraApiEc.Success;
|
||||
private (Guid Id, string Name)? _oldCollection;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var _ = ImRaii.TreeNode("Collections");
|
||||
if (!_)
|
||||
return;
|
||||
|
||||
ImGuiUtil.GenericEnumCombo("Collection Type", 200, _type, out _type, t => ((CollectionType)t).ToName());
|
||||
ImGui.InputInt("Object Index##Collections", ref _objectIdx, 0, 0);
|
||||
ImGuiUtil.GuidInput("Collection Id##Collections", "Collection GUID...", string.Empty, ref _collectionId, ref _collectionIdString);
|
||||
ImGui.Checkbox("Allow Assignment Creation", ref _allowCreation);
|
||||
ImGui.SameLine();
|
||||
ImGui.Checkbox("Allow Assignment Deletion", ref _allowDeletion);
|
||||
|
||||
using var table = ImRaii.Table(string.Empty, 4, ImGuiTableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
IpcTester.DrawIntro("Last Return Code", _returnCode.ToString());
|
||||
if (_oldCollection != null)
|
||||
ImGui.TextUnformatted(!_oldCollection.HasValue ? "Created" : _oldCollection.ToString());
|
||||
|
||||
IpcTester.DrawIntro(GetCollection.Label, "Current Collection");
|
||||
DrawCollection(new GetCollection(pi).Invoke(ApiCollectionType.Current));
|
||||
|
||||
IpcTester.DrawIntro(GetCollection.Label, "Default Collection");
|
||||
DrawCollection(new GetCollection(pi).Invoke(ApiCollectionType.Default));
|
||||
|
||||
IpcTester.DrawIntro(GetCollection.Label, "Interface Collection");
|
||||
DrawCollection(new GetCollection(pi).Invoke(ApiCollectionType.Interface));
|
||||
|
||||
IpcTester.DrawIntro(GetCollection.Label, "Special Collection");
|
||||
DrawCollection(new GetCollection(pi).Invoke(_type));
|
||||
|
||||
IpcTester.DrawIntro(GetCollections.Label, "Collections");
|
||||
DrawCollectionPopup();
|
||||
if (ImGui.Button("Get##Collections"))
|
||||
{
|
||||
_collections = new GetCollections(pi).Invoke();
|
||||
ImGui.OpenPopup("Collections");
|
||||
}
|
||||
|
||||
IpcTester.DrawIntro(GetCollectionForObject.Label, "Get Object Collection");
|
||||
var (valid, individual, effectiveCollection) = new GetCollectionForObject(pi).Invoke(_objectIdx);
|
||||
DrawCollection(effectiveCollection);
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted($"({(valid ? "Valid" : "Invalid")} Object{(individual ? ", Individual Assignment)" : ")")}");
|
||||
|
||||
IpcTester.DrawIntro(SetCollection.Label, "Set Special Collection");
|
||||
if (ImGui.Button("Set##SpecialCollection"))
|
||||
(_returnCode, _oldCollection) =
|
||||
new SetCollection(pi).Invoke(_type, _collectionId.GetValueOrDefault(Guid.Empty), _allowCreation, _allowDeletion);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Remove##SpecialCollection"))
|
||||
(_returnCode, _oldCollection) = new SetCollection(pi).Invoke(_type, null, _allowCreation, _allowDeletion);
|
||||
|
||||
IpcTester.DrawIntro(SetCollectionForObject.Label, "Set Object Collection");
|
||||
if (ImGui.Button("Set##ObjectCollection"))
|
||||
(_returnCode, _oldCollection) = new SetCollectionForObject(pi).Invoke(_objectIdx, _collectionId.GetValueOrDefault(Guid.Empty),
|
||||
_allowCreation, _allowDeletion);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Remove##ObjectCollection"))
|
||||
(_returnCode, _oldCollection) = new SetCollectionForObject(pi).Invoke(_objectIdx, null, _allowCreation, _allowDeletion);
|
||||
|
||||
IpcTester.DrawIntro(GetChangedItemsForCollection.Label, "Changed Item List");
|
||||
DrawChangedItemPopup();
|
||||
if (ImGui.Button("Get##ChangedItems"))
|
||||
{
|
||||
var items = new GetChangedItemsForCollection(pi).Invoke(_collectionId.GetValueOrDefault(Guid.Empty));
|
||||
_changedItems = items.Select(kvp =>
|
||||
{
|
||||
var (type, id) = ChangedItemExtensions.ChangedItemToTypeAndId(kvp.Value);
|
||||
return (kvp.Key, type, id);
|
||||
}).ToArray();
|
||||
ImGui.OpenPopup("Changed Item List");
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawChangedItemPopup()
|
||||
{
|
||||
ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(500, 500));
|
||||
using var p = ImRaii.Popup("Changed Item List");
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
using (var t = ImRaii.Table("##ChangedItems", 3, ImGuiTableFlags.SizingFixedFit))
|
||||
{
|
||||
if (t)
|
||||
ImGuiClip.ClippedDraw(_changedItems, t =>
|
||||
{
|
||||
ImGuiUtil.DrawTableColumn(t.Item1);
|
||||
ImGuiUtil.DrawTableColumn(t.Item2.ToString());
|
||||
ImGuiUtil.DrawTableColumn(t.Item3.ToString());
|
||||
}, ImGui.GetTextLineHeightWithSpacing());
|
||||
}
|
||||
|
||||
if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused())
|
||||
ImGui.CloseCurrentPopup();
|
||||
}
|
||||
|
||||
private void DrawCollectionPopup()
|
||||
{
|
||||
ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(500, 500));
|
||||
using var p = ImRaii.Popup("Collections");
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
using (var t = ImRaii.Table("collections", 2, ImGuiTableFlags.SizingFixedFit))
|
||||
{
|
||||
if (t)
|
||||
foreach (var collection in _collections)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
DrawCollection((collection.Key, collection.Value));
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused())
|
||||
ImGui.CloseCurrentPopup();
|
||||
}
|
||||
|
||||
private static void DrawCollection((Guid Id, string Name)? collection)
|
||||
{
|
||||
if (collection == null)
|
||||
{
|
||||
ImGui.TextUnformatted("<Unassigned>");
|
||||
ImGui.TableNextColumn();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui.TextUnformatted(collection.Value.Name);
|
||||
ImGui.TableNextColumn();
|
||||
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
||||
{
|
||||
ImGuiUtil.CopyOnClickSelectable(collection.Value.Id.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
70
Penumbra/Api/IpcTester/EditingIpcTester.cs
Normal file
70
Penumbra/Api/IpcTester/EditingIpcTester.cs
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
using Dalamud.Plugin;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Api.IpcSubscribers;
|
||||
|
||||
namespace Penumbra.Api.IpcTester;
|
||||
|
||||
public class EditingIpcTester(DalamudPluginInterface pi) : IUiService
|
||||
{
|
||||
private string _inputPath = string.Empty;
|
||||
private string _inputPath2 = string.Empty;
|
||||
private string _outputPath = string.Empty;
|
||||
private string _outputPath2 = string.Empty;
|
||||
|
||||
private TextureType _typeSelector;
|
||||
private bool _mipMaps = true;
|
||||
|
||||
private Task? _task1;
|
||||
private Task? _task2;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var _ = ImRaii.TreeNode("Editing");
|
||||
if (!_)
|
||||
return;
|
||||
|
||||
ImGui.InputTextWithHint("##inputPath", "Input Texture Path...", ref _inputPath, 256);
|
||||
ImGui.InputTextWithHint("##outputPath", "Output Texture Path...", ref _outputPath, 256);
|
||||
ImGui.InputTextWithHint("##inputPath2", "Input Texture Path 2...", ref _inputPath2, 256);
|
||||
ImGui.InputTextWithHint("##outputPath2", "Output Texture Path 2...", ref _outputPath2, 256);
|
||||
TypeCombo();
|
||||
ImGui.Checkbox("Add MipMaps", ref _mipMaps);
|
||||
|
||||
using var table = ImRaii.Table("...", 3, ImGuiTableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
IpcTester.DrawIntro(ConvertTextureFile.Label, (string)"Convert Texture 1");
|
||||
if (ImGuiUtil.DrawDisabledButton("Save 1", Vector2.Zero, string.Empty, _task1 is { IsCompleted: false }))
|
||||
_task1 = new ConvertTextureFile(pi).Invoke(_inputPath, _outputPath, _typeSelector, _mipMaps);
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(_task1 == null ? "Not Initiated" : _task1.Status.ToString());
|
||||
if (ImGui.IsItemHovered() && _task1?.Status == TaskStatus.Faulted)
|
||||
ImGui.SetTooltip(_task1.Exception?.ToString());
|
||||
|
||||
IpcTester.DrawIntro(ConvertTextureFile.Label, (string)"Convert Texture 2");
|
||||
if (ImGuiUtil.DrawDisabledButton("Save 2", Vector2.Zero, string.Empty, _task2 is { IsCompleted: false }))
|
||||
_task2 = new ConvertTextureFile(pi).Invoke(_inputPath2, _outputPath2, _typeSelector, _mipMaps);
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(_task2 == null ? "Not Initiated" : _task2.Status.ToString());
|
||||
if (ImGui.IsItemHovered() && _task2?.Status == TaskStatus.Faulted)
|
||||
ImGui.SetTooltip(_task2.Exception?.ToString());
|
||||
}
|
||||
|
||||
private void TypeCombo()
|
||||
{
|
||||
using var combo = ImRaii.Combo("Convert To", _typeSelector.ToString());
|
||||
if (!combo)
|
||||
return;
|
||||
|
||||
foreach (var value in Enum.GetValues<TextureType>())
|
||||
{
|
||||
if (ImGui.Selectable(value.ToString(), _typeSelector == value))
|
||||
_typeSelector = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
137
Penumbra/Api/IpcTester/GameStateIpcTester.cs
Normal file
137
Penumbra/Api/IpcTester/GameStateIpcTester.cs
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Plugin;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.Api.IpcSubscribers;
|
||||
using Penumbra.String;
|
||||
|
||||
namespace Penumbra.Api.IpcTester;
|
||||
|
||||
public class GameStateIpcTester : IUiService, IDisposable
|
||||
{
|
||||
private readonly DalamudPluginInterface _pi;
|
||||
public readonly EventSubscriber<nint, Guid, nint, nint, nint> CharacterBaseCreating;
|
||||
public readonly EventSubscriber<nint, Guid, nint> CharacterBaseCreated;
|
||||
public readonly EventSubscriber<nint, string, string> GameObjectResourcePathResolved;
|
||||
|
||||
private string _lastCreatedGameObjectName = string.Empty;
|
||||
private nint _lastCreatedDrawObject = nint.Zero;
|
||||
private DateTimeOffset _lastCreatedGameObjectTime = DateTimeOffset.MaxValue;
|
||||
private string _lastResolvedGamePath = string.Empty;
|
||||
private string _lastResolvedFullPath = string.Empty;
|
||||
private string _lastResolvedObject = string.Empty;
|
||||
private DateTimeOffset _lastResolvedGamePathTime = DateTimeOffset.MaxValue;
|
||||
private string _currentDrawObjectString = string.Empty;
|
||||
private nint _currentDrawObject = nint.Zero;
|
||||
private int _currentCutsceneActor;
|
||||
private int _currentCutsceneParent;
|
||||
private PenumbraApiEc _cutsceneError = PenumbraApiEc.Success;
|
||||
|
||||
public GameStateIpcTester(DalamudPluginInterface pi)
|
||||
{
|
||||
_pi = pi;
|
||||
CharacterBaseCreating = CreatingCharacterBase.Subscriber(pi, UpdateLastCreated);
|
||||
CharacterBaseCreated = CreatedCharacterBase.Subscriber(pi, UpdateLastCreated2);
|
||||
GameObjectResourcePathResolved = IpcSubscribers.GameObjectResourcePathResolved.Subscriber(pi, UpdateGameObjectResourcePath);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
CharacterBaseCreating.Dispose();
|
||||
CharacterBaseCreated.Dispose();
|
||||
GameObjectResourcePathResolved.Dispose();
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var _ = ImRaii.TreeNode("Game State");
|
||||
if (!_)
|
||||
return;
|
||||
|
||||
if (ImGui.InputTextWithHint("##drawObject", "Draw Object Address..", ref _currentDrawObjectString, 16,
|
||||
ImGuiInputTextFlags.CharsHexadecimal))
|
||||
_currentDrawObject = nint.TryParse(_currentDrawObjectString, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
|
||||
out var tmp)
|
||||
? tmp
|
||||
: nint.Zero;
|
||||
|
||||
ImGui.InputInt("Cutscene Actor", ref _currentCutsceneActor, 0);
|
||||
ImGui.InputInt("Cutscene Parent", ref _currentCutsceneParent, 0);
|
||||
if (_cutsceneError is not PenumbraApiEc.Success)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("Invalid Argument on last Call");
|
||||
}
|
||||
|
||||
using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
IpcTester.DrawIntro(GetDrawObjectInfo.Label, "Draw Object Info");
|
||||
if (_currentDrawObject == nint.Zero)
|
||||
{
|
||||
ImGui.TextUnformatted("Invalid");
|
||||
}
|
||||
else
|
||||
{
|
||||
var (ptr, (collectionId, collectionName)) = new GetDrawObjectInfo(_pi).Invoke(_currentDrawObject);
|
||||
ImGui.TextUnformatted(ptr == nint.Zero ? $"No Actor Associated, {collectionName}" : $"{ptr:X}, {collectionName}");
|
||||
ImGui.SameLine();
|
||||
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
||||
{
|
||||
ImGui.TextUnformatted(collectionId.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
IpcTester.DrawIntro(GetCutsceneParentIndex.Label, "Cutscene Parent");
|
||||
ImGui.TextUnformatted(new GetCutsceneParentIndex(_pi).Invoke(_currentCutsceneActor).ToString());
|
||||
|
||||
IpcTester.DrawIntro(SetCutsceneParentIndex.Label, "Cutscene Parent");
|
||||
if (ImGui.Button("Set Parent"))
|
||||
_cutsceneError = new SetCutsceneParentIndex(_pi)
|
||||
.Invoke(_currentCutsceneActor, _currentCutsceneParent);
|
||||
|
||||
IpcTester.DrawIntro(CreatingCharacterBase.Label, "Last Drawobject created");
|
||||
if (_lastCreatedGameObjectTime < DateTimeOffset.Now)
|
||||
ImGui.TextUnformatted(_lastCreatedDrawObject != nint.Zero
|
||||
? $"0x{_lastCreatedDrawObject:X} for <{_lastCreatedGameObjectName}> at {_lastCreatedGameObjectTime}"
|
||||
: $"NULL for <{_lastCreatedGameObjectName}> at {_lastCreatedGameObjectTime}");
|
||||
|
||||
IpcTester.DrawIntro(IpcSubscribers.GameObjectResourcePathResolved.Label, "Last GamePath resolved");
|
||||
if (_lastResolvedGamePathTime < DateTimeOffset.Now)
|
||||
ImGui.TextUnformatted(
|
||||
$"{_lastResolvedGamePath} -> {_lastResolvedFullPath} for <{_lastResolvedObject}> at {_lastResolvedGamePathTime}");
|
||||
}
|
||||
|
||||
private void UpdateLastCreated(nint gameObject, Guid _, nint _2, nint _3, nint _4)
|
||||
{
|
||||
_lastCreatedGameObjectName = GetObjectName(gameObject);
|
||||
_lastCreatedGameObjectTime = DateTimeOffset.Now;
|
||||
_lastCreatedDrawObject = nint.Zero;
|
||||
}
|
||||
|
||||
private void UpdateLastCreated2(nint gameObject, Guid _, nint drawObject)
|
||||
{
|
||||
_lastCreatedGameObjectName = GetObjectName(gameObject);
|
||||
_lastCreatedGameObjectTime = DateTimeOffset.Now;
|
||||
_lastCreatedDrawObject = drawObject;
|
||||
}
|
||||
|
||||
private void UpdateGameObjectResourcePath(nint gameObject, string gamePath, string fullPath)
|
||||
{
|
||||
_lastResolvedObject = GetObjectName(gameObject);
|
||||
_lastResolvedGamePath = gamePath;
|
||||
_lastResolvedFullPath = fullPath;
|
||||
_lastResolvedGamePathTime = DateTimeOffset.Now;
|
||||
}
|
||||
|
||||
private static unsafe string GetObjectName(nint gameObject)
|
||||
{
|
||||
var obj = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)gameObject;
|
||||
var name = obj != null ? obj->Name : null;
|
||||
return name != null && *name != 0 ? new ByteString(name).ToString() : "Unknown";
|
||||
}
|
||||
}
|
||||
133
Penumbra/Api/IpcTester/IpcTester.cs
Normal file
133
Penumbra/Api/IpcTester/IpcTester.cs
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Api;
|
||||
|
||||
namespace Penumbra.Api.IpcTester;
|
||||
|
||||
public class IpcTester(
|
||||
IpcProviders ipcProviders,
|
||||
IPenumbraApi api,
|
||||
PluginStateIpcTester pluginStateIpcTester,
|
||||
UiIpcTester uiIpcTester,
|
||||
RedrawingIpcTester redrawingIpcTester,
|
||||
GameStateIpcTester gameStateIpcTester,
|
||||
ResolveIpcTester resolveIpcTester,
|
||||
CollectionsIpcTester collectionsIpcTester,
|
||||
MetaIpcTester metaIpcTester,
|
||||
ModsIpcTester modsIpcTester,
|
||||
ModSettingsIpcTester modSettingsIpcTester,
|
||||
EditingIpcTester editingIpcTester,
|
||||
TemporaryIpcTester temporaryIpcTester,
|
||||
ResourceTreeIpcTester resourceTreeIpcTester,
|
||||
IFramework framework) : IUiService
|
||||
{
|
||||
private readonly IpcProviders _ipcProviders = ipcProviders;
|
||||
private DateTime _lastUpdate;
|
||||
private bool _subscribed = false;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
try
|
||||
{
|
||||
_lastUpdate = framework.LastUpdateUTC.AddSeconds(1);
|
||||
Subscribe();
|
||||
|
||||
ImGui.TextUnformatted($"API Version: {api.ApiVersion.Breaking}.{api.ApiVersion.Feature:D4}");
|
||||
collectionsIpcTester.Draw();
|
||||
editingIpcTester.Draw();
|
||||
gameStateIpcTester.Draw();
|
||||
metaIpcTester.Draw();
|
||||
modSettingsIpcTester.Draw();
|
||||
modsIpcTester.Draw();
|
||||
pluginStateIpcTester.Draw();
|
||||
redrawingIpcTester.Draw();
|
||||
resolveIpcTester.Draw();
|
||||
resourceTreeIpcTester.Draw();
|
||||
uiIpcTester.Draw();
|
||||
temporaryIpcTester.Draw();
|
||||
temporaryIpcTester.DrawCollections();
|
||||
temporaryIpcTester.DrawMods();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error($"Error during IPC Tests:\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
internal static void DrawIntro(string label, string info)
|
||||
{
|
||||
ImGui.TableNextRow();
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(label);
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(info);
|
||||
ImGui.TableNextColumn();
|
||||
}
|
||||
|
||||
private void Subscribe()
|
||||
{
|
||||
if (_subscribed)
|
||||
return;
|
||||
|
||||
Penumbra.Log.Debug("[IPCTester] Subscribed to IPC events for IPC tester.");
|
||||
gameStateIpcTester.GameObjectResourcePathResolved.Enable();
|
||||
gameStateIpcTester.CharacterBaseCreated.Enable();
|
||||
gameStateIpcTester.CharacterBaseCreating.Enable();
|
||||
modSettingsIpcTester.SettingChanged.Enable();
|
||||
modsIpcTester.DeleteSubscriber.Enable();
|
||||
modsIpcTester.AddSubscriber.Enable();
|
||||
modsIpcTester.MoveSubscriber.Enable();
|
||||
pluginStateIpcTester.ModDirectoryChanged.Enable();
|
||||
pluginStateIpcTester.Initialized.Enable();
|
||||
pluginStateIpcTester.Disposed.Enable();
|
||||
pluginStateIpcTester.EnabledChange.Enable();
|
||||
redrawingIpcTester.Redrawn.Enable();
|
||||
uiIpcTester.PreSettingsTabBar.Enable();
|
||||
uiIpcTester.PreSettingsPanel.Enable();
|
||||
uiIpcTester.PostEnabled.Enable();
|
||||
uiIpcTester.PostSettingsPanelDraw.Enable();
|
||||
uiIpcTester.ChangedItemTooltip.Enable();
|
||||
uiIpcTester.ChangedItemClicked.Enable();
|
||||
|
||||
framework.Update += CheckUnsubscribe;
|
||||
_subscribed = true;
|
||||
}
|
||||
|
||||
private void CheckUnsubscribe(IFramework framework1)
|
||||
{
|
||||
if (_lastUpdate > framework.LastUpdateUTC)
|
||||
return;
|
||||
|
||||
Unsubscribe();
|
||||
framework.Update -= CheckUnsubscribe;
|
||||
}
|
||||
|
||||
private void Unsubscribe()
|
||||
{
|
||||
if (!_subscribed)
|
||||
return;
|
||||
|
||||
Penumbra.Log.Debug("[IPCTester] Unsubscribed from IPC events for IPC tester.");
|
||||
_subscribed = false;
|
||||
gameStateIpcTester.GameObjectResourcePathResolved.Disable();
|
||||
gameStateIpcTester.CharacterBaseCreated.Disable();
|
||||
gameStateIpcTester.CharacterBaseCreating.Disable();
|
||||
modSettingsIpcTester.SettingChanged.Disable();
|
||||
modsIpcTester.DeleteSubscriber.Disable();
|
||||
modsIpcTester.AddSubscriber.Disable();
|
||||
modsIpcTester.MoveSubscriber.Disable();
|
||||
pluginStateIpcTester.ModDirectoryChanged.Disable();
|
||||
pluginStateIpcTester.Initialized.Disable();
|
||||
pluginStateIpcTester.Disposed.Disable();
|
||||
pluginStateIpcTester.EnabledChange.Disable();
|
||||
redrawingIpcTester.Redrawn.Disable();
|
||||
uiIpcTester.PreSettingsTabBar.Disable();
|
||||
uiIpcTester.PreSettingsPanel.Disable();
|
||||
uiIpcTester.PostEnabled.Disable();
|
||||
uiIpcTester.PostSettingsPanelDraw.Disable();
|
||||
uiIpcTester.ChangedItemTooltip.Disable();
|
||||
uiIpcTester.ChangedItemClicked.Disable();
|
||||
}
|
||||
}
|
||||
38
Penumbra/Api/IpcTester/MetaIpcTester.cs
Normal file
38
Penumbra/Api/IpcTester/MetaIpcTester.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
using Dalamud.Plugin;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.IpcSubscribers;
|
||||
|
||||
namespace Penumbra.Api.IpcTester;
|
||||
|
||||
public class MetaIpcTester(DalamudPluginInterface pi) : IUiService
|
||||
{
|
||||
private int _gameObjectIndex;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var _ = ImRaii.TreeNode("Meta");
|
||||
if (!_)
|
||||
return;
|
||||
|
||||
ImGui.InputInt("##metaIdx", ref _gameObjectIndex, 0, 0);
|
||||
using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
IpcTester.DrawIntro(GetPlayerMetaManipulations.Label, "Player Meta Manipulations");
|
||||
if (ImGui.Button("Copy to Clipboard##Player"))
|
||||
{
|
||||
var base64 = new GetPlayerMetaManipulations(pi).Invoke();
|
||||
ImGui.SetClipboardText(base64);
|
||||
}
|
||||
|
||||
IpcTester.DrawIntro(GetMetaManipulations.Label, "Game Object Manipulations");
|
||||
if (ImGui.Button("Copy to Clipboard##GameObject"))
|
||||
{
|
||||
var base64 = new GetMetaManipulations(pi).Invoke(_gameObjectIndex);
|
||||
ImGui.SetClipboardText(base64);
|
||||
}
|
||||
}
|
||||
}
|
||||
181
Penumbra/Api/IpcTester/ModSettingsIpcTester.cs
Normal file
181
Penumbra/Api/IpcTester/ModSettingsIpcTester.cs
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
using Dalamud.Plugin;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.Api.IpcSubscribers;
|
||||
using Penumbra.UI;
|
||||
|
||||
namespace Penumbra.Api.IpcTester;
|
||||
|
||||
public class ModSettingsIpcTester : IUiService, IDisposable
|
||||
{
|
||||
private readonly DalamudPluginInterface _pi;
|
||||
public readonly EventSubscriber<ModSettingChange, Guid, string, bool> SettingChanged;
|
||||
|
||||
private PenumbraApiEc _lastSettingsError = PenumbraApiEc.Success;
|
||||
private ModSettingChange _lastSettingChangeType;
|
||||
private Guid _lastSettingChangeCollection = Guid.Empty;
|
||||
private string _lastSettingChangeMod = string.Empty;
|
||||
private bool _lastSettingChangeInherited;
|
||||
private DateTimeOffset _lastSettingChange;
|
||||
|
||||
private string _settingsModDirectory = string.Empty;
|
||||
private string _settingsModName = string.Empty;
|
||||
private Guid? _settingsCollection;
|
||||
private string _settingsCollectionName = string.Empty;
|
||||
private bool _settingsIgnoreInheritance;
|
||||
private bool _settingsInherit;
|
||||
private bool _settingsEnabled;
|
||||
private int _settingsPriority;
|
||||
private IReadOnlyDictionary<string, (string[], GroupType)>? _availableSettings;
|
||||
private Dictionary<string, List<string>>? _currentSettings;
|
||||
|
||||
public ModSettingsIpcTester(DalamudPluginInterface pi)
|
||||
{
|
||||
_pi = pi;
|
||||
SettingChanged = ModSettingChanged.Subscriber(pi, UpdateLastModSetting);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
SettingChanged.Dispose();
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var _ = ImRaii.TreeNode("Mod Settings");
|
||||
if (!_)
|
||||
return;
|
||||
|
||||
ImGui.InputTextWithHint("##settingsDir", "Mod Directory Name...", ref _settingsModDirectory, 100);
|
||||
ImGui.InputTextWithHint("##settingsName", "Mod Name...", ref _settingsModName, 100);
|
||||
ImGuiUtil.GuidInput("##settingsCollection", "Collection...", string.Empty, ref _settingsCollection, ref _settingsCollectionName);
|
||||
ImGui.Checkbox("Ignore Inheritance", ref _settingsIgnoreInheritance);
|
||||
var collection = _settingsCollection.GetValueOrDefault(Guid.Empty);
|
||||
|
||||
using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
IpcTester.DrawIntro("Last Error", _lastSettingsError.ToString());
|
||||
|
||||
IpcTester.DrawIntro(ModSettingChanged.Label, "Last Mod Setting Changed");
|
||||
ImGui.TextUnformatted(_lastSettingChangeMod.Length > 0
|
||||
? $"{_lastSettingChangeType} of {_lastSettingChangeMod} in {_lastSettingChangeCollection}{(_lastSettingChangeInherited ? " (Inherited)" : string.Empty)} at {_lastSettingChange}"
|
||||
: "None");
|
||||
|
||||
IpcTester.DrawIntro(GetAvailableModSettings.Label, "Get Available Settings");
|
||||
if (ImGui.Button("Get##Available"))
|
||||
{
|
||||
_availableSettings = new GetAvailableModSettings(_pi).Invoke(_settingsModDirectory, _settingsModName);
|
||||
_lastSettingsError = _availableSettings == null ? PenumbraApiEc.ModMissing : PenumbraApiEc.Success;
|
||||
}
|
||||
|
||||
IpcTester.DrawIntro(GetCurrentModSettings.Label, "Get Current Settings");
|
||||
if (ImGui.Button("Get##Current"))
|
||||
{
|
||||
var ret = new GetCurrentModSettings(_pi)
|
||||
.Invoke(collection, _settingsModDirectory, _settingsModName, _settingsIgnoreInheritance);
|
||||
_lastSettingsError = ret.Item1;
|
||||
if (ret.Item1 == PenumbraApiEc.Success)
|
||||
{
|
||||
_settingsEnabled = ret.Item2?.Item1 ?? false;
|
||||
_settingsInherit = ret.Item2?.Item4 ?? true;
|
||||
_settingsPriority = ret.Item2?.Item2 ?? 0;
|
||||
_currentSettings = ret.Item2?.Item3;
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentSettings = null;
|
||||
}
|
||||
}
|
||||
|
||||
IpcTester.DrawIntro(TryInheritMod.Label, "Inherit Mod");
|
||||
ImGui.Checkbox("##inherit", ref _settingsInherit);
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Set##Inherit"))
|
||||
_lastSettingsError = new TryInheritMod(_pi)
|
||||
.Invoke(collection, _settingsModDirectory, _settingsInherit, _settingsModName);
|
||||
|
||||
IpcTester.DrawIntro(TrySetMod.Label, "Set Enabled");
|
||||
ImGui.Checkbox("##enabled", ref _settingsEnabled);
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Set##Enabled"))
|
||||
_lastSettingsError = new TrySetMod(_pi)
|
||||
.Invoke(collection, _settingsModDirectory, _settingsEnabled, _settingsModName);
|
||||
|
||||
IpcTester.DrawIntro(TrySetModPriority.Label, "Set Priority");
|
||||
ImGui.SetNextItemWidth(200 * UiHelpers.Scale);
|
||||
ImGui.DragInt("##Priority", ref _settingsPriority);
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Set##Priority"))
|
||||
_lastSettingsError = new TrySetModPriority(_pi)
|
||||
.Invoke(collection, _settingsModDirectory, _settingsPriority, _settingsModName);
|
||||
|
||||
IpcTester.DrawIntro(CopyModSettings.Label, "Copy Mod Settings");
|
||||
if (ImGui.Button("Copy Settings"))
|
||||
_lastSettingsError = new CopyModSettings(_pi)
|
||||
.Invoke(_settingsCollection, _settingsModDirectory, _settingsModName);
|
||||
|
||||
ImGuiUtil.HoverTooltip("Copy settings from Mod Directory Name to Mod Name (as directory) in collection.");
|
||||
|
||||
IpcTester.DrawIntro(TrySetModSetting.Label, "Set Setting(s)");
|
||||
if (_availableSettings == null)
|
||||
return;
|
||||
|
||||
foreach (var (group, (list, type)) in _availableSettings)
|
||||
{
|
||||
using var id = ImRaii.PushId(group);
|
||||
var preview = list.Length > 0 ? list[0] : string.Empty;
|
||||
if (_currentSettings != null && _currentSettings.TryGetValue(group, out var current) && current.Count > 0)
|
||||
{
|
||||
preview = current[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
current = [];
|
||||
if (_currentSettings != null)
|
||||
_currentSettings[group] = current;
|
||||
}
|
||||
|
||||
ImGui.SetNextItemWidth(200 * UiHelpers.Scale);
|
||||
using (var c = ImRaii.Combo("##group", preview))
|
||||
{
|
||||
if (c)
|
||||
foreach (var s in list)
|
||||
{
|
||||
var contained = current.Contains(s);
|
||||
if (ImGui.Checkbox(s, ref contained))
|
||||
{
|
||||
if (contained)
|
||||
current.Add(s);
|
||||
else
|
||||
current.Remove(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Set##setting"))
|
||||
_lastSettingsError = type == GroupType.Single
|
||||
? new TrySetModSetting(_pi).Invoke(collection, _settingsModDirectory, group, current.Count > 0 ? current[0] : string.Empty,
|
||||
_settingsModName)
|
||||
: new TrySetModSettings(_pi).Invoke(collection, _settingsModDirectory, group, current.ToArray(), _settingsModName);
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(group);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLastModSetting(ModSettingChange type, Guid collection, string mod, bool inherited)
|
||||
{
|
||||
_lastSettingChangeType = type;
|
||||
_lastSettingChangeCollection = collection;
|
||||
_lastSettingChangeMod = mod;
|
||||
_lastSettingChangeInherited = inherited;
|
||||
_lastSettingChange = DateTimeOffset.Now;
|
||||
}
|
||||
}
|
||||
154
Penumbra/Api/IpcTester/ModsIpcTester.cs
Normal file
154
Penumbra/Api/IpcTester/ModsIpcTester.cs
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Plugin;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.Api.IpcSubscribers;
|
||||
|
||||
namespace Penumbra.Api.IpcTester;
|
||||
|
||||
public class ModsIpcTester : IUiService, IDisposable
|
||||
{
|
||||
private readonly DalamudPluginInterface _pi;
|
||||
|
||||
private string _modDirectory = string.Empty;
|
||||
private string _modName = string.Empty;
|
||||
private string _pathInput = string.Empty;
|
||||
private string _newInstallPath = string.Empty;
|
||||
private PenumbraApiEc _lastReloadEc;
|
||||
private PenumbraApiEc _lastAddEc;
|
||||
private PenumbraApiEc _lastDeleteEc;
|
||||
private PenumbraApiEc _lastSetPathEc;
|
||||
private PenumbraApiEc _lastInstallEc;
|
||||
private Dictionary<string, string> _mods = [];
|
||||
|
||||
public readonly EventSubscriber<string> DeleteSubscriber;
|
||||
public readonly EventSubscriber<string> AddSubscriber;
|
||||
public readonly EventSubscriber<string, string> MoveSubscriber;
|
||||
|
||||
private DateTimeOffset _lastDeletedModTime = DateTimeOffset.UnixEpoch;
|
||||
private string _lastDeletedMod = string.Empty;
|
||||
private DateTimeOffset _lastAddedModTime = DateTimeOffset.UnixEpoch;
|
||||
private string _lastAddedMod = string.Empty;
|
||||
private DateTimeOffset _lastMovedModTime = DateTimeOffset.UnixEpoch;
|
||||
private string _lastMovedModFrom = string.Empty;
|
||||
private string _lastMovedModTo = string.Empty;
|
||||
|
||||
public ModsIpcTester(DalamudPluginInterface pi)
|
||||
{
|
||||
_pi = pi;
|
||||
DeleteSubscriber = ModDeleted.Subscriber(pi, s =>
|
||||
{
|
||||
_lastDeletedModTime = DateTimeOffset.UtcNow;
|
||||
_lastDeletedMod = s;
|
||||
});
|
||||
AddSubscriber = ModAdded.Subscriber(pi, s =>
|
||||
{
|
||||
_lastAddedModTime = DateTimeOffset.UtcNow;
|
||||
_lastAddedMod = s;
|
||||
});
|
||||
MoveSubscriber = ModMoved.Subscriber(pi, (s1, s2) =>
|
||||
{
|
||||
_lastMovedModTime = DateTimeOffset.UtcNow;
|
||||
_lastMovedModFrom = s1;
|
||||
_lastMovedModTo = s2;
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DeleteSubscriber.Dispose();
|
||||
AddSubscriber.Dispose();
|
||||
MoveSubscriber.Dispose();
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var _ = ImRaii.TreeNode("Mods");
|
||||
if (!_)
|
||||
return;
|
||||
|
||||
ImGui.InputTextWithHint("##install", "Install File Path...", ref _newInstallPath, 100);
|
||||
ImGui.InputTextWithHint("##modDir", "Mod Directory Name...", ref _modDirectory, 100);
|
||||
ImGui.InputTextWithHint("##modName", "Mod Name...", ref _modName, 100);
|
||||
ImGui.InputTextWithHint("##path", "New Path...", ref _pathInput, 100);
|
||||
using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
IpcTester.DrawIntro(GetModList.Label, "Mods");
|
||||
DrawModsPopup();
|
||||
if (ImGui.Button("Get##Mods"))
|
||||
{
|
||||
_mods = new GetModList(_pi).Invoke();
|
||||
ImGui.OpenPopup("Mods");
|
||||
}
|
||||
|
||||
IpcTester.DrawIntro(ReloadMod.Label, "Reload Mod");
|
||||
if (ImGui.Button("Reload"))
|
||||
_lastReloadEc = new ReloadMod(_pi).Invoke(_modDirectory, _modName);
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(_lastReloadEc.ToString());
|
||||
|
||||
IpcTester.DrawIntro(InstallMod.Label, "Install Mod");
|
||||
if (ImGui.Button("Install"))
|
||||
_lastInstallEc = new InstallMod(_pi).Invoke(_newInstallPath);
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(_lastInstallEc.ToString());
|
||||
|
||||
IpcTester.DrawIntro(AddMod.Label, "Add Mod");
|
||||
if (ImGui.Button("Add"))
|
||||
_lastAddEc = new AddMod(_pi).Invoke(_modDirectory);
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(_lastAddEc.ToString());
|
||||
|
||||
IpcTester.DrawIntro(DeleteMod.Label, "Delete Mod");
|
||||
if (ImGui.Button("Delete"))
|
||||
_lastDeleteEc = new DeleteMod(_pi).Invoke(_modDirectory, _modName);
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(_lastDeleteEc.ToString());
|
||||
|
||||
IpcTester.DrawIntro(GetModPath.Label, "Current Path");
|
||||
var (ec, path, def, nameDef) = new GetModPath(_pi).Invoke(_modDirectory, _modName);
|
||||
ImGui.TextUnformatted($"{path} ({(def ? "Custom" : "Default")} Path, {(nameDef ? "Custom" : "Default")} Name) [{ec}]");
|
||||
|
||||
IpcTester.DrawIntro(SetModPath.Label, "Set Path");
|
||||
if (ImGui.Button("Set"))
|
||||
_lastSetPathEc = new SetModPath(_pi).Invoke(_modDirectory, _pathInput, _modName);
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(_lastSetPathEc.ToString());
|
||||
|
||||
IpcTester.DrawIntro(ModDeleted.Label, "Last Mod Deleted");
|
||||
if (_lastDeletedModTime > DateTimeOffset.UnixEpoch)
|
||||
ImGui.TextUnformatted($"{_lastDeletedMod} at {_lastDeletedModTime}");
|
||||
|
||||
IpcTester.DrawIntro(ModAdded.Label, "Last Mod Added");
|
||||
if (_lastAddedModTime > DateTimeOffset.UnixEpoch)
|
||||
ImGui.TextUnformatted($"{_lastAddedMod} at {_lastAddedModTime}");
|
||||
|
||||
IpcTester.DrawIntro(ModMoved.Label, "Last Mod Moved");
|
||||
if (_lastMovedModTime > DateTimeOffset.UnixEpoch)
|
||||
ImGui.TextUnformatted($"{_lastMovedModFrom} -> {_lastMovedModTo} at {_lastMovedModTime}");
|
||||
}
|
||||
|
||||
private void DrawModsPopup()
|
||||
{
|
||||
ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(500, 500));
|
||||
using var p = ImRaii.Popup("Mods");
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
foreach (var (modDir, modName) in _mods)
|
||||
ImGui.TextUnformatted($"{modDir}: {modName}");
|
||||
|
||||
if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused())
|
||||
ImGui.CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
132
Penumbra/Api/IpcTester/PluginStateIpcTester.cs
Normal file
132
Penumbra/Api/IpcTester/PluginStateIpcTester.cs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Plugin;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.Api.IpcSubscribers;
|
||||
|
||||
namespace Penumbra.Api.IpcTester;
|
||||
|
||||
public class PluginStateIpcTester : IUiService, IDisposable
|
||||
{
|
||||
private readonly DalamudPluginInterface _pi;
|
||||
public readonly EventSubscriber<string, bool> ModDirectoryChanged;
|
||||
public readonly EventSubscriber Initialized;
|
||||
public readonly EventSubscriber Disposed;
|
||||
public readonly EventSubscriber<bool> EnabledChange;
|
||||
|
||||
private string _currentConfiguration = string.Empty;
|
||||
private string _lastModDirectory = string.Empty;
|
||||
private bool _lastModDirectoryValid;
|
||||
private DateTimeOffset _lastModDirectoryTime = DateTimeOffset.MinValue;
|
||||
|
||||
private readonly List<DateTimeOffset> _initializedList = [];
|
||||
private readonly List<DateTimeOffset> _disposedList = [];
|
||||
|
||||
private DateTimeOffset _lastEnabledChange = DateTimeOffset.UnixEpoch;
|
||||
private bool? _lastEnabledValue;
|
||||
|
||||
public PluginStateIpcTester(DalamudPluginInterface pi)
|
||||
{
|
||||
_pi = pi;
|
||||
ModDirectoryChanged = IpcSubscribers.ModDirectoryChanged.Subscriber(pi, UpdateModDirectoryChanged);
|
||||
Initialized = IpcSubscribers.Initialized.Subscriber(pi, AddInitialized);
|
||||
Disposed = IpcSubscribers.Disposed.Subscriber(pi, AddDisposed);
|
||||
EnabledChange = IpcSubscribers.EnabledChange.Subscriber(pi, SetLastEnabled);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ModDirectoryChanged.Dispose();
|
||||
Initialized.Dispose();
|
||||
Disposed.Dispose();
|
||||
EnabledChange.Dispose();
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var _ = ImRaii.TreeNode("Plugin State");
|
||||
if (!_)
|
||||
return;
|
||||
|
||||
using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
DrawList(IpcSubscribers.Initialized.Label, "Last Initialized", _initializedList);
|
||||
DrawList(IpcSubscribers.Disposed.Label, "Last Disposed", _disposedList);
|
||||
|
||||
IpcTester.DrawIntro(ApiVersion.Label, "Current Version");
|
||||
var (breaking, features) = new ApiVersion(_pi).Invoke();
|
||||
ImGui.TextUnformatted($"{breaking}.{features:D4}");
|
||||
|
||||
IpcTester.DrawIntro(GetEnabledState.Label, "Current State");
|
||||
ImGui.TextUnformatted($"{new GetEnabledState(_pi).Invoke()}");
|
||||
|
||||
IpcTester.DrawIntro(IpcSubscribers.EnabledChange.Label, "Last Change");
|
||||
ImGui.TextUnformatted(_lastEnabledValue is { } v ? $"{_lastEnabledChange} (to {v})" : "Never");
|
||||
|
||||
DrawConfigPopup();
|
||||
IpcTester.DrawIntro(GetConfiguration.Label, "Configuration");
|
||||
if (ImGui.Button("Get"))
|
||||
{
|
||||
_currentConfiguration = new GetConfiguration(_pi).Invoke();
|
||||
ImGui.OpenPopup("Config Popup");
|
||||
}
|
||||
|
||||
IpcTester.DrawIntro(GetModDirectory.Label, "Current Mod Directory");
|
||||
ImGui.TextUnformatted(new GetModDirectory(_pi).Invoke());
|
||||
|
||||
IpcTester.DrawIntro(IpcSubscribers.ModDirectoryChanged.Label, "Last Mod Directory Change");
|
||||
ImGui.TextUnformatted(_lastModDirectoryTime > DateTimeOffset.MinValue
|
||||
? $"{_lastModDirectory} ({(_lastModDirectoryValid ? "Valid" : "Invalid")}) at {_lastModDirectoryTime}"
|
||||
: "None");
|
||||
|
||||
void DrawList(string label, string text, List<DateTimeOffset> list)
|
||||
{
|
||||
IpcTester.DrawIntro(label, text);
|
||||
if (list.Count == 0)
|
||||
{
|
||||
ImGui.TextUnformatted("Never");
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextUnformatted(list[^1].LocalDateTime.ToString(CultureInfo.CurrentCulture));
|
||||
if (list.Count > 1 && ImGui.IsItemHovered())
|
||||
ImGui.SetTooltip(string.Join("\n",
|
||||
list.SkipLast(1).Select(t => t.LocalDateTime.ToString(CultureInfo.CurrentCulture))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawConfigPopup()
|
||||
{
|
||||
ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(500, 500));
|
||||
using var popup = ImRaii.Popup("Config Popup");
|
||||
if (!popup)
|
||||
return;
|
||||
|
||||
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
||||
{
|
||||
ImGuiUtil.TextWrapped(_currentConfiguration);
|
||||
}
|
||||
|
||||
if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused())
|
||||
ImGui.CloseCurrentPopup();
|
||||
}
|
||||
|
||||
private void UpdateModDirectoryChanged(string path, bool valid)
|
||||
=> (_lastModDirectory, _lastModDirectoryValid, _lastModDirectoryTime) = (path, valid, DateTimeOffset.Now);
|
||||
|
||||
private void AddInitialized()
|
||||
=> _initializedList.Add(DateTimeOffset.UtcNow);
|
||||
|
||||
private void AddDisposed()
|
||||
=> _disposedList.Add(DateTimeOffset.UtcNow);
|
||||
|
||||
private void SetLastEnabled(bool val)
|
||||
=> (_lastEnabledChange, _lastEnabledValue) = (DateTimeOffset.Now, val);
|
||||
}
|
||||
72
Penumbra/Api/IpcTester/RedrawingIpcTester.cs
Normal file
72
Penumbra/Api/IpcTester/RedrawingIpcTester.cs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.Api.IpcSubscribers;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Penumbra.UI;
|
||||
|
||||
namespace Penumbra.Api.IpcTester;
|
||||
|
||||
public class RedrawingIpcTester : IUiService, IDisposable
|
||||
{
|
||||
private readonly DalamudPluginInterface _pi;
|
||||
private readonly ObjectManager _objects;
|
||||
public readonly EventSubscriber<nint, int> Redrawn;
|
||||
|
||||
private int _redrawIndex;
|
||||
private string _lastRedrawnString = "None";
|
||||
|
||||
public RedrawingIpcTester(DalamudPluginInterface pi, ObjectManager objects)
|
||||
{
|
||||
_pi = pi;
|
||||
_objects = objects;
|
||||
Redrawn = GameObjectRedrawn.Subscriber(_pi, SetLastRedrawn);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Redrawn.Dispose();
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var _ = ImRaii.TreeNode("Redrawing");
|
||||
if (!_)
|
||||
return;
|
||||
|
||||
using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
IpcTester.DrawIntro(RedrawObject.Label, "Redraw by Index");
|
||||
var tmp = _redrawIndex;
|
||||
ImGui.SetNextItemWidth(100 * UiHelpers.Scale);
|
||||
if (ImGui.DragInt("##redrawIndex", ref tmp, 0.1f, 0, _objects.TotalCount))
|
||||
_redrawIndex = Math.Clamp(tmp, 0, _objects.TotalCount);
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Redraw##Index"))
|
||||
new RedrawObject(_pi).Invoke(_redrawIndex);
|
||||
|
||||
IpcTester.DrawIntro(RedrawAll.Label, "Redraw All");
|
||||
if (ImGui.Button("Redraw##All"))
|
||||
new RedrawAll(_pi).Invoke();
|
||||
|
||||
IpcTester.DrawIntro(GameObjectRedrawn.Label, "Last Redrawn Object:");
|
||||
ImGui.TextUnformatted(_lastRedrawnString);
|
||||
}
|
||||
|
||||
private void SetLastRedrawn(nint address, int index)
|
||||
{
|
||||
if (index < 0
|
||||
|| index > _objects.TotalCount
|
||||
|| address == nint.Zero
|
||||
|| _objects[index].Address != address)
|
||||
_lastRedrawnString = "Invalid";
|
||||
|
||||
_lastRedrawnString = $"{_objects[index].Utf8Name} (0x{address:X}, {index})";
|
||||
}
|
||||
}
|
||||
114
Penumbra/Api/IpcTester/ResolveIpcTester.cs
Normal file
114
Penumbra/Api/IpcTester/ResolveIpcTester.cs
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
using Dalamud.Plugin;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.IpcSubscribers;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Api.IpcTester;
|
||||
|
||||
public class ResolveIpcTester(DalamudPluginInterface pi) : IUiService
|
||||
{
|
||||
private string _currentResolvePath = string.Empty;
|
||||
private string _currentReversePath = string.Empty;
|
||||
private int _currentReverseIdx;
|
||||
private Task<(string[], string[][])> _task = Task.FromResult<(string[], string[][])>(([], []));
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var tree = ImRaii.TreeNode("Resolving");
|
||||
if (!tree)
|
||||
return;
|
||||
|
||||
ImGui.InputTextWithHint("##resolvePath", "Resolve this game path...", ref _currentResolvePath, Utf8GamePath.MaxGamePathLength);
|
||||
ImGui.InputTextWithHint("##resolveInversePath", "Reverse-resolve this path...", ref _currentReversePath,
|
||||
Utf8GamePath.MaxGamePathLength);
|
||||
ImGui.InputInt("##resolveIdx", ref _currentReverseIdx, 0, 0);
|
||||
using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
IpcTester.DrawIntro(ResolveDefaultPath.Label, "Default Collection Resolve");
|
||||
if (_currentResolvePath.Length != 0)
|
||||
ImGui.TextUnformatted(new ResolveDefaultPath(pi).Invoke(_currentResolvePath));
|
||||
|
||||
IpcTester.DrawIntro(ResolveInterfacePath.Label, "Interface Collection Resolve");
|
||||
if (_currentResolvePath.Length != 0)
|
||||
ImGui.TextUnformatted(new ResolveInterfacePath(pi).Invoke(_currentResolvePath));
|
||||
|
||||
IpcTester.DrawIntro(ResolvePlayerPath.Label, "Player Collection Resolve");
|
||||
if (_currentResolvePath.Length != 0)
|
||||
ImGui.TextUnformatted(new ResolvePlayerPath(pi).Invoke(_currentResolvePath));
|
||||
|
||||
IpcTester.DrawIntro(ResolveGameObjectPath.Label, "Game Object Collection Resolve");
|
||||
if (_currentResolvePath.Length != 0)
|
||||
ImGui.TextUnformatted(new ResolveGameObjectPath(pi).Invoke(_currentResolvePath, _currentReverseIdx));
|
||||
|
||||
IpcTester.DrawIntro(ReverseResolvePlayerPath.Label, "Reversed Game Paths (Player)");
|
||||
if (_currentReversePath.Length > 0)
|
||||
{
|
||||
var list = new ReverseResolvePlayerPath(pi).Invoke(_currentReversePath);
|
||||
if (list.Length > 0)
|
||||
{
|
||||
ImGui.TextUnformatted(list[0]);
|
||||
if (list.Length > 1 && ImGui.IsItemHovered())
|
||||
ImGui.SetTooltip(string.Join("\n", list.Skip(1)));
|
||||
}
|
||||
}
|
||||
|
||||
IpcTester.DrawIntro(ReverseResolveGameObjectPath.Label, "Reversed Game Paths (Game Object)");
|
||||
if (_currentReversePath.Length > 0)
|
||||
{
|
||||
var list = new ReverseResolveGameObjectPath(pi).Invoke(_currentReversePath, _currentReverseIdx);
|
||||
if (list.Length > 0)
|
||||
{
|
||||
ImGui.TextUnformatted(list[0]);
|
||||
if (list.Length > 1 && ImGui.IsItemHovered())
|
||||
ImGui.SetTooltip(string.Join("\n", list.Skip(1)));
|
||||
}
|
||||
}
|
||||
|
||||
var forwardArray = _currentResolvePath.Length > 0
|
||||
? [_currentResolvePath]
|
||||
: Array.Empty<string>();
|
||||
var reverseArray = _currentReversePath.Length > 0
|
||||
? [_currentReversePath]
|
||||
: Array.Empty<string>();
|
||||
|
||||
IpcTester.DrawIntro(ResolvePlayerPaths.Label, "Resolved Paths (Player)");
|
||||
if (forwardArray.Length > 0 || reverseArray.Length > 0)
|
||||
{
|
||||
var ret = new ResolvePlayerPaths(pi).Invoke(forwardArray, reverseArray);
|
||||
ImGui.TextUnformatted(ConvertText(ret));
|
||||
}
|
||||
|
||||
IpcTester.DrawIntro(ResolvePlayerPathsAsync.Label, "Resolved Paths Async (Player)");
|
||||
if (ImGui.Button("Start"))
|
||||
_task = new ResolvePlayerPathsAsync(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));
|
||||
return;
|
||||
|
||||
static 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])}.";
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
349
Penumbra/Api/IpcTester/ResourceTreeIpcTester.cs
Normal file
349
Penumbra/Api/IpcTester/ResourceTreeIpcTester.cs
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Plugin;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.Api.IpcSubscribers;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Penumbra.Api.IpcTester;
|
||||
|
||||
public class ResourceTreeIpcTester(DalamudPluginInterface pi, ObjectManager objects) : IUiService
|
||||
{
|
||||
private readonly Stopwatch _stopwatch = new();
|
||||
|
||||
private string _gameObjectIndices = "0";
|
||||
private ResourceType _type = ResourceType.Mtrl;
|
||||
private bool _withUiData;
|
||||
|
||||
private (string, Dictionary<string, HashSet<string>>?)[]? _lastGameObjectResourcePaths;
|
||||
private (string, Dictionary<string, HashSet<string>>?)[]? _lastPlayerResourcePaths;
|
||||
private (string, IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>?)[]? _lastGameObjectResourcesOfType;
|
||||
private (string, IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>?)[]? _lastPlayerResourcesOfType;
|
||||
private (string, ResourceTreeDto?)[]? _lastGameObjectResourceTrees;
|
||||
private (string, ResourceTreeDto)[]? _lastPlayerResourceTrees;
|
||||
private TimeSpan _lastCallDuration;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var _ = ImRaii.TreeNode("Resource Tree");
|
||||
if (!_)
|
||||
return;
|
||||
|
||||
ImGui.InputText("GameObject indices", ref _gameObjectIndices, 511);
|
||||
ImGuiUtil.GenericEnumCombo("Resource type", ImGui.CalcItemWidth(), _type, out _type, Enum.GetValues<ResourceType>());
|
||||
ImGui.Checkbox("Also get names and icons", ref _withUiData);
|
||||
|
||||
using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
IpcTester.DrawIntro(GetGameObjectResourcePaths.Label, "Get GameObject resource paths");
|
||||
if (ImGui.Button("Get##GameObjectResourcePaths"))
|
||||
{
|
||||
var gameObjects = GetSelectedGameObjects();
|
||||
var subscriber = new GetGameObjectResourcePaths(pi);
|
||||
_stopwatch.Restart();
|
||||
var resourcePaths = subscriber.Invoke(gameObjects);
|
||||
|
||||
_lastCallDuration = _stopwatch.Elapsed;
|
||||
_lastGameObjectResourcePaths = gameObjects
|
||||
.Select(i => GameObjectToString(i))
|
||||
.Zip(resourcePaths)
|
||||
.ToArray();
|
||||
|
||||
ImGui.OpenPopup(nameof(GetGameObjectResourcePaths));
|
||||
}
|
||||
|
||||
IpcTester.DrawIntro(GetPlayerResourcePaths.Label, "Get local player resource paths");
|
||||
if (ImGui.Button("Get##PlayerResourcePaths"))
|
||||
{
|
||||
var subscriber = new GetPlayerResourcePaths(pi);
|
||||
_stopwatch.Restart();
|
||||
var resourcePaths = subscriber.Invoke();
|
||||
|
||||
_lastCallDuration = _stopwatch.Elapsed;
|
||||
_lastPlayerResourcePaths = resourcePaths
|
||||
.Select(pair => (GameObjectToString(pair.Key), pair.Value))
|
||||
.ToArray()!;
|
||||
|
||||
ImGui.OpenPopup(nameof(GetPlayerResourcePaths));
|
||||
}
|
||||
|
||||
IpcTester.DrawIntro(GetGameObjectResourcesOfType.Label, "Get GameObject resources of type");
|
||||
if (ImGui.Button("Get##GameObjectResourcesOfType"))
|
||||
{
|
||||
var gameObjects = GetSelectedGameObjects();
|
||||
var subscriber = new GetGameObjectResourcesOfType(pi);
|
||||
_stopwatch.Restart();
|
||||
var resourcesOfType = subscriber.Invoke(_type, _withUiData, gameObjects);
|
||||
|
||||
_lastCallDuration = _stopwatch.Elapsed;
|
||||
_lastGameObjectResourcesOfType = gameObjects
|
||||
.Select(i => GameObjectToString(i))
|
||||
.Zip(resourcesOfType)
|
||||
.ToArray();
|
||||
|
||||
ImGui.OpenPopup(nameof(GetGameObjectResourcesOfType));
|
||||
}
|
||||
|
||||
IpcTester.DrawIntro(GetPlayerResourcesOfType.Label, "Get local player resources of type");
|
||||
if (ImGui.Button("Get##PlayerResourcesOfType"))
|
||||
{
|
||||
var subscriber = new GetPlayerResourcesOfType(pi);
|
||||
_stopwatch.Restart();
|
||||
var resourcesOfType = subscriber.Invoke(_type, _withUiData);
|
||||
|
||||
_lastCallDuration = _stopwatch.Elapsed;
|
||||
_lastPlayerResourcesOfType = resourcesOfType
|
||||
.Select(pair => (GameObjectToString(pair.Key), (IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>?)pair.Value))
|
||||
.ToArray();
|
||||
|
||||
ImGui.OpenPopup(nameof(GetPlayerResourcesOfType));
|
||||
}
|
||||
|
||||
IpcTester.DrawIntro(GetGameObjectResourceTrees.Label, "Get GameObject resource trees");
|
||||
if (ImGui.Button("Get##GameObjectResourceTrees"))
|
||||
{
|
||||
var gameObjects = GetSelectedGameObjects();
|
||||
var subscriber = new GetGameObjectResourceTrees(pi);
|
||||
_stopwatch.Restart();
|
||||
var trees = subscriber.Invoke(_withUiData, gameObjects);
|
||||
|
||||
_lastCallDuration = _stopwatch.Elapsed;
|
||||
_lastGameObjectResourceTrees = gameObjects
|
||||
.Select(i => GameObjectToString(i))
|
||||
.Zip(trees)
|
||||
.ToArray();
|
||||
|
||||
ImGui.OpenPopup(nameof(GetGameObjectResourceTrees));
|
||||
}
|
||||
|
||||
IpcTester.DrawIntro(GetPlayerResourceTrees.Label, "Get local player resource trees");
|
||||
if (ImGui.Button("Get##PlayerResourceTrees"))
|
||||
{
|
||||
var subscriber = new GetPlayerResourceTrees(pi);
|
||||
_stopwatch.Restart();
|
||||
var trees = subscriber.Invoke(_withUiData);
|
||||
|
||||
_lastCallDuration = _stopwatch.Elapsed;
|
||||
_lastPlayerResourceTrees = trees
|
||||
.Select(pair => (GameObjectToString(pair.Key), pair.Value))
|
||||
.ToArray();
|
||||
|
||||
ImGui.OpenPopup(nameof(GetPlayerResourceTrees));
|
||||
}
|
||||
|
||||
DrawPopup(nameof(GetGameObjectResourcePaths), ref _lastGameObjectResourcePaths, DrawResourcePaths,
|
||||
_lastCallDuration);
|
||||
DrawPopup(nameof(GetPlayerResourcePaths), ref _lastPlayerResourcePaths!, DrawResourcePaths, _lastCallDuration);
|
||||
|
||||
DrawPopup(nameof(GetGameObjectResourcesOfType), ref _lastGameObjectResourcesOfType, DrawResourcesOfType,
|
||||
_lastCallDuration);
|
||||
DrawPopup(nameof(GetPlayerResourcesOfType), ref _lastPlayerResourcesOfType, DrawResourcesOfType,
|
||||
_lastCallDuration);
|
||||
|
||||
DrawPopup(nameof(GetGameObjectResourceTrees), ref _lastGameObjectResourceTrees, DrawResourceTrees,
|
||||
_lastCallDuration);
|
||||
DrawPopup(nameof(GetPlayerResourceTrees), ref _lastPlayerResourceTrees, DrawResourceTrees!, _lastCallDuration);
|
||||
}
|
||||
|
||||
private static void DrawPopup<T>(string popupId, ref T? result, Action<T> drawResult, TimeSpan duration) where T : class
|
||||
{
|
||||
ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(1000, 500));
|
||||
using var popup = ImRaii.Popup(popupId);
|
||||
if (!popup)
|
||||
{
|
||||
result = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
drawResult(result);
|
||||
|
||||
ImGui.TextUnformatted($"Invoked in {duration.TotalMilliseconds} ms");
|
||||
|
||||
if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused())
|
||||
{
|
||||
result = null;
|
||||
ImGui.CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawWithHeaders<T>((string, T?)[] result, Action<T> drawItem) where T : class
|
||||
{
|
||||
var firstSeen = new Dictionary<T, string>();
|
||||
foreach (var (label, item) in result)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
ImRaii.TreeNode($"{label}: null", ImGuiTreeNodeFlags.Leaf).Dispose();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (firstSeen.TryGetValue(item, out var firstLabel))
|
||||
{
|
||||
ImRaii.TreeNode($"{label}: same as {firstLabel}", ImGuiTreeNodeFlags.Leaf).Dispose();
|
||||
continue;
|
||||
}
|
||||
|
||||
firstSeen.Add(item, label);
|
||||
|
||||
using var header = ImRaii.TreeNode(label);
|
||||
if (!header)
|
||||
continue;
|
||||
|
||||
drawItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawResourcePaths((string, Dictionary<string, HashSet<string>>?)[] result)
|
||||
{
|
||||
DrawWithHeaders(result, paths =>
|
||||
{
|
||||
using var table = ImRaii.Table(string.Empty, 2, ImGuiTableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
ImGui.TableSetupColumn("Actual Path", ImGuiTableColumnFlags.WidthStretch, 0.6f);
|
||||
ImGui.TableSetupColumn("Game Paths", ImGuiTableColumnFlags.WidthStretch, 0.4f);
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
foreach (var (actualPath, gamePaths) in paths)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(actualPath);
|
||||
ImGui.TableNextColumn();
|
||||
foreach (var gamePath in gamePaths)
|
||||
ImGui.TextUnformatted(gamePath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void DrawResourcesOfType((string, IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>?)[] result)
|
||||
{
|
||||
DrawWithHeaders(result, resources =>
|
||||
{
|
||||
using var table = ImRaii.Table(string.Empty, _withUiData ? 3 : 2, ImGuiTableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
ImGui.TableSetupColumn("Resource Handle", ImGuiTableColumnFlags.WidthStretch, 0.15f);
|
||||
ImGui.TableSetupColumn("Actual Path", ImGuiTableColumnFlags.WidthStretch, _withUiData ? 0.55f : 0.85f);
|
||||
if (_withUiData)
|
||||
ImGui.TableSetupColumn("Icon & Name", ImGuiTableColumnFlags.WidthStretch, 0.3f);
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
foreach (var (resourceHandle, (actualPath, name, icon)) in resources)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
TextUnformattedMono($"0x{resourceHandle:X}");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(actualPath);
|
||||
if (_withUiData)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
TextUnformattedMono(icon.ToString());
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void DrawResourceTrees((string, ResourceTreeDto?)[] 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(ResourceNodeDto 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)
|
||||
{
|
||||
using var _ = ImRaii.PushFont(UiBuilder.MonoFont);
|
||||
ImGui.TextUnformatted(text);
|
||||
}
|
||||
|
||||
private ushort[] GetSelectedGameObjects()
|
||||
=> _gameObjectIndices.Split(',')
|
||||
.SelectWhere(index => (ushort.TryParse(index.Trim(), out var i), i))
|
||||
.ToArray();
|
||||
|
||||
private unsafe string GameObjectToString(ObjectIndex gameObjectIndex)
|
||||
{
|
||||
var gameObject = objects[gameObjectIndex];
|
||||
|
||||
return gameObject.Valid
|
||||
? $"[{gameObjectIndex}] {gameObject.Utf8Name} ({(ObjectKind)gameObject.AsObject->ObjectKind})"
|
||||
: $"[{gameObjectIndex}] null";
|
||||
}
|
||||
}
|
||||
203
Penumbra/Api/IpcTester/TemporaryIpcTester.cs
Normal file
203
Penumbra/Api/IpcTester/TemporaryIpcTester.cs
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Plugin;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Api.IpcSubscribers;
|
||||
using Penumbra.Collections.Manager;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Services;
|
||||
|
||||
namespace Penumbra.Api.IpcTester;
|
||||
|
||||
public class TemporaryIpcTester(
|
||||
DalamudPluginInterface pi,
|
||||
ModManager modManager,
|
||||
CollectionManager collections,
|
||||
TempModManager tempMods,
|
||||
TempCollectionManager tempCollections,
|
||||
SaveService saveService,
|
||||
Configuration config)
|
||||
: IUiService
|
||||
{
|
||||
public Guid LastCreatedCollectionId = Guid.Empty;
|
||||
|
||||
private Guid? _tempGuid;
|
||||
private string _tempCollectionName = string.Empty;
|
||||
private string _tempCollectionGuidName = string.Empty;
|
||||
private string _tempModName = string.Empty;
|
||||
private string _tempGamePath = "test/game/path.mtrl";
|
||||
private string _tempFilePath = "test/success.mtrl";
|
||||
private string _tempManipulation = string.Empty;
|
||||
private PenumbraApiEc _lastTempError;
|
||||
private int _tempActorIndex;
|
||||
private bool _forceOverwrite;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var _ = ImRaii.TreeNode("Temporary");
|
||||
if (!_)
|
||||
return;
|
||||
|
||||
ImGui.InputTextWithHint("##tempCollection", "Collection Name...", ref _tempCollectionName, 128);
|
||||
ImGuiUtil.GuidInput("##guid", "Collection GUID...", string.Empty, ref _tempGuid, ref _tempCollectionGuidName);
|
||||
ImGui.InputInt("##tempActorIndex", ref _tempActorIndex, 0, 0);
|
||||
ImGui.InputTextWithHint("##tempMod", "Temporary Mod Name...", ref _tempModName, 32);
|
||||
ImGui.InputTextWithHint("##tempGame", "Game Path...", ref _tempGamePath, 256);
|
||||
ImGui.InputTextWithHint("##tempFile", "File Path...", ref _tempFilePath, 256);
|
||||
ImGui.InputTextWithHint("##tempManip", "Manipulation Base64 String...", ref _tempManipulation, 256);
|
||||
ImGui.Checkbox("Force Character Collection Overwrite", ref _forceOverwrite);
|
||||
|
||||
using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
IpcTester.DrawIntro("Last Error", _lastTempError.ToString());
|
||||
ImGuiUtil.DrawTableColumn("Last Created Collection");
|
||||
ImGui.TableNextColumn();
|
||||
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
||||
{
|
||||
ImGuiUtil.CopyOnClickSelectable(LastCreatedCollectionId.ToString());
|
||||
}
|
||||
|
||||
IpcTester.DrawIntro(CreateTemporaryCollection.Label, "Create Temporary Collection");
|
||||
if (ImGui.Button("Create##Collection"))
|
||||
{
|
||||
LastCreatedCollectionId = new CreateTemporaryCollection(pi).Invoke(_tempCollectionName);
|
||||
if (_tempGuid == null)
|
||||
{
|
||||
_tempGuid = LastCreatedCollectionId;
|
||||
_tempCollectionGuidName = LastCreatedCollectionId.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
var guid = _tempGuid.GetValueOrDefault(Guid.Empty);
|
||||
|
||||
IpcTester.DrawIntro(DeleteTemporaryCollection.Label, "Delete Temporary Collection");
|
||||
if (ImGui.Button("Delete##Collection"))
|
||||
_lastTempError = new DeleteTemporaryCollection(pi).Invoke(guid);
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Delete Last##Collection"))
|
||||
_lastTempError = new DeleteTemporaryCollection(pi).Invoke(LastCreatedCollectionId);
|
||||
|
||||
IpcTester.DrawIntro(AssignTemporaryCollection.Label, "Assign Temporary Collection");
|
||||
if (ImGui.Button("Assign##NamedCollection"))
|
||||
_lastTempError = new AssignTemporaryCollection(pi).Invoke(guid, _tempActorIndex, _forceOverwrite);
|
||||
|
||||
IpcTester.DrawIntro(AddTemporaryMod.Label, "Add Temporary Mod to specific Collection");
|
||||
if (ImGui.Button("Add##Mod"))
|
||||
_lastTempError = new AddTemporaryMod(pi).Invoke(_tempModName, guid,
|
||||
new Dictionary<string, string> { { _tempGamePath, _tempFilePath } },
|
||||
_tempManipulation.Length > 0 ? _tempManipulation : string.Empty, int.MaxValue);
|
||||
|
||||
IpcTester.DrawIntro(CreateTemporaryCollection.Label, "Copy Existing Collection");
|
||||
if (ImGuiUtil.DrawDisabledButton("Copy##Collection", Vector2.Zero,
|
||||
"Copies the effective list from the collection named in Temporary Mod Name...",
|
||||
!collections.Storage.ByName(_tempModName, out var copyCollection))
|
||||
&& copyCollection is { HasCache: true })
|
||||
{
|
||||
var files = copyCollection.ResolvedFiles.ToDictionary(kvp => kvp.Key.ToString(), kvp => kvp.Value.Path.ToString());
|
||||
var manips = Functions.ToCompressedBase64(copyCollection.MetaCache?.Manipulations.ToArray() ?? Array.Empty<MetaManipulation>(),
|
||||
MetaManipulation.CurrentVersion);
|
||||
_lastTempError = new AddTemporaryMod(pi).Invoke(_tempModName, guid, files, manips, 999);
|
||||
}
|
||||
|
||||
IpcTester.DrawIntro(AddTemporaryModAll.Label, "Add Temporary Mod to all Collections");
|
||||
if (ImGui.Button("Add##All"))
|
||||
_lastTempError = new AddTemporaryModAll(pi).Invoke(_tempModName,
|
||||
new Dictionary<string, string> { { _tempGamePath, _tempFilePath } },
|
||||
_tempManipulation.Length > 0 ? _tempManipulation : string.Empty, int.MaxValue);
|
||||
|
||||
IpcTester.DrawIntro(RemoveTemporaryMod.Label, "Remove Temporary Mod from specific Collection");
|
||||
if (ImGui.Button("Remove##Mod"))
|
||||
_lastTempError = new RemoveTemporaryMod(pi).Invoke(_tempModName, guid, int.MaxValue);
|
||||
|
||||
IpcTester.DrawIntro(RemoveTemporaryModAll.Label, "Remove Temporary Mod from all Collections");
|
||||
if (ImGui.Button("Remove##ModAll"))
|
||||
_lastTempError = new RemoveTemporaryModAll(pi).Invoke(_tempModName, int.MaxValue);
|
||||
}
|
||||
|
||||
public void DrawCollections()
|
||||
{
|
||||
using var collTree = ImRaii.TreeNode("Temporary Collections##TempCollections");
|
||||
if (!collTree)
|
||||
return;
|
||||
|
||||
using var table = ImRaii.Table("##collTree", 6, ImGuiTableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
foreach (var (collection, idx) in tempCollections.Values.WithIndex())
|
||||
{
|
||||
using var id = ImRaii.PushId(idx);
|
||||
ImGui.TableNextColumn();
|
||||
var character = tempCollections.Collections.Where(p => p.Collection == collection).Select(p => p.DisplayName)
|
||||
.FirstOrDefault()
|
||||
?? "Unknown";
|
||||
if (ImGui.Button("Save##Collection"))
|
||||
TemporaryMod.SaveTempCollection(config, saveService, modManager, collection, character);
|
||||
|
||||
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
ImGuiUtil.CopyOnClickSelectable(collection.Identifier);
|
||||
}
|
||||
|
||||
ImGuiUtil.DrawTableColumn(collection.Name);
|
||||
ImGuiUtil.DrawTableColumn(collection.ResolvedFiles.Count.ToString());
|
||||
ImGuiUtil.DrawTableColumn(collection.MetaCache?.Count.ToString() ?? "0");
|
||||
ImGuiUtil.DrawTableColumn(string.Join(", ",
|
||||
tempCollections.Collections.Where(p => p.Collection == collection).Select(c => c.DisplayName)));
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawMods()
|
||||
{
|
||||
using var modTree = ImRaii.TreeNode("Temporary Mods##TempMods");
|
||||
if (!modTree)
|
||||
return;
|
||||
|
||||
using var table = ImRaii.Table("##modTree", 5, ImGuiTableFlags.SizingFixedFit);
|
||||
|
||||
void PrintList(string collectionName, IReadOnlyList<TemporaryMod> list)
|
||||
{
|
||||
foreach (var mod in list)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(mod.Name);
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(mod.Priority.ToString());
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(collectionName);
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(mod.Default.Files.Count.ToString());
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
using var tt = ImRaii.Tooltip();
|
||||
foreach (var (path, file) in mod.Default.Files)
|
||||
ImGui.TextUnformatted($"{path} -> {file}");
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(mod.TotalManipulations.ToString());
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
using var tt = ImRaii.Tooltip();
|
||||
foreach (var manip in mod.Default.Manipulations)
|
||||
ImGui.TextUnformatted(manip.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (table)
|
||||
{
|
||||
PrintList("All", tempMods.ModsForAllCollections);
|
||||
foreach (var (collection, list) in tempMods.Mods)
|
||||
PrintList(collection.Name, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
128
Penumbra/Api/IpcTester/UiIpcTester.cs
Normal file
128
Penumbra/Api/IpcTester/UiIpcTester.cs
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
using Dalamud.Plugin;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.Api.IpcSubscribers;
|
||||
using Penumbra.Communication;
|
||||
|
||||
namespace Penumbra.Api.IpcTester;
|
||||
|
||||
public class UiIpcTester : IUiService, IDisposable
|
||||
{
|
||||
private readonly DalamudPluginInterface _pi;
|
||||
public readonly EventSubscriber<string, float, float> PreSettingsTabBar;
|
||||
public readonly EventSubscriber<string> PreSettingsPanel;
|
||||
public readonly EventSubscriber<string> PostEnabled;
|
||||
public readonly EventSubscriber<string> PostSettingsPanelDraw;
|
||||
public readonly EventSubscriber<ChangedItemType, uint> ChangedItemTooltip;
|
||||
public readonly EventSubscriber<MouseButton, ChangedItemType, uint> ChangedItemClicked;
|
||||
|
||||
private string _lastDrawnMod = string.Empty;
|
||||
private DateTimeOffset _lastDrawnModTime = DateTimeOffset.MinValue;
|
||||
private bool _subscribedToTooltip;
|
||||
private bool _subscribedToClick;
|
||||
private string _lastClicked = string.Empty;
|
||||
private string _lastHovered = string.Empty;
|
||||
private TabType _selectTab = TabType.None;
|
||||
private string _modName = string.Empty;
|
||||
private PenumbraApiEc _ec = PenumbraApiEc.Success;
|
||||
|
||||
public UiIpcTester(DalamudPluginInterface pi)
|
||||
{
|
||||
_pi = pi;
|
||||
PreSettingsTabBar = IpcSubscribers.PreSettingsTabBarDraw.Subscriber(pi, UpdateLastDrawnMod);
|
||||
PreSettingsPanel = IpcSubscribers.PreSettingsPanelDraw.Subscriber(pi, UpdateLastDrawnMod);
|
||||
PostEnabled = IpcSubscribers.PostEnabledDraw.Subscriber(pi, UpdateLastDrawnMod);
|
||||
PostSettingsPanelDraw = IpcSubscribers.PostSettingsPanelDraw.Subscriber(pi, UpdateLastDrawnMod);
|
||||
ChangedItemTooltip = IpcSubscribers.ChangedItemTooltip.Subscriber(pi, AddedTooltip);
|
||||
ChangedItemClicked = IpcSubscribers.ChangedItemClicked.Subscriber(pi, AddedClick);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
PreSettingsTabBar.Dispose();
|
||||
PreSettingsPanel.Dispose();
|
||||
PostEnabled.Dispose();
|
||||
PostSettingsPanelDraw.Dispose();
|
||||
ChangedItemTooltip.Dispose();
|
||||
ChangedItemClicked.Dispose();
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var _ = ImRaii.TreeNode("UI");
|
||||
if (!_)
|
||||
return;
|
||||
|
||||
using (var combo = ImRaii.Combo("Tab to Open at", _selectTab.ToString()))
|
||||
{
|
||||
if (combo)
|
||||
foreach (var val in Enum.GetValues<TabType>())
|
||||
{
|
||||
if (ImGui.Selectable(val.ToString(), _selectTab == val))
|
||||
_selectTab = val;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.InputTextWithHint("##openMod", "Mod to Open at...", ref _modName, 256);
|
||||
using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
IpcTester.DrawIntro(IpcSubscribers.PostSettingsPanelDraw.Label, "Last Drawn Mod");
|
||||
ImGui.TextUnformatted(_lastDrawnMod.Length > 0 ? $"{_lastDrawnMod} at {_lastDrawnModTime}" : "None");
|
||||
|
||||
IpcTester.DrawIntro(IpcSubscribers.ChangedItemTooltip.Label, "Add Tooltip");
|
||||
if (ImGui.Checkbox("##tooltip", ref _subscribedToTooltip))
|
||||
{
|
||||
if (_subscribedToTooltip)
|
||||
ChangedItemTooltip.Enable();
|
||||
else
|
||||
ChangedItemTooltip.Disable();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(_lastHovered);
|
||||
|
||||
IpcTester.DrawIntro(IpcSubscribers.ChangedItemClicked.Label, "Subscribe Click");
|
||||
if (ImGui.Checkbox("##click", ref _subscribedToClick))
|
||||
{
|
||||
if (_subscribedToClick)
|
||||
ChangedItemClicked.Enable();
|
||||
else
|
||||
ChangedItemClicked.Disable();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(_lastClicked);
|
||||
IpcTester.DrawIntro(OpenMainWindow.Label, "Open Mod Window");
|
||||
if (ImGui.Button("Open##window"))
|
||||
_ec = new OpenMainWindow(_pi).Invoke(_selectTab, _modName, _modName);
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(_ec.ToString());
|
||||
|
||||
IpcTester.DrawIntro(CloseMainWindow.Label, "Close Mod Window");
|
||||
if (ImGui.Button("Close##window"))
|
||||
new CloseMainWindow(_pi).Invoke();
|
||||
}
|
||||
|
||||
private void UpdateLastDrawnMod(string name)
|
||||
=> (_lastDrawnMod, _lastDrawnModTime) = (name, DateTimeOffset.Now);
|
||||
|
||||
private void UpdateLastDrawnMod(string name, float _1, float _2)
|
||||
=> (_lastDrawnMod, _lastDrawnModTime) = (name, DateTimeOffset.Now);
|
||||
|
||||
private void AddedTooltip(ChangedItemType type, uint id)
|
||||
{
|
||||
_lastHovered = $"{type} {id} at {DateTime.UtcNow.ToLocalTime().ToString(CultureInfo.CurrentCulture)}";
|
||||
ImGui.TextUnformatted("IPC Test Successful");
|
||||
}
|
||||
|
||||
private void AddedClick(MouseButton button, ChangedItemType type, uint id)
|
||||
{
|
||||
_lastClicked = $"{button}-click on {type} {id} at {DateTime.UtcNow.ToLocalTime().ToString(CultureInfo.CurrentCulture)}";
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,435 +0,0 @@
|
|||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Plugin;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.Collections.Manager;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Api;
|
||||
|
||||
using CurrentSettings = ValueTuple<PenumbraApiEc, (bool, int, IDictionary<string, IList<string>>, bool)?>;
|
||||
|
||||
public class PenumbraIpcProviders : IDisposable
|
||||
{
|
||||
internal readonly IPenumbraApi Api;
|
||||
|
||||
// Plugin State
|
||||
internal readonly EventProvider Initialized;
|
||||
internal readonly EventProvider Disposed;
|
||||
internal readonly FuncProvider<int> ApiVersion;
|
||||
internal readonly FuncProvider<(int Breaking, int Features)> ApiVersions;
|
||||
internal readonly FuncProvider<bool> GetEnabledState;
|
||||
internal readonly EventProvider<bool> EnabledChange;
|
||||
|
||||
// Configuration
|
||||
internal readonly FuncProvider<string> GetModDirectory;
|
||||
internal readonly FuncProvider<string> GetConfiguration;
|
||||
internal readonly EventProvider<string, bool> ModDirectoryChanged;
|
||||
|
||||
// UI
|
||||
internal readonly EventProvider<string, float, float> PreSettingsTabBarDraw;
|
||||
internal readonly EventProvider<string> PreSettingsDraw;
|
||||
internal readonly EventProvider<string> PostEnabledDraw;
|
||||
internal readonly EventProvider<string> PostSettingsDraw;
|
||||
internal readonly EventProvider<ChangedItemType, uint> ChangedItemTooltip;
|
||||
internal readonly EventProvider<MouseButton, ChangedItemType, uint> ChangedItemClick;
|
||||
internal readonly FuncProvider<TabType, string, string, PenumbraApiEc> OpenMainWindow;
|
||||
internal readonly ActionProvider CloseMainWindow;
|
||||
|
||||
// Redrawing
|
||||
internal readonly ActionProvider<RedrawType> RedrawAll;
|
||||
internal readonly ActionProvider<GameObject, RedrawType> RedrawObject;
|
||||
internal readonly ActionProvider<int, RedrawType> RedrawObjectByIndex;
|
||||
internal readonly ActionProvider<string, RedrawType> RedrawObjectByName;
|
||||
internal readonly EventProvider<nint, int> GameObjectRedrawn;
|
||||
|
||||
// Game State
|
||||
internal readonly FuncProvider<nint, (nint, string)> GetDrawObjectInfo;
|
||||
internal readonly FuncProvider<int, int> GetCutsceneParentIndex;
|
||||
internal readonly FuncProvider<int, int, PenumbraApiEc> SetCutsceneParentIndex;
|
||||
internal readonly EventProvider<nint, string, nint, nint, nint> CreatingCharacterBase;
|
||||
internal readonly EventProvider<nint, string, nint> CreatedCharacterBase;
|
||||
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[], (string[], string[][])> ResolvePlayerPaths;
|
||||
internal readonly FuncProvider<string[], string[], Task<(string[], string[][])>> ResolvePlayerPathsAsync;
|
||||
|
||||
// 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<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;
|
||||
|
||||
// Meta
|
||||
internal readonly FuncProvider<string> GetPlayerMetaManipulations;
|
||||
internal readonly FuncProvider<string, string> GetMetaManipulations;
|
||||
internal readonly FuncProvider<int, string> GetGameObjectMetaManipulations;
|
||||
|
||||
// Mods
|
||||
internal readonly FuncProvider<IList<(string, string)>> GetMods;
|
||||
internal readonly FuncProvider<string, string, PenumbraApiEc> ReloadMod;
|
||||
internal readonly FuncProvider<string, PenumbraApiEc> InstallMod;
|
||||
internal readonly FuncProvider<string, PenumbraApiEc> AddMod;
|
||||
internal readonly FuncProvider<string, string, PenumbraApiEc> DeleteMod;
|
||||
internal readonly FuncProvider<string, string, (PenumbraApiEc, string, bool)> GetModPath;
|
||||
internal readonly FuncProvider<string, string, string, PenumbraApiEc> SetModPath;
|
||||
internal readonly EventProvider<string> ModDeleted;
|
||||
internal readonly EventProvider<string> ModAdded;
|
||||
internal readonly EventProvider<string, string> ModMoved;
|
||||
|
||||
// ModSettings
|
||||
internal readonly FuncProvider<string, string, IDictionary<string, (IList<string>, GroupType)>?> GetAvailableModSettings;
|
||||
internal readonly FuncProvider<string, string, string, bool, CurrentSettings> GetCurrentModSettings;
|
||||
internal readonly FuncProvider<string, string, string, bool, PenumbraApiEc> TryInheritMod;
|
||||
internal readonly FuncProvider<string, string, string, bool, PenumbraApiEc> TrySetMod;
|
||||
internal readonly FuncProvider<string, string, string, int, PenumbraApiEc> TrySetModPriority;
|
||||
internal readonly FuncProvider<string, string, string, string, string, PenumbraApiEc> TrySetModSetting;
|
||||
internal readonly FuncProvider<string, string, string, string, IReadOnlyList<string>, PenumbraApiEc> TrySetModSettings;
|
||||
internal readonly EventProvider<ModSettingChange, string, string, bool> ModSettingChanged;
|
||||
internal readonly FuncProvider<string, string, string, PenumbraApiEc> CopyModSettings;
|
||||
|
||||
// Editing
|
||||
internal readonly FuncProvider<string, string, TextureType, bool, Task> ConvertTextureFile;
|
||||
internal readonly FuncProvider<byte[], int, string, TextureType, bool, Task> ConvertTextureData;
|
||||
|
||||
// Temporary
|
||||
internal readonly FuncProvider<string, string, bool, (PenumbraApiEc, string)> CreateTemporaryCollection;
|
||||
internal readonly FuncProvider<string, PenumbraApiEc> RemoveTemporaryCollection;
|
||||
internal readonly FuncProvider<string, PenumbraApiEc> CreateNamedTemporaryCollection;
|
||||
internal readonly FuncProvider<string, PenumbraApiEc> RemoveTemporaryCollectionByName;
|
||||
internal readonly FuncProvider<string, int, bool, PenumbraApiEc> AssignTemporaryCollection;
|
||||
internal readonly FuncProvider<string, Dictionary<string, string>, string, int, PenumbraApiEc> AddTemporaryModAll;
|
||||
internal readonly FuncProvider<string, string, Dictionary<string, string>, string, int, PenumbraApiEc> AddTemporaryMod;
|
||||
internal readonly FuncProvider<string, int, PenumbraApiEc> RemoveTemporaryModAll;
|
||||
internal readonly FuncProvider<string, string, int, PenumbraApiEc> RemoveTemporaryMod;
|
||||
|
||||
// Resource Tree
|
||||
internal readonly FuncProvider<ushort[], IReadOnlyDictionary<string, string[]>?[]> GetGameObjectResourcePaths;
|
||||
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<bool, ushort[], Ipc.ResourceTree?[]> GetGameObjectResourceTrees;
|
||||
internal readonly FuncProvider<bool, IReadOnlyDictionary<ushort, Ipc.ResourceTree>> GetPlayerResourceTrees;
|
||||
|
||||
public PenumbraIpcProviders(DalamudPluginInterface pi, IPenumbraApi api, ModManager modManager, CollectionManager collections,
|
||||
TempModManager tempMods, TempCollectionManager tempCollections, SaveService saveService, Configuration config)
|
||||
{
|
||||
Api = api;
|
||||
|
||||
// Plugin State
|
||||
Initialized = Ipc.Initialized.Provider(pi);
|
||||
Disposed = Ipc.Disposed.Provider(pi);
|
||||
ApiVersion = Ipc.ApiVersion.Provider(pi, DeprecatedVersion);
|
||||
ApiVersions = Ipc.ApiVersions.Provider(pi, () => Api.ApiVersion);
|
||||
GetEnabledState = Ipc.GetEnabledState.Provider(pi, Api.GetEnabledState);
|
||||
EnabledChange =
|
||||
Ipc.EnabledChange.Provider(pi, () => Api.EnabledChange += EnabledChangeEvent, () => Api.EnabledChange -= EnabledChangeEvent);
|
||||
|
||||
// Configuration
|
||||
GetModDirectory = Ipc.GetModDirectory.Provider(pi, Api.GetModDirectory);
|
||||
GetConfiguration = Ipc.GetConfiguration.Provider(pi, Api.GetConfiguration);
|
||||
ModDirectoryChanged = Ipc.ModDirectoryChanged.Provider(pi, a => Api.ModDirectoryChanged += a, a => Api.ModDirectoryChanged -= a);
|
||||
|
||||
// UI
|
||||
PreSettingsTabBarDraw =
|
||||
Ipc.PreSettingsTabBarDraw.Provider(pi, a => Api.PreSettingsTabBarDraw += a, a => Api.PreSettingsTabBarDraw -= a);
|
||||
PreSettingsDraw = Ipc.PreSettingsDraw.Provider(pi, a => Api.PreSettingsPanelDraw += a, a => Api.PreSettingsPanelDraw -= a);
|
||||
PostEnabledDraw =
|
||||
Ipc.PostEnabledDraw.Provider(pi, a => Api.PostEnabledDraw += a, a => Api.PostEnabledDraw -= a);
|
||||
PostSettingsDraw = Ipc.PostSettingsDraw.Provider(pi, a => Api.PostSettingsPanelDraw += a, a => Api.PostSettingsPanelDraw -= a);
|
||||
ChangedItemTooltip =
|
||||
Ipc.ChangedItemTooltip.Provider(pi, () => Api.ChangedItemTooltip += OnTooltip, () => Api.ChangedItemTooltip -= OnTooltip);
|
||||
ChangedItemClick = Ipc.ChangedItemClick.Provider(pi, () => Api.ChangedItemClicked += OnClick, () => Api.ChangedItemClicked -= OnClick);
|
||||
OpenMainWindow = Ipc.OpenMainWindow.Provider(pi, Api.OpenMainWindow);
|
||||
CloseMainWindow = Ipc.CloseMainWindow.Provider(pi, Api.CloseMainWindow);
|
||||
|
||||
// Redrawing
|
||||
RedrawAll = Ipc.RedrawAll.Provider(pi, Api.RedrawAll);
|
||||
RedrawObject = Ipc.RedrawObject.Provider(pi, Api.RedrawObject);
|
||||
RedrawObjectByIndex = Ipc.RedrawObjectByIndex.Provider(pi, Api.RedrawObject);
|
||||
RedrawObjectByName = Ipc.RedrawObjectByName.Provider(pi, Api.RedrawObject);
|
||||
GameObjectRedrawn = Ipc.GameObjectRedrawn.Provider(pi, () => Api.GameObjectRedrawn += OnGameObjectRedrawn,
|
||||
() => Api.GameObjectRedrawn -= OnGameObjectRedrawn);
|
||||
|
||||
// Game State
|
||||
GetDrawObjectInfo = Ipc.GetDrawObjectInfo.Provider(pi, Api.GetDrawObjectInfo);
|
||||
GetCutsceneParentIndex = Ipc.GetCutsceneParentIndex.Provider(pi, Api.GetCutsceneParentIndex);
|
||||
SetCutsceneParentIndex = Ipc.SetCutsceneParentIndex.Provider(pi, Api.SetCutsceneParentIndex);
|
||||
CreatingCharacterBase = Ipc.CreatingCharacterBase.Provider(pi,
|
||||
() => Api.CreatingCharacterBase += CreatingCharacterBaseEvent,
|
||||
() => Api.CreatingCharacterBase -= CreatingCharacterBaseEvent);
|
||||
CreatedCharacterBase = Ipc.CreatedCharacterBase.Provider(pi,
|
||||
() => Api.CreatedCharacterBase += CreatedCharacterBaseEvent,
|
||||
() => Api.CreatedCharacterBase -= CreatedCharacterBaseEvent);
|
||||
GameObjectResourcePathResolved = Ipc.GameObjectResourcePathResolved.Provider(pi,
|
||||
() => Api.GameObjectResourceResolved += GameObjectResourceResolvedEvent,
|
||||
() => Api.GameObjectResourceResolved -= GameObjectResourceResolvedEvent);
|
||||
|
||||
// Resolve
|
||||
ResolveDefaultPath = Ipc.ResolveDefaultPath.Provider(pi, Api.ResolveDefaultPath);
|
||||
ResolveInterfacePath = Ipc.ResolveInterfacePath.Provider(pi, Api.ResolveInterfacePath);
|
||||
ResolvePlayerPath = Ipc.ResolvePlayerPath.Provider(pi, Api.ResolvePlayerPath);
|
||||
ResolveGameObjectPath = Ipc.ResolveGameObjectPath.Provider(pi, Api.ResolveGameObjectPath);
|
||||
ResolveCharacterPath = Ipc.ResolveCharacterPath.Provider(pi, Api.ResolvePath);
|
||||
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);
|
||||
ResolvePlayerPathsAsync = Ipc.ResolvePlayerPathsAsync.Provider(pi, Api.ResolvePlayerPathsAsync);
|
||||
|
||||
// Collections
|
||||
GetCollections = Ipc.GetCollections.Provider(pi, Api.GetCollections);
|
||||
GetCurrentCollectionName = Ipc.GetCurrentCollectionName.Provider(pi, Api.GetCurrentCollection);
|
||||
GetDefaultCollectionName = Ipc.GetDefaultCollectionName.Provider(pi, Api.GetDefaultCollection);
|
||||
GetInterfaceCollectionName = Ipc.GetInterfaceCollectionName.Provider(pi, Api.GetInterfaceCollection);
|
||||
GetCharacterCollectionName = Ipc.GetCharacterCollectionName.Provider(pi, Api.GetCharacterCollection);
|
||||
GetCollectionForType = Ipc.GetCollectionForType.Provider(pi, Api.GetCollectionForType);
|
||||
SetCollectionForType = Ipc.SetCollectionForType.Provider(pi, Api.SetCollectionForType);
|
||||
GetCollectionForObject = Ipc.GetCollectionForObject.Provider(pi, Api.GetCollectionForObject);
|
||||
SetCollectionForObject = Ipc.SetCollectionForObject.Provider(pi, Api.SetCollectionForObject);
|
||||
GetChangedItems = Ipc.GetChangedItems.Provider(pi, Api.GetChangedItemsForCollection);
|
||||
|
||||
// Meta
|
||||
GetPlayerMetaManipulations = Ipc.GetPlayerMetaManipulations.Provider(pi, Api.GetPlayerMetaManipulations);
|
||||
GetMetaManipulations = Ipc.GetMetaManipulations.Provider(pi, Api.GetMetaManipulations);
|
||||
GetGameObjectMetaManipulations = Ipc.GetGameObjectMetaManipulations.Provider(pi, Api.GetGameObjectMetaManipulations);
|
||||
|
||||
// Mods
|
||||
GetMods = Ipc.GetMods.Provider(pi, Api.GetModList);
|
||||
ReloadMod = Ipc.ReloadMod.Provider(pi, Api.ReloadMod);
|
||||
InstallMod = Ipc.InstallMod.Provider(pi, Api.InstallMod);
|
||||
AddMod = Ipc.AddMod.Provider(pi, Api.AddMod);
|
||||
DeleteMod = Ipc.DeleteMod.Provider(pi, Api.DeleteMod);
|
||||
GetModPath = Ipc.GetModPath.Provider(pi, Api.GetModPath);
|
||||
SetModPath = Ipc.SetModPath.Provider(pi, Api.SetModPath);
|
||||
ModDeleted = Ipc.ModDeleted.Provider(pi, () => Api.ModDeleted += ModDeletedEvent, () => Api.ModDeleted -= ModDeletedEvent);
|
||||
ModAdded = Ipc.ModAdded.Provider(pi, () => Api.ModAdded += ModAddedEvent, () => Api.ModAdded -= ModAddedEvent);
|
||||
ModMoved = Ipc.ModMoved.Provider(pi, () => Api.ModMoved += ModMovedEvent, () => Api.ModMoved -= ModMovedEvent);
|
||||
|
||||
// ModSettings
|
||||
GetAvailableModSettings = Ipc.GetAvailableModSettings.Provider(pi, Api.GetAvailableModSettings);
|
||||
GetCurrentModSettings = Ipc.GetCurrentModSettings.Provider(pi, Api.GetCurrentModSettings);
|
||||
TryInheritMod = Ipc.TryInheritMod.Provider(pi, Api.TryInheritMod);
|
||||
TrySetMod = Ipc.TrySetMod.Provider(pi, Api.TrySetMod);
|
||||
TrySetModPriority = Ipc.TrySetModPriority.Provider(pi, Api.TrySetModPriority);
|
||||
TrySetModSetting = Ipc.TrySetModSetting.Provider(pi, Api.TrySetModSetting);
|
||||
TrySetModSettings = Ipc.TrySetModSettings.Provider(pi, Api.TrySetModSettings);
|
||||
ModSettingChanged = Ipc.ModSettingChanged.Provider(pi,
|
||||
() => Api.ModSettingChanged += ModSettingChangedEvent,
|
||||
() => Api.ModSettingChanged -= ModSettingChangedEvent);
|
||||
CopyModSettings = Ipc.CopyModSettings.Provider(pi, Api.CopyModSettings);
|
||||
|
||||
// Editing
|
||||
ConvertTextureFile = Ipc.ConvertTextureFile.Provider(pi, Api.ConvertTextureFile);
|
||||
ConvertTextureData = Ipc.ConvertTextureData.Provider(pi, Api.ConvertTextureData);
|
||||
|
||||
// Temporary
|
||||
CreateTemporaryCollection = Ipc.CreateTemporaryCollection.Provider(pi, Api.CreateTemporaryCollection);
|
||||
RemoveTemporaryCollection = Ipc.RemoveTemporaryCollection.Provider(pi, Api.RemoveTemporaryCollection);
|
||||
CreateNamedTemporaryCollection = Ipc.CreateNamedTemporaryCollection.Provider(pi, Api.CreateNamedTemporaryCollection);
|
||||
RemoveTemporaryCollectionByName = Ipc.RemoveTemporaryCollectionByName.Provider(pi, Api.RemoveTemporaryCollectionByName);
|
||||
AssignTemporaryCollection = Ipc.AssignTemporaryCollection.Provider(pi, Api.AssignTemporaryCollection);
|
||||
AddTemporaryModAll = Ipc.AddTemporaryModAll.Provider(pi, Api.AddTemporaryModAll);
|
||||
AddTemporaryMod = Ipc.AddTemporaryMod.Provider(pi, Api.AddTemporaryMod);
|
||||
RemoveTemporaryModAll = Ipc.RemoveTemporaryModAll.Provider(pi, Api.RemoveTemporaryModAll);
|
||||
RemoveTemporaryMod = Ipc.RemoveTemporaryMod.Provider(pi, Api.RemoveTemporaryMod);
|
||||
|
||||
// ResourceTree
|
||||
GetGameObjectResourcePaths = Ipc.GetGameObjectResourcePaths.Provider(pi, Api.GetGameObjectResourcePaths);
|
||||
GetPlayerResourcePaths = Ipc.GetPlayerResourcePaths.Provider(pi, Api.GetPlayerResourcePaths);
|
||||
GetGameObjectResourcesOfType = Ipc.GetGameObjectResourcesOfType.Provider(pi, Api.GetGameObjectResourcesOfType);
|
||||
GetPlayerResourcesOfType = Ipc.GetPlayerResourcesOfType.Provider(pi, Api.GetPlayerResourcesOfType);
|
||||
GetGameObjectResourceTrees = Ipc.GetGameObjectResourceTrees.Provider(pi, Api.GetGameObjectResourceTrees);
|
||||
GetPlayerResourceTrees = Ipc.GetPlayerResourceTrees.Provider(pi, Api.GetPlayerResourceTrees);
|
||||
|
||||
Initialized.Invoke();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Plugin State
|
||||
Initialized.Dispose();
|
||||
ApiVersion.Dispose();
|
||||
ApiVersions.Dispose();
|
||||
GetEnabledState.Dispose();
|
||||
EnabledChange.Dispose();
|
||||
|
||||
// Configuration
|
||||
GetModDirectory.Dispose();
|
||||
GetConfiguration.Dispose();
|
||||
ModDirectoryChanged.Dispose();
|
||||
|
||||
// UI
|
||||
PreSettingsTabBarDraw.Dispose();
|
||||
PreSettingsDraw.Dispose();
|
||||
PostEnabledDraw.Dispose();
|
||||
PostSettingsDraw.Dispose();
|
||||
ChangedItemTooltip.Dispose();
|
||||
ChangedItemClick.Dispose();
|
||||
OpenMainWindow.Dispose();
|
||||
CloseMainWindow.Dispose();
|
||||
|
||||
// Redrawing
|
||||
RedrawAll.Dispose();
|
||||
RedrawObject.Dispose();
|
||||
RedrawObjectByIndex.Dispose();
|
||||
RedrawObjectByName.Dispose();
|
||||
GameObjectRedrawn.Dispose();
|
||||
|
||||
// Game State
|
||||
GetDrawObjectInfo.Dispose();
|
||||
GetCutsceneParentIndex.Dispose();
|
||||
SetCutsceneParentIndex.Dispose();
|
||||
CreatingCharacterBase.Dispose();
|
||||
CreatedCharacterBase.Dispose();
|
||||
GameObjectResourcePathResolved.Dispose();
|
||||
|
||||
// Resolve
|
||||
ResolveDefaultPath.Dispose();
|
||||
ResolveInterfacePath.Dispose();
|
||||
ResolvePlayerPath.Dispose();
|
||||
ResolveGameObjectPath.Dispose();
|
||||
ResolveCharacterPath.Dispose();
|
||||
ReverseResolvePath.Dispose();
|
||||
ReverseResolveGameObjectPath.Dispose();
|
||||
ReverseResolvePlayerPath.Dispose();
|
||||
ResolvePlayerPaths.Dispose();
|
||||
ResolvePlayerPathsAsync.Dispose();
|
||||
|
||||
// Collections
|
||||
GetCollections.Dispose();
|
||||
GetCurrentCollectionName.Dispose();
|
||||
GetDefaultCollectionName.Dispose();
|
||||
GetInterfaceCollectionName.Dispose();
|
||||
GetCharacterCollectionName.Dispose();
|
||||
GetCollectionForType.Dispose();
|
||||
SetCollectionForType.Dispose();
|
||||
GetCollectionForObject.Dispose();
|
||||
SetCollectionForObject.Dispose();
|
||||
GetChangedItems.Dispose();
|
||||
|
||||
// Meta
|
||||
GetPlayerMetaManipulations.Dispose();
|
||||
GetMetaManipulations.Dispose();
|
||||
GetGameObjectMetaManipulations.Dispose();
|
||||
|
||||
// Mods
|
||||
GetMods.Dispose();
|
||||
ReloadMod.Dispose();
|
||||
InstallMod.Dispose();
|
||||
AddMod.Dispose();
|
||||
DeleteMod.Dispose();
|
||||
GetModPath.Dispose();
|
||||
SetModPath.Dispose();
|
||||
ModDeleted.Dispose();
|
||||
ModAdded.Dispose();
|
||||
ModMoved.Dispose();
|
||||
|
||||
// ModSettings
|
||||
GetAvailableModSettings.Dispose();
|
||||
GetCurrentModSettings.Dispose();
|
||||
TryInheritMod.Dispose();
|
||||
TrySetMod.Dispose();
|
||||
TrySetModPriority.Dispose();
|
||||
TrySetModSetting.Dispose();
|
||||
TrySetModSettings.Dispose();
|
||||
ModSettingChanged.Dispose();
|
||||
CopyModSettings.Dispose();
|
||||
|
||||
// Temporary
|
||||
CreateTemporaryCollection.Dispose();
|
||||
RemoveTemporaryCollection.Dispose();
|
||||
CreateNamedTemporaryCollection.Dispose();
|
||||
RemoveTemporaryCollectionByName.Dispose();
|
||||
AssignTemporaryCollection.Dispose();
|
||||
AddTemporaryModAll.Dispose();
|
||||
AddTemporaryMod.Dispose();
|
||||
RemoveTemporaryModAll.Dispose();
|
||||
RemoveTemporaryMod.Dispose();
|
||||
|
||||
// Editing
|
||||
ConvertTextureFile.Dispose();
|
||||
ConvertTextureData.Dispose();
|
||||
|
||||
// Resource Tree
|
||||
GetGameObjectResourcePaths.Dispose();
|
||||
GetPlayerResourcePaths.Dispose();
|
||||
GetGameObjectResourcesOfType.Dispose();
|
||||
GetPlayerResourcesOfType.Dispose();
|
||||
GetGameObjectResourceTrees.Dispose();
|
||||
GetPlayerResourceTrees.Dispose();
|
||||
|
||||
Disposed.Invoke();
|
||||
Disposed.Dispose();
|
||||
}
|
||||
|
||||
// Wrappers
|
||||
private int DeprecatedVersion()
|
||||
{
|
||||
Penumbra.Log.Warning($"{Ipc.ApiVersion.Label} is outdated. Please use {Ipc.ApiVersions.Label} instead.");
|
||||
return Api.ApiVersion.Breaking;
|
||||
}
|
||||
|
||||
private void OnClick(MouseButton click, object? item)
|
||||
{
|
||||
var (type, id) = ChangedItemExtensions.ChangedItemToTypeAndId(item);
|
||||
ChangedItemClick.Invoke(click, type, id);
|
||||
}
|
||||
|
||||
private void OnTooltip(object? item)
|
||||
{
|
||||
var (type, id) = ChangedItemExtensions.ChangedItemToTypeAndId(item);
|
||||
ChangedItemTooltip.Invoke(type, id);
|
||||
}
|
||||
|
||||
private void EnabledChangeEvent(bool value)
|
||||
=> EnabledChange.Invoke(value);
|
||||
|
||||
private void OnGameObjectRedrawn(IntPtr objectAddress, int objectTableIndex)
|
||||
=> GameObjectRedrawn.Invoke(objectAddress, objectTableIndex);
|
||||
|
||||
private void CreatingCharacterBaseEvent(IntPtr gameObject, string collectionName, IntPtr modelId, IntPtr customize, IntPtr equipData)
|
||||
=> CreatingCharacterBase.Invoke(gameObject, collectionName, modelId, customize, equipData);
|
||||
|
||||
private void CreatedCharacterBaseEvent(IntPtr gameObject, string collectionName, IntPtr drawObject)
|
||||
=> CreatedCharacterBase.Invoke(gameObject, collectionName, drawObject);
|
||||
|
||||
private void GameObjectResourceResolvedEvent(IntPtr gameObject, string gamePath, string localPath)
|
||||
=> GameObjectResourcePathResolved.Invoke(gameObject, gamePath, localPath);
|
||||
|
||||
private void ModSettingChangedEvent(ModSettingChange type, string collection, string mod, bool inherited)
|
||||
=> ModSettingChanged.Invoke(type, collection, mod, inherited);
|
||||
|
||||
private void ModDeletedEvent(string name)
|
||||
=> ModDeleted.Invoke(name);
|
||||
|
||||
private void ModAddedEvent(string name)
|
||||
=> ModAdded.Invoke(name);
|
||||
|
||||
private void ModMovedEvent(string from, string to)
|
||||
=> ModMoved.Invoke(from, to);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue