Implemented rest of profile methods for IPC
This commit is contained in:
@@ -24,6 +24,7 @@ using CustomizePlus.GameData.Extensions;
|
|||||||
|
|
||||||
namespace CustomizePlus.Api.Compatibility;
|
namespace CustomizePlus.Api.Compatibility;
|
||||||
|
|
||||||
|
[Obsolete("Will be removed in the next release")]
|
||||||
public class CustomizePlusLegacyIpc : IDisposable
|
public class CustomizePlusLegacyIpc : IDisposable
|
||||||
{
|
{
|
||||||
private readonly IObjectTable _objectTable;
|
private readonly IObjectTable _objectTable;
|
||||||
|
|||||||
@@ -3,13 +3,36 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using ECommons.EzIpcManager;
|
using ECommons.EzIpcManager;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using CustomizePlus.Api.Data;
|
||||||
|
using CustomizePlus.GameData.Data;
|
||||||
|
using CustomizePlus.Api.Enums;
|
||||||
|
using CustomizePlus.Profiles.Exceptions;
|
||||||
|
using CustomizePlus.Profiles.Data;
|
||||||
|
using CustomizePlus.Core.Extensions;
|
||||||
|
using CustomizePlus.Profiles.Events;
|
||||||
|
using CustomizePlus.Armatures.Data;
|
||||||
|
using CustomizePlus.Armatures.Events;
|
||||||
|
using CustomizePlus.GameData.Extensions;
|
||||||
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
|
using Penumbra.GameData.Actors;
|
||||||
|
|
||||||
using IPCProfileDataTuple = (System.Guid UniqueId, string Name, string CharacterName, bool IsEnabled);
|
using IPCProfileDataTuple = (System.Guid UniqueId, string Name, string CharacterName, bool IsEnabled);
|
||||||
|
//using OnUpdateTuple = (Dalamud.Game.ClientState.Objects.Types.Character Character, System.Guid? ProfileUniqueId, string? ProfileJson);
|
||||||
|
|
||||||
namespace CustomizePlus.Api;
|
namespace CustomizePlus.Api;
|
||||||
|
|
||||||
public partial class CustomizePlusIpc
|
public partial class CustomizePlusIpc
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Triggered when changes in currently active profiles are detected. (like changing active profile or making any changes to it)
|
||||||
|
/// Not triggered if any changes happen due to character no longer existing.
|
||||||
|
/// Right now ignores every character but local player.
|
||||||
|
/// Ignores temporary profiles.
|
||||||
|
/// </summary>
|
||||||
|
[EzIPCEvent("Profile.OnUpdate")]
|
||||||
|
private Action<Character, Guid?> OnProfileUpdate;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve list of all user profiles
|
/// Retrieve list of all user profiles
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -24,21 +47,302 @@ public partial class CustomizePlusIpc
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enable profile using its Unique ID
|
/// Get JSON copy of profile with specified unique id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="uniqueId"></param>
|
[EzIPC("Profile.GetProfileById")]
|
||||||
[EzIPC("Profile.EnableByUniqueId")]
|
private (int, string?) GetProfileById(Guid uniqueId)
|
||||||
private void EnableProfileByUniqueId(Guid uniqueId)
|
|
||||||
{
|
{
|
||||||
_profileManager.SetEnabled(uniqueId, true);
|
if (uniqueId == Guid.Empty)
|
||||||
|
return ((int)ErrorCode.ProfileNotFound, null);
|
||||||
|
|
||||||
|
var profile = _profileManager.Profiles.Where(x => x.UniqueId == uniqueId && !x.IsTemporary).FirstOrDefault(); //todo: move into profile manager
|
||||||
|
|
||||||
|
if (profile == null)
|
||||||
|
return ((int)ErrorCode.ProfileNotFound, null);
|
||||||
|
|
||||||
|
var convertedProfile = IPCCharacterProfile.FromFullProfile(profile);
|
||||||
|
|
||||||
|
if (convertedProfile == null)
|
||||||
|
{
|
||||||
|
_logger.Error($"IPCCharacterProfile.FromFullProfile returned empty converted profile for id: {uniqueId}");
|
||||||
|
return ((int)ErrorCode.UnknownError, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ((int)ErrorCode.Success, JsonConvert.SerializeObject(convertedProfile));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error($"Exception in IPCCharacterProfile.FromFullProfile for id {uniqueId}: {ex}");
|
||||||
|
return ((int)ErrorCode.UnknownError, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Disable profile using its Unique ID
|
/// Enable profile using its Unique ID. Does not work on temporary profiles.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uniqueId"></param>
|
||||||
|
[EzIPC("Profile.EnableByUniqueId")]
|
||||||
|
private ErrorCode EnableProfileByUniqueId(Guid uniqueId)
|
||||||
|
{
|
||||||
|
return SetProfileStateInternal(uniqueId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disable profile using its Unique ID. Does not work on temporary profiles.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[EzIPC("Profile.DisableByUniqueId")]
|
[EzIPC("Profile.DisableByUniqueId")]
|
||||||
private void DisableProfileByUniqueId(Guid uniqueId)
|
private ErrorCode DisableProfileByUniqueId(Guid uniqueId)
|
||||||
{
|
{
|
||||||
_profileManager.SetEnabled(uniqueId, false);
|
return SetProfileStateInternal(uniqueId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ErrorCode SetProfileStateInternal(Guid uniqueId, bool state)
|
||||||
|
{
|
||||||
|
if (uniqueId == Guid.Empty)
|
||||||
|
return ErrorCode.ProfileNotFound;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_profileManager.SetEnabled(uniqueId, true);
|
||||||
|
return ErrorCode.Success;
|
||||||
|
}
|
||||||
|
catch (ProfileNotFoundException ex)
|
||||||
|
{
|
||||||
|
return ErrorCode.ProfileNotFound;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error($"Exception in SetProfileStateInternal. Unique id: {uniqueId}, state: {state}, exception: {ex}.");
|
||||||
|
return ErrorCode.UnknownError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get JSON copy of active profile for character.
|
||||||
|
/// </summary>
|
||||||
|
[EzIPC("Profile.GetCurrentlyActiveProfileOnCharacter")]
|
||||||
|
private (int, string?) GetCurrentlyActiveProfileOnCharacter(Character character)
|
||||||
|
{
|
||||||
|
if (character == null)
|
||||||
|
return ((int)ErrorCode.InvalidCharacter, null);
|
||||||
|
|
||||||
|
var profile = _profileManager.GetProfileByCharacterName(character.Name.ToString(), true);
|
||||||
|
|
||||||
|
if (profile == null)
|
||||||
|
return ((int)ErrorCode.ProfileNotFound, null);
|
||||||
|
|
||||||
|
var convertedProfile = IPCCharacterProfile.FromFullProfile(profile);
|
||||||
|
|
||||||
|
if (convertedProfile == null)
|
||||||
|
{
|
||||||
|
_logger.Error($"IPCCharacterProfile.FromFullProfile returned empty converted profile for character {character?.Name.ToString().Incognify()}, profile: {profile.UniqueId}");
|
||||||
|
return ((int)ErrorCode.UnknownError, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ((int)ErrorCode.Success, JsonConvert.SerializeObject(convertedProfile));
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error($"Exception in IPCCharacterProfile.FromFullProfile for character {character?.Name.ToString().Incognify()}, profile: {profile.UniqueId}: {ex}");
|
||||||
|
return ((int)ErrorCode.UnknownError, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Apply provided profile as temporary profile on specified character.
|
||||||
|
/// Returns profile's unique id which can be used to manipulate it at a later date.
|
||||||
|
/// </summary>
|
||||||
|
[EzIPC("Profile.SetTemporaryProfileOnCharacter")]
|
||||||
|
private (int, Guid?) SetTemporaryProfileOnCharacter(Character character, string profileJson)
|
||||||
|
{
|
||||||
|
if (character == null)
|
||||||
|
return ((int)ErrorCode.InvalidCharacter, null);
|
||||||
|
|
||||||
|
var actor = (Actor)character.Address;
|
||||||
|
if (!actor.Valid)
|
||||||
|
return ((int)ErrorCode.InvalidCharacter, null);
|
||||||
|
|
||||||
|
/*if (character == _objectTable[0])
|
||||||
|
{
|
||||||
|
_logger.Error($"Received request to set profile on local character, this is not allowed");
|
||||||
|
return;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IPCCharacterProfile? profile;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
profile = JsonConvert.DeserializeObject<IPCCharacterProfile>(profileJson);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error($"IPCCharacterProfile deserialization issue. Character: {character?.Name.ToString().Incognify()}, exception: {ex}.");
|
||||||
|
return ((int)ErrorCode.CorruptedProfile, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile == null)
|
||||||
|
{
|
||||||
|
_logger.Error($"IPCCharacterProfile is null after deserialization. Character: {character?.Name.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);
|
||||||
|
return ((int)ErrorCode.Success, fullProfile.UniqueId);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error($"Unable to set temporary profile. Character: {character?.Name.ToString().Incognify()}, exception: {ex}.");
|
||||||
|
return ((int)ErrorCode.UnknownError, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete temporary profile currently active on character
|
||||||
|
/// </summary>
|
||||||
|
[EzIPC("Profile.DeleteTemporaryProfileOnCharacter")]
|
||||||
|
private int DeleteTemporaryProfileOnCharacter(Character character)
|
||||||
|
{
|
||||||
|
if (character == null)
|
||||||
|
return (int)ErrorCode.InvalidCharacter;
|
||||||
|
|
||||||
|
var actor = (Actor)character.Address;
|
||||||
|
if (!actor.Valid)
|
||||||
|
return (int)ErrorCode.InvalidCharacter;
|
||||||
|
|
||||||
|
/*if (character == _objectTable[0])
|
||||||
|
{
|
||||||
|
_logger.Error($"Received request to revert profile on local character, this is not allowed");
|
||||||
|
return;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_profileManager.RemoveTemporaryProfile(actor);
|
||||||
|
return (int)ErrorCode.Success;
|
||||||
|
}
|
||||||
|
catch(ProfileException ex)
|
||||||
|
{
|
||||||
|
switch(ex)
|
||||||
|
{
|
||||||
|
case ActorNotFoundException _:
|
||||||
|
return (int)ErrorCode.InvalidCharacter;
|
||||||
|
case ProfileNotFoundException:
|
||||||
|
return (int)ErrorCode.ProfileNotFound;
|
||||||
|
default:
|
||||||
|
_logger.Error($"Exception in DeleteTemporaryProfileOnCharacter. Character: {character?.Name.ToString().Incognify()}. Exception: {ex}");
|
||||||
|
return (int)ErrorCode.UnknownError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error($"Exception in DeleteTemporaryProfileOnCharacter. Character: {character?.Name.ToString().Incognify()}. Exception: {ex}");
|
||||||
|
return (int)ErrorCode.UnknownError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete temporary profile using its unique id
|
||||||
|
/// </summary>
|
||||||
|
[EzIPC("Profile.DeleteTemporaryProfileByUniqueId")]
|
||||||
|
private int DeleteTemporaryProfileByUniqueId(Guid uniqueId)
|
||||||
|
{
|
||||||
|
if (uniqueId == Guid.Empty)
|
||||||
|
return (int)ErrorCode.ProfileNotFound;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_profileManager.RemoveTemporaryProfile(uniqueId);
|
||||||
|
return (int)ErrorCode.Success;
|
||||||
|
}
|
||||||
|
catch (ProfileException ex)
|
||||||
|
{
|
||||||
|
switch (ex)
|
||||||
|
{
|
||||||
|
case ActorNotFoundException _:
|
||||||
|
return (int)ErrorCode.InvalidCharacter; //note: this is not considered an error for this case, returned just so external caller knows what is going on
|
||||||
|
case ProfileNotFoundException:
|
||||||
|
return (int)ErrorCode.ProfileNotFound;
|
||||||
|
default:
|
||||||
|
_logger.Error($"Exception in DeleteTemporaryProfileOnCharacter. Unique id: {uniqueId}. Exception: {ex}");
|
||||||
|
return (int)ErrorCode.UnknownError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error($"Exception in DeleteTemporaryProfileOnCharacter. Unique id: {uniqueId}. Exception: {ex}");
|
||||||
|
return (int)ErrorCode.UnknownError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//warn: limitation - ignores default profiles but why you would use default profile on your own character
|
||||||
|
private void OnProfileChange(ProfileChanged.Type type, Profile? profile, object? arg3)
|
||||||
|
{
|
||||||
|
if (type != ProfileChanged.Type.AddedTemplate &&
|
||||||
|
type != ProfileChanged.Type.RemovedTemplate &&
|
||||||
|
type != ProfileChanged.Type.MovedTemplate &&
|
||||||
|
type != ProfileChanged.Type.ChangedTemplate &&
|
||||||
|
type != ProfileChanged.Type.Toggled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (profile == null ||
|
||||||
|
!profile.Enabled || //profile = null event will be sent from OnArmatureChanged
|
||||||
|
profile.CharacterName.Text != _gameObjectService.GetCurrentPlayerName())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Character? localPlayerCharacter = (Character?)_gameObjectService.GetDalamudGameObjectFromActor(_gameObjectService.GetLocalPlayerActor());
|
||||||
|
if (localPlayerCharacter == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
OnProfileUpdateInternal(localPlayerCharacter, profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnArmatureChanged(ArmatureChanged.Type type, Armature armature, object? arg3)
|
||||||
|
{
|
||||||
|
string currentPlayerName = _gameObjectService.GetCurrentPlayerName();
|
||||||
|
|
||||||
|
if (armature.ActorIdentifier.ToNameWithoutOwnerName() != currentPlayerName)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Character? localPlayerCharacter = (Character?)_gameObjectService.GetDalamudGameObjectFromActor(_gameObjectService.GetLocalPlayerActor());
|
||||||
|
if (localPlayerCharacter == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (type == ArmatureChanged.Type.Created || //todo: might create second call after OnProfileChange?
|
||||||
|
type == ArmatureChanged.Type.Rebound)
|
||||||
|
{
|
||||||
|
if (armature.Profile == null)
|
||||||
|
_logger.Warning("Armature created/rebound and profile is null");
|
||||||
|
|
||||||
|
OnProfileUpdateInternal(localPlayerCharacter, armature.Profile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == ArmatureChanged.Type.Deleted)
|
||||||
|
{
|
||||||
|
OnProfileUpdateInternal(localPlayerCharacter, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnProfileUpdateInternal(Character character, Profile? profile)
|
||||||
|
{
|
||||||
|
if (character == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (profile != null && profile.IsTemporary)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_logger.Debug($"Sending player update message: Character: {character.Name.ToString().Incognify()}, Profile: {(profile != null ? profile.ToString() : "no profile")}");
|
||||||
|
|
||||||
|
OnProfileUpdate(character, profile != null ? profile.UniqueId : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
using CustomizePlus.Core.Services;
|
using CustomizePlus.Armatures.Events;
|
||||||
|
using CustomizePlus.Core.Services;
|
||||||
|
using CustomizePlus.Game.Services;
|
||||||
using CustomizePlus.Profiles;
|
using CustomizePlus.Profiles;
|
||||||
|
using CustomizePlus.Profiles.Events;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using ECommons.EzIpcManager;
|
using ECommons.EzIpcManager;
|
||||||
using OtterGui.Log;
|
using OtterGui.Log;
|
||||||
@@ -7,12 +10,16 @@ using System;
|
|||||||
|
|
||||||
namespace CustomizePlus.Api;
|
namespace CustomizePlus.Api;
|
||||||
|
|
||||||
public partial class CustomizePlusIpc
|
public partial class CustomizePlusIpc : IDisposable
|
||||||
{
|
{
|
||||||
private readonly DalamudPluginInterface _pluginInterface;
|
private readonly DalamudPluginInterface _pluginInterface;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
private readonly HookingService _hookingService;
|
private readonly HookingService _hookingService;
|
||||||
private readonly ProfileManager _profileManager;
|
private readonly ProfileManager _profileManager;
|
||||||
|
private readonly GameObjectService _gameObjectService;
|
||||||
|
|
||||||
|
private readonly ProfileChanged _profileChangedEvent;
|
||||||
|
private readonly ArmatureChanged _armatureChangedEvent;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shows if IPC failed to initialize or any other unrecoverable fatal error occured.
|
/// Shows if IPC failed to initialize or any other unrecoverable fatal error occured.
|
||||||
@@ -23,13 +30,30 @@ public partial class CustomizePlusIpc
|
|||||||
DalamudPluginInterface pluginInterface,
|
DalamudPluginInterface pluginInterface,
|
||||||
Logger logger,
|
Logger logger,
|
||||||
HookingService hookingService,
|
HookingService hookingService,
|
||||||
ProfileManager profileManager)
|
ProfileManager profileManager,
|
||||||
|
GameObjectService gameObjectService,
|
||||||
|
ArmatureChanged armatureChangedEvent,
|
||||||
|
ProfileChanged profileChangedEvent)
|
||||||
{
|
{
|
||||||
_pluginInterface = pluginInterface;
|
_pluginInterface = pluginInterface;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_hookingService = hookingService;
|
_hookingService = hookingService;
|
||||||
_profileManager = profileManager;
|
_profileManager = profileManager;
|
||||||
|
_gameObjectService = gameObjectService;
|
||||||
|
|
||||||
|
|
||||||
|
_profileChangedEvent = profileChangedEvent;
|
||||||
|
_armatureChangedEvent = armatureChangedEvent;
|
||||||
|
|
||||||
EzIPC.Init(this, "CustomizePlus");
|
EzIPC.Init(this, "CustomizePlus");
|
||||||
|
|
||||||
|
_profileChangedEvent.Subscribe(OnProfileChange, ProfileChanged.Priority.CustomizePlusIpc);
|
||||||
|
_armatureChangedEvent.Subscribe(OnArmatureChanged, ArmatureChanged.Priority.CustomizePlusIpc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_profileChangedEvent.Unsubscribe(OnProfileChange);
|
||||||
|
_armatureChangedEvent.Unsubscribe(OnArmatureChanged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
134
CustomizePlus/Api/Data/IPCCharacterProfile.cs
Normal file
134
CustomizePlus/Api/Data/IPCCharacterProfile.cs
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
using CustomizePlus.Configuration.Data.Version3;
|
||||||
|
using CustomizePlus.Core.Data;
|
||||||
|
using CustomizePlus.Profiles.Data;
|
||||||
|
using CustomizePlus.Templates.Data;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CustomizePlus.Api.Data;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bare essentials version of character profile
|
||||||
|
/// </summary>
|
||||||
|
public class IPCCharacterProfile
|
||||||
|
{
|
||||||
|
public string CharacterName { get; set; } = "Invalid";
|
||||||
|
public Dictionary<string, IPCBoneTransform> Bones { get; init; } = new();
|
||||||
|
|
||||||
|
public static IPCCharacterProfile FromFullProfile(Profile profile)
|
||||||
|
{
|
||||||
|
var ipcProfile = new IPCCharacterProfile
|
||||||
|
{
|
||||||
|
CharacterName = profile.CharacterName,
|
||||||
|
Bones = new Dictionary<string, IPCBoneTransform>()
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var template in profile.Templates)
|
||||||
|
{
|
||||||
|
foreach (var kvPair in template.Bones) //not super optimal but whatever
|
||||||
|
{
|
||||||
|
ipcProfile.Bones[kvPair.Key] = new IPCBoneTransform
|
||||||
|
{
|
||||||
|
Translation = kvPair.Value.Translation,
|
||||||
|
Rotation = kvPair.Value.Rotation,
|
||||||
|
Scaling = kvPair.Value.Scaling
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipcProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (Profile, Template) ToFullProfile(IPCCharacterProfile profile, bool isTemporary = true)
|
||||||
|
{
|
||||||
|
var fullProfile = new Profile
|
||||||
|
{
|
||||||
|
Name = $"{profile.CharacterName}'s IPC profile",
|
||||||
|
CharacterName = profile.CharacterName,
|
||||||
|
CreationDate = DateTimeOffset.UtcNow,
|
||||||
|
ModifiedDate = DateTimeOffset.UtcNow,
|
||||||
|
Enabled = true,
|
||||||
|
LimitLookupToOwnedObjects = false,
|
||||||
|
UniqueId = Guid.NewGuid(),
|
||||||
|
Templates = new List<Template>(1),
|
||||||
|
ProfileType = isTemporary ? Profiles.Enums.ProfileType.Temporary : Profiles.Enums.ProfileType.Normal
|
||||||
|
};
|
||||||
|
|
||||||
|
var template = new Template
|
||||||
|
{
|
||||||
|
Name = $"{fullProfile.Name}'s template",
|
||||||
|
CreationDate = fullProfile.CreationDate,
|
||||||
|
ModifiedDate = fullProfile.ModifiedDate,
|
||||||
|
UniqueId = Guid.NewGuid(),
|
||||||
|
Bones = new Dictionary<string, BoneTransform>(profile.Bones.Count)
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var kvPair in profile.Bones)
|
||||||
|
template.Bones.Add(kvPair.Key,
|
||||||
|
new BoneTransform { Translation = kvPair.Value.Translation, Rotation = kvPair.Value.Rotation, Scaling = kvPair.Value.Scaling });
|
||||||
|
|
||||||
|
fullProfile.Templates.Add(template);
|
||||||
|
|
||||||
|
return (fullProfile, template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IPCBoneTransform
|
||||||
|
{
|
||||||
|
private Vector3 _translation;
|
||||||
|
public Vector3 Translation
|
||||||
|
{
|
||||||
|
get => _translation;
|
||||||
|
set => _translation = ClampVector(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector3 _rotation;
|
||||||
|
public Vector3 Rotation
|
||||||
|
{
|
||||||
|
get => _rotation;
|
||||||
|
set => _rotation = ClampAngles(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector3 _scaling;
|
||||||
|
public Vector3 Scaling
|
||||||
|
{
|
||||||
|
get => _scaling;
|
||||||
|
set => _scaling = ClampVector(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clamp all vector values to be within allowed limits.
|
||||||
|
/// </summary>
|
||||||
|
private Vector3 ClampVector(Vector3 vector)
|
||||||
|
{
|
||||||
|
return new Vector3
|
||||||
|
{
|
||||||
|
X = Math.Clamp(vector.X, -512, 512),
|
||||||
|
Y = Math.Clamp(vector.Y, -512, 512),
|
||||||
|
Z = Math.Clamp(vector.Z, -512, 512)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector3 ClampAngles(Vector3 rotVec)
|
||||||
|
{
|
||||||
|
static float Clamp(float angle)
|
||||||
|
{
|
||||||
|
if (angle > 180)
|
||||||
|
angle -= 360;
|
||||||
|
else if (angle < -180)
|
||||||
|
angle += 360;
|
||||||
|
|
||||||
|
return angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
rotVec.X = Clamp(rotVec.X);
|
||||||
|
rotVec.Y = Clamp(rotVec.Y);
|
||||||
|
rotVec.Z = Clamp(rotVec.Z);
|
||||||
|
|
||||||
|
return rotVec;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
CustomizePlus/Api/Enums/ErrorCode.cs
Normal file
29
CustomizePlus/Api/Enums/ErrorCode.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CustomizePlus.Api.Enums;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Error codes returned by some API methods
|
||||||
|
/// </summary>
|
||||||
|
public enum ErrorCode
|
||||||
|
{
|
||||||
|
Success = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// Returned when invalid character address was provided
|
||||||
|
/// </summary>
|
||||||
|
InvalidCharacter = 1,
|
||||||
|
/// <summary>
|
||||||
|
/// Returned if IPCCharacterProfile could not be deserialized or deserialized into an empty object
|
||||||
|
/// </summary>
|
||||||
|
CorruptedProfile = 2,
|
||||||
|
/// <summary>
|
||||||
|
/// Provided character does not have active profiles, provided profile id is invalid or provided profile id is not valid for use in current function
|
||||||
|
/// </summary>
|
||||||
|
ProfileNotFound = 3,
|
||||||
|
|
||||||
|
UnknownError = 255
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ using CustomizePlus.GameData.Data;
|
|||||||
using CustomizePlus.GameData.Services;
|
using CustomizePlus.GameData.Services;
|
||||||
using CustomizePlus.GameData.Extensions;
|
using CustomizePlus.GameData.Extensions;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
using DalamudGameObject = Dalamud.Game.ClientState.Objects.Types.GameObject;
|
||||||
|
|
||||||
namespace CustomizePlus.Game.Services;
|
namespace CustomizePlus.Game.Services;
|
||||||
|
|
||||||
@@ -69,4 +70,14 @@ public class GameObjectService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Actor GetLocalPlayerActor()
|
||||||
|
{
|
||||||
|
return _objectManager.Player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DalamudGameObject? GetDalamudGameObjectFromActor(Actor actor)
|
||||||
|
{
|
||||||
|
return _objectTable.CreateObjectReference(actor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
23
CustomizePlus/Profiles/Exceptions/ActorNotFoundException.cs
Normal file
23
CustomizePlus/Profiles/Exceptions/ActorNotFoundException.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace CustomizePlus.Profiles.Exceptions;
|
||||||
|
|
||||||
|
internal class ActorNotFoundException : ProfileException
|
||||||
|
{
|
||||||
|
public ActorNotFoundException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActorNotFoundException(string? message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActorNotFoundException(string? message, Exception? innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ActorNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
23
CustomizePlus/Profiles/Exceptions/ProfileException.cs
Normal file
23
CustomizePlus/Profiles/Exceptions/ProfileException.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace CustomizePlus.Profiles.Exceptions;
|
||||||
|
|
||||||
|
internal class ProfileException : Exception
|
||||||
|
{
|
||||||
|
public ProfileException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProfileException(string? message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProfileException(string? message, Exception? innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProfileException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CustomizePlus.Profiles.Exceptions;
|
||||||
|
internal class ProfileNotFoundException : ProfileException
|
||||||
|
{
|
||||||
|
public ProfileNotFoundException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProfileNotFoundException(string? message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProfileNotFoundException(string? message, Exception? innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProfileNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ using CustomizePlus.GameData.Services;
|
|||||||
using CustomizePlus.GameData.Extensions;
|
using CustomizePlus.GameData.Extensions;
|
||||||
using CustomizePlus.Profiles.Enums;
|
using CustomizePlus.Profiles.Enums;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
using CustomizePlus.Profiles.Exceptions;
|
||||||
|
|
||||||
namespace CustomizePlus.Profiles;
|
namespace CustomizePlus.Profiles;
|
||||||
|
|
||||||
@@ -299,6 +300,8 @@ public class ProfileManager : IDisposable
|
|||||||
{
|
{
|
||||||
SetEnabled(profile, value);
|
SetEnabled(profile, value);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
throw new ProfileNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetLimitLookupToOwned(Profile profile, bool value)
|
public void SetLimitLookupToOwned(Profile profile, bool value)
|
||||||
@@ -390,7 +393,7 @@ public class ProfileManager : IDisposable
|
|||||||
public void AddTemporaryProfile(Profile profile, Actor actor/*, Template template*/)
|
public void AddTemporaryProfile(Profile profile, Actor actor/*, Template template*/)
|
||||||
{
|
{
|
||||||
if (!actor.Identifier(_actorManager, out var identifier))
|
if (!actor.Identifier(_actorManager, out var identifier))
|
||||||
return;
|
throw new ActorNotFoundException();
|
||||||
|
|
||||||
profile.Enabled = true;
|
profile.Enabled = true;
|
||||||
profile.ProfileType = ProfileType.Temporary;
|
profile.ProfileType = ProfileType.Temporary;
|
||||||
@@ -418,21 +421,30 @@ public class ProfileManager : IDisposable
|
|||||||
public void RemoveTemporaryProfile(Profile profile)
|
public void RemoveTemporaryProfile(Profile profile)
|
||||||
{
|
{
|
||||||
if (!Profiles.Remove(profile))
|
if (!Profiles.Remove(profile))
|
||||||
return;
|
throw new ProfileNotFoundException();
|
||||||
|
|
||||||
_logger.Debug($"Removed temporary profile for {profile.CharacterName}");
|
_logger.Debug($"Removed temporary profile for {profile.CharacterName}");
|
||||||
|
|
||||||
_event.Invoke(ProfileChanged.Type.TemporaryProfileDeleted, profile, null);
|
_event.Invoke(ProfileChanged.Type.TemporaryProfileDeleted, profile, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RemoveTemporaryProfile(Guid profileId)
|
||||||
|
{
|
||||||
|
var profile = Profiles.FirstOrDefault(x => x.UniqueId == profileId && x.IsTemporary);
|
||||||
|
if (profile == null)
|
||||||
|
throw new ProfileNotFoundException();
|
||||||
|
|
||||||
|
RemoveTemporaryProfile(profile);
|
||||||
|
}
|
||||||
|
|
||||||
public void RemoveTemporaryProfile(Actor actor)
|
public void RemoveTemporaryProfile(Actor actor)
|
||||||
{
|
{
|
||||||
if (!actor.Identifier(_actorManager, out var identifier))
|
if (!actor.Identifier(_actorManager, out var identifier))
|
||||||
return;
|
throw new ActorNotFoundException();
|
||||||
|
|
||||||
var profile = Profiles.FirstOrDefault(x => x.TemporaryActor == identifier && x.IsTemporary);
|
var profile = Profiles.FirstOrDefault(x => x.TemporaryActor == identifier && x.IsTemporary);
|
||||||
if (profile == null)
|
if (profile == null)
|
||||||
return;
|
throw new ProfileNotFoundException();
|
||||||
|
|
||||||
RemoveTemporaryProfile(profile);
|
RemoveTemporaryProfile(profile);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using IPCProfileDataTuple = (System.Guid UniqueId, string Name, string CharacterName, bool IsEnabled);
|
using IPCProfileDataTuple = (System.Guid UniqueId, string Name, string CharacterName, bool IsEnabled);
|
||||||
|
using OtterGui.Log;
|
||||||
|
using CustomizePlus.Core.Extensions;
|
||||||
|
|
||||||
namespace CustomizePlus.UI.Windows.MainWindow.Tabs.Debug;
|
namespace CustomizePlus.UI.Windows.MainWindow.Tabs.Debug;
|
||||||
|
|
||||||
@@ -28,6 +30,7 @@ public class IPCTestTab //: IDisposable
|
|||||||
private readonly GameObjectService _gameObjectService;
|
private readonly GameObjectService _gameObjectService;
|
||||||
private readonly ObjectManager _objectManager;
|
private readonly ObjectManager _objectManager;
|
||||||
private readonly ActorManager _actorManager;
|
private readonly ActorManager _actorManager;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
[EzIPC("General.GetApiVersion")]
|
[EzIPC("General.GetApiVersion")]
|
||||||
private readonly Func<(int, int)> _getApiVersionIpcFunc;
|
private readonly Func<(int, int)> _getApiVersionIpcFunc;
|
||||||
@@ -44,9 +47,24 @@ public class IPCTestTab //: IDisposable
|
|||||||
[EzIPC("Profile.DisableByUniqueId")]
|
[EzIPC("Profile.DisableByUniqueId")]
|
||||||
private readonly Action<Guid> _disableProfileByUniqueIdIpcFunc;
|
private readonly Action<Guid> _disableProfileByUniqueIdIpcFunc;
|
||||||
|
|
||||||
private readonly ICallGateSubscriber<string, Character?, object>? _setCharacterProfile;
|
[EzIPC("Profile.GetCurrentlyActiveProfileOnCharacter")]
|
||||||
private readonly ICallGateSubscriber<Character?, string>? _getProfileFromCharacter;
|
private readonly Func<Character, (int, string?)> _getCurrentlyActiveProfileOnCharacterIpcFunc;
|
||||||
private readonly ICallGateSubscriber<Character?, object>? _revertCharacter;
|
|
||||||
|
[EzIPC("Profile.SetTemporaryProfileOnCharacter")]
|
||||||
|
private readonly Func<Character, string, (int, Guid?)> _setTemporaryProfileOnCharacterIpcFunc;
|
||||||
|
|
||||||
|
[EzIPC("Profile.DeleteTemporaryProfileOnCharacter")]
|
||||||
|
private readonly Func<Character, int> _deleteTemporaryProfileOnCharacterIpcFunc;
|
||||||
|
|
||||||
|
[EzIPC("Profile.DeleteTemporaryProfileByUniqueId")]
|
||||||
|
private readonly Func<Guid, int> _deleteTemporaryProfileByUniqueIdIpcFunc;
|
||||||
|
|
||||||
|
[EzIPC("Profile.GetProfileById")]
|
||||||
|
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 readonly ICallGateSubscriber<string?, string?, object?>? _onProfileUpdate;
|
||||||
|
|
||||||
private string? _rememberedProfileJson;
|
private string? _rememberedProfileJson;
|
||||||
@@ -66,7 +84,8 @@ public class IPCTestTab //: IDisposable
|
|||||||
PopupSystem popupSystem,
|
PopupSystem popupSystem,
|
||||||
ObjectManager objectManager,
|
ObjectManager objectManager,
|
||||||
GameObjectService gameObjectService,
|
GameObjectService gameObjectService,
|
||||||
ActorManager actorManager)
|
ActorManager actorManager,
|
||||||
|
Logger logger)
|
||||||
{
|
{
|
||||||
_objectTable = objectTable;
|
_objectTable = objectTable;
|
||||||
_profileManager = profileManager;
|
_profileManager = profileManager;
|
||||||
@@ -74,15 +93,16 @@ public class IPCTestTab //: IDisposable
|
|||||||
_objectManager = objectManager;
|
_objectManager = objectManager;
|
||||||
_gameObjectService = gameObjectService;
|
_gameObjectService = gameObjectService;
|
||||||
_actorManager = actorManager;
|
_actorManager = actorManager;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
EzIPC.Init(this, "CustomizePlus");
|
EzIPC.Init(this, "CustomizePlus");
|
||||||
|
|
||||||
if (_getApiVersionIpcFunc != null)
|
if (_getApiVersionIpcFunc != null)
|
||||||
_apiVersion = _getApiVersionIpcFunc();
|
_apiVersion = _getApiVersionIpcFunc();
|
||||||
|
|
||||||
_setCharacterProfile = pluginInterface.GetIpcSubscriber<string, Character?, object>("CustomizePlus.SetProfileToCharacter");
|
//_setCharacterProfile = pluginInterface.GetIpcSubscriber<string, Character?, object>("CustomizePlus.SetProfileToCharacter");
|
||||||
_getProfileFromCharacter = pluginInterface.GetIpcSubscriber<Character?, string>("CustomizePlus.GetProfileFromCharacter");
|
//_getProfileFromCharacter = pluginInterface.GetIpcSubscriber<Character?, string>("CustomizePlus.GetProfileFromCharacter");
|
||||||
_revertCharacter = pluginInterface.GetIpcSubscriber<Character?, object>("CustomizePlus.RevertCharacter");
|
//_revertCharacter = pluginInterface.GetIpcSubscriber<Character?, object>("CustomizePlus.RevertCharacter");
|
||||||
/*_onProfileUpdate = pluginInterface.GetIpcSubscriber<string?, string?, object?>("CustomizePlus.OnProfileUpdate");
|
/*_onProfileUpdate = pluginInterface.GetIpcSubscriber<string?, string?, object?>("CustomizePlus.OnProfileUpdate");
|
||||||
_onProfileUpdate.Subscribe(OnProfileUpdate);*/
|
_onProfileUpdate.Subscribe(OnProfileUpdate);*/
|
||||||
}
|
}
|
||||||
@@ -141,37 +161,59 @@ public class IPCTestTab //: IDisposable
|
|||||||
_popupSystem.ShowPopup(PopupSystem.Messages.IPCV4ProfileRemembered);
|
_popupSystem.ShowPopup(PopupSystem.Messages.IPCV4ProfileRemembered);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.Button("GetProfileFromCharacter into memory"))
|
if (ImGui.Button("GetCurrentlyActiveProfileOnCharacter into memory"))
|
||||||
{
|
{
|
||||||
var actors = _gameObjectService.FindActorsByName(_targetCharacterName).ToList();
|
var actors = _gameObjectService.FindActorsByName(_targetCharacterName).ToList();
|
||||||
if (actors.Count == 0)
|
if (actors.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_rememberedProfileJson = _getProfileFromCharacter!.InvokeFunc(FindCharacterByAddress(actors[0].Item2.Address));
|
(int result, _rememberedProfileJson) = _getCurrentlyActiveProfileOnCharacterIpcFunc(FindCharacterByAddress(actors[0].Item2.Address));
|
||||||
|
|
||||||
|
if(result == 0)
|
||||||
_popupSystem.ShowPopup(PopupSystem.Messages.IPCGetProfileFromChrRemembered);
|
_popupSystem.ShowPopup(PopupSystem.Messages.IPCGetProfileFromChrRemembered);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Error($"Error code {result} while calling GetCurrentlyActiveProfileOnCharacter");
|
||||||
|
_popupSystem.ShowPopup(PopupSystem.Messages.ActionError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var disabled = ImRaii.Disabled(_rememberedProfileJson == null))
|
using (var disabled = ImRaii.Disabled(_rememberedProfileJson == null))
|
||||||
{
|
{
|
||||||
if (ImGui.Button("SetProfileToCharacter from memory") && _rememberedProfileJson != null)
|
if (ImGui.Button("SetTemporaryProfileOnCharacter from memory") && _rememberedProfileJson != null)
|
||||||
{
|
{
|
||||||
var actors = _gameObjectService.FindActorsByName(_targetCharacterName).ToList();
|
var actors = _gameObjectService.FindActorsByName(_targetCharacterName).ToList();
|
||||||
if (actors.Count == 0)
|
if (actors.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_setCharacterProfile!.InvokeAction(_rememberedProfileJson, FindCharacterByAddress(actors[0].Item2.Address));
|
(int result, Guid? profileGuid) = _setTemporaryProfileOnCharacterIpcFunc(FindCharacterByAddress(actors[0].Item2.Address),_rememberedProfileJson);
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
_popupSystem.ShowPopup(PopupSystem.Messages.IPCSetProfileToChrDone);
|
_popupSystem.ShowPopup(PopupSystem.Messages.IPCSetProfileToChrDone);
|
||||||
|
_logger.Information($"Temporary profile id: {profileGuid}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Error($"Error code {result} while calling SetTemporaryProfileOnCharacter");
|
||||||
|
_popupSystem.ShowPopup(PopupSystem.Messages.ActionError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.Button("RevertCharacter") && _rememberedProfileJson != null)
|
if (ImGui.Button("DeleteTemporaryProfileOnCharacter"))
|
||||||
{
|
{
|
||||||
var actors = _gameObjectService.FindActorsByName(_targetCharacterName).ToList();
|
var actors = _gameObjectService.FindActorsByName(_targetCharacterName).ToList();
|
||||||
if (actors.Count == 0)
|
if (actors.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_revertCharacter!.InvokeAction(FindCharacterByAddress(actors[0].Item2.Address));
|
int result = _deleteTemporaryProfileOnCharacterIpcFunc(FindCharacterByAddress(actors[0].Item2.Address));
|
||||||
|
if (result == 0)
|
||||||
_popupSystem.ShowPopup(PopupSystem.Messages.IPCRevertDone);
|
_popupSystem.ShowPopup(PopupSystem.Messages.IPCRevertDone);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Error($"Error code {result} while calling DeleteTemporaryProfileOnCharacter");
|
||||||
|
_popupSystem.ShowPopup(PopupSystem.Messages.ActionError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
@@ -179,13 +221,28 @@ public class IPCTestTab //: IDisposable
|
|||||||
if (ImGui.Button("Copy user profile list to clipboard"))
|
if (ImGui.Button("Copy user profile list to clipboard"))
|
||||||
{
|
{
|
||||||
ImGui.SetClipboardText(string.Join("\n", _getProfileListIpcFunc().Select(x => $"{x.UniqueId}, {x.Name}, {x.CharacterName}, {x.IsEnabled}")));
|
ImGui.SetClipboardText(string.Join("\n", _getProfileListIpcFunc().Select(x => $"{x.UniqueId}, {x.Name}, {x.CharacterName}, {x.IsEnabled}")));
|
||||||
_popupSystem.ShowPopup(PopupSystem.Messages.IPCProfileListCopied);
|
_popupSystem.ShowPopup(PopupSystem.Messages.IPCCopiedToClipboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Text("Profile Unique ID to set:");
|
ImGui.Text("Profile Unique ID:");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.InputText("##profileguid", ref _targetProfileId, 128);
|
ImGui.InputText("##profileguid", ref _targetProfileId, 128);
|
||||||
|
|
||||||
|
if (ImGui.Button("Get profile by Unique ID"))
|
||||||
|
{
|
||||||
|
(int result, string? profileJson) = _getProfileByIdIpcFunc(Guid.Parse(_targetProfileId));
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
|
ImGui.SetClipboardText(profileJson);
|
||||||
|
_popupSystem.ShowPopup(PopupSystem.Messages.IPCCopiedToClipboard);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Error($"Error code {result} while calling GetProfileById");
|
||||||
|
_popupSystem.ShowPopup(PopupSystem.Messages.ActionError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGui.Button("Enable profile by Unique ID"))
|
if (ImGui.Button("Enable profile by Unique ID"))
|
||||||
{
|
{
|
||||||
_enableProfileByUniqueIdIpcFunc(Guid.Parse(_targetProfileId));
|
_enableProfileByUniqueIdIpcFunc(Guid.Parse(_targetProfileId));
|
||||||
@@ -197,6 +254,28 @@ public class IPCTestTab //: IDisposable
|
|||||||
_disableProfileByUniqueIdIpcFunc(Guid.Parse(_targetProfileId));
|
_disableProfileByUniqueIdIpcFunc(Guid.Parse(_targetProfileId));
|
||||||
_popupSystem.ShowPopup(PopupSystem.Messages.IPCDisableProfileByIdDone);
|
_popupSystem.ShowPopup(PopupSystem.Messages.IPCDisableProfileByIdDone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui.Button("DeleteTemporaryProfileByUniqueId"))
|
||||||
|
{
|
||||||
|
var actors = _gameObjectService.FindActorsByName(_targetCharacterName).ToList();
|
||||||
|
if (actors.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int result = _deleteTemporaryProfileByUniqueIdIpcFunc(Guid.Parse(_targetProfileId));
|
||||||
|
if (result == 0)
|
||||||
|
_popupSystem.ShowPopup(PopupSystem.Messages.IPCRevertDone);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Error($"Error code {result} while calling DeleteTemporaryProfileByUniqueId");
|
||||||
|
_popupSystem.ShowPopup(PopupSystem.Messages.ActionError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[EzIPCEvent("Profile.OnUpdate")]
|
||||||
|
private void OnProfileUpdate(Character Character, Guid? ProfileUniqueId)
|
||||||
|
{
|
||||||
|
_logger.Debug($"IPC Test Tab - OnProfileUpdate: Character: {Character.Name.ToString().Incognify()}, Profile ID: {(ProfileUniqueId != null ? ProfileUniqueId.ToString() : "no id")}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Character? FindCharacterByAddress(nint address)
|
private Character? FindCharacterByAddress(nint address)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public partial class PopupSystem
|
|||||||
public const string IPCGetProfileFromChrRemembered = "ipc_get_profile_from_character_remembered";
|
public const string IPCGetProfileFromChrRemembered = "ipc_get_profile_from_character_remembered";
|
||||||
public const string IPCSetProfileToChrDone = "ipc_set_profile_to_character_done";
|
public const string IPCSetProfileToChrDone = "ipc_set_profile_to_character_done";
|
||||||
public const string IPCRevertDone = "ipc_revert_done";
|
public const string IPCRevertDone = "ipc_revert_done";
|
||||||
public const string IPCProfileListCopied = "ipc_profile_list_copied";
|
public const string IPCCopiedToClipboard = "ipc_copied_to clipboard";
|
||||||
public const string IPCEnableProfileByIdDone = "ipc_enable_profile_by_id_done";
|
public const string IPCEnableProfileByIdDone = "ipc_enable_profile_by_id_done";
|
||||||
public const string IPCDisableProfileByIdDone = "ipc_disable_profile_by_id_done";
|
public const string IPCDisableProfileByIdDone = "ipc_disable_profile_by_id_done";
|
||||||
|
|
||||||
@@ -32,9 +32,9 @@ public partial class PopupSystem
|
|||||||
|
|
||||||
RegisterPopup(Messages.IPCV4ProfileRemembered, "Current profile has been copied into memory");
|
RegisterPopup(Messages.IPCV4ProfileRemembered, "Current profile has been copied into memory");
|
||||||
RegisterPopup(Messages.IPCGetProfileFromChrRemembered, "GetProfileFromCharacter result has been copied into memory");
|
RegisterPopup(Messages.IPCGetProfileFromChrRemembered, "GetProfileFromCharacter result has been copied into memory");
|
||||||
RegisterPopup(Messages.IPCSetProfileToChrDone, "SetProfileToCharacter has been called with data from memory");
|
RegisterPopup(Messages.IPCSetProfileToChrDone, "SetProfileToCharacter has been called with data from memory, profile id printed to log");
|
||||||
RegisterPopup(Messages.IPCRevertDone, "Revert has been called");
|
RegisterPopup(Messages.IPCRevertDone, "Revert has been called");
|
||||||
RegisterPopup(Messages.IPCProfileListCopied, "Profile list copied into clipboard");
|
RegisterPopup(Messages.IPCCopiedToClipboard, "Copied into clipboard");
|
||||||
RegisterPopup(Messages.IPCEnableProfileByIdDone, "Enable profile by id has been called");
|
RegisterPopup(Messages.IPCEnableProfileByIdDone, "Enable profile by id has been called");
|
||||||
RegisterPopup(Messages.IPCDisableProfileByIdDone, "Disable profile by id has been called");
|
RegisterPopup(Messages.IPCDisableProfileByIdDone, "Disable profile by id has been called");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user