Console Unify (#1513)

* Renamed shared ICommand to IConsoleCommand.

* Lots of refactoring into a shared context.

* Removed ICommonSession from server concmd Execute.

* Added argStr parameter to concmd execute.

* The execute function of client concmds now returns void, use the new shell.RemoteExecuteCommand function to forward commands.

# Conflicts:
#	Robust.Client/Console/Commands/Debug.cs

* Finally move shells and commands into shared.

* Console commands can now be registered directly without a class in a shared context.

* Pulled up ConsoleHost and Console shell into a shared context.

* Pulled up half the functions of ConsoleHost into a shared context.

* Repair rebase damage.

* Make LoadConsoleCommands function not remove any previously registered commands.
This commit is contained in:
Acruid
2021-02-01 16:40:26 -08:00
committed by GitHub
parent 1f64f93ef4
commit 3eb6e067f9
58 changed files with 1302 additions and 1153 deletions

View File

@@ -78,7 +78,7 @@ namespace Robust.Client
IoCManager.Register<IDebugDrawingManager, DebugDrawingManager>();
IoCManager.Register<ILightManager, LightManager>();
IoCManager.Register<IDiscordRichPresence, DiscordRichPresence>();
IoCManager.Register<IClientConsole, ClientConsole>();
IoCManager.Register<IClientConsoleHost, ClientConsoleHost>();
IoCManager.Register<IFontManager, FontManager>();
IoCManager.Register<IFontManagerInternal, FontManager>();
IoCManager.Register<IMidiManager, MidiManager>();

View File

@@ -1,243 +0,0 @@
using System;
using System.Collections.Generic;
using Robust.Client.Interfaces.Console;
using Robust.Client.Log;
using Robust.Client.Utility;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Reflection;
using Robust.Shared.Utility;
namespace Robust.Client.Console
{
public class AddStringArgs : EventArgs
{
public string Text { get; }
public Color Color { get; }
public AddStringArgs(string text, Color color)
{
Text = text;
Color = color;
}
}
public class AddFormattedMessageArgs : EventArgs
{
public readonly FormattedMessage Message;
public AddFormattedMessageArgs(FormattedMessage message)
{
Message = message;
}
}
internal sealed class ClientConsole : IClientConsole, IDebugConsole
{
private static readonly Color MsgColor = new Color(65, 105, 225);
[Dependency] private readonly IClientNetManager _network = default!;
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
[Dependency] private readonly ILogManager logManager = default!;
private readonly Dictionary<string, IConsoleCommand> _commands = new Dictionary<string, IConsoleCommand>();
private bool _requestedCommands;
/// <inheritdoc />
public void Initialize()
{
_network.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME, HandleConCmdReg);
_network.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME, HandleConCmdAck);
_network.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME);
Reset();
logManager.RootSawmill.AddHandler(new DebugConsoleLogHandler(this));
}
/// <inheritdoc />
public void Reset()
{
_commands.Clear();
_requestedCommands = false;
_network.Connected += OnNetworkConnected;
InitializeCommands();
SendServerCommandRequest();
}
private void OnNetworkConnected(object? sender, NetChannelArgs netChannelArgs)
{
SendServerCommandRequest();
}
/// <inheritdoc />
public void Dispose()
{
// We don't have anything to dispose.
}
public IReadOnlyDictionary<string, IConsoleCommand> Commands => _commands;
public void AddLine(string text, Color color)
{
AddString?.Invoke(this, new AddStringArgs(text, color));
}
public void AddLine(string text)
{
AddLine(text, Color.White);
}
public void Clear()
{
ClearText?.Invoke(this, EventArgs.Empty);
}
public event EventHandler<AddStringArgs>? AddString;
public event EventHandler? ClearText;
public event EventHandler<AddFormattedMessageArgs>? AddFormatted;
private void HandleConCmdAck(MsgConCmdAck msg)
{
AddLine("< " + msg.Text, MsgColor);
}
private void HandleConCmdReg(MsgConCmdReg msg)
{
foreach (var cmd in msg.Commands)
{
var commandName = cmd.Name;
// Do not do duplicate commands.
if (_commands.ContainsKey(commandName))
{
Logger.DebugS("console", $"Server sent console command {commandName}, but we already have one with the same name. Ignoring.");
continue;
}
var command = new ServerDummyCommand(commandName, cmd.Help, cmd.Description);
_commands[commandName] = command;
}
}
/// <summary>
/// Processes commands (chat messages starting with /)
/// </summary>
/// <param name="text">input text</param>
public void ProcessCommand(string text)
{
if (string.IsNullOrWhiteSpace(text))
return;
// echo the command locally
AddLine("> " + text, Color.Lime);
//Commands are processed locally and then sent to the server to be processed there again.
var args = new List<string>();
CommandParsing.ParseArguments(text, args);
var commandname = args[0];
var forward = true;
if (_commands.ContainsKey(commandname))
{
var command = _commands[commandname];
args.RemoveAt(0);
forward = command.Execute(this, args.ToArray());
}
else if (!_network.IsConnected)
{
AddLine("Unknown command: " + commandname, Color.Red);
return;
}
if (forward)
SendServerConsoleCommand(text);
}
/// <summary>
/// Locates and registeres all local commands.
/// </summary>
private void InitializeCommands()
{
foreach (var t in _reflectionManager.GetAllChildren<IConsoleCommand>())
{
var instance = (IConsoleCommand)Activator.CreateInstance(t)!;
if (_commands.ContainsKey(instance.Command))
throw new InvalidOperationException($"Command already registered: {instance.Command}");
_commands[instance.Command] = instance;
}
}
/// <summary>
/// Requests remote commands from server.
/// </summary>
public void SendServerCommandRequest()
{
if (_requestedCommands)
return;
if (!_network.IsConnected)
return;
var msg = _network.CreateNetMessage<MsgConCmdReg>();
_network.ClientSendMessage(msg);
_requestedCommands = true;
}
/// <summary>
/// Sends a command directly to the server.
/// </summary>
private void SendServerConsoleCommand(string text)
{
if (_network == null || !_network.IsConnected)
return;
var msg = _network.CreateNetMessage<MsgConCmd>();
msg.Text = text;
_network.ClientSendMessage(msg);
}
public void AddFormattedLine(FormattedMessage message)
{
// Why the hell does this class implement IDebugConsole.
AddFormatted?.Invoke(this, new AddFormattedMessageArgs(message));
}
}
/// <summary>
/// These dummies are made purely so list and help can list server-side commands.
/// </summary>
[Reflect(false)]
internal class ServerDummyCommand : IConsoleCommand
{
internal ServerDummyCommand(string command, string help, string description)
{
Command = command;
Help = help;
Description = description;
}
public string Command { get; }
public string Help { get; }
public string Description { get; }
// Always forward to server.
public bool Execute(IDebugConsole console, params string[] args)
{
return true;
}
}
}

View File

@@ -0,0 +1,198 @@
using System;
using System.Collections.Generic;
using Robust.Client.Log;
using Robust.Shared.Console;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Players;
using Robust.Shared.Reflection;
using Robust.Shared.Utility;
namespace Robust.Client.Console
{
public class AddStringArgs : EventArgs
{
public Color Color { get; }
public string Text { get; }
public AddStringArgs(string text, Color color)
{
Text = text;
Color = color;
}
}
public class AddFormattedMessageArgs : EventArgs
{
public readonly FormattedMessage Message;
public AddFormattedMessageArgs(FormattedMessage message)
{
Message = message;
}
}
/// <inheritdoc cref="IClientConsoleHost" />
internal class ClientConsoleHost : ConsoleHost, IClientConsoleHost
{
private static readonly Color _msgColor = new(65, 105, 225);
private bool _requestedCommands;
/// <inheritdoc />
public void Initialize()
{
NetManager.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME, HandleConCmdReg);
NetManager.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME, HandleConCmdAck);
NetManager.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME);
Reset();
LogManager.RootSawmill.AddHandler(new DebugConsoleLogHandler(this));
}
/// <inheritdoc />
public void Reset()
{
AvailableCommands.Clear();
_requestedCommands = false;
NetManager.Connected += OnNetworkConnected;
LoadConsoleCommands();
SendServerCommandRequest();
}
public event EventHandler<AddStringArgs>? AddString;
public event EventHandler<AddFormattedMessageArgs>? AddFormatted;
public void AddFormattedLine(FormattedMessage message)
{
AddFormatted?.Invoke(this, new AddFormattedMessageArgs(message));
}
public override void WriteLine(ICommonSession? session, string text, Color color)
{
AddString?.Invoke(this, new AddStringArgs(text, color));
}
public override void ExecuteCommand(ICommonSession? session, string command)
{
if (string.IsNullOrWhiteSpace(command))
return;
// echo the command locally
WriteLine(null, "> " + command, Color.Lime);
//Commands are processed locally and then sent to the server to be processed there again.
var args = new List<string>();
CommandParsing.ParseArguments(command, args);
var commandname = args[0];
if (AvailableCommands.ContainsKey(commandname))
{
var command1 = AvailableCommands[commandname];
args.RemoveAt(0);
command1.Execute(new ConsoleShell(this, null), command, args.ToArray());
}
else if (!NetManager.IsConnected) WriteLine(null, "Unknown command: " + commandname, Color.Red);
}
/// <summary>
/// Sends a command directly to the server.
/// </summary>
public override void RemoteExecuteCommand(ICommonSession? session, string command)
{
if (!NetManager.IsConnected)
return;
var msg = NetManager.CreateNetMessage<MsgConCmd>();
msg.Text = command;
NetManager.ClientSendMessage(msg);
}
public override void WriteLine(ICommonSession? session, string text)
{
WriteLine(null, text, Color.White);
}
/// <inheritdoc />
public void Dispose()
{
// We don't have anything to dispose.
}
private void OnNetworkConnected(object? sender, NetChannelArgs netChannelArgs)
{
SendServerCommandRequest();
}
private void HandleConCmdAck(MsgConCmdAck msg)
{
WriteLine(null, "< " + msg.Text, _msgColor);
}
private void HandleConCmdReg(MsgConCmdReg msg)
{
foreach (var cmd in msg.Commands)
{
var commandName = cmd.Name;
// Do not do duplicate commands.
if (AvailableCommands.ContainsKey(commandName))
{
Logger.DebugS("console", $"Server sent console command {commandName}, but we already have one with the same name. Ignoring.");
continue;
}
var command = new ServerDummyCommand(commandName, cmd.Help, cmd.Description);
AvailableCommands[commandName] = command;
}
}
/// <summary>
/// Requests remote commands from server.
/// </summary>
private void SendServerCommandRequest()
{
if (_requestedCommands)
return;
if (!NetManager.IsConnected)
return;
var msg = NetManager.CreateNetMessage<MsgConCmdReg>();
NetManager.ClientSendMessage(msg);
_requestedCommands = true;
}
}
/// <summary>
/// These dummies are made purely so list and help can list server-side commands.
/// </summary>
[Reflect(false)]
internal class ServerDummyCommand : IConsoleCommand
{
public string Command { get; }
public string Description { get; }
public string Help { get; }
internal ServerDummyCommand(string command, string help, string description)
{
Command = command;
Help = help;
Description = description;
}
// Always forward to server.
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
shell.RemoteExecuteCommand(argStr);
}
}
}

View File

@@ -1,6 +1,6 @@
using JetBrains.Annotations;
using Robust.Client.Interfaces.Console;
using Robust.Client.Player;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
@@ -14,13 +14,13 @@ namespace Robust.Client.Console.Commands
public string Description => "Adds a component to an entity on the client";
public string Help => "addcompc <uid> <componentName>";
public bool Execute(IDebugConsole shell, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.AddLine("Wrong number of arguments");
return false;
shell.WriteLine("Wrong number of arguments");
return;
}
var entityUid = EntityUid.Parse(args[0]);
@@ -36,8 +36,6 @@ namespace Robust.Client.Console.Commands
component.Owner = entity;
compManager.AddComponent(entity, component);
return false;
}
}
@@ -48,12 +46,12 @@ namespace Robust.Client.Console.Commands
public string Description => "Removes a component from an entity.";
public string Help => "rmcompc <uid> <componentName>";
public bool Execute(IDebugConsole shell, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.AddLine("Wrong number of arguments");
return false;
shell.WriteLine("Wrong number of arguments");
return;
}
var entityUid = EntityUid.Parse(args[0]);
@@ -65,8 +63,6 @@ namespace Robust.Client.Console.Commands
var registration = compFactory.GetRegistration(componentName);
compManager.RemoveComponent(entityUid, registration.Type);
return false;
}
}
}

View File

@@ -1,6 +1,6 @@
using JetBrains.Annotations;
using Robust.Client.Interfaces.Console;
using Robust.Client.Player;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
@@ -13,18 +13,17 @@ namespace Robust.Client.Console.Commands
public string Description => "Spawns a client-side entity with specific type at your feet.";
public string Help => "cspawn <entity type>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var player = IoCManager.Resolve<IPlayerManager>().LocalPlayer;
if (player?.ControlledEntity == null)
{
console.AddLine("You don't have an attached entity.");
return false;
shell.WriteLine("You don't have an attached entity.");
return;
}
var entityManager = IoCManager.Resolve<IEntityManager>();
entityManager.SpawnEntity(args[0], player.ControlledEntity.Transform.Coordinates);
return false;
}
}
}

View File

@@ -1,8 +1,8 @@
using System;
using System.Linq;
using JetBrains.Annotations;
using Robust.Client.Interfaces.Console;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
@@ -12,12 +12,12 @@ namespace Robust.Client.Console.Commands
[UsedImplicitly]
internal sealed class CVarCommand : SharedCVarCommand, IConsoleCommand
{
public bool Execute(IDebugConsole console, params string[] args)
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1 || args.Length > 2)
{
console.AddLine("Must provide exactly one or two arguments.", Color.Red);
return false;
shell.WriteLine("Must provide exactly one or two arguments.", Color.Red);
return;
}
var configManager = IoCManager.Resolve<IConfigurationManager>();
@@ -26,21 +26,21 @@ namespace Robust.Client.Console.Commands
if (name == "?")
{
var cvars = configManager.GetRegisteredCVars().OrderBy(c => c);
console.AddLine(string.Join("\n", cvars));
return false;
shell.WriteLine(string.Join("\n", cvars));
return;
}
if (!configManager.IsCVarRegistered(name))
{
console.AddLine($"CVar '{name}' is not registered. Use 'cvar ?' to get a list of all registered CVars.", Color.Red);
return false;
shell.WriteLine($"CVar '{name}' is not registered. Use 'cvar ?' to get a list of all registered CVars.", Color.Red);
return;
}
if (args.Length == 1)
{
// Read CVar
var value = configManager.GetCVar<object>(name);
console.AddLine(value.ToString() ?? "");
shell.WriteLine(value.ToString() ?? "");
}
else
{
@@ -54,11 +54,9 @@ namespace Robust.Client.Console.Commands
}
catch (FormatException)
{
console.AddLine($"Input value is in incorrect format for type {type}");
shell.WriteLine($"Input value is in incorrect format for type {type}");
}
}
return false;
}
}
@@ -69,10 +67,9 @@ namespace Robust.Client.Console.Commands
public string Description => "Saves the client configuration to the config file";
public string Help => "saveconfig";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
IoCManager.Resolve<IConfigurationManager>().SaveToFile();
return false;
}
}

View File

