server | v3 switch to v4  

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でサポートされている型

Photonでサポートされているそれぞれの型には、 type_infoに予約バイトが必要です。

  • 基本タイプはtype_infoに1バイト必要です。
  • 多くのコレクションは、コレクションの型に対して追加のもう1バイトを必要とします。  コレクションの型がtype_infoの一部であるHashtableやObject配列、Byte配列の場合にはその限りではありません。
  • 厳密に型指定されたコレクションは要素の型を一度送信します。要素ごとにtype_infoを送信するわけではありません。
  • すべてのコレクションは、長さを保管するのに2バイトを必要とします。これは長さの型がshortであることによります。 バイト配列はこのルールの例外です。バイト配列はintの型で、長さを保管するのに4バイト必要です。
  • 文字列[]にnull値を含めることはできません。代わりに空白を使用してください。

以下の型には多くの場合対応しており、Photonのバイナリプロトコルで認識されます。 リストに載っている全ての型を備えていない言語では、SDKがサポートする型が少ない場合もあります。

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.

Back To Top

カスタム型

通信する全てのクライアントにカスタムタイプを登録してください。

Back To Top

Custom Types

上記リストに含まれていない型の場合、重要な値をシリアライズ化または非シリアライズ化する必要があります。

基本的には二つのメソッドを書いてクラスをバイト配列に変換してから戻します。その後にそれをPhoton APIに登録します。 登録が完了すれば、メッセージにその型のインスタンスを含むことができます。

type_infoに2バイトをもつカスタム型: 1バイトはこれがカスタム型であることを示し、そしてもう1バイトはカスタムコード型用です。 Photonでは256のカスタム型をサポートしています。カスタム型コードの推奨数は255以下です。

Photonが登録された型のシリアライズ化メソッドを呼び、自動的に作成したバイト配列(4byte)をプリフィックスします: 2バイトは必要な型情報のため、もう2バイトはペイロード長のためです。 4バイトのオーバーヘッドがあるので、バイトの少ないデータの登録は避けた方が良いかもしれません。

Photon Serverは不明のカスタムタイプをそのまま転送することもできます。 このため、Photon Cloudではタイプの登録をする必要がありません。

通信する全てのクライアントにカスタム型を登録してください。 必要な場合にはサーバーサイドもしくはプラグインにカスタム型を登録してください。

RegisterTypeメソッドは、ブーリアンの結果を返します。この結果で型が登録可能か判断します。 カスタム型の登録中に何かしらのエラーが生じた場合は、メソッドがfalseを返し、変更は何も起こりません。 それ以外の場合、登録は正常に行われtrue値が返されます。 カスタムコードがすでに使用されている場合は、登録は正常に行われずメソッドはfalseを返します。 同一のカスタム型に対して登録済みのシリアル化・デシリアル化メソッドの上書きは行われず、古いものが使われ続けます。

Back To Top

C#でのカスタムタイプ

全てのC#ベースのAPIは(.NET, Unity, Xamarin, etc)同じ方法でクラスの登録を行います。 カスタムSteamクラスを使用するか、「普通」のbyte配列を使用するかによって2通りの方法があります。

Back To Top

Byte配列メソッド

コールの静的メソッド:

    PhotonPeer.RegisterType(Type customType, byte code, SerializeMethod serializeMethod, DeserializeMethod deserializeMethod)

SerializeMethodDeserializeMethodは以下の各シグネチャで定義されたデリゲートです。

    public delegate byte[] SerializeMethod(object customObject);
    public delegate object DeserializeMethod(byte[] serializedCustomObject);

Back To Top

例として、基本的なMyCustomTypeを実装しました:

public class MyCustomType
{
  public byte Id { get; set; }

  public static object Deserialize(byte[] data)
  {
    var result = new MyCustomType();
    result.Id = data[0];
    return result;
  }

  public static byte[] Serialize(object customType)
  {
    var c = (MyCustomType)customType;
    return new byte[] { c.Id };
  }
}

これを登録するには以下を参照してください:

    PhotonPeer.RegisterType(typeof(MyCustomType), myCustomTypeCode, MyCustomType.Serialize, MyCustomType.Deserialize);

Back To Top

StreamBufferメソッド

StreamBufferStreamクラスのカスタム実装です。これを実行すると、byte配列ラッパーのメリットを存分に受けられます。また、Photonにビルトインのシリアライズ可能なタイプもサポートしています。

コールの静的メソッド:

    RegisterType(Type customType, byte code, SerializeStreamMethod serializeMethod, DeserializeStreamMethod deserializeMethod)

SerializeStreamMethod and DeserializeStreamMethod are defined delegates with the following respective signatures:

    public delegate byte[] SerializeMethod(StreamBuffer outStream, object customobject);
    public delegate object DeserializeMethod(StreamBuffer inStream, short length);

Back To Top

PUNがどのようにUnityのVector2に対応しているかを説明します。

Vector2にはxとyの二つのfloatがあり、それぞれVector2.xVector2.yです。 Photonはfloatには対応していますがVector2には対応していません。

基本的にはC#のどのような方法でfloat型を表す4byteを取得しても問題ありません。PhotonのProtocolクラスには効果的に複数のタイプをバイト配列に書き出すシリアライズ方法がいくつかあります。

    public static readonly byte[] memVector2 = new byte[2 * 4];
    private static short SerializeVector2(StreamBuffer outStream, object customobject)
    {
        Vector2 vo = (Vector2)customobject;
        lock (memVector2)
        {
            byte[] bytes = memVector2;
            int index = 0;
            Protocol.Serialize(vo.x, bytes, ref index);
            Protocol.Serialize(vo.y, bytes, ref index);
            outStream.Write(bytes, 0, 2 * 4);
        }

        return 2 * 4;
    }

注記としてSerializeVector2はオブジェクトを取得し、それを予測されるVector2タイプに投げる必要があります。

あとはDeserializeVector2にオブジェクトを返すのみです:

    private static object DeserializeVector2(StreamBuffer inStream, short length)
    {
        Vector2 vo = new Vector2();
        lock (memVector2)
        {
            inStream.Read(memVector2, 0, 2 * 4);
            int index = 0;
            Protocol.Deserialize(out vo.x, memVector2, ref index);
            Protocol.Deserialize(out vo.y, memVector2, ref index);
        }

        return vo;
    }

そして最後にVector2を登録する必要があります:

    PhotonPeer.RegisterType(typeof(Vector2), (byte)'W', SerializeVector2, DeserializeVector2);

Back To Top

追加情報

複雑なデータのカスタムシリアライザーションを行う場合、データがどのように転送されるか、またどのようにトラフィックを監視して管理下に置いておくかを理解することが重要です。

Back To Top

サーバ上でカスタムタイプを登録

Photon Serverのシリアル化で不明なタイプを登録するには、TryRegisterCustomTypeを使います。

    Protocol.TryRegisterCustomType(typeof(MyCustomType), myCustomTypeCode, MyCustomType.Serialize, MyCustomType.Deserialize);

タイプをbyte-arrayに書き込んで受信側に再生成するには二つの方式が必要です。例として、MyCustomTypeを実装しました:

public class MyCustomType
{
  public byte Id { get; set; }

  public static object Deserialize(byte[] data)
  {
    var result = new MyCustomType();
    result.Id = data[0];
    return result;
  }

  public static byte[] Serialize(object customType)
  {
    var c = (MyCustomType)customType;
    return new byte[] { c.Id };
  }
}

ドキュメントのトップへ戻る