1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 01:52:55 +08:00

Merge branch 'master' into realm-fix-async-write-after-disposal

This commit is contained in:
Dean Herbert 2022-06-29 22:39:05 +09:00
commit a3b4a515fc
149 changed files with 542 additions and 518 deletions

View File

@ -8,20 +8,20 @@ GEM
artifactory (3.0.15) artifactory (3.0.15)
atomos (0.1.3) atomos (0.1.3)
aws-eventstream (1.2.0) aws-eventstream (1.2.0)
aws-partitions (1.570.0) aws-partitions (1.601.0)
aws-sdk-core (3.130.0) aws-sdk-core (3.131.2)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0) aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
jmespath (~> 1.0) jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.55.0) aws-sdk-kms (1.57.0)
aws-sdk-core (~> 3, >= 3.127.0) aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.113.0) aws-sdk-s3 (1.114.0)
aws-sdk-core (~> 3, >= 3.127.0) aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (~> 1) aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4) aws-sigv4 (~> 1.4)
aws-sigv4 (1.4.0) aws-sigv4 (1.5.0)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4) babosa (1.0.4)
claide (1.1.0) claide (1.1.0)
@ -36,7 +36,7 @@ GEM
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.6) dotenv (2.7.6)
emoji_regex (3.2.3) emoji_regex (3.2.3)
excon (0.92.1) excon (0.92.3)
faraday (1.10.0) faraday (1.10.0)
faraday-em_http (~> 1.0) faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0) faraday-em_synchrony (~> 1.0)
@ -56,8 +56,8 @@ GEM
faraday-em_synchrony (1.0.0) faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0) faraday-excon (1.1.0)
faraday-httpclient (1.0.1) faraday-httpclient (1.0.1)
faraday-multipart (1.0.3) faraday-multipart (1.0.4)
multipart-post (>= 1.2, < 3) multipart-post (~> 2)
faraday-net_http (1.0.1) faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0) faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0) faraday-patron (1.0.0)
@ -66,7 +66,7 @@ GEM
faraday_middleware (1.2.0) faraday_middleware (1.2.0)
faraday (~> 1.0) faraday (~> 1.0)
fastimage (2.2.6) fastimage (2.2.6)
fastlane (2.205.1) fastlane (2.206.2)
CFPropertyList (>= 2.3, < 4.0.0) CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0) addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0) artifactory (~> 3.0)
@ -110,9 +110,9 @@ GEM
souyuz (= 0.11.1) souyuz (= 0.11.1)
fastlane-plugin-xamarin (0.6.3) fastlane-plugin-xamarin (0.6.3)
gh_inspector (1.1.3) gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.16.0) google-apis-androidpublisher_v3 (0.23.0)
google-apis-core (>= 0.4, < 2.a) google-apis-core (>= 0.6, < 2.a)
google-apis-core (0.4.2) google-apis-core (0.6.0)
addressable (~> 2.5, >= 2.5.1) addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a) googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a) httpclient (>= 2.8.1, < 3.a)
@ -121,19 +121,19 @@ GEM
retriable (>= 2.0, < 4.a) retriable (>= 2.0, < 4.a)
rexml rexml
webrick webrick
google-apis-iamcredentials_v1 (0.10.0) google-apis-iamcredentials_v1 (0.12.0)
google-apis-core (>= 0.4, < 2.a) google-apis-core (>= 0.6, < 2.a)
google-apis-playcustomapp_v1 (0.7.0) google-apis-playcustomapp_v1 (0.9.0)
google-apis-core (>= 0.4, < 2.a) google-apis-core (>= 0.6, < 2.a)
google-apis-storage_v1 (0.11.0) google-apis-storage_v1 (0.16.0)
google-apis-core (>= 0.4, < 2.a) google-apis-core (>= 0.6, < 2.a)
google-cloud-core (1.6.0) google-cloud-core (1.6.0)
google-cloud-env (~> 1.0) google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0) google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0) google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0) faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.2.0) google-cloud-errors (1.2.0)
google-cloud-storage (1.36.1) google-cloud-storage (1.36.2)
addressable (~> 2.8) addressable (~> 2.8)
digest-crc (~> 0.4) digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1) google-apis-iamcredentials_v1 (~> 0.1)
@ -141,7 +141,7 @@ GEM
google-cloud-core (~> 1.6) google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a) googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0) mini_mime (~> 1.0)
googleauth (1.1.2) googleauth (1.2.0)
faraday (>= 0.17.3, < 3.a) faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0) jwt (>= 1.4, < 3.0)
memoist (~> 0.16) memoist (~> 0.16)
@ -149,12 +149,12 @@ GEM
os (>= 0.9, < 2.0) os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a) signet (>= 0.16, < 2.a)
highline (2.0.3) highline (2.0.3)
http-cookie (1.0.4) http-cookie (1.0.5)
domain_name (~> 0.5) domain_name (~> 0.5)
httpclient (2.8.3) httpclient (2.8.3)
jmespath (1.6.1) jmespath (1.6.1)
json (2.6.1) json (2.6.2)
jwt (2.3.0) jwt (2.4.1)
memoist (0.16.2) memoist (0.16.2)
mini_magick (4.11.0) mini_magick (4.11.0)
mini_mime (1.1.2) mini_mime (1.1.2)
@ -169,10 +169,10 @@ GEM
optparse (0.1.1) optparse (0.1.1)
os (1.1.4) os (1.1.4)
plist (3.6.0) plist (3.6.0)
public_suffix (4.0.6) public_suffix (4.0.7)
racc (1.6.0) racc (1.6.0)
rake (13.0.6) rake (13.0.6)
representable (3.1.1) representable (3.2.0)
declarative (< 0.1.0) declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0) trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0) uber (< 0.2.0)
@ -182,9 +182,9 @@ GEM
ruby2_keywords (0.0.5) ruby2_keywords (0.0.5)
rubyzip (2.3.2) rubyzip (2.3.2)
security (0.1.3) security (0.1.3)
signet (0.16.1) signet (0.17.0)
addressable (~> 2.8) addressable (~> 2.8)
faraday (>= 0.17.5, < 3.0) faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0) jwt (>= 1.5, < 3.0)
multi_json (~> 1.10) multi_json (~> 1.10)
simctl (1.6.8) simctl (1.6.8)
@ -205,11 +205,11 @@ GEM
uber (0.1.0) uber (0.1.0)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.8.1) unf_ext (0.0.8.2)
unicode-display_width (1.8.0) unicode-display_width (1.8.0)
webrick (1.7.0) webrick (1.7.0)
word_wrap (1.0.0) word_wrap (1.0.0)
xcodeproj (1.21.0) xcodeproj (1.22.0)
CFPropertyList (>= 2.3.3, < 4.0) CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3) atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0) claide (>= 1.0.2, < 2.0)

View File

@ -51,8 +51,8 @@
<Reference Include="Java.Interop" /> <Reference Include="Java.Interop" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.623.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2022.628.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.624.1" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2022.628.1" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Transitive Dependencies"> <ItemGroup Label="Transitive Dependencies">
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. --> <!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->

View File

@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.Tests
AddStep("change component scale", () => Player.ChildrenOfType<LegacyScoreCounter>().First().Scale = new Vector2(2f)); AddStep("change component scale", () => Player.ChildrenOfType<LegacyScoreCounter>().First().Scale = new Vector2(2f));
AddStep("update target", () => Player.ChildrenOfType<SkinnableTargetContainer>().ForEach(LegacySkin.UpdateDrawableTarget)); AddStep("update target", () => Player.ChildrenOfType<SkinnableTargetContainer>().ForEach(LegacySkin.UpdateDrawableTarget));
AddStep("exit player", () => Player.Exit()); AddStep("exit player", () => Player.Exit());
CreateTest(null); CreateTest();
} }
AddAssert("legacy HUD combo counter hidden", () => AddAssert("legacy HUD combo counter hidden", () =>

View File

@ -19,12 +19,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty
[JsonProperty("great_hit_window")] [JsonProperty("great_hit_window")]
public double GreatHitWindow { get; set; } public double GreatHitWindow { get; set; }
/// <summary>
/// The score multiplier applied via score-reducing mods.
/// </summary>
[JsonProperty("score_multiplier")]
public double ScoreMultiplier { get; set; }
public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes()
{ {
foreach (var v in base.ToDatabaseAttributes()) foreach (var v in base.ToDatabaseAttributes())
@ -33,7 +27,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty
yield return (ATTRIB_ID_MAX_COMBO, MaxCombo); yield return (ATTRIB_ID_MAX_COMBO, MaxCombo);
yield return (ATTRIB_ID_DIFFICULTY, StarRating); yield return (ATTRIB_ID_DIFFICULTY, StarRating);
yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow); yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow);
yield return (ATTRIB_ID_SCORE_MULTIPLIER, ScoreMultiplier);
} }
public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> values, IBeatmapOnlineInfo onlineInfo) public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> values, IBeatmapOnlineInfo onlineInfo)
@ -43,7 +36,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty
MaxCombo = (int)values[ATTRIB_ID_MAX_COMBO]; MaxCombo = (int)values[ATTRIB_ID_MAX_COMBO];
StarRating = values[ATTRIB_ID_DIFFICULTY]; StarRating = values[ATTRIB_ID_DIFFICULTY];
GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW]; GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW];
ScoreMultiplier = values[ATTRIB_ID_SCORE_MULTIPLIER];
} }
} }
} }

