something something performance

This commit is contained in:
Stanley Dimant
2024-09-04 15:30:19 +02:00
parent 6b3d79869b
commit 92182ba0b1
13 changed files with 412 additions and 31 deletions

View File

@@ -0,0 +1,16 @@
namespace MareSynchronos.MareConfiguration.Configurations;
public class PlayerPerformanceConfig : IMareConfiguration
{
public int Version { get; set; } = 1;
public bool ShowPerformanceIndicator { get; set; } = true;
public bool WarnOnExceedingThresholds { get; set; } = true;
public bool WarnOnPreferredPermissionsExceedingThresholds { get; set; } = false;
public int VRAMSizeWarningThresholdMiB { get; set; } = 375;
public int TrisWarningThresholdThousands { get; set; } = 165;
public bool AutoPausePlayersExceedingThresholds { get; set; } = false;
public bool AutoPausePlayersWithPreferredPermissionsExceedingThresholds { get; set; } = false;
public int VRAMSizeAutoPauseThresholdMiB { get; set; } = 550;
public int TrisAutoPauseThresholdThousands { get; set; } = 250;
public List<string> UIDsToIgnore { get; set; } = new();
}

View File

@@ -0,0 +1,11 @@
using MareSynchronos.MareConfiguration.Configurations;
namespace MareSynchronos.MareConfiguration;
public class PlayerPerformanceConfigService : ConfigurationServiceBase<PlayerPerformanceConfig>
{
public const string ConfigName = "playerperformance.json";
public PlayerPerformanceConfigService(string configDir) : base(configDir) { }
protected override string ConfigurationName => ConfigName;
}

View File

@@ -21,12 +21,13 @@ public class PairHandlerFactory
private readonly ILoggerFactory _loggerFactory; private readonly ILoggerFactory _loggerFactory;
private readonly MareMediator _mareMediator; private readonly MareMediator _mareMediator;
private readonly XivDataAnalyzer _xivDataAnalyzer; private readonly XivDataAnalyzer _xivDataAnalyzer;
private readonly PlayerPerformanceService _playerPerformanceService;
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, XivDataAnalyzer modelAnalyzer) FileCacheManager fileCacheManager, MareMediator mareMediator, XivDataAnalyzer modelAnalyzer, PlayerPerformanceService playerPerformanceService)
{ {
_loggerFactory = loggerFactory; _loggerFactory = loggerFactory;
_gameObjectHandlerFactory = gameObjectHandlerFactory; _gameObjectHandlerFactory = gameObjectHandlerFactory;
@@ -38,12 +39,13 @@ public class PairHandlerFactory
_fileCacheManager = fileCacheManager; _fileCacheManager = fileCacheManager;
_mareMediator = mareMediator; _mareMediator = mareMediator;
_xivDataAnalyzer = modelAnalyzer; _xivDataAnalyzer = modelAnalyzer;
_playerPerformanceService = playerPerformanceService;
} }
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, _xivDataAnalyzer); _fileCacheManager, _mareMediator, _xivDataAnalyzer, _playerPerformanceService);
} }
} }

View File

