From c0ab27189271e37a3e84e1603ed628013d0d3012 Mon Sep 17 00:00:00 2001 From: Raymond Date: Wed, 14 Jul 2021 16:58:06 -0400 Subject: [PATCH] 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