Client rework for API change and paradigm shift (#39)

* most of the groups refactoring on client

* register OnMethods for group stuff

* start implementing client (still pretty broken)

* finish implementing new api first iteration

* idk rework everything for pair shit (still WIP); goal is to remove PairedClients and GroupPairClients from ApiController

* move everything to PairManager, remove dictionaries from APiController

* remove admin stuff from client, cleanup

* adjust reconnection handling, add new settings, todo still to remove access from old stuff that's marked obsolete from config

* add back adding servers, fix intro ui

* fix obsolete calls

* adjust config namespace

* add UI for setting animation/sound permissions to syncshells

* add ConfigurationService to hot reload config on change from external

* move transient data cache to configuration

* add deleting service to ui

* fix saving of transient resources

* fix group pair user assignments

* halt scanner when penumbra inactive, add visible/online/offline split to individual pairs and tags

* add presence to syncshell ui

* move fullpause from config to server config

* fixes in code style

* more codestyle

* show info icon on player in shells, don't show icon when no changes from default state are made, add online notifs

* fixes to intro UI

---------

Co-authored-by: rootdarkarchon <root.darkarchon@outlook.com>
This commit is contained in:
rootdarkarchon
2023-01-29 15:13:53 +01:00
committed by GitHub
parent 616af3626a
commit b2276a1883
79 changed files with 3585 additions and 2701 deletions

View File

@@ -1,7 +1,6 @@
#nullable disable
using MareSynchronos;
using System.Globalization;
namespace MareSynchronos.FileCache;
@@ -22,7 +21,7 @@ public class FileCacheEntity
public void SetResolvedFilePath(string filePath)
{
ResolvedFilepath = filePath.ToLowerInvariant().Replace("\\\\", "\\", System.StringComparison.Ordinal);
ResolvedFilepath = filePath.ToLowerInvariant().Replace("\\\\", "\\", StringComparison.Ordinal);
}
public string CsvEntry => $"{Hash}{FileCacheManager.CsvSplit}{PrefixedFilePath}{FileCacheManager.CsvSplit}{LastModifiedDateTicks.ToString(CultureInfo.InvariantCulture)}";

View File

@@ -1,44 +1,41 @@
using MareSynchronos.Managers;
using MareSynchronos.MareConfiguration;
using MareSynchronos.Utils;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
namespace MareSynchronos.FileCache;
public class FileCacheManager : IDisposable
{
private const string PenumbraPrefix = "{penumbra}";
private const string CachePrefix = "{cache}";
private const string _penumbraPrefix = "{penumbra}";
private const string _cachePrefix = "{cache}";
private readonly IpcManager _ipcManager;
private readonly Configuration _configuration;
private readonly string CsvPath;
private string CsvBakPath => CsvPath + ".bak";
private readonly ConcurrentDictionary<string, FileCacheEntity> FileCaches = new(StringComparer.Ordinal);
private readonly ConfigurationService _configService;
private readonly string _csvPath;
private string CsvBakPath => _csvPath + ".bak";
private readonly ConcurrentDictionary<string, FileCacheEntity> _fileCaches = new(StringComparer.Ordinal);
public const string CsvSplit = "|";
private object _fileWriteLock = new();
private readonly object _fileWriteLock = new();
public FileCacheManager(IpcManager ipcManager, Configuration configuration, string configDirectoryName)
public FileCacheManager(IpcManager ipcManager, ConfigurationService configService)
{
_ipcManager = ipcManager;
_configuration = configuration;
CsvPath = Path.Combine(configDirectoryName, "FileCache.csv");
_configService = configService;
_csvPath = Path.Combine(configService.ConfigurationDirectory, "FileCache.csv");
lock (_fileWriteLock)
{
if (File.Exists(CsvBakPath))
{
File.Move(CsvBakPath, CsvPath, true);
File.Move(CsvBakPath, _csvPath, overwrite: true);
}
}
if (File.Exists(CsvPath))
if (File.Exists(_csvPath))
{
var entries = File.ReadAllLines(CsvPath);
var entries = File.ReadAllLines(_csvPath);
foreach (var entry in entries)
{
var splittedEntry = entry.Split(CsvSplit, StringSplitOptions.None);
@@ -47,7 +44,7 @@ public class FileCacheManager : IDisposable
var hash = splittedEntry[0];
var path = splittedEntry[1];
var time = splittedEntry[2];
FileCaches[path] = ReplacePathPrefixes(new FileCacheEntity(hash, path, time));
_fileCaches[path] = ReplacePathPrefixes(new FileCacheEntity(hash, path, time));
}
catch (Exception)
{
@@ -60,19 +57,19 @@ public class FileCacheManager : IDisposable
public void WriteOutFullCsv()
{
StringBuilder sb = new();
foreach (var entry in FileCaches.OrderBy(f => f.Value.PrefixedFilePath))
foreach (var entry in _fileCaches.OrderBy(f => f.Value.PrefixedFilePath, StringComparer.OrdinalIgnoreCase))
{
sb.AppendLine(entry.Value.CsvEntry);
}
if (File.Exists(CsvPath))
if (File.Exists(_csvPath))
{
File.Copy(CsvPath, CsvBakPath, true);
File.Copy(_csvPath, CsvBakPath, overwrite: true);
}
lock (_fileWriteLock)
{
try
{
File.WriteAllText(CsvPath, sb.ToString());
File.WriteAllText(_csvPath, sb.ToString());
File.Delete(CsvBakPath);
}
catch
@@ -82,13 +79,13 @@ public class FileCacheManager : IDisposable
}
}
public List<FileCacheEntity> GetAllFileCaches() => FileCaches.Values.ToList();
public List<FileCacheEntity> GetAllFileCaches() => _fileCaches.Values.ToList();
public FileCacheEntity? GetFileCacheByHash(string hash)
{
if (FileCaches.Any(f => string.Equals(f.Value.Hash, hash, StringComparison.Ordinal)))
if (_fileCaches.Any(f => string.Equals(f.Value.Hash, hash, StringComparison.Ordinal)))
{
return GetValidatedFileCache(FileCaches.FirstOrDefault(f => string.Equals(f.Value.Hash, hash, StringComparison.Ordinal)).Value);
return GetValidatedFileCache(_fileCaches.FirstOrDefault(f => string.Equals(f.Value.Hash, hash, StringComparison.Ordinal)).Value);
}
return null;
@@ -113,7 +110,7 @@ public class FileCacheManager : IDisposable
public FileCacheEntity? GetFileCacheByPath(string path)
{
var cleanedPath = path.Replace("/", "\\", StringComparison.OrdinalIgnoreCase).ToLowerInvariant().Replace(_ipcManager.PenumbraModDirectory()!.ToLowerInvariant(), "", StringComparison.OrdinalIgnoreCase);
var entry = FileCaches.FirstOrDefault(f => f.Value.ResolvedFilepath.EndsWith(cleanedPath, StringComparison.OrdinalIgnoreCase)).Value;
var entry = _fileCaches.FirstOrDefault(f => f.Value.ResolvedFilepath.EndsWith(cleanedPath, StringComparison.OrdinalIgnoreCase)).Value;
if (entry == null)
{
@@ -132,8 +129,8 @@ public class FileCacheManager : IDisposable
FileInfo fi = new(path);
if (!fi.Exists) return null;
var fullName = fi.FullName.ToLowerInvariant();
if (!fullName.Contains(_configuration.CacheFolder.ToLowerInvariant(), StringComparison.Ordinal)) return null;
string prefixedPath = fullName.Replace(_configuration.CacheFolder.ToLowerInvariant(), CachePrefix + "\\", StringComparison.Ordinal).Replace("\\\\", "\\", StringComparison.Ordinal);
if (!fullName.Contains(_configService.Current.CacheFolder.ToLowerInvariant(), StringComparison.Ordinal)) return null;
string prefixedPath = fullName.Replace(_configService.Current.CacheFolder.ToLowerInvariant(), _cachePrefix + "\\", StringComparison.Ordinal).Replace("\\\\", "\\", StringComparison.Ordinal);
return CreateFileCacheEntity(fi, prefixedPath, fi.Name.ToUpper(CultureInfo.InvariantCulture));
}
@@ -144,22 +141,19 @@ public class FileCacheManager : IDisposable
if (!fi.Exists) return null;
var fullName = fi.FullName.ToLowerInvariant();
if (!fullName.Contains(_ipcManager.PenumbraModDirectory()!.ToLowerInvariant(), StringComparison.Ordinal)) return null;
string prefixedPath = fullName.Replace(_ipcManager.PenumbraModDirectory()!.ToLowerInvariant(), PenumbraPrefix + "\\", StringComparison.Ordinal).Replace("\\\\", "\\", StringComparison.Ordinal);
string prefixedPath = fullName.Replace(_ipcManager.PenumbraModDirectory()!.ToLowerInvariant(), _penumbraPrefix + "\\", StringComparison.Ordinal).Replace("\\\\", "\\", StringComparison.Ordinal);
return CreateFileCacheEntity(fi, prefixedPath);
}
private FileCacheEntity? CreateFileCacheEntity(FileInfo fileInfo, string prefixedPath, string? hash = null)
{
if (hash == null)
{
hash = Crypto.GetFileHash(fileInfo.FullName);
}
hash ??= Crypto.GetFileHash(fileInfo.FullName);
var entity = new FileCacheEntity(hash, prefixedPath, fileInfo.LastWriteTimeUtc.Ticks.ToString(CultureInfo.InvariantCulture));
entity = ReplacePathPrefixes(entity);
FileCaches[prefixedPath] = entity;
_fileCaches[prefixedPath] = entity;
lock (_fileWriteLock)
{
File.AppendAllLines(CsvPath, new[] { entity.CsvEntry });
File.AppendAllLines(_csvPath, new[] { entity.CsvEntry });
}
var result = GetFileCacheByPath(fileInfo.FullName);
Logger.Debug("Creating file cache for " + fileInfo.FullName + " success: " + (result != null));
@@ -178,7 +172,7 @@ public class FileCacheManager : IDisposable
var file = new FileInfo(fileCache.ResolvedFilepath);
if (!file.Exists)
{
FileCaches.Remove(fileCache.PrefixedFilePath, out _);
_fileCaches.Remove(fileCache.PrefixedFilePath, out _);
return null;
}
@@ -193,7 +187,7 @@ public class FileCacheManager : IDisposable
public void RemoveHash(FileCacheEntity entity)
{
Logger.Verbose("Removing " + entity.ResolvedFilepath);
FileCaches.Remove(entity.PrefixedFilePath, out _);
_fileCaches.Remove(entity.PrefixedFilePath, out _);
}
public void UpdateHash(FileCacheEntity fileCache)
@@ -201,19 +195,19 @@ public class FileCacheManager : IDisposable
Logger.Verbose("Updating hash for " + fileCache.ResolvedFilepath);
fileCache.Hash = Crypto.GetFileHash(fileCache.ResolvedFilepath);
fileCache.LastModifiedDateTicks = new FileInfo(fileCache.ResolvedFilepath).LastWriteTimeUtc.Ticks.ToString(CultureInfo.InvariantCulture);
FileCaches.Remove(fileCache.PrefixedFilePath, out _);
FileCaches[fileCache.PrefixedFilePath] = fileCache;
_fileCaches.Remove(fileCache.PrefixedFilePath, out _);
_fileCaches[fileCache.PrefixedFilePath] = fileCache;
}
private FileCacheEntity ReplacePathPrefixes(FileCacheEntity fileCache)
{
if (fileCache.PrefixedFilePath.StartsWith(PenumbraPrefix, StringComparison.OrdinalIgnoreCase))
if (fileCache.PrefixedFilePath.StartsWith(_penumbraPrefix, StringComparison.OrdinalIgnoreCase))
{
fileCache.SetResolvedFilePath(fileCache.PrefixedFilePath.Replace(PenumbraPrefix, _ipcManager.PenumbraModDirectory(), StringComparison.Ordinal));
fileCache.SetResolvedFilePath(fileCache.PrefixedFilePath.Replace(_penumbraPrefix, _ipcManager.PenumbraModDirectory(), StringComparison.Ordinal));
}
else if (fileCache.PrefixedFilePath.StartsWith(CachePrefix, StringComparison.OrdinalIgnoreCase))
else if (fileCache.PrefixedFilePath.StartsWith(_cachePrefix, StringComparison.OrdinalIgnoreCase))
{
fileCache.SetResolvedFilePath(fileCache.PrefixedFilePath.Replace(CachePrefix, _configuration.CacheFolder, StringComparison.Ordinal));
fileCache.SetResolvedFilePath(fileCache.PrefixedFilePath.Replace(_cachePrefix, _configService.Current.CacheFolder, StringComparison.Ordinal));
}
return fileCache;

View File

@@ -1,11 +1,9 @@
using MareSynchronos.Models;
namespace MareSynchronos.FileCache;
namespace MareSynchronos.FileCache;
public enum FileState
{
Valid,
RequireUpdate,
RequireDeletion
RequireDeletion,
}

View File

@@ -1,11 +1,6 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using MareSynchronos.Managers;
using MareSynchronos.MareConfiguration;
using MareSynchronos.Utils;
using MareSynchronos.WebAPI;
@@ -14,19 +9,19 @@ namespace MareSynchronos.FileCache;
public class PeriodicFileScanner : IDisposable
{
private readonly IpcManager _ipcManager;
private readonly Configuration _pluginConfiguration;
private readonly ConfigurationService _configService;
private readonly FileCacheManager _fileDbManager;
private readonly ApiController _apiController;
private readonly DalamudUtil _dalamudUtil;
private CancellationTokenSource? _scanCancellationTokenSource;
private Task? _fileScannerTask = null;
public ConcurrentDictionary<string, int> haltScanLocks = new(StringComparer.Ordinal);
public PeriodicFileScanner(IpcManager ipcManager, Configuration pluginConfiguration, FileCacheManager fileDbManager, ApiController apiController, DalamudUtil dalamudUtil)
public PeriodicFileScanner(IpcManager ipcManager, ConfigurationService configService, FileCacheManager fileDbManager, ApiController apiController, DalamudUtil dalamudUtil)
{
Logger.Verbose("Creating " + nameof(PeriodicFileScanner));
_ipcManager = ipcManager;
_pluginConfiguration = pluginConfiguration;
_configService = configService;
_fileDbManager = fileDbManager;
_apiController = apiController;
_dalamudUtil = dalamudUtil;
@@ -69,10 +64,10 @@ public class PeriodicFileScanner : IDisposable
haltScanLocks[source]--;
if (haltScanLocks[source] < 0) haltScanLocks[source] = 0;
if (fileScanWasRunning && haltScanLocks.All(f => f.Value == 0))
if (_fileScanWasRunning && haltScanLocks.All(f => f.Value == 0))
{
fileScanWasRunning = false;
InvokeScan(true);
_fileScanWasRunning = false;
InvokeScan(forced: true);
}
}
@@ -84,13 +79,13 @@ public class PeriodicFileScanner : IDisposable
if (IsScanRunning && haltScanLocks.Any(f => f.Value > 0))
{
_scanCancellationTokenSource?.Cancel();
fileScanWasRunning = true;
_fileScanWasRunning = true;
}
}
private bool fileScanWasRunning = false;
private long currentFileProgress = 0;
public long CurrentFileProgress => currentFileProgress;
private bool _fileScanWasRunning = false;
private long _currentFileProgress = 0;
public long CurrentFileProgress => _currentFileProgress;
public long FileCacheSize { get; set; }
@@ -100,7 +95,7 @@ public class PeriodicFileScanner : IDisposable
public string TimeUntilNextScan => _timeUntilNextScan.ToString(@"mm\:ss");
private TimeSpan _timeUntilNextScan = TimeSpan.Zero;
private int timeBetweenScans => _pluginConfiguration.TimeSpanBetweenScansInSeconds;
private int TimeBetweenScans => _configService.Current.TimeSpanBetweenScansInSeconds;
public void Dispose()
{
@@ -118,7 +113,7 @@ public class PeriodicFileScanner : IDisposable
{
bool isForced = forced;
TotalFiles = 0;
currentFileProgress = 0;
_currentFileProgress = 0;
_scanCancellationTokenSource?.Cancel();
_scanCancellationTokenSource = new CancellationTokenSource();
var token = _scanCancellationTokenSource.Token;
@@ -126,22 +121,22 @@ public class PeriodicFileScanner : IDisposable
{
while (!token.IsCancellationRequested)
{
while (haltScanLocks.Any(f => f.Value > 0))
while (haltScanLocks.Any(f => f.Value > 0) || !_ipcManager.CheckPenumbraApi())
{
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
}
isForced |= RecalculateFileCacheSize();
if (!_pluginConfiguration.FileScanPaused || isForced)
if (!_configService.Current.FileScanPaused || isForced)
{
isForced = false;
TotalFiles = 0;
currentFileProgress = 0;
_currentFileProgress = 0;
PeriodicFileScan(token);
TotalFiles = 0;
currentFileProgress = 0;
_currentFileProgress = 0;
}
_timeUntilNextScan = TimeSpan.FromSeconds(timeBetweenScans);
_timeUntilNextScan = TimeSpan.FromSeconds(TimeBetweenScans);
while (_timeUntilNextScan.TotalSeconds >= 0)
{
await Task.Delay(TimeSpan.FromSeconds(1), token).ConfigureAwait(false);
@@ -153,7 +148,7 @@ public class PeriodicFileScanner : IDisposable
public bool RecalculateFileCacheSize()
{
FileCacheSize = Directory.EnumerateFiles(_pluginConfiguration.CacheFolder).Sum(f =>
FileCacheSize = Directory.EnumerateFiles(_configService.Current.CacheFolder).Sum(f =>
{
try
{
@@ -165,13 +160,13 @@ public class PeriodicFileScanner : IDisposable
}
});
if (FileCacheSize < (long)_pluginConfiguration.MaxLocalCacheInGiB * 1024 * 1024 * 1024) return false;
if (FileCacheSize < (long)_configService.Current.MaxLocalCacheInGiB * 1024 * 1024 * 1024) return false;
var allFiles = Directory.EnumerateFiles(_pluginConfiguration.CacheFolder)
var allFiles = Directory.EnumerateFiles(_configService.Current.CacheFolder)
.Select(f => new FileInfo(f)).OrderBy(f => f.LastAccessTime).ToList();
while (FileCacheSize > (long)_pluginConfiguration.MaxLocalCacheInGiB * 1024 * 1024 * 1024)
while (FileCacheSize > (long)_configService.Current.MaxLocalCacheInGiB * 1024 * 1024 * 1024)
{
var oldestFile = allFiles.First();
var oldestFile = allFiles[0];
FileCacheSize -= oldestFile.Length;
File.Delete(oldestFile.FullName);
allFiles.Remove(oldestFile);
@@ -191,7 +186,7 @@ public class PeriodicFileScanner : IDisposable
penDirExists = false;
Logger.Warn("Penumbra directory is not set or does not exist.");
}
if (string.IsNullOrEmpty(_pluginConfiguration.CacheFolder) || !Directory.Exists(_pluginConfiguration.CacheFolder))
if (string.IsNullOrEmpty(_configService.Current.CacheFolder) || !Directory.Exists(_configService.Current.CacheFolder))
{
cacheDirExists = false;
Logger.Warn("Mare Cache directory is not set or does not exist.");
@@ -201,19 +196,19 @@ public class PeriodicFileScanner : IDisposable
return;
}
Logger.Debug("Getting files from " + penumbraDir + " and " + _pluginConfiguration.CacheFolder);
Logger.Debug("Getting files from " + penumbraDir + " and " + _configService.Current.CacheFolder);
string[] ext = { ".mdl", ".tex", ".mtrl", ".tmb", ".pap", ".avfx", ".atex", ".sklb", ".eid", ".phyb", ".scd", ".skp", ".shpk" };
var scannedFiles = new ConcurrentDictionary<string, bool>(Directory.EnumerateFiles(penumbraDir, "*.*", SearchOption.AllDirectories)
var scannedFiles = new ConcurrentDictionary<string, bool>(Directory.EnumerateFiles(penumbraDir!, "*.*", SearchOption.AllDirectories)
.Select(s => s.ToLowerInvariant())
.Where(f => ext.Any(e => f.EndsWith(e, StringComparison.OrdinalIgnoreCase))
&& !f.Contains(@"\bg\", StringComparison.OrdinalIgnoreCase)
&& !f.Contains(@"\bgcommon\", StringComparison.OrdinalIgnoreCase)
&& !f.Contains(@"\ui\", StringComparison.OrdinalIgnoreCase))
.Concat(Directory.EnumerateFiles(_pluginConfiguration.CacheFolder, "*.*", SearchOption.TopDirectoryOnly)
.Concat(Directory.EnumerateFiles(_configService.Current.CacheFolder, "*.*", SearchOption.TopDirectoryOnly)
.Where(f => new FileInfo(f).Name.Length == 40)
.Select(s => s.ToLowerInvariant()).ToList())
.Select(c => new KeyValuePair<string, bool>(c, false)), StringComparer.OrdinalIgnoreCase);
.Select(c => new KeyValuePair<string, bool>(c, value: false)), StringComparer.OrdinalIgnoreCase);
TotalFiles = scannedFiles.Count;
@@ -253,7 +248,7 @@ public class PeriodicFileScanner : IDisposable
Logger.Warn(ex.StackTrace);
}
Interlocked.Increment(ref currentFileProgress);
Interlocked.Increment(ref _currentFileProgress);
Thread.Sleep(1);
}, ct);
@@ -308,7 +303,7 @@ public class PeriodicFileScanner : IDisposable
Logger.Warn(ex.StackTrace);
}
Interlocked.Increment(ref currentFileProgress);
Interlocked.Increment(ref _currentFileProgress);
Thread.Sleep(1);
}, ct);
@@ -321,22 +316,22 @@ public class PeriodicFileScanner : IDisposable
Logger.Debug("Scan complete");
TotalFiles = 0;
currentFileProgress = 0;
_currentFileProgress = 0;
entitiesToRemove.Clear();
scannedFiles.Clear();
dbTasks = Array.Empty<Task>();
if (!_pluginConfiguration.InitialScanComplete)
if (!_configService.Current.InitialScanComplete)
{
_pluginConfiguration.InitialScanComplete = true;
_pluginConfiguration.Save();
_configService.Current.InitialScanComplete = true;
_configService.Save();
}
}
public void StartScan()
{
if (!_ipcManager.Initialized || !_pluginConfiguration.HasValidSetup()) return;
if (!_ipcManager.Initialized || !_configService.Current.HasValidSetup()) return;
Logger.Verbose("Penumbra is active, configuration is valid, scan");
InvokeScan(true);
InvokeScan(forced: true);
}
}