7.0 の新機能
全般
StreamingHub メソッドの戻り値として void
をサポート
StreamingHub メソッドの戻り値として void
をサポートしました。これはサーバーからの戻り値を必要としない Fire-and-Forget パターンで使用できます。
クライアント結果
StreamingHub でレシーバーのメソッド呼び出しを待機し、クライアントでの処理結果をサーバーで受け取れる仕組みを追加しました。詳細については クライアント結果 を参照してください。
StreamingHub の組み込みハートビート
StreamingHub で組み込みのハートビート機能を追加しました。詳細については ハートビート を参照してください。
Upgrade to MessagePack v3
MessagePack が v3 にアップグレードされました。これにより Unity や AOT 環境での MessagePack Generator (mpc
) の使用が不要になります。詳しくは MessagePack-CSharp のリリース を参照してください。
サーバー
StreamingHub のグループのバックエンド実装の刷新
StreamingHub のグループのバックエンドが Multicaster ベースに刷新され、より柔軟なグループ操作が可能となりました。これに伴いグループに関する API が変更となりました。
- InMemoryStorage の削除
- IGroup インターフェースの API 変更
- グループのレシーバーの取得 API の変更
All
,Single
,Only
,Except
のメソッドの追加- StreamingHubBase の
Broadcast
系メソッド および Group のCreateBroadcaster
系のメソッドの削除
- グループのレシーバーの取得 API の変更
詳しくは グループ および アプリケーション管理のグループ を参照してください。また v6 からの移行に関しては 移行ガイド も参照してください。
MagicOnion.Server.JsonTranscoding の導入と MagicOnion.Server.HttpGateway の削除
Swagger サポートのための MagicOnion.Server.HttpGateway は削除されました。代わりに MagicOnion.Server.JsonTranscoding が導入され、JSON でのリクエスト/レスポンスのエンコード/デコードを行う MagicOnion.Server.JsonTranscoding とその Swagger サポートが追加されました。詳しくは JSON トランスコーディング を参照してください。
その他の改善
- .NET 9 をサポートしました
AddLogging
とAddGrpc
を暗黙的に呼び出すようにしました
クライアント
WaitForDisconnectAsync API の追加
StreamingHub クライアントに WaitForDisconnectAsync
メソッドが追加されました。このメソッドは以前の WaitForDisconnect
と同様に切断を待機しますが、切断時に理由を含めて返すようになりました。これによりハートビートによるタイムアウトであるといった切断理由を取得できるようになりました。詳しくは 切断 を参照してください。
その他の改善
- クライアントライブラリーでのトリミングの警告を有効にしました
- StreamingHubClient でのアロケーションを削減しました
破壊的変更
サーバー
.NET 7 のサポートを削除しました
.NET 7 はランタイムのサポートが終了したことを受けサポートされなくなりました。.NET 8 以上を使用してください。
StreamingHubContext の再利用するようになりました
StreamingHubContext は再利用されるようになったため、Hub メソッドの呼び出しの外で StreamingHubContext をキャッシュすると予期せぬ挙動となる可能性があります。コンテキストは呼び出し中にのみ有効です。
gRPC メソッドのバインディングロジックを再設計しました
サーバーに MagicOnion のメソッドを gRPC メソッドとして登録する仕組みを再設計しました。これにより MagicOnion の Unary/SteramingHub サービスを登録するタイミングがアプリケーションのビルドではなく、リクエストパイプライン/ルート構築時に変更となりました。
以前は AddMagicOnion
で登録していた Unary/StreamingHub サービスは MapMagicOnionService
で登録するよう変更する必要があります
クライアント
C-Core gRPC ライブラリーのサポートを削除しました
Unity での C-core gRPC ライブラリーのサポートを削除しました。代わりに grpc-dotnet と YetAnotherHttpHandler を使用してください。詳しくは Unity での利用 を参照してください。
StreamingHub クライアントが切断された後、ObjectDisposedException ではなく RpcException をスローするようになりました
以前のバージョンでは StreamingHub から切断された後、クライアントでメソッド呼び出しを行うと ObjectDisposedException がスローされていましたが、 RpcException がスローされるようになりました。一般的なクラスと同様に Dispose されたときのみ ObjectDisposedException がスローされます。
v6 からの移行ガイド
Unity クライアントの移行
一般的な .NET アプリケーションと異なり、Unity クライアントのアップグレードでは空の Unity プロジェクトで MagicOnion をインストールしてコピーする必要があります。
これは Unity プロジェクトにすでに Unity パッケージマネージャー経由で MagicOnion や MessagePack がインストールされている状態で NuGet パッケージをインストールできず、それらの単純にライブラリーを削除した場合でもビルドエラーとなりパッケージがある状態が維持されてしまうためです。
作業用の空の Unity プロジェクトを作成する
- NuGetForUnity をインストールする
- NuGetForUnity で MagicOnion.Client をインストールする
アップグレード対象のプロジェクト
- NuGetForUnity をインストールする
- MagicOnion, MessagePack を削除する
作業用のプロジェクトにインストールされた NuGet パッケージ一式をアップグレード対象プロジェクトにコピーする
- Assets 以下の NuGet.config と packages.config もコピーまたはマージする
Packages/manifest.json を書き換える
"com.cysharp.magiconion": "https://github.com/Cysharp/MagicOnion.git?path=/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion#{Version}",
上記の行を下記の通り書き換えます。{Version}
はインストールするバージョンに合わせて変更してください。
"com.cysharp.magiconion.client.unity": "https://github.com/Cysharp/MagicOnion.git?path=/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion.Client.Unity#{Version}",
サーバー API: StreamingHub
StreamingHub のグループ機能が大きく変更となったため、コードレベルでの API 互換性がなくなっています。このため、v6 から v7 に移行する際には v7 での使用方法に変更するか、互換 API shim を使用する必要があります。
互換 API shim
StreamingHub を使用している場合、v7 に移行する際に以下の互換 API shim を使用することで段階的な移行を可能にします。ただし、この互換 shim にはグループの InMemoryStorage の機能は含まれていないため、それらの機能を使用している場合には別途対応が必要となります。
using Cysharp.Runtime.Multicast;
namespace MagicOnion.Server.Hubs;
public abstract class StreamingHubBaseCompat<THub, TReceiver> : StreamingHubBase<THub, TReceiver>
where THub : IStreamingHub<THub, TReceiver>
{
protected TReceiver Broadcast(IGroup group) => ((GroupCompat<TReceiver>)group).Inner.All;
protected TReceiver BroadcastExceptSelf(IGroup group) => ((GroupCompat<TReceiver>)group).Inner.Except(ConnectionId);
protected TReceiver BroadcastExcept(IGroup group, Guid except) => ((GroupCompat<TReceiver>)group).Inner.Except(except);
protected TReceiver BroadcastExcept(IGroup group, IReadOnlyList<Guid> excepts) => ((GroupCompat<TReceiver>)group).Inner.Except(excepts);
protected TReceiver BroadcastToSelf(IGroup group) => ((GroupCompat<TReceiver>)group).Inner.Single(ConnectionId);
protected TReceiver BroadcastTo(IGroup group, Guid toConnectionId) => ((GroupCompat<TReceiver>)group).Inner.Single(toConnectionId);
protected TReceiver BroadcastTo(IGroup group, IReadOnlyList<Guid> toConnectionIds) => ((GroupCompat<TReceiver>)group).Inner.Only(toConnectionIds);
protected new HubGroupRepository Group { get; }
protected StreamingHubBaseCompat()
{
Group = new HubGroupRepositoryCompat<TReceiver>(base.Group);
}
class HubGroupRepositoryCompat<T> : HubGroupRepository
{
readonly HubGroupRepository<T> groupRepo;
public HubGroupRepositoryCompat(HubGroupRepository<T> groupRepo)
{
this.groupRepo = groupRepo;
}
public override async ValueTask<IGroup> AddAsync(string name)
=> new GroupCompat<T>(await groupRepo.AddAsync(name));
}
class GroupCompat<T> : IGroup
{
public IGroup<T> Inner { get; }
public GroupCompat(IGroup<T> inner)
{
this.Inner = inner;
}
public ValueTask RemoveAsync(ServiceContext serviceContext)
=> this.Inner.RemoveAsync(serviceContext);
public T1 CreateBroadcaster<T1>() => (T1)(object)Inner.All!;
public T1 CreateBroadcasterTo<T1>(Guid toConnectionId) => (T1)(object)Inner.Single(toConnectionId)!;
public T1 CreateBroadcasterTo<T1>(IReadOnlyList<Guid> toConnectionIds) => (T1)(object)Inner.Only(toConnectionIds)!;
public T1 CreateBroadcasterExcept<T1>(Guid toConnectionId) => (T1)(object)Inner.Except(toConnectionId)!;
public T1 CreateBroadcasterExcept<T1>(IReadOnlyList<Guid> toConnectionIds) => (T1)(object)Inner.Except(toConnectionIds)!;
}
}
public interface IGroup
{
ValueTask RemoveAsync(ServiceContext serviceContext);
T CreateBroadcaster<T>();
T CreateBroadcasterTo<T>(Guid toConnectionId);
T CreateBroadcasterTo<T>(IReadOnlyList<Guid> toConnectionIds);
T CreateBroadcasterExcept<T>(Guid toConnectionId);
T CreateBroadcasterExcept<T>(IReadOnlyList<Guid> toConnectionIds);
}
public abstract class HubGroupRepository
{
public abstract ValueTask<IGroup> AddAsync(string name);
}