Skip to content

Commit 5bf3dcb

Browse files
committed
2.8.0
1 parent c6e33f1 commit 5bf3dcb

21 files changed

Lines changed: 1876 additions & 231 deletions

Snowcloak/Configuration/Configurations/SnowcloakConfig.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public class SnowcloakConfig : ISnowcloakConfiguration
4646
public DownloadSpeeds DownloadSpeedType { get; set; } = DownloadSpeeds.MBps;
4747
public float ProfileDelay { get; set; } = 1.5f;
4848
public bool ProfilePopoutRight { get; set; } = false;
49+
public bool ProfileEditorTutorialSeen { get; set; } = false;
4950
public bool ProfilesAllowNsfw { get; set; } = false;
5051
public bool ProfilesShow { get; set; } = false;
5152
public bool ShowSyncshellUsersInVisible { get; set; } = true;
@@ -94,6 +95,10 @@ public class SnowcloakConfig : ISnowcloakConfiguration
9495
public ElezenStrings.Colour PairRequestNameColors { get; set; } = new(Foreground: 0x87D745u, Glow: 0x7A6E0Eu);
9596
public bool PairingSystemEnabled { get; set; } = false;
9697
public bool FrostbrandWelcomeSeen { get; set; } = false;
98+
public bool FrostbrandUseProfileCards { get; set; } = false;
99+
public bool FrostbrandOnlyWithProfiles { get; set; } = false;
100+
public string FrostbrandProfileSearch { get; set; } = string.Empty;
101+
public string FrostbrandRequiredTag { get; set; } = string.Empty;
97102
public HashSet<AutoRejectCombo> AutoRejectCombos { get; set; } = [];
98103
public int PairRequestMinimumLevel { get; set; } = 15;
99104
public HashSet<ushort> PairRequestRejectedHomeworlds { get; set; } = [];

Snowcloak/PlayerData/Moodles/ModNullificationMoodles.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ public static bool TryAppend(string? moodlesData, ModNullificationKind kinds, ou
1616
return true;
1717
}
1818

19-
if (!MoodlesDataParser.TryParse(moodlesData, out var parsedStatuses))
19+
if (!MoodlesDataParser.TryParsePayload(moodlesData, out var payload))
2020
{
2121
return false;
2222
}
2323

24-
var statuses = parsedStatuses
24+
var statuses = payload.Statuses
2525
.Where(status => status.GUID != HeightGuid && status.GUID != VfxGuid && status.GUID != SfxGuid)
2626
.ToList();
2727

@@ -43,7 +43,9 @@ public static bool TryAppend(string? moodlesData, ModNullificationKind kinds, ou
4343
"Snowcloak is nullifying this player's active SFX mod in line with your settings."));
4444
}
4545

46-
return MoodlesDataParser.TrySerialize(statuses, out composedMoodlesData);
46+
payload.Statuses.Clear();
47+
payload.Statuses.AddRange(statuses);
48+
return MoodlesDataParser.TrySerializePayload(payload, out composedMoodlesData);
4749
}
4850

4951
private static MoodlesStatusData CreateStatus(Guid guid, int icon, string title, string description)

Snowcloak/PlayerData/Moodles/MoodlesStatusData.cs

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ public partial class MoodlesStatusData
1919
public MoodlesChainTrigger ChainTrigger;
2020
public string Applier = string.Empty;
2121
public string Dispeller = string.Empty;
22+
public bool Persistent;
23+
public int Days;
24+
public int Hours;
25+
public int Minutes;
26+
public int Seconds;
27+
public bool NoExpire;
28+
public bool AsPermanent;
2229
}
2330

