28 Commits
0.2.1 ... 0.2.4

Author SHA1 Message Date
b4cad9c745 Version bump
Some checks failed
.NET Build and Publish to Gitea / build (push) Has been cancelled
2025-09-09 22:03:05 +01:00
100c5612dc Fix VRAM sort being a little funky. And in the wrong order. 2025-09-09 21:46:42 +01:00
b8e0100bdf Merge pull request #20 from ProfessorFartsalot/main
Colour changes, including our own colour library!
2025-09-09 21:38:10 +01:00
137db42446 Colour changes, including our own colour library!
+ Also began work on namespace changes! Will need to be merged with any current pending namespace changes.
2025-09-08 14:48:11 -04:00
956d2009eb Merge pull request #19 from ProfessorFartsalot/main
Multiple UI improvements and convenience things
2025-09-06 15:11:10 +01:00
ec66da8b5e Merge pull request #18 from BoxuChan/main
Code Optimizations for SignalR/ApiController
2025-09-06 15:08:24 +01:00
0555ee0db7 Clean up unused imports
Not sure how these got put in, but they are not used nor needed.
2025-09-05 17:34:15 -04:00
837b487281 Several UI enhancements
+ Added paused user field to main UI
+ Excluded paused users from online list
+ Excluded visible users from online list
+ Fixed an issue where remotely paused users would always show offline
+ Ensured paused users returns true if EITHER client OR remote paused them.
+ Made paused users show as Grey
+ Changed paired icon from check to snowflake
+ Fixed a bug where clients would have icons for both paired AND visible simultaneously
+ Fixed a bug where clients would show both online AND visible unnecessarily.
+ Made paused users render just above offline users.
+ Changed default ui icon from SS to Snowflake/Flower thingy.
2025-09-05 17:31:45 -04:00
9a0f2c062c Add /snow as possible command
Due to popular request, I've added /snow as a possible command.
2025-09-05 13:39:25 -04:00
BoxuChan
6e9c69c2a8 Code Optimizations for SignalR/ApiController 2025-09-05 17:47:02 +02:00
f0cef81b5e Merge pull request #17 from ProfessorFartsalot/main
Some checks failed
.NET Build and Publish to Gitea / build (push) Has been cancelled
Fix health check, reconnect automatically on error
2025-09-05 06:12:43 +01:00
d8160effd0 Fix up client reconnect code
Simplified the reconnect code so that redis is made aware that we have a new connection.

Made the reconnect fire immediately on error rather than waiting for subsequent errors.
2025-09-04 21:50:47 -04:00
18da07763c Add in extra check to see if hub server DC'd 2025-09-04 18:41:44 -04:00
3c448f2290 Fix health check, reconnect automatically on error
+ Made health check actually do something instead of just logging connection health issues.

+ Requires server code change to enable the health check so it's not just returning false all the time.