View File

@ -53,7 +53,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty
// In osu-stable mania, rate-adjustment mods don't affect the hit window. // In osu-stable mania, rate-adjustment mods don't affect the hit window.
// This is done the way it is to introduce fractional differences in order to match osu-stable for the time being. // This is done the way it is to introduce fractional differences in order to match osu-stable for the time being.
GreatHitWindow = Math.Ceiling((int)(getHitWindow300(mods) * clockRate) / clockRate), GreatHitWindow = Math.Ceiling((int)(getHitWindow300(mods) * clockRate) / clockRate),
ScoreMultiplier = getScoreMultiplier(mods),
MaxCombo = beatmap.HitObjects.Sum(maxComboForObject) MaxCombo = beatmap.HitObjects.Sum(maxComboForObject)
}; };
} }
@ -147,32 +146,5 @@ namespace osu.Game.Rulesets.Mania.Difficulty
return value; return value;
} }
} }
private double getScoreMultiplier(Mod[] mods)
{
double scoreMultiplier = 1;
foreach (var m in mods)
{
switch (m)
{
case ManiaModNoFail:
case ManiaModEasy:
case ManiaModHalfTime:
scoreMultiplier *= 0.5;
break;
}
}
var maniaBeatmap = (ManiaBeatmap)Beatmap;
int diff = maniaBeatmap.TotalColumns - maniaBeatmap.OriginalTotalColumns;
if (diff > 0)
scoreMultiplier *= 0.9;
else if (diff < 0)
scoreMultiplier *= 0.9 + 0.04 * diff;
return scoreMultiplier;
}
} }
} }

View File

@ -14,19 +14,12 @@ namespace osu.Game.Rulesets.Mania.Difficulty
[JsonProperty("difficulty")] [JsonProperty("difficulty")]
public double Difficulty { get; set; } public double Difficulty { get; set; }
[JsonProperty("accuracy")]
public double Accuracy { get; set; }
[JsonProperty("scaled_score")]
public double ScaledScore { get; set; }
public override IEnumerable<PerformanceDisplayAttribute> GetAttributesForDisplay() public override IEnumerable<PerformanceDisplayAttribute> GetAttributesForDisplay()
{ {
foreach (var attribute in base.GetAttributesForDisplay()) foreach (var attribute in base.GetAttributesForDisplay())
yield return attribute; yield return attribute;
yield return new PerformanceDisplayAttribute(nameof(Difficulty), "Difficulty", Difficulty); yield return new PerformanceDisplayAttribute(nameof(Difficulty), "Difficulty", Difficulty);
yield return new PerformanceDisplayAttribute(nameof(Accuracy), "Accuracy", Accuracy);
} }
} }
} }

View File

@ -15,15 +15,13 @@ namespace osu.Game.Rulesets.Mania.Difficulty
{ {
public class ManiaPerformanceCalculator : PerformanceCalculator public class ManiaPerformanceCalculator : PerformanceCalculator
{ {
// Score after being scaled by non-difficulty-increasing mods
private double scaledScore;
private int countPerfect; private int countPerfect;
private int countGreat; private int countGreat;
private int countGood; private int countGood;
private int countOk; private int countOk;
private int countMeh; private int countMeh;
private int countMiss; private int countMiss;
private double scoreAccuracy;
public ManiaPerformanceCalculator() public ManiaPerformanceCalculator()
: base(new ManiaRuleset()) : base(new ManiaRuleset())
@ -34,82 +32,47 @@ namespace osu.Game.Rulesets.Mania.Difficulty
{ {
var maniaAttributes = (ManiaDifficultyAttributes)attributes; var maniaAttributes = (ManiaDifficultyAttributes)attributes;
scaledScore = score.TotalScore;
countPerfect = score.Statistics.GetValueOrDefault(HitResult.Perfect); countPerfect = score.Statistics.GetValueOrDefault(HitResult.Perfect);
countGreat = score.Statistics.GetValueOrDefault(HitResult.Great); countGreat = score.Statistics.GetValueOrDefault(HitResult.Great);
countGood = score.Statistics.GetValueOrDefault(HitResult.Good); countGood = score.Statistics.GetValueOrDefault(HitResult.Good);
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok); countOk = score.Statistics.GetValueOrDefault(HitResult.Ok);
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh); countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss); countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
scoreAccuracy = customAccuracy;
if (maniaAttributes.ScoreMultiplier > 0)
{
// Scale score up, so it's comparable to other keymods
scaledScore *= 1.0 / maniaAttributes.ScoreMultiplier;
}
// Arbitrary initial value for scaling pp in order to standardize distributions across game modes. // Arbitrary initial value for scaling pp in order to standardize distributions across game modes.
// The specific number has no intrinsic meaning and can be adjusted as needed. // The specific number has no intrinsic meaning and can be adjusted as needed.
double multiplier = 0.8; double multiplier = 8.0;
if (score.Mods.Any(m => m is ModNoFail)) if (score.Mods.Any(m => m is ModNoFail))
multiplier *= 0.9; multiplier *= 0.75;
if (score.Mods.Any(m => m is ModEasy)) if (score.Mods.Any(m => m is ModEasy))
multiplier *= 0.5; multiplier *= 0.5;
double difficultyValue = computeDifficultyValue(maniaAttributes); double difficultyValue = computeDifficultyValue(maniaAttributes);
double accValue = computeAccuracyValue(difficultyValue, maniaAttributes); double totalValue = difficultyValue * multiplier;
double totalValue =
Math.Pow(
Math.Pow(difficultyValue, 1.1) +
Math.Pow(accValue, 1.1), 1.0 / 1.1
) * multiplier;
return new ManiaPerformanceAttributes return new ManiaPerformanceAttributes
{ {
Difficulty = difficultyValue, Difficulty = difficultyValue,
Accuracy = accValue,
ScaledScore = scaledScore,
Total = totalValue Total = totalValue
}; };
} }
private double computeDifficultyValue(ManiaDifficultyAttributes attributes) private double computeDifficultyValue(ManiaDifficultyAttributes attributes)
{ {
double difficultyValue = Math.Pow(5 * Math.Max(1, attributes.StarRating / 0.2) - 4.0, 2.2) / 135.0; double difficultyValue = Math.Pow(Math.Max(attributes.StarRating - 0.15, 0.05), 2.2) // Star rating to pp curve
* Math.Max(0, 5 * scoreAccuracy - 4) // From 80% accuracy, 1/20th of total pp is awarded per additional 1% accuracy
difficultyValue *= 1.0 + 0.1 * Math.Min(1.0, totalHits / 1500.0); * (1 + 0.1 * Math.Min(1, totalHits / 1500)); // Length bonus, capped at 1500 notes
if (scaledScore <= 500000)
difficultyValue = 0;
else if (scaledScore <= 600000)
difficultyValue *= (scaledScore - 500000) / 100000 * 0.3;
else if (scaledScore <= 700000)
difficultyValue *= 0.3 + (scaledScore - 600000) / 100000 * 0.25;
else if (scaledScore <= 800000)
difficultyValue *= 0.55 + (scaledScore - 700000) / 100000 * 0.20;
else if (scaledScore <= 900000)
difficultyValue *= 0.75 + (scaledScore - 800000) / 100000 * 0.15;
else
difficultyValue *= 0.90 + (scaledScore - 900000) / 100000 * 0.1;
return difficultyValue; return difficultyValue;
} }
private double computeAccuracyValue(double difficultyValue, ManiaDifficultyAttributes attributes)
{
if (attributes.GreatHitWindow <= 0)
return 0;
// Lots of arbitrary values from testing.
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
double accuracyValue = Math.Max(0.0, 0.2 - (attributes.GreatHitWindow - 34) * 0.006667)
* difficultyValue
* Math.Pow(Math.Max(0.0, scaledScore - 960000) / 40000, 1.1);
return accuracyValue;
}
private double totalHits => countPerfect + countOk + countGreat + countGood + countMeh + countMiss; private double totalHits => countPerfect + countOk + countGreat + countGood + countMeh + countMiss;
/// <summary>
/// Accuracy used to weight judgements independently from the score's actual accuracy.
/// </summary>
private double customAccuracy => (countPerfect * 320 + countGreat * 300 + countGood * 200 + countOk * 100 + countMeh * 50) / (totalHits * 320);
} }
} }

View File

