diff --git a/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs b/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs index 22ef9fb..9e29d1b 100644 --- a/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs +++ b/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs @@ -290,7 +290,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase Address = IntPtr.Zero; DrawObjectAddress = IntPtr.Zero; - throw new InvalidOperationException($"CurPtr for {this} turned ZERO"); + throw new ArgumentNullException($"CurPtr for {this} turned ZERO"); } if (_dalamudUtil.IsAnythingDrawing) diff --git a/MareSynchronos/PlayerData/Pairs/CachedPlayer.cs b/MareSynchronos/PlayerData/Pairs/CachedPlayer.cs index 4885def..b29c8bc 100644 --- a/MareSynchronos/PlayerData/Pairs/CachedPlayer.cs +++ b/MareSynchronos/PlayerData/Pairs/CachedPlayer.cs @@ -1,6 +1,5 @@ using Dalamud.Interface.Internal.Notifications; using Dalamud.Logging; -using FFXIVClientStructs.FFXIV.Client.Game.Object; using MareSynchronos.API.Data; using MareSynchronos.API.Dto.User; using MareSynchronos.FileCache; @@ -36,7 +35,6 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase private GameObjectHandler? _charaHandler; private CancellationTokenSource? _downloadCancellationTokenSource = new(); private CharacterData? _firstTimeInitData; - private int _framesSinceNotVisible = 0; private string _lastGlamourerData = string.Empty; private string _originalGlamourerData = string.Empty; private string _penumbraCollection; @@ -98,7 +96,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase public IntPtr PlayerCharacter => _charaHandler?.Address ?? IntPtr.Zero; public unsafe uint PlayerCharacterId => (_charaHandler?.Address ?? IntPtr.Zero) == IntPtr.Zero ? uint.MaxValue - : ((GameObject*)_charaHandler!.Address)->ObjectID; + : ((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)_charaHandler!.Address)->ObjectID; public string? PlayerName { get; private set; } public string PlayerNameHash => OnlineUser.Ident; @@ -492,6 +490,15 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase Logger.LogDebug("[{applicationId}] Application finished", _applicationId); } + catch (ArgumentNullException ex) + { + Logger.LogWarning(ex, "[{applicationId}] Cancelled, player turned null during application", _applicationId); + IsVisible = false; + if (_cachedData == null) + { + _firstTimeInitData = charaData.DeepClone(); + } + } catch (Exception ex) { Logger.LogWarning(ex, "[{applicationId}] Cancelled", _applicationId); @@ -516,7 +523,6 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase IsVisible = true; Mediator.Publish(new CachedPlayerVisibleMessage(this)); Logger.LogTrace("{this} visibility changed, now: {visi}", this, IsVisible); - _framesSinceNotVisible = 0; if (_firstTimeInitData != null || _cachedData != null) { Task.Run(async () => @@ -528,13 +534,8 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase } else if (_charaHandler?.Address == IntPtr.Zero && IsVisible) { - _framesSinceNotVisible++; - if (_framesSinceNotVisible > 30) - { - _framesSinceNotVisible = 30; - IsVisible = false; - Logger.LogTrace("{this} visibility changed, now: {visi}", this, IsVisible); - } + IsVisible = false; + Logger.LogTrace("{this} visibility changed, now: {visi}", this, IsVisible); } } @@ -552,6 +553,10 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase { Logger.LogTrace("Saving new Glamourer Data for {this}", this); _lastGlamourerData = await _ipcManager.GlamourerGetCharacterCustomizationAsync(PlayerCharacter).ConfigureAwait(false); + if (_cachedData != null) + { + ApplyCharacterData(_cachedData!, true); + } } }); Mediator.Subscribe(this, async (_) => diff --git a/MareSynchronos/PlayerData/Pairs/Pair.cs b/MareSynchronos/PlayerData/Pairs/Pair.cs index 839fb8d..53907bb 100644 --- a/MareSynchronos/PlayerData/Pairs/Pair.cs +++ b/MareSynchronos/PlayerData/Pairs/Pair.cs @@ -19,6 +19,7 @@ public class Pair private readonly ILogger _logger; private readonly MareMediator _mediator; private readonly ServerConfigurationManager _serverConfigurationManager; + private CancellationTokenSource _applicationCts = new CancellationTokenSource(); private OnlineUserIdentDto? _onlineUserIdentDto = null; public Pair(ILogger logger, CachedPlayerFactory cachedPlayerFactory, @@ -73,10 +74,32 @@ public class Pair public void ApplyData(OnlineUserCharaDataDto data) { - if (CachedPlayer == null) throw new InvalidOperationException("CachedPlayer not initialized"); - + _applicationCts = _applicationCts.CancelRecreate(); LastReceivedCharacterData = data.CharaData; + if (CachedPlayer == null) + { + _logger.LogDebug("Received Data for {uid} but CachedPlayer does not exist, waiting", data.User.UID); + Task.Run(async () => + { + using var timeoutCts = new CancellationTokenSource(); + timeoutCts.CancelAfter(TimeSpan.FromSeconds(120)); + var appToken = _applicationCts.Token; + using var combined = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, appToken); + while (CachedPlayer == null && !combined.Token.IsCancellationRequested) + { + await Task.Delay(250, combined.Token).ConfigureAwait(false); + } + + if (!combined.IsCancellationRequested) + { + _logger.LogDebug("Applying delayed data for {uid}", data.User.UID); + ApplyLastReceivedData(); + } + }); + return; + } + ApplyLastReceivedData(); }