diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs
index 2dad62e07a..50606aca41 100644
--- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs
+++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs
@@ -27,6 +27,7 @@
using osu.Game.Models;
using osu.Game.Online.Broadcasts;
using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Metadata;
using osu.Game.Online.Multiplayer;
@@ -117,6 +118,11 @@ public partial class StandAloneChatDisplay : CompositeDrawable
[Resolved(CanBeNull = true)] // not sure if it actually can be null
private ChatTimerHandler? chatTimerHandler { get; set; }
+ [Resolved]
+ private MultiplayerRefereeTracker multiplayerRefereeTracker { get; set; } = null!;
+
+ private GetUserRequest? userReq;
+
///
/// Construct a new instance.
///
@@ -364,309 +370,346 @@ public void EnqueueUserMessage(string message)
messageQueue.Enqueue(new Tuple(message, Channel.Value));
}
- private void postMessage(TextBox sender, bool newText)
+ private void processChatCommands(string[] parts)
{
- Debug.Assert(TextBox != null);
-
- string text = TextBox.Text.Trim();
-
- if (string.IsNullOrWhiteSpace(text))
- return;
-
- if (text[0] == '/')
- channelManager?.PostCommand(text.Substring(1), Channel.Value);
- else
+ for (;;)
{
- EnqueueUserMessage(text);
-
- string[] parts = text.Split();
+ // 3 part commands
+ if (!(parts.Length == 3 && parts[0] == @"!mp"))
+ break;
- for (;;)
+ // commands with numerical parameter
+ if (int.TryParse(parts[2], out int numericParam))
{
- // 3 part commands
- if (!(parts.Length == 3 && parts[0] == @"!mp"))
- break;
-
- // commands with numerical parameter
- if (int.TryParse(parts[2], out int numericParam))
+ switch (parts[1])
{
- switch (parts[1])
- {
- case @"map":
- beatmapLookupCache.GetBeatmapAsync(numericParam).ContinueWith(task => Schedule(() =>
- {
- APIBeatmap? beatmapInfo = task.GetResultSafely();
+ case @"map":
+ beatmapLookupCache.GetBeatmapAsync(numericParam).ContinueWith(task => Schedule(() =>
+ {
+ APIBeatmap? beatmapInfo = task.GetResultSafely();
- if (beatmapInfo?.BeatmapSet == null)
- {
- EnqueueBotMessage($@"Couldn't retrieve metadata for map ID {numericParam}");
- return;
- }
+ if (beatmapInfo?.BeatmapSet == null)
+ {
+ EnqueueBotMessage($@"Couldn't retrieve metadata for map ID {numericParam}");
+ return;
+ }
- addPlaylistItem(beatmapInfo);
+ addPlaylistItem(beatmapInfo);
- RemoveInternal(beatmapDownloadTracker, true);
- AddInternal(beatmapDownloadTracker = new BeatmapDownloadTracker(beatmapInfo.BeatmapSet));
- beatmapDownloadTracker.State.BindValueChanged(changeEvent =>
- {
- if (changeEvent.NewValue != DownloadState.NotDownloaded) return;
+ RemoveInternal(beatmapDownloadTracker, true);
+ AddInternal(beatmapDownloadTracker = new BeatmapDownloadTracker(beatmapInfo.BeatmapSet));
+ beatmapDownloadTracker.State.BindValueChanged(changeEvent =>
+ {
+ if (changeEvent.NewValue != DownloadState.NotDownloaded) return;
- if (autoDownload.Value)
- beatmapsDownloader.Download(beatmapInfo.BeatmapSet);
+ if (autoDownload.Value)
+ beatmapsDownloader.Download(beatmapInfo.BeatmapSet);
- beatmapDownloadTracker.State.UnbindAll();
- RemoveInternal(beatmapDownloadTracker, true);
- });
- }));
- break;
+ beatmapDownloadTracker.State.UnbindAll();
+ RemoveInternal(beatmapDownloadTracker, true);
+ });
+ }));
+ break;
- case @"timer":
- chatTimerHandler?.SetTimer(TimeSpan.FromSeconds(numericParam), Time.Current);
- break;
+ case @"timer":
+ chatTimerHandler?.SetTimer(TimeSpan.FromSeconds(numericParam), Time.Current);
+ break;
- case @"start":
- // we intentionally do this check both in startMatch and here
- if (!Client.IsHost)
- {
- Logger.Log(@"Tried to start match when user is not host of the room. Cancelling!", LoggingTarget.Runtime, LogLevel.Important);
- return;
- }
+ case @"start":
+ // we intentionally do this check both in startMatch and here
+ if (!Client.IsHost)
+ {
+ Logger.Log(@"Tried to start match when user is not host of the room. Cancelling!", LoggingTarget.Runtime, LogLevel.Important);
+ return;
+ }
- chatTimerHandler?.SetTimer(TimeSpan.FromSeconds(numericParam), Time.Current, messagePrefix: @"Match starts in", onTimerComplete: startMatch);
- break;
- }
- }
- // special-case player invites
- else if (parts[2].StartsWith('#') && int.TryParse(parts[2].AsSpan(1), out numericParam) && parts[1] == @"invite")
- {
- inviteUserToRoom(numericParam);
+ chatTimerHandler?.SetTimer(TimeSpan.FromSeconds(numericParam), Time.Current, messagePrefix: @"Match starts in", onTimerComplete: startMatch);
+ break;
}
- else
+ }
+ // special-case player invites
+ else if (parts[2].StartsWith('#') && int.TryParse(parts[2].AsSpan(1), out numericParam) && parts[1] == @"invite")
+ {
+ inviteUserToRoom(numericParam);
+ }
+ else
+ {
+ switch (parts[1])
{
- switch (parts[1])
- {
- // i don't think this belongs here in the first place... whatever
- // ReSharper disable once StringLiteralTypo
- case @"aborttimer":
+ // i don't think this belongs here in the first place... whatever
+ // ReSharper disable once StringLiteralTypo
+ case @"aborttimer":
+ abortTimer();
+ break;
+
+ case @"timer":
+ if (parts[2] == @"abort")
abortTimer();
- break;
- case @"timer":
- if (parts[2] == @"abort")
- abortTimer();
+ break;
- break;
+ case @"invite":
+ EnqueueBotMessage(@"use their user IDs from the sheets please kthx");
+ break;
+
+ case @"mods":
+ var itemToEdit = Client.Room?.Playlist.SingleOrDefault(i => i.ID == Client.Room?.Settings.PlaylistItemId);
- case @"invite":
- // // parameter is a username since it didn't start with `#`
- // if (string.IsNullOrEmpty(parts[2]))
- // {
- // EnqueueBotMessage(@"Invalid username provided");
- // break;
- // }
- //
- // string username = parts[2].Replace('_', ' ');
- //
- // // check if user is already in the lobby
- // var matchingUser = Client.Room?.Users.FirstOrDefault(u => string.Equals(u.User?.Username, username, StringComparison.OrdinalIgnoreCase));
- //
- // if (matchingUser != null)
- // {
- // EnqueueBotMessage($@"User {matchingUser.User?.Username ?? ""} is already in the room!");
- // break;
- // }
- //
- // // try to resolve the username
- // userReq?.Cancel();
- // userReq = new GetUserRequest(username);
- // userReq.Success += u => inviteUserToRoom(u.Id);
- // userReq.Failure += e =>
- // {
- // EnqueueBotMessage($@"Couldn't find user {username}: {e.InnerException?.Message}");
- // };
- //
- // API.Queue(userReq);
- EnqueueBotMessage("use their user IDs from the sheets please kthx");
+ if (itemToEdit == null)
break;
- case @"mods":
- var itemToEdit = Client.Room?.Playlist.SingleOrDefault(i => i.ID == Client.Room?.Settings.PlaylistItemId);
+ string[] mods = parts[2].Split("+");
+ List modInstances = new List();
- if (itemToEdit == null)
- break;
+ foreach (string mod in mods)
+ {
+ if (mod.Length < 2)
+ {
+ Logger.Log($@"[!mp mods] Unknown mod '{mod}', ignoring", LoggingTarget.Runtime, LogLevel.Important);
+ continue;
+ }
- string[] mods = parts[2].Split("+");
- List modInstances = new List();
+ string modAcronym = mod[..2];
+ var rulesetInstance = RulesetStore.GetRuleset(itemToEdit.RulesetID)?.CreateInstance();
- foreach (string mod in mods)
+ if (rulesetInstance == null)
{
- if (mod.Length < 2)
- {
- Logger.Log($@"[!mp mods] Unknown mod '{mod}', ignoring", LoggingTarget.Runtime, LogLevel.Important);
- continue;
- }
+ Logger.Log($@"[!mp mods] Couldn't create ruleset instance with ruleset ID {itemToEdit.RulesetID}, ignoring mod '{mod}'",
+ LoggingTarget.Runtime, LogLevel.Important);
+ continue;
+ }
- string modAcronym = mod[..2];
- var rulesetInstance = RulesetStore.GetRuleset(itemToEdit.RulesetID)?.CreateInstance();
+ Mod? modInstance;
- if (rulesetInstance == null)
- {
- Logger.Log($@"[!mp mods] Couldn't create ruleset instance with ruleset ID {itemToEdit.RulesetID}, ignoring mod '{mod}'",
- LoggingTarget.Runtime, LogLevel.Important);
- continue;
- }
+ // mod with no params
+ if (mod.Length == 2)
+ {
+ modInstance = ParseMod(rulesetInstance, modAcronym, Array.Empty