Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現

Yoshifumi Kawai
January 25, 2020
210

 Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現

Yoshifumi Kawai

January 25, 2020
Tweet

More Decks by Yoshifumi Kawai

Transcript

  1. ⚫ Unity Connected Games ⚫ Photon Engine ⚫ Monobit Engine

    ⚫ DIY - WebSocket + Server App ⚫ DIY - WebSocket + mBaaS ⚫ DIY - TCP(UDP) + Server App ⚫ DIY - TCP(UDP) + Unity Headless
  2. C#の型が通信定義となる単方向/双方向RPC // 自然な書き味で、タイプセーフにRPC(Remote Procedure Call)を実現 // C#のasync/await構文により、非同期通信も自然に見える var client =

    MagicOnionClient.Create<ITestService>(channel); var result = await client.Sum(100, 200); public class TestService : ITestService { public async UnaryResult<int> Sum(int x, int y) { return x + y; } }
  3. C#の型が通信定義となる単方向/双方向RPC // 自然な書き味で、タイプセーフにRPC(Remote Procedure Call)を実現 // C#のasync/await構文により、非同期通信も自然に見える var client =

    MagicOnionClient.Create<ITestService>(channel); var result = await client.Sum(100, 200); public class TestService : ITestService { public async UnaryResult<int> Sum(int x, int y) { return x + y; } } クライアントもサーバーも自 然に繋がっているように見え る(デバッガもサーバー/クラ イアント共有でステップ実行 で繋がって動いていく)
  4. リアルタイム通信のための双方向の型付きRPC public interface IGamingHub : IStreamingHub<IGamingHub, IGamingHub { Task<Player[]> JoinAsync(string

    roomName, string userName, Vec Task LeaveAsync(); Task MoveAsync(Vector3 position, Quaternion rotation); } public interface IGamingHubReceiver { void OnJoin(Player player); void OnLeave(Player player); void OnMove(Player player); }
  5. il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Call, typeof(AsyncDuplexStreamingCall<byte[], byte[]>).GetProperty("RequestStream").GetMethod); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, serializerOptionsField); il.Emit(OpCodes.Newobj, (typeof(MarshallingClientStreamWriter<>).MakeGenericType(def.RequestType).GetConstructors().Singl

    il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Call, typeof(AsyncDuplexStreamingCall<byte[], byte[]>).GetProperty("ResponseStream").GetMethod); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, serializerOptionsField); il.Emit(OpCodes.Newobj, (typeof(MarshallingAsyncStreamReader<>).MakeGenericType(def.ResponseType).GetConstructors().Singl il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, serializerOptionsField); resultType2 = typeof(DuplexStreamingResult<,>).MakeGenericType(def.RequestType, def.ResponseType); il.Emit(OpCodes.Newobj, resultType2.GetConstructors()[0]); MethodType t; string requestType; string responseType; ITypeSymbol unwrappedOriginalResponseType; ExtractRequestResponseType(y, out t, out requestType, ou var id = FNV1A32.GetHashCode(y.Name); return new MethodDefinition { Name = y.Name, MethodType = t, RequestType = requestType, ResponseType = responseType, UnwrappedOriginalResposneTypeSymbol = unwrappedOrigi OriginalResponseTypeSymbol = y.ReturnType, IsIfDebug = y.GetAttributes().FindAttributeShortName HubId = id, Parameters = y.Parameters.Select(p => {
  6. 言語の違うREST Response型を別々 に書く APIクライアント を手書きする (ザ・マイクロ サービスみたいな 構成) 中間IDLを書く そこからクライア

    ント・レスポンス 型自動生成 (←を嫌う時によ くある構成、一番 メジャー) サービスを普通に 書く、そこからク ライアントを自動 生成、リクエス ト・レスポンス型 はC#のDLLとして 共有 サービスを普通に 書く、クライアン トはそのプロジェ クト参照から実行 時動的生成
  7. ConsoleAppFramework https://github.com/Cysharp/ConsoleAppFramework 引数の自動割当 複数コマンドのルーティング コンフィグ処理 ロギング処理 ライフサイクル管理(Daemon) などCLIの面倒ごとに対応 class Program

    : ConsoleAppBas { static async Task Main(string[] args) { await Host.CreateDefaultBuilder() .RunConsoleAppFrameworkAsync<Program>(args); } public void Run(string name, int repeat = 3) { for (int i = 0; i < repeat; i++) { Console.WriteLine($"Hello from {name}"); } } }