SupaLidlGame/Debug/DebugConsole.cs

298 lines
8.2 KiB
C#
Raw Normal View History

2023-07-24 12:40:28 -07:00
using Godot;
using System.Collections.Generic;
2023-07-24 12:40:28 -07:00
namespace SupaLidlGame.Debug;
2023-09-24 18:51:23 -07:00
public sealed partial class DebugConsole : Control
2023-07-24 12:40:28 -07:00
{
2023-09-24 18:51:23 -07:00
private Node _context;
public Node Context
2023-07-24 12:40:28 -07:00
{
2023-09-24 18:51:23 -07:00
get => _context;
private set
{
if (value is not null)
{
_context = value;
if (_entry is not null)
{
_entry.PlaceholderText = "Enter Godot expression from " +
_context.GetPath();
2023-09-29 11:32:15 -07:00
_entry.SetContext(_context);
2023-09-24 18:51:23 -07:00
GetParent<Window>().Title = "Supa Developer Console: " +
_context.GetPath();
}
}
}
}
private Entry _entry;
2023-09-29 11:32:15 -07:00
public Entry Entry => _entry;
2023-09-24 18:51:23 -07:00
private RichTextLabel _output;
private Node ctx => Context;
public const double DEBUG_REFRESH_INTERVAL = 0.25;
2023-09-24 18:51:23 -07:00
public override void _Ready()
{
_entry = GetNode<Entry>("%Entry");
_output = GetNode<RichTextLabel>("%Output");
Context = Utils.World.Instance;
_entry.ConsoleInput += (string input) =>
2023-07-24 12:40:28 -07:00
{
2023-09-24 18:51:23 -07:00
try
{
Execute(input);
}
catch (InterpreterException ex)
{
2023-09-29 11:32:15 -07:00
_output.Text += ex.Message;
if (!ex.Message.StartsWith("Error occurred while"))
{
_output.Text += $" @{ex.Line}:{ex.Column}" + '\n';
_output.Text += input + '\n';
_output.Text += new string(' ', ex.Column - 1) + "^\n";
}
else
{
_output.Text += '\n';
}
2023-09-24 18:51:23 -07:00
}
};
GD.Print("DebugConsole init");
// TODO: put this in a separate class
// watch godot.log
bool isFileLoggingEnabled = ProjectSettings
.GetSetting("debug/file_logging/enable_file_logging.pc")
.AsBool();
if (isFileLoggingEnabled)
{
GD.Print("File logging is enabled.");
string logPath = ProjectSettings
.GetSetting("debug/file_logging/log_path")
.AsString();
var fs = FileAccess.Open(logPath, FileAccess.ModeFlags.Read);
var timer = new Timer();
AddChild(timer);
timer.Timeout += () =>
{
// push
while (fs.GetPosition() < fs.GetLength())
{
string line = fs.GetLine();
_output.Text += line + "\n";
}
};
timer.Start(DEBUG_REFRESH_INTERVAL);
}
else
{
GD.PushWarning("File logging is not enabled.");
}
2023-09-24 18:51:23 -07:00
}
public IEnumerable<NodePathToken> SplitPath(NodePath path)
2023-09-24 18:51:23 -07:00
{
CharIterator iterator = new(path);
return NodePathParser.ParseNodePath(iterator);
}
public NodePath ToNodePath(string path)
{
return Variant.From(path).AsNodePath();
}
2023-09-29 11:32:15 -07:00
public object GetNodeOrProperty(NodePath path)
{
var tokens = new List<NodePathToken>(SplitPath(path));
Node variant = Context ?? this;
for (int i = 0; i < tokens.Count; i++)
{
var subpath = tokens[i];
if (i == tokens.Count - 1)
{
if (subpath.Type == NodePathTokenType.Property)
{
return new PropertyPointer(variant, subpath.Path);
}
}
else
{
if (subpath.Type == NodePathTokenType.Node)
{
if (subpath.Path != "")
{
variant = variant.GetNode(subpath.Path);
}
}
else
{
variant = variant.GetIndexed(subpath.Path)
.AsGodotObject() as Node;
}
}
}
return variant;
}
public Variant From(NodePath path)
{
2023-09-24 18:51:23 -07:00
Variant variant = Context ?? this;
foreach (var subpath in SplitPath(path))
2023-09-24 18:51:23 -07:00
{
if (variant.VariantType == Variant.Type.Object)
{
if (variant.AsGodotObject() is Node n)
{
if (subpath.Type == NodePathTokenType.Node)
{
if (subpath.Path != "")
{
variant = n.GetNode(subpath.Path);
}
}
else
{
variant = n.GetIndexed(subpath.Path);
}
}
}
2023-07-24 12:40:28 -07:00
}
2023-09-24 18:51:23 -07:00
return variant;
}
public void SetProp(Variant prop, Variant value)
2023-09-24 18:51:23 -07:00
{
if (prop.VariantType == Variant.Type.NodePath)
{
var tokens = new List<NodePathToken>(SplitPath(prop.AsNodePath()));
Node variant = Context ?? this;
for (int i = 0; i < tokens.Count; i++)
{
var subpath = tokens[i];
if (i == tokens.Count - 1)
{
if (subpath.Type == NodePathTokenType.Property)
{
variant.SetIndexed(":" + subpath.Path, value);
}
}
else
{
if (subpath.Type == NodePathTokenType.Node)
{
if (subpath.Path != "")
{
variant = variant.GetNode(subpath.Path);
}
}
else
{
variant = variant.GetIndexed(subpath.Path)
.AsGodotObject() as Node;
}
}
}
}
else
{
}
2023-07-24 12:40:28 -07:00
}
public string CallMethod(
Utils.World world,
string entityName,
string method,
string[] args)
{
var ent = world.CurrentMap.Entities.GetNodeOrNull(entityName);
if (ent is not null)
{
var returnValue = ent.Call(method, args);
return returnValue.ToString();
}
return "";
}
2023-09-24 18:51:23 -07:00
public void Print(string text)
{
GD.Print(text);
}
2023-09-29 11:32:15 -07:00
public void Inspect(NodePath node)
{
}
2023-09-24 18:51:23 -07:00
public void Execute(string str)
{
//str = Sanitizer.Sanitize(str);
str = Transpiler.Transpiler.Transpile(str);
2023-09-24 18:51:23 -07:00
string inputMirror = $"[b]{Context.GetPath()}:[/b] {str}";
_output.Text += inputMirror + "\n";
var context = Context;
Godot.Expression exp = new();
string[] reserved = {
"from",
"set_context",
"context",
"set_prop",
"to_node_path",
"load",
};
2023-09-24 18:51:23 -07:00
Godot.Collections.Array reservedMap = new();
reservedMap.Add(new Callable(this, MethodName.From));
reservedMap.Add(new Callable(this, MethodName.SetContext));
reservedMap.Add(Context);
reservedMap.Add(new Callable(this, MethodName.SetProp));
reservedMap.Add(new Callable(this, MethodName.ToNodePath));
reservedMap.Add(new Callable(this, MethodName.Load));
2023-09-24 18:51:23 -07:00
var err = exp.Parse(str, reserved);
if (err != Error.Ok)
{
throw new InterpreterException(
"Error occurred while parsing Godot.Expression: \n" +
exp.GetErrorText(),
0, 0);
}
Variant result = exp.Execute(reservedMap, context);
if (exp.HasExecuteFailed())
{
throw new InterpreterException(
"Error occurred while evaluating Godot.Expression: \n" +
exp.GetErrorText(),
0, 0);
}
// send result to output
if (result.VariantType != Variant.Type.Nil)
{
_output.Text += result + "\n";
}
}
public void SetContext(Node node)
{
Context = node;
}
private Resource Load(string path)
{
return ResourceLoader.Load(path);
}
2023-07-24 12:40:28 -07:00
}