17 Commits

Author SHA1 Message Date
9e5398105e Added a comment and TODO regarding UI bugs in IntroUI.
Some checks failed
.NET Build / build (push) Has been cancelled
2025-08-29 20:53:31 +01:00
7c2f5445e1 Bumped minimum Penumbra version due to recent API changes. 2025-08-29 20:47:15 +01:00
ed4630cd51 Users now need to re-accept the TOS if they only accepted an old version. 2025-08-29 20:47:00 +01:00
ac231ef800 Added age qualifier to TOS. 2025-08-29 20:32:38 +01:00
a3f5ee766a Merge pull request #12 from ProfessorFartsalot/main
Changed display name in advanced config UI
2025-08-29 18:01:52 +01:00
a697301895 Update main ui to match theme of snowcloak sync 2025-08-28 10:29:40 -04:00
27a09aa420 Add discord invite and update catchphrase 2025-08-27 14:16:33 -04:00
86cb7d2221 Update README.md 2025-08-27 13:34:24 -04:00
bbf79bacac Changed display name in advanced config
+ Renamed to snowcloak to support planned api changes
2025-08-27 04:23:46 -04:00
91d4cf8532 Removed logo due to AI
Some checks failed
Create Release / build (push) Has been cancelled
2025-08-26 19:22:05 +01:00
df7e6b2a51 Logo, by Dia 2025-08-26 18:18:02 +01:00
b9ce34bf10 Adjusted triangle threshold for auto pause, and made notifications for syncshell members the default as well. 2025-08-26 15:53:19 +01:00
267fb2d25c Merge pull request #11 from ProfessorFartsalot/main
Fix icons not showing correctly
2025-08-26 13:10:20 +01:00
291f005888 Fix icons not showing correctly 2025-08-26 06:17:54 -04:00
2e1bc4840b 1.8 bump
Some checks failed
Create Release / build (push) Has been cancelled
2025-08-25 18:33:08 +01:00
8526f6c240 Cosmetic "fix" 2025-08-25 18:25:41 +01:00
77d62b68b4 PenumbraAPI 5.12 fixes 2025-08-25 18:21:14 +01:00
13 changed files with 49 additions and 35 deletions

View File

