This commit is contained in:
Ottermandias 2023-06-09 17:57:40 +02:00
parent 7710cfadfa
commit 2d6fd6015d
88 changed files with 2304 additions and 383 deletions

View file

@ -0,0 +1,318 @@
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Logging;
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using System;
using Glamourer.Api;
using Glamourer.Designs;
using Glamourer.Services;
using Glamourer.State;
using Penumbra.Api.Enums;
namespace Glamourer;
public partial class Glamourer
{
public class GlamourerIpc : IDisposable
{
public const int CurrentApiVersion = 0;
public const string LabelProviderApiVersion = "Glamourer.ApiVersion";
public const string LabelProviderGetAllCustomization = "Glamourer.GetAllCustomization";
public const string LabelProviderGetAllCustomizationFromCharacter = "Glamourer.GetAllCustomizationFromCharacter";
public const string LabelProviderApplyAll = "Glamourer.ApplyAll";
public const string LabelProviderApplyAllToCharacter = "Glamourer.ApplyAllToCharacter";
public const string LabelProviderApplyOnlyEquipment = "Glamourer.ApplyOnlyEquipment";
public const string LabelProviderApplyOnlyEquipmentToCharacter = "Glamourer.ApplyOnlyEquipmentToCharacter";
public const string LabelProviderApplyOnlyCustomization = "Glamourer.ApplyOnlyCustomization";
public const string LabelProviderApplyOnlyCustomizationToCharacter = "Glamourer.ApplyOnlyCustomizationToCharacter";
public const string LabelProviderRevert = "Glamourer.Revert";
public const string LabelProviderRevertCharacter = "Glamourer.RevertCharacter";
private readonly ObjectTable _objectTable;
private readonly DalamudPluginInterface _pluginInterface;
private readonly ActiveDesign.Manager _stateManager;
private readonly ItemManager _items;
private readonly PenumbraAttach _penumbra;
private readonly ActorService _actors;
internal ICallGateProvider<string, string?>? ProviderGetAllCustomization;
internal ICallGateProvider<Character?, string?>? ProviderGetAllCustomizationFromCharacter;
internal ICallGateProvider<string, string, object>? ProviderApplyAll;
internal ICallGateProvider<string, Character?, object>? ProviderApplyAllToCharacter;
internal ICallGateProvider<string, string, object>? ProviderApplyOnlyCustomization;
internal ICallGateProvider<string, Character?, object>? ProviderApplyOnlyCustomizationToCharacter;
internal ICallGateProvider<string, string, object>? ProviderApplyOnlyEquipment;
internal ICallGateProvider<string, Character?, object>? ProviderApplyOnlyEquipmentToCharacter;
internal ICallGateProvider<string, object>? ProviderRevert;
internal ICallGateProvider<Character?, object>? ProviderRevertCharacter;
internal ICallGateProvider<int>? ProviderGetApiVersion;
public GlamourerIpc(ObjectTable objectTable, DalamudPluginInterface pluginInterface, ActiveDesign.Manager stateManager,
ItemManager items, PenumbraAttach penumbra, ActorService actors)
{
_objectTable = objectTable;
_pluginInterface = pluginInterface;
_stateManager = stateManager;
_items = items;
_penumbra = penumbra;
_actors = actors;
InitializeProviders();
}
public void Dispose()
=> DisposeProviders();
private void DisposeProviders()
{
ProviderGetAllCustomization?.UnregisterFunc();
ProviderGetAllCustomizationFromCharacter?.UnregisterFunc();
ProviderApplyAll?.UnregisterAction();
ProviderApplyAllToCharacter?.UnregisterAction();
ProviderApplyOnlyCustomization?.UnregisterAction();
ProviderApplyOnlyCustomizationToCharacter?.UnregisterAction();
ProviderApplyOnlyEquipment?.UnregisterAction();
ProviderApplyOnlyEquipmentToCharacter?.UnregisterAction();
ProviderRevert?.UnregisterAction();
ProviderRevertCharacter?.UnregisterAction();
ProviderGetApiVersion?.UnregisterFunc();
}
private void InitializeProviders()
{
try
{
ProviderGetApiVersion = _pluginInterface.GetIpcProvider<int>(LabelProviderApiVersion);
ProviderGetApiVersion.RegisterFunc(GetApiVersion);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApiVersion}.");
}
try
{
ProviderGetAllCustomization = _pluginInterface.GetIpcProvider<string, string?>(LabelProviderGetAllCustomization);
ProviderGetAllCustomization.RegisterFunc(GetAllCustomization);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyEquipment}.");
}
try
{
ProviderGetAllCustomizationFromCharacter =
_pluginInterface.GetIpcProvider<Character?, string?>(LabelProviderGetAllCustomizationFromCharacter);
ProviderGetAllCustomizationFromCharacter.RegisterFunc(GetAllCustomization);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderGetAllCustomizationFromCharacter}.");
}
try
{
ProviderApplyAll =
_pluginInterface.GetIpcProvider<string, string, object>(LabelProviderApplyAll);
ProviderApplyAll.RegisterAction(ApplyAll);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyAll}.");
}
try
{
ProviderApplyAllToCharacter =
_pluginInterface.GetIpcProvider<string, Character?, object>(LabelProviderApplyAllToCharacter);
ProviderApplyAllToCharacter.RegisterAction(ApplyAll);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyAll}.");
}
try
{
ProviderApplyOnlyCustomization =
_pluginInterface.GetIpcProvider<string, string, object>(LabelProviderApplyOnlyCustomization);
ProviderApplyOnlyCustomization.RegisterAction(ApplyOnlyCustomization);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyCustomization}.");
}
try
{
ProviderApplyOnlyCustomizationToCharacter =
_pluginInterface.GetIpcProvider<string, Character?, object>(LabelProviderApplyOnlyCustomizationToCharacter);
ProviderApplyOnlyCustomizationToCharacter.RegisterAction(ApplyOnlyCustomization);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyCustomization}.");
}
try
{
ProviderApplyOnlyEquipment =
_pluginInterface.GetIpcProvider<string, string, object>(LabelProviderApplyOnlyEquipment);
ProviderApplyOnlyEquipment.RegisterAction(ApplyOnlyEquipment);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyEquipment}.");
}
try
{
ProviderApplyOnlyEquipmentToCharacter =
_pluginInterface.GetIpcProvider<string, Character?, object>(LabelProviderApplyOnlyEquipmentToCharacter);
ProviderApplyOnlyEquipmentToCharacter.RegisterAction(ApplyOnlyEquipment);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyEquipment}.");
}
try
{
ProviderRevert =
_pluginInterface.GetIpcProvider<string, object>(LabelProviderRevert);
ProviderRevert.RegisterAction(Revert);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderRevert}.");
}
try
{
ProviderRevertCharacter =
_pluginInterface.GetIpcProvider<Character?, object>(LabelProviderRevertCharacter);
ProviderRevertCharacter.RegisterAction(Revert);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderRevert}.");
}
}
private static int GetApiVersion()
=> CurrentApiVersion;
private void ApplyAll(string customization, string characterName)
{
foreach (var gameObject in _objectTable)
{
if (gameObject.Name.ToString() == characterName)
{
ApplyAll(customization, gameObject as Character);
return;
}
}
}
private void ApplyAll(string customization, Character? character)
{
if (character == null)
return;
var design = Design.CreateTemporaryFromBase64(_items, customization, true, true);
var active = _stateManager.GetOrCreateSave(character.Address);
_stateManager.ApplyDesign(active, design, false);
}
private void ApplyOnlyCustomization(string customization, string characterName)
{
foreach (var gameObject in _objectTable)
{
if (gameObject.Name.ToString() == characterName)
{
ApplyOnlyCustomization(customization, gameObject as Character);
return;
}
}
}
private void ApplyOnlyCustomization(string customization, Character? character)
{
if (character == null)
return;
var design = Design.CreateTemporaryFromBase64(_items, customization, true, false);
var active = _stateManager.GetOrCreateSave(character.Address);
_stateManager.ApplyDesign(active, design, false);
}
private void ApplyOnlyEquipment(string customization, string characterName)
{
foreach (var gameObject in _objectTable)
{
if (gameObject.Name.ToString() != characterName)
continue;
ApplyOnlyEquipment(customization, gameObject as Character);
return;
}
}
private void ApplyOnlyEquipment(string customization, Character? character)
{
if (character == null)
return;
var design = Design.CreateTemporaryFromBase64(_items, customization, false, true);
var active = _stateManager.GetOrCreateSave(character.Address);
_stateManager.ApplyDesign(active, design, false);
}
private void Revert(string characterName)
{
foreach (var gameObject in _objectTable)
{
if (gameObject.Name.ToString() != characterName)
continue;
Revert(gameObject as Character);
}
}
private void Revert(Character? character)
{
if (character == null)
return;
var ident = _actors.AwaitedService.FromObject(character, true, false, false);
_stateManager.DeleteSave(ident);
_penumbra.RedrawObject(character.Address, RedrawType.Redraw);
}
private string? GetAllCustomization(Character? character)
{
if (character == null)
return null;
var ident = _actors.AwaitedService.FromObject(character, true, false, false);
if (!_stateManager.TryGetValue(ident, out var design))
design = new ActiveDesign(_items, ident, character.Address);
return design.CreateOldBase64();
}
private string? GetAllCustomization(string characterName)
{
foreach (var gameObject in _objectTable)
{
if (gameObject.Name.ToString() == characterName)
return GetAllCustomization(gameObject as Character);
}
return null;
}
}
}

