Compare commits

...

10 Commits

Author SHA1 Message Date
defnotken
2f383c3ebe Version bump 2025-09-01 09:05:56 -05:00
defnotken
5dfd859542 Adding gitea workflow 2025-09-01 09:04:37 -05:00
Pim
beb737b267 Added functionality to rename groups (#12)
* Updated logos in Profile screen.

* Updated IPC calls for Petrenamer and bumped API version.

* IPC calls updated, APIVersion pushed for Moodles

* Updated version 1.11.4 in csproj

* Added Rename Tag UI for renaming of groups
2025-09-01 08:53:28 -05:00
azyges
68c0bf1334 random ui changes 2025-09-01 07:52:50 +09:00
azyges
05daa59e40 incorrect label.. 2025-08-31 10:02:02 +09:00
azyges
c3a6f2c88d fancy settings 2025-08-31 08:29:23 +09:00
defnotken
dc0265f614 1.11.4 Release Branch (#9)
* Updated logos in Profile screen. (#8)

* UI updates + bugfixes

* Updated version 1.11.4 in csproj

---------

Co-authored-by: Pim <48980200+CakeAndBanana@users.noreply.github.com>
2025-08-30 12:39:03 -05:00
defnotken
ba0e1cea08 Update lightless-tag-and-release.yml 2025-08-29 19:40:00 -05:00
defnotken
1dea9b713e Update LightlessSync.csproj 2025-08-29 18:51:29 -05:00
defnotken
23c57aedc4 Add Nameplates + Clean up.
* Yeet Token!

* Cleaning up workflow

* Testing auto version bump

* ExistingNames

* Remove a key

* Github Token no work

* Changing Assembly Version

* Version Fix

* Fixing version v2

* Cleanup naming

* Update LightlessSync.csproj

* Add nameplate settings + run code clean up

* purple
2025-08-29 18:48:01 -05:00
71 changed files with 1427 additions and 761 deletions

View File

@@ -0,0 +1,182 @@
name: Tag and Release Lightless
on:
push:
branches: [ master ]
env:
PLUGIN_NAME: LightlessSync
DOTNET_VERSION: 9.x
jobs:
tag-and-release:
runs-on: ubuntu-22.04
permissions:
contents: write
steps:
- name: Checkout Lightless
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- name: Setup .NET 9 SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.x
- name: Download Dalamud
run: |
cd /
mkdir -p root/.xlcore/dalamud/Hooks/dev
curl -O https://goatcorp.github.io/dalamud-distrib/stg/latest.zip
unzip latest.zip -d /root/.xlcore/dalamud/Hooks/dev
- name: Lets Build Lightless!
run: |
dotnet restore
dotnet build --configuration Release --no-restore
dotnet publish --configuration Release --no-build
- name: Get version
id: package_version
uses: KageKirin/get-csproj-version@v0
with:
file: LightlessSync/LightlessSync.csproj
- name: Display version
run: |
echo "Version: ${{ steps.package_version.outputs.version }}"
- name: Prepare Lightless Client
run: |
PUBLISH_PATH="/workspace/Lightless-Sync/LightlessClient/LightlessSync/bin/x64/Release/publish/"
if [ -d "$PUBLISH_PATH" ]; then
rm -rf "$PUBLISH_PATH"
echo "Removed $PUBLISH_PATH"
else
echo "$PUBLISH_PATH does not exist, nothing to remove."
fi
mkdir -p output
(cd /workspace/Lightless-Sync/LightlessClient/LightlessSync/bin/x64/Release/ && zip -r $OLDPWD/output/LightlessClient.zip *)
- name: Create Git tag if not exists
run: |
tag="${{ steps.package_version.outputs.version }}"
git fetch --tags
if ! git tag -l "$tag" | grep -q "$tag"; then
echo "Tag $tag does not exist. Creating and pushing..."
git config user.name "GitHub Action"
git config user.email "action@github.com"
git tag "$tag"
git push origin "$tag"
else
echo "Tag $tag already exists. Skipping tag creation."
fi
- name: Create Release
id: create_release
run: |
echo "=== Searching for existing release ${{ steps.package_version.outputs.version }}==="
release_id=$(curl -s -H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
"https://git.lightless-sync.org/api/v1/repos/${GITHUB_REPOSITORY}/releases/tags/${{ steps.package_version.outputs.version }}" | jq -r .id)
if [ "$release_id" != "null" ]; then
echo "=== Deleting existing release ${{ steps.package_version.outputs.version }}==="
curl -X DELETE -H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
"https://git.lightless-sync.org/api/v1/repos/${GITHUB_REPOSITORY}/releases/$release_id"
fi
echo "=== Creating new release ${{ steps.package_version.outputs.version }}==="
response=$(
curl --fail-with-body -X POST \
-H "Content-Type: application/json" \
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
-d '{
"tag_name": "${{ steps.package_version.outputs.version }}",
"name": "${{ steps.package_version.outputs.version }}",
"draft": false,
"prerelease": false
}' \
"https://git.lightless-sync.org/api/v1/repos/${GITHUB_REPOSITORY}/releases"
)
release_id=$(echo "$response" | jq -r .id)
echo "release_id=$release_id" >> "$GITHUB_OUTPUT"
- name: Upload Assets to release
run: |
curl --fail-with-body -s -X POST \
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
-F "attachment=@output/LightlessClient.zip" \
"https://git.lightless-sync.org/api/v1/repos/${GITHUB_REPOSITORY}/releases/${{ steps.create_release.outputs.release_id }}/assets"
- name: Clone plugin hosting repo
run: |
mkdir LightlessSyncRepo
cd LightlessSyncRepo
git clone https://git.lightless-sync.org/${{ gitea.repository_owner }}/LightlessSync.git
env:
GIT_TERMINAL_PROMPT: 0
- name: Update plogonmaster.json with version
env:
VERSION: ${{ steps.package_version.outputs.version }}
run: |
set -e
pluginJsonPath="${PLUGIN_NAME}/bin/x64/Release/${PLUGIN_NAME}.json"
repoJsonPath="LightlessSyncRepo/LightlessSync/plogonmaster.json"
version="${VERSION}"
downloadUrl="https://git.lightless-sync.org/${{ gitea.repository_owner }}/LightlessClient/releases/download/$version/LightlessClient.zip"
# Read plugin JSON
pluginJson=$(cat "$pluginJsonPath")
internalName=$(jq -r '.InternalName' <<< "$pluginJson")
dalamudApiLevel=$(jq -r '.DalamudApiLevel' <<< "$pluginJson")
# Read repo JSON (force array if not already)
repoJsonRaw=$(cat "$repoJsonPath")
if echo "$repoJsonRaw" | jq 'type' | grep -q '"array"'; then
repoJson="$repoJsonRaw"
else
repoJson="[$repoJsonRaw]"
fi
# Update matching plugin entry
updatedRepoJson=$(jq \
--arg internalName "$internalName" \
--arg dalamudApiLevel "$dalamudApiLevel" \
--arg version "$version" \
--arg downloadUrl "$downloadUrl" \
'
map(
if .InternalName == $internalName
then
.DalamudApiLevel = $dalamudApiLevel
| .AssemblyVersion = $version
| .DownloadLinkInstall = $downloadUrl
| .DownloadLinkTesting = $downloadUrl
| .DownloadLinkUpdate = $downloadUrl
else
.
end
)
' <<< "$repoJson")
# Write back to file
echo "$updatedRepoJson" > "$repoJsonPath"
# Output the content of the file
cat "$repoJsonPath"
- name: Commit and push to LightlessSync
run: |
cd LightlessSyncRepo/LightlessSync
git config user.name "github-actions"
git config user.email "github-actions@github.com"
git add .
git diff-index --quiet HEAD || git commit -m "Update ${{ env.PLUGIN_NAME }} to ${{ steps.package_version.outputs.version }}"
git push https://x-access-token:${{ secrets.AUTOMATION_TOKEN }}@git.lightless-sync.org/${{ gitea.repository_owner }}/LightlessSync.git HEAD:main

View File

@@ -81,4 +81,60 @@ jobs:
name: ${{ steps.package_version.outputs.version }}
draft: false
prerelease: false
files: output/LightlessClient.zip
files: output/LightlessClient.zip
- name: Clone plugin hosting repo
run: |
mkdir LightlessSyncRepo
cd LightlessSyncRepo
git clone https://github.com/${{ github.repository_owner }}/LightlessSync.git
env:
GIT_TERMINAL_PROMPT: 0
- name: Update plogonmaster.json with version
shell: pwsh
env:
VERSION: ${{ steps.package_version.outputs.version }}
run: |
$pluginJsonPath = "${{ env.PLUGIN_NAME }}/bin/x64/Release/${{ env.PLUGIN_NAME }}.json"
$pluginJson = Get-Content $pluginJsonPath | ConvertFrom-Json
$repoJsonPath = "LightlessSyncRepo/LightlessSync/plogonmaster.json"
$repoJsonRaw = Get-Content $repoJsonPath -Raw
$repoJson = $repoJsonRaw | ConvertFrom-Json
$version = $env:VERSION
$downloadUrl = "https://github.com/${{ github.repository_owner }}/LightlessClient/releases/download/$version/LightlessClient.zip"
if (-not ($repoJson -is [System.Collections.IEnumerable])) {
$repoJson = @($repoJson)
}
foreach ($plugin in $repoJson) {
if ($plugin.InternalName -eq $pluginJson.InternalName) {
$plugin.DalamudApiLevel = $pluginJson.DalamudApiLevel
$plugin.AssemblyVersion = $version
$plugin.DownloadLinkInstall = $downloadUrl
$plugin.DownloadLinkTesting = $downloadUrl
$plugin.DownloadLinkUpdate = $downloadUrl
}
}
$repoJson | ConvertTo-Json -Depth 100 | Set-Content $repoJsonPath
# Convert to JSON and force array brackets if necessary
$repoJsonString = $repoJson | ConvertTo-Json -Depth 100
# If the output is not an array, wrap it manually
if ($repoJsonString.Trim().StartsWith('{')) {
$repoJsonString = "[$repoJsonString]"
}
$repoJsonString | Set-Content $repoJsonPath
- name: Commit and push to LightlessSync
run: |
cd LightlessSyncRepo/LightlessSync
git config user.name "github-actions"
git config user.email "github-actions@github.com"
git add .
git commit -m "Update ${{ env.PLUGIN_NAME }} to ${{ steps.package_version.outputs.version }}"
git push https://x-access-token:${{ secrets.LIGHTLESS_TOKEN }}@github.com/${{ github.repository_owner }}/LightlessSync.git HEAD:main

View File

@@ -27,9 +27,9 @@ public sealed class IpcCallerMoodles : IIpcCaller
_moodlesApiVersion = pi.GetIpcSubscriber<int>("Moodles.Version");
_moodlesOnChange = pi.GetIpcSubscriber<IPlayerCharacter, object>("Moodles.StatusManagerModified");
_moodlesGetStatus = pi.GetIpcSubscriber<nint, string>("Moodles.GetStatusManagerByPtr");
_moodlesSetStatus = pi.GetIpcSubscriber<nint, string, object>("Moodles.SetStatusManagerByPtr");
_moodlesRevertStatus = pi.GetIpcSubscriber<nint, object>("Moodles.ClearStatusManagerByPtr");
_moodlesGetStatus = pi.GetIpcSubscriber<nint, string>("Moodles.GetStatusManagerByPtrV2");
_moodlesSetStatus = pi.GetIpcSubscriber<nint, string, object>("Moodles.SetStatusManagerByPtrV2");
_moodlesRevertStatus = pi.GetIpcSubscriber<nint, object>("Moodles.ClearStatusManagerByPtrV2");
_moodlesOnChange.Subscribe(OnMoodlesChange);
@@ -47,7 +47,7 @@ public sealed class IpcCallerMoodles : IIpcCaller
{
try
{
APIAvailable = _moodlesApiVersion.InvokeFunc() == 1;
APIAvailable = _moodlesApiVersion.InvokeFunc() == 3;
}
catch
{

View File

@@ -30,12 +30,12 @@ public sealed class IpcCallerPetNames : IIpcCaller
_dalamudUtil = dalamudUtil;
_lightlessMediator = lightlessMediator;
_petnamesReady = pi.GetIpcSubscriber<object>("PetRenamer.Ready");
_petnamesDisposing = pi.GetIpcSubscriber<object>("PetRenamer.Disposing");
_petnamesReady = pi.GetIpcSubscriber<object>("PetRenamer.OnReady");
_petnamesDisposing = pi.GetIpcSubscriber<object>("PetRenamer.OnDisposing");
_apiVersion = pi.GetIpcSubscriber<(uint, uint)>("PetRenamer.ApiVersion");
_enabled = pi.GetIpcSubscriber<bool>("PetRenamer.Enabled");
_enabled = pi.GetIpcSubscriber<bool>("PetRenamer.IsEnabled");
_playerDataChanged = pi.GetIpcSubscriber<string, object>("PetRenamer.PlayerDataChanged");
_playerDataChanged = pi.GetIpcSubscriber<string, object>("PetRenamer.OnPlayerDataChanged");
_getPlayerData = pi.GetIpcSubscriber<string>("PetRenamer.GetPlayerData");
_setPlayerData = pi.GetIpcSubscriber<string, object>("PetRenamer.SetPlayerData");
_clearPlayerData = pi.GetIpcSubscriber<ushort, object>("PetRenamer.ClearPlayerData");
@@ -56,7 +56,7 @@ public sealed class IpcCallerPetNames : IIpcCaller
APIAvailable = _enabled?.InvokeFunc() ?? false;
if (APIAvailable)
{
APIAvailable = _apiVersion?.InvokeFunc() is { Item1: 3, Item2: >= 1 };
APIAvailable = _apiVersion?.InvokeFunc() is { Item1: 4, Item2: >= 0 };
}
}
catch
@@ -84,7 +84,7 @@ public sealed class IpcCallerPetNames : IIpcCaller
{
string localNameData = _getPlayerData.InvokeFunc();
return string.IsNullOrEmpty(localNameData) ? string.Empty : localNameData;
}
}
catch (Exception e)
{
_logger.LogWarning(e, "Could not obtain Pet Nicknames data");

View File

@@ -1,5 +1,4 @@
using LightlessSync.LightlessConfiguration.Configurations;
using LightlessSync.LightlessConfiguration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Reflection;

View File

@@ -13,6 +13,8 @@ public class LightlessConfig : ILightlessConfiguration
public bool EnableDtrEntry { get; set; } = false;
public bool ShowUidInDtrTooltip { get; set; } = true;
public bool PreferNoteInDtrTooltip { get; set; } = false;
public bool IsNameplateColorsEnabled { get; set; } = false;
public DtrEntry.Colors NameplateColors { get; set; } = new(Foreground: 0xE69138u, Glow: 0xFFBA47u);
public bool UseColorsInDtr { get; set; } = true;
public DtrEntry.Colors DtrColorsDefault { get; set; } = default;
public DtrEntry.Colors DtrColorsNotConnected { get; set; } = new(Glow: 0x0428FFu);
@@ -60,4 +62,6 @@ public class LightlessConfig : ILightlessConfiguration
public int Version { get; set; } = 1;
public NotificationLocation WarningNotification { get; set; } = NotificationLocation.Both;
public bool UseFocusTarget { get; set; } = false;
public bool overrideFriendColor { get; set; } = false;
public bool overridePartyColor { get; set; } = false;
}

View File

@@ -1,5 +1,4 @@
using LightlessSync.API.Data.Enum;
using LightlessSync.LightlessConfiguration.Configurations;
namespace LightlessSync.LightlessConfiguration.Configurations;

View File

@@ -151,6 +151,7 @@ public class LightlessPlugin : MediatorSubscriberBase, IHostedService
_runtimeServiceScope.ServiceProvider.GetRequiredService<TransientResourceManager>();
_runtimeServiceScope.ServiceProvider.GetRequiredService<VisibleUserDataDistributor>();
_runtimeServiceScope.ServiceProvider.GetRequiredService<NotificationService>();
_runtimeServiceScope.ServiceProvider.GetRequiredService<NameplateService>();
#if !DEBUG
if (_lightlessConfigService.Current.LogLevel != LogLevel.Information)

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<Authors></Authors>
<Company></Company>
<Version>1.11.2</Version>
<Version>1.11.5</Version>
<Description></Description>
<Copyright></Copyright>
<PackageProjectUrl>https://github.com/Light-Public-Syncshells/LightlessClient</PackageProjectUrl>

View File

@@ -8,7 +8,6 @@ using LightlessSync.PlayerData.Handlers;
using LightlessSync.Services;
using LightlessSync.Services.Mediator;
using Microsoft.Extensions.Logging;
using CharacterData = LightlessSync.PlayerData.Data.CharacterData;
namespace LightlessSync.PlayerData.Factories;

View File

@@ -119,6 +119,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
Mediator.Publish(new EventMessage(new Event(PlayerName, Pair.UserData, nameof(PairHandler),
EventSeverity.Informational, text)));
Mediator.Publish(new RefreshUiMessage());
Mediator.Publish(new VisibilityChange());
}
}
}

