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(); |  | ||||||
|  |  | ||||||
|         if (newVisiblePlayers.Any()) |  | ||||||
|         { |  | ||||||
|         Logger.LogTrace("Has new visible players, pushing character data"); |         Logger.LogTrace("Has new visible players, pushing character data"); | ||||||
|             PushCharacterData(newVisiblePlayers); |         PushCharacterData(newVisiblePlayers.Select(c => c.OnlineUser.User).ToList()); | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     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,31 +350,15 @@ 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.RecreateCachedPlayer(); |  | ||||||
|             else |  | ||||||
|             item.Value.MarkOffline(); |             item.Value.MarkOffline(); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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,8 +135,7 @@ 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) |             foreach (SubscriberAction subscriber in subscribersCopy) | ||||||
|             { |             { | ||||||
|                 try |                 try | ||||||
| @@ -155,13 +154,13 @@ public sealed class MareMediator : IHostedService | |||||||
|                     _lastErrorTime[subscriber] = DateTime.UtcNow; |                     _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
	 rootdarkarchon
					rootdarkarchon