2431
public enum MoodlesStatusType
@@ -58,6 +65,16 @@ public static class MoodlesDataParser
5865
public static bool TryParse(string? base64, out IReadOnlyList<MoodlesStatusData> statuses)
5966
{
6067
statuses = Array.Empty<MoodlesStatusData>();
68+
if (!TryParsePayload(base64, out var payload))
69+
return false;
70+
71+
statuses = payload.Statuses;
72+
return true;
73+
}
74+
75+
public static bool TryParsePayload(string? base64, out MoodlesStatusPayload payload)
76+
{
77+
payload = new MoodlesStatusPayload([], null);
6178
if (string.IsNullOrWhiteSpace(base64))
6279
{
6380
return true;
@@ -66,8 +83,73 @@ public static bool TryParse(string? base64, out IReadOnlyList<MoodlesStatusData>
6683
try
6784
{
6885
var data = Convert.FromBase64String(base64);
69-
var parsed = MemoryPackSerializer.Deserialize<List<MoodlesStatusData>>(data, SerializerOptions);
70-
statuses = parsed != null ? parsed : Array.Empty<MoodlesStatusData>();
86+
var parsedStatuses = TryDeserializeStatuses(data, out var parsed);
87+
var parsedManager = TryDeserializeManager(data, out var manager);
88+
89+
if (parsedStatuses && HasRenderableStatuses(parsed))
90+
{
91+
payload = new MoodlesStatusPayload(parsed, null);
92+
return true;
93+
}
94+
95+
if (parsedManager && HasRenderableStatuses(manager.Statuses))
96+
{
97+
payload = new MoodlesStatusPayload(manager.Statuses, manager);
98+
return true;
99+
}
100+
101+
if (parsedStatuses)
102+
{
103+
payload = new MoodlesStatusPayload(parsed, null);
104+
return true;
105+
}
106+
107+
if (parsedManager)
108+
{
109+
payload = new MoodlesStatusPayload(manager.Statuses, manager);
110+
return true;
111+
}
112+
}
113+
catch
114+
{
115+
// handled by returning false below
116+
}
117+
118+
return false;
119+
}
120+
121+
public static bool TrySerializePayload(MoodlesStatusPayload payload, out string base64)
122+
{
123+
base64 = string.Empty;
124+
try
125+
{
126+
if (payload.Manager != null)
127+
{
128+
payload.Manager.Statuses = payload.Statuses;
129+
var managerData = MemoryPackSerializer.Serialize(payload.Manager, SerializerOptions);
130+
base64 = Convert.ToBase64String(managerData);
131+
return true;
132+
}
133+
134+
return TrySerialize(payload.Statuses, out base64);
135+
}
136+
catch
137+
{
138+
return false;
139+
}
140+
}
141+
142+
private static bool TryDeserializeManager(byte[] data, out MoodlesStatusManagerData manager)
143+
{
144+
manager = new MoodlesStatusManagerData();
145+
try
146+
{
147+
var parsed = MemoryPackSerializer.Deserialize<MoodlesStatusManagerData>(data, SerializerOptions);
148+
if (parsed == null)
149+
return false;
150+
151+
manager = parsed;
152+
manager.Statuses ??= [];
71153
return true;
72154
}
73155
catch
@@ -76,6 +158,25 @@ public static bool TryParse(string? base64, out IReadOnlyList<MoodlesStatusData>
76158
}
77159
}
78160

161+
private static bool TryDeserializeStatuses(byte[] data, out List<MoodlesStatusData> statuses)
162+
{
163+
statuses = [];
164+
try
165+
{
166+
statuses = MemoryPackSerializer.Deserialize<List<MoodlesStatusData>>(data, SerializerOptions) ?? [];
167+
return true;
168+
}
169+
catch
170+
{
171+
return false;
172+
}
173+
}
174+
175+
private static bool HasRenderableStatuses(IEnumerable<MoodlesStatusData>? statuses)
176+
=> statuses?.Any(status => status.IconID > 0
177+
|| !string.IsNullOrWhiteSpace(status.Title)
178+
|| !string.IsNullOrWhiteSpace(status.Description)) == true;
179+
79180
public static bool TrySerialize(IEnumerable<MoodlesStatusData> statuses, out string base64)
80181
{
81182
base64 = string.Empty;
@@ -91,3 +192,25 @@ public static bool TrySerialize(IEnumerable<MoodlesStatusData> statuses, out str
91192
}
92193
}
93194
}
195+
196+
public sealed class MoodlesStatusPayload
197+
{
198+
internal MoodlesStatusPayload(List<MoodlesStatusData> statuses, MoodlesStatusManagerData? manager)
199+
{
200+
Statuses = statuses;
201+
Manager = manager;
202+
}
203+
204+
public List<MoodlesStatusData> Statuses { get; }
205+
internal MoodlesStatusManagerData? Manager { get; }
206+
}
207+
208+
[MemoryPackable]
209+
public partial class MoodlesStatusManagerData
210+
{
211+
public List<Guid> AddTextShown = [];
212+
public List<Guid> RemTextShown = [];
213+
public List<MoodlesStatusData> Statuses = [];
214+
public bool Ephemeral;
215+
public bool WasTouchedByIPC;
216+
}

Snowcloak/PlayerData/Pairs/Pair.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ void Add(string name, Action<IMenuItemClickedArgs>? action)
139139
bool isBlacklisted = _serverConfigurationManager.IsUserBlacklisted(UserData);
140140
bool isWhitelisted = _serverConfigurationManager.IsUserWhitelisted(UserData);
141141

142-
Add("Open Profile", _ => Mediator.Publish(new ProfileOpenStandaloneMessage(UserData, this)));
142+
Add("Open Profile", _ => Mediator.Publish(new ProfileOpenStandaloneMessage(UserData, this, FallbackName: PlayerName)));
143143

144144
if (!isBlocked && !isBlacklisted)
145145
Add("Always Block Modded Appearance", _ => {

Snowcloak/Services/CharacterProfileBackupService.cs

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,50 @@ public IReadOnlyList<CharacterProfileBackup> GetBackups()
3131
}
3232
}
3333

34-
public void Save(string ident, string characterLabel, ProfileVisibility visibility, CharacterProfileDocumentDto document)
34+
public CharacterProfileDraft? GetDraft(string ident)
35+
{
36+
if (string.IsNullOrWhiteSpace(ident)) return null;
37+
38+
lock (_syncRoot)
39+
{
40+
var draft = _file.Drafts.FirstOrDefault(d => d.Ident.Equals(ident, StringComparison.Ordinal));
41+
return draft == null ? null : Clone(draft);
42+
}
43+
}
44+
45+
public void SaveDraft(string ident, string characterLabel, CharacterProfileDocumentDto document)
46+
{
47+
if (string.IsNullOrWhiteSpace(ident)) return;
48+
49+
lock (_syncRoot)
50+
{
51+
var draft = _file.Drafts.FirstOrDefault(d => d.Ident.Equals(ident, StringComparison.Ordinal));
52+
if (draft == null)
53+
{
54+
draft = new CharacterProfileDraft { Ident = ident };
55+
_file.Drafts.Add(draft);
56+
}
57+
58+
if (!string.IsNullOrWhiteSpace(characterLabel))
59+
draft.CharacterLabel = characterLabel.Trim();
60+
draft.Document = document;
61+
draft.UpdatedAtUtc = DateTimeOffset.UtcNow;
62+
Persist();
63+
}
64+
}
65+
66+
public void ClearDraft(string ident)
67+
{
68+
if (string.IsNullOrWhiteSpace(ident)) return;
69+
70+
lock (_syncRoot)
71+
{
72+
if (_file.Drafts.RemoveAll(d => d.Ident.Equals(ident, StringComparison.Ordinal)) > 0)
73+
Persist();
74+
}
75+
}
76+
77+
public void Save(string ident, string characterLabel, CharacterProfileDocumentDto document)
3578
{
3679
if (string.IsNullOrWhiteSpace(ident)) return;
3780

@@ -49,21 +92,18 @@ public void Save(string ident, string characterLabel, ProfileVisibility visibili
4992
if (!string.IsNullOrWhiteSpace(characterLabel))
5093
backup.CharacterLabel = characterLabel.Trim();
5194

52-
if (visibility == ProfileVisibility.Public)
53-
backup.PublicProfile = document;
54-
else
55-
backup.PrivateProfile = document;
95+
backup.Profile = document;
5696
backup.UpdatedAtUtc = DateTimeOffset.UtcNow;
5797
Persist();
5898
}
5999
}
60100

61-
public CharacterProfileDocumentDto? GetDocument(Guid backupId, ProfileVisibility visibility)
101+
public CharacterProfileDocumentDto? GetDocument(Guid backupId)
62102
{
63103
lock (_syncRoot)
64104
{
65105
var backup = _file.Profiles.SingleOrDefault(p => p.Id == backupId);
66-
return visibility == ProfileVisibility.Public ? backup?.PublicProfile : backup?.PrivateProfile;
106+
return backup?.Profile ?? backup?.PrivateProfile ?? backup?.PublicProfile;
67107
}
68108
}
69109

@@ -104,16 +144,29 @@ private static CharacterProfileBackup Clone(CharacterProfileBackup profile)
104144
Id = profile.Id,
105145
CharacterLabel = profile.CharacterLabel,
106146
KnownIdents = [.. profile.KnownIdents],
147+
Profile = profile.Profile,
107148
PublicProfile = profile.PublicProfile,
108149
PrivateProfile = profile.PrivateProfile,
109150
UpdatedAtUtc = profile.UpdatedAtUtc,
110151
};
111152
}
112153

