cleanup and fix application issues
This commit is contained in:
@@ -62,7 +62,6 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
private readonly ActionSubscriber<GameObject, RedrawType> _penumbraRedrawObject;
|
private readonly ActionSubscriber<GameObject, RedrawType> _penumbraRedrawObject;
|
||||||
private readonly ConcurrentDictionary<IntPtr, bool> _penumbraRedrawRequests = new();
|
private readonly ConcurrentDictionary<IntPtr, bool> _penumbraRedrawRequests = new();
|
||||||
private readonly FuncSubscriber<string, PenumbraApiEc> _penumbraRemoveTemporaryCollection;
|
private readonly FuncSubscriber<string, PenumbraApiEc> _penumbraRemoveTemporaryCollection;
|
||||||
private readonly FuncSubscriber<string, string, int, PenumbraApiEc> _penumbraRemoveTemporaryMod;
|
|
||||||
private readonly FuncSubscriber<string> _penumbraResolveModDir;
|
private readonly FuncSubscriber<string> _penumbraResolveModDir;
|
||||||
private readonly FuncSubscriber<string[], string[], (string[], string[][])> _penumbraResolvePaths;
|
private readonly FuncSubscriber<string[], string[], (string[], string[][])> _penumbraResolvePaths;
|
||||||
private readonly SemaphoreSlim _redrawSemaphore = new(2);
|
private readonly SemaphoreSlim _redrawSemaphore = new(2);
|
||||||
@@ -92,7 +91,6 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
_penumbraAddTemporaryMod = Penumbra.Api.Ipc.AddTemporaryMod.Subscriber(pi);
|
_penumbraAddTemporaryMod = Penumbra.Api.Ipc.AddTemporaryMod.Subscriber(pi);
|
||||||
_penumbraCreateNamedTemporaryCollection = Penumbra.Api.Ipc.CreateNamedTemporaryCollection.Subscriber(pi);
|
_penumbraCreateNamedTemporaryCollection = Penumbra.Api.Ipc.CreateNamedTemporaryCollection.Subscriber(pi);
|
||||||
_penumbraRemoveTemporaryCollection = Penumbra.Api.Ipc.RemoveTemporaryCollectionByName.Subscriber(pi);
|
_penumbraRemoveTemporaryCollection = Penumbra.Api.Ipc.RemoveTemporaryCollectionByName.Subscriber(pi);
|
||||||
_penumbraRemoveTemporaryMod = Penumbra.Api.Ipc.RemoveTemporaryMod.Subscriber(pi);
|
|
||||||
_penumbraAssignTemporaryCollection = Penumbra.Api.Ipc.AssignTemporaryCollection.Subscriber(pi);
|
_penumbraAssignTemporaryCollection = Penumbra.Api.Ipc.AssignTemporaryCollection.Subscriber(pi);
|
||||||
_penumbraResolvePaths = Penumbra.Api.Ipc.ResolvePlayerPaths.Subscriber(pi);
|
_penumbraResolvePaths = Penumbra.Api.Ipc.ResolvePlayerPaths.Subscriber(pi);
|
||||||
_penumbraEnabled = Penumbra.Api.Ipc.GetEnabledState.Subscriber(pi);
|
_penumbraEnabled = Penumbra.Api.Ipc.GetEnabledState.Subscriber(pi);
|
||||||
@@ -409,6 +407,31 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<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 string PenumbraGetMetaManipulations()
|
public string PenumbraGetMetaManipulations()
|
||||||
{
|
{
|
||||||
if (!CheckPenumbraApi()) return string.Empty;
|
if (!CheckPenumbraApi()) return string.Empty;
|
||||||
@@ -449,6 +472,18 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
return await _dalamudUtil.RunOnFrameworkThread(() => _penumbraResolvePaths.Invoke(forward, reverse)).ConfigureAwait(false);
|
return await _dalamudUtil.RunOnFrameworkThread(() => _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, new Dictionary<string, string>(), 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)
|
public async Task PenumbraSetTemporaryModsAsync(ILogger logger, Guid applicationId, string collName, Dictionary<string, string> modPaths)
|
||||||
{
|
{
|
||||||
if (!CheckPenumbraApi()) return;
|
if (!CheckPenumbraApi()) return;
|
||||||
@@ -459,54 +494,11 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
logger.LogTrace("[{applicationId}] Change: {from} => {to}", applicationId, mod.Key, mod.Value);
|
logger.LogTrace("[{applicationId}] Change: {from} => {to}", applicationId, mod.Key, mod.Value);
|
||||||
}
|
}
|
||||||
var retRemove = _penumbraRemoveTemporaryMod.Invoke("MareChara_Files", collName, 0);
|
|
||||||
logger.LogTrace("[{applicationId}] Removing prev mod files mod for {collName}, Success: {ret}", applicationId, collName, retRemove);
|
|
||||||
var retAdd = _penumbraAddTemporaryMod.Invoke("MareChara_Files", collName, modPaths, string.Empty, 0);
|
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);
|
logger.LogTrace("[{applicationId}] Setting temp files mod for {collName}, Success: {ret}", applicationId, collName, retAdd);
|
||||||
}).ConfigureAwait(false);
|
}).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 retRemove = _penumbraRemoveTemporaryMod.Invoke("MareChara_Meta", collName, 0);
|
|
||||||
logger.LogTrace("[{applicationId}] Removing prev meta mod for {collName}, Success: {ret}", applicationId, collName, retRemove);
|
|
||||||
var retAdd = _penumbraAddTemporaryMod.Invoke("MareChara_Meta", collName, new Dictionary<string, string>(), manipulationData, 0);
|
|
||||||
logger.LogTrace("[{applicationId}] Setting temp meta mod for {collName}, Success: {ret}", applicationId, collName, retAdd);
|
|
||||||
}).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> PenumbraCreateTemporaryCollection(ILogger logger, string uid)
|
|
||||||
{
|
|
||||||
if (!CheckPenumbraApi()) return string.Empty;
|
|
||||||
|
|
||||||
return await _dalamudUtil.RunOnFrameworkThread(() =>
|
|
||||||
{
|
|
||||||
var collName = "Mare_" + uid;
|
|
||||||
var retRemove = _penumbraRemoveTemporaryCollection.Invoke(collName);
|
|
||||||
logger.LogTrace("Removing Temp Collection {collName}, Success: {ret}", collName, retRemove);
|
|
||||||
var retCreate = _penumbraCreateNamedTemporaryCollection.Invoke(collName);
|
|
||||||
logger.LogTrace("Creating Temp Collection {collName}, Success: {ret}", collName, retCreate);
|
|
||||||
return collName;
|
|
||||||
}).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task PenumbraAssignTemporaryCollection(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 void ToggleGposeQueueMode(bool on)
|
public void ToggleGposeQueueMode(bool on)
|
||||||
{
|
{
|
||||||
_inGposeQueueMode = on;
|
_inGposeQueueMode = on;
|
||||||
|
|||||||
12
MareSynchronos/PlayerData/Data/PlayerChanges.cs
Normal file
12
MareSynchronos/PlayerData/Data/PlayerChanges.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace MareSynchronos.PlayerData.Pairs;
|
||||||
|
|
||||||
|
public enum PlayerChanges
|
||||||
|
{
|
||||||
|
Heels = 1,
|
||||||
|
Customize = 2,
|
||||||
|
Palette = 3,
|
||||||
|
Honorific = 4,
|
||||||
|
ModFiles = 5,
|
||||||
|
ModManip = 6,
|
||||||
|
Glamourer = 7
|
||||||
|
}
|
||||||
@@ -66,8 +66,8 @@ public class MareCharaFileManager
|
|||||||
var applicationId = Guid.NewGuid();
|
var applicationId = Guid.NewGuid();
|
||||||
_ipcManager.ToggleGposeQueueMode(on: true);
|
_ipcManager.ToggleGposeQueueMode(on: true);
|
||||||
await _ipcManager.PenumbraRemoveTemporaryCollectionAsync(_logger, applicationId, charaTarget.Name.TextValue).ConfigureAwait(false);
|
await _ipcManager.PenumbraRemoveTemporaryCollectionAsync(_logger, applicationId, charaTarget.Name.TextValue).ConfigureAwait(false);
|
||||||
var coll = await _ipcManager.PenumbraCreateTemporaryCollection(_logger, charaTarget.Name.TextValue).ConfigureAwait(false);
|
var coll = await _ipcManager.PenumbraCreateTemporaryCollectionAsync(_logger, charaTarget.Name.TextValue).ConfigureAwait(false);
|
||||||
await _ipcManager.PenumbraAssignTemporaryCollection(_logger, coll, charaTarget.ObjectTableIndex()!.Value).ConfigureAwait(false);
|
await _ipcManager.PenumbraAssignTemporaryCollectionAsync(_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.PenumbraSetTemporaryModsAsync(_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.PenumbraSetManipulationDataAsync(_logger, applicationId, coll, LoadedCharaFile.CharaFileData.ManipulationData).ConfigureAwait(false);
|
||||||
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Player, () => charaTarget.Address, false).ConfigureAwait(false);
|
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Player, () => charaTarget.Address, false).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ namespace MareSynchronos.PlayerData.Factories;
|
|||||||
|
|
||||||
public class PairFactory
|
public class PairFactory
|
||||||
{
|
{
|
||||||
private readonly CachedPlayerFactory _cachedPlayerFactory;
|
private readonly PairHandlerFactory _cachedPlayerFactory;
|
||||||
private readonly ILoggerFactory _loggerFactory;
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
private readonly MareMediator _mareMediator;
|
private readonly MareMediator _mareMediator;
|
||||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||||
|
|
||||||
public PairFactory(ILoggerFactory loggerFactory, CachedPlayerFactory cachedPlayerFactory,
|
public PairFactory(ILoggerFactory loggerFactory, PairHandlerFactory cachedPlayerFactory,
|
||||||
MareMediator mareMediator, ServerConfigurationManager serverConfigurationManager)
|
MareMediator mareMediator, ServerConfigurationManager serverConfigurationManager)
|
||||||
{
|
{
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using MareSynchronos.API.Dto.User;
|
using MareSynchronos.API.Dto.User;
|
||||||
using MareSynchronos.FileCache;
|
using MareSynchronos.FileCache;
|
||||||
using MareSynchronos.Interop;
|
using MareSynchronos.Interop;
|
||||||
using MareSynchronos.MareConfiguration;
|
using MareSynchronos.PlayerData.Handlers;
|
||||||
using MareSynchronos.PlayerData.Pairs;
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
using MareSynchronos.Services;
|
using MareSynchronos.Services;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
@@ -10,7 +10,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace MareSynchronos.PlayerData.Factories;
|
namespace MareSynchronos.PlayerData.Factories;
|
||||||
|
|
||||||
public class CachedPlayerFactory
|
public class PairHandlerFactory
|
||||||
{
|
{
|
||||||
private readonly DalamudUtilService _dalamudUtilService;
|
private readonly DalamudUtilService _dalamudUtilService;
|
||||||
private readonly FileCacheManager _fileCacheManager;
|
private readonly FileCacheManager _fileCacheManager;
|
||||||
@@ -19,28 +19,29 @@ public class CachedPlayerFactory
|
|||||||
private readonly IHostApplicationLifetime _hostApplicationLifetime;
|
private readonly IHostApplicationLifetime _hostApplicationLifetime;
|
||||||
private readonly IpcManager _ipcManager;
|
private readonly IpcManager _ipcManager;
|
||||||
private readonly ILoggerFactory _loggerFactory;
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
private readonly MareConfigService _mareConfigService;
|
|
||||||
private readonly MareMediator _mareMediator;
|
private readonly MareMediator _mareMediator;
|
||||||
|
private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
|
||||||
|
|
||||||
public CachedPlayerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager,
|
public PairHandlerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager,
|
||||||
FileDownloadManagerFactory fileDownloadManagerFactory, MareConfigService mareConfigService, DalamudUtilService dalamudUtilService,
|
FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService,
|
||||||
IHostApplicationLifetime hostApplicationLifetime, FileCacheManager fileCacheManager, MareMediator mareMediator)
|
PluginWarningNotificationService pluginWarningNotificationManager, IHostApplicationLifetime hostApplicationLifetime,
|
||||||
|
FileCacheManager fileCacheManager, MareMediator mareMediator)
|
||||||
{
|
{
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
||||||
_ipcManager = ipcManager;
|
_ipcManager = ipcManager;
|
||||||
_fileDownloadManagerFactory = fileDownloadManagerFactory;
|
_fileDownloadManagerFactory = fileDownloadManagerFactory;
|
||||||
_mareConfigService = mareConfigService;
|
|
||||||
_dalamudUtilService = dalamudUtilService;
|
_dalamudUtilService = dalamudUtilService;
|
||||||
|
_pluginWarningNotificationManager = pluginWarningNotificationManager;
|
||||||
_hostApplicationLifetime = hostApplicationLifetime;
|
_hostApplicationLifetime = hostApplicationLifetime;
|
||||||
_fileCacheManager = fileCacheManager;
|
_fileCacheManager = fileCacheManager;
|
||||||
_mareMediator = mareMediator;
|
_mareMediator = mareMediator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CachedPlayer Create(OnlineUserIdentDto onlineUserIdentDto)
|
public PairHandler Create(OnlineUserIdentDto onlineUserIdentDto)
|
||||||
{
|
{
|
||||||
return new CachedPlayer(_loggerFactory.CreateLogger<CachedPlayer>(), onlineUserIdentDto, _gameObjectHandlerFactory,
|
return new PairHandler(_loggerFactory.CreateLogger<PairHandler>(), onlineUserIdentDto, _gameObjectHandlerFactory,
|
||||||
_ipcManager, _fileDownloadManagerFactory.Create(), _mareConfigService, _dalamudUtilService, _hostApplicationLifetime,
|
_ipcManager, _fileDownloadManagerFactory.Create(), _pluginWarningNotificationManager, _dalamudUtilService, _hostApplicationLifetime,
|
||||||
_fileCacheManager, _mareMediator);
|
_fileCacheManager, _mareMediator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,6 +118,18 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void CompareNameAndThrow(string name)
|
||||||
|
{
|
||||||
|
if (!string.Equals(Name, name, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Player name not equal to requested name, pointer invalid");
|
||||||
|
}
|
||||||
|
if (Address == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Player pointer is zero, pointer invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IntPtr CurrentAddress()
|
public IntPtr CurrentAddress()
|
||||||
{
|
{
|
||||||
_dalamudUtil.EnsureIsOnFramework();
|
_dalamudUtil.EnsureIsOnFramework();
|
||||||
@@ -133,6 +145,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
Address = IntPtr.Zero;
|
Address = IntPtr.Zero;
|
||||||
DrawObjectAddress = IntPtr.Zero;
|
DrawObjectAddress = IntPtr.Zero;
|
||||||
|
_haltProcessing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> IsBeingDrawnRunOnFrameworkAsync()
|
public async Task<bool> IsBeingDrawnRunOnFrameworkAsync()
|
||||||
@@ -156,8 +169,6 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
private unsafe void CheckAndUpdateObject()
|
private unsafe void CheckAndUpdateObject()
|
||||||
{
|
{
|
||||||
if (_haltProcessing) return;
|
|
||||||
|
|
||||||
var prevAddr = Address;
|
var prevAddr = Address;
|
||||||
var prevDrawObj = DrawObjectAddress;
|
var prevDrawObj = DrawObjectAddress;
|
||||||
|
|
||||||
@@ -172,6 +183,8 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
|
|||||||
DrawObjectAddress = IntPtr.Zero;
|
DrawObjectAddress = IntPtr.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_haltProcessing) return;
|
||||||
|
|
||||||
bool drawObjDiff = DrawObjectAddress != prevDrawObj;
|
bool drawObjDiff = DrawObjectAddress != prevDrawObj;
|
||||||
bool addrDiff = Address != prevAddr;
|
bool addrDiff = Address != prevAddr;
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
using Dalamud.Interface.Internal.Notifications;
|
using Dalamud.Logging;
|
||||||
using Dalamud.Logging;
|
|
||||||
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;
|
||||||
using MareSynchronos.MareConfiguration;
|
|
||||||
using MareSynchronos.PlayerData.Factories;
|
using MareSynchronos.PlayerData.Factories;
|
||||||
using MareSynchronos.PlayerData.Handlers;
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
using MareSynchronos.Services;
|
using MareSynchronos.Services;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
@@ -17,9 +15,9 @@ using System.Collections.Concurrent;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using ObjectKind = MareSynchronos.API.Data.Enum.ObjectKind;
|
using ObjectKind = MareSynchronos.API.Data.Enum.ObjectKind;
|
||||||
|
|
||||||
namespace MareSynchronos.PlayerData.Pairs;
|
namespace MareSynchronos.PlayerData.Handlers;
|
||||||
|
|
||||||
public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
public sealed class PairHandler : DisposableMediatorSubscriberBase
|
||||||
{
|
{
|
||||||
private readonly DalamudUtilService _dalamudUtil;
|
private readonly DalamudUtilService _dalamudUtil;
|
||||||
private readonly FileDownloadManager _downloadManager;
|
private readonly FileDownloadManager _downloadManager;
|
||||||
@@ -27,33 +25,36 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
private readonly GameObjectHandlerFactory _gameObjectHandlerFactory;
|
private readonly GameObjectHandlerFactory _gameObjectHandlerFactory;
|
||||||
private readonly IpcManager _ipcManager;
|
private readonly IpcManager _ipcManager;
|
||||||
private readonly IHostApplicationLifetime _lifetime;
|
private readonly IHostApplicationLifetime _lifetime;
|
||||||
private readonly OptionalPluginWarning _pluginWarnings;
|
private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
|
||||||
private CancellationTokenSource? _applicationCancellationTokenSource = new();
|
private CancellationTokenSource? _applicationCancellationTokenSource = new();
|
||||||
private Guid _applicationId;
|
private Guid _applicationId;
|
||||||
private Task? _applicationTask;
|
private Task? _applicationTask;
|
||||||
|
private bool _applyLastReceivedDataOnVisible = false;
|
||||||
private CharacterData? _cachedData = null;
|
private CharacterData? _cachedData = null;
|
||||||
private GameObjectHandler? _charaHandler;
|
private GameObjectHandler? _charaHandler;
|
||||||
private CancellationTokenSource? _downloadCancellationTokenSource = new();
|
private CancellationTokenSource? _downloadCancellationTokenSource = new();
|
||||||
private CharacterData? _firstTimeInitData;
|
|
||||||
private string _lastGlamourerData = string.Empty;
|
private string _lastGlamourerData = string.Empty;
|
||||||
private string _originalGlamourerData = string.Empty;
|
private string _originalGlamourerData = string.Empty;
|
||||||
private string _penumbraCollection;
|
private string _penumbraCollection;
|
||||||
private CancellationTokenSource _redrawCts = new();
|
private CancellationTokenSource _redrawCts = new();
|
||||||
|
|
||||||
public CachedPlayer(ILogger<CachedPlayer> logger, OnlineUserIdentDto onlineUser,
|
public PairHandler(ILogger<PairHandler> logger, OnlineUserIdentDto onlineUser,
|
||||||
GameObjectHandlerFactory gameObjectHandlerFactory,
|
GameObjectHandlerFactory gameObjectHandlerFactory,
|
||||||
IpcManager ipcManager, FileDownloadManager transferManager, MareConfigService mareConfigService,
|
IpcManager ipcManager, FileDownloadManager transferManager,
|
||||||
DalamudUtilService dalamudUtil, IHostApplicationLifetime lifetime, FileCacheManager fileDbManager, MareMediator mediator) : base(logger, mediator)
|
PluginWarningNotificationService pluginWarningNotificationManager,
|
||||||
|
DalamudUtilService dalamudUtil, IHostApplicationLifetime lifetime,
|
||||||
|
FileCacheManager fileDbManager, MareMediator mediator) : base(logger, mediator)
|
||||||
{
|
{
|
||||||
OnlineUser = onlineUser;
|
OnlineUser = onlineUser;
|
||||||
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
||||||
_ipcManager = ipcManager;
|
_ipcManager = ipcManager;
|
||||||
_downloadManager = transferManager;
|
_downloadManager = transferManager;
|
||||||
|
_pluginWarningNotificationManager = pluginWarningNotificationManager;
|
||||||
_dalamudUtil = dalamudUtil;
|
_dalamudUtil = dalamudUtil;
|
||||||
_lifetime = lifetime;
|
_lifetime = lifetime;
|
||||||
_fileDbManager = fileDbManager;
|
_fileDbManager = fileDbManager;
|
||||||
|
|
||||||
_penumbraCollection = _ipcManager.PenumbraCreateTemporaryCollection(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult();
|
_penumbraCollection = _ipcManager.PenumbraCreateTemporaryCollectionAsync(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, (_) =>
|
||||||
@@ -63,7 +64,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
});
|
});
|
||||||
Mediator.Subscribe<PenumbraInitializedMessage>(this, (_) =>
|
Mediator.Subscribe<PenumbraInitializedMessage>(this, (_) =>
|
||||||
{
|
{
|
||||||
_penumbraCollection = _ipcManager.PenumbraCreateTemporaryCollection(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult();
|
_penumbraCollection = _ipcManager.PenumbraCreateTemporaryCollectionAsync(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||||
if (!IsVisible && _charaHandler != null)
|
if (!IsVisible && _charaHandler != null)
|
||||||
{
|
{
|
||||||
PlayerName = string.Empty;
|
PlayerName = string.Empty;
|
||||||
@@ -71,40 +72,23 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
_charaHandler = null;
|
_charaHandler = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
_pluginWarnings ??= new()
|
|
||||||
{
|
|
||||||
ShownCustomizePlusWarning = mareConfigService.Current.DisableOptionalPluginWarnings,
|
|
||||||
ShownHeelsWarning = mareConfigService.Current.DisableOptionalPluginWarnings,
|
|
||||||
ShownPalettePlusWarning = mareConfigService.Current.DisableOptionalPluginWarnings,
|
|
||||||
ShownHonorificWarning = mareConfigService.Current.DisableOptionalPluginWarnings,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum PlayerChanges
|
|
||||||
{
|
|
||||||
Heels = 1,
|
|
||||||
Customize = 2,
|
|
||||||
Palette = 3,
|
|
||||||
Honorific = 4,
|
|
||||||
ModFiles = 5,
|
|
||||||
ModManip = 6,
|
|
||||||
Glamourer = 7
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsVisible { get; private set; }
|
public bool IsVisible { get; private set; }
|
||||||
public OnlineUserIdentDto OnlineUser { get; private set; }
|
public OnlineUserIdentDto OnlineUser { get; private set; }
|
||||||
public IntPtr PlayerCharacter => _charaHandler?.Address ?? IntPtr.Zero;
|
public nint PlayerCharacter => _charaHandler?.Address ?? nint.Zero;
|
||||||
public unsafe uint PlayerCharacterId => (_charaHandler?.Address ?? IntPtr.Zero) == IntPtr.Zero
|
public unsafe uint PlayerCharacterId => (_charaHandler?.Address ?? nint.Zero) == nint.Zero
|
||||||
? uint.MaxValue
|
? uint.MaxValue
|
||||||
: ((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)_charaHandler!.Address)->ObjectID;
|
: ((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)_charaHandler!.Address)->ObjectID;
|
||||||
public string? PlayerName { get; private set; }
|
public string? PlayerName { get; private set; }
|
||||||
public string PlayerNameHash => OnlineUser.Ident;
|
public string PlayerNameHash => OnlineUser.Ident;
|
||||||
|
|
||||||
public void ApplyCharacterData(CharacterData characterData, bool forced = false)
|
public void ApplyCharacterData(API.Data.CharacterData characterData, bool forced = false)
|
||||||
{
|
{
|
||||||
if (_charaHandler == null)
|
if (_charaHandler == null)
|
||||||
{
|
{
|
||||||
_firstTimeInitData = characterData;
|
_applyLastReceivedDataOnVisible = true;
|
||||||
|
_cachedData = characterData;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,16 +110,16 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var charaDataToUpdate = CheckUpdatedData(_cachedData?.DeepClone() ?? new(), characterData, forced);
|
var charaDataToUpdate = characterData.CheckUpdatedData(_cachedData?.DeepClone() ?? new(), Logger, this, forced, _applyLastReceivedDataOnVisible);
|
||||||
|
|
||||||
if (_charaHandler != null && _firstTimeInitData != null)
|
if (_charaHandler != null && _applyLastReceivedDataOnVisible)
|
||||||
{
|
{
|
||||||
_firstTimeInitData = null;
|
_applyLastReceivedDataOnVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (charaDataToUpdate.TryGetValue(ObjectKind.Player, out var playerChanges))
|
if (charaDataToUpdate.TryGetValue(ObjectKind.Player, out var playerChanges))
|
||||||
{
|
{
|
||||||
NotifyForMissingPlugins(playerChanges);
|
_pluginWarningNotificationManager.NotifyForMissingPlugins(OnlineUser.User, PlayerName!, playerChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogDebug("Downloading and applying character for {name}", this);
|
Logger.LogDebug("Downloading and applying character for {name}", this);
|
||||||
@@ -146,8 +130,8 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return OnlineUser == null
|
return OnlineUser == null
|
||||||
? (base.ToString() ?? string.Empty)
|
? base.ToString() ?? string.Empty
|
||||||
: (OnlineUser.User.AliasOrUID + ":" + PlayerName + ":" + (PlayerCharacter != IntPtr.Zero ? "HasChar" : "NoChar"));
|
: OnlineUser.User.AliasOrUID + ":" + PlayerName + ":" + (PlayerCharacter != nint.Zero ? "HasChar" : "NoChar");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SetUploading(bool isUploading = true)
|
internal void SetUploading(bool isUploading = true)
|
||||||
@@ -210,21 +194,9 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CheckForNameAndThrow(GameObjectHandler handler, string name)
|
|
||||||
{
|
|
||||||
if (!string.Equals(handler.Name, name, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Player name not equal to requested name, pointer invalid");
|
|
||||||
}
|
|
||||||
if (handler.Address == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Player pointer is zero, pointer invalid");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ApplyCustomizationDataAsync(Guid applicationId, KeyValuePair<ObjectKind, HashSet<PlayerChanges>> changes, CharacterData charaData, CancellationToken token)
|
private async Task ApplyCustomizationDataAsync(Guid applicationId, KeyValuePair<ObjectKind, HashSet<PlayerChanges>> changes, CharacterData charaData, CancellationToken token)
|
||||||
{
|
{
|
||||||
if (PlayerCharacter == IntPtr.Zero) return;
|
if (PlayerCharacter == nint.Zero) return;
|
||||||
var ptr = PlayerCharacter;
|
var ptr = PlayerCharacter;
|
||||||
|
|
||||||
var handler = changes.Key switch
|
var handler = changes.Key switch
|
||||||
@@ -238,7 +210,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (handler.Address == IntPtr.Zero)
|
if (handler.Address == nint.Zero)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -291,105 +263,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dictionary<ObjectKind, HashSet<PlayerChanges>> CheckUpdatedData(CharacterData oldData, CharacterData newData, bool forced)
|
private void DownloadAndApplyCharacter(API.Data.CharacterData charaData, Dictionary<ObjectKind, HashSet<PlayerChanges>> updatedData)
|
||||||
{
|
|
||||||
var charaDataToUpdate = new Dictionary<ObjectKind, HashSet<PlayerChanges>>();
|
|
||||||
foreach (ObjectKind objectKind in Enum.GetValues<ObjectKind>())
|
|
||||||
{
|
|
||||||
charaDataToUpdate[objectKind] = new();
|
|
||||||
oldData.FileReplacements.TryGetValue(objectKind, out var existingFileReplacements);
|
|
||||||
newData.FileReplacements.TryGetValue(objectKind, out var newFileReplacements);
|
|
||||||
oldData.GlamourerData.TryGetValue(objectKind, out var existingGlamourerData);
|
|
||||||
newData.GlamourerData.TryGetValue(objectKind, out var newGlamourerData);
|
|
||||||
|
|
||||||
bool hasNewButNotOldFileReplacements = newFileReplacements != null && existingFileReplacements == null;
|
|
||||||
bool hasOldButNotNewFileReplacements = existingFileReplacements != null && newFileReplacements == null;
|
|
||||||
|
|
||||||
bool hasNewButNotOldGlamourerData = newGlamourerData != null && existingGlamourerData == null;
|
|
||||||
bool hasOldButNotNewGlamourerData = existingGlamourerData != null && newGlamourerData == null;
|
|
||||||
|
|
||||||
bool hasNewAndOldFileReplacements = newFileReplacements != null && existingFileReplacements != null;
|
|
||||||
bool hasNewAndOldGlamourerData = newGlamourerData != null && existingGlamourerData != null;
|
|
||||||
|
|
||||||
if (hasNewButNotOldFileReplacements || hasOldButNotNewFileReplacements || hasNewButNotOldGlamourerData || hasOldButNotNewGlamourerData)
|
|
||||||
{
|
|
||||||
Logger.LogDebug("Updating {object}/{kind} (Some new data arrived: NewButNotOldFiles:{hasNewButNotOldFileReplacements}," +
|
|
||||||
" OldButNotNewFiles:{hasOldButNotNewFileReplacements}, NewButNotOldGlam:{hasNewButNotOldGlamourerData}, OldButNotNewGlam:{hasOldButNotNewGlamourerData}) => {change}, {change2}",
|
|
||||||
this, objectKind, hasNewButNotOldFileReplacements, hasOldButNotNewFileReplacements, hasNewButNotOldGlamourerData, hasOldButNotNewGlamourerData, PlayerChanges.ModFiles, PlayerChanges.Glamourer);
|
|
||||||
charaDataToUpdate[objectKind].Add(PlayerChanges.ModFiles);
|
|
||||||
charaDataToUpdate[objectKind].Add(PlayerChanges.Glamourer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (hasNewAndOldFileReplacements)
|
|
||||||
{
|
|
||||||
bool listsAreEqual = oldData.FileReplacements[objectKind].SequenceEqual(newData.FileReplacements[objectKind], Data.FileReplacementDataComparer.Instance);
|
|
||||||
if (!listsAreEqual || _firstTimeInitData != null)
|
|
||||||
{
|
|
||||||
Logger.LogDebug("Updating {object}/{kind} (FileReplacements not equal) => {change}", this, objectKind, PlayerChanges.ModFiles);
|
|
||||||
charaDataToUpdate[objectKind].Add(PlayerChanges.ModFiles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasNewAndOldGlamourerData)
|
|
||||||
{
|
|
||||||
bool glamourerDataDifferent = !string.Equals(oldData.GlamourerData[objectKind], newData.GlamourerData[objectKind], StringComparison.Ordinal);
|
|
||||||
if (glamourerDataDifferent || forced)
|
|
||||||
{
|
|
||||||
Logger.LogDebug("Updating {object}/{kind} (Glamourer different) => {change}", this, objectKind, PlayerChanges.Glamourer);
|
|
||||||
charaDataToUpdate[objectKind].Add(PlayerChanges.Glamourer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objectKind != ObjectKind.Player) continue;
|
|
||||||
|
|
||||||
bool manipDataDifferent = !string.Equals(oldData.ManipulationData, newData.ManipulationData, StringComparison.Ordinal);
|
|
||||||
if (manipDataDifferent || _firstTimeInitData != null)
|
|
||||||
{
|
|
||||||
Logger.LogDebug("Updating {object}/{kind} (Diff manip data) => {change}", this, objectKind, PlayerChanges.ModManip);
|
|
||||||
charaDataToUpdate[objectKind].Add(PlayerChanges.ModManip);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool heelsOffsetDifferent = oldData.HeelsOffset != newData.HeelsOffset;
|
|
||||||
if (heelsOffsetDifferent || (forced && newData.HeelsOffset != 0))
|
|
||||||
{
|
|
||||||
Logger.LogDebug("Updating {object}/{kind} (Diff heels data) => {change}", this, objectKind, PlayerChanges.Heels);
|
|
||||||
charaDataToUpdate[objectKind].Add(PlayerChanges.Heels);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool customizeDataDifferent = !string.Equals(oldData.CustomizePlusData, newData.CustomizePlusData, StringComparison.Ordinal);
|
|
||||||
if (customizeDataDifferent || (forced && !string.IsNullOrEmpty(newData.CustomizePlusData)))
|
|
||||||
{
|
|
||||||
Logger.LogDebug("Updating {object}/{kind} (Diff customize data) => {change}", this, objectKind, PlayerChanges.Customize);
|
|
||||||
charaDataToUpdate[objectKind].Add(PlayerChanges.Customize);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool palettePlusDataDifferent = !string.Equals(oldData.PalettePlusData, newData.PalettePlusData, StringComparison.Ordinal);
|
|
||||||
if (palettePlusDataDifferent || (forced && !string.IsNullOrEmpty(newData.PalettePlusData)))
|
|
||||||
{
|
|
||||||
Logger.LogDebug("Updating {object}/{kind} (Diff palette data) => {change}", this, objectKind, PlayerChanges.Palette);
|
|
||||||
charaDataToUpdate[objectKind].Add(PlayerChanges.Palette);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool honorificDataDifferent = !string.Equals(oldData.HonorificData, newData.HonorificData, StringComparison.Ordinal);
|
|
||||||
if (honorificDataDifferent || (forced && !string.IsNullOrEmpty(newData.HonorificData)))
|
|
||||||
{
|
|
||||||
Logger.LogDebug("Updating {object}/{kind} (Diff honorific data) => {change}", this, objectKind, PlayerChanges.Honorific);
|
|
||||||
charaDataToUpdate[objectKind].Add(PlayerChanges.Honorific);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (KeyValuePair<ObjectKind, HashSet<PlayerChanges>> data in charaDataToUpdate.ToList())
|
|
||||||
{
|
|
||||||
if (!data.Value.Any()) charaDataToUpdate.Remove(data.Key);
|
|
||||||
else charaDataToUpdate[data.Key] = data.Value.OrderByDescending(p => (int)p).ToHashSet();
|
|
||||||
}
|
|
||||||
|
|
||||||
return charaDataToUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DownloadAndApplyCharacter(CharacterData charaData, Dictionary<ObjectKind, HashSet<PlayerChanges>> updatedData)
|
|
||||||
{
|
{
|
||||||
if (!updatedData.Any())
|
if (!updatedData.Any())
|
||||||
{
|
{
|
||||||
@@ -412,7 +286,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
int attempts = 0;
|
int attempts = 0;
|
||||||
List<FileReplacementData> toDownloadReplacements = TryCalculateModdedDictionary(charaData, out moddedPaths, downloadToken);
|
List<FileReplacementData> toDownloadReplacements = TryCalculateModdedDictionary(charaData, out moddedPaths, downloadToken);
|
||||||
|
|
||||||
while ((toDownloadReplacements.Count > 0 && attempts++ <= 10 && !downloadToken.IsCancellationRequested))
|
while (toDownloadReplacements.Count > 0 && attempts++ <= 10 && !downloadToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
_downloadManager.CancelDownload();
|
_downloadManager.CancelDownload();
|
||||||
Logger.LogDebug("Downloading missing files for player {name}, {kind}", PlayerName, updatedData);
|
Logger.LogDebug("Downloading missing files for player {name}, {kind}", PlayerName, updatedData);
|
||||||
@@ -494,14 +368,21 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
Logger.LogWarning(ex, "[{applicationId}] Cancelled, player turned null during application", _applicationId);
|
Logger.LogWarning(ex, "[{applicationId}] Cancelled, player turned null during application", _applicationId);
|
||||||
IsVisible = false;
|
IsVisible = false;
|
||||||
if (_cachedData == null)
|
_applyLastReceivedDataOnVisible = true;
|
||||||
{
|
|
||||||
_firstTimeInitData = charaData.DeepClone();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogWarning(ex, "[{applicationId}] Cancelled", _applicationId);
|
if (ex is AggregateException aggr && aggr.InnerExceptions.Any(e => e is ArgumentNullException))
|
||||||
|
{
|
||||||
|
IsVisible = false;
|
||||||
|
_applyLastReceivedDataOnVisible = true;
|
||||||
|
_cachedData = charaData;
|
||||||
|
Logger.LogWarning(aggr, "[{applicationId}] Cancelled, player turned null during application", _applicationId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.LogWarning(ex, "[{applicationId}] Cancelled", _applicationId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, token);
|
}, token);
|
||||||
}, downloadToken);
|
}, downloadToken);
|
||||||
@@ -518,21 +399,21 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
Logger.LogDebug("One-Time Initialized {this}", this);
|
Logger.LogDebug("One-Time Initialized {this}", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_charaHandler?.Address != IntPtr.Zero && !IsVisible)
|
if (_charaHandler?.Address != nint.Zero && !IsVisible)
|
||||||
{
|
{
|
||||||
IsVisible = true;
|
IsVisible = true;
|
||||||
Mediator.Publish(new CachedPlayerVisibleMessage(this));
|
Mediator.Publish(new PairHandlerVisibleMessage(this));
|
||||||
Logger.LogTrace("{this} visibility changed, now: {visi}", this, IsVisible);
|
Logger.LogTrace("{this} visibility changed, now: {visi}", this, IsVisible);
|
||||||
if (_firstTimeInitData != null || _cachedData != null)
|
if (_applyLastReceivedDataOnVisible && _cachedData != null)
|
||||||
{
|
{
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
_lastGlamourerData = await _ipcManager.GlamourerGetCharacterCustomizationAsync(PlayerCharacter).ConfigureAwait(false);
|
_lastGlamourerData = await _ipcManager.GlamourerGetCharacterCustomizationAsync(PlayerCharacter).ConfigureAwait(false);
|
||||||
ApplyCharacterData(_firstTimeInitData ?? _cachedData!, true);
|
ApplyCharacterData(_cachedData!, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (_charaHandler?.Address == IntPtr.Zero && IsVisible)
|
else if (_charaHandler?.Address == nint.Zero && IsVisible)
|
||||||
{
|
{
|
||||||
IsVisible = false;
|
IsVisible = false;
|
||||||
Logger.LogTrace("{this} visibility changed, now: {visi}", this, IsVisible);
|
Logger.LogTrace("{this} visibility changed, now: {visi}", this, IsVisible);
|
||||||
@@ -566,7 +447,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
await _ipcManager.HonorificSetTitleAsync(PlayerCharacter, _cachedData.HonorificData).ConfigureAwait(false);
|
await _ipcManager.HonorificSetTitleAsync(PlayerCharacter, _cachedData.HonorificData).ConfigureAwait(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
_ipcManager.PenumbraAssignTemporaryCollection(Logger, _penumbraCollection, _charaHandler.GetGameObject()!.ObjectIndex).GetAwaiter().GetResult();
|
_ipcManager.PenumbraAssignTemporaryCollectionAsync(Logger, _penumbraCollection, _charaHandler.GetGameObject()!.ObjectIndex).GetAwaiter().GetResult();
|
||||||
|
|
||||||
_downloadManager.Initialize();
|
_downloadManager.Initialize();
|
||||||
}
|
}
|
||||||
@@ -593,44 +474,10 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
}, token);
|
}, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NotifyForMissingPlugins(HashSet<PlayerChanges> changes)
|
|
||||||
{
|
|
||||||
List<string> missingPluginsForData = new();
|
|
||||||
if (changes.Contains(PlayerChanges.Heels) && !_pluginWarnings.ShownHeelsWarning && !_ipcManager.CheckHeelsApi())
|
|
||||||
{
|
|
||||||
missingPluginsForData.Add("Heels");
|
|
||||||
_pluginWarnings.ShownHeelsWarning = true;
|
|
||||||
}
|
|
||||||
if (changes.Contains(PlayerChanges.Customize) && !_pluginWarnings.ShownCustomizePlusWarning && !_ipcManager.CheckCustomizePlusApi())
|
|
||||||
{
|
|
||||||
missingPluginsForData.Add("Customize+");
|
|
||||||
_pluginWarnings.ShownCustomizePlusWarning = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changes.Contains(PlayerChanges.Palette) && !_pluginWarnings.ShownPalettePlusWarning && !_ipcManager.CheckPalettePlusApi())
|
|
||||||
{
|
|
||||||
missingPluginsForData.Add("Palette+");
|
|
||||||
_pluginWarnings.ShownPalettePlusWarning = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changes.Contains(PlayerChanges.Honorific) && !_pluginWarnings.ShownHonorificWarning && !_ipcManager.CheckHonorificApi())
|
|
||||||
{
|
|
||||||
missingPluginsForData.Add("Honorific");
|
|
||||||
_pluginWarnings.ShownHonorificWarning = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (missingPluginsForData.Any())
|
|
||||||
{
|
|
||||||
Mediator.Publish(new NotificationMessage("Missing plugins for " + PlayerName,
|
|
||||||
$"Received data for {PlayerName} that contained information for plugins you have not installed. Install {string.Join(", ", missingPluginsForData)} to experience their character fully.",
|
|
||||||
NotificationType.Warning, 10000));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task RevertCustomizationDataAsync(ObjectKind objectKind, string name, Guid applicationId)
|
private async Task RevertCustomizationDataAsync(ObjectKind objectKind, string name, Guid applicationId)
|
||||||
{
|
{
|
||||||
nint address = _dalamudUtil.GetPlayerCharacterFromCachedTableByIdent(OnlineUser.Ident);
|
nint address = _dalamudUtil.GetPlayerCharacterFromCachedTableByIdent(OnlineUser.Ident);
|
||||||
if (address == IntPtr.Zero) return;
|
if (address == nint.Zero) return;
|
||||||
|
|
||||||
var cancelToken = new CancellationTokenSource();
|
var cancelToken = new CancellationTokenSource();
|
||||||
cancelToken.CancelAfter(TimeSpan.FromSeconds(60));
|
cancelToken.CancelAfter(TimeSpan.FromSeconds(60));
|
||||||
@@ -640,26 +487,26 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
if (objectKind == ObjectKind.Player)
|
if (objectKind == ObjectKind.Player)
|
||||||
{
|
{
|
||||||
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Player, () => address, false).ConfigureAwait(false);
|
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Player, () => address, false).ConfigureAwait(false);
|
||||||
CheckForNameAndThrow(tempHandler, name);
|
tempHandler.CompareNameAndThrow(name);
|
||||||
Logger.LogDebug("[{applicationId}] Restoring Customization and Equipment for {alias}/{name}: {data}", applicationId, OnlineUser.User.AliasOrUID, name, _originalGlamourerData);
|
Logger.LogDebug("[{applicationId}] Restoring Customization and Equipment for {alias}/{name}: {data}", applicationId, OnlineUser.User.AliasOrUID, name, _originalGlamourerData);
|
||||||
await _ipcManager.GlamourerApplyCustomizationAndEquipmentAsync(Logger, tempHandler, _originalGlamourerData, _lastGlamourerData, applicationId, cancelToken.Token, fireAndForget: false).ConfigureAwait(false);
|
await _ipcManager.GlamourerApplyCustomizationAndEquipmentAsync(Logger, tempHandler, _originalGlamourerData, _lastGlamourerData, applicationId, cancelToken.Token, fireAndForget: false).ConfigureAwait(false);
|
||||||
CheckForNameAndThrow(tempHandler, 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.HeelsRestoreOffsetForPlayerAsync(address).ConfigureAwait(false);
|
||||||
CheckForNameAndThrow(tempHandler, 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.CustomizePlusRevertAsync(address).ConfigureAwait(false);
|
||||||
CheckForNameAndThrow(tempHandler, name);
|
tempHandler.CompareNameAndThrow(name);
|
||||||
Logger.LogDebug("[{applicationId}] Restoring Palette+ for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name);
|
Logger.LogDebug("[{applicationId}] Restoring Palette+ for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name);
|
||||||
await _ipcManager.PalettePlusRemovePaletteAsync(address).ConfigureAwait(false);
|
await _ipcManager.PalettePlusRemovePaletteAsync(address).ConfigureAwait(false);
|
||||||
CheckForNameAndThrow(tempHandler, 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.HonorificClearTitleAsync(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 != IntPtr.Zero)
|
if (minionOrMount != nint.Zero)
|
||||||
{
|
{
|
||||||
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.MinionOrMount, () => minionOrMount, false).ConfigureAwait(false);
|
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.MinionOrMount, () => minionOrMount, false).ConfigureAwait(false);
|
||||||
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false);
|
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false);
|
||||||
@@ -668,7 +515,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
else if (objectKind == ObjectKind.Pet)
|
else if (objectKind == ObjectKind.Pet)
|
||||||
{
|
{
|
||||||
var pet = await _dalamudUtil.GetPetAsync(address).ConfigureAwait(false);
|
var pet = await _dalamudUtil.GetPetAsync(address).ConfigureAwait(false);
|
||||||
if (pet != IntPtr.Zero)
|
if (pet != nint.Zero)
|
||||||
{
|
{
|
||||||
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => pet, false).ConfigureAwait(false);
|
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => pet, false).ConfigureAwait(false);
|
||||||
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false);
|
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false);
|
||||||
@@ -677,7 +524,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
else if (objectKind == ObjectKind.Companion)
|
else if (objectKind == ObjectKind.Companion)
|
||||||
{
|
{
|
||||||
var companion = await _dalamudUtil.GetCompanionAsync(address).ConfigureAwait(false);
|
var companion = await _dalamudUtil.GetCompanionAsync(address).ConfigureAwait(false);
|
||||||
if (companion != IntPtr.Zero)
|
if (companion != nint.Zero)
|
||||||
{
|
{
|
||||||
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => companion, false).ConfigureAwait(false);
|
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => companion, false).ConfigureAwait(false);
|
||||||
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false);
|
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false);
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using MareSynchronos.API.Data;
|
using MareSynchronos.API.Data;
|
||||||
|
using MareSynchronos.PlayerData.Handlers;
|
||||||
using MareSynchronos.Services;
|
using MareSynchronos.Services;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
@@ -13,7 +14,7 @@ public class OnlinePlayerManager : DisposableMediatorSubscriberBase
|
|||||||
private readonly ApiController _apiController;
|
private readonly ApiController _apiController;
|
||||||
private readonly DalamudUtilService _dalamudUtil;
|
private readonly DalamudUtilService _dalamudUtil;
|
||||||
private readonly FileUploadManager _fileTransferManager;
|
private readonly FileUploadManager _fileTransferManager;
|
||||||
private readonly HashSet<CachedPlayer> _newVisiblePlayers = new();
|
private readonly HashSet<PairHandler> _newVisiblePlayers = new();
|
||||||
private readonly PairManager _pairManager;
|
private readonly PairManager _pairManager;
|
||||||
private CharacterData? _lastSentData;
|
private CharacterData? _lastSentData;
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ public class OnlinePlayerManager : DisposableMediatorSubscriberBase
|
|||||||
Logger.LogDebug("Not sending data for {hash}", newData.DataHash.Value);
|
Logger.LogDebug("Not sending data for {hash}", newData.DataHash.Value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Mediator.Subscribe<CachedPlayerVisibleMessage>(this, (msg) => _newVisiblePlayers.Add(msg.Player));
|
Mediator.Subscribe<PairHandlerVisibleMessage>(this, (msg) => _newVisiblePlayers.Add(msg.Player));
|
||||||
Mediator.Subscribe<ConnectedMessage>(this, (_) => PushCharacterData(_pairManager.GetVisibleUsers()));
|
Mediator.Subscribe<ConnectedMessage>(this, (_) => PushCharacterData(_pairManager.GetVisibleUsers()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using MareSynchronos.API.Data.Extensions;
|
|||||||
using MareSynchronos.API.Dto.Group;
|
using MareSynchronos.API.Dto.Group;
|
||||||
using MareSynchronos.API.Dto.User;
|
using MareSynchronos.API.Dto.User;
|
||||||
using MareSynchronos.PlayerData.Factories;
|
using MareSynchronos.PlayerData.Factories;
|
||||||
|
using MareSynchronos.PlayerData.Handlers;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.Services.ServerConfiguration;
|
using MareSynchronos.Services.ServerConfiguration;
|
||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
@@ -14,7 +15,7 @@ namespace MareSynchronos.PlayerData.Pairs;
|
|||||||
|
|
||||||
public class Pair
|
public class Pair
|
||||||
{
|
{
|
||||||
private readonly CachedPlayerFactory _cachedPlayerFactory;
|
private readonly PairHandlerFactory _cachedPlayerFactory;
|
||||||
private readonly SemaphoreSlim _creationSemaphore = new(1);
|
private readonly SemaphoreSlim _creationSemaphore = new(1);
|
||||||
private readonly ILogger<Pair> _logger;
|
private readonly ILogger<Pair> _logger;
|
||||||
private readonly MareMediator _mediator;
|
private readonly MareMediator _mediator;
|
||||||
@@ -22,7 +23,7 @@ public class Pair
|
|||||||
private CancellationTokenSource _applicationCts = new CancellationTokenSource();
|
private CancellationTokenSource _applicationCts = new CancellationTokenSource();
|
||||||
private OnlineUserIdentDto? _onlineUserIdentDto = null;
|
private OnlineUserIdentDto? _onlineUserIdentDto = null;
|
||||||
|
|
||||||
public Pair(ILogger<Pair> logger, CachedPlayerFactory cachedPlayerFactory,
|
public Pair(ILogger<Pair> logger, PairHandlerFactory cachedPlayerFactory,
|
||||||
MareMediator mediator, ServerConfigurationManager serverConfigurationManager)
|
MareMediator mediator, ServerConfigurationManager serverConfigurationManager)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -46,7 +47,7 @@ public class Pair
|
|||||||
|
|
||||||
public UserPairDto? UserPair { get; set; }
|
public UserPairDto? UserPair { get; set; }
|
||||||
|
|
||||||
private CachedPlayer? CachedPlayer { get; set; }
|
private PairHandler? CachedPlayer { get; set; }
|
||||||
|
|
||||||
public void AddContextMenu(GameObjectContextMenuOpenArgs args)
|
public void AddContextMenu(GameObjectContextMenuOpenArgs args)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -69,8 +69,9 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddSingleton<UidDisplayHandler>();
|
collection.AddSingleton<UidDisplayHandler>();
|
||||||
collection.AddSingleton<GameObjectHandlerFactory>();
|
collection.AddSingleton<GameObjectHandlerFactory>();
|
||||||
collection.AddSingleton<FileDownloadManagerFactory>();
|
collection.AddSingleton<FileDownloadManagerFactory>();
|
||||||
collection.AddSingleton<CachedPlayerFactory>();
|
collection.AddSingleton<PairHandlerFactory>();
|
||||||
collection.AddSingleton<PairFactory>();
|
collection.AddSingleton<PairFactory>();
|
||||||
|
collection.AddSingleton<PluginWarningNotificationService>();
|
||||||
collection.AddSingleton((s) => new DalamudUtilService(s.GetRequiredService<ILogger<DalamudUtilService>>(),
|
collection.AddSingleton((s) => new DalamudUtilService(s.GetRequiredService<ILogger<DalamudUtilService>>(),
|
||||||
clientState, objectTable, framework, gameGui, condition, gameData,
|
clientState, objectTable, framework, gameGui, condition, gameData,
|
||||||
s.GetRequiredService<MareMediator>(), s.GetRequiredService<PerformanceCollectorService>()));
|
s.GetRequiredService<MareMediator>(), s.GetRequiredService<PerformanceCollectorService>()));
|
||||||
|
|||||||
@@ -65,6 +65,6 @@ public record ProfilePopoutToggle(Pair? Pair) : MessageBase;
|
|||||||
public record CompactUiChange(Vector2 Size, Vector2 Position) : MessageBase;
|
public record CompactUiChange(Vector2 Size, Vector2 Position) : MessageBase;
|
||||||
public record ProfileOpenStandaloneMessage(Pair Pair) : MessageBase;
|
public record ProfileOpenStandaloneMessage(Pair Pair) : MessageBase;
|
||||||
public record RemoveWindowMessage(WindowMediatorSubscriberBase Window) : MessageBase;
|
public record RemoveWindowMessage(WindowMediatorSubscriberBase Window) : MessageBase;
|
||||||
public record CachedPlayerVisibleMessage(CachedPlayer Player) : MessageBase;
|
public record PairHandlerVisibleMessage(PairHandler Player) : MessageBase;
|
||||||
|
|
||||||
#pragma warning restore MA0048 // File name must match type name
|
#pragma warning restore MA0048 // File name must match type name
|
||||||
68
MareSynchronos/Services/PluginWarningNotificationService.cs
Normal file
68
MareSynchronos/Services/PluginWarningNotificationService.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
|
using MareSynchronos.API.Data;
|
||||||
|
using MareSynchronos.API.Data.Comparer;
|
||||||
|
using MareSynchronos.Interop;
|
||||||
|
using MareSynchronos.MareConfiguration;
|
||||||
|
using MareSynchronos.Services.Mediator;
|
||||||
|
|
||||||
|
namespace MareSynchronos.PlayerData.Pairs;
|
||||||
|
|
||||||
|
public class PluginWarningNotificationService
|
||||||
|
{
|
||||||
|
private readonly Dictionary<UserData, OptionalPluginWarning> _cachedOptionalPluginWarnings = new(UserDataComparer.Instance);
|
||||||
|
private readonly IpcManager _ipcManager;
|
||||||
|
private readonly MareConfigService _mareConfigService;
|
||||||
|
private readonly MareMediator _mediator;
|
||||||
|
|
||||||
|
public PluginWarningNotificationService(MareConfigService mareConfigService, IpcManager ipcManager, MareMediator mediator)
|
||||||
|
{
|
||||||
|
_mareConfigService = mareConfigService;
|
||||||
|
_ipcManager = ipcManager;
|
||||||
|
_mediator = mediator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotifyForMissingPlugins(UserData user, string playerName, HashSet<PlayerChanges> changes)
|
||||||
|
{
|
||||||
|
if (!_cachedOptionalPluginWarnings.TryGetValue(user, out var warning))
|
||||||
|
{
|
||||||
|
_cachedOptionalPluginWarnings[user] = warning = new()
|
||||||
|
{
|
||||||
|
ShownCustomizePlusWarning = _mareConfigService.Current.DisableOptionalPluginWarnings,
|
||||||
|
ShownHeelsWarning = _mareConfigService.Current.DisableOptionalPluginWarnings,
|
||||||
|
ShownPalettePlusWarning = _mareConfigService.Current.DisableOptionalPluginWarnings,
|
||||||
|
ShownHonorificWarning = _mareConfigService.Current.DisableOptionalPluginWarnings,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
List<string> missingPluginsForData = new();
|
||||||
|
if (changes.Contains(PlayerChanges.Heels) && !warning.ShownHeelsWarning && !_ipcManager.CheckHeelsApi())
|
||||||
|
{
|
||||||
|
missingPluginsForData.Add("Heels");
|
||||||
|
warning.ShownHeelsWarning = true;
|
||||||
|
}
|
||||||
|
if (changes.Contains(PlayerChanges.Customize) && !warning.ShownCustomizePlusWarning && !_ipcManager.CheckCustomizePlusApi())
|
||||||
|
{
|
||||||
|
missingPluginsForData.Add("Customize+");
|
||||||
|
warning.ShownCustomizePlusWarning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changes.Contains(PlayerChanges.Palette) && !warning.ShownPalettePlusWarning && !_ipcManager.CheckPalettePlusApi())
|
||||||
|
{
|
||||||
|
missingPluginsForData.Add("Palette+");
|
||||||
|
warning.ShownPalettePlusWarning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changes.Contains(PlayerChanges.Honorific) && !warning.ShownHonorificWarning && !_ipcManager.CheckHonorificApi())
|
||||||
|
{
|
||||||
|
missingPluginsForData.Add("Honorific");
|
||||||
|
warning.ShownHonorificWarning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingPluginsForData.Any())
|
||||||
|
{
|
||||||
|
_mediator.Publish(new NotificationMessage("Missing plugins for " + playerName,
|
||||||
|
$"Received data for {playerName} that contained information for plugins you have not installed. Install {string.Join(", ", missingPluginsForData)} to experience their character fully.",
|
||||||
|
NotificationType.Warning, 10000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,133 @@
|
|||||||
using Dalamud.Game.ClientState.Objects.Types;
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
|
using MareSynchronos.API.Data;
|
||||||
|
using MareSynchronos.API.Data.Enum;
|
||||||
|
using MareSynchronos.PlayerData.Handlers;
|
||||||
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace MareSynchronos.Utils;
|
namespace MareSynchronos.Utils;
|
||||||
|
|
||||||
public static class VariousExtensions
|
public static class VariousExtensions
|
||||||
{
|
{
|
||||||
|
public static void CancelDispose(this CancellationTokenSource? cts)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cts?.Cancel();
|
||||||
|
cts?.Dispose();
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
// swallow it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CancellationTokenSource CancelRecreate(this CancellationTokenSource? cts)
|
||||||
|
{
|
||||||
|
cts.CancelDispose();
|
||||||
|
return new CancellationTokenSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<ObjectKind, HashSet<PlayerChanges>> CheckUpdatedData(this CharacterData newData, CharacterData? oldData, ILogger logger, PairHandler cachedPlayer, bool forced, bool applyLastOnVisible)
|
||||||
|
{
|
||||||
|
oldData ??= new();
|
||||||
|
var charaDataToUpdate = new Dictionary<ObjectKind, HashSet<PlayerChanges>>();
|
||||||
|
foreach (ObjectKind objectKind in Enum.GetValues<ObjectKind>())
|
||||||
|
{
|
||||||
|
charaDataToUpdate[objectKind] = new();
|
||||||
|
oldData.FileReplacements.TryGetValue(objectKind, out var existingFileReplacements);
|
||||||
|
newData.FileReplacements.TryGetValue(objectKind, out var newFileReplacements);
|
||||||
|
oldData.GlamourerData.TryGetValue(objectKind, out var existingGlamourerData);
|
||||||
|
newData.GlamourerData.TryGetValue(objectKind, out var newGlamourerData);
|
||||||
|
|
||||||
|
bool hasNewButNotOldFileReplacements = newFileReplacements != null && existingFileReplacements == null;
|
||||||
|
bool hasOldButNotNewFileReplacements = existingFileReplacements != null && newFileReplacements == null;
|
||||||
|
|
||||||
|
bool hasNewButNotOldGlamourerData = newGlamourerData != null && existingGlamourerData == null;
|
||||||
|
bool hasOldButNotNewGlamourerData = existingGlamourerData != null && newGlamourerData == null;
|
||||||
|
|
||||||
|
bool hasNewAndOldFileReplacements = newFileReplacements != null && existingFileReplacements != null;
|
||||||
|
bool hasNewAndOldGlamourerData = newGlamourerData != null && existingGlamourerData != null;
|
||||||
|
|
||||||
|
if (hasNewButNotOldFileReplacements || hasOldButNotNewFileReplacements || hasNewButNotOldGlamourerData || hasOldButNotNewGlamourerData)
|
||||||
|
{
|
||||||
|
logger.LogDebug("Updating {object}/{kind} (Some new data arrived: NewButNotOldFiles:{hasNewButNotOldFileReplacements}," +
|
||||||
|
" OldButNotNewFiles:{hasOldButNotNewFileReplacements}, NewButNotOldGlam:{hasNewButNotOldGlamourerData}, OldButNotNewGlam:{hasOldButNotNewGlamourerData}) => {change}, {change2}",
|
||||||
|
cachedPlayer, objectKind, hasNewButNotOldFileReplacements, hasOldButNotNewFileReplacements, hasNewButNotOldGlamourerData, hasOldButNotNewGlamourerData, PlayerChanges.ModFiles, PlayerChanges.Glamourer);
|
||||||
|
charaDataToUpdate[objectKind].Add(PlayerChanges.ModFiles);
|
||||||
|
charaDataToUpdate[objectKind].Add(PlayerChanges.Glamourer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (hasNewAndOldFileReplacements)
|
||||||
|
{
|
||||||
|
bool listsAreEqual = oldData.FileReplacements[objectKind].SequenceEqual(newData.FileReplacements[objectKind], PlayerData.Data.FileReplacementDataComparer.Instance);
|
||||||
|
if (!listsAreEqual || applyLastOnVisible)
|
||||||
|
{
|
||||||
|
logger.LogDebug("Updating {object}/{kind} (FileReplacements not equal) => {change}", cachedPlayer, objectKind, PlayerChanges.ModFiles);
|
||||||
|
charaDataToUpdate[objectKind].Add(PlayerChanges.ModFiles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasNewAndOldGlamourerData)
|
||||||
|
{
|
||||||
|
bool glamourerDataDifferent = !string.Equals(oldData.GlamourerData[objectKind], newData.GlamourerData[objectKind], StringComparison.Ordinal);
|
||||||
|
if (glamourerDataDifferent || forced)
|
||||||
|
{
|
||||||
|
logger.LogDebug("Updating {object}/{kind} (Glamourer different) => {change}", cachedPlayer, objectKind, PlayerChanges.Glamourer);
|
||||||
|
charaDataToUpdate[objectKind].Add(PlayerChanges.Glamourer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objectKind != ObjectKind.Player) continue;
|
||||||
|
|
||||||
|
bool manipDataDifferent = !string.Equals(oldData.ManipulationData, newData.ManipulationData, StringComparison.Ordinal);
|
||||||
|
if (manipDataDifferent || applyLastOnVisible)
|
||||||
|
{
|
||||||
|
logger.LogDebug("Updating {object}/{kind} (Diff manip data) => {change}", cachedPlayer, objectKind, PlayerChanges.ModManip);
|
||||||
|
charaDataToUpdate[objectKind].Add(PlayerChanges.ModManip);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool heelsOffsetDifferent = oldData.HeelsOffset != newData.HeelsOffset;
|
||||||
|
if (heelsOffsetDifferent || (forced && newData.HeelsOffset != 0))
|
||||||
|
{
|
||||||
|
logger.LogDebug("Updating {object}/{kind} (Diff heels data) => {change}", cachedPlayer, objectKind, PlayerChanges.Heels);
|
||||||
|
charaDataToUpdate[objectKind].Add(PlayerChanges.Heels);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool customizeDataDifferent = !string.Equals(oldData.CustomizePlusData, newData.CustomizePlusData, StringComparison.Ordinal);
|
||||||
|
if (customizeDataDifferent || (forced && !string.IsNullOrEmpty(newData.CustomizePlusData)))
|
||||||
|
{
|
||||||
|
logger.LogDebug("Updating {object}/{kind} (Diff customize data) => {change}", cachedPlayer, objectKind, PlayerChanges.Customize);
|
||||||
|
charaDataToUpdate[objectKind].Add(PlayerChanges.Customize);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool palettePlusDataDifferent = !string.Equals(oldData.PalettePlusData, newData.PalettePlusData, StringComparison.Ordinal);
|
||||||
|
if (palettePlusDataDifferent || (forced && !string.IsNullOrEmpty(newData.PalettePlusData)))
|
||||||
|
{
|
||||||
|
logger.LogDebug("Updating {object}/{kind} (Diff palette data) => {change}", cachedPlayer, objectKind, PlayerChanges.Palette);
|
||||||
|
charaDataToUpdate[objectKind].Add(PlayerChanges.Palette);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool honorificDataDifferent = !string.Equals(oldData.HonorificData, newData.HonorificData, StringComparison.Ordinal);
|
||||||
|
if (honorificDataDifferent || (forced && !string.IsNullOrEmpty(newData.HonorificData)))
|
||||||
|
{
|
||||||
|
logger.LogDebug("Updating {object}/{kind} (Diff honorific data) => {change}", cachedPlayer, objectKind, PlayerChanges.Honorific);
|
||||||
|
charaDataToUpdate[objectKind].Add(PlayerChanges.Honorific);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (KeyValuePair<ObjectKind, HashSet<PlayerChanges>> data in charaDataToUpdate.ToList())
|
||||||
|
{
|
||||||
|
if (!data.Value.Any()) charaDataToUpdate.Remove(data.Key);
|
||||||
|
else charaDataToUpdate[data.Key] = data.Value.OrderByDescending(p => (int)p).ToHashSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
return charaDataToUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
public static T DeepClone<T>(this T obj)
|
public static T DeepClone<T>(this T obj)
|
||||||
{
|
{
|
||||||
return JsonSerializer.Deserialize<T>(JsonSerializer.Serialize(obj))!;
|
return JsonSerializer.Deserialize<T>(JsonSerializer.Serialize(obj))!;
|
||||||
@@ -19,23 +142,4 @@ public static class VariousExtensions
|
|||||||
|
|
||||||
return ((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)gameObject.Address)->ObjectIndex;
|
return ((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)gameObject.Address)->ObjectIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void CancelDispose(this CancellationTokenSource? cts)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
cts?.Cancel();
|
|
||||||
cts?.Dispose();
|
|
||||||
}
|
|
||||||
catch(ObjectDisposedException)
|
|
||||||
{
|
|
||||||
// swallow it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CancellationTokenSource CancelRecreate(this CancellationTokenSource? cts)
|
|
||||||
{
|
|
||||||
cts.CancelDispose();
|
|
||||||
return new CancellationTokenSource();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user