diff --git a/Source/peote/net/Remote.hx b/Source/peote/net/Remote.hx index dba6578..88467c0 100644 --- a/Source/peote/net/Remote.hx +++ b/Source/peote/net/Remote.hx @@ -17,13 +17,29 @@ class RemoteImpl { #if macro + public static function getPackFromImports(name:String, imports:Array):Array + { + var pack = new Array(); + + for (imp in imports) { + if (imp.path[imp.path.length - 1].name == name) + for (i in 0 ... (imp.path.length - 1) ) + pack.push(imp.path[i].name); + } + return pack; + } + public static function build() { + try { Context.resolvePath("org/msgpack/MsgPack.hx"); isMsgPack = true; } catch (e:Dynamic) {} + var remoteNames = new Array(); var remoteParams = new Array>(); var hasNoNew:Bool = true; var classname = Context.getLocalClass().get().name; + var classpackage = Context.getLocalClass().get().pack; + var fields = Context.getBuildFields(); for (f in fields) { @@ -53,7 +69,9 @@ class RemoteImpl case TPath(param): switch (param.name) { case "Void": - default: remParams.push(param); // add param type + default: + if (param.pack.length == 0) param.pack = getPackFromImports(param.name, Context.getLocalImports()); + remParams.push(param); // add param type } default: } @@ -76,7 +94,9 @@ class RemoteImpl case TPath(param): switch (param.name) { case "Void": - default: remParams.push(param); // add param type + default: + if (param.pack.length == 0) param.pack = getPackFromImports(param.name, Context.getLocalImports()); + remParams.push(param); // add param type } default: } @@ -135,14 +155,15 @@ class RemoteImpl // ------------------------------------- generates new classs for remote-calling --------------------- // ------------------------------------------------------------------------------------------------- var classnameRemote = classname+"RemoteServer"; - Context.defineType(generateRemoteCaller(classnameRemote, true, remoteNames, remoteParams)); - // add function to return an instanze of that class - var getRemoteServer:Function = { + //Context.defineType(generateRemoteCaller(classnameRemote, true, remoteNames, remoteParams)); + Context.defineModule(classpackage.concat([classnameRemote]).join('.'),[generateRemoteCaller(classnameRemote, true, remoteNames, remoteParams)],Context.getLocalImports()); + + var getRemoteServer:Function = { // add function to return an instanze of that class args:[ {name:"server", type:macro:peote.net.PeoteServer, opt:false, value:null}, {name:"user", type:macro:Int, opt:false, value:null}, {name:"remoteId", type:macro:Int, opt:false, value:null} ], - expr: Context.parse( 'return new $classnameRemote(server, user, remoteId)', Context.currentPos()) , + expr: Context.parse( 'return new $classnameRemote(server, user, remoteId)', Context.currentPos()), ret: TPath({ name:classnameRemote, pack:[], params:[] }) // ret = return type } fields.push({ @@ -153,9 +174,10 @@ class RemoteImpl }); classnameRemote = classname+"RemoteClient"; - Context.defineType(generateRemoteCaller(classnameRemote, false, remoteNames, remoteParams)); - // add function to return an instanze of that class - var getRemoteClient:Function = { + //Context.defineType(generateRemoteCaller(classnameRemote, false, remoteNames, remoteParams)); + Context.defineModule(classpackage.concat([classnameRemote]).join('.'),[generateRemoteCaller(classnameRemote, false, remoteNames, remoteParams)],Context.getLocalImports()); + + var getRemoteClient:Function = { // add function to return an instanze of that class args:[ {name:"client", type:macro:peote.net.PeoteClient, opt:false, value:null}, {name:"remoteId", type:macro:Int, opt:false, value:null} ], @@ -207,12 +229,13 @@ class RemoteImpl name:'p$j', type:TPath({ name: remoteParams[i][j].name, - pack: switch(remoteParams[i][j].name) { + /*pack: switch(remoteParams[i][j].name) { case "Byte"|"UInt16"|"Int16"|"Int32"|"Double": ["peote", "io"]; case "Bytes": ["haxe", "io"]; case "Vector"|"IntMap"|"StringMap": ["haxe", "ds"]; default:remoteParams[i][j].pack; - }, + },*/ + pack: remoteParams[i][j].pack, params:remoteParams[i][j].params }), opt:false }], @@ -249,6 +272,7 @@ class RemoteImpl case TPType(TPath(t)): subtypes.push(t); default: } + var moduleName = ((tp.pack.length != 0) ? tp.pack.join(".") + "." : "") + tp.name; return ((depth == 0) ? 'var ' : '') + varname + "=" + switch (tp.name) { case "Array": 'new Array'+getFullType(tp)+'();' + @@ -285,7 +309,22 @@ class RemoteImpl case "Double": 'input.readDouble();'; case "String": 'input.readString();'; case "Bytes": 'input.read();'; - default: 'haxe.Unserializer.run(input.readString());'; + + case "Dynamic": + if (isMsgPack) { + trace('Remote param "$moduleName" is using MsgPack Serialization.'); + 'org.msgpack.MsgPack.decode(input.read());'; + } else { + trace('Remote param "$moduleName" is using haxe.Serializer.'); + 'haxe.Unserializer.run(input.readString());'; + } + + case "Enum": 'haxe.Unserializer.run(input.readString());'; // TODO + + default: + if (isHxbit(tp, false)) '(new hxbit.Serializer()).unserialize(input.read(),$moduleName);'; + else if (isTypedef(tp, false)) 'org.msgpack.MsgPack.decode(input.read());'; + else 'haxe.Unserializer.run(input.readString());'; } } @@ -324,10 +363,65 @@ class RemoteImpl case "Double": 'output.writeDouble($varname);'; case "String": 'output.writeString($varname);'; case "Bytes": 'output.write($varname);'; - default: 'output.writeString(haxe.Serializer.run($varname));'; + + case "Dynamic": + if (isMsgPack) 'output.write(org.msgpack.MsgPack.encode($varname));'; + else 'output.writeString(haxe.Serializer.run($varname));'; + + + case "Enum": 'output.writeString(haxe.Serializer.run($varname));'; // TODO + + default: + if (isHxbit(tp)) 'output.write((new hxbit.Serializer()).serialize($varname));'; + else if (isTypedef(tp)) 'output.write(org.msgpack.MsgPack.encode($varname));'; + else 'output.writeString(haxe.Serializer.run($varname));'; } } + public static function isHxbit(t:TypePath, silent:Bool = true):Bool + { + #if hxbit + var path = t.name + ".hx"; + if (t.pack.length != 0) path = t.pack.join("/") +"/" + path; + //trace(path); + try { + var p = Context.resolvePath(path); + var s:String = sys.io.File.getContent(p); + var r = new EReg('class\\s+'+t.name+getFullType(t)+'\\s+[^{]*?implements\\s+hxbit.Serializable[\\s{]', ""); + if (r.match(s)) { + if (!silent) trace("Remote param '"+t.pack.join(".") + ((t.pack.length != 0) ? "." : "") + t.name + "' is using hxbit Serialization."); + return true; + } + } + catch(e:Dynamic) {} + #end + return false; + } + + public static var isMsgPack:Bool = false; + public static function isTypedef(t:TypePath, silent:Bool = true):Bool + { + if (isMsgPack) + { + var path = t.name + ".hx"; + if (t.pack.length != 0) path = t.pack.join("/") +"/" + path; + //trace(path); + try { + var p = Context.resolvePath(path); + var s:String = sys.io.File.getContent(p); + var r = new EReg('typedef\\s+'+t.name+getFullType(t)+'\\s*=\\s*{', ""); + if (r.match(s)) { + if (!silent) trace("Remote param '"+t.pack.join(".") + ((t.pack.length != 0) ? "." : "") + t.name + "' is using MsgPack Serialization."); + return true; + } + } + catch(e:Dynamic) {} + } + + if (!silent) trace("Remote param '"+t.pack.join(".") + ((t.pack.length != 0) ? "." : "") + t.name + "' is using haxe.Serializer."); + return false; + } + #end } diff --git a/haxelib.json b/haxelib.json index 60e5357..2774f5f 100644 --- a/haxelib.json +++ b/haxelib.json @@ -4,9 +4,9 @@ "license": "MIT", "tags": ["peote", "socket", "tcp", "networking", "rpc"], "description": "Library to write TCP-Networking Applications", - "version": "0.6.8", + "version": "0.6.9", "classPath": "Source/", - "releasenote": "variable chunksizes and errorhandling", + "releasenote": "hxbit and msgpack-haxe bindings", "contributors": ["maitag"], "dependencies": { "peote-socket": "" diff --git a/samples/rpc/project.xml b/samples/rpc/project.xml index 775a255..ec2e719 100644 --- a/samples/rpc/project.xml +++ b/samples/rpc/project.xml @@ -1,7 +1,7 @@ - + @@ -14,6 +14,12 @@ + + + + + + @@ -21,6 +27,7 @@ + diff --git a/samples/rpc/src/MainOpenfl.hx b/samples/rpc/src/MainOpenfl.hx index 8d87ab0..a5c35ad 100644 --- a/samples/rpc/src/MainOpenfl.hx +++ b/samples/rpc/src/MainOpenfl.hx @@ -4,13 +4,12 @@ import haxe.ds.IntMap; import haxe.ds.StringMap; import haxe.ds.Vector; import haxe.io.Bytes; -import peote.net.Remote; - import openfl.display.Sprite; +import peote.bridge.PeoteSocketBridge; import peote.net.PeoteServer; import peote.net.PeoteClient; -import peote.bridge.PeoteSocketBridge; +import peote.net.Remote; import peote.io.Byte; import peote.io.UInt16; @@ -18,6 +17,9 @@ import peote.io.Int16; import peote.io.Int32; import peote.io.Double; +import remoteParams.User; +import remoteParams.Message; + import ui.OutputText; class MainOpenfl extends Sprite @@ -84,7 +86,17 @@ class MainOpenfl extends Sprite serverFunctions.maps = function(m:Map< Int, Map> >) { out.log('serverobject -> maps('); for (k in m.keys()) out.log(""+m.get(k)); - }; + }; + serverFunctions.hxbit = function(u:User) { + out.log('serverobject -> hxbit(${u.name}, ${u.age})'); + }; + serverFunctions.msgpack = function(o:Dynamic) { + out.log('serverobject -> msgpack($o)'); + }; + serverFunctions.msgpackTyped = function(m:Message) { + out.log('serverobject -> msgpackTyped($m)'); + }; + server.setRemote(userNr, serverFunctions); // --> Client's onRemote on will be called with 0 }, @@ -146,10 +158,10 @@ class MainOpenfl extends Sprite serverFunctions.message("hello from client", true); serverFunctions.numbers(255, 0xFFFF, 0x7FFF, 0x7FFFFFFF, 0x7FFFFFFF, 1.2345678901234, 1.2345678901234 ); - var v = new Vector>(2); + var v = new Vector>(3); v[0] = [1, 2]; v[1] = [3, 4, 5]; - //v[2] = null; // null will result on remote in an empty Array + v[2] = null; // null will result on remote in an empty Array serverFunctions.complex(Bytes.ofString("dada"), v); var list = new List(); for (i in 0...5) list.add(i); @@ -159,10 +171,17 @@ class MainOpenfl extends Sprite var m = [ 1 => ["a1" => [10,11], "b1" => [12,13]], 2 => ["a2" => [20, 21], "b2" => [22, 23]], - //7 => null // null will result on remote in an empty Map + 7 => null // null will result on remote in an empty Map ]; serverFunctions.maps(m); + var u = new User("Alice", 42, "test"); + serverFunctions.hxbit(u); + + var m:Message = {name:"Klaus", age:23}; + serverFunctions.msgpackTyped(m); + + serverFunctions.msgpack({name:"Bob", age:48, friends:["Mary","Johan"]}); }, onDisconnect: function(client:PeoteClient, reason:Int) { @@ -189,6 +208,13 @@ class ServerFunctions implements Remote { @:remote public var lists:List -> Void; //@:remote public var maps:IntMap< haxe.ds.StringMap< Array> > -> Void; @:remote public var maps:Map> > -> Void; + + // enable hxbit lib in project.xml to use it's serialization here ( https://lib.haxe.org/p/hxbit/ ) + @:remote public var hxbit: User -> Void; + + // enable msgpack lib in project.xml to use it's serialization here( https://lib.haxe.org/p/msgpack-haxe/ ) + @:remote public var msgpackTyped: Message -> Void; + @:remote public var msgpack: Dynamic -> Void; } class FirstClientFunctions implements Remote { diff --git a/samples/rpc/src/remoteParams/Message.hx b/samples/rpc/src/remoteParams/Message.hx new file mode 100644 index 0000000..1dc79b3 --- /dev/null +++ b/samples/rpc/src/remoteParams/Message.hx @@ -0,0 +1,8 @@ +package remoteParams; + +typedef Message = +{ + name: String, + age: Int, + ?other: String, +} \ No newline at end of file diff --git a/samples/rpc/src/remoteParams/User.hx b/samples/rpc/src/remoteParams/User.hx new file mode 100644 index 0000000..d96c678 --- /dev/null +++ b/samples/rpc/src/remoteParams/User.hx @@ -0,0 +1,21 @@ +package remoteParams; + +#if hxbit + class User implements hxbit.Serializable { + @:s public var name: String; // serialized + @:s public var age: Int; // serialized + public var other:String; // NOT serialized +#else + class User { + @:s public var name: String; // serialized + @:s public var age: Int; // serialized + public var other:String; // serialized +#end + + public function new(n:String, a:Int, o:String) { + name = n; + age = a; + other = o; + } +} +