Skip to content

Commit 0a3f605

Browse files
committed
feat(Saves): enhance progress record validation suppression and model ID management
- Introduced methods to suppress expected validation warnings during deserialization, improving the handling of unavailable preserved records. - Added functionality to gather preserved model IDs, enhancing the accuracy of validation error checks. - Updated the Postfix method to call the new suppression logic, ensuring expected warnings are managed effectively during progress state operations.
1 parent 01b736e commit 0a3f605

2 files changed

Lines changed: 77 additions & 1 deletion

File tree

Saves/Patches/ProgressStatePreserveUnknownRecordsPatches.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,12 @@ public static void Prefix(SerializableProgress save, out PreservedProgressRecord
4444
/// Attaches the snapshot to the parsed progress instance.
4545
/// </summary>
4646
// ReSharper disable InconsistentNaming
47-
public static void Postfix(ProgressState __result, PreservedProgressRecords? __state)
47+
public static void Postfix(ProgressState __result, DeserializationContext ctx,
48+
PreservedProgressRecords? __state)
4849
// ReSharper restore InconsistentNaming
4950
{
5051
PreservedProgressRecords.Attach(__result, __state);
52+
__state?.SuppressExpectedWarnings(ctx);
5153
}
5254
}
5355

Saves/PreservedProgressRecords.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
using System.Reflection;
12
using System.Runtime.CompilerServices;
23
using System.Text.Json;
34
using MegaCrit.Sts2.Core.Achievements;
45
using MegaCrit.Sts2.Core.Models;
56
using MegaCrit.Sts2.Core.Saves;
7+
using MegaCrit.Sts2.Core.Saves.Validation;
68
using MegaCrit.Sts2.Core.Timeline;
79

810
namespace STS2RitsuLib.Saves
@@ -15,6 +17,10 @@ public sealed class PreservedProgressRecords
1517
{
1618
private static readonly ConditionalWeakTable<ProgressState, PreservedProgressRecords> RecordsByProgress = new();
1719
private static readonly HashSet<string> KnownAchievementNames = BuildKnownAchievementNames();
20+
21+
private static readonly FieldInfo? ValidationErrorsField =
22+
typeof(DeserializationContext).GetField("_errors", BindingFlags.Instance | BindingFlags.NonPublic);
23+
1824
private readonly List<AncientStats> _ancientStats = [];
1925
private readonly List<CardStats> _cardStats = [];
2026

@@ -80,6 +86,19 @@ internal static void Attach(ProgressState? progress, PreservedProgressRecords? r
8086
RitsuLibFramework.Logger.Info($"[Saves] Preserving unavailable progress records: {records.FormatCounts()}");
8187
}
8288

89+
internal int SuppressExpectedWarnings(DeserializationContext ctx)
90+
{
91+
if (ValidationErrorsField?.GetValue(ctx) is not List<ValidationError> errors)
92+
return 0;
93+
94+
var removed = errors.RemoveAll(IsExpectedPreservedWarning);
95+
if (removed > 0)
96+
RitsuLibFramework.Logger.Info(
97+
$"[Saves] Suppressed {removed} expected progress validation warning(s) for unavailable preserved records");
98+
99+
return removed;
100+
}
101+
83102
internal static void MergeInto(ProgressState? progress, SerializableProgress? save)
84103
{
85104
if (progress == null || save == null)
@@ -306,6 +325,61 @@ private static string FormatCount(string label, int count)
306325
return count > 0 ? $"{label}={count}" : "";
307326
}
308327

328+
private bool IsExpectedPreservedWarning(ValidationError error)
329+
{
330+
if (error.IsFatal || !error.Message.StartsWith("Unknown ", StringComparison.Ordinal))
331+
return false;
332+
333+
var modelIds = GetPreservedModelIdStrings();
334+
foreach (var id in modelIds)
335+
if (error.Message.Contains(id, StringComparison.Ordinal))
336+
return true;
337+
338+
foreach (var epochId in _epochs.Select(static epoch => epoch.Id))
339+
if (error.Message.Contains(epochId, StringComparison.Ordinal))
340+
return true;
341+
342+
foreach (var achievement in _unlockedAchievements.Select(static item => item.Achievement))
343+
if (error.Message.Contains('"' + achievement + '"', StringComparison.Ordinal))
344+
return true;
345+
346+
return false;
347+
}
348+
349+
private HashSet<string> GetPreservedModelIdStrings()
350+
{
351+
var ids = new HashSet<string>(StringComparer.Ordinal);
352+
AddIds(ids, _characterStats.Select(static stats => stats.Id));
353+
AddIds(ids, _cardStats.Select(static stats => stats.Id));
354+
AddIds(ids, _encounterStats.Select(static stats => stats.Id));
355+
AddIds(ids, _enemyStats.Select(static stats => stats.Id));
356+
AddIds(ids, _ancientStats.Select(static stats => stats.Id));
357+
AddIds(ids, _encounterStats.SelectMany(static stats =>
358+
stats.FightStats.Select(static fight => fight.Character)));
359+
AddIds(ids,
360+
_enemyStats.SelectMany(static stats => stats.FightStats.Select(static fight => fight.Character)));
361+
AddIds(ids, _ancientStats.SelectMany(static stats =>
362+
stats.CharStats.Select(static charStats => charStats.Character)));
363+
AddIds(ids, _discoveredCards);
364+
AddIds(ids, _discoveredRelics);
365+
AddIds(ids, _discoveredPotions);
366+
AddIds(ids, _discoveredEvents);
367+
AddIds(ids, _discoveredActs);
368+
369+
if (_pendingCharacterUnlock is { } pendingCharacterUnlock &&
370+
pendingCharacterUnlock != ModelId.none)
371+
ids.Add(pendingCharacterUnlock.ToString());
372+
373+
return ids;
374+
}
375+
376+
private static void AddIds(HashSet<string> target, IEnumerable<ModelId?> source)
377+
{
378+
foreach (var id in source)
379+
if (id is { } modelId && modelId != ModelId.none)
380+
target.Add(modelId.ToString());
381+
}
382+
309383
private static bool IsUnknownModel<TModel>(ModelId? id)
310384
where TModel : AbstractModel
311385
{

0 commit comments

Comments
 (0)