@@ -122,24 +122,24 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase
public bool StorageisNTFS { get; private set; } = false; public bool StorageisNTFS { get; private set; } = false;
public void StartMareWatcher(string? marePath) public void StartMareWatcher(string? snowPath)
{ {
MareWatcher?.Dispose(); MareWatcher?.Dispose();
if (string.IsNullOrEmpty(marePath) || !Directory.Exists(marePath)) if (string.IsNullOrEmpty(snowPath) || !Directory.Exists(snowPath))
{ {
MareWatcher = null; MareWatcher = null;
Logger.LogWarning("Mare file path is not set, cannot start the FSW for Mare."); Logger.LogWarning("Snowcloak file path is not set, cannot start the FSW for Snowcloak.");
return; return;
} }
DriveInfo di = new(new DirectoryInfo(_configService.Current.CacheFolder).Root.FullName); DriveInfo di = new(new DirectoryInfo(_configService.Current.CacheFolder).Root.FullName);
StorageisNTFS = string.Equals("NTFS", di.DriveFormat, StringComparison.OrdinalIgnoreCase); StorageisNTFS = string.Equals("NTFS", di.DriveFormat, StringComparison.OrdinalIgnoreCase);
Logger.LogInformation("Mare Storage is on NTFS drive: {isNtfs}", StorageisNTFS); Logger.LogInformation("Snowcloak Storage is on NTFS drive: {isNtfs}", StorageisNTFS);
Logger.LogDebug("Initializing Mare FSW on {path}", marePath); Logger.LogDebug("Initializing Mare FSW on {path}", snowPath);
MareWatcher = new() MareWatcher = new()
{ {
Path = marePath, Path = snowPath,
InternalBufferSize = 8388608, InternalBufferSize = 8388608,
NotifyFilter = NotifyFilters.CreationTime NotifyFilter = NotifyFilters.CreationTime
| NotifyFilters.LastWrite | NotifyFilters.LastWrite
@@ -161,7 +161,7 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase
if (string.IsNullOrEmpty(substPath)) if (string.IsNullOrEmpty(substPath))
{ {
SubstWatcher = null; SubstWatcher = null;
Logger.LogWarning("Mare file path is not set, cannot start the FSW for Mare."); Logger.LogWarning("Snowcloak file path is not set, cannot start the FSW for Snowcloak.");
return; return;
} }
@@ -197,7 +197,7 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase
private void MareWatcher_FileChanged(object sender, FileSystemEventArgs e) private void MareWatcher_FileChanged(object sender, FileSystemEventArgs e)
{ {
Logger.LogTrace("Mare FSW: FileChanged: {change} => {path}", e.ChangeType, e.FullPath); Logger.LogTrace("Snowcloak FSW: FileChanged: {change} => {path}", e.ChangeType, e.FullPath);
if (!AllowedFileExtensions.Any(ext => e.FullPath.EndsWith(ext, StringComparison.OrdinalIgnoreCase))) return; if (!AllowedFileExtensions.Any(ext => e.FullPath.EndsWith(ext, StringComparison.OrdinalIgnoreCase))) return;

View File

@@ -114,7 +114,7 @@ public sealed class IpcCallerPenumbra : DisposableMediatorSubscriberBase, IIpcCa
bool penumbraAvailable = false; bool penumbraAvailable = false;
try try
{ {
penumbraAvailable = _pluginLoaded && _pluginVersion >= new Version(1, 0, 1, 0); penumbraAvailable = _pluginLoaded && _pluginVersion >= new Version(1, 5, 1, 0);
try try
{ {
penumbraAvailable &= _penumbraEnabled.Invoke(); penumbraAvailable &= _penumbraEnabled.Invoke();
@@ -225,9 +225,15 @@ public sealed class IpcCallerPenumbra : DisposableMediatorSubscriberBase, IIpcCa
return await _dalamudUtil.RunOnFrameworkThread(() => return await _dalamudUtil.RunOnFrameworkThread(() =>
{ {
Guid collId;
var collName = "ElfSync_" + uid; var collName = "ElfSync_" + uid;
var collId = _penumbraCreateNamedTemporaryCollection.Invoke(collName); PenumbraApiEc penEC = _penumbraCreateNamedTemporaryCollection.Invoke(uid, collName, out collId);
logger.LogTrace("Creating Temp Collection {collName}, GUID: {collId}", collName, collId); logger.LogTrace("Creating Temp Collection {collName}, GUID: {collId}", collName, collId);
if (penEC != PenumbraApiEc.Success)
{
logger.LogError("Failed to create temporary collection for {collName} with error code {penEC}. Please include this line in any error reports", collName, penEC);
return Guid.Empty;
}
return collId; return collId;
}).ConfigureAwait(false); }).ConfigureAwait(false);

View File

@@ -8,6 +8,6 @@ public static class ConfigurationExtensions
{ {
return configuration.AcceptedAgreement && configuration.InitialScanComplete return configuration.AcceptedAgreement && configuration.InitialScanComplete
&& !string.IsNullOrEmpty(configuration.CacheFolder) && !string.IsNullOrEmpty(configuration.CacheFolder)
&& Directory.Exists(configuration.CacheFolder); && Directory.Exists(configuration.CacheFolder) && configuration.AcceptedTOSVersion == configuration.ExpectedTOSVersion;
} }
} }

View File

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

View File

@@ -7,9 +7,9 @@ public class PlayerPerformanceConfig : IMareConfiguration
public int Version { get; set; } = 1; public int Version { get; set; } = 1;
public bool AutoPausePlayersExceedingThresholds { get; set; } = true; public bool AutoPausePlayersExceedingThresholds { get; set; } = true;
public bool NotifyAutoPauseDirectPairs { get; set; } = true; public bool NotifyAutoPauseDirectPairs { get; set; } = true;
public bool NotifyAutoPauseGroupPairs { get; set; } = false; public bool NotifyAutoPauseGroupPairs { get; set; } = true;
public int VRAMSizeAutoPauseThresholdMiB { get; set; } = 500; public int VRAMSizeAutoPauseThresholdMiB { get; set; } = 500;
public int TrisAutoPauseThresholdThousands { get; set; } = 175; public int TrisAutoPauseThresholdThousands { get; set; } = 400;
public bool IgnoreDirectPairs { get; set; } = true; public bool IgnoreDirectPairs { get; set; } = true;
public TextureShrinkMode TextureShrinkMode { get; set; } = TextureShrinkMode.Default; public TextureShrinkMode TextureShrinkMode { get; set; } = TextureShrinkMode.Default;
public bool TextureShrinkDeleteOriginal { get; set; } = false; public bool TextureShrinkDeleteOriginal { get; set; } = false;

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.1.7</Version> <Version>0.1.8.1</Version>
<PackageProjectUrl>https://github.com/Eauldane/SnowcloakClient/</PackageProjectUrl> <PackageProjectUrl>https://github.com/Eauldane/SnowcloakClient/</PackageProjectUrl>
</PropertyGroup> </PropertyGroup>
@@ -36,7 +36,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup Condition="!Exists('.\Penumbra.Api\Penumbra.Api.csproj')"> <ItemGroup Condition="!Exists('.\Penumbra.Api\Penumbra.Api.csproj')">
<PackageReference Include="Penumbra.Api" Version="5.10.0" /> <PackageReference Include="Penumbra.Api" Version="5.12.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="Exists('.\Glamourer.Api\Glamourer.Api.csproj')"> <ItemGroup Condition="Exists('.\Glamourer.Api\Glamourer.Api.csproj')">

View File

@@ -86,8 +86,8 @@ public class Pair : DisposableMediatorSubscriberBase
{ {
Name = name, Name = name,
OnClicked = action, OnClicked = action,
PrefixColor = 559, PrefixColor = 526,
PrefixChar = 'L' PrefixChar = 'S'
}); });
} }

View File

@@ -8,7 +8,7 @@
"Tags": [ "Tags": [
"customization" "customization"
], ],
"IconUrl": "https://i.imgur.com/cMKeOjm.jpeg", "IconUrl": "https://raw.githubusercontent.com/Eauldane/SnowcloakClient/refs/heads/main/MareSynchronos/images/logo.png",
"RepoUrl": "https://github.com/Eauldane/SnowcloakClient", "RepoUrl": "https://github.com/Eauldane/SnowcloakClient",
"CanUnloadAsync": true "CanUnloadAsync": true
} }

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 Vector4(1.0f, 0.8666f, 0.06666f, 1.0f); UiSharedService.AccentColor = new(0.4275f, 0.6863f, 1f, 1f);
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);

View File

@@ -106,8 +106,9 @@ public partial class IntroUi : WindowMediatorSubscriberBase
{ {
if (_uiShared.IsInGpose) return; if (_uiShared.IsInGpose) return;
if (!_configService.Current.AcceptedAgreement && !_readFirstPage) if ((!_configService.Current.AcceptedAgreement || _configService.Current.AcceptedTOSVersion != _configService.Current.ExpectedTOSVersion) && !_readFirstPage)
{ {
// TODO: The UI bugs hard if this page *isn't* shown before the new TOS. There's probably a way around it.
_uiShared.BigText("Welcome to Snowcloak"); _uiShared.BigText("Welcome to Snowcloak");
ImGui.Separator(); ImGui.Separator();
UiSharedService.TextWrapped("Snowcloak is a plugin that will replicate your full current character state including all Penumbra mods to other paired users. " + UiSharedService.TextWrapped("Snowcloak is a plugin that will replicate your full current character state including all Penumbra mods to other paired users. " +
@@ -125,7 +126,7 @@ public partial class IntroUi : WindowMediatorSubscriberBase
#if !DEBUG #if !DEBUG
_timeoutTask = Task.Run(async () => _timeoutTask = Task.Run(async () =>
{ {
for (int i = 10; i > 0; i--) for (int i = 45; i > 0; i--)
{ {
_timeoutLabel = $"'I agree' button will be available in {i}s"; _timeoutLabel = $"'I agree' button will be available in {i}s";
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false); await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
@@ -136,7 +137,7 @@ public partial class IntroUi : WindowMediatorSubscriberBase
#endif #endif
} }
} }
else if (!_configService.Current.AcceptedAgreement && _readFirstPage) else if ((!_configService.Current.AcceptedAgreement || _configService.Current.AcceptedTOSVersion != _configService.Current.ExpectedTOSVersion) && _readFirstPage)
{ {
using (_uiShared.UidFont.Push()) using (_uiShared.UidFont.Push())
{ {
@@ -152,6 +153,9 @@ public partial class IntroUi : WindowMediatorSubscriberBase
ImGui.SetWindowFontScale(1.0f); ImGui.SetWindowFontScale(1.0f);
ImGui.Separator(); ImGui.Separator();
UiSharedService.TextWrapped("""
To use Snowcloak, you must be over the age of 18, or 21 in some jurisdictions.
""");
UiSharedService.TextWrapped(""" UiSharedService.TextWrapped("""
All of the mod files currently active on your character as well as your current character state will be uploaded to the service you registered yourself at automatically. The plugin will exclusively upload the necessary mod files and not the whole mod. All of the mod files currently active on your character as well as your current character state will be uploaded to the service you registered yourself at automatically. The plugin will exclusively upload the necessary mod files and not the whole mod.
"""); """);
@@ -186,6 +190,7 @@ public partial class IntroUi : WindowMediatorSubscriberBase
if (ImGui.Button("I agree##toSetup")) if (ImGui.Button("I agree##toSetup"))
{ {
_configService.Current.AcceptedAgreement = true; _configService.Current.AcceptedAgreement = true;
_configService.Current.AcceptedTOSVersion = _configService.Current.ExpectedTOSVersion;
_configService.Save(); _configService.Save();
} }
} }
@@ -194,7 +199,7 @@ public partial class IntroUi : WindowMediatorSubscriberBase
UiSharedService.TextWrapped(_timeoutLabel); UiSharedService.TextWrapped(_timeoutLabel);
} }
} }
else if (_configService.Current.AcceptedAgreement else if ((!_configService.Current.AcceptedAgreement || _configService.Current.AcceptedTOSVersion != _configService.Current.ExpectedTOSVersion)
&& (string.IsNullOrEmpty(_configService.Current.CacheFolder) && (string.IsNullOrEmpty(_configService.Current.CacheFolder)
|| !_configService.Current.InitialScanComplete || !_configService.Current.InitialScanComplete
|| !Directory.Exists(_configService.Current.CacheFolder))) || !Directory.Exists(_configService.Current.CacheFolder)))

View File

@@ -581,32 +581,32 @@ public class SettingsUi : WindowMediatorSubscriberBase
_uiShared.BigText("Advanced"); _uiShared.BigText("Advanced");
bool mareApi = _configService.Current.MareAPI; bool mareApi = _configService.Current.MareAPI;
if (ImGui.Checkbox("Enable Mare Synchronos API", ref mareApi)) if (ImGui.Checkbox("Enable Snowcloak Sync API", ref mareApi))
{ {
_configService.Current.MareAPI = mareApi; _configService.Current.MareAPI = mareApi;
_configService.Save(); _configService.Save();
_ipcProvider.HandleMareImpersonation(); _ipcProvider.HandleMareImpersonation();
} }
_uiShared.DrawHelpText("Enables handling of the Mare Synchronos API. This currently includes:\n\n" + _uiShared.DrawHelpText("Enables handling of the Snowcloak Sync API. This currently includes:\n\n" +
" - MCDF loading support for other plugins\n" + " - MCDF loading support for other plugins\n" +
" - Blocking Moodles applications to paired users\n\n" + " - Blocking Moodles applications to paired users\n\n" +
"If the Mare Synchronos plugin is loaded while this option is enabled, control of its API will be relinquished."); "If the Snowcloak Sync plugin is loaded while this option is enabled, control of its API will be relinquished.");
using (_ = ImRaii.PushIndent()) using (_ = ImRaii.PushIndent())
{ {
ImGui.SameLine(300.0f * ImGuiHelpers.GlobalScale); ImGui.SameLine(300.0f * ImGuiHelpers.GlobalScale);
if (_ipcProvider.ImpersonationActive) if (_ipcProvider.ImpersonationActive)
{ {
UiSharedService.ColorTextWrapped("Mare API active!", ImGuiColors.HealerGreen); UiSharedService.ColorTextWrapped("Snowcloak API active!", ImGuiColors.HealerGreen);
} }
else else
{ {
if (!mareApi) if (!mareApi)
UiSharedService.ColorTextWrapped("Mare API inactive: Option is disabled", ImGuiColors.DalamudYellow); UiSharedService.ColorTextWrapped("Snowcloak API inactive: Option is disabled", ImGuiColors.DalamudYellow);
else if (_ipcProvider.MarePluginEnabled) else if (_ipcProvider.MarePluginEnabled)
UiSharedService.ColorTextWrapped("Mare API inactive: Mare plugin is loaded", ImGuiColors.DalamudYellow); UiSharedService.ColorTextWrapped("Snowcloak API inactive: Snowcloak plugin is loaded", ImGuiColors.DalamudYellow);
else else
UiSharedService.ColorTextWrapped("Mare API inactive: Unknown reason", ImGuiColors.DalamudRed); UiSharedService.ColorTextWrapped("Snowcloak API inactive: Unknown reason", ImGuiColors.DalamudRed);
} }
} }

View File

@@ -113,9 +113,9 @@
}, },
"Penumbra.Api": { "Penumbra.Api": {
"type": "Direct", "type": "Direct",
"requested": "[5.10.0, )", "requested": "[5.12.0, )",
"resolved": "5.10.0", "resolved": "5.12.0",
"contentHash": "ZRIIXQCluNlLoI4I4gNYuYXqb7p48OR1P2/LcL27owToR/A/IG0mH8Ks2HguB/Kjq4RpCinaVkyufpGxGspbTA==" "contentHash": "XGWviAZgokj2djpH50FWgM24jOTpKUuDHvd0HwrzBRY6BEMmpb3HfGIl8+BDE/DqbpH63u6aO2TvzUV6BmXT5w=="
}, },
"SonarAnalyzer.CSharp": { "SonarAnalyzer.CSharp": {
"type": "Direct", "type": "Direct",

View File

@@ -1,5 +1,6 @@
# Snowcloak Sync # Snowcloak Sync
The snow may cloak the world in silence, but come in, warm up, and reveal your true colours.
Come in from the cold, and be your true self.
A Dalamud plugin. A Dalamud plugin.
[![Discord](https://img.shields.io/discord/1408265972720078990?color=5865F2&label=discord&logo=discord&logoColor=white)](https://discord.gg/snowcloak)