diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs
index 6a31fb3fda..10460f52fd 100644
--- a/osu.Game.Rulesets.Mania/UI/Column.cs
+++ b/osu.Game.Rulesets.Mania/UI/Column.cs
@@ -134,6 +134,9 @@ namespace osu.Game.Rulesets.Mania.UI
protected override void Dispose(bool isDisposing)
{
+ // must happen before children are disposed in base call to prevent illegal accesses to the hit explosion pool.
+ NewResult -= OnNewResult;
+
base.Dispose(isDisposing);
if (skin != null)
diff --git a/osu.Game.Rulesets.Mania/UI/Stage.cs b/osu.Game.Rulesets.Mania/UI/Stage.cs
index fc38a96a35..c1d3e85bf1 100644
--- a/osu.Game.Rulesets.Mania/UI/Stage.cs
+++ b/osu.Game.Rulesets.Mania/UI/Stage.cs
@@ -156,6 +156,9 @@ namespace osu.Game.Rulesets.Mania.UI
protected override void Dispose(bool isDisposing)
{
+ // must happen before children are disposed in base call to prevent illegal accesses to the judgement pool.
+ NewResult -= OnNewResult;
+
base.Dispose(isDisposing);
if (currentSkin != null)
diff --git a/osu.Game/Online/Spectator/ISpectatorClient.cs b/osu.Game/Online/Spectator/ISpectatorClient.cs
index ccba280001..605ebc4ef0 100644
--- a/osu.Game/Online/Spectator/ISpectatorClient.cs
+++ b/osu.Game/Online/Spectator/ISpectatorClient.cs
@@ -32,5 +32,12 @@ namespace osu.Game.Online.Spectator
/// The user.
/// The frame data.
Task UserSentFrames(int userId, FrameDataBundle data);
+
+ ///
+ /// Signals that a user's submitted score was fully processed.
+ ///
+ /// The ID of the user who achieved the score.
+ /// The ID of the score.
+ Task UserScoreProcessed(int userId, long scoreId);
}
}
diff --git a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs
index 01b775549e..3118e05053 100644
--- a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs
+++ b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs
@@ -41,6 +41,7 @@ namespace osu.Game.Online.Spectator
connection.On(nameof(ISpectatorClient.UserBeganPlaying), ((ISpectatorClient)this).UserBeganPlaying);
connection.On(nameof(ISpectatorClient.UserSentFrames), ((ISpectatorClient)this).UserSentFrames);
connection.On(nameof(ISpectatorClient.UserFinishedPlaying), ((ISpectatorClient)this).UserFinishedPlaying);
+ connection.On(nameof(ISpectatorClient.UserScoreProcessed), ((ISpectatorClient)this).UserScoreProcessed);
};
IsConnected.BindTo(connector.IsConnected);
diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs
index fce61c019b..b60cef2835 100644
--- a/osu.Game/Online/Spectator/SpectatorClient.cs
+++ b/osu.Game/Online/Spectator/SpectatorClient.cs
@@ -64,6 +64,11 @@ namespace osu.Game.Online.Spectator
///
public virtual event Action? OnUserFinishedPlaying;
+ ///
+ /// Called whenever a user-submitted score has been fully processed.
+ ///
+ public virtual event Action? OnUserScoreProcessed;
+
///
/// A dictionary containing all users currently being watched, with the number of watching components for each user.
///
@@ -160,6 +165,13 @@ namespace osu.Game.Online.Spectator
return Task.CompletedTask;
}
+ Task ISpectatorClient.UserScoreProcessed(int userId, long scoreId)
+ {
+ Schedule(() => OnUserScoreProcessed?.Invoke(userId, scoreId));
+
+ return Task.CompletedTask;
+ }
+
public void BeginPlaying(long? scoreToken, GameplayState state, Score score)
{
// This schedule is only here to match the one below in `EndPlaying`.
diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs
index f6133e3643..4af40e5ad6 100644
--- a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs
+++ b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs
@@ -87,18 +87,21 @@ namespace osu.Game.Overlays.FirstRunSetup
});
frameworkLocale = frameworkConfig.GetBindable(FrameworkSetting.Locale);
+ frameworkLocale.BindValueChanged(_ => onLanguageChange());
localisationParameters = localisation.CurrentParameters.GetBoundCopy();
- localisationParameters.BindValueChanged(p =>
- {
- var language = LanguageExtensions.GetLanguageFor(frameworkLocale.Value, p.NewValue);
+ localisationParameters.BindValueChanged(_ => onLanguageChange(), true);
+ }
- // Changing language may cause a short period of blocking the UI thread while the new glyphs are loaded.
- // Scheduling ensures the button animation plays smoothly after any blocking operation completes.
- // Note that a delay is required (the alternative would be a double-schedule; delay feels better).
- updateSelectedDelegate?.Cancel();
- updateSelectedDelegate = Scheduler.AddDelayed(() => updateSelectedStates(language), 50);
- }, true);
+ private void onLanguageChange()
+ {
+ var language = LanguageExtensions.GetLanguageFor(frameworkLocale.Value, localisationParameters.Value);
+
+ // Changing language may cause a short period of blocking the UI thread while the new glyphs are loaded.
+ // Scheduling ensures the button animation plays smoothly after any blocking operation completes.
+ // Note that a delay is required (the alternative would be a double-schedule; delay feels better).
+ updateSelectedDelegate?.Cancel();
+ updateSelectedDelegate = Scheduler.AddDelayed(() => updateSelectedStates(language), 50);
}
private void updateSelectedStates(Language language)
diff --git a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs
index a4ec919658..982cbec376 100644
--- a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs
@@ -44,10 +44,13 @@ namespace osu.Game.Overlays.Settings.Sections.General
},
};
- localisationParameters.BindValueChanged(p
- => languageSelection.Current.Value = LanguageExtensions.GetLanguageFor(frameworkLocale.Value, p.NewValue), true);
+ frameworkLocale.BindValueChanged(_ => updateSelection());
+ localisationParameters.BindValueChanged(_ => updateSelection(), true);
languageSelection.Current.BindValueChanged(val => frameworkLocale.Value = val.NewValue.ToCultureCode());
}
+
+ private void updateSelection() =>
+ languageSelection.Current.Value = LanguageExtensions.GetLanguageFor(frameworkLocale.Value, localisationParameters.Value);
}
}
diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs
index 1eec71f33a..5fa6508a31 100644
--- a/osu.Game/Screens/Play/SubmittingPlayer.cs
+++ b/osu.Game/Screens/Play/SubmittingPlayer.cs
@@ -13,6 +13,7 @@ using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Online.API;
+using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
using osu.Game.Online.Spectator;
using osu.Game.Rulesets.Scoring;
@@ -158,8 +159,11 @@ namespace osu.Game.Screens.Play
if (LoadedBeatmapSuccessfully)
{
- submitScore(Score.DeepClone());
- spectatorClient.EndPlaying(GameplayState);
+ Task.Run(async () =>
+ {
+ await submitScore(Score.DeepClone()).ConfigureAwait(false);
+ spectatorClient.EndPlaying(GameplayState);
+ }).FireAndForget();
}
return exiting;