From d1d7bc146327fa1ff835fddfb6d10339df5a4b5c Mon Sep 17 00:00:00 2001 From: Alessandro Proto Date: Wed, 25 Jan 2023 23:06:37 +0100 Subject: [PATCH] Completely rewritten the Lua runtime. Somehow fixed annoying bug in coroutine --- Capy64/BIOS/Bios.cs | 185 ---------------- Capy64/BIOS/PanicScreen.cs | 56 ----- Capy64/Capy64.cs | 7 +- Capy64/IGame.cs | 4 +- Capy64/LuaRuntime/ILuaEvent.cs | 7 - Capy64/LuaRuntime/LuaDelegateEvent.cs | 11 - Capy64/LuaRuntime/LuaEvent.cs | 8 - Capy64/LuaRuntime/Runtime.cs | 201 ------------------ Capy64/{LuaRuntime => Runtime}/Constants.cs | 2 +- .../Extensions/Libraries.cs | 2 +- .../Extensions/NativeLibraries.cs | 2 +- .../Extensions/Utils.cs | 2 +- .../InputEmitter.cs} | 111 +++++----- .../Libraries/Event.cs | 22 +- .../Libraries/FileSystem.cs | 6 +- .../{LuaRuntime => Runtime}/Libraries/GPU.cs | 4 +- .../{LuaRuntime => Runtime}/Libraries/HTTP.cs | 82 ++++--- .../{LuaRuntime => Runtime}/Libraries/OS.cs | 6 +- .../{LuaRuntime => Runtime}/Libraries/Term.cs | 2 +- .../Libraries/Timer.cs | 9 +- Capy64/Runtime/LuaEvent.cs | 20 ++ .../{LuaRuntime => Runtime}/LuaException.cs | 2 +- Capy64/Runtime/LuaState.cs | 148 +++++++++++++ .../Objects/GPUBuffer.cs | 2 +- .../Objects/Handlers/BinaryReadHandle.cs | 2 +- .../Objects/Handlers/BinaryWriteHandle.cs | 2 +- .../Objects/Handlers/IHandle.cs | 2 +- .../Objects/Handlers/ReadHandle.cs | 2 +- .../Objects/Handlers/WebSocketHandle.cs | 11 +- .../Objects/Handlers/WriteHandle.cs | 2 +- Capy64/Runtime/RuntimeManager.cs | 180 ++++++++++++++++ Capy64/{LuaRuntime => Runtime}/Sandbox.cs | 6 +- 32 files changed, 516 insertions(+), 592 deletions(-) delete mode 100644 Capy64/BIOS/Bios.cs delete mode 100644 Capy64/BIOS/PanicScreen.cs delete mode 100644 Capy64/LuaRuntime/ILuaEvent.cs delete mode 100644 Capy64/LuaRuntime/LuaDelegateEvent.cs delete mode 100644 Capy64/LuaRuntime/LuaEvent.cs delete mode 100644 Capy64/LuaRuntime/Runtime.cs rename Capy64/{LuaRuntime => Runtime}/Constants.cs (66%) rename Capy64/{LuaRuntime => Runtime}/Extensions/Libraries.cs (97%) rename Capy64/{LuaRuntime => Runtime}/Extensions/NativeLibraries.cs (98%) rename Capy64/{LuaRuntime => Runtime}/Extensions/Utils.cs (98%) rename Capy64/{BIOS/RuntimeInputEvents.cs => Runtime/InputEmitter.cs} (60%) rename Capy64/{LuaRuntime => Runtime}/Libraries/Event.cs (82%) rename Capy64/{LuaRuntime => Runtime}/Libraries/FileSystem.cs (99%) rename Capy64/{LuaRuntime => Runtime}/Libraries/GPU.cs (99%) rename Capy64/{LuaRuntime => Runtime}/Libraries/HTTP.cs (80%) rename Capy64/{LuaRuntime => Runtime}/Libraries/OS.cs (89%) rename Capy64/{LuaRuntime => Runtime}/Libraries/Term.cs (99%) rename Capy64/{LuaRuntime => Runtime}/Libraries/Timer.cs (87%) create mode 100644 Capy64/Runtime/LuaEvent.cs rename Capy64/{LuaRuntime => Runtime}/LuaException.cs (93%) create mode 100644 Capy64/Runtime/LuaState.cs rename Capy64/{LuaRuntime => Runtime}/Objects/GPUBuffer.cs (98%) rename Capy64/{LuaRuntime => Runtime}/Objects/Handlers/BinaryReadHandle.cs (99%) rename Capy64/{LuaRuntime => Runtime}/Objects/Handlers/BinaryWriteHandle.cs (99%) rename Capy64/{LuaRuntime => Runtime}/Objects/Handlers/IHandle.cs (68%) rename Capy64/{LuaRuntime => Runtime}/Objects/Handlers/ReadHandle.cs (98%) rename Capy64/{LuaRuntime => Runtime}/Objects/Handlers/WebSocketHandle.cs (89%) rename Capy64/{LuaRuntime => Runtime}/Objects/Handlers/WriteHandle.cs (98%) create mode 100644 Capy64/Runtime/RuntimeManager.cs rename Capy64/{LuaRuntime => Runtime}/Sandbox.cs (98%) diff --git a/Capy64/BIOS/Bios.cs b/Capy64/BIOS/Bios.cs deleted file mode 100644 index ac28322..0000000 --- a/Capy64/BIOS/Bios.cs +++ /dev/null @@ -1,185 +0,0 @@ -using Capy64.API; -using Capy64.Core; -using Capy64.Eventing; -using Capy64.Eventing.Events; -using Capy64.LuaRuntime; -using Capy64.LuaRuntime.Libraries; -using KeraLua; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; - -namespace Capy64.BIOS; - -public class Bios : IPlugin -{ - private static IGame _game; - private readonly EventEmitter _eventEmitter; - private RuntimeInputEvents _runtimeInputEvents; - private readonly Drawing _drawing; - private static bool CloseRuntime = false; - private static bool OpenBios = false; - - public Bios(IGame game) - { - _game = game; - _eventEmitter = game.EventEmitter; - _drawing = game.Drawing; - _game.EventEmitter.OnInit += OnInit; - _eventEmitter.OnTick += OnTick; - } - - private void OnInit(object sender, EventArgs e) - { - RunBIOS(); - } - - private void OnTick(object sender, TickEvent e) - { - if (CloseRuntime) - { - _runtimeInputEvents.Unregister(); - _game.LuaRuntime.Close(); - CloseRuntime = false; - - if (OpenBios) - { - OpenBios = false; - RunBIOS(); - } - else - { - StartLuaOS(); - } - } - - Resume(); - } - - private void RunBIOS() - { - _game.LuaRuntime = new(); - InitLuaPlugins(); - - _game.LuaRuntime.Thread.PushCFunction(L_OpenDataFolder); - _game.LuaRuntime.Thread.SetGlobal("openDataFolder"); - - _game.LuaRuntime.Thread.PushCFunction(L_InstallOS); - _game.LuaRuntime.Thread.SetGlobal("installOS"); - - _game.LuaRuntime.Thread.PushCFunction(L_Exit); - _game.LuaRuntime.Thread.SetGlobal("exit"); - - - var status = _game.LuaRuntime.Thread.LoadFile("Assets/bios.lua"); - if (status != LuaStatus.OK) - { - throw new LuaException(_game.LuaRuntime.Thread.ToString(-1)); - } - - _runtimeInputEvents = new RuntimeInputEvents(_eventEmitter, _game.LuaRuntime); - _runtimeInputEvents.Register(); - } - - private void StartLuaOS() - { - InstallOS(); - - try - { - _game.LuaRuntime = new Runtime(); - InitLuaPlugins(); - _game.LuaRuntime.Patch(); - _game.LuaRuntime.Init(); - _runtimeInputEvents = new(_eventEmitter, _game.LuaRuntime); - _runtimeInputEvents.Register(); - - } - catch (LuaException ex) - { - var panic = new PanicScreen(_game.Drawing); - _drawing.Begin(); - panic.Render("Cannot load operating system!", ex.Message); - _drawing.End(); - } - } - - public void Resume() - { - try - { - var yielded = _game.LuaRuntime.Resume(); - if (!yielded) - { - _game.Exit(); - } - } - catch (LuaException e) - { - Console.WriteLine(e); - var panic = new PanicScreen(_game.Drawing); - panic.Render(e.Message); - _runtimeInputEvents.Unregister(); - } - } - - private static void InitLuaPlugins() - { - var allPlugins = new List(_game.NativePlugins); - allPlugins.AddRange(_game.Plugins); - foreach (var plugin in allPlugins) - { - plugin.LuaInit(_game.LuaRuntime.Thread); - } - } - - public static void InstallOS(bool force = false) - { - var installedFilePath = Path.Combine(Capy64.AppDataPath, ".installed"); - if (!File.Exists(installedFilePath) || force) - { - FileSystem.CopyDirectory("Assets/Lua", FileSystem.DataPath, true, true); - File.Create(installedFilePath).Dispose(); - } - } - - public static void Shutdown() - { - _game.Exit(); - } - - public static void Reboot() - { - CloseRuntime = true; - OpenBios = true; - } - - private static int L_OpenDataFolder(IntPtr state) - { - var path = FileSystem.DataPath; - switch (Environment.OSVersion.Platform) - { - case PlatformID.Win32NT: - Process.Start("explorer.exe", path); - break; - case PlatformID.Unix: - Process.Start("xdg-open", path); - break; - } - - return 0; - } - - private static int L_InstallOS(IntPtr state) - { - InstallOS(true); - return 0; - } - - private static int L_Exit(IntPtr state) - { - CloseRuntime = true; - return 0; - } -} diff --git a/Capy64/BIOS/PanicScreen.cs b/Capy64/BIOS/PanicScreen.cs deleted file mode 100644 index a67bb9f..0000000 --- a/Capy64/BIOS/PanicScreen.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Capy64.Core; -using Capy64.LuaRuntime.Libraries; -using Microsoft.Xna.Framework; - -namespace Capy64.BIOS; - -public class PanicScreen -{ - public Color ForegroundColor = Color.White; - public Color BackgroundColor = new Color(0, 51, 187); - - private Drawing _drawing; - public PanicScreen(Drawing drawing) - { - _drawing = drawing; - } - - public void Render(string error, string details = null) - { - Term.ForegroundColor = ForegroundColor; - Term.BackgroundColor = BackgroundColor; - Term.SetCursorBlink(false); - Term.SetSize(57, 23); - Term.Clear(); - - var title = " Capy64 "; - var halfX = (Term.Width / 2) + 1; - Term.SetCursorPosition(halfX - (title.Length / 2), 2); - Term.ForegroundColor = BackgroundColor; - Term.BackgroundColor = ForegroundColor; - Term.Write(title); - - Term.ForegroundColor = ForegroundColor; - Term.BackgroundColor = BackgroundColor; - Term.SetCursorPosition(1, 4); - Print(error + '\n'); - - if (details is not null) - { - Print(details); - } - } - - private void Print(string txt) - { - foreach (var ch in txt) - { - Term.Write(ch.ToString()); - if (Term.CursorPosition.X >= Term.Width || ch == '\n') - { - Term.SetCursorPosition(1, (int)Term.CursorPosition.Y + 1); - } - } - Term.SetCursorPosition(1, (int)Term.CursorPosition.Y + 1); - } -} diff --git a/Capy64/Capy64.cs b/Capy64/Capy64.cs index 0ddee2f..65edb0c 100644 --- a/Capy64/Capy64.cs +++ b/Capy64/Capy64.cs @@ -2,7 +2,7 @@ using Capy64.Core; using Capy64.Eventing; using Capy64.Extensions; -using Capy64.LuaRuntime; +using Capy64.Runtime; using Capy64.PluginManager; using Microsoft.Extensions.DependencyInjection; using Microsoft.Xna.Framework; @@ -23,6 +23,7 @@ public class Capy64 : Game, IGame Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create), "Capy64"); + public static Capy64 Instance; public Capy64 Game => this; public IList NativePlugins { get; private set; } public IList Plugins { get; private set; } @@ -30,7 +31,7 @@ public class Capy64 : Game, IGame public int Height { get; set; } = 300; public float Scale { get; set; } = 2f; public Drawing Drawing { get; private set; } - public Runtime LuaRuntime { get; set; } + public LuaState LuaRuntime { get; set; } public EventEmitter EventEmitter { get; private set; } public Borders Borders = new() { @@ -50,6 +51,8 @@ public class Capy64 : Game, IGame public Capy64() { + Instance = this; + _graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; IsMouseVisible = true; diff --git a/Capy64/IGame.cs b/Capy64/IGame.cs index 0103b07..78e08b1 100644 --- a/Capy64/IGame.cs +++ b/Capy64/IGame.cs @@ -1,7 +1,7 @@ using Capy64.API; using Capy64.Core; using Capy64.Eventing; -using Capy64.LuaRuntime; +using Capy64.Runtime; using Microsoft.Xna.Framework; using System; using System.Collections.Generic; @@ -15,7 +15,7 @@ public interface IGame IList Plugins { get; } GameWindow Window { get; } Drawing Drawing { get; } - Runtime LuaRuntime { get; set; } + LuaState LuaRuntime { get; set; } EventEmitter EventEmitter { get; } void ConfigureServices(IServiceProvider serviceProvider); diff --git a/Capy64/LuaRuntime/ILuaEvent.cs b/Capy64/LuaRuntime/ILuaEvent.cs deleted file mode 100644 index 0818c92..0000000 --- a/Capy64/LuaRuntime/ILuaEvent.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Capy64.LuaRuntime; - -public interface ILuaEvent -{ - public string Name { get; set; } - public bool BypassFilter { get; set; } -} diff --git a/Capy64/LuaRuntime/LuaDelegateEvent.cs b/Capy64/LuaRuntime/LuaDelegateEvent.cs deleted file mode 100644 index ddbf1d4..0000000 --- a/Capy64/LuaRuntime/LuaDelegateEvent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using KeraLua; -using System; - -namespace Capy64.LuaRuntime; - -public class LuaDelegateEvent : ILuaEvent -{ - public string Name { get; set; } - public Func Handler { get; set; } - public bool BypassFilter { get; set; } = false; -} diff --git a/Capy64/LuaRuntime/LuaEvent.cs b/Capy64/LuaRuntime/LuaEvent.cs deleted file mode 100644 index 3cf8b65..0000000 --- a/Capy64/LuaRuntime/LuaEvent.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Capy64.LuaRuntime; - -public class LuaEvent : ILuaEvent -{ - public string Name { get; set; } - public object[] Parameters { get; set; } - public bool BypassFilter { get; set; } = false; -} diff --git a/Capy64/LuaRuntime/Runtime.cs b/Capy64/LuaRuntime/Runtime.cs deleted file mode 100644 index e38e0c7..0000000 --- a/Capy64/LuaRuntime/Runtime.cs +++ /dev/null @@ -1,201 +0,0 @@ -using Capy64.LuaRuntime.Extensions; -using Capy64.LuaRuntime.Libraries; -using KeraLua; -using System; -using System.Collections.Concurrent; -using System.IO; -using System.Linq; -using System.Text; -using System.Timers; - -namespace Capy64.LuaRuntime; - -public class Runtime -{ - private readonly ConcurrentQueue eventQueue = new(new LuaEvent[] - { - new() - { - Name = "init", - Parameters = Array.Empty() - } - }); - - private readonly Lua Parent; - public Lua Thread { get; private set; } - private string[] filters = Array.Empty(); - private bool _disposing = false; - private static System.Timers.Timer yieldTimeoutTimer = new(TimeSpan.FromSeconds(3)); - private static bool yieldTimedOut = false; - - public Runtime() - { - Parent = new(false) - { - Encoding = Encoding.UTF8, - }; - - Sandbox.OpenLibraries(Parent); - - Thread = Parent.NewThread(); - - Thread.SetHook(LH_YieldTimeout, LuaHookMask.Count, 7000); - yieldTimeoutTimer.Elapsed += (sender, ev) => - { - yieldTimedOut = true; - }; - } - - private static void LH_YieldTimeout(IntPtr state, IntPtr ar) - { - var L = Lua.FromIntPtr(state); - - if (yieldTimedOut) - { - L.Error("no yield timeout"); - Console.WriteLine("tick"); - } - } - - public void Patch() - { - Sandbox.Patch(Parent); - } - - public void Init() - { - Parent.SetTop(0); - var initContent = File.ReadAllText(Path.Combine(FileSystem.DataPath, "init.lua")); - var status = Thread.LoadString(initContent, "=init.lua"); - if (status != LuaStatus.OK) - { - throw new LuaException(Thread.ToString(-1)); - } - } - - public void PushEvent(LuaEvent ev) - { - eventQueue.Enqueue(ev); - } - - public void PushEvent(string name, params object[] pars) - { - eventQueue.Enqueue(new LuaEvent() { Name = name, Parameters = pars }); - } - - public void PushEvent(LuaDelegateEvent ev) - { - eventQueue.Enqueue(ev); - } - - /// - /// Push a new event to the event queue. - /// - /// Event name - /// Event handler. Push any needed parameter from here. Return n as amount of parameters pushed, minus event name. - /// For example: I push an event with 3 parameters, then I return 3. - /// - public void PushEvent(string name, Func handler) - { - eventQueue.Enqueue(new LuaDelegateEvent - { - Name = name, - Handler = handler - }); - } - - /// - /// Resume the Lua thread - /// - /// Whether it yielded - public bool Resume() - { - while (eventQueue.TryDequeue(out ILuaEvent ev)) - { - if (!ResumeThread(ev)) - return false; - } - - return true; - } - - private bool ResumeThread(ILuaEvent ev) - { - - - if (filters.Length > 0 && !filters.Contains(ev.Name)) - { - if (!ev.BypassFilter) - { - return true; - } - } - - filters = Array.Empty(); - var evpars = PushEventToStack(ev); - if (_disposing) - return false; - - yieldTimeoutTimer.Start(); - yieldTimedOut = false; - - var status = Thread.Resume(null, evpars, out int pars); - - yieldTimeoutTimer.Stop(); - if (status is LuaStatus.OK or LuaStatus.Yield) - { - if (_disposing) - return false; - if (status == LuaStatus.Yield) - { - filters = new string[pars]; - for (int i = 0; i < pars; i++) - { - filters[i] = Thread.OptString(i + 1, null); - } - } - Thread.Pop(pars); - return status == LuaStatus.Yield; - } - - var error = Thread.OptString(-1, "Unknown exception"); - Thread.Traceback(Thread); - var stacktrace = Thread.OptString(-1, ""); - - throw new LuaException($"Top thread exception:\n{error}\n{stacktrace}"); - } - - private int PushEventToStack(ILuaEvent ev) - { - Thread.PushString(ev.Name); - - switch (ev) - { - case LuaEvent e: - - if (e.Parameters != null) - { - foreach (var par in e.Parameters) - { - Thread.PushValue(par); - } - } - - return (e.Parameters?.Length ?? 0) + 1; - - case LuaDelegateEvent e: - - int n = e.Handler(Thread); - return n + 1; - - default: - throw new NotImplementedException(); - } - } - - public void Close() - { - _disposing = true; - Parent.Close(); - } -} diff --git a/Capy64/LuaRuntime/Constants.cs b/Capy64/Runtime/Constants.cs similarity index 66% rename from Capy64/LuaRuntime/Constants.cs rename to Capy64/Runtime/Constants.cs index dfbefbd..efcbba7 100644 --- a/Capy64/LuaRuntime/Constants.cs +++ b/Capy64/Runtime/Constants.cs @@ -1,4 +1,4 @@ -namespace Capy64.LuaRuntime; +namespace Capy64.Runtime; public class Constants { diff --git a/Capy64/LuaRuntime/Extensions/Libraries.cs b/Capy64/Runtime/Extensions/Libraries.cs similarity index 97% rename from Capy64/LuaRuntime/Extensions/Libraries.cs rename to Capy64/Runtime/Extensions/Libraries.cs index 90071af..3c6ed3d 100644 --- a/Capy64/LuaRuntime/Extensions/Libraries.cs +++ b/Capy64/Runtime/Extensions/Libraries.cs @@ -1,6 +1,6 @@ using KeraLua; -namespace Capy64.LuaRuntime.Extensions +namespace Capy64.Runtime.Extensions { static class LuaExtensions { diff --git a/Capy64/LuaRuntime/Extensions/NativeLibraries.cs b/Capy64/Runtime/Extensions/NativeLibraries.cs similarity index 98% rename from Capy64/LuaRuntime/Extensions/NativeLibraries.cs rename to Capy64/Runtime/Extensions/NativeLibraries.cs index ff30b78..fd37a2f 100644 --- a/Capy64/LuaRuntime/Extensions/NativeLibraries.cs +++ b/Capy64/Runtime/Extensions/NativeLibraries.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.InteropServices; -namespace Capy64.LuaRuntime.Extensions +namespace Capy64.Runtime.Extensions { internal static partial class NativeLibraries { diff --git a/Capy64/LuaRuntime/Extensions/Utils.cs b/Capy64/Runtime/Extensions/Utils.cs similarity index 98% rename from Capy64/LuaRuntime/Extensions/Utils.cs rename to Capy64/Runtime/Extensions/Utils.cs index ece813c..618220d 100644 --- a/Capy64/LuaRuntime/Extensions/Utils.cs +++ b/Capy64/Runtime/Extensions/Utils.cs @@ -4,7 +4,7 @@ using System.Collections; using System.Linq; using System.Reflection; -namespace Capy64.LuaRuntime.Extensions; +namespace Capy64.Runtime.Extensions; public static class Utils { public static void PushArray(this Lua L, object obj) diff --git a/Capy64/BIOS/RuntimeInputEvents.cs b/Capy64/Runtime/InputEmitter.cs similarity index 60% rename from Capy64/BIOS/RuntimeInputEvents.cs rename to Capy64/Runtime/InputEmitter.cs index e2aba8d..21d9519 100644 --- a/Capy64/BIOS/RuntimeInputEvents.cs +++ b/Capy64/Runtime/InputEmitter.cs @@ -1,21 +1,22 @@ using Capy64.Core; -using Capy64.Eventing; using Capy64.Eventing.Events; -using Capy64.LuaRuntime; +using Capy64.Eventing; using Microsoft.Xna.Framework.Input; +using System; using System.Linq; using static Capy64.Eventing.InputManager; +using Capy64.Runtime.Extensions; -namespace Capy64.BIOS; +namespace Capy64.Runtime; -internal class RuntimeInputEvents +internal class InputEmitter { private EventEmitter _eventEmitter; - private Runtime _runtime; + private LuaState _runtime; private const int rebootDelay = 30; private int heldReboot = 0; - public RuntimeInputEvents(EventEmitter eventEmitter, Runtime runtime) + public InputEmitter(EventEmitter eventEmitter, LuaState runtime) { _eventEmitter = eventEmitter; _runtime = runtime; @@ -74,66 +75,84 @@ internal class RuntimeInputEvents if (heldReboot >= rebootDelay) { heldReboot = 0; - Bios.Reboot(); + RuntimeManager.Reboot(); } } private void OnMouseUp(object sender, MouseButtonEvent e) { - _runtime.PushEvent("mouse_up", new object[] + _runtime.QueueEvent("mouse_up", LK => { - (int)e.Button, - e.Position.X, - e.Position.Y, + LK.PushInteger((int)e.Button); + LK.PushInteger(e.Position.X); + LK.PushInteger(e.Position.Y); + + return 3; }); } private void OnMouseDown(object sender, MouseButtonEvent e) { - _runtime.PushEvent("mouse_down", new object[] + _runtime.QueueEvent("mouse_down", LK => { - (int)e.Button, - e.Position.X, - e.Position.Y, + LK.PushInteger((int)e.Button); + LK.PushInteger(e.Position.X); + LK.PushInteger(e.Position.Y); + + return 3; }); } private void OnMouseMove(object sender, MouseMoveEvent e) { - _runtime.PushEvent("mouse_move", new object[] + _runtime.QueueEvent("mouse_move", LK => { - e.PressedButtons, - e.Position.X, - e.Position.Y, + LK.NewTable(); + for (int i = 1; i <= e.PressedButtons.Length; i++) + { + LK.PushInteger(e.PressedButtons[i - 1]); + LK.SetInteger(-2, i); + + } + LK.PushInteger(e.Position.X); + LK.PushInteger(e.Position.Y); + + return 3; }); } private void OnMouseWheel(object sender, MouseWheelEvent e) { - _runtime.PushEvent("mouse_scroll", new object[] + _runtime.QueueEvent("mouse_scroll", LK => { - e.Position.X, - e.Position.Y, - e.VerticalValue, - e.HorizontalValue, + LK.PushInteger(e.Position.X); + LK.PushInteger(e.Position.Y); + LK.PushInteger(e.VerticalValue); + LK.PushInteger(e.HorizontalValue); + + return 4; }); } private void OnKeyUp(object sender, KeyEvent e) { - _runtime.PushEvent("key_up", new object[] + _runtime.QueueEvent("key_up", LK => { - e.KeyCode, - e.KeyName, - (int)e.Mods + LK.PushInteger(e.KeyCode); + LK.PushString(e.KeyName); + LK.PushInteger((int)e.Mods); + + return 3; }); } private void OnKeyDown(object sender, KeyEvent e) { - _runtime.PushEvent("key_down", new object[] + _runtime.QueueEvent("key_down", LK => { - e.KeyCode, - e.KeyName, - (int)e.Mods, - e.IsHeld, + LK.PushInteger(e.KeyCode); + LK.PushString(e.KeyName); + LK.PushInteger((int)e.Mods); + LK.PushBoolean(e.IsHeld); + + return 4; }); if ((e.Mods & Modifiers.Ctrl) != Modifiers.None && !e.IsHeld) @@ -141,26 +160,17 @@ internal class RuntimeInputEvents if ((e.Mods & Modifiers.Alt) != Modifiers.None) { if (e.Key == Keys.C) - { - _runtime.PushEvent(new LuaEvent() - { - Name = "interrupt", - Parameters = { }, - BypassFilter = true, - }); - } + _runtime.QueueEvent("interrupt", LK => 0); } else if (e.Key == Keys.V) { if (SDL.HasClipboardText()) { var text = SDL.GetClipboardText(); - _runtime.PushEvent(new LuaEvent() - { - Name = "paste", - Parameters = new[] { - text, - }, + _runtime.QueueEvent("paste", LK => { + LK.PushString(text); + + return 1; }); } } @@ -169,9 +179,10 @@ internal class RuntimeInputEvents private void OnChar(object sender, CharEvent e) { - _runtime.PushEvent("char", new object[] - { - e.Character, + _runtime.QueueEvent("char", LK => { + LK.PushString(e.Character.ToString()); + + return 1; }); } } diff --git a/Capy64/LuaRuntime/Libraries/Event.cs b/Capy64/Runtime/Libraries/Event.cs similarity index 82% rename from Capy64/LuaRuntime/Libraries/Event.cs rename to Capy64/Runtime/Libraries/Event.cs index 7859586..e08f091 100644 --- a/Capy64/LuaRuntime/Libraries/Event.cs +++ b/Capy64/Runtime/Libraries/Event.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Capy64.LuaRuntime.Libraries; +namespace Capy64.Runtime.Libraries; public class Event : IPlugin { @@ -52,7 +52,7 @@ public class Event : IPlugin { var L = Lua.FromIntPtr(state); - if(L.ToString(1) == "interrupt") + if (L.ToString(1) == "interrupt") { L.Error("interrupt"); } @@ -67,7 +67,7 @@ public class Event : IPlugin var L = Lua.FromIntPtr(state); var nargs = L.GetTop(); - for(int i = 1; i <= nargs; i++) + for (int i = 1; i <= nargs; i++) { L.CheckString(i); } @@ -100,18 +100,14 @@ public class Event : IPlugin var nargs = L.GetTop(); - var evParsState = L.NewThread(); - - for(int i = 2; i <= nargs; i++) + _game.LuaRuntime.QueueEvent(eventName, LK => { - L.PushCopy(i); - } + for (int i = 2; i <= nargs; i++) + { + L.PushCopy(i); + } - L.XMove(evParsState, nargs - 1); - - _game.LuaRuntime.PushEvent(eventName, LK => - { - evParsState.XMove(LK, nargs - 1); + L.XMove(LK, nargs - 1); return nargs - 1; }); diff --git a/Capy64/LuaRuntime/Libraries/FileSystem.cs b/Capy64/Runtime/Libraries/FileSystem.cs similarity index 99% rename from Capy64/LuaRuntime/Libraries/FileSystem.cs rename to Capy64/Runtime/Libraries/FileSystem.cs index f746bb5..2cb2648 100644 --- a/Capy64/LuaRuntime/Libraries/FileSystem.cs +++ b/Capy64/Runtime/Libraries/FileSystem.cs @@ -1,13 +1,13 @@ using Capy64.API; -using Capy64.LuaRuntime.Extensions; -using Capy64.LuaRuntime.Objects.Handlers; +using Capy64.Runtime.Objects.Handlers; +using Capy64.Runtime.Extensions; using KeraLua; using System; using System.Collections.Generic; using System.IO; using System.Linq; -namespace Capy64.LuaRuntime.Libraries; +namespace Capy64.Runtime.Libraries; public class FileSystem : IPlugin { diff --git a/Capy64/LuaRuntime/Libraries/GPU.cs b/Capy64/Runtime/Libraries/GPU.cs similarity index 99% rename from Capy64/LuaRuntime/Libraries/GPU.cs rename to Capy64/Runtime/Libraries/GPU.cs index b8953de..da1845e 100644 --- a/Capy64/LuaRuntime/Libraries/GPU.cs +++ b/Capy64/Runtime/Libraries/GPU.cs @@ -1,11 +1,11 @@ using Capy64.API; -using Capy64.LuaRuntime.Objects; +using Capy64.Runtime.Objects; using KeraLua; using Microsoft.Xna.Framework; using System; using System.Collections.Generic; -namespace Capy64.LuaRuntime.Libraries; +namespace Capy64.Runtime.Libraries; public class GPU : IPlugin { diff --git a/Capy64/LuaRuntime/Libraries/HTTP.cs b/Capy64/Runtime/Libraries/HTTP.cs similarity index 80% rename from Capy64/LuaRuntime/Libraries/HTTP.cs rename to Capy64/Runtime/Libraries/HTTP.cs index dc7b1d7..03188be 100644 --- a/Capy64/LuaRuntime/Libraries/HTTP.cs +++ b/Capy64/Runtime/Libraries/HTTP.cs @@ -1,6 +1,6 @@ using Capy64.API; -using Capy64.LuaRuntime.Extensions; -using Capy64.LuaRuntime.Objects.Handlers; +using Capy64.Runtime.Extensions; +using Capy64.Runtime.Objects.Handlers; using KeraLua; using Microsoft.Extensions.Configuration; using System; @@ -11,7 +11,7 @@ using System.Net.WebSockets; using System.Text; using System.Threading; -namespace Capy64.LuaRuntime.Libraries; +namespace Capy64.Runtime.Libraries; #nullable enable public class HTTP : IPlugin { @@ -189,7 +189,13 @@ public class HTTP : IPlugin if (task.IsFaulted || task.IsCanceled) { - _game.LuaRuntime.PushEvent("http_failure", requestId, task.Exception?.Message); + _game.LuaRuntime.QueueEvent("http_failure", LK => + { + LK.PushInteger(requestId); + LK.PushString(task.Exception?.Message); + + return 2; + }); return; } @@ -203,41 +209,42 @@ public class HTTP : IPlugin else handler = new ReadHandle(stream); - _game.LuaRuntime.PushEvent("http_response", L => + _game.LuaRuntime.QueueEvent("http_response", LK => { // arg 1, request id - L.PushInteger(requestId); + LK.PushInteger(requestId); // arg 2, response data - L.NewTable(); + LK.NewTable(); - L.PushString("success"); - L.PushBoolean(response.IsSuccessStatusCode); - L.SetTable(-3); + LK.PushString("success"); + LK.PushBoolean(response.IsSuccessStatusCode); + LK.SetTable(-3); - L.PushString("statusCode"); - L.PushNumber((int)response.StatusCode); - L.SetTable(-3); + LK.PushString("statusCode"); + LK.PushNumber((int)response.StatusCode); + LK.SetTable(-3); - L.PushString("reasonPhrase"); - L.PushString(response.ReasonPhrase); - L.SetTable(-3); + LK.PushString("reasonPhrase"); + LK.PushString(response.ReasonPhrase); + LK.SetTable(-3); - L.PushString("headers"); - L.NewTable(); + LK.PushString("headers"); + LK.NewTable(); foreach (var header in response.Headers) { - L.PushString(header.Key); - L.PushArray(header.Value.ToArray()); - L.SetTable(-3); + LK.PushString(header.Key); + LK.PushArray(header.Value.ToArray()); + LK.SetTable(-3); } - L.SetTable(-3); + LK.SetTable(-3); - handler.Push(L, false); + handler.Push(LK, false); return 2; + }); //_game.LuaRuntime.PushEvent("http_response", requestId, response.IsSuccessStatusCode, content, (int)response.StatusCode, response.ReasonPhrase); @@ -316,7 +323,13 @@ public class HTTP : IPlugin { if (task.IsFaulted || task.IsCanceled) { - _game.LuaRuntime.PushEvent("websocket_failure", requestId, task.Exception?.Message); + _game.LuaRuntime.QueueEvent("websocket_failure", LK => + { + LK.PushInteger(requestId); + LK.PushString(task.Exception?.Message); + + return 2; + }); return; } @@ -325,11 +338,11 @@ public class HTTP : IPlugin var handle = new WebSocketHandle(wsClient, requestId, _game); WebSocketConnections.Add(handle); - _game.LuaRuntime.PushEvent("websocket_connect", L => + _game.LuaRuntime.QueueEvent("websocket_connect", LK => { - L.PushInteger(requestId); + LK.PushInteger(requestId); - handle.Push(L, true); + handle.Push(LK, true); return 2; }); @@ -342,7 +355,12 @@ public class HTTP : IPlugin if (result.MessageType == WebSocketMessageType.Close) { await wsClient.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None); - _game.LuaRuntime.PushEvent("websocket_close", requestId); + _game.LuaRuntime.QueueEvent("websocket_close", LK => + { + LK.PushInteger(requestId); + + return 1; + }); return; } else @@ -353,7 +371,13 @@ public class HTTP : IPlugin if (result.EndOfMessage) { - _game.LuaRuntime.PushEvent("websocket_message", requestId, builder.ToString()); + _game.LuaRuntime.QueueEvent("websocket_message", LK => + { + LK.PushInteger(requestId); + LK.PushString(builder.ToString()); + + return 2; + }); builder.Clear(); } } diff --git a/Capy64/LuaRuntime/Libraries/OS.cs b/Capy64/Runtime/Libraries/OS.cs similarity index 89% rename from Capy64/LuaRuntime/Libraries/OS.cs rename to Capy64/Runtime/Libraries/OS.cs index 7d69022..8a04778 100644 --- a/Capy64/LuaRuntime/Libraries/OS.cs +++ b/Capy64/Runtime/Libraries/OS.cs @@ -2,7 +2,7 @@ using KeraLua; using System; -namespace Capy64.LuaRuntime.Libraries; +namespace Capy64.Runtime.Libraries; public class OS : IPlugin { @@ -47,11 +47,11 @@ public class OS : IPlugin if (doReboot) { - BIOS.Bios.Reboot(); + RuntimeManager.Reboot(); } else { - BIOS.Bios.Shutdown(); + RuntimeManager.Shutdown(); } return 0; diff --git a/Capy64/LuaRuntime/Libraries/Term.cs b/Capy64/Runtime/Libraries/Term.cs similarity index 99% rename from Capy64/LuaRuntime/Libraries/Term.cs rename to Capy64/Runtime/Libraries/Term.cs index 442b402..6de70e0 100644 --- a/Capy64/LuaRuntime/Libraries/Term.cs +++ b/Capy64/Runtime/Libraries/Term.cs @@ -8,7 +8,7 @@ using System; using static Capy64.Utils; using static System.Formats.Asn1.AsnWriter; -namespace Capy64.LuaRuntime.Libraries; +namespace Capy64.Runtime.Libraries; internal class Term : IPlugin { diff --git a/Capy64/LuaRuntime/Libraries/Timer.cs b/Capy64/Runtime/Libraries/Timer.cs similarity index 87% rename from Capy64/LuaRuntime/Libraries/Timer.cs rename to Capy64/Runtime/Libraries/Timer.cs index 8efbd5b..db2dd50 100644 --- a/Capy64/LuaRuntime/Libraries/Timer.cs +++ b/Capy64/Runtime/Libraries/Timer.cs @@ -2,7 +2,7 @@ using KeraLua; using System; -namespace Capy64.LuaRuntime.Libraries; +namespace Capy64.Runtime.Libraries; class Timer : IPlugin { @@ -54,7 +54,12 @@ class Timer : IPlugin timer.Elapsed += (o, e) => { - _game.LuaRuntime.PushEvent("timer", timerId); + _game.LuaRuntime.QueueEvent("timer", LK => + { + LK.PushInteger(timerId); + + return 1; + }); }; L.PushInteger(timerId); diff --git a/Capy64/Runtime/LuaEvent.cs b/Capy64/Runtime/LuaEvent.cs new file mode 100644 index 0000000..f7bc468 --- /dev/null +++ b/Capy64/Runtime/LuaEvent.cs @@ -0,0 +1,20 @@ +using KeraLua; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Capy64.Runtime; + +public class LuaEvent +{ + public LuaEvent(string name, Func handler) + { + Name = name; + Handler = handler; + } + + public string Name { get; set; } + public Func Handler { get; set; } +} diff --git a/Capy64/LuaRuntime/LuaException.cs b/Capy64/Runtime/LuaException.cs similarity index 93% rename from Capy64/LuaRuntime/LuaException.cs rename to Capy64/Runtime/LuaException.cs index 7ba3929..b22d3e0 100644 --- a/Capy64/LuaRuntime/LuaException.cs +++ b/Capy64/Runtime/LuaException.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.Serialization; -namespace Capy64.LuaRuntime; +namespace Capy64.Runtime; public class LuaException : Exception { diff --git a/Capy64/Runtime/LuaState.cs b/Capy64/Runtime/LuaState.cs new file mode 100644 index 0000000..ceaea66 --- /dev/null +++ b/Capy64/Runtime/LuaState.cs @@ -0,0 +1,148 @@ +using Capy64.API; +using KeraLua; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace Capy64.Runtime; + +public class LuaState : IDisposable +{ + public Lua Thread; + private Lua _parent; + + private Queue _queue = new(); + + private string[] _eventFilters = Array.Empty(); + + public LuaState() + { + _parent = new Lua(false) + { + Encoding = Encoding.UTF8, + }; + + Sandbox.OpenLibraries(_parent); + Sandbox.Patch(_parent); + + Thread = _parent.NewThread(); + + InitPlugins(); + + Thread.SetTop(0); + } + + private void InitPlugins() + { + var allPlugins = new List(Capy64.Instance.NativePlugins); + allPlugins.AddRange(Capy64.Instance.Plugins); + foreach (var plugin in allPlugins) + { + plugin.LuaInit(Thread); + } + } + + public void QueueEvent(string eventName, Func handler) + { + _queue.Enqueue(new(eventName, handler)); + } + + /// + /// Process all events in the queue. + /// + /// Whether the thread can still be resumed and is not finished + public bool ProcessQueue() + { + while (Dequeue(out var npars)) + { + if (!Resume(npars)) + { + return false; + } + } + + return true; + } + + /// + /// Dequeue the state with event parameters at head of queue + /// and push values to thread + /// + /// Amount of parameters of event + /// If an event is dequeued + private bool Dequeue(out int npars) + { + npars = 0; + + if (_queue.Count == 0) + return false; + + var ev = _queue.Dequeue(); + + Thread.PushString(ev.Name); + npars = 1; + + npars += ev.Handler(Thread); + + return true; + } + + /// + /// Resume the Lua thread + /// + /// If yieldable + /// + private bool Resume(int npars) + { + //Sandbox.DumpStack(Thread); + var eventName = Thread.ToString(1); + if (_eventFilters.Length != 0 && !_eventFilters.Contains(eventName) && eventName != "interrupt") + { + Thread.Pop(npars); + return true; + } + + var status = Thread.Resume(null, npars, out var nresults); + + // the Lua script is finished, there's nothing else to resume + if (status == LuaStatus.OK) + return false; + + if (status == LuaStatus.Yield) + { + _eventFilters = new string[nresults]; + for (var i = 1; i <= nresults; i++) + { + _eventFilters[i - 1] = Thread.ToString(i); + } + + Thread.Pop(nresults); + + return true; + } + + // something bad happened + var error = Thread.ToString(-1); + Thread.Traceback(Thread); + string stacktrace = Thread.OptString(-1, null); + + var builder = new StringBuilder(); + builder.AppendLine(error); + if (!string.IsNullOrWhiteSpace(stacktrace)) + { + builder.Append(stacktrace); + } + + throw new LuaException(builder.ToString()); + } + + public void Dispose() + { + _queue.Clear(); + Thread.Close(); + _parent.Close(); + } +} diff --git a/Capy64/LuaRuntime/Objects/GPUBuffer.cs b/Capy64/Runtime/Objects/GPUBuffer.cs similarity index 98% rename from Capy64/LuaRuntime/Objects/GPUBuffer.cs rename to Capy64/Runtime/Objects/GPUBuffer.cs index dea6889..20442f7 100644 --- a/Capy64/LuaRuntime/Objects/GPUBuffer.cs +++ b/Capy64/Runtime/Objects/GPUBuffer.cs @@ -1,7 +1,7 @@ using KeraLua; using System; -namespace Capy64.LuaRuntime.Objects; +namespace Capy64.Runtime.Objects; public class GPUBuffer { diff --git a/Capy64/LuaRuntime/Objects/Handlers/BinaryReadHandle.cs b/Capy64/Runtime/Objects/Handlers/BinaryReadHandle.cs similarity index 99% rename from Capy64/LuaRuntime/Objects/Handlers/BinaryReadHandle.cs rename to Capy64/Runtime/Objects/Handlers/BinaryReadHandle.cs index 3975fe0..8290816 100644 --- a/Capy64/LuaRuntime/Objects/Handlers/BinaryReadHandle.cs +++ b/Capy64/Runtime/Objects/Handlers/BinaryReadHandle.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Text; -namespace Capy64.LuaRuntime.Objects.Handlers; +namespace Capy64.Runtime.Objects.Handlers; public class BinaryReadHandle : IHandle { diff --git a/Capy64/LuaRuntime/Objects/Handlers/BinaryWriteHandle.cs b/Capy64/Runtime/Objects/Handlers/BinaryWriteHandle.cs similarity index 99% rename from Capy64/LuaRuntime/Objects/Handlers/BinaryWriteHandle.cs rename to Capy64/Runtime/Objects/Handlers/BinaryWriteHandle.cs index 494340c..e1b6388 100644 --- a/Capy64/LuaRuntime/Objects/Handlers/BinaryWriteHandle.cs +++ b/Capy64/Runtime/Objects/Handlers/BinaryWriteHandle.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Text; -namespace Capy64.LuaRuntime.Objects.Handlers; +namespace Capy64.Runtime.Objects.Handlers; public class BinaryWriteHandle : IHandle { diff --git a/Capy64/LuaRuntime/Objects/Handlers/IHandle.cs b/Capy64/Runtime/Objects/Handlers/IHandle.cs similarity index 68% rename from Capy64/LuaRuntime/Objects/Handlers/IHandle.cs rename to Capy64/Runtime/Objects/Handlers/IHandle.cs index ecf02c8..f9fe840 100644 --- a/Capy64/LuaRuntime/Objects/Handlers/IHandle.cs +++ b/Capy64/Runtime/Objects/Handlers/IHandle.cs @@ -1,6 +1,6 @@ using KeraLua; -namespace Capy64.LuaRuntime.Objects.Handlers; +namespace Capy64.Runtime.Objects.Handlers; public interface IHandle { diff --git a/Capy64/LuaRuntime/Objects/Handlers/ReadHandle.cs b/Capy64/Runtime/Objects/Handlers/ReadHandle.cs similarity index 98% rename from Capy64/LuaRuntime/Objects/Handlers/ReadHandle.cs rename to Capy64/Runtime/Objects/Handlers/ReadHandle.cs index 7719df6..b8b7cc6 100644 --- a/Capy64/LuaRuntime/Objects/Handlers/ReadHandle.cs +++ b/Capy64/Runtime/Objects/Handlers/ReadHandle.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using System.IO; -namespace Capy64.LuaRuntime.Objects.Handlers; +namespace Capy64.Runtime.Objects.Handlers; public class ReadHandle : IHandle { diff --git a/Capy64/LuaRuntime/Objects/Handlers/WebSocketHandle.cs b/Capy64/Runtime/Objects/Handlers/WebSocketHandle.cs similarity index 89% rename from Capy64/LuaRuntime/Objects/Handlers/WebSocketHandle.cs rename to Capy64/Runtime/Objects/Handlers/WebSocketHandle.cs index 702150b..6c688e6 100644 --- a/Capy64/LuaRuntime/Objects/Handlers/WebSocketHandle.cs +++ b/Capy64/Runtime/Objects/Handlers/WebSocketHandle.cs @@ -1,4 +1,4 @@ -using Capy64.LuaRuntime.Libraries; +using Capy64.Runtime.Libraries; using KeraLua; using System; using System.Collections.Generic; @@ -8,7 +8,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -namespace Capy64.LuaRuntime.Objects.Handlers; +namespace Capy64.Runtime.Objects.Handlers; public class WebSocketHandle : IHandle { @@ -92,7 +92,12 @@ public class WebSocketHandle : IHandle .ContinueWith(async task => { await task; - _game.LuaRuntime.PushEvent("websocket_close", h._requestId); + _game.LuaRuntime.QueueEvent("websocket_close", LK => + { + LK.PushInteger(h._requestId); + + return 1; + }); }); HTTP.WebSocketConnections.Remove(h); diff --git a/Capy64/LuaRuntime/Objects/Handlers/WriteHandle.cs b/Capy64/Runtime/Objects/Handlers/WriteHandle.cs similarity index 98% rename from Capy64/LuaRuntime/Objects/Handlers/WriteHandle.cs rename to Capy64/Runtime/Objects/Handlers/WriteHandle.cs index b0c16d5..f15b203 100644 --- a/Capy64/LuaRuntime/Objects/Handlers/WriteHandle.cs +++ b/Capy64/Runtime/Objects/Handlers/WriteHandle.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using System.IO; -namespace Capy64.LuaRuntime.Objects.Handlers; +namespace Capy64.Runtime.Objects.Handlers; public class WriteHandle : IHandle { diff --git a/Capy64/Runtime/RuntimeManager.cs b/Capy64/Runtime/RuntimeManager.cs new file mode 100644 index 0000000..67fbc1f --- /dev/null +++ b/Capy64/Runtime/RuntimeManager.cs @@ -0,0 +1,180 @@ +using Capy64.API; +using Capy64.Eventing.Events; +using Capy64.Runtime.Libraries; +using KeraLua; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Capy64.Runtime; + +internal class RuntimeManager : IPlugin +{ + private LuaState luaState; + private InputEmitter emitter; + private int step = 0; + + private static bool close = false; + + private static IGame _game; + public RuntimeManager(IGame game) + { + _game = game; + + _game.EventEmitter.OnInit += OnInit; + _game.EventEmitter.OnTick += OnTick; + } + + /// + /// Start Capy64 state. + /// First BIOS, then OS + /// + private void Start() + { + emitter?.Unregister(); + + luaState?.Dispose(); + luaState = null; + close = false; + + switch (step++) + { + case 0: + InitBIOS(); + break; + case 1: + InitOS(); + break; + default: + step = 0; + Start(); + break; + } + } + + private void Resume() + { + if (close || !luaState.ProcessQueue()) + { + Start(); + } + } + + private void InitBIOS() + { + luaState = new LuaState(); + _game.LuaRuntime = luaState; + + emitter = new(_game.EventEmitter, luaState); + + luaState.QueueEvent("init", LK => + { + LK.PushInteger(step); + return 1; + }); + + emitter.Register(); + + luaState.Thread.PushCFunction(L_OpenDataFolder); + luaState.Thread.SetGlobal("openDataFolder"); + + luaState.Thread.PushCFunction(L_InstallOS); + luaState.Thread.SetGlobal("installOS"); + + luaState.Thread.PushCFunction(L_Exit); + luaState.Thread.SetGlobal("exit"); + + var status = luaState.Thread.LoadFile("Assets/bios.lua"); + if (status != LuaStatus.OK) + { + throw new LuaException(luaState.Thread.ToString(-1)); + } + } + + private void InitOS() + { + luaState = new LuaState(); + _game.LuaRuntime = luaState; + + emitter = new(_game.EventEmitter, luaState); + + luaState.QueueEvent("init", LK => + { + LK.PushInteger(step); + return 1; + }); + + emitter.Register(); + + var initContent = File.ReadAllText(Path.Combine(FileSystem.DataPath, "init.lua")); + var status = luaState.Thread.LoadString(initContent, "=init.lua"); + if (status != LuaStatus.OK) + { + throw new LuaException(luaState.Thread.ToString(-1)); + } + } + + public static void Reboot() + { + close = true; + } + + public static void Shutdown() + { + close = true; + _game.Exit(); + } + + public static void InstallOS(bool force = false) + { + var installedFilePath = Path.Combine(Capy64.AppDataPath, ".installed"); + if (!File.Exists(installedFilePath) || force) + { + FileSystem.CopyDirectory("Assets/Lua", FileSystem.DataPath, true, true); + File.Create(installedFilePath).Dispose(); + } + } + + private static int L_OpenDataFolder(IntPtr state) + { + var path = FileSystem.DataPath; + switch (Environment.OSVersion.Platform) + { + case PlatformID.Win32NT: + Process.Start("explorer.exe", path); + break; + case PlatformID.Unix: + Process.Start("xdg-open", path); + break; + } + + return 0; + } + + private static int L_InstallOS(IntPtr state) + { + InstallOS(true); + return 0; + } + + private static int L_Exit(IntPtr state) + { + close = true; + return 0; + } + + private void OnInit(object sender, EventArgs e) + { + Start(); + } + + private void OnTick(object sender, TickEvent e) + { + Resume(); + } +} diff --git a/Capy64/LuaRuntime/Sandbox.cs b/Capy64/Runtime/Sandbox.cs similarity index 98% rename from Capy64/LuaRuntime/Sandbox.cs rename to Capy64/Runtime/Sandbox.cs index 44afcfb..6e1872c 100644 --- a/Capy64/LuaRuntime/Sandbox.cs +++ b/Capy64/Runtime/Sandbox.cs @@ -1,12 +1,12 @@ -using Capy64.LuaRuntime.Extensions; -using Capy64.LuaRuntime.Libraries; +using Capy64.Runtime.Extensions; +using Capy64.Runtime.Libraries; using KeraLua; using System; using System.IO; using System.Linq; using System.Text; -namespace Capy64.LuaRuntime; +namespace Capy64.Runtime; internal class Sandbox {