using Dalamud.Plugin.Services; using Dalamud.Game.ClientState.Objects.SubKinds; using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.UI.Info; using Microsoft.Extensions.Logging; namespace LightlessSync.Interop; public unsafe class BlockedCharacterHandler { private sealed record CharaData(ulong AccId, ulong ContentId); private readonly Dictionary _blockedCharacterCache = new(); private readonly ILogger _logger; private readonly IObjectTable _objectTable; public BlockedCharacterHandler(ILogger logger, IGameInteropProvider gameInteropProvider, IObjectTable objectTable) { gameInteropProvider.InitializeFromAttributes(this); _logger = logger; _objectTable = objectTable; } private CharaData? TryGetIdsFromPlayerPointer(nint ptr, ushort objectIndex) { if (ptr == nint.Zero || objectIndex >= 200) return null; var obj = _objectTable[objectIndex]; if (obj is not IPlayerCharacter player || player.Address != ptr) return null; var castChar = (BattleChara*)player.Address; return new(castChar->Character.AccountId, castChar->Character.ContentId); } public bool IsCharacterBlocked(nint ptr, ushort objectIndex, out bool firstTime) { firstTime = false; var combined = TryGetIdsFromPlayerPointer(ptr, objectIndex); if (combined == null) return false; if (_blockedCharacterCache.TryGetValue(combined, out var isBlocked)) return isBlocked; firstTime = true; var blockStatus = InfoProxyBlacklist.Instance()->GetBlockResultType(combined.AccId, combined.ContentId); _logger.LogTrace("CharaPtr {ptr} is BlockStatus: {status}", ptr, blockStatus); if ((int)blockStatus == 0) return false; return _blockedCharacterCache[combined] = blockStatus != InfoProxyBlacklist.BlockResultType.NotBlocked; } }