1.11.6 (#4)
All checks were successful
Tag and Release Lightless / tag-and-release (push) Successful in 36s

1.11.6 Changelog (In Progress)
---
* Update submodule reference
* Update dalamud sdk
* Reworked the Syncshell Admin Page
   - Fixed that owners are visible in the list, Removed Pin/Remove/Ban buttons on Owners.
   - Styling is done similiar as settings page.
   - Added 1 or 3 day(s) option for inactive check.
+ Added new functions on the Server Top Bar button
   - Right click on the button will disconnect you from Lightless
   - Shift+Left click will open the settings page
+ Added colors section in the settings to change accent colors.
   - The nameplate coloring has been moved to this section
+ Added pin option from Dalamud in the UI.
+ Added ability to pause syncing while going in Instance/Duty
+ Added functionality to make syncshell folders
+ Fixed nameplate bug in PVP
+ added self-threshold warning

Co-authored-by: defnotken <itsdefnotken@gmail.com>
Co-authored-by: CakeAndBanana <admin@cakeandbanana.nl>
Co-authored-by: thijmenh <thijmenhogenkamp@gmail.com>
Co-authored-by: choco <choco@noreply.git.lightless-sync.org>
Co-authored-by: cake <cake@noreply.git.lightless-sync.org>
Co-authored-by: choco <thijmenhogenkamp@gmail.com>
Reviewed-on: #4
This commit was merged in pull request #4.
This commit is contained in:
2025-09-11 23:43:11 +02:00
parent b177dbd595
commit abe28e931c
52 changed files with 1557 additions and 631 deletions

View File

@@ -1,13 +1,14 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Utility;
using LightlessSync.API.Data.Enum;
using LightlessSync.API.Data.Extensions;
using LightlessSync.API.Dto.Group;
using LightlessSync.Interop.Ipc;
using LightlessSync.LightlessConfiguration;
using LightlessSync.LightlessConfiguration.Configurations;
using LightlessSync.PlayerData.Handlers;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services;
@@ -15,6 +16,7 @@ using LightlessSync.Services.Mediator;
using LightlessSync.Services.ServerConfiguration;
using LightlessSync.UI.Components;
using LightlessSync.UI.Handlers;
using LightlessSync.Utils;
using LightlessSync.WebAPI;
using LightlessSync.WebAPI.Files;
using LightlessSync.WebAPI.Files.Models;
@@ -30,21 +32,27 @@ namespace LightlessSync.UI;
public class CompactUi : WindowMediatorSubscriberBase
{
private readonly CharacterAnalyzer _characterAnalyzer;
private readonly ApiController _apiController;
private readonly LightlessConfigService _configService;
private readonly ConcurrentDictionary<GameObjectHandler, Dictionary<string, FileDownloadStatus>> _currentDownloads = new();
private readonly DrawEntityFactory _drawEntityFactory;
private readonly FileUploadManager _fileTransferManager;
private readonly PlayerPerformanceConfigService _playerPerformanceConfig;
private readonly PairManager _pairManager;
private readonly SelectTagForPairUi _selectGroupForPairUi;
private readonly SelectTagForPairUi _selectTagForPairUi;
private readonly SelectTagForSyncshellUi _selectTagForSyncshellUi;
private readonly SelectSyncshellForTagUi _selectSyncshellForTagUi;
private readonly RenameSyncshellTagUi _renameSyncshellTagUi;
private readonly SelectPairForTagUi _selectPairsForGroupUi;
private readonly RenameTagUi _renameTagUi;
private readonly RenamePairTagUi _renamePairTagUi;
private readonly IpcManager _ipcManager;
private readonly ServerConfigurationManager _serverManager;
private readonly TopTabMenu _tabMenu;
private readonly TagHandler _tagHandler;
private readonly UiSharedService _uiSharedService;
private List<IDrawFolder> _drawFolders;
private Dictionary<ObjectKind, Dictionary<string, CharacterAnalyzer.FileDataEntry>>? _cachedAnalysis;
private Pair? _lastAddedUser;
private string _lastAddedUserComment = string.Empty;
private Vector2 _lastPosition = Vector2.One;
@@ -57,8 +65,10 @@ public class CompactUi : WindowMediatorSubscriberBase
public CompactUi(ILogger<CompactUi> logger, UiSharedService uiShared, LightlessConfigService configService, ApiController apiController, PairManager pairManager,
ServerConfigurationManager serverManager, LightlessMediator mediator, FileUploadManager fileTransferManager,
TagHandler tagHandler, DrawEntityFactory drawEntityFactory, SelectTagForPairUi selectTagForPairUi, SelectPairForTagUi selectPairForTagUi, RenameTagUi renameTagUi,
PerformanceCollectorService performanceCollectorService, IpcManager ipcManager)
TagHandler tagHandler, DrawEntityFactory drawEntityFactory,
SelectTagForPairUi selectTagForPairUi, SelectPairForTagUi selectPairForTagUi, RenamePairTagUi renameTagUi,
SelectTagForSyncshellUi selectTagForSyncshellUi, SelectSyncshellForTagUi selectSyncshellForTagUi, RenameSyncshellTagUi renameSyncshellTagUi,
PerformanceCollectorService performanceCollectorService, IpcManager ipcManager, CharacterAnalyzer characterAnalyzer, PlayerPerformanceConfigService playerPerformanceConfig)
: base(logger, mediator, "###LightlessSyncMainUI", performanceCollectorService)
{
_uiSharedService = uiShared;
@@ -69,13 +79,16 @@ public class CompactUi : WindowMediatorSubscriberBase
_fileTransferManager = fileTransferManager;
_tagHandler = tagHandler;
_drawEntityFactory = drawEntityFactory;
_selectGroupForPairUi = selectTagForPairUi;
_selectTagForPairUi = selectTagForPairUi;
_selectTagForSyncshellUi = selectTagForSyncshellUi;
_selectSyncshellForTagUi = selectSyncshellForTagUi;
_renameSyncshellTagUi = renameSyncshellTagUi;
_selectPairsForGroupUi = selectPairForTagUi;
_renameTagUi = renameTagUi;
_renamePairTagUi = renameTagUi;
_ipcManager = ipcManager;
_tabMenu = new TopTabMenu(Mediator, _apiController, _pairManager, _uiSharedService);
AllowPinning = false;
AllowPinning = true;
AllowClickthrough = false;
TitleBarButtons = new()
{
@@ -137,6 +150,8 @@ public class CompactUi : WindowMediatorSubscriberBase
MinimumSize = new Vector2(375, 400),
MaximumSize = new Vector2(375, 2000),
};
_characterAnalyzer = characterAnalyzer;
_playerPerformanceConfig = playerPerformanceConfig;
}
protected override void DrawInternal()
@@ -199,9 +214,12 @@ public class CompactUi : WindowMediatorSubscriberBase
float pairlistEnd = ImGui.GetCursorPosY();
using (ImRaii.PushId("transfers")) DrawTransfers();
_transferPartHeight = ImGui.GetCursorPosY() - pairlistEnd - ImGui.GetTextLineHeight();
using (ImRaii.PushId("group-user-popup")) _selectPairsForGroupUi.Draw(_pairManager.DirectPairs);
using (ImRaii.PushId("group-user-edit")) _renameTagUi.Draw(_pairManager.DirectPairs);
using (ImRaii.PushId("grouping-popup")) _selectGroupForPairUi.Draw();
using (ImRaii.PushId("group-pair-popup")) _selectPairsForGroupUi.Draw(_pairManager.DirectPairs);
using (ImRaii.PushId("group-syncshell-popup")) _selectSyncshellForTagUi.Draw([.. _pairManager.Groups.Values]);
using (ImRaii.PushId("group-pair-edit")) _renamePairTagUi.Draw();
using (ImRaii.PushId("group-syncshell-edit")) _renameSyncshellTagUi.Draw();
using (ImRaii.PushId("grouping-pair-popup")) _selectTagForPairUi.Draw();
using (ImRaii.PushId("grouping-syncshell-popup")) _selectTagForSyncshellUi.Draw();
}
if (_configService.Current.OpenPopupOnAdd && _pairManager.LastAddedUser != null)
@@ -396,6 +414,13 @@ public class CompactUi : WindowMediatorSubscriberBase
{
var uidText = GetUidText();
//Getting information of character and triangles threshold to show overlimit status in UID bar.
_cachedAnalysis = _characterAnalyzer.LastAnalysis.DeepClone();
var groupedfiles = _cachedAnalysis.First().Value.Select(v => v.Value).GroupBy(f => f.FileType, StringComparer.Ordinal)
.OrderBy(k => k.Key, StringComparer.Ordinal).ToList();
var actualTriCount = _cachedAnalysis.First().Value.Sum(f => f.Value.Triangles);
var isOverTriHold = actualTriCount > (_playerPerformanceConfig.Current.TrisWarningThresholdThousands * 1000);
using (_uiSharedService.UidFont.Push())
{
var uidTextSize = ImGui.CalcTextSize(uidText);
@@ -403,24 +428,50 @@ public class CompactUi : WindowMediatorSubscriberBase
ImGui.TextColored(GetUidColor(), uidText);
}
if (groupedfiles != null)
{
//Checking of VRAM threshhold
var actualVramUsage = groupedfiles.SingleOrDefault(v => string.Equals(v.Key, "tex", StringComparison.Ordinal)).Sum(f => f.OriginalSize);
var isOverVRAMUsage = _playerPerformanceConfig.Current.VRAMSizeWarningThresholdMiB * 1024 * 1024 < actualVramUsage;
if (isOverTriHold || isOverVRAMUsage)
{
ImGui.SameLine();
_uiSharedService.IconText(FontAwesomeIcon.ExclamationTriangle, UIColors.Get("LightlessYellow"));
string warningMessage = "";
if (isOverTriHold)
{
warningMessage += $"You exceed your own triangles threshold by " +
$"{actualTriCount - _playerPerformanceConfig.Current.TrisWarningThresholdThousands * 1000} triangles.";
warningMessage += Environment.NewLine;
}
if (isOverVRAMUsage)
{
warningMessage += $"You exceed your own VRAM threshold by " +
$"{UiSharedService.ByteToString(actualVramUsage - (_playerPerformanceConfig.Current.VRAMSizeWarningThresholdMiB * 1024 * 1024))}.";
}
UiSharedService.AttachToolTip(warningMessage);
}
}
if (_apiController.ServerState is ServerState.Connected)
{
if (ImGui.IsItemClicked())
{
ImGui.SetClipboardText(_apiController.DisplayName);
}
UiSharedService.AttachToolTip("Click to copy");
if (!string.Equals(_apiController.DisplayName, _apiController.UID, StringComparison.Ordinal))
{
var origTextSize = ImGui.CalcTextSize(_apiController.UID);
ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X) / 2 - (origTextSize.X / 2));
ImGui.TextColored(GetUidColor(), _apiController.UID);
UiSharedService.AttachToolTip("Click to copy");
if (ImGui.IsItemClicked())
{
ImGui.SetClipboardText(_apiController.UID);
}
UiSharedService.AttachToolTip("Click to copy");
}
}
else
@@ -462,12 +513,12 @@ public class CompactUi : WindowMediatorSubscriberBase
bool FilterVisibleUsers(KeyValuePair<Pair, List<GroupFullInfoDto>> u)
=> u.Key.IsVisible
&& (_configService.Current.ShowSyncshellUsersInVisible || !(!_configService.Current.ShowSyncshellUsersInVisible && !u.Key.IsDirectlyPaired));
bool FilterTagusers(KeyValuePair<Pair, List<GroupFullInfoDto>> u, string tag)
=> u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && _tagHandler.HasTag(u.Key.UserData.UID, tag);
bool FilterTagUsers(KeyValuePair<Pair, List<GroupFullInfoDto>> u, string tag)
=> u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && _tagHandler.HasPairTag(u.Key.UserData.UID, tag);
bool FilterGroupUsers(KeyValuePair<Pair, List<GroupFullInfoDto>> u, GroupFullInfoDto group)
=> u.Value.Exists(g => string.Equals(g.GID, group.GID, StringComparison.Ordinal));
bool FilterNotTaggedUsers(KeyValuePair<Pair, List<GroupFullInfoDto>> u)
=> u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && !_tagHandler.HasAnyTag(u.Key.UserData.UID);
=> u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && !_tagHandler.HasAnyPairTag(u.Key.UserData.UID);
bool FilterOfflineUsers(KeyValuePair<Pair, List<GroupFullInfoDto>> u)
=> ((u.Key.IsDirectlyPaired && _configService.Current.ShowSyncshellOfflineUsersSeparately)
|| !_configService.Current.ShowSyncshellOfflineUsersSeparately)
@@ -487,46 +538,48 @@ public class CompactUi : WindowMediatorSubscriberBase
}
List<IDrawFolder> groupFolders = new();
foreach (var group in _pairManager.GroupPairs.Select(g => g.Key).OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase))
{
var allGroupPairs = ImmutablePairList(allPairs
.Where(u => FilterGroupUsers(u, group)));
var filteredGroupPairs = filteredPairs
.Where(u => FilterGroupUsers(u, group) && FilterOnlineOrPausedSelf(u))
.OrderByDescending(u => u.Key.IsOnline)
.ThenBy(u =>
{
if (string.Equals(u.Key.UserData.UID, group.OwnerUID, StringComparison.Ordinal)) return 0;
if (group.GroupPairUserInfos.TryGetValue(u.Key.UserData.UID, out var info))
{
if (info.IsModerator()) return 1;
if (info.IsPinned()) return 2;
}
return u.Key.IsVisible ? 3 : 4;
})
.ThenBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase)
.ToDictionary(k => k.Key, k => k.Value);
GetGroups(allPairs, filteredPairs, group, out ImmutableList<Pair> allGroupPairs, out Dictionary<Pair, List<GroupFullInfoDto>> filteredGroupPairs);
groupFolders.Add(_drawEntityFactory.CreateDrawGroupFolder(group, filteredGroupPairs, allGroupPairs));
}
if (_configService.Current.GroupUpSyncshells)
drawFolders.Add(new DrawGroupedGroupFolder(groupFolders, _tagHandler, _uiSharedService));
drawFolders.Add(new DrawGroupedGroupFolder(groupFolders, _tagHandler, _uiSharedService, _selectSyncshellForTagUi, _renameSyncshellTagUi, ""));
else
drawFolders.AddRange(groupFolders);
var tags = _tagHandler.GetAllTagsSorted();
var tags = _tagHandler.GetAllPairTagsSorted();
foreach (var tag in tags)
{
var allTagPairs = ImmutablePairList(allPairs
.Where(u => FilterTagusers(u, tag)));
.Where(u => FilterTagUsers(u, tag)));
var filteredTagPairs = BasicSortedDictionary(filteredPairs
.Where(u => FilterTagusers(u, tag) && FilterOnlineOrPausedSelf(u)));
.Where(u => FilterTagUsers(u, tag) && FilterOnlineOrPausedSelf(u)));
drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(tag, filteredTagPairs, allTagPairs));
}
var syncshellTags = _tagHandler.GetAllSyncshellTagsSorted();
foreach (var syncshelltag in syncshellTags)
{
List<IDrawFolder> syncshellFolderTags = [];
foreach (var group in _pairManager.GroupPairs.Select(g => g.Key).OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase))
{
if (_tagHandler.HasSyncshellTag(group.GID, syncshelltag))
{
GetGroups(allPairs, filteredPairs, group, out ImmutableList<Pair> allGroupPairs, out Dictionary<Pair, List<GroupFullInfoDto>> filteredGroupPairs);
syncshellFolderTags.Add(_drawEntityFactory.CreateDrawGroupFolder($"tag_{group.GID}", group, filteredGroupPairs, allGroupPairs));
}
}
if (syncshellFolderTags.Count > 0)
{
drawFolders.Add(new DrawGroupedGroupFolder(syncshellFolderTags, _tagHandler, _uiSharedService, _selectSyncshellForTagUi, _renameSyncshellTagUi, syncshelltag));
}
}
var allOnlineNotTaggedPairs = ImmutablePairList(allPairs
.Where(FilterNotTaggedUsers));
var onlineNotTaggedPairs = BasicSortedDictionary(filteredPairs
@@ -561,6 +614,27 @@ public class CompactUi : WindowMediatorSubscriberBase
ImmutablePairList(allPairs.Where(u => u.Key.IsOneSidedPair))));
return drawFolders;
void GetGroups(Dictionary<Pair, List<GroupFullInfoDto>> allPairs, Dictionary<Pair, List<GroupFullInfoDto>> filteredPairs, GroupFullInfoDto group, out ImmutableList<Pair> allGroupPairs, out Dictionary<Pair, List<GroupFullInfoDto>> filteredGroupPairs)
{
allGroupPairs = ImmutablePairList(allPairs
.Where(u => FilterGroupUsers(u, group)));
filteredGroupPairs = filteredPairs
.Where(u => FilterGroupUsers(u, group) && FilterOnlineOrPausedSelf(u))
.OrderByDescending(u => u.Key.IsOnline)
.ThenBy(u =>
{
if (string.Equals(u.Key.UserData.UID, group.OwnerUID, StringComparison.Ordinal)) return 0;
if (group.GroupPairUserInfos.TryGetValue(u.Key.UserData.UID, out var info))
{
if (info.IsModerator()) return 1;
if (info.IsPinned()) return 2;
}
return u.Key.IsVisible ? 3 : 4;
})
.ThenBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase)
.ToDictionary(k => k.Key, k => k.Value);
}
}
private string GetServerError()