refactor a little bit

This commit is contained in:
rootdarkarchon
2024-02-29 01:16:01 +01:00
committed by Loporrit
parent 6ab9812f9a
commit dd878e6e36
21 changed files with 1122 additions and 923 deletions

View File

@@ -1,4 +1,4 @@
using MareSynchronos.Interop; using MareSynchronos.Interop.Ipc;
using MareSynchronos.MareConfiguration; using MareSynchronos.MareConfiguration;
using MareSynchronos.Services; using MareSynchronos.Services;
using MareSynchronos.Services.Mediator; using MareSynchronos.Services.Mediator;
@@ -33,7 +33,7 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase
_fileCompactor = fileCompactor; _fileCompactor = fileCompactor;
Mediator.Subscribe<PenumbraInitializedMessage>(this, (_) => Mediator.Subscribe<PenumbraInitializedMessage>(this, (_) =>
{ {
StartPenumbraWatcher(_ipcManager.PenumbraModDirectory); StartPenumbraWatcher(_ipcManager.Penumbra.PenumbraModDirectory);
StartMareWatcher(configService.Current.CacheFolder); StartMareWatcher(configService.Current.CacheFolder);
InvokeScan(); InvokeScan();
}); });
@@ -42,7 +42,7 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase
Mediator.Subscribe<DalamudLoginMessage>(this, (_) => Mediator.Subscribe<DalamudLoginMessage>(this, (_) =>
{ {
StartMareWatcher(configService.Current.CacheFolder); StartMareWatcher(configService.Current.CacheFolder);
StartPenumbraWatcher(_ipcManager.PenumbraModDirectory); StartPenumbraWatcher(_ipcManager.Penumbra.PenumbraModDirectory);
InvokeScan(); InvokeScan();
}); });
Mediator.Subscribe<PenumbraDirectoryChangedMessage>(this, (msg) => Mediator.Subscribe<PenumbraDirectoryChangedMessage>(this, (msg) =>
@@ -50,9 +50,9 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase
StartPenumbraWatcher(msg.ModDirectory); StartPenumbraWatcher(msg.ModDirectory);
InvokeScan(); InvokeScan();
}); });
if (_ipcManager.CheckPenumbraApi() && !string.IsNullOrEmpty(_ipcManager.PenumbraModDirectory)) if (_ipcManager.Penumbra.APIAvailable && !string.IsNullOrEmpty(_ipcManager.Penumbra.PenumbraModDirectory))
{ {
StartPenumbraWatcher(_ipcManager.PenumbraModDirectory); StartPenumbraWatcher(_ipcManager.Penumbra.PenumbraModDirectory);
} }
if (configService.Current.HasValidSetup()) if (configService.Current.HasValidSetup())
{ {
@@ -466,7 +466,7 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase
private void FullFileScan(CancellationToken ct) private void FullFileScan(CancellationToken ct)
{ {
TotalFiles = 1; TotalFiles = 1;
var penumbraDir = _ipcManager.PenumbraModDirectory; var penumbraDir = _ipcManager.Penumbra.PenumbraModDirectory;
bool penDirExists = true; bool penDirExists = true;
bool cacheDirExists = true; bool cacheDirExists = true;
if (string.IsNullOrEmpty(penumbraDir) || !Directory.Exists(penumbraDir)) if (string.IsNullOrEmpty(penumbraDir) || !Directory.Exists(penumbraDir))
@@ -558,7 +558,7 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase
{ {
if (ct.IsCancellationRequested) return; if (ct.IsCancellationRequested) return;
if (!_ipcManager.CheckPenumbraApi()) if (!_ipcManager.Penumbra.APIAvailable)
{ {
Logger.LogWarning("Penumbra not available"); Logger.LogWarning("Penumbra not available");
return; return;
@@ -605,7 +605,7 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase
Logger.LogTrace("Threads exited"); Logger.LogTrace("Threads exited");
if (!_ipcManager.CheckPenumbraApi()) if (!_ipcManager.Penumbra.APIAvailable)
{ {
Logger.LogWarning("Penumbra not available"); Logger.LogWarning("Penumbra not available");
return; return;
@@ -628,7 +628,7 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase
Logger.LogTrace("Scanner validated existing db files"); Logger.LogTrace("Scanner validated existing db files");
if (!_ipcManager.CheckPenumbraApi()) if (!_ipcManager.Penumbra.APIAvailable)
{ {
Logger.LogWarning("Penumbra not available"); Logger.LogWarning("Penumbra not available");
return; return;
@@ -648,7 +648,7 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase
{ {
if (ct.IsCancellationRequested) return; if (ct.IsCancellationRequested) return;
if (!_ipcManager.CheckPenumbraApi()) if (!_ipcManager.Penumbra.APIAvailable)
{ {
Logger.LogWarning("Penumbra not available"); Logger.LogWarning("Penumbra not available");
return; return;

View File

@@ -1,5 +1,5 @@
using K4os.Compression.LZ4.Streams; using K4os.Compression.LZ4.Streams;
using MareSynchronos.Interop; using MareSynchronos.Interop.Ipc;
using MareSynchronos.MareConfiguration; using MareSynchronos.MareConfiguration;
using MareSynchronos.Services.Mediator; using MareSynchronos.Services.Mediator;
using MareSynchronos.Utils; using MareSynchronos.Utils;
@@ -53,8 +53,8 @@ public sealed class FileCacheManager : IHostedService
if (!fi.Exists) return null; if (!fi.Exists) return null;
_logger.LogTrace("Creating file entry for {path}", path); _logger.LogTrace("Creating file entry for {path}", path);
var fullName = fi.FullName.ToLowerInvariant(); var fullName = fi.FullName.ToLowerInvariant();
if (!fullName.Contains(_ipcManager.PenumbraModDirectory!.ToLowerInvariant(), StringComparison.Ordinal)) return null; if (!fullName.Contains(_ipcManager.Penumbra.PenumbraModDirectory!.ToLowerInvariant(), StringComparison.Ordinal)) return null;
string prefixedPath = fullName.Replace(_ipcManager.PenumbraModDirectory!.ToLowerInvariant(), PenumbraPrefix + "\\", StringComparison.Ordinal).Replace("\\\\", "\\", StringComparison.Ordinal); string prefixedPath = fullName.Replace(_ipcManager.Penumbra.PenumbraModDirectory!.ToLowerInvariant(), PenumbraPrefix + "\\", StringComparison.Ordinal).Replace("\\\\", "\\", StringComparison.Ordinal);
return CreateFileCacheEntity(fi, prefixedPath); return CreateFileCacheEntity(fi, prefixedPath);
} }
@@ -173,7 +173,8 @@ public sealed class FileCacheManager : IHostedService
private FileCacheEntity? GetFileCacheByPath(string path) private FileCacheEntity? GetFileCacheByPath(string path)
{ {
var cleanedPath = path.Replace("/", "\\", StringComparison.OrdinalIgnoreCase).ToLowerInvariant().Replace(_ipcManager.PenumbraModDirectory!.ToLowerInvariant(), "", StringComparison.OrdinalIgnoreCase); var cleanedPath = path.Replace("/", "\\", StringComparison.OrdinalIgnoreCase).ToLowerInvariant()
.Replace(_ipcManager.Penumbra.PenumbraModDirectory!.ToLowerInvariant(), "", StringComparison.OrdinalIgnoreCase);
var entry = _fileCaches.SelectMany(v => v.Value).FirstOrDefault(f => f.ResolvedFilepath.EndsWith(cleanedPath, StringComparison.OrdinalIgnoreCase)); var entry = _fileCaches.SelectMany(v => v.Value).FirstOrDefault(f => f.ResolvedFilepath.EndsWith(cleanedPath, StringComparison.OrdinalIgnoreCase));
if (entry == null) if (entry == null)
@@ -195,7 +196,7 @@ public sealed class FileCacheManager : IHostedService
{ {
var cleanedPaths = paths.Distinct(StringComparer.OrdinalIgnoreCase).ToDictionary(p => p, var cleanedPaths = paths.Distinct(StringComparer.OrdinalIgnoreCase).ToDictionary(p => p,
p => p.Replace("/", "\\", StringComparison.OrdinalIgnoreCase) p => p.Replace("/", "\\", StringComparison.OrdinalIgnoreCase)
.Replace(_ipcManager.PenumbraModDirectory!, _ipcManager.PenumbraModDirectory!.EndsWith('\\') ? PenumbraPrefix + '\\' : PenumbraPrefix, StringComparison.OrdinalIgnoreCase) .Replace(_ipcManager.Penumbra.PenumbraModDirectory!, _ipcManager.Penumbra.PenumbraModDirectory!.EndsWith('\\') ? PenumbraPrefix + '\\' : PenumbraPrefix, StringComparison.OrdinalIgnoreCase)
.Replace(_configService.Current.CacheFolder, _configService.Current.CacheFolder.EndsWith('\\') ? CachePrefix + '\\' : CachePrefix, StringComparison.OrdinalIgnoreCase) .Replace(_configService.Current.CacheFolder, _configService.Current.CacheFolder.EndsWith('\\') ? CachePrefix + '\\' : CachePrefix, StringComparison.OrdinalIgnoreCase)
.Replace("\\\\", "\\", StringComparison.Ordinal), .Replace("\\\\", "\\", StringComparison.Ordinal),
StringComparer.OrdinalIgnoreCase); StringComparer.OrdinalIgnoreCase);
@@ -367,7 +368,7 @@ public sealed class FileCacheManager : IHostedService
{ {
if (fileCache.PrefixedFilePath.StartsWith(PenumbraPrefix, StringComparison.OrdinalIgnoreCase)) if (fileCache.PrefixedFilePath.StartsWith(PenumbraPrefix, StringComparison.OrdinalIgnoreCase))
{ {
fileCache.SetResolvedFilePath(fileCache.PrefixedFilePath.Replace(PenumbraPrefix, _ipcManager.PenumbraModDirectory, StringComparison.Ordinal)); fileCache.SetResolvedFilePath(fileCache.PrefixedFilePath.Replace(PenumbraPrefix, _ipcManager.Penumbra.PenumbraModDirectory, StringComparison.Ordinal));
} }
else if (fileCache.PrefixedFilePath.StartsWith(CachePrefix, StringComparison.OrdinalIgnoreCase)) else if (fileCache.PrefixedFilePath.StartsWith(CachePrefix, StringComparison.OrdinalIgnoreCase))
{ {

View File

@@ -0,0 +1,7 @@
namespace MareSynchronos.Interop.Ipc;
public interface IIpcCaller : IDisposable
{
bool APIAvailable { get; }
void CheckAPI();
}

View File

@@ -0,0 +1,139 @@
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using Dalamud.Utility;
using MareSynchronos.Services;
using MareSynchronos.Services.Mediator;
using Microsoft.Extensions.Logging;
using System.Text;
namespace MareSynchronos.Interop.Ipc;
public sealed class IpcCallerCustomize : IIpcCaller
{
private readonly ICallGateSubscriber<(int, int)> _customizePlusApiVersion;
private readonly ICallGateSubscriber<ushort, (int, Guid?)> _customizePlusGetActiveProfile;
private readonly ICallGateSubscriber<Guid, (int, string?)> _customizePlusGetProfileById;
private readonly ICallGateSubscriber<ushort, Guid, object> _customizePlusOnScaleUpdate;
private readonly ICallGateSubscriber<ushort, int> _customizePlusRevertCharacter;
private readonly ICallGateSubscriber<ushort, string, (int, Guid?)> _customizePlusSetBodyScaleToCharacter;
private readonly ICallGateSubscriber<Guid, int> _customizePlusDeleteByUniqueId;
private readonly ILogger<IpcCallerCustomize> _logger;
private readonly DalamudUtilService _dalamudUtil;
private readonly MareMediator _mareMediator;
public IpcCallerCustomize(ILogger<IpcCallerCustomize> logger, IDalamudPluginInterface dalamudPluginInterface,
DalamudUtilService dalamudUtil, MareMediator mareMediator)
{
_customizePlusApiVersion = dalamudPluginInterface.GetIpcSubscriber<(int, int)>("CustomizePlus.General.GetApiVersion");
_customizePlusGetActiveProfile = dalamudPluginInterface.GetIpcSubscriber<ushort, (int, Guid?)>("CustomizePlus.Profile.GetActiveProfileIdOnCharacter");
_customizePlusGetProfileById = dalamudPluginInterface.GetIpcSubscriber<Guid, (int, string?)>("CustomizePlus.Profile.GetByUniqueId");
_customizePlusRevertCharacter = dalamudPluginInterface.GetIpcSubscriber<ushort, int>("CustomizePlus.Profile.DeleteTemporaryProfileOnCharacter");
_customizePlusSetBodyScaleToCharacter = dalamudPluginInterface.GetIpcSubscriber<ushort, string, (int, Guid?)>("CustomizePlus.Profile.SetTemporaryProfileOnCharacter");
_customizePlusOnScaleUpdate = dalamudPluginInterface.GetIpcSubscriber<ushort, Guid, object>("CustomizePlus.Profile.OnUpdate");
_customizePlusDeleteByUniqueId = dalamudPluginInterface.GetIpcSubscriber<Guid, int>("CustomizePlus.Profile.DeleteTemporaryProfileByUniqueId");
_customizePlusOnScaleUpdate.Subscribe(OnCustomizePlusScaleChange);
_logger = logger;
_dalamudUtil = dalamudUtil;
_mareMediator = mareMediator;
CheckAPI();
}
public bool APIAvailable { get; private set; } = false;
public async Task RevertAsync(nint character)
{
if (!APIAvailable) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj is ICharacter c)
{
_logger.LogTrace("CustomizePlus reverting for {chara}", c.Address.ToString("X"));
_customizePlusRevertCharacter!.InvokeFunc(c.ObjectIndex);
}
}).ConfigureAwait(false);
}
public async Task<Guid?> SetBodyScaleAsync(nint character, string scale)
{
if (!APIAvailable) return null;
return await _dalamudUtil.RunOnFrameworkThread(() =>
{
var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj is ICharacter c)
{
string decodedScale = Encoding.UTF8.GetString(Convert.FromBase64String(scale));
_logger.LogTrace("CustomizePlus applying for {chara}", c.Address.ToString("X"));
if (scale.IsNullOrEmpty())
{
_customizePlusRevertCharacter!.InvokeFunc(c.ObjectIndex);
return null;
}
else
{
var result = _customizePlusSetBodyScaleToCharacter!.InvokeFunc(c.ObjectIndex, decodedScale);
return result.Item2;
}
}
return null;
}).ConfigureAwait(false);
}
public async Task RevertByIdAsync(Guid? profileId)
{
if (!APIAvailable || profileId == null) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
_ = _customizePlusDeleteByUniqueId.InvokeFunc(profileId.Value);
}).ConfigureAwait(false);
}
public async Task<string?> GetScaleAsync(nint character)
{
if (!APIAvailable) return null;
var scale = await _dalamudUtil.RunOnFrameworkThread(() =>
{
var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj is ICharacter c)
{
var res = _customizePlusGetActiveProfile.InvokeFunc(c.ObjectIndex);
_logger.LogTrace("CustomizePlus GetActiveProfile returned {err}", res.Item1);
if (res.Item1 != 0 || res.Item2 == null) return string.Empty;
return _customizePlusGetProfileById.InvokeFunc(res.Item2.Value).Item2;
}
return string.Empty;
}).ConfigureAwait(false);
if (string.IsNullOrEmpty(scale)) return string.Empty;
return Convert.ToBase64String(Encoding.UTF8.GetBytes(scale));
}
public void CheckAPI()
{
try
{
var version = _customizePlusApiVersion.InvokeFunc();
APIAvailable = (version.Item1 == 6 && version.Item2 >= 0);
}
catch
{
APIAvailable = false;
}
}
private void OnCustomizePlusScaleChange(ushort c, Guid g)
{
var obj = _dalamudUtil.GetCharacterFromObjectTableByIndex(c);
_mareMediator.Publish(new CustomizePlusMessage(obj?.Name.ToString() ?? string.Empty));
}
public void Dispose()
{
_customizePlusOnScaleUpdate.Unsubscribe(OnCustomizePlusScaleChange);
}
}

View File

@@ -0,0 +1,211 @@
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Plugin;
using Glamourer.Api.Helpers;
using Glamourer.Api.IpcSubscribers;
using MareSynchronos.PlayerData.Handlers;
using MareSynchronos.Services;
using MareSynchronos.Services.Mediator;
using Microsoft.Extensions.Logging;
namespace MareSynchronos.Interop.Ipc;
public sealed class IpcCallerGlamourer : IIpcCaller
{
private readonly ILogger<IpcCallerGlamourer> _logger;
private readonly IDalamudPluginInterface _pi;
private readonly DalamudUtilService _dalamudUtil;
private readonly MareMediator _mareMediator;
private readonly RedrawManager _redrawManager;
private readonly ApiVersion _glamourerApiVersions;
private readonly ApplyState? _glamourerApplyAll;
private readonly GetStateBase64? _glamourerGetAllCustomization;
private readonly RevertState _glamourerRevert;
private readonly RevertStateName _glamourerRevertByName;
private readonly UnlockState _glamourerUnlock;
private readonly UnlockStateName _glamourerUnlockByName;
private readonly EventSubscriber<nint>? _glamourerStateChanged;
private bool _shownGlamourerUnavailable = false;
private readonly uint LockCode = 0x626E7579;
public IpcCallerGlamourer(ILogger<IpcCallerGlamourer> logger, IDalamudPluginInterface pi, DalamudUtilService dalamudUtil, MareMediator mareMediator,
RedrawManager redrawManager)
{
_glamourerApiVersions = new(pi);
_glamourerGetAllCustomization = new(pi);
_glamourerApplyAll = new(pi);
_glamourerRevert = new(pi);
_glamourerRevertByName = new(pi);
_glamourerUnlock = new(pi);
_glamourerUnlockByName = new(pi);
_logger = logger;
_pi = pi;
_dalamudUtil = dalamudUtil;
_mareMediator = mareMediator;
_redrawManager = redrawManager;
CheckAPI();
_glamourerStateChanged = StateChanged.Subscriber(pi, GlamourerChanged);
_glamourerStateChanged.Enable();
}
public bool APIAvailable { get; private set; }
public void CheckAPI()
{
bool apiAvailable = false;
try
{
bool versionValid = (_pi.InstalledPlugins
.FirstOrDefault(p => string.Equals(p.InternalName, "Glamourer", StringComparison.OrdinalIgnoreCase))
?.Version ?? new Version(0, 0, 0, 0)) >= new Version(1, 0, 6, 1);
try
{
var version = _glamourerApiVersions.Invoke();
if (version is { Major: 1, Minor: >= 1 } && versionValid)
{
apiAvailable = true;
}
}
catch
{
// ignore
}
_shownGlamourerUnavailable = _shownGlamourerUnavailable && !apiAvailable;
APIAvailable = apiAvailable;
}
catch
{
APIAvailable = apiAvailable;
}
finally
{
if (!apiAvailable && !_shownGlamourerUnavailable)
{
_shownGlamourerUnavailable = true;
_mareMediator.Publish(new NotificationMessage("Glamourer inactive", "Your Glamourer installation is not active or out of date. Update Glamourer to continue to use Loporrit. If you just updated Glamourer, ignore this message.",
NotificationType.Error));
}
}
}
public void Dispose()
{
_glamourerStateChanged?.Dispose();
}
public async Task ApplyAllAsync(ILogger logger, GameObjectHandler handler, string? customization, Guid applicationId, CancellationToken token, bool fireAndForget = false)
{
if (!APIAvailable || string.IsNullOrEmpty(customization) || _dalamudUtil.IsZoning) return;
await _redrawManager.RedrawSemaphore.WaitAsync(token).ConfigureAwait(false);
try
{
await _redrawManager.PenumbraRedrawInternalAsync(logger, handler, applicationId, (chara) =>
{
try
{
logger.LogDebug("[{appid}] Calling on IPC: GlamourerApplyAll", applicationId);
_glamourerApplyAll!.Invoke(customization, chara.ObjectIndex, LockCode);
}
catch (Exception ex)
{
logger.LogWarning(ex, "[{appid}] Failed to apply Glamourer data", applicationId);
}
}).ConfigureAwait(false);
}
finally
{
_redrawManager.RedrawSemaphore.Release();
}
}
public async Task<string> GetCharacterCustomizationAsync(IntPtr character)
{
if (!APIAvailable) return string.Empty;
try
{
return await _dalamudUtil.RunOnFrameworkThread(() =>
{
var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj is ICharacter c)
{
return _glamourerGetAllCustomization!.Invoke(c.ObjectIndex).Item2 ?? string.Empty;
}
return string.Empty;
}).ConfigureAwait(false);
}
catch
{
return string.Empty;
}
}
public async Task RevertAsync(ILogger logger, GameObjectHandler handler, Guid applicationId, CancellationToken token)
{
if ((!APIAvailable) || _dalamudUtil.IsZoning) return;
try
{
await _redrawManager.RedrawSemaphore.WaitAsync(token).ConfigureAwait(false);
await _redrawManager.PenumbraRedrawInternalAsync(logger, handler, applicationId, (chara) =>
{
try
{
logger.LogDebug("[{appid}] Calling On IPC: GlamourerUnlock", applicationId);
_glamourerUnlock.Invoke(chara.ObjectIndex, LockCode);
logger.LogDebug("[{appid}] Calling On IPC: GlamourerRevert", applicationId);
_glamourerRevert.Invoke(chara.ObjectIndex, LockCode);
logger.LogDebug("[{appid}] Calling On IPC: PenumbraRedraw", applicationId);
_mareMediator.Publish(new PenumbraRedrawCharacterMessage(chara));
}
catch (Exception ex)
{
logger.LogWarning(ex, "[{appid}] Error during GlamourerRevert", applicationId);
}
}).ConfigureAwait(false);
}
finally
{
_redrawManager.RedrawSemaphore.Release();
}
}
public async Task RevertByNameAsync(ILogger logger, string name, Guid applicationId)
{
if ((!APIAvailable) || _dalamudUtil.IsZoning) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
RevertByName(logger, name, applicationId);
}).ConfigureAwait(false);
}
public void RevertByName(ILogger logger, string name, Guid applicationId)
{
if ((!APIAvailable) || _dalamudUtil.IsZoning) return;
try
{
logger.LogDebug("[{appid}] Calling On IPC: GlamourerRevertByName", applicationId);
_glamourerRevertByName.Invoke(name, LockCode);
logger.LogDebug("[{appid}] Calling On IPC: GlamourerUnlockName", applicationId);
_glamourerUnlockByName.Invoke(name, LockCode);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Error during Glamourer RevertByName");
}
}
private void GlamourerChanged(nint address)
{
_mareMediator.Publish(new GlamourerChangedMessage(address));
}
}

View File

@@ -0,0 +1,94 @@
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using MareSynchronos.Services;
using MareSynchronos.Services.Mediator;
using Microsoft.Extensions.Logging;
namespace MareSynchronos.Interop.Ipc;
public sealed class IpcCallerHeels : IIpcCaller
{
private readonly ILogger<IpcCallerHeels> _logger;
private readonly MareMediator _mareMediator;
private readonly DalamudUtilService _dalamudUtil;
private readonly ICallGateSubscriber<(int, int)> _heelsGetApiVersion;
private readonly ICallGateSubscriber<string> _heelsGetOffset;
private readonly ICallGateSubscriber<string, object?> _heelsOffsetUpdate;
private readonly ICallGateSubscriber<int, string, object?> _heelsRegisterPlayer;
private readonly ICallGateSubscriber<int, object?> _heelsUnregisterPlayer;
public IpcCallerHeels(ILogger<IpcCallerHeels> logger, IDalamudPluginInterface pi, DalamudUtilService dalamudUtil, MareMediator mareMediator)
{
_logger = logger;
_mareMediator = mareMediator;
_dalamudUtil = dalamudUtil;
_heelsGetApiVersion = pi.GetIpcSubscriber<(int, int)>("SimpleHeels.ApiVersion");
_heelsGetOffset = pi.GetIpcSubscriber<string>("SimpleHeels.GetLocalPlayer");
_heelsRegisterPlayer = pi.GetIpcSubscriber<int, string, object?>("SimpleHeels.RegisterPlayer");
_heelsUnregisterPlayer = pi.GetIpcSubscriber<int, object?>("SimpleHeels.UnregisterPlayer");
_heelsOffsetUpdate = pi.GetIpcSubscriber<string, object?>("SimpleHeels.LocalChanged");
_heelsOffsetUpdate.Subscribe(HeelsOffsetChange);
CheckAPI();
}
public bool APIAvailable { get; private set; } = false;
private void HeelsOffsetChange(string offset)
{
_mareMediator.Publish(new HeelsOffsetMessage());
}
public async Task<string> GetOffsetAsync()
{
if (!APIAvailable) return string.Empty;
return await _dalamudUtil.RunOnFrameworkThread(_heelsGetOffset.InvokeFunc).ConfigureAwait(false);
}
public async Task RestoreOffsetForPlayerAsync(IntPtr character)
{
if (!APIAvailable) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj != null)
{
_logger.LogTrace("Restoring Heels data to {chara}", character.ToString("X"));
_heelsUnregisterPlayer.InvokeAction(gameObj.ObjectIndex);
}
}).ConfigureAwait(false);
}
public async Task SetOffsetForPlayerAsync(IntPtr character, string data)
{
if (!APIAvailable) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj != null)
{
_logger.LogTrace("Applying Heels data to {chara}", character.ToString("X"));
_heelsRegisterPlayer.InvokeAction(gameObj.ObjectIndex, data);
}
}).ConfigureAwait(false);
}
public void CheckAPI()
{
try
{
APIAvailable = _heelsGetApiVersion.InvokeFunc() is { Item1: 2, Item2: >= 0 };
}
catch
{
APIAvailable = false;
}
}
public void Dispose()
{
_heelsOffsetUpdate.Unsubscribe(HeelsOffsetChange);
}
}

View File

@@ -0,0 +1,132 @@
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using MareSynchronos.Services;
using MareSynchronos.Services.Mediator;
using Microsoft.Extensions.Logging;
using System.Text;
namespace MareSynchronos.Interop.Ipc;
public sealed class IpcCallerHonorific : IIpcCaller
{
private readonly ICallGateSubscriber<(uint major, uint minor)> _honorificApiVersion;
private readonly ICallGateSubscriber<int, object> _honorificClearCharacterTitle;
private readonly ICallGateSubscriber<object> _honorificDisposing;
private readonly ICallGateSubscriber<string> _honorificGetLocalCharacterTitle;
private readonly ICallGateSubscriber<string, object> _honorificLocalCharacterTitleChanged;
private readonly ICallGateSubscriber<object> _honorificReady;
private readonly ICallGateSubscriber<int, string, object> _honorificSetCharacterTitle;
private readonly ILogger<IpcCallerHonorific> _logger;
private readonly MareMediator _mareMediator;
private readonly DalamudUtilService _dalamudUtil;
public IpcCallerHonorific(ILogger<IpcCallerHonorific> logger, IDalamudPluginInterface pi, DalamudUtilService dalamudUtil,
MareMediator mareMediator)
{
_logger = logger;
_mareMediator = mareMediator;
_dalamudUtil = dalamudUtil;
_honorificApiVersion = pi.GetIpcSubscriber<(uint, uint)>("Honorific.ApiVersion");
_honorificGetLocalCharacterTitle = pi.GetIpcSubscriber<string>("Honorific.GetLocalCharacterTitle");
_honorificClearCharacterTitle = pi.GetIpcSubscriber<int, object>("Honorific.ClearCharacterTitle");
_honorificSetCharacterTitle = pi.GetIpcSubscriber<int, string, object>("Honorific.SetCharacterTitle");
_honorificLocalCharacterTitleChanged = pi.GetIpcSubscriber<string, object>("Honorific.LocalCharacterTitleChanged");
_honorificDisposing = pi.GetIpcSubscriber<object>("Honorific.Disposing");
_honorificReady = pi.GetIpcSubscriber<object>("Honorific.Ready");
_honorificLocalCharacterTitleChanged.Subscribe(OnHonorificLocalCharacterTitleChanged);
_honorificDisposing.Subscribe(OnHonorificDisposing);
_honorificReady.Subscribe(OnHonorificReady);
CheckAPI();
}
public bool APIAvailable { get; private set; } = false;
public void CheckAPI()
{
try
{
APIAvailable = _honorificApiVersion.InvokeFunc() is { Item1: 3, Item2: >= 0 };
}
catch
{
APIAvailable = false;
}
}
public void Dispose()
{
_honorificLocalCharacterTitleChanged.Unsubscribe(OnHonorificLocalCharacterTitleChanged);
_honorificDisposing.Unsubscribe(OnHonorificDisposing);
_honorificReady.Unsubscribe(OnHonorificReady);
}
public async Task ClearTitleAsync(nint character)
{
if (!APIAvailable) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj is IPlayerCharacter c)
{
_logger.LogTrace("Honorific removing for {addr}", c.Address.ToString("X"));
_honorificClearCharacterTitle!.InvokeAction(c.ObjectIndex);
}
}).ConfigureAwait(false);
}
public string GetTitle()
{
if (!APIAvailable) return string.Empty;
string title = _honorificGetLocalCharacterTitle.InvokeFunc();
return string.IsNullOrEmpty(title) ? string.Empty : Convert.ToBase64String(Encoding.UTF8.GetBytes(title));
}
public async Task SetTitleAsync(IntPtr character, string honorificDataB64)
{
if (!APIAvailable) return;
_logger.LogTrace("Applying Honorific data to {chara}", character.ToString("X"));
try
{
await _dalamudUtil.RunOnFrameworkThread(() =>
{
var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj is IPlayerCharacter pc)
{
string honorificData = string.IsNullOrEmpty(honorificDataB64) ? string.Empty : Encoding.UTF8.GetString(Convert.FromBase64String(honorificDataB64));
if (string.IsNullOrEmpty(honorificData))
{
_honorificClearCharacterTitle!.InvokeAction(pc.ObjectIndex);
}
else
{
_honorificSetCharacterTitle!.InvokeAction(pc.ObjectIndex, honorificData);
}
}
}).ConfigureAwait(false);
}
catch (Exception e)
{
_logger.LogWarning(e, "Could not apply Honorific data");
}
}
private void OnHonorificDisposing()
{
_mareMediator.Publish(new HonorificMessage(string.Empty));
}
private void OnHonorificLocalCharacterTitleChanged(string titleJson)
{
string titleData = string.IsNullOrEmpty(titleJson) ? string.Empty : Convert.ToBase64String(Encoding.UTF8.GetBytes(titleJson));
_mareMediator.Publish(new HonorificMessage(titleData));
}
private void OnHonorificReady()
{
CheckAPI();
_mareMediator.Publish(new HonorificReadyMessage());
}
}

View File

@@ -0,0 +1,334 @@
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Plugin;
using MareSynchronos.MareConfiguration.Models;
using MareSynchronos.PlayerData.Handlers;
using MareSynchronos.Services;
using MareSynchronos.Services.Mediator;
using Microsoft.Extensions.Logging;
using Penumbra.Api.Enums;
using Penumbra.Api.Helpers;
using Penumbra.Api.IpcSubscribers;
using System.Collections.Concurrent;
namespace MareSynchronos.Interop.Ipc;
public sealed class IpcCallerPenumbra : DisposableMediatorSubscriberBase, IIpcCaller
{
private readonly IDalamudPluginInterface _pi;
private readonly DalamudUtilService _dalamudUtil;
private readonly MareMediator _mareMediator;
private readonly RedrawManager _redrawManager;
private bool _shownPenumbraUnavailable = false;
private string? _penumbraModDirectory;
public string? PenumbraModDirectory
{
get => _penumbraModDirectory;
private set
{
if (!string.Equals(_penumbraModDirectory, value, StringComparison.Ordinal))
{
_penumbraModDirectory = value;
_mareMediator.Publish(new PenumbraDirectoryChangedMessage(_penumbraModDirectory));
}
}
}
private readonly ConcurrentDictionary<IntPtr, bool> _penumbraRedrawRequests = new();
private readonly EventSubscriber _penumbraDispose;
private readonly EventSubscriber<nint, string, string> _penumbraGameObjectResourcePathResolved;
private readonly EventSubscriber _penumbraInit;
private readonly EventSubscriber<ModSettingChange, Guid, string, bool> _penumbraModSettingChanged;
private readonly EventSubscriber<nint, int> _penumbraObjectIsRedrawn;
private readonly AddTemporaryMod _penumbraAddTemporaryMod;
private readonly AssignTemporaryCollection _penumbraAssignTemporaryCollection;
private readonly ConvertTextureFile _penumbraConvertTextureFile;
private readonly CreateTemporaryCollection _penumbraCreateNamedTemporaryCollection;
private readonly GetEnabledState _penumbraEnabled;
private readonly GetPlayerMetaManipulations _penumbraGetMetaManipulations;
private readonly RedrawObject _penumbraRedraw;
private readonly DeleteTemporaryCollection _penumbraRemoveTemporaryCollection;
private readonly RemoveTemporaryMod _penumbraRemoveTemporaryMod;
private readonly GetModDirectory _penumbraResolveModDir;
private readonly ResolvePlayerPathsAsync _penumbraResolvePaths;
private readonly GetGameObjectResourcePaths _penumbraResourcePaths;
public IpcCallerPenumbra(ILogger<IpcCallerPenumbra> logger, IDalamudPluginInterface pi, DalamudUtilService dalamudUtil,
MareMediator mareMediator, RedrawManager redrawManager) : base(logger, mareMediator)
{
_pi = pi;
_dalamudUtil = dalamudUtil;
_mareMediator = mareMediator;
_redrawManager = redrawManager;
_penumbraInit = Initialized.Subscriber(pi, PenumbraInit);
_penumbraDispose = Disposed.Subscriber(pi, PenumbraDispose);
_penumbraResolveModDir = new GetModDirectory(pi);
_penumbraRedraw = new RedrawObject(pi);
_penumbraObjectIsRedrawn = GameObjectRedrawn.Subscriber(pi, RedrawEvent);
_penumbraGetMetaManipulations = new GetPlayerMetaManipulations(pi);
_penumbraRemoveTemporaryMod = new RemoveTemporaryMod(pi);
_penumbraAddTemporaryMod = new AddTemporaryMod(pi);
_penumbraCreateNamedTemporaryCollection = new CreateTemporaryCollection(pi);
_penumbraRemoveTemporaryCollection = new DeleteTemporaryCollection(pi);
_penumbraAssignTemporaryCollection = new AssignTemporaryCollection(pi);
_penumbraResolvePaths = new ResolvePlayerPathsAsync(pi);
_penumbraEnabled = new GetEnabledState(pi);
_penumbraModSettingChanged = ModSettingChanged.Subscriber(pi, (change, arg1, arg, b) =>
{
if (change == ModSettingChange.EnableState)
_mareMediator.Publish(new PenumbraModSettingChangedMessage());
});
_penumbraConvertTextureFile = new ConvertTextureFile(pi);
_penumbraResourcePaths = new GetGameObjectResourcePaths(pi);
_penumbraGameObjectResourcePathResolved = GameObjectResourcePathResolved.Subscriber(pi, ResourceLoaded);
CheckAPI();
CheckModDirectory();
Mediator.Subscribe<PenumbraRedrawCharacterMessage>(this, (msg) =>
{
_penumbraRedraw.Invoke(msg.Character.ObjectIndex, RedrawType.AfterGPose);
});
}
public bool APIAvailable { get; private set; } = false;
public void CheckAPI()
{
bool penumbraAvailable = false;
try
{
var penumbraVersion = (_pi.InstalledPlugins
.FirstOrDefault(p => string.Equals(p.InternalName, "Penumbra", StringComparison.OrdinalIgnoreCase))
?.Version ?? new Version(0, 0, 0, 0));
penumbraAvailable = penumbraVersion >= new Version(1, 0, 1, 0);
penumbraAvailable &= _penumbraEnabled.Invoke();
_shownPenumbraUnavailable = _shownPenumbraUnavailable && !penumbraAvailable;
APIAvailable = penumbraAvailable;
}
catch
{
APIAvailable = penumbraAvailable;
}
finally
{
if (!penumbraAvailable && !_shownPenumbraUnavailable)
{
_shownPenumbraUnavailable = true;
_mareMediator.Publish(new NotificationMessage("Penumbra inactive",
"Your Penumbra installation is not active or out of date. Update Penumbra and/or the Enable Mods setting in Penumbra to continue to use Loporrit. If you just updated Penumbra, ignore this message.",
NotificationType.Error));
}
}
}
public void CheckModDirectory()
{
if (!APIAvailable)
{
PenumbraModDirectory = string.Empty;
}
else
{
PenumbraModDirectory = _penumbraResolveModDir!.Invoke().ToLowerInvariant();
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
_redrawManager.Cancel();
_penumbraModSettingChanged.Dispose();
_penumbraGameObjectResourcePathResolved.Dispose();
_penumbraDispose.Dispose();
_penumbraInit.Dispose();
_penumbraObjectIsRedrawn.Dispose();
}
public async Task AssignTemporaryCollectionAsync(ILogger logger, Guid collName, int idx)
{
if (!APIAvailable) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
var retAssign = _penumbraAssignTemporaryCollection.Invoke(collName, idx, forceAssignment: true);
logger.LogTrace("Assigning Temp Collection {collName} to index {idx}, Success: {ret}", collName, idx, retAssign);
return collName;
}).ConfigureAwait(false);
}
public async Task ConvertTextureFiles(ILogger logger, Dictionary<string, string[]> textures, IProgress<(string, int)> progress, CancellationToken token)
{
if (!APIAvailable) return;
_mareMediator.Publish(new HaltScanMessage(nameof(ConvertTextureFiles)));
int currentTexture = 0;
foreach (var texture in textures)
{
if (token.IsCancellationRequested) break;
progress.Report((texture.Key, ++currentTexture));
logger.LogInformation("Converting Texture {path} to {type}", texture.Key, TextureType.Bc7Tex);
var convertTask = _penumbraConvertTextureFile.Invoke(texture.Key, texture.Key, TextureType.Bc7Tex, mipMaps: true);
await convertTask.ConfigureAwait(false);
if (convertTask.IsCompletedSuccessfully && texture.Value.Any())
{
foreach (var duplicatedTexture in texture.Value)
{
logger.LogInformation("Migrating duplicate {dup}", duplicatedTexture);
try
{
File.Copy(texture.Key, duplicatedTexture, overwrite: true);
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to copy duplicate {dup}", duplicatedTexture);
}
}
}
}
_mareMediator.Publish(new ResumeScanMessage(nameof(ConvertTextureFiles)));
await _dalamudUtil.RunOnFrameworkThread(async () =>
{
var gameObject = await _dalamudUtil.CreateGameObjectAsync(await _dalamudUtil.GetPlayerPointerAsync().ConfigureAwait(false)).ConfigureAwait(false);
_penumbraRedraw.Invoke(gameObject!.ObjectIndex, RedrawType.Redraw);
}).ConfigureAwait(false);
}
public async Task<Guid> CreateTemporaryCollectionAsync(ILogger logger, string uid)
{
if (!APIAvailable) return Guid.Empty;
return await _dalamudUtil.RunOnFrameworkThread(() =>
{
var collName = "Loporrit_" + uid;
var collId = _penumbraCreateNamedTemporaryCollection.Invoke(collName);
logger.LogTrace("Creating Temp Collection {collName}, GUID: {collId}", collName, collId);
return collId;
}).ConfigureAwait(false);
}
public async Task<Dictionary<string, HashSet<string>>?> GetCharacterData(ILogger logger, GameObjectHandler handler)
{
if (!APIAvailable) return null;
return await _dalamudUtil.RunOnFrameworkThread(() =>
{
logger.LogTrace("Calling On IPC: Penumbra.GetGameObjectResourcePaths");
var idx = handler.GetGameObject()?.ObjectIndex;
if (idx == null) return null;
return _penumbraResourcePaths.Invoke(idx.Value)[0];
}).ConfigureAwait(false);
}
public string GetMetaManipulations()
{
if (!APIAvailable) return string.Empty;
return _penumbraGetMetaManipulations.Invoke();
}
public async Task RedrawAsync(ILogger logger, GameObjectHandler handler, Guid applicationId, CancellationToken token)
{
if (!APIAvailable || _dalamudUtil.IsZoning) return;
try
{
await _redrawManager.RedrawSemaphore.WaitAsync(token).ConfigureAwait(false);
await _redrawManager.PenumbraRedrawInternalAsync(logger, handler, applicationId, (chara) =>
{
logger.LogDebug("[{appid}] Calling on IPC: PenumbraRedraw", applicationId);
_penumbraRedraw!.Invoke(chara.ObjectIndex, setting: RedrawType.Redraw);
}).ConfigureAwait(false);
}
finally
{
_redrawManager.RedrawSemaphore.Release();
}
}
public async Task RemoveTemporaryCollectionAsync(ILogger logger, Guid applicationId, Guid collId)
{
if (!APIAvailable) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
logger.LogTrace("[{applicationId}] Removing temp collection for {collId}", applicationId, collId);
var ret2 = _penumbraRemoveTemporaryCollection.Invoke(collId);
logger.LogTrace("[{applicationId}] RemoveTemporaryCollection: {ret2}", applicationId, ret2);
}).ConfigureAwait(false);
}
public async Task<(string[] forward, string[][] reverse)> ResolvePathsAsync(string[] forward, string[] reverse)
{
return await _penumbraResolvePaths.Invoke(forward, reverse).ConfigureAwait(false);
}
public async Task SetManipulationDataAsync(ILogger logger, Guid applicationId, Guid collId, string manipulationData)
{
if (!APIAvailable) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
logger.LogTrace("[{applicationId}] Manip: {data}", applicationId, manipulationData);
var retAdd = _penumbraAddTemporaryMod.Invoke("MareChara_Meta", collId, [], manipulationData, 0);
logger.LogTrace("[{applicationId}] Setting temp meta mod for {collId}, Success: {ret}", applicationId, collId, retAdd);
}).ConfigureAwait(false);
}
public async Task SetTemporaryModsAsync(ILogger logger, Guid applicationId, Guid collId, Dictionary<string, string> modPaths)
{
if (!APIAvailable) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
foreach (var mod in modPaths)
{
logger.LogTrace("[{applicationId}] Change: {from} => {to}", applicationId, mod.Key, mod.Value);
}
var retRemove = _penumbraRemoveTemporaryMod.Invoke("MareChara_Files", collId, 0);
logger.LogTrace("[{applicationId}] Removing temp files mod for {collId}, Success: {ret}", applicationId, collId, retRemove);
var retAdd = _penumbraAddTemporaryMod.Invoke("MareChara_Files", collId, modPaths, string.Empty, 0);
logger.LogTrace("[{applicationId}] Setting temp files mod for {collId}, Success: {ret}", applicationId, collId, retAdd);
}).ConfigureAwait(false);
}
private void RedrawEvent(IntPtr objectAddress, int objectTableIndex)
{
bool wasRequested = false;
if (_penumbraRedrawRequests.TryGetValue(objectAddress, out var redrawRequest) && redrawRequest)
{
_penumbraRedrawRequests[objectAddress] = false;
}
else
{
_mareMediator.Publish(new PenumbraRedrawMessage(objectAddress, objectTableIndex, wasRequested));
}
}
private void ResourceLoaded(IntPtr ptr, string arg1, string arg2)
{
if (ptr != IntPtr.Zero && string.Compare(arg1, arg2, ignoreCase: true, System.Globalization.CultureInfo.InvariantCulture) != 0)
{
_mareMediator.Publish(new PenumbraResourceLoadMessage(ptr, arg1, arg2));
}
}
private void PenumbraDispose()
{
_redrawManager.Cancel();
_mareMediator.Publish(new PenumbraDisposedMessage());
}
private void PenumbraInit()
{
APIAvailable = true;
PenumbraModDirectory = _penumbraResolveModDir.Invoke();
_mareMediator.Publish(new PenumbraInitializedMessage());
_penumbraRedraw!.Invoke(0, setting: RedrawType.Redraw);
}
}

