This commit is contained in:
Ottermandias 2023-02-09 18:52:53 +01:00
parent 145b64bb7a
commit 4cf082aa19
37 changed files with 1362 additions and 1255 deletions

View file

@ -66,6 +66,9 @@ public unsafe partial struct Actor : IEquatable<Actor>, IDesignable
public bool Valid
=> Pointer != null;
public int Index
=> Pointer->GameObject.ObjectIndex;
public uint ModelId
{
get => (uint)Pointer->ModelCharaId;

View file

@ -1,22 +1,15 @@
using System.Collections.Generic;
using System.Text;
using Dalamud.Game.ClientState.Objects.Enums;
using Lumina.Excel.GeneratedSheets;
using System;
using System.Collections;
using System.Collections.Generic;
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Objects;
using Penumbra.GameData.Actors;
using static Glamourer.Interop.Actor;
namespace Glamourer.Interop;
public static class ObjectManager
public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ObjectManager.ActorData>
{
private const int CutsceneIndex = 200;
private const int GPosePlayerIndex = 201;
private const int CharacterScreenIndex = 240;
private const int ExamineScreenIndex = 241;
private const int FittingRoomIndex = 242;
private const int DyePreviewIndex = 243;
private const int PortraitIndex = 244;
public readonly struct ActorData
{
public readonly List<Actor> Objects;
@ -40,28 +33,22 @@ public static class ObjectManager
}
}
public static bool IsInGPose { get; private set; }
public static ushort World { get; private set; }
public DateTime LastUpdate { get; private set; }
public static IReadOnlyDictionary<ActorIdentifier, ActorData> Actors
=> Identifiers;
public bool IsInGPose { get; private set; }
public ushort World { get; private set; }
public static IReadOnlyList<(ActorIdentifier, ActorData)> List
=> ListData;
private readonly Dictionary<ActorIdentifier, ActorData> _identifiers = new(200);
private static readonly Dictionary<ActorIdentifier, ActorData> Identifiers = new(200);
private static readonly List<(ActorIdentifier, ActorData)> ListData = new(Dalamud.Objects.Length);
private static void HandleIdentifier(ActorIdentifier identifier, Actor character)
private void HandleIdentifier(ActorIdentifier identifier, Actor character)
{
if (!character.DrawObject || !identifier.IsValid)
return;
if (!Identifiers.TryGetValue(identifier, out var data))
if (!_identifiers.TryGetValue(identifier, out var data))
{
data = new ActorData(character, identifier.ToString());
Identifiers[identifier] = data;
ListData.Add((identifier, data));
data = new ActorData(character, identifier.ToString());
_identifiers[identifier] = data;
}
else
{
@ -69,88 +56,100 @@ public static class ObjectManager
}
}
public static void Update()
{
World = (ushort)(Dalamud.ClientState.LocalPlayer?.CurrentWorld.Id ?? 0u);
Identifiers.Clear();
ListData.Clear();
private readonly Framework _framework;
private readonly ClientState _clientState;
private readonly ObjectTable _objects;
for (var i = 0; i < CutsceneIndex; ++i)
public ObjectManager(Framework framework, ClientState clientState, ObjectTable objects)
{
_framework = framework;
_clientState = clientState;
_objects = objects;
}
public void Update()
{
var lastUpdate = _framework.LastUpdate;
if (lastUpdate <= LastUpdate)
return;
LastUpdate = lastUpdate;
World = (ushort)(_clientState.LocalPlayer?.CurrentWorld.Id ?? 0u);
_identifiers.Clear();
for (var i = 0; i < (int)ScreenActor.CutsceneStart; ++i)
{
Actor character = Dalamud.Objects.GetObjectAddress(i);
Actor character = _objects.GetObjectAddress(i);
if (character.Identifier(out var identifier))
HandleIdentifier(identifier, character);
}
for (var i = CutsceneIndex; i < CharacterScreenIndex; ++i)
for (var i = (int)ScreenActor.CutsceneStart; i < (int)ScreenActor.CutsceneEnd; ++i)
{
Actor character = Dalamud.Objects.GetObjectAddress(i);
Actor character = _objects.GetObjectAddress(i);
if (!character.Identifier(out var identifier))
break;
HandleIdentifier(identifier, character);
}
void AddSpecial(int idx, string label)
void AddSpecial(ScreenActor idx, string label)
{
Actor actor = Dalamud.Objects.GetObjectAddress(idx);
Actor actor = _objects.GetObjectAddress((int)idx);
if (actor.Identifier(out var ident))
{
var data = new ActorData(actor, label);
Identifiers.Add(ident, data);
ListData.Add((ident, data));
_identifiers.Add(ident, data);
}
}
AddSpecial(CharacterScreenIndex, "Character Screen Actor");
AddSpecial(ExamineScreenIndex, "Examine Screen Actor");
AddSpecial(FittingRoomIndex, "Fitting Room Actor");
AddSpecial(DyePreviewIndex, "Dye Preview Actor");
AddSpecial(PortraitIndex, "Portrait Actor");
AddSpecial(ScreenActor.CharacterScreen, "Character Screen Actor");
AddSpecial(ScreenActor.ExamineScreen, "Examine Screen Actor");
AddSpecial(ScreenActor.FittingRoom, "Fitting Room Actor");
AddSpecial(ScreenActor.DyePreview, "Dye Preview Actor");
AddSpecial(ScreenActor.Portrait, "Portrait Actor");
AddSpecial(ScreenActor.Card6, "Card Actor 6");
AddSpecial(ScreenActor.Card7, "Card Actor 7");
AddSpecial(ScreenActor.Card8, "Card Actor 8");
for (var i = PortraitIndex + 1; i < Dalamud.Objects.Length; ++i)
for (var i = (int)ScreenActor.ScreenEnd; i < Dalamud.Objects.Length; ++i)
{
Actor character = Dalamud.Objects.GetObjectAddress(i);
Actor character = _objects.GetObjectAddress(i);
if (character.Identifier(out var identifier))
HandleIdentifier(identifier, character);
}
Actor gPose = Dalamud.Objects.GetObjectAddress(GPosePlayerIndex);
var gPose = GPosePlayer;
IsInGPose = gPose && gPose.Utf8Name.Length > 0;
}
public static Actor GPosePlayer
=> Dalamud.Objects.GetObjectAddress(GPosePlayerIndex);
public Actor GPosePlayer
=> _objects.GetObjectAddress((int)ScreenActor.GPosePlayer);
public static Actor Player
=> Dalamud.Objects.GetObjectAddress(0);
public Actor Player
=> _objects.GetObjectAddress(0);
private static unsafe string GetLabel(Actor player, string playerName, int num, bool gPose)
{
var sb = new StringBuilder(64);
sb.Append(playerName);
public IEnumerator<KeyValuePair<ActorIdentifier, ActorData>> GetEnumerator()
=> _identifiers.GetEnumerator();
if (gPose)
{
sb.Append(" (GPose");
IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
if (player.ObjectKind == ObjectKind.Player)
sb.Append(')');
else
sb.Append(player.ModelId == 0 ? ", NPC)" : ", Monster)");
}
else if (player.ObjectKind != ObjectKind.Player)
{
sb.Append(player.ModelId == 0 ? " (NPC)" : " (Monster)");
}
public int Count
=> _identifiers.Count;
if (num > 1)
{
sb.Append(" #");
sb.Append(num);
}
public bool ContainsKey(ActorIdentifier key)
=> _identifiers.ContainsKey(key);
return sb.ToString();
}
public bool TryGetValue(ActorIdentifier key, out ActorData value)
=> _identifiers.TryGetValue(key, out value);
public ActorData this[ActorIdentifier key]
=> _identifiers[key];
public IEnumerable<ActorIdentifier> Keys
=> _identifiers.Keys;
public IEnumerable<ActorData> Values
=> _identifiers.Values;
}

View file

@ -8,35 +8,6 @@ namespace Glamourer.Interop;
public unsafe partial class RedrawManager
{
// Update
public delegate bool ChangeCustomizeDelegate(Human* human, byte* data, byte skipEquipment);
[Signature("E8 ?? ?? ?? ?? 41 0F B6 C5 66 41 89 86")]
private readonly ChangeCustomizeDelegate _changeCustomize = null!;
public bool UpdateCustomize(Actor actor, Customize customize)
{
if (!actor.Valid || !actor.DrawObject.Valid)
return false;
var d = actor.DrawObject;
if (NeedsRedraw(d.Customize, customize))
{
Glamourer.Penumbra.RedrawObject(actor.Character, RedrawType.Redraw);
return true;
}
return _changeCustomize(d.Pointer, (byte*)customize.Data, 1);
}
public static bool NeedsRedraw(Customize lhs, Customize rhs)
=> lhs.Race != rhs.Race
|| lhs.Gender != rhs.Gender
|| lhs.BodyType != rhs.BodyType
|| lhs.Face != rhs.Face
|| lhs.Race == Race.Hyur && lhs.Clan != rhs.Clan;
public static void SetVisor(Human* data, bool on)

View file

@ -69,16 +69,4 @@ public unsafe partial class RedrawManager
//
//return _flagSlotForUpdateHook.Original(drawObject, slotIdx, data);
}
private void HandleEquipUpdate(nint drawObject, EquipSlot slot, ref CharacterArmor data, bool manual)
{
var actor = Glamourer.Penumbra.GameObjectFromDrawObject(drawObject);
var identifier = actor.GetIdentifier();
if (!_currentManipulations.TryGetDesign(identifier, out var design))
return;
var flag = slot.ToFlag();
var stainFlag = slot.ToStainFlag();
}
}