View File

@@ -48,6 +48,7 @@ public class Pair
public long LastAppliedDataTris { get; set; } = -1;
public long LastAppliedApproximateVRAMBytes { get; set; } = -1;
public string Ident => _onlineUserIdentDto?.Ident ?? string.Empty;
public uint PlayerCharacterId => CachedPlayer?.PlayerCharacterId ?? uint.MaxValue;
public UserData UserData => UserPair.User;
@@ -71,8 +72,8 @@ public class Pair
Name = openProfileSeString,
OnClicked = (a) => _mediator.Publish(new ProfileOpenStandaloneMessage(this)),
UseDefaultPrefix = false,
PrefixChar = 'M',
PrefixColor = 526
PrefixChar = 'L',
PrefixColor = 708
});
args.AddMenuItem(new MenuItem()
@@ -80,8 +81,8 @@ public class Pair
Name = reapplyDataSeString,
OnClicked = (a) => ApplyLastReceivedData(forced: true),
UseDefaultPrefix = false,
PrefixChar = 'M',
PrefixColor = 526
PrefixChar = 'L',
PrefixColor = 708
});
args.AddMenuItem(new MenuItem()
@@ -89,8 +90,8 @@ public class Pair
Name = changePermissions,
OnClicked = (a) => _mediator.Publish(new OpenPermissionWindow(this)),
UseDefaultPrefix = false,
PrefixChar = 'M',
PrefixColor = 526
PrefixChar = 'L',
PrefixColor = 708
});
args.AddMenuItem(new MenuItem()
@@ -98,8 +99,8 @@ public class Pair
Name = cyclePauseState,
OnClicked = (a) => _mediator.Publish(new CyclePauseMessage(UserData)),
UseDefaultPrefix = false,
PrefixChar = 'M',
PrefixColor = 526
PrefixChar = 'L',
PrefixColor = 708
});
}