@ -10,7 +10,6 @@ using osu.Framework.Input;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
@ -41,10 +40,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false); protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
private bool editorComponentsReady => editor.ChildrenOfType<HitObjectComposer>().FirstOrDefault()?.IsLoaded == true
&& editor.ChildrenOfType<TimelineArea>().FirstOrDefault()?.IsLoaded == true
&& editor?.ChildrenOfType<Playfield>().FirstOrDefault()?.IsLoaded == true;
[TestCase(true)] [TestCase(true)]
[TestCase(false)] [TestCase(false)]
public void TestVelocityChangeSavesCorrectly(bool adjustVelocity) public void TestVelocityChangeSavesCorrectly(bool adjustVelocity)
@ -52,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
double? velocity = null; double? velocity = null;
AddStep("enter editor", () => Game.ScreenStack.Push(new EditorLoader())); AddStep("enter editor", () => Game.ScreenStack.Push(new EditorLoader()));
AddUntilStep("wait for editor load", () => editorComponentsReady); AddUntilStep("wait for editor load", () => editor?.ReadyForUse == true);
AddStep("seek to first control point", () => editorClock.Seek(editorBeatmap.ControlPointInfo.TimingPoints.First().Time)); AddStep("seek to first control point", () => editorClock.Seek(editorBeatmap.ControlPointInfo.TimingPoints.First().Time));
AddStep("enter slider placement mode", () => InputManager.Key(Key.Number3)); AddStep("enter slider placement mode", () => InputManager.Key(Key.Number3));
@ -91,7 +86,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddStep("exit", () => InputManager.Key(Key.Escape)); AddStep("exit", () => InputManager.Key(Key.Escape));
AddStep("enter editor (again)", () => Game.ScreenStack.Push(new EditorLoader())); AddStep("enter editor (again)", () => Game.ScreenStack.Push(new EditorLoader()));
AddUntilStep("wait for editor load", () => editorComponentsReady); AddUntilStep("wait for editor load", () => editor?.ReadyForUse == true);
AddStep("seek to slider", () => editorClock.Seek(slider.StartTime)); AddStep("seek to slider", () => editorClock.Seek(slider.StartTime));
AddAssert("slider has correct velocity", () => slider.Velocity == velocity); AddAssert("slider has correct velocity", () => slider.Velocity == velocity);

View File

@ -0,0 +1,120 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Replays;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests
{
[HeadlessTest]
public class TestSceneSliderFollowCircleInput : RateAdjustedBeatmapTestScene
{
private List<JudgementResult>? judgementResults;
private ScoreAccessibleReplayPlayer? currentPlayer;
[Test]
public void TestMaximumDistanceTrackingWithoutMovement(
[Values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)]
float circleSize,
[Values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)]
double velocity)
{
const double time_slider_start = 1000;
float circleRadius = OsuHitObject.OBJECT_RADIUS * (1.0f - 0.7f * (circleSize - 5) / 5) / 2;
float followCircleRadius = circleRadius * 1.2f;
performTest(new Beatmap<OsuHitObject>
{
HitObjects =
{
new Slider
{
StartTime = time_slider_start,
Position = new Vector2(0, 0),
DifficultyControlPoint = new DifficultyControlPoint { SliderVelocity = velocity },
Path = new SliderPath(PathType.Linear, new[]
{
Vector2.Zero,
new Vector2(followCircleRadius, 0),
}, followCircleRadius),
},
},
BeatmapInfo =
{
Difficulty = new BeatmapDifficulty
{
CircleSize = circleSize,
SliderTickRate = 1
},
Ruleset = new OsuRuleset().RulesetInfo
},
}, new List<ReplayFrame>
{
new OsuReplayFrame { Position = new Vector2(-circleRadius + 1, 0), Actions = { OsuAction.LeftButton }, Time = time_slider_start },
});
AddAssert("Tracking kept", assertMaxJudge);
}
private bool assertMaxJudge() => judgementResults?.Any() == true && judgementResults.All(t => t.Type == t.Judgement.MaxResult);
private void performTest(Beatmap<OsuHitObject> beatmap, List<ReplayFrame> frames)
{
AddStep("load player", () =>
{
Beatmap.Value = CreateWorkingBeatmap(beatmap);
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });
p.OnLoadComplete += _ =>
{
p.ScoreProcessor.NewJudgement += result =>
{
if (currentPlayer == p) judgementResults?.Add(result);
};
};
LoadScreen(currentPlayer = p);
judgementResults = new List<JudgementResult>();
});
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
AddUntilStep("Wait for completion", () => currentPlayer?.ScoreProcessor.HasCompleted.Value == true);
}
private class ScoreAccessibleReplayPlayer : ReplayPlayer
{
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
protected override bool PauseOnFocusLost => false;
public ScoreAccessibleReplayPlayer(Score score)
: base(score, new PlayerConfiguration
{
AllowPause = false,
ShowResults = false,
})
{
}
}
}
}

View File

@ -66,10 +66,7 @@ namespace osu.Game.Rulesets.Osu.Tests
drawableSlider = null; drawableSlider = null;
}); });
[SetUpSteps] protected override bool HasCustomSteps => true;
public override void SetUpSteps()
{
}
[TestCase(0)] [TestCase(0)]
[TestCase(1)] [TestCase(1)]
@ -77,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public void TestSnakingEnabled(int sliderIndex) public void TestSnakingEnabled(int sliderIndex)
{ {
AddStep("enable autoplay", () => autoplay = true); AddStep("enable autoplay", () => autoplay = true);
base.SetUpSteps(); CreateTest();
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning); AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
retrieveSlider(sliderIndex); retrieveSlider(sliderIndex);
@ -101,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public void TestSnakingDisabled(int sliderIndex) public void TestSnakingDisabled(int sliderIndex)
{ {
AddStep("have autoplay", () => autoplay = true); AddStep("have autoplay", () => autoplay = true);
base.SetUpSteps(); CreateTest();
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning); AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
retrieveSlider(sliderIndex); retrieveSlider(sliderIndex);
@ -121,8 +118,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
AddStep("enable autoplay", () => autoplay = true); AddStep("enable autoplay", () => autoplay = true);
setSnaking(true); setSnaking(true);
base.SetUpSteps(); CreateTest();
// repeat might have a chance to update its position depending on where in the frame its hit, // repeat might have a chance to update its position depending on where in the frame its hit,
// so some leniency is allowed here instead of checking strict equality // so some leniency is allowed here instead of checking strict equality
addCheckPositionChangeSteps(() => 16600, getSliderRepeat, positionAlmostSame); addCheckPositionChangeSteps(() => 16600, getSliderRepeat, positionAlmostSame);
@ -133,15 +129,14 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
AddStep("disable autoplay", () => autoplay = false); AddStep("disable autoplay", () => autoplay = false);
setSnaking(true); setSnaking(true);
base.SetUpSteps(); CreateTest();
addCheckPositionChangeSteps(() => 16600, getSliderRepeat, positionDecreased); addCheckPositionChangeSteps(() => 16600, getSliderRepeat, positionDecreased);
} }
private void retrieveSlider(int index) private void retrieveSlider(int index)
{ {
AddStep("retrieve slider at index", () => slider = (Slider)beatmap.HitObjects[index]); AddStep("retrieve slider at index", () => slider = (Slider)beatmap.HitObjects[index]);
addSeekStep(() => slider); addSeekStep(() => slider.StartTime);
AddUntilStep("retrieve drawable slider", () => AddUntilStep("retrieve drawable slider", () =>
(drawableSlider = (DrawableSlider)Player.DrawableRuleset.Playfield.AllHitObjects.SingleOrDefault(d => d.HitObject == slider)) != null); (drawableSlider = (DrawableSlider)Player.DrawableRuleset.Playfield.AllHitObjects.SingleOrDefault(d => d.HitObject == slider)) != null);
} }
@ -205,16 +200,10 @@ namespace osu.Game.Rulesets.Osu.Tests
}); });
} }
private void addSeekStep(Func<Slider> slider) private void addSeekStep(Func<double> getTime)
{ {
AddStep("seek to slider", () => Player.GameplayClockContainer.Seek(slider().StartTime)); AddStep("seek to time", () => Player.GameplayClockContainer.Seek(getTime()));
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(slider().StartTime, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100)); AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(getTime(), Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
}
private void addSeekStep(Func<double> time)
{
AddStep("seek to time", () => Player.GameplayClockContainer.Seek(time()));
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time(), Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
} }
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap { HitObjects = createHitObjects() }; protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap { HitObjects = createHitObjects() };

View File

