Use object table index instead of passing ICharacter/Character address. Seems like the dev who decided to shit talk behind my back about this forgot it was them who implemented it that way.
This commit is contained in:
@@ -4,7 +4,7 @@ namespace CustomizePlus.Api;
|
|||||||
|
|
||||||
public partial class CustomizePlusIpc
|
public partial class CustomizePlusIpc
|
||||||
{
|
{
|
||||||
private readonly (int Breaking, int Feature) _apiVersion = (4, 0);
|
private readonly (int Breaking, int Feature) _apiVersion = (5, 0);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When there are breaking changes the first number is bumped up and second one is reset.
|
/// When there are breaking changes the first number is bumped up and second one is reset.
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public partial class CustomizePlusIpc
|
|||||||
/// /!\ If no profile is set on specified character profile id will be equal to Guid.Empty
|
/// /!\ If no profile is set on specified character profile id will be equal to Guid.Empty
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[EzIPCEvent("Profile.OnUpdate")]
|
[EzIPCEvent("Profile.OnUpdate")]
|
||||||
private Action<ICharacter, Guid> OnProfileUpdate;
|
private Action<ushort, Guid> OnProfileUpdate;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve list of all user profiles
|
/// Retrieve list of all user profiles
|
||||||
@@ -125,12 +125,14 @@ public partial class CustomizePlusIpc
|
|||||||
/// Get unique id of currently active profile for character.
|
/// Get unique id of currently active profile for character.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[EzIPC("Profile.GetActiveProfileIdOnCharacter")]
|
[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);
|
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)
|
if (profile == null)
|
||||||
return ((int)ErrorCode.ProfileNotFound, null);
|
return ((int)ErrorCode.ProfileNotFound, null);
|
||||||
@@ -143,14 +145,12 @@ public partial class CustomizePlusIpc
|
|||||||
/// Returns profile's unique id which can be used to manipulate it at a later date.
|
/// Returns profile's unique id which can be used to manipulate it at a later date.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[EzIPC("Profile.SetTemporaryProfileOnCharacter")]
|
[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)
|
var actor = _gameObjectService.GetActorByObjectIndex(gameObjectIndex);
|
||||||
if (character == null)
|
|
||||||
return ((int)ErrorCode.InvalidCharacter, null);
|
|
||||||
|
|
||||||
var actor = (Actor)character.Address;
|
//todo: do not allow to set temporary profile on reserved actors (examine, etc)
|
||||||
if (!actor.Valid)
|
if (actor == null || !actor.Value.Valid || !actor.Value.IsCharacter)
|
||||||
return ((int)ErrorCode.InvalidCharacter, null);
|
return ((int)ErrorCode.InvalidCharacter, null);
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -162,26 +162,26 @@ public partial class CustomizePlusIpc
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
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);
|
return ((int)ErrorCode.CorruptedProfile, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (profile == 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);
|
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
|
//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;
|
var fullProfile = IPCCharacterProfile.ToFullProfile(profile).Item1;
|
||||||
|
|
||||||
_profileManager.AddTemporaryProfile(fullProfile, actor);
|
_profileManager.AddTemporaryProfile(fullProfile, actor.Value);
|
||||||
return ((int)ErrorCode.Success, fullProfile.UniqueId);
|
return ((int)ErrorCode.Success, fullProfile.UniqueId);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
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);
|
return ((int)ErrorCode.UnknownError, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,18 +190,16 @@ public partial class CustomizePlusIpc
|
|||||||
/// Delete temporary profile currently active on character
|
/// Delete temporary profile currently active on character
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[EzIPC("Profile.DeleteTemporaryProfileOnCharacter")]
|
[EzIPC("Profile.DeleteTemporaryProfileOnCharacter")]
|
||||||
private int DeleteTemporaryProfileOnCharacter(ICharacter character)
|
private int DeleteTemporaryProfileOnCharacter(ushort gameObjectIndex)
|
||||||
{
|
{
|
||||||
if (character == null)
|
var actor = _gameObjectService.GetActorByObjectIndex(gameObjectIndex);
|
||||||
return (int)ErrorCode.InvalidCharacter;
|
|
||||||
|
|
||||||
var actor = (Actor)character.Address;
|
if (actor == null || !actor.Value.Valid || !actor.Value.IsCharacter)
|
||||||
if (!actor.Valid)
|
|
||||||
return (int)ErrorCode.InvalidCharacter;
|
return (int)ErrorCode.InvalidCharacter;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_profileManager.RemoveTemporaryProfile(actor);
|
_profileManager.RemoveTemporaryProfile(actor.Value);
|
||||||
return (int)ErrorCode.Success;
|
return (int)ErrorCode.Success;
|
||||||
}
|
}
|
||||||
catch(ProfileException ex)
|
catch(ProfileException ex)
|
||||||
@@ -213,13 +211,13 @@ public partial class CustomizePlusIpc
|
|||||||
case ProfileNotFoundException:
|
case ProfileNotFoundException:
|
||||||
return (int)ErrorCode.ProfileNotFound;
|
return (int)ErrorCode.ProfileNotFound;
|
||||||
default:
|
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;
|
return (int)ErrorCode.UnknownError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Exception ex)
|
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;
|
return (int)ErrorCode.UnknownError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -309,7 +307,7 @@ public partial class CustomizePlusIpc
|
|||||||
if (type == ArmatureChanged.Type.Deleted)
|
if (type == ArmatureChanged.Type.Deleted)
|
||||||
{
|
{
|
||||||
//Do not send event if default or editor profile was used
|
//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;
|
return;
|
||||||
|
|
||||||
OnProfileUpdateInternal(localPlayerCharacter, null);
|
OnProfileUpdateInternal(localPlayerCharacter, null);
|
||||||
@@ -327,6 +325,6 @@ public partial class CustomizePlusIpc
|
|||||||
|
|
||||||
_logger.Debug($"Sending player update message: Character: {character.Name.ToString().Incognify()}, Profile: {(profile != null ? profile.ToString() : "no 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,8 @@ using Penumbra.GameData.Enums;
|
|||||||
using Penumbra.GameData.Interop;
|
using Penumbra.GameData.Interop;
|
||||||
using ObjectManager = CustomizePlus.GameData.Services.ObjectManager;
|
using ObjectManager = CustomizePlus.GameData.Services.ObjectManager;
|
||||||
using DalamudGameObject = Dalamud.Game.ClientState.Objects.Types.IGameObject;
|
using DalamudGameObject = Dalamud.Game.ClientState.Objects.Types.IGameObject;
|
||||||
using ECommons.Configuration;
|
|
||||||
using System;
|
|
||||||
using CustomizePlus.Configuration.Data;
|
using CustomizePlus.Configuration.Data;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
using Penumbra.GameData;
|
|
||||||
using Penumbra.String;
|
|
||||||
using Dalamud.Logging;
|
|
||||||
|
|
||||||
namespace CustomizePlus.Game.Services;
|
namespace CustomizePlus.Game.Services;
|
||||||
|
|
||||||
@@ -145,6 +140,19 @@ public class GameObjectService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Find actor in object manager based on its Object ID.
|
||||||
|
/// </summary>
|
||||||
|
public Actor? GetActorByObjectIndex(ushort objectIndex)
|
||||||
|
{
|
||||||
|
if (objectIndex < 0 || objectIndex >= _objectManager.TotalCount)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var ptr = _objectManager[(int)objectIndex];
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
public enum SpecialResult
|
public enum SpecialResult
|
||||||
{
|
{
|
||||||
PartyBanner,
|
PartyBanner,
|
||||||
|
|||||||
@@ -50,13 +50,13 @@ public class IPCTestTab //: IDisposable
|
|||||||
private readonly Func<Guid, int> _disableProfileByUniqueIdIpcFunc;
|
private readonly Func<Guid, int> _disableProfileByUniqueIdIpcFunc;
|
||||||
|
|
||||||
[EzIPC("Profile.GetActiveProfileIdOnCharacter")]
|
[EzIPC("Profile.GetActiveProfileIdOnCharacter")]
|
||||||
private readonly Func<ICharacter, (int, Guid?)> _getActiveProfileIdOnCharacterIpcFunc;
|
private readonly Func<ushort, (int, Guid?)> _getActiveProfileIdOnCharacterIpcFunc;
|
||||||
|
|
||||||
[EzIPC("Profile.SetTemporaryProfileOnCharacter")]
|
[EzIPC("Profile.SetTemporaryProfileOnCharacter")]
|
||||||
private readonly Func<ICharacter, string, (int, Guid?)> _setTemporaryProfileOnCharacterIpcFunc;
|
private readonly Func<ushort, string, (int, Guid?)> _setTemporaryProfileOnCharacterIpcFunc;
|
||||||
|
|
||||||
[EzIPC("Profile.DeleteTemporaryProfileOnCharacter")]
|
[EzIPC("Profile.DeleteTemporaryProfileOnCharacter")]
|
||||||
private readonly Func<ICharacter, int> _deleteTemporaryProfileOnCharacterIpcFunc;
|
private readonly Func<ushort, int> _deleteTemporaryProfileOnCharacterIpcFunc;
|
||||||
|
|
||||||
[EzIPC("Profile.DeleteTemporaryProfileByUniqueId")]
|
[EzIPC("Profile.DeleteTemporaryProfileByUniqueId")]
|
||||||
private readonly Func<Guid, int> _deleteTemporaryProfileByUniqueIdIpcFunc;
|
private readonly Func<Guid, int> _deleteTemporaryProfileByUniqueIdIpcFunc;
|
||||||
@@ -64,11 +64,6 @@ public class IPCTestTab //: IDisposable
|
|||||||
[EzIPC("Profile.GetByUniqueId")]
|
[EzIPC("Profile.GetByUniqueId")]
|
||||||
private readonly Func<Guid, (int, string?)> _getProfileByIdIpcFunc;
|
private readonly Func<Guid, (int, string?)> _getProfileByIdIpcFunc;
|
||||||
|
|
||||||
//private readonly ICallGateSubscriber<string, Character?, object>? _setCharacterProfile;
|
|
||||||
//private readonly ICallGateSubscriber<Character?, string>? _getProfileFromCharacter;
|
|
||||||
//private readonly ICallGateSubscriber<Character?, object>? _revertCharacter;
|
|
||||||
//private readonly ICallGateSubscriber<string?, string?, object?>? _onProfileUpdate;
|
|
||||||
|
|
||||||
private string? _rememberedProfileJson;
|
private string? _rememberedProfileJson;
|
||||||
|
|
||||||
private (int, int) _apiVersion;
|
private (int, int) _apiVersion;
|
||||||
@@ -103,24 +98,8 @@ public class IPCTestTab //: IDisposable
|
|||||||
|
|
||||||
if (_getApiVersionIpcFunc != null)
|
if (_getApiVersionIpcFunc != null)
|
||||||
_apiVersion = _getApiVersionIpcFunc();
|
_apiVersion = _getApiVersionIpcFunc();
|
||||||
|
|
||||||
//_setCharacterProfile = pluginInterface.GetIpcSubscriber<string, Character?, object>("CustomizePlus.SetProfileToCharacter");
|
|
||||||
//_getProfileFromCharacter = pluginInterface.GetIpcSubscriber<Character?, string>("CustomizePlus.GetProfileFromCharacter");
|
|
||||||
//_revertCharacter = pluginInterface.GetIpcSubscriber<Character?, object>("CustomizePlus.RevertCharacter");
|
|
||||||
/*_onProfileUpdate = pluginInterface.GetIpcSubscriber<string?, string?, object?>("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()
|
public unsafe void Draw()
|
||||||
{
|
{
|
||||||
_objectManager.Update();
|
_objectManager.Update();
|
||||||
@@ -141,7 +120,6 @@ public class IPCTestTab //: IDisposable
|
|||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
//ImGui.Text($"Last profile update: {_lastProfileUpdate}, Character: {_lastProfileUpdateName}");
|
|
||||||
ImGui.Text($"Memory: {(string.IsNullOrWhiteSpace(_rememberedProfileJson) ? "empty" : "has data")}");
|
ImGui.Text($"Memory: {(string.IsNullOrWhiteSpace(_rememberedProfileJson) ? "empty" : "has data")}");
|
||||||
|
|
||||||
ImGui.Text("Character to operate on:");
|
ImGui.Text("Character to operate on:");
|
||||||
@@ -171,7 +149,7 @@ public class IPCTestTab //: IDisposable
|
|||||||
if (actors.Count == 0)
|
if (actors.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
(int result, Guid? uniqueId) = _getActiveProfileIdOnCharacterIpcFunc(FindCharacterByAddress(actors[0].Item2.Address));
|
(int result, Guid? uniqueId) = _getActiveProfileIdOnCharacterIpcFunc(actors[0].Item2.Index.Index);
|
||||||
|
|
||||||
if(result == 0)
|
if(result == 0)
|
||||||
{
|
{
|
||||||
@@ -193,7 +171,7 @@ public class IPCTestTab //: IDisposable
|
|||||||
if (actors.Count == 0)
|
if (actors.Count == 0)
|
||||||
return;
|
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)
|
if (result == 0)
|
||||||
{
|
{
|
||||||
_popupSystem.ShowPopup(PopupSystem.Messages.IPCSetProfileToChrDone);
|
_popupSystem.ShowPopup(PopupSystem.Messages.IPCSetProfileToChrDone);
|
||||||
@@ -213,7 +191,7 @@ public class IPCTestTab //: IDisposable
|
|||||||
if (actors.Count == 0)
|
if (actors.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int result = _deleteTemporaryProfileOnCharacterIpcFunc(FindCharacterByAddress(actors[0].Item2.Address));
|
int result = _deleteTemporaryProfileOnCharacterIpcFunc(actors[0].Item2.Index.Index);
|
||||||
if (result == 0)
|
if (result == 0)
|
||||||
_popupSystem.ShowPopup(PopupSystem.Messages.IPCRevertDone);
|
_popupSystem.ShowPopup(PopupSystem.Messages.IPCRevertDone);
|
||||||
else
|
else
|
||||||
@@ -311,17 +289,10 @@ public class IPCTestTab //: IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
[EzIPCEvent("Profile.OnUpdate")]
|
[EzIPCEvent("Profile.OnUpdate")]
|
||||||
private void OnProfileUpdate(ICharacter Character, Guid ProfileUniqueId)
|
private void OnProfileUpdate(ushort gameObjectIndex, Guid profileUniqueId)
|
||||||
{
|
{
|
||||||
_logger.Debug($"IPC Test Tab - OnProfileUpdate: Character: {Character.Name.ToString().Incognify()}, Profile ID: {(ProfileUniqueId != Guid.Empty ? ProfileUniqueId.ToString() : "no id")}");
|
var actor = _gameObjectService.GetActorByObjectIndex(gameObjectIndex);
|
||||||
}
|
|
||||||
|
|
||||||
private ICharacter? FindCharacterByAddress(nint address)
|
_logger.Debug($"IPC Test Tab - OnProfileUpdate: Character: {actor?.Utf8Name.ToString().Incognify() ?? "None"}, Profile ID: {(profileUniqueId != Guid.Empty ? profileUniqueId.ToString() : "no id")}");
|
||||||
{
|
|
||||||
foreach (var obj in _objectTable)
|
|
||||||
if (obj.Address == address)
|
|
||||||
return (ICharacter)obj;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user