RPC 機能

Sora Unity SDK の RPC 機能について説明します。

注意

この機能は実験的機能のため、正式版では仕様が変更される可能性があります

RPC 機能について

RPC 機能は DataChannel 経由で Sora の一部 HTTP API を利用するための機能です。 JSON-RPC 2.0 プロトコルの仕様に準拠しています。

この機能は Sora 2025.2 以降で、かつ DataChannel 経由のシグナリングが有効な場合に利用可能です。 Sora の RPC 機能の詳細は Sora ドキュメントの RPC 機能 をご確認ください。

利用時の注意事項

  • label が rpc のメッセージは Sora.OnMessage には届きません

  • RPC レスポンス受信とタイムアウト判定は Sora.DispatchEvents() の呼び出しで処理されます

    • DispatchEvents() を定期的に呼び出さない場合、コールバックは呼ばれず、タイムアウトも返りません

  • 切断時、およびアプリ終了に伴う Dispose() 実行時、未完了の RPC リクエストは破棄されます

  • paramsJson は JSON 文字列をそのまま埋め込むため、JSON を渡してください

Sora Unity SDK の実装内容

Sora Unity SDK では RPC の送信とレスポンス受信を以下の API で提供しています。

  • void RequestRpcNotification(string method, string paramsJson): JSON-RPC 2.0 Notification を送信

  • void RequestRpc(string method, string paramsJson, Action<RpcResult> onResult, long timeoutMillis): JSON-RPC 2.0 Request を送信(タイムアウトをミリ秒で指定)

RequestRpcNotification のパラメータ

  • method: 呼び出すメソッド名 (例: "2025.2.0/RequestSimulcastRid")

  • paramsJson: メソッドのパラメータを表す JSON 文字列。オブジェクト形式 (例: {"key":"value"}) または配列形式 (例: [1,2,3]) で指定します。パラメータがない場合は "{}" を指定してください

注釈

JSON-RPC 2.0 Notification として送信されます。この場合 Sora はレスポンスを返しません。

RequestRpc のパラメータ

  • method: 呼び出すメソッド名 (例: "2025.2.0/RequestSimulcastRid")

  • paramsJson: メソッドのパラメータを表す JSON 文字列。オブジェクト形式 (例: {"key":"value"}) または配列形式 (例: [1,2,3]) で指定します。パラメータがない場合は "{}" を指定してください

  • onResult: Sora.RpcResult 型のレスポンス用のコールバック

  • timeoutMillis: Sora レスポンスのタイムアウト時間(ミリ秒)。 0 より大きな値を指定してください

    • Sora.DefaultRpcTimeoutMillis で指定すると 5,000 ms となります

注釈

SDK は内部でリクエスト ID を自動採番し、レスポンスと紐付けます。アプリケーション側で ID を指定することはできません。

RpcResult のメンバー

  • ResultKind: RPC リクエスト結果です。 Response / Timeout のいずれかが入ります

  • Method: RPC リクエストしたメソッドです。リクエスト時に指定したメソッドと同じものが入ります

  • ParamsJson: RPC リクエストパラメータの JSON 文字列です。リクエスト時に指定した JSON 文字列と同じものが入ります

  • ResponseJson: RPC レスポンスの JSON 文字列です。データ内容のパースは SDK の利用者側の責務となります

重要

ResultKind.Response は Sora からの RPC レスポンスを受信することができたことのみを表し、リクエスト内容の成功 / 失敗を表しません。 ResponseJson の内容(result / error)を利用者側で判定してください

利用方法

シグナリング設定

Sora Unity SDK Examples の SoraSample.cs では、Inspector で dataChannel signaling を設定し、 Sora.Config に反映しています。

以下のように DataChannel 経由のシグナリングを有効にする設定を行います。

var config = new Sora.Config()
{
    EnableDataChannelSignaling = true,
    DataChannelSignaling = true,
};

RPC リクエストの送信とレスポンス受信

ここでは UI のボタンから RPC リクエストを送信し、レスポンスを受信する例を示します。

レスポンスデータはコールバックで受け取るため、レスポンスデータを処理するコールバックハンドラを Sora.RpcResult 型で用意します。 また受信したレスポンスデータは JSON 形式の文字列です。パース処理は独自に実装する必要があります。

重要

Sora.RequestRpc に渡される onResult コールバックは DispatchEvents() で処理されるため、 Update() で定期的に DispatchEvents() が呼ばれている必要があります。

// SoraSample.cs からの抜粋した RPC 機能を使用するための設定パラメータと送信/受信メソッドの例です。
// [Header]`` 属性が付与されたフィールドで、各 RPC メソッドのパラメータを Inspector から設定できます。

