Merge pull request #19830 from peppy/test-ruleset-compatibility

Test ruleset compatibility during initial startup to avoid runtime errors
This commit is contained in:
Dan Balasescu 2022-08-22 15:24:46 +09:00 committed by GitHub
commit 2bc0a68911
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 8 deletions

View File

@ -21,8 +21,11 @@ namespace osu.Game.Rulesets.Pippidon.Beatmaps
public PippidonBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) public PippidonBeatmapConverter(IBeatmap beatmap, Ruleset ruleset)
: base(beatmap, ruleset) : base(beatmap, ruleset)
{ {
minPosition = beatmap.HitObjects.Min(getUsablePosition); if (beatmap.HitObjects.Any())
maxPosition = beatmap.HitObjects.Max(getUsablePosition); {
minPosition = beatmap.HitObjects.Min(getUsablePosition);
maxPosition = beatmap.HitObjects.Max(getUsablePosition);
}
} }
public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition && h is IHasYPosition); public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition && h is IHasYPosition);

View File

@ -5,8 +5,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Logging;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Beatmaps;
using osu.Game.Database; using osu.Game.Database;
namespace osu.Game.Rulesets namespace osu.Game.Rulesets
@ -68,8 +68,14 @@ namespace osu.Game.Rulesets
{ {
try try
{ {
var resolvedType = Type.GetType(r.InstantiationInfo) var resolvedType = Type.GetType(r.InstantiationInfo);
?? throw new RulesetLoadException(@"Type could not be resolved");
if (resolvedType == null)
{
// ruleset DLL was probably deleted.
r.Available = false;
continue;
}
var instanceInfo = (Activator.CreateInstance(resolvedType) as Ruleset)?.RulesetInfo var instanceInfo = (Activator.CreateInstance(resolvedType) as Ruleset)?.RulesetInfo
?? throw new RulesetLoadException(@"Instantiation failure"); ?? throw new RulesetLoadException(@"Instantiation failure");
@ -83,17 +89,35 @@ namespace osu.Game.Rulesets
r.InstantiationInfo = instanceInfo.InstantiationInfo; r.InstantiationInfo = instanceInfo.InstantiationInfo;
r.Available = true; r.Available = true;
testRulesetCompatibility(r);
detachedRulesets.Add(r.Clone()); detachedRulesets.Add(r.Clone());
} }
catch (Exception ex) catch (Exception ex)
{ {
r.Available = false; r.Available = false;
Logger.Log($"Could not load ruleset {r}: {ex.Message}"); LogFailedLoad(r.Name, ex);
} }
} }
availableRulesets.AddRange(detachedRulesets.OrderBy(r => r)); availableRulesets.AddRange(detachedRulesets.OrderBy(r => r));
}); });
} }
private void testRulesetCompatibility(RulesetInfo rulesetInfo)
{
// do various operations to ensure that we are in a good state.
// if we can avoid loading the ruleset at this point (rather than erroring later in runtime) then that is preferred.
var instance = rulesetInfo.CreateInstance();
instance.CreateAllMods();
instance.CreateIcon();
instance.CreateResourceStore();
var beatmap = new Beatmap();
var converter = instance.CreateBeatmapConverter(beatmap);
instance.CreateBeatmapProcessor(converter.Convert());
}
} }
} }

View File

@ -138,7 +138,7 @@ namespace osu.Game.Rulesets
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Error(e, $"Failed to load ruleset {filename}"); LogFailedLoad(filename, e);
} }
} }
@ -158,7 +158,7 @@ namespace osu.Game.Rulesets
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Error(e, $"Failed to add ruleset {assembly}"); LogFailedLoad(assembly.FullName, e);
} }
} }
@ -173,6 +173,12 @@ namespace osu.Game.Rulesets
AppDomain.CurrentDomain.AssemblyResolve -= resolveRulesetDependencyAssembly; AppDomain.CurrentDomain.AssemblyResolve -= resolveRulesetDependencyAssembly;
} }
protected void LogFailedLoad(string name, Exception exception)
{
Logger.Log($"Could not load ruleset {name}. Please check for an update from the developer.", level: LogLevel.Error);
Logger.Log($"Ruleset load failed: {exception}");
}
#region Implementation of IRulesetStore #region Implementation of IRulesetStore
IRulesetInfo? IRulesetStore.GetRuleset(int id) => GetRuleset(id); IRulesetInfo? IRulesetStore.GetRuleset(int id) => GetRuleset(id);