View File

@@ -0,0 +1,52 @@
using MareSynchronos.Services.Mediator;
using Microsoft.Extensions.Logging;
namespace MareSynchronos.Interop.Ipc;
public sealed partial class IpcManager : DisposableMediatorSubscriberBase
{
public IpcManager(ILogger<IpcManager> logger, MareMediator mediator,
IpcCallerPenumbra penumbraIpc, IpcCallerGlamourer glamourerIpc, IpcCallerCustomize customizeIpc, IpcCallerHeels heelsIpc,
IpcCallerHonorific honorificIpc) : base(logger, mediator)
{
CustomizePlus = customizeIpc;
Heels = heelsIpc;
Glamourer = glamourerIpc;
Penumbra = penumbraIpc;
Honorific = honorificIpc;
if (Initialized)
{
Mediator.Publish(new PenumbraInitializedMessage());
}
Mediator.Subscribe<DelayedFrameworkUpdateMessage>(this, (_) => PeriodicApiStateCheck());
try
{
PeriodicApiStateCheck();
}
catch (Exception ex)
{
logger.LogWarning(ex, "Failed to check for some IPC, plugin not installed?");
}
}
public bool Initialized => Penumbra.APIAvailable && Glamourer.APIAvailable;
public IpcCallerCustomize CustomizePlus { get; init; }
public IpcCallerHonorific Honorific { get; init; }
public IpcCallerHeels Heels { get; init; }
public IpcCallerGlamourer Glamourer { get; }
public IpcCallerPenumbra Penumbra { get; }
private void PeriodicApiStateCheck()
{
Penumbra.CheckAPI();
Penumbra.CheckModDirectory();
Glamourer.CheckAPI();
Heels.CheckAPI();
CustomizePlus.CheckAPI();
Honorific.CheckAPI();
}
}