View file

@ -0,0 +1,207 @@
using System;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Logging;
using Dalamud.Plugin;
using Glamourer.Interop;
using Penumbra.Api;
using Penumbra.Api.Enums;
using Penumbra.Api.Helpers;
namespace Glamourer.Api;
public unsafe class PenumbraAttach : IDisposable
{
public const int RequiredPenumbraBreakingVersion = 4;
public const int RequiredPenumbraFeatureVersion = 15;
private readonly DalamudPluginInterface _pluginInterface;
private readonly EventSubscriber<ChangedItemType, uint> _tooltipSubscriber;
private readonly EventSubscriber<MouseButton, ChangedItemType, uint> _clickSubscriber;
private readonly EventSubscriber<nint, string, nint, nint, nint> _creatingCharacterBase;
private readonly EventSubscriber<nint, string, nint> _createdCharacterBase;
private ActionSubscriber<int, RedrawType> _redrawSubscriber;
private FuncSubscriber<nint, (nint, string)> _drawObjectInfo;
private FuncSubscriber<int, int> _cutsceneParent;
private readonly EventSubscriber _initializedEvent;
private readonly EventSubscriber _disposedEvent;
public bool Available { get; private set; }
public PenumbraAttach(DalamudPluginInterface pi)
{
_pluginInterface = pi;
_initializedEvent = Ipc.Initialized.Subscriber(pi, Reattach);
_disposedEvent = Ipc.Disposed.Subscriber(pi, Unattach);
_tooltipSubscriber = Ipc.ChangedItemTooltip.Subscriber(pi);
_clickSubscriber = Ipc.ChangedItemClick.Subscriber(pi);
_createdCharacterBase = Ipc.CreatedCharacterBase.Subscriber(pi);
_creatingCharacterBase = Ipc.CreatingCharacterBase.Subscriber(pi);
Reattach();
}
public event Action<MouseButton, ChangedItemType, uint> Click
{
add => _clickSubscriber.Event += value;
remove => _clickSubscriber.Event -= value;
}
public event Action<ChangedItemType, uint> Tooltip
{
add => _tooltipSubscriber.Event += value;
remove => _tooltipSubscriber.Event -= value;
}
public event Action<nint, string, nint, nint, nint> CreatingCharacterBase
{
add => _creatingCharacterBase.Event += value;
remove => _creatingCharacterBase.Event -= value;
}
public event Action<nint, string, nint> CreatedCharacterBase
{
add => _createdCharacterBase.Event += value;
remove => _createdCharacterBase.Event -= value;
}
public Actor GameObjectFromDrawObject(IntPtr drawObject)
=> Available ? _drawObjectInfo.Invoke(drawObject).Item1 : Actor.Null;
public int CutsceneParent(int idx)
=> Available ? _cutsceneParent.Invoke(idx) : -1;
public void RedrawObject(Actor actor, RedrawType settings)
{
if (!actor || !Available)
return;
try
{
_redrawSubscriber.Invoke(actor.Index, settings);
}
catch (Exception e)
{
PluginLog.Debug($"Failure redrawing object:\n{e}");
}
}
public void Reattach()
{
try
{
Unattach();
var (breaking, feature) = Ipc.ApiVersions.Subscriber(_pluginInterface).Invoke();
if (breaking != RequiredPenumbraBreakingVersion || feature < RequiredPenumbraFeatureVersion)
throw new Exception(
$"Invalid Version {breaking}.{feature:D4}, required major Version {RequiredPenumbraBreakingVersion} with feature greater or equal to {RequiredPenumbraFeatureVersion}.");
_tooltipSubscriber.Enable();
_clickSubscriber.Enable();
_creatingCharacterBase.Enable();
_createdCharacterBase.Enable();
_drawObjectInfo = Ipc.GetDrawObjectInfo.Subscriber(_pluginInterface);
_cutsceneParent = Ipc.GetCutsceneParentIndex.Subscriber(_pluginInterface);
_redrawSubscriber = Ipc.RedrawObjectByIndex.Subscriber(_pluginInterface);
Available = true;
Glamourer.Log.Debug("Glamourer attached to Penumbra.");
}
catch (Exception e)
{
Glamourer.Log.Debug($"Could not attach to Penumbra:\n{e}");
}
}
public void Unattach()
{
_tooltipSubscriber.Disable();
_clickSubscriber.Disable();
_creatingCharacterBase.Disable();
_createdCharacterBase.Disable();
if (Available)
{
Available = false;
Glamourer.Log.Debug("Glamourer detached from Penumbra.");
}
}
public void Dispose()
{
Unattach();
_tooltipSubscriber.Dispose();
_clickSubscriber.Dispose();
_creatingCharacterBase.Dispose();
_createdCharacterBase.Dispose();
_initializedEvent.Dispose();
_disposedEvent.Dispose();
}
//private static void PenumbraTooltip(ChangedItemType type, uint _)
//{
// if (type == ChangedItemType.Item)
// ImGui.Text("Right click to apply to current Glamourer Set. [Glamourer]");
//}
//
//private void PenumbraRightClick(MouseButton button, ChangedItemType type, uint id)
//{
// if (button != MouseButton.Right || type != ChangedItemType.Item)
// return;
//
// var item = (Lumina.Excel.GeneratedSheets.Item)type.GetObject(Dalamud.GameData, id)!;
// var writeItem = new Item2(item, string.Empty);
//
// UpdateItem(_objects.GPosePlayer, writeItem);
// UpdateItem(_objects.Player, writeItem);
//}
//private static void UpdateItem(Actor actor, Item2 item2)
//{
// if (!actor || !actor.DrawObject)
// return;
//
// switch (item2.EquippableTo)
// {
// case EquipSlot.MainHand:
// {
// var off = item2.HasSubModel
// ? new CharacterWeapon(item2.SubModel.id, item2.SubModel.type, item2.SubModel.variant, actor.DrawObject.OffHand.Stain)
// : item2.IsBothHand
// ? CharacterWeapon.Empty
// : actor.OffHand;
// var main = new CharacterWeapon(item2.MainModel.id, item2.MainModel.type, item2.MainModel.variant,
// actor.DrawObject.MainHand.Stain);
// Glamourer.RedrawManager.LoadWeapon(actor, main, off);
// return;
// }
// case EquipSlot.OffHand:
// {
// var off = new CharacterWeapon(item2.MainModel.id, item2.MainModel.type, item2.MainModel.variant,
// actor.DrawObject.OffHand.Stain);
// var main = actor.MainHand;
// Glamourer.RedrawManager.LoadWeapon(actor, main, off);
// return;
// }
// default:
// {
// var current = actor.DrawObject.Equip[item2.EquippableTo];
// var armor = new CharacterArmor(item2.MainModel.id, (byte)item2.MainModel.variant, current.Stain);
// Glamourer.RedrawManager.UpdateSlot(actor.DrawObject, item2.EquippableTo, armor);
// return;
// }
// }
//}
// Update objects without triggering PlayerWatcher Events,
// then manually redraw using Penumbra.
public void UpdateCharacters(Character character, Character? gPoseOriginalCharacter = null)
{
//RedrawObject(character, RedrawType.Redraw, true);
//
//// Special case for carrying over changes to the gPose player to the regular player, too.
//if (gPoseOriginalCharacter == null)
// return;
//
//newEquip.Write(gPoseOriginalCharacter.Address);
//RedrawObject(gPoseOriginalCharacter, RedrawType.AfterGPose, false);
}
}