remade RPC system

This commit is contained in:
2024-03-12 21:44:08 +01:00
parent 1d9e08641e
commit fe85c33d64
4 changed files with 196 additions and 111 deletions

View File

@@ -1,129 +1,136 @@
package macros.rpc;
import haxe.macro.ComplexTypeTools;
import haxe.macro.TypeTools;
import haxe.macro.Context;
import haxe.macro.Expr;
using Lambda;
class RPC {
macro static public function buildRPC():Array<Field> {
var fields = Context.getBuildFields();
macro static public function buildRPC(iface:Expr):Array<Field> {
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) {
switch (field.type) {
case TFun(args, ret):
var argsExprs:Array<Expr> = [for (a in args) macro $i{a.name}];
for (field in fields) {
if (field.meta == null)
continue;
if (field.meta.exists((i) -> i.name == "rpc") == false)
continue;
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);
var convertedArgs:Array<FunctionArg> = [];
for (a in args) {
convertedArgs.push({
name: a.name,
opt: a.opt,
type: TypeTools.toComplexType(a.t)
});
}
});
} else {
exprs.push(macro {
if (pack.data.func == $v{funName}) {
pack.respond(this.$funName($a{callArgs}));
var retComplexType = TypeTools.toComplexType(ret);
var retType:ComplexType = if (Helper.isPromise(ret)) {
TypeTools.toComplexType(Helper.getPromiseType(retComplexType));
} 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 functions can be used for rpc", field.pos);
}
}
default:
Context.error("Only Interfaces are supported", iface.pos);
}
default:
Context.error("Only Interfaces are supported", iface.pos);
}
return macro {
kernel.net.Net.registerProto($v{proto}, (pack) -> {
$a{exprs}
});
};
// 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;
}
}