mirror of
https://github.com/Ale32bit/Capy64.git
synced 2025-01-18 10:36:44 +00:00
Completely rewritten the Lua runtime.
Somehow fixed annoying bug in coroutine
This commit is contained in:
parent
c270bbe8bd
commit
d1d7bc1463
32 changed files with 516 additions and 592 deletions
|
@ -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<IPlugin>(_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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<IPlugin> NativePlugins { get; private set; }
|
||||
public IList<IPlugin> 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;
|
||||
|
|
|
@ -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<IPlugin> Plugins { get; }
|
||||
GameWindow Window { get; }
|
||||
Drawing Drawing { get; }
|
||||
Runtime LuaRuntime { get; set; }
|
||||
LuaState LuaRuntime { get; set; }
|
||||
EventEmitter EventEmitter { get; }
|
||||
void ConfigureServices(IServiceProvider serviceProvider);
|
||||
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
namespace Capy64.LuaRuntime;
|
||||
|
||||
public interface ILuaEvent
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public bool BypassFilter { get; set; }
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
using KeraLua;
|
||||
using System;
|
||||
|
||||
namespace Capy64.LuaRuntime;
|
||||
|
||||
public class LuaDelegateEvent : ILuaEvent
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public Func<Lua, int> Handler { get; set; }
|
||||
public bool BypassFilter { get; set; } = false;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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<ILuaEvent> eventQueue = new(new LuaEvent[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
Name = "init",
|
||||
Parameters = Array.Empty<object>()
|
||||
}
|
||||
});
|
||||
|
||||
private readonly Lua Parent;
|
||||
public Lua Thread { get; private set; }
|
||||
private string[] filters = Array.Empty<string>();
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Push a new event to the event queue.
|
||||
/// </summary>
|
||||
/// <param name="name">Event name</param>
|
||||
/// <param name="handler">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.
|
||||
/// </param>
|
||||
public void PushEvent(string name, Func<Lua, int> handler)
|
||||
{
|
||||
eventQueue.Enqueue(new LuaDelegateEvent
|
||||
{
|
||||
Name = name,
|
||||
Handler = handler
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resume the Lua thread
|
||||
/// </summary>
|
||||
/// <returns>Whether it yielded</returns>
|
||||
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<string>();
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
namespace Capy64.LuaRuntime;
|
||||
namespace Capy64.Runtime;
|
||||
|
||||
public class Constants
|
||||
{
|
|
@ -1,6 +1,6 @@
|
|||
using KeraLua;
|
||||
|
||||
namespace Capy64.LuaRuntime.Extensions
|
||||
namespace Capy64.Runtime.Extensions
|
||||
{
|
||||
static class LuaExtensions
|
||||
{
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Capy64.LuaRuntime.Extensions
|
||||
namespace Capy64.Runtime.Extensions
|
||||
{
|
||||
internal static partial class NativeLibraries
|
||||
{
|
|
@ -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)
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
});
|
|
@ -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
|
||||
{
|
|
@ -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
|
||||
{
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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
|
||||
{
|
|
@ -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);
|
20
Capy64/Runtime/LuaEvent.cs
Normal file
20
Capy64/Runtime/LuaEvent.cs
Normal file
|
@ -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<Lua, int> handler)
|
||||
{
|
||||
Name = name;
|
||||
Handler = handler;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public Func<Lua, int> Handler { get; set; }
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Capy64.LuaRuntime;
|
||||
namespace Capy64.Runtime;
|
||||
|
||||
public class LuaException : Exception
|
||||
{
|
148
Capy64/Runtime/LuaState.cs
Normal file
148
Capy64/Runtime/LuaState.cs
Normal file
|
@ -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<LuaEvent> _queue = new();
|
||||
|
||||
private string[] _eventFilters = Array.Empty<string>();
|
||||
|
||||
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<IPlugin>(Capy64.Instance.NativePlugins);
|
||||
allPlugins.AddRange(Capy64.Instance.Plugins);
|
||||
foreach (var plugin in allPlugins)
|
||||
{
|
||||
plugin.LuaInit(Thread);
|
||||
}
|
||||
}
|
||||
|
||||
public void QueueEvent(string eventName, Func<Lua, int> handler)
|
||||
{
|
||||
_queue.Enqueue(new(eventName, handler));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process all events in the queue.
|
||||
/// </summary>
|
||||
/// <returns>Whether the thread can still be resumed and is not finished</returns>
|
||||
public bool ProcessQueue()
|
||||
{
|
||||
while (Dequeue(out var npars))
|
||||
{
|
||||
if (!Resume(npars))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dequeue the state with event parameters at head of queue
|
||||
/// and push values to thread
|
||||
/// </summary>
|
||||
/// <param name="npars">Amount of parameters of event</param>
|
||||
/// <returns>If an event is dequeued</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resume the Lua thread
|
||||
/// </summary>
|
||||
/// <returns>If yieldable</returns>
|
||||
/// <exception cref="LuaException"></exception>
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
using KeraLua;
|
||||
using System;
|
||||
|
||||
namespace Capy64.LuaRuntime.Objects;
|
||||
namespace Capy64.Runtime.Objects;
|
||||
|
||||
public class GPUBuffer
|
||||
{
|
|
@ -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
|
||||
{
|
|
@ -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
|
||||
{
|
|
@ -1,6 +1,6 @@
|
|||
using KeraLua;
|
||||
|
||||
namespace Capy64.LuaRuntime.Objects.Handlers;
|
||||
namespace Capy64.Runtime.Objects.Handlers;
|
||||
|
||||
public interface IHandle
|
||||
{
|
|
@ -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
|
||||
{
|
|
@ -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);
|
|
@ -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
|
||||
{
|
180
Capy64/Runtime/RuntimeManager.cs
Normal file
180
Capy64/Runtime/RuntimeManager.cs
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start Capy64 state.
|
||||
/// First BIOS, then OS
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
Loading…
Reference in a new issue