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
*.sln.docstates
.DS_Store
MareSynchronos/.DS_Store
*.zip
UmbraServer_extracted/
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

Binary file not shown.

View File

@@ -59,6 +59,10 @@ public class MareConfig : IMareConfiguration
public bool ShowUploading { get; set; } = true;
public bool ShowUploadingBigText { 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 TransferBarsHeight { get; set; } = 12;
public bool TransferBarsShowText { get; set; } = true;

View File

@@ -175,6 +175,7 @@ public sealed class Plugin : IDalamudPlugin
collection.AddScoped<WindowMediatorSubscriberBase, CompactUi>();
collection.AddScoped<WindowMediatorSubscriberBase, IntroUi>();
collection.AddScoped<WindowMediatorSubscriberBase, DownloadUi>();
collection.AddScoped<WindowMediatorSubscriberBase, AutoDetectUi>();
collection.AddScoped<WindowMediatorSubscriberBase, PopoutProfileUi>();
collection.AddScoped<WindowMediatorSubscriberBase, DataAnalysisUi>();
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 _showSyncShells;
private bool _wasOpen;
private bool _nearbyOpen = true;
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,
PerformanceCollectorService performanceCollectorService)
: base(logger, mediator, "###UmbraSyncSyncMainUI", performanceCollectorService)
: base(logger, mediator, "###UmbraSyncMainUI", performanceCollectorService)
{
_uiSharedService = uiShared;
_configService = configService;
@@ -80,11 +81,11 @@ public class CompactUi : WindowMediatorSubscriberBase
#if DEBUG
string dev = "Dev Build";
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();
#else
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
Mediator.Subscribe<SwitchToMainUiMessage>(this, (_) => IsOpen = true);
Mediator.Subscribe<SwitchToIntroUiMessage>(this, (_) => IsOpen = false);
@@ -104,7 +105,7 @@ public class CompactUi : WindowMediatorSubscriberBase
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);
WindowContentWidth = UiSharedService.GetWindowContentRegionWidth();
if (!_apiController.IsCurrentVersion)
@@ -176,6 +177,14 @@ public class CompactUi : WindowMediatorSubscriberBase
}
ImGui.Separator();
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;
using (ImRaii.PushId("group-user-popup")) _selectPairsForGroupUi.Draw(_pairManager.DirectPairs);
using (ImRaii.PushId("grouping-popup")) _selectGroupForPairUi.Draw();
@@ -367,6 +376,29 @@ public class CompactUi : WindowMediatorSubscriberBase
_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();
}
@@ -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);
if (!printShard) ImGui.AlignTextToFramePadding();
ImGui.TextColored(ImGuiColors.ParsedBlue, userCount);
ImGui.TextColored(UiSharedService.AccentColor, userCount);
ImGui.SameLine();
if (!printShard) ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Users Online");
@@ -592,7 +624,7 @@ public class CompactUi : WindowMediatorSubscriberBase
{
ServerState.Connecting => ImGuiColors.DalamudYellow,
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.Disconnecting => ImGuiColors.DalamudYellow,
ServerState.Unauthorized => ImGuiColors.DalamudRed,

View File

@@ -163,13 +163,13 @@ public class DownloadUi : WindowMediatorSubscriberBase
UiSharedService.Color(0, 0, 0, transparency), 1);
drawList.AddRectFilled(dlBarStart with { X = dlBarStart.X - dlBarBorder, Y = dlBarStart.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,
UiSharedService.Color(0, 0, 0, transparency), 1);
var dlProgressPercent = transferredBytes / (double)totalBytes;
drawList.AddRectFilled(dlBarStart,
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)
{

View File

@@ -211,6 +211,40 @@ public class SettingsUi : WindowMediatorSubscriberBase
_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();
_uiShared.BigText("Transfer UI");

View File

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

View File

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