@ -25,6 +25,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty
[JsonProperty("speed_difficulty")] [JsonProperty("speed_difficulty")]
public double SpeedDifficulty { get; set; } public double SpeedDifficulty { get; set; }
/// <summary>
/// The number of clickable objects weighted by difficulty.
/// Related to <see cref="SpeedDifficulty"/>
/// </summary>
[JsonProperty("speed_note_count")]
public double SpeedNoteCount { get; set; }
/// <summary> /// <summary>
/// The difficulty corresponding to the flashlight skill. /// The difficulty corresponding to the flashlight skill.
/// </summary> /// </summary>
@ -93,6 +100,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
yield return (ATTRIB_ID_FLASHLIGHT, FlashlightDifficulty); yield return (ATTRIB_ID_FLASHLIGHT, FlashlightDifficulty);
yield return (ATTRIB_ID_SLIDER_FACTOR, SliderFactor); yield return (ATTRIB_ID_SLIDER_FACTOR, SliderFactor);
yield return (ATTRIB_ID_SPEED_NOTE_COUNT, SpeedNoteCount);
} }
public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> values, IBeatmapOnlineInfo onlineInfo) public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> values, IBeatmapOnlineInfo onlineInfo)
@ -107,6 +115,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
StarRating = values[ATTRIB_ID_DIFFICULTY]; StarRating = values[ATTRIB_ID_DIFFICULTY];
FlashlightDifficulty = values.GetValueOrDefault(ATTRIB_ID_FLASHLIGHT); FlashlightDifficulty = values.GetValueOrDefault(ATTRIB_ID_FLASHLIGHT);
SliderFactor = values[ATTRIB_ID_SLIDER_FACTOR]; SliderFactor = values[ATTRIB_ID_SLIDER_FACTOR];
SpeedNoteCount = values[ATTRIB_ID_SPEED_NOTE_COUNT];
DrainRate = onlineInfo.DrainRate; DrainRate = onlineInfo.DrainRate;
HitCircleCount = onlineInfo.CircleCount; HitCircleCount = onlineInfo.CircleCount;

View File

@ -38,6 +38,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
double aimRatingNoSliders = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; double aimRatingNoSliders = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
double speedRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier; double speedRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier;
double speedNotes = ((Speed)skills[2]).RelevantNoteCount();
double flashlightRating = Math.Sqrt(skills[3].DifficultyValue()) * difficulty_multiplier; double flashlightRating = Math.Sqrt(skills[3].DifficultyValue()) * difficulty_multiplier;
double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1; double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
@ -75,6 +76,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
Mods = mods, Mods = mods,
AimDifficulty = aimRating, AimDifficulty = aimRating,
SpeedDifficulty = speedRating, SpeedDifficulty = speedRating,
SpeedNoteCount = speedNotes,
FlashlightDifficulty = flashlightRating, FlashlightDifficulty = flashlightRating,
SliderFactor = sliderFactor, SliderFactor = sliderFactor,
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,

View File

@ -163,8 +163,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty
speedValue *= 1.0 + 0.04 * (12.0 - attributes.ApproachRate); speedValue *= 1.0 + 0.04 * (12.0 - attributes.ApproachRate);
} }
// Calculate accuracy assuming the worst case scenario
double relevantTotalDiff = totalHits - attributes.SpeedNoteCount;
double relevantCountGreat = Math.Max(0, countGreat - relevantTotalDiff);
double relevantCountOk = Math.Max(0, countOk - Math.Max(0, relevantTotalDiff - countGreat));
double relevantCountMeh = Math.Max(0, countMeh - Math.Max(0, relevantTotalDiff - countGreat - countOk));
double relevantAccuracy = attributes.SpeedNoteCount == 0 ? 0 : (relevantCountGreat * 6.0 + relevantCountOk * 2.0 + relevantCountMeh) / (attributes.SpeedNoteCount * 6.0);
// Scale the speed value with accuracy and OD. // Scale the speed value with accuracy and OD.
speedValue *= (0.95 + Math.Pow(attributes.OverallDifficulty, 2) / 750) * Math.Pow(accuracy, (14.5 - Math.Max(attributes.OverallDifficulty, 8)) / 2); speedValue *= (0.95 + Math.Pow(attributes.OverallDifficulty, 2) / 750) * Math.Pow((accuracy + relevantAccuracy) / 2.0, (14.5 - Math.Max(attributes.OverallDifficulty, 8)) / 2);
// Scale the speed value with # of 50s to punish doubletapping. // Scale the speed value with # of 50s to punish doubletapping.
speedValue *= Math.Pow(0.98, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); speedValue *= Math.Pow(0.98, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0);

View File

@ -6,6 +6,7 @@
using System; using System;
using System.Linq; using System.Linq;
using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Difficulty.Evaluators; using osu.Game.Rulesets.Osu.Difficulty.Evaluators;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
@ -15,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
/// <summary> /// <summary>
/// Represents the skill required to memorise and hit every object in a map with the Flashlight mod enabled. /// Represents the skill required to memorise and hit every object in a map with the Flashlight mod enabled.
/// </summary> /// </summary>
public class Flashlight : OsuStrainSkill public class Flashlight : StrainSkill
{ {
private readonly bool hasHiddenMod; private readonly bool hasHiddenMod;
@ -27,7 +28,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
private double skillMultiplier => 0.05; private double skillMultiplier => 0.05;
private double strainDecayBase => 0.15; private double strainDecayBase => 0.15;
protected override double DecayWeight => 1.0;
private double currentStrain; private double currentStrain;
@ -42,5 +42,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
return currentStrain; return currentStrain;
} }
public override double DifficultyValue() => GetCurrentStrainPeaks().Sum() * OsuStrainSkill.DEFAULT_DIFFICULTY_MULTIPLIER;
} }
} }

View File

@ -14,6 +14,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
{ {
public abstract class OsuStrainSkill : StrainSkill public abstract class OsuStrainSkill : StrainSkill
{ {
/// <summary>
/// The default multiplier applied by <see cref="OsuStrainSkill"/> to the final difficulty value after all other calculations.
/// May be overridden via <see cref="DifficultyMultiplier"/>.
/// </summary>
public const double DEFAULT_DIFFICULTY_MULTIPLIER = 1.06;
/// <summary> /// <summary>
/// The number of sections with the highest strains, which the peak strain reductions will apply to. /// The number of sections with the highest strains, which the peak strain reductions will apply to.
/// This is done in order to decrease their impact on the overall difficulty of the map for this skill. /// This is done in order to decrease their impact on the overall difficulty of the map for this skill.
@ -28,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
/// <summary> /// <summary>
/// The final multiplier to be applied to <see cref="DifficultyValue"/> after all other calculations. /// The final multiplier to be applied to <see cref="DifficultyValue"/> after all other calculations.
/// </summary> /// </summary>
protected virtual double DifficultyMultiplier => 1.06; protected virtual double DifficultyMultiplier => DEFAULT_DIFFICULTY_MULTIPLIER;
protected OsuStrainSkill(Mod[] mods) protected OsuStrainSkill(Mod[] mods)
: base(mods) : base(mods)

View File

@ -8,6 +8,8 @@ using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Difficulty.Evaluators; using osu.Game.Rulesets.Osu.Difficulty.Evaluators;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Rulesets.Osu.Difficulty.Skills namespace osu.Game.Rulesets.Osu.Difficulty.Skills
{ {
@ -26,6 +28,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
protected override double DifficultyMultiplier => 1.04; protected override double DifficultyMultiplier => 1.04;
private readonly double greatWindow; private readonly double greatWindow;
private readonly List<double> objectStrains = new List<double>();
public Speed(Mod[] mods, double hitWindowGreat) public Speed(Mod[] mods, double hitWindowGreat)
: base(mods) : base(mods)
{ {
@ -43,7 +47,24 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current, greatWindow); currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current, greatWindow);
return currentStrain * currentRhythm; double totalStrain = currentStrain * currentRhythm;
objectStrains.Add(totalStrain);
return totalStrain;
}
public double RelevantNoteCount()
{
if (objectStrains.Count == 0)
return 0;
double maxStrain = objectStrains.Max();
if (maxStrain == 0)
return 0;
return objectStrains.Aggregate((total, next) => total + (1.0 / (1.0 + Math.Exp(-(next / maxStrain * 12.0 - 6.0)))));
} }
} }
} }

View File

