remade RPC system

This commit is contained in:
Niklas Kapelle 2024-03-12 21:44:08 +01:00
parent 1d9e08641e
commit fe85c33d64
Signed by: niklas
GPG Key ID: 4EB651B36D841D16
4 changed files with 196 additions and 111 deletions

41
src/bin/debug/Debug.hx Normal file
View File

@ -0,0 +1,41 @@
package bin.debug;
import kernel.log.Log;
import lib.turtle.InvManager;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
/**
Use this to test whatever you are working on. It will also print debug statements.
IDK if you commit changes in this file. It will not be included in non debug build.
**/
#if debug
@:build(macros.Binstore.includeBin("Debug", ["dbg", "debug"]))
#end
class Debug implements Process {
public function new() {}
public function run(handle:ProcessHandle) {
var link = Log.onLog.handle((line) -> {
handle.writeLine('[${line.level}] ${line.message}');
});
handle.addDeferFunc(() -> {
link.cancel();
});
// Add your stuff here
// -----
var rpc = new bin.debug.DebugRPC.DebugRPCImpl(1, "debug");
var a = rpc.addNumber(1, 2);
// rpc.addNumber(1,2).handle((e)->{
// Log.debug(e);
// handle.close();
// });
// -----
}
}

10
src/bin/debug/DebugRPC.hx Normal file
View File

@ -0,0 +1,10 @@
package bin.debug;
import macros.rpc.RPCBase;
interface DebugRPC {
function addNumber(a:Int, b:Int):Int;
}
@:build(macros.rpc.RPC.buildRPC(DebugRPC))
class DebugRPCImpl extends RPCBase {}

View File

@ -0,0 +1,27 @@
package bin.debug;
import bin.debug.DebugRPC.DebugRPCImpl;
import macros.rpc.RPC;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
#if debug
@:build(macros.Binstore.includeBin("Debug SRV", ["dbg-srv", "debug-srv"]))
#end
class DebugService implements Process implements DebugRPC {
private var handle:ProcessHandle;
public function new() {}
public function run(handle:ProcessHandle) {
this.handle = handle;
kernel.net.Net.registerProto("debug", (pack) -> {
DebugRPCImpl.handlePackage(this, pack);
});
}
public function addNumber(a:Int, b:Int):Int {
return a + b;
}
}

View File

