rework character disposal
This commit is contained in:
@@ -55,7 +55,7 @@ public class PlayerDataFactory
|
|||||||
pointerIsZero = playerRelatedObject.Address == IntPtr.Zero;
|
pointerIsZero = playerRelatedObject.Address == IntPtr.Zero;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
pointerIsZero = await CheckForNullDrawObject(playerRelatedObject.Address);
|
pointerIsZero = await CheckForNullDrawObject(playerRelatedObject.Address).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -101,16 +101,6 @@ public class PlayerDataFactory
|
|||||||
previousData.GlamourerString = previousGlamourerData;
|
previousData.GlamourerString = previousGlamourerData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> CheckForNullDrawObject(IntPtr playerPointer)
|
|
||||||
{
|
|
||||||
return await _dalamudUtil.RunOnFrameworkThread(() => CheckForNullDrawObjectUnsafe(playerPointer));
|
|
||||||
}
|
|
||||||
|
|
||||||
private unsafe bool CheckForNullDrawObjectUnsafe(IntPtr playerPointer)
|
|
||||||
{
|
|
||||||
return ((Character*)playerPointer)->GameObject.DrawObject == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private unsafe void AddPlayerSpecificReplacements(Human* human, HashSet<string> forwardResolve, HashSet<string> reverseResolve)
|
private unsafe void AddPlayerSpecificReplacements(Human* human, HashSet<string> forwardResolve, HashSet<string> reverseResolve)
|
||||||
{
|
{
|
||||||
var weaponObject = (Weapon*)((Object*)human)->ChildObject;
|
var weaponObject = (Weapon*)((Object*)human)->ChildObject;
|
||||||
@@ -297,6 +287,16 @@ public class PlayerDataFactory
|
|||||||
return (forwardResolve, reverseResolve);
|
return (forwardResolve, reverseResolve);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<bool> CheckForNullDrawObject(IntPtr playerPointer)
|
||||||
|
{
|
||||||
|
return await _dalamudUtil.RunOnFrameworkThread(() => CheckForNullDrawObjectUnsafe(playerPointer)).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe bool CheckForNullDrawObjectUnsafe(IntPtr playerPointer)
|
||||||
|
{
|
||||||
|
return ((Character*)playerPointer)->GameObject.DrawObject == null;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<CharacterData> CreateCharacterData(CharacterData previousData, GameObjectHandler playerRelatedObject, CancellationToken token)
|
private async Task<CharacterData> CreateCharacterData(CharacterData previousData, GameObjectHandler playerRelatedObject, CancellationToken token)
|
||||||
{
|
{
|
||||||
var objectKind = playerRelatedObject.ObjectKind;
|
var objectKind = playerRelatedObject.ObjectKind;
|
||||||
@@ -326,7 +326,7 @@ public class PlayerDataFactory
|
|||||||
Stopwatch st = Stopwatch.StartNew();
|
Stopwatch st = Stopwatch.StartNew();
|
||||||
|
|
||||||
// gather static replacements from render model
|
// gather static replacements from render model
|
||||||
var (forwardResolve, reverseResolve) = await _dalamudUtil.RunOnFrameworkThread(() => BuildDataFromModel(objectKind, charaPointer, token));
|
var (forwardResolve, reverseResolve) = await _dalamudUtil.RunOnFrameworkThread(() => BuildDataFromModel(objectKind, charaPointer, token)).ConfigureAwait(false);
|
||||||
Dictionary<string, List<string>> resolvedPaths = await GetFileReplacementsFromPaths(forwardResolve, reverseResolve).ConfigureAwait(false);
|
Dictionary<string, List<string>> resolvedPaths = await GetFileReplacementsFromPaths(forwardResolve, reverseResolve).ConfigureAwait(false);
|
||||||
previousData.FileReplacements[objectKind] =
|
previousData.FileReplacements[objectKind] =
|
||||||
new HashSet<FileReplacement>(resolvedPaths.Select(c => new FileReplacement(c.Value, c.Key, _fileCacheManager)), FileReplacementComparer.Instance)
|
new HashSet<FileReplacement>(resolvedPaths.Select(c => new FileReplacement(c.Value, c.Key, _fileCacheManager)), FileReplacementComparer.Instance)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ 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.Handlers;
|
using MareSynchronos.PlayerData.Handlers;
|
||||||
using MareSynchronos.Services;
|
using MareSynchronos.Services;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
@@ -26,20 +27,21 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
private readonly Func<ObjectKind, Func<nint>, bool, GameObjectHandler> _gameObjectHandlerFactory;
|
private readonly Func<ObjectKind, Func<nint>, bool, GameObjectHandler> _gameObjectHandlerFactory;
|
||||||
private readonly IpcManager _ipcManager;
|
private readonly IpcManager _ipcManager;
|
||||||
private readonly IHostApplicationLifetime _lifetime;
|
private readonly IHostApplicationLifetime _lifetime;
|
||||||
|
private readonly OptionalPluginWarning _pluginWarnings;
|
||||||
private CancellationTokenSource? _applicationCancellationTokenSource = new();
|
private CancellationTokenSource? _applicationCancellationTokenSource = new();
|
||||||
private Guid _applicationId;
|
private Guid _applicationId;
|
||||||
private Task? _applicationTask;
|
private Task? _applicationTask;
|
||||||
private CharacterData _cachedData = new();
|
private CharacterData _cachedData = new();
|
||||||
private GameObjectHandler? _charaHandler;
|
private GameObjectHandler? _charaHandler;
|
||||||
private CancellationTokenSource? _downloadCancellationTokenSource = new();
|
private CancellationTokenSource? _downloadCancellationTokenSource = new();
|
||||||
|
private int _framesSinceNotVisible = 0;
|
||||||
private string _lastGlamourerData = string.Empty;
|
private string _lastGlamourerData = string.Empty;
|
||||||
private string _originalGlamourerData = string.Empty;
|
private string _originalGlamourerData = string.Empty;
|
||||||
|
|
||||||
private CancellationTokenSource _redrawCts = new();
|
private CancellationTokenSource _redrawCts = new();
|
||||||
|
|
||||||
public CachedPlayer(ILogger<CachedPlayer> logger, OnlineUserIdentDto onlineUser,
|
public CachedPlayer(ILogger<CachedPlayer> logger, OnlineUserIdentDto onlineUser,
|
||||||
Func<ObjectKind, Func<nint>, bool, GameObjectHandler> gameObjectHandlerFactory,
|
Func<ObjectKind, Func<nint>, bool, GameObjectHandler> gameObjectHandlerFactory,
|
||||||
IpcManager ipcManager, FileDownloadManager transferManager,
|
IpcManager ipcManager, FileDownloadManager transferManager, MareConfigService mareConfigService,
|
||||||
DalamudUtilService dalamudUtil, IHostApplicationLifetime lifetime, FileCacheManager fileDbManager, MareMediator mediator) : base(logger, mediator)
|
DalamudUtilService dalamudUtil, IHostApplicationLifetime lifetime, FileCacheManager fileDbManager, MareMediator mediator) : base(logger, mediator)
|
||||||
{
|
{
|
||||||
OnlineUser = onlineUser;
|
OnlineUser = onlineUser;
|
||||||
@@ -49,6 +51,14 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
_dalamudUtil = dalamudUtil;
|
_dalamudUtil = dalamudUtil;
|
||||||
_lifetime = lifetime;
|
_lifetime = lifetime;
|
||||||
_fileDbManager = fileDbManager;
|
_fileDbManager = fileDbManager;
|
||||||
|
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
|
||||||
|
_pluginWarnings ??= new()
|
||||||
|
{
|
||||||
|
ShownCustomizePlusWarning = mareConfigService.Current.DisableOptionalPluginWarnings,
|
||||||
|
ShownHeelsWarning = mareConfigService.Current.DisableOptionalPluginWarnings,
|
||||||
|
ShownPalettePlusWarning = mareConfigService.Current.DisableOptionalPluginWarnings,
|
||||||
|
ShownHonorificWarning = mareConfigService.Current.DisableOptionalPluginWarnings,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum PlayerChanges
|
private enum PlayerChanges
|
||||||
@@ -56,28 +66,34 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
Heels = 1,
|
Heels = 1,
|
||||||
Customize = 2,
|
Customize = 2,
|
||||||
Palette = 3,
|
Palette = 3,
|
||||||
Mods = 4,
|
Honorific = 4,
|
||||||
Honorific = 5,
|
Mods = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsVisible { get; private set; }
|
||||||
|
public OnlineUserIdentDto OnlineUser { get; private set; }
|
||||||
public IntPtr PlayerCharacter => _charaHandler?.Address ?? IntPtr.Zero;
|
public IntPtr PlayerCharacter => _charaHandler?.Address ?? IntPtr.Zero;
|
||||||
|
|
||||||
public unsafe uint PlayerCharacterId => (_charaHandler?.Address ?? IntPtr.Zero) == IntPtr.Zero
|
public unsafe uint PlayerCharacterId => (_charaHandler?.Address ?? IntPtr.Zero) == IntPtr.Zero
|
||||||
? uint.MaxValue
|
? uint.MaxValue
|
||||||
: ((GameObject*)_charaHandler.Address)->ObjectID;
|
: ((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;
|
||||||
private OnlineUserIdentDto OnlineUser { get; set; }
|
public uint? PlayerWorld { get; private set; }
|
||||||
|
|
||||||
public void ApplyCharacterData(CharacterData characterData, OptionalPluginWarning warning, bool forced = false)
|
public void ApplyCharacterData(CharacterData characterData, bool forced = false)
|
||||||
{
|
{
|
||||||
|
if (_charaHandler == null)
|
||||||
|
{
|
||||||
|
_cachedData = characterData;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SetUploading(false);
|
SetUploading(false);
|
||||||
|
|
||||||
Logger.LogDebug("Received data for {player}", this);
|
Logger.LogDebug("Received data for {player}", this);
|
||||||
|
Logger.LogDebug("Hash for data is {newHash}, current cache hash is {oldHash}", characterData.DataHash.Value, _cachedData.DataHash.Value);
|
||||||
|
|
||||||
Logger.LogDebug("Checking for files to download for player {name}", this);
|
Logger.LogDebug("Checking for files to download for player {name}", this);
|
||||||
Logger.LogDebug("Hash for data is {newHash}, current cache hash is {oldHash}", characterData.DataHash.Value, _cachedData.DataHash.Value);
|
|
||||||
|
|
||||||
if (!_ipcManager.CheckPenumbraApi()) return;
|
if (!_ipcManager.CheckPenumbraApi()) return;
|
||||||
if (!_ipcManager.CheckGlamourerApi()) return;
|
if (!_ipcManager.CheckGlamourerApi()) return;
|
||||||
@@ -94,7 +110,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
if (charaDataToUpdate.TryGetValue(ObjectKind.Player, out var playerChanges))
|
if (charaDataToUpdate.TryGetValue(ObjectKind.Player, out var playerChanges))
|
||||||
{
|
{
|
||||||
NotifyForMissingPlugins(playerChanges, warning);
|
NotifyForMissingPlugins(playerChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogDebug("Downloading and applying character for {name}", this);
|
Logger.LogDebug("Downloading and applying character for {name}", this);
|
||||||
@@ -116,28 +132,6 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Initialize(string name)
|
|
||||||
{
|
|
||||||
PlayerName = name;
|
|
||||||
_charaHandler = _gameObjectHandlerFactory(ObjectKind.Player, () => _dalamudUtil.GetPlayerCharacterFromObjectTableByName(PlayerName), false);
|
|
||||||
|
|
||||||
_originalGlamourerData = await _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter).ConfigureAwait(false);
|
|
||||||
_lastGlamourerData = _originalGlamourerData;
|
|
||||||
Mediator.Subscribe<PenumbraRedrawMessage>(this, IpcManagerOnPenumbraRedrawEvent);
|
|
||||||
Mediator.Subscribe<CharacterChangedMessage>(this, async (msg) =>
|
|
||||||
{
|
|
||||||
if (msg.GameObjectHandler == _charaHandler && (_applicationTask?.IsCompleted ?? true))
|
|
||||||
{
|
|
||||||
Logger.LogTrace("Saving new Glamourer Data for {this}", this);
|
|
||||||
_lastGlamourerData = await _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_downloadManager.Initialize();
|
|
||||||
|
|
||||||
Logger.LogDebug("Initializing Player {obj}", this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return OnlineUser == null
|
return OnlineUser == null
|
||||||
@@ -163,6 +157,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
SetUploading(false);
|
SetUploading(false);
|
||||||
_downloadManager.Dispose();
|
_downloadManager.Dispose();
|
||||||
var name = PlayerName;
|
var name = PlayerName;
|
||||||
|
var world = PlayerWorld;
|
||||||
Logger.LogDebug("Disposing {name} ({user})", name, OnlineUser);
|
Logger.LogDebug("Disposing {name} ({user})", name, OnlineUser);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -185,7 +180,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
RevertCustomizationData(item.Key, name, applicationId).GetAwaiter().GetResult();
|
RevertCustomizationData(item.Key, name, world ?? 0, applicationId).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
catch (InvalidOperationException ex)
|
catch (InvalidOperationException ex)
|
||||||
{
|
{
|
||||||
@@ -414,7 +409,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
Logger.LogDebug("Downloading missing files for player {name}, {kind}", PlayerName, updatedData);
|
Logger.LogDebug("Downloading missing files for player {name}, {kind}", PlayerName, updatedData);
|
||||||
if (toDownloadReplacements.Any())
|
if (toDownloadReplacements.Any())
|
||||||
{
|
{
|
||||||
await _downloadManager.DownloadFiles(_charaHandler, toDownloadReplacements, downloadToken).ConfigureAwait(false);
|
await _downloadManager.DownloadFiles(_charaHandler!, toDownloadReplacements, downloadToken).ConfigureAwait(false);
|
||||||
_downloadManager.CancelDownload();
|
_downloadManager.CancelDownload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,6 +481,61 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
}, downloadToken);
|
}, downloadToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void FrameworkUpdate()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(PlayerName))
|
||||||
|
{
|
||||||
|
var pc = _dalamudUtil.FindPlayerByNameHash(OnlineUser.Ident);
|
||||||
|
if (pc == null) return;
|
||||||
|
Logger.LogDebug("One-Time Initializing {this}", this);
|
||||||
|
Initialize(pc.Name.ToString(), pc.HomeWorld.Id);
|
||||||
|
Logger.LogDebug("One-Time Initialized {this}", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_charaHandler?.Address != IntPtr.Zero && !IsVisible)
|
||||||
|
{
|
||||||
|
IsVisible = true;
|
||||||
|
Mediator.Publish(new CachedPlayerVisibleMessage(this));
|
||||||
|
Logger.LogTrace("{this} visibility changed, now: {visi}", this, IsVisible);
|
||||||
|
_framesSinceNotVisible = 0;
|
||||||
|
_lastGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter).GetAwaiter().GetResult();
|
||||||
|
if (_cachedData != null)
|
||||||
|
{
|
||||||
|
Task.Run(() => ApplyCharacterData(_cachedData, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_charaHandler?.Address == IntPtr.Zero && IsVisible)
|
||||||
|
{
|
||||||
|
_framesSinceNotVisible++;
|
||||||
|
if (_framesSinceNotVisible > 30)
|
||||||
|
{
|
||||||
|
IsVisible = false;
|
||||||
|
Logger.LogTrace("{this} visibility changed, now: {visi}", this, IsVisible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Initialize(string name, uint worldid)
|
||||||
|
{
|
||||||
|
PlayerName = name;
|
||||||
|
PlayerWorld = worldid;
|
||||||
|
_charaHandler = _gameObjectHandlerFactory(ObjectKind.Player, () => _dalamudUtil.GetPlayerCharacterFromObjectTableByName(PlayerName, worldid), false);
|
||||||
|
|
||||||
|
_originalGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||||
|
_lastGlamourerData = _originalGlamourerData;
|
||||||
|
Mediator.Subscribe<PenumbraRedrawMessage>(this, IpcManagerOnPenumbraRedrawEvent);
|
||||||
|
Mediator.Subscribe<CharacterChangedMessage>(this, async (msg) =>
|
||||||
|
{
|
||||||
|
if (msg.GameObjectHandler == _charaHandler && (_applicationTask?.IsCompleted ?? true))
|
||||||
|
{
|
||||||
|
Logger.LogTrace("Saving new Glamourer Data for {this}", this);
|
||||||
|
_lastGlamourerData = await _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_downloadManager.Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
private void IpcManagerOnPenumbraRedrawEvent(PenumbraRedrawMessage msg)
|
private void IpcManagerOnPenumbraRedrawEvent(PenumbraRedrawMessage msg)
|
||||||
{
|
{
|
||||||
var player = _dalamudUtil.GetCharacterFromObjectTableByIndex(msg.ObjTblIdx);
|
var player = _dalamudUtil.GetCharacterFromObjectTableByIndex(msg.ObjTblIdx);
|
||||||
@@ -505,30 +555,30 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
}, token);
|
}, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NotifyForMissingPlugins(HashSet<PlayerChanges> changes, OptionalPluginWarning warning)
|
private void NotifyForMissingPlugins(HashSet<PlayerChanges> changes)
|
||||||
{
|
{
|
||||||
List<string> missingPluginsForData = new();
|
List<string> missingPluginsForData = new();
|
||||||
if (changes.Contains(PlayerChanges.Heels) && !warning.ShownHeelsWarning && !_ipcManager.CheckHeelsApi())
|
if (changes.Contains(PlayerChanges.Heels) && !_pluginWarnings.ShownHeelsWarning && !_ipcManager.CheckHeelsApi())
|
||||||
{
|
{
|
||||||
missingPluginsForData.Add("Heels");
|
missingPluginsForData.Add("Heels");
|
||||||
warning.ShownHeelsWarning = true;
|
_pluginWarnings.ShownHeelsWarning = true;
|
||||||
}
|
}
|
||||||
if (changes.Contains(PlayerChanges.Customize) && !warning.ShownCustomizePlusWarning && !_ipcManager.CheckCustomizePlusApi())
|
if (changes.Contains(PlayerChanges.Customize) && !_pluginWarnings.ShownCustomizePlusWarning && !_ipcManager.CheckCustomizePlusApi())
|
||||||
{
|
{
|
||||||
missingPluginsForData.Add("Customize+");
|
missingPluginsForData.Add("Customize+");
|
||||||
warning.ShownCustomizePlusWarning = true;
|
_pluginWarnings.ShownCustomizePlusWarning = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changes.Contains(PlayerChanges.Palette) && !warning.ShownPalettePlusWarning && !_ipcManager.CheckPalettePlusApi())
|
if (changes.Contains(PlayerChanges.Palette) && !_pluginWarnings.ShownPalettePlusWarning && !_ipcManager.CheckPalettePlusApi())
|
||||||
{
|
{
|
||||||
missingPluginsForData.Add("Palette+");
|
missingPluginsForData.Add("Palette+");
|
||||||
warning.ShownPalettePlusWarning = true;
|
_pluginWarnings.ShownPalettePlusWarning = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changes.Contains(PlayerChanges.Honorific) && !warning.ShownHonorificWarning && !_ipcManager.CheckHonorificApi())
|
if (changes.Contains(PlayerChanges.Honorific) && !_pluginWarnings.ShownHonorificWarning && !_ipcManager.CheckHonorificApi())
|
||||||
{
|
{
|
||||||
missingPluginsForData.Add("Honorific");
|
missingPluginsForData.Add("Honorific");
|
||||||
warning.ShownHonorificWarning = true;
|
_pluginWarnings.ShownHonorificWarning = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (missingPluginsForData.Any())
|
if (missingPluginsForData.Any())
|
||||||
@@ -539,9 +589,9 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RevertCustomizationData(ObjectKind objectKind, string name, Guid applicationId)
|
private async Task RevertCustomizationData(ObjectKind objectKind, string name, uint world, Guid applicationId)
|
||||||
{
|
{
|
||||||
nint address = _dalamudUtil.GetPlayerCharacterFromObjectTableByName(name);
|
nint address = _dalamudUtil.GetPlayerCharacterFromObjectTableByName(name, world);
|
||||||
if (address == IntPtr.Zero) return;
|
if (address == IntPtr.Zero) return;
|
||||||
|
|
||||||
var cancelToken = new CancellationTokenSource();
|
var cancelToken = new CancellationTokenSource();
|
||||||
|
|||||||
@@ -13,6 +13,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 PairManager _pairManager;
|
private readonly PairManager _pairManager;
|
||||||
private CharacterData? _lastSentData;
|
private CharacterData? _lastSentData;
|
||||||
|
|
||||||
@@ -39,22 +40,18 @@ 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FrameworkOnUpdate()
|
private void FrameworkOnUpdate()
|
||||||
{
|
{
|
||||||
if (!_dalamudUtil.IsPlayerPresent || !_apiController.IsConnected) return;
|
if (!_dalamudUtil.IsPlayerPresent || !_apiController.IsConnected) return;
|
||||||
|
|
||||||
var playerCharacters = _dalamudUtil.GetPlayerCharacters();
|
if (!_newVisiblePlayers.Any()) return;
|
||||||
var chars = _pairManager.FindAllPairs(playerCharacters);
|
var newVisiblePlayers = _newVisiblePlayers.ToList();
|
||||||
var newVisiblePlayers = (from pChar in chars.Where(p => p.Pair.InitializePair(p.Character.Name.ToString()))
|
_newVisiblePlayers.Clear();
|
||||||
select pChar.Pair.UserData).ToList();
|
Logger.LogTrace("Has new visible players, pushing character data");
|
||||||
|
PushCharacterData(newVisiblePlayers.Select(c => c.OnlineUser.User).ToList());
|
||||||
if (newVisiblePlayers.Any())
|
|
||||||
{
|
|
||||||
Logger.LogTrace("Has new visible players, pushing character data");
|
|
||||||
PushCharacterData(newVisiblePlayers);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PlayerManagerOnPlayerHasChanged()
|
private void PlayerManagerOnPlayerHasChanged()
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
using Dalamud.ContextMenu;
|
using Dalamud.ContextMenu;
|
||||||
using Dalamud.Utility;
|
|
||||||
using MareSynchronos.API.Data;
|
using MareSynchronos.API.Data;
|
||||||
using MareSynchronos.API.Data.Comparer;
|
using MareSynchronos.API.Data.Comparer;
|
||||||
using MareSynchronos.API.Data.Extensions;
|
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.MareConfiguration;
|
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.Services.ServerConfiguration;
|
using MareSynchronos.Services.ServerConfiguration;
|
||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
@@ -16,20 +14,17 @@ namespace MareSynchronos.PlayerData.Pairs;
|
|||||||
public class Pair
|
public class Pair
|
||||||
{
|
{
|
||||||
private readonly Func<OnlineUserIdentDto, CachedPlayer> _cachedPlayerFactory;
|
private readonly Func<OnlineUserIdentDto, CachedPlayer> _cachedPlayerFactory;
|
||||||
private readonly MareConfigService _configService;
|
|
||||||
private readonly ILogger<Pair> _logger;
|
private readonly ILogger<Pair> _logger;
|
||||||
private readonly MareMediator _mediator;
|
private readonly MareMediator _mediator;
|
||||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||||
private OnlineUserIdentDto? _onlineUserIdentDto = null;
|
private OnlineUserIdentDto? _onlineUserIdentDto = null;
|
||||||
private OptionalPluginWarning? _pluginWarnings;
|
|
||||||
|
|
||||||
public Pair(ILogger<Pair> logger, Func<OnlineUserIdentDto, CachedPlayer> cachedPlayerFactory,
|
public Pair(ILogger<Pair> logger, Func<OnlineUserIdentDto, CachedPlayer> cachedPlayerFactory,
|
||||||
MareMediator mediator, MareConfigService configService, ServerConfigurationManager serverConfigurationManager)
|
MareMediator mediator, ServerConfigurationManager serverConfigurationManager)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_cachedPlayerFactory = cachedPlayerFactory;
|
_cachedPlayerFactory = cachedPlayerFactory;
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
_configService = configService;
|
|
||||||
_serverConfigurationManager = serverConfigurationManager;
|
_serverConfigurationManager = serverConfigurationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +36,7 @@ public class Pair
|
|||||||
public bool IsPaused => UserPair != null && UserPair.OtherPermissions.IsPaired() ? UserPair.OtherPermissions.IsPaused() || UserPair.OwnPermissions.IsPaused()
|
public bool IsPaused => UserPair != null && UserPair.OtherPermissions.IsPaired() ? UserPair.OtherPermissions.IsPaused() || UserPair.OwnPermissions.IsPaused()
|
||||||
: GroupPair.All(p => p.Key.GroupUserPermissions.IsPaused() || p.Value.GroupUserPermissions.IsPaused());
|
: GroupPair.All(p => p.Key.GroupUserPermissions.IsPaused() || p.Value.GroupUserPermissions.IsPaused());
|
||||||
|
|
||||||
public bool IsVisible => CachedPlayer?.PlayerName != null;
|
public bool IsVisible => CachedPlayer?.IsVisible ?? false;
|
||||||
public CharacterData? LastReceivedCharacterData { get; set; }
|
public CharacterData? LastReceivedCharacterData { get; set; }
|
||||||
public string? PlayerName => CachedPlayer?.PlayerName ?? string.Empty;
|
public string? PlayerName => CachedPlayer?.PlayerName ?? string.Empty;
|
||||||
|
|
||||||
@@ -79,8 +74,6 @@ public class Pair
|
|||||||
{
|
{
|
||||||
if (CachedPlayer == null) throw new InvalidOperationException("CachedPlayer not initialized");
|
if (CachedPlayer == null) throw new InvalidOperationException("CachedPlayer not initialized");
|
||||||
|
|
||||||
if (string.Equals(LastReceivedCharacterData?.DataHash.Value, data.CharaData.DataHash.Value, StringComparison.Ordinal)) return;
|
|
||||||
|
|
||||||
LastReceivedCharacterData = data.CharaData;
|
LastReceivedCharacterData = data.CharaData;
|
||||||
|
|
||||||
ApplyLastReceivedData();
|
ApplyLastReceivedData();
|
||||||
@@ -91,71 +84,10 @@ public class Pair
|
|||||||
if (CachedPlayer == null) return;
|
if (CachedPlayer == null) return;
|
||||||
if (LastReceivedCharacterData == null) return;
|
if (LastReceivedCharacterData == null) return;
|
||||||
|
|
||||||
_pluginWarnings ??= new()
|
CachedPlayer.ApplyCharacterData(RemoveNotSyncedFiles(LastReceivedCharacterData.DeepClone())!, forced);
|
||||||
{
|
|
||||||
ShownCustomizePlusWarning = _configService.Current.DisableOptionalPluginWarnings,
|
|
||||||
ShownHeelsWarning = _configService.Current.DisableOptionalPluginWarnings,
|
|
||||||
ShownPalettePlusWarning = _configService.Current.DisableOptionalPluginWarnings,
|
|
||||||
ShownHonorificWarning = _configService.Current.DisableOptionalPluginWarnings,
|
|
||||||
};
|
|
||||||
|
|
||||||
CachedPlayer.ApplyCharacterData(RemoveNotSyncedFiles(LastReceivedCharacterData.DeepClone())!, _pluginWarnings, forced);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string? GetNote()
|
public void CreateCachedPlayer(OnlineUserIdentDto? dto = null)
|
||||||
{
|
|
||||||
return _serverConfigurationManager.GetNoteForUid(UserData.UID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetPlayerNameHash()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return CachedPlayer?.PlayerNameHash ?? string.Empty;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "Error accessing PlayerNameHash, recreating CachedPlayer");
|
|
||||||
RecreateCachedPlayer();
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasAnyConnection()
|
|
||||||
{
|
|
||||||
return UserPair != null || GroupPair.Any();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool InitializePair(string name)
|
|
||||||
{
|
|
||||||
if (!PlayerName.IsNullOrEmpty()) return false;
|
|
||||||
|
|
||||||
if (CachedPlayer == null) throw new InvalidOperationException("CachedPlayer not initialized");
|
|
||||||
_pluginWarnings ??= new()
|
|
||||||
{
|
|
||||||
ShownCustomizePlusWarning = _configService.Current.DisableOptionalPluginWarnings,
|
|
||||||
ShownHeelsWarning = _configService.Current.DisableOptionalPluginWarnings,
|
|
||||||
ShownPalettePlusWarning = _configService.Current.DisableOptionalPluginWarnings,
|
|
||||||
ShownHonorificWarning = _configService.Current.DisableOptionalPluginWarnings,
|
|
||||||
};
|
|
||||||
|
|
||||||
CachedPlayer.Initialize(name).Wait();
|
|
||||||
|
|
||||||
ApplyLastReceivedData();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void MarkOffline()
|
|
||||||
{
|
|
||||||
_onlineUserIdentDto = null;
|
|
||||||
LastReceivedCharacterData = null;
|
|
||||||
CachedPlayer?.Dispose();
|
|
||||||
CachedPlayer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecreateCachedPlayer(OnlineUserIdentDto? dto = null)
|
|
||||||
{
|
{
|
||||||
if (dto == null && _onlineUserIdentDto == null)
|
if (dto == null && _onlineUserIdentDto == null)
|
||||||
{
|
{
|
||||||
@@ -172,6 +104,29 @@ public class Pair
|
|||||||
CachedPlayer = _cachedPlayerFactory(_onlineUserIdentDto!);
|
CachedPlayer = _cachedPlayerFactory(_onlineUserIdentDto!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string? GetNote()
|
||||||
|
{
|
||||||
|
return _serverConfigurationManager.GetNoteForUid(UserData.UID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetPlayerNameHash()
|
||||||
|
{
|
||||||
|
return CachedPlayer?.PlayerNameHash ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasAnyConnection()
|
||||||
|
{
|
||||||
|
return UserPair != null || GroupPair.Any();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MarkOffline()
|
||||||
|
{
|
||||||
|
_onlineUserIdentDto = null;
|
||||||
|
LastReceivedCharacterData = null;
|
||||||
|
CachedPlayer?.Dispose();
|
||||||
|
CachedPlayer = null;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetNote(string note)
|
public void SetNote(string note)
|
||||||
{
|
{
|
||||||
_serverConfigurationManager.SetNoteForUid(UserData.UID, note);
|
_serverConfigurationManager.SetNoteForUid(UserData.UID, note);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Dalamud.ContextMenu;
|
using Dalamud.ContextMenu;
|
||||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||||
using Dalamud.Interface.Internal.Notifications;
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
using Dalamud.Utility;
|
|
||||||
using MareSynchronos.API.Data;
|
using MareSynchronos.API.Data;
|
||||||
using MareSynchronos.API.Data.Comparer;
|
using MareSynchronos.API.Data.Comparer;
|
||||||
using MareSynchronos.API.Data.Extensions;
|
using MareSynchronos.API.Data.Extensions;
|
||||||
@@ -33,7 +32,6 @@ public sealed class PairManager : DisposableMediatorSubscriberBase
|
|||||||
_pairFactory = pairFactory;
|
_pairFactory = pairFactory;
|
||||||
_configurationService = configurationService;
|
_configurationService = configurationService;
|
||||||
_dalamudContextMenu = dalamudContextMenu;
|
_dalamudContextMenu = dalamudContextMenu;
|
||||||
Mediator.Subscribe<ZoneSwitchStartMessage>(this, (_) => DalamudUtilOnZoneSwitched());
|
|
||||||
Mediator.Subscribe<DelayedFrameworkUpdateMessage>(this, (_) => DalamudUtilOnDelayedFrameworkUpdate());
|
Mediator.Subscribe<DelayedFrameworkUpdateMessage>(this, (_) => DalamudUtilOnDelayedFrameworkUpdate());
|
||||||
Mediator.Subscribe<DisconnectedMessage>(this, (_) => ClearPairs());
|
Mediator.Subscribe<DisconnectedMessage>(this, (_) => ClearPairs());
|
||||||
Mediator.Subscribe<CutsceneEndMessage>(this, (_) => ReapplyPairData());
|
Mediator.Subscribe<CutsceneEndMessage>(this, (_) => ReapplyPairData());
|
||||||
@@ -105,7 +103,7 @@ public sealed class PairManager : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
public List<Pair> GetOnlineUserPairs() => _allClientPairs.Where(p => !string.IsNullOrEmpty(p.Value.GetPlayerNameHash())).Select(p => p.Value).ToList();
|
public List<Pair> GetOnlineUserPairs() => _allClientPairs.Where(p => !string.IsNullOrEmpty(p.Value.GetPlayerNameHash())).Select(p => p.Value).ToList();
|
||||||
|
|
||||||
public List<UserData> GetVisibleUsers() => _allClientPairs.Where(p => p.Value.HasCachedPlayer).Select(p => p.Key).ToList();
|
public List<UserData> GetVisibleUsers() => _allClientPairs.Where(p => p.Value.IsVisible).Select(p => p.Key).ToList();
|
||||||
|
|
||||||
public void MarkPairOffline(UserData user)
|
public void MarkPairOffline(UserData user)
|
||||||
{
|
{
|
||||||
@@ -139,7 +137,7 @@ public sealed class PairManager : DisposableMediatorSubscriberBase
|
|||||||
Mediator.Publish(new NotificationMessage("User online", msg, NotificationType.Info, 5000));
|
Mediator.Publish(new NotificationMessage("User online", msg, NotificationType.Info, 5000));
|
||||||
}
|
}
|
||||||
|
|
||||||
pair.RecreateCachedPlayer(dto);
|
pair.CreateCachedPlayer(dto);
|
||||||
RecreateLazy();
|
RecreateLazy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,15 +145,7 @@ public sealed class PairManager : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
if (!_allClientPairs.ContainsKey(dto.User)) throw new InvalidOperationException("No user found for " + dto.User);
|
if (!_allClientPairs.ContainsKey(dto.User)) throw new InvalidOperationException("No user found for " + dto.User);
|
||||||
|
|
||||||
var pair = _allClientPairs[dto.User];
|
_allClientPairs[dto.User].ApplyData(dto);
|
||||||
if (!pair.PlayerName.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
pair.ApplyData(dto);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_allClientPairs[dto.User].LastReceivedCharacterData = dto.CharaData;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveGroup(GroupData data)
|
public void RemoveGroup(GroupData data)
|
||||||
@@ -360,32 +350,16 @@ public sealed class PairManager : DisposableMediatorSubscriberBase
|
|||||||
if (string.IsNullOrEmpty(hash)) continue;
|
if (string.IsNullOrEmpty(hash)) continue;
|
||||||
_indexedPairs[hash] = pair;
|
_indexedPairs[hash] = pair;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Pair pair in _allClientPairs.Select(p => p.Value).Where(p => p.HasCachedPlayer).ToList())
|
|
||||||
{
|
|
||||||
if (!pair.CachedPlayerExists)
|
|
||||||
{
|
|
||||||
pair.RecreateCachedPlayer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DalamudUtilOnZoneSwitched()
|
|
||||||
{
|
|
||||||
DisposePairs(recreate: true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Lazy<List<Pair>> DirectPairsLazy() => new(() => _allClientPairs.Select(k => k.Value).Where(k => k.UserPair != null).ToList());
|
private Lazy<List<Pair>> DirectPairsLazy() => new(() => _allClientPairs.Select(k => k.Value).Where(k => k.UserPair != null).ToList());
|
||||||
|
|
||||||
private void DisposePairs(bool recreate = false)
|
private void DisposePairs()
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Disposing all Pairs");
|
Logger.LogDebug("Disposing all Pairs");
|
||||||
Parallel.ForEach(_allClientPairs, item =>
|
Parallel.ForEach(_allClientPairs, item =>
|
||||||
{
|
{
|
||||||
if (recreate)
|
item.Value.MarkOffline();
|
||||||
item.Value.RecreateCachedPlayer();
|
|
||||||
else
|
|
||||||
item.Value.MarkOffline();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
RecreateLazy();
|
RecreateLazy();
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
s.GetRequiredService<Func<ObjectKind, Func<nint>, bool, GameObjectHandler>>(),
|
s.GetRequiredService<Func<ObjectKind, Func<nint>, bool, GameObjectHandler>>(),
|
||||||
s.GetRequiredService<IpcManager>(),
|
s.GetRequiredService<IpcManager>(),
|
||||||
s.GetRequiredService<Func<FileDownloadManager>>().Invoke(),
|
s.GetRequiredService<Func<FileDownloadManager>>().Invoke(),
|
||||||
|
s.GetRequiredService<MareConfigService>(),
|
||||||
s.GetRequiredService<DalamudUtilService>(),
|
s.GetRequiredService<DalamudUtilService>(),
|
||||||
s.GetRequiredService<IHostApplicationLifetime>(),
|
s.GetRequiredService<IHostApplicationLifetime>(),
|
||||||
s.GetRequiredService<FileCacheManager>(),
|
s.GetRequiredService<FileCacheManager>(),
|
||||||
@@ -107,7 +108,6 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
=> new Pair(s.GetRequiredService<ILogger<Pair>>(),
|
=> new Pair(s.GetRequiredService<ILogger<Pair>>(),
|
||||||
s.GetRequiredService<Func<OnlineUserIdentDto, CachedPlayer>>(),
|
s.GetRequiredService<Func<OnlineUserIdentDto, CachedPlayer>>(),
|
||||||
s.GetRequiredService<MareMediator>(),
|
s.GetRequiredService<MareMediator>(),
|
||||||
s.GetRequiredService<MareConfigService>(),
|
|
||||||
s.GetRequiredService<ServerConfigurationManager>())));
|
s.GetRequiredService<ServerConfigurationManager>())));
|
||||||
collection.AddSingleton(s =>
|
collection.AddSingleton(s =>
|
||||||
new Func<FileDownloadManager>(()
|
new Func<FileDownloadManager>(()
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ using MareSynchronos.Services.Mediator;
|
|||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Globalization;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
|
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
|
||||||
@@ -112,9 +113,9 @@ public class DalamudUtilService : IHostedService
|
|||||||
return await RunOnFrameworkThread(() => GetPetInternal(playerPointer)).ConfigureAwait(false);
|
return await RunOnFrameworkThread(() => GetPetInternal(playerPointer)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IntPtr GetPlayerCharacterFromObjectTableByName(string characterName)
|
public IntPtr GetPlayerCharacterFromObjectTableByName(string characterName, uint worldid)
|
||||||
{
|
{
|
||||||
if (_playerCharas.TryGetValue(characterName, out var pchar)) return pchar;
|
if (_playerCharas.TryGetValue(characterName + worldid.ToString(CultureInfo.InvariantCulture), out var pchar)) return pchar;
|
||||||
return IntPtr.Zero;
|
return IntPtr.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,8 +134,6 @@ public class DalamudUtilService : IHostedService
|
|||||||
{
|
{
|
||||||
if (!_framework.IsInFrameworkUpdateThread)
|
if (!_framework.IsInFrameworkUpdateThread)
|
||||||
{
|
{
|
||||||
//_logger.LogTrace("Running Action on framework thread (FrameworkContext: {ctx}): {member} in {file}:{line}", _framework.IsInFrameworkUpdateThread, callerMember, callerFilePath, lineNumber);
|
|
||||||
|
|
||||||
await _framework.RunOnFrameworkThread(act).ContinueWith((_) => Task.CompletedTask).ConfigureAwait(false);
|
await _framework.RunOnFrameworkThread(act).ContinueWith((_) => Task.CompletedTask).ConfigureAwait(false);
|
||||||
while (_framework.IsInFrameworkUpdateThread) // yield the thread again, should technically never be triggered
|
while (_framework.IsInFrameworkUpdateThread) // yield the thread again, should technically never be triggered
|
||||||
{
|
{
|
||||||
@@ -151,8 +150,6 @@ public class DalamudUtilService : IHostedService
|
|||||||
{
|
{
|
||||||
if (!_framework.IsInFrameworkUpdateThread)
|
if (!_framework.IsInFrameworkUpdateThread)
|
||||||
{
|
{
|
||||||
//_logger.LogTrace("Running Func on framework thread (FrameworkContext: {ctx}): {member} in {file}:{line}", _framework.IsInFrameworkUpdateThread, callerMember, callerFilePath, lineNumber);
|
|
||||||
|
|
||||||
var result = await _framework.RunOnFrameworkThread(func).ContinueWith((task) => task.Result).ConfigureAwait(false);
|
var result = await _framework.RunOnFrameworkThread(func).ContinueWith((task) => task.Result).ConfigureAwait(false);
|
||||||
while (_framework.IsInFrameworkUpdateThread) // yield the thread again, should technically never be triggered
|
while (_framework.IsInFrameworkUpdateThread) // yield the thread again, should technically never be triggered
|
||||||
{
|
{
|
||||||
@@ -239,6 +236,11 @@ public class DalamudUtilService : IHostedService
|
|||||||
return _gameGui.WorldToScreen(obj.Position, out var screenPos) ? screenPos : Vector2.Zero;
|
return _gameGui.WorldToScreen(obj.Position, out var screenPos) ? screenPos : Vector2.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal PlayerCharacter? FindPlayerByNameHash(string ident)
|
||||||
|
{
|
||||||
|
return _objectTable.OfType<PlayerCharacter>().FirstOrDefault(p => p.GetHash256().Equals(ident, StringComparison.Ordinal));
|
||||||
|
}
|
||||||
|
|
||||||
private void FrameworkOnUpdate(Framework framework)
|
private void FrameworkOnUpdate(Framework framework)
|
||||||
{
|
{
|
||||||
_performanceCollector.LogPerformance(this, "FrameworkOnUpdate", FrameworkOnUpdateInternal);
|
_performanceCollector.LogPerformance(this, "FrameworkOnUpdate", FrameworkOnUpdateInternal);
|
||||||
@@ -248,7 +250,7 @@ public class DalamudUtilService : IHostedService
|
|||||||
{
|
{
|
||||||
if (_clientState.LocalPlayer?.IsDead ?? false) return;
|
if (_clientState.LocalPlayer?.IsDead ?? false) return;
|
||||||
|
|
||||||
_playerCharas = _objectTable.OfType<PlayerCharacter>().ToDictionary(p => p.Name.ToString(), p => p.Address, StringComparer.Ordinal);
|
_playerCharas = _objectTable.OfType<PlayerCharacter>().ToDictionary(p => p.Name.ToString() + p.HomeWorld.Id.ToString(), p => p.Address, StringComparer.Ordinal);
|
||||||
|
|
||||||
if (GposeTarget != null && !IsInGpose)
|
if (GposeTarget != null && !IsInGpose)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -135,33 +135,32 @@ public sealed class MareMediator : IHostedService
|
|||||||
{
|
{
|
||||||
subscribersCopy = subscribers?.Where(s => s.Subscriber != null).ToHashSet() ?? new HashSet<SubscriberAction>();
|
subscribersCopy = subscribers?.Where(s => s.Subscriber != null).ToHashSet() ?? new HashSet<SubscriberAction>();
|
||||||
}
|
}
|
||||||
_performanceCollector.LogPerformance(this, $"Execute>{message.GetType().Name}", () =>
|
|
||||||
{
|
|
||||||
foreach (SubscriberAction subscriber in subscribersCopy)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
typeof(MareMediator)
|
|
||||||
.GetMethod(nameof(ExecuteSubscriber), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?
|
|
||||||
.MakeGenericMethod(message.GetType())
|
|
||||||
.Invoke(this, new object[] { subscriber, message });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (_lastErrorTime.TryGetValue(subscriber, out var lastErrorTime) && lastErrorTime.Add(TimeSpan.FromSeconds(10)) > DateTime.UtcNow)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
_logger.LogCritical(ex, "Error executing {type} for subscriber {subscriber}", message.GetType().Name, subscriber.Subscriber.GetType().Name);
|
foreach (SubscriberAction subscriber in subscribersCopy)
|
||||||
_lastErrorTime[subscriber] = DateTime.UtcNow;
|
{
|
||||||
}
|
try
|
||||||
|
{
|
||||||
|
typeof(MareMediator)
|
||||||
|
.GetMethod(nameof(ExecuteSubscriber), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?
|
||||||
|
.MakeGenericMethod(message.GetType())
|
||||||
|
.Invoke(this, new object[] { subscriber, message });
|
||||||
}
|
}
|
||||||
});
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (_lastErrorTime.TryGetValue(subscriber, out var lastErrorTime) && lastErrorTime.Add(TimeSpan.FromSeconds(10)) > DateTime.UtcNow)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
_logger.LogCritical(ex, "Error executing {type} for subscriber {subscriber}", message.GetType().Name, subscriber.Subscriber.GetType().Name);
|
||||||
|
_lastErrorTime[subscriber] = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecuteSubscriber<T>(SubscriberAction subscriber, T message) where T : MessageBase
|
private void ExecuteSubscriber<T>(SubscriberAction subscriber, T message) where T : MessageBase
|
||||||
{
|
{
|
||||||
_performanceCollector.LogPerformance(this, $"Publish>{message.GetType().Name}+{subscriber.Subscriber.GetType().Name}", () => ((Action<T>)subscriber.Action).Invoke(message));
|
var isSameThread = message.KeepThreadContext ? "$" : string.Empty;
|
||||||
|
_performanceCollector.LogPerformance(this, $"{isSameThread}Execute>{message.GetType().Name}+{subscriber.Subscriber.GetType().Name}", () => ((Action<T>)subscriber.Action).Invoke(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class SubscriberAction
|
private sealed class SubscriberAction
|
||||||
|
|||||||
@@ -4,3 +4,8 @@ public abstract record MessageBase
|
|||||||
{
|
{
|
||||||
public virtual bool KeepThreadContext => false;
|
public virtual bool KeepThreadContext => false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record SameThreadMessage : MessageBase
|
||||||
|
{
|
||||||
|
public override bool KeepThreadContext => true;
|
||||||
|
}
|
||||||
@@ -15,36 +15,24 @@ public record SwitchToMainUiMessage : MessageBase;
|
|||||||
public record OpenSettingsUiMessage : MessageBase;
|
public record OpenSettingsUiMessage : MessageBase;
|
||||||
public record DalamudLoginMessage : MessageBase;
|
public record DalamudLoginMessage : MessageBase;
|
||||||
public record DalamudLogoutMessage : MessageBase;
|
public record DalamudLogoutMessage : MessageBase;
|
||||||
public record FrameworkUpdateMessage : MessageBase
|
public record FrameworkUpdateMessage : SameThreadMessage;
|
||||||
{
|
|
||||||
public override bool KeepThreadContext => true;
|
|
||||||
}
|
|
||||||
public record ClassJobChangedMessage(uint? ClassJob) : MessageBase;
|
public record ClassJobChangedMessage(uint? ClassJob) : MessageBase;
|
||||||
public record DelayedFrameworkUpdateMessage : MessageBase
|
public record DelayedFrameworkUpdateMessage : SameThreadMessage;
|
||||||
{
|
|
||||||
public override bool KeepThreadContext => true;
|
|
||||||
}
|
|
||||||
public record ZoneSwitchStartMessage : MessageBase;
|
public record ZoneSwitchStartMessage : MessageBase;
|
||||||
public record ZoneSwitchEndMessage : MessageBase;
|
public record ZoneSwitchEndMessage : MessageBase;
|
||||||
public record CutsceneStartMessage : MessageBase;
|
public record CutsceneStartMessage : MessageBase;
|
||||||
public record GposeStartMessage : MessageBase;
|
public record GposeStartMessage : MessageBase;
|
||||||
public record GposeEndMessage : MessageBase;
|
public record GposeEndMessage : MessageBase;
|
||||||
public record CutsceneEndMessage : MessageBase;
|
public record CutsceneEndMessage : MessageBase;
|
||||||
public record CutsceneFrameworkUpdateMessage : MessageBase
|
public record CutsceneFrameworkUpdateMessage : SameThreadMessage;
|
||||||
{
|
|
||||||
public override bool KeepThreadContext => true;
|
|
||||||
}
|
|
||||||
public record ConnectedMessage(ConnectionDto Connection) : MessageBase;
|
public record ConnectedMessage(ConnectionDto Connection) : MessageBase;
|
||||||
public record DisconnectedMessage : MessageBase;
|
public record DisconnectedMessage : SameThreadMessage;
|
||||||
public record PenumbraModSettingChangedMessage : MessageBase;
|
public record PenumbraModSettingChangedMessage : MessageBase;
|
||||||
public record PenumbraInitializedMessage : MessageBase;
|
public record PenumbraInitializedMessage : MessageBase;
|
||||||
public record PenumbraDisposedMessage : MessageBase;
|
public record PenumbraDisposedMessage : MessageBase;
|
||||||
public record PenumbraRedrawMessage(IntPtr Address, int ObjTblIdx, bool WasRequested) : MessageBase;
|
public record PenumbraRedrawMessage(IntPtr Address, int ObjTblIdx, bool WasRequested) : MessageBase;
|
||||||
public record HeelsOffsetMessage : MessageBase;
|
public record HeelsOffsetMessage : MessageBase;
|
||||||
public record PenumbraResourceLoadMessage(IntPtr GameObject, string GamePath, string FilePath) : MessageBase
|
public record PenumbraResourceLoadMessage(IntPtr GameObject, string GamePath, string FilePath) : SameThreadMessage;
|
||||||
{
|
|
||||||
public override bool KeepThreadContext => true;
|
|
||||||
}
|
|
||||||
public record CustomizePlusMessage : MessageBase;
|
public record CustomizePlusMessage : MessageBase;
|
||||||
public record PalettePlusMessage(Character Character) : MessageBase;
|
public record PalettePlusMessage(Character Character) : MessageBase;
|
||||||
public record HonorificMessage(string NewHonorificTitle) : MessageBase;
|
public record HonorificMessage(string NewHonorificTitle) : MessageBase;
|
||||||
@@ -76,5 +64,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;
|
||||||
|
|
||||||
#pragma warning restore MA0048 // File name must match type name
|
#pragma warning restore MA0048 // File name must match type name
|
||||||
@@ -329,7 +329,6 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
|||||||
{
|
{
|
||||||
_healthCheckTokenSource?.Cancel();
|
_healthCheckTokenSource?.Cancel();
|
||||||
Mediator.Publish(new DisconnectedMessage());
|
Mediator.Publish(new DisconnectedMessage());
|
||||||
_pairManager.ClearPairs();
|
|
||||||
ServerState = ServerState.Offline;
|
ServerState = ServerState.Offline;
|
||||||
if (arg != null)
|
if (arg != null)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user