Coder Social home page Coder Social logo

hxbit's Introduction

HxBit

HxBit is a binary serialization and network synchronization library for Haxe.

Installation

Install through haxelib with haxelib install hxbit and use -lib hxbit to use it.

Serialization

You can serialize objects by implementing the hxbit.Serializable interface. You need to specify which fields you want to serialize by using the @:s metadata:

class User implements hxbit.Serializable {
    @:s public var name : String;
    @:s public var age : Int;
    @:s public var friends : Array<User>;
    ...
}

This will automatically add a few methods and variables to your User class:

/**
  These fields are automatically generated when implementing the interface.
**/
interface Serializable {
	/** Unique identifier for the object, automatically set on new() **/
	public var __uid : Int;
	/** Returns the unique class id for this object **/
	public function getCLID() : Int;
	/** Serialize the object id and fields using this Serializer **/
	public function serialize( ctx : hxbit.Serializer ) : Void;
	/** Unserialize object fields using this Serializer **/  
	public function unserialize( ctx : hxbit.Serializer ) : Void;
	/** Returns the object data schema **/  
	public function getSerializeSchema() : hxbit.Schema;
}

This allows you to serialize/unserialize using this code:

var s = new hxbit.Serializer();
var bytes = s.serialize(user);
....
var u = new hxbit.Serializer();
var user = u.unserialize(bytes, User);
....

Comparison with haxe.Serializer/Unserializer

Haxe standard library serialization works by doing runtime type checking, which is slower. However it can serialize any value even if it's not been marked as Serializable.

HxBit serialization uses macro to generate strictly typed serialization code that allows very fast I/O. OTOH this increase code size and is using a less readable binary format instead of Haxe standard serialization which uses a text representation.

Supported types

The following types are supported:

  • Int / haxe.UInt : stored as either 1 byte (0-254) or 5 bytes (0xFF + 32 bits)
  • Float : stored as single precision 32 bits IEEE float
  • Bool : stored as single by (0 or 1)
  • String : stored as size+1 prefix, then utf8 bytes (0 prefix = null)
  • any Enum value : stored as index byte + args values
  • haxe.io.Bytes : stored as size+1 prefix + raw bytes (0 prefix = null)
  • Array<T> and haxe.ds.Vector<T> : stored as size+1 prefix, then T list (0 prefix = null)
  • Map<K,V> : stored as size+1 prefix, then (K,V) pairs (0 prefix = null)
  • Null<T> : stored as a byte 0 for null, 1 followed by T either
  • Serializable (any other serializable instance) : stored with __uid, then class id and data if if was not already serialized
  • Strutures { field : T... } : optimized to store a bit field of not null values, then only defined fields values

Default values

When unserializing, the class constructor is not called. If you want to have some non-serialized field already initialized before starting unserialization, you can set the default value using Haxe initializers:

class User implements hxbit.Serializable {
    ...
    // when unserializing, someOtherField will be set to [] instead of null
    var someOtherField : Array<Int> = []; 
}

Unsupported types

If you want to serialize unsupported types, you could implement your own serialization through the optional methods customSerialize and customUnserialize.

class Float32ArrayContainer implements hxbit.Serializable {

    public var value:Float32Array;

    ...

    @:keep
    public function customSerialize(ctx : hxbit.Serializer) {
        ctx.addInt(value.length);
        for(i in 0...value.length)
            ctx.addFloat(value[i]);
    }

    @:keep
    public function customUnserialize(ctx : hxbit.Serializer) {
        var length = ctx.getInt();
        var tempArray = new Array<Float>();
        for(i in 0...length)
            tempArray.push(ctx.getFloat());

        value = new Float32Array(tempArray);
    }
}

Versioning

HxBit serialization is capable of performing versioning, by storing in serialized data the schema of each serialized class, then comparing it to the current schema when unserializing, making sure that data is not corrupted.

In order to save some data with versioning, use the following:

var s = new hxbit.Serializer();
s.beginSave();
// ... serialize your data...
var bytes = s.endSave();

And in order to load versionned data, use:

var s = new hxbit.Serializer();
s.beginLoad(bytes);
// .. unserializer your data

Versioned data is slightly larger than unversioned one since it contains the Schema data of each serialized class.

Currently versioning handles:

  • removing fields (previous data is ignored)
  • adding fields (they are set to default value: 0 for Int/Float, false for Bool, empty Array/Map/etc.)

