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; | ||
|  | 	} | ||
|  | } |