From 6f1c00041b7ba8a792d17da63110f05bbea88d4c Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Mon, 12 Feb 2024 17:02:49 +0100 Subject: [PATCH] minor performance improvements --- .../PlayerData/Handlers/GameObjectHandler.cs | 5 +- MareSynchronos/Services/DalamudUtilService.cs | 336 +++++++++--------- .../Services/Mediator/MareMediator.cs | 35 +- MareSynchronos/Utils/Crypto.cs | 23 +- 4 files changed, 200 insertions(+), 199 deletions(-) diff --git a/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs b/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs index ee2e71f..9514e08 100644 --- a/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs +++ b/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs @@ -1,4 +1,5 @@ -using FFXIVClientStructs.FFXIV.Client.Game.Character; +using Dalamud.Memory; +using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using MareSynchronos.Services; using MareSynchronos.Services.Mediator; @@ -209,7 +210,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase _clearCts = null; } var chara = (Character*)Address; - var name = new ByteString(chara->GameObject.Name).ToString(); + MemoryHelper.ReadStringNullTerminated((nint)chara->GameObject.Name, out var name); bool nameChange = !string.Equals(name, Name, StringComparison.Ordinal); if (nameChange) { diff --git a/MareSynchronos/Services/DalamudUtilService.cs b/MareSynchronos/Services/DalamudUtilService.cs index 560f6de..35f5f55 100644 --- a/MareSynchronos/Services/DalamudUtilService.cs +++ b/MareSynchronos/Services/DalamudUtilService.cs @@ -1,5 +1,6 @@ using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Objects; +using Dalamud.Memory; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Control; @@ -386,7 +387,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber if (!_playerInfoCache.TryGetValue(id, out var info)) { info.Character.ObjectId = id; - info.Character.Name = chara.Name.ToString(); + MemoryHelper.ReadStringNullTerminated((nint)((GameObject*)chara.Address)->Name, out info.Character.Name); info.Character.HomeWorldId = ((BattleChara*)chara.Address)->Character.HomeWorld; info.Character.Address = chara.Address; info.Hash = Crypto.GetHash256(info.Character.Name + info.Character.HomeWorldId.ToString()); @@ -400,59 +401,57 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber private unsafe void CheckCharacterForDrawing(PlayerCharacter p) { - if (!IsAnythingDrawing) + var gameObj = (GameObject*)p.Address; + var drawObj = gameObj->DrawObject; + var playerName = p.Name; + bool isDrawing = false; + bool isDrawingChanged = false; + if ((nint)drawObj != IntPtr.Zero) { - var gameObj = (GameObject*)p.Address; - var drawObj = gameObj->DrawObject; - bool isDrawing = false; - bool isDrawingChanged = false; - if ((nint)drawObj != IntPtr.Zero) + isDrawing = gameObj->RenderFlags == 0b100000000000; + if (!isDrawing) { - isDrawing = gameObj->RenderFlags == 0b100000000000; + isDrawing = ((CharacterBase*)drawObj)->HasModelInSlotLoaded != 0; if (!isDrawing) { - isDrawing = ((CharacterBase*)drawObj)->HasModelInSlotLoaded != 0; - if (!isDrawing) + isDrawing = ((CharacterBase*)drawObj)->HasModelFilesInSlotLoaded != 0; + if (isDrawing && !string.Equals(_lastGlobalBlockPlayer, playerName, StringComparison.Ordinal) + && !string.Equals(_lastGlobalBlockReason, "HasModelFilesInSlotLoaded", StringComparison.Ordinal)) { - isDrawing = ((CharacterBase*)drawObj)->HasModelFilesInSlotLoaded != 0; - if (isDrawing && !string.Equals(_lastGlobalBlockPlayer, p.Name, StringComparison.Ordinal) - && !string.Equals(_lastGlobalBlockReason, "HasModelFilesInSlotLoaded", StringComparison.Ordinal)) - { - _lastGlobalBlockPlayer = p.Name; - _lastGlobalBlockReason = "HasModelFilesInSlotLoaded"; - isDrawingChanged = true; - } - } - else - { - if (!string.Equals(_lastGlobalBlockPlayer, p.Name, StringComparison.Ordinal) - && !string.Equals(_lastGlobalBlockReason, "HasModelInSlotLoaded", StringComparison.Ordinal)) - { - _lastGlobalBlockPlayer = p.Name; - _lastGlobalBlockReason = "HasModelInSlotLoaded"; - isDrawingChanged = true; - } + _lastGlobalBlockPlayer = playerName; + _lastGlobalBlockReason = "HasModelFilesInSlotLoaded"; + isDrawingChanged = true; } } else { - if (!string.Equals(_lastGlobalBlockPlayer, p.Name, StringComparison.Ordinal) - && !string.Equals(_lastGlobalBlockReason, "RenderFlags", StringComparison.Ordinal)) + if (!string.Equals(_lastGlobalBlockPlayer, playerName, StringComparison.Ordinal) + && !string.Equals(_lastGlobalBlockReason, "HasModelInSlotLoaded", StringComparison.Ordinal)) { - _lastGlobalBlockPlayer = p.Name; - _lastGlobalBlockReason = "RenderFlags"; + _lastGlobalBlockPlayer = playerName; + _lastGlobalBlockReason = "HasModelInSlotLoaded"; isDrawingChanged = true; } } } - - if (isDrawingChanged) + else { - _logger.LogTrace("Global draw block: START => {name} ({reason})", p.Name, _lastGlobalBlockReason); + if (!string.Equals(_lastGlobalBlockPlayer, playerName, StringComparison.Ordinal) + && !string.Equals(_lastGlobalBlockReason, "RenderFlags", StringComparison.Ordinal)) + { + _lastGlobalBlockPlayer = playerName; + _lastGlobalBlockReason = "RenderFlags"; + isDrawingChanged = true; + } } - - IsAnythingDrawing |= isDrawing; } + + if (isDrawingChanged) + { + _logger.LogTrace("Global draw block: START => {name} ({reason})", playerName, _lastGlobalBlockReason); + } + + IsAnythingDrawing |= isDrawing; } private void FrameworkOnUpdate(IFramework framework) @@ -467,141 +466,156 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber return; } - IsAnythingDrawing = false; - _performanceCollector.LogPerformance(this, "ObjTableToCharas", - () => + bool isNormalFrameworkUpdate = DateTime.Now < _delayedFrameworkUpdateCheck.AddSeconds(1); + + _performanceCollector.LogPerformance(this, "FrameworkOnUpdateInternal+" + (isNormalFrameworkUpdate ? "Regular" : "Delayed"), () => + { + IsAnythingDrawing = false; + _performanceCollector.LogPerformance(this, "ObjTableToCharas", + () => + { + if (_sentBetweenAreas) + return; + + _notUpdatedCharas.AddRange(_playerCharas.Keys); + + for (int i = 0; i < 200; i += 2) + { + var chara = _objectTable[i]; + if (chara == null || chara.ObjectKind != Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player) + continue; + + var info = GetPlayerInfo(chara); + + if (!IsAnythingDrawing) + CheckCharacterForDrawing(info.Character); + _notUpdatedCharas.Remove(info.Hash); + _playerCharas[info.Hash] = info.Character; + } + + foreach (var notUpdatedChara in _notUpdatedCharas) + { + _playerCharas.Remove(notUpdatedChara); + } + + _notUpdatedCharas.Clear(); + }); + + if (!IsAnythingDrawing && !string.IsNullOrEmpty(_lastGlobalBlockPlayer)) { - _notUpdatedCharas.AddRange(_playerCharas.Keys); - - foreach (var chara in _objectTable) - { - if (chara.ObjectIndex % 2 != 0 || chara.ObjectIndex >= 200) continue; - - string charaName = chara.Name.ToString(); - uint homeWorldId = ((BattleChara*)chara.Address)->Character.HomeWorld; - - var info = GetPlayerInfo(chara); - if (!IsAnythingDrawing) - CheckCharacterForDrawing(info.Character); - _notUpdatedCharas.Remove(info.Hash); - _playerCharas[info.Hash] = info.Character; - } - - foreach (var notUpdatedChara in _notUpdatedCharas) - { - _playerCharas.Remove(notUpdatedChara); - } - - _notUpdatedCharas.Clear(); - }); - - if (!IsAnythingDrawing && !string.IsNullOrEmpty(_lastGlobalBlockPlayer)) - { - _logger.LogTrace("Global draw block: END => {name}", _lastGlobalBlockPlayer); - _lastGlobalBlockPlayer = string.Empty; - _lastGlobalBlockReason = string.Empty; - } - - if (GposeTarget != null && !IsInGpose) - { - _logger.LogDebug("Gpose start"); - IsInGpose = true; - Mediator.Publish(new GposeStartMessage()); - } - else if (GposeTarget == null && IsInGpose) - { - _logger.LogDebug("Gpose end"); - IsInGpose = false; - 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) - { - _logger.LogDebug("Cutscene start"); - IsInCutscene = true; - Mediator.Publish(new CutsceneStartMessage()); - Mediator.Publish(new HaltScanMessage(nameof(IsInCutscene))); - } - else if (!_condition[ConditionFlag.WatchingCutscene] && IsInCutscene) - { - _logger.LogDebug("Cutscene end"); - IsInCutscene = false; - Mediator.Publish(new CutsceneEndMessage()); - Mediator.Publish(new ResumeScanMessage(nameof(IsInCutscene))); - } - - if (IsInCutscene) { Mediator.Publish(new CutsceneFrameworkUpdateMessage()); return; } - - if (_condition[ConditionFlag.BetweenAreas] || _condition[ConditionFlag.BetweenAreas51]) - { - var zone = _clientState.TerritoryType; - if (_lastZone != zone) - { - _lastZone = zone; - if (!_sentBetweenAreas) - { - _logger.LogDebug("Zone switch/Gpose start"); - _sentBetweenAreas = true; - Mediator.Publish(new ZoneSwitchStartMessage()); - Mediator.Publish(new HaltScanMessage(nameof(ConditionFlag.BetweenAreas))); - _playerInfoCache.Clear(); - } + _logger.LogTrace("Global draw block: END => {name}", _lastGlobalBlockPlayer); + _lastGlobalBlockPlayer = string.Empty; + _lastGlobalBlockReason = string.Empty; } - return; - } + if (GposeTarget != null && !IsInGpose) + { + _logger.LogDebug("Gpose start"); + IsInGpose = true; + Mediator.Publish(new GposeStartMessage()); + } + else if (GposeTarget == null && IsInGpose) + { + _logger.LogDebug("Gpose end"); + IsInGpose = false; + Mediator.Publish(new GposeEndMessage()); + } - if (_sentBetweenAreas) - { - _logger.LogDebug("Zone switch/Gpose end"); - _sentBetweenAreas = false; - Mediator.Publish(new ZoneSwitchEndMessage()); - Mediator.Publish(new ResumeScanMessage(nameof(ConditionFlag.BetweenAreas))); - } + 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 (!IsInCombat) - Mediator.Publish(new FrameworkUpdateMessage()); + if (_condition[ConditionFlag.WatchingCutscene] && !IsInCutscene) + { + _logger.LogDebug("Cutscene start"); + IsInCutscene = true; + Mediator.Publish(new CutsceneStartMessage()); + Mediator.Publish(new HaltScanMessage(nameof(IsInCutscene))); + } + else if (!_condition[ConditionFlag.WatchingCutscene] && IsInCutscene) + { + _logger.LogDebug("Cutscene end"); + IsInCutscene = false; + Mediator.Publish(new CutsceneEndMessage()); + Mediator.Publish(new ResumeScanMessage(nameof(IsInCutscene))); + } - Mediator.Publish(new PriorityFrameworkUpdateMessage()); + if (IsInCutscene) + { + Mediator.Publish(new CutsceneFrameworkUpdateMessage()); + return; + } - if (DateTime.Now < _delayedFrameworkUpdateCheck.AddSeconds(1)) return; + if (_condition[ConditionFlag.BetweenAreas] || _condition[ConditionFlag.BetweenAreas51]) + { + var zone = _clientState.TerritoryType; + if (_lastZone != zone) + { + _lastZone = zone; + if (!_sentBetweenAreas) + { + _logger.LogDebug("Zone switch/Gpose start"); + _sentBetweenAreas = true; + _playerInfoCache.Clear(); + Mediator.Publish(new ZoneSwitchStartMessage()); + Mediator.Publish(new HaltScanMessage(nameof(ConditionFlag.BetweenAreas))); + } + } - var localPlayer = _clientState.LocalPlayer; + return; + } - if (localPlayer != null && !IsLoggedIn) - { - _logger.LogDebug("Logged in"); - IsLoggedIn = true; - _lastZone = _clientState.TerritoryType; - Mediator.Publish(new DalamudLoginMessage()); - } - else if (localPlayer == null && IsLoggedIn) - { - _logger.LogDebug("Logged out"); - IsLoggedIn = false; - Mediator.Publish(new DalamudLogoutMessage()); - } + if (_sentBetweenAreas) + { + _logger.LogDebug("Zone switch/Gpose end"); + _sentBetweenAreas = false; + Mediator.Publish(new ZoneSwitchEndMessage()); + Mediator.Publish(new ResumeScanMessage(nameof(ConditionFlag.BetweenAreas))); + } - if (IsInCombat) - Mediator.Publish(new FrameworkUpdateMessage()); + if (!IsInCombat) + _performanceCollector.LogPerformance(this, "FrameworkOnUpdateInternal>MediatorFrameworkUpdate", + () => Mediator.Publish(new FrameworkUpdateMessage())); - Mediator.Publish(new DelayedFrameworkUpdateMessage()); + Mediator.Publish(new PriorityFrameworkUpdateMessage()); - _delayedFrameworkUpdateCheck = DateTime.Now; + if (isNormalFrameworkUpdate) + return; + + var localPlayer = _clientState.LocalPlayer; + + if (localPlayer != null && !IsLoggedIn) + { + _logger.LogDebug("Logged in"); + IsLoggedIn = true; + _lastZone = _clientState.TerritoryType; + Mediator.Publish(new DalamudLoginMessage()); + } + else if (localPlayer == null && IsLoggedIn) + { + _logger.LogDebug("Logged out"); + IsLoggedIn = false; + Mediator.Publish(new DalamudLogoutMessage()); + } + + if (IsInCombat) + _performanceCollector.LogPerformance(this, "FrameworkOnUpdateInternal>MediatorFrameworkUpdate", + () => Mediator.Publish(new FrameworkUpdateMessage())); + + Mediator.Publish(new DelayedFrameworkUpdateMessage()); + + _delayedFrameworkUpdateCheck = DateTime.Now; + }); } } \ No newline at end of file diff --git a/MareSynchronos/Services/Mediator/MareMediator.cs b/MareSynchronos/Services/Mediator/MareMediator.cs index 5489aa5..b5a654b 100644 --- a/MareSynchronos/Services/Mediator/MareMediator.cs +++ b/MareSynchronos/Services/Mediator/MareMediator.cs @@ -130,40 +130,43 @@ public sealed class MareMediator : IHostedService { if (!_subscriberDict.TryGetValue(message.GetType(), out HashSet? subscribers) || subscribers == null || !subscribers.Any()) return; - HashSet subscribersCopy = []; + List subscribersCopy = []; lock (_addRemoveLock) { - subscribersCopy = subscribers?.Where(s => s.Subscriber != null).ToHashSet() ?? []; + subscribersCopy = subscribers?.Where(s => s.Subscriber != null).ToList() ?? []; } - foreach (SubscriberAction subscriber in subscribersCopy) +#pragma warning disable S3011 // Reflection should not be used to increase accessibility of classes, methods, or fields + GetType() + .GetMethod(nameof(ExecuteReflected), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)? + .MakeGenericMethod(message.GetType())? + .Invoke(this, [subscribersCopy, message]); +#pragma warning restore S3011 // Reflection should not be used to increase accessibility of classes, methods, or fields + } + + private void ExecuteReflected(List subscribers, T message) where T : MessageBase + { + var msgTypeName = message.GetType().Name; + foreach (SubscriberAction subscriber in subscribers) { try { -#pragma warning disable S3011 // Reflection should not be used to increase accessibility of classes, methods, or fields - typeof(MareMediator) - .GetMethod(nameof(ExecuteSubscriber), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)? - .MakeGenericMethod(message.GetType()) - .Invoke(this, new object[] { subscriber, message }); -#pragma warning restore S3011 // Reflection should not be used to increase accessibility of classes, methods, or fields + var isSameThread = message.KeepThreadContext ? "$" : string.Empty; + _performanceCollector.LogPerformance(this, $"{isSameThread}Execute>{msgTypeName}+{subscriber.Subscriber.GetType().Name}>{subscriber.Subscriber}", + () => ((Action)subscriber.Action).Invoke(message)); } catch (Exception ex) { if (_lastErrorTime.TryGetValue(subscriber, out var lastErrorTime) && lastErrorTime.Add(TimeSpan.FromSeconds(10)) > DateTime.UtcNow) continue; - _logger.LogError(ex.InnerException ?? ex, "Error executing {type} for subscriber {subscriber}", message.GetType().Name, subscriber.Subscriber.GetType().Name); + _logger.LogError(ex.InnerException ?? ex, "Error executing {type} for subscriber {subscriber}", + message.GetType().Name, subscriber.Subscriber.GetType().Name); _lastErrorTime[subscriber] = DateTime.UtcNow; } } } - private void ExecuteSubscriber(SubscriberAction subscriber, T message) where T : MessageBase - { - var isSameThread = message.KeepThreadContext ? "$" : string.Empty; - _performanceCollector.LogPerformance(this, $"{isSameThread}Execute>{message.GetType().Name}+{subscriber.Subscriber.GetType().Name}>{subscriber.Subscriber}", () => ((Action)subscriber.Action).Invoke(message)); - } - private sealed class SubscriberAction { public SubscriberAction(IMediatorSubscriber subscriber, object action) diff --git a/MareSynchronos/Utils/Crypto.cs b/MareSynchronos/Utils/Crypto.cs index a954c02..97389f1 100644 --- a/MareSynchronos/Utils/Crypto.cs +++ b/MareSynchronos/Utils/Crypto.cs @@ -7,10 +7,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 _hashListSHA256 = new(StringComparer.Ordinal); private static readonly SHA256CryptoServiceProvider _sha256CryptoProvider = new(); - private static readonly SHA1CryptoServiceProvider _sha1CryptoProvider = new(); public static string GetFileHash(this string filePath) { @@ -18,31 +15,17 @@ public static class Crypto return BitConverter.ToString(cryptoProvider.ComputeHash(File.ReadAllBytes(filePath))).Replace("-", "", StringComparison.Ordinal); } - public static string GetHash(this string stringToHash) - { - return GetOrComputeHashSHA1(stringToHash); - } - public static string GetHash256(this string stringToHash) { return GetOrComputeHashSHA256(stringToHash); } - private static string GetOrComputeHashSHA1(string stringToCompute) - { - if (_hashListSHA1.TryGetValue(stringToCompute, out var hash)) - return hash; - - return _hashListSHA1[stringToCompute] = BitConverter.ToString(_sha1CryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToCompute))).Replace("-", "", StringComparison.Ordinal); - } - private static string GetOrComputeHashSHA256(string stringToCompute) { - if (_hashListSHA256.TryGetValue(stringToCompute, out var hash)) - return hash; - - return _hashListSHA256[stringToCompute] = BitConverter.ToString(_sha256CryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToCompute))).Replace("-", "", StringComparison.Ordinal); + return BitConverter.ToString(_sha256CryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToCompute))).Replace("-", "", StringComparison.Ordinal); } + + #pragma warning restore SYSLIB0021 // Type or member is obsolete } \ No newline at end of file