View File

@@ -0,0 +1,51 @@
using Dalamud.Game.ClientState.Objects.Types;
using MareSynchronos.PlayerData.Handlers;
using MareSynchronos.Services;
using MareSynchronos.Services.Mediator;
using MareSynchronos.Utils;
using Microsoft.Extensions.Logging;
namespace MareSynchronos.Interop.Ipc;
public class RedrawManager
{
private readonly MareMediator _mareMediator;
private readonly DalamudUtilService _dalamudUtil;
private readonly Dictionary<nint, bool> _penumbraRedrawRequests = [];
private CancellationTokenSource _disposalCts = new();
public SemaphoreSlim RedrawSemaphore { get; init; } = new(2, 2);
public RedrawManager(MareMediator mareMediator, DalamudUtilService dalamudUtil)
{
_mareMediator = mareMediator;
_dalamudUtil = dalamudUtil;
}
public async Task PenumbraRedrawInternalAsync(ILogger logger, GameObjectHandler handler, Guid applicationId, Action<ICharacter> action)
{
_mareMediator.Publish(new PenumbraStartRedrawMessage(handler.Address));
_penumbraRedrawRequests[handler.Address] = true;
try
{
CancellationTokenSource cancelToken = new CancellationTokenSource();
cancelToken.CancelAfter(TimeSpan.FromSeconds(15));
await handler.ActOnFrameworkAfterEnsureNoDrawAsync(action, cancelToken.Token).ConfigureAwait(false);
if (!_disposalCts.Token.IsCancellationRequested)
await _dalamudUtil.WaitWhileCharacterIsDrawing(logger, handler, applicationId, 30000, _disposalCts.Token).ConfigureAwait(false);
}
finally
{
_penumbraRedrawRequests[handler.Address] = false;
_mareMediator.Publish(new PenumbraEndRedrawMessage(handler.Address));
}
}
internal void Cancel()
{
_disposalCts = _disposalCts.CancelRecreate();
}
}

