202 lines
7.6 KiB
C#
202 lines
7.6 KiB
C#
using Dalamud.Bindings.ImGui;
|
|
using Dalamud.Interface.Colors;
|
|
using Dalamud.Game.ClientState.Objects.SubKinds;
|
|
using Dalamud.Interface.Utility;
|
|
using Dalamud.Interface.Utility.Raii;
|
|
using Dalamud.Plugin.Services;
|
|
using MareSynchronos.MareConfiguration;
|
|
using MareSynchronos.Services;
|
|
using MareSynchronos.PlayerData.Pairs;
|
|
using MareSynchronos.Services.Mediator;
|
|
using Microsoft.Extensions.Logging;
|
|
using System.Numerics;
|
|
using System.Globalization;
|
|
using System.Text;
|
|
|
|
namespace MareSynchronos.UI;
|
|
|
|
public class AutoDetectUi : WindowMediatorSubscriberBase
|
|
{
|
|
private readonly MareConfigService _configService;
|
|
private readonly DalamudUtilService _dalamud;
|
|
private readonly IObjectTable _objectTable;
|
|
private readonly Services.AutoDetect.AutoDetectRequestService _requestService;
|
|
private readonly PairManager _pairManager;
|
|
private List<Services.Mediator.NearbyEntry> _entries = new();
|
|
|
|
public AutoDetectUi(ILogger<AutoDetectUi> logger, MareMediator mediator,
|
|
MareConfigService configService, DalamudUtilService dalamudUtilService, IObjectTable objectTable,
|
|
Services.AutoDetect.AutoDetectRequestService requestService, PairManager pairManager,
|
|
PerformanceCollectorService performanceCollectorService)
|
|
: base(logger, mediator, "AutoDetect", performanceCollectorService)
|
|
{
|
|
_configService = configService;
|
|
_dalamud = dalamudUtilService;
|
|
_objectTable = objectTable;
|
|
_requestService = requestService;
|
|
_pairManager = pairManager;
|
|
|
|
Flags |= ImGuiWindowFlags.NoScrollbar;
|
|
SizeConstraints = new WindowSizeConstraints()
|
|
{
|
|
MinimumSize = new Vector2(350, 220),
|
|
MaximumSize = new Vector2(600, 600),
|
|
};
|
|
}
|
|
|
|
public override bool DrawConditions()
|
|
{
|
|
// Visible when explicitly opened; allow drawing even if discovery is disabled to show hint
|
|
return true;
|
|
}
|
|
|
|
protected override void DrawInternal()
|
|
{
|
|
using var idScope = ImRaii.PushId("autodetect-ui");
|
|
|
|
if (!_configService.Current.EnableAutoDetectDiscovery)
|
|
{
|
|
UiSharedService.ColorTextWrapped("Nearby detection is disabled. Enable it in Settings to start detecting nearby Umbra users.", ImGuiColors.DalamudYellow);
|
|
ImGuiHelpers.ScaledDummy(6);
|
|
}
|
|
|
|
int maxDist = Math.Clamp(_configService.Current.AutoDetectMaxDistanceMeters, 5, 100);
|
|
ImGui.AlignTextToFramePadding();
|
|
ImGui.TextUnformatted("Max distance (m)");
|
|
ImGui.SameLine();
|
|
ImGui.SetNextItemWidth(120 * ImGuiHelpers.GlobalScale);
|
|
if (ImGui.SliderInt("##autodetect-dist", ref maxDist, 5, 100))
|
|
{
|
|
_configService.Current.AutoDetectMaxDistanceMeters = maxDist;
|
|
_configService.Save();
|
|
}
|
|
|
|
ImGuiHelpers.ScaledDummy(6);
|
|
|
|
// Table header
|
|
if (ImGui.BeginTable("autodetect-nearby", 5, ImGuiTableFlags.SizingStretchProp))
|
|
{
|
|
ImGui.TableSetupColumn("Name");
|
|
ImGui.TableSetupColumn("World");
|
|
ImGui.TableSetupColumn("Distance");
|
|
ImGui.TableSetupColumn("Status");
|
|
ImGui.TableSetupColumn("Action");
|
|
ImGui.TableHeadersRow();
|
|
|
|
var data = _entries.Count > 0 ? _entries.Where(e => e.IsMatch).ToList() : new List<Services.Mediator.NearbyEntry>();
|
|
foreach (var e in data)
|
|
{
|
|
ImGui.TableNextColumn();
|
|
ImGui.TextUnformatted(e.Name);
|
|
ImGui.TableNextColumn();
|
|
ImGui.TextUnformatted(e.WorldId == 0 ? "-" : (_dalamud.WorldData.Value.TryGetValue(e.WorldId, out var w) ? w : e.WorldId.ToString()));
|
|
ImGui.TableNextColumn();
|
|
ImGui.TextUnformatted(float.IsNaN(e.Distance) ? "-" : $"{e.Distance:0.0} m");
|
|
ImGui.TableNextColumn();
|
|
bool alreadyPaired = IsAlreadyPairedByUidOrAlias(e);
|
|
string status = alreadyPaired ? "Paired" : (string.IsNullOrEmpty(e.Token) ? "Requests disabled" : "On Umbra");
|
|
ImGui.TextUnformatted(status);
|
|
ImGui.TableNextColumn();
|
|
using (ImRaii.Disabled(alreadyPaired || string.IsNullOrEmpty(e.Token)))
|
|
{
|
|
if (alreadyPaired)
|
|
{
|
|
ImGui.Button($"Already sync##{e.Name}");
|
|
}
|
|
else if (string.IsNullOrEmpty(e.Token))
|
|
{
|
|
ImGui.Button($"Requests disabled##{e.Name}");
|
|
}
|
|
else if (ImGui.Button($"Send request##{e.Name}"))
|
|
{
|
|
_ = _requestService.SendRequestAsync(e.Token!);
|
|
}
|
|
}
|
|
}
|
|
|
|
ImGui.EndTable();
|
|
}
|
|
}
|
|
|
|
public override void OnOpen()
|
|
{
|
|
base.OnOpen();
|
|
Mediator.Subscribe<Services.Mediator.DiscoveryListUpdated>(this, OnDiscoveryUpdated);
|
|
}
|
|
|
|
public override void OnClose()
|
|
{
|
|
Mediator.Unsubscribe<Services.Mediator.DiscoveryListUpdated>(this);
|
|
base.OnClose();
|
|
}
|
|
|
|
private void OnDiscoveryUpdated(Services.Mediator.DiscoveryListUpdated msg)
|
|
{
|
|
_entries = msg.Entries;
|
|
}
|
|
|
|
private List<Services.Mediator.NearbyEntry> BuildLocalSnapshot(int maxDist)
|
|
{
|
|
var list = new List<Services.Mediator.NearbyEntry>();
|
|
var local = _dalamud.GetPlayerCharacter();
|
|
var localPos = local?.Position ?? Vector3.Zero;
|
|
for (int i = 0; i < 200; i += 2)
|
|
{
|
|
var obj = _objectTable[i];
|
|
if (obj == null || obj.ObjectKind != Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player) continue;
|
|
if (local != null && obj.Address == local.Address) continue;
|
|
float dist = local == null ? float.NaN : Vector3.Distance(localPos, obj.Position);
|
|
if (!float.IsNaN(dist) && dist > maxDist) continue;
|
|
string name = obj.Name.ToString();
|
|
ushort worldId = 0;
|
|
if (obj is IPlayerCharacter pc) worldId = (ushort)pc.HomeWorld.RowId;
|
|
list.Add(new Services.Mediator.NearbyEntry(name, worldId, dist, false, null, null, null));
|
|
}
|
|
return list;
|
|
}
|
|
|
|
private bool IsAlreadyPairedByUidOrAlias(Services.Mediator.NearbyEntry e)
|
|
{
|
|
try
|
|
{
|
|
// 1) Match by UID when available (authoritative)
|
|
if (!string.IsNullOrEmpty(e.Uid))
|
|
{
|
|
foreach (var p in _pairManager.DirectPairs)
|
|
{
|
|
if (string.Equals(p.UserData.UID, e.Uid, StringComparison.Ordinal))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// 2) Fallback on alias/name (legacy compatibility)
|
|
var key = NormalizeKey(e.DisplayName ?? e.Name);
|
|
if (string.IsNullOrEmpty(key)) return false;
|
|
foreach (var p in _pairManager.DirectPairs)
|
|
{
|
|
if (NormalizeKey(p.UserData.AliasOrUID) == key) return true;
|
|
if (!string.IsNullOrEmpty(p.UserData.Alias) && NormalizeKey(p.UserData.Alias!) == key) return true;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignore
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static string NormalizeKey(string? input)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(input)) return string.Empty;
|
|
var formD = input.Normalize(NormalizationForm.FormD);
|
|
var sb = new StringBuilder(formD.Length);
|
|
foreach (var ch in formD)
|
|
{
|
|
var cat = CharUnicodeInfo.GetUnicodeCategory(ch);
|
|
if (cat != UnicodeCategory.NonSpacingMark)
|
|
sb.Append(char.ToLowerInvariant(ch));
|
|
}
|
|
return sb.ToString();
|
|
}
|
|
}
|