Show Sidebar

Serialization in Photon

Photon and its clients are using a highly optimized binary protocol to communicate. It's compact, yet easy to parse.

Photon must convert all data into this binary protocol before it can be sent. This is done automatically for a range of commonly used data types. On most client APIs, you can also register your own serialization methods for other classes you might need. See below.

Photon Supported Types

Each Photon supported type needs some reserved bytes for the type_info.

  • Primitive types need 1 byte for type_info.
  • Most collections need 1 extra byte for the type of the collection. This is not the case for Hashtable, Object array and Byte array as the collection type is part of the type_info.
  • Strongly typed collections send the element type once instead of sending type_info per element.
  • All collections need 2 bytes to store their length. This is because the type of the length is short. Byte array is the exception to this rule. Its length is of type int and it needs 4 bytes to store the length.

The following types are generally supported and known by Photon's binary protocol. As some languages don't provide every listed type, some SDKs support less types.

type (C#) size [bytes] (photon_sizeof) description
byte 2 8 bit unsigned
2 = type_info(byte) + sizeof(byte)
bool (boolean) 2 true or false
2 = type_info(bool) + sizeof(bool)
short 3 16 bit
3 = type_info(short) + sizeof(short)
int (integer) 5 32 bit
5 = type_info(int) + sizeof(int)
long 9 64 bit
9 = type_info(long) + sizeof(long)
float 5 32 bit
5 = type_info(float) + sizeof(float)
double 9 64 bit
9 = type_info(double) + sizeof(double)
String 3 + sizeof( UTF8.GetBytes(string_value) ) length ≤ short.MaxValue
3 = type_info(String) + length_size;
length_size = sizeof(short)
Object[] (Object-array) 3 + photon_sizeof(elements) length ≤ short.MaxValue
3 = type_info(Object[]) + length_size;
length_size = sizeof(short)
byte[] (byte-array) 5 + length length ≤ int.MaxValue
5 = type_info(byte[]) + length_size;
length_size = sizeof(int)
array (array of type T, T[]) 4 + photon_sizeof(elements) - length * type_info(T) length ≤ short.MaxValue
T-type can be any of the types listed in this table except byte.
4 = type_info(array) + type_info(T) + length_size;
length_size = sizeof(short)
Hashtable 3 + photon_sizeof(keys) + photon_sizeof(values) pairs count ≤ short.MaxValue
3 = type_info(Hashtable) + length_size;
length_size = sizeof(short)
Dictionary<Object,Object> 5 + photon_sizeof(keys) + photon_sizeof(values) pairs count ≤ short.MaxValue
5 = type_info(Dictionary) + 2 * type_info(Object) + length_size;
length_size = sizeof(short)
Dictionary keys should not be of type Dictionary.
Dictionary<Object,V> 5 + photon_sizeof(keys) + photon_sizeof(values) - count(keys) * type_info(V) pairs count ≤ short.MaxValue
V-type can be any of the types listed in this table.
5 = type_info(Dictionary) + type_info(Object) + type_info(V) + length_size;
length_size = sizeof(short)
Dictionary keys should not be of type Dictionary.
Dictionary<K,Object> 5 + photon_sizeof(keys) + photon_sizeof(values) - count(keys) * type_info(K) pairs count ≤ short.MaxValue
K-type can be any of the types listed in this table.
5 = type_info(Dictionary) + type_info(K) + type_info(Object) + length_size;
length_size = sizeof(short)
Dictionary keys should not be of type Dictionary.
Dictionary<K,V> 5 + photon_sizeof(keys) + photon_sizeof(values) - count(keys) * (type_info(K) + type_info(V)) pairs count ≤ short.MaxValue
K- and V-types can be any of the types listed in this table.
5 = type_info(Dictionary) + type_info(K) + type_info(V) + length_size; length_size = sizeof(short)
Dictionary keys should not be of type Dictionary.

Custom Types

For any type not listed above, Photon will need your help to de/serialize important values.

The basic idea is that you write two methods to convert your class to a byte-array and back, then register those with the Photon API. When that's done you can include instances of that type in any message you send.

Custom types have 2 bytes for type_info: one byte to tell that it's a custom type plus one more for the custom type code. Photon supports up to 256 custom types.

Photon will call the serialization methods for a registered type and automatically prefix the created byte-arrays with 4 bytes: 2 bytes for the necessary type information and 2 bytes for payload length. Due to the 4 bytes overhead, you might want to avoid registering types that are just a few bytes of data.

The Photon Server is able to forward unknown custom types "as is". This is why you don't need to register your types in the Photon Cloud.

Make sure to register your custom types on all communicating clients. Register custom types on server side or on plugins when needed.

RegisterType method returns a boolean result, which tells you if the type could be registered. If any error occurs during custom type registration the method will return false and nothing will be changed. Otherwise registration should be successful and the returned value is true. If the custom code is already used the registration will fail and the method will return false. Overriding registered serialization and deserialization methods for a same custom type will fail and the old ones will still be used.

Custom Types in C#

All of our C#-based APIs (.NET, Unity, Xamarin, etc) provide the same way to register classes.

The static method to call is:

SerializeMethod and DeserializeMethod are defined delegates:

Example

Let's have a look at how PUN implements support for Unity's Vector2.

A Vector2 has 2 floats: x and y. While floats are supported by Photon, Vector2 is not.

For a SerializeMethod, we need a byte-array as result. Basically, using any C#-way to get the 4 bytes that resemble the float is ok. Photon's Protocol class has a few Serialize methods that effectively write several types to byte-arrays.

Note that SerializeVector2 gets an object and has to cast this to the expected Vector2 type first.

Conversely, we also return just an object in DeserializeVector2:

And finally, we have to register the Vector2:

Additional information

When doing custom serializion of complex data, it's important to understand how data is transported and how to monitor traffic to keep it under control.

 To Document Top