diff --git a/.editorconfig b/.editorconfig
index 67c47000d3..c249e5e9b3 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -9,6 +9,9 @@ indent_style = space
indent_size = 2
trim_trailing_whitespace = true
+[g_*.cs]
+generated_code = true
+
[*.cs]
end_of_line = crlf
insert_final_newline = true
diff --git a/osu.Android.props b/osu.Android.props
index fdec4e575b..270a0aa44b 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -11,7 +11,7 @@
manifestmerger.jar
-
+
diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs
index fe3e08537e..b1e11d7a60 100644
--- a/osu.Desktop/DiscordRichPresence.cs
+++ b/osu.Desktop/DiscordRichPresence.cs
@@ -187,7 +187,7 @@ namespace osu.Desktop
return edit.BeatmapInfo.ToString() ?? string.Empty;
case UserActivity.WatchingReplay watching:
- return watching.BeatmapInfo.ToString();
+ return watching.BeatmapInfo?.ToString() ?? string.Empty;
case UserActivity.InLobby lobby:
return privacyMode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value;
diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs
index efd3d358b7..a0db896f46 100644
--- a/osu.Desktop/OsuGameDesktop.cs
+++ b/osu.Desktop/OsuGameDesktop.cs
@@ -147,14 +147,12 @@ namespace osu.Desktop
{
base.SetHost(host);
- var desktopWindow = (SDL2DesktopWindow)host.Window;
-
var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico");
if (iconStream != null)
- desktopWindow.SetIconFromStream(iconStream);
+ host.Window.SetIconFromStream(iconStream);
- desktopWindow.CursorState |= CursorState.Hidden;
- desktopWindow.Title = Name;
+ host.Window.CursorState |= CursorState.Hidden;
+ host.Window.Title = Name;
}
protected override BatteryInfo CreateBatteryInfo() => new SDL2BatteryInfo();
diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs
index 55b24b3ffa..f38b9b430e 100644
--- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs
+++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs
@@ -4,6 +4,7 @@
using osu.Framework.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Catch.UI;
+using osu.Game.Screens.Play;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
@@ -59,7 +60,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
lastDisplayedCombo = combo;
- if (Time.Elapsed < 0)
+ if ((Clock as IGameplayClock)?.IsRewinding == true)
{
// needs more work to make rewind somehow look good.
// basically we want the previous increment to play... or turning off RemoveCompletedTransforms (not feasible from a performance angle).
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
index 1b99270b65..567c288b47 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -10,6 +10,7 @@ using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.UI;
+using osu.Game.Screens.Play;
using osuTK;
namespace osu.Game.Rulesets.Catch.UI
@@ -96,7 +97,7 @@ namespace osu.Game.Rulesets.Catch.UI
comboDisplay.X = Catcher.X;
- if (Time.Elapsed <= 0)
+ if ((Clock as IGameplayClock)?.IsRewinding == true)
{
// This is probably a wrong value, but currently the true value is not recorded.
// Setting `true` will prevent generation of false-positive after-images (with more false-negatives).
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
index a8563d65c4..c3fec92b92 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
@@ -16,6 +16,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Screens.Play;
using osu.Game.Skinning;
using osuTK;
@@ -298,7 +299,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return false;
// do not run any of this logic when rewinding, as it inverts order of presses/releases.
- if (Time.Elapsed < 0)
+ if ((Clock as IGameplayClock)?.IsRewinding == true)
return false;
if (CheckHittable?.Invoke(this, Time.Current) == false)
@@ -337,7 +338,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return;
// do not run any of this logic when rewinding, as it inverts order of presses/releases.
- if (Time.Elapsed < 0)
+ if ((Clock as IGameplayClock)?.IsRewinding == true)
return;
Tail.UpdateResult();
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs
index e1766adc20..d06fb5b4de 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs
@@ -14,6 +14,7 @@ using osu.Framework.Input.Events;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Skinning.Default;
+using osu.Game.Screens.Play;
using osu.Game.Skinning;
using osuTK;
@@ -179,16 +180,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private Vector2? lastPosition;
- private bool rewinding;
-
public void UpdateProgress(double completionProgress)
{
Position = drawableSlider.HitObject.CurvePositionAt(completionProgress);
var diff = lastPosition.HasValue ? lastPosition.Value - Position : Position - drawableSlider.HitObject.CurvePositionAt(completionProgress + 0.01f);
- if (Clock.ElapsedFrameTime != 0)
- rewinding = Clock.ElapsedFrameTime < 0;
+ bool rewinding = (Clock as IGameplayClock)?.IsRewinding == true;
// Ensure the value is substantially high enough to allow for Atan2 to get a valid angle.
if (diff.LengthFast < 0.01f)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index a193bacde5..ac4462c18b 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
effectiveMissCount = Math.Max(1.0, 1000.0 / totalSuccessfulHits) * countMiss;
// TODO: The detection of rulesets is temporary until the leftover old skills have been reworked.
- bool isConvert = score.BeatmapInfo.Ruleset.OnlineID != 1;
+ bool isConvert = score.BeatmapInfo!.Ruleset.OnlineID != 1;
double multiplier = 1.13;
diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs
index 37eb95b86f..5516e025cd 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@@ -8,6 +9,7 @@ using osu.Framework.Graphics.Animations;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Objects;
@@ -26,11 +28,12 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
private Bindable currentCombo { get; } = new BindableInt();
private int animationFrame;
- private double beatLength;
// required for editor blueprints (not sure why these circle pieces are zero size).
public override Quad ScreenSpaceDrawQuad => backgroundLayer.ScreenSpaceDrawQuad;
+ private TimingControlPoint timingPoint = TimingControlPoint.DEFAULT;
+
public LegacyCirclePiece()
{
RelativeSizeAxes = Axes.Both;
@@ -39,11 +42,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
[Resolved(canBeNull: true)]
private GameplayState? gameplayState { get; set; }
- [Resolved(canBeNull: true)]
- private IBeatSyncProvider? beatSyncProvider { get; set; }
-
[BackgroundDependencyLoader]
- private void load(ISkinSource skin, DrawableHitObject drawableHitObject)
+ private void load(ISkinSource skin, DrawableHitObject drawableHitObject, IBeatSyncProvider? beatSyncProvider)
{
Drawable? getDrawableFor(string lookup)
{
@@ -64,6 +64,11 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
if (foregroundLayer != null)
AddInternal(foregroundLayer);
+ drawableHitObject.StartTimeBindable.BindValueChanged(startTime =>
+ {
+ timingPoint = beatSyncProvider?.ControlPoints?.TimingPointAt(startTime.NewValue) ?? TimingControlPoint.DEFAULT;
+ }, true);
+
// Animations in taiko skins are used in a custom way (>150 combo and animating in time with beat).
// For now just stop at first frame for sanity.
foreach (var c in InternalChildren)
@@ -115,14 +120,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
return;
}
- if (beatSyncProvider?.ControlPoints != null)
- {
- beatLength = beatSyncProvider.ControlPoints.TimingPointAt(Time.Current).BeatLength;
-
- animationFrame = Time.Current % ((beatLength * 2) / multiplier) >= beatLength / multiplier ? 0 : 1;
-
- animatableForegroundLayer.GotoFrame(animationFrame);
- }
+ animationFrame = Math.Abs(Time.Current - timingPoint.Time) % ((timingPoint.BeatLength * 2) / multiplier) >= timingPoint.BeatLength / multiplier ? 0 : 1;
+ animatableForegroundLayer.GotoFrame(animationFrame);
}
private Color4 accentColour;
diff --git a/osu.Game.Tests/Beatmaps/WorkingBeatmapManagerTest.cs b/osu.Game.Tests/Beatmaps/WorkingBeatmapManagerTest.cs
index 89b8c8927d..237fe758b5 100644
--- a/osu.Game.Tests/Beatmaps/WorkingBeatmapManagerTest.cs
+++ b/osu.Game.Tests/Beatmaps/WorkingBeatmapManagerTest.cs
@@ -64,7 +64,7 @@ namespace osu.Game.Tests.Beatmaps
[Test]
public void TestCachedRetrievalWithFiles() => AddStep("run test", () =>
{
- var beatmap = Realm.Run(r => r.Find(importedSet.Beatmaps.First().ID).Detach());
+ var beatmap = Realm.Run(r => r.Find(importedSet.Beatmaps.First().ID)!.Detach());
Assert.That(beatmap.BeatmapSet?.Files, Has.Count.GreaterThan(0));
@@ -90,7 +90,7 @@ namespace osu.Game.Tests.Beatmaps
[Test]
public void TestForcedRefetchRetrievalWithFiles() => AddStep("run test", () =>
{
- var beatmap = Realm.Run(r => r.Find(importedSet.Beatmaps.First().ID).Detach());
+ var beatmap = Realm.Run(r => r.Find(importedSet.Beatmaps.First().ID)!.Detach());
Assert.That(beatmap.BeatmapSet?.Files, Has.Count.GreaterThan(0));
@@ -102,7 +102,7 @@ namespace osu.Game.Tests.Beatmaps
[Test]
public void TestSavePreservesCollections() => AddStep("run test", () =>
{
- var beatmap = Realm.Run(r => r.Find(importedSet.Beatmaps.First().ID).Detach());
+ var beatmap = Realm.Run(r => r.Find(importedSet.Beatmaps.First().ID)!.Detach());
var working = beatmaps.GetWorkingBeatmap(beatmap);
diff --git a/osu.Game.Tests/Database/BackgroundBeatmapProcessorTests.cs b/osu.Game.Tests/Database/BackgroundBeatmapProcessorTests.cs
index ddb60606ec..c876316be4 100644
--- a/osu.Game.Tests/Database/BackgroundBeatmapProcessorTests.cs
+++ b/osu.Game.Tests/Database/BackgroundBeatmapProcessorTests.cs
@@ -42,7 +42,7 @@ namespace osu.Game.Tests.Database
{
return Realm.Run(r =>
{
- var beatmapSetInfo = r.Find(importedSet.ID);
+ var beatmapSetInfo = r.Find(importedSet.ID)!;
return beatmapSetInfo.Beatmaps.All(b => b.StarRating > 0);
});
});
@@ -51,7 +51,7 @@ namespace osu.Game.Tests.Database
{
Realm.Write(r =>
{
- var beatmapSetInfo = r.Find(importedSet.ID);
+ var beatmapSetInfo = r.Find(importedSet.ID)!;
foreach (var b in beatmapSetInfo.Beatmaps)
b.StarRating = -1;
});
@@ -66,7 +66,7 @@ namespace osu.Game.Tests.Database
{
return Realm.Run(r =>
{
- var beatmapSetInfo = r.Find(importedSet.ID);
+ var beatmapSetInfo = r.Find(importedSet.ID)!;
return beatmapSetInfo.Beatmaps.All(b => b.StarRating > 0);
});
});
@@ -79,7 +79,7 @@ namespace osu.Game.Tests.Database
{
return Realm.Run(r =>
{
- var beatmapSetInfo = r.Find(importedSet.ID);
+ var beatmapSetInfo = r.Find(importedSet.ID)!;
return beatmapSetInfo.Beatmaps.All(b => b.StarRating > 0);
});
});
@@ -90,7 +90,7 @@ namespace osu.Game.Tests.Database
{
Realm.Write(r =>
{
- var beatmapSetInfo = r.Find(importedSet.ID);
+ var beatmapSetInfo = r.Find(importedSet.ID)!;
foreach (var b in beatmapSetInfo.Beatmaps)
b.StarRating = -1;
});
@@ -107,7 +107,7 @@ namespace osu.Game.Tests.Database
{
return Realm.Run(r =>
{
- var beatmapSetInfo = r.Find(importedSet.ID);
+ var beatmapSetInfo = r.Find(importedSet.ID)!;
return beatmapSetInfo.Beatmaps.All(b => b.StarRating == -1);
});
});
@@ -118,7 +118,7 @@ namespace osu.Game.Tests.Database
{
return Realm.Run(r =>
{
- var beatmapSetInfo = r.Find(importedSet.ID);
+ var beatmapSetInfo = r.Find(importedSet.ID)!;
return beatmapSetInfo.Beatmaps.All(b => b.StarRating > 0);
});
});
diff --git a/osu.Game.Tests/Database/BeatmapImporterTests.cs b/osu.Game.Tests/Database/BeatmapImporterTests.cs
index 84e6a6c00f..0eac70f9c8 100644
--- a/osu.Game.Tests/Database/BeatmapImporterTests.cs
+++ b/osu.Game.Tests/Database/BeatmapImporterTests.cs
@@ -417,6 +417,60 @@ namespace osu.Game.Tests.Database
});
}
+ [Test]
+ public void TestImport_Modify_Revert()
+ {
+ RunTestWithRealmAsync(async (realm, storage) =>
+ {
+ var importer = new BeatmapImporter(storage, realm);
+ using var store = new RealmRulesetStore(realm, storage);
+
+ var imported = await LoadOszIntoStore(importer, realm.Realm);
+
+ await createScoreForBeatmap(realm.Realm, imported.Beatmaps.First());
+
+ var score = realm.Run(r => r.All().Single());
+
+ string originalHash = imported.Beatmaps.First().Hash;
+ const string modified_hash = "new_hash";
+
+ Assert.That(imported.Beatmaps.First().Scores.Single(), Is.EqualTo(score));
+
+ Assert.That(score.BeatmapHash, Is.EqualTo(originalHash));
+ Assert.That(score.BeatmapInfo, Is.EqualTo(imported.Beatmaps.First()));
+
+ // imitate making local changes via editor
+ // ReSharper disable once MethodHasAsyncOverload
+ realm.Write(r =>
+ {
+ BeatmapInfo beatmap = imported.Beatmaps.First();
+ beatmap.Hash = modified_hash;
+ beatmap.ResetOnlineInfo();
+ beatmap.UpdateLocalScores(r);
+ });
+
+ Assert.That(!imported.Beatmaps.First().Scores.Any());
+
+ Assert.That(score.BeatmapInfo, Is.Null);
+ Assert.That(score.BeatmapHash, Is.EqualTo(originalHash));
+
+ // imitate reverting the local changes made above
+ // ReSharper disable once MethodHasAsyncOverload
+ realm.Write(r =>
+ {
+ BeatmapInfo beatmap = imported.Beatmaps.First();
+ beatmap.Hash = originalHash;
+ beatmap.ResetOnlineInfo();
+ beatmap.UpdateLocalScores(r);
+ });
+
+ Assert.That(imported.Beatmaps.First().Scores.Single(), Is.EqualTo(score));
+
+ Assert.That(score.BeatmapHash, Is.EqualTo(originalHash));
+ Assert.That(score.BeatmapInfo, Is.EqualTo(imported.Beatmaps.First()));
+ });
+ }
+
[Test]
public void TestImport_ThenModifyMapWithScore_ThenImport()
{
@@ -431,19 +485,19 @@ namespace osu.Game.Tests.Database
await createScoreForBeatmap(realm.Realm, imported.Beatmaps.First());
+ Assert.That(imported.Beatmaps.First().Scores.Any());
+
// imitate making local changes via editor
// ReSharper disable once MethodHasAsyncOverload
- realm.Write(_ =>
+ realm.Write(r =>
{
BeatmapInfo beatmap = imported.Beatmaps.First();
beatmap.Hash = "new_hash";
beatmap.ResetOnlineInfo();
+ beatmap.UpdateLocalScores(r);
});
- // for now, making changes to a beatmap doesn't remove the backlink from the score to the beatmap.
- // the logic of ensuring that scores match the beatmap is upheld via comparing the hash in usages (see: https://github.com/ppy/osu/pull/22539).
- // TODO: revisit when fixing https://github.com/ppy/osu/issues/24069.
- Assert.That(imported.Beatmaps.First().Scores.Any());
+ Assert.That(!imported.Beatmaps.First().Scores.Any());
var importedSecondTime = await importer.Import(new ImportTask(temp));
@@ -461,6 +515,7 @@ namespace osu.Game.Tests.Database
Assert.That(importedFirstTimeBeatmap.Hash != importedSecondTimeBeatmap.Hash);
Assert.That(!importedFirstTimeBeatmap.Scores.Any());
Assert.That(importedSecondTimeBeatmap.Scores.Count() == 1);
+ Assert.That(importedSecondTimeBeatmap.Scores.Single().BeatmapInfo, Is.EqualTo(importedSecondTimeBeatmap));
});
}
diff --git a/osu.Game.Tests/Database/BeatmapImporterUpdateTests.cs b/osu.Game.Tests/Database/BeatmapImporterUpdateTests.cs
index 83cb54df3f..d30b3c089e 100644
--- a/osu.Game.Tests/Database/BeatmapImporterUpdateTests.cs
+++ b/osu.Game.Tests/Database/BeatmapImporterUpdateTests.cs
@@ -323,7 +323,7 @@ namespace osu.Game.Tests.Database
var beatmapInfo = s.Beatmaps.First(b => b.File?.Filename != removedFilename);
scoreTargetBeatmapHash = beatmapInfo.Hash;
- s.Realm.Add(new ScoreInfo(beatmapInfo, s.Realm.All().First(), new RealmUser()));
+ s.Realm!.Add(new ScoreInfo(beatmapInfo, s.Realm.All().First(), new RealmUser()));
});
realm.Run(r => r.Refresh());
@@ -372,7 +372,7 @@ namespace osu.Game.Tests.Database
scoreTargetBeatmapHash = beatmapInfo.Hash;
- s.Realm.Add(new ScoreInfo(beatmapInfo, s.Realm.All().First(), new RealmUser()));
+ s.Realm!.Add(new ScoreInfo(beatmapInfo, s.Realm.All().First(), new RealmUser()));
});
// locally modify beatmap
@@ -383,14 +383,14 @@ namespace osu.Game.Tests.Database
beatmapInfo.Hash = new_beatmap_hash;
beatmapInfo.ResetOnlineInfo();
+ beatmapInfo.UpdateLocalScores(s.Realm!);
});
realm.Run(r => r.Refresh());
- // for now, making changes to a beatmap doesn't remove the backlink from the score to the beatmap.
- // the logic of ensuring that scores match the beatmap is upheld via comparing the hash in usages (https://github.com/ppy/osu/pull/22539).
- // TODO: revisit when fixing https://github.com/ppy/osu/issues/24069.
+ // making changes to a beatmap doesn't remove the score from realm, but should disassociate the beatmap.
checkCount(realm, 1);
+ Assert.That(realm.Run(r => r.All().First().BeatmapInfo), Is.Null);
// reimport the original beatmap before local modifications
var importAfterUpdate = await importer.ImportAsUpdate(new ProgressNotification(), new ImportTask(pathOnlineCopy), importBeforeUpdate.Value);
@@ -435,7 +435,7 @@ namespace osu.Game.Tests.Database
{
var beatmapInfo = s.Beatmaps.Last();
scoreTargetFilename = beatmapInfo.File?.Filename;
- s.Realm.Add(new ScoreInfo(beatmapInfo, s.Realm.All().First(), new RealmUser()));
+ s.Realm!.Add(new ScoreInfo(beatmapInfo, s.Realm.All().First(), new RealmUser()));
});
realm.Run(r => r.Refresh());
@@ -528,7 +528,7 @@ namespace osu.Game.Tests.Database
importBeforeUpdate.PerformWrite(s =>
{
- var beatmapCollection = s.Realm.Add(new BeatmapCollection("test collection"));
+ var beatmapCollection = s.Realm!.Add(new BeatmapCollection("test collection"));
beatmapsToAddToCollection = s.Beatmaps.Count - (allOriginalBeatmapsInCollection ? 0 : 1);
for (int i = 0; i < beatmapsToAddToCollection; i++)
@@ -543,7 +543,7 @@ namespace osu.Game.Tests.Database
importAfterUpdate.PerformRead(updated =>
{
- updated.Realm.Refresh();
+ updated.Realm!.Refresh();
string[] hashes = updated.Realm.All().Single().BeatmapMD5Hashes.ToArray();
@@ -593,7 +593,7 @@ namespace osu.Game.Tests.Database
importBeforeUpdate.PerformWrite(s =>
{
- var beatmapCollection = s.Realm.Add(new BeatmapCollection("test collection"));
+ var beatmapCollection = s.Realm!.Add(new BeatmapCollection("test collection"));
originalHash = s.Beatmaps.Single(b => b.DifficultyName == "Hard").MD5Hash;
beatmapCollection.BeatmapMD5Hashes.Add(originalHash);
@@ -607,7 +607,7 @@ namespace osu.Game.Tests.Database
importAfterUpdate.PerformRead(updated =>
{
- updated.Realm.Refresh();
+ updated.Realm!.Refresh();
string[] hashes = updated.Realm.All().Single().BeatmapMD5Hashes.ToArray();
string updatedHash = updated.Beatmaps.Single(b => b.DifficultyName == "Hard").MD5Hash;
diff --git a/osu.Game.Tests/Database/GeneralUsageTests.cs b/osu.Game.Tests/Database/GeneralUsageTests.cs
index fd0b391d0d..b8073a65bc 100644
--- a/osu.Game.Tests/Database/GeneralUsageTests.cs
+++ b/osu.Game.Tests/Database/GeneralUsageTests.cs
@@ -128,7 +128,7 @@ namespace osu.Game.Tests.Database
realm.RegisterCustomSubscription(r =>
{
- var subscription = r.All().QueryAsyncWithNotifications((_, _, _) =>
+ var subscription = r.All().QueryAsyncWithNotifications((_, _) =>
{
realm.Run(_ =>
{
diff --git a/osu.Game.Tests/Database/RealmLiveTests.cs b/osu.Game.Tests/Database/RealmLiveTests.cs
index d853e75db0..cea30acf3f 100644
--- a/osu.Game.Tests/Database/RealmLiveTests.cs
+++ b/osu.Game.Tests/Database/RealmLiveTests.cs
@@ -355,7 +355,7 @@ namespace osu.Game.Tests.Database
return null;
});
- void gotChange(IRealmCollection sender, ChangeSet changes, Exception error)
+ void gotChange(IRealmCollection sender, ChangeSet? changes)
{
changesTriggered++;
}
diff --git a/osu.Game.Tests/Database/RealmSubscriptionRegistrationTests.cs b/osu.Game.Tests/Database/RealmSubscriptionRegistrationTests.cs
index 4ee302bbd0..45842a952a 100644
--- a/osu.Game.Tests/Database/RealmSubscriptionRegistrationTests.cs
+++ b/osu.Game.Tests/Database/RealmSubscriptionRegistrationTests.cs
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@@ -54,7 +53,7 @@ namespace osu.Game.Tests.Database
registration.Dispose();
});
- void onChanged(IRealmCollection sender, ChangeSet? changes, Exception error)
+ void onChanged(IRealmCollection sender, ChangeSet? changes)
{
lastChanges = changes;
@@ -92,7 +91,7 @@ namespace osu.Game.Tests.Database
registration.Dispose();
});
- void onChanged(IRealmCollection sender, ChangeSet? changes, Exception error) => lastChanges = changes;
+ void onChanged(IRealmCollection sender, ChangeSet? changes) => lastChanges = changes;
}
[Test]
@@ -185,7 +184,7 @@ namespace osu.Game.Tests.Database
}
});
- void onChanged(IRealmCollection sender, ChangeSet? changes, Exception error)
+ void onChanged(IRealmCollection sender, ChangeSet? changes)
{
if (changes == null)
resolvedItems = sender;
diff --git a/osu.Game.Tests/Database/RulesetStoreTests.cs b/osu.Game.Tests/Database/RulesetStoreTests.cs
index a5662fa121..8b4c6e2411 100644
--- a/osu.Game.Tests/Database/RulesetStoreTests.cs
+++ b/osu.Game.Tests/Database/RulesetStoreTests.cs
@@ -76,12 +76,12 @@ namespace osu.Game.Tests.Database
Available = true,
}));
- Assert.That(realm.Run(r => r.Find(rulesetShortName).Available), Is.True);
+ Assert.That(realm.Run(r => r.Find(rulesetShortName)!.Available), Is.True);
// Availability is updated on construction of a RealmRulesetStore
var _ = new RealmRulesetStore(realm, storage);
- Assert.That(realm.Run(r => r.Find(rulesetShortName).Available), Is.False);
+ Assert.That(realm.Run(r => r.Find(rulesetShortName)!.Available), Is.False);
});
}
@@ -101,18 +101,18 @@ namespace osu.Game.Tests.Database
Available = true,
}));
- Assert.That(realm.Run(r => r.Find(rulesetShortName).Available), Is.True);
+ Assert.That(realm.Run(r => r.Find(rulesetShortName)!.Available), Is.True);
// Availability is updated on construction of a RealmRulesetStore
var _ = new RealmRulesetStore(realm, storage);
- Assert.That(realm.Run(r => r.Find(rulesetShortName).Available), Is.False);
+ Assert.That(realm.Run(r => r.Find(rulesetShortName)!.Available), Is.False);
// Simulate the ruleset getting updated
LoadTestRuleset.Version = Ruleset.CURRENT_RULESET_API_VERSION;
var __ = new RealmRulesetStore(realm, storage);
- Assert.That(realm.Run(r => r.Find(rulesetShortName).Available), Is.True);
+ Assert.That(realm.Run(r => r.Find(rulesetShortName)!.Available), Is.True);
});
}
diff --git a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs
index f4467867db..e2774cef00 100644
--- a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs
+++ b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs
@@ -104,7 +104,7 @@ namespace osu.Game.Tests.Database
realm.Run(innerRealm =>
{
- var binding = innerRealm.ResolveReference(tsr);
+ var binding = innerRealm.ResolveReference(tsr)!;
innerRealm.Write(() => binding.KeyCombination = new KeyCombination(InputKey.BackSpace));
});
diff --git a/osu.Game.Tests/Models/DisplayStringTest.cs b/osu.Game.Tests/Models/DisplayStringTest.cs
index d585a0eb9f..b5303e1dd6 100644
--- a/osu.Game.Tests/Models/DisplayStringTest.cs
+++ b/osu.Game.Tests/Models/DisplayStringTest.cs
@@ -87,10 +87,10 @@ namespace osu.Game.Tests.Models
var mock = new Mock();
mock.Setup(m => m.User).Returns(new APIUser { Username = "user" }); // TODO: temporary.
- mock.Setup(m => m.Beatmap.Metadata.Artist).Returns("artist");
- mock.Setup(m => m.Beatmap.Metadata.Title).Returns("title");
- mock.Setup(m => m.Beatmap.Metadata.Author.Username).Returns("author");
- mock.Setup(m => m.Beatmap.DifficultyName).Returns("difficulty");
+ mock.Setup(m => m.Beatmap!.Metadata.Artist).Returns("artist");
+ mock.Setup(m => m.Beatmap!.Metadata.Title).Returns("title");
+ mock.Setup(m => m.Beatmap!.Metadata.Author.Username).Returns("author");
+ mock.Setup(m => m.Beatmap!.DifficultyName).Returns("difficulty");
Assert.That(mock.Object.GetDisplayString(), Is.EqualTo("user playing artist - title (author) [difficulty]"));
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs
index 59c9c4587a..0f16d3f394 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs
@@ -107,7 +107,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
base.SetUpSteps();
- AddStep("Add trigger source", () => Player.GameplayClockContainer.Add(sampleTriggerSource = new TestGameplaySampleTriggerSource(Player.DrawableRuleset.Playfield.HitObjectContainer)));
+ AddStep("Add trigger source", () => Player.DrawableRuleset.FrameStableComponents.Add(sampleTriggerSource = new TestGameplaySampleTriggerSource(Player.DrawableRuleset.Playfield.HitObjectContainer)));
}
[Test]
@@ -153,6 +153,14 @@ namespace osu.Game.Tests.Visual.Gameplay
waitForAliveObjectIndex(2);
checkValidObjectIndex(2);
+ // test rewinding
+ seekBeforeIndex(1);
+ waitForAliveObjectIndex(1);
+ checkValidObjectIndex(1);
+
+ seekBeforeIndex(1, 400);
+ checkValidObjectIndex(0);
+
seekBeforeIndex(3);
waitForAliveObjectIndex(3);
checkValidObjectIndex(3);
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs
index 80c4e4bce9..b0b9d48cbe 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs
@@ -131,7 +131,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddAssert("results screen score has matching", () => (Player.GetChildScreen() as ResultsScreen)?.Score.Mods.First(), () => Is.EqualTo(playerMods.First()));
AddUntilStep("score in database", () => Realm.Run(r => r.Find(Player.Score.ScoreInfo.ID) != null));
- AddUntilStep("databased score has correct mods", () => Realm.Run(r => r.Find(Player.Score.ScoreInfo.ID)).Mods.First(), () => Is.EqualTo(playerMods.First()));
+ AddUntilStep("databased score has correct mods", () => Realm.Run(r => r.Find(Player.Score.ScoreInfo.ID))!.Mods.First(), () => Is.EqualTo(playerMods.First()));
}
[Test]
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs
index 3004cb8a0c..0f17b08b7b 100644
--- a/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs
@@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual.Ranking
AddStep("show excess mods score", () =>
{
var score = TestResources.CreateTestScoreInfo();
- score.Mods = score.BeatmapInfo.Ruleset.CreateInstance().CreateAllMods().ToArray();
+ score.Mods = score.BeatmapInfo!.Ruleset.CreateInstance().CreateAllMods().ToArray();
showPanel(CreateWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)), score);
});
}
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs
index c05774400f..d71c72f4ec 100644
--- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs
@@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Ranking
var author = new RealmUser { Username = "mapper_name" };
var score = TestResources.CreateTestScoreInfo(createTestBeatmap(author));
- score.Mods = score.BeatmapInfo.Ruleset.CreateInstance().CreateAllMods().ToArray();
+ score.Mods = score.BeatmapInfo!.Ruleset.CreateInstance().CreateAllMods().ToArray();
showPanel(score);
});
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
index 42068ff117..c5b61c1a90 100644
--- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
@@ -405,7 +405,7 @@ namespace osu.Game.Tests.Visual.Ranking
public UnrankedSoloResultsScreen(ScoreInfo score)
: base(score, true)
{
- Score.BeatmapInfo.OnlineID = 0;
+ Score.BeatmapInfo!.OnlineID = 0;
Score.BeatmapInfo.Status = BeatmapOnlineStatus.Pending;
}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
index af3a6e178c..0bff40f258 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
@@ -163,7 +163,7 @@ namespace osu.Game.Tests.Visual.SongSelect
InputManager.Key(Key.Enter);
});
- AddUntilStep("wait for not current", () => !songSelect!.IsCurrentScreen());
+ waitForDismissed();
AddAssert("ensure selection changed", () => selected != Beatmap.Value);
}
@@ -186,7 +186,7 @@ namespace osu.Game.Tests.Visual.SongSelect
InputManager.Key(Key.Down);
});
- AddUntilStep("wait for not current", () => !songSelect!.IsCurrentScreen());
+ waitForDismissed();
AddAssert("ensure selection didn't change", () => selected == Beatmap.Value);
}
@@ -215,7 +215,7 @@ namespace osu.Game.Tests.Visual.SongSelect
InputManager.Key(Key.Enter);
});
- AddUntilStep("wait for not current", () => !songSelect!.IsCurrentScreen());
+ waitForDismissed();
AddAssert("ensure selection changed", () => selected != Beatmap.Value);
}
@@ -244,7 +244,7 @@ namespace osu.Game.Tests.Visual.SongSelect
InputManager.ReleaseButton(MouseButton.Left);
});
- AddUntilStep("wait for not current", () => !songSelect!.IsCurrentScreen());
+ waitForDismissed();
AddAssert("ensure selection didn't change", () => selected == Beatmap.Value);
}
@@ -257,7 +257,7 @@ namespace osu.Game.Tests.Visual.SongSelect
createSongSelect();
AddStep("push child screen", () => Stack.Push(new TestSceneOsuScreenStack.TestScreen("test child")));
- AddUntilStep("wait for not current", () => !songSelect!.IsCurrentScreen());
+ waitForDismissed();
AddStep("return", () => songSelect!.MakeCurrent());
AddUntilStep("wait for current", () => songSelect!.IsCurrentScreen());
@@ -275,7 +275,7 @@ namespace osu.Game.Tests.Visual.SongSelect
createSongSelect();
AddStep("push child screen", () => Stack.Push(new TestSceneOsuScreenStack.TestScreen("test child")));
- AddUntilStep("wait for not current", () => !songSelect!.IsCurrentScreen());
+ waitForDismissed();
AddStep("change convert setting", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, true));
@@ -292,7 +292,7 @@ namespace osu.Game.Tests.Visual.SongSelect
createSongSelect();
AddStep("push child screen", () => Stack.Push(new TestSceneOsuScreenStack.TestScreen("test child")));
- AddUntilStep("wait for not current", () => !songSelect!.IsCurrentScreen());
+ waitForDismissed();
AddStep("update beatmap", () =>
{
@@ -1011,7 +1011,7 @@ namespace osu.Game.Tests.Visual.SongSelect
});
});
- AddUntilStep("wait for results screen presented", () => !songSelect!.IsCurrentScreen());
+ waitForDismissed();
AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(getPresentBeatmap()));
AddAssert("check ruleset is correct for score", () => Ruleset.Value.OnlineID == 0);
@@ -1040,7 +1040,7 @@ namespace osu.Game.Tests.Visual.SongSelect
songSelect!.PresentScore(TestResources.CreateTestScoreInfo(getPresentBeatmap()));
});
- AddUntilStep("wait for results screen presented", () => !songSelect!.IsCurrentScreen());
+ waitForDismissed();
AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(getPresentBeatmap()));
AddAssert("check ruleset is correct for score", () => Ruleset.Value.OnlineID == 0);
@@ -1161,6 +1161,8 @@ namespace osu.Game.Tests.Visual.SongSelect
rulesets.Dispose();
}
+ private void waitForDismissed() => AddUntilStep("wait for not current", () => !songSelect.AsNonNull().IsCurrentScreen());
+
private partial class TestSongSelect : PlaySongSelect
{
public Action? StartRequested;
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs
index 2d54a4e566..bf6d8e524f 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs
@@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual.UserInterface
var testPresets = createTestPresets();
foreach (var preset in testPresets)
- preset.Ruleset = realm.Find(preset.Ruleset.ShortName);
+ preset.Ruleset = realm.Find(preset.Ruleset.ShortName)!;
realm.Add(testPresets);
});
@@ -103,7 +103,7 @@ namespace osu.Game.Tests.Visual.UserInterface
new ManiaModNightcore(),
new ManiaModHardRock()
},
- Ruleset = r.Find("mania")
+ Ruleset = r.Find("mania")!
})));
AddUntilStep("2 panels visible", () => this.ChildrenOfType().Count() == 2);
@@ -115,7 +115,7 @@ namespace osu.Game.Tests.Visual.UserInterface
new OsuModHidden(),
new OsuModHardRock()
},
- Ruleset = r.Find("osu")
+ Ruleset = r.Find("osu")!
})));
AddUntilStep("2 panels visible", () => this.ChildrenOfType().Count() == 2);
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
index dcb1f730a2..4cb6899ebc 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
@@ -60,7 +60,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{
Name = "AR0",
Description = "Too... many... circles...",
- Ruleset = r.Find(OsuRuleset.SHORT_NAME),
+ Ruleset = r.Find(OsuRuleset.SHORT_NAME)!,
Mods = new[]
{
new OsuModDifficultyAdjust
@@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{
Name = "Half Time 0.5x",
Description = "Very slow",
- Ruleset = r.Find(OsuRuleset.SHORT_NAME),
+ Ruleset = r.Find(OsuRuleset.SHORT_NAME)!,
Mods = new[]
{
new OsuModHalfTime
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs
index 3cd5daf7a1..4d3ae079e3 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs
@@ -52,6 +52,32 @@ namespace osu.Game.Tests.Visual.UserInterface
notificationOverlay.UnreadCount.ValueChanged += count => { displayedCount.Text = $"unread count: {count.NewValue}"; };
});
+ [Test]
+ public void TestBasicFlow()
+ {
+ setState(Visibility.Visible);
+ AddStep(@"simple #1", sendHelloNotification);
+ AddStep(@"simple #2", sendAmazingNotification);
+ AddStep(@"progress #1", sendUploadProgress);
+ AddStep(@"progress #2", sendDownloadProgress);
+
+ checkProgressingCount(2);
+
+ setState(Visibility.Hidden);
+
+ AddRepeatStep(@"add many simple", sendManyNotifications, 3);
+
+ waitForCompletion();
+
+ AddStep(@"progress #3", sendUploadProgress);
+
+ checkProgressingCount(1);
+
+ checkDisplayedCount(33);
+
+ waitForCompletion();
+ }
+
[Test]
public void TestForwardWithFlingRight()
{
@@ -411,32 +437,6 @@ namespace osu.Game.Tests.Visual.UserInterface
AddUntilStep("wait for update applied", () => applyUpdate);
}
- [Test]
- public void TestBasicFlow()
- {
- setState(Visibility.Visible);
- AddStep(@"simple #1", sendHelloNotification);
- AddStep(@"simple #2", sendAmazingNotification);
- AddStep(@"progress #1", sendUploadProgress);
- AddStep(@"progress #2", sendDownloadProgress);
-
- checkProgressingCount(2);
-
- setState(Visibility.Hidden);
-
- AddRepeatStep(@"add many simple", sendManyNotifications, 3);
-
- waitForCompletion();
-
- AddStep(@"progress #3", sendUploadProgress);
-
- checkProgressingCount(1);
-
- checkDisplayedCount(33);
-
- waitForCompletion();
- }
-
[Test]
public void TestImportantWhileClosed()
{
diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs
index 7d0571dde0..29f0f4f356 100644
--- a/osu.Game.Tournament/TournamentGameBase.cs
+++ b/osu.Game.Tournament/TournamentGameBase.cs
@@ -9,7 +9,6 @@ using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using osu.Framework.Allocation;
-using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
@@ -94,7 +93,7 @@ namespace osu.Game.Tournament
Task.Run(readBracket);
}
- private void readBracket()
+ private async Task readBracket()
{
try
{
@@ -102,7 +101,7 @@ namespace osu.Game.Tournament
{
using (Stream stream = storage.GetStream(BRACKET_FILENAME, FileAccess.Read, FileMode.Open))
using (var sr = new StreamReader(stream))
- ladder = JsonConvert.DeserializeObject(sr.ReadToEnd(), new JsonPointConverter());
+ ladder = JsonConvert.DeserializeObject(await sr.ReadToEndAsync().ConfigureAwait(false), new JsonPointConverter());
}
ladder ??= new LadderInfo();
@@ -166,8 +165,8 @@ namespace osu.Game.Tournament
}
addedInfo |= addPlayers();
- addedInfo |= addRoundBeatmaps();
- addedInfo |= addSeedingBeatmaps();
+ addedInfo |= await addRoundBeatmaps().ConfigureAwait(false);
+ addedInfo |= await addSeedingBeatmaps().ConfigureAwait(false);
if (addedInfo)
saveChanges();
@@ -233,7 +232,7 @@ namespace osu.Game.Tournament
///
/// Add missing beatmap info based on beatmap IDs
///
- private bool addRoundBeatmaps()
+ private async Task addRoundBeatmaps()
{
var beatmapsRequiringPopulation = ladder.Rounds
.SelectMany(r => r.Beatmaps)
@@ -246,7 +245,7 @@ namespace osu.Game.Tournament
{
var b = beatmapsRequiringPopulation[i];
- b.Beatmap = new TournamentBeatmap(beatmapCache.GetBeatmapAsync(b.ID).GetResultSafely() ?? new APIBeatmap());
+ b.Beatmap = new TournamentBeatmap(await beatmapCache.GetBeatmapAsync(b.ID).ConfigureAwait(false) ?? new APIBeatmap());
updateLoadProgressMessage($"Populating round beatmaps ({i} / {beatmapsRequiringPopulation.Count})");
}
@@ -257,7 +256,7 @@ namespace osu.Game.Tournament
///
/// Add missing beatmap info based on beatmap IDs
///
- private bool addSeedingBeatmaps()
+ private async Task addSeedingBeatmaps()
{
var beatmapsRequiringPopulation = ladder.Teams
.SelectMany(r => r.SeedingResults)
@@ -271,7 +270,7 @@ namespace osu.Game.Tournament
{
var b = beatmapsRequiringPopulation[i];
- b.Beatmap = new TournamentBeatmap(beatmapCache.GetBeatmapAsync(b.ID).GetResultSafely() ?? new APIBeatmap());
+ b.Beatmap = new TournamentBeatmap(await beatmapCache.GetBeatmapAsync(b.ID).ConfigureAwait(false) ?? new APIBeatmap());
updateLoadProgressMessage($"Populating seeding beatmaps ({i} / {beatmapsRequiringPopulation.Count})");
}
diff --git a/osu.Game/BackgroundBeatmapProcessor.cs b/osu.Game/BackgroundBeatmapProcessor.cs
index 9fe3a41b03..9f62dae1f5 100644
--- a/osu.Game/BackgroundBeatmapProcessor.cs
+++ b/osu.Game/BackgroundBeatmapProcessor.cs
@@ -102,7 +102,7 @@ namespace osu.Game
}
}
- r.Find(ruleset.ShortName).LastAppliedDifficultyVersion = currentVersion;
+ r.Find(ruleset.ShortName)!.LastAppliedDifficultyVersion = currentVersion;
});
Logger.Log($"Finished resetting {countReset} beatmap sets for {ruleset.Name}");
@@ -163,8 +163,12 @@ namespace osu.Game
{
foreach (var score in r.All())
{
- if (score.Statistics.Sum(kvp => kvp.Value) > 0 && score.MaximumStatistics.Sum(kvp => kvp.Value) == 0)
+ if (score.BeatmapInfo != null
+ && score.Statistics.Sum(kvp => kvp.Value) > 0
+ && score.MaximumStatistics.Sum(kvp => kvp.Value) == 0)
+ {
scoreIds.Add(score.ID);
+ }
}
});
@@ -184,7 +188,7 @@ namespace osu.Game
// ReSharper disable once MethodHasAsyncOverload
realmAccess.Write(r =>
{
- r.Find(id).MaximumStatisticsJson = JsonConvert.SerializeObject(score.MaximumStatistics);
+ r.Find(id)!.MaximumStatisticsJson = JsonConvert.SerializeObject(score.MaximumStatistics);
});
Logger.Log($"Populated maximum statistics for score {id}");
@@ -204,7 +208,9 @@ namespace osu.Game
{
Logger.Log("Querying for scores that need total score conversion...");
- HashSet scoreIds = realmAccess.Run(r => new HashSet(r.All().Where(s => s.TotalScoreVersion == 30000002).AsEnumerable().Select(s => s.ID)));
+ HashSet scoreIds = realmAccess.Run(r => new HashSet(r.All()
+ .Where(s => s.BeatmapInfo != null && s.TotalScoreVersion == 30000002)
+ .AsEnumerable().Select(s => s.ID)));
Logger.Log($"Found {scoreIds.Count} scores which require total score conversion.");
@@ -237,7 +243,7 @@ namespace osu.Game
// ReSharper disable once MethodHasAsyncOverload
realmAccess.Write(r =>
{
- ScoreInfo s = r.Find(id);
+ ScoreInfo s = r.Find(id)!;
s.TotalScore = newTotalScore;
s.TotalScoreVersion = LegacyScoreEncoder.LATEST_VERSION;
});
diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs
index fd766490fc..eadf7a3666 100644
--- a/osu.Game/Beatmaps/BeatmapImporter.cs
+++ b/osu.Game/Beatmaps/BeatmapImporter.cs
@@ -20,7 +20,6 @@ using osu.Game.IO;
using osu.Game.IO.Archives;
using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets;
-using osu.Game.Scoring;
using Realms;
namespace osu.Game.Beatmaps
@@ -69,7 +68,7 @@ namespace osu.Game.Beatmaps
Logger.Log($"Beatmap \"{updated}\" update completed successfully", LoggingTarget.Database);
- original = realm.Find(original.ID);
+ original = realm!.Find(original.ID)!;
// Generally the import process will do this for us if the OnlineIDs match,
// but that isn't a guarantee (ie. if the .osu file doesn't have OnlineIDs populated).
@@ -210,8 +209,7 @@ namespace osu.Game.Beatmaps
// Let's reattach any matching scores that exist in the database, based on hash.
foreach (BeatmapInfo beatmap in model.Beatmaps)
{
- foreach (var score in realm.All().Where(score => score.BeatmapHash == beatmap.Hash))
- score.BeatmapInfo = beatmap;
+ beatmap.UpdateLocalScores(realm);
}
ProcessBeatmap?.Invoke(model, parameters.Batch ? MetadataLookupScope.LocalCacheFirst : MetadataLookupScope.OnlineFirst);
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 5019d64276..c1aeec1f71 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -234,6 +234,22 @@ namespace osu.Game.Beatmaps
}
}
+ ///
+ /// Local scores are retained separate from a beatmap's lifetime, matched via .
+ /// Therefore we need to detach / reattach scores when a beatmap is edited or imported.
+ ///
+ /// A realm instance in an active write transaction.
+ public void UpdateLocalScores(Realm realm)
+ {
+ // first disassociate any scores which are already attached and no longer valid.
+ foreach (var score in Scores)
+ score.BeatmapInfo = null;
+
+ // then attach any scores which match the new hash.
+ foreach (var score in realm.All().Where(s => s.BeatmapHash == Hash))
+ score.BeatmapInfo = this;
+ }
+
IBeatmapMetadataInfo IBeatmapInfo.Metadata => Metadata;
IBeatmapSetInfo? IBeatmapInfo.BeatmapSet => BeatmapSet;
IRulesetInfo IBeatmapInfo.Ruleset => Ruleset;
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 73811b2e62..54c243e842 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -208,7 +208,7 @@ namespace osu.Game.Beatmaps
using (var transaction = r.BeginWrite())
{
if (!beatmapInfo.IsManaged)
- beatmapInfo = r.Find(beatmapInfo.ID);
+ beatmapInfo = r.Find(beatmapInfo.ID)!;
beatmapInfo.Hidden = true;
transaction.Commit();
@@ -227,7 +227,7 @@ namespace osu.Game.Beatmaps
using (var transaction = r.BeginWrite())
{
if (!beatmapInfo.IsManaged)
- beatmapInfo = r.Find(beatmapInfo.ID);
+ beatmapInfo = r.Find(beatmapInfo.ID)!;
beatmapInfo.Hidden = false;
transaction.Commit();
@@ -330,7 +330,7 @@ namespace osu.Game.Beatmaps
Realm.Write(r =>
{
if (!beatmapInfo.IsManaged)
- beatmapInfo = r.Find(beatmapInfo.ID);
+ beatmapInfo = r.Find(beatmapInfo.ID)!;
Debug.Assert(beatmapInfo.BeatmapSet != null);
Debug.Assert(beatmapInfo.File != null);
@@ -460,13 +460,16 @@ namespace osu.Game.Beatmaps
Realm.Write(r =>
{
- var liveBeatmapSet = r.Find(setInfo.ID);
+ var liveBeatmapSet = r.Find(setInfo.ID)!;
setInfo.CopyChangesToRealm(liveBeatmapSet);
if (transferCollections)
beatmapInfo.TransferCollectionReferences(r, oldMd5Hash);
+ liveBeatmapSet.Beatmaps.Single(b => b.ID == beatmapInfo.ID)
+ .UpdateLocalScores(r);
+
// do not look up metadata.
// this is a locally-modified set now, so looking up metadata is busy work at best and harmful at worst.
ProcessBeatmap?.Invoke(liveBeatmapSet, MetadataLookupScope.None);
diff --git a/osu.Game/Beatmaps/BeatmapUpdater.cs b/osu.Game/Beatmaps/BeatmapUpdater.cs
index 046adb8327..56bfdc5001 100644
--- a/osu.Game/Beatmaps/BeatmapUpdater.cs
+++ b/osu.Game/Beatmaps/BeatmapUpdater.cs
@@ -52,7 +52,7 @@ namespace osu.Game.Beatmaps
///
/// The managed beatmap set to update. A transaction will be opened to apply changes.
/// The preferred scope to use for metadata lookup.
- public void Process(BeatmapSetInfo beatmapSet, MetadataLookupScope lookupScope = MetadataLookupScope.LocalCacheFirst) => beatmapSet.Realm.Write(r =>
+ public void Process(BeatmapSetInfo beatmapSet, MetadataLookupScope lookupScope = MetadataLookupScope.LocalCacheFirst) => beatmapSet.Realm!.Write(_ =>
{
// Before we use below, we want to invalidate.
workingBeatmapCache.Invalidate(beatmapSet);
diff --git a/osu.Game/Collections/CollectionDropdown.cs b/osu.Game/Collections/CollectionDropdown.cs
index e95565a5c8..e435992381 100644
--- a/osu.Game/Collections/CollectionDropdown.cs
+++ b/osu.Game/Collections/CollectionDropdown.cs
@@ -59,7 +59,7 @@ namespace osu.Game.Collections
Current.BindValueChanged(selectionChanged);
}
- private void collectionsChanged(IRealmCollection collections, ChangeSet? changes, Exception error)
+ private void collectionsChanged(IRealmCollection collections, ChangeSet? changes)
{
var selectedItem = SelectedItem?.Value?.Collection;
diff --git a/osu.Game/Collections/DrawableCollectionList.cs b/osu.Game/Collections/DrawableCollectionList.cs
index 0fdf196c4a..6fe38a3229 100644
--- a/osu.Game/Collections/DrawableCollectionList.cs
+++ b/osu.Game/Collections/DrawableCollectionList.cs
@@ -41,7 +41,7 @@ namespace osu.Game.Collections
realmSubscription = realm.RegisterForNotifications(r => r.All().OrderBy(c => c.Name), collectionsChanged);
}
- private void collectionsChanged(IRealmCollection collections, ChangeSet? changes, Exception error)
+ private void collectionsChanged(IRealmCollection collections, ChangeSet? changes)
{
Items.Clear();
Items.AddRange(collections.AsEnumerable().Select(c => c.ToLive(realm)));
diff --git a/osu.Game/Collections/DrawableCollectionListItem.cs b/osu.Game/Collections/DrawableCollectionListItem.cs
index 0ab0ff520d..4131148f3f 100644
--- a/osu.Game/Collections/DrawableCollectionListItem.cs
+++ b/osu.Game/Collections/DrawableCollectionListItem.cs
@@ -197,7 +197,7 @@ namespace osu.Game.Collections
return true;
}
- private void deleteCollection() => collection.PerformWrite(c => c.Realm.Remove(c));
+ private void deleteCollection() => collection.PerformWrite(c => c.Realm!.Remove(c));
}
}
}
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index ba555a7926..edcbb94368 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -129,6 +129,7 @@ namespace osu.Game.Configuration
SetDefault(OsuSetting.ShowHealthDisplayWhenCantFail, true);
SetDefault(OsuSetting.FadePlayfieldWhenHealthLow, true);
SetDefault(OsuSetting.KeyOverlay, false);
+ SetDefault(OsuSetting.ReplaySettingsOverlay, true);
SetDefault(OsuSetting.GameplayLeaderboard, true);
SetDefault(OsuSetting.AlwaysPlayFirstComboBreak, true);
@@ -382,6 +383,7 @@ namespace osu.Game.Configuration
SafeAreaConsiderations,
ComboColourNormalisationAmount,
ProfileCoverExpanded,
- EditorLimitedDistanceSnap
+ EditorLimitedDistanceSnap,
+ ReplaySettingsOverlay
}
}
diff --git a/osu.Game/Database/EmptyRealmSet.cs b/osu.Game/Database/EmptyRealmSet.cs
index 7db946d79f..02dfa50fe5 100644
--- a/osu.Game/Database/EmptyRealmSet.cs
+++ b/osu.Game/Database/EmptyRealmSet.cs
@@ -19,8 +19,8 @@ namespace osu.Game.Database
IEnumerator IEnumerable.GetEnumerator() => emptySet.GetEnumerator();
public int Count => emptySet.Count;
public T this[int index] => emptySet[index];
- public int IndexOf(object item) => emptySet.IndexOf((T)item);
- public bool Contains(object item) => emptySet.Contains((T)item);
+ public int IndexOf(object? item) => item == null ? -1 : emptySet.IndexOf((T)item);
+ public bool Contains(object? item) => item != null && emptySet.Contains((T)item);
public event NotifyCollectionChangedEventHandler? CollectionChanged
{
diff --git a/osu.Game/Database/ModelManager.cs b/osu.Game/Database/ModelManager.cs
index 7d1dc5239a..47feb8a8f9 100644
--- a/osu.Game/Database/ModelManager.cs
+++ b/osu.Game/Database/ModelManager.cs
@@ -34,13 +34,13 @@ namespace osu.Game.Database
}
public void DeleteFile(TModel item, RealmNamedFileUsage file) =>
- performFileOperation(item, managed => DeleteFile(managed, managed.Files.First(f => f.Filename == file.Filename), managed.Realm));
+ performFileOperation(item, managed => DeleteFile(managed, managed.Files.First(f => f.Filename == file.Filename), managed.Realm!));
public void ReplaceFile(TModel item, RealmNamedFileUsage file, Stream contents) =>
- performFileOperation(item, managed => ReplaceFile(file, contents, managed.Realm));
+ performFileOperation(item, managed => ReplaceFile(file, contents, managed.Realm!));
public void AddFile(TModel item, Stream contents, string filename) =>
- performFileOperation(item, managed => AddFile(managed, contents, filename, managed.Realm));
+ performFileOperation(item, managed => AddFile(managed, contents, filename, managed.Realm!));
private void performFileOperation(TModel item, Action operation)
{
@@ -178,13 +178,14 @@ namespace osu.Game.Database
// (ie. if an async import finished very recently).
return Realm.Write(realm =>
{
- if (!item.IsManaged)
- item = realm.Find(item.ID);
+ TModel? processableItem = item;
+ if (!processableItem.IsManaged)
+ processableItem = realm.Find(item.ID);
- if (item?.DeletePending != false)
+ if (processableItem?.DeletePending != false)
return false;
- item.DeletePending = true;
+ processableItem.DeletePending = true;
return true;
});
}
@@ -195,13 +196,14 @@ namespace osu.Game.Database
// (ie. if an async import finished very recently).
Realm.Write(realm =>
{
- if (!item.IsManaged)
- item = realm.Find(item.ID);
+ TModel? processableItem = item;
+ if (!processableItem.IsManaged)
+ processableItem = realm.Find(item.ID);
- if (item?.DeletePending != true)
+ if (processableItem?.DeletePending != true)
return;
- item.DeletePending = false;
+ processableItem.DeletePending = false;
});
}
diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs
index 2bc932f307..f9f11c49ff 100644
--- a/osu.Game/Database/RealmAccess.cs
+++ b/osu.Game/Database/RealmAccess.cs
@@ -535,7 +535,7 @@ namespace osu.Game.Database
lock (notificationsResetMap)
{
// Store an action which is used when blocking to ensure consumers don't use results of a stale changeset firing.
- notificationsResetMap.Add(action, () => callback(new EmptyRealmSet(), null, null));
+ notificationsResetMap.Add(action, () => callback(new EmptyRealmSet(), null));
}
return RegisterCustomSubscription(action);
@@ -755,10 +755,10 @@ namespace osu.Game.Database
for (int i = 0; i < itemCount; i++)
{
- dynamic? oldItem = oldItems.ElementAt(i);
- dynamic? newItem = newItems.ElementAt(i);
+ dynamic oldItem = oldItems.ElementAt(i);
+ dynamic newItem = newItems.ElementAt(i);
- long? nullableOnlineID = oldItem?.OnlineID;
+ long? nullableOnlineID = oldItem.OnlineID;
newItem.OnlineID = (int)(nullableOnlineID ?? -1);
}
}
@@ -795,7 +795,7 @@ namespace osu.Game.Database
for (int i = 0; i < metadataCount; i++)
{
- dynamic? oldItem = oldMetadata.ElementAt(i);
+ dynamic oldItem = oldMetadata.ElementAt(i);
var newItem = newMetadata.ElementAt(i);
string username = oldItem.Author;
@@ -818,7 +818,7 @@ namespace osu.Game.Database
for (int i = 0; i < newSettings.Count; i++)
{
- dynamic? oldItem = oldSettings.ElementAt(i);
+ dynamic oldItem = oldSettings.ElementAt(i);
var newItem = newSettings.ElementAt(i);
long rulesetId = oldItem.RulesetID;
@@ -843,7 +843,7 @@ namespace osu.Game.Database
for (int i = 0; i < newKeyBindings.Count; i++)
{
- dynamic? oldItem = oldKeyBindings.ElementAt(i);
+ dynamic oldItem = oldKeyBindings.ElementAt(i);
var newItem = newKeyBindings.ElementAt(i);
if (oldItem.RulesetID == null)
@@ -897,7 +897,7 @@ namespace osu.Game.Database
var scores = migration.NewRealm.All();
foreach (var score in scores)
- score.BeatmapHash = score.BeatmapInfo.Hash;
+ score.BeatmapHash = score.BeatmapInfo?.Hash ?? string.Empty;
break;
}
diff --git a/osu.Game/Database/RealmLive.cs b/osu.Game/Database/RealmLive.cs
index 9c871a3929..509fabec59 100644
--- a/osu.Game/Database/RealmLive.cs
+++ b/osu.Game/Database/RealmLive.cs
@@ -4,6 +4,7 @@
using System;
using System.Diagnostics;
using osu.Framework.Development;
+using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Statistics;
using Realms;
@@ -104,7 +105,7 @@ namespace osu.Game.Database
PerformRead(t =>
{
- using (var transaction = t.Realm.BeginWrite())
+ using (var transaction = t.Realm!.BeginWrite())
{
perform(t);
transaction.Commit();
@@ -133,7 +134,7 @@ namespace osu.Game.Database
{
Debug.Assert(ThreadSafety.IsUpdateThread);
- if (dataIsFromUpdateThread && !data.Realm.IsClosed)
+ if (dataIsFromUpdateThread && !data.Realm.AsNonNull().IsClosed)
{
RealmLiveStatistics.USAGE_UPDATE_IMMEDIATE.Value++;
return;
@@ -154,7 +155,7 @@ namespace osu.Game.Database
// To ensure that behaviour matches what we'd expect (the object *is* available), force
// a refresh to bring in any off-thread changes immediately.
realm.Refresh();
- found = realm.Find(ID);
+ found = realm.Find(ID)!;
}
return found;
diff --git a/osu.Game/Database/RealmObjectExtensions.cs b/osu.Game/Database/RealmObjectExtensions.cs
index 5a6c2e3232..72529ed9ff 100644
--- a/osu.Game/Database/RealmObjectExtensions.cs
+++ b/osu.Game/Database/RealmObjectExtensions.cs
@@ -43,7 +43,7 @@ namespace osu.Game.Database
.ForMember(s => s.BeatmapSet, cc => cc.Ignore())
.AfterMap((s, d) =>
{
- d.Ruleset = d.Realm.Find(s.Ruleset.ShortName);
+ d.Ruleset = d.Realm!.Find(s.Ruleset.ShortName)!;
copyChangesToRealm(s.Difficulty, d.Difficulty);
copyChangesToRealm(s.Metadata, d.Metadata);
});
@@ -57,7 +57,7 @@ namespace osu.Game.Database
// Importantly, search all of realm for the beatmap (not just the set's beatmaps).
// It may have gotten detached, and if that's the case let's use this opportunity to fix
// things up.
- var existingBeatmap = d.Realm.Find(beatmap.ID);
+ var existingBeatmap = d.Realm!.Find(beatmap.ID);
if (existingBeatmap != null)
{
@@ -77,7 +77,7 @@ namespace osu.Game.Database
{
ID = beatmap.ID,
BeatmapSet = d,
- Ruleset = d.Realm.Find(beatmap.Ruleset.ShortName)
+ Ruleset = d.Realm.Find(beatmap.Ruleset.ShortName)!
};
d.Beatmaps.Add(newBeatmap);
@@ -282,12 +282,10 @@ namespace osu.Game.Database
///
/// A subscription token. It must be kept alive for as long as you want to receive change notifications.
/// To stop receiving notifications, call .
- ///
- /// May be null in the case the provided collection is not managed.
///
///
///
- public static IDisposable? QueryAsyncWithNotifications(this IRealmCollection collection, NotificationCallbackDelegate callback)
+ public static IDisposable QueryAsyncWithNotifications(this IRealmCollection collection, NotificationCallbackDelegate callback)
where T : RealmObjectBase
{
if (!RealmAccess.CurrentThreadSubscriptionsAllowed)
diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs
index fab0be6cf0..be025e3aa2 100644
--- a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs
+++ b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs
@@ -51,7 +51,7 @@ namespace osu.Game.Input.Bindings
protected override void LoadComplete()
{
- realmSubscription = realm.RegisterForNotifications(queryRealmKeyBindings, (sender, _, _) =>
+ realmSubscription = realm.RegisterForNotifications(queryRealmKeyBindings, (sender, _) =>
{
// The first fire of this is a bit redundant as this is being called in base.LoadComplete,
// but this is safest in case the subscription is restored after a context recycle.
diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs
index 64268c73d0..01c454e3f9 100644
--- a/osu.Game/Input/Bindings/GlobalActionContainer.cs
+++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs
@@ -129,6 +129,7 @@ namespace osu.Game.Input.Bindings
new KeyBinding(InputKey.MouseMiddle, GlobalAction.TogglePauseReplay),
new KeyBinding(InputKey.Left, GlobalAction.SeekReplayBackward),
new KeyBinding(InputKey.Right, GlobalAction.SeekReplayForward),
+ new KeyBinding(new[] { InputKey.Control, InputKey.H }, GlobalAction.ToggleReplaySettings),
};
public IEnumerable SongSelectKeyBindings => new[]
@@ -374,5 +375,8 @@ namespace osu.Game.Input.Bindings
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ExportReplay))]
ExportReplay,
+
+ [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleReplaySettings))]
+ ToggleReplaySettings,
}
}
diff --git a/osu.Game/Localisation/GameplaySettingsStrings.cs b/osu.Game/Localisation/GameplaySettingsStrings.cs
index 40f39d927d..f52f6abb89 100644
--- a/osu.Game/Localisation/GameplaySettingsStrings.cs
+++ b/osu.Game/Localisation/GameplaySettingsStrings.cs
@@ -65,10 +65,15 @@ namespace osu.Game.Localisation
public static LocalisableString HUDVisibilityMode => new TranslatableString(getKey(@"hud_visibility_mode"), @"HUD overlay visibility mode");
///
- /// "Show health display even when you can't fail"
+ /// "Show health display even when you can't fail"
///
public static LocalisableString ShowHealthDisplayWhenCantFail => new TranslatableString(getKey(@"show_health_display_when_cant_fail"), @"Show health display even when you can't fail");
+ ///
+ /// "Show replay settings overlay"
+ ///
+ public static LocalisableString ShowReplaySettingsOverlay => new TranslatableString(getKey(@"show_replay_settings_overlay"), @"Show replay settings overlay");
+
///
/// "Fade playfield to red when health is low"
///
@@ -134,6 +139,6 @@ namespace osu.Game.Localisation
///
public static LocalisableString ClassicScoreDisplay => new TranslatableString(getKey(@"classic_score_display"), @"Classic");
- private static string getKey(string key) => $"{prefix}:{key}";
+ private static string getKey(string key) => $@"{prefix}:{key}";
}
}
diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs
index 9e53b23180..f93d86225c 100644
--- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs
+++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs
@@ -324,6 +324,11 @@ namespace osu.Game.Localisation
///
public static LocalisableString ToggleChatFocus => new TranslatableString(getKey(@"toggle_chat_focus"), @"Toggle chat focus");
+ ///
+ /// "Toggle replay settings"
+ ///
+ public static LocalisableString ToggleReplaySettings => new TranslatableString(getKey(@"toggle_replay_settings"), @"Toggle replay settings");
+
///
/// "Save replay"
///
diff --git a/osu.Game/Localisation/NotificationsStrings.cs b/osu.Game/Localisation/NotificationsStrings.cs
index 44e440e8d9..53687f2b28 100644
--- a/osu.Game/Localisation/NotificationsStrings.cs
+++ b/osu.Game/Localisation/NotificationsStrings.cs
@@ -29,11 +29,6 @@ namespace osu.Game.Localisation
///
public static LocalisableString ClearAll => new TranslatableString(getKey(@"clear_all"), @"Clear All");
- ///
- /// "Cancel All"
- ///
- public static LocalisableString CancelAll => new TranslatableString(getKey(@"cancel_all"), @"Cancel All");
-
///
/// "Your battery level is low! Charge your device to prevent interruptions during gameplay."
///
diff --git a/osu.Game/Online/BeatmapDownloadTracker.cs b/osu.Game/Online/BeatmapDownloadTracker.cs
index 144c4445a3..3db602c353 100644
--- a/osu.Game/Online/BeatmapDownloadTracker.cs
+++ b/osu.Game/Online/BeatmapDownloadTracker.cs
@@ -40,7 +40,7 @@ namespace osu.Game.Online
// Used to interact with manager classes that don't support interface types. Will eventually be replaced.
var beatmapSetInfo = new BeatmapSetInfo { OnlineID = TrackedItem.OnlineID };
- realmSubscription = realm.RegisterForNotifications(r => r.All().Where(s => s.OnlineID == TrackedItem.OnlineID && !s.DeletePending), (items, _, _) =>
+ realmSubscription = realm.RegisterForNotifications(r => r.All().Where(s => s.OnlineID == TrackedItem.OnlineID && !s.DeletePending), (items, _) =>
{
if (items.Any())
Schedule(() => UpdateState(DownloadState.LocallyAvailable));
diff --git a/osu.Game/Online/Chat/MessageNotifier.cs b/osu.Game/Online/Chat/MessageNotifier.cs
index ae249d1b7f..65aac723da 100644
--- a/osu.Game/Online/Chat/MessageNotifier.cs
+++ b/osu.Game/Online/Chat/MessageNotifier.cs
@@ -171,6 +171,8 @@ namespace osu.Game.Online.Chat
public abstract partial class HighlightMessageNotification : SimpleNotification
{
+ public override string PopInSampleName => "UI/notification-mention";
+
protected HighlightMessageNotification(Message message, Channel channel)
{
this.message = message;
diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs
index 331a471ad5..ceb8e53778 100644
--- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs
+++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs
@@ -107,7 +107,7 @@ namespace osu.Game.Online.Rooms
// handles changes to hash that didn't occur from the import process (ie. a user editing the beatmap in the editor, somehow).
realmSubscription?.Dispose();
- realmSubscription = realm.RegisterForNotifications(_ => filteredBeatmaps(), (_, changes, _) =>
+ realmSubscription = realm.RegisterForNotifications(_ => filteredBeatmaps(), (_, changes) =>
{
if (changes == null)
return;
diff --git a/osu.Game/Online/ScoreDownloadTracker.cs b/osu.Game/Online/ScoreDownloadTracker.cs
index 4ddcb40368..de42292372 100644
--- a/osu.Game/Online/ScoreDownloadTracker.cs
+++ b/osu.Game/Online/ScoreDownloadTracker.cs
@@ -48,7 +48,7 @@ namespace osu.Game.Online
realmSubscription = realm.RegisterForNotifications(r => r.All().Where(s =>
((s.OnlineID > 0 && s.OnlineID == TrackedItem.OnlineID)
|| (!string.IsNullOrEmpty(s.Hash) && s.Hash == TrackedItem.Hash))
- && !s.DeletePending), (items, _, _) =>
+ && !s.DeletePending), (items, _) =>
{
if (items.Any())
Schedule(() => UpdateState(DownloadState.LocallyAvailable));
diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs
index 89da8b9d32..14e137caf1 100644
--- a/osu.Game/Online/Spectator/SpectatorClient.cs
+++ b/osu.Game/Online/Spectator/SpectatorClient.cs
@@ -185,7 +185,7 @@ namespace osu.Game.Online.Spectator
IsPlaying = true;
// transfer state at point of beginning play
- currentState.BeatmapID = score.ScoreInfo.BeatmapInfo.OnlineID;
+ currentState.BeatmapID = score.ScoreInfo.BeatmapInfo!.OnlineID;
currentState.RulesetID = score.ScoreInfo.RulesetID;
currentState.Mods = score.ScoreInfo.Mods.Select(m => new APIMod(m)).ToArray();
currentState.State = SpectatedUserState.Playing;
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 93dd97ea15..5b654e0c16 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -289,9 +289,9 @@ namespace osu.Game
{
base.SetHost(host);
- if (host.Window is SDL2Window sdlWindow)
+ if (host.Window != null)
{
- sdlWindow.DragDrop += path =>
+ host.Window.DragDrop += path =>
{
// on macOS/iOS, URL associations are handled via SDL_DROPFILE events.
if (path.StartsWith(OSU_PROTOCOL, StringComparison.Ordinal))
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs
index 425f40258e..615a3e39af 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs
@@ -163,7 +163,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
},
username,
#pragma warning disable 618
- new StatisticText(score.MaxCombo, score.BeatmapInfo.MaxCombo, @"0\x"),
+ new StatisticText(score.MaxCombo, score.BeatmapInfo!.MaxCombo, @"0\x"),
#pragma warning restore 618
};
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs
index e030b1e34f..c92b79cb4d 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs
@@ -123,7 +123,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
accuracyColumn.Text = value.DisplayAccuracy;
maxComboColumn.Text = value.MaxCombo.ToLocalisableString(@"0\x");
- ppColumn.Alpha = value.BeatmapInfo.Status.GrantsPerformancePoints() ? 1 : 0;
+ ppColumn.Alpha = value.BeatmapInfo!.Status.GrantsPerformancePoints() ? 1 : 0;
if (value.PP is double pp)
ppColumn.Text = pp.ToLocalisableString(@"N0");
diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs
index 9969677826..e1e5604e4c 100644
--- a/osu.Game/Overlays/Dialog/PopupDialog.cs
+++ b/osu.Game/Overlays/Dialog/PopupDialog.cs
@@ -225,7 +225,12 @@ namespace osu.Game.Overlays.Dialog
///
/// Programmatically clicks the first button of the provided type.
///
- public void PerformAction() where T : PopupDialogButton => Buttons.OfType().First().TriggerClick();
+ public void PerformAction() where T : PopupDialogButton
+ {
+ // Buttons are regularly added in BDL or LoadComplete, so let's schedule to ensure
+ // they are ready to be pressed.
+ Schedule(() => Buttons.OfType().First().TriggerClick());
+ }
protected override bool OnKeyDown(KeyDownEvent e)
{
diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs
index 75bc8fd3a8..385695f669 100644
--- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs
+++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs
@@ -123,7 +123,7 @@ namespace osu.Game.Overlays.FirstRunSetup
beatmapSubscription?.Dispose();
}
- private void beatmapsChanged(IRealmCollection sender, ChangeSet? changes, Exception error) => Schedule(() =>
+ private void beatmapsChanged(IRealmCollection sender, ChangeSet? changes) => Schedule(() =>
{
currentlyLoadedBeatmaps.Text = FirstRunSetupBeatmapScreenStrings.CurrentlyLoadedBeatmaps(sender.Count);
diff --git a/osu.Game/Overlays/INotificationOverlay.cs b/osu.Game/Overlays/INotificationOverlay.cs
index c5ff10c619..19c646a714 100644
--- a/osu.Game/Overlays/INotificationOverlay.cs
+++ b/osu.Game/Overlays/INotificationOverlay.cs
@@ -42,8 +42,8 @@ namespace osu.Game.Overlays
IEnumerable AllNotifications { get; }
///
- /// All ongoing operations (ie. any not in a completed state).
+ /// All ongoing operations (ie. any not in a completed or cancelled state).
///
- public IEnumerable OngoingOperations => AllNotifications.OfType().Where(p => p.State != ProgressNotificationState.Completed && p.State != ProgressNotificationState.Cancelled);
+ public IEnumerable OngoingOperations => AllNotifications.OfType().Where(p => p.Ongoing);
}
}
diff --git a/osu.Game/Overlays/Mods/AddPresetPopover.cs b/osu.Game/Overlays/Mods/AddPresetPopover.cs
index d9e350e560..ef855f6166 100644
--- a/osu.Game/Overlays/Mods/AddPresetPopover.cs
+++ b/osu.Game/Overlays/Mods/AddPresetPopover.cs
@@ -102,7 +102,7 @@ namespace osu.Game.Overlays.Mods
Name = nameTextBox.Current.Value,
Description = descriptionTextBox.Current.Value,
Mods = selectedMods.Value.ToArray(),
- Ruleset = r.Find(ruleset.Value.ShortName)
+ Ruleset = r.Find(ruleset.Value.ShortName)!
}));
this.HidePopover();
diff --git a/osu.Game/Overlays/Mods/ModPresetColumn.cs b/osu.Game/Overlays/Mods/ModPresetColumn.cs
index bf5e576277..3b12eec195 100644
--- a/osu.Game/Overlays/Mods/ModPresetColumn.cs
+++ b/osu.Game/Overlays/Mods/ModPresetColumn.cs
@@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Mods
private Task? latestLoadTask;
internal bool ItemsLoaded => latestLoadTask?.IsCompleted == true;
- private void asyncLoadPanels(IRealmCollection presets, ChangeSet changes, Exception error)
+ private void asyncLoadPanels(IRealmCollection presets, ChangeSet? changes)
{
cancellationTokenSource?.Cancel();
diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs
index 43b9024303..7784643163 100644
--- a/osu.Game/Overlays/Music/PlaylistOverlay.cs
+++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs
@@ -109,7 +109,7 @@ namespace osu.Game.Overlays.Music
beatmap.BindValueChanged(working => list.SelectedSet.Value = working.NewValue.BeatmapSetInfo.ToLive(realm), true);
}
- private void beatmapsChanged(IRealmCollection sender, ChangeSet changes, Exception error)
+ private void beatmapsChanged(IRealmCollection sender, ChangeSet changes)
{
if (changes == null)
{
diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs
index 21027b0931..c9d09848f8 100644
--- a/osu.Game/Overlays/NotificationOverlay.cs
+++ b/osu.Game/Overlays/NotificationOverlay.cs
@@ -108,8 +108,8 @@ namespace osu.Game.Overlays
RelativeSizeAxes = Axes.X,
Children = new[]
{
- new NotificationSection(AccountsStrings.NotificationsTitle, new[] { typeof(SimpleNotification) }, NotificationsStrings.ClearAll),
- new NotificationSection(NotificationsStrings.RunningTasks, new[] { typeof(ProgressNotification) }, NotificationsStrings.CancelAll),
+ new NotificationSection(AccountsStrings.NotificationsTitle, new[] { typeof(SimpleNotification) }),
+ new NotificationSection(NotificationsStrings.RunningTasks, new[] { typeof(ProgressNotification) }),
}
}
}
@@ -169,7 +169,7 @@ namespace osu.Game.Overlays
Logger.Log($"⚠️ {notification.Text}");
- notification.Closed += notificationClosed;
+ notification.Closed += () => notificationClosed(notification);
if (notification is IHasCompletionTarget hasCompletionTarget)
hasCompletionTarget.CompletionTarget = Post;
@@ -229,17 +229,20 @@ namespace osu.Game.Overlays
mainContent.FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.In);
}
- private void notificationClosed() => Schedule(() =>
+ private void notificationClosed(Notification notification) => Schedule(() =>
{
updateCounts();
// this debounce is currently shared between popin/popout sounds, which means one could potentially not play when the user is expecting it.
// popout is constant across all notification types, and should therefore be handled using playback concurrency instead, but seems broken at the moment.
- playDebouncedSample("UI/overlay-pop-out");
+ playDebouncedSample(notification.PopOutSampleName);
});
private void playDebouncedSample(string sampleName)
{
+ if (string.IsNullOrEmpty(sampleName))
+ return;
+
if (lastSamplePlayback == null || Time.Current - lastSamplePlayback > OsuGameBase.SAMPLE_DEBOUNCE_TIME)
{
audio.Samples.Get(sampleName)?.Play();
diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs
index 77d3317b1f..8cdc373417 100644
--- a/osu.Game/Overlays/Notifications/Notification.cs
+++ b/osu.Game/Overlays/Notifications/Notification.cs
@@ -50,7 +50,8 @@ namespace osu.Game.Overlays.Notifications
///
public virtual bool DisplayOnTop => true;
- public virtual string PopInSampleName => "UI/notification-pop-in";
+ public virtual string PopInSampleName => "UI/notification-default";
+ public virtual string PopOutSampleName => "UI/overlay-pop-out";
protected NotificationLight Light;
diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs
index 4e28ade802..10c2900d63 100644
--- a/osu.Game/Overlays/Notifications/NotificationSection.cs
+++ b/osu.Game/Overlays/Notifications/NotificationSection.cs
@@ -13,6 +13,7 @@ using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
+using osu.Game.Localisation;
using osuTK;
namespace osu.Game.Overlays.Notifications
@@ -38,15 +39,12 @@ namespace osu.Game.Overlays.Notifications
public IEnumerable AcceptedNotificationTypes { get; }
- private readonly LocalisableString clearButtonText;
-
private readonly LocalisableString titleText;
- public NotificationSection(LocalisableString title, IEnumerable acceptedNotificationTypes, LocalisableString clearButtonText)
+ public NotificationSection(LocalisableString title, IEnumerable acceptedNotificationTypes)
{
AcceptedNotificationTypes = acceptedNotificationTypes.ToArray();
- this.clearButtonText = clearButtonText.ToUpper();
titleText = title;
}
@@ -75,7 +73,7 @@ namespace osu.Game.Overlays.Notifications
{
new ClearAllButton
{
- Text = clearButtonText,
+ Text = NotificationsStrings.ClearAll.ToUpper(),
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Action = clearAll
@@ -115,10 +113,11 @@ namespace osu.Game.Overlays.Notifications
});
}
- private void clearAll()
+ private void clearAll() => notifications.Children.ForEach(c =>
{
- notifications.Children.ForEach(c => c.Close(true));
- }
+ if (c is not ProgressNotification p || !p.Ongoing)
+ c.Close(true);
+ });
protected override void Update()
{
diff --git a/osu.Game/Overlays/Notifications/ProgressCompletionNotification.cs b/osu.Game/Overlays/Notifications/ProgressCompletionNotification.cs
index 46972d4b5e..93286d9d36 100644
--- a/osu.Game/Overlays/Notifications/ProgressCompletionNotification.cs
+++ b/osu.Game/Overlays/Notifications/ProgressCompletionNotification.cs
@@ -10,6 +10,8 @@ namespace osu.Game.Overlays.Notifications
{
public partial class ProgressCompletionNotification : SimpleNotification
{
+ public override string PopInSampleName => "UI/notification-done";
+
public ProgressCompletionNotification()
{
Icon = FontAwesome.Solid.Check;
diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs
index e6662e2179..6ea032213e 100644
--- a/osu.Game/Overlays/Notifications/ProgressNotification.cs
+++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs
@@ -4,6 +4,8 @@
using System;
using System.Threading;
using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Framework.Audio.Sample;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
@@ -25,8 +27,15 @@ namespace osu.Game.Overlays.Notifications
public Func? CancelRequested { get; set; }
+ ///
+ /// Whether the operation represented by the is still ongoing.
+ ///
+ public bool Ongoing => State != ProgressNotificationState.Completed && State != ProgressNotificationState.Cancelled;
+
protected override bool AllowFlingDismiss => false;
+ public override string PopOutSampleName => State is ProgressNotificationState.Cancelled ? base.PopOutSampleName : "";
+
///
/// The function to post completion notifications back to.
///
@@ -122,6 +131,7 @@ namespace osu.Game.Overlays.Notifications
cancellationTokenSource.Cancel();
IconContent.FadeColour(ColourInfo.GradientVertical(Color4.Gray, Color4.Gray.Lighten(0.5f)), colour_fade_duration);
+ cancelSample?.Play();
loadingSpinner.Hide();
var icon = new SpriteIcon
@@ -190,6 +200,8 @@ namespace osu.Game.Overlays.Notifications
private LoadingSpinner loadingSpinner = null!;
+ private Sample? cancelSample;
+
private readonly TextFlowContainer textDrawable;
public ProgressNotification()
@@ -217,7 +229,7 @@ namespace osu.Game.Overlays.Notifications
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ private void load(OsuColour colours, AudioManager audioManager)
{
colourQueued = colours.YellowDark;
colourActive = colours.Blue;
@@ -236,6 +248,8 @@ namespace osu.Game.Overlays.Notifications
Size = new Vector2(loading_spinner_size),
}
});
+
+ cancelSample = audioManager.Samples.Get(@"UI/notification-cancel");
}
public override void Close(bool runFlingAnimation)
diff --git a/osu.Game/Overlays/Notifications/SimpleErrorNotification.cs b/osu.Game/Overlays/Notifications/SimpleErrorNotification.cs
index 758eea93d4..81e3b40ffc 100644
--- a/osu.Game/Overlays/Notifications/SimpleErrorNotification.cs
+++ b/osu.Game/Overlays/Notifications/SimpleErrorNotification.cs
@@ -7,7 +7,7 @@ namespace osu.Game.Overlays.Notifications
{
public partial class SimpleErrorNotification : SimpleNotification
{
- public override string PopInSampleName => "UI/error-notification-pop-in";
+ public override string PopInSampleName => "UI/notification-error";
public SimpleErrorNotification()
{
diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs
index e7c83159cd..3e67b2f103 100644
--- a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs
@@ -25,10 +25,9 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
},
new SettingsCheckbox
{
- ClassicDefault = false,
- LabelText = GameplaySettingsStrings.ShowHealthDisplayWhenCantFail,
- Current = config.GetBindable(OsuSetting.ShowHealthDisplayWhenCantFail),
- Keywords = new[] { "hp", "bar" }
+ LabelText = GameplaySettingsStrings.ShowReplaySettingsOverlay,
+ Current = config.GetBindable(OsuSetting.ReplaySettingsOverlay),
+ Keywords = new[] { "hide" },
},
new SettingsCheckbox
{
@@ -41,6 +40,13 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
LabelText = GameplaySettingsStrings.AlwaysShowGameplayLeaderboard,
Current = config.GetBindable(OsuSetting.GameplayLeaderboard),
},
+ new SettingsCheckbox
+ {
+ ClassicDefault = false,
+ LabelText = GameplaySettingsStrings.ShowHealthDisplayWhenCantFail,
+ Current = config.GetBindable(OsuSetting.ShowHealthDisplayWhenCantFail),
+ Keywords = new[] { "hp", "bar" }
+ },
};
}
}
diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs
index b04e514ec2..725925c8cf 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs
@@ -440,7 +440,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
}
private void updateStoreFromButton(KeyButton button) =>
- realm.WriteAsync(r => r.Find(button.KeyBinding.ID).KeyCombinationString = button.KeyBinding.KeyCombinationString);
+ realm.WriteAsync(r => r.Find(button.KeyBinding.ID)!.KeyCombinationString = button.KeyBinding.KeyCombinationString);
private void updateIsDefaultValue()
{
diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
index 5382eac675..e997e70157 100644
--- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
@@ -92,7 +92,7 @@ namespace osu.Game.Overlays.Settings.Sections
});
}
- private void skinsChanged(IRealmCollection sender, ChangeSet changes, Exception error)
+ private void skinsChanged(IRealmCollection sender, ChangeSet changes)
{
// This can only mean that realm is recycling, else we would see the protected skins.
// Because we are using `Live<>` in this class, we don't need to worry about this scenario too much.
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index e4d8eb2335..bf649a0a15 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -24,6 +24,7 @@ using osu.Game.Rulesets.Objects.Pooling;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
+using osu.Game.Screens.Play;
using osu.Game.Skinning;
using osuTK.Graphics;
@@ -688,7 +689,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
protected bool UpdateResult(bool userTriggered)
{
// It's possible for input to get into a bad state when rewinding gameplay, so results should not be processed
- if (Time.Elapsed < 0)
+ if ((Clock as IGameplayClock)?.IsRewinding == true)
return false;
if (Judged)
diff --git a/osu.Game/Scoring/IScoreInfo.cs b/osu.Game/Scoring/IScoreInfo.cs
index 3644d099d9..d17558f800 100644
--- a/osu.Game/Scoring/IScoreInfo.cs
+++ b/osu.Game/Scoring/IScoreInfo.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Scoring
double? PP { get; }
- IBeatmapInfo Beatmap { get; }
+ IBeatmapInfo? Beatmap { get; }
IRulesetInfo Ruleset { get; }
diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs
index ef033bf5bd..6868c89d26 100644
--- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs
+++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs
@@ -66,7 +66,7 @@ namespace osu.Game.Scoring.Legacy
{
sw.Write((byte)(score.ScoreInfo.Ruleset.OnlineID));
sw.Write(LATEST_VERSION);
- sw.Write(score.ScoreInfo.BeatmapInfo.MD5Hash);
+ sw.Write(score.ScoreInfo.BeatmapInfo!.MD5Hash);
sw.Write(score.ScoreInfo.User.Username);
sw.Write(FormattableString.Invariant($"lazer-{score.ScoreInfo.User.Username}-{score.ScoreInfo.Date}").ComputeMD5Hash());
sw.Write((ushort)(score.ScoreInfo.GetCount300() ?? 0));
diff --git a/osu.Game/Scoring/ScoreImporter.cs b/osu.Game/Scoring/ScoreImporter.cs
index 5ada2a410d..81b9f57bbc 100644
--- a/osu.Game/Scoring/ScoreImporter.cs
+++ b/osu.Game/Scoring/ScoreImporter.cs
@@ -64,12 +64,14 @@ namespace osu.Game.Scoring
protected override void Populate(ScoreInfo model, ArchiveReader? archive, Realm realm, CancellationToken cancellationToken = default)
{
+ Debug.Assert(model.BeatmapInfo != null);
+
// Ensure the beatmap is not detached.
if (!model.BeatmapInfo.IsManaged)
- model.BeatmapInfo = realm.Find(model.BeatmapInfo.ID);
+ model.BeatmapInfo = realm.Find(model.BeatmapInfo.ID)!;
if (!model.Ruleset.IsManaged)
- model.Ruleset = realm.Find(model.Ruleset.ShortName);
+ model.Ruleset = realm.Find(model.Ruleset.ShortName)!;
// These properties are known to be non-null, but these final checks ensure a null hasn't come from somewhere (or the refetch has failed).
// Under no circumstance do we want these to be written to realm as null.
@@ -101,10 +103,12 @@ namespace osu.Game.Scoring
/// The score to populate the statistics of.
public void PopulateMaximumStatistics(ScoreInfo score)
{
+ Debug.Assert(score.BeatmapInfo != null);
+
if (score.MaximumStatistics.Select(kvp => kvp.Value).Sum() > 0)
return;
- var beatmap = score.BeatmapInfo.Detach();
+ var beatmap = score.BeatmapInfo!.Detach();
var ruleset = score.Ruleset.Detach();
var rulesetInstance = ruleset.CreateInstance();
diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs
index ea9007f4dc..c6f4433824 100644
--- a/osu.Game/Scoring/ScoreInfo.cs
+++ b/osu.Game/Scoring/ScoreInfo.cs
@@ -35,9 +35,16 @@ namespace osu.Game.Scoring
/// The this score was made against.
///
///
- /// When setting this, make sure to also set to allow relational consistency when a beatmap is potentially changed.
+ ///
+ /// This property may be if the score was set on a beatmap (or a version of the beatmap) that is not available locally
+ /// e.g. due to online updates, or local modifications to the beatmap.
+ /// The property will only link to a if its matches .
+ ///
+ ///
+ /// Due to the above, whenever setting this, make sure to also set to allow relational consistency when a beatmap is potentially changed.
+ ///
///
- public BeatmapInfo BeatmapInfo { get; set; } = null!;
+ public BeatmapInfo? BeatmapInfo { get; set; }
///
/// The at the point in time when the score was set.
@@ -150,14 +157,12 @@ namespace osu.Game.Scoring
public int RankInt { get; set; }
IRulesetInfo IScoreInfo.Ruleset => Ruleset;
- IBeatmapInfo IScoreInfo.Beatmap => BeatmapInfo;
+ IBeatmapInfo? IScoreInfo.Beatmap => BeatmapInfo;
IUser IScoreInfo.User => User;
IEnumerable IHasNamedFiles.Files => Files;
#region Properties required to make things work with existing usages
- public Guid BeatmapInfoID => BeatmapInfo.ID;
-
public int UserID => RealmUser.OnlineID;
public int RulesetID => Ruleset.OnlineID;
diff --git a/osu.Game/Scoring/ScoreInfoExtensions.cs b/osu.Game/Scoring/ScoreInfoExtensions.cs
index 85598076d6..6e57a9fd0b 100644
--- a/osu.Game/Scoring/ScoreInfoExtensions.cs
+++ b/osu.Game/Scoring/ScoreInfoExtensions.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Scoring
///
/// A user-presentable display title representing this score.
///
- public static string GetDisplayTitle(this IScoreInfo scoreInfo) => $"{scoreInfo.User.Username} playing {scoreInfo.Beatmap.GetDisplayTitle()}";
+ public static string GetDisplayTitle(this IScoreInfo scoreInfo) => $"{scoreInfo.User.Username} playing {scoreInfo.Beatmap?.GetDisplayTitle() ?? "unknown"}";
///
/// Orders an array of s by total score.
diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs
index 55bcb9f79d..31b5bd8365 100644
--- a/osu.Game/Scoring/ScoreManager.cs
+++ b/osu.Game/Scoring/ScoreManager.cs
@@ -141,7 +141,7 @@ namespace osu.Game.Scoring
{
Realm.Run(r =>
{
- var beatmapScores = r.Find(beatmap.ID).Scores.ToList();
+ var beatmapScores = r.Find(beatmap.ID)!.Scores.ToList();
Delete(beatmapScores, silent);
});
}
diff --git a/osu.Game/Scoring/ScorePerformanceCache.cs b/osu.Game/Scoring/ScorePerformanceCache.cs
index bdbcfe4efe..1f2b1aeb95 100644
--- a/osu.Game/Scoring/ScorePerformanceCache.cs
+++ b/osu.Game/Scoring/ScorePerformanceCache.cs
@@ -34,7 +34,7 @@ namespace osu.Game.Scoring
{
var score = lookup.ScoreInfo;
- var attributes = await difficultyCache.GetDifficultyAsync(score.BeatmapInfo, score.Ruleset, score.Mods, token).ConfigureAwait(false);
+ var attributes = await difficultyCache.GetDifficultyAsync(score.BeatmapInfo!, score.Ruleset, score.Mods, token).ConfigureAwait(false);
// Performance calculation requires the beatmap and ruleset to be locally available. If not, return a default value.
if (attributes?.Attributes == null)
diff --git a/osu.Game/Screens/Play/ComboEffects.cs b/osu.Game/Screens/Play/ComboEffects.cs
index 09c94a8f1d..6f12cfde64 100644
--- a/osu.Game/Screens/Play/ComboEffects.cs
+++ b/osu.Game/Screens/Play/ComboEffects.cs
@@ -53,7 +53,7 @@ namespace osu.Game.Screens.Play
if (gameplayClock.CurrentTime < firstBreakTime)
firstBreakTime = null;
- if (gameplayClock.ElapsedFrameTime < 0)
+ if (gameplayClock.IsRewinding)
return;
if (combo.NewValue == 0 && (combo.OldValue > 20 || (alwaysPlayFirst.Value && firstBreakTime == null)))
diff --git a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs
index b6b385e262..dbb0456cd0 100644
--- a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs
+++ b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs
@@ -3,10 +3,8 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input.Events;
using osuTK;
using osu.Game.Screens.Play.PlayerSettings;
-using osuTK.Input;
namespace osu.Game.Screens.Play.HUD
{
@@ -14,16 +12,12 @@ namespace osu.Game.Screens.Play.HUD
{
private const int fade_duration = 200;
- public bool ReplayLoaded;
-
public readonly PlaybackSettings PlaybackSettings;
public readonly VisualSettings VisualSettings;
public PlayerSettingsOverlay()
{
- AlwaysPresent = true;
-
Anchor = Anchor.TopRight;
Origin = Anchor.TopRight;
AutoSizeAxes = Axes.Both;
@@ -37,8 +31,6 @@ namespace osu.Game.Screens.Play.HUD
Spacing = new Vector2(0, 20),
Children = new PlayerSettingsGroup[]
{
- //CollectionSettings = new CollectionSettings(),
- //DiscussionSettings = new DiscussionSettings(),
PlaybackSettings = new PlaybackSettings { Expanded = { Value = false } },
VisualSettings = new VisualSettings { Expanded = { Value = false } },
new AudioSettings { Expanded = { Value = false } }
@@ -48,24 +40,5 @@ namespace osu.Game.Screens.Play.HUD
protected override void PopIn() => this.FadeIn(fade_duration);
protected override void PopOut() => this.FadeOut(fade_duration);
-
- // We want to handle keyboard inputs all the time in order to trigger ToggleVisibility() when not visible
- public override bool PropagateNonPositionalInputSubTree => true;
-
- protected override bool OnKeyDown(KeyDownEvent e)
- {
- if (e.Repeat) return false;
-
- if (e.ControlPressed)
- {
- if (e.Key == Key.H && ReplayLoaded)
- {
- ToggleVisibility();
- return true;
- }
- }
-
- return base.OnKeyDown(e);
- }
}
}
diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs
index f0a2975958..d11171e3fe 100644
--- a/osu.Game/Screens/Play/HUDOverlay.cs
+++ b/osu.Game/Screens/Play/HUDOverlay.cs
@@ -78,6 +78,7 @@ namespace osu.Game.Screens.Play
public Bindable ShowHud { get; } = new BindableBool();
private Bindable configVisibilityMode;
+ private Bindable configSettingsOverlay;
private readonly BindableBool replayLoaded = new BindableBool();
@@ -178,6 +179,7 @@ namespace osu.Game.Screens.Play
ModDisplay.Current.Value = mods;
configVisibilityMode = config.GetBindable(OsuSetting.HUDVisibilityMode);
+ configSettingsOverlay = config.GetBindable(OsuSetting.ReplaySettingsOverlay);
if (configVisibilityMode.Value == HUDVisibilityMode.Never && !hasShownNotificationOnce)
{
@@ -204,9 +206,24 @@ namespace osu.Game.Screens.Play
holdingForHUD.BindValueChanged(_ => updateVisibility());
IsPlaying.BindValueChanged(_ => updateVisibility());
- configVisibilityMode.BindValueChanged(_ => updateVisibility(), true);
+ configVisibilityMode.BindValueChanged(_ => updateVisibility());
+ configSettingsOverlay.BindValueChanged(_ => updateVisibility());
- replayLoaded.BindValueChanged(replayLoadedValueChanged, true);
+ replayLoaded.BindValueChanged(e =>
+ {
+ if (e.NewValue)
+ {
+ ModDisplay.FadeIn(200);
+ InputCountController.Margin = new MarginPadding(10) { Bottom = 30 };
+ }
+ else
+ {
+ ModDisplay.Delay(2000).FadeOut(200);
+ InputCountController.Margin = new MarginPadding(10);
+ }
+
+ updateVisibility();
+ }, true);
}
protected override void Update()
@@ -280,6 +297,11 @@ namespace osu.Game.Screens.Play
return;
}
+ if (configSettingsOverlay.Value && replayLoaded.Value)
+ PlayerSettingsOverlay.Show();
+ else
+ PlayerSettingsOverlay.Hide();
+
switch (configVisibilityMode.Value)
{
case HUDVisibilityMode.Never:
@@ -297,26 +319,6 @@ namespace osu.Game.Screens.Play
}
}
- private void replayLoadedValueChanged(ValueChangedEvent e)
- {
- PlayerSettingsOverlay.ReplayLoaded = e.NewValue;
-
- if (e.NewValue)
- {
- PlayerSettingsOverlay.Show();
- ModDisplay.FadeIn(200);
- InputCountController.Margin = new MarginPadding(10) { Bottom = 30 };
- }
- else
- {
- PlayerSettingsOverlay.Hide();
- ModDisplay.Delay(2000).FadeOut(200);
- InputCountController.Margin = new MarginPadding(10);
- }
-
- updateVisibility();
- }
-
protected virtual void BindDrawableRuleset(DrawableRuleset drawableRuleset)
{
if (drawableRuleset is ICanAttachHUDPieces attachTarget)
@@ -354,6 +356,10 @@ namespace osu.Game.Screens.Play
switch (e.Action)
{
+ case GlobalAction.ToggleReplaySettings:
+ configSettingsOverlay.Value = !configSettingsOverlay.Value;
+ return true;
+
case GlobalAction.HoldForHUD:
holdingForHUD.Value = true;
return true;
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index b979fc2740..379c10a4a4 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -11,8 +11,6 @@ using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using osu.Framework.Allocation;
-using osu.Framework.Audio;
-using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
@@ -114,8 +112,6 @@ namespace osu.Game.Screens.Play
private Ruleset ruleset;
- private Sample sampleRestart;
-
public BreakOverlay BreakOverlay;
///
@@ -195,7 +191,7 @@ namespace osu.Game.Screens.Play
}
[BackgroundDependencyLoader(true)]
- private void load(AudioManager audio, OsuConfigManager config, OsuGameBase game, CancellationToken cancellationToken)
+ private void load(OsuConfigManager config, OsuGameBase game, CancellationToken cancellationToken)
{
var gameplayMods = Mods.Value.Select(m => m.DeepClone()).ToArray();
@@ -213,8 +209,6 @@ namespace osu.Game.Screens.Play
if (playableBeatmap == null)
return;
- sampleRestart = audio.Samples.Get(@"Gameplay/restart");
-
mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel);
if (game != null)
@@ -295,14 +289,17 @@ namespace osu.Game.Screens.Play
if (Configuration.AllowRestart)
{
- rulesetSkinProvider.Add(new HotkeyRetryOverlay
+ rulesetSkinProvider.AddRange(new Drawable[]
{
- Action = () =>
+ new HotkeyRetryOverlay
{
- if (!this.IsCurrentScreen()) return;
+ Action = () =>
+ {
+ if (!this.IsCurrentScreen()) return;
- fadeOut(true);
- Restart(true);
+ fadeOut(true);
+ Restart(true);
+ },
},
});
}
@@ -673,7 +670,6 @@ namespace osu.Game.Screens.Play
// stopping here is to ensure music doesn't become audible after exiting back to PlayerLoader.
musicController.Stop();
- sampleRestart?.Play();
RestartRequested?.Invoke(quickRestart);
PerformExit(false);
diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs
index 30ae5ee5aa..4b15bac0f3 100644
--- a/osu.Game/Screens/Play/PlayerLoader.cs
+++ b/osu.Game/Screens/Play/PlayerLoader.cs
@@ -15,6 +15,7 @@ using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using osu.Framework.Screens;
using osu.Framework.Threading;
+using osu.Game.Audio;
using osu.Game.Audio.Effects;
using osu.Game.Configuration;
using osu.Game.Graphics;
@@ -25,6 +26,7 @@ using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play.PlayerSettings;
+using osu.Game.Skinning;
using osu.Game.Users;
using osu.Game.Utils;
using osuTK;
@@ -76,6 +78,8 @@ namespace osu.Game.Screens.Play
private AudioFilter lowPassFilter = null!;
private AudioFilter highPassFilter = null!;
+ private SkinnableSound sampleRestart = null!;
+
[Cached]
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
@@ -199,7 +203,8 @@ namespace osu.Game.Screens.Play
},
idleTracker = new IdleTracker(750),
lowPassFilter = new AudioFilter(audio.TrackMixer),
- highPassFilter = new AudioFilter(audio.TrackMixer, BQFType.HighPass)
+ highPassFilter = new AudioFilter(audio.TrackMixer, BQFType.HighPass),
+ sampleRestart = new SkinnableSound(new SampleInfo(@"Gameplay/restart", @"pause-retry-click"))
};
if (Beatmap.Value.BeatmapInfo.EpilepsyWarning)
@@ -265,6 +270,8 @@ namespace osu.Game.Screens.Play
playerConsumed = false;
cancelLoad();
+ sampleRestart.Play();
+
contentIn();
}
diff --git a/osu.Game/Screens/Play/SoloPlayer.cs b/osu.Game/Screens/Play/SoloPlayer.cs
index dafdf00136..f7ae3eb62b 100644
--- a/osu.Game/Screens/Play/SoloPlayer.cs
+++ b/osu.Game/Screens/Play/SoloPlayer.cs
@@ -68,7 +68,7 @@ namespace osu.Game.Screens.Play
{
IBeatmapInfo beatmap = score.ScoreInfo.BeatmapInfo;
- Debug.Assert(beatmap.OnlineID > 0);
+ Debug.Assert(beatmap!.OnlineID > 0);
return new SubmitSoloScoreRequest(score.ScoreInfo, token, beatmap.OnlineID);
}
diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs
index 82c429798e..d1dc1a81db 100644
--- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs
+++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs
@@ -66,7 +66,7 @@ namespace osu.Game.Screens.Ranking.Expanded
[BackgroundDependencyLoader]
private void load(BeatmapDifficultyCache beatmapDifficultyCache)
{
- var beatmap = score.BeatmapInfo;
+ var beatmap = score.BeatmapInfo!;
var metadata = beatmap.BeatmapSet?.Metadata ?? beatmap.Metadata;
string creator = metadata.Author.Username;
diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs
index b9f3b65129..96fed3e6ba 100644
--- a/osu.Game/Screens/Ranking/ResultsScreen.cs
+++ b/osu.Game/Screens/Ranking/ResultsScreen.cs
@@ -305,7 +305,7 @@ namespace osu.Game.Screens.Ranking
float origLocation = detachedPanelContainer.ToLocalSpace(screenSpacePos).X;
expandedPanel.MoveToX(origLocation)
.Then()
- .MoveToX(StatisticsPanel.SIDE_PADDING, 150, Easing.OutQuint);
+ .MoveToX(StatisticsPanel.SIDE_PADDING, 400, Easing.OutElasticQuarter);
// Hide contracted panels.
foreach (var contracted in ScorePanelList.GetScorePanels().Where(p => p.State == PanelState.Contracted))
@@ -313,7 +313,7 @@ namespace osu.Game.Screens.Ranking
ScorePanelList.HandleInput = false;
// Dim background.
- ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.1f), 150));
+ ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.4f), 400, Easing.OutQuint));
detachedPanel = expandedPanel;
}
@@ -329,7 +329,7 @@ namespace osu.Game.Screens.Ranking
float origLocation = detachedPanel.Parent.ToLocalSpace(screenSpacePos).X;
detachedPanel.MoveToX(origLocation)
.Then()
- .MoveToX(0, 150, Easing.OutQuint);
+ .MoveToX(0, 250, Easing.OutElasticQuarter);
// Show contracted panels.
foreach (var contracted in ScorePanelList.GetScorePanels().Where(p => p.State == PanelState.Contracted))
@@ -337,7 +337,7 @@ namespace osu.Game.Screens.Ranking
ScorePanelList.HandleInput = true;
// Un-dim background.
- ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.5f), 150));
+ ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.5f), 250, Easing.OutQuint));
detachedPanel = null;
}
diff --git a/osu.Game/Screens/Ranking/SoloResultsScreen.cs b/osu.Game/Screens/Ranking/SoloResultsScreen.cs
index c8920a734d..f187b8a302 100644
--- a/osu.Game/Screens/Ranking/SoloResultsScreen.cs
+++ b/osu.Game/Screens/Ranking/SoloResultsScreen.cs
@@ -63,7 +63,7 @@ namespace osu.Game.Screens.Ranking
protected override APIRequest? FetchScores(Action>? scoresCallback)
{
- if (Score.BeatmapInfo.OnlineID <= 0 || Score.BeatmapInfo.Status <= BeatmapOnlineStatus.Pending)
+ if (Score.BeatmapInfo!.OnlineID <= 0 || Score.BeatmapInfo.Status <= BeatmapOnlineStatus.Pending)
return null;
getScoreRequest = new GetScoresRequest(Score.BeatmapInfo, Score.Ruleset);
diff --git a/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs b/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs
index c36d7726dc..8b059efaf4 100644
--- a/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs
+++ b/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs
@@ -223,7 +223,7 @@ namespace osu.Game.Screens.Ranking.Statistics
protected override void PopIn()
{
- this.FadeIn(150, Easing.OutQuint);
+ this.FadeIn(350, Easing.OutQuint);
popInSample?.Play();
wasOpened = true;
@@ -231,7 +231,7 @@ namespace osu.Game.Screens.Ranking.Statistics
protected override void PopOut()
{
- this.FadeOut(150, Easing.OutQuint);
+ this.FadeOut(250, Easing.OutQuint);
if (wasOpened)
popOutSample?.Play();
diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs
index 3d87a57295..9af9a0ce72 100644
--- a/osu.Game/Screens/Select/BeatmapCarousel.cs
+++ b/osu.Game/Screens/Select/BeatmapCarousel.cs
@@ -223,7 +223,7 @@ namespace osu.Game.Screens.Select
subscriptionHiddenBeatmaps = realm.RegisterForNotifications(r => r.All().Where(b => b.Hidden), beatmapsChanged);
}
- private void deletedBeatmapSetsChanged(IRealmCollection sender, ChangeSet? changes, Exception? error)
+ private void deletedBeatmapSetsChanged(IRealmCollection sender, ChangeSet? changes)
{
// If loading test beatmaps, avoid overwriting with realm subscription callbacks.
if (loadedTestBeatmaps)
@@ -236,7 +236,7 @@ namespace osu.Game.Screens.Select
removeBeatmapSet(sender[i].ID);
}
- private void beatmapSetsChanged(IRealmCollection sender, ChangeSet? changes, Exception? error)
+ private void beatmapSetsChanged(IRealmCollection sender, ChangeSet? changes)
{
// If loading test beatmaps, avoid overwriting with realm subscription callbacks.
if (loadedTestBeatmaps)
@@ -255,7 +255,7 @@ namespace osu.Game.Screens.Select
foreach (var id in realmSets)
{
if (!root.BeatmapSetsByID.ContainsKey(id))
- UpdateBeatmapSet(realm.Realm.Find(id).Detach());
+ UpdateBeatmapSet(realm.Realm.Find(id)!.Detach());
}
foreach (var id in root.BeatmapSetsByID.Keys)
@@ -315,7 +315,7 @@ namespace osu.Game.Screens.Select
}
}
- private void beatmapsChanged(IRealmCollection sender, ChangeSet? changes, Exception? error)
+ private void beatmapsChanged(IRealmCollection sender, ChangeSet? changes)
{
// we only care about actual changes in hidden status.
if (changes == null)
diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
index 5dd2486e1c..3605e3d706 100644
--- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs
+++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
@@ -76,14 +76,12 @@ namespace osu.Game.Screens.Select
protected override void PopIn()
{
this.MoveToX(0, animation_duration, Easing.OutQuint);
- this.RotateTo(0, animation_duration, Easing.OutQuint);
this.FadeIn(transition_duration);
}
protected override void PopOut()
{
this.MoveToX(-100, animation_duration, Easing.In);
- this.RotateTo(10, animation_duration, Easing.In);
this.FadeOut(transition_duration * 2, Easing.In);
}
diff --git a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs
index 7c632b63db..c17de77619 100644
--- a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs
+++ b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs
@@ -68,7 +68,7 @@ namespace osu.Game.Screens.Select.Carousel
localScoresChanged);
}, true);
- void localScoresChanged(IRealmCollection sender, ChangeSet? changes, Exception _)
+ void localScoresChanged(IRealmCollection sender, ChangeSet? changes)
{
// This subscription may fire from changes to linked beatmaps, which we don't care about.
// It's currently not possible for a score to be modified after insertion, so we can safely ignore callbacks with only modifications.
diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
index 4c41ed3622..58c14b15b9 100644
--- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
+++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
@@ -193,7 +193,7 @@ namespace osu.Game.Screens.Select.Leaderboards
+ $" AND {nameof(ScoreInfo.DeletePending)} == false"
, beatmapInfo.ID, ruleset.Value.ShortName), localScoresChanged);
- void localScoresChanged(IRealmCollection sender, ChangeSet? changes, Exception exception)
+ void localScoresChanged(IRealmCollection sender, ChangeSet? changes)
{
if (cancellationToken.IsCancellationRequested)
return;
diff --git a/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs b/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs
index c4add31a4f..cd98872b65 100644
--- a/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs
+++ b/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs
@@ -4,9 +4,7 @@
using osu.Framework.Allocation;
using osu.Game.Overlays.Dialog;
using osu.Game.Scoring;
-using System.Diagnostics;
using osu.Framework.Graphics.Sprites;
-using osu.Game.Beatmaps;
namespace osu.Game.Screens.Select
{
@@ -20,11 +18,8 @@ namespace osu.Game.Screens.Select
}
[BackgroundDependencyLoader]
- private void load(BeatmapManager beatmapManager, ScoreManager scoreManager)
+ private void load(ScoreManager scoreManager)
{
- BeatmapInfo? beatmapInfo = beatmapManager.QueryBeatmap(b => b.ID == score.BeatmapInfoID);
- Debug.Assert(beatmapInfo != null);
-
BodyText = $"{score.User} ({score.DisplayAccuracy}, {score.Rank})";
Icon = FontAwesome.Regular.TrashAlt;
diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs
index 2b56767bd0..48b5c210b8 100644
--- a/osu.Game/Screens/Spectate/SpectatorScreen.cs
+++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs
@@ -90,7 +90,7 @@ namespace osu.Game.Screens.Spectate
}));
}
- private void beatmapsChanged(IRealmCollection items, ChangeSet changes, Exception ___)
+ private void beatmapsChanged(IRealmCollection items, ChangeSet changes)
{
if (changes?.InsertedIndices == null) return;
diff --git a/osu.Game/Skinning/RealmBackedResourceStore.cs b/osu.Game/Skinning/RealmBackedResourceStore.cs
index cc887a7a61..cce099a268 100644
--- a/osu.Game/Skinning/RealmBackedResourceStore.cs
+++ b/osu.Game/Skinning/RealmBackedResourceStore.cs
@@ -38,7 +38,7 @@ namespace osu.Game.Skinning
realmSubscription?.Dispose();
}
- private void skinChanged(IRealmCollection sender, ChangeSet changes, Exception error) => invalidateCache();
+ private void skinChanged(IRealmCollection sender, ChangeSet? changes) => invalidateCache();
protected override IEnumerable GetFilenames(string name)
{
diff --git a/osu.Game/Skinning/SkinImporter.cs b/osu.Game/Skinning/SkinImporter.cs
index 43760c4a19..f2103a45c4 100644
--- a/osu.Game/Skinning/SkinImporter.cs
+++ b/osu.Game/Skinning/SkinImporter.cs
@@ -198,7 +198,7 @@ namespace osu.Game.Skinning
using (var streamContent = new MemoryStream(Encoding.UTF8.GetBytes(skinInfoJson)))
{
- modelManager.AddFile(s, streamContent, skin_info_file, s.Realm);
+ modelManager.AddFile(s, streamContent, skin_info_file, s.Realm!);
}
// Then serialise each of the drawable component groups into respective files.
@@ -213,9 +213,9 @@ namespace osu.Game.Skinning
var oldFile = s.GetFile(filename);
if (oldFile != null)
- modelManager.ReplaceFile(oldFile, streamContent, s.Realm);
+ modelManager.ReplaceFile(oldFile, streamContent, s.Realm!);
else
- modelManager.AddFile(s, streamContent, filename, s.Realm);
+ modelManager.AddFile(s, streamContent, filename, s.Realm!);
}
}
diff --git a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs
index 16496ff320..37260b3b13 100644
--- a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs
+++ b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs
@@ -8,6 +8,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Testing;
using osu.Framework.Testing.Input;
+using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterfaceV2;
@@ -25,8 +26,7 @@ namespace osu.Game.Tests.Visual
protected readonly ManualInputManager InputManager;
- private readonly RoundedButton buttonTest;
- private readonly RoundedButton buttonLocal;
+ private readonly Container takeControlOverlay;
///
/// Whether to create a nested container to handle s that result from local (manual) test input.
@@ -66,12 +66,12 @@ namespace osu.Game.Tests.Visual
UseParentInput = true,
Child = mainContent
},
- new Container
+ takeControlOverlay = new Container
{
AutoSizeAxes = Axes.Both,
- Anchor = Anchor.TopRight,
- Origin = Anchor.TopRight,
- Margin = new MarginPadding(5),
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ Margin = new MarginPadding(40),
CornerRadius = 5,
Masking = true,
Children = new Drawable[]
@@ -80,44 +80,38 @@ namespace osu.Game.Tests.Visual
{
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
- Alpha = 0.5f,
+ Alpha = 0.4f,
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
- Margin = new MarginPadding(5),
- Spacing = new Vector2(5),
+ Margin = new MarginPadding(10),
+ Spacing = new Vector2(10),
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
- Text = "Input Priority"
+ Font = OsuFont.Default.With(weight: FontWeight.Bold),
+ Text = "The test is currently overriding local input",
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
- Margin = new MarginPadding(5),
Spacing = new Vector2(5),
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
- buttonLocal = new RoundedButton
+ new RoundedButton
{
- Text = "local",
- Size = new Vector2(50, 30),
- Action = returnUserInput
- },
- buttonTest = new RoundedButton
- {
- Text = "test",
- Size = new Vector2(50, 30),
- Action = returnTestInput
+ Text = "Take control back",
+ Size = new Vector2(180, 30),
+ Action = () => InputManager.UseParentInput = true
},
}
},
@@ -128,6 +122,13 @@ namespace osu.Game.Tests.Visual
});
}
+ protected override void Update()
+ {
+ base.Update();
+
+ takeControlOverlay.Alpha = InputManager.UseParentInput ? 0 : 1;
+ }
+
///
/// Wait for a button to become enabled, then click it.
///
@@ -146,19 +147,5 @@ namespace osu.Game.Tests.Visual
InputManager.Click(MouseButton.Left);
});
}
-
- protected override void Update()
- {
- base.Update();
-
- buttonTest.Enabled.Value = InputManager.UseParentInput;
- buttonLocal.Enabled.Value = !InputManager.UseParentInput;
- }
-
- private void returnUserInput() =>
- InputManager.UseParentInput = true;
-
- private void returnTestInput() =>
- InputManager.UseParentInput = false;
}
}
diff --git a/osu.Game/Users/UserActivity.cs b/osu.Game/Users/UserActivity.cs
index 1761282e2e..c82f642fdc 100644
--- a/osu.Game/Users/UserActivity.cs
+++ b/osu.Game/Users/UserActivity.cs
@@ -111,7 +111,7 @@ namespace osu.Game.Users
protected string Username => score.User.Username;
- public BeatmapInfo BeatmapInfo => score.BeatmapInfo;
+ public BeatmapInfo? BeatmapInfo => score.BeatmapInfo;
public WatchingReplay(ScoreInfo score)
{
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 83d4bcb336..184a77a286 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -35,9 +35,9 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
+
+
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 86694e268a..51bcc36621 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -16,6 +16,6 @@
iossimulator-x64
-
+
diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings
index b54794cd6d..482095db57 100644
--- a/osu.sln.DotSettings
+++ b/osu.sln.DotSettings
@@ -5,6 +5,7 @@
True
ExplicitlyExcluded
ExplicitlyExcluded
+ g_*.cs
SOLUTION
WARNING
WARNING