diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index b55cc41454..fb9a7f7965 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -215,16 +215,20 @@ namespace osu.Game
private ExternalLinkOpener externalLinkOpener;
- public void HandleLink(string url) => HandleLink(MessageFormatter.GetLinkDetails(url));
+ ///
+ /// Handle an arbitrary URL. Displays via in-game overlays where possible.
+ /// This can be called from a non-thread-safe non-game-loaded state.
+ ///
+ /// The URL to load.
+ public void HandleLink(string url) => Schedule(() => HandleLink(MessageFormatter.GetLinkDetails(url)));
+ ///
+ /// Handle a specific .
+ /// This can be called from a non-thread-safe non-game-loaded state.
+ ///
+ /// The link to load.
public void HandleLink(LinkDetails link)
{
- Action showNotImplementedError = () => notifications?.Post(new SimpleNotification
- {
- Text = @"This link type is not yet supported!",
- Icon = FontAwesome.Solid.LifeRing,
- });
-
switch (link.Action)
{
case LinkAction.OpenBeatmap:
@@ -239,21 +243,17 @@ namespace osu.Game
break;
case LinkAction.OpenChannel:
- try
- {
- channelManager.OpenChannel(link.Argument);
- }
- catch (ChannelNotFoundException)
- {
- Logger.Log($"The requested channel \"{link.Argument}\" does not exist");
- }
-
+ ShowChannel(link.Argument);
break;
case LinkAction.OpenEditorTimestamp:
case LinkAction.JoinMultiplayerMatch:
case LinkAction.Spectate:
- showNotImplementedError.Invoke();
+ waitForReady(() => notifications, _ => notifications?.Post(new SimpleNotification
+ {
+ Text = @"This link type is not yet supported!",
+ Icon = FontAwesome.Solid.LifeRing,
+ }));
break;
case LinkAction.External:
@@ -270,31 +270,47 @@ namespace osu.Game
}
}
- public void OpenUrlExternally(string url)
+ public void OpenUrlExternally(string url) => waitForReady(() => externalLinkOpener, _ =>
{
if (url.StartsWith("/"))
url = $"{API.Endpoint}{url}";
externalLinkOpener.OpenUrlExternally(url);
- }
+ });
+
+ ///
+ /// Open a specific channel in chat.
+ ///
+ /// The channel to display.
+ public void ShowChannel(string channel) => waitForReady(() => channelManager, _ =>
+ {
+ try
+ {
+ channelManager.OpenChannel(channel);
+ }
+ catch (ChannelNotFoundException)
+ {
+ Logger.Log($"The requested channel \"{channel}\" does not exist");
+ }
+ });
///
/// Show a beatmap set as an overlay.
///
/// The set to display.
- public void ShowBeatmapSet(int setId) => beatmapSetOverlay.FetchAndShowBeatmapSet(setId);
+ public void ShowBeatmapSet(int setId) => waitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmapSet(setId));
///
/// Show a user's profile as an overlay.
///
/// The user to display.
- public void ShowUser(long userId) => userProfile.ShowUser(userId);
+ public void ShowUser(long userId) => waitForReady(() => userProfile, _ => userProfile.ShowUser(userId));
///
/// Show a beatmap's set as an overlay, displaying the given beatmap.
///
/// The beatmap to show.
- public void ShowBeatmap(int beatmapId) => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId);
+ public void ShowBeatmap(int beatmapId) => waitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId));
///
/// Present a beatmap at song select immediately.
@@ -452,6 +468,23 @@ namespace osu.Game
performFromMainMenuTask = Schedule(() => performFromMainMenu(action, taskName));
}
+ ///
+ /// Wait for the game (and target component) to become loaded and then run an action.
+ ///
+ /// A function to retrieve a (potentially not-yet-constructed) target instance.
+ /// The action to perform on the instance when load is confirmed.
+ /// The type of the target instance.
+ private void waitForReady(Func retrieveInstance, Action action)
+ where T : Drawable
+ {
+ var instance = retrieveInstance();
+
+ if (ScreenStack == null || ScreenStack.CurrentScreen is StartupScreen || instance?.IsLoaded != true)
+ Schedule(() => waitForReady(retrieveInstance, action));
+ else
+ action(instance);
+ }
+
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);