Fix UI + Amélioration AutoDetect & Self Analyse + Update Penumbra API
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MareSynchronos.WebAPI.AutoDetect;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
@@ -20,6 +23,7 @@ public class AutoDetectRequestService
|
||||
private readonly object _syncRoot = new();
|
||||
private readonly Dictionary<string, DateTime> _activeCooldowns = new(StringComparer.Ordinal);
|
||||
private readonly Dictionary<string, RefusalTracker> _refusalTrackers = new(StringComparer.Ordinal);
|
||||
private readonly ConcurrentDictionary<string, PendingRequestInfo> _pendingRequests = new(StringComparer.Ordinal);
|
||||
private static readonly TimeSpan RequestCooldown = TimeSpan.FromMinutes(5);
|
||||
private static readonly TimeSpan RefusalLockDuration = TimeSpan.FromMinutes(15);
|
||||
|
||||
@@ -118,6 +122,11 @@ public class AutoDetectRequestService
|
||||
}
|
||||
}
|
||||
_mediator.Publish(new NotificationMessage("Nearby request sent", "The other user will receive a request notification.", NotificationType.Info));
|
||||
var pendingKey = EnsureTargetKey(targetKey);
|
||||
var label = !string.IsNullOrWhiteSpace(targetDisplayName)
|
||||
? targetDisplayName!
|
||||
: (!string.IsNullOrEmpty(uid) ? uid : (!string.IsNullOrEmpty(token) ? token : pendingKey));
|
||||
_pendingRequests[pendingKey] = new PendingRequestInfo(pendingKey, uid, token, label, DateTime.UtcNow);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -145,6 +154,7 @@ public class AutoDetectRequestService
|
||||
tracker.LockUntil = now.Add(RefusalLockDuration);
|
||||
}
|
||||
}
|
||||
_pendingRequests.TryRemove(targetKey, out _);
|
||||
}
|
||||
_mediator.Publish(new NotificationMessage("Nearby request failed", "The server rejected the request. Try again soon.", NotificationType.Warning));
|
||||
}
|
||||
@@ -207,4 +217,35 @@ public class AutoDetectRequestService
|
||||
public int Count;
|
||||
public DateTime? LockUntil;
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<PendingRequestInfo> GetPendingRequestsSnapshot()
|
||||
{
|
||||
return _pendingRequests.Values.OrderByDescending(v => v.SentAt).ToList();
|
||||
}
|
||||
|
||||
public void RemovePendingRequestByUid(string uid)
|
||||
{
|
||||
if (string.IsNullOrEmpty(uid)) return;
|
||||
foreach (var kvp in _pendingRequests)
|
||||
{
|
||||
if (string.Equals(kvp.Value.Uid, uid, StringComparison.Ordinal))
|
||||
{
|
||||
_pendingRequests.TryRemove(kvp.Key, out _);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemovePendingRequestByKey(string key)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key)) return;
|
||||
_pendingRequests.TryRemove(key, out _);
|
||||
}
|
||||
|
||||
private static string EnsureTargetKey(string? targetKey)
|
||||
{
|
||||
return !string.IsNullOrEmpty(targetKey) ? targetKey! : "target:" + Guid.NewGuid().ToString("N");
|
||||
}
|
||||
|
||||
public sealed record PendingRequestInfo(string Key, string? Uid, string? Token, string TargetDisplayName, DateTime SentAt);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Lumina.Excel.Sheets;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.MareConfiguration.Models;
|
||||
using MareSynchronos.Services;
|
||||
using MareSynchronos.Services.Mediator;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MareSynchronos.Services.AutoDetect;
|
||||
|
||||
public sealed class AutoDetectSuppressionService : IHostedService, IMediatorSubscriber
|
||||
{
|
||||
private static readonly string[] ContentTypeKeywords =
|
||||
[
|
||||
"dungeon",
|
||||
"donjon",
|
||||
"raid",
|
||||
"trial",
|
||||
"défi",
|
||||
"front",
|
||||
"frontline",
|
||||
"pvp",
|
||||
"jcj",
|
||||
"conflict",
|
||||
"conflit"
|
||||
];
|
||||
|
||||
private readonly ILogger<AutoDetectSuppressionService> _logger;
|
||||
private readonly MareConfigService _configService;
|
||||
private readonly IClientState _clientState;
|
||||
private readonly IDataManager _dataManager;
|
||||
private readonly MareMediator _mediator;
|
||||
private readonly DalamudUtilService _dalamudUtilService;
|
||||
|
||||
private bool _isSuppressed;
|
||||
private bool _hasSavedState;
|
||||
private bool _savedDiscoveryEnabled;
|
||||
private bool _savedAllowRequests;
|
||||
private bool _suppressionWarningShown;
|
||||
public bool IsSuppressed => _isSuppressed;
|
||||
|
||||
public AutoDetectSuppressionService(ILogger<AutoDetectSuppressionService> logger,
|
||||
MareConfigService configService, IClientState clientState,
|
||||
IDataManager dataManager, DalamudUtilService dalamudUtilService, MareMediator mediator)
|
||||
{
|
||||
_logger = logger;
|
||||
_configService = configService;
|
||||
_clientState = clientState;
|
||||
_dataManager = dataManager;
|
||||
_dalamudUtilService = dalamudUtilService;
|
||||
_mediator = mediator;
|
||||
}
|
||||
|
||||
public MareMediator Mediator => _mediator;
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_mediator.Subscribe<ZoneSwitchEndMessage>(this, _ => UpdateSuppressionState());
|
||||
_mediator.Subscribe<DalamudLoginMessage>(this, _ => UpdateSuppressionState());
|
||||
_mediator.Subscribe<DalamudLogoutMessage>(this, _ => ClearSuppression());
|
||||
UpdateSuppressionState();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_mediator.UnsubscribeAll(this);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void UpdateSuppressionState()
|
||||
{
|
||||
_ = _dalamudUtilService.RunOnFrameworkThread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_clientState.IsLoggedIn || _clientState.LocalPlayer == null)
|
||||
{
|
||||
ClearSuppression();
|
||||
return;
|
||||
}
|
||||
|
||||
uint territoryId = _clientState.TerritoryType;
|
||||
bool shouldSuppress = ShouldSuppressForTerritory(territoryId);
|
||||
ApplySuppression(shouldSuppress, territoryId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to update AutoDetect suppression state");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void ApplySuppression(bool shouldSuppress, uint territoryId)
|
||||
{
|
||||
if (shouldSuppress)
|
||||
{
|
||||
if (!_isSuppressed)
|
||||
{
|
||||
_savedDiscoveryEnabled = _configService.Current.EnableAutoDetectDiscovery;
|
||||
_savedAllowRequests = _configService.Current.AllowAutoDetectPairRequests;
|
||||
_hasSavedState = true;
|
||||
_isSuppressed = true;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
if (_configService.Current.EnableAutoDetectDiscovery)
|
||||
{
|
||||
_configService.Current.EnableAutoDetectDiscovery = false;
|
||||
changed = true;
|
||||
}
|
||||
if (_configService.Current.AllowAutoDetectPairRequests)
|
||||
{
|
||||
_configService.Current.AllowAutoDetectPairRequests = false;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
_logger.LogInformation("AutoDetect temporarily disabled in instanced content (territory {territoryId}).", territoryId);
|
||||
if (!_suppressionWarningShown)
|
||||
{
|
||||
_suppressionWarningShown = true;
|
||||
const string warningText = "Zone instanciée détectée : les fonctions AutoDetect/Nearby sont coupées pour économiser de la bande passante.";
|
||||
_mediator.Publish(new DualNotificationMessage("AutoDetect désactivé",
|
||||
warningText,
|
||||
NotificationType.Warning, TimeSpan.FromSeconds(5)));
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_isSuppressed) return;
|
||||
|
||||
bool changed = false;
|
||||
bool wasSuppressed = _suppressionWarningShown;
|
||||
if (_hasSavedState)
|
||||
{
|
||||
if (_configService.Current.EnableAutoDetectDiscovery != _savedDiscoveryEnabled)
|
||||
{
|
||||
_configService.Current.EnableAutoDetectDiscovery = _savedDiscoveryEnabled;
|
||||
changed = true;
|
||||
}
|
||||
if (_configService.Current.AllowAutoDetectPairRequests != _savedAllowRequests)
|
||||
{
|
||||
_configService.Current.AllowAutoDetectPairRequests = _savedAllowRequests;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
_isSuppressed = false;
|
||||
_hasSavedState = false;
|
||||
_suppressionWarningShown = false;
|
||||
|
||||
if (changed || wasSuppressed)
|
||||
{
|
||||
_logger.LogInformation("AutoDetect restored after leaving instanced content (territory {territoryId}).", territoryId);
|
||||
const string restoredText = "Vous avez quitté la zone instanciée : AutoDetect/Nearby fonctionnent de nouveau.";
|
||||
_mediator.Publish(new DualNotificationMessage("AutoDetect réactivé",
|
||||
restoredText,
|
||||
NotificationType.Info, TimeSpan.FromSeconds(5)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearSuppression()
|
||||
{
|
||||
if (!_isSuppressed) return;
|
||||
_isSuppressed = false;
|
||||
if (_hasSavedState)
|
||||
{
|
||||
_configService.Current.EnableAutoDetectDiscovery = _savedDiscoveryEnabled;
|
||||
_configService.Current.AllowAutoDetectPairRequests = _savedAllowRequests;
|
||||
}
|
||||
_hasSavedState = false;
|
||||
_suppressionWarningShown = false;
|
||||
}
|
||||
|
||||
private bool ShouldSuppressForTerritory(uint territoryId)
|
||||
{
|
||||
if (territoryId == 0) return false;
|
||||
|
||||
var cfcSheet = _dataManager.GetExcelSheet<ContentFinderCondition>();
|
||||
if (cfcSheet == null) return false;
|
||||
|
||||
var cfc = cfcSheet.FirstOrDefault(c => c.TerritoryType.RowId == territoryId);
|
||||
if (cfc.RowId == 0) return false;
|
||||
|
||||
if (MatchesSuppressionKeyword(cfc.Name.ToString())) return true;
|
||||
|
||||
var contentType = cfc.ContentType.Value;
|
||||
if (contentType.RowId == 0) return false;
|
||||
|
||||
return MatchesSuppressionKeyword(contentType.Name.ToString());
|
||||
}
|
||||
|
||||
private static bool MatchesSuppressionKeyword(string? text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text)) return false;
|
||||
return ContentTypeKeywords.Any(keyword => text.Contains(keyword, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,7 @@ public sealed class NearbyPendingService : IMediatorSubscriber
|
||||
{
|
||||
_ = _api.UserAddPair(new MareSynchronos.API.Dto.User.UserDto(new MareSynchronos.API.Data.UserData(uidA)));
|
||||
_pending.TryRemove(uidA, out _);
|
||||
_requestService.RemovePendingRequestByUid(uidA);
|
||||
_logger.LogInformation("NearbyPending: auto-accepted pairing with {uid}", uidA);
|
||||
}
|
||||
return;
|
||||
@@ -67,6 +68,7 @@ public sealed class NearbyPendingService : IMediatorSubscriber
|
||||
public void Remove(string uid)
|
||||
{
|
||||
_pending.TryRemove(uid, out _);
|
||||
_requestService.RemovePendingRequestByUid(uid);
|
||||
}
|
||||
|
||||
public async Task<bool> AcceptAsync(string uid)
|
||||
@@ -75,6 +77,7 @@ public sealed class NearbyPendingService : IMediatorSubscriber
|
||||
{
|
||||
await _api.UserAddPair(new MareSynchronos.API.Dto.User.UserDto(new MareSynchronos.API.Data.UserData(uid))).ConfigureAwait(false);
|
||||
_pending.TryRemove(uid, out _);
|
||||
_requestService.RemovePendingRequestByUid(uid);
|
||||
_ = _requestService.SendAcceptNotifyAsync(uid);
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user