mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-02-23 00:07:53 +01:00
Add shape meta manipulations and rework attribute hook.
This commit is contained in:
parent
0adec35848
commit
6ad0b4299a
23 changed files with 900 additions and 298 deletions
85
Penumbra/Interop/Hooks/PostProcessing/AttributeHook.cs
Normal file
85
Penumbra/Interop/Hooks/PostProcessing/AttributeHook.cs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
using Dalamud.Hooking;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Penumbra.Interop.PathResolving;
|
||||
using Penumbra.Meta;
|
||||
|
||||
namespace Penumbra.Interop.Hooks.PostProcessing;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered whenever a model recomputes its attribute masks.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the game object that recomputed its attributes. </item>
|
||||
/// <item>Parameter is the draw object on which the recomputation was called. </item>
|
||||
/// <item>Parameter is the collection associated with the game object. </item>
|
||||
/// <item>Parameter is the slot that was recomputed. If this is Unknown, it is a general new update call. </item>
|
||||
/// </list> </summary>
|
||||
public sealed unsafe class AttributeHook : EventWrapper<Actor, Model, ModCollection, AttributeHook.Priority>, IHookService
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="ShapeManager.OnAttributeComputed"/>
|
||||
ShapeManager = 0,
|
||||
}
|
||||
|
||||
private readonly CollectionResolver _resolver;
|
||||
private readonly Configuration _config;
|
||||
|
||||
public AttributeHook(HookManager hooks, Configuration config, CollectionResolver resolver)
|
||||
: base("Update Model Attributes")
|
||||
{
|
||||
_config = config;
|
||||
_resolver = resolver;
|
||||
_task = hooks.CreateHook<Delegate>(Name, Sigs.UpdateAttributes, Detour, config.EnableCustomShapes);
|
||||
}
|
||||
|
||||
private readonly Task<Hook<Delegate>> _task;
|
||||
|
||||
public nint Address
|
||||
=> _task.Result.Address;
|
||||
|
||||
public void Enable()
|
||||
=> SetState(true);
|
||||
|
||||
public void Disable()
|
||||
=> SetState(false);
|
||||
|
||||
public void SetState(bool enabled)
|
||||
{
|
||||
if (_config.EnableCustomShapes == enabled)
|
||||
return;
|
||||
|
||||
_config.EnableCustomShapes = enabled;
|
||||
_config.Save();
|
||||
if (enabled)
|
||||
_task.Result.Enable();
|
||||
else
|
||||
_task.Result.Disable();
|
||||
}
|
||||
|
||||
|
||||
public Task Awaiter
|
||||
=> _task;
|
||||
|
||||
public bool Finished
|
||||
=> _task.IsCompletedSuccessfully;
|
||||
|
||||
private delegate void Delegate(Human* human);
|
||||
|
||||
private void Detour(Human* human)
|
||||
{
|
||||
_task.Result.Original(human);
|
||||
var resolveData = _resolver.IdentifyCollection((DrawObject*)human, true);
|
||||
var identifiedActor = resolveData.AssociatedGameObject;
|
||||
var identifiedCollection = resolveData.ModCollection;
|
||||
Penumbra.Log.Excessive($"[{Name}] Invoked on 0x{(ulong)human:X} (0x{identifiedActor:X}).");
|
||||
Invoke(identifiedActor, human, identifiedCollection);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
=> _task.Result.Dispose();
|
||||
}
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
using Dalamud.Hooking;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Communication;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Penumbra.Interop.PathResolving;
|
||||
using Penumbra.Services;
|
||||
|
||||
namespace Penumbra.Interop.Hooks.PostProcessing;
|
||||
|
||||
public sealed unsafe class AttributeHooks : IRequiredService, IDisposable
|
||||
{
|
||||
private delegate void SetupAttributes(Human* human, byte* data);
|
||||
private delegate void AttributeUpdate(Human* human);
|
||||
|
||||
private readonly Configuration _config;
|
||||
private readonly ModelAttributeComputed _event;
|
||||
private readonly CollectionResolver _resolver;
|
||||
|
||||
private readonly AttributeHook[] _hooks;
|
||||
private readonly Task<Hook<AttributeUpdate>> _updateHook;
|
||||
private ModCollection _identifiedCollection = ModCollection.Empty;
|
||||
private Actor _identifiedActor = Actor.Null;
|
||||
private bool _inUpdate;
|
||||
|
||||
public AttributeHooks(Configuration config, CommunicatorService communication, CollectionResolver resolver, HookManager hooks)
|
||||
{
|
||||
_config = config;
|
||||
_event = communication.ModelAttributeComputed;
|
||||
_resolver = resolver;
|
||||
_hooks =
|
||||
[
|
||||
new AttributeHook(this, hooks, Sigs.SetupTopModelAttributes, _config.EnableCustomShapes, HumanSlot.Body),
|
||||
new AttributeHook(this, hooks, Sigs.SetupHandModelAttributes, _config.EnableCustomShapes, HumanSlot.Hands),
|
||||
new AttributeHook(this, hooks, Sigs.SetupLegModelAttributes, _config.EnableCustomShapes, HumanSlot.Legs),
|
||||
new AttributeHook(this, hooks, Sigs.SetupFootModelAttributes, _config.EnableCustomShapes, HumanSlot.Feet),
|
||||
];
|
||||
_updateHook = hooks.CreateHook<AttributeUpdate>("UpdateAttributes", Sigs.UpdateAttributes, UpdateAttributesDetour,
|
||||
_config.EnableCustomShapes);
|
||||
}
|
||||
|
||||
private class AttributeHook
|
||||
{
|
||||
private readonly AttributeHooks _parent;
|
||||
public readonly string Name;
|
||||
public readonly Task<Hook<SetupAttributes>> Hook;
|
||||
public readonly uint ModelIndex;
|
||||
public readonly HumanSlot Slot;
|
||||
|
||||
public AttributeHook(AttributeHooks parent, HookManager hooks, string signature, bool enabled, HumanSlot slot)
|
||||
{
|
||||
_parent = parent;
|
||||
Name = $"Setup{slot}Attributes";
|
||||
Slot = slot;
|
||||
ModelIndex = slot.ToIndex();
|
||||
Hook = hooks.CreateHook<SetupAttributes>(Name, signature, Detour, enabled);
|
||||
}
|
||||
|
||||
private void Detour(Human* human, byte* data)
|
||||
{
|
||||
Penumbra.Log.Excessive($"[{Name}] Invoked on 0x{(ulong)human:X} (0x{_parent._identifiedActor.Address:X}).");
|
||||
Hook.Result.Original(human, data);
|
||||
if (_parent is { _inUpdate: true, _identifiedActor.Valid: true })
|
||||
_parent._event.Invoke(_parent._identifiedActor, human, _parent._identifiedCollection, Slot);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAttributesDetour(Human* human)
|
||||
{
|
||||
var resolveData = _resolver.IdentifyCollection((DrawObject*)human, true);
|
||||
_identifiedActor = resolveData.AssociatedGameObject;
|
||||
_identifiedCollection = resolveData.ModCollection;
|
||||
_inUpdate = true;
|
||||
Penumbra.Log.Excessive($"[UpdateAttributes] Invoked on 0x{(ulong)human:X} (0x{_identifiedActor.Address:X}).");
|
||||
_event.Invoke(_identifiedActor, human, _identifiedCollection, HumanSlot.Unknown);
|
||||
_updateHook.Result.Original(human);
|
||||
_inUpdate = false;
|
||||
}
|
||||
|
||||
public void SetState(bool enabled)
|
||||
{
|
||||
if (_config.EnableCustomShapes == enabled)
|
||||
return;
|
||||
|
||||
_config.EnableCustomShapes = enabled;
|
||||
_config.Save();
|
||||
if (enabled)
|
||||
{
|
||||
foreach (var hook in _hooks)
|
||||
hook.Hook.Result.Enable();
|
||||
_updateHook.Result.Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var hook in _hooks)
|
||||
hook.Hook.Result.Disable();
|
||||
_updateHook.Result.Disable();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var hook in _hooks)
|
||||
hook.Hook.Result.Dispose();
|
||||
_updateHook.Result.Dispose();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue