264 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
			
		
		
	
	
			264 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
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;
 | 
						|
	}
 | 
						|
}
 |