@@ -1,9 +1,8 @@
// This file is for commands that do something to the console itself.
// This file is for commands that do something to the console itself.
// Not some generic console command type.
// Couldn't think of a better name sorry.
using System;
using Robust.Client.Interfaces.Console;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
@@ -17,10 +16,9 @@ namespace Robust.Client.Console.Commands
public string Help => "Clears the debug console of all messages.";
public string Description => "Clears the console.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
console.Clear();
return false;
shell.Clear();
}
}
@@ -30,15 +28,14 @@ namespace Robust.Client.Console.Commands
public string Help => "Fills the console with some nonsense for debugging.";
public string Description => "Fill up the console for debugging.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
Color[] colors = { Color.Green, Color.Blue, Color.Red };
var random = IoCManager.Resolve<IRobustRandom>();
for (int x = 0; x < 50; x++)
{
console.AddLine("filling...", colors[random.Next(0, colors.Length)]);
shell.WriteLine("filling...", colors[random.Next(0, colors.Length)]);
}
return false;
}
}
}

View File

@@ -12,7 +12,6 @@ using System.Text.RegularExpressions;
using Robust.Client.Input;
using System.Threading;
using Robust.Client.Interfaces;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.Debugging;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.Interfaces.Graphics.ClientEye;
@@ -26,6 +25,7 @@ using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Asynchronous;
using Robust.Shared.Console;
using Robust.Shared.ContentPack;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Transform;
@@ -49,16 +49,14 @@ namespace Robust.Client.Console.Commands
public string Help => "Dump entity list";
public string Description => "Dumps entity list of UIDs and prototype.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var entityManager = IoCManager.Resolve<IEntityManager>();
foreach (var e in entityManager.GetEntities().OrderBy(e => e.Uid))
{
console.AddLine($"entity {e.Uid}, {e.Prototype?.ID}, {e.Transform.Coordinates}.", Color.White);
shell.WriteLine($"entity {e.Uid}, {e.Prototype?.ID}, {e.Transform.Coordinates}.", Color.White);
}
return false;
}
}
@@ -68,12 +66,12 @@ namespace Robust.Client.Console.Commands
public string Help => "Usage: getcomponentregistration <componentName>";
public string Description => "Gets component registration information";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
var componentFactory = IoCManager.Resolve<IComponentFactory>();
@@ -94,19 +92,17 @@ namespace Robust.Client.Console.Commands
message.Append($", NSE: {registration.NetworkSynchronizeExistence}, references:");
console.AddLine(message.ToString(), Color.White);
shell.WriteLine(message.ToString(), Color.White);
foreach (var type in registration.References)
{
console.AddLine($" {type}", Color.White);
shell.WriteLine($" {type}", Color.White);
}
}
catch (UnknownComponentException)
{
console.AddLine($"No registration found for '{args[0]}'", Color.Red);
shell.WriteLine($"No registration found for '{args[0]}'", Color.Red);
}
return false;
}
}
@@ -119,14 +115,14 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggles a debug monitor in the F3 menu.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var monitor = IoCManager.Resolve<IUserInterfaceManager>().DebugMonitors;
if (args.Length != 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
switch (args[0])
@@ -159,11 +155,9 @@ namespace Robust.Client.Console.Commands
monitor.ShowInput ^= true;
break;
default:
console.AddLine($"Invalid key: {args[0]}");
shell.WriteLine($"Invalid key: {args[0]}");
break;
}
return false;
}
}
@@ -173,7 +167,7 @@ namespace Robust.Client.Console.Commands
public string Help => "Throws an exception";
public string Description => "Throws an exception";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
throw new InvalidOperationException("Fuck");
}
@@ -185,11 +179,10 @@ namespace Robust.Client.Console.Commands
public string Help => "";
public string Description => "Enables debug drawing over all bounding boxes in the game, showing their size.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IDebugDrawing>();
mgr.DebugColliders = !mgr.DebugColliders;
return false;
}
}
@@ -199,11 +192,10 @@ namespace Robust.Client.Console.Commands
public string Help => "";
public string Description => "Enables debug drawing over all entity positions in the game.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IDebugDrawing>();
mgr.DebugPositions = !mgr.DebugPositions;
return false;
}
}
@@ -213,25 +205,24 @@ namespace Robust.Client.Console.Commands
public string Help => "Usage: showrays <raylifetime>";
public string Description => "Toggles debug drawing of physics rays. An integer for <raylifetime> must be provided";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
if (!int.TryParse(args[0], out var id))
{
console.AddLine($"{args[0]} is not a valid integer.",Color.Red);
return false;
shell.WriteLine($"{args[0]} is not a valid integer.",Color.Red);
return;
}
var mgr = IoCManager.Resolve<IDebugDrawingManager>();
mgr.DebugDrawRays = !mgr.DebugDrawRays;
console.AddLine("Toggled showing rays to:" + mgr.DebugDrawRays.ToString(), Color.Green);
shell.WriteLine("Toggled showing rays to:" + mgr.DebugDrawRays.ToString(), Color.Green);
mgr.DebugRayLifetime = TimeSpan.FromSeconds((double)int.Parse(args[0], CultureInfo.InvariantCulture));
return false;
}
}
@@ -241,10 +232,9 @@ namespace Robust.Client.Console.Commands
public string Help => "";
public string Description => "Immediately disconnect from the server and go back to the main menu.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
IoCManager.Resolve<IClientNetManager>().ClientDisconnect("Disconnect command used.");
return false;
}
}
@@ -257,33 +247,33 @@ namespace Robust.Client.Console.Commands
public string Description => "Displays verbose diagnostics for an entity.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
if ((!new Regex(@"^c?[0-9]+$").IsMatch(args[0])))
{
console.AddLine("Malformed UID", Color.Red);
return false;
shell.WriteLine("Malformed UID", Color.Red);
return;
}
var uid = EntityUid.Parse(args[0]);
var entmgr = IoCManager.Resolve<IEntityManager>();
if (!entmgr.TryGetEntity(uid, out var entity))
{
console.AddLine("That entity does not exist. Sorry lad.", Color.Red);
return false;
shell.WriteLine("That entity does not exist. Sorry lad.", Color.Red);
return;
}
console.AddLine($"{entity.Uid}: {entity.Prototype?.ID}/{entity.Name}");
console.AddLine($"init/del/lmt: {entity.Initialized}/{entity.Deleted}/{entity.LastModifiedTick}");
shell.WriteLine($"{entity.Uid}: {entity.Prototype?.ID}/{entity.Name}");
shell.WriteLine($"init/del/lmt: {entity.Initialized}/{entity.Deleted}/{entity.LastModifiedTick}");
foreach (var component in entity.GetAllComponents())
{
console.AddLine(component.ToString() ?? "");
shell.WriteLine(component.ToString() ?? "");
if (component is IComponentDebug debug)
{
foreach (var line in debug.GetDebugString().Split('\n'))
@@ -293,12 +283,10 @@ namespace Robust.Client.Console.Commands
continue;
}
console.AddLine("\t" + line);
shell.WriteLine("\t" + line);
}
}
}
return false;
}
}
@@ -308,12 +296,12 @@ namespace Robust.Client.Console.Commands
public string Help => "sggcell <gridID> <vector2i> [offset]\nThat vector2i param is in the form x<int>,y<int>.";
public string Description => "Lists entities on a snap grid cell.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2 && args.Length != 3)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
string gridId = args[0];
@@ -322,14 +310,14 @@ namespace Robust.Client.Console.Commands
if (!int.TryParse(args[0], out var id))
{
console.AddLine($"{args[0]} is not a valid integer.",Color.Red);
return false;
shell.WriteLine($"{args[0]} is not a valid integer.",Color.Red);
return;
}
if (!new Regex(@"^-?[0-9]+,-?[0-9]+$").IsMatch(indices))
{
console.AddLine("mapIndicies must be of form x<int>,y<int>", Color.Red);
return false;
shell.WriteLine("mapIndicies must be of form x<int>,y<int>", Color.Red);
return;
}
SnapGridOffset selectedOffset;
@@ -339,8 +327,8 @@ namespace Robust.Client.Console.Commands
}
else
{
console.AddLine("given offset type is not defined", Color.Red);
return false;
shell.WriteLine("given offset type is not defined", Color.Red);
return;
}
var mapMan = IoCManager.Resolve<IMapManager>();
@@ -354,16 +342,13 @@ namespace Robust.Client.Console.Commands
int.Parse(indices.Split(',')[1], CultureInfo.InvariantCulture)),
selectedOffset))
{
console.AddLine(entity.Owner.Uid.ToString());
shell.WriteLine(entity.Owner.Uid.ToString());
}
}
else
{
console.AddLine("grid does not exist", Color.Red);
return false;
shell.WriteLine("grid does not exist", Color.Red);
}
return false;
}
}
@@ -373,19 +358,17 @@ namespace Robust.Client.Console.Commands
public string Description => "Changes the name used when attempting to connect to the server.";
public string Help => Command + " <name>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
var client = IoCManager.Resolve<IBaseClient>();
client.PlayerNameOverride = args[0];
console.AddLine($"Overriding player name to \"{args[0]}\".", Color.White);
return false;
shell.WriteLine($"Overriding player name to \"{args[0]}\".", Color.White);
}
}
@@ -395,12 +378,12 @@ namespace Robust.Client.Console.Commands
public string Description => "Pre-caches a resource.";
public string Help => "ldrsc <path> <type>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 2)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
var resourceCache = IoCManager.Resolve<IResourceCache>();
var reflection = IoCManager.Resolve<IReflectionManager>();
@@ -412,8 +395,8 @@ namespace Robust.Client.Console.Commands
}
catch(ArgumentException)
{
console.AddLine("Unable to find type", Color.Red);
return false;
shell.WriteLine("Unable to find type", Color.Red);
return;
}
var getResourceMethod =
@@ -423,7 +406,6 @@ namespace Robust.Client.Console.Commands
DebugTools.Assert(getResourceMethod != null);
var generic = getResourceMethod!.MakeGenericMethod(type);
generic.Invoke(resourceCache, new object[] { args[0], true });
return false;
}
}
@@ -433,12 +415,12 @@ namespace Robust.Client.Console.Commands
public string Description => "Reloads a resource.";
public string Help => "rldrsc <path> <type>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 2)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
var resourceCache = IoCManager.Resolve<IResourceCache>();
var reflection = IoCManager.Resolve<IReflectionManager>();
@@ -450,15 +432,14 @@ namespace Robust.Client.Console.Commands
}
catch(ArgumentException)
{
console.AddLine("Unable to find type", Color.Red);
return false;
shell.WriteLine("Unable to find type", Color.Red);
return;
}
var getResourceMethod = resourceCache.GetType().GetMethod("ReloadResource", new[] { typeof(string) });
DebugTools.Assert(getResourceMethod != null);
var generic = getResourceMethod!.MakeGenericMethod(type);
generic.Invoke(resourceCache, new object[] { args[0] });
return false;
}
}
@@ -468,18 +449,18 @@ namespace Robust.Client.Console.Commands
public string Description => "Gets the tile count of a grid";
public string Help => "Usage: gridtc <gridId>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
if (!int.TryParse(args[0], out var id))
{
console.AddLine($"{args[0]} is not a valid integer.");
return false;
shell.WriteLine($"{args[0]} is not a valid integer.");
return;
}
var gridId = new GridId(int.Parse(args[0]));
@@ -487,13 +468,11 @@ namespace Robust.Client.Console.Commands
if (mapManager.TryGetGrid(gridId, out var grid))
{
console.AddLine(mapManager.GetGrid(gridId).GetAllTiles().Count().ToString());
return false;
shell.WriteLine(mapManager.GetGrid(gridId).GetAllTiles().Count().ToString());
}
else
{
console.AddLine($"No grid exists with id {id}",Color.Red);
return false;
shell.WriteLine($"No grid exists with id {id}",Color.Red);
}
}
}
@@ -504,7 +483,7 @@ namespace Robust.Client.Console.Commands
public string Description => "Dump GUI tree to /guidump.txt in user data.";
public string Help => "guidump";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var root = IoCManager.Resolve<IUserInterfaceManager>().RootControl;
var res = IoCManager.Resolve<IResourceManager>();
@@ -514,8 +493,6 @@ namespace Robust.Client.Console.Commands
{
_writeNode(root, 0, writer);
}
return false;
}
private static void _writeNode(Control control, int indents, TextWriter writer)
@@ -575,7 +552,7 @@ namespace Robust.Client.Console.Commands
public string Description => "Open a dummy UI testing window";
public string Help => "uitest";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var window = new SS14Window { CustomMinimumSize = (500, 400)};
var tabContainer = new TabContainer();
@@ -667,8 +644,6 @@ namespace Robust.Client.Console.Commands
});
window.OpenCentered();
return false;
}
}
@@ -678,11 +653,10 @@ namespace Robust.Client.Console.Commands
public string Description => "Sets the system clipboard";
public string Help => "setclipboard <text>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IClipboardManager>();
mgr.SetText(args[0]);
return false;
}
}
@@ -692,11 +666,10 @@ namespace Robust.Client.Console.Commands
public string Description => "Gets the system clipboard";
public string Help => "getclipboard";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IClipboardManager>();
console.AddLine(mgr.GetText());
return false;
shell.WriteLine(mgr.GetText());
}
}
@@ -706,12 +679,11 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggles light rendering.";
public string Help => "togglelight";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<ILightManager>();
if (!mgr.LockConsoleAccess)
mgr.Enabled = !mgr.Enabled;
return false;
}
}
@@ -721,14 +693,11 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggles fov for client.";
public string Help => "togglefov";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var lmgr = IoCManager.Resolve<ILightManager>();
var mgr = IoCManager.Resolve<IEyeManager>();
if (!lmgr.LockConsoleAccess)
if (mgr.CurrentEye != null)
mgr.CurrentEye.DrawFov = !mgr.CurrentEye.DrawFov;
return false;
var mgr = IoCManager.Resolve<IEyeManager>();
if (mgr.CurrentEye != null)
mgr.CurrentEye.DrawFov = !mgr.CurrentEye.DrawFov;
}
}
@@ -738,12 +707,11 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggles hard fov for client (for debugging space-station-14#2353).";
public string Help => "togglehardfov";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<ILightManager>();
if (!mgr.LockConsoleAccess)
mgr.DrawHardFov = !mgr.DrawHardFov;
return false;
}
}
@@ -753,12 +721,11 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggles shadow rendering.";
public string Help => "toggleshadows";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<ILightManager>();
if (!mgr.LockConsoleAccess)
mgr.DrawShadows = !mgr.DrawShadows;
return false;
}
}
internal class ToggleLightBuf : IConsoleCommand
@@ -767,12 +734,11 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggles lighting rendering. This includes shadows but not FOV.";
public string Help => "togglelightbuf";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<ILightManager>();
if (!mgr.LockConsoleAccess)
mgr.DrawLighting = !mgr.DrawLighting;
return false;
}
}
@@ -782,7 +748,7 @@ namespace Robust.Client.Console.Commands
public string Description => "Run the GC.";
public string Help => "gc [generation]";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length == 0)
{
@@ -793,10 +759,8 @@ namespace Robust.Client.Console.Commands
if (int.TryParse(args[0], out int result))
GC.Collect(result);
else
console.AddLine("Failed to parse argument.",Color.Red);
shell.WriteLine("Failed to parse argument.",Color.Red);
}
return false;
}
}
@@ -806,12 +770,10 @@ namespace Robust.Client.Console.Commands
public string Description => "Run the GC, fully, compacting LOH and everything.";
public string Help => "gcf";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect(2, GCCollectionMode.Forced, true, true);
return false;
}
}
@@ -824,16 +786,16 @@ namespace Robust.Client.Console.Commands
public string Help => "gc_mode\nSee current GC Latencymode\ngc_mode [type]\n Change GC Latency mode to [type]";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var prevMode = GCSettings.LatencyMode;
if (args.Length == 0)
{
console.AddLine($"current gc latency mode: {(int) prevMode} ({prevMode})");
console.AddLine("possible modes:");
shell.WriteLine($"current gc latency mode: {(int) prevMode} ({prevMode})");
shell.WriteLine("possible modes:");
foreach (int mode in (int[]) Enum.GetValues(typeof(GCLatencyMode)))
{
console.AddLine($" {mode}: {Enum.GetName(typeof(GCLatencyMode), mode)}");
shell.WriteLine($" {mode}: {Enum.GetName(typeof(GCLatencyMode), mode)}");
}
}
else
@@ -845,16 +807,14 @@ namespace Robust.Client.Console.Commands
}
else if (!Enum.TryParse(args[0], true, out mode))
{
console.AddLine($"unknown gc latency mode: {args[0]}");
return false;
shell.WriteLine($"unknown gc latency mode: {args[0]}");
return;
}
console.AddLine($"attempting gc latency mode change: {(int) prevMode} ({prevMode}) -> {(int) mode} ({mode})");
shell.WriteLine($"attempting gc latency mode change: {(int) prevMode} ({prevMode}) -> {(int) mode} ({mode})");
GCSettings.LatencyMode = mode;
console.AddLine($"resulting gc latency mode: {(int) GCSettings.LatencyMode} ({GCSettings.LatencyMode})");
shell.WriteLine($"resulting gc latency mode: {(int) GCSettings.LatencyMode} ({GCSettings.LatencyMode})");
}
return false;
}
}
@@ -868,15 +828,13 @@ namespace Robust.Client.Console.Commands
public string Help => "szr_stats";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
console.AddLine($"serialized: {RobustSerializer.BytesSerialized} bytes, {RobustSerializer.ObjectsSerialized} objects");
console.AddLine($"largest serialized: {RobustSerializer.LargestObjectSerializedBytes} bytes, {RobustSerializer.LargestObjectSerializedType} objects");
console.AddLine($"deserialized: {RobustSerializer.BytesDeserialized} bytes, {RobustSerializer.ObjectsDeserialized} objects");
console.AddLine($"largest serialized: {RobustSerializer.LargestObjectDeserializedBytes} bytes, {RobustSerializer.LargestObjectDeserializedType} objects");
return false;
shell.WriteLine($"serialized: {RobustSerializer.BytesSerialized} bytes, {RobustSerializer.ObjectsSerialized} objects");
shell.WriteLine($"largest serialized: {RobustSerializer.LargestObjectSerializedBytes} bytes, {RobustSerializer.LargestObjectSerializedType} objects");
shell.WriteLine($"deserialized: {RobustSerializer.BytesDeserialized} bytes, {RobustSerializer.ObjectsDeserialized} objects");
shell.WriteLine($"largest serialized: {RobustSerializer.LargestObjectDeserializedBytes} bytes, {RobustSerializer.LargestObjectDeserializedType} objects");
}
}
@@ -887,7 +845,7 @@ namespace Robust.Client.Console.Commands
public string Description => "Gets info about a chunk under your mouse cursor.";
public string Help => Command;
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mapMan = IoCManager.Resolve<IMapManager>();
var inputMan = IoCManager.Resolve<IInputManager>();
@@ -897,8 +855,8 @@ namespace Robust.Client.Console.Commands
if (!mapMan.TryFindGridAt(mousePos, out var grid))
{
console.AddLine("No grid under your mouse cursor.");
return false;
shell.WriteLine("No grid under your mouse cursor.");
return;
}
var internalGrid = (IMapGridInternal)grid;
@@ -906,8 +864,7 @@ namespace Robust.Client.Console.Commands
var chunkIndex = grid.LocalToChunkIndices(grid.MapToGrid(mousePos));
var chunk = internalGrid.GetChunk(chunkIndex);
console.AddLine($"worldBounds: {chunk.CalcWorldBounds()} localBounds: {chunk.CalcLocalBounds()}");
return false;
shell.WriteLine($"worldBounds: {chunk.CalcWorldBounds()} localBounds: {chunk.CalcLocalBounds()}");
}
}
@@ -924,7 +881,7 @@ namespace Robust.Client.Console.Commands
public static ConcurrentDictionary<string, bool>? _reloadShadersQueued = new();
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
IResourceCacheInternal resC;
if (args.Length == 1)
@@ -933,8 +890,8 @@ namespace Robust.Client.Console.Commands
{
if (_watchers != null)
{
console.AddLine("Already watching.");
return false;
shell.WriteLine("Already watching.");
return;
}
resC = IoCManager.Resolve<IResourceCacheInternal>();
@@ -1003,11 +960,11 @@ namespace Robust.Client.Console.Commands
{
IoCManager.Resolve<IResourceCache>()
.ReloadResource<ShaderSourceResource>(resPath);
console.AddLine($"Reloaded shader: {resPath}");
shell.WriteLine($"Reloaded shader: {resPath}");
}
catch (Exception)
{
console.AddLine($"Failed to reload shader: {resPath}");
shell.WriteLine($"Failed to reload shader: {resPath}");
}
_reloadShadersQueued.TryRemove(ev.FullPath, out var _);
@@ -1027,17 +984,17 @@ namespace Robust.Client.Console.Commands
++created;
}
console.AddLine($"Created {created} shader directory watchers for {shaderCount} shaders.");
shell.WriteLine($"Created {created} shader directory watchers for {shaderCount} shaders.");
return false;
return;
}
if (args[0] == "-watch")
{
if (_watchers == null)
{
console.AddLine("No shader directory watchers active.");
return false;
shell.WriteLine("No shader directory watchers active.");
return;
}
var disposed = 0;
@@ -1049,19 +1006,19 @@ namespace Robust.Client.Console.Commands
_watchers = null;
console.AddLine($"Disposed of {disposed} shader directory watchers.");
shell.WriteLine($"Disposed of {disposed} shader directory watchers.");
return false;
return;
}
}
if (args.Length > 1)
{
console.AddLine("Not implemented.");
return false;
shell.WriteLine("Not implemented.");
return;
}
console.AddLine("Reloading content shader resources...");
shell.WriteLine("Reloading content shader resources...");
resC = IoCManager.Resolve<IResourceCacheInternal>();
@@ -1073,13 +1030,11 @@ namespace Robust.Client.Console.Commands
}
catch (Exception)
{
console.AddLine($"Failed to reload shader: {path}");
shell.WriteLine($"Failed to reload shader: {path}");
}
}
console.AddLine("Done.");
return false;
shell.WriteLine("Done.");
}
}
@@ -1090,14 +1045,14 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggle fov and light debug layers";
public string Help => "cldbglyr <layer>: Toggle <layer>\ncldbglyr: Turn all Layers off";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var clyde = IoCManager.Resolve<IClydeInternal>();
if (args.Length < 1)
{
clyde.DebugLayers = ClydeDebugLayers.None;
return false;
return;
}
clyde.DebugLayers = args[0] switch
@@ -1106,8 +1061,6 @@ namespace Robust.Client.Console.Commands
"light" => ClydeDebugLayers.Light,
_ => ClydeDebugLayers.None
};
return false;
}
}
@@ -1117,12 +1070,12 @@ namespace Robust.Client.Console.Commands
public string Description => "Keys key info for a key";
public string Help => "keyinfo <Key>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
var clyde = IoCManager.Resolve<IClydeInternal>();
@@ -1135,15 +1088,13 @@ namespace Robust.Client.Console.Commands
var scanCode = clyde.GetKeyScanCode(key);
var nameScanCode = clyde.GetKeyNameScanCode(scanCode);
console.AddLine($"name: '{name}' scan code: '{scanCode}' name via scan code: '{nameScanCode}'");
shell.WriteLine($"name: '{name}' scan code: '{scanCode}' name via scan code: '{nameScanCode}'");
}
else if (int.TryParse(args[0], out var scanCode))
{
var nameScanCode = clyde.GetKeyNameScanCode(scanCode);
console.AddLine($"name via scan code: '{nameScanCode}'");
shell.WriteLine($"name via scan code: '{nameScanCode}'");
}
return false;
}
}
}