+ Forcibly reconnects the client when they've had connection issues.
2025-09-04 17:18:26 -04:00
cadc7e223f minor fixes 2025-09-04 14:26:15 +01:00
e1baca7940 Version bump
Some checks failed
.NET Build and Publish to Gitea / build (push) Has been cancelled
2025-09-03 16:11:51 +01:00
c2e0cf65a8 Packages update 2025-09-03 16:07:00 +01:00
a3d0408d6f More sensible VRAM sorting thing
Some checks failed
.NET Build and Publish to Gitea / build (push) Has been cancelled
2025-09-03 16:01:51 +01:00
9ca6931bb8 Fixed syncshell UI messing up if there were members you didn't have an individual pair with
Some checks failed
.NET Build and Publish to Gitea / build (push) Has been cancelled
2025-09-03 15:41:47 +01:00
f22eff1f72 Pause users in syncshells (prototype) 2025-09-03 14:20:43 +01:00
11e097d696 Sort syncshells by VRAM usage 2025-09-03 12:53:21 +01:00
f7ecb45774 Logo, by Wasa Bee.
Some checks failed
.NET Build and Publish to Gitea / build (push) Has been cancelled
2025-09-02 11:18:27 +01:00
00a7328e3a Merge pull request 'Multiple aesthetic changes with the profile editor ui and connection error on duplicate UIDs.' (#2) from ProfessorFartsalot/SnowcloakClient:main into main
Some checks failed
.NET Build and Publish to Gitea / build (push) Has been cancelled
Reviewed-on: #2
Reviewed-by: Eauldane <elf@eauldane.com>
2025-09-02 02:09:39 +00:00
c39229a28b Merge branch 'main' into main
Some checks failed
.NET Build and Publish to Gitea / build (pull_request) Has been cancelled
2025-09-02 02:09:04 +00:00
00c56b0888 Merge branch 'main' of https://imbuilding.anuke.org/ProfessorFartsalot/SnowcloakClient
Some checks failed
.NET Build and Publish to Gitea / build (pull_request) Has been cancelled
2025-09-01 16:12:06 -04:00
15898d54c2 Update color for online member count 2025-09-01 16:11:45 -04:00
92a8e224ef merge upstream
Some checks failed
.NET Build and Publish to Gitea / build (pull_request) Has been cancelled
2025-09-01 15:54:40 +00:00
669ceed2a9 More aesthetic changes
+ Added rules and guidelines to the profile editor
+ Adjusted the title of the profile editor to be more aesthetically pleasing
+ Added a way to detect if the image dimensions were too big, or if the file size was just too large.
+ Fixed connection error message on duplicate UIDs still saying 'Mare'.
2025-09-01 11:54:02 -04:00
21 changed files with 277 additions and 113 deletions

View File

@@ -12,6 +12,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.editorconfig = .editorconfig .editorconfig = .editorconfig
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SnowcloakSync", "SnowcloakSync\SnowcloakSync.csproj", "{E633A968-2FB8-48FF-8136-5EBAEDF8E6F3}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -36,6 +38,14 @@ Global
{5A0B7434-8D89-4E90-B55C-B4A7AE1A6ADE}.Release|Any CPU.Build.0 = Release|Any CPU {5A0B7434-8D89-4E90-B55C-B4A7AE1A6ADE}.Release|Any CPU.Build.0 = Release|Any CPU
{5A0B7434-8D89-4E90-B55C-B4A7AE1A6ADE}.Release|x64.ActiveCfg = Release|Any CPU {5A0B7434-8D89-4E90-B55C-B4A7AE1A6ADE}.Release|x64.ActiveCfg = Release|Any CPU
{5A0B7434-8D89-4E90-B55C-B4A7AE1A6ADE}.Release|x64.Build.0 = Release|Any CPU {5A0B7434-8D89-4E90-B55C-B4A7AE1A6ADE}.Release|x64.Build.0 = Release|Any CPU
{E633A968-2FB8-48FF-8136-5EBAEDF8E6F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E633A968-2FB8-48FF-8136-5EBAEDF8E6F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E633A968-2FB8-48FF-8136-5EBAEDF8E6F3}.Debug|x64.ActiveCfg = Debug|Any CPU
{E633A968-2FB8-48FF-8136-5EBAEDF8E6F3}.Debug|x64.Build.0 = Debug|Any CPU
{E633A968-2FB8-48FF-8136-5EBAEDF8E6F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E633A968-2FB8-48FF-8136-5EBAEDF8E6F3}.Release|Any CPU.Build.0 = Release|Any CPU
{E633A968-2FB8-48FF-8136-5EBAEDF8E6F3}.Release|x64.ActiveCfg = Release|Any CPU
{E633A968-2FB8-48FF-8136-5EBAEDF8E6F3}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -7,6 +7,7 @@ namespace MareSynchronos.MareConfiguration.Configurations;
[Serializable] [Serializable]
public class MareConfig : IMareConfiguration public class MareConfig : IMareConfiguration
{ {
public bool SortSyncshellsByVRAM { get; set; } = false;
public int ExpectedTOSVersion = 2; public int ExpectedTOSVersion = 2;
public int AcceptedTOSVersion { get; set; } = 0; public int AcceptedTOSVersion { get; set; } = 0;
public bool AcceptedAgreement { get; set; } = false; public bool AcceptedAgreement { get; set; } = false;

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Dalamud.NET.Sdk/13.0.0"> <Project Sdk="Dalamud.NET.Sdk/13.1.0">
<PropertyGroup> <PropertyGroup>
<AssemblyName>Snowcloak</AssemblyName> <AssemblyName>Snowcloak</AssemblyName>
<Version>0.2.1</Version> <Version>0.2.4</Version>
<PackageProjectUrl>https://github.com/Eauldane/SnowcloakClient/</PackageProjectUrl> <PackageProjectUrl>https://git.snowcloak-sync.com/Eauldane/SnowcloakClient/</PackageProjectUrl>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -14,10 +14,12 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Chaos.NaCl.Standard" Version="1.0.0" /> <PackageReference Include="Chaos.NaCl.Standard" Version="1.0.0" />
<PackageReference Include="Downloader" Version="3.3.4" /> <PackageReference Include="Downloader" Version="4.0.3" />
<PackageReference Include="K4os.Compression.LZ4.Legacy" Version="1.3.8" /> <PackageReference Include="K4os.Compression.LZ4.Legacy" Version="1.3.8" />
<PackageReference Include="K4os.Compression.LZ4.Streams" Version="1.3.8" /> <PackageReference Include="K4os.Compression.LZ4.Streams" Version="1.3.8" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.212"> <PackageReference Include="MessagePack" Version="3.1.4" />
<PackageReference Include="MessagePack.Annotations" Version="3.1.4" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.213">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
@@ -29,6 +31,8 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.14.0" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.14.0" />
<PackageReference Update="DalamudPackager" Version="13.1.0" />
<PackageReference Include="System.IO.Pipelines" Version="9.0.8" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="Exists('.\Penumbra.Api\Penumbra.Api.csproj')"> <ItemGroup Condition="Exists('.\Penumbra.Api\Penumbra.Api.csproj')">
@@ -54,6 +58,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\MareAPI\MareSynchronosAPI\MareSynchronos.API.csproj" /> <ProjectReference Include="..\MareAPI\MareSynchronosAPI\MareSynchronos.API.csproj" />
<ProjectReference Include="..\SnowcloakSync\SnowcloakSync.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -235,11 +235,17 @@ public class Pair : DisposableMediatorSubscriberBase
{ {
string? noteOrName = GetNoteOrName(); string? noteOrName = GetNoteOrName();
if (noteOrName != null) if (_mareConfig.Current.SortSyncshellsByVRAM)
{
return($"0{LastAppliedApproximateVRAMBytes}");
}
else if (noteOrName != null) {
return $"0{noteOrName}"; return $"0{noteOrName}";
else }
else {
return $"9{UserData.AliasOrUID}"; return $"9{UserData.AliasOrUID}";
} }
}
public string GetPlayerNameHash() public string GetPlayerNameHash()
{ {

View File

@@ -14,8 +14,9 @@ namespace MareSynchronos.Services;
public sealed class CommandManagerService : IDisposable public sealed class CommandManagerService : IDisposable
{ {
private const string _commandName = "/sync"; private const string _commandName = "/snow";
private const string _commandName2 = "/snowcloak"; private const string _commandName2 = "/snowcloak";
private const string _commandName3 = "/sync";
private const string _ssCommandPrefix = "/ss"; private const string _ssCommandPrefix = "/ss";
@@ -48,6 +49,10 @@ public sealed class CommandManagerService : IDisposable
{ {
HelpMessage = "Opens the Snowcloak UI" HelpMessage = "Opens the Snowcloak UI"
}); });
_commandManager.AddHandler(_commandName3, new CommandInfo(OnCommand)
{
HelpMessage = "Opens the Snowcloak UI"
});
// Lazy registration of all possible /ss# commands which tbf is what the game does for linkshells anyway // Lazy registration of all possible /ss# commands which tbf is what the game does for linkshells anyway
for (int i = 1; i <= ChatService.CommandMaxNumber; ++i) for (int i = 1; i <= ChatService.CommandMaxNumber; ++i)

View File

@@ -9,6 +9,6 @@
"customization" "customization"
], ],
"IconUrl": "https://raw.githubusercontent.com/Eauldane/SnowcloakClient/refs/heads/main/MareSynchronos/images/logo.png", "IconUrl": "https://raw.githubusercontent.com/Eauldane/SnowcloakClient/refs/heads/main/MareSynchronos/images/logo.png",
"RepoUrl": "https://github.com/Eauldane/SnowcloakClient", "RepoUrl": "https://git.snowcloak-sync.com/Eauldane/SnowcloakClient",
"CanUnloadAsync": true "CanUnloadAsync": true
} }

View File

@@ -1054,7 +1054,7 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase
_configService.Current.OpenMareHubOnGposeStart = openInGpose; _configService.Current.OpenMareHubOnGposeStart = openInGpose;
_configService.Save(); _configService.Save();
} }
_uiSharedService.DrawHelpText("This will automatically open the import menu when loading into Gpose. If unchecked you can open the menu manually with /sync gpose"); _uiSharedService.DrawHelpText("This will automatically open the import menu when loading into Gpose. If unchecked you can open the menu manually with /snow gpose");
bool downloadDataOnConnection = _configService.Current.DownloadMcdDataOnConnection; bool downloadDataOnConnection = _configService.Current.DownloadMcdDataOnConnection;
if (ImGui.Checkbox("Download Online Character Data on connecting", ref downloadDataOnConnection)) if (ImGui.Checkbox("Download Online Character Data on connecting", ref downloadDataOnConnection))
{ {

View File

@@ -105,7 +105,7 @@ public class CompactUi : WindowMediatorSubscriberBase
protected override void DrawInternal() protected override void DrawInternal()
{ {
if (_serverManager.CurrentApiUrl.Equals(ApiController.SnowcloakServiceUri, StringComparison.Ordinal)) if (_serverManager.CurrentApiUrl.Equals(ApiController.SnowcloakServiceUri, StringComparison.Ordinal))
UiSharedService.AccentColor = new(0.4275f, 0.6863f, 1f, 1f); UiSharedService.AccentColor = SnowcloakSync.Utils.Colours._snowcloakOnline;
else else
UiSharedService.AccentColor = ImGuiColors.ParsedGreen; UiSharedService.AccentColor = ImGuiColors.ParsedGreen;
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);
@@ -362,13 +362,14 @@ public class CompactUi : WindowMediatorSubscriberBase
: (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y) - TransferPartHeight - ImGui.GetCursorPosY(); : (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y) - TransferPartHeight - ImGui.GetCursorPosY();
var users = GetFilteredUsers().OrderBy(u => u.GetPairSortKey(), StringComparer.Ordinal); var users = GetFilteredUsers().OrderBy(u => u.GetPairSortKey(), StringComparer.Ordinal);
var onlineUsers = users.Where(u => u.UserPair!.OtherPermissions.IsPaired() && (u.IsOnline || u.UserPair!.OwnPermissions.IsPaused())).Select(c => new DrawUserPair("Online" + c.UserData.UID, c, _uidDisplayHandler, _apiController, Mediator, _selectGroupForPairUi, _uiSharedService, _charaDataManager)).ToList(); var onlineUsers = users.Where(u => u.UserPair!.OtherPermissions.IsPaired() && (u.IsOnline && !u.IsVisible && (!u.UserPair!.OtherPermissions.IsPaused() && !u.UserPair!.OwnPermissions.IsPaused()))).Select(c => new DrawUserPair("Online" + c.UserData.UID, c, _uidDisplayHandler, _apiController, Mediator, _selectGroupForPairUi, _uiSharedService, _charaDataManager)).ToList();
var pausedUsers = users.Where(u => u.UserPair!.OtherPermissions.IsPaired() && (u.UserPair!.OtherPermissions.IsPaused() || u.UserPair!.OwnPermissions.IsPaused())).Select(c => new DrawUserPair("Paused" + c.UserData.UID, c, _uidDisplayHandler, _apiController, Mediator, _selectGroupForPairUi, _uiSharedService, _charaDataManager)).ToList();
var visibleUsers = users.Where(u => u.IsVisible).Select(c => new DrawUserPair("Visible" + c.UserData.UID, c, _uidDisplayHandler, _apiController, Mediator, _selectGroupForPairUi, _uiSharedService, _charaDataManager)).ToList(); var visibleUsers = users.Where(u => u.IsVisible).Select(c => new DrawUserPair("Visible" + c.UserData.UID, c, _uidDisplayHandler, _apiController, Mediator, _selectGroupForPairUi, _uiSharedService, _charaDataManager)).ToList();
var offlineUsers = users.Where(u => !u.UserPair!.OtherPermissions.IsPaired() || (!u.IsOnline && !u.UserPair!.OwnPermissions.IsPaused())).Select(c => new DrawUserPair("Offline" + c.UserData.UID, c, _uidDisplayHandler, _apiController, Mediator, _selectGroupForPairUi, _uiSharedService, _charaDataManager)).ToList(); var offlineUsers = users.Where(u => !u.UserPair!.OtherPermissions.IsPaired() || !u.IsOnline && (!u.UserPair!.OwnPermissions.IsPaused() && !u.UserPair.OtherPermissions.IsPaused())).Select(c => new DrawUserPair("Offline" + c.UserData.UID, c, _uidDisplayHandler, _apiController, Mediator, _selectGroupForPairUi, _uiSharedService, _charaDataManager)).ToList();
ImGui.BeginChild("list", new Vector2(WindowContentWidth, ySize), border: false); ImGui.BeginChild("list", new Vector2(WindowContentWidth, ySize), border: false);
_pairGroupsUi.Draw(visibleUsers, onlineUsers, offlineUsers); _pairGroupsUi.Draw(visibleUsers, onlineUsers, pausedUsers, offlineUsers);
ImGui.EndChild(); ImGui.EndChild();
} }
@@ -387,7 +388,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.ParsedGreen, userCount); ImGui.TextColored(new Vector4(0.675f, 0.985f, 1f, 1f), userCount);
ImGui.SameLine(); ImGui.SameLine();
if (!printShard) ImGui.AlignTextToFramePadding(); if (!printShard) ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Users Online"); ImGui.TextUnformatted("Users Online");

