LNXSDK/Kha/Sources/kha/netsync/SyncBuilder.hx

264 lines
6.3 KiB
Haxe
Raw Permalink Normal View History

2025-01-22 16:18:30 +01:00
package kha.netsync;
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Expr.Field;
class SyncBuilder {
public static var nextId: Int = 0;
public static var objects: Array<Dynamic> = new Array<Dynamic>();
macro static public function build(): Array<Field> {
var fields = Context.getBuildFields();
var isBaseEntity = false;
for (i in Context.getLocalClass().get().interfaces) {
var intf = i.t.get();
if (intf.module == "kha.netsync.Sync") {
isBaseEntity = true;
break;
}
}
for (field in fields) {
if (field.name == "new") {
switch (field.kind) {
case FFun(f):
var cexpr = f.expr;
cexpr = macro @:mergeBlock {
$cexpr;
kha.netsync.SyncBuilder.objects[_syncId()] = this;
}
f.expr = cexpr;
continue;
default:
}
}
var synced = false;
// TODO: Avoid hardcoding the target ids
var target = 0; // kha.netsync.Session.RPC_SERVER;
var isStatic = field.access.lastIndexOf(AStatic) >= 0;
for (meta in field.meta) {
if (meta.name == "sync" || meta.name == "synced") {
// TODO: Figure out if there is a "nicer" way to do this
for (param in meta.params) {
if (param.expr.equals(EConst(CString("server")))) {
target = 0; // kha.netsync.Session.RPC_SERVER;
break;
}
else if (param.expr.equals(EConst(CString("all")))) {
target = 1; // kha.netsync.Session.RPC_ALL;
break;
}
}
synced = true;
break;
}
}
if (!synced)
continue;
switch (field.kind) {
case FFun(f):
var original = f.expr;
var size = 6;
for (arg in f.args) {
switch (arg.type) {
case TPath(p):
switch (p.name) {
case "Int":
size += 5;
case "Float":
size += 9;
case "Bool":
size += 2;
}
default:
}
}
var expr = macro @:mergeBlock {
var size: Int = $v{size};
};
for (arg in f.args) {
switch (arg.type) {
case TPath(p):
switch (p.name) {
case "String":
var argname = arg.name;
expr = macro @:mergeBlock {
$expr;
size += $i{argname}.length + 3;
}
}
default:
}
}
var classname = Context.getLocalClass().toString();
var methodname = field.name;
expr = macro @:mergeBlock {
$expr;
size += $v{classname}.length + 2;
size += $v{methodname}.length + 2;
var bytes = haxe.io.Bytes.alloc(size);
bytes.set(0, kha.netsync.Session.REMOTE_CALL);
bytes.set(1, $v{target});
}
if (isStatic) {
expr = macro @:mergeBlock {
$expr;
bytes.setInt32(2, -1);
}
}
else {
expr = macro @:mergeBlock {
$expr;
bytes.setInt32(2, _syncId());
}
}
expr = macro @:mergeBlock {
$expr;
var index = 6;
bytes.setUInt16(index, $v{classname}.length);
index += 2;
for (i in 0...$v{classname}.length) {
bytes.set(index, $v{classname}.charCodeAt(i));
++index;
}
bytes.setUInt16(index, $v{methodname}.length);
index += 2;
for (i in 0...$v{methodname}.length) {
bytes.set(index, $v{methodname}.charCodeAt(i));
++index;
}
};
for (arg in f.args) {
switch (arg.type) {
case TPath(p):
switch (p.name) {
case "Int":
var argname = arg.name;
expr = macro @:mergeBlock {
$expr;
bytes.set(index, 'I'.charCodeAt(0));
++index;
bytes.setInt32(index, $i{argname});
index += 4;
};
case "String":
var argname = arg.name;
expr = macro @:mergeBlock {
$expr;
bytes.set(index, 'S'.charCodeAt(0));
++index;
bytes.setUInt16(index, $i{argname}.length);
index += 2;
for (i in 0...$i{argname}.length) {
bytes.set(index, $i{argname}.charCodeAt(i));
++index;
}
};
case "Float":
var argname = arg.name;
expr = macro @:mergeBlock {
$expr;
bytes.set(index, 'F'.charCodeAt(0));
++index;
bytes.setDouble(index, $i{argname});
index += 8;
};
case "Bool":
var argname = arg.name;
expr = macro @:mergeBlock {
$expr;
bytes.set(index, 'B'.charCodeAt(0));
++index;
bytes.set(index, $i{argname} ?1:0);
++index;
};
default:
trace("Warning: type '" + p.name + "' of property '" + arg.name + "' cannot be synced");
}
default:
}
}
#if sys_server
expr = macro {
if (kha.netsync.Session.the() != null) {
$expr;
kha.netsync.Session.the().processRPC(bytes);
}
};
#else
expr = macro {
if (kha.netsync.Session.the() != null) {
$expr;
kha.netsync.Session.the().sendToServer(bytes);
}
else {
$original;
}
};
#end
fields.push({
name: field.name + "_remotely",
doc: null,
meta: [],
access: (isStatic ? [APublic, AStatic] : [APublic]),
kind: FFun({
ret: f.ret,
params: f.params,
expr: original,
args: f.args
}),
pos: Context.currentPos()
});
f.expr = expr;
default:
trace("Warning: Synced property " + field.name + " is not a function.");
}
}
fields.push({
name: "_syncId",
doc: null,
meta: [],
access: isBaseEntity ? [APublic] : [APublic, AOverride],
kind: FFun({
ret: Context.toComplexType(Context.getType("Int")),
params: null,
expr: macro {
return __syncId;
},
args: []
}),
pos: Context.currentPos()
});
if (isBaseEntity) {
fields.push({
name: "__syncId",
doc: null,
meta: [],
access: [APublic],
kind: FVar(macro : Int, macro kha.netsync.SyncBuilder.nextId++),
pos: Context.currentPos()
});
}
return fields;
}
}