View File

@@ -1,836 +0,0 @@
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using Glamourer.Api.Helpers;
using Glamourer.Api.IpcSubscribers;
using Dalamud.Utility;
using MareSynchronos.PlayerData.Handlers;
using MareSynchronos.Services;
using MareSynchronos.Services.Mediator;
using Microsoft.Extensions.Logging;
using Penumbra.Api.Enums;
using Penumbra.Api.Helpers;
using Penumbra.Api.IpcSubscribers;
using System.Collections.Concurrent;
using System.Text;
namespace MareSynchronos.Interop;
public sealed class IpcManager : DisposableMediatorSubscriberBase
{
private readonly ICallGateSubscriber<(int, int)> _customizePlusApiVersion;
private readonly ICallGateSubscriber<ushort, (int, Guid?)> _customizePlusGetActiveProfile;
private readonly ICallGateSubscriber<Guid, (int, string?)> _customizePlusGetProfileById;
private readonly ICallGateSubscriber<ushort, Guid, object> _customizePlusOnScaleUpdate;
private readonly ICallGateSubscriber<ushort, int> _customizePlusRevertCharacter;
private readonly ICallGateSubscriber<ushort, string, (int, Guid?)> _customizePlusSetBodyScaleToCharacter;
private readonly ICallGateSubscriber<Guid, int> _customizePlusDeleteByUniqueId;
private readonly IDalamudPluginInterface _pi;
private readonly DalamudUtilService _dalamudUtil;
private readonly Glamourer.Api.IpcSubscribers.ApiVersion _glamourerApiVersions;
private readonly Glamourer.Api.IpcSubscribers.ApplyState? _glamourerApplyAll;
private readonly Glamourer.Api.IpcSubscribers.GetStateBase64? _glamourerGetAllCustomization;
private readonly Glamourer.Api.IpcSubscribers.RevertState _glamourerRevert;
private readonly Glamourer.Api.IpcSubscribers.RevertStateName _glamourerRevertByName;
private readonly Glamourer.Api.IpcSubscribers.UnlockState _glamourerUnlock;
private readonly Glamourer.Api.IpcSubscribers.UnlockStateName _glamourerUnlockByName;
private readonly Glamourer.Api.Helpers.EventSubscriber<nint> _glamourerStateChanged;
private readonly ICallGateSubscriber<(int, int)> _heelsGetApiVersion;
private readonly ICallGateSubscriber<string> _heelsGetOffset;
private readonly ICallGateSubscriber<string, object?> _heelsOffsetUpdate;
private readonly ICallGateSubscriber<int, string, object?> _heelsRegisterPlayer;
private readonly ICallGateSubscriber<int, object?> _heelsUnregisterPlayer;
private readonly ICallGateSubscriber<(uint major, uint minor)> _honorificApiVersion;
private readonly ICallGateSubscriber<int, object> _honorificClearCharacterTitle;
private readonly ICallGateSubscriber<object> _honorificDisposing;
private readonly ICallGateSubscriber<string> _honorificGetLocalCharacterTitle;
private readonly ICallGateSubscriber<string, object> _honorificLocalCharacterTitleChanged;
private readonly ICallGateSubscriber<object> _honorificReady;
private readonly ICallGateSubscriber<int, string, object> _honorificSetCharacterTitle;
private readonly ConcurrentDictionary<IntPtr, bool> _penumbraRedrawRequests = new();
private readonly Penumbra.Api.Helpers.EventSubscriber _penumbraDispose;
private readonly Penumbra.Api.Helpers.EventSubscriber<nint, string, string> _penumbraGameObjectResourcePathResolved;
private readonly Penumbra.Api.Helpers.EventSubscriber _penumbraInit;
private readonly Penumbra.Api.Helpers.EventSubscriber<ModSettingChange, Guid, string, bool> _penumbraModSettingChanged;
private readonly Penumbra.Api.Helpers.EventSubscriber<nint, int> _penumbraObjectIsRedrawn;
private readonly Penumbra.Api.IpcSubscribers.ApiVersion _penumbraApiVersion;
private readonly Penumbra.Api.IpcSubscribers.AddTemporaryMod _penumbraAddTemporaryMod;
private readonly Penumbra.Api.IpcSubscribers.AssignTemporaryCollection _penumbraAssignTemporaryCollection;
private readonly Penumbra.Api.IpcSubscribers.ConvertTextureFile _penumbraConvertTextureFile;
private readonly Penumbra.Api.IpcSubscribers.CreateTemporaryCollection _penumbraCreateNamedTemporaryCollection;
private readonly Penumbra.Api.IpcSubscribers.GetEnabledState _penumbraEnabled;
private readonly Penumbra.Api.IpcSubscribers.GetPlayerMetaManipulations _penumbraGetMetaManipulations;
private readonly Penumbra.Api.IpcSubscribers.RedrawObject _penumbraRedraw;
private readonly Penumbra.Api.IpcSubscribers.DeleteTemporaryCollection _penumbraRemoveTemporaryCollection;
private readonly Penumbra.Api.IpcSubscribers.RemoveTemporaryMod _penumbraRemoveTemporaryMod;
private readonly Penumbra.Api.IpcSubscribers.GetModDirectory _penumbraResolveModDir;
private readonly Penumbra.Api.IpcSubscribers.ResolvePlayerPathsAsync _penumbraResolvePaths;
private readonly Penumbra.Api.IpcSubscribers.GetGameObjectResourcePaths _penumbraResourcePaths;
private readonly SemaphoreSlim _redrawSemaphore = new(2);
private readonly uint LockCode = 0x626E7579;
private bool _customizePlusAvailable = false;
private CancellationTokenSource _disposalCts = new();
private bool _glamourerAvailable = false;
private bool _heelsAvailable = false;
private bool _honorificAvailable = false;
private bool _penumbraAvailable = false;
private bool _shownGlamourerUnavailable = false;
private bool _shownPenumbraUnavailable = false;
public IpcManager(ILogger<IpcManager> logger, IDalamudPluginInterface pi, DalamudUtilService dalamudUtil, MareMediator mediator) : base(logger, mediator)
{
_pi = pi;
_dalamudUtil = dalamudUtil;
_penumbraInit = Penumbra.Api.IpcSubscribers.Initialized.Subscriber(pi, PenumbraInit);
_penumbraDispose = Penumbra.Api.IpcSubscribers.Disposed.Subscriber(pi, PenumbraDispose);
_penumbraResolveModDir = new(pi);
_penumbraRedraw = new(pi);
_penumbraObjectIsRedrawn = Penumbra.Api.IpcSubscribers.GameObjectRedrawn.Subscriber(pi, RedrawEvent);
_penumbraGetMetaManipulations = new(pi);
_penumbraRemoveTemporaryMod = new(pi);
_penumbraAddTemporaryMod = new(pi);
_penumbraCreateNamedTemporaryCollection = new(pi);
_penumbraRemoveTemporaryCollection = new(pi);
_penumbraAssignTemporaryCollection = new(pi);
_penumbraResolvePaths = new(pi);
_penumbraEnabled = new(pi);
_penumbraModSettingChanged = Penumbra.Api.IpcSubscribers.ModSettingChanged.Subscriber(pi, (change, arg1, arg, b) =>
{
if (change == ModSettingChange.EnableState)
Mediator.Publish(new PenumbraModSettingChangedMessage());
});
_penumbraConvertTextureFile = new(pi);
_penumbraResourcePaths = new(pi);
_penumbraApiVersion = new(pi);
_penumbraGameObjectResourcePathResolved = Penumbra.Api.IpcSubscribers.GameObjectResourcePathResolved.Subscriber(pi, ResourceLoaded);
_glamourerApiVersions = new(pi);
_glamourerGetAllCustomization = new(pi);
_glamourerApplyAll = new(pi);
_glamourerRevert = new(pi);
_glamourerRevertByName = new(pi);
_glamourerUnlock = new(pi);
_glamourerUnlockByName = new(pi);
_glamourerStateChanged = Glamourer.Api.IpcSubscribers.StateChanged.Subscriber(pi, GlamourerChanged);
_glamourerStateChanged.Enable();
_heelsGetApiVersion = pi.GetIpcSubscriber<(int, int)>("SimpleHeels.ApiVersion");
_heelsGetOffset = pi.GetIpcSubscriber<string>("SimpleHeels.GetLocalPlayer");
_heelsRegisterPlayer = pi.GetIpcSubscriber<int, string, object?>("SimpleHeels.RegisterPlayer");
_heelsUnregisterPlayer = pi.GetIpcSubscriber<int, object?>("SimpleHeels.UnregisterPlayer");
_heelsOffsetUpdate = pi.GetIpcSubscriber<string, object?>("SimpleHeels.LocalChanged");
_heelsOffsetUpdate.Subscribe(HeelsOffsetChange);
_customizePlusApiVersion = pi.GetIpcSubscriber<(int, int)>("CustomizePlus.General.GetApiVersion");
_customizePlusGetActiveProfile = pi.GetIpcSubscriber<ushort, (int, Guid?)>("CustomizePlus.Profile.GetActiveProfileIdOnCharacter");
_customizePlusGetProfileById = pi.GetIpcSubscriber<Guid, (int, string?)>("CustomizePlus.Profile.GetByUniqueId");
_customizePlusRevertCharacter = pi.GetIpcSubscriber<ushort, int>("CustomizePlus.Profile.DeleteTemporaryProfileOnCharacter");
_customizePlusSetBodyScaleToCharacter = pi.GetIpcSubscriber<ushort, string, (int, Guid?)>("CustomizePlus.Profile.SetTemporaryProfileOnCharacter");
_customizePlusOnScaleUpdate = pi.GetIpcSubscriber<ushort, Guid, object>("CustomizePlus.Profile.OnUpdate");
_customizePlusDeleteByUniqueId = pi.GetIpcSubscriber<Guid, int>("CustomizePlus.Profile.DeleteTemporaryProfileByUniqueId");
_customizePlusOnScaleUpdate.Subscribe(OnCustomizePlusScaleChange);
_honorificApiVersion = pi.GetIpcSubscriber<(uint, uint)>("Honorific.ApiVersion");
_honorificGetLocalCharacterTitle = pi.GetIpcSubscriber<string>("Honorific.GetLocalCharacterTitle");
_honorificClearCharacterTitle = pi.GetIpcSubscriber<int, object>("Honorific.ClearCharacterTitle");
_honorificSetCharacterTitle = pi.GetIpcSubscriber<int, string, object>("Honorific.SetCharacterTitle");
_honorificLocalCharacterTitleChanged = pi.GetIpcSubscriber<string, object>("Honorific.LocalCharacterTitleChanged");
_honorificDisposing = pi.GetIpcSubscriber<object>("Honorific.Disposing");
_honorificReady = pi.GetIpcSubscriber<object>("Honorific.Ready");
_honorificLocalCharacterTitleChanged.Subscribe(OnHonorificLocalCharacterTitleChanged);
_honorificDisposing.Subscribe(OnHonorificDisposing);
_honorificReady.Subscribe(OnHonorificReady);
if (Initialized)
{
Mediator.Publish(new PenumbraInitializedMessage());
}
Mediator.Subscribe<DelayedFrameworkUpdateMessage>(this, (_) => PeriodicApiStateCheck());
try
{
PeriodicApiStateCheck();
}
catch (Exception ex)
{
logger.LogWarning(ex, "Failed to check for some IPC, plugin not installed?");
}
_glamourerStateChanged = StateChanged.Subscriber(pi, GlamourerChanged);
_glamourerStateChanged.Enable();
}
public bool Initialized => CheckPenumbraApiInternal() && CheckGlamourerApiInternal();
private string? _penumbraModDirectory;
public string? PenumbraModDirectory
{
get => _penumbraModDirectory;
private set
{
if (!string.Equals(_penumbraModDirectory, value, StringComparison.Ordinal))
{
_penumbraModDirectory = value;
Mediator.Publish(new PenumbraDirectoryChangedMessage(_penumbraModDirectory));
}
}
}
public bool CheckCustomizePlusApi() => _customizePlusAvailable;
public bool CheckGlamourerApi() => _glamourerAvailable;
public bool CheckHeelsApi() => _heelsAvailable;
public bool CheckHonorificApi() => _honorificAvailable;
public bool CheckPenumbraApi() => _penumbraAvailable;
public async Task CustomizePlusRevertAsync(IntPtr character)
{
if (!CheckCustomizePlusApi()) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj is ICharacter c)
{
Logger.LogTrace("CustomizePlus reverting for {chara}", c.Address.ToString("X"));
_customizePlusRevertCharacter!.InvokeFunc(c.ObjectIndex);
}
}).ConfigureAwait(false);
}
public async Task CustomizePlusRevertByIdAsync(Guid? profileId)
{
if (!CheckCustomizePlusApi() || profileId == null) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
_ = _customizePlusDeleteByUniqueId.InvokeFunc(profileId.Value);
}).ConfigureAwait(false);
}
public async Task<Guid?> CustomizePlusSetBodyScaleAsync(IntPtr character, string scale)
{
if (!CheckCustomizePlusApi()) return null;
return await _dalamudUtil.RunOnFrameworkThread(() =>
{
var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj is ICharacter c)
{
string decodedScale = Encoding.UTF8.GetString(Convert.FromBase64String(scale));
Logger.LogTrace("CustomizePlus applying for {chara}", c.Address.ToString("X"));
if (scale.IsNullOrEmpty())
{
_customizePlusRevertCharacter!.InvokeFunc(c.ObjectIndex);
return null;
}
else
{
var result = _customizePlusSetBodyScaleToCharacter!.InvokeFunc(c.ObjectIndex, decodedScale);
return result.Item2;
}
}
return null;
}).ConfigureAwait(false);
}
public async Task<string?> GetCustomizePlusScaleAsync(IntPtr character)
{
if (!CheckCustomizePlusApi()) return null;
var scale = await _dalamudUtil.RunOnFrameworkThread(() =>
{
var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj is ICharacter c)
{
var res = _customizePlusGetActiveProfile.InvokeFunc(c.ObjectIndex);
Logger.LogTrace("CustomizePlus GetActiveProfile returned {err}", res.Item1);
if (res.Item1 != 0 || res.Item2 == null) return string.Empty;
return _customizePlusGetProfileById.InvokeFunc(res.Item2.Value).Item2;
}
return string.Empty;
}).ConfigureAwait(false);
if (string.IsNullOrEmpty(scale)) return string.Empty;
return Convert.ToBase64String(Encoding.UTF8.GetBytes(scale));
}
public async Task<string> GetHeelsOffsetAsync()
{
if (!CheckHeelsApi()) return string.Empty;
return await _dalamudUtil.RunOnFrameworkThread(_heelsGetOffset.InvokeFunc).ConfigureAwait(false);
}
public async Task GlamourerApplyAllAsync(ILogger logger, GameObjectHandler handler, string? customization, Guid applicationId, CancellationToken token, bool fireAndForget = false)
{
if (!CheckGlamourerApi() || string.IsNullOrEmpty(customization) || _dalamudUtil.IsZoning) return;
try
{
await _redrawSemaphore.WaitAsync(token).ConfigureAwait(false);
await PenumbraRedrawInternalAsync(logger, handler, applicationId, (chara) =>
{
try
{
logger.LogDebug("[{appid}] Calling on IPC: GlamourerApplyAll", applicationId);
_glamourerApplyAll!.Invoke(customization, chara.ObjectIndex, LockCode);
}
catch (Exception ex)
{
logger.LogWarning(ex, "[{appid}] Failed to apply Glamourer data", applicationId);
}
}).ConfigureAwait(false);
}
finally
{
_redrawSemaphore.Release();
}
}
public async Task<string> GlamourerGetCharacterCustomizationAsync(IntPtr character)
{
if (!CheckGlamourerApi()) return string.Empty;
try
{
return await _dalamudUtil.RunOnFrameworkThread(() =>
{
var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj is ICharacter c)
{
return _glamourerGetAllCustomization!.Invoke(c.ObjectIndex).Item2 ?? string.Empty;
}
return string.Empty;
}).ConfigureAwait(false);
}
catch
{
return string.Empty;
}
}
public async Task GlamourerRevert(ILogger logger, string name, GameObjectHandler handler, Guid applicationId, CancellationToken token)
{
if ((!CheckGlamourerApi()) || _dalamudUtil.IsZoning) return;
try
{
await _redrawSemaphore.WaitAsync(token).ConfigureAwait(false);
await PenumbraRedrawInternalAsync(logger, handler, applicationId, (chara) =>
{
try
{
logger.LogDebug("[{appid}] Calling On IPC: GlamourerUnlockName", applicationId);
_glamourerUnlock.Invoke(chara.ObjectIndex, LockCode);
logger.LogDebug("[{appid}] Calling On IPC: GlamourerRevert", applicationId);
_glamourerRevert.Invoke(chara.ObjectIndex, LockCode);
logger.LogDebug("[{appid}] Calling On IPC: PenumbraRedraw", applicationId);
_penumbraRedraw.Invoke(chara.ObjectIndex, RedrawType.AfterGPose);
}
catch (Exception ex)
{
logger.LogWarning(ex, "[{appid}] Error during GlamourerRevert", applicationId);
}
}).ConfigureAwait(false);
}
finally
{
_redrawSemaphore.Release();
}
}
public async Task GlamourerRevertByNameAsync(ILogger logger, string name, Guid applicationId)
{
if ((!CheckGlamourerApi()) || _dalamudUtil.IsZoning) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
GlamourerRevertByName(logger, name, applicationId);
}).ConfigureAwait(false);
}
public void GlamourerRevertByName(ILogger logger, string name, Guid applicationId)
{
if ((!CheckGlamourerApi()) || _dalamudUtil.IsZoning) return;
try
{
logger.LogDebug("[{appid}] Calling On IPC: GlamourerRevertByName", applicationId);
_glamourerRevertByName.Invoke(name, LockCode);
logger.LogDebug("[{appid}] Calling On IPC: GlamourerUnlockName", applicationId);
_glamourerUnlockByName.Invoke(name, LockCode);
}
catch (Exception ex)
{
Logger.LogWarning(ex, "Error during Glamourer RevertByName");
}
}
public async Task HeelsRestoreOffsetForPlayerAsync(IntPtr character)
{
if (!CheckHeelsApi()) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj != null)
{
Logger.LogTrace("Restoring Heels data to {chara}", character.ToString("X"));
_heelsUnregisterPlayer.InvokeAction(gameObj.ObjectIndex);
}
}).ConfigureAwait(false);
}
public async Task HeelsSetOffsetForPlayerAsync(IntPtr character, string data)
{
if (!CheckHeelsApi()) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj != null)
{
Logger.LogTrace("Applying Heels data to {chara}", character.ToString("X"));
_heelsRegisterPlayer.InvokeAction(gameObj.ObjectIndex, data);
}
}).ConfigureAwait(false);
}
public async Task HonorificClearTitleAsync(nint character)
{
if (!CheckHonorificApi()) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj is IPlayerCharacter c)
{
Logger.LogTrace("Honorific removing for {addr}", c.Address.ToString("X"));
_honorificClearCharacterTitle!.InvokeAction(c.ObjectIndex);
}
}).ConfigureAwait(false);
}
public string HonorificGetTitle()
{
if (!CheckHonorificApi()) return string.Empty;
string title = _honorificGetLocalCharacterTitle.InvokeFunc();
return string.IsNullOrEmpty(title) ? string.Empty : Convert.ToBase64String(Encoding.UTF8.GetBytes(title));
}
public async Task HonorificSetTitleAsync(IntPtr character, string honorificDataB64)
{
if (!CheckHonorificApi()) return;
Logger.LogTrace("Applying Honorific data to {chara}", character.ToString("X"));
try
{
await _dalamudUtil.RunOnFrameworkThread(() =>
{
var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj is IPlayerCharacter pc)
{
string honorificData = string.IsNullOrEmpty(honorificDataB64) ? string.Empty : Encoding.UTF8.GetString(Convert.FromBase64String(honorificDataB64));
if (string.IsNullOrEmpty(honorificData))
{
_honorificClearCharacterTitle!.InvokeAction(pc.ObjectIndex);
}
else
{
_honorificSetCharacterTitle!.InvokeAction(pc.ObjectIndex, honorificData);
}
}
}).ConfigureAwait(false);
}
catch (Exception e)
{
Logger.LogWarning(e, "Could not apply Honorific data");
}
}
public async Task PenumbraAssignTemporaryCollectionAsync(ILogger logger, Guid collName, int idx)
{
if (!CheckPenumbraApi()) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
var retAssign = _penumbraAssignTemporaryCollection.Invoke(collName, idx, forceAssignment: true);
logger.LogTrace("Assigning Temp Collection {collName} to index {idx}, Success: {ret}", collName, idx, retAssign);
return collName;
}).ConfigureAwait(false);
}
public async Task PenumbraConvertTextureFiles(ILogger logger, Dictionary<string, string[]> textures, IProgress<(string, int)> progress, CancellationToken token)
{
if (!CheckPenumbraApi()) return;
Mediator.Publish(new HaltScanMessage(nameof(PenumbraConvertTextureFiles)));
int currentTexture = 0;
foreach (var texture in textures)
{
if (token.IsCancellationRequested) break;
progress.Report((texture.Key, ++currentTexture));
logger.LogInformation("Converting Texture {path} to {type}", texture.Key, TextureType.Bc7Tex);
var convertTask = _penumbraConvertTextureFile.Invoke(texture.Key, texture.Key, TextureType.Bc7Tex, mipMaps: true);
await convertTask.ConfigureAwait(false);
if (convertTask.IsCompletedSuccessfully && texture.Value.Any())
{
foreach (var duplicatedTexture in texture.Value)
{
logger.LogInformation("Migrating duplicate {dup}", duplicatedTexture);
try
{
File.Copy(texture.Key, duplicatedTexture, overwrite: true);
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to copy duplicate {dup}", duplicatedTexture);
}
}
}
}
Mediator.Publish(new ResumeScanMessage(nameof(PenumbraConvertTextureFiles)));
await _dalamudUtil.RunOnFrameworkThread(async () =>
{
var gameObject = await _dalamudUtil.CreateGameObjectAsync(await _dalamudUtil.GetPlayerPointerAsync().ConfigureAwait(false)).ConfigureAwait(false);
_penumbraRedraw.Invoke(gameObject!.ObjectIndex, RedrawType.Redraw);
}).ConfigureAwait(false);
}
public async Task<Guid> PenumbraCreateTemporaryCollectionAsync(ILogger logger, string uid)
{
if (!CheckPenumbraApi()) return Guid.Empty;
return await _dalamudUtil.RunOnFrameworkThread(() =>
{
var collName = "Loporrit_" + uid;
var collId = _penumbraCreateNamedTemporaryCollection.Invoke(collName);
logger.LogTrace("Creating Temp Collection {collName}, GUID: {collId}", collName, collId);
return collId;
}).ConfigureAwait(false);
}
public async Task<Dictionary<string, HashSet<string>>?> PenumbraGetCharacterData(ILogger logger, GameObjectHandler handler)
{
if (!CheckPenumbraApi()) return null;
return await _dalamudUtil.RunOnFrameworkThread(() =>
{
logger.LogTrace("Calling On IPC: Penumbra.GetGameObjectResourcePaths");
var idx = handler.GetGameObject()?.ObjectIndex;
if (idx == null) return null;
return _penumbraResourcePaths.Invoke(idx.Value)[0];
}).ConfigureAwait(false);
}
public string PenumbraGetMetaManipulations()
{
if (!CheckPenumbraApi()) return string.Empty;
return _penumbraGetMetaManipulations.Invoke();
}
public async Task PenumbraRedrawAsync(ILogger logger, GameObjectHandler handler, Guid applicationId, CancellationToken token)
{
if (!CheckPenumbraApi() || _dalamudUtil.IsZoning) return;
try
{
await _redrawSemaphore.WaitAsync(token).ConfigureAwait(false);
await PenumbraRedrawInternalAsync(logger, handler, applicationId, (chara) =>
{
logger.LogDebug("[{appid}] Calling on IPC: PenumbraRedraw", applicationId);
_penumbraRedraw.Invoke(chara.ObjectIndex, RedrawType.Redraw);
}).ConfigureAwait(false);
}
finally
{
_redrawSemaphore.Release();
}
}
public async Task PenumbraRemoveTemporaryCollectionAsync(ILogger logger, Guid applicationId, Guid collId)
{
if (!CheckPenumbraApi()) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
logger.LogTrace("[{applicationId}] Removing temp collection for {collId}", applicationId, collId);
var ret2 = _penumbraRemoveTemporaryCollection.Invoke(collId);
logger.LogTrace("[{applicationId}] RemoveTemporaryCollection: {ret2}", applicationId, ret2);
}).ConfigureAwait(false);
}
public async Task<(string[] forward, string[][] reverse)> PenumbraResolvePathsAsync(string[] forward, string[] reverse)
{
return await _penumbraResolvePaths.Invoke(forward, reverse).ConfigureAwait(false);
}
public async Task PenumbraSetManipulationDataAsync(ILogger logger, Guid applicationId, Guid collId, string manipulationData)
{
if (!CheckPenumbraApi()) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
logger.LogTrace("[{applicationId}] Manip: {data}", applicationId, manipulationData);
var retAdd = _penumbraAddTemporaryMod.Invoke("MareChara_Meta", collId, [], manipulationData, 0);
logger.LogTrace("[{applicationId}] Setting temp meta mod for {collId}, Success: {ret}", applicationId, collId, retAdd);
}).ConfigureAwait(false);
}
public async Task PenumbraSetTemporaryModsAsync(ILogger logger, Guid applicationId, Guid collId, Dictionary<string, string> modPaths)
{
if (!CheckPenumbraApi()) return;
await _dalamudUtil.RunOnFrameworkThread(() =>
{
foreach (var mod in modPaths)
{
logger.LogTrace("[{applicationId}] Change: {from} => {to}", applicationId, mod.Key, mod.Value);
}
var retRemove = _penumbraRemoveTemporaryMod.Invoke("MareChara_Files", collId, 0);
logger.LogTrace("[{applicationId}] Removing temp files mod for {collId}, Success: {ret}", applicationId, collId, retRemove);
var retAdd = _penumbraAddTemporaryMod.Invoke("MareChara_Files", collId, modPaths, string.Empty, 0);
logger.LogTrace("[{applicationId}] Setting temp files mod for {collId}, Success: {ret}", applicationId, collId, retAdd);
}).ConfigureAwait(false);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
_disposalCts.Cancel();
_glamourerStateChanged?.Dispose();
_penumbraModSettingChanged.Dispose();
_penumbraGameObjectResourcePathResolved.Dispose();
_penumbraDispose.Dispose();
_penumbraInit.Dispose();
_penumbraObjectIsRedrawn.Dispose();
_heelsOffsetUpdate.Unsubscribe(HeelsOffsetChange);
_customizePlusOnScaleUpdate.Unsubscribe(OnCustomizePlusScaleChange);
_honorificLocalCharacterTitleChanged.Unsubscribe(OnHonorificLocalCharacterTitleChanged);
_honorificDisposing.Unsubscribe(OnHonorificDisposing);
_honorificReady.Unsubscribe(OnHonorificReady);
}
private bool CheckCustomizePlusApiInternal()
{
try
{
var version = _customizePlusApiVersion.InvokeFunc();
if (version.Item1 == 6 && version.Item2 >= 0) return true;
return false;
}
catch
{
return false;
}
}
private bool CheckGlamourerApiInternal()
{
bool apiAvailable = false;
try
{
bool versionValid = (_pi.InstalledPlugins
.FirstOrDefault(p => string.Equals(p.InternalName, "Glamourer", StringComparison.OrdinalIgnoreCase))
?.Version ?? new Version(0, 0, 0, 0)) >= new Version(1, 0, 6, 1);
try
{
var version = _glamourerApiVersions.Invoke();
if (version is { Major: 1, Minor: >= 1 } && versionValid)
{
apiAvailable = true;
}
}
catch
{
}
_shownGlamourerUnavailable = _shownGlamourerUnavailable && !apiAvailable;
return apiAvailable;
}
catch
{
return apiAvailable;
}
finally
{
if (!apiAvailable && !_shownGlamourerUnavailable)
{
_shownGlamourerUnavailable = true;
Mediator.Publish(new NotificationMessage("Glamourer inactive", "Your Glamourer installation is not active or out of date. Update Glamourer to continue to use Loporrit.", NotificationType.Error));
}
}
}
private bool CheckHeelsApiInternal()
{
try
{
return _heelsGetApiVersion.InvokeFunc() is { Item1: 2, Item2: >= 0 };
}
catch
{
return false;
}
}
private bool CheckHonorificApiInternal()
{
try
{
return _honorificApiVersion.InvokeFunc() is { Item1: 3, Item2: >= 0 };
}
catch
{
return false;
}
}
private bool CheckPenumbraApiInternal()
{
bool penumbraAvailable = false;
try
{
bool pluginFound = (_pi.InstalledPlugins
.FirstOrDefault(p => string.Equals(p.InternalName, "Penumbra", StringComparison.OrdinalIgnoreCase))
?.Version ?? new Version(0, 0, 0, 0)) >= new Version(1, 0, 1, 0);
try
{
_ = _penumbraApiVersion.Invoke();
}
catch
{
}
penumbraAvailable = pluginFound && _penumbraEnabled.Invoke();
_shownPenumbraUnavailable = _shownPenumbraUnavailable && !penumbraAvailable;
return penumbraAvailable;
}
catch
{
return penumbraAvailable;
}
finally
{
if (!penumbraAvailable && !_shownPenumbraUnavailable)
{
_shownPenumbraUnavailable = true;
Mediator.Publish(new NotificationMessage("Penumbra inactive", "Your Penumbra installation is not active or out of date. Update Penumbra and/or the Enable Mods setting in Penumbra to continue to use Loporrit.", NotificationType.Error));
}
}
}
private string? GetPenumbraModDirectoryInternal()
{
if (!CheckPenumbraApi()) return null;
return _penumbraResolveModDir!.Invoke().ToLowerInvariant();
}
private void GlamourerChanged(nint address)
{
Mediator.Publish(new GlamourerChangedMessage(address));
}
private void HeelsOffsetChange(string offset)
{
Mediator.Publish(new HeelsOffsetMessage());
}
private void OnCustomizePlusScaleChange(ushort c, Guid g)
{
var obj = _dalamudUtil.GetCharacterFromObjectTableByIndex(c);
Mediator.Publish(new CustomizePlusMessage(obj?.Name.ToString() ?? string.Empty));
}
private void OnHonorificDisposing()
{
Mediator.Publish(new HonorificMessage(string.Empty));
}
private void OnHonorificLocalCharacterTitleChanged(string titleJson)
{
string titleData = string.IsNullOrEmpty(titleJson) ? string.Empty : Convert.ToBase64String(Encoding.UTF8.GetBytes(titleJson));
Mediator.Publish(new HonorificMessage(titleData));
}
private void OnHonorificReady()
{
_honorificAvailable = CheckHonorificApiInternal();
Mediator.Publish(new HonorificReadyMessage());
}
private void PenumbraDispose()
{
_disposalCts.Cancel();
_disposalCts.Dispose();
Mediator.Publish(new PenumbraDisposedMessage());
_disposalCts = new();
}
private void PenumbraInit()
{
PenumbraModDirectory = _penumbraResolveModDir.Invoke();
_penumbraAvailable = true;
Mediator.Publish(new PenumbraInitializedMessage());
_penumbraRedraw!.Invoke(0, RedrawType.Redraw);
}
private async Task PenumbraRedrawInternalAsync(ILogger logger, GameObjectHandler handler, Guid applicationId, Action<ICharacter> action)
{
Mediator.Publish(new PenumbraStartRedrawMessage(handler.Address));
_penumbraRedrawRequests[handler.Address] = true;
try
{
CancellationTokenSource cancelToken = new CancellationTokenSource();
cancelToken.CancelAfter(TimeSpan.FromSeconds(15));
await handler.ActOnFrameworkAfterEnsureNoDrawAsync(action, cancelToken.Token).ConfigureAwait(false);
if (!_disposalCts.Token.IsCancellationRequested)
await _dalamudUtil.WaitWhileCharacterIsDrawing(logger, handler, applicationId, 30000, _disposalCts.Token).ConfigureAwait(false);
}
finally
{
_penumbraRedrawRequests[handler.Address] = false;
}
Mediator.Publish(new PenumbraEndRedrawMessage(handler.Address));
}
private void PeriodicApiStateCheck()
{
_glamourerAvailable = CheckGlamourerApiInternal();
_penumbraAvailable = CheckPenumbraApiInternal();
_heelsAvailable = CheckHeelsApiInternal();
_customizePlusAvailable = CheckCustomizePlusApiInternal();
_honorificAvailable = CheckHonorificApiInternal();
PenumbraModDirectory = GetPenumbraModDirectoryInternal();
}
private void RedrawEvent(IntPtr objectAddress, int objectTableIndex)
{
bool wasRequested = false;
if (_penumbraRedrawRequests.TryGetValue(objectAddress, out var redrawRequest) && redrawRequest)
{
_penumbraRedrawRequests[objectAddress] = false;
}
else
{
Mediator.Publish(new PenumbraRedrawMessage(objectAddress, objectTableIndex, wasRequested));
}
}
private void ResourceLoaded(IntPtr ptr, string arg1, string arg2)
{
if (ptr != IntPtr.Zero && string.Compare(arg1, arg2, ignoreCase: true, System.Globalization.CultureInfo.InvariantCulture) != 0)
{
Mediator.Publish(new PenumbraResourceLoadMessage(ptr, arg1, arg2));
}
}
}

