add mare synchronos full pause

This commit is contained in:
Stanley Dimant
2022-06-26 16:39:28 +02:00
parent 085d918397
commit 9aaab87c91
11 changed files with 155 additions and 123 deletions

View File

@@ -44,6 +44,7 @@ namespace MareSynchronos
} }
} }
public bool FullPause { get; set; } = false;
public Dictionary<string, string> UidComments { get; set; } = new(); public Dictionary<string, string> UidComments { get; set; } = new();
public bool UseCustomService { get; set; } = false; public bool UseCustomService { get; set; } = false;
public int Version { get; set; } = 0; public int Version { get; set; } = 0;

View File

@@ -213,6 +213,7 @@ namespace MareSynchronos.Managers
Task.Run(RecalculateFileCacheSize); Task.Run(RecalculateFileCacheSize);
} }
} }
private void RecalculateFileCacheSize() private void RecalculateFileCacheSize()
{ {
FileCacheSize = 0; FileCacheSize = 0;

View File

@@ -53,7 +53,6 @@ namespace MareSynchronos.Managers
internal void StartWatchingPlayer() internal void StartWatchingPlayer()
{ {
_watcher.AddPlayerToWatch(_dalamudUtil.PlayerName); _watcher.AddPlayerToWatch(_dalamudUtil.PlayerName);
_watcher.PlayerChanged += Watcher_PlayerChanged;
_apiController.Connected += ApiController_Connected; _apiController.Connected += ApiController_Connected;
_apiController.Disconnected += ApiController_Disconnected; _apiController.Disconnected += ApiController_Disconnected;
@@ -62,12 +61,11 @@ namespace MareSynchronos.Managers
{ {
ApiController_Connected(null, EventArgs.Empty); ApiController_Connected(null, EventArgs.Empty);
} }
_ipcManager.PenumbraRedraw(_dalamudUtil.PlayerName);
} }
private void ApiController_Connected(object? sender, EventArgs args) private void ApiController_Connected(object? sender, EventArgs args)
{ {
Logger.Debug("ApiController Connected");
var apiTask = _apiController.SendCharacterName(_dalamudUtil.PlayerNameHashed); var apiTask = _apiController.SendCharacterName(_dalamudUtil.PlayerNameHashed);
_lastSentHash = string.Empty; _lastSentHash = string.Empty;
@@ -76,6 +74,8 @@ namespace MareSynchronos.Managers
_cachedPlayersManager.AddInitialPairs(apiTask.Result); _cachedPlayersManager.AddInitialPairs(apiTask.Result);
_ipcManager.PenumbraRedrawEvent += IpcManager_PenumbraRedrawEvent; _ipcManager.PenumbraRedrawEvent += IpcManager_PenumbraRedrawEvent;
_ipcManager.PenumbraRedraw(_dalamudUtil.PlayerName);
_watcher.PlayerChanged += Watcher_PlayerChanged;
} }
private void ApiController_Disconnected(object? sender, EventArgs args) private void ApiController_Disconnected(object? sender, EventArgs args)
@@ -83,6 +83,7 @@ namespace MareSynchronos.Managers
Logger.Debug(nameof(ApiController_Disconnected)); Logger.Debug(nameof(ApiController_Disconnected));
_ipcManager.PenumbraRedrawEvent -= IpcManager_PenumbraRedrawEvent; _ipcManager.PenumbraRedrawEvent -= IpcManager_PenumbraRedrawEvent;
_watcher.PlayerChanged -= Watcher_PlayerChanged;
} }
private async Task<CharacterData> CreateFullCharacterCache() private async Task<CharacterData> CreateFullCharacterCache()
@@ -106,10 +107,9 @@ namespace MareSynchronos.Managers
private void IpcManager_PenumbraRedrawEvent(object? objectTableIndex, EventArgs e) private void IpcManager_PenumbraRedrawEvent(object? objectTableIndex, EventArgs e)
{ {
var objTableObj = _objectTable[(int)objectTableIndex!]; var player = _dalamudUtil.GetPlayerCharacterFromObjectTableIndex((int)objectTableIndex!);
if (objTableObj!.ObjectKind != Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player) return; if (player != null && player.Name.ToString() != _dalamudUtil.PlayerName) return;
if (objTableObj.Name.ToString() != _dalamudUtil.PlayerName) return; Logger.Debug("Penumbra Redraw Event for " + _dalamudUtil.PlayerName);
Logger.Debug("Penumbra Redraw Event");
PlayerChanged(_dalamudUtil.PlayerName); PlayerChanged(_dalamudUtil.PlayerName);
} }
@@ -131,6 +131,16 @@ namespace MareSynchronos.Managers
_playerChangedTask = Task.Run(async () => _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(); Stopwatch st = Stopwatch.StartNew();
_dalamudUtil.WaitWhileSelfIsDrawing(); _dalamudUtil.WaitWhileSelfIsDrawing();
@@ -152,7 +162,6 @@ namespace MareSynchronos.Managers
private void Watcher_PlayerChanged(Dalamud.Game.ClientState.Objects.Types.Character actor) private void Watcher_PlayerChanged(Dalamud.Game.ClientState.Objects.Types.Character actor)
{ {
Logger.Debug("Watcher Player Changed");
Task.Run(() => Task.Run(() =>
{ {
// fix for redraw from anamnesis // fix for redraw from anamnesis

View File

@@ -140,8 +140,8 @@ public class CachedPlayer
var tempCollection = _ipcManager.PenumbraCreateTemporaryCollection(PlayerName!); var tempCollection = _ipcManager.PenumbraCreateTemporaryCollection(PlayerName!);
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerCharacter!.Address); _dalamudUtil.WaitWhileCharacterIsDrawing(PlayerCharacter!.Address);
RequestedPenumbraRedraw = true; RequestedPenumbraRedraw = true;
Logger.Warn( Logger.Debug(
$"Request Redraw for {PlayerName}: RequestedRedraws now {RequestedPenumbraRedraw}"); $"Request Redraw for {PlayerName}");
_ipcManager.PenumbraSetTemporaryMods(tempCollection, moddedPaths, cache.ManipulationData); _ipcManager.PenumbraSetTemporaryMods(tempCollection, moddedPaths, cache.ManipulationData);
_ipcManager.GlamourerRevertCharacterCustomization(PlayerName!); _ipcManager.GlamourerRevertCharacterCustomization(PlayerName!);
_ipcManager.GlamourerApplyAll(cache.GlamourerData, PlayerName!); _ipcManager.GlamourerApplyAll(cache.GlamourerData, PlayerName!);
@@ -205,14 +205,14 @@ public class CachedPlayer
RequestedPenumbraRedraw = false; RequestedPenumbraRedraw = false;
Logger.Debug( Logger.Debug(
$"Penumbra Redraw for {PlayerName}: RequestedRedraws now {RequestedPenumbraRedraw}"); $"Penumbra Redraw done for {PlayerName}");
}); });
} }
private void WatcherOnPlayerChanged(Character actor) private void WatcherOnPlayerChanged(Character actor)
{ {
if (actor.Name.ToString() != PlayerName) return; 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!); PlayerCharacter = _dalamudUtil.GetPlayerCharacterFromObjectTableByName(PlayerName!);
if (PlayerCharacter is null) if (PlayerCharacter is null)
{ {

View File

@@ -22,7 +22,7 @@ namespace MareSynchronos.Models
[JsonProperty] [JsonProperty]
public string CacheHash { get; set; } = string.Empty; public string CacheHash { get; set; } = string.Empty;
public List<FileReplacement> FileReplacements { get; set; } = new List<FileReplacement>(); public List<FileReplacement> FileReplacements { get; set; } = new();
[JsonProperty] [JsonProperty]
public string GlamourerString { get; set; } = string.Empty; public string GlamourerString { get; set; } = string.Empty;
@@ -91,7 +91,7 @@ namespace MareSynchronos.Models
FileReplacements = AllReplacements.Select(f => f.ToFileReplacementDto()).ToList(), FileReplacements = AllReplacements.Select(f => f.ToFileReplacementDto()).ToList(),
GlamourerData = GlamourerString, GlamourerData = GlamourerString,
Hash = CacheHash, Hash = CacheHash,
JobId = (int)JobId, JobId = JobId,
ManipulationData = ManipulationString ManipulationData = ManipulationString
}; };
} }