View File

@@ -1,5 +1,5 @@
using System;
using Robust.Client.Interfaces.Console;
using Robust.Shared.Console;
using Robust.Shared.ContentPack;
using Robust.Shared.Maths;
@@ -12,23 +12,21 @@ namespace Robust.Client.Console.Commands
public string Description => "Dumps a type's members in a format suitable for the sandbox configuration file.";
public string Help => "Usage: dmetamem <type>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var type = Type.GetType(args[0]);
if (type == null)
{
console.AddLine("That type does not exist", Color.Red);
return false;
shell.WriteLine("That type does not exist", Color.Red);
return;
}
foreach (var sig in AssemblyTypeChecker.DumpMetaMembers(type))
{
System.Console.WriteLine(@$"- ""{sig}""");
console.AddLine(sig);
shell.WriteLine(sig);
}
return false;
}
}
#endif

View File

@@ -1,5 +1,5 @@
using System.Linq;
using Robust.Client.Interfaces.Console;
using System.Linq;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
@@ -12,37 +12,36 @@ namespace Robust.Client.Console.Commands
public string Help => "When no arguments are provided, displays a generic help text. When an argument is passed, display the help text for the command with that name.";
public string Description => "Display help text.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
switch (args.Length)
{
case 0:
console.AddLine("To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'.");
shell.WriteLine("To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'.");
break;
case 1:
string commandname = args[0];
if (!console.Commands.ContainsKey(commandname))
if (!shell.ConsoleHost.RegisteredCommands.ContainsKey(commandname))
{
if (!IoCManager.Resolve<IClientNetManager>().IsConnected)
{
// No server so nothing to respond with unknown command.
console.AddLine("Unknown command: " + commandname, Color.Red);
return false;
shell.WriteLine("Unknown command: " + commandname, Color.Red);
return;
}
// TODO: Maybe have a server side help?
return false;
return;
}
IConsoleCommand command = console.Commands[commandname];
console.AddLine(string.Format("{0} - {1}", command.Command, command.Description));
console.AddLine(command.Help);
IConsoleCommand command = shell.ConsoleHost.RegisteredCommands[commandname];
shell.WriteLine(string.Format("{0} - {1}", command.Command, command.Description));
shell.WriteLine(command.Help);
break;
default:
console.AddLine("Invalid amount of arguments.", Color.Red);
shell.WriteLine("Invalid amount of arguments.", Color.Red);
break;
}
return false;
}
}
@@ -55,7 +54,7 @@ namespace Robust.Client.Console.Commands
"only commands that contain the given string in their name will be listed.";
public string Description => "List all commands, optionally with a filter.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var filter = "";
if (args.Length == 1)
@@ -64,14 +63,12 @@ namespace Robust.Client.Console.Commands
}
var conGroup = IoCManager.Resolve<IClientConGroupController>();
foreach (var command in console.Commands.Values
foreach (var command in shell.ConsoleHost.RegisteredCommands.Values
.Where(p => p.Command.Contains(filter) && conGroup.CanCommand(p.Command))
.OrderBy(c => c.Command))
{
console.AddLine(command.Command + ": " + command.Description);
shell.WriteLine(command.Command + ": " + command.Description);
}
return false;
}
}
}

View File

@@ -3,9 +3,9 @@ using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Robust.Client.Interfaces.Console;
using Robust.Client.Utility;
using Robust.Shared;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.IoC;
@@ -17,7 +17,7 @@ namespace Robust.Client.Console.Commands
public string Description => "Load authentication tokens from launcher data to aid in testing of live servers";
public string Help => "launchauth [account name]";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var wantName = args.Length > 0 ? args[0] : null;
@@ -32,8 +32,8 @@ namespace Robust.Client.Console.Commands
if (login == null)
{
console.AddLine("Unable to find a matching login");
return false;
shell.WriteLine("Unable to find a matching login");
return;
}
var token = login.Token.Token;
@@ -42,8 +42,6 @@ namespace Robust.Client.Console.Commands
var cfg = IoCManager.Resolve<IConfigurationManagerInternal>();
cfg.SetSecureCVar(CVars.AuthUserId, userId);
cfg.SetSecureCVar(CVars.AuthToken, token);
return false;
}
private sealed class LauncherConfig

View File

@@ -2,7 +2,7 @@ using System.Linq;
using System.Runtime.Loader;
using System.Text;
using JetBrains.Annotations;
using Robust.Client.Interfaces.Console;
using Robust.Shared.Console;
namespace Robust.Client.Console.Commands
{
@@ -13,18 +13,16 @@ namespace Robust.Client.Console.Commands
public string Description => "Lists loaded assemblies by load context.";
public string Help => Command;
public bool Execute(IDebugConsole console, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
foreach (var context in AssemblyLoadContext.All)
{
console.AddLine($"{context.Name}:");
shell.WriteLine($"{context.Name}:");
foreach (var assembly in context.Assemblies.OrderBy(a => a.FullName))
{
console.AddLine($" {assembly.FullName}");
shell.WriteLine($" {assembly.FullName}");
}
}
return false;
}
}
}

View File

@@ -1,7 +1,7 @@
using Robust.Client.Interfaces.Console;
using Robust.Shared.Log;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using System;
using Robust.Shared.Console;
namespace Robust.Client.Console.Commands
{
@@ -13,12 +13,12 @@ namespace Robust.Client.Console.Commands
+ "\n sawmill: A label prefixing log messages. This is the one you're setting the level for."
+ "\n level: The log level. Must match one of the values of the LogLevel enum.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
console.AddLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
return false;
shell.WriteLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
return;
}
var name = args[0];
@@ -32,13 +32,12 @@ namespace Robust.Client.Console.Commands
{
if (!Enum.TryParse<LogLevel>(levelname, out var result))
{
console.AddLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
return false;
shell.WriteLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
return;
}
level = result;
}
Logger.GetSawmill(name).Level = level;
return false;
}
}
@@ -51,12 +50,12 @@ namespace Robust.Client.Console.Commands
+ "\n level: The log level. Must match one of the values of the LogLevel enum."
+ "\n message: The message to be logged. Wrap this in double quotes if you want to use spaces.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 3)
{
console.AddLine("Invalid argument amount. Expected 3 arguments.", Color.Red);
return false;
shell.WriteLine("Invalid argument amount. Expected 3 arguments.", Color.Red);
return;
}
var name = args[0];
@@ -64,13 +63,12 @@ namespace Robust.Client.Console.Commands
var message = args[2]; // yes this doesn't support spaces idgaf.
if (!Enum.TryParse<LogLevel>(levelname, out var result))
{
console.AddLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
return false;
shell.WriteLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
return;
}
var level = result;
Logger.LogS(level, name, message);
return false;
}
}
}

View File

@@ -1,5 +1,5 @@
using Robust.Client.Interfaces.Console;
using System;
using Robust.Shared.Console;
namespace Robust.Client.Console.Commands
{
@@ -9,10 +9,9 @@ namespace Robust.Client.Console.Commands
public string Description => "Kills the game client instantly.";
public string Help => "Kills the game client instantly, leaving no traces. No telling the server goodbye";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
Environment.Exit(0);
return false;
}
}
}

