Compare commits

2 Commits

11 changed files with 193 additions and 13 deletions

BIN
.DS_Store vendored

Binary file not shown.

3
.gitignore vendored
View File

@@ -10,6 +10,9 @@
*.userosscache *.userosscache
*.sln.docstates *.sln.docstates
.DS_Store .DS_Store
MareSynchronos/.DS_Store
*.zip
UmbraServer_extracted/
# User-specific files (MonoDevelop/Xamarin Studio) # User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs *.userprefs

Binary file not shown.

View File

@@ -59,6 +59,10 @@ public class MareConfig : IMareConfiguration
public bool ShowUploading { get; set; } = true; public bool ShowUploading { get; set; } = true;
public bool ShowUploadingBigText { get; set; } = true; public bool ShowUploadingBigText { get; set; } = true;
public bool ShowVisibleUsersSeparately { get; set; } = true; public bool ShowVisibleUsersSeparately { get; set; } = true;
public bool EnableAutoDetectDiscovery { get; set; } = false;
public bool AllowAutoDetectPairRequests { get; set; } = false;
public int AutoDetectMaxDistanceMeters { get; set; } = 40;
public int AutoDetectMuteMinutes { get; set; } = 5;
public int TimeSpanBetweenScansInSeconds { get; set; } = 30; public int TimeSpanBetweenScansInSeconds { get; set; } = 30;
public int TransferBarsHeight { get; set; } = 12; public int TransferBarsHeight { get; set; } = 12;
public bool TransferBarsShowText { get; set; } = true; public bool TransferBarsShowText { get; set; } = true;

View File

@@ -175,6 +175,7 @@ public sealed class Plugin : IDalamudPlugin
collection.AddScoped<WindowMediatorSubscriberBase, CompactUi>(); collection.AddScoped<WindowMediatorSubscriberBase, CompactUi>();
collection.AddScoped<WindowMediatorSubscriberBase, IntroUi>(); collection.AddScoped<WindowMediatorSubscriberBase, IntroUi>();
collection.AddScoped<WindowMediatorSubscriberBase, DownloadUi>(); collection.AddScoped<WindowMediatorSubscriberBase, DownloadUi>();
collection.AddScoped<WindowMediatorSubscriberBase, AutoDetectUi>();
collection.AddScoped<WindowMediatorSubscriberBase, PopoutProfileUi>(); collection.AddScoped<WindowMediatorSubscriberBase, PopoutProfileUi>();
collection.AddScoped<WindowMediatorSubscriberBase, DataAnalysisUi>(); collection.AddScoped<WindowMediatorSubscriberBase, DataAnalysisUi>();
collection.AddScoped<WindowMediatorSubscriberBase, EventViewerUI>(); collection.AddScoped<WindowMediatorSubscriberBase, EventViewerUI>();

View File

@@ -0,0 +1,106 @@
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.Services.Mediator;
using Microsoft.Extensions.Logging;
using System.Numerics;
namespace MareSynchronos.UI;
public class AutoDetectUi : WindowMediatorSubscriberBase
{
private readonly MareConfigService _configService;
private readonly DalamudUtilService _dalamud;
private readonly IObjectTable _objectTable;
public AutoDetectUi(ILogger<AutoDetectUi> logger, MareMediator mediator,
MareConfigService configService, DalamudUtilService dalamudUtilService, IObjectTable objectTable,
PerformanceCollectorService performanceCollectorService)
: base(logger, mediator, "Umbra Nearby", performanceCollectorService)
{
_configService = configService;
_dalamud = dalamudUtilService;
_objectTable = objectTable;
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 _ = ImRaii.PushId("autosync-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("autosync-nearby", 3, ImGuiTableFlags.SizingStretchProp))
{
ImGui.TableSetupColumn("Name");
ImGui.TableSetupColumn("World");
ImGui.TableSetupColumn("Distance");
ImGui.TableHeadersRow();
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;
}
string world = worldId == 0 ? "-" : (_dalamud.WorldData.Value.TryGetValue(worldId, out var w) ? w : worldId.ToString());
ImGui.TableNextColumn();
ImGui.TextUnformatted(name);
ImGui.TableNextColumn();
ImGui.TextUnformatted(world);
ImGui.TableNextColumn();
ImGui.TextUnformatted(float.IsNaN(dist) ? "-" : $"{dist:0.0} m");
}
ImGui.EndTable();
}
}
}

View File