View File

@@ -20,6 +20,7 @@ public class DrawGroupPair : DrawPairBase
private readonly GroupPairFullInfoDto _fullInfoDto; private readonly GroupPairFullInfoDto _fullInfoDto;
private readonly GroupFullInfoDto _group; private readonly GroupFullInfoDto _group;
private readonly CharaDataManager _charaDataManager; private readonly CharaDataManager _charaDataManager;
public long VRAMUsage { get; set; }
public DrawGroupPair(string id, Pair entry, ApiController apiController, public DrawGroupPair(string id, Pair entry, ApiController apiController,
MareMediator mareMediator, GroupFullInfoDto group, GroupPairFullInfoDto fullInfoDto, MareMediator mareMediator, GroupFullInfoDto group, GroupPairFullInfoDto fullInfoDto,
@@ -80,6 +81,7 @@ public class DrawGroupPair : DrawPairBase
} }
if (_pair.LastAppliedDataBytes >= 0) if (_pair.LastAppliedDataBytes >= 0)
{ {
presenceText += UiSharedService.TooltipSeparator; presenceText += UiSharedService.TooltipSeparator;
presenceText += ((!_pair.IsVisible) ? "(Last) " : string.Empty) + "Mods Info" + Environment.NewLine; presenceText += ((!_pair.IsVisible) ? "(Last) " : string.Empty) + "Mods Info" + Environment.NewLine;
presenceText += "Files Size: " + UiSharedService.ByteToString(_pair.LastAppliedDataBytes, true); presenceText += "Files Size: " + UiSharedService.ByteToString(_pair.LastAppliedDataBytes, true);
@@ -127,6 +129,9 @@ public class DrawGroupPair : DrawPairBase
protected override float DrawRightSide(float textPosY, float originalY) protected override float DrawRightSide(float textPosY, float originalY)
{ {
var pauseIcon = _pair.IsPaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
var pauseIconSize = _uiSharedService.GetIconButtonSize(pauseIcon);
var spacingX = ImGui.GetStyle().ItemSpacing.X;
var entryUID = _fullInfoDto.UserAliasOrUID; var entryUID = _fullInfoDto.UserAliasOrUID;
var entryIsMod = _fullInfoDto.GroupPairStatusInfo.IsModerator(); var entryIsMod = _fullInfoDto.GroupPairStatusInfo.IsModerator();
var entryIsOwner = string.Equals(_pair.UserData.UID, _group.OwnerUID, StringComparison.Ordinal); var entryIsOwner = string.Equals(_pair.UserData.UID, _group.OwnerUID, StringComparison.Ordinal);
@@ -145,19 +150,27 @@ public class DrawGroupPair : DrawPairBase
bool showInfo = (individualAnimDisabled || individualSoundsDisabled || animDisabled || soundsDisabled); bool showInfo = (individualAnimDisabled || individualSoundsDisabled || animDisabled || soundsDisabled);
bool showPlus = _pair.UserPair == null; bool showPlus = _pair.UserPair == null;
bool showBars = (userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner)) || !_pair.IsPaused; bool showBars = (userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner)) || !_pair.IsPaused;
bool showPause = true;
var spacing = ImGui.GetStyle().ItemSpacing.X; var spacing = ImGui.GetStyle().ItemSpacing.X;
var permIcon = (individualAnimDisabled || individualSoundsDisabled || individualVFXDisabled) ? FontAwesomeIcon.ExclamationTriangle var permIcon = (individualAnimDisabled || individualSoundsDisabled || individualVFXDisabled) ? FontAwesomeIcon.ExclamationTriangle
: ((soundsDisabled || animDisabled || vfxDisabled) ? FontAwesomeIcon.InfoCircle : FontAwesomeIcon.None); : ((soundsDisabled || animDisabled || vfxDisabled) ? FontAwesomeIcon.InfoCircle : FontAwesomeIcon.None);
var runningIconWidth = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Running).X; var runningIconWidth = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Running).X;
var infoIconWidth = UiSharedService.GetIconSize(permIcon).X; var infoIconWidth = UiSharedService.GetIconSize(permIcon).X;
var plusButtonWidth = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Plus).X; var plusButtonWidth = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Plus).X;
var pauseButtonWidth = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Plus).X;
var barButtonWidth = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars).X; var barButtonWidth = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars).X;
var barButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars);
var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth();
var rightSidePos = windowEndX - barButtonSize.X;
var pos = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() + spacing var pos = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() + spacing
- (showShared ? (runningIconWidth + spacing) : 0) - (showShared ? (runningIconWidth + spacing) : 0)
- (showInfo ? (infoIconWidth + spacing) : 0) - (showInfo ? (infoIconWidth + spacing) : 0)
- (showPlus ? (plusButtonWidth + spacing) : 0) - (showPlus ? (plusButtonWidth + spacing) : 0)
- (showPause ? (pauseButtonWidth + spacing) : 0)
- (showBars ? (barButtonWidth + spacing) : 0); - (showBars ? (barButtonWidth + spacing) : 0);
ImGui.SameLine(pos); ImGui.SameLine(pos);
@@ -264,6 +277,7 @@ public class DrawGroupPair : DrawPairBase
ImGui.SameLine(); ImGui.SameLine();
} }
if (showPlus) if (showPlus)
{ {
ImGui.SetCursorPosY(originalY); ImGui.SetCursorPosY(originalY);
@@ -275,7 +289,25 @@ public class DrawGroupPair : DrawPairBase
UiSharedService.AttachToolTip("Pair with " + entryUID + " individually"); UiSharedService.AttachToolTip("Pair with " + entryUID + " individually");
ImGui.SameLine(); ImGui.SameLine();
} }
if (showPause)
{
//rightSidePos -= pauseIconSize.X + spacingX;
ImGui.SetCursorPosY(originalY);
if (_uiSharedService.IconButton(pauseIcon))
{
var perm = _pair.UserPair!.OwnPermissions;
perm.SetPaused(!perm.IsPaused());
_ = _apiController.UserSetPairPermissions(new(_pair.UserData, perm));
}
UiSharedService.AttachToolTip(!_fullInfoDto.GroupUserPermissions.IsPaused()
? "Pause pairing with " + entryUID
: "Resume pairing with " + entryUID);
ImGui.SameLine();
}
if (showBars) if (showBars)
{ {
ImGui.SetCursorPosY(originalY); ImGui.SetCursorPosY(originalY);
@@ -285,7 +317,6 @@ public class DrawGroupPair : DrawPairBase
ImGui.OpenPopup("Popup"); ImGui.OpenPopup("Popup");
} }
} }
if (ImGui.BeginPopup("Popup")) if (ImGui.BeginPopup("Popup"))
{ {
if ((userIsModerator || userIsOwner) && !(entryIsMod || entryIsOwner)) if ((userIsModerator || userIsOwner) && !(entryIsMod || entryIsOwner))

View File

@@ -19,6 +19,7 @@ public class DrawUserPair : DrawPairBase
protected readonly MareMediator _mediator; protected readonly MareMediator _mediator;
private readonly SelectGroupForPairUi _selectGroupForPairUi; private readonly SelectGroupForPairUi _selectGroupForPairUi;
private readonly CharaDataManager _charaDataManager; private readonly CharaDataManager _charaDataManager;
public long VramUsage { get; set; }
public DrawUserPair(string id, Pair entry, UidDisplayHandler displayHandler, ApiController apiController, public DrawUserPair(string id, Pair entry, UidDisplayHandler displayHandler, ApiController apiController,
MareMediator mareMediator, SelectGroupForPairUi selectGroupForPairUi, MareMediator mareMediator, SelectGroupForPairUi selectGroupForPairUi,
@@ -51,26 +52,27 @@ public class DrawUserPair : DrawPairBase
{ {
connectionIcon = FontAwesomeIcon.PauseCircle; connectionIcon = FontAwesomeIcon.PauseCircle;
connectionText = "Pairing status with " + _pair.UserData.AliasOrUID + " is paused"; connectionText = "Pairing status with " + _pair.UserData.AliasOrUID + " is paused";
connectionColor = ImGuiColors.DalamudYellow; connectionColor = ImGuiColors.DalamudGrey;
} }
else else
{ {
connectionIcon = FontAwesomeIcon.Check; connectionIcon = FontAwesomeIcon.Snowflake;
connectionText = "You are paired with " + _pair.UserData.AliasOrUID; connectionText = "You are paired with " + _pair.UserData.AliasOrUID;
connectionColor = ImGuiColors.ParsedGreen; connectionColor = _pair.IsOnline ? SnowcloakSync.Utils.Colours._snowcloakOnline : ImGuiColors.DalamudGrey;
} }
if (!_pair.IsVisible)
ImGui.SetCursorPosY(textPosY);
ImGui.PushFont(UiBuilder.IconFont);
UiSharedService.ColorText(connectionIcon.ToIconString(), connectionColor);
ImGui.PopFont();
UiSharedService.AttachToolTip(connectionText);
if (_pair is { IsOnline: true, IsVisible: true })
{ {
ImGui.SameLine();
ImGui.SetCursorPosY(textPosY); ImGui.SetCursorPosY(textPosY);
ImGui.PushFont(UiBuilder.IconFont); ImGui.PushFont(UiBuilder.IconFont);
UiSharedService.ColorText(FontAwesomeIcon.Eye.ToIconString(), ImGuiColors.ParsedGreen); UiSharedService.ColorText(connectionIcon.ToIconString(), connectionColor);
ImGui.PopFont();
UiSharedService.AttachToolTip(connectionText);
}
if (_pair is { IsOnline: true, IsVisible: true })
{
ImGui.SetCursorPosY(textPosY);
ImGui.PushFont(UiBuilder.IconFont);
UiSharedService.ColorText(FontAwesomeIcon.Eye.ToIconString(), SnowcloakSync.Utils.Colours._snowcloakOnline);
if (ImGui.IsItemClicked()) if (ImGui.IsItemClicked())
{ {
_mediator.Publish(new TargetPairMessage(_pair)); _mediator.Publish(new TargetPairMessage(_pair));
@@ -84,6 +86,7 @@ public class DrawUserPair : DrawPairBase
visibleTooltip += "Files Size: " + UiSharedService.ByteToString(_pair.LastAppliedDataBytes, true); visibleTooltip += "Files Size: " + UiSharedService.ByteToString(_pair.LastAppliedDataBytes, true);
if (_pair.LastAppliedApproximateVRAMBytes >= 0) if (_pair.LastAppliedApproximateVRAMBytes >= 0)
{ {
VramUsage = _pair.LastAppliedApproximateVRAMBytes;
visibleTooltip += Environment.NewLine + "Approx. VRAM Usage: " + UiSharedService.ByteToString(_pair.LastAppliedApproximateVRAMBytes, true); visibleTooltip += Environment.NewLine + "Approx. VRAM Usage: " + UiSharedService.ByteToString(_pair.LastAppliedApproximateVRAMBytes, true);
} }
if (_pair.LastAppliedDataTris >= 0) if (_pair.LastAppliedDataTris >= 0)
@@ -97,6 +100,8 @@ public class DrawUserPair : DrawPairBase
} }
} }
protected override float DrawRightSide(float textPosY, float originalY) protected override float DrawRightSide(float textPosY, float originalY)
{ {
var pauseIcon = _pair.UserPair!.OwnPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; var pauseIcon = _pair.UserPair!.OwnPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;

View File

@@ -418,21 +418,34 @@ internal sealed class GroupPanel
ImGui.Indent(20); ImGui.Indent(20);
if (_expandedGroupState[groupDto.GID]) if (_expandedGroupState[groupDto.GID])
{ {
var sortedPairs = pairsInGroup IOrderedEnumerable<Pair> sortedPairs;
.OrderByDescending(u => string.Equals(u.UserData.UID, groupDto.OwnerUID, StringComparison.Ordinal)) if (!_mareConfig.Current.SortSyncshellsByVRAM)
.ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsModerator()) {
.ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned()) sortedPairs = pairsInGroup
.ThenBy(u => u.GetPairSortKey(), StringComparer.OrdinalIgnoreCase); .OrderByDescending(u => string.Equals(u.UserData.UID, groupDto.OwnerUID, StringComparison.Ordinal))
.ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsModerator())
.ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned())
.ThenBy(u => u.GetPairSortKey(), StringComparer.OrdinalIgnoreCase);
}
else
{
sortedPairs = pairsInGroup
.OrderByDescending(u => string.Equals(u.UserData.UID, groupDto.OwnerUID, StringComparison.Ordinal))
.ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsModerator())
.ThenByDescending(u => u.GroupPair[groupDto].GroupPairStatusInfo.IsPinned())
.ThenByDescending(u => u.LastAppliedApproximateVRAMBytes);
}
var visibleUsers = new List<DrawGroupPair>(); var visibleUsers = new List<DrawGroupPair>();
var onlineUsers = new List<DrawGroupPair>(); var onlineUsers = new List<DrawGroupPair>();
var offlineUsers = new List<DrawGroupPair>(); var offlineUsers = new List<DrawGroupPair>();
foreach (var pair in sortedPairs) foreach (var pair in sortedPairs)
{ {
var drawPair = new DrawGroupPair( var drawPair = new DrawGroupPair(
groupDto.GID + pair.UserData.UID, pair, groupDto.GID + pair.UserData.UID, pair,
ApiController, _mainUi.Mediator, groupDto, ApiController, _mainUi.Mediator, groupDto,
pair.GroupPair.Single( pair.GroupPair.Single(
g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group) g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)
).Value, ).Value,
@@ -453,6 +466,8 @@ internal sealed class GroupPanel
ImGui.TextUnformatted("Visible"); ImGui.TextUnformatted("Visible");
ImGui.Separator(); ImGui.Separator();
_uidDisplayHandler.RenderPairList(visibleUsers); _uidDisplayHandler.RenderPairList(visibleUsers);
} }
if (onlineUsers.Count > 0) if (onlineUsers.Count > 0)

View File

@@ -28,15 +28,15 @@ public class PairGroupsUi
_uiSharedService = uiSharedService; _uiSharedService = uiSharedService;
} }
public void Draw<T>(List<T> visibleUsers, List<T> onlineUsers, List<T> offlineUsers) where T : DrawPairBase public void Draw<T>(List<T> visibleUsers, List<T> onlineUsers, List<T> pausedUsers, List<T> offlineUsers) where T : DrawPairBase
{ {
// Only render those tags that actually have pairs in them, otherwise // Only render those tags that actually have pairs in them, otherwise
// we can end up with a bunch of useless pair groups // we can end up with a bunch of useless pair groups
var tagsWithPairsInThem = _tagHandler.GetAllTagsSorted(); var tagsWithPairsInThem = _tagHandler.GetAllTagsSorted();
var allUsers = onlineUsers.Concat(offlineUsers).ToList(); var allUsers = onlineUsers.Concat(offlineUsers).Concat(pausedUsers).ToList();
if (typeof(T) == typeof(DrawUserPair)) if (typeof(T) == typeof(DrawUserPair))
{ {
DrawUserPairs(tagsWithPairsInThem, allUsers.Cast<DrawUserPair>().ToList(), visibleUsers.Cast<DrawUserPair>(), onlineUsers.Cast<DrawUserPair>(), offlineUsers.Cast<DrawUserPair>()); DrawUserPairs(tagsWithPairsInThem, allUsers.Cast<DrawUserPair>().ToList(), visibleUsers.Cast<DrawUserPair>(), onlineUsers.Cast<DrawUserPair>(), pausedUsers.Cast<DrawUserPair>(), offlineUsers.Cast<DrawUserPair>());
} }
} }
@@ -91,13 +91,19 @@ public class PairGroupsUi
} }
} }
private void DrawCategory(string tag, IEnumerable<DrawPairBase> onlineUsers, IEnumerable<DrawPairBase> allUsers, IEnumerable<DrawPairBase>? visibleUsers = null) private void DrawCategory(string tag, IEnumerable<DrawPairBase> onlineUsers, IEnumerable<DrawPairBase> pausedUsers, IEnumerable<DrawPairBase> allUsers, IEnumerable<DrawPairBase>? visibleUsers = null)
{ {
IEnumerable<DrawPairBase> usersInThisTag; IEnumerable<DrawPairBase> usersInThisTag;
HashSet<string>? otherUidsTaggedWithTag = null; HashSet<string>? otherUidsTaggedWithTag = null;
bool isSpecialTag = false; bool isSpecialTag = false;
int visibleInThisTag = 0; int visibleInThisTag = 0;
if (tag is TagHandler.CustomOfflineTag or TagHandler.CustomOnlineTag or TagHandler.CustomVisibleTag or TagHandler.CustomUnpairedTag)
if (tag is TagHandler.CustomPausedTag)
{
usersInThisTag = pausedUsers;
isSpecialTag = true;
}
else if (tag is TagHandler.CustomOfflineTag or TagHandler.CustomOnlineTag or TagHandler.CustomVisibleTag or TagHandler.CustomUnpairedTag)
{ {
usersInThisTag = onlineUsers; usersInThisTag = onlineUsers;
isSpecialTag = true; isSpecialTag = true;
@@ -113,14 +119,13 @@ public class PairGroupsUi
if (isSpecialTag && !usersInThisTag.Any()) return; if (isSpecialTag && !usersInThisTag.Any()) return;
DrawName(tag, isSpecialTag, visibleInThisTag, usersInThisTag.Count(), otherUidsTaggedWithTag?.Count); DrawName(tag, isSpecialTag, visibleInThisTag, usersInThisTag.Count(), pausedUsers.Count(), otherUidsTaggedWithTag?.Count);
if (!isSpecialTag) if (!isSpecialTag)
{ {
using (ImRaii.PushId($"group-{tag}-buttons")) DrawButtons(tag, allUsers.Cast<DrawUserPair>().Where(p => otherUidsTaggedWithTag!.Contains(p.UID)).ToList()); using (ImRaii.PushId($"group-{tag}-buttons")) DrawButtons(tag, allUsers.Cast<DrawUserPair>().Where(p => otherUidsTaggedWithTag!.Contains(p.UID)).ToList());
} }
else else
{ {
// Avoid uncomfortably close group names
if (!_tagHandler.IsTagOpen(tag)) if (!_tagHandler.IsTagOpen(tag))
{ {
var size = ImGui.CalcTextSize("").Y + ImGui.GetStyle().FramePadding.Y * 2f; var size = ImGui.CalcTextSize("").Y + ImGui.GetStyle().FramePadding.Y * 2f;
@@ -151,18 +156,19 @@ public class PairGroupsUi
UiSharedService.AttachToolTip($"Delete Group {tag} (Will not delete the pairs)" + Environment.NewLine + "Hold CTRL to delete"); UiSharedService.AttachToolTip($"Delete Group {tag} (Will not delete the pairs)" + Environment.NewLine + "Hold CTRL to delete");
} }
private void DrawName(string tag, bool isSpecialTag, int visible, int online, int? total) private void DrawName(string tag, bool isSpecialTag, int visible, int online, int paused, int? total)
{ {
string displayedName = tag switch string displayedName = tag switch
{ {
TagHandler.CustomUnpairedTag => "Unpaired", TagHandler.CustomUnpairedTag => "Unpaired",
TagHandler.CustomOfflineTag => "Offline", TagHandler.CustomOfflineTag => "Offline",
TagHandler.CustomOnlineTag => _mareConfig.Current.ShowOfflineUsersSeparately ? "Online/Paused" : "Contacts", TagHandler.CustomOnlineTag => _mareConfig.Current.ShowOfflineUsersSeparately ? "Online" : "Contacts",
TagHandler.CustomPausedTag => "Paused",
TagHandler.CustomVisibleTag => "Visible", TagHandler.CustomVisibleTag => "Visible",
_ => tag _ => tag
}; };
string resultFolderName = !isSpecialTag ? $"{displayedName} ({visible}/{online}/{total} Pairs)" : $"{displayedName} ({online} Pairs)"; string resultFolderName = !isSpecialTag ? $"{displayedName} ({visible}/{online}/{paused}/{total} Pairs)" : $"{displayedName} ({online} Pairs)";
// FontAwesomeIcon.CaretSquareDown : FontAwesomeIcon.CaretSquareRight // FontAwesomeIcon.CaretSquareDown : FontAwesomeIcon.CaretSquareRight
var icon = _tagHandler.IsTagOpen(tag) ? FontAwesomeIcon.CaretSquareDown : FontAwesomeIcon.CaretSquareRight; var icon = _tagHandler.IsTagOpen(tag) ? FontAwesomeIcon.CaretSquareDown : FontAwesomeIcon.CaretSquareRight;
@@ -184,7 +190,8 @@ public class PairGroupsUi
ImGui.TextUnformatted($"Group {tag}"); ImGui.TextUnformatted($"Group {tag}");
ImGui.Separator(); ImGui.Separator();
ImGui.TextUnformatted($"{visible} Pairs visible"); ImGui.TextUnformatted($"{visible} Pairs visible");
ImGui.TextUnformatted($"{online} Pairs online/paused"); ImGui.TextUnformatted($"{online} Pairs online");
ImGui.TextUnformatted($"{paused} Pairs paused");
ImGui.TextUnformatted($"{total} Pairs total"); ImGui.TextUnformatted($"{total} Pairs total");
ImGui.EndTooltip(); ImGui.EndTooltip();
} }
@@ -197,39 +204,45 @@ public class PairGroupsUi
ImGui.Separator(); ImGui.Separator();
} }
private void DrawUserPairs(List<string> tagsWithPairsInThem, List<DrawUserPair> allUsers, IEnumerable<DrawUserPair> visibleUsers, IEnumerable<DrawUserPair> onlineUsers, IEnumerable<DrawUserPair> offlineUsers) private void DrawUserPairs(List<string> tagsWithPairsInThem, List<DrawUserPair> allUsers, IEnumerable<DrawUserPair> visibleUsers, IEnumerable<DrawUserPair> onlineUsers, IEnumerable<DrawUserPair> pausedUsers, IEnumerable<DrawUserPair> offlineUsers)
{ {
if (_mareConfig.Current.ShowVisibleUsersSeparately) if (_mareConfig.Current.ShowVisibleUsersSeparately)
{ {
using (ImRaii.PushId("$group-VisibleCustomTag")) DrawCategory(TagHandler.CustomVisibleTag, visibleUsers, allUsers); using (ImRaii.PushId("$group-VisibleCustomTag")) DrawCategory(TagHandler.CustomVisibleTag, visibleUsers, Enumerable.Empty<DrawUserPair>(), allUsers);
} }
foreach (var tag in tagsWithPairsInThem) foreach (var tag in tagsWithPairsInThem)
{ {
if (_mareConfig.Current.ShowOfflineUsersSeparately) if (_mareConfig.Current.ShowOfflineUsersSeparately)
{ {
using (ImRaii.PushId($"group-{tag}")) DrawCategory(tag, onlineUsers, allUsers, visibleUsers); using (ImRaii.PushId($"group-{tag}")) DrawCategory(tag, onlineUsers, pausedUsers, allUsers, visibleUsers);
} }
else else
{ {
using (ImRaii.PushId($"group-{tag}")) DrawCategory(tag, allUsers, allUsers, visibleUsers); using (ImRaii.PushId($"group-{tag}")) DrawCategory(tag, allUsers, Enumerable.Empty<DrawUserPair>(), allUsers, visibleUsers);
} }
} }
if (_mareConfig.Current.ShowOfflineUsersSeparately) if (_mareConfig.Current.ShowOfflineUsersSeparately)
{ {
using (ImRaii.PushId($"group-OnlineCustomTag")) DrawCategory(TagHandler.CustomOnlineTag, using (ImRaii.PushId($"group-OnlineCustomTag")) DrawCategory(TagHandler.CustomOnlineTag,
onlineUsers.Where(u => !_tagHandler.HasAnyTag(u.UID)).ToList(), allUsers); onlineUsers.Where(u => !_tagHandler.HasAnyTag(u.UID)).ToList(), Enumerable.Empty<DrawUserPair>(), allUsers);
if (pausedUsers.Any()) using (ImRaii.PushId("group-PausedCustomTag")) DrawCategory(TagHandler.CustomPausedTag,
Enumerable.Empty<DrawUserPair>(), pausedUsers, allUsers);
using (ImRaii.PushId($"group-OfflineCustomTag")) DrawCategory(TagHandler.CustomOfflineTag, using (ImRaii.PushId($"group-OfflineCustomTag")) DrawCategory(TagHandler.CustomOfflineTag,
offlineUsers.Where(u => u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers); offlineUsers.Where(u => u.UserPair!.OtherPermissions.IsPaired()).ToList(), Enumerable.Empty<DrawUserPair>(), allUsers);
} }
else else
{ {
using (ImRaii.PushId($"group-OnlineCustomTag")) DrawCategory(TagHandler.CustomOnlineTag, using (ImRaii.PushId($"group-OnlineCustomTag")) DrawCategory(TagHandler.CustomOnlineTag,
onlineUsers.Concat(offlineUsers.Where(u => u.UserPair!.OtherPermissions.IsPaired())).Where(u => !_tagHandler.HasAnyTag(u.UID)).ToList(), allUsers); onlineUsers.Concat(offlineUsers.Where(u => u.UserPair!.OtherPermissions.IsPaired())).Where(u => !_tagHandler.HasAnyTag(u.UID)).ToList(), Enumerable.Empty<DrawUserPair>(), allUsers);
} }
using (ImRaii.PushId($"group-UnpairedCustomTag")) DrawCategory(TagHandler.CustomUnpairedTag, using (ImRaii.PushId($"group-UnpairedCustomTag")) DrawCategory(TagHandler.CustomUnpairedTag,
offlineUsers.Where(u => !u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers); offlineUsers.Where(u => !u.UserPair!.OtherPermissions.IsPaired()).ToList(), Enumerable.Empty<DrawUserPair>(), allUsers);
} }
private void PauseRemainingPairs(List<DrawUserPair> availablePairs) private void PauseRemainingPairs(List<DrawUserPair> availablePairs)
{ {
foreach (var pairToPause in availablePairs.Where(pair => !pair.UserPair!.OwnPermissions.IsPaused())) foreach (var pairToPause in availablePairs.Where(pair => !pair.UserPair!.OwnPermissions.IsPaused()))

View File

@@ -203,10 +203,10 @@ public sealed class DtrEntry : IDisposable, IHostedService
DtrStyle.Style4 => $"\xE03A {text}", DtrStyle.Style4 => $"\xE03A {text}",
DtrStyle.Style5 => $"\xE033 {text}", DtrStyle.Style5 => $"\xE033 {text}",
DtrStyle.Style6 => $"\xE038 {text}", DtrStyle.Style6 => $"\xE038 {text}",
DtrStyle.Style7 => $"\xE05D {text}", DtrStyle.Style7 => $"\xE044 {text}",
DtrStyle.Style8 => $"\xE03C{text}", DtrStyle.Style8 => $"\xE03C{text}",
DtrStyle.Style9 => $"\xE040 {text} \xE041", DtrStyle.Style9 => $"\xE040 {text} \xE041",
_ => $"\uE044 {text}" _ => $"\uE05D {text}"
}; };
} }

View File

@@ -4,6 +4,7 @@ using Dalamud.Interface.Colors;
using Dalamud.Interface.ImGuiFileDialog; using Dalamud.Interface.ImGuiFileDialog;
using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Interface.Textures.TextureWraps;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Dalamud.Utility;
using MareSynchronos.API.Data; using MareSynchronos.API.Data;
using MareSynchronos.API.Dto.User; using MareSynchronos.API.Dto.User;
using MareSynchronos.Services; using MareSynchronos.Services;
@@ -28,14 +29,14 @@ public class EditProfileUi : WindowMediatorSubscriberBase
private IDalamudTextureWrap? _pfpTextureWrap; private IDalamudTextureWrap? _pfpTextureWrap;
private string _profileDescription = string.Empty; private string _profileDescription = string.Empty;
private byte[] _profileImage = []; private byte[] _profileImage = [];
private bool _showFileDialogError = false; private string _showFileDialogError = string.Empty;
private bool _wasOpen; private bool _wasOpen;
public EditProfileUi(ILogger<EditProfileUi> logger, MareMediator mediator, public EditProfileUi(ILogger<EditProfileUi> logger, MareMediator mediator,
ApiController apiController, UiSharedService uiSharedService, FileDialogManager fileDialogManager, ApiController apiController, UiSharedService uiSharedService, FileDialogManager fileDialogManager,
ServerConfigurationManager serverConfigurationManager, ServerConfigurationManager serverConfigurationManager,
MareProfileManager mareProfileManager, PerformanceCollectorService performanceCollectorService) MareProfileManager mareProfileManager, PerformanceCollectorService performanceCollectorService)
: base(logger, mediator, "Snowcloak Edit Profile###SnowcloakSyncEditProfileUI", performanceCollectorService) : base(logger, mediator, "Snowcloak Profile Editor###SnowcloakSyncEditProfileUI", performanceCollectorService)
{ {
IsOpen = false; IsOpen = false;
this.SizeConstraints = new() this.SizeConstraints = new()
@@ -92,20 +93,13 @@ public class EditProfileUi : WindowMediatorSubscriberBase
ImGui.Image(_pfpTextureWrap.Handle, ImGuiHelpers.ScaledVector2(_pfpTextureWrap.Width, _pfpTextureWrap.Height)); ImGui.Image(_pfpTextureWrap.Handle, ImGuiHelpers.ScaledVector2(_pfpTextureWrap.Width, _pfpTextureWrap.Height));
} }
var spacing = ImGui.GetStyle().ItemSpacing.X; var spacing = ImGui.GetStyle().ItemSpacing.X + 200;
ImGuiHelpers.ScaledRelativeSameLine(256, spacing); ImGuiHelpers.ScaledRelativeSameLine(256, spacing);
using (_uiSharedService.GameFont.Push()) using (_uiSharedService.GameFont.Push())
{ {
var descriptionTextSize = ImGui.CalcTextSize(profile.Description, hideTextAfterDoubleHash: false, 256f); var descriptionTextSize = ImGui.CalcTextSize(profile.Description, hideTextAfterDoubleHash: false, 256f);
var childFrame = ImGuiHelpers.ScaledVector2(256 + ImGui.GetStyle().WindowPadding.X + ImGui.GetStyle().WindowBorderSize, 256); var childFrame = ImGuiHelpers.ScaledVector2(256 + ImGui.GetStyle().WindowPadding.X + ImGui.GetStyle().WindowBorderSize, 256);
if (descriptionTextSize.Y > childFrame.Y) _adjustedForScollBarsOnlineProfile = (descriptionTextSize.Y > childFrame.Y);
{
_adjustedForScollBarsOnlineProfile = true;
}
else
{
_adjustedForScollBarsOnlineProfile = false;
}
childFrame = childFrame with childFrame = childFrame with
{ {
X = childFrame.X + (_adjustedForScollBarsOnlineProfile ? ImGui.GetStyle().ScrollbarSize : 0), X = childFrame.X + (_adjustedForScollBarsOnlineProfile ? ImGui.GetStyle().ScrollbarSize : 0),
@@ -122,9 +116,18 @@ public class EditProfileUi : WindowMediatorSubscriberBase
ImGui.Checkbox("Is NSFW", ref nsfw); ImGui.Checkbox("Is NSFW", ref nsfw);
ImGui.EndDisabled(); ImGui.EndDisabled();
ImGui.Separator();
_uiSharedService.BigText("Rules and Guidelines");
UiSharedService.ColorTextWrapped("Users that are paired with you (not paused) will be able to see your profile picture and description.", ImGuiColors.DalamudWhite);
UiSharedService.ColorTextWrapped("All users have the capability to report your profile if it violates the rules.", ImGuiColors.DalamudGrey);
UiSharedService.ColorTextWrapped(" - Please do NOT upload anything that can be considered highly illegal or obscene (beastiality, sexual acts depicting minors or anything representing a minor (including Lalafel), etc.)", ImGuiColors.DalamudRed);
UiSharedService.ColorTextWrapped(" - Please avoid the use of slurs, hate speech, threatening behaviour, etc.", ImGuiColors.DalamudRed);
UiSharedService.ColorTextWrapped(" - In the event we receive a report of an offensive profile, we may disable your profile forever or terminate your Snowcloak service account.", ImGuiColors.DalamudRed);
UiSharedService.ColorTextWrapped(" - You may not appeal any bans of your profile and or Snowcloak service account.", ImGuiColors.DalamudRed);
UiSharedService.ColorTextWrapped("Users who wish to mark their profile as NSFW should enable the toggle below.", ImGuiColors.DalamudWhite);
ImGui.Separator(); ImGui.Separator();
_uiSharedService.BigText("Profile Settings"); _uiSharedService.BigText("Profile Settings");
UiSharedService.ColorTextWrapped("Profile pictures must be cropped to 256x256px and have a file size of 250KiB or smaller.", ImGuiColors.DalamudGrey);
if (_uiSharedService.IconTextButton(FontAwesomeIcon.FileUpload, "Upload new profile picture")) if (_uiSharedService.IconTextButton(FontAwesomeIcon.FileUpload, "Upload new profile picture"))
{ {
_fileDialogManager.OpenFileDialog("Select new Profile picture", ".png", (success, file) => _fileDialogManager.OpenFileDialog("Select new Profile picture", ".png", (success, file) =>
@@ -138,11 +141,10 @@ public class EditProfileUi : WindowMediatorSubscriberBase
if (format.Width > 256 || format.Height > 256 || (fileContent.Length > 250 * 1024)) if (format.Width > 256 || format.Height > 256 || (fileContent.Length > 250 * 1024))
{ {
_showFileDialogError = true; _showFileDialogError = format.Width > 256 || format.Height > 256 ? "ERROR: Image dimensions must be 256x256px or smaller." : fileContent.Length > 250 * 1024 ? "ERROR: File size was bigger than 250KiB" : "ERROR: An unknown error has occured.";
return; return;
} }
_showFileDialogError = string.Empty;
_showFileDialogError = false;
await _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, Convert.ToBase64String(fileContent), Description: null)) await _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, Convert.ToBase64String(fileContent), Description: null))
.ConfigureAwait(false); .ConfigureAwait(false);
}); });
@@ -155,9 +157,9 @@ public class EditProfileUi : WindowMediatorSubscriberBase
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, "", Description: null)); _ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, "", Description: null));
} }
UiSharedService.AttachToolTip("Clear your currently uploaded profile picture"); UiSharedService.AttachToolTip("Clear your currently uploaded profile picture");
if (_showFileDialogError) if (!_showFileDialogError.IsNullOrEmpty())
{ {
UiSharedService.ColorTextWrapped("The profile picture must be a PNG file with a maximum height and width of 256px and 250KiB size", ImGuiColors.DalamudRed); UiSharedService.ColorTextWrapped(_showFileDialogError, ImGuiColors.DalamudRed);
} }
var isNsfw = profile.IsNSFW; var isNsfw = profile.IsNSFW;
if (ImGui.Checkbox("Profile is NSFW", ref isNsfw)) if (ImGui.Checkbox("Profile is NSFW", ref isNsfw))

