finalize petnames & performance service

This commit is contained in:
Stanley Dimant
2024-09-10 10:48:08 +02:00
parent 0522224f21
commit 62474147b8
11 changed files with 241 additions and 162 deletions

View File

@@ -8,13 +8,12 @@ public class CharacterData
{ {
public Dictionary<ObjectKind, string> CustomizePlusScale { get; set; } = []; public Dictionary<ObjectKind, string> CustomizePlusScale { get; set; } = [];
public Dictionary<ObjectKind, HashSet<FileReplacement>> FileReplacements { get; set; } = []; public Dictionary<ObjectKind, HashSet<FileReplacement>> FileReplacements { get; set; } = [];
public Dictionary<ObjectKind, string> GlamourerString { get; set; } = []; public Dictionary<ObjectKind, string> GlamourerString { get; set; } = [];
public string HeelsData { get; set; } = string.Empty; public string HeelsData { get; set; } = string.Empty;
public string HonorificData { get; set; } = string.Empty; public string HonorificData { get; set; } = string.Empty;
public string ManipulationString { get; set; } = string.Empty; public string ManipulationString { get; set; } = string.Empty;
public string MoodlesData { get; set; } = string.Empty; public string MoodlesData { get; set; } = string.Empty;
public string PetNamesData { get; set; } = string.Empty;
public API.Data.CharacterData ToAPI() public API.Data.CharacterData ToAPI()
{ {
@@ -44,7 +43,8 @@ public class CharacterData
HeelsData = HeelsData, HeelsData = HeelsData,
CustomizePlusData = CustomizePlusScale.ToDictionary(d => d.Key, d => d.Value), CustomizePlusData = CustomizePlusScale.ToDictionary(d => d.Key, d => d.Value),
HonorificData = HonorificData, HonorificData = HonorificData,
MoodlesData = MoodlesData MoodlesData = MoodlesData,
PetNamesData = PetNamesData
}; };
} }
} }

View File

@@ -10,4 +10,5 @@ public enum PlayerChanges
Honorific = 7, Honorific = 7,
ForcedRedraw = 8, ForcedRedraw = 8,
Moodles = 9, Moodles = 9,
PetNames = 10,
} }

View File

@@ -218,8 +218,8 @@ public class PlayerDataFactory
previousData.MoodlesData = await _ipcManager.Moodles.GetStatusAsync(playerRelatedObject.Address).ConfigureAwait(false) ?? string.Empty; previousData.MoodlesData = await _ipcManager.Moodles.GetStatusAsync(playerRelatedObject.Address).ConfigureAwait(false) ?? string.Empty;
_logger.LogDebug("Moodles is now: {moodles}", previousData.MoodlesData); _logger.LogDebug("Moodles is now: {moodles}", previousData.MoodlesData);
previousData.MoodlesData = _ipcManager.PetNames.GetLocalNames(); previousData.PetNamesData = _ipcManager.PetNames.GetLocalNames();
_logger.LogDebug("Pet Nicknames is now: {moodles}", previousData.MoodlesData); _logger.LogDebug("Pet Nicknames is now: {petnames}", previousData.PetNamesData);
} }
if (previousData.FileReplacements.TryGetValue(objectKind, out HashSet<FileReplacement>? fileReplacements)) if (previousData.FileReplacements.TryGetValue(objectKind, out HashSet<FileReplacement>? fileReplacements))

View File

