diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs
index 89dd8b8b1..e0a5df06d 100644
--- a/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs
+++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs
@@ -63,47 +63,37 @@ public interface IAetheryteEntry
}
///
-/// Class representing an aetheryte entry available to the game.
+/// This struct represents an aetheryte entry available to the game.
///
-internal sealed class AetheryteEntry : IAetheryteEntry
+/// Data read from the Aetheryte List.
+internal readonly struct AetheryteEntry(TeleportInfo data) : IAetheryteEntry
{
- private readonly TeleportInfo data;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Data read from the Aetheryte List.
- internal AetheryteEntry(TeleportInfo data)
- {
- this.data = data;
- }
+ ///
+ public uint AetheryteId => data.AetheryteId;
///
- public uint AetheryteId => this.data.AetheryteId;
+ public uint TerritoryId => data.TerritoryId;
///
- public uint TerritoryId => this.data.TerritoryId;
+ public byte SubIndex => data.SubIndex;
///
- public byte SubIndex => this.data.SubIndex;
+ public byte Ward => data.Ward;
///
- public byte Ward => this.data.Ward;
+ public byte Plot => data.Plot;
///
- public byte Plot => this.data.Plot;
+ public uint GilCost => data.GilCost;
///
- public uint GilCost => this.data.GilCost;
+ public bool IsFavourite => data.IsFavourite;
///
- public bool IsFavourite => this.data.IsFavourite;
+ public bool IsSharedHouse => data.IsSharedHouse;
///
- public bool IsSharedHouse => this.data.IsSharedHouse;
-
- ///
- public bool IsApartment => this.data.IsApartment;
+ public bool IsApartment => data.IsApartment;
///
public RowRef AetheryteData => LuminaUtils.CreateRef(this.AetheryteId);
diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs
index a3d44d423..f72339ed2 100644
--- a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs
+++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs
@@ -87,10 +87,7 @@ internal sealed partial class AetheryteList
///
public IEnumerator GetEnumerator()
{
- for (var i = 0; i < this.Length; i++)
- {
- yield return this[i];
- }
+ return new Enumerator(this);
}
///
@@ -98,4 +95,30 @@ internal sealed partial class AetheryteList
{
return this.GetEnumerator();
}
+
+ private struct Enumerator(AetheryteList aetheryteList) : IEnumerator
+ {
+ private int index = 0;
+
+ public IAetheryteEntry Current { get; private set; }
+
+ object IEnumerator.Current => this.Current;
+
+ public bool MoveNext()
+ {
+ if (this.index == aetheryteList.Length) return false;
+ this.Current = aetheryteList[this.index];
+ this.index++;
+ return true;
+ }
+
+ public void Reset()
+ {
+ this.index = 0;
+ }
+
+ public void Dispose()
+ {
+ }
+ }
}
diff --git a/Dalamud/Game/ClientState/Buddy/BuddyList.cs b/Dalamud/Game/ClientState/Buddy/BuddyList.cs
index 84cfd24a3..78809f8ba 100644
--- a/Dalamud/Game/ClientState/Buddy/BuddyList.cs
+++ b/Dalamud/Game/ClientState/Buddy/BuddyList.cs
@@ -8,6 +8,9 @@ using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
+using CSBuddy = FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy;
+using CSBuddyMember = FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember;
+
namespace Dalamud.Game.ClientState.Buddy;
///
@@ -21,7 +24,7 @@ namespace Dalamud.Game.ClientState.Buddy;
#pragma warning restore SA1015
internal sealed partial class BuddyList : IServiceType, IBuddyList
{
- private const uint InvalidObjectID = 0xE0000000;
+ private const uint InvalidEntityId = 0xE0000000;
[ServiceManager.ServiceDependency]
private readonly ClientState clientState = Service.Get();
@@ -69,7 +72,7 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList
}
}
- private unsafe FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy* BuddyListStruct => &UIState.Instance()->Buddy;
+ private unsafe CSBuddy* BuddyListStruct => &UIState.Instance()->Buddy;
///
public IBuddyMember? this[int index]
@@ -82,37 +85,37 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList
}
///
- public unsafe IntPtr GetCompanionBuddyMemberAddress()
+ public unsafe nint GetCompanionBuddyMemberAddress()
{
- return (IntPtr)this.BuddyListStruct->CompanionInfo.Companion;
+ return (nint)this.BuddyListStruct->CompanionInfo.Companion;
}
///
- public unsafe IntPtr GetPetBuddyMemberAddress()
+ public unsafe nint GetPetBuddyMemberAddress()
{
- return (IntPtr)this.BuddyListStruct->PetInfo.Pet;
+ return (nint)this.BuddyListStruct->PetInfo.Pet;
}
///
- public unsafe IntPtr GetBattleBuddyMemberAddress(int index)
+ public unsafe nint GetBattleBuddyMemberAddress(int index)
{
if (index < 0 || index >= 3)
- return IntPtr.Zero;
+ return 0;
- return (IntPtr)Unsafe.AsPointer(ref this.BuddyListStruct->BattleBuddies[index]);
+ return (nint)Unsafe.AsPointer(ref this.BuddyListStruct->BattleBuddies[index]);
}
///
- public IBuddyMember? CreateBuddyMemberReference(IntPtr address)
+ public unsafe IBuddyMember? CreateBuddyMemberReference(nint address)
{
+ if (address == 0)
+ return null;
+
if (this.clientState.LocalContentId == 0)
return null;
- if (address == IntPtr.Zero)
- return null;
-
- var buddy = new BuddyMember(address);
- if (buddy.ObjectId == InvalidObjectID)
+ var buddy = new BuddyMember((CSBuddyMember*)address);
+ if (buddy.EntityId == InvalidEntityId)
return null;
return buddy;
@@ -130,12 +133,35 @@ internal sealed partial class BuddyList
///
public IEnumerator GetEnumerator()
{
- for (var i = 0; i < this.Length; i++)
- {
- yield return this[i];
- }
+ return new Enumerator(this);
}
///
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
+
+ private struct Enumerator(BuddyList buddyList) : IEnumerator
+ {
+ private int index = 0;
+
+ public IBuddyMember Current { get; private set; }
+
+ object IEnumerator.Current => this.Current;
+
+ public bool MoveNext()
+ {
+ if (this.index == buddyList.Length) return false;
+ this.Current = buddyList[this.index];
+ this.index++;
+ return true;
+ }
+
+ public void Reset()
+ {
+ this.index = 0;
+ }
+
+ public void Dispose()
+ {
+ }
+ }
}
diff --git a/Dalamud/Game/ClientState/Buddy/BuddyMember.cs b/Dalamud/Game/ClientState/Buddy/BuddyMember.cs
index 393598d32..8018bafaf 100644
--- a/Dalamud/Game/ClientState/Buddy/BuddyMember.cs
+++ b/Dalamud/Game/ClientState/Buddy/BuddyMember.cs
@@ -1,20 +1,24 @@
+using System.Diagnostics.CodeAnalysis;
+
using Dalamud.Data;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Types;
using Lumina.Excel;
+using CSBuddyMember = FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember;
+
namespace Dalamud.Game.ClientState.Buddy;
///
/// Interface representing represents a buddy such as the chocobo companion, summoned pets, squadron groups and trust parties.
///
-public interface IBuddyMember
+public interface IBuddyMember : IEquatable
{
///
/// Gets the address of the buddy in memory.
///
- IntPtr Address { get; }
+ nint Address { get; }
///
/// Gets the object ID of this buddy.
@@ -67,42 +71,34 @@ public interface IBuddyMember
}
///
-/// This class represents a buddy such as the chocobo companion, summoned pets, squadron groups and trust parties.
+/// This struct represents a buddy such as the chocobo companion, summoned pets, squadron groups and trust parties.
///
-internal unsafe class BuddyMember : IBuddyMember
+/// A pointer to the BuddyMember.
+internal readonly unsafe struct BuddyMember(CSBuddyMember* ptr) : IBuddyMember
{
[ServiceManager.ServiceDependency]
private readonly ObjectTable objectTable = Service.Get();
- ///
- /// Initializes a new instance of the class.
- ///
- /// Buddy address.
- internal BuddyMember(IntPtr address)
- {
- this.Address = address;
- }
+ ///
+ public nint Address => (nint)ptr;
///
- public IntPtr Address { get; }
+ public uint ObjectId => this.EntityId;
///
- public uint ObjectId => this.Struct->EntityId;
+ public uint EntityId => ptr->EntityId;
///
- public uint EntityId => this.Struct->EntityId;
+ public IGameObject? GameObject => this.objectTable.SearchById(this.EntityId);
///
- public IGameObject? GameObject => this.objectTable.SearchById(this.ObjectId);
+ public uint CurrentHP => ptr->CurrentHealth;
///
- public uint CurrentHP => this.Struct->CurrentHealth;
+ public uint MaxHP => ptr->MaxHealth;
///
- public uint MaxHP => this.Struct->MaxHealth;
-
- ///
- public uint DataID => this.Struct->DataId;
+ public uint DataID => ptr->DataId;
///
public RowRef MountData => LuminaUtils.CreateRef(this.DataID);
@@ -113,5 +109,25 @@ internal unsafe class BuddyMember : IBuddyMember
///
public RowRef TrustData => LuminaUtils.CreateRef(this.DataID);
- private FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember* Struct => (FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember*)this.Address;
+ public static bool operator ==(BuddyMember x, BuddyMember y) => x.Equals(y);
+
+ public static bool operator !=(BuddyMember x, BuddyMember y) => !(x == y);
+
+ ///
+ public bool Equals(IBuddyMember? other)
+ {
+ return this.EntityId == other.EntityId;
+ }
+
+ ///
+ public override bool Equals([NotNullWhen(true)] object? obj)
+ {
+ return obj is BuddyMember fate && this.Equals(fate);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return this.EntityId.GetHashCode();
+ }
}
diff --git a/Dalamud/Game/ClientState/Fates/Fate.cs b/Dalamud/Game/ClientState/Fates/Fate.cs
index 504b690c3..c40a8960e 100644
--- a/Dalamud/Game/ClientState/Fates/Fate.cs
+++ b/Dalamud/Game/ClientState/Fates/Fate.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using Dalamud.Data;
@@ -6,10 +7,12 @@ using Dalamud.Memory;
using Lumina.Excel;
+using CSFateContext = FFXIVClientStructs.FFXIV.Client.Game.Fate.FateContext;
+
namespace Dalamud.Game.ClientState.Fates;
///
-/// Interface representing an fate entry that can be seen in the current area.
+/// Interface representing a fate entry that can be seen in the current area.
///
public interface IFate : IEquatable
{
@@ -111,133 +114,96 @@ public interface IFate : IEquatable
///
/// Gets the address of this Fate in memory.
///
- IntPtr Address { get; }
+ nint Address { get; }
}
///
-/// This class represents an FFXIV Fate.
+/// This struct represents a Fate.
///
-internal unsafe partial class Fate
+/// A pointer to the FateContext.
+internal readonly unsafe struct Fate(CSFateContext* ptr) : IFate
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The address of this fate in memory.
- internal Fate(IntPtr address)
- {
- this.Address = address;
- }
-
///
- public IntPtr Address { get; }
-
- private FFXIVClientStructs.FFXIV.Client.Game.Fate.FateContext* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Fate.FateContext*)this.Address;
-
- 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)
- {
- var clientState = Service.GetNullable();
-
- if (fate == null || clientState == null)
- return false;
-
- if (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);
+ public nint Address => (nint)ptr;
///
- bool IEquatable.Equals(IFate other) => this.FateId == other?.FateId;
-
- ///
- public override bool Equals(object obj) => ((IEquatable)this).Equals(obj as IFate);
-
- ///
- public override int GetHashCode() => this.FateId.GetHashCode();
-}
-
-///
-/// This class represents an FFXIV Fate.
-///
-internal unsafe partial class Fate : IFate
-{
- ///
- public ushort FateId => this.Struct->FateId;
+ public ushort FateId => ptr->FateId;
///
public RowRef GameData => LuminaUtils.CreateRef(this.FateId);
///
- public int StartTimeEpoch => this.Struct->StartTimeEpoch;
+ public int StartTimeEpoch => ptr->StartTimeEpoch;
///
- public short Duration => this.Struct->Duration;
+ public short Duration => ptr->Duration;
///
public long TimeRemaining => this.StartTimeEpoch + this.Duration - DateTimeOffset.Now.ToUnixTimeSeconds();
///
- public SeString Name => MemoryHelper.ReadSeString(&this.Struct->Name);
+ public SeString Name => MemoryHelper.ReadSeString(&ptr->Name);
///
- public SeString Description => MemoryHelper.ReadSeString(&this.Struct->Description);
+ public SeString Description => MemoryHelper.ReadSeString(&ptr->Description);
///
- public SeString Objective => MemoryHelper.ReadSeString(&this.Struct->Objective);
+ public SeString Objective => MemoryHelper.ReadSeString(&ptr->Objective);
///
- public FateState State => (FateState)this.Struct->State;
+ public FateState State => (FateState)ptr->State;
///
- public byte HandInCount => this.Struct->HandInCount;
+ public byte HandInCount => ptr->HandInCount;
///
- public byte Progress => this.Struct->Progress;
+ public byte Progress => ptr->Progress;
///
- public bool HasBonus => this.Struct->IsBonus;
+ public bool HasBonus => ptr->IsBonus;
///
- public uint IconId => this.Struct->IconId;
+ public uint IconId => ptr->IconId;
///
- public byte Level => this.Struct->Level;
+ public byte Level => ptr->Level;
///
- public byte MaxLevel => this.Struct->MaxLevel;
+ public byte MaxLevel => ptr->MaxLevel;
///
- public Vector3 Position => this.Struct->Location;
+ public Vector3 Position => ptr->Location;
///
- public float Radius => this.Struct->Radius;
+ public float Radius => ptr->Radius;
///
- public uint MapIconId => this.Struct->MapIconId;
+ public uint MapIconId => ptr->MapIconId;
///
/// Gets the territory this is located in.
///
- public RowRef TerritoryType => LuminaUtils.CreateRef(this.Struct->MapMarkers[0].MapMarkerData.TerritoryTypeId);
+ public RowRef TerritoryType => LuminaUtils.CreateRef(ptr->MapMarkers[0].MapMarkerData.TerritoryTypeId);
+
+ public static bool operator ==(Fate x, Fate y) => x.Equals(y);
+
+ public static bool operator !=(Fate x, Fate y) => !(x == y);
+
+ ///
+ public bool Equals(IFate? other)
+ {
+ return this.FateId == other.FateId;
+ }
+
+ ///
+ public override bool Equals([NotNullWhen(true)] object? obj)
+ {
+ return obj is Fate fate && this.Equals(fate);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return this.FateId.GetHashCode();
+ }
}
diff --git a/Dalamud/Game/ClientState/Fates/FateTable.cs b/Dalamud/Game/ClientState/Fates/FateTable.cs
index 1bf557ad5..a6edf4a18 100644
--- a/Dalamud/Game/ClientState/Fates/FateTable.cs
+++ b/Dalamud/Game/ClientState/Fates/FateTable.cs
@@ -5,6 +5,7 @@ using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Dalamud.Plugin.Services;
+using CSFateContext = FFXIVClientStructs.FFXIV.Client.Game.Fate.FateContext;
using CSFateManager = FFXIVClientStructs.FFXIV.Client.Game.Fate.FateManager;
namespace Dalamud.Game.ClientState.Fates;
@@ -25,7 +26,7 @@ internal sealed partial class FateTable : IServiceType, IFateTable
}
///
- public unsafe IntPtr Address => (nint)CSFateManager.Instance();
+ public unsafe nint Address => (nint)CSFateManager.Instance();
///
public unsafe int Length
@@ -72,30 +73,29 @@ internal sealed partial class FateTable : IServiceType, IFateTable
}
///
- public unsafe IntPtr GetFateAddress(int index)
+ public unsafe nint GetFateAddress(int index)
{
if (index >= this.Length)
- return IntPtr.Zero;
+ return 0;
var fateManager = CSFateManager.Instance();
if (fateManager == null)
- return IntPtr.Zero;
+ return 0;
- return (IntPtr)fateManager->Fates[index].Value;
+ return (nint)fateManager->Fates[index].Value;
}
///
- public IFate? CreateFateReference(IntPtr offset)
+ public unsafe IFate? CreateFateReference(IntPtr address)
{
- var clientState = Service.Get();
+ if (address == 0)
+ return null;
+ var clientState = Service.Get();
if (clientState.LocalContentId == 0)
return null;
- if (offset == IntPtr.Zero)
- return null;
-
- return new Fate(offset);
+ return new Fate((CSFateContext*)address);
}
}
@@ -110,12 +110,35 @@ internal sealed partial class FateTable
///
public IEnumerator GetEnumerator()
{
- for (var i = 0; i < this.Length; i++)
- {
- yield return this[i];
- }
+ return new Enumerator(this);
}
///
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
+
+ private struct Enumerator(FateTable fateTable) : IEnumerator
+ {
+ private int index = 0;
+
+ public IFate Current { get; private set; }
+
+ object IEnumerator.Current => this.Current;
+
+ public bool MoveNext()
+ {
+ if (this.index == fateTable.Length) return false;
+ this.Current = fateTable[this.index];
+ this.index++;
+ return true;
+ }
+
+ public void Reset()
+ {
+ this.index = 0;
+ }
+
+ public void Dispose()
+ {
+ }
+ }
}
diff --git a/Dalamud/Game/ClientState/Objects/ObjectTable.cs b/Dalamud/Game/ClientState/Objects/ObjectTable.cs
index 84c1b5693..598daf518 100644
--- a/Dalamud/Game/ClientState/Objects/ObjectTable.cs
+++ b/Dalamud/Game/ClientState/Objects/ObjectTable.cs
@@ -12,8 +12,6 @@ using Dalamud.Utility;
using FFXIVClientStructs.Interop;
-using Microsoft.Extensions.ObjectPool;
-
using CSGameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
using CSGameObjectManager = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObjectManager;
@@ -34,8 +32,6 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable
private readonly ClientState clientState;
private readonly CachedEntry[] cachedObjectTable;
- private readonly Enumerator?[] frameworkThreadEnumerators = new Enumerator?[4];
-
[ServiceManager.ServiceConstructor]
private unsafe ObjectTable(ClientState clientState)
{
@@ -47,9 +43,6 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable
this.cachedObjectTable = new CachedEntry[objectTableLength];
for (var i = 0; i < this.cachedObjectTable.Length; i++)
this.cachedObjectTable[i] = new(nativeObjectTable.GetPointer(i));
-
- for (var i = 0; i < this.frameworkThreadEnumerators.Length; i++)
- this.frameworkThreadEnumerators[i] = new(this, i);
}
///
@@ -239,30 +232,14 @@ internal sealed partial class ObjectTable
public IEnumerator GetEnumerator()
{
ThreadSafety.AssertMainThread();
-
- // If we're on the framework thread, see if there's an already allocated enumerator available for use.
- foreach (ref var x in this.frameworkThreadEnumerators.AsSpan())
- {
- if (x is not null)
- {
- var t = x;
- x = null;
- t.Reset();
- return t;
- }
- }
-
- // No reusable enumerator is available; allocate a new temporary one.
- return new Enumerator(this, -1);
+ return new Enumerator(this);
}
///
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
- private sealed class Enumerator(ObjectTable owner, int slotId) : IEnumerator, IResettable
+ private struct Enumerator(ObjectTable owner) : IEnumerator
{
- private ObjectTable? owner = owner;
-
private int index = -1;
public IGameObject Current { get; private set; } = null!;
@@ -274,7 +251,7 @@ internal sealed partial class ObjectTable
if (this.index == objectTableLength)
return false;
- var cache = this.owner!.cachedObjectTable.AsSpan();
+ var cache = owner.cachedObjectTable.AsSpan();
for (this.index++; this.index < objectTableLength; this.index++)
{
if (cache[this.index].Update() is { } ao)
@@ -291,17 +268,6 @@ internal sealed partial class ObjectTable
public void Dispose()
{
- if (this.owner is not { } o)
- return;
-
- if (slotId != -1)
- o.frameworkThreadEnumerators[slotId] = this;
- }
-
- public bool TryReset()
- {
- this.Reset();
- return true;
}
}
}
diff --git a/Dalamud/Game/ClientState/Party/PartyList.cs b/Dalamud/Game/ClientState/Party/PartyList.cs
index a016a8211..ec22932ab 100644
--- a/Dalamud/Game/ClientState/Party/PartyList.cs
+++ b/Dalamud/Game/ClientState/Party/PartyList.cs
@@ -8,6 +8,7 @@ using Dalamud.IoC.Internal;
using Dalamud.Plugin.Services;
using CSGroupManager = FFXIVClientStructs.FFXIV.Client.Game.Group.GroupManager;
+using CSPartyMember = FFXIVClientStructs.FFXIV.Client.Game.Group.PartyMember;
namespace Dalamud.Game.ClientState.Party;
@@ -42,20 +43,20 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList
public bool IsAlliance => this.GroupManagerStruct->MainGroup.AllianceFlags > 0;
///
- public unsafe IntPtr GroupManagerAddress => (nint)CSGroupManager.Instance();
+ public unsafe nint GroupManagerAddress => (nint)CSGroupManager.Instance();
///
- public IntPtr GroupListAddress => (IntPtr)Unsafe.AsPointer(ref GroupManagerStruct->MainGroup.PartyMembers[0]);
+ public nint GroupListAddress => (nint)Unsafe.AsPointer(ref GroupManagerStruct->MainGroup.PartyMembers[0]);
///
- public IntPtr AllianceListAddress => (IntPtr)Unsafe.AsPointer(ref this.GroupManagerStruct->MainGroup.AllianceMembers[0]);
+ public nint AllianceListAddress => (nint)Unsafe.AsPointer(ref this.GroupManagerStruct->MainGroup.AllianceMembers[0]);
///
public long PartyId => this.GroupManagerStruct->MainGroup.PartyId;
- private static int PartyMemberSize { get; } = Marshal.SizeOf();
+ private static int PartyMemberSize { get; } = Marshal.SizeOf();
- private FFXIVClientStructs.FFXIV.Client.Game.Group.GroupManager* GroupManagerStruct => (FFXIVClientStructs.FFXIV.Client.Game.Group.GroupManager*)this.GroupManagerAddress;
+ private CSGroupManager* GroupManagerStruct => (CSGroupManager*)this.GroupManagerAddress;
///
public IPartyMember? this[int index]
@@ -80,45 +81,45 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList
}
///
- public IntPtr GetPartyMemberAddress(int index)
+ public nint GetPartyMemberAddress(int index)
{
if (index < 0 || index >= GroupLength)
- return IntPtr.Zero;
+ return 0;
return this.GroupListAddress + (index * PartyMemberSize);
}
///
- public IPartyMember? CreatePartyMemberReference(IntPtr address)
+ public IPartyMember? CreatePartyMemberReference(nint address)
{
if (this.clientState.LocalContentId == 0)
return null;
- if (address == IntPtr.Zero)
+ if (address == 0)
return null;
- return new PartyMember(address);
+ return new PartyMember((CSPartyMember*)address);
}
///
- public IntPtr GetAllianceMemberAddress(int index)
+ public nint GetAllianceMemberAddress(int index)
{
if (index < 0 || index >= AllianceLength)
- return IntPtr.Zero;
+ return 0;
return this.AllianceListAddress + (index * PartyMemberSize);
}
///
- public IPartyMember? CreateAllianceMemberReference(IntPtr address)
+ public IPartyMember? CreateAllianceMemberReference(nint address)
{
if (this.clientState.LocalContentId == 0)
return null;
- if (address == IntPtr.Zero)
+ if (address == 0)
return null;
- return new PartyMember(address);
+ return new PartyMember((CSPartyMember*)address);
}
}
@@ -133,18 +134,44 @@ internal sealed partial class PartyList
///
public IEnumerator GetEnumerator()
{
- // Normally using Length results in a recursion crash, however we know the party size via ptr.
- for (var i = 0; i < this.Length; i++)
- {
- var member = this[i];
-
- if (member == null)
- break;
-
- yield return member;
- }
+ return new Enumerator(this);
}
///
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
+
+ private struct Enumerator(PartyList partyList) : IEnumerator
+ {
+ private int index = 0;
+
+ public IPartyMember Current { get; private set; }
+
+ object IEnumerator.Current => this.Current;
+
+ public bool MoveNext()
+ {
+ if (this.index == partyList.Length) return false;
+
+ for (; this.index < partyList.Length; this.index++)
+ {
+ var partyMember = partyList[this.index];
+ if (partyMember != null)
+ {
+ this.Current = partyMember;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public void Reset()
+ {
+ this.index = 0;
+ }
+
+ public void Dispose()
+ {
+ }
+ }
}
diff --git a/Dalamud/Game/ClientState/Party/PartyMember.cs b/Dalamud/Game/ClientState/Party/PartyMember.cs
index 4c738d866..c9980d9f2 100644
--- a/Dalamud/Game/ClientState/Party/PartyMember.cs
+++ b/Dalamud/Game/ClientState/Party/PartyMember.cs
@@ -1,26 +1,27 @@
+using System.Diagnostics.CodeAnalysis;
using System.Numerics;
-using System.Runtime.CompilerServices;
using Dalamud.Data;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.ClientState.Statuses;
using Dalamud.Game.Text.SeStringHandling;
-using Dalamud.Memory;
using Lumina.Excel;
+using CSPartyMember = FFXIVClientStructs.FFXIV.Client.Game.Group.PartyMember;
+
namespace Dalamud.Game.ClientState.Party;
///
/// Interface representing a party member.
///
-public interface IPartyMember
+public interface IPartyMember : IEquatable
{
///
/// Gets the address of this party member in memory.
///
- IntPtr Address { get; }
+ nint Address { get; }
///
/// Gets a list of buffs or debuffs applied to this party member.
@@ -108,69 +109,81 @@ public interface IPartyMember
}
///
-/// This class represents a party member in the group manager.
+/// This struct represents a party member in the group manager.
///
-internal unsafe class PartyMember : IPartyMember
+/// A pointer to the PartyMember.
+internal unsafe readonly struct PartyMember(CSPartyMember* ptr) : IPartyMember
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// Address of the party member.
- internal PartyMember(IntPtr address)
- {
- this.Address = address;
- }
+ ///
+ public nint Address => (nint)ptr;
///
- public IntPtr Address { get; }
+ public StatusList Statuses => new(&ptr->StatusManager);
///
- public StatusList Statuses => new(&this.Struct->StatusManager);
+ public Vector3 Position => ptr->Position;
///
- public Vector3 Position => this.Struct->Position;
+ public long ContentId => (long)ptr->ContentId;
///
- public long ContentId => (long)this.Struct->ContentId;
+ public uint ObjectId => ptr->EntityId;
///
- public uint ObjectId => this.Struct->EntityId;
-
- ///
- public uint EntityId => this.Struct->EntityId;
+ public uint EntityId => ptr->EntityId;
///
public IGameObject? GameObject => Service.Get().SearchById(this.EntityId);
///
- public uint CurrentHP => this.Struct->CurrentHP;
+ public uint CurrentHP => ptr->CurrentHP;
///
- public uint MaxHP => this.Struct->MaxHP;
+ public uint MaxHP => ptr->MaxHP;
///
- public ushort CurrentMP => this.Struct->CurrentMP;
+ public ushort CurrentMP => ptr->CurrentMP;
///
- public ushort MaxMP => this.Struct->MaxMP;
+ public ushort MaxMP => ptr->MaxMP;
///
- public RowRef Territory => LuminaUtils.CreateRef(this.Struct->TerritoryType);
+ public RowRef Territory => LuminaUtils.CreateRef(ptr->TerritoryType);
///
- public RowRef World => LuminaUtils.CreateRef(this.Struct->HomeWorld);
+ public RowRef World => LuminaUtils.CreateRef(ptr->HomeWorld);
///
- public SeString Name => SeString.Parse(this.Struct->Name);
+ public SeString Name => SeString.Parse(ptr->Name);
///
- public byte Sex => this.Struct->Sex;
+ public byte Sex => ptr->Sex;
///
- public RowRef ClassJob => LuminaUtils.CreateRef(this.Struct->ClassJob);
+ public RowRef ClassJob => LuminaUtils.CreateRef(ptr->ClassJob);
///
- public byte Level => this.Struct->Level;
+ public byte Level => ptr->Level;
- private FFXIVClientStructs.FFXIV.Client.Game.Group.PartyMember* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Group.PartyMember*)this.Address;
+ public static bool operator ==(PartyMember x, PartyMember y) => x.Equals(y);
+
+ public static bool operator !=(PartyMember x, PartyMember y) => !(x == y);
+
+ ///
+ public bool Equals(IPartyMember? other)
+ {
+ return this.EntityId == other.EntityId;
+ }
+
+ ///
+ public override bool Equals([NotNullWhen(true)] object? obj)
+ {
+ return obj is PartyMember fate && this.Equals(fate);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return this.EntityId.GetHashCode();
+ }
}
diff --git a/Dalamud/Game/ClientState/Statuses/Status.cs b/Dalamud/Game/ClientState/Statuses/Status.cs
index 2775f8f9b..160b15de5 100644
--- a/Dalamud/Game/ClientState/Statuses/Status.cs
+++ b/Dalamud/Game/ClientState/Statuses/Status.cs
@@ -1,61 +1,49 @@
+using System.Diagnostics.CodeAnalysis;
+
using Dalamud.Data;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Types;
using Lumina.Excel;
+using CSStatus = FFXIVClientStructs.FFXIV.Client.Game.Status;
+
namespace Dalamud.Game.ClientState.Statuses;
///
-/// This class represents a status effect an actor is afflicted by.
+/// Interface representing a status.
///
-public unsafe class Status
+public interface IStatus : IEquatable
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// Status address.
- internal Status(IntPtr address)
- {
- this.Address = address;
- }
-
///
/// Gets the address of the status in memory.
///
- public IntPtr Address { get; }
+ nint Address { get; }
///
/// Gets the status ID of this status.
///
- public uint StatusId => this.Struct->StatusId;
+ uint StatusId { get; }
///
/// Gets the GameData associated with this status.
///
- public RowRef GameData => LuminaUtils.CreateRef(this.Struct->StatusId);
+ RowRef GameData { get; }
///
/// Gets the parameter value of the status.
///
- public ushort Param => this.Struct->Param;
-
- ///
- /// Gets the stack count of this status.
- /// Only valid if this is a non-food status.
- ///
- [Obsolete($"Replaced with {nameof(Param)}", true)]
- public byte StackCount => (byte)this.Struct->Param;
+ ushort Param { get; }
///
/// Gets the time remaining of this status.
///
- public float RemainingTime => this.Struct->RemainingTime;
+ float RemainingTime { get; }
///
/// Gets the source ID of this status.
///
- public uint SourceId => this.Struct->SourceObject.ObjectId;
+ uint SourceId { get; }
///
/// Gets the source actor associated with this status.
@@ -63,7 +51,55 @@ public unsafe class Status
///
/// This iterates the actor table, it should be used with care.
///
+ IGameObject? SourceObject { get; }
+}
+
+///
+/// This struct represents a status effect an actor is afflicted by.
+///
+/// A pointer to the Status.
+internal unsafe readonly struct Status(CSStatus* ptr) : IStatus
+{
+ ///
+ public nint Address => (nint)ptr;
+
+ ///
+ public uint StatusId => ptr->StatusId;
+
+ ///
+ public RowRef GameData => LuminaUtils.CreateRef(ptr->StatusId);
+
+ ///
+ public ushort Param => ptr->Param;
+
+ ///
+ public float RemainingTime => ptr->RemainingTime;
+
+ ///
+ public uint SourceId => ptr->SourceObject.ObjectId;
+
+ ///
public IGameObject? SourceObject => Service.Get().SearchById(this.SourceId);
- private FFXIVClientStructs.FFXIV.Client.Game.Status* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Status*)this.Address;
+ public static bool operator ==(Status x, Status y) => x.Equals(y);
+
+ public static bool operator !=(Status x, Status y) => !(x == y);
+
+ ///
+ public bool Equals(IStatus? other)
+ {
+ return this.StatusId == other.StatusId && this.SourceId == other.SourceId && this.Param == other.Param && this.RemainingTime == other.RemainingTime;
+ }
+
+ ///
+ public override bool Equals([NotNullWhen(true)] object? obj)
+ {
+ return obj is Status fate && this.Equals(fate);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(this.StatusId, this.SourceId, this.Param, this.RemainingTime);
+ }
}
diff --git a/Dalamud/Game/ClientState/Statuses/StatusList.cs b/Dalamud/Game/ClientState/Statuses/StatusList.cs
index a38e45ea3..50d242d33 100644
--- a/Dalamud/Game/ClientState/Statuses/StatusList.cs
+++ b/Dalamud/Game/ClientState/Statuses/StatusList.cs
@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using CSStatus = FFXIVClientStructs.FFXIV.Client.Game.Status;
+
namespace Dalamud.Game.ClientState.Statuses;
///
@@ -14,7 +16,7 @@ public sealed unsafe partial class StatusList
/// Initializes a new instance of the class.
///
/// Address of the status list.
- internal StatusList(IntPtr address)
+ internal StatusList(nint address)
{
this.Address = address;
}
@@ -24,14 +26,14 @@ public sealed unsafe partial class StatusList
///
/// Pointer to the status list.
internal unsafe StatusList(void* pointer)
- : this((IntPtr)pointer)
+ : this((nint)pointer)
{
}
///
/// Gets the address of the status list in memory.
///
- public IntPtr Address { get; }
+ public nint Address { get; }
///
/// Gets the amount of status effect slots the actor has.
@@ -47,7 +49,7 @@ public sealed unsafe partial class StatusList
///
/// Status Index.
/// The status at the specified index.
- public Status? this[int index]
+ public IStatus? this[int index]
{
get
{
@@ -64,7 +66,7 @@ public sealed unsafe partial class StatusList
///
/// The address of the status list in memory.
/// The status object containing the requested data.
- public static StatusList? CreateStatusListReference(IntPtr address)
+ public static StatusList? CreateStatusListReference(nint address)
{
// The use case for CreateStatusListReference and CreateStatusReference to be static is so
// fake status lists can be generated. Since they aren't exposed as services, it's either
@@ -74,7 +76,7 @@ public sealed unsafe partial class StatusList
if (clientState.LocalContentId == 0)
return null;
- if (address == IntPtr.Zero)
+ if (address == 0)
return null;
return new StatusList(address);
@@ -85,17 +87,17 @@ public sealed unsafe partial class StatusList
///
/// The address of the status effect in memory.
/// The status object containing the requested data.
- public static Status? CreateStatusReference(IntPtr address)
+ public static IStatus? CreateStatusReference(nint address)
{
var clientState = Service.Get();
if (clientState.LocalContentId == 0)
return null;
- if (address == IntPtr.Zero)
+ if (address == 0)
return null;
- return new Status(address);
+ return new Status((CSStatus*)address);
}
///
@@ -103,22 +105,22 @@ public sealed unsafe partial class StatusList
///
/// The index of the status.
/// The memory address of the status.
- public IntPtr GetStatusAddress(int index)
+ public nint GetStatusAddress(int index)
{
if (index < 0 || index >= this.Length)
- return IntPtr.Zero;
+ return 0;
- return (IntPtr)Unsafe.AsPointer(ref this.Struct->Status[index]);
+ return (nint)Unsafe.AsPointer(ref this.Struct->Status[index]);
}
}
///
/// This collection represents the status effects an actor is afflicted by.
///
-public sealed partial class StatusList : IReadOnlyCollection, ICollection
+public sealed partial class StatusList : IReadOnlyCollection, ICollection
{
///
- int IReadOnlyCollection.Count => this.Length;
+ int IReadOnlyCollection.Count => this.Length;
///
int ICollection.Count => this.Length;
@@ -130,17 +132,9 @@ public sealed partial class StatusList : IReadOnlyCollection, ICollectio
object ICollection.SyncRoot => this;
///
- public IEnumerator GetEnumerator()
+ public IEnumerator GetEnumerator()
{
- for (var i = 0; i < this.Length; i++)
- {
- var status = this[i];
-
- if (status == null || status.StatusId == 0)
- continue;
-
- yield return status;
- }
+ return new Enumerator(this);
}
///
@@ -155,4 +149,39 @@ public sealed partial class StatusList : IReadOnlyCollection, ICollectio
index++;
}
}
+
+ private struct Enumerator(StatusList statusList) : IEnumerator
+ {
+ private int index = 0;
+
+ public IStatus Current { get; private set; }
+
+ object IEnumerator.Current => this.Current;
+
+ public bool MoveNext()
+ {
+ if (this.index == statusList.Length) return false;
+
+ for (; this.index < statusList.Length; this.index++)
+ {
+ var status = statusList[this.index];
+ if (status != null && status.StatusId != 0)
+ {
+ this.Current = status;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public void Reset()
+ {
+ this.index = 0;
+ }
+
+ public void Dispose()
+ {
+ }
+ }
}
diff --git a/Dalamud/Game/ClientState/Structs/StatusEffect.cs b/Dalamud/Game/ClientState/Structs/StatusEffect.cs
deleted file mode 100644
index 2a60a7d3b..000000000
--- a/Dalamud/Game/ClientState/Structs/StatusEffect.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System.Runtime.InteropServices;
-
-namespace Dalamud.Game.ClientState.Structs;
-
-///
-/// Native memory representation of a FFXIV status effect.
-///
-[StructLayout(LayoutKind.Sequential)]
-public struct StatusEffect
-{
- ///
- /// The effect ID.
- ///
- public short EffectId;
-
- ///
- /// How many stacks are present.
- ///
- public byte StackCount;
-
- ///
- /// Additional parameters.
- ///
- public byte Param;
-
- ///
- /// The duration remaining.
- ///
- public float Duration;
-
- ///
- /// The ID of the actor that caused this effect.
- ///
- public int OwnerId;
-}