From c0ab27189271e37a3e84e1603ed628013d0d3012 Mon Sep 17 00:00:00 2001 From: Raymond Date: Wed, 14 Jul 2021 16:58:06 -0400 Subject: [PATCH 01/12] feat: unsafe actors --- Dalamud/Dalamud.csproj | 2 +- Dalamud/Game/ClientState/Actors/ActorTable.cs | 125 ++++---- .../Actors/Resolvers/BaseResolver.cs | 6 +- .../ClientState/Actors/Resolvers/ClassJob.cs | 10 +- .../ClientState/Actors/Resolvers/World.cs | 10 +- Dalamud/Game/ClientState/Actors/Targets.cs | 4 +- .../Game/ClientState/Actors/Types/Actor.cs | 136 +++++--- .../ClientState/Actors/Types/ActorOffsets.cs | 61 ++++ .../Game/ClientState/Actors/Types/Chara.cs | 91 ++++-- .../Actors/Types/NonPlayer/BattleNpc.cs | 20 +- .../Actors/Types/NonPlayer/EventObj.cs | 15 +- .../ClientState/Actors/Types/NonPlayer/Npc.cs | 17 +- .../ClientState/Actors/Types/PartyMember.cs | 46 +-- .../Actors/Types/PlayerCharacter.cs | 28 +- Dalamud/Game/ClientState/ClientState.cs | 113 +++---- Dalamud/Game/ClientState/Structs/Actor.cs | 296 ------------------ .../Game/ClientState/Structs/GamepadInput.cs | 22 +- Dalamud/GlobalSuppressions.cs | 20 -- .../Interface/Internal/Windows/DataWindow.cs | 2 +- Dalamud/Memory/MemoryHelper.cs | 15 +- 20 files changed, 441 insertions(+), 598 deletions(-) create mode 100644 Dalamud/Game/ClientState/Actors/Types/ActorOffsets.cs delete mode 100644 Dalamud/Game/ClientState/Structs/Actor.cs diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index a64f4f936..28892907e 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -51,7 +51,7 @@ - IDE0003;IDE1006;CS1591;CS1701;CS1702 + IDE0003;IDE0044;IDE1006;CS1591;CS1701;CS1702 diff --git a/Dalamud/Game/ClientState/Actors/ActorTable.cs b/Dalamud/Game/ClientState/Actors/ActorTable.cs index 610841114..39238ae6e 100644 --- a/Dalamud/Game/ClientState/Actors/ActorTable.cs +++ b/Dalamud/Game/ClientState/Actors/ActorTable.cs @@ -1,22 +1,18 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Runtime.InteropServices; using Dalamud.Game.ClientState.Actors.Types; using Dalamud.Game.ClientState.Actors.Types.NonPlayer; -using Dalamud.Game.ClientState.Structs; using JetBrains.Annotations; using Serilog; -using Actor = Dalamud.Game.ClientState.Actors.Types.Actor; - namespace Dalamud.Game.ClientState.Actors { /// - /// This collection represents the currently spawned FFXIV actors. + /// This collection represents the currently spawned FFXIV actors. /// - public class ActorTable : IReadOnlyCollection, ICollection + public sealed partial class ActorTable { private const int ActorTableLength = 424; @@ -56,37 +52,36 @@ namespace Dalamud.Game.ClientState.Actors } } - /// - int IReadOnlyCollection.Count => this.Length; - - /// - int ICollection.Count => this.Length; - - /// - bool ICollection.IsSynchronized => false; - - /// - object ICollection.SyncRoot => this; - private ClientStateAddressResolver Address { get; } /// - /// Get an actor at the specified spawn index. + /// Get an actor at the specified spawn index. /// /// Spawn index. - /// at the specified spawn index. + /// An at the specified spawn index. [CanBeNull] public Actor this[int index] { get { - var ptr = this.GetActorAddress(index); - if (ptr != IntPtr.Zero) - { - return this.CreateActorReference(ptr); - } + var address = this.GetActorAddress(index); + return this[address]; + } + } - return null; + /// + /// Get an actor at the specified address. + /// + /// The actor address. + /// An at the specified address. + public Actor this[IntPtr address] + { + get + { + if (address == IntPtr.Zero) + return null; + + return this.CreateActorReference(address); } } @@ -105,31 +100,6 @@ namespace Dalamud.Game.ClientState.Actors return *(IntPtr*)(this.Address.ActorTable + (8 * index)); } - /// - public IEnumerator GetEnumerator() - { - for (var i = 0; i < ActorTableLength; i++) - { - yield return this[i]; - } - } - - /// - IEnumerator IEnumerable.GetEnumerator() - { - return this.GetEnumerator(); - } - - /// - void ICollection.CopyTo(Array array, int index) - { - for (var i = 0; i < this.Length; i++) - { - array.SetValue(this[i], index); - index++; - } - } - /// /// Create a reference to a FFXIV actor. /// @@ -145,17 +115,54 @@ namespace Dalamud.Game.ClientState.Actors var objKind = *(ObjectKind*)(offset + ActorOffsets.ObjectKind); - // TODO: This is for compatibility with legacy actor classes - superseded once ready - var actorStruct = Marshal.PtrToStructure(offset); - return objKind switch { - ObjectKind.Player => new PlayerCharacter(offset, actorStruct, this.dalamud), - ObjectKind.BattleNpc => new BattleNpc(offset, actorStruct, this.dalamud), - ObjectKind.EventObj => new EventObj(offset, actorStruct, this.dalamud), - ObjectKind.Companion => new Npc(offset, actorStruct, this.dalamud), - _ => new Actor(offset, actorStruct, this.dalamud), + ObjectKind.Player => new PlayerCharacter(offset, this.dalamud), + ObjectKind.BattleNpc => new BattleNpc(offset, this.dalamud), + ObjectKind.EventObj => new EventObj(offset, this.dalamud), + ObjectKind.Companion => new Npc(offset, this.dalamud), + _ => new Actor(offset, this.dalamud), }; } } + + /// + /// This collection represents the currently spawned FFXIV actors. + /// + public sealed partial class ActorTable : IReadOnlyCollection, ICollection + { + /// + int IReadOnlyCollection.Count => this.Length; + + /// + int ICollection.Count => this.Length; + + /// + bool ICollection.IsSynchronized => false; + + /// + object ICollection.SyncRoot => this; + + /// + public IEnumerator GetEnumerator() + { + for (var i = 0; i < ActorTableLength; i++) + { + yield return this[i]; + } + } + + /// + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + + /// + void ICollection.CopyTo(Array array, int index) + { + for (var i = 0; i < this.Length; i++) + { + array.SetValue(this[i], index); + index++; + } + } + } } diff --git a/Dalamud/Game/ClientState/Actors/Resolvers/BaseResolver.cs b/Dalamud/Game/ClientState/Actors/Resolvers/BaseResolver.cs index 728939255..035b8b817 100644 --- a/Dalamud/Game/ClientState/Actors/Resolvers/BaseResolver.cs +++ b/Dalamud/Game/ClientState/Actors/Resolvers/BaseResolver.cs @@ -5,20 +5,18 @@ namespace Dalamud.Game.ClientState.Actors.Resolvers /// public abstract class BaseResolver { - private Dalamud dalamud; - /// /// Initializes a new instance of the class. /// /// The Dalamud instance. internal BaseResolver(Dalamud dalamud) { - this.dalamud = dalamud; + this.Dalamud = dalamud; } /// /// Gets the Dalamud instance. /// - internal Dalamud Dalamud => this.dalamud; + private protected Dalamud Dalamud { get; } } } diff --git a/Dalamud/Game/ClientState/Actors/Resolvers/ClassJob.cs b/Dalamud/Game/ClientState/Actors/Resolvers/ClassJob.cs index 19fea14a7..cb4fd771f 100644 --- a/Dalamud/Game/ClientState/Actors/Resolvers/ClassJob.cs +++ b/Dalamud/Game/ClientState/Actors/Resolvers/ClassJob.cs @@ -5,11 +5,6 @@ namespace Dalamud.Game.ClientState.Actors.Resolvers /// public class ClassJob : BaseResolver { - /// - /// ID of the ClassJob. - /// - public readonly uint Id; - /// /// Initializes a new instance of the class. /// Set up the ClassJob resolver with the provided ID. @@ -22,6 +17,11 @@ namespace Dalamud.Game.ClientState.Actors.Resolvers this.Id = id; } + /// + /// Gets the ID of the ClassJob. + /// + public uint Id { get; } + /// /// Gets GameData linked to this ClassJob. /// diff --git a/Dalamud/Game/ClientState/Actors/Resolvers/World.cs b/Dalamud/Game/ClientState/Actors/Resolvers/World.cs index 6a5d437f0..993b3dde5 100644 --- a/Dalamud/Game/ClientState/Actors/Resolvers/World.cs +++ b/Dalamud/Game/ClientState/Actors/Resolvers/World.cs @@ -5,11 +5,6 @@ namespace Dalamud.Game.ClientState.Actors.Resolvers /// public class World : BaseResolver { - /// - /// ID of the world. - /// - public readonly uint Id; - /// /// Initializes a new instance of the class. /// Set up the world resolver with the provided ID. @@ -22,6 +17,11 @@ namespace Dalamud.Game.ClientState.Actors.Resolvers this.Id = id; } + /// + /// Gets the ID of the world. + /// + public uint Id { get; } + /// /// Gets GameData linked to this world. /// diff --git a/Dalamud/Game/ClientState/Actors/Targets.cs b/Dalamud/Game/ClientState/Actors/Targets.cs index 98f36d4e3..154aa446d 100644 --- a/Dalamud/Game/ClientState/Actors/Targets.cs +++ b/Dalamud/Game/ClientState/Actors/Targets.cs @@ -11,8 +11,8 @@ namespace Dalamud.Game.ClientState.Actors /// public sealed class Targets { - private Dalamud dalamud; - private ClientStateAddressResolver address; + private readonly Dalamud dalamud; + private readonly ClientStateAddressResolver address; /// /// Initializes a new instance of the class. diff --git a/Dalamud/Game/ClientState/Actors/Types/Actor.cs b/Dalamud/Game/ClientState/Actors/Types/Actor.cs index a38daf5b0..6065ab48e 100644 --- a/Dalamud/Game/ClientState/Actors/Types/Actor.cs +++ b/Dalamud/Game/ClientState/Actors/Types/Actor.cs @@ -1,101 +1,155 @@ using System; +using System.Runtime.InteropServices; using Dalamud.Game.ClientState.Structs; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Memory; namespace Dalamud.Game.ClientState.Actors.Types { /// - /// This class represents a basic FFXIV actor. + /// This class represents a basic actor (GameObject) in FFXIV. /// - public class Actor : IEquatable + public unsafe partial class Actor : IEquatable { - private readonly Structs.Actor actorStruct; - private readonly Dalamud dalamud; - - private string name; - /// /// Initializes a new instance of the class. - /// This represents a basic FFXIV actor. /// - /// The memory representation of the base actor. - /// A dalamud reference needed to access game data in Resolvers. /// The address of this actor in memory. - internal Actor(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud) + /// A dalamud reference needed to access game data in Resolvers. + internal Actor(IntPtr address, Dalamud dalamud) { - this.actorStruct = actorStruct; - this.dalamud = dalamud; + this.Dalamud = dalamud; this.Address = address; } /// - /// Gets the position of this . + /// Gets the address of the actor in memory. /// - public Position3 Position => this.ActorStruct.Position; + public IntPtr Address { get; } /// - /// Gets the rotation of this . - /// This ranges from -pi to pi radians. + /// Gets Dalamud itself. /// - public float Rotation => this.ActorStruct.Rotation; + private protected Dalamud Dalamud { get; } + /// + /// This allows you to if (actor) {...} to check for validity. + /// + /// The actor to check. + /// True or false. + public static implicit operator bool(Actor actor) => IsValid(actor); + + /// + /// Gets a value indicating whether this actor is still valid in memory. + /// + /// The actor to check. + /// True or false. + public static bool IsValid(Actor actor) + { + if (actor == null) + return false; + + if (actor.Dalamud.ClientState.LocalContentId == 0) + return false; + + return true; + } + + /// + /// Gets a value indicating whether this actor is still valid in memory. + /// + /// True or false. + public bool IsValid() => IsValid(this); + + /// + bool IEquatable.Equals(Actor other) => this.ActorId == other?.ActorId; + + /// + public override bool Equals(object obj) => ((IEquatable)this).Equals(obj as Actor); + + /// + public override int GetHashCode() => base.GetHashCode(); + } + + /// + /// This class represents a basic actor (GameObject) in FFXIV. + /// + public unsafe partial class Actor + { /// /// Gets the displayname of this . /// - public string Name => this.name ??= Util.GetUTF8String(this.actorStruct.Name); + public SeString Name => MemoryHelper.ReadSeString(this.Address + ActorOffsets.Name, 32); /// /// Gets the actor ID of this . /// - public int ActorId => this.ActorStruct.ActorId; + public uint ActorId => *(uint*)(this.Address + ActorOffsets.ActorId); /// - /// Gets the hitbox radius of this . + /// Gets the data ID for linking to other respective game data. /// - public float HitboxRadius => this.ActorStruct.HitboxRadius; + public uint DataId => *(uint*)(this.Address + ActorOffsets.DataId); + + /// + /// Gets the ID of this GameObject's owner. + /// + public uint OwnerId => *(uint*)(this.Address + ActorOffsets.OwnerId); /// /// Gets the entity kind of this . /// See the ObjectKind enum for possible values. /// - public ObjectKind ObjectKind => this.ActorStruct.ObjectKind; + public ObjectKind ObjectKind => *(ObjectKind*)(this.Address + ActorOffsets.ObjectKind); + + /// + /// Gets the sub kind of this Actor. + /// + public byte SubKind => *(byte*)(this.Address + ActorOffsets.SubKind); + + /// + /// Gets a value indicating whether the actor is friendly. + /// + public bool IsFriendly => *(int*)(this.Address + ActorOffsets.IsFriendly) > 0; /// /// Gets the X distance from the local player in yalms. /// - public byte YalmDistanceX => this.ActorStruct.YalmDistanceFromPlayerX; + public byte YalmDistanceX => *(byte*)(this.Address + ActorOffsets.YalmDistanceFromObjectX); + + /// + /// Gets the target status. + /// + /// + /// This is some kind of enum. It may be . + /// + public byte TargetStatus => *(byte*)(this.Address + ActorOffsets.TargetStatus); /// /// Gets the Y distance from the local player in yalms. /// - public byte YalmDistanceY => this.ActorStruct.YalmDistanceFromPlayerY; + public byte YalmDistanceY => *(byte*)(this.Address + ActorOffsets.YalmDistanceFromObjectY); /// - /// Gets the target of the actor. + /// Gets the position of this . /// - public virtual int TargetActorID => 0; + public Position3 Position => *(Position3*)(this.Address + ActorOffsets.Position); /// - /// Gets status Effects. + /// Gets the rotation of this . + /// This ranges from -pi to pi radians. /// - public StatusEffect[] StatusEffects => this.ActorStruct.UIStatusEffects; + public float Rotation => *(float*)(this.Address + ActorOffsets.Rotation); /// - /// Gets the address of this actor in memory. + /// Gets the hitbox radius of this . /// - public readonly IntPtr Address; + public float HitboxRadius => *(float*)(this.Address + ActorOffsets.HitboxRadius); /// - /// Gets the memory representation of the base actor. + /// Gets the current target of the Actor. /// - internal Structs.Actor ActorStruct => this.actorStruct; - - /// - /// Gets the backing instance. - /// - internal Dalamud Dalamud => this.dalamud; - - /// - bool IEquatable.Equals(Actor other) => this.ActorId == other.ActorId; + public virtual uint TargetActorID => 0; } } diff --git a/Dalamud/Game/ClientState/Actors/Types/ActorOffsets.cs b/Dalamud/Game/ClientState/Actors/Types/ActorOffsets.cs new file mode 100644 index 000000000..e3ef26a5c --- /dev/null +++ b/Dalamud/Game/ClientState/Actors/Types/ActorOffsets.cs @@ -0,0 +1,61 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Dalamud.Game.ClientState.Actors.Types +{ + /// + /// Memory offsets for the type and all that inherit from it. + /// + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Document the offset usage instead.")] + public static class ActorOffsets + { + // GameObject(Actor) + // GameObject :: Character + // GameObject :: Character :: BattleChara + // GameObject :: Character :: Companion + + public const int Name = 0x30; + public const int ActorId = 0x74; + public const int DataId = 0x80; + public const int OwnerId = 0x84; + public const int ObjectKind = 0x8C; + public const int SubKind = 0x8D; + public const int IsFriendly = 0x8E; + public const int YalmDistanceFromObjectX = 0x90; + public const int TargetStatus = 0x91; + public const int YalmDistanceFromObjectY = 0x92; + public const int Position = 0xA0; + public const int Rotation = 0xB0; + public const int HitboxRadius = 0xC0; + // End GameObject 0x1A0 + + public const int CurrentHp = 0x1C4; + public const int MaxHp = 0x1C8; + public const int CurrentMp = 0x1CC; + public const int MaxMp = 0x1D0; + public const int CurrentGp = 0x1D4; + public const int MaxGp = 0x1D6; + public const int CurrentCp = 0x1D8; + public const int MaxCp = 0x1DA; + public const int ClassJob = 0x1E2; + public const int Level = 0x1E3; + public const int PlayerCharacterTargetActorId = 0x230; + public const int Customize = 0x1898; + public const int CompanyTag = 0x18B2; + public const int BattleNpcTargetActorId = 0x18D8; + public const int NameId = 0x1940; + public const int CurrentWorld = 0x195C; + public const int HomeWorld = 0x195E; + public const int StatusFlags = 0x19A0; + // End Character 0x19B0 + // End Companion 0x19C0 + + public const int UIStatusEffects = 0x19F8; + public const int IsCasting = 0x1B80; + public const int IsCasting2 = 0x1B82; + public const int CurrentCastSpellActionId = 0x1B84; + public const int CurrentCastTargetActorId = 0x1B90; + public const int CurrentCastTime = 0x1BB4; + public const int TotalCastTime = 0x1BB8; + // End BattleChara 0x2C00 + } +} diff --git a/Dalamud/Game/ClientState/Actors/Types/Chara.cs b/Dalamud/Game/ClientState/Actors/Types/Chara.cs index 4977801e9..1a60b7499 100644 --- a/Dalamud/Game/ClientState/Actors/Types/Chara.cs +++ b/Dalamud/Game/ClientState/Actors/Types/Chara.cs @@ -1,85 +1,124 @@ using System; using Dalamud.Game.ClientState.Actors.Resolvers; +using Dalamud.Game.ClientState.Structs; +using Dalamud.Memory; namespace Dalamud.Game.ClientState.Actors.Types { /// /// This class represents the base for non-static entities. /// - public class Chara : Actor + public unsafe class Chara : Actor { /// /// Initializes a new instance of the class. /// This represents a non-static entity. /// - /// The memory representation of the base actor. - /// A dalamud reference needed to access game data in Resolvers. /// The address of this actor in memory. - internal Chara(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud) - : base(address, actorStruct, dalamud) + /// A dalamud reference needed to access game data in Resolvers. + internal Chara(IntPtr address, Dalamud dalamud) + : base(address, dalamud) { } - /// - /// Gets the level of this Chara. - /// - public byte Level => this.ActorStruct.Level; - - /// - /// Gets the ClassJob of this Chara. - /// - public ClassJob ClassJob => new(this.ActorStruct.ClassJob, this.Dalamud); - /// /// Gets the current HP of this Chara. /// - public int CurrentHp => this.ActorStruct.CurrentHp; + public uint CurrentHp => *(uint*)(this.Address + ActorOffsets.CurrentHp); /// /// Gets the maximum HP of this Chara. /// - public int MaxHp => this.ActorStruct.MaxHp; + public uint MaxHp => *(uint*)(this.Address + ActorOffsets.MaxHp); /// /// Gets the current MP of this Chara. /// - public int CurrentMp => this.ActorStruct.CurrentMp; + public uint CurrentMp => *(uint*)(this.Address + ActorOffsets.CurrentMp); /// /// Gets the maximum MP of this Chara. /// - public int MaxMp => this.ActorStruct.MaxMp; + public uint MaxMp => *(uint*)(this.Address + ActorOffsets.MaxMp); /// /// Gets the current GP of this Chara. /// - public int CurrentGp => this.ActorStruct.CurrentGp; + public uint CurrentGp => *(uint*)(this.Address + ActorOffsets.CurrentGp); /// /// Gets the maximum GP of this Chara. /// - public int MaxGp => this.ActorStruct.MaxGp; + public uint MaxGp => *(uint*)(this.Address + ActorOffsets.MaxGp); /// /// Gets the current CP of this Chara. /// - public int CurrentCp => this.ActorStruct.CurrentCp; + public uint CurrentCp => *(uint*)(this.Address + ActorOffsets.CurrentCp); /// /// Gets the maximum CP of this Chara. /// - public int MaxCp => this.ActorStruct.MaxCp; + public uint MaxCp => *(uint*)(this.Address + ActorOffsets.MaxCp); + + /// + /// Gets the ClassJob of this Chara. + /// + public ClassJob ClassJob => new(*(byte*)(this.Address + ActorOffsets.ClassJob), this.Dalamud); + + /// + /// Gets the level of this Chara. + /// + public byte Level => *(byte*)(this.Address + ActorOffsets.Level); /// /// Gets a byte array describing the visual appearance of this Chara. /// Indexed by . /// - public byte[] Customize => this.ActorStruct.Customize; + public byte[] Customize => MemoryHelper.Read(this.Address + ActorOffsets.Customize, 28); /// - /// Gets status Effects. + /// Gets the status flags. /// - public StatusFlags StatusFlags => this.ActorStruct.StatusFlags; + public StatusFlags StatusFlags => *(StatusFlags*)(this.Address + ActorOffsets.StatusFlags); + + /// + /// Gets the current status effects. + /// + /// + /// This copies every time it is invoked, so make sure to only grab it once. + /// + public StatusEffect[] StatusEffects => MemoryHelper.Read(this.Address + ActorOffsets.UIStatusEffects, 20, true); + + /// + /// Gets a value indicating whether the actor is currently casting. + /// + public bool IsCasting => *(int*)(this.Address + ActorOffsets.IsCasting) > 0; + + /// + /// Gets a value indicating whether the actor is currently casting (again?). + /// + public bool IsCasting2 => *(int*)(this.Address + ActorOffsets.IsCasting2) > 0; + + /// + /// Gets the spell action ID currently being cast by the actor. + /// + public uint CurrentCastSpellActionId => *(uint*)(this.Address + ActorOffsets.CurrentCastSpellActionId); + + /// + /// Gets the actor ID of the target currently being cast at by the actor. + /// + public uint CurrentCastTargetActorId => *(uint*)(this.Address + ActorOffsets.CurrentCastTargetActorId); + + /// + /// Gets the current casting time of the spell being cast by the actor. + /// + public float CurrentCastTime => *(float*)(this.Address + ActorOffsets.CurrentCastTime); + + /// + /// Gets the total casting time of the spell being cast by the actor. + /// + public float TotalCastTime => *(float*)(this.Address + ActorOffsets.TotalCastTime); } } diff --git a/Dalamud/Game/ClientState/Actors/Types/NonPlayer/BattleNpc.cs b/Dalamud/Game/ClientState/Actors/Types/NonPlayer/BattleNpc.cs index bcc0ebe70..3fa8f0c28 100644 --- a/Dalamud/Game/ClientState/Actors/Types/NonPlayer/BattleNpc.cs +++ b/Dalamud/Game/ClientState/Actors/Types/NonPlayer/BattleNpc.cs @@ -5,33 +5,27 @@ namespace Dalamud.Game.ClientState.Actors.Types.NonPlayer /// /// This class represents a battle NPC. /// - public class BattleNpc : Npc + public unsafe class BattleNpc : Npc { /// /// Initializes a new instance of the class. /// Set up a new BattleNpc with the provided memory representation. /// - /// The memory representation of the base actor. - /// A dalamud reference needed to access game data in Resolvers. /// The address of this actor in memory. - internal BattleNpc(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud) - : base(address, actorStruct, dalamud) + /// A dalamud reference needed to access game data in Resolvers. + internal BattleNpc(IntPtr address, Dalamud dalamud) + : base(address, dalamud) { } /// /// Gets the BattleNpc of this BattleNpc. /// - public BattleNpcSubKind BattleNpcKind => (BattleNpcSubKind)this.ActorStruct.SubKind; + public BattleNpcSubKind BattleNpcKind => *(BattleNpcSubKind*)(this.Address + ActorOffsets.SubKind); /// - /// Gets the ID of this BattleNpc's owner. + /// Gets the target of the Battle NPC. /// - public int OwnerId => this.ActorStruct.OwnerId; - - /// - /// Gets target of the Battle NPC. - /// - public override int TargetActorID => this.ActorStruct.BattleNpcTargetActorId; + public override uint TargetActorID => *(uint*)(this.Address + ActorOffsets.BattleNpcTargetActorId); } } diff --git a/Dalamud/Game/ClientState/Actors/Types/NonPlayer/EventObj.cs b/Dalamud/Game/ClientState/Actors/Types/NonPlayer/EventObj.cs index fd112c247..7d9dce443 100644 --- a/Dalamud/Game/ClientState/Actors/Types/NonPlayer/EventObj.cs +++ b/Dalamud/Game/ClientState/Actors/Types/NonPlayer/EventObj.cs @@ -5,23 +5,22 @@ namespace Dalamud.Game.ClientState.Actors.Types.NonPlayer /// /// This class represents an EventObj. /// - public class EventObj : Actor + public unsafe class EventObj : Actor { /// /// Initializes a new instance of the class. - /// This represents an Event Object. + /// Set up a new EventObj with the provided memory representation. /// - /// The memory representation of the base actor. - /// A dalamud reference needed to access game data in Resolvers. /// The address of this actor in memory. - internal EventObj(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud) - : base(address, actorStruct, dalamud) + /// A dalamud reference needed to access game data in Resolvers. + internal EventObj(IntPtr address, Dalamud dalamud) + : base(address, dalamud) { } /// - /// Gets the data ID of the NPC linking to their respective game data. + /// Gets the event object ID of the linking to their respective game data. /// - public int DataId => this.ActorStruct.DataId; + public uint EventObjectId => *(uint*)(this.Address + ActorOffsets.DataId); } } diff --git a/Dalamud/Game/ClientState/Actors/Types/NonPlayer/Npc.cs b/Dalamud/Game/ClientState/Actors/Types/NonPlayer/Npc.cs index 4571203c9..a3597570c 100644 --- a/Dalamud/Game/ClientState/Actors/Types/NonPlayer/Npc.cs +++ b/Dalamud/Game/ClientState/Actors/Types/NonPlayer/Npc.cs @@ -5,28 +5,27 @@ namespace Dalamud.Game.ClientState.Actors.Types.NonPlayer /// /// This class represents a NPC. /// - public class Npc : Chara + public unsafe class Npc : Chara { /// /// Initializes a new instance of the class. - /// This represents a Non-playable Character. + /// Set up a new NPC with the provided memory representation. /// - /// The memory representation of the base actor. - /// A dalamud reference needed to access game data in Resolvers. /// The address of this actor in memory. - internal Npc(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud) - : base(address, actorStruct, dalamud) + /// A dalamud reference needed to access game data in Resolvers. + internal Npc(IntPtr address, Dalamud dalamud) + : base(address, dalamud) { } /// - /// Gets the data ID of the NPC linking to their respective game data. + /// Gets the data ID of the NPC linking to their assoicated BNpcBase data. /// - public int DataId => this.ActorStruct.DataId; + public uint BaseId => *(uint*)(this.Address + ActorOffsets.DataId); /// /// Gets the name ID of the NPC linking to their respective game data. /// - public int NameId => this.ActorStruct.NameId; + public uint NameId => *(uint*)(this.Address + ActorOffsets.NameId); } } diff --git a/Dalamud/Game/ClientState/Actors/Types/PartyMember.cs b/Dalamud/Game/ClientState/Actors/Types/PartyMember.cs index 74cf9c97c..2e2d67f54 100644 --- a/Dalamud/Game/ClientState/Actors/Types/PartyMember.cs +++ b/Dalamud/Game/ClientState/Actors/Types/PartyMember.cs @@ -1,4 +1,5 @@ -using System.Runtime.InteropServices; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Memory; namespace Dalamud.Game.ClientState.Actors.Types { @@ -7,26 +8,6 @@ namespace Dalamud.Game.ClientState.Actors.Types /// public class PartyMember { - /// - /// The name of the character. - /// - public string CharacterName; - - /// - /// Unknown. - /// - public long Unknown; - - /// - /// The actor object that corresponds to this party member. - /// - public Actor Actor; - - /// - /// The kind or type of actor. - /// - public ObjectKind ObjectKind; - /// /// Initializes a new instance of the class. /// @@ -34,9 +15,10 @@ namespace Dalamud.Game.ClientState.Actors.Types /// The interop data struct. public PartyMember(ActorTable table, Structs.PartyMember rawData) { - this.CharacterName = Marshal.PtrToStringAnsi(rawData.namePtr); + this.CharacterName = MemoryHelper.ReadSeString(rawData.namePtr); this.Unknown = rawData.unknown; this.Actor = null; + for (var i = 0; i < table.Length; i++) { if (table[i] != null && table[i].ActorId == rawData.actorId) @@ -48,5 +30,25 @@ namespace Dalamud.Game.ClientState.Actors.Types this.ObjectKind = rawData.objectKind; } + + /// + /// Gets the name of the character. + /// + public SeString CharacterName { get; } + + /// + /// Gets something unknown. + /// + public long Unknown { get; } + + /// + /// Gets the actor object that corresponds to this party member. + /// + public Actor Actor { get; } + + /// + /// Gets the kind or type of actor. + /// + public ObjectKind ObjectKind { get; } } } diff --git a/Dalamud/Game/ClientState/Actors/Types/PlayerCharacter.cs b/Dalamud/Game/ClientState/Actors/Types/PlayerCharacter.cs index 44d01f783..4be3d7a61 100644 --- a/Dalamud/Game/ClientState/Actors/Types/PlayerCharacter.cs +++ b/Dalamud/Game/ClientState/Actors/Types/PlayerCharacter.cs @@ -1,51 +1,45 @@ using System; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; using Dalamud.Game.ClientState.Actors.Resolvers; -using Dalamud.Game.ClientState.Structs; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Memory; namespace Dalamud.Game.ClientState.Actors.Types { /// /// This class represents a player character. /// - public class PlayerCharacter : Chara + public unsafe class PlayerCharacter : Chara { /// /// Initializes a new instance of the class. /// This represents a player character. /// - /// The memory representation of the base actor. - /// A dalamud reference needed to access game data in Resolvers. /// The address of this actor in memory. - internal PlayerCharacter(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud) - : base(address, actorStruct, dalamud) + /// A dalamud reference needed to access game data in Resolvers. + internal PlayerCharacter(IntPtr address, Dalamud dalamud) + : base(address, dalamud) { - var companyTagBytes = new byte[5]; - Marshal.Copy(this.Address + ActorOffsets.CompanyTag, companyTagBytes, 0, companyTagBytes.Length); - this.CompanyTag = Encoding.UTF8.GetString(companyTagBytes.TakeWhile(c => c != 0x0).ToArray()); } /// /// Gets the current world of the character. /// - public World CurrentWorld => new(this.ActorStruct.CurrentWorld, this.Dalamud); + public World CurrentWorld => new(*(ushort*)(this.Address + ActorOffsets.CurrentWorld), this.Dalamud); /// /// Gets the home world of the character. /// - public World HomeWorld => new(this.ActorStruct.HomeWorld, this.Dalamud); + public World HomeWorld => new(*(ushort*)(this.Address + ActorOffsets.HomeWorld), this.Dalamud); /// /// Gets the Free Company tag of this player. /// - public string CompanyTag { get; private set; } + public SeString CompanyTag => MemoryHelper.ReadSeString(this.Address + ActorOffsets.CompanyTag, 6); /// - /// Gets the target of the PlayerCharacter. + /// Gets the target actor ID of the PlayerCharacter. /// - public override int TargetActorID => this.ActorStruct.PlayerCharacterTargetActorId; + public override uint TargetActorID => *(uint*)(this.Address + ActorOffsets.PlayerCharacterTargetActorId); } } diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index d593786d3..d67bac813 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -17,56 +17,6 @@ namespace Dalamud.Game.ClientState /// public sealed class ClientState : INotifyPropertyChanged, IDisposable { - /// - /// The table of all present actors. - /// - public readonly ActorTable Actors; - - /// - /// Gets the language of the client. - /// - public readonly ClientLanguage ClientLanguage; - - /// - /// The current Territory the player resides in. - /// - public ushort TerritoryType; - - /// - /// The class facilitating Job Gauge data access. - /// - public JobGauges JobGauges; - - /// - /// The class facilitating party list data access. - /// - public PartyList PartyList; - - /// - /// Provides access to the keypress state of keyboard keys in game. - /// - public KeyState KeyState; - - /// - /// Provides access to the button state of gamepad buttons in game. - /// - public GamepadState GamepadState; - - /// - /// Provides access to client conditions/player state. Allows you to check if a player is in a duty, mounted, etc. - /// - public Condition Condition; - - /// - /// The class facilitating target data access. - /// - public Targets Targets; - - /// - /// Event that gets fired when the current Territory changes. - /// - public EventHandler TerritoryChanged; - private readonly Dalamud dalamud; private readonly ClientStateAddressResolver address; private readonly Hook setupTerritoryTypeHook; @@ -122,6 +72,11 @@ namespace Dalamud.Game.ClientState public event PropertyChangedEventHandler PropertyChanged; #pragma warning restore + /// + /// Event that gets fired when the current Territory changes. + /// + public event EventHandler TerritoryChanged; + /// /// Event that fires when a character is logging in. /// @@ -137,22 +92,56 @@ namespace Dalamud.Game.ClientState /// public event EventHandler CfPop; + /// + /// Gets the table of all present actors. + /// + public ActorTable Actors { get; } + + /// + /// Gets the language of the client. + /// + public ClientLanguage ClientLanguage { get; } + + /// + /// Gets the class facilitating Job Gauge data access. + /// + public JobGauges JobGauges { get; } + + /// + /// Gets the class facilitating party list data access. + /// + public PartyList PartyList { get; } + + /// + /// Gets access to the keypress state of keyboard keys in game. + /// + public KeyState KeyState { get; } + + /// + /// Gets access to the button state of gamepad buttons in game. + /// + public GamepadState GamepadState { get; } + + /// + /// Gets access to client conditions/player state. Allows you to check if a player is in a duty, mounted, etc. + /// + public Condition Condition { get; } + + /// + /// Gets the class facilitating target data access. + /// + public Targets Targets { get; } + + /// + /// Gets the current Territory the player resides in. + /// + public ushort TerritoryType { get; private set; } + /// /// Gets the local player character, if one is present. /// [CanBeNull] - public PlayerCharacter LocalPlayer - { - get - { - var actor = this.Actors[0]; - - if (actor is PlayerCharacter pc) - return pc; - - return null; - } - } + public PlayerCharacter LocalPlayer => this.Actors[0] as PlayerCharacter; /// /// Gets the content ID of the local character. diff --git a/Dalamud/Game/ClientState/Structs/Actor.cs b/Dalamud/Game/ClientState/Structs/Actor.cs deleted file mode 100644 index 6f882f6f9..000000000 --- a/Dalamud/Game/ClientState/Structs/Actor.cs +++ /dev/null @@ -1,296 +0,0 @@ -using System.Runtime.InteropServices; - -using Dalamud.Game.ClientState.Actors; - -namespace Dalamud.Game.ClientState.Structs -{ - /// - /// Native memory representation of an FFXIV actor. - /// - [StructLayout(LayoutKind.Explicit, Pack = 2)] - public struct Actor - { - /// - /// The actor name. - /// - [FieldOffset(ActorOffsets.Name)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)] - public byte[] Name; - - /// - /// The actor's internal id. - /// - [FieldOffset(ActorOffsets.ActorId)] - public int ActorId; - - /// - /// The actor's data id. - /// - [FieldOffset(ActorOffsets.DataId)] - public int DataId; - - /// - /// The actor's owner id. This is useful for pets, summons, and the like. - /// - [FieldOffset(ActorOffsets.OwnerId)] - public int OwnerId; - - /// - /// The type or kind of actor. - /// - [FieldOffset(ActorOffsets.ObjectKind)] - public ObjectKind ObjectKind; - - /// - /// The sub-type or sub-kind of actor. - /// - [FieldOffset(ActorOffsets.SubKind)] - public byte SubKind; - - /// - /// Whether the actor is friendly. - /// - [FieldOffset(ActorOffsets.IsFriendly)] - public bool IsFriendly; - - /// - /// The horizontal distance in game units from the player. - /// - [FieldOffset(ActorOffsets.YalmDistanceFromPlayerX)] - public byte YalmDistanceFromPlayerX; - - /// - /// The player target status. - /// - /// - /// This is some kind of enum. - /// - [FieldOffset(ActorOffsets.PlayerTargetStatus)] - public byte PlayerTargetStatus; - - /// - /// The vertical distance in game units from the player. - /// - [FieldOffset(ActorOffsets.YalmDistanceFromPlayerY)] - public byte YalmDistanceFromPlayerY; - - /// - /// The (X,Z,Y) position of the actor. - /// - [FieldOffset(ActorOffsets.Position)] - public Position3 Position; - - /// - /// The rotation of the actor. - /// - /// - /// The rotation is around the vertical axis (yaw), from -pi to pi radians. - /// - [FieldOffset(ActorOffsets.Rotation)] - public float Rotation; - - /// - /// The hitbox radius of the actor. - /// - [FieldOffset(ActorOffsets.HitboxRadius)] - public float HitboxRadius; - - /// - /// The current HP of the actor. - /// - [FieldOffset(ActorOffsets.CurrentHp)] - public int CurrentHp; - - /// - /// The max HP of the actor. - /// - [FieldOffset(ActorOffsets.MaxHp)] - public int MaxHp; - - /// - /// The current MP of the actor. - /// - [FieldOffset(ActorOffsets.CurrentMp)] - public int CurrentMp; - - /// - /// The max MP of the actor. - /// - [FieldOffset(ActorOffsets.MaxMp)] - public short MaxMp; - - /// - /// The current GP of the actor. - /// - [FieldOffset(ActorOffsets.CurrentGp)] - public short CurrentGp; - - /// - /// The max GP of the actor. - /// - [FieldOffset(ActorOffsets.MaxGp)] - public short MaxGp; - - /// - /// The current CP of the actor. - /// - [FieldOffset(ActorOffsets.CurrentCp)] - public short CurrentCp; - - /// - /// The max CP of the actor. - /// - [FieldOffset(ActorOffsets.MaxCp)] - public short MaxCp; - - /// - /// The class-job of the actor. - /// - [FieldOffset(ActorOffsets.ClassJob)] - public byte ClassJob; - - /// - /// The level of the actor. - /// - [FieldOffset(ActorOffsets.Level)] - public byte Level; - - /// - /// The (player character) actor ID being targeted by the actor. - /// - [FieldOffset(ActorOffsets.PlayerCharacterTargetActorId)] - public int PlayerCharacterTargetActorId; - - /// - /// The customization byte/bitfield of the actor. - /// - [FieldOffset(ActorOffsets.Customize)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)] - public byte[] Customize; - - // Normally pack=2 should work, but ByTVal or Injection breaks this. - // [FieldOffset(ActorOffsets.CompanyTag)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] public string CompanyTag; - - /// - /// The (battle npc) actor ID being targeted by the actor. - /// - [FieldOffset(ActorOffsets.BattleNpcTargetActorId)] - public int BattleNpcTargetActorId; - - /// - /// The name ID of the actor. - /// - [FieldOffset(ActorOffsets.NameId)] - public int NameId; - - /// - /// The current world ID of the actor. - /// - [FieldOffset(ActorOffsets.CurrentWorld)] - public ushort CurrentWorld; - - /// - /// The home world ID of the actor. - /// - [FieldOffset(ActorOffsets.HomeWorld)] - public ushort HomeWorld; - - /// - /// Whether the actor is currently casting. - /// - [FieldOffset(ActorOffsets.IsCasting)] - public bool IsCasting; - - /// - /// Whether the actor is currently casting (dup?). - /// - [FieldOffset(ActorOffsets.IsCasting2)] - public bool IsCasting2; - - /// - /// The spell action ID currently being cast by the actor. - /// - [FieldOffset(ActorOffsets.CurrentCastSpellActionId)] - public uint CurrentCastSpellActionId; - - /// - /// The actor ID of the target currently being cast at by the actor. - /// - [FieldOffset(ActorOffsets.CurrentCastTargetActorId)] - public uint CurrentCastTargetActorId; - - /// - /// The current casting time of the spell being cast by the actor. - /// - [FieldOffset(ActorOffsets.CurrentCastTime)] - public float CurrentCastTime; - - /// - /// The total casting time of the spell being cast by the actor. - /// - [FieldOffset(ActorOffsets.TotalCastTime)] - public float TotalCastTime; - - /// - /// Actor status flags. - /// - [FieldOffset(ActorOffsets.StatusFlags)] - public StatusFlags StatusFlags; - - /// - /// The array of status effects that the actor is currently affected by. - /// - [FieldOffset(ActorOffsets.UIStatusEffects)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)] - public StatusEffect[] UIStatusEffects; - } - - /// - /// Memory offsets for the type. - /// - public static class ActorOffsets - { - // Reference https://github.com/FFXIVAPP/sharlayan-resources/blob/master/structures/5.4/x64.json for more - public const int Name = 48; // 0x0030 - public const int ActorId = 116; // 0x0074 - // public const int ??? = 120; // 0x0078 NPCID1 - public const int DataId = 128; // 0x0080 NPCID2 - public const int OwnerId = 132; // 0x0084 - public const int ObjectKind = 140; // 0x008C Type - public const int SubKind = 141; // 0x008D - public const int IsFriendly = 142; // 0x008E - public const int YalmDistanceFromPlayerX = 144; // 0x0090 - public const int PlayerTargetStatus = 145; // 0x0091 - public const int YalmDistanceFromPlayerY = 146; // 0x0092 Distance - public const int Position = 160; // 0x00A0 (X,Z,Y) - public const int Rotation = 176; // 0x00B0 Heading - public const int HitboxRadius = 192; // 0x00C0 - public const int CurrentHp = 452; // 0x01C4 HPCurrent - public const int MaxHp = 456; // 0x01C8 HPMax - public const int CurrentMp = 460; // 0x01CC MPCurrent - public const int MaxMp = 464; // 0x01D0 MPMax - public const int CurrentGp = 468; // 0x01D4 GPCurrent - public const int MaxGp = 470; // 0x01D6 GPMax - public const int CurrentCp = 472; // 0x01D8 CPCurrent - public const int MaxCp = 474; // 0x01DA CPMax - public const int ClassJob = 482; // 0x01E2 Job - public const int Level = 483; // 0x01E3 Level - public const int PlayerCharacterTargetActorId = 560; // 0x01F0 TargetID - - public const int Customize = 0x1898; // Needs verification - public const int CompanyTag = 0x18B2; - public const int BattleNpcTargetActorId = 0x18D8; // Needs verification - public const int NameId = 0x1940; // Needs verification - public const int CurrentWorld = 0x195C; - public const int HomeWorld = 0x195E; - - public const int IsCasting = 0x1B80; - public const int IsCasting2 = 0x1B82; - public const int CurrentCastSpellActionId = 0x1B84; - public const int CurrentCastTargetActorId = 0x1B90; - public const int CurrentCastTime = 0x1BB4; - public const int TotalCastTime = 0x1BB8; - public const int StatusFlags = 0x19A0; - public const int UIStatusEffects = 0x19F8; - } -} diff --git a/Dalamud/Game/ClientState/Structs/GamepadInput.cs b/Dalamud/Game/ClientState/Structs/GamepadInput.cs index ce7440b61..df80b0ef4 100644 --- a/Dalamud/Game/ClientState/Structs/GamepadInput.cs +++ b/Dalamud/Game/ClientState/Structs/GamepadInput.cs @@ -1,4 +1,4 @@ -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace Dalamud.Game.ClientState.Structs { @@ -40,25 +40,37 @@ namespace Dalamud.Game.ClientState.Structs /// /// Raw input, set the whole time while a button is held. See for the mapping. /// + /// + /// This is a bitfield. + /// [FieldOffset(0x98)] - public ushort ButtonsRaw; // bitfield + public ushort ButtonsRaw; /// /// Button pressed, set once when the button is pressed. See for the mapping. /// + /// + /// This is a bitfield. + /// [FieldOffset(0x9C)] - public ushort ButtonsPressed; // bitfield + public ushort ButtonsPressed; /// /// Button released input, set once right after the button is not hold anymore. See for the mapping. /// + /// + /// This is a bitfield. + /// [FieldOffset(0xA0)] - public ushort ButtonsReleased; // bitfield + public ushort ButtonsReleased; /// /// Repeatedly emits the held button input in fixed intervals. See for the mapping. /// + /// + /// This is a bitfield. + /// [FieldOffset(0xA4)] - public ushort ButtonsRepeat; // bitfield + public ushort ButtonsRepeat; } } diff --git a/Dalamud/GlobalSuppressions.cs b/Dalamud/GlobalSuppressions.cs index 73bbf6329..15bce83e0 100644 --- a/Dalamud/GlobalSuppressions.cs +++ b/Dalamud/GlobalSuppressions.cs @@ -37,10 +37,8 @@ using System.Diagnostics.CodeAnalysis; // Offsets.cs [assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static elements should appear before instance elements", Justification = "Offset classes goto the end of file.", Scope = "type", Target = "~T:Dalamud.Game.ClientState.Actors.TargetOffsets")] [assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Group offset classes with the relevant class.", Scope = "type", Target = "~T:Dalamud.Game.ClientState.Actors.TargetOffsets")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Group offset classes with the relevant class.", Scope = "type", Target = "~T:Dalamud.Game.ClientState.Structs.ActorOffsets")] [assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Group offset classes with the relevant class.", Scope = "type", Target = "~T:Dalamud.Game.Internal.Gui.Structs.AddonOffsets")] [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Document the offset usage instead.", Scope = "type", Target = "~T:Dalamud.Game.ClientState.Actors.TargetOffsets")] -[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Document the offset usage instead.", Scope = "type", Target = "~T:Dalamud.Game.ClientState.Structs.ActorOffsets")] [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Document the offset usage instead.", Scope = "type", Target = "~T:Dalamud.Game.Internal.Gui.Structs.AddonOffsets")] // Breaking api changes: these should be split into a PartyFinder subdirectory @@ -57,33 +55,15 @@ using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "breaking api change", Scope = "member", Target = "~E:Dalamud.Game.Internal.Gui.PartyFinderGui.ReceiveListing")] // Breaking api changes -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.Actors.Resolvers.ClassJob.Id")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.Actors.Resolvers.World.Id")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.Actors.Types.Actor.Address")] [assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.Internal.BaseAddressResolver.DebugScannedValues")] [assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.Internal.Gui.Addon.Addon.Address")] [assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.Internal.Gui.Addon.Addon.addonStruct")] [assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.Internal.Gui.GameGui.GetBaseUIObject")] [assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.Text.SeStringHandling.Payload.DataResolver")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.ClientState.Actors")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.ClientState.TerritoryType")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.ClientState.TerritoryChanged")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.ClientState.ClientLanguage")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.ClientState.JobGauges")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.ClientState.PartyList")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.ClientState.KeyState")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.ClientState.GamepadState")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.ClientState.Condition")] -[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.ClientState.Targets")] [assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "type", Target = "~T:Dalamud.Game.ClientState.Actors.Types.PartyMember")] -[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.Actors.Types.Actor.Address")] -[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.Structs.JobGauge.BLMGauge.NumUmbralHearts")] -[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.Structs.JobGauge.DNCGauge.NumCompleteSteps")] -[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.Structs.JobGauge.DRKGauge.ShadowTimeRemaining")] [assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.Text.SeStringHandling.Payload.START_BYTE")] [assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.Text.SeStringHandling.Payload.END_BYTE")] [assembly: SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Unused, but eventually, maybe.", Scope = "member", Target = "~F:Dalamud.Game.ClientState.PartyList.address")] -[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "breaking api change", Scope = "member", Target = "~E:Dalamud.Game.ClientState.ClientState.CfPop")] [assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static elements should appear before instance elements", Justification = "breaking api change, move to util", Scope = "type", Target = "~T:Dalamud.Game.Text.EnumExtensions")] [assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "breaking api change, move to util", Scope = "type", Target = "~T:Dalamud.Game.Text.EnumExtensions")] [assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.Internal.Framework.StatsHistory")] diff --git a/Dalamud/Interface/Internal/Windows/DataWindow.cs b/Dalamud/Interface/Internal/Windows/DataWindow.cs index 476e52e50..81aab9ca6 100644 --- a/Dalamud/Interface/Internal/Windows/DataWindow.cs +++ b/Dalamud/Interface/Internal/Windows/DataWindow.cs @@ -693,7 +693,7 @@ namespace Dalamud.Interface.Internal.Windows $"{actor.Address.ToInt64():X}:{actor.ActorId:X}[{tag}] - {actor.ObjectKind} - {actor.Name} - X{actor.Position.X} Y{actor.Position.Y} Z{actor.Position.Z} D{actor.YalmDistanceX} R{actor.Rotation} - Target: {actor.TargetActorID:X}\n"; if (actor is Npc npc) - actorString += $" DataId: {npc.DataId} NameId:{npc.NameId}\n"; + actorString += $" DataId: {npc.BaseId} NameId:{npc.NameId}\n"; if (actor is Chara chara) { diff --git a/Dalamud/Memory/MemoryHelper.cs b/Dalamud/Memory/MemoryHelper.cs index f47f48263..b962d637e 100644 --- a/Dalamud/Memory/MemoryHelper.cs +++ b/Dalamud/Memory/MemoryHelper.cs @@ -79,7 +79,7 @@ namespace Dalamud.Memory /// The read in struct array. public static T[] Read(IntPtr memoryAddress, int arrayLength, bool marshal) { - var structSize = SizeOf(); + var structSize = SizeOf(marshal); var value = new T[arrayLength]; for (var i = 0; i < arrayLength; i++) @@ -223,7 +223,18 @@ namespace Dalamud.Memory public static SeString ReadSeString(IntPtr memoryAddress, int maxLength) { ReadRaw(memoryAddress, maxLength, out var buffer); - return seStringManager.Parse(buffer); + + var eos = Array.IndexOf(buffer, (byte)0); + if (eos < 0) + { + return seStringManager.Parse(buffer); + } + else + { + var newBuffer = new byte[eos]; + Buffer.BlockCopy(buffer, 0, newBuffer, 0, eos); + return seStringManager.Parse(newBuffer); + } } #endregion From c3dfe0eb31bd57244eecaa48d3b780b9c3632174 Mon Sep 17 00:00:00 2001 From: Raymond Date: Wed, 14 Jul 2021 16:12:06 -0400 Subject: [PATCH 02/12] Break JobGauges --- .../ClientState/Structs/JobGauge/BLMGauge.cs | 40 +++++++++++-------- .../ClientState/Structs/JobGauge/BRDGauge.cs | 24 +++++++---- .../ClientState/Structs/JobGauge/DNCGauge.cs | 26 +++++++----- .../ClientState/Structs/JobGauge/DRGGauge.cs | 18 ++++++--- .../ClientState/Structs/JobGauge/DRKGauge.cs | 26 +++++++----- .../ClientState/Structs/JobGauge/GNBGauge.cs | 18 ++++++--- .../ClientState/Structs/JobGauge/MCHGauge.cs | 36 +++++++++++------ .../ClientState/Structs/JobGauge/MNKGauge.cs | 32 ++------------- .../ClientState/Structs/JobGauge/NINGauge.cs | 26 ++++++------ .../ClientState/Structs/JobGauge/PLDGauge.cs | 6 ++- .../ClientState/Structs/JobGauge/RDMGauge.cs | 12 ++++-- .../ClientState/Structs/JobGauge/SAMGauge.cs | 18 ++++++--- .../ClientState/Structs/JobGauge/SCHGauge.cs | 24 +++++++---- .../ClientState/Structs/JobGauge/SMNGauge.cs | 24 +++++++---- .../ClientState/Structs/JobGauge/WARGauge.cs | 6 ++- .../ClientState/Structs/JobGauge/WHMGauge.cs | 18 ++++++--- 16 files changed, 207 insertions(+), 147 deletions(-) diff --git a/Dalamud/Game/ClientState/Structs/JobGauge/BLMGauge.cs b/Dalamud/Game/ClientState/Structs/JobGauge/BLMGauge.cs index 694a0d981..e4125745b 100644 --- a/Dalamud/Game/ClientState/Structs/JobGauge/BLMGauge.cs +++ b/Dalamud/Game/ClientState/Structs/JobGauge/BLMGauge.cs @@ -8,35 +8,43 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge [StructLayout(LayoutKind.Explicit)] public struct BLMGauge { - /// - /// Gets the time until the next Polyglot stack in milliseconds. - /// [FieldOffset(0)] - public short TimeUntilNextPolyglot; // enochian timer + private short timeUntilNextPolyglot; // enochian timer - /// - /// Gets the time remaining for Astral Fire or Umbral Ice in milliseconds. - /// [FieldOffset(2)] - public short ElementTimeRemaining; // umbral ice and astral fire timer + private short elementTimeRemaining; // umbral ice and astral fire timer [FieldOffset(4)] private byte elementStance; // umbral ice or astral fire - /// - /// Gets the number of Umbral Hearts remaining. - /// [FieldOffset(5)] - public byte NumUmbralHearts; + private byte numUmbralHearts; + + [FieldOffset(6)] + private byte numPolyglotStacks; + + [FieldOffset(7)] + private byte enochianState; + + /// + /// Gets the time until the next Polyglot stack in milliseconds. + /// + public short TimeUntilNextPolyglot => this.timeUntilNextPolyglot; + + /// + /// Gets the time remaining for Astral Fire or Umbral Ice in milliseconds. + /// + public short ElementTimeRemaining => this.elementTimeRemaining; /// /// Gets the number of Polyglot stacks remaining. /// - [FieldOffset(6)] - public byte NumPolyglotStacks; + public byte NumPolyglotStacks => this.numPolyglotStacks; - [FieldOffset(7)] - private byte enochianState; + /// + /// Gets the number of Umbral Hearts remaining. + /// + public byte NumUmbralHearts => this.numUmbralHearts; /// /// Gets if the player is in Umbral Ice. diff --git a/Dalamud/Game/ClientState/Structs/JobGauge/BRDGauge.cs b/Dalamud/Game/ClientState/Structs/JobGauge/BRDGauge.cs index 3fe2b5dee..98590805c 100644 --- a/Dalamud/Game/ClientState/Structs/JobGauge/BRDGauge.cs +++ b/Dalamud/Game/ClientState/Structs/JobGauge/BRDGauge.cs @@ -8,28 +8,36 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge [StructLayout(LayoutKind.Explicit)] public struct BRDGauge { + [FieldOffset(0)] + private short songTimer; + + [FieldOffset(2)] + private byte numSongStacks; + + [FieldOffset(3)] + private byte soulVoiceValue; + + [FieldOffset(4)] + private CurrentSong activeSong; + /// /// Gets the current song timer in milliseconds. /// - [FieldOffset(0)] - public short SongTimer; + public short SongTimer => this.songTimer; /// /// Gets the number of stacks for the current song. /// - [FieldOffset(2)] - public byte NumSongStacks; + public byte NumSongStacks => this.numSongStacks; /// /// Gets the amount of Soul Voice accumulated. /// - [FieldOffset(3)] - public byte SoulVoiceValue; + public byte SoulVoiceValue => this.soulVoiceValue; /// /// Gets the type of song that is active. /// - [FieldOffset(4)] - public CurrentSong ActiveSong; + public CurrentSong ActiveSong => this.activeSong; } } diff --git a/Dalamud/Game/ClientState/Structs/JobGauge/DNCGauge.cs b/Dalamud/Game/ClientState/Structs/JobGauge/DNCGauge.cs index 8002a2f19..e2f4f6544 100644 --- a/Dalamud/Game/ClientState/Structs/JobGauge/DNCGauge.cs +++ b/Dalamud/Game/ClientState/Structs/JobGauge/DNCGauge.cs @@ -8,26 +8,32 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge [StructLayout(LayoutKind.Explicit)] public unsafe struct DNCGauge { - /// - /// Gets the number of feathers available. - /// [FieldOffset(0)] - public byte NumFeathers; + private byte numFeathers; - /// - /// Gets the amount of Espirit available. - /// [FieldOffset(1)] - public byte Esprit; + private byte esprit; [FieldOffset(2)] private fixed byte stepOrder[4]; + [FieldOffset(6)] + private byte numCompleteSteps; + + /// + /// Gets the number of feathers available. + /// + public byte NumFeathers => this.numFeathers; + + /// + /// Gets the amount of Espirit available. + /// + public byte Esprit => this.esprit; + /// /// Gets the number of steps completed for the current dance. /// - [FieldOffset(6)] - public byte NumCompleteSteps; + public byte NumCompleteSteps => this.numCompleteSteps; /// /// Gets the next step in the current dance. diff --git a/Dalamud/Game/ClientState/Structs/JobGauge/DRGGauge.cs b/Dalamud/Game/ClientState/Structs/JobGauge/DRGGauge.cs index 101c483b0..2f4c4ef16 100644 --- a/Dalamud/Game/ClientState/Structs/JobGauge/DRGGauge.cs +++ b/Dalamud/Game/ClientState/Structs/JobGauge/DRGGauge.cs @@ -8,22 +8,28 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge [StructLayout(LayoutKind.Explicit)] public struct DRGGauge { + [FieldOffset(0)] + private short botdTimer; + + [FieldOffset(2)] + private BOTDState botdState; + + [FieldOffset(3)] + private byte eyeCount; + /// /// Gets the time remaining for Blood of the Dragon in milliseconds. /// - [FieldOffset(0)] - public short BOTDTimer; + public short BOTDTimer => this.botdTimer; /// /// Gets the current state of Blood of the Dragon. /// - [FieldOffset(2)] - public BOTDState BOTDState; + public BOTDState BOTDState => this.botdState; /// /// Gets the count of eyes opened during Blood of the Dragon. /// - [FieldOffset(3)] - public byte EyeCount; + public byte EyeCount => this.eyeCount; } } diff --git a/Dalamud/Game/ClientState/Structs/JobGauge/DRKGauge.cs b/Dalamud/Game/ClientState/Structs/JobGauge/DRKGauge.cs index 9c5306764..97ab73161 100644 --- a/Dalamud/Game/ClientState/Structs/JobGauge/DRKGauge.cs +++ b/Dalamud/Game/ClientState/Structs/JobGauge/DRKGauge.cs @@ -8,26 +8,32 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge [StructLayout(LayoutKind.Explicit)] public struct DRKGauge { - /// - /// Gets the amount of blood accumulated. - /// [FieldOffset(0)] - public byte Blood; + private byte blood; - /// - /// Gets the Darkside time remaining in milliseconds. - /// [FieldOffset(2)] - public ushort DarksideTimeRemaining; + private ushort darksideTimeRemaining; [FieldOffset(4)] private byte darkArtsState; + [FieldOffset(6)] + private ushort shadowTimeRemaining; + + /// + /// Gets the amount of blood accumulated. + /// + public byte Blood => this.blood; + + /// + /// Gets the Darkside time remaining in milliseconds. + /// + public ushort DarksideTimeRemaining => this.darksideTimeRemaining; + /// /// Gets the Shadow time remaining in milliseconds. /// - [FieldOffset(6)] - public ushort ShadowTimeRemaining; + public ushort ShadowTimeRemaining => this.shadowTimeRemaining; /// /// Gets if the player has Dark Arts or not. diff --git a/Dalamud/Game/ClientState/Structs/JobGauge/GNBGauge.cs b/Dalamud/Game/ClientState/Structs/JobGauge/GNBGauge.cs index c1f7e496b..d42bd9fa1 100644 --- a/Dalamud/Game/ClientState/Structs/JobGauge/GNBGauge.cs +++ b/Dalamud/Game/ClientState/Structs/JobGauge/GNBGauge.cs @@ -8,22 +8,28 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge [StructLayout(LayoutKind.Explicit)] public struct GNBGauge { + [FieldOffset(0)] + private byte numAmmo; + + [FieldOffset(2)] + private short maxTimerDuration; + + [FieldOffset(4)] + private byte ammoComboStepNumber; + /// /// Gets the amount of ammo available. /// - [FieldOffset(0)] - public byte NumAmmo; + public byte NumAmmo => this.numAmmo; /// /// Gets the max combo time of the Gnashing Fang combo. /// - [FieldOffset(2)] - public short MaxTimerDuration; + public short MaxTimerDuration => this.maxTimerDuration; /// /// Gets the current step of the Gnashing Fang combo. /// - [FieldOffset(4)] - public byte AmmoComboStepNumber; + public byte AmmoComboStepNumber => this.ammoComboStepNumber; } } diff --git a/Dalamud/Game/ClientState/Structs/JobGauge/MCHGauge.cs b/Dalamud/Game/ClientState/Structs/JobGauge/MCHGauge.cs index 18f684c21..1444beac0 100644 --- a/Dalamud/Game/ClientState/Structs/JobGauge/MCHGauge.cs +++ b/Dalamud/Game/ClientState/Structs/JobGauge/MCHGauge.cs @@ -8,38 +8,48 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge [StructLayout(LayoutKind.Explicit)] public struct MCHGauge { + [FieldOffset(0)] + private short overheatTimeRemaining; + + [FieldOffset(2)] + private short robotTimeRemaining; + + [FieldOffset(4)] + private byte heat; + + [FieldOffset(5)] + private byte battery; + + [FieldOffset(6)] + private byte lastRobotBatteryPower; + + [FieldOffset(7)] + private byte timerActive; + /// /// Gets the time time remaining for Overheat in milliseconds. /// - [FieldOffset(0)] - public short OverheatTimeRemaining; + public short OverheatTimeRemaining => this.overheatTimeRemaining; /// /// Gets the time remaining for the Rook or Queen in milliseconds. /// - [FieldOffset(2)] - public short RobotTimeRemaining; + public short RobotTimeRemaining => this.robotTimeRemaining; /// /// Gets the current Heat level. /// - [FieldOffset(4)] - public byte Heat; + public byte Heat => this.heat; /// /// Gets the current Battery level. /// - [FieldOffset(5)] - public byte Battery; + public byte Battery => this.battery; /// /// Gets the battery level of the last Robot. /// - [FieldOffset(6)] - public byte LastRobotBatteryPower; - - [FieldOffset(7)] - private byte timerActive; + public byte LastRobotBatteryPower => this.lastRobotBatteryPower; /// /// Gets if the player is currently Overheated. diff --git a/Dalamud/Game/ClientState/Structs/JobGauge/MNKGauge.cs b/Dalamud/Game/ClientState/Structs/JobGauge/MNKGauge.cs index 177b077fc..ef1520d9e 100644 --- a/Dalamud/Game/ClientState/Structs/JobGauge/MNKGauge.cs +++ b/Dalamud/Game/ClientState/Structs/JobGauge/MNKGauge.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Dalamud.Game.ClientState.Structs.JobGauge @@ -9,35 +8,12 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge [StructLayout(LayoutKind.Explicit)] public struct MNKGauge { + [FieldOffset(0)] + private byte numChakra; + /// /// Gets the number of Chakra available. /// - [FieldOffset(0)] - public byte NumChakra; - - /// - /// Gets the Greased Lightning timer in milliseconds. - /// - [Obsolete("GL has been removed from the game")] - [FieldOffset(0)] - public byte GLTimer; - - /// - /// Gets the amount of Greased Lightning stacks. - /// - [Obsolete("GL has been removed from the game")] - [FieldOffset(2)] - public byte NumGLStacks; - - [Obsolete("GL has been removed from the game")] - [FieldOffset(4)] - private byte glTimerFreezeState; - - /// - /// Gets if the Greased Lightning timer has been frozen. - /// - /// >true or false. - [Obsolete("GL has been removed from the game")] - public bool IsGLTimerFroze() => false; + public byte NumChakra => this.numChakra; } } diff --git a/Dalamud/Game/ClientState/Structs/JobGauge/NINGauge.cs b/Dalamud/Game/ClientState/Structs/JobGauge/NINGauge.cs index 13eaec09d..f2019caf8 100644 --- a/Dalamud/Game/ClientState/Structs/JobGauge/NINGauge.cs +++ b/Dalamud/Game/ClientState/Structs/JobGauge/NINGauge.cs @@ -9,30 +9,28 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge [StructLayout(LayoutKind.Explicit)] public struct NINGauge { + [FieldOffset(0)] + private int hutonTimeLeft; + + [FieldOffset(4)] + private byte ninki; + + [FieldOffset(5)] + private byte numHutonManualCasts; + /// /// Gets the time left on Huton in milliseconds. /// - // TODO: Probably a short, confirm. - [FieldOffset(0)] - public int HutonTimeLeft; + public int HutonTimeLeft => this.hutonTimeLeft; /// /// Gets the amount of Ninki available. /// - [FieldOffset(4)] - public byte Ninki; - - /// - /// Obsolete. - /// - [Obsolete("Does not appear to be used")] - [FieldOffset(4)] - public byte TCJMudrasUsed; + public byte Ninki => this.ninki; /// /// Gets the number of times Huton has been cast manually. /// - [FieldOffset(5)] - public byte NumHutonManualCasts; + public byte NumHutonManualCasts => this.numHutonManualCasts; } } diff --git a/Dalamud/Game/ClientState/Structs/JobGauge/PLDGauge.cs b/Dalamud/Game/ClientState/Structs/JobGauge/PLDGauge.cs index d3eae81f3..545ae695a 100644 --- a/Dalamud/Game/ClientState/Structs/JobGauge/PLDGauge.cs +++ b/Dalamud/Game/ClientState/Structs/JobGauge/PLDGauge.cs @@ -8,10 +8,12 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge [StructLayout(LayoutKind.Explicit)] public struct PLDGauge { + [FieldOffset(0)] + private byte gaugeAmount; + /// /// Gets the current level of the Oath gauge. /// - [FieldOffset(0)] - public byte GaugeAmount; + public byte GaugeAmount => this.gaugeAmount; } } diff --git a/Dalamud/Game/ClientState/Structs/JobGauge/RDMGauge.cs b/Dalamud/Game/ClientState/Structs/JobGauge/RDMGauge.cs index f72d61d13..8d0ec38e1 100644 --- a/Dalamud/Game/ClientState/Structs/JobGauge/RDMGauge.cs +++ b/Dalamud/Game/ClientState/Structs/JobGauge/RDMGauge.cs @@ -8,16 +8,20 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge [StructLayout(LayoutKind.Explicit)] public struct RDMGauge { + [FieldOffset(0)] + private byte whiteGauge; + + [FieldOffset(1)] + private byte blackGauge; + /// /// Gets the level of the White gauge. /// - [FieldOffset(0)] - public byte WhiteGauge; + public byte WhiteGauge => this.whiteGauge; /// /// Gets the level of the Black gauge. /// - [FieldOffset(1)] - public byte BlackGauge; + public byte BlackGauge => this.blackGauge; } } diff --git a/Dalamud/Game/ClientState/Structs/JobGauge/SAMGauge.cs b/Dalamud/Game/ClientState/Structs/JobGauge/SAMGauge.cs index d0796e5c9..139a93265 100644 --- a/Dalamud/Game/ClientState/Structs/JobGauge/SAMGauge.cs +++ b/Dalamud/Game/ClientState/Structs/JobGauge/SAMGauge.cs @@ -8,23 +8,29 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge [StructLayout(LayoutKind.Explicit)] public struct SAMGauge { + [FieldOffset(3)] + private byte kenki; + + [FieldOffset(4)] + private byte meditationStacks; + + [FieldOffset(5)] + private Sen sen; + /// /// Gets the current amount of Kenki available. /// - [FieldOffset(3)] - public byte Kenki; + public byte Kenki => this.kenki; /// /// Gets the amount of Meditation stacks. /// - [FieldOffset(4)] - public byte MeditationStacks; + public byte MeditationStacks => this.meditationStacks; /// /// Gets the active Sen. /// - [FieldOffset(5)] - public Sen Sen; + public Sen Sen => this.sen; /// /// Gets if the Setsu Sen is active. diff --git a/Dalamud/Game/ClientState/Structs/JobGauge/SCHGauge.cs b/Dalamud/Game/ClientState/Structs/JobGauge/SCHGauge.cs index 5dc99cf15..66347b62e 100644 --- a/Dalamud/Game/ClientState/Structs/JobGauge/SCHGauge.cs +++ b/Dalamud/Game/ClientState/Structs/JobGauge/SCHGauge.cs @@ -8,28 +8,36 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge [StructLayout(LayoutKind.Explicit)] public struct SCHGauge { + [FieldOffset(2)] + private byte numAetherflowStacks; + + [FieldOffset(3)] + private byte fairyGaugeAmount; + + [FieldOffset(4)] + private short seraphTimer; + + [FieldOffset(6)] + private DismissedFairy dismissedFairy; + /// /// Gets the amount of Aetherflow stacks available. /// - [FieldOffset(2)] - public byte NumAetherflowStacks; + public byte NumAetherflowStacks => this.numAetherflowStacks; /// /// Gets the current level of the Fairy Gauge. /// - [FieldOffset(3)] - public byte FairyGaugeAmount; + public byte FairyGaugeAmount => this.fairyGaugeAmount; /// /// Gets the Seraph time remaining in milliseconds. /// - [FieldOffset(4)] - public short SeraphTimer; + public short SeraphTimer => this.seraphTimer; /// /// Gets the last dismissed fairy. /// - [FieldOffset(6)] - public DismissedFairy DismissedFairy; + public DismissedFairy DismissedFairy => this.dismissedFairy; } } diff --git a/Dalamud/Game/ClientState/Structs/JobGauge/SMNGauge.cs b/Dalamud/Game/ClientState/Structs/JobGauge/SMNGauge.cs index a5e9cc219..92cbcc19c 100644 --- a/Dalamud/Game/ClientState/Structs/JobGauge/SMNGauge.cs +++ b/Dalamud/Game/ClientState/Structs/JobGauge/SMNGauge.cs @@ -8,30 +8,38 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge [StructLayout(LayoutKind.Explicit)] public struct SMNGauge { + [FieldOffset(0)] + private short timerRemaining; + + [FieldOffset(2)] + private SummonPet returnSummon; + + [FieldOffset(3)] + private PetGlam returnSummonGlam; + + [FieldOffset(4)] + private byte numStacks; + /// /// Gets the time remaining for the current summon. /// - [FieldOffset(0)] - public short TimerRemaining; + public short TimerRemaining => this.timerRemaining; /// /// Gets the summon that will return after the current summon expires. /// - [FieldOffset(2)] - public SummonPet ReturnSummon; + public SummonPet ReturnSummon => this.returnSummon; /// /// Gets the summon glam for the . /// - [FieldOffset(3)] - public PetGlam ReturnSummonGlam; + public PetGlam ReturnSummonGlam => this.returnSummonGlam; /// /// Gets the current stacks. /// Use the summon accessors instead. /// - [FieldOffset(4)] - public byte NumStacks; + public byte NumStacks => this.numStacks; /// /// Gets if Phoenix is ready to be summoned. diff --git a/Dalamud/Game/ClientState/Structs/JobGauge/WARGauge.cs b/Dalamud/Game/ClientState/Structs/JobGauge/WARGauge.cs index 7747b9cea..0d070d68e 100644 --- a/Dalamud/Game/ClientState/Structs/JobGauge/WARGauge.cs +++ b/Dalamud/Game/ClientState/Structs/JobGauge/WARGauge.cs @@ -8,10 +8,12 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge [StructLayout(LayoutKind.Explicit)] public struct WARGauge { + [FieldOffset(0)] + private byte beastGaugeAmount; + /// /// Gets the amount of wrath in the Beast gauge. /// - [FieldOffset(0)] - public byte BeastGaugeAmount; + public byte BeastGaugeAmount => this.beastGaugeAmount; } } diff --git a/Dalamud/Game/ClientState/Structs/JobGauge/WHMGauge.cs b/Dalamud/Game/ClientState/Structs/JobGauge/WHMGauge.cs index 0ea51470e..496a39831 100644 --- a/Dalamud/Game/ClientState/Structs/JobGauge/WHMGauge.cs +++ b/Dalamud/Game/ClientState/Structs/JobGauge/WHMGauge.cs @@ -8,22 +8,28 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge [StructLayout(LayoutKind.Explicit)] public struct WHMGauge { + [FieldOffset(2)] + private short lilyTimer; + + [FieldOffset(4)] + private byte numLilies; + + [FieldOffset(5)] + private byte numBloodLily; + /// /// Gets the time to next lily in milliseconds. /// - [FieldOffset(2)] - public short LilyTimer; + public short LilyTimer => this.lilyTimer; /// /// Gets the number of Lilies. /// - [FieldOffset(4)] - public byte NumLilies; + public byte NumLilies => this.numLilies; /// /// Gets the number of times the blood lily has been nourished. /// - [FieldOffset(5)] - public byte NumBloodLily; + public byte NumBloodLily => this.numBloodLily; } } From 53e52dbae3dc933d03f30c4534db93c7ef89c88e Mon Sep 17 00:00:00 2001 From: Raymond Date: Thu, 15 Jul 2021 14:28:06 -0400 Subject: [PATCH 03/12] Simplify Resolvers, move to ClientState.Resolvers namespace --- .../Actors/Resolvers/BaseResolver.cs | 22 ------------ .../ClientState/Actors/Resolvers/ClassJob.cs | 31 ----------------- .../ClientState/Actors/Resolvers/World.cs | 31 ----------------- .../ClientState/Resolvers/BaseResolver{T}.cs | 34 +++++++++++++++++++ .../ClientState/Resolvers/ClassJobResolver.cs | 19 +++++++++++ .../ClientState/Resolvers/FateResolver.cs | 19 +++++++++++ .../Resolvers/TerritoryTypeResolver.cs | 19 +++++++++++ .../ClientState/Resolvers/WorldResolver.cs | 19 +++++++++++ 8 files changed, 110 insertions(+), 84 deletions(-) delete mode 100644 Dalamud/Game/ClientState/Actors/Resolvers/BaseResolver.cs delete mode 100644 Dalamud/Game/ClientState/Actors/Resolvers/ClassJob.cs delete mode 100644 Dalamud/Game/ClientState/Actors/Resolvers/World.cs create mode 100644 Dalamud/Game/ClientState/Resolvers/BaseResolver{T}.cs create mode 100644 Dalamud/Game/ClientState/Resolvers/ClassJobResolver.cs create mode 100644 Dalamud/Game/ClientState/Resolvers/FateResolver.cs create mode 100644 Dalamud/Game/ClientState/Resolvers/TerritoryTypeResolver.cs create mode 100644 Dalamud/Game/ClientState/Resolvers/WorldResolver.cs diff --git a/Dalamud/Game/ClientState/Actors/Resolvers/BaseResolver.cs b/Dalamud/Game/ClientState/Actors/Resolvers/BaseResolver.cs deleted file mode 100644 index 035b8b817..000000000 --- a/Dalamud/Game/ClientState/Actors/Resolvers/BaseResolver.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Dalamud.Game.ClientState.Actors.Resolvers -{ - /// - /// Base object resolver. - /// - public abstract class BaseResolver - { - /// - /// Initializes a new instance of the class. - /// - /// The Dalamud instance. - internal BaseResolver(Dalamud dalamud) - { - this.Dalamud = dalamud; - } - - /// - /// Gets the Dalamud instance. - /// - private protected Dalamud Dalamud { get; } - } -} diff --git a/Dalamud/Game/ClientState/Actors/Resolvers/ClassJob.cs b/Dalamud/Game/ClientState/Actors/Resolvers/ClassJob.cs deleted file mode 100644 index cb4fd771f..000000000 --- a/Dalamud/Game/ClientState/Actors/Resolvers/ClassJob.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Dalamud.Game.ClientState.Actors.Resolvers -{ - /// - /// This object represents a class or job. - /// - public class ClassJob : BaseResolver - { - /// - /// Initializes a new instance of the class. - /// Set up the ClassJob resolver with the provided ID. - /// - /// The ID of the classJob. - /// The Dalamud instance. - internal ClassJob(byte id, Dalamud dalamud) - : base(dalamud) - { - this.Id = id; - } - - /// - /// Gets the ID of the ClassJob. - /// - public uint Id { get; } - - /// - /// Gets GameData linked to this ClassJob. - /// - public Lumina.Excel.GeneratedSheets.ClassJob GameData => - this.Dalamud.Data.GetExcelSheet().GetRow(this.Id); - } -} diff --git a/Dalamud/Game/ClientState/Actors/Resolvers/World.cs b/Dalamud/Game/ClientState/Actors/Resolvers/World.cs deleted file mode 100644 index 993b3dde5..000000000 --- a/Dalamud/Game/ClientState/Actors/Resolvers/World.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Dalamud.Game.ClientState.Actors.Resolvers -{ - /// - /// This object represents a world a character can reside on. - /// - public class World : BaseResolver - { - /// - /// Initializes a new instance of the class. - /// Set up the world resolver with the provided ID. - /// - /// The ID of the world. - /// The Dalamud instance. - internal World(ushort id, Dalamud dalamud) - : base(dalamud) - { - this.Id = id; - } - - /// - /// Gets the ID of the world. - /// - public uint Id { get; } - - /// - /// Gets GameData linked to this world. - /// - public Lumina.Excel.GeneratedSheets.World GameData => - this.Dalamud.Data.GetExcelSheet().GetRow(this.Id); - } -} diff --git a/Dalamud/Game/ClientState/Resolvers/BaseResolver{T}.cs b/Dalamud/Game/ClientState/Resolvers/BaseResolver{T}.cs new file mode 100644 index 000000000..2ded995d7 --- /dev/null +++ b/Dalamud/Game/ClientState/Resolvers/BaseResolver{T}.cs @@ -0,0 +1,34 @@ +using Lumina.Excel; + +namespace Dalamud.Game.ClientState.Resolvers +{ + /// + /// This object represents a class or job. + /// + /// The type of Lumina sheet to resolve. + public class BaseResolver where T : ExcelRow + { + private readonly Dalamud dalamud; + + /// + /// Initializes a new instance of the class. + /// + /// The ID of the classJob. + /// The Dalamud instance. + internal BaseResolver(uint id, Dalamud dalamud) + { + this.dalamud = dalamud; + this.Id = id; + } + + /// + /// Gets the ID to be resolved. + /// + public uint Id { get; } + + /// + /// Gets GameData linked to this excel row. + /// + public T GameData => this.dalamud.Data.GetExcelSheet().GetRow(this.Id); + } +} diff --git a/Dalamud/Game/ClientState/Resolvers/ClassJobResolver.cs b/Dalamud/Game/ClientState/Resolvers/ClassJobResolver.cs new file mode 100644 index 000000000..b9603838d --- /dev/null +++ b/Dalamud/Game/ClientState/Resolvers/ClassJobResolver.cs @@ -0,0 +1,19 @@ +namespace Dalamud.Game.ClientState.Resolvers +{ + /// + /// This object represents a class or job. + /// + public class ClassJobResolver : BaseResolver + { + /// + /// Initializes a new instance of the class. + /// Set up the ClassJob resolver with the provided ID. + /// + /// The ID of the classJob. + /// The Dalamud instance. + internal ClassJobResolver(ushort id, Dalamud dalamud) + : base(id, dalamud) + { + } + } +} diff --git a/Dalamud/Game/ClientState/Resolvers/FateResolver.cs b/Dalamud/Game/ClientState/Resolvers/FateResolver.cs new file mode 100644 index 000000000..15f2fce0d --- /dev/null +++ b/Dalamud/Game/ClientState/Resolvers/FateResolver.cs @@ -0,0 +1,19 @@ +namespace Dalamud.Game.ClientState.Resolvers +{ + /// + /// This object represents a Fate a character can participate in. + /// + public class FateResolver : BaseResolver + { + /// + /// Initializes a new instance of the class. + /// Set up the Fate resolver with the provided ID. + /// + /// The ID of the Fate. + /// The Dalamud instance. + internal FateResolver(ushort id, Dalamud dalamud) + : base(id, dalamud) + { + } + } +} diff --git a/Dalamud/Game/ClientState/Resolvers/TerritoryTypeResolver.cs b/Dalamud/Game/ClientState/Resolvers/TerritoryTypeResolver.cs new file mode 100644 index 000000000..248bf94bb --- /dev/null +++ b/Dalamud/Game/ClientState/Resolvers/TerritoryTypeResolver.cs @@ -0,0 +1,19 @@ +namespace Dalamud.Game.ClientState.Resolvers +{ + /// + /// This object represents a territory a character can be in. + /// + public class TerritoryTypeResolver : BaseResolver + { + /// + /// Initializes a new instance of the class. + /// Set up the territory type resolver with the provided ID. + /// + /// The ID of the territory type. + /// The Dalamud instance. + internal TerritoryTypeResolver(ushort id, Dalamud dalamud) + : base(id, dalamud) + { + } + } +} diff --git a/Dalamud/Game/ClientState/Resolvers/WorldResolver.cs b/Dalamud/Game/ClientState/Resolvers/WorldResolver.cs new file mode 100644 index 000000000..0d37e3549 --- /dev/null +++ b/Dalamud/Game/ClientState/Resolvers/WorldResolver.cs @@ -0,0 +1,19 @@ +namespace Dalamud.Game.ClientState.Resolvers +{ + /// + /// This object represents a world a character can reside on. + /// + public class WorldResolver : BaseResolver + { + /// + /// Initializes a new instance of the class. + /// Set up the world resolver with the provided ID. + /// + /// The ID of the world. + /// The Dalamud instance. + internal WorldResolver(ushort id, Dalamud dalamud) + : base(id, dalamud) + { + } + } +} From c1755d4bf8bb62de1d6d19a55d48f2a4be5a3640 Mon Sep 17 00:00:00 2001 From: Raymond Date: Thu, 15 Jul 2021 14:28:17 -0400 Subject: [PATCH 04/12] Move Position3 to Game namespace --- Dalamud/Game/{ClientState/Actors => }/Position3.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Dalamud/Game/{ClientState/Actors => }/Position3.cs (96%) diff --git a/Dalamud/Game/ClientState/Actors/Position3.cs b/Dalamud/Game/Position3.cs similarity index 96% rename from Dalamud/Game/ClientState/Actors/Position3.cs rename to Dalamud/Game/Position3.cs index df8b0c471..3812376df 100644 --- a/Dalamud/Game/ClientState/Actors/Position3.cs +++ b/Dalamud/Game/Position3.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace Dalamud.Game.ClientState.Actors +namespace Dalamud.Game { /// /// A game native equivalent of a Vector3. From 976688a924e3ed95c7177b622880c254ad5fcc0a Mon Sep 17 00:00:00 2001 From: Raymond Date: Thu, 15 Jul 2021 14:28:57 -0400 Subject: [PATCH 05/12] Actor formatting, updates for resolvers --- Dalamud/Game/ClientState/Actors/ActorTable.cs | 21 ++++++++----------- .../Game/ClientState/Actors/Types/Actor.cs | 12 ++++++++++- .../Game/ClientState/Actors/Types/Chara.cs | 4 ++-- .../Actors/Types/PlayerCharacter.cs | 10 ++++----- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/Dalamud/Game/ClientState/Actors/ActorTable.cs b/Dalamud/Game/ClientState/Actors/ActorTable.cs index 39238ae6e..5a7cfc4cd 100644 --- a/Dalamud/Game/ClientState/Actors/ActorTable.cs +++ b/Dalamud/Game/ClientState/Actors/ActorTable.cs @@ -17,6 +17,7 @@ namespace Dalamud.Game.ClientState.Actors private const int ActorTableLength = 424; private readonly Dalamud dalamud; + private readonly ClientStateAddressResolver address; /// /// Initializes a new instance of the class. @@ -25,10 +26,10 @@ namespace Dalamud.Game.ClientState.Actors /// Client state address resolver. internal ActorTable(Dalamud dalamud, ClientStateAddressResolver addressResolver) { - this.Address = addressResolver; this.dalamud = dalamud; + this.address = addressResolver; - Log.Verbose("Actor table address {ActorTable}", this.Address.ActorTable); + Log.Verbose($"Actor table address 0x{this.address.ActorTable.ToInt64():X}"); } /// @@ -52,13 +53,11 @@ namespace Dalamud.Game.ClientState.Actors } } - private ClientStateAddressResolver Address { get; } - /// /// Get an actor at the specified spawn index. /// /// Spawn index. - /// An at the specified spawn index. + /// An at the specified spawn index. [CanBeNull] public Actor this[int index] { @@ -73,7 +72,7 @@ namespace Dalamud.Game.ClientState.Actors /// Get an actor at the specified address. /// /// The actor address. - /// An at the specified address. + /// An at the specified address. public Actor this[IntPtr address] { get @@ -93,11 +92,9 @@ namespace Dalamud.Game.ClientState.Actors public unsafe IntPtr GetActorAddress(int index) { if (index >= ActorTableLength) - { return IntPtr.Zero; - } - return *(IntPtr*)(this.Address.ActorTable + (8 * index)); + return *(IntPtr*)(this.address.ActorTable + (8 * index)); } /// @@ -109,12 +106,12 @@ namespace Dalamud.Game.ClientState.Actors internal unsafe Actor CreateActorReference(IntPtr offset) { if (this.dalamud.ClientState.LocalContentId == 0) - { return null; - } + + if (offset == IntPtr.Zero) + return null; var objKind = *(ObjectKind*)(offset + ActorOffsets.ObjectKind); - return objKind switch { ObjectKind.Player => new PlayerCharacter(offset, this.dalamud), diff --git a/Dalamud/Game/ClientState/Actors/Types/Actor.cs b/Dalamud/Game/ClientState/Actors/Types/Actor.cs index 6065ab48e..f98d8932e 100644 --- a/Dalamud/Game/ClientState/Actors/Types/Actor.cs +++ b/Dalamud/Game/ClientState/Actors/Types/Actor.cs @@ -40,6 +40,16 @@ namespace Dalamud.Game.ClientState.Actors.Types /// True or false. public static implicit operator bool(Actor actor) => IsValid(actor); + public static bool operator ==(Actor actor1, Actor actor2) + { + if (actor1 is null || actor2 is null) + return Equals(actor1, actor2); + + return actor1.Equals(actor2); + } + + public static bool operator !=(Actor actor1, Actor actor2) => !(actor1 == actor2); + /// /// Gets a value indicating whether this actor is still valid in memory. /// @@ -69,7 +79,7 @@ namespace Dalamud.Game.ClientState.Actors.Types public override bool Equals(object obj) => ((IEquatable)this).Equals(obj as Actor); /// - public override int GetHashCode() => base.GetHashCode(); + public override int GetHashCode() => this.ActorId.GetHashCode(); } /// diff --git a/Dalamud/Game/ClientState/Actors/Types/Chara.cs b/Dalamud/Game/ClientState/Actors/Types/Chara.cs index 1a60b7499..c1a51e6dd 100644 --- a/Dalamud/Game/ClientState/Actors/Types/Chara.cs +++ b/Dalamud/Game/ClientState/Actors/Types/Chara.cs @@ -1,6 +1,6 @@ using System; -using Dalamud.Game.ClientState.Actors.Resolvers; +using Dalamud.Game.ClientState.Resolvers; using Dalamud.Game.ClientState.Structs; using Dalamud.Memory; @@ -65,7 +65,7 @@ namespace Dalamud.Game.ClientState.Actors.Types /// /// Gets the ClassJob of this Chara. /// - public ClassJob ClassJob => new(*(byte*)(this.Address + ActorOffsets.ClassJob), this.Dalamud); + public ClassJobResolver ClassJob => new(*(byte*)(this.Address + ActorOffsets.ClassJob), this.Dalamud); /// /// Gets the level of this Chara. diff --git a/Dalamud/Game/ClientState/Actors/Types/PlayerCharacter.cs b/Dalamud/Game/ClientState/Actors/Types/PlayerCharacter.cs index 4be3d7a61..a1ebe1ab0 100644 --- a/Dalamud/Game/ClientState/Actors/Types/PlayerCharacter.cs +++ b/Dalamud/Game/ClientState/Actors/Types/PlayerCharacter.cs @@ -1,6 +1,6 @@ using System; -using Dalamud.Game.ClientState.Actors.Resolvers; +using Dalamud.Game.ClientState.Resolvers; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Memory; @@ -23,14 +23,14 @@ namespace Dalamud.Game.ClientState.Actors.Types } /// - /// Gets the current world of the character. + /// Gets the current world of the character. /// - public World CurrentWorld => new(*(ushort*)(this.Address + ActorOffsets.CurrentWorld), this.Dalamud); + public WorldResolver CurrentWorld => new(*(ushort*)(this.Address + ActorOffsets.CurrentWorld), this.Dalamud); /// - /// Gets the home world of the character. + /// Gets the home world of the character. /// - public World HomeWorld => new(*(ushort*)(this.Address + ActorOffsets.HomeWorld), this.Dalamud); + public WorldResolver HomeWorld => new(*(ushort*)(this.Address + ActorOffsets.HomeWorld), this.Dalamud); /// /// Gets the Free Company tag of this player. From a18bcc385ce8b509634001852cfbe5accabf7458 Mon Sep 17 00:00:00 2001 From: Raymond Date: Thu, 15 Jul 2021 14:31:17 -0400 Subject: [PATCH 06/12] DataWindow should use an Enum --- .../Interface/Internal/Windows/DataWindow.cs | 640 ++++++++++-------- 1 file changed, 352 insertions(+), 288 deletions(-) diff --git a/Dalamud/Interface/Internal/Windows/DataWindow.cs b/Dalamud/Interface/Internal/Windows/DataWindow.cs index 81aab9ca6..f107a8041 100644 --- a/Dalamud/Interface/Internal/Windows/DataWindow.cs +++ b/Dalamud/Interface/Internal/Windows/DataWindow.cs @@ -27,16 +27,11 @@ namespace Dalamud.Interface.Internal.Windows internal class DataWindow : Window { private readonly Dalamud dalamud; - - private readonly string[] dataKinds = new[] - { - "ServerOpCode", "Address", "Actor Table", "Font Test", "Party List", "Plugin IPC", "Condition", - "Gauge", "Command", "Addon", "Addon Inspector", "StartInfo", "Target", "Toast", "ImGui", "Tex", "Gamepad", - }; + private readonly string[] dataKindNames = Enum.GetNames(typeof(DataKind)).Select(k => k.Replace("_", " ")).ToArray(); private bool wasReady; private string serverOpString; - private int currentKind; + private DataKind currentKind; private bool drawActors = false; private float maxActorDrawDistance = 20; @@ -86,6 +81,27 @@ namespace Dalamud.Interface.Internal.Windows this.Load(); } + private enum DataKind + { + Server_OpCode, + Address, + Actor_Table, + Font_Test, + Party_List, + Plugin_IPC, + Condition, + Gauge, + Command, + Addon, + Addon_Inspector, + StartInfo, + Target, + Toast, + ImGui, + Tex, + Gamepad, + } + /// /// Set the DataKind dropdown menu. /// @@ -98,12 +114,15 @@ namespace Dalamud.Interface.Internal.Windows if (dataKind == "ai") dataKind = "Addon Inspector"; - int index; dataKind = dataKind.Replace(" ", string.Empty).ToLower(); - var dataKinds = this.dataKinds.Select(k => k.Replace(" ", string.Empty).ToLower()).ToList(); - if ((index = dataKinds.IndexOf(dataKind)) != -1) + var dataKinds = Enum.GetValues(typeof(DataKind)) + .Cast() + .Where(k => nameof(k).Replace("_", string.Empty).ToLower() == dataKind) + .ToList(); + + if (dataKinds.Count > 0) { - this.currentKind = index; + this.currentKind = dataKinds.First(); } else { @@ -125,7 +144,11 @@ namespace Dalamud.Interface.Internal.Windows var copy = ImGui.Button("Copy all"); ImGui.SameLine(); - ImGui.Combo("Data kind", ref this.currentKind, this.dataKinds, this.dataKinds.Length); + var currentKindIndex = (int)this.currentKind; + if (ImGui.Combo("Data kind", ref currentKindIndex, this.dataKindNames, this.dataKindNames.Length)) + { + this.currentKind = (DataKind)currentKindIndex; + } ImGui.Checkbox("Resolve GameData", ref this.resolveGameData); @@ -142,309 +165,74 @@ namespace Dalamud.Interface.Internal.Windows { switch (this.currentKind) { - case 0: - ImGui.TextUnformatted(this.serverOpString); - break; - case 1: - - ImGui.InputText(".text sig", ref this.inputSig, 400); - if (ImGui.Button("Resolve")) - { - try - { - this.sigResult = this.dalamud.SigScanner.ScanText(this.inputSig); - } - catch (KeyNotFoundException) - { - this.sigResult = new IntPtr(-1); - } - } - - ImGui.Text($"Result: {this.sigResult.ToInt64():X}"); - ImGui.SameLine(); - if (ImGui.Button($"C{this.copyButtonIndex++}")) - ImGui.SetClipboardText(this.sigResult.ToInt64().ToString("x")); - - foreach (var debugScannedValue in BaseAddressResolver.DebugScannedValues) - { - ImGui.TextUnformatted($"{debugScannedValue.Key}"); - foreach (var valueTuple in debugScannedValue.Value) - { - ImGui.TextUnformatted( - $" {valueTuple.Item1} - 0x{valueTuple.Item2.ToInt64():x}"); - ImGui.SameLine(); - - if (ImGui.Button($"C##copyAddress{this.copyButtonIndex++}")) - ImGui.SetClipboardText(valueTuple.Item2.ToInt64().ToString("x")); - } - } - + case DataKind.Server_OpCode: + this.DrawServerOpCode(); break; - // AT - case 2: + case DataKind.Address: + this.DrawAddress(); + break; + + case DataKind.Actor_Table: this.DrawActorTable(); + break; break; - // Font - case 3: - var specialChars = string.Empty; - for (var i = 0xE020; i <= 0xE0DB; i++) - specialChars += $"0x{i:X} - {(SeIconChar)i} - {(char)i}\n"; - - ImGui.TextUnformatted(specialChars); - - foreach (var fontAwesomeIcon in Enum.GetValues(typeof(FontAwesomeIcon)) - .Cast()) - { - ImGui.Text(((int)fontAwesomeIcon.ToIconChar()).ToString("X") + " - "); - ImGui.SameLine(); - - ImGui.PushFont(UiBuilder.IconFont); - ImGui.Text(fontAwesomeIcon.ToIconString()); - ImGui.PopFont(); - } - + case DataKind.Font_Test: + this.DrawFontTest(); break; - // Party - case 4: - var partyString = string.Empty; - - if (this.dalamud.ClientState.PartyList.Length == 0) - { - ImGui.TextUnformatted("Data not ready."); - } - else - { - partyString += $"{this.dalamud.ClientState.PartyList.Count} Members\n"; - for (var i = 0; i < this.dalamud.ClientState.PartyList.Count; i++) - { - var member = this.dalamud.ClientState.PartyList[i]; - if (member == null) - { - partyString += - $"[{i}] was null\n"; - continue; - } - - partyString += - $"[{i}] {member.CharacterName} - {member.ObjectKind} - {member.Actor.ActorId}\n"; - } - - ImGui.TextUnformatted(partyString); - } - + case DataKind.Party_List: + this.DrawPartyList(); break; - // Subscriptions - case 5: - this.DrawIpcDebug(); - + case DataKind.Plugin_IPC: + this.DrawPluginIPC(); break; - // Condition - case 6: -#if DEBUG - ImGui.Text($"ptr: 0x{this.dalamud.ClientState.Condition.ConditionArrayBase.ToInt64():X}"); -#endif - - ImGui.Text("Current Conditions:"); - ImGui.Separator(); - - var didAny = false; - - for (var i = 0; i < Condition.MaxConditionEntries; i++) - { - var typedCondition = (ConditionFlag)i; - var cond = this.dalamud.ClientState.Condition[typedCondition]; - - if (!cond) continue; - - didAny = true; - - ImGui.Text($"ID: {i} Enum: {typedCondition}"); - } - - if (!didAny) - ImGui.Text("None. Talk to a shop NPC or visit a market board to find out more!!!!!!!"); - + case DataKind.Condition: + this.DrawCondition(); break; - // Gauge - case 7: - var gauge = this.dalamud.ClientState.JobGauges.Get(); - ImGui.Text($"Moon: {gauge.ContainsSeal(SealType.MOON)} Drawn: {gauge.DrawnCard()}"); - + case DataKind.Gauge: + this.DrawGauge(); break; - // Command - case 8: - foreach (var command in this.dalamud.CommandManager.Commands) - ImGui.Text($"{command.Key}\n -> {command.Value.HelpMessage}\n -> In help: {command.Value.ShowInHelp}\n\n"); - + case DataKind.Command: + this.DrawCommand(); break; - // Addon - case 9: - this.DrawAddonDebug(); + case DataKind.Addon: + this.DrawAddon(); break; - // Addon Inspector - case 10: - this.addonInspector ??= new UIDebug(this.dalamud); - this.addonInspector.Draw(); + case DataKind.Addon_Inspector: + this.DrawAddonInspector(); break; - // StartInfo - case 11: - ImGui.Text(JsonConvert.SerializeObject(this.dalamud.StartInfo, Formatting.Indented)); + case DataKind.StartInfo: + this.DrawStartInfo(); break; - // Target - case 12: - this.DrawTargetDebug(); + case DataKind.Target: + this.DrawTarget(); break; - // Toast - case 13: - ImGui.InputText("Toast text", ref this.inputTextToast, 200); - - ImGui.Combo("Toast Position", ref this.toastPosition, new[] { "Bottom", "Top", }, 2); - ImGui.Combo("Toast Speed", ref this.toastSpeed, new[] { "Slow", "Fast", }, 2); - ImGui.Combo("Quest Toast Position", ref this.questToastPosition, new[] { "Centre", "Right", "Left" }, 3); - ImGui.Checkbox("Quest Checkmark", ref this.questToastCheckmark); - ImGui.Checkbox("Quest Play Sound", ref this.questToastSound); - ImGui.InputInt("Quest Icon ID", ref this.questToastIconId); - - ImGuiHelpers.ScaledDummy(new Vector2(10, 10)); - - if (ImGui.Button("Show toast")) - { - this.dalamud.Framework.Gui.Toast.ShowNormal(this.inputTextToast, new ToastOptions - { - Position = (ToastPosition)this.toastPosition, - Speed = (ToastSpeed)this.toastSpeed, - }); - } - - if (ImGui.Button("Show Quest toast")) - { - this.dalamud.Framework.Gui.Toast.ShowQuest(this.inputTextToast, new QuestToastOptions - { - Position = (QuestToastPosition)this.questToastPosition, - DisplayCheckmark = this.questToastCheckmark, - IconId = (uint)this.questToastIconId, - PlaySound = this.questToastSound, - }); - } - - if (ImGui.Button("Show Error toast")) - { - this.dalamud.Framework.Gui.Toast.ShowError(this.inputTextToast); - } - + case DataKind.Toast: + this.DrawToast(); break; - // ImGui - case 14: - ImGui.Text("Monitor count: " + ImGui.GetPlatformIO().Monitors.Size); - ImGui.Text("OverrideGameCursor: " + this.dalamud.InterfaceManager.OverrideGameCursor); - - ImGui.Button("THIS IS A BUTTON###hoverTestButton"); - this.dalamud.InterfaceManager.OverrideGameCursor = !ImGui.IsItemHovered(); - + case DataKind.ImGui: + this.DrawImGui(); break; - // Tex - case 15: - ImGui.InputText("Tex Path", ref this.inputTexPath, 255); - ImGui.InputFloat2("UV0", ref this.inputTexUv0); - ImGui.InputFloat2("UV1", ref this.inputTexUv1); - ImGui.InputFloat4("Tint", ref this.inputTintCol); - ImGui.InputFloat2("Scale", ref this.inputTexScale); - - if (ImGui.Button("Load Tex")) - { - try - { - this.debugTex = this.dalamud.Data.GetImGuiTexture(this.inputTexPath); - this.inputTexScale = new Vector2(this.debugTex.Width, this.debugTex.Height); - } - catch (Exception ex) - { - Log.Error(ex, "Could not load tex."); - } - } - - ImGuiHelpers.ScaledDummy(10); - - if (this.debugTex != null) - { - ImGui.Image(this.debugTex.ImGuiHandle, this.inputTexScale, this.inputTexUv0, this.inputTexUv1, this.inputTintCol); - ImGuiHelpers.ScaledDummy(5); - Util.ShowObject(this.debugTex); - } - + case DataKind.Tex: + this.DrawTex(); break; - // Gamepad - case 16: - Action> helper = (text, mask, resolve) => - { - ImGui.Text($"{text} {mask:X4}"); - ImGui.Text($"DPadLeft {resolve(GamepadButtons.DpadLeft)} " + - $"DPadUp {resolve(GamepadButtons.DpadUp)} " + - $"DPadRight {resolve(GamepadButtons.DpadRight)} " + - $"DPadDown {resolve(GamepadButtons.DpadDown)} "); - ImGui.Text($"West {resolve(GamepadButtons.West)} " + - $"North {resolve(GamepadButtons.North)} " + - $"East {resolve(GamepadButtons.East)} " + - $"South {resolve(GamepadButtons.South)} "); - ImGui.Text($"L1 {resolve(GamepadButtons.L1)} " + - $"L2 {resolve(GamepadButtons.L2)} " + - $"R1 {resolve(GamepadButtons.R1)} " + - $"R2 {resolve(GamepadButtons.R2)} "); - ImGui.Text($"Select {resolve(GamepadButtons.Select)} " + - $"Start {resolve(GamepadButtons.Start)} " + - $"L3 {resolve(GamepadButtons.L3)} " + - $"R3 {resolve(GamepadButtons.R3)} "); - }; -#if DEBUG - ImGui.Text($"GamepadInput 0x{this.dalamud.ClientState.GamepadState.GamepadInput.ToInt64():X}"); - - if (ImGui.IsItemHovered()) - ImGui.SetMouseCursor(ImGuiMouseCursor.Hand); - - if (ImGui.IsItemClicked()) - ImGui.SetClipboardText($"0x{this.dalamud.ClientState.GamepadState.GamepadInput.ToInt64():X}"); -#endif - - helper( - "Buttons Raw", - this.dalamud.ClientState.GamepadState.ButtonsRaw, - this.dalamud.ClientState.GamepadState.Raw); - helper( - "Buttons Pressed", - this.dalamud.ClientState.GamepadState.ButtonsPressed, - this.dalamud.ClientState.GamepadState.Pressed); - helper( - "Buttons Repeat", - this.dalamud.ClientState.GamepadState.ButtonsRepeat, - this.dalamud.ClientState.GamepadState.Repeat); - helper( - "Buttons Released", - this.dalamud.ClientState.GamepadState.ButtonsReleased, - this.dalamud.ClientState.GamepadState.Released); - ImGui.Text($"LeftStickLeft {this.dalamud.ClientState.GamepadState.LeftStickLeft:0.00} " + - $"LeftStickUp {this.dalamud.ClientState.GamepadState.LeftStickUp:0.00} " + - $"LeftStickRight {this.dalamud.ClientState.GamepadState.LeftStickRight:0.00} " + - $"LeftStickDown {this.dalamud.ClientState.GamepadState.LeftStickDown:0.00} "); - ImGui.Text($"RightStickLeft {this.dalamud.ClientState.GamepadState.RightStickLeft:0.00} " + - $"RightStickUp {this.dalamud.ClientState.GamepadState.RightStickUp:0.00} " + - $"RightStickRight {this.dalamud.ClientState.GamepadState.RightStickRight:0.00} " + - $"RightStickDown {this.dalamud.ClientState.GamepadState.RightStickDown:0.00} "); + case DataKind.Gamepad: + this.DrawGamepad(); break; } } @@ -463,6 +251,46 @@ namespace Dalamud.Interface.Internal.Windows ImGui.EndChild(); } + private void DrawServerOpCode() + { + ImGui.TextUnformatted(this.serverOpString); + } + + private void DrawAddress() + { + ImGui.InputText(".text sig", ref this.inputSig, 400); + if (ImGui.Button("Resolve")) + { + try + { + this.sigResult = this.dalamud.SigScanner.ScanText(this.inputSig); + } + catch (KeyNotFoundException) + { + this.sigResult = new IntPtr(-1); + } + } + + ImGui.Text($"Result: {this.sigResult.ToInt64():X}"); + ImGui.SameLine(); + if (ImGui.Button($"C{this.copyButtonIndex++}")) + ImGui.SetClipboardText(this.sigResult.ToInt64().ToString("x")); + + foreach (var debugScannedValue in BaseAddressResolver.DebugScannedValues) + { + ImGui.TextUnformatted($"{debugScannedValue.Key}"); + foreach (var valueTuple in debugScannedValue.Value) + { + ImGui.TextUnformatted( + $" {valueTuple.Item1} - 0x{valueTuple.Item2.ToInt64():x}"); + ImGui.SameLine(); + + if (ImGui.Button($"C##copyAddress{this.copyButtonIndex++}")) + ImGui.SetClipboardText(valueTuple.Item2.ToInt64().ToString("x")); + } + } + } + private void DrawActorTable() { var stateString = string.Empty; @@ -545,9 +373,58 @@ namespace Dalamud.Interface.Internal.Windows } } -#pragma warning disable CS0618 // Type or member is obsolete - private void DrawIpcDebug() + private void DrawFontTest() { + var specialChars = string.Empty; + + for (var i = 0xE020; i <= 0xE0DB; i++) + specialChars += $"0x{i:X} - {(SeIconChar)i} - {(char)i}\n"; + + ImGui.TextUnformatted(specialChars); + + foreach (var fontAwesomeIcon in Enum.GetValues(typeof(FontAwesomeIcon)).Cast()) + { + ImGui.Text(((int)fontAwesomeIcon.ToIconChar()).ToString("X") + " - "); + ImGui.SameLine(); + + ImGui.PushFont(UiBuilder.IconFont); + ImGui.Text(fontAwesomeIcon.ToIconString()); + ImGui.PopFont(); + } + } + + private void DrawPartyList() + { + var partyString = string.Empty; + + if (this.dalamud.ClientState.PartyList.Length == 0) + { + ImGui.TextUnformatted("Data not ready."); + } + else + { + partyString += $"{this.dalamud.ClientState.PartyList.Count} Members\n"; + for (var i = 0; i < this.dalamud.ClientState.PartyList.Count; i++) + { + var member = this.dalamud.ClientState.PartyList[i]; + if (member == null) + { + partyString += + $"[{i}] was null\n"; + continue; + } + + partyString += + $"[{i}] {member.CharacterName} - {member.ObjectKind} - {member.Actor.ActorId}\n"; + } + + ImGui.TextUnformatted(partyString); + } + } + + private void DrawPluginIPC() + { +#pragma warning disable CS0618 // Type or member is obsolete var i1 = new DalamudPluginInterface(this.dalamud, "DalamudTestSub", null, PluginLoadReason.Unknown); var i2 = new DalamudPluginInterface(this.dalamud, "DalamudTestPub", null, PluginLoadReason.Unknown); @@ -592,10 +469,49 @@ namespace Dalamud.Interface.Internal.Windows foreach (var ipc in this.dalamud.PluginManager.IpcSubscriptions) ImGui.Text($"Source:{ipc.SourcePluginName} Sub:{ipc.SubPluginName}"); - } #pragma warning restore CS0618 // Type or member is obsolete + } - private void DrawAddonDebug() + private void DrawCondition() + { +#if DEBUG + ImGui.Text($"ptr: 0x{this.dalamud.ClientState.Condition.ConditionArrayBase.ToInt64():X}"); +#endif + + ImGui.Text("Current Conditions:"); + ImGui.Separator(); + + var didAny = false; + + for (var i = 0; i < Condition.MaxConditionEntries; i++) + { + var typedCondition = (ConditionFlag)i; + var cond = this.dalamud.ClientState.Condition[typedCondition]; + + if (!cond) continue; + + didAny = true; + + ImGui.Text($"ID: {i} Enum: {typedCondition}"); + } + + if (!didAny) + ImGui.Text("None. Talk to a shop NPC or visit a market board to find out more!!!!!!!"); + } + + private void DrawGauge() + { + var gauge = this.dalamud.ClientState.JobGauges.Get(); + ImGui.Text($"Moon: {gauge.ContainsSeal(SealType.MOON)} Drawn: {gauge.DrawnCard()}"); + } + + private void DrawCommand() + { + foreach (var command in this.dalamud.CommandManager.Commands) + ImGui.Text($"{command.Key}\n -> {command.Value.HelpMessage}\n -> In help: {command.Value.ShowInHelp}\n\n"); + } + + private void DrawAddon() { ImGui.InputText("Addon name", ref this.inputAddonName, 256); ImGui.InputInt("Addon Index", ref this.inputAddonIndex); @@ -634,7 +550,18 @@ namespace Dalamud.Interface.Internal.Windows } } - private void DrawTargetDebug() + private void DrawAddonInspector() + { + this.addonInspector ??= new UIDebug(this.dalamud); + this.addonInspector.Draw(); + } + + private void DrawStartInfo() + { + ImGui.Text(JsonConvert.SerializeObject(this.dalamud.StartInfo, Formatting.Indented)); + } + + private void DrawTarget() { var targetMgr = this.dalamud.ClientState.Targets; @@ -678,6 +605,143 @@ namespace Dalamud.Interface.Internal.Windows } } + private void DrawToast() + { + ImGui.InputText("Toast text", ref this.inputTextToast, 200); + + ImGui.Combo("Toast Position", ref this.toastPosition, new[] { "Bottom", "Top", }, 2); + ImGui.Combo("Toast Speed", ref this.toastSpeed, new[] { "Slow", "Fast", }, 2); + ImGui.Combo("Quest Toast Position", ref this.questToastPosition, new[] { "Centre", "Right", "Left" }, 3); + ImGui.Checkbox("Quest Checkmark", ref this.questToastCheckmark); + ImGui.Checkbox("Quest Play Sound", ref this.questToastSound); + ImGui.InputInt("Quest Icon ID", ref this.questToastIconId); + + ImGuiHelpers.ScaledDummy(new Vector2(10, 10)); + + if (ImGui.Button("Show toast")) + { + this.dalamud.Framework.Gui.Toast.ShowNormal(this.inputTextToast, new ToastOptions + { + Position = (ToastPosition)this.toastPosition, + Speed = (ToastSpeed)this.toastSpeed, + }); + } + + if (ImGui.Button("Show Quest toast")) + { + this.dalamud.Framework.Gui.Toast.ShowQuest(this.inputTextToast, new QuestToastOptions + { + Position = (QuestToastPosition)this.questToastPosition, + DisplayCheckmark = this.questToastCheckmark, + IconId = (uint)this.questToastIconId, + PlaySound = this.questToastSound, + }); + } + + if (ImGui.Button("Show Error toast")) + { + this.dalamud.Framework.Gui.Toast.ShowError(this.inputTextToast); + } + } + + private void DrawImGui() + { + ImGui.Text("Monitor count: " + ImGui.GetPlatformIO().Monitors.Size); + ImGui.Text("OverrideGameCursor: " + this.dalamud.InterfaceManager.OverrideGameCursor); + + ImGui.Button("THIS IS A BUTTON###hoverTestButton"); + this.dalamud.InterfaceManager.OverrideGameCursor = !ImGui.IsItemHovered(); + } + + private void DrawTex() + { + ImGui.InputText("Tex Path", ref this.inputTexPath, 255); + ImGui.InputFloat2("UV0", ref this.inputTexUv0); + ImGui.InputFloat2("UV1", ref this.inputTexUv1); + ImGui.InputFloat4("Tint", ref this.inputTintCol); + ImGui.InputFloat2("Scale", ref this.inputTexScale); + + if (ImGui.Button("Load Tex")) + { + try + { + this.debugTex = this.dalamud.Data.GetImGuiTexture(this.inputTexPath); + this.inputTexScale = new Vector2(this.debugTex.Width, this.debugTex.Height); + } + catch (Exception ex) + { + Log.Error(ex, "Could not load tex."); + } + } + + ImGuiHelpers.ScaledDummy(10); + + if (this.debugTex != null) + { + ImGui.Image(this.debugTex.ImGuiHandle, this.inputTexScale, this.inputTexUv0, this.inputTexUv1, this.inputTintCol); + ImGuiHelpers.ScaledDummy(5); + Util.ShowObject(this.debugTex); + } + } + + private void DrawGamepad() + { + static void DrawHelper(string text, uint mask, Func resolve) + { + ImGui.Text($"{text} {mask:X4}"); + ImGui.Text($"DPadLeft {resolve(GamepadButtons.DpadLeft)} " + + $"DPadUp {resolve(GamepadButtons.DpadUp)} " + + $"DPadRight {resolve(GamepadButtons.DpadRight)} " + + $"DPadDown {resolve(GamepadButtons.DpadDown)} "); + ImGui.Text($"West {resolve(GamepadButtons.West)} " + + $"North {resolve(GamepadButtons.North)} " + + $"East {resolve(GamepadButtons.East)} " + + $"South {resolve(GamepadButtons.South)} "); + ImGui.Text($"L1 {resolve(GamepadButtons.L1)} " + + $"L2 {resolve(GamepadButtons.L2)} " + + $"R1 {resolve(GamepadButtons.R1)} " + + $"R2 {resolve(GamepadButtons.R2)} "); + ImGui.Text($"Select {resolve(GamepadButtons.Select)} " + + $"Start {resolve(GamepadButtons.Start)} " + + $"L3 {resolve(GamepadButtons.L3)} " + + $"R3 {resolve(GamepadButtons.R3)} "); + } +#if DEBUG + ImGui.Text($"GamepadInput 0x{this.dalamud.ClientState.GamepadState.GamepadInput.ToInt64():X}"); + + if (ImGui.IsItemHovered()) + ImGui.SetMouseCursor(ImGuiMouseCursor.Hand); + + if (ImGui.IsItemClicked()) + ImGui.SetClipboardText($"0x{this.dalamud.ClientState.GamepadState.GamepadInput.ToInt64():X}"); +#endif + + DrawHelper( + "Buttons Raw", + this.dalamud.ClientState.GamepadState.ButtonsRaw, + this.dalamud.ClientState.GamepadState.Raw); + DrawHelper( + "Buttons Pressed", + this.dalamud.ClientState.GamepadState.ButtonsPressed, + this.dalamud.ClientState.GamepadState.Pressed); + DrawHelper( + "Buttons Repeat", + this.dalamud.ClientState.GamepadState.ButtonsRepeat, + this.dalamud.ClientState.GamepadState.Repeat); + DrawHelper( + "Buttons Released", + this.dalamud.ClientState.GamepadState.ButtonsReleased, + this.dalamud.ClientState.GamepadState.Released); + ImGui.Text($"LeftStickLeft {this.dalamud.ClientState.GamepadState.LeftStickLeft:0.00} " + + $"LeftStickUp {this.dalamud.ClientState.GamepadState.LeftStickUp:0.00} " + + $"LeftStickRight {this.dalamud.ClientState.GamepadState.LeftStickRight:0.00} " + + $"LeftStickDown {this.dalamud.ClientState.GamepadState.LeftStickDown:0.00} "); + ImGui.Text($"RightStickLeft {this.dalamud.ClientState.GamepadState.RightStickLeft:0.00} " + + $"RightStickUp {this.dalamud.ClientState.GamepadState.RightStickUp:0.00} " + + $"RightStickRight {this.dalamud.ClientState.GamepadState.RightStickRight:0.00} " + + $"RightStickDown {this.dalamud.ClientState.GamepadState.RightStickDown:0.00} "); + } + private void Load() { if (this.dalamud.Data.IsDataReady) From 4e24c8fa3ac7f6681886ab0698af9d4aeeed3009 Mon Sep 17 00:00:00 2001 From: Raymond Date: Thu, 15 Jul 2021 18:30:10 -0400 Subject: [PATCH 07/12] feat: Fate Table --- Dalamud/Game/ClientState/ClientState.cs | 8 + .../ClientState/ClientStateAddressResolver.cs | 16 ++ Dalamud/Game/ClientState/Fates/FateTable.cs | 178 ++++++++++++++++++ Dalamud/Game/ClientState/Fates/Types/Fate.cs | 138 ++++++++++++++ .../ClientState/Fates/Types/FateOffsets.cs | 21 +++ .../Game/ClientState/Fates/Types/FateState.cs | 33 ++++ .../Interface/Internal/Windows/DataWindow.cs | 43 +++++ Dalamud/Memory/MemoryHelper.cs | 29 ++- 8 files changed, 465 insertions(+), 1 deletion(-) create mode 100644 Dalamud/Game/ClientState/Fates/FateTable.cs create mode 100644 Dalamud/Game/ClientState/Fates/Types/Fate.cs create mode 100644 Dalamud/Game/ClientState/Fates/Types/FateOffsets.cs create mode 100644 Dalamud/Game/ClientState/Fates/Types/FateState.cs diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index d67bac813..b9e40ccd7 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -4,6 +4,7 @@ using System.Runtime.InteropServices; using Dalamud.Game.ClientState.Actors; using Dalamud.Game.ClientState.Actors.Types; +using Dalamud.Game.ClientState.Fates; using Dalamud.Game.Internal; using Dalamud.Hooking; using JetBrains.Annotations; @@ -42,6 +43,8 @@ namespace Dalamud.Game.ClientState this.Actors = new ActorTable(dalamud, this.address); + this.Fates = new FateTable(dalamud, this.address); + this.PartyList = new PartyList(dalamud, this.address); this.JobGauges = new JobGauges(this.address); @@ -97,6 +100,11 @@ namespace Dalamud.Game.ClientState /// public ActorTable Actors { get; } + /// + /// Gets the table of all present fates. + /// + public FateTable Fates { get; } + /// /// Gets the language of the client. /// diff --git a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs index ed87f06d5..275809f82 100644 --- a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs +++ b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; using Dalamud.Game.Internal; @@ -9,6 +10,8 @@ namespace Dalamud.Game.ClientState /// public sealed class ClientStateAddressResolver : BaseAddressResolver { + private delegate IntPtr GetFateTableDelegate(); + // Static offsets /// @@ -16,6 +19,14 @@ namespace Dalamud.Game.ClientState /// public IntPtr ActorTable { get; private set; } + /// + /// Gets the address of the fate table pointer. + /// + /// + /// This is a static address into the table, not the address of the table itself. + /// + public IntPtr FateTablePtr { get; private set; } + // public IntPtr ViewportActorTable { get; private set; } /// @@ -68,8 +79,13 @@ namespace Dalamud.Game.ClientState // We don't need those anymore, but maybe someone else will - let's leave them here for good measure // ViewportActorTable = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? 85 ED", 0) + 0x148; // SomeActorTableAccess = sig.ScanText("E8 ?? ?? ?? ?? 48 8D 55 A0 48 8D 8E ?? ?? ?? ??"); + this.ActorTable = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 44 0F B6 83"); + var getFateTableAddr = sig.ScanText("E8 ?? ?? ?? ?? 48 8B C8 E8 ?? ?? ?? ?? 80 BF ?? ?? ?? ?? ??"); + var getFateTable = Marshal.GetDelegateForFunctionPointer(getFateTableAddr); + this.FateTablePtr = getFateTable(); + this.LocalContentId = sig.GetStaticAddressFromSig("48 0F 44 05 ?? ?? ?? ?? 48 39 07"); this.JobGaugeData = sig.GetStaticAddressFromSig("E8 ?? ?? ?? ?? FF C6 48 8D 5B 0C", 0xB9) + 0x10; diff --git a/Dalamud/Game/ClientState/Fates/FateTable.cs b/Dalamud/Game/ClientState/Fates/FateTable.cs new file mode 100644 index 000000000..07158820e --- /dev/null +++ b/Dalamud/Game/ClientState/Fates/FateTable.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +using Dalamud.Game.ClientState.Fates.Types; +using JetBrains.Annotations; +using Serilog; + +namespace Dalamud.Game.ClientState.Fates +{ + /// + /// This collection represents the currently available Fate events. + /// + public sealed partial class FateTable + { + // If the pointer at this offset is 0, do not scan the table + private const int CheckPtrOffset = 0x80; + private const int FirstPtrOffset = 0x90; + private const int LastPtrOffset = 0x98; + + private readonly Dalamud dalamud; + private readonly ClientStateAddressResolver address; + + /// + /// Initializes a new instance of the class. + /// + /// The instance. + /// Client state address resolver. + internal FateTable(Dalamud dalamud, ClientStateAddressResolver addressResolver) + { + this.address = addressResolver; + this.dalamud = dalamud; + + Log.Verbose($"Fate table address 0x{this.address.FateTablePtr.ToInt64():X}"); + } + + /// + /// Gets the amount of currently active Fates. + /// + public unsafe int Length + { + get + { + var fateTable = this.FateTableAddress; + if (fateTable == IntPtr.Zero) + return 0; + + var check = *(long*)(fateTable + CheckPtrOffset); + if (check == 0) + return 0; + + var start = *(long*)(fateTable + FirstPtrOffset); + var end = *(long*)(fateTable + LastPtrOffset); + if (start == 0 || end == 0) + return 0; + + return (int)((end - start) / 8); + } + } + + private unsafe IntPtr FateTableAddress + { + get + { + if (this.address.FateTablePtr == IntPtr.Zero) + return IntPtr.Zero; + + return *(IntPtr*)this.address.FateTablePtr; + } + } + + /// + /// Get an actor at the specified spawn index. + /// + /// Spawn index. + /// A at the specified spawn index. + [CanBeNull] + public Fate this[int index] + { + get + { + var address = this.GetFateAddress(index); + return this[address]; + } + } + + /// + /// Get a Fate at the specified address. + /// + /// The Fate address. + /// A at the specified address. + public Fate this[IntPtr address] + { + get + { + if (address == IntPtr.Zero) + return null; + + return this.CreateFateReference(address); + } + } + + /// + /// Gets the address of the Fate at the specified index of the fate table. + /// + /// The index of the Fate. + /// The memory address of the Fate. + public unsafe IntPtr GetFateAddress(int index) + { + if (index >= this.Length) + return IntPtr.Zero; + + var fateTable = this.FateTableAddress; + if (fateTable == IntPtr.Zero) + return IntPtr.Zero; + + var firstFate = *(IntPtr*)(fateTable + FirstPtrOffset); + return *(IntPtr*)(firstFate + (8 * index)); + } + + /// + /// Create a reference to a FFXIV actor. + /// + /// The offset of the actor in memory. + /// object containing requested data. + [CanBeNull] + internal unsafe Fate CreateFateReference(IntPtr offset) + { + if (this.dalamud.ClientState.LocalContentId == 0) + return null; + + if (offset == IntPtr.Zero) + return null; + + return new Fate(offset, this.dalamud); + } + } + + /// + /// This collection represents the currently available Fate events. + /// + public sealed partial class FateTable : IReadOnlyCollection, ICollection + { + /// + int IReadOnlyCollection.Count => this.Length; + + /// + int ICollection.Count => this.Length; + + /// + bool ICollection.IsSynchronized => false; + + /// + object ICollection.SyncRoot => this; + + /// + public IEnumerator GetEnumerator() + { + for (var i = 0; i < this.Length; i++) + { + yield return this[i]; + } + } + + /// + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + + /// + void ICollection.CopyTo(Array array, int index) + { + for (var i = 0; i < this.Length; i++) + { + array.SetValue(this[i], index); + index++; + } + } + } +} diff --git a/Dalamud/Game/ClientState/Fates/Types/Fate.cs b/Dalamud/Game/ClientState/Fates/Types/Fate.cs new file mode 100644 index 000000000..a42e3f925 --- /dev/null +++ b/Dalamud/Game/ClientState/Fates/Types/Fate.cs @@ -0,0 +1,138 @@ +using System; + +using Dalamud.Game.ClientState.Resolvers; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Memory; +using FFXIVClientStructs.FFXIV.Client.System.String; + +namespace Dalamud.Game.ClientState.Fates.Types +{ + /// + /// This class represents an FFXIV Fate. + /// + public unsafe partial class Fate : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + /// The address of this fate in memory. + /// Dalamud instance. + internal Fate(IntPtr address, Dalamud dalamud) + { + this.Address = address; + this.Dalamud = dalamud; + } + + /// + /// Gets the address of this Fate in memory. + /// + public IntPtr Address { get; } + + /// + /// Gets Dalamud itself. + /// + private protected Dalamud Dalamud { get; } + + public static bool operator ==(Fate fate1, Fate fate2) + { + if (fate1 is null || fate2 is null) + return Equals(fate1, fate2); + + return fate1.Equals(fate2); + } + + public static bool operator !=(Fate fate1, Fate fate2) => !(fate1 == fate2); + + /// + /// Gets a value indicating whether this Fate is still valid in memory. + /// + /// The fate to check. + /// True or false. + public static bool IsValid(Fate fate) + { + if (fate == null) + return false; + + if (fate.Dalamud.ClientState.LocalContentId == 0) + return false; + + return true; + } + + /// + /// Gets a value indicating whether this actor is still valid in memory. + /// + /// True or false. + public bool IsValid() => IsValid(this); + + /// + bool IEquatable.Equals(Fate other) => this.FateId == other?.FateId; + + /// + public override bool Equals(object obj) => ((IEquatable)this).Equals(obj as Fate); + + /// + public override int GetHashCode() => this.FateId.GetHashCode(); + } + + /// + /// This class represents an FFXIV Fate. + /// + public unsafe partial class Fate + { + /// + /// Gets the Fate ID of this . + /// + public ushort FateId => *(ushort*)(this.Address + FateOffsets.FateId); + + /// + /// Gets game data linked to this Fate. + /// + public Lumina.Excel.GeneratedSheets.Fate GameData => this.Dalamud.Data.GetExcelSheet().GetRow(this.FateId); + + /// + /// Gets the time this started. + /// + public int StartTimeEpoch => *(int*)(this.Address + FateOffsets.StartTimeEpoch); + + /// + /// Gets how long this will run. + /// + public short Duration => *(short*)(this.Address + FateOffsets.Duration); + + /// + /// Gets the remaining time in seconds for this . + /// + public long TimeRemaining => this.StartTimeEpoch + this.Duration - DateTimeOffset.Now.ToUnixTimeSeconds(); + + /// + /// Gets the displayname of this . + /// + public SeString Name => MemoryHelper.ReadSeString((Utf8String*)(this.Address + FateOffsets.Name)); + + /// + /// Gets the state of this (Running, Ended, Failed, Preparation, WaitingForEnd). + /// + public FateState State => *(FateState*)(this.Address + FateOffsets.State); + + /// + /// Gets the progress amount of this . + /// + public byte Progress => *(byte*)(this.Address + FateOffsets.Progress); + + /// + /// Gets the level of this . + /// + public byte Level => *(byte*)(this.Address + FateOffsets.Level); + + /// + /// Gets the position of this . + /// + public Position3 Position => *(Position3*)(this.Address + FateOffsets.Position); + + /// + /// Gets the territory this is located in. + /// + public TerritoryTypeResolver TerritoryType => new(*(ushort*)(this.Address + FateOffsets.Territory), this.Dalamud); + } +} diff --git a/Dalamud/Game/ClientState/Fates/Types/FateOffsets.cs b/Dalamud/Game/ClientState/Fates/Types/FateOffsets.cs new file mode 100644 index 000000000..73bc7a702 --- /dev/null +++ b/Dalamud/Game/ClientState/Fates/Types/FateOffsets.cs @@ -0,0 +1,21 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Dalamud.Game.ClientState.Fates.Types +{ + /// + /// Memory offsets for the type. + /// + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Document the offset usage instead.")] + public static class FateOffsets + { + public const int FateId = 0x18; + public const int StartTimeEpoch = 0x20; + public const int Duration = 0x28; + public const int Name = 0xC0; + public const int State = 0x3AC; + public const int Progress = 0x3B8; + public const int Level = 0x3F9; + public const int Position = 0x450; + public const int Territory = 0x74E; + } +} diff --git a/Dalamud/Game/ClientState/Fates/Types/FateState.cs b/Dalamud/Game/ClientState/Fates/Types/FateState.cs new file mode 100644 index 000000000..94eb00717 --- /dev/null +++ b/Dalamud/Game/ClientState/Fates/Types/FateState.cs @@ -0,0 +1,33 @@ +namespace Dalamud.Game.ClientState.Fates.Types +{ + /// + /// This represents the state of a single Fate. + /// + public enum FateState : byte + { + /// + /// The Fate is active. + /// + Running = 0x02, + + /// + /// The Fate has ended. + /// + Ended = 0x04, + + /// + /// The player failed the Fate. + /// + Failed = 0x05, + + /// + /// The Fate is preparing to run. + /// + Preparation = 0x07, + + /// + /// The Fate is preparing to end. + /// + WaitingForEnd = 0x08, + } +} diff --git a/Dalamud/Interface/Internal/Windows/DataWindow.cs b/Dalamud/Interface/Internal/Windows/DataWindow.cs index f107a8041..6b723098f 100644 --- a/Dalamud/Interface/Internal/Windows/DataWindow.cs +++ b/Dalamud/Interface/Internal/Windows/DataWindow.cs @@ -86,6 +86,7 @@ namespace Dalamud.Interface.Internal.Windows Server_OpCode, Address, Actor_Table, + Fate_Table, Font_Test, Party_List, Plugin_IPC, @@ -177,6 +178,8 @@ namespace Dalamud.Interface.Internal.Windows this.DrawActorTable(); break; + case DataKind.Fate_Table: + this.DrawFateTable(); break; case DataKind.Font_Test: @@ -373,6 +376,46 @@ namespace Dalamud.Interface.Internal.Windows } } + private void DrawFateTable() + { + var stateString = string.Empty; + if (this.dalamud.ClientState.Fates.Length == 0) + { + ImGui.TextUnformatted("No fates or data not ready."); + } + else + { + stateString += $"FrameworkBase: {this.dalamud.Framework.Address.BaseAddress.ToInt64():X}\n"; + stateString += $"FateTableLen: {this.dalamud.ClientState.Fates.Length}\n"; + + ImGui.TextUnformatted(stateString); + + for (var i = 0; i < this.dalamud.ClientState.Fates.Length; i++) + { + var fate = this.dalamud.ClientState.Fates[i]; + if (fate == null) + continue; + + var fateString = $"{fate.Address.ToInt64():X}:[{i}]" + + $" - Lv.{fate.Level} {fate.Name} ({fate.Progress}%)" + + $" - X{fate.Position.X} Y{fate.Position.Y} Z{fate.Position.Z}" + + $" - Territory {(this.resolveGameData ? (fate.TerritoryType.GameData?.Name ?? fate.TerritoryType.Id.ToString()) : fate.TerritoryType.Id.ToString())}\n"; + + fateString += $" StartTimeEpoch: {fate.StartTimeEpoch}" + + $" - Duration: {fate.Duration}" + + $" - State: {fate.State}" + + $" - GameData name: {(this.resolveGameData ? (fate.GameData?.Name ?? fate.FateId.ToString()) : fate.FateId.ToString())}"; + + ImGui.TextUnformatted(fateString); + ImGui.SameLine(); + if (ImGui.Button("C")) + { + ImGui.SetClipboardText(fate.Address.ToString("X")); + } + } + } + } + private void DrawFontTest() { var specialChars = string.Empty; diff --git a/Dalamud/Memory/MemoryHelper.cs b/Dalamud/Memory/MemoryHelper.cs index b962d637e..66a5908ee 100644 --- a/Dalamud/Memory/MemoryHelper.cs +++ b/Dalamud/Memory/MemoryHelper.cs @@ -6,7 +6,7 @@ using System.Text; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Memory.Exceptions; - +using FFXIVClientStructs.FFXIV.Client.System.String; using static Dalamud.NativeFunctions; // Heavily inspired from Reloaded (https://github.com/Reloaded-Project/Reloaded.Memory) @@ -237,6 +237,25 @@ namespace Dalamud.Memory } } + /// + /// Read an SeString from a specified Utf8String structure. + /// + /// The memory address to read from. + /// The read in string. + public static unsafe SeString ReadSeString(Utf8String* utf8String) + { + if (utf8String == null) + return string.Empty; + + var ptr = utf8String->StringPtr; + if (ptr == null) + return string.Empty; + + var len = Math.Max(utf8String->BufUsed, utf8String->StringLength); + + return ReadSeString((IntPtr)ptr, (int)len); + } + #endregion #region ReadString(out) @@ -306,6 +325,14 @@ namespace Dalamud.Memory public static void ReadSeString(IntPtr memoryAddress, int maxLength, out SeString value) => value = ReadSeString(memoryAddress, maxLength); + /// + /// Read an SeString from a specified Utf8String structure. + /// + /// The memory address to read from. + /// The read in string. + public static unsafe void ReadSeString(Utf8String* utf8String, out SeString value) + => value = ReadSeString(utf8String); + #endregion #region Write From 2e26bf8000d7e82df474c691f7700fba62b6d1bf Mon Sep 17 00:00:00 2001 From: Raymond Date: Thu, 15 Jul 2021 18:24:01 -0400 Subject: [PATCH 08/12] Widen suppression scope for coreplugin --- Dalamud.CorePlugin/GlobalSuppressions.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Dalamud.CorePlugin/GlobalSuppressions.cs b/Dalamud.CorePlugin/GlobalSuppressions.cs index 9b5d219bb..bfaba20d0 100644 --- a/Dalamud.CorePlugin/GlobalSuppressions.cs +++ b/Dalamud.CorePlugin/GlobalSuppressions.cs @@ -6,11 +6,12 @@ using System.Diagnostics.CodeAnalysis; // General -[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines", Justification = "Preventing long lines", Scope = "namespaceanddescendants", Target = "~N:Dalamud.CorePlugin")] -[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "I like regions", Scope = "namespaceanddescendants", Target = "~N:Dalamud.CorePlugin")] -[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1123:Do not place regions within elements", Justification = "I like regions in elements too", Scope = "namespaceanddescendants", Target = "~N:Dalamud.CorePlugin")] -[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying", Scope = "namespaceanddescendants", Target = "~N:Dalamud.CorePlugin")] -[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud.CorePlugin")] -[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud.CorePlugin")] -[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1127:Generic type constraints should be on their own line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud.CorePlugin")] +[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines", Justification = "Preventing long lines", Scope = "namespaceanddescendants", Target = "~N:Dalamud")] +[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "I like regions", Scope = "namespaceanddescendants", Target = "~N:Dalamud")] +[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1123:Do not place regions within elements", Justification = "I like regions in elements too", Scope = "namespaceanddescendants", Target = "~N:Dalamud")] +[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying", Scope = "namespaceanddescendants", Target = "~N:Dalamud")] +[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")] +[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")] +[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1127:Generic type constraints should be on their own line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")] +[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")] [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")] From 639c7b61830c7ec3df2fecc9d1c4da24d8dd2bdb Mon Sep 17 00:00:00 2001 From: Raymond Date: Thu, 15 Jul 2021 18:24:42 -0400 Subject: [PATCH 09/12] Add window stub as default --- Dalamud.CorePlugin/PluginImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud.CorePlugin/PluginImpl.cs b/Dalamud.CorePlugin/PluginImpl.cs index d2f140d72..4d635eeb5 100644 --- a/Dalamud.CorePlugin/PluginImpl.cs +++ b/Dalamud.CorePlugin/PluginImpl.cs @@ -32,7 +32,7 @@ namespace Dalamud.CorePlugin this.Interface = pluginInterface; - // this.windowSystem.AddWindow(your_window); + this.windowSystem.AddWindow(new PluginWindow(Dalamud.Instance)); this.Interface.UiBuilder.OnBuildUi += this.OnDraw; this.Interface.UiBuilder.OnOpenConfigUi += this.OnOpenConfigUi; From 4fbdec34d3c1b7342bd3461d72dcea8505c6fd71 Mon Sep 17 00:00:00 2001 From: Raymond Date: Thu, 15 Jul 2021 18:35:04 -0400 Subject: [PATCH 10/12] Linter --- Dalamud/Memory/MemoryHelper.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Dalamud/Memory/MemoryHelper.cs b/Dalamud/Memory/MemoryHelper.cs index 66a5908ee..0e72955af 100644 --- a/Dalamud/Memory/MemoryHelper.cs +++ b/Dalamud/Memory/MemoryHelper.cs @@ -7,6 +7,7 @@ using System.Text; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Memory.Exceptions; using FFXIVClientStructs.FFXIV.Client.System.String; + using static Dalamud.NativeFunctions; // Heavily inspired from Reloaded (https://github.com/Reloaded-Project/Reloaded.Memory) From f93773e2caadba5f1872e9d2d8fe00f253043388 Mon Sep 17 00:00:00 2001 From: Raymond Date: Fri, 16 Jul 2021 08:47:28 -0400 Subject: [PATCH 11/12] Apply requested change --- Dalamud/Game/ClientState/Actors/ActorTable.cs | 34 +++++-------------- .../Actors/{ => Types}/CustomizeIndex.cs | 2 +- .../Actors/{ => Types}/ObjectKind.cs | 2 +- .../Actors/{ => Types}/StatusFlags.cs | 2 +- .../ClientState/ClientStateAddressResolver.cs | 14 +++----- 5 files changed, 16 insertions(+), 38 deletions(-) rename Dalamud/Game/ClientState/Actors/{ => Types}/CustomizeIndex.cs (98%) rename Dalamud/Game/ClientState/Actors/{ => Types}/ObjectKind.cs (97%) rename Dalamud/Game/ClientState/Actors/{ => Types}/StatusFlags.cs (95%) diff --git a/Dalamud/Game/ClientState/Actors/ActorTable.cs b/Dalamud/Game/ClientState/Actors/ActorTable.cs index 5a7cfc4cd..a139f8b48 100644 --- a/Dalamud/Game/ClientState/Actors/ActorTable.cs +++ b/Dalamud/Game/ClientState/Actors/ActorTable.cs @@ -64,22 +64,6 @@ namespace Dalamud.Game.ClientState.Actors get { var address = this.GetActorAddress(index); - return this[address]; - } - } - - /// - /// Get an actor at the specified address. - /// - /// The actor address. - /// An at the specified address. - public Actor this[IntPtr address] - { - get - { - if (address == IntPtr.Zero) - return null; - return this.CreateActorReference(address); } } @@ -100,25 +84,25 @@ namespace Dalamud.Game.ClientState.Actors /// /// Create a reference to a FFXIV actor. /// - /// The offset of the actor in memory. + /// The address of the actor in memory. /// object or inheritor containing requested data. [CanBeNull] - internal unsafe Actor CreateActorReference(IntPtr offset) + public unsafe Actor CreateActorReference(IntPtr address) { if (this.dalamud.ClientState.LocalContentId == 0) return null; - if (offset == IntPtr.Zero) + if (address == IntPtr.Zero) return null; - var objKind = *(ObjectKind*)(offset + ActorOffsets.ObjectKind); + var objKind = *(ObjectKind*)(address + ActorOffsets.ObjectKind); return objKind switch { - ObjectKind.Player => new PlayerCharacter(offset, this.dalamud), - ObjectKind.BattleNpc => new BattleNpc(offset, this.dalamud), - ObjectKind.EventObj => new EventObj(offset, this.dalamud), - ObjectKind.Companion => new Npc(offset, this.dalamud), - _ => new Actor(offset, this.dalamud), + ObjectKind.Player => new PlayerCharacter(address, this.dalamud), + ObjectKind.BattleNpc => new BattleNpc(address, this.dalamud), + ObjectKind.EventObj => new EventObj(address, this.dalamud), + ObjectKind.Companion => new Npc(address, this.dalamud), + _ => new Actor(address, this.dalamud), }; } } diff --git a/Dalamud/Game/ClientState/Actors/CustomizeIndex.cs b/Dalamud/Game/ClientState/Actors/Types/CustomizeIndex.cs similarity index 98% rename from Dalamud/Game/ClientState/Actors/CustomizeIndex.cs rename to Dalamud/Game/ClientState/Actors/Types/CustomizeIndex.cs index 2461a028a..23618ae24 100644 --- a/Dalamud/Game/ClientState/Actors/CustomizeIndex.cs +++ b/Dalamud/Game/ClientState/Actors/Types/CustomizeIndex.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.ClientState.Actors +namespace Dalamud.Game.ClientState.Actors.Types { /// /// This enum describes the indices of the Customize array. diff --git a/Dalamud/Game/ClientState/Actors/ObjectKind.cs b/Dalamud/Game/ClientState/Actors/Types/ObjectKind.cs similarity index 97% rename from Dalamud/Game/ClientState/Actors/ObjectKind.cs rename to Dalamud/Game/ClientState/Actors/Types/ObjectKind.cs index 4e62d812d..b79adb784 100644 --- a/Dalamud/Game/ClientState/Actors/ObjectKind.cs +++ b/Dalamud/Game/ClientState/Actors/Types/ObjectKind.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.ClientState.Actors +namespace Dalamud.Game.ClientState.Actors.Types { /// /// Enum describing possible entity kinds. diff --git a/Dalamud/Game/ClientState/Actors/StatusFlags.cs b/Dalamud/Game/ClientState/Actors/Types/StatusFlags.cs similarity index 95% rename from Dalamud/Game/ClientState/Actors/StatusFlags.cs rename to Dalamud/Game/ClientState/Actors/Types/StatusFlags.cs index bb3864cf5..a1101ceca 100644 --- a/Dalamud/Game/ClientState/Actors/StatusFlags.cs +++ b/Dalamud/Game/ClientState/Actors/Types/StatusFlags.cs @@ -1,6 +1,6 @@ using System; -namespace Dalamud.Game.ClientState.Actors +namespace Dalamud.Game.ClientState.Actors.Types { /// /// Enum describing possible status flags. diff --git a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs index 275809f82..a9937b40c 100644 --- a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs +++ b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs @@ -10,8 +10,6 @@ namespace Dalamud.Game.ClientState /// public sealed class ClientStateAddressResolver : BaseAddressResolver { - private delegate IntPtr GetFateTableDelegate(); - // Static offsets /// @@ -23,7 +21,7 @@ namespace Dalamud.Game.ClientState /// Gets the address of the fate table pointer. /// /// - /// This is a static address into the table, not the address of the table itself. + /// This is a static address to a pointer, not the address of the table itself. /// public IntPtr FateTablePtr { get; private set; } @@ -61,9 +59,6 @@ namespace Dalamud.Game.ClientState /// public IntPtr SetupTerritoryType { get; private set; } - // public IntPtr SomeActorTableAccess { get; private set; } - // public IntPtr PartyListUpdate { get; private set; } - /// /// Gets the address of the method which polls the gamepads for data. /// Called every frame, even when `Enable Gamepad` is off in the settings. @@ -82,16 +77,15 @@ namespace Dalamud.Game.ClientState this.ActorTable = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 44 0F B6 83"); - var getFateTableAddr = sig.ScanText("E8 ?? ?? ?? ?? 48 8B C8 E8 ?? ?? ?? ?? 80 BF ?? ?? ?? ?? ??"); - var getFateTable = Marshal.GetDelegateForFunctionPointer(getFateTableAddr); - this.FateTablePtr = getFateTable(); + this.FateTablePtr = sig.GetStaticAddressFromSig("48 8B 15 ?? ?? ?? ?? 48 8B F9 44 0F B7 41 ??"); this.LocalContentId = sig.GetStaticAddressFromSig("48 0F 44 05 ?? ?? ?? ?? 48 39 07"); this.JobGaugeData = sig.GetStaticAddressFromSig("E8 ?? ?? ?? ?? FF C6 48 8D 5B 0C", 0xB9) + 0x10; this.SetupTerritoryType = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F9 66 89 91 ?? ?? ?? ??"); - // This resolves to a fixed offset only, without the base address added in, so GetStaticAddressFromSig() can't be used + // This resolves to a fixed offset only, without the base address added in, + // so GetStaticAddressFromSig() can't be used. lea rcx, ds:1DB9F74h[rax*4] this.KeyboardState = sig.ScanText("48 8D 0C 85 ?? ?? ?? ?? 8B 04 31 85 C2 0F 85") + 0x4; // PartyListUpdate = sig.ScanText("E8 ?? ?? ?? ?? 49 8B D7 4C 8D 86 ?? ?? ?? ??"); From 974bc2108f675aadd70f775c80493c5160b711d4 Mon Sep 17 00:00:00 2001 From: Raymond Date: Fri, 16 Jul 2021 08:52:15 -0400 Subject: [PATCH 12/12] Missed a change reference --- Dalamud/Game/ClientState/Structs/PartyMember.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Game/ClientState/Structs/PartyMember.cs b/Dalamud/Game/ClientState/Structs/PartyMember.cs index 6acff52e7..926730e54 100644 --- a/Dalamud/Game/ClientState/Structs/PartyMember.cs +++ b/Dalamud/Game/ClientState/Structs/PartyMember.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.InteropServices; -using Dalamud.Game.ClientState.Actors; +using Dalamud.Game.ClientState.Actors.Types; namespace Dalamud.Game.ClientState.Structs {