Browse Source

added checkEOM, sequence UIDs, keep var initializers in unserialize()

ncannasse 9 years ago
parent
commit
172b53eb91
4 changed files with 148 additions and 62 deletions
  1. 5 1
      hxd/net/LocalHost.hx
  2. 64 48
      hxd/net/Macros.hx
  3. 59 8
      hxd/net/NetworkHost.hx
  4. 20 5
      hxd/net/Serializer.hx

+ 5 - 1
hxd/net/LocalHost.hx

@@ -33,8 +33,12 @@ class LocalClient extends NetworkClient {
 		pendingPos += len;
 		if( pendingPos == messageLength ) {
 			pendingPos = 0;
-			while( pendingPos < messageLength )
+			while( pendingPos < messageLength ) {
+				var oldPos = pendingPos;
 				pendingPos = processMessage(pendingBuffer, pendingPos);
+				if( host.checkEOM && pendingBuffer.get(pendingPos++) != NetworkHost.EOM )
+					throw "Message missing EOM " + pendingBuffer.sub(oldPos, pendingPos - oldPos).toHex()+"..."+(pendingBuffer.sub(pendingPos,hxd.Math.imin(messageLength-pendingPos,128)).toHex());
+			}
 			messageLength = -1;
 			readData();
 		}

+ 64 - 48
hxd/net/Macros.hx

@@ -439,6 +439,17 @@ class Macros {
 				}
 		}
 
+		var fieldsInits = [];
+		for( f in fields ) {
+			if( f.access.indexOf(AStatic) >= 0 ) continue;
+			switch( f.kind ) {
+			case FVar(_, e), FProp(_, _, _, e) if( e != null ):
+				// before unserializing
+				fieldsInits.push({ expr : EBinop(OpAssign,{ expr : EConst(CIdent(f.name)), pos : e.pos },e), pos : e.pos });
+			default:
+			}
+		}
+
 		var sup = cl.superClass;
 		var isSubSer = sup != null && isSerializable(sup.t);
 