// 送信する RPC メッセージの種類
public enum RpcMessageType
{
    None,
    RequestSimulcastRid,
    RequestSpotlightRid,
    ResetSpotlightRid,
    PutSignalingNotifyMetadata,
    PutSignalingNotifyMetadataItem,
}

[Header("RPC メッセージの設定")]
// Inspector で送信したい RPC メッセージの種類を選択します
public RpcMessageType rpcMessageType = RpcMessageType.None;
// RPC リクエストの timeoutMillis(ミリ秒)。空欄の場合は SDK のデフォルト値を利用します
public string rpcTimeoutMillis = "";

[Header("RequestSimulcastRid の設定")]
public string sendRequestSimulcastRid = "r0";
public string sendRequestSimulcastRidSenderConnectionId = "";

[Header("RequestSpotlightRid の設定")]
public string sendRequestSpotlightFocusRid = "r1";
public string sendRequestSpotlightUnfocusRid = "none";
public string sendRequestSpotlightRidConnectionId = "";

[Header("ResetSpotlightRid の設定")]
public string sendResetSpotlightRidConnectionId = "";

[Header("PutSignalingNotifyMetadata の設定")]
public string sendPutSignalingNotifyMetadataJson = "{\"key\":\"value\"}";
public bool sendPutSignalingNotifyMetadataPush = false;

[Header("PutSignalingNotifyMetadataItem の設定")]
public string sendPutSignalingNotifyMetadataItemKey = "status";
public string sendPutSignalingNotifyMetadataItemValue = "\"active\"";
public bool sendPutSignalingNotifyMetadataItemPush = false;

// ボタンを押したときに呼び出される RPC 送信メソッド例
// RpcMessageType enum で送信する RPC メッセージの種類を選択します。
public void OnClickSendRpc()
{
    if (sora == null)
    {
        return;
    }

    // Inspector で選択された RPC メッセージの種類に応じて処理を分岐する
    switch (rpcMessageType)
    {
        case RpcMessageType.None:
            Debug.Log("RPC メッセージの種類が選択されていません");
            break;
        case RpcMessageType.RequestSimulcastRid:
            SendRequestSimulcastRid();
            break;
        case RpcMessageType.RequestSpotlightRid:
            SendRequestSpotlightRid();
            break;
        case RpcMessageType.ResetSpotlightRid:
            SendResetSpotlightRid();
            break;
        case RpcMessageType.PutSignalingNotifyMetadata:
            SendPutSignalingNotifyMetadata();
            break;
        case RpcMessageType.PutSignalingNotifyMetadataItem:
            SendPutSignalingNotifyMetadataItem();
            break;
    }
}

// レスポンスをコールバックで受け取るハンドラです
// sora.RequestRpc の引数に指定します
void HandleRpcResult(Sora.RpcResult result) {
    switch (result.ResultKind) {
        // レスポンスを受信できた場合です
        // レスポンスの内容が成功かエラーか判定するにはさらにレスポンスデータをパースする必要があります
        case Sora.RpcResultKind.Response:
        {
            var responseJson = result.ResponseJson;
            Debug.LogFormat("RPC response: method={0}, response={1}", result.Method, responseJson);

            // JSON-RPC 2.0 の result / error 判定例
            // 実際には JSON パーサーを使用してください
            if (responseJson != null && responseJson.Contains("\"result\""))
            {
                Debug.Log("RPC result が返りました");
            }
            else if (responseJson != null && responseJson.Contains("\"error\""))
            {
                Debug.LogError("RPC error が返りました");
            }
            break;
        }
        // リクエストがタイムアウトになった場合です
        case Sora.RpcResultKind.Timeout:
            Debug.LogErrorFormat("RPC timeout: method={0}", result.Method);
            break;
    }
}

// Sora.RequestRpc メソッドを利用して RPC リクエストします
void RequestRpcInternal(string method, string paramsJson)
{
    if (sora == null)
    {
        return;
    }

    // タイムアウト時間無指定であれば Sora.DefaultRpcTimeoutMillis を指定します
    if (string.IsNullOrWhiteSpace(rpcTimeoutMillis))
    {
        sora.RequestRpc(method, paramsJson, HandleRpcResult, Sora.DefaultRpcTimeoutMillis);
        return;
    }

    // タイムアウト時間は long 型のためパースします
    var timeoutMillisText = rpcTimeoutMillis.Trim();
    if (!long.TryParse(timeoutMillisText, out var timeoutMillis))
    {
        Debug.LogErrorFormat("RPC timeoutMillis の形式が不正です: timeoutMillis={0}", rpcTimeoutMillis);
        return;
    }
    if (timeoutMillis <= 0)
    {
        Debug.LogErrorFormat("RPC timeoutMillis は 1 以上を指定してください: timeoutMillis={0}", timeoutMillis);
        return;
    }

    sora.RequestRpc(method, paramsJson, HandleRpcResult, timeoutMillis);
}

