Преглед на файлове

new save format, includes schema data

ncannasse преди 9 години
родител
ревизия
8741fa0d54
променени са 3 файла, в които са добавени 126 реда и са изтрити 39 реда
  1. 9 30
      hxd/net/NetworkHost.hx
  2. 16 2
      hxd/net/Schema.hx
  3. 101 7
      hxd/net/Serializer.hx

+ 9 - 30
hxd/net/NetworkHost.hx

@@ -195,45 +195,24 @@ class NetworkHost {
 
 	public function saveState() {
 		var s = new hxd.net.Serializer();
-		s.begin();
-		var clids = [];
+		s.beginSave();
 		for( r in ctx.refs )
-			if( !s.refs.exists(r.__uid) ) {
-				var cl = r.getCLID();
-				var cval = Type.getClass(r);
-				s.addCLID(cl);
-				if( !clids[cl] ) {
-					clids[cl] = true;
-					s.addString(Type.getClassName(cval));
-				}
-				s.addKnownRef(r);
-				s.addByte(EOM);
-			}
-		s.addCLID(0xFFFF);
-		return s.end();
+			if( !s.refs.exists(r.__uid) )
+				s.addAnyRef(r);
+		s.addAnyRef(null);
+		return s.endSave();
 	}
 
 	public function loadSave( bytes : haxe.io.Bytes ) {
 		ctx.refs = new Map();
 		@:privateAccess ctx.newObjects = [];
 		ctx.setInput(bytes, 0);
-		var classByName = new Map();
-		for( c in @:privateAccess Serializer.CLASSES )
-			classByName.set(Type.getClassName(c), c);
-		var clids = [];
+		ctx.beginLoadSave();
 		while( true ) {
-			var cl = ctx.getCLID();
-			if( cl == 0xFFFF ) break;
-			var cval = clids[cl];
-			if( cval == null ) {
-				var cname = ctx.getString();
-				cval = classByName.get(cname);
-				if( cval == null ) throw "Unsupported class " + cname;
-				clids[cl] = cval;
-			}
-			ctx.getKnownRef(cval);
-			if( ctx.getByte() != EOM ) throw "Save file is not compatible with current version";
+			var v = ctx.getAnyRef();
+			if( v == null ) break;
 		}
+		ctx.setInput(null,0);
 	}
 
 	function mark(o:NetworkSerializable) {

+ 16 - 2
hxd/net/Schema.hx

@@ -2,8 +2,10 @@ package hxd.net;
 
 typedef FieldType = Macros.PropTypeDesc<FieldType>;
 
-class Schema #if !macro implements Serializable #end {
+#if !macro
+class Schema implements Serializable {
 
+	public var checkSum(get,never) : Int;
 	@:s @:notMutable public var fieldsNames : Array<String>;
 	@:s @:notMutable public var fieldsTypes : Array<FieldType>;
 
@@ -12,4 +14,16 @@ class Schema #if !macro implements Serializable #end {
 		fieldsTypes = [];
 	}
 
-}
+	function get_checkSum() {
+		var s = new Serializer();
+		s.begin();
+		var old = __uid;
+		__uid = 0;
+		s.addKnownRef(this);
+		__uid = old;
+		var bytes = s.end();
+		return haxe.crypto.Crc32.make(bytes);
+	}
+
+}
+#end

+ 101 - 7
hxd/net/Serializer.hx

