21 Commits

Author SHA1 Message Date
c39229a28b Merge branch 'main' into main
Some checks are pending
.NET Build and Publish to Gitea / build (pull_request) Waiting to run
2025-09-02 02:09:04 +00:00
00c56b0888 Merge branch 'main' of https://imbuilding.anuke.org/ProfessorFartsalot/SnowcloakClient
Some checks are pending
.NET Build and Publish to Gitea / build (pull_request) Blocked by required conditions
2025-09-01 16:12:06 -04:00
15898d54c2 Update color for online member count 2025-09-01 16:11:45 -04:00
6d5ed42e60 Version bump
Some checks failed
.NET Build and Publish to Gitea / build (push) Has been cancelled
2025-09-01 20:57:26 +01:00
92a8e224ef merge upstream
Some checks are pending
.NET Build and Publish to Gitea / build (pull_request) Blocked by required conditions
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
778d30ae26 Merge pull request 'Fix avatars being limited to 255px (again)' (#1) from ProfessorFartsalot/SnowcloakClient:main into main
Some checks failed
.NET Build and Publish to Gitea / build (push) Has been cancelled
Reviewed-on: #1
Reviewed-by: Eauldane <elf@eauldane.com>
2025-09-01 09:47:36 +00:00
d2212da456 Switch from testing to production build tag
Some checks failed
.NET Build and Publish to Gitea / build (pull_request) Has been cancelled
2025-08-31 14:20:16 -04:00
a3dfe35df6 Add initial build script
Some checks failed
.NET Build and Publish to Gitea / build (pull_request) Has been cancelled
2025-08-31 14:18:20 -04:00
da6623741c Delete test file used yesterday for testing purposes
Some checks failed
.NET Build and Publish to Gitea / build (pull_request) Has been cancelled
2025-08-31 14:16:53 -04:00
abea8f0856 Fix PNG images being limited to 255px
Some checks failed
.NET Build and Publish to Gitea / build (pull_request) Has been cancelled
2025-08-31 14:13:16 -04:00
1624a84cf1 merge upstream 2025-08-31 06:47:27 +00:00
ba852831aa Update build.yml 2025-08-31 02:31:18 -04:00
5de9ad3a54 Update build.yml 2025-08-31 02:27:15 -04:00
4c8ce23e8a Update build.yml 2025-08-31 02:22:22 -04:00
2be5f5d320 MAYBE NOW IT WORKS? 2025-08-31 02:19:45 -04:00
c428f306ca NOW fix build issues??? 2025-08-31 02:07:22 -04:00
3dde713c91 Fix build issues? 2025-08-31 01:58:27 -04:00
3280446c7e Create build.yml 2025-08-31 01:35:49 -04:00
a408e22a68 Tentative Moodles v3 support. They might change something between now and release.
Some checks failed
.NET Build / build (push) Has been cancelled
2025-08-31 06:33:56 +01:00
abc361faf0 Hello!
Testing from the gitea instance owned by ProfessorFartsalot~
2025-08-31 00:01:07 -04:00
7 changed files with 90 additions and 28 deletions

View File

@@ -0,0 +1,52 @@
name: .NET Build and Publish to Gitea
on:
push:
branches: '*'
pull_request:
jobs:
build:
runs-on: dotnet
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: true
- name: Restore dependencies
run: dotnet restore
- name: Download Dalamud
run: |
mkdir -p $HOME/.xlcore/dalamud/Hooks/dev/
curl -L https://goatcorp.github.io/dalamud-distrib/stg/latest.zip -o latest.zip
unzip -o latest.zip -d $HOME/.xlcore/dalamud/Hooks/dev/
- name: Build project
run: dotnet build --no-restore --configuration Release --nologo
- name: Publish Windows executable
run: dotnet publish -c Release -r win-x64 --self-contained true -o ./publish
- name: Archive published files
run: zip -r SnowcloakClient.zip ./publish/*
- name: Create Gitea release
env:
GITEA_TOKEN: ${{ secrets.BUILD_SNOWCLOAK_CLIENT }}
run: |
API_URL="https://git.snowcloak-sync.com/api/v1/repos/Eauldane/SnowcloakClient/releases"
TAG="v$(date +%Y%m%d%H%M)"
# Create release
RELEASE_ID=$(curl -s -X POST \
-H "Content-Type: application/json" \
-H "Authorization: token $GITEA_TOKEN" \
-d "{\"tag_name\":\"$TAG\",\"name\":\"Automated Build $TAG\",\"body\":\"Automated build artifact\"}" \
$API_URL | jq -r '.id')
echo "Release ID: $RELEASE_ID"
# Upload asset
curl -s -X POST \
-H "Authorization: token $GITEA_TOKEN" \
-F "name=SnowcloakClient.zip" \
-F "attachment=@SnowcloakClient.zip" \
"$API_URL/$RELEASE_ID/assets"

View File

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

View File

@@ -2,7 +2,7 @@
<Project Sdk="Dalamud.NET.Sdk/13.0.0"> <Project Sdk="Dalamud.NET.Sdk/13.0.0">
<PropertyGroup> <PropertyGroup>
<AssemblyName>Snowcloak</AssemblyName> <AssemblyName>Snowcloak</AssemblyName>
<Version>0.2.0.3</Version> <Version>0.2.1</Version>
<PackageProjectUrl>https://github.com/Eauldane/SnowcloakClient/</PackageProjectUrl> <PackageProjectUrl>https://github.com/Eauldane/SnowcloakClient/</PackageProjectUrl>
</PropertyGroup> </PropertyGroup>

View File

@@ -387,7 +387,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

@@ -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

@@ -20,7 +20,7 @@ public class PngHdr
stream.ReadExactly(buffer[..8]); stream.ReadExactly(buffer[..8]);
uint ihdrLength = BitConverter.ToUInt32(buffer); uint ihdrLength = ReadBigEndianUInt32(buffer[..4]);
// The next four bytes will be the length of the IHDR section (it should be 13 bytes but we only need 8) // The next four bytes will be the length of the IHDR section (it should be 13 bytes but we only need 8)
if (ihdrLength < 8) if (ihdrLength < 8)
@@ -32,8 +32,8 @@ public class PngHdr
stream.ReadExactly(buffer[..8]); stream.ReadExactly(buffer[..8]);
uint width = BitConverter.ToUInt32(buffer); uint width = ReadBigEndianUInt32(buffer[..4]);
uint height = BitConverter.ToUInt32(buffer[4..]); uint height = ReadBigEndianUInt32(buffer[4..8]);
// Validate the width/height are non-negative and... that's all we care about! // Validate the width/height are non-negative and... that's all we care about!
if (width > int.MaxValue || height > int.MaxValue) if (width > int.MaxValue || height > int.MaxValue)
@@ -46,4 +46,12 @@ public class PngHdr
return InvalidSize; return InvalidSize;
} }
} }
// Minimal helper for big-endian conversion
private static uint ReadBigEndianUInt32(ReadOnlySpan<byte> bytes)
{
return ((uint)bytes[0] << 24) |
((uint)bytes[1] << 16) |
((uint)bytes[2] << 8) |
bytes[3];
}
} }

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();