ArmatureChanged.Type.Rebound -> Updated IsPendingProfileRebind is now being set instead of calling RebuildBoneTemplateBinding and that function is called as a part of rebind process (not always though, pending rewrite) Profiles can no longer be toggled in the UI while editor is active ProfileChanged is no longer used in IPC, instead we are fully relying on armature events Added warnings to legacy IPC messages to not use it
292 lines
10 KiB
C#
292 lines
10 KiB
C#
using Dalamud.Plugin.Services;
|
|
using Dalamud.Plugin;
|
|
using System;
|
|
using System.Text;
|
|
using OtterGui.Log;
|
|
using Newtonsoft.Json;
|
|
using System.IO.Compression;
|
|
using System.IO;
|
|
using Dalamud.Plugin.Ipc;
|
|
using Dalamud.Game.ClientState.Objects.Types;
|
|
using CustomizePlus.Configuration.Helpers;
|
|
using CustomizePlus.Profiles;
|
|
using CustomizePlus.Configuration.Data.Version3;
|
|
using CustomizePlus.Profiles.Data;
|
|
using CustomizePlus.Game.Services;
|
|
using CustomizePlus.Templates.Events;
|
|
using CustomizePlus.Profiles.Events;
|
|
using CustomizePlus.Templates.Data;
|
|
using CustomizePlus.GameData.Data;
|
|
using CustomizePlus.Core.Extensions;
|
|
using CustomizePlus.Armatures.Events;
|
|
using CustomizePlus.Armatures.Data;
|
|
using CustomizePlus.GameData.Extensions;
|
|
using Penumbra.GameData.Interop;
|
|
|
|
namespace CustomizePlus.Api.Compatibility;
|
|
|
|
[Obsolete("Will be removed in the near future, do not use this IPC")]
|
|
public class CustomizePlusLegacyIpc : IDisposable
|
|
{
|
|
private readonly IObjectTable _objectTable;
|
|
private readonly DalamudPluginInterface _pluginInterface;
|
|
private readonly Logger _logger;
|
|
private readonly ProfileManager _profileManager;
|
|
private readonly GameObjectService _gameObjectService;
|
|
|
|
private readonly ProfileChanged _profileChangedEvent;
|
|
private readonly ArmatureChanged _armatureChangedEvent;
|
|
|
|
private const int _configurationVersion = 3;
|
|
|
|
public const string ProviderApiVersionLabel = $"CustomizePlus.{nameof(GetApiVersion)}";
|
|
public const string GetProfileFromCharacterLabel = $"CustomizePlus.{nameof(GetProfileFromCharacter)}";
|
|
public const string SetProfileToCharacterLabel = $"CustomizePlus.{nameof(SetProfileToCharacter)}";
|
|
public const string RevertCharacterLabel = $"CustomizePlus.{nameof(RevertCharacter)}";
|
|
public const string OnProfileUpdateLabel = $"CustomizePlus.{nameof(OnProfileUpdate)}";
|
|
public static readonly (int, int) ApiVersion = (3, 0);
|
|
|
|
//Sends local player's profile every time their active profile is changed
|
|
//If no profile is applied sends null
|
|
internal ICallGateProvider<string?, string?, object?>? ProviderOnProfileUpdate;
|
|
internal ICallGateProvider<Character?, object>? ProviderRevertCharacter;
|
|
internal ICallGateProvider<string, Character?, object>? ProviderSetProfileToCharacter;
|
|
internal ICallGateProvider<Character?, string?>? ProviderGetProfileFromCharacter;
|
|
internal ICallGateProvider<(int, int)>? ProviderGetApiVersion;
|
|
|
|
public CustomizePlusLegacyIpc(
|
|
IObjectTable objectTable,
|
|
DalamudPluginInterface pluginInterface,
|
|
Logger logger,
|
|
ProfileManager profileManager,
|
|
GameObjectService gameObjectService,
|
|
ArmatureChanged armatureChangedEvent,
|
|
ProfileChanged profileChangedEvent)
|
|
{
|
|
_objectTable = objectTable;
|
|
_pluginInterface = pluginInterface;
|
|
_logger = logger;
|
|
_profileManager = profileManager;
|
|
_gameObjectService = gameObjectService;
|
|
_profileChangedEvent = profileChangedEvent;
|
|
_armatureChangedEvent = armatureChangedEvent;
|
|
|
|
InitializeProviders();
|
|
|
|
_profileChangedEvent.Subscribe(OnProfileChange, ProfileChanged.Priority.CustomizePlusLegacyIpc);
|
|
_armatureChangedEvent.Subscribe(OnArmatureChanged, ArmatureChanged.Priority.CustomizePlusIpc);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_profileChangedEvent.Unsubscribe(OnProfileChange);
|
|
_armatureChangedEvent.Unsubscribe(OnArmatureChanged);
|
|
DisposeProviders();
|
|
}
|
|
|
|
//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)
|
|
return;
|
|
|
|
if (profile == null ||
|
|
!profile.Enabled ||
|
|
profile.CharacterName.Text != _gameObjectService.GetCurrentPlayerName())
|
|
return;
|
|
|
|
OnProfileUpdate(profile);
|
|
}
|
|
|
|
private void OnArmatureChanged(ArmatureChanged.Type type, Armature armature, object? arg3)
|
|
{
|
|
string currentPlayerName = _gameObjectService.GetCurrentPlayerName();
|
|
|
|
if (armature.ActorIdentifier.ToNameWithoutOwnerName() != currentPlayerName)
|
|
return;
|
|
|
|
if (type == ArmatureChanged.Type.Created ||
|
|
type == ArmatureChanged.Type.Updated)
|
|
{
|
|
if(armature.Profile == null)
|
|
_logger.Warning("[LEGACY IPC DO NOT USE] Armature created/rebound and profile is null");
|
|
|
|
OnProfileUpdate(armature.Profile);
|
|
return;
|
|
}
|
|
|
|
if(type == ArmatureChanged.Type.Deleted)
|
|
{
|
|
OnProfileUpdate(null);
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void InitializeProviders()
|
|
{
|
|
_logger.Debug("[LEGACY IPC DO NOT USE] Initializing legacy Customize+ IPC providers.");
|
|
try
|
|
{
|
|
ProviderGetApiVersion = _pluginInterface.GetIpcProvider<(int, int)>(ProviderApiVersionLabel);
|
|
ProviderGetApiVersion.RegisterFunc(GetApiVersion);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error($"[LEGACY IPC DO NOT USE] Error registering legacy Customize+ IPC provider for {ProviderApiVersionLabel}: {ex}");
|
|
}
|
|
|
|
try
|
|
{
|
|
ProviderGetProfileFromCharacter =
|
|
_pluginInterface.GetIpcProvider<Character?, string?>(GetProfileFromCharacterLabel);
|
|
ProviderGetProfileFromCharacter.RegisterFunc(GetProfileFromCharacter);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error($"[LEGACY IPC DO NOT USE] Error registering legacy Customize+ IPC provider for {GetProfileFromCharacterLabel}: {ex}");
|
|
}
|
|
|
|
try
|
|
{
|
|
ProviderSetProfileToCharacter =
|
|
_pluginInterface.GetIpcProvider<string, Character?, object>(SetProfileToCharacterLabel);
|
|
ProviderSetProfileToCharacter.RegisterAction(SetProfileToCharacter);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error($"[LEGACY IPC DO NOT USE] Error registering legacy Customize+ IPC provider for {SetProfileToCharacterLabel}: {ex}");
|
|
}
|
|
|
|
try
|
|
{
|
|
ProviderRevertCharacter =
|
|
_pluginInterface.GetIpcProvider<Character?, object>(RevertCharacterLabel);
|
|
ProviderRevertCharacter.RegisterAction(RevertCharacter);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error($"[LEGACY IPC DO NOT USE] Error registering legacy Customize+ IPC provider for {RevertCharacterLabel}: {ex}");
|
|
}
|
|
|
|
try
|
|
{
|
|
ProviderOnProfileUpdate = _pluginInterface.GetIpcProvider<string?, string?, object?>(OnProfileUpdateLabel);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error($"[LEGACY IPC DO NOT USE] Error registering legacy Customize+ IPC provider for {OnProfileUpdateLabel}: {ex}");
|
|
}
|
|
}
|
|
|
|
private void DisposeProviders()
|
|
{
|
|
ProviderGetProfileFromCharacter?.UnregisterFunc();
|
|
ProviderSetProfileToCharacter?.UnregisterAction();
|
|
ProviderRevertCharacter?.UnregisterAction();
|
|
ProviderGetApiVersion?.UnregisterFunc();
|
|
ProviderOnProfileUpdate?.UnregisterFunc();
|
|
}
|
|
|
|
private void OnProfileUpdate(Profile? profile)
|
|
{
|
|
_logger.Debug($"[LEGACY IPC DO NOT USE] Sending local player update message: {(profile != null ? profile.ToString() : "no profile")}");
|
|
|
|
var convertedProfile = profile != null ? GetVersion3Profile(profile) : null;
|
|
|
|
ProviderOnProfileUpdate?.SendMessage(convertedProfile?.CharacterName ?? null, convertedProfile == null ? null : JsonConvert.SerializeObject(convertedProfile));
|
|
}
|
|
|
|
private static (int, int) GetApiVersion()
|
|
{
|
|
return ApiVersion;
|
|
}
|
|
|
|
private string? GetCharacterProfile(string characterName)
|
|
{
|
|
var profile = _profileManager.GetProfileByCharacterName(characterName, true);
|
|
|
|
var convertedProfile = profile != null ? GetVersion3Profile(profile) : null;
|
|
|
|
return convertedProfile != null ? JsonConvert.SerializeObject(convertedProfile) : null;
|
|
}
|
|
|
|
private string? GetProfileFromCharacter(Character? character)
|
|
{
|
|
return character == null ? null : GetCharacterProfile(character.Name.ToString());
|
|
}
|
|
|
|
private void SetProfileToCharacter(string profileJson, Character? character)
|
|
{
|
|
if (character == null)
|
|
return;
|
|
|
|
var actor = (Actor)character.Address;
|
|
if (!actor.Valid)
|
|
return;
|
|
|
|
/*if (character == _objectTable[0])
|
|
{
|
|
_logger.Error($"Received request to set profile on local character, this is not allowed");
|
|
return;
|
|
}*/
|
|
|
|
try
|
|
{
|
|
var profile = JsonConvert.DeserializeObject<Version3Profile>(profileJson);
|
|
if (profile != null)
|
|
{
|
|
if (profile.ConfigVersion != _configurationVersion)
|
|
throw new Exception("Incompatible version");
|
|
|
|
_profileManager.AddTemporaryProfile(GetProfileFromVersion3(profile).Item1, actor);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Warning($"[LEGACY IPC DO NOT USE] Unable to set body profile. Character: {character?.Name}, exception: {ex}, debug data: {GetBase64String(profileJson)}");
|
|
}
|
|
}
|
|
|
|
private void RevertCharacter(Character? character)
|
|
{
|
|
if (character == null)
|
|
return;
|
|
|
|
var actor = (Actor)character.Address;
|
|
if (!actor.Valid)
|
|
return;
|
|
|
|
/*if (character == _objectTable[0])
|
|
{
|
|
_logger.Error($"Received request to revert profile on local character, this is not allowed");
|
|
return;
|
|
}*/
|
|
|
|
_profileManager.RemoveTemporaryProfile(actor);
|
|
}
|
|
|
|
private string GetBase64String(string data)
|
|
{
|
|
var json = JsonConvert.SerializeObject(data, Formatting.None);
|
|
var bytes = Encoding.UTF8.GetBytes(json);
|
|
using var compressedStream = new MemoryStream();
|
|
using (var zipStream = new GZipStream(compressedStream, CompressionMode.Compress))
|
|
zipStream.Write(bytes, 0, bytes.Length);
|
|
|
|
return Convert.ToBase64String(compressedStream.ToArray());
|
|
}
|
|
|
|
private Version3Profile GetVersion3Profile(Profile profile)
|
|
{
|
|
return V4ProfileToV3Converter.Convert(profile);
|
|
}
|
|
|
|
private (Profile, Template) GetProfileFromVersion3(Version3Profile profile)
|
|
{
|
|
return V3ProfileToV4Converter.Convert(profile);
|
|
}
|
|
}
|