Merge tag '0.9.21' into mare-classic
* tag '0.9.21': fix combat situations not redrawing every time after combat ends add more resilience to MCDF export and loading disable data application and scanner in combat fix bug add lock around adding to cached handled paths disable target in pvp add click to target in ui change tooltip for penumbra version to 0.8.2.1 add file storage validation add experimental resolving of data through penumbra adjust initial dialog to opt in/out into census with buttons
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
using LZ4;
|
using LZ4;
|
||||||
using MareSynchronos.Interop;
|
using MareSynchronos.Interop;
|
||||||
using MareSynchronos.MareConfiguration;
|
using MareSynchronos.MareConfiguration;
|
||||||
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
@@ -15,17 +16,19 @@ public sealed class FileCacheManager : IDisposable
|
|||||||
public const string CsvSplit = "|";
|
public const string CsvSplit = "|";
|
||||||
public const string PenumbraPrefix = "{penumbra}";
|
public const string PenumbraPrefix = "{penumbra}";
|
||||||
private readonly MareConfigService _configService;
|
private readonly MareConfigService _configService;
|
||||||
|
private readonly MareMediator _mareMediator;
|
||||||
private readonly string _csvPath;
|
private readonly string _csvPath;
|
||||||
private readonly ConcurrentDictionary<string, List<FileCacheEntity>> _fileCaches = new(StringComparer.Ordinal);
|
private readonly ConcurrentDictionary<string, List<FileCacheEntity>> _fileCaches = new(StringComparer.Ordinal);
|
||||||
private readonly object _fileWriteLock = new();
|
private readonly object _fileWriteLock = new();
|
||||||
private readonly IpcManager _ipcManager;
|
private readonly IpcManager _ipcManager;
|
||||||
private readonly ILogger<FileCacheManager> _logger;
|
private readonly ILogger<FileCacheManager> _logger;
|
||||||
|
|
||||||
public FileCacheManager(ILogger<FileCacheManager> logger, IpcManager ipcManager, MareConfigService configService)
|
public FileCacheManager(ILogger<FileCacheManager> logger, IpcManager ipcManager, MareConfigService configService, MareMediator mareMediator)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_ipcManager = ipcManager;
|
_ipcManager = ipcManager;
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
|
_mareMediator = mareMediator;
|
||||||
_csvPath = Path.Combine(configService.ConfigurationDirectory, "FileCache.csv");
|
_csvPath = Path.Combine(configService.ConfigurationDirectory, "FileCache.csv");
|
||||||
|
|
||||||
lock (_fileWriteLock)
|
lock (_fileWriteLock)
|
||||||
@@ -172,6 +175,47 @@ public sealed class FileCacheManager : IDisposable
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<List<FileCacheEntity>> ValidateLocalIntegrity(IProgress<(int, int, FileCacheEntity)> progress, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_mareMediator.Publish(new HaltScanMessage(nameof(ValidateLocalIntegrity)));
|
||||||
|
_logger.LogInformation("Validating local storage");
|
||||||
|
var cacheEntries = _fileCaches.SelectMany(v => v.Value).Where(v => v.IsCacheEntry).ToList();
|
||||||
|
List<FileCacheEntity> brokenEntities = new();
|
||||||
|
int i = 0;
|
||||||
|
foreach (var fileCache in cacheEntries)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Validating {file}", fileCache.ResolvedFilepath);
|
||||||
|
|
||||||
|
progress.Report((i, cacheEntries.Count, fileCache));
|
||||||
|
i++;
|
||||||
|
var computedHash = Crypto.GetFileHash(fileCache.ResolvedFilepath);
|
||||||
|
if (!string.Equals(computedHash, fileCache.Hash, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Failed to validate {file}, got hash {hash}, expected hash {hash}", fileCache.ResolvedFilepath, computedHash, fileCache.Hash);
|
||||||
|
brokenEntities.Add(fileCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancellationToken.IsCancellationRequested) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var brokenEntity in brokenEntities)
|
||||||
|
{
|
||||||
|
RemoveHashedFile(brokenEntity.Hash, brokenEntity.PrefixedFilePath);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Delete(brokenEntity.ResolvedFilepath);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Could not delete {file}", brokenEntity.ResolvedFilepath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_mareMediator.Publish(new ResumeScanMessage(nameof(ValidateLocalIntegrity)));
|
||||||
|
return Task.FromResult(brokenEntities);
|
||||||
|
}
|
||||||
|
|
||||||
public string GetCacheFilePath(string hash, string extension)
|
public string GetCacheFilePath(string hash, string extension)
|
||||||
{
|
{
|
||||||
return Path.Combine(_configService.Current.CacheFolder, hash + "." + extension);
|
return Path.Combine(_configService.Current.CacheFolder, hash + "." + extension);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
|
|||||||
private readonly string[] _fileTypesToHandle = ["tmb", "pap", "avfx", "atex", "sklb", "eid", "phyb", "scd", "skp", "shpk"];
|
private readonly string[] _fileTypesToHandle = ["tmb", "pap", "avfx", "atex", "sklb", "eid", "phyb", "scd", "skp", "shpk"];
|
||||||
private readonly HashSet<GameObjectHandler> _playerRelatedPointers = [];
|
private readonly HashSet<GameObjectHandler> _playerRelatedPointers = [];
|
||||||
private HashSet<IntPtr> _cachedFrameAddresses = [];
|
private HashSet<IntPtr> _cachedFrameAddresses = [];
|
||||||
|
private readonly object _cacheAdditionLock = new();
|
||||||
|
|
||||||
public TransientResourceManager(ILogger<TransientResourceManager> logger, TransientConfigService configurationService,
|
public TransientResourceManager(ILogger<TransientResourceManager> logger, TransientConfigService configurationService,
|
||||||
DalamudUtilService dalamudUtil, MareMediator mediator) : base(logger, mediator)
|
DalamudUtilService dalamudUtil, MareMediator mediator) : base(logger, mediator)
|
||||||
@@ -29,7 +30,7 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
|
|||||||
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => DalamudUtil_FrameworkUpdate());
|
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => DalamudUtil_FrameworkUpdate());
|
||||||
Mediator.Subscribe<ClassJobChangedMessage>(this, (msg) =>
|
Mediator.Subscribe<ClassJobChangedMessage>(this, (msg) =>
|
||||||
{
|
{
|
||||||
if (_playerRelatedPointers.Contains(msg.gameObjectHandler))
|
if (_playerRelatedPointers.Contains(msg.GameObjectHandler))
|
||||||
{
|
{
|
||||||
DalamudUtil_ClassJobChanged();
|
DalamudUtil_ClassJobChanged();
|
||||||
}
|
}
|
||||||
@@ -189,7 +190,10 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
|
|||||||
private void DalamudUtil_FrameworkUpdate()
|
private void DalamudUtil_FrameworkUpdate()
|
||||||
{
|
{
|
||||||
_cachedFrameAddresses = _playerRelatedPointers.Select(c => c.CurrentAddress()).ToHashSet();
|
_cachedFrameAddresses = _playerRelatedPointers.Select(c => c.CurrentAddress()).ToHashSet();
|
||||||
_cachedHandledPaths.Clear();
|
lock (_cacheAdditionLock)
|
||||||
|
{
|
||||||
|
_cachedHandledPaths.Clear();
|
||||||
|
}
|
||||||
foreach (var item in TransientResources.Where(item => !_dalamudUtil.IsGameObjectPresent(item.Key)).Select(i => i.Key).ToList())
|
foreach (var item in TransientResources.Where(item => !_dalamudUtil.IsGameObjectPresent(item.Key)).Select(i => i.Key).ToList())
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Object not present anymore: {addr}", item.ToString("X"));
|
Logger.LogDebug("Object not present anymore: {addr}", item.ToString("X"));
|
||||||
@@ -218,7 +222,10 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
|
|||||||
// ignore files already processed this frame
|
// ignore files already processed this frame
|
||||||
if (_cachedHandledPaths.Contains(gamePath)) return;
|
if (_cachedHandledPaths.Contains(gamePath)) return;
|
||||||
|
|
||||||
_cachedHandledPaths.Add(gamePath);
|
lock (_cacheAdditionLock)
|
||||||
|
{
|
||||||
|
_cachedHandledPaths.Add(gamePath);
|
||||||
|
}
|
||||||
|
|
||||||
// replace individual mtrl stuff
|
// replace individual mtrl stuff
|
||||||
if (filePath.StartsWith("|", StringComparison.OrdinalIgnoreCase))
|
if (filePath.StartsWith("|", StringComparison.OrdinalIgnoreCase))
|
||||||
@@ -235,14 +242,20 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
|
|||||||
// ignore files to not handle
|
// ignore files to not handle
|
||||||
if (!_fileTypesToHandle.Any(type => gamePath.EndsWith(type, StringComparison.OrdinalIgnoreCase)))
|
if (!_fileTypesToHandle.Any(type => gamePath.EndsWith(type, StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
_cachedHandledPaths.Add(gamePath);
|
lock (_cacheAdditionLock)
|
||||||
|
{
|
||||||
|
_cachedHandledPaths.Add(gamePath);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore files not belonging to anything player related
|
// ignore files not belonging to anything player related
|
||||||
if (!_cachedFrameAddresses.Contains(gameObject))
|
if (!_cachedFrameAddresses.Contains(gameObject))
|
||||||
{
|
{
|
||||||
_cachedHandledPaths.Add(gamePath);
|
lock (_cacheAdditionLock)
|
||||||
|
{
|
||||||
|
_cachedHandledPaths.Add(gamePath);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -277,12 +277,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||||
if (gameObj is Character c)
|
if (gameObj is Character c)
|
||||||
{
|
{
|
||||||
var glamourerString = _glamourerGetAllCustomization!.InvokeFunc(c);
|
return _glamourerGetAllCustomization!.InvokeFunc(c);
|
||||||
byte[] bytes = Convert.FromBase64String(glamourerString);
|
|
||||||
// ignore transparency
|
|
||||||
bytes[88] = 128;
|
|
||||||
bytes[89] = 63;
|
|
||||||
return Convert.ToBase64String(bytes);
|
|
||||||
}
|
}
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
@@ -497,7 +492,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
if (!CheckPenumbraApi()) return;
|
if (!CheckPenumbraApi()) return;
|
||||||
|
|
||||||
Mediator.Publish(new HaltScanMessage("TextureConversion"));
|
Mediator.Publish(new HaltScanMessage(nameof(PenumbraConvertTextureFiles)));
|
||||||
int currentTexture = 0;
|
int currentTexture = 0;
|
||||||
foreach (var texture in textures)
|
foreach (var texture in textures)
|
||||||
{
|
{
|
||||||
@@ -524,7 +519,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Mediator.Publish(new ResumeScanMessage("TextureConversion"));
|
Mediator.Publish(new ResumeScanMessage(nameof(PenumbraConvertTextureFiles)));
|
||||||
|
|
||||||
await _dalamudUtil.RunOnFrameworkThread(async () =>
|
await _dalamudUtil.RunOnFrameworkThread(async () =>
|
||||||
{
|
{
|
||||||
@@ -667,7 +662,10 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var version = _glamourerApiVersions.InvokeFunc();
|
var version = _glamourerApiVersions.InvokeFunc();
|
||||||
if (version.Item1 == 0 && version.Item2 >= 1)
|
bool versionValid = (_pi.InstalledPlugins
|
||||||
|
.FirstOrDefault(p => string.Equals(p.InternalName, "Glamourer", StringComparison.OrdinalIgnoreCase))
|
||||||
|
?.Version ?? new Version(0, 0, 0, 0)) >= new Version(1, 0, 6, 1);
|
||||||
|
if (version.Item1 == 0 && version.Item2 >= 1 && versionValid)
|
||||||
{
|
{
|
||||||
apiAvailable = true;
|
apiAvailable = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ public class MareConfig : IMareConfiguration
|
|||||||
public int TransferBarsWidth { get; set; } = 250;
|
public int TransferBarsWidth { get; set; } = 250;
|
||||||
public bool UseAlternativeFileUpload { get; set; } = false;
|
public bool UseAlternativeFileUpload { get; set; } = false;
|
||||||
public bool UseCompactor { get; set; } = false;
|
public bool UseCompactor { get; set; } = false;
|
||||||
|
public bool ExperimentalUsePenumbraResourceTree { get; set; } = false;
|
||||||
public int Version { get; set; } = 1;
|
public int Version { get; set; } = 1;
|
||||||
public NotificationLocation WarningNotification { get; set; } = NotificationLocation.Both;
|
public NotificationLocation WarningNotification { get; set; } = NotificationLocation.Both;
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ public class MareCharaFileManager : DisposableMediatorSubscriberBase
|
|||||||
public bool CurrentlyWorking { get; private set; } = false;
|
public bool CurrentlyWorking { get; private set; } = false;
|
||||||
public MareCharaFileHeader? LoadedCharaFile { get; private set; }
|
public MareCharaFileHeader? LoadedCharaFile { get; private set; }
|
||||||
|
|
||||||
public async Task ApplyMareCharaFile(GameObject? charaTarget)
|
public async Task ApplyMareCharaFile(GameObject? charaTarget, long expectedLength)
|
||||||
{
|
{
|
||||||
if (charaTarget == null) return;
|
if (charaTarget == null) return;
|
||||||
Dictionary<string, string> extractedFiles = new(StringComparer.Ordinal);
|
Dictionary<string, string> extractedFiles = new(StringComparer.Ordinal);
|
||||||
@@ -80,8 +80,8 @@ public class MareCharaFileManager : DisposableMediatorSubscriberBase
|
|||||||
using var lz4Stream = new LZ4Stream(unwrapped, LZ4StreamMode.Decompress, LZ4StreamFlags.HighCompression);
|
using var lz4Stream = new LZ4Stream(unwrapped, LZ4StreamMode.Decompress, LZ4StreamFlags.HighCompression);
|
||||||
using var reader = new BinaryReader(lz4Stream);
|
using var reader = new BinaryReader(lz4Stream);
|
||||||
MareCharaFileHeader.AdvanceReaderToData(reader);
|
MareCharaFileHeader.AdvanceReaderToData(reader);
|
||||||
_logger.LogDebug("Applying to {chara}", charaTarget.Name.TextValue);
|
_logger.LogDebug("Applying to {chara}, expected length of contents: {exp}", charaTarget.Name.TextValue, expectedLength);
|
||||||
extractedFiles = ExtractFilesFromCharaFile(LoadedCharaFile, reader);
|
extractedFiles = ExtractFilesFromCharaFile(LoadedCharaFile, reader, expectedLength);
|
||||||
Dictionary<string, string> fileSwaps = new(StringComparer.Ordinal);
|
Dictionary<string, string> fileSwaps = new(StringComparer.Ordinal);
|
||||||
foreach (var fileSwap in LoadedCharaFile.CharaFileData.FileSwaps)
|
foreach (var fileSwap in LoadedCharaFile.CharaFileData.FileSwaps)
|
||||||
{
|
{
|
||||||
@@ -125,14 +125,19 @@ public class MareCharaFileManager : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Failure to read MCDF");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
CurrentlyWorking = false;
|
CurrentlyWorking = false;
|
||||||
|
|
||||||
_logger.LogDebug("Clearing local files");
|
_logger.LogDebug("Clearing local files");
|
||||||
foreach (var file in extractedFiles)
|
foreach (var file in Directory.EnumerateFiles(_configService.Current.CacheFolder, "*.tmp"))
|
||||||
{
|
{
|
||||||
File.Delete(file.Value);
|
File.Delete(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,7 +147,7 @@ public class MareCharaFileManager : DisposableMediatorSubscriberBase
|
|||||||
LoadedCharaFile = null;
|
LoadedCharaFile = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadMareCharaFile(string filePath)
|
public long LoadMareCharaFile(string filePath)
|
||||||
{
|
{
|
||||||
CurrentlyWorking = true;
|
CurrentlyWorking = true;
|
||||||
try
|
try
|
||||||
@@ -179,6 +184,7 @@ public class MareCharaFileManager : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
_logger.LogInformation("Expected length: {expected}", expectedLength);
|
_logger.LogInformation("Expected length: {expected}", expectedLength);
|
||||||
}
|
}
|
||||||
|
return expectedLength;
|
||||||
}
|
}
|
||||||
finally { CurrentlyWorking = false; }
|
finally { CurrentlyWorking = false; }
|
||||||
}
|
}
|
||||||
@@ -214,34 +220,38 @@ public class MareCharaFileManager : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failure Saving Mare Chara File, deleting output");
|
||||||
|
File.Delete(filePath);
|
||||||
|
}
|
||||||
finally { CurrentlyWorking = false; }
|
finally { CurrentlyWorking = false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dictionary<string, string> ExtractFilesFromCharaFile(MareCharaFileHeader charaFileHeader, BinaryReader reader)
|
private Dictionary<string, string> ExtractFilesFromCharaFile(MareCharaFileHeader charaFileHeader, BinaryReader reader, long expectedLength)
|
||||||
{
|
{
|
||||||
|
long totalRead = 0;
|
||||||
Dictionary<string, string> gamePathToFilePath = new(StringComparer.Ordinal);
|
Dictionary<string, string> gamePathToFilePath = new(StringComparer.Ordinal);
|
||||||
foreach (var fileData in charaFileHeader.CharaFileData.Files)
|
foreach (var fileData in charaFileHeader.CharaFileData.Files)
|
||||||
{
|
{
|
||||||
var fileName = Path.Combine(_configService.Current.CacheFolder, "mare_" + _globalFileCounter++ + ".tmp");
|
var fileName = Path.Combine(_configService.Current.CacheFolder, "mare_" + _globalFileCounter++ + ".tmp");
|
||||||
var length = fileData.Length;
|
var length = (int)fileData.Length;
|
||||||
var bufferSize = 4 * 1024 * 1024;
|
var bufferSize = length;
|
||||||
using var fs = File.OpenWrite(fileName);
|
using var fs = File.OpenWrite(fileName);
|
||||||
using var wr = new BinaryWriter(fs);
|
using var wr = new BinaryWriter(fs);
|
||||||
int chunk = 0;
|
_logger.LogTrace("Reading {length} of {fileName}", length, fileName);
|
||||||
while (length > 0)
|
var buffer = reader.ReadBytes(bufferSize);
|
||||||
{
|
wr.Write(buffer);
|
||||||
if (length < bufferSize) bufferSize = (int)length;
|
|
||||||
_logger.LogTrace("Reading chunk {chunk} {bufferSize}/{length} of {fileName}", chunk++, bufferSize, length, fileName);
|
|
||||||
var buffer = reader.ReadBytes(bufferSize);
|
|
||||||
wr.Write(length > bufferSize ? buffer : buffer.Take((int)length).ToArray());
|
|
||||||
length -= bufferSize;
|
|
||||||
}
|
|
||||||
wr.Flush();
|
wr.Flush();
|
||||||
|
wr.Close();
|
||||||
|
if (buffer.Length == 0) throw new EndOfStreamException("Unexpected EOF");
|
||||||
foreach (var path in fileData.GamePaths)
|
foreach (var path in fileData.GamePaths)
|
||||||
{
|
{
|
||||||
gamePathToFilePath[path] = fileName;
|
gamePathToFilePath[path] = fileName;
|
||||||
_logger.LogTrace("{path} => {fileName}", path, fileName);
|
_logger.LogTrace("{path} => {fileName}", path, fileName);
|
||||||
}
|
}
|
||||||
|
totalRead += length;
|
||||||
|
_logger.LogTrace("Read {read}/{expected} bytes", totalRead, expectedLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
return gamePathToFilePath;
|
return gamePathToFilePath;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using MareSynchronos.API.Dto.User;
|
using MareSynchronos.API.Dto.User;
|
||||||
using MareSynchronos.FileCache;
|
using MareSynchronos.FileCache;
|
||||||
using MareSynchronos.Interop;
|
using MareSynchronos.Interop;
|
||||||
using MareSynchronos.MareConfiguration;
|
|
||||||
using MareSynchronos.PlayerData.Handlers;
|
using MareSynchronos.PlayerData.Handlers;
|
||||||
using MareSynchronos.PlayerData.Pairs;
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
using MareSynchronos.Services;
|
using MareSynchronos.Services;
|
||||||
@@ -21,13 +20,12 @@ public class PairHandlerFactory
|
|||||||
private readonly IpcManager _ipcManager;
|
private readonly IpcManager _ipcManager;
|
||||||
private readonly ILoggerFactory _loggerFactory;
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
private readonly MareMediator _mareMediator;
|
private readonly MareMediator _mareMediator;
|
||||||
private readonly MareConfigService _mareConfigService;
|
|
||||||
private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
|
private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
|
||||||
|
|
||||||
public PairHandlerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager,
|
public PairHandlerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager,
|
||||||
FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService,
|
FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService,
|
||||||
PluginWarningNotificationService pluginWarningNotificationManager, IHostApplicationLifetime hostApplicationLifetime,
|
PluginWarningNotificationService pluginWarningNotificationManager, IHostApplicationLifetime hostApplicationLifetime,
|
||||||
FileCacheManager fileCacheManager, MareMediator mareMediator, MareConfigService mareConfigService)
|
FileCacheManager fileCacheManager, MareMediator mareMediator)
|
||||||
{
|
{
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
||||||
@@ -38,13 +36,12 @@ public class PairHandlerFactory
|
|||||||
_hostApplicationLifetime = hostApplicationLifetime;
|
_hostApplicationLifetime = hostApplicationLifetime;
|
||||||
_fileCacheManager = fileCacheManager;
|
_fileCacheManager = fileCacheManager;
|
||||||
_mareMediator = mareMediator;
|
_mareMediator = mareMediator;
|
||||||
_mareConfigService = mareConfigService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public PairHandler Create(OnlineUserIdentDto onlineUserIdentDto)
|
public PairHandler Create(OnlineUserIdentDto onlineUserIdentDto)
|
||||||
{
|
{
|
||||||
return new PairHandler(_loggerFactory.CreateLogger<PairHandler>(), onlineUserIdentDto, _gameObjectHandlerFactory,
|
return new PairHandler(_loggerFactory.CreateLogger<PairHandler>(), onlineUserIdentDto, _gameObjectHandlerFactory,
|
||||||
_ipcManager, _fileDownloadManagerFactory.Create(), _pluginWarningNotificationManager, _dalamudUtilService, _hostApplicationLifetime,
|
_ipcManager, _fileDownloadManagerFactory.Create(), _pluginWarningNotificationManager, _dalamudUtilService, _hostApplicationLifetime,
|
||||||
_fileCacheManager, _mareMediator, _mareConfigService);
|
_fileCacheManager, _mareMediator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
|||||||
using MareSynchronos.API.Data.Enum;
|
using MareSynchronos.API.Data.Enum;
|
||||||
using MareSynchronos.FileCache;
|
using MareSynchronos.FileCache;
|
||||||
using MareSynchronos.Interop;
|
using MareSynchronos.Interop;
|
||||||
|
using MareSynchronos.MareConfiguration;
|
||||||
using MareSynchronos.PlayerData.Data;
|
using MareSynchronos.PlayerData.Data;
|
||||||
using MareSynchronos.PlayerData.Handlers;
|
using MareSynchronos.PlayerData.Handlers;
|
||||||
using MareSynchronos.Services;
|
using MareSynchronos.Services;
|
||||||
@@ -25,11 +26,12 @@ public class PlayerDataFactory
|
|||||||
private readonly IpcManager _ipcManager;
|
private readonly IpcManager _ipcManager;
|
||||||
private readonly ILogger<PlayerDataFactory> _logger;
|
private readonly ILogger<PlayerDataFactory> _logger;
|
||||||
private readonly PerformanceCollectorService _performanceCollector;
|
private readonly PerformanceCollectorService _performanceCollector;
|
||||||
|
private readonly MareConfigService _mareConfigService;
|
||||||
private readonly TransientResourceManager _transientResourceManager;
|
private readonly TransientResourceManager _transientResourceManager;
|
||||||
|
|
||||||
public PlayerDataFactory(ILogger<PlayerDataFactory> logger, DalamudUtilService dalamudUtil, IpcManager ipcManager,
|
public PlayerDataFactory(ILogger<PlayerDataFactory> logger, DalamudUtilService dalamudUtil, IpcManager ipcManager,
|
||||||
TransientResourceManager transientResourceManager, FileCacheManager fileReplacementFactory,
|
TransientResourceManager transientResourceManager, FileCacheManager fileReplacementFactory,
|
||||||
PerformanceCollectorService performanceCollector)
|
PerformanceCollectorService performanceCollector, MareConfigService mareConfigService)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_dalamudUtil = dalamudUtil;
|
_dalamudUtil = dalamudUtil;
|
||||||
@@ -37,7 +39,7 @@ public class PlayerDataFactory
|
|||||||
_transientResourceManager = transientResourceManager;
|
_transientResourceManager = transientResourceManager;
|
||||||
_fileCacheManager = fileReplacementFactory;
|
_fileCacheManager = fileReplacementFactory;
|
||||||
_performanceCollector = performanceCollector;
|
_performanceCollector = performanceCollector;
|
||||||
|
_mareConfigService = mareConfigService;
|
||||||
_logger.LogTrace("Creating " + nameof(PlayerDataFactory));
|
_logger.LogTrace("Creating " + nameof(PlayerDataFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +283,7 @@ public class PlayerDataFactory
|
|||||||
AddReplacementsFromRenderModel(mdl, forwardResolve, reverseResolve);
|
AddReplacementsFromRenderModel(mdl, forwardResolve, reverseResolve);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (objectKind == ObjectKind.Player)
|
if (objectKind == ObjectKind.Player && human->CharacterBase.GetModelType() == CharacterBase.ModelType.Human)
|
||||||
{
|
{
|
||||||
AddPlayerSpecificReplacements(human, forwardResolve, reverseResolve);
|
AddPlayerSpecificReplacements(human, forwardResolve, reverseResolve);
|
||||||
}
|
}
|
||||||
@@ -330,12 +332,18 @@ public class PlayerDataFactory
|
|||||||
Stopwatch st = Stopwatch.StartNew();
|
Stopwatch st = Stopwatch.StartNew();
|
||||||
|
|
||||||
// penumbra call, it's currently broken
|
// penumbra call, it's currently broken
|
||||||
// var data = (await _ipcManager.PenumbraGetCharacterData(_logger, playerRelatedObject).ConfigureAwait(false))![0];
|
IReadOnlyDictionary<string, string[]>? resolvedPaths;
|
||||||
// if (data == null) throw new InvalidOperationException("Penumbra returned null data");
|
if (_mareConfigService.Current.ExperimentalUsePenumbraResourceTree)
|
||||||
|
{
|
||||||
// gather static replacements from render model
|
resolvedPaths = (await _ipcManager.PenumbraGetCharacterData(_logger, playerRelatedObject).ConfigureAwait(false))![0];
|
||||||
var (forwardResolve, reverseResolve) = await _dalamudUtil.RunOnFrameworkThread(() => BuildDataFromModel(objectKind, charaPointer, token)).ConfigureAwait(false);
|
if (resolvedPaths == null) throw new InvalidOperationException("Penumbra returned null data");
|
||||||
Dictionary<string, List<string>> resolvedPaths = await GetFileReplacementsFromPaths(forwardResolve, reverseResolve).ConfigureAwait(false);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// gather static replacements from render model
|
||||||
|
var (forwardResolve, reverseResolve) = await _dalamudUtil.RunOnFrameworkThread(() => BuildDataFromModel(objectKind, charaPointer, token)).ConfigureAwait(false);
|
||||||
|
resolvedPaths = await GetFileReplacementsFromPaths(forwardResolve, reverseResolve).ConfigureAwait(false);
|
||||||
|
}
|
||||||
previousData.FileReplacements[objectKind] =
|
previousData.FileReplacements[objectKind] =
|
||||||
new HashSet<FileReplacement>(resolvedPaths.Select(c => new FileReplacement([.. c.Value], c.Key)), FileReplacementComparer.Instance)
|
new HashSet<FileReplacement>(resolvedPaths.Select(c => new FileReplacement([.. c.Value], c.Key)), FileReplacementComparer.Instance)
|
||||||
.Where(p => p.HasFileReplacement).ToHashSet();
|
.Where(p => p.HasFileReplacement).ToHashSet();
|
||||||
@@ -353,6 +361,7 @@ public class PlayerDataFactory
|
|||||||
{
|
{
|
||||||
foreach (var item in previousData.FileReplacements[objectKind].Where(i => i.HasFileReplacement).SelectMany(p => p.GamePaths))
|
foreach (var item in previousData.FileReplacements[objectKind].Where(i => i.HasFileReplacement).SelectMany(p => p.GamePaths))
|
||||||
{
|
{
|
||||||
|
_logger.LogDebug("Persisting {item}", item);
|
||||||
_transientResourceManager.AddSemiTransientResource(objectKind, item);
|
_transientResourceManager.AddSemiTransientResource(objectKind, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -425,7 +434,7 @@ public class PlayerDataFactory
|
|||||||
return previousData;
|
return previousData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Dictionary<string, List<string>>> GetFileReplacementsFromPaths(HashSet<string> forwardResolve, HashSet<string> reverseResolve)
|
private async Task<IReadOnlyDictionary<string, string[]>> GetFileReplacementsFromPaths(HashSet<string> forwardResolve, HashSet<string> reverseResolve)
|
||||||
{
|
{
|
||||||
var forwardPaths = forwardResolve.ToArray();
|
var forwardPaths = forwardResolve.ToArray();
|
||||||
var reversePaths = reverseResolve.ToArray();
|
var reversePaths = reverseResolve.ToArray();
|
||||||
@@ -457,7 +466,7 @@ public class PlayerDataFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resolvedPaths;
|
return resolvedPaths.ToDictionary(k => k.Key, k => k.Value.ToArray(), StringComparer.OrdinalIgnoreCase).AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
private HashSet<string> ManageSemiTransientData(ObjectKind objectKind, IntPtr charaPointer)
|
private HashSet<string> ManageSemiTransientData(ObjectKind objectKind, IntPtr charaPointer)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using MareSynchronos.API.Dto.User;
|
using MareSynchronos.API.Dto.User;
|
||||||
using MareSynchronos.FileCache;
|
using MareSynchronos.FileCache;
|
||||||
using MareSynchronos.Interop;
|
using MareSynchronos.Interop;
|
||||||
using MareSynchronos.MareConfiguration;
|
|
||||||
using MareSynchronos.PlayerData.Factories;
|
using MareSynchronos.PlayerData.Factories;
|
||||||
using MareSynchronos.PlayerData.Pairs;
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
using MareSynchronos.Services;
|
using MareSynchronos.Services;
|
||||||
@@ -19,10 +18,11 @@ namespace MareSynchronos.PlayerData.Handlers;
|
|||||||
|
|
||||||
public sealed class PairHandler : DisposableMediatorSubscriberBase
|
public sealed class PairHandler : DisposableMediatorSubscriberBase
|
||||||
{
|
{
|
||||||
|
private sealed record CombatData(Guid ApplicationId, CharacterData CharacterData, bool Forced);
|
||||||
|
|
||||||
private readonly DalamudUtilService _dalamudUtil;
|
private readonly DalamudUtilService _dalamudUtil;
|
||||||
private readonly FileDownloadManager _downloadManager;
|
private readonly FileDownloadManager _downloadManager;
|
||||||
private readonly FileCacheManager _fileDbManager;
|
private readonly FileCacheManager _fileDbManager;
|
||||||
private readonly MareConfigService _mareConfigService;
|
|
||||||
private readonly GameObjectHandlerFactory _gameObjectHandlerFactory;
|
private readonly GameObjectHandlerFactory _gameObjectHandlerFactory;
|
||||||
private readonly IpcManager _ipcManager;
|
private readonly IpcManager _ipcManager;
|
||||||
private readonly IHostApplicationLifetime _lifetime;
|
private readonly IHostApplicationLifetime _lifetime;
|
||||||
@@ -37,6 +37,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
private bool _isVisible;
|
private bool _isVisible;
|
||||||
private string _penumbraCollection;
|
private string _penumbraCollection;
|
||||||
private bool _redrawOnNextApplication = false;
|
private bool _redrawOnNextApplication = false;
|
||||||
|
private CombatData? _dataReceivedInCombat;
|
||||||
public long LastAppliedDataSize { get; private set; }
|
public long LastAppliedDataSize { get; private set; }
|
||||||
|
|
||||||
public PairHandler(ILogger<PairHandler> logger, OnlineUserIdentDto onlineUser,
|
public PairHandler(ILogger<PairHandler> logger, OnlineUserIdentDto onlineUser,
|
||||||
@@ -44,8 +45,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
IpcManager ipcManager, FileDownloadManager transferManager,
|
IpcManager ipcManager, FileDownloadManager transferManager,
|
||||||
PluginWarningNotificationService pluginWarningNotificationManager,
|
PluginWarningNotificationService pluginWarningNotificationManager,
|
||||||
DalamudUtilService dalamudUtil, IHostApplicationLifetime lifetime,
|
DalamudUtilService dalamudUtil, IHostApplicationLifetime lifetime,
|
||||||
FileCacheManager fileDbManager, MareMediator mediator,
|
FileCacheManager fileDbManager, MareMediator mediator) : base(logger, mediator)
|
||||||
MareConfigService mareConfigService) : base(logger, mediator)
|
|
||||||
{
|
{
|
||||||
OnlineUser = onlineUser;
|
OnlineUser = onlineUser;
|
||||||
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
||||||
@@ -55,7 +55,6 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
_dalamudUtil = dalamudUtil;
|
_dalamudUtil = dalamudUtil;
|
||||||
_lifetime = lifetime;
|
_lifetime = lifetime;
|
||||||
_fileDbManager = fileDbManager;
|
_fileDbManager = fileDbManager;
|
||||||
_mareConfigService = mareConfigService;
|
|
||||||
_penumbraCollection = _ipcManager.PenumbraCreateTemporaryCollectionAsync(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult();
|
_penumbraCollection = _ipcManager.PenumbraCreateTemporaryCollectionAsync(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||||
|
|
||||||
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
|
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
|
||||||
@@ -77,11 +76,26 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
});
|
});
|
||||||
Mediator.Subscribe<ClassJobChangedMessage>(this, (msg) =>
|
Mediator.Subscribe<ClassJobChangedMessage>(this, (msg) =>
|
||||||
{
|
{
|
||||||
if (msg.gameObjectHandler == _charaHandler)
|
if (msg.GameObjectHandler == _charaHandler)
|
||||||
{
|
{
|
||||||
_redrawOnNextApplication = true;
|
_redrawOnNextApplication = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Mediator.Subscribe<CombatEndMessage>(this, (msg) =>
|
||||||
|
{
|
||||||
|
if (IsVisible && _dataReceivedInCombat != null)
|
||||||
|
{
|
||||||
|
ApplyCharacterData(_dataReceivedInCombat.ApplicationId,
|
||||||
|
_dataReceivedInCombat.CharacterData, _dataReceivedInCombat.Forced);
|
||||||
|
_dataReceivedInCombat = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Mediator.Subscribe<CombatStartMessage>(this, _ =>
|
||||||
|
{
|
||||||
|
_dataReceivedInCombat = null;
|
||||||
|
_downloadCancellationTokenSource = _downloadCancellationTokenSource?.CancelRecreate();
|
||||||
|
_applicationCancellationTokenSource = _applicationCancellationTokenSource?.CancelRecreate();
|
||||||
|
});
|
||||||
|
|
||||||
LastAppliedDataSize = -1;
|
LastAppliedDataSize = -1;
|
||||||
}
|
}
|
||||||
@@ -107,12 +121,21 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
public void ApplyCharacterData(Guid applicationBase, CharacterData characterData, bool forceApplyCustomization = false)
|
public void ApplyCharacterData(Guid applicationBase, CharacterData characterData, bool forceApplyCustomization = false)
|
||||||
{
|
{
|
||||||
|
if (_dalamudUtil.IsInCombat)
|
||||||
|
{
|
||||||
|
Logger.LogDebug("[BASE-{appBase}] Received data but player is in combat", applicationBase);
|
||||||
|
_dataReceivedInCombat = new(applicationBase, characterData, forceApplyCustomization);
|
||||||
|
SetUploading(isUploading: false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_charaHandler == null || (PlayerCharacter == IntPtr.Zero))
|
if (_charaHandler == null || (PlayerCharacter == IntPtr.Zero))
|
||||||
{
|
{
|
||||||
Logger.LogDebug("[BASE-{appBase}] Received data but player was in invalid state, charaHandlerIsNull: {charaIsNull}, playerPointerIsNull: {ptrIsNull}",
|
Logger.LogDebug("[BASE-{appBase}] Received data but player was in invalid state, charaHandlerIsNull: {charaIsNull}, playerPointerIsNull: {ptrIsNull}",
|
||||||
applicationBase, _charaHandler == null, PlayerCharacter == IntPtr.Zero);
|
applicationBase, _charaHandler == null, PlayerCharacter == IntPtr.Zero);
|
||||||
var hasDiffMods = characterData.CheckUpdatedData(applicationBase, _cachedData, Logger,
|
var hasDiffMods = characterData.CheckUpdatedData(applicationBase, _cachedData, Logger,
|
||||||
this, forceApplyCustomization, forceApplyMods: false).Any(p => p.Value.Contains(PlayerChanges.ModManip) || p.Value.Contains(PlayerChanges.ModFiles));
|
this, forceApplyCustomization, forceApplyMods: false)
|
||||||
|
.Any(p => p.Value.Contains(PlayerChanges.ModManip) || p.Value.Contains(PlayerChanges.ModFiles));
|
||||||
_forceApplyMods = hasDiffMods || _forceApplyMods || (PlayerCharacter == IntPtr.Zero && _cachedData == null);
|
_forceApplyMods = hasDiffMods || _forceApplyMods || (PlayerCharacter == IntPtr.Zero && _cachedData == null);
|
||||||
_cachedData = characterData;
|
_cachedData = characterData;
|
||||||
Logger.LogDebug("[BASE-{appBase}] Setting data: {hash}, forceApplyMods: {force}", applicationBase, _cachedData.DataHash.Value, _forceApplyMods);
|
Logger.LogDebug("[BASE-{appBase}] Setting data: {hash}, forceApplyMods: {force}", applicationBase, _cachedData.DataHash.Value, _forceApplyMods);
|
||||||
@@ -395,7 +418,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
await _ipcManager.PenumbraSetTemporaryModsAsync(Logger, _applicationId, _penumbraCollection, moddedPaths).ConfigureAwait(false);
|
await _ipcManager.PenumbraSetTemporaryModsAsync(Logger, _applicationId, _penumbraCollection, moddedPaths).ConfigureAwait(false);
|
||||||
LastAppliedDataSize = -1;
|
LastAppliedDataSize = -1;
|
||||||
foreach (var path in moddedPaths.Select(v => new FileInfo(v.Value)).Where(p => p.Exists))
|
foreach (var path in moddedPaths.Values.Distinct(StringComparer.OrdinalIgnoreCase).Select(v => new FileInfo(v)).Where(p => p.Exists))
|
||||||
{
|
{
|
||||||
LastAppliedDataSize += path.Length;
|
LastAppliedDataSize += path.Length;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,8 +48,21 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase
|
|||||||
_playerRelatedObjects[ObjectKind.Companion] = gameObjectHandlerFactory.Create(ObjectKind.Companion, () => dalamudUtil.GetCompanion(), isWatched: true)
|
_playerRelatedObjects[ObjectKind.Companion] = gameObjectHandlerFactory.Create(ObjectKind.Companion, () => dalamudUtil.GetCompanion(), isWatched: true)
|
||||||
.GetAwaiter().GetResult();
|
.GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
Mediator.Subscribe<ClassJobChangedMessage>(this, (msg) =>
|
||||||
|
{
|
||||||
|
if (msg.GameObjectHandler != _playerRelatedObjects[ObjectKind.Player]) return;
|
||||||
|
|
||||||
|
Logger.LogTrace("Removing pet data for {obj}", msg.GameObjectHandler);
|
||||||
|
_playerData.FileReplacements.Remove(ObjectKind.Pet);
|
||||||
|
_playerData.GlamourerString.Remove(ObjectKind.Pet);
|
||||||
|
_playerData.CustomizePlusScale.Remove(ObjectKind.Pet);
|
||||||
|
Mediator.Publish(new CharacterDataCreatedMessage(_playerData.ToAPI()));
|
||||||
|
});
|
||||||
|
|
||||||
Mediator.Subscribe<ClearCacheForObjectMessage>(this, (msg) =>
|
Mediator.Subscribe<ClearCacheForObjectMessage>(this, (msg) =>
|
||||||
{
|
{
|
||||||
|
// ignore pets
|
||||||
|
if (msg.ObjectToCreateFor == _playerRelatedObjects[ObjectKind.Pet]) return;
|
||||||
_ = Task.Run(() =>
|
_ = Task.Run(() =>
|
||||||
{
|
{
|
||||||
Logger.LogTrace("Clearing cache for {obj}", msg.ObjectToCreateFor);
|
Logger.LogTrace("Clearing cache for {obj}", msg.ObjectToCreateFor);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Dalamud.ContextMenu;
|
using Dalamud.ContextMenu;
|
||||||
|
using Dalamud.Game.ClientState.Objects;
|
||||||
using Dalamud.Interface.ImGuiFileDialog;
|
using Dalamud.Interface.ImGuiFileDialog;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
@@ -33,7 +34,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
|
|
||||||
public Plugin(DalamudPluginInterface pluginInterface, ICommandManager commandManager, IDataManager gameData,
|
public Plugin(DalamudPluginInterface pluginInterface, ICommandManager commandManager, IDataManager gameData,
|
||||||
IFramework framework, IObjectTable objectTable, IClientState clientState, ICondition condition, IChatGui chatGui,
|
IFramework framework, IObjectTable objectTable, IClientState clientState, ICondition condition, IChatGui chatGui,
|
||||||
IGameGui gameGui, IDtrBar dtrBar, IPluginLog pluginLog)
|
IGameGui gameGui, IDtrBar dtrBar, IPluginLog pluginLog, ITargetManager targetManager)
|
||||||
{
|
{
|
||||||
_hostBuilderRunTask = new HostBuilder()
|
_hostBuilderRunTask = new HostBuilder()
|
||||||
.UseContentRoot(pluginInterface.ConfigDirectory.FullName)
|
.UseContentRoot(pluginInterface.ConfigDirectory.FullName)
|
||||||
@@ -74,7 +75,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddSingleton<UidDisplayHandler>();
|
collection.AddSingleton<UidDisplayHandler>();
|
||||||
collection.AddSingleton((s) => new DalamudContextMenu(pluginInterface));
|
collection.AddSingleton((s) => new DalamudContextMenu(pluginInterface));
|
||||||
collection.AddSingleton((s) => new DalamudUtilService(s.GetRequiredService<ILogger<DalamudUtilService>>(),
|
collection.AddSingleton((s) => new DalamudUtilService(s.GetRequiredService<ILogger<DalamudUtilService>>(),
|
||||||
clientState, objectTable, framework, gameGui, condition, gameData,
|
clientState, objectTable, framework, gameGui, condition, gameData, targetManager,
|
||||||
s.GetRequiredService<MareMediator>(), s.GetRequiredService<PerformanceCollectorService>()));
|
s.GetRequiredService<MareMediator>(), s.GetRequiredService<PerformanceCollectorService>()));
|
||||||
collection.AddSingleton((s) => new DtrEntry(s.GetRequiredService<ILogger<DtrEntry>>(), dtrBar, s.GetRequiredService<MareConfigService>(),
|
collection.AddSingleton((s) => new DtrEntry(s.GetRequiredService<ILogger<DtrEntry>>(), dtrBar, s.GetRequiredService<MareConfigService>(),
|
||||||
s.GetRequiredService<MareMediator>(), s.GetRequiredService<PairManager>(), s.GetRequiredService<ApiController>()));
|
s.GetRequiredService<MareMediator>(), s.GetRequiredService<PairManager>(), s.GetRequiredService<ApiController>()));
|
||||||
|
|||||||
@@ -51,18 +51,27 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
|
|||||||
CurrentFile = 1;
|
CurrentFile = 1;
|
||||||
Logger.LogDebug("=== Computing {amount} remaining files ===", remaining.Count);
|
Logger.LogDebug("=== Computing {amount} remaining files ===", remaining.Count);
|
||||||
|
|
||||||
Mediator.Publish(new HaltScanMessage("CharacterAnalyzer"));
|
Mediator.Publish(new HaltScanMessage(nameof(CharacterAnalyzer)));
|
||||||
|
try
|
||||||
foreach (var file in remaining)
|
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Computing file {file}", file.FilePaths[0]);
|
foreach (var file in remaining)
|
||||||
await file.ComputeSizes(_fileCacheManager, cancelToken).ConfigureAwait(false);
|
{
|
||||||
CurrentFile++;
|
Logger.LogDebug("Computing file {file}", file.FilePaths[0]);
|
||||||
|
await file.ComputeSizes(_fileCacheManager, cancelToken).ConfigureAwait(false);
|
||||||
|
CurrentFile++;
|
||||||
|
}
|
||||||
|
|
||||||
|
_fileCacheManager.WriteOutFullCsv();
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogWarning(ex, "Failed to analyze files");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Mediator.Publish(new ResumeScanMessage(nameof(CharacterAnalyzer)));
|
||||||
}
|
}
|
||||||
|
|
||||||
_fileCacheManager.WriteOutFullCsv();
|
|
||||||
|
|
||||||
Mediator.Publish(new ResumeScanMessage("CharacterAnalzyer"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Mediator.Publish(new CharacterDataAnalyzedMessage());
|
Mediator.Publish(new CharacterDataAnalyzedMessage());
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Dalamud.Game.ClientState.Conditions;
|
using Dalamud.Game.ClientState.Conditions;
|
||||||
|
using Dalamud.Game.ClientState.Objects;
|
||||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||||
@@ -15,7 +16,7 @@ using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
|
|||||||
|
|
||||||
namespace MareSynchronos.Services;
|
namespace MareSynchronos.Services;
|
||||||
|
|
||||||
public class DalamudUtilService : IHostedService
|
public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||||
{
|
{
|
||||||
private readonly List<uint> _classJobIdsIgnoredForPets = [30];
|
private readonly List<uint> _classJobIdsIgnoredForPets = [30];
|
||||||
private readonly IClientState _clientState;
|
private readonly IClientState _clientState;
|
||||||
@@ -23,7 +24,6 @@ public class DalamudUtilService : IHostedService
|
|||||||
private readonly IFramework _framework;
|
private readonly IFramework _framework;
|
||||||
private readonly IGameGui _gameGui;
|
private readonly IGameGui _gameGui;
|
||||||
private readonly ILogger<DalamudUtilService> _logger;
|
private readonly ILogger<DalamudUtilService> _logger;
|
||||||
private readonly MareMediator _mediator;
|
|
||||||
private readonly IObjectTable _objectTable;
|
private readonly IObjectTable _objectTable;
|
||||||
private readonly PerformanceCollectorService _performanceCollector;
|
private readonly PerformanceCollectorService _performanceCollector;
|
||||||
private uint? _classJobId = 0;
|
private uint? _classJobId = 0;
|
||||||
@@ -35,7 +35,8 @@ public class DalamudUtilService : IHostedService
|
|||||||
private bool _sentBetweenAreas = false;
|
private bool _sentBetweenAreas = false;
|
||||||
|
|
||||||
public DalamudUtilService(ILogger<DalamudUtilService> logger, IClientState clientState, IObjectTable objectTable, IFramework framework,
|
public DalamudUtilService(ILogger<DalamudUtilService> logger, IClientState clientState, IObjectTable objectTable, IFramework framework,
|
||||||
IGameGui gameGui, ICondition condition, IDataManager gameData, MareMediator mediator, PerformanceCollectorService performanceCollector)
|
IGameGui gameGui, ICondition condition, IDataManager gameData, ITargetManager targetManager,
|
||||||
|
MareMediator mediator, PerformanceCollectorService performanceCollector)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_clientState = clientState;
|
_clientState = clientState;
|
||||||
@@ -43,7 +44,7 @@ public class DalamudUtilService : IHostedService
|
|||||||
_framework = framework;
|
_framework = framework;
|
||||||
_gameGui = gameGui;
|
_gameGui = gameGui;
|
||||||
_condition = condition;
|
_condition = condition;
|
||||||
_mediator = mediator;
|
Mediator = mediator;
|
||||||
_performanceCollector = performanceCollector;
|
_performanceCollector = performanceCollector;
|
||||||
WorldData = new(() =>
|
WorldData = new(() =>
|
||||||
{
|
{
|
||||||
@@ -51,6 +52,18 @@ public class DalamudUtilService : IHostedService
|
|||||||
.Where(w => w.IsPublic && !w.Name.RawData.IsEmpty)
|
.Where(w => w.IsPublic && !w.Name.RawData.IsEmpty)
|
||||||
.ToDictionary(w => (ushort)w.RowId, w => w.Name.ToString());
|
.ToDictionary(w => (ushort)w.RowId, w => w.Name.ToString());
|
||||||
});
|
});
|
||||||
|
mediator.Subscribe<TargetPairMessage>(this, async (msg) =>
|
||||||
|
{
|
||||||
|
if (clientState.IsPvP) return;
|
||||||
|
var name = msg.Pair.PlayerName;
|
||||||
|
if (string.IsNullOrEmpty(name)) return;
|
||||||
|
var addr = _playerCharas.FirstOrDefault(f => string.Equals(f.Value.Name, name, StringComparison.Ordinal)).Value.Address;
|
||||||
|
if (addr == nint.Zero) return;
|
||||||
|
await RunOnFrameworkThread(() =>
|
||||||
|
{
|
||||||
|
targetManager.Target = CreateGameObject(addr);
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe GameObject* GposeTarget => TargetSystem.Instance()->GPoseTarget;
|
public unsafe GameObject* GposeTarget => TargetSystem.Instance()->GPoseTarget;
|
||||||
@@ -61,9 +74,12 @@ public class DalamudUtilService : IHostedService
|
|||||||
public bool IsLoggedIn { get; private set; }
|
public bool IsLoggedIn { get; private set; }
|
||||||
public bool IsOnFrameworkThread => _framework.IsInFrameworkUpdateThread;
|
public bool IsOnFrameworkThread => _framework.IsInFrameworkUpdateThread;
|
||||||
public bool IsZoning => _condition[ConditionFlag.BetweenAreas] || _condition[ConditionFlag.BetweenAreas51];
|
public bool IsZoning => _condition[ConditionFlag.BetweenAreas] || _condition[ConditionFlag.BetweenAreas51];
|
||||||
|
public bool IsInCombat { get; private set; } = false;
|
||||||
|
|
||||||
public Lazy<Dictionary<ushort, string>> WorldData { get; private set; }
|
public Lazy<Dictionary<ushort, string>> WorldData { get; private set; }
|
||||||
|
|
||||||
|
public MareMediator Mediator { get; }
|
||||||
|
|
||||||
public Dalamud.Game.ClientState.Objects.Types.GameObject? CreateGameObject(IntPtr reference)
|
public Dalamud.Game.ClientState.Objects.Types.GameObject? CreateGameObject(IntPtr reference)
|
||||||
{
|
{
|
||||||
EnsureIsOnFramework();
|
EnsureIsOnFramework();
|
||||||
@@ -279,6 +295,7 @@ public class DalamudUtilService : IHostedService
|
|||||||
{
|
{
|
||||||
_logger.LogTrace("Stopping {type}", GetType());
|
_logger.LogTrace("Stopping {type}", GetType());
|
||||||
|
|
||||||
|
Mediator.UnsubscribeAll(this);
|
||||||
_framework.Update -= FrameworkOnUpdate;
|
_framework.Update -= FrameworkOnUpdate;
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
@@ -433,31 +450,46 @@ public class DalamudUtilService : IHostedService
|
|||||||
{
|
{
|
||||||
_logger.LogDebug("Gpose start");
|
_logger.LogDebug("Gpose start");
|
||||||
IsInGpose = true;
|
IsInGpose = true;
|
||||||
_mediator.Publish(new GposeStartMessage());
|
Mediator.Publish(new GposeStartMessage());
|
||||||
}
|
}
|
||||||
else if (GposeTarget == null && IsInGpose)
|
else if (GposeTarget == null && IsInGpose)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Gpose end");
|
_logger.LogDebug("Gpose end");
|
||||||
IsInGpose = false;
|
IsInGpose = false;
|
||||||
_mediator.Publish(new GposeEndMessage());
|
Mediator.Publish(new GposeEndMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_condition[ConditionFlag.InCombat] && !IsInCombat)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Combat start");
|
||||||
|
IsInCombat = true;
|
||||||
|
Mediator.Publish(new CombatStartMessage());
|
||||||
|
Mediator.Publish(new HaltScanMessage(nameof(IsInCombat)));
|
||||||
|
}
|
||||||
|
else if (!_condition[ConditionFlag.InCombat] && IsInCombat)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Combat end");
|
||||||
|
IsInCombat = false;
|
||||||
|
Mediator.Publish(new CombatEndMessage());
|
||||||
|
Mediator.Publish(new ResumeScanMessage(nameof(IsInCombat)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_condition[ConditionFlag.WatchingCutscene] && !IsInCutscene)
|
if (_condition[ConditionFlag.WatchingCutscene] && !IsInCutscene)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Cutscene start");
|
_logger.LogDebug("Cutscene start");
|
||||||
IsInCutscene = true;
|
IsInCutscene = true;
|
||||||
_mediator.Publish(new CutsceneStartMessage());
|
Mediator.Publish(new CutsceneStartMessage());
|
||||||
_mediator.Publish(new HaltScanMessage("Cutscene"));
|
Mediator.Publish(new HaltScanMessage(nameof(IsInCutscene)));
|
||||||
}
|
}
|
||||||
else if (!_condition[ConditionFlag.WatchingCutscene] && IsInCutscene)
|
else if (!_condition[ConditionFlag.WatchingCutscene] && IsInCutscene)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Cutscene end");
|
_logger.LogDebug("Cutscene end");
|
||||||
IsInCutscene = false;
|
IsInCutscene = false;
|
||||||
_mediator.Publish(new CutsceneEndMessage());
|
Mediator.Publish(new CutsceneEndMessage());
|
||||||
_mediator.Publish(new ResumeScanMessage("Cutscene"));
|
Mediator.Publish(new ResumeScanMessage(nameof(IsInCutscene)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsInCutscene) { _mediator.Publish(new CutsceneFrameworkUpdateMessage()); return; }
|
if (IsInCutscene) { Mediator.Publish(new CutsceneFrameworkUpdateMessage()); return; }
|
||||||
|
|
||||||
if (_condition[ConditionFlag.BetweenAreas] || _condition[ConditionFlag.BetweenAreas51])
|
if (_condition[ConditionFlag.BetweenAreas] || _condition[ConditionFlag.BetweenAreas51])
|
||||||
{
|
{
|
||||||
@@ -469,8 +501,8 @@ public class DalamudUtilService : IHostedService
|
|||||||
{
|
{
|
||||||
_logger.LogDebug("Zone switch/Gpose start");
|
_logger.LogDebug("Zone switch/Gpose start");
|
||||||
_sentBetweenAreas = true;
|
_sentBetweenAreas = true;
|
||||||
_mediator.Publish(new ZoneSwitchStartMessage());
|
Mediator.Publish(new ZoneSwitchStartMessage());
|
||||||
_mediator.Publish(new HaltScanMessage("Zone switch"));
|
Mediator.Publish(new HaltScanMessage(nameof(ConditionFlag.BetweenAreas)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,11 +513,12 @@ public class DalamudUtilService : IHostedService
|
|||||||
{
|
{
|
||||||
_logger.LogDebug("Zone switch/Gpose end");
|
_logger.LogDebug("Zone switch/Gpose end");
|
||||||
_sentBetweenAreas = false;
|
_sentBetweenAreas = false;
|
||||||
_mediator.Publish(new ZoneSwitchEndMessage());
|
Mediator.Publish(new ZoneSwitchEndMessage());
|
||||||
_mediator.Publish(new ResumeScanMessage("Zone switch"));
|
Mediator.Publish(new ResumeScanMessage(nameof(ConditionFlag.BetweenAreas)));
|
||||||
}
|
}
|
||||||
|
|
||||||
_mediator.Publish(new FrameworkUpdateMessage());
|
if (!IsInCombat)
|
||||||
|
Mediator.Publish(new FrameworkUpdateMessage());
|
||||||
|
|
||||||
if (DateTime.Now < _delayedFrameworkUpdateCheck.AddSeconds(1)) return;
|
if (DateTime.Now < _delayedFrameworkUpdateCheck.AddSeconds(1)) return;
|
||||||
|
|
||||||
@@ -496,16 +529,19 @@ public class DalamudUtilService : IHostedService
|
|||||||
_logger.LogDebug("Logged in");
|
_logger.LogDebug("Logged in");
|
||||||
IsLoggedIn = true;
|
IsLoggedIn = true;
|
||||||
_lastZone = _clientState.TerritoryType;
|
_lastZone = _clientState.TerritoryType;
|
||||||
_mediator.Publish(new DalamudLoginMessage());
|
Mediator.Publish(new DalamudLoginMessage());
|
||||||
}
|
}
|
||||||
else if (localPlayer == null && IsLoggedIn)
|
else if (localPlayer == null && IsLoggedIn)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Logged out");
|
_logger.LogDebug("Logged out");
|
||||||
IsLoggedIn = false;
|
IsLoggedIn = false;
|
||||||
_mediator.Publish(new DalamudLogoutMessage());
|
Mediator.Publish(new DalamudLogoutMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
_mediator.Publish(new DelayedFrameworkUpdateMessage());
|
if (IsInCombat)
|
||||||
|
Mediator.Publish(new FrameworkUpdateMessage());
|
||||||
|
|
||||||
|
Mediator.Publish(new DelayedFrameworkUpdateMessage());
|
||||||
|
|
||||||
_delayedFrameworkUpdateCheck = DateTime.Now;
|
_delayedFrameworkUpdateCheck = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public record OpenSettingsUiMessage : MessageBase;
|
|||||||
public record DalamudLoginMessage : MessageBase;
|
public record DalamudLoginMessage : MessageBase;
|
||||||
public record DalamudLogoutMessage : MessageBase;
|
public record DalamudLogoutMessage : MessageBase;
|
||||||
public record FrameworkUpdateMessage : SameThreadMessage;
|
public record FrameworkUpdateMessage : SameThreadMessage;
|
||||||
public record ClassJobChangedMessage(GameObjectHandler gameObjectHandler) : MessageBase;
|
public record ClassJobChangedMessage(GameObjectHandler GameObjectHandler) : MessageBase;
|
||||||
public record DelayedFrameworkUpdateMessage : SameThreadMessage;
|
public record DelayedFrameworkUpdateMessage : SameThreadMessage;
|
||||||
public record ZoneSwitchStartMessage : MessageBase;
|
public record ZoneSwitchStartMessage : MessageBase;
|
||||||
public record ZoneSwitchEndMessage : MessageBase;
|
public record ZoneSwitchEndMessage : MessageBase;
|
||||||
@@ -75,6 +75,9 @@ public record OpenSyncshellAdminPanel(GroupFullInfoDto GroupInfo) : MessageBase;
|
|||||||
public record OpenPermissionWindow(Pair Pair) : MessageBase;
|
public record OpenPermissionWindow(Pair Pair) : MessageBase;
|
||||||
public record DownloadLimitChangedMessage() : SameThreadMessage;
|
public record DownloadLimitChangedMessage() : SameThreadMessage;
|
||||||
public record CensusUpdateMessage(byte Gender, byte RaceId, byte TribeId) : MessageBase;
|
public record CensusUpdateMessage(byte Gender, byte RaceId, byte TribeId) : MessageBase;
|
||||||
|
public record TargetPairMessage(Pair Pair) : MessageBase;
|
||||||
|
public record CombatStartMessage : MessageBase;
|
||||||
|
public record CombatEndMessage : MessageBase;
|
||||||
|
|
||||||
#pragma warning restore S2094
|
#pragma warning restore S2094
|
||||||
#pragma warning restore MA0048 // File name must match type name
|
#pragma warning restore MA0048 // File name must match type name
|
||||||
@@ -173,7 +173,7 @@ public sealed class PerformanceCollectorService : IHostedService
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var last = entries.Value.Last();
|
var last = entries.Value.ToList().Last();
|
||||||
if (last.Item1.AddMinutes(10) < TimeOnly.FromDateTime(DateTime.Now) && !_performanceCounters.TryRemove(entries.Key, out _))
|
if (last.Item1.AddMinutes(10) < TimeOnly.FromDateTime(DateTime.Now) && !_performanceCounters.TryRemove(entries.Key, out _))
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Could not remove performance counter {counter}", entries.Key);
|
_logger.LogDebug("Could not remove performance counter {counter}", entries.Key);
|
||||||
|
|||||||
@@ -66,13 +66,18 @@ public class DrawUserPair : DrawPairBase
|
|||||||
ImGui.SetCursorPosY(textPosY);
|
ImGui.SetCursorPosY(textPosY);
|
||||||
ImGui.PushFont(UiBuilder.IconFont);
|
ImGui.PushFont(UiBuilder.IconFont);
|
||||||
UiSharedService.ColorText(FontAwesomeIcon.Eye.ToIconString(), ImGuiColors.ParsedGreen);
|
UiSharedService.ColorText(FontAwesomeIcon.Eye.ToIconString(), ImGuiColors.ParsedGreen);
|
||||||
|
if (ImGui.IsItemClicked())
|
||||||
|
{
|
||||||
|
_mediator.Publish(new TargetPairMessage(_pair));
|
||||||
|
}
|
||||||
ImGui.PopFont();
|
ImGui.PopFont();
|
||||||
var visibleTooltip = _pair.UserData.AliasOrUID + " is visible: " + _pair.PlayerName!;
|
var visibleTooltip = _pair.UserData.AliasOrUID + " is visible: " + _pair.PlayerName! + Environment.NewLine + "Click to target this player";
|
||||||
if (_pair.LastAppliedDataSize >= 0)
|
if (_pair.LastAppliedDataSize >= 0)
|
||||||
{
|
{
|
||||||
visibleTooltip += UiSharedService.TooltipSeparator +
|
visibleTooltip += UiSharedService.TooltipSeparator +
|
||||||
"Loaded Mods Size: " + UiSharedService.ByteToString(_pair.LastAppliedDataSize, true);
|
"Loaded Mods Size: " + UiSharedService.ByteToString(_pair.LastAppliedDataSize, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
UiSharedService.AttachToolTip(visibleTooltip);
|
UiSharedService.AttachToolTip(visibleTooltip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ public class BanUserPopupHandler : IPopupHandler
|
|||||||
|
|
||||||
public Vector2 PopupSize => new(500, 250);
|
public Vector2 PopupSize => new(500, 250);
|
||||||
|
|
||||||
|
public bool ShowClose => true;
|
||||||
|
|
||||||
public void DrawContent()
|
public void DrawContent()
|
||||||
{
|
{
|
||||||
UiSharedService.TextWrapped("User " + (_reportedPair.UserData.AliasOrUID) + " will be banned and removed from this Syncshell.");
|
UiSharedService.TextWrapped("User " + (_reportedPair.UserData.AliasOrUID) + " will be banned and removed from this Syncshell.");
|
||||||
@@ -37,10 +39,6 @@ public class BanUserPopupHandler : IPopupHandler
|
|||||||
UiSharedService.TextWrapped("The reason will be displayed in the banlist. The current server-side alias if present (Vanity ID) will automatically be attached to the reason.");
|
UiSharedService.TextWrapped("The reason will be displayed in the banlist. The current server-side alias if present (Vanity ID) will automatically be attached to the reason.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnClose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Open(OpenBanUserPopupMessage message)
|
public void Open(OpenBanUserPopupMessage message)
|
||||||
{
|
{
|
||||||
_reportedPair = message.PairToBan;
|
_reportedPair = message.PairToBan;
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ namespace MareSynchronos.UI.Components.Popup;
|
|||||||
public interface IPopupHandler
|
public interface IPopupHandler
|
||||||
{
|
{
|
||||||
Vector2 PopupSize { get; }
|
Vector2 PopupSize { get; }
|
||||||
|
bool ShowClose { get; }
|
||||||
|
|
||||||
void DrawContent();
|
void DrawContent();
|
||||||
|
|
||||||
void OnClose();
|
|
||||||
}
|
}
|
||||||
@@ -62,17 +62,13 @@ public class PopupHandler : WindowMediatorSubscriberBase
|
|||||||
using var popup = ImRaii.Popup(WindowName, ImGuiWindowFlags.Modal);
|
using var popup = ImRaii.Popup(WindowName, ImGuiWindowFlags.Modal);
|
||||||
if (!popup) return;
|
if (!popup) return;
|
||||||
_currentHandler.DrawContent();
|
_currentHandler.DrawContent();
|
||||||
ImGui.Separator();
|
if (_currentHandler.ShowClose)
|
||||||
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Times, "Close"))
|
|
||||||
{
|
{
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.Separator();
|
||||||
_currentHandler.OnClose();
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Times, "Close"))
|
||||||
|
{
|
||||||
|
ImGui.CloseCurrentPopup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnClose()
|
|
||||||
{
|
|
||||||
base.OnClose();
|
|
||||||
_currentHandler?.OnClose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,8 @@ internal class ReportPopupHandler : IPopupHandler
|
|||||||
|
|
||||||
public Vector2 PopupSize => new(500, 500);
|
public Vector2 PopupSize => new(500, 500);
|
||||||
|
|
||||||
|
public bool ShowClose => true;
|
||||||
|
|
||||||
public void DrawContent()
|
public void DrawContent()
|
||||||
{
|
{
|
||||||
using (ImRaii.PushFont(_uiSharedService.UidFont))
|
using (ImRaii.PushFont(_uiSharedService.UidFont))
|
||||||
@@ -49,10 +51,6 @@ internal class ReportPopupHandler : IPopupHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnClose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Open(OpenReportPopupMessage msg)
|
public void Open(OpenReportPopupMessage msg)
|
||||||
{
|
{
|
||||||
_reportedPair = msg.PairToReport;
|
_reportedPair = msg.PairToReport;
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ public class GposeUi : WindowMediatorSubscriberBase
|
|||||||
private readonly DalamudUtilService _dalamudUtil;
|
private readonly DalamudUtilService _dalamudUtil;
|
||||||
private readonly FileDialogManager _fileDialogManager;
|
private readonly FileDialogManager _fileDialogManager;
|
||||||
private readonly MareCharaFileManager _mareCharaFileManager;
|
private readonly MareCharaFileManager _mareCharaFileManager;
|
||||||
|
private Task<long>? _expectedLength;
|
||||||
|
private Task? _applicationTask;
|
||||||
|
|
||||||
public GposeUi(ILogger<GposeUi> logger, MareCharaFileManager mareCharaFileManager,
|
public GposeUi(ILogger<GposeUi> logger, MareCharaFileManager mareCharaFileManager,
|
||||||
DalamudUtilService dalamudUtil, FileDialogManager fileDialogManager, MareConfigService configService,
|
DalamudUtilService dalamudUtil, FileDialogManager fileDialogManager, MareConfigService configService,
|
||||||
@@ -51,21 +53,28 @@ public class GposeUi : WindowMediatorSubscriberBase
|
|||||||
_configService.Current.ExportFolder = Path.GetDirectoryName(path) ?? string.Empty;
|
_configService.Current.ExportFolder = Path.GetDirectoryName(path) ?? string.Empty;
|
||||||
_configService.Save();
|
_configService.Save();
|
||||||
|
|
||||||
_ = Task.Run(() => _mareCharaFileManager.LoadMareCharaFile(path));
|
_expectedLength = Task.Run(() => _mareCharaFileManager.LoadMareCharaFile(path));
|
||||||
}, 1, Directory.Exists(_configService.Current.ExportFolder) ? _configService.Current.ExportFolder : null);
|
}, 1, Directory.Exists(_configService.Current.ExportFolder) ? _configService.Current.ExportFolder : null);
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Applies it to the currently selected GPose actor");
|
UiSharedService.AttachToolTip("Applies it to the currently selected GPose actor");
|
||||||
if (_mareCharaFileManager.LoadedCharaFile != null)
|
if (_mareCharaFileManager.LoadedCharaFile != null && _expectedLength != null)
|
||||||
{
|
{
|
||||||
UiSharedService.TextWrapped("Loaded file: " + _mareCharaFileManager.LoadedCharaFile.FilePath);
|
UiSharedService.TextWrapped("Loaded file: " + _mareCharaFileManager.LoadedCharaFile.FilePath);
|
||||||
UiSharedService.TextWrapped("File Description: " + _mareCharaFileManager.LoadedCharaFile.CharaFileData.Description);
|
UiSharedService.TextWrapped("File Description: " + _mareCharaFileManager.LoadedCharaFile.CharaFileData.Description);
|
||||||
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Check, "Apply loaded MCDF"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Check, "Apply loaded MCDF"))
|
||||||
{
|
{
|
||||||
_ = Task.Run(async () => await _mareCharaFileManager.ApplyMareCharaFile(_dalamudUtil.GposeTargetGameObject).ConfigureAwait(false));
|
_applicationTask = Task.Run(async () => await _mareCharaFileManager.ApplyMareCharaFile(_dalamudUtil.GposeTargetGameObject, _expectedLength!.GetAwaiter().GetResult()).ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Applies it to the currently selected GPose actor");
|
UiSharedService.AttachToolTip("Applies it to the currently selected GPose actor");
|
||||||
UiSharedService.ColorTextWrapped("Warning: redrawing or changing the character will revert all applied mods.", ImGuiColors.DalamudYellow);
|
UiSharedService.ColorTextWrapped("Warning: redrawing or changing the character will revert all applied mods.", ImGuiColors.DalamudYellow);
|
||||||
}
|
}
|
||||||
|
if (_applicationTask?.IsFaulted ?? false)
|
||||||
|
{
|
||||||
|
UiSharedService.ColorTextWrapped("Failure to read MCDF file. MCDF file is possibly corrupt. Re-export the MCDF file and try again.",
|
||||||
|
ImGuiColors.DalamudRed);
|
||||||
|
UiSharedService.ColorTextWrapped("Note: if this is your MCDF, try redrawing yourself, wait and re-export the file. " +
|
||||||
|
"If you received it from someone else have them do the same.", ImGuiColors.DalamudYellow);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -77,6 +86,8 @@ public class GposeUi : WindowMediatorSubscriberBase
|
|||||||
private void EndGpose()
|
private void EndGpose()
|
||||||
{
|
{
|
||||||
IsOpen = false;
|
IsOpen = false;
|
||||||
|
_applicationTask = null;
|
||||||
|
_expectedLength = null;
|
||||||
_mareCharaFileManager.ClearMareCharaFile();
|
_mareCharaFileManager.ClearMareCharaFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
private readonly FileCompactor _fileCompactor;
|
private readonly FileCompactor _fileCompactor;
|
||||||
private readonly FileUploadManager _fileTransferManager;
|
private readonly FileUploadManager _fileTransferManager;
|
||||||
private readonly FileTransferOrchestrator _fileTransferOrchestrator;
|
private readonly FileTransferOrchestrator _fileTransferOrchestrator;
|
||||||
|
private readonly FileCacheManager _fileCacheManager;
|
||||||
private readonly MareCharaFileManager _mareCharaFileManager;
|
private readonly MareCharaFileManager _mareCharaFileManager;
|
||||||
private readonly PairManager _pairManager;
|
private readonly PairManager _pairManager;
|
||||||
private readonly PerformanceCollectorService _performanceCollector;
|
private readonly PerformanceCollectorService _performanceCollector;
|
||||||
@@ -49,6 +50,11 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
private bool _readClearCache = false;
|
private bool _readClearCache = false;
|
||||||
private bool _readExport = false;
|
private bool _readExport = false;
|
||||||
private bool _wasOpen = false;
|
private bool _wasOpen = false;
|
||||||
|
private readonly IProgress<(int, int, FileCacheEntity)> _validationProgress;
|
||||||
|
private Task<List<FileCacheEntity>>? _validationTask;
|
||||||
|
private CancellationTokenSource? _validationCts;
|
||||||
|
private (int, int, FileCacheEntity) _currentProgress;
|
||||||
|
private Task? _exportTask;
|
||||||
|
|
||||||
public SettingsUi(ILogger<SettingsUi> logger,
|
public SettingsUi(ILogger<SettingsUi> logger,
|
||||||
UiSharedService uiShared, MareConfigService configService,
|
UiSharedService uiShared, MareConfigService configService,
|
||||||
@@ -57,6 +63,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
MareMediator mediator, PerformanceCollectorService performanceCollector,
|
MareMediator mediator, PerformanceCollectorService performanceCollector,
|
||||||
FileUploadManager fileTransferManager,
|
FileUploadManager fileTransferManager,
|
||||||
FileTransferOrchestrator fileTransferOrchestrator,
|
FileTransferOrchestrator fileTransferOrchestrator,
|
||||||
|
FileCacheManager fileCacheManager,
|
||||||
FileCompactor fileCompactor, ApiController apiController) : base(logger, mediator, "Mare Synchronos Settings")
|
FileCompactor fileCompactor, ApiController apiController) : base(logger, mediator, "Mare Synchronos Settings")
|
||||||
{
|
{
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
@@ -66,11 +73,13 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
_performanceCollector = performanceCollector;
|
_performanceCollector = performanceCollector;
|
||||||
_fileTransferManager = fileTransferManager;
|
_fileTransferManager = fileTransferManager;
|
||||||
_fileTransferOrchestrator = fileTransferOrchestrator;
|
_fileTransferOrchestrator = fileTransferOrchestrator;
|
||||||
|
_fileCacheManager = fileCacheManager;
|
||||||
_apiController = apiController;
|
_apiController = apiController;
|
||||||
_fileCompactor = fileCompactor;
|
_fileCompactor = fileCompactor;
|
||||||
_uiShared = uiShared;
|
_uiShared = uiShared;
|
||||||
AllowClickthrough = false;
|
AllowClickthrough = false;
|
||||||
AllowPinning = false;
|
AllowPinning = false;
|
||||||
|
_validationProgress = new Progress<(int, int, FileCacheEntity)>(v => _currentProgress = v);
|
||||||
|
|
||||||
SizeConstraints = new WindowSizeConstraints()
|
SizeConstraints = new WindowSizeConstraints()
|
||||||
{
|
{
|
||||||
@@ -431,17 +440,11 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
_configService.Current.ExportFolder = Path.GetDirectoryName(path) ?? string.Empty;
|
_configService.Current.ExportFolder = Path.GetDirectoryName(path) ?? string.Empty;
|
||||||
_configService.Save();
|
_configService.Save();
|
||||||
|
|
||||||
_ = Task.Run(() =>
|
_exportTask = Task.Run(() =>
|
||||||
{
|
{
|
||||||
try
|
var desc = _exportDescription;
|
||||||
{
|
_exportDescription = string.Empty;
|
||||||
_mareCharaFileManager.SaveMareCharaFile(LastCreatedCharacterData, _exportDescription, path);
|
_mareCharaFileManager.SaveMareCharaFile(LastCreatedCharacterData, desc, path);
|
||||||
_exportDescription = string.Empty;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogCritical(ex, "Error saving data");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}, Directory.Exists(_configService.Current.ExportFolder) ? _configService.Current.ExportFolder : null);
|
}, Directory.Exists(_configService.Current.ExportFolder) ? _configService.Current.ExportFolder : null);
|
||||||
}
|
}
|
||||||
@@ -453,6 +456,11 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
UiSharedService.ColorTextWrapped("Export in progress", ImGuiColors.DalamudYellow);
|
UiSharedService.ColorTextWrapped("Export in progress", ImGuiColors.DalamudYellow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_exportTask?.IsFaulted ?? false)
|
||||||
|
{
|
||||||
|
UiSharedService.ColorTextWrapped("Export failed, check /xllog for more details.", ImGuiColors.DalamudRed);
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.Unindent();
|
ImGui.Unindent();
|
||||||
}
|
}
|
||||||
bool openInGpose = _configService.Current.OpenGposeImportOnGposeStart;
|
bool openInGpose = _configService.Current.OpenGposeImportOnGposeStart;
|
||||||
@@ -509,6 +517,49 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.EndDisabled();
|
ImGui.EndDisabled();
|
||||||
ImGui.TextUnformatted("The file compactor is only available on Windows.");
|
ImGui.TextUnformatted("The file compactor is only available on Windows.");
|
||||||
}
|
}
|
||||||
|
ImGuiHelpers.ScaledDummy(new Vector2(10, 10));
|
||||||
|
|
||||||
|
ImGui.Separator();
|
||||||
|
UiSharedService.TextWrapped("File Storage validation can make sure that all files in your local Mare Storage are valid. " +
|
||||||
|
"Run the validation before you clear the Storage for no reason. " + Environment.NewLine +
|
||||||
|
"This operation, depending on how many files you have in your storage, can take a while and will be CPU and drive intensive.");
|
||||||
|
using (ImRaii.Disabled(_validationTask != null && !_validationTask.IsCompleted))
|
||||||
|
{
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Check, "Start File Storage Validation"))
|
||||||
|
{
|
||||||
|
_validationCts?.Cancel();
|
||||||
|
_validationCts?.Dispose();
|
||||||
|
_validationCts = new();
|
||||||
|
var token = _validationCts.Token;
|
||||||
|
_validationTask = Task.Run(() => _fileCacheManager.ValidateLocalIntegrity(_validationProgress, token));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_validationTask != null && !_validationTask.IsCompleted)
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Times, "Cancel"))
|
||||||
|
{
|
||||||
|
_validationCts?.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_validationTask != null)
|
||||||
|
{
|
||||||
|
using (ImRaii.PushIndent(20f))
|
||||||
|
{
|
||||||
|
if (_validationTask.IsCompleted)
|
||||||
|
{
|
||||||
|
UiSharedService.TextWrapped($"The storage validation has completed and removed {_validationTask.Result.Count} invalid files from storage.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
UiSharedService.TextWrapped($"Storage validation is running: {_currentProgress.Item1}/{_currentProgress.Item2}");
|
||||||
|
UiSharedService.TextWrapped($"Current item: {_currentProgress.Item3.ResolvedFilepath}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(new Vector2(10, 10));
|
ImGuiHelpers.ScaledDummy(new Vector2(10, 10));
|
||||||
ImGui.TextUnformatted("To clear the local storage accept the following disclaimer");
|
ImGui.TextUnformatted("To clear the local storage accept the following disclaimer");
|
||||||
@@ -549,6 +600,16 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
_lastTab = "General";
|
_lastTab = "General";
|
||||||
|
UiSharedService.FontText("Experimental", _uiShared.UidFont);
|
||||||
|
var usePenumbraResolve = _configService.Current.ExperimentalUsePenumbraResourceTree;
|
||||||
|
if (ImGui.Checkbox("Use Penumbra to resolve character", ref usePenumbraResolve))
|
||||||
|
{
|
||||||
|
_configService.Current.ExperimentalUsePenumbraResourceTree = usePenumbraResolve;
|
||||||
|
_configService.Save();
|
||||||
|
}
|
||||||
|
UiSharedService.DrawHelpText("Requires Penumbra version greater equal to 0.8.2.1 - please report issues with that feature to the Penumbra Discord");
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
UiSharedService.FontText("Notes", _uiShared.UidFont);
|
UiSharedService.FontText("Notes", _uiShared.UidFont);
|
||||||
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.StickyNote, "Export all your user notes to clipboard"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.StickyNote, "Export all your user notes to clipboard"))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
public async Task DownloadFiles(GameObjectHandler gameObject, List<FileReplacementData> fileReplacementDto, CancellationToken ct)
|
public async Task DownloadFiles(GameObjectHandler gameObject, List<FileReplacementData> fileReplacementDto, CancellationToken ct)
|
||||||
{
|
{
|
||||||
Mediator.Publish(new HaltScanMessage("Download"));
|
Mediator.Publish(new HaltScanMessage(nameof(DownloadFiles)));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await DownloadFilesInternal(gameObject, fileReplacementDto, ct).ConfigureAwait(false);
|
await DownloadFilesInternal(gameObject, fileReplacementDto, ct).ConfigureAwait(false);
|
||||||
@@ -77,7 +77,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
Mediator.Publish(new DownloadFinishedMessage(gameObject));
|
Mediator.Publish(new DownloadFinishedMessage(gameObject));
|
||||||
Mediator.Publish(new ResumeScanMessage("Download"));
|
Mediator.Publish(new ResumeScanMessage(nameof(DownloadFiles)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
namespace MareSynchronos.WebAPI.Files
|
||||||
|
|
||||||
namespace MareSynchronos.WebAPI.Files
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class for streaming data with throttling support.
|
/// Class for streaming data with throttling support.
|
||||||
|
|||||||
Reference in New Issue
Block a user