Remove unecessary casting, handle sorting migrations in extension

This commit is contained in:
Archangel
2025-08-05 04:10:13 +02:00
parent 0b250161be
commit be289d590d
4 changed files with 75 additions and 66 deletions
@@ -0,0 +1,59 @@
using SPTarkov.Server.Core.Migration;
namespace SPTarkov.Server.Core.Extensions;
public static class ProfileMigratorExtensions
{
public static IEnumerable<IProfileMigration> Sort(this IEnumerable<IProfileMigration> profileMigrations)
{
var sortedMigrations = new List<IProfileMigration>();
var visitedMigrations = new Dictionary<Type, bool>();
var migrationDict = profileMigrations.ToDictionary(m => m.GetType());
foreach (var migration in profileMigrations)
{
VisitMigrationForSort(migration, migrationDict, visitedMigrations, sortedMigrations);
}
return sortedMigrations;
}
internal static void VisitMigrationForSort(
IProfileMigration migration,
Dictionary<Type, IProfileMigration> migrationTypeDictionary,
Dictionary<Type, bool> visitedTypeDictionary,
List<IProfileMigration> sortedMigrations
)
{
var migrationType = migration.GetType();
if (visitedTypeDictionary.TryGetValue(migrationType, out var isVisited))
{
if (isVisited)
{
return;
}
// Big error, two migrations should never depend on one another
throw new InvalidOperationException($"Cycle detected in migration prerequisites involving: {migrationType.Name}");
}
// Mark the current migration type for visiting
visitedTypeDictionary[migrationType] = false;
foreach (var prerequisiteType in migration.PrerequisiteMigrations)
{
if (!migrationTypeDictionary.TryGetValue(prerequisiteType, out var prereqMigration))
{
continue;
}
// Visit the next prerequisite
VisitMigrationForSort(prereqMigration, migrationTypeDictionary, visitedTypeDictionary, sortedMigrations);
}
// Done visiting, mark it as fully visited and add it to the sorted migrations
visitedTypeDictionary[migrationType] = true;
sortedMigrations.Add(migration);
}
}
@@ -5,11 +5,11 @@ namespace SPTarkov.Server.Core.Migration;
public abstract class AbstractProfileMigration : IProfileMigration
{
public virtual string MigrationName { get; }
public virtual IEnumerable<Type> PrerequisiteMigrations { get; }
public abstract string FromVersion { get; }
public abstract string ToVersion { get; }
public abstract string MigrationName { get; }
public abstract IEnumerable<Type> PrerequisiteMigrations { get; }
public abstract bool CanMigrate(JsonObject profile, IEnumerable<IProfileMigration> previouslyRanMigrations);
@@ -5,6 +5,16 @@ namespace SPTarkov.Server.Core.Migration;
public interface IProfileMigration
{
/// <summary>
/// The name of the migration
/// </summary>
public abstract string MigrationName { get; }
/// <summary>
/// An IEnumerable of migrations that need to come before the current one
/// </summary>
public abstract IEnumerable<Type> PrerequisiteMigrations { get; }
/// <summary>
/// Allows for adding checks if the profile in question can migrate
/// </summary>
@@ -1,6 +1,7 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Migration;
using SPTarkov.Server.Core.Models.Eft.Profile;
using SPTarkov.Server.Core.Models.Utils;
@@ -15,18 +16,10 @@ public class ProfileMigratorService(
ISptLogger<ProfileMigratorService> logger
)
{
private IEnumerable<AbstractProfileMigration> _sortedMigrations = [];
private readonly IEnumerable<IProfileMigration> _sortedMigrations = profileMigrations.Sort();
public SptProfile HandlePendingMigrations(JsonObject profile)
{
// On the initial run, begin sorting our migrations
// This will sort it so that any non-prerequisite migrations go first
// And then all the prerequisite ones.
if (!_sortedMigrations.Any())
{
_sortedMigrations = SortMigrations();
}
var profileId = profile["info"]?["id"]?.GetValue<string>();
// Profile is due for a wipe or a reset, do not continue here.
@@ -40,7 +33,7 @@ public class ProfileMigratorService(
?? throw new InvalidOperationException($"Could not deserialize the profile: {profileId}");
}
var ranMigrations = new List<AbstractProfileMigration>();
var ranMigrations = new List<IProfileMigration>();
foreach (var profileMigration in _sortedMigrations)
{
@@ -98,57 +91,4 @@ public class ProfileMigratorService(
return sptReadyProfile;
}
protected IEnumerable<AbstractProfileMigration> SortMigrations()
{
var sortedMigrations = new List<AbstractProfileMigration>();
var visitedMigrations = new Dictionary<Type, bool>();
var migrationDict = profileMigrations.Cast<AbstractProfileMigration>().ToDictionary(m => m.GetType());
foreach (var migration in profileMigrations.Cast<AbstractProfileMigration>())
{
VisitMigrationForSort(migration, migrationDict, visitedMigrations, sortedMigrations);
}
return sortedMigrations;
}
protected void VisitMigrationForSort(
AbstractProfileMigration migration,
Dictionary<Type, AbstractProfileMigration> migrationTypeDictionary,
Dictionary<Type, bool> visitedTypeDictionary,
List<AbstractProfileMigration> sortedMigrations
)
{
var migrationType = migration.GetType();
if (visitedTypeDictionary.TryGetValue(migrationType, out var isVisited))
{
if (isVisited)
{
return;
}
// Big error, two migrations should never depend on one another
throw new InvalidOperationException($"Cycle detected in migration prerequisites involving: {migrationType.Name}");
}
// Mark the current migration type for visiting
visitedTypeDictionary[migrationType] = false;
foreach (var prerequisiteType in migration.PrerequisiteMigrations)
{
if (!migrationTypeDictionary.TryGetValue(prerequisiteType, out var prereqMigration))
{
continue;
}
// Visit the next prerequisite
VisitMigrationForSort(prereqMigration, migrationTypeDictionary, visitedTypeDictionary, sortedMigrations);
}
// Done visiting, mark it as fully visited and add it to the sorted migrations
visitedTypeDictionary[migrationType] = true;
sortedMigrations.Add(migration);
}
}