[UE4]全アセット または 特定アセットを強制的に再保存する方法について（ResavePackagesCommandletの解説含む）

# はじめに
何らかの理由で全アセットを再保存したいケースがしばしば稀によく時々あります。例えば、**一部のアセットが古いバージョンのままになっている**せいで、Cookが長引いたりパッチサイズが大きくなるなどの問題が発生している場合などです。

今回はその方法について説明します。なお、プロジェクトの規模に応じて再保存にかかる時間が増えるのでご注意ください（後述のオプションなどを使って再保存対象のアセットを制限することで時間を削減可能です）。

# コンテンツブラウザで行う方法
フォルダの右クリックメニューにある **Resave All** から再保存可能です。この時に走る処理は ```Engine\Source\Editor\ContentBrowser\Private\PathContextMenu.cpp``` の ```FPathContextMenu::ExecuteResaveFolder()``` です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/0df98010-6e48-3267-87e5-bc23576b6cdb.png)

# Commandlet で行う方法
JenkinsなどのCIツールから定期的に実行したい場合は **ResavePackagesCommandlet** を使うことになります。

Commandlet について
https://docs.unrealengine.com/en-US/API/Runtime/Engine/Commandlets/UCommandlet/index.html
https://docs.unrealengine.com/en-US/API/Editor/UnrealEd/Commandlets/index.html
https://docs.unrealengine.com/ja/SharingAndReleasing/Deployment/Cooking/index.html
[[UE4] ライトビルドをコマンドから実行しよう！](https://historia.co.jp/archives/7343/)
[UE4 Commandletでアセットを一括修正してみる](https://qiita.com/unknown_ds/items/e49aa05e142a04a1ee59)

プロジェクト・エンジンの全アセットを全保存したい場合は以下のコマンドを実行すればOKです。

```cpp
\Engine\Binaries\Win64\UE4Editor-Cmd.exe {PROJECT_PATH}/{PROJECT_NAME}.uproject -run=resavepackages
```

もし正常に再保存されない場合はCIツール上の出力やプロジェクト側の Saved/Logsにあるログを確認しましょう。
例えば、僕が検証した際は再保存が行われないアセットに関する以下のWarningが出力されていました。

```cpp
LogContentCommandlet: Display: Loading ../../../{PROJECT_NAME}/Content/Mannequin/Character/Textures/UE4Man_Logo_N.uasset
LogContentCommandlet: Warning: Skipping resave of ../../../{PROJECT_NAME}/Content/Mannequin/Character/Textures/UE4Man_Logo_N.uasset due to engine version mismatch (Package:14469661, Editor:0) 
```

2行目にある通り、アセットが持っているエンジンバージョン情報と現在のエンジンバージョンが異なるために再保存をスキップしていました。もし「一部のアセットが古いバージョンのままになっている」問題を解決したい場合はこのスキップ処理は無効にしたいところです。(参考： [アセットとパッケージのバージョンを管理する](https://docs.unrealengine.com/ja/ProgrammingAndScripting/ProgrammingWithCPP/UnrealArchitecture/VersioningAssetsAndPackages/index.html) )

そんな時に便利なのが、**IgnoreChangelist** オプションです。このオプションを以下のようにして使うことでアセットが持つエンジンバージョンを無視して強制的に再保存するようになります。

```cpp
\Engine\Binaries\Win64\UE4Editor-Cmd.exe {PROJECT_PATH}/{PROJECT_NAME}.uproject -run=resavepackages -IgnoreChangelist
```

ResavePackagesCommandlet には IgnoreChangelist 以外にも様々なオプションが用意されています。例えば **buildlighting** や**buildreflectioncaptures** を使うことで、自動的かつ定期的にライト・リフレクションキャプチャのビルドを実行することが可能です。

上記以外のオプションが気になる方は ```Engine\Source\Editor\UnrealEd\Private\Commandlets\ContentCommandlets.cpp``` における以下の関数を是非ご確認ください。各オプションに関する実装・コメントが書かれています。
```UResavePackagesCommandlet::Main```
```UResavePackagesCommandlet::InitializeResaveParameters```

おしまい

## ResavePackagesCommandletの処理対象を制限する方法
で終わるのは微妙な気がしたので、予告通り「オプションなどを使って再保存対象のアセットを制限すること」について説明します。といっても、オプションについて簡単にざーっと説明する感じになります（抜けがあったらゴメンナサイ！）。

ちなみに、これから説明するオプションは buildlighting オプションなどを使って再保存以外の処理を使用する際も有効です。

### UResavePackagesCommandlet::Main関数 より

#### OnlyUnversioned
エンジンバージョン情報が空の場合のアセットのみを対象にします。何らかの原因でエンジンバージョンが空になっているアセットに対してバージョン情報をちゃんと埋め込みたい場合に便利だと思います。

#### STRIPEDITORONLY
エディタでしか使われないアセットは処理の対象外にします。パッケージには含まれないアセットになるため、今回のようなケースでは非常に有効なオプションです。

#### OnlySaveDirtyPackages
何らかの変更が行われてDirtyフラグが立っているアセットのみを対象にします。ツールで大量のアセットを編集した後に保存処理を行う際に便利かもしれません。ただ勝手に保存処理を行うのもどうかと思うので、保存するか否かのダイアログを直前に出した方が親切かもです。
 
#### AutoCheckOutPackages または AutoCheckOut
保存を行うアセットを自動的にチェックアウトします。

#### SkipCheckedOutPackages
**自動チェックアウトが有効** かつ 既にチェックアウト済みのアセット、またはsyncが行われていない場合は対象のアセットを単純にスキップするオプションです。このオプションを使用しない場合はチェックアウトに失敗した時にエラーを出すようになります。

#### AutoCheckIn または AutoSubmit
上述の自動チェックアウトが有効な場合、保存後にチェックインを行います。自動チェックアウトが無効な場合はこちらも必ず無効になるのでご注意ください

#### OnlyLicenseed
アセットが持つ LicenseeVersion が0以外のアセットのみを対象にします。LicenseeVersion については、 [アセットとパッケージのバージョンを管理する](https://docs.unrealengine.com/ja/ProgrammingAndScripting/ProgrammingWithCPP/UnrealArchitecture/VersioningAssetsAndPackages/index.html) にてご確認ください。

#### onlymaterials
マテリアル系のアセットのみを対象にします。プログラマ向けに言うと、UMaterialInterface継承のアセットのみを対象にします。

#### FilterByCollection
[コンテンツブラウザのコレクション機能](https://docs.unrealengine.com/ja/Basics/ContentBrowser/UserGuide/Collections/index.html) で対象を制限することができます。例えばコレクション名がhogehogeの場合は以下のように使用します。再保存系よりもビルド系の方が役に立つ気がします（ライトビルドを定期的に走らせたいレベルアセットを登録したりとか）。

```cpp
-run=resavepackages -FilterByCollection=hogehoge
```
### UResavePackagesCommandlet::InitializeResaveParameters関数 より

#### PACKAGE
対象となるアセットを直接指定するオプションです。このオプションは複数回使用可能です。例えばこんな感じ。

```cpp
-run=resavepackages -package=/Game/Mannequin/Character/Mesh/SK_Mannequin.uasset -package=/Game/Mannequin/Character/Mesh/UE4_Mannequin_Skeleton.uasset
```
#### PACKAGEFOLDER
PACKAGEオプションのフォルダ指定版です。こちらも複数回使用可能です。

#### MAP
こちらレベルアセット限定のものというわけではなく… ```-MAP=Value1+Value2+Value3```という形式で指定できるPACKAGEオプションです。再保存したいアセットが複数個ある場合はPACKAGEオプションよりこちらの方がシンプルになりそうです。ちなみに、このオプションも複数回使用可能です。

```cpp
-run=resavepackages -map=ThirdPersonExampleMap+SK_Mannequin
```

#### FILE
指定のテキストファイルから対象にするアセットを決定するオプションです。例えば以下の内容を持つテキストファイルを用意し、

```cpp
/Game/Mannequin/Character/Mesh/SK_Mannequin.uasset
/Game/Mannequin/Character/Mesh/UE4_Mannequin_Skeleton.uasset
```
以下のように指定して使います。こちらも複数回使用可能です。

```cpp
-run=resavepackages -file=D:\ResaveAssets.txt
```

#### 番外編：コンフィグファイルから指定する方法
<details><summary>詳しくはこちら</summary><div>
PACKAGE、PACKAGEFOLDER、MAP、FILEオプションを **使わなかった** 場合 かつ buildlighting と buildreflectioncaptures のどちらかを使用する場合は、Editor系のiniファイルで対象となるアセットを指定可能です。

```cpp
[CommandletSettings]
+ResavePackages=...
+ResavePackages=...
```

</div></details>

#### SKIPMAPS
レベルアセットを対象外にします。なお、BuildDataは対象外になりません（ちょっと怖い気がする…）。

#### MAPSONLY
レベルアセットのみを対象にします。なお、BuildDataは対象になりません。

#### PROJECTONLY
プロジェクトのアセットのみを対象にします。逆に言うと、エンジンが持つアセットを対象外にします。エンジンが持つアセットもなかなか多いので、このオプションは積極的に活用したいところです

#### SkipDeveloperFolders または NODEV
デベロッパーフォルダを対象外にします。デベロッパーフォルダは個人用のサンドボックス環境なので対象外にできる・すべきケースは多いかと思います。

デベロッパーフォルダについて
https://docs.unrealengine.com/ja/Basics/ContentBrowser/UserGuide/DevelopersFolder/index.html
[【UE4】テストや実験に有用なデベロッパーフォルダについて](https://qiita.com/4_mio_11/items/52fddeb00b598abe5039)

#### OnlyDeveloperFolders
デベロッパーフォルダのみを対象にします。…どういう時に使うんだろう

#### MAXPACKAGESTORESAVE
再保存するアセットの最大数を設定できます。意図せずに全アセットが保存対象になって呆然することを避けるために設定しておくのが良いのかもです。

#### ResaveDirectRefsAndDeps
**オプションにより指定したアセットが1つだけの場合**、そのアセットの参照・被参照アセットを処理対象に含めます。例えば、特定のレベルアセットに関連するアセットのみを処理対象にしたい場合などに便利だと思います。

#### FixupRedirects または FixupRedirectors
リダイレクタを修正するためのオプションです。詳しくは以下の公式ドキュメントにて
https://docs.unrealengine.com/ja/ProductionPipelines/Redirectors/index.html

ちなみに、UE4.16以前では専用のCommandletが用意されていましたが、UE4.17からResavePackagesCommandletの一部になりました。なので、UE4.17以降では以下のコマンドを実行しても機能しません。

```cpp
-run=fixupredirects
```
#### 指定のバージョン以下・以上は対象にしないオプション

- CHECKLICENSEEVER
- CHECKUE4VER
- RESAVEDEPRECATED
- MINVER
- MAXVER

設定・条件が少し複雑なので、こちらに関しては ```UResavePackagesCommandlet::InitializeResaveParameters``` の ```// Check for the min and max versions``` 以下のコードをご確認ください。い、いや、説明が面倒くさいわけではないですよ…？

#### RESAVECLASS
再保存対象のアセットクラスを制限します。**,** 区切りで複数のクラスを指定することもできます。例えばStaticMesh, SkeletalMeshに制限したい場合は以下のように書きます。

```cpp
-run=resavepackages resaveclass=StaticMesh,SkeletalMesh
```

ただし、指定したクラスのアセット以外もロードは行われるのでご注意ください（再保存は行われません）。

#### IncludeChildClasses
RESAVECLASSオプションで指定したクラスの子クラスのアセットも再保存するためのオプションです。そのため、RESAVECLASSを指定しない場合はこのオプションは機能しません

# さいごに
予想以上にオプションが多くて個人的にビックリしました…。今回紹介したオプションを組み合わせることで効率的にResavePackagesCommandletを使うことが出来ます。特にCIツールで使用する際は重要になってくるはずです。是非ご活用ください！

おしまい