Added more null checks and redid active broadcasting cache.

This commit is contained in:
cake
2025-11-03 20:19:02 +01:00
parent b6aa2bebb1
commit 1b686e45dc

View File

@@ -15,8 +15,8 @@ using LightlessSync.UtilsEnum.Enum;
// Created using https://github.com/PunishedPineapple/Distance as a reference, thank you! // Created using https://github.com/PunishedPineapple/Distance as a reference, thank you!
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Collections.Immutable;
using System.Globalization; using System.Globalization;
using System.Text;
namespace LightlessSync.Services; namespace LightlessSync.Services;
@@ -32,10 +32,10 @@ public unsafe class NameplateHandler : IMediatorSubscriber
private readonly LightlessMediator _mediator; private readonly LightlessMediator _mediator;
public LightlessMediator Mediator => _mediator; public LightlessMediator Mediator => _mediator;
private bool mEnabled = false; private bool _mEnabled = false;
private bool _needsLabelRefresh = false; private bool _needsLabelRefresh = false;
private AddonNamePlate* mpNameplateAddon = null; private AddonNamePlate* _mpNameplateAddon = null;
private readonly AtkTextNode*[] mTextNodes = new AtkTextNode*[AddonNamePlate.NumNamePlateObjects]; private readonly AtkTextNode*[] _mTextNodes = new AtkTextNode*[AddonNamePlate.NumNamePlateObjects];
private readonly int[] _cachedNameplateTextWidths = new int[AddonNamePlate.NumNamePlateObjects]; private readonly int[] _cachedNameplateTextWidths = new int[AddonNamePlate.NumNamePlateObjects];
private readonly int[] _cachedNameplateTextHeights = new int[AddonNamePlate.NumNamePlateObjects]; private readonly int[] _cachedNameplateTextHeights = new int[AddonNamePlate.NumNamePlateObjects];
private readonly int[] _cachedNameplateContainerHeights = new int[AddonNamePlate.NumNamePlateObjects]; private readonly int[] _cachedNameplateContainerHeights = new int[AddonNamePlate.NumNamePlateObjects];
@@ -44,10 +44,10 @@ public unsafe class NameplateHandler : IMediatorSubscriber
internal const uint mNameplateNodeIDBase = 0x7D99D500; internal const uint mNameplateNodeIDBase = 0x7D99D500;
private const string DefaultLabelText = "LightFinder"; private const string DefaultLabelText = "LightFinder";
private const SeIconChar DefaultIcon = SeIconChar.Hyadelyn; private const SeIconChar DefaultIcon = SeIconChar.Hyadelyn;
private const int ContainerOffsetX = 50; private const int _containerOffsetX = 50;
private static readonly string DefaultIconGlyph = SeIconCharExtensions.ToIconString(DefaultIcon); private static readonly string DefaultIconGlyph = SeIconCharExtensions.ToIconString(DefaultIcon);
private volatile HashSet<string> _activeBroadcastingCids = []; private ImmutableHashSet<string> _activeBroadcastingCids = [];
public NameplateHandler(ILogger<NameplateHandler> logger, IAddonLifecycle addonLifecycle, IGameGui gameGui, DalamudUtilService dalamudUtil, LightlessConfigService configService, LightlessMediator mediator, IClientState clientState, PairManager pairManager) public NameplateHandler(ILogger<NameplateHandler> logger, IAddonLifecycle addonLifecycle, IGameGui gameGui, DalamudUtilService dalamudUtil, LightlessConfigService configService, LightlessMediator mediator, IClientState clientState, PairManager pairManager)
{ {
@@ -74,17 +74,17 @@ public unsafe class NameplateHandler : IMediatorSubscriber
DisableNameplate(); DisableNameplate();
DestroyNameplateNodes(); DestroyNameplateNodes();
_mediator.Unsubscribe<PriorityFrameworkUpdateMessage>(this); _mediator.Unsubscribe<PriorityFrameworkUpdateMessage>(this);
mpNameplateAddon = null; _mpNameplateAddon = null;
} }
internal void EnableNameplate() internal void EnableNameplate()
{ {
if (!mEnabled) if (!_mEnabled)
{ {
try try
{ {
_addonLifecycle.RegisterListener(AddonEvent.PostDraw, "NamePlate", NameplateDrawDetour); _addonLifecycle.RegisterListener(AddonEvent.PostDraw, "NamePlate", NameplateDrawDetour);
mEnabled = true; _mEnabled = true;
} }
catch (Exception e) catch (Exception e)
{ {
@@ -96,7 +96,7 @@ public unsafe class NameplateHandler : IMediatorSubscriber
internal void DisableNameplate() internal void DisableNameplate()
{ {
if (mEnabled) if (_mEnabled)
{ {
try try
{ {
@@ -107,7 +107,7 @@ public unsafe class NameplateHandler : IMediatorSubscriber
_logger.LogError($"Unknown error while unregistering nameplate listener:\n{e}"); _logger.LogError($"Unknown error while unregistering nameplate listener:\n{e}");
} }
mEnabled = false; _mEnabled = false;
HideAllNameplateNodes(); HideAllNameplateNodes();
} }
} }
@@ -116,15 +116,15 @@ public unsafe class NameplateHandler : IMediatorSubscriber
{ {
var pNameplateAddon = (AddonNamePlate*)args.Addon.Address; var pNameplateAddon = (AddonNamePlate*)args.Addon.Address;
if (mpNameplateAddon != pNameplateAddon) if (_mpNameplateAddon != pNameplateAddon)
{ {
for (int i = 0; i < mTextNodes.Length; ++i) mTextNodes[i] = null; for (int i = 0; i < _mTextNodes.Length; ++i) _mTextNodes[i] = null;
System.Array.Clear(_cachedNameplateTextWidths, 0, _cachedNameplateTextWidths.Length); System.Array.Clear(_cachedNameplateTextWidths, 0, _cachedNameplateTextWidths.Length);
System.Array.Clear(_cachedNameplateTextHeights, 0, _cachedNameplateTextHeights.Length); System.Array.Clear(_cachedNameplateTextHeights, 0, _cachedNameplateTextHeights.Length);
System.Array.Clear(_cachedNameplateContainerHeights, 0, _cachedNameplateContainerHeights.Length); System.Array.Clear(_cachedNameplateContainerHeights, 0, _cachedNameplateContainerHeights.Length);
System.Array.Fill(_cachedNameplateTextOffsets, int.MinValue); System.Array.Fill(_cachedNameplateTextOffsets, int.MinValue);
mpNameplateAddon = pNameplateAddon; _mpNameplateAddon = pNameplateAddon;
if (mpNameplateAddon != null) CreateNameplateNodes(); if (_mpNameplateAddon != null) CreateNameplateNodes();
} }
UpdateNameplateNodes(); UpdateNameplateNodes();
@@ -139,6 +139,11 @@ public unsafe class NameplateHandler : IMediatorSubscriber
continue; continue;
var pNameplateResNode = nameplateObject.Value.NameContainer; var pNameplateResNode = nameplateObject.Value.NameContainer;
if (pNameplateResNode == null)
continue;
if (pNameplateResNode->ChildNode == null)
continue;
var pNewNode = AtkNodeHelpers.CreateOrphanTextNode(mNameplateNodeIDBase + (uint)i, TextFlags.Edge | TextFlags.Glare); var pNewNode = AtkNodeHelpers.CreateOrphanTextNode(mNameplateNodeIDBase + (uint)i, TextFlags.Edge | TextFlags.Glare);
if (pNewNode != null) if (pNewNode != null)
@@ -150,7 +155,7 @@ public unsafe class NameplateHandler : IMediatorSubscriber
pLastChild->PrevSiblingNode = (AtkResNode*)pNewNode; pLastChild->PrevSiblingNode = (AtkResNode*)pNewNode;
nameplateObject.Value.RootComponentNode->Component->UldManager.UpdateDrawNodeList(); nameplateObject.Value.RootComponentNode->Component->UldManager.UpdateDrawNodeList();
pNewNode->AtkResNode.SetUseDepthBasedPriority(true); pNewNode->AtkResNode.SetUseDepthBasedPriority(true);
mTextNodes[i] = pNewNode; _mTextNodes[i] = pNewNode;
} }
} }
} }
@@ -158,12 +163,12 @@ public unsafe class NameplateHandler : IMediatorSubscriber
private void DestroyNameplateNodes() private void DestroyNameplateNodes()
{ {
var pCurrentNameplateAddon = (AddonNamePlate*)_gameGui.GetAddonByName("NamePlate", 1).Address; var pCurrentNameplateAddon = (AddonNamePlate*)_gameGui.GetAddonByName("NamePlate", 1).Address;
if (mpNameplateAddon == null || mpNameplateAddon != pCurrentNameplateAddon) if (_mpNameplateAddon == null || _mpNameplateAddon != pCurrentNameplateAddon)
return; return;
for (int i = 0; i < AddonNamePlate.NumNamePlateObjects; ++i) for (int i = 0; i < AddonNamePlate.NumNamePlateObjects; ++i)
{ {
var pTextNode = mTextNodes[i]; var pTextNode = _mTextNodes[i];
var pNameplateNode = GetNameplateComponentNode(i); var pNameplateNode = GetNameplateComponentNode(i);
if (pTextNode != null && pNameplateNode != null) if (pTextNode != null && pNameplateNode != null)
{ {
@@ -175,7 +180,7 @@ public unsafe class NameplateHandler : IMediatorSubscriber
pTextNode->AtkResNode.NextSiblingNode->PrevSiblingNode = pTextNode->AtkResNode.PrevSiblingNode; pTextNode->AtkResNode.NextSiblingNode->PrevSiblingNode = pTextNode->AtkResNode.PrevSiblingNode;
pNameplateNode->Component->UldManager.UpdateDrawNodeList(); pNameplateNode->Component->UldManager.UpdateDrawNodeList();
pTextNode->AtkResNode.Destroy(true); pTextNode->AtkResNode.Destroy(true);
mTextNodes[i] = null; _mTextNodes[i] = null;
} }
catch (Exception e) catch (Exception e)
{ {
@@ -192,7 +197,7 @@ public unsafe class NameplateHandler : IMediatorSubscriber
private void HideAllNameplateNodes() private void HideAllNameplateNodes()
{ {
for (int i = 0; i < mTextNodes.Length; ++i) for (int i = 0; i < _mTextNodes.Length; ++i)
{ {
HideNameplateTextNode(i); HideNameplateTextNode(i);
} }
@@ -200,22 +205,34 @@ public unsafe class NameplateHandler : IMediatorSubscriber
private void UpdateNameplateNodes() private void UpdateNameplateNodes()
{ {
var framework = Framework.Instance(); var currentAddon = (AddonNamePlate*)_gameGui.GetAddonByName("NamePlate", 1).Address;
var ui3DModule = framework->GetUIModule()->GetUI3DModule(); if (_mpNameplateAddon == null || currentAddon == null || currentAddon != _mpNameplateAddon)
return;
var framework = Framework.Instance();
var ui3DModule = framework->GetUIModule()->GetUI3DModule();
if (ui3DModule == null) if (ui3DModule == null)
return; return;
for (int i = 0; i < ui3DModule->NamePlateObjectInfoCount; ++i) var vec = ui3DModule->NamePlateObjectInfoPointers;
if (vec.IsEmpty)
return;
var safeCount = System.Math.Min(
ui3DModule->NamePlateObjectInfoCount,
vec.Length
);
for (int i = 0; i < safeCount; ++i)
{ {
if (ui3DModule->NamePlateObjectInfoPointers.IsEmpty) continue; var config = _configService.Current;
var objectInfoPtr = ui3DModule->NamePlateObjectInfoPointers[i]; var objectInfoPtr = vec[i];
if (objectInfoPtr == null)
if (objectInfoPtr == null) continue; continue;
var objectInfo = objectInfoPtr.Value; var objectInfo = objectInfoPtr.Value;
if (objectInfo == null || objectInfo->GameObject == null) if (objectInfo == null || objectInfo->GameObject == null)
continue; continue;
@@ -223,62 +240,61 @@ public unsafe class NameplateHandler : IMediatorSubscriber
if (nameplateIndex < 0 || nameplateIndex >= AddonNamePlate.NumNamePlateObjects) if (nameplateIndex < 0 || nameplateIndex >= AddonNamePlate.NumNamePlateObjects)
continue; continue;
var pNode = mTextNodes[nameplateIndex]; var pNode = _mTextNodes[nameplateIndex];
if (pNode == null) if (pNode == null)
continue; continue;
if (mpNameplateAddon == null) // CID gating
continue;
var cid = DalamudUtilService.GetHashedCIDFromPlayerPointer((nint)objectInfo->GameObject); var cid = DalamudUtilService.GetHashedCIDFromPlayerPointer((nint)objectInfo->GameObject);
if (cid == null || !_activeBroadcastingCids.Contains(cid)) if (cid == null || !_activeBroadcastingCids.Contains(cid))
{ {
pNode->AtkResNode.ToggleVisibility(false); pNode->AtkResNode.ToggleVisibility(false);
continue; continue;
} }
if (!_configService.Current.LightfinderLabelShowOwn && (objectInfo->GameObject->GetGameObjectId() == _clientState.LocalPlayer.GameObjectId)) var local = _clientState.LocalPlayer;
if (!config.LightfinderLabelShowOwn && local != null &&
objectInfo->GameObject->GetGameObjectId() == local.GameObjectId)
{ {
pNode->AtkResNode.ToggleVisibility(false); pNode->AtkResNode.ToggleVisibility(false);
continue; continue;
} }
if (!_configService.Current.LightfinderLabelShowPaired && VisibleUserIds.Any(u => u == objectInfo->GameObject->GetGameObjectId())) var visibleUserIds = VisibleUserIds;
{ var hidePaired = !config.LightfinderLabelShowPaired;
pNode->AtkResNode.ToggleVisibility(false);
continue; var goId = (ulong)objectInfo->GameObject->GetGameObjectId();
} if (hidePaired && visibleUserIds.Contains(goId))
var nameplateObject = mpNameplateAddon->NamePlateObjectArray[nameplateIndex];
nameplateObject.RootComponentNode->Component->UldManager.UpdateDrawNodeList();
var pNameplateIconNode = nameplateObject.MarkerIcon;
var pNameplateResNode = nameplateObject.NameContainer;
var pNameplateTextNode = nameplateObject.NameText;
bool IsVisible = pNameplateIconNode->AtkResNode.IsVisible() || (pNameplateResNode->IsVisible() && pNameplateTextNode->AtkResNode.IsVisible()) || _configService.Current.LightfinderLabelShowHidden;
pNode->AtkResNode.ToggleVisibility(IsVisible);
if (nameplateObject.RootComponentNode == null ||
nameplateObject.NameContainer == null ||
nameplateObject.NameText == null)
{ {
pNode->AtkResNode.ToggleVisibility(false); pNode->AtkResNode.ToggleVisibility(false);
continue; continue;
} }
var nameplateObject = _mpNameplateAddon->NamePlateObjectArray[nameplateIndex];
var root = nameplateObject.RootComponentNode;
var nameContainer = nameplateObject.NameContainer; var nameContainer = nameplateObject.NameContainer;
var nameText = nameplateObject.NameText; var nameText = nameplateObject.NameText;
var marker = nameplateObject.MarkerIcon;
if (nameContainer == null || nameText == null) if (root == null || nameContainer == null || nameText == null)
{ {
pNode->AtkResNode.ToggleVisibility(false); pNode->AtkResNode.ToggleVisibility(false);
continue; continue;
} }
root->Component->UldManager.UpdateDrawNodeList();
bool isVisible =
((marker != null) && marker->AtkResNode.IsVisible()) ||
(nameContainer->IsVisible() && nameText->AtkResNode.IsVisible()) ||
config.LightfinderLabelShowHidden;
pNode->AtkResNode.ToggleVisibility(isVisible);
if (!isVisible)
continue;
var labelColor = UIColors.Get("Lightfinder"); var labelColor = UIColors.Get("Lightfinder");
var edgeColor = UIColors.Get("LightfinderEdge"); var edgeColor = UIColors.Get("LightfinderEdge");
var config = _configService.Current;
var scaleMultiplier = System.Math.Clamp(config.LightfinderLabelScale, 0.5f, 2.0f); var scaleMultiplier = System.Math.Clamp(config.LightfinderLabelScale, 0.5f, 2.0f);
var baseScale = config.LightfinderLabelUseIcon ? 1.0f : 0.5f; var baseScale = config.LightfinderLabelUseIcon ? 1.0f : 0.5f;
@@ -545,7 +561,7 @@ public unsafe class NameplateHandler : IMediatorSubscriber
} }
private void HideNameplateTextNode(int i) private void HideNameplateTextNode(int i)
{ {
var pNode = mTextNodes[i]; var pNode = _mTextNodes[i];
if (pNode != null) if (pNode != null)
{ {
pNode->AtkResNode.ToggleVisibility(false); pNode->AtkResNode.ToggleVisibility(false);
@@ -555,10 +571,10 @@ public unsafe class NameplateHandler : IMediatorSubscriber
private AddonNamePlate.NamePlateObject? GetNameplateObject(int i) private AddonNamePlate.NamePlateObject? GetNameplateObject(int i)
{ {
if (i < AddonNamePlate.NumNamePlateObjects && if (i < AddonNamePlate.NumNamePlateObjects &&
mpNameplateAddon != null && _mpNameplateAddon != null &&
mpNameplateAddon->NamePlateObjectArray[i].RootComponentNode != null) _mpNameplateAddon->NamePlateObjectArray[i].RootComponentNode != null)
{ {
return mpNameplateAddon->NamePlateObjectArray[i]; return _mpNameplateAddon->NamePlateObjectArray[i];
} }
else else
{ {
@@ -576,6 +592,7 @@ public unsafe class NameplateHandler : IMediatorSubscriber
.Where(u => u.IsVisible && u.PlayerCharacterId != uint.MaxValue) .Where(u => u.IsVisible && u.PlayerCharacterId != uint.MaxValue)
.Select(u => (ulong)u.PlayerCharacterId)]; .Select(u => (ulong)u.PlayerCharacterId)];
public void FlagRefresh() public void FlagRefresh()
{ {
_needsLabelRefresh = true; _needsLabelRefresh = true;
@@ -592,18 +609,13 @@ public unsafe class NameplateHandler : IMediatorSubscriber
public void UpdateBroadcastingCids(IEnumerable<string> cids) public void UpdateBroadcastingCids(IEnumerable<string> cids)
{ {
var newSet = cids.ToHashSet(); var newSet = cids.ToImmutableHashSet(StringComparer.Ordinal);
if (ReferenceEquals(_activeBroadcastingCids, newSet) || _activeBroadcastingCids.SetEquals(newSet))
var changed = !_activeBroadcastingCids.SetEquals(newSet);
if (!changed)
return; return;
_activeBroadcastingCids.Clear(); // single atomic swap <20> readers always see a consistent snapshot
foreach (var cid in newSet) _activeBroadcastingCids = newSet;
_activeBroadcastingCids.Add(cid);
_logger.LogInformation("Active broadcast CIDs: {Cids}", string.Join(",", _activeBroadcastingCids)); _logger.LogInformation("Active broadcast CIDs: {Cids}", string.Join(",", _activeBroadcastingCids));
FlagRefresh(); FlagRefresh();
} }