CodeGeneratorをMacで使えるようにする。MessagePack for C# [Unity]

## 環境
- [MessagePack for C# v2.1.90](https://github.com/neuecc/MessagePack-CSharp/releases/tag/v2.1.90)
- Unity 2018.4.14f1
- macOS Mojave 10.14.6

##MacでCodeGeneratorが使えない。。。
`Windows -> MessagePack -> CodeGenerator`
<img width="356" alt="スクリーンショット 2020-02-27 22.08.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389614/010484f5-c932-649c-e667-b3f5ab4e4c53.png">

Macでは`CodeGenerator`が上記画面から進みません。
それを解決します。

**今回の方法は複数人での開発に向きません。
複数人で開発する場合はCodeGeneratorの担当者を一人決める必要があります。他に方法が見つかるか、またはOfficialで修正があればこの記事を書き直します。**

## 基礎知識
####AOT Code Generationとは？
MessagePack for C#をIL2CPP上で動作されるには中間コードが必要になります。それを出力するのが[AOT Code Generation](https://github.com/neuecc/MessagePack-CSharp#aot)です。iOS/AndroidアプリをリリースするにはIL2CPPで出力する必要があるので必須となります。

####CodeGeneratorとは？
![スクリーンショット 2020-02-28 1.18.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389614/dbc5840c-912a-d8b0-0dfe-c74d4925b01a.png)

Unity上から「Generate」ボタンをクリックするだけで中間コードを出力してくれる便利機能です。

##必要なアプリがインストール済みかチェックする
ターミナルを開き、下記手順で進めます。
#### 1 .NET Core 3 Runtime
```
dotnet --version
```
ターミナルに上記を入力しEnterを押すと`3.1.102`などのバージョンが表示されます。表示されなければ未インストールなのでのインストールしてください。

[.NET Core 3 Runtimのダウンロード](https://dotnet.microsoft.com/download)

#### 2 MessagePackCompiler(mpc)
```
dotnet tool list -g
```
`messagePack.generator`が未インストールであればターミナルで下記を実行しインストールしてください。`messagePack.generator`の文言が表示されなければ未インストールです。

```
dotnet tool install --global messagePack.generator
```

## 必要なアプリのパスを取得
#### 1 .NET Core 3 Runtime
```
type dotnet
```
ターミナルに上記を入力しEnterを押すと`dotnet is hashed (/usr/local/share/dotnet/dotnet)`の形で出力されます。()内のパスを後ほど使用します。

#### 2 MessagePackCompiler(mpc)
```
type dotnet-mpc
```
同様に()内のパスを後ほど使用します。

##コードを一部書き換える
書き換えてしまうのでWindowsで動作しなくなります。もしWindowsでも使う予定があれば[ディレクティブ](https://docs.unity3d.com/ja/2018.4/Manual/PlatformDependentCompilation.html)など使って工夫してください。違うMacでもパスが代わるので対応が必要になります。

1 `Assets/Scripts/MessagePack/Unity/MessagePackWindow.cs`を開きます。
2 `InvokeProcessStartAsync`の第一引数`dotnet`をすべて上記で取得したパスに修正します。例)

```C#
var list = await InvokeProcessStartAsync("/usr/local/share/dotnet/dotnet", "tool list -g");
```
3 153行目 `ProcessHelper.InvokeProcessStartAsync`の第一引数`dotnet-mpc`を上記で取得したパスに修正します。例)

```C#
var log = await ProcessHelper.InvokeProcessStartAsync("/Users/account/.dotnet/tools/dotnet-mpc", "mpc " + commnadLineArguments);
```

以上です！
CodeGeneratorを開くと下記のような画面が表示されれば成功です。
もし表示されなければひとつずつ見直してみてください。

## CodeGeneratorの設定
`Windows -> MessagePack -> CodeGenerator`
![スクリーンショット 2020-02-28 1.18.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389614/dbc5840c-912a-d8b0-0dfe-c74d4925b01a.png)

#### [セーブデータが含まれるcsprojの場所]
-i input path(csproj or directory): 

```
../Assembly-CSharp.csproj
```

[Assembly Definition](https://docs.unity3d.com/ja/2018.4/Manual/ScriptCompilationAssemblyDefinitionFiles.html)を利用していて、Auto ReferencedをOffにしている方は正しいcsprojを指定してください。

#### [中間コード出力先]
-o output filepath(.cs) or directory(multiple): 

```
Scripts/MessagePackGenerated
```
拡張子`.cs`を追加するとひとつのファイルにすべての中間コードをまとめてくれます(多分)。
逆に下記のように`.cs`を含めなければクラス別に出力+その出力されたクラスを参照するファイルが出力されます。

## 中間コードの生成
「Generate」ボタンを押すことで生成されます。
※生成されたコードが表示されない場合は、Unity Editor以外の画面をクリック -> Unity Editorに戻ることで表示されます。

## 実装

最後に生成した`GeneratedResolver.Instance`を登録して使えるようにして完了です。
必ず初めに初期化される必要があるようです。リファレンスでは、`RuntimeInitializeOnLoadMethod`を使って初期化してますが、プラグインの利用があってもなくても初期化が走るので、セーブデータにアクセスする前に初期化を済ませられればどのような実装でも良いと思います。

コードの参照元 : [AOT Code Generation](https://github.com/neuecc/MessagePack-CSharp#aot)

```C#
using MessagePack;
using MessagePack.Resolvers;
using UnityEngine;

public class Startup
{
    static bool serializerRegistered = false;

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    static void Initialize()
    {
        if (!serializerRegistered)
        {
            StaticCompositeResolver.Instance.Register(
                 MessagePack.Resolvers.GeneratedResolver.Instance,
                 MessagePack.Resolvers.StandardResolver.Instance
            );

            var option = MessagePackSerializerOptions.Standard.WithResolver(StaticCompositeResolver.Instance);

            MessagePackSerializer.DefaultOptions = option;
            serializerRegistered = true;
        }
    }

#if UNITY_EDITOR


    [UnityEditor.InitializeOnLoadMethod]
    static void EditorInitialize()
    {
        Initialize();
    }

#endif
}
```

##最後に
[MessagePack for C#](https://github.com/neuecc/MessagePack-CSharp)いいですよね！
製作者のCysharp, Inc. CEO/CTOの[河合 宜文さん](https://twitter.com/neuecc)に日々感謝です。
いつかお逢いしたいものです。
多分緊張しすぎてしゃべれないと思いますが