Refactored some functions
This commit is contained in:
@@ -18,6 +18,7 @@ public sealed class FileCacheManager : IHostedService
|
||||
public const string PenumbraPrefix = "{penumbra}";
|
||||
private const int FileCacheVersion = 1;
|
||||
private const string FileCacheVersionHeaderPrefix = "#lightless-file-cache-version:";
|
||||
private readonly SemaphoreSlim _fileWriteSemaphore = new(1, 1);
|
||||
private readonly LightlessConfigService _configService;
|
||||
private readonly LightlessMediator _lightlessMediator;
|
||||
private readonly string _csvPath;
|
||||
@@ -169,29 +170,55 @@ public sealed class FileCacheManager : IHostedService
|
||||
return CreateFileCacheEntity(fi, prefixedPath);
|
||||
}
|
||||
|
||||
public List<FileCacheEntity> GetAllFileCaches() => _fileCaches.Values.SelectMany(v => v.Values.Where(e => e != null)).ToList();
|
||||
public List<FileCacheEntity> GetAllFileCaches() => [.. _fileCaches.Values.SelectMany(v => v.Values.Where(e => e != null))];
|
||||
|
||||
public List<FileCacheEntity> GetAllFileCachesByHash(string hash, bool ignoreCacheEntries = false, bool validate = true)
|
||||
{
|
||||
List<FileCacheEntity> output = [];
|
||||
if (_fileCaches.TryGetValue(hash, out var fileCacheEntities))
|
||||
var output = new List<FileCacheEntity>();
|
||||
|
||||
if (!_fileCaches.TryGetValue(hash, out var fileCacheEntities))
|
||||
return output;
|
||||
|
||||
foreach (var fileCache in fileCacheEntities.Values
|
||||
.Where(c => !ignoreCacheEntries || !c.IsCacheEntry))
|
||||
{
|
||||
foreach (var fileCache in fileCacheEntities.Values.Where(c => !ignoreCacheEntries || !c.IsCacheEntry).ToList())
|
||||
if (!validate)
|
||||
{
|
||||
output.Add(fileCache);
|
||||
continue;
|
||||
}
|
||||
|
||||
var validated = GetValidatedFileCache(fileCache);
|
||||
if (validated != null)
|
||||
output.Add(validated);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public async Task<List<FileCacheEntity>> GetAllFileCachesByHashAsync(string hash, bool ignoreCacheEntries = false, bool validate = true,CancellationToken token = default)
|
||||
{
|
||||
var output = new List<FileCacheEntity>();
|
||||
|
||||
if (!_fileCaches.TryGetValue(hash, out var fileCacheEntities))
|
||||
return output;
|
||||
|
||||
foreach (var fileCache in fileCacheEntities.Values.Where(c => !ignoreCacheEntries || !c.IsCacheEntry))
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
if (!validate)
|
||||
{
|
||||
output.Add(fileCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
var validated = GetValidatedFileCache(fileCache);
|
||||
var validated = await GetValidatedFileCacheAsync(fileCache, token).ConfigureAwait(false);
|
||||
|
||||
if (validated != null)
|
||||
{
|
||||
output.Add(validated);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
@@ -479,6 +506,44 @@ public sealed class FileCacheManager : IHostedService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task WriteOutFullCsvAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _fileWriteSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine(BuildVersionHeader());
|
||||
|
||||
foreach (var entry in _fileCaches.Values
|
||||
.SelectMany(k => k.Values)
|
||||
.OrderBy(f => f.PrefixedFilePath, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
sb.AppendLine(entry.CsvEntry);
|
||||
}
|
||||
|
||||
if (File.Exists(_csvPath))
|
||||
{
|
||||
File.Copy(_csvPath, CsvBakPath, overwrite: true);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await File.WriteAllTextAsync(_csvPath, sb.ToString(), cancellationToken).ConfigureAwait(false);
|
||||
|
||||
File.Delete(CsvBakPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await File.WriteAllTextAsync(CsvBakPath, sb.ToString(), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_fileWriteSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureCsvHeaderLocked()
|
||||
{
|
||||
if (!File.Exists(_csvPath))
|
||||
@@ -601,6 +666,13 @@ public sealed class FileCacheManager : IHostedService
|
||||
return resultingFileCache;
|
||||
}
|
||||
|
||||
private async Task<FileCacheEntity?> GetValidatedFileCacheAsync(FileCacheEntity fileCache, CancellationToken token = default)
|
||||
{
|
||||
var resultingFileCache = ReplacePathPrefixes(fileCache);
|
||||
resultingFileCache = await ValidateAsync(resultingFileCache, token).ConfigureAwait(false);
|
||||
return resultingFileCache;
|
||||
}
|
||||
|
||||
private FileCacheEntity ReplacePathPrefixes(FileCacheEntity fileCache)
|
||||
{
|
||||
if (fileCache.PrefixedFilePath.StartsWith(PenumbraPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
@@ -623,6 +695,34 @@ public sealed class FileCacheManager : IHostedService
|
||||
RemoveHashedFile(fileCache.Hash, fileCache.PrefixedFilePath);
|
||||
return null;
|
||||
}
|
||||
|
||||
var file = new FileInfo(fileCache.ResolvedFilepath);
|
||||
if (!file.Exists)
|
||||
{
|
||||
RemoveHashedFile(fileCache.Hash, fileCache.PrefixedFilePath);
|
||||
return null;
|
||||
}
|
||||
|
||||
var lastWriteTicks = file.LastWriteTimeUtc.Ticks.ToString(CultureInfo.InvariantCulture);
|
||||
if (!string.Equals(lastWriteTicks, fileCache.LastModifiedDateTicks, StringComparison.Ordinal))
|
||||
{
|
||||
UpdateHashedFile(fileCache);
|
||||
}
|
||||
|
||||
return fileCache;
|
||||
}
|
||||
|
||||
private async Task<FileCacheEntity?> ValidateAsync(FileCacheEntity fileCache, CancellationToken token)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(fileCache.ResolvedFilepath))
|
||||
{
|
||||
_logger.LogWarning("FileCacheEntity has empty ResolvedFilepath for hash {hash}, prefixed path {prefixed}", fileCache.Hash, fileCache.PrefixedFilePath);
|
||||
RemoveHashedFile(fileCache.Hash, fileCache.PrefixedFilePath);
|
||||
return null;
|
||||
}
|
||||
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
var file = new FileInfo(fileCache.ResolvedFilepath);
|
||||
if (!file.Exists)
|
||||
{
|
||||
@@ -636,6 +736,7 @@ public sealed class FileCacheManager : IHostedService
|
||||
}
|
||||
|
||||
return fileCache;
|
||||
}, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
@@ -811,7 +912,7 @@ public sealed class FileCacheManager : IHostedService
|
||||
|
||||
if (rewriteRequired)
|
||||
{
|
||||
WriteOutFullCsv();
|
||||
await WriteOutFullCsvAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -822,7 +923,7 @@ public sealed class FileCacheManager : IHostedService
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
WriteOutFullCsv();
|
||||
await WriteOutFullCsvAsync(cancellationToken).ConfigureAwait(false);
|
||||
await Task.CompletedTask.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -396,8 +396,6 @@ public class BroadcastService : IHostedService, IMediatorSubscriber
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async void ToggleBroadcast()
|
||||
{
|
||||
|
||||
|
||||
@@ -54,25 +54,39 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
|
||||
var cancelToken = _analysisCts.Token;
|
||||
|
||||
var allFiles = LastAnalysis.SelectMany(v => v.Value.Select(d => d.Value)).ToList();
|
||||
if (allFiles.Exists(c => !c.IsComputed || recalculate))
|
||||
{
|
||||
|
||||
var remaining = allFiles.Where(c => !c.IsComputed || recalculate).ToList();
|
||||
|
||||
if (remaining.Count == 0)
|
||||
return;
|
||||
|
||||
TotalFiles = remaining.Count;
|
||||
CurrentFile = 1;
|
||||
CurrentFile = 0;
|
||||
|
||||
Logger.LogDebug("=== Computing {amount} remaining files ===", remaining.Count);
|
||||
|
||||
Mediator.Publish(new HaltScanMessage(nameof(CharacterAnalyzer)));
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var file in remaining)
|
||||
{
|
||||
Logger.LogDebug("Computing file {file}", file.FilePaths[0]);
|
||||
cancelToken.ThrowIfCancellationRequested();
|
||||
|
||||
var path = file.FilePaths.FirstOrDefault() ?? "<unknown>";
|
||||
Logger.LogDebug("Computing file {file}", path);
|
||||
|
||||
await file.ComputeSizes(_fileCacheManager, cancelToken).ConfigureAwait(false);
|
||||
|
||||
CurrentFile++;
|
||||
}
|
||||
|
||||
_fileCacheManager.WriteOutFullCsv();
|
||||
|
||||
await _fileCacheManager.WriteOutFullCsvAsync(cancelToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
Logger.LogInformation("File analysis cancelled");
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -82,7 +96,6 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
|
||||
{
|
||||
Mediator.Publish(new ResumeScanMessage(nameof(CharacterAnalyzer)));
|
||||
}
|
||||
}
|
||||
|
||||
RecalculateSummary();
|
||||
|
||||
@@ -113,7 +126,7 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
var fileCacheEntries = _fileCacheManager.GetAllFileCachesByHash(fileEntry.Hash, ignoreCacheEntries: true, validate: false).ToList();
|
||||
var fileCacheEntries = (await _fileCacheManager.GetAllFileCachesByHashAsync(fileEntry.Hash, ignoreCacheEntries: true, validate: false, token).ConfigureAwait(false)).ToList();
|
||||
if (fileCacheEntries.Count == 0) continue;
|
||||
|
||||
var filePath = fileCacheEntries[0].ResolvedFilepath;
|
||||
@@ -230,7 +243,7 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
|
||||
{
|
||||
var compressedsize = await fileCacheManager.GetCompressedFileData(Hash, token).ConfigureAwait(false);
|
||||
var normalSize = new FileInfo(FilePaths[0]).Length;
|
||||
var entries = fileCacheManager.GetAllFileCachesByHash(Hash, ignoreCacheEntries: true, validate: false);
|
||||
var entries = await fileCacheManager.GetAllFileCachesByHashAsync(Hash, ignoreCacheEntries: true, validate: false, token).ConfigureAwait(false);
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
entry.Size = normalSize;
|
||||
|
||||
@@ -675,76 +675,75 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||
_lastGlobalBlockReason = string.Empty;
|
||||
}
|
||||
|
||||
if (_clientState.IsGPosing && !IsInGpose)
|
||||
{
|
||||
_logger.LogDebug("Gpose start");
|
||||
IsInGpose = true;
|
||||
Mediator.Publish(new GposeStartMessage());
|
||||
}
|
||||
else if (!_clientState.IsGPosing && IsInGpose)
|
||||
{
|
||||
_logger.LogDebug("Gpose end");
|
||||
IsInGpose = false;
|
||||
Mediator.Publish(new GposeEndMessage());
|
||||
}
|
||||
// Checks on conditions
|
||||
var shouldBeInGpose = _clientState.IsGPosing;
|
||||
var shouldBeInCombat = _condition[ConditionFlag.InCombat] && !IsInInstance && _playerPerformanceConfigService.Current.PauseInCombat;
|
||||
var shouldBePerforming = _condition[ConditionFlag.Performing] && _playerPerformanceConfigService.Current.PauseWhilePerforming;
|
||||
var shouldBeInInstance = _condition[ConditionFlag.BoundByDuty] && _playerPerformanceConfigService.Current.PauseInInstanceDuty;
|
||||
var shouldBeInCutscene = _condition[ConditionFlag.WatchingCutscene];
|
||||
|
||||
if ((_condition[ConditionFlag.InCombat]) && !IsInCombat && !IsInInstance && _playerPerformanceConfigService.Current.PauseInCombat)
|
||||
// Gpose
|
||||
HandleStateTransition(() => IsInGpose, v => IsInGpose = v, shouldBeInGpose, "Gpose",
|
||||
onEnter: () =>
|
||||
{
|
||||
Mediator.Publish(new GposeStartMessage());
|
||||
},
|
||||
onExit: () =>
|
||||
{
|
||||
Mediator.Publish(new GposeEndMessage());
|
||||
});
|
||||
|
||||
// Combat
|
||||
HandleStateTransition(() => IsInCombat, v => IsInCombat = v, shouldBeInCombat, "Combat",
|
||||
onEnter: () =>
|
||||
{
|
||||
_logger.LogDebug("Combat start");
|
||||
IsInCombat = true;
|
||||
Mediator.Publish(new CombatStartMessage());
|
||||
Mediator.Publish(new HaltScanMessage(nameof(IsInCombat)));
|
||||
}
|
||||
else if ((!_condition[ConditionFlag.InCombat]) && IsInCombat && !IsInInstance && _playerPerformanceConfigService.Current.PauseInCombat)
|
||||
},
|
||||
onExit: () =>
|
||||
{
|
||||
_logger.LogDebug("Combat end");
|
||||
IsInCombat = false;
|
||||
Mediator.Publish(new CombatEndMessage());
|
||||
Mediator.Publish(new ResumeScanMessage(nameof(IsInCombat)));
|
||||
}
|
||||
if (_condition[ConditionFlag.Performing] && !IsPerforming && _playerPerformanceConfigService.Current.PauseWhilePerforming)
|
||||
});
|
||||
|
||||
// Performance
|
||||
HandleStateTransition(() => IsPerforming, v => IsPerforming = v, shouldBePerforming, "Performance",
|
||||
onEnter: () =>
|
||||
{
|
||||
_logger.LogDebug("Performance start");
|
||||
IsInCombat = true;
|
||||
Mediator.Publish(new PerformanceStartMessage());
|
||||
Mediator.Publish(new HaltScanMessage(nameof(IsPerforming)));
|
||||
}
|
||||
else if (!_condition[ConditionFlag.Performing] && IsPerforming && _playerPerformanceConfigService.Current.PauseWhilePerforming)
|
||||
},
|
||||
onExit: () =>
|
||||
{
|
||||
_logger.LogDebug("Performance end");
|
||||
IsInCombat = false;
|
||||
Mediator.Publish(new PerformanceEndMessage());
|
||||
Mediator.Publish(new ResumeScanMessage(nameof(IsPerforming)));
|
||||
}
|
||||
if ((_condition[ConditionFlag.BoundByDuty]) && !IsInInstance && _playerPerformanceConfigService.Current.PauseInInstanceDuty)
|
||||
});
|
||||
|
||||
// Instance / Duty
|
||||
HandleStateTransition(() => IsInInstance, v => IsInInstance = v, shouldBeInInstance, "Instance",
|
||||
onEnter: () =>
|
||||
{
|
||||
_logger.LogDebug("Instance start");
|
||||
IsInInstance = true;
|
||||
Mediator.Publish(new InstanceOrDutyStartMessage());
|
||||
Mediator.Publish(new HaltScanMessage(nameof(IsInInstance)));
|
||||
}
|
||||
else if (((!_condition[ConditionFlag.BoundByDuty]) && IsInInstance && _playerPerformanceConfigService.Current.PauseInInstanceDuty) || ((_condition[ConditionFlag.BoundByDuty]) && IsInInstance && !_playerPerformanceConfigService.Current.PauseInInstanceDuty))
|
||||
},
|
||||
onExit: () =>
|
||||
{
|
||||
_logger.LogDebug("Instance end");
|
||||
IsInInstance = false;
|
||||
Mediator.Publish(new InstanceOrDutyEndMessage());
|
||||
Mediator.Publish(new ResumeScanMessage(nameof(IsInInstance)));
|
||||
}
|
||||
});
|
||||
|
||||
if (_condition[ConditionFlag.WatchingCutscene] && !IsInCutscene)
|
||||
// Cutscene
|
||||
HandleStateTransition(() => IsInCutscene,v => IsInCutscene = v, shouldBeInCutscene, "Cutscene",
|
||||
onEnter: () =>
|
||||
{
|
||||
_logger.LogDebug("Cutscene start");
|
||||
IsInCutscene = true;
|
||||
Mediator.Publish(new CutsceneStartMessage());
|
||||
Mediator.Publish(new HaltScanMessage(nameof(IsInCutscene)));
|
||||
}
|
||||
else if (!_condition[ConditionFlag.WatchingCutscene] && IsInCutscene)
|
||||
},
|
||||
onExit: () =>
|
||||
{
|
||||
_logger.LogDebug("Cutscene end");
|
||||
IsInCutscene = false;
|
||||
Mediator.Publish(new CutsceneEndMessage());
|
||||
Mediator.Publish(new ResumeScanMessage(nameof(IsInCutscene)));
|
||||
}
|
||||
});
|
||||
|
||||
if (IsInCutscene)
|
||||
{
|
||||
@@ -821,4 +820,31 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||
_delayedFrameworkUpdateCheck = DateTime.UtcNow;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for the transition of different states of game
|
||||
/// </summary>
|
||||
/// <param name="getState">Get state of condition</param>
|
||||
/// <param name="setState">Set state of condition</param>
|
||||
/// <param name="shouldBeActive">Correction of the state of the condition</param>
|
||||
/// <param name="stateName">Condition name</param>
|
||||
/// <param name="onEnter">Function for on entering the state</param>
|
||||
/// <param name="onExit">Function for on leaving the state</param>
|
||||
private void HandleStateTransition(Func<bool> getState, Action<bool> setState, bool shouldBeActive, string stateName, System.Action onEnter, System.Action onExit)
|
||||
{
|
||||
var isActive = getState();
|
||||
|
||||
if (shouldBeActive && !isActive)
|
||||
{
|
||||
_logger.LogDebug("{stateName} start", stateName);
|
||||
setState(true);
|
||||
onEnter();
|
||||
}
|
||||
else if (!shouldBeActive && isActive)
|
||||
{
|
||||
_logger.LogDebug("{stateName} end", stateName);
|
||||
setState(false);
|
||||
onExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user