View File

@@ -1,4 +1,5 @@
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Interface.ImGuiFileDialog;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
@@ -12,6 +13,7 @@ using LightlessSync.PlayerData.Factories;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.PlayerData.Services;
using LightlessSync.Services;
using LightlessSync.Services.CharaData;
using LightlessSync.Services.Events;
using LightlessSync.Services.Mediator;
using LightlessSync.Services.ServerConfiguration;
@@ -28,8 +30,6 @@ using Microsoft.Extensions.Logging;
using NReco.Logging.File;
using System.Net.Http.Headers;
using System.Reflection;
using LightlessSync.Services.CharaData;
using Dalamud.Game;
namespace LightlessSync;
@@ -41,7 +41,7 @@ public sealed class Plugin : IDalamudPlugin
IFramework framework, IObjectTable objectTable, IClientState clientState, ICondition condition, IChatGui chatGui,
IGameGui gameGui, IDtrBar dtrBar, IPluginLog pluginLog, ITargetManager targetManager, INotificationManager notificationManager,
ITextureProvider textureProvider, IContextMenu contextMenu, IGameInteropProvider gameInteropProvider, IGameConfig gameConfig,
ISigScanner sigScanner)
ISigScanner sigScanner, INamePlateGui namePlateGui)
{
if (!Directory.Exists(pluginInterface.ConfigDirectory.FullName))
Directory.CreateDirectory(pluginInterface.ConfigDirectory.FullName);
@@ -130,6 +130,7 @@ public sealed class Plugin : IDalamudPlugin
s.GetRequiredService<CharaDataManager>(),
s.GetRequiredService<LightlessMediator>()));
collection.AddSingleton<SelectPairForTagUi>();
collection.AddSingleton<RenameTagUi>();
collection.AddSingleton((s) => new EventAggregator(pluginInterface.ConfigDirectory.FullName,
s.GetRequiredService<ILogger<EventAggregator>>(), s.GetRequiredService<LightlessMediator>()));
collection.AddSingleton((s) => new DalamudUtilService(s.GetRequiredService<ILogger<DalamudUtilService>>(),
@@ -228,6 +229,8 @@ public sealed class Plugin : IDalamudPlugin
s.GetRequiredService<CacheMonitor>(), s.GetRequiredService<FileDialogManager>(), s.GetRequiredService<LightlessConfigService>(), s.GetRequiredService<DalamudUtilService>(),
pluginInterface, textureProvider, s.GetRequiredService<Dalamud.Localization>(), s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<TokenProvider>(),
s.GetRequiredService<LightlessMediator>()));
collection.AddScoped((s) => new NameplateService(s.GetRequiredService<ILogger<NameplateService>>(), s.GetRequiredService<LightlessConfigService>(), namePlateGui, clientState,
s.GetRequiredService<PairManager>(), s.GetRequiredService<LightlessMediator>()));
collection.AddHostedService(p => p.GetRequiredService<ConfigurationSaveService>());
collection.AddHostedService(p => p.GetRequiredService<LightlessMediator>());

View File

