From 71ced4dc8860afb0e0d3d91e91d644c591ac9d4c Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Sun, 26 Jun 2022 02:34:24 +0200 Subject: [PATCH] fuck character states --- .../Factories/CharacterDataFactory.cs | 2 +- .../Managers/CharacterCacheManager.cs | 84 ++++++++++++++++--- MareSynchronos/Managers/CharacterManager.cs | 4 - MareSynchronos/Managers/FileCacheManager.cs | 8 +- MareSynchronos/Managers/IpcManager.cs | 41 ++++++--- MareSynchronos/Models/CachedPlayer.cs | 12 ++- MareSynchronos/Plugin.cs | 2 +- MareSynchronos/Utils/Logger.cs | 6 ++ MareSynchronos/WebAPI/ApiController.cs | 16 ++-- 9 files changed, 137 insertions(+), 38 deletions(-) diff --git a/MareSynchronos/Factories/CharacterDataFactory.cs b/MareSynchronos/Factories/CharacterDataFactory.cs index f2e9ad2..e50a99b 100644 --- a/MareSynchronos/Factories/CharacterDataFactory.cs +++ b/MareSynchronos/Factories/CharacterDataFactory.cs @@ -107,7 +107,7 @@ namespace MareSynchronos.Factories } } - cache.GlamourerString = _ipcManager.GlamourerGetCharacterCustomization()!; + cache.GlamourerString = _ipcManager.GlamourerGetCharacterCustomization(_dalamudUtil.PlayerName)!; cache.ManipulationString = _ipcManager.PenumbraGetMetaManipulations(_dalamudUtil.PlayerName); cache.JobId = _dalamudUtil.PlayerJobId; diff --git a/MareSynchronos/Managers/CharacterCacheManager.cs b/MareSynchronos/Managers/CharacterCacheManager.cs index 5349255..09dc764 100644 --- a/MareSynchronos/Managers/CharacterCacheManager.cs +++ b/MareSynchronos/Managers/CharacterCacheManager.cs @@ -6,12 +6,14 @@ using Dalamud.Game; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.SubKinds; +using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Logging; using MareSynchronos.API; using MareSynchronos.FileCacheDB; using MareSynchronos.Models; using MareSynchronos.Utils; using MareSynchronos.WebAPI; +using Penumbra.PlayerWatch; namespace MareSynchronos.Managers; @@ -22,12 +24,13 @@ public class CharacterCacheManager : IDisposable private readonly DalamudUtil _dalamudUtil; private readonly Framework _framework; private readonly IpcManager _ipcManager; + private readonly IPlayerWatcher _watcher; private readonly ObjectTable _objectTable; private readonly List _onlineCachedPlayers = new(); private readonly List _localVisiblePlayers = new(); private DateTime _lastPlayerObjectCheck = DateTime.Now; - public CharacterCacheManager(ClientState clientState, Framework framework, ObjectTable objectTable, ApiController apiController, DalamudUtil dalamudUtil, IpcManager ipcManager) + public CharacterCacheManager(ClientState clientState, Framework framework, ObjectTable objectTable, ApiController apiController, DalamudUtil dalamudUtil, IpcManager ipcManager, IPlayerWatcher watcher) { Logger.Debug("Creating " + nameof(CharacterCacheManager)); @@ -37,6 +40,7 @@ public class CharacterCacheManager : IDisposable _apiController = apiController; _dalamudUtil = dalamudUtil; _ipcManager = ipcManager; + _watcher = watcher; _clientState.Login += ClientStateOnLogin; _clientState.Logout += ClientStateOnLogout; @@ -48,6 +52,8 @@ public class CharacterCacheManager : IDisposable _apiController.UnpairedFromOther += ApiControllerOnUnpairedFromOther; _apiController.Disconnected += ApiControllerOnDisconnected; _ipcManager.PenumbraDisposed += IpcManagerOnPenumbraDisposed; + _ipcManager.PenumbraRedrawEvent += IpcManagerOnPenumbraRedrawEvent; + _watcher.PlayerChanged += WatcherOnPlayerChanged; if (clientState.IsLoggedIn) { @@ -55,11 +61,45 @@ public class CharacterCacheManager : IDisposable } } + private void IpcManagerOnPenumbraRedrawEvent(object? objectTableIndex, EventArgs e) + { + var objTableObj = _objectTable[(int)objectTableIndex!]; + if (objTableObj!.ObjectKind != Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player) return; + string objTableObjName = objTableObj.Name.ToString(); + var cachedPlayer = _onlineCachedPlayers.SingleOrDefault(p => p.PlayerName == objTableObjName); + if (cachedPlayer != null) + { + Task.Run(() => + { + cachedPlayer.PlayerCharacter = (PlayerCharacter)objTableObj; + _dalamudUtil.WaitWhileCharacterIsDrawing(cachedPlayer.PlayerCharacter.Address); + + cachedPlayer.RequestedRedraws--; + Logger.Warn( + $"Penumbra Redraw for {cachedPlayer.PlayerName}: RequestedRedraws now {cachedPlayer.RequestedRedraws}"); + }); + } + } + + private void WatcherOnPlayerChanged(Character actor) + { + var cachedChar = _onlineCachedPlayers.SingleOrDefault(p => p.PlayerName == actor.Name.ToString()); + if (cachedChar == null) return; + Logger.Warn($"Player {cachedChar.PlayerName} changed, RequestedRedraws {cachedChar.RequestedRedraws}"); + if (cachedChar.RequestedRedraws == 0) + { + Logger.Warn($"Saving new Glamourer data for job " + cachedChar.JobId); + cachedChar.LastGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(cachedChar.PlayerName!); + } + } + private void IpcManagerOnPenumbraDisposed(object? sender, EventArgs e) { foreach (var character in _onlineCachedPlayers.ToList()) { RestoreCharacter(character); + if (!string.IsNullOrEmpty(character.PlayerName)) + _watcher.RemovePlayerFromWatch(character.PlayerName); character.IsVisible = false; } } @@ -99,6 +139,7 @@ public class CharacterCacheManager : IDisposable _framework.Update -= FrameworkOnUpdate; _clientState.Login -= ClientStateOnLogin; _clientState.Logout -= ClientStateOnLogout; + _watcher.PlayerChanged -= WatcherOnPlayerChanged; RestoreAllCharacters(); } @@ -107,6 +148,8 @@ public class CharacterCacheManager : IDisposable foreach (var character in _onlineCachedPlayers.ToList()) { RestoreCharacter(character); + if (!string.IsNullOrEmpty(character.PlayerName)) + _watcher.RemovePlayerFromWatch(character.PlayerName); } _onlineCachedPlayers.Clear(); @@ -121,7 +164,6 @@ public class CharacterCacheManager : IDisposable { Logger.Debug("Received hash for " + e.CharacterNameHash); string otherPlayerName; - var localPlayers = _dalamudUtil.GetLocalPlayers(); if (localPlayers.ContainsKey(e.CharacterNameHash)) { @@ -134,8 +176,9 @@ public class CharacterCacheManager : IDisposable return; } - _onlineCachedPlayers.Single(p => p.PlayerNameHash == e.CharacterNameHash) - .CharacterCache[e.CharacterData.JobId] = e.CharacterData; + var cachedPlayer = _onlineCachedPlayers.Single(p => p.PlayerNameHash == e.CharacterNameHash); + + cachedPlayer.CharacterCache[e.CharacterData.JobId] = e.CharacterData; List toDownloadReplacements; using (var db = new FileCacheContext()) @@ -179,9 +222,12 @@ public class CharacterCacheManager : IDisposable } _dalamudUtil.WaitWhileCharacterIsDrawing(localPlayers[e.CharacterNameHash].Address); - + cachedPlayer.RequestedRedraws++; + Logger.Warn( + $"Request Redraw for {cachedPlayer.PlayerName}: RequestedRedraws now {cachedPlayer.RequestedRedraws}"); _ipcManager.PenumbraSetTemporaryMods(tempCollection, moddedPaths, e.CharacterData.ManipulationData); - _ipcManager.GlamourerApplyCharacterCustomization(e.CharacterData.GlamourerData, otherPlayerName); + _ipcManager.GlamourerRevertCharacterCustomization(otherPlayerName); + _ipcManager.GlamourerApplyAll(e.CharacterData.GlamourerData, otherPlayerName); }); } @@ -260,12 +306,20 @@ public class CharacterCacheManager : IDisposable foreach (var item in _onlineCachedPlayers.Where(p => !string.IsNullOrEmpty(p.PlayerName) && !p.IsVisible && p.WasVisible)) { Logger.Debug("Player not visible anymore: " + item.PlayerName); + _watcher.RemovePlayerFromWatch(item.PlayerName!); RestoreCharacter(item); } - var newVisiblePlayers = _onlineCachedPlayers.Where(p => p.IsVisible && !p.WasVisible).ToList(); + var newVisiblePlayers = _onlineCachedPlayers.Where(p => p.IsVisible && !p.WasVisible && p.PlayerCharacter != null).ToList(); if (newVisiblePlayers.Any()) { + foreach (var player in newVisiblePlayers) + { + Logger.Debug("New watched player, adding to watch and getting glamourer data: " + player.PlayerName); + _watcher.AddPlayerToWatch(player.PlayerName!); + player.OriginalGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(player.PlayerName!); + } + Logger.Debug("Getting data for new players: " + string.Join(Environment.NewLine, newVisiblePlayers)); Task.Run(async () => await UpdatePlayersFromService(newVisiblePlayers .ToDictionary(k => k.PlayerNameHash, k => (int)k.PlayerCharacter!.ClassJob.Id))); @@ -282,10 +336,18 @@ public class CharacterCacheManager : IDisposable private void RestoreCharacter(CachedPlayer? character) { if (character == null || string.IsNullOrEmpty(character.PlayerName)) return; - - Logger.Debug("Restoring state for " + character.PlayerName); - _ipcManager.PenumbraRemoveTemporaryCollection(character.PlayerName); - _ipcManager.GlamourerRevertCharacterCustomization(character.PlayerName); + try + { + Logger.Debug("Restoring state for " + character.PlayerName); + _ipcManager.PenumbraRemoveTemporaryCollection(character.PlayerName); + _ipcManager.GlamourerRevertCharacterCustomization(character.PlayerName); + _ipcManager.GlamourerApplyOnlyCustomization(character.OriginalGlamourerData, character.PlayerName); + _ipcManager.GlamourerApplyOnlyEquipment(character.LastGlamourerData, character.PlayerName); + } + catch (Exception ex) + { + Logger.Warn(ex.Message + Environment.NewLine + ex.StackTrace); + } character.Reset(); } diff --git a/MareSynchronos/Managers/CharacterManager.cs b/MareSynchronos/Managers/CharacterManager.cs index e5b953c..3adda38 100644 --- a/MareSynchronos/Managers/CharacterManager.cs +++ b/MareSynchronos/Managers/CharacterManager.cs @@ -167,10 +167,6 @@ namespace MareSynchronos.Managers Logger.Debug("Watcher: PlayerChanged"); PlayerChanged(actor.Name.ToString()); } - else - { - Logger.Debug("PlayerChanged: " + actor.Name.ToString()); - } }); } diff --git a/MareSynchronos/Managers/FileCacheManager.cs b/MareSynchronos/Managers/FileCacheManager.cs index ecd6c61..5f9a780 100644 --- a/MareSynchronos/Managers/FileCacheManager.cs +++ b/MareSynchronos/Managers/FileCacheManager.cs @@ -235,8 +235,9 @@ namespace MareSynchronos.Managers .Select(s => s.ToLowerInvariant())) .Where(f => (f.EndsWith(".tex", StringComparison.OrdinalIgnoreCase) || f.EndsWith(".mdl", StringComparison.OrdinalIgnoreCase) || f.EndsWith(".mtrl", StringComparison.OrdinalIgnoreCase))) .Select(p => new KeyValuePair(p, false))); - await using FileCacheContext db = new(); - List fileCaches = db.FileCaches.ToList(); + List fileCaches; + await using (var db = new FileCacheContext()) + fileCaches = db.FileCaches.ToList(); TotalFiles = scannedFiles.Count; @@ -293,6 +294,8 @@ namespace MareSynchronos.Managers if (fileCachesToAdd.Any() || fileCachesToDelete.Any()) { + await using FileCacheContext db = new(); + Logger.Debug("Found " + fileCachesToAdd.Count + " additions and " + fileCachesToDelete.Count + " deletions"); try { @@ -303,6 +306,7 @@ namespace MareSynchronos.Managers if (entry != null) db.FileCaches.Remove(entry); } + await db.SaveChangesAsync(ct); db.FileCaches.AddRange(fileCachesToAdd); await db.SaveChangesAsync(ct); } diff --git a/MareSynchronos/Managers/IpcManager.cs b/MareSynchronos/Managers/IpcManager.cs index 3e356eb..a7c7d58 100644 --- a/MareSynchronos/Managers/IpcManager.cs +++ b/MareSynchronos/Managers/IpcManager.cs @@ -10,9 +10,11 @@ namespace MareSynchronos.Managers public class IpcManager : IDisposable { private readonly ICallGateSubscriber _glamourerApiVersion; - private readonly ICallGateSubscriber? _glamourerApplyCharacterCustomization; - private readonly ICallGateSubscriber? _glamourerGetCharacterCustomization; + private readonly ICallGateSubscriber? _glamourerApplyAll; + private readonly ICallGateSubscriber? _glamourerGetAllCustomization; private readonly ICallGateSubscriber _glamourerRevertCustomization; + private readonly ICallGateSubscriber? _glamourerApplyOnlyEquipment; + private readonly ICallGateSubscriber? _glamourerApplyOnlyCustomization; private readonly ICallGateSubscriber _penumbraApiVersion; private readonly ICallGateSubscriber _penumbraCreateTemporaryCollection; private readonly ICallGateSubscriber _penumbraGetMetaManipulations; @@ -35,16 +37,19 @@ namespace MareSynchronos.Managers _penumbraResolvePath = pi.GetIpcSubscriber("Penumbra.ResolveCharacterPath"); _penumbraResolveModDir = pi.GetIpcSubscriber("Penumbra.GetModDirectory"); _penumbraRedraw = pi.GetIpcSubscriber("Penumbra.RedrawObjectByName"); - _glamourerGetCharacterCustomization = pi.GetIpcSubscriber("Glamourer.GetCharacterCustomization"); - _glamourerApplyCharacterCustomization = pi.GetIpcSubscriber("Glamourer.ApplyCharacterCustomization"); _penumbraReverseResolvePath = pi.GetIpcSubscriber("Penumbra.ReverseResolvePath"); _penumbraApiVersion = pi.GetIpcSubscriber("Penumbra.ApiVersion"); - _glamourerApiVersion = pi.GetIpcSubscriber("Glamourer.ApiVersion"); - _glamourerRevertCustomization = pi.GetIpcSubscriber("Glamourer.RevertCharacterCustomization"); _penumbraObjectIsRedrawn = pi.GetIpcSubscriber("Penumbra.GameObjectRedrawn"); _penumbraGetMetaManipulations = pi.GetIpcSubscriber("Penumbra.GetMetaManipulations"); + _glamourerApiVersion = pi.GetIpcSubscriber("Glamourer.ApiVersion"); + _glamourerGetAllCustomization = pi.GetIpcSubscriber("Glamourer.GetAllCustomization"); + _glamourerApplyAll = pi.GetIpcSubscriber("Glamourer.ApplyAll"); + _glamourerApplyOnlyCustomization = pi.GetIpcSubscriber("Glamourer.ApplyOnlyCustomization"); + _glamourerApplyOnlyEquipment = pi.GetIpcSubscriber("Glamourer.ApplyOnlyEquipment"); + _glamourerRevertCustomization = pi.GetIpcSubscriber("Glamourer.Revert"); + _penumbraObjectIsRedrawn.Subscribe(RedrawEvent); _penumbraInit.Subscribe(PenumbraInit); _penumbraDispose.Subscribe(PenumbraDispose); @@ -104,17 +109,31 @@ namespace MareSynchronos.Managers Logger.Debug("IPC Manager disposed"); } - public void GlamourerApplyCharacterCustomization(string customization, string characterName) + public void GlamourerApplyAll(string customization, string characterName) { if (!CheckGlamourerApi()) return; Logger.Debug("GlamourerString: " + customization); - _glamourerApplyCharacterCustomization!.InvokeAction(customization, characterName); + _glamourerApplyAll!.InvokeAction(customization, characterName); } - public string? GlamourerGetCharacterCustomization() + public void GlamourerApplyOnlyEquipment(string customization, string characterName) { - if (!CheckGlamourerApi()) return null; - return _glamourerGetCharacterCustomization!.InvokeFunc(); + if (!CheckGlamourerApi()) return; + Logger.Debug("GlamourerString: " + customization); + _glamourerApplyOnlyEquipment!.InvokeAction(customization, characterName); + } + + public void GlamourerApplyOnlyCustomization(string customization, string characterName) + { + if (!CheckGlamourerApi()) return; + Logger.Debug("GlamourerString: " + customization); + _glamourerApplyOnlyCustomization!.InvokeAction(customization, characterName); + } + + public string GlamourerGetCharacterCustomization(string characterName) + { + if (!CheckGlamourerApi()) return string.Empty; + return _glamourerGetAllCustomization!.InvokeFunc(characterName); } public void GlamourerRevertCharacterCustomization(string characterName) diff --git a/MareSynchronos/Models/CachedPlayer.cs b/MareSynchronos/Models/CachedPlayer.cs index 12d60ac..14b7916 100644 --- a/MareSynchronos/Models/CachedPlayer.cs +++ b/MareSynchronos/Models/CachedPlayer.cs @@ -24,15 +24,23 @@ public class CachedPlayer } } - public int? JobId { get; set; } + public string OriginalGlamourerData { get; set; } + public string LastGlamourerData { get; set; } + public int? JobId => (int?)PlayerCharacter?.ClassJob.Id; public PlayerCharacter? PlayerCharacter { get; set; } public string? PlayerName { get; set; } public string PlayerNameHash { get; } public bool WasVisible { get; private set; } + public int RequestedRedraws + { + get => _requestedRedraws; + set => _requestedRedraws = value < 0 ? 0 : value; + } + + private int _requestedRedraws; public void Reset() { PlayerName = string.Empty; - JobId = null; PlayerCharacter = null; } diff --git a/MareSynchronos/Plugin.cs b/MareSynchronos/Plugin.cs index 36f317f..640cdd9 100644 --- a/MareSynchronos/Plugin.cs +++ b/MareSynchronos/Plugin.cs @@ -163,7 +163,7 @@ namespace MareSynchronos var characterCacheFactory = new CharacterDataFactory(_dalamudUtil, _ipcManager); _characterCacheManager = new CharacterCacheManager(_clientState, _framework, _objectTable, - _apiController, _dalamudUtil, _ipcManager); + _apiController, _dalamudUtil, _ipcManager, _playerWatcher); _characterManager = new CharacterManager(_apiController, _objectTable, _ipcManager, characterCacheFactory, _characterCacheManager, _dalamudUtil, _playerWatcher); _characterManager.StartWatchingPlayer(); diff --git a/MareSynchronos/Utils/Logger.cs b/MareSynchronos/Utils/Logger.cs index 084c811..05e799d 100644 --- a/MareSynchronos/Utils/Logger.cs +++ b/MareSynchronos/Utils/Logger.cs @@ -10,5 +10,11 @@ namespace MareSynchronos.Utils var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown"; PluginLog.Debug($"[{caller}] {debug}"); } + + public static void Warn(string warn) + { + var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown"; + PluginLog.Warning($"[{caller}] {warn}"); + } } } diff --git a/MareSynchronos/WebAPI/ApiController.cs b/MareSynchronos/WebAPI/ApiController.cs index 983601b..c86e3a7 100644 --- a/MareSynchronos/WebAPI/ApiController.cs +++ b/MareSynchronos/WebAPI/ApiController.cs @@ -93,21 +93,22 @@ namespace MareSynchronos.WebAPI _ = DisposeHubConnections(); } - public async Task DownloadFile(string hash) + public async Task DownloadFile(string hash) { var reader = await _fileHub!.StreamAsChannelAsync("DownloadFile", hash); - List downloadedData = new(); int i = 0; + string fileName = Path.GetTempFileName(); + await using var fs = File.OpenWrite(fileName); while (await reader.WaitToReadAsync()) { while (reader.TryRead(out var data)) { CurrentDownloads[hash] = (CurrentDownloads[hash].Item1 + data.Length, CurrentDownloads[hash].Item2); - downloadedData.AddRange(data); + await fs.WriteAsync(data); } } - return downloadedData.ToArray(); + return fileName; } public async Task DownloadFiles(List fileReplacementDto) @@ -128,8 +129,11 @@ namespace MareSynchronos.WebAPI continue; } var hash = file.Hash; - var data = await DownloadFile(hash); - var extractedFile = LZ4Codec.Unwrap(data); + var tempFile = await DownloadFile(hash); + var tempFileData = await File.ReadAllBytesAsync(tempFile); + var extractedFile = LZ4Codec.Unwrap(tempFileData); + File.Delete(tempFile); + tempFileData = null; var ext = file.GamePaths.First().Split(".").Last(); var filePath = Path.Combine(_pluginConfiguration.CacheFolder, file.Hash + "." + ext); await File.WriteAllBytesAsync(filePath, extractedFile);