mirror of
https://github.com/ppy/osu.git
synced 2024-12-13 03:42:57 +08:00
Attempt to disable rulesets that can be linked to an unhandled crash
This commit is contained in:
parent
77660e57ea
commit
c7c0330265
@ -678,12 +678,14 @@ namespace osu.Game
|
||||
/// <summary>
|
||||
/// Allows a maximum of one unhandled exception, per second of execution.
|
||||
/// </summary>
|
||||
private bool onExceptionThrown(Exception _)
|
||||
private bool onExceptionThrown(Exception ex)
|
||||
{
|
||||
bool continueExecution = Interlocked.Decrement(ref allowableExceptions) >= 0;
|
||||
|
||||
Logger.Log($"Unhandled exception has been {(continueExecution ? $"allowed with {allowableExceptions} more allowable exceptions" : "denied")} .");
|
||||
|
||||
RulesetStore.TryDisableCustomRulesetsCausing(ex);
|
||||
|
||||
// restore the stock of allowable exceptions after a short delay.
|
||||
Task.Delay(1000).ContinueWith(_ => Interlocked.Increment(ref allowableExceptions));
|
||||
|
||||
|
@ -3,8 +3,11 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
@ -13,17 +16,20 @@ namespace osu.Game.Rulesets
|
||||
{
|
||||
public class RealmRulesetStore : RulesetStore
|
||||
{
|
||||
private readonly RealmAccess realmAccess;
|
||||
public override IEnumerable<RulesetInfo> AvailableRulesets => availableRulesets;
|
||||
|
||||
private readonly List<RulesetInfo> availableRulesets = new List<RulesetInfo>();
|
||||
|
||||
public RealmRulesetStore(RealmAccess realm, Storage? storage = null)
|
||||
public RealmRulesetStore(RealmAccess realmAccess, Storage? storage = null)
|
||||
: base(storage)
|
||||
{
|
||||
prepareDetachedRulesets(realm);
|
||||
this.realmAccess = realmAccess;
|
||||
prepareDetachedRulesets();
|
||||
informUserAboutBrokenRulesets();
|
||||
}
|
||||
|
||||
private void prepareDetachedRulesets(RealmAccess realmAccess)
|
||||
private void prepareDetachedRulesets()
|
||||
{
|
||||
realmAccess.Write(realm =>
|
||||
{
|
||||
@ -143,5 +149,41 @@ namespace osu.Game.Rulesets
|
||||
|
||||
instance.CreateBeatmapProcessor(converter.Convert());
|
||||
}
|
||||
|
||||
private void informUserAboutBrokenRulesets()
|
||||
{
|
||||
if (RulesetStorage == null)
|
||||
return;
|
||||
|
||||
foreach (string brokenRulesetDll in RulesetStorage.GetFiles(@".", @"*.dll.broken"))
|
||||
{
|
||||
Logger.Log($"Ruleset '{Path.GetFileNameWithoutExtension(brokenRulesetDll)}' has been disabled due to causing a crash.\n\n"
|
||||
+ "Please update the ruleset or report the issue to the developers of the ruleset if no updates are available.", level: LogLevel.Important);
|
||||
}
|
||||
}
|
||||
|
||||
internal void TryDisableCustomRulesetsCausing(Exception exception)
|
||||
{
|
||||
var stackTrace = new StackTrace(exception);
|
||||
|
||||
foreach (var frame in stackTrace.GetFrames())
|
||||
{
|
||||
var declaringAssembly = frame.GetMethod()?.DeclaringType?.Assembly;
|
||||
if (declaringAssembly == null)
|
||||
continue;
|
||||
|
||||
if (UserRulesetAssemblies.Contains(declaringAssembly))
|
||||
{
|
||||
string sourceLocation = declaringAssembly.Location;
|
||||
string destinationLocation = Path.ChangeExtension(sourceLocation, @".dll.broken");
|
||||
|
||||
if (File.Exists(sourceLocation))
|
||||
{
|
||||
Logger.Log($"Unhandled exception traced back to custom ruleset {Path.GetFileNameWithoutExtension(sourceLocation)}. Marking as broken.");
|
||||
File.Move(sourceLocation, destinationLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ namespace osu.Game.Rulesets
|
||||
private const string ruleset_library_prefix = @"osu.Game.Rulesets";
|
||||
|
||||
protected readonly Dictionary<Assembly, Type> LoadedAssemblies = new Dictionary<Assembly, Type>();
|
||||
protected readonly HashSet<Assembly> UserRulesetAssemblies = new HashSet<Assembly>();
|
||||
protected readonly Storage? RulesetStorage;
|
||||
|
||||
/// <summary>
|
||||
/// All available rulesets.
|
||||
@ -41,9 +43,9 @@ namespace osu.Game.Rulesets
|
||||
// to load as unable to locate the game core assembly.
|
||||
AppDomain.CurrentDomain.AssemblyResolve += resolveRulesetDependencyAssembly;
|
||||
|
||||
var rulesetStorage = storage?.GetStorageForDirectory(@"rulesets");
|
||||
if (rulesetStorage != null)
|
||||
loadUserRulesets(rulesetStorage);
|
||||
RulesetStorage = storage?.GetStorageForDirectory(@"rulesets");
|
||||
if (RulesetStorage != null)
|
||||
loadUserRulesets(RulesetStorage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -105,7 +107,11 @@ namespace osu.Game.Rulesets
|
||||
var rulesets = rulesetStorage.GetFiles(@".", @$"{ruleset_library_prefix}.*.dll");
|
||||
|
||||
foreach (string? ruleset in rulesets.Where(f => !f.Contains(@"Tests")))
|
||||
loadRulesetFromFile(rulesetStorage.GetFullPath(ruleset));
|
||||
{
|
||||
var assembly = loadRulesetFromFile(rulesetStorage.GetFullPath(ruleset));
|
||||
if (assembly != null)
|
||||
UserRulesetAssemblies.Add(assembly);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFromDisk()
|
||||
@ -126,21 +132,25 @@ namespace osu.Game.Rulesets
|
||||
}
|
||||
}
|
||||
|
||||
private void loadRulesetFromFile(string file)
|
||||
private Assembly? loadRulesetFromFile(string file)
|
||||
{
|
||||
string filename = Path.GetFileNameWithoutExtension(file);
|
||||
|
||||
if (LoadedAssemblies.Values.Any(t => Path.GetFileNameWithoutExtension(t.Assembly.Location) == filename))
|
||||
return;
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
addRuleset(Assembly.LoadFrom(file));
|
||||
var assembly = Assembly.LoadFrom(file);
|
||||
addRuleset(assembly);
|
||||
return assembly;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogFailedLoad(filename, e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void addRuleset(Assembly assembly)
|
||||
|
Loading…
Reference in New Issue
Block a user