View file

@ -45,7 +45,7 @@ public unsafe partial class RedrawManager
_ => weapon,
};
}
else if (redrawOnEquality == 1 && _currentManipulations.TryGetDesign(identifier, out var save2))
else if (redrawOnEquality == 1 && _stateManager.TryGetValue(identifier, out var save2))
{
PluginLog.Information($"Loaded weapon from current design for {identifier}.");
//switch (slot)

View file

@ -1,31 +1,104 @@
using System;
using System.Diagnostics;
using System.Linq;
using Dalamud.Hooking;
using Dalamud.Logging;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Glamourer.Customization;
using Glamourer.State;
using Glamourer.Structs;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
namespace Glamourer.Interop;
public class DesignBaseValidator
public partial class Interop : IDisposable
{
private readonly CustomizationManager _manager;
private readonly RestrictedGear _restrictedGear;
public DesignBaseValidator(CustomizationManager manager, RestrictedGear restrictedGear)
public Interop()
{
_manager = manager;
_restrictedGear = restrictedGear;
SignatureHelper.Initialise(this);
_changeJobHook.Enable();
}
public void Dispose()
{
_changeJobHook.Dispose();
}
}
public unsafe partial class Interop
{
private delegate ulong FlagSlotForUpdateDelegateIntern(nint drawObject, uint slot, CharacterArmor* data);
public delegate void FlagSlotForUpdateDelegate(DrawObject drawObject, EquipSlot slot, ref CharacterArmor item);
public unsafe partial class RedrawManager
// This gets called when one of the ten equip items of an existing draw object gets changed.
[Signature(Sigs.FlagSlotForUpdate, DetourName = nameof(FlagSlotForUpdateDetour))]
private readonly Hook<FlagSlotForUpdateDelegateIntern> _flagSlotForUpdateHook = null!;
public event FlagSlotForUpdateDelegate? EquipUpdate;
public ulong FlagSlotForUpdateInterop(DrawObject drawObject, EquipSlot slot, CharacterArmor armor)
=> _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor);
public void UpdateSlot(DrawObject drawObject, EquipSlot slot, CharacterArmor data)
{
InvokeFlagSlotEvent(drawObject, slot, ref data);
FlagSlotForUpdateInterop(drawObject, slot, data);
}
public void UpdateStain(DrawObject drawObject, EquipSlot slot, StainId stain)
{
var armor = drawObject.Equip[slot] with { Stain = stain };
UpdateSlot(drawObject, slot, armor);
}
private ulong FlagSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
{
var slot = slotIdx.ToEquipSlot();
InvokeFlagSlotEvent(drawObject, slot, ref *data);
return _flagSlotForUpdateHook.Original(drawObject, slotIdx, data);
}
private void InvokeFlagSlotEvent(DrawObject drawObject, EquipSlot slot, ref CharacterArmor armor)
{
if (EquipUpdate == null)
return;
foreach (var del in EquipUpdate.GetInvocationList().OfType<FlagSlotForUpdateDelegate>())
{
try
{
del(drawObject, slot, ref armor);
}
catch (Exception ex)
{
Glamourer.Log.Error($"Could not invoke {nameof(EquipUpdate)} Subscriber:\n{ex}");
}
}
}
}
public unsafe partial class Interop
{
public delegate bool ChangeCustomizeDelegate(Human* human, byte* data, byte skipEquipment);
[Signature(Sigs.ChangeCustomize)]
private readonly ChangeCustomizeDelegate _changeCustomize = null!;
public bool UpdateCustomize(Actor actor, CustomizeData customize)
{
Debug.Assert(customize.Data != null, "Customize was invalid.");
if (!actor.Valid || !actor.DrawObject.Valid)
return false;
return _changeCustomize(actor.DrawObject.Pointer, customize.Data, 1);
}
}
public partial class Interop
{
private delegate void ChangeJobDelegate(IntPtr data, uint job);
@ -44,27 +117,21 @@ public unsafe partial class RedrawManager
public unsafe partial class RedrawManager : IDisposable
{
private readonly FixedDesigns _fixedDesigns;
private readonly CurrentManipulations _currentManipulations;
private readonly ActiveDesign.Manager _stateManager;
public RedrawManager(FixedDesigns fixedDesigns, CurrentManipulations currentManipulations)
public RedrawManager(FixedDesigns fixedDesigns, ActiveDesign.Manager stateManager)
{
SignatureHelper.Initialise(this);
Glamourer.Penumbra.CreatingCharacterBase.Event += OnCharacterRedraw;
Glamourer.Penumbra.CreatedCharacterBase.Event += OnCharacterRedrawFinished;
_fixedDesigns = fixedDesigns;
_currentManipulations = currentManipulations;
_fixedDesigns = fixedDesigns;
_stateManager = stateManager;
_flagSlotForUpdateHook.Enable();
_loadWeaponHook.Enable();
_changeJobHook.Enable();
}
public void Dispose()
{
_flagSlotForUpdateHook.Dispose();
_loadWeaponHook.Dispose();
_changeJobHook.Dispose();
Glamourer.Penumbra.CreatingCharacterBase.Event -= OnCharacterRedraw;
Glamourer.Penumbra.CreatedCharacterBase.Event -= OnCharacterRedrawFinished;
}
private void OnCharacterRedraw(Actor actor, uint* modelId, Customize customize, CharacterEquip equip)
@ -76,7 +143,7 @@ public unsafe partial class RedrawManager : IDisposable
// Check if we have a current design in use, or if not if the actor has a fixed design.
var identifier = actor.GetIdentifier();
if (!(_currentManipulations.TryGetDesign(identifier, out var save) || _fixedDesigns.TryGetDesign(identifier, out var save2)))
if (!(_stateManager.TryGetValue(identifier, out var save) || _fixedDesigns.TryGetDesign(identifier, out var save2)))
return;
// Compare game object customize data against draw object customize data for transformations.
@ -116,4 +183,4 @@ public unsafe partial class RedrawManager : IDisposable
{
//SetVisor((Human*)drawObject, true);
}
}
}