add intro UI for first time registration, add FileCacheManager to scan and rescan for file changes, fix namings, polish UI for normal usage
This commit is contained in:
		| @@ -2,6 +2,9 @@ | |||||||
| using Dalamud.Plugin; | using Dalamud.Plugin; | ||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
|  | using System.IO; | ||||||
|  | using MareSynchronos.WebAPI; | ||||||
|  | using Newtonsoft.Json; | ||||||
|  |  | ||||||
| namespace MareSynchronos | namespace MareSynchronos | ||||||
| { | { | ||||||
| @@ -11,23 +14,49 @@ namespace MareSynchronos | |||||||
|         public int Version { get; set; } = 0; |         public int Version { get; set; } = 0; | ||||||
|  |  | ||||||
|         public string CacheFolder { get; set; } = string.Empty; |         public string CacheFolder { get; set; } = string.Empty; | ||||||
|         public Dictionary<string, string> ClientSecret { get; internal set; } = new(); |         public Dictionary<string, string> ClientSecret { get; set; } = new(); | ||||||
|         public string ApiUri { get; internal set; } = string.Empty; |         public Dictionary<string, string> UidComments { get; set; } = new(); | ||||||
|         public bool UseCustomService { get; internal set; } |         private string _apiUri = string.Empty; | ||||||
|  |         public string ApiUri | ||||||
|  |         { | ||||||
|  |             get => string.IsNullOrEmpty(_apiUri) ? ApiController.MainServiceUri : _apiUri; | ||||||
|  |             set => _apiUri = value; | ||||||
|  |         } | ||||||
|  |         public bool UseCustomService { get; set; } | ||||||
|  |         public bool InitialScanComplete { get; set; } | ||||||
|  |         public bool AcceptedAgreement { get; set; } | ||||||
|  |         private int _maxParallelScan = 10; | ||||||
|  |         public int MaxParallelScan | ||||||
|  |         { | ||||||
|  |             get => _maxParallelScan; | ||||||
|  |             set | ||||||
|  |             { | ||||||
|  |                 _maxParallelScan = value switch | ||||||
|  |                 { | ||||||
|  |                     < 0 => 1, | ||||||
|  |                     > 20 => 10, | ||||||
|  |                     _ => value | ||||||
|  |                 }; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         [JsonIgnore] | ||||||
|  |         public bool HasValidSetup => AcceptedAgreement && InitialScanComplete && !string.IsNullOrEmpty(CacheFolder) && | ||||||
|  |                                      Directory.Exists(CacheFolder) && ClientSecret.ContainsKey(ApiUri); | ||||||
|  |  | ||||||
|         // the below exist just to make saving less cumbersome |         // the below exist just to make saving less cumbersome | ||||||
|  |  | ||||||
|         [NonSerialized] |         [NonSerialized] | ||||||
|         private DalamudPluginInterface? pluginInterface; |         private DalamudPluginInterface? _pluginInterface; | ||||||
|  |  | ||||||
|         public void Initialize(DalamudPluginInterface pluginInterface) |         public void Initialize(DalamudPluginInterface pluginInterface) | ||||||
|         { |         { | ||||||
|             this.pluginInterface = pluginInterface; |             this._pluginInterface = pluginInterface; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public void Save() |         public void Save() | ||||||
|         { |         { | ||||||
|             this.pluginInterface!.SavePluginConfig(this); |             this._pluginInterface!.SavePluginConfig(this); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ namespace MareSynchronos.Factories | |||||||
|  |  | ||||||
|         public FileReplacement Create() |         public FileReplacement Create() | ||||||
|         { |         { | ||||||
|             if (!ipcManager.CheckPenumbraAPI()) |             if (!ipcManager.CheckPenumbraApi()) | ||||||
|             { |             { | ||||||
|                 throw new System.Exception(); |                 throw new System.Exception(); | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -45,7 +45,7 @@ namespace MareSynchronos.Managers | |||||||
|         private string _lastSentHash = string.Empty; |         private string _lastSentHash = string.Empty; | ||||||
|         private Task? _playerChangedTask = null; |         private Task? _playerChangedTask = null; | ||||||
|  |  | ||||||
|         private HashSet<string> onlineWhitelistedUsers = new(); |         private HashSet<string> _onlineWhitelistedUsers = new(); | ||||||
|  |  | ||||||
|         public CharacterManager(ClientState clientState, Framework framework, ApiController apiController, ObjectTable objectTable, IpcManager ipcManager, FileReplacementFactory factory, |         public CharacterManager(ClientState clientState, Framework framework, ApiController apiController, ObjectTable objectTable, IpcManager ipcManager, FileReplacementFactory factory, | ||||||
|                     Configuration pluginConfiguration) |                     Configuration pluginConfiguration) | ||||||
| @@ -154,6 +154,11 @@ namespace MareSynchronos.Managers | |||||||
|             _watcher.Disable(); |             _watcher.Disable(); | ||||||
|             _watcher.PlayerChanged -= Watcher_PlayerChanged; |             _watcher.PlayerChanged -= Watcher_PlayerChanged; | ||||||
|             _watcher?.Dispose(); |             _watcher?.Dispose(); | ||||||
|  |  | ||||||
|  |             foreach (var character in _onlineWhitelistedUsers) | ||||||
|  |             { | ||||||
|  |                 RestoreCharacter(character); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public void StopWatchPlayer(string name) |         public void StopWatchPlayer(string name) | ||||||
| @@ -164,7 +169,7 @@ namespace MareSynchronos.Managers | |||||||
|         public async Task UpdatePlayersFromService(Dictionary<string, PlayerCharacter> currentLocalPlayers) |         public async Task UpdatePlayersFromService(Dictionary<string, PlayerCharacter> currentLocalPlayers) | ||||||
|         { |         { | ||||||
|             PluginLog.Debug("Updating local players from service"); |             PluginLog.Debug("Updating local players from service"); | ||||||
|             currentLocalPlayers = currentLocalPlayers.Where(k => onlineWhitelistedUsers.Contains(k.Key)) |             currentLocalPlayers = currentLocalPlayers.Where(k => _onlineWhitelistedUsers.Contains(k.Key)) | ||||||
|                 .ToDictionary(k => k.Key, k => k.Value); |                 .ToDictionary(k => k.Key, k => k.Value); | ||||||
|             await _apiController.GetCharacterData(currentLocalPlayers |             await _apiController.GetCharacterData(currentLocalPlayers | ||||||
|                 .ToDictionary( |                 .ToDictionary( | ||||||
| @@ -205,10 +210,10 @@ namespace MareSynchronos.Managers | |||||||
|  |  | ||||||
|             Task.WaitAll(apiTask); |             Task.WaitAll(apiTask); | ||||||
|  |  | ||||||
|             onlineWhitelistedUsers = new HashSet<string>(apiTask.Result); |             _onlineWhitelistedUsers = new HashSet<string>(apiTask.Result); | ||||||
|             var assignTask = AssignLocalPlayersData(); |             var assignTask = AssignLocalPlayersData(); | ||||||
|             Task.WaitAll(assignTask); |             Task.WaitAll(assignTask); | ||||||
|             PluginLog.Debug("Online and whitelisted users: " + string.Join(",", onlineWhitelistedUsers)); |             PluginLog.Debug("Online and whitelisted users: " + string.Join(",", _onlineWhitelistedUsers)); | ||||||
|  |  | ||||||
|             _framework.Update += Framework_Update; |             _framework.Update += Framework_Update; | ||||||
|             _ipcManager.PenumbraRedrawEvent += IpcManager_PenumbraRedrawEvent; |             _ipcManager.PenumbraRedrawEvent += IpcManager_PenumbraRedrawEvent; | ||||||
| @@ -221,6 +226,12 @@ namespace MareSynchronos.Managers | |||||||
|             _framework.Update -= Framework_Update; |             _framework.Update -= Framework_Update; | ||||||
|             _ipcManager.PenumbraRedrawEvent -= IpcManager_PenumbraRedrawEvent; |             _ipcManager.PenumbraRedrawEvent -= IpcManager_PenumbraRedrawEvent; | ||||||
|             _clientState.TerritoryChanged -= ClientState_TerritoryChanged; |             _clientState.TerritoryChanged -= ClientState_TerritoryChanged; | ||||||
|  |             foreach (var character in _onlineWhitelistedUsers) | ||||||
|  |             { | ||||||
|  |                 RestoreCharacter(character); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             _lastSentHash = string.Empty; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private void ApiControllerOnAddedToWhitelist(object? sender, EventArgs e) |         private void ApiControllerOnAddedToWhitelist(object? sender, EventArgs e) | ||||||
| @@ -230,7 +241,7 @@ namespace MareSynchronos.Managers | |||||||
|             var players = GetLocalPlayers(); |             var players = GetLocalPlayers(); | ||||||
|             if (players.ContainsKey(characterHash)) |             if (players.ContainsKey(characterHash)) | ||||||
|             { |             { | ||||||
|                 PluginLog.Debug("You got added to a whitelist, restoring data for " + characterHash); |                 PluginLog.Debug("Removed from whitelist, restoring data for " + characterHash); | ||||||
|                 _ = _apiController.GetCharacterData(new Dictionary<string, int> { { characterHash, (int)players[characterHash].ClassJob.Id } }); |                 _ = _apiController.GetCharacterData(new Dictionary<string, int> { { characterHash, (int)players[characterHash].ClassJob.Id } }); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -287,8 +298,8 @@ namespace MareSynchronos.Managers | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             PluginLog.Debug("Assigned hash to visible player: " + otherPlayerName); |             PluginLog.Debug("Assigned hash to visible player: " + otherPlayerName); | ||||||
|             /*ipcManager.PenumbraRemoveTemporaryCollection(otherPlayerName); |             _ipcManager.PenumbraRemoveTemporaryCollection(otherPlayerName); | ||||||
|             ipcManager.PenumbraCreateTemporaryCollection(otherPlayerName); |             _ipcManager.PenumbraCreateTemporaryCollection(otherPlayerName); | ||||||
|             Dictionary<string, string> moddedPaths = new(); |             Dictionary<string, string> moddedPaths = new(); | ||||||
|             using (var db = new FileCacheContext()) |             using (var db = new FileCacheContext()) | ||||||
|             { |             { | ||||||
| @@ -306,41 +317,49 @@ namespace MareSynchronos.Managers | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             ipcManager.PenumbraSetTemporaryMods(otherPlayerName, moddedPaths);*/ |             _ipcManager.PenumbraSetTemporaryMods(otherPlayerName, moddedPaths); | ||||||
|             _ipcManager.GlamourerApplyCharacterCustomization(e.CharacterData.GlamourerData, otherPlayerName); |             _ipcManager.GlamourerApplyCharacterCustomization(e.CharacterData.GlamourerData, otherPlayerName); | ||||||
|             //ipcManager.PenumbraRedraw(otherPlayerName); |             _ipcManager.PenumbraRedraw(otherPlayerName); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private void ApiControllerOnRemovedFromWhitelist(object? sender, EventArgs e) |         private void ApiControllerOnRemovedFromWhitelist(object? sender, EventArgs e) | ||||||
|         { |         { | ||||||
|             var characterHash = (string?)sender; |             var characterHash = (string?)sender; | ||||||
|             if (string.IsNullOrEmpty(characterHash)) return; |             if (string.IsNullOrEmpty(characterHash)) return; | ||||||
|  |             RestoreCharacter(characterHash); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void RestoreCharacter(string characterHash) | ||||||
|  |         { | ||||||
|             var players = GetLocalPlayers(); |             var players = GetLocalPlayers(); | ||||||
|  |  | ||||||
|             foreach (var entry in _characterCache.Where(c => c.Key.Item1 == characterHash)) |             foreach (var entry in _characterCache.Where(c => c.Key.Item1 == characterHash)) | ||||||
|             { |             { | ||||||
|                 _characterCache.Remove(entry.Key); |                 _characterCache.Remove(entry.Key); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             var playerName = players.SingleOrDefault(p => p.Key == characterHash).Value.Name.ToString() ?? null; |             foreach (var player in players) | ||||||
|             if (playerName != null) |  | ||||||
|             { |             { | ||||||
|  |                 if (player.Key != characterHash) continue; | ||||||
|  |                 var playerName = player.Value.Name.ToString(); | ||||||
|                 RestorePreviousCharacter(playerName); |                 RestorePreviousCharacter(playerName); | ||||||
|                 PluginLog.Debug("Removed from whitelist, restoring glamourer state for " + playerName); |                 PluginLog.Debug("Removed from whitelist, restoring glamourer state for " + playerName); | ||||||
|                 _ipcManager.PenumbraRemoveTemporaryCollection(playerName); |                 _ipcManager.PenumbraRemoveTemporaryCollection(playerName); | ||||||
|                 _ipcManager.GlamourerRevertCharacterCustomization(playerName); |                 _ipcManager.GlamourerRevertCharacterCustomization(playerName); | ||||||
|  |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private void ApiControllerOnWhitelistedPlayerOffline(object? sender, EventArgs e) |         private void ApiControllerOnWhitelistedPlayerOffline(object? sender, EventArgs e) | ||||||
|         { |         { | ||||||
|             PluginLog.Debug("Player offline: " + sender!); |             PluginLog.Debug("Player offline: " + sender!); | ||||||
|             onlineWhitelistedUsers.Remove((string)sender!); |             _onlineWhitelistedUsers.Remove((string)sender!); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private void ApiControllerOnWhitelistedPlayerOnline(object? sender, EventArgs e) |         private void ApiControllerOnWhitelistedPlayerOnline(object? sender, EventArgs e) | ||||||
|         { |         { | ||||||
|             PluginLog.Debug("Player online: " + sender!); |             PluginLog.Debug("Player online: " + sender!); | ||||||
|             onlineWhitelistedUsers.Add((string)sender!); |             _onlineWhitelistedUsers.Add((string)sender!); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private async Task AssignLocalPlayersData() |         private async Task AssignLocalPlayersData() | ||||||
| @@ -351,11 +370,7 @@ namespace MareSynchronos.Managers | |||||||
|             { |             { | ||||||
|                 if (currentLocalPlayers.ContainsKey(player.Key.Item1)) |                 if (currentLocalPlayers.ContainsKey(player.Key.Item1)) | ||||||
|                 { |                 { | ||||||
|                     await Task.Run(() => ApiControllerOnCharacterReceived(null, new CharacterReceivedEventArgs |                     await Task.Run(() => ApiControllerOnCharacterReceived(null, new CharacterReceivedEventArgs(player.Key.Item1, player.Value))); | ||||||
|                     { |  | ||||||
|                         CharacterNameHash = player.Key.Item1, |  | ||||||
|                         CharacterData = player.Value |  | ||||||
|                     })); |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -413,7 +428,7 @@ namespace MareSynchronos.Managers | |||||||
|                     var pObj = (PlayerCharacter)obj; |                     var pObj = (PlayerCharacter)obj; | ||||||
|                     var hashedName = Crypto.GetHash256(pObj.Name.ToString() + pObj.HomeWorld.Id.ToString()); |                     var hashedName = Crypto.GetHash256(pObj.Name.ToString() + pObj.HomeWorld.Id.ToString()); | ||||||
|  |  | ||||||
|                     if (!onlineWhitelistedUsers.Contains(hashedName)) continue; |                     if (!_onlineWhitelistedUsers.Contains(hashedName)) continue; | ||||||
|  |  | ||||||
|                     localPlayersList.Add(hashedName); |                     localPlayersList.Add(hashedName); | ||||||
|                     if (!_cachedLocalPlayers.ContainsKey(hashedName)) newPlayers[hashedName] = pObj; |                     if (!_cachedLocalPlayers.ContainsKey(hashedName)) newPlayers[hashedName] = pObj; | ||||||
| @@ -455,7 +470,7 @@ namespace MareSynchronos.Managers | |||||||
|                 string playerName = obj.Name.ToString(); |                 string playerName = obj.Name.ToString(); | ||||||
|                 if (playerName == GetPlayerName()) continue; |                 if (playerName == GetPlayerName()) continue; | ||||||
|                 var playerObject = (PlayerCharacter)obj; |                 var playerObject = (PlayerCharacter)obj; | ||||||
|                 allLocalPlayers.Add(Crypto.GetHash256(playerObject.Name.ToString() + playerObject.HomeWorld.Id.ToString()), playerObject); |                 allLocalPlayers[Crypto.GetHash256(playerObject.Name.ToString() + playerObject.HomeWorld.Id.ToString())] = playerObject; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             return allLocalPlayers; |             return allLocalPlayers; | ||||||
| @@ -509,7 +524,7 @@ namespace MareSynchronos.Managers | |||||||
|                 var cacheDto = characterCacheTask.Result.ToCharacterCacheDto(); |                 var cacheDto = characterCacheTask.Result.ToCharacterCacheDto(); | ||||||
|                 if (cacheDto.Hash == _lastSentHash) |                 if (cacheDto.Hash == _lastSentHash) | ||||||
|                 { |                 { | ||||||
|                     PluginLog.Warning("Not sending data, already sent"); |                     PluginLog.Debug("Not sending data, already sent"); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|                 Task.WaitAll(_apiController.SendCharacterData(cacheDto, GetLocalPlayers().Select(d => d.Key).ToList())); |                 Task.WaitAll(_apiController.SendCharacterData(cacheDto, GetLocalPlayers().Select(d => d.Key).ToList())); | ||||||
|   | |||||||
							
								
								
									
										187
									
								
								MareSynchronos/Managers/FileCacheManager.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								MareSynchronos/Managers/FileCacheManager.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,187 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Concurrent; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Diagnostics; | ||||||
|  | using System.IO; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Runtime.CompilerServices; | ||||||
|  | using System.Threading; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  | using Dalamud.Logging; | ||||||
|  | using MareSynchronos.Factories; | ||||||
|  | using MareSynchronos.FileCacheDB; | ||||||
|  |  | ||||||
|  | namespace MareSynchronos.Managers | ||||||
|  | { | ||||||
|  |     internal class FileCacheManager : IDisposable | ||||||
|  |     { | ||||||
|  |         private const int MinutesForScan = 10; | ||||||
|  |         private readonly FileCacheFactory _fileCacheFactory; | ||||||
|  |         private readonly IpcManager _ipcManager; | ||||||
|  |         private readonly Configuration _pluginConfiguration; | ||||||
|  |         private CancellationTokenSource? _scanCancellationTokenSource; | ||||||
|  |         private System.Timers.Timer? _scanScheduler; | ||||||
|  |         private Task? _scanTask; | ||||||
|  |         private Stopwatch? _timerStopWatch; | ||||||
|  |         public FileCacheManager(FileCacheFactory fileCacheFactory, IpcManager ipcManager, Configuration pluginConfiguration) | ||||||
|  |         { | ||||||
|  |             _fileCacheFactory = fileCacheFactory; | ||||||
|  |             _ipcManager = ipcManager; | ||||||
|  |             _pluginConfiguration = pluginConfiguration; | ||||||
|  |  | ||||||
|  |             if (_ipcManager.CheckPenumbraApi() | ||||||
|  |                && _pluginConfiguration.AcceptedAgreement | ||||||
|  |                && !string.IsNullOrEmpty(_pluginConfiguration.CacheFolder) | ||||||
|  |                && _pluginConfiguration.ClientSecret.ContainsKey(_pluginConfiguration.ApiUri) | ||||||
|  |                && !string.IsNullOrEmpty(_ipcManager.PenumbraModDirectory())) | ||||||
|  |             { | ||||||
|  |                 StartInitialScan(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public long CurrentFileProgress { get; private set; } | ||||||
|  |         public bool IsScanRunning => !_scanTask?.IsCompleted ?? false; | ||||||
|  |  | ||||||
|  |         public TimeSpan TimeToNextScan => TimeSpan.FromMinutes(MinutesForScan).Subtract(_timerStopWatch?.Elapsed ?? TimeSpan.FromMinutes(MinutesForScan)); | ||||||
|  |         public long TotalFiles { get; private set; } | ||||||
|  |  | ||||||
|  |         public void Dispose() | ||||||
|  |         { | ||||||
|  |             PluginLog.Debug("Disposing File Cache Manager"); | ||||||
|  |             _scanScheduler?.Stop(); | ||||||
|  |             _scanCancellationTokenSource?.Cancel(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void StartInitialScan() | ||||||
|  |         { | ||||||
|  |             _scanCancellationTokenSource = new CancellationTokenSource(); | ||||||
|  |             _scanTask = StartFileScan(_scanCancellationTokenSource.Token); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private async Task StartFileScan(CancellationToken ct) | ||||||
|  |         { | ||||||
|  |             _scanCancellationTokenSource = new CancellationTokenSource(); | ||||||
|  |             var penumbraDir = _ipcManager.PenumbraModDirectory()!; | ||||||
|  |             PluginLog.Debug("Getting files from " + penumbraDir); | ||||||
|  |             var scannedFiles = new ConcurrentDictionary<string, bool>( | ||||||
|  |                 Directory.EnumerateFiles(penumbraDir, "*.*", SearchOption.AllDirectories) | ||||||
|  |                                 .Select(s => s.ToLowerInvariant()) | ||||||
|  |                                 .Where(f => f.Contains(@"\chara\") && (f.EndsWith(".tex") || f.EndsWith(".mdl") || f.EndsWith(".mtrl"))) | ||||||
|  |                                 .Select(p => new KeyValuePair<string, bool>(p, false))); | ||||||
|  |             List<FileCache> fileCaches; | ||||||
|  |             await using (FileCacheContext db = new()) | ||||||
|  |             { | ||||||
|  |                 fileCaches = db.FileCaches.ToList(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             TotalFiles = scannedFiles.Count; | ||||||
|  |  | ||||||
|  |             var fileCachesToUpdate = new ConcurrentBag<FileCache>(); | ||||||
|  |             var fileCachesToDelete = new ConcurrentBag<FileCache>(); | ||||||
|  |             var fileCachesToAdd = new ConcurrentBag<FileCache>(); | ||||||
|  |  | ||||||
|  |             PluginLog.Debug("Getting file list from Database"); | ||||||
|  |             // scan files from database | ||||||
|  |             Parallel.ForEach(fileCaches, new ParallelOptions() | ||||||
|  |             { | ||||||
|  |                 MaxDegreeOfParallelism = _pluginConfiguration.MaxParallelScan, | ||||||
|  |                 CancellationToken = ct, | ||||||
|  |             }, | ||||||
|  |             cache => | ||||||
|  |             { | ||||||
|  |                 if (ct.IsCancellationRequested) return; | ||||||
|  |                 if (!File.Exists(cache.Filepath)) | ||||||
|  |                 { | ||||||
|  |                     fileCachesToDelete.Add(cache); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     if (scannedFiles.ContainsKey(cache.Filepath)) | ||||||
|  |                     { | ||||||
|  |                         scannedFiles[cache.Filepath] = true; | ||||||
|  |                     } | ||||||
|  |                     FileInfo fileInfo = new(cache.Filepath); | ||||||
|  |                     if (fileInfo.LastWriteTimeUtc.Ticks == long.Parse(cache.LastModifiedDate)) return; | ||||||
|  |                     _fileCacheFactory.UpdateFileCache(cache); | ||||||
|  |                     fileCachesToUpdate.Add(cache); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 var files = CurrentFileProgress; | ||||||
|  |                 Interlocked.Increment(ref files); | ||||||
|  |                 CurrentFileProgress = files; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             if (ct.IsCancellationRequested) return; | ||||||
|  |  | ||||||
|  |             // scan new files | ||||||
|  |             Parallel.ForEach(scannedFiles.Where(c => c.Value == false), new ParallelOptions() | ||||||
|  |             { | ||||||
|  |                 MaxDegreeOfParallelism = _pluginConfiguration.MaxParallelScan, | ||||||
|  |                 CancellationToken = ct | ||||||
|  |             }, | ||||||
|  |             file => | ||||||
|  |             { | ||||||
|  |                 fileCachesToAdd.Add(_fileCacheFactory.Create(file.Key)); | ||||||
|  |  | ||||||
|  |                 var files = CurrentFileProgress; | ||||||
|  |                 Interlocked.Increment(ref files); | ||||||
|  |                 CurrentFileProgress = files; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             await using (FileCacheContext db = new()) | ||||||
|  |             { | ||||||
|  |                 if (fileCachesToAdd.Any() || fileCachesToUpdate.Any() || fileCachesToDelete.Any()) | ||||||
|  |                 { | ||||||
|  |                     db.FileCaches.AddRange(fileCachesToAdd); | ||||||
|  |                     db.FileCaches.UpdateRange(fileCachesToUpdate); | ||||||
|  |                     db.FileCaches.RemoveRange(fileCachesToDelete); | ||||||
|  |  | ||||||
|  |                     await db.SaveChangesAsync(ct); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             PluginLog.Debug("Scan complete"); | ||||||
|  |             TotalFiles = 0; | ||||||
|  |             CurrentFileProgress = 0; | ||||||
|  |  | ||||||
|  |             if (!_pluginConfiguration.InitialScanComplete) | ||||||
|  |             { | ||||||
|  |                 _pluginConfiguration.InitialScanComplete = true; | ||||||
|  |                 _pluginConfiguration.Save(); | ||||||
|  |                 _timerStopWatch = Stopwatch.StartNew(); | ||||||
|  |                 StartScheduler(); | ||||||
|  |             } | ||||||
|  |             else if (_timerStopWatch == null) | ||||||
|  |             { | ||||||
|  |                 StartScheduler(); | ||||||
|  |                 _timerStopWatch = Stopwatch.StartNew(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void StartScheduler() | ||||||
|  |         { | ||||||
|  |             PluginLog.Debug("Scheduling next scan for in " + MinutesForScan + " minutes"); | ||||||
|  |             _scanScheduler = new System.Timers.Timer(TimeSpan.FromMinutes(MinutesForScan).TotalMilliseconds) | ||||||
|  |             { | ||||||
|  |                 AutoReset = false, | ||||||
|  |                 Enabled = false, | ||||||
|  |             }; | ||||||
|  |             _scanScheduler.AutoReset = true; | ||||||
|  |             _scanScheduler.Elapsed += (_, _) => | ||||||
|  |             { | ||||||
|  |                 _timerStopWatch?.Stop(); | ||||||
|  |                 if (_scanTask?.IsCompleted ?? false) | ||||||
|  |                 { | ||||||
|  |                     PluginLog.Warning("Scanning task is still running, not reinitiating."); | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 PluginLog.Debug("Initiating periodic scan for mod changes"); | ||||||
|  |                 _scanTask = StartFileScan(_scanCancellationTokenSource!.Token); | ||||||
|  |                 _timerStopWatch = Stopwatch.StartNew(); | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             _scanScheduler.Start(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -10,23 +10,23 @@ namespace MareSynchronos.Managers | |||||||
| { | { | ||||||
|     public class IpcManager : IDisposable |     public class IpcManager : IDisposable | ||||||
|     { |     { | ||||||
|         private readonly DalamudPluginInterface pluginInterface; |         private readonly DalamudPluginInterface _pluginInterface; | ||||||
|         private ICallGateSubscriber<object> penumbraInit; |         private readonly ICallGateSubscriber<object> _penumbraInit; | ||||||
|         private ICallGateSubscriber<string, string, string>? penumbraResolvePath; |         private readonly ICallGateSubscriber<string, string, string>? _penumbraResolvePath; | ||||||
|         private ICallGateSubscriber<string>? penumbraResolveModDir; |         private readonly ICallGateSubscriber<string>? _penumbraResolveModDir; | ||||||
|         private ICallGateSubscriber<string>? glamourerGetCharacterCustomization; |         private readonly ICallGateSubscriber<string>? _glamourerGetCharacterCustomization; | ||||||
|         private ICallGateSubscriber<string, string, object>? glamourerApplyCharacterCustomization; |         private readonly ICallGateSubscriber<string, string, object>? _glamourerApplyCharacterCustomization; | ||||||
|         private ICallGateSubscriber<int> penumbraApiVersion; |         private readonly ICallGateSubscriber<int> _penumbraApiVersion; | ||||||
|         private ICallGateSubscriber<int> glamourerApiVersion; |         private readonly ICallGateSubscriber<int> _glamourerApiVersion; | ||||||
|         private ICallGateSubscriber<IntPtr, int, object?> penumbraObjectIsRedrawn; |         private readonly ICallGateSubscriber<IntPtr, int, object?> _penumbraObjectIsRedrawn; | ||||||
|         private ICallGateSubscriber<string, int, object>? penumbraRedraw; |         private readonly ICallGateSubscriber<string, int, object>? _penumbraRedraw; | ||||||
|         private ICallGateSubscriber<string, string, string[]>? penumbraReverseResolvePath; |         private readonly ICallGateSubscriber<string, string, string[]>? _penumbraReverseResolvePath; | ||||||
|         private ICallGateSubscriber<string, object> glamourerRevertCustomization; |         private readonly ICallGateSubscriber<string, object> _glamourerRevertCustomization; | ||||||
|  |  | ||||||
|         private ICallGateSubscriber<string, string, IReadOnlyDictionary<string, string>, List<string>, int, int> |         private readonly ICallGateSubscriber<string, string, IReadOnlyDictionary<string, string>, List<string>, int, int> | ||||||
|             penumbraSetTemporaryMod; |             _penumbraSetTemporaryMod; | ||||||
|         private ICallGateSubscriber<string, string, bool, (int, string)> penumbraCreateTemporaryCollection; |         private readonly ICallGateSubscriber<string, string, bool, (int, string)> _penumbraCreateTemporaryCollection; | ||||||
|         private ICallGateSubscriber<string, int> penumbraRemoveTemporaryCollection; |         private readonly ICallGateSubscriber<string, int> _penumbraRemoveTemporaryCollection; | ||||||
|  |  | ||||||
|         public bool Initialized { get; private set; } = false; |         public bool Initialized { get; private set; } = false; | ||||||
|  |  | ||||||
| @@ -34,41 +34,41 @@ namespace MareSynchronos.Managers | |||||||
|  |  | ||||||
|         public IpcManager(DalamudPluginInterface pi) |         public IpcManager(DalamudPluginInterface pi) | ||||||
|         { |         { | ||||||
|             pluginInterface = pi; |             _pluginInterface = pi; | ||||||
|  |  | ||||||
|             penumbraInit = pluginInterface.GetIpcSubscriber<object>("Penumbra.Initialized"); |             _penumbraInit = _pluginInterface.GetIpcSubscriber<object>("Penumbra.Initialized"); | ||||||
|             penumbraResolvePath = pluginInterface.GetIpcSubscriber<string, string, string>("Penumbra.ResolveCharacterPath"); |             _penumbraResolvePath = _pluginInterface.GetIpcSubscriber<string, string, string>("Penumbra.ResolveCharacterPath"); | ||||||
|             penumbraResolveModDir = pluginInterface.GetIpcSubscriber<string>("Penumbra.GetModDirectory"); |             _penumbraResolveModDir = _pluginInterface.GetIpcSubscriber<string>("Penumbra.GetModDirectory"); | ||||||
|             penumbraRedraw = pluginInterface.GetIpcSubscriber<string, int, object>("Penumbra.RedrawObjectByName"); |             _penumbraRedraw = _pluginInterface.GetIpcSubscriber<string, int, object>("Penumbra.RedrawObjectByName"); | ||||||
|             glamourerGetCharacterCustomization = pluginInterface.GetIpcSubscriber<string>("Glamourer.GetCharacterCustomization"); |             _glamourerGetCharacterCustomization = _pluginInterface.GetIpcSubscriber<string>("Glamourer.GetCharacterCustomization"); | ||||||
|             glamourerApplyCharacterCustomization = pluginInterface.GetIpcSubscriber<string, string, object>("Glamourer.ApplyCharacterCustomization"); |             _glamourerApplyCharacterCustomization = _pluginInterface.GetIpcSubscriber<string, string, object>("Glamourer.ApplyCharacterCustomization"); | ||||||
|             penumbraReverseResolvePath = pluginInterface.GetIpcSubscriber<string, string, string[]>("Penumbra.ReverseResolvePath"); |             _penumbraReverseResolvePath = _pluginInterface.GetIpcSubscriber<string, string, string[]>("Penumbra.ReverseResolvePath"); | ||||||
|             penumbraApiVersion = pluginInterface.GetIpcSubscriber<int>("Penumbra.ApiVersion"); |             _penumbraApiVersion = _pluginInterface.GetIpcSubscriber<int>("Penumbra.ApiVersion"); | ||||||
|             glamourerApiVersion = pluginInterface.GetIpcSubscriber<int>("Glamourer.ApiVersion"); |             _glamourerApiVersion = _pluginInterface.GetIpcSubscriber<int>("Glamourer.ApiVersion"); | ||||||
|             glamourerRevertCustomization = pluginInterface.GetIpcSubscriber<string, object>("Glamourer.RevertCharacterCustomization"); |             _glamourerRevertCustomization = _pluginInterface.GetIpcSubscriber<string, object>("Glamourer.RevertCharacterCustomization"); | ||||||
|             penumbraObjectIsRedrawn = pluginInterface.GetIpcSubscriber<IntPtr, int, object?>("Penumbra.GameObjectRedrawn"); |             _penumbraObjectIsRedrawn = _pluginInterface.GetIpcSubscriber<IntPtr, int, object?>("Penumbra.GameObjectRedrawn"); | ||||||
|  |  | ||||||
|             penumbraObjectIsRedrawn.Subscribe(RedrawEvent); |             _penumbraObjectIsRedrawn.Subscribe(RedrawEvent); | ||||||
|             penumbraInit.Subscribe(RedrawSelf); |             _penumbraInit.Subscribe(RedrawSelf); | ||||||
|  |  | ||||||
|             penumbraSetTemporaryMod = |             _penumbraSetTemporaryMod = | ||||||
|                 pluginInterface |                 _pluginInterface | ||||||
|                     .GetIpcSubscriber<string, string, IReadOnlyDictionary<string, string>, List<string>, int, |                     .GetIpcSubscriber<string, string, IReadOnlyDictionary<string, string>, List<string>, int, | ||||||
|                         int>("Penumbra.AddTemporaryMod"); |                         int>("Penumbra.AddTemporaryMod"); | ||||||
|  |  | ||||||
|             penumbraCreateTemporaryCollection = |             _penumbraCreateTemporaryCollection = | ||||||
|                 pluginInterface.GetIpcSubscriber<string, string, bool, (int, string)>("Penumbra.CreateTemporaryCollection"); |                 _pluginInterface.GetIpcSubscriber<string, string, bool, (int, string)>("Penumbra.CreateTemporaryCollection"); | ||||||
|             penumbraRemoveTemporaryCollection = |             _penumbraRemoveTemporaryCollection = | ||||||
|                 pluginInterface.GetIpcSubscriber<string, int>("Penumbra.RemoveTemporaryCollection"); |                 _pluginInterface.GetIpcSubscriber<string, int>("Penumbra.RemoveTemporaryCollection"); | ||||||
|  |  | ||||||
|             Initialized = true; |             Initialized = true; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public bool CheckPenumbraAPI() |         public bool CheckPenumbraApi() | ||||||
|         { |         { | ||||||
|             try |             try | ||||||
|             { |             { | ||||||
|                 return penumbraApiVersion.InvokeFunc() >= 4; |                 return _penumbraApiVersion.InvokeFunc() >= 4; | ||||||
|             } |             } | ||||||
|             catch |             catch | ||||||
|             { |             { | ||||||
| @@ -76,11 +76,11 @@ namespace MareSynchronos.Managers | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public bool CheckGlamourerAPI() |         public bool CheckGlamourerApi() | ||||||
|         { |         { | ||||||
|             try |             try | ||||||
|             { |             { | ||||||
|                 return glamourerApiVersion.InvokeFunc() >= 0; |                 return _glamourerApiVersion.InvokeFunc() >= 0; | ||||||
|             } |             } | ||||||
|             catch |             catch | ||||||
|             { |             { | ||||||
| @@ -95,91 +95,78 @@ namespace MareSynchronos.Managers | |||||||
|  |  | ||||||
|         private void RedrawSelf() |         private void RedrawSelf() | ||||||
|         { |         { | ||||||
|             penumbraRedraw!.InvokeAction("self", 0); |             _penumbraRedraw!.InvokeAction("self", 0); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private void Uninitialize() |         private void Uninitialize() | ||||||
|         { |         { | ||||||
|             penumbraInit.Unsubscribe(RedrawSelf); |             _penumbraInit.Unsubscribe(RedrawSelf); | ||||||
|             penumbraObjectIsRedrawn.Unsubscribe(RedrawEvent); |             _penumbraObjectIsRedrawn.Unsubscribe(RedrawEvent); | ||||||
|             penumbraResolvePath = null; |  | ||||||
|             penumbraResolveModDir = null; |  | ||||||
|             glamourerGetCharacterCustomization = null; |  | ||||||
|             glamourerApplyCharacterCustomization = null; |  | ||||||
|             penumbraReverseResolvePath = null; |  | ||||||
|             Initialized = false; |             Initialized = false; | ||||||
|             PluginLog.Debug("IPC Manager disposed"); |             PluginLog.Debug("IPC Manager disposed"); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public string[] PenumbraReverseResolvePath(string path, string characterName) |         public string[] PenumbraReverseResolvePath(string path, string characterName) | ||||||
|         { |         { | ||||||
|             if (!CheckPenumbraAPI()) return new[] { path }; |             if (!CheckPenumbraApi()) return new[] { path }; | ||||||
|             return penumbraReverseResolvePath!.InvokeFunc(path, characterName); |             return _penumbraReverseResolvePath!.InvokeFunc(path, characterName); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public string? PenumbraResolvePath(string path, string characterName) |         public string? PenumbraResolvePath(string path, string characterName) | ||||||
|         { |         { | ||||||
|             if (!CheckPenumbraAPI()) return null; |             if (!CheckPenumbraApi()) return null; | ||||||
|             return penumbraResolvePath!.InvokeFunc(path, characterName); |             return _penumbraResolvePath!.InvokeFunc(path, characterName); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public string? PenumbraModDirectory() |         public string? PenumbraModDirectory() | ||||||
|         { |         { | ||||||
|             if (!CheckPenumbraAPI()) return null; |             if (!CheckPenumbraApi()) return null; | ||||||
|             return penumbraResolveModDir!.InvokeFunc(); |             return _penumbraResolveModDir!.InvokeFunc(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public string? GlamourerGetCharacterCustomization() |         public string? GlamourerGetCharacterCustomization() | ||||||
|         { |         { | ||||||
|             if (!CheckGlamourerAPI()) return null; |             if (!CheckGlamourerApi()) return null; | ||||||
|             return glamourerGetCharacterCustomization!.InvokeFunc(); |             return _glamourerGetCharacterCustomization!.InvokeFunc(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public void GlamourerApplyCharacterCustomization(string customization, string characterName) |         public void GlamourerApplyCharacterCustomization(string customization, string characterName) | ||||||
|         { |         { | ||||||
|             if (!CheckGlamourerAPI()) return; |             if (!CheckGlamourerApi()) return; | ||||||
|             glamourerApplyCharacterCustomization!.InvokeAction(customization, characterName); |             _glamourerApplyCharacterCustomization!.InvokeAction(customization, characterName); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public void GlamourerRevertCharacterCustomization(string characterName) |         public void GlamourerRevertCharacterCustomization(string characterName) | ||||||
|         { |         { | ||||||
|             if (!CheckGlamourerAPI()) return; |             if (!CheckGlamourerApi()) return; | ||||||
|             glamourerRevertCustomization!.InvokeAction(characterName); |             _glamourerRevertCustomization!.InvokeAction(characterName); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public void PenumbraRedraw(string actorName) |         public void PenumbraRedraw(string actorName) | ||||||
|         { |         { | ||||||
|             if (!CheckPenumbraAPI()) return; |             if (!CheckPenumbraApi()) return; | ||||||
|             penumbraRedraw!.InvokeAction(actorName, 0); |             _penumbraRedraw!.InvokeAction(actorName, 0); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public void PenumbraCreateTemporaryCollection(string characterName) |         public void PenumbraCreateTemporaryCollection(string characterName) | ||||||
|         { |         { | ||||||
|             if (!CheckPenumbraAPI()) return; |             if (!CheckPenumbraApi()) return; | ||||||
|             PluginLog.Debug("Creating temp collection for " + characterName); |             PluginLog.Debug("Creating temp collection for " + characterName); | ||||||
|             //penumbraCreateTemporaryCollection.InvokeFunc("MareSynchronos", characterName, true); |             //penumbraCreateTemporaryCollection.InvokeFunc("MareSynchronos", characterName, true); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public void PenumbraRemoveTemporaryCollection(string characterName) |         public void PenumbraRemoveTemporaryCollection(string characterName) | ||||||
|         { |         { | ||||||
|             if (!CheckPenumbraAPI()) return; |             if (!CheckPenumbraApi()) return; | ||||||
|             PluginLog.Debug("Removing temp collection for " + characterName); |             PluginLog.Debug("Removing temp collection for " + characterName); | ||||||
|             //penumbraRemoveTemporaryCollection.InvokeFunc(characterName); |             //penumbraRemoveTemporaryCollection.InvokeFunc(characterName); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public void PenumbraSetTemporaryMods(string characterName, IReadOnlyDictionary<string, string> modPaths) |         public void PenumbraSetTemporaryMods(string characterName, IReadOnlyDictionary<string, string> modPaths) | ||||||
|         { |         { | ||||||
|             if (!CheckPenumbraAPI()) return; |             if (!CheckPenumbraApi()) return; | ||||||
|             PluginLog.Debug("Assigning temp mods for " + characterName); |             PluginLog.Debug("Assigning temp mods for " + characterName); | ||||||
|             try |             //penumbraSetTemporaryMod.InvokeFunc("MareSynchronos", characterName, modPaths, new List<string>(), 0); | ||||||
|             { |  | ||||||
|                 //var result = penumbraSetTemporaryMod.InvokeFunc("MareSynchronos", characterName, modPaths, new List<string>(), 0); |  | ||||||
|                 //PluginLog.Debug("Success: " + result); |  | ||||||
|             } |  | ||||||
|             catch (Exception ex) |  | ||||||
|             { |  | ||||||
|                 PluginLog.Error(ex, "Error during IPC call"); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public void Dispose() |         public void Dispose() | ||||||
|   | |||||||
| @@ -13,44 +13,61 @@ using Dalamud.Game.ClientState.Objects; | |||||||
| using Dalamud.Game.ClientState; | using Dalamud.Game.ClientState; | ||||||
| using System; | using System; | ||||||
| using MareSynchronos.Models; | using MareSynchronos.Models; | ||||||
| using Dalamud.Game.Gui; |  | ||||||
| using MareSynchronos.PenumbraMod; | using MareSynchronos.PenumbraMod; | ||||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||||
| using MareSynchronos.Managers; | using MareSynchronos.Managers; | ||||||
| using LZ4; | using LZ4; | ||||||
| using MareSynchronos.WebAPI; | using MareSynchronos.WebAPI; | ||||||
| using Dalamud.Interface.Windowing; | using Dalamud.Interface.Windowing; | ||||||
|  | using MareSynchronos.UI; | ||||||
|  |  | ||||||
| namespace MareSynchronos | namespace MareSynchronos | ||||||
| { | { | ||||||
|     public sealed class Plugin : IDalamudPlugin |     public sealed class Plugin : IDalamudPlugin | ||||||
|     { |     { | ||||||
|         private const string commandName = "/mare"; |         private const string CommandName = "/mare"; | ||||||
|         private readonly ClientState clientState; |         private readonly ApiController _apiController; | ||||||
|         private readonly Framework framework; |         private readonly ClientState _clientState; | ||||||
|         private readonly ObjectTable objectTable; |         private readonly CommandManager _commandManager; | ||||||
|         private readonly WindowSystem windowSystem; |         private readonly Configuration _configuration; | ||||||
|         private readonly ApiController apiController; |         private readonly FileCacheManager _fileCacheManager; | ||||||
|         private CharacterManager? characterManager; |         private readonly Framework _framework; | ||||||
|         private IpcManager ipcManager; |         private readonly IntroUI _introUi; | ||||||
|  |         private readonly IpcManager _ipcManager; | ||||||
|  |         private readonly ObjectTable _objectTable; | ||||||
|  |         private readonly DalamudPluginInterface _pluginInterface; | ||||||
|  |         private readonly PluginUi _pluginUi; | ||||||
|  |         private readonly WindowSystem _windowSystem; | ||||||
|  |         private CharacterManager? _characterManager; | ||||||
|         public Plugin(DalamudPluginInterface pluginInterface, CommandManager commandManager, |         public Plugin(DalamudPluginInterface pluginInterface, CommandManager commandManager, | ||||||
|             Framework framework, ObjectTable objectTable, ClientState clientState) |             Framework framework, ObjectTable objectTable, ClientState clientState) | ||||||
|         { |         { | ||||||
|             this.PluginInterface = pluginInterface; |             _pluginInterface = pluginInterface; | ||||||
|             this.CommandManager = commandManager; |             _commandManager = commandManager; | ||||||
|             this.framework = framework; |             _framework = framework; | ||||||
|             this.objectTable = objectTable; |             _objectTable = objectTable; | ||||||
|             this.clientState = clientState; |             _clientState = clientState; | ||||||
|             Configuration = this.PluginInterface.GetPluginConfig() as Configuration ?? new Configuration(); |             _configuration = _pluginInterface.GetPluginConfig() as Configuration ?? new Configuration(); | ||||||
|             Configuration.Initialize(this.PluginInterface); |             _configuration.Initialize(_pluginInterface); | ||||||
|  |  | ||||||
|             windowSystem = new WindowSystem("MareSynchronos"); |             _windowSystem = new WindowSystem("MareSynchronos"); | ||||||
|  |  | ||||||
|             apiController = new ApiController(Configuration); |             _apiController = new ApiController(_configuration); | ||||||
|             ipcManager = new IpcManager(PluginInterface); |             _ipcManager = new IpcManager(_pluginInterface); | ||||||
|  |             _fileCacheManager = new FileCacheManager(new FileCacheFactory(), _ipcManager, _configuration); | ||||||
|  |  | ||||||
|  |             var uiSharedComponent = | ||||||
|  |                 new UIShared(_ipcManager, _apiController, _fileCacheManager, _configuration); | ||||||
|  |  | ||||||
|             // you might normally want to embed resources and load them from the manifest stream |             // you might normally want to embed resources and load them from the manifest stream | ||||||
|             this.PluginUi = new PluginUI(this.Configuration, windowSystem, apiController, ipcManager); |             _pluginUi = new PluginUi(_windowSystem, uiSharedComponent, _configuration, _apiController); | ||||||
|  |             _introUi = new IntroUI(_windowSystem, uiSharedComponent, _configuration, _fileCacheManager); | ||||||
|  |             _introUi.FinishedRegistration += (_, _) => | ||||||
|  |             { | ||||||
|  |                 _pluginUi.IsOpen = true; | ||||||
|  |                 _introUi?.Dispose(); | ||||||
|  |                 ClientState_Login(null, EventArgs.Empty); | ||||||
|  |             }; | ||||||
|  |  | ||||||
|             new FileCacheContext().Dispose(); // make sure db is initialized I guess |             new FileCacheContext().Dispose(); // make sure db is initialized I guess | ||||||
|  |  | ||||||
| @@ -64,54 +81,64 @@ namespace MareSynchronos | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         public string Name => "Mare Synchronos"; |         public string Name => "Mare Synchronos"; | ||||||
|         private CommandManager CommandManager { get; init; } |  | ||||||
|         private Configuration Configuration { get; init; } |  | ||||||
|         private DalamudPluginInterface PluginInterface { get; init; } |  | ||||||
|         private PluginUI PluginUi { get; init; } |  | ||||||
|         public void Dispose() |         public void Dispose() | ||||||
|         { |         { | ||||||
|             this.PluginUi?.Dispose(); |             _commandManager.RemoveHandler(CommandName); | ||||||
|             this.CommandManager.RemoveHandler(commandName); |             _clientState.Login -= ClientState_Login; | ||||||
|             clientState.Login -= ClientState_Login; |             _clientState.Logout -= ClientState_Logout; | ||||||
|             clientState.Logout -= ClientState_Logout; |  | ||||||
|             ipcManager?.Dispose(); |             _pluginUi?.Dispose(); | ||||||
|             characterManager?.Dispose(); |             _introUi?.Dispose(); | ||||||
|             apiController?.Dispose(); |  | ||||||
|  |             _fileCacheManager?.Dispose(); | ||||||
|  |             _ipcManager?.Dispose(); | ||||||
|  |             _characterManager?.Dispose(); | ||||||
|  |             _apiController?.Dispose(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private void ClientState_Login(object? sender, EventArgs e) |         private void ClientState_Login(object? sender, EventArgs e) | ||||||
|         { |         { | ||||||
|             PluginLog.Debug("Client login"); |             PluginLog.Debug("Client login"); | ||||||
|  |  | ||||||
|  |             if (!_configuration.HasValidSetup) | ||||||
|  |             { | ||||||
|  |                 _introUi.IsOpen = true; | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 _introUi.IsOpen = false; | ||||||
|  |                 _pluginInterface.UiBuilder.Draw += Draw; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             Task.Run(async () => |             Task.Run(async () => | ||||||
|             { |             { | ||||||
|                 while (clientState.LocalPlayer == null) |                 while (_clientState.LocalPlayer == null) | ||||||
|                 { |                 { | ||||||
|                     await Task.Delay(50); |                     await Task.Delay(50); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 characterManager = new CharacterManager( |                 _characterManager = new CharacterManager( | ||||||
|                     clientState, framework, apiController, objectTable, ipcManager, new FileReplacementFactory(ipcManager), Configuration); |                     _clientState, _framework, _apiController, _objectTable, _ipcManager, new FileReplacementFactory(_ipcManager), _configuration); | ||||||
|                 characterManager.StartWatchingPlayer(); |                 _characterManager.StartWatchingPlayer(); | ||||||
|                 ipcManager.PenumbraRedraw(clientState.LocalPlayer!.Name.ToString()); |                 _ipcManager.PenumbraRedraw(_clientState.LocalPlayer!.Name.ToString()); | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             PluginInterface.UiBuilder.Draw += Draw; |             _pluginInterface.UiBuilder.OpenConfigUi += OpenConfigUi; | ||||||
|             PluginInterface.UiBuilder.OpenConfigUi += OpenConfigUI; |  | ||||||
|  |  | ||||||
|             CommandManager.AddHandler(commandName, new CommandInfo(OnCommand) |             _commandManager.AddHandler(CommandName, new CommandInfo(OnCommand) | ||||||
|             { |             { | ||||||
|                 HelpMessage = "pass 'scan' to initialize or rescan files into the database" |                 HelpMessage = "Opens the Mare Synchronos UI" | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private void ClientState_Logout(object? sender, EventArgs e) |         private void ClientState_Logout(object? sender, EventArgs e) | ||||||
|         { |         { | ||||||
|             PluginLog.Debug("Client logout"); |             PluginLog.Debug("Client logout"); | ||||||
|             characterManager?.Dispose(); |             _characterManager?.Dispose(); | ||||||
|             PluginInterface.UiBuilder.Draw -= Draw; |             _pluginInterface.UiBuilder.Draw -= Draw; | ||||||
|             PluginInterface.UiBuilder.OpenConfigUi -= OpenConfigUI; |             _pluginInterface.UiBuilder.OpenConfigUi -= OpenConfigUi; | ||||||
|             CommandManager.RemoveHandler(commandName); |             _commandManager.RemoveHandler(CommandName); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private void CopyFile(FileReplacement replacement, string targetDirectory, Dictionary<string, string>? resourceDict = null) |         private void CopyFile(FileReplacement replacement, string targetDirectory, Dictionary<string, string>? resourceDict = null) | ||||||
| @@ -126,18 +153,18 @@ namespace MareSynchronos | |||||||
|                 { |                 { | ||||||
|                     var ext = new FileInfo(fileCache.Filepath).Extension; |                     var ext = new FileInfo(fileCache.Filepath).Extension; | ||||||
|                     var newFilePath = Path.Combine(targetDirectory, "files", fileCache.Hash.ToLower() + ext); |                     var newFilePath = Path.Combine(targetDirectory, "files", fileCache.Hash.ToLower() + ext); | ||||||
|                     string lc4hcPath = Path.Combine(targetDirectory, "files", "lz4hc." + fileCache.Hash.ToLower() + ext); |                     string lc4HcPath = Path.Combine(targetDirectory, "files", "lz4hc." + fileCache.Hash.ToLower() + ext); | ||||||
|                     if (!File.Exists(lc4hcPath)) |                     if (!File.Exists(lc4HcPath)) | ||||||
|                     { |                     { | ||||||
|  |  | ||||||
|                         Stopwatch st = Stopwatch.StartNew(); |                         Stopwatch st = Stopwatch.StartNew(); | ||||||
|                         File.WriteAllBytes(lc4hcPath, LZ4Codec.WrapHC(File.ReadAllBytes(fileCache.Filepath), 0, (int)new FileInfo(fileCache.Filepath).Length)); |                         File.WriteAllBytes(lc4HcPath, LZ4Codec.WrapHC(File.ReadAllBytes(fileCache.Filepath), 0, (int)new FileInfo(fileCache.Filepath).Length)); | ||||||
|                         st.Stop(); |                         st.Stop(); | ||||||
|                         PluginLog.Debug("Compressed " + new FileInfo(fileCache.Filepath).Length + " bytes to " + new FileInfo(lc4hcPath).Length + " bytes in " + st.Elapsed); |                         PluginLog.Debug("Compressed " + new FileInfo(fileCache.Filepath).Length + " bytes to " + new FileInfo(lc4HcPath).Length + " bytes in " + st.Elapsed); | ||||||
|                         File.Copy(fileCache.Filepath, newFilePath); |                         File.Copy(fileCache.Filepath, newFilePath); | ||||||
|                         if (resourceDict != null) |                         if (resourceDict != null) | ||||||
|                         { |                         { | ||||||
|                             foreach(var path in replacement.GamePaths) |                             foreach (var path in replacement.GamePaths) | ||||||
|                             { |                             { | ||||||
|                                 resourceDict[path] = $"files\\{fileCache.Hash.ToLower() + ext}"; |                                 resourceDict[path] = $"files\\{fileCache.Hash.ToLower() + ext}"; | ||||||
|                             } |                             } | ||||||
| @@ -157,40 +184,35 @@ namespace MareSynchronos | |||||||
|  |  | ||||||
|         private void Draw() |         private void Draw() | ||||||
|         { |         { | ||||||
|             windowSystem.Draw(); |             _windowSystem.Draw(); | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private void OpenConfigUI() |  | ||||||
|         { |  | ||||||
|             this.PluginUi.Toggle(); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private void OnCommand(string command, string args) |         private void OnCommand(string command, string args) | ||||||
|         { |         { | ||||||
|             if (args == "printjson") |             if (args == "printjson") | ||||||
|             { |             { | ||||||
|                 _ = characterManager?.DebugJson(); |                 _ = _characterManager?.DebugJson(); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (args.StartsWith("watch")) |             if (args.StartsWith("watch")) | ||||||
|             { |             { | ||||||
|                 var playerName = args.Replace("watch", "").Trim(); |                 var playerName = args.Replace("watch", "").Trim(); | ||||||
|                 characterManager!.WatchPlayer(playerName); |                 _characterManager!.WatchPlayer(playerName); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (args.StartsWith("stop")) |             if (args.StartsWith("stop")) | ||||||
|             { |             { | ||||||
|                 var playerName = args.Replace("watch", "").Trim(); |                 var playerName = args.Replace("watch", "").Trim(); | ||||||
|                 characterManager!.StopWatchPlayer(playerName); |                 _characterManager!.StopWatchPlayer(playerName); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (args == "createtestmod") |             if (args == "createtestmod") | ||||||
|             { |             { | ||||||
|                 Task.Run(() => |                 Task.Run(() => | ||||||
|                 { |                 { | ||||||
|                     var playerName = clientState.LocalPlayer!.Name.ToString(); |                     var playerName = _clientState.LocalPlayer!.Name.ToString(); | ||||||
|                     var modName = $"Mare Synchronos Test Mod {playerName}"; |                     var modName = $"Mare Synchronos Test Mod {playerName}"; | ||||||
|                     var modDirectory = ipcManager!.PenumbraModDirectory()!; |                     var modDirectory = _ipcManager!.PenumbraModDirectory()!; | ||||||
|                     string modDirectoryPath = Path.Combine(modDirectory, modName); |                     string modDirectoryPath = Path.Combine(modDirectory, modName); | ||||||
|                     if (Directory.Exists(modDirectoryPath)) |                     if (Directory.Exists(modDirectoryPath)) | ||||||
|                     { |                     { | ||||||
| @@ -206,7 +228,7 @@ namespace MareSynchronos | |||||||
|                         Description = "Mare Synchronous Test Mod Export", |                         Description = "Mare Synchronous Test Mod Export", | ||||||
|                     }; |                     }; | ||||||
|  |  | ||||||
|                     var resources = characterManager!.BuildCharacterCache(); |                     var resources = _characterManager!.BuildCharacterCache(); | ||||||
|                     var metaJson = JsonConvert.SerializeObject(meta); |                     var metaJson = JsonConvert.SerializeObject(meta); | ||||||
|                     File.WriteAllText(Path.Combine(modDirectoryPath, "meta.json"), metaJson); |                     File.WriteAllText(Path.Combine(modDirectoryPath, "meta.json"), metaJson); | ||||||
|  |  | ||||||
| @@ -229,8 +251,13 @@ namespace MareSynchronos | |||||||
|  |  | ||||||
|             if (string.IsNullOrEmpty(args)) |             if (string.IsNullOrEmpty(args)) | ||||||
|             { |             { | ||||||
|                 PluginUi.Toggle(); |                 _pluginUi.Toggle(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         private void OpenConfigUi() | ||||||
|  |         { | ||||||
|  |             _pluginUi.Toggle(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,348 +0,0 @@ | |||||||
| using Dalamud.Interface; |  | ||||||
| using Dalamud.Interface.Colors; |  | ||||||
| using Dalamud.Interface.Windowing; |  | ||||||
| using Dalamud.Logging; |  | ||||||
| using ImGuiNET; |  | ||||||
| using MareSynchronos.Managers; |  | ||||||
| using MareSynchronos.WebAPI; |  | ||||||
| using System; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Numerics; |  | ||||||
| using Dalamud.Configuration; |  | ||||||
| using MareSynchronos.API; |  | ||||||
|  |  | ||||||
| namespace MareSynchronos |  | ||||||
| { |  | ||||||
|     // It is good to have this be disposable in general, in case you ever need it |  | ||||||
|     // to do any cleanup |  | ||||||
|     class PluginUI : Window, IDisposable |  | ||||||
|     { |  | ||||||
|         private Configuration configuration; |  | ||||||
|         private readonly WindowSystem windowSystem; |  | ||||||
|         private readonly ApiController apiController; |  | ||||||
|         private readonly IpcManager ipcManager; |  | ||||||
|         private string? uid; |  | ||||||
|         private const string mainServer = "Lunae Crescere Incipientis (Central Server EU)"; |  | ||||||
|  |  | ||||||
|         // passing in the image here just for simplicity |  | ||||||
|         public PluginUI(Configuration configuration, WindowSystem windowSystem, ApiController apiController, IpcManager ipcManager) : base("Mare Synchronos Settings", ImGuiWindowFlags.None) |  | ||||||
|         { |  | ||||||
|             SizeConstraints = new WindowSizeConstraints() |  | ||||||
|             { |  | ||||||
|                 MinimumSize = new(700, 400), |  | ||||||
|                 MaximumSize = new(700, 2000) |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             this.configuration = configuration; |  | ||||||
|             this.windowSystem = windowSystem; |  | ||||||
|             this.apiController = apiController; |  | ||||||
|             this.ipcManager = ipcManager; |  | ||||||
|             windowSystem.AddWindow(this); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public void Dispose() |  | ||||||
|         { |  | ||||||
|             windowSystem.RemoveWindow(this); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public override void Draw() |  | ||||||
|         { |  | ||||||
|             if (!IsOpen) |  | ||||||
|             { |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|             if (apiController.SecretKey != "-" && !apiController.IsConnected && apiController.ServerAlive) |  | ||||||
|             { |  | ||||||
|                 if (ImGui.Button("Reset Secret Key")) |  | ||||||
|                 { |  | ||||||
|                     configuration.ClientSecret.Clear(); |  | ||||||
|                     configuration.Save(); |  | ||||||
|                     apiController.RestartHeartbeat(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else if (apiController.SecretKey == "-") |  | ||||||
|             { |  | ||||||
|                 DrawIntroContent(); |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 if (!OtherPluginStateOk()) return; |  | ||||||
|  |  | ||||||
|                 DrawSettingsContent(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private void DrawSettingsContent() |  | ||||||
|         { |  | ||||||
|             PrintServerState(); |  | ||||||
|             ImGui.Separator(); |  | ||||||
|             ImGui.Text("Your UID"); |  | ||||||
|             ImGui.SameLine(); |  | ||||||
|             if (apiController.ServerAlive) |  | ||||||
|             { |  | ||||||
|                 ImGui.TextColored(ImGuiColors.ParsedGreen, apiController.UID); |  | ||||||
|                 ImGui.SameLine(); |  | ||||||
|                 if (ImGui.Button("Copy UID")) |  | ||||||
|                 { |  | ||||||
|                     ImGui.SetClipboardText(apiController.UID); |  | ||||||
|                 } |  | ||||||
|                 ImGui.Text("Share this UID to other Mare users so they can add you to their whitelist."); |  | ||||||
|                 ImGui.Separator(); |  | ||||||
|                 DrawWhiteListContent(); |  | ||||||
|                 ImGui.Separator(); |  | ||||||
|                 string cachePath = configuration.CacheFolder; |  | ||||||
|                 if (ImGui.InputText("CachePath", ref cachePath, 255)) |  | ||||||
|                 { |  | ||||||
|                     configuration.CacheFolder = cachePath; |  | ||||||
|                     configuration.Save(); |  | ||||||
|                 } |  | ||||||
|                 ImGui.Separator(); |  | ||||||
|                 ImGui.Text("Pending uploads"); |  | ||||||
|                 ImGui.Text($"Total: {ByteToKiB(apiController.CurrentUploads.Sum(a => a.Value.Item1))} / {ByteToKiB(apiController.CurrentUploads.Sum(a => a.Value.Item2))}"); |  | ||||||
|                 if (ImGui.BeginTable("Uploads", 3)) |  | ||||||
|                 { |  | ||||||
|                     ImGui.TableSetupColumn("File"); |  | ||||||
|                     ImGui.TableSetupColumn("Uploaded"); |  | ||||||
|                     ImGui.TableSetupColumn("Size"); |  | ||||||
|                     ImGui.TableHeadersRow(); |  | ||||||
|                     foreach (var hash in apiController.CurrentUploads.Keys) |  | ||||||
|                     { |  | ||||||
|                         var color = UploadColor(apiController.CurrentUploads[hash]); |  | ||||||
|                         ImGui.PushStyleColor(ImGuiCol.Text, color); |  | ||||||
|                         ImGui.TableNextColumn(); |  | ||||||
|                         ImGui.Text(hash); |  | ||||||
|                         ImGui.TableNextColumn(); |  | ||||||
|                         ImGui.Text(ByteToKiB(apiController.CurrentUploads[hash].Item1)); |  | ||||||
|                         ImGui.TableNextColumn(); |  | ||||||
|                         ImGui.Text(ByteToKiB(apiController.CurrentUploads[hash].Item2)); |  | ||||||
|                         ImGui.PopStyleColor(); |  | ||||||
|                         ImGui.TableNextRow(); |  | ||||||
|                     } |  | ||||||
|                     ImGui.EndTable(); |  | ||||||
|                 } |  | ||||||
|                 ImGui.Separator(); |  | ||||||
|                 ImGui.Text("Pending Downloads"); |  | ||||||
|                 ImGui.Text($"Total: {ByteToKiB(apiController.CurrentDownloads.Sum(a => a.Value.Item1))} / {ByteToKiB(apiController.CurrentDownloads.Sum(a => a.Value.Item2))}"); |  | ||||||
|                 if (ImGui.BeginTable("Downloads", 3)) |  | ||||||
|                 { |  | ||||||
|                     ImGui.TableSetupColumn("File"); |  | ||||||
|                     ImGui.TableSetupColumn("Downloaded"); |  | ||||||
|                     ImGui.TableSetupColumn("Size"); |  | ||||||
|                     ImGui.TableHeadersRow(); |  | ||||||
|                     foreach (var hash in apiController.CurrentDownloads.Keys) |  | ||||||
|                     { |  | ||||||
|                         var color = UploadColor(apiController.CurrentDownloads[hash]); |  | ||||||
|                         ImGui.PushStyleColor(ImGuiCol.Text, color); |  | ||||||
|                         ImGui.TableNextColumn(); |  | ||||||
|                         ImGui.Text(hash); |  | ||||||
|                         ImGui.TableNextColumn(); |  | ||||||
|                         ImGui.Text(ByteToKiB(apiController.CurrentDownloads[hash].Item1)); |  | ||||||
|                         ImGui.TableNextColumn(); |  | ||||||
|                         ImGui.Text(ByteToKiB(apiController.CurrentDownloads[hash].Item2)); |  | ||||||
|                         ImGui.PopStyleColor(); |  | ||||||
|                         ImGui.TableNextRow(); |  | ||||||
|                     } |  | ||||||
|                     ImGui.EndTable(); |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 ImGui.TextColored(ImGuiColors.DalamudRed, "Service unavailable"); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private Vector4 UploadColor((long, long) data) => data.Item1 == 0 ? ImGuiColors.DalamudGrey : |  | ||||||
|             data.Item1 == data.Item2 ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudYellow; |  | ||||||
|  |  | ||||||
|         private string ByteToKiB(long bytes) => (bytes / 1024.0).ToString("0.00") + " KiB"; |  | ||||||
|  |  | ||||||
|         private void DrawWhiteListContent() |  | ||||||
|         { |  | ||||||
|             if (!apiController.ServerAlive) return; |  | ||||||
|             ImGui.Text("Whitelists"); |  | ||||||
|             if (ImGui.BeginTable("WhitelistTable", 5)) |  | ||||||
|             { |  | ||||||
|                 ImGui.TableSetupColumn("Pause"); |  | ||||||
|                 ImGui.TableSetupColumn("UID"); |  | ||||||
|                 ImGui.TableSetupColumn("Sync"); |  | ||||||
|                 ImGui.TableSetupColumn("Paused (you/other)"); |  | ||||||
|                 ImGui.TableSetupColumn(""); |  | ||||||
|                 ImGui.TableHeadersRow(); |  | ||||||
|                 foreach (var item in apiController.WhitelistEntries.ToList()) |  | ||||||
|                 { |  | ||||||
|                     ImGui.TableNextColumn(); |  | ||||||
|                     bool isPaused = item.IsPaused; |  | ||||||
|                     if (ImGui.Checkbox("Paused##" + item.OtherUID, ref isPaused)) |  | ||||||
|                     { |  | ||||||
|                         _ = apiController.SendWhitelistPauseChange(item.OtherUID, isPaused); |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     ImGui.TableNextColumn(); |  | ||||||
|                     ImGui.TextColored(GetBoolColor(item.IsSynced && !item.IsPausedFromOthers && !item.IsPaused), |  | ||||||
|                         item.OtherUID); |  | ||||||
|                     ImGui.TableNextColumn(); |  | ||||||
|                     ImGui.TextColored(GetBoolColor(item.IsSynced), !item.IsSynced ? "Has not added you" : "On both whitelists"); |  | ||||||
|                     ImGui.TableNextColumn(); |  | ||||||
|                     ImGui.TextColored(GetBoolColor((!item.IsPausedFromOthers && !item.IsPaused)), item.IsPaused + " / " + item.IsPausedFromOthers); |  | ||||||
|                     ImGui.TableNextColumn(); |  | ||||||
|                     if (ImGui.Button("Delete##" + item.OtherUID)) |  | ||||||
|                     { |  | ||||||
|                         _ = apiController.SendWhitelistRemoval(item.OtherUID); |  | ||||||
|                         apiController.WhitelistEntries.Remove(item); |  | ||||||
|                     } |  | ||||||
|                     ImGui.TableNextRow(); |  | ||||||
|                 } |  | ||||||
|                 ImGui.EndTable(); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             var whitelistEntry = tempDto.OtherUID; |  | ||||||
|             if (ImGui.InputText("Add new whitelist entry", ref whitelistEntry, 20)) |  | ||||||
|             { |  | ||||||
|                 tempDto.OtherUID = whitelistEntry; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             ImGui.SameLine(); |  | ||||||
|             if (ImGui.Button("Add")) |  | ||||||
|             { |  | ||||||
|                 if (apiController.WhitelistEntries.All(w => w.OtherUID != tempDto.OtherUID)) |  | ||||||
|                 { |  | ||||||
|                     apiController.WhitelistEntries.Add(new WhitelistDto() |  | ||||||
|                     { |  | ||||||
|                         OtherUID = tempDto.OtherUID |  | ||||||
|                     }); |  | ||||||
|  |  | ||||||
|                     _ = apiController.SendWhitelistAddition(tempDto.OtherUID); |  | ||||||
|  |  | ||||||
|                     tempDto.OtherUID = string.Empty; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private WhitelistDto tempDto = new WhitelistDto() { OtherUID = string.Empty }; |  | ||||||
|  |  | ||||||
|         private int serverSelectionIndex = 0; |  | ||||||
|  |  | ||||||
|         private async void DrawIntroContent() |  | ||||||
|         { |  | ||||||
|             ImGui.SetWindowFontScale(1.3f); |  | ||||||
|             ImGui.Text("Welcome to Mare Synchronos!"); |  | ||||||
|             ImGui.SetWindowFontScale(1.0f); |  | ||||||
|             ImGui.Separator(); |  | ||||||
|             ImGui.TextWrapped("Mare Synchronos is a plugin that will replicate your full current character state including all Penumbra mods to other whitelisted Mare Synchronos users. " + |  | ||||||
|                 "Note that you will have to have Penumbra as well as Glamourer installed to use this plugin."); |  | ||||||
|             if (!OtherPluginStateOk()) return; |  | ||||||
|  |  | ||||||
|             ImGui.SetWindowFontScale(1.5f); |  | ||||||
|             string readThis = "READ THIS CAREFULLY BEFORE REGISTERING"; |  | ||||||
|             var textSize = ImGui.CalcTextSize(readThis); |  | ||||||
|             ImGui.SetCursorPosX(ImGui.GetWindowSize().X / 2 - textSize.X / 2); |  | ||||||
|             ImGui.TextColored(ImGuiColors.DalamudRed, readThis); |  | ||||||
|             ImGui.SetWindowFontScale(1.0f); |  | ||||||
|  |  | ||||||
|             ImGui.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."); |  | ||||||
|             ImGui.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. " + |  | ||||||
|                 "Files present on the service that already represent your active mod files will not be uploaded again. To register at a service you will need to hold ctrl."); |  | ||||||
|             ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); |  | ||||||
|             ImGui.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 whitelist 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 in question."); |  | ||||||
|             ImGui.PopStyleColor(); |  | ||||||
|             ImGui.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. " + |  | ||||||
|                 "You will also be able to wipe all the files you have personally uploaded on request."); |  | ||||||
|             ImGui.TextColored(ImGuiColors.DalamudRed, "This service is provided as-is."); |  | ||||||
|             ImGui.Separator(); |  | ||||||
|             string[] comboEntries = new[] { mainServer, "Custom Service" }; |  | ||||||
|             if (ImGui.BeginCombo("Service", comboEntries[serverSelectionIndex])) |  | ||||||
|             { |  | ||||||
|                 for (int n = 0; n < comboEntries.Length; n++) |  | ||||||
|                 { |  | ||||||
|                     bool isSelected = serverSelectionIndex == n; |  | ||||||
|                     if (ImGui.Selectable(comboEntries[n], isSelected)) |  | ||||||
|                     { |  | ||||||
|                         serverSelectionIndex = n; |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     if (isSelected) |  | ||||||
|                     { |  | ||||||
|                         ImGui.SetItemDefaultFocus(); |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     bool useCustomService = (serverSelectionIndex != 0); |  | ||||||
|  |  | ||||||
|                     if (apiController.UseCustomService != useCustomService) |  | ||||||
|                     { |  | ||||||
|                         PluginLog.Debug("Configuration " + apiController.UseCustomService + " changing to " + useCustomService); |  | ||||||
|                         apiController.UseCustomService = useCustomService; |  | ||||||
|                         configuration.Save(); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 ImGui.EndCombo(); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (apiController.UseCustomService) |  | ||||||
|             { |  | ||||||
|                 string serviceAddress = configuration.ApiUri; |  | ||||||
|                 if (ImGui.InputText("Service address", ref serviceAddress, 255)) |  | ||||||
|                 { |  | ||||||
|                     if (configuration.ApiUri != serviceAddress) |  | ||||||
|                     { |  | ||||||
|                         configuration.ApiUri = serviceAddress; |  | ||||||
|                         apiController.RestartHeartbeat(); |  | ||||||
|                         configuration.Save(); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             PrintServerState(); |  | ||||||
|             if (apiController.ServerAlive) |  | ||||||
|             { |  | ||||||
|                 if (ImGui.Button("Register")) |  | ||||||
|                 { |  | ||||||
|                     if (ImGui.GetIO().KeyCtrl) |  | ||||||
|                     { |  | ||||||
|                         await apiController.Register(); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private Vector4 GetBoolColor(bool input) => input ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; |  | ||||||
|  |  | ||||||
|         private bool OtherPluginStateOk() |  | ||||||
|         { |  | ||||||
|             var penumbraExists = ipcManager.CheckPenumbraAPI(); |  | ||||||
|             var glamourerExists = ipcManager.CheckGlamourerAPI(); |  | ||||||
|  |  | ||||||
|             var penumbraColor = penumbraExists ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; |  | ||||||
|             var glamourerColor = glamourerExists ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; |  | ||||||
|             ImGui.Text("Penumbra:"); |  | ||||||
|             ImGui.SameLine(); |  | ||||||
|             ImGui.TextColored(penumbraColor, penumbraExists ? "Available" : "Unavailable"); |  | ||||||
|             ImGui.Text("Glamourer:"); |  | ||||||
|             ImGui.SameLine(); |  | ||||||
|             ImGui.TextColored(glamourerColor, glamourerExists ? "Available" : "Unavailable"); |  | ||||||
|  |  | ||||||
|             if (!penumbraExists || !glamourerExists) |  | ||||||
|             { |  | ||||||
|                 ImGui.TextColored(ImGuiColors.DalamudRed, "You need to install both Penumbra and Glamourer and keep them up to date to use Mare Synchronos."); |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private void PrintServerState() |  | ||||||
|         { |  | ||||||
|             ImGui.Text("Service status of " + (string.IsNullOrEmpty(configuration.ApiUri) ? mainServer : configuration.ApiUri)); |  | ||||||
|             ImGui.SameLine(); |  | ||||||
|             var color = apiController.ServerAlive ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; |  | ||||||
|             ImGui.TextColored(color, apiController.ServerAlive ? "Available" : "Unavailable"); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										193
									
								
								MareSynchronos/UI/IntroUI.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								MareSynchronos/UI/IntroUI.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,193 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.IO; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Text; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  | using Dalamud.Interface.Colors; | ||||||
|  | using Dalamud.Interface.Windowing; | ||||||
|  | using ImGuiNET; | ||||||
|  | using MareSynchronos.FileCacheDB; | ||||||
|  | using MareSynchronos.Managers; | ||||||
|  | using MareSynchronos.WebAPI; | ||||||
|  |  | ||||||
|  | namespace MareSynchronos.UI | ||||||
|  | { | ||||||
|  |     internal class IntroUI : Window, IDisposable | ||||||
|  |     { | ||||||
|  |         private readonly UIShared _uiShared; | ||||||
|  |         private readonly Configuration _pluginConfiguration; | ||||||
|  |         private readonly FileCacheManager _fileCacheManager; | ||||||
|  |         private readonly WindowSystem _windowSystem; | ||||||
|  |         private bool _readFirstPage = false; | ||||||
|  |  | ||||||
|  |         public event EventHandler? FinishedRegistration; | ||||||
|  |  | ||||||
|  |         public void Dispose() | ||||||
|  |         { | ||||||
|  |             _windowSystem.RemoveWindow(this); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public IntroUI(WindowSystem windowSystem, UIShared uiShared, Configuration pluginConfiguration, | ||||||
|  |             FileCacheManager fileCacheManager) : base("Mare Synchronos Setup") | ||||||
|  |         { | ||||||
|  |             _uiShared = uiShared; | ||||||
|  |             _pluginConfiguration = pluginConfiguration; | ||||||
|  |             _fileCacheManager = fileCacheManager; | ||||||
|  |             _windowSystem = windowSystem; | ||||||
|  |  | ||||||
|  |             SizeConstraints = new WindowSizeConstraints() | ||||||
|  |             { | ||||||
|  |                 MinimumSize = new(600, 400), | ||||||
|  |                 MaximumSize = new(600, 2000) | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             _windowSystem.AddWindow(this); | ||||||
|  |             IsOpen = true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public override void Draw() | ||||||
|  |         { | ||||||
|  |             if (!IsOpen) | ||||||
|  |             { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (!_pluginConfiguration.AcceptedAgreement && !_readFirstPage) | ||||||
|  |             { | ||||||
|  |                 ImGui.SetWindowFontScale(1.3f); | ||||||
|  |                 ImGui.Text("Welcome to Mare Synchronos!"); | ||||||
|  |                 ImGui.SetWindowFontScale(1.0f); | ||||||
|  |                 ImGui.Separator(); | ||||||
|  |                 UIShared.TextWrapped("Mare Synchronos is a plugin that will replicate your full current character state including all Penumbra mods to other whitelisted Mare Synchronos users. " + | ||||||
|  |                                   "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."); | ||||||
|  |  | ||||||
|  |                 if (!_uiShared.DrawOtherPluginState()) return; | ||||||
|  |                 ImGui.Separator(); | ||||||
|  |                 if (ImGui.Button("Next##toAgreement")) | ||||||
|  |                 { | ||||||
|  |                     _readFirstPage = true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else if (!_pluginConfiguration.AcceptedAgreement && _readFirstPage) | ||||||
|  |             { | ||||||
|  |                 ImGui.SetWindowFontScale(1.3f); | ||||||
|  |                 ImGui.Text("Agreement of Usage of Service"); | ||||||
|  |                 ImGui.SetWindowFontScale(1.0f); | ||||||
|  |                 ImGui.Separator(); | ||||||
|  |                 ImGui.SetWindowFontScale(1.5f); | ||||||
|  |                 string readThis = "READ THIS CAREFULLY"; | ||||||
|  |                 var textSize = ImGui.CalcTextSize(readThis); | ||||||
|  |                 ImGui.SetCursorPosX(ImGui.GetWindowSize().X / 2 - textSize.X / 2); | ||||||
|  |                 ImGui.TextColored(ImGuiColors.DalamudRed, readThis); | ||||||
|  |                 ImGui.SetWindowFontScale(1.0f); | ||||||
|  |                 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. " + | ||||||
|  |                     "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. " + | ||||||
|  |                     "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."); | ||||||
|  |                 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. " + | ||||||
|  |                     "Please think about who you are going to whitelist 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."); | ||||||
|  |                 ImGui.PopStyleColor(); | ||||||
|  |                 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 add everyone to your whitelist."); | ||||||
|  |                 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. " + | ||||||
|  |                                   "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. " + | ||||||
|  |                                   "The service holds no information about which mod files belong to which mod."); | ||||||
|  |                 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. " + | ||||||
|  |                                                           "To accept those conditions hold CTRL while clicking 'I agree'"); | ||||||
|  |                 ImGui.PopStyleColor(); | ||||||
|  |                 ImGui.Separator(); | ||||||
|  |  | ||||||
|  |                 if (ImGui.Button("I agree##toSetup")) | ||||||
|  |                 { | ||||||
|  |                     if (ImGui.GetIO().KeyCtrl) | ||||||
|  |                     { | ||||||
|  |                         _pluginConfiguration.AcceptedAgreement = true; | ||||||
|  |                         _pluginConfiguration.Save(); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else if (_pluginConfiguration.AcceptedAgreement && (string.IsNullOrEmpty(_pluginConfiguration.CacheFolder) || _pluginConfiguration.InitialScanComplete == false)) | ||||||
|  |             { | ||||||
|  |                 ImGui.SetWindowFontScale(1.3f); | ||||||
|  |                 ImGui.Text("File Cache Setup"); | ||||||
|  |                 ImGui.SetWindowFontScale(1.0f); | ||||||
|  |                 ImGui.Separator(); | ||||||
|  |                 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. " + | ||||||
|  |                                   "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."); | ||||||
|  |                 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. " + | ||||||
|  |                                   "Otherwise on the next launch a full re-scan of the file cache database will be initiated."); | ||||||
|  |                 ImGui.PopStyleColor(); | ||||||
|  |                 _uiShared.DrawCacheDirectorySetting(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |                 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. " + | ||||||
|  |                                       "More is not necessarily better, the default of 10 should be fine for most cases."); | ||||||
|  |                     _uiShared.DrawParallelScansSetting(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |                     if (ImGui.Button("Start Scan##startScan")) | ||||||
|  |                     { | ||||||
|  |                         _fileCacheManager.StartInitialScan(); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     _uiShared.DrawFileScanState(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 ImGui.SetWindowFontScale(1.3f); | ||||||
|  |                 ImGui.Text("Service registration"); | ||||||
|  |                 ImGui.SetWindowFontScale(1.0f); | ||||||
|  |                 ImGui.Separator(); | ||||||
|  |                 if (_pluginConfiguration.ClientSecret.ContainsKey(_pluginConfiguration.ApiUri)) | ||||||
|  |                 { | ||||||
|  |                     ImGui.Separator(); | ||||||
|  |                     UIShared.TextWrapped(_pluginConfiguration.ClientSecret[_pluginConfiguration.ApiUri]); | ||||||
|  |                     ImGui.Separator(); | ||||||
|  |                     ImGui.Button("Copy secret key to clipboard"); | ||||||
|  |                     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."); | ||||||
|  |                     ImGui.PopStyleColor(); | ||||||
|  |                     ImGui.Separator(); | ||||||
|  |                     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."); | ||||||
|  |                     ImGui.PopStyleColor(); | ||||||
|  |                     ImGui.Separator(); | ||||||
|  |                     if (ImGui.Button("Finish##finishIntro")) | ||||||
|  |                     { | ||||||
|  |                         FinishedRegistration?.Invoke(null, EventArgs.Empty); | ||||||
|  |                         IsOpen = false; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     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."); | ||||||
|  |                     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. " + | ||||||
|  |                                          "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."); | ||||||
|  |                     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(); | ||||||
|  |                     _uiShared.DrawServiceSelection(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										263
									
								
								MareSynchronos/UI/PluginUI.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								MareSynchronos/UI/PluginUI.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,263 @@ | |||||||
|  | using Dalamud.Interface; | ||||||
|  | using Dalamud.Interface.Colors; | ||||||
|  | using Dalamud.Interface.Windowing; | ||||||
|  | using Dalamud.Logging; | ||||||
|  | using ImGuiNET; | ||||||
|  | using MareSynchronos.WebAPI; | ||||||
|  | using System; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Numerics; | ||||||
|  | using Dalamud.Configuration; | ||||||
|  | using MareSynchronos.API; | ||||||
|  |  | ||||||
|  | namespace MareSynchronos.UI | ||||||
|  | { | ||||||
|  |     class PluginUi : Window, IDisposable | ||||||
|  |     { | ||||||
|  |         private readonly Configuration _configuration; | ||||||
|  |         private readonly WindowSystem _windowSystem; | ||||||
|  |         private readonly ApiController _apiController; | ||||||
|  |         private readonly UIShared _uiShared; | ||||||
|  |  | ||||||
|  |         public PluginUi(WindowSystem windowSystem, | ||||||
|  |             UIShared uiShared, Configuration configuration, ApiController apiController) : base("Mare Synchronos Settings", ImGuiWindowFlags.None) | ||||||
|  |         { | ||||||
|  |             SizeConstraints = new WindowSizeConstraints() | ||||||
|  |             { | ||||||
|  |                 MinimumSize = new(1000, 400), | ||||||
|  |                 MaximumSize = new(1000, 2000), | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             this._configuration = configuration; | ||||||
|  |             this._windowSystem = windowSystem; | ||||||
|  |             this._apiController = apiController; | ||||||
|  |             _uiShared = uiShared; | ||||||
|  |             windowSystem.AddWindow(this); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void Dispose() | ||||||
|  |         { | ||||||
|  |             _windowSystem.RemoveWindow(this); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public override void Draw() | ||||||
|  |         { | ||||||
|  |             if (!IsOpen) | ||||||
|  |             { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (_apiController.SecretKey != "-" && !_apiController.IsConnected && _apiController.ServerAlive) | ||||||
|  |             { | ||||||
|  |                 if (ImGui.Button("Reset Secret Key")) | ||||||
|  |                 { | ||||||
|  |                     _configuration.ClientSecret.Clear(); | ||||||
|  |                     _configuration.Save(); | ||||||
|  |                     _apiController.RestartHeartbeat(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 if (!_uiShared.DrawOtherPluginState()) return; | ||||||
|  |  | ||||||
|  |                 DrawSettingsContent(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void DrawSettingsContent() | ||||||
|  |         { | ||||||
|  |             _uiShared.PrintServerState(); | ||||||
|  |             ImGui.Separator(); | ||||||
|  |             ImGui.SetWindowFontScale(1.2f); | ||||||
|  |             ImGui.Text("Your UID"); | ||||||
|  |             ImGui.SameLine(); | ||||||
|  |             if (_apiController.ServerAlive) | ||||||
|  |             { | ||||||
|  |                 ImGui.TextColored(ImGuiColors.ParsedGreen, _apiController.UID); | ||||||
|  |                 ImGui.SameLine(); | ||||||
|  |                 ImGui.SetWindowFontScale(1.0f); | ||||||
|  |                 if (ImGui.Button("Copy UID")) | ||||||
|  |                 { | ||||||
|  |                     ImGui.SetClipboardText(_apiController.UID); | ||||||
|  |                 } | ||||||
|  |                 ImGui.Text("Share this UID to other Mare users so they can add you to their whitelist."); | ||||||
|  |                 ImGui.Separator(); | ||||||
|  |                 DrawWhiteListContent(); | ||||||
|  |                 DrawFileCacheSettings(); | ||||||
|  |                 DrawCurrentTransfers(); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 ImGui.TextColored(ImGuiColors.DalamudRed, "No UID (Service unavailable)"); | ||||||
|  |                 ImGui.SetWindowFontScale(1.0f); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void DrawCurrentTransfers() | ||||||
|  |         { | ||||||
|  |             if (ImGui.TreeNode( | ||||||
|  |                     $"Current Transfers")) | ||||||
|  |             { | ||||||
|  |                 if (ImGui.BeginTable("TransfersTable", 2)) | ||||||
|  |                 { | ||||||
|  |                     ImGui.TableSetupColumn( | ||||||
|  |                         $"Uploads ({UIShared.ByteToKiB(_apiController.CurrentUploads.Sum(a => a.Value.Item1))} / {UIShared.ByteToKiB(_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.TableHeadersRow(); | ||||||
|  |  | ||||||
|  |                     ImGui.TableNextColumn(); | ||||||
|  |                     if (ImGui.BeginTable("UploadsTable", 3)) | ||||||
|  |                     { | ||||||
|  |                         ImGui.TableSetupColumn("File"); | ||||||
|  |                         ImGui.TableSetupColumn("Uploaded"); | ||||||
|  |                         ImGui.TableSetupColumn("Size"); | ||||||
|  |                         ImGui.TableHeadersRow(); | ||||||
|  |                         foreach (var hash in _apiController.CurrentUploads.Keys) | ||||||
|  |                         { | ||||||
|  |                             var color = UIShared.UploadColor(_apiController.CurrentUploads[hash]); | ||||||
|  |                             ImGui.PushStyleColor(ImGuiCol.Text, color); | ||||||
|  |                             ImGui.TableNextColumn(); | ||||||
|  |                             ImGui.Text(hash); | ||||||
|  |                             ImGui.TableNextColumn(); | ||||||
|  |                             ImGui.Text(UIShared.ByteToKiB(_apiController.CurrentUploads[hash].Item1)); | ||||||
|  |                             ImGui.TableNextColumn(); | ||||||
|  |                             ImGui.Text(UIShared.ByteToKiB(_apiController.CurrentUploads[hash].Item2)); | ||||||
|  |                             ImGui.PopStyleColor(); | ||||||
|  |                             ImGui.TableNextRow(); | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                         ImGui.EndTable(); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     ImGui.TableNextColumn(); | ||||||
|  |                     if (ImGui.BeginTable("DownloadsTable", 3)) | ||||||
|  |                     { | ||||||
|  |                         ImGui.TableSetupColumn("File"); | ||||||
|  |                         ImGui.TableSetupColumn("Downloaded"); | ||||||
|  |                         ImGui.TableSetupColumn("Size"); | ||||||
|  |                         ImGui.TableHeadersRow(); | ||||||
|  |                         foreach (var hash in _apiController.CurrentDownloads.Keys) | ||||||
|  |                         { | ||||||
|  |                             var color = UIShared.UploadColor(_apiController.CurrentDownloads[hash]); | ||||||
|  |                             ImGui.PushStyleColor(ImGuiCol.Text, color); | ||||||
|  |                             ImGui.TableNextColumn(); | ||||||
|  |                             ImGui.Text(hash); | ||||||
|  |                             ImGui.TableNextColumn(); | ||||||
|  |                             ImGui.Text(UIShared.ByteToKiB(_apiController.CurrentDownloads[hash].Item1)); | ||||||
|  |                             ImGui.TableNextColumn(); | ||||||
|  |                             ImGui.Text(UIShared.ByteToKiB(_apiController.CurrentDownloads[hash].Item2)); | ||||||
|  |                             ImGui.PopStyleColor(); | ||||||
|  |                             ImGui.TableNextRow(); | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                         ImGui.EndTable(); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     ImGui.EndTable(); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 ImGui.TreePop(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void DrawFileCacheSettings() | ||||||
|  |         { | ||||||
|  |             if (ImGui.TreeNode("File Cache Settings")) | ||||||
|  |             { | ||||||
|  |                 _uiShared.DrawFileScanState(); | ||||||
|  |                 _uiShared.DrawCacheDirectorySetting(); | ||||||
|  |                 _uiShared.DrawParallelScansSetting(); | ||||||
|  |                 ImGui.TreePop(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void DrawWhiteListContent() | ||||||
|  |         { | ||||||
|  |             if (!_apiController.ServerAlive) return; | ||||||
|  |             if (ImGui.TreeNode("Whitelist Configuration")) | ||||||
|  |             { | ||||||
|  |                 if (ImGui.BeginTable("WhitelistTable", 6)) | ||||||
|  |                 { | ||||||
|  |                     ImGui.TableSetupColumn("Pause", ImGuiTableColumnFlags.WidthFixed, 50); | ||||||
|  |                     ImGui.TableSetupColumn("UID", ImGuiTableColumnFlags.WidthFixed, 110); | ||||||
|  |                     ImGui.TableSetupColumn("Status", ImGuiTableColumnFlags.WidthFixed, 120); | ||||||
|  |                     ImGui.TableSetupColumn("Paused", ImGuiTableColumnFlags.WidthFixed, 140); | ||||||
|  |                     ImGui.TableSetupColumn("Comment", ImGuiTableColumnFlags.WidthFixed, 400); | ||||||
|  |                     ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, 70); | ||||||
|  |  | ||||||
|  |                     ImGui.TableHeadersRow(); | ||||||
|  |                     foreach (var item in _apiController.WhitelistEntries.ToList()) | ||||||
|  |                     { | ||||||
|  |                         ImGui.TableNextColumn(); | ||||||
|  |                         bool isPaused = item.IsPaused; | ||||||
|  |                         if (ImGui.Checkbox("##paused" + item.OtherUID, ref isPaused)) | ||||||
|  |                         { | ||||||
|  |                             _ = _apiController.SendWhitelistPauseChange(item.OtherUID, isPaused); | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                         ImGui.TableNextColumn(); | ||||||
|  |                         ImGui.TextColored( | ||||||
|  |                             UIShared.GetBoolColor(item.IsSynced && !item.IsPausedFromOthers && !item.IsPaused), | ||||||
|  |                             item.OtherUID); | ||||||
|  |                         ImGui.TableNextColumn(); | ||||||
|  |                         ImGui.TextColored(UIShared.GetBoolColor(item.IsSynced), | ||||||
|  |                             !item.IsSynced ? "Has not added you" : "Whitelisted"); | ||||||
|  |                         ImGui.TableNextColumn(); | ||||||
|  |                         string pauseYou = item.IsPaused ? "You paused them" : ""; | ||||||
|  |                         string pauseThey = item.IsPausedFromOthers ? "They paused you" : ""; | ||||||
|  |                         string separator = (item.IsPaused && item.IsPausedFromOthers) ? Environment.NewLine : ""; | ||||||
|  |                         string entry = pauseYou + separator + pauseThey; | ||||||
|  |                         ImGui.TextColored(UIShared.GetBoolColor(!item.IsPausedFromOthers && !item.IsPaused), | ||||||
|  |                             string.IsNullOrEmpty(entry) ? "No" : entry); | ||||||
|  |                         ImGui.TableNextColumn(); | ||||||
|  |                         string charComment = _configuration.UidComments.ContainsKey(item.OtherUID) ? _configuration.UidComments[item.OtherUID] : string.Empty; | ||||||
|  |                         ImGui.SetNextItemWidth(400); | ||||||
|  |                         if (ImGui.InputTextWithHint("##comment" + item.OtherUID, "Add your comment here (comments will not be synced)", ref charComment, 255)) | ||||||
|  |                         { | ||||||
|  |                             _configuration.UidComments[item.OtherUID] = charComment; | ||||||
|  |                             _configuration.Save(); | ||||||
|  |                         } | ||||||
|  |                         ImGui.TableNextColumn(); | ||||||
|  |                         if (ImGui.Button("Delete##" + item.OtherUID)) | ||||||
|  |                         { | ||||||
|  |                             _ = _apiController.SendWhitelistRemoval(item.OtherUID); | ||||||
|  |                             _apiController.WhitelistEntries.Remove(item); | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                         ImGui.TableNextRow(); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     ImGui.EndTable(); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 var whitelistEntry = tempDto.OtherUID; | ||||||
|  |                 ImGui.SetNextItemWidth(200); | ||||||
|  |                 if (ImGui.InputText("UID", ref whitelistEntry, 20)) | ||||||
|  |                 { | ||||||
|  |                     tempDto.OtherUID = whitelistEntry; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 ImGui.SameLine(); | ||||||
|  |                 if (ImGui.Button("Add to whitelist")) | ||||||
|  |                 { | ||||||
|  |                     if (_apiController.WhitelistEntries.All(w => w.OtherUID != tempDto.OtherUID)) | ||||||
|  |                     { | ||||||
|  |                         _apiController.WhitelistEntries.Add(new WhitelistDto() | ||||||
|  |                         { | ||||||
|  |                             OtherUID = tempDto.OtherUID | ||||||
|  |                         }); | ||||||
|  |  | ||||||
|  |                         _ = _apiController.SendWhitelistAddition(tempDto.OtherUID); | ||||||
|  |  | ||||||
|  |                         tempDto.OtherUID = string.Empty; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 ImGui.TreePop(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private WhitelistDto tempDto = new WhitelistDto() { OtherUID = string.Empty }; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										184
									
								
								MareSynchronos/UI/UIShared.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								MareSynchronos/UI/UIShared.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,184 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.IO; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Numerics; | ||||||
|  | using System.Text; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  | using Dalamud.Interface.Colors; | ||||||
|  | using ImGuiNET; | ||||||
|  | using MareSynchronos.FileCacheDB; | ||||||
|  | using MareSynchronos.Managers; | ||||||
|  | using MareSynchronos.WebAPI; | ||||||
|  |  | ||||||
|  | namespace MareSynchronos.UI | ||||||
|  | { | ||||||
|  |     internal class UIShared | ||||||
|  |     { | ||||||
|  |         private readonly IpcManager _ipcManager; | ||||||
|  |         private readonly ApiController _apiController; | ||||||
|  |         private readonly FileCacheManager _fileCacheManager; | ||||||
|  |         private readonly Configuration _pluginConfiguration; | ||||||
|  |  | ||||||
|  |         public UIShared(IpcManager ipcManager, ApiController apiController, FileCacheManager fileCacheManager, Configuration pluginConfiguration) | ||||||
|  |         { | ||||||
|  |             _ipcManager = ipcManager; | ||||||
|  |             _apiController = apiController; | ||||||
|  |             _fileCacheManager = fileCacheManager; | ||||||
|  |             _pluginConfiguration = pluginConfiguration; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public bool DrawOtherPluginState() | ||||||
|  |         { | ||||||
|  |             var penumbraExists = _ipcManager.CheckPenumbraApi(); | ||||||
|  |             var glamourerExists = _ipcManager.CheckGlamourerApi(); | ||||||
|  |  | ||||||
|  |             var penumbraColor = penumbraExists ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; | ||||||
|  |             var glamourerColor = glamourerExists ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; | ||||||
|  |             ImGui.Text("Penumbra:"); | ||||||
|  |             ImGui.SameLine(); | ||||||
|  |             ImGui.TextColored(penumbraColor, penumbraExists ? "Available" : "Unavailable"); | ||||||
|  |             ImGui.SameLine(); | ||||||
|  |             ImGui.Text("Glamourer:"); | ||||||
|  |             ImGui.SameLine(); | ||||||
|  |             ImGui.TextColored(glamourerColor, glamourerExists ? "Available" : "Unavailable"); | ||||||
|  |  | ||||||
|  |             if (!penumbraExists || !glamourerExists) | ||||||
|  |             { | ||||||
|  |                 ImGui.TextColored(ImGuiColors.DalamudRed, "You need to install both Penumbra and Glamourer and keep them up to date to use Mare Synchronos."); | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void DrawFileScanState() | ||||||
|  |         { | ||||||
|  |             ImGui.Text("File Scanner Status"); | ||||||
|  |             if (_fileCacheManager.IsScanRunning) | ||||||
|  |             { | ||||||
|  |                 ImGui.Text("Scan is running"); | ||||||
|  |                 ImGui.Text("Current Progress:"); | ||||||
|  |                 ImGui.SameLine(); | ||||||
|  |                 ImGui.Text(_fileCacheManager.TotalFiles <= 0 | ||||||
|  |                     ? "Collecting files" | ||||||
|  |                     : $"Processing {_fileCacheManager.CurrentFileProgress} / {_fileCacheManager.TotalFiles} files"); | ||||||
|  |             } | ||||||
|  |             else if (_fileCacheManager.TimeToNextScan.TotalSeconds == 0) | ||||||
|  |             { | ||||||
|  |                 ImGui.Text("Scan not started"); | ||||||
|  |             } | ||||||
|  |             { | ||||||
|  |                 ImGui.Text("Next scan in " + _fileCacheManager.TimeToNextScan.ToString(@"mm\:ss") + " minutes"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void PrintServerState() | ||||||
|  |         { | ||||||
|  |             ImGui.Text("Service status of " + (string.IsNullOrEmpty(_pluginConfiguration.ApiUri) ? ApiController.MainServer : _pluginConfiguration.ApiUri)); | ||||||
|  |             ImGui.SameLine(); | ||||||
|  |             var color = _apiController.ServerAlive ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; | ||||||
|  |             ImGui.TextColored(color, _apiController.ServerAlive ? "Available" : "Unavailable"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public static void TextWrapped(string text) | ||||||
|  |         { | ||||||
|  |             ImGui.PushTextWrapPos(0); | ||||||
|  |             ImGui.TextUnformatted(text); | ||||||
|  |             ImGui.PopTextWrapPos(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public static Vector4 GetBoolColor(bool input) => input ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; | ||||||
|  |  | ||||||
|  |         public static Vector4 UploadColor((long, long) data) => data.Item1 == 0 ? ImGuiColors.DalamudGrey : | ||||||
|  |             data.Item1 == data.Item2 ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudYellow; | ||||||
|  |  | ||||||
|  |         public static string ByteToKiB(long bytes) => (bytes / 1024.0).ToString("0.00") + " KiB"; | ||||||
|  |  | ||||||
|  |         private int _serverSelectionIndex = 0; | ||||||
|  |  | ||||||
|  |         public void DrawServiceSelection() | ||||||
|  |         { | ||||||
|  |             string[] comboEntries = new[] { ApiController.MainServer, "Custom Service" }; | ||||||
|  |             if (ImGui.BeginCombo("Service", comboEntries[_serverSelectionIndex])) | ||||||
|  |             { | ||||||
|  |                 for (int n = 0; n < comboEntries.Length; n++) | ||||||
|  |                 { | ||||||
|  |                     bool isSelected = _serverSelectionIndex == n; | ||||||
|  |                     if (ImGui.Selectable(comboEntries[n], isSelected)) | ||||||
|  |                     { | ||||||
|  |                         _serverSelectionIndex = n; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     if (isSelected) | ||||||
|  |                     { | ||||||
|  |                         ImGui.SetItemDefaultFocus(); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     bool useCustomService = _serverSelectionIndex != 0; | ||||||
|  |  | ||||||
|  |                     if (_apiController.UseCustomService != useCustomService) | ||||||
|  |                     { | ||||||
|  |                         _apiController.UseCustomService = useCustomService; | ||||||
|  |                         _pluginConfiguration.Save(); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 ImGui.EndCombo(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (_apiController.UseCustomService) | ||||||
|  |             { | ||||||
|  |                 string serviceAddress = _pluginConfiguration.ApiUri; | ||||||
|  |                 if (ImGui.InputText("Service address", ref serviceAddress, 255)) | ||||||
|  |                 { | ||||||
|  |                     if (_pluginConfiguration.ApiUri != serviceAddress) | ||||||
|  |                     { | ||||||
|  |                         _pluginConfiguration.ApiUri = serviceAddress; | ||||||
|  |                         _apiController.RestartHeartbeat(); | ||||||
|  |                         _pluginConfiguration.Save(); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             PrintServerState(); | ||||||
|  |             if (_apiController.ServerAlive) | ||||||
|  |             { | ||||||
|  |                 if (ImGui.Button("Register")) | ||||||
|  |                 { | ||||||
|  |                     Task.WaitAll(_apiController.Register()); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void DrawCacheDirectorySetting() | ||||||
|  |         { | ||||||
|  |             var cacheDirectory = _pluginConfiguration.CacheFolder; | ||||||
|  |             if (ImGui.InputText("Cache Folder##cache", ref cacheDirectory, 255)) | ||||||
|  |             { | ||||||
|  |                 _pluginConfiguration.CacheFolder = cacheDirectory; | ||||||
|  |                 if (!string.IsNullOrEmpty(_pluginConfiguration.CacheFolder) && Directory.Exists(_pluginConfiguration.CacheFolder)) | ||||||
|  |                 { | ||||||
|  |                     _pluginConfiguration.Save(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (!Directory.Exists(cacheDirectory)) | ||||||
|  |             { | ||||||
|  |                 ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); | ||||||
|  |                 UIShared.TextWrapped("The folder you selected does not exist. Please provide a valid path."); | ||||||
|  |                 ImGui.PopStyleColor(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void DrawParallelScansSetting() | ||||||
|  |         { | ||||||
|  |             var parallelScans = _pluginConfiguration.MaxParallelScan; | ||||||
|  |             if (ImGui.SliderInt("Parallel File Scans##parallelism", ref parallelScans, 1, 20)) | ||||||
|  |             { | ||||||
|  |                 _pluginConfiguration.MaxParallelScan = parallelScans; | ||||||
|  |                 _pluginConfiguration.Save(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -27,32 +27,39 @@ namespace MareSynchronos.WebAPI | |||||||
| { | { | ||||||
|     public class CharacterReceivedEventArgs : EventArgs |     public class CharacterReceivedEventArgs : EventArgs | ||||||
|     { |     { | ||||||
|  |         public CharacterReceivedEventArgs(string characterNameHash, CharacterCacheDto characterData) | ||||||
|  |         { | ||||||
|  |             CharacterData = characterData; | ||||||
|  |             CharacterNameHash = characterNameHash; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         public CharacterCacheDto CharacterData { get; set; } |         public CharacterCacheDto CharacterData { get; set; } | ||||||
|         public string CharacterNameHash { get; set; } |         public string CharacterNameHash { get; set; } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class ApiController : IDisposable |     public class ApiController : IDisposable | ||||||
|     { |     { | ||||||
|         private readonly Configuration pluginConfiguration; |         public const string MainServer = "Lunae Crescere Incipientis (Central Server EU)"; | ||||||
|         private const string MainService = "https://darkarchon.internet-box.ch:5001"; |  | ||||||
|  |         private readonly Configuration _pluginConfiguration; | ||||||
|  |         public const string MainServiceUri = "https://darkarchon.internet-box.ch:5001"; | ||||||
|         public string UID { get; private set; } = string.Empty; |         public string UID { get; private set; } = string.Empty; | ||||||
|         public string SecretKey => pluginConfiguration.ClientSecret.ContainsKey(ApiUri) ? pluginConfiguration.ClientSecret[ApiUri] : "-"; |         public string SecretKey => _pluginConfiguration.ClientSecret.ContainsKey(ApiUri) ? _pluginConfiguration.ClientSecret[ApiUri] : "-"; | ||||||
|         private string CacheFolder => pluginConfiguration.CacheFolder; |         private string CacheFolder => _pluginConfiguration.CacheFolder; | ||||||
|         public ConcurrentDictionary<string, (long, long)> CurrentUploads { get; private set; } = new(); |         public ConcurrentDictionary<string, (long, long)> CurrentUploads { get; } = new(); | ||||||
|         public ConcurrentDictionary<string, (long, long)> CurrentDownloads { get; private set; } = new(); |         public ConcurrentDictionary<string, (long, long)> CurrentDownloads { get; } = new(); | ||||||
|         public bool IsDownloading { get; private set; } = false; |         public bool IsDownloading { get; private set; } = false; | ||||||
|         public bool IsUploading { get; private set; } = false; |         public bool IsUploading { get; private set; } = false; | ||||||
|         public int TotalTransfersPending { get; set; } = 0; |  | ||||||
|         public bool UseCustomService |         public bool UseCustomService | ||||||
|         { |         { | ||||||
|             get => pluginConfiguration.UseCustomService; |             get => _pluginConfiguration.UseCustomService; | ||||||
|             set |             set | ||||||
|             { |             { | ||||||
|                 pluginConfiguration.UseCustomService = value; |                 _pluginConfiguration.UseCustomService = value; | ||||||
|                 pluginConfiguration.Save(); |                 _pluginConfiguration.Save(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         private string ApiUri => UseCustomService ? pluginConfiguration.ApiUri : MainService; |         private string ApiUri => UseCustomService ? _pluginConfiguration.ApiUri : MainServiceUri; | ||||||
|  |  | ||||||
|         public bool ServerAlive => |         public bool ServerAlive => | ||||||
|             (_heartbeatHub?.State ?? HubConnectionState.Disconnected) == HubConnectionState.Connected; |             (_heartbeatHub?.State ?? HubConnectionState.Disconnected) == HubConnectionState.Connected; | ||||||
| @@ -76,7 +83,7 @@ namespace MareSynchronos.WebAPI | |||||||
|  |  | ||||||
|         public ApiController(Configuration pluginConfiguration) |         public ApiController(Configuration pluginConfiguration) | ||||||
|         { |         { | ||||||
|             this.pluginConfiguration = pluginConfiguration; |             this._pluginConfiguration = pluginConfiguration; | ||||||
|             cts = new CancellationTokenSource(); |             cts = new CancellationTokenSource(); | ||||||
|  |  | ||||||
|             _ = Heartbeat(); |             _ = Heartbeat(); | ||||||
| @@ -294,8 +301,8 @@ namespace MareSynchronos.WebAPI | |||||||
|             if (!ServerAlive) return; |             if (!ServerAlive) return; | ||||||
|             PluginLog.Debug("Registering at service " + ApiUri); |             PluginLog.Debug("Registering at service " + ApiUri); | ||||||
|             var response = await _userHub!.InvokeAsync<string>("Register"); |             var response = await _userHub!.InvokeAsync<string>("Register"); | ||||||
|             pluginConfiguration.ClientSecret[ApiUri] = response; |             _pluginConfiguration.ClientSecret[ApiUri] = response; | ||||||
|             pluginConfiguration.Save(); |             _pluginConfiguration.Save(); | ||||||
|             RestartHeartbeat(); |             RestartHeartbeat(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -317,7 +324,7 @@ namespace MareSynchronos.WebAPI | |||||||
|             CancelUpload(); |             CancelUpload(); | ||||||
|             uploadCancellationTokenSource = new CancellationTokenSource(); |             uploadCancellationTokenSource = new CancellationTokenSource(); | ||||||
|             var uploadToken = uploadCancellationTokenSource.Token; |             var uploadToken = uploadCancellationTokenSource.Token; | ||||||
|             PluginLog.Warning("New Token Created"); |             PluginLog.Debug("New Token Created"); | ||||||
|  |  | ||||||
|             var filesToUpload = await _fileHub!.InvokeAsync<List<string>>("SendFiles", character.FileReplacements, uploadToken); |             var filesToUpload = await _fileHub!.InvokeAsync<List<string>>("SendFiles", character.FileReplacements, uploadToken); | ||||||
|  |  | ||||||
| @@ -344,7 +351,7 @@ namespace MareSynchronos.WebAPI | |||||||
|             } |             } | ||||||
|             PluginLog.Debug("Upload tasks complete, waiting for server to confirm"); |             PluginLog.Debug("Upload tasks complete, waiting for server to confirm"); | ||||||
|             var anyUploadsOpen = await _fileHub!.InvokeAsync<bool>("IsUploadFinished", uploadToken); |             var anyUploadsOpen = await _fileHub!.InvokeAsync<bool>("IsUploadFinished", uploadToken); | ||||||
|             PluginLog.Warning("Uploads open: " + anyUploadsOpen); |             PluginLog.Debug("Uploads open: " + anyUploadsOpen); | ||||||
|             while (anyUploadsOpen && !uploadToken.IsCancellationRequested) |             while (anyUploadsOpen && !uploadToken.IsCancellationRequested) | ||||||
|             { |             { | ||||||
|                 anyUploadsOpen = await _fileHub!.InvokeAsync<bool>("IsUploadFinished", uploadToken); |                 anyUploadsOpen = await _fileHub!.InvokeAsync<bool>("IsUploadFinished", uploadToken); | ||||||
| @@ -357,7 +364,7 @@ namespace MareSynchronos.WebAPI | |||||||
|  |  | ||||||
|             if (!uploadToken.IsCancellationRequested) |             if (!uploadToken.IsCancellationRequested) | ||||||
|             { |             { | ||||||
|                 PluginLog.Warning("=== Pushing character data ==="); |                 PluginLog.Debug("=== Pushing character data ==="); | ||||||
|                 await _userHub!.InvokeAsync("PushCharacterData", character, visibleCharacterIds, uploadToken); |                 await _userHub!.InvokeAsync("PushCharacterData", character, visibleCharacterIds, uploadToken); | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
| @@ -372,11 +379,7 @@ namespace MareSynchronos.WebAPI | |||||||
|         public Task ReceiveCharacterData(CharacterCacheDto character, string characterHash) |         public Task ReceiveCharacterData(CharacterCacheDto character, string characterHash) | ||||||
|         { |         { | ||||||
|             PluginLog.Debug("Received DTO for " + characterHash); |             PluginLog.Debug("Received DTO for " + characterHash); | ||||||
|             CharacterReceived?.Invoke(null, new CharacterReceivedEventArgs() |             CharacterReceived?.Invoke(null, new CharacterReceivedEventArgs(characterHash, character)); | ||||||
|             { |  | ||||||
|                 CharacterData = character, |  | ||||||
|                 CharacterNameHash = characterHash |  | ||||||
|             }); |  | ||||||
|             return Task.CompletedTask; |             return Task.CompletedTask; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Stanley Dimant
					Stanley Dimant