From c7439ac76934b280e17e5ccec2da738adba5bf1d Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Mon, 27 Jun 2022 12:31:41 +0200 Subject: [PATCH] why did I start writing this plugin --- .../Factories/CharacterDataFactory.cs | 12 +++--- MareSynchronos/Managers/FileCacheManager.cs | 2 +- MareSynchronos/Managers/PlayerManager.cs | 37 +++++++++++++------ MareSynchronos/Models/CachedPlayer.cs | 15 +++++--- MareSynchronos/UI/DownloadUi.cs | 6 +-- MareSynchronos/Utils/DalamudUtil.cs | 7 ++-- MareSynchronos/WebAPI/ApiController.cs | 13 ++----- 7 files changed, 53 insertions(+), 39 deletions(-) diff --git a/MareSynchronos/Factories/CharacterDataFactory.cs b/MareSynchronos/Factories/CharacterDataFactory.cs index e50a99b..d57edc0 100644 --- a/MareSynchronos/Factories/CharacterDataFactory.cs +++ b/MareSynchronos/Factories/CharacterDataFactory.cs @@ -43,13 +43,17 @@ namespace MareSynchronos.Factories private unsafe CharacterData CreateCharacterData() { Stopwatch st = Stopwatch.StartNew(); - var cache = new CharacterData(); - while (!_dalamudUtil.IsPlayerPresent) { Logger.Debug("Character is null but it shouldn't be, waiting"); Thread.Sleep(50); } + var cache = new CharacterData + { + JobId = _dalamudUtil.PlayerJobId, + GlamourerString = _ipcManager.GlamourerGetCharacterCustomization(_dalamudUtil.PlayerName), + ManipulationString = _ipcManager.PenumbraGetMetaManipulations(_dalamudUtil.PlayerName) + }; var model = (CharacterBase*)((Character*)_dalamudUtil.PlayerPointer)->GameObject.GetDrawObject(); for (var idx = 0; idx < model->SlotCount; ++idx) { @@ -107,10 +111,6 @@ namespace MareSynchronos.Factories } } - cache.GlamourerString = _ipcManager.GlamourerGetCharacterCustomization(_dalamudUtil.PlayerName)!; - cache.ManipulationString = _ipcManager.PenumbraGetMetaManipulations(_dalamudUtil.PlayerName); - cache.JobId = _dalamudUtil.PlayerJobId; - st.Stop(); Logger.Debug("Building Character Data took " + st.Elapsed); diff --git a/MareSynchronos/Managers/FileCacheManager.cs b/MareSynchronos/Managers/FileCacheManager.cs index d33699a..2f3e5ef 100644 --- a/MareSynchronos/Managers/FileCacheManager.cs +++ b/MareSynchronos/Managers/FileCacheManager.cs @@ -144,7 +144,7 @@ namespace MareSynchronos.Managers { try { - using var fs = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None); + using var fs = file.Open(FileMode.Open, FileAccess.Read, FileShare.Read); } catch { diff --git a/MareSynchronos/Managers/PlayerManager.cs b/MareSynchronos/Managers/PlayerManager.cs index 64f8527..13c9404 100644 --- a/MareSynchronos/Managers/PlayerManager.cs +++ b/MareSynchronos/Managers/PlayerManager.cs @@ -21,6 +21,7 @@ namespace MareSynchronos.Managers private readonly IpcManager _ipcManager; private string _lastSentHash = string.Empty; private Task? _playerChangedTask; + private CancellationTokenSource? _playerChangedCts; public PlayerManager(ApiController apiController, IpcManager ipcManager, CharacterDataFactory characterDataFactory, CachedPlayersManager cachedPlayersManager, DalamudUtil dalamudUtil) @@ -77,21 +78,23 @@ namespace MareSynchronos.Managers _dalamudUtil.PlayerChanged -= Watcher_PlayerChanged; } - private async Task CreateFullCharacterCache() + private async Task CreateFullCharacterCache(CancellationToken token) { var cache = _characterDataFactory.BuildCharacterData(); await Task.Run(async () => { - while (!cache.IsReady) + while (!cache.IsReady && !token.IsCancellationRequested) { - await Task.Delay(50); + await Task.Delay(50, token); } + if (token.IsCancellationRequested) return; + var json = JsonConvert.SerializeObject(cache, Formatting.Indented); cache.CacheHash = Crypto.GetHash(json); - }); + }, token); return cache; } @@ -108,11 +111,14 @@ namespace MareSynchronos.Managers { //if (sender == null) return; Logger.Debug("Player changed: " + name); + _playerChangedCts?.Cancel(); + _playerChangedCts = new CancellationTokenSource(); + var token = _playerChangedCts.Token;/* if (_playerChangedTask is { IsCompleted: false }) { PluginLog.Warning("PlayerChanged Task still running"); return; - } + }*/ if (!_ipcManager.Initialized) { @@ -123,23 +129,30 @@ namespace MareSynchronos.Managers _playerChangedTask = Task.Run(async () => { int attempts = 0; - while (!_apiController.IsConnected && attempts < 10) + while (!_apiController.IsConnected && attempts < 10 && !token.IsCancellationRequested) { Logger.Warn("No connection to the API"); - await Task.Delay(TimeSpan.FromSeconds(1)); + await Task.Delay(TimeSpan.FromSeconds(1), token); attempts++; } - if (attempts == 10) return; + if (attempts == 10 || token.IsCancellationRequested) return; Stopwatch st = Stopwatch.StartNew(); - _dalamudUtil.WaitWhileSelfIsDrawing(); + _dalamudUtil.WaitWhileSelfIsDrawing(token); - var characterCacheTask = await CreateFullCharacterCache(); + var characterCacheTask = await CreateFullCharacterCache(token); + + if (token.IsCancellationRequested) return; var cacheDto = characterCacheTask.ToCharacterCacheDto(); - st.Stop(); + + if (token.IsCancellationRequested) + { + return; + } + Logger.Debug("Elapsed time PlayerChangedTask: " + st.Elapsed); if (cacheDto.Hash == _lastSentHash) { @@ -148,7 +161,7 @@ namespace MareSynchronos.Managers } _ = _apiController.SendCharacterData(cacheDto, _dalamudUtil.GetLocalPlayers().Select(d => d.Key).ToList()); _lastSentHash = cacheDto.Hash; - }); + }, token); } private void Watcher_PlayerChanged(Dalamud.Game.ClientState.Objects.Types.Character actor) diff --git a/MareSynchronos/Models/CachedPlayer.cs b/MareSynchronos/Models/CachedPlayer.cs index 7f4c0d3..ca5c2d1 100644 --- a/MareSynchronos/Models/CachedPlayer.cs +++ b/MareSynchronos/Models/CachedPlayer.cs @@ -70,12 +70,12 @@ public class CachedPlayer { Logger.Debug("Received total " + e.CharacterData.FileReplacements.Count + " file replacement data"); _cache[e.CharacterData.Hash] = e.CharacterData; - _lastAppliedEquipmentHash = e.CharacterData.Hash; } else { Logger.Debug("Had valid local cache for " + PlayerName); } + _lastAppliedEquipmentHash = e.CharacterData.Hash; DownloadAndApplyCharacter(); } @@ -158,15 +158,19 @@ public class CachedPlayer try { Logger.Debug("Restoring state for " + PlayerName); - IsVisible = false; _downloadCancellationTokenSource?.Cancel(); _downloadCancellationTokenSource?.Dispose(); _downloadCancellationTokenSource = null; _dalamudUtil.RemovePlayerFromWatch(PlayerName); _ipcManager.PenumbraRemoveTemporaryCollection(PlayerName); - _ipcManager.GlamourerRevertCharacterCustomization(PlayerName); - _ipcManager.GlamourerApplyOnlyCustomization(_originalGlamourerData, PlayerName); - _ipcManager.GlamourerApplyOnlyEquipment(_lastGlamourerData, PlayerName); + if (IsVisible) + { + _ipcManager.GlamourerRevertCharacterCustomization(PlayerName); + _ipcManager.GlamourerApplyOnlyCustomization(_originalGlamourerData, PlayerName); + _ipcManager.GlamourerApplyOnlyEquipment(_lastGlamourerData, PlayerName); + } + + IsVisible = false; } catch (Exception ex) { @@ -215,6 +219,7 @@ public class CachedPlayer if (RequestedPenumbraRedraw == false && !string.IsNullOrEmpty(_lastAppliedEquipmentHash)) { + Logger.Warn("Unauthorized character change detected"); DownloadAndApplyCharacter(); } else diff --git a/MareSynchronos/UI/DownloadUi.cs b/MareSynchronos/UI/DownloadUi.cs index 2cc463b..34796b6 100644 --- a/MareSynchronos/UI/DownloadUi.cs +++ b/MareSynchronos/UI/DownloadUi.cs @@ -50,7 +50,7 @@ public class DownloadUi : Window, IDisposable var basePosition = ImGui.GetWindowPos() + ImGui.GetWindowContentRegionMin(); - if (_apiController.IsUploading) + if (_apiController.CurrentUploads.Any()) { var doneUploads = _apiController.CurrentUploads.Count(c => c.Value.Item1 == c.Value.Item2); var totalUploads = _apiController.CurrentUploads.Keys.Count; @@ -67,9 +67,9 @@ public class DownloadUi : Window, IDisposable UiShared.Color(255, 255, 255, 255), UiShared.Color(0, 0, 0, 255), 2); } - if (_apiController.IsDownloading) + if (_apiController.CurrentDownloads.Any()) { - var multBase = _apiController.IsDownloading ? 0 : 2; + var multBase = _apiController.CurrentUploads.Any() ? 0 : 2; var doneDownloads = _apiController.CurrentDownloads.Count(c => c.Value.Item1 == c.Value.Item2); var totalDownloads = _apiController.CurrentDownloads.Keys.Count; var totalDownloaded = _apiController.CurrentDownloads.Sum(c => c.Value.Item1); diff --git a/MareSynchronos/Utils/DalamudUtil.cs b/MareSynchronos/Utils/DalamudUtil.cs index 2654869..e8affad 100644 --- a/MareSynchronos/Utils/DalamudUtil.cs +++ b/MareSynchronos/Utils/DalamudUtil.cs @@ -100,24 +100,25 @@ namespace MareSynchronos.Utils return null; } - public unsafe void WaitWhileCharacterIsDrawing(IntPtr characterAddress) + public unsafe void WaitWhileCharacterIsDrawing(IntPtr characterAddress, CancellationToken? ct = null) { if (!_clientState.IsLoggedIn) return; var obj = (GameObject*)characterAddress; // ReSharper disable once LoopVariableIsNeverChangedInsideLoop - while ((obj->RenderFlags & 0b100000000000) == 0b100000000000) // 0b100000000000 is "still rendering" or something + while ((obj->RenderFlags & 0b100000000000) == 0b100000000000 && (!ct?.IsCancellationRequested ?? true)) // 0b100000000000 is "still rendering" or something { Logger.Debug("Waiting for character to finish drawing"); Thread.Sleep(1000); } + if (ct?.IsCancellationRequested ?? false) return; // wait half a second just in case Thread.Sleep(500); } - public void WaitWhileSelfIsDrawing() => WaitWhileCharacterIsDrawing(_clientState.LocalPlayer?.Address ?? new IntPtr()); + public void WaitWhileSelfIsDrawing(CancellationToken token) => WaitWhileCharacterIsDrawing(_clientState.LocalPlayer?.Address ?? new IntPtr(), token); public void Dispose() { diff --git a/MareSynchronos/WebAPI/ApiController.cs b/MareSynchronos/WebAPI/ApiController.cs index 2cf83f8..3d3c046 100644 --- a/MareSynchronos/WebAPI/ApiController.cs +++ b/MareSynchronos/WebAPI/ApiController.cs @@ -247,19 +247,14 @@ namespace MareSynchronos.WebAPI public async Task DownloadFile(string hash, CancellationToken ct) { - var reader = await _fileHub!.StreamAsChannelAsync("DownloadFile", hash, ct); - int i = 0; + var reader = _fileHub!.StreamAsync("DownloadFileAsync", hash, ct); string fileName = Path.GetTempFileName(); await using var fs = File.OpenWrite(fileName); - while (await reader.WaitToReadAsync(ct) && !ct.IsCancellationRequested) + await foreach (var data in reader.WithCancellation(ct)) { - while (reader.TryRead(out var data) && !ct.IsCancellationRequested) - { - CurrentDownloads[hash] = (CurrentDownloads[hash].Item1 + data.Length, CurrentDownloads[hash].Item2); - await fs.WriteAsync(data, ct); - } + CurrentDownloads[hash] = (CurrentDownloads[hash].Item1 + data.Length, CurrentDownloads[hash].Item2); + await fs.WriteAsync(data, ct); } - return fileName; }