@@ -446,8 +457,8 @@ class Macros {
 		var el = [], ul = [];
 		for( f in toSerialize ) {
 			var fname = f.f.name;
-			el.push(withPos(macro hxd.net.Macros.serializeValue(ctx, this.$fname),f.f.pos));
-			ul.push(withPos(macro hxd.net.Macros.unserializeValue(ctx, this.$fname),f.f.pos));
+			el.push(withPos(macro hxd.net.Macros.serializeValue(__ctx, this.$fname),f.f.pos));
+			ul.push(withPos(macro hxd.net.Macros.unserializeValue(__ctx, this.$fname),f.f.pos));
 		}
 
 		var access = [APublic];
@@ -459,7 +470,7 @@ class Macros {
 				pos : pos,
 				access : [APublic],
 				meta : [{ name : ":noCompletion", pos : pos }],
-				kind : FVar(macro : Int, macro @:privateAccess ++hxd.net.Serializer.UID),
+				kind : FVar(macro : Int, macro @:privateAccess hxd.net.Serializer.allocUID()),
 			});
 		fields.push({
 			name : "__clid",
@@ -476,56 +487,60 @@ class Macros {
 			kind : FFun({ args : [], ret : macro : Int, expr : macro return __clid }),
 		});
 
-		if( toSerialize.length == 0 && isSubSer )
-			return fields;
-
-		fields.push({
-			name : "serialize",
-			pos : pos,
-			meta : [ { name:":keep", pos:pos } ],
-			access : access,
-			kind : FFun({
-				args : [ { name : "ctx", type : macro : hxd.net.Serializer } ],
-				ret : null,
-				expr : macro @:privateAccess { ${ if( isSubSer ) macro super.serialize(ctx) else macro { } }; $b{el} }
-			}),
-		});
+		var needSerialize = toSerialize.length != 0 || !isSubSer;
+		var needUnserialize = needSerialize || fieldsInits.length != 0;
 
-		var unserExpr = macro @:privateAccess { ${ if( isSubSer ) macro super.unserialize(ctx) else macro { } }; $b{ul} };
+		if( needSerialize ) {
+			fields.push({
+				name : "serialize",
+				pos : pos,
+				meta : [ { name:":keep", pos:pos } ],
+				access : access,
+				kind : FFun({
+					args : [ { name : "__ctx", type : macro : hxd.net.Serializer } ],
+					ret : null,
+					expr : macro @:privateAccess { ${ if( isSubSer ) macro super.serialize(__ctx) else macro { } }; $b{el} }
+				}),
+			});
+		}
 
-		for( f in fields )
-			if( f.name == "unserialize" ) {
-				var found = false;
-				function repl(e:Expr) {
-					switch( e.expr ) {
-					case ECall( { expr : EField( { expr : EConst(CIdent("super")) }, "unserialize") }, [ctx]):
-						found = true;
-						return macro { var ctx : hxd.net.Serializer = $ctx; $unserExpr; }
+		if( needUnserialize ) {
+			var unserExpr = macro @:privateAccess { $b{fieldsInits}; ${ if( isSubSer ) macro super.unserialize(__ctx) else macro { } }; $b{ul} };
+
+			for( f in fields )
+				if( f.name == "unserialize" ) {
+					var found = false;
+					function repl(e:Expr) {
+						switch( e.expr ) {
+						case ECall( { expr : EField( { expr : EConst(CIdent("super")) }, "unserialize") }, [ctx]):
+							found = true;
+							return macro { var __ctx : hxd.net.Serializer = $ctx; $unserExpr; }
+						default:
+							return haxe.macro.ExprTools.map(e, repl);
+						}
+					}
+					switch( f.kind ) {
+					case FFun(f):
+						f.expr = repl(f.expr);
 					default:
-						return haxe.macro.ExprTools.map(e, repl);
 					}
+					f.meta.push( { name:":keep", pos:pos } );
+					if( !found ) Context.error("Override of unserialize() with no super.unserialize(ctx) found", f.pos);
+					return fields;
 				}
-				switch( f.kind ) {
-				case FFun(f):
-					f.expr = repl(f.expr);
-				default:
-				}
-				f.meta.push( { name:":keep", pos:pos } );
-				if( !found ) Context.error("Override of unserialize() with no super.unserialize(ctx) found", f.pos);
-				return fields;
-			}
 
-		fields.push({
-			name : "unserialize",
-			pos : pos,
-			meta : [ { name:":keep", pos:pos } ],
-			access : access,
-			kind : FFun({
-				args : [ { name : "ctx", type : macro : hxd.net.Serializer } ],
-				ret : null,
-				expr : unserExpr,
-			}),
-		});
+			fields.push({
+				name : "unserialize",
+				pos : pos,
+				meta : [ { name:":keep", pos:pos } ],
+				access : access,
+				kind : FFun({
+					args : [ { name : "__ctx", type : macro : hxd.net.Serializer } ],
+					ret : null,
+					expr : unserExpr,
+				}),
+			});
+		}
 
 		return fields;
 	}
@@ -934,7 +949,8 @@ class Macros {
 					$b{[
 						for( a in f.args )
 							macro hxd.net.Macros.serializeValue(__ctx,$i{a.name})
-					]};
+					] };
+					@:privateAccess __host.endRPC();
 				};
 
 				var rpcExpr = switch( r.mode ) {

+ 59 - 8
hxd/net/NetworkHost.hx

@@ -4,6 +4,7 @@ class NetworkClient {
 
 	var host : NetworkHost;
 	var resultID : Int;
+	public var seqID : Int;
 	public var ownerObject : NetworkSerializable;
 
 	public function new(h) {
@@ -29,7 +30,8 @@ class NetworkClient {
 	function processMessage( bytes : haxe.io.Bytes, pos : Int ) {
 		var ctx = host.ctx;
 		ctx.setInput(bytes, pos);
-		switch( ctx.getByte() ) {
+		var mid = ctx.getByte();
+		switch( mid ) {
 		case NetworkHost.SYNC:
 			var o : hxd.net.NetworkSerializable = cast ctx.refs[ctx.getInt()];
 			var old = o.__bits;
@@ -45,10 +47,14 @@ class NetworkClient {
 		case NetworkHost.UNREG:
 			var o : hxd.net.NetworkSerializable = cast ctx.refs[ctx.getInt()];
 			o.enableReplication = false;
-			ctx.refs[o.__uid] = null;
+			ctx.refs.remove(o.__uid);
 		case NetworkHost.FULLSYNC:
-			ctx.refs = [];
-			@:privateAccess ctx.newObjects = [];
+			ctx.refs = new Map();
+			@:privateAccess {
+				hxd.net.Serializer.UID = 0;
+				hxd.net.Serializer.SEQ = ctx.getByte();
+				ctx.newObjects = [];
+			};
 			while( true ) {
 				var o = ctx.getAnyRef();
 				if( o == null ) break;
@@ -78,6 +84,8 @@ class NetworkClient {
 			} else
 				o.networkRPC(ctx, fid, this);
 
+			if( host.checkEOM ) ctx.addByte(NetworkHost.EOM);
+
 			host.doSend();
 			host.targetClient = null;
 			resultID = old;
@@ -129,6 +137,10 @@ class NetworkHost {
 	static inline var RPC_WITH_RESULT = 6;
 	static inline var RPC_RESULT = 7;
 	static inline var MSG		 = 8;
+	static inline var EOM		 = 0xFF;
+
+	public var checkEOM(get, never) : Bool;
+	inline function get_checkEOM() return true;
 
 	public static var current : NetworkHost = null;
 
@@ -168,7 +180,7 @@ class NetworkHost {
 	}
 
 	public function loadSave<T:Serializable>( bytes : haxe.io.Bytes, c : Class<T> ) : T {
-		ctx.refs = [];
+		ctx.refs = new Map();
 		@:privateAccess ctx.newObjects = [];
 		ctx.setInput(bytes, 0);
 		return ctx.getKnownRef(c);
@@ -195,6 +207,7 @@ class NetworkHost {
 		targetClient = to;
 		ctx.addByte(MSG);
 		ctx.addString(haxe.Serializer.run(msg));
+		if( checkEOM ) ctx.addByte(EOM);
 		doSend();
 		targetClient = null;
 	}
@@ -203,6 +216,7 @@ class NetworkHost {
 		if( !isAuth )
 			return true;
 		if( owner == null ) {
+			if( checkEOM ) ctx.addByte(EOM);
 			doSend();
 			targetClient = null;
 			return true;
@@ -236,18 +250,40 @@ class NetworkHost {
 		return ctx;
 	}
 
+	inline function endRPC() {
+		if( checkEOM ) ctx.addByte(EOM);
+	}
+
 	function fullSync( c : NetworkClient ) {
 		if( !pendingClients.remove(c) )
 			return;
 		flush();
+
+		// unique client sequence number
+		var seq = clients.length + 1;
+		while( true ) {
+			var found = false;
+			for( c in clients )
+				if( c.seqID == seq ) {
+					found = true;
+					break;
+				}
+			if( !found ) break;
+			seq++;
+		}
+		ctx.addByte(seq);
+		c.seqID = seq;
+
 		clients.push(c);
 		var refs = ctx.refs;
 		ctx.begin();
-		ctx.addByte(NetworkHost.FULLSYNC);
+		ctx.addByte(FULLSYNC);
+		ctx.addByte(c.seqID);
 		for( o in refs )
 			if( o != null )
 				ctx.addAnyRef(o);
 		ctx.addAnyRef(null);
+		if( checkEOM ) ctx.addByte(EOM);
 		targetClient = c;
 		doSend();
 		targetClient = null;
@@ -295,13 +331,26 @@ class NetworkHost {
 		o.__host = this;
 		if( ctx.refs[o.__uid] != null )
 			return;
+		if( !isAuth ) {
+			var owner = o.networkGetOwner();
+			if( owner == null || owner != self.ownerObject )
+				throw "Can't register "+o+" without ownership (" + owner + " should be " + self.ownerObject + ")";
+		}
 		if( logger != null )
-			logger("Register " + o+"#"+o.__uid);
+			logger("Register " + o + "#" + o.__uid);
 		ctx.addByte(REG);
 		ctx.addAnyRef(o);
+		if( checkEOM ) ctx.addByte(EOM);
 	}
 
 	function unregister( o : NetworkSerializable ) {
+		if( o.__host == null )
+			return;
+		if( !isAuth ) {
+			var owner = o.networkGetOwner();
+			if( owner == null || owner != self.ownerObject )
+				throw "Can't unregister "+o+" without ownership (" + owner + " should be " + self.ownerObject + ")";
+		}
 		flushProps(); // send changes
 		o.__host = null;
 		o.__bits = 0;
@@ -309,7 +358,8 @@ class NetworkHost {
 			logger("Unregister " + o+"#"+o.__uid);
 		ctx.addByte(UNREG);
 		ctx.addInt(o.__uid);
-		ctx.refs[o.__uid] = null;
+		if( checkEOM ) ctx.addByte(EOM);
+		ctx.refs.remove(o.__uid);
 	}
 
 	function doSend() {
@@ -343,6 +393,7 @@ class NetworkHost {
 				ctx.addByte(SYNC);
 				ctx.addInt(o.__uid);
 				o.networkFlush(ctx);
+				if( checkEOM ) ctx.addByte(EOM);
 				hasData = true;
 			}
 			var n = o.__next;

+ 20 - 5
hxd/net/Serializer.hx

@@ -3,6 +3,19 @@ package hxd.net;
 class Serializer {
 
 	static var UID = 0;
+	static var SEQ = 0;
+	static inline var SEQ_BITS = 8;
+	static inline var SEQ_MASK = 0xFFFFFFFF >>> SEQ_BITS;
+
+	public static function resetCounters() {
+		UID = 0;
+		SEQ = 0;
+	}
+
+	static inline function allocUID() {
+		return (SEQ << (32 - SEQ_BITS)) | (++UID);
+	}
+
 	static var CLASSES : Array<Class<Dynamic>> = [];
 	static var CL_BYID = null;
 	static var CLIDS = null;
@@ -44,7 +57,7 @@ class Serializer {
 		}
 	}
 
-	public var refs : Array<Serializable>;
+	public var refs : Map<Int,Serializable>;
 	var newObjects : Array<Serializable>;
 	var out : haxe.io.BytesBuffer;
 	var input : haxe.io.Bytes;
@@ -56,7 +69,7 @@ class Serializer {
 
 	public function begin() {
 		out = new haxe.io.BytesBuffer();
-		refs = [];
+		refs = new Map();
 	}
 
 	public function setInput(data, pos) {
@@ -71,7 +84,7 @@ class Serializer {
 	}
 
 	public function unserialize<T:Serializable>( data : haxe.io.Bytes, c : Class<T> ) : T {
-		refs = [];
+		refs = new Map();
 		setInput(data, 0);
 		return getRef(c, Reflect.field(c,"__clid"));
 	}
@@ -250,7 +263,8 @@ class Serializer {
 		if( id == 0 ) return null;
 		if( refs[id] != null )
 			return cast refs[id];
-		if( UID < id ) UID = id;
+		var rid = id & SEQ_MASK;
+		if( UID < rid ) UID = rid;
 		var clid = getInt();
 		var i : Serializable = Type.createEmptyInstance(CLASSES[clid]);
 		if( newObjects != null ) newObjects.push(i);
@@ -265,7 +279,8 @@ class Serializer {
 		if( id == 0 ) return null;
 		if( refs[id] != null )
 			return cast refs[id];
-		if( UID < id ) UID = id;
+		var rid = id & SEQ_MASK;
+		if( UID < rid ) UID = rid;
 		var clid = CLIDS[clid];
 		var i = Type.createEmptyInstance(clid == 0 ? c : cast CL_BYID[getByte()]);
 		if( newObjects != null ) newObjects.push(i);