@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LightlessSync.Services.CharaData
namespace LightlessSync.Services.CharaData
{
internal class CharaDataTogetherManager
{

View File

@@ -1,6 +1,6 @@
using Dalamud.Utility;
using Lumina.Excel.Sheets;
using LightlessSync.API.Dto.CharaData;
using Lumina.Excel.Sheets;
using System.Globalization;
using System.Numerics;
using System.Text;

View File

@@ -1,10 +1,10 @@
using Lumina.Data.Files;
using LightlessSync.API.Data;
using LightlessSync.API.Data;
using LightlessSync.API.Data.Enum;
using LightlessSync.FileCache;
using LightlessSync.Services.Mediator;
using LightlessSync.UI;
using LightlessSync.Utils;
using Lumina.Data.Files;
using Microsoft.Extensions.Logging;
namespace LightlessSync.Services;

View File

@@ -1,5 +1,4 @@
using Dalamud.Game;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types;
@@ -10,13 +9,13 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Control;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using Lumina.Excel.Sheets;
using LightlessSync.API.Dto.CharaData;
using LightlessSync.Interop;
using LightlessSync.LightlessConfiguration;
using LightlessSync.PlayerData.Handlers;
using LightlessSync.Services.Mediator;
using LightlessSync.Utils;
using Lumina.Excel.Sheets;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Numerics;
@@ -135,7 +134,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
_cid = RebuildCID();
}
private Lazy<ulong> RebuildCID() => new(GetCID);
private Lazy<ulong> RebuildCID() => new(GetCID);
public bool IsWine { get; init; }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -93,5 +93,7 @@ public record GPoseLobbyReceiveCharaData(CharaDataDownloadDto CharaDataDownloadD
public record GPoseLobbyReceivePoseData(UserData UserData, PoseData PoseData) : MessageBase;
public record GPoseLobbyReceiveWorldData(UserData UserData, WorldData WorldData) : MessageBase;
public record OpenCharaDataHubWithFilterMessage(UserData UserData) : MessageBase;
public record VisibilityChange : MessageBase;
#pragma warning restore S2094
#pragma warning restore MA0048 // File name must match type name

View File

@@ -0,0 +1,91 @@
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.Gui.NamePlate;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using LightlessSync.LightlessConfiguration;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services.Mediator;
using LightlessSync.UI;
using Microsoft.Extensions.Logging;
namespace LightlessSync.Services;
public class NameplateService : DisposableMediatorSubscriberBase
{
private readonly LightlessConfigService _configService;
private readonly IClientState _clientState;
private readonly INamePlateGui _namePlateGui;
private readonly PairManager _pairManager;
public NameplateService(ILogger<NameplateService> logger,
LightlessConfigService configService,
INamePlateGui namePlateGui,
IClientState clientState,
PairManager pairManager,
LightlessMediator lightlessMediator) : base(logger, lightlessMediator)
{
_configService = configService;
_namePlateGui = namePlateGui;
_clientState = clientState;
_pairManager = pairManager;
_namePlateGui.OnNamePlateUpdate += OnNamePlateUpdate;
_namePlateGui.RequestRedraw();
Mediator.Subscribe<VisibilityChange>(this, (_) => _namePlateGui.RequestRedraw());
}
private void OnNamePlateUpdate(INamePlateUpdateContext context, IReadOnlyList<INamePlateUpdateHandler> handlers)
{
if (!_configService.Current.IsNameplateColorsEnabled && !_clientState.IsPvPExcludingDen) return;
var visibleUsersIds = _pairManager.GetOnlineUserPairs().Where(u => u.IsVisible && u.PlayerCharacterId != uint.MaxValue).Select(u => (ulong)u.PlayerCharacterId).ToHashSet();
var colors = _configService.Current.NameplateColors;
foreach (var handler in handlers)
{
var playerCharacter = handler.PlayerCharacter;
if (playerCharacter == null) { continue; }
var isInParty = playerCharacter.StatusFlags.HasFlag(StatusFlags.PartyMember);
var isFriend = playerCharacter.StatusFlags.HasFlag(StatusFlags.Friend);
bool partyColorAllowed = (_configService.Current.overridePartyColor && isInParty);
bool friendColorAllowed = (_configService.Current.overrideFriendColor && isFriend);
if (visibleUsersIds.Contains(handler.GameObjectId) &&
!(
(isInParty && !partyColorAllowed) ||
(isFriend && !friendColorAllowed)
))
{
handler.NameParts.TextWrap = CreateTextWrap(colors);
}
}
}
public void RequestRedraw()
{
_namePlateGui.RequestRedraw();
}
private static (SeString, SeString) CreateTextWrap(DtrEntry.Colors color)
{
var left = new Lumina.Text.SeStringBuilder();
var right = new Lumina.Text.SeStringBuilder();
left.PushColorRgba(color.Foreground);
right.PopColor();
left.PushEdgeColorRgba(color.Glow);
right.PopEdgeColor();
return (left.ToReadOnlySeString().ToDalamudString(), right.ToReadOnlySeString().ToDalamudString());
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
_namePlateGui.OnNamePlateUpdate -= OnNamePlateUpdate;
_namePlateGui.RequestRedraw();
}
}

View File

@@ -8,187 +8,187 @@ namespace LightlessSync.UI;
internal sealed partial class CharaDataHubUi
{
private static string GetAccessTypeString(AccessTypeDto dto) => dto switch
{
AccessTypeDto.AllPairs => "All Pairs",
AccessTypeDto.ClosePairs => "Direct Pairs",
AccessTypeDto.Individuals => "Specified",
AccessTypeDto.Public => "Everyone"
};
private static string GetAccessTypeString(AccessTypeDto dto) => dto switch
{
AccessTypeDto.AllPairs => "All Pairs",
AccessTypeDto.ClosePairs => "Direct Pairs",
AccessTypeDto.Individuals => "Specified",
AccessTypeDto.Public => "Everyone"
};
private static string GetShareTypeString(ShareTypeDto dto) => dto switch
{
ShareTypeDto.Private => "Code Only",
ShareTypeDto.Shared => "Shared"
};
private static string GetShareTypeString(ShareTypeDto dto) => dto switch
{
ShareTypeDto.Private => "Code Only",
ShareTypeDto.Shared => "Shared"
};
private static string GetWorldDataTooltipText(PoseEntryExtended poseEntry)
{
if (!poseEntry.HasWorldData) return "This Pose has no world data attached.";
return poseEntry.WorldDataDescriptor;
}
private static string GetWorldDataTooltipText(PoseEntryExtended poseEntry)
{
if (!poseEntry.HasWorldData) return "This Pose has no world data attached.";
return poseEntry.WorldDataDescriptor;
}
private void GposeMetaInfoAction(Action<CharaDataMetaInfoExtendedDto?> gposeActionDraw, string actionDescription, CharaDataMetaInfoExtendedDto? dto, bool hasValidGposeTarget, bool isSpawning)
{
StringBuilder sb = new StringBuilder();
private void GposeMetaInfoAction(Action<CharaDataMetaInfoExtendedDto?> gposeActionDraw, string actionDescription, CharaDataMetaInfoExtendedDto? dto, bool hasValidGposeTarget, bool isSpawning)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(actionDescription);
bool isDisabled = false;
sb.AppendLine(actionDescription);
bool isDisabled = false;
void AddErrorStart(StringBuilder sb)
{
sb.Append(UiSharedService.TooltipSeparator);
sb.AppendLine("Cannot execute:");
}
void AddErrorStart(StringBuilder sb)
{
sb.Append(UiSharedService.TooltipSeparator);
sb.AppendLine("Cannot execute:");
}
if (dto == null)
{
if (!isDisabled) AddErrorStart(sb);
sb.AppendLine("- No metainfo present");
isDisabled = true;
}
if (!dto?.CanBeDownloaded ?? false)
{
if (!isDisabled) AddErrorStart(sb);
sb.AppendLine("- Character is not downloadable");
isDisabled = true;
}
if (!_uiSharedService.IsInGpose)
{
if (!isDisabled) AddErrorStart(sb);
sb.AppendLine("- Requires to be in GPose");
isDisabled = true;
}
if (!hasValidGposeTarget && !isSpawning)
{
if (!isDisabled) AddErrorStart(sb);
sb.AppendLine("- Requires a valid GPose target");
isDisabled = true;
}
if (isSpawning && !_charaDataManager.BrioAvailable)
{
if (!isDisabled) AddErrorStart(sb);
sb.AppendLine("- Requires Brio to be installed.");
isDisabled = true;
}
if (dto == null)
{
if (!isDisabled) AddErrorStart(sb);
sb.AppendLine("- No metainfo present");
isDisabled = true;
}
if (!dto?.CanBeDownloaded ?? false)
{
if (!isDisabled) AddErrorStart(sb);
sb.AppendLine("- Character is not downloadable");
isDisabled = true;
}
if (!_uiSharedService.IsInGpose)
{
if (!isDisabled) AddErrorStart(sb);
sb.AppendLine("- Requires to be in GPose");
isDisabled = true;
}
if (!hasValidGposeTarget && !isSpawning)
{
if (!isDisabled) AddErrorStart(sb);
sb.AppendLine("- Requires a valid GPose target");
isDisabled = true;
}
if (isSpawning && !_charaDataManager.BrioAvailable)
{
if (!isDisabled) AddErrorStart(sb);
sb.AppendLine("- Requires Brio to be installed.");
isDisabled = true;
}
using (ImRaii.Group())
{
using var dis = ImRaii.Disabled(isDisabled);
gposeActionDraw.Invoke(dto);
}
if (sb.Length > 0)
{
UiSharedService.AttachToolTip(sb.ToString());
}
}
using (ImRaii.Group())
{
using var dis = ImRaii.Disabled(isDisabled);
gposeActionDraw.Invoke(dto);
}
if (sb.Length > 0)
{
UiSharedService.AttachToolTip(sb.ToString());
}
}
private void GposePoseAction(Action poseActionDraw, string poseDescription, bool hasValidGposeTarget)
{
StringBuilder sb = new StringBuilder();
private void GposePoseAction(Action poseActionDraw, string poseDescription, bool hasValidGposeTarget)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(poseDescription);
bool isDisabled = false;
sb.AppendLine(poseDescription);
bool isDisabled = false;
void AddErrorStart(StringBuilder sb)
{
sb.Append(UiSharedService.TooltipSeparator);
sb.AppendLine("Cannot execute:");
}
void AddErrorStart(StringBuilder sb)
{
sb.Append(UiSharedService.TooltipSeparator);
sb.AppendLine("Cannot execute:");
}
if (!_uiSharedService.IsInGpose)
{
if (!isDisabled) AddErrorStart(sb);
sb.AppendLine("- Requires to be in GPose");
isDisabled = true;
}
if (!hasValidGposeTarget)
{
if (!isDisabled) AddErrorStart(sb);
sb.AppendLine("- Requires a valid GPose target");
isDisabled = true;
}
if (!_charaDataManager.BrioAvailable)
{
if (!isDisabled) AddErrorStart(sb);
sb.AppendLine("- Requires Brio to be installed.");
isDisabled = true;
}
if (!_uiSharedService.IsInGpose)
{
if (!isDisabled) AddErrorStart(sb);
sb.AppendLine("- Requires to be in GPose");
isDisabled = true;
}
if (!hasValidGposeTarget)
{
if (!isDisabled) AddErrorStart(sb);
sb.AppendLine("- Requires a valid GPose target");
isDisabled = true;
}
if (!_charaDataManager.BrioAvailable)
{
if (!isDisabled) AddErrorStart(sb);
sb.AppendLine("- Requires Brio to be installed.");
isDisabled = true;
}
using (ImRaii.Group())
{
using var dis = ImRaii.Disabled(isDisabled);
poseActionDraw.Invoke();
}
if (sb.Length > 0)
{
UiSharedService.AttachToolTip(sb.ToString());
}
}
using (ImRaii.Group())
{
using var dis = ImRaii.Disabled(isDisabled);
poseActionDraw.Invoke();
}
if (sb.Length > 0)
{
UiSharedService.AttachToolTip(sb.ToString());
}
}
private void SetWindowSizeConstraints(bool? inGposeTab = null)
{
SizeConstraints = new()
{
MinimumSize = new((inGposeTab ?? false) ? 400 : 1000, 500),
MaximumSize = new((inGposeTab ?? false) ? 400 : 1000, 2000)
};
}
private void SetWindowSizeConstraints(bool? inGposeTab = null)
{
SizeConstraints = new()
{
MinimumSize = new((inGposeTab ?? false) ? 400 : 1000, 500),
MaximumSize = new((inGposeTab ?? false) ? 400 : 1000, 2000)
};
}
private void UpdateFilteredFavorites()
{
_ = Task.Run(async () =>
{
if (_charaDataManager.DownloadMetaInfoTask != null)
{
await _charaDataManager.DownloadMetaInfoTask.ConfigureAwait(false);
}
Dictionary<string, (CharaDataFavorite, CharaDataMetaInfoExtendedDto?, bool)> newFiltered = [];
foreach (var favorite in _configService.Current.FavoriteCodes)
{
var uid = favorite.Key.Split(":")[0];
var note = _serverConfigurationManager.GetNoteForUid(uid) ?? string.Empty;
bool hasMetaInfo = _charaDataManager.TryGetMetaInfo(favorite.Key, out var metaInfo);
bool addFavorite =
(string.IsNullOrEmpty(_filterCodeNote)
|| (note.Contains(_filterCodeNote, StringComparison.OrdinalIgnoreCase)
|| uid.Contains(_filterCodeNote, StringComparison.OrdinalIgnoreCase)))
&& (string.IsNullOrEmpty(_filterDescription)
|| (favorite.Value.CustomDescription.Contains(_filterDescription, StringComparison.OrdinalIgnoreCase)
|| (metaInfo != null && metaInfo!.Description.Contains(_filterDescription, StringComparison.OrdinalIgnoreCase))))
&& (!_filterPoseOnly
|| (metaInfo != null && metaInfo!.HasPoses))
&& (!_filterWorldOnly
|| (metaInfo != null && metaInfo!.HasWorldData));
if (addFavorite)
{
newFiltered[favorite.Key] = (favorite.Value, metaInfo, hasMetaInfo);
}
}
private void UpdateFilteredFavorites()
{
_ = Task.Run(async () =>
{
if (_charaDataManager.DownloadMetaInfoTask != null)
{
await _charaDataManager.DownloadMetaInfoTask.ConfigureAwait(false);
}
Dictionary<string, (CharaDataFavorite, CharaDataMetaInfoExtendedDto?, bool)> newFiltered = [];
foreach (var favorite in _configService.Current.FavoriteCodes)
{
var uid = favorite.Key.Split(":")[0];
var note = _serverConfigurationManager.GetNoteForUid(uid) ?? string.Empty;
bool hasMetaInfo = _charaDataManager.TryGetMetaInfo(favorite.Key, out var metaInfo);
bool addFavorite =
(string.IsNullOrEmpty(_filterCodeNote)
|| (note.Contains(_filterCodeNote, StringComparison.OrdinalIgnoreCase)
|| uid.Contains(_filterCodeNote, StringComparison.OrdinalIgnoreCase)))
&& (string.IsNullOrEmpty(_filterDescription)
|| (favorite.Value.CustomDescription.Contains(_filterDescription, StringComparison.OrdinalIgnoreCase)
|| (metaInfo != null && metaInfo!.Description.Contains(_filterDescription, StringComparison.OrdinalIgnoreCase))))
&& (!_filterPoseOnly
|| (metaInfo != null && metaInfo!.HasPoses))
&& (!_filterWorldOnly
|| (metaInfo != null && metaInfo!.HasWorldData));
if (addFavorite)
{
newFiltered[favorite.Key] = (favorite.Value, metaInfo, hasMetaInfo);
}
}
_filteredFavorites = newFiltered;
});
}
_filteredFavorites = newFiltered;
});
}
private void UpdateFilteredItems()
{
if (_charaDataManager.GetSharedWithYouTask == null)
{
_filteredDict = _charaDataManager.SharedWithYouData
.SelectMany(k => k.Value)
.Where(k =>
(!_sharedWithYouDownloadableFilter || k.CanBeDownloaded)
&& (string.IsNullOrEmpty(_sharedWithYouDescriptionFilter) || k.Description.Contains(_sharedWithYouDescriptionFilter, StringComparison.OrdinalIgnoreCase)))
.GroupBy(k => k.Uploader)
.ToDictionary(k =>
{
var note = _serverConfigurationManager.GetNoteForUid(k.Key.UID);
if (note == null) return k.Key.AliasOrUID;
return $"{note} ({k.Key.AliasOrUID})";
}, k => k.ToList(), StringComparer.OrdinalIgnoreCase)
.Where(k => (string.IsNullOrEmpty(_sharedWithYouOwnerFilter) || k.Key.Contains(_sharedWithYouOwnerFilter, StringComparison.OrdinalIgnoreCase)))
.OrderBy(k => k.Key, StringComparer.OrdinalIgnoreCase).ToDictionary();
}
}
private void UpdateFilteredItems()
{
if (_charaDataManager.GetSharedWithYouTask == null)
{
_filteredDict = _charaDataManager.SharedWithYouData
.SelectMany(k => k.Value)
.Where(k =>
(!_sharedWithYouDownloadableFilter || k.CanBeDownloaded)
&& (string.IsNullOrEmpty(_sharedWithYouDescriptionFilter) || k.Description.Contains(_sharedWithYouDescriptionFilter, StringComparison.OrdinalIgnoreCase)))
.GroupBy(k => k.Uploader)
.ToDictionary(k =>
{
var note = _serverConfigurationManager.GetNoteForUid(k.Key.UID);
if (note == null) return k.Key.AliasOrUID;
return $"{note} ({k.Key.AliasOrUID})";
}, k => k.ToList(), StringComparer.OrdinalIgnoreCase)
.Where(k => (string.IsNullOrEmpty(_sharedWithYouOwnerFilter) || k.Key.Contains(_sharedWithYouOwnerFilter, StringComparison.OrdinalIgnoreCase)))
.OrderBy(k => k.Key, StringComparer.OrdinalIgnoreCase).ToDictionary();
}
}
}

