diff --git a/LightlessSync/Services/BroadcastScanningService.cs b/LightlessSync/Services/BroadcastScanningService.cs index 79fe984..95abdae 100644 --- a/LightlessSync/Services/BroadcastScanningService.cs +++ b/LightlessSync/Services/BroadcastScanningService.cs @@ -28,7 +28,7 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos private readonly CancellationTokenSource _cleanupCts = new(); private Task? _cleanupTask; - private int _checkEveryFrames = 20; + private readonly int _checkEveryFrames = 20; private int _frameCounter = 0; private int _lookupsThisFrame = 0; private const int MaxLookupsPerFrame = 30; @@ -221,6 +221,16 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos (excludeHashedCid is null || !comparer.Equals(entry.Key, excludeHashedCid))); } + public List> GetActiveBroadcasts(string? excludeHashedCid = null) + { + var now = DateTime.UtcNow; + var comparer = StringComparer.Ordinal; + return [.. _broadcastCache.Where(entry => + entry.Value.IsBroadcasting && + entry.Value.ExpiryTime > now && + (excludeHashedCid is null || !comparer.Equals(entry.Key, excludeHashedCid)))]; + } + protected override void Dispose(bool disposing) { base.Dispose(disposing); diff --git a/LightlessSync/Services/CommandManagerService.cs b/LightlessSync/Services/CommandManagerService.cs index 7aedc7b..88f8780 100644 --- a/LightlessSync/Services/CommandManagerService.cs +++ b/LightlessSync/Services/CommandManagerService.cs @@ -13,7 +13,8 @@ namespace LightlessSync.Services; public sealed class CommandManagerService : IDisposable { - private const string _commandName = "/light"; + private const string _longName = "/lightless"; + private const string _shortName = "/light"; private readonly ApiController _apiController; private readonly ICommandManager _commandManager; @@ -34,7 +35,11 @@ public sealed class CommandManagerService : IDisposable _apiController = apiController; _mediator = mediator; _lightlessConfigService = lightlessConfigService; - _commandManager.AddHandler(_commandName, new CommandInfo(OnCommand) + _commandManager.AddHandler(_longName, new CommandInfo(OnCommand) + { + HelpMessage = $"\u2191;" + }); + _commandManager.AddHandler(_shortName, new CommandInfo(OnCommand) { HelpMessage = "Opens the Lightless Sync UI" + Environment.NewLine + Environment.NewLine + "Additionally possible commands:" + Environment.NewLine + @@ -49,7 +54,8 @@ public sealed class CommandManagerService : IDisposable public void Dispose() { - _commandManager.RemoveHandler(_commandName); + _commandManager.RemoveHandler(_longName); + _commandManager.RemoveHandler(_shortName); } private void OnCommand(string command, string args) diff --git a/LightlessSync/Services/PairRequestService.cs b/LightlessSync/Services/PairRequestService.cs index 7190825..2531a3a 100644 --- a/LightlessSync/Services/PairRequestService.cs +++ b/LightlessSync/Services/PairRequestService.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using LightlessSync.LightlessConfiguration.Models; using LightlessSync.PlayerData.Pairs; using LightlessSync.Services.Mediator; @@ -14,10 +10,10 @@ public sealed class PairRequestService : DisposableMediatorSubscriberBase private readonly DalamudUtilService _dalamudUtil; private readonly PairManager _pairManager; private readonly Lazy _apiController; - private readonly object _syncRoot = new(); + private readonly Lock _syncRoot = new(); private readonly List _requests = []; - private static readonly TimeSpan Expiration = TimeSpan.FromMinutes(5); + private static readonly TimeSpan _expiration = TimeSpan.FromMinutes(5); public PairRequestService( ILogger logger, @@ -189,7 +185,7 @@ public sealed class PairRequestService : DisposableMediatorSubscriberBase } var now = DateTime.UtcNow; - return _requests.RemoveAll(r => now - r.ReceivedAt > Expiration) > 0; + return _requests.RemoveAll(r => now - r.ReceivedAt > _expiration) > 0; } public void AcceptPairRequest(string hashedCid, string displayName) diff --git a/LightlessSync/UI/DtrEntry.cs b/LightlessSync/UI/DtrEntry.cs index 89c7389..17bc871 100644 --- a/LightlessSync/UI/DtrEntry.cs +++ b/LightlessSync/UI/DtrEntry.cs @@ -2,19 +2,22 @@ using Dalamud.Game.Gui.Dtr; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Plugin.Services; +using Dalamud.Utility; using LightlessSync.LightlessConfiguration; using LightlessSync.LightlessConfiguration.Configurations; using LightlessSync.PlayerData.Pairs; using LightlessSync.Services; using LightlessSync.Services.Mediator; using LightlessSync.Services.ServerConfiguration; +using LightlessSync.Utils; using LightlessSync.WebAPI; using LightlessSync.WebAPI.SignalR.Utils; -using LightlessSync.Utils; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Primitives; using System.Runtime.InteropServices; using System.Text; +using static LightlessSync.Services.PairRequestService; namespace LightlessSync.UI; @@ -106,7 +109,7 @@ public sealed class DtrEntry : IDisposable, IHostedService } catch (OperationCanceledException) { - + _logger.LogInformation("Lightfinder operation was canceled."); } finally { @@ -363,29 +366,46 @@ public sealed class DtrEntry : IDisposable, IHostedService } } - private int GetNearbyBroadcastCount() - { - var localHashedCid = GetLocalHashedCid(); - return _broadcastScannerService.CountActiveBroadcasts( - string.IsNullOrEmpty(localHashedCid) ? null : localHashedCid); - } - - private int GetPendingPairRequestCount() + private List GetNearbyBroadcasts() { try { - return _pairRequestService.GetActiveRequests().Count; + var localHashedCid = GetLocalHashedCid(); + return [.. _broadcastScannerService + .GetActiveBroadcasts(string.IsNullOrEmpty(localHashedCid) ? null : localHashedCid) + .Select(b => _dalamudUtilService.FindPlayerByNameHash(b.Key).Name)]; } catch (Exception ex) { var now = DateTime.UtcNow; + + if (now >= _pairRequestNextErrorLog) + { + _logger.LogDebug(ex, "Failed to retrieve nearby broadcasts for Lightfinder DTR entry."); + _pairRequestNextErrorLog = now + _localHashedCidErrorCooldown; + } + + return []; + } + } + + private IReadOnlyList GetPendingPairRequest() + { + try + { + return _pairRequestService.GetActiveRequests(); + } + 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; + return []; } } @@ -400,23 +420,15 @@ public sealed class DtrEntry : IDisposable, IHostedService if (_broadcastService.IsBroadcasting) { - 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()); + return FormatTooltip("Pending pair requests", GetPendingPairRequest().Select(x => x.DisplayName), icon, SwapColorChannels(config.DtrColorsLightfinderEnabled)); } default: { - var broadcastCount = GetNearbyBroadcastCount(); - tooltipBuilder.AppendLine(); - tooltipBuilder.Append("Nearby Lightfinder users: ").Append(broadcastCount); - return ($"{icon} {broadcastCount}", SwapColorChannels(config.DtrColorsLightfinderEnabled), tooltipBuilder.ToString()); + return FormatTooltip("Nearby Lightfinder users", GetNearbyBroadcasts(), icon, SwapColorChannels(config.DtrColorsLightfinderEnabled)); } } } @@ -433,6 +445,18 @@ public sealed class DtrEntry : IDisposable, IHostedService return ($"{icon} OFF", colors, tooltip.ToString()); } + private (string, Colors, string) FormatTooltip(string title, IEnumerable names, string icon, Colors color) + { + var list = names.Where(x => !string.IsNullOrEmpty(x)).ToList(); + var tooltip = new StringBuilder() + .Append($"Lightfinder - Enabled{Environment.NewLine}") + .Append($"{title}: {list.Count}{Environment.NewLine}") + .AppendJoin(Environment.NewLine, list) + .ToString(); + + return ($"{icon} {list.Count}", color, tooltip); + } + private static string BuildLightfinderTooltip(string baseTooltip) { var builder = new StringBuilder();