Attach to Penumbra via IPC, independently of API interface.

This commit is contained in:
Ottermandias 2021-08-27 17:00:51 +02:00
parent f10280d15d
commit d4a00dca0c
4 changed files with 159 additions and 115 deletions

View file

@ -106,7 +106,7 @@ namespace Glamourer.Gui
save.Apply(player); save.Apply(player);
if (_inGPose) if (_inGPose)
save.Apply(fallback!); save.Apply(fallback!);
_plugin.UpdateActors(player, fallback); Glamourer.Penumbra.UpdateActors(player, fallback);
} }
private void DrawApplyToTargetButton(CharacterSave save) private void DrawApplyToTargetButton(CharacterSave save)
@ -122,7 +122,7 @@ namespace Glamourer.Gui
save.Apply(player); save.Apply(player);
if (fallBackActor != null) if (fallBackActor != null)
save.Apply(fallBackActor); save.Apply(fallBackActor);
_plugin.UpdateActors(player, fallBackActor); Glamourer.Penumbra.UpdateActors(player, fallBackActor);
} }
private void SaveNewDesign(CharacterSave save) private void SaveNewDesign(CharacterSave save)
@ -180,7 +180,7 @@ namespace Glamourer.Gui
changes |= DrawMiscellaneous(_currentSave, _player); changes |= DrawMiscellaneous(_currentSave, _player);
if (_player != null && changes) if (_player != null && changes)
_plugin.UpdateActors(_player); Glamourer.Penumbra.UpdateActors(_player);
ImGui.EndChild(); ImGui.EndChild();
ImGui.EndGroup(); ImGui.EndGroup();
} }

View file

@ -53,10 +53,9 @@ namespace Glamourer.Gui
return; return;
} }
if (ImGui.Button(buttonLabel) && Glamourer.GetPenumbra()) if (ImGui.Button(buttonLabel))
{ {
_plugin.UnregisterFunctions(); Glamourer.Penumbra.Reattach(true);
_plugin.RegisterFunctions();
} }
if (ImGui.IsItemHovered()) if (ImGui.IsItemHovered())
@ -88,12 +87,11 @@ namespace Glamourer.Gui
cfg.AttachToPenumbra = v; cfg.AttachToPenumbra = v;
if (v) if (v)
{ {
if (Glamourer.GetPenumbra()) Glamourer.Penumbra.Reattach(true);
_plugin.RegisterFunctions();
} }
else else
{ {
_plugin.UnregisterFunctions(); Glamourer.Penumbra.Unattach();
} }
}); });
ImGui.SameLine(); ImGui.SameLine();

View file