More convertions can be easily added in Serializer.convertValue, including custom ones if you extends Serializer.

Networking

Additionaly to serialization, HxBit supports synchronization of objects over network and Remote Procedure Call.

Example

An example of networking in action can be found as part of the Heaps samples here: https://github.com/ncannasse/heaps/blob/master/samples/Network.hx

In order to use Networking, your classes need to implement hxbit.NetworkSerializable. You also need to implement a NetworkHost such as done with Heaps here: https://github.com/ncannasse/heaps/blob/master/hxd/net/SocketHost.hx

Principles

A host is a NetworkHost instance which handles communications betwen a server (or authority) and several clients.

A NetworkSerializable can be shared over the network by setting enableReplication = true on it.

When a shared Serializable field is modified, we store a bit to mark it as changed. When host.sync() is called (or when a RPC occurs), we send the modified fields over the network so the objects state is correctly replicated on other connected nodes.

Only the authority can modify the serializable fields and call normal RPCs. By default an object doesn't have any ownership rights, you can define the networkAllow() method to specify it.

In order to detect whenever a new object has been shared over the network, you must implement the alive() method, which will be triggered once an object has been fully initialized.

Remote Procedure Calls (RPC)

RPC can be easily performed by tagging a shared object member method with @:rpc:

class Cursor implements hxbit.NetworkSerializable {
   @:rpc public function blink() {
      ...
   }
}

There are different RPC modes, which can be specified by using @:rpc(mode):

  • all (default) : When called on the client, will forward the call on the server, but not execute locally. When called on the server, will forward the call to the clients (and force its execution), then execute.
  • clients : When called on the server, will forward the call to the clients, but not execute locally. When called on the client, will execute locally.
  • server : When called on the client: will forward the call the server (if networkAllow(RPCServer) allows it), but not execute locally. When called on the server, will execute locally.
  • owner : When called on the client: will forward the call to the server (if networkAllow(RPC) allows it), but not execute locally. When called on the server: will forward the call to the owners as defined by networkAllow(Ownership).
  • immediate : Like all but executes immediately locally

Return values are possible unless you are in all mode, and will add an extra callback to capture the result asynchronously:

   @:rpc(server) public function sendAction( act : String ) : Bool {
   }
   
   ...
   sendAction("test", function(onResult) { ... });
   

RPC executing on the client can change network properties on the current object without triggering errors or network data

Filtering

You might not want to send the property value everytime it is modified. You can specify the following metadata together with @:s in order to perform some filtering:

  • @:increment(value) only send if the value has changed by more than the increment. For instance if you write @:increment(10) the value will only be sent if its tens value change.
  • @:condSend(cond) only send if the cond is true. You can use @:condSend(false) to disable network sync for this property (but still keep it serializable - for instance for a server only value). You can also use @:condSend(x > current) to only send if the value x is greater than the last sent value for the current property. You can insert any expression here including calls.
  • @:notMutable is used to disable proxy creation on a property (see below)

Proxys

In order to track changes inside a mutable value such as Array/Map/Vector/Structure, a proxy object will be used to wrap the value and make sure that all changes to it correctly set the mark bit.

At the moment, each change in one of these structures will send the whole content again. In the future it might be possible to track each mutation and only send the information necessary to replicate this specific change.

Local change

Sometimes you might want to perform some local change without triggering network data. You must be sure that the same changes occur on the server and all the connected clients or else you risk having unsynchronized states.

This can be performed by wrapping the changes as the following example shows:

function teleport( t : Target ) {
    networkLocalChanges(function() {
       var pos = t.getPosition();
       // changes to x/y will not be sent over the network
       // clients are also allowed to change serialized properties this way
       x = pos.x;
       y = pos.y;
    });
}

Cancel change

You can also cancel the change of a property can calling networkCancelProperty(propId) the id of the property can be obtained from its name by accessing networkProp<Name>, with name being the name of the property with first letter uppercased (networkPropPosition for position for instance)

Save and load

The whole set of currently shared network objects can be saved using host.saveState() and loaded using host.loadState(bytes). It uses the versionning decribed previously. Once loaded, call host.makeAlive() to make sure all alive() calls are made to object.

hxbit's People

Contributors

azrou avatar chriba avatar clenhof avatar dfadev avatar klabz avatar nadako avatar ncannasse avatar nuclearcookie avatar pperidont avatar speedphoenix avatar starburst997 avatar thalesalexcin avatar trethaller avatar yuxiaomao avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hxbit's Issues

