お世話になっております、弊プロジェクトで
[FixupRedirectors is trying to delete regular Blueprint [Content removed]
<br/>
プロジェクト内のBlueprintおよびLevelSequenceアセットに、メインアセットデータと同居するObjectRedirectorが含まれています。
Cmd: obj list outer=/Game/[ProjectName]/[FolderName]/[BP_Name]
Objects:
BlueprintGeneratedClass /Game/.../[BP_Name].SKEL_[BP_Name]_C
BlueprintGeneratedClass /Game/.../[BP_Name].[BP_Name]_C
Blueprint /Game/.../[BP_Name].[BP_Name]
ObjectRedirector /Game/.../[BP_Name].Default__[BP_Name]_C_3
※部外秘情報のため、名称・パスを省略系にしていることをご容赦ください
<br/>
このリダイレクタ(/Game/…/[BP_Name].Default__[BP_Name]_C_3)の DestinationObject は同パッケージ内の [BP_Name]_C を指しています。FixUpRedirectors ではこのリダイレクタは削除されず、再保存しても消えません。
<br/>
確認済み事項
・DestinationObject は同パッケージ内を指している(Joseph Cheng氏の報告と一致)
・Blueprint、LevelSequence(DirectorBP残骸)の両方で発生
・アセットの複製時にリダイレクタが削除される事を確認
・Joseph氏の解決策 Redirector->Rename(nullptr, GetTransientPackage(), REN_DontCreateRedirectors) で削除される事を確認
<br/>
質問
Joseph氏の解決策 Redirector->Rename(nullptr, GetTransientPackage(), REN_DontCreateRedirectors) + 再保存を適用したいのですが、事前に確認したい点があります。
<br/>
このObjectRedirectorが外部から参照されていないと見なして安全でしょうか? DestinationObjectが同パッケージ内を指しているため、外部からの参照はパッケージ経由で実体に到達できるはずですが、確実に検証する手段がありません。
<br/>
オブジェクト単位で参照元を調べる方法はありますか? IAssetRegistry::GetReferencers はパッケージレベルの参照のみ返します。
オブジェクト単位での参照を調べることはできませんでした。
<br/>
仮にこのリダイレクタへの外部参照が存在する場合、削除するとどうなりますか? 参照がnullになりますか?それともUEのパッケージロード機構が同パッケージ内のDestinationObjectに透過的に解決しますか?
<br/>
クリーンアップを安全に実施するためのご助言をいただけると助かります。
[Attachment Removed]
お世話になっております。
ご質問ありがとうございます。当方でも再現をとって確認をいたしました。以下がご質問事項への回答となります。
> このObjectRedirectorが外部から参照されていないと見なして安全でしょうか?
実務上ほぼ安全のはずですが、「無条件に安全」とまで言い切れないため、削除前に被参照ゼロの確認をお勧めいたします。
> オブジェクト単位で参照元を調べる方法はありますか?
ご認識は正しく、IAssetRegistry::GetReferencers はパッケージレベルの参照のみを返します。オブジェクト単位で調べるには、メモリに乗っているロード済みオブジェクトに限れば・・・という条件付きですが、まずコンソールコマンド obj refs NAME=<オブジェクトパス> が利用できます。ツールプログラムから調べる場合は、UObject::RetrieveReferencers()を使用するか、その内部の実装が参考になると思います。
UObject::RetrieveReferencers() は対象オブジェクトへの参照元を、「内部参照」と「外部参照」に分けて返すため、「外部参照の有無」は、返された ExternalReferencers の要素数を見るだけで確認できます。ただし、上述のとおり、ディスク上の未ロードパッケージが保持する参照はここに現れません。そのため実際には、
1.IAssetRegistry::GetReferencers() で(パッケージレベルでよいので)候補となる参照元パッケージを洗い出す
2.洗い出されたパッケージをロードし、メモリに乗せる
3.RedirectorObject->RetrieveReferencers(&Internal, &External) を実行し、External が空かどうかを確認
・空であれば、外部参照はないので、そのまま Joseph 氏の解決策でリダイレクタを削除
・空でなければ、(Q3 のとおり)その参照元を先に再保存してから、Joseph氏の解決策で削除
という手順を踏む必要があります。
Q3. 仮に外部参照が存在する場合、削除するとどうなるか? null になるか、透過解決されるか?
仮に何も対策せずに(リダイレクタを)削除しますと、その外部参照は次回ロード時に null 化します。 「同パッケージ内の DestinationObject へ透過的に解決する」という挙動が、リダイレクタの存在を前提としているため、リダイレクタ削除後にはこの透過解決が走らない、という事情があります。
ただし、これは「参照元を再保存せずに削除した場合」のシナリオです。参照元をロードした後、先に再保存さえしておけば、透過解決されていたインポートが DestinationObject の直参照として焼き直され、リダイレクタが不要になります。その結果、リダイレクタは安全に削除が可能で、null 化の心配はなくなります。
最後に一点補足です。 前述の削除処理(Joseph氏の手法)を一括処理ツールやコマンドレットに組み込む(=①リダイレクタの Rename と②保存を同一フレーム内で連続実行する)場合は、念のため、①と②の間に明示的な GC 実行を挟んでいただくのがよさそうです。GC を挟まずに保存すると、まだ破棄されていないリダイレクタが .uasset に書き戻されてしまう恐れがあります。なお、「削除処理を行った後、保存は別途コンテンツブラウザからCTRL+Sで手動で行う」という運用であれば、この心配はございませんので、ご放念ください。
以上となります。
よろしくお願いいたします。
[Attachment Removed]
ご回答ありがとうございます。また、返信が遅くなり申し訳ありません。
オブジェクト単位での参照元検索方法についてご提示いただき、大変助かりました。
また、保存時のGCに関する注意点についてもご教示いただき感謝申し上げます。
今回は対象となる数が少ないため、ご説明いただいた内容をもとに、一括処理ではなく手動で保存作業を行おうと思います。
[Attachment Removed]
お世話になっております。
回答のご確認ありがとうございます。
それでは本件は対応済みとしてCloseとさせていただきます。
また何かご不明点やご質問などございましたら、お気軽にEPSをご利用ください。
(本件で追加のご質問がある場合は一定期間内であればコメントをご追記いただくことで自動再Openしますので、そちらもご利用ください)
以上、よろしくお願いいたします。
[Attachment Removed]