@@ -1,5 +1,4 @@
using MareSynchronos.API.Data; using MareSynchronos.API.Data;
using MareSynchronos.API.Dto.User;
using MareSynchronos.FileCache; using MareSynchronos.FileCache;
using MareSynchronos.Interop.Ipc; using MareSynchronos.Interop.Ipc;
using MareSynchronos.PlayerData.Factories; using MareSynchronos.PlayerData.Factories;
@@ -344,8 +343,10 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
case PlayerChanges.Moodles: case PlayerChanges.Moodles:
await _ipcManager.Moodles.SetStatusAsync(handler.Address, charaData.MoodlesData).ConfigureAwait(false); await _ipcManager.Moodles.SetStatusAsync(handler.Address, charaData.MoodlesData).ConfigureAwait(false);
break;
await _ipcManager.PetNames.SetPlayerData(handler.Address, charaData.MoodlesData).ConfigureAwait(false); case PlayerChanges.PetNames:
await _ipcManager.PetNames.SetPlayerData(handler.Address, charaData.PetNamesData).ConfigureAwait(false);
break; break;
case PlayerChanges.ForcedRedraw: case PlayerChanges.ForcedRedraw:
@@ -378,135 +379,135 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
_downloadCancellationTokenSource = _downloadCancellationTokenSource?.CancelRecreate() ?? new CancellationTokenSource(); _downloadCancellationTokenSource = _downloadCancellationTokenSource?.CancelRecreate() ?? new CancellationTokenSource();
var downloadToken = _downloadCancellationTokenSource.Token; var downloadToken = _downloadCancellationTokenSource.Token;
_ = Task.Run(async () => _ = DownloadAndApplyCharacterAsync(applicationBase, charaData, updatedData, updateModdedPaths, updateManip, downloadToken).ConfigureAwait(false);
}
private async Task DownloadAndApplyCharacterAsync(Guid applicationBase, CharacterData charaData, Dictionary<ObjectKind, HashSet<PlayerChanges>> updatedData,
bool updateModdedPaths, bool updateManip, CancellationToken downloadToken)
{
Dictionary<(string GamePath, string? Hash), string> moddedPaths = [];
if (updateModdedPaths)
{ {
Dictionary<(string GamePath, string? Hash), string> moddedPaths = []; int attempts = 0;
List<FileReplacementData> toDownloadReplacements = TryCalculateModdedDictionary(applicationBase, charaData, out moddedPaths, downloadToken);
while (toDownloadReplacements.Count > 0 && attempts++ <= 10 && !downloadToken.IsCancellationRequested)
{
_downloadManager.CancelDownload();
Logger.LogDebug("[BASE-{appBase}] Downloading missing files for player {name}, {kind}", applicationBase, PlayerName, updatedData);
Mediator.Publish(new EventMessage(new Event(PlayerName, Pair.UserData, nameof(PairHandler), EventSeverity.Informational,
$"Starting download for {toDownloadReplacements.Count} files")));
var toDownloadFiles = await _downloadManager.InitiateDownloadList(_charaHandler!, toDownloadReplacements, downloadToken).ConfigureAwait(false);
if (!_playerPerformanceService.ComputeAndAutoPauseOnVRAMUsageThresholds(this, charaData, toDownloadFiles))
{
_downloadManager.CancelDownload();
return;
}
await _downloadManager.DownloadFiles(_charaHandler!, toDownloadReplacements, downloadToken).ConfigureAwait(false);
_downloadManager.CancelDownload();
if (downloadToken.IsCancellationRequested)
{
Logger.LogTrace("[BASE-{appBase}] Detected cancellation", applicationBase);
_downloadManager.CancelDownload();
return;
}
toDownloadReplacements = TryCalculateModdedDictionary(applicationBase, charaData, out moddedPaths, downloadToken);
if (toDownloadReplacements.TrueForAll(c => _downloadManager.ForbiddenTransfers.Exists(f => string.Equals(f.Hash, c.Hash, StringComparison.Ordinal))))
{
break;
}
await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false);
}
if (!await _playerPerformanceService.CheckBothThresholds(this, charaData).ConfigureAwait(false))
return;
}
downloadToken.ThrowIfCancellationRequested();
var appToken = _applicationCancellationTokenSource?.Token;
while ((!_applicationTask?.IsCompleted ?? false)
&& !downloadToken.IsCancellationRequested
&& (!appToken?.IsCancellationRequested ?? false))
{
// block until current application is done
Logger.LogDebug("[BASE-{appBase}] Waiting for current data application (Id: {id}) for player ({handler}) to finish", applicationBase, _applicationId, PlayerName);
await Task.Delay(250).ConfigureAwait(false);
}
if (downloadToken.IsCancellationRequested || (appToken?.IsCancellationRequested ?? false)) return;
_applicationCancellationTokenSource = _applicationCancellationTokenSource.CancelRecreate() ?? new CancellationTokenSource();
var token = _applicationCancellationTokenSource.Token;
_applicationTask = ApplyCharacterDataAsync(applicationBase, charaData, updatedData, updateModdedPaths, updateManip, moddedPaths, token);
}
private async Task ApplyCharacterDataAsync(Guid applicationBase, CharacterData charaData, Dictionary<ObjectKind, HashSet<PlayerChanges>> updatedData, bool updateModdedPaths, bool updateManip,
Dictionary<(string GamePath, string? Hash), string> moddedPaths, CancellationToken token)
{
try
{
_applicationId = Guid.NewGuid();
Logger.LogDebug("[BASE-{applicationId}] Starting application task for {this}: {appId}", applicationBase, this, _applicationId);
Logger.LogDebug("[{applicationId}] Waiting for initial draw for for {handler}", _applicationId, _charaHandler);
await _dalamudUtil.WaitWhileCharacterIsDrawing(Logger, _charaHandler!, _applicationId, 30000, token).ConfigureAwait(false);
token.ThrowIfCancellationRequested();
if (updateModdedPaths) if (updateModdedPaths)
{ {
int attempts = 0; await _ipcManager.Penumbra.SetTemporaryModsAsync(Logger, _applicationId, _penumbraCollection,
List<FileReplacementData> toDownloadReplacements = TryCalculateModdedDictionary(applicationBase, charaData, out moddedPaths, downloadToken); moddedPaths.ToDictionary(k => k.Key.GamePath, k => k.Value, StringComparer.Ordinal)).ConfigureAwait(false);
LastAppliedDataBytes = -1;
while (toDownloadReplacements.Count > 0 && attempts++ <= 10 && !downloadToken.IsCancellationRequested) foreach (var path in moddedPaths.Values.Distinct(StringComparer.OrdinalIgnoreCase).Select(v => new FileInfo(v)).Where(p => p.Exists))
{ {
_downloadManager.CancelDownload(); if (LastAppliedDataBytes == -1) LastAppliedDataBytes = 0;
Logger.LogDebug("[BASE-{appBase}] Downloading missing files for player {name}, {kind}", applicationBase, PlayerName, updatedData);
Mediator.Publish(new EventMessage(new Event(PlayerName, Pair.UserData, nameof(PairHandler), EventSeverity.Informational, LastAppliedDataBytes += path.Length;
$"Starting download for {toDownloadReplacements.Count} files")));
var toDownloadFiles = await _downloadManager.InitiateDownloadList(_charaHandler!, toDownloadReplacements, downloadToken).ConfigureAwait(false);
if (!_playerPerformanceService.CheckVRAMUsageThresholds(this, charaData, toDownloadFiles))
{
_downloadManager.CancelDownload();
return;
}
await _downloadManager.DownloadFiles(_charaHandler!, toDownloadReplacements, downloadToken).ConfigureAwait(false);
_downloadManager.CancelDownload();
if (downloadToken.IsCancellationRequested)
{
Logger.LogTrace("[BASE-{appBase}] Detected cancellation", applicationBase);
_downloadManager.CancelDownload();
return;
}
toDownloadReplacements = TryCalculateModdedDictionary(applicationBase, charaData, out moddedPaths, downloadToken);
if (toDownloadReplacements.TrueForAll(c => _downloadManager.ForbiddenTransfers.Exists(f => string.Equals(f.Hash, c.Hash, StringComparison.Ordinal))))
{
break;
}
await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false);
}
if (attempts == 0 && !_playerPerformanceService.CheckVRAMUsageThresholds(this, charaData, []))
{
return;
}
if (!await _playerPerformanceService.CheckTriangleUsageThresholds(this, charaData).ConfigureAwait(false))
{
return;
} }
} }
downloadToken.ThrowIfCancellationRequested(); if (updateManip)
var appToken = _applicationCancellationTokenSource?.Token;
while ((!_applicationTask?.IsCompleted ?? false)
&& !downloadToken.IsCancellationRequested
&& (!appToken?.IsCancellationRequested ?? false))
{ {
// block until current application is done await _ipcManager.Penumbra.SetManipulationDataAsync(Logger, _applicationId, _penumbraCollection, charaData.ManipulationData).ConfigureAwait(false);
Logger.LogDebug("[BASE-{appBase}] Waiting for current data application (Id: {id}) for player ({handler}) to finish", applicationBase, _applicationId, PlayerName);
await Task.Delay(250).ConfigureAwait(false);
} }
if (downloadToken.IsCancellationRequested || (appToken?.IsCancellationRequested ?? false)) return; token.ThrowIfCancellationRequested();
_applicationCancellationTokenSource = _applicationCancellationTokenSource.CancelRecreate() ?? new CancellationTokenSource(); foreach (var kind in updatedData)
var token = _applicationCancellationTokenSource.Token;
_applicationTask = Task.Run(async () =>
{ {
try await ApplyCustomizationDataAsync(_applicationId, kind, charaData, token).ConfigureAwait(false);
{ token.ThrowIfCancellationRequested();
_applicationId = Guid.NewGuid(); }
Logger.LogDebug("[BASE-{applicationId}] Starting application task for {this}: {appId}", applicationBase, this, _applicationId);
Logger.LogDebug("[{applicationId}] Waiting for initial draw for for {handler}", _applicationId, _charaHandler); _cachedData = charaData;
await _dalamudUtil.WaitWhileCharacterIsDrawing(Logger, _charaHandler!, _applicationId, 30000, token).ConfigureAwait(false);
token.ThrowIfCancellationRequested(); Logger.LogDebug("[{applicationId}] Application finished", _applicationId);
}
if (updateModdedPaths) catch (Exception ex)
{ {
await _ipcManager.Penumbra.SetTemporaryModsAsync(Logger, _applicationId, _penumbraCollection, if (ex is AggregateException aggr && aggr.InnerExceptions.Any(e => e is ArgumentNullException))
moddedPaths.ToDictionary(k => k.Key.GamePath, k => k.Value, StringComparer.Ordinal)).ConfigureAwait(false); {
LastAppliedDataBytes = -1; IsVisible = false;
foreach (var path in moddedPaths.Values.Distinct(StringComparer.OrdinalIgnoreCase).Select(v => new FileInfo(v)).Where(p => p.Exists)) _forceApplyMods = true;
{ _cachedData = charaData;
if (LastAppliedDataBytes == -1) LastAppliedDataBytes = 0; Logger.LogDebug("[{applicationId}] Cancelled, player turned null during application", _applicationId);
}
LastAppliedDataBytes += path.Length; else
} {
} Logger.LogWarning(ex, "[{applicationId}] Cancelled", _applicationId);
}
if (updateManip) }
{
await _ipcManager.Penumbra.SetManipulationDataAsync(Logger, _applicationId, _penumbraCollection, charaData.ManipulationData).ConfigureAwait(false);
}
token.ThrowIfCancellationRequested();
foreach (var kind in updatedData)
{
await ApplyCustomizationDataAsync(_applicationId, kind, charaData, token).ConfigureAwait(false);
token.ThrowIfCancellationRequested();
}
_cachedData = charaData;
Logger.LogDebug("[{applicationId}] Application finished", _applicationId);
}
catch (Exception ex)
{
if (ex is AggregateException aggr && aggr.InnerExceptions.Any(e => e is ArgumentNullException))
{
IsVisible = false;
_forceApplyMods = true;
_cachedData = charaData;
Logger.LogDebug("[{applicationId}] Cancelled, player turned null during application", _applicationId);
}
else
{
Logger.LogWarning(ex, "[{applicationId}] Cancelled", _applicationId);
}
}
}, token);
}, downloadToken);
} }
private void FrameworkUpdate() private void FrameworkUpdate()
@@ -595,7 +596,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
await _ipcManager.Honorific.ClearTitleAsync(address).ConfigureAwait(false); await _ipcManager.Honorific.ClearTitleAsync(address).ConfigureAwait(false);
Logger.LogDebug("[{applicationId}] Restoring Moodles for {alias}/{name}", applicationId, Pair.UserData.AliasOrUID, name); Logger.LogDebug("[{applicationId}] Restoring Moodles for {alias}/{name}", applicationId, Pair.UserData.AliasOrUID, name);
await _ipcManager.Moodles.RevertStatusAsync(address).ConfigureAwait(false); await _ipcManager.Moodles.RevertStatusAsync(address).ConfigureAwait(false);
Logger.LogDebug("[{applicationId}] Restoring Pet Nicknames for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name); Logger.LogDebug("[{applicationId}] Restoring Pet Nicknames for {alias}/{name}", applicationId, Pair.UserData.AliasOrUID, name);
await _ipcManager.PetNames.ClearPlayerData(address).ConfigureAwait(false); await _ipcManager.PetNames.ClearPlayerData(address).ConfigureAwait(false);
} }
else if (objectKind == ObjectKind.MinionOrMount) else if (objectKind == ObjectKind.MinionOrMount)