View File

@@ -1,4 +1,4 @@
using Robust.Client.Interfaces.Console;
using Robust.Shared.Console;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
@@ -6,17 +6,15 @@ using Robust.Shared.Maths;
namespace Robust.Client.Console.Commands
{
#if CLIENT_SCRIPTING
internal sealed class ScriptConsoleCommand : IConsoleCommand
internal sealed class ScriptCommand : IConsoleCommand
{
public string Command => "csi";
public string Description => "Opens a C# interactive console.";
public string Help => "csi";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
new ScriptConsoleClient().OpenCentered();
return false;
}
}
@@ -26,33 +24,29 @@ namespace Robust.Client.Console.Commands
public string Description => "Opens a variable watch window.";
public string Help => "watch";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
new WatchWindow().OpenCentered();
return false;
}
}
#endif
internal sealed class ServerScriptConsoleCommand : IConsoleCommand
internal sealed class ServerScriptCommand : IConsoleCommand
{
public string Command => "scsi";
public string Description => "Opens a C# interactive console on the server.";
public string Help => "scsi";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IScriptClient>();
if (!mgr.CanScript)
{
console.AddLine(Loc.GetString("You do not have server side scripting permission."), Color.Red);
return false;
shell.WriteLine(Loc.GetString("You do not have server side scripting permission."), Color.Red);
return;
}
mgr.StartSession();
return false;
}
}
}

View File

@@ -1,4 +1,4 @@
using Robust.Client.Interfaces.Console;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Network;
@@ -11,7 +11,7 @@ namespace Robust.Client.Console.Commands
public string Description => "Sends garbage to the server.";
public string Help => "The server will reply with 'no u'";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
// MsgStringTableEntries is registered as NetMessageAccept.Client so the server will immediately deny it.
// And kick us.
@@ -19,8 +19,6 @@ namespace Robust.Client.Console.Commands
var msg = net.CreateNetMessage<MsgStringTableEntries>();
msg.Entries = new MsgStringTableEntries.Entry[0];
net.ClientSendMessage(msg);
return false;
}
}
}

View File

@@ -1,6 +1,6 @@
using JetBrains.Annotations;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.Input;
using Robust.Shared.Console;
using Robust.Shared.IoC;
namespace Robust.Client.Console.Commands
@@ -12,24 +12,23 @@ namespace Robust.Client.Console.Commands
public string Description => "Sets the active input context.";
public string Help => "setinputcontext <context>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine("Invalid number of arguments!");
return false;
shell.WriteLine("Invalid number of arguments!");
return;
}
var inputMan = IoCManager.Resolve<IInputManager>();
if (!inputMan.Contexts.Exists(args[0]))
{
console.AddLine("Context not found!");
return false;
shell.WriteLine("Context not found!");
return;
}
inputMan.Contexts.SetActiveContext(args[0]);
return false;
}
}
}

View File

@@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using Robust.Client.Interfaces.Console;
namespace Robust.Client.Console
{
public interface IClientConsole : IDisposable
{
/// <summary>
/// Initializes the console into a useable state.
/// </summary>
void Initialize();
/// <summary>
/// Resets the console to a post-initialized state.
/// </summary>
void Reset();
event EventHandler<AddStringArgs> AddString;
event EventHandler<AddFormattedMessageArgs> AddFormatted;
event EventHandler ClearText;
IReadOnlyDictionary<string, IConsoleCommand> Commands { get; }
/// <summary>
/// Parses console commands (verbs).
/// </summary>
/// <param name="text"></param>
void ProcessCommand(string text);
void SendServerCommandRequest();
}
}

View File

@@ -0,0 +1,24 @@
using System;
using Robust.Shared.Console;
using Robust.Shared.Utility;
namespace Robust.Client.Console
{
public interface IClientConsoleHost : IConsoleHost, IDisposable
{
/// <summary>
/// Initializes the console into a useable state.
/// </summary>
void Initialize();
/// <summary>
/// Resets the console to a post-initialized state.
/// </summary>
void Reset();
event EventHandler<AddStringArgs> AddString;
event EventHandler<AddFormattedMessageArgs> AddFormatted;
void AddFormattedLine(FormattedMessage message);
}
}

View File

@@ -50,7 +50,7 @@ namespace Robust.Client
[Dependency] private readonly IUserInterfaceManagerInternal _userInterfaceManager = default!;
[Dependency] private readonly IBaseClient _client = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IClientConsole _console = default!;
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
[Dependency] private readonly ITimerManager _timerManager = default!;
[Dependency] private readonly IClientEntityManager _entityManager = default!;
[Dependency] private readonly IPlacementManager _placementManager = default!;
@@ -167,7 +167,7 @@ namespace Robust.Client
IoCManager.Resolve<INetConfigurationManager>().SetupNetworking();
_serializer.Initialize();
_inputManager.Initialize();
_console.Initialize();
_consoleHost.Initialize();
_prototypeManager.LoadDirectory(new ResourcePath(@"/Prototypes/"));
_prototypeManager.Resync();
_mapManager.Initialize();

View File

@@ -2,11 +2,11 @@
using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Graphics.Overlays;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.GameStates;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.ResourceManagement;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
@@ -172,18 +172,18 @@ namespace Robust.Client.GameStates
public string Help => "net_graph <0|1>";
public string Description => "Toggles the net statistics pannel.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
return false;
shell.WriteLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
return;
}
if (!byte.TryParse(args[0], out var iValue))
{
console.AddLine("Invalid argument: Needs to be 0 or 1.");
return false;
shell.WriteLine("Invalid argument: Needs to be 0 or 1.");
return;
}
var bValue = iValue > 0;
@@ -192,15 +192,13 @@ namespace Robust.Client.GameStates
if(bValue && !overlayMan.HasOverlay(nameof(NetGraphOverlay)))
{
overlayMan.AddOverlay(new NetGraphOverlay());
console.AddLine("Enabled network overlay.");
shell.WriteLine("Enabled network overlay.");
}
else if(overlayMan.HasOverlay(nameof(NetGraphOverlay)))
{
overlayMan.RemoveOverlay(nameof(NetGraphOverlay));
console.AddLine("Disabled network overlay.");
shell.WriteLine("Disabled network overlay.");
}
return false;
}
}
}

View File

@@ -1,9 +1,9 @@
using Robust.Client.Graphics.Drawing;
using Robust.Client.Graphics.Overlays;
using Robust.Client.Graphics.Shaders;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Shared.Console;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Timing;
@@ -73,18 +73,18 @@ namespace Robust.Client.GameStates
public string Help => "net_draw_interp <0|1>";
public string Description => "Toggles the debug drawing of the network interpolation.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
return false;
shell.WriteLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
return;
}
if (!byte.TryParse(args[0], out var iValue))
{
console.AddLine("Invalid argument: Needs to be 0 or 1.");
return false;
shell.WriteLine("Invalid argument: Needs to be 0 or 1.");
return;
}
var bValue = iValue > 0;
@@ -93,15 +93,13 @@ namespace Robust.Client.GameStates
if (bValue && !overlayMan.HasOverlay(nameof(NetInterpOverlay)))
{
overlayMan.AddOverlay(new NetInterpOverlay());
console.AddLine("Enabled network interp overlay.");
shell.WriteLine("Enabled network interp overlay.");
}
else if (overlayMan.HasOverlay(nameof(NetInterpOverlay)))
{
overlayMan.RemoveOverlay(nameof(NetInterpOverlay));
console.AddLine("Disabled network interp overlay.");
shell.WriteLine("Disabled network interp overlay.");
}
return false;
}
}
}

View File

@@ -7,9 +7,9 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using JetBrains.Annotations;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.UserInterface;
using Robust.Shared.Console;
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.Interfaces.Reflection;
@@ -812,34 +812,34 @@ namespace Robust.Client.Input
public string Description => "Binds an input key to an input command.";
public string Help => "bind <KeyName> <BindMode> <InputCommand>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 3)
{
console.AddLine("Too few arguments.");
return false;
shell.WriteLine("Too few arguments.");
return;
}
if (args.Length > 3)
{
console.AddLine("Too many arguments.");
return false;
shell.WriteLine("Too many arguments.");
return;
}
var keyName = args[0];
if (!Enum.TryParse(typeof(Key), keyName, true, out var keyIdObj))
{
console.AddLine($"Key '{keyName}' is unrecognized.");
return false;
shell.WriteLine($"Key '{keyName}' is unrecognized.");
return;
}
var keyId = (Key) keyIdObj!;
if (!Enum.TryParse(typeof(KeyBindingType), args[1], true, out var keyModeObj))
{
console.AddLine($"BindMode '{args[1]}' is unrecognized.");
return false;
shell.WriteLine($"BindMode '{args[1]}' is unrecognized.");
return;
}
var keyMode = (KeyBindingType) keyModeObj!;
@@ -856,8 +856,6 @@ namespace Robust.Client.Input
};
inputMan.RegisterBinding(registration);
return false;
}
}
@@ -868,12 +866,10 @@ namespace Robust.Client.Input
public string Description => "";
public string Help => "";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
IoCManager.Resolve<IInputManager>()
.SaveToUserData();
return false;
}
}
}

View File

@@ -1,13 +0,0 @@
using Robust.Shared.Console;
namespace Robust.Client.Interfaces.Console
{
public interface IConsoleCommand : ICommand
{
/// <summary>
/// Executes the command
/// </summary>
/// <returns>Whether or not the command should also be forwarded to the server. True to allow forwarding, false to block.</returns>
bool Execute(IDebugConsole console, params string[] args);
}
}

View File

@@ -1,25 +0,0 @@
using System.Collections.Generic;
using Robust.Shared.Maths;
using Robust.Shared;
using Robust.Shared.Console;
using Robust.Client.Utility;
using Robust.Shared.Utility;
namespace Robust.Client.Interfaces.Console
{
public interface IDebugConsole
{
IReadOnlyDictionary<string, IConsoleCommand> Commands { get; }
/// <summary>
/// Write a line with a specific color to the console window.
/// </summary>
void AddLine(string text, Color color);
void AddLine(string text);
void AddFormattedLine(FormattedMessage message);
void Clear();
}
}

View File

@@ -1,4 +1,4 @@
using Robust.Client.Interfaces.Console;
using Robust.Client.Console;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.Log;
using Robust.Shared.Maths;
@@ -12,9 +12,9 @@ namespace Robust.Client.Log
/// </summary>
class DebugConsoleLogHandler : ILogHandler
{
readonly IDebugConsole Console;
readonly IClientConsoleHost Console;
public DebugConsoleLogHandler(IDebugConsole console)
public DebugConsoleLogHandler(IClientConsoleHost console)
{
Console = console;
}

View File

@@ -5,7 +5,6 @@ using System.IO;
using Newtonsoft.Json;
using Robust.Client.Console;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.Console;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input;
using Robust.Shared.Interfaces.Resources;
@@ -15,15 +14,29 @@ using Robust.Shared.Utility;
namespace Robust.Client.UserInterface.CustomControls
{
public interface IDebugConsoleView
{
/// <summary>
/// Write a line with a specific color to the console window.
/// </summary>
void AddLine(string text, Color color);
void AddLine(string text);
void AddFormattedLine(FormattedMessage message);
void Clear();
}
// Quick note on how thread safety works in here:
// Messages from other threads are not actually immediately drawn. They're stored in a queue.
// Every frame OR the next time a message on the main thread comes in, this queue is drained.
// This keeps thread safety while still making it so messages are ordered how they come in.
// And also if Update() stops firing due to an exception loop the console will still work.
// (At least from the main thread, which is what's throwing the exceptions..)
public class DebugConsole : Control, IDebugConsole
public class DebugConsole : Control, IDebugConsoleView
{
private readonly IClientConsole _console;
private readonly IClientConsoleHost _consoleHost;
private readonly IResourceManager _resourceManager;
private static readonly ResourcePath HistoryPath = new("/debug_console_history.json");
@@ -32,7 +45,6 @@ namespace Robust.Client.UserInterface.CustomControls
private readonly OutputPanel Output;
private readonly Control MainControl;
public IReadOnlyDictionary<string, IConsoleCommand> Commands => _console.Commands;
private readonly ConcurrentQueue<FormattedMessage> _messageQueue = new();
private bool _targetVisible;
@@ -41,9 +53,9 @@ namespace Robust.Client.UserInterface.CustomControls
private readonly List<string> searchResults;
private int searchIndex = 0;
public DebugConsole(IClientConsole console, IResourceManager resMan)
public DebugConsole(IClientConsoleHost consoleHost, IResourceManager resMan)
{
_console = console;
_consoleHost = consoleHost;
_resourceManager = resMan;
Visible = false;
@@ -82,9 +94,9 @@ namespace Robust.Client.UserInterface.CustomControls
CommandBar.OnTextEntered += CommandEntered;
CommandBar.OnHistoryChanged += OnHistoryChanged;
_console.AddString += (_, args) => AddLine(args.Text, args.Color);
_console.AddFormatted += (_, args) => AddFormattedLine(args.Message);
_console.ClearText += (_, args) => Clear();
_consoleHost.AddString += (_, args) => AddLine(args.Text, args.Color);
_consoleHost.AddFormatted += (_, args) => AddFormattedLine(args.Message);
_consoleHost.ClearText += (_, args) => Clear();
_loadHistoryFromDisk();
@@ -141,9 +153,10 @@ namespace Robust.Client.UserInterface.CustomControls
{
if (!string.IsNullOrWhiteSpace(args.Text))
{
_console.ProcessCommand(args.Text);
_consoleHost.ExecuteCommand(args.Text);
CommandBar.Clear();
}
commandChanged = true;
}
@@ -211,7 +224,7 @@ namespace Robust.Client.UserInterface.CustomControls
NextCommand();
args.Handle();
}
else if(args.Function == EngineKeyFunctions.GuiTabNavigatePrev)
else if (args.Function == EngineKeyFunctions.GuiTabNavigatePrev)
{
PrevCommand();
args.Handle();
@@ -229,7 +242,7 @@ namespace Robust.Client.UserInterface.CustomControls
searchResults.Clear();
searchIndex = 0;
commandChanged = false;
foreach (var cmd in Commands)
foreach (var cmd in _consoleHost.RegisteredCommands)
{
if (cmd.Key.StartsWith(CommandBar.Text))
{
@@ -264,7 +277,7 @@ namespace Robust.Client.UserInterface.CustomControls
if (searchResults.Count == 0)
return;
searchIndex = MathHelper.Mod(searchIndex - 1, searchResults.Count);
searchIndex = MathHelper.Mod(searchIndex - 1, searchResults.Count);
SetInput(searchResults[searchIndex]);
return;
}

View File

@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.Text;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.State;
using Robust.Client.Interfaces.UserInterface;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.IoC;
@@ -17,7 +17,7 @@ namespace Robust.Client.UserInterface
public string Help => "scene <className>";
public string Description => "Immediately changes the UI scene/state.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var reflection = IoCManager.Resolve<IReflectionManager>();
var type = reflection.LooseGetType(args[0]);
@@ -25,8 +25,6 @@ namespace Robust.Client.UserInterface
var stateMan = IoCManager.Resolve<IStateManager>();
stateMan.RequestStateChange(type);
return false;
}
}
}

View File

@@ -29,7 +29,7 @@ namespace Robust.Client.UserInterface
{
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IClyde _displayManager = default!;
[Dependency] private readonly IClientConsole _console = default!;
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
[Dependency] private readonly IResourceManager _resourceManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
@@ -98,7 +98,7 @@ namespace Robust.Client.UserInterface
_initializeCommon();
DebugConsole = new DebugConsole(_console, _resourceManager);
DebugConsole = new DebugConsole(_consoleHost, _resourceManager);
RootControl.AddChild(DebugConsole);
_debugMonitors = new DebugMonitors(_gameTiming, _playerManager, _eyeManager, _inputManager, _stateManager,

View File

@@ -1,8 +1,8 @@
using System.Collections;
using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.UserInterface;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Reflection;
@@ -20,7 +20,7 @@ namespace Robust.Client.ViewVariables
public string Description => "Opens View Variables.";
public string Help => "Usage: vv <entity ID|IoC interface name|SIoC interface name>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var vvm = IoCManager.Resolve<IViewVariablesManager>();
// If you don't provide an entity ID, it opens the test class.
@@ -28,7 +28,7 @@ namespace Robust.Client.ViewVariables
if (args.Length == 0)
{
vvm.OpenVV(new VVTest());
return false;
return;
}
var valArg = args[0];
@@ -37,7 +37,7 @@ namespace Robust.Client.ViewVariables
// Server-side IoC selector.
var selector = new ViewVariablesIoCSelector(valArg.Substring(1));
vvm.OpenVV(selector);
return false;
return;
}
if (valArg.StartsWith("I"))
@@ -46,8 +46,8 @@ namespace Robust.Client.ViewVariables
var reflection = IoCManager.Resolve<IReflectionManager>();
if (!reflection.TryLooseGetType(valArg, out var type))
{
console.AddLine("Unable to find that type.");
return false;
shell.WriteLine("Unable to find that type.");
return;
}
object obj;
@@ -57,11 +57,11 @@ namespace Robust.Client.ViewVariables
}
catch (UnregisteredTypeException)
{
console.AddLine("Unable to find that type.");
return false;
shell.WriteLine("Unable to find that type.");
return;
}
vvm.OpenVV(obj);
return false;
return;
}
if (valArg.StartsWith("guihover"))
@@ -70,30 +70,28 @@ namespace Robust.Client.ViewVariables
var obj = IoCManager.Resolve<IUserInterfaceManager>().CurrentlyHovered;
if (obj == null)
{
console.AddLine("Not currently hovering any control.");
return false;
shell.WriteLine("Not currently hovering any control.");
return;
}
vvm.OpenVV(obj);
return false;
return;
}
// Entity.
if (!EntityUid.TryParse(args[0], out var uid))
{
console.AddLine("Invalid specifier format.");
return false;
shell.WriteLine("Invalid specifier format.");
return;
}
var entityManager = IoCManager.Resolve<IEntityManager>();
if (!entityManager.TryGetEntity(uid, out var entity))
{
console.AddLine("That entity does not exist.");
return false;
shell.WriteLine("That entity does not exist.");
return;
}
vvm.OpenVV(entity);
return false;
}
/// <summary>