View File

@@ -9,6 +9,7 @@ public class TagHandler
public const string CustomOnlineTag = "Mare_Online"; public const string CustomOnlineTag = "Mare_Online";
public const string CustomUnpairedTag = "Mare_Unpaired"; public const string CustomUnpairedTag = "Mare_Unpaired";
public const string CustomVisibleTag = "Mare_Visible"; public const string CustomVisibleTag = "Mare_Visible";
public const string CustomPausedTag = "Mare_Paused";
private readonly ServerConfigurationManager _serverConfigurationManager; private readonly ServerConfigurationManager _serverConfigurationManager;
public TagHandler(ServerConfigurationManager serverConfigurationManager) public TagHandler(ServerConfigurationManager serverConfigurationManager)

View File

@@ -956,6 +956,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
_uiShared.BigText("UI"); _uiShared.BigText("UI");
var showCharacterNames = _configService.Current.ShowCharacterNames; var showCharacterNames = _configService.Current.ShowCharacterNames;
var showVisibleSeparate = _configService.Current.ShowVisibleUsersSeparately; var showVisibleSeparate = _configService.Current.ShowVisibleUsersSeparately;
var sortSyncshellByVRAM = _configService.Current.SortSyncshellsByVRAM;
var showOfflineSeparate = _configService.Current.ShowOfflineUsersSeparately; var showOfflineSeparate = _configService.Current.ShowOfflineUsersSeparately;
var showProfiles = _configService.Current.ProfilesShow; var showProfiles = _configService.Current.ProfilesShow;
var showNsfwProfiles = _configService.Current.ProfilesAllowNsfw; var showNsfwProfiles = _configService.Current.ProfilesAllowNsfw;
@@ -1074,13 +1075,20 @@ public class SettingsUi : WindowMediatorSubscriberBase
_configService.Save(); _configService.Save();
} }
_uiShared.DrawHelpText("This will show all currently visible users in a special 'Visible' group in the main UI."); _uiShared.DrawHelpText("This will show all currently visible users in a special 'Visible' group in the main UI.");
if (ImGui.Checkbox("Sort visible syncshell users by VRAM usage", ref sortSyncshellByVRAM))
{
_configService.Current.SortSyncshellsByVRAM = sortSyncshellByVRAM;
_logger.LogWarning("Changing value: {sortSyncshellsByVRAM}", sortSyncshellByVRAM);
if (ImGui.Checkbox("Show separate Offline group", ref showOfflineSeparate)) _configService.Save();
}
_uiShared.DrawHelpText("This will put users using the most VRAM in a syncshell at the top of the list.");
if (ImGui.Checkbox("Group users by connection status", ref showOfflineSeparate))
{ {
_configService.Current.ShowOfflineUsersSeparately = showOfflineSeparate; _configService.Current.ShowOfflineUsersSeparately = showOfflineSeparate;
_configService.Save(); _configService.Save();
} }
_uiShared.DrawHelpText("This will show all currently offline users in a special 'Offline' group in the main UI."); _uiShared.DrawHelpText("This will categorize users by their connection status in the main UI.");
if (ImGui.Checkbox("Show player names", ref showCharacterNames)) if (ImGui.Checkbox("Show player names", ref showCharacterNames))
{ {

View File

@@ -121,7 +121,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
{ {
Logger.LogWarning("Multiple secret keys for current character"); Logger.LogWarning("Multiple secret keys for current character");
_connectionDto = null; _connectionDto = null;
Mediator.Publish(new NotificationMessage("Multiple Identical Characters detected", "Your Service configuration has multiple characters with the same name and world set up. Delete the duplicates in the character management to be able to connect to Mare.", Mediator.Publish(new NotificationMessage("Multiple Identical Characters detected", "Your Service configuration has multiple characters with the same name and world set up. Delete the duplicates in the character management to be able to connect.",
NotificationType.Error)); NotificationType.Error));
await StopConnection(ServerState.MultiChara).ConfigureAwait(false); await StopConnection(ServerState.MultiChara).ConfigureAwait(false);
_connectionCancellationTokenSource?.Cancel(); _connectionCancellationTokenSource?.Cancel();
@@ -183,10 +183,12 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
_connectionDto = await GetConnectionDto().ConfigureAwait(false); _connectionDto = await GetConnectionDto().ConfigureAwait(false);
await CheckClientHealth().ConfigureAwait(false);
ServerState = ServerState.Connected; ServerState = ServerState.Connected;
var currentClientVer = Assembly.GetExecutingAssembly().GetName().Version!; var currentClientVer = Assembly.GetExecutingAssembly().GetName().Version!;
if (_connectionDto.ServerVersion != IMareHub.ApiVersion) if (_connectionDto.ServerVersion != IMareHub.ApiVersion)
{ {
if (_connectionDto.CurrentClientVersion > currentClientVer) if (_connectionDto.CurrentClientVersion > currentClientVer)
@@ -308,14 +310,18 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
_ = Task.Run(async () => await StopConnection(ServerState.Disconnected).ConfigureAwait(false)); _ = Task.Run(async () => await StopConnection(ServerState.Disconnected).ConfigureAwait(false));
_connectionCancellationTokenSource?.Cancel(); _connectionCancellationTokenSource?.Cancel();
} }
private async Task ClientHealthCheck(CancellationToken ct) private async Task ClientHealthCheck(CancellationToken ct)
{ {
while (!ct.IsCancellationRequested && _mareHub != null) while (!ct.IsCancellationRequested && _mareHub != null)
{ {
await Task.Delay(TimeSpan.FromSeconds(30), ct).ConfigureAwait(false); await Task.Delay(TimeSpan.FromSeconds(30), ct).ConfigureAwait(false);
Logger.LogDebug("Checking Client Health State"); var healthy = await CheckClientHealth().ConfigureAwait(false);
_ = await CheckClientHealth().ConfigureAwait(false); if (!healthy || _mareHub.State != HubConnectionState.Connected)
{
Logger.LogWarning("Health check failed, forcing reconnect. ClientHealth: {0} HubConnected: {1}", healthy, _mareHub.State != HubConnectionState.Connected);
await ForceResetConnection().ConfigureAwait(false);
}
} }
} }
@@ -391,7 +397,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
{ {
var users = await GroupsGetUsersInGroup(group).ConfigureAwait(false); var users = await GroupsGetUsersInGroup(group).ConfigureAwait(false);
foreach (var user in users) foreach (var user in users)
{ {
Logger.LogDebug("Group Pair: {user}", user); Logger.LogDebug("Group Pair: {user}", user);
_pairManager.AddGroupPair(user); _pairManager.AddGroupPair(user);
} }
@@ -478,5 +484,30 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
ServerState = state; ServerState = state;
} }
//Because this plugin really likes to bug out with connections, lets "fix" it....
public async Task ForceResetConnection()
{
if (!_initialized) return;
Logger.LogInformation("ForceReconnect called");
try
{
await StopConnection(ServerState.Disconnected).ConfigureAwait(false);
// Cancel any ongoing health checks to prevent conflicts
_healthCheckTokenSource?.Cancel();
_healthCheckTokenSource?.Dispose();
_healthCheckTokenSource = null;
await CreateConnections().ConfigureAwait(false);
Logger.LogInformation("ForceReconnect completed successfully");
}
catch (Exception ex)
{
Logger.LogError(ex, "Failure during ForceReconnect, disconnecting");
await StopConnection(ServerState.Disconnected).ConfigureAwait(false);
}
}
} }
#pragma warning restore MA0040 #pragma warning restore MA0040

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -10,9 +10,9 @@
}, },
"DalamudPackager": { "DalamudPackager": {
"type": "Direct", "type": "Direct",
"requested": "[13.0.0, )", "requested": "[13.1.0, )",
"resolved": "13.0.0", "resolved": "13.1.0",
"contentHash": "Mb3cUDSK/vDPQ8gQIeuCw03EMYrej1B4J44a1AvIJ9C759p9XeqdU9Hg4WgOmlnlPe0G7ILTD32PKSUpkQNa8w==" "contentHash": "XdoNhJGyFby5M/sdcRhnc5xTop9PHy+H50PTWpzLhJugjB19EDBiHD/AsiDF66RETM+0qKUdJBZrNuebn7qswQ=="
}, },
"DotNet.ReproducibleBuilds": { "DotNet.ReproducibleBuilds": {
"type": "Direct", "type": "Direct",
@@ -22,11 +22,11 @@
}, },
"Downloader": { "Downloader": {
"type": "Direct", "type": "Direct",
"requested": "[3.3.4, )", "requested": "[4.0.3, )",
"resolved": "3.3.4", "resolved": "4.0.3",
"contentHash": "/M/c80e1L0WW1XrLSSiQhgFxk8rrfbpWiWDn2CeBg1tPD393Neo+v184yG/ThyhE9rrNp36yCrugiCmEbRf+VQ==", "contentHash": "Vg1+UqPDstpMw2CKXV9XvB8jKHC95KQfbqPxQXvOMRMFnTov4Ixvvw6GZV5DXLnKuL2sfnmVYX9CaQtcURia1Q==",
"dependencies": { "dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "8.0.1" "Microsoft.Extensions.Logging.Abstractions": "8.0.3"
} }
}, },
"Glamourer.Api": { "Glamourer.Api": {
@@ -55,11 +55,28 @@
"System.IO.Pipelines": "6.0.3" "System.IO.Pipelines": "6.0.3"
} }
}, },
"MessagePack": {
"type": "Direct",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "BH0wlHWmVoZpbAPyyt2Awbq30C+ZsS3eHSkYdnyUAbqVJ22fAJDzn2xTieBeoT5QlcBzp61vHcv878YJGfi3mg==",
"dependencies": {
"MessagePack.Annotations": "3.1.4",
"MessagePackAnalyzer": "3.1.4",
"Microsoft.NET.StringTools": "17.11.4"
}
},
"MessagePack.Annotations": {
"type": "Direct",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aVWrDAkCdqxwQsz/q0ldPh2EFn48M99YUzE9OvZjMq2RNLKz4o2z88iGFvSvbMqOWRweRvKPHBJZe22PRqzslQ=="
},
"Meziantou.Analyzer": { "Meziantou.Analyzer": {
"type": "Direct", "type": "Direct",
"requested": "[2.0.212, )", "requested": "[2.0.213, )",
"resolved": "2.0.212", "resolved": "2.0.213",
"contentHash": "U91ktjjTRTccUs3Lk+hrLD9vW+2+lhnsOf4G1GpRSJi1pLn3uK5CU6wGP9Bmz1KlJs6Oz1GGoMhxQBoqQsmAuQ==" "contentHash": "LHnFGBqhlBjbf8Uo4OIzGM0llRxFrIMuo/hP9oHq+aldS+28G4LqG12LK0co9b+S0yj1Vbf0rclDHN0Ji2DTkA=="
}, },
"Microsoft.AspNetCore.SignalR.Client": { "Microsoft.AspNetCore.SignalR.Client": {
"type": "Direct", "type": "Direct",
@@ -133,6 +150,12 @@
"Microsoft.IdentityModel.Tokens": "8.14.0" "Microsoft.IdentityModel.Tokens": "8.14.0"
} }
}, },
"System.IO.Pipelines": {
"type": "Direct",
"requested": "[9.0.8, )",
"resolved": "9.0.8",
"contentHash": "6vPmJt73mgUo1gzc/OcXlJvClz/2jxZ4TQPRfriVaLoGRH2mye530D9WHJYbFQRNMxF3PWCoeofsFdCyN7fLzA=="
},
"K4os.Compression.LZ4": { "K4os.Compression.LZ4": {
"type": "Transitive", "type": "Transitive",
"resolved": "1.3.8", "resolved": "1.3.8",
@@ -143,19 +166,10 @@
"resolved": "1.0.8", "resolved": "1.0.8",
"contentHash": "Wp2F7BamQ2Q/7Hk834nV9vRQapgcr8kgv9Jvfm8J3D0IhDqZMMl+a2yxUq5ltJitvXvQfB8W6K4F4fCbw/P6YQ==" "contentHash": "Wp2F7BamQ2Q/7Hk834nV9vRQapgcr8kgv9Jvfm8J3D0IhDqZMMl+a2yxUq5ltJitvXvQfB8W6K4F4fCbw/P6YQ=="
}, },
"MessagePack": { "MessagePackAnalyzer": {
"type": "Transitive", "type": "Transitive",
"resolved": "2.5.187", "resolved": "3.1.4",
"contentHash": "uW4j8m4Nc+2Mk5n6arOChavJ9bLjkis0qWASOj2h2OwmfINuzYv+mjCHUymrYhmyyKTu3N+ObtTXAY4uQ7jIhg==", "contentHash": "CTaSsN/liJ7MhLCAB7Z4ZLBNuVGCq9lt2BT/cbrc9vzGv89yK3CqIA+z9T19a11eQYl9etZHL6MQJgCqECRVpg=="
"dependencies": {
"MessagePack.Annotations": "2.5.187",
"Microsoft.NET.StringTools": "17.6.3"
}
},
"MessagePack.Annotations": {
"type": "Transitive",
"resolved": "2.5.198",
"contentHash": "3U9OvqQGTra+Mz1k1zfNAScSdNHobnqtQ51qdMGUZppkNDZJl0X/igq6Qz5zDBLEZoYqZrFtZwFx6wBJHHI8BA=="
}, },
"Microsoft.AspNetCore.Connections.Abstractions": { "Microsoft.AspNetCore.Connections.Abstractions": {
"type": "Transitive", "type": "Transitive",
@@ -499,19 +513,14 @@
}, },
"Microsoft.NET.StringTools": { "Microsoft.NET.StringTools": {
"type": "Transitive", "type": "Transitive",
"resolved": "17.6.3", "resolved": "17.11.4",
"contentHash": "N0ZIanl1QCgvUumEL1laasU0a7sOE5ZwLZVTn0pAePnfhq8P7SvTjF8Axq+CnavuQkmdQpGNXQ1efZtu5kDFbA==" "contentHash": "mudqUHhNpeqIdJoUx2YDWZO/I9uEDYVowan89R6wsomfnUJQk6HteoQTlNjZDixhT2B4IXMkMtgZtoceIjLRmA=="
}, },
"System.Diagnostics.EventLog": { "System.Diagnostics.EventLog": {
"type": "Transitive", "type": "Transitive",
"resolved": "9.0.8", "resolved": "9.0.8",
"contentHash": "gebRF3JLLJ76jz1CQpvwezNapZUjFq20JQsaGHzBH0DzlkHBLpdhwkOei9usiOkIGMwU/L0ALWpNe1JE+5/itw==" "contentHash": "gebRF3JLLJ76jz1CQpvwezNapZUjFq20JQsaGHzBH0DzlkHBLpdhwkOei9usiOkIGMwU/L0ALWpNe1JE+5/itw=="
}, },
"System.IO.Pipelines": {
"type": "Transitive",
"resolved": "6.0.3",
"contentHash": "ryTgF+iFkpGZY1vRQhfCzX0xTdlV3pyaTTqRu2ETbEv+HlV7O6y7hyQURnghNIXvctl5DuZ//Dpks6HdL/Txgw=="
},
"System.Net.ServerSentEvents": { "System.Net.ServerSentEvents": {
"type": "Transitive", "type": "Transitive",
"resolved": "9.0.8", "resolved": "9.0.8",
@@ -525,8 +534,11 @@
"maresynchronos.api": { "maresynchronos.api": {
"type": "Project", "type": "Project",
"dependencies": { "dependencies": {
"MessagePack.Annotations": "[2.5.198, )" "MessagePack.Annotations": "[2.5.129, )"
} }
},
"snowcloaksync": {
"type": "Project"
} }
} }
} }

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,9 @@
using System.Numerics;
namespace SnowcloakSync.Utils
{
public static class Colours
{
public static readonly Vector4 _snowcloakOnline = new(0.4275f, 0.6863f, 1f, 1f);
}
}