Multi-Userセッションの同期完了時にEditorUtilityWidgetとAVPEditorTickableActorBaseで処理を実行したい

前提:

・現在EditorUtilityWidgetのUIにレベル上に配置されているAVPEditorTickableActorBaseを継承したカスタムアクタークラスの内部情報を表示しています。

・EUWはカスタムアクタークラスが持っているデリゲートの通知を受け取ってUIの更新を行っています。

・カスタムアクタークラスはOnLevelActorAttached, OnLevelActorDetached, OnLevelActorDeleted等のGEditorのデリゲートを使用している

問題点:

通常のユースケースでは特に問題は無いのですが、MultiUserSession入室時(同期時)にアクターの追加や削除が行われてしまい不用意にOnLevelActorAttached等にバインドしている処理が実行されてしまいます。

一時的にデリゲートの購読削除ないし処理の停止をしたいのですが、IConcertSyncClientのOnWorkspaceStartupで入室時のタイミングを取れるのですが、Multi-User Session同期完了後(レベル上にいる全てのアクターとアクターコンポーネントのロード完了後)のタイミングを通知してくれるデリゲートを教えてほしいです(デリゲートの購読開始ないし処理の再開をしたい為)。

またMulti-User Sessionに入室時にローカルに居るアクターがMulti-User Session上にいるアクターと同一な物が存在する場合はアクターが再生成されるのではなく内部情報を書き換えて再利用される(PostLoadやその他初期化処理が実行されない)という認識で合っているでしょうか?

[Attachment Removed]

再現手順[Attachment Removed]

お世話になっております。

こちら並行してAPI周りの調査は進めますが、頂いた情報からこちらの環境で再現するまでにお時間を頂く事になりそうなので、本件の事象が再現できる最小のプロジェクトと手順の方をご共有して頂く事は可能でしょうか?

お手数をおかけしますが、ご確認の方何卒よろしくお願い申し上げます。

[Attachment Removed]

ご回答ありがとうございます。

毎回マルチユーザーの同期完了時(自分のマルチユーザー環境同期時)自動的に何かしらの関数を実行したい場合は以下の方法で正しいでしょうか?

// 初期化のタイミングでイベントに登録する
void ACustomActor::BindDelegates()
{
  if (IConcertSyncClientModule::IsAvailable())
  {
    if (TSharedPtr<IConcertSyncClient> ConcertSyncClient = IConcertSyncClientModule::Get().GetClient(TEXT("MultiUser")))
    {
      ConcertSyncClient->OnWorkspaceStartup().AddUObject(this, &ACustomActor::OnMultiUserSessionStartup);
    }
  }
}
 
void ACustomActor::OnMultiUserSessionStartup(const TSharedPtr<IConcertClientWorkspace>& Workspace)
{
  Workspace->OnFinalizeWorkspaceSyncCompleted().AddUObject(this, &ACustomActor::OnMultiUserSessionFullySynced);
}
 
void ACustomActor::OnMultiUserSessionFullySynced()
{
    // マルチユーザーセッション同期完了時に実行したい処理を呼ぶ
  SomeFunction();
}

[Attachment Removed]

追加質問:

細かいケアや遅延実行のご回答ありがとうございます。

少し別問題にはなるのですが、現在Editor Utility Widgetを開くとアクターをスポーンする実装をしているのですがそのスポーン処理が正しくTransactionに乗らない問題に直面しています。

EUWのNativeConstruct内でスポーンの処理を実装しています(FScopedTransactionを使用している)。

現象としては以下の流れになります:

  1. 空のレベルのマルチユーザーセッションを作る
  2. 一旦マルチユーザーセッションを抜ける
  3. 該当のEUWを開く(ローカル環境ではこの時点でアクターがスポーンされている)
  4. 該当のEUWを開いたままマルチユーザーセッションに入室(入室時に出てくる通知はローカルの変更の破棄を選択)
  5. マルチユーザーセッションで同期が完了した時点では自分の環境ではアクターがスポーンされているが他ユーザーにはスポーンされていない(Transactionされていない)

検証を重ねた結果以下の順番で処理が行われているように思えるのですが正しいでしょうか?

OnMultiUserSessionStartup->EUWのNativeConstruct(大体二回くらい実行される)->OnFinalizeWorkspaceSyncCompleted

上記が正しい場合はどのように対処するのが正しいでしょうか?(ツールを閉じてマルチユーザーセッションに入室後、ツールを開く場合は正常動作します)

補足:スポーンされる予定のアクターが既に存在しているマルチユーザーセッションだとEUWのNativeConstructが呼ばれなかったりするのですが、その場合は回答いただいた方法でUIの更新を行えています。

[Attachment Removed]

ご回答いただき、ありがとうございます!

こちら解決いたしましたので、クローズして頂けると幸いです。

よろしくお願いいたします。

[Attachment Removed]

お世話になっております。

ご連絡ありがとうございます。

