using System; using System.Collections.Generic; using Robust.Shared.Enums; using Robust.Shared.IoC; using Robust.Shared.IoC.Exceptions; using Robust.Shared.Log; using Robust.Shared.Network; using Robust.Shared.Players; using Robust.Shared.Reflection; using Robust.Shared.Timing; using Robust.Shared.ViewVariables; namespace Robust.Shared.Console { /// public abstract class ConsoleHost : IConsoleHost { protected const string SawmillName = "con"; [Dependency] protected readonly ILogManager LogManager = default!; [Dependency] private readonly IReflectionManager ReflectionManager = default!; [Dependency] protected readonly INetManager NetManager = default!; [Dependency] private readonly IDynamicTypeFactoryInternal _typeFactory = default!; [Dependency] private readonly IGameTiming _timing = default!; [ViewVariables] protected readonly Dictionary AvailableCommands = new(); private readonly CommandBuffer _commandBuffer = new CommandBuffer(); /// public bool IsServer => NetManager.IsServer; /// public IConsoleShell LocalShell { get; } /// public IReadOnlyDictionary RegisteredCommands => AvailableCommands; public abstract event ConAnyCommandCallback? AnyCommandExecuted; protected ConsoleHost() { LocalShell = new ConsoleShell(this, null); } /// public event EventHandler? ClearText; /// public void LoadConsoleCommands() { // search for all client commands in all assemblies, and register them foreach (var type in ReflectionManager.GetAllChildren()) { var instance = (IConsoleCommand) _typeFactory.CreateInstanceUnchecked(type, true); 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; } } /// 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); } /// public void UnregisterCommand(string command) { if (!AvailableCommands.TryGetValue(command, out var cmd)) throw new KeyNotFoundException($"Command {command} is not registered."); if (cmd is not RegisteredCommand) throw new InvalidOperationException("You cannot unregister commands that have been registered automatically."); AvailableCommands.Remove(command); } //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 WriteError(ICommonSession? session, string text); /// public void ClearLocalConsole() { ClearText?.Invoke(this, EventArgs.Empty); } /// 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); } /// public void ExecuteCommand(string command) { ExecuteCommand(null, command); } /// public void AppendCommand(string command) { _commandBuffer.Append(command); } /// public void InsertCommand(string command) { _commandBuffer.Insert(command); } /// public void CommandBufferExecute() { _commandBuffer.Tick(_timing.TickRate); while (_commandBuffer.TryGetCommand(out var cmd)) { try { ExecuteCommand(cmd); } catch (Exception e) { LocalShell.WriteError(e.Message); } } } /// /// A console command that was registered inline through . /// [Reflect(false)] public sealed class RegisteredCommand : IConsoleCommand { public ConCommandCallback Callback { get; } /// public string Command { get; } /// public string Description { get; } /// public string Help { get; } /// /// Constructs a new instance of . /// /// Name of the command. /// Short description of the command. /// Extended description for the command. /// Callback function that is ran when the command is executed. public RegisteredCommand(string command, string description, string help, ConCommandCallback callback) { Command = command; // Should these two be localized somehow? Description = description; Help = help; Callback = callback; } /// public void Execute(IConsoleShell shell, string argStr, string[] args) { Callback(shell, argStr, args); } } } }