@ -1,129 +1,136 @@
package macros.rpc; package macros.rpc;
import haxe.macro.ComplexTypeTools;
import haxe.macro.TypeTools; import haxe.macro.TypeTools;
import haxe.macro.Context; import haxe.macro.Context;
import haxe.macro.Expr; import haxe.macro.Expr;
using Lambda;
class RPC { class RPC {
macro static public function buildRPC():Array<Field> { macro static public function buildRPC(iface:Expr):Array<Field> {
var fields = Context.getBuildFields(); var buildFields = Context.getBuildFields();
var localClass = Context.getLocalClass().get();
var handleExprs:Array<Expr> = [];
var className = Context.getLocalClass().get().name + "RPC"; var ifaceType = null;
var c = macro class $className extends macros.rpc.RPCBase {
public function new(id:kernel.net.Package.NetworkID) {
super(id, $v{className});
}
}
switch (iface.expr) {
case EConst(CIdent(i)):
var t = Context.getType(i);
ifaceType = TypeTools.toComplexType(t);
switch (t) {
case TInst(t2, params):
var fields = t2.get().fields.get();
for (field in fields) { for (field in fields) {
if (field.meta == null) switch (field.type) {
continue; case TFun(args, ret):
if (field.meta.exists((i) -> i.name == "rpc") == false) var argsExprs:Array<Expr> = [for (a in args) macro $i{a.name}];
continue;
switch (field.kind) { var convertedArgs:Array<FunctionArg> = [];
case FFun(f): for (a in args) {
var argsExprs:Array<Expr> = [for (a in f.args) macro $i{a.name}]; convertedArgs.push({
name: a.name,
var convertedArgs = []; opt: a.opt,
for (a in f.args) { type: TypeTools.toComplexType(a.t)
a.type = Helper.resolveType(a.type, field.pos); });
convertedArgs.push(a);
} }
var rtn = if (Helper.isPromise(ComplexTypeTools.toType(f.ret))) { var retComplexType = TypeTools.toComplexType(ret);
TypeTools.toComplexType(Helper.getPromiseType(f.ret)); var retType:ComplexType = if (Helper.isPromise(ret)) {
TypeTools.toComplexType(Helper.getPromiseType(retComplexType));
} else { } else {
Helper.resolveType(f.ret, field.pos); retComplexType;
} }
c.fields.push({ buildFields.push({
name: field.name, name: field.name,
pos: field.pos, pos: localClass.pos,
access: [APublic, AInline],
kind: FFun({ kind: FFun({
args: convertedArgs, args: convertedArgs,
expr: macro { expr: macro {
return cast this._performRequest($v{field.name}, $a{argsExprs}); return cast this._performRequest($v{field.name}, $a{argsExprs});
}, },
ret: Helper.newPromise(rtn), ret: Helper.newPromise(retType),
}), }),
access: [APublic],
doc: null,
meta: [],
}); });
default:
Context.error("Only functions can be used for rpc", field.pos);
}
}
haxe.macro.Context.defineType(c); // Add expr for package handle
return fields; // TODO: this needs to be more efficient. Maybe use a switch case.
}
macro static public function generateRPCPackageHandle() { var callArgs = [for (k => v in args) macro p.data.args[$v{k}]];
#if display
return macro {};
#end
var proto = Context.getLocalClass().get().name + "RPC";
var exprs:Array<Expr> = [];
var fields = Context.getLocalClass().get().fields.get();
for (field in fields) {
if (field.meta == null)
continue;
if (!field.meta.has("rpc"))
continue;
switch (field.kind) {
case FMethod(k):
var funName = field.name; var funName = field.name;
var exprsType = field.expr().t;
switch (exprsType) {
case TFun(args, ret):
var callArgs = [for (k => v in args) macro pack.data.args[$v{k}]];
if (Helper.isVoid(ret)) { if (Helper.isVoid(ret)) {
exprs.push(macro { handleExprs.push(macro {
if (pack.data.func == $v{funName}) { if (p.data.func == $v{funName}) {
this.$funName($a{callArgs}); d.$funName($a{callArgs});
pack.respond(null); p.respond(null);
return true;
} }
true;
}); });
} else if (Helper.isPromise(ret)) { } else if (Helper.isPromise(ret)) {
exprs.push(macro { handleExprs.push(macro {
if (pack.data.func == $v{funName}) { if (p.data.func == $v{funName}) {
this.$funName($a{callArgs}).handle((r) -> { d.$funName($a{callArgs}).handle((r) -> {
pack.respond(r); p.respond(r);
return true;
}); });
} }
});
} else {
exprs.push(macro {
if (pack.data.func == $v{funName}) {
pack.respond(this.$funName($a{callArgs}));
}
});
}
default:
Context.error("Only functions can be used for rpc", field.pos);
}
default:
Context.error("Only functions can be used for rpc", field.pos);
}
}
return macro { true;
kernel.net.Net.registerProto($v{proto}, (pack) -> {
$a{exprs}
}); });
}; } else {
handleExprs.push(macro {
if (p.data.func == $v{funName}) {
p.respond(d.$funName($a{callArgs}));
return true;
}
// HACK: I not a 100% sure why this need to be here but it does not work without it.
// My guess is that a macro has to be able to be evaluated to a value and a simple if
// statement is of type Void. I don't think this last true statement will have an impact
// on the resulting Expr.
true;
});
}
default:
Context.warning("IDFK man", field.pos);
}
}
default:
Context.error("Only Interfaces are supported", iface.pos);
}
default:
Context.error("Only Interfaces are supported", iface.pos);
}
// Add handle package static method
buildFields.push({
name: "handlePackage",
access: [APublic, AStatic],
pos: localClass.pos,
kind: FFun({
args: [
{
name: "d",
type: ifaceType,
opt: false,
},
{
name: "p",
type: macro :kernel.net.Package.GenericPackage,
opt: false,
},
],
expr: macro {
$a{handleExprs} return false;
}
})
});
return buildFields;
} }
} }