View File

@@ -1,8 +1,8 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Utility;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using LightlessSync.API.Dto.CharaData;
using LightlessSync.Services.CharaData.Models;
using System.Numerics;

View File

@@ -1,8 +1,8 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Utility;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using System.Numerics;
namespace LightlessSync.UI;

View File

@@ -38,6 +38,7 @@ public class CompactUi : WindowMediatorSubscriberBase
private readonly PairManager _pairManager;
private readonly SelectTagForPairUi _selectGroupForPairUi;
private readonly SelectPairForTagUi _selectPairsForGroupUi;
private readonly RenameTagUi _renameTagUi;
private readonly IpcManager _ipcManager;
private readonly ServerConfigurationManager _serverManager;
private readonly TopTabMenu _tabMenu;
@@ -56,7 +57,7 @@ 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,
TagHandler tagHandler, DrawEntityFactory drawEntityFactory, SelectTagForPairUi selectTagForPairUi, SelectPairForTagUi selectPairForTagUi, RenameTagUi renameTagUi,
PerformanceCollectorService performanceCollectorService, IpcManager ipcManager)
: base(logger, mediator, "###LightlessSyncMainUI", performanceCollectorService)
{
@@ -70,6 +71,7 @@ public class CompactUi : WindowMediatorSubscriberBase
_drawEntityFactory = drawEntityFactory;
_selectGroupForPairUi = selectTagForPairUi;
_selectPairsForGroupUi = selectPairForTagUi;
_renameTagUi = renameTagUi;
_ipcManager = ipcManager;
_tabMenu = new TopTabMenu(Mediator, _apiController, _pairManager, _uiSharedService);
@@ -149,10 +151,10 @@ public class CompactUi : WindowMediatorSubscriberBase
var uidTextSize = ImGui.CalcTextSize(unsupported);
ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMax().X + ImGui.GetWindowContentRegionMin().X) / 2 - uidTextSize.X / 2);
ImGui.AlignTextToFramePadding();
ImGui.TextColored(ImGuiColors.DalamudRed, unsupported);
ImGui.TextColored(UIColors.Get("DimRed"), unsupported);
}
UiSharedService.ColorTextWrapped($"Your Lightless Sync installation is out of date, the current version is {ver.Major}.{ver.Minor}.{ver.Build}. " +
$"It is highly recommended to keep Lightless Sync up to date. Open /xlplugins and update the plugin.", ImGuiColors.DalamudRed);
$"It is highly recommended to keep Lightless Sync up to date. Open /xlplugins and update the plugin.", UIColors.Get("DimRed"));
}
if (!_ipcManager.Initialized)
@@ -164,12 +166,12 @@ public class CompactUi : WindowMediatorSubscriberBase
var uidTextSize = ImGui.CalcTextSize(unsupported);
ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMax().X + ImGui.GetWindowContentRegionMin().X) / 2 - uidTextSize.X / 2);
ImGui.AlignTextToFramePadding();
ImGui.TextColored(ImGuiColors.DalamudRed, unsupported);
ImGui.TextColored(UIColors.Get("DimRed"), unsupported);
}
var penumAvailable = _ipcManager.Penumbra.APIAvailable;
var glamAvailable = _ipcManager.Glamourer.APIAvailable;
UiSharedService.ColorTextWrapped($"One or more Plugins essential for Lightless operation are unavailable. Enable or update following plugins:", ImGuiColors.DalamudRed);
UiSharedService.ColorTextWrapped($"One or more Plugins essential for Lightless operation are unavailable. Enable or update following plugins:", UIColors.Get("DimRed"));
using var indent = ImRaii.PushIndent(10f);
if (!penumAvailable)
{
@@ -185,7 +187,7 @@ public class CompactUi : WindowMediatorSubscriberBase
}
using (ImRaii.PushId("header")) DrawUIDHeader();
ImGui.Separator();
_uiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 2f);
using (ImRaii.PushId("serverstatus")) DrawServerStatus();
ImGui.Separator();
@@ -198,6 +200,7 @@ public class CompactUi : WindowMediatorSubscriberBase
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();
}
@@ -284,7 +287,7 @@ public class CompactUi : WindowMediatorSubscriberBase
else
{
ImGui.AlignTextToFramePadding();
ImGui.TextColored(ImGuiColors.DalamudRed, "Not connected to any server");
ImGui.TextColored(UIColors.Get("DimRed"), "Not connected to any server");
}
if (printShard)
@@ -587,21 +590,21 @@ public class CompactUi : WindowMediatorSubscriberBase
{
return _apiController.ServerState switch
{
ServerState.Connecting => ImGuiColors.DalamudYellow,
ServerState.Reconnecting => ImGuiColors.DalamudRed,
ServerState.Connecting => UIColors.Get("LightlessYellow"),
ServerState.Reconnecting => UIColors.Get("DimRed"),
ServerState.Connected => UIColors.Get("LightlessPurple"),
ServerState.Disconnected => ImGuiColors.DalamudYellow,
ServerState.Disconnecting => ImGuiColors.DalamudYellow,
ServerState.Unauthorized => ImGuiColors.DalamudRed,
ServerState.VersionMisMatch => ImGuiColors.DalamudRed,
ServerState.Offline => ImGuiColors.DalamudRed,
ServerState.RateLimited => ImGuiColors.DalamudYellow,
ServerState.NoSecretKey => ImGuiColors.DalamudYellow,
ServerState.MultiChara => ImGuiColors.DalamudYellow,
ServerState.OAuthMisconfigured => ImGuiColors.DalamudRed,
ServerState.OAuthLoginTokenStale => ImGuiColors.DalamudRed,
ServerState.NoAutoLogon => ImGuiColors.DalamudYellow,
_ => ImGuiColors.DalamudRed
ServerState.Disconnected => UIColors.Get("LightlessYellow"),
ServerState.Disconnecting => UIColors.Get("LightlessYellow"),
ServerState.Unauthorized => UIColors.Get("DimRed"),
ServerState.VersionMisMatch => UIColors.Get("DimRed"),
ServerState.Offline => UIColors.Get("DimRed"),
ServerState.RateLimited => UIColors.Get("LightlessYellow"),
ServerState.NoSecretKey => UIColors.Get("LightlessYellow"),
ServerState.MultiChara => UIColors.Get("LightlessYellow"),
ServerState.OAuthMisconfigured => UIColors.Get("DimRed"),
ServerState.OAuthLoginTokenStale => UIColors.Get("DimRed"),
ServerState.NoAutoLogon => UIColors.Get("LightlessYellow"),
_ => UIColors.Get("DimRed")
};
}

