README ¶
unity-grpc
gRPC lib ported to support .Net 3.5 (unity 4+) with Tutorial.
If you are interested in gRPC usage in your Unity and you don't want to wait for Unity C# .Net 4.5 support... this is a tutorial for you!
All credits for providing gRPC library goes to https://github.com/neuecc
lib/gRPC
code was copied without single change from here
The idea of this short tutorial came in response to https://github.com/neuecc/MagicOnion/issues/18
Tutorial
Step by step setup of example client and server to show gRPC support for Unity 4+.
Client (Unity with C# .Net 3.5)
- Create new project
- Copy
lib/gRPC
directory to your Assets (you can just drag & drop it in your unity)
From now on, we need to solve all dependency issues to support gRPC lib and protobufs.
- Install UniRx from AssetStore (async lib used by this gRPC lib)
- Window > AssetStore
- Find UniRx - Reactive Extension for Unity > Login > Import
At that point you should be able to build all in Visual Studio (or any IDE else you are using).
Let's now run through some example of gRPC client <-> server communication with the client side in Unity. I will use standard example from grpc repo with server streaming: https://github.com/grpc/grpc/blob/master/examples/protos/hellostreamingworld.proto
We cannot use gRPC generated code for C#, because it uses .Net 4.5+ code, but we can stil generate protobuf messages (including serialization) that will be .Net 3.5 compatible C# data structures.
The cool part is, that you can use exactly the same proto file with service definition. Protobuf will just ignore this, so you can generate gRPC + protobuf messages for server (in my example it will be Golang), and C# protobuf messages for client.
The only manual part is to use gRPC lib's classes provided in lib/gRPC
to call known ServiceName and Method you prepared. This is normally done by gRPC generated code from proto itself, but needed to recreate that manually here. We could write plugin to generate this, but having .Net 4.5 C# support in Unity "soon-ish", not sure if it's worth it.
- Go to example directory.
- Run
bash ./example/protogen_csharp.sh
(I am using windows migw32) to generate protobuf messages for our example.
This scripts is using protoc-3.0.0, which is provided in this repo here. This is standard protoc downloaded from upstream.
You can use protogen_csharp
script to generate your own stuff. However, generated protobufs are already commited into this repo
for convenience.
- Copy
example/csharp-client
to Unity Assets. (you can drag & drop it). This contains:- generated proto messages
- manually written client
- Unity TestGrpcBehaviour that tries to run the service targeting
127.0.0.1:9991
The most important part here is manually written client, so code that normally would be generated:
return this.invoker.AsyncServerStreamingCall<Example.HelloRequest, Example.HelloReply>(
// Your protobufs message and response as T1, T2.
new Method<Example.HelloRequest, Example.HelloReply>(
// MethodType: Unary, ClientStreaming, ServerStreaming or DuplexStreaming (Bidirectional stream).
MethodType.ServerStreaming,
// This is bit manual, but basically get from your .proto service:
// <package name>.<service name>
"example.MultiGreeter",
// <method name after "rpc" in your service>
"sayHello",
// Serialization definition for request.
new ProtoMarshaller<Example.HelloRequest>(Example.HelloRequest.Parser),
// Serialization definition for respone.
new ProtoMarshaller<Example.HelloReply>(Example.HelloReply.Parser)
),
this.host,
new CallOptions(cancellationToken: new GrpcCancellationToken(ctx)),
req
);
You need to manually implement that for each method in your proto service!
For unary calls you can switch this.invoker.AsyncServerStreamingCall
for just this.BlockingUnaryCall
if you don't
want to play with async code. (: If you wish to deal with concurrency just use this.invoker.AsyncUnaryCall
.
After copying the example csharp_client
directory you should immediately see errors like:
Assets/csharp-client/Model/Greeter.cs(6,12): error CS0400: The type or namespace name `Google' could not be found in the global namespace (are you missing an assembly reference?)
This is tricky part, because we need to get protobuf library that will fit with .Net 3.5
I used this great fork https://github.com/emikra/protobuf3-cs. Nuget install command from README.md totally did not work for me (visual 2013), so I ended up building the .dll myself (using visual 2017). I provide what I built here in this repo, so:
- Copy provided dll from protobuf-net35/protobuf3.dll into your unity Assets
There is a risk it will not work you (not sure even if I built for x86 or x64, so you might want to rebuild it from source if needed.
From now you should be able to rebuild the Unity code.
However when you use Unity Free version. When you run the code it will fail with (more fun!):
DllNotFoundException: grpc_csharp_ext
Grpc.Core.Internal.NativeLogRedirector.Redirect (Grpc.Core.Internal.NativeMethods native) (at Assets/gRPC/Core/Internal/NativeLogRedirector.cs:63)
Grpc.Core.Internal.NativeExtension..ctor () (at Assets/gRPC/Core/Internal/NativeExtension.cs:59)
Grpc.Core.Internal.NativeExtension.Get () (at Assets/gRPC/Core/Internal/NativeExtension.cs:77)
Grpc.Core.Internal.NativeMethods.Get () (at Assets/gRPC/Core/Internal/NativeMethods.cs:269)
Grpc.Core.GrpcEnvironment.GrpcNativeInit () (at Assets/gRPC/Core/GrpcEnvironment.cs:314)
Grpc.Core.GrpcEnvironment..ctor () (at Assets/gRPC/Core/GrpcEnvironment.cs:250)
Grpc.Core.GrpcEnvironment.AddRef () (at Assets/gRPC/Core/GrpcEnvironment.cs:117)
Grpc.Core.Channel..ctor (System.String target, Grpc.Core.ChannelCredentials credentials, IEnumerable`1 options) (at Assets/gRPC/Core/Channel.cs:87)
Grpc.Core.Channel..ctor (System.String host, Int32 port, Grpc.Core.ChannelCredentials credentials, IEnumerable`1 options)
Grpc.Core.Channel..ctor (System.String host, Int32 port, Grpc.Core.ChannelCredentials credentials)
Example.Client..ctor (System.String host, Int32 port) (at Assets/csharp-client/GreeterClient.cs:52)
TestGRPCBehaviour.Start () (at Assets/csharp-client/TestGRPCBehaviour.cs:12)
This is because of some native API calls used by gRPC core. (https://github.com/grpc/grpc/issues/905)
The problem with Free Unity is that it does not support unmanaged dlls packed into per platform directories
as we have in lib/gRPC/Native/(...)
. (called "plugins" support)
What we can do, we can copy the dll you need and place it in Unity root (above Assets!). You will also need to copy this dll with you binary everywhere you want to export it on release. You can find more details here: http://ericeastwood.com/blog/17/unity-and-dlls-c-managed-and-c-unmanaged
Once you provide grpc_csharp_ext.dll
there is only last step missing.
Last step for client-side is to pin behaviour to any game object in your scene
- Create new GameObject > AddComponent > Put TestGRPCBehaviour.cs as Script
You can run Unity now, but it will fail... Since there is no server running around (:
RpcException: Status(StatusCode=Unavailable, Detail="Connect Failed")
UniRx.Stubs.<Throw>m__6B (System.Exception ex) (at Assets/Plugins/UniRx/Scripts/Observer.cs:495)
UniRx.Observable+<ToAwaitableEnumerator>c__Iterator10`1[System.Boolean].MoveNext () (at Assets/Plugins/UniRx/Scripts/UnityEngineBridge/Observable.Unity.cs:939)
The client is done, but now all we need to do is to spin up our server to handle gRPC traffic!
Server (Go)
Prepate go enviroment (golang installed, GOPATH exported)
- Follow https://grpc.io/docs/quickstart/go.html
go get github.com/google/protobuf/...
- Run
bash ./example/protogen_go.sh
- Start server using
go run example/go-server/server.go
Ii will listen on 127.0.0.1:9991
- Now let's start our unity again.
You should be able to see logs on both Go server and Unity with gRPC communication!
I hope this tutorial will help to create your own gRPC client (:
Directories ¶
Path | Synopsis |
---|---|
example
|
|
go-server/model/example
Package example is a generated protocol buffer package.
|
Package example is a generated protocol buffer package. |