Loopback client no longer works.

I think that something changed recently where the server and client share registration. I have a loopback client that now is telling me there are duplicate registrations on the first object.

The #if hxbit_visibility that got introduced seems to switch between different usages of 'globalCtx'. I'm wondering if there's an assumption that the program wouldn't have both a client and server inside the same application.

I'm going to keep digging.

Executing method on unserializing attribute

I have serializable Models and json-loaded Definitions. I use a serializable Reference object to point to a Definition class and a name. When I initialize the Reference, it fetches the Definition from a name.

Usually, I'd keep an array of references and initialize them all when everything is loaded. Unfortunately, when I unserialize Models which contain References, I cannot know that the Reference was unserialized and, since the constructor was not called, I cannot add it to a static array of references.

How would I call a method on a certain type of object when it is unserialized if this object is serialized as part of another object?

I thought serializer.unserialize(instance) was called on every child object but I discovered with a custom serializer that it is not the case.
I cannot seem to override methods added by hxbit.Serializable, though I haven't tried with macros.

Pseudocode

class Reference<T>
  static all:Array<Reference>
  @:s name:String
  @:s className:String
  value:T
  function initialize()
    return MyDefinition.all.get(this.name) // using reflection

class MyDefinition extends Definition
  static all:StringMap<MyDefinition>
  name:Int
  x:Int
  y:Int

class MyModel extends Model
  @:s myDef:Reference<MyDefinition>

Objective
unserialize a MyModel instance, which also unserializes a Reference instance
call initialize() on the Reference instance, or simply store it in Reference.all for subsequent handling

Constraints
While I understand that if MyDefinition was serialized, this would remove the need for a Reference object, in this specific case the feel needs to be json-loaded as it might change. Unserialized Models need to access up to date Definition data at any time.

Serialization without including UID

This is a feature request, but I'd like to hear the producers' thoughts first.

hxbit is amazing.
I'm trying to use hxbit for the structure of the save data and system data.
Because the serialized data contains UIDs, we are having the following problems

  • Make my save data bigger
  • I can't use it to verify that the data matches

So I want a serialization that does not include UID.

breaks with dox, any suggested work round ?

Just thought I would add dox to hxTShopify, but I am getting a lot of errors
Haxe Compiler 4.2.0-rc.1+5d48282d2 - (C)2005-2020 Haxe Foundation

/usr/local/lib/haxeLibrary/hxbit/git/hxbit/Schema.hx:42: characters 13-18 : Unknown identifier : __uid
/usr/local/lib/haxeLibrary/hxbit/git/hxbit/Schema.hx:43: characters 3-8 : Unknown identifier : __uid
/usr/local/lib/haxeLibrary/hxbit/git/hxbit/Schema.hx:45: characters 3-8 : Unknown identifier : __uid
/usr/local/lib/haxeLibrary/hxbit/git/hxbit/Serializer.hx:616: characters 23-28 : hxbit.Schema has no field __uid
/usr/local/lib/haxeLibrary/hxbit/git/hxbit/Serializer.hx:680: characters 24-29 : hxbit.Schema has no field __uid
src/hxTShopify/t/ProductSerializer.hx:4: characters 7-24 : Field __uid needed by hxbit.Serializable is missing
src/hxTShopify/t/ProductSerializer.hx:4: characters 7-24 : Field getCLID needed by hxbit.Serializable is missing
src/hxTShopify/t/ProductSerializer.hx:4: characters 7-24 : Field getSerializeSchema needed by hxbit.Serializable is missing
src/hxTShopify/t/ProductSerializer.hx:4: characters 7-24 : Field serialize needed by hxbit.Serializable is missing
src/hxTShopify/t/ProductSerializer.hx:4: characters 7-24 : Field unserialize needed by hxbit.Serializable is missing
src/hxTShopify/t/ProductSerializer.hx:4: characters 7-24 : Field unserializeInit needed by hxbit.Serializable is missing
/usr/local/lib/haxeLibrary/hxbit/git/hxbit/Schema.hx:27: characters 7-13 : Field __uid needed by hxbit.Serializable is missing
/usr/local/lib/haxeLibrary/hxbit/git/hxbit/Schema.hx:27: characters 7-13 : Field getCLID needed by hxbit.Serializable is missing
/usr/local/lib/haxeLibrary/hxbit/git/hxbit/Schema.hx:27: characters 7-13 : Field getSerializeSchema needed by hxbit.Serializable is missing
/usr/local/lib/haxeLibrary/hxbit/git/hxbit/Schema.hx:27: characters 7-13 : Field serialize needed by hxbit.Serializable is missing
/usr/local/lib/haxeLibrary/hxbit/git/hxbit/Schema.hx:27: characters 7-13 : Field unserialize needed by hxbit.Serializable is missing
/usr/local/lib/haxeLibrary/hxbit/git/hxbit/Schema.hx:27: characters 7-13 : Field unserializeInit needed by hxbit.Serializable is missing