View File

@@ -24,7 +24,7 @@ namespace MareSynchronos.Models
this._penumbraDirectory = penumbraDirectory; this._penumbraDirectory = penumbraDirectory;
} }
public List<FileReplacement> Associated { get; set; } = new List<FileReplacement>(); public List<FileReplacement> Associated { get; set; } = new();
public bool Computed => (_computationTask == null || (_computationTask?.IsCompleted ?? true)) && Associated.All(f => f.Computed); public bool Computed => (_computationTask == null || (_computationTask?.IsCompleted ?? true)) && Associated.All(f => f.Computed);

View File

@@ -29,8 +29,8 @@ public class DownloadUi : Window, IDisposable
SizeConstraints = new WindowSizeConstraints() SizeConstraints = new WindowSizeConstraints()
{ {
MaximumSize = new(300, 90), MaximumSize = new Vector2(300, 90),
MinimumSize = new(300, 90) MinimumSize = new Vector2(300, 90)
}; };
Flags = ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoBackground; Flags = ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoBackground;

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Numerics;
using Dalamud.Interface.Colors; using Dalamud.Interface.Colors;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
using ImGuiNET; using ImGuiNET;
@@ -36,8 +37,8 @@ namespace MareSynchronos.UI
SizeConstraints = new WindowSizeConstraints() SizeConstraints = new WindowSizeConstraints()
{ {
MinimumSize = new(600, 400), MinimumSize = new Vector2(600, 400),
MaximumSize = new(600, 2000) MaximumSize = new Vector2(600, 2000)
}; };
_windowSystem.AddWindow(this); _windowSystem.AddWindow(this);