View File

@@ -6,7 +6,6 @@ using System.Threading;
using Prometheus;
using Robust.Server.Console;
using Robust.Server.Interfaces;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.GameState;
using Robust.Server.Interfaces.Placement;
@@ -309,7 +308,7 @@ namespace Robust.Server
prototypeManager.LoadDirectory(new ResourcePath(@"/Prototypes"));
prototypeManager.Resync();
IoCManager.Resolve<IConsoleShell>().Initialize();
IoCManager.Resolve<IServerConsoleHost>().Initialize();
_entities.Startup();
_scriptHost.Initialize();

View File

@@ -1,6 +1,6 @@
using JetBrains.Annotations;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
@@ -8,17 +8,17 @@ using Robust.Shared.IoC;
namespace Robust.Server.Console.Commands
{
[UsedImplicitly]
internal sealed class AddCompCommand : IClientCommand
internal sealed class AddCompCommand : IConsoleCommand
{
public string Command => "addcomp";
public string Description => "Adds a component to an entity";
public string Help => "addcomp <uid> <componentName>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.SendText(player, "Wrong number of arguments");
shell.WriteLine("Wrong number of arguments");
return;
}
@@ -39,16 +39,16 @@ namespace Robust.Server.Console.Commands
}
[UsedImplicitly]
internal sealed class RemoveCompCommand : IClientCommand
internal sealed class RemoveCompCommand : IConsoleCommand
{
public string Command => "rmcomp";
public string Description => "Removes a component from an entity.";
public string Help => "rmcomp <uid> <componentName>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.SendText(player, "Wrong number of arguments");
shell.WriteLine("Wrong number of arguments");
return;
}

View File

@@ -1,21 +1,21 @@
using System;
using JetBrains.Annotations;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.IoC;
namespace Robust.Server.Console.Commands
{
[UsedImplicitly]
internal sealed class CVarCommand : SharedCVarCommand, IClientCommand
internal sealed class CVarCommand : SharedCVarCommand, IConsoleCommand
{
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1 || args.Length > 2)
{
shell.SendText(player, "Must provide exactly one or two arguments.");
shell.WriteLine("Must provide exactly one or two arguments.");
return;
}
@@ -25,13 +25,13 @@ namespace Robust.Server.Console.Commands
if (name == "?")
{
var cvars = configManager.GetRegisteredCVars();
shell.SendText(player, string.Join("\n", cvars));
shell.WriteLine(string.Join("\n", cvars));
return;
}
if (!configManager.IsCVarRegistered(name))
{
shell.SendText(player, $"CVar '{name}' is not registered. Use 'cvar ?' to get a list of all registered CVars.");
shell.WriteLine($"CVar '{name}' is not registered. Use 'cvar ?' to get a list of all registered CVars.");
return;
}
@@ -39,7 +39,7 @@ namespace Robust.Server.Console.Commands
{
// Read CVar
var value = configManager.GetCVar<object>(name);
shell.SendText(player, value.ToString());
shell.WriteLine(value.ToString()!);
}
else
{
@@ -53,7 +53,7 @@ namespace Robust.Server.Console.Commands
}
catch (FormatException)
{
shell.SendText(player, $"Input value is in incorrect format for type {type}");
shell.WriteLine($"Input value is in incorrect format for type {type}");
}
}
}

View File

@@ -1,22 +1,22 @@
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Robust.Server.Console.Commands
{
public sealed class DeleteCommand : IClientCommand
public sealed class DeleteCommand : IConsoleCommand
{
public string Command => "delete";
public string Description => "Deletes the entity with the specified ID.";
public string Help => "delete <entity UID>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.SendText(player, "You should provide exactly one argument.");
shell.WriteLine("You should provide exactly one argument.");
return;
}
@@ -24,13 +24,13 @@ namespace Robust.Server.Console.Commands
if (!EntityUid.TryParse(args[0], out var uid))
{
shell.SendText(player, "Invalid entity UID.");
shell.WriteLine("Invalid entity UID.");
return;
}
if (!ent.TryGetEntity(uid, out var entity))
{
shell.SendText(player, "That entity does not exist.");
shell.WriteLine("That entity does not exist.");
return;
}

View File

@@ -1,19 +1,19 @@
using System.Runtime.Loader;
using System.Text;
using JetBrains.Annotations;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
namespace Robust.Server.Console.Commands
{
[UsedImplicitly]
internal class ListAssembliesCommand : IClientCommand
internal class ListAssembliesCommand : IConsoleCommand
{
public string Command => "lsasm";
public string Description => "Lists loaded assemblies by load context.";
public string Help => Command;
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var sb = new StringBuilder();
foreach (var context in AssemblyLoadContext.All)
@@ -25,7 +25,7 @@ namespace Robust.Server.Console.Commands
}
}
shell.SendText(player, sb.ToString());
shell.WriteLine(sb.ToString());
}
}
}

View File

@@ -1,11 +1,11 @@
using System.Linq;
using System.Linq;
using System.Text;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
namespace Robust.Server.Console.Commands
{
public class ListCommands : IClientCommand
public class ListCommands : IConsoleCommand
{
public string Command => "list";
@@ -15,7 +15,7 @@ namespace Robust.Server.Console.Commands
public string Help => "Usage: list [filter]";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var filter = "";
if (args.Length == 1)
@@ -24,7 +24,7 @@ namespace Robust.Server.Console.Commands
}
var builder = new StringBuilder("SIDE NAME DESC\n-------------------------\n");
foreach (var command in shell.AvailableCommands.Values
foreach (var command in shell.ConsoleHost.RegisteredCommands.Values
.Where(p => p.Command.Contains(filter))
.OrderBy(c => c.Command))
{
@@ -34,7 +34,7 @@ namespace Robust.Server.Console.Commands
}
var message = builder.ToString().Trim(' ', '\n');
shell.SendText(player, message);
shell.WriteLine(message);
}
}
}

View File