@@ -25,6 +25,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
private readonly FileDownloadManager _downloadManager; private readonly FileDownloadManager _downloadManager;
private readonly FileCacheManager _fileDbManager; private readonly FileCacheManager _fileDbManager;
private readonly XivDataAnalyzer _xivDataAnalyzer; private readonly XivDataAnalyzer _xivDataAnalyzer;
private readonly PlayerPerformanceService _playerPerformanceService;
private readonly GameObjectHandlerFactory _gameObjectHandlerFactory; private readonly GameObjectHandlerFactory _gameObjectHandlerFactory;
private readonly IpcManager _ipcManager; private readonly IpcManager _ipcManager;
private readonly IHostApplicationLifetime _lifetime; private readonly IHostApplicationLifetime _lifetime;
@@ -41,8 +42,9 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
private Dictionary<ObjectKind, Guid?> _customizeIds = []; private Dictionary<ObjectKind, Guid?> _customizeIds = [];
private bool _redrawOnNextApplication = false; private bool _redrawOnNextApplication = false;
private CombatData? _dataReceivedInDowntime; private CombatData? _dataReceivedInDowntime;
public long LastAppliedDataSize { get; private set; } public long LastAppliedDataBytes { get; private set; }
public long LastAppliedDataTris { get; private set; } public long LastAppliedDataTris { get; private set; }
public long LastAppliedApproximateVRAMBytes { get; private set; }
public PairHandler(ILogger<PairHandler> logger, OnlineUserIdentDto onlineUser, public PairHandler(ILogger<PairHandler> logger, OnlineUserIdentDto onlineUser,
GameObjectHandlerFactory gameObjectHandlerFactory, GameObjectHandlerFactory gameObjectHandlerFactory,
@@ -50,7 +52,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
PluginWarningNotificationService pluginWarningNotificationManager, PluginWarningNotificationService pluginWarningNotificationManager,
DalamudUtilService dalamudUtil, IHostApplicationLifetime lifetime, DalamudUtilService dalamudUtil, IHostApplicationLifetime lifetime,
FileCacheManager fileDbManager, MareMediator mediator, FileCacheManager fileDbManager, MareMediator mediator,
XivDataAnalyzer modelAnalyzer) : base(logger, mediator) XivDataAnalyzer modelAnalyzer, PlayerPerformanceService playerPerformanceService) : base(logger, mediator)
{ {
OnlineUser = onlineUser; OnlineUser = onlineUser;
_gameObjectHandlerFactory = gameObjectHandlerFactory; _gameObjectHandlerFactory = gameObjectHandlerFactory;
@@ -61,6 +63,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
_lifetime = lifetime; _lifetime = lifetime;
_fileDbManager = fileDbManager; _fileDbManager = fileDbManager;
_xivDataAnalyzer = modelAnalyzer; _xivDataAnalyzer = modelAnalyzer;
_playerPerformanceService = playerPerformanceService;
_penumbraCollection = _ipcManager.Penumbra.CreateTemporaryCollectionAsync(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult(); _penumbraCollection = _ipcManager.Penumbra.CreateTemporaryCollectionAsync(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult();
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate()); Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
@@ -103,7 +106,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
_applicationCancellationTokenSource = _applicationCancellationTokenSource?.CancelRecreate(); _applicationCancellationTokenSource = _applicationCancellationTokenSource?.CancelRecreate();
}); });
LastAppliedDataSize = -1; LastAppliedDataBytes = -1;
LastAppliedDataTris = -1; LastAppliedDataTris = -1;
} }
@@ -384,18 +387,29 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
int attempts = 0; int attempts = 0;
List<FileReplacementData> toDownloadReplacements = TryCalculateModdedDictionary(applicationBase, charaData, out moddedPaths, downloadToken); List<FileReplacementData> toDownloadReplacements = TryCalculateModdedDictionary(applicationBase, charaData, out moddedPaths, downloadToken);
LastAppliedApproximateVRAMBytes = -1;
while (toDownloadReplacements.Count > 0 && attempts++ <= 10 && !downloadToken.IsCancellationRequested) while (toDownloadReplacements.Count > 0 && attempts++ <= 10 && !downloadToken.IsCancellationRequested)
{ {
_downloadManager.CancelDownload(); _downloadManager.CancelDownload();
Logger.LogDebug("[BASE-{appBase}] Downloading missing files for player {name}, {kind}", applicationBase, PlayerName, updatedData); Logger.LogDebug("[BASE-{appBase}] Downloading missing files for player {name}, {kind}", applicationBase, PlayerName, updatedData);
if (toDownloadReplacements.Any())
Mediator.Publish(new EventMessage(new Event(PlayerName, OnlineUser.User, nameof(PairHandler), EventSeverity.Informational,
$"Starting download for {toDownloadReplacements.Count} files")));
var toDownloadFiles = await _downloadManager.InitiateDownloadList(_charaHandler!, toDownloadReplacements, downloadToken).ConfigureAwait(false);
if (!_playerPerformanceService.TryCalculateVRAMUsage(this, charaData, toDownloadFiles, out long vramDuringDl))
{ {
Mediator.Publish(new EventMessage(new Event(PlayerName, OnlineUser.User, nameof(PairHandler), EventSeverity.Informational, LastAppliedApproximateVRAMBytes = vramDuringDl;
$"Starting download for {toDownloadReplacements.Count} files")));
await _downloadManager.DownloadFiles(_charaHandler!, toDownloadReplacements, downloadToken).ConfigureAwait(false);
_downloadManager.CancelDownload(); _downloadManager.CancelDownload();
return;
} }
LastAppliedApproximateVRAMBytes = vramDuringDl;
await _downloadManager.DownloadFiles(_charaHandler!, toDownloadReplacements, downloadToken).ConfigureAwait(false);
_downloadManager.CancelDownload();
if (downloadToken.IsCancellationRequested) if (downloadToken.IsCancellationRequested)
{ {
Logger.LogTrace("[BASE-{appBase}] Detected cancellation", applicationBase); Logger.LogTrace("[BASE-{appBase}] Detected cancellation", applicationBase);
@@ -412,6 +426,13 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false); await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false);
} }
if (LastAppliedApproximateVRAMBytes == -1
&& !_playerPerformanceService.TryCalculateVRAMUsage(this, charaData, [], out long vramUsage))
{
LastAppliedApproximateVRAMBytes = vramUsage;
return;
}
} }
downloadToken.ThrowIfCancellationRequested(); downloadToken.ThrowIfCancellationRequested();
@@ -446,12 +467,18 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
{ {
await _ipcManager.Penumbra.SetTemporaryModsAsync(Logger, _applicationId, _penumbraCollection, await _ipcManager.Penumbra.SetTemporaryModsAsync(Logger, _applicationId, _penumbraCollection,
moddedPaths.ToDictionary(k => k.Key.GamePath, k => k.Value, StringComparer.Ordinal)).ConfigureAwait(false); moddedPaths.ToDictionary(k => k.Key.GamePath, k => k.Value, StringComparer.Ordinal)).ConfigureAwait(false);
LastAppliedDataSize = -1; LastAppliedDataBytes = -1;
LastAppliedDataTris = -1; LastAppliedDataTris = -1;
foreach (var path in moddedPaths.Values.Distinct(StringComparer.OrdinalIgnoreCase).Select(v => new FileInfo(v)).Where(p => p.Exists)) foreach (var path in moddedPaths.Values.Distinct(StringComparer.OrdinalIgnoreCase).Select(v => new FileInfo(v)).Where(p => p.Exists))
{ {
if (LastAppliedDataSize == -1) LastAppliedDataSize = 0; if (LastAppliedDataBytes == -1) LastAppliedDataBytes = 0;
LastAppliedDataSize += path.Length; if (LastAppliedApproximateVRAMBytes == -1) LastAppliedApproximateVRAMBytes = 0;
LastAppliedDataBytes += path.Length;
if (path.Name.EndsWith(".tex", StringComparison.OrdinalIgnoreCase))
{
LastAppliedApproximateVRAMBytes += path.Length;
}
} }
foreach (var key in moddedPaths.Keys.Where(k => !string.IsNullOrEmpty(k.Hash))) foreach (var key in moddedPaths.Keys.Where(k => !string.IsNullOrEmpty(k.Hash)))
{ {

View File

@@ -44,8 +44,9 @@ public class Pair
public bool IsVisible => CachedPlayer?.IsVisible ?? false; public bool IsVisible => CachedPlayer?.IsVisible ?? false;
public CharacterData? LastReceivedCharacterData { get; set; } public CharacterData? LastReceivedCharacterData { get; set; }
public string? PlayerName => CachedPlayer?.PlayerName ?? string.Empty; public string? PlayerName => CachedPlayer?.PlayerName ?? string.Empty;
public long LastAppliedDataSize => CachedPlayer?.LastAppliedDataSize ?? -1; public long LastAppliedDataBytes => CachedPlayer?.LastAppliedDataBytes ?? -1;
public long LastAppliedDataTris => CachedPlayer?.LastAppliedDataTris ?? -1; public long LastAppliedDataTris => CachedPlayer?.LastAppliedDataTris ?? -1;
public long LastAppliedApproximateVRAMBytes => CachedPlayer?.LastAppliedApproximateVRAMBytes ?? -1;
public UserData UserData => UserPair.User; public UserData UserData => UserPair.User;

View File

@@ -110,6 +110,7 @@ public sealed class Plugin : IDalamudPlugin
collection.AddSingleton<FileCompactor>(); collection.AddSingleton<FileCompactor>();
collection.AddSingleton<TagHandler>(); collection.AddSingleton<TagHandler>();
collection.AddSingleton<IdDisplayHandler>(); collection.AddSingleton<IdDisplayHandler>();
collection.AddSingleton<PlayerPerformanceService>();
collection.AddSingleton((s) => new IpcProvider(s.GetRequiredService<ILogger<IpcProvider>>(), collection.AddSingleton((s) => new IpcProvider(s.GetRequiredService<ILogger<IpcProvider>>(),
pluginInterface, pluginInterface,
s.GetRequiredService<MareCharaFileManager>(), s.GetRequiredService<DalamudUtilService>(), s.GetRequiredService<MareCharaFileManager>(), s.GetRequiredService<DalamudUtilService>(),
@@ -150,6 +151,7 @@ public sealed class Plugin : IDalamudPlugin
collection.AddSingleton((s) => new ServerTagConfigService(pluginInterface.ConfigDirectory.FullName)); collection.AddSingleton((s) => new ServerTagConfigService(pluginInterface.ConfigDirectory.FullName));
collection.AddSingleton((s) => new TransientConfigService(pluginInterface.ConfigDirectory.FullName)); collection.AddSingleton((s) => new TransientConfigService(pluginInterface.ConfigDirectory.FullName));
collection.AddSingleton((s) => new XivDataStorageService(pluginInterface.ConfigDirectory.FullName)); collection.AddSingleton((s) => new XivDataStorageService(pluginInterface.ConfigDirectory.FullName));
collection.AddSingleton((s) => new PlayerPerformanceConfigService(pluginInterface.ConfigDirectory.FullName));
collection.AddSingleton((s) => new ConfigurationMigrator(s.GetRequiredService<ILogger<ConfigurationMigrator>>(), pluginInterface)); collection.AddSingleton((s) => new ConfigurationMigrator(s.GetRequiredService<ILogger<ConfigurationMigrator>>(), pluginInterface));
collection.AddSingleton<HubFactory>(); collection.AddSingleton<HubFactory>();
@@ -197,6 +199,7 @@ public sealed class Plugin : IDalamudPlugin
collection.AddHostedService(p => p.GetRequiredService<DalamudUtilService>()); collection.AddHostedService(p => p.GetRequiredService<DalamudUtilService>());
collection.AddHostedService(p => p.GetRequiredService<PerformanceCollectorService>()); collection.AddHostedService(p => p.GetRequiredService<PerformanceCollectorService>());
collection.AddHostedService(p => p.GetRequiredService<DtrEntry>()); collection.AddHostedService(p => p.GetRequiredService<DtrEntry>());
collection.AddHostedService(p => p.GetRequiredService<PlayerPerformanceService>());
collection.AddHostedService(p => p.GetRequiredService<EventAggregator>()); collection.AddHostedService(p => p.GetRequiredService<EventAggregator>());
collection.AddHostedService(p => p.GetRequiredService<IpcProvider>()); collection.AddHostedService(p => p.GetRequiredService<IpcProvider>());
collection.AddHostedService(p => p.GetRequiredService<MarePlugin>()); collection.AddHostedService(p => p.GetRequiredService<MarePlugin>());

View File

@@ -0,0 +1,134 @@
using MareSynchronos.API.Data;
using MareSynchronos.API.Data.Extensions;
using MareSynchronos.FileCache;
using MareSynchronos.MareConfiguration;
using MareSynchronos.PlayerData.Handlers;
using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.Services.Mediator;
using MareSynchronos.UI;
using MareSynchronos.Utils;
using MareSynchronos.WebAPI;
using MareSynchronos.WebAPI.Files.Models;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace MareSynchronos.Services;
public class PlayerPerformanceService : IHostedService
{
private readonly ILogger<PlayerPerformanceService> _logger;
private readonly MareMediator _mediator;
private readonly PlayerPerformanceConfigService _playerPerformanceConfigService;
private readonly FileCacheManager _fileCacheManager;
private readonly PairManager _pairManager;
private readonly ApiController _apiController;
public PlayerPerformanceService(ILogger<PlayerPerformanceService> logger, MareMediator mediator,
PlayerPerformanceConfigService playerPerformanceConfigService, FileCacheManager fileCacheManager,
PairManager pairManager, ApiController apiController)
{
_logger = logger;
_mediator = mediator;
_playerPerformanceConfigService = playerPerformanceConfigService;
_fileCacheManager = fileCacheManager;
_pairManager = pairManager;
_apiController = apiController;
}
public Task StartAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public bool TryCalculateVRAMUsage(PairHandler pairHandler, CharacterData charaData, List<DownloadFileTransfer> toDownloadFiles, out long vramUsage)
{
var config = _playerPerformanceConfigService.Current;
var pair = _pairManager.GetOnlineUserPairs().First(p => string.Equals(p.UserData.UID, pairHandler.OnlineUser.User.UID, StringComparison.OrdinalIgnoreCase));
vramUsage = 0;
if (!charaData.FileReplacements.TryGetValue(API.Data.Enum.ObjectKind.Player, out List<FileReplacementData>? playerReplacements))
return true;
var moddedTextureHashes = playerReplacements.Where(p => string.IsNullOrEmpty(p.FileSwapPath) && p.GamePaths.Any(g => g.EndsWith("tex", StringComparison.OrdinalIgnoreCase)))
.Select(p => p.Hash)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
foreach (var hash in moddedTextureHashes.ToList())
{
long fileSize = 0;
var download = toDownloadFiles.Find(f => string.Equals(hash, f.Hash, StringComparison.OrdinalIgnoreCase));
if (download != null)
{
fileSize = download.Total;
}
else
{
var fileEntry = _fileCacheManager.GetFileCacheByHash(hash);
if (fileEntry == null) continue;
if (fileEntry.Size == null)
{
fileEntry.Size = new FileInfo(fileEntry.ResolvedFilepath).Length;
_fileCacheManager.UpdateHashedFile(fileEntry, computeProperties: true);
}
fileSize = fileEntry.Size.Value;
}
vramUsage += fileSize;
}
_logger.LogDebug("Calculated VRAM usage for {p}", pairHandler);
// no warning of any kind on ignored pairs
if (config.UIDsToIgnore
.Exists(uid => string.Equals(uid, pair.UserData.Alias, StringComparison.Ordinal) || string.Equals(uid, pair.UserData.UID, StringComparison.Ordinal)))
return true;
bool isPrefPerm = pair.UserPair.OwnPermissions.HasFlag(API.Data.Enum.UserPermissions.Sticky);
// now check auto pause
if (CheckForThreshold(config.AutoPausePlayersExceedingThresholds, config.VRAMSizeAutoPauseThresholdMiB * 1024 * 1024,
vramUsage, config.AutoPausePlayersWithPreferredPermissionsExceedingThresholds, isPrefPerm))
{
_mediator.Publish(new NotificationMessage($"{pair.PlayerName} ({pair.UserData.AliasOrUID}) automatically paused",
$"Player {pair.PlayerName} exceeded your configured VRAM auto pause threshold (" +
$"{UiSharedService.ByteToString(vramUsage, addSuffix: true)}/{config.VRAMSizeAutoPauseThresholdMiB}MiB)" +
$" and has been automatically paused.",
MareConfiguration.Models.NotificationType.Warning));
// pause
var perm = pair.UserPair.OwnPermissions.DeepClone();
perm.SetPaused(paused: true);
_ = _apiController.SetBulkPermissions(new(
new(StringComparer.Ordinal) { { pair.UserData.UID, perm } },
new(StringComparer.Ordinal)));
return false;
}
// and fucking warnings
if (CheckForThreshold(config.WarnOnExceedingThresholds, config.VRAMSizeWarningThresholdMiB * 1024 * 1024,
vramUsage, config.WarnOnPreferredPermissionsExceedingThresholds, isPrefPerm))
{
_mediator.Publish(new NotificationMessage($"{pair.PlayerName} ({pair.UserData.AliasOrUID}) exceeds performance threshold",
$"Player {pair.PlayerName} exceeds your configured VRAM warning threshold (" +
$"{UiSharedService.ByteToString(vramUsage, true)}/{config.VRAMSizeWarningThresholdMiB}MiB).",
MareConfiguration.Models.NotificationType.Warning));
return true;
}
return true;
}
private static bool CheckForThreshold(bool thresholdEnabled, long threshold, long value, bool checkForPrefPerm, bool isPrefPerm) =>
thresholdEnabled && threshold > 0 && threshold < value && ((checkForPrefPerm && isPrefPerm) || !isPrefPerm);
}

View File

@@ -169,7 +169,7 @@ public class ServerConfigurationManager
Save(); Save();
} }
internal void AddCurrentCharacterToServer(int serverSelectionIndex = -1, bool addLastSecretKey = false) internal void AddCurrentCharacterToServer(int serverSelectionIndex = -1)
{ {
if (serverSelectionIndex == -1) serverSelectionIndex = CurrentServerIndex; if (serverSelectionIndex == -1) serverSelectionIndex = CurrentServerIndex;
var server = GetServerByIndex(serverSelectionIndex); var server = GetServerByIndex(serverSelectionIndex);
@@ -181,7 +181,7 @@ public class ServerConfigurationManager
{ {
CharacterName = _dalamudUtil.GetPlayerNameAsync().GetAwaiter().GetResult(), CharacterName = _dalamudUtil.GetPlayerNameAsync().GetAwaiter().GetResult(),
WorldId = _dalamudUtil.GetHomeWorldIdAsync().GetAwaiter().GetResult(), WorldId = _dalamudUtil.GetHomeWorldIdAsync().GetAwaiter().GetResult(),
SecretKeyIdx = addLastSecretKey ? server.SecretKeys.Last().Key : -1, SecretKeyIdx = server.SecretKeys.Last().Key,
}); });
Save(); Save();
} }
@@ -189,7 +189,10 @@ public class ServerConfigurationManager
internal void AddEmptyCharacterToServer(int serverSelectionIndex) internal void AddEmptyCharacterToServer(int serverSelectionIndex)
{ {
var server = GetServerByIndex(serverSelectionIndex); var server = GetServerByIndex(serverSelectionIndex);
server.Authentications.Add(new Authentication()); server.Authentications.Add(new Authentication()
{
SecretKeyIdx = server.SecretKeys.Any() ? server.SecretKeys.First().Key : -1,
});
Save(); Save();
} }

View File

@@ -177,7 +177,7 @@ public sealed class XivDataAnalyzer
public Task<long> GetTrianglesByHash(string hash) public Task<long> GetTrianglesByHash(string hash)
{ {
if (_configService.Current.TriangleDictionary.TryGetValue(hash, out var cachedTris)) if (_configService.Current.TriangleDictionary.TryGetValue(hash, out var cachedTris) && cachedTris > 0)
return Task.FromResult(cachedTris); return Task.FromResult(cachedTris);
var path = _fileCacheManager.GetFileCacheByHash(hash); var path = _fileCacheManager.GetFileCacheByHash(hash);
@@ -192,13 +192,29 @@ public sealed class XivDataAnalyzer
var file = _luminaGameData.GetFileFromDisk<MdlFile>(filePath); var file = _luminaGameData.GetFileFromDisk<MdlFile>(filePath);
if (file.FileHeader.LodCount <= 0) if (file.FileHeader.LodCount <= 0)
return Task.FromResult((long)0); return Task.FromResult((long)0);
var meshIdx = file.Lods[0].MeshIndex; long tris = 0;
var meshCnt = file.Lods[0].MeshCount; for (int i = 0; i < file.FileHeader.LodCount; i++)
var tris = file.Meshes.Skip(meshIdx).Take(meshCnt).Sum(p => p.IndexCount) / 3; {
try
{
var meshIdx = file.Lods[i].MeshIndex;
var meshCnt = file.Lods[i].MeshCount;
tris = file.Meshes.Skip(meshIdx).Take(meshCnt).Sum(p => p.IndexCount) / 3;
}
catch (Exception ex)
{
_logger.LogDebug(ex, "Could not load lod mesh {mesh} from {path}", i, filePath);
continue;
}
_logger.LogDebug("{filePath} => {tris} triangles", filePath, tris); if (tris > 0)
_configService.Current.TriangleDictionary[hash] = tris; {
_configService.Save(); _logger.LogDebug("{filePath} => {tris} triangles", filePath, tris);
_configService.Current.TriangleDictionary[hash] = tris;
_configService.Save();
break;
}
}
return Task.FromResult(tris); return Task.FromResult(tris);
} }
catch (Exception e) catch (Exception e)

View File

@@ -228,11 +228,15 @@ public class DrawUserPair
userPairText += UiSharedService.TooltipSeparator + "You are directly Paired"; userPairText += UiSharedService.TooltipSeparator + "You are directly Paired";
} }
if (_pair.LastAppliedDataSize >= 0) if (_pair.LastAppliedDataBytes >= 0)
{ {
userPairText += UiSharedService.TooltipSeparator; userPairText += UiSharedService.TooltipSeparator;
userPairText += ((!_pair.IsPaired) ? "(Last) " : string.Empty) + "Mods Info" + Environment.NewLine; userPairText += ((!_pair.IsPaired) ? "(Last) " : string.Empty) + "Mods Info" + Environment.NewLine;
userPairText += "Files Size: " + UiSharedService.ByteToString(_pair.LastAppliedDataSize, true); userPairText += "Files Size: " + UiSharedService.ByteToString(_pair.LastAppliedDataBytes, true);
if (_pair.LastAppliedApproximateVRAMBytes >= 0)
{
userPairText += Environment.NewLine + "Approximate max. VRAM Usage: " + UiSharedService.ByteToString(_pair.LastAppliedApproximateVRAMBytes, true);
}
if (_pair.LastAppliedDataTris >= 0) if (_pair.LastAppliedDataTris >= 0)
{ {
userPairText += Environment.NewLine + "Triangle Count (excl. Vanilla): " userPairText += Environment.NewLine + "Triangle Count (excl. Vanilla): "

View File

@@ -244,7 +244,7 @@ public partial class IntroUi : WindowMediatorSubscriberBase
FriendlyName = $"Secret Key added on Setup ({DateTime.Now:yyyy-MM-dd})", FriendlyName = $"Secret Key added on Setup ({DateTime.Now:yyyy-MM-dd})",
Key = _secretKey, Key = _secretKey,
}); });
_serverConfigurationManager.AddCurrentCharacterToServer(addLastSecretKey: true); _serverConfigurationManager.AddCurrentCharacterToServer();
} }
else else
{ {

View File

@@ -44,6 +44,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
private readonly PairManager _pairManager; private readonly PairManager _pairManager;
private readonly PerformanceCollectorService _performanceCollector; private readonly PerformanceCollectorService _performanceCollector;
private readonly ServerConfigurationManager _serverConfigurationManager; private readonly ServerConfigurationManager _serverConfigurationManager;
private readonly PlayerPerformanceConfigService _playerPerformanceConfigService;
private readonly UiSharedService _uiShared; private readonly UiSharedService _uiShared;
private bool _deleteAccountPopupModalShown = false; private bool _deleteAccountPopupModalShown = false;
private bool _deleteFilesPopupModalShown = false; private bool _deleteFilesPopupModalShown = false;
@@ -64,6 +65,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
UiSharedService uiShared, MareConfigService configService, UiSharedService uiShared, MareConfigService configService,
MareCharaFileManager mareCharaFileManager, PairManager pairManager, MareCharaFileManager mareCharaFileManager, PairManager pairManager,
ServerConfigurationManager serverConfigurationManager, ServerConfigurationManager serverConfigurationManager,
PlayerPerformanceConfigService playerPerformanceConfigService,
MareMediator mediator, PerformanceCollectorService performanceCollector, MareMediator mediator, PerformanceCollectorService performanceCollector,
FileUploadManager fileTransferManager, FileUploadManager fileTransferManager,
FileTransferOrchestrator fileTransferOrchestrator, FileTransferOrchestrator fileTransferOrchestrator,
@@ -76,6 +78,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
_mareCharaFileManager = mareCharaFileManager; _mareCharaFileManager = mareCharaFileManager;
_pairManager = pairManager; _pairManager = pairManager;
_serverConfigurationManager = serverConfigurationManager; _serverConfigurationManager = serverConfigurationManager;
_playerPerformanceConfigService = playerPerformanceConfigService;
_performanceCollector = performanceCollector; _performanceCollector = performanceCollector;
_fileTransferManager = fileTransferManager; _fileTransferManager = fileTransferManager;
_fileTransferOrchestrator = fileTransferOrchestrator; _fileTransferOrchestrator = fileTransferOrchestrator;
@@ -118,6 +121,8 @@ public class SettingsUi : WindowMediatorSubscriberBase
public override void OnClose() public override void OnClose()
{ {
_uiShared.EditTrackerPosition = false; _uiShared.EditTrackerPosition = false;
_uidToAddForIgnore = string.Empty;
base.OnClose(); base.OnClose();
} }
@@ -1316,6 +1321,156 @@ public class SettingsUi : WindowMediatorSubscriberBase
} }
} }
private void DrawPerformance()
{
_uiShared.BigText("Performance Settings");
UiSharedService.TextWrapped("The configuration options here are to give you more informed warnings and automation when it comes to other performance-intensive synced players.");
ImGui.Dummy(new Vector2(10));
ImGui.Separator();
ImGui.Dummy(new Vector2(10));
bool showPerformanceIndicator = _playerPerformanceConfigService.Current.ShowPerformanceIndicator;
if (ImGui.Checkbox("Show performance indicator", ref showPerformanceIndicator))
{
_playerPerformanceConfigService.Current.ShowPerformanceIndicator = showPerformanceIndicator;
_playerPerformanceConfigService.Save();
}
_uiShared.DrawHelpText("Will show a performance indicator when players exceed defined thresholds in the main UI." + Environment.NewLine + "Will use warning thresholds.");
bool warnOnExceedingThresholds = _playerPerformanceConfigService.Current.WarnOnExceedingThresholds;
if (ImGui.Checkbox("Warn on loading in players exceeding performance thresholds", ref warnOnExceedingThresholds))
{
_playerPerformanceConfigService.Current.WarnOnExceedingThresholds = warnOnExceedingThresholds;
_playerPerformanceConfigService.Save();
}
_uiShared.DrawHelpText("Mare will print a warning in chat once per session of meeting those people. Will not warn on players with preferred permissions.");
using (ImRaii.Disabled(!warnOnExceedingThresholds))
{
using var indent = ImRaii.PushIndent();
var warnOnPref = _playerPerformanceConfigService.Current.WarnOnPreferredPermissionsExceedingThresholds;
if (ImGui.Checkbox("Warn also on players with preferred permissions", ref warnOnPref))
{
_playerPerformanceConfigService.Current.WarnOnPreferredPermissionsExceedingThresholds = warnOnPref;
_playerPerformanceConfigService.Save();
}
_uiShared.DrawHelpText("Mare will also print warnings for players where you enabled preferred permissions.");
}
using (ImRaii.Disabled(!showPerformanceIndicator && !warnOnExceedingThresholds))
{
var vram = _playerPerformanceConfigService.Current.VRAMSizeWarningThresholdMiB;
var tris = _playerPerformanceConfigService.Current.TrisWarningThresholdThousands;
ImGui.SetNextItemWidth(100);
if (ImGui.InputInt("Warning VRAM threshold", ref vram))
{
_playerPerformanceConfigService.Current.VRAMSizeWarningThresholdMiB = vram;
_playerPerformanceConfigService.Save();
}
ImGui.SameLine();
ImGui.Text("(MiB)");
_uiShared.DrawHelpText("Limit in MiB of approximate VRAM usage to trigger warning or performance indicator on UI." + UiSharedService.TooltipSeparator
+ "Default: 375 MiB");
ImGui.SetNextItemWidth(100);
if (ImGui.InputInt("Warning Triangle threshold", ref tris))
{
_playerPerformanceConfigService.Current.TrisWarningThresholdThousands = tris;
_playerPerformanceConfigService.Save();
}
ImGui.SameLine();
ImGui.Text("(thousand triangles)");
_uiShared.DrawHelpText("Limit in approximate used triangles from mods to trigger warning or performance indicator on UI." + UiSharedService.TooltipSeparator
+ "Default: 165 thousand");
}
ImGui.Dummy(new Vector2(10));
bool autoPause = _playerPerformanceConfigService.Current.AutoPausePlayersExceedingThresholds;
bool autoPauseEveryone = _playerPerformanceConfigService.Current.AutoPausePlayersWithPreferredPermissionsExceedingThresholds;
if (ImGui.Checkbox("Automatically pause players exceeding thresholds", ref autoPause))
{
_playerPerformanceConfigService.Current.AutoPausePlayersExceedingThresholds = autoPause;
_playerPerformanceConfigService.Save();
}
_uiShared.DrawHelpText("When enabled, it will automatically pause all players without preferred permissions that exceed the thresholds defined below." + Environment.NewLine
+ "Will print a warning in chat when a player got paused automatically."
+ UiSharedService.TooltipSeparator + "Warning: this will not automatically unpause those people again, you will have to do this manually.");
using (ImRaii.Disabled(!autoPause))
{
using var indent = ImRaii.PushIndent();
if (ImGui.Checkbox("Automatically pause also players with preferred permissions", ref autoPauseEveryone))
{
_playerPerformanceConfigService.Current.AutoPausePlayersWithPreferredPermissionsExceedingThresholds = autoPauseEveryone;
_playerPerformanceConfigService.Save();
}
_uiShared.DrawHelpText("When enabled, will automatically pause all players regardless of preferred permissions that exceed thresholds defined below." + UiSharedService.TooltipSeparator +
"Warning: this will not automatically unpause those people again, you will have to do this manually." + UiSharedService.TooltipSeparator
+ "Default: 550 MiB");
var vramAuto = _playerPerformanceConfigService.Current.VRAMSizeAutoPauseThresholdMiB;
var trisAuto = _playerPerformanceConfigService.Current.TrisAutoPauseThresholdThousands;
ImGui.SetNextItemWidth(100);
if (ImGui.InputInt("Auto Pause VRAM threshold", ref vramAuto))
{
_playerPerformanceConfigService.Current.VRAMSizeAutoPauseThresholdMiB = vramAuto;
_playerPerformanceConfigService.Save();
}
ImGui.SameLine();
ImGui.Text("(MiB)");
_uiShared.DrawHelpText("When a loading in player and their VRAM usage exceeds this amount, automatically pauses the synced player.");
ImGui.SetNextItemWidth(100);
if (ImGui.InputInt("Auto Pause Triangle threshold", ref trisAuto))
{
_playerPerformanceConfigService.Current.TrisAutoPauseThresholdThousands = trisAuto;
_playerPerformanceConfigService.Save();
}
ImGui.SameLine();
ImGui.Text("(thousand triangles)");
_uiShared.DrawHelpText("When a loading in player and their triangle count exceeds this amount, automatically pauses the synced player." + UiSharedService.TooltipSeparator
+ "Default: 250 thousand");
}
ImGui.Dummy(new Vector2(10));
_uiShared.BigText("Whitelisted UIDs");
UiSharedService.TextWrapped("The entries in the list below will be ignored for all warnings and auto pause operations.");
ImGui.Dummy(new Vector2(10));
ImGui.SetNextItemWidth(200);
ImGui.InputText("##ignoreuid", ref _uidToAddForIgnore, 20);
ImGui.SameLine();
using (ImRaii.Disabled(string.IsNullOrEmpty(_uidToAddForIgnore)))
{
if (_uiShared.IconTextButton(FontAwesomeIcon.Plus, "Add UID to whitelist"))
{
if (!_playerPerformanceConfigService.Current.UIDsToIgnore.Contains(_uidToAddForIgnore, StringComparer.Ordinal))
{
_playerPerformanceConfigService.Current.UIDsToIgnore.Add(_uidToAddForIgnore);
}
_uidToAddForIgnore = string.Empty;
}
}
_uiShared.DrawHelpText("Hint: UIDs are case sensitive.");
var playerList = _playerPerformanceConfigService.Current.UIDsToIgnore;
ImGui.SetNextItemWidth(200);
using (var lb = ImRaii.ListBox("UID whitelist"))
{
if (lb)
{
for (int i = 0; i < playerList.Count; i++)
{
bool shouldBeSelected = _selectedEntry == i;
if (ImGui.Selectable(playerList[i] + "##" + i, shouldBeSelected))
{
_selectedEntry = i;
}
}
}
}
using (ImRaii.Disabled(_selectedEntry == -1))
{
if (_uiShared.IconTextButton(FontAwesomeIcon.Trash, "Delete selected UID"))
{
_playerPerformanceConfigService.Current.UIDsToIgnore.RemoveAt(_selectedEntry);
_selectedEntry = -1;
_playerPerformanceConfigService.Save();
}
}
}
private string _uidToAddForIgnore = string.Empty;
private int _selectedEntry = -1;
private void DrawSettingsContent() private void DrawSettingsContent()
{ {
if (_apiController.ServerState is ServerState.Connected) if (_apiController.ServerState is ServerState.Connected)
@@ -1349,6 +1504,12 @@ public class SettingsUi : WindowMediatorSubscriberBase
ImGui.EndTabItem(); ImGui.EndTabItem();
} }
if (ImGui.BeginTabItem("Performance"))
{
DrawPerformance();
ImGui.EndTabItem();
}
if (ImGui.BeginTabItem("Export & Storage")) if (ImGui.BeginTabItem("Export & Storage"))
{ {
DrawFileStorageSettings(); DrawFileStorageSettings();

View File

@@ -210,7 +210,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
} }
} }
private async Task DownloadFilesInternal(GameObjectHandler gameObjectHandler, List<FileReplacementData> fileReplacement, CancellationToken ct) public async Task<List<DownloadFileTransfer>> InitiateDownloadList(GameObjectHandler gameObjectHandler, List<FileReplacementData> fileReplacement, CancellationToken ct)
{ {
Logger.LogDebug("Download start: {id}", gameObjectHandler.Name); Logger.LogDebug("Download start: {id}", gameObjectHandler.Name);
@@ -221,9 +221,6 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
Logger.LogDebug("Files with size 0 or less: {files}", string.Join(", ", downloadFileInfoFromService.Where(f => f.Size <= 0).Select(f => f.Hash))); Logger.LogDebug("Files with size 0 or less: {files}", string.Join(", ", downloadFileInfoFromService.Where(f => f.Size <= 0).Select(f => f.Hash)));
CurrentDownloads = downloadFileInfoFromService.Distinct().Select(d => new DownloadFileTransfer(d))
.Where(d => d.CanBeTransferred).ToList();
foreach (var dto in downloadFileInfoFromService.Where(c => c.IsForbidden)) foreach (var dto in downloadFileInfoFromService.Where(c => c.IsForbidden))
{ {
if (!_orchestrator.ForbiddenTransfers.Exists(f => string.Equals(f.Hash, dto.Hash, StringComparison.Ordinal))) if (!_orchestrator.ForbiddenTransfers.Exists(f => string.Equals(f.Hash, dto.Hash, StringComparison.Ordinal)))
@@ -232,7 +229,13 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
} }
} }
var downloadGroups = CurrentDownloads.Where(f => f.CanBeTransferred).GroupBy(f => f.DownloadUri.Host + ":" + f.DownloadUri.Port, StringComparer.Ordinal); return CurrentDownloads = downloadFileInfoFromService.Distinct().Select(d => new DownloadFileTransfer(d))
.Where(d => d.CanBeTransferred).ToList();
}
private async Task DownloadFilesInternal(GameObjectHandler gameObjectHandler, List<FileReplacementData> fileReplacement, CancellationToken ct)
{
var downloadGroups = CurrentDownloads.GroupBy(f => f.DownloadUri.Host + ":" + f.DownloadUri.Port, StringComparer.Ordinal);
foreach (var downloadGroup in downloadGroups) foreach (var downloadGroup in downloadGroups)
{ {