View File

@@ -26,8 +26,8 @@ namespace MareSynchronos.UI
SizeConstraints = new WindowSizeConstraints() SizeConstraints = new WindowSizeConstraints()
{ {
MinimumSize = new(800, 400), MinimumSize = new Vector2(800, 400),
MaximumSize = new(800, 2000), MaximumSize = new Vector2(800, 2000),
}; };
_configuration = configuration; _configuration = configuration;
@@ -62,13 +62,13 @@ namespace MareSynchronos.UI
} }
else else
{ {
if (!_uiShared.DrawOtherPluginState()) return; var pluginState = _uiShared.DrawOtherPluginState();
DrawSettingsContent(); DrawSettingsContent(pluginState);
} }
} }
private void DrawSettingsContent() private void DrawSettingsContent(bool pluginState)
{ {
_uiShared.PrintServerState(); _uiShared.PrintServerState();
ImGui.Separator(); ImGui.Separator();
@@ -84,86 +84,112 @@ namespace MareSynchronos.UI
{ {
ImGui.SetClipboardText(_apiController.UID); ImGui.SetClipboardText(_apiController.UID);
} }
ImGui.Text("Share this UID to other Mare users so they pair their client with yours."); ImGui.Text("Share this UID to other Mare users so they pair their client with yours.");
ImGui.Separator();
DrawPairedClientsContent();
DrawFileCacheSettings();
DrawCurrentTransfers();
DrawAdministration();
} }
else else
{ {
ImGui.TextColored(ImGuiColors.DalamudRed, "No UID (Service unavailable)"); ImGui.TextColored(ImGuiColors.DalamudRed, "No UID (Service unavailable)");
ImGui.SetWindowFontScale(1.0f); ImGui.SetWindowFontScale(1.0f);
} }
ImGui.Separator();
if (_apiController.ServerAlive)
DrawPairedClientsContent();
DrawFileCacheSettings();
if (_apiController.ServerAlive)
DrawCurrentTransfers();
DrawAdministration(_apiController.ServerAlive);
} }
private bool _deleteFilesPopupModalShown = false; private bool _deleteFilesPopupModalShown = false;
private bool _deleteAccountPopupModalShown = false; private bool _deleteAccountPopupModalShown = false;
private void DrawAdministration() private void DrawAdministration(bool serverAlive)
{ {
if (ImGui.TreeNode( if (ImGui.TreeNode(
$"User Administration")) $"User Administration"))
{ {
if (ImGui.Button("Delete all my files")) if (serverAlive)
{ {
_deleteFilesPopupModalShown = true; if (ImGui.Button("Delete all my files"))
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)))
{ {
Task.Run(() => _apiController.DeleteAllMyFiles()); _deleteFilesPopupModalShown = true;
ImGui.CloseCurrentPopup(); ImGui.OpenPopup("Delete all your files?");
_deleteFilesPopupModalShown = false;
} }
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(); UiShared.TextWrapped(
_deleteFilesPopupModalShown = false; "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; _configuration.FullPause = marePaused;
ImGui.OpenPopup("Delete your account?"); _configuration.Save();
_apiController.RestartHeartbeat();
} }
if (ImGui.BeginPopupModal("Delete your account?", ref _deleteAccountPopupModalShown, ImGuiWindowFlags.AlwaysAutoResize)) UiShared.DrawHelpText("Completely pauses the sync and clear your current data (not uploaded files) on the service.");
{
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();
}
ImGui.TreePop(); ImGui.TreePop();
} }