@@ -1,11 +1,11 @@
using System;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
using Robust.Shared.Log;
namespace Robust.Server.Console.Commands
{
class LogSetLevelCommand : IClientCommand
class LogSetLevelCommand : IConsoleCommand
{
public string Command => "loglevel";
public string Description => "Changes the log level for a provided sawmill.";
@@ -13,11 +13,11 @@ namespace Robust.Server.Console.Commands
+ "\n sawmill: A label prefixing log messages. This is the one you're setting the level for."
+ "\n level: The log level. Must match one of the values of the LogLevel enum.";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.SendText(player, "Invalid argument amount. Expected 2 arguments.");
shell.WriteLine("Invalid argument amount. Expected 2 arguments.");
return;
}
@@ -32,7 +32,7 @@ namespace Robust.Server.Console.Commands
{
if (!Enum.TryParse<LogLevel>(levelname, out var result))
{
shell.SendText(player, "Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
shell.WriteLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
return;
}
level = result;
@@ -41,7 +41,7 @@ namespace Robust.Server.Console.Commands
}
}
class TestLog : IClientCommand
class TestLog : IConsoleCommand
{
public string Command => "testlog";
public string Description => "Writes a test log to a sawmill.";
@@ -50,11 +50,11 @@ namespace Robust.Server.Console.Commands
+ "\n level: The log level. Must match one of the values of the LogLevel enum."
+ "\n message: The message to be logged. Wrap this in double quotes if you want to use spaces.";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 3)
{
shell.SendText(player, "Invalid argument amount. Expected exactly 3 arguments.");
shell.WriteLine("Invalid argument amount. Expected exactly 3 arguments.");
return;
}
@@ -63,7 +63,7 @@ namespace Robust.Server.Console.Commands
var message = args[2]; // yes this doesn't support spaces idgaf.
if (!Enum.TryParse<LogLevel>(levelname, out var result))
{
shell.SendText(player, "Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
shell.WriteLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
return;
}
var level = result;

View File

@@ -1,11 +1,11 @@
using System.Globalization;
using System.Globalization;
using System.Linq;
using System.Text;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Maps;
using Robust.Server.Interfaces.Player;
using Robust.Server.Interfaces.Timing;
using Robust.Server.Maps;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
@@ -14,13 +14,13 @@ using Robust.Shared.Maths;
namespace Robust.Server.Console.Commands
{
class AddMapCommand : IClientCommand
class AddMapCommand : IConsoleCommand
{
public string Command => "addmap";
public string Description => "Adds a new empty map to the round. If the mapID already exists, this command does nothing.";
public string Help => "addmap <mapID> [initialize]";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1)
return;
@@ -37,24 +37,24 @@ namespace Robust.Server.Console.Commands
{
pauseMgr.AddUninitializedMap(mapId);
}
shell.SendText(player, $"Map with ID {mapId} created.");
shell.WriteLine($"Map with ID {mapId} created.");
return;
}
shell.SendText(player, $"Map with ID {mapId} already exists!");
shell.WriteLine($"Map with ID {mapId} already exists!");
}
}
class RemoveMapCommand : IClientCommand
class RemoveMapCommand : IConsoleCommand
{
public string Command => "rmmap";
public string Description => "Removes a map from the world. You cannot remove nullspace.";
public string Help => "rmmap <mapId>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.SendText(player, "Wrong number of args.");
shell.WriteLine("Wrong number of args.");
return;
}
@@ -63,32 +63,32 @@ namespace Robust.Server.Console.Commands
if (!mapManager.MapExists(mapId))
{
shell.SendText(player, $"Map {mapId.Value} does not exist.");
shell.WriteLine($"Map {mapId.Value} does not exist.");
return;
}
mapManager.DeleteMap(mapId);
shell.SendText(player, $"Map {mapId.Value} was removed.");
shell.WriteLine($"Map {mapId.Value} was removed.");
}
}
public class SaveBp : IClientCommand
public class SaveBp : IConsoleCommand
{
public string Command => "savebp";
public string Description => "Serializes a grid to disk.";
public string Help => "savebp <gridID> <Path>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 2)
{
shell.SendText(player, "Not enough arguments.");
shell.WriteLine("Not enough arguments.");
return;
}
if (!int.TryParse(args[0], out var intGridId))
{
shell.SendText(player, "Not a valid grid ID.");
shell.WriteLine("Not a valid grid ID.");
return;
}
@@ -99,22 +99,22 @@ namespace Robust.Server.Console.Commands
// no saving default grid
if (!mapManager.TryGetGrid(gridId, out var grid))
{
shell.SendText(player, "That grid does not exist.");
shell.WriteLine("That grid does not exist.");
return;
}
IoCManager.Resolve<IMapLoader>().SaveBlueprint(gridId, args[1]);
shell.SendText(player, "Save successful. Look in the user data directory.");
shell.WriteLine("Save successful. Look in the user data directory.");
}
}
public class LoadBp : IClientCommand
public class LoadBp : IConsoleCommand
{
public string Command => "loadbp";
public string Description => "Loads a blueprint from disk into the game.";
public string Help => "loadbp <MapID> <Path> [storeUids]";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 2)
{
@@ -131,14 +131,14 @@ namespace Robust.Server.Console.Commands
// no loading into null space
if (mapId == MapId.Nullspace)
{
shell.SendText(player, "Cannot load into nullspace.");
shell.WriteLine("Cannot load into nullspace.");
return;
}
var mapManager = IoCManager.Resolve<IMapManager>();
if (!mapManager.MapExists(mapId))
{
shell.SendText(player, "Target map does not exist.");
shell.WriteLine("Target map does not exist.");
return;
}
@@ -153,13 +153,13 @@ namespace Robust.Server.Console.Commands
}
}
public class SaveMap : IClientCommand
public class SaveMap : IConsoleCommand
{
public string Command => "savemap";
public string Description => "Serializes a map to disk.";
public string Help => "savemap <MapID> <Path>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1)
return;
@@ -182,13 +182,13 @@ namespace Robust.Server.Console.Commands
}
}
public class LoadMap : IClientCommand
public class LoadMap : IConsoleCommand
{
public string Command => "loadmap";
public string Description => "Loads a map from disk into the game.";
public string Help => "loadmap <MapID> <Path>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1)
return;
@@ -201,49 +201,49 @@ namespace Robust.Server.Console.Commands
// no loading null space
if (mapID == MapId.Nullspace)
{
shell.SendText(player, "You cannot load into map 0.");
shell.WriteLine("You cannot load into map 0.");
return;
}
var mapManager = IoCManager.Resolve<IMapManager>();
if (mapManager.MapExists(mapID))
{
shell.SendText(player, $"Map {mapID} already exists.");
shell.WriteLine($"Map {mapID} already exists.");
return;
}
// TODO: Parse path
var mapPath = "Maps/Demo/DemoMap.yaml";
IoCManager.Resolve<IMapLoader>().LoadMap(mapID, mapPath);
shell.SendText(player, $"Map {mapID} has been loaded from {mapPath}.");
shell.WriteLine($"Map {mapID} has been loaded from {mapPath}.");
}
}
class LocationCommand : IClientCommand
class LocationCommand : IConsoleCommand
{
public string Command => "loc";
public string Description => "Prints the absolute location of the player's entity to console.";
public string Help => "loc";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if(player?.AttachedEntity == null)
var player = shell.Player as IPlayerSession;
if (player?.AttachedEntity == null)
return;
var pos = player.AttachedEntity.Transform.Coordinates;
var entityManager = IoCManager.Resolve<IEntityManager>();
shell.SendText(player,
$"MapID:{pos.GetMapId(entityManager)} GridID:{pos.GetGridId(entityManager)} X:{pos.X:N2} Y:{pos.Y:N2}");
shell.WriteLine($"MapID:{pos.GetMapId(entityManager)} GridID:{pos.GetGridId(entityManager)} X:{pos.X:N2} Y:{pos.Y:N2}");
}
}
class PauseMapCommand : IClientCommand
class PauseMapCommand : IConsoleCommand
{
public string Command => "pausemap";
public string Description => "Pauses a map, pausing all simulation processing on it.";
public string Help => "Usage: pausemap <map ID>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var arg = args[0];
var mapId = new MapId(int.Parse(arg, CultureInfo.InvariantCulture));
@@ -252,19 +252,19 @@ namespace Robust.Server.Console.Commands
var mapManager = IoCManager.Resolve<IMapManager>();
if (!mapManager.MapExists(mapId))
{
shell.SendText(player, "That map does not exist.");
shell.WriteLine("That map does not exist.");
return;
}
pauseManager.SetMapPaused(mapId, true);
}
}
class UnpauseMapCommand : IClientCommand
class UnpauseMapCommand : IConsoleCommand
{
public string Command => "unpausemap";
public string Description => "unpauses a map, resuming all simulation processing on it.";
public string Help => "Usage: unpausemap <map ID>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var arg = args[0];
var mapId = new MapId(int.Parse(arg, CultureInfo.InvariantCulture));
@@ -273,19 +273,19 @@ namespace Robust.Server.Console.Commands
var mapManager = IoCManager.Resolve<IMapManager>();
if (!mapManager.MapExists(mapId))
{
shell.SendText(player, "That map does not exist.");
shell.WriteLine("That map does not exist.");
return;
}
pauseManager.SetMapPaused(mapId, false);
}
}
class QueryMapPausedCommand : IClientCommand
class QueryMapPausedCommand : IConsoleCommand
{
public string Command => "querymappaused";
public string Description => "Check whether a map is paused or not.";
public string Help => "Usage: querymappaused <map ID>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var arg = args[0];
var mapId = new MapId(int.Parse(arg, CultureInfo.InvariantCulture));
@@ -294,24 +294,24 @@ namespace Robust.Server.Console.Commands
var mapManager = IoCManager.Resolve<IMapManager>();
if (!mapManager.MapExists(mapId))
{
shell.SendText(player, "That map does not exist.");
shell.WriteLine("That map does not exist.");
return;
}
shell.SendText(player, pauseManager.IsMapPaused(mapId).ToString());
shell.WriteLine(pauseManager.IsMapPaused(mapId).ToString());
}
}
class TpGridCommand : IClientCommand
class TpGridCommand : IConsoleCommand
{
public string Command => "tpgrid";
public string Description => "Teleports a grid to a new location.";
public string Help => "tpgrid <gridId> <X> <Y> [<MapId>]";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 3 || args.Length > 4)
{
shell.SendText(player, "Wrong number of args.");
shell.WriteLine("Wrong number of args.");
}
var gridId = new GridId(int.Parse(args[0]));
@@ -327,21 +327,21 @@ namespace Robust.Server.Console.Commands
grid.ParentMapId = mapId;
grid.WorldPosition = new Vector2(xpos, ypos);
shell.SendText(player, "Grid was teleported.");
shell.WriteLine("Grid was teleported.");
}
}
}
class RemoveGridCommand : IClientCommand
class RemoveGridCommand : IConsoleCommand
{
public string Command => "rmgrid";
public string Description => "Removes a grid from a map. You cannot remove the default grid.";
public string Help => "rmgrid <gridId>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.SendText(player, "Wrong number of args.");
shell.WriteLine("Wrong number of args.");
return;
}
@@ -350,26 +350,26 @@ namespace Robust.Server.Console.Commands
if (!mapManager.GridExists(gridId))
{
shell.SendText(player, $"Grid {gridId.Value} does not exist.");
shell.WriteLine($"Grid {gridId.Value} does not exist.");
return;
}
mapManager.DeleteGrid(gridId);
shell.SendText(player, $"Grid {gridId.Value} was removed.");
shell.WriteLine($"Grid {gridId.Value} was removed.");
}
}
internal sealed class RunMapInitCommand : IClientCommand
internal sealed class RunMapInitCommand : IConsoleCommand
{
public string Command => "mapinit";
public string Description => "Runs map init on a map";
public string Help => "mapinit <mapID>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.SendText(player, "Wrong number of args.");
shell.WriteLine("Wrong number of args.");
return;
}
@@ -381,13 +381,13 @@ namespace Robust.Server.Console.Commands
if (!mapManager.MapExists(mapId))
{
shell.SendText(player, "Map does not exist!");
shell.WriteLine("Map does not exist!");
return;
}
if (pauseManager.IsMapInitialized(mapId))
{
shell.SendText(player, "Map is already initialized!");
shell.WriteLine("Map is already initialized!");
return;
}
@@ -395,13 +395,13 @@ namespace Robust.Server.Console.Commands
}
}
internal sealed class ListMapsCommand : IClientCommand
internal sealed class ListMapsCommand : IConsoleCommand
{
public string Command => "lsmap";
public string Description => "Lists maps";
public string Help => "lsmap";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mapManager = IoCManager.Resolve<IMapManager>();
var pauseManager = IoCManager.Resolve<IPauseManager>();
@@ -417,17 +417,17 @@ namespace Robust.Server.Console.Commands
mapManager.GetMapEntityId(mapId));
}
shell.SendText(player, msg.ToString());
shell.WriteLine(msg.ToString());
}
}
internal sealed class ListGridsCommand : IClientCommand
internal sealed class ListGridsCommand : IConsoleCommand
{
public string Command => "lsgrid";
public string Description => "List grids";
public string Help => "lsgrid";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mapManager = IoCManager.Resolve<IMapManager>();
@@ -439,7 +439,7 @@ namespace Robust.Server.Console.Commands
grid.Index, grid.ParentMapId, grid.WorldPosition, grid.GridEntityId);
}
shell.SendText(player, msg.ToString());
shell.WriteLine(msg.ToString());
}
}
}

View File

@@ -1,8 +1,8 @@
using System;
using System;
using System.Linq;
using System.Text;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
using Robust.Shared.Enums;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
@@ -14,14 +14,15 @@ using Robust.Shared.Network;
namespace Robust.Server.Console.Commands
{
internal class TeleportCommand : IClientCommand
internal class TeleportCommand : IConsoleCommand
{
public string Command => "tp";
public string Description => "Teleports a player to any location in the round.";
public string Help => "tp <x> <y> [<mapID>]";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var player = shell.Player as IPlayerSession;
if (player?.Status != SessionStatus.InGame || player.AttachedEntity == null)
return;
@@ -58,18 +59,19 @@ namespace Robust.Server.Console.Commands
transform.WorldPosition = position;
}
shell.SendText(player, $"Teleported {player} to {mapId}:{posX},{posY}.");
shell.WriteLine($"Teleported {player} to {mapId}:{posX},{posY}.");
}
}
public class TeleportToPlayerCommand : IClientCommand
public class TeleportToPlayerCommand : IConsoleCommand
{
public string Command => "tpto";
public string Description => "Teleports the current player to the location of another player.";
public string Help => "tpto <username>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var player = shell.Player as IPlayerSession;
if (player?.Status != SessionStatus.InGame || player.AttachedEntity == null)
return;
@@ -89,13 +91,13 @@ namespace Robust.Server.Console.Commands
}
}
public class ListPlayers : IClientCommand
public class ListPlayers : IConsoleCommand
{
public string Command => "listplayers";
public string Description => "Lists all players currently connected";
public string Help => "listplayers";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
// Player: number of people connected and their byond keys
// Admin: read a byond variable which shows their ip, byond version, ckey, attached entity and hardware id
@@ -117,29 +119,29 @@ namespace Robust.Server.Console.Commands
p.Name));
}
shell.SendText(player, sb.ToString());
shell.WriteLine(sb.ToString());
}
}
internal class KickCommand : IClientCommand
internal class KickCommand : IConsoleCommand
{
public string Command => "kick";
public string Description => "Kicks a connected player out of the server, disconnecting them.";
public string Help => "kick <PlayerIndex> [<Reason>]";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var players = IoCManager.Resolve<IPlayerManager>();
if (args.Length < 1)
{
var player = shell.Player as IPlayerSession;
var toKickPlayer = player ?? players.GetAllPlayers().FirstOrDefault();
if (toKickPlayer == null)
{
shell.SendText(player, "You need to provide a player to kick.");
shell.WriteLine("You need to provide a player to kick.");
return;
}
shell.SendText(player,
$"You need to provide a player to kick. Try running 'kick {toKickPlayer?.Name}' as an example.");
shell.WriteLine($"You need to provide a player to kick. Try running 'kick {toKickPlayer?.Name}' as an example.");
return;
}

View File

@@ -1,19 +1,20 @@
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.IoC;
namespace Robust.Server.Console.Commands
{
public class SpawnCommand : IClientCommand
public class SpawnCommand : IConsoleCommand
{
public string Command => "spawn";
public string Description => "Spawns an entity with specific type at your feet.";
public string Help => "spawn <entity type>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var player = shell.Player as IPlayerSession;
var ent = IoCManager.Resolve<IServerEntityManager>();
if (player?.AttachedEntity != null)
{

View File

@@ -1,9 +1,9 @@
using System;
using System;
using System.Runtime;
using System.Text;
using Robust.Server.Interfaces;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Timing;
@@ -13,49 +13,49 @@ using Robust.Shared.Serialization;
namespace Robust.Server.Console.Commands
{
class RestartCommand : IClientCommand
class RestartCommand : IConsoleCommand
{
public string Command => "restart";
public string Description => "Gracefully restarts the server (not just the round).";
public string Help => "restart";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
IoCManager.Resolve<IBaseServer>().Restart();
}
}
class ShutdownCommand : IClientCommand
class ShutdownCommand : IConsoleCommand
{
public string Command => "shutdown";
public string Description => "Gracefully shuts down the server.";
public string Help => "shutdown";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
IoCManager.Resolve<IBaseServer>().Shutdown(null);
}
}
public class SaveConfig : IClientCommand
public class SaveConfig : IConsoleCommand
{
public string Command => "saveconfig";
public string Description => "Saves the server configuration to the config file";
public string Help => "saveconfig";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
IoCManager.Resolve<IConfigurationManager>().SaveToFile();
}
}
class NetworkAuditCommand : IClientCommand
class NetworkAuditCommand : IConsoleCommand
{
public string Command => "netaudit";
public string Description => "Prints into about NetMsg security.";
public string Help => "netaudit";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var network = (NetManager) IoCManager.Resolve<INetManager>();
@@ -71,11 +71,11 @@ namespace Robust.Server.Console.Commands
sb.AppendLine($"Type: {msgType.Name.PadRight(16)} Call:{call.Target}");
}
shell.SendText(player, sb.ToString());
shell.WriteLine(sb.ToString());
}
}
class HelpCommand : IClientCommand
class HelpCommand : IConsoleCommand
{
public string Command => "help";
@@ -84,54 +84,52 @@ namespace Robust.Server.Console.Commands
public string Help => "Help";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
switch (args.Length)
{
case 0:
shell.SendText(player,
"To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'.");
shell.WriteLine("To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'.");
break;
case 1:
var commandName = args[0];
if (!shell.AvailableCommands.TryGetValue(commandName, out var cmd))
if (!shell.ConsoleHost.RegisteredCommands.TryGetValue(commandName, out var cmd))
{
shell.SendText(player, $"Unknown command: {commandName}");
shell.WriteLine($"Unknown command: {commandName}");
return;
}
shell.SendText(player, $"Use: {cmd.Help}\n{cmd.Description}");
shell.WriteLine($"Use: {cmd.Help}\n{cmd.Description}");
break;
default:
shell.SendText(player, "Invalid amount of arguments.");
shell.WriteLine("Invalid amount of arguments.");
break;
}
}
}
class ShowTimeCommand : IClientCommand
class ShowTimeCommand : IConsoleCommand
{
public string Command => "showtime";
public string Description => "Shows the server time.";
public string Help => "showtime";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var timing = IoCManager.Resolve<IGameTiming>();
shell.SendText(player,
$"Paused: {timing.Paused}, CurTick: {timing.CurTick}, CurTime: {timing.CurTime}, RealTime: {timing.RealTime}");
shell.WriteLine($"Paused: {timing.Paused}, CurTick: {timing.CurTick}, CurTime: {timing.CurTime}, RealTime: {timing.RealTime}");
}
}
internal class GcCommand : IClientCommand
internal class GcCommand : IConsoleCommand
{
public string Command => "gc";
public string Description => "Run the GC.";
public string Help => "gc [generation]";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length == 0)
{
@@ -142,12 +140,12 @@ namespace Robust.Server.Console.Commands
if(int.TryParse(args[0], out int result))
GC.Collect(result);
else
shell.SendText(player, "Failed to parse argument.");
shell.WriteLine("Failed to parse argument.");
}
}
}
internal class GcModeCommand : IClientCommand
internal class GcModeCommand : IConsoleCommand
{
public string Command => "gc_mode";
@@ -156,16 +154,16 @@ namespace Robust.Server.Console.Commands
public string Help => "gc_mode [type]";
public void Execute(IConsoleShell console, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell console, string argStr, string[] args)
{
var prevMode = GCSettings.LatencyMode;
if (args.Length == 0)
{
console.SendText(player,$"current gc latency mode: {(int) prevMode} ({prevMode})");
console.SendText(player,"possible modes:");
console.WriteLine($"current gc latency mode: {(int) prevMode} ({prevMode})");
console.WriteLine("possible modes:");
foreach (var mode in (int[]) Enum.GetValues(typeof(GCLatencyMode)))
{
console.SendText(player,$" {mode}: {Enum.GetName(typeof(GCLatencyMode), mode)}");
console.WriteLine($" {mode}: {Enum.GetName(typeof(GCLatencyMode), mode)}");
}
}
else
@@ -177,13 +175,13 @@ namespace Robust.Server.Console.Commands
}
else if (!Enum.TryParse(args[0], true, out mode))
{
console.SendText(player,$"unknown gc latency mode: {args[0]}");
console.WriteLine($"unknown gc latency mode: {args[0]}");
return;
}
console.SendText(player,$"attempting gc latency mode change: {(int) prevMode} ({prevMode}) -> {(int) mode} ({mode})");
console.WriteLine($"attempting gc latency mode change: {(int) prevMode} ({prevMode}) -> {(int) mode} ({mode})");
GCSettings.LatencyMode = mode;
console.SendText(player,$"resulting gc latency mode: {(int) GCSettings.LatencyMode} ({GCSettings.LatencyMode})");
console.WriteLine($"resulting gc latency mode: {(int) GCSettings.LatencyMode} ({GCSettings.LatencyMode})");
}
return;
@@ -191,7 +189,7 @@ namespace Robust.Server.Console.Commands
}
internal class SerializeStatsCommand : IClientCommand
internal class SerializeStatsCommand : IConsoleCommand
{
public string Command => "szr_stats";
@@ -200,31 +198,31 @@ namespace Robust.Server.Console.Commands
public string Help => "szr_stats";
public void Execute(IConsoleShell console, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell console, string argStr, string[] args)
{
console.SendText(player,$"serialized: {RobustSerializer.BytesSerialized} bytes, {RobustSerializer.ObjectsSerialized} objects");
console.SendText(player,$"largest serialized: {RobustSerializer.LargestObjectSerializedBytes} bytes, {RobustSerializer.LargestObjectSerializedType} objects");
console.SendText(player,$"deserialized: {RobustSerializer.BytesDeserialized} bytes, {RobustSerializer.ObjectsDeserialized} objects");
console.SendText(player,$"largest serialized: {RobustSerializer.LargestObjectDeserializedBytes} bytes, {RobustSerializer.LargestObjectDeserializedType} objects");
console.WriteLine($"serialized: {RobustSerializer.BytesSerialized} bytes, {RobustSerializer.ObjectsSerialized} objects");
console.WriteLine($"largest serialized: {RobustSerializer.LargestObjectSerializedBytes} bytes, {RobustSerializer.LargestObjectSerializedType} objects");
console.WriteLine($"deserialized: {RobustSerializer.BytesDeserialized} bytes, {RobustSerializer.ObjectsDeserialized} objects");
console.WriteLine($"largest serialized: {RobustSerializer.LargestObjectDeserializedBytes} bytes, {RobustSerializer.LargestObjectDeserializedType} objects");
}
}
internal sealed class MemCommand : IClientCommand
internal sealed class MemCommand : IConsoleCommand
{
public string Command => "mem";
public string Description => "prints memory info";
public string Help => "mem";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
#if !NETCOREAPP
shell.SendText(player, "Memory info is only available on .NET Core");
#else
var info = GC.GetGCMemoryInfo();
shell.SendText(player, $@"Heap Size: {FormatBytes(info.HeapSizeBytes)} Total Allocated: {FormatBytes(GC.GetTotalMemory(false))}");
shell.WriteLine($@"Heap Size: {FormatBytes(info.HeapSizeBytes)} Total Allocated: {FormatBytes(GC.GetTotalMemory(false))}");
#endif
}

