diff --git a/MareSynchronos/FileCache/CacheMonitor.cs b/MareSynchronos/FileCache/CacheMonitor.cs index 3191e57..852c144 100644 --- a/MareSynchronos/FileCache/CacheMonitor.cs +++ b/MareSynchronos/FileCache/CacheMonitor.cs @@ -368,7 +368,7 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase { try { - _performanceCollector.LogPerformance(this, "FullFileScan", () => FullFileScan(token)); + _performanceCollector.LogPerformance(this, $"FullFileScan", () => FullFileScan(token)); } catch (Exception ex) { diff --git a/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs b/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs index 2e005a0..07d4bfa 100644 --- a/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs +++ b/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs @@ -76,7 +76,7 @@ public class PlayerDataFactory try { - await _performanceCollector.LogPerformance(this, "CreateCharacterData>" + playerRelatedObject.ObjectKind, async () => + await _performanceCollector.LogPerformance(this, $"CreateCharacterData>{playerRelatedObject.ObjectKind}", async () => { await CreateCharacterData(previousData, playerRelatedObject, token).ConfigureAwait(false); }).ConfigureAwait(true); diff --git a/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs b/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs index 8015308..15804b1 100644 --- a/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs +++ b/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs @@ -374,8 +374,8 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase try { - _performanceCollector.LogPerformance(this, "CheckAndUpdateObject>" + (_isOwnedObject ? "Self+" : "Other+") + ObjectKind + "/" - + (string.IsNullOrEmpty(Name) ? "Unk" : Name) + "+" + Address.ToString("X"), CheckAndUpdateObject); + _performanceCollector.LogPerformance(this, $"CheckAndUpdateObject>{(_isOwnedObject ? "Self" : "Other")}+{ObjectKind}/{(string.IsNullOrEmpty(Name) ? "Unk" : Name)}" + + $"+{Address.ToString("X")}", CheckAndUpdateObject); } catch (Exception ex) { diff --git a/MareSynchronos/Services/DalamudUtilService.cs b/MareSynchronos/Services/DalamudUtilService.cs index a4b0e72..c4c49e0 100644 --- a/MareSynchronos/Services/DalamudUtilService.cs +++ b/MareSynchronos/Services/DalamudUtilService.cs @@ -197,7 +197,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber public async Task GetPlayerNameHashedAsync() { - return await RunOnFrameworkThread(() => (GetPlayerName() + GetHomeWorldId()).GetHash256()).ConfigureAwait(false); + return await RunOnFrameworkThread(() => (GetPlayerName(), (ushort)GetHomeWorldId()).GetHash256()).ConfigureAwait(false); } public IntPtr GetPlayerPointer() @@ -252,7 +252,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber public async Task RunOnFrameworkThread(Action act, [CallerMemberName] string callerMember = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0) { var fileName = Path.GetFileNameWithoutExtension(callerFilePath); - await _performanceCollector.LogPerformance(this, "RunOnFramework:Act/" + fileName + ">" + callerMember + ":" + callerLineNumber, async () => + await _performanceCollector.LogPerformance(this, $"RunOnFramework:Act/{fileName}>{callerMember}:{callerLineNumber}", async () => { if (!_framework.IsInFrameworkUpdateThread) { @@ -271,7 +271,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber public async Task RunOnFrameworkThread(Func func, [CallerMemberName] string callerMember = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0) { var fileName = Path.GetFileNameWithoutExtension(callerFilePath); - return await _performanceCollector.LogPerformance(this, "RunOnFramework:Func<" + typeof(T) + ">/" + fileName + ">" + callerMember + ":" + callerLineNumber, async () => + return await _performanceCollector.LogPerformance(this, $"RunOnFramework:Func<{typeof(T)}>/{fileName}>{callerMember}:{callerLineNumber}", async () => { if (!_framework.IsInFrameworkUpdateThread) { @@ -426,7 +426,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber private void FrameworkOnUpdate(IFramework framework) { - _performanceCollector.LogPerformance(this, "FrameworkOnUpdate", FrameworkOnUpdateInternal); + _performanceCollector.LogPerformance(this, $"FrameworkOnUpdate", FrameworkOnUpdateInternal); } private unsafe void FrameworkOnUpdateInternal() @@ -438,10 +438,10 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber bool isNormalFrameworkUpdate = DateTime.Now < _delayedFrameworkUpdateCheck.AddSeconds(1); - _performanceCollector.LogPerformance(this, "FrameworkOnUpdateInternal+" + (isNormalFrameworkUpdate ? "Regular" : "Delayed"), () => + _performanceCollector.LogPerformance(this, $"FrameworkOnUpdateInternal+{(isNormalFrameworkUpdate ? "Regular" : "Delayed")}", () => { IsAnythingDrawing = false; - _performanceCollector.LogPerformance(this, "ObjTableToCharas", + _performanceCollector.LogPerformance(this, $"ObjTableToCharas", () => { _notUpdatedCharas.AddRange(_playerCharas.Keys); @@ -453,7 +453,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber continue; MemoryHelper.ReadStringNullTerminated((nint)((GameObject*)chara.Address)->Name, out string charaName); - var hash = (charaName + ((BattleChara*)chara.Address)->Character.HomeWorld.ToString()).GetHash256(); + var hash = (charaName, ((BattleChara*)chara.Address)->Character.HomeWorld).GetHash256(); if (!IsAnythingDrawing) CheckCharacterForDrawing(chara.Address, charaName); _notUpdatedCharas.Remove(hash); diff --git a/MareSynchronos/Services/Mediator/WindowMediatorSubscriberBase.cs b/MareSynchronos/Services/Mediator/WindowMediatorSubscriberBase.cs index 87a7b41..5e905fd 100644 --- a/MareSynchronos/Services/Mediator/WindowMediatorSubscriberBase.cs +++ b/MareSynchronos/Services/Mediator/WindowMediatorSubscriberBase.cs @@ -35,7 +35,7 @@ public abstract class WindowMediatorSubscriberBase : Window, IMediatorSubscriber public override void Draw() { - _performanceCollectorService.LogPerformance(this, "Draw", DrawInternal); + _performanceCollectorService.LogPerformance(this, $"Draw", DrawInternal); } protected abstract void DrawInternal(); diff --git a/MareSynchronos/Services/PerformanceCollectorService.cs b/MareSynchronos/Services/PerformanceCollectorService.cs index 22a132d..fad205c 100644 --- a/MareSynchronos/Services/PerformanceCollectorService.cs +++ b/MareSynchronos/Services/PerformanceCollectorService.cs @@ -14,7 +14,7 @@ public sealed class PerformanceCollectorService : IHostedService private readonly ILogger _logger; private readonly MareConfigService _mareConfigService; public ConcurrentDictionary> PerformanceCounters { get; } = new(StringComparer.Ordinal); - private readonly CancellationTokenSource _periodicLogPruneTask = new(); + private readonly CancellationTokenSource _periodicLogPruneTaskCts = new(); public PerformanceCollectorService(ILogger logger, MareConfigService mareConfigService) { @@ -22,15 +22,15 @@ public sealed class PerformanceCollectorService : IHostedService _mareConfigService = mareConfigService; } - public T LogPerformance(object sender, string counterName, Func func, int maxEntries = 10000) + public T LogPerformance(object sender, MareInterpolatedStringHandler counterName, Func func, int maxEntries = 10000) { if (!_mareConfigService.Current.LogPerformance) return func.Invoke(); - counterName = sender.GetType().Name + _counterSplit + counterName; + string cn = sender.GetType().Name + _counterSplit + counterName.BuildMessage(); - if (!PerformanceCounters.TryGetValue(counterName, out var list)) + if (!PerformanceCounters.TryGetValue(cn, out var list)) { - list = PerformanceCounters[counterName] = new(maxEntries); + list = PerformanceCounters[cn] = new(maxEntries); } var dt = DateTime.UtcNow.Ticks; @@ -43,21 +43,21 @@ public sealed class PerformanceCollectorService : IHostedService var elapsed = DateTime.UtcNow.Ticks - dt; #if DEBUG if (TimeSpan.FromTicks(elapsed) > TimeSpan.FromMilliseconds(10)) - _logger.LogWarning(">10ms spike on {counterName}: {time}", counterName, TimeSpan.FromTicks(elapsed)); + _logger.LogWarning(">10ms spike on {counterName}: {time}", cn, TimeSpan.FromTicks(elapsed)); #endif list.Add((TimeOnly.FromDateTime(DateTime.Now), elapsed)); } } - public void LogPerformance(object sender, string counterName, Action act, int maxEntries = 10000) + public void LogPerformance(object sender, MareInterpolatedStringHandler counterName, Action act, int maxEntries = 10000) { if (!_mareConfigService.Current.LogPerformance) { act.Invoke(); return; } - counterName = sender.GetType().Name + _counterSplit + counterName; + var cn = sender.GetType().Name + _counterSplit + counterName.BuildMessage(); - if (!PerformanceCounters.TryGetValue(counterName, out var list)) + if (!PerformanceCounters.TryGetValue(cn, out var list)) { - list = PerformanceCounters[counterName] = new(maxEntries); + list = PerformanceCounters[cn] = new(maxEntries); } var dt = DateTime.UtcNow.Ticks; @@ -70,7 +70,7 @@ public sealed class PerformanceCollectorService : IHostedService var elapsed = DateTime.UtcNow.Ticks - dt; #if DEBUG if (TimeSpan.FromTicks(elapsed) > TimeSpan.FromMilliseconds(10)) - _logger.LogWarning(">10ms spike on {counterName}: {time}", counterName, TimeSpan.FromTicks(elapsed)); + _logger.LogWarning(">10ms spike on {counterName}: {time}", cn, TimeSpan.FromTicks(elapsed)); #endif list.Add(new(TimeOnly.FromDateTime(DateTime.Now), elapsed)); } @@ -79,14 +79,15 @@ public sealed class PerformanceCollectorService : IHostedService public Task StartAsync(CancellationToken cancellationToken) { _logger.LogInformation("Starting PerformanceCollectorService"); - _ = Task.Run(PeriodicLogPrune, _periodicLogPruneTask.Token); + _ = Task.Run(PeriodicLogPrune, _periodicLogPruneTaskCts.Token); _logger.LogInformation("Started PerformanceCollectorService"); return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) { - _periodicLogPruneTask.Cancel(); + _periodicLogPruneTaskCts.Cancel(); + _periodicLogPruneTaskCts.Dispose(); return Task.CompletedTask; } @@ -174,9 +175,9 @@ public sealed class PerformanceCollectorService : IHostedService private async Task PeriodicLogPrune() { - while (!_periodicLogPruneTask.Token.IsCancellationRequested) + while (!_periodicLogPruneTaskCts.Token.IsCancellationRequested) { - await Task.Delay(TimeSpan.FromMinutes(10), _periodicLogPruneTask.Token).ConfigureAwait(false); + await Task.Delay(TimeSpan.FromMinutes(10), _periodicLogPruneTaskCts.Token).ConfigureAwait(false); foreach (var entries in PerformanceCounters.ToList()) { diff --git a/MareSynchronos/UI/UISharedService.cs b/MareSynchronos/UI/UISharedService.cs index 5d50bc7..a0c6055 100644 --- a/MareSynchronos/UI/UISharedService.cs +++ b/MareSynchronos/UI/UISharedService.cs @@ -114,6 +114,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase }); GameFont = _pluginInterface.UiBuilder.FontAtlas.NewGameFontHandle(new(GameFontFamilyAndSize.Axis12)); IconFont = _pluginInterface.UiBuilder.IconFontHandle; + } public ApiController ApiController => _apiController; @@ -294,9 +295,27 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase return ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X; } - public static bool IconButton(FontAwesomeIcon icon) + public static bool IconButton(FontAwesomeIcon icon, float? height = null) { - return ImGuiComponents.IconButton(icon); + string text = icon.ToIconString(); + + ImGui.PushID(text); + ImGui.PushFont(UiBuilder.IconFont); + Vector2 vector = ImGui.CalcTextSize(text); + ImGui.PopFont(); + ImDrawListPtr windowDrawList = ImGui.GetWindowDrawList(); + Vector2 cursorScreenPos = ImGui.GetCursorScreenPos(); + float x = vector.X + ImGui.GetStyle().FramePadding.X * 2f; + float frameHeight = height ?? ImGui.GetFrameHeight(); + bool result = ImGui.Button(string.Empty, new Vector2(x, frameHeight)); + Vector2 pos = new Vector2(cursorScreenPos.X + ImGui.GetStyle().FramePadding.X, + cursorScreenPos.Y + (height ?? ImGui.GetFrameHeight()) / 2f - (vector.Y / 2f)); + ImGui.PushFont(UiBuilder.IconFont); + windowDrawList.AddText(pos, ImGui.GetColorU32(ImGuiCol.Text), text); + ImGui.PopFont(); + ImGui.PopID(); + + return result; } private static bool IconTextButtonInternal(FontAwesomeIcon icon, string text, Vector4? defaultColor = null, float? width = null) diff --git a/MareSynchronos/Utils/Crypto.cs b/MareSynchronos/Utils/Crypto.cs index e4d81fb..3353891 100644 --- a/MareSynchronos/Utils/Crypto.cs +++ b/MareSynchronos/Utils/Crypto.cs @@ -8,6 +8,7 @@ public static class Crypto #pragma warning disable SYSLIB0021 // Type or member is obsolete private static readonly Dictionary _hashListSHA1 = new(StringComparer.Ordinal); + private static readonly Dictionary<(string, ushort), string> _hashListPlayersSHA256 = new(); private static readonly Dictionary _hashListSHA256 = new(StringComparer.Ordinal); private static readonly SHA256CryptoServiceProvider _sha256CryptoProvider = new(); private static readonly SHA1CryptoServiceProvider _sha1CryptoProvider = new(); @@ -23,6 +24,15 @@ public static class Crypto return GetOrComputeHashSHA1(stringToHash); } + public static string GetHash256(this (string, ushort) playerToHash) + { + if (_hashListPlayersSHA256.TryGetValue(playerToHash, out var hash)) + return hash; + + return _hashListPlayersSHA256[playerToHash] = + BitConverter.ToString(_sha256CryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(playerToHash.Item1 + playerToHash.Item2.ToString()))).Replace("-", "", StringComparison.Ordinal); + } + public static string GetHash256(this string stringToHash) { return GetOrComputeHashSHA256(stringToHash); @@ -33,7 +43,7 @@ public static class Crypto if (_hashListSHA256.TryGetValue(stringToCompute, out var hash)) return hash; - return _hashListSHA256[stringToCompute] = + return _hashListSHA256[stringToCompute] = BitConverter.ToString(_sha256CryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToCompute))).Replace("-", "", StringComparison.Ordinal); } @@ -42,7 +52,7 @@ public static class Crypto if (_hashListSHA1.TryGetValue(stringToCompute, out var hash)) return hash; - return _hashListSHA1[stringToCompute] = + return _hashListSHA1[stringToCompute] = BitConverter.ToString(_sha1CryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToCompute))).Replace("-", "", StringComparison.Ordinal); } #pragma warning restore SYSLIB0021 // Type or member is obsolete diff --git a/MareSynchronos/Utils/MareInterpolatedStringHandler.cs b/MareSynchronos/Utils/MareInterpolatedStringHandler.cs new file mode 100644 index 0000000..2f96533 --- /dev/null +++ b/MareSynchronos/Utils/MareInterpolatedStringHandler.cs @@ -0,0 +1,27 @@ +using System.Runtime.CompilerServices; +using System.Text; + +namespace MareSynchronos.Utils; + +[InterpolatedStringHandler] +public readonly ref struct MareInterpolatedStringHandler +{ + readonly StringBuilder _logMessageStringbuilder; + + public MareInterpolatedStringHandler(int literalLength, int formattedCount) + { + _logMessageStringbuilder = new StringBuilder(literalLength); + } + + public void AppendLiteral(string s) + { + _logMessageStringbuilder.Append(s); + } + + public void AppendFormatted(T t) + { + _logMessageStringbuilder.Append(t?.ToString()); + } + + public string BuildMessage() => _logMessageStringbuilder.ToString(); +}