View File

@@ -127,7 +127,7 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase
Mediator.Subscribe<PetNamesMessage>(this, (msg) => Mediator.Subscribe<PetNamesMessage>(this, (msg) =>
{ {
if (_isZoning) return; if (_isZoning) return;
if (!string.Equals(msg.PetNicknamesData, _playerData.MoodlesData, StringComparison.Ordinal)) if (!string.Equals(msg.PetNicknamesData, _playerData.PetNamesData, StringComparison.Ordinal))
{ {
Logger.LogDebug("Received Pet Nicknames change, updating player"); Logger.LogDebug("Received Pet Nicknames change, updating player");
PetNicknamesChanged(); PetNicknamesChanged();

View File

@@ -29,6 +29,67 @@ public class PlayerPerformanceService
_xivDataAnalyzer = xivDataAnalyzer; _xivDataAnalyzer = xivDataAnalyzer;
} }
public async Task<bool> CheckBothThresholds(PairHandler pairHandler, CharacterData charaData)
{
var config = _playerPerformanceConfigService.Current;
bool notPausedAfterVram = ComputeAndAutoPauseOnVRAMUsageThresholds(pairHandler, charaData, []);
if (!notPausedAfterVram) return false;
bool notPausedAfterTris = await CheckTriangleUsageThresholds(pairHandler, charaData).ConfigureAwait(false);
if (!notPausedAfterTris) return false;
if (config.UIDsToIgnore
.Exists(uid => string.Equals(uid, pairHandler.Pair.UserData.Alias, StringComparison.Ordinal) || string.Equals(uid, pairHandler.Pair.UserData.UID, StringComparison.Ordinal)))
return true;
var vramUsage = pairHandler.Pair.LastAppliedApproximateVRAMBytes;
var triUsage = pairHandler.Pair.LastAppliedDataTris;
bool isPrefPerm = pairHandler.Pair.UserPair.OwnPermissions.HasFlag(API.Data.Enum.UserPermissions.Sticky);
bool exceedsTris = CheckForThreshold(config.WarnOnExceedingThresholds, config.TrisWarningThresholdThousands * 1000,
triUsage, config.WarnOnPreferredPermissionsExceedingThresholds, isPrefPerm);
bool exceedsVram = CheckForThreshold(config.WarnOnExceedingThresholds, config.VRAMSizeWarningThresholdMiB * 1024 * 1024,
vramUsage, config.WarnOnPreferredPermissionsExceedingThresholds, isPrefPerm);
if (exceedsVram)
{
_mediator.Publish(new EventMessage(new Event(pairHandler.Pair.PlayerName, pairHandler.Pair.UserData, nameof(PlayerPerformanceService), EventSeverity.Warning,
$"Exceeds VRAM threshold: ({UiSharedService.ByteToString(vramUsage, addSuffix: true)}/{config.VRAMSizeWarningThresholdMiB} MiB)")));
}
if (exceedsTris)
{
_mediator.Publish(new EventMessage(new Event(pairHandler.Pair.PlayerName, pairHandler.Pair.UserData, nameof(PlayerPerformanceService), EventSeverity.Warning,
$"Exceeds triangle threshold: ({triUsage}/{config.TrisAutoPauseThresholdThousands * 1000} triangles)")));
}
if (exceedsTris || exceedsVram)
{
string warningText = string.Empty;
if (exceedsTris && !exceedsVram)
{
warningText = $"Player {pairHandler.Pair.PlayerName} exceeds your configured triangle warning threshold (" +
$"{triUsage}/{config.TrisAutoPauseThresholdThousands * 1000} triangles).";
}
else if (!exceedsTris)
{
warningText = $"Player {pairHandler.Pair.PlayerName} exceeds your configured VRAM warning threshold (" +
$"{UiSharedService.ByteToString(vramUsage, true)}/{config.VRAMSizeWarningThresholdMiB} MiB).";
}
else
{
warningText = $"Player {pairHandler.Pair.PlayerName} exceeds both VRAM warning threshold (" +
$"{UiSharedService.ByteToString(vramUsage, true)}/{config.VRAMSizeWarningThresholdMiB} MiB) and " +
$"triangle warning threshold ({triUsage}/{config.TrisAutoPauseThresholdThousands * 1000} triangles).";
}
_mediator.Publish(new NotificationMessage($"{pairHandler.Pair.PlayerName} ({pairHandler.Pair.UserData.AliasOrUID}) exceeds performance threshold(s)",
warningText, MareConfiguration.Models.NotificationType.Warning));
}
return true;
}
public async Task<bool> CheckTriangleUsageThresholds(PairHandler pairHandler, CharacterData charaData) public async Task<bool> CheckTriangleUsageThresholds(PairHandler pairHandler, CharacterData charaData)
{ {
var config = _playerPerformanceConfigService.Current; var config = _playerPerformanceConfigService.Current;
@@ -74,30 +135,17 @@ public class PlayerPerformanceService
MareConfiguration.Models.NotificationType.Warning)); MareConfiguration.Models.NotificationType.Warning));
_mediator.Publish(new EventMessage(new Event(pair.PlayerName, pair.UserData, nameof(PlayerPerformanceService), EventSeverity.Warning, _mediator.Publish(new EventMessage(new Event(pair.PlayerName, pair.UserData, nameof(PlayerPerformanceService), EventSeverity.Warning,
$"Exceeds triangle threshold: automatically paused ({triUsage}/{triUsage * 1000} triangles)"))); $"Exceeds triangle threshold: automatically paused ({triUsage}/{config.TrisAutoPauseThresholdThousands * 1000} triangles)")));
_mediator.Publish(new PauseMessage(pair.UserData)); _mediator.Publish(new PauseMessage(pair.UserData));
return false; return false;
} }
// and fucking warnings
if (CheckForThreshold(config.WarnOnExceedingThresholds, config.TrisWarningThresholdThousands * 1000,
triUsage, config.WarnOnPreferredPermissionsExceedingThresholds, isPrefPerm))
{
_mediator.Publish(new NotificationMessage($"{pair.PlayerName} ({pair.UserData.AliasOrUID}) exceeds performance threshold",
$"Player {pair.PlayerName} exceeds your configured triangle warning threshold (" +
$"{triUsage}/{triUsage * 1000} triangles).",
MareConfiguration.Models.NotificationType.Warning));
_mediator.Publish(new EventMessage(new Event(pair.PlayerName, pair.UserData, nameof(PlayerPerformanceService), EventSeverity.Warning,
$"Exceeds triangle threshold: ({triUsage}/{triUsage * 1000} triangles)")));
}
return true; return true;
} }
public bool CheckVRAMUsageThresholds(PairHandler pairHandler, CharacterData charaData, List<DownloadFileTransfer> toDownloadFiles) public bool ComputeAndAutoPauseOnVRAMUsageThresholds(PairHandler pairHandler, CharacterData charaData, List<DownloadFileTransfer> toDownloadFiles)
{ {
var config = _playerPerformanceConfigService.Current; var config = _playerPerformanceConfigService.Current;
var pair = pairHandler.Pair; var pair = pairHandler.Pair;
@@ -165,24 +213,11 @@ public class PlayerPerformanceService
_mediator.Publish(new PauseMessage(pair.UserData)); _mediator.Publish(new PauseMessage(pair.UserData));
_mediator.Publish(new EventMessage(new Event(pair.PlayerName, pair.UserData, nameof(PlayerPerformanceService), EventSeverity.Warning, _mediator.Publish(new EventMessage(new Event(pair.PlayerName, pair.UserData, nameof(PlayerPerformanceService), EventSeverity.Warning,
$"Exceeds VRAM threshold: automatically paused ({UiSharedService.ByteToString(vramUsage, addSuffix: true)}/{config.VRAMSizeAutoPauseThresholdMiB}MiB)"))); $"Exceeds VRAM threshold: automatically paused ({UiSharedService.ByteToString(vramUsage, addSuffix: true)}/{config.VRAMSizeAutoPauseThresholdMiB} MiB)")));
return false; 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));
_mediator.Publish(new EventMessage(new Event(pair.PlayerName, pair.UserData, nameof(PlayerPerformanceService), EventSeverity.Warning,
$"Exceeds triangle threshold: ({UiSharedService.ByteToString(vramUsage, addSuffix: true)}/{config.VRAMSizeAutoPauseThresholdMiB}MiB)")));
}
return true; return true;
} }