View File

@@ -1,179 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.IoC;
using Robust.Shared.IoC.Exceptions;
using Robust.Shared.Network.Messages;
using Robust.Shared.Utility;
namespace Robust.Server.Console
{
/// <inheritdoc />
internal class ConsoleShell : IConsoleShell
{
private const string SawmillName = "con";
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
[Dependency] private readonly IPlayerManager _players = default!;
[Dependency] private readonly IServerNetManager _net = default!;
[Dependency] private readonly ISystemConsoleManager _systemConsole = default!;
[Dependency] private readonly ILogManager _logMan = default!;
[Dependency] private readonly IConGroupController _groupController = default!;
private readonly Dictionary<string, IClientCommand> _availableCommands =
new Dictionary<string, IClientCommand>();
/// <inheritdoc />
public IReadOnlyDictionary<string, IClientCommand> AvailableCommands => _availableCommands;
private void HandleRegistrationRequest(INetChannel senderConnection)
{
var netMgr = IoCManager.Resolve<IServerNetManager>();
var message = netMgr.CreateNetMessage<MsgConCmdReg>();
var counter = 0;
message.Commands = new MsgConCmdReg.Command[AvailableCommands.Count];
foreach (var command in AvailableCommands.Values)
{
message.Commands[counter++] = new MsgConCmdReg.Command
{
Name = command.Command,
Description = command.Description,
Help = command.Help
};
}
netMgr.ServerSendMessage(message, senderConnection);
}
/// <inheritdoc />
public void Initialize()
{
ReloadCommands();
// setup networking with clients
_net.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME, ProcessCommand);
_net.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME);
_net.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME,
message => HandleRegistrationRequest(message.MsgChannel));
}
/// <inheritdoc />
public void ReloadCommands()
{
// search for all client commands in all assemblies, and register them
_availableCommands.Clear();
foreach (var type in _reflectionManager.GetAllChildren<IClientCommand>())
{
var instance = (IClientCommand) Activator.CreateInstance(type, null)!;
if (AvailableCommands.TryGetValue(instance.Command, out var duplicate))
throw new InvalidImplementationException(instance.GetType(), typeof(IClientCommand),
$"Command name already registered: {instance.Command}, previous: {duplicate.GetType()}");
_availableCommands[instance.Command] = instance;
}
}
private void ProcessCommand(MsgConCmd message)
{
var text = message.Text;
var sender = message.MsgChannel;
var session = _players.GetSessionByChannel(sender);
_logMan.GetSawmill(SawmillName).Info($"{FormatPlayerString(session)}:{text}");
ExecuteCommand(session, text);
}
/// <inheritdoc />
public void ExecuteCommand(string command)
{
ExecuteCommand(null, command);
}
/// <inheritdoc />
public void ExecuteCommand(IPlayerSession? session, string command)
{
try
{
var args = new List<string>();
CommandParsing.ParseArguments(command, args);
// missing cmdName
if (args.Count == 0)
return;
var cmdName = args[0];
if (_availableCommands.TryGetValue(cmdName, out var conCmd)) // command registered
{
if (session != null) // remote client
{
if (_groupController.CanCommand(session, cmdName)) // client has permission
{
args.RemoveAt(0);
conCmd.Execute(this, session, args.ToArray());
}
else
SendText(session, $"Unknown command: '{cmdName}'");
}
else // system console
{
args.RemoveAt(0);
conCmd.Execute(this, null, args.ToArray());
}
}
else
SendText(session, $"Unknown command: '{cmdName}'");
}
catch (Exception e)
{
_logMan.GetSawmill(SawmillName).Warning($"{FormatPlayerString(session)}: ExecuteError - {command}:\n{e}");
SendText(session, $"There was an error while executing the command: {e}");
}
}
/// <inheritdoc />
public void SendText(IPlayerSession? session, string? text)
{
if (session != null)
SendText(session.ConnectedClient, text);
else
_systemConsole.Print(text + "\n");
}
/// <inheritdoc />
public void SendText(INetChannel target, string? text)
{
var replyMsg = _net.CreateNetMessage<MsgConCmdAck>();
replyMsg.Text = text;
_net.ServerSendMessage(replyMsg, target);
}
private static string FormatPlayerString(IPlayerSession? session)
{
return session != null ? $"{session.Name}" : "[HOST]";
}
private class SudoCommand : IClientCommand
{
public string Command => "sudo";
public string Description => "sudo make me a sandwich";
public string Help => "sudo";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
{
var command = args[0];
var cArgs = args[1..].Select(CommandParsing.Escape);
shell.ExecuteCommand($"{command} {string.Join(' ', cArgs)}");
}
}
}
}

View File

@@ -0,0 +1,15 @@
using Robust.Shared.Console;
namespace Robust.Server.Console
{
/// <summary>
/// The server console shell that executes commands.
/// </summary>
public interface IServerConsoleHost : IConsoleHost
{
/// <summary>
/// Initializes the ConsoleShell service.
/// </summary>
void Initialize();
}
}

View File

@@ -1,4 +1,4 @@
namespace Robust.Server.Interfaces.Console
namespace Robust.Server.Console
{
/// <summary>
/// Wraps the system console.

View File

@@ -0,0 +1,169 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Network.Messages;
using Robust.Shared.Players;
using Robust.Shared.Utility;
namespace Robust.Server.Console
{
/// <inheritdoc cref="IServerConsoleHost" />
internal class ServerConsoleHost : ConsoleHost, IServerConsoleHost
{
[Dependency] private readonly IPlayerManager _players = default!;
[Dependency] private readonly ISystemConsoleManager _systemConsole = default!;
[Dependency] private readonly IConGroupController _groupController = default!;
/// <inheritdoc />
public override void ExecuteCommand(ICommonSession? session, string command)
{
var svSession = session as IPlayerSession;
try
{
var args = new List<string>();
CommandParsing.ParseArguments(command, args);
// missing cmdName
if (args.Count == 0)
return;
var cmdName = args[0];
if (AvailableCommands.TryGetValue(cmdName, out var conCmd)) // command registered
{
if (svSession != null) // remote client
{
if (_groupController.CanCommand(svSession, cmdName)) // client has permission
{
args.RemoveAt(0);
conCmd.Execute(new ConsoleShell(this, session), command, args.ToArray());
}
else
SendText(svSession, $"Unknown command: '{cmdName}'");
}
else // system console
{
args.RemoveAt(0);
conCmd.Execute(new ConsoleShell(this, null), command, args.ToArray());
}
}
else
SendText(svSession, $"Unknown command: '{cmdName}'");
}
catch (Exception e)
{
LogManager.GetSawmill(SawmillName).Warning($"{FormatPlayerString(svSession)}: ExecuteError - {command}:\n{e}");
SendText(svSession, $"There was an error while executing the command: {e}");
}
}
public override void RemoteExecuteCommand(ICommonSession? session, string command)
{
//TODO: Server -> Client remote execute, just like how the client forwards the command
}
public override void WriteLine(ICommonSession? session, string text)
{
if (session is IPlayerSession playerSession)
SendText(playerSession, text);
else
SendText(null as IPlayerSession, text);
}
public override void WriteLine(ICommonSession? session, string text, Color color)
{
//TODO: Make colors work.
WriteLine(session, text);
}
/// <inheritdoc />
public void Initialize()
{
RegisterCommand("sudo", "sudo make me a sandwich", "sudo <command>", (shell, _, args) =>
{
var command = args[0];
var cArgs = args[1..].Select(CommandParsing.Escape);
var localShell = shell.ConsoleHost.LocalShell;
localShell.ExecuteCommand($"{command} {string.Join(' ', cArgs)}");
});
LoadConsoleCommands();
// setup networking with clients
NetManager.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME, ProcessCommand);
NetManager.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME);
NetManager.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME,
message => HandleRegistrationRequest(message.MsgChannel));
}
private void HandleRegistrationRequest(INetChannel senderConnection)
{
var netMgr = IoCManager.Resolve<IServerNetManager>();
var message = netMgr.CreateNetMessage<MsgConCmdReg>();
var counter = 0;
message.Commands = new MsgConCmdReg.Command[RegisteredCommands.Count];
foreach (var command in RegisteredCommands.Values)
{
message.Commands[counter++] = new MsgConCmdReg.Command
{
Name = command.Command,
Description = command.Description,
Help = command.Help
};
}
netMgr.ServerSendMessage(message, senderConnection);
}
private void ProcessCommand(MsgConCmd message)
{
var text = message.Text;
var sender = message.MsgChannel;
var session = _players.GetSessionByChannel(sender);
LogManager.GetSawmill(SawmillName).Info($"{FormatPlayerString(session)}:{text}");
ExecuteCommand(session, text);
}
/// <summary>
/// Sends a text string to the remote player.
/// </summary>
/// <param name="session">
/// Remote player to send the text message to. If this is null, the text is sent to the local
/// console.
/// </param>
/// <param name="text">Text message to send.</param>
private void SendText(IPlayerSession? session, string text)
{
if (session != null)
SendText(session.ConnectedClient, text);
else
_systemConsole.Print(text + "\n");
}
/// <summary>
/// Sends a text string to the remote console.
/// </summary>
/// <param name="target">Net channel to send the text string to.</param>
/// <param name="text">Text message to send.</param>
private void SendText(INetChannel target, string text)
{
var replyMsg = NetManager.CreateNetMessage<MsgConCmdAck>();
replyMsg.Text = text;
NetManager.ServerSendMessage(replyMsg, target);
}
private static string FormatPlayerString(IBaseSession? session)
{
return session != null ? $"{session.Name}" : "[HOST]";
}
}
}

View File

