1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-28 07:23:14 +08:00

Merge pull request #26502 from frenzibyte/prevent-submission-with-invalid-mods

Add guard against submitting score with invalid mod instances
This commit is contained in:
Bartłomiej Dach 2024-01-17 11:39:32 +01:00 committed by GitHub
commit 5cb17bcacf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 81 additions and 11 deletions

View File

@ -7,7 +7,9 @@ using Moq;
using NUnit.Framework;
using osu.Framework.Localisation;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Utils;
namespace osu.Game.Tests.Mods
@ -310,6 +312,16 @@ namespace osu.Game.Tests.Mods
Assert.That(invalid?.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
}
[Test]
public void TestModBelongsToRuleset()
{
Assert.That(ModUtils.CheckModsBelongToRuleset(new OsuRuleset(), Array.Empty<Mod>()));
Assert.That(ModUtils.CheckModsBelongToRuleset(new OsuRuleset(), new Mod[] { new OsuModDoubleTime() }));
Assert.That(ModUtils.CheckModsBelongToRuleset(new OsuRuleset(), new Mod[] { new OsuModDoubleTime(), new OsuModAccuracyChallenge() }));
Assert.That(ModUtils.CheckModsBelongToRuleset(new OsuRuleset(), new Mod[] { new OsuModDoubleTime(), new ModAccuracyChallenge() }), Is.False);
Assert.That(ModUtils.CheckModsBelongToRuleset(new OsuRuleset(), new Mod[] { new OsuModDoubleTime(), new TaikoModFlashlight() }), Is.False);
}
[Test]
public void TestFormatScoreMultiplier()
{

View File

@ -13,7 +13,6 @@ using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Framework.Utils;
@ -487,13 +486,8 @@ namespace osu.Game.Tests.Visual.Gameplay
}
}
private class TestMod : Mod, IApplicableToScoreProcessor
private class TestMod : OsuModDoubleTime, IApplicableToScoreProcessor
{
public override string Name => string.Empty;
public override string Acronym => string.Empty;
public override double ScoreMultiplier => 1;
public override LocalisableString Description => string.Empty;
public bool Applied { get; private set; }
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)

View File

@ -15,11 +15,13 @@ using osu.Game.Online.Rooms;
using osu.Game.Online.Solo;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko;
using osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
using osu.Game.Tests.Beatmaps;
@ -34,12 +36,19 @@ namespace osu.Game.Tests.Visual.Gameplay
private Func<RulesetInfo, IBeatmap> createCustomBeatmap;
private Func<Ruleset> createCustomRuleset;
private Func<Mod[]> createCustomMods;
private DummyAPIAccess dummyAPI => (DummyAPIAccess)API;
protected override bool HasCustomSteps => true;
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new FakeImportingPlayer(false);
protected override TestPlayer CreatePlayer(Ruleset ruleset)
{
if (createCustomMods != null)
SelectedMods.Value = SelectedMods.Value.Concat(createCustomMods()).ToList();
return new FakeImportingPlayer(false);
}
protected new FakeImportingPlayer Player => (FakeImportingPlayer)base.Player;
@ -277,13 +286,28 @@ namespace osu.Game.Tests.Visual.Gameplay
AddAssert("ensure no submission", () => Player.SubmittedScore == null);
}
private void createPlayerTest(bool allowFail = false, Func<RulesetInfo, IBeatmap> createBeatmap = null, Func<Ruleset> createRuleset = null)
[Test]
public void TestNoSubmissionWithModsOfDifferentRuleset()
{
prepareTestAPI(true);
createPlayerTest(createRuleset: () => new OsuRuleset(), createMods: () => new Mod[] { new TaikoModHidden() });
AddUntilStep("wait for token request", () => Player.TokenCreationRequested);
AddAssert("gameplay not loaded", () => Player.DrawableRuleset == null);
AddStep("exit", () => Player.Exit());
AddAssert("ensure no submission", () => Player.SubmittedScore == null);
}
private void createPlayerTest(bool allowFail = false, Func<RulesetInfo, IBeatmap> createBeatmap = null, Func<Ruleset> createRuleset = null, Func<Mod[]> createMods = null)
{
CreateTest(() => AddStep("set up requirements", () =>
{
this.allowFail = allowFail;
createCustomBeatmap = createBeatmap;
createCustomRuleset = createRuleset;
createCustomMods = createMods;
}));
}

View File

@ -8,6 +8,7 @@ using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osuTK;
@ -29,7 +30,7 @@ namespace osu.Game.Tests.Visual.Mods
public void TestMaximumAchievableAccuracy() =>
CreateModTest(new ModTestData
{
Mod = new ModAccuracyChallenge
Mod = new OsuModAccuracyChallenge
{
MinimumAccuracy = { Value = 0.6 }
},
@ -49,7 +50,7 @@ namespace osu.Game.Tests.Visual.Mods
public void TestStandardAccuracy() =>
CreateModTest(new ModTestData
{
Mod = new ModAccuracyChallenge
Mod = new OsuModAccuracyChallenge
{
MinimumAccuracy = { Value = 0.6 },
AccuracyJudgeMode = { Value = ModAccuracyChallenge.AccuracyMode.Standard }

View File

@ -38,6 +38,7 @@ using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Ranking;
using osu.Game.Skinning;
using osu.Game.Users;
using osu.Game.Utils;
using osuTK.Graphics;
namespace osu.Game.Screens.Play
@ -213,6 +214,12 @@ namespace osu.Game.Screens.Play
if (playableBeatmap == null)
return;
if (!ModUtils.CheckModsBelongToRuleset(ruleset, gameplayMods))
{
Logger.Log($@"Gameplay was started with a mod belonging to a ruleset different than '{ruleset.Description}'.", level: LogLevel.Important);
return;
}
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
if (game != null)

View File

@ -229,6 +229,38 @@ namespace osu.Game.Utils
return proposedWereValid;
}
/// <summary>
/// Verifies all mods provided belong to the given ruleset.
/// </summary>
/// <param name="ruleset">The ruleset to check the proposed mods against.</param>
/// <param name="proposedMods">The mods proposed for checking.</param>
/// <returns>Whether all <paramref name="proposedMods"/> belong to the given <paramref name="ruleset"/>.</returns>
public static bool CheckModsBelongToRuleset(Ruleset ruleset, IEnumerable<Mod> proposedMods)
{
var rulesetModsTypes = ruleset.AllMods.Select(m => m.GetType()).ToList();
foreach (var proposedMod in proposedMods)
{
bool found = false;
var proposedModType = proposedMod.GetType();
foreach (var rulesetModType in rulesetModsTypes)
{
if (rulesetModType.IsAssignableFrom(proposedModType))
{
found = true;
break;
}
}
if (!found)
return false;
}
return true;
}
/// <summary>
/// Given a value of a score multiplier, returns a string version with special handling for a value near 1.00x.
/// </summary>