///
/// Handles parsing and execution of console commands, as well as collecting log output.
///
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
public class ConsoleController
{
public static Component MessageReceiver;
private static ConsoleController _instance;
private static Regex _permittedCharacters = new Regex("[^a-zA-Z0-9 -]");
private static Dictionary> _commands = new Dictionary>();
private ConsoleController()
{
}
public static ConsoleController Instance
{
get => _instance ?? (_instance = new ConsoleController());
}
public static void ClearCommands()
{
_commands.Clear();
}
public static void AddCommand(string command, Action action)
{
var lower = command.ToLowerInvariant();
if(!_commands.ContainsKey(lower))
_commands.Add(lower, action);
}
#region Event declarations
// Used to communicate with ConsoleView
public delegate void LogChangedHandler(string[] log);
public event LogChangedHandler LogChanged;
public delegate void VisibilityChangedHandler(bool visible);
public event VisibilityChangedHandler VisibilityChanged;
#endregion
///
/// How many log lines should be retained?
/// Note that strings submitted to appendLogLine with embedded newlines will be counted as a single line.
///
private const int ScrollbackSize = 100;
private readonly Queue _scrollback = new Queue(ScrollbackSize);
private readonly List _commandHistory = new List();
public string[] Log { get; private set; } //Copy of scrollback as an array for easier use by ConsoleView
public void AppendLogLine(string line) {
if (_scrollback.Count >= ScrollbackSize) {
_scrollback.Dequeue();
}
_scrollback.Enqueue(line);
Log = _scrollback.ToArray();
LogChanged?.Invoke(Log);
}
public void RunCommandString(string commandString) {
AppendLogLine($"$ {commandString}");
string[] commandSplit = ParseArguments(commandString);
string[] args = {"",""};
if (commandSplit == null) {
AppendLogLine("Refused: commands take only letters, digits, spaces and hyphens");
return;
}
if (commandSplit.Length < 1) {
AppendLogLine($"Unable to process command '{commandString}'");
return;
}
if (commandSplit.Length >= 2) {
int numArgs = commandSplit.Length - 1;
args = new string[numArgs];
Array.Copy(commandSplit, 1, args, 0, numArgs);
}
var cmd = commandSplit[0].ToLower();
if(_commands.ContainsKey(cmd))
_commands[cmd].Invoke(args);
else AppendLogLine("Invalid command!");
//MessageReceiver.SendMessage(commandSplit[0].ToLower(), args, SendMessageOptions.DontRequireReceiver);
_commandHistory.Add(commandString);
}
static string[] ParseArguments(string commandString)
{
var parmChars = new LinkedList(commandString.ToCharArray());
var inQuote = false;
var node = parmChars.First;
while (node != null)
{
var next = node.Next;
if (node.Value == '"') {
inQuote = !inQuote;
parmChars.Remove(node);
}
if (!inQuote && node.Value == ' ') {
node.Value = '\n';
}
node = next;
}
var parmCharsArr = new char[parmChars.Count];
parmChars.CopyTo(parmCharsArr, 0);
var args = new string(parmCharsArr).Split(new[] {'\n'}, StringSplitOptions.RemoveEmptyEntries);
// An argument with any other character is refused (null), never silently stripped into a different one.
return args.Any(arg => _permittedCharacters.IsMatch(arg)) ? null : args;
}
}