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 { switch (iface.expr) {
public function new(id:kernel.net.Package.NetworkID) { case EConst(CIdent(i)):
super(id, $v{className}); 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) {
switch (field.type) {
case TFun(args, ret):
var argsExprs:Array<Expr> = [for (a in args) macro $i{a.name}];
for (field in fields) { var convertedArgs:Array<FunctionArg> = [];
if (field.meta == null) for (a in args) {
continue; convertedArgs.push({
if (field.meta.exists((i) -> i.name == "rpc") == false) name: a.name,
continue; opt: a.opt,
type: TypeTools.toComplexType(a.t)
switch (field.kind) {
case FFun(f):
var argsExprs:Array<Expr> = [for (a in f.args) macro $i{a.name}];
var convertedArgs = [];
for (a in f.args) {
a.type = Helper.resolveType(a.type, field.pos);
convertedArgs.push(a);
}
var rtn = if (Helper.isPromise(ComplexTypeTools.toType(f.ret))) {
TypeTools.toComplexType(Helper.getPromiseType(f.ret));
} else {
Helper.resolveType(f.ret, field.pos);
}
c.fields.push({
name: field.name,
pos: field.pos,
kind: FFun({
args: convertedArgs,
expr: macro {
return cast this._performRequest($v{field.name}, $a{argsExprs});
},
ret: Helper.newPromise(rtn),
}),
access: [APublic],
doc: null,
meta: [],
});
default:
Context.error("Only functions can be used for rpc", field.pos);
}
}
haxe.macro.Context.defineType(c);
return fields;
}
macro static public function generateRPCPackageHandle() {
#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 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)) {
exprs.push(macro {
if (pack.data.func == $v{funName}) {
this.$funName($a{callArgs});
pack.respond(null);
}
});
} else if (Helper.isPromise(ret)) {
exprs.push(macro {
if (pack.data.func == $v{funName}) {
this.$funName($a{callArgs}).handle((r) -> {
pack.respond(r);
}); });
} }
});
} else { var retComplexType = TypeTools.toComplexType(ret);
exprs.push(macro { var retType:ComplexType = if (Helper.isPromise(ret)) {
if (pack.data.func == $v{funName}) { TypeTools.toComplexType(Helper.getPromiseType(retComplexType));
pack.respond(this.$funName($a{callArgs})); } else {
retComplexType;
} }
});
buildFields.push({
name: field.name,
pos: localClass.pos,
access: [APublic, AInline],
kind: FFun({
args: convertedArgs,
expr: macro {
return cast this._performRequest($v{field.name}, $a{argsExprs});
},
ret: Helper.newPromise(retType),
}),
});
// Add expr for package handle
// TODO: this needs to be more efficient. Maybe use a switch case.
var callArgs = [for (k => v in args) macro p.data.args[$v{k}]];
var funName = field.name;
if (Helper.isVoid(ret)) {
handleExprs.push(macro {
if (p.data.func == $v{funName}) {
d.$funName($a{callArgs});
p.respond(null);
return true;
}
true;
});
} else if (Helper.isPromise(ret)) {
handleExprs.push(macro {
if (p.data.func == $v{funName}) {
d.$funName($a{callArgs}).handle((r) -> {
p.respond(r);
return true;
});
}
true;
});
} 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 functions can be used for rpc", field.pos); default:
} Context.error("Only Interfaces are supported", iface.pos);
default: }
Context.error("Only functions can be used for rpc", field.pos);
} default:
Context.error("Only Interfaces are supported", iface.pos);
} }
return macro { // Add handle package static method
kernel.net.Net.registerProto($v{proto}, (pack) -> { buildFields.push({
$a{exprs} 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;
} }
} }