Narazaka::Blog

奈良阪という人のなにか

Udon Network Updateなど同期関連についての個人的まとめメモ

未検証・不明のとこと判明しているとこを正しく峻別したい

Udon network

大本ドキュメント docs.vrchat.com

  • 変数
  • イベント
  • Owner

を理解しよう

Owner

  • GameObjectごとの所有権

Onwer以外の特殊な権限

  • IsInstanceOwner
    • インスタンスを建てた人
    • 同期系では特別扱いされない気がするが、VRChatのUI上部屋の権限(問答無用でkickできるとか)がこの人に紐付いていたりする。
  • IsMaster
    • インスタンスにはじめて入ってきた人
    • 最初に全てのGameObjectのOwnerをつかむ
    • 退出した場合次に入ってきた人が順次選ばれる

同期手段

イベント

変数同期より遅いらしい?

  • ローカルのみに通知
    • SendCustomEvent
    • SendCustomEventDelayedSeconds
    • SendCustomEventDelayedFreams
  • Onwerのみに通知(Onwerが発火した場合Ownerに送られる)
    • SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.All)
  • 全員に通知(送った人を含む)
    • SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.Owner)

特にネットワーク系は引数が取れないのでFoo1、Foo2などのイベントを作って対応したりするハックがあったりする。

同期変数

  • 同期変数値の変更はOwnerのもののみがネットワーク越しに反映される
    • 非ローカルで一時的に変更することは可能
    • 変更後Ownerの値に戻るタイミング?(次フレーム前か次の変数同期時か)
  • 同期間隔中の補完手段が選択できる
    • None: 補完無し
    • Linear: 線形補間
      • 直前2値の線形補間?
    • Smooth: スムース補完
      • 直前3値のベジエとか?
    • 詳しくは検証していない
  • 変数同期の方針として、GameObjectごとに連続同期(Continuous)と手動同期(Manual)を設定できる
    • 同じGameObject内のUdonBehavior及びVRCObjectSyncは全てその設定上で最も厳しい(Manualのほうが厳しい)モードになる
  • Onwerが切り替わる瞬間がいろいろ怪しい?

連続同期

  • 頻繁に書き換わる変数に適する
  • 帯域幅節約のために(自動的にタイミングが間引かれて?)中途の変更が送信されない可能性がある
  • 変化が終わったら最終値はいずれ同期されるはず

手動同期

  • 変更が少ない変数に適する
  • 変数を変更して同期したい場合、RequestSerializationを呼ぶ
    • 変数変更してからRequestSerializationを呼んでも、RequestSerializationを呼んでから発火されるOnPreSerialization中で変更しても良い。
    • フレーム内や次フレームと言う保証はなく、次のネットワーク同期タイミングになる
  • RequestSerializationを高速で呼ぶとレート制限がかかる
    • 待機時間の後にOnPreSerialization→送信→OnPostSerializationが実行される
    • 更新が遅れるが失われはしない?
  • VRCObjectSyncはこの方式では動作できないため、VRCObjectSyncと手動同期UdonBehaviorを同一GameObjectに配置することは出来ない。

OnDeserialization

  • 変数が変更されたか否かにかかわらず、同期変数値が到着したら常に発火される。

VRCObjectSync

  • positionとrotationを高速に同期する
    • localのそれなのか?
    • parentを変更しても動作するか?

細かい仕様・動作挙動

参照ツリー

ネットワーク初期化タイミング

  • Startメソッド時点ではネットワークの初期化がすんでいない
    • ネットワーク系の記述はしないべき
    • IsOwnerがつねにfalseだったり、SendCustomNetworkEventが正しく動作しない
  • OnPlayerJoin時点で初期化済み
    • 後からjoinしたユーザーにも変数同期済み
    • unu以前は入室直後に同期変数の値が更新されていない問題が存在した

エディタ上での特殊な挙動

  • シーン再生開始時に非アクティブなGameObjectについたUdonBehaviorにおいてはOnEnable、OnDisable、Startが実行されない。

RequestSerialization / OnDeserialization (Manual)

  • RequestSerialization呼び出しはOwnerのみ有効
  • RequestSerialization後のOnDeserializationは非Owner(送信先)でしか呼ばれない。  - →送信元でも同期変数の変化に伴う処理をしたい場合は、OnPostSerializationあたりでOnDeserializationと同様の処理をするのが良さそうかも?

SetOwnerの挙動

  • OwnerがSetOwnerを呼ぶ
    • OnOwnershipRequestやOnOwnershipTransferedは呼ばれない。
  • 非OwnerがSetOwnerを呼ぶ
    • 同一フレーム内でローカルのOnOwnershipRequest及びOnOwnershipTransferedが呼ばれる
      • この時点ではネットワーク上実際にOwnerが移動してはいない
    • OwnerのOnOwnershipRequest及びOnOwnershipTransferedも数百ms後に呼ばれる
  • Ownerと非Ownerの結果が食い違った場合
    • 非OwnerローカルでOnOwnershipRequestがtrueだったがOwner側がfalseを後で返した場合、再び非Owner側にてOnOwnershipTransferedが呼ばれ、IsOwnerの結果もfalseになる。

つまり

  • SetOwner呼び出し同一フレーム中のOnOwnershipTransferedはかりそめの状態。
    • SetOwner要求後OwnerがOnOwnershipRequestでtrueを返すまでの間、Owner要求者と実際のOwner両方のローカルでIsOwnerがtrueになっている。
    • つまりIsOwnerで判定して動く処理が、要求者およびOwnerの両方で動作することに注意。

応答時間

  • SendCustomNetworkEvent
    • PlayerAのSendCustomNetworkEvent(Foo)~PlayerBのFoo呼び出しにかかる時間:550ms~650ms(参考値)
  • RequestSerialization / OnDeserialization (Manual)
    • PlayerAのRequestSerialization~PlayerBのOnDeserialization呼び出しにかかる時間:250ms~350ms(参考値)
  • 環境差異がある(UnU betaとOpen Betaで違ったり)が、同期変数のほうがイベントより速く伝わる傾向は変わらない
  • Continuousの同期間隔