Files
GoakeFlax/Source/Game/Cabrito/Console/Console.cs
GoaLitiuM 8bde0a7f18 tests
2021-08-31 20:03:02 +03:00

390 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
namespace Cabrito
{
public class ConsoleLine
{
public string content;
internal ConsoleLine(string line)
{
content = line;
}
public static implicit operator string(ConsoleLine line) => line.content;
public static explicit operator ConsoleLine(string line) => new ConsoleLine(line);
public override string ToString() => content.ToString();
}
public static class Console
{
private static ConsoleInstance instance;
public static void Init()
{
Destroy();
instance = new ConsoleInstance();
}
public static void Destroy()
{
if (instance != null)
{
instance.Dispose();
instance = null;
}
}
// Returns if Console window open right now.
public static bool IsOpen => instance.IsOpen;
// For debugging only: Returns true when Console was not closed during the same frame.
// Needed when Escape-key both closes the console and exits the game.
public static bool IsSafeToQuit => instance.IsSafeToQuit;
// Called when Console is opened.
public static Action OnOpen
{
get { return instance.OnOpen; }
set { instance.OnOpen = value; }
}
// Called when Console is closed.
public static Action OnClose
{
get { return instance.OnClose; }
set { instance.OnClose = value; }
}
// Called when a line of text was printed in Console.
public static Action<string> OnPrint
{
get { return instance.OnPrint; }
set { instance.OnPrint = value; }
}
public static bool ShowExecutedLines => instance.ShowExecutedLines;
public static string LinePrefix => instance.LinePrefix;
public static IReadOnlyCollection<ConsoleLine> Lines => instance.Lines;
// Echoes text to Console
public static void Print(string text) => instance.Print(text);
// Echoes warning text to Console
public static void PrintWarning(string text) => instance.PrintWarning(text);
// Echoes error text to Console
public static void PrintError(string text) => instance.PrintError(text);
// Echoes developer/debug text to Console
public static void PrintDebug(string text) => instance.PrintDebug(text);
// Opens the Console
public static void Open() => instance.Open();
// Closes the Console
public static void Close() => instance.Close();
// Clears the content of the Console
public static void Clear() => instance.Clear();
public static void Execute(string str) => instance.Execute(str);
public static string GetVariable(string variableName) => instance.GetVariable(variableName);
}
public class ConsoleInstance : IDisposable
{
public bool IsOpen { get; internal set; } = true;
public bool IsSafeToQuit
{
get { return stopwatch.Elapsed.TotalSeconds > 0.1; }
}
private Stopwatch stopwatch = Stopwatch.StartNew();
public Action OnOpen;
public Action OnClose;
public Action<string> OnPrint;
public bool ShowExecutedLines = true;
public string LinePrefix { get; internal set; } = "]";
//private static List<string> consoleLines = new List<string>();
private List<ConsoleLine> consoleLines = new List<ConsoleLine>();
private Dictionary<string, ConsoleCommand> consoleCommands = new Dictionary<string, ConsoleCommand>();
private Dictionary<string, ConsoleVariable> consoleVariables = new Dictionary<string, ConsoleVariable>();
// Initializes the Console system.
public ConsoleInstance()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
Assembly[] assemblies = currentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
// Skip common assemblies
var assemblyName = assembly.GetName().Name;
if (assemblyName == "System" ||
assemblyName.StartsWith("System.") ||
assemblyName.StartsWith("Mono.") ||
assemblyName == "mscorlib" ||
assemblyName == "Newtonsoft.Json" ||
assemblyName.StartsWith("FlaxEngine.") ||
assemblyName.StartsWith("JetBrains.") ||
assemblyName.StartsWith("Microsoft.") ||
assemblyName.StartsWith("nunit."))
{
continue;
}
foreach (var type in assembly.GetTypes())
{
Dictionary<string, ConsoleCommand> cmdParsed = new Dictionary<string, ConsoleCommand>();
Dictionary<string, List<MethodInfo>> cmdMethods = new Dictionary<string, List<MethodInfo>>();
foreach (MethodInfo method in type.GetMethods())
{
if (!method.IsStatic)
continue;
Attribute[] attributes = Attribute.GetCustomAttributes(method);
foreach (Attribute attr in attributes)
{
if (attr is ConsoleCommandAttribute cmdAttribute)
{
//Console.Print("found cmd '" + cmdAttribute.name + "' bound to field '" + method.Name + "'");
// Defer constructing the command until we have parsed all the methods for it in this assembly.
List<MethodInfo> methods;
if (!cmdMethods.TryGetValue(cmdAttribute.name, out methods))
{
methods = new List<MethodInfo>();
cmdMethods.Add(cmdAttribute.name, methods);
}
methods.Add(method);
ConsoleCommand cmd = new ConsoleCommand(cmdAttribute.name, null);
if (!cmdParsed.ContainsKey(cmdAttribute.name))
cmdParsed.Add(cmdAttribute.name, cmd);
foreach (var alias in cmdAttribute.aliases)
{
if (!cmdParsed.ContainsKey(alias))
cmdParsed.Add(alias, cmd);
List<MethodInfo> aliasMethods;
if (!cmdMethods.TryGetValue(alias, out aliasMethods))
{
aliasMethods = new List<MethodInfo>();
cmdMethods.Add(alias, aliasMethods);
}
aliasMethods.Add(method);
}
}
}
}
foreach (var kv in cmdParsed)
{
var methods = cmdMethods[kv.Key];
var definition = kv.Value;
ConsoleCommand cmd = new ConsoleCommand(definition.name, methods.ToArray());
consoleCommands.Add(kv.Key, cmd);
}
foreach (FieldInfo field in type.GetFields())
{
if (!field.IsStatic)
continue;
Attribute[] attributes = Attribute.GetCustomAttributes(field);
foreach (Attribute attr in attributes)
{
if (attr is ConsoleVariableAttribute cvarAttribute)
{
//Console.Print("found cvar '" + cvarAttribute.name + "' bound to field '" + field.Name + "'");
consoleVariables.Add(cvarAttribute.name,
new ConsoleVariable(cvarAttribute.name, cvarAttribute.flags, field));
foreach (var alias in cvarAttribute.aliases)
consoleVariables.Add(alias,
new ConsoleVariable(cvarAttribute.name,
cvarAttribute.flags | ConsoleFlags.NoSerialize, field));
}
}
}
foreach (PropertyInfo prop in type.GetProperties())
{
MethodInfo getter = prop.GetGetMethod();
MethodInfo setter = prop.GetSetMethod();
if (getter == null || setter == null || !getter.IsStatic || !setter.IsStatic)
continue;
Attribute[] attributes = Attribute.GetCustomAttributes(prop);
foreach (Attribute attr in attributes)
{
if (attr is ConsoleVariableAttribute cvarAttribute)
{
//Console.Print("found cvar '" + cvarAttribute.name + "' bound to field '" + field.Name + "'");
consoleVariables.Add(cvarAttribute.name,
new ConsoleVariable(cvarAttribute.name, cvarAttribute.flags, getter, setter));
foreach (var alias in cvarAttribute.aliases)
consoleVariables.Add(alias,
new ConsoleVariable(cvarAttribute.name,
cvarAttribute.flags | ConsoleFlags.NoSerialize, getter, setter));
}
}
}
}
}
}
public void Dispose()
{
}
public IReadOnlyCollection<ConsoleLine> Lines
{
get => consoleLines.AsReadOnly();
}
// Echoes text to Console
public void Print(string text)
{
foreach (var line in text.Split(new []{'\n'}))
{
ConsoleLine lineEntry = new ConsoleLine(line);
consoleLines.Add(lineEntry);
OnPrint?.Invoke(text);
}
}
// Echoes warning text to Console
public void PrintWarning(string text)
{
foreach (var line in text.Split(new[] { '\n' }))
{
ConsoleLine lineEntry = new ConsoleLine(line);
consoleLines.Add(lineEntry);
OnPrint?.Invoke(text);
}
}
// Echoes error text to Console
public void PrintError(string text)
{
foreach (var line in text.Split(new[] { '\n' }))
{
ConsoleLine lineEntry = new ConsoleLine(line);
consoleLines.Add(lineEntry);
OnPrint?.Invoke(text);
}
if (Debugger.IsAttached)
{
Debugger.Break();
text = text;
}
else
throw new Exception(text);
}
// Echoes developer/debug text to Console
public void PrintDebug(string text)
{
foreach (var line in text.Split(new[] { '\n' }))
{
ConsoleLine lineEntry = new ConsoleLine(line);
consoleLines.Add(lineEntry);
OnPrint?.Invoke(text);
}
}
// Opens the Console
public void Open()
{
if (IsOpen)
return;
IsOpen = true;
OnOpen?.Invoke();
}
// Closes the Console
public void Close()
{
if (!IsOpen)
return;
IsOpen = false;
OnClose?.Invoke();
stopwatch.Restart();
}
// Clears the content of the Console
public void Clear()
{
consoleLines.Clear();
}
public void Execute(string str)
{
str = str.Trim();
if (ShowExecutedLines)
Console.Print(LinePrefix + str);
string[] strs = str.Split(' ');
string execute = strs[0];
string executeLower = execute.ToLowerInvariant();
string value = strs.Length > 1 ? str.Substring(execute.Length + 1) : null;
//Console.PrintDebug("Executed '" + execute + "' with params: '" + value + "'");
if (consoleCommands.TryGetValue(executeLower, out ConsoleCommand cmd))
{
string[] values = strs.Skip(1).ToArray();
if (values.Length > 0)
cmd.Invoke(values);
else
cmd.Invoke();
//Console.Print("Command bound to '" + execute + "' is '" + cmd.method.Name + "'");
}
else if (consoleVariables.TryGetValue(executeLower, out ConsoleVariable cvar))
{
if (value != null)
cvar.SetValue(value);
Console.Print("'" + execute + "' is '" + cvar.GetValueString() + "'");
}
else
Console.Print("Unknown command '" + execute + "'");
}
public string GetVariable(string variableName)
{
if (consoleVariables.TryGetValue(variableName, out ConsoleVariable cvar))
{
string value = cvar.GetValueString();
return value;
}
return null;
}
}
}