154+
private static CharacterProfileDraft Clone(CharacterProfileDraft draft)
155+
{
156+
return new CharacterProfileDraft
157+
{
158+
Ident = draft.Ident,
159+
CharacterLabel = draft.CharacterLabel,
160+
Document = draft.Document,
161+
UpdatedAtUtc = draft.UpdatedAtUtc,
162+
};
163+
}
164+
113165
private sealed class CharacterProfileBackupFile
114166
{
115-
public int Version { get; set; } = 1;
167+
public int Version { get; set; } = 2;
116168
public List<CharacterProfileBackup> Profiles { get; set; } = [];
169+
public List<CharacterProfileDraft> Drafts { get; set; } = [];
117170
}
118171
}
119172

@@ -122,7 +175,16 @@ public sealed class CharacterProfileBackup
122175
public Guid Id { get; set; } = Guid.NewGuid();
123176
public string CharacterLabel { get; set; } = string.Empty;
124177
public List<string> KnownIdents { get; set; } = [];
178+
public CharacterProfileDocumentDto? Profile { get; set; }
125179
public CharacterProfileDocumentDto? PublicProfile { get; set; }
126180
public CharacterProfileDocumentDto? PrivateProfile { get; set; }
127181
public DateTimeOffset UpdatedAtUtc { get; set; } = DateTimeOffset.UtcNow;
128182
}
183+
184+
public sealed class CharacterProfileDraft
185+
{
186+
public string Ident { get; set; } = string.Empty;
187+
public string CharacterLabel { get; set; } = string.Empty;
188+
public CharacterProfileDocumentDto Document { get; set; } = new();
189+
public DateTimeOffset UpdatedAtUtc { get; set; } = DateTimeOffset.UtcNow;
190+
}

Snowcloak/Services/Mediator/Messages.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public record DownloadFinishedMessage(GameObjectHandler DownloadId, string? UID
7878
public record UiToggleMessage(Type UiType) : MessageBase;
7979
public record PlayerUploadingMessage(GameObjectHandler Handler, bool IsUploading) : MessageBase;
8080
public record ClearProfileDataMessage(UserData? UserData = null, ProfileVisibility? Visibility = null) : MessageBase;
81-
public record ClearCharacterProfileDataMessage(string? Ident = null, ProfileVisibility? Visibility = null) : MessageBase;
81+
public record ClearCharacterProfileDataMessage(string? Ident = null, ProfileVisibility? Visibility = null, bool PreserveSummary = false) : MessageBase;
8282
public record CyclePauseMessage(UserData UserData) : MessageBase;
8383
public record PauseMessage(UserData UserData) : MessageBase;
8484
public record ProfilePopoutToggle(Pair? Pair) : MessageBase;

0 commit comments

Comments
 (0)