Photonでの直列化
Photonとクライアントは、非常に最適化されたバイナリプロトコルを通信に使用しています。
このバイナリプロトコルは簡潔で、容易に解析できます。
Photonは送信前に、すべてのデータをこのバイナリプロトコルに変換する必要があります。
この処理は、通常使用されるデータ型について自動的に実行されます。
ほとんどのクライアントAPIでは、必要となる他のクラスに独自の直列化手順を登録することも可能です。
以下を参照してください。
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. |
カスタム型
通信する全てのクライアントにカスタムタイプを登録してください。
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を返します。
同一のカスタム型に対して登録済みのシリアル化・デシリアル化メソッドの上書きは行われず、古いものが使われ続けます。
C#でのカスタムタイプ
全てのC#ベースのAPIは(.NET, Unity, Xamarin, etc)同じ方法でクラスの登録を行います。
カスタムSteam
クラスを使用するか、「普通」のbyte配列を使用するかによって2通りの方法があります。
byte配列メソッド
コールの静的メソッド:
C#
PhotonPeer.RegisterType(Type customType, byte code, SerializeMethod serializeMethod, DeserializeMethod deserializeMethod)
SerializeMethod
とDeserializeMethod
は以下の各シグネチャで定義されたデリゲートです。
C#
public delegate byte[] SerializeMethod(object customObject);
public delegate object DeserializeMethod(byte[] serializedCustomObject);
例
例として、基本的なMyCustomType
を実装しました:
C#
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 };
}
}
これを登録するには以下を参照してください:
C#
PhotonPeer.RegisterType(typeof(MyCustomType), myCustomTypeCode, MyCustomType.Serialize, MyCustomType.Deserialize);
StreamBufferメソッド
StreamBuffer
はStream
クラスのカスタム実装です。これを実行すると、byte配列ラッパーのメリットを存分に受けられます。また、Photonにビルトインのシリアライズ可能なタイプもサポートしています。
コールの静的メソッド:
C#
RegisterType(Type customType, byte code, SerializeStreamMethod serializeMethod, DeserializeStreamMethod deserializeMethod)
SerializeStreamMethod
and DeserializeStreamMethod
are defined delegates with the following respective signatures:
C#
public delegate short SerializeStreamMethod(StreamBuffer outStream, object customobject);
public delegate object DeserializeStreamMethod(StreamBuffer inStream, short length);
例
PUNがどのようにUnityのVector2
に対応しているかを説明します。
Vector2にはxとyの二つのfloatがあり、それぞれVector2.x
とVector2.y
です。
Photonはfloatには対応していますがVector2には対応していません。
基本的にはC#のどのような方法でfloat型を表す4byteを取得しても問題ありません。PhotonのProtocolクラスには効果的に複数のタイプをバイト配列に書き出すシリアライズ方法がいくつかあります。
C#
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にオブジェクトを返すのみです:
C#
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を登録する必要があります:
C#
PhotonPeer.RegisterType(typeof(Vector2), (byte)'W', SerializeVector2, DeserializeVector2);
追加情報
複雑なデータのカスタムシリアライザーションを行う場合、データがどのように転送されるか、またどのようにトラフィックを監視して管理下に置いておくかを理解することが重要です。
Back to top