View File

@@ -60,7 +60,7 @@ public class PluginWarningNotificationService
warning.ShownMoodlesWarning = true; warning.ShownMoodlesWarning = true;
} }
if (changes.Contains(PlayerChanges.Moodles) && !warning.ShowPetNicknamesWarning && !_ipcManager.PetNames.APIAvailable) if (changes.Contains(PlayerChanges.PetNames) && !warning.ShowPetNicknamesWarning && !_ipcManager.PetNames.APIAvailable)
{ {
missingPluginsForData.Add("PetNicknames"); missingPluginsForData.Add("PetNicknames");
warning.ShowPetNicknamesWarning = true; warning.ShowPetNicknamesWarning = true;

View File

@@ -6,6 +6,7 @@ using ImGuiNET;
using MareSynchronos.API.Data.Extensions; using MareSynchronos.API.Data.Extensions;
using MareSynchronos.API.Dto.Group; using MareSynchronos.API.Dto.Group;
using MareSynchronos.API.Dto.User; using MareSynchronos.API.Dto.User;
using MareSynchronos.MareConfiguration;
using MareSynchronos.PlayerData.Pairs; using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.Services.Mediator; using MareSynchronos.Services.Mediator;
using MareSynchronos.Services.ServerConfiguration; using MareSynchronos.Services.ServerConfiguration;
@@ -26,6 +27,7 @@ public class DrawUserPair
private readonly SelectTagForPairUi _selectTagForPairUi; private readonly SelectTagForPairUi _selectTagForPairUi;
private readonly ServerConfigurationManager _serverConfigurationManager; private readonly ServerConfigurationManager _serverConfigurationManager;
private readonly UiSharedService _uiSharedService; private readonly UiSharedService _uiSharedService;
private readonly PlayerPerformanceConfigService _performanceConfigService;
private float _menuWidth = -1; private float _menuWidth = -1;
private bool _wasHovered = false; private bool _wasHovered = false;
@@ -34,7 +36,7 @@ public class DrawUserPair
ApiController apiController, IdDisplayHandler uIDDisplayHandler, ApiController apiController, IdDisplayHandler uIDDisplayHandler,
MareMediator mareMediator, SelectTagForPairUi selectTagForPairUi, MareMediator mareMediator, SelectTagForPairUi selectTagForPairUi,
ServerConfigurationManager serverConfigurationManager, ServerConfigurationManager serverConfigurationManager,
UiSharedService uiSharedService) UiSharedService uiSharedService, PlayerPerformanceConfigService performanceConfigService)
{ {
_id = id; _id = id;
_pair = entry; _pair = entry;
@@ -46,6 +48,7 @@ public class DrawUserPair
_selectTagForPairUi = selectTagForPairUi; _selectTagForPairUi = selectTagForPairUi;
_serverConfigurationManager = serverConfigurationManager; _serverConfigurationManager = serverConfigurationManager;
_uiSharedService = uiSharedService; _uiSharedService = uiSharedService;
_performanceConfigService = performanceConfigService;
} }
public Pair Pair => _pair; public Pair Pair => _pair;
@@ -235,11 +238,11 @@ public class DrawUserPair
userPairText += "Files Size: " + UiSharedService.ByteToString(_pair.LastAppliedDataBytes, true); userPairText += "Files Size: " + UiSharedService.ByteToString(_pair.LastAppliedDataBytes, true);
if (_pair.LastAppliedApproximateVRAMBytes >= 0) if (_pair.LastAppliedApproximateVRAMBytes >= 0)
{ {
userPairText += Environment.NewLine + "Approximate max. VRAM Usage: " + UiSharedService.ByteToString(_pair.LastAppliedApproximateVRAMBytes, true); userPairText += Environment.NewLine + "Approx. 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 + "Approx. Triangle Count (excl. Vanilla): "
+ (_pair.LastAppliedDataTris > 1000 ? (_pair.LastAppliedDataTris / 1000d).ToString("0.0'k'") : _pair.LastAppliedDataTris); + (_pair.LastAppliedDataTris > 1000 ? (_pair.LastAppliedDataTris / 1000d).ToString("0.0'k'") : _pair.LastAppliedDataTris);
} }
} }
@@ -257,6 +260,34 @@ public class DrawUserPair
UiSharedService.AttachToolTip(userPairText); UiSharedService.AttachToolTip(userPairText);
if (_performanceConfigService.Current.ShowPerformanceIndicator
&& !_performanceConfigService.Current.UIDsToIgnore
.Exists(uid => string.Equals(uid, UserPair.User.Alias, StringComparison.Ordinal) || string.Equals(uid, UserPair.User.UID, StringComparison.Ordinal))
&& (_performanceConfigService.Current.VRAMSizeWarningThresholdMiB * 1024 * 1024 < _pair.LastAppliedApproximateVRAMBytes
|| _performanceConfigService.Current.TrisWarningThresholdThousands * 1000 < _pair.LastAppliedDataTris)
&& (!_pair.UserPair.OwnPermissions.IsSticky()
|| _performanceConfigService.Current.WarnOnPreferredPermissionsExceedingThresholds))
{
ImGui.SameLine();
_uiSharedService.IconText(FontAwesomeIcon.ExclamationTriangle, ImGuiColors.DalamudYellow);
string userWarningText = "WARNING: This user exceeds one or more of your defined thresholds:" + UiSharedService.TooltipSeparator;
bool shownVram = false;
if (_performanceConfigService.Current.VRAMSizeWarningThresholdMiB * 1024 * 1024 < _pair.LastAppliedApproximateVRAMBytes)
{
shownVram = true;
userWarningText += $"Approx. VRAM Usage: Used: {UiSharedService.ByteToString(_pair.LastAppliedApproximateVRAMBytes)}, Threshold: {_performanceConfigService.Current.VRAMSizeWarningThresholdMiB} MiB";
}
if (_performanceConfigService.Current.TrisWarningThresholdThousands * 1024 < _pair.LastAppliedDataTris)
{
if (shownVram) userWarningText += Environment.NewLine;
userWarningText += $"Approx. Triangle count: Used: {_pair.LastAppliedDataTris}, Threshold: {_performanceConfigService.Current.TrisWarningThresholdThousands * 1000}";
}
UiSharedService.AttachToolTip(userWarningText);
}
ImGui.SameLine(); ImGui.SameLine();
} }

