-
Notifications
You must be signed in to change notification settings - Fork 256
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c21e525
commit 967a9fc
Showing
7 changed files
with
411 additions
and
0 deletions.
There are no files selected for viewing
27 changes: 27 additions & 0 deletions
27
Dalamud/Game/Gui/Nameplates/EventArgs/NameplateUpdateEventArgs.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using Dalamud.Game.Gui.Nameplates.Model; | ||
|
||
namespace Dalamud.Game.Gui.Nameplates.EventArgs; | ||
|
||
/// <summary> | ||
/// Event arguments for <see cref="INameplatesGui.OnNameplateUpdate"/>. | ||
/// </summary> | ||
/// <param name="nameplateInfo">dddd.</param> | ||
/// <param name="nameplateObject">dedd.</param> | ||
public class NameplateUpdateEventArgs(NameplateInfo nameplateInfo, NameplateObject nameplateObject) | ||
{ | ||
/// <summary> | ||
/// Gets an object that represents the nameplate object (mostly like the player or NPC). | ||
/// </summary> | ||
public NameplateObject NameplateObject { get; } = nameplateObject; | ||
|
||
/// <summary> | ||
/// Gets an object that holds some infos about the nameplate. | ||
/// </summary> | ||
public NameplateInfo NameplateInfo { get; } = nameplateInfo; | ||
|
||
/// <summary> | ||
/// Gets or sets a value indicating whether <see cref="NameplateInfo"/> has been changed. | ||
/// <br/>Set this to <see cref="T:true"/> if you changes to <see cref="NameplateInfo"/> should take affect. | ||
/// </summary> | ||
public bool HasChanged { get; set; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
using Dalamud.Game.Text.SeStringHandling; | ||
|
||
namespace Dalamud.Game.Gui.Nameplates.Model; | ||
|
||
/// <summary> | ||
/// Represents a nameplate by providing some information about the visual look. | ||
/// </summary> | ||
public class NameplateInfo | ||
{ | ||
/// <summary> | ||
/// Gets or sets the title text of the Nameplate. | ||
/// </summary> | ||
public SeString Title { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the name text of the Nameplate. | ||
/// </summary> | ||
public SeString Name { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the free company text. | ||
/// </summary> | ||
public SeString FreeCompany { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the prefix text. Mostly used for the job name shortcuts or some status icons that can be shown within a text. | ||
/// </summary> | ||
public SeString Prefix { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets a value indicating whether the title is shown above the name. | ||
/// <br/>If the value is true, the title is shown above the name. Otherwise the title is shown below the name. | ||
/// </summary> | ||
public bool IsTitleAboveName { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets a value indicating whether the title is visible. | ||
/// <br/>If the title value is true, the title is visible. Otherwise the title text is not visible and ignored. | ||
/// </summary> | ||
public bool IsTitleVisible { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the icon shown on the nameplate. Mostly used for the status icon. | ||
/// </summary> | ||
public StatusIcons IconID { get; set; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
namespace Dalamud.Game.Gui.Nameplates.Model; | ||
|
||
/// <summary> | ||
/// Represents a nameplate object (mostly like player or NPC). | ||
/// </summary> | ||
/// <param name="pointer">The pointer for the nameplate object.</param> | ||
public class NameplateObject(IntPtr pointer) | ||
{ | ||
/// <summary> | ||
/// Gets the pointer for the nameplate object. | ||
/// </summary> | ||
public IntPtr Pointer { get; } = pointer; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
using Newtonsoft.Json; | ||
using Newtonsoft.Json.Converters; | ||
|
||
namespace Dalamud.Game.Gui.Nameplates.Model; | ||
|
||
/// <summary> | ||
/// Possible status icons that are able to show as icon on the Nameplate. | ||
/// </summary> | ||
[JsonConverter(typeof(StringEnumConverter))] | ||
public enum StatusIcons | ||
{ | ||
/// <summary> | ||
/// The status for a player that is disconnecting. | ||
/// </summary> | ||
Disconnecting = 061503, | ||
|
||
/// <summary> | ||
/// The status for a player that is in a duty. | ||
/// </summary> | ||
InDuty = 061506, | ||
|
||
/// <summary> | ||
/// The status for a player that is viewing a cutscene. | ||
/// </summary> | ||
ViewingCutscene = 061508, | ||
|
||
/// <summary> | ||
/// The status for a player that is marked as busy. | ||
/// </summary> | ||
Busy = 061509, | ||
|
||
/// <summary> | ||
/// The status for a player that is afk. | ||
/// </summary> | ||
Idle = 061511, | ||
|
||
/// <summary> | ||
/// The status for a player that is registred for a duty and searching a group. | ||
/// </summary> | ||
DutyFinder = 061517, | ||
|
||
/// <summary> | ||
/// The status for a player that is leader of a party. | ||
/// </summary> | ||
PartyLeader = 061521, | ||
|
||
/// <summary> | ||
/// The status for a player that is member of a party. | ||
/// </summary> | ||
PartyMember = 061522, | ||
|
||
/// <summary> | ||
/// The status for a player that is marked as role-play. | ||
/// </summary> | ||
RolePlaying = 061545, | ||
|
||
/// <summary> | ||
/// The status for a player that makes nice photos. | ||
/// </summary> | ||
GroupPose = 061546, | ||
|
||
/// <summary> | ||
/// The status for new players. | ||
/// </summary> | ||
NewAdventurer = 061523, | ||
|
||
/// <summary> | ||
/// The status for mentors. | ||
/// </summary> | ||
Mentor = 061540, | ||
|
||
/// <summary> | ||
/// The status for PvE mentors. | ||
/// </summary> | ||
MentorPvE = 061542, | ||
|
||
/// <summary> | ||
/// The status for crafting mentors. | ||
/// </summary> | ||
MentorCrafting = 061543, | ||
|
||
/// <summary> | ||
/// The status for PvP mentors. | ||
/// </summary> | ||
MentorPvP = 061544, | ||
|
||
/// <summary> | ||
/// The status for a player that recently took a break playing FFXIV. | ||
/// </summary> | ||
Returner = 061547, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
using System.Linq; | ||
using System.Runtime.InteropServices; | ||
|
||
using Dalamud.Game.ClientState.Objects; | ||
using Dalamud.Game.ClientState.Objects.Types; | ||
using Dalamud.Game.Gui.Nameplates.EventArgs; | ||
using Dalamud.Game.Gui.Nameplates.Model; | ||
using Dalamud.Game.Text.SeStringHandling; | ||
using Dalamud.Hooking; | ||
using Dalamud.IoC.Internal; | ||
using Dalamud.Memory; | ||
|
||
using FFXIVClientStructs.FFXIV.Client.UI; | ||
|
||
namespace Dalamud.Game.Gui.Nameplates; | ||
|
||
/// <summary> | ||
/// This class handles interacting with native Nameplate update events and management. | ||
/// </summary> | ||
[InterfaceVersion("1.0")] | ||
[ServiceManager.EarlyLoadedService] | ||
internal class NameplateGui : IInternalDisposableService, INameplatesGui | ||
{ | ||
private readonly GameGui gameGui; | ||
private readonly ObjectTable objectTable; | ||
|
||
private readonly NameplateGuiAddressResolver addresses; | ||
private readonly IntPtr namePlatePtr; | ||
|
||
private Hook<SetPlayerNameplateDetourDelegate>? setPlayerNameplateDetourHook = null; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="NameplateGui"/> class. | ||
/// </summary> | ||
[ServiceManager.ServiceConstructor] | ||
private NameplateGui(TargetSigScanner sigScanner) | ||
{ | ||
// Services | ||
this.gameGui = Service<GameGui>.Get(); | ||
this.objectTable = Service<ObjectTable>.Get(); | ||
|
||
// Pointers | ||
this.namePlatePtr = this.gameGui.GetAddonByName("NamePlate", 1); | ||
|
||
// Address resolver | ||
this.addresses = new(); | ||
this.addresses.Setup(sigScanner); | ||
|
||
// Hooks | ||
this.setPlayerNameplateDetourHook = Hook<SetPlayerNameplateDetourDelegate>.FromAddress(this.addresses.SetPlayerNameplateDetour, this.HandleSetPlayerNameplateDetour); | ||
this.setPlayerNameplateDetourHook.Enable(); | ||
} | ||
|
||
private unsafe delegate IntPtr SetPlayerNameplateDetourDelegate(IntPtr playerNameplateObjectPtr, bool isTitleAboveName, bool isTitleVisible, IntPtr titlePtr, IntPtr namePtr, IntPtr freeCompanyPtr, IntPtr prefix, int iconId); | ||
|
||
/// <inheritdoc/> | ||
public event INameplatesGui.OnNameplateUpdateDelegate OnNameplateUpdate; | ||
|
||
/// <inheritdoc/> | ||
public void DisposeService() | ||
{ | ||
this.setPlayerNameplateDetourHook.Dispose(); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public T? GetNameplateGameObject<T>(NameplateObject nameplateObject) where T : GameObject | ||
{ | ||
return this.GetNameplateGameObject<T>(nameplateObject.Pointer); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public T? GetNameplateGameObject<T>(IntPtr nameplateObjectPtr) where T : GameObject | ||
{ | ||
// Get the nameplate object array | ||
var nameplateAddonPtr = this.gameGui.GetAddonByName("NamePlate", 1); | ||
var nameplateObjectArrayPtrPtr = nameplateAddonPtr + Marshal.OffsetOf(typeof(AddonNamePlate), nameof(AddonNamePlate.NamePlateObjectArray)).ToInt32(); | ||
var nameplateObjectArrayPtr = Marshal.ReadIntPtr(nameplateObjectArrayPtrPtr); | ||
|
||
if (nameplateObjectArrayPtr == IntPtr.Zero) | ||
return null; | ||
|
||
// Determine the index of the nameplate object within the nameplate object array | ||
var namePlateObjectSize = Marshal.SizeOf(typeof(AddonNamePlate.NamePlateObject)); | ||
var namePlateObjectPtr0 = nameplateObjectArrayPtr + namePlateObjectSize * 0; | ||
var namePlateIndex = (nameplateObjectPtr.ToInt64() - namePlateObjectPtr0.ToInt64()) / namePlateObjectSize; | ||
|
||
if (namePlateIndex < 0 || namePlateIndex >= AddonNamePlate.NumNamePlateObjects) | ||
return null; | ||
|
||
// Get the nameplate info array | ||
var nameplateInfoArrayPtr = IntPtr.Zero; | ||
unsafe | ||
{ | ||
var framework = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework.Instance(); | ||
nameplateInfoArrayPtr = new IntPtr(&framework->GetUiModule()->GetRaptureAtkModule()->NamePlateInfoArray); | ||
} | ||
|
||
// Get the nameplate info for the nameplate object | ||
var namePlateInfoPtr = new IntPtr(nameplateInfoArrayPtr.ToInt64() + Marshal.SizeOf(typeof(RaptureAtkModule.NamePlateInfo)) * namePlateIndex); | ||
var namePlateInfo = Marshal.PtrToStructure<RaptureAtkModule.NamePlateInfo>(namePlateInfoPtr); | ||
|
||
// Return the object for its object id | ||
var objectId = namePlateInfo.ObjectID.ObjectID; | ||
return this.objectTable.SearchById(objectId) as T; | ||
} | ||
|
||
private IntPtr HandleSetPlayerNameplateDetour(IntPtr playerNameplateObjectPtr, bool isTitleAboveName, bool isTitleVisible, IntPtr titlePtr, IntPtr namePtr, IntPtr freeCompanyPtr, IntPtr prefixPtr, int iconId) | ||
{ | ||
|
||
if (this.OnNameplateUpdate != null) | ||
{ | ||
// Create NamePlateObject if possible | ||
var namePlateObj = new NameplateObject(playerNameplateObjectPtr); | ||
|
||
// Create new event | ||
var nameplateInfo = new NameplateInfo | ||
{ | ||
Title = MemoryHelper.ReadSeStringNullTerminated(titlePtr), | ||
Name = MemoryHelper.ReadSeStringNullTerminated(namePtr), | ||
FreeCompany = MemoryHelper.ReadSeStringNullTerminated(freeCompanyPtr), | ||
Prefix = MemoryHelper.ReadSeStringNullTerminated(prefixPtr), | ||
IsTitleAboveName = isTitleAboveName, | ||
IsTitleVisible = isTitleVisible, | ||
IconID = (StatusIcons)iconId, | ||
}; | ||
|
||
// Invoke event | ||
var eventArgs = new NameplateUpdateEventArgs(nameplateInfo, namePlateObj); | ||
this.OnNameplateUpdate.Invoke(eventArgs); | ||
|
||
if (eventArgs.HasChanged) | ||
{ | ||
// Get new states | ||
isTitleAboveName = nameplateInfo.IsTitleAboveName; | ||
isTitleVisible = nameplateInfo.IsTitleVisible; | ||
iconId = (int)nameplateInfo.IconID; | ||
|
||
// Get new Title string content | ||
var titleRaw = nameplateInfo.Title.Encode(); | ||
var titleNewRaw = nameplateInfo.Title.Encode(); | ||
if (!titleRaw.SequenceEqual(titleNewRaw)) | ||
MemoryHelper.WriteSeString(titlePtr, nameplateInfo.Title); | ||
|
||
// Get new Name string content | ||
var nameRaw = nameplateInfo.Name.Encode(); | ||
var nameNewRaw = nameplateInfo.Name.Encode(); | ||
if (!nameRaw.SequenceEqual(nameNewRaw)) | ||
MemoryHelper.WriteSeString(namePtr, nameplateInfo.Name); | ||
|
||
// Get new Free Company string content | ||
var freeCompanyRaw = nameplateInfo.FreeCompany.Encode(); | ||
var freeCompanyNewRaw = nameplateInfo.FreeCompany.Encode(); | ||
if (!freeCompanyRaw.SequenceEqual(freeCompanyNewRaw)) | ||
MemoryHelper.WriteSeString(freeCompanyPtr, nameplateInfo.FreeCompany); | ||
|
||
// Get new Prefix string content | ||
var prefixRaw = nameplateInfo.Prefix.Encode(); | ||
var prefixNewRaw = nameplateInfo.Prefix.Encode(); | ||
if (!prefixRaw.SequenceEqual(prefixNewRaw)) | ||
MemoryHelper.WriteSeString(prefixPtr, nameplateInfo.Prefix); | ||
} | ||
} | ||
|
||
// Call original | ||
var result = this.setPlayerNameplateDetourHook.Original( | ||
playerNameplateObjectPtr, | ||
isTitleAboveName, | ||
isTitleVisible, | ||
titlePtr, | ||
namePtr, | ||
freeCompanyPtr, | ||
prefixPtr, | ||
iconId); | ||
|
||
// Return result | ||
return result; | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
Dalamud/Game/Gui/Nameplates/NameplateGuiAddressResolver.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
namespace Dalamud.Game.Gui.Nameplates; | ||
|
||
/// <summary> | ||
/// The address resolver for the <see cref="NameplateGui"/> class. | ||
/// </summary> | ||
internal class NameplateGuiAddressResolver : BaseAddressResolver | ||
{ | ||
/// <summary> | ||
/// Gets the address of the native SetPlayerNameplateDetour method. | ||
/// </summary> | ||
public IntPtr SetPlayerNameplateDetour { get; private set; } | ||
|
||
/// <inheritdoc/> | ||
protected override void SetupInternal(ISigScanner scanner) | ||
{ | ||
this.SetPlayerNameplateDetour = scanner.ScanText("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B 5C 24 ?? 45 38 BE"); | ||
} | ||
} |
Oops, something went wrong.