Further attempt to resolve profile corruption issues (#650)

* Further attempt to resolve profile corruption issues
- FileUtil now uses File.Replace and does a sync flush
- Add restore capabilities to BackupService
- If loading a profile fails, attempt to restore from the most recent backup
- Trigger a backup creation on raid start, raid end, and game close
- Load profiles before starting the backupService to avoid backing up corrupt profiles

* - Switch async calls to .GetAwaiter().GetResult() for better exception handling

---------

Co-authored-by: DrakiaXYZ <565558+TheDgtl@users.noreply.github.com>
This commit is contained in:
DrakiaXYZ
2025-10-22 22:49:24 -07:00
committed by GitHub
parent bda2c8757c
commit 47089afdd1
6 changed files with 97 additions and 4 deletions
@@ -1,5 +1,6 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Nodes;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.DI;
@@ -22,6 +23,7 @@ public class SaveServer(
HashUtil hashUtil,
ServerLocalisationService serverLocalisationService,
ProfileValidatorService profileValidatorService,
BackupService backupService,
ISptLogger<SaveServer> logger,
ConfigServer configServer
)
@@ -211,7 +213,31 @@ public class SaveServer(
if (fileUtil.FileExists(filePath))
// File found, store in profiles[]
{
var profile = await jsonUtil.DeserializeFromFileAsync<JsonObject>(filePath);
JsonObject? profile = null;
try
{
profile = await jsonUtil.DeserializeFromFileAsync<JsonObject>(filePath);
}
catch (JsonException e)
{
// If the profile fails to deserialize, it may have corrupted, try to restore from a backup
logger.Warning($"Failed loading profile for {sessionID.ToString()}. Attempting to load backup");
// We make a copy of the profile before overwriting it, just incase
var corruptBackupPath = $"{profileFilepath}{sessionID.ToString()}-corrupt.json";
File.Copy(filePath, corruptBackupPath, true);
if (backupService.RestoreProfile(sessionID))
{
profile = await jsonUtil.DeserializeFromFileAsync<JsonObject>(filePath);
logger.Success("Profile restored from backup!");
}
else
{
throw new Exception("Failed to restore profile backup", e);
}
}
if (profile is not null)
{