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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using MareSynchronos.WebAPI;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MareSynchronos
|
||||
{
|
||||
@@ -11,23 +14,49 @@ namespace MareSynchronos
|
||||
public int Version { get; set; } = 0;
|
||||
|
||||
public string CacheFolder { get; set; } = string.Empty;
|
||||
public Dictionary<string, string> ClientSecret { get; internal set; } = new();
|
||||
public string ApiUri { get; internal set; } = string.Empty;
|
||||
public bool UseCustomService { get; internal set; }
|
||||
public Dictionary<string, string> ClientSecret { get; set; } = new();
|
||||
public Dictionary<string, string> UidComments { get; set; } = new();
|
||||
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
|
||||
|
||||
[NonSerialized]
|
||||
private DalamudPluginInterface? pluginInterface;
|
||||
private DalamudPluginInterface? _pluginInterface;
|
||||
|
||||
public void Initialize(DalamudPluginInterface pluginInterface)
|
||||
{
|
||||
this.pluginInterface = pluginInterface;
|
||||
this._pluginInterface = pluginInterface;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
this.pluginInterface!.SavePluginConfig(this);
|
||||
this._pluginInterface!.SavePluginConfig(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace MareSynchronos.Factories
|
||||
|
||||
public FileReplacement Create()
|
||||
{
|
||||
if (!ipcManager.CheckPenumbraAPI())
|
||||
if (!ipcManager.CheckPenumbraApi())
|
||||
{
|
||||
throw new System.Exception();
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace MareSynchronos.Managers
|
||||
private string _lastSentHash = string.Empty;
|
||||
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,
|
||||
Configuration pluginConfiguration)
|
||||
@@ -154,6 +154,11 @@ namespace MareSynchronos.Managers
|
||||
_watcher.Disable();
|
||||
_watcher.PlayerChanged -= Watcher_PlayerChanged;
|
||||
_watcher?.Dispose();
|
||||
|
||||
foreach (var character in _onlineWhitelistedUsers)
|
||||
{
|
||||
RestoreCharacter(character);
|
||||
}
|
||||
}
|
||||
|
||||
public void StopWatchPlayer(string name)
|
||||
@@ -164,7 +169,7 @@ namespace MareSynchronos.Managers
|
||||
public async Task UpdatePlayersFromService(Dictionary<string, PlayerCharacter> currentLocalPlayers)
|
||||
{
|
||||
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);
|
||||
await _apiController.GetCharacterData(currentLocalPlayers
|
||||
.ToDictionary(
|
||||
@@ -205,10 +210,10 @@ namespace MareSynchronos.Managers
|
||||
|
||||
Task.WaitAll(apiTask);
|
||||
|
||||
onlineWhitelistedUsers = new HashSet<string>(apiTask.Result);
|
||||
_onlineWhitelistedUsers = new HashSet<string>(apiTask.Result);
|
||||
var assignTask = AssignLocalPlayersData();
|
||||
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;
|
||||
_ipcManager.PenumbraRedrawEvent += IpcManager_PenumbraRedrawEvent;
|
||||
@@ -221,6 +226,12 @@ namespace MareSynchronos.Managers
|
||||
_framework.Update -= Framework_Update;
|
||||
_ipcManager.PenumbraRedrawEvent -= IpcManager_PenumbraRedrawEvent;
|
||||
_clientState.TerritoryChanged -= ClientState_TerritoryChanged;
|
||||
foreach (var character in _onlineWhitelistedUsers)
|
||||
{
|
||||
RestoreCharacter(character);
|
||||
}
|
||||
|
||||
_lastSentHash = string.Empty;
|
||||
}
|
||||
|
||||
private void ApiControllerOnAddedToWhitelist(object? sender, EventArgs e)
|
||||
@@ -230,7 +241,7 @@ namespace MareSynchronos.Managers
|
||||
var players = GetLocalPlayers();
|
||||
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 } });
|
||||
}
|
||||
}
|
||||
@@ -287,8 +298,8 @@ namespace MareSynchronos.Managers
|
||||
}
|
||||
|
||||
PluginLog.Debug("Assigned hash to visible player: " + otherPlayerName);
|
||||
/*ipcManager.PenumbraRemoveTemporaryCollection(otherPlayerName);
|
||||
ipcManager.PenumbraCreateTemporaryCollection(otherPlayerName);
|
||||
_ipcManager.PenumbraRemoveTemporaryCollection(otherPlayerName);
|
||||
_ipcManager.PenumbraCreateTemporaryCollection(otherPlayerName);
|
||||
Dictionary<string, string> moddedPaths = new();
|
||||
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.PenumbraRedraw(otherPlayerName);
|
||||
_ipcManager.PenumbraRedraw(otherPlayerName);
|
||||
}
|
||||
|
||||
private void ApiControllerOnRemovedFromWhitelist(object? sender, EventArgs e)
|
||||
{
|
||||
var characterHash = (string?)sender;
|
||||
if (string.IsNullOrEmpty(characterHash)) return;
|
||||
RestoreCharacter(characterHash);
|
||||
}
|
||||
|
||||
private void RestoreCharacter(string characterHash)
|
||||
{
|
||||
var players = GetLocalPlayers();
|
||||
|
||||
foreach (var entry in _characterCache.Where(c => c.Key.Item1 == characterHash))
|
||||
{
|
||||
_characterCache.Remove(entry.Key);
|
||||
}
|
||||
|
||||
var playerName = players.SingleOrDefault(p => p.Key == characterHash).Value.Name.ToString() ?? null;
|
||||
if (playerName != null)
|
||||
foreach (var player in players)
|
||||
{
|
||||
if (player.Key != characterHash) continue;
|
||||
var playerName = player.Value.Name.ToString();
|
||||
RestorePreviousCharacter(playerName);
|
||||
PluginLog.Debug("Removed from whitelist, restoring glamourer state for " + playerName);
|
||||
_ipcManager.PenumbraRemoveTemporaryCollection(playerName);
|
||||
_ipcManager.GlamourerRevertCharacterCustomization(playerName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApiControllerOnWhitelistedPlayerOffline(object? sender, EventArgs e)
|
||||
{
|
||||
PluginLog.Debug("Player offline: " + sender!);
|
||||
onlineWhitelistedUsers.Remove((string)sender!);
|
||||
_onlineWhitelistedUsers.Remove((string)sender!);
|
||||
}
|
||||
|
||||
private void ApiControllerOnWhitelistedPlayerOnline(object? sender, EventArgs e)
|
||||
{
|
||||
PluginLog.Debug("Player online: " + sender!);
|
||||
onlineWhitelistedUsers.Add((string)sender!);
|
||||
_onlineWhitelistedUsers.Add((string)sender!);
|
||||
}
|
||||
|
||||
private async Task AssignLocalPlayersData()
|
||||
@@ -351,11 +370,7 @@ namespace MareSynchronos.Managers
|
||||
{
|
||||
if (currentLocalPlayers.ContainsKey(player.Key.Item1))
|
||||
{
|
||||
await Task.Run(() => ApiControllerOnCharacterReceived(null, new CharacterReceivedEventArgs
|
||||
{
|
||||
CharacterNameHash = player.Key.Item1,
|
||||
CharacterData = player.Value
|
||||
}));
|
||||
await Task.Run(() => ApiControllerOnCharacterReceived(null, new CharacterReceivedEventArgs(player.Key.Item1, player.Value)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,7 +428,7 @@ namespace MareSynchronos.Managers
|
||||
var pObj = (PlayerCharacter)obj;
|
||||
var hashedName = Crypto.GetHash256(pObj.Name.ToString() + pObj.HomeWorld.Id.ToString());
|
||||
|
||||
if (!onlineWhitelistedUsers.Contains(hashedName)) continue;
|
||||
if (!_onlineWhitelistedUsers.Contains(hashedName)) continue;
|
||||
|
||||
localPlayersList.Add(hashedName);
|
||||
if (!_cachedLocalPlayers.ContainsKey(hashedName)) newPlayers[hashedName] = pObj;
|
||||
@@ -455,7 +470,7 @@ namespace MareSynchronos.Managers
|
||||
string playerName = obj.Name.ToString();
|
||||
if (playerName == GetPlayerName()) continue;
|
||||
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;
|
||||
@@ -509,7 +524,7 @@ namespace MareSynchronos.Managers
|
||||
var cacheDto = characterCacheTask.Result.ToCharacterCacheDto();
|
||||
if (cacheDto.Hash == _lastSentHash)
|
||||
{
|
||||
PluginLog.Warning("Not sending data, already sent");
|
||||
PluginLog.Debug("Not sending data, already sent");
|
||||
return;
|
||||
}
|
||||
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
|
||||
{
|
||||
private readonly DalamudPluginInterface pluginInterface;
|
||||
private ICallGateSubscriber<object> penumbraInit;
|
||||
private ICallGateSubscriber<string, string, string>? penumbraResolvePath;
|
||||
private ICallGateSubscriber<string>? penumbraResolveModDir;
|
||||
private ICallGateSubscriber<string>? glamourerGetCharacterCustomization;
|
||||
private ICallGateSubscriber<string, string, object>? glamourerApplyCharacterCustomization;
|
||||
private ICallGateSubscriber<int> penumbraApiVersion;
|
||||
private ICallGateSubscriber<int> glamourerApiVersion;
|
||||
private ICallGateSubscriber<IntPtr, int, object?> penumbraObjectIsRedrawn;
|
||||
private ICallGateSubscriber<string, int, object>? penumbraRedraw;
|
||||
private ICallGateSubscriber<string, string, string[]>? penumbraReverseResolvePath;
|
||||
private ICallGateSubscriber<string, object> glamourerRevertCustomization;
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly ICallGateSubscriber<object> _penumbraInit;
|
||||
private readonly ICallGateSubscriber<string, string, string>? _penumbraResolvePath;
|
||||
private readonly ICallGateSubscriber<string>? _penumbraResolveModDir;
|
||||
private readonly ICallGateSubscriber<string>? _glamourerGetCharacterCustomization;
|
||||
private readonly ICallGateSubscriber<string, string, object>? _glamourerApplyCharacterCustomization;
|
||||
private readonly ICallGateSubscriber<int> _penumbraApiVersion;
|
||||
private readonly ICallGateSubscriber<int> _glamourerApiVersion;
|
||||
private readonly ICallGateSubscriber<IntPtr, int, object?> _penumbraObjectIsRedrawn;
|
||||
private readonly ICallGateSubscriber<string, int, object>? _penumbraRedraw;
|
||||
private readonly ICallGateSubscriber<string, string, string[]>? _penumbraReverseResolvePath;
|
||||
private readonly ICallGateSubscriber<string, object> _glamourerRevertCustomization;
|
||||
|
||||
private ICallGateSubscriber<string, string, IReadOnlyDictionary<string, string>, List<string>, int, int>
|
||||
penumbraSetTemporaryMod;
|
||||
private ICallGateSubscriber<string, string, bool, (int, string)> penumbraCreateTemporaryCollection;
|
||||
private ICallGateSubscriber<string, int> penumbraRemoveTemporaryCollection;
|
||||
private readonly ICallGateSubscriber<string, string, IReadOnlyDictionary<string, string>, List<string>, int, int>
|
||||
_penumbraSetTemporaryMod;
|
||||
private readonly ICallGateSubscriber<string, string, bool, (int, string)> _penumbraCreateTemporaryCollection;
|
||||
private readonly ICallGateSubscriber<string, int> _penumbraRemoveTemporaryCollection;
|
||||
|
||||
public bool Initialized { get; private set; } = false;
|
||||
|
||||
@@ -34,41 +34,41 @@ namespace MareSynchronos.Managers
|
||||
|
||||
public IpcManager(DalamudPluginInterface pi)
|
||||
{
|
||||
pluginInterface = pi;
|
||||
_pluginInterface = pi;
|
||||
|
||||
penumbraInit = pluginInterface.GetIpcSubscriber<object>("Penumbra.Initialized");
|
||||
penumbraResolvePath = pluginInterface.GetIpcSubscriber<string, string, string>("Penumbra.ResolveCharacterPath");
|
||||
penumbraResolveModDir = pluginInterface.GetIpcSubscriber<string>("Penumbra.GetModDirectory");
|
||||
penumbraRedraw = pluginInterface.GetIpcSubscriber<string, int, object>("Penumbra.RedrawObjectByName");
|
||||
glamourerGetCharacterCustomization = pluginInterface.GetIpcSubscriber<string>("Glamourer.GetCharacterCustomization");
|
||||
glamourerApplyCharacterCustomization = pluginInterface.GetIpcSubscriber<string, string, object>("Glamourer.ApplyCharacterCustomization");
|
||||
penumbraReverseResolvePath = pluginInterface.GetIpcSubscriber<string, string, string[]>("Penumbra.ReverseResolvePath");
|
||||
penumbraApiVersion = pluginInterface.GetIpcSubscriber<int>("Penumbra.ApiVersion");
|
||||
glamourerApiVersion = pluginInterface.GetIpcSubscriber<int>("Glamourer.ApiVersion");
|
||||
glamourerRevertCustomization = pluginInterface.GetIpcSubscriber<string, object>("Glamourer.RevertCharacterCustomization");
|
||||
penumbraObjectIsRedrawn = pluginInterface.GetIpcSubscriber<IntPtr, int, object?>("Penumbra.GameObjectRedrawn");
|
||||
_penumbraInit = _pluginInterface.GetIpcSubscriber<object>("Penumbra.Initialized");
|
||||
_penumbraResolvePath = _pluginInterface.GetIpcSubscriber<string, string, string>("Penumbra.ResolveCharacterPath");
|
||||
_penumbraResolveModDir = _pluginInterface.GetIpcSubscriber<string>("Penumbra.GetModDirectory");
|
||||
_penumbraRedraw = _pluginInterface.GetIpcSubscriber<string, int, object>("Penumbra.RedrawObjectByName");
|
||||
_glamourerGetCharacterCustomization = _pluginInterface.GetIpcSubscriber<string>("Glamourer.GetCharacterCustomization");
|
||||
_glamourerApplyCharacterCustomization = _pluginInterface.GetIpcSubscriber<string, string, object>("Glamourer.ApplyCharacterCustomization");
|
||||
_penumbraReverseResolvePath = _pluginInterface.GetIpcSubscriber<string, string, string[]>("Penumbra.ReverseResolvePath");
|
||||
_penumbraApiVersion = _pluginInterface.GetIpcSubscriber<int>("Penumbra.ApiVersion");
|
||||
_glamourerApiVersion = _pluginInterface.GetIpcSubscriber<int>("Glamourer.ApiVersion");
|
||||
_glamourerRevertCustomization = _pluginInterface.GetIpcSubscriber<string, object>("Glamourer.RevertCharacterCustomization");
|
||||
_penumbraObjectIsRedrawn = _pluginInterface.GetIpcSubscriber<IntPtr, int, object?>("Penumbra.GameObjectRedrawn");
|
||||
|
||||
penumbraObjectIsRedrawn.Subscribe(RedrawEvent);
|
||||
penumbraInit.Subscribe(RedrawSelf);
|
||||
_penumbraObjectIsRedrawn.Subscribe(RedrawEvent);
|
||||
_penumbraInit.Subscribe(RedrawSelf);
|
||||
|
||||
penumbraSetTemporaryMod =
|
||||
pluginInterface
|
||||
_penumbraSetTemporaryMod =
|
||||
_pluginInterface
|
||||
.GetIpcSubscriber<string, string, IReadOnlyDictionary<string, string>, List<string>, int,
|
||||
int>("Penumbra.AddTemporaryMod");
|
||||
|
||||
penumbraCreateTemporaryCollection =
|
||||
pluginInterface.GetIpcSubscriber<string, string, bool, (int, string)>("Penumbra.CreateTemporaryCollection");
|
||||
penumbraRemoveTemporaryCollection =
|
||||
pluginInterface.GetIpcSubscriber<string, int>("Penumbra.RemoveTemporaryCollection");
|
||||
_penumbraCreateTemporaryCollection =
|
||||
_pluginInterface.GetIpcSubscriber<string, string, bool, (int, string)>("Penumbra.CreateTemporaryCollection");
|
||||
_penumbraRemoveTemporaryCollection =
|
||||
_pluginInterface.GetIpcSubscriber<string, int>("Penumbra.RemoveTemporaryCollection");
|
||||
|
||||
Initialized = true;
|
||||
}
|
||||
|
||||
public bool CheckPenumbraAPI()
|
||||
public bool CheckPenumbraApi()
|
||||
{
|
||||
try
|
||||
{
|
||||
return penumbraApiVersion.InvokeFunc() >= 4;
|
||||
return _penumbraApiVersion.InvokeFunc() >= 4;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -76,11 +76,11 @@ namespace MareSynchronos.Managers
|
||||
}
|
||||
}
|
||||
|
||||
public bool CheckGlamourerAPI()
|
||||
public bool CheckGlamourerApi()
|
||||
{
|
||||
try
|
||||
{
|
||||
return glamourerApiVersion.InvokeFunc() >= 0;
|
||||
return _glamourerApiVersion.InvokeFunc() >= 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -95,91 +95,78 @@ namespace MareSynchronos.Managers
|
||||
|
||||
private void RedrawSelf()
|
||||
{
|
||||
penumbraRedraw!.InvokeAction("self", 0);
|
||||
_penumbraRedraw!.InvokeAction("self", 0);
|
||||
}
|
||||
|
||||
private void Uninitialize()
|
||||
{
|
||||
penumbraInit.Unsubscribe(RedrawSelf);
|
||||
penumbraObjectIsRedrawn.Unsubscribe(RedrawEvent);
|
||||
penumbraResolvePath = null;
|
||||
penumbraResolveModDir = null;
|
||||
glamourerGetCharacterCustomization = null;
|
||||
glamourerApplyCharacterCustomization = null;
|
||||
penumbraReverseResolvePath = null;
|
||||
_penumbraInit.Unsubscribe(RedrawSelf);
|
||||
_penumbraObjectIsRedrawn.Unsubscribe(RedrawEvent);
|
||||
Initialized = false;
|
||||
PluginLog.Debug("IPC Manager disposed");
|
||||
}
|
||||
|
||||
public string[] PenumbraReverseResolvePath(string path, string characterName)
|
||||
{
|
||||
if (!CheckPenumbraAPI()) return new[] { path };
|
||||
return penumbraReverseResolvePath!.InvokeFunc(path, characterName);
|
||||
if (!CheckPenumbraApi()) return new[] { path };
|
||||
return _penumbraReverseResolvePath!.InvokeFunc(path, characterName);
|
||||
}
|
||||
|
||||
public string? PenumbraResolvePath(string path, string characterName)
|
||||
{
|
||||
if (!CheckPenumbraAPI()) return null;
|
||||
return penumbraResolvePath!.InvokeFunc(path, characterName);
|
||||
if (!CheckPenumbraApi()) return null;
|
||||
return _penumbraResolvePath!.InvokeFunc(path, characterName);
|
||||
}
|
||||
|
||||
public string? PenumbraModDirectory()
|
||||
{
|
||||
if (!CheckPenumbraAPI()) return null;
|
||||
return penumbraResolveModDir!.InvokeFunc();
|
||||
if (!CheckPenumbraApi()) return null;
|
||||
return _penumbraResolveModDir!.InvokeFunc();
|
||||
}
|
||||
|
||||
public string? GlamourerGetCharacterCustomization()
|
||||
{
|
||||
if (!CheckGlamourerAPI()) return null;
|
||||
return glamourerGetCharacterCustomization!.InvokeFunc();
|
||||
if (!CheckGlamourerApi()) return null;
|
||||
return _glamourerGetCharacterCustomization!.InvokeFunc();
|
||||
}
|
||||
|
||||
public void GlamourerApplyCharacterCustomization(string customization, string characterName)
|
||||
{
|
||||
if (!CheckGlamourerAPI()) return;
|
||||
glamourerApplyCharacterCustomization!.InvokeAction(customization, characterName);
|
||||
if (!CheckGlamourerApi()) return;
|
||||
_glamourerApplyCharacterCustomization!.InvokeAction(customization, characterName);
|
||||
}
|
||||
|
||||
public void GlamourerRevertCharacterCustomization(string characterName)
|
||||
{
|
||||
if (!CheckGlamourerAPI()) return;
|
||||
glamourerRevertCustomization!.InvokeAction(characterName);
|
||||
if (!CheckGlamourerApi()) return;
|
||||
_glamourerRevertCustomization!.InvokeAction(characterName);
|
||||
}
|
||||
|
||||
public void PenumbraRedraw(string actorName)
|
||||
{
|
||||
if (!CheckPenumbraAPI()) return;
|
||||
penumbraRedraw!.InvokeAction(actorName, 0);
|
||||
if (!CheckPenumbraApi()) return;
|
||||
_penumbraRedraw!.InvokeAction(actorName, 0);
|
||||
}
|
||||
|
||||
public void PenumbraCreateTemporaryCollection(string characterName)
|
||||
{
|
||||
if (!CheckPenumbraAPI()) return;
|
||||
if (!CheckPenumbraApi()) return;
|
||||
PluginLog.Debug("Creating temp collection for " + characterName);
|
||||
//penumbraCreateTemporaryCollection.InvokeFunc("MareSynchronos", characterName, true);
|
||||
}
|
||||
|
||||
public void PenumbraRemoveTemporaryCollection(string characterName)
|
||||
{
|
||||
if (!CheckPenumbraAPI()) return;
|
||||
if (!CheckPenumbraApi()) return;
|
||||
PluginLog.Debug("Removing temp collection for " + characterName);
|
||||
//penumbraRemoveTemporaryCollection.InvokeFunc(characterName);
|
||||
}
|
||||
|
||||
public void PenumbraSetTemporaryMods(string characterName, IReadOnlyDictionary<string, string> modPaths)
|
||||
{
|
||||
if (!CheckPenumbraAPI()) return;
|
||||
if (!CheckPenumbraApi()) return;
|
||||
PluginLog.Debug("Assigning temp mods for " + characterName);
|
||||
try
|
||||
{
|
||||
//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");
|
||||
}
|
||||
//penumbraSetTemporaryMod.InvokeFunc("MareSynchronos", characterName, modPaths, new List<string>(), 0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -13,44 +13,61 @@ using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Game.ClientState;
|
||||
using System;
|
||||
using MareSynchronos.Models;
|
||||
using Dalamud.Game.Gui;
|
||||
using MareSynchronos.PenumbraMod;
|
||||
using Newtonsoft.Json;
|
||||
using MareSynchronos.Managers;
|
||||
using LZ4;
|
||||
using MareSynchronos.WebAPI;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using MareSynchronos.UI;
|
||||
|
||||
namespace MareSynchronos
|
||||
{
|
||||
public sealed class Plugin : IDalamudPlugin
|
||||
{
|
||||
private const string commandName = "/mare";
|
||||
private readonly ClientState clientState;
|
||||
private readonly Framework framework;
|
||||
private readonly ObjectTable objectTable;
|
||||
private readonly WindowSystem windowSystem;
|
||||
private readonly ApiController apiController;
|
||||
private CharacterManager? characterManager;
|
||||
private IpcManager ipcManager;
|
||||
private const string CommandName = "/mare";
|
||||
private readonly ApiController _apiController;
|
||||
private readonly ClientState _clientState;
|
||||
private readonly CommandManager _commandManager;
|
||||
private readonly Configuration _configuration;
|
||||
private readonly FileCacheManager _fileCacheManager;
|
||||
private readonly Framework _framework;
|
||||
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,
|
||||
Framework framework, ObjectTable objectTable, ClientState clientState)
|
||||
{
|
||||
this.PluginInterface = pluginInterface;
|
||||
this.CommandManager = commandManager;
|
||||
this.framework = framework;
|
||||
this.objectTable = objectTable;
|
||||
this.clientState = clientState;
|
||||
Configuration = this.PluginInterface.GetPluginConfig() as Configuration ?? new Configuration();
|
||||
Configuration.Initialize(this.PluginInterface);
|
||||
_pluginInterface = pluginInterface;
|
||||
_commandManager = commandManager;
|
||||
_framework = framework;
|
||||
_objectTable = objectTable;
|
||||
_clientState = clientState;
|
||||
_configuration = _pluginInterface.GetPluginConfig() as Configuration ?? new Configuration();
|
||||
_configuration.Initialize(_pluginInterface);
|
||||
|
||||
windowSystem = new WindowSystem("MareSynchronos");
|
||||
_windowSystem = new WindowSystem("MareSynchronos");
|
||||
|
||||
apiController = new ApiController(Configuration);
|
||||
ipcManager = new IpcManager(PluginInterface);
|
||||
_apiController = new ApiController(_configuration);
|
||||
_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
|
||||
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
|
||||
|
||||
@@ -64,54 +81,64 @@ namespace MareSynchronos
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
this.PluginUi?.Dispose();
|
||||
this.CommandManager.RemoveHandler(commandName);
|
||||
clientState.Login -= ClientState_Login;
|
||||
clientState.Logout -= ClientState_Logout;
|
||||
ipcManager?.Dispose();
|
||||
characterManager?.Dispose();
|
||||
apiController?.Dispose();
|
||||
_commandManager.RemoveHandler(CommandName);
|
||||
_clientState.Login -= ClientState_Login;
|
||||
_clientState.Logout -= ClientState_Logout;
|
||||
|
||||
_pluginUi?.Dispose();
|
||||
_introUi?.Dispose();
|
||||
|
||||
_fileCacheManager?.Dispose();
|
||||
_ipcManager?.Dispose();
|
||||
_characterManager?.Dispose();
|
||||
_apiController?.Dispose();
|
||||
}
|
||||
|
||||
private void ClientState_Login(object? sender, EventArgs e)
|
||||
{
|
||||
PluginLog.Debug("Client login");
|
||||
|
||||
if (!_configuration.HasValidSetup)
|
||||
{
|
||||
_introUi.IsOpen = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_introUi.IsOpen = false;
|
||||
_pluginInterface.UiBuilder.Draw += Draw;
|
||||
}
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (clientState.LocalPlayer == null)
|
||||
while (_clientState.LocalPlayer == null)
|
||||
{
|
||||
await Task.Delay(50);
|
||||
}
|
||||
|
||||
characterManager = new CharacterManager(
|
||||
clientState, framework, apiController, objectTable, ipcManager, new FileReplacementFactory(ipcManager), Configuration);
|
||||
characterManager.StartWatchingPlayer();
|
||||
ipcManager.PenumbraRedraw(clientState.LocalPlayer!.Name.ToString());
|
||||
_characterManager = new CharacterManager(
|
||||
_clientState, _framework, _apiController, _objectTable, _ipcManager, new FileReplacementFactory(_ipcManager), _configuration);
|
||||
_characterManager.StartWatchingPlayer();
|
||||
_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)
|
||||
{
|
||||
PluginLog.Debug("Client logout");
|
||||
characterManager?.Dispose();
|
||||
PluginInterface.UiBuilder.Draw -= Draw;
|
||||
PluginInterface.UiBuilder.OpenConfigUi -= OpenConfigUI;
|
||||
CommandManager.RemoveHandler(commandName);
|
||||
_characterManager?.Dispose();
|
||||
_pluginInterface.UiBuilder.Draw -= Draw;
|
||||
_pluginInterface.UiBuilder.OpenConfigUi -= OpenConfigUi;
|
||||
_commandManager.RemoveHandler(CommandName);
|
||||
}
|
||||
|
||||
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 newFilePath = Path.Combine(targetDirectory, "files", fileCache.Hash.ToLower() + ext);
|
||||
string lc4hcPath = Path.Combine(targetDirectory, "files", "lz4hc." + fileCache.Hash.ToLower() + ext);
|
||||
if (!File.Exists(lc4hcPath))
|
||||
string lc4HcPath = Path.Combine(targetDirectory, "files", "lz4hc." + fileCache.Hash.ToLower() + ext);
|
||||
if (!File.Exists(lc4HcPath))
|
||||
{
|
||||
|
||||
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();
|
||||
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);
|
||||
if (resourceDict != null)
|
||||
{
|
||||
foreach(var path in replacement.GamePaths)
|
||||
foreach (var path in replacement.GamePaths)
|
||||
{
|
||||
resourceDict[path] = $"files\\{fileCache.Hash.ToLower() + ext}";
|
||||
}
|
||||
@@ -157,40 +184,35 @@ namespace MareSynchronos
|
||||
|
||||
private void Draw()
|
||||
{
|
||||
windowSystem.Draw();
|
||||
}
|
||||
|
||||
private void OpenConfigUI()
|
||||
{
|
||||
this.PluginUi.Toggle();
|
||||
_windowSystem.Draw();
|
||||
}
|
||||
|
||||
private void OnCommand(string command, string args)
|
||||
{
|
||||
if (args == "printjson")
|
||||
{
|
||||
_ = characterManager?.DebugJson();
|
||||
_ = _characterManager?.DebugJson();
|
||||
}
|
||||
|
||||
if (args.StartsWith("watch"))
|
||||
{
|
||||
var playerName = args.Replace("watch", "").Trim();
|
||||
characterManager!.WatchPlayer(playerName);
|
||||
_characterManager!.WatchPlayer(playerName);
|
||||
}
|
||||
|
||||
if (args.StartsWith("stop"))
|
||||
{
|
||||
var playerName = args.Replace("watch", "").Trim();
|
||||
characterManager!.StopWatchPlayer(playerName);
|
||||
_characterManager!.StopWatchPlayer(playerName);
|
||||
}
|
||||
|
||||
if (args == "createtestmod")
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
var playerName = clientState.LocalPlayer!.Name.ToString();
|
||||
var playerName = _clientState.LocalPlayer!.Name.ToString();
|
||||
var modName = $"Mare Synchronos Test Mod {playerName}";
|
||||
var modDirectory = ipcManager!.PenumbraModDirectory()!;
|
||||
var modDirectory = _ipcManager!.PenumbraModDirectory()!;
|
||||
string modDirectoryPath = Path.Combine(modDirectory, modName);
|
||||
if (Directory.Exists(modDirectoryPath))
|
||||
{
|
||||
@@ -206,7 +228,7 @@ namespace MareSynchronos
|
||||
Description = "Mare Synchronous Test Mod Export",
|
||||
};
|
||||
|
||||
var resources = characterManager!.BuildCharacterCache();
|
||||
var resources = _characterManager!.BuildCharacterCache();
|
||||
var metaJson = JsonConvert.SerializeObject(meta);
|
||||
File.WriteAllText(Path.Combine(modDirectoryPath, "meta.json"), metaJson);
|
||||
|
||||
@@ -229,8 +251,13 @@ namespace MareSynchronos
|
||||
|
||||
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 CharacterReceivedEventArgs(string characterNameHash, CharacterCacheDto characterData)
|
||||
{
|
||||
CharacterData = characterData;
|
||||
CharacterNameHash = characterNameHash;
|
||||
}
|
||||
|
||||
public CharacterCacheDto CharacterData { get; set; }
|
||||
public string CharacterNameHash { get; set; }
|
||||
}
|
||||
|
||||
public class ApiController : IDisposable
|
||||
{
|
||||
private readonly Configuration pluginConfiguration;
|
||||
private const string MainService = "https://darkarchon.internet-box.ch:5001";
|
||||
public const string MainServer = "Lunae Crescere Incipientis (Central Server EU)";
|
||||
|
||||
private readonly Configuration _pluginConfiguration;
|
||||
public const string MainServiceUri = "https://darkarchon.internet-box.ch:5001";
|
||||
public string UID { get; private set; } = string.Empty;
|
||||
public string SecretKey => pluginConfiguration.ClientSecret.ContainsKey(ApiUri) ? pluginConfiguration.ClientSecret[ApiUri] : "-";
|
||||
private string CacheFolder => pluginConfiguration.CacheFolder;
|
||||
public ConcurrentDictionary<string, (long, long)> CurrentUploads { get; private set; } = new();
|
||||
public ConcurrentDictionary<string, (long, long)> CurrentDownloads { get; private set; } = new();
|
||||
public string SecretKey => _pluginConfiguration.ClientSecret.ContainsKey(ApiUri) ? _pluginConfiguration.ClientSecret[ApiUri] : "-";
|
||||
private string CacheFolder => _pluginConfiguration.CacheFolder;
|
||||
public ConcurrentDictionary<string, (long, long)> CurrentUploads { get; } = new();
|
||||
public ConcurrentDictionary<string, (long, long)> CurrentDownloads { get; } = new();
|
||||
public bool IsDownloading { get; private set; } = false;
|
||||
public bool IsUploading { get; private set; } = false;
|
||||
public int TotalTransfersPending { get; set; } = 0;
|
||||
public bool UseCustomService
|
||||
{
|
||||
get => pluginConfiguration.UseCustomService;
|
||||
get => _pluginConfiguration.UseCustomService;
|
||||
set
|
||||
{
|
||||
pluginConfiguration.UseCustomService = value;
|
||||
pluginConfiguration.Save();
|
||||
_pluginConfiguration.UseCustomService = value;
|
||||
_pluginConfiguration.Save();
|
||||
}
|
||||
}
|
||||
private string ApiUri => UseCustomService ? pluginConfiguration.ApiUri : MainService;
|
||||
private string ApiUri => UseCustomService ? _pluginConfiguration.ApiUri : MainServiceUri;
|
||||
|
||||
public bool ServerAlive =>
|
||||
(_heartbeatHub?.State ?? HubConnectionState.Disconnected) == HubConnectionState.Connected;
|
||||
@@ -76,7 +83,7 @@ namespace MareSynchronos.WebAPI
|
||||
|
||||
public ApiController(Configuration pluginConfiguration)
|
||||
{
|
||||
this.pluginConfiguration = pluginConfiguration;
|
||||
this._pluginConfiguration = pluginConfiguration;
|
||||
cts = new CancellationTokenSource();
|
||||
|
||||
_ = Heartbeat();
|
||||
@@ -294,8 +301,8 @@ namespace MareSynchronos.WebAPI
|
||||
if (!ServerAlive) return;
|
||||
PluginLog.Debug("Registering at service " + ApiUri);
|
||||
var response = await _userHub!.InvokeAsync<string>("Register");
|
||||
pluginConfiguration.ClientSecret[ApiUri] = response;
|
||||
pluginConfiguration.Save();
|
||||
_pluginConfiguration.ClientSecret[ApiUri] = response;
|
||||
_pluginConfiguration.Save();
|
||||
RestartHeartbeat();
|
||||
}
|
||||
|
||||
@@ -317,7 +324,7 @@ namespace MareSynchronos.WebAPI
|
||||
CancelUpload();
|
||||
uploadCancellationTokenSource = new CancellationTokenSource();
|
||||
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);
|
||||
|
||||
@@ -344,7 +351,7 @@ namespace MareSynchronos.WebAPI
|
||||
}
|
||||
PluginLog.Debug("Upload tasks complete, waiting for server to confirm");
|
||||
var anyUploadsOpen = await _fileHub!.InvokeAsync<bool>("IsUploadFinished", uploadToken);
|
||||
PluginLog.Warning("Uploads open: " + anyUploadsOpen);
|
||||
PluginLog.Debug("Uploads open: " + anyUploadsOpen);
|
||||
while (anyUploadsOpen && !uploadToken.IsCancellationRequested)
|
||||
{
|
||||
anyUploadsOpen = await _fileHub!.InvokeAsync<bool>("IsUploadFinished", uploadToken);
|
||||
@@ -357,7 +364,7 @@ namespace MareSynchronos.WebAPI
|
||||
|
||||
if (!uploadToken.IsCancellationRequested)
|
||||
{
|
||||
PluginLog.Warning("=== Pushing character data ===");
|
||||
PluginLog.Debug("=== Pushing character data ===");
|
||||
await _userHub!.InvokeAsync("PushCharacterData", character, visibleCharacterIds, uploadToken);
|
||||
}
|
||||
else
|
||||
@@ -372,11 +379,7 @@ namespace MareSynchronos.WebAPI
|
||||
public Task ReceiveCharacterData(CharacterCacheDto character, string characterHash)
|
||||
{
|
||||
PluginLog.Debug("Received DTO for " + characterHash);
|
||||
CharacterReceived?.Invoke(null, new CharacterReceivedEventArgs()
|
||||
{
|
||||
CharacterData = character,
|
||||
CharacterNameHash = characterHash
|
||||
});
|
||||
CharacterReceived?.Invoke(null, new CharacterReceivedEventArgs(characterHash, character));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user