323 lines
12 KiB
C#
323 lines
12 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using DynamicExpresso;
|
||
using System.Text.RegularExpressions;
|
||
using System.Linq;
|
||
using UnityEngine.InputSystem;
|
||
using TMPro;
|
||
using System.Reflection;
|
||
using System.Linq.Expressions;
|
||
using Sirenix.Utilities;
|
||
using System.Collections;
|
||
using DG.Tweening;
|
||
using Unity.VisualScripting;
|
||
|
||
//又在写大粪 ——神币
|
||
namespace Ichni.Editor
|
||
{
|
||
public partial class EditorConsole : MonoBehaviour
|
||
{
|
||
public Interpreter functionInterpreter;
|
||
public TMP_InputField InputCommand;
|
||
private Dictionary<int, string> historyCommand = new Dictionary<int, string>();
|
||
private int historycount = 0;
|
||
public GameObject ConsoleUI;
|
||
public TMP_Text cueText;
|
||
bool isHide = true;
|
||
private string FillCueCommand(MethodInfo method)
|
||
{
|
||
if (method == null)
|
||
{
|
||
return "";
|
||
}
|
||
string methodName = method.Name;
|
||
// 3. 获取参数信息
|
||
var parameters = method.GetParameters();
|
||
if (parameters.Length == 0)
|
||
{
|
||
|
||
return $"{methodName} " + "\n";
|
||
}
|
||
|
||
// 4. 生成参数提示
|
||
string paramHint = string.Join(" ", parameters.Select(p =>
|
||
p.IsOptional ? $"[{p.ParameterType.Name}:{p.Name}]" : $"{p.ParameterType.Name}:{p.Name}"
|
||
));
|
||
return $"{methodName} {paramHint}".Trim() + "\n";
|
||
}
|
||
public void GetChange(string change)
|
||
{
|
||
if (change == "help")
|
||
{
|
||
List<MethodInfo> Allmethods = typeof(EditorConsoleMethods).GetMethods().
|
||
Where(m => m.IsStatic && m.IsPublic && m.ReturnType == typeof(void)).
|
||
ToList();
|
||
cueText.text = "";
|
||
for (int i = 0; i < Allmethods.Count; i++)
|
||
{
|
||
MethodInfo method = Allmethods[i];
|
||
cueText.text += $"{i}: | " + FillCueCommand(method);
|
||
}
|
||
return;
|
||
}
|
||
// Task t = new Task(() =>
|
||
// {
|
||
// 1. 提取命令名
|
||
string trimmed = change.Trim();
|
||
if (string.IsNullOrEmpty(trimmed))
|
||
{
|
||
cueText.text = "";
|
||
return;
|
||
}
|
||
// 支持 func(...) 或 func abc 123
|
||
var match = Regex.Match(trimmed, @"^(\w+)");
|
||
if (!match.Success)
|
||
{
|
||
cueText.text = "";
|
||
return;
|
||
}
|
||
string funcName = match.Groups[1].Value;
|
||
|
||
// 2. 查找方法
|
||
List<MethodInfo> methods = typeof(EditorConsoleMethods).GetMethods()
|
||
.Where(m => m.IsStatic && m.IsPublic && m.ReturnType == typeof(void) && m.Name.Contains(funcName)).ToList();
|
||
cueText.text = "";
|
||
for (int i = 0; i < methods.Count; i++)
|
||
{
|
||
MethodInfo method = methods[i];
|
||
cueText.text += $"{i}: | " + FillCueCommand(method);
|
||
}
|
||
if (cueText.text.IsNullOrWhitespace())
|
||
{
|
||
cueText.text = "No matching commands found.";
|
||
}
|
||
// });
|
||
|
||
// t.RunSynchronously();
|
||
}
|
||
public void GetCommand(string Command)//当提交命令时
|
||
{
|
||
try
|
||
{
|
||
functionInterpreter.Eval(Command);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogWarning("WTF Command! " + e);
|
||
LogWindow.Log("Unknow Command!", Color.red);
|
||
}
|
||
}
|
||
private void Update()
|
||
{
|
||
UIscale();
|
||
if (InputCommand.isFocused) InputDect();
|
||
}
|
||
private void UIscale()
|
||
{
|
||
if (Keyboard.current.backquoteKey.wasPressedThisFrame)
|
||
{
|
||
|
||
|
||
|
||
isHide = !isHide;
|
||
if (!isHide)
|
||
{
|
||
ConsoleUI.SetActive(true);
|
||
|
||
}
|
||
else
|
||
ConsoleUI.SetActive(false);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
//这是史,不要看
|
||
private void InputDect()
|
||
{
|
||
// 向下翻历史命令
|
||
if (Keyboard.current.downArrowKey.wasPressedThisFrame)
|
||
{
|
||
if (historyCommand.Count - 1 > historycount)
|
||
{
|
||
historycount++;
|
||
InputCommand.text = historyCommand[historycount];
|
||
}
|
||
else
|
||
{
|
||
InputCommand.text = "";
|
||
historycount = historyCommand.Count;
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 向上翻历史命令
|
||
if (Keyboard.current.upArrowKey.wasPressedThisFrame && historycount != 0)
|
||
{
|
||
historycount--;
|
||
InputCommand.text = historyCommand[historycount];
|
||
return;
|
||
}
|
||
|
||
// 提交命令
|
||
if (Keyboard.current.enterKey.wasPressedThisFrame)
|
||
{
|
||
string input = InputCommand.text;
|
||
if (string.IsNullOrWhiteSpace(input))
|
||
{
|
||
InputCommand.text = "";
|
||
return;
|
||
}
|
||
string ExpoCommand = TransformCommand(input);
|
||
|
||
|
||
print(ExpoCommand);
|
||
GetCommand(ExpoCommand);
|
||
|
||
// 记录历史命令
|
||
if (historyCommand.ContainsKey(historycount))
|
||
historyCommand[historycount] = input;
|
||
else
|
||
historyCommand.Add(historycount, input);
|
||
|
||
historycount++;
|
||
InputCommand.text = "";
|
||
}
|
||
CheckCtrlNumberShortcut();
|
||
|
||
}
|
||
// 支持Ctrl+数字键自动填充cueText内方法名到输入框
|
||
private void CheckCtrlNumberShortcut()
|
||
{
|
||
for (int i = 1; i <= 9; i++)
|
||
{
|
||
var key = (Key)((int)Key.Digit1 + i - 1);
|
||
if (Keyboard.current.ctrlKey.isPressed && Keyboard.current[key].wasPressedThisFrame)
|
||
{
|
||
var lines = cueText.text.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||
if (lines.Length >= i)
|
||
{
|
||
var line = lines[i - 1];
|
||
var match = Regex.Match(line, @"\|\s*(\w+)");
|
||
if (match.Success)
|
||
{
|
||
InputCommand.text = match.Groups[1].Value;
|
||
InputCommand.ActivateInputField();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
private string TransformCommand(string input)
|
||
{
|
||
string getFloatorInt(string a)
|
||
{
|
||
return a.Contains(".") ? $"{a}f" : a;
|
||
}
|
||
// 处理命令格式,适配 DynamicExpresso
|
||
string trimmed = input.Trim();
|
||
|
||
// 1. 直接支持 func(...) 形式
|
||
if (Regex.IsMatch(trimmed, @"^\w+\s*\(.*\)$"))
|
||
return trimmed;
|
||
|
||
// 2. 直接支持赋值表达式
|
||
if (trimmed.Contains("="))
|
||
return trimmed;
|
||
|
||
// 3. 支持 func abc 123 [1,2,3] 形式
|
||
var match = Regex.Match(trimmed, @"^(\w+)\s*(.*)$");
|
||
if (match.Success)
|
||
{
|
||
string func = match.Groups[1].Value;
|
||
string args = match.Groups[2].Value.Trim();
|
||
|
||
// 匹配参数:中括号、引号、或连续非空白
|
||
var argMatches = Regex.Matches(args, @"(\[[^\]]+\]|""(?:[^""\\]|\\.)*""|\S+)");
|
||
var argList = argMatches.Cast<Match>().Select(m => m.Value).ToList();
|
||
|
||
List<string> processedArgs = new List<string>();
|
||
foreach (var arg in argList)
|
||
{
|
||
string a = arg.Trim();
|
||
|
||
// Vector2/3: [1,2] 或 [1,2,3],支持小数
|
||
if (Regex.IsMatch(a, @"^\[\s*-?\d*\.?\d+\s*,\s*-?\d*\.?\d+(\s*,\s*-?\d*\.?\d+)*\s*\]$"))
|
||
{
|
||
// 去除中括号和空格
|
||
string content = a.Substring(1, a.Length - 2).Trim();
|
||
var nums = content.Split(',').Select(s => s.Trim()).ToArray();
|
||
if (nums.Length == 2)
|
||
processedArgs.Add($"new Vector2({getFloatorInt(nums[0])},{getFloatorInt(nums[1])})");
|
||
else if (nums.Length == 3)
|
||
processedArgs.Add($"new Vector3({getFloatorInt(nums[0])},{getFloatorInt(nums[1])},{getFloatorInt(nums[2])})");
|
||
else
|
||
processedArgs.Add(a); // 不是2或3维,原样返回
|
||
}
|
||
// 已有引号的字符串
|
||
else if (a.StartsWith("\"") && a.EndsWith("\""))
|
||
{
|
||
processedArgs.Add(a);
|
||
}
|
||
// 数字
|
||
else if (Regex.IsMatch(a, @"^-?\d+(\.\d+)?([eE][+-]?\d+)?$"))
|
||
{
|
||
processedArgs.Add(getFloatorInt(a));
|
||
}
|
||
// 变量名(字母开头,允许字母数字下划线)
|
||
else if (Regex.IsMatch(a, @"^[a-zA-Z_]\w*$"))
|
||
{
|
||
processedArgs.Add(a);
|
||
}
|
||
// 其它未加引号的参数,自动加引号
|
||
else
|
||
{
|
||
processedArgs.Add($"\"{a}\"");
|
||
}
|
||
}
|
||
|
||
string joinedArgs = string.Join(",", processedArgs);
|
||
return string.IsNullOrEmpty(joinedArgs) ? $"{func}()" : $"{func}({joinedArgs})";
|
||
}
|
||
return trimmed;
|
||
}
|
||
private void Start()
|
||
{
|
||
|
||
SetUpFunctions();
|
||
|
||
//Test
|
||
// functionInterpreter.Eval("print(\"Hello World!\")");
|
||
// functionInterpreter.Eval("log(\"Hello World but debug!\")");
|
||
}
|
||
}
|
||
|
||
public partial class EditorConsole
|
||
{
|
||
public Inspector inspector => EditorManager.instance.uiManager.inspector;
|
||
public Hierarchy hierarchy => EditorManager.instance.uiManager.hierarchy;
|
||
public LogWindow logWindow => EditorManager.instance.uiManager.mainPage.logWindow;
|
||
|
||
public List<string> commandList = new List<string>();
|
||
public void SetUpFunctions()
|
||
{
|
||
functionInterpreter = new Interpreter()
|
||
.Reference(typeof(Vector3))
|
||
.Reference(typeof(Vector2));//这是AI给的东西?
|
||
foreach (MethodInfo i in typeof(EditorConsoleMethods).GetMethods().
|
||
ToList().Where(i => i.IsStatic && i.IsPublic && (i.ReturnType == typeof(void))))
|
||
{
|
||
var parameters = i.GetParameters().Select(p => p.ParameterType).ToArray();
|
||
var delegateType = Expression.GetDelegateType(parameters.Concat(new[] { i.ReturnType }).ToArray());
|
||
functionInterpreter.SetFunction(i.Name, i.CreateDelegate(delegateType));
|
||
commandList.Add(i.Name);
|
||
}
|
||
|
||
}
|
||
|
||
|
||
}
|
||
} |