View File

@@ -2,7 +2,7 @@
using K4os.Compression.LZ4.Legacy; using K4os.Compression.LZ4.Legacy;
using MareSynchronos.API.Data.Enum; using MareSynchronos.API.Data.Enum;
using MareSynchronos.FileCache; using MareSynchronos.FileCache;
using MareSynchronos.Interop; using MareSynchronos.Interop.Ipc;
using MareSynchronos.MareConfiguration; using MareSynchronos.MareConfiguration;
using MareSynchronos.PlayerData.Factories; using MareSynchronos.PlayerData.Factories;
using MareSynchronos.PlayerData.Handlers; using MareSynchronos.PlayerData.Handlers;
@@ -51,12 +51,12 @@ public class MareCharaFileManager : DisposableMediatorSubscriberBase
{ {
if ((await dalamudUtil.RunOnFrameworkThread(() => item.Value.CurrentAddress()).ConfigureAwait(false)) != nint.Zero) if ((await dalamudUtil.RunOnFrameworkThread(() => item.Value.CurrentAddress()).ConfigureAwait(false)) != nint.Zero)
{ {
await _ipcManager.GlamourerRevert(logger, item.Value.Name, item.Value, Guid.NewGuid(), cts.Token).ConfigureAwait(false); await _ipcManager.Glamourer.RevertAsync(logger, item.Value, Guid.NewGuid(), cts.Token).ConfigureAwait(false);
} }
else else
{ {
_logger.LogDebug("Reverting by name: {name}", item.Key); _logger.LogDebug("Reverting by name: {name}", item.Key);
_ipcManager.GlamourerRevertByName(logger, item.Key, Guid.NewGuid()); _ipcManager.Glamourer.RevertByName(logger, item.Key, Guid.NewGuid());
} }
@@ -64,7 +64,7 @@ public class MareCharaFileManager : DisposableMediatorSubscriberBase
} }
foreach (var id in _gposeCustomizeObjects.Where(d => d != null)) foreach (var id in _gposeCustomizeObjects.Where(d => d != null))
{ {
await _ipcManager.CustomizePlusRevertByIdAsync(id).ConfigureAwait(false); await _ipcManager.CustomizePlus.RevertByIdAsync(id).ConfigureAwait(false);
} }
_gposeGameObjects.Clear(); _gposeGameObjects.Clear();
}); });
@@ -99,10 +99,10 @@ public class MareCharaFileManager : DisposableMediatorSubscriberBase
} }
} }
var applicationId = Guid.NewGuid(); var applicationId = Guid.NewGuid();
var coll = await _ipcManager.PenumbraCreateTemporaryCollectionAsync(_logger, charaTarget.Name.TextValue).ConfigureAwait(false); var coll = await _ipcManager.Penumbra.CreateTemporaryCollectionAsync(_logger, charaTarget.Name.TextValue).ConfigureAwait(false);
await _ipcManager.PenumbraAssignTemporaryCollectionAsync(_logger, coll, charaTarget.ObjectIndex).ConfigureAwait(false); await _ipcManager.Penumbra.AssignTemporaryCollectionAsync(_logger, coll, charaTarget.ObjectTableIndex()!.Value).ConfigureAwait(false);
await _ipcManager.PenumbraSetTemporaryModsAsync(_logger, applicationId, coll, extractedFiles.Union(fileSwaps).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal)).ConfigureAwait(false); await _ipcManager.Penumbra.SetTemporaryModsAsync(_logger, applicationId, coll, extractedFiles.Union(fileSwaps).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal)).ConfigureAwait(false);
await _ipcManager.PenumbraSetManipulationDataAsync(_logger, applicationId, coll, LoadedCharaFile.CharaFileData.ManipulationData).ConfigureAwait(false); await _ipcManager.Penumbra.SetManipulationDataAsync(_logger, applicationId, coll, LoadedCharaFile.CharaFileData.ManipulationData).ConfigureAwait(false);
GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Player, GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Player,
() => _dalamudUtil.GetGposeCharacterFromObjectTableByName(charaTarget.Name.ToString(), _isInGpose)?.Address ?? IntPtr.Zero, isWatched: false).ConfigureAwait(false); () => _dalamudUtil.GetGposeCharacterFromObjectTableByName(charaTarget.Name.ToString(), _isInGpose)?.Address ?? IntPtr.Zero, isWatched: false).ConfigureAwait(false);
@@ -110,18 +110,18 @@ public class MareCharaFileManager : DisposableMediatorSubscriberBase
if (!_gposeGameObjects.ContainsKey(charaTarget.Name.ToString())) if (!_gposeGameObjects.ContainsKey(charaTarget.Name.ToString()))
_gposeGameObjects[charaTarget.Name.ToString()] = tempHandler; _gposeGameObjects[charaTarget.Name.ToString()] = tempHandler;
await _ipcManager.GlamourerApplyAllAsync(_logger, tempHandler, LoadedCharaFile.CharaFileData.GlamourerData, applicationId, disposeCts.Token).ConfigureAwait(false); await _ipcManager.Glamourer.ApplyAllAsync(_logger, tempHandler, LoadedCharaFile.CharaFileData.GlamourerData, applicationId, disposeCts.Token).ConfigureAwait(false);
await _ipcManager.PenumbraRedrawAsync(_logger, tempHandler, applicationId, disposeCts.Token).ConfigureAwait(false); await _ipcManager.Penumbra.RedrawAsync(_logger, tempHandler, applicationId, disposeCts.Token).ConfigureAwait(false);
_dalamudUtil.WaitWhileGposeCharacterIsDrawing(charaTarget.Address, 30000); _dalamudUtil.WaitWhileGposeCharacterIsDrawing(charaTarget.Address, 30000);
await _ipcManager.PenumbraRemoveTemporaryCollectionAsync(_logger, applicationId, coll).ConfigureAwait(false); await _ipcManager.Penumbra.RemoveTemporaryCollectionAsync(_logger, applicationId, coll).ConfigureAwait(false);
if (!string.IsNullOrEmpty(LoadedCharaFile.CharaFileData.CustomizePlusData)) if (!string.IsNullOrEmpty(LoadedCharaFile.CharaFileData.CustomizePlusData))
{ {
var id = await _ipcManager.CustomizePlusSetBodyScaleAsync(tempHandler.Address, LoadedCharaFile.CharaFileData.CustomizePlusData).ConfigureAwait(false); var id = await _ipcManager.CustomizePlus.SetBodyScaleAsync(tempHandler.Address, LoadedCharaFile.CharaFileData.CustomizePlusData).ConfigureAwait(false);
_gposeCustomizeObjects.Add(id); _gposeCustomizeObjects.Add(id);
} }
else else
{ {
var id = await _ipcManager.CustomizePlusSetBodyScaleAsync(tempHandler.Address, Convert.ToBase64String(Encoding.UTF8.GetBytes("{}"))).ConfigureAwait(false); var id = await _ipcManager.CustomizePlus.SetBodyScaleAsync(tempHandler.Address, Convert.ToBase64String(Encoding.UTF8.GetBytes("{}"))).ConfigureAwait(false);
_gposeCustomizeObjects.Add(id); _gposeCustomizeObjects.Add(id);
} }
} }

