using MoonSharp.Interpreter; using System.Diagnostics; namespace anjianjingling.Lua { public enum LuaState { Idle, Running, Paused } /// 脚本中止信号(停止 / 急停 / 主动 stop)。 internal sealed class AbortSignal : Exception { public bool ByPanic { get; } public AbortSignal(bool byPanic) : base(byPanic ? "鼠标触及屏幕左上角" : "脚本已停止") => ByPanic = byPanic; } /// /// Lua 脚本执行器:MoonSharp 宿主 + 暂停/停止/急停 + 任务队列。 /// public sealed class LuaEngine { private readonly ILuaHost _host; private CancellationTokenSource? _cts; private readonly ManualResetEventSlim _pauseGate = new(true); private Task? _task; private volatile bool _paused; private volatile bool _byPanic; private long _lastPanicCheckTick; public LuaState State { get; private set; } = LuaState.Idle; public event Action? StateChanged; /// 执行速度倍率:只影响 delay 等待时长。 public double Speed { get; set; } = 1.0; public LuaEngine(ILuaHost host) => _host = host; // ===================== 语法检查 ===================== /// 只编译不执行;返回 null 表示语法通过。 public string? Validate(string code) { try { var script = CreateScript(); script.LoadString(code, null, "脚本"); return null; } catch (SyntaxErrorException ex) { return Localize(ex.Message); } catch (Exception ex) { return ex.Message; } } // ===================== 运行 ===================== /// 运行单段脚本(一次)。 public void Run(string code) { if (State != LuaState.Idle) { _host.Log("已有脚本在运行"); return; } DynValue chunk; try { var script = CreateScript(); chunk = script.LoadString(code, null, "脚本"); } catch (SyntaxErrorException ex) { _host.Log("语法错误:" + Localize(ex.Message)); return; } RunBackground(() => { chunk.Function.Call(); _host.Log("脚本执行完毕"); }); } /// 按顺序执行勾选的任务;每个任务按自身循环设置重复。 public void RunQueue(IReadOnlyList tasks) { if (State != LuaState.Idle) { _host.Log("已有脚本在运行"); return; } var enabled = tasks.Where(t => t.Enabled).ToList(); if (enabled.Count == 0) { _host.Log("没有勾选的任务"); return; } // 启动前统一编译,提前暴露语法错误 var prepared = new List<(LuaTask Task, DynValue Chunk)>(); foreach (var t in enabled) { try { var script = CreateScript(); prepared.Add((t, script.LoadString(t.Script, null, t.Name))); } catch (SyntaxErrorException ex) { _host.Log($"任务「{t.Name}」语法错误:{Localize(ex.Message)}"); return; } } RunBackground(() => { for (int i = 0; i < prepared.Count; i++) { var (task, chunk) = prepared[i]; _host.Log($"▶ 开始任务 {i + 1}/{prepared.Count}:{task.Name}({task.ModeText})"); int round = 0; while (true) { Tick(); if (!task.Infinite && round >= task.LoopCount) break; chunk.Function.Call(); round++; bool more = task.Infinite || round < task.LoopCount; if (more && task.IntervalMs > 0) Sleep(task.IntervalMs); } _host.Log($"✔ 任务完成:{task.Name}({round} 轮)"); } _host.Log("★ 任务队列全部执行完毕"); }); } private void RunBackground(Action body) { _paused = false; _byPanic = false; _pauseGate.Set(); _cts = new CancellationTokenSource(); SetState(LuaState.Running); _task = Task.Run(() => { try { body(); } catch (AbortSignal ex) { _host.Log(ex.ByPanic ? "■ 鼠标触及屏幕左上角,紧急停止" : "■ 脚本已停止"); } catch (SyntaxErrorException ex) { _host.Log("语法错误:" + Localize(ex.Message)); } catch (ScriptRuntimeException ex) { _host.Log("运行错误:" + Localize(ex.Message)); } catch (InterpreterException ex) { _host.Log("运行错误:" + Localize(ex.Message)); } catch (Exception ex) { _host.Log("运行错误:" + ex.Message); } finally { _cts?.Dispose(); _cts = null; _pauseGate.Set(); _paused = false; SetState(LuaState.Idle); } }); } private Script CreateScript() { var script = new Script(CoreModules.Preset_HardSandbox | CoreModules.String | CoreModules.Math | CoreModules.Table | CoreModules.Coroutine); script.Options.DebugPrint = s => _host.Log(s); ScriptApi.Bind(script, this, _host); return script; } // ===================== 控制 ===================== public void PauseOrResume() { if (State == LuaState.Running) { _paused = true; _pauseGate.Reset(); _host.Log("‖ 已暂停(F9 继续)"); SetState(LuaState.Paused); } else if (State == LuaState.Paused) { _paused = false; _pauseGate.Set(); _host.Log("▶ 继续执行"); SetState(LuaState.Running); } } public void Stop() { if (State == LuaState.Idle) return; _byPanic = false; _paused = false; _pauseGate.Set(); _cts?.Cancel(); } private void SetState(LuaState s) { State = s; StateChanged?.Invoke(s); } // ===================== 中断点(API 回调内调用)===================== internal void Tick() { var cts = _cts; if (cts is { IsCancellationRequested: true }) throw new AbortSignal(_byPanic); if (_paused) { _pauseGate.Wait(); if (cts is { IsCancellationRequested: true }) throw new AbortSignal(_byPanic); } // 急停检查节流到 100ms 一次,避免每条命令都调系统 API long now = Environment.TickCount64; if (now - _lastPanicCheckTick < 100) return; _lastPanicCheckTick = now; Native.Win32.GetCursorPos(out var p); if (p.X > 0 || p.Y > 0) return; Thread.Sleep(120); Native.Win32.GetCursorPos(out var p2); if (p2.X <= 0 && p2.Y <= 0) { _byPanic = true; _cts?.Cancel(); throw new AbortSignal(true); } } /// 可中断、可暂停的延时(分片 20ms,支持倍速)。 internal void Sleep(double ms) { ms = Math.Max(0, ms / Math.Max(0.05, Speed)); var sw = Stopwatch.StartNew(); while (sw.Elapsed.TotalMilliseconds < ms) { Tick(); int slice = (int)Math.Min(20, Math.Max(1, ms - sw.Elapsed.TotalMilliseconds)); _pauseGate.Wait(slice); } } // ===================== 错误信息中文化 ===================== private static readonly (string From, string To)[] Dict = { ("unexpected symbol near ''", "语句未写完(意外到达文件结尾)"), ("unexpected symbol", "意外的符号"), ("'end' expected", "缺少 end"), ("'=' expected", "缺少 ="), ("'then' expected", "缺少 then"), ("'do' expected", "缺少 do"), ("unfinished string", "字符串未闭合"), ("malformed number", "数字格式错误"), ("attempt to index a nil value", "访问了空值(nil)"), ("attempt to call a nil value", "调用了不存在的函数"), ("attempt to perform arithmetic", "算术运算类型错误"), ("attempt to compare", "比较运算类型错误"), ("index out of range", "索引越界"), ("cannot convert", "类型转换失败"), ("invalid arguments", "参数不合法"), }; private static string Localize(string msg) { if (string.IsNullOrEmpty(msg)) return msg; foreach (var (from, to) in Dict) if (msg.Contains(from, StringComparison.OrdinalIgnoreCase)) msg = msg.Replace(from, to, StringComparison.OrdinalIgnoreCase); return msg.Trim(); } } }