View File

@@ -1,4 +1,5 @@
using MareSynchronos.API.Dto.Group; using MareSynchronos.API.Dto.Group;
using MareSynchronos.MareConfiguration;
using MareSynchronos.PlayerData.Pairs; using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.Services.Mediator; using MareSynchronos.Services.Mediator;
using MareSynchronos.Services.ServerConfiguration; using MareSynchronos.Services.ServerConfiguration;
@@ -18,6 +19,7 @@ public class DrawEntityFactory
private readonly SelectPairForTagUi _selectPairForTagUi; private readonly SelectPairForTagUi _selectPairForTagUi;
private readonly ServerConfigurationManager _serverConfigurationManager; private readonly ServerConfigurationManager _serverConfigurationManager;
private readonly UiSharedService _uiSharedService; private readonly UiSharedService _uiSharedService;
private readonly PlayerPerformanceConfigService _playerPerformanceConfigService;
private readonly SelectTagForPairUi _selectTagForPairUi; private readonly SelectTagForPairUi _selectTagForPairUi;
private readonly TagHandler _tagHandler; private readonly TagHandler _tagHandler;
private readonly IdDisplayHandler _uidDisplayHandler; private readonly IdDisplayHandler _uidDisplayHandler;
@@ -25,7 +27,8 @@ public class DrawEntityFactory
public DrawEntityFactory(ILogger<DrawEntityFactory> logger, ApiController apiController, IdDisplayHandler uidDisplayHandler, public DrawEntityFactory(ILogger<DrawEntityFactory> logger, ApiController apiController, IdDisplayHandler uidDisplayHandler,
SelectTagForPairUi selectTagForPairUi, MareMediator mediator, SelectTagForPairUi selectTagForPairUi, MareMediator mediator,
TagHandler tagHandler, SelectPairForTagUi selectPairForTagUi, TagHandler tagHandler, SelectPairForTagUi selectPairForTagUi,
ServerConfigurationManager serverConfigurationManager, UiSharedService uiSharedService) ServerConfigurationManager serverConfigurationManager, UiSharedService uiSharedService,
PlayerPerformanceConfigService playerPerformanceConfigService)
{ {
_logger = logger; _logger = logger;
_apiController = apiController; _apiController = apiController;
@@ -36,6 +39,7 @@ public class DrawEntityFactory
_selectPairForTagUi = selectPairForTagUi; _selectPairForTagUi = selectPairForTagUi;
_serverConfigurationManager = serverConfigurationManager; _serverConfigurationManager = serverConfigurationManager;
_uiSharedService = uiSharedService; _uiSharedService = uiSharedService;
_playerPerformanceConfigService = playerPerformanceConfigService;
} }
public DrawFolderGroup CreateDrawGroupFolder(GroupFullInfoDto groupFullInfoDto, public DrawFolderGroup CreateDrawGroupFolder(GroupFullInfoDto groupFullInfoDto,
@@ -58,6 +62,6 @@ public class DrawEntityFactory
public DrawUserPair CreateDrawPair(string id, Pair user, List<GroupFullInfoDto> groups, GroupFullInfoDto? currentGroup) public DrawUserPair CreateDrawPair(string id, Pair user, List<GroupFullInfoDto> groups, GroupFullInfoDto? currentGroup)
{ {
return new DrawUserPair(id + user.UserData.UID, user, groups, currentGroup, _apiController, _uidDisplayHandler, return new DrawUserPair(id + user.UserData.UID, user, groups, currentGroup, _apiController, _uidDisplayHandler,
_mediator, _selectTagForPairUi, _serverConfigurationManager, _uiSharedService); _mediator, _selectTagForPairUi, _serverConfigurationManager, _uiSharedService, _playerPerformanceConfigService);
} }
} }

