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);
}
}
}
}