My ProductSerializer I have not looked at recently but I think it worked fine last time I used it:

package hxTShopify.t;
import hxTShopify.t.Product;
import hxbit.Serializable;
class ProductSerializer implements hxbit.Serializable {
    @:s public var product : Product;
    public function new( product_ ){
        product = ( product_: ProductWrapper );
    }
    public static function product2Bytes( p: Product ): haxe.io.Bytes {
        var productSerializer = new ProductSerializer( new ProductWrapper( p ) );// important make sure id's are String
        var s = new hxbit.Serializer();
        var bytesOut = s.serialize( productSerializer );
        return bytesOut;
    }
    public static function bytes2Product( bytesIn: haxe.io.Bytes ): Product {
        var u = new hxbit.Serializer();
        var productSerializerOut = u.unserialize( bytesIn, ProductSerializer );
        return productSerializerOut.product;
    }
}

To see the error:

-cmd haxe git folderNode https://github.com/nanjizal/folderNode.git
# -cmd haxe git hxbit https://github.com/HeapsIO/hxbit.git # you probably have already!!
-cmd git clone https://github.com/nanjizal/hxTShopify.git
-cmd cd hxTShopify
-cmd haxe generateJS.hxml # or # -cmd haxe generateDoc.hxml

Issue when compiling

I'm trying to use this to serialize packet to send for a p2p network but I get this errors.

Serializer.cpp
./src/hxbit/Serializer.cpp(194) : error C2039: '__clid' : is not a member of 'hx::Class_obj'
C:\HaxeToolkit\haxe\lib\hxcpp\3,4,64\include\hx/Class.h(98) : see declaration of 'hx::Class_obj'
./src/hxbit/Serializer.cpp(194) : error C2660: 'hxbit::Serializer_obj::getRef' : function does not take 1 arguments
./src/hxbit/Serializer.cpp(1371) : error C2039: '__clid' : is not a member of 'hx::Class_obj'
C:\HaxeToolkit\haxe\lib\hxcpp\3,4,64\include\hx/Class.h(98) : see declaration of 'hx::Class_obj'
./src/hxbit/Serializer.cpp(1402) : error C2039: '__clid' : is not a member of 'hx::Class_obj'
C:\HaxeToolkit\haxe\lib\hxcpp\3,4,64\include\hx/Class.h(98) : see declaration of 'hx::Class_obj'
./src/hxbit/Serializer.cpp(1402) : error C2660: 'hxbit::Serializer_obj::getRef' : function does not take 1 arguments
./src/hxbit/Serializer.cpp(1742) : error C2039: '__clid' : is not a member of 'hx::Class_obj'
C:\HaxeToolkit\haxe\lib\hxcpp\3,4,64\include\hx/Class.h(98) : see declaration of 'hx::Class_obj'
./src/hxbit/Serializer.cpp(1742) : error C2660: 'hxbit::Serializer_obj::getRef' : function does not take 1 arguments

1.3.0 Type not found: hxd.Math

On the latest version I'm getting this type error on builds:

/home/coderah/haxelib/hxbit/1,3,0/hxbit/NetworkStats.hx:92: characters 88-91 : Type not found : hxd.Math

still a little new to Haxe but sounds like a missing lib?

"Unsupported serializable type" with generics

The following code compiles with an error: "Unsupported serializable type Test.T", even though the type T is constrained as Serializable
Compiled with haxe -m Main -lib hxbit --interp

// file "Main.hx"
package;

class Test<T: hxbit.Serializable> implements hxbit.Serializable {
    @:s public var test: T;
}

class Main {
    static public function main () {}
}

Is what I'm trying to achieve possible?