@@ -64,6 +64,7 @@ class Serializer {
 	var out : haxe.io.BytesBuffer;
 	var input : haxe.io.Bytes;
 	var inPos : Int;
+	var usedClasses : Array<Bool> = [];
 
 	public function new() {
 		if( CLIDS == null ) initClassIDS();
@@ -115,6 +116,10 @@ class Serializer {
 		}
 	}
 
+	public inline function addInt32(v:Int) {
+		out.addInt32(v);
+	}
+
 	public inline function addFloat(v:Float) {
 		out.addFloat(v);
 	}
@@ -208,6 +213,12 @@ class Serializer {
 		return v;
 	}
 
+	public inline function getInt32() {
+		var v = input.getInt32(inPos);
+		inPos += 4;
+		return v;
+	}
+
 	public inline function getDouble() {
 		var v = input.getDouble(inPos);
 		inPos += 8;
@@ -267,7 +278,7 @@ class Serializer {
 		return (getByte() << 8) | getByte();
 	}
 
-	public inline function addAnyRef( s : Serializable ) {
+	public function addAnyRef( s : Serializable ) {
 		if( s == null ) {
 			addByte(0);
 			return;
@@ -276,11 +287,13 @@ class Serializer {
 		if( refs[s.__uid] != null )
 			return;
 		refs[s.__uid] = s;
-		addCLID(s.getCLID());
+		var index = s.getCLID();
+		usedClasses[index] = true;
+		addCLID(index);
 		s.serialize(this);
 	}
 
-	public inline function addKnownRef( s : Serializable ) {
+	public function addKnownRef( s : Serializable ) {
 		if( s == null ) {
 			addByte(0);
 			return;
@@ -289,13 +302,15 @@ class Serializer {
 		if( refs[s.__uid] != null )
 			return;
 		refs[s.__uid] = s;
-		var clid = CLIDS[s.getCLID()];
+		var index = s.getCLID();
+		usedClasses[index] = true;
+		var clid = CLIDS[index];
 		if( clid != 0 )
 			addCLID(clid);
 		s.serialize(this);
 	}
 
-	public inline function getAnyRef() : Serializable {
+	public function getAnyRef() : Serializable {
 		var id = getInt();
 		if( id == 0 ) return null;
 		if( refs[id] != null )
@@ -311,7 +326,7 @@ class Serializer {
 		return i;
 	}
 
-	public inline function getRef<T:Serializable>( c : Class<T>, clid : Int ) : T {
+	public function getRef<T:Serializable>( c : Class<T>, clid : Int ) : T {
 		var id = getInt();
 		if( id == 0 ) return null;
 		if( refs[id] != null )
@@ -319,7 +334,7 @@ class Serializer {
 		var rid = id & SEQ_MASK;
 		if( UID < rid ) UID = rid;
 		var clid = CLIDS[clid];
-		var i = Type.createEmptyInstance(clid == 0 ? c : cast CL_BYID[getCLID()]);
+		var i : T = Type.createEmptyInstance(clid == 0 ? c : cast CL_BYID[getCLID()]);
 		if( newObjects != null ) newObjects.push(i);
 		i.__uid = id;
 		refs[id] = i;
@@ -331,4 +346,83 @@ class Serializer {
 		return getRef(c, untyped c.__clid);
 	}
 
+
+	public function beginSave() {
+		begin();
+		usedClasses = [];
+	}
+
+	public function endSave() {
+		var content = end();
+		begin();
+		var classes = [];
+		var schemas = [];
+		var sidx = CLASSES.indexOf(Schema);
+		for( i in 0...usedClasses.length ) {
+			if( !usedClasses[i] || i == sidx ) continue;
+			var c = CLASSES[i];
+			var schema = (Type.createEmptyInstance(c) : Serializable).getSerializeSchema();
+			schemas.push(schema);
+			classes.push(i);
+			addKnownRef(schema);
+		}
+		var schemaData = end();
+		begin();
+		addString("HXS");
+		addByte(1);
+		for( i in 0...classes.length ) {
+			var index = classes[i];
+			addString(Type.getClassName(CLASSES[index]));
+			addCLID(index);
+			addInt32(schemas[i].checkSum);
+		}
+		addString(null);
+		addInt(schemaData.length);
+		out.add(schemaData);
+		out.add(content);
+		return end();
+	}
+
+	public function beginLoadSave() {
+		var classByName = new Map();
+		var convert = [];
+		var mapClasses = [];
+		var needConvert = false;
+		var needReindex = false;
+		for( i in 0...CLASSES.length )
+			classByName.set(Type.getClassName(CLASSES[i]), i);
+		if( getString() != "HXS" )
+			throw "Invalid HXS data";
+		var version = getByte();
+		if( version != 1 )
+			throw "Unsupported HXS version " + version;
+		while( true ) {
+			var clname = getString();
+			if( clname == null ) break;
+			var index = getCLID();
+			var crc = getInt32();
+
+			var ourClassIndex = classByName.get(clname);
+			if( ourClassIndex == null ) throw "Missing class " + clname+" found in HXS data";
+			var ourSchema = (Type.createEmptyInstance(CLASSES[ourClassIndex]) : Serializable).getSerializeSchema();
+			if( ourSchema.checkSum != crc ) {
+				needConvert = true;
+				convert[index] = ourSchema;
+			}
+			if( index != ourClassIndex )
+				needReindex = true;
+			mapClasses[index] = ourClassIndex;
+		}
+		if( needConvert ) {
+			throw "TODO : convert schema (save file not compatible)";
+		} else {
+			// skip schema data
+			var schemaDataSize = getInt();
+			inPos += schemaDataSize;
+		}
+		if( needReindex ) {
+			throw "TODO : reindex (save file not compatible)";
+		}
+	}
+
 }