こちら再現できる最小プロジェクトの提供にもお時間を頂くようです。。

お手数をおかけしますが、。API周りの調査をお願いできればと思います。

よろしくお願いします

[Attachment Removed]

お世話になっております。

>Multi-User Session同期完了後(レベル上にいる全てのアクターとアクターコンポーネントのロード完了後)のタイミングを通知してくれるデリゲートを教えてほしいです(デリゲートの購読開始ないし処理の再開をしたい為)。

上記ですが、

1.

IConcertClientWorkspace::OnWorkspaceSynchronized()

Concert が保持するトランザクション履歴の適用が完了したフェーズで呼ばれるイベントです。トランザクション適用完了及びパッケージ更新反映完了が保証されている所見です。注意点としては、非同期ロードやWorldPartitionのストリーミングに対応しているか現時点では厳密には保証していないようです。

2.

IConcertClientWorkspace::OnFinalizeWorkspaceSyncCompleted()

ワークスペース同期の finalize フェーズが完了したタイミング。

Concert 的には「この同期サイクルが収束した」と見なす地点です。

イベント処理再開のトリガーとして使用する場合はこちらの方が安全そうな所見でした。

3.

IConcertClientWorkspace::AddWorkspaceFinalizeDelegate

Workspace 同期の finalize を、自分が OK を返すまで遅延させることができます。

全クライアントの準備完了を待ってから finalize したい場合や明示的な同期ポイントを設けたい場合に使うと効果が見込めそうです。こちらのドキュメントを見て頂くとより理解が深まると思います。https://dev.epicgames.com/documentation/en\-us/unreal\-engine/API/Plugins/ConcertSyncClient/IConcertClientWorkspace/AddWorkspaceFina\-

以上の三点を確認して頂ければと存じます。

>またMulti-User Sessionに入室時にローカルに居るアクターがMulti-User Session上にいるアクターと同一な物が存在する場合はアクターが再生成されるのではなく内部情報を書き換えて再利用される(PostLoadやその他初期化処理が実行されない)という認識で合っているでしょうか?

こちらですが、

こちらに関しましては基本的には頂いた内容で合っている認識です。

ただし注意点として、レベルのフルリロード、特殊なストリーミングパターン、World Partition のセル再構築

などが絡む場合は挙動が変わる可能性がありますので、必ずPostLoadがコールされないわけではないようです。

ProcessTransactionEvent

関数内を読んで頂くと詳細な実装が確認できます。

大変お手数おかけしますが、ご確認の方何卒よろしくお願い申し上げます。

[Attachment Removed]

お世話になっております。

方向性は概ね合ってる所見です。

細かい所ですが、入室の度にデリゲートが登録されるのを防ぐために、

BindDelegates()

内でデリゲートハンドルが有効だったら登録しない等の処理を追加する、EndPlayで登録を解除する等の処理を追加すると安全です。

次に、

Workspace->OnFinalizeWorkspaceSyncCompletedはConcertの同期収束です。

状況次第ではありますが、 ストリーミングや他の非同期ロード完了を厳密保証するとは言い切れない余白があるので

void ACustomActor::OnMultiUserSessionFullySynced()

内のSomeFunctionのコールを

 FTSTicker::GetCoreTicker().AddTicker(
 
 
 FTickerDelegate::CreateWeakLambda(this, [this](float)
 {
 

 
            SomeFunction();
 

 
            return false;
 

 
 })
 );

で遅延実行すると想定に更に近い動作になりそうです。

お手数をおかけしますがご確認の方何卒よろしくお願い申し上げます。

[Attachment Removed]

お世話になっております。

>検証を重ねた結果以下の順番で処理が行われているように思えるのですが正しいでしょうか?

>OnMultiUserSessionStartup->EUWのNativeConstruct(大体二回くらい実行される)->OnFinalizeWorkspaceSyncCompleted

上記ですが、概ね相違ございませんでした。

>上記が正しい場合はどのように対処するのが正しいでしょうか?

こちらですが、

OnFinalizeWorkspaceSyncCompleted

で同期収束した辺りでSpawnするとTransactionに乗り、同期することが多いです。

以前の回答にも記載しましたが、

FTSTicker::GetCoreTicker().AddTicker(
    FTickerDelegate::CreateWeakLambda(this, [this](float)))

の様に遅延実行が推奨です。

そして

Spawn直前に

const FScopedTransaction Tx(NSLOCTEXT("Spawn Actor","Spawn Actor","Spawn Actor"));

のようにトランザクションを開始して

Spawnを行い

NewActor->SetFlags(RF_Transactional);
NewActor->Modify();

のように変更を記録する処理を追加する事が推奨されます。

お手数をおかけしますがご確認の方何卒よろしくお願い申し上げます。

[Attachment Removed]

お世話になっております。

ではこちらクローズさせて頂きます。

またご質問あればご遠慮なくご連絡ください。

[Attachment Removed]