MessagePackってmacで使えないんでしょ? やだー!
MessagePack-CSharp
存在はけっこう前から知っていたのですが、Unity
のIL2CPP
環境下だと事前コードジェネレート1が必要で、それがwindows環境でしか動かない……とのことだったので敬遠していました。
MessagePack for C# v2によるC#における最新のI/Oパイプライン最適化
ところがv2が出てなんかmacでも動くようになった? っぽい? ということなので試してみました。
実際動いたやり方はこことここに書いてあります。
おま環疑惑がだいぶ濃厚なので、そこのところを念頭に置いてお読みください。
事前説明
MessagePackのバージョンは以下のとおりです。
2.0.335
データクラス
このデータクラスをSerialize/Deserialize
するのが今回の目標です。
こんな感じのディレクトリに配置してあります。
using MessagePack;
using UnityEngine;
[MessagePackObject]
public class LineData
{
[Key(0)]
public int Index;
[Key(1)]
public string ID;
[Key(2)]
public Vector3[] Pos;
[Key(3)]
public Quaternion[] Rot;
public override string ToString()
{
return $"{nameof(Index)}: {Index}, {nameof(ID)}: {ID}, {nameof(Pos)}: {Pos}, {nameof(Rot)}: {Rot}";
}
}
テストコード
[SerializeField]
private Text _text;
private void Start()
{
StaticCompositeResolver.Instance.Register(
StandardResolver.Instance,
UnityBlitResolver.Instance,
GeneratedResolver.Instance // 今回事前生成しようとしているResolver
);
var option = MessagePackSerializerOptions.Standard.WithResolver(StaticCompositeResolver.Instance);
MessagePackSerializer.DefaultOptions = option;
var before = new LineData();
before.ID = Guid.NewGuid().ToString();
before.Index = 1;
before.Pos = Enumerable.Range(0, 10).Select(i => UnityEngine.Random.insideUnitSphere).ToArray();
before.Rot = Enumerable.Range(0, 10).Select(i => UnityEngine.Random.rotation).ToArray();
Debug.Log($"before = {before.ToString()}");
var binary = MessagePackSerializer.Serialize(before);
var after = MessagePackSerializer.Deserialize<LineData>(binary);
Debug.Log($"after = {after.ToString()}");
_text.text = before.ToString() + Environment.NewLine + after.ToString();
}
事前生成なしでもエディタでは動きますが、Android
とかにビルドすると動きません。
みちすじ
初回起動
Window -> MessagePack -> CodeGenerator
dotnet --version
3.1.101
入ってます。というわけでもう一回起動。
ねえぞ! って言われます。
ある!!!!!!
Assets/Scripts/MessagePack/Unity/MessagePackWindow.cs
メッセージを生成しているのはこのスクリプトです。
ねえぞ! って言ってきてるのはこのあたり。
public static async Task<(bool found, string version)> FindDotnetAsync()
{
try
{
var version = await InvokeProcessStartAsync("dotnet", "--version");
return (true, version);
}
catch
{
return (false, null);
}
}
InvokeProcessStartAsync
の中身を見る限り、普通にTerminal
でdotnet --version
と入力したときと同じ結果を返してくる……はず。再起動してみても変化はありません。
ProcessStartInfo
のFileName
とかWorkingDirectory
とかがうまくいってないっぽいですが、シェルにぜんぜん詳しくないのでわかりません。
気分転換
いったんUnityを離れてリポジトリのReadMeの手順を進めます。MessagePack.Generator
をインストール。
dotnet tool install --global MessagePack.Generator
入ってます。でなんかdotnet toolsをPATHに追加しろとか言ってくるので素直に表示されるコマンドをコピー→実行していきます。今回もTerminal
から存在チェック。
dotnet-mpc
argument list:
-i, -input: Input path of analyze csproj or directory, if input multiple csproj split with ','.
-o, -output: Output file path(.cs) or directory(multiple generate file).
-c, -conditionalSymbol: [default=null]Conditional compiler symbols, split with ','.
-r, -resolverName: [default=GeneratedResolver]Set resolver name.
-n, -namespace: [default=MessagePack]Set namespace root name.
-m, -useMapMode: [default=False]Force use map mode serialization.
-ms, -multipleIfDirectiveOutputSymbols: [default=null]Generate #if-- files by symbols, split with ','.
います。でもどうせねえぞって言われるんでしょ。知ってる。
コマンドラインで実行してみる
/Users/XXXXXX/.dotnet/tools/dotnet-mpc
実態っぽいのはここのパスにあります。なのでAssets直下に移動して次のコマンドを打ってみます。
/Users/XXXXXX/.dotnet/tools/dotnet-mpc -i Scripts/Entity -o Scripts/Entity/Generated/MessagePackGenerated.cs
こんな感じで生成されました。
MessagePackGenerated.cs
にはusing UnityEngine
が足りなくてエラーが出ていますが、それさえ補填してあげればビルドは通ります。
ビルドすると実機でも動作します。
エディタ拡張
こんな感じのメソッドを作ってみました。とりあえず自分の環境では動いてます。
既存のエディタ拡張をいろいろコメントアウトするとか、必要なとこだけコピペして新しく作るとかしてこのメソッドに食わせれば動く……はず。
using UnityEngine
が足りなくてエラー出てるのは相変わらずなのでこわいですが。
public static Task<string> GenerateResolverAsync(MpcArgument argument)
{
var psi = new ProcessStartInfo()
{
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
FileName = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), ".dotnet/tools/dotnet-mpc"),
Arguments = argument.ToString(),
WorkingDirectory = Application.dataPath
};
Process p;
try
{
p = Process.Start(psi);
}
catch (Exception ex)
{
return Task.FromException<string>(ex);
}
var tcs = new TaskCompletionSource<string>();
p.EnableRaisingEvents = true;
p.Exited += (object sender, System.EventArgs e) =>
{
var data = p.StandardOutput.ReadToEnd();
p.Dispose();
p = null;
tcs.TrySetResult(data);
};
return tcs.Task;
}
まとめ
macでもなんとかならないことはないっぽい。です。
windowsでは試していませんが、あっさり動くのではないでしょうか。
ぶっちゃけSerialize/Deserialize
するクラスを頻繁に変更することってそうないと思うので、必要なときだけwindowsで生成してgitで共有してもらったほうが早いと思います。
ちなみにOSアップデートしてない、まだbash
のmacでも同様でした。とはいえどちらも自分が使っているやつなので、他の人が使ってるmacだと動くかも。
あと、ぱっと見た感じIssuesに似たような問題は上がってませんでした。おま環なのでは疑惑。
おしまい。
参考
MessagePack-Csharp と MagicOnion のコード生成を自動化する
-
コンパイル時にコードを生成しておく、のが大好きです。もっと言うなら通信とかするなら
ProtoBuf
みたいにお約束を作っておいたほうがみんなしあわせなのでは? くらいに思っています。でも.proto
書くのめんどくせえ! ↩