Godot Binary Serialization
Allows us to serialize and deserialize types based on Godot's Binary Serialization API
Goals
- Write server infrastructure for Godot games outside of the engine and outside of Godot's supported languages.
- Rust has an extensive library ecosystem(the Cargo ecosystem), allowing you to offload different tasks on an external server that may not be possible when confined to the Godot engine.
Why an external server?
- Gives you complete control over how the server is created, run, expanded and secured.
- Godot games are very easy to reverse. Any server code written in the engine can be easily viewed with tools such as gdsdecomp.
- Writing a server in languages such as Rust gives you easy access to high performance, memory safe and multi-threaded code.
Supported types
| Type | Encode | Decode |
|---|---|---|
| Null | ✅ | ✅ |
| Bool | ✅ | ✅ |
| Integer | ✅ | ✅ |
| Float | ✅ | ✅ |
| String | ✅ | ✅ |
| Vector2 | ✅ | ✅ |
| Rect2 | ❌ | ❌ |
| Vector3 | ✅ | ✅ |
| Transform2d | ❌ | ❌ |
| Plane | ❌ | ❌ |
| Quaternion | ❌ | ❌ |
| AABB | ❌ | ❌ |
| Basis | ❌ | ❌ |
| Transform3d | ❌ | ❌ |
| Color | ❌ | ❌ |
| Node Path | ❌ | ❌ |
| RID | ❌ | ❌ |
| Object | ❌ | ❌ |
| Dictionary | ✅ | ✅ |
| Array | ❌ | ❌ |
| Raw Array | ❌ | ❌ |
Examples
Decoding bytes received from a client
// Typically this would be recieved from the UDP socket and serialized in Godot with "var2bytes"
let bytes = /* Pretend this is some form of valid bytes we have received from godot */
// And it's just this simple, all you have to do is call this and it will determine
// the type and the value of that type
let variant = decode_variant;
// You must know the type that has been received from Godot. In this example we know that
// the client has sent us a string
let Some = variant.as_any. else ;
// Now to use the value all we have to do is access the value field
println!;
Sending a string to the client
// Most Godot variants will have a new type that takes in a primitive rust value
let string = new;
// To encode the type into bytes we simply just call this and the encoder will encode it into bytes
// This will only fail if the type is unsupported or for some reason we cant write bytes to the buffer. It can only take in anything that impl GodotVariant
let Ok = encode_variant else ;
// Assuming we have some form of UDP socket or server set up we can do something along these lines.
// And this will broadcast to everything client with the string hello
socket.broadcast;
Sending a dictionary
// Dictionaries use the indexmap crate due to the nature that key:value pairs keep their inserted position in Godot
use IndexMap;
// Dictionaries require a lot more boiler plate due to Godot's type system. Dictionaries can consist of
// any variant in the key or value slot, regardless of other types in the dictionary
let mut hashmap = new;
// Creates a key value pair with a string key of "position" and a value of Vector3
hashmap.insert;
hashmap.insert;
// We can now create the dictionary from an index map
// Godot Dictionary structs contain a field called "byte_size" this is not needed unless decoding
// so this call just fills it in as 0
let dictionary = new_from_map;
// We can just call this function and the encoder will do turn it into bytes
// This will only fail if the type is unsupported or for some reason we cant write bytes to the buffer. It can only take in anything that impl GodotVariant
let Ok = encode_variant else ;
// Assuming we have some form of UDP socket or server set up we can do something along these lines.
// And this will broadcast to everything client with the dictionary
socket.broadcast;
Receiving a dictionary and using it's values
// Typically this would be recieved from the UDP socket and serialized in Godot with "var2bytes"
let bytes = /* Pretend we have valid bytes here */
// You must know the type that has been received from Godot. In this example we know that
// the client has sent us a dictionary
let Ok = decode_variant else
// Assuming the dictionary has a key value pair that has a key of "position"
let key = Boxnew as ;
// We can get the value now with this key
let Some = dictionary.map.get else ;
// To get the value of the type we must downcast_ref it
if let Some = value.as_any.