View File

@@ -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() public void DrawCacheDirectorySetting()
{ {
var cacheDirectory = _pluginConfiguration.CacheFolder; var cacheDirectory = _pluginConfiguration.CacheFolder;

View File

@@ -74,7 +74,6 @@ namespace MareSynchronos.WebAPI
} }
private string ApiUri => UseCustomService ? _pluginConfiguration.ApiUri : MainServiceUri; private string ApiUri => UseCustomService ? _pluginConfiguration.ApiUri : MainServiceUri;
private string CacheFolder => _pluginConfiguration.CacheFolder;
public void CancelUpload() public void CancelUpload()
{ {
if (_uploadCancellationTokenSource != null) if (_uploadCancellationTokenSource != null)
@@ -174,25 +173,9 @@ namespace MareSynchronos.WebAPI
{ {
try try
{ {
if (_pluginConfiguration.FullPause) return;
Logger.Debug("Attempting to establish heartbeat connection to " + ApiUri); Logger.Debug("Attempting to establish heartbeat connection to " + ApiUri);
_heartbeatHub = new HubConnectionBuilder() _heartbeatHub = BuildHubConnection("heartbeat");
.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();
await _heartbeatHub.StartAsync(_cts.Token); await _heartbeatHub.StartAsync(_cts.Token);
UID = await _heartbeatHub!.InvokeAsync<string>("Heartbeat"); UID = await _heartbeatHub!.InvokeAsync<string>("Heartbeat");
@@ -240,13 +223,18 @@ namespace MareSynchronos.WebAPI
{ {
Logger.Debug("Restarting heartbeat"); Logger.Debug("Restarting heartbeat");
_heartbeatHub!.Closed -= OnHeartbeatHubOnClosed;
_heartbeatHub!.Reconnected -= OnHeartbeatHubOnReconnected;
Task.Run(async () => Task.Run(async () =>
{ {
await _heartbeatHub.StopAsync(_cts.Token); if (_heartbeatHub != null)
await _heartbeatHub.DisposeAsync(); {
_heartbeatHub = null!; _heartbeatHub.Closed -= OnHeartbeatHubOnClosed;
_heartbeatHub.Reconnected -= OnHeartbeatHubOnReconnected;
await _heartbeatHub.StopAsync(_cts.Token);
await _heartbeatHub.DisposeAsync();
await OnHeartbeatHubOnClosed(null);
_heartbeatHub = null!;
}
_ = Heartbeat(); _ = Heartbeat();
}); });
} }
@@ -335,11 +323,6 @@ namespace MareSynchronos.WebAPI
await _userHub!.SendAsync("SendPairedClientRemoval", uid); await _userHub!.SendAsync("SendPairedClientRemoval", uid);
} }
public async Task UpdateCurrentDownloadSize(string hash)
{
long fileSize = await _fileHub!.InvokeAsync<long>("GetFileSize", hash);
}
public async Task DeleteAllMyFiles() public async Task DeleteAllMyFiles()
{ {
await _fileHub!.SendAsync("DeleteAllFiles"); await _fileHub!.SendAsync("DeleteAllFiles");
@@ -362,6 +345,7 @@ namespace MareSynchronos.WebAPI
CancelUpload(); CancelUpload();
await _fileHub!.StopAsync(); await _fileHub!.StopAsync();
await _fileHub!.DisposeAsync(); await _fileHub!.DisposeAsync();
_fileHub = null;
} }
if (_userHub != null) if (_userHub != null)
@@ -369,6 +353,7 @@ namespace MareSynchronos.WebAPI
Logger.Debug("Disposing User Hub"); Logger.Debug("Disposing User Hub");
await _userHub.StopAsync(); await _userHub.StopAsync();
await _userHub.DisposeAsync(); await _userHub.DisposeAsync();
_userHub = null;
} }
} }
@@ -385,21 +370,7 @@ namespace MareSynchronos.WebAPI
await DisposeHubConnections(); await DisposeHubConnections();
Logger.Debug("Creating User Hub"); Logger.Debug("Creating User Hub");
_userHub = new HubConnectionBuilder() _userHub = BuildHubConnection("user");
.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();
await _userHub.StartAsync(); await _userHub.StartAsync();
_userHub.On<ClientPairDto, string>("UpdateClientPairs", UpdateLocalClientPairs); _userHub.On<ClientPairDto, string>("UpdateClientPairs", UpdateLocalClientPairs);
_userHub.On<CharacterCacheDto, string>("ReceiveCharacterData", ReceiveCharacterData); _userHub.On<CharacterCacheDto, string>("ReceiveCharacterData", ReceiveCharacterData);
@@ -407,10 +378,19 @@ namespace MareSynchronos.WebAPI
_userHub.On<string>("AddOnlinePairedPlayer", (s) => PairedClientOnline?.Invoke(s, EventArgs.Empty)); _userHub.On<string>("AddOnlinePairedPlayer", (s) => PairedClientOnline?.Invoke(s, EventArgs.Empty));
Logger.Debug("Creating File Hub"); Logger.Debug("Creating File Hub");
_fileHub = new HubConnectionBuilder() _fileHub = BuildHubConnection("files");
.WithUrl(ApiUri + "/files", options => 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 #if DEBUG
options.HttpMessageHandlerFactory = (message) => options.HttpMessageHandlerFactory = (message) =>
{ {
@@ -422,7 +402,6 @@ namespace MareSynchronos.WebAPI
#endif #endif
}) })
.Build(); .Build();
await _fileHub.StartAsync(_cts.Token);
} }
private async Task LoadInitialData() private async Task LoadInitialData()
@@ -435,6 +414,7 @@ namespace MareSynchronos.WebAPI
{ {
Logger.Debug("Connection closed: " + ApiUri); Logger.Debug("Connection closed: " + ApiUri);
Disconnected?.Invoke(null, EventArgs.Empty); Disconnected?.Invoke(null, EventArgs.Empty);
Task.Run(DisposeHubConnections);
RestartHeartbeat(); RestartHeartbeat();
return Task.CompletedTask; return Task.CompletedTask;
} }