Duplicabled Symbol from iPhoneOS SDK 13.5

In file included from /Users/qkdreyer/Dev/heaps-ios/out/main.c:10:
In file included from /Users/qkdreyer/Dev/heaps-ios/out/hl/functions.c:1070:
/Users/qkdreyer/Dev/heaps-ios/out/hxbit/Serializer.h:38:18: error: expected member name or ';' after declaration specifiers
        haxe__io__Bytes __SIGN;
        ~~~~~~~~~~~~~~~ ^
In file included from /Users/qkdreyer/Dev/heaps-ios/out/main.c:3:
In file included from /Users/qkdreyer/Dev/heaps-ios/deps/hashlink/src/hlc.h:26:
In file included from /Users/qkdreyer/Dev/heaps-ios/deps/hashlink/src/hl.h:197:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.5.sdk/usr/include/stdio.h:88:17: note: expanded from macro '__SIGN'
#define __SIGN  0x8000          /* ignore this file in _fwalk */

This issue also happened with deepnightLibs
Maybe we should find a better than renamed these variable in haxe libs. What do you think @ncannasse ?

Hash is not computed correctly in some environments

Sample.hx

class Sample {
	static function main() {
		var serializer = new hxbit.Serializer();
		var serializedA = serializer.serialize(new AElement());
		trace(serializedA.toHex());
	}
}

class Element implements hxbit.Serializable {
}

class AElement extends Element {
	public function new() {}
}

lua.hxml

-main Sample
-lib hxbit
-lua sample.lua
-D lua-vanilla
-cmd lua sample.lua

result

lua: Conflicting CLID between Element and AElement
stack traceback:
	[C]: in function 'error'
	sample.lua:640: in field 'initClassIDS'
	sample.lua:535: in field 'super'
	sample.lua:529: in field 'new'
	sample.lua:515: in field 'main'
	sample.lua:6708: in main chunk
	[C]: in ?
Error: Command failed with error 1

This is a bug that occurs because the result of Std.int is unspecified, when argument is outside of the signed int 32 range.

static inline function hash(name:String) {
var v = 1;
for( i in 0...name.length )
v = Std.int(v * 223 + StringTools.fastCodeAt(name,i));
v = 1 + ((v & 0x3FFFFFFF) % 65423);
return v;
}


I looked into it further.

class Sample {
	static public function main(): Void {
		var v = 14337447;
		var v1 = Std.int(v * 223 + 101);
		var v2 = Std.int(v1 * 223 + 101);
		trace(v1);
		trace(v2);
	}
}
+ haxe -main Sample --interp
Sample.hx:6: -1097716514
Sample.hx:7: 22353351

+ haxe -main Sample -js sample.js
+ node sample.js
Sample.hx:6: -1097716514
Sample.hx:7: 22353351

+ haxe -main Sample -cs sample_cs
+ mono sample_cs/bin/Sample.exe
Sample.hx:6: -1097716514
Sample.hx:7: 22353351

+ haxe -main Sample -lua sample.lua
+ lua sample.lua
Sample.hx:6: -1097716514
Sample.hx:7: 22353351

+ haxe -main Sample -lua v_sample.lua -D lua-vanilla
+ lua v_sample.lua
Sample.hx:6: 2147483647
Sample.hx:7: 2147483647

lua-vanilla returns same value, when over 2147483647.
Do you have a plan?

Signature mismatch due to DCE

I have a client executable and a server executable. They both should have the same classes listed in 'registerClass'. I have added @:keep to all the classes but it still ignores them.

I have to add a dummy variable in the client for each of the missing classes in order for them to be included. Is there a better way to do this?

Doesn't work with haxe.ds.StringMap, haxe.ds.IntMap or abstracts of Map

Given an abstract like this:

abstract Upgrades(Map<String, Int>) { ... }

I would expect it to be serializable since Map is supported. However, I see that in the macro getPropType skips over Map and goes straight to StringMap, "dereferencing" the abstract twice in one pass:

/home/ben/Dev/hxbit/hxbit/Macros.hx:201: TAbstract(catalyst.Upgrades,[])
/home/ben/Dev/hxbit/hxbit/Macros.hx:253: TInst(haxe.ds.StringMap,[TAbstract(Int,[])])

StringMap doesn't implement Serializable, so it is rejected.