View File

@@ -13,13 +13,15 @@ public class DrawFolderTag : DrawFolderBase
{
private readonly ApiController _apiController;
private readonly SelectPairForTagUi _selectPairForTagUi;
private readonly RenameTagUi _renameTagUi;
public DrawFolderTag(string id, IImmutableList<DrawUserPair> drawPairs, IImmutableList<Pair> allPairs,
TagHandler tagHandler, ApiController apiController, SelectPairForTagUi selectPairForTagUi, UiSharedService uiSharedService)
TagHandler tagHandler, ApiController apiController, SelectPairForTagUi selectPairForTagUi, RenameTagUi renameTagUi, UiSharedService uiSharedService)
: base(id, drawPairs, allPairs, tagHandler, uiSharedService)
{
_apiController = apiController;
_selectPairForTagUi = selectPairForTagUi;
_renameTagUi = renameTagUi;
}
protected override bool RenderIfEmpty => _id switch
@@ -100,12 +102,15 @@ public class DrawFolderTag : DrawFolderBase
protected override void DrawMenu(float menuWidth)
{
ImGui.TextUnformatted("Group Menu");
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Users, "Select Pairs", menuWidth, true))
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Users, "Select Pairs", menuWidth, isInPopup: true))
{
_selectPairForTagUi.Open(_id);
}
UiSharedService.AttachToolTip("Select Individual Pairs for this Pair Group");
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Pair Group", menuWidth, true) && UiSharedService.CtrlPressed())
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Edit, "Rename Pair Group", menuWidth, isInPopup: true))
{
_renameTagUi.Open(_id);
}
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Pair Group", menuWidth, isInPopup: true) && UiSharedService.CtrlPressed())
{
_tagHandler.RemoveTag(_id);
}

