2023-07-24 12:40:28 -07:00
|
|
|
using Godot;
|
2023-09-26 10:23:04 -07:00
|
|
|
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;
|
|
|
|
|
2024-06-04 22:12:01 -07:00
|
|
|
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
|
|
|
}
|
|
|
|
};
|
2024-06-04 22:12:01 -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
|
|
|
}
|
|
|
|
|
2023-09-26 10:23:04 -07:00
|
|
|
public IEnumerable<NodePathToken> SplitPath(NodePath path)
|
2023-09-24 18:51:23 -07:00
|
|
|
{
|
|
|
|
CharIterator iterator = new(path);
|
2023-09-26 10:23:04 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-09-26 10:23:04 -07:00
|
|
|
public Variant From(NodePath path)
|
|
|
|
{
|
2023-09-24 18:51:23 -07:00
|
|
|
Variant variant = Context ?? this;
|
2023-09-26 10:23:04 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-09-26 10:23:04 -07:00
|
|
|
public void SetProp(Variant prop, Variant value)
|
2023-09-24 18:51:23 -07:00
|
|
|
{
|
2023-09-26 10:23:04 -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)
|
|
|
|
{
|
2023-09-26 10:23:04 -07:00
|
|
|
//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();
|
|
|
|
|
2024-06-04 22:12:01 -07:00
|
|
|
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);
|
2023-09-26 10:23:04 -07:00
|
|
|
reservedMap.Add(new Callable(this, MethodName.SetProp));
|
|
|
|
reservedMap.Add(new Callable(this, MethodName.ToNodePath));
|
2024-06-04 22:12:01 -07:00
|
|
|
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;
|
|
|
|
}
|
2024-06-04 22:12:01 -07:00
|
|
|
|
|
|
|
private Resource Load(string path)
|
|
|
|
{
|
|
|
|
return ResourceLoader.Load(path);
|
|
|
|
}
|
2023-07-24 12:40:28 -07:00
|
|
|
}
|