@ -30,9 +30,6 @@ namespace osu.Game.Rulesets.Osu.Mods
[SettingSource("Apply classic note lock", "Applies note lock to the full hit window.")] [SettingSource("Apply classic note lock", "Applies note lock to the full hit window.")]
public Bindable<bool> ClassicNoteLock { get; } = new BindableBool(true); public Bindable<bool> ClassicNoteLock { get; } = new BindableBool(true);
[SettingSource("Use fixed slider follow circle hit area", "Makes the slider follow circle track its final size at all times.")]
public Bindable<bool> FixedFollowCircleHitArea { get; } = new BindableBool(true);
[SettingSource("Always play a slider's tail sample", "Always plays a slider's tail sample regardless of whether it was hit or not.")] [SettingSource("Always play a slider's tail sample", "Always plays a slider's tail sample regardless of whether it was hit or not.")]
public Bindable<bool> AlwaysPlayTailSample { get; } = new BindableBool(true); public Bindable<bool> AlwaysPlayTailSample { get; } = new BindableBool(true);
@ -62,10 +59,6 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
switch (obj) switch (obj)
{ {
case DrawableSlider slider:
slider.Ball.InputTracksVisualSize = !FixedFollowCircleHitArea.Value;
break;
case DrawableSliderHead head: case DrawableSliderHead head:
head.TrackFollowCircle = !NoSliderHeadMovement.Value; head.TrackFollowCircle = !NoSliderHeadMovement.Value;
break; break;

View File

@ -34,13 +34,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
set => ball.Colour = value; set => ball.Colour = value;
} }
/// <summary>
/// Whether to track accurately to the visual size of this <see cref="SliderBall"/>.
/// If <c>false</c>, tracking will be performed at the final scale at all times.
/// </summary>
public bool InputTracksVisualSize = true;
private readonly Drawable followCircle; private readonly Drawable followCircle;
private readonly Drawable fullSizeFollowCircle;
private readonly DrawableSlider drawableSlider; private readonly DrawableSlider drawableSlider;
private readonly Drawable ball; private readonly Drawable ball;
@ -62,6 +57,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
Alpha = 0, Alpha = 0,
Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderFollowCircle), _ => new DefaultFollowCircle()), Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderFollowCircle), _ => new DefaultFollowCircle()),
}, },
fullSizeFollowCircle = new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true
},
ball = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBall), _ => new DefaultSliderBall()) ball = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBall), _ => new DefaultSliderBall())
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
@ -104,14 +106,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
tracking = value; tracking = value;
if (InputTracksVisualSize) fullSizeFollowCircle.Scale = new Vector2(tracking ? 2.4f : 1f);
followCircle.ScaleTo(tracking ? 2.4f : 1f, 300, Easing.OutQuint);
else
{
// We need to always be tracking the final size, at both endpoints. For now, this is achieved by removing the scale duration.
followCircle.ScaleTo(tracking ? 2.4f : 1f);
}
followCircle.ScaleTo(tracking ? 2.4f : 1f, 300, Easing.OutQuint);
followCircle.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint); followCircle.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint);
} }
} }
@ -170,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
// in valid time range // in valid time range
Time.Current >= drawableSlider.HitObject.StartTime && Time.Current < drawableSlider.HitObject.EndTime && Time.Current >= drawableSlider.HitObject.StartTime && Time.Current < drawableSlider.HitObject.EndTime &&
// in valid position range // in valid position range
lastScreenSpaceMousePosition.HasValue && followCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) && lastScreenSpaceMousePosition.HasValue && fullSizeFollowCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) &&
// valid action // valid action
(actions?.Any(isValidTrackingAction) ?? false); (actions?.Any(isValidTrackingAction) ?? false);

View File

@ -607,6 +607,12 @@ namespace osu.Game.Tests.Database
using (var outStream = File.Open(brokenTempFilename, FileMode.CreateNew)) using (var outStream = File.Open(brokenTempFilename, FileMode.CreateNew))
using (var zip = ZipArchive.Open(brokenOsz)) using (var zip = ZipArchive.Open(brokenOsz))
{ {
foreach (var entry in zip.Entries.ToArray())
{
if (entry.Key.EndsWith(".osu", StringComparison.InvariantCulture))
zip.RemoveEntry(entry);
}
zip.AddEntry("broken.osu", brokenOsu, false); zip.AddEntry("broken.osu", brokenOsu, false);
zip.SaveTo(outStream, CompressionType.Deflate); zip.SaveTo(outStream, CompressionType.Deflate);
} }
@ -627,7 +633,7 @@ namespace osu.Game.Tests.Database
checkSingleReferencedFileCount(realm.Realm, 18); checkSingleReferencedFileCount(realm.Realm, 18);
Assert.AreEqual(1, loggedExceptionCount); Assert.AreEqual(0, loggedExceptionCount);
File.Delete(brokenTempFilename); File.Delete(brokenTempFilename);
}); });

View File

@ -83,14 +83,14 @@ namespace osu.Game.Tests.NonVisual
public override event Action<JudgementResult> NewResult public override event Action<JudgementResult> NewResult
{ {
add => throw new InvalidOperationException(); add => throw new InvalidOperationException($"{nameof(NewResult)} operations not supported in test context");
remove => throw new InvalidOperationException(); remove => throw new InvalidOperationException($"{nameof(NewResult)} operations not supported in test context");
} }
public override event Action<JudgementResult> RevertResult public override event Action<JudgementResult> RevertResult
{ {
add => throw new InvalidOperationException(); add => throw new InvalidOperationException($"{nameof(RevertResult)} operations not supported in test context");
remove => throw new InvalidOperationException(); remove => throw new InvalidOperationException($"{nameof(RevertResult)} operations not supported in test context");
} }
public override Playfield Playfield { get; } public override Playfield Playfield { get; }

View File

@ -36,8 +36,6 @@ namespace osu.Game.Tests.Visual.Editing
{ {
protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
protected override bool EditorComponentsReady => Editor.ChildrenOfType<SetupScreen>().SingleOrDefault()?.IsLoaded == true;
protected override bool IsolateSavingFromDatabase => false; protected override bool IsolateSavingFromDatabase => false;
[Resolved] [Resolved]
@ -95,18 +93,23 @@ namespace osu.Game.Tests.Visual.Editing
string extractedFolder = $"{temp}_extracted"; string extractedFolder = $"{temp}_extracted";
Directory.CreateDirectory(extractedFolder); Directory.CreateDirectory(extractedFolder);
using (var zip = ZipArchive.Open(temp)) try
zip.WriteToDirectory(extractedFolder); {
using (var zip = ZipArchive.Open(temp))
zip.WriteToDirectory(extractedFolder);
bool success = setup.ChildrenOfType<ResourcesSection>().First().ChangeAudioTrack(new FileInfo(Path.Combine(extractedFolder, "03. Renatus - Soleily 192kbps.mp3"))); bool success = setup.ChildrenOfType<ResourcesSection>().First().ChangeAudioTrack(new FileInfo(Path.Combine(extractedFolder, "03. Renatus - Soleily 192kbps.mp3")));
File.Delete(temp); // ensure audio file is copied to beatmap as "audio.mp3" rather than original filename.
Directory.Delete(extractedFolder, true); Assert.That(Beatmap.Value.Metadata.AudioFile == "audio.mp3");
// ensure audio file is copied to beatmap as "audio.mp3" rather than original filename. return success;
Assert.That(Beatmap.Value.Metadata.AudioFile == "audio.mp3"); }
finally
return success; {
File.Delete(temp);
Directory.Delete(extractedFolder, true);
}
}); });
AddAssert("track length changed", () => Beatmap.Value.Track.Length > 60000); AddAssert("track length changed", () => Beatmap.Value.Track.Length > 60000);

View File