View File

@@ -0,0 +1,93 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.UI.Handlers;
using System.Numerics;
namespace LightlessSync.UI.Components;
public class RenameTagUi
{
private readonly TagHandler _tagHandler;
private readonly UiSharedService _uiSharedService;
private string _desiredName = string.Empty;
private bool _opened = false;
private HashSet<string> _peopleInGroup = new(StringComparer.Ordinal);
private bool _show = false;
private string _tag = string.Empty;
public RenameTagUi(TagHandler tagHandler, UiSharedService uiSharedService)
{
_tagHandler = tagHandler;
_uiSharedService = uiSharedService;
}
public void Draw(List<Pair> pairs)
{
var workHeight = ImGui.GetMainViewport().WorkSize.Y / ImGuiHelpers.GlobalScale;
var minSize = new Vector2(300, workHeight < 110 ? workHeight : 110) * ImGuiHelpers.GlobalScale;
var maxSize = new Vector2(300, 110) * ImGuiHelpers.GlobalScale;
var popupName = $"Renaming Group {_tag}";
if (!_show)
{
_opened = false;
}
if (_show && !_opened)
{
ImGui.SetNextWindowSize(minSize);
UiSharedService.CenterNextWindow(minSize.X, minSize.Y, ImGuiCond.Always);
ImGui.OpenPopup(popupName);
_opened = true;
}
ImGui.SetNextWindowSizeConstraints(minSize, maxSize);
if (ImGui.BeginPopupModal(popupName, ref _show, ImGuiWindowFlags.Popup | ImGuiWindowFlags.Modal))
{
ImGui.TextUnformatted($"Renaming {_tag}");
ImGui.InputTextWithHint("##desiredname", "Enter new group name", ref _desiredName, 255, ImGuiInputTextFlags.None);
using (ImRaii.Disabled(string.IsNullOrEmpty(_desiredName)))
{
if (_uiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.Plus, "Rename Group"))
{
RenameTag(pairs, _tag, _desiredName);
_show = false;
}
}
ImGui.EndPopup();
}
else
{
_show = false;
}
}
public void Open(string tag)
{
_peopleInGroup = _tagHandler.GetOtherUidsForTag(tag);
_tag = tag;
_desiredName = "";
_show = true;
}
public void RenameTag(List<Pair> pairs, string oldTag, string newTag)
{
//Removal of old tag
_tagHandler.RemoveTag(oldTag);
//Creation of new tag and adding of old group pairs in new one.
_tagHandler.AddTag(newTag);
foreach (Pair pair in pairs)
{
var isInTag = _peopleInGroup.Contains(pair.UserData.UID);
if (isInTag)
{
_tagHandler.AddTagToPairedUid(pair.UserData.UID, newTag);
}
}
}
}

View File

@@ -169,7 +169,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
var dlProgressPercent = transferredBytes / (double)totalBytes;
drawList.AddRectFilled(dlBarStart,
dlBarEnd with { X = dlBarStart.X + (float)(dlProgressPercent * dlBarWidth) },
UiSharedService.Color(50, 205, 50, transparency), 1);
UiSharedService.Color(173, 138, 245, transparency), 1);
if (_configService.Current.TransferBarsShowText)
{

View File

@@ -23,11 +23,12 @@ public class DrawEntityFactory
private readonly PlayerPerformanceConfigService _playerPerformanceConfigService;
private readonly CharaDataManager _charaDataManager;
private readonly SelectTagForPairUi _selectTagForPairUi;
private readonly RenameTagUi _renameTagUi;
private readonly TagHandler _tagHandler;
private readonly IdDisplayHandler _uidDisplayHandler;
public DrawEntityFactory(ILogger<DrawEntityFactory> logger, ApiController apiController, IdDisplayHandler uidDisplayHandler,
SelectTagForPairUi selectTagForPairUi, LightlessMediator mediator,
SelectTagForPairUi selectTagForPairUi, RenameTagUi renameTagUi, LightlessMediator mediator,
TagHandler tagHandler, SelectPairForTagUi selectPairForTagUi,
ServerConfigurationManager serverConfigurationManager, UiSharedService uiSharedService,
PlayerPerformanceConfigService playerPerformanceConfigService, CharaDataManager charaDataManager)
@@ -36,6 +37,7 @@ public class DrawEntityFactory
_apiController = apiController;
_uidDisplayHandler = uidDisplayHandler;
_selectTagForPairUi = selectTagForPairUi;
_renameTagUi = renameTagUi;
_mediator = mediator;
_tagHandler = tagHandler;
_selectPairForTagUi = selectPairForTagUi;
@@ -58,8 +60,8 @@ public class DrawEntityFactory
Dictionary<Pair, List<GroupFullInfoDto>> filteredPairs,
IImmutableList<Pair> allPairs)
{
return new(tag, filteredPairs.Select(u => CreateDrawPair(tag, u.Key, u.Value, null)).ToImmutableList(),
allPairs, _tagHandler, _apiController, _selectPairForTagUi, _uiSharedService);
return new(tag, filteredPairs.Select(u => CreateDrawPair(tag, u.Key, u.Value, currentGroup: null)).ToImmutableList(),
allPairs, _tagHandler, _apiController, _selectPairForTagUi, _renameTagUi, _uiSharedService);
}
public DrawUserPair CreateDrawPair(string id, Pair user, List<GroupFullInfoDto> groups, GroupFullInfoDto? currentGroup)

View File

