{"id":13534193,"url":"https://github.com/HeapsIO/hxbit","last_synced_at":"2025-04-01T22:31:16.990Z","repository":{"id":41744745,"uuid":"76732241","full_name":"HeapsIO/hxbit","owner":"HeapsIO","description":"Haxe Binary serialization and network synchronization library","archived":false,"fork":false,"pushed_at":"2024-04-30T07:48:51.000Z","size":758,"stargazers_count":149,"open_issues_count":8,"forks_count":29,"subscribers_count":15,"default_branch":"master","last_synced_at":"2024-05-16T10:38:27.519Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Haxe","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HeapsIO.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-12-17T15:43:53.000Z","updated_at":"2024-05-29T16:10:13.165Z","dependencies_parsed_at":"2023-09-23T09:44:52.603Z","dependency_job_id":"55d8beae-30c2-42bd-8080-7d24b38b4a92","html_url":"https://github.com/HeapsIO/hxbit","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HeapsIO%2Fhxbit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HeapsIO%2Fhxbit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HeapsIO%2Fhxbit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HeapsIO%2Fhxbit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HeapsIO","download_url":"https://codeload.github.com/HeapsIO/hxbit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246720528,"owners_count":20822914,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T07:01:27.657Z","updated_at":"2025-04-01T22:31:16.977Z","avatar_url":"https://github.com/HeapsIO.png","language":"Haxe","readme":"# HxBit\n\nHxBit is a binary serialization and network synchronization library for Haxe.\n\n## Installation\n\nInstall through haxelib with `haxelib install hxbit` and use `-lib hxbit` to use it.\n\n## Serialization\n\nYou can serialize objects by implementing the `hxbit.Serializable` interface. You need to specify which fields you want to serialize by using the `@:s` metadata:\n\n```haxe\nclass User implements hxbit.Serializable {\n    @:s public var name : String;\n    @:s public var age : Int;\n    @:s public var friends : Array\u003cUser\u003e;\n    ...\n}\n```\n\nThis will automatically add a few methods and variables to your `User` class:\n\n```haxe\n/**\n  These fields are automatically generated when implementing the interface.\n**/\ninterface Serializable {\n\t/** Unique identifier for the object, automatically set on new() **/\n\tpublic var __uid : Int;\n\t/** Returns the unique class id for this object **/\n\tpublic function getCLID() : Int;\n\t/** Serialize the object id and fields using this Serializer **/\n\tpublic function serialize( ctx : hxbit.Serializer ) : Void;\n\t/** Unserialize object fields using this Serializer **/  \n\tpublic function unserialize( ctx : hxbit.Serializer ) : Void;\n\t/** Returns the object data schema **/  \n\tpublic function getSerializeSchema() : hxbit.Schema;\n}\n```\n\nThis allows you to serialize/unserialize using this code:\n\n```haxe\nvar s = new hxbit.Serializer();\nvar bytes = s.serialize(user);\n....\nvar u = new hxbit.Serializer();\nvar user = u.unserialize(bytes, User);\n....\n```\n\n### Comparison with haxe.Serializer/Unserializer\n\nHaxe 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.\n\nHxBit 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.\n\n### Supported types\n\nThe following types are supported:\n\n  - Int / haxe.UInt : stored as either 1 byte (0-254) or 5 bytes (0xFF + 32 bits)\n  - Float : stored as single precision 32 bits IEEE float\n  - Bool : stored as single by (0 or 1)\n  - String : stored as size+1 prefix, then utf8 bytes (0 prefix = null)\n  - any Enum value : stored as index byte + args values\n  - haxe.io.Bytes : stored as size+1 prefix + raw bytes (0 prefix = null)\n  - Array\u0026lt;T\u0026gt; and haxe.ds.Vector\u0026lt;T\u0026gt; : stored as size+1 prefix, then T list (0 prefix = null)\n  - Map\u0026lt;K,V\u0026gt; : stored as size+1 prefix, then (K,V) pairs (0 prefix = null)\n  - Null\u0026lt;T\u0026gt; : stored as a byte 0 for null, 1 followed by T either\n  - Serializable (any other serializable instance) : stored with __uid, then class id and data if if was not already serialized\n  - Strutures { field : T... } : optimized to store a bit field of not null values, then only defined fields values \n\n### Default values\n\nWhen 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:\n\n```haxe\nclass User implements hxbit.Serializable {\n    ...\n    // when unserializing, someOtherField will be set to [] instead of null\n    var someOtherField : Array\u003cInt\u003e = []; \n}\n```\n\n### Unsupported types\n\nIf you want to serialize unsupported types, you could implement your own serialization through the optional methods `customSerialize` and `customUnserialize`.\n\n```haxe\nclass Float32ArrayContainer implements hxbit.Serializable {\n\n    public var value:Float32Array;\n\n    ...\n\n    @:keep\n    public function customSerialize(ctx : hxbit.Serializer) {\n        ctx.addInt(value.length);\n        for(i in 0...value.length)\n            ctx.addFloat(value[i]);\n    }\n\n    @:keep\n    public function customUnserialize(ctx : hxbit.Serializer) {\n        var length = ctx.getInt();\n        var tempArray = new Array\u003cFloat\u003e();\n        for(i in 0...length)\n            tempArray.push(ctx.getFloat());\n\n        value = new Float32Array(tempArray);\n    }\n}\n```\n\n## Versioning\n\nHxBit 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.\n\nIn order to save some data with versioning, use the following:\n\n```haxe\nvar s = new hxbit.Serializer();\ns.beginSave();\n// ... serialize your data...\nvar bytes = s.endSave();\n```\n\nAnd in order to load versioned data, use:\n\n```haxe\nvar s = new hxbit.Serializer();\ns.beginLoad(bytes);\n// .. unserializer your data\n```\nVersioned data is slightly larger than unversioned one since it contains the Schema data of each serialized class.\n\nCurrently versioning handles:\n - removing fields (previous data is ignored)\n - adding fields (they are set to default value: 0 for Int/Float, false for Bool, empty Array/Map/etc.)\n\nMore convertions can be easily added in `Serializer.convertValue`, including custom ones if you extends Serializer. \n\n## Networking\n\nAdditionaly to serialization, HxBit supports synchronization of objects over network and Remote Procedure Call.\n\n### Example\n\nAn 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\n\nIn 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\n\n### Principles\n\nA host is a `NetworkHost` instance which handles communications betwen a server (or authority) and several clients.\n\nA NetworkSerializable can be shared over the network by setting `enableReplication = true` on it.\n\nWhen 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.\n\nOnly 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.\n\nIn 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. \n\n### Remote Procedure Calls (RPC)\n\nRPC can be easily performed by tagging a shared object member method with `@:rpc`:\n\n```haxe\nclass Cursor implements hxbit.NetworkSerializable {\n   @:rpc public function blink() {\n      ...\n   }\n}\n```\n\nThere are different RPC modes, which can be specified by using `@:rpc(mode)`:\n\n  - `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.\n  - `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. \n  - `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.\n  - `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).\n  - `immediate` : Like `all` but executes immediately locally\n  \nReturn values are possible unless you are in `all` mode, and will add an extra callback to capture the result asynchronously:\n\n```haxe\n   @:rpc(server) public function sendAction( act : String ) : Bool {\n   }\n   \n   ...\n   sendAction(\"test\", function(onResult) { ... });\n   \n```\n\nRPC executing on the client can change network properties on the current object without triggering errors or network data\n\n### Filtering\n\nYou 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:\n\n - `@: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.\n - `@: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 \u003e 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.  \n - `@:notMutable` is used to disable proxy creation on a property (see below)\n \n### Proxys\n\nIn 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.\n\nAt 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.\n\n### Local change\n\nSometimes 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. \n\nThis can be performed by wrapping the changes as the following example shows:\n\n```haxe\nfunction teleport( t : Target ) {\n    networkLocalChanges(function() {\n       var pos = t.getPosition();\n       // changes to x/y will not be sent over the network\n       // clients are also allowed to change serialized properties this way\n       x = pos.x;\n       y = pos.y;\n    });\n}\n```\n\n### Cancel change\n\nYou 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\u003cName\u003e`, with name being the name of the property with first letter uppercased (`networkPropPosition` for `position` for instance)\n\n### Save and load\n\nThe 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.\n\n","funding_links":[],"categories":["Libraries"],"sub_categories":["Haxe"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FHeapsIO%2Fhxbit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FHeapsIO%2Fhxbit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FHeapsIO%2Fhxbit/lists"}