// 各 RPC メソッド ( SendRequestSimulcastRid、SendRequestSpotlightRid など) は、
// Inspector で選択されたメッセージタイプに応じて OnClickSendRpc() から呼び出されます。
// 実際の RPC リクエストは sora.RequestRpc() メソッドを使用して送信されます。

void SendRequestSimulcastRid()
{
    Debug.LogFormat("SendRequestSimulcastRid: rid={0}, sender_connection_id={1}", sendRequestSimulcastRid, sendRequestSimulcastRidSenderConnectionId);
    string paramsJson;
    if (string.IsNullOrEmpty(sendRequestSimulcastRidSenderConnectionId))
    {
        paramsJson = $"{{\"rid\":\"{sendRequestSimulcastRid}\"}}";
    }
    else
    {
        paramsJson = $"{{\"sender_connection_id\":\"{sendRequestSimulcastRidSenderConnectionId}\",\"rid\":\"{sendRequestSimulcastRid}\"}}";
    }
    RequestRpcInternal("2025.2.0/RequestSimulcastRid", paramsJson);
}

void SendRequestSpotlightRid()
{
    Debug.LogFormat("SendRequestSpotlightRid: focus={0}, unfocus={1}, send_connection_id={2}", sendRequestSpotlightFocusRid, sendRequestSpotlightUnfocusRid, sendRequestSpotlightRidConnectionId);
    string paramsJson;
    if (string.IsNullOrEmpty(sendRequestSpotlightRidConnectionId))
    {
        paramsJson = $"{{\"spotlight_focus_rid\":\"{sendRequestSpotlightFocusRid}\",\"spotlight_unfocus_rid\":\"{sendRequestSpotlightUnfocusRid}\"}}";
    }
    else
    {
        paramsJson = $"{{\"send_connection_id\":\"{sendRequestSpotlightRidConnectionId}\",\"spotlight_focus_rid\":\"{sendRequestSpotlightFocusRid}\",\"spotlight_unfocus_rid\":\"{sendRequestSpotlightUnfocusRid}\"}}";
    }
    RequestRpcInternal("2025.2.0/RequestSpotlightRid", paramsJson);
}

void SendResetSpotlightRid()
{
    Debug.LogFormat("SendResetSpotlightRid: send_connection_id={0}", sendResetSpotlightRidConnectionId);
    string paramsJson;
    if (string.IsNullOrEmpty(sendResetSpotlightRidConnectionId))
    {
        paramsJson = "{}";
    }
    else
    {
        paramsJson = $"{{\"send_connection_id\":\"{sendResetSpotlightRidConnectionId}\"}}";
    }
    RequestRpcInternal("2025.2.0/ResetSpotlightRid", paramsJson);
}

void SendPutSignalingNotifyMetadata()
{
    Debug.LogFormat("SendPutSignalingNotifyMetadata: {0}", sendPutSignalingNotifyMetadataJson);
    string paramsJson = sendPutSignalingNotifyMetadataPush
        ? $"{{\"push\":true,\"metadata\":{sendPutSignalingNotifyMetadataJson}}}"
        : $"{{\"metadata\":{sendPutSignalingNotifyMetadataJson}}}";
    RequestRpcInternal("2025.2.0/PutSignalingNotifyMetadata", paramsJson);
}

void SendPutSignalingNotifyMetadataItem()
{
    Debug.LogFormat("SendPutSignalingNotifyMetadataItem: key={0}, value={1}", sendPutSignalingNotifyMetadataItemKey, sendPutSignalingNotifyMetadataItemValue);
    string paramsJson = sendPutSignalingNotifyMetadataItemPush
        ? $"{{\"push\":true,\"key\":\"{sendPutSignalingNotifyMetadataItemKey}\",\"value\":{sendPutSignalingNotifyMetadataItemValue}}}"
        : $"{{\"key\":\"{sendPutSignalingNotifyMetadataItemKey}\",\"value\":{sendPutSignalingNotifyMetadataItemValue}}}";
    RequestRpcInternal("2025.2.0/PutSignalingNotifyMetadataItem", paramsJson);
}

// コールバック発火のため DispatchEvents の定期的な呼び出しが必要です
void Update()
{
    if (sora != null)
    {
        sora.DispatchEvents();
    }
}
© Copyright 2024, Shiguredo Inc. Created using Sphinx 9.1.0