@@ -56,11 +56,12 @@ public class CompactUi : WindowMediatorSubscriberBase
private bool _showModalForUserAddition; private bool _showModalForUserAddition;
private bool _showSyncShells; private bool _showSyncShells;
private bool _wasOpen; private bool _wasOpen;
private bool _nearbyOpen = true;
public CompactUi(ILogger<CompactUi> logger, UiSharedService uiShared, MareConfigService configService, ApiController apiController, PairManager pairManager, ChatService chatService, public CompactUi(ILogger<CompactUi> logger, UiSharedService uiShared, MareConfigService configService, ApiController apiController, PairManager pairManager, ChatService chatService,
ServerConfigurationManager serverManager, MareMediator mediator, FileUploadManager fileTransferManager, UidDisplayHandler uidDisplayHandler, CharaDataManager charaDataManager, ServerConfigurationManager serverManager, MareMediator mediator, FileUploadManager fileTransferManager, UidDisplayHandler uidDisplayHandler, CharaDataManager charaDataManager,
PerformanceCollectorService performanceCollectorService) PerformanceCollectorService performanceCollectorService)
: base(logger, mediator, "###UmbraSyncSyncMainUI", performanceCollectorService) : base(logger, mediator, "###UmbraSyncMainUI", performanceCollectorService)
{ {
_uiSharedService = uiShared; _uiSharedService = uiShared;
_configService = configService; _configService = configService;
@@ -80,11 +81,11 @@ public class CompactUi : WindowMediatorSubscriberBase
#if DEBUG #if DEBUG
string dev = "Dev Build"; string dev = "Dev Build";
var ver = Assembly.GetExecutingAssembly().GetName().Version!; var ver = Assembly.GetExecutingAssembly().GetName().Version!;
WindowName = $"UmbraSync {dev} ({ver.Major}.{ver.Minor}.{ver.Build})###UmbraSyncSyncMainUIDev"; WindowName = $"UmbraSync {dev} ({ver.Major}.{ver.Minor}.{ver.Build})###UmbraSyncMainUIDev";
Toggle(); Toggle();
#else #else
var ver = Assembly.GetExecutingAssembly().GetName().Version!; var ver = Assembly.GetExecutingAssembly().GetName().Version!;
WindowName = "UmbraSync " + ver.Major + "." + ver.Minor + "." + ver.Build + "###UmbraSyncSyncMainUI"; WindowName = "UmbraSync " + ver.Major + "." + ver.Minor + "." + ver.Build + "###UmbracSyncMainUI";
#endif #endif
Mediator.Subscribe<SwitchToMainUiMessage>(this, (_) => IsOpen = true); Mediator.Subscribe<SwitchToMainUiMessage>(this, (_) => IsOpen = true);
Mediator.Subscribe<SwitchToIntroUiMessage>(this, (_) => IsOpen = false); Mediator.Subscribe<SwitchToIntroUiMessage>(this, (_) => IsOpen = false);
@@ -104,7 +105,7 @@ public class CompactUi : WindowMediatorSubscriberBase
protected override void DrawInternal() protected override void DrawInternal()
{ {
UiSharedService.AccentColor = new Vector4(0.2f, 0.6f, 1f, 1f); // custom blue UiSharedService.AccentColor = new Vector4(0.63f, 0.25f, 1f, 1f);
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ImGui.GetStyle().WindowPadding.Y - 1f * ImGuiHelpers.GlobalScale + ImGui.GetStyle().ItemSpacing.Y); ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ImGui.GetStyle().WindowPadding.Y - 1f * ImGuiHelpers.GlobalScale + ImGui.GetStyle().ItemSpacing.Y);
WindowContentWidth = UiSharedService.GetWindowContentRegionWidth(); WindowContentWidth = UiSharedService.GetWindowContentRegionWidth();
if (!_apiController.IsCurrentVersion) if (!_apiController.IsCurrentVersion)
@@ -176,6 +177,14 @@ public class CompactUi : WindowMediatorSubscriberBase
} }
ImGui.Separator(); ImGui.Separator();
using (ImRaii.PushId("transfers")) DrawTransfers(); using (ImRaii.PushId("transfers")) DrawTransfers();
using (ImRaii.PushId("autosync"))
{
ImGui.SameLine();
if (_uiSharedService.IconTextButton(FontAwesomeIcon.UserPlus, "Nearby", (WindowContentWidth - ImGui.GetStyle().ItemSpacing.X) / 2))
{
Mediator.Publish(new UiToggleMessage(typeof(AutoDetectUi)));
}
}
TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight; TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight;
using (ImRaii.PushId("group-user-popup")) _selectPairsForGroupUi.Draw(_pairManager.DirectPairs); using (ImRaii.PushId("group-user-popup")) _selectPairsForGroupUi.Draw(_pairManager.DirectPairs);
using (ImRaii.PushId("grouping-popup")) _selectGroupForPairUi.Draw(); using (ImRaii.PushId("grouping-popup")) _selectGroupForPairUi.Draw();
@@ -367,6 +376,29 @@ public class CompactUi : WindowMediatorSubscriberBase
_pairGroupsUi.Draw(visibleUsers, onlineUsers, offlineUsers); _pairGroupsUi.Draw(visibleUsers, onlineUsers, offlineUsers);
// Always show a Nearby group when detection is enabled, even if empty
if (_configService.Current.EnableAutoDetectDiscovery)
{
ImGui.Separator();
using (ImRaii.PushId("group-Nearby"))
{
var icon = _nearbyOpen ? FontAwesomeIcon.CaretSquareDown : FontAwesomeIcon.CaretSquareRight;
_uiSharedService.IconText(icon);
if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) _nearbyOpen = !_nearbyOpen;
ImGui.SameLine();
ImGui.TextUnformatted("Nearby (0 Players)");
if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) _nearbyOpen = !_nearbyOpen;
if (_nearbyOpen)
{
ImGui.Indent();
UiSharedService.ColorTextWrapped("No nearby players detected.", ImGuiColors.DalamudGrey3);
ImGui.Unindent();
ImGui.Separator();
}
}
}
ImGui.EndChild(); ImGui.EndChild();
} }
@@ -384,7 +416,7 @@ public class CompactUi : WindowMediatorSubscriberBase
{ {
ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth()) / 2 - (userSize.X + textSize.X) / 2 - ImGui.GetStyle().ItemSpacing.X / 2); ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth()) / 2 - (userSize.X + textSize.X) / 2 - ImGui.GetStyle().ItemSpacing.X / 2);
if (!printShard) ImGui.AlignTextToFramePadding(); if (!printShard) ImGui.AlignTextToFramePadding();
ImGui.TextColored(ImGuiColors.ParsedBlue, userCount); ImGui.TextColored(UiSharedService.AccentColor, userCount);
ImGui.SameLine(); ImGui.SameLine();
if (!printShard) ImGui.AlignTextToFramePadding(); if (!printShard) ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Users Online"); ImGui.TextUnformatted("Users Online");
@@ -592,7 +624,7 @@ public class CompactUi : WindowMediatorSubscriberBase
{ {
ServerState.Connecting => ImGuiColors.DalamudYellow, ServerState.Connecting => ImGuiColors.DalamudYellow,
ServerState.Reconnecting => ImGuiColors.DalamudRed, ServerState.Reconnecting => ImGuiColors.DalamudRed,
ServerState.Connected => new Vector4(0.2f, 0.6f, 1f, 1f), // custom blue ServerState.Connected => new Vector4(0.63f, 0.25f, 1f, 1f), // custom violet
ServerState.Disconnected => ImGuiColors.DalamudYellow, ServerState.Disconnected => ImGuiColors.DalamudYellow,
ServerState.Disconnecting => ImGuiColors.DalamudYellow, ServerState.Disconnecting => ImGuiColors.DalamudYellow,
ServerState.Unauthorized => ImGuiColors.DalamudRed, ServerState.Unauthorized => ImGuiColors.DalamudRed,

View File

@@ -163,13 +163,13 @@ public class DownloadUi : WindowMediatorSubscriberBase
UiSharedService.Color(0, 0, 0, transparency), 1); UiSharedService.Color(0, 0, 0, transparency), 1);
drawList.AddRectFilled(dlBarStart with { X = dlBarStart.X - dlBarBorder, Y = dlBarStart.Y - dlBarBorder }, drawList.AddRectFilled(dlBarStart with { X = dlBarStart.X - dlBarBorder, Y = dlBarStart.Y - dlBarBorder },
dlBarEnd with { X = dlBarEnd.X + dlBarBorder, Y = dlBarEnd.Y + dlBarBorder }, dlBarEnd with { X = dlBarEnd.X + dlBarBorder, Y = dlBarEnd.Y + dlBarBorder },
UiSharedService.Color(220, 220, 255, transparency), 1); UiSharedService.Color(230, 200, 255, transparency), 1);
drawList.AddRectFilled(dlBarStart, dlBarEnd, drawList.AddRectFilled(dlBarStart, dlBarEnd,
UiSharedService.Color(0, 0, 0, transparency), 1); UiSharedService.Color(0, 0, 0, transparency), 1);
var dlProgressPercent = transferredBytes / (double)totalBytes; var dlProgressPercent = transferredBytes / (double)totalBytes;
drawList.AddRectFilled(dlBarStart, drawList.AddRectFilled(dlBarStart,
dlBarEnd with { X = dlBarStart.X + (float)(dlProgressPercent * dlBarWidth) }, dlBarEnd with { X = dlBarStart.X + (float)(dlProgressPercent * dlBarWidth) },
UiSharedService.Color(100, 100, 255, transparency), 1); UiSharedService.Color(160, 64, 255, transparency), 1);
if (_configService.Current.TransferBarsShowText) if (_configService.Current.TransferBarsShowText)
{ {

View File

@@ -211,6 +211,40 @@ public class SettingsUi : WindowMediatorSubscriberBase
_configService.Save(); _configService.Save();
} }
ImGui.Separator();
_uiShared.BigText("Nearby");
bool enableDiscovery = _configService.Current.EnableAutoDetectDiscovery;
if (ImGui.Checkbox("Enable Nearby detection (beta)", ref enableDiscovery))
{
_configService.Current.EnableAutoDetectDiscovery = enableDiscovery;
_configService.Save();
}
bool allowRequests = _configService.Current.AllowAutoDetectPairRequests;
if (ImGui.Checkbox("Allow pair requests", ref allowRequests))
{
_configService.Current.AllowAutoDetectPairRequests = allowRequests;
_configService.Save();
}
if (enableDiscovery)
{
ImGui.Indent();
int maxMeters = _configService.Current.AutoDetectMaxDistanceMeters;
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
if (ImGui.SliderInt("Max distance (meters)", ref maxMeters, 5, 100))
{
_configService.Current.AutoDetectMaxDistanceMeters = maxMeters;
_configService.Save();
}
int muteMin = _configService.Current.AutoDetectMuteMinutes;
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
if (ImGui.SliderInt("Default mute duration (minutes)", ref muteMin, 1, 120))
{
_configService.Current.AutoDetectMuteMinutes = muteMin;
_configService.Save();
}
ImGui.Unindent();
}
ImGui.Separator(); ImGui.Separator();
_uiShared.BigText("Transfer UI"); _uiShared.BigText("Transfer UI");

