rough impl of FSW, goodbye periodic filescan
This commit is contained in:
@@ -2,12 +2,13 @@
|
|||||||
using MareSynchronos.MareConfiguration;
|
using MareSynchronos.MareConfiguration;
|
||||||
using MareSynchronos.Services;
|
using MareSynchronos.Services;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
|
using MareSynchronos.Utils;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace MareSynchronos.FileCache;
|
namespace MareSynchronos.FileCache;
|
||||||
|
|
||||||
public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase
|
public sealed class CacheMonitor : DisposableMediatorSubscriberBase
|
||||||
{
|
{
|
||||||
private readonly MareConfigService _configService;
|
private readonly MareConfigService _configService;
|
||||||
private readonly DalamudUtilService _dalamudUtil;
|
private readonly DalamudUtilService _dalamudUtil;
|
||||||
@@ -16,11 +17,10 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase
|
|||||||
private readonly IpcManager _ipcManager;
|
private readonly IpcManager _ipcManager;
|
||||||
private readonly PerformanceCollectorService _performanceCollector;
|
private readonly PerformanceCollectorService _performanceCollector;
|
||||||
private long _currentFileProgress = 0;
|
private long _currentFileProgress = 0;
|
||||||
private bool _fileScanWasRunning = false;
|
|
||||||
private CancellationTokenSource _scanCancellationTokenSource = new();
|
private CancellationTokenSource _scanCancellationTokenSource = new();
|
||||||
private TimeSpan _timeUntilNextScan = TimeSpan.Zero;
|
private readonly string[] _allowedExtensions = [".mdl", ".tex", ".mtrl", ".tmb", ".pap", ".avfx", ".atex", ".sklb", ".eid", ".phyb", ".pbd", ".scd", ".skp", ".shpk"];
|
||||||
|
|
||||||
public PeriodicFileScanner(ILogger<PeriodicFileScanner> logger, IpcManager ipcManager, MareConfigService configService,
|
public CacheMonitor(ILogger<CacheMonitor> logger, IpcManager ipcManager, MareConfigService configService,
|
||||||
FileCacheManager fileDbManager, MareMediator mediator, PerformanceCollectorService performanceCollector, DalamudUtilService dalamudUtil,
|
FileCacheManager fileDbManager, MareMediator mediator, PerformanceCollectorService performanceCollector, DalamudUtilService dalamudUtil,
|
||||||
FileCompactor fileCompactor) : base(logger, mediator)
|
FileCompactor fileCompactor) : base(logger, mediator)
|
||||||
{
|
{
|
||||||
@@ -30,38 +30,287 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase
|
|||||||
_performanceCollector = performanceCollector;
|
_performanceCollector = performanceCollector;
|
||||||
_dalamudUtil = dalamudUtil;
|
_dalamudUtil = dalamudUtil;
|
||||||
_fileCompactor = fileCompactor;
|
_fileCompactor = fileCompactor;
|
||||||
Mediator.Subscribe<PenumbraInitializedMessage>(this, (_) => StartScan());
|
Mediator.Subscribe<PenumbraInitializedMessage>(this, (_) =>
|
||||||
|
{
|
||||||
|
StartPenumbraWatcher(_ipcManager.PenumbraModDirectory);
|
||||||
|
StartMareWatcher(configService.Current.CacheFolder);
|
||||||
|
InvokeScan();
|
||||||
|
});
|
||||||
Mediator.Subscribe<HaltScanMessage>(this, (msg) => HaltScan(msg.Source));
|
Mediator.Subscribe<HaltScanMessage>(this, (msg) => HaltScan(msg.Source));
|
||||||
Mediator.Subscribe<ResumeScanMessage>(this, (msg) => ResumeScan(msg.Source));
|
Mediator.Subscribe<ResumeScanMessage>(this, (msg) => ResumeScan(msg.Source));
|
||||||
Mediator.Subscribe<SwitchToMainUiMessage>(this, (_) => StartScan());
|
Mediator.Subscribe<DalamudLoginMessage>(this, (_) =>
|
||||||
Mediator.Subscribe<DalamudLoginMessage>(this, (_) => StartScan());
|
{
|
||||||
|
StartMareWatcher(configService.Current.CacheFolder);
|
||||||
|
StartPenumbraWatcher(_ipcManager.PenumbraModDirectory);
|
||||||
|
InvokeScan();
|
||||||
|
});
|
||||||
|
Mediator.Subscribe<PenumbraDirectoryChangedMessage>(this, (msg) => StartPenumbraWatcher(msg.ModDirectory));
|
||||||
|
if (_ipcManager.CheckPenumbraApi() && !string.IsNullOrEmpty(_ipcManager.PenumbraModDirectory))
|
||||||
|
StartPenumbraWatcher(_ipcManager.PenumbraModDirectory);
|
||||||
|
if (configService.Current.HasValidSetup())
|
||||||
|
{
|
||||||
|
StartMareWatcher(configService.Current.CacheFolder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long CurrentFileProgress => _currentFileProgress;
|
public long CurrentFileProgress => _currentFileProgress;
|
||||||
public long FileCacheSize { get; set; }
|
public long FileCacheSize { get; set; }
|
||||||
public ConcurrentDictionary<string, int> HaltScanLocks { get; set; } = new(StringComparer.Ordinal);
|
public ConcurrentDictionary<string, int> HaltScanLocks { get; set; } = new(StringComparer.Ordinal);
|
||||||
public bool IsScanRunning => CurrentFileProgress > 0 || TotalFiles > 0;
|
public bool IsScanRunning => CurrentFileProgress > 0 || TotalFiles > 0;
|
||||||
public string TimeUntilNextScan => _timeUntilNextScan.ToString(@"mm\:ss");
|
|
||||||
public long TotalFiles { get; private set; }
|
public long TotalFiles { get; private set; }
|
||||||
public long TotalFilesStorage { get; private set; }
|
public long TotalFilesStorage { get; private set; }
|
||||||
private int TimeBetweenScans => _configService.Current.TimeSpanBetweenScansInSeconds;
|
|
||||||
|
|
||||||
public void HaltScan(string source)
|
public void HaltScan(string source)
|
||||||
{
|
{
|
||||||
if (!HaltScanLocks.ContainsKey(source)) HaltScanLocks[source] = 0;
|
if (!HaltScanLocks.ContainsKey(source)) HaltScanLocks[source] = 0;
|
||||||
HaltScanLocks[source]++;
|
HaltScanLocks[source]++;
|
||||||
|
}
|
||||||
|
|
||||||
if (IsScanRunning && HaltScanLocks.Any(f => f.Value > 0))
|
record WatcherChange(WatcherChangeTypes ChangeType, string? OldPath = null);
|
||||||
|
private readonly Dictionary<string, WatcherChange> _watcherChanges = new Dictionary<string, WatcherChange>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
private readonly Dictionary<string, WatcherChange> _mareChanges = new Dictionary<string, WatcherChange>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
public void StopMonitoring()
|
||||||
|
{
|
||||||
|
Logger.LogInformation("Stopping monitoring of Penumbra and Mare storage folders");
|
||||||
|
MareWatcher?.Dispose();
|
||||||
|
PenumbraWatcher?.Dispose();
|
||||||
|
MareWatcher = null;
|
||||||
|
PenumbraWatcher = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartMareWatcher(string? marePath)
|
||||||
|
{
|
||||||
|
MareWatcher?.Dispose();
|
||||||
|
if (string.IsNullOrEmpty(marePath))
|
||||||
{
|
{
|
||||||
_scanCancellationTokenSource?.Cancel();
|
MareWatcher = null;
|
||||||
_fileScanWasRunning = true;
|
Logger.LogWarning("Mare file path is not set, cannot start the FSW for Mare.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecalculateFileCacheSize();
|
||||||
|
|
||||||
|
Logger.LogDebug("Initializing Mare FSW on {path}", marePath);
|
||||||
|
MareWatcher = new()
|
||||||
|
{
|
||||||
|
Path = marePath,
|
||||||
|
InternalBufferSize = 8388608,
|
||||||
|
NotifyFilter = NotifyFilters.CreationTime
|
||||||
|
| NotifyFilters.LastWrite
|
||||||
|
| NotifyFilters.FileName
|
||||||
|
| NotifyFilters.DirectoryName
|
||||||
|
| NotifyFilters.Size,
|
||||||
|
Filter = "*.*",
|
||||||
|
IncludeSubdirectories = false
|
||||||
|
};
|
||||||
|
|
||||||
|
MareWatcher.Deleted += MareWatcher_FileChanged;
|
||||||
|
MareWatcher.Created += MareWatcher_FileChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MareWatcher_FileChanged(object sender, FileSystemEventArgs e)
|
||||||
|
{
|
||||||
|
if (!_allowedExtensions.Any(ext => e.FullPath.EndsWith(ext, StringComparison.OrdinalIgnoreCase))) return;
|
||||||
|
|
||||||
|
lock (_watcherChanges)
|
||||||
|
{
|
||||||
|
_mareChanges[e.FullPath] = new(e.ChangeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = MareWatcherExecution();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartPenumbraWatcher(string? penumbraPath)
|
||||||
|
{
|
||||||
|
PenumbraWatcher?.Dispose();
|
||||||
|
if (string.IsNullOrEmpty(penumbraPath))
|
||||||
|
{
|
||||||
|
PenumbraWatcher = null;
|
||||||
|
Logger.LogWarning("Penumbra is not connected or the path is not set, cannot start FSW for Penumbra.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.LogDebug("Initializing Penumbra FSW on {path}", penumbraPath);
|
||||||
|
PenumbraWatcher = new()
|
||||||
|
{
|
||||||
|
Path = penumbraPath,
|
||||||
|
InternalBufferSize = 8388608,
|
||||||
|
NotifyFilter = NotifyFilters.CreationTime
|
||||||
|
| NotifyFilters.LastWrite
|
||||||
|
| NotifyFilters.FileName
|
||||||
|
| NotifyFilters.DirectoryName
|
||||||
|
| NotifyFilters.Size,
|
||||||
|
Filter = "*.*",
|
||||||
|
IncludeSubdirectories = true
|
||||||
|
};
|
||||||
|
|
||||||
|
PenumbraWatcher.Deleted += Fs_Changed;
|
||||||
|
PenumbraWatcher.Created += Fs_Changed;
|
||||||
|
PenumbraWatcher.Changed += Fs_Changed;
|
||||||
|
PenumbraWatcher.Renamed += Fs_Renamed;
|
||||||
|
PenumbraWatcher.EnableRaisingEvents = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Fs_Changed(object sender, FileSystemEventArgs e)
|
||||||
|
{
|
||||||
|
if (Directory.Exists(e.FullPath)) return;
|
||||||
|
if (!_allowedExtensions.Any(ext => e.FullPath.EndsWith(ext, StringComparison.OrdinalIgnoreCase))) return;
|
||||||
|
|
||||||
|
if (e.ChangeType is not (WatcherChangeTypes.Changed or WatcherChangeTypes.Deleted or WatcherChangeTypes.Created))
|
||||||
|
return;
|
||||||
|
|
||||||
|
lock (_watcherChanges)
|
||||||
|
{
|
||||||
|
_watcherChanges[e.FullPath] = new(e.ChangeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.LogTrace("FSW {event}: {path}", e.ChangeType, e.FullPath);
|
||||||
|
|
||||||
|
_ = PenumbraWatcherExecution();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Fs_Renamed(object sender, RenamedEventArgs e)
|
||||||
|
{
|
||||||
|
if (Directory.Exists(e.FullPath))
|
||||||
|
{
|
||||||
|
var directoryFiles = Directory.GetFiles(e.FullPath, "*.*", SearchOption.AllDirectories);
|
||||||
|
lock (_watcherChanges)
|
||||||
|
{
|
||||||
|
foreach (var file in directoryFiles)
|
||||||
|
{
|
||||||
|
if (!_allowedExtensions.Any(ext => file.EndsWith(ext, StringComparison.OrdinalIgnoreCase))) continue;
|
||||||
|
var oldPath = file.Replace(e.FullPath, e.OldFullPath, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
_watcherChanges.Remove(oldPath);
|
||||||
|
_watcherChanges[file] = new(WatcherChangeTypes.Renamed, oldPath);
|
||||||
|
Logger.LogTrace("FSW Renamed: {path} -> {new}", oldPath, file);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!_allowedExtensions.Any(ext => e.FullPath.EndsWith(ext, StringComparison.OrdinalIgnoreCase))) return;
|
||||||
|
|
||||||
|
lock (_watcherChanges)
|
||||||
|
{
|
||||||
|
_watcherChanges.Remove(e.OldFullPath);
|
||||||
|
_watcherChanges[e.FullPath] = new(WatcherChangeTypes.Renamed, e.OldFullPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.LogTrace("FSW Renamed: {path} -> {new}", e.OldFullPath, e.FullPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = PenumbraWatcherExecution();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CancellationTokenSource _penumbraFswCts = new();
|
||||||
|
private CancellationTokenSource _mareFswCts = new();
|
||||||
|
public FileSystemWatcher? PenumbraWatcher { get; private set; }
|
||||||
|
public FileSystemWatcher? MareWatcher { get; private set; }
|
||||||
|
|
||||||
|
private async Task MareWatcherExecution()
|
||||||
|
{
|
||||||
|
_mareFswCts = _mareFswCts.CancelRecreate();
|
||||||
|
var token = _mareFswCts.Token;
|
||||||
|
var delay = TimeSpan.FromSeconds(5);
|
||||||
|
Dictionary<string, WatcherChange> changes;
|
||||||
|
lock (_mareChanges)
|
||||||
|
changes = _mareChanges.ToDictionary(t => t.Key, t => t.Value, StringComparer.Ordinal);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
await Task.Delay(delay, token).ConfigureAwait(false);
|
||||||
|
} while (HaltScanLocks.Any(f => f.Value > 0));
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_mareChanges)
|
||||||
|
{
|
||||||
|
foreach (var key in changes.Keys)
|
||||||
|
{
|
||||||
|
_mareChanges.Remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = RecalculateFileCacheSize();
|
||||||
|
|
||||||
|
if (changes.Any(c => c.Value.ChangeType == WatcherChangeTypes.Deleted))
|
||||||
|
{
|
||||||
|
var threadCount = Math.Clamp((int)(Environment.ProcessorCount / 2.0f), 2, 8);
|
||||||
|
|
||||||
|
Parallel.ForEach(changes, new ParallelOptions()
|
||||||
|
{
|
||||||
|
MaxDegreeOfParallelism = threadCount,
|
||||||
|
},
|
||||||
|
(change) =>
|
||||||
|
{
|
||||||
|
Logger.LogDebug("FSW Change: {change} = {val}", change.Key, change.Value);
|
||||||
|
_ = _fileDbManager.GetFileCacheByPath(change.Key);
|
||||||
|
});
|
||||||
|
|
||||||
|
_fileDbManager.WriteOutFullCsv();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InvokeScan(bool forced = false)
|
private async Task PenumbraWatcherExecution()
|
||||||
|
{
|
||||||
|
_penumbraFswCts = _penumbraFswCts.CancelRecreate();
|
||||||
|
var token = _penumbraFswCts.Token;
|
||||||
|
Dictionary<string, WatcherChange> changes;
|
||||||
|
lock (_watcherChanges)
|
||||||
|
changes = _watcherChanges.ToDictionary(t => t.Key, t => t.Value, StringComparer.Ordinal);
|
||||||
|
var delay = TimeSpan.FromSeconds(10);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
await Task.Delay(delay, token).ConfigureAwait(false);
|
||||||
|
} while (HaltScanLocks.Any(f => f.Value > 0));
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_watcherChanges)
|
||||||
|
{
|
||||||
|
foreach (var key in changes.Keys)
|
||||||
|
{
|
||||||
|
_watcherChanges.Remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var threadCount = Math.Clamp((int)(Environment.ProcessorCount / 2.0f), 2, 8);
|
||||||
|
|
||||||
|
Parallel.ForEach(changes, new ParallelOptions()
|
||||||
|
{
|
||||||
|
MaxDegreeOfParallelism = threadCount,
|
||||||
|
},
|
||||||
|
(change) =>
|
||||||
|
{
|
||||||
|
Logger.LogDebug("FSW Change: {change} = {val}", change.Key, change.Value);
|
||||||
|
if (change.Value.ChangeType == WatcherChangeTypes.Deleted)
|
||||||
|
{
|
||||||
|
_fileDbManager.GetFileCacheByPath(change.Key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (change.Value.OldPath != null) _fileDbManager.GetFileCacheByPath(change.Value.OldPath);
|
||||||
|
_fileDbManager.CreateFileEntry(change.Key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_fileDbManager.WriteOutFullCsv();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InvokeScan()
|
||||||
{
|
{
|
||||||
bool isForced = forced;
|
|
||||||
bool isForcedFromExternal = forced;
|
|
||||||
TotalFiles = 0;
|
TotalFiles = 0;
|
||||||
_currentFileProgress = 0;
|
_currentFileProgress = 0;
|
||||||
_scanCancellationTokenSource?.Cancel();
|
_scanCancellationTokenSource?.Cancel();
|
||||||
@@ -69,56 +318,36 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase
|
|||||||
var token = _scanCancellationTokenSource.Token;
|
var token = _scanCancellationTokenSource.Token;
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
while (!token.IsCancellationRequested)
|
TotalFiles = 0;
|
||||||
|
_currentFileProgress = 0;
|
||||||
|
while (_dalamudUtil.IsOnFrameworkThread)
|
||||||
{
|
{
|
||||||
while (HaltScanLocks.Any(f => f.Value > 0) || !_ipcManager.CheckPenumbraApi() || _dalamudUtil.IsOnFrameworkThread)
|
Logger.LogWarning("Scanner is on framework, waiting for leaving thread before continuing");
|
||||||
{
|
await Task.Delay(250, token).ConfigureAwait(false);
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
isForced |= RecalculateFileCacheSize();
|
|
||||||
if (!_configService.Current.FileScanPaused || isForced)
|
|
||||||
{
|
|
||||||
isForced = false;
|
|
||||||
TotalFiles = 0;
|
|
||||||
_currentFileProgress = 0;
|
|
||||||
while (_dalamudUtil.IsOnFrameworkThread)
|
|
||||||
{
|
|
||||||
Logger.LogWarning("Scanner is on framework, waiting for leaving thread before continuing");
|
|
||||||
await Task.Delay(250, token).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread scanThread = new(() =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_performanceCollector.LogPerformance(this, "PeriodicFileScan", () => PeriodicFileScan(isForcedFromExternal, token));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogError(ex, "Error during Periodic File Scan");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
{
|
|
||||||
Priority = ThreadPriority.Lowest,
|
|
||||||
IsBackground = true
|
|
||||||
};
|
|
||||||
scanThread.Start();
|
|
||||||
while (scanThread.IsAlive)
|
|
||||||
{
|
|
||||||
await Task.Delay(250).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
if (isForcedFromExternal) isForcedFromExternal = false;
|
|
||||||
TotalFiles = 0;
|
|
||||||
_currentFileProgress = 0;
|
|
||||||
}
|
|
||||||
_timeUntilNextScan = TimeSpan.FromSeconds(TimeBetweenScans);
|
|
||||||
while (_timeUntilNextScan.TotalSeconds >= 0 || _dalamudUtil.IsOnFrameworkThread)
|
|
||||||
{
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1), token).ConfigureAwait(false);
|
|
||||||
_timeUntilNextScan -= TimeSpan.FromSeconds(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Thread scanThread = new(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_performanceCollector.LogPerformance(this, "FullFileScan", () => FullFileScan(token));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex, "Error during Full File Scan");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
{
|
||||||
|
Priority = ThreadPriority.Lowest,
|
||||||
|
IsBackground = true
|
||||||
|
};
|
||||||
|
scanThread.Start();
|
||||||
|
while (scanThread.IsAlive)
|
||||||
|
{
|
||||||
|
await Task.Delay(250).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
TotalFiles = 0;
|
||||||
|
_currentFileProgress = 0;
|
||||||
}, token);
|
}, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,28 +394,19 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
HaltScanLocks[source]--;
|
HaltScanLocks[source]--;
|
||||||
if (HaltScanLocks[source] < 0) HaltScanLocks[source] = 0;
|
if (HaltScanLocks[source] < 0) HaltScanLocks[source] = 0;
|
||||||
|
|
||||||
if (_fileScanWasRunning && HaltScanLocks.All(f => f.Value == 0))
|
|
||||||
{
|
|
||||||
_fileScanWasRunning = false;
|
|
||||||
InvokeScan(forced: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StartScan()
|
|
||||||
{
|
|
||||||
if (!_ipcManager.Initialized || !_configService.Current.HasValidSetup()) return;
|
|
||||||
Logger.LogTrace("Penumbra is active, configuration is valid, scan");
|
|
||||||
InvokeScan(forced: true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
_scanCancellationTokenSource?.Cancel();
|
_scanCancellationTokenSource?.Cancel();
|
||||||
|
PenumbraWatcher?.Dispose();
|
||||||
|
MareWatcher?.Dispose();
|
||||||
|
_penumbraFswCts?.CancelDispose();
|
||||||
|
_mareFswCts?.CancelDispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PeriodicFileScan(bool noWaiting, CancellationToken ct)
|
private void FullFileScan(CancellationToken ct)
|
||||||
{
|
{
|
||||||
TotalFiles = 1;
|
TotalFiles = 1;
|
||||||
var penumbraDir = _ipcManager.PenumbraModDirectory;
|
var penumbraDir = _ipcManager.PenumbraModDirectory;
|
||||||
@@ -210,7 +430,6 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase
|
|||||||
var previousThreadPriority = Thread.CurrentThread.Priority;
|
var previousThreadPriority = Thread.CurrentThread.Priority;
|
||||||
Thread.CurrentThread.Priority = ThreadPriority.Lowest;
|
Thread.CurrentThread.Priority = ThreadPriority.Lowest;
|
||||||
Logger.LogDebug("Getting files from {penumbra} and {storage}", penumbraDir, _configService.Current.CacheFolder);
|
Logger.LogDebug("Getting files from {penumbra} and {storage}", penumbraDir, _configService.Current.CacheFolder);
|
||||||
string[] ext = [".mdl", ".tex", ".mtrl", ".tmb", ".pap", ".avfx", ".atex", ".sklb", ".eid", ".phyb", ".pbd", ".scd", ".skp", ".shpk"];
|
|
||||||
|
|
||||||
Dictionary<string, string[]> penumbraFiles = new(StringComparer.Ordinal);
|
Dictionary<string, string[]> penumbraFiles = new(StringComparer.Ordinal);
|
||||||
foreach (var folder in Directory.EnumerateDirectories(penumbraDir!))
|
foreach (var folder in Directory.EnumerateDirectories(penumbraDir!))
|
||||||
@@ -221,7 +440,7 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase
|
|||||||
[
|
[
|
||||||
.. Directory.GetFiles(folder, "*.*", SearchOption.AllDirectories)
|
.. Directory.GetFiles(folder, "*.*", SearchOption.AllDirectories)
|
||||||
.AsParallel()
|
.AsParallel()
|
||||||
.Where(f => ext.Any(e => f.EndsWith(e, StringComparison.OrdinalIgnoreCase))
|
.Where(f => _allowedExtensions.Any(e => f.EndsWith(e, StringComparison.OrdinalIgnoreCase))
|
||||||
&& !f.Contains(@"\bg\", StringComparison.OrdinalIgnoreCase)
|
&& !f.Contains(@"\bg\", StringComparison.OrdinalIgnoreCase)
|
||||||
&& !f.Contains(@"\bgcommon\", StringComparison.OrdinalIgnoreCase)
|
&& !f.Contains(@"\bgcommon\", StringComparison.OrdinalIgnoreCase)
|
||||||
&& !f.Contains(@"\ui\", StringComparison.OrdinalIgnoreCase)),
|
&& !f.Contains(@"\ui\", StringComparison.OrdinalIgnoreCase)),
|
||||||
@@ -309,7 +528,6 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase
|
|||||||
Logger.LogWarning(ex, "Failed validating {path}", workload.ResolvedFilepath);
|
Logger.LogWarning(ex, "Failed validating {path}", workload.ResolvedFilepath);
|
||||||
}
|
}
|
||||||
Interlocked.Increment(ref _currentFileProgress);
|
Interlocked.Increment(ref _currentFileProgress);
|
||||||
if (!noWaiting) Thread.Sleep(5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogTrace("Ending Worker Thread {i}", threadNr);
|
Logger.LogTrace("Ending Worker Thread {i}", threadNr);
|
||||||
@@ -390,7 +608,6 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
Interlocked.Increment(ref _currentFileProgress);
|
Interlocked.Increment(ref _currentFileProgress);
|
||||||
if (!noWaiting) Thread.Sleep(5);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Logger.LogTrace("Scanner added {notScanned} new files to db", allScannedFiles.Count(c => !c.Value));
|
Logger.LogTrace("Scanner added {notScanned} new files to db", allScannedFiles.Count(c => !c.Value));
|
||||||
@@ -406,6 +623,8 @@ public sealed class PeriodicFileScanner : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
_configService.Current.InitialScanComplete = true;
|
_configService.Current.InitialScanComplete = true;
|
||||||
_configService.Save();
|
_configService.Save();
|
||||||
|
StartMareWatcher(_configService.Current.CacheFolder);
|
||||||
|
StartPenumbraWatcher(penumbraDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ using MareSynchronos.Interop;
|
|||||||
using MareSynchronos.MareConfiguration;
|
using MareSynchronos.MareConfiguration;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@@ -10,7 +11,7 @@ using System.Text;
|
|||||||
|
|
||||||
namespace MareSynchronos.FileCache;
|
namespace MareSynchronos.FileCache;
|
||||||
|
|
||||||
public sealed class FileCacheManager : IDisposable
|
public sealed class FileCacheManager : IHostedService
|
||||||
{
|
{
|
||||||
public const string CachePrefix = "{cache}";
|
public const string CachePrefix = "{cache}";
|
||||||
public const string CsvSplit = "|";
|
public const string CsvSplit = "|";
|
||||||
@@ -30,101 +31,6 @@ public sealed class FileCacheManager : IDisposable
|
|||||||
_configService = configService;
|
_configService = configService;
|
||||||
_mareMediator = mareMediator;
|
_mareMediator = mareMediator;
|
||||||
_csvPath = Path.Combine(configService.ConfigurationDirectory, "FileCache.csv");
|
_csvPath = Path.Combine(configService.ConfigurationDirectory, "FileCache.csv");
|
||||||
|
|
||||||
lock (_fileWriteLock)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (File.Exists(CsvBakPath))
|
|
||||||
{
|
|
||||||
File.Move(CsvBakPath, _csvPath, overwrite: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "Failed to move BAK to ORG, deleting BAK");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (File.Exists(CsvBakPath))
|
|
||||||
File.Delete(CsvBakPath);
|
|
||||||
}
|
|
||||||
catch (Exception ex1)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex1, "Could not delete bak file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (File.Exists(_csvPath))
|
|
||||||
{
|
|
||||||
bool success = false;
|
|
||||||
string[] entries = [];
|
|
||||||
int attempts = 0;
|
|
||||||
while (!success && attempts < 10)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
entries = File.ReadAllLines(_csvPath);
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
attempts++;
|
|
||||||
_logger.LogWarning(ex, "Could not open {file}, trying again", _csvPath);
|
|
||||||
Thread.Sleep(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!entries.Any())
|
|
||||||
{
|
|
||||||
_logger.LogWarning("Could not load entries from {path}, continuing with empty file cache", _csvPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
Dictionary<string, bool> processedFiles = new(StringComparer.OrdinalIgnoreCase);
|
|
||||||
foreach (var entry in entries)
|
|
||||||
{
|
|
||||||
var splittedEntry = entry.Split(CsvSplit, StringSplitOptions.None);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var hash = splittedEntry[0];
|
|
||||||
if (hash.Length != 40) throw new InvalidOperationException("Expected Hash length of 40, received " + hash.Length);
|
|
||||||
var path = splittedEntry[1];
|
|
||||||
var time = splittedEntry[2];
|
|
||||||
|
|
||||||
if (processedFiles.ContainsKey(path))
|
|
||||||
{
|
|
||||||
_logger.LogWarning("Already processed {file}, ignoring", path);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
processedFiles.Add(path, value: true);
|
|
||||||
|
|
||||||
long size = -1;
|
|
||||||
long compressed = -1;
|
|
||||||
if (splittedEntry.Length > 3)
|
|
||||||
{
|
|
||||||
if (long.TryParse(splittedEntry[3], CultureInfo.InvariantCulture, out long result))
|
|
||||||
{
|
|
||||||
size = result;
|
|
||||||
}
|
|
||||||
if (long.TryParse(splittedEntry[4], CultureInfo.InvariantCulture, out long resultCompressed))
|
|
||||||
{
|
|
||||||
compressed = resultCompressed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AddHashedFile(ReplacePathPrefixes(new FileCacheEntity(hash, path, time, size, compressed)));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "Failed to initialize entry {entry}, ignoring", entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (processedFiles.Count != entries.Length)
|
|
||||||
{
|
|
||||||
WriteOutFullCsv();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CsvBakPath => _csvPath + ".bak";
|
private string CsvBakPath => _csvPath + ".bak";
|
||||||
@@ -151,13 +57,6 @@ public sealed class FileCacheManager : IDisposable
|
|||||||
return CreateFileCacheEntity(fi, prefixedPath);
|
return CreateFileCacheEntity(fi, prefixedPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_logger.LogTrace("Disposing {type}", GetType());
|
|
||||||
WriteOutFullCsv();
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<FileCacheEntity> GetAllFileCaches() => _fileCaches.Values.SelectMany(v => v).ToList();
|
public List<FileCacheEntity> GetAllFileCaches() => _fileCaches.Values.SelectMany(v => v).ToList();
|
||||||
|
|
||||||
public List<FileCacheEntity> GetAllFileCachesByHash(string hash)
|
public List<FileCacheEntity> GetAllFileCachesByHash(string hash)
|
||||||
@@ -346,13 +245,14 @@ public sealed class FileCacheManager : IDisposable
|
|||||||
|
|
||||||
public void WriteOutFullCsv()
|
public void WriteOutFullCsv()
|
||||||
{
|
{
|
||||||
StringBuilder sb = new();
|
|
||||||
foreach (var entry in _fileCaches.SelectMany(k => k.Value).OrderBy(f => f.PrefixedFilePath, StringComparer.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
sb.AppendLine(entry.CsvEntry);
|
|
||||||
}
|
|
||||||
lock (_fileWriteLock)
|
lock (_fileWriteLock)
|
||||||
{
|
{
|
||||||
|
StringBuilder sb = new();
|
||||||
|
foreach (var entry in _fileCaches.SelectMany(k => k.Value).OrderBy(f => f.PrefixedFilePath, StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
sb.AppendLine(entry.CsvEntry);
|
||||||
|
}
|
||||||
|
|
||||||
if (File.Exists(_csvPath))
|
if (File.Exists(_csvPath))
|
||||||
{
|
{
|
||||||
File.Copy(_csvPath, CsvBakPath, overwrite: true);
|
File.Copy(_csvPath, CsvBakPath, overwrite: true);
|
||||||
@@ -458,4 +358,110 @@ public sealed class FileCacheManager : IDisposable
|
|||||||
|
|
||||||
return fileCache;
|
return fileCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
lock (_fileWriteLock)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(CsvBakPath))
|
||||||
|
{
|
||||||
|
File.Move(CsvBakPath, _csvPath, overwrite: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Failed to move BAK to ORG, deleting BAK");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(CsvBakPath))
|
||||||
|
File.Delete(CsvBakPath);
|
||||||
|
}
|
||||||
|
catch (Exception ex1)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex1, "Could not delete bak file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.Exists(_csvPath))
|
||||||
|
{
|
||||||
|
bool success = false;
|
||||||
|
string[] entries = [];
|
||||||
|
int attempts = 0;
|
||||||
|
while (!success && attempts < 10)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
entries = File.ReadAllLines(_csvPath);
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
attempts++;
|
||||||
|
_logger.LogWarning(ex, "Could not open {file}, trying again", _csvPath);
|
||||||
|
Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entries.Any())
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Could not load entries from {path}, continuing with empty file cache", _csvPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<string, bool> processedFiles = new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var entry in entries)
|
||||||
|
{
|
||||||
|
var splittedEntry = entry.Split(CsvSplit, StringSplitOptions.None);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var hash = splittedEntry[0];
|
||||||
|
if (hash.Length != 40) throw new InvalidOperationException("Expected Hash length of 40, received " + hash.Length);
|
||||||
|
var path = splittedEntry[1];
|
||||||
|
var time = splittedEntry[2];
|
||||||
|
|
||||||
|
if (processedFiles.ContainsKey(path))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Already processed {file}, ignoring", path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
processedFiles.Add(path, value: true);
|
||||||
|
|
||||||
|
long size = -1;
|
||||||
|
long compressed = -1;
|
||||||
|
if (splittedEntry.Length > 3)
|
||||||
|
{
|
||||||
|
if (long.TryParse(splittedEntry[3], CultureInfo.InvariantCulture, out long result))
|
||||||
|
{
|
||||||
|
size = result;
|
||||||
|
}
|
||||||
|
if (long.TryParse(splittedEntry[4], CultureInfo.InvariantCulture, out long resultCompressed))
|
||||||
|
{
|
||||||
|
compressed = resultCompressed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AddHashedFile(ReplacePathPrefixes(new FileCacheEntity(hash, path, time, size, compressed)));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Failed to initialize entry {entry}, ignoring", entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (processedFiles.Count != entries.Length)
|
||||||
|
{
|
||||||
|
WriteOutFullCsv();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
WriteOutFullCsv();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -170,7 +170,19 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool Initialized => CheckPenumbraApiInternal() && CheckGlamourerApiInternal();
|
public bool Initialized => CheckPenumbraApiInternal() && CheckGlamourerApiInternal();
|
||||||
public string? PenumbraModDirectory { get; private set; }
|
private string? _penumbraModDirectory;
|
||||||
|
public string? PenumbraModDirectory
|
||||||
|
{
|
||||||
|
get => _penumbraModDirectory;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (!string.Equals(_penumbraModDirectory, value, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
_penumbraModDirectory = value;
|
||||||
|
Mediator.Publish(new PenumbraDirectoryChangedMessage(_penumbraModDirectory));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool CheckCustomizePlusApi() => _customizePlusAvailable;
|
public bool CheckCustomizePlusApi() => _customizePlusAvailable;
|
||||||
|
|
||||||
|
|||||||
@@ -142,7 +142,6 @@ public class MarePlugin : MediatorSubscriberBase, IHostedService
|
|||||||
Mediator.Publish(new SwitchToIntroUiMessage());
|
Mediator.Publish(new SwitchToIntroUiMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_runtimeServiceScope.ServiceProvider.GetRequiredService<PeriodicFileScanner>().StartScan();
|
|
||||||
_runtimeServiceScope.ServiceProvider.GetRequiredService<CacheCreationService>();
|
_runtimeServiceScope.ServiceProvider.GetRequiredService<CacheCreationService>();
|
||||||
_runtimeServiceScope.ServiceProvider.GetRequiredService<TransientResourceManager>();
|
_runtimeServiceScope.ServiceProvider.GetRequiredService<TransientResourceManager>();
|
||||||
_runtimeServiceScope.ServiceProvider.GetRequiredService<OnlinePlayerManager>();
|
_runtimeServiceScope.ServiceProvider.GetRequiredService<OnlinePlayerManager>();
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddSingleton<HubFactory>();
|
collection.AddSingleton<HubFactory>();
|
||||||
|
|
||||||
// add scoped services
|
// add scoped services
|
||||||
collection.AddScoped<PeriodicFileScanner>();
|
collection.AddScoped<CacheMonitor>();
|
||||||
collection.AddScoped<UiFactory>();
|
collection.AddScoped<UiFactory>();
|
||||||
collection.AddScoped<WindowMediatorSubscriberBase, SettingsUi>();
|
collection.AddScoped<WindowMediatorSubscriberBase, SettingsUi>();
|
||||||
collection.AddScoped<WindowMediatorSubscriberBase, CompactUi>();
|
collection.AddScoped<WindowMediatorSubscriberBase, CompactUi>();
|
||||||
@@ -134,12 +134,12 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
s.GetRequiredService<UiFactory>(),
|
s.GetRequiredService<UiFactory>(),
|
||||||
s.GetRequiredService<FileDialogManager>(), s.GetRequiredService<MareMediator>()));
|
s.GetRequiredService<FileDialogManager>(), s.GetRequiredService<MareMediator>()));
|
||||||
collection.AddScoped((s) => new CommandManagerService(commandManager, s.GetRequiredService<PerformanceCollectorService>(),
|
collection.AddScoped((s) => new CommandManagerService(commandManager, s.GetRequiredService<PerformanceCollectorService>(),
|
||||||
s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<PeriodicFileScanner>(), s.GetRequiredService<ChatService>(),
|
s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<CacheMonitor>(), s.GetRequiredService<ChatService>(),
|
||||||
s.GetRequiredService<ApiController>(), s.GetRequiredService<MareMediator>(), s.GetRequiredService<MareConfigService>()));
|
s.GetRequiredService<ApiController>(), s.GetRequiredService<MareMediator>(), s.GetRequiredService<MareConfigService>()));
|
||||||
collection.AddScoped((s) => new NotificationService(s.GetRequiredService<ILogger<NotificationService>>(),
|
collection.AddScoped((s) => new NotificationService(s.GetRequiredService<ILogger<NotificationService>>(),
|
||||||
s.GetRequiredService<MareMediator>(), notificationManager, chatGui, s.GetRequiredService<MareConfigService>()));
|
s.GetRequiredService<MareMediator>(), notificationManager, chatGui, s.GetRequiredService<MareConfigService>()));
|
||||||
collection.AddScoped((s) => new UiSharedService(s.GetRequiredService<ILogger<UiSharedService>>(), s.GetRequiredService<IpcManager>(), s.GetRequiredService<ApiController>(),
|
collection.AddScoped((s) => new UiSharedService(s.GetRequiredService<ILogger<UiSharedService>>(), s.GetRequiredService<IpcManager>(), s.GetRequiredService<ApiController>(),
|
||||||
s.GetRequiredService<PeriodicFileScanner>(), s.GetRequiredService<FileDialogManager>(), s.GetRequiredService<MareConfigService>(), s.GetRequiredService<DalamudUtilService>(),
|
s.GetRequiredService<CacheMonitor>(), s.GetRequiredService<FileDialogManager>(), s.GetRequiredService<MareConfigService>(), s.GetRequiredService<DalamudUtilService>(),
|
||||||
pluginInterface, textureProvider, s.GetRequiredService<Dalamud.Localization>(), s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<MareMediator>()));
|
pluginInterface, textureProvider, s.GetRequiredService<Dalamud.Localization>(), s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<MareMediator>()));
|
||||||
collection.AddScoped((s) => new ChatService(s.GetRequiredService<ILogger<ChatService>>(), s.GetRequiredService<DalamudUtilService>(),
|
collection.AddScoped((s) => new ChatService(s.GetRequiredService<ILogger<ChatService>>(), s.GetRequiredService<DalamudUtilService>(),
|
||||||
s.GetRequiredService<MareMediator>(), s.GetRequiredService<ApiController>(), s.GetRequiredService<PairManager>(),
|
s.GetRequiredService<MareMediator>(), s.GetRequiredService<ApiController>(), s.GetRequiredService<PairManager>(),
|
||||||
@@ -152,6 +152,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddHostedService(p => p.GetRequiredService<PerformanceCollectorService>());
|
collection.AddHostedService(p => p.GetRequiredService<PerformanceCollectorService>());
|
||||||
collection.AddHostedService(p => p.GetRequiredService<DtrEntry>());
|
collection.AddHostedService(p => p.GetRequiredService<DtrEntry>());
|
||||||
collection.AddHostedService(p => p.GetRequiredService<MarePlugin>());
|
collection.AddHostedService(p => p.GetRequiredService<MarePlugin>());
|
||||||
|
collection.AddHostedService(p => p.GetRequiredService<FileCacheManager>());
|
||||||
})
|
})
|
||||||
.Build()
|
.Build()
|
||||||
.RunAsync(_pluginCts.Token);
|
.RunAsync(_pluginCts.Token);
|
||||||
|
|||||||
@@ -25,18 +25,18 @@ public sealed class CommandManagerService : IDisposable
|
|||||||
private readonly MareMediator _mediator;
|
private readonly MareMediator _mediator;
|
||||||
private readonly MareConfigService _mareConfigService;
|
private readonly MareConfigService _mareConfigService;
|
||||||
private readonly PerformanceCollectorService _performanceCollectorService;
|
private readonly PerformanceCollectorService _performanceCollectorService;
|
||||||
private readonly PeriodicFileScanner _periodicFileScanner;
|
private readonly CacheMonitor _cacheMonitor;
|
||||||
private readonly ChatService _chatService;
|
private readonly ChatService _chatService;
|
||||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||||
|
|
||||||
public CommandManagerService(ICommandManager commandManager, PerformanceCollectorService performanceCollectorService,
|
public CommandManagerService(ICommandManager commandManager, PerformanceCollectorService performanceCollectorService,
|
||||||
ServerConfigurationManager serverConfigurationManager, PeriodicFileScanner periodicFileScanner, ChatService chatService,
|
ServerConfigurationManager serverConfigurationManager, CacheMonitor periodicFileScanner, ChatService chatService,
|
||||||
ApiController apiController, MareMediator mediator, MareConfigService mareConfigService)
|
ApiController apiController, MareMediator mediator, MareConfigService mareConfigService)
|
||||||
{
|
{
|
||||||
_commandManager = commandManager;
|
_commandManager = commandManager;
|
||||||
_performanceCollectorService = performanceCollectorService;
|
_performanceCollectorService = performanceCollectorService;
|
||||||
_serverConfigurationManager = serverConfigurationManager;
|
_serverConfigurationManager = serverConfigurationManager;
|
||||||
_periodicFileScanner = periodicFileScanner;
|
_cacheMonitor = periodicFileScanner;
|
||||||
_chatService = chatService;
|
_chatService = chatService;
|
||||||
_apiController = apiController;
|
_apiController = apiController;
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
@@ -112,7 +112,7 @@ public sealed class CommandManagerService : IDisposable
|
|||||||
}
|
}
|
||||||
else if (string.Equals(splitArgs[0], "rescan", StringComparison.OrdinalIgnoreCase))
|
else if (string.Equals(splitArgs[0], "rescan", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
_periodicFileScanner.InvokeScan(forced: true);
|
_cacheMonitor.InvokeScan();
|
||||||
}
|
}
|
||||||
else if (string.Equals(splitArgs[0], "perf", StringComparison.OrdinalIgnoreCase))
|
else if (string.Equals(splitArgs[0], "perf", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ public record CensusUpdateMessage(byte Gender, byte RaceId, byte TribeId) : Mess
|
|||||||
public record TargetPairMessage(Pair Pair) : MessageBase;
|
public record TargetPairMessage(Pair Pair) : MessageBase;
|
||||||
public record CombatStartMessage : MessageBase;
|
public record CombatStartMessage : MessageBase;
|
||||||
public record CombatEndMessage : MessageBase;
|
public record CombatEndMessage : MessageBase;
|
||||||
|
public record PenumbraDirectoryChangedMessage(string? ModDirectory) : MessageBase;
|
||||||
|
|
||||||
public record UserChatMsgMessage(SignedChatMessage ChatMsg) : MessageBase;
|
public record UserChatMsgMessage(SignedChatMessage ChatMsg) : MessageBase;
|
||||||
public record GroupChatMsgMessage(GroupDto GroupInfo, SignedChatMessage ChatMsg) : MessageBase;
|
public record GroupChatMsgMessage(GroupDto GroupInfo, SignedChatMessage ChatMsg) : MessageBase;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class IntroUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
private readonly ApiController _apiController;
|
private readonly ApiController _apiController;
|
||||||
private readonly MareConfigService _configService;
|
private readonly MareConfigService _configService;
|
||||||
private readonly PeriodicFileScanner _fileCacheManager;
|
private readonly CacheMonitor _cacheMonitor;
|
||||||
private readonly Dictionary<string, string> _languages = new(StringComparer.Ordinal) { { "English", "en" }, { "Deutsch", "de" }, { "Français", "fr" } };
|
private readonly Dictionary<string, string> _languages = new(StringComparer.Ordinal) { { "English", "en" }, { "Deutsch", "de" }, { "Français", "fr" } };
|
||||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||||
private readonly UiSharedService _uiShared;
|
private readonly UiSharedService _uiShared;
|
||||||
@@ -41,12 +41,12 @@ public class IntroUi : WindowMediatorSubscriberBase
|
|||||||
private RegisterReplyDto? _registrationReply;
|
private RegisterReplyDto? _registrationReply;
|
||||||
|
|
||||||
public IntroUi(ILogger<IntroUi> logger, UiSharedService uiShared, MareConfigService configService, ApiController apiController,
|
public IntroUi(ILogger<IntroUi> logger, UiSharedService uiShared, MareConfigService configService, ApiController apiController,
|
||||||
PeriodicFileScanner fileCacheManager, ServerConfigurationManager serverConfigurationManager, MareMediator mareMediator) : base(logger, mareMediator, "Loporrit Setup")
|
CacheMonitor fileCacheManager, ServerConfigurationManager serverConfigurationManager, MareMediator mareMediator) : base(logger, mareMediator, "Loporrit Setup")
|
||||||
{
|
{
|
||||||
_uiShared = uiShared;
|
_uiShared = uiShared;
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
_apiController = apiController;
|
_apiController = apiController;
|
||||||
_fileCacheManager = fileCacheManager;
|
_cacheMonitor = fileCacheManager;
|
||||||
_serverConfigurationManager = serverConfigurationManager;
|
_serverConfigurationManager = serverConfigurationManager;
|
||||||
|
|
||||||
IsOpen = false;
|
IsOpen = false;
|
||||||
@@ -176,11 +176,11 @@ public class IntroUi : WindowMediatorSubscriberBase
|
|||||||
_uiShared.DrawCacheDirectorySetting();
|
_uiShared.DrawCacheDirectorySetting();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_fileCacheManager.IsScanRunning && !string.IsNullOrEmpty(_configService.Current.CacheFolder) && _uiShared.HasValidPenumbraModPath && Directory.Exists(_configService.Current.CacheFolder))
|
if (!_cacheMonitor.IsScanRunning && !string.IsNullOrEmpty(_configService.Current.CacheFolder) && _uiShared.HasValidPenumbraModPath && Directory.Exists(_configService.Current.CacheFolder))
|
||||||
{
|
{
|
||||||
if (ImGui.Button("Start Scan##startScan"))
|
if (ImGui.Button("Start Scan##startScan"))
|
||||||
{
|
{
|
||||||
_fileCacheManager.InvokeScan(forced: true);
|
_cacheMonitor.InvokeScan();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using MareSynchronos.API.Data.Comparer;
|
|||||||
using MareSynchronos.API.Dto.Account;
|
using MareSynchronos.API.Dto.Account;
|
||||||
using MareSynchronos.API.Routes;
|
using MareSynchronos.API.Routes;
|
||||||
using MareSynchronos.FileCache;
|
using MareSynchronos.FileCache;
|
||||||
|
using MareSynchronos.Interop;
|
||||||
using MareSynchronos.MareConfiguration;
|
using MareSynchronos.MareConfiguration;
|
||||||
using MareSynchronos.MareConfiguration.Models;
|
using MareSynchronos.MareConfiguration.Models;
|
||||||
using MareSynchronos.PlayerData.Export;
|
using MareSynchronos.PlayerData.Export;
|
||||||
@@ -36,6 +37,8 @@ namespace MareSynchronos.UI;
|
|||||||
public class SettingsUi : WindowMediatorSubscriberBase
|
public class SettingsUi : WindowMediatorSubscriberBase
|
||||||
{
|
{
|
||||||
private readonly ApiController _apiController;
|
private readonly ApiController _apiController;
|
||||||
|
private readonly IpcManager _ipcManager;
|
||||||
|
private readonly CacheMonitor _cacheMonitor;
|
||||||
private readonly MareConfigService _configService;
|
private readonly MareConfigService _configService;
|
||||||
private readonly ConcurrentDictionary<GameObjectHandler, Dictionary<string, FileDownloadStatus>> _currentDownloads = new();
|
private readonly ConcurrentDictionary<GameObjectHandler, Dictionary<string, FileDownloadStatus>> _currentDownloads = new();
|
||||||
private readonly FileCompactor _fileCompactor;
|
private readonly FileCompactor _fileCompactor;
|
||||||
@@ -77,7 +80,8 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
FileUploadManager fileTransferManager,
|
FileUploadManager fileTransferManager,
|
||||||
FileTransferOrchestrator fileTransferOrchestrator,
|
FileTransferOrchestrator fileTransferOrchestrator,
|
||||||
FileCacheManager fileCacheManager,
|
FileCacheManager fileCacheManager,
|
||||||
FileCompactor fileCompactor, ApiController apiController) : base(logger, mediator, "Loporrit Settings")
|
FileCompactor fileCompactor, ApiController apiController,
|
||||||
|
IpcManager ipcManager, CacheMonitor cacheMonitor) : base(logger, mediator, "Loporrit Settings")
|
||||||
{
|
{
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
_mareCharaFileManager = mareCharaFileManager;
|
_mareCharaFileManager = mareCharaFileManager;
|
||||||
@@ -90,6 +94,8 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
_fileTransferOrchestrator = fileTransferOrchestrator;
|
_fileTransferOrchestrator = fileTransferOrchestrator;
|
||||||
_fileCacheManager = fileCacheManager;
|
_fileCacheManager = fileCacheManager;
|
||||||
_apiController = apiController;
|
_apiController = apiController;
|
||||||
|
_ipcManager = ipcManager;
|
||||||
|
_cacheMonitor = cacheMonitor;
|
||||||
_fileCompactor = fileCompactor;
|
_fileCompactor = fileCompactor;
|
||||||
_uiShared = uiShared;
|
_uiShared = uiShared;
|
||||||
AllowClickthrough = false;
|
AllowClickthrough = false;
|
||||||
@@ -727,7 +733,57 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
"The storage governs itself by clearing data beyond the set storage size. Please set the storage size accordingly. It is not necessary to manually clear the storage.");
|
"The storage governs itself by clearing data beyond the set storage size. Please set the storage size accordingly. It is not necessary to manually clear the storage.");
|
||||||
|
|
||||||
_uiShared.DrawFileScanState();
|
_uiShared.DrawFileScanState();
|
||||||
_uiShared.DrawTimeSpanBetweenScansSetting();
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.TextUnformatted("Monitoring Penumbra Folder: " + (_cacheMonitor.PenumbraWatcher?.Path ?? "Not monitoring"));
|
||||||
|
if (string.IsNullOrEmpty(_cacheMonitor.PenumbraWatcher?.Path))
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
using var id = ImRaii.PushId("penumbraMonitor");
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.ArrowsToCircle, "Try to reinitialize Monitor"))
|
||||||
|
{
|
||||||
|
_cacheMonitor.StartPenumbraWatcher(_ipcManager.PenumbraModDirectory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.TextUnformatted("Monitoring Mare Storage Folder: " + (_cacheMonitor.MareWatcher?.Path ?? "Not monitoring"));
|
||||||
|
if (string.IsNullOrEmpty(_cacheMonitor.MareWatcher?.Path))
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
using var id = ImRaii.PushId("mareMonitor");
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.ArrowsToCircle, "Try to reinitialize Monitor"))
|
||||||
|
{
|
||||||
|
_cacheMonitor.StartMareWatcher(_configService.Current.CacheFolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_cacheMonitor.MareWatcher == null || _cacheMonitor.PenumbraWatcher == null)
|
||||||
|
{
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Play, "Resume Monitoring"))
|
||||||
|
{
|
||||||
|
_cacheMonitor.StartMareWatcher(_configService.Current.CacheFolder);
|
||||||
|
_cacheMonitor.StartPenumbraWatcher(_ipcManager.PenumbraModDirectory);
|
||||||
|
_cacheMonitor.InvokeScan();
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip("Attempts to resume monitoring for both Penumbra and Mare Storage. "
|
||||||
|
+ "Resuming the monitoring will also force a full scan to run." + Environment.NewLine
|
||||||
|
+ "If the button remains present after clicking it, consult /xllog for errors");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
|
||||||
|
{
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Stop, "Stop Monitoring"))
|
||||||
|
{
|
||||||
|
_cacheMonitor.StopMonitoring();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip("Stops the monitoring for both Penumbra and Mare Storage. "
|
||||||
|
+ "Do not stop the monitoring, unless you plan to move the Penumbra and Mare Storage folders, to ensure correct functionality of Mare." + Environment.NewLine
|
||||||
|
+ "If you stop the monitoring to move folders around, resume it after you are finished moving the files."
|
||||||
|
+ UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
_uiShared.DrawCacheDirectorySetting();
|
_uiShared.DrawCacheDirectorySetting();
|
||||||
ImGui.TextUnformatted($"Currently utilized local storage: {UiSharedService.ByteToString(_uiShared.FileCacheSize)}");
|
ImGui.TextUnformatted($"Currently utilized local storage: {UiSharedService.ByteToString(_uiShared.FileCacheSize)}");
|
||||||
bool isLinux = Util.IsWine();
|
bool isLinux = Util.IsWine();
|
||||||
@@ -827,8 +883,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
File.Delete(file);
|
File.Delete(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
_uiShared.RecalculateFileCacheSize();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("You normally do not need to do this. THIS IS NOT SOMETHING YOU SHOULD BE DOING TO TRY TO FIX SYNC ISSUES." + Environment.NewLine
|
UiSharedService.AttachToolTip("You normally do not need to do this. THIS IS NOT SOMETHING YOU SHOULD BE DOING TO TRY TO FIX SYNC ISSUES." + Environment.NewLine
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
private readonly ApiController _apiController;
|
private readonly ApiController _apiController;
|
||||||
|
|
||||||
private readonly PeriodicFileScanner _cacheScanner;
|
private readonly CacheMonitor _cacheMonitor;
|
||||||
|
|
||||||
private readonly MareConfigService _configService;
|
private readonly MareConfigService _configService;
|
||||||
|
|
||||||
@@ -79,14 +79,14 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
private int _serverSelectionIndex = -1;
|
private int _serverSelectionIndex = -1;
|
||||||
|
|
||||||
public UiSharedService(ILogger<UiSharedService> logger, IpcManager ipcManager, ApiController apiController,
|
public UiSharedService(ILogger<UiSharedService> logger, IpcManager ipcManager, ApiController apiController,
|
||||||
PeriodicFileScanner cacheScanner, FileDialogManager fileDialogManager,
|
CacheMonitor cacheMonitor, FileDialogManager fileDialogManager,
|
||||||
MareConfigService configService, DalamudUtilService dalamudUtil, IDalamudPluginInterface pluginInterface,
|
MareConfigService configService, DalamudUtilService dalamudUtil, IDalamudPluginInterface pluginInterface,
|
||||||
ITextureProvider textureProvider, Dalamud.Localization localization,
|
ITextureProvider textureProvider, Dalamud.Localization localization,
|
||||||
ServerConfigurationManager serverManager, MareMediator mediator) : base(logger, mediator)
|
ServerConfigurationManager serverManager, MareMediator mediator) : base(logger, mediator)
|
||||||
{
|
{
|
||||||
_ipcManager = ipcManager;
|
_ipcManager = ipcManager;
|
||||||
_apiController = apiController;
|
_apiController = apiController;
|
||||||
_cacheScanner = cacheScanner;
|
_cacheMonitor = cacheMonitor;
|
||||||
FileDialogManager = fileDialogManager;
|
FileDialogManager = fileDialogManager;
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
_dalamudUtil = dalamudUtil;
|
_dalamudUtil = dalamudUtil;
|
||||||
@@ -123,7 +123,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
public bool EditTrackerPosition { get; set; }
|
public bool EditTrackerPosition { get; set; }
|
||||||
|
|
||||||
public long FileCacheSize => _cacheScanner.FileCacheSize;
|
public long FileCacheSize => _cacheMonitor.FileCacheSize;
|
||||||
|
|
||||||
public bool HasValidPenumbraModPath => !(_ipcManager.PenumbraModDirectory ?? string.Empty).IsNullOrEmpty() && Directory.Exists(_ipcManager.PenumbraModDirectory);
|
public bool HasValidPenumbraModPath => !(_ipcManager.PenumbraModDirectory ?? string.Empty).IsNullOrEmpty() && Directory.Exists(_ipcManager.PenumbraModDirectory);
|
||||||
|
|
||||||
@@ -618,7 +618,8 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
_configService.Current.CacheFolder = path;
|
_configService.Current.CacheFolder = path;
|
||||||
_configService.Save();
|
_configService.Save();
|
||||||
_cacheScanner.StartScan();
|
_cacheMonitor.StartMareWatcher(path);
|
||||||
|
_cacheMonitor.InvokeScan();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -742,41 +743,45 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
public void DrawFileScanState()
|
public void DrawFileScanState()
|
||||||
{
|
{
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
ImGui.TextUnformatted("File Scanner Status");
|
ImGui.TextUnformatted("File Scanner Status");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (_cacheScanner.IsScanRunning)
|
if (_cacheMonitor.IsScanRunning)
|
||||||
{
|
{
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
|
||||||
ImGui.TextUnformatted("Scan is running");
|
ImGui.TextUnformatted("Scan is running");
|
||||||
ImGui.TextUnformatted("Current Progress:");
|
ImGui.TextUnformatted("Current Progress:");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.TextUnformatted(_cacheScanner.TotalFiles == 1
|
ImGui.TextUnformatted(_cacheMonitor.TotalFiles == 1
|
||||||
? "Collecting files"
|
? "Collecting files"
|
||||||
: $"Processing {_cacheScanner.CurrentFileProgress}/{_cacheScanner.TotalFilesStorage} from storage ({_cacheScanner.TotalFiles} scanned in)");
|
: $"Processing {_cacheMonitor.CurrentFileProgress}/{_cacheMonitor.TotalFilesStorage} from storage ({_cacheMonitor.TotalFiles} scanned in)");
|
||||||
AttachToolTip("Note: it is possible to have more files in storage than scanned in, " +
|
AttachToolTip("Note: it is possible to have more files in storage than scanned in, " +
|
||||||
"this is due to the scanner normally ignoring those files but the game loading them in and using them on your character, so they get " +
|
"this is due to the scanner normally ignoring those files but the game loading them in and using them on your character, so they get " +
|
||||||
"added to the local storage.");
|
"added to the local storage.");
|
||||||
}
|
}
|
||||||
else if (_configService.Current.FileScanPaused)
|
else if (_cacheMonitor.HaltScanLocks.Any(f => f.Value > 0))
|
||||||
{
|
{
|
||||||
ImGui.TextUnformatted("File scanner is paused");
|
ImGui.AlignTextToFramePadding();
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGui.Button("Force Rescan##forcedrescan"))
|
ImGui.TextUnformatted("Halted (" + string.Join(", ", _cacheMonitor.HaltScanLocks.Where(f => f.Value > 0).Select(locker => locker.Key + ": " + locker.Value + " halt requests")) + ")");
|
||||||
{
|
|
||||||
_cacheScanner.InvokeScan(forced: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (_cacheScanner.HaltScanLocks.Any(f => f.Value > 0))
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted("Halted (" + string.Join(", ", _cacheScanner.HaltScanLocks.Where(f => f.Value > 0).Select(locker => locker.Key + ": " + locker.Value + " halt requests")) + ")");
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImGui.Button("Reset halt requests##clearlocks"))
|
if (ImGui.Button("Reset halt requests##clearlocks"))
|
||||||
{
|
{
|
||||||
_cacheScanner.ResetLocks();
|
_cacheMonitor.ResetLocks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ImGui.TextUnformatted("Next scan in " + _cacheScanner.TimeUntilNextScan);
|
ImGui.TextUnformatted("Idle");
|
||||||
|
if (_configService.Current.InitialScanComplete)
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (NormalizedIconTextButton(FontAwesomeIcon.Play, "Force rescan"))
|
||||||
|
{
|
||||||
|
_cacheMonitor.InvokeScan();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -940,36 +945,12 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
return _serverSelectionIndex;
|
return _serverSelectionIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawTimeSpanBetweenScansSetting()
|
|
||||||
{
|
|
||||||
var timeSpan = _configService.Current.TimeSpanBetweenScansInSeconds;
|
|
||||||
ImGui.SetNextItemWidth(250 * ImGuiHelpers.GlobalScale);
|
|
||||||
if (ImGui.SliderInt("Seconds between scans##timespan", ref timeSpan, 20, 60))
|
|
||||||
{
|
|
||||||
_configService.Current.TimeSpanBetweenScansInSeconds = timeSpan;
|
|
||||||
_configService.Save();
|
|
||||||
}
|
|
||||||
DrawHelpText("This is the time in seconds between file scans. Increase it to reduce system load. A too high setting can cause issues when manually fumbling about in the cache or Penumbra mods folders.");
|
|
||||||
var isPaused = _configService.Current.FileScanPaused;
|
|
||||||
if (ImGui.Checkbox("Pause periodic file scan##filescanpause", ref isPaused))
|
|
||||||
{
|
|
||||||
_configService.Current.FileScanPaused = isPaused;
|
|
||||||
_configService.Save();
|
|
||||||
}
|
|
||||||
DrawHelpText("This allows you to stop the periodic scans of your Penumbra and Loporrit cache directories. Use this to move the Loporrit cache and Penumbra mod folders around. If you enable this permanently, run a Force rescan after adding mods to Penumbra.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadLocalization(string languageCode)
|
public void LoadLocalization(string languageCode)
|
||||||
{
|
{
|
||||||
_localization.SetupWithLangCode(languageCode);
|
_localization.SetupWithLangCode(languageCode);
|
||||||
Strings.ToS = new Strings.ToSStrings();
|
Strings.ToS = new Strings.ToSStrings();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RecalculateFileCacheSize()
|
|
||||||
{
|
|
||||||
_cacheScanner.InvokeScan(forced: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
[LibraryImport("user32")]
|
[LibraryImport("user32")]
|
||||||
internal static partial short GetKeyState(int nVirtKey);
|
internal static partial short GetKeyState(int nVirtKey);
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,5 @@ public static class Crypto
|
|||||||
{
|
{
|
||||||
return BitConverter.ToString(_sha256CryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToCompute))).Replace("-", "", StringComparison.Ordinal);
|
return BitConverter.ToString(_sha256CryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToCompute))).Replace("-", "", StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#pragma warning restore SYSLIB0021 // Type or member is obsolete
|
#pragma warning restore SYSLIB0021 // Type or member is obsolete
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user