From 9048b3bd87bb384f927646a6c770376484ac7b51 Mon Sep 17 00:00:00 2001 From: defnotken Date: Mon, 5 Jan 2026 15:07:48 -0600 Subject: [PATCH 1/3] more checks on drawing --- LightlessSync/Services/DalamudUtilService.cs | 83 ++++++++++++-------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/LightlessSync/Services/DalamudUtilService.cs b/LightlessSync/Services/DalamudUtilService.cs index 0806b42..2606d7b 100644 --- a/LightlessSync/Services/DalamudUtilService.cs +++ b/LightlessSync/Services/DalamudUtilService.cs @@ -958,59 +958,78 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber if (address == nint.Zero) return; - var gameObj = (GameObject*)address; - - if (gameObj == null || gameObj->ObjectKind == 0) - return; - var drawObj = gameObj->DrawObject; - bool isDrawing = false; - bool isDrawingChanged = false; - if ((nint)drawObj != IntPtr.Zero) + try { - isDrawing = gameObj->RenderFlags == (VisibilityFlags)0b100000000000; - if (!isDrawing) + var gameObj = (GameObject*)address; + + if (gameObj == null || gameObj->ObjectKind == 0) + return; + + if (!IsGameObjectPresent(address)) { - isDrawing = ((CharacterBase*)drawObj)->HasModelInSlotLoaded != 0; + _logger.LogDebug("Character {name} at {addr} no longer present in object table", characterName, address.ToString("X")); + return; + } + + var drawObj = gameObj->DrawObject; + bool isDrawing = false; + bool isDrawingChanged = false; + + if ((nint)drawObj != IntPtr.Zero) + { + isDrawing = gameObj->RenderFlags == (VisibilityFlags)0b100000000000; if (!isDrawing) { - isDrawing = ((CharacterBase*)drawObj)->HasModelFilesInSlotLoaded != 0; - if (isDrawing && !string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal) - && !string.Equals(_lastGlobalBlockReason, "HasModelFilesInSlotLoaded", StringComparison.Ordinal)) + var charBase = (CharacterBase*)drawObj; + if (charBase != null) { - _lastGlobalBlockPlayer = characterName; - _lastGlobalBlockReason = "HasModelFilesInSlotLoaded"; - isDrawingChanged = true; + isDrawing = charBase->HasModelInSlotLoaded != 0; + if (!isDrawing) + { + isDrawing = charBase->HasModelFilesInSlotLoaded != 0; + if (isDrawing && !string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal) + && !string.Equals(_lastGlobalBlockReason, "HasModelFilesInSlotLoaded", StringComparison.Ordinal)) + { + _lastGlobalBlockPlayer = characterName; + _lastGlobalBlockReason = "HasModelFilesInSlotLoaded"; + isDrawingChanged = true; + } + } + else + { + if (!string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal) + && !string.Equals(_lastGlobalBlockReason, "HasModelInSlotLoaded", StringComparison.Ordinal)) + { + _lastGlobalBlockPlayer = characterName; + _lastGlobalBlockReason = "HasModelInSlotLoaded"; + isDrawingChanged = true; + } + } } } else { if (!string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal) - && !string.Equals(_lastGlobalBlockReason, "HasModelInSlotLoaded", StringComparison.Ordinal)) + && !string.Equals(_lastGlobalBlockReason, "RenderFlags", StringComparison.Ordinal)) { _lastGlobalBlockPlayer = characterName; - _lastGlobalBlockReason = "HasModelInSlotLoaded"; + _lastGlobalBlockReason = "RenderFlags"; isDrawingChanged = true; } } } - else + + if (isDrawingChanged) { - if (!string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal) - && !string.Equals(_lastGlobalBlockReason, "RenderFlags", StringComparison.Ordinal)) - { - _lastGlobalBlockPlayer = characterName; - _lastGlobalBlockReason = "RenderFlags"; - isDrawingChanged = true; - } + _logger.LogTrace("Global draw block: START => {name} ({reason})", characterName, _lastGlobalBlockReason); } - } - if (isDrawingChanged) + IsAnythingDrawing |= isDrawing; + } + catch (AccessViolationException ex) { - _logger.LogTrace("Global draw block: START => {name} ({reason})", characterName, _lastGlobalBlockReason); + _logger.LogWarning(ex, "access violation checking character {name} at {addr}", characterName, address.ToString("X")); } - - IsAnythingDrawing |= isDrawing; } private void FrameworkOnUpdate(IFramework framework) From d00df84ed608ca4a25ab5391f5a027625d3f3ac6 Mon Sep 17 00:00:00 2001 From: defnotken Date: Mon, 5 Jan 2026 15:39:18 -0600 Subject: [PATCH 2/3] even more violation checks.... --- LightlessSync/Services/DalamudUtilService.cs | 172 ++++++++++++------- 1 file changed, 111 insertions(+), 61 deletions(-) diff --git a/LightlessSync/Services/DalamudUtilService.cs b/LightlessSync/Services/DalamudUtilService.cs index 2606d7b..684c011 100644 --- a/LightlessSync/Services/DalamudUtilService.cs +++ b/LightlessSync/Services/DalamudUtilService.cs @@ -962,50 +962,68 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber { var gameObj = (GameObject*)address; - if (gameObj == null || gameObj->ObjectKind == 0) + if (gameObj == null) return; - if (!IsGameObjectPresent(address)) + if (!_objectTable.Any(o => o?.Address == address)) { - _logger.LogDebug("Character {name} at {addr} no longer present in object table", characterName, address.ToString("X")); + _logger.LogDebug("Character {name} at {addr} no longer in object table", characterName, address.ToString("X")); return; } + if (gameObj->ObjectKind == 0) + return; + var drawObj = gameObj->DrawObject; bool isDrawing = false; bool isDrawingChanged = false; if ((nint)drawObj != IntPtr.Zero) { - isDrawing = gameObj->RenderFlags == (VisibilityFlags)0b100000000000; + try + { + isDrawing = gameObj->RenderFlags == (VisibilityFlags)0b100000000000; + } + catch (AccessViolationException) + { + return; + } + if (!isDrawing) { - var charBase = (CharacterBase*)drawObj; - if (charBase != null) + try { - isDrawing = charBase->HasModelInSlotLoaded != 0; - if (!isDrawing) + var charBase = (CharacterBase*)drawObj; + if (charBase != null) { - isDrawing = charBase->HasModelFilesInSlotLoaded != 0; - if (isDrawing && !string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal) - && !string.Equals(_lastGlobalBlockReason, "HasModelFilesInSlotLoaded", StringComparison.Ordinal)) + isDrawing = charBase->HasModelInSlotLoaded != 0; + if (!isDrawing) { - _lastGlobalBlockPlayer = characterName; - _lastGlobalBlockReason = "HasModelFilesInSlotLoaded"; - isDrawingChanged = true; - } - } - else - { - if (!string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal) - && !string.Equals(_lastGlobalBlockReason, "HasModelInSlotLoaded", StringComparison.Ordinal)) - { - _lastGlobalBlockPlayer = characterName; - _lastGlobalBlockReason = "HasModelInSlotLoaded"; - isDrawingChanged = true; + isDrawing = charBase->HasModelFilesInSlotLoaded != 0; + if (isDrawing && !string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal) + && !string.Equals(_lastGlobalBlockReason, "HasModelFilesInSlotLoaded", StringComparison.Ordinal)) + { + _lastGlobalBlockPlayer = characterName; + _lastGlobalBlockReason = "HasModelFilesInSlotLoaded"; + isDrawingChanged = true; + } + } + else + { + if (!string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal) + && !string.Equals(_lastGlobalBlockReason, "HasModelInSlotLoaded", StringComparison.Ordinal)) + { + _lastGlobalBlockPlayer = characterName; + _lastGlobalBlockReason = "HasModelInSlotLoaded"; + isDrawingChanged = true; + } } } } + catch (AccessViolationException) + { + return; + } } else { @@ -1028,7 +1046,11 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber } catch (AccessViolationException ex) { - _logger.LogWarning(ex, "access violation checking character {name} at {addr}", characterName, address.ToString("X")); + _logger.LogDebug(ex, "Memory access violation checking character {name} at {addr}", characterName, address.ToString("X")); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Unexpected error checking character {name} at {addr}", characterName, address.ToString("X")); } } @@ -1057,51 +1079,79 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber _actorObjectService.RefreshTrackedActors(); } + // CRITICAL: Capture snapshot once to prevent mid-iteration changes var playerDescriptors = _actorObjectService.PlayerDescriptors; - for (var i = 0; i < playerDescriptors.Count; i++) + var descriptorCount = playerDescriptors.Count; + + for (var i = 0; i < descriptorCount; i++) { - var actor = playerDescriptors[i]; - - var playerAddress = actor.Address; - if (playerAddress == nint.Zero) - continue; - - if (actor.ObjectIndex >= 200) - continue; - - if (_blockedCharacterHandler.IsCharacterBlocked(playerAddress, actor.ObjectIndex, out bool firstTime) && firstTime) + try { - _logger.LogTrace("Skipping character {addr}, blocked/muted", playerAddress.ToString("X")); - continue; - } + // Revalidate the count in case collection changed + if (i >= playerDescriptors.Count) + break; - if (!IsAnythingDrawing) - { - try + var actor = playerDescriptors[i]; + + var playerAddress = actor.Address; + if (playerAddress == nint.Zero) + continue; + + if (actor.ObjectIndex >= 200) + continue; + + if (_blockedCharacterHandler.IsCharacterBlocked(playerAddress, actor.ObjectIndex, out bool firstTime) && firstTime) { - var gameObj = (GameObject*)playerAddress; - - if (gameObj == null || gameObj->ObjectKind == 0) - { - continue; - } - - var currentName = gameObj->NameString ?? string.Empty; - var charaName = string.IsNullOrEmpty(currentName) ? actor.Name : currentName; - - CheckCharacterForDrawing(playerAddress, charaName); - - if (IsAnythingDrawing) - break; - } - catch (AccessViolationException ex) - { - _logger.LogWarning(ex, "Memory access violation reading character at {addr}", playerAddress.ToString("X")); + _logger.LogTrace("Skipping character {addr}, blocked/muted", playerAddress.ToString("X")); continue; } + + if (!IsAnythingDrawing) + { + // Wrap ALL pointer access in try-catch + try + { + var gameObj = (GameObject*)playerAddress; + + if (gameObj == null || gameObj->ObjectKind == 0) + { + continue; + } + + // Get name with protection - NameString internally dereferences pointers + string currentName; + try + { + currentName = gameObj->NameString ?? string.Empty; + } + catch (AccessViolationException) + { + currentName = string.Empty; + } + + var charaName = string.IsNullOrEmpty(currentName) ? actor.Name : currentName; + + CheckCharacterForDrawing(playerAddress, charaName); + + if (IsAnythingDrawing) + break; + } + catch (AccessViolationException ex) + { + _logger.LogDebug(ex, "Access violation on GameObject pointer for actor {index} at {addr}", i, playerAddress.ToString("X")); + } + } + } + catch (AccessViolationException ex) + { + _logger.LogDebug(ex, "Access violation processing actor {index} - object likely destroyed", i); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Unexpected error processing actor {index}", i); } } - }); + }); if (!IsAnythingDrawing && !string.IsNullOrEmpty(_lastGlobalBlockPlayer)) { From 4eec363cd286787fb5964764a6e41c8c8739abda Mon Sep 17 00:00:00 2001 From: defnotken Date: Mon, 5 Jan 2026 15:40:32 -0600 Subject: [PATCH 3/3] yeet some comments --- LightlessSync/Services/DalamudUtilService.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/LightlessSync/Services/DalamudUtilService.cs b/LightlessSync/Services/DalamudUtilService.cs index 684c011..9ae2a39 100644 --- a/LightlessSync/Services/DalamudUtilService.cs +++ b/LightlessSync/Services/DalamudUtilService.cs @@ -1079,7 +1079,6 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber _actorObjectService.RefreshTrackedActors(); } - // CRITICAL: Capture snapshot once to prevent mid-iteration changes var playerDescriptors = _actorObjectService.PlayerDescriptors; var descriptorCount = playerDescriptors.Count; @@ -1087,7 +1086,6 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber { try { - // Revalidate the count in case collection changed if (i >= playerDescriptors.Count) break; @@ -1108,7 +1106,6 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber if (!IsAnythingDrawing) { - // Wrap ALL pointer access in try-catch try { var gameObj = (GameObject*)playerAddress; @@ -1118,7 +1115,6 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber continue; } - // Get name with protection - NameString internally dereferences pointers string currentName; try {