View File

@@ -49,7 +49,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
public List<FileTransfer> ForbiddenTransfers => _orchestrator.ForbiddenTransfers; public List<FileTransfer> ForbiddenTransfers => _orchestrator.ForbiddenTransfers;
public bool IsDownloading => !CurrentDownloads.Any(); public bool IsDownloading => CurrentDownloads.Any();
public void ClearDownload() public void ClearDownload()
{ {
@@ -507,4 +507,4 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
_orchestrator.ClearDownloadRequest(requestId); _orchestrator.ClearDownloadRequest(requestId);
} }
} }
} }

View File

@@ -222,7 +222,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
#endif #endif
} }
await LoadIninitialPairs().ConfigureAwait(false); await LoadInitialPairs().ConfigureAwait(false);
await LoadOnlinePairs().ConfigureAwait(false); await LoadOnlinePairs().ConfigureAwait(false);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
@@ -375,7 +375,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
_initialized = true; _initialized = true;
} }
private async Task LoadIninitialPairs() private async Task LoadInitialPairs()
{ {
foreach (var userPair in await UserGetPairedClients().ConfigureAwait(false)) foreach (var userPair in await UserGetPairedClients().ConfigureAwait(false))
{ {
@@ -435,7 +435,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
return; return;
} }
ServerState = ServerState.Connected; ServerState = ServerState.Connected;
await LoadIninitialPairs().ConfigureAwait(false); await LoadInitialPairs().ConfigureAwait(false);
await LoadOnlinePairs().ConfigureAwait(false); await LoadOnlinePairs().ConfigureAwait(false);
Mediator.Publish(new ConnectedMessage(_connectionDto)); Mediator.Publish(new ConnectedMessage(_connectionDto));
} }