diff --git a/MareSynchronos/Configuration.cs b/MareSynchronos/Configuration.cs index 7329f7a..ed8ec17 100644 --- a/MareSynchronos/Configuration.cs +++ b/MareSynchronos/Configuration.cs @@ -44,6 +44,7 @@ namespace MareSynchronos } } + public bool FullPause { get; set; } = false; public Dictionary UidComments { get; set; } = new(); public bool UseCustomService { get; set; } = false; public int Version { get; set; } = 0; diff --git a/MareSynchronos/Managers/FileCacheManager.cs b/MareSynchronos/Managers/FileCacheManager.cs index 5f9a780..4867366 100644 --- a/MareSynchronos/Managers/FileCacheManager.cs +++ b/MareSynchronos/Managers/FileCacheManager.cs @@ -213,6 +213,7 @@ namespace MareSynchronos.Managers Task.Run(RecalculateFileCacheSize); } } + private void RecalculateFileCacheSize() { FileCacheSize = 0; diff --git a/MareSynchronos/Managers/PlayerManager.cs b/MareSynchronos/Managers/PlayerManager.cs index 62fe914..f210312 100644 --- a/MareSynchronos/Managers/PlayerManager.cs +++ b/MareSynchronos/Managers/PlayerManager.cs @@ -53,7 +53,6 @@ namespace MareSynchronos.Managers internal void StartWatchingPlayer() { _watcher.AddPlayerToWatch(_dalamudUtil.PlayerName); - _watcher.PlayerChanged += Watcher_PlayerChanged; _apiController.Connected += ApiController_Connected; _apiController.Disconnected += ApiController_Disconnected; @@ -62,12 +61,11 @@ namespace MareSynchronos.Managers { ApiController_Connected(null, EventArgs.Empty); } - - _ipcManager.PenumbraRedraw(_dalamudUtil.PlayerName); } private void ApiController_Connected(object? sender, EventArgs args) { + Logger.Debug("ApiController Connected"); var apiTask = _apiController.SendCharacterName(_dalamudUtil.PlayerNameHashed); _lastSentHash = string.Empty; @@ -76,6 +74,8 @@ namespace MareSynchronos.Managers _cachedPlayersManager.AddInitialPairs(apiTask.Result); _ipcManager.PenumbraRedrawEvent += IpcManager_PenumbraRedrawEvent; + _ipcManager.PenumbraRedraw(_dalamudUtil.PlayerName); + _watcher.PlayerChanged += Watcher_PlayerChanged; } private void ApiController_Disconnected(object? sender, EventArgs args) @@ -83,6 +83,7 @@ namespace MareSynchronos.Managers Logger.Debug(nameof(ApiController_Disconnected)); _ipcManager.PenumbraRedrawEvent -= IpcManager_PenumbraRedrawEvent; + _watcher.PlayerChanged -= Watcher_PlayerChanged; } private async Task CreateFullCharacterCache() @@ -106,10 +107,9 @@ namespace MareSynchronos.Managers private void IpcManager_PenumbraRedrawEvent(object? objectTableIndex, EventArgs e) { - var objTableObj = _objectTable[(int)objectTableIndex!]; - if (objTableObj!.ObjectKind != Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player) return; - if (objTableObj.Name.ToString() != _dalamudUtil.PlayerName) return; - Logger.Debug("Penumbra Redraw Event"); + var player = _dalamudUtil.GetPlayerCharacterFromObjectTableIndex((int)objectTableIndex!); + if (player != null && player.Name.ToString() != _dalamudUtil.PlayerName) return; + Logger.Debug("Penumbra Redraw Event for " + _dalamudUtil.PlayerName); PlayerChanged(_dalamudUtil.PlayerName); } @@ -131,6 +131,16 @@ namespace MareSynchronos.Managers _playerChangedTask = Task.Run(async () => { + int attempts = 0; + while (!_apiController.IsConnected && attempts < 10) + { + Logger.Warn("No connection to the API"); + await Task.Delay(TimeSpan.FromSeconds(1)); + attempts++; + } + + if (attempts == 10) return; + Stopwatch st = Stopwatch.StartNew(); _dalamudUtil.WaitWhileSelfIsDrawing(); @@ -152,7 +162,6 @@ namespace MareSynchronos.Managers private void Watcher_PlayerChanged(Dalamud.Game.ClientState.Objects.Types.Character actor) { - Logger.Debug("Watcher Player Changed"); Task.Run(() => { // fix for redraw from anamnesis diff --git a/MareSynchronos/Models/CachedPlayer.cs b/MareSynchronos/Models/CachedPlayer.cs index 144f145..5c2596e 100644 --- a/MareSynchronos/Models/CachedPlayer.cs +++ b/MareSynchronos/Models/CachedPlayer.cs @@ -140,8 +140,8 @@ public class CachedPlayer var tempCollection = _ipcManager.PenumbraCreateTemporaryCollection(PlayerName!); _dalamudUtil.WaitWhileCharacterIsDrawing(PlayerCharacter!.Address); RequestedPenumbraRedraw = true; - Logger.Warn( - $"Request Redraw for {PlayerName}: RequestedRedraws now {RequestedPenumbraRedraw}"); + Logger.Debug( + $"Request Redraw for {PlayerName}"); _ipcManager.PenumbraSetTemporaryMods(tempCollection, moddedPaths, cache.ManipulationData); _ipcManager.GlamourerRevertCharacterCustomization(PlayerName!); _ipcManager.GlamourerApplyAll(cache.GlamourerData, PlayerName!); @@ -205,14 +205,14 @@ public class CachedPlayer RequestedPenumbraRedraw = false; Logger.Debug( - $"Penumbra Redraw for {PlayerName}: RequestedRedraws now {RequestedPenumbraRedraw}"); + $"Penumbra Redraw done for {PlayerName}"); }); } private void WatcherOnPlayerChanged(Character actor) { if (actor.Name.ToString() != PlayerName) return; - Logger.Debug($"Player {PlayerName} changed, RequestedRedraws {RequestedPenumbraRedraw}"); + Logger.Debug($"Player {PlayerName} changed, PenumbraRedraw is {RequestedPenumbraRedraw}"); PlayerCharacter = _dalamudUtil.GetPlayerCharacterFromObjectTableByName(PlayerName!); if (PlayerCharacter is null) { diff --git a/MareSynchronos/Models/CharacterData.cs b/MareSynchronos/Models/CharacterData.cs index 5f81d90..83b0ee5 100644 --- a/MareSynchronos/Models/CharacterData.cs +++ b/MareSynchronos/Models/CharacterData.cs @@ -22,7 +22,7 @@ namespace MareSynchronos.Models [JsonProperty] public string CacheHash { get; set; } = string.Empty; - public List FileReplacements { get; set; } = new List(); + public List FileReplacements { get; set; } = new(); [JsonProperty] public string GlamourerString { get; set; } = string.Empty; @@ -91,7 +91,7 @@ namespace MareSynchronos.Models FileReplacements = AllReplacements.Select(f => f.ToFileReplacementDto()).ToList(), GlamourerData = GlamourerString, Hash = CacheHash, - JobId = (int)JobId, + JobId = JobId, ManipulationData = ManipulationString }; } diff --git a/MareSynchronos/Models/FileReplacement.cs b/MareSynchronos/Models/FileReplacement.cs index d749f3b..ffae5a0 100644 --- a/MareSynchronos/Models/FileReplacement.cs +++ b/MareSynchronos/Models/FileReplacement.cs @@ -24,7 +24,7 @@ namespace MareSynchronos.Models this._penumbraDirectory = penumbraDirectory; } - public List Associated { get; set; } = new List(); + public List Associated { get; set; } = new(); public bool Computed => (_computationTask == null || (_computationTask?.IsCompleted ?? true)) && Associated.All(f => f.Computed); diff --git a/MareSynchronos/UI/DownloadUi.cs b/MareSynchronos/UI/DownloadUi.cs index 4c4761e..2cc463b 100644 --- a/MareSynchronos/UI/DownloadUi.cs +++ b/MareSynchronos/UI/DownloadUi.cs @@ -29,8 +29,8 @@ public class DownloadUi : Window, IDisposable SizeConstraints = new WindowSizeConstraints() { - MaximumSize = new(300, 90), - MinimumSize = new(300, 90) + MaximumSize = new Vector2(300, 90), + MinimumSize = new Vector2(300, 90) }; Flags = ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoBackground; diff --git a/MareSynchronos/UI/IntroUI.cs b/MareSynchronos/UI/IntroUI.cs index 4bef6f9..a2871d4 100644 --- a/MareSynchronos/UI/IntroUI.cs +++ b/MareSynchronos/UI/IntroUI.cs @@ -1,4 +1,5 @@ using System; +using System.Numerics; using Dalamud.Interface.Colors; using Dalamud.Interface.Windowing; using ImGuiNET; @@ -36,8 +37,8 @@ namespace MareSynchronos.UI SizeConstraints = new WindowSizeConstraints() { - MinimumSize = new(600, 400), - MaximumSize = new(600, 2000) + MinimumSize = new Vector2(600, 400), + MaximumSize = new Vector2(600, 2000) }; _windowSystem.AddWindow(this); diff --git a/MareSynchronos/UI/PluginUI.cs b/MareSynchronos/UI/PluginUI.cs index 7f19351..5cf6926 100644 --- a/MareSynchronos/UI/PluginUI.cs +++ b/MareSynchronos/UI/PluginUI.cs @@ -26,8 +26,8 @@ namespace MareSynchronos.UI SizeConstraints = new WindowSizeConstraints() { - MinimumSize = new(800, 400), - MaximumSize = new(800, 2000), + MinimumSize = new Vector2(800, 400), + MaximumSize = new Vector2(800, 2000), }; _configuration = configuration; @@ -62,13 +62,13 @@ namespace MareSynchronos.UI } else { - if (!_uiShared.DrawOtherPluginState()) return; + var pluginState = _uiShared.DrawOtherPluginState(); - DrawSettingsContent(); + DrawSettingsContent(pluginState); } } - private void DrawSettingsContent() + private void DrawSettingsContent(bool pluginState) { _uiShared.PrintServerState(); ImGui.Separator(); @@ -84,86 +84,112 @@ namespace MareSynchronos.UI { ImGui.SetClipboardText(_apiController.UID); } + ImGui.Text("Share this UID to other Mare users so they pair their client with yours."); - ImGui.Separator(); - DrawPairedClientsContent(); - DrawFileCacheSettings(); - DrawCurrentTransfers(); - DrawAdministration(); } else { ImGui.TextColored(ImGuiColors.DalamudRed, "No UID (Service unavailable)"); ImGui.SetWindowFontScale(1.0f); } + + ImGui.Separator(); + if (_apiController.ServerAlive) + DrawPairedClientsContent(); + DrawFileCacheSettings(); + if (_apiController.ServerAlive) + DrawCurrentTransfers(); + DrawAdministration(_apiController.ServerAlive); + } private bool _deleteFilesPopupModalShown = false; private bool _deleteAccountPopupModalShown = false; - private void DrawAdministration() + private void DrawAdministration(bool serverAlive) { if (ImGui.TreeNode( $"User Administration")) { - if (ImGui.Button("Delete all my files")) + if (serverAlive) { - _deleteFilesPopupModalShown = true; - ImGui.OpenPopup("Delete all your files?"); - } - - if (ImGui.BeginPopupModal("Delete all your files?", ref _deleteFilesPopupModalShown, ImGuiWindowFlags.AlwaysAutoResize)) - { - UiShared.TextWrapped("All your own uploaded files on the service will be deleted.\nThis operation cannot be undone."); - ImGui.Text("Are you sure you want to continue?"); - ImGui.Separator(); - if (ImGui.Button("Delete everything", new Vector2(150, 0))) + if (ImGui.Button("Delete all my files")) { - Task.Run(() => _apiController.DeleteAllMyFiles()); - ImGui.CloseCurrentPopup(); - _deleteFilesPopupModalShown = false; + _deleteFilesPopupModalShown = true; + ImGui.OpenPopup("Delete all your files?"); } - ImGui.SameLine(); + UiShared.DrawHelpText("Completely deletes all your uploaded files on the service."); - if (ImGui.Button("Cancel##cancelDelete", new Vector2(150, 0))) + if (ImGui.BeginPopupModal("Delete all your files?", ref _deleteFilesPopupModalShown, + ImGuiWindowFlags.AlwaysAutoResize)) { - ImGui.CloseCurrentPopup(); - _deleteFilesPopupModalShown = false; + UiShared.TextWrapped( + "All your own uploaded files on the service will be deleted.\nThis operation cannot be undone."); + ImGui.Text("Are you sure you want to continue?"); + ImGui.Separator(); + if (ImGui.Button("Delete everything", new Vector2(150, 0))) + { + Task.Run(() => _apiController.DeleteAllMyFiles()); + ImGui.CloseCurrentPopup(); + _deleteFilesPopupModalShown = false; + } + + ImGui.SameLine(); + + if (ImGui.Button("Cancel##cancelDelete", new Vector2(150, 0))) + { + ImGui.CloseCurrentPopup(); + _deleteFilesPopupModalShown = false; + } + + ImGui.EndPopup(); } - ImGui.EndPopup(); + if (ImGui.Button("Delete account")) + { + _deleteAccountPopupModalShown = true; + ImGui.OpenPopup("Delete your account?"); + } + + UiShared.DrawHelpText("Completely deletes your account and all uploaded files to the service."); + + if (ImGui.BeginPopupModal("Delete your account?", ref _deleteAccountPopupModalShown, + ImGuiWindowFlags.AlwaysAutoResize)) + { + UiShared.TextWrapped( + "Your account and all associated files and data on the service will be deleted."); + UiShared.TextWrapped("Your UID will be removed from all pairing lists."); + ImGui.Text("Are you sure you want to continue?"); + ImGui.Separator(); + if (ImGui.Button("Delete account", new Vector2(150, 0))) + { + Task.Run(() => _apiController.DeleteAccount()); + ImGui.CloseCurrentPopup(); + _deleteAccountPopupModalShown = false; + } + + ImGui.SameLine(); + + if (ImGui.Button("Cancel##cancelDelete", new Vector2(150, 0))) + { + ImGui.CloseCurrentPopup(); + _deleteAccountPopupModalShown = false; + } + + ImGui.EndPopup(); + } } - if (ImGui.Button("Delete account")) + var marePaused = _configuration.FullPause; + if (ImGui.Checkbox("Pause Mare Synchronos", ref marePaused)) { - _deleteAccountPopupModalShown = true; - ImGui.OpenPopup("Delete your account?"); + _configuration.FullPause = marePaused; + _configuration.Save(); + _apiController.RestartHeartbeat(); } - if (ImGui.BeginPopupModal("Delete your account?", ref _deleteAccountPopupModalShown, ImGuiWindowFlags.AlwaysAutoResize)) - { - UiShared.TextWrapped("Your account and all associated files and data on the service will be deleted."); - UiShared.TextWrapped("Your UID will be removed from all pairing lists."); - ImGui.Text("Are you sure you want to continue?"); - ImGui.Separator(); - if (ImGui.Button("Delete account", new Vector2(150, 0))) - { - Task.Run(() => _apiController.DeleteAccount()); - ImGui.CloseCurrentPopup(); - _deleteAccountPopupModalShown = false; - } - - ImGui.SameLine(); - - if (ImGui.Button("Cancel##cancelDelete", new Vector2(150, 0))) - { - ImGui.CloseCurrentPopup(); - _deleteAccountPopupModalShown = false; - } - - ImGui.EndPopup(); - } + UiShared.DrawHelpText("Completely pauses the sync and clear your current data (not uploaded files) on the service."); ImGui.TreePop(); } diff --git a/MareSynchronos/UI/UIShared.cs b/MareSynchronos/UI/UIShared.cs index 12b5713..6fb25c6 100644 --- a/MareSynchronos/UI/UIShared.cs +++ b/MareSynchronos/UI/UIShared.cs @@ -183,6 +183,20 @@ namespace MareSynchronos.UI } } + public static void DrawHelpText(string helpText) + { + ImGui.SameLine(); + ImGui.TextDisabled("(?)"); + if (ImGui.IsItemHovered()) + { + ImGui.BeginTooltip(); + ImGui.PushTextWrapPos(ImGui.GetFontSize() * 35.0f); + ImGui.TextUnformatted(helpText); + ImGui.PopTextWrapPos(); + ImGui.EndTooltip(); + } + } + public void DrawCacheDirectorySetting() { var cacheDirectory = _pluginConfiguration.CacheFolder; diff --git a/MareSynchronos/WebAPI/ApiController.cs b/MareSynchronos/WebAPI/ApiController.cs index faf42a2..76099f2 100644 --- a/MareSynchronos/WebAPI/ApiController.cs +++ b/MareSynchronos/WebAPI/ApiController.cs @@ -74,7 +74,6 @@ namespace MareSynchronos.WebAPI } private string ApiUri => UseCustomService ? _pluginConfiguration.ApiUri : MainServiceUri; - private string CacheFolder => _pluginConfiguration.CacheFolder; public void CancelUpload() { if (_uploadCancellationTokenSource != null) @@ -174,25 +173,9 @@ namespace MareSynchronos.WebAPI { try { + if (_pluginConfiguration.FullPause) return; Logger.Debug("Attempting to establish heartbeat connection to " + ApiUri); - _heartbeatHub = new HubConnectionBuilder() - .WithUrl(ApiUri + "/heartbeat", options => - { - if (!string.IsNullOrEmpty(SecretKey)) - { - options.Headers.Add("Authorization", SecretKey); - } - -#if DEBUG - options.HttpMessageHandlerFactory = (message) => - { - if (message is HttpClientHandler clientHandler) - clientHandler.ServerCertificateCustomValidationCallback += - (_, _, _, _) => true; - return message; - }; -#endif - }).Build(); + _heartbeatHub = BuildHubConnection("heartbeat"); await _heartbeatHub.StartAsync(_cts.Token); UID = await _heartbeatHub!.InvokeAsync("Heartbeat"); @@ -240,13 +223,18 @@ namespace MareSynchronos.WebAPI { Logger.Debug("Restarting heartbeat"); - _heartbeatHub!.Closed -= OnHeartbeatHubOnClosed; - _heartbeatHub!.Reconnected -= OnHeartbeatHubOnReconnected; Task.Run(async () => { - await _heartbeatHub.StopAsync(_cts.Token); - await _heartbeatHub.DisposeAsync(); - _heartbeatHub = null!; + if (_heartbeatHub != null) + { + _heartbeatHub.Closed -= OnHeartbeatHubOnClosed; + _heartbeatHub.Reconnected -= OnHeartbeatHubOnReconnected; + await _heartbeatHub.StopAsync(_cts.Token); + await _heartbeatHub.DisposeAsync(); + await OnHeartbeatHubOnClosed(null); + _heartbeatHub = null!; + } + _ = Heartbeat(); }); } @@ -335,11 +323,6 @@ namespace MareSynchronos.WebAPI await _userHub!.SendAsync("SendPairedClientRemoval", uid); } - public async Task UpdateCurrentDownloadSize(string hash) - { - long fileSize = await _fileHub!.InvokeAsync("GetFileSize", hash); - } - public async Task DeleteAllMyFiles() { await _fileHub!.SendAsync("DeleteAllFiles"); @@ -362,6 +345,7 @@ namespace MareSynchronos.WebAPI CancelUpload(); await _fileHub!.StopAsync(); await _fileHub!.DisposeAsync(); + _fileHub = null; } if (_userHub != null) @@ -369,6 +353,7 @@ namespace MareSynchronos.WebAPI Logger.Debug("Disposing User Hub"); await _userHub.StopAsync(); await _userHub.DisposeAsync(); + _userHub = null; } } @@ -385,21 +370,7 @@ namespace MareSynchronos.WebAPI await DisposeHubConnections(); Logger.Debug("Creating User Hub"); - _userHub = new HubConnectionBuilder() - .WithUrl(ApiUri + "/user", options => - { - options.Headers.Add("Authorization", SecretKey); -#if DEBUG - options.HttpMessageHandlerFactory = (message) => - { - if (message is HttpClientHandler clientHandler) - clientHandler.ServerCertificateCustomValidationCallback += - (sender, certificate, chain, sslPolicyErrors) => true; - return message; - }; -#endif - }) - .Build(); + _userHub = BuildHubConnection("user"); await _userHub.StartAsync(); _userHub.On("UpdateClientPairs", UpdateLocalClientPairs); _userHub.On("ReceiveCharacterData", ReceiveCharacterData); @@ -407,10 +378,19 @@ namespace MareSynchronos.WebAPI _userHub.On("AddOnlinePairedPlayer", (s) => PairedClientOnline?.Invoke(s, EventArgs.Empty)); Logger.Debug("Creating File Hub"); - _fileHub = new HubConnectionBuilder() - .WithUrl(ApiUri + "/files", options => + _fileHub = BuildHubConnection("files"); + await _fileHub.StartAsync(_cts.Token); + } + + private HubConnection BuildHubConnection(string hubName) + { + return new HubConnectionBuilder() + .WithUrl(ApiUri + "/" + hubName, options => { - options.Headers.Add("Authorization", SecretKey); + if (!string.IsNullOrEmpty(SecretKey) && !_pluginConfiguration.FullPause) + { + options.Headers.Add("Authorization", SecretKey); + } #if DEBUG options.HttpMessageHandlerFactory = (message) => { @@ -422,7 +402,6 @@ namespace MareSynchronos.WebAPI #endif }) .Build(); - await _fileHub.StartAsync(_cts.Token); } private async Task LoadInitialData() @@ -435,6 +414,7 @@ namespace MareSynchronos.WebAPI { Logger.Debug("Connection closed: " + ApiUri); Disconnected?.Invoke(null, EventArgs.Empty); + Task.Run(DisposeHubConnections); RestartHeartbeat(); return Task.CompletedTask; }