add downloads UI, fix some bugs on disconnect
This commit is contained in:
		| @@ -47,6 +47,9 @@ namespace MareSynchronos | |||||||
|         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; | ||||||
|  |  | ||||||
|  |         public bool ShowTransferWindow { get; set; } = true; | ||||||
|  |  | ||||||
|         // the below exist just to make saving less cumbersome |         // the below exist just to make saving less cumbersome | ||||||
|         public void Initialize(DalamudPluginInterface pluginInterface) |         public void Initialize(DalamudPluginInterface pluginInterface) | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -1,8 +1,6 @@ | |||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.Diagnostics; |  | ||||||
| using System.Linq; | using System.Linq; | ||||||
| using System.Threading; |  | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using Dalamud.Game; | using Dalamud.Game; | ||||||
| using Dalamud.Game.ClientState; | using Dalamud.Game.ClientState; | ||||||
| @@ -14,7 +12,6 @@ using MareSynchronos.FileCacheDB; | |||||||
| using MareSynchronos.Models; | using MareSynchronos.Models; | ||||||
| using MareSynchronos.Utils; | using MareSynchronos.Utils; | ||||||
| using MareSynchronos.WebAPI; | using MareSynchronos.WebAPI; | ||||||
| using Microsoft.EntityFrameworkCore; |  | ||||||
|  |  | ||||||
| namespace MareSynchronos.Managers; | namespace MareSynchronos.Managers; | ||||||
|  |  | ||||||
| @@ -63,6 +60,8 @@ public class CharacterCacheManager : IDisposable | |||||||
|         { |         { | ||||||
|             RestoreCharacter(character); |             RestoreCharacter(character); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         _onlineCachedPlayers.Clear(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void Initialize() |     public void Initialize() | ||||||
| @@ -74,6 +73,7 @@ public class CharacterCacheManager : IDisposable | |||||||
|         _apiController.UnpairedFromOther += ApiControllerOnUnpairedFromOther; |         _apiController.UnpairedFromOther += ApiControllerOnUnpairedFromOther; | ||||||
|         _framework.Update += FrameworkOnUpdate; |         _framework.Update += FrameworkOnUpdate; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public async Task UpdatePlayersFromService(Dictionary<string, int> playerJobIds) |     public async Task UpdatePlayersFromService(Dictionary<string, int> playerJobIds) | ||||||
|     { |     { | ||||||
|         await _apiController.GetCharacterData(playerJobIds); |         await _apiController.GetCharacterData(playerJobIds); | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ using MareSynchronos.Utils; | |||||||
|  |  | ||||||
| namespace MareSynchronos.Managers | namespace MareSynchronos.Managers | ||||||
| { | { | ||||||
|     internal class FileCacheManager : IDisposable |     public class FileCacheManager : IDisposable | ||||||
|     { |     { | ||||||
|         private readonly FileCacheFactory _fileCacheFactory; |         private readonly FileCacheFactory _fileCacheFactory; | ||||||
|         private readonly IpcManager _ipcManager; |         private readonly IpcManager _ipcManager; | ||||||
|   | |||||||
| @@ -24,7 +24,7 @@ namespace MareSynchronos | |||||||
|         private readonly CommandManager _commandManager; |         private readonly CommandManager _commandManager; | ||||||
|         private readonly Configuration _configuration; |         private readonly Configuration _configuration; | ||||||
|         private readonly FileCacheManager _fileCacheManager; |         private readonly FileCacheManager _fileCacheManager; | ||||||
|         private readonly IntroUI _introUi; |         private readonly IntroUi _introUi; | ||||||
|         private readonly IpcManager _ipcManager; |         private readonly IpcManager _ipcManager; | ||||||
|         private readonly ObjectTable _objectTable; |         private readonly ObjectTable _objectTable; | ||||||
|         private readonly DalamudPluginInterface _pluginInterface; |         private readonly DalamudPluginInterface _pluginInterface; | ||||||
| @@ -34,6 +34,7 @@ namespace MareSynchronos | |||||||
|         private readonly DalamudUtil _dalamudUtil; |         private readonly DalamudUtil _dalamudUtil; | ||||||
|         private readonly CharacterCacheManager _characterCacheManager; |         private readonly CharacterCacheManager _characterCacheManager; | ||||||
|         private readonly IPlayerWatcher _playerWatcher; |         private readonly IPlayerWatcher _playerWatcher; | ||||||
|  |         private readonly DownloadUi _downloadUi; | ||||||
|  |  | ||||||
|         public Plugin(DalamudPluginInterface pluginInterface, CommandManager commandManager, |         public Plugin(DalamudPluginInterface pluginInterface, CommandManager commandManager, | ||||||
|             Framework framework, ObjectTable objectTable, ClientState clientState) |             Framework framework, ObjectTable objectTable, ClientState clientState) | ||||||
| @@ -58,16 +59,17 @@ namespace MareSynchronos | |||||||
|             _playerWatcher.Enable(); |             _playerWatcher.Enable(); | ||||||
|  |  | ||||||
|             var uiSharedComponent = |             var uiSharedComponent = | ||||||
|                 new UIShared(_ipcManager, _apiController, _fileCacheManager, _configuration); |                 new UiShared(_ipcManager, _apiController, _fileCacheManager, _configuration); | ||||||
|  |  | ||||||
|             _pluginUi = new PluginUi(_windowSystem, uiSharedComponent, _configuration, _apiController); |             _pluginUi = new PluginUi(_windowSystem, uiSharedComponent, _configuration, _apiController); | ||||||
|             _introUi = new IntroUI(_windowSystem, uiSharedComponent, _configuration, _fileCacheManager); |             _introUi = new IntroUi(_windowSystem, uiSharedComponent, _configuration, _fileCacheManager); | ||||||
|             _introUi.FinishedRegistration += (_, _) => |             _introUi.FinishedRegistration += (_, _) => | ||||||
|             { |             { | ||||||
|                 _introUi.IsOpen = false; |                 _introUi.IsOpen = false; | ||||||
|                 _pluginUi.IsOpen = true; |                 _pluginUi.IsOpen = true; | ||||||
|                 ReLaunchCharacterManager(); |                 ReLaunchCharacterManager(); | ||||||
|             }; |             }; | ||||||
|  |             _downloadUi = new DownloadUi(_windowSystem, _configuration, _apiController); | ||||||
|  |  | ||||||
|             new FileCacheContext().Dispose(); // make sure db is initialized I guess |             new FileCacheContext().Dispose(); // make sure db is initialized I guess | ||||||
|  |  | ||||||
| @@ -91,6 +93,7 @@ namespace MareSynchronos | |||||||
|  |  | ||||||
|             _pluginUi?.Dispose(); |             _pluginUi?.Dispose(); | ||||||
|             _introUi?.Dispose(); |             _introUi?.Dispose(); | ||||||
|  |             _downloadUi?.Dispose(); | ||||||
|  |  | ||||||
|             _fileCacheManager?.Dispose(); |             _fileCacheManager?.Dispose(); | ||||||
|             _ipcManager?.Dispose(); |             _ipcManager?.Dispose(); | ||||||
|   | |||||||
							
								
								
									
										88
									
								
								MareSynchronos/UI/DownloadUi.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								MareSynchronos/UI/DownloadUi.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | |||||||
|  | using System; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Numerics; | ||||||
|  | using Dalamud.Interface.Windowing; | ||||||
|  | using ImGuiNET; | ||||||
|  | using MareSynchronos.Utils; | ||||||
|  | using MareSynchronos.WebAPI; | ||||||
|  |  | ||||||
|  | namespace MareSynchronos.UI; | ||||||
|  |  | ||||||
|  | public class DownloadUi : Window, IDisposable | ||||||
|  | { | ||||||
|  |     private readonly WindowSystem _windowSystem; | ||||||
|  |     private readonly Configuration _pluginConfiguration; | ||||||
|  |     private readonly ApiController _apiController; | ||||||
|  |  | ||||||
|  |     public void Dispose() | ||||||
|  |     { | ||||||
|  |         Logger.Debug("Disposing " + nameof(DownloadUi)); | ||||||
|  |         _windowSystem.RemoveWindow(this); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public DownloadUi(WindowSystem windowSystem, Configuration pluginConfiguration, ApiController apiController) : base("Mare Synchronos Downloads") | ||||||
|  |     { | ||||||
|  |         Logger.Debug("Creating " + nameof(DownloadUi)); | ||||||
|  |         _windowSystem = windowSystem; | ||||||
|  |         _pluginConfiguration = pluginConfiguration; | ||||||
|  |         _apiController = apiController; | ||||||
|  |  | ||||||
|  |         SizeConstraints = new WindowSizeConstraints() | ||||||
|  |         { | ||||||
|  |             MaximumSize = new(300, 90), | ||||||
|  |             MinimumSize = new(300, 90) | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         Flags = ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoBackground; | ||||||
|  |  | ||||||
|  |         windowSystem.AddWindow(this); | ||||||
|  |         IsOpen = true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public override void Draw() | ||||||
|  |     { | ||||||
|  |         if (!_pluginConfiguration.ShowTransferWindow) return; | ||||||
|  |         if (!_apiController.IsDownloading && !_apiController.IsUploading) return; | ||||||
|  |  | ||||||
|  |         var drawList = ImGui.GetWindowDrawList(); | ||||||
|  |         var yDistance = 20; | ||||||
|  |         var xDistance = 20; | ||||||
|  |  | ||||||
|  |         var basePosition = ImGui.GetWindowPos() + ImGui.GetWindowContentRegionMin(); | ||||||
|  |  | ||||||
|  |         if (_apiController.IsUploading) | ||||||
|  |         { | ||||||
|  |             var doneUploads = _apiController.CurrentUploads.Count(c => c.Value.Item1 == c.Value.Item2); | ||||||
|  |             var totalUploads = _apiController.CurrentUploads.Keys.Count; | ||||||
|  |             var totalUploaded = _apiController.CurrentUploads.Sum(c => c.Value.Item1); | ||||||
|  |             var totalToUpload = _apiController.CurrentUploads.Sum(c => c.Value.Item2); | ||||||
|  |             UiShared.DrawOutlinedFont(drawList, "▲", | ||||||
|  |                 new Vector2(basePosition.X + 0, basePosition.Y + (int)(yDistance * 0.5)), | ||||||
|  |                 UiShared.Color(255, 255, 255, 255), UiShared.Color(0, 0, 0, 255), 2); | ||||||
|  |             UiShared.DrawOutlinedFont(drawList, $"Uploading {doneUploads}/{totalUploads}", | ||||||
|  |                 new Vector2(basePosition.X + xDistance, basePosition.Y + yDistance * 0), | ||||||
|  |                 UiShared.Color(255, 255, 255, 255), UiShared.Color(0, 0, 0, 255), 2); | ||||||
|  |             UiShared.DrawOutlinedFont(drawList, $"{UiShared.ByteToString(totalUploaded)}/{UiShared.ByteToString(totalToUpload)}", | ||||||
|  |                 new Vector2(basePosition.X + xDistance, basePosition.Y + yDistance * 1), | ||||||
|  |                 UiShared.Color(255, 255, 255, 255), UiShared.Color(0, 0, 0, 255), 2); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (_apiController.IsDownloading) | ||||||
|  |         { | ||||||
|  |             var multBase = _apiController.IsDownloading ? 0 : 2; | ||||||
|  |             var doneDownloads = _apiController.CurrentDownloads.Count(c => c.Value.Item1 == c.Value.Item2); | ||||||
|  |             var totalDownloads = _apiController.CurrentDownloads.Keys.Count; | ||||||
|  |             var totalDownloaded = _apiController.CurrentDownloads.Sum(c => c.Value.Item1); | ||||||
|  |             var totalToDownload = _apiController.CurrentDownloads.Sum(c => c.Value.Item2); | ||||||
|  |             UiShared.DrawOutlinedFont(drawList, "▼", | ||||||
|  |                 new Vector2(basePosition.X + 0, basePosition.Y + (int)(yDistance * multBase + (yDistance * 0.5))), | ||||||
|  |                 UiShared.Color(255, 255, 255, 255), UiShared.Color(0, 0, 0, 255), 2); | ||||||
|  |             UiShared.DrawOutlinedFont(drawList, $"Downloading {doneDownloads}/{totalDownloads}", | ||||||
|  |                 new Vector2(basePosition.X + xDistance, basePosition.Y + yDistance * multBase), | ||||||
|  |                 UiShared.Color(255, 255, 255, 255), UiShared.Color(0, 0, 0, 255), 2); | ||||||
|  |             UiShared.DrawOutlinedFont(drawList, $"{UiShared.ByteToString(totalDownloaded)}/{UiShared.ByteToString(totalToDownload)}", | ||||||
|  |                 new Vector2(basePosition.X + xDistance, basePosition.Y + yDistance * (1 + multBase)), | ||||||
|  |                 UiShared.Color(255, 255, 255, 255), UiShared.Color(0, 0, 0, 255), 2); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -7,9 +7,9 @@ using MareSynchronos.Utils; | |||||||
|  |  | ||||||
| namespace MareSynchronos.UI | namespace MareSynchronos.UI | ||||||
| { | { | ||||||
|     internal class IntroUI : Window, IDisposable |     internal class IntroUi : Window, IDisposable | ||||||
|     { |     { | ||||||
|         private readonly UIShared _uiShared; |         private readonly UiShared _uiShared; | ||||||
|         private readonly Configuration _pluginConfiguration; |         private readonly Configuration _pluginConfiguration; | ||||||
|         private readonly FileCacheManager _fileCacheManager; |         private readonly FileCacheManager _fileCacheManager; | ||||||
|         private readonly WindowSystem _windowSystem; |         private readonly WindowSystem _windowSystem; | ||||||
| @@ -19,14 +19,16 @@ namespace MareSynchronos.UI | |||||||
|  |  | ||||||
|         public void Dispose() |         public void Dispose() | ||||||
|         { |         { | ||||||
|             Logger.Debug("Disposing " + nameof(IntroUI)); |             Logger.Debug("Disposing " + nameof(IntroUi)); | ||||||
|  |  | ||||||
|             _windowSystem.RemoveWindow(this); |             _windowSystem.RemoveWindow(this); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public IntroUI(WindowSystem windowSystem, UIShared uiShared, Configuration pluginConfiguration, |         public IntroUi(WindowSystem windowSystem, UiShared uiShared, Configuration pluginConfiguration, | ||||||
|             FileCacheManager fileCacheManager) : base("Mare Synchronos Setup") |             FileCacheManager fileCacheManager) : base("Mare Synchronos Setup") | ||||||
|         { |         { | ||||||
|  |             Logger.Debug("Creating " + nameof(IntroUi)); | ||||||
|  |  | ||||||
|             _uiShared = uiShared; |             _uiShared = uiShared; | ||||||
|             _pluginConfiguration = pluginConfiguration; |             _pluginConfiguration = pluginConfiguration; | ||||||
|             _fileCacheManager = fileCacheManager; |             _fileCacheManager = fileCacheManager; | ||||||
| @@ -54,12 +56,12 @@ namespace MareSynchronos.UI | |||||||
|                 ImGui.Text("Welcome to Mare Synchronos!"); |                 ImGui.Text("Welcome to Mare Synchronos!"); | ||||||
|                 ImGui.SetWindowFontScale(1.0f); |                 ImGui.SetWindowFontScale(1.0f); | ||||||
|                 ImGui.Separator(); |                 ImGui.Separator(); | ||||||
|                 UIShared.TextWrapped("Mare Synchronos is a plugin that will replicate your full current character state including all Penumbra mods to other paired Mare Synchronos users. " + |                 UiShared.TextWrapped("Mare Synchronos is a plugin that will replicate your full current character state including all Penumbra mods to other paired Mare Synchronos users. " + | ||||||
|                                   "Note that you will have to have Penumbra as well as Glamourer installed to use this plugin."); |                                   "Note that you will have to have Penumbra as well as Glamourer installed to use this plugin."); | ||||||
|                 UIShared.TextWrapped("We will have to setup a few things first before you can start using this plugin. Click on next to continue."); |                 UiShared.TextWrapped("We will have to setup a few things first before you can start using this plugin. Click on next to continue."); | ||||||
|  |  | ||||||
|                 ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); |                 ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); | ||||||
|                 UIShared.TextWrapped("Note: Any modifications you have applied through anything but Penumbra cannot be shared and your character state on other clients " + |                 UiShared.TextWrapped("Note: Any modifications you have applied through anything but Penumbra cannot be shared and your character state on other clients " + | ||||||
|                                      "might look broken because of this. If you want to use this plugin you will have to move your mods to Penumbra."); |                                      "might look broken because of this. If you want to use this plugin you will have to move your mods to Penumbra."); | ||||||
|                 ImGui.PopStyleColor(); |                 ImGui.PopStyleColor(); | ||||||
|                 if (!_uiShared.DrawOtherPluginState()) return; |                 if (!_uiShared.DrawOtherPluginState()) return; | ||||||
| @@ -82,25 +84,25 @@ namespace MareSynchronos.UI | |||||||
|                 ImGui.TextColored(ImGuiColors.DalamudRed, readThis); |                 ImGui.TextColored(ImGuiColors.DalamudRed, readThis); | ||||||
|                 ImGui.SetWindowFontScale(1.0f); |                 ImGui.SetWindowFontScale(1.0f); | ||||||
|                 ImGui.Separator(); |                 ImGui.Separator(); | ||||||
|                 UIShared.TextWrapped("All of the mod files currently active on your character as well as your current character state will be uploaded to the service you registered yourself at automatically. " + |                 UiShared.TextWrapped("All of the mod files currently active on your character as well as your current character state will be uploaded to the service you registered yourself at automatically. " + | ||||||
|                     "The plugin will exclusively upload the necessary mod files and not the whole mod."); |                     "The plugin will exclusively upload the necessary mod files and not the whole mod."); | ||||||
|                 UIShared.TextWrapped("If you are on a data capped internet connection, higher fees due to data usage depending on the amount of downloaded and uploaded mod files might occur. " + |                 UiShared.TextWrapped("If you are on a data capped internet connection, higher fees due to data usage depending on the amount of downloaded and uploaded mod files might occur. " + | ||||||
|                     "Mod files will be compressed on up- and download to save on bandwidth usage. Due to varying up- and download speeds, changes in characters might not be visible immediately. " + |                     "Mod files will be compressed on up- and download to save on bandwidth usage. Due to varying up- and download speeds, changes in characters might not be visible immediately. " + | ||||||
|                     "Files present on the service that already represent your active mod files will not be uploaded again."); |                     "Files present on the service that already represent your active mod files will not be uploaded again."); | ||||||
|                 ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); |                 ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); | ||||||
|                 UIShared.TextWrapped("The mod files you are uploading are confidential and will not be distributed to parties other than the ones who are requesting the exact same mod files. " + |                 UiShared.TextWrapped("The mod files you are uploading are confidential and will not be distributed to parties other than the ones who are requesting the exact same mod files. " + | ||||||
|                     "Please think about who you are going to pair since it is unavoidable that they will receive and locally cache the necessary mod files that you have currently in use. " + |                     "Please think about who you are going to pair since it is unavoidable that they will receive and locally cache the necessary mod files that you have currently in use. " + | ||||||
|                     "Locally cached mod files will have arbitrary file names to discourage attempts at replicating the original mod."); |                     "Locally cached mod files will have arbitrary file names to discourage attempts at replicating the original mod."); | ||||||
|                 ImGui.PopStyleColor(); |                 ImGui.PopStyleColor(); | ||||||
|                 ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); |                 ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); | ||||||
|                 UIShared.TextWrapped("The plugin creator tried their best to keep you secure. However, there is no guarantee for 100% security. Do not blindly pair your client with everyone."); |                 UiShared.TextWrapped("The plugin creator tried their best to keep you secure. However, there is no guarantee for 100% security. Do not blindly pair your client with everyone."); | ||||||
|                 ImGui.PopStyleColor(); |                 ImGui.PopStyleColor(); | ||||||
|                 UIShared.TextWrapped("Mod files that are saved on the service will remain on the service as long as there are requests for the files from clients. " + |                 UiShared.TextWrapped("Mod files that are saved on the service will remain on the service as long as there are requests for the files from clients. " + | ||||||
|                                   "After a period of not being used, the mod files will be automatically deleted. " + |                                   "After a period of not being used, the mod files will be automatically deleted. " + | ||||||
|                                   "You will also be able to wipe all the files you have personally uploaded on request. " + |                                   "You will also be able to wipe all the files you have personally uploaded on request. " + | ||||||
|                                   "The service holds no information about which mod files belong to which mod."); |                                   "The service holds no information about which mod files belong to which mod."); | ||||||
|                 ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); |                 ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); | ||||||
|                 UIShared.TextWrapped("This service is provided as-is. In case of abuse, contact darkarchon#4313 on Discord or join the Mare Synchronos Discord. " + |                 UiShared.TextWrapped("This service is provided as-is. In case of abuse, contact darkarchon#4313 on Discord or join the Mare Synchronos Discord. " + | ||||||
|                                                           "To accept those conditions hold CTRL while clicking 'I agree'"); |                                                           "To accept those conditions hold CTRL while clicking 'I agree'"); | ||||||
|                 ImGui.PopStyleColor(); |                 ImGui.PopStyleColor(); | ||||||
|                 ImGui.Separator(); |                 ImGui.Separator(); | ||||||
| @@ -120,12 +122,12 @@ namespace MareSynchronos.UI | |||||||
|                 ImGui.Text("File Cache Setup"); |                 ImGui.Text("File Cache Setup"); | ||||||
|                 ImGui.SetWindowFontScale(1.0f); |                 ImGui.SetWindowFontScale(1.0f); | ||||||
|                 ImGui.Separator(); |                 ImGui.Separator(); | ||||||
|                 UIShared.TextWrapped("To not unnecessary download files already present on your computer, Mare Synchronos will have to scan your Penumbra mod directory. " + |                 UiShared.TextWrapped("To not unnecessary download files already present on your computer, Mare Synchronos will have to scan your Penumbra mod directory. " + | ||||||
|                                   "Additionally, a local cache folder must be set where Mare Synchronos will download its local file cache to. " + |                                   "Additionally, a local cache folder must be set where Mare Synchronos will download its local file cache to. " + | ||||||
|                                   "Once the Cache Folder is set and the scan complete, this page will automatically forward to registration at a service."); |                                   "Once the Cache Folder is set and the scan complete, this page will automatically forward to registration at a service."); | ||||||
|                 UIShared.TextWrapped("Note: The initial scan, depending on the amount of mods you have, might take a while. Please wait until it is completed."); |                 UiShared.TextWrapped("Note: The initial scan, depending on the amount of mods you have, might take a while. Please wait until it is completed."); | ||||||
|                 ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); |                 ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); | ||||||
|                 UIShared.TextWrapped("Warning: once past this step you should not delete the FileCache.db of Mare Synchronos in the Plugin Configurations folder of Dalamud. " + |                 UiShared.TextWrapped("Warning: once past this step you should not delete the FileCache.db of Mare Synchronos in the Plugin Configurations folder of Dalamud. " + | ||||||
|                                   "Otherwise on the next launch a full re-scan of the file cache database will be initiated."); |                                   "Otherwise on the next launch a full re-scan of the file cache database will be initiated."); | ||||||
|                 ImGui.PopStyleColor(); |                 ImGui.PopStyleColor(); | ||||||
|                 _uiShared.DrawCacheDirectorySetting(); |                 _uiShared.DrawCacheDirectorySetting(); | ||||||
| @@ -133,7 +135,7 @@ namespace MareSynchronos.UI | |||||||
|  |  | ||||||
|                 if (!_fileCacheManager.IsScanRunning) |                 if (!_fileCacheManager.IsScanRunning) | ||||||
|                 { |                 { | ||||||
|                     UIShared.TextWrapped("You can adjust how many parallel threads will be used for scanning. Mind that ultimately it will depend on the amount of mods, your disk speed and your CPU. " + |                     UiShared.TextWrapped("You can adjust how many parallel threads will be used for scanning. Mind that ultimately it will depend on the amount of mods, your disk speed and your CPU. " + | ||||||
|                                       "More is not necessarily better, the default of 10 should be fine for most cases."); |                                       "More is not necessarily better, the default of 10 should be fine for most cases."); | ||||||
|                     _uiShared.DrawParallelScansSetting(); |                     _uiShared.DrawParallelScansSetting(); | ||||||
|  |  | ||||||
| @@ -157,18 +159,18 @@ namespace MareSynchronos.UI | |||||||
|                 if (_pluginConfiguration.ClientSecret.ContainsKey(_pluginConfiguration.ApiUri)) |                 if (_pluginConfiguration.ClientSecret.ContainsKey(_pluginConfiguration.ApiUri)) | ||||||
|                 { |                 { | ||||||
|                     ImGui.Separator(); |                     ImGui.Separator(); | ||||||
|                     UIShared.TextWrapped(_pluginConfiguration.ClientSecret[_pluginConfiguration.ApiUri]); |                     UiShared.TextWrapped(_pluginConfiguration.ClientSecret[_pluginConfiguration.ApiUri]); | ||||||
|                     ImGui.Separator(); |                     ImGui.Separator(); | ||||||
|                     if (ImGui.Button("Copy secret key to clipboard")) |                     if (ImGui.Button("Copy secret key to clipboard")) | ||||||
|                     { |                     { | ||||||
|                         ImGui.SetClipboardText(_pluginConfiguration.ClientSecret[_pluginConfiguration.ApiUri]); |                         ImGui.SetClipboardText(_pluginConfiguration.ClientSecret[_pluginConfiguration.ApiUri]); | ||||||
|                     } |                     } | ||||||
|                     ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); |                     ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); | ||||||
|                     UIShared.TextWrapped("This is the only time you will be able to see this key in the UI. You can copy it to make a backup somewhere."); |                     UiShared.TextWrapped("This is the only time you will be able to see this key in the UI. You can copy it to make a backup somewhere."); | ||||||
|                     ImGui.PopStyleColor(); |                     ImGui.PopStyleColor(); | ||||||
|                     ImGui.Separator(); |                     ImGui.Separator(); | ||||||
|                     ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.ParsedGreen); |                     ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.ParsedGreen); | ||||||
|                     UIShared.TextWrapped("You are now ready to go. Press Finish to finalize the settings and open the Mare Synchronos main UI."); |                     UiShared.TextWrapped("You are now ready to go. Press Finish to finalize the settings and open the Mare Synchronos main UI."); | ||||||
|                     ImGui.PopStyleColor(); |                     ImGui.PopStyleColor(); | ||||||
|                     ImGui.Separator(); |                     ImGui.Separator(); | ||||||
|                     if (ImGui.Button("Finish##finishIntro")) |                     if (ImGui.Button("Finish##finishIntro")) | ||||||
| @@ -179,13 +181,13 @@ namespace MareSynchronos.UI | |||||||
|                 } |                 } | ||||||
|                 else |                 else | ||||||
|                 { |                 { | ||||||
|                     UIShared.TextWrapped("You will now have to register at a service. You can use the provided central service or pick a custom one. " + |                     UiShared.TextWrapped("You will now have to register at a service. You can use the provided central service or pick a custom one. " + | ||||||
|                                          "There is no support for custom services from the plugin creator. Use at your own risk."); |                                          "There is no support for custom services from the plugin creator. Use at your own risk."); | ||||||
|                     ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); |                     ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); | ||||||
|                     UIShared.TextWrapped("On registration on a service the plugin will create and save a secret key to your plugin configuration. " + |                     UiShared.TextWrapped("On registration on a service the plugin will create and save a secret key to your plugin configuration. " + | ||||||
|                                          "Make a backup of your secret key. In case of loss, it cannot be restored. The secret key is your identification to the service " + |                                          "Make a backup of your secret key. In case of loss, it cannot be restored. The secret key is your identification to the service " + | ||||||
|                                          "to verify who you are. It is directly tied to the UID you will be receiving. In case of loss, you will have to re-register an account."); |                                          "to verify who you are. It is directly tied to the UID you will be receiving. In case of loss, you will have to re-register an account."); | ||||||
|                     UIShared.TextWrapped("Do not ever, under any circumstances, share your secret key to anyone! Likewise do not share your Mare Synchronos plugin configuration to anyone!"); |                     UiShared.TextWrapped("Do not ever, under any circumstances, share your secret key to anyone! Likewise do not share your Mare Synchronos plugin configuration to anyone!"); | ||||||
|                     ImGui.PopStyleColor(); |                     ImGui.PopStyleColor(); | ||||||
|                     _uiShared.DrawServiceSelection(); |                     _uiShared.DrawServiceSelection(); | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -5,21 +5,23 @@ using ImGuiNET; | |||||||
| using MareSynchronos.WebAPI; | using MareSynchronos.WebAPI; | ||||||
| using System; | using System; | ||||||
| using System.Linq; | using System.Linq; | ||||||
| using MareSynchronos.Managers; | using System.Threading.Tasks; | ||||||
| using MareSynchronos.Utils; | using MareSynchronos.Utils; | ||||||
|  |  | ||||||
| namespace MareSynchronos.UI | namespace MareSynchronos.UI | ||||||
| { | { | ||||||
|     class PluginUi : Window, IDisposable |     public class PluginUi : Window, IDisposable | ||||||
|     { |     { | ||||||
|         private readonly Configuration _configuration; |         private readonly Configuration _configuration; | ||||||
|         private readonly WindowSystem _windowSystem; |         private readonly WindowSystem _windowSystem; | ||||||
|         private readonly ApiController _apiController; |         private readonly ApiController _apiController; | ||||||
|         private readonly UIShared _uiShared; |         private readonly UiShared _uiShared; | ||||||
|  |  | ||||||
|         public PluginUi(WindowSystem windowSystem, |         public PluginUi(WindowSystem windowSystem, | ||||||
|             UIShared uiShared, Configuration configuration, ApiController apiController) : base("Mare Synchronos Settings", ImGuiWindowFlags.None) |             UiShared uiShared, Configuration configuration, ApiController apiController) : base("Mare Synchronos Settings", ImGuiWindowFlags.None) | ||||||
|         { |         { | ||||||
|  |             Logger.Debug("Creating " + nameof(PluginUi)); | ||||||
|  |  | ||||||
|             SizeConstraints = new WindowSizeConstraints() |             SizeConstraints = new WindowSizeConstraints() | ||||||
|             { |             { | ||||||
|                 MinimumSize = new(800, 400), |                 MinimumSize = new(800, 400), | ||||||
| @@ -85,6 +87,7 @@ namespace MareSynchronos.UI | |||||||
|                 DrawPairedClientsContent(); |                 DrawPairedClientsContent(); | ||||||
|                 DrawFileCacheSettings(); |                 DrawFileCacheSettings(); | ||||||
|                 DrawCurrentTransfers(); |                 DrawCurrentTransfers(); | ||||||
|  |                 DrawAdministration(); | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
|             { |             { | ||||||
| @@ -93,16 +96,36 @@ namespace MareSynchronos.UI | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         private void DrawAdministration() | ||||||
|  |         { | ||||||
|  |             if (ImGui.TreeNode( | ||||||
|  |                     $"User Administration")) | ||||||
|  |             { | ||||||
|  |                 if (ImGui.Button("Delete all my files")) | ||||||
|  |                 { | ||||||
|  |                     Task.Run(() => _apiController.DeleteAllMyFiles()); | ||||||
|  |                 } | ||||||
|  |                 ImGui.TreePop(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         private void DrawCurrentTransfers() |         private void DrawCurrentTransfers() | ||||||
|         { |         { | ||||||
|             if (ImGui.TreeNode( |             if (ImGui.TreeNode( | ||||||
|                     $"Current Transfers")) |                     $"Current Transfers")) | ||||||
|             { |             { | ||||||
|  |                 bool showTransferWindow = _configuration.ShowTransferWindow; | ||||||
|  |                 if (ImGui.Checkbox("Show separate Transfer window while transfers are active", ref showTransferWindow)) | ||||||
|  |                 { | ||||||
|  |                     _configuration.ShowTransferWindow = showTransferWindow; | ||||||
|  |                     _configuration.Save(); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 if (ImGui.BeginTable("TransfersTable", 2)) |                 if (ImGui.BeginTable("TransfersTable", 2)) | ||||||
|                 { |                 { | ||||||
|                     ImGui.TableSetupColumn( |                     ImGui.TableSetupColumn( | ||||||
|                         $"Uploads ({UIShared.ByteToKiB(_apiController.CurrentUploads.Sum(a => a.Value.Item1))} / {UIShared.ByteToKiB(_apiController.CurrentUploads.Sum(a => a.Value.Item2))})"); |                         $"Uploads ({UiShared.ByteToString(_apiController.CurrentUploads.Sum(a => a.Value.Item1))} / {UiShared.ByteToString(_apiController.CurrentUploads.Sum(a => a.Value.Item2))})"); | ||||||
|                     ImGui.TableSetupColumn($"Downloads ({UIShared.ByteToKiB(_apiController.CurrentDownloads.Sum(a => a.Value.Item1))} / {UIShared.ByteToKiB(_apiController.CurrentDownloads.Sum(a => a.Value.Item2))})"); |                     ImGui.TableSetupColumn($"Downloads ({UiShared.ByteToString(_apiController.CurrentDownloads.Sum(a => a.Value.Item1))} / {UiShared.ByteToString(_apiController.CurrentDownloads.Sum(a => a.Value.Item2))})"); | ||||||
|  |  | ||||||
|                     ImGui.TableHeadersRow(); |                     ImGui.TableHeadersRow(); | ||||||
|  |  | ||||||
| @@ -115,14 +138,14 @@ namespace MareSynchronos.UI | |||||||
|                         ImGui.TableHeadersRow(); |                         ImGui.TableHeadersRow(); | ||||||
|                         foreach (var hash in _apiController.CurrentUploads.Keys) |                         foreach (var hash in _apiController.CurrentUploads.Keys) | ||||||
|                         { |                         { | ||||||
|                             var color = UIShared.UploadColor(_apiController.CurrentUploads[hash]); |                             var color = UiShared.UploadColor(_apiController.CurrentUploads[hash]); | ||||||
|                             ImGui.PushStyleColor(ImGuiCol.Text, color); |                             ImGui.PushStyleColor(ImGuiCol.Text, color); | ||||||
|                             ImGui.TableNextColumn(); |                             ImGui.TableNextColumn(); | ||||||
|                             ImGui.Text(hash); |                             ImGui.Text(hash); | ||||||
|                             ImGui.TableNextColumn(); |                             ImGui.TableNextColumn(); | ||||||
|                             ImGui.Text(UIShared.ByteToKiB(_apiController.CurrentUploads[hash].Item1)); |                             ImGui.Text(UiShared.ByteToString(_apiController.CurrentUploads[hash].Item1)); | ||||||
|                             ImGui.TableNextColumn(); |                             ImGui.TableNextColumn(); | ||||||
|                             ImGui.Text(UIShared.ByteToKiB(_apiController.CurrentUploads[hash].Item2)); |                             ImGui.Text(UiShared.ByteToString(_apiController.CurrentUploads[hash].Item2)); | ||||||
|                             ImGui.PopStyleColor(); |                             ImGui.PopStyleColor(); | ||||||
|                             ImGui.TableNextRow(); |                             ImGui.TableNextRow(); | ||||||
|                         } |                         } | ||||||
| @@ -139,14 +162,14 @@ namespace MareSynchronos.UI | |||||||
|                         ImGui.TableHeadersRow(); |                         ImGui.TableHeadersRow(); | ||||||
|                         foreach (var hash in _apiController.CurrentDownloads.Keys) |                         foreach (var hash in _apiController.CurrentDownloads.Keys) | ||||||
|                         { |                         { | ||||||
|                             var color = UIShared.UploadColor(_apiController.CurrentDownloads[hash]); |                             var color = UiShared.UploadColor(_apiController.CurrentDownloads[hash]); | ||||||
|                             ImGui.PushStyleColor(ImGuiCol.Text, color); |                             ImGui.PushStyleColor(ImGuiCol.Text, color); | ||||||
|                             ImGui.TableNextColumn(); |                             ImGui.TableNextColumn(); | ||||||
|                             ImGui.Text(hash); |                             ImGui.Text(hash); | ||||||
|                             ImGui.TableNextColumn(); |                             ImGui.TableNextColumn(); | ||||||
|                             ImGui.Text(UIShared.ByteToKiB(_apiController.CurrentDownloads[hash].Item1)); |                             ImGui.Text(UiShared.ByteToString(_apiController.CurrentDownloads[hash].Item1)); | ||||||
|                             ImGui.TableNextColumn(); |                             ImGui.TableNextColumn(); | ||||||
|                             ImGui.Text(UIShared.ByteToKiB(_apiController.CurrentDownloads[hash].Item2)); |                             ImGui.Text(UiShared.ByteToString(_apiController.CurrentDownloads[hash].Item2)); | ||||||
|                             ImGui.PopStyleColor(); |                             ImGui.PopStyleColor(); | ||||||
|                             ImGui.TableNextRow(); |                             ImGui.TableNextRow(); | ||||||
|                         } |                         } | ||||||
| @@ -199,13 +222,13 @@ namespace MareSynchronos.UI | |||||||
|  |  | ||||||
|                         ImGui.TableNextColumn(); |                         ImGui.TableNextColumn(); | ||||||
|                         ImGui.TextColored( |                         ImGui.TextColored( | ||||||
|                             UIShared.GetBoolColor(item.IsSynced && !item.IsPausedFromOthers && !item.IsPaused), |                             UiShared.GetBoolColor(item.IsSynced && !item.IsPausedFromOthers && !item.IsPaused), | ||||||
|                             item.OtherUID); |                             item.OtherUID); | ||||||
|                         ImGui.TableNextColumn(); |                         ImGui.TableNextColumn(); | ||||||
|                         string pairString = !item.IsSynced |                         string pairString = !item.IsSynced | ||||||
|                             ? "Has not added you" |                             ? "Has not added you" | ||||||
|                             : ((item.IsPaused || item.IsPausedFromOthers) ? "Unpaired" : "Paired"); |                             : ((item.IsPaused || item.IsPausedFromOthers) ? "Unpaired" : "Paired"); | ||||||
|                         ImGui.TextColored(UIShared.GetBoolColor(item.IsSynced && !item.IsPaused && !item.IsPausedFromOthers), pairString); |                         ImGui.TextColored(UiShared.GetBoolColor(item.IsSynced && !item.IsPaused && !item.IsPausedFromOthers), pairString); | ||||||
|                         ImGui.TableNextColumn(); |                         ImGui.TableNextColumn(); | ||||||
|                         string charComment = _configuration.UidComments.ContainsKey(item.OtherUID) ? _configuration.UidComments[item.OtherUID] : string.Empty; |                         string charComment = _configuration.UidComments.ContainsKey(item.OtherUID) ? _configuration.UidComments[item.OtherUID] : string.Empty; | ||||||
|                         ImGui.SetNextItemWidth(400); |                         ImGui.SetNextItemWidth(400); | ||||||
|   | |||||||
| @@ -9,14 +9,14 @@ using MareSynchronos.WebAPI; | |||||||
|  |  | ||||||
| namespace MareSynchronos.UI | namespace MareSynchronos.UI | ||||||
| { | { | ||||||
|     internal class UIShared |     public class UiShared | ||||||
|     { |     { | ||||||
|         private readonly IpcManager _ipcManager; |         private readonly IpcManager _ipcManager; | ||||||
|         private readonly ApiController _apiController; |         private readonly ApiController _apiController; | ||||||
|         private readonly FileCacheManager _fileCacheManager; |         private readonly FileCacheManager _fileCacheManager; | ||||||
|         private readonly Configuration _pluginConfiguration; |         private readonly Configuration _pluginConfiguration; | ||||||
|  |  | ||||||
|         public UIShared(IpcManager ipcManager, ApiController apiController, FileCacheManager fileCacheManager, Configuration pluginConfiguration) |         public UiShared(IpcManager ipcManager, ApiController apiController, FileCacheManager fileCacheManager, Configuration pluginConfiguration) | ||||||
|         { |         { | ||||||
|             _ipcManager = ipcManager; |             _ipcManager = ipcManager; | ||||||
|             _apiController = apiController; |             _apiController = apiController; | ||||||
| @@ -87,7 +87,45 @@ namespace MareSynchronos.UI | |||||||
|         public static Vector4 UploadColor((long, long) data) => data.Item1 == 0 ? ImGuiColors.DalamudGrey : |         public static Vector4 UploadColor((long, long) data) => data.Item1 == 0 ? ImGuiColors.DalamudGrey : | ||||||
|             data.Item1 == data.Item2 ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudYellow; |             data.Item1 == data.Item2 ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudYellow; | ||||||
|  |  | ||||||
|         public static string ByteToKiB(long bytes) => (bytes / 1024.0).ToString("0.00") + " KiB"; |         public static uint Color(byte r, byte g, byte b, byte a) | ||||||
|  |         { uint ret = a; ret <<= 8; ret += b; ret <<= 8; ret += g; ret <<= 8; ret += r; return ret; } | ||||||
|  |  | ||||||
|  |         public static void DrawOutlinedFont(ImDrawListPtr drawList, string text, Vector2 textPos, uint fontColor, uint outlineColor, int thickness) | ||||||
|  |         { | ||||||
|  |             drawList.AddText(textPos with { Y = textPos.Y - thickness }, | ||||||
|  |                 outlineColor, text); | ||||||
|  |             drawList.AddText(textPos with { X = textPos.X - thickness }, | ||||||
|  |                 outlineColor, text); | ||||||
|  |             drawList.AddText(textPos with { Y = textPos.Y + thickness }, | ||||||
|  |                 outlineColor, text); | ||||||
|  |             drawList.AddText(textPos with { X = textPos.X + thickness }, | ||||||
|  |                 outlineColor, text); | ||||||
|  |             drawList.AddText(new Vector2(textPos.X - thickness, textPos.Y - thickness), | ||||||
|  |                 outlineColor, text); | ||||||
|  |             drawList.AddText(new Vector2(textPos.X + thickness, textPos.Y + thickness), | ||||||
|  |                 outlineColor, text); | ||||||
|  |             drawList.AddText(new Vector2(textPos.X - thickness, textPos.Y + thickness), | ||||||
|  |                 outlineColor, text); | ||||||
|  |             drawList.AddText(new Vector2(textPos.X + thickness, textPos.Y - thickness), | ||||||
|  |                 outlineColor, text); | ||||||
|  |  | ||||||
|  |             drawList.AddText(textPos, fontColor, text); | ||||||
|  |             drawList.AddText(textPos, fontColor, text); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public static string ByteToString(long bytes) | ||||||
|  |         { | ||||||
|  |             double output = bytes; | ||||||
|  |             var suffix = new[] { "B", "KiB", "MiB", "GiB" }; | ||||||
|  |             var suffixIdx = 0; | ||||||
|  |             while (output / 1024.0 > 1024) | ||||||
|  |             { | ||||||
|  |                 output /= 1024.0; | ||||||
|  |                 suffixIdx++; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return output.ToString("0.00") + " " + suffix[suffixIdx]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         private int _serverSelectionIndex = 0; |         private int _serverSelectionIndex = 0; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -119,8 +119,10 @@ namespace MareSynchronos.WebAPI | |||||||
|                 CurrentDownloads[file.Hash] = (0, fileSize); |                 CurrentDownloads[file.Hash] = (0, fileSize); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             List<string> downloadedHashes = new(); | ||||||
|             foreach (var file in fileReplacementDto.Where(f => CurrentDownloads[f.Hash].Item2 > 0)) |             foreach (var file in fileReplacementDto.Where(f => CurrentDownloads[f.Hash].Item2 > 0)) | ||||||
|             { |             { | ||||||
|  |                 if (downloadedHashes.Contains(file.Hash)) continue; | ||||||
|                 var hash = file.Hash; |                 var hash = file.Hash; | ||||||
|                 var data = await DownloadFile(hash); |                 var data = await DownloadFile(hash); | ||||||
|                 var extractedFile = LZ4Codec.Unwrap(data); |                 var extractedFile = LZ4Codec.Unwrap(data); | ||||||
| @@ -128,6 +130,7 @@ namespace MareSynchronos.WebAPI | |||||||
|                 var filePath = Path.Combine(_pluginConfiguration.CacheFolder, file.Hash + "." + ext); |                 var filePath = Path.Combine(_pluginConfiguration.CacheFolder, file.Hash + "." + ext); | ||||||
|                 await File.WriteAllBytesAsync(filePath, extractedFile); |                 await File.WriteAllBytesAsync(filePath, extractedFile); | ||||||
|                 Logger.Debug("File downloaded to " + filePath); |                 Logger.Debug("File downloaded to " + filePath); | ||||||
|  |                 downloadedHashes.Add(hash); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             CurrentDownloads.Clear(); |             CurrentDownloads.Clear(); | ||||||
| @@ -311,6 +314,11 @@ namespace MareSynchronos.WebAPI | |||||||
|             long fileSize = await _fileHub!.InvokeAsync<long>("GetFileSize", hash); |             long fileSize = await _fileHub!.InvokeAsync<long>("GetFileSize", hash); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         public async Task DeleteAllMyFiles() | ||||||
|  |         { | ||||||
|  |             await _fileHub!.SendAsync("DeleteAllFiles"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         private async Task DisposeHubConnections() |         private async Task DisposeHubConnections() | ||||||
|         { |         { | ||||||
|             if (_fileHub != null) |             if (_fileHub != null) | ||||||
| @@ -430,6 +438,7 @@ namespace MareSynchronos.WebAPI | |||||||
|                 UnpairedFromOther?.Invoke(characterIdentifier, EventArgs.Empty); |                 UnpairedFromOther?.Invoke(characterIdentifier, EventArgs.Empty); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private async Task UploadFile(byte[] compressedFile, string fileHash, CancellationToken uploadToken) |         private async Task UploadFile(byte[] compressedFile, string fileHash, CancellationToken uploadToken) | ||||||
|         { |         { | ||||||
|             if (uploadToken.IsCancellationRequested) return; |             if (uploadToken.IsCancellationRequested) return; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Stanley Dimant
					Stanley Dimant