diff --git a/CustomizePlus/Api/CustomizePlusIpc.General.cs b/CustomizePlus/Api/CustomizePlusIpc.General.cs
index 7e027d0..50e386b 100644
--- a/CustomizePlus/Api/CustomizePlusIpc.General.cs
+++ b/CustomizePlus/Api/CustomizePlusIpc.General.cs
@@ -4,7 +4,7 @@ namespace CustomizePlus.Api;
public partial class CustomizePlusIpc
{
- private readonly (int Breaking, int Feature) _apiVersion = (4, 0);
+ private readonly (int Breaking, int Feature) _apiVersion = (5, 0);
///
/// When there are breaking changes the first number is bumped up and second one is reset.
diff --git a/CustomizePlus/Api/CustomizePlusIpc.Profile.cs b/CustomizePlus/Api/CustomizePlusIpc.Profile.cs
index 765e252..5cb1556 100644
--- a/CustomizePlus/Api/CustomizePlusIpc.Profile.cs
+++ b/CustomizePlus/Api/CustomizePlusIpc.Profile.cs
@@ -30,7 +30,7 @@ public partial class CustomizePlusIpc
/// /!\ If no profile is set on specified character profile id will be equal to Guid.Empty
///
[EzIPCEvent("Profile.OnUpdate")]
- private Action OnProfileUpdate;
+ private Action OnProfileUpdate;
///
/// Retrieve list of all user profiles
@@ -125,12 +125,14 @@ private ErrorCode SetProfileStateInternal(Guid uniqueId, bool state)
/// Get unique id of currently active profile for character.
///
[EzIPC("Profile.GetActiveProfileIdOnCharacter")]
- private (int, Guid?) GetActiveProfileIdOnCharacter(ICharacter character)
+ private (int, Guid?) GetActiveProfileIdOnCharacter(ushort gameObjectIndex)
{
- if (character == null)
+ var actor = _gameObjectService.GetActorByObjectIndex(gameObjectIndex);
+
+ if (actor == null || !actor.Value.Valid || !actor.Value.IsCharacter)
return ((int)ErrorCode.InvalidCharacter, null);
- var profile = _profileManager.GetProfileByCharacterName(character.Name.ToString(), true);
+ var profile = _profileManager.GetProfileByCharacterName(actor.Value.Utf8Name.ToString(), true);
if (profile == null)
return ((int)ErrorCode.ProfileNotFound, null);
@@ -143,14 +145,12 @@ private ErrorCode SetProfileStateInternal(Guid uniqueId, bool state)
/// Returns profile's unique id which can be used to manipulate it at a later date.
///
[EzIPC("Profile.SetTemporaryProfileOnCharacter")]
- private (int, Guid?) SetTemporaryProfileOnCharacter(ICharacter character, string profileJson)
+ private (int, Guid?) SetTemporaryProfileOnCharacter(ushort gameObjectIndex, string profileJson)
{
- //todo: do not allow to set temporary profile on reserved actors (examine, etc)
- if (character == null)
- return ((int)ErrorCode.InvalidCharacter, null);
+ var actor = _gameObjectService.GetActorByObjectIndex(gameObjectIndex);
- var actor = (Actor)character.Address;
- if (!actor.Valid)
+ //todo: do not allow to set temporary profile on reserved actors (examine, etc)
+ if (actor == null || !actor.Value.Valid || !actor.Value.IsCharacter)
return ((int)ErrorCode.InvalidCharacter, null);
try
@@ -162,26 +162,26 @@ private ErrorCode SetProfileStateInternal(Guid uniqueId, bool state)
}
catch (Exception ex)
{
- _logger.Error($"IPCCharacterProfile deserialization issue. Character: {character?.Name.ToString().Incognify()}, exception: {ex}.");
+ _logger.Error($"IPCCharacterProfile deserialization issue. Character: {actor.Value.Utf8Name.ToString().Incognify()}, exception: {ex}.");
return ((int)ErrorCode.CorruptedProfile, null);
}
if (profile == null)
{
- _logger.Error($"IPCCharacterProfile is null after deserialization. Character: {character?.Name.ToString().Incognify()}.");
+ _logger.Error($"IPCCharacterProfile is null after deserialization. Character: {actor.Value.Utf8Name.ToString().Incognify()}.");
return ((int)ErrorCode.CorruptedProfile, null);
}
//todo: ideally we'd probably want to make sure ID returned by that function does not have collision with other profiles
var fullProfile = IPCCharacterProfile.ToFullProfile(profile).Item1;
- _profileManager.AddTemporaryProfile(fullProfile, actor);
+ _profileManager.AddTemporaryProfile(fullProfile, actor.Value);
return ((int)ErrorCode.Success, fullProfile.UniqueId);
}
catch (Exception ex)
{
- _logger.Error($"Unable to set temporary profile. Character: {character?.Name.ToString().Incognify()}, exception: {ex}.");
+ _logger.Error($"Unable to set temporary profile. Character: {actor.Value.Utf8Name.ToString().Incognify()}, exception: {ex}.");
return ((int)ErrorCode.UnknownError, null);
}
}
@@ -190,18 +190,16 @@ private ErrorCode SetProfileStateInternal(Guid uniqueId, bool state)
/// Delete temporary profile currently active on character
///
[EzIPC("Profile.DeleteTemporaryProfileOnCharacter")]
- private int DeleteTemporaryProfileOnCharacter(ICharacter character)
+ private int DeleteTemporaryProfileOnCharacter(ushort gameObjectIndex)
{
- if (character == null)
- return (int)ErrorCode.InvalidCharacter;
+ var actor = _gameObjectService.GetActorByObjectIndex(gameObjectIndex);
- var actor = (Actor)character.Address;
- if (!actor.Valid)
+ if (actor == null || !actor.Value.Valid || !actor.Value.IsCharacter)
return (int)ErrorCode.InvalidCharacter;
try
{
- _profileManager.RemoveTemporaryProfile(actor);
+ _profileManager.RemoveTemporaryProfile(actor.Value);
return (int)ErrorCode.Success;
}
catch(ProfileException ex)
@@ -213,13 +211,13 @@ private int DeleteTemporaryProfileOnCharacter(ICharacter character)
case ProfileNotFoundException:
return (int)ErrorCode.ProfileNotFound;
default:
- _logger.Error($"Exception in DeleteTemporaryProfileOnCharacter. Character: {character?.Name.ToString().Incognify()}. Exception: {ex}");
+ _logger.Error($"Exception in DeleteTemporaryProfileOnCharacter. Character: {actor.Value.Utf8Name.ToString().Incognify()}. Exception: {ex}");
return (int)ErrorCode.UnknownError;
}
}
catch(Exception ex)
{
- _logger.Error($"Exception in DeleteTemporaryProfileOnCharacter. Character: {character?.Name.ToString().Incognify()}. Exception: {ex}");
+ _logger.Error($"Exception in DeleteTemporaryProfileOnCharacter. Character: {actor.Value.Utf8Name.ToString().Incognify()}. Exception: {ex}");
return (int)ErrorCode.UnknownError;
}
}
@@ -309,7 +307,7 @@ private void OnArmatureChanged(ArmatureChanged.Type type, Armature armature, obj
if (type == ArmatureChanged.Type.Deleted)
{
//Do not send event if default or editor profile was used
- if (armature.Profile == _profileManager.DefaultProfile || armature.Profile.ProfileType == ProfileType.Editor)
+ if (armature.Profile == _profileManager.DefaultProfile || armature.Profile.ProfileType == ProfileType.Editor) //todo: never send if ProfileType != normal?
return;
OnProfileUpdateInternal(localPlayerCharacter, null);
@@ -327,6 +325,6 @@ private void OnProfileUpdateInternal(ICharacter character, Profile? profile)
_logger.Debug($"Sending player update message: Character: {character.Name.ToString().Incognify()}, Profile: {(profile != null ? profile.ToString() : "no profile")}");
- OnProfileUpdate(character, profile != null ? profile.UniqueId : Guid.Empty);
+ OnProfileUpdate(character.ObjectIndex, profile != null ? profile.UniqueId : Guid.Empty);
}
}
diff --git a/CustomizePlus/Game/Services/GameObjectService.cs b/CustomizePlus/Game/Services/GameObjectService.cs
index 8e15ba5..6fac33c 100644
--- a/CustomizePlus/Game/Services/GameObjectService.cs
+++ b/CustomizePlus/Game/Services/GameObjectService.cs
@@ -7,13 +7,8 @@
using Penumbra.GameData.Interop;
using ObjectManager = CustomizePlus.GameData.Services.ObjectManager;
using DalamudGameObject = Dalamud.Game.ClientState.Objects.Types.IGameObject;
-using ECommons.Configuration;
-using System;
using CustomizePlus.Configuration.Data;
-using FFXIVClientStructs.FFXIV.Client.UI.Agent;
-using Penumbra.GameData;
-using Penumbra.String;
-using Dalamud.Logging;
+using FFXIVClientStructs.FFXIV.Client.Game.Object;
namespace CustomizePlus.Game.Services;
@@ -145,6 +140,19 @@ public Actor GetLocalPlayerActor()
}
}
+ ///
+ /// Find actor in object manager based on its Object ID.
+ ///
+ public Actor? GetActorByObjectIndex(ushort objectIndex)
+ {
+ if (objectIndex < 0 || objectIndex >= _objectManager.TotalCount)
+ return null;
+
+ var ptr = _objectManager[(int)objectIndex];
+
+ return ptr;
+ }
+
public enum SpecialResult
{
PartyBanner,
diff --git a/CustomizePlus/UI/Windows/MainWindow/Tabs/Debug/IPCTestTab.cs b/CustomizePlus/UI/Windows/MainWindow/Tabs/Debug/IPCTestTab.cs
index ba0d248..6bcc7dd 100644
--- a/CustomizePlus/UI/Windows/MainWindow/Tabs/Debug/IPCTestTab.cs
+++ b/CustomizePlus/UI/Windows/MainWindow/Tabs/Debug/IPCTestTab.cs
@@ -50,13 +50,13 @@ public class IPCTestTab //: IDisposable
private readonly Func _disableProfileByUniqueIdIpcFunc;
[EzIPC("Profile.GetActiveProfileIdOnCharacter")]
- private readonly Func _getActiveProfileIdOnCharacterIpcFunc;
+ private readonly Func _getActiveProfileIdOnCharacterIpcFunc;
[EzIPC("Profile.SetTemporaryProfileOnCharacter")]
- private readonly Func _setTemporaryProfileOnCharacterIpcFunc;
+ private readonly Func _setTemporaryProfileOnCharacterIpcFunc;
[EzIPC("Profile.DeleteTemporaryProfileOnCharacter")]
- private readonly Func _deleteTemporaryProfileOnCharacterIpcFunc;
+ private readonly Func _deleteTemporaryProfileOnCharacterIpcFunc;
[EzIPC("Profile.DeleteTemporaryProfileByUniqueId")]
private readonly Func _deleteTemporaryProfileByUniqueIdIpcFunc;
@@ -64,11 +64,6 @@ public class IPCTestTab //: IDisposable
[EzIPC("Profile.GetByUniqueId")]
private readonly Func _getProfileByIdIpcFunc;
- //private readonly ICallGateSubscriber? _setCharacterProfile;
- //private readonly ICallGateSubscriber? _getProfileFromCharacter;
- //private readonly ICallGateSubscriber? _revertCharacter;
- //private readonly ICallGateSubscriber? _onProfileUpdate;
-
private string? _rememberedProfileJson;
private (int, int) _apiVersion;
@@ -103,24 +98,8 @@ public IPCTestTab(
if (_getApiVersionIpcFunc != null)
_apiVersion = _getApiVersionIpcFunc();
-
- //_setCharacterProfile = pluginInterface.GetIpcSubscriber("CustomizePlus.SetProfileToCharacter");
- //_getProfileFromCharacter = pluginInterface.GetIpcSubscriber("CustomizePlus.GetProfileFromCharacter");
- //_revertCharacter = pluginInterface.GetIpcSubscriber("CustomizePlus.RevertCharacter");
- /*_onProfileUpdate = pluginInterface.GetIpcSubscriber("CustomizePlus.OnProfileUpdate");
- _onProfileUpdate.Subscribe(OnProfileUpdate);*/
}
- /* public void Dispose()
- {
- _onProfileUpdate?.Unsubscribe(OnProfileUpdate);
- }
-
- private void OnProfileUpdate(string? characterName, string? profileJson)
- {
- _lastProfileUpdate = DateTime.Now;
- _lastProfileUpdateName = characterName;
- }
- */
+
public unsafe void Draw()
{
_objectManager.Update();
@@ -141,7 +120,6 @@ public unsafe void Draw()
ImGui.Separator();
- //ImGui.Text($"Last profile update: {_lastProfileUpdate}, Character: {_lastProfileUpdateName}");
ImGui.Text($"Memory: {(string.IsNullOrWhiteSpace(_rememberedProfileJson) ? "empty" : "has data")}");
ImGui.Text("Character to operate on:");
@@ -171,7 +149,7 @@ public unsafe void Draw()
if (actors.Count == 0)
return;
- (int result, Guid? uniqueId) = _getActiveProfileIdOnCharacterIpcFunc(FindCharacterByAddress(actors[0].Item2.Address));
+ (int result, Guid? uniqueId) = _getActiveProfileIdOnCharacterIpcFunc(actors[0].Item2.Index.Index);
if(result == 0)
{
@@ -193,7 +171,7 @@ public unsafe void Draw()
if (actors.Count == 0)
return;
- (int result, Guid? profileGuid) = _setTemporaryProfileOnCharacterIpcFunc(FindCharacterByAddress(actors[0].Item2.Address), _rememberedProfileJson);
+ (int result, Guid? profileGuid) = _setTemporaryProfileOnCharacterIpcFunc(actors[0].Item2.Index.Index, _rememberedProfileJson);
if (result == 0)
{
_popupSystem.ShowPopup(PopupSystem.Messages.IPCSetProfileToChrDone);
@@ -213,7 +191,7 @@ public unsafe void Draw()
if (actors.Count == 0)
return;
- int result = _deleteTemporaryProfileOnCharacterIpcFunc(FindCharacterByAddress(actors[0].Item2.Address));
+ int result = _deleteTemporaryProfileOnCharacterIpcFunc(actors[0].Item2.Index.Index);
if (result == 0)
_popupSystem.ShowPopup(PopupSystem.Messages.IPCRevertDone);
else
@@ -311,17 +289,10 @@ public unsafe void Draw()
}
[EzIPCEvent("Profile.OnUpdate")]
- private void OnProfileUpdate(ICharacter Character, Guid ProfileUniqueId)
- {
- _logger.Debug($"IPC Test Tab - OnProfileUpdate: Character: {Character.Name.ToString().Incognify()}, Profile ID: {(ProfileUniqueId != Guid.Empty ? ProfileUniqueId.ToString() : "no id")}");
- }
-
- private ICharacter? FindCharacterByAddress(nint address)
+ private void OnProfileUpdate(ushort gameObjectIndex, Guid profileUniqueId)
{
- foreach (var obj in _objectTable)
- if (obj.Address == address)
- return (ICharacter)obj;
+ var actor = _gameObjectService.GetActorByObjectIndex(gameObjectIndex);
- return null;
+ _logger.Debug($"IPC Test Tab - OnProfileUpdate: Character: {actor?.Utf8Name.ToString().Incognify() ?? "None"}, Profile ID: {(profileUniqueId != Guid.Empty ? profileUniqueId.ToString() : "no id")}");
}
}