@ -9,12 +9,11 @@ using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Rulesets.Edit; using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Components.Timelines.Summary; using osu.Game.Screens.Edit.Components.Timelines.Summary;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osu.Game.Screens.Edit.GameplayTest; using osu.Game.Screens.Edit.GameplayTest;
using osu.Game.Screens.Select; using osu.Game.Screens.Select;
using osu.Game.Tests.Resources; using osu.Game.Tests.Resources;
@ -37,13 +36,12 @@ namespace osu.Game.Tests.Visual.Editing
() => Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet) () => Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet)
&& Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect
&& songSelect.IsLoaded); && songSelect.IsLoaded);
AddUntilStep("wait for completion notification", () => Game.Notifications.ChildrenOfType<ProgressCompletionNotification>().Count() == 1);
AddStep("dismiss notifications", () => Game.Notifications.Hide());
AddStep("switch ruleset", () => Game.Ruleset.Value = new ManiaRuleset().RulesetInfo); AddStep("switch ruleset", () => Game.Ruleset.Value = new ManiaRuleset().RulesetInfo);
AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0))); AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0)));
AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse);
&& editor.IsLoaded
&& editor.ChildrenOfType<HitObjectComposer>().FirstOrDefault()?.IsLoaded == true
&& editor.ChildrenOfType<TimelineArea>().FirstOrDefault()?.IsLoaded == true);
AddStep("test gameplay", () => AddStep("test gameplay", () =>
{ {
var testGameplayButton = this.ChildrenOfType<TestGameplayButton>().Single(); var testGameplayButton = this.ChildrenOfType<TestGameplayButton>().Single();

View File

@ -273,14 +273,14 @@ namespace osu.Game.Tests.Visual.Gameplay
public override event Action<JudgementResult> NewResult public override event Action<JudgementResult> NewResult
{ {
add => throw new InvalidOperationException(); add => throw new InvalidOperationException($"{nameof(NewResult)} operations not supported in test context");
remove => throw new InvalidOperationException(); remove => throw new InvalidOperationException($"{nameof(NewResult)} operations not supported in test context");
} }
public override event Action<JudgementResult> RevertResult public override event Action<JudgementResult> RevertResult
{ {
add => throw new InvalidOperationException(); add => throw new InvalidOperationException($"{nameof(RevertResult)} operations not supported in test context");
remove => throw new InvalidOperationException(); remove => throw new InvalidOperationException($"{nameof(RevertResult)} operations not supported in test context");
} }
public override Playfield Playfield { get; } public override Playfield Playfield { get; }

View File

@ -167,11 +167,16 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("start failing sends", () => AddStep("start failing sends", () =>
{ {
spectatorClient.ShouldFailSendingFrames = true; spectatorClient.ShouldFailSendingFrames = true;
framesReceivedSoFar = replay.Frames.Count;
frameSendAttemptsSoFar = spectatorClient.FrameSendAttempts; frameSendAttemptsSoFar = spectatorClient.FrameSendAttempts;
}); });
AddUntilStep("wait for send attempts", () => spectatorClient.FrameSendAttempts > frameSendAttemptsSoFar + 5); AddUntilStep("wait for next send attempt", () =>
{
framesReceivedSoFar = replay.Frames.Count;
return spectatorClient.FrameSendAttempts > frameSendAttemptsSoFar + 1;
});
AddUntilStep("wait for more send attempts", () => spectatorClient.FrameSendAttempts > frameSendAttemptsSoFar + 10);
AddAssert("frames did not increase", () => framesReceivedSoFar == replay.Frames.Count); AddAssert("frames did not increase", () => framesReceivedSoFar == replay.Frames.Count);
AddStep("stop failing sends", () => spectatorClient.ShouldFailSendingFrames = false); AddStep("stop failing sends", () => spectatorClient.ShouldFailSendingFrames = false);

View File

@ -121,7 +121,7 @@ namespace osu.Game.Tests.Visual.Gameplay
private void createPlayerTest() private void createPlayerTest()
{ {
CreateTest(null); CreateTest();
AddAssert("storyboard loaded", () => Player.Beatmap.Value.Storyboard != null); AddAssert("storyboard loaded", () => Player.Beatmap.Value.Storyboard != null);
waitUntilStoryboardSamplesPlay(); waitUntilStoryboardSamplesPlay();

View File

@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public void TestStoryboardSkipOutro() public void TestStoryboardSkipOutro()
{ {
AddStep("set storyboard duration to long", () => currentStoryboardDuration = 200000); AddStep("set storyboard duration to long", () => currentStoryboardDuration = 200000);
CreateTest(null); CreateTest();
AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value);
AddStep("skip outro", () => InputManager.Key(osuTK.Input.Key.Space)); AddStep("skip outro", () => InputManager.Key(osuTK.Input.Key.Space));
AddUntilStep("player is no longer current screen", () => !Player.IsCurrentScreen()); AddUntilStep("player is no longer current screen", () => !Player.IsCurrentScreen());
@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestStoryboardNoSkipOutro() public void TestStoryboardNoSkipOutro()
{ {
CreateTest(null); CreateTest();
AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= currentStoryboardDuration); AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= currentStoryboardDuration);
AddUntilStep("wait for score shown", () => Player.IsScoreShown); AddUntilStep("wait for score shown", () => Player.IsScoreShown);
} }
@ -71,7 +71,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestStoryboardExitDuringOutroStillExits() public void TestStoryboardExitDuringOutroStillExits()
{ {
CreateTest(null); CreateTest();
AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value);
AddStep("exit via pause", () => Player.ExitViaPause()); AddStep("exit via pause", () => Player.ExitViaPause());
AddAssert("player exited", () => !Player.IsCurrentScreen() && Player.GetChildScreen() == null); AddAssert("player exited", () => !Player.IsCurrentScreen() && Player.GetChildScreen() == null);
@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[TestCase(true)] [TestCase(true)]
public void TestStoryboardToggle(bool enabledAtBeginning) public void TestStoryboardToggle(bool enabledAtBeginning)
{ {
CreateTest(null); CreateTest();
AddStep($"{(enabledAtBeginning ? "enable" : "disable")} storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, enabledAtBeginning)); AddStep($"{(enabledAtBeginning ? "enable" : "disable")} storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, enabledAtBeginning));
AddStep("toggle storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, !enabledAtBeginning)); AddStep("toggle storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, !enabledAtBeginning));
AddUntilStep("wait for score shown", () => Player.IsScoreShown); AddUntilStep("wait for score shown", () => Player.IsScoreShown);
@ -130,7 +130,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
SkipOverlay.FadeContainer fadeContainer() => Player.ChildrenOfType<SkipOverlay.FadeContainer>().First(); SkipOverlay.FadeContainer fadeContainer() => Player.ChildrenOfType<SkipOverlay.FadeContainer>().First();
CreateTest(null); CreateTest();
AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value);
AddUntilStep("skip overlay content becomes visible", () => fadeContainer().State == Visibility.Visible); AddUntilStep("skip overlay content becomes visible", () => fadeContainer().State == Visibility.Visible);
@ -144,7 +144,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestPerformExitNoOutro() public void TestPerformExitNoOutro()
{ {
CreateTest(null); CreateTest();
AddStep("disable storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, false)); AddStep("disable storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, false));
AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value);
AddStep("exit via pause", () => Player.ExitViaPause()); AddStep("exit via pause", () => Player.ExitViaPause());

View File

@ -40,7 +40,6 @@ namespace osu.Game.Tests.Visual.Online
private ChannelManager channelManager; private ChannelManager channelManager;
private APIUser testUser; private APIUser testUser;
private Channel testPMChannel;
private Channel[] testChannels; private Channel[] testChannels;
private Channel testChannel1 => testChannels[0]; private Channel testChannel1 => testChannels[0];
@ -53,7 +52,6 @@ namespace osu.Game.Tests.Visual.Online
public void SetUp() => Schedule(() => public void SetUp() => Schedule(() =>
{ {
testUser = new APIUser { Username = "test user", Id = 5071479 }; testUser = new APIUser { Username = "test user", Id = 5071479 };
testPMChannel = new Channel(testUser);
testChannels = Enumerable.Range(1, 10).Select(createPublicChannel).ToArray(); testChannels = Enumerable.Range(1, 10).Select(createPublicChannel).ToArray();
Child = new DependencyProvidingContainer Child = new DependencyProvidingContainer
@ -80,6 +78,14 @@ namespace osu.Game.Tests.Visual.Online
{ {
switch (req) switch (req)
{ {
case CreateChannelRequest createRequest:
createRequest.TriggerSuccess(new APIChatChannel
{
ChannelID = ((int)createRequest.Channel.Id),
RecentMessages = new List<Message>()
});
return true;
case GetUpdatesRequest getUpdates: case GetUpdatesRequest getUpdates:
getUpdates.TriggerFailure(new WebException()); getUpdates.TriggerFailure(new WebException());
return true; return true;
@ -181,7 +187,7 @@ namespace osu.Game.Tests.Visual.Online
{ {
AddStep("Show overlay", () => chatOverlay.Show()); AddStep("Show overlay", () => chatOverlay.Show());
AddAssert("Listing is visible", () => listingIsVisible); AddAssert("Listing is visible", () => listingIsVisible);
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); joinTestChannel(0);
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
waitForChannel1Visible(); waitForChannel1Visible();
} }
@ -203,12 +209,11 @@ namespace osu.Game.Tests.Visual.Online
[Test] [Test]
public void TestChannelCloseButton() public void TestChannelCloseButton()
{ {
var testPMChannel = new Channel(testUser);
AddStep("Show overlay", () => chatOverlay.Show()); AddStep("Show overlay", () => chatOverlay.Show());
AddStep("Join PM and public channels", () => joinTestChannel(0);
{ joinChannel(testPMChannel);
channelManager.JoinChannel(testChannel1);
channelManager.JoinChannel(testPMChannel);
});
AddStep("Select PM channel", () => clickDrawable(getChannelListItem(testPMChannel))); AddStep("Select PM channel", () => clickDrawable(getChannelListItem(testPMChannel)));
AddStep("Click close button", () => AddStep("Click close button", () =>
{ {
@ -229,7 +234,7 @@ namespace osu.Game.Tests.Visual.Online
public void TestChatCommand() public void TestChatCommand()
{ {
AddStep("Show overlay", () => chatOverlay.Show()); AddStep("Show overlay", () => chatOverlay.Show());
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); joinTestChannel(0);
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
AddStep("Open chat with user", () => channelManager.PostCommand($"chat {testUser.Username}")); AddStep("Open chat with user", () => channelManager.PostCommand($"chat {testUser.Username}"));
AddAssert("PM channel is selected", () => AddAssert("PM channel is selected", () =>
@ -248,14 +253,16 @@ namespace osu.Game.Tests.Visual.Online
[Test] [Test]
public void TestMultiplayerChannelIsNotShown() public void TestMultiplayerChannelIsNotShown()
{ {
Channel multiplayerChannel = null; Channel multiplayerChannel;
AddStep("Show overlay", () => chatOverlay.Show()); AddStep("Show overlay", () => chatOverlay.Show());
AddStep("Join multiplayer channel", () => channelManager.JoinChannel(multiplayerChannel = new Channel(new APIUser())
joinChannel(multiplayerChannel = new Channel(new APIUser())
{ {
Name = "#mp_1", Name = "#mp_1",
Type = ChannelType.Multiplayer, Type = ChannelType.Multiplayer,
})); });
AddAssert("Channel is joined", () => channelManager.JoinedChannels.Contains(multiplayerChannel)); AddAssert("Channel is joined", () => channelManager.JoinedChannels.Contains(multiplayerChannel));
AddUntilStep("Channel not present in listing", () => !chatOverlay.ChildrenOfType<ChannelListingItem>() AddUntilStep("Channel not present in listing", () => !chatOverlay.ChildrenOfType<ChannelListingItem>()
.Where(item => item.IsPresent) .Where(item => item.IsPresent)
@ -269,7 +276,7 @@ namespace osu.Game.Tests.Visual.Online
Message message = null; Message message = null;
AddStep("Show overlay", () => chatOverlay.Show()); AddStep("Show overlay", () => chatOverlay.Show());
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); joinTestChannel(0);
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
AddStep("Send message in channel 1", () => AddStep("Send message in channel 1", () =>
{ {
@ -291,8 +298,8 @@ namespace osu.Game.Tests.Visual.Online
Message message = null; Message message = null;
AddStep("Show overlay", () => chatOverlay.Show()); AddStep("Show overlay", () => chatOverlay.Show());
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); joinTestChannel(0);
AddStep("Join channel 2", () => channelManager.JoinChannel(testChannel2)); joinTestChannel(1);
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
AddStep("Send message in channel 2", () => AddStep("Send message in channel 2", () =>
{ {
@ -314,8 +321,8 @@ namespace osu.Game.Tests.Visual.Online
Message message = null; Message message = null;
AddStep("Show overlay", () => chatOverlay.Show()); AddStep("Show overlay", () => chatOverlay.Show());
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); joinTestChannel(0);
AddStep("Join channel 2", () => channelManager.JoinChannel(testChannel2)); joinTestChannel(1);
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
AddStep("Send message in channel 2", () => AddStep("Send message in channel 2", () =>
{ {
@ -337,7 +344,7 @@ namespace osu.Game.Tests.Visual.Online
{ {
Message message = null; Message message = null;
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); joinTestChannel(0);
AddStep("Send message in channel 1", () => AddStep("Send message in channel 1", () =>
{ {
testChannel1.AddNewMessages(message = new Message testChannel1.AddNewMessages(message = new Message
@ -357,7 +364,7 @@ namespace osu.Game.Tests.Visual.Online
{ {
Message message = null; Message message = null;
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); joinTestChannel(0);
AddStep("Send message in channel 1", () => AddStep("Send message in channel 1", () =>
{ {
testChannel1.AddNewMessages(message = new Message testChannel1.AddNewMessages(message = new Message
@ -378,7 +385,7 @@ namespace osu.Game.Tests.Visual.Online
{ {
AddStep("Show overlay", () => chatOverlay.Show()); AddStep("Show overlay", () => chatOverlay.Show());
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); joinTestChannel(0);
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
waitForChannel1Visible(); waitForChannel1Visible();
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
@ -404,11 +411,11 @@ namespace osu.Game.Tests.Visual.Online
chatOverlay.Show(); chatOverlay.Show();
chatOverlay.SlowLoading = true; chatOverlay.SlowLoading = true;
}); });
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); joinTestChannel(0);
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
AddUntilStep("Channel 1 loading", () => !channelIsVisible && chatOverlay.GetSlowLoadingChannel(testChannel1).LoadState == LoadState.Loading); AddUntilStep("Channel 1 loading", () => !channelIsVisible && chatOverlay.GetSlowLoadingChannel(testChannel1).LoadState == LoadState.Loading);
AddStep("Join channel 2", () => channelManager.JoinChannel(testChannel2)); joinTestChannel(1);
AddStep("Select channel 2", () => clickDrawable(getChannelListItem(testChannel2))); AddStep("Select channel 2", () => clickDrawable(getChannelListItem(testChannel2)));
AddUntilStep("Channel 2 loading", () => !channelIsVisible && chatOverlay.GetSlowLoadingChannel(testChannel2).LoadState == LoadState.Loading); AddUntilStep("Channel 2 loading", () => !channelIsVisible && chatOverlay.GetSlowLoadingChannel(testChannel2).LoadState == LoadState.Loading);
@ -461,19 +468,17 @@ namespace osu.Game.Tests.Visual.Online
Channel pmChannel1 = createPrivateChannel(); Channel pmChannel1 = createPrivateChannel();
Channel pmChannel2 = createPrivateChannel(); Channel pmChannel2 = createPrivateChannel();
AddStep("Show overlay with channels", () => joinTestChannel(0);
{ joinTestChannel(1);
channelManager.JoinChannel(testChannel1); joinChannel(pmChannel1);
channelManager.JoinChannel(testChannel2); joinChannel(pmChannel2);
channelManager.JoinChannel(pmChannel1); joinChannel(announceChannel);
channelManager.JoinChannel(pmChannel2);
channelManager.JoinChannel(announceChannel); AddStep("Show overlay", () => chatOverlay.Show());
chatOverlay.Show();
});
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
waitForChannel1Visible(); waitForChannel1Visible();
AddStep("Press document next keys", () => InputManager.Keys(PlatformAction.DocumentNext)); AddStep("Press document next keys", () => InputManager.Keys(PlatformAction.DocumentNext));
waitForChannel2Visible(); waitForChannel2Visible();
@ -490,6 +495,18 @@ namespace osu.Game.Tests.Visual.Online
waitForChannel1Visible(); waitForChannel1Visible();
} }
private void joinTestChannel(int i)
{
AddStep($"Join test channel {i}", () => channelManager.JoinChannel(testChannels[i]));
AddUntilStep("wait for join completed", () => testChannels[i].Joined.Value);
}
private void joinChannel(Channel channel)
{
AddStep($"Join channel {channel}", () => channelManager.JoinChannel(channel));
AddUntilStep("wait for join completed", () => channel.Joined.Value);
}
private void waitForChannel1Visible() => private void waitForChannel1Visible() =>
AddUntilStep("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel?.Channel == testChannel1); AddUntilStep("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel?.Channel == testChannel1);

View File

@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
AddStep("set beatmap", () => advancedStats.BeatmapInfo = new BeatmapInfo AddStep("set beatmap", () => advancedStats.BeatmapInfo = new BeatmapInfo
{ {
Ruleset = rulesets.GetRuleset(3) ?? throw new InvalidOperationException(), Ruleset = rulesets.GetRuleset(3) ?? throw new InvalidOperationException("osu!mania ruleset not found"),
Difficulty = new BeatmapDifficulty Difficulty = new BeatmapDifficulty
{ {
CircleSize = 5, CircleSize = 5,

View File

@ -13,7 +13,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Overlays.Settings; using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
using osuTK; using osuTK;
@ -246,7 +246,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{ {
AddStep($"Set {name} slider to {value}", () => AddStep($"Set {name} slider to {value}", () =>
this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name) this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name)
.ChildrenOfType<SettingsSlider<float>>().First().Current.Value = value); .ChildrenOfType<OsuSliderBar<float>>().First().Current.Value = value);
} }
private void checkBindableAtValue(string name, float? expectedValue) private void checkBindableAtValue(string name, float? expectedValue)
@ -260,7 +260,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{ {
AddAssert($"Slider {name} at {expectedValue}", () => AddAssert($"Slider {name} at {expectedValue}", () =>
this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name) this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name)
.ChildrenOfType<SettingsSlider<float>>().First().Current.Value == expectedValue); .ChildrenOfType<OsuSliderBar<float>>().First().Current.Value == expectedValue);
} }
private void setBeatmapWithDifficultyParameters(float value) private void setBeatmapWithDifficultyParameters(float value)

View File

@ -230,7 +230,7 @@ namespace osu.Game.Tests.Visual.UserInterface
createScreen(); createScreen();
AddStep("select diff adjust", () => SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() }); AddStep("select diff adjust", () => SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() });
AddStep("set setting", () => modSelectOverlay.ChildrenOfType<SettingsSlider<float>>().First().Current.Value = 8); AddStep("set setting", () => modSelectOverlay.ChildrenOfType<OsuSliderBar<float>>().First().Current.Value = 8);
AddAssert("ensure setting is propagated", () => SelectedMods.Value.OfType<OsuModDifficultyAdjust>().Single().CircleSize.Value == 8); AddAssert("ensure setting is propagated", () => SelectedMods.Value.OfType<OsuModDifficultyAdjust>().Single().CircleSize.Value == 8);

View File

@ -207,7 +207,7 @@ namespace osu.Game.Beatmaps
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
return; return;
var starDifficulty = task.GetResultSafely(); StarDifficulty? starDifficulty = task.GetResultSafely();
if (starDifficulty != null) if (starDifficulty != null)
bindable.Value = starDifficulty.Value; bindable.Value = starDifficulty.Value;

View File

@ -18,7 +18,6 @@ using osu.Game.Database;
using osu.Game.Extensions; using osu.Game.Extensions;
using osu.Game.IO; using osu.Game.IO;
using osu.Game.IO.Archives; using osu.Game.IO.Archives;
using osu.Game.Models;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -49,7 +48,7 @@ namespace osu.Game.Beatmaps
protected override void Populate(BeatmapSetInfo beatmapSet, ArchiveReader? archive, Realm realm, CancellationToken cancellationToken = default) protected override void Populate(BeatmapSetInfo beatmapSet, ArchiveReader? archive, Realm realm, CancellationToken cancellationToken = default)
{ {
if (archive != null) if (archive != null)
beatmapSet.Beatmaps.AddRange(createBeatmapDifficulties(beatmapSet.Files, realm)); beatmapSet.Beatmaps.AddRange(createBeatmapDifficulties(beatmapSet, realm));
foreach (BeatmapInfo b in beatmapSet.Beatmaps) foreach (BeatmapInfo b in beatmapSet.Beatmaps)
{ {
@ -200,23 +199,32 @@ namespace osu.Game.Beatmaps
/// <summary> /// <summary>
/// Create all required <see cref="BeatmapInfo"/>s for the provided archive. /// Create all required <see cref="BeatmapInfo"/>s for the provided archive.
/// </summary> /// </summary>
private List<BeatmapInfo> createBeatmapDifficulties(IList<RealmNamedFileUsage> files, Realm realm) private List<BeatmapInfo> createBeatmapDifficulties(BeatmapSetInfo beatmapSet, Realm realm)
{ {
var beatmaps = new List<BeatmapInfo>(); var beatmaps = new List<BeatmapInfo>();
foreach (var file in files.Where(f => f.Filename.EndsWith(".osu", StringComparison.OrdinalIgnoreCase))) foreach (var file in beatmapSet.Files.Where(f => f.Filename.EndsWith(".osu", StringComparison.OrdinalIgnoreCase)))
{ {
using (var memoryStream = new MemoryStream(Files.Store.Get(file.File.GetStoragePath()))) // we need a memory stream so we can seek using (var memoryStream = new MemoryStream(Files.Store.Get(file.File.GetStoragePath()))) // we need a memory stream so we can seek
{ {
IBeatmap decoded; IBeatmap decoded;
using (var lineReader = new LineBufferedReader(memoryStream, true)) using (var lineReader = new LineBufferedReader(memoryStream, true))
{
if (lineReader.PeekLine() == null)
{
LogForModel(beatmapSet, $"No content found in beatmap file {file.Filename}.");
continue;
}
decoded = Decoder.GetDecoder<Beatmap>(lineReader).Decode(lineReader); decoded = Decoder.GetDecoder<Beatmap>(lineReader).Decode(lineReader);
}
string hash = memoryStream.ComputeSHA2Hash(); string hash = memoryStream.ComputeSHA2Hash();
if (beatmaps.Any(b => b.Hash == hash)) if (beatmaps.Any(b => b.Hash == hash))
{ {
Logger.Log($"Skipping import of {file.Filename} due to duplicate file content.", LoggingTarget.Database); LogForModel(beatmapSet, $"Skipping import of {file.Filename} due to duplicate file content.");
continue; continue;
} }
@ -227,7 +235,7 @@ namespace osu.Game.Beatmaps
if (ruleset?.Available != true) if (ruleset?.Available != true)
{ {
Logger.Log($"Skipping import of {file.Filename} due to missing local ruleset {decodedInfo.Ruleset.OnlineID}.", LoggingTarget.Database); LogForModel(beatmapSet, $"Skipping import of {file.Filename} due to missing local ruleset {decodedInfo.Ruleset.OnlineID}.");
continue; continue;
} }

View File

@ -34,7 +34,7 @@ namespace osu.Game.Beatmaps
/// </summary> /// </summary>
public StarDifficulty([NotNull] DifficultyAttributes attributes) public StarDifficulty([NotNull] DifficultyAttributes attributes)
{ {
Stars = attributes.StarRating; Stars = double.IsFinite(attributes.StarRating) ? attributes.StarRating : 0;
MaxCombo = attributes.MaxCombo; MaxCombo = attributes.MaxCombo;
Attributes = attributes; Attributes = attributes;
// Todo: Add more members (BeatmapInfo.DifficultyRating? Attributes? Etc...) // Todo: Add more members (BeatmapInfo.DifficultyRating? Attributes? Etc...)
@ -46,7 +46,7 @@ namespace osu.Game.Beatmaps
/// </summary> /// </summary>
public StarDifficulty(double starDifficulty, int maxCombo) public StarDifficulty(double starDifficulty, int maxCombo)
{ {
Stars = starDifficulty; Stars = double.IsFinite(starDifficulty) ? starDifficulty : 0;
MaxCombo = maxCombo; MaxCombo = maxCombo;
Attributes = null; Attributes = null;
} }

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using osu.Framework.Platform; using osu.Framework.Platform;
@ -46,6 +47,7 @@ namespace osu.Game.Database
Realm.Realm.Write(realm => Realm.Realm.Write(realm =>
{ {
var managed = realm.Find<TModel>(item.ID); var managed = realm.Find<TModel>(item.ID);
Debug.Assert(managed != null);
operation(managed); operation(managed);
item.Files.Clear(); item.Files.Clear();

View File

@ -128,7 +128,7 @@ namespace osu.Game.Database
Logger.Log(@$"Opened realm ""{updateRealm.Config.DatabasePath}"" at version {updateRealm.Config.SchemaVersion}"); Logger.Log(@$"Opened realm ""{updateRealm.Config.DatabasePath}"" at version {updateRealm.Config.SchemaVersion}");
// Resubscribe any subscriptions // Resubscribe any subscriptions
foreach (var action in customSubscriptionsResetMap.Keys) foreach (var action in customSubscriptionsResetMap.Keys.ToArray())
registerSubscription(action); registerSubscription(action);
} }

View File

@ -296,7 +296,8 @@ namespace osu.Game.Database
try try
{ {
LogForModel(item, @"Beginning import..."); // Log output here will be missing a valid hash in non-batch imports.
LogForModel(item, $@"Beginning import from {archive?.Name ?? "unknown"}...");
// TODO: do we want to make the transaction this local? not 100% sure, will need further investigation. // TODO: do we want to make the transaction this local? not 100% sure, will need further investigation.
using (var transaction = realm.BeginWrite()) using (var transaction = realm.BeginWrite())

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.ComponentModel; using System.ComponentModel;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Localisation namespace osu.Game.Localisation

View File

@ -1,7 +1,5 @@
// <auto-generated /> // <auto-generated />
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
#nullable disable
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations namespace osu.Game.Migrations

View File

@ -1,7 +1,5 @@
// <auto-generated /> // <auto-generated />
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
#nullable disable
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations namespace osu.Game.Migrations

View File

@ -1,7 +1,5 @@
// <auto-generated /> // <auto-generated />
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
#nullable disable
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations namespace osu.Game.Migrations

View File

@ -1,7 +1,5 @@
// <auto-generated /> // <auto-generated />
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
#nullable disable
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations namespace osu.Game.Migrations

View File

@ -1,7 +1,5 @@
// <auto-generated /> // <auto-generated />
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
#nullable disable
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations namespace osu.Game.Migrations

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using osu.Game.Database; using osu.Game.Database;

View File

@ -1,7 +1,5 @@
// <auto-generated /> // <auto-generated />
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
#nullable disable
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations namespace osu.Game.Migrations

View File

@ -1,7 +1,5 @@
// <auto-generated /> // <auto-generated />
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
#nullable disable
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations namespace osu.Game.Migrations

View File

@ -1,7 +1,5 @@
// <auto-generated /> // <auto-generated />
using System; using System;
#nullable disable
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations namespace osu.Game.Migrations

View File

@ -1,7 +1,5 @@
// <auto-generated /> // <auto-generated />
using System; using System;
#nullable disable
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations namespace osu.Game.Migrations

View File

@ -1,7 +1,5 @@
// <auto-generated /> // <auto-generated />
using System; using System;
#nullable disable
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations namespace osu.Game.Migrations

View File

@ -1,7 +1,5 @@
// <auto-generated /> // <auto-generated />
using System; using System;
#nullable disable
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations namespace osu.Game.Migrations

View File

@ -1,7 +1,5 @@
// <auto-generated /> // <auto-generated />
using System; using System;
#nullable disable
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations namespace osu.Game.Migrations

View File

@ -1,7 +1,5 @@
// <auto-generated /> // <auto-generated />
using System; using System;
#nullable disable
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System; using System;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@ -1,7 +1,5 @@
// <auto-generated /> // <auto-generated />
using System; using System;
#nullable disable
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations namespace osu.Game.Migrations

Some files were not shown because too many files have changed in this diff Show More