rework the whole connection garbage/custom servers, other cleanups/refactors
This commit is contained in:
		| @@ -25,6 +25,7 @@ namespace MareSynchronos | ||||
|  | ||||
|         public string CacheFolder { get; set; } = string.Empty; | ||||
|         public Dictionary<string, string> ClientSecret { get; set; } = new(); | ||||
|         public Dictionary<string, string> CustomServerList { get; set; } = new(); | ||||
|         [JsonIgnore] | ||||
|         public bool HasValidSetup => AcceptedAgreement && InitialScanComplete && !string.IsNullOrEmpty(CacheFolder) && | ||||
|                                      Directory.Exists(CacheFolder) && ClientSecret.ContainsKey(ApiUri); | ||||
| @@ -46,7 +47,6 @@ namespace MareSynchronos | ||||
|  | ||||
|         public bool FullPause { get; set; } = false; | ||||
|         public Dictionary<string, string> UidComments { get; set; } = new(); | ||||
|         public bool UseCustomService { get; set; } = false; | ||||
|         public int Version { get; set; } = 0; | ||||
|  | ||||
|         public bool ShowTransferWindow { get; set; } = true; | ||||
|   | ||||
| @@ -4,12 +4,9 @@ using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
| using Dalamud.Game; | ||||
| using Dalamud.Game.ClientState; | ||||
| using Dalamud.Game.ClientState.Objects; | ||||
| using Dalamud.Game.ClientState.Objects.SubKinds; | ||||
| using MareSynchronos.Models; | ||||
| using MareSynchronos.Utils; | ||||
| using MareSynchronos.WebAPI; | ||||
| using Penumbra.PlayerWatch; | ||||
|  | ||||
| namespace MareSynchronos.Managers; | ||||
|  | ||||
| @@ -20,23 +17,20 @@ public class CachedPlayersManager : IDisposable | ||||
|     private readonly DalamudUtil _dalamudUtil; | ||||
|     private readonly Framework _framework; | ||||
|     private readonly IpcManager _ipcManager; | ||||
|     private readonly IPlayerWatcher _watcher; | ||||
|     private readonly ObjectTable _objectTable; | ||||
|     private readonly List<CachedPlayer> _onlineCachedPlayers = new(); | ||||
|     private readonly List<string> _localVisiblePlayers = new(); | ||||
|     private DateTime _lastPlayerObjectCheck = DateTime.Now; | ||||
|  | ||||
|     public CachedPlayersManager(ClientState clientState, Framework framework, ObjectTable objectTable, ApiController apiController, DalamudUtil dalamudUtil, IpcManager ipcManager, IPlayerWatcher watcher) | ||||
|     public CachedPlayersManager(ClientState clientState, Framework framework, | ||||
|         ApiController apiController, DalamudUtil dalamudUtil, IpcManager ipcManager) | ||||
|     { | ||||
|         Logger.Debug("Creating " + nameof(CachedPlayersManager)); | ||||
|  | ||||
|         _clientState = clientState; | ||||
|         _framework = framework; | ||||
|         _objectTable = objectTable; | ||||
|         _apiController = apiController; | ||||
|         _dalamudUtil = dalamudUtil; | ||||
|         _ipcManager = ipcManager; | ||||
|         _watcher = watcher; | ||||
|  | ||||
|         _clientState.Login += ClientStateOnLogin; | ||||
|         _clientState.Logout += ClientStateOnLogout; | ||||
| @@ -159,14 +153,10 @@ public class CachedPlayersManager : IDisposable | ||||
|         if (DateTime.Now < _lastPlayerObjectCheck.AddSeconds(0.25)) return; | ||||
|  | ||||
|         _localVisiblePlayers.Clear(); | ||||
|         string ownName = _dalamudUtil.PlayerName; | ||||
|         var playerCharacters = _objectTable.Where(obj => | ||||
|             obj.ObjectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player && | ||||
|             obj.Name.ToString() != ownName).ToList(); | ||||
|         foreach (var obj in playerCharacters) | ||||
|         var playerCharacters = _dalamudUtil.GetPlayerCharacters(); | ||||
|         foreach (var pChar in playerCharacters) | ||||
|         { | ||||
|             var pObj = (PlayerCharacter)obj; | ||||
|             var pObjName = pObj.Name.ToString(); | ||||
|             var pObjName = pChar.Name.ToString(); | ||||
|             _localVisiblePlayers.Add(pObjName); | ||||
|             var existingCachedPlayer = _onlineCachedPlayers.SingleOrDefault(p => p.PlayerName == pObjName); | ||||
|             if (existingCachedPlayer != null) | ||||
| @@ -175,8 +165,8 @@ public class CachedPlayersManager : IDisposable | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             var hashedName = Crypto.GetHash256(pObj.Name.ToString() + pObj.HomeWorld.Id.ToString()); | ||||
|             _onlineCachedPlayers.SingleOrDefault(p => p.PlayerNameHash == hashedName)?.InitializePlayer(pObj); | ||||
|             var hashedName = Crypto.GetHash256(pChar); | ||||
|             _onlineCachedPlayers.SingleOrDefault(p => p.PlayerNameHash == hashedName)?.InitializePlayer(pChar); | ||||
|         } | ||||
|  | ||||
|         _onlineCachedPlayers.Where(p => !string.IsNullOrEmpty(p.PlayerName) && !_localVisiblePlayers.Contains(p.PlayerName)) | ||||
| @@ -191,6 +181,6 @@ public class CachedPlayersManager : IDisposable | ||||
|  | ||||
|     private CachedPlayer CreateCachedPlayer(string hashedName) | ||||
|     { | ||||
|         return new CachedPlayer(hashedName, _ipcManager, _apiController, _dalamudUtil, _watcher); | ||||
|         return new CachedPlayer(hashedName, _ipcManager, _apiController, _dalamudUtil); | ||||
|     } | ||||
| } | ||||
| @@ -34,13 +34,11 @@ namespace MareSynchronos.Managers | ||||
|  | ||||
|         private void StartWatchersAndScan() | ||||
|         { | ||||
|             if (_ipcManager.Initialized && _pluginConfiguration.HasValidSetup) | ||||
|             { | ||||
|             if (!_ipcManager.Initialized || !_pluginConfiguration.HasValidSetup) return; | ||||
|             Logger.Debug("Penumbra is active, configuration is valid, starting watchers and scan"); | ||||
|             StartWatchers(); | ||||
|             StartInitialScan(); | ||||
|         } | ||||
|         } | ||||
|  | ||||
|         private void IpcManagerOnPenumbraInitialized(object? sender, EventArgs e) | ||||
|         { | ||||
|   | ||||
| @@ -146,7 +146,9 @@ namespace MareSynchronos.Managers | ||||
|         { | ||||
|             if (!CheckPenumbraApi()) return string.Empty; | ||||
|             Logger.Debug("Creating temp collection for " + characterName); | ||||
|             return _penumbraCreateTemporaryCollection.InvokeFunc("MareSynchronos", characterName, true).Item2; | ||||
|             var ret = _penumbraCreateTemporaryCollection.InvokeFunc("MareSynchronos", characterName, true); | ||||
|             Logger.Debug("Penumbra ret: " + ret.Item1); | ||||
|             return ret.Item2; | ||||
|         } | ||||
|  | ||||
|         public string PenumbraGetMetaManipulations(string characterName) | ||||
|   | ||||
| @@ -4,7 +4,6 @@ using MareSynchronos.Models; | ||||
| using MareSynchronos.Utils; | ||||
| using MareSynchronos.WebAPI; | ||||
| using Newtonsoft.Json; | ||||
| using Penumbra.PlayerWatch; | ||||
| using System; | ||||
| using System.Diagnostics; | ||||
| using System.Linq; | ||||
| @@ -20,12 +19,11 @@ namespace MareSynchronos.Managers | ||||
|         private readonly CharacterDataFactory _characterDataFactory; | ||||
|         private readonly DalamudUtil _dalamudUtil; | ||||
|         private readonly IpcManager _ipcManager; | ||||
|         private readonly IPlayerWatcher _watcher; | ||||
|         private string _lastSentHash = string.Empty; | ||||
|         private Task? _playerChangedTask; | ||||
|  | ||||
|         public PlayerManager(ApiController apiController, IpcManager ipcManager, | ||||
|             CharacterDataFactory characterDataFactory, CachedPlayersManager cachedPlayersManager, DalamudUtil dalamudUtil, IPlayerWatcher watcher) | ||||
|             CharacterDataFactory characterDataFactory, CachedPlayersManager cachedPlayersManager, DalamudUtil dalamudUtil) | ||||
|         { | ||||
|             Logger.Debug("Creating " + nameof(PlayerManager)); | ||||
|  | ||||
| @@ -34,9 +32,8 @@ namespace MareSynchronos.Managers | ||||
|             _characterDataFactory = characterDataFactory; | ||||
|             _cachedPlayersManager = cachedPlayersManager; | ||||
|             _dalamudUtil = dalamudUtil; | ||||
|             _watcher = watcher; | ||||
|  | ||||
|             _watcher.AddPlayerToWatch(_dalamudUtil.PlayerName); | ||||
|             _dalamudUtil.AddPlayerToWatch(_dalamudUtil.PlayerName); | ||||
|             _apiController.Connected += ApiController_Connected; | ||||
|             _apiController.Disconnected += ApiController_Disconnected; | ||||
|  | ||||
| @@ -54,7 +51,7 @@ namespace MareSynchronos.Managers | ||||
|             _ipcManager.PenumbraRedrawEvent -= IpcManager_PenumbraRedrawEvent; | ||||
|             _apiController.Connected -= ApiController_Connected; | ||||
|             _apiController.Disconnected -= ApiController_Disconnected; | ||||
|             _watcher.PlayerChanged -= Watcher_PlayerChanged; | ||||
|             _dalamudUtil.PlayerChanged -= Watcher_PlayerChanged; | ||||
|         } | ||||
|  | ||||
|         private void ApiController_Connected(object? sender, EventArgs args) | ||||
| @@ -69,7 +66,7 @@ namespace MareSynchronos.Managers | ||||
|  | ||||
|             _ipcManager.PenumbraRedrawEvent += IpcManager_PenumbraRedrawEvent; | ||||
|             _ipcManager.PenumbraRedraw(_dalamudUtil.PlayerName); | ||||
|             _watcher.PlayerChanged += Watcher_PlayerChanged; | ||||
|             _dalamudUtil.PlayerChanged += Watcher_PlayerChanged; | ||||
|         } | ||||
|  | ||||
|         private void ApiController_Disconnected(object? sender, EventArgs args) | ||||
| @@ -77,7 +74,7 @@ namespace MareSynchronos.Managers | ||||
|             Logger.Debug(nameof(ApiController_Disconnected)); | ||||
|  | ||||
|             _ipcManager.PenumbraRedrawEvent -= IpcManager_PenumbraRedrawEvent; | ||||
|             _watcher.PlayerChanged -= Watcher_PlayerChanged; | ||||
|             _dalamudUtil.PlayerChanged -= Watcher_PlayerChanged; | ||||
|         } | ||||
|  | ||||
|         private async Task<CharacterData> CreateFullCharacterCache() | ||||
|   | ||||
| @@ -11,7 +11,6 @@ using MareSynchronos.FileCacheDB; | ||||
| using MareSynchronos.Managers; | ||||
| using MareSynchronos.Utils; | ||||
| using MareSynchronos.WebAPI; | ||||
| using Penumbra.PlayerWatch; | ||||
|  | ||||
| namespace MareSynchronos.Models; | ||||
|  | ||||
| @@ -20,16 +19,14 @@ public class CachedPlayer | ||||
|     private readonly DalamudUtil _dalamudUtil; | ||||
|     private readonly IpcManager _ipcManager; | ||||
|     private readonly ApiController _apiController; | ||||
|     private readonly IPlayerWatcher _watcher; | ||||
|     private bool _isVisible; | ||||
|  | ||||
|     public CachedPlayer(string nameHash, IpcManager ipcManager, ApiController apiController, DalamudUtil dalamudUtil, IPlayerWatcher watcher) | ||||
|     public CachedPlayer(string nameHash, IpcManager ipcManager, ApiController apiController, DalamudUtil dalamudUtil) | ||||
|     { | ||||
|         PlayerNameHash = nameHash; | ||||
|         _ipcManager = ipcManager; | ||||
|         _apiController = apiController; | ||||
|         _dalamudUtil = dalamudUtil; | ||||
|         _watcher = watcher; | ||||
|     } | ||||
|  | ||||
|     public bool IsVisible | ||||
| @@ -165,7 +162,7 @@ public class CachedPlayer | ||||
|             _downloadCancellationTokenSource?.Cancel(); | ||||
|             _downloadCancellationTokenSource?.Dispose(); | ||||
|             _downloadCancellationTokenSource = null; | ||||
|             _watcher.RemovePlayerFromWatch(PlayerName); | ||||
|             _dalamudUtil.RemovePlayerFromWatch(PlayerName); | ||||
|             _ipcManager.PenumbraRemoveTemporaryCollection(PlayerName); | ||||
|             _ipcManager.GlamourerRevertCharacterCustomization(PlayerName); | ||||
|             _ipcManager.GlamourerApplyOnlyCustomization(_originalGlamourerData, PlayerName); | ||||
| @@ -177,7 +174,7 @@ public class CachedPlayer | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             _watcher.PlayerChanged -= WatcherOnPlayerChanged; | ||||
|             _dalamudUtil.PlayerChanged -= WatcherOnPlayerChanged; | ||||
|             _ipcManager.PenumbraRedrawEvent -= IpcManagerOnPenumbraRedrawEvent; | ||||
|             _apiController.CharacterReceived -= ApiControllerOnCharacterReceived; | ||||
|             PlayerName = string.Empty; | ||||
| @@ -191,8 +188,8 @@ public class CachedPlayer | ||||
|         PlayerName = character.Name.ToString(); | ||||
|         PlayerCharacter = character; | ||||
|         Logger.Debug("Initializing Player " + this); | ||||
|         _watcher.AddPlayerToWatch(PlayerName!); | ||||
|         _watcher.PlayerChanged += WatcherOnPlayerChanged; | ||||
|         _dalamudUtil.AddPlayerToWatch(PlayerName!); | ||||
|         _dalamudUtil.PlayerChanged += WatcherOnPlayerChanged; | ||||
|         _ipcManager.PenumbraRedrawEvent += IpcManagerOnPenumbraRedrawEvent; | ||||
|         _apiController.CharacterReceived += ApiControllerOnCharacterReceived; | ||||
|         _originalGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(PlayerName); | ||||
| @@ -203,15 +200,15 @@ public class CachedPlayer | ||||
|         return PlayerNameHash + ":" + PlayerName + ":HasChar " + (PlayerCharacter != null); | ||||
|     } | ||||
|  | ||||
|     private Task? penumbraRedrawEventTask; | ||||
|     private Task? _penumbraRedrawEventTask; | ||||
|  | ||||
|     private void IpcManagerOnPenumbraRedrawEvent(object? sender, EventArgs e) | ||||
|     { | ||||
|         var player = _dalamudUtil.GetPlayerCharacterFromObjectTableIndex((int)sender!); | ||||
|         if (player == null || player.Name.ToString() != PlayerName) return; | ||||
|         if (!penumbraRedrawEventTask?.IsCompleted ?? false) return; | ||||
|         if (!_penumbraRedrawEventTask?.IsCompleted ?? false) return; | ||||
|  | ||||
|         penumbraRedrawEventTask = Task.Run(() => | ||||
|         _penumbraRedrawEventTask = Task.Run(() => | ||||
|         { | ||||
|             PlayerCharacter = player; | ||||
|             _dalamudUtil.WaitWhileCharacterIsDrawing(PlayerCharacter.Address); | ||||
|   | ||||
| @@ -28,14 +28,12 @@ namespace MareSynchronos | ||||
|         private readonly FileCacheManager _fileCacheManager; | ||||
|         private readonly IntroUi _introUi; | ||||
|         private readonly IpcManager _ipcManager; | ||||
|         private readonly ObjectTable _objectTable; | ||||
|         public static DalamudPluginInterface PluginInterface { get; set; } | ||||
|         private readonly PluginUi _pluginUi; | ||||
|         private readonly WindowSystem _windowSystem; | ||||
|         private PlayerManager? _playerManager; | ||||
|         private readonly DalamudUtil _dalamudUtil; | ||||
|         private CachedPlayersManager? _characterCacheManager; | ||||
|         private readonly IPlayerWatcher _playerWatcher; | ||||
|         private readonly DownloadUi _downloadUi; | ||||
|         private readonly FileDialogManager _fileDialogManager; | ||||
|  | ||||
| @@ -46,7 +44,6 @@ namespace MareSynchronos | ||||
|             PluginInterface = pluginInterface; | ||||
|             _commandManager = commandManager; | ||||
|             _framework = framework; | ||||
|             _objectTable = objectTable; | ||||
|             _clientState = clientState; | ||||
|             _configuration = PluginInterface.GetPluginConfig() as Configuration ?? new Configuration(); | ||||
|             _configuration.Initialize(PluginInterface); | ||||
| @@ -74,13 +71,11 @@ namespace MareSynchronos | ||||
|             }; | ||||
|             _downloadUi = new DownloadUi(_windowSystem, _configuration, _apiController); | ||||
|  | ||||
|             _dalamudUtil = new DalamudUtil(_clientState, _objectTable); | ||||
|             _playerWatcher = PlayerWatchFactory.Create(framework, _clientState, _objectTable); | ||||
|             _playerWatcher.Enable(); | ||||
|             _dalamudUtil = new DalamudUtil(_clientState, objectTable, PlayerWatchFactory.Create(framework, _clientState, objectTable)); | ||||
|  | ||||
|             clientState.Login += ClientState_Login; | ||||
|             clientState.Logout += ClientState_Logout; | ||||
|             _apiController.AccountDeleted += ApiControllerOnAccountDeleted; | ||||
|             _apiController.ChangingServers += ApiControllerOnChangingServers; | ||||
|  | ||||
|             if (clientState.IsLoggedIn) | ||||
|             { | ||||
| @@ -88,19 +83,17 @@ namespace MareSynchronos | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private void ApiControllerOnAccountDeleted(object? sender, EventArgs e) | ||||
|         private void ApiControllerOnChangingServers(object? sender, EventArgs e) | ||||
|         { | ||||
|             _pluginUi.IsOpen = false; | ||||
|             _introUi.IsOpen = true; | ||||
|             _characterCacheManager?.Dispose(); | ||||
|             _playerManager?.Dispose(); | ||||
|         } | ||||
|  | ||||
|         public string Name => "Mare Synchronos"; | ||||
|         public void Dispose() | ||||
|         { | ||||
|             Logger.Debug("Disposing " + Name); | ||||
|             _apiController.AccountDeleted -= ApiControllerOnAccountDeleted; | ||||
|             _apiController.ChangingServers -= ApiControllerOnChangingServers; | ||||
|             _apiController?.Dispose(); | ||||
|  | ||||
|             _commandManager.RemoveHandler(CommandName); | ||||
| @@ -115,8 +108,7 @@ namespace MareSynchronos | ||||
|             _ipcManager?.Dispose(); | ||||
|             _playerManager?.Dispose(); | ||||
|             _characterCacheManager?.Dispose(); | ||||
|             _playerWatcher.Disable(); | ||||
|             _playerWatcher.Dispose(); | ||||
|             _dalamudUtil.Dispose(); | ||||
|         } | ||||
|  | ||||
|  | ||||
| @@ -152,10 +144,13 @@ namespace MareSynchronos | ||||
|  | ||||
|         public void ReLaunchCharacterManager() | ||||
|         { | ||||
|             _playerManager?.Dispose(); | ||||
|             _characterCacheManager?.Dispose(); | ||||
|             _playerManager?.Dispose(); | ||||
|  | ||||
|             Task.Run(async () => | ||||
|             Task.Run(WaitForPlayerAndLaunchCharacterManager); | ||||
|         } | ||||
|  | ||||
|         private async Task WaitForPlayerAndLaunchCharacterManager() | ||||
|         { | ||||
|             while (!_dalamudUtil.IsPlayerPresent) | ||||
|             { | ||||
| @@ -166,16 +161,15 @@ namespace MareSynchronos | ||||
|             { | ||||
|                 var characterCacheFactory = | ||||
|                     new CharacterDataFactory(_dalamudUtil, _ipcManager); | ||||
|                     _characterCacheManager = new CachedPlayersManager(_clientState, _framework, _objectTable, | ||||
|                         _apiController, _dalamudUtil, _ipcManager, _playerWatcher); | ||||
|                 _characterCacheManager = new CachedPlayersManager(_clientState, _framework, | ||||
|                     _apiController, _dalamudUtil, _ipcManager); | ||||
|                 _playerManager = new PlayerManager(_apiController, _ipcManager, | ||||
|                         characterCacheFactory, _characterCacheManager, _dalamudUtil, _playerWatcher); | ||||
|                     characterCacheFactory, _characterCacheManager, _dalamudUtil); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 Logger.Debug(ex.Message); | ||||
|             } | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         private void Draw() | ||||
|   | ||||
| @@ -51,31 +51,20 @@ namespace MareSynchronos.UI | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             if (_apiController.SecretKey != "-" && !_apiController.IsConnected && _apiController.ServerAlive) | ||||
|             { | ||||
|                 if (ImGui.Button("Reset Secret Key")) | ||||
|                 { | ||||
|                     _configuration.ClientSecret.Clear(); | ||||
|                     _configuration.Save(); | ||||
|                     _apiController.RestartHeartbeat(); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|             var pluginState = _uiShared.DrawOtherPluginState(); | ||||
|  | ||||
|                 DrawSettingsContent(pluginState); | ||||
|             } | ||||
|             if (pluginState) | ||||
|                 DrawSettingsContent(); | ||||
|         } | ||||
|  | ||||
|         private void DrawSettingsContent(bool pluginState) | ||||
|         private void DrawSettingsContent() | ||||
|         { | ||||
|             _uiShared.PrintServerState(); | ||||
|             ImGui.Separator(); | ||||
|             ImGui.SetWindowFontScale(1.2f); | ||||
|             ImGui.Text("Your UID"); | ||||
|             ImGui.SameLine(); | ||||
|             if (_apiController.ServerAlive) | ||||
|             if (_apiController.IsConnected) | ||||
|             { | ||||
|                 ImGui.TextColored(ImGuiColors.ParsedGreen, _apiController.UID); | ||||
|                 ImGui.SameLine(); | ||||
| @@ -89,18 +78,18 @@ namespace MareSynchronos.UI | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ImGui.TextColored(ImGuiColors.DalamudRed, "No UID (Service unavailable)"); | ||||
|                 string error = _configuration.FullPause ? "Fully Paused" : "Service unavailable"; | ||||
|                 ImGui.TextColored(ImGuiColors.DalamudRed, $"No UID ({error})"); | ||||
|                 ImGui.SetWindowFontScale(1.0f); | ||||
|             } | ||||
|  | ||||
|             ImGui.Separator(); | ||||
|             if (_apiController.ServerAlive) | ||||
|             if (_apiController.IsConnected) | ||||
|                 DrawPairedClientsContent(); | ||||
|             DrawFileCacheSettings(); | ||||
|             if (_apiController.ServerAlive) | ||||
|             if (_apiController.IsConnected) | ||||
|                 DrawCurrentTransfers(); | ||||
|             DrawAdministration(_apiController.ServerAlive); | ||||
|  | ||||
|             DrawAdministration(_apiController.IsConnected); | ||||
|         } | ||||
|  | ||||
|         private bool _deleteFilesPopupModalShown = false; | ||||
| @@ -181,15 +170,39 @@ namespace MareSynchronos.UI | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 if (!_configuration.FullPause) | ||||
|                 { | ||||
|                     ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); | ||||
|                     UiShared.TextWrapped("Note: to change servers you need to pause Mare Synchronos."); | ||||
|                     ImGui.PopStyleColor(); | ||||
|                 } | ||||
|  | ||||
|                 var marePaused = _configuration.FullPause; | ||||
|  | ||||
|                 if (_configuration.HasValidSetup) | ||||
|                 { | ||||
|                     if (ImGui.Checkbox("Pause Mare Synchronos", ref marePaused)) | ||||
|                     { | ||||
|                         _configuration.FullPause = marePaused; | ||||
|                         _configuration.Save(); | ||||
|                     _apiController.RestartHeartbeat(); | ||||
|                         Task.Run(_apiController.CreateConnections); | ||||
|                     } | ||||
|  | ||||
|                     UiShared.DrawHelpText("Completely pauses the sync and clear your current data (not uploaded files) on the service."); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); | ||||
|                     ImGui.TextUnformatted("You cannot resume pause without a valid account on the service."); | ||||
|                     ImGui.PopStyleColor(); | ||||
|                 } | ||||
|  | ||||
|  | ||||
|                 if (marePaused) | ||||
|                 { | ||||
|                     _uiShared.DrawServiceSelection(); | ||||
|                 } | ||||
|  | ||||
|  | ||||
|                 ImGui.TreePop(); | ||||
|             } | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| using System; | ||||
| using System.IO; | ||||
| using System.Linq; | ||||
| using System.Numerics; | ||||
| using System.Threading.Tasks; | ||||
| using Dalamud.Interface; | ||||
| @@ -74,7 +75,10 @@ namespace MareSynchronos.UI | ||||
|  | ||||
|         public void PrintServerState() | ||||
|         { | ||||
|             ImGui.Text("Service status of " + (string.IsNullOrEmpty(_pluginConfiguration.ApiUri) ? ApiController.MainServer : _pluginConfiguration.ApiUri)); | ||||
|             var serverName = _apiController.ServerDictionary.ContainsKey(_pluginConfiguration.ApiUri) | ||||
|                 ? _apiController.ServerDictionary[_pluginConfiguration.ApiUri] | ||||
|                 : _pluginConfiguration.ApiUri; | ||||
|             ImGui.Text("Service status of " + serverName); | ||||
|             ImGui.SameLine(); | ||||
|             var color = _apiController.ServerAlive ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; | ||||
|             ImGui.TextColored(color, _apiController.ServerAlive ? "Available" : "Unavailable"); | ||||
| @@ -132,59 +136,91 @@ namespace MareSynchronos.UI | ||||
|         } | ||||
|  | ||||
|         private int _serverSelectionIndex = 0; | ||||
|         private string _customServerName = ""; | ||||
|         private string _customServerUri = ""; | ||||
|  | ||||
|         public void DrawServiceSelection() | ||||
|         { | ||||
|             string[] comboEntries = new[] { ApiController.MainServer, "Custom Service" }; | ||||
|             if (ImGui.BeginCombo("Service", comboEntries[_serverSelectionIndex])) | ||||
|             string[] comboEntries = _apiController.ServerDictionary.Values.ToArray(); | ||||
|             _serverSelectionIndex = Array.IndexOf(_apiController.ServerDictionary.Keys.ToArray(), _pluginConfiguration.ApiUri); | ||||
|             if (ImGui.BeginCombo("Select Service", comboEntries[_serverSelectionIndex])) | ||||
|             { | ||||
|                 for (int n = 0; n < comboEntries.Length; n++) | ||||
|                 for (int i = 0; i < comboEntries.Length; i++) | ||||
|                 { | ||||
|                     bool isSelected = _serverSelectionIndex == n; | ||||
|                     if (ImGui.Selectable(comboEntries[n], isSelected)) | ||||
|                     bool isSelected = _serverSelectionIndex == i; | ||||
|                     if (ImGui.Selectable(comboEntries[i], isSelected)) | ||||
|                     { | ||||
|                         _serverSelectionIndex = n; | ||||
|                         _pluginConfiguration.ApiUri = _apiController.ServerDictionary.Single(k => k.Value == comboEntries[i]).Key; | ||||
|                         _pluginConfiguration.Save(); | ||||
|                     } | ||||
|  | ||||
|                     if (isSelected) | ||||
|                     { | ||||
|                         ImGui.SetItemDefaultFocus(); | ||||
|                     } | ||||
|  | ||||
|                     bool useCustomService = _serverSelectionIndex != 0; | ||||
|  | ||||
|                     if (_apiController.UseCustomService != useCustomService) | ||||
|                     { | ||||
|                         _apiController.UseCustomService = useCustomService; | ||||
|                         _pluginConfiguration.Save(); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 ImGui.EndCombo(); | ||||
|             } | ||||
|  | ||||
|             if (_apiController.UseCustomService) | ||||
|             if (_serverSelectionIndex != 0) | ||||
|             { | ||||
|                 string serviceAddress = _pluginConfiguration.ApiUri; | ||||
|                 if (ImGui.InputText("Service address", ref serviceAddress, 255)) | ||||
|                 ImGui.SameLine(); | ||||
|                 ImGui.PushFont(UiBuilder.IconFont); | ||||
|                 if (ImGui.Button(FontAwesomeIcon.Trash.ToIconString() + "##deleteService")) | ||||
|                 { | ||||
|                     if (_pluginConfiguration.ApiUri != serviceAddress) | ||||
|                     { | ||||
|                         _pluginConfiguration.ApiUri = serviceAddress; | ||||
|                         _apiController.RestartHeartbeat(); | ||||
|                     _pluginConfiguration.CustomServerList.Remove(_pluginConfiguration.ApiUri); | ||||
|                     _pluginConfiguration.ApiUri = ApiController.MainServiceUri; | ||||
|                     _pluginConfiguration.Save(); | ||||
|                 } | ||||
|                 } | ||||
|                 ImGui.PopFont(); | ||||
|             } | ||||
|  | ||||
|             PrintServerState(); | ||||
|             if (_apiController.ServerAlive) | ||||
|  | ||||
|             if (_apiController.ServerAlive && !_pluginConfiguration.ClientSecret.ContainsKey(_pluginConfiguration.ApiUri)) | ||||
|             { | ||||
|                 if (ImGui.Button("Register")) | ||||
|                 { | ||||
|                     _pluginConfiguration.FullPause = false; | ||||
|                     _pluginConfiguration.Save(); | ||||
|                     Task.WaitAll(_apiController.Register()); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); | ||||
|                 TextWrapped("You already have an account on this server."); | ||||
|                 ImGui.PopStyleColor(); | ||||
|                 ImGui.SameLine(); | ||||
|                 if (ImGui.Button("Connect##connectToService")) | ||||
|                 { | ||||
|                     _pluginConfiguration.FullPause = false; | ||||
|                     _pluginConfiguration.Save(); | ||||
|                     Task.Run(_apiController.CreateConnections); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (ImGui.TreeNode("Custom Service")) | ||||
|             { | ||||
|                 ImGui.SetNextItemWidth(250); | ||||
|                 ImGui.InputText("Custom Service Name", ref _customServerName, 255); | ||||
|                 ImGui.SetNextItemWidth(250); | ||||
|                 ImGui.InputText("Custom Service Address", ref _customServerUri, 255); | ||||
|                 if (ImGui.Button("Add Custom Service")) | ||||
|                 { | ||||
|                     if (!string.IsNullOrEmpty(_customServerUri)  | ||||
|                         && !string.IsNullOrEmpty(_customServerName) | ||||
|                         && !_pluginConfiguration.CustomServerList.ContainsValue(_customServerName)) | ||||
|                     { | ||||
|                         _pluginConfiguration.CustomServerList[_customServerUri] = _customServerName; | ||||
|                         _customServerUri = string.Empty; | ||||
|                         _customServerName = string.Empty; | ||||
|                         _pluginConfiguration.Save(); | ||||
|                     } | ||||
|                 } | ||||
|                 ImGui.TreePop(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public static void DrawHelpText(string helpText) | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
| using System.IO; | ||||
| using System.Security.Cryptography; | ||||
| using System.Text; | ||||
| using Dalamud.Game.ClientState.Objects.SubKinds; | ||||
|  | ||||
| namespace MareSynchronos.Utils | ||||
| { | ||||
| @@ -24,5 +25,11 @@ namespace MareSynchronos.Utils | ||||
|             using SHA256CryptoServiceProvider cryptoProvider = new(); | ||||
|             return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToHash))).Replace("-", ""); | ||||
|         } | ||||
|  | ||||
|         public static string GetHash256(PlayerCharacter character) | ||||
|         { | ||||
|             using SHA256CryptoServiceProvider cryptoProvider = new(); | ||||
|             return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(character.Name + character.HomeWorld.Id.ToString()))).Replace("-", ""); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,22 +1,48 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Threading; | ||||
| using Dalamud.Game.ClientState; | ||||
| using Dalamud.Game.ClientState.Objects; | ||||
| using Dalamud.Game.ClientState.Objects.SubKinds; | ||||
| using FFXIVClientStructs.FFXIV.Client.Game.Object; | ||||
| using Dalamud.Game.ClientState.Objects.Types; | ||||
| using Penumbra.PlayerWatch; | ||||
| using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; | ||||
|  | ||||
| namespace MareSynchronos.Utils | ||||
| { | ||||
|     public class DalamudUtil | ||||
|     public delegate void PlayerChange(Character actor); | ||||
|  | ||||
|     public class DalamudUtil : IDisposable | ||||
|     { | ||||
|         private readonly ClientState _clientState; | ||||
|         private readonly ObjectTable _objectTable; | ||||
|         private readonly IPlayerWatcher _watcher; | ||||
|         public event PlayerChange? PlayerChanged; | ||||
|  | ||||
|         public DalamudUtil(ClientState clientState, ObjectTable objectTable) | ||||
|         public DalamudUtil(ClientState clientState, ObjectTable objectTable, IPlayerWatcher watcher) | ||||
|         { | ||||
|             _clientState = clientState; | ||||
|             _objectTable = objectTable; | ||||
|             _watcher = watcher; | ||||
|             _watcher.Enable(); | ||||
|             _watcher.PlayerChanged += WatcherOnPlayerChanged; | ||||
|         } | ||||
|  | ||||
|         private void WatcherOnPlayerChanged(Character actor) | ||||
|         { | ||||
|             PlayerChanged?.Invoke(actor); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public void AddPlayerToWatch(string playerName) | ||||
|         { | ||||
|             _watcher.AddPlayerToWatch(playerName); | ||||
|         } | ||||
|  | ||||
|         public void RemovePlayerFromWatch(string playerName) | ||||
|         { | ||||
|             _watcher.RemovePlayerFromWatch(playerName); | ||||
|         } | ||||
|  | ||||
|         public bool IsPlayerPresent => _clientState.LocalPlayer != null; | ||||
| @@ -49,6 +75,13 @@ namespace MareSynchronos.Utils | ||||
|             return allLocalPlayers; | ||||
|         } | ||||
|  | ||||
|         public List<PlayerCharacter> GetPlayerCharacters() | ||||
|         { | ||||
|             return _objectTable.Where(obj => | ||||
|                 obj.ObjectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player && | ||||
|                 obj.Name.ToString() != PlayerName).Select(p => (PlayerCharacter)p).ToList(); | ||||
|         } | ||||
|  | ||||
|         public PlayerCharacter? GetPlayerCharacterFromObjectTableIndex(int index) | ||||
|         { | ||||
|             var objTableObj = _objectTable[index]; | ||||
| @@ -85,5 +118,11 @@ namespace MareSynchronos.Utils | ||||
|         } | ||||
|  | ||||
|         public void WaitWhileSelfIsDrawing() => WaitWhileCharacterIsDrawing(_clientState.LocalPlayer?.Address ?? new IntPtr()); | ||||
|  | ||||
|         public void Dispose() | ||||
|         { | ||||
|             _watcher.Disable(); | ||||
|             _watcher.Dispose(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -16,17 +16,28 @@ using Microsoft.AspNetCore.SignalR.Client; | ||||
|  | ||||
| namespace MareSynchronos.WebAPI | ||||
| { | ||||
|     public class ApiController : IDisposable | ||||
|     public partial class ApiController : IDisposable | ||||
|     { | ||||
|         public const string MainServer = "Lunae Crescere Incipientis (Central Server EU)"; | ||||
|  | ||||
| #if DEBUG | ||||
|         public const string MainServer = "darkarchons Debug Server (Dev Server (CH))"; | ||||
|         public const string MainServiceUri = "https://darkarchon.internet-box.ch:5001"; | ||||
|         readonly CancellationTokenSource _cts; | ||||
| #else | ||||
|         public const string MainServer = "Lunae Crescere Incipientis (Central Server EU)"; | ||||
|         public const string MainServiceUri = "to be defined"; | ||||
| #endif | ||||
|  | ||||
|         private readonly Configuration _pluginConfiguration; | ||||
|  | ||||
|         private CancellationTokenSource _cts; | ||||
|  | ||||
|         private HubConnection? _fileHub; | ||||
|  | ||||
|         private HubConnection? _heartbeatHub; | ||||
|  | ||||
|         private CancellationTokenSource? _uploadCancellationTokenSource; | ||||
|  | ||||
|         private HubConnection? _userHub; | ||||
|  | ||||
|         public ApiController(Configuration pluginConfiguration) | ||||
|         { | ||||
|             Logger.Debug("Creating " + nameof(ApiController)); | ||||
| @@ -34,9 +45,11 @@ namespace MareSynchronos.WebAPI | ||||
|             _pluginConfiguration = pluginConfiguration; | ||||
|             _cts = new CancellationTokenSource(); | ||||
|  | ||||
|             _ = Heartbeat(); | ||||
|             Task.Run(CreateConnections); | ||||
|         } | ||||
|  | ||||
|         public event EventHandler? ChangingServers; | ||||
|  | ||||
|         public event EventHandler<CharacterReceivedEventArgs>? CharacterReceived; | ||||
|  | ||||
|         public event EventHandler? Connected; | ||||
| @@ -50,30 +63,165 @@ namespace MareSynchronos.WebAPI | ||||
|         public event EventHandler? PairedWithOther; | ||||
|  | ||||
|         public event EventHandler? UnpairedFromOther; | ||||
|         public event EventHandler? AccountDeleted; | ||||
|  | ||||
|         public ConcurrentDictionary<string, (long, long)> CurrentDownloads { get; } = new(); | ||||
|  | ||||
|         public ConcurrentDictionary<string, (long, long)> CurrentUploads { get; } = new(); | ||||
|  | ||||
|         public bool IsConnected => !string.IsNullOrEmpty(UID); | ||||
|  | ||||
|         public bool IsDownloading { get; private set; } | ||||
|  | ||||
|         public bool IsUploading { get; private set; } | ||||
|  | ||||
|         public List<ClientPairDto> PairedClients { get; set; } = new(); | ||||
|  | ||||
|         public string SecretKey => _pluginConfiguration.ClientSecret.ContainsKey(ApiUri) ? _pluginConfiguration.ClientSecret[ApiUri] : "-"; | ||||
|  | ||||
|         public bool ServerAlive => | ||||
|             (_heartbeatHub?.State ?? HubConnectionState.Disconnected) == HubConnectionState.Connected; | ||||
|  | ||||
|         public Dictionary<string, string> ServerDictionary => new Dictionary<string, string>() { { MainServiceUri, MainServer } } | ||||
|             .Concat(_pluginConfiguration.CustomServerList) | ||||
|             .ToDictionary(k => k.Key, k => k.Value); | ||||
|         public string UID { get; private set; } = string.Empty; | ||||
|         public bool UseCustomService | ||||
|  | ||||
|         private string ApiUri => _pluginConfiguration.ApiUri; | ||||
|  | ||||
|         public async Task CreateConnections() | ||||
|         { | ||||
|             get => _pluginConfiguration.UseCustomService; | ||||
|             set | ||||
|             _cts = new CancellationTokenSource(); | ||||
|             var token = _cts.Token; | ||||
|             await StopAllConnections(token); | ||||
|  | ||||
|             while (!ServerAlive && !token.IsCancellationRequested) | ||||
|             { | ||||
|                 _pluginConfiguration.UseCustomService = value; | ||||
|                 _pluginConfiguration.Save(); | ||||
|                 await StopAllConnections(token); | ||||
|  | ||||
|                 try | ||||
|                 { | ||||
|                     Logger.Debug("Building connection"); | ||||
|                     _heartbeatHub = BuildHubConnection("heartbeat"); | ||||
|                     _userHub = BuildHubConnection("user"); | ||||
|                     _fileHub = BuildHubConnection("files"); | ||||
|  | ||||
|                     await _heartbeatHub.StartAsync(token); | ||||
|                     await _userHub.StartAsync(token); | ||||
|                     await _fileHub.StartAsync(token); | ||||
|  | ||||
|                     if (_pluginConfiguration.FullPause) | ||||
|                     { | ||||
|                         UID = string.Empty; | ||||
|                         return; | ||||
|                     } | ||||
|                     UID = await _heartbeatHub.InvokeAsync<string>("Heartbeat", token); | ||||
|                     if (!string.IsNullOrEmpty(UID) && !token.IsCancellationRequested) // user is authorized | ||||
|                     { | ||||
|                         Logger.Debug("Initializing data"); | ||||
|                         _userHub.On<ClientPairDto, string>("UpdateClientPairs", UpdateLocalClientPairs); | ||||
|                         _userHub.On<CharacterCacheDto, string>("ReceiveCharacterData", ReceiveCharacterData); | ||||
|                         _userHub.On<string>("RemoveOnlinePairedPlayer", | ||||
|                             (s) => PairedClientOffline?.Invoke(s, EventArgs.Empty)); | ||||
|                         _userHub.On<string>("AddOnlinePairedPlayer", | ||||
|                             (s) => PairedClientOnline?.Invoke(s, EventArgs.Empty)); | ||||
|  | ||||
|                         PairedClients = await _userHub!.InvokeAsync<List<ClientPairDto>>("GetPairedClients", token); | ||||
|  | ||||
|                         _heartbeatHub.Closed += HeartbeatHubOnClosed; | ||||
|                         _heartbeatHub.Reconnected += HeartbeatHubOnReconnected; | ||||
|                         _heartbeatHub.Reconnecting += HeartbeatHubOnReconnecting; | ||||
|                         Connected?.Invoke(this, EventArgs.Empty); | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     Logger.Warn(ex.Message); | ||||
|                     Logger.Warn(ex.StackTrace); | ||||
|                     Logger.Debug("Failed to establish connection, retrying"); | ||||
|                     await Task.Delay(TimeSpan.FromSeconds(5), token); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private string ApiUri => UseCustomService ? _pluginConfiguration.ApiUri : MainServiceUri; | ||||
|         public void Dispose() | ||||
|         { | ||||
|             Logger.Debug("Disposing " + nameof(ApiController)); | ||||
|  | ||||
|             Task.Run(async () => await StopAllConnections(_cts.Token)); | ||||
|             _cts?.Cancel(); | ||||
|         } | ||||
|  | ||||
|         private HubConnection BuildHubConnection(string hubName) | ||||
|         { | ||||
|             return new HubConnectionBuilder() | ||||
|                 .WithUrl(ApiUri + "/" + hubName, options => | ||||
|                 { | ||||
|                     if (!string.IsNullOrEmpty(SecretKey) && !_pluginConfiguration.FullPause) | ||||
|                     { | ||||
|                         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 | ||||
|                 }) | ||||
|                 .WithAutomaticReconnect(new ForeverRetryPolicy()) | ||||
|                 .Build(); | ||||
|         } | ||||
|  | ||||
|         private Task HeartbeatHubOnClosed(Exception? arg) | ||||
|         { | ||||
|             Logger.Debug("Connection closed"); | ||||
|             Disconnected?.Invoke(null, EventArgs.Empty); | ||||
|             return Task.CompletedTask; | ||||
|         } | ||||
|  | ||||
|         private Task HeartbeatHubOnReconnected(string? arg) | ||||
|         { | ||||
|             Logger.Debug("Connection restored"); | ||||
|             Connected?.Invoke(this, EventArgs.Empty); | ||||
|             return Task.CompletedTask; | ||||
|         } | ||||
|  | ||||
|         private Task HeartbeatHubOnReconnecting(Exception? arg) | ||||
|         { | ||||
|             Logger.Debug("Connection closed... Reconnecting…"); | ||||
|             Disconnected?.Invoke(null, EventArgs.Empty); | ||||
|             return Task.CompletedTask; | ||||
|         } | ||||
|  | ||||
|         private async Task StopAllConnections(CancellationToken token) | ||||
|         { | ||||
|             if (_heartbeatHub is { State: HubConnectionState.Connected }) | ||||
|             { | ||||
|                 await _heartbeatHub.StopAsync(token); | ||||
|                 _heartbeatHub.Closed -= HeartbeatHubOnClosed; | ||||
|                 _heartbeatHub.Reconnected -= HeartbeatHubOnReconnected; | ||||
|                 _heartbeatHub.Reconnecting += HeartbeatHubOnReconnecting; | ||||
|                 await _heartbeatHub.DisposeAsync(); | ||||
|             } | ||||
|  | ||||
|             if (_fileHub is { State: HubConnectionState.Connected }) | ||||
|             { | ||||
|                 await _fileHub.StopAsync(token); | ||||
|                 await _fileHub.DisposeAsync(); | ||||
|             } | ||||
|  | ||||
|             if (_userHub is { State: HubConnectionState.Connected }) | ||||
|             { | ||||
|                 await _userHub.StopAsync(token); | ||||
|                 await _userHub.DisposeAsync(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public partial class ApiController | ||||
|     { | ||||
|         public void CancelUpload() | ||||
|         { | ||||
|             if (_uploadCancellationTokenSource != null) | ||||
| @@ -84,12 +232,17 @@ namespace MareSynchronos.WebAPI | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void Dispose() | ||||
|         public async Task DeleteAccount() | ||||
|         { | ||||
|             Logger.Debug("Disposing " + nameof(ApiController)); | ||||
|             _pluginConfiguration.ClientSecret.Remove(ApiUri); | ||||
|             await _fileHub!.SendAsync("DeleteAllFiles"); | ||||
|             await _userHub!.SendAsync("DeleteAccount"); | ||||
|             await CreateConnections(); | ||||
|         } | ||||
|  | ||||
|             _cts?.Cancel(); | ||||
|             _ = DisposeHubConnections(); | ||||
|         public async Task DeleteAllMyFiles() | ||||
|         { | ||||
|             await _fileHub!.SendAsync("DeleteAllFiles"); | ||||
|         } | ||||
|  | ||||
|         public async Task<string> DownloadFile(string hash, CancellationToken ct) | ||||
| @@ -167,41 +320,6 @@ namespace MareSynchronos.WebAPI | ||||
|                 hashedCharacterNames); | ||||
|         } | ||||
|  | ||||
|         public async Task Heartbeat() | ||||
|         { | ||||
|             while (!ServerAlive && !_cts.Token.IsCancellationRequested) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     if (_pluginConfiguration.FullPause) return; | ||||
|                     Logger.Debug("Attempting to establish heartbeat connection to " + ApiUri); | ||||
|                     _heartbeatHub = BuildHubConnection("heartbeat"); | ||||
|  | ||||
|                     await _heartbeatHub.StartAsync(_cts.Token); | ||||
|                     UID = await _heartbeatHub!.InvokeAsync<string>("Heartbeat"); | ||||
|                     Logger.Debug("Heartbeat started: " + ApiUri); | ||||
|                     try | ||||
|                     { | ||||
|                         await InitializeHubConnections(); | ||||
|                         await LoadInitialData(); | ||||
|                         Connected?.Invoke(this, EventArgs.Empty); | ||||
|                     } | ||||
|                     catch | ||||
|                     { | ||||
|                         //PluginLog.Error(ex, "Error during Heartbeat initialization"); | ||||
|                     } | ||||
|  | ||||
|                     _heartbeatHub.Closed += OnHeartbeatHubOnClosed; | ||||
|                     _heartbeatHub.Reconnected += OnHeartbeatHubOnReconnected; | ||||
|                     Logger.Debug("Heartbeat established to: " + ApiUri); | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     PluginLog.Error(ex, "Creating heartbeat failure"); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public Task ReceiveCharacterData(CharacterCacheDto character, string characterHash) | ||||
|         { | ||||
|             Logger.Debug("Received DTO for " + characterHash); | ||||
| @@ -216,29 +334,9 @@ namespace MareSynchronos.WebAPI | ||||
|             var response = await _userHub!.InvokeAsync<string>("Register"); | ||||
|             _pluginConfiguration.ClientSecret[ApiUri] = response; | ||||
|             _pluginConfiguration.Save(); | ||||
|             RestartHeartbeat(); | ||||
|             ChangingServers?.Invoke(null, EventArgs.Empty); | ||||
|             await CreateConnections(); | ||||
|         } | ||||
|  | ||||
|         public void RestartHeartbeat() | ||||
|         { | ||||
|             Logger.Debug("Restarting heartbeat"); | ||||
|  | ||||
|             Task.Run(async () => | ||||
|             { | ||||
|                 if (_heartbeatHub != null) | ||||
|                 { | ||||
|                     _heartbeatHub.Closed -= OnHeartbeatHubOnClosed; | ||||
|                     _heartbeatHub.Reconnected -= OnHeartbeatHubOnReconnected; | ||||
|                     await _heartbeatHub.StopAsync(_cts.Token); | ||||
|                     await _heartbeatHub.DisposeAsync(); | ||||
|                     await OnHeartbeatHubOnClosed(null); | ||||
|                     _heartbeatHub = null!; | ||||
|                 } | ||||
|  | ||||
|                 _ = Heartbeat(); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         public async Task SendCharacterData(CharacterCacheDto character, List<string> visibleCharacterIds) | ||||
|         { | ||||
|             if (!IsConnected || SecretKey == "-") return; | ||||
| @@ -323,40 +421,6 @@ namespace MareSynchronos.WebAPI | ||||
|             await _userHub!.SendAsync("SendPairedClientRemoval", uid); | ||||
|         } | ||||
|  | ||||
|         public async Task DeleteAllMyFiles() | ||||
|         { | ||||
|             await _fileHub!.SendAsync("DeleteAllFiles"); | ||||
|         } | ||||
|  | ||||
|         public async Task DeleteAccount() | ||||
|         { | ||||
|             _pluginConfiguration.ClientSecret.Remove(ApiUri); | ||||
|             await _fileHub!.SendAsync("DeleteAllFiles"); | ||||
|             await _userHub!.SendAsync("DeleteAccount"); | ||||
|             _ = OnHeartbeatHubOnClosed(null); | ||||
|             AccountDeleted?.Invoke(null, EventArgs.Empty); | ||||
|         } | ||||
|  | ||||
|         private async Task DisposeHubConnections() | ||||
|         { | ||||
|             if (_fileHub != null) | ||||
|             { | ||||
|                 Logger.Debug("Disposing File Hub"); | ||||
|                 CancelUpload(); | ||||
|                 await _fileHub!.StopAsync(); | ||||
|                 await _fileHub!.DisposeAsync(); | ||||
|                 _fileHub = null; | ||||
|             } | ||||
|  | ||||
|             if (_userHub != null) | ||||
|             { | ||||
|                 Logger.Debug("Disposing User Hub"); | ||||
|                 await _userHub.StopAsync(); | ||||
|                 await _userHub.DisposeAsync(); | ||||
|                 _userHub = null; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private async Task<(string, byte[])> GetCompressedFileData(string fileHash, CancellationToken uploadToken) | ||||
|         { | ||||
|             await using var db = new FileCacheContext(); | ||||
| @@ -364,67 +428,6 @@ namespace MareSynchronos.WebAPI | ||||
|             return (fileHash, LZ4Codec.WrapHC(await File.ReadAllBytesAsync(fileCache.Filepath, uploadToken), 0, | ||||
|                 (int)new FileInfo(fileCache.Filepath).Length)); | ||||
|         } | ||||
|  | ||||
|         private async Task InitializeHubConnections() | ||||
|         { | ||||
|             await DisposeHubConnections(); | ||||
|  | ||||
|             Logger.Debug("Creating User Hub"); | ||||
|             _userHub = BuildHubConnection("user"); | ||||
|             await _userHub.StartAsync(); | ||||
|             _userHub.On<ClientPairDto, string>("UpdateClientPairs", UpdateLocalClientPairs); | ||||
|             _userHub.On<CharacterCacheDto, string>("ReceiveCharacterData", ReceiveCharacterData); | ||||
|             _userHub.On<string>("RemoveOnlinePairedPlayer", (s) => PairedClientOffline?.Invoke(s, EventArgs.Empty)); | ||||
|             _userHub.On<string>("AddOnlinePairedPlayer", (s) => PairedClientOnline?.Invoke(s, EventArgs.Empty)); | ||||
|  | ||||
|             Logger.Debug("Creating File Hub"); | ||||
|             _fileHub = BuildHubConnection("files"); | ||||
|             await _fileHub.StartAsync(_cts.Token); | ||||
|         } | ||||
|  | ||||
|         private HubConnection BuildHubConnection(string hubName) | ||||
|         { | ||||
|             return new HubConnectionBuilder() | ||||
|                 .WithUrl(ApiUri + "/" + hubName, options => | ||||
|                 { | ||||
|                     if (!string.IsNullOrEmpty(SecretKey) && !_pluginConfiguration.FullPause) | ||||
|                     { | ||||
|                         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(); | ||||
|         } | ||||
|  | ||||
|         private async Task LoadInitialData() | ||||
|         { | ||||
|             var pairedClients = await _userHub!.InvokeAsync<List<ClientPairDto>>("GetPairedClients"); | ||||
|             PairedClients = pairedClients.ToList(); | ||||
|         } | ||||
|  | ||||
|         private Task OnHeartbeatHubOnClosed(Exception? exception) | ||||
|         { | ||||
|             Logger.Debug("Connection closed: " + ApiUri); | ||||
|             Disconnected?.Invoke(null, EventArgs.Empty); | ||||
|             Task.Run(DisposeHubConnections); | ||||
|             RestartHeartbeat(); | ||||
|             return Task.CompletedTask; | ||||
|         } | ||||
|  | ||||
|         private async Task OnHeartbeatHubOnReconnected(string? s) | ||||
|         { | ||||
|             Logger.Debug("Reconnected: " + ApiUri); | ||||
|             UID = await _heartbeatHub!.InvokeAsync<string>("Heartbeat"); | ||||
|         } | ||||
|  | ||||
|         private void UpdateLocalClientPairs(ClientPairDto dto, string characterIdentifier) | ||||
|         { | ||||
|             var entry = PairedClients.SingleOrDefault(e => e.OtherUID == dto.OtherUID); | ||||
| @@ -492,4 +495,12 @@ namespace MareSynchronos.WebAPI | ||||
|         public CharacterCacheDto CharacterData { get; set; } | ||||
|         public string CharacterNameHash { get; set; } | ||||
|     } | ||||
|  | ||||
|     public class ForeverRetryPolicy : IRetryPolicy | ||||
|     { | ||||
|         public TimeSpan? NextRetryDelay(RetryContext retryContext) | ||||
|         { | ||||
|             return TimeSpan.FromSeconds(5); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Stanley Dimant
					Stanley Dimant