#godot #binary-encoding #binary #serialization #networking #encode-decode

godot-binary-serialization

A Godot binary serializer to encode & decode types from & to godot

5 releases

0.2.0 Dec 2, 2024
0.1.12 Nov 21, 2024
0.1.11 Nov 14, 2024
0.1.1 Nov 14, 2024
0.1.0 Nov 14, 2024

#210 in Game dev

Download history 304/week @ 2024-11-11 119/week @ 2024-11-18 9/week @ 2024-11-25 141/week @ 2024-12-02 16/week @ 2024-12-09

298 downloads per month

MIT/Apache

47KB
1K SLoC

Godot Binary Serialization

Allows us to serialize and deserialize types based on Godot's Binary Serialization API

Getting started

Add the crate to your project from crates.io

Goals

  • Write server infrastructure for Godot games outside of the 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 memory safety and multi-threading.

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 = Decoder::decode_variant(&bytes);

// 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(string) = variant.as_var::<GodotString>() else {
    panic!("DIDNT RECIEVE A STRING");
};

// Now to use the value all we have to do is access the value field
println!("{}", string.value);

Sending a string to the client

// Most Godot variants will have a new type that takes in a primitive rust value
let string = GodotString::new("hello");

// 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(bytes) = Encoder::encode_variant(string) else {
    panic!("Failed to encode variant")
};

// 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(bytes, PacketDelivery::Reliable);

Sending a dictionary

// We can simply create a godot dictionary as such
let mut dict = GodotDictionary::new();

// And insert values just as if it was a hashmap. The values must impl GodotVariant and the types
// don't have to match for each key/value
dict.insert(GodotString::new("position"), GodotVector3::new(0.52, 502.0, 68.0));
dict.insert(GodotString::new("id"), GodotInteger::new_from_i32(693));

// 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(bytes) = Encoder::encode_variant(dict) else {
    panic!("Failed to encode variant")
};

// 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(bytes, PacketDelivery::Unreliable);

Recieving 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 recieved from Godot. In this example we know that
// the client has sent us a dictionary
let Ok(dictionary) = Decoder::decode_variant(&bytes) else {
    panic!("Invalid bytes");
}

// Assuming the dictionary has a key value pair that has a key of "position" with type Vector3
let Some(value) = dictionary.get::<Vector3>(GodotString::new("position")) else {
    panic!("Value not in dictionary");
};

let x = value.x;
let y = value.y;
let z = value.z;
println!("BANANA AT ({}, {}, {}) AAAAAA", x, y, z);

Dependencies

~1MB
~19K SLoC