I tried adding IntMap and StringMap in the TInst switch:

            case "haxe.ds.IntMap":
                var tk = getPropType(toType(macro : Int));
                var tv = getPropType(pl[0]);
                if( tv == null )
                    return null;
                PMap(tk, tv);
            case "haxe.ds.StringMap":
                var tk = getPropType(toType(macro : String));
                var tv = getPropType(pl[0]);
                if( tv == null )
                    return null;
                PMap(tk, tv);

but I may have done it incorrectly as this also fails with:

/home/ben/Dev/hxbit/hxbit/Macros.hx:458: characters 4-160 : Null<Map<String, Int>> should be Int
/home/ben/Dev/hxbit/hxbit/Macros.hx:458: characters 4-160 : Map<String, Int> should be Int

Sparse Data serialization support.

Is there any support for sparse data serialization where fields are only stored in binary if their state differs from the default value. Sparse may not be the correct term, it's normally a term used in relation to matrices ( and arrays ) which are mostly just 0, and often calculations are limited to populated areas and storage involves storing only populated parts.
If you see the section on 'Storing a sparse matrix'
https://en.wikipedia.org/wiki/Sparse_matrix

Slight variation on the meaning, I am interested to reduce file sizes by only saving fields that are used, ie vary from null or default value.

class User implements hxbit.Serializable {
    ...
    // field is set to it's default on reconstruction 
    // and it's value is only serialized if it differs from the default.
    sp:@ public var sparseDataField0 : Array<Int> = [];
    sp:@ public var sparseDataField1 :  Null<Int> = null; 
}

So with a game state if a field is still in it's default you would not serialize that field, as default could be inferred by it's absence. My use case is for storage of triangles generated from SVG or similar, if thier opacity is 1, transformation matrix is unit matrix, and texturePropertiesID are null, I don't want that data to be serialized as when rebuilding that can be deduced. It's always going to be a trade off between filesizes and deserialisation complexity but I think it would be useful to have an option to 'sparse serialize' ( my term for the concept ).

Can typedef's be serialized ?

Loop in class building prevent compiler termination

When a serializable class ends-up referencing itself in its graph of references, this error can occur:
Loop in class building prevent compiler termination

The current workaround is to declare the reference of an more abstract type, eg, Serializable, and add strongly typed get/set.

Should this be standardized? For example:

@:s(sub) var subRef : MyClass;

@:storeAs(ParentClass) @:s var subRef : MyClass;

@:selfRef @:s var subRef : MyClass;

Can this lib be used with websockets?

Hey,
is it possible to use this lib with websocket?
As far as i see in your wiki examples, i have to use Socket Host class from heaps, which i guess is TCP.
But is there way to use it over hxWebSockets, for example?

float error.

package;


import hxbit.*;



class User implements hxbit.Serializable {
	@:s public var date: Float;
   
	public function new(){
		
	}
   
   
}


 
class Main 
{
	
	static function main() 
	{
		var s = new hxbit.Serializer();
		var u = new User();
		
		
		var now = Date.now().getTime();
		u.date = now;
		trace(now);
		
		
	
		var xx = s.serialize(u);
		
		
		
		var u = new hxbit.Serializer();
		
		var yy = u.unserialize(xx,User);
		
		
		trace(yy.date);
		
		trace(yy.date==now);//false
		
		
	}
	
}

networkAllow not tested on failing @condSend() variable writes

Currently clients are allowed to modify vars marked as condSend(false) without any error:

		if( t.condSend.expr.match(EConst(CIdent("false"))) )
			return macro {}; // no marking
		var condSend = loop(t.condSend);
		needRef = true;
		mark = macro if( $condSend ) { this.$rname = this.$fname; $mark; };

We discussed removing the "no marking" optimization and adding a checkWrite() call in the else case so all failing condSend() are properly tested on clients.

ArrayProxys auto-complete as Arrays

When declaring a serializable Array like so:

@:s var array : Array<Object>;

The auto completion considers array to be an Array, while at compile-time it will become an ArrayProxy. This works fine as long as ArrayProxy implements all the same methods as Array, but starts to break when static extensions are added to Array.

For that reason (misleading auto-completion) our developers started declaring ArrayProxies specifically. This works fine, except for length which for some reason is marked as @:noCompletion. Could this be removed?

Single precision float

Hi, I think that using single precision float for serialization is a little inconvenient when the float in haxe has double precision.

Everything else is awesome, thanks.

exception:Too late to register class on cs and java