View File

@@ -1,6 +1,6 @@
using MareSynchronos.API.Dto.User; using MareSynchronos.API.Dto.User;
using MareSynchronos.FileCache; using MareSynchronos.FileCache;
using MareSynchronos.Interop; using MareSynchronos.Interop.Ipc;
using MareSynchronos.PlayerData.Handlers; using MareSynchronos.PlayerData.Handlers;
using MareSynchronos.PlayerData.Pairs; using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.Services; using MareSynchronos.Services;

View File

@@ -1,12 +1,11 @@
using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Character;
using MareSynchronos.API.Data.Enum; using MareSynchronos.API.Data.Enum;
using MareSynchronos.FileCache; using MareSynchronos.FileCache;
using MareSynchronos.Interop; using MareSynchronos.Interop.Ipc;
using MareSynchronos.PlayerData.Data; using MareSynchronos.PlayerData.Data;
using MareSynchronos.PlayerData.Handlers; using MareSynchronos.PlayerData.Handlers;
using MareSynchronos.Services; using MareSynchronos.Services;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Diagnostics;
using CharacterData = MareSynchronos.PlayerData.Data.CharacterData; using CharacterData = MareSynchronos.PlayerData.Data.CharacterData;
namespace MareSynchronos.PlayerData.Factories; namespace MareSynchronos.PlayerData.Factories;
@@ -141,7 +140,7 @@ public class PlayerDataFactory
// penumbra call, it's currently broken // penumbra call, it's currently broken
Dictionary<string, HashSet<string>>? resolvedPaths; Dictionary<string, HashSet<string>>? resolvedPaths;
resolvedPaths = await _ipcManager.PenumbraGetCharacterData(_logger, playerRelatedObject).ConfigureAwait(false); resolvedPaths = await _ipcManager.Penumbra.GetCharacterData(_logger, playerRelatedObject).ConfigureAwait(false);
if (resolvedPaths == null) throw new InvalidOperationException("Penumbra returned null data"); if (resolvedPaths == null) throw new InvalidOperationException("Penumbra returned null data");
previousData.FileReplacements[objectKind] = previousData.FileReplacements[objectKind] =
@@ -192,16 +191,16 @@ public class PlayerDataFactory
} }
// gather up data from ipc // gather up data from ipc
previousData.ManipulationString = _ipcManager.PenumbraGetMetaManipulations(); previousData.ManipulationString = _ipcManager.Penumbra.GetMetaManipulations();
Task<string> getHeelsOffset = _ipcManager.GetHeelsOffsetAsync(); Task<string> getHeelsOffset = _ipcManager.Heels.GetOffsetAsync();
Task<string> getGlamourerData = _ipcManager.GlamourerGetCharacterCustomizationAsync(playerRelatedObject.Address); Task<string> getGlamourerData = _ipcManager.Glamourer.GetCharacterCustomizationAsync(playerRelatedObject.Address);
Task<string?> getCustomizeData = _ipcManager.GetCustomizePlusScaleAsync(playerRelatedObject.Address); Task<string?> getCustomizeData = _ipcManager.CustomizePlus.GetScaleAsync(playerRelatedObject.Address);
previousData.GlamourerString[playerRelatedObject.ObjectKind] = await getGlamourerData.ConfigureAwait(false); previousData.GlamourerString[playerRelatedObject.ObjectKind] = await getGlamourerData.ConfigureAwait(false);
_logger.LogDebug("Glamourer is now: {data}", previousData.GlamourerString[playerRelatedObject.ObjectKind]); _logger.LogDebug("Glamourer is now: {data}", previousData.GlamourerString[playerRelatedObject.ObjectKind]);
var customizeScale = await getCustomizeData.ConfigureAwait(false); var customizeScale = await getCustomizeData.ConfigureAwait(false);
previousData.CustomizePlusScale[playerRelatedObject.ObjectKind] = customizeScale ?? string.Empty; previousData.CustomizePlusScale[playerRelatedObject.ObjectKind] = customizeScale ?? string.Empty;
_logger.LogDebug("Customize is now: {data}", previousData.CustomizePlusScale[playerRelatedObject.ObjectKind]); _logger.LogDebug("Customize is now: {data}", previousData.CustomizePlusScale[playerRelatedObject.ObjectKind]);
previousData.HonorificData = _ipcManager.HonorificGetTitle(); previousData.HonorificData = _ipcManager.Honorific.GetTitle();
_logger.LogDebug("Honorific is now: {data}", previousData.HonorificData); _logger.LogDebug("Honorific is now: {data}", previousData.HonorificData);
previousData.HeelsData = await getHeelsOffset.ConfigureAwait(false); previousData.HeelsData = await getHeelsOffset.ConfigureAwait(false);
_logger.LogDebug("Heels is now: {heels}", previousData.HeelsData); _logger.LogDebug("Heels is now: {heels}", previousData.HeelsData);
@@ -232,7 +231,7 @@ public class PlayerDataFactory
var forwardPaths = forwardResolve.ToArray(); var forwardPaths = forwardResolve.ToArray();
var reversePaths = reverseResolve.ToArray(); var reversePaths = reverseResolve.ToArray();
Dictionary<string, List<string>> resolvedPaths = new(StringComparer.Ordinal); Dictionary<string, List<string>> resolvedPaths = new(StringComparer.Ordinal);
var (forward, reverse) = await _ipcManager.PenumbraResolvePathsAsync(forwardPaths, reversePaths).ConfigureAwait(false); var (forward, reverse) = await _ipcManager.Penumbra.ResolvePathsAsync(forwardPaths, reversePaths).ConfigureAwait(false);
for (int i = 0; i < forwardPaths.Length; i++) for (int i = 0; i < forwardPaths.Length; i++)
{ {
var filePath = forward[i].ToLowerInvariant(); var filePath = forward[i].ToLowerInvariant();

View File

@@ -1,7 +1,7 @@
using MareSynchronos.API.Data; using MareSynchronos.API.Data;
using MareSynchronos.API.Dto.User; using MareSynchronos.API.Dto.User;
using MareSynchronos.FileCache; using MareSynchronos.FileCache;
using MareSynchronos.Interop; using MareSynchronos.Interop.Ipc;
using MareSynchronos.PlayerData.Factories; using MareSynchronos.PlayerData.Factories;
using MareSynchronos.PlayerData.Pairs; using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.Services; using MareSynchronos.Services;
@@ -59,7 +59,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
_dalamudUtil = dalamudUtil; _dalamudUtil = dalamudUtil;
_lifetime = lifetime; _lifetime = lifetime;
_fileDbManager = fileDbManager; _fileDbManager = fileDbManager;
_penumbraCollection = _ipcManager.PenumbraCreateTemporaryCollectionAsync(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult(); _penumbraCollection = _ipcManager.Penumbra.CreateTemporaryCollectionAsync(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult();
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate()); Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
Mediator.Subscribe<ZoneSwitchStartMessage>(this, (_) => Mediator.Subscribe<ZoneSwitchStartMessage>(this, (_) =>
@@ -70,7 +70,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
}); });
Mediator.Subscribe<PenumbraInitializedMessage>(this, (_) => Mediator.Subscribe<PenumbraInitializedMessage>(this, (_) =>
{ {
_penumbraCollection = _ipcManager.PenumbraCreateTemporaryCollectionAsync(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult(); _penumbraCollection = _ipcManager.Penumbra.CreateTemporaryCollectionAsync(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult();
if (!IsVisible && _charaHandler != null) if (!IsVisible && _charaHandler != null)
{ {
PlayerName = string.Empty; PlayerName = string.Empty;
@@ -160,7 +160,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
if (string.Equals(characterData.DataHash.Value, _cachedData?.DataHash.Value ?? string.Empty, StringComparison.Ordinal) && !forceApplyCustomization) return; if (string.Equals(characterData.DataHash.Value, _cachedData?.DataHash.Value ?? string.Empty, StringComparison.Ordinal) && !forceApplyCustomization) return;
if (_dalamudUtil.IsInCutscene || _dalamudUtil.IsInGpose || !_ipcManager.CheckPenumbraApi() || !_ipcManager.CheckGlamourerApi()) if (_dalamudUtil.IsInCutscene || _dalamudUtil.IsInGpose || !_ipcManager.Penumbra.APIAvailable || !_ipcManager.Glamourer.APIAvailable)
{ {
Mediator.Publish(new EventMessage(new Event(PlayerName, OnlineUser.User, nameof(PairHandler), EventSeverity.Warning, Mediator.Publish(new EventMessage(new Event(PlayerName, OnlineUser.User, nameof(PairHandler), EventSeverity.Warning,
"Cannot apply character data: you are in GPose, a Cutscene or Penumbra/Glamourer is not available"))); "Cannot apply character data: you are in GPose, a Cutscene or Penumbra/Glamourer is not available")));
@@ -238,7 +238,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
if (_lifetime.IsCancellationRequested) return; if (_lifetime.IsCancellationRequested) return;
Logger.LogDebug("[{applicationId}] Removing Temp Collection for {name} ({user})", applicationId, name, OnlineUser); Logger.LogDebug("[{applicationId}] Removing Temp Collection for {name} ({user})", applicationId, name, OnlineUser);
_ipcManager.PenumbraRemoveTemporaryCollectionAsync(Logger, applicationId, _penumbraCollection).GetAwaiter().GetResult(); _ipcManager.Penumbra.RemoveTemporaryCollectionAsync(Logger, applicationId, _penumbraCollection).GetAwaiter().GetResult();
if (_dalamudUtil is { IsZoning: false, IsInCutscene: false } && !string.IsNullOrEmpty(name)) if (_dalamudUtil is { IsZoning: false, IsInCutscene: false } && !string.IsNullOrEmpty(name))
{ {
@@ -246,7 +246,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
if (!IsVisible) if (!IsVisible)
{ {
Logger.LogDebug("[{applicationId}] Restoring Glamourer for {name} ({user})", applicationId, name, OnlineUser); Logger.LogDebug("[{applicationId}] Restoring Glamourer for {name} ({user})", applicationId, name, OnlineUser);
_ipcManager.GlamourerRevertByNameAsync(Logger, name, applicationId).GetAwaiter().GetResult(); _ipcManager.Glamourer.RevertByNameAsync(Logger, name, applicationId).GetAwaiter().GetResult();
} }
else else
{ {
@@ -314,32 +314,32 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
case PlayerChanges.Customize: case PlayerChanges.Customize:
if (charaData.CustomizePlusData.TryGetValue(changes.Key, out var customizePlusData)) if (charaData.CustomizePlusData.TryGetValue(changes.Key, out var customizePlusData))
{ {
_customizeIds[changes.Key] = await _ipcManager.CustomizePlusSetBodyScaleAsync(handler.Address, customizePlusData).ConfigureAwait(false); _customizeIds[changes.Key] = await _ipcManager.CustomizePlus.SetBodyScaleAsync(handler.Address, customizePlusData).ConfigureAwait(false);
} }
else if (_customizeIds.TryGetValue(changes.Key, out var customizeId)) else if (_customizeIds.TryGetValue(changes.Key, out var customizeId))
{ {
await _ipcManager.CustomizePlusRevertByIdAsync(customizeId).ConfigureAwait(false); await _ipcManager.CustomizePlus.RevertByIdAsync(customizeId).ConfigureAwait(false);
_customizeIds.Remove(changes.Key); _customizeIds.Remove(changes.Key);
} }
break; break;
case PlayerChanges.Heels: case PlayerChanges.Heels:
await _ipcManager.HeelsSetOffsetForPlayerAsync(handler.Address, charaData.HeelsData).ConfigureAwait(false); await _ipcManager.Heels.SetOffsetForPlayerAsync(handler.Address, charaData.HeelsData).ConfigureAwait(false);
break; break;
case PlayerChanges.Honorific: case PlayerChanges.Honorific:
await _ipcManager.HonorificSetTitleAsync(handler.Address, charaData.HonorificData).ConfigureAwait(false); await _ipcManager.Honorific.SetTitleAsync(handler.Address, charaData.HonorificData).ConfigureAwait(false);
break; break;
case PlayerChanges.Glamourer: case PlayerChanges.Glamourer:
if (charaData.GlamourerData.TryGetValue(changes.Key, out var glamourerData)) if (charaData.GlamourerData.TryGetValue(changes.Key, out var glamourerData))
{ {
await _ipcManager.GlamourerApplyAllAsync(Logger, handler, glamourerData, applicationId, token).ConfigureAwait(false); await _ipcManager.Glamourer.ApplyAllAsync(Logger, handler, glamourerData, applicationId, token).ConfigureAwait(false);
} }
break; break;
case PlayerChanges.ForcedRedraw: case PlayerChanges.ForcedRedraw:
await _ipcManager.PenumbraRedrawAsync(Logger, handler, applicationId, token).ConfigureAwait(false); await _ipcManager.Penumbra.RedrawAsync(Logger, handler, applicationId, token).ConfigureAwait(false);
break; break;
default: default:
@@ -437,7 +437,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
if (updateModdedPaths) if (updateModdedPaths)
{ {
await _ipcManager.PenumbraSetTemporaryModsAsync(Logger, _applicationId, _penumbraCollection, moddedPaths).ConfigureAwait(false); await _ipcManager.Penumbra.SetTemporaryModsAsync(Logger, _applicationId, _penumbraCollection, moddedPaths).ConfigureAwait(false);
LastAppliedDataSize = -1; LastAppliedDataSize = -1;
foreach (var path in moddedPaths.Values.Distinct(StringComparer.OrdinalIgnoreCase).Select(v => new FileInfo(v)).Where(p => p.Exists)) foreach (var path in moddedPaths.Values.Distinct(StringComparer.OrdinalIgnoreCase).Select(v => new FileInfo(v)).Where(p => p.Exists))
{ {
@@ -447,7 +447,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
if (updateManip) if (updateManip)
{ {
await _ipcManager.PenumbraSetManipulationDataAsync(Logger, _applicationId, _penumbraCollection, charaData.ManipulationData).ConfigureAwait(false); await _ipcManager.Penumbra.SetManipulationDataAsync(Logger, _applicationId, _penumbraCollection, charaData.ManipulationData).ConfigureAwait(false);
} }
token.ThrowIfCancellationRequested(); token.ThrowIfCancellationRequested();
@@ -531,10 +531,10 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
{ {
if (string.IsNullOrEmpty(_cachedData?.HonorificData)) return; if (string.IsNullOrEmpty(_cachedData?.HonorificData)) return;
Logger.LogTrace("Reapplying Honorific data for {this}", this); Logger.LogTrace("Reapplying Honorific data for {this}", this);
await _ipcManager.HonorificSetTitleAsync(PlayerCharacter, _cachedData.HonorificData).ConfigureAwait(false); await _ipcManager.Honorific.SetTitleAsync(PlayerCharacter, _cachedData.HonorificData).ConfigureAwait(false);
}); });
_ipcManager.PenumbraAssignTemporaryCollectionAsync(Logger, _penumbraCollection, _charaHandler.GetGameObject()!.ObjectIndex).GetAwaiter().GetResult(); _ipcManager.Penumbra.AssignTemporaryCollectionAsync(Logger, _penumbraCollection, _charaHandler.GetGameObject()!.ObjectIndex).GetAwaiter().GetResult();
_serverConfigurationManager.SetNameForUid(OnlineUser.User.UID, name); _serverConfigurationManager.SetNameForUid(OnlineUser.User.UID, name);
} }
@@ -555,26 +555,26 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Player, () => address, isWatched: false).ConfigureAwait(false); using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Player, () => address, isWatched: false).ConfigureAwait(false);
tempHandler.CompareNameAndThrow(name); tempHandler.CompareNameAndThrow(name);
Logger.LogDebug("[{applicationId}] Restoring Customization and Equipment for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name); Logger.LogDebug("[{applicationId}] Restoring Customization and Equipment for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name);
await _ipcManager.GlamourerRevert(Logger, name, tempHandler, applicationId, cancelToken).ConfigureAwait(false); await _ipcManager.Glamourer.RevertAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false);
tempHandler.CompareNameAndThrow(name); tempHandler.CompareNameAndThrow(name);
Logger.LogDebug("[{applicationId}] Restoring Heels for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name); Logger.LogDebug("[{applicationId}] Restoring Heels for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name);
await _ipcManager.HeelsRestoreOffsetForPlayerAsync(address).ConfigureAwait(false); await _ipcManager.Heels.RestoreOffsetForPlayerAsync(address).ConfigureAwait(false);
tempHandler.CompareNameAndThrow(name); tempHandler.CompareNameAndThrow(name);
Logger.LogDebug("[{applicationId}] Restoring C+ for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name); Logger.LogDebug("[{applicationId}] Restoring C+ for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name);
await _ipcManager.CustomizePlusRevertByIdAsync(customizeId).ConfigureAwait(false); await _ipcManager.CustomizePlus.RevertByIdAsync(customizeId).ConfigureAwait(false);
tempHandler.CompareNameAndThrow(name); tempHandler.CompareNameAndThrow(name);
Logger.LogDebug("[{applicationId}] Restoring Honorific for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name); Logger.LogDebug("[{applicationId}] Restoring Honorific for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name);
await _ipcManager.HonorificClearTitleAsync(address).ConfigureAwait(false); await _ipcManager.Honorific.ClearTitleAsync(address).ConfigureAwait(false);
} }
else if (objectKind == ObjectKind.MinionOrMount) else if (objectKind == ObjectKind.MinionOrMount)
{ {
var minionOrMount = await _dalamudUtil.GetMinionOrMountAsync(address).ConfigureAwait(false); var minionOrMount = await _dalamudUtil.GetMinionOrMountAsync(address).ConfigureAwait(false);
if (minionOrMount != nint.Zero) if (minionOrMount != nint.Zero)
{ {
await _ipcManager.CustomizePlusRevertByIdAsync(customizeId).ConfigureAwait(false); await _ipcManager.CustomizePlus.RevertByIdAsync(customizeId).ConfigureAwait(false);
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.MinionOrMount, () => minionOrMount, isWatched: false).ConfigureAwait(false); using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.MinionOrMount, () => minionOrMount, isWatched: false).ConfigureAwait(false);
await _ipcManager.GlamourerRevert(Logger, tempHandler.Name, tempHandler, applicationId, cancelToken).ConfigureAwait(false); await _ipcManager.Glamourer.RevertAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false);
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false); await _ipcManager.Penumbra.RedrawAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false);
} }
} }
else if (objectKind == ObjectKind.Pet) else if (objectKind == ObjectKind.Pet)
@@ -582,10 +582,10 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
var pet = await _dalamudUtil.GetPetAsync(address).ConfigureAwait(false); var pet = await _dalamudUtil.GetPetAsync(address).ConfigureAwait(false);
if (pet != nint.Zero) if (pet != nint.Zero)
{ {
await _ipcManager.CustomizePlusRevertByIdAsync(customizeId).ConfigureAwait(false); await _ipcManager.CustomizePlus.RevertByIdAsync(customizeId).ConfigureAwait(false);
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => pet, isWatched: false).ConfigureAwait(false); using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => pet, isWatched: false).ConfigureAwait(false);
await _ipcManager.GlamourerRevert(Logger, tempHandler.Name, tempHandler, applicationId, cancelToken).ConfigureAwait(false); await _ipcManager.Glamourer.RevertAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false);
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false); await _ipcManager.Penumbra.RedrawAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false);
} }
} }
else if (objectKind == ObjectKind.Companion) else if (objectKind == ObjectKind.Companion)
@@ -593,10 +593,10 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
var companion = await _dalamudUtil.GetCompanionAsync(address).ConfigureAwait(false); var companion = await _dalamudUtil.GetCompanionAsync(address).ConfigureAwait(false);
if (companion != nint.Zero) if (companion != nint.Zero)
{ {
await _ipcManager.CustomizePlusRevertByIdAsync(customizeId).ConfigureAwait(false); await _ipcManager.CustomizePlus.RevertByIdAsync(customizeId).ConfigureAwait(false);
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => companion, isWatched: false).ConfigureAwait(false); using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => companion, isWatched: false).ConfigureAwait(false);
await _ipcManager.GlamourerRevert(Logger, tempHandler.Name, tempHandler, applicationId, cancelToken).ConfigureAwait(false); await _ipcManager.Glamourer.RevertAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false);
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false); await _ipcManager.Penumbra.RedrawAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false);
} }
} }
} }

