refactor a little bit
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using LZ4;
|
using LZ4;
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +158,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)
|
||||||
@@ -180,7 +181,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);
|
||||||
@@ -352,7 +353,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))
|
||||||
{
|
{
|
||||||
|
|||||||
7
MareSynchronos/Interop/Ipc/IIpcCaller.cs
Normal file
7
MareSynchronos/Interop/Ipc/IIpcCaller.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace MareSynchronos.Interop.Ipc;
|
||||||
|
|
||||||
|
public interface IIpcCaller : IDisposable
|
||||||
|
{
|
||||||
|
bool APIAvailable { get; }
|
||||||
|
void CheckAPI();
|
||||||
|
}
|
||||||
117
MareSynchronos/Interop/Ipc/IpcCallerCustomize.cs
Normal file
117
MareSynchronos/Interop/Ipc/IpcCallerCustomize.cs
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
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<Character?, string?> _customizePlusGetBodyScale;
|
||||||
|
private readonly ICallGateSubscriber<string?, string?, object> _customizePlusOnScaleUpdate;
|
||||||
|
private readonly ICallGateSubscriber<Character?, object> _customizePlusRevertCharacter;
|
||||||
|
private readonly ICallGateSubscriber<string, Character?, object> _customizePlusSetBodyScaleToCharacter;
|
||||||
|
private readonly ILogger<IpcCallerCustomize> _logger;
|
||||||
|
private readonly DalamudUtilService _dalamudUtil;
|
||||||
|
private readonly MareMediator _mareMediator;
|
||||||
|
|
||||||
|
public IpcCallerCustomize(ILogger<IpcCallerCustomize> logger, DalamudPluginInterface dalamudPluginInterface,
|
||||||
|
DalamudUtilService dalamudUtil, MareMediator mareMediator)
|
||||||
|
{
|
||||||
|
_customizePlusApiVersion = dalamudPluginInterface.GetIpcSubscriber<(int, int)>("CustomizePlus.GetApiVersion");
|
||||||
|
_customizePlusGetBodyScale = dalamudPluginInterface.GetIpcSubscriber<Character?, string?>("CustomizePlus.GetProfileFromCharacter");
|
||||||
|
_customizePlusRevertCharacter = dalamudPluginInterface.GetIpcSubscriber<Character?, object>("CustomizePlus.RevertCharacter");
|
||||||
|
_customizePlusSetBodyScaleToCharacter = dalamudPluginInterface.GetIpcSubscriber<string, Character?, object>("CustomizePlus.SetProfileToCharacter");
|
||||||
|
_customizePlusOnScaleUpdate = dalamudPluginInterface.GetIpcSubscriber<string?, string?, object>("CustomizePlus.OnProfileUpdate");
|
||||||
|
|
||||||
|
_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 Character c)
|
||||||
|
{
|
||||||
|
_logger.LogTrace("CustomizePlus reverting for {chara}", c.Address.ToString("X"));
|
||||||
|
_customizePlusRevertCharacter!.InvokeAction(c);
|
||||||
|
}
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetBodyScaleAsync(nint character, string scale)
|
||||||
|
{
|
||||||
|
if (!APIAvailable) return;
|
||||||
|
await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||||
|
{
|
||||||
|
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||||
|
if (gameObj is Character c)
|
||||||
|
{
|
||||||
|
string decodedScale = Encoding.UTF8.GetString(Convert.FromBase64String(scale));
|
||||||
|
_logger.LogTrace("CustomizePlus applying for {chara}", c.Address.ToString("X"));
|
||||||
|
if (scale.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
_customizePlusRevertCharacter!.InvokeAction(c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_customizePlusSetBodyScaleToCharacter!.InvokeAction(decodedScale, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).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 Character c)
|
||||||
|
{
|
||||||
|
return _customizePlusGetBodyScale.InvokeFunc(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 == 3 && version.Item2 >= 0);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
APIAvailable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCustomizePlusScaleChange(string? profileName, string? scale)
|
||||||
|
{
|
||||||
|
_mareMediator.Publish(new CustomizePlusMessage(profileName ?? string.Empty));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_customizePlusOnScaleUpdate.Unsubscribe(OnCustomizePlusScaleChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
205
MareSynchronos/Interop/Ipc/IpcCallerGlamourer.cs
Normal file
205
MareSynchronos/Interop/Ipc/IpcCallerGlamourer.cs
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
|
using Dalamud.Plugin;
|
||||||
|
using Dalamud.Plugin.Ipc;
|
||||||
|
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 ICallGateSubscriber<(int, int)> _glamourerApiVersions;
|
||||||
|
private readonly ICallGateSubscriber<string, GameObject?, uint, object>? _glamourerApplyAll;
|
||||||
|
private readonly ICallGateSubscriber<GameObject?, string>? _glamourerGetAllCustomization;
|
||||||
|
private readonly ICallGateSubscriber<Character?, uint, object?> _glamourerRevert;
|
||||||
|
private readonly ICallGateSubscriber<string, uint, object?> _glamourerRevertByName;
|
||||||
|
private readonly ICallGateSubscriber<string, uint, bool> _glamourerUnlock;
|
||||||
|
private readonly ILogger<IpcCallerGlamourer> _logger;
|
||||||
|
private readonly DalamudPluginInterface _pi;
|
||||||
|
private readonly DalamudUtilService _dalamudUtil;
|
||||||
|
private readonly MareMediator _mareMediator;
|
||||||
|
private readonly RedrawManager _redrawManager;
|
||||||
|
private bool _shownGlamourerUnavailable = false;
|
||||||
|
private readonly uint LockCode = 0x6D617265;
|
||||||
|
|
||||||
|
public IpcCallerGlamourer(ILogger<IpcCallerGlamourer> logger, DalamudPluginInterface pi, DalamudUtilService dalamudUtil, MareMediator mareMediator,
|
||||||
|
RedrawManager redrawManager)
|
||||||
|
{
|
||||||
|
_glamourerApiVersions = pi.GetIpcSubscriber<(int, int)>("Glamourer.ApiVersions");
|
||||||
|
_glamourerGetAllCustomization = pi.GetIpcSubscriber<GameObject?, string>("Glamourer.GetAllCustomizationFromCharacter");
|
||||||
|
_glamourerApplyAll = pi.GetIpcSubscriber<string, GameObject?, uint, object>("Glamourer.ApplyAllToCharacterLock");
|
||||||
|
_glamourerRevert = pi.GetIpcSubscriber<Character?, uint, object?>("Glamourer.RevertCharacterLock");
|
||||||
|
_glamourerRevertByName = pi.GetIpcSubscriber<string, uint, object?>("Glamourer.RevertLock");
|
||||||
|
_glamourerUnlock = pi.GetIpcSubscriber<string, uint, bool>("Glamourer.UnlockName");
|
||||||
|
|
||||||
|
pi.GetIpcSubscriber<int, nint, Lazy<string>, object?>("Glamourer.StateChanged").Subscribe((type, address, customize) => GlamourerChanged(address));
|
||||||
|
_logger = logger;
|
||||||
|
_pi = pi;
|
||||||
|
_dalamudUtil = dalamudUtil;
|
||||||
|
_mareMediator = mareMediator;
|
||||||
|
_redrawManager = redrawManager;
|
||||||
|
CheckAPI();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool APIAvailable { get; private set; }
|
||||||
|
|
||||||
|
public void CheckAPI()
|
||||||
|
{
|
||||||
|
bool apiAvailable = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var version = _glamourerApiVersions.InvokeFunc();
|
||||||
|
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);
|
||||||
|
if (version.Item1 == 0 && version.Item2 >= 1 && versionValid)
|
||||||
|
{
|
||||||
|
apiAvailable = true;
|
||||||
|
}
|
||||||
|
_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 Mare. If you just updated Glamourer, ignore this message.",
|
||||||
|
NotificationType.Error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_pi.GetIpcSubscriber<int, nint, Lazy<string>, object?>("Glamourer.StateChanged").Unsubscribe((type, address, customize) => GlamourerChanged(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
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!.InvokeAction(customization, chara, LockCode);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
logger.LogWarning("[{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 Character c)
|
||||||
|
{
|
||||||
|
return _glamourerGetAllCustomization!.InvokeFunc(c);
|
||||||
|
}
|
||||||
|
return string.Empty;
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RevertAsync(ILogger logger, string name, 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: GlamourerUnlockName", applicationId);
|
||||||
|
_glamourerUnlock.InvokeFunc(name, LockCode);
|
||||||
|
logger.LogDebug("[{appid}] Calling On IPC: GlamourerRevert", applicationId);
|
||||||
|
_glamourerRevert.InvokeAction(chara, 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(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
logger.LogDebug("[{appid}] Calling On IPC: GlamourerRevertByName", applicationId);
|
||||||
|
_glamourerRevertByName.InvokeAction(name, LockCode);
|
||||||
|
logger.LogDebug("[{appid}] Calling On IPC: GlamourerUnlockName", applicationId);
|
||||||
|
_glamourerUnlock.InvokeFunc(name, LockCode);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Error during Glamourer RevertByName");
|
||||||
|
}
|
||||||
|
}).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.InvokeAction(name, LockCode);
|
||||||
|
logger.LogDebug("[{appid}] Calling On IPC: GlamourerUnlockName", applicationId);
|
||||||
|
_glamourerUnlock.InvokeFunc(name, LockCode);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Error during Glamourer RevertByName");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GlamourerChanged(nint address)
|
||||||
|
{
|
||||||
|
_mareMediator.Publish(new GlamourerChangedMessage(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
94
MareSynchronos/Interop/Ipc/IpcCallerHeels.cs
Normal file
94
MareSynchronos/Interop/Ipc/IpcCallerHeels.cs
Normal 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<GameObject, string, object?> _heelsRegisterPlayer;
|
||||||
|
private readonly ICallGateSubscriber<GameObject, object?> _heelsUnregisterPlayer;
|
||||||
|
|
||||||
|
public IpcCallerHeels(ILogger<IpcCallerHeels> logger, DalamudPluginInterface 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<GameObject, string, object?>("SimpleHeels.RegisterPlayer");
|
||||||
|
_heelsUnregisterPlayer = pi.GetIpcSubscriber<GameObject, 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);
|
||||||
|
}
|
||||||
|
}).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, data);
|
||||||
|
}
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CheckAPI()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
APIAvailable = _heelsGetApiVersion.InvokeFunc() is { Item1: 1, Item2: >= 1 };
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
APIAvailable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_heelsOffsetUpdate.Unsubscribe(HeelsOffsetChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
133
MareSynchronos/Interop/Ipc/IpcCallerHonorific.cs
Normal file
133
MareSynchronos/Interop/Ipc/IpcCallerHonorific.cs
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||||
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
|
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<Character, 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<Character, string, object> _honorificSetCharacterTitle;
|
||||||
|
private readonly ILogger<IpcCallerHonorific> _logger;
|
||||||
|
private readonly MareMediator _mareMediator;
|
||||||
|
private readonly DalamudUtilService _dalamudUtil;
|
||||||
|
|
||||||
|
public IpcCallerHonorific(ILogger<IpcCallerHonorific> logger, DalamudPluginInterface 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<Character, object>("Honorific.ClearCharacterTitle");
|
||||||
|
_honorificSetCharacterTitle = pi.GetIpcSubscriber<Character, 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: 2, 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 PlayerCharacter c)
|
||||||
|
{
|
||||||
|
_logger.LogTrace("Honorific removing for {addr}", c.Address.ToString("X"));
|
||||||
|
_honorificClearCharacterTitle!.InvokeAction(c);
|
||||||
|
}
|
||||||
|
}).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 PlayerCharacter pc)
|
||||||
|
{
|
||||||
|
string honorificData = string.IsNullOrEmpty(honorificDataB64) ? string.Empty : Encoding.UTF8.GetString(Convert.FromBase64String(honorificDataB64));
|
||||||
|
if (string.IsNullOrEmpty(honorificData))
|
||||||
|
{
|
||||||
|
_honorificClearCharacterTitle!.InvokeAction(pc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_honorificSetCharacterTitle!.InvokeAction(pc, 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
328
MareSynchronos/Interop/Ipc/IpcCallerPenumbra.cs
Normal file
328
MareSynchronos/Interop/Ipc/IpcCallerPenumbra.cs
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
|
using Dalamud.Plugin;
|
||||||
|
using MareSynchronos.PlayerData.Handlers;
|
||||||
|
using MareSynchronos.Services;
|
||||||
|
using MareSynchronos.Services.Mediator;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Penumbra.Api.Enums;
|
||||||
|
using Penumbra.Api.Helpers;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace MareSynchronos.Interop.Ipc;
|
||||||
|
|
||||||
|
public sealed class IpcCallerPenumbra : DisposableMediatorSubscriberBase, IIpcCaller
|
||||||
|
{
|
||||||
|
private readonly DalamudPluginInterface _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 FuncSubscriber<string, string, Dictionary<string, string>, string, int, PenumbraApiEc> _penumbraAddTemporaryMod;
|
||||||
|
private readonly FuncSubscriber<string, int, bool, PenumbraApiEc> _penumbraAssignTemporaryCollection;
|
||||||
|
private readonly FuncSubscriber<string, string, TextureType, bool, Task> _penumbraConvertTextureFile;
|
||||||
|
private readonly FuncSubscriber<string, PenumbraApiEc> _penumbraCreateNamedTemporaryCollection;
|
||||||
|
private readonly EventSubscriber _penumbraDispose;
|
||||||
|
private readonly FuncSubscriber<bool> _penumbraEnabled;
|
||||||
|
private readonly EventSubscriber<nint, string, string> _penumbraGameObjectResourcePathResolved;
|
||||||
|
private readonly FuncSubscriber<string> _penumbraGetMetaManipulations;
|
||||||
|
private readonly EventSubscriber _penumbraInit;
|
||||||
|
private readonly EventSubscriber<ModSettingChange, string, string, bool> _penumbraModSettingChanged;
|
||||||
|
private readonly EventSubscriber<nint, int> _penumbraObjectIsRedrawn;
|
||||||
|
private readonly ActionSubscriber<string, RedrawType> _penumbraRedraw;
|
||||||
|
private readonly ActionSubscriber<GameObject, RedrawType> _penumbraRedrawObject;
|
||||||
|
private readonly ConcurrentDictionary<IntPtr, bool> _penumbraRedrawRequests = new();
|
||||||
|
private readonly FuncSubscriber<string, PenumbraApiEc> _penumbraRemoveTemporaryCollection;
|
||||||
|
private readonly FuncSubscriber<string, string, int, PenumbraApiEc> _penumbraRemoveTemporaryMod;
|
||||||
|
private readonly FuncSubscriber<string> _penumbraResolveModDir;
|
||||||
|
private readonly FuncSubscriber<string[], string[], Task<(string[], string[][])>> _penumbraResolvePaths;
|
||||||
|
private readonly ParamsFuncSubscriber<ushort, IReadOnlyDictionary<string, string[]>?[]> _penumbraResourcePaths;
|
||||||
|
|
||||||
|
public IpcCallerPenumbra(ILogger<IpcCallerPenumbra> logger, DalamudPluginInterface pi, DalamudUtilService dalamudUtil,
|
||||||
|
MareMediator mareMediator, RedrawManager redrawManager) : base(logger, mareMediator)
|
||||||
|
{
|
||||||
|
_pi = pi;
|
||||||
|
_dalamudUtil = dalamudUtil;
|
||||||
|
_mareMediator = mareMediator;
|
||||||
|
_redrawManager = redrawManager;
|
||||||
|
_penumbraInit = Penumbra.Api.Ipc.Initialized.Subscriber(pi, PenumbraInit);
|
||||||
|
_penumbraDispose = Penumbra.Api.Ipc.Disposed.Subscriber(pi, PenumbraDispose);
|
||||||
|
_penumbraResolveModDir = Penumbra.Api.Ipc.GetModDirectory.Subscriber(pi);
|
||||||
|
_penumbraRedraw = Penumbra.Api.Ipc.RedrawObjectByName.Subscriber(pi);
|
||||||
|
_penumbraRedrawObject = Penumbra.Api.Ipc.RedrawObject.Subscriber(pi);
|
||||||
|
_penumbraObjectIsRedrawn = Penumbra.Api.Ipc.GameObjectRedrawn.Subscriber(pi, RedrawEvent);
|
||||||
|
_penumbraGetMetaManipulations = Penumbra.Api.Ipc.GetPlayerMetaManipulations.Subscriber(pi);
|
||||||
|
_penumbraRemoveTemporaryMod = Penumbra.Api.Ipc.RemoveTemporaryMod.Subscriber(pi);
|
||||||
|
_penumbraAddTemporaryMod = Penumbra.Api.Ipc.AddTemporaryMod.Subscriber(pi);
|
||||||
|
_penumbraCreateNamedTemporaryCollection = Penumbra.Api.Ipc.CreateNamedTemporaryCollection.Subscriber(pi);
|
||||||
|
_penumbraRemoveTemporaryCollection = Penumbra.Api.Ipc.RemoveTemporaryCollectionByName.Subscriber(pi);
|
||||||
|
_penumbraAssignTemporaryCollection = Penumbra.Api.Ipc.AssignTemporaryCollection.Subscriber(pi);
|
||||||
|
_penumbraResolvePaths = Penumbra.Api.Ipc.ResolvePlayerPathsAsync.Subscriber(pi);
|
||||||
|
_penumbraEnabled = Penumbra.Api.Ipc.GetEnabledState.Subscriber(pi);
|
||||||
|
_penumbraModSettingChanged = Penumbra.Api.Ipc.ModSettingChanged.Subscriber(pi, (change, arg1, arg, b) =>
|
||||||
|
{
|
||||||
|
if (change == ModSettingChange.EnableState)
|
||||||
|
_mareMediator.Publish(new PenumbraModSettingChangedMessage());
|
||||||
|
});
|
||||||
|
_penumbraConvertTextureFile = Penumbra.Api.Ipc.ConvertTextureFile.Subscriber(pi);
|
||||||
|
_penumbraResourcePaths = Penumbra.Api.Ipc.GetGameObjectResourcePaths.Subscriber(pi);
|
||||||
|
|
||||||
|
_penumbraGameObjectResourcePathResolved = Penumbra.Api.Ipc.GameObjectResourcePathResolved.Subscriber(pi, ResourceLoaded);
|
||||||
|
|
||||||
|
CheckAPI();
|
||||||
|
CheckModDirectory();
|
||||||
|
|
||||||
|
Mediator.Subscribe<PenumbraRedrawCharacterMessage>(this, (msg) => _penumbraRedrawObject.Invoke(msg.Character, RedrawType.AfterGPose));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool APIAvailable { get; private set; } = false;
|
||||||
|
|
||||||
|
public void CheckAPI()
|
||||||
|
{
|
||||||
|
bool penumbraAvailable = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
penumbraAvailable = (_pi.InstalledPlugins
|
||||||
|
.FirstOrDefault(p => string.Equals(p.InternalName, "Penumbra", StringComparison.OrdinalIgnoreCase))
|
||||||
|
?.Version ?? new Version(0, 0, 0, 0)) >= new Version(0, 8, 1, 6);
|
||||||
|
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 Mare. 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, string collName, int idx)
|
||||||
|
{
|
||||||
|
if (!APIAvailable) return;
|
||||||
|
|
||||||
|
await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||||
|
{
|
||||||
|
var retAssign = _penumbraAssignTemporaryCollection.Invoke(collName, idx, c: 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, d: 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);
|
||||||
|
_penumbraRedrawObject.Invoke(gameObject!, RedrawType.Redraw);
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> CreateTemporaryCollectionAsync(ILogger logger, string uid)
|
||||||
|
{
|
||||||
|
if (!APIAvailable) return string.Empty;
|
||||||
|
|
||||||
|
return await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||||
|
{
|
||||||
|
var collName = "Mare_" + uid;
|
||||||
|
var retCreate = _penumbraCreateNamedTemporaryCollection.Invoke(collName);
|
||||||
|
logger.LogTrace("Creating Temp Collection {collName}, Success: {ret}", collName, retCreate);
|
||||||
|
return collName;
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyDictionary<string, 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);
|
||||||
|
}).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);
|
||||||
|
_penumbraRedrawObject!.Invoke(chara, RedrawType.Redraw);
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_redrawManager.RedrawSemaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RemoveTemporaryCollectionAsync(ILogger logger, Guid applicationId, string collName)
|
||||||
|
{
|
||||||
|
if (!APIAvailable) return;
|
||||||
|
await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||||
|
{
|
||||||
|
logger.LogTrace("[{applicationId}] Removing temp collection for {collName}", applicationId, collName);
|
||||||
|
var ret2 = _penumbraRemoveTemporaryCollection.Invoke(collName);
|
||||||
|
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, string collName, string manipulationData)
|
||||||
|
{
|
||||||
|
if (!APIAvailable) return;
|
||||||
|
|
||||||
|
await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||||
|
{
|
||||||
|
logger.LogTrace("[{applicationId}] Manip: {data}", applicationId, manipulationData);
|
||||||
|
var retAdd = _penumbraAddTemporaryMod.Invoke("MareChara_Meta", collName, [], manipulationData, 0);
|
||||||
|
logger.LogTrace("[{applicationId}] Setting temp meta mod for {collName}, Success: {ret}", applicationId, collName, retAdd);
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetTemporaryModsAsync(ILogger logger, Guid applicationId, string collName, 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", collName, 0);
|
||||||
|
logger.LogTrace("[{applicationId}] Removing temp files mod for {collName}, Success: {ret}", applicationId, collName, retRemove);
|
||||||
|
var retAdd = _penumbraAddTemporaryMod.Invoke("MareChara_Files", collName, modPaths, string.Empty, 0);
|
||||||
|
logger.LogTrace("[{applicationId}] Setting temp files mod for {collName}, Success: {ret}", applicationId, collName, 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("self", RedrawType.Redraw);
|
||||||
|
}
|
||||||
|
}
|
||||||
52
MareSynchronos/Interop/Ipc/IpcManager.cs
Normal file
52
MareSynchronos/Interop/Ipc/IpcManager.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
51
MareSynchronos/Interop/Ipc/RedrawManager.cs
Normal file
51
MareSynchronos/Interop/Ipc/RedrawManager.cs
Normal 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<Character> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,802 +0,0 @@
|
|||||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
|
||||||
using Dalamud.Game.ClientState.Objects.Types;
|
|
||||||
using Dalamud.Interface.Internal.Notifications;
|
|
||||||
using Dalamud.Plugin;
|
|
||||||
using Dalamud.Plugin.Ipc;
|
|
||||||
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 System.Collections.Concurrent;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace MareSynchronos.Interop;
|
|
||||||
|
|
||||||
public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|
||||||
{
|
|
||||||
private readonly ICallGateSubscriber<(int, int)> _customizePlusApiVersion;
|
|
||||||
private readonly ICallGateSubscriber<Character?, string?> _customizePlusGetBodyScale;
|
|
||||||
private readonly ICallGateSubscriber<string?, string?, object> _customizePlusOnScaleUpdate;
|
|
||||||
private readonly ICallGateSubscriber<Character?, object> _customizePlusRevertCharacter;
|
|
||||||
private readonly ICallGateSubscriber<string, Character?, object> _customizePlusSetBodyScaleToCharacter;
|
|
||||||
private readonly DalamudPluginInterface _pi;
|
|
||||||
private readonly DalamudUtilService _dalamudUtil;
|
|
||||||
private readonly ICallGateSubscriber<(int, int)> _glamourerApiVersions;
|
|
||||||
private readonly ICallGateSubscriber<string, GameObject?, uint, object>? _glamourerApplyAll;
|
|
||||||
private readonly ICallGateSubscriber<GameObject?, string>? _glamourerGetAllCustomization;
|
|
||||||
private readonly ICallGateSubscriber<Character?, uint, object?> _glamourerRevert;
|
|
||||||
private readonly ICallGateSubscriber<string, uint, object?> _glamourerRevertByName;
|
|
||||||
private readonly ICallGateSubscriber<string, uint, bool> _glamourerUnlock;
|
|
||||||
private readonly ICallGateSubscriber<(int, int)> _heelsGetApiVersion;
|
|
||||||
private readonly ICallGateSubscriber<string> _heelsGetOffset;
|
|
||||||
private readonly ICallGateSubscriber<string, object?> _heelsOffsetUpdate;
|
|
||||||
private readonly ICallGateSubscriber<GameObject, string, object?> _heelsRegisterPlayer;
|
|
||||||
private readonly ICallGateSubscriber<GameObject, object?> _heelsUnregisterPlayer;
|
|
||||||
private readonly ICallGateSubscriber<(uint major, uint minor)> _honorificApiVersion;
|
|
||||||
private readonly ICallGateSubscriber<Character, 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<Character, string, object> _honorificSetCharacterTitle;
|
|
||||||
private readonly FuncSubscriber<string, string, Dictionary<string, string>, string, int, PenumbraApiEc> _penumbraAddTemporaryMod;
|
|
||||||
private readonly FuncSubscriber<string, int, bool, PenumbraApiEc> _penumbraAssignTemporaryCollection;
|
|
||||||
private readonly FuncSubscriber<string, string, TextureType, bool, Task> _penumbraConvertTextureFile;
|
|
||||||
private readonly FuncSubscriber<string, PenumbraApiEc> _penumbraCreateNamedTemporaryCollection;
|
|
||||||
private readonly EventSubscriber _penumbraDispose;
|
|
||||||
private readonly FuncSubscriber<bool> _penumbraEnabled;
|
|
||||||
private readonly EventSubscriber<nint, string, string> _penumbraGameObjectResourcePathResolved;
|
|
||||||
private readonly FuncSubscriber<string> _penumbraGetMetaManipulations;
|
|
||||||
private readonly EventSubscriber _penumbraInit;
|
|
||||||
private readonly EventSubscriber<ModSettingChange, string, string, bool> _penumbraModSettingChanged;
|
|
||||||
private readonly EventSubscriber<nint, int> _penumbraObjectIsRedrawn;
|
|
||||||
private readonly ActionSubscriber<string, RedrawType> _penumbraRedraw;
|
|
||||||
private readonly ActionSubscriber<GameObject, RedrawType> _penumbraRedrawObject;
|
|
||||||
private readonly ConcurrentDictionary<IntPtr, bool> _penumbraRedrawRequests = new();
|
|
||||||
private readonly FuncSubscriber<string, PenumbraApiEc> _penumbraRemoveTemporaryCollection;
|
|
||||||
private readonly FuncSubscriber<string, string, int, PenumbraApiEc> _penumbraRemoveTemporaryMod;
|
|
||||||
private readonly FuncSubscriber<string> _penumbraResolveModDir;
|
|
||||||
private readonly FuncSubscriber<string[], string[], Task<(string[], string[][])>> _penumbraResolvePaths;
|
|
||||||
private readonly ParamsFuncSubscriber<ushort, IReadOnlyDictionary<string, string[]>?[]> _penumbraResourcePaths;
|
|
||||||
private readonly SemaphoreSlim _redrawSemaphore = new(2);
|
|
||||||
private readonly uint LockCode = 0x6D617265;
|
|
||||||
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, DalamudPluginInterface pi, DalamudUtilService dalamudUtil, MareMediator mediator) : base(logger, mediator)
|
|
||||||
{
|
|
||||||
_pi = pi;
|
|
||||||
_dalamudUtil = dalamudUtil;
|
|
||||||
|
|
||||||
_penumbraInit = Penumbra.Api.Ipc.Initialized.Subscriber(pi, PenumbraInit);
|
|
||||||
_penumbraDispose = Penumbra.Api.Ipc.Disposed.Subscriber(pi, PenumbraDispose);
|
|
||||||
_penumbraResolveModDir = Penumbra.Api.Ipc.GetModDirectory.Subscriber(pi);
|
|
||||||
_penumbraRedraw = Penumbra.Api.Ipc.RedrawObjectByName.Subscriber(pi);
|
|
||||||
_penumbraRedrawObject = Penumbra.Api.Ipc.RedrawObject.Subscriber(pi);
|
|
||||||
_penumbraObjectIsRedrawn = Penumbra.Api.Ipc.GameObjectRedrawn.Subscriber(pi, RedrawEvent);
|
|
||||||
_penumbraGetMetaManipulations = Penumbra.Api.Ipc.GetPlayerMetaManipulations.Subscriber(pi);
|
|
||||||
_penumbraRemoveTemporaryMod = Penumbra.Api.Ipc.RemoveTemporaryMod.Subscriber(pi);
|
|
||||||
_penumbraAddTemporaryMod = Penumbra.Api.Ipc.AddTemporaryMod.Subscriber(pi);
|
|
||||||
_penumbraCreateNamedTemporaryCollection = Penumbra.Api.Ipc.CreateNamedTemporaryCollection.Subscriber(pi);
|
|
||||||
_penumbraRemoveTemporaryCollection = Penumbra.Api.Ipc.RemoveTemporaryCollectionByName.Subscriber(pi);
|
|
||||||
_penumbraAssignTemporaryCollection = Penumbra.Api.Ipc.AssignTemporaryCollection.Subscriber(pi);
|
|
||||||
_penumbraResolvePaths = Penumbra.Api.Ipc.ResolvePlayerPathsAsync.Subscriber(pi);
|
|
||||||
_penumbraEnabled = Penumbra.Api.Ipc.GetEnabledState.Subscriber(pi);
|
|
||||||
_penumbraModSettingChanged = Penumbra.Api.Ipc.ModSettingChanged.Subscriber(pi, (change, arg1, arg, b) =>
|
|
||||||
{
|
|
||||||
if (change == ModSettingChange.EnableState)
|
|
||||||
Mediator.Publish(new PenumbraModSettingChangedMessage());
|
|
||||||
});
|
|
||||||
_penumbraConvertTextureFile = Penumbra.Api.Ipc.ConvertTextureFile.Subscriber(pi);
|
|
||||||
_penumbraResourcePaths = Penumbra.Api.Ipc.GetGameObjectResourcePaths.Subscriber(pi);
|
|
||||||
|
|
||||||
_penumbraGameObjectResourcePathResolved = Penumbra.Api.Ipc.GameObjectResourcePathResolved.Subscriber(pi, ResourceLoaded);
|
|
||||||
|
|
||||||
_glamourerApiVersions = pi.GetIpcSubscriber<(int, int)>("Glamourer.ApiVersions");
|
|
||||||
_glamourerGetAllCustomization = pi.GetIpcSubscriber<GameObject?, string>("Glamourer.GetAllCustomizationFromCharacter");
|
|
||||||
_glamourerApplyAll = pi.GetIpcSubscriber<string, GameObject?, uint, object>("Glamourer.ApplyAllToCharacterLock");
|
|
||||||
_glamourerRevert = pi.GetIpcSubscriber<Character?, uint, object?>("Glamourer.RevertCharacterLock");
|
|
||||||
_glamourerRevertByName = pi.GetIpcSubscriber<string, uint, object?>("Glamourer.RevertLock");
|
|
||||||
_glamourerUnlock = pi.GetIpcSubscriber<string, uint, bool>("Glamourer.UnlockName");
|
|
||||||
|
|
||||||
pi.GetIpcSubscriber<int, nint, Lazy<string>, object?>("Glamourer.StateChanged").Subscribe((type, address, customize) => GlamourerChanged(address));
|
|
||||||
|
|
||||||
_heelsGetApiVersion = pi.GetIpcSubscriber<(int, int)>("SimpleHeels.ApiVersion");
|
|
||||||
_heelsGetOffset = pi.GetIpcSubscriber<string>("SimpleHeels.GetLocalPlayer");
|
|
||||||
_heelsRegisterPlayer = pi.GetIpcSubscriber<GameObject, string, object?>("SimpleHeels.RegisterPlayer");
|
|
||||||
_heelsUnregisterPlayer = pi.GetIpcSubscriber<GameObject, object?>("SimpleHeels.UnregisterPlayer");
|
|
||||||
_heelsOffsetUpdate = pi.GetIpcSubscriber<string, object?>("SimpleHeels.LocalChanged");
|
|
||||||
|
|
||||||
_heelsOffsetUpdate.Subscribe(HeelsOffsetChange);
|
|
||||||
|
|
||||||
_customizePlusApiVersion = pi.GetIpcSubscriber<(int, int)>("CustomizePlus.GetApiVersion");
|
|
||||||
_customizePlusGetBodyScale = pi.GetIpcSubscriber<Character?, string?>("CustomizePlus.GetProfileFromCharacter");
|
|
||||||
_customizePlusRevertCharacter = pi.GetIpcSubscriber<Character?, object>("CustomizePlus.RevertCharacter");
|
|
||||||
_customizePlusSetBodyScaleToCharacter = pi.GetIpcSubscriber<string, Character?, object>("CustomizePlus.SetProfileToCharacter");
|
|
||||||
_customizePlusOnScaleUpdate = pi.GetIpcSubscriber<string?, string?, object>("CustomizePlus.OnProfileUpdate");
|
|
||||||
|
|
||||||
_customizePlusOnScaleUpdate.Subscribe(OnCustomizePlusScaleChange);
|
|
||||||
|
|
||||||
_honorificApiVersion = pi.GetIpcSubscriber<(uint, uint)>("Honorific.ApiVersion");
|
|
||||||
_honorificGetLocalCharacterTitle = pi.GetIpcSubscriber<string>("Honorific.GetLocalCharacterTitle");
|
|
||||||
_honorificClearCharacterTitle = pi.GetIpcSubscriber<Character, object>("Honorific.ClearCharacterTitle");
|
|
||||||
_honorificSetCharacterTitle = pi.GetIpcSubscriber<Character, 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?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 Character c)
|
|
||||||
{
|
|
||||||
Logger.LogTrace("CustomizePlus reverting for {chara}", c.Address.ToString("X"));
|
|
||||||
_customizePlusRevertCharacter!.InvokeAction(c);
|
|
||||||
}
|
|
||||||
}).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task CustomizePlusSetBodyScaleAsync(IntPtr character, string scale)
|
|
||||||
{
|
|
||||||
if (!CheckCustomizePlusApi()) return;
|
|
||||||
await _dalamudUtil.RunOnFrameworkThread(() =>
|
|
||||||
{
|
|
||||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
|
||||||
if (gameObj is Character c)
|
|
||||||
{
|
|
||||||
string decodedScale = Encoding.UTF8.GetString(Convert.FromBase64String(scale));
|
|
||||||
Logger.LogTrace("CustomizePlus applying for {chara}", c.Address.ToString("X"));
|
|
||||||
if (scale.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
_customizePlusRevertCharacter!.InvokeAction(c);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_customizePlusSetBodyScaleToCharacter!.InvokeAction(decodedScale, c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).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 Character c)
|
|
||||||
{
|
|
||||||
return _customizePlusGetBodyScale.InvokeFunc(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
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!.InvokeAction(customization, chara, LockCode);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
logger.LogWarning("[{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 Character c)
|
|
||||||
{
|
|
||||||
return _glamourerGetAllCustomization!.InvokeFunc(c);
|
|
||||||
}
|
|
||||||
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.InvokeFunc(name, LockCode);
|
|
||||||
logger.LogDebug("[{appid}] Calling On IPC: GlamourerRevert", applicationId);
|
|
||||||
_glamourerRevert.InvokeAction(chara, LockCode);
|
|
||||||
logger.LogDebug("[{appid}] Calling On IPC: PenumbraRedraw", applicationId);
|
|
||||||
_penumbraRedrawObject.Invoke(chara, 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(() =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
logger.LogDebug("[{appid}] Calling On IPC: GlamourerRevertByName", applicationId);
|
|
||||||
_glamourerRevertByName.InvokeAction(name, LockCode);
|
|
||||||
logger.LogDebug("[{appid}] Calling On IPC: GlamourerUnlockName", applicationId);
|
|
||||||
_glamourerUnlock.InvokeFunc(name, LockCode);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogWarning(ex, "Error during Glamourer RevertByName");
|
|
||||||
}
|
|
||||||
}).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.InvokeAction(name, LockCode);
|
|
||||||
logger.LogDebug("[{appid}] Calling On IPC: GlamourerUnlockName", applicationId);
|
|
||||||
_glamourerUnlock.InvokeFunc(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);
|
|
||||||
}
|
|
||||||
}).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, data);
|
|
||||||
}
|
|
||||||
}).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task HonorificClearTitleAsync(nint character)
|
|
||||||
{
|
|
||||||
if (!CheckHonorificApi()) return;
|
|
||||||
await _dalamudUtil.RunOnFrameworkThread(() =>
|
|
||||||
{
|
|
||||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
|
||||||
if (gameObj is PlayerCharacter c)
|
|
||||||
{
|
|
||||||
Logger.LogTrace("Honorific removing for {addr}", c.Address.ToString("X"));
|
|
||||||
_honorificClearCharacterTitle!.InvokeAction(c);
|
|
||||||
}
|
|
||||||
}).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 PlayerCharacter pc)
|
|
||||||
{
|
|
||||||
string honorificData = string.IsNullOrEmpty(honorificDataB64) ? string.Empty : Encoding.UTF8.GetString(Convert.FromBase64String(honorificDataB64));
|
|
||||||
if (string.IsNullOrEmpty(honorificData))
|
|
||||||
{
|
|
||||||
_honorificClearCharacterTitle!.InvokeAction(pc);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_honorificSetCharacterTitle!.InvokeAction(pc, honorificData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.LogWarning(e, "Could not apply Honorific data");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task PenumbraAssignTemporaryCollectionAsync(ILogger logger, string collName, int idx)
|
|
||||||
{
|
|
||||||
if (!CheckPenumbraApi()) return;
|
|
||||||
|
|
||||||
await _dalamudUtil.RunOnFrameworkThread(() =>
|
|
||||||
{
|
|
||||||
var retAssign = _penumbraAssignTemporaryCollection.Invoke(collName, idx, c: 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, d: 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);
|
|
||||||
_penumbraRedrawObject.Invoke(gameObject!, RedrawType.Redraw);
|
|
||||||
}).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> PenumbraCreateTemporaryCollectionAsync(ILogger logger, string uid)
|
|
||||||
{
|
|
||||||
if (!CheckPenumbraApi()) return string.Empty;
|
|
||||||
|
|
||||||
return await _dalamudUtil.RunOnFrameworkThread(() =>
|
|
||||||
{
|
|
||||||
var collName = "Mare_" + uid;
|
|
||||||
var retCreate = _penumbraCreateNamedTemporaryCollection.Invoke(collName);
|
|
||||||
logger.LogTrace("Creating Temp Collection {collName}, Success: {ret}", collName, retCreate);
|
|
||||||
return collName;
|
|
||||||
}).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IReadOnlyDictionary<string, 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);
|
|
||||||
}).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);
|
|
||||||
_penumbraRedrawObject!.Invoke(chara, RedrawType.Redraw);
|
|
||||||
}).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_redrawSemaphore.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task PenumbraRemoveTemporaryCollectionAsync(ILogger logger, Guid applicationId, string collName)
|
|
||||||
{
|
|
||||||
if (!CheckPenumbraApi()) return;
|
|
||||||
await _dalamudUtil.RunOnFrameworkThread(() =>
|
|
||||||
{
|
|
||||||
logger.LogTrace("[{applicationId}] Removing temp collection for {collName}", applicationId, collName);
|
|
||||||
var ret2 = _penumbraRemoveTemporaryCollection.Invoke(collName);
|
|
||||||
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, string collName, string manipulationData)
|
|
||||||
{
|
|
||||||
if (!CheckPenumbraApi()) return;
|
|
||||||
|
|
||||||
await _dalamudUtil.RunOnFrameworkThread(() =>
|
|
||||||
{
|
|
||||||
logger.LogTrace("[{applicationId}] Manip: {data}", applicationId, manipulationData);
|
|
||||||
var retAdd = _penumbraAddTemporaryMod.Invoke("MareChara_Meta", collName, [], manipulationData, 0);
|
|
||||||
logger.LogTrace("[{applicationId}] Setting temp meta mod for {collName}, Success: {ret}", applicationId, collName, retAdd);
|
|
||||||
}).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task PenumbraSetTemporaryModsAsync(ILogger logger, Guid applicationId, string collName, 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", collName, 0);
|
|
||||||
logger.LogTrace("[{applicationId}] Removing temp files mod for {collName}, Success: {ret}", applicationId, collName, retRemove);
|
|
||||||
var retAdd = _penumbraAddTemporaryMod.Invoke("MareChara_Files", collName, modPaths, string.Empty, 0);
|
|
||||||
logger.LogTrace("[{applicationId}] Setting temp files mod for {collName}, Success: {ret}", applicationId, collName, retAdd);
|
|
||||||
}).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
base.Dispose(disposing);
|
|
||||||
|
|
||||||
_disposalCts.Cancel();
|
|
||||||
|
|
||||||
_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 == 3 && version.Item2 >= 0) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CheckGlamourerApiInternal()
|
|
||||||
{
|
|
||||||
bool apiAvailable = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var version = _glamourerApiVersions.InvokeFunc();
|
|
||||||
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);
|
|
||||||
if (version.Item1 == 0 && version.Item2 >= 1 && versionValid)
|
|
||||||
{
|
|
||||||
apiAvailable = true;
|
|
||||||
}
|
|
||||||
_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 Mare. If you just updated Glamourer, ignore this message.",
|
|
||||||
NotificationType.Error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CheckHeelsApiInternal()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return _heelsGetApiVersion.InvokeFunc() is { Item1: 1, Item2: >= 1 };
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CheckHonorificApiInternal()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return _honorificApiVersion.InvokeFunc() is { Item1: 2, Item2: >= 0 };
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CheckPenumbraApiInternal()
|
|
||||||
{
|
|
||||||
bool penumbraAvailable = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
penumbraAvailable = (_pi.InstalledPlugins
|
|
||||||
.FirstOrDefault(p => string.Equals(p.InternalName, "Penumbra", StringComparison.OrdinalIgnoreCase))
|
|
||||||
?.Version ?? new Version(0, 0, 0, 0)) >= new Version(0, 8, 1, 6);
|
|
||||||
penumbraAvailable &= _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 Mare. If you just updated Penumbra, ignore this message.",
|
|
||||||
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(string? profileName, string? scale)
|
|
||||||
{
|
|
||||||
Mediator.Publish(new CustomizePlusMessage(profileName ?? 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()
|
|
||||||
{
|
|
||||||
_penumbraAvailable = true;
|
|
||||||
PenumbraModDirectory = _penumbraResolveModDir.Invoke();
|
|
||||||
Mediator.Publish(new PenumbraInitializedMessage());
|
|
||||||
_penumbraRedraw!.Invoke("self", RedrawType.Redraw);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task PenumbraRedrawInternalAsync(ILogger logger, GameObjectHandler handler, Guid applicationId, Action<Character> 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
using LZ4;
|
using LZ4;
|
||||||
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;
|
||||||
@@ -48,12 +48,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.Name, 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -92,11 +92,11 @@ public class MareCharaFileManager : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var applicationId = Guid.NewGuid();
|
var applicationId = Guid.NewGuid();
|
||||||
await _ipcManager.PenumbraRemoveTemporaryCollectionAsync(_logger, applicationId, charaTarget.Name.TextValue).ConfigureAwait(false);
|
await _ipcManager.Penumbra.RemoveTemporaryCollectionAsync(_logger, applicationId, charaTarget.Name.TextValue).ConfigureAwait(false);
|
||||||
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.ObjectTableIndex()!.Value).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);
|
||||||
@@ -104,17 +104,17 @@ 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))
|
||||||
{
|
{
|
||||||
await _ipcManager.CustomizePlusSetBodyScaleAsync(tempHandler.Address, LoadedCharaFile.CharaFileData.CustomizePlusData).ConfigureAwait(false);
|
await _ipcManager.CustomizePlus.SetBodyScaleAsync(tempHandler.Address, LoadedCharaFile.CharaFileData.CustomizePlusData).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _ipcManager.CustomizePlusRevertAsync(tempHandler.Address).ConfigureAwait(false);
|
await _ipcManager.CustomizePlus.RevertAsync(tempHandler.Address).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
IReadOnlyDictionary<string, string[]>? resolvedPaths;
|
IReadOnlyDictionary<string, string[]>? resolvedPaths;
|
||||||
|
|
||||||
resolvedPaths = (await _ipcManager.PenumbraGetCharacterData(_logger, playerRelatedObject).ConfigureAwait(false))![0];
|
resolvedPaths = (await _ipcManager.Penumbra.GetCharacterData(_logger, playerRelatedObject).ConfigureAwait(false))![0];
|
||||||
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,10 +191,10 @@ 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);
|
||||||
@@ -204,7 +203,7 @@ public class PlayerDataFactory
|
|||||||
previousData.CustomizePlusScale[playerRelatedObject.ObjectKind] = customizeScale;
|
previousData.CustomizePlusScale[playerRelatedObject.ObjectKind] = customizeScale;
|
||||||
_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);
|
||||||
@@ -235,7 +234,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();
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using MareSynchronos.Services;
|
|||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Penumbra.String;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using static FFXIVClientStructs.FFXIV.Client.Game.Character.DrawDataContainer;
|
using static FFXIVClientStructs.FFXIV.Client.Game.Character.DrawDataContainer;
|
||||||
using ObjectKind = MareSynchronos.API.Data.Enum.ObjectKind;
|
using ObjectKind = MareSynchronos.API.Data.Enum.ObjectKind;
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -56,7 +56,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, (_) =>
|
||||||
@@ -67,7 +67,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;
|
||||||
@@ -158,7 +158,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")));
|
||||||
@@ -239,11 +239,11 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
Logger.LogTrace("[{applicationId}] Restoring state for {name} ({OnlineUser})", applicationId, name, OnlineUser);
|
Logger.LogTrace("[{applicationId}] Restoring state for {name} ({OnlineUser})", applicationId, name, OnlineUser);
|
||||||
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 (!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
|
||||||
{
|
{
|
||||||
@@ -311,31 +311,31 @@ 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))
|
||||||
{
|
{
|
||||||
await _ipcManager.CustomizePlusSetBodyScaleAsync(handler.Address, customizePlusData).ConfigureAwait(false);
|
await _ipcManager.CustomizePlus.SetBodyScaleAsync(handler.Address, customizePlusData).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _ipcManager.CustomizePlusRevertAsync(handler.Address).ConfigureAwait(false);
|
await _ipcManager.CustomizePlus.RevertAsync(handler.Address).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
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:
|
||||||
@@ -433,7 +433,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))
|
||||||
{
|
{
|
||||||
@@ -443,7 +443,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();
|
||||||
@@ -527,10 +527,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RevertCustomizationDataAsync(ObjectKind objectKind, string name, Guid applicationId, CancellationToken cancelToken)
|
private async Task RevertCustomizationDataAsync(ObjectKind objectKind, string name, Guid applicationId, CancellationToken cancelToken)
|
||||||
@@ -545,26 +545,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, name, 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.CustomizePlusRevertAsync(address).ConfigureAwait(false);
|
await _ipcManager.CustomizePlus.RevertAsync(address).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.CustomizePlusRevertAsync(minionOrMount).ConfigureAwait(false);
|
await _ipcManager.CustomizePlus.RevertAsync(minionOrMount).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.Name, 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)
|
||||||
@@ -572,10 +572,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.CustomizePlusRevertAsync(pet).ConfigureAwait(false);
|
await _ipcManager.CustomizePlus.RevertAsync(pet).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.Name, 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)
|
||||||
@@ -583,10 +583,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.CustomizePlusRevertAsync(companion).ConfigureAwait(false);
|
await _ipcManager.CustomizePlus.RevertAsync(companion).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.Name, tempHandler, applicationId, cancelToken).ConfigureAwait(false);
|
||||||
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false);
|
await _ipcManager.Penumbra.RedrawAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,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;
|
||||||
@@ -88,8 +89,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));
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Dalamud.Interface.Internal.Notifications;
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
using MareSynchronos.API.Data;
|
using MareSynchronos.API.Data;
|
||||||
using MareSynchronos.API.Dto;
|
using MareSynchronos.API.Dto;
|
||||||
using MareSynchronos.API.Dto.Group;
|
using MareSynchronos.API.Dto.Group;
|
||||||
@@ -83,6 +84,6 @@ public record CombatOrPerformanceStartMessage : MessageBase;
|
|||||||
public record CombatOrPerformanceEndMessage : MessageBase;
|
public record CombatOrPerformanceEndMessage : 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(Character Character) : SameThreadMessage;
|
||||||
#pragma warning restore S2094
|
#pragma warning restore S2094
|
||||||
#pragma warning restore MA0048 // File name must match type name
|
#pragma warning restore MA0048 // File name must match type name
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using Dalamud.Interface.Internal.Notifications;
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using ImGuiNET;
|
|||||||
using MareSynchronos.API.Data;
|
using MareSynchronos.API.Data;
|
||||||
using MareSynchronos.API.Data.Comparer;
|
using MareSynchronos.API.Data.Comparer;
|
||||||
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;
|
||||||
@@ -493,7 +493,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,7 +513,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 Mare Storage. "
|
UiSharedService.AttachToolTip("Attempts to resume monitoring for both Penumbra and Mare Storage. "
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ using Dalamud.Plugin;
|
|||||||
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;
|
||||||
@@ -98,11 +98,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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +110,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;
|
||||||
|
|
||||||
@@ -569,7 +569,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();
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace MareSynchronos.Utils;
|
namespace MareSynchronos.Utils;
|
||||||
|
|||||||
Reference in New Issue
Block a user