@@ -4,9 +4,9 @@ using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Utility;
using LightlessSync.FileCache;
using LightlessSync.Localization;
using LightlessSync.LightlessConfiguration;
using LightlessSync.LightlessConfiguration.Models;
using LightlessSync.Localization;
using LightlessSync.Services;
using LightlessSync.Services.Mediator;
using LightlessSync.Services.ServerConfiguration;

View File

@@ -25,7 +25,7 @@ internal class JoinSyncshellUI : WindowMediatorSubscriberBase
private string _syncshellPassword = string.Empty;
public JoinSyncshellUI(ILogger<JoinSyncshellUI> logger, LightlessMediator mediator,
UiSharedService uiSharedService, ApiController apiController, PerformanceCollectorService performanceCollectorService)
UiSharedService uiSharedService, ApiController apiController, PerformanceCollectorService performanceCollectorService)
: base(logger, mediator, "Join existing Syncshell###LightlessSyncJoinSyncshell", performanceCollectorService)
{
_uiSharedService = uiSharedService;

View File

@@ -102,7 +102,7 @@ public class PermissionWindowUI : WindowMediatorSubscriberBase
{
_ownPermissions.SetDisableAnimations(disableAnimations);
}
_uiSharedService.DrawHelpText("Disabling sounds will remove all animations synced with this user on both sides." + UiSharedService.TooltipSeparator
_uiSharedService.DrawHelpText("Disabling animations will remove all animations synced with this user on both sides." + UiSharedService.TooltipSeparator
+ "Note: this is bidirectional, either user disabling animation sync will stop animation sync on both sides.");
using (ImRaii.PushIndent(indentSize, false))
{
@@ -116,7 +116,7 @@ public class PermissionWindowUI : WindowMediatorSubscriberBase
{
_ownPermissions.SetDisableVFX(disableVfx);
}
_uiSharedService.DrawHelpText("Disabling sounds will remove all VFX synced with this user on both sides." + UiSharedService.TooltipSeparator
_uiSharedService.DrawHelpText("Disabling VFX will remove all VFX synced with this user on both sides." + UiSharedService.TooltipSeparator
+ "Note: this is bidirectional, either user disabling VFX sync will stop VFX sync on both sides.");
using (ImRaii.PushIndent(indentSize, false))
{

File diff suppressed because it is too large Load Diff

View File

@@ -8,9 +8,10 @@ namespace LightlessSync.UI
private static readonly Dictionary<string, string> HexColors = new(StringComparer.OrdinalIgnoreCase)
{
{ "LightlessPurple", "#ad8af5" },
{ "LightlessBlue", "#64c7e8" },
{ "PairBlue", "#4e98b1" },
{ "DimRed", "#bd0000" },
{ "LightlessBlue", "#a6c2ff" },
{ "LightlessYellow", "#ffe97a" },
{ "PairBlue", "#88a2db" },
{ "DimRed", "#d44444" },
};
public static Vector4 Get(string name)

View File

@@ -12,9 +12,9 @@ using Dalamud.Plugin.Services;
using Dalamud.Utility;
using LightlessSync.FileCache;
using LightlessSync.Interop.Ipc;
using LightlessSync.Localization;
using LightlessSync.LightlessConfiguration;
using LightlessSync.LightlessConfiguration.Models;
using LightlessSync.Localization;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services;
using LightlessSync.Services.Mediator;
@@ -114,6 +114,15 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
SizePx = 35
}));
});
MediumFont = _pluginInterface.UiBuilder.FontAtlas.NewDelegateFontHandle(e =>
{
e.OnPreBuild(tk => tk.AddDalamudAssetFont(Dalamud.DalamudAsset.NotoSansJpMedium, new()
{
SizePx = 22,
}));
});
GameFont = _pluginInterface.UiBuilder.FontAtlas.NewGameFontHandle(new(GameFontFamilyAndSize.Axis12));
IconFont = _pluginInterface.UiBuilder.IconFontFixedWidthHandle;
}
@@ -133,6 +142,8 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
public string PlayerName => _dalamudUtil.GetPlayerName();
public IFontHandle UidFont { get; init; }
public IFontHandle MediumFont { get; init; }
public Dictionary<ushort, string> WorldData => _dalamudUtil.WorldData.Value;
public uint WorldId => _dalamudUtil.GetHomeWorldId();
@@ -434,6 +445,79 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
FontText(text, UidFont, color);
}
public void UnderlinedBigText(
string text,
Vector4? textColor = null,
Vector4? underlineColor = null,
float underlineThickness = 2f)
{
using var font = UidFont.Push();
var drawList = ImGui.GetWindowDrawList();
var textSize = ImGui.CalcTextSize(text);
var pos = ImGui.GetCursorScreenPos();
var text_color = textColor ?? ImGuiColors.DalamudWhite;
var line_color = underlineColor ?? text_color;
ImGui.PushStyleColor(ImGuiCol.Text, text_color);
ImGui.TextUnformatted(text);
ImGui.PopStyleColor();
var lineY = pos.Y + textSize.Y + 2f;
drawList.AddLine(
new Vector2(pos.X, lineY),
new Vector2(pos.X + textSize.X, lineY),
ImGui.GetColorU32(line_color),
underlineThickness * ImGuiHelpers.GlobalScale
);
}
public void ColoredSeparator(Vector4? color = null, float thickness = 1f, float indent = 0f)
{
var drawList = ImGui.GetWindowDrawList();
var min = ImGui.GetCursorScreenPos();
var max = new Vector2(min.X + ImGui.GetContentRegionAvail().X, min.Y);
min.X += indent;
max.X -= indent;
drawList.AddLine(
min,
new Vector2(max.X, min.Y),
ImGui.GetColorU32(color ?? ImGuiColors.DalamudGrey),
thickness * ImGuiHelpers.GlobalScale
);
ImGui.Dummy(new Vector2(0, thickness * ImGuiHelpers.GlobalScale));
}
public void MediumText(string text, Vector4? color = null)
{
FontText(text, MediumFont, color);
}
public bool MediumTreeNode(string label, Vector4? textColor = null, float lineWidth = 2f, ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags.SpanAvailWidth)
{
using var font = MediumFont.Push();
var lineColor = textColor ?? ImGuiColors.DalamudWhite;
var textSize = ImGui.CalcTextSize(label);
var cursorScreen = ImGui.GetCursorScreenPos();
var cursorLocal = ImGui.GetCursorPos();
ImGui.GetWindowDrawList().AddLine(
new Vector2(cursorScreen.X, cursorScreen.Y),
new Vector2(cursorScreen.X, cursorScreen.Y + textSize.Y),
ImGui.GetColorU32(lineColor),
lineWidth * ImGuiHelpers.GlobalScale
);
ImGui.SetCursorPosX(cursorLocal.X + 6f);
using var color = ImRaii.PushColor(ImGuiCol.Text, lineColor);
return ImGui.TreeNodeEx(label, flags);
}
public void BooleanToColoredIcon(bool value, bool inline = true)
{
using var colorgreen = ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("LightlessBlue"), value);
@@ -1085,6 +1169,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
UidFont.Dispose();
GameFont.Dispose();
MediumFont.Dispose();
}
private static void CenterWindow(float width, float height, ImGuiCond cond = ImGuiCond.None)

View File

@@ -8,7 +8,6 @@ using LightlessSync.LightlessConfiguration.Models;
using LightlessSync.Services.Mediator;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.Logging;
using static FFXIVClientStructs.FFXIV.Client.Game.UI.MapMarkerData.Delegates;
namespace LightlessSync.WebAPI;

View File

@@ -1,6 +1,5 @@
using LightlessSync.API.Data;
using LightlessSync.API.Dto.CharaData;
using LightlessSync.Services.CharaData.Models;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.Logging;