@ -3,109 +3,32 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.Command; using Dalamud.Game.Command;
using Dalamud.Logging;
using Dalamud.Plugin; using Dalamud.Plugin;
using Glamourer.Customization; using Glamourer.Customization;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.FileSystem; using Glamourer.FileSystem;
using Glamourer.Gui; using Glamourer.Gui;
using ImGuiNET; using ImGuiNET;
using Penumbra.Api;
using Penumbra.PlayerWatch; using Penumbra.PlayerWatch;
namespace Glamourer namespace Glamourer
{ {
public class Glamourer : IDalamudPlugin public class Glamourer : IDalamudPlugin
{ {
public const int RequiredPenumbraShareVersion = 3;
private const string HelpString = "[Copy|Apply|Save],[Name or PlaceHolder],<Name for Save>"; private const string HelpString = "[Copy|Apply|Save],[Name or PlaceHolder],<Name for Save>";
public string Name public string Name
=> "Glamourer"; => "Glamourer";
public static GlamourerConfig Config = null!; public static GlamourerConfig Config = null!;
private Interface _interface = null!; public static IPlayerWatcher PlayerWatcher = null!;
public static ICustomizationManager Customization = null!; public static ICustomizationManager Customization = null!;
public DesignManager Designs = null!; private readonly Interface _interface;
public IPlayerWatcher PlayerWatcher = null!; public DesignManager Designs;
public static string Version = string.Empty; public static string Version = string.Empty;
public static PenumbraAttach Penumbra = null!;
public static IPenumbraApi? Penumbra;
private static void PenumbraTooltip(object? it)
{
if (it is Lumina.Excel.GeneratedSheets.Item)
ImGui.Text("Right click to apply to current Glamourer Set. [Glamourer]");
}
private void PenumbraRightClick(MouseButton button, object? it)
{
if (button != MouseButton.Right || it is not Lumina.Excel.GeneratedSheets.Item item)
return;
var gPose = Dalamud.Objects[Interface.GPoseActorId] as Character;
var player = Dalamud.Objects[0] as Character;
var writeItem = new Item(item, string.Empty);
if (gPose != null)
{
writeItem.Write(gPose.Address);
UpdateActors(gPose, player);
}
else if (player != null)
{
writeItem.Write(player.Address);
UpdateActors(player);
}
}
public void RegisterFunctions()
{
if (Penumbra == null || !Penumbra.Valid)
return;
Penumbra!.ChangedItemTooltip += PenumbraTooltip;
Penumbra!.ChangedItemClicked += PenumbraRightClick;
}
public void UnregisterFunctions()
{
if (Penumbra == null || !Penumbra.Valid)
return;
Penumbra!.ChangedItemTooltip -= PenumbraTooltip;
Penumbra!.ChangedItemClicked -= PenumbraRightClick;
}
internal static bool GetPenumbra()
{
try
{
var subscriber = Dalamud.PluginInterface.GetIpcSubscriber<IPenumbraApiBase>("Penumbra.Api");
var penumbraApiBase = subscriber.InvokeFunc();
if (penumbraApiBase.ApiVersion != RequiredPenumbraShareVersion)
{
PluginLog.Debug("Could not get Penumbra because API version {penumbraApiBase.ApiVersion} does not equal the required version {RequiredPenumbraShareVersion}.");
Penumbra = null;
return false;
}
Penumbra = penumbraApiBase as IPenumbraApi;
}
catch (IpcNotReadyError ipc)
{
Penumbra = null;
PluginLog.Debug($"Could not get Penumbra because IPC not registered:\n{ipc}");
}
catch (Exception e)
{
Penumbra = null;
PluginLog.Debug($"Could not get Penumbra for unknown reason:\n{e}");
}
return Penumbra != null;
}
public Glamourer(DalamudPluginInterface pluginInterface) public Glamourer(DalamudPluginInterface pluginInterface)
{ {
@ -114,8 +37,7 @@ namespace Glamourer
Config = GlamourerConfig.Load(); Config = GlamourerConfig.Load();
Customization = CustomizationManager.Create(Dalamud.PluginInterface, Dalamud.GameData, Dalamud.ClientState.ClientLanguage); Customization = CustomizationManager.Create(Dalamud.PluginInterface, Dalamud.GameData, Dalamud.ClientState.ClientLanguage);
Designs = new DesignManager(); Designs = new DesignManager();
if (GetPenumbra() && Config.AttachToPenumbra) Penumbra = new PenumbraAttach(Config.AttachToPenumbra);
RegisterFunctions();
PlayerWatcher = PlayerWatchFactory.Create(Dalamud.Framework, Dalamud.ClientState, Dalamud.Objects); PlayerWatcher = PlayerWatchFactory.Create(Dalamud.Framework, Dalamud.ClientState, Dalamud.Objects);
Dalamud.Commands.AddHandler("/glamourer", new CommandInfo(OnGlamourer) Dalamud.Commands.AddHandler("/glamourer", new CommandInfo(OnGlamourer)
@ -177,7 +99,7 @@ namespace Glamourer
save = d.Data; save = d.Data;
save?.Apply(actor); save?.Apply(actor);
UpdateActors(actor); Penumbra.UpdateActors(actor);
} }
public void SaveCommand(Character actor, string path) public void SaveCommand(Character actor, string path)
@ -268,27 +190,11 @@ namespace Glamourer
public void Dispose() public void Dispose()
{ {
PlayerWatcher?.Dispose(); Penumbra.Dispose();
UnregisterFunctions(); PlayerWatcher.Dispose();
_interface?.Dispose(); _interface.Dispose();
Dalamud.Commands.RemoveHandler("/glamour"); Dalamud.Commands.RemoveHandler("/glamour");
Dalamud.Commands.RemoveHandler("/glamourer"); Dalamud.Commands.RemoveHandler("/glamourer");
} }
// Update actors without triggering PlayerWatcher Events,
// then manually redraw using Penumbra.
public void UpdateActors(Character actor, Character? gPoseOriginalActor = null)
{
var newEquip = PlayerWatcher.UpdateActorWithoutEvent(actor);
Penumbra?.RedrawObject(actor, RedrawType.WithSettings);
// Special case for carrying over changes to the gPose actor to the regular player actor, too.
if (gPoseOriginalActor == null)
return;
newEquip.Write(gPoseOriginalActor.Address);
PlayerWatcher.UpdateActorWithoutEvent(gPoseOriginalActor);
Penumbra?.RedrawObject(gPoseOriginalActor, RedrawType.AfterGPoseWithSettings);
}
} }
} }

140
Glamourer/PenumbraAttach.cs Normal file
View file

@ -0,0 +1,140 @@
using System;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Logging;
using Dalamud.Plugin;
using Glamourer.Gui;
using ImGuiNET;
using Penumbra.Api;
using Penumbra.PlayerWatch;
namespace Glamourer
{
public class PenumbraAttach : IDisposable
{
public const int RequiredPenumbraShareVersion = 3;
private ICallGateSubscriber<object?, object>? TooltipSubscriber;
private ICallGateSubscriber<int, object?, object>? ClickSubscriber;
private ICallGateSubscriber<string, int, object>? RedrawSubscriberName;
private ICallGateSubscriber<GameObject, int, object>? RedrawSubscriberObject;
public PenumbraAttach(bool attach)
=> Reattach(attach);
public void Reattach(bool attach)
{
try
{
Unattach();
var versionSubscriber = Dalamud.PluginInterface.GetIpcSubscriber<int>("Penumbra.ApiVersion");
var version = versionSubscriber.InvokeFunc();
if (version != RequiredPenumbraShareVersion)
throw new Exception($"Invalid Version {version}, required Version {RequiredPenumbraShareVersion}.");
RedrawSubscriberName = Dalamud.PluginInterface.GetIpcSubscriber<string, int, object>("Penumbra.RedrawObjectByName");
RedrawSubscriberObject = Dalamud.PluginInterface.GetIpcSubscriber<GameObject, int, object>("Penumbra.RedrawObject");
if (!attach)
return;
TooltipSubscriber = Dalamud.PluginInterface.GetIpcSubscriber<object?, object>("Penumbra.ChangedItemTooltip");
ClickSubscriber = Dalamud.PluginInterface.GetIpcSubscriber<int, object?, object>("Penumbra.ChangedItemClick");
TooltipSubscriber.Subscribe(PenumbraTooltip);
ClickSubscriber.Subscribe(PenumbraRightClickWrapper);
}
catch (Exception e)
{
PluginLog.Debug($"Could not attach to Penumbra:\n{e}");
}
}
public void Unattach()
{
TooltipSubscriber?.Unsubscribe(PenumbraTooltip);
ClickSubscriber?.Unsubscribe(PenumbraRightClickWrapper);
TooltipSubscriber = null;
ClickSubscriber = null;
RedrawSubscriberName = null;
RedrawSubscriberObject = null;
}
public void Dispose()
=> Unattach();
private static void PenumbraTooltip(object? it)
{
if (it is Lumina.Excel.GeneratedSheets.Item)
ImGui.Text("Right click to apply to current Glamourer Set. [Glamourer]");
}
private void PenumbraRightClick(MouseButton button, object? it)
{
if (button != MouseButton.Right || it is not Lumina.Excel.GeneratedSheets.Item item)
return;
var gPose = Dalamud.Objects[Interface.GPoseActorId] as Character;
var player = Dalamud.Objects[0] as Character;
var writeItem = new Item(item, string.Empty);
if (gPose != null)
{
writeItem.Write(gPose.Address);
UpdateActors(gPose, player);
}
else if (player != null)
{
writeItem.Write(player.Address);
UpdateActors(player);
}
}
private void PenumbraRightClickWrapper(int button, object? it)
=> PenumbraRightClick((MouseButton) button, it);
public void RedrawObject(GameObject actor, RedrawType settings, bool repeat)
{
if (RedrawSubscriberObject != null)
{
try
{
RedrawSubscriberObject.InvokeFunc(actor, (int) settings);
}
catch (Exception e)
{
if (repeat)
{
Reattach(Glamourer.Config.AttachToPenumbra);
RedrawObject(actor, settings, false);
}
else
{
PluginLog.Debug($"Failure redrawing actor:\n{e}");
}
}
}
else if (repeat)
{
Reattach(Glamourer.Config.AttachToPenumbra);
RedrawObject(actor, settings, false);
}
else
PluginLog.Debug("Trying to redraw actor, but not attached to Penumbra.");
}
// Update actors without triggering PlayerWatcher Events,
// then manually redraw using Penumbra.
public void UpdateActors(Character actor, Character? gPoseOriginalActor = null)
{
var newEquip = Glamourer.PlayerWatcher.UpdateActorWithoutEvent(actor);
RedrawObject(actor, RedrawType.WithSettings, true);
// Special case for carrying over changes to the gPose actor to the regular player actor, too.
if (gPoseOriginalActor == null)
return;
newEquip.Write(gPoseOriginalActor.Address);
Glamourer.PlayerWatcher.UpdateActorWithoutEvent(gPoseOriginalActor);
RedrawObject(gPoseOriginalActor, RedrawType.AfterGPoseWithSettings, false);
}
}
}