I don't know if it is a bug of the cs and java target or of hxbit. But the following code throws an exception on both targets "Too late to register class" at hxbit.Serializer.registerClass()
So I post the issue here.

class SomeBase implements hxbit.Serializable {
	@:s public var name: String = "";
	@:s public var className: String = "";
	@:s public var module: String = "";
	@:s public var headerTAN: Int = 0;

	public function new(name, className, module) {
		this.name = name;
		this.className = className;
		this.module = module;
	}
}

class Person implements hxbit.Serializable {
	@:s public var name: String = "";
	@:s public var age: Int = 0;

	public function new(name, age) {
		this.name = name;
		this.age = age;
	}
}

class SomePerson extends Person {

	public function new(name, age) {
		super(name, age);
	}
}

class Bug {

	static function main() {
		var p = new SomePerson("Adrian", 54);
		var s = new hxbit.Serializer();
		var data = s.serialize(p);
		var test = s.unserialize(data, Person);
		trace(test); 
		var sb = new SomeBase("Hallo", "Haxe", ""); // here it throws
		var data = s.serialize(sb);
		var sb2 = s.unserialize(data, SomeBase);
		trace(sb2);
		
	}
}

not usable in macro context

Hi,
inside macro context hxbit can't be used because of @:genericBuild cannot be used in macros in line Serializable.hx:44

Serializing generic class

Hi again.

I'm getting closer to serializing our openfl with hxbit!
I have another issue:
We have a generic class UnshrinkableArray that we'd like to serialize.
https://github.com/FishingCactus/openfl/blob/feature/hxbit_serialization/openfl/utils/UnshrinkableArray.hx

