From 4422622e1e3c0c8e2ec5b0a6f2dc046afbbdc661 Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Sun, 5 Oct 2025 13:30:34 +0200 Subject: [PATCH 01/18] Add IPlayerState service --- Dalamud/Game/ChatHandlers.cs | 2 +- .../ClientState/Aetherytes/AetheryteList.cs | 9 +- Dalamud/Game/ClientState/Buddy/BuddyList.cs | 6 +- Dalamud/Game/ClientState/ClientState.cs | 14 +- Dalamud/Game/ClientState/Fates/Fate.cs | 10 +- Dalamud/Game/ClientState/Fates/FateTable.cs | 17 +- .../Game/ClientState/Objects/ObjectTable.cs | 15 +- .../ClientState/Objects/Types/GameObject.cs | 12 +- Dalamud/Game/ClientState/Party/PartyList.cs | 12 +- .../Game/ClientState/Statuses/StatusList.cs | 18 +- .../Game/Network/Internal/NetworkHandlers.cs | 30 +- Dalamud/Game/PlayerState/MentorVersion.cs | 27 + Dalamud/Game/PlayerState/PlayerAttribute.cs | 489 ++++++++++++++++++ Dalamud/Game/PlayerState/PlayerState.cs | 205 ++++++++ Dalamud/Game/PlayerState/Sex.cs | 17 + .../Game/Text/Evaluator/SeStringEvaluator.cs | 6 +- .../Windows/Data/Widgets/GaugeWidget.cs | 6 +- .../Windows/Data/Widgets/ObjectTableWidget.cs | 12 +- .../Windows/Data/Widgets/PluginIpcWidget.cs | 8 +- .../Windows/Data/Widgets/TargetWidget.cs | 6 +- .../Steps/SeStringEvaluatorSelfTestStep.cs | 5 +- Dalamud/Plugin/Services/IClientState.cs | 2 + Dalamud/Plugin/Services/IObjectTable.cs | 8 +- Dalamud/Plugin/Services/IPlayerState.cs | 212 ++++++++ Dalamud/Utility/Util.cs | 7 +- 25 files changed, 1043 insertions(+), 112 deletions(-) create mode 100644 Dalamud/Game/PlayerState/MentorVersion.cs create mode 100644 Dalamud/Game/PlayerState/PlayerAttribute.cs create mode 100644 Dalamud/Game/PlayerState/PlayerState.cs create mode 100644 Dalamud/Game/PlayerState/Sex.cs create mode 100644 Dalamud/Plugin/Services/IPlayerState.cs diff --git a/Dalamud/Game/ChatHandlers.cs b/Dalamud/Game/ChatHandlers.cs index c57dd70b8..8237219bf 100644 --- a/Dalamud/Game/ChatHandlers.cs +++ b/Dalamud/Game/ChatHandlers.cs @@ -77,7 +77,7 @@ internal partial class ChatHandlers : IServiceType } // For injections while logged in - if (clientState.LocalPlayer != null && clientState.TerritoryType == 0 && !this.hasSeenLoadingMsg) + if (clientState.IsLoggedIn && clientState.TerritoryType == 0 && !this.hasSeenLoadingMsg) this.PrintWelcomeMessage(); #if !DEBUG && false diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs index a3d44d423..4a6d011e9 100644 --- a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs +++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs @@ -1,6 +1,7 @@ using System.Collections; using System.Collections.Generic; +using Dalamud.Game.ClientState.Objects; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; @@ -22,7 +23,7 @@ namespace Dalamud.Game.ClientState.Aetherytes; internal sealed unsafe partial class AetheryteList : IServiceType, IAetheryteList { [ServiceManager.ServiceDependency] - private readonly ClientState clientState = Service.Get(); + private readonly ObjectTable objectTable = Service.Get(); private readonly Telepo* telepoInstance = Telepo.Instance(); @@ -37,7 +38,7 @@ internal sealed unsafe partial class AetheryteList : IServiceType, IAetheryteLis { get { - if (this.clientState.LocalPlayer == null) + if (this.objectTable.LocalPlayer == null) return 0; this.Update(); @@ -59,7 +60,7 @@ internal sealed unsafe partial class AetheryteList : IServiceType, IAetheryteLis return null; } - if (this.clientState.LocalPlayer == null) + if (this.objectTable.LocalPlayer == null) return null; return new AetheryteEntry(this.telepoInstance->TeleportList[index]); @@ -69,7 +70,7 @@ internal sealed unsafe partial class AetheryteList : IServiceType, IAetheryteLis private void Update() { // this is very very important as otherwise it crashes - if (this.clientState.LocalPlayer == null) + if (this.objectTable.LocalPlayer == null) return; this.telepoInstance->UpdateAetheryteList(); diff --git a/Dalamud/Game/ClientState/Buddy/BuddyList.cs b/Dalamud/Game/ClientState/Buddy/BuddyList.cs index 84cfd24a3..44774a574 100644 --- a/Dalamud/Game/ClientState/Buddy/BuddyList.cs +++ b/Dalamud/Game/ClientState/Buddy/BuddyList.cs @@ -24,7 +24,7 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList private const uint InvalidObjectID = 0xE0000000; [ServiceManager.ServiceDependency] - private readonly ClientState clientState = Service.Get(); + private readonly PlayerState.PlayerState playerState = Service.Get(); [ServiceManager.ServiceConstructor] private BuddyList() @@ -105,10 +105,10 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList /// public IBuddyMember? CreateBuddyMemberReference(IntPtr address) { - if (this.clientState.LocalContentId == 0) + if (address == IntPtr.Zero) return null; - if (address == IntPtr.Zero) + if (!this.playerState.IsLoaded) return null; var buddy = new BuddyMember(address); diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index e92af21c3..64be5cc67 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -46,6 +46,12 @@ internal sealed class ClientState : IInternalDisposableService, IClientState [ServiceManager.ServiceDependency] private readonly NetworkHandlers networkHandlers = Service.Get(); + [ServiceManager.ServiceDependency] + private readonly PlayerState.PlayerState playerState = Service.Get(); + + [ServiceManager.ServiceDependency] + private readonly ObjectTable objectTable = Service.Get(); + private Hook onLogoutHook; private bool initialized; private ushort territoryTypeId; @@ -184,10 +190,10 @@ internal sealed class ClientState : IInternalDisposableService, IClientState } /// - public IPlayerCharacter? LocalPlayer => Service.GetNullable()?[0] as IPlayerCharacter; + public IPlayerCharacter? LocalPlayer => this.objectTable.LocalPlayer; /// - public unsafe ulong LocalContentId => PlayerState.Instance()->ContentId; + public unsafe ulong LocalContentId => this.playerState.ContentId; /// public unsafe bool IsLoggedIn @@ -241,7 +247,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState public bool IsClientIdle(out ConditionFlag blockingFlag) { blockingFlag = 0; - if (this.LocalPlayer is null) return true; + if (this.objectTable.LocalPlayer is null) return true; var condition = Service.GetNullable(); @@ -368,7 +374,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState if (condition == null || gameGui == null || data == null) return; - if (condition.Any() && this.lastConditionNone && this.LocalPlayer != null) + if (condition.Any() && this.lastConditionNone && this.objectTable.LocalPlayer != null) { Log.Debug("Is login"); this.lastConditionNone = false; diff --git a/Dalamud/Game/ClientState/Fates/Fate.cs b/Dalamud/Game/ClientState/Fates/Fate.cs index 504b690c3..5a82ef0c5 100644 --- a/Dalamud/Game/ClientState/Fates/Fate.cs +++ b/Dalamud/Game/ClientState/Fates/Fate.cs @@ -150,15 +150,11 @@ internal unsafe partial class Fate /// True or false. public static bool IsValid(Fate fate) { - var clientState = Service.GetNullable(); - - if (fate == null || clientState == null) + if (fate == null) return false; - if (clientState.LocalContentId == 0) - return false; - - return true; + var playerState = Service.Get(); + return playerState.IsLoaded == true; } /// diff --git a/Dalamud/Game/ClientState/Fates/FateTable.cs b/Dalamud/Game/ClientState/Fates/FateTable.cs index 1bf557ad5..2266c762d 100644 --- a/Dalamud/Game/ClientState/Fates/FateTable.cs +++ b/Dalamud/Game/ClientState/Fates/FateTable.cs @@ -60,15 +60,11 @@ internal sealed partial class FateTable : IServiceType, IFateTable /// public bool IsValid(IFate fate) { - var clientState = Service.GetNullable(); - - if (fate == null || clientState == null) + if (fate == null) return false; - if (clientState.LocalContentId == 0) - return false; - - return true; + var playerState = Service.Get(); + return playerState.IsLoaded == true; } /// @@ -87,12 +83,11 @@ internal sealed partial class FateTable : IServiceType, IFateTable /// public IFate? CreateFateReference(IntPtr offset) { - var clientState = Service.Get(); - - if (clientState.LocalContentId == 0) + if (offset == IntPtr.Zero) return null; - if (offset == IntPtr.Zero) + var playerState = Service.Get(); + if (!playerState.IsLoaded) return null; return new Fate(offset); diff --git a/Dalamud/Game/ClientState/Objects/ObjectTable.cs b/Dalamud/Game/ClientState/Objects/ObjectTable.cs index 84c1b5693..0a5e900f0 100644 --- a/Dalamud/Game/ClientState/Objects/ObjectTable.cs +++ b/Dalamud/Game/ClientState/Objects/ObjectTable.cs @@ -31,16 +31,16 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable { private static int objectTableLength; - private readonly ClientState clientState; + [ServiceManager.ServiceDependency] + private readonly PlayerState.PlayerState playerState = Service.Get(); + private readonly CachedEntry[] cachedObjectTable; private readonly Enumerator?[] frameworkThreadEnumerators = new Enumerator?[4]; [ServiceManager.ServiceConstructor] - private unsafe ObjectTable(ClientState clientState) + private unsafe ObjectTable() { - this.clientState = clientState; - var nativeObjectTable = CSGameObjectManager.Instance()->Objects.IndexSorted; objectTableLength = nativeObjectTable.Length; @@ -66,6 +66,9 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable /// public int Length => objectTableLength; + /// + public IPlayerCharacter? LocalPlayer => this[0] as IPlayerCharacter; + /// public IEnumerable PlayerObjects => this.GetPlayerObjects(); @@ -142,10 +145,10 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable { ThreadSafety.AssertMainThread(); - if (this.clientState.LocalContentId == 0) + if (address == nint.Zero) return null; - if (address == nint.Zero) + if (!this.playerState.IsLoaded) return null; var obj = (CSGameObject*)address; diff --git a/Dalamud/Game/ClientState/Objects/Types/GameObject.cs b/Dalamud/Game/ClientState/Objects/Types/GameObject.cs index 829949c12..c37b72961 100644 --- a/Dalamud/Game/ClientState/Objects/Types/GameObject.cs +++ b/Dalamud/Game/ClientState/Objects/Types/GameObject.cs @@ -1,9 +1,7 @@ using System.Numerics; -using System.Runtime.CompilerServices; using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Memory; namespace Dalamud.Game.ClientState.Objects.Types; @@ -170,15 +168,11 @@ internal partial class GameObject /// True or false. public static bool IsValid(IGameObject? actor) { - var clientState = Service.GetNullable(); - - if (actor is null || clientState == null) + if (actor == null) return false; - if (clientState.LocalContentId == 0) - return false; - - return true; + var playerState = Service.Get(); + return playerState.IsLoaded == true; } /// diff --git a/Dalamud/Game/ClientState/Party/PartyList.cs b/Dalamud/Game/ClientState/Party/PartyList.cs index a016a8211..0a81095c6 100644 --- a/Dalamud/Game/ClientState/Party/PartyList.cs +++ b/Dalamud/Game/ClientState/Party/PartyList.cs @@ -25,7 +25,7 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList private const int AllianceLength = 20; [ServiceManager.ServiceDependency] - private readonly ClientState clientState = Service.Get(); + private readonly PlayerState.PlayerState playerState = Service.Get(); [ServiceManager.ServiceConstructor] private PartyList() @@ -91,10 +91,7 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList /// public IPartyMember? CreatePartyMemberReference(IntPtr address) { - if (this.clientState.LocalContentId == 0) - return null; - - if (address == IntPtr.Zero) + if (address == IntPtr.Zero || !this.playerState.IsLoaded) return null; return new PartyMember(address); @@ -112,10 +109,7 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList /// public IPartyMember? CreateAllianceMemberReference(IntPtr address) { - if (this.clientState.LocalContentId == 0) - return null; - - if (address == IntPtr.Zero) + if (address == IntPtr.Zero || !this.playerState.IsLoaded) return null; return new PartyMember(address); diff --git a/Dalamud/Game/ClientState/Statuses/StatusList.cs b/Dalamud/Game/ClientState/Statuses/StatusList.cs index a38e45ea3..04d0d822c 100644 --- a/Dalamud/Game/ClientState/Statuses/StatusList.cs +++ b/Dalamud/Game/ClientState/Statuses/StatusList.cs @@ -66,15 +66,14 @@ public sealed unsafe partial class StatusList /// The status object containing the requested data. public static StatusList? CreateStatusListReference(IntPtr address) { + if (address == IntPtr.Zero) + return null; + // 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 // here or somewhere else. - var clientState = Service.Get(); - - if (clientState.LocalContentId == 0) - return null; - - if (address == IntPtr.Zero) + var playerState = Service.Get(); + if (!playerState.IsLoaded) return null; return new StatusList(address); @@ -87,12 +86,11 @@ public sealed unsafe partial class StatusList /// The status object containing the requested data. public static Status? CreateStatusReference(IntPtr address) { - var clientState = Service.Get(); - - if (clientState.LocalContentId == 0) + if (address == IntPtr.Zero) return null; - if (address == IntPtr.Zero) + var playerState = Service.Get(); + if (!playerState.IsLoaded) return null; return new Status(address); diff --git a/Dalamud/Game/Network/Internal/NetworkHandlers.cs b/Dalamud/Game/Network/Internal/NetworkHandlers.cs index 9b85d0ff3..2f9276cc0 100644 --- a/Dalamud/Game/Network/Internal/NetworkHandlers.cs +++ b/Dalamud/Game/Network/Internal/NetworkHandlers.cs @@ -16,13 +16,12 @@ using Dalamud.Hooking; using Dalamud.Networking.Http; using Dalamud.Utility; -using FFXIVClientStructs.FFXIV.Client.Game.Control; using FFXIVClientStructs.FFXIV.Client.Game.InstanceContent; -using FFXIVClientStructs.FFXIV.Client.Game.UI; using FFXIVClientStructs.FFXIV.Client.Network; -using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Client.UI.Info; + using Lumina.Excel.Sheets; + using Serilog; namespace Dalamud.Game.Network.Internal; @@ -269,29 +268,8 @@ internal unsafe class NetworkHandlers : IInternalDisposableService private static (ulong UploaderId, uint WorldId) GetUploaderInfo() { - var agentLobby = AgentLobby.Instance(); - - var uploaderId = agentLobby->LobbyData.ContentId; - if (uploaderId == 0) - { - var playerState = PlayerState.Instance(); - if (playerState->IsLoaded) - { - uploaderId = playerState->ContentId; - } - } - - var worldId = agentLobby->LobbyData.CurrentWorldId; - if (worldId == 0) - { - var localPlayer = Control.GetLocalPlayer(); - if (localPlayer != null) - { - worldId = localPlayer->CurrentWorld; - } - } - - return (uploaderId, worldId); + var playerState = Service.Get(); + return (playerState.ContentId, playerState.CurrentWorld.RowId); } private unsafe nint CfPopDetour(PublicContentDirector.EnterContentInfoPacket* packetData) diff --git a/Dalamud/Game/PlayerState/MentorVersion.cs b/Dalamud/Game/PlayerState/MentorVersion.cs new file mode 100644 index 000000000..701eda112 --- /dev/null +++ b/Dalamud/Game/PlayerState/MentorVersion.cs @@ -0,0 +1,27 @@ +namespace Dalamud.Game.PlayerState; + +/// +/// Specifies the mentor certification version for a player. +/// +public enum MentorVersion : byte +{ + /// + /// Indicates that the player has never held mentor status in any expansion. + /// + None = 0, + + /// + /// Indicates that the player was last a mentor during the Shadowbringers expansion. + /// + Shadowbringers = 1, + + /// + /// Indicates that the player was last a mentor during the Endwalker expansion. + /// + Endwalker = 2, + + /// + /// Indicates that the player was last a mentor during the Dawntrail expansion. + /// + Dawntrail = 3, +} diff --git a/Dalamud/Game/PlayerState/PlayerAttribute.cs b/Dalamud/Game/PlayerState/PlayerAttribute.cs new file mode 100644 index 000000000..4db8af107 --- /dev/null +++ b/Dalamud/Game/PlayerState/PlayerAttribute.cs @@ -0,0 +1,489 @@ +namespace Dalamud.Game.PlayerState; + +/// +/// Represents a player's attribute. +/// +public enum PlayerAttribute +{ + /// + /// Strength. + /// + /// + /// Affects physical damage dealt by gladiator's arms, marauder's arms, dark knight's arms, gunbreaker's arms, pugilist's arms, lancer's arms, samurai's arms, reaper's arms, thaumaturge's arms, arcanist's arms, red mage's arms, pictomancer's arms, conjurer's arms, astrologian's arms, sage's arms, and blue mage's arms. + /// + Strength = 1, + + /// + /// Dexterity. + /// + /// + /// Affects physical damage dealt by rogue's arms, viper's arms, archer's arms, machinist's arms, and dancer's arms. + /// + Dexterity = 2, + + /// + /// Vitality. + /// + /// + /// Affects maximum HP. + /// + Vitality = 3, + + /// + /// Intelligence. + /// + /// + /// Affects attack magic potency when role is DPS. + /// + Intelligence = 4, + + /// + /// Mind. + /// + /// + /// Affects healing magic potency. Also affects attack magic potency when role is Healer. + /// + Mind = 5, + + /// + /// Piety. + /// + /// + /// Affects MP regeneration. Regeneration rate is determined by piety. Only applicable when in battle and role is Healer. + /// + Piety = 6, + + /// + /// Health Points. + /// + HP = 7, + + /// + /// Mana Points. + /// + MP = 8, + + /// + /// Tactical Points. + /// + TP = 9, + + /// + /// Gathering Point. + /// + GP = 10, + + /// + /// Crafting Points. + /// + CP = 11, + + /// + /// Physical Damage. + /// + PhysicalDamage = 12, + + /// + /// Magic Damage. + /// + MagicDamage = 13, + + /// + /// Delay. + /// + Delay = 14, + + /// + /// Additional Effect. + /// + AdditionalEffect = 15, + + /// + /// Attack Speed. + /// + AttackSpeed = 16, + + /// + /// Block Rate. + /// + BlockRate = 17, + + /// + /// Block Strength. + /// + BlockStrength = 18, + + /// + /// Tenacity. + /// + /// + /// Affects the amount of physical and magic damage dealt and received, as well as HP restored. The higher the value, the more damage dealt, the more HP restored, and the less damage taken. Only applicable when role is Tank. + /// + Tenacity = 19, + + /// + /// Attack Power. + /// + /// + /// Affects amount of damage dealt by physical attacks. The higher the value, the more damage dealt. + /// + AttackPower = 20, + + /// + /// Defense. + /// + /// + /// Affects the amount of damage taken by physical attacks. The higher the value, the less damage taken. + /// + Defense = 21, + + /// + /// Direct Hit Rate. + /// + /// + /// Affects the rate at which your physical and magic attacks land direct hits, dealing slightly more damage than normal hits. The higher the value, the higher the frequency with which your hits will be direct. Higher values will also result in greater damage for actions which guarantee direct hits. + /// + DirectHitRate = 22, + + /// + /// Evasion. + /// + Evasion = 23, + + /// + /// Magic Defense. + /// + /// + /// Affects the amount of damage taken by magic attacks. The higher the value, the less damage taken. + /// + MagicDefense = 24, + + /// + /// Critical Hit Power. + /// + CriticalHitPower = 25, + + /// + /// Critical Hit Resilience. + /// + CriticalHitResilience = 26, + + /// + /// Critical Hit. + /// + /// + /// Affects the amount of physical and magic damage dealt, as well as HP restored. The higher the value, the higher the frequency with which your hits will be critical/higher the potency of critical hits. + /// + CriticalHit = 27, + + /// + /// Critical Hit Evasion. + /// + CriticalHitEvasion = 28, + + /// + /// Slashing Resistance. + /// + /// + /// Decreases damage done by slashing attacks. + /// + SlashingResistance = 29, + + /// + /// Piercing Resistance. + /// + /// + /// Decreases damage done by piercing attacks. + /// + PiercingResistance = 30, + + /// + /// Blunt Resistance. + /// + /// + /// Decreases damage done by blunt attacks. + /// + BluntResistance = 31, + + /// + /// Projectile Resistance. + /// + ProjectileResistance = 32, + + /// + /// Attack Magic Potency. + /// + /// + /// Affects the amount of damage dealt by magic attacks. + /// + AttackMagicPotency = 33, + + /// + /// Healing Magic Potency. + /// + /// + /// Affects the amount of HP restored via healing magic. + /// + HealingMagicPotency = 34, + + /// + /// Enhancement Magic Potency. + /// + EnhancementMagicPotency = 35, + + /// + /// Elemental Bonus. + /// + ElementalBonus = 36, + + /// + /// Fire Resistance. + /// + /// + /// Decreases fire-aspected damage. + /// + FireResistance = 37, + + /// + /// Ice Resistance. + /// + /// + /// Decreases ice-aspected damage. + /// + IceResistance = 38, + + /// + /// Wind Resistance. + /// + /// + /// Decreases wind-aspected damage. + /// + WindResistance = 39, + + /// + /// Earth Resistance. + /// + /// + /// Decreases earth-aspected damage. + /// + EarthResistance = 40, + + /// + /// Lightning Resistance. + /// + /// + /// Decreases lightning-aspected damage. + /// + LightningResistance = 41, + + /// + /// Water Resistance. + /// + /// + /// Decreases water-aspected damage. + /// + WaterResistance = 42, + + /// + /// Magic Resistance. + /// + MagicResistance = 43, + + /// + /// Determination. + /// + /// + /// Affects the amount of damage dealt by both physical and magic attacks, as well as the amount of HP restored by healing spells. + /// + Determination = 44, + + /// + /// Skill Speed. + /// + /// + /// Affects both the casting and recast timers, as well as the damage over time potency for weaponskills and auto-attacks. The higher the value, the shorter the timers/higher the potency. + /// + SkillSpeed = 45, + + /// + /// Spell Speed. + /// + /// + /// Affects both the casting and recast timers for spells. The higher the value, the shorter the timers. Also affects a spell's damage over time or healing over time potency. + /// + SpellSpeed = 46, + + /// + /// Haste. + /// + Haste = 47, + + /// + /// Morale. + /// + /// + /// In PvP, replaces physical and magical defense in determining damage inflicted by other players. Also influences the amount of damage dealt to other players. + /// + Morale = 48, + + /// + /// Enmity. + /// + Enmity = 49, + + /// + /// Enmity Reduction. + /// + EnmityReduction = 50, + + /// + /// Desynthesis Skill Gain. + /// + DesynthesisSkillGain = 51, + + /// + /// EXP Bonus. + /// + EXPBonus = 52, + + /// + /// Regen. + /// + Regen = 53, + + /// + /// Special Attribute. + /// + SpecialAttribute = 54, + + /// + /// Main Attribute. + /// + MainAttribute = 55, + + /// + /// Secondary Attribute. + /// + SecondaryAttribute = 56, + + /// + /// Slow Resistance. + /// + /// + /// Shortens the duration of slow. + /// + SlowResistance = 57, + + /// + /// Petrification Resistance. + /// + PetrificationResistance = 58, + + /// + /// Paralysis Resistance. + /// + ParalysisResistance = 59, + + /// + /// Silence Resistance. + /// + /// + /// Shortens the duration of silence. + /// + SilenceResistance = 60, + + /// + /// Blind Resistance. + /// + /// + /// Shortens the duration of blind. + /// + BlindResistance = 61, + + /// + /// Poison Resistance. + /// + /// + /// Shortens the duration of poison. + /// + PoisonResistance = 62, + + /// + /// Stun Resistance. + /// + /// + /// Shortens the duration of stun. + /// + StunResistance = 63, + + /// + /// Sleep Resistance. + /// + /// + /// Shortens the duration of sleep. + /// + SleepResistance = 64, + + /// + /// Bind Resistance. + /// + /// + /// Shortens the duration of bind. + /// + BindResistance = 65, + + /// + /// Heavy Resistance. + /// + /// + /// Shortens the duration of heavy. + /// + HeavyResistance = 66, + + /// + /// Doom Resistance. + /// + DoomResistance = 67, + + /// + /// Reduced Durability Loss. + /// + ReducedDurabilityLoss = 68, + + /// + /// Increased Spiritbond Gain. + /// + IncreasedSpiritbondGain = 69, + + /// + /// Craftsmanship. + /// + /// + /// Affects the amount of progress achieved in a single synthesis step. + /// + Craftsmanship = 70, + + /// + /// Control. + /// + /// + /// Affects the amount of quality improved in a single synthesis step. + /// + Control = 71, + + /// + /// Gathering. + /// + /// + /// Affects the rate at which items are gathered. + /// + Gathering = 72, + + /// + /// Perception. + /// + /// + /// Affects item yield when gathering as a botanist or miner, and the size of fish when fishing or spearfishing. + /// + Perception = 73, +} diff --git a/Dalamud/Game/PlayerState/PlayerState.cs b/Dalamud/Game/PlayerState/PlayerState.cs new file mode 100644 index 000000000..cebdb0ef8 --- /dev/null +++ b/Dalamud/Game/PlayerState/PlayerState.cs @@ -0,0 +1,205 @@ +using Dalamud.Data; +using Dalamud.IoC.Internal; +using Dalamud.Plugin.Services; + +using FFXIVClientStructs.FFXIV.Client.UI.Agent; + +using Lumina.Excel; +using Lumina.Excel.Sheets; + +using CSPlayerState = FFXIVClientStructs.FFXIV.Client.Game.UI.PlayerState; +using GrandCompany = Lumina.Excel.Sheets.GrandCompany; + +namespace Dalamud.Game.PlayerState; + +/// +/// This class contains the PlayerState wrappers. +/// +[ServiceManager.EarlyLoadedService] +[ResolveVia] +internal unsafe class PlayerState : IPlayerState, IServiceType +{ + /// + public bool IsLoaded => CSPlayerState.Instance()->IsLoaded; + + /// + public string CharacterName => this.IsLoaded ? CSPlayerState.Instance()->CharacterNameString : string.Empty; + + /// + public uint EntityId => this.IsLoaded ? CSPlayerState.Instance()->EntityId : default; + + /// + public ulong ContentId => this.IsLoaded ? CSPlayerState.Instance()->ContentId : default; + + /// + public RowRef CurrentWorld + { + get + { + var agentLobby = AgentLobby.Instance(); + return agentLobby->IsLoggedIn + ? LuminaUtils.CreateRef(agentLobby->LobbyData.CurrentWorldId) + : default; + } + } + + /// + public RowRef HomeWorld + { + get + { + var agentLobby = AgentLobby.Instance(); + return agentLobby->IsLoggedIn + ? LuminaUtils.CreateRef(agentLobby->LobbyData.HomeWorldId) + : default; + } + } + + /// + public Sex Sex => this.IsLoaded ? (Sex)CSPlayerState.Instance()->Sex : default; + + /// + public RowRef Race => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->Race) : default; + + /// + public RowRef Tribe => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->Tribe) : default; + + /// + public RowRef ClassJob => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->CurrentClassJobId) : default; + + /// + public short Level => this.IsLoaded ? CSPlayerState.Instance()->CurrentLevel : default; + + /// + public bool IsLevelSynced => this.IsLoaded && CSPlayerState.Instance()->IsLevelSynced; + + /// + public short EffectiveLevel => this.IsLoaded ? (this.IsLevelSynced ? CSPlayerState.Instance()->SyncedLevel : CSPlayerState.Instance()->CurrentLevel) : default; + + /// + public RowRef GuardianDeity => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->GuardianDeity) : default; + + /// + public byte BirthMonth => this.IsLoaded ? CSPlayerState.Instance()->BirthMonth : default; + + /// + public byte BirthDay => this.IsLoaded ? CSPlayerState.Instance()->BirthDay : default; + + /// + public RowRef FirstClass => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->FirstClass) : default; + + /// + public RowRef StartTown => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->StartTown) : default; + + /// + public int BaseStrength => this.IsLoaded ? CSPlayerState.Instance()->BaseStrength : default; + + /// + public int BaseDexterity => this.IsLoaded ? CSPlayerState.Instance()->BaseDexterity : default; + + /// + public int BaseVitality => this.IsLoaded ? CSPlayerState.Instance()->BaseVitality : default; + + /// + public int BaseIntelligence => this.IsLoaded ? CSPlayerState.Instance()->BaseIntelligence : default; + + /// + public int BaseMind => this.IsLoaded ? CSPlayerState.Instance()->BaseMind : default; + + /// + public int BasePiety => this.IsLoaded ? CSPlayerState.Instance()->BasePiety : default; + + /// + public RowRef GrandCompany => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->GrandCompany) : default; + + /// + public RowRef HomeAetheryte => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->HomeAetheryteId) : default; + + /// + public ReadOnlySpan> FavouriteAetherytes + { + get + { + var playerState = CSPlayerState.Instance(); + if (playerState->IsLoaded || playerState->FavouriteAetheryteCount == 0) + return []; + + var count = playerState->FavouriteAetheryteCount; + var array = new RowRef[count]; + + for (var i = 0; i < count; i++) + array[i] = LuminaUtils.CreateRef(playerState->FavouriteAetherytes[i]); + + return array; + } + } + + /// + public RowRef FreeAetheryte => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->FreeAetheryteId) : default; + + /// + public uint BaseRestedExperience => this.IsLoaded ? CSPlayerState.Instance()->BaseRestedExperience : default; + + /// + public short PlayerCommendations => this.IsLoaded ? CSPlayerState.Instance()->PlayerCommendations : default; + + /// + public byte DeliveryLevel => this.IsLoaded ? CSPlayerState.Instance()->DeliveryLevel : default; + + /// + public MentorVersion MentorVersion => this.IsLoaded ? (MentorVersion)CSPlayerState.Instance()->MentorVersion : default; + + /// + public int GetAttribute(PlayerAttribute attribute) => this.IsLoaded ? CSPlayerState.Instance()->Attributes[(int)attribute] : default; + + /// + public byte GetGrandCompanyRank(GrandCompany grandCompany) + { + if (!this.IsLoaded) + return default; + + return grandCompany.RowId switch + { + 1 => CSPlayerState.Instance()->GCRankMaelstrom, + 2 => CSPlayerState.Instance()->GCRankTwinAdders, + 3 => CSPlayerState.Instance()->GCRankImmortalFlames, + _ => default, + }; + } + + /// + public short GetClassJobLevel(ClassJob classJob) + { + if (classJob.ExpArrayIndex == -1) + return default; + + if (!this.IsLoaded) + return default; + + return CSPlayerState.Instance()->ClassJobLevels[classJob.ExpArrayIndex]; + } + + /// + public int GetClassJobExperience(ClassJob classJob) + { + if (classJob.ExpArrayIndex == -1) + return default; + + if (!this.IsLoaded) + return default; + + return CSPlayerState.Instance()->ClassJobExperience[classJob.ExpArrayIndex]; + } + + /// + public float GetDesynthesisLevel(ClassJob classJob) + { + if (classJob.DohDolJobIndex == -1) + return default; + + if (!this.IsLoaded) + return default; + + return CSPlayerState.Instance()->DesynthesisLevels[classJob.DohDolJobIndex] / 100f; + } +} diff --git a/Dalamud/Game/PlayerState/Sex.cs b/Dalamud/Game/PlayerState/Sex.cs new file mode 100644 index 000000000..e6ed6cc78 --- /dev/null +++ b/Dalamud/Game/PlayerState/Sex.cs @@ -0,0 +1,17 @@ +namespace Dalamud.Game.PlayerState; + +/// +/// Represents the sex of a character. +/// +public enum Sex : byte +{ + /// + /// Male sex. + /// + Male = 0, + + /// + /// Female sex. + /// + Female = 1, +} diff --git a/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs b/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs index 9f898bcca..018742271 100644 --- a/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs +++ b/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs @@ -35,7 +35,6 @@ using Lumina.Text.Payloads; using Lumina.Text.ReadOnly; using AddonSheet = Lumina.Excel.Sheets.Addon; -using PlayerState = FFXIVClientStructs.FFXIV.Client.Game.UI.PlayerState; using StatusSheet = Lumina.Excel.Sheets.Status; namespace Dalamud.Game.Text.Evaluator; @@ -68,6 +67,9 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator [ServiceManager.ServiceDependency] private readonly SheetRedirectResolver sheetRedirectResolver = Service.Get(); + [ServiceManager.ServiceDependency] + private readonly PlayerState.PlayerState playerState = Service.Get(); + private readonly ConcurrentDictionary, string> actStrCache = []; private readonly ConcurrentDictionary, string> objStrCache = []; @@ -564,7 +566,7 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator return false; // the game uses LocalPlayer here, but using PlayerState seems more safe. - return this.ResolveStringExpression(in context, PlayerState.Instance()->EntityId == entityId ? eTrue : eFalse); + return this.ResolveStringExpression(in context, playerState.EntityId == entityId ? eTrue : eFalse); } private bool TryResolveColor(in SeStringContext context, in ReadOnlySePayloadSpan payload) diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs index bf6800a53..7a5a9c89b 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs @@ -1,7 +1,7 @@ using Dalamud.Bindings.ImGui; -using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.JobGauge; using Dalamud.Game.ClientState.JobGauge.Types; +using Dalamud.Game.ClientState.Objects; using Dalamud.Utility; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -29,10 +29,10 @@ internal class GaugeWidget : IDataWindowWidget /// public void Draw() { - var clientState = Service.Get(); + var objectTable = Service.Get(); var jobGauges = Service.Get(); - var player = clientState.LocalPlayer; + var player = objectTable.LocalPlayer; if (player == null) { ImGui.Text("Player is not present"u8); diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs index 290c7d9a2..9a2de7261 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs @@ -4,6 +4,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.Gui; +using Dalamud.Game.PlayerState; using Dalamud.Utility; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -39,12 +40,13 @@ internal class ObjectTableWidget : IDataWindowWidget var chatGui = Service.Get(); var clientState = Service.Get(); + var playerState = Service.Get(); var gameGui = Service.Get(); var objectTable = Service.Get(); var stateString = string.Empty; - if (clientState.LocalPlayer == null) + if (objectTable.LocalPlayer == null) { ImGui.Text("LocalPlayer null."u8); } @@ -55,10 +57,10 @@ internal class ObjectTableWidget : IDataWindowWidget else { stateString += $"ObjectTableLen: {objectTable.Length}\n"; - stateString += $"LocalPlayerName: {clientState.LocalPlayer.Name}\n"; - stateString += $"CurrentWorldName: {(this.resolveGameData ? clientState.LocalPlayer.CurrentWorld.ValueNullable?.Name : clientState.LocalPlayer.CurrentWorld.RowId.ToString())}\n"; - stateString += $"HomeWorldName: {(this.resolveGameData ? clientState.LocalPlayer.HomeWorld.ValueNullable?.Name : clientState.LocalPlayer.HomeWorld.RowId.ToString())}\n"; - stateString += $"LocalCID: {clientState.LocalContentId:X}\n"; + stateString += $"LocalPlayerName: {playerState.CharacterName}\n"; + stateString += $"CurrentWorldName: {(this.resolveGameData ? playerState.CurrentWorld.ValueNullable?.Name : playerState.CurrentWorld.RowId.ToString())}\n"; + stateString += $"HomeWorldName: {(this.resolveGameData ? playerState.HomeWorld.ValueNullable?.Name : playerState.HomeWorld.RowId.ToString())}\n"; + stateString += $"LocalCID: {playerState.ContentId:X}\n"; stateString += $"LastLinkedItem: {chatGui.LastLinkedItemId}\n"; stateString += $"TerritoryType: {clientState.TerritoryType}\n\n"; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs index 6c581604e..0ca754a91 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs @@ -1,10 +1,10 @@ -using Dalamud.Bindings.ImGui; -using Dalamud.Game.ClientState; +using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Plugin.Ipc; using Dalamud.Plugin.Ipc.Internal; using Dalamud.Utility; + using Serilog; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -111,12 +111,12 @@ internal class PluginIpcWidget : IDataWindowWidget if (ImGui.Button("Action GO"u8)) { - this.ipcSubGo.InvokeAction(Service.Get().LocalPlayer); + this.ipcSubGo.InvokeAction(Service.Get().LocalPlayer); } if (ImGui.Button("Func GO"u8)) { - this.callGateResponse = this.ipcSubGo.InvokeFunc(Service.Get().LocalPlayer); + this.callGateResponse = this.ipcSubGo.InvokeFunc(Service.Get().LocalPlayer); } if (!this.callGateResponse.IsNullOrEmpty()) diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs index 081f3ec96..6caf3286d 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs @@ -1,4 +1,4 @@ -using Dalamud.Bindings.ImGui; +using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Objects; using Dalamud.Interface.Utility; @@ -33,7 +33,7 @@ internal class TargetWidget : IDataWindowWidget { ImGui.Checkbox("Resolve GameData"u8, ref this.resolveGameData); - var clientState = Service.Get(); + var objectTable = Service.Get(); var targetMgr = Service.Get(); if (targetMgr.Target != null) @@ -80,7 +80,7 @@ internal class TargetWidget : IDataWindowWidget if (ImGui.Button("Clear FT"u8)) targetMgr.FocusTarget = null; - var localPlayer = clientState.LocalPlayer; + var localPlayer = objectTable.LocalPlayer; if (localPlayer != null) { diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SeStringEvaluatorSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SeStringEvaluatorSelfTestStep.cs index 9853e31d4..e32b6cd2a 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SeStringEvaluatorSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SeStringEvaluatorSelfTestStep.cs @@ -1,6 +1,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Game.ClientState; +using Dalamud.Game.ClientState.Objects; using Dalamud.Game.Text.Evaluator; using Dalamud.Game.Text.SeStringHandling.Payloads; @@ -51,8 +52,8 @@ internal class SeStringEvaluatorSelfTestStep : ISelfTestStep // that it returned the local players name by using its EntityId, // and that it didn't include the world name by checking the HomeWorldId against AgentLobby.Instance()->LobbyData.HomeWorldId. - var clientState = Service.Get(); - var localPlayer = clientState.LocalPlayer; + var objectTable = Service.Get(); + var localPlayer = objectTable.LocalPlayer; if (localPlayer is null) { ImGui.Text("You need to be logged in for this step."u8); diff --git a/Dalamud/Plugin/Services/IClientState.cs b/Dalamud/Plugin/Services/IClientState.cs index 0342ea77c..de0c5dad8 100644 --- a/Dalamud/Plugin/Services/IClientState.cs +++ b/Dalamud/Plugin/Services/IClientState.cs @@ -109,11 +109,13 @@ public interface IClientState /// /// Gets the local player character, if one is present. /// + [Obsolete($"Use {nameof(IPlayerState)} or {nameof(IObjectTable)}.{nameof(IObjectTable.LocalPlayer)} if you need to.")] public IPlayerCharacter? LocalPlayer { get; } /// /// Gets the content ID of the local character. /// + [Obsolete($"Use {nameof(IPlayerState)}.{nameof(IPlayerState.ContentId)}")] public ulong LocalContentId { get; } /// diff --git a/Dalamud/Plugin/Services/IObjectTable.cs b/Dalamud/Plugin/Services/IObjectTable.cs index 4c5305513..36cd72ebe 100644 --- a/Dalamud/Plugin/Services/IObjectTable.cs +++ b/Dalamud/Plugin/Services/IObjectTable.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; +using System.Collections.Generic; +using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.Types; namespace Dalamud.Plugin.Services; @@ -19,6 +20,11 @@ public interface IObjectTable : IEnumerable /// public int Length { get; } + /// + /// Gets the local player character, if one is present. + /// + public IPlayerCharacter? LocalPlayer { get; } + /// /// Gets an enumerator for accessing player objects. This will only contain BattleChara objects. /// Does not contain any mounts, minions, or accessories. diff --git a/Dalamud/Plugin/Services/IPlayerState.cs b/Dalamud/Plugin/Services/IPlayerState.cs new file mode 100644 index 000000000..dc507e461 --- /dev/null +++ b/Dalamud/Plugin/Services/IPlayerState.cs @@ -0,0 +1,212 @@ +using Dalamud.Game.PlayerState; + +using Lumina.Excel; +using Lumina.Excel.Sheets; + +namespace Dalamud.Plugin.Services; + +#pragma warning disable SA1400 // Access modifier should be declared: Interface members are public by default + +/// +/// Interface for determining unlock state of various content in the game. +/// +public interface IPlayerState +{ + /// + /// Gets a value indicating whether the local character is loaded. + /// + /// + /// The actual GameObject will not immediately exist when this changes to true. + /// + bool IsLoaded { get; } + + /// + /// Gets the name of the local character. + /// + string CharacterName { get; } + + /// + /// Gets the entity ID of the local character. + /// + uint EntityId { get; } + + /// + /// Gets the content ID of the local character. + /// + ulong ContentId { get; } + + /// + /// Gets the World row for the local character's current world. + /// + RowRef CurrentWorld { get; } + + /// + /// Gets the World row for the local character's home world. + /// + RowRef HomeWorld { get; } + + /// + /// Gets the sex of the local character. + /// + Sex Sex { get; } + + /// + /// Gets the Race row for the local character. + /// + RowRef Race { get; } + + /// + /// Gets the Tribe row for the local character. + /// + RowRef Tribe { get; } + + /// + /// Gets the ClassJob row for the local character's current class/job. + /// + RowRef ClassJob { get; } + + /// + /// Gets the current class/job's level of the local character. + /// + short Level { get; } + + /// + /// Gets a value indicating whether the local character's level is synced. + /// + bool IsLevelSynced { get; } + + /// + /// Gets the effective level of the local character. + /// + short EffectiveLevel { get; } + + /// + /// Gets the GuardianDeity row for the local character. + /// + RowRef GuardianDeity { get; } + + /// + /// Gets the birth month of the local character. + /// + byte BirthMonth { get; } + + /// + /// Gets the birth day of the local character. + /// + byte BirthDay { get; } + + /// + /// Gets the ClassJob row for the local character's starting class. + /// + RowRef FirstClass { get; } + + /// + /// Gets the Town row for the local character's starting town. + /// + RowRef StartTown { get; } + + /// + /// Gets the base strength of the local character. + /// + int BaseStrength { get; } + + /// + /// Gets the base dexterity of the local character. + /// + int BaseDexterity { get; } + + /// + /// Gets the base vitality of the local character. + /// + int BaseVitality { get; } + + /// + /// Gets the base intelligence of the local character. + /// + int BaseIntelligence { get; } + + /// + /// Gets the base mind of the local character. + /// + int BaseMind { get; } + + /// + /// Gets the piety mind of the local character. + /// + int BasePiety { get; } + + /// + /// Gets the GrandCompany row for the local character's current Grand Company affiliation. + /// + RowRef GrandCompany { get; } + + /// + /// Gets the Aetheryte row for the local character's home aetheryte. + /// + RowRef HomeAetheryte { get; } + + /// + /// Gets a span of Aetheryte rows for the local character's favourite aetherytes. + /// + ReadOnlySpan> FavouriteAetherytes { get; } + + /// + /// Gets the Aetheryte row for the local character's free aetheryte. + /// + RowRef FreeAetheryte { get; } + + /// + /// Gets the amount of received player commendations of the local character. + /// + uint BaseRestedExperience { get; } + + /// + /// Gets the amount of received player commendations of the local character. + /// + short PlayerCommendations { get; } + + /// + /// Gets the Carrier Level of Delivery Moogle Quests of the local character. + /// + byte DeliveryLevel { get; } + + /// + /// Gets the mentor version of the local character. + /// + MentorVersion MentorVersion { get; } + + /// + /// Gets the value of an attribute of the local character. + /// + /// The attribute to check. + /// The value of the specific attribute. + int GetAttribute(PlayerAttribute attribute); + + /// + /// Gets the Grand Company rank of the local character. + /// + /// The Grand Company to check. + /// The Grand Company rank of the local character. + byte GetGrandCompanyRank(GrandCompany grandCompany); + + /// + /// Gets the level of the local character's class/job. + /// + /// The ClassJob row to check. + /// The level of the requested class/job. + short GetClassJobLevel(ClassJob classJob); + + /// + /// Gets the experience of the local character's class/job. + /// + /// The ClassJob row to check. + /// The experience of the requested class/job. + int GetClassJobExperience(ClassJob classJob); + + /// + /// Gets the desynthesis level of the local character's crafter job. + /// + /// The ClassJob row to check. + /// The desynthesis level of the requested crafter job. + float GetDesynthesisLevel(ClassJob classJob); +} diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs index ff06618ab..a3e6e16ed 100644 --- a/Dalamud/Utility/Util.cs +++ b/Dalamud/Utility/Util.cs @@ -18,18 +18,21 @@ using Dalamud.Game; using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Interface.Colors; +using Dalamud.Interface.Internal; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Support; + using Lumina.Excel.Sheets; + using Serilog; + using TerraFX.Interop.Windows; + using Windows.Win32.System.Memory; using Windows.Win32.System.Ole; using Windows.Win32.UI.WindowsAndMessaging; -using Dalamud.Interface.Internal; - using FLASHWINFO = Windows.Win32.UI.WindowsAndMessaging.FLASHWINFO; using HWND = Windows.Win32.Foundation.HWND; using MEMORY_BASIC_INFORMATION = Windows.Win32.System.Memory.MEMORY_BASIC_INFORMATION; From 8cac4862494bf2a9a31188310b5cb8dc39a42e50 Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Sun, 5 Oct 2025 14:36:55 +0200 Subject: [PATCH 02/18] Add PluginInterface attribute to PlayerState --- Dalamud/Game/PlayerState/PlayerState.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dalamud/Game/PlayerState/PlayerState.cs b/Dalamud/Game/PlayerState/PlayerState.cs index cebdb0ef8..5e5528eca 100644 --- a/Dalamud/Game/PlayerState/PlayerState.cs +++ b/Dalamud/Game/PlayerState/PlayerState.cs @@ -1,4 +1,5 @@ using Dalamud.Data; +using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; @@ -15,9 +16,10 @@ namespace Dalamud.Game.PlayerState; /// /// This class contains the PlayerState wrappers. /// +[PluginInterface] [ServiceManager.EarlyLoadedService] [ResolveVia] -internal unsafe class PlayerState : IPlayerState, IServiceType +internal unsafe class PlayerState : IServiceType, IPlayerState { /// public bool IsLoaded => CSPlayerState.Instance()->IsLoaded; From c2fc04c3a80c93694584b3db9eed40963dc9507a Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Sun, 5 Oct 2025 14:37:11 +0200 Subject: [PATCH 03/18] Improve wording --- Dalamud/Plugin/Services/IClientState.cs | 2 +- Dalamud/Plugin/Services/IPlayerState.cs | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Dalamud/Plugin/Services/IClientState.cs b/Dalamud/Plugin/Services/IClientState.cs index de0c5dad8..36bf2e296 100644 --- a/Dalamud/Plugin/Services/IClientState.cs +++ b/Dalamud/Plugin/Services/IClientState.cs @@ -109,7 +109,7 @@ public interface IClientState /// /// Gets the local player character, if one is present. /// - [Obsolete($"Use {nameof(IPlayerState)} or {nameof(IObjectTable)}.{nameof(IObjectTable.LocalPlayer)} if you need to.")] + [Obsolete($"Use {nameof(IPlayerState)} or {nameof(IObjectTable)}.{nameof(IObjectTable.LocalPlayer)} if necessary.")] public IPlayerCharacter? LocalPlayer { get; } /// diff --git a/Dalamud/Plugin/Services/IPlayerState.cs b/Dalamud/Plugin/Services/IPlayerState.cs index dc507e461..a119f231b 100644 --- a/Dalamud/Plugin/Services/IPlayerState.cs +++ b/Dalamud/Plugin/Services/IPlayerState.cs @@ -8,15 +8,16 @@ namespace Dalamud.Plugin.Services; #pragma warning disable SA1400 // Access modifier should be declared: Interface members are public by default /// -/// Interface for determining unlock state of various content in the game. +/// Interface for determining the players state. /// public interface IPlayerState { /// - /// Gets a value indicating whether the local character is loaded. + /// Gets a value indicating whether the local players data is loaded. /// /// - /// The actual GameObject will not immediately exist when this changes to true. + /// PlayerState is separate from , + /// and as such the game object might not exist when it's loaded. /// bool IsLoaded { get; } @@ -141,37 +142,37 @@ public interface IPlayerState RowRef GrandCompany { get; } /// - /// Gets the Aetheryte row for the local character's home aetheryte. + /// Gets the Aetheryte row for the local player's home aetheryte. /// RowRef HomeAetheryte { get; } /// - /// Gets a span of Aetheryte rows for the local character's favourite aetherytes. + /// Gets a span of Aetheryte rows for the local player's favourite aetherytes. /// ReadOnlySpan> FavouriteAetherytes { get; } /// - /// Gets the Aetheryte row for the local character's free aetheryte. + /// Gets the Aetheryte row for the local player's free aetheryte. /// RowRef FreeAetheryte { get; } /// - /// Gets the amount of received player commendations of the local character. + /// Gets the amount of received player commendations of the local player. /// uint BaseRestedExperience { get; } /// - /// Gets the amount of received player commendations of the local character. + /// Gets the amount of received player commendations of the local player. /// short PlayerCommendations { get; } /// - /// Gets the Carrier Level of Delivery Moogle Quests of the local character. + /// Gets the Carrier Level of Delivery Moogle Quests of the local player. /// byte DeliveryLevel { get; } /// - /// Gets the mentor version of the local character. + /// Gets the mentor version of the local player. /// MentorVersion MentorVersion { get; } From 153870a053252f19e759301ead3a79fcc6b7a72e Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Sun, 5 Oct 2025 14:37:22 +0200 Subject: [PATCH 04/18] Add mentor states --- Dalamud/Game/PlayerState/PlayerState.cs | 15 +++++++++++++ Dalamud/Plugin/Services/IPlayerState.cs | 28 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/Dalamud/Game/PlayerState/PlayerState.cs b/Dalamud/Game/PlayerState/PlayerState.cs index 5e5528eca..7af067119 100644 --- a/Dalamud/Game/PlayerState/PlayerState.cs +++ b/Dalamud/Game/PlayerState/PlayerState.cs @@ -151,6 +151,21 @@ internal unsafe class PlayerState : IServiceType, IPlayerState /// public MentorVersion MentorVersion => this.IsLoaded ? (MentorVersion)CSPlayerState.Instance()->MentorVersion : default; + /// + public bool IsMentor => this.IsLoaded && CSPlayerState.Instance()->IsMentor(); + + /// + public bool IsBattleMentor => this.IsLoaded && CSPlayerState.Instance()->IsBattleMentor(); + + /// + public bool IsTradeMentor => this.IsLoaded && CSPlayerState.Instance()->IsTradeMentor(); + + /// + public bool IsNovice => this.IsLoaded && CSPlayerState.Instance()->IsNovice(); + + /// + public bool IsReturner => this.IsLoaded && CSPlayerState.Instance()->IsReturner(); + /// public int GetAttribute(PlayerAttribute attribute) => this.IsLoaded ? CSPlayerState.Instance()->Attributes[(int)attribute] : default; diff --git a/Dalamud/Plugin/Services/IPlayerState.cs b/Dalamud/Plugin/Services/IPlayerState.cs index a119f231b..98b0c36da 100644 --- a/Dalamud/Plugin/Services/IPlayerState.cs +++ b/Dalamud/Plugin/Services/IPlayerState.cs @@ -176,6 +176,34 @@ public interface IPlayerState /// MentorVersion MentorVersion { get; } + /// + /// Gets a value indicating whether the local player is any kind of Mentor (Battle or Trade Mentor). + /// + bool IsMentor { get; } + + /// + /// Gets a value indicating whether the local player is a Battle Mentor. + /// + bool IsBattleMentor { get; } + + /// + /// Gets a value indicating whether the local player is a Trade Mentor. + /// + bool IsTradeMentor { get; } + + /// + /// Gets a value indicating whether the local player is a novice (aka. Sprout or New Adventurer). + /// + /// + /// Can be if /nastatus was used to deactivate it. + /// + bool IsNovice { get; } + + /// + /// Gets a value indicating whether the local player is a returner. + /// + bool IsReturner { get; } + /// /// Gets the value of an attribute of the local character. /// From a55c8ca773cb0f82a2df0c0e7f934a1f7efe31a8 Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Sun, 5 Oct 2025 14:38:50 +0200 Subject: [PATCH 05/18] Fix warning --- Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs b/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs index 018742271..a4efad488 100644 --- a/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs +++ b/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs @@ -566,7 +566,7 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator return false; // the game uses LocalPlayer here, but using PlayerState seems more safe. - return this.ResolveStringExpression(in context, playerState.EntityId == entityId ? eTrue : eFalse); + return this.ResolveStringExpression(in context, this.playerState.EntityId == entityId ? eTrue : eFalse); } private bool TryResolveColor(in SeStringContext context, in ReadOnlySePayloadSpan payload) From bcf651b5c17e25f48fbc44d96f5837845fd4ee27 Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Mon, 6 Oct 2025 01:38:35 +0200 Subject: [PATCH 06/18] Fix FavoriteAetherytes --- Dalamud/Game/PlayerState/PlayerState.cs | 9 ++++++--- Dalamud/Plugin/Services/IPlayerState.cs | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Dalamud/Game/PlayerState/PlayerState.cs b/Dalamud/Game/PlayerState/PlayerState.cs index 7af067119..06d57133d 100644 --- a/Dalamud/Game/PlayerState/PlayerState.cs +++ b/Dalamud/Game/PlayerState/PlayerState.cs @@ -118,15 +118,18 @@ internal unsafe class PlayerState : IServiceType, IPlayerState public RowRef HomeAetheryte => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->HomeAetheryteId) : default; /// - public ReadOnlySpan> FavouriteAetherytes + public ReadOnlySpan> FavoriteAetherytes { get { var playerState = CSPlayerState.Instance(); - if (playerState->IsLoaded || playerState->FavouriteAetheryteCount == 0) - return []; + if (!playerState->IsLoaded) + return default; var count = playerState->FavouriteAetheryteCount; + if (count == 0) + return default; + var array = new RowRef[count]; for (var i = 0; i < count; i++) diff --git a/Dalamud/Plugin/Services/IPlayerState.cs b/Dalamud/Plugin/Services/IPlayerState.cs index 98b0c36da..bf84227ef 100644 --- a/Dalamud/Plugin/Services/IPlayerState.cs +++ b/Dalamud/Plugin/Services/IPlayerState.cs @@ -149,7 +149,7 @@ public interface IPlayerState /// /// Gets a span of Aetheryte rows for the local player's favourite aetherytes. /// - ReadOnlySpan> FavouriteAetherytes { get; } + ReadOnlySpan> FavoriteAetherytes { get; } /// /// Gets the Aetheryte row for the local player's free aetheryte. From 2cf869872d5bbf1045858367ae9c3954ec48397b Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Mon, 6 Oct 2025 02:08:18 +0200 Subject: [PATCH 07/18] Return IReadOnlyList instead of ReadOnlySpan --- Dalamud/Game/PlayerState/PlayerState.cs | 4 +++- Dalamud/Plugin/Services/IPlayerState.cs | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Dalamud/Game/PlayerState/PlayerState.cs b/Dalamud/Game/PlayerState/PlayerState.cs index 06d57133d..c80166dd5 100644 --- a/Dalamud/Game/PlayerState/PlayerState.cs +++ b/Dalamud/Game/PlayerState/PlayerState.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + using Dalamud.Data; using Dalamud.IoC; using Dalamud.IoC.Internal; @@ -118,7 +120,7 @@ internal unsafe class PlayerState : IServiceType, IPlayerState public RowRef HomeAetheryte => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->HomeAetheryteId) : default; /// - public ReadOnlySpan> FavoriteAetherytes + public IReadOnlyList> FavoriteAetherytes { get { diff --git a/Dalamud/Plugin/Services/IPlayerState.cs b/Dalamud/Plugin/Services/IPlayerState.cs index bf84227ef..1a22f58d6 100644 --- a/Dalamud/Plugin/Services/IPlayerState.cs +++ b/Dalamud/Plugin/Services/IPlayerState.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + using Dalamud.Game.PlayerState; using Lumina.Excel; @@ -147,9 +149,9 @@ public interface IPlayerState RowRef HomeAetheryte { get; } /// - /// Gets a span of Aetheryte rows for the local player's favourite aetherytes. + /// Gets an array of Aetheryte rows for the local player's favourite aetherytes. /// - ReadOnlySpan> FavoriteAetherytes { get; } + IReadOnlyList> FavoriteAetherytes { get; } /// /// Gets the Aetheryte row for the local player's free aetheryte. From 750fa58147b8a68cd0110b084e93f0742cc38411 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 17 Nov 2025 12:52:07 +0000 Subject: [PATCH 08/18] Update ClientStructs --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index f6c479b3f..0afa6b672 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit f6c479b3fa0b452b44403c8ea53d592bec415e1e +Subproject commit 0afa6b67288e5e667da74c1d3ad582e6c964644c From 64d4f7061ad2973d48f8f7cb8108dd70bd358fd1 Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Mon, 17 Nov 2025 19:29:48 +0100 Subject: [PATCH 09/18] Rename namespace PlayerState to Player --- Dalamud/Game/ClientState/Buddy/BuddyList.cs | 8 +++++--- Dalamud/Game/ClientState/ClientState.cs | 7 ++++--- Dalamud/Game/ClientState/Fates/Fate.cs | 3 ++- Dalamud/Game/ClientState/Fates/FateTable.cs | 5 +++-- Dalamud/Game/ClientState/Objects/ObjectTable.cs | 3 ++- Dalamud/Game/ClientState/Objects/Types/GameObject.cs | 3 ++- Dalamud/Game/ClientState/Party/PartyList.cs | 3 ++- Dalamud/Game/ClientState/Statuses/StatusList.cs | 6 ++++-- Dalamud/Game/Network/Internal/NetworkHandlers.cs | 3 ++- Dalamud/Game/{PlayerState => Player}/MentorVersion.cs | 2 +- Dalamud/Game/{PlayerState => Player}/PlayerAttribute.cs | 2 +- Dalamud/Game/{PlayerState => Player}/PlayerState.cs | 2 +- Dalamud/Game/{PlayerState => Player}/Sex.cs | 2 +- Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs | 3 ++- .../Internal/Windows/Data/Widgets/ObjectTableWidget.cs | 2 +- Dalamud/Plugin/Services/IPlayerState.cs | 2 +- 16 files changed, 34 insertions(+), 22 deletions(-) rename Dalamud/Game/{PlayerState => Player}/MentorVersion.cs (95%) rename Dalamud/Game/{PlayerState => Player}/PlayerAttribute.cs (99%) rename Dalamud/Game/{PlayerState => Player}/PlayerState.cs (99%) rename Dalamud/Game/{PlayerState => Player}/Sex.cs (86%) diff --git a/Dalamud/Game/ClientState/Buddy/BuddyList.cs b/Dalamud/Game/ClientState/Buddy/BuddyList.cs index 44774a574..dbac76518 100644 --- a/Dalamud/Game/ClientState/Buddy/BuddyList.cs +++ b/Dalamud/Game/ClientState/Buddy/BuddyList.cs @@ -2,11 +2,13 @@ using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; +using Dalamud.Game.Player; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; -using FFXIVClientStructs.FFXIV.Client.Game.UI; +using CSBuddy = FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy; +using CSUIState = FFXIVClientStructs.FFXIV.Client.Game.UI.UIState; namespace Dalamud.Game.ClientState.Buddy; @@ -24,7 +26,7 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList private const uint InvalidObjectID = 0xE0000000; [ServiceManager.ServiceDependency] - private readonly PlayerState.PlayerState playerState = Service.Get(); + private readonly PlayerState playerState = Service.Get(); [ServiceManager.ServiceConstructor] private BuddyList() @@ -69,7 +71,7 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList } } - private unsafe FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy* BuddyListStruct => &UIState.Instance()->Buddy; + private unsafe CSBuddy* BuddyListStruct => &CSUIState.Instance()->Buddy; /// public IBuddyMember? this[int index] diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 64be5cc67..caf307683 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -6,6 +6,7 @@ using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.Gui; using Dalamud.Game.Network.Internal; +using Dalamud.Game.Player; using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; @@ -15,7 +16,6 @@ using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Application.Network; using FFXIVClientStructs.FFXIV.Client.Game; -using FFXIVClientStructs.FFXIV.Client.Game.UI; using FFXIVClientStructs.FFXIV.Client.Network; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI.Agent; @@ -23,6 +23,7 @@ using FFXIVClientStructs.FFXIV.Client.UI.Agent; using Lumina.Excel.Sheets; using Action = System.Action; +using CSUIState = FFXIVClientStructs.FFXIV.Client.Game.UI.UIState; namespace Dalamud.Game.ClientState; @@ -47,7 +48,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState private readonly NetworkHandlers networkHandlers = Service.Get(); [ServiceManager.ServiceDependency] - private readonly PlayerState.PlayerState playerState = Service.Get(); + private readonly PlayerState playerState = Service.Get(); [ServiceManager.ServiceDependency] private readonly ObjectTable objectTable = Service.Get(); @@ -285,7 +286,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState this.TerritoryType = (ushort)GameMain.Instance()->CurrentTerritoryTypeId; this.MapId = AgentMap.Instance()->CurrentMapId; - this.Instance = UIState.Instance()->PublicInstance.InstanceId; + this.Instance = CSUIState.Instance()->PublicInstance.InstanceId; this.initialized = true; diff --git a/Dalamud/Game/ClientState/Fates/Fate.cs b/Dalamud/Game/ClientState/Fates/Fate.cs index 5a82ef0c5..f82109fd0 100644 --- a/Dalamud/Game/ClientState/Fates/Fate.cs +++ b/Dalamud/Game/ClientState/Fates/Fate.cs @@ -1,6 +1,7 @@ using System.Numerics; using Dalamud.Data; +using Dalamud.Game.Player; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Memory; @@ -153,7 +154,7 @@ internal unsafe partial class Fate if (fate == null) return false; - var playerState = Service.Get(); + var playerState = Service.Get(); return playerState.IsLoaded == true; } diff --git a/Dalamud/Game/ClientState/Fates/FateTable.cs b/Dalamud/Game/ClientState/Fates/FateTable.cs index 2266c762d..30b0f4102 100644 --- a/Dalamud/Game/ClientState/Fates/FateTable.cs +++ b/Dalamud/Game/ClientState/Fates/FateTable.cs @@ -1,6 +1,7 @@ using System.Collections; using System.Collections.Generic; +using Dalamud.Game.Player; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; @@ -63,7 +64,7 @@ internal sealed partial class FateTable : IServiceType, IFateTable if (fate == null) return false; - var playerState = Service.Get(); + var playerState = Service.Get(); return playerState.IsLoaded == true; } @@ -86,7 +87,7 @@ internal sealed partial class FateTable : IServiceType, IFateTable if (offset == IntPtr.Zero) return null; - var playerState = Service.Get(); + var playerState = Service.Get(); if (!playerState.IsLoaded) return null; diff --git a/Dalamud/Game/ClientState/Objects/ObjectTable.cs b/Dalamud/Game/ClientState/Objects/ObjectTable.cs index 0a5e900f0..b66dd4775 100644 --- a/Dalamud/Game/ClientState/Objects/ObjectTable.cs +++ b/Dalamud/Game/ClientState/Objects/ObjectTable.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Game.Player; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; @@ -32,7 +33,7 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable private static int objectTableLength; [ServiceManager.ServiceDependency] - private readonly PlayerState.PlayerState playerState = Service.Get(); + private readonly PlayerState playerState = Service.Get(); private readonly CachedEntry[] cachedObjectTable; diff --git a/Dalamud/Game/ClientState/Objects/Types/GameObject.cs b/Dalamud/Game/ClientState/Objects/Types/GameObject.cs index c37b72961..4b331a479 100644 --- a/Dalamud/Game/ClientState/Objects/Types/GameObject.cs +++ b/Dalamud/Game/ClientState/Objects/Types/GameObject.cs @@ -1,6 +1,7 @@ using System.Numerics; using Dalamud.Game.ClientState.Objects.Enums; +using Dalamud.Game.Player; using Dalamud.Game.Text.SeStringHandling; namespace Dalamud.Game.ClientState.Objects.Types; @@ -171,7 +172,7 @@ internal partial class GameObject if (actor == null) return false; - var playerState = Service.Get(); + var playerState = Service.Get(); return playerState.IsLoaded == true; } diff --git a/Dalamud/Game/ClientState/Party/PartyList.cs b/Dalamud/Game/ClientState/Party/PartyList.cs index 0a81095c6..9618b679c 100644 --- a/Dalamud/Game/ClientState/Party/PartyList.cs +++ b/Dalamud/Game/ClientState/Party/PartyList.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Dalamud.Game.Player; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; @@ -25,7 +26,7 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList private const int AllianceLength = 20; [ServiceManager.ServiceDependency] - private readonly PlayerState.PlayerState playerState = Service.Get(); + private readonly PlayerState playerState = Service.Get(); [ServiceManager.ServiceConstructor] private PartyList() diff --git a/Dalamud/Game/ClientState/Statuses/StatusList.cs b/Dalamud/Game/ClientState/Statuses/StatusList.cs index 04d0d822c..410ae9d7c 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 Dalamud.Game.Player; + namespace Dalamud.Game.ClientState.Statuses; /// @@ -72,7 +74,7 @@ public sealed unsafe partial class StatusList // 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 // here or somewhere else. - var playerState = Service.Get(); + var playerState = Service.Get(); if (!playerState.IsLoaded) return null; @@ -89,7 +91,7 @@ public sealed unsafe partial class StatusList if (address == IntPtr.Zero) return null; - var playerState = Service.Get(); + var playerState = Service.Get(); if (!playerState.IsLoaded) return null; diff --git a/Dalamud/Game/Network/Internal/NetworkHandlers.cs b/Dalamud/Game/Network/Internal/NetworkHandlers.cs index 2f9276cc0..6a6d73b33 100644 --- a/Dalamud/Game/Network/Internal/NetworkHandlers.cs +++ b/Dalamud/Game/Network/Internal/NetworkHandlers.cs @@ -11,6 +11,7 @@ using Dalamud.Game.Gui; using Dalamud.Game.Network.Internal.MarketBoardUploaders; using Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis; using Dalamud.Game.Network.Structures; +using Dalamud.Game.Player; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Hooking; using Dalamud.Networking.Http; @@ -268,7 +269,7 @@ internal unsafe class NetworkHandlers : IInternalDisposableService private static (ulong UploaderId, uint WorldId) GetUploaderInfo() { - var playerState = Service.Get(); + var playerState = Service.Get(); return (playerState.ContentId, playerState.CurrentWorld.RowId); } diff --git a/Dalamud/Game/PlayerState/MentorVersion.cs b/Dalamud/Game/Player/MentorVersion.cs similarity index 95% rename from Dalamud/Game/PlayerState/MentorVersion.cs rename to Dalamud/Game/Player/MentorVersion.cs index 701eda112..e856e1169 100644 --- a/Dalamud/Game/PlayerState/MentorVersion.cs +++ b/Dalamud/Game/Player/MentorVersion.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.PlayerState; +namespace Dalamud.Game.Player; /// /// Specifies the mentor certification version for a player. diff --git a/Dalamud/Game/PlayerState/PlayerAttribute.cs b/Dalamud/Game/Player/PlayerAttribute.cs similarity index 99% rename from Dalamud/Game/PlayerState/PlayerAttribute.cs rename to Dalamud/Game/Player/PlayerAttribute.cs index 4db8af107..9d9954817 100644 --- a/Dalamud/Game/PlayerState/PlayerAttribute.cs +++ b/Dalamud/Game/Player/PlayerAttribute.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.PlayerState; +namespace Dalamud.Game.Player; /// /// Represents a player's attribute. diff --git a/Dalamud/Game/PlayerState/PlayerState.cs b/Dalamud/Game/Player/PlayerState.cs similarity index 99% rename from Dalamud/Game/PlayerState/PlayerState.cs rename to Dalamud/Game/Player/PlayerState.cs index c80166dd5..316b09e2f 100644 --- a/Dalamud/Game/PlayerState/PlayerState.cs +++ b/Dalamud/Game/Player/PlayerState.cs @@ -13,7 +13,7 @@ using Lumina.Excel.Sheets; using CSPlayerState = FFXIVClientStructs.FFXIV.Client.Game.UI.PlayerState; using GrandCompany = Lumina.Excel.Sheets.GrandCompany; -namespace Dalamud.Game.PlayerState; +namespace Dalamud.Game.Player; /// /// This class contains the PlayerState wrappers. diff --git a/Dalamud/Game/PlayerState/Sex.cs b/Dalamud/Game/Player/Sex.cs similarity index 86% rename from Dalamud/Game/PlayerState/Sex.cs rename to Dalamud/Game/Player/Sex.cs index e6ed6cc78..0981cb9a4 100644 --- a/Dalamud/Game/PlayerState/Sex.cs +++ b/Dalamud/Game/Player/Sex.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.PlayerState; +namespace Dalamud.Game.Player; /// /// Represents the sex of a character. diff --git a/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs b/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs index a4efad488..58bcdbd0b 100644 --- a/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs +++ b/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs @@ -8,6 +8,7 @@ using Dalamud.Configuration.Internal; using Dalamud.Data; using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.Config; +using Dalamud.Game.Player; using Dalamud.Game.Text.Evaluator.Internal; using Dalamud.Game.Text.Noun; using Dalamud.Game.Text.Noun.Enums; @@ -68,7 +69,7 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator private readonly SheetRedirectResolver sheetRedirectResolver = Service.Get(); [ServiceManager.ServiceDependency] - private readonly PlayerState.PlayerState playerState = Service.Get(); + private readonly PlayerState playerState = Service.Get(); private readonly ConcurrentDictionary, string> actStrCache = []; private readonly ConcurrentDictionary, string> objStrCache = []; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs index 9a2de7261..71fb18352 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs @@ -4,7 +4,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.Gui; -using Dalamud.Game.PlayerState; +using Dalamud.Game.Player; using Dalamud.Utility; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; diff --git a/Dalamud/Plugin/Services/IPlayerState.cs b/Dalamud/Plugin/Services/IPlayerState.cs index 1a22f58d6..425ffc963 100644 --- a/Dalamud/Plugin/Services/IPlayerState.cs +++ b/Dalamud/Plugin/Services/IPlayerState.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -using Dalamud.Game.PlayerState; +using Dalamud.Game.Player; using Lumina.Excel; using Lumina.Excel.Sheets; From 7ec1de4c76073bc67ff65516c4bc7175addf269c Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Sun, 19 Oct 2025 22:45:50 +0200 Subject: [PATCH 10/18] Let IDalamudPluginInterface inherit from IServiceProvider --- Dalamud/IoC/Internal/ServiceScope.cs | 10 ++++++++-- Dalamud/Plugin/DalamudPluginInterface.cs | 6 ++++++ Dalamud/Plugin/IDalamudPluginInterface.cs | 4 +--- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Dalamud/IoC/Internal/ServiceScope.cs b/Dalamud/IoC/Internal/ServiceScope.cs index 98209eeb7..8b12dce0b 100644 --- a/Dalamud/IoC/Internal/ServiceScope.cs +++ b/Dalamud/IoC/Internal/ServiceScope.cs @@ -1,4 +1,4 @@ -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -12,7 +12,7 @@ namespace Dalamud.IoC.Internal; /// /// Container enabling the creation of scoped services. /// -internal interface IServiceScope : IAsyncDisposable +internal interface IServiceScope : IServiceProvider, IAsyncDisposable { /// /// Register objects that may be injected to scoped services, @@ -57,6 +57,12 @@ internal class ServiceScopeImpl : IServiceScope /// The container this scope will use to create services. public ServiceScopeImpl(ServiceContainer container) => this.container = container; + /// + object? IServiceProvider.GetService(Type serviceType) + { + return ((IServiceProvider)this.container).GetService(serviceType); + } + /// public void RegisterPrivateScopes(params object[] scopes) { diff --git a/Dalamud/Plugin/DalamudPluginInterface.cs b/Dalamud/Plugin/DalamudPluginInterface.cs index 8455ce164..603bed7a5 100644 --- a/Dalamud/Plugin/DalamudPluginInterface.cs +++ b/Dalamud/Plugin/DalamudPluginInterface.cs @@ -454,6 +454,12 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa #region Dependency Injection + /// + public object? GetService(Type serviceType) + { + return this.plugin.ServiceScope.GetService(serviceType); + } + /// public T? Create(params object[] scopedObjects) where T : class { diff --git a/Dalamud/Plugin/IDalamudPluginInterface.cs b/Dalamud/Plugin/IDalamudPluginInterface.cs index e1dd34f87..d1b6977d4 100644 --- a/Dalamud/Plugin/IDalamudPluginInterface.cs +++ b/Dalamud/Plugin/IDalamudPluginInterface.cs @@ -8,8 +8,6 @@ using System.Threading.Tasks; using Dalamud.Configuration; using Dalamud.Game.Text; using Dalamud.Game.Text.Sanitizer; -using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Interface; using Dalamud.Interface.Internal.Windows.PluginInstaller; using Dalamud.Interface.Internal.Windows.Settings; @@ -24,7 +22,7 @@ namespace Dalamud.Plugin; /// /// This interface acts as an interface to various objects needed to interact with Dalamud and the game. /// -public interface IDalamudPluginInterface +public interface IDalamudPluginInterface : IServiceProvider { /// /// Delegate for localization change with two-letter iso lang code. From d3c812ba6c8f25a57c4a5b7b528ee5908e1b22f6 Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Sun, 19 Oct 2025 22:52:20 +0200 Subject: [PATCH 11/18] Add IDalamudService marker interface --- Dalamud/Plugin/Services/IAddonEventManager.cs | 4 ++-- Dalamud/Plugin/Services/IAddonLifecycle.cs | 4 ++-- Dalamud/Plugin/Services/IAetheryteList.cs | 4 ++-- Dalamud/Plugin/Services/IBuddyList.cs | 4 ++-- Dalamud/Plugin/Services/IChatGui.cs | 2 +- Dalamud/Plugin/Services/IClientState.cs | 2 +- Dalamud/Plugin/Services/ICommandManager.cs | 4 ++-- Dalamud/Plugin/Services/ICondition.cs | 4 ++-- Dalamud/Plugin/Services/IConsole.cs | 4 ++-- Dalamud/Plugin/Services/IContextMenu.cs | 2 +- Dalamud/Plugin/Services/IDalamudService.cs | 10 ++++++++++ Dalamud/Plugin/Services/IDataManager.cs | 2 +- Dalamud/Plugin/Services/IDtrBar.cs | 4 ++-- Dalamud/Plugin/Services/IDutyState.cs | 4 ++-- Dalamud/Plugin/Services/IFateTable.cs | 4 ++-- Dalamud/Plugin/Services/IFlyTextGui.cs | 4 ++-- Dalamud/Plugin/Services/IFramework.cs | 4 ++-- Dalamud/Plugin/Services/IGameConfig.cs | 4 ++-- Dalamud/Plugin/Services/IGameGui.cs | 2 +- Dalamud/Plugin/Services/IGameInteropProvider.cs | 4 ++-- Dalamud/Plugin/Services/IGameInventory.cs | 4 ++-- Dalamud/Plugin/Services/IGameLifecycle.cs | 4 ++-- Dalamud/Plugin/Services/IGameNetwork.cs | 4 ++-- Dalamud/Plugin/Services/IGamepadState.cs | 4 ++-- Dalamud/Plugin/Services/IJobGauges.cs | 4 ++-- Dalamud/Plugin/Services/IKeyState.cs | 4 ++-- Dalamud/Plugin/Services/IMarketBoard.cs | 4 ++-- Dalamud/Plugin/Services/INamePlateGui.cs | 2 +- Dalamud/Plugin/Services/INotificationManager.cs | 2 +- Dalamud/Plugin/Services/IObjectTable.cs | 2 +- Dalamud/Plugin/Services/IPartyFinderGui.cs | 4 ++-- Dalamud/Plugin/Services/IPartyList.cs | 4 ++-- Dalamud/Plugin/Services/IPluginLog.cs | 4 ++-- Dalamud/Plugin/Services/ISeStringEvaluator.cs | 2 +- Dalamud/Plugin/Services/ISigScanner.cs | 4 +++- Dalamud/Plugin/Services/ITargetManager.cs | 5 +++-- Dalamud/Plugin/Services/ITextureProvider.cs | 2 +- Dalamud/Plugin/Services/ITextureReadbackProvider.cs | 3 +-- .../Plugin/Services/ITextureSubstitutionProvider.cs | 4 ++-- Dalamud/Plugin/Services/ITitleScreenMenu.cs | 4 ++-- Dalamud/Plugin/Services/IToastGui.cs | 4 ++-- 41 files changed, 81 insertions(+), 69 deletions(-) create mode 100644 Dalamud/Plugin/Services/IDalamudService.cs diff --git a/Dalamud/Plugin/Services/IAddonEventManager.cs b/Dalamud/Plugin/Services/IAddonEventManager.cs index c6499e4e2..6b7b1166e 100644 --- a/Dalamud/Plugin/Services/IAddonEventManager.cs +++ b/Dalamud/Plugin/Services/IAddonEventManager.cs @@ -1,4 +1,4 @@ -using Dalamud.Game.Addon.Events; +using Dalamud.Game.Addon.Events; using Dalamud.Game.Addon.Events.EventDataTypes; namespace Dalamud.Plugin.Services; @@ -6,7 +6,7 @@ namespace Dalamud.Plugin.Services; /// /// Service provider for addon event management. /// -public interface IAddonEventManager +public interface IAddonEventManager : IDalamudService { /// /// Delegate to be called when an event is received. diff --git a/Dalamud/Plugin/Services/IAddonLifecycle.cs b/Dalamud/Plugin/Services/IAddonLifecycle.cs index ebf629b85..1269b13dc 100644 --- a/Dalamud/Plugin/Services/IAddonLifecycle.cs +++ b/Dalamud/Plugin/Services/IAddonLifecycle.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Runtime.InteropServices; using Dalamud.Game.Addon.Lifecycle; @@ -9,7 +9,7 @@ namespace Dalamud.Plugin.Services; /// /// This class provides events for in-game addon lifecycles. /// -public interface IAddonLifecycle +public interface IAddonLifecycle : IDalamudService { /// /// Delegate for receiving addon lifecycle event messages. diff --git a/Dalamud/Plugin/Services/IAetheryteList.cs b/Dalamud/Plugin/Services/IAetheryteList.cs index 88c2ff616..58b82ebf6 100644 --- a/Dalamud/Plugin/Services/IAetheryteList.cs +++ b/Dalamud/Plugin/Services/IAetheryteList.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Game.ClientState.Aetherytes; @@ -7,7 +7,7 @@ namespace Dalamud.Plugin.Services; /// /// This collection represents the list of available Aetherytes in the Teleport window. /// -public interface IAetheryteList : IReadOnlyCollection +public interface IAetheryteList : IDalamudService, IReadOnlyCollection { /// /// Gets the amount of Aetherytes the local player has unlocked. diff --git a/Dalamud/Plugin/Services/IBuddyList.cs b/Dalamud/Plugin/Services/IBuddyList.cs index 77c0b9c17..8d3790b6d 100644 --- a/Dalamud/Plugin/Services/IBuddyList.cs +++ b/Dalamud/Plugin/Services/IBuddyList.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Game.ClientState.Buddy; @@ -8,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// This collection represents the buddies present in your squadron or trust party. /// It does not include the local player. /// -public interface IBuddyList : IReadOnlyCollection +public interface IBuddyList : IDalamudService, IReadOnlyCollection { /// /// Gets the amount of battle buddies the local player has. diff --git a/Dalamud/Plugin/Services/IChatGui.cs b/Dalamud/Plugin/Services/IChatGui.cs index c474ca386..572ac6c95 100644 --- a/Dalamud/Plugin/Services/IChatGui.cs +++ b/Dalamud/Plugin/Services/IChatGui.cs @@ -10,7 +10,7 @@ namespace Dalamud.Plugin.Services; /// /// This class handles interacting with the native chat UI. /// -public interface IChatGui +public interface IChatGui : IDalamudService { /// /// A delegate type used with the event. diff --git a/Dalamud/Plugin/Services/IClientState.cs b/Dalamud/Plugin/Services/IClientState.cs index 36bf2e296..2555b3b30 100644 --- a/Dalamud/Plugin/Services/IClientState.cs +++ b/Dalamud/Plugin/Services/IClientState.cs @@ -8,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// /// This class represents the state of the game client at the time of access. /// -public interface IClientState +public interface IClientState : IDalamudService { /// /// A delegate type used for the event. diff --git a/Dalamud/Plugin/Services/ICommandManager.cs b/Dalamud/Plugin/Services/ICommandManager.cs index a6bc4763f..46138cd71 100644 --- a/Dalamud/Plugin/Services/ICommandManager.cs +++ b/Dalamud/Plugin/Services/ICommandManager.cs @@ -1,4 +1,4 @@ -using System.Collections.ObjectModel; +using System.Collections.ObjectModel; using Dalamud.Game.Command; @@ -7,7 +7,7 @@ namespace Dalamud.Plugin.Services; /// /// This class manages registered in-game slash commands. /// -public interface ICommandManager +public interface ICommandManager : IDalamudService { /// /// Gets a read-only list of all registered commands. diff --git a/Dalamud/Plugin/Services/ICondition.cs b/Dalamud/Plugin/Services/ICondition.cs index 4ea9e7f76..c37117f3c 100644 --- a/Dalamud/Plugin/Services/ICondition.cs +++ b/Dalamud/Plugin/Services/ICondition.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Game.ClientState.Conditions; @@ -7,7 +7,7 @@ namespace Dalamud.Plugin.Services; /// /// Provides access to conditions (generally player state). You can check whether a player is in combat, mounted, etc. /// -public interface ICondition +public interface ICondition : IDalamudService { /// /// A delegate type used with the event. diff --git a/Dalamud/Plugin/Services/IConsole.cs b/Dalamud/Plugin/Services/IConsole.cs index 0b6832efb..be920a5c9 100644 --- a/Dalamud/Plugin/Services/IConsole.cs +++ b/Dalamud/Plugin/Services/IConsole.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using Dalamud.Console; @@ -8,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// Provides functions to register console commands and variables. /// [Experimental("Dalamud001")] -public interface IConsole +public interface IConsole : IDalamudService { /// /// Gets this plugin's namespace prefix, derived off its internal name. diff --git a/Dalamud/Plugin/Services/IContextMenu.cs b/Dalamud/Plugin/Services/IContextMenu.cs index 02f773441..ed99f595e 100644 --- a/Dalamud/Plugin/Services/IContextMenu.cs +++ b/Dalamud/Plugin/Services/IContextMenu.cs @@ -5,7 +5,7 @@ namespace Dalamud.Plugin.Services; /// /// This class provides methods for interacting with the game's context menu. /// -public interface IContextMenu +public interface IContextMenu : IDalamudService { /// /// A delegate type used for the event. diff --git a/Dalamud/Plugin/Services/IDalamudService.cs b/Dalamud/Plugin/Services/IDalamudService.cs new file mode 100644 index 000000000..1472b27da --- /dev/null +++ b/Dalamud/Plugin/Services/IDalamudService.cs @@ -0,0 +1,10 @@ +namespace Dalamud.Plugin.Services; + +/// +/// Marker interface for Dalamud services. +/// +/// +/// This interface is implemented by all services provided through Dalamud's +/// dependency injection system. +/// +public interface IDalamudService; diff --git a/Dalamud/Plugin/Services/IDataManager.cs b/Dalamud/Plugin/Services/IDataManager.cs index 65c51a9fb..474d34ecb 100644 --- a/Dalamud/Plugin/Services/IDataManager.cs +++ b/Dalamud/Plugin/Services/IDataManager.cs @@ -14,7 +14,7 @@ namespace Dalamud.Plugin.Services; /// /// This class provides data for Dalamud-internal features, but can also be used by plugins if needed. /// -public interface IDataManager +public interface IDataManager : IDalamudService { /// /// Gets the current game client language. diff --git a/Dalamud/Plugin/Services/IDtrBar.cs b/Dalamud/Plugin/Services/IDtrBar.cs index 8ab34c6f2..a24327e23 100644 --- a/Dalamud/Plugin/Services/IDtrBar.cs +++ b/Dalamud/Plugin/Services/IDtrBar.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Game.Gui.Dtr; using Dalamud.Game.Text.SeStringHandling; @@ -8,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// /// Class used to interface with the server info bar. /// -public interface IDtrBar +public interface IDtrBar : IDalamudService { /// /// Gets a read-only copy of the list of all DTR bar entries. diff --git a/Dalamud/Plugin/Services/IDutyState.cs b/Dalamud/Plugin/Services/IDutyState.cs index 3d49f68cb..9ad2e3c24 100644 --- a/Dalamud/Plugin/Services/IDutyState.cs +++ b/Dalamud/Plugin/Services/IDutyState.cs @@ -1,9 +1,9 @@ -namespace Dalamud.Plugin.Services; +namespace Dalamud.Plugin.Services; /// /// This class represents the state of the currently occupied duty. /// -public interface IDutyState +public interface IDutyState : IDalamudService { /// /// Event that gets fired when the duty starts. diff --git a/Dalamud/Plugin/Services/IFateTable.cs b/Dalamud/Plugin/Services/IFateTable.cs index d10141050..3392d8e23 100644 --- a/Dalamud/Plugin/Services/IFateTable.cs +++ b/Dalamud/Plugin/Services/IFateTable.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Game.ClientState.Fates; @@ -7,7 +7,7 @@ namespace Dalamud.Plugin.Services; /// /// This collection represents the currently available Fate events. /// -public interface IFateTable : IReadOnlyCollection +public interface IFateTable : IDalamudService, IReadOnlyCollection { /// /// Gets the address of the Fate table. diff --git a/Dalamud/Plugin/Services/IFlyTextGui.cs b/Dalamud/Plugin/Services/IFlyTextGui.cs index 04fae351d..6c0e40fd6 100644 --- a/Dalamud/Plugin/Services/IFlyTextGui.cs +++ b/Dalamud/Plugin/Services/IFlyTextGui.cs @@ -1,4 +1,4 @@ -using Dalamud.Game.Gui.FlyText; +using Dalamud.Game.Gui.FlyText; using Dalamud.Game.Text.SeStringHandling; namespace Dalamud.Plugin.Services; @@ -6,7 +6,7 @@ namespace Dalamud.Plugin.Services; /// /// This class facilitates interacting with and creating native in-game "fly text". /// -public interface IFlyTextGui +public interface IFlyTextGui : IDalamudService { /// /// The delegate defining the type for the FlyText event. diff --git a/Dalamud/Plugin/Services/IFramework.cs b/Dalamud/Plugin/Services/IFramework.cs index f1a4b6906..3524ca668 100644 --- a/Dalamud/Plugin/Services/IFramework.cs +++ b/Dalamud/Plugin/Services/IFramework.cs @@ -1,4 +1,4 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; using Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -24,7 +24,7 @@ namespace Dalamud.Plugin.Services; /// See to see the difference in behaviors, and how would a misuse of these /// functions result in a deadlock. /// -public interface IFramework +public interface IFramework : IDalamudService { /// /// A delegate type used with the event. diff --git a/Dalamud/Plugin/Services/IGameConfig.cs b/Dalamud/Plugin/Services/IGameConfig.cs index 5d8378659..10883c6d1 100644 --- a/Dalamud/Plugin/Services/IGameConfig.cs +++ b/Dalamud/Plugin/Services/IGameConfig.cs @@ -1,4 +1,4 @@ -using System.Diagnostics; +using System.Diagnostics; using Dalamud.Game.Config; using Dalamud.Plugin.Internal.Types; @@ -17,7 +17,7 @@ namespace Dalamud.Plugin.Services; /// If property access from the plugin constructor is desired, do the value retrieval asynchronously via /// ; do not wait for the result right away. /// -public interface IGameConfig +public interface IGameConfig : IDalamudService { /// /// Event which is fired when any game config option is changed. diff --git a/Dalamud/Plugin/Services/IGameGui.cs b/Dalamud/Plugin/Services/IGameGui.cs index 6c2e0083e..933252ff4 100644 --- a/Dalamud/Plugin/Services/IGameGui.cs +++ b/Dalamud/Plugin/Services/IGameGui.cs @@ -9,7 +9,7 @@ namespace Dalamud.Plugin.Services; /// /// A class handling many aspects of the in-game UI. /// -public unsafe interface IGameGui +public unsafe interface IGameGui : IDalamudService { /// /// Event which is fired when the game UI hiding is toggled. diff --git a/Dalamud/Plugin/Services/IGameInteropProvider.cs b/Dalamud/Plugin/Services/IGameInteropProvider.cs index 99e36c7ed..645d70ac6 100644 --- a/Dalamud/Plugin/Services/IGameInteropProvider.cs +++ b/Dalamud/Plugin/Services/IGameInteropProvider.cs @@ -1,4 +1,4 @@ -using System.Diagnostics; +using System.Diagnostics; using Dalamud.Hooking; using Dalamud.Utility.Signatures; @@ -8,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// /// Service responsible for the creation of hooks. /// -public interface IGameInteropProvider +public interface IGameInteropProvider : IDalamudService { /// /// Available hooking backends. diff --git a/Dalamud/Plugin/Services/IGameInventory.cs b/Dalamud/Plugin/Services/IGameInventory.cs index 0dff1ff03..cba1c9872 100644 --- a/Dalamud/Plugin/Services/IGameInventory.cs +++ b/Dalamud/Plugin/Services/IGameInventory.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Game.Inventory; using Dalamud.Game.Inventory.InventoryEventArgTypes; @@ -8,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// /// This class provides events for the in-game inventory. /// -public interface IGameInventory +public interface IGameInventory : IDalamudService { /// /// Delegate function to be called when inventories have been changed. diff --git a/Dalamud/Plugin/Services/IGameLifecycle.cs b/Dalamud/Plugin/Services/IGameLifecycle.cs index caa64ed23..8fae3fc0e 100644 --- a/Dalamud/Plugin/Services/IGameLifecycle.cs +++ b/Dalamud/Plugin/Services/IGameLifecycle.cs @@ -1,11 +1,11 @@ -using System.Threading; +using System.Threading; namespace Dalamud.Plugin.Services; /// /// Class offering cancellation tokens for common gameplay events. /// -public interface IGameLifecycle +public interface IGameLifecycle : IDalamudService { /// /// Gets a token that is cancelled when Dalamud is unloading. diff --git a/Dalamud/Plugin/Services/IGameNetwork.cs b/Dalamud/Plugin/Services/IGameNetwork.cs index 969176da7..4abf20834 100644 --- a/Dalamud/Plugin/Services/IGameNetwork.cs +++ b/Dalamud/Plugin/Services/IGameNetwork.cs @@ -1,4 +1,4 @@ -using Dalamud.Game.Network; +using Dalamud.Game.Network; namespace Dalamud.Plugin.Services; @@ -6,7 +6,7 @@ namespace Dalamud.Plugin.Services; /// This class handles interacting with game network events. /// [Obsolete("Will be removed in a future release. Use packet handler hooks instead.", true)] -public interface IGameNetwork +public interface IGameNetwork : IDalamudService { // TODO(v9): we shouldn't be passing pointers to the actual data here diff --git a/Dalamud/Plugin/Services/IGamepadState.cs b/Dalamud/Plugin/Services/IGamepadState.cs index 2816c927e..bdb07b91b 100644 --- a/Dalamud/Plugin/Services/IGamepadState.cs +++ b/Dalamud/Plugin/Services/IGamepadState.cs @@ -1,4 +1,4 @@ -using System.Numerics; +using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.GamePad; @@ -10,7 +10,7 @@ namespace Dalamud.Plugin.Services; /// /// Will block game's gamepad input if is set. /// -public interface IGamepadState +public interface IGamepadState : IDalamudService { /// /// Gets the pointer to the current instance of the GamepadInput struct. diff --git a/Dalamud/Plugin/Services/IJobGauges.cs b/Dalamud/Plugin/Services/IJobGauges.cs index 4489a7be7..3313de7f6 100644 --- a/Dalamud/Plugin/Services/IJobGauges.cs +++ b/Dalamud/Plugin/Services/IJobGauges.cs @@ -1,11 +1,11 @@ -using Dalamud.Game.ClientState.JobGauge.Types; +using Dalamud.Game.ClientState.JobGauge.Types; namespace Dalamud.Plugin.Services; /// /// This class converts in-memory Job gauge data to structs. /// -public interface IJobGauges +public interface IJobGauges : IDalamudService { /// /// Gets the address of the JobGauge data. diff --git a/Dalamud/Plugin/Services/IKeyState.cs b/Dalamud/Plugin/Services/IKeyState.cs index de78978ca..06d6c9b49 100644 --- a/Dalamud/Plugin/Services/IKeyState.cs +++ b/Dalamud/Plugin/Services/IKeyState.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Game.ClientState.Keys; @@ -16,7 +16,7 @@ namespace Dalamud.Plugin.Services; /// index & 2 = key up (ephemeral). /// index & 3 = short key press (ephemeral). /// -public interface IKeyState +public interface IKeyState : IDalamudService { /// /// Get or set the key-pressed state for a given vkCode. diff --git a/Dalamud/Plugin/Services/IMarketBoard.cs b/Dalamud/Plugin/Services/IMarketBoard.cs index 3fded6987..0bdfad175 100644 --- a/Dalamud/Plugin/Services/IMarketBoard.cs +++ b/Dalamud/Plugin/Services/IMarketBoard.cs @@ -1,11 +1,11 @@ -using Dalamud.Game.Network.Structures; +using Dalamud.Game.Network.Structures; namespace Dalamud.Plugin.Services; /// /// Provides access to market board related events as the client receives/sends them. /// -public interface IMarketBoard +public interface IMarketBoard : IDalamudService { /// /// A delegate type used with the event. diff --git a/Dalamud/Plugin/Services/INamePlateGui.cs b/Dalamud/Plugin/Services/INamePlateGui.cs index eb2579bae..b58b5b7d0 100644 --- a/Dalamud/Plugin/Services/INamePlateGui.cs +++ b/Dalamud/Plugin/Services/INamePlateGui.cs @@ -7,7 +7,7 @@ namespace Dalamud.Plugin.Services; /// /// Class used to modify the data used when rendering nameplates. /// -public interface INamePlateGui +public interface INamePlateGui : IDalamudService { /// /// The delegate used for receiving nameplate update events. diff --git a/Dalamud/Plugin/Services/INotificationManager.cs b/Dalamud/Plugin/Services/INotificationManager.cs index 7d9ccd0b0..6d9dbf584 100644 --- a/Dalamud/Plugin/Services/INotificationManager.cs +++ b/Dalamud/Plugin/Services/INotificationManager.cs @@ -3,7 +3,7 @@ using Dalamud.Interface.ImGuiNotification; namespace Dalamud.Plugin.Services; /// Manager for notifications provided by Dalamud using ImGui. -public interface INotificationManager +public interface INotificationManager : IDalamudService { /// Adds a notification. /// The new notification. diff --git a/Dalamud/Plugin/Services/IObjectTable.cs b/Dalamud/Plugin/Services/IObjectTable.cs index 36cd72ebe..be8e50dea 100644 --- a/Dalamud/Plugin/Services/IObjectTable.cs +++ b/Dalamud/Plugin/Services/IObjectTable.cs @@ -8,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// /// This collection represents the currently spawned FFXIV game objects. /// -public interface IObjectTable : IEnumerable +public interface IObjectTable : IDalamudService, IEnumerable { /// /// Gets the address of the object table. diff --git a/Dalamud/Plugin/Services/IPartyFinderGui.cs b/Dalamud/Plugin/Services/IPartyFinderGui.cs index fb7a49acd..d9b14baed 100644 --- a/Dalamud/Plugin/Services/IPartyFinderGui.cs +++ b/Dalamud/Plugin/Services/IPartyFinderGui.cs @@ -1,11 +1,11 @@ -using Dalamud.Game.Gui.PartyFinder.Types; +using Dalamud.Game.Gui.PartyFinder.Types; namespace Dalamud.Plugin.Services; /// /// This class handles interacting with the native PartyFinder window. /// -public interface IPartyFinderGui +public interface IPartyFinderGui : IDalamudService { /// /// Event type fired each time the game receives an individual Party Finder listing. diff --git a/Dalamud/Plugin/Services/IPartyList.cs b/Dalamud/Plugin/Services/IPartyList.cs index b046f36db..1af3fa962 100644 --- a/Dalamud/Plugin/Services/IPartyList.cs +++ b/Dalamud/Plugin/Services/IPartyList.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Game.ClientState.Party; @@ -7,7 +7,7 @@ namespace Dalamud.Plugin.Services; /// /// This collection represents the actors present in your party or alliance. /// -public interface IPartyList : IReadOnlyCollection +public interface IPartyList : IDalamudService, IReadOnlyCollection { /// /// Gets the amount of party members the local player has. diff --git a/Dalamud/Plugin/Services/IPluginLog.cs b/Dalamud/Plugin/Services/IPluginLog.cs index 38406fd91..38786f0d2 100644 --- a/Dalamud/Plugin/Services/IPluginLog.cs +++ b/Dalamud/Plugin/Services/IPluginLog.cs @@ -1,4 +1,4 @@ -using Serilog; +using Serilog; using Serilog.Events; #pragma warning disable CS1573 // See https://github.com/dotnet/roslyn/issues/40325 @@ -8,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// /// An opinionated service to handle logging for plugins. /// -public interface IPluginLog +public interface IPluginLog : IDalamudService { /// /// Gets a Serilog ILogger instance for this plugin. This is the entrypoint for plugins that wish to use more diff --git a/Dalamud/Plugin/Services/ISeStringEvaluator.cs b/Dalamud/Plugin/Services/ISeStringEvaluator.cs index 4efc29e3e..8ab7adad1 100644 --- a/Dalamud/Plugin/Services/ISeStringEvaluator.cs +++ b/Dalamud/Plugin/Services/ISeStringEvaluator.cs @@ -9,7 +9,7 @@ namespace Dalamud.Plugin.Services; /// /// Defines a service for retrieving localized text for various in-game entities. /// -public interface ISeStringEvaluator +public interface ISeStringEvaluator : IDalamudService { /// /// Evaluates macros in a . diff --git a/Dalamud/Plugin/Services/ISigScanner.cs b/Dalamud/Plugin/Services/ISigScanner.cs index ac0f2c55f..fbbd8b05a 100644 --- a/Dalamud/Plugin/Services/ISigScanner.cs +++ b/Dalamud/Plugin/Services/ISigScanner.cs @@ -2,12 +2,14 @@ using System.Collections.Generic; using System.Diagnostics; using System.Threading; +using Dalamud.Plugin.Services; + namespace Dalamud.Game; /// /// A SigScanner facilitates searching for memory signatures in a given ProcessModule. /// -public interface ISigScanner +public interface ISigScanner : IDalamudService { /// /// Gets a value indicating whether the search on this module is performed on a copy. diff --git a/Dalamud/Plugin/Services/ITargetManager.cs b/Dalamud/Plugin/Services/ITargetManager.cs index 5ba9f390e..9c9fce550 100644 --- a/Dalamud/Plugin/Services/ITargetManager.cs +++ b/Dalamud/Plugin/Services/ITargetManager.cs @@ -1,11 +1,12 @@ -using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Plugin.Services; namespace Dalamud.Game.ClientState.Objects; /// /// Get and set various kinds of targets for the player. /// -public interface ITargetManager +public interface ITargetManager : IDalamudService { /// /// Gets or sets the current target. diff --git a/Dalamud/Plugin/Services/ITextureProvider.cs b/Dalamud/Plugin/Services/ITextureProvider.cs index a8ad76995..7cd1b7c86 100644 --- a/Dalamud/Plugin/Services/ITextureProvider.cs +++ b/Dalamud/Plugin/Services/ITextureProvider.cs @@ -32,7 +32,7 @@ namespace Dalamud.Plugin.Services; /// . /// /// -public interface ITextureProvider +public interface ITextureProvider : IDalamudService { /// Creates an empty texture. /// Texture specifications. diff --git a/Dalamud/Plugin/Services/ITextureReadbackProvider.cs b/Dalamud/Plugin/Services/ITextureReadbackProvider.cs index 3d2894355..00b684cbb 100644 --- a/Dalamud/Plugin/Services/ITextureReadbackProvider.cs +++ b/Dalamud/Plugin/Services/ITextureReadbackProvider.cs @@ -3,14 +3,13 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using Dalamud.Interface.Internal; using Dalamud.Interface.Textures; using Dalamud.Interface.Textures.TextureWraps; namespace Dalamud.Plugin.Services; /// Service that grants you to read instances of . -public interface ITextureReadbackProvider +public interface ITextureReadbackProvider : IDalamudService { /// Gets the raw data of a texture wrap. /// The source texture wrap. diff --git a/Dalamud/Plugin/Services/ITextureSubstitutionProvider.cs b/Dalamud/Plugin/Services/ITextureSubstitutionProvider.cs index 371fbaf0f..dcd1b00cc 100644 --- a/Dalamud/Plugin/Services/ITextureSubstitutionProvider.cs +++ b/Dalamud/Plugin/Services/ITextureSubstitutionProvider.cs @@ -1,11 +1,11 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Dalamud.Plugin.Services; /// /// Service that grants you the ability to replace texture data that is to be loaded by Dalamud. /// -public interface ITextureSubstitutionProvider +public interface ITextureSubstitutionProvider : IDalamudService { /// /// Delegate describing a function that may be used to intercept and replace texture data. diff --git a/Dalamud/Plugin/Services/ITitleScreenMenu.cs b/Dalamud/Plugin/Services/ITitleScreenMenu.cs index 9f7b17ea3..50bae62a1 100644 --- a/Dalamud/Plugin/Services/ITitleScreenMenu.cs +++ b/Dalamud/Plugin/Services/ITitleScreenMenu.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Interface; using Dalamud.Interface.Textures; @@ -8,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// /// Interface for class responsible for managing elements in the title screen menu. /// -public interface ITitleScreenMenu +public interface ITitleScreenMenu : IDalamudService { /// /// Gets the list of read only entries in the title screen menu. diff --git a/Dalamud/Plugin/Services/IToastGui.cs b/Dalamud/Plugin/Services/IToastGui.cs index ef83e95ac..c472cbb1f 100644 --- a/Dalamud/Plugin/Services/IToastGui.cs +++ b/Dalamud/Plugin/Services/IToastGui.cs @@ -1,4 +1,4 @@ -using Dalamud.Game.Gui.Toast; +using Dalamud.Game.Gui.Toast; using Dalamud.Game.Text.SeStringHandling; namespace Dalamud.Plugin.Services; @@ -6,7 +6,7 @@ namespace Dalamud.Plugin.Services; /// /// This class facilitates interacting with and creating native toast windows. /// -public interface IToastGui +public interface IToastGui : IDalamudService { /// /// A delegate type used when a normal toast window appears. From 46dee9a483fd886b453be35a3cb182823ecc7e60 Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Sun, 19 Oct 2025 22:54:34 +0200 Subject: [PATCH 12/18] Inherit documentation in DalamudPluginInterface --- Dalamud/Plugin/DalamudPluginInterface.cs | 196 ++++++----------------- 1 file changed, 53 insertions(+), 143 deletions(-) diff --git a/Dalamud/Plugin/DalamudPluginInterface.cs b/Dalamud/Plugin/DalamudPluginInterface.cs index 603bed7a5..6fd9064b6 100644 --- a/Dalamud/Plugin/DalamudPluginInterface.cs +++ b/Dalamud/Plugin/DalamudPluginInterface.cs @@ -86,126 +86,73 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa configuration.DalamudConfigurationSaved += this.OnDalamudConfigurationSaved; } - /// - /// Event that gets fired when loc is changed - /// + /// public event IDalamudPluginInterface.LanguageChangedDelegate? LanguageChanged; - /// - /// Event that is fired when the active list of plugins is changed. - /// + /// public event IDalamudPluginInterface.ActivePluginsChangedDelegate? ActivePluginsChanged; - /// - /// Gets the reason this plugin was loaded. - /// + /// public PluginLoadReason Reason { get; } - /// - /// Gets a value indicating whether auto-updates have already completed this session. - /// + /// public bool IsAutoUpdateComplete => Service.GetNullable()?.IsAutoUpdateComplete ?? false; - /// - /// Gets the repository from which this plugin was installed. - /// - /// If a plugin was installed from the official/main repository, this will return the value of - /// . Developer plugins will return the value of - /// . - /// + /// public string SourceRepository { get; } - /// - /// Gets the current internal plugin name. - /// + /// public string InternalName => this.plugin.InternalName; - /// - /// Gets the plugin's manifest. - /// + /// public IPluginManifest Manifest => this.plugin.Manifest; - /// - /// Gets a value indicating whether this is a dev plugin. - /// + /// public bool IsDev => this.plugin.IsDev; - /// - /// Gets a value indicating whether this is a testing release of a plugin. - /// - /// - /// Dev plugins have undefined behavior for this value, but can be expected to return false. - /// + /// public bool IsTesting { get; } - /// - /// Gets the time that this plugin was loaded. - /// + /// public DateTime LoadTime { get; } - /// - /// Gets the UTC time that this plugin was loaded. - /// + /// public DateTime LoadTimeUTC { get; } - /// - /// Gets the timespan delta from when this plugin was loaded. - /// + /// public TimeSpan LoadTimeDelta => DateTime.Now - this.LoadTime; - /// - /// Gets the directory Dalamud assets are stored in. - /// + /// public DirectoryInfo DalamudAssetDirectory => Service.Get().AssetDirectory; - /// - /// Gets the location of your plugin assembly. - /// + /// public FileInfo AssemblyLocation => this.plugin.DllFile; - /// - /// Gets the directory your plugin configurations are stored in. - /// + /// public DirectoryInfo ConfigDirectory => new(this.GetPluginConfigDirectory()); - /// - /// Gets the config file of your plugin. - /// + /// public FileInfo ConfigFile => this.configs.GetConfigFile(this.plugin.InternalName); - /// - /// Gets the instance which allows you to draw UI into the game via ImGui draw calls. - /// + /// public IUiBuilder UiBuilder { get; private set; } - /// - /// Gets a value indicating whether Dalamud is running in Debug mode or the /xldev menu is open. This can occur on release builds. - /// + /// public bool IsDevMenuOpen => Service.GetNullable() is { IsDevMenuOpen: true }; // Can be null during boot - /// - /// Gets a value indicating whether a debugger is attached. - /// + /// public bool IsDebugging => Debugger.IsAttached; - /// - /// Gets the current UI language in two-letter iso format. - /// + /// public string UiLanguage { get; private set; } - /// - /// Gets serializer class with functions to remove special characters from strings. - /// + /// public ISanitizer Sanitizer { get; } - /// - /// Gets the chat type used by default for plugin messages. - /// + /// public XivChatType GeneralChatType { get; private set; } - /// - /// Gets a list of installed plugins along with their current state. - /// + /// public IEnumerable InstalledPlugins => Service.Get().InstalledPlugins.Select(p => new ExposedPlugin(p)); @@ -214,12 +161,7 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa /// internal UiBuilder LocalUiBuilder => this.uiBuilder; - /// - /// Opens the , with an optional search term. - /// - /// The page to open the installer to. Defaults to the "All Plugins" page. - /// An optional search text to input in the search box. - /// Returns false if the DalamudInterface was null. + /// public bool OpenPluginInstallerTo(PluginInstallerOpenKind openTo = PluginInstallerOpenKind.AllPlugins, string? searchText = null) { var dalamudInterface = Service.GetNullable(); // Can be null during boot @@ -234,12 +176,7 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa return true; } - /// - /// Opens the , with an optional search term. - /// - /// The tab to open the settings to. Defaults to the "General" tab. - /// An optional search text to input in the search box. - /// Returns false if the DalamudInterface was null. + /// public bool OpenDalamudSettingsTo(SettingsOpenKind openTo = SettingsOpenKind.General, string? searchText = null) { var dalamudInterface = Service.GetNullable(); // Can be null during boot @@ -254,10 +191,7 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa return true; } - /// - /// Opens the dev menu bar. - /// - /// Returns false if the DalamudInterface was null. + /// public bool OpenDeveloperMenu() { var dalamudInterface = Service.GetNullable(); // Can be null during boot @@ -296,102 +230,91 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa #region IPC - /// + /// public T GetOrCreateData(string tag, Func dataGenerator) where T : class => Service.Get().GetOrCreateData(tag, dataGenerator); - /// + /// public void RelinquishData(string tag) => Service.Get().RelinquishData(tag); - /// + /// public bool TryGetData(string tag, [NotNullWhen(true)] out T? data) where T : class => Service.Get().TryGetData(tag, out data); - /// + /// public T? GetData(string tag) where T : class => Service.Get().GetData(tag); - /// - /// Gets an IPC provider. - /// - /// The return type for funcs. Use object if this is unused. - /// The name of the IPC registration. - /// An IPC provider. - /// This is thrown when the requested types do not match the previously registered types are different. + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// - /// Gets an IPC subscriber. - /// - /// The return type for funcs. Use object if this is unused. - /// The name of the IPC registration. - /// An IPC subscriber. + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); - /// + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); - /// + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); - /// + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); - /// + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); - /// + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); - /// + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); - /// + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); - /// + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); @@ -399,10 +322,7 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa #region Configuration - /// - /// Save a plugin configuration(inheriting IPluginConfiguration). - /// - /// The current configuration. + /// public void SavePluginConfig(IPluginConfiguration? currentConfig) { if (currentConfig == null) @@ -411,10 +331,7 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa this.configs.Save(currentConfig, this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId); } - /// - /// Get a previously saved plugin configuration or null if none was saved before. - /// - /// A previously saved config or null if none was saved before. + /// public IPluginConfiguration? GetPluginConfig() { // This is done to support json deserialization of plugin configurations @@ -438,16 +355,10 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa return this.configs.Load(this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId); } - /// - /// Get the config directory. - /// - /// directory with path of AppData/XIVLauncher/pluginConfig/PluginInternalName. + /// public string GetPluginConfigDirectory() => this.configs.GetDirectory(this.plugin.InternalName); - /// - /// Get the loc directory. - /// - /// directory with path of AppData/XIVLauncher/pluginConfig/PluginInternalName/loc. + /// public string GetPluginLocDirectory() => this.configs.GetDirectory(Path.Combine(this.plugin.InternalName, "loc")); #endregion @@ -508,8 +419,7 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa #endregion - /// Unregister the plugin and dispose all references. - /// Dalamud internal use only. + /// public void Dispose() { Service.Get().RemoveChatLinkHandler(this.plugin.InternalName); From 9001c969864f16bddd0a302ee4e39d9378585f2d Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Sun, 19 Oct 2025 23:30:25 +0200 Subject: [PATCH 13/18] Return the resulting service, not the Task --- Dalamud/IoC/Internal/ServiceContainer.cs | 2 +- Dalamud/IoC/Internal/ServiceScope.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dalamud/IoC/Internal/ServiceContainer.cs b/Dalamud/IoC/Internal/ServiceContainer.cs index 31d16e02e..6083f5a73 100644 --- a/Dalamud/IoC/Internal/ServiceContainer.cs +++ b/Dalamud/IoC/Internal/ServiceContainer.cs @@ -161,7 +161,7 @@ internal class ServiceContainer : IServiceProvider, IServiceType public IServiceScope GetScope() => new ServiceScopeImpl(this); /// - object? IServiceProvider.GetService(Type serviceType) => this.GetSingletonService(serviceType); + public object? GetService(Type serviceType) => this.GetSingletonService(serviceType).Result; private async Task GetService(Type serviceType, ServiceScopeImpl? scope, object[] scopedObjects) { diff --git a/Dalamud/IoC/Internal/ServiceScope.cs b/Dalamud/IoC/Internal/ServiceScope.cs index 8b12dce0b..70d409b58 100644 --- a/Dalamud/IoC/Internal/ServiceScope.cs +++ b/Dalamud/IoC/Internal/ServiceScope.cs @@ -58,9 +58,9 @@ internal class ServiceScopeImpl : IServiceScope public ServiceScopeImpl(ServiceContainer container) => this.container = container; /// - object? IServiceProvider.GetService(Type serviceType) + public object? GetService(Type serviceType) { - return ((IServiceProvider)this.container).GetService(serviceType); + return this.container.GetService(serviceType); } /// From 9ea417c9ef75b94f3b2dbb9ffb8c905cfd9ec29e Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Mon, 20 Oct 2025 10:45:02 +0200 Subject: [PATCH 14/18] Fix GetService getting stuck in Task --- Dalamud/IoC/Internal/ServiceContainer.cs | 21 ++++++++++++++++----- Dalamud/IoC/Internal/ServiceScope.cs | 2 +- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Dalamud/IoC/Internal/ServiceContainer.cs b/Dalamud/IoC/Internal/ServiceContainer.cs index 6083f5a73..6383b6b11 100644 --- a/Dalamud/IoC/Internal/ServiceContainer.cs +++ b/Dalamud/IoC/Internal/ServiceContainer.cs @@ -18,7 +18,7 @@ namespace Dalamud.IoC.Internal; /// Dalamud services are constructed via Service{T}.ConstructObject at the moment. /// [ServiceManager.ProvidedService] -internal class ServiceContainer : IServiceProvider, IServiceType +internal class ServiceContainer : IServiceType { private static readonly ModuleLog Log = new("SERVICECONTAINER"); @@ -160,10 +160,21 @@ internal class ServiceContainer : IServiceProvider, IServiceType /// An implementation of a service scope. public IServiceScope GetScope() => new ServiceScopeImpl(this); - /// - public object? GetService(Type serviceType) => this.GetSingletonService(serviceType).Result; - - private async Task GetService(Type serviceType, ServiceScopeImpl? scope, object[] scopedObjects) + /// + /// Resolves and returns an instance of the specified service type, using either singleton or scoped lifetime as + /// appropriate. + /// + /// The type of the service to resolve. This must be a concrete or interface type registered with the service + /// manager. + /// The scope within which to create scoped services. Required if the requested service type is registered as + /// scoped; otherwise, can be null. + /// An array of objects available for scoped resolution. Used to locate or create scoped service instances when + /// applicable. + /// An instance of the requested service type. Returns a singleton instance if available, a scoped instance if + /// required, or an object from the provided scoped objects if it matches the service type. + /// Thrown if a scoped service is requested but no scope is provided, or if the requested service type cannot be + /// resolved from the scoped objects. + public async Task GetService(Type serviceType, ServiceScopeImpl? scope, object[] scopedObjects) { if (this.interfaceToTypeMap.TryGetValue(serviceType, out var implementingType)) serviceType = implementingType; diff --git a/Dalamud/IoC/Internal/ServiceScope.cs b/Dalamud/IoC/Internal/ServiceScope.cs index 70d409b58..c0c4e0b08 100644 --- a/Dalamud/IoC/Internal/ServiceScope.cs +++ b/Dalamud/IoC/Internal/ServiceScope.cs @@ -60,7 +60,7 @@ internal class ServiceScopeImpl : IServiceScope /// public object? GetService(Type serviceType) { - return this.container.GetService(serviceType); + return this.container.GetService(serviceType, this, []).ConfigureAwait(false).GetAwaiter().GetResult(); } /// From 53a082e68dad0e97d96a6239f2386548c06f3a9d Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Mon, 20 Oct 2025 12:10:59 +0200 Subject: [PATCH 15/18] Check that PluginInterfaces implement IDalamudService --- Dalamud/Service/ServiceManager.cs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Dalamud/Service/ServiceManager.cs b/Dalamud/Service/ServiceManager.cs index 9847f7147..88c6366fd 100644 --- a/Dalamud/Service/ServiceManager.cs +++ b/Dalamud/Service/ServiceManager.cs @@ -9,11 +9,14 @@ using System.Threading.Tasks; using Dalamud.Configuration.Internal; using Dalamud.Game; +using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Logging.Internal; +using Dalamud.Plugin.Services; using Dalamud.Storage; using Dalamud.Utility; using Dalamud.Utility.Timing; + using JetBrains.Annotations; // API10 TODO: Move to Dalamud.Service namespace. Some plugins reflect this... including my own, oops. There's a todo @@ -541,9 +544,11 @@ internal static class ServiceManager if (attr == null) return ServiceKind.None; - Debug.Assert( - type.IsAssignableTo(typeof(IServiceType)), - "Service did not inherit from IServiceType"); + if (!type.IsAssignableTo(typeof(IServiceType))) + { + Log.Error($"Service {type.Name} did not inherit from IServiceType"); + Debug.Fail("Service did not inherit from IServiceType"); + } if (attr.IsAssignableTo(typeof(BlockingEarlyLoadedServiceAttribute))) return ServiceKind.BlockingEarlyLoadedService; @@ -552,7 +557,16 @@ internal static class ServiceManager return ServiceKind.EarlyLoadedService; if (attr.IsAssignableTo(typeof(ScopedServiceAttribute))) + { + if (type.GetCustomAttribute() != null + && !type.IsAssignableTo(typeof(IDalamudService))) + { + Log.Error($"Plugin-scoped service {type.Name} must inherit from IDalamudService"); + Debug.Fail("Plugin-scoped service must inherit from IDalamudService"); + } + return ServiceKind.ScopedService; + } return ServiceKind.ProvidedService; } From f4c9c16c68a0664966e9624c34ca7ede41d2205b Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Mon, 17 Nov 2025 19:47:36 +0100 Subject: [PATCH 16/18] Mark IUnlockState with IDalamudService --- Dalamud/Plugin/Services/IUnlockState.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Plugin/Services/IUnlockState.cs b/Dalamud/Plugin/Services/IUnlockState.cs index 00f2df190..a0d733f55 100644 --- a/Dalamud/Plugin/Services/IUnlockState.cs +++ b/Dalamud/Plugin/Services/IUnlockState.cs @@ -11,7 +11,7 @@ namespace Dalamud.Plugin.Services; /// Interface for determining unlock state of various content in the game. /// [Experimental("UnlockState")] -public interface IUnlockState +public interface IUnlockState : IDalamudService { /// /// A delegate type used for the event. From 1bdad092ca1ecb1e4d5640ab8daace0f6fcd1f5f Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Mon, 17 Nov 2025 20:28:52 +0100 Subject: [PATCH 17/18] Mark IPlayerState with IDalamudService --- Dalamud/Plugin/Services/IPlayerState.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Plugin/Services/IPlayerState.cs b/Dalamud/Plugin/Services/IPlayerState.cs index 425ffc963..1416dfb77 100644 --- a/Dalamud/Plugin/Services/IPlayerState.cs +++ b/Dalamud/Plugin/Services/IPlayerState.cs @@ -12,7 +12,7 @@ namespace Dalamud.Plugin.Services; /// /// Interface for determining the players state. /// -public interface IPlayerState +public interface IPlayerState : IDalamudService { /// /// Gets a value indicating whether the local players data is loaded. From cb441631e1c51941b5b0d6a1e5f27c37ad4879ca Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Mon, 17 Nov 2025 20:58:14 +0100 Subject: [PATCH 18/18] Add empty ServiceConstructor to PlayerState --- Dalamud/Game/Player/PlayerState.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Dalamud/Game/Player/PlayerState.cs b/Dalamud/Game/Player/PlayerState.cs index 316b09e2f..917c946db 100644 --- a/Dalamud/Game/Player/PlayerState.cs +++ b/Dalamud/Game/Player/PlayerState.cs @@ -23,6 +23,11 @@ namespace Dalamud.Game.Player; [ResolveVia] internal unsafe class PlayerState : IServiceType, IPlayerState { + [ServiceManager.ServiceConstructor] + private PlayerState() + { + } + /// public bool IsLoaded => CSPlayerState.Instance()->IsLoaded;