View File

@@ -1400,7 +1400,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
_playerPerformanceConfigService.Save(); _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."); _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 (ImRaii.Disabled(!warnOnExceedingThresholds && !showPerformanceIndicator))
{ {
using var indent = ImRaii.PushIndent(); using var indent = ImRaii.PushIndent();
var warnOnPref = _playerPerformanceConfigService.Current.WarnOnPreferredPermissionsExceedingThresholds; var warnOnPref = _playerPerformanceConfigService.Current.WarnOnPreferredPermissionsExceedingThresholds;
@@ -1409,7 +1409,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
_playerPerformanceConfigService.Current.WarnOnPreferredPermissionsExceedingThresholds = warnOnPref; _playerPerformanceConfigService.Current.WarnOnPreferredPermissionsExceedingThresholds = warnOnPref;
_playerPerformanceConfigService.Save(); _playerPerformanceConfigService.Save();
} }
_uiShared.DrawHelpText("Mare will also print warnings for players where you enabled preferred permissions."); _uiShared.DrawHelpText("Mare will also print warnings and show performance indicator for players where you enabled preferred permissions.");
} }
using (ImRaii.Disabled(!showPerformanceIndicator && !warnOnExceedingThresholds)) using (ImRaii.Disabled(!showPerformanceIndicator && !warnOnExceedingThresholds))
{ {
@@ -1456,8 +1456,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
_playerPerformanceConfigService.Save(); _playerPerformanceConfigService.Save();
} }
_uiShared.DrawHelpText("When enabled, will automatically pause all players regardless of preferred permissions that exceed thresholds defined below." + UiSharedService.TooltipSeparator + _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 "Warning: this will not automatically unpause those people again, you will have to do this manually.");
+ "Default: 550 MiB");
var vramAuto = _playerPerformanceConfigService.Current.VRAMSizeAutoPauseThresholdMiB; var vramAuto = _playerPerformanceConfigService.Current.VRAMSizeAutoPauseThresholdMiB;
var trisAuto = _playerPerformanceConfigService.Current.TrisAutoPauseThresholdThousands; var trisAuto = _playerPerformanceConfigService.Current.TrisAutoPauseThresholdThousands;
ImGui.SetNextItemWidth(100); ImGui.SetNextItemWidth(100);
@@ -1468,7 +1467,8 @@ public class SettingsUi : WindowMediatorSubscriberBase
} }
ImGui.SameLine(); ImGui.SameLine();
ImGui.Text("(MiB)"); ImGui.Text("(MiB)");
_uiShared.DrawHelpText("When a loading in player and their VRAM usage exceeds this amount, automatically pauses the synced player."); _uiShared.DrawHelpText("When a loading in player and their VRAM usage exceeds this amount, automatically pauses the synced player." + UiSharedService.TooltipSeparator
+ "Default: 550 MiB");
ImGui.SetNextItemWidth(100); ImGui.SetNextItemWidth(100);
if (ImGui.InputInt("Auto Pause Triangle threshold", ref trisAuto)) if (ImGui.InputInt("Auto Pause Triangle threshold", ref trisAuto))
{ {

View File

@@ -190,6 +190,13 @@ public static class VariousExtensions
logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (Diff moodles data) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.Moodles); logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (Diff moodles data) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.Moodles);
charaDataToUpdate[objectKind].Add(PlayerChanges.Moodles); charaDataToUpdate[objectKind].Add(PlayerChanges.Moodles);
} }
bool petNamesDataDifferent = !string.Equals(oldData.PetNamesData, newData.PetNamesData, StringComparison.Ordinal);
if (petNamesDataDifferent || (forceApplyCustomization && !string.IsNullOrEmpty(newData.PetNamesData)))
{
logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (Diff petnames data) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.PetNames);
charaDataToUpdate[objectKind].Add(PlayerChanges.PetNames);
}
} }
foreach (KeyValuePair<ObjectKind, HashSet<PlayerChanges>> data in charaDataToUpdate.ToList()) foreach (KeyValuePair<ObjectKind, HashSet<PlayerChanges>> data in charaDataToUpdate.ToList())