Since it's generic, I don't know how I can serialize @:s _items:Array<T> !
When I make the UnshrinkableArrayData<T> implement hxbit.Serializable`, I get the following compile error:

../openfl/utils/UnshrinkableArray.hx:169: characters 15-35 : Unsupported serializable type Array<openfl.utils.UnshrinkableArrayData.T>

Thanks for your help!

Cannot unserialize empty instance

Hej,

I just gave a try to hxBit, only the serializer part. I wanted to do some bench to compare with the traditionnal haxe.Serializer, to pass DB objects (record-macros) from Server to Client and I get "null" when unserializing. I tried with another instance class (not a DB object) but using with Type.createEmptyInstance and it gives me "null" also. So I think it comes from here (Type.createEmptyInstance).
Is there a way to make it work with that please ?

Thanks for reading,
Michal

16/8 bit support?

Any reason why there's no getInt16 / setInt16 for Serializer? There's getByte()/readByte(), etc. though (and i noticed the checks do check for values of of v >= 0 && v < 128 , kinda weird to read write bytes? Why didn't they go for v>=0 && v < 256?). It seems that approach for Serializer resorts to a universal lookup table of ints/bytes as a general approach? How do i go about reading shorts 16s?

Game Save State Best Practices

Do you have any recommendation for how to use this for a networked save game?

Do you typically serialize the whole world state? or a select slice? or a different structure entirely?

I use the system to send only what is needed to the client, not marked up for single player serialization.

I'm expecting to have to create a separate 'save' structure, but before I did that, I was wondering if there was a way of looking at this that I'm not familiar with.

Thanks.

haxe 3.2.1 compatibility

hi,
when i try to use this library i get some macro errors with haxe 3.2.1

this is the code


class User implements hxbit.Serializable {
  @:s public var name:String;
  @:s public var surname:String;
}


class Main {
  static function main() {
      var user = new User();
      user.name = 'mario';
      user.surname = 'cino';
      var s = user.serialize();
      trace(s);
  }
}

and this the errors


Running build...
: characters 13-40 : Class<haxe.macro.Context> has no field followWithAbstracts
: characters 13-32 : Class<haxe.macro.Context> has no field resolveType
: characters 13-32 : Class<haxe.macro.Context> has no field resolveType
: characters 2-11 : Build failure
Main.hx:2: lines 2-10 : Defined in this class
: characters 2-11 : Build failure


Multiple Serializable interface inheritance

It seems that we get an error if Serializable is inherited more than once. Is this an accepted limitation? Gives the build error Duplicate class field declaration : X.__clid

JS: makeAlive Std.Instance check incorrect.

This code NetworkHost:544 will always return false due to the way createEmptyInstance works in the JS implementation at Serializer.hx:473

       public static function createEmptyInstance<T>( cl : Class<T> ) : T untyped {
		__js__("function empty() {}; empty.prototype = cl.prototype");
		return __js__("new empty()");
	}

I'm not sure the best approach to fixing this, I'll likely do a hacky fix for the time being, potentially more of a Haxe JS implementation issue than hxbit?

Feature - Per field update callback

Is there any way currently to somehow get an event / callback when a given field changes?

I can't seem to find anything, but I thought I'd ask before building it.

Change Proxy PObj field

Changes in Proxy PObj fields should be sent on a per field basis. Currently all fields are re-sent for every change.

Unserialize immediately after Serialize does not maintain the same object?

if you were to serialize something then immediately unserialize it, hxbit will create a new instance every time, it even loses references to any unserialized object before it. Is there a design reason for this?

My use-case requires updating an existing object in memory via unserializing from bytes obtained elsewhere.

in short, if I serialize an object with __uid = 4 then unserialize the bytes returned i'd expect it to update the original object in memory, instead I'm returned a new object with the same __uid = 4

how to deal with proxy types being invalid when passed to a function?

given this type:

typedef TPosition = {
    x: Int,
    y: Int
}

the following will throw an error compiling to c++

class Test extends hxbit.NetworkSerializable {
    @:s position: TPosition = { x: 0, y: 0 }

    public function test() {
        Logic.checkPosition(position);
    }
}

class Logic {
    public static function checkPosition(pos: TPosition) {
        // do something with pos

        return false;
    }
}

error:

hxbit.ObjProxy_x_Int_y_Int should be TPosition
hxbit.ObjProxy_x_Int_y_Int should be { y : Int, x : Int }
inconsistent setter for field x : set should be default For function argument 'pos'

the example provided is not a direct copy (might not run) but is written to provide clarity on the issue.

Uncaught exception - std@buffer_add_char

Hello,

I'm integrating hxbit into our fork of the openfl framework. This because the normal haxe serialization library results in huge serialization files and very slow parsing.

I've progressed quite far, but now I'm stuck. When serializing, I get this wonderful neko error message:
Uncaught exception - std@buffer_add_char

I've added a callstack and it comes from this:

Called from hxbit/Serializer.hx line 141
Called from hxbit/Serializer.hx line 449
Called from hxbit/Macros.hx line 372

Which is this code:

case PMap(kt, vt):
			var kt = kt.t;
			var vt = vt.t;
			var vk = { expr : EConst(CIdent("k")), pos : v.pos };
			var vv = { expr : EConst(CIdent("v")), pos : v.pos };
			return macro $ctx.addMap($v, function(k:$kt) { trace("Test"); return hxbit.Macros.serializeValue($ctx, $vk);}, function(v:$vt) return hxbit.Macros.serializeValue($ctx, $vv));	

The only place that I can see where std@buffer_add_char could be called from, is from Query.hx:

	function readIdent() {
		var s = new StringBuf();
		while( true ) {
			var c = nextChar();
			if( (c >= 'A'.code && c <= 'Z'.code) || (c >= 'a'.code && c <= 'z'.code) || (c >= '0'.code && c <= '9'.code) || c == '_'.code || c == '-'.code )
				s.addChar(c);
			else {
				pos--;
				break;
			}
		}
		return s.toString();
	}

When calling nextChar(). However, commenting out this code still gives me the same error. I'm lost on how to progress.
Do you have any clue, or can you give some tips on how to debug neko with macros? I tried to trace in several places but most places don't give me any output at all.

You can see the code here: https://github.com/FishingCactus/openfl/tree/feature/hxbit_serialization

Thanks,

Pieter

no Unserializer

There is no class 'Unserializer' in this library. What's the point of serializing things if you can't reverse it? haxe is giving me the error 'Type not found : hxbit.Unserializer' and if I look in your source, there is a hxbit.Serializer but no hxbit.Unserializer. is this somewhere else or is the library not ready for use yet?

Circular references

Hej Nicolas,

I try to use the hxBit Serializer instead of the haxe.Serializer to pass objects from server to client.
I have circular references (contact has companies which have contacts...) and there's no cache as I can see.
I have some questions :
-Do you think it's a good idea to use hxBit for that ? You say hxBit Serializer is faster than haxe.Serializer, so I wanted to test it.
-If yes, could a cache be implemented, to pass objects by references as the haxe.Serializer do ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.