mirror of
https://github.com/Ale32bit/Capy64.git
synced 2025-01-18 18:46:43 +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.Core;
|
||||||
using Capy64.Eventing;
|
using Capy64.Eventing;
|
||||||
using Capy64.Extensions;
|
using Capy64.Extensions;
|
||||||
using Capy64.LuaRuntime;
|
using Capy64.Runtime;
|
||||||
using Capy64.PluginManager;
|
using Capy64.PluginManager;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
|
@ -23,6 +23,7 @@ public class Capy64 : Game, IGame
|
||||||
Environment.SpecialFolder.ApplicationData,
|
Environment.SpecialFolder.ApplicationData,
|
||||||
Environment.SpecialFolderOption.Create),
|
Environment.SpecialFolderOption.Create),
|
||||||
"Capy64");
|
"Capy64");
|
||||||
|
public static Capy64 Instance;
|
||||||
public Capy64 Game => this;
|
public Capy64 Game => this;
|
||||||
public IList<IPlugin> NativePlugins { get; private set; }
|
public IList<IPlugin> NativePlugins { get; private set; }
|
||||||
public IList<IPlugin> Plugins { 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 int Height { get; set; } = 300;
|
||||||
public float Scale { get; set; } = 2f;
|
public float Scale { get; set; } = 2f;
|
||||||
public Drawing Drawing { get; private set; }
|
public Drawing Drawing { get; private set; }
|
||||||
public Runtime LuaRuntime { get; set; }
|
public LuaState LuaRuntime { get; set; }
|
||||||
public EventEmitter EventEmitter { get; private set; }
|
public EventEmitter EventEmitter { get; private set; }
|
||||||
public Borders Borders = new()
|
public Borders Borders = new()
|
||||||
{
|
{
|
||||||
|
@ -50,6 +51,8 @@ public class Capy64 : Game, IGame
|
||||||
|
|
||||||
public Capy64()
|
public Capy64()
|
||||||
{
|
{
|
||||||
|
Instance = this;
|
||||||
|
|
||||||
_graphics = new GraphicsDeviceManager(this);
|
_graphics = new GraphicsDeviceManager(this);
|
||||||
Content.RootDirectory = "Content";
|
Content.RootDirectory = "Content";
|
||||||
IsMouseVisible = true;
|
IsMouseVisible = true;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using Capy64.API;
|
using Capy64.API;
|
||||||
using Capy64.Core;
|
using Capy64.Core;
|
||||||
using Capy64.Eventing;
|
using Capy64.Eventing;
|
||||||
using Capy64.LuaRuntime;
|
using Capy64.Runtime;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -15,7 +15,7 @@ public interface IGame
|
||||||
IList<IPlugin> Plugins { get; }
|
IList<IPlugin> Plugins { get; }
|
||||||
GameWindow Window { get; }
|
GameWindow Window { get; }
|
||||||
Drawing Drawing { get; }
|
Drawing Drawing { get; }
|
||||||
Runtime LuaRuntime { get; set; }
|
LuaState LuaRuntime { get; set; }
|
||||||
EventEmitter EventEmitter { get; }
|
EventEmitter EventEmitter { get; }
|
||||||
void ConfigureServices(IServiceProvider serviceProvider);
|
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
|
public class Constants
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using KeraLua;
|
using KeraLua;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Extensions
|
namespace Capy64.Runtime.Extensions
|
||||||
{
|
{
|
||||||
static class LuaExtensions
|
static class LuaExtensions
|
||||||
{
|
{
|
|
@ -1,7 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Extensions
|
namespace Capy64.Runtime.Extensions
|
||||||
{
|
{
|
||||||
internal static partial class NativeLibraries
|
internal static partial class NativeLibraries
|
||||||
{
|
{
|
|
@ -4,7 +4,7 @@ using System.Collections;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Extensions;
|
namespace Capy64.Runtime.Extensions;
|
||||||
public static class Utils
|
public static class Utils
|
||||||
{
|
{
|
||||||
public static void PushArray(this Lua L, object obj)
|
public static void PushArray(this Lua L, object obj)
|
|
@ -1,21 +1,22 @@
|
||||||
using Capy64.Core;
|
using Capy64.Core;
|
||||||
using Capy64.Eventing;
|
|
||||||
using Capy64.Eventing.Events;
|
using Capy64.Eventing.Events;
|
||||||
using Capy64.LuaRuntime;
|
using Capy64.Eventing;
|
||||||
using Microsoft.Xna.Framework.Input;
|
using Microsoft.Xna.Framework.Input;
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using static Capy64.Eventing.InputManager;
|
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 EventEmitter _eventEmitter;
|
||||||
private Runtime _runtime;
|
private LuaState _runtime;
|
||||||
private const int rebootDelay = 30;
|
private const int rebootDelay = 30;
|
||||||
private int heldReboot = 0;
|
private int heldReboot = 0;
|
||||||
|
|
||||||
public RuntimeInputEvents(EventEmitter eventEmitter, Runtime runtime)
|
public InputEmitter(EventEmitter eventEmitter, LuaState runtime)
|
||||||
{
|
{
|
||||||
_eventEmitter = eventEmitter;
|
_eventEmitter = eventEmitter;
|
||||||
_runtime = runtime;
|
_runtime = runtime;
|
||||||
|
@ -74,66 +75,84 @@ internal class RuntimeInputEvents
|
||||||
if (heldReboot >= rebootDelay)
|
if (heldReboot >= rebootDelay)
|
||||||
{
|
{
|
||||||
heldReboot = 0;
|
heldReboot = 0;
|
||||||
Bios.Reboot();
|
RuntimeManager.Reboot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMouseUp(object sender, MouseButtonEvent e)
|
private void OnMouseUp(object sender, MouseButtonEvent e)
|
||||||
{
|
{
|
||||||
_runtime.PushEvent("mouse_up", new object[]
|
_runtime.QueueEvent("mouse_up", LK =>
|
||||||
{
|
{
|
||||||
(int)e.Button,
|
LK.PushInteger((int)e.Button);
|
||||||
e.Position.X,
|
LK.PushInteger(e.Position.X);
|
||||||
e.Position.Y,
|
LK.PushInteger(e.Position.Y);
|
||||||
|
|
||||||
|
return 3;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
private void OnMouseDown(object sender, MouseButtonEvent e)
|
private void OnMouseDown(object sender, MouseButtonEvent e)
|
||||||
{
|
{
|
||||||
_runtime.PushEvent("mouse_down", new object[]
|
_runtime.QueueEvent("mouse_down", LK =>
|
||||||
{
|
{
|
||||||
(int)e.Button,
|
LK.PushInteger((int)e.Button);
|
||||||
e.Position.X,
|
LK.PushInteger(e.Position.X);
|
||||||
e.Position.Y,
|
LK.PushInteger(e.Position.Y);
|
||||||
|
|
||||||
|
return 3;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMouseMove(object sender, MouseMoveEvent e)
|
private void OnMouseMove(object sender, MouseMoveEvent e)
|
||||||
{
|
{
|
||||||
_runtime.PushEvent("mouse_move", new object[]
|
_runtime.QueueEvent("mouse_move", LK =>
|
||||||
{
|
{
|
||||||
e.PressedButtons,
|
LK.NewTable();
|
||||||
e.Position.X,
|
for (int i = 1; i <= e.PressedButtons.Length; i++)
|
||||||
e.Position.Y,
|
{
|
||||||
|
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)
|
private void OnMouseWheel(object sender, MouseWheelEvent e)
|
||||||
{
|
{
|
||||||
_runtime.PushEvent("mouse_scroll", new object[]
|
_runtime.QueueEvent("mouse_scroll", LK =>
|
||||||
{
|
{
|
||||||
e.Position.X,
|
LK.PushInteger(e.Position.X);
|
||||||
e.Position.Y,
|
LK.PushInteger(e.Position.Y);
|
||||||
e.VerticalValue,
|
LK.PushInteger(e.VerticalValue);
|
||||||
e.HorizontalValue,
|
LK.PushInteger(e.HorizontalValue);
|
||||||
|
|
||||||
|
return 4;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnKeyUp(object sender, KeyEvent e)
|
private void OnKeyUp(object sender, KeyEvent e)
|
||||||
{
|
{
|
||||||
_runtime.PushEvent("key_up", new object[]
|
_runtime.QueueEvent("key_up", LK =>
|
||||||
{
|
{
|
||||||
e.KeyCode,
|
LK.PushInteger(e.KeyCode);
|
||||||
e.KeyName,
|
LK.PushString(e.KeyName);
|
||||||
(int)e.Mods
|
LK.PushInteger((int)e.Mods);
|
||||||
|
|
||||||
|
return 3;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
private void OnKeyDown(object sender, KeyEvent e)
|
private void OnKeyDown(object sender, KeyEvent e)
|
||||||
{
|
{
|
||||||
_runtime.PushEvent("key_down", new object[]
|
_runtime.QueueEvent("key_down", LK =>
|
||||||
{
|
{
|
||||||
e.KeyCode,
|
LK.PushInteger(e.KeyCode);
|
||||||
e.KeyName,
|
LK.PushString(e.KeyName);
|
||||||
(int)e.Mods,
|
LK.PushInteger((int)e.Mods);
|
||||||
e.IsHeld,
|
LK.PushBoolean(e.IsHeld);
|
||||||
|
|
||||||
|
return 4;
|
||||||
});
|
});
|
||||||
|
|
||||||
if ((e.Mods & Modifiers.Ctrl) != Modifiers.None && !e.IsHeld)
|
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.Mods & Modifiers.Alt) != Modifiers.None)
|
||||||
{
|
{
|
||||||
if (e.Key == Keys.C)
|
if (e.Key == Keys.C)
|
||||||
{
|
_runtime.QueueEvent("interrupt", LK => 0);
|
||||||
_runtime.PushEvent(new LuaEvent()
|
|
||||||
{
|
|
||||||
Name = "interrupt",
|
|
||||||
Parameters = { },
|
|
||||||
BypassFilter = true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (e.Key == Keys.V)
|
else if (e.Key == Keys.V)
|
||||||
{
|
{
|
||||||
if (SDL.HasClipboardText())
|
if (SDL.HasClipboardText())
|
||||||
{
|
{
|
||||||
var text = SDL.GetClipboardText();
|
var text = SDL.GetClipboardText();
|
||||||
_runtime.PushEvent(new LuaEvent()
|
_runtime.QueueEvent("paste", LK => {
|
||||||
{
|
LK.PushString(text);
|
||||||
Name = "paste",
|
|
||||||
Parameters = new[] {
|
return 1;
|
||||||
text,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,9 +179,10 @@ internal class RuntimeInputEvents
|
||||||
|
|
||||||
private void OnChar(object sender, CharEvent e)
|
private void OnChar(object sender, CharEvent e)
|
||||||
{
|
{
|
||||||
_runtime.PushEvent("char", new object[]
|
_runtime.QueueEvent("char", LK => {
|
||||||
{
|
LK.PushString(e.Character.ToString());
|
||||||
e.Character,
|
|
||||||
|
return 1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@ using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Libraries;
|
namespace Capy64.Runtime.Libraries;
|
||||||
|
|
||||||
public class Event : IPlugin
|
public class Event : IPlugin
|
||||||
{
|
{
|
||||||
|
@ -52,7 +52,7 @@ public class Event : IPlugin
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
if(L.ToString(1) == "interrupt")
|
if (L.ToString(1) == "interrupt")
|
||||||
{
|
{
|
||||||
L.Error("interrupt");
|
L.Error("interrupt");
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ public class Event : IPlugin
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
var nargs = L.GetTop();
|
var nargs = L.GetTop();
|
||||||
for(int i = 1; i <= nargs; i++)
|
for (int i = 1; i <= nargs; i++)
|
||||||
{
|
{
|
||||||
L.CheckString(i);
|
L.CheckString(i);
|
||||||
}
|
}
|
||||||
|
@ -100,18 +100,14 @@ public class Event : IPlugin
|
||||||
|
|
||||||
var nargs = L.GetTop();
|
var nargs = L.GetTop();
|
||||||
|
|
||||||
var evParsState = L.NewThread();
|
_game.LuaRuntime.QueueEvent(eventName, LK =>
|
||||||
|
|
||||||
for(int i = 2; i <= nargs; i++)
|
|
||||||
{
|
{
|
||||||
L.PushCopy(i);
|
for (int i = 2; i <= nargs; i++)
|
||||||
}
|
{
|
||||||
|
L.PushCopy(i);
|
||||||
|
}
|
||||||
|
|
||||||
L.XMove(evParsState, nargs - 1);
|
L.XMove(LK, nargs - 1);
|
||||||
|
|
||||||
_game.LuaRuntime.PushEvent(eventName, LK =>
|
|
||||||
{
|
|
||||||
evParsState.XMove(LK, nargs - 1);
|
|
||||||
|
|
||||||
return nargs - 1;
|
return nargs - 1;
|
||||||
});
|
});
|
|
@ -1,13 +1,13 @@
|
||||||
using Capy64.API;
|
using Capy64.API;
|
||||||
using Capy64.LuaRuntime.Extensions;
|
using Capy64.Runtime.Objects.Handlers;
|
||||||
using Capy64.LuaRuntime.Objects.Handlers;
|
using Capy64.Runtime.Extensions;
|
||||||
using KeraLua;
|
using KeraLua;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Libraries;
|
namespace Capy64.Runtime.Libraries;
|
||||||
|
|
||||||
public class FileSystem : IPlugin
|
public class FileSystem : IPlugin
|
||||||
{
|
{
|
|
@ -1,11 +1,11 @@
|
||||||
using Capy64.API;
|
using Capy64.API;
|
||||||
using Capy64.LuaRuntime.Objects;
|
using Capy64.Runtime.Objects;
|
||||||
using KeraLua;
|
using KeraLua;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Libraries;
|
namespace Capy64.Runtime.Libraries;
|
||||||
|
|
||||||
public class GPU : IPlugin
|
public class GPU : IPlugin
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using Capy64.API;
|
using Capy64.API;
|
||||||
using Capy64.LuaRuntime.Extensions;
|
using Capy64.Runtime.Extensions;
|
||||||
using Capy64.LuaRuntime.Objects.Handlers;
|
using Capy64.Runtime.Objects.Handlers;
|
||||||
using KeraLua;
|
using KeraLua;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using System;
|
using System;
|
||||||
|
@ -11,7 +11,7 @@ using System.Net.WebSockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Libraries;
|
namespace Capy64.Runtime.Libraries;
|
||||||
#nullable enable
|
#nullable enable
|
||||||
public class HTTP : IPlugin
|
public class HTTP : IPlugin
|
||||||
{
|
{
|
||||||
|
@ -189,7 +189,13 @@ public class HTTP : IPlugin
|
||||||
|
|
||||||
if (task.IsFaulted || task.IsCanceled)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,41 +209,42 @@ public class HTTP : IPlugin
|
||||||
else
|
else
|
||||||
handler = new ReadHandle(stream);
|
handler = new ReadHandle(stream);
|
||||||
|
|
||||||
_game.LuaRuntime.PushEvent("http_response", L =>
|
_game.LuaRuntime.QueueEvent("http_response", LK =>
|
||||||
{
|
{
|
||||||
// arg 1, request id
|
// arg 1, request id
|
||||||
L.PushInteger(requestId);
|
LK.PushInteger(requestId);
|
||||||
|
|
||||||
// arg 2, response data
|
// arg 2, response data
|
||||||
L.NewTable();
|
LK.NewTable();
|
||||||
|
|
||||||
L.PushString("success");
|
LK.PushString("success");
|
||||||
L.PushBoolean(response.IsSuccessStatusCode);
|
LK.PushBoolean(response.IsSuccessStatusCode);
|
||||||
L.SetTable(-3);
|
LK.SetTable(-3);
|
||||||
|
|
||||||
L.PushString("statusCode");
|
LK.PushString("statusCode");
|
||||||
L.PushNumber((int)response.StatusCode);
|
LK.PushNumber((int)response.StatusCode);
|
||||||
L.SetTable(-3);
|
LK.SetTable(-3);
|
||||||
|
|
||||||
L.PushString("reasonPhrase");
|
LK.PushString("reasonPhrase");
|
||||||
L.PushString(response.ReasonPhrase);
|
LK.PushString(response.ReasonPhrase);
|
||||||
L.SetTable(-3);
|
LK.SetTable(-3);
|
||||||
|
|
||||||
L.PushString("headers");
|
LK.PushString("headers");
|
||||||
L.NewTable();
|
LK.NewTable();
|
||||||
|
|
||||||
foreach (var header in response.Headers)
|
foreach (var header in response.Headers)
|
||||||
{
|
{
|
||||||
L.PushString(header.Key);
|
LK.PushString(header.Key);
|
||||||
L.PushArray(header.Value.ToArray());
|
LK.PushArray(header.Value.ToArray());
|
||||||
L.SetTable(-3);
|
LK.SetTable(-3);
|
||||||
}
|
}
|
||||||
|
|
||||||
L.SetTable(-3);
|
LK.SetTable(-3);
|
||||||
|
|
||||||
handler.Push(L, false);
|
handler.Push(LK, false);
|
||||||
|
|
||||||
return 2;
|
return 2;
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//_game.LuaRuntime.PushEvent("http_response", requestId, response.IsSuccessStatusCode, content, (int)response.StatusCode, response.ReasonPhrase);
|
//_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)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,11 +338,11 @@ public class HTTP : IPlugin
|
||||||
var handle = new WebSocketHandle(wsClient, requestId, _game);
|
var handle = new WebSocketHandle(wsClient, requestId, _game);
|
||||||
WebSocketConnections.Add(handle);
|
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;
|
return 2;
|
||||||
});
|
});
|
||||||
|
@ -342,7 +355,12 @@ public class HTTP : IPlugin
|
||||||
if (result.MessageType == WebSocketMessageType.Close)
|
if (result.MessageType == WebSocketMessageType.Close)
|
||||||
{
|
{
|
||||||
await wsClient.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -353,7 +371,13 @@ public class HTTP : IPlugin
|
||||||
|
|
||||||
if (result.EndOfMessage)
|
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();
|
builder.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
using KeraLua;
|
using KeraLua;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Libraries;
|
namespace Capy64.Runtime.Libraries;
|
||||||
|
|
||||||
public class OS : IPlugin
|
public class OS : IPlugin
|
||||||
{
|
{
|
||||||
|
@ -47,11 +47,11 @@ public class OS : IPlugin
|
||||||
|
|
||||||
if (doReboot)
|
if (doReboot)
|
||||||
{
|
{
|
||||||
BIOS.Bios.Reboot();
|
RuntimeManager.Reboot();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
BIOS.Bios.Shutdown();
|
RuntimeManager.Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
|
@ -8,7 +8,7 @@ using System;
|
||||||
using static Capy64.Utils;
|
using static Capy64.Utils;
|
||||||
using static System.Formats.Asn1.AsnWriter;
|
using static System.Formats.Asn1.AsnWriter;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Libraries;
|
namespace Capy64.Runtime.Libraries;
|
||||||
|
|
||||||
internal class Term : IPlugin
|
internal class Term : IPlugin
|
||||||
{
|
{
|
|
@ -2,7 +2,7 @@
|
||||||
using KeraLua;
|
using KeraLua;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Libraries;
|
namespace Capy64.Runtime.Libraries;
|
||||||
|
|
||||||
class Timer : IPlugin
|
class Timer : IPlugin
|
||||||
{
|
{
|
||||||
|
@ -54,7 +54,12 @@ class Timer : IPlugin
|
||||||
|
|
||||||
timer.Elapsed += (o, e) =>
|
timer.Elapsed += (o, e) =>
|
||||||
{
|
{
|
||||||
_game.LuaRuntime.PushEvent("timer", timerId);
|
_game.LuaRuntime.QueueEvent("timer", LK =>
|
||||||
|
{
|
||||||
|
LK.PushInteger(timerId);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
L.PushInteger(timerId);
|
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;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime;
|
namespace Capy64.Runtime;
|
||||||
|
|
||||||
public class LuaException : Exception
|
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 KeraLua;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Objects;
|
namespace Capy64.Runtime.Objects;
|
||||||
|
|
||||||
public class GPUBuffer
|
public class GPUBuffer
|
||||||
{
|
{
|
|
@ -4,7 +4,7 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Objects.Handlers;
|
namespace Capy64.Runtime.Objects.Handlers;
|
||||||
|
|
||||||
public class BinaryReadHandle : IHandle
|
public class BinaryReadHandle : IHandle
|
||||||
{
|
{
|
|
@ -4,7 +4,7 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Objects.Handlers;
|
namespace Capy64.Runtime.Objects.Handlers;
|
||||||
|
|
||||||
public class BinaryWriteHandle : IHandle
|
public class BinaryWriteHandle : IHandle
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using KeraLua;
|
using KeraLua;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Objects.Handlers;
|
namespace Capy64.Runtime.Objects.Handlers;
|
||||||
|
|
||||||
public interface IHandle
|
public interface IHandle
|
||||||
{
|
{
|
|
@ -3,7 +3,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Objects.Handlers;
|
namespace Capy64.Runtime.Objects.Handlers;
|
||||||
|
|
||||||
public class ReadHandle : IHandle
|
public class ReadHandle : IHandle
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
using Capy64.LuaRuntime.Libraries;
|
using Capy64.Runtime.Libraries;
|
||||||
using KeraLua;
|
using KeraLua;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -8,7 +8,7 @@ using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Objects.Handlers;
|
namespace Capy64.Runtime.Objects.Handlers;
|
||||||
|
|
||||||
public class WebSocketHandle : IHandle
|
public class WebSocketHandle : IHandle
|
||||||
{
|
{
|
||||||
|
@ -92,7 +92,12 @@ public class WebSocketHandle : IHandle
|
||||||
.ContinueWith(async task =>
|
.ContinueWith(async task =>
|
||||||
{
|
{
|
||||||
await 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);
|
HTTP.WebSocketConnections.Remove(h);
|
|
@ -3,7 +3,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Objects.Handlers;
|
namespace Capy64.Runtime.Objects.Handlers;
|
||||||
|
|
||||||
public class WriteHandle : IHandle
|
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.Runtime.Extensions;
|
||||||
using Capy64.LuaRuntime.Libraries;
|
using Capy64.Runtime.Libraries;
|
||||||
using KeraLua;
|
using KeraLua;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime;
|
namespace Capy64.Runtime;
|
||||||
|
|
||||||
internal class Sandbox
|
internal class Sandbox
|
||||||
{
|
{
|
Loading…
Reference in a new issue