View File

@@ -5,6 +5,7 @@ using Dalamud.Plugin;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using MareSynchronos.FileCache; using MareSynchronos.FileCache;
using MareSynchronos.Interop; using MareSynchronos.Interop;
using MareSynchronos.Interop.Ipc;
using MareSynchronos.MareConfiguration; using MareSynchronos.MareConfiguration;
using MareSynchronos.PlayerData.Export; using MareSynchronos.PlayerData.Export;
using MareSynchronos.PlayerData.Factories; using MareSynchronos.PlayerData.Factories;
@@ -98,8 +99,21 @@ public sealed class Plugin : IDalamudPlugin
s.GetRequiredService<MareMediator>(), s.GetRequiredService<PerformanceCollectorService>())); s.GetRequiredService<MareMediator>(), s.GetRequiredService<PerformanceCollectorService>()));
collection.AddSingleton((s) => new DtrEntry(s.GetRequiredService<ILogger<DtrEntry>>(), dtrBar, s.GetRequiredService<MareConfigService>(), collection.AddSingleton((s) => new DtrEntry(s.GetRequiredService<ILogger<DtrEntry>>(), dtrBar, s.GetRequiredService<MareConfigService>(),
s.GetRequiredService<MareMediator>(), s.GetRequiredService<PairManager>(), s.GetRequiredService<ApiController>())); s.GetRequiredService<MareMediator>(), s.GetRequiredService<PairManager>(), s.GetRequiredService<ApiController>()));
collection.AddSingleton<RedrawManager>();
collection.AddSingleton((s) => new IpcCallerPenumbra(s.GetRequiredService<ILogger<IpcCallerPenumbra>>(), pluginInterface,
s.GetRequiredService<DalamudUtilService>(), s.GetRequiredService<MareMediator>(), s.GetRequiredService<RedrawManager>()));
collection.AddSingleton((s) => new IpcCallerGlamourer(s.GetRequiredService<ILogger<IpcCallerGlamourer>>(), pluginInterface,
s.GetRequiredService<DalamudUtilService>(), s.GetRequiredService<MareMediator>(), s.GetRequiredService<RedrawManager>()));
collection.AddSingleton((s) => new IpcCallerCustomize(s.GetRequiredService<ILogger<IpcCallerCustomize>>(), pluginInterface,
s.GetRequiredService<DalamudUtilService>(), s.GetRequiredService<MareMediator>()));
collection.AddSingleton((s) => new IpcCallerHeels(s.GetRequiredService<ILogger<IpcCallerHeels>>(), pluginInterface,
s.GetRequiredService<DalamudUtilService>(), s.GetRequiredService<MareMediator>()));
collection.AddSingleton((s) => new IpcCallerHonorific(s.GetRequiredService<ILogger<IpcCallerHonorific>>(), pluginInterface,
s.GetRequiredService<DalamudUtilService>(), s.GetRequiredService<MareMediator>()));
collection.AddSingleton((s) => new IpcManager(s.GetRequiredService<ILogger<IpcManager>>(), collection.AddSingleton((s) => new IpcManager(s.GetRequiredService<ILogger<IpcManager>>(),
pluginInterface, s.GetRequiredService<DalamudUtilService>(), s.GetRequiredService<MareMediator>())); s.GetRequiredService<MareMediator>(), s.GetRequiredService<IpcCallerPenumbra>(), s.GetRequiredService<IpcCallerGlamourer>(),
s.GetRequiredService<IpcCallerCustomize>(), s.GetRequiredService<IpcCallerHeels>(), s.GetRequiredService<IpcCallerHonorific>()));
collection.AddSingleton((s) => new MareConfigService(pluginInterface.ConfigDirectory.FullName)); collection.AddSingleton((s) => new MareConfigService(pluginInterface.ConfigDirectory.FullName));
collection.AddSingleton((s) => new ServerConfigService(pluginInterface.ConfigDirectory.FullName)); collection.AddSingleton((s) => new ServerConfigService(pluginInterface.ConfigDirectory.FullName));

