diff --git a/Content.Client/Administration/Commands/PlayGlobalSoundCommand.cs b/Content.Client/Administration/Commands/PlayGlobalSoundCommand.cs
new file mode 100644
index 0000000000..e52bf2d33a
--- /dev/null
+++ b/Content.Client/Administration/Commands/PlayGlobalSoundCommand.cs
@@ -0,0 +1,48 @@
+using System.Linq;
+using Robust.Client.Player;
+using Robust.Shared.Console;
+using Robust.Shared.ContentPack;
+
+namespace Content.Client.Administration.Commands;
+
+///
+/// Proxy to server-side playglobalsound command. Implements completions.
+///
+public sealed class PlayGlobalSoundCommand : IConsoleCommand
+{
+ public string Command => "playglobalsound";
+ public string Description => Loc.GetString("play-global-sound-command-description");
+ public string Help => Loc.GetString("play-global-sound-command-help");
+
+ public void Execute(IConsoleShell shell, string argStr, string[] args)
+ {
+ shell.RemoteExecuteCommand(argStr);
+ }
+
+ public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
+ {
+ if (args.Length == 1)
+ {
+ var hint = Loc.GetString("play-global-sound-command-arg-path");
+ var res = IoCManager.Resolve();
+
+ var options = CompletionHelper.ContentFilePath(args[0], res);
+
+ return CompletionResult.FromHintOptions(options, hint);
+ }
+
+ if (args.Length == 2)
+ return CompletionResult.FromHint(Loc.GetString("play-global-sound-command-arg-volume"));
+
+ if (args.Length > 2)
+ {
+ var plyMgr = IoCManager.Resolve();
+ var options = plyMgr.Sessions.Select(c => c.Name);
+ return CompletionResult.FromHintOptions(
+ options,
+ Loc.GetString("play-global-sound-command-arg-usern", ("user", args.Length - 2)));
+ }
+
+ return CompletionResult.Empty;
+ }
+}
diff --git a/Content.Server/Administration/Commands/BanCommand.cs b/Content.Server/Administration/Commands/BanCommand.cs
index fc53e1930b..baff7d1b68 100644
--- a/Content.Server/Administration/Commands/BanCommand.cs
+++ b/Content.Server/Administration/Commands/BanCommand.cs
@@ -1,3 +1,4 @@
+using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
@@ -110,5 +111,32 @@ namespace Content.Server.Administration.Commands
targetPlayer.ConnectedClient.Disconnect(banDef.DisconnectMessage);
}
}
+
+ public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
+ {
+ if (args.Length == 1)
+ {
+ var playerMgr = IoCManager.Resolve();
+ var options = playerMgr.ServerSessions.Select(c => c.Name).OrderBy(c => c).ToArray();
+ return CompletionResult.FromHintOptions(options, "");
+ }
+
+ if (args.Length == 2)
+ return CompletionResult.FromHint("");
+
+ if (args.Length == 3)
+ {
+ var durations = new CompletionOption[]
+ {
+ new("0", "Permanent"),
+ new("1440", "1 day"),
+ new("10080", "1 week"),
+ };
+
+ return CompletionResult.FromHintOptions(durations, "[duration]");
+ }
+
+ return CompletionResult.Empty;
+ }
}
}
diff --git a/Resources/Locale/en-US/administration/commands/play-global-sound-command.ftl b/Resources/Locale/en-US/administration/commands/play-global-sound-command.ftl
index 2c4d9fbc72..34510aa5fa 100644
--- a/Resources/Locale/en-US/administration/commands/play-global-sound-command.ftl
+++ b/Resources/Locale/en-US/administration/commands/play-global-sound-command.ftl
@@ -2,3 +2,6 @@ play-global-sound-command-description = Plays a global sound for a specific play
play-global-sound-command-help = playglobalsound [volume] [user 1] ... [user n]
play-global-sound-command-player-not-found = Player "{$username}" not found.
play-global-sound-command-volume-parse = Invalid volume of {$volume} specified.
+play-global-sound-command-arg-path =
+play-global-sound-command-arg-volume = [volume]
+play-global-sound-command-arg-usern = [user {$user}]
diff --git a/Resources/keybinds.yml b/Resources/keybinds.yml
index 7518c52bcb..fc230fca01 100644
--- a/Resources/keybinds.yml
+++ b/Resources/keybinds.yml
@@ -288,6 +288,16 @@ binds:
- function: TextHistoryNext
type: State
key: Down
+- function: TextCompleteNext
+ type: State
+ key: Down
+ priority: 1
+ canRepeat: true
+- function: TextCompletePrev
+ type: State
+ key: Up
+ priority: 1
+ canRepeat: true
- function: TextReleaseFocus
type: State
key: Escape