mirror of
https://github.com/Ale32bit/Capy64.git
synced 2025-01-18 02:26:44 +00:00
Added http.request to Lua, renamed Lua.PushValue to Lua.PushObject, added lua.lua REPL
This commit is contained in:
parent
c4d5ebd500
commit
9d978fd9ab
12 changed files with 282 additions and 37 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -27,7 +27,8 @@ x86/
|
||||||
[Aa][Rr][Mm]/
|
[Aa][Rr][Mm]/
|
||||||
[Aa][Rr][Mm]64/
|
[Aa][Rr][Mm]64/
|
||||||
bld/
|
bld/
|
||||||
[Bb]in/
|
/[Bb]in/
|
||||||
|
/[Cc]apy64/bin/
|
||||||
[Oo]bj/
|
[Oo]bj/
|
||||||
[Oo]ut/
|
[Oo]ut/
|
||||||
[Ll]og/
|
[Ll]og/
|
||||||
|
|
|
@ -11,6 +11,6 @@ namespace Capy64.API;
|
||||||
public interface IPlugin
|
public interface IPlugin
|
||||||
{
|
{
|
||||||
void ConfigureServices(IServiceCollection services) { }
|
void ConfigureServices(IServiceCollection services) { }
|
||||||
void LuaInit(Lua state) { }
|
void LuaInit(Lua L) { }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
78
Capy64/Assets/Lua/bin/lua.lua
Normal file
78
Capy64/Assets/Lua/bin/lua.lua
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
local term = require("term")
|
||||||
|
local io = require("io")
|
||||||
|
local colors = require("colors")
|
||||||
|
local colours = colors
|
||||||
|
|
||||||
|
local tArgs = { ... }
|
||||||
|
if #tArgs > 0 then
|
||||||
|
print("This is an interactive Lua prompt.")
|
||||||
|
print("To run a lua program, just type its name.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
--local pretty = require "cc.pretty"
|
||||||
|
|
||||||
|
local bRunning = true
|
||||||
|
local tCommandHistory = {}
|
||||||
|
local tEnv = {
|
||||||
|
["exit"] = setmetatable({}, {
|
||||||
|
__tostring = function() return "Call exit() to exit." end,
|
||||||
|
__call = function() bRunning = false end,
|
||||||
|
}),
|
||||||
|
["_echo"] = function(...)
|
||||||
|
return ...
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
setmetatable(tEnv, { __index = _ENV })
|
||||||
|
|
||||||
|
for k, v in pairs(package.loaded) do
|
||||||
|
tEnv[k] = v
|
||||||
|
end
|
||||||
|
|
||||||
|
term.setForeground(colours.yellow)
|
||||||
|
print("Interactive Lua prompt.")
|
||||||
|
print("Call exit() to exit.")
|
||||||
|
term.setForeground(colours.white)
|
||||||
|
|
||||||
|
while bRunning do
|
||||||
|
term.setForeground( colours.yellow )
|
||||||
|
write("lua> ")
|
||||||
|
term.setForeground( colours.white )
|
||||||
|
|
||||||
|
local s = io.read(nil, tCommandHistory)
|
||||||
|
if s:match("%S") and tCommandHistory[#tCommandHistory] ~= s then
|
||||||
|
table.insert(tCommandHistory, s)
|
||||||
|
end
|
||||||
|
|
||||||
|
local nForcePrint = 0
|
||||||
|
local func, e = load(s, "=lua", "t", tEnv)
|
||||||
|
local func2 = load("return _echo(" .. s .. ");", "=lua", "t", tEnv)
|
||||||
|
if not func then
|
||||||
|
if func2 then
|
||||||
|
func = func2
|
||||||
|
e = nil
|
||||||
|
nForcePrint = 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if func2 then
|
||||||
|
func = func2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if func then
|
||||||
|
local tResults = table.pack(pcall(func))
|
||||||
|
if tResults[1] then
|
||||||
|
local n = 1
|
||||||
|
while n < tResults.n or n <= nForcePrint do
|
||||||
|
local value = tResults[n + 1]
|
||||||
|
print(tostring(value))
|
||||||
|
n = n + 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
print(tResults[2])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
print(e)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -10,4 +10,4 @@ term.setPos(1,1)
|
||||||
|
|
||||||
print(os.version())
|
print(os.version())
|
||||||
|
|
||||||
dofile("/bin/shell.lua")
|
dofile("/bin/lua.lua")
|
|
@ -144,7 +144,7 @@ public class Bios : IPlugin
|
||||||
|
|
||||||
public static void InstallOS(bool force = false)
|
public static void InstallOS(bool force = false)
|
||||||
{
|
{
|
||||||
var installedFilePath = Path.Combine(FileSystem.BasePath, ".installed");
|
var installedFilePath = Path.Combine(Capy64.AppDataPath, ".installed");
|
||||||
if (!File.Exists(installedFilePath) || force)
|
if (!File.Exists(installedFilePath) || force)
|
||||||
{
|
{
|
||||||
FileSystem.CopyDirectory("Assets/Lua", FileSystem.DataPath, true, true);
|
FileSystem.CopyDirectory("Assets/Lua", FileSystem.DataPath, true, true);
|
||||||
|
|
|
@ -15,12 +15,18 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using static Capy64.Utils;
|
using static Capy64.Utils;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Capy64;
|
namespace Capy64;
|
||||||
|
|
||||||
public class Capy64 : Game, IGame
|
public class Capy64 : Game, IGame
|
||||||
{
|
{
|
||||||
public const string Version = "Capy64 a0.0.1";
|
public const string Version = "a0.0.1";
|
||||||
|
public static string AppDataPath = Path.Combine(
|
||||||
|
Environment.GetFolderPath(
|
||||||
|
Environment.SpecialFolder.ApplicationData,
|
||||||
|
Environment.SpecialFolderOption.Create),
|
||||||
|
"Capy64");
|
||||||
public Game Game => this;
|
public Game 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; }
|
||||||
|
@ -102,7 +108,7 @@ public class Capy64 : Game, IGame
|
||||||
|
|
||||||
protected override void Initialize()
|
protected override void Initialize()
|
||||||
{
|
{
|
||||||
Window.Title = Version;
|
Window.Title = "Capy64 " + Version;
|
||||||
|
|
||||||
UpdateSize();
|
UpdateSize();
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace Capy64;
|
||||||
|
|
||||||
public interface IGame
|
public interface IGame
|
||||||
{
|
{
|
||||||
|
|
||||||
Game Game { get; }
|
Game Game { get; }
|
||||||
IList<IPlugin> NativePlugins { get; }
|
IList<IPlugin> NativePlugins { get; }
|
||||||
IList<IPlugin> Plugins { get; }
|
IList<IPlugin> Plugins { get; }
|
||||||
|
|
|
@ -11,31 +11,31 @@ using System.Threading.Tasks;
|
||||||
namespace Capy64.LuaRuntime.Extensions;
|
namespace Capy64.LuaRuntime.Extensions;
|
||||||
public static class Utils
|
public static class Utils
|
||||||
{
|
{
|
||||||
public static void PushArray(this Lua state, object obj)
|
public static void PushArray(this Lua L, object obj)
|
||||||
{
|
{
|
||||||
var iterable = obj as IEnumerable;
|
var iterable = obj as IEnumerable;
|
||||||
|
|
||||||
state.NewTable();
|
L.NewTable();
|
||||||
long i = 1;
|
long i = 1;
|
||||||
foreach (var item in iterable)
|
foreach (var item in iterable)
|
||||||
{
|
{
|
||||||
state.PushValue(item);
|
L.PushObject(item);
|
||||||
state.RawSetInteger(-2, i++);
|
L.RawSetInteger(-2, i++);
|
||||||
}
|
}
|
||||||
state.SetTop(-1);
|
L.SetTop(-1);
|
||||||
}
|
}
|
||||||
#nullable enable
|
#nullable enable
|
||||||
public static int PushValue(this Lua state, object? obj)
|
public static int PushObject(this Lua L, object? obj)
|
||||||
{
|
{
|
||||||
var type = obj?.GetType();
|
var type = obj?.GetType();
|
||||||
switch (obj)
|
switch (obj)
|
||||||
{
|
{
|
||||||
case string str:
|
case string str:
|
||||||
state.PushString(str);
|
L.PushString(str);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case char:
|
case char:
|
||||||
state.PushString(obj.ToString());
|
L.PushString(obj.ToString());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case byte:
|
case byte:
|
||||||
|
@ -45,37 +45,37 @@ public static class Utils
|
||||||
case int:
|
case int:
|
||||||
case uint:
|
case uint:
|
||||||
case double:
|
case double:
|
||||||
state.PushNumber(Convert.ToDouble(obj));
|
L.PushNumber(Convert.ToDouble(obj));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case long l:
|
case long l:
|
||||||
state.PushInteger(l);
|
L.PushInteger(l);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case bool b:
|
case bool b:
|
||||||
state.PushBoolean(b);
|
L.PushBoolean(b);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case null:
|
case null:
|
||||||
state.PushNil();
|
L.PushNil();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case byte[] b:
|
case byte[] b:
|
||||||
state.PushBuffer(b);
|
L.PushBuffer(b);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LuaFunction func:
|
case LuaFunction func:
|
||||||
state.PushCFunction(func);
|
L.PushCFunction(func);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IntPtr ptr:
|
case IntPtr ptr:
|
||||||
state.PushLightUserData(ptr);
|
L.PushLightUserData(ptr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (type is not null && type.IsArray)
|
if (type is not null && type.IsArray)
|
||||||
{
|
{
|
||||||
state.PushArray(obj);
|
L.PushArray(obj);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -87,15 +87,16 @@ public static class Utils
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void PushManagedObject<T>(this Lua state, T obj)
|
[Obsolete("This method does not work as intended and requires more research")]
|
||||||
|
public static void PushManagedObject<T>(this Lua L, T obj)
|
||||||
{
|
{
|
||||||
var type = obj.GetType();
|
var type = obj.GetType();
|
||||||
var members = type.GetMembers().Where(m => m.MemberType == MemberTypes.Method);
|
var members = type.GetMembers().Where(m => m.MemberType == MemberTypes.Method);
|
||||||
state.CreateTable(0, members.Count());
|
L.CreateTable(0, members.Count());
|
||||||
foreach (var m in members)
|
foreach (var m in members)
|
||||||
{
|
{
|
||||||
state.PushCFunction(L => (int)type.InvokeMember(m.Name, BindingFlags.InvokeMethod, null, obj, new object[] { L }));
|
L.PushCFunction(L => (int)type.InvokeMember(m.Name, BindingFlags.InvokeMethod, null, obj, new object[] { L }));
|
||||||
state.SetField(-2, m.Name);
|
L.SetField(-2, m.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,7 @@ namespace Capy64.LuaRuntime.Libraries;
|
||||||
|
|
||||||
public class FileSystem : IPlugin
|
public class FileSystem : IPlugin
|
||||||
{
|
{
|
||||||
public static string BasePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create), "Capy64");
|
public static string DataPath = Path.Combine(Capy64.AppDataPath, "data");
|
||||||
public static string DataPath = Path.Combine(BasePath, "data");
|
|
||||||
|
|
||||||
public FileSystem()
|
public FileSystem()
|
||||||
{
|
{
|
||||||
|
@ -451,7 +450,7 @@ public class FileSystem : IPlugin
|
||||||
foreach (var attribute in attributes)
|
foreach (var attribute in attributes)
|
||||||
{
|
{
|
||||||
L.PushString(attribute.Key);
|
L.PushString(attribute.Key);
|
||||||
L.PushValue(attribute.Value);
|
Extensions.Utils.PushObject(L, attribute.Value);
|
||||||
|
|
||||||
L.SetTable(-3);
|
L.SetTable(-3);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,21 +2,180 @@
|
||||||
using KeraLua;
|
using KeraLua;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Net.Http;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Capy64.LuaRuntime.Libraries;
|
namespace Capy64.LuaRuntime.Libraries;
|
||||||
|
#nullable enable
|
||||||
public class HTTP : IPlugin
|
public class HTTP : IPlugin
|
||||||
{
|
{
|
||||||
private static IGame _game;
|
private static IGame _game;
|
||||||
|
private static HttpClient _client;
|
||||||
|
private static long RequestId;
|
||||||
|
|
||||||
|
private readonly LuaRegister[] HttpLib = new LuaRegister[]
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
name = "request",
|
||||||
|
function = L_Request,
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
name = "checkURL",
|
||||||
|
function = L_CheckUrl,
|
||||||
|
},
|
||||||
|
new(),
|
||||||
|
};
|
||||||
public HTTP(IGame game)
|
public HTTP(IGame game)
|
||||||
{
|
{
|
||||||
_game = game;
|
_game = game;
|
||||||
|
RequestId = 0;
|
||||||
|
_client = new();
|
||||||
|
_client.DefaultRequestHeaders.Add("User-Agent", $"Capy64/{Capy64.Version}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LuaInit(Lua state)
|
public void LuaInit(Lua L)
|
||||||
{
|
{
|
||||||
|
L.RequireF("http", Open, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int Open(IntPtr state)
|
||||||
|
{
|
||||||
|
var L = Lua.FromIntPtr(state);
|
||||||
|
L.NewLib(HttpLib);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryGetUri(string url, out Uri? uri)
|
||||||
|
{
|
||||||
|
return (Uri.TryCreate(url, UriKind.Absolute, out uri)
|
||||||
|
&& uri?.Scheme == Uri.UriSchemeHttp) || uri?.Scheme == Uri.UriSchemeHttps;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int L_Request(IntPtr state)
|
||||||
|
{
|
||||||
|
var L = Lua.FromIntPtr(state);
|
||||||
|
var request = new HttpRequestMessage();
|
||||||
|
|
||||||
|
var url = L.CheckString(1);
|
||||||
|
if (!TryGetUri(url, out Uri? uri) || uri is null)
|
||||||
|
{
|
||||||
|
L.ArgumentError(1, "invalid request url");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
request.RequestUri = uri;
|
||||||
|
|
||||||
|
if (L.IsTable(3)) // headers
|
||||||
|
{
|
||||||
|
L.PushCopy(3);
|
||||||
|
L.PushNil();
|
||||||
|
|
||||||
|
while (L.Next(-2))
|
||||||
|
{
|
||||||
|
L.PushCopy(-2);
|
||||||
|
|
||||||
|
var k = L.CheckString(-1);
|
||||||
|
if (L.IsStringOrNumber(-2))
|
||||||
|
{
|
||||||
|
var v = L.ToString(-2);
|
||||||
|
|
||||||
|
request.Headers.Add(k, v);
|
||||||
|
}
|
||||||
|
else if (L.IsNil(-2))
|
||||||
|
{
|
||||||
|
request.Headers.Remove(k);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
L.ArgumentError(3, "string, number or nil expected, got " + L.TypeName(L.Type(-2)) + " in field " + k);
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Pop(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Pop(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["binary"] = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (L.IsTable(4)) // other options?
|
||||||
|
{
|
||||||
|
L.PushCopy(4);
|
||||||
|
L.PushNil();
|
||||||
|
|
||||||
|
while (L.Next(-2))
|
||||||
|
{
|
||||||
|
L.PushCopy(-2);
|
||||||
|
var k = L.CheckString(-1);
|
||||||
|
|
||||||
|
switch (k)
|
||||||
|
{
|
||||||
|
case "method":
|
||||||
|
options["method"] = L.CheckString(-2);
|
||||||
|
break;
|
||||||
|
case "binary":
|
||||||
|
options["binary"] = L.IsBoolean(-2) ? L.ToBoolean(-2) : false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Pop(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Pop(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!L.IsNoneOrNil(2))
|
||||||
|
{
|
||||||
|
if ((bool)options["binary"])
|
||||||
|
{
|
||||||
|
request.Content = new ByteArrayContent(L.CheckBuffer(2));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
request.Content = new StringContent(L.CheckString(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request.Method = options.TryGetValue("method", out var value)
|
||||||
|
? new HttpMethod((string)value)
|
||||||
|
: request.Content is not null ? HttpMethod.Post : HttpMethod.Get;
|
||||||
|
|
||||||
|
var requestId = RequestId++;
|
||||||
|
|
||||||
|
var reqTask = _client.SendAsync(request);
|
||||||
|
reqTask.ContinueWith(async (task) =>
|
||||||
|
{
|
||||||
|
var response = await task;
|
||||||
|
object content;
|
||||||
|
if ((bool)options["binary"])
|
||||||
|
content = await response.Content.ReadAsByteArrayAsync();
|
||||||
|
else
|
||||||
|
content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
_game.LuaRuntime.PushEvent("http_response", requestId, content, (int)response.StatusCode);
|
||||||
|
else
|
||||||
|
_game.LuaRuntime.PushEvent("http_failure", requestId, content, (int)response.StatusCode, response.ReasonPhrase);
|
||||||
|
});
|
||||||
|
|
||||||
|
L.PushInteger(requestId);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int L_CheckUrl(IntPtr state)
|
||||||
|
{
|
||||||
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
|
var url = L.CheckString(1);
|
||||||
|
|
||||||
|
var isValid = TryGetUri(url, out _);
|
||||||
|
|
||||||
|
L.PushBoolean(isValid);
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ public class OS : IPlugin
|
||||||
{
|
{
|
||||||
var L = Lua.FromIntPtr(state);
|
var L = Lua.FromIntPtr(state);
|
||||||
|
|
||||||
L.PushString(Capy64.Version);
|
L.PushString("Capy64 " + Capy64.Version);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,7 +124,7 @@ public class Runtime
|
||||||
{
|
{
|
||||||
foreach (var par in ev.Parameters)
|
foreach (var par in ev.Parameters)
|
||||||
{
|
{
|
||||||
Thread.PushValue(par);
|
Thread.PushObject(par);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (ev.Parameters?.Length ?? 0) + 1;
|
return (ev.Parameters?.Length ?? 0) + 1;
|
||||||
|
|
Loading…
Reference in a new issue