@@ -1,8 +1,7 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Server.Interfaces;
using Robust.Server.Interfaces.Console;
using Robust.Shared.Asynchronous;
using Robust.Shared.IoC;
using Con = System.Console;
@@ -11,7 +10,7 @@ namespace Robust.Server.Console
{
internal sealed class SystemConsoleManager : ISystemConsoleManager, IPostInjectInit, IDisposable
{
[Dependency] private readonly IConsoleShell _conShell = default!;
[Dependency] private readonly IServerConsoleHost _conShell = default!;
[Dependency] private readonly ITaskManager _taskManager = default!;
[Dependency] private readonly IBaseServer _baseServer = default!;
@@ -66,7 +65,7 @@ namespace Robust.Server.Console
Con.WriteLine("> " + currentBuffer);
commandHistory.Add(commandHistory.Count, currentBuffer);
historyIndex = commandHistory.Count;
ExecuteCommand(currentBuffer);
_conShell.ExecuteCommand(currentBuffer);
currentBuffer = "";
internalCursor = 0;
break;
@@ -146,11 +145,6 @@ namespace Robust.Server.Console
}
}
private void ExecuteCommand(string commandLine)
{
_conShell.ExecuteCommand(commandLine);
}
public void Print(string text)
{
Con.Write(text);
@@ -181,7 +175,7 @@ namespace Robust.Server.Console
if (tabCompleteList.Count == 0)
{
tabCompleteList = _conShell.AvailableCommands.Keys.Where(key => key.StartsWith(currentBuffer)).ToList();
tabCompleteList = _conShell.RegisteredCommands.Keys.Where(key => key.StartsWith(currentBuffer)).ToList();
if (tabCompleteList.Count == 0)
{
return String.Empty;

View File

@@ -1,5 +1,3 @@
using Robust.Server.Interfaces.Console;
namespace Robust.Server.Console
{
internal sealed class SystemConsoleManagerDummy : ISystemConsoleManager

View File

@@ -1,19 +0,0 @@
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
namespace Robust.Server.Interfaces.Console
{
/// <summary>
/// A command, executed from the debug console of a client.
/// </summary>
public interface IClientCommand : ICommand
{
/// <summary>
/// Executes the client command.
/// </summary>
/// <param name="shell">The console that executed this command.</param>
/// <param name="player">The player that ran this command. This is null if the command was ran by the server console.</param>
/// <param name="args">An array of all the parsed arguments.</param>
void Execute(IConsoleShell shell, IPlayerSession? player, string[] args);
}
}

View File

@@ -1,55 +0,0 @@
using System.Collections.Generic;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Interfaces.Network;
namespace Robust.Server.Interfaces.Console
{
/// <summary>
/// The server console shell that executes commands.
/// </summary>
public interface IConsoleShell
{
/// <summary>
/// A map of (commandName -> ICommand) of every registered command in the shell.
/// </summary>
IReadOnlyDictionary<string, IClientCommand> AvailableCommands { get; }
/// <summary>
/// Initializes the ConsoleShell service.
/// </summary>
void Initialize();
/// <summary>
/// Scans all loaded assemblies for console commands and registers them. This will NOT sync with connected clients, and
/// should only be used during server initialization.
/// </summary>
void ReloadCommands();
/// <summary>
/// Sends a text string to the remote player.
/// </summary>
/// <param name="session">Remote player to send the text message to. If this is null, the text is sent to the local console.</param>
/// <param name="text">Text message to send.</param>
void SendText(IPlayerSession? session, string? text);
/// <summary>
/// Sends a text string to the remote console.
/// </summary>
/// <param name="target">Net channel to send the text string to.</param>
/// <param name="text">Text message to send.</param>
void SendText(INetChannel target, string? text);
/// <summary>
/// Execute a command string on the local shell.
/// </summary>
/// <param name="command">Command string to execute.</param>
void ExecuteCommand(string command);
/// <summary>
/// Execute a command string as a player.
/// </summary>
/// <param name="player">Session of the remote player. If this is null, the command is executed as the local console.</param>
/// <param name="command">Command string to execute.</param>
void ExecuteCommand(IPlayerSession? player, string command);
}
}

View File

@@ -1,10 +1,9 @@
using Robust.Server.Console;
using Robust.Server.Console;
using Robust.Server.DataMetrics;
using Robust.Server.Debugging;
using Robust.Server.GameObjects;
using Robust.Server.GameStates;
using Robust.Server.Interfaces;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Debugging;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.GameState;
@@ -50,7 +49,7 @@ namespace Robust.Server
IoCManager.Register<IBaseServerInternal, BaseServer>();
IoCManager.Register<IComponentFactory, ServerComponentFactory>();
IoCManager.Register<IConGroupController, ConGroupController>();
IoCManager.Register<IConsoleShell, ConsoleShell>();
IoCManager.Register<IServerConsoleHost, ServerConsoleHost>();
IoCManager.Register<IEntityManager, ServerEntityManager>();
IoCManager.Register<IEntityNetworkManager, ServerEntityNetworkManager>();
IoCManager.Register<IServerEntityNetworkManager, ServerEntityNetworkManager>();

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using Robust.Shared.Console;
@@ -6,7 +6,7 @@ using Robust.Shared.Console;
namespace Robust.Shared.Configuration
{
[SuppressMessage("ReSharper", "StringLiteralTypo")]
internal abstract class SharedCVarCommand : ICommand
internal abstract class SharedCVarCommand : IConsoleCommand
{
public string Command => "cvar";
public string Description => "Gets or sets a CVar.";
@@ -16,6 +16,8 @@ If a value is passed, the value is parsed and stored as the new value of the CVa
If not, the current value of the CVar is displayed.
Use 'cvar ?' to get a list of all registered CVars.";
public abstract void Execute(IConsoleShell shell, string argStr, string[] args);
protected static object ParseObject(Type type, string input)
{
if (type == typeof(bool))

View File

@@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
using Robust.Shared.Enums;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.IoC;
using Robust.Shared.IoC.Exceptions;
using Robust.Shared.Maths;
using Robust.Shared.Players;
using Robust.Shared.Reflection;
namespace Robust.Shared.Console
{
/// <inheritdoc />
public abstract class ConsoleHost : IConsoleHost
{
protected const string SawmillName = "con";
[Dependency] protected readonly ILogManager LogManager = default!;
[Dependency] protected readonly IReflectionManager ReflectionManager = default!;
[Dependency] protected readonly INetManager NetManager = default!;
protected readonly Dictionary<string, IConsoleCommand> AvailableCommands = new();
/// <inheritdoc />
public bool IsServer => NetManager.IsServer;
/// <inheritdoc />
public IConsoleShell LocalShell { get; }
/// <inheritdoc />
public IReadOnlyDictionary<string, IConsoleCommand> RegisteredCommands => AvailableCommands;
protected ConsoleHost()
{
LocalShell = new ConsoleShell(this, null);
}
/// <inheritdoc />
public event EventHandler? ClearText;
/// <inheritdoc />
public void LoadConsoleCommands()
{
// search for all client commands in all assemblies, and register them
foreach (var type in ReflectionManager.GetAllChildren<IConsoleCommand>())
{
var instance = (IConsoleCommand) Activator.CreateInstance(type, null)!;
if (RegisteredCommands.TryGetValue(instance.Command, out var duplicate))
{
throw new InvalidImplementationException(instance.GetType(), typeof(IConsoleCommand),
$"Command name already registered: {instance.Command}, previous: {duplicate.GetType()}");
}
AvailableCommands[instance.Command] = instance;
}
}
/// <inheritdoc />
public void RegisterCommand(string command, string description, string help, ConCommandCallback callback)
{
if (AvailableCommands.ContainsKey(command))
throw new InvalidOperationException($"Command already registered: {command}");
var newCmd = new RegisteredCommand(command, description, help, callback);
AvailableCommands.Add(command, newCmd);
}
//TODO: Pull up
public abstract void ExecuteCommand(ICommonSession? session, string command);
//TODO: server -> client forwarding, making the system asymmetrical
public abstract void RemoteExecuteCommand(ICommonSession? session, string command);
//TODO: IConsoleOutput for [e#1225]
public abstract void WriteLine(ICommonSession? session, string text);
public abstract void WriteLine(ICommonSession? session, string text, Color color);
/// <inheritdoc />
public void ClearLocalConsole()
{
ClearText?.Invoke(this, EventArgs.Empty);
}
/// <inheritdoc />
public IConsoleShell GetSessionShell(ICommonSession session)
{
if (!IsServer)
return LocalShell;
if (session.Status >= SessionStatus.Disconnected)
throw new InvalidOperationException("Tried to get the session shell of a disconnected peer.");
return new ConsoleShell(this, session);
}
/// <inheritdoc />
public void ExecuteCommand(string command)
{
ExecuteCommand(null, command);
}
/// <summary>
/// A console command that was registered inline through <see cref="IConsoleHost"/>.
/// </summary>
[Reflect(false)]
private class RegisteredCommand : IConsoleCommand
{
private readonly ConCommandCallback _callback;
/// <inheritdoc />
public string Command { get; }
/// <inheritdoc />
public string Description { get; }
/// <inheritdoc />
public string Help { get; }
/// <summary>
/// Constructs a new instance of <see cref="RegisteredCommand"/>.
/// </summary>
/// <param name="command">Name of the command.</param>
/// <param name="description">Short description of the command.</param>
/// <param name="help">Extended description for the command.</param>
/// <param name="callback">Callback function that is ran when the command is executed.</param>
public RegisteredCommand(string command, string description, string help, ConCommandCallback callback)
{
Command = command;
Description = description;
Help = help;
_callback = callback;
}
/// <inheritdoc />
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
_callback(shell, argStr, args);
}
}
}
}

View File

@@ -0,0 +1,58 @@
using Robust.Shared.Maths;
using Robust.Shared.Players;
namespace Robust.Shared.Console
{
/// <inheritdoc />
public class ConsoleShell : IConsoleShell
{
/// <inheritdoc />
public IConsoleHost ConsoleHost { get; }
/// <inheritdoc />
public bool IsServer => ConsoleHost.IsServer;
/// <inheritdoc />
public ICommonSession? Player { get; }
public ConsoleShell(IConsoleHost host, ICommonSession? session)
{
ConsoleHost = host;
Player = session;
}
/// <inheritdoc />
public void ExecuteCommand(string command)
{
ConsoleHost.ExecuteCommand(Player, command);
}
/// <inheritdoc />
public void RemoteExecuteCommand(string command)
{
ConsoleHost.RemoteExecuteCommand(Player, command);
}
/// <inheritdoc />
public void WriteLine(string text)
{
ConsoleHost.WriteLine(Player, text);
}
/// <inheritdoc />
public void WriteLine(string text, Color color)
{
ConsoleHost.WriteLine(Player, text, color);
}
/// <inheritdoc />
public void Clear()
{
// Only the local shell can clear the console
if (Player is not null)
return;
ConsoleHost.ClearLocalConsole();
}
}
}

View File

@@ -1,34 +0,0 @@
namespace Robust.Shared.Console
{
/// <summary>
/// Basic abstract to handle console commands.
/// Note that there is no Execute() function, this is due to chat & client commands needing a client,
/// While client-side and server console commands don't.
/// </summary>
public interface ICommand
{
/// <summary>
/// Name of the command.
/// </summary>
/// <value>
/// A string as identifier for this command.
/// </value>
string Command { get; }
/// <summary>
/// Short description of the command.
/// </summary>
/// <value>
/// String printed as short summary in the "help" command.
/// </value>
string Description { get; }
/// <summary>
/// Extended description for the command.
/// </summary>
/// <value>
/// String printed as summary when "help Command" is used.
/// </value>
string Help { get; }
}
}

View File

@@ -0,0 +1,41 @@
namespace Robust.Shared.Console
{
/// <summary>
/// Basic interface to handle console commands. Any class implementing this will be
/// registered with the console system through reflection.
/// </summary>
public interface IConsoleCommand
{
/// <summary>
/// Name of the command.
/// </summary>
/// <value>
/// A string as identifier for this command.
/// </value>
string Command { get; }
/// <summary>
/// Short description of the command.
/// </summary>
/// <value>
/// String printed as short summary in the "help" command.
/// </value>
string Description { get; }
/// <summary>
/// Extended description for the command.
/// </summary>
/// <value>
/// String printed as summary when "help Command" is used.
/// </value>
string Help { get; }
/// <summary>
/// Executes the client command.
/// </summary>
/// <param name="shell">The console that executed this command.</param>
/// <param name="argStr">Unparsed text of the complete command with arguments.</param>
/// <param name="args">An array of all the parsed arguments.</param>
void Execute(IConsoleShell shell, string argStr, string[] args);
}
}

View File

@@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using Robust.Shared.Maths;
using Robust.Shared.Players;
namespace Robust.Shared.Console
{
/// <summary>
/// A delegate that is called when the command is executed inside a shell.
/// </summary>
/// <param name="shell">The console shell that executed this command.</param>
/// <param name="argStr">Unparsed text of the complete command with arguments.</param>
/// <param name="args">An array of all the parsed arguments.</param>
public delegate void ConCommandCallback(IConsoleShell shell, string argStr, string[] args);
/// <summary>
/// The console host exists as a singleton subsystem that provides all of the features of the console API.
/// It will register console commands, spawn console shells and execute command strings.
/// </summary>
public interface IConsoleHost
{
/// <summary>
/// Is the shell running on the client?
/// </summary>
bool IsClient => !IsServer;
/// <summary>
/// Is the shell running on the server?
/// </summary>
bool IsServer { get; }
/// <summary>
/// The local shell of the peer that is always available.
/// </summary>
IConsoleShell LocalShell { get; }
/// <summary>
/// A map of (commandName -> ICommand) of every registered command in the shell.
/// </summary>
IReadOnlyDictionary<string, IConsoleCommand> RegisteredCommands { get; }
event EventHandler ClearText;
/// <summary>
/// Scans all loaded assemblies for console commands and registers them. This will NOT sync with connected clients, and
/// should only be used during server initialization.
/// </summary>
void LoadConsoleCommands();
/// <summary>
/// Registers a console command into the console system. This is an alternative to
/// creating an <see cref="IConsoleCommand" /> class.
/// </summary>
/// <param name="command">A string as identifier for this command.</param>
/// <param name="description">Short one sentence description of the command.</param>
/// <param name="help">Command format string.</param>
/// <param name="callback"></param>
void RegisterCommand(string command, string description, string help, ConCommandCallback callback);
/// <summary>
/// Returns the console shell for a given active session.
/// </summary>
/// <remarks>
/// On the client this will always return the local shell, on the server this will return the shell of the active
/// session.
/// </remarks>
/// <param name="session">Session to get the shell of.</param>
/// <returns>Shell of the specified session.</returns>
IConsoleShell GetSessionShell(ICommonSession session);
/// <summary>
/// Execute a command string on the local shell.
/// </summary>
/// <param name="command">Command string to execute.</param>
void ExecuteCommand(string command);
/// <summary>
/// Executes a command string on this specific session shell. If the command does not exist, the command will be forwarded
/// to the
/// remote shell.
/// </summary>
/// <param name="session">Session of the client to execute the command.</param>
/// <param name="command">command line string to execute.</param>
void ExecuteCommand(ICommonSession? session, string command);
/// <summary>
/// Executes the command string on the remote peer. This is mainly used to forward commands from the client to the server.
/// If there is no remote peer (this is a local shell), this function does nothing.
/// </summary>
/// <param name="session">Session of the remote peer to execute the command on.</param>
/// <param name="command">Command line string to execute at the remote endpoint.</param>
void RemoteExecuteCommand(ICommonSession? session, string command);
/// <summary>
/// Sends a text string to the remote session.
/// </summary>
/// <param name="session">
/// Remote session to send the text message to. If this is null, the text is sent to the local
/// console.
/// </param>
/// <param name="text">Text message to send.</param>
void WriteLine(ICommonSession? session, string text);
/// <summary>
/// Sends a foreground colored text string to the remote session.
/// </summary>
/// <param name="session">
/// Remote session to send the text message to. If this is null, the text is sent to the local
/// console.
/// </param>
/// <param name="text">Text message to send.</param>
/// <param name="color">Foreground color of the text.</param>
void WriteLine(ICommonSession? session, string text, Color color);
/// <summary>
/// Removes all text from the local console.
/// </summary>
void ClearLocalConsole();
}
}

View File

@@ -0,0 +1,70 @@
using Robust.Shared.Maths;
using Robust.Shared.Players;
namespace Robust.Shared.Console
{
/// <summary>
/// The console shell that executes commands. Each shell executes commands in the context of a player
/// session, or without a session in a local context.
/// </summary>
public interface IConsoleShell
{
/// <summary>
/// The console host that owns this shell.
/// </summary>
IConsoleHost ConsoleHost { get; }
/// <summary>
/// Is the shell running on the client?
/// </summary>
bool IsClient => !IsServer;
/// <summary>
/// Is the shell running in a local context (no remote peer session)? If true, <see cref="Player" /> will be null.
/// </summary>
bool IsLocal => Player is not null;
/// <summary>
/// Is the shell running on the server?
/// </summary>
bool IsServer { get; }
/// <summary>
/// The remote peer that owns this shell. This is null if the shell is running local (<see cref="IsLocal" /> is true.).
/// </summary>
ICommonSession? Player { get; }
/// <summary>
/// Executes a command string on this specific session shell. If the command does not exist, the command will be forwarded
/// to the
/// remote shell.
/// </summary>
/// <param name="command">command line string to execute.</param>
void ExecuteCommand(string command);
/// <summary>
/// Executes the command string on the remote peer. This is mainly used to forward commands from the client to the server.
/// If there is no remote peer (this is a local shell), this function does nothing.
/// </summary>
/// <param name="command">Command line string to execute at the remote endpoint.</param>
void RemoteExecuteCommand(string command);
/// <summary>
/// Writes a line to the output of the console.
/// </summary>
/// <param name="text">Line of text to write.</param>
void WriteLine(string text);
/// <summary>
/// Write a line with a specific color to the console window.
/// </summary>
/// <param name="text">Line of text to write.</param>
/// <param name="color">Foreground color of the string of text.</param>
void WriteLine(string text, Color color);
/// <summary>
/// Clears the entire console of text.
/// </summary>
void Clear();
}
}

View File

@@ -14,7 +14,6 @@ using Robust.Client.Interfaces;
using Robust.Server;
using Robust.Server.Console;
using Robust.Server.Interfaces;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.ServerStatus;
using Robust.Shared;
using Robust.Shared.ContentPack;