From 53b2d1d7bcb54e2f6726e4aaae66123a8529acc0 Mon Sep 17 00:00:00 2001 From: SuuperW Date: Tue, 19 Sep 2023 02:31:10 -0500 Subject: [PATCH] Rename file. (Next commit will rename class and create new file/class with the old filename.) --- .../{LuaLibraries.cs => LuaLibrariesBase.cs} | 746 +++++++++--------- 1 file changed, 373 insertions(+), 373 deletions(-) rename src/BizHawk.Client.EmuHawk/tools/Lua/{LuaLibraries.cs => LuaLibrariesBase.cs} (96%) diff --git a/src/BizHawk.Client.EmuHawk/tools/Lua/LuaLibraries.cs b/src/BizHawk.Client.EmuHawk/tools/Lua/LuaLibrariesBase.cs similarity index 96% rename from src/BizHawk.Client.EmuHawk/tools/Lua/LuaLibraries.cs rename to src/BizHawk.Client.EmuHawk/tools/Lua/LuaLibrariesBase.cs index 38b53b33f6..c645397ed9 100644 --- a/src/BizHawk.Client.EmuHawk/tools/Lua/LuaLibraries.cs +++ b/src/BizHawk.Client.EmuHawk/tools/Lua/LuaLibrariesBase.cs @@ -1,373 +1,373 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading; - -using NLua; - -using BizHawk.Common; -using BizHawk.Emulation.Common; -using BizHawk.Client.Common; - -namespace BizHawk.Client.EmuHawk -{ - public class LuaLibraries : ILuaLibraries - { - public LuaLibraries( - LuaFileList scriptList, - LuaFunctionList registeredFuncList, - IEmulatorServiceProvider serviceProvider, - MainForm mainForm, - DisplayManagerBase displayManager, - InputManager inputManager, - Config config, - IEmulator emulator, - IGameInfo game) - { - void EnumerateLuaFunctions(string name, Type type, LuaLibraryBase instance) - { - if (instance != null) _lua.NewTable(name); - foreach (var method in type.GetMethods()) - { - var foundAttrs = method.GetCustomAttributes(typeof(LuaMethodAttribute), false); - if (foundAttrs.Length == 0) continue; - if (instance != null) _lua.RegisterFunction($"{name}.{((LuaMethodAttribute)foundAttrs[0]).Name}", instance, method); - LibraryFunction libFunc = new( - name, - type.GetCustomAttributes(typeof(DescriptionAttribute), false).Cast() - .Select(descAttr => descAttr.Description).FirstOrDefault() ?? string.Empty, - method - ); - Docs.Add(libFunc); - } - } - - _th = new NLuaTableHelper(_lua, LogToLuaConsole); - _displayManager = displayManager; - _inputManager = inputManager; - _mainForm = mainForm; - LuaWait = new AutoResetEvent(false); - PathEntries = config.PathEntries; - RegisteredFunctions = registeredFuncList; - ScriptList = scriptList; - Docs.Clear(); - _apiContainer = ApiManager.RestartLua(serviceProvider, LogToLuaConsole, _mainForm, _displayManager, _inputManager, _mainForm.MovieSession, _mainForm.Tools, config, emulator, game); - - // Register lua libraries - foreach (var lib in Client.Common.ReflectionCache.Types.Concat(EmuHawk.ReflectionCache.Types) - .Where(t => typeof(LuaLibraryBase).IsAssignableFrom(t) && t.IsSealed && ServiceInjector.IsAvailable(serviceProvider, t))) - { - if (VersionInfo.DeveloperBuild - || lib.GetCustomAttribute(inherit: false)?.Released is not false) - { - var instance = (LuaLibraryBase)Activator.CreateInstance(lib, this, _apiContainer, (Action)LogToLuaConsole); - if (!ServiceInjector.UpdateServices(serviceProvider, instance, mayCache: true)) throw new Exception("Lua lib has required service(s) that can't be fulfilled"); - - // TODO: make EmuHawk libraries have a base class with common properties such as this - // and inject them here - if (instance is ClientLuaLibrary clientLib) - { - clientLib.MainForm = _mainForm; - } - else if (instance is ConsoleLuaLibrary consoleLib) - { - consoleLib.Tools = _mainForm.Tools; - _logToLuaConsoleCallback = consoleLib.Log; - } - else if (instance is FormsLuaLibrary formsLib) - { - formsLib.MainForm = _mainForm; - } - else if (instance is GuiLuaLibrary guiLib) - { - // emu lib may be null now, depending on order of ReflectionCache.Types, but definitely won't be null when this is called - guiLib.CreateLuaCanvasCallback = (width, height, x, y) => - { - var canvas = new LuaCanvas(EmulationLuaLibrary, width, height, x, y, _th, LogToLuaConsole); - canvas.Show(); - return _th.ObjectToTable(canvas); - }; - } - else if (instance is TAStudioLuaLibrary tastudioLib) - { - tastudioLib.Tools = _mainForm.Tools; - } - - EnumerateLuaFunctions(instance.Name, lib, instance); - Libraries.Add(lib, instance); - } - } - - _lua.RegisterFunction("print", this, typeof(LuaLibraries).GetMethod(nameof(Print))); - - var packageTable = (LuaTable) _lua["package"]; - var luaPath = PathEntries.LuaAbsolutePath(); - if (OSTailoredCode.IsUnixHost) - { - // add %exe%/Lua to library resolution pathset (LUA_PATH) - // this is done already on windows, but not on linux it seems? - packageTable["path"] = $"{luaPath}/?.lua;{luaPath}?/init.lua;{packageTable["path"]}"; - // we need to modifiy the cpath so it looks at our lua dir too, and remove the relative pathing - // we do this on Windows too, but keep in mind Linux uses .so and Windows use .dll - // TODO: Does the relative pathing issue Windows has also affect Linux? I'd assume so... - packageTable["cpath"] = $"{luaPath}/?.so;{luaPath}/loadall.so;{packageTable["cpath"]}"; - packageTable["cpath"] = ((string)packageTable["cpath"]).Replace(";./?.so", ""); - } - else - { - packageTable["cpath"] = $"{luaPath}\\?.dll;{luaPath}\\loadall.dll;{packageTable["cpath"]}"; - packageTable["cpath"] = ((string)packageTable["cpath"]).Replace(";.\\?.dll", ""); - } - - EmulationLuaLibrary.FrameAdvanceCallback = FrameAdvance; - EmulationLuaLibrary.YieldCallback = EmuYield; - - EnumerateLuaFunctions(nameof(LuaCanvas), typeof(LuaCanvas), null); // add LuaCanvas to Lua function reference table - } - - private ApiContainer _apiContainer; - - private readonly DisplayManagerBase _displayManager; - - private GuiApi GuiAPI => (GuiApi)_apiContainer.Gui; - - private readonly InputManager _inputManager; - - private readonly MainForm _mainForm; - - private Lua _lua = new(); - private LuaThread _currThread; - - private readonly NLuaTableHelper _th; - - private static Action _logToLuaConsoleCallback = a => Console.WriteLine("a Lua lib is logging during init and the console lib hasn't been initialised yet"); - - private FormsLuaLibrary FormsLibrary => (FormsLuaLibrary)Libraries[typeof(FormsLuaLibrary)]; - - public LuaDocumentation Docs { get; } = new LuaDocumentation(); - - private EmulationLuaLibrary EmulationLuaLibrary => (EmulationLuaLibrary)Libraries[typeof(EmulationLuaLibrary)]; - - public string EngineName => "NLua+Lua"; - - public bool IsRebootingCore { get; set; } - - public bool IsUpdateSupressed { get; set; } - - private readonly IDictionary Libraries = new Dictionary(); - - private EventWaitHandle LuaWait; - - public PathEntryCollection PathEntries { get; private set; } - - public LuaFileList ScriptList { get; } - - private static void LogToLuaConsole(object outputs) => _logToLuaConsoleCallback(new[] { outputs }); - - public NLuaTableHelper GetTableHelper() => _th; - - public void Restart( - IEmulatorServiceProvider newServiceProvider, - Config config, - IEmulator emulator, - IGameInfo game) - { - _apiContainer = ApiManager.RestartLua(newServiceProvider, LogToLuaConsole, _mainForm, _displayManager, _inputManager, _mainForm.MovieSession, _mainForm.Tools, config, emulator, game); - PathEntries = config.PathEntries; - foreach (var lib in Libraries.Values) - { - lib.APIs = _apiContainer; - Debug.Assert(ServiceInjector.UpdateServices(newServiceProvider, lib, mayCache: true)); - lib.Restarted(); - } - } - - public bool FrameAdvanceRequested { get; private set; } - - public LuaFunctionList RegisteredFunctions { get; } - - public void CallSaveStateEvent(string name) - { - using var luaAutoUnlockHack = GuiAPI.ThisIsTheLuaAutoUnlockHack(); - - try - { - foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_SAVESTATE).ToList()) - { - lf.Call(name); - } - } - catch (Exception e) - { - LogToLuaConsole($"error running function attached by lua function event.onsavestate\nError message: {e.Message}"); - } - } - - public void CallLoadStateEvent(string name) - { - using var luaAutoUnlockHack = GuiAPI.ThisIsTheLuaAutoUnlockHack(); - - try - { - foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_LOADSTATE).ToList()) - { - lf.Call(name); - } - } - catch (Exception e) - { - LogToLuaConsole($"error running function attached by lua function event.onloadstate\nError message: {e.Message}"); - } - } - - public void CallFrameBeforeEvent() - { - if (IsUpdateSupressed) return; - - using var luaAutoUnlockHack = GuiAPI.ThisIsTheLuaAutoUnlockHack(); - - try - { - foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_PREFRAME).ToList()) - { - lf.Call(); - } - } - catch (Exception e) - { - LogToLuaConsole($"error running function attached by lua function event.onframestart\nError message: {e.Message}"); - } - } - - public void CallFrameAfterEvent() - { - if (IsUpdateSupressed) return; - - using var luaAutoUnlockHack = GuiAPI.ThisIsTheLuaAutoUnlockHack(); - - try - { - foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_POSTFRAME).ToList()) - { - lf.Call(); - } - } - catch (Exception e) - { - LogToLuaConsole($"error running function attached by lua function event.onframeend\nError message: {e.Message}"); - } - } - - public void CallExitEvent(LuaFile lf) - { - using var luaAutoUnlockHack = GuiAPI.ThisIsTheLuaAutoUnlockHack(); - - foreach (var exitCallback in RegisteredFunctions - .Where(l => l.Event == NamedLuaFunction.EVENT_TYPE_ENGINESTOP - && (l.LuaFile.Path == lf.Path || ReferenceEquals(l.LuaFile.Thread, lf.Thread))) - .ToList()) - { - exitCallback.Call(); - } - } - - public void Close() - { - foreach (var closeCallback in RegisteredFunctions - .Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_CONSOLECLOSE) - .ToList()) - { - closeCallback.Call(); - } - - RegisteredFunctions.Clear(_mainForm.Emulator); - ScriptList.Clear(); - FormsLibrary.DestroyAll(); - _lua.Dispose(); - _lua = null; - } - - public INamedLuaFunction CreateAndRegisterNamedFunction( - LuaFunction function, - string theEvent, - Action logCallback, - LuaFile luaFile, - string name = null) - { - var nlf = new NamedLuaFunction(function, theEvent, logCallback, luaFile, - () => { _lua.NewThread(out var thread); return thread; }, name); - RegisteredFunctions.Add(nlf); - return nlf; - } - - public bool RemoveNamedFunctionMatching(Func predicate) - { - var nlf = (NamedLuaFunction)RegisteredFunctions.FirstOrDefault(predicate); - if (nlf == null) return false; - RegisteredFunctions.Remove(nlf, _mainForm.Emulator); - return true; - } - - public LuaThread SpawnCoroutine(string file) - { - var content = File.ReadAllText(file); - var main = _lua.LoadString(content, "main"); - _lua.NewThread(main, out var ret); - return ret; - } - - public void SpawnAndSetFileThread(string pathToLoad, LuaFile lf) - => lf.Thread = SpawnCoroutine(pathToLoad); - - public void ExecuteString(string command) - => _lua.DoString(command); - - public (bool WaitForFrame, bool Terminated) ResumeScript(LuaFile lf) - { - _currThread = lf.Thread; - using var luaAutoUnlockHack = GuiAPI.ThisIsTheLuaAutoUnlockHack(); - - try - { - LuaLibraryBase.SetCurrentThread(lf); - - var execResult = _currThread.Resume(); - - _currThread = null; - var result = execResult switch - { - LuaStatus.OK => (WaitForFrame: false, Terminated: true), - LuaStatus.Yield => (WaitForFrame: FrameAdvanceRequested, Terminated: false), - _ => throw new InvalidOperationException($"{nameof(_currThread.Resume)}() returned {execResult}?") - }; - - FrameAdvanceRequested = false; - return result; - } - finally - { - LuaLibraryBase.ClearCurrentThread(); - } - } - - public static void Print(params object[] outputs) - { - _logToLuaConsoleCallback(outputs); - } - - private void FrameAdvance() - { - FrameAdvanceRequested = true; - _currThread.Yield(); - } - - private void EmuYield() - { - _currThread.Yield(); - } - } -} +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading; + +using NLua; + +using BizHawk.Common; +using BizHawk.Emulation.Common; +using BizHawk.Client.Common; + +namespace BizHawk.Client.EmuHawk +{ + public class LuaLibraries : ILuaLibraries + { + public LuaLibraries( + LuaFileList scriptList, + LuaFunctionList registeredFuncList, + IEmulatorServiceProvider serviceProvider, + MainForm mainForm, + DisplayManagerBase displayManager, + InputManager inputManager, + Config config, + IEmulator emulator, + IGameInfo game) + { + void EnumerateLuaFunctions(string name, Type type, LuaLibraryBase instance) + { + if (instance != null) _lua.NewTable(name); + foreach (var method in type.GetMethods()) + { + var foundAttrs = method.GetCustomAttributes(typeof(LuaMethodAttribute), false); + if (foundAttrs.Length == 0) continue; + if (instance != null) _lua.RegisterFunction($"{name}.{((LuaMethodAttribute)foundAttrs[0]).Name}", instance, method); + LibraryFunction libFunc = new( + name, + type.GetCustomAttributes(typeof(DescriptionAttribute), false).Cast() + .Select(descAttr => descAttr.Description).FirstOrDefault() ?? string.Empty, + method + ); + Docs.Add(libFunc); + } + } + + _th = new NLuaTableHelper(_lua, LogToLuaConsole); + _displayManager = displayManager; + _inputManager = inputManager; + _mainForm = mainForm; + LuaWait = new AutoResetEvent(false); + PathEntries = config.PathEntries; + RegisteredFunctions = registeredFuncList; + ScriptList = scriptList; + Docs.Clear(); + _apiContainer = ApiManager.RestartLua(serviceProvider, LogToLuaConsole, _mainForm, _displayManager, _inputManager, _mainForm.MovieSession, _mainForm.Tools, config, emulator, game); + + // Register lua libraries + foreach (var lib in Client.Common.ReflectionCache.Types.Concat(EmuHawk.ReflectionCache.Types) + .Where(t => typeof(LuaLibraryBase).IsAssignableFrom(t) && t.IsSealed && ServiceInjector.IsAvailable(serviceProvider, t))) + { + if (VersionInfo.DeveloperBuild + || lib.GetCustomAttribute(inherit: false)?.Released is not false) + { + var instance = (LuaLibraryBase)Activator.CreateInstance(lib, this, _apiContainer, (Action)LogToLuaConsole); + if (!ServiceInjector.UpdateServices(serviceProvider, instance, mayCache: true)) throw new Exception("Lua lib has required service(s) that can't be fulfilled"); + + // TODO: make EmuHawk libraries have a base class with common properties such as this + // and inject them here + if (instance is ClientLuaLibrary clientLib) + { + clientLib.MainForm = _mainForm; + } + else if (instance is ConsoleLuaLibrary consoleLib) + { + consoleLib.Tools = _mainForm.Tools; + _logToLuaConsoleCallback = consoleLib.Log; + } + else if (instance is FormsLuaLibrary formsLib) + { + formsLib.MainForm = _mainForm; + } + else if (instance is GuiLuaLibrary guiLib) + { + // emu lib may be null now, depending on order of ReflectionCache.Types, but definitely won't be null when this is called + guiLib.CreateLuaCanvasCallback = (width, height, x, y) => + { + var canvas = new LuaCanvas(EmulationLuaLibrary, width, height, x, y, _th, LogToLuaConsole); + canvas.Show(); + return _th.ObjectToTable(canvas); + }; + } + else if (instance is TAStudioLuaLibrary tastudioLib) + { + tastudioLib.Tools = _mainForm.Tools; + } + + EnumerateLuaFunctions(instance.Name, lib, instance); + Libraries.Add(lib, instance); + } + } + + _lua.RegisterFunction("print", this, typeof(LuaLibraries).GetMethod(nameof(Print))); + + var packageTable = (LuaTable) _lua["package"]; + var luaPath = PathEntries.LuaAbsolutePath(); + if (OSTailoredCode.IsUnixHost) + { + // add %exe%/Lua to library resolution pathset (LUA_PATH) + // this is done already on windows, but not on linux it seems? + packageTable["path"] = $"{luaPath}/?.lua;{luaPath}?/init.lua;{packageTable["path"]}"; + // we need to modifiy the cpath so it looks at our lua dir too, and remove the relative pathing + // we do this on Windows too, but keep in mind Linux uses .so and Windows use .dll + // TODO: Does the relative pathing issue Windows has also affect Linux? I'd assume so... + packageTable["cpath"] = $"{luaPath}/?.so;{luaPath}/loadall.so;{packageTable["cpath"]}"; + packageTable["cpath"] = ((string)packageTable["cpath"]).Replace(";./?.so", ""); + } + else + { + packageTable["cpath"] = $"{luaPath}\\?.dll;{luaPath}\\loadall.dll;{packageTable["cpath"]}"; + packageTable["cpath"] = ((string)packageTable["cpath"]).Replace(";.\\?.dll", ""); + } + + EmulationLuaLibrary.FrameAdvanceCallback = FrameAdvance; + EmulationLuaLibrary.YieldCallback = EmuYield; + + EnumerateLuaFunctions(nameof(LuaCanvas), typeof(LuaCanvas), null); // add LuaCanvas to Lua function reference table + } + + private ApiContainer _apiContainer; + + private readonly DisplayManagerBase _displayManager; + + private GuiApi GuiAPI => (GuiApi)_apiContainer.Gui; + + private readonly InputManager _inputManager; + + private readonly MainForm _mainForm; + + private Lua _lua = new(); + private LuaThread _currThread; + + private readonly NLuaTableHelper _th; + + private static Action _logToLuaConsoleCallback = a => Console.WriteLine("a Lua lib is logging during init and the console lib hasn't been initialised yet"); + + private FormsLuaLibrary FormsLibrary => (FormsLuaLibrary)Libraries[typeof(FormsLuaLibrary)]; + + public LuaDocumentation Docs { get; } = new LuaDocumentation(); + + private EmulationLuaLibrary EmulationLuaLibrary => (EmulationLuaLibrary)Libraries[typeof(EmulationLuaLibrary)]; + + public string EngineName => "NLua+Lua"; + + public bool IsRebootingCore { get; set; } + + public bool IsUpdateSupressed { get; set; } + + private readonly IDictionary Libraries = new Dictionary(); + + private EventWaitHandle LuaWait; + + public PathEntryCollection PathEntries { get; private set; } + + public LuaFileList ScriptList { get; } + + private static void LogToLuaConsole(object outputs) => _logToLuaConsoleCallback(new[] { outputs }); + + public NLuaTableHelper GetTableHelper() => _th; + + public void Restart( + IEmulatorServiceProvider newServiceProvider, + Config config, + IEmulator emulator, + IGameInfo game) + { + _apiContainer = ApiManager.RestartLua(newServiceProvider, LogToLuaConsole, _mainForm, _displayManager, _inputManager, _mainForm.MovieSession, _mainForm.Tools, config, emulator, game); + PathEntries = config.PathEntries; + foreach (var lib in Libraries.Values) + { + lib.APIs = _apiContainer; + Debug.Assert(ServiceInjector.UpdateServices(newServiceProvider, lib, mayCache: true)); + lib.Restarted(); + } + } + + public bool FrameAdvanceRequested { get; private set; } + + public LuaFunctionList RegisteredFunctions { get; } + + public void CallSaveStateEvent(string name) + { + using var luaAutoUnlockHack = GuiAPI.ThisIsTheLuaAutoUnlockHack(); + + try + { + foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_SAVESTATE).ToList()) + { + lf.Call(name); + } + } + catch (Exception e) + { + LogToLuaConsole($"error running function attached by lua function event.onsavestate\nError message: {e.Message}"); + } + } + + public void CallLoadStateEvent(string name) + { + using var luaAutoUnlockHack = GuiAPI.ThisIsTheLuaAutoUnlockHack(); + + try + { + foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_LOADSTATE).ToList()) + { + lf.Call(name); + } + } + catch (Exception e) + { + LogToLuaConsole($"error running function attached by lua function event.onloadstate\nError message: {e.Message}"); + } + } + + public void CallFrameBeforeEvent() + { + if (IsUpdateSupressed) return; + + using var luaAutoUnlockHack = GuiAPI.ThisIsTheLuaAutoUnlockHack(); + + try + { + foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_PREFRAME).ToList()) + { + lf.Call(); + } + } + catch (Exception e) + { + LogToLuaConsole($"error running function attached by lua function event.onframestart\nError message: {e.Message}"); + } + } + + public void CallFrameAfterEvent() + { + if (IsUpdateSupressed) return; + + using var luaAutoUnlockHack = GuiAPI.ThisIsTheLuaAutoUnlockHack(); + + try + { + foreach (var lf in RegisteredFunctions.Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_POSTFRAME).ToList()) + { + lf.Call(); + } + } + catch (Exception e) + { + LogToLuaConsole($"error running function attached by lua function event.onframeend\nError message: {e.Message}"); + } + } + + public void CallExitEvent(LuaFile lf) + { + using var luaAutoUnlockHack = GuiAPI.ThisIsTheLuaAutoUnlockHack(); + + foreach (var exitCallback in RegisteredFunctions + .Where(l => l.Event == NamedLuaFunction.EVENT_TYPE_ENGINESTOP + && (l.LuaFile.Path == lf.Path || ReferenceEquals(l.LuaFile.Thread, lf.Thread))) + .ToList()) + { + exitCallback.Call(); + } + } + + public void Close() + { + foreach (var closeCallback in RegisteredFunctions + .Where(static l => l.Event == NamedLuaFunction.EVENT_TYPE_CONSOLECLOSE) + .ToList()) + { + closeCallback.Call(); + } + + RegisteredFunctions.Clear(_mainForm.Emulator); + ScriptList.Clear(); + FormsLibrary.DestroyAll(); + _lua.Dispose(); + _lua = null; + } + + public INamedLuaFunction CreateAndRegisterNamedFunction( + LuaFunction function, + string theEvent, + Action logCallback, + LuaFile luaFile, + string name = null) + { + var nlf = new NamedLuaFunction(function, theEvent, logCallback, luaFile, + () => { _lua.NewThread(out var thread); return thread; }, name); + RegisteredFunctions.Add(nlf); + return nlf; + } + + public bool RemoveNamedFunctionMatching(Func predicate) + { + var nlf = (NamedLuaFunction)RegisteredFunctions.FirstOrDefault(predicate); + if (nlf == null) return false; + RegisteredFunctions.Remove(nlf, _mainForm.Emulator); + return true; + } + + public LuaThread SpawnCoroutine(string file) + { + var content = File.ReadAllText(file); + var main = _lua.LoadString(content, "main"); + _lua.NewThread(main, out var ret); + return ret; + } + + public void SpawnAndSetFileThread(string pathToLoad, LuaFile lf) + => lf.Thread = SpawnCoroutine(pathToLoad); + + public void ExecuteString(string command) + => _lua.DoString(command); + + public (bool WaitForFrame, bool Terminated) ResumeScript(LuaFile lf) + { + _currThread = lf.Thread; + using var luaAutoUnlockHack = GuiAPI.ThisIsTheLuaAutoUnlockHack(); + + try + { + LuaLibraryBase.SetCurrentThread(lf); + + var execResult = _currThread.Resume(); + + _currThread = null; + var result = execResult switch + { + LuaStatus.OK => (WaitForFrame: false, Terminated: true), + LuaStatus.Yield => (WaitForFrame: FrameAdvanceRequested, Terminated: false), + _ => throw new InvalidOperationException($"{nameof(_currThread.Resume)}() returned {execResult}?") + }; + + FrameAdvanceRequested = false; + return result; + } + finally + { + LuaLibraryBase.ClearCurrentThread(); + } + } + + public static void Print(params object[] outputs) + { + _logToLuaConsoleCallback(outputs); + } + + private void FrameAdvance() + { + FrameAdvanceRequested = true; + _currThread.Yield(); + } + + private void EmuYield() + { + _currThread.Yield(); + } + } +}