mirror of
https://github.com/ppy/osu.git
synced 2025-01-27 10:23:03 +08:00
Merge branch 'add-mod-utils' into mod-consistency-function
This commit is contained in:
commit
5881b8be96
@ -18,50 +18,88 @@ namespace osu.Game.Tests.Mods
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestModIsCompatibleByItself()
|
public void TestModIsCompatibleByItself()
|
||||||
{
|
{
|
||||||
var mod = new Mock<Mod>();
|
var mod = new Mock<CustomMod1>();
|
||||||
Assert.That(ModUtils.CheckCompatibleSet(new[] { mod.Object }));
|
Assert.That(ModUtils.CheckCompatibleSet(new[] { mod.Object }));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestIncompatibleThroughTopLevel()
|
public void TestIncompatibleThroughTopLevel()
|
||||||
{
|
{
|
||||||
var mod1 = new Mock<Mod>();
|
var mod1 = new Mock<CustomMod1>();
|
||||||
var mod2 = new Mock<Mod>();
|
var mod2 = new Mock<CustomMod2>();
|
||||||
|
|
||||||
mod1.Setup(m => m.IncompatibleMods).Returns(new[] { mod2.Object.GetType() });
|
mod1.Setup(m => m.IncompatibleMods).Returns(new[] { mod2.Object.GetType() });
|
||||||
|
|
||||||
// Test both orderings.
|
// Test both orderings.
|
||||||
Assert.That(ModUtils.CheckCompatibleSet(new[] { mod1.Object, mod2.Object }), Is.False);
|
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod1.Object, mod2.Object }), Is.False);
|
||||||
Assert.That(ModUtils.CheckCompatibleSet(new[] { mod2.Object, mod1.Object }), Is.False);
|
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod2.Object, mod1.Object }), Is.False);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestIncompatibleThroughMultiMod()
|
public void TestMultiModIncompatibleWithTopLevel()
|
||||||
{
|
{
|
||||||
var mod1 = new Mock<Mod>();
|
var mod1 = new Mock<CustomMod1>();
|
||||||
|
|
||||||
// The nested mod.
|
// The nested mod.
|
||||||
var mod2 = new Mock<Mod>();
|
var mod2 = new Mock<CustomMod2>();
|
||||||
mod2.Setup(m => m.IncompatibleMods).Returns(new[] { mod1.Object.GetType() });
|
mod2.Setup(m => m.IncompatibleMods).Returns(new[] { mod1.Object.GetType() });
|
||||||
|
|
||||||
var multiMod = new MultiMod(new MultiMod(mod2.Object));
|
var multiMod = new MultiMod(new MultiMod(mod2.Object));
|
||||||
|
|
||||||
// Test both orderings.
|
// Test both orderings.
|
||||||
Assert.That(ModUtils.CheckCompatibleSet(new[] { multiMod, mod1.Object }), Is.False);
|
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { multiMod, mod1.Object }), Is.False);
|
||||||
Assert.That(ModUtils.CheckCompatibleSet(new[] { mod1.Object, multiMod }), Is.False);
|
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod1.Object, multiMod }), Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTopLevelIncompatibleWithMultiMod()
|
||||||
|
{
|
||||||
|
// The nested mod.
|
||||||
|
var mod1 = new Mock<CustomMod1>();
|
||||||
|
var multiMod = new MultiMod(new MultiMod(mod1.Object));
|
||||||
|
|
||||||
|
var mod2 = new Mock<CustomMod2>();
|
||||||
|
mod2.Setup(m => m.IncompatibleMods).Returns(new[] { typeof(CustomMod1) });
|
||||||
|
|
||||||
|
// Test both orderings.
|
||||||
|
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { multiMod, mod2.Object }), Is.False);
|
||||||
|
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod2.Object, multiMod }), Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCompatibleMods()
|
||||||
|
{
|
||||||
|
var mod1 = new Mock<CustomMod1>();
|
||||||
|
var mod2 = new Mock<CustomMod2>();
|
||||||
|
|
||||||
|
// Test both orderings.
|
||||||
|
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod1.Object, mod2.Object }), Is.True);
|
||||||
|
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod2.Object, mod1.Object }), Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestIncompatibleThroughBaseType()
|
||||||
|
{
|
||||||
|
var mod1 = new Mock<CustomMod1>();
|
||||||
|
var mod2 = new Mock<CustomMod2>();
|
||||||
|
mod2.Setup(m => m.IncompatibleMods).Returns(new[] { typeof(Mod) });
|
||||||
|
|
||||||
|
// Test both orderings.
|
||||||
|
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod1.Object, mod2.Object }), Is.False);
|
||||||
|
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod2.Object, mod1.Object }), Is.False);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestAllowedThroughMostDerivedType()
|
public void TestAllowedThroughMostDerivedType()
|
||||||
{
|
{
|
||||||
var mod = new Mock<Mod>();
|
var mod = new Mock<CustomMod1>();
|
||||||
Assert.That(ModUtils.CheckAllowed(new[] { mod.Object }, new[] { mod.Object.GetType() }));
|
Assert.That(ModUtils.CheckAllowed(new[] { mod.Object }, new[] { mod.Object.GetType() }));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestNotAllowedThroughBaseType()
|
public void TestNotAllowedThroughBaseType()
|
||||||
{
|
{
|
||||||
var mod = new Mock<Mod>();
|
var mod = new Mock<CustomMod1>();
|
||||||
Assert.That(ModUtils.CheckAllowed(new[] { mod.Object }, new[] { typeof(Mod) }), Is.False);
|
Assert.That(ModUtils.CheckAllowed(new[] { mod.Object }, new[] { typeof(Mod) }), Is.False);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,5 +126,13 @@ namespace osu.Game.Tests.Mods
|
|||||||
else
|
else
|
||||||
Assert.That(invalid?.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
|
Assert.That(invalid?.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract class CustomMod1 : Mod
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class CustomMod2 : Mod
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Extensions.TypeExtensions;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
@ -29,7 +28,7 @@ namespace osu.Game.Utils
|
|||||||
{
|
{
|
||||||
// Prevent multiple-enumeration.
|
// Prevent multiple-enumeration.
|
||||||
var combinationList = combination as ICollection<Mod> ?? combination.ToArray();
|
var combinationList = combination as ICollection<Mod> ?? combination.ToArray();
|
||||||
return CheckCompatibleSet(combinationList) && CheckAllowed(combinationList, allowedTypes);
|
return CheckCompatibleSet(combinationList, out _) && CheckAllowed(combinationList, allowedTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -38,32 +37,32 @@ namespace osu.Game.Utils
|
|||||||
/// <param name="combination">The <see cref="Mod"/> combination to check.</param>
|
/// <param name="combination">The <see cref="Mod"/> combination to check.</param>
|
||||||
/// <returns>Whether all <see cref="Mod"/>s in the combination are compatible with each-other.</returns>
|
/// <returns>Whether all <see cref="Mod"/>s in the combination are compatible with each-other.</returns>
|
||||||
public static bool CheckCompatibleSet(IEnumerable<Mod> combination)
|
public static bool CheckCompatibleSet(IEnumerable<Mod> combination)
|
||||||
|
=> CheckCompatibleSet(combination, out _);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks that all <see cref="Mod"/>s in a combination are compatible with each-other.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="combination">The <see cref="Mod"/> combination to check.</param>
|
||||||
|
/// <param name="invalidMods">Any invalid mods in the set.</param>
|
||||||
|
/// <returns>Whether all <see cref="Mod"/>s in the combination are compatible with each-other.</returns>
|
||||||
|
public static bool CheckCompatibleSet(IEnumerable<Mod> combination, out List<Mod>? invalidMods)
|
||||||
{
|
{
|
||||||
var incompatibleTypes = new HashSet<Type>();
|
combination = FlattenMods(combination).ToArray();
|
||||||
var incomingTypes = new HashSet<Type>();
|
invalidMods = null;
|
||||||
|
|
||||||
foreach (var mod in combination.SelectMany(FlattenMod))
|
foreach (var mod in combination)
|
||||||
{
|
{
|
||||||
// Add the new mod incompatibilities, checking whether any match the existing mod types.
|
foreach (var type in mod.IncompatibleMods)
|
||||||
foreach (var t in mod.IncompatibleMods)
|
|
||||||
{
|
{
|
||||||
if (incomingTypes.Contains(t))
|
foreach (var invalid in combination.Where(m => type.IsInstanceOfType(m)))
|
||||||
return false;
|
{
|
||||||
|
invalidMods ??= new List<Mod>();
|
||||||
incompatibleTypes.Add(t);
|
invalidMods.Add(invalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the new mod types, checking whether any match the incompatible types.
|
|
||||||
foreach (var t in mod.GetType().EnumerateBaseTypes())
|
|
||||||
{
|
|
||||||
if (incomingTypes.Contains(t))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
incomingTypes.Add(t);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return invalidMods == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
Loading…
Reference in New Issue
Block a user