add info options for server bar, direct settings button in lightfinder window and fix color swaps

This commit is contained in:
azyges
2025-10-11 00:48:19 +09:00
parent f01229a97f
commit 9b04976aa6
9 changed files with 238 additions and 18 deletions

View File

@@ -10,6 +10,7 @@ using LightlessSync.Services.Mediator;
using LightlessSync.Services.ServerConfiguration;
using LightlessSync.WebAPI;
using LightlessSync.WebAPI.SignalR.Utils;
using LightlessSync.Utils;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Runtime.InteropServices;
@@ -19,6 +20,9 @@ namespace LightlessSync.UI;
public sealed class DtrEntry : IDisposable, IHostedService
{
private static readonly TimeSpan _localHashedCidCacheDuration = TimeSpan.FromMinutes(2);
private static readonly TimeSpan _localHashedCidErrorCooldown = TimeSpan.FromMinutes(1);
private readonly ApiController _apiController;
private readonly ServerConfigurationManager _serverManager;
private readonly CancellationTokenSource _cancellationTokenSource = new();
@@ -28,8 +32,11 @@ public sealed class DtrEntry : IDisposable, IHostedService
private readonly Lazy<IDtrBarEntry> _lightfinderEntry;
private readonly ILogger<DtrEntry> _logger;
private readonly BroadcastService _broadcastService;
private readonly BroadcastScannerService _broadcastScannerService;
private readonly LightlessMediator _lightlessMediator;
private readonly PairManager _pairManager;
private readonly PairRequestService _pairRequestService;
private readonly DalamudUtilService _dalamudUtilService;
private Task? _runTask;
private string? _statusText;
private string? _statusTooltip;
@@ -37,6 +44,10 @@ public sealed class DtrEntry : IDisposable, IHostedService
private string? _lightfinderText;
private string? _lightfinderTooltip;
private Colors _lightfinderColors;
private string? _localHashedCid;
private DateTime _localHashedCidFetchedAt = DateTime.MinValue;
private DateTime _localHashedCidNextErrorLog = DateTime.MinValue;
private DateTime _pairRequestNextErrorLog = DateTime.MinValue;
public DtrEntry(
ILogger<DtrEntry> logger,
@@ -44,9 +55,12 @@ public sealed class DtrEntry : IDisposable, IHostedService
ConfigurationServiceBase<LightlessConfig> configService,
LightlessMediator lightlessMediator,
PairManager pairManager,
PairRequestService pairRequestService,
ApiController apiController,
ServerConfigurationManager serverManager,
BroadcastService broadcastService)
BroadcastService broadcastService,
BroadcastScannerService broadcastScannerService,
DalamudUtilService dalamudUtilService)
{
_logger = logger;
_dtrBar = dtrBar;
@@ -55,9 +69,12 @@ public sealed class DtrEntry : IDisposable, IHostedService
_configService = configService;
_lightlessMediator = lightlessMediator;
_pairManager = pairManager;
_pairRequestService = pairRequestService;
_apiController = apiController;
_serverManager = serverManager;
_broadcastService = broadcastService;
_broadcastScannerService = broadcastScannerService;
_dalamudUtilService = dalamudUtilService;
}
public void Dispose()
@@ -318,27 +335,99 @@ public sealed class DtrEntry : IDisposable, IHostedService
}
}
private string? GetLocalHashedCid()
{
var now = DateTime.UtcNow;
if (_localHashedCid is not null && now - _localHashedCidFetchedAt < _localHashedCidCacheDuration)
return _localHashedCid;
try
{
var cid = _dalamudUtilService.GetCIDAsync().GetAwaiter().GetResult();
var hashedCid = cid.ToString().GetHash256();
_localHashedCid = hashedCid;
_localHashedCidFetchedAt = now;
return hashedCid;
}
catch (Exception ex)
{
if (now >= _localHashedCidNextErrorLog)
{
_logger.LogDebug(ex, "Failed to refresh local hashed CID for Lightfinder DTR entry.");
_localHashedCidNextErrorLog = now + _localHashedCidErrorCooldown;
}
_localHashedCid = null;
_localHashedCidFetchedAt = now;
return null;
}
}
private int GetNearbyBroadcastCount()
{
var localHashedCid = GetLocalHashedCid();
return _broadcastScannerService.CountActiveBroadcasts(
string.IsNullOrEmpty(localHashedCid) ? null : localHashedCid);
}
private int GetPendingPairRequestCount()
{
try
{
return _pairRequestService.GetActiveRequests().Count;
}
catch (Exception ex)
{
var now = DateTime.UtcNow;
if (now >= _pairRequestNextErrorLog)
{
_logger.LogDebug(ex, "Failed to retrieve pair request count for Lightfinder DTR entry.");
_pairRequestNextErrorLog = now + _localHashedCidErrorCooldown;
}
return 0;
}
}
private (string Text, Colors Colors, string Tooltip) BuildLightfinderIndicator()
{
var config = _configService.Current;
const string icon = "\uE048";
if (!_broadcastService.IsLightFinderAvailable)
{
return ($"{icon} --", config.DtrColorsLightfinderUnavailable, "Lightfinder - Unavailable on this server.");
return ($"{icon} --", SwapColorChannels(config.DtrColorsLightfinderUnavailable), "Lightfinder - Unavailable on this server.");
}
if (_broadcastService.IsBroadcasting)
{
return ($"{icon} ON", config.DtrColorsLightfinderEnabled, "Lightfinder - Enabled");
var tooltipBuilder = new StringBuilder("Lightfinder - Enabled");
switch (config.LightfinderDtrDisplayMode)
{
case LightfinderDtrDisplayMode.PendingPairRequests:
{
var requestCount = GetPendingPairRequestCount();
tooltipBuilder.AppendLine();
tooltipBuilder.Append("Pending pair requests: ").Append(requestCount);
return ($"{icon} Requests {requestCount}", SwapColorChannels(config.DtrColorsLightfinderEnabled), tooltipBuilder.ToString());
}
default:
{
var broadcastCount = GetNearbyBroadcastCount();
tooltipBuilder.AppendLine();
tooltipBuilder.Append("Nearby Lightfinder users: ").Append(broadcastCount);
return ($"{icon} {broadcastCount}", SwapColorChannels(config.DtrColorsLightfinderEnabled), tooltipBuilder.ToString());
}
}
}
var tooltip = new StringBuilder("Lightfinder - Disabled");
var colors = config.DtrColorsLightfinderDisabled;
var colors = SwapColorChannels(config.DtrColorsLightfinderDisabled);
if (_broadcastService.RemainingCooldown is { } cooldown && cooldown > TimeSpan.Zero)
{
tooltip.AppendLine();
tooltip.Append("Cooldown: ").Append(Math.Ceiling(cooldown.TotalSeconds)).Append("s");
colors = config.DtrColorsLightfinderCooldown;
colors = SwapColorChannels(config.DtrColorsLightfinderCooldown);
}
return ($"{icon} OFF", colors, tooltip.ToString());
@@ -377,6 +466,17 @@ public sealed class DtrEntry : IDisposable, IHostedService
private const byte _colorTypeForeground = 0x13;
private const byte _colorTypeGlow = 0x14;
private static Colors SwapColorChannels(Colors colors)
=> new(SwapColorComponent(colors.Foreground), SwapColorComponent(colors.Glow));
private static uint SwapColorComponent(uint color)
{
if (color == 0)
return 0;
return ((color & 0xFFu) << 16) | (color & 0xFF00u) | ((color >> 16) & 0xFFu);
}
private static SeString BuildColoredSeString(string text, Colors colors)
{
var ssb = new SeStringBuilder();
@@ -385,7 +485,16 @@ public sealed class DtrEntry : IDisposable, IHostedService
}
private static RawPayload BuildColorStartPayload(byte colorType, uint color)
=> new(unchecked([0x02, colorType, 0x05, 0xF6, byte.Max((byte)color, 0x01), byte.Max((byte)(color >> 8), 0x01), byte.Max((byte)(color >> 16), 0x01), 0x03]));
=> new(unchecked([
0x02,
colorType,
0x05,
0xF6,
byte.Max((byte)color, (byte)0x01),
byte.Max((byte)(color >> 8), (byte)0x01),
byte.Max((byte)(color >> 16), (byte)0x01),
0x03
]));
private static RawPayload BuildColorEndPayload(byte colorType)
=> new([0x02, colorType, 0x02, 0xEC, 0x03]);