View File

@@ -1,3 +1,4 @@
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification;
using MareSynchronos.API.Data; using MareSynchronos.API.Data;
using MareSynchronos.API.Dto; using MareSynchronos.API.Dto;
@@ -81,7 +82,7 @@ public record CombatStartMessage : MessageBase;
public record CombatEndMessage : MessageBase; public record CombatEndMessage : MessageBase;
public record EventMessage(Event Event) : MessageBase; public record EventMessage(Event Event) : MessageBase;
public record PenumbraDirectoryChangedMessage(string? ModDirectory) : MessageBase; public record PenumbraDirectoryChangedMessage(string? ModDirectory) : MessageBase;
public record PenumbraRedrawCharacterMessage(ICharacter Character) : SameThreadMessage;
public record UserChatMsgMessage(SignedChatMessage ChatMsg) : MessageBase; public record UserChatMsgMessage(SignedChatMessage ChatMsg) : MessageBase;
public record GroupChatMsgMessage(GroupDto GroupInfo, SignedChatMessage ChatMsg) : MessageBase; public record GroupChatMsgMessage(GroupDto GroupInfo, SignedChatMessage ChatMsg) : MessageBase;
#pragma warning restore S2094 #pragma warning restore S2094

View File

@@ -1,7 +1,7 @@
using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification;
using MareSynchronos.API.Data; using MareSynchronos.API.Data;
using MareSynchronos.API.Data.Comparer; using MareSynchronos.API.Data.Comparer;
using MareSynchronos.Interop; using MareSynchronos.Interop.Ipc;
using MareSynchronos.MareConfiguration; using MareSynchronos.MareConfiguration;
using MareSynchronos.Services.Mediator; using MareSynchronos.Services.Mediator;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@@ -35,18 +35,18 @@ public class PluginWarningNotificationService
} }
List<string> missingPluginsForData = []; List<string> missingPluginsForData = [];
if (changes.Contains(PlayerChanges.Heels) && !warning.ShownHeelsWarning && !_ipcManager.CheckHeelsApi()) if (changes.Contains(PlayerChanges.Heels) && !warning.ShownHeelsWarning && !_ipcManager.Heels.APIAvailable)
{ {
missingPluginsForData.Add("SimpleHeels"); missingPluginsForData.Add("SimpleHeels");
warning.ShownHeelsWarning = true; warning.ShownHeelsWarning = true;
} }
if (changes.Contains(PlayerChanges.Customize) && !warning.ShownCustomizePlusWarning && !_ipcManager.CheckCustomizePlusApi()) if (changes.Contains(PlayerChanges.Customize) && !warning.ShownCustomizePlusWarning && !_ipcManager.CustomizePlus.APIAvailable)
{ {
missingPluginsForData.Add("Customize+"); missingPluginsForData.Add("Customize+");
warning.ShownCustomizePlusWarning = true; warning.ShownCustomizePlusWarning = true;
} }
if (changes.Contains(PlayerChanges.Honorific) && !warning.ShownHonorificWarning && !_ipcManager.CheckHonorificApi()) if (changes.Contains(PlayerChanges.Honorific) && !warning.ShownHonorificWarning && !_ipcManager.Honorific.APIAvailable)
{ {
missingPluginsForData.Add("Honorific"); missingPluginsForData.Add("Honorific");
warning.ShownHonorificWarning = true; warning.ShownHonorificWarning = true;

View File

@@ -3,7 +3,7 @@ using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using ImGuiNET; using ImGuiNET;
using MareSynchronos.API.Data.Enum; using MareSynchronos.API.Data.Enum;
using MareSynchronos.Interop; using MareSynchronos.Interop.Ipc;
using MareSynchronos.Services; using MareSynchronos.Services;
using MareSynchronos.Services.Mediator; using MareSynchronos.Services.Mediator;
using MareSynchronos.Utils; using MareSynchronos.Utils;
@@ -263,7 +263,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
if (_texturesToConvert.Count > 0 && UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.PlayCircle, "Start conversion of " + _texturesToConvert.Count + " texture(s)")) if (_texturesToConvert.Count > 0 && UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.PlayCircle, "Start conversion of " + _texturesToConvert.Count + " texture(s)"))
{ {
_conversionCancellationTokenSource = _conversionCancellationTokenSource.CancelRecreate(); _conversionCancellationTokenSource = _conversionCancellationTokenSource.CancelRecreate();
_conversionTask = _ipcManager.PenumbraConvertTextureFiles(_logger, _texturesToConvert, _conversionProgress, _conversionCancellationTokenSource.Token); _conversionTask = _ipcManager.Penumbra.ConvertTextureFiles(_logger, _texturesToConvert, _conversionProgress, _conversionCancellationTokenSource.Token);
} }
} }
} }

View File

@@ -10,7 +10,7 @@ using MareSynchronos.API.Data.Comparer;
using MareSynchronos.API.Dto.Account; using MareSynchronos.API.Dto.Account;
using MareSynchronos.API.Routes; using MareSynchronos.API.Routes;
using MareSynchronos.FileCache; using MareSynchronos.FileCache;
using MareSynchronos.Interop; using MareSynchronos.Interop.Ipc;
using MareSynchronos.MareConfiguration; using MareSynchronos.MareConfiguration;
using MareSynchronos.MareConfiguration.Models; using MareSynchronos.MareConfiguration.Models;
using MareSynchronos.PlayerData.Export; using MareSynchronos.PlayerData.Export;
@@ -750,7 +750,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
using var id = ImRaii.PushId("penumbraMonitor"); using var id = ImRaii.PushId("penumbraMonitor");
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.ArrowsToCircle, "Try to reinitialize Monitor")) if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.ArrowsToCircle, "Try to reinitialize Monitor"))
{ {
_cacheMonitor.StartPenumbraWatcher(_ipcManager.PenumbraModDirectory); _cacheMonitor.StartPenumbraWatcher(_ipcManager.Penumbra.PenumbraModDirectory);
} }
} }
@@ -770,7 +770,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Play, "Resume Monitoring")) if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Play, "Resume Monitoring"))
{ {
_cacheMonitor.StartMareWatcher(_configService.Current.CacheFolder); _cacheMonitor.StartMareWatcher(_configService.Current.CacheFolder);
_cacheMonitor.StartPenumbraWatcher(_ipcManager.PenumbraModDirectory); _cacheMonitor.StartPenumbraWatcher(_ipcManager.Penumbra.PenumbraModDirectory);
_cacheMonitor.InvokeScan(); _cacheMonitor.InvokeScan();
} }
UiSharedService.AttachToolTip("Attempts to resume monitoring for both Penumbra and Loporrit Storage. " UiSharedService.AttachToolTip("Attempts to resume monitoring for both Penumbra and Loporrit Storage. "

View File

@@ -11,7 +11,7 @@ using Dalamud.Plugin.Services;
using Dalamud.Utility; using Dalamud.Utility;
using ImGuiNET; using ImGuiNET;
using MareSynchronos.FileCache; using MareSynchronos.FileCache;
using MareSynchronos.Interop; using MareSynchronos.Interop.Ipc;
using MareSynchronos.Localization; using MareSynchronos.Localization;
using MareSynchronos.MareConfiguration; using MareSynchronos.MareConfiguration;
using MareSynchronos.MareConfiguration.Models; using MareSynchronos.MareConfiguration.Models;
@@ -111,11 +111,11 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
Mediator.Subscribe<DelayedFrameworkUpdateMessage>(this, (_) => Mediator.Subscribe<DelayedFrameworkUpdateMessage>(this, (_) =>
{ {
_penumbraExists = _ipcManager.CheckPenumbraApi(); _penumbraExists = _ipcManager.Penumbra.APIAvailable;
_glamourerExists = _ipcManager.CheckGlamourerApi(); _glamourerExists = _ipcManager.Glamourer.APIAvailable;
_customizePlusExists = _ipcManager.CheckCustomizePlusApi(); _customizePlusExists = _ipcManager.CustomizePlus.APIAvailable;
_heelsExists = _ipcManager.CheckHeelsApi(); _heelsExists = _ipcManager.Heels.APIAvailable;
_honorificExists = _ipcManager.CheckHonorificApi(); _honorificExists = _ipcManager.Honorific.APIAvailable;
}); });
} }
@@ -123,7 +123,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
public bool EditTrackerPosition { get; set; } public bool EditTrackerPosition { get; set; }
public bool HasValidPenumbraModPath => !(_ipcManager.PenumbraModDirectory ?? string.Empty).IsNullOrEmpty() && Directory.Exists(_ipcManager.PenumbraModDirectory); public bool HasValidPenumbraModPath => !(_ipcManager.Penumbra.PenumbraModDirectory ?? string.Empty).IsNullOrEmpty() && Directory.Exists(_ipcManager.Penumbra.PenumbraModDirectory);
public bool IsInGpose => _dalamudUtil.IsInCutscene; public bool IsInGpose => _dalamudUtil.IsInCutscene;
@@ -601,7 +601,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
if (!success) return; if (!success) return;
_isOneDrive = path.Contains("onedrive", StringComparison.OrdinalIgnoreCase); _isOneDrive = path.Contains("onedrive", StringComparison.OrdinalIgnoreCase);
_isPenumbraDirectory = string.Equals(path.ToLowerInvariant(), _ipcManager.PenumbraModDirectory?.ToLowerInvariant(), StringComparison.Ordinal); _isPenumbraDirectory = string.Equals(path.ToLowerInvariant(), _ipcManager.Penumbra.PenumbraModDirectory?.ToLowerInvariant(), StringComparison.Ordinal);
_isDirectoryWritable = IsDirectoryWritable(path); _isDirectoryWritable = IsDirectoryWritable(path);
_cacheDirectoryHasOtherFilesThanCache = Directory.GetFiles(path, "*", SearchOption.AllDirectories).Any(f => Path.GetFileNameWithoutExtension(f).Length != 40) _cacheDirectoryHasOtherFilesThanCache = Directory.GetFiles(path, "*", SearchOption.AllDirectories).Any(f => Path.GetFileNameWithoutExtension(f).Length != 40)
|| Directory.GetDirectories(path).Any(); || Directory.GetDirectories(path).Any();