Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unity support #1309

Open
ChristopheBougere opened this issue May 28, 2021 · 50 comments
Open

Unity support #1309

ChristopheBougere opened this issue May 28, 2021 · 50 comments
Labels
question Further information is requested
Milestone

Comments

@ChristopheBougere
Copy link

I know that Unity support isn't planned for grpc-dotnet (#710 and #1155).
However, since Grpc.Core is going to be deprecated (see The future of gRPC in C# belongs to grpc-dotnet), what is the plan for Unity users? I haven't seen anything related in the blog post.
I know that Unity support was experimental, but are you 100% dropping it?

@ChristopheBougere ChristopheBougere added the question Further information is requested label May 28, 2021
@JamesNK
Copy link
Member

JamesNK commented May 28, 2021

Grpc.Net.Client on Unity requires Unity to add support for HTTP/2. If HTTP/2 and netstandard2.1 are supported, then Grpc.Net.Client will start working without any changes.

I want this to happen and I asked the Unity team about it here.

Basically, the work required is in Unity. You should tell them that gRPC is important to you so they prioritize it. I don't know the best way to do that. Comment on Unity Future .NET Development Status?

@JamesNK JamesNK added this to the Discussion milestone May 28, 2021
@ChristopheBougere
Copy link
Author

Thanks for the clear and detailed answer @JamesNK 🙏
I didn't know that .NET 6 support was in Unity roadmap (with a preview planned for the fall of 2021).
So if everything goes well, at the time Grpc.Core becomes deprecated, grpc-dotnet will be supported in Unity.
I'll let Unity knows that it is a high priority for us, thanks!

@jayd16
Copy link

jayd16 commented Aug 6, 2021

Unity has released beta support for netstandard2.1 in Unity 2021.2.0b6. It does appear to have http/2 support.

@JamesNK
Copy link
Member

JamesNK commented Aug 6, 2021

If anyone wants to try out Grpc.Net.Client on Unity, please go ahead and report back whether it works or not. I'm not a Unity developer and don't have an environment setup.

@jayd16
Copy link

jayd16 commented Aug 8, 2021

I made a quick attempt. While I can get the project to compile, it seems that the SendAsync call simply hangs without error. The logs happily print Message sent. from Grpc.Net.Client.Internal.GrpcCallLog:MessageSent even when the server is unreachable. As far as I can tell no actually connection is attempted.

It appears that the HttpClient in Unity's implementation does not actually support Http/2. For normal restful calls, you can reference and pass HttpVersion.Version20 but it seems that the response has version 1.1. I'm not sure if this is intended or a bug on Unity's end.

@jayd16
Copy link

jayd16 commented Aug 16, 2021

@Blackclaws
Copy link
Contributor

So Grpc.Net.Client.Web almost works. There is an issue with the referenced version of System.Buffers that makes it fail during runtime. But a rebuild linking to the correct assemblies should fix that issue as well. If I manage to build it I'll let you know.

@plucked
Copy link

plucked commented Nov 21, 2021

So Grpc.Net.Client.Web almost works. There is an issue with the referenced version of System.Buffers that makes it fail during runtime. But a rebuild linking to the correct assemblies should fix that issue as well. If I manage to build it I'll let you know.

I looked into it in regards of the gRPC.net.client lib. It seems that unity adds their own version of system.memory which is way too old and you can not overwrite it.

@Blackclaws
Copy link
Contributor

Blackclaws commented Nov 22, 2021

Right, so it does work at least in 2021 and up (haven't tried the older versions yet). The issue is you have to actually compile it IN unity, i.e. add it to your project. You also have to change a couple of files due to Unity's different style of includes. It should be possible to get it to compile against Unity's libraries without having to actually add it to your project each time somehow.

I've verified that the client works with simple message formats at least using the generated source code from a different project via Grpc.Tools. Can't say it works for everything and also no guarantees about whether or not there might be hidden bugs.

The main reason why you can't use the assemblies as is from nuget is because as others have stated the versions of System.Memory and other assemblies does differ, if you add them to the project as well you run into naming conflicts within Unity itself if you try to use any type from them.

@Blackclaws
Copy link
Contributor

Blackclaws commented Nov 22, 2021

Alright, its actually even much simpler. You can use all the libraries targeting netstandard2.1 by default, the Grpc.Core.Api needs to be recompiled for netstandard2.1 instead of 2.0 else it won't link in Unity (the System.Memory issue). You don't even need to change any files that way.

If you're using Google.Protobuf (which I guess a lot of you will be) you also need to recompile that for netstandard 2.1 (or include it in the project). You need to be careful there that it actually has a dependency set per target framework in the csproj:

  <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
    <PackageReference Include="System.Memory" Version="4.5.3" />
    <!-- Needed for netcoreapp3.1 to work correctly. .NET is not able to load the assembly without this -->
    <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.2" />
  </ItemGroup>

where you need to add:

  <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' ">
    <PackageReference Include="System.Memory" Version="4.5.3" />
    <!-- Needed for netcoreapp3.1 to work correctly. .NET is not able to load the assembly without this -->
    <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
  </ItemGroup>

do note the Version 6.0.0 for System.Runtime.CompilerServices.Unsafe as that is the version that Unity apparently ships with.

So in total you're going to need the following dlls for Grpc Web to work in Unity:

  • Grpc.Core.Api.dll (recompiled for netstandard 2.1)
  • Grpc.Net.Client.dll (can be copied as a netstandard 2.1 version)
  • Grpc.Net.Client.Web.dll (can be copied as well)
  • Grpc.Net.Common.dll (same)
  • System.Diagnostics.DiagnosticSource.dll (can be copied as is from the nuget package, I've used the netstandard 1.3 version)
  • Microsoft.Extensions.Logging.Abstractions.dll (can also be copied from the nuget package, netstandard 2.0 package works fine here)

optionally:

  • Google.Protobuf.dll (recompiled for netstandard 2.1)

You can then use any code generated clients in Unity. I have not tested server mode in any way and I don't think there is an easy way to get it to work.

For code generated clients you can either add the source or just add any library that you built yourself targeting netstandard2.1.

Maybe in the future Grpc will work without the Web intermediate form, but for now Grpc Web is as good as its going to get and its still faster than REST.

@bryding
Copy link

bryding commented Nov 23, 2021

This is insanely useful but I seem to have one lingering issue. Here's my code:

using Grpc.Core;
using Grpc.Net.Client;
using UnityEngine;

namespace Assets.src.Networking
{

  class GameRunner : MonoBehaviour
  {
    void Start()
    {
      var option = new GrpcChannelOptions {Credentials = ChannelCredentials.Insecure};
      var channel = new GrpcChannel("127.0.0.1:9091", option);
      var client = new GameService.GameServiceClient(channel);
      var reply = client.getMatchResult(new GetMatchResultRequest());
      Debug.Log($"Got reply: {reply.AllMonsters[0].MonsterName}");
    }
  }
}

Error is:
error CS1729: 'GrpcChannel' does not contain a constructor that takes 2 arguments

I have all the dll's in the project. It seems to have no problems compiling or finding all the relevant grpc code. I can replace that line with this instead and it compiles fine:

var channel = GrpcChannel.ForAddress("http://127.0.0.1:9091");

I can see both of these bits of code in the Grpc.Net.Client dll in the project, so I'm at a loss for why the first doesn't compile.

Any ideas? I suspect I've gone wrong somewhere since I pretty just blindly added all the dll's to the unity project.

EDIT: The documentation/tutorials are just out of date. Looking at the source code they made that constructor internal. So the correct usage is the factory method ForAddress like I had changed it to. Ignore me.

@bryding
Copy link

bryding commented Nov 23, 2021

Worth pointing out I also got this working simply by using the nuget package for unity. It downloaded the dll's correctly. Except for System.Runtime.CompilerServices.Unsafe which I got the dll for manually in the nuget package and added it to my Unity project.

https://github.com/GlitchEnzo/NuGetForUnity

I'm on Unity 2021.2.3.

@JamesNK
Copy link
Member

JamesNK commented Nov 23, 2021

This seems like a Unity issue to me.

If Unity supports .NET Standard 2.1 then it should support be able to use .NET Standard 2.0 targetted packages like Grpc.Core.Api and Google.Protobuf.

Unity should fix itself so it is ok with running a package that is referencing a package that is using an older version of System.Memory, System.Buffers, System.Runtime.CompilerServices.Unsafe, or whatever.

@Blackclaws
Copy link
Contributor

Blackclaws commented Nov 23, 2021

@bryding That's an internal constructor. You cannot access it.

@Blackclaws
Copy link
Contributor

Blackclaws commented Nov 23, 2021

Worth pointing out I also got this working simply by using the nuget package for unity. It downloaded the dll's correctly. Except for System.Runtime.CompilerServices.Unsafe which I got the dll for manually in the nuget package and added it to my Unity project.

https://github.com/GlitchEnzo/NuGetForUnity

I'm on Unity 2021.2.3.

Interesting I tried the same thing on Unity 2021.2.0 and 2021.1 and it didn't work. Seems they are making progress regarding their compatbility. Google.Protobuf still won't load unfortunately due to the Unsafe part. This means however that you won't have to recompile any of the other libraries for netstandard2.1 if you are using 2021.2.3 and up it seems.

@JamesNK You are of course correct that in a perfect world that is exactly what they should be doing. The reality is however that Unity has some idiosyncratic behaviour when it comes to assemblies. This is just some help how to work around that until it works out of the box :)

@bryding
Copy link

bryding commented Nov 24, 2021

I suppose I spoke too soon. Everything compiles, but I can't get a response back.

I get this error:

RpcException: Status(StatusCode="Internal", Detail="Error starting gRPC call. HttpRequestException: An error occurred while sending the request WebException: Error getting response stream (ReadDoneAsync2): ReceiveFailure", DebugException="System.Net.Http.HttpRequestException: An error occurred while sending the request ---> System.Net.WebException: Error getting response stream (ReadDoneAsync2): ReceiveFailure
  at System.Net.WebResponseStream.InitReadAsync (System.Threading.CancellationToken cancellationToken) [0x000f3] in <c7c32602e34d422f9c1a5e3ed52782b2>:0 
  at System.Net.WebOperation.Run () [0x001d9] in <c7c32602e34d422f9c1a5e3ed52782b2>:0 
  at System.Net.WebCompletionSource`1[T].WaitForCompletion () [0x00094] in <c7c32602e34d422f9c1a5e3ed52782b2>:0 
  at System.Net.HttpWebRequest.RunWithTimeoutWorker[T] (System.Threading.Tasks.Task`1[TResult] workerTask, System.Int32 timeout, System.Action abort, System.Func`1[TResult] aborted, System.Threading.CancellationTokenSource cts) [0x000f8] in <c7c32602e34d422f9c1a5e3ed52782b2>:0 
  at System.Net.HttpWebRequest.EndGetResponse (System.IAsyncResult asyncResult) [0x00020] in <c7c32602e34d422f9c1a5e3ed52782b2>:0 
  at System.Threading.Tasks.TaskFactory`1[TResult].FromAsyncCoreLogic (System.IAsyncResult iar, System.Func`2[T,TResult] endFunction, System.Action`1[T] endAction, System.Threading.Tasks.Task`1[TResult] promise, System.Boolean requiresSynchronization) [0x0000f] in <00c558282d074245ab3496e2d108079b>:0 

There's very few instances of this exact error when I google it. My server and client code are pretty bare bones right now:

Server:

      ServerServiceDefinition gameServiceService = GameService.BindService(new GameServerImpl());

      Server server = new Server
      {
        Services = {gameServiceService},
        Ports = {new ServerPort("localhost", Port, ServerCredentials.Insecure)},
      };
      server.Start();

      Console.WriteLine("Server listening on port " + Port);
      Console.WriteLine("Press any key to stop the server...");
      Console.ReadKey();

      server.ShutdownAsync().Wait();

Unity Client Code:

      var option = new GrpcChannelOptions
      {
        Credentials = ChannelCredentials.Insecure,
      };
      var channel = GrpcChannel.ForAddress("http://127.0.0.1:9091", option);
      var client = new GameService.GameServiceClient(channel);
      FullMonsterStateResponse reply = client.getMatchResult(new GetMatchResultRequest());
      Debug.Log(reply);

Error only shows up if I add that debug.log statement or otherwise try to interact with the response object at all.

@Blackclaws
Copy link
Contributor

I suppose I spoke too soon. Everything compiles, but I can't get a response back.

I get this error:

RpcException: Status(StatusCode="Internal", Detail="Error starting gRPC call. HttpRequestException: An error occurred while sending the request WebException: Error getting response stream (ReadDoneAsync2): ReceiveFailure", DebugException="System.Net.Http.HttpRequestException: An error occurred while sending the request ---> System.Net.WebException: Error getting response stream (ReadDoneAsync2): ReceiveFailure
  at System.Net.WebResponseStream.InitReadAsync (System.Threading.CancellationToken cancellationToken) [0x000f3] in <c7c32602e34d422f9c1a5e3ed52782b2>:0 
  at System.Net.WebOperation.Run () [0x001d9] in <c7c32602e34d422f9c1a5e3ed52782b2>:0 
  at System.Net.WebCompletionSource`1[T].WaitForCompletion () [0x00094] in <c7c32602e34d422f9c1a5e3ed52782b2>:0 
  at System.Net.HttpWebRequest.RunWithTimeoutWorker[T] (System.Threading.Tasks.Task`1[TResult] workerTask, System.Int32 timeout, System.Action abort, System.Func`1[TResult] aborted, System.Threading.CancellationTokenSource cts) [0x000f8] in <c7c32602e34d422f9c1a5e3ed52782b2>:0 
  at System.Net.HttpWebRequest.EndGetResponse (System.IAsyncResult asyncResult) [0x00020] in <c7c32602e34d422f9c1a5e3ed52782b2>:0 
  at System.Threading.Tasks.TaskFactory`1[TResult].FromAsyncCoreLogic (System.IAsyncResult iar, System.Func`2[T,TResult] endFunction, System.Action`1[T] endAction, System.Threading.Tasks.Task`1[TResult] promise, System.Boolean requiresSynchronization) [0x0000f] in <00c558282d074245ab3496e2d108079b>:0 

There's very few instances of this exact error when I google it. My server and client code are pretty bare bones right now:

Server:

      ServerServiceDefinition gameServiceService = GameService.BindService(new GameServerImpl());

      Server server = new Server
      {
        Services = {gameServiceService},
        Ports = {new ServerPort("localhost", Port, ServerCredentials.Insecure)},
      };
      server.Start();

      Console.WriteLine("Server listening on port " + Port);
      Console.WriteLine("Press any key to stop the server...");
      Console.ReadKey();

      server.ShutdownAsync().Wait();

Unity Client Code:

      var option = new GrpcChannelOptions
      {
        Credentials = ChannelCredentials.Insecure,
      };
      var channel = GrpcChannel.ForAddress("http://127.0.0.1:9091", option);
      var client = new GameService.GameServiceClient(channel);
      FullMonsterStateResponse reply = client.getMatchResult(new GetMatchResultRequest());
      Debug.Log(reply);

Error only shows up if I add that debug.log statement or otherwise try to interact with the response object at all.

Are you using Grpc Web here? It seems you are missing that part in the creation of the channel. You need to set the HttpClientHandler to a GrpcWeb handler. You also need to enable GrpcWeb on the server side (or use Envoy as a proxy). Also client side streaming is not supported in Grpc Web, serverside streaming is.

If you can share a minimal repro I can also take a look at it.

@bryding
Copy link

bryding commented Nov 24, 2021

@Blackclaws Thanks a bunch for helping me out with this, here's a minimal repo:

https://github.com/bryding/mean-monsters-minimal

I added the GrpcWebHandler, which now seems obvious in retrospect, but the error is the same (presumably because I'm not implementing the server correctly as well).

@Blackclaws
Copy link
Contributor

I've pushed to a branch working-minimal that works.

You need to use aspnetcore and not the raw Grpc Server as you actually need a webserver on the other side that transforms the grpc web requests. Also Grpc is usually over http so I've switched that over as well, certificate validation has been turned off because of the localhost dev certificate, you should turn that on in production.

I've also used Grpc Codegeneration directly in csproj. That required a change in the relative import paths between the protobuf files.

@MHDante
Copy link

MHDante commented Nov 24, 2021

Hey everyone, just wanted to say that this works out of the box for me using:

I'm using Unity LTS: 2020.3.23

My backend is as follows:

  • Python Grpc Server
  • Envoy Proxy

I generated my C# (and python) proto files using the latest protoc binaries:
https://github.com/protocolbuffers/protobuf/releases/tag/v3.19.1

I then Installed NuGetForUnity:
https://github.com/GlitchEnzo/NuGetForUnity.

I used it to install the following Packages:

  • Grpc.Core.Api
  • Grpc.Net.Client
  • Grpc.Net.Client.Web
  • Google.Protobuf

This provided the the following dlls

  • netstandard2.0:

    • Google.Protobuf.3.19.1
    • Grpc.Core.Api.2.40.0
    • Grpc.Net.Client.2.40.0
    • Grpc.Net.Client.Web.2.40.0
    • Grpc.Net.Common.2.40.0
    • Microsoft.Extensions.Logging.Abstractions.3.0.3
    • System.Buffers.4.4.0
    • System.Memory.4.5.3
    • System.Runtime.CompilerServices.Unsafe.4.5.2
  • netstandard1.3:

    • System.Diagnostics.DiagnosticSource.4.5.1

My client code is as follows:

using System.Net.Http;
using System.Threading;
using Ai.Transforms.Grpcwebunity;
using Grpc.Net.Client;
using Grpc.Net.Client.Web;
using UnityEngine;

public class Spinner : MonoBehaviour
{
    private int i = 0;
    TestService.TestServiceClient client;
    CancellationTokenSource cancellationTokenSource;

    // Update is called once per frame
    async void Awake()
    {
        var channel = GrpcChannel.ForAddress("http://localhost:8001", new GrpcChannelOptions
        {
            HttpHandler = new GrpcWebHandler(new HttpClientHandler()),
        });

        cancellationTokenSource = new CancellationTokenSource();
        var token = cancellationTokenSource.Token;

        client = new TestService.TestServiceClient(channel);
        using var call = client.ServerStream(new Request { Data = "Streem" });
        while (await call.ResponseStream.MoveNext(token))
        {
            var item = call.ResponseStream.Current;
            Debug.Log("Success! " + item.Data);
        }
    }
    private void Update()
    {
        if (i++ % 200 == 0)
        {
            PrintUnary();
        }
        transform.Rotate(Vector3.up, 1f);
    }

    private async void PrintUnary()
    {
        if (client == null) return;
        using var call = client.UnaryAsync(new Request { Data = "Cheem " + i });
        var response = await call;
        Debug.Log(response.Data);
    }

    private void OnDestroy()
    {
        cancellationTokenSource.Cancel();
    }
}

Both Unary and server streaming calls worked fine:

image

Congratulations!!!

If there are compiling issues, I think it may have to do with DLL collisions with other packages/Assets.

N.B. I've only tested this on windows editor and standalone.

On an unrelated note, At the moment, I can't seem to get the calls to respond on a WebGL build. Currently, the calls get stuck waiting for awaits to resolve. I think this may have to do with the lack of thread support on Emscripten\WebGL. (how does blazor do it?)

@Blackclaws
Copy link
Contributor

Yeah the issues I had with netstandard2.1 might have had to do with me using a preview of 2021.2 instead of the released build, my bad. Just glad that it works that easy now :)

Blazorwasm ships with its own HttpClient implementation that uses XHR/fetch under the hood, I'm not sure what Unity does there.

@ztolley
Copy link

ztolley commented Jan 24, 2022

@MHDante @Blackclaws dod you get any closer to knowing if WebGL builds can support this?

@MHDante
Copy link

MHDante commented Jan 24, 2022

I've actually written a version of the client that can support it. It's here, but it requires a lot of work (regarding documentation and whatnot) : https://github.com/transformsai/UnityGrpcWeb

You can install it in the package manager through OpenUPM:

https://openupm.com/packages/ai.transforms.unity-grpc-web/

@ztolley
Copy link

ztolley commented Jan 24, 2022

Looks interesting. Does it support streaming?

@m-yukio
Copy link

m-yukio commented Mar 2, 2022

I checked the operation using HTTP / 2 on Unity 2021.2.11f1 and found an error.

I downloaded the package from the NuGet site. The following is the configuration.

.--Plugins
   |-- google.protobuf.3.19.4/lib/netstandard2.0/Google.Protobuf.dll
   |-- grpc.core.2.44.0/lib/netstandard2.0/Grpc.Core.dll
   |-- grpc.core.api.2.44.0/lib/netstandard2.0/Grpc.Core.Api.dll
   |-- grpc.net.client.2.42.0/lib/netstandard2.1/Grpc.Net.Client.dll
   |-- grpc.net.client.web.2.42.0/lib/netstandard2.1/Grpc.Net.Client.Web.dll
   |-- grpc.net.common.2.42.0/lib/netstandard2.1/Grpc.Net.Common.dll
   |-- microsoft.extensions.logging.abstractions.6.0.0/lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll
   |-- system.buffers.4.5.1/lib/netstandard2.0/System.Buffers.dll
   |-- system.diagnostics.diagnosticsource.6.0.0/lib/netstandard2.0/System.Diagnostics.DiagnosticSource.dll
   |-- system.memory.4.5.4/lib/netstandard2.0/System.Memory.dll
   `-- system.runtime.compilerservices.unsafe.6.0.0/lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll

Code for using HTTP/2:

string certPath = System.IO.Path.Combine(Application.streamingAssetsPath, "example.crt");
string keyPath = System.IO.Path.Combine(Application.streamingAssetsPath, "example.key");
string caPath = System.IO.Path.Combine(Application.streamingAssetsPath, "root_ca.crt");
Grpc.Core.ChannelBase channel;
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (HttpRequestMessage request, X509Certificate2 certificate, X509Chain certificateChain, SslPolicyErrors policy) =>
{
    return true;
};
handler.ClientCertificates.Add(new X509Certificate2(certPath));
HttpClient httpClient = new HttpClient(handler);
GrpcChannelOptions option = new GrpcChannelOptions();
channel = GrpcChannel.ForAddress("https://192.168.200.3:50051", new GrpcChannelOptions
{
    HttpClient = httpClient
});
Greeter.GreeterClient client = new Greeter.GreeterClient(channel);
HelloReply reply = client.SayHello(new HelloRequest { Name = "Unity" });
channel.ShutdownAsync().Wait();

The code that caused the error:

namespace Helloworld
{
    public static partial class Greeter
    {
        static readonly string __ServiceName = "helloworld.Greeter";

        [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
        static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
        {
#if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
            if (message is global::Google.Protobuf.IBufferMessage)
            {
                context.SetPayloadLength(message.CalculateSize());
                global::Google.Protobuf.MessageExtensions.WriteTo(message, context.GetBufferWriter());
                context.Complete();
                return;
            }
#endif
            context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
        }

An error occurred in global :: Google.Protobuf.MessageExtensions.WriteTo ().

The error log:

RpcException: Status(StatusCode="Internal", Detail="Error starting gRPC call. NotImplementedException: The method or operation is not implemented.", DebugException="System.NotImplementedException: The method or operation is not implemented.
  at Grpc.Core.SerializationContext.GetBufferWriter () [0x00000] in <0ee522fbcf364fe4a4f606525a3c1893>:0 
  at Helloworld.Greeter.__Helper_SerializeMessage (Google.Protobuf.IMessage message, Grpc.Core.SerializationContext context) [0x00014] in ${HOME}/Stubby/Stubby/Assets/Scripts/Greeter.cs:26 
  at Grpc.Net.Client.StreamExtensions.WriteMessageAsync[TMessage] (System.IO.Stream stream, Grpc.Net.Client.Internal.GrpcCall call, TMessage message, System.Action`2[T1,T2] serializer, Grpc.Core.CallOptions callOptions) [0x0010d] in <69757168ff984c9294d06552593564d7>:0 
  at Grpc.Net.Client.Internal.PushUnaryContent`2[TRequest,TResponse].WriteMessageCore (System.Threading.Tasks.ValueTask writeMessageTask) [0x00064] in <69757168ff984c9294d06552593564d7>:0 
  at System.Net.Http.HttpContent.CopyToAsyncCore (System.Threading.Tasks.ValueTask copyTask) [0x00067] in <156582be1ebd4171b9d70018d8f80e4f>:0 
  at System.Net.Http.HttpConnection.SendRequestContentAsync (System.Net.Http.HttpRequestMessage request, System.Net.Http.HttpConnection+HttpContentWriteStream stream, System.Threading.CancellationToken cancellationToken) [0x000a0] in <156582be1ebd4171b9d70018d8f80e4f>:0 
  at System.Net.Http.HttpConnection.SendAsyncCore (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x012e6] in <156582be1ebd4171b9d70018d8f80e4f>:0 
  at System.Net.Http.HttpConnectionPool.SendWithNtConnectionAuthAsync (System.Net.Http.HttpConnection connection, System.Net.Http.HttpRequestMessage request, System.Boolean doRequestAuth, System.Threading.CancellationToken cancellationToken) [0x0012b] in <156582be1ebd4171b9d70018d8f80e4f>:0 
  at System.Net.Http.HttpConnectionPool.SendWithRetryAsync (System.Net.Http.HttpRequestMessage request, System.Boolean doRequestAuth, System.Threading.CancellationToken cancellationToken) [0x0014b] in <156582be1ebd4171b9d70018d8f80e4f>:0 
  at System.Net.Http.RedirectHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x000ba] in <156582be1ebd4171b9d70018d8f80e4f>:0 
  at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered (System.Threading.Tasks.Task`1[TResult] sendTask, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationTokenSource cts, System.Boolean disposeCts) [0x000b3] in <156582be1ebd4171b9d70018d8f80e4f>:0 
  at Grpc.Net.Client.Internal.GrpcCall`2[TRequest,TResponse].RunCall (System.Net.Http.HttpRequestMessage request, System.Nullable`1[T] timeout) [0x00215] in <69757168ff984c9294d06552593564d7>:0 ")
Grpc.Net.Client.Internal.HttpClientCallInvoker.BlockingUnaryCall[TRequest,TResponse] (Grpc.Core.Method`2[TRequest,TResponse] method, System.String host, Grpc.Core.CallOptions options, TRequest request) (at <69757168ff984c9294d06552593564d7>:0)
Grpc.Core.Interceptors.InterceptingCallInvoker.<BlockingUnaryCall>b__3_0[TRequest,TResponse] (TRequest req, Grpc.Core.Interceptors.ClientInterceptorContext`2[TRequest,TResponse] ctx) (at <0ee522fbcf364fe4a4f606525a3c1893>:0)
Grpc.Core.ClientBase+ClientBaseConfiguration+ClientBaseConfigurationInterceptor.BlockingUnaryCall[TRequest,TResponse] (TRequest request, Grpc.Core.Interceptors.ClientInterceptorContext`2[TRequest,TResponse] context, Grpc.Core.Interceptors.Interceptor+BlockingUnaryCallContinuation`2[TRequest,TResponse] continuation) (at <0ee522fbcf364fe4a4f606525a3c1893>:0)
Grpc.Core.Interceptors.InterceptingCallInvoker.BlockingUnaryCall[TRequest,TResponse] (Grpc.Core.Method`2[TRequest,TResponse] method, System.String host, Grpc.Core.CallOptions options, TRequest request) (at <0ee522fbcf364fe4a4f606525a3c1893>:0)
Helloworld.Greeter+GreeterClient.SayHello (Helloworld.HelloRequest request, Grpc.Core.CallOptions options) (at Assets/Scripts/Greeter.cs:144)
Helloworld.Greeter+GreeterClient.SayHello (Helloworld.HelloRequest request, Grpc.Core.Metadata headers, System.Nullable`1[T] deadline, System.Threading.CancellationToken cancellationToken) (at Assets/Scripts/Greeter.cs:133)
GrpcTest.RunHelloWorld (System.Boolean useNet) (at Assets/Scripts/GrpcTest.cs:82)
GrpcTest.RunNet () (at Assets/Scripts/GrpcTest.cs:34)
UnityEngine.Events.InvokableCall.Invoke () (at /Users/bokken/buildslave/unity/build/Runtime/Export/UnityEvent/UnityEvent.cs:178)
UnityEngine.Events.UnityEvent.Invoke () (at /Users/bokken/buildslave/unity/build/Runtime/Export/UnityEvent/UnityEvent/UnityEvent_0.cs:58)
UnityEngine.UI.Button.Press () (at Library/PackageCache/[email protected]/Runtime/UI/Core/Button.cs:70)
UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at Library/PackageCache/[email protected]/Runtime/UI/Core/Button.cs:114)
UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at Library/PackageCache/[email protected]/Runtime/EventSystem/ExecuteEvents.cs:57)
UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) (at Library/PackageCache/[email protected]/Runtime/EventSystem/ExecuteEvents.cs:272)
UnityEngine.EventSystems.EventSystem:Update() (at Library/PackageCache/[email protected]/Runtime/EventSystem/EventSystem.cs:501)

Was SerializeContext determined to be unimplemented?

@m-yukio
Copy link

m-yukio commented Mar 10, 2022

When I installed the package using NuGetForUnity, the error wording changed.
The following is at startup:

Assembly 'Library/ScriptAssemblies/Assembly-CSharp.dll' will not be loaded due to errors:
Reference has errors 'Google.Protobuf'.
Assembly 'Assets/Packages/Google.Protobuf.3.19.4/lib/netstandard2.0/Google.Protobuf.dll' will not be loaded due to errors:
Unable to resolve reference 'System.Runtime.CompilerServices.Unsafe'. Is the assembly missing or incompatible with the current platform?
Reference validation can be disabled in the Plugin Inspector.

It can be executed even in this state, the following is the error log during communication:

RpcException: Status(StatusCode="Unavailable", Detail="Error starting gRPC call. HttpRequestException: Error while copying content to a stream. FileNotFoundException: Could not load file or assembly 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies.", DebugException="System.Net.Http.HttpRequestException: Error while copying content to a stream. ---> System.IO.FileNotFoundException: Could not load file or assembly 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies.
  at Google.Protobuf.WritingPrimitives.WriteString (System.Span`1[System.Byte]& buffer, Google.Protobuf.WriterInternalState& state, System.String value) [0x00076] in <2ccdeadc259d431f8c7eae367a5d08d1>:0 
  at Google.Protobuf.WriteContext.WriteString (System.String value) [0x00000] in <2ccdeadc259d431f8c7eae367a5d08d1>:0 
  at Helloworld.HelloRequest.pb::Google.Protobuf.IBufferMessage.InternalWriteTo (Google.Protobuf.WriteContext& output) [0x0001d] in /Users/murakami_yukio/Projects/murakami_yukio/Stubby/Stubby/Assets/Scripts/Proto/Hello.cs:184 
  at Google.Protobuf.WritingPrimitivesMessages.WriteRawMessage (Google.Protobuf.WriteContext& ctx, Google.Protobuf.IMessage message) [0x0000a] in <2ccdeadc259d431f8c7eae367a5d08d1>:0 
  at Google.Protobuf.MessageExtensions.WriteTo (Google.Protobuf.IMessage message, System.Buffers.IBufferWriter`1[T] output) [0x00020] in <2ccdeadc259d431f8c7eae367a5d08d1>:0 
  at Helloworld.Greeter.__Helper_SerializeMessage (Google.Protobuf.IMessage message, Grpc.Core.SerializationContext context) [0x0001c] in /Users/murakami_yukio/Projects/murakami_yukio/Stubby/Stubby/Assets/Scripts/Greeter.cs:26 
  at Grpc.Net.Client.StreamExtensions.WriteMessageAsync[TMessage] (System.IO.Stream stream, Grpc.Net.Client.Internal.GrpcCall call, TMessage message, System.Action`2[T1,T2] serializer, Grpc.Core.CallOptions callOptions) [0x00108] in <93d755b016cc4513ac20ab2598098c88>:0 
  at Grpc.Net.Client.Internal.PushUnaryContent`2[TRequest,TResponse].WriteMessageCore (System.Threading.Tasks.Task writeMessageTask) [0x00064] in <93d755b016cc4513ac20ab2598098c88>:0 
  at System.Net.Http.HttpContent.CopyToAsyncCore (System.Threading.Tasks.ValueTask copyTask) [0x00067] in <156582be1ebd4171b9d70018d8f80e4f>:0 
   --- End of inner exception stack trace ---
  at System.Net.Http.HttpContent.CopyToAsyncCore (System.Threading.Tasks.ValueTask copyTask) [0x0008f] in <156582be1ebd4171b9d70018d8f80e4f>:0 
  at System.Net.Http.HttpConnection.SendRequestContentAsync (System.Net.Http.HttpRequestMessage request, System.Net.Http.HttpConnection+HttpContentWriteStream stream, System.Threading.CancellationToken cancellationToken) [0x000a0] in <156582be1ebd4171b9d70018d8f80e4f>:0 
  at System.Net.Http.HttpConnection.SendAsyncCore (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x012e6] in <156582be1ebd4171b9d70018d8f80e4f>:0 
  at System.Net.Http.HttpConnectionPool.SendWithNtConnectionAuthAsync (System.Net.Http.HttpConnection connection, System.Net.Http.HttpRequestMessage request, System.Boolean doRequestAuth, System.Threading.CancellationToken cancellationToken) [0x0012b] in <156582be1ebd4171b9d70018d8f80e4f>:0 
  at System.Net.Http.HttpConnectionPool.SendWithRetryAsync (System.Net.Http.HttpRequestMessage request, System.Boolean doRequestAuth, System.Threading.CancellationToken cancellationToken) [0x0014b] in <156582be1ebd4171b9d70018d8f80e4f>:0 
  at System.Net.Http.RedirectHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x000ba] in <156582be1ebd4171b9d70018d8f80e4f>:0 
  at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered (System.Threading.Tasks.Task`1[TResult] sendTask, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationTokenSource cts, System.Boolean disposeCts) [0x000b3] in <156582be1ebd4171b9d70018d8f80e4f>:0 
  at Grpc.Net.Client.Internal.GrpcCall`2[TRequest,TResponse].RunCall (System.Net.Http.HttpRequestMessage request, System.Nullable`1[T] timeout) [0x00215] in <93d755b016cc4513ac20ab2598098c88>:0 ")
Grpc.Net.Client.Internal.HttpClientCallInvoker.BlockingUnaryCall[TRequest,TResponse] (Grpc.Core.Method`2[TRequest,TResponse] method, System.String host, Grpc.Core.CallOptions options, TRequest request) (at <93d755b016cc4513ac20ab2598098c88>:0)
Grpc.Core.Interceptors.InterceptingCallInvoker.<BlockingUnaryCall>b__3_0[TRequest,TResponse] (TRequest req, Grpc.Core.Interceptors.ClientInterceptorContext`2[TRequest,TResponse] ctx) (at <4f08a803638948218f0b2255b55def9b>:0)
Grpc.Core.ClientBase+ClientBaseConfiguration+ClientBaseConfigurationInterceptor.BlockingUnaryCall[TRequest,TResponse] (TRequest request, Grpc.Core.Interceptors.ClientInterceptorContext`2[TRequest,TResponse] context, Grpc.Core.Interceptors.Interceptor+BlockingUnaryCallContinuation`2[TRequest,TResponse] continuation) (at <4f08a803638948218f0b2255b55def9b>:0)
Grpc.Core.Interceptors.InterceptingCallInvoker.BlockingUnaryCall[TRequest,TResponse] (Grpc.Core.Method`2[TRequest,TResponse] method, System.String host, Grpc.Core.CallOptions options, TRequest request) (at <4f08a803638948218f0b2255b55def9b>:0)
Helloworld.Greeter+GreeterClient.SayHello (Helloworld.HelloRequest request, Grpc.Core.CallOptions options) (at Assets/Scripts/Greeter.cs:144)
Helloworld.Greeter+GreeterClient.SayHello (Helloworld.HelloRequest request, Grpc.Core.Metadata headers, System.Nullable`1[T] deadline, System.Threading.CancellationToken cancellationToken) (at Assets/Scripts/Greeter.cs:133)
GrpcTest.RunHelloWorld (System.Boolean useNet) (at Assets/Scripts/GrpcTest.cs:82)
GrpcTest.RunNet () (at Assets/Scripts/GrpcTest.cs:34)
UnityEngine.Events.InvokableCall.Invoke () (at /Users/bokken/buildslave/unity/build/Runtime/Export/UnityEvent/UnityEvent.cs:178)
UnityEngine.Events.UnityEvent.Invoke () (at /Users/bokken/buildslave/unity/build/Runtime/Export/UnityEvent/UnityEvent/UnityEvent_0.cs:58)
UnityEngine.UI.Button.Press () (at Library/PackageCache/[email protected]/Runtime/UI/Core/Button.cs:70)
UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at Library/PackageCache/[email protected]/Runtime/UI/Core/Button.cs:114)
UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at Library/PackageCache/[email protected]/Runtime/EventSystem/ExecuteEvents.cs:57)
UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) (at Library/PackageCache/[email protected]/Runtime/EventSystem/ExecuteEvents.cs:272)
UnityEngine.EventSystems.EventSystem:Update() (at Library/PackageCache/[email protected]/Runtime/EventSystem/EventSystem.cs:501)

It seems that the connection with the server is established, so is there an error related to Protobuf?

@m-yukio
Copy link

m-yukio commented Mar 10, 2022

The gRPC C# core library does not generate Protobuf related errors. It's working properly.
So is there a reason on the gRPC for .NET side?

@m-yukio
Copy link

m-yukio commented Mar 11, 2022

Since it is an error related to Protobuf, I also reported it in Protobuf issues.
protocolbuffers/protobuf#9618

I also reported on the Unity forum.
https://forum.unity.com/threads/unity-future-net-development-status.1092205/page-14#post-7955898

@mandicLuka
Copy link

@MHDante Can you please give some more details on the server side implementation? Is there any way right now to avoid using grpc-web with grpc-dotnet in Unity?

@ianlevesque
Copy link

@mandicLuka I had good luck putting this envoy configuration in front of my gRPC server to get grpc-web to work. Just change the ports/addresses to match: https://github.com/grpc/grpc-web/blob/8c5502186445e35002697f4bd8d1b820abdbed5d/net/grpc/gateway/examples/echo/envoy.yaml

@davzoltan
Copy link

@ianlevesque Can you please give some more details? I tried to use this envoy.yaml, my grpc python server is using 0.0.0.0:9090 and my Unity client is using
var channel = GrpcChannel.ForAddress("http://localhost:8080", new GrpcChannelOptions { HttpHandler = new GrpcWebHandler(new HttpClientHandler()) });
and I get this error:
RpcException: Status(StatusCode="Unavailable", Detail="no healthy upstream")

What did I missed, or did I misunderstood something?

@ianlevesque
Copy link

That error sounds like something from envoy, complaining about not reaching your real gRPC server. I can check my exact config later, I had that error first when not enabling http2 correctly in envoy and then again when trying to reach localhost from envoy inside docker on windows (there’s a special hostname docker requires to do that). Are you using docker to run envoy?

@m-yukio
Copy link

m-yukio commented May 27, 2022

There is a problem with unsupported HTTP/2 in Unity, so I posted information for supporting mac IL2CPP builds in gRPC Core issues in gRPC C # core-library.

Build script for gRPC C# core-library for Apple M1 #29815
grpc/grpc#29815

How to avoid Unity IL2CPP build errors on macOS #29817
grpc/grpc#29817

@davzoltan
Copy link

@ianlevesque The server and client are not in docker, only the envoy proxy, but I fixed it, I had to change the socket address to 0.0.0.0 and change the url in the client to http://localhost:8080/echo_service and its working

@HannaGurwitzTT
Copy link

HannaGurwitzTT commented Jul 8, 2022

Having gone through this thread, is there any way using grpc-dotnet to implement client-side streaming on unity currently?
Thank you very much.

@mandicLuka
Copy link

@HannaGurwitzTT I ended up using old and deprecated Grpc.Core instead of grpc-dotnet and it works fine for both client and server implementation in Unity (tested with Unity 2021.3 LTS with grpc libs built for netstandard2.1, both on Windows and Linux). The API of both is pretty much the same and the transition from one to the other is not too much work. I hope when Unity transitions to .net5 or .net6 instead of their fork of netstandard, HTTP2 will be supported and gprc-dotnet will completely take over (at least for client side). This transition could happen in the following years, and by then there is not a standardized solution

@SamPaladin
Copy link

I did include this framework on my project and got it, at least, compiled well. I am targeting .net2 standard and made a simple call to a endpoint. I have no control over this endpoint but basically is giving me some TLS security errors:

RpcException: Status(StatusCode="Internal", Detail="Error starting gRPC call. HttpRequestException: An error occurred while sending the request WebException: Error: TrustFailure (A call to SSPI failed, see inner exception.) AuthenticationException: A call to SSPI failed, see inner exception. TlsException: Handshake failed - error code: UNITYTLS_INTERNAL_ERROR, verify result: 4294957312

When I configured channel I did this:

      GrpcChannelOptions channelOptions = new GrpcChannelOptions
      {
          Credentials = ChannelCredentials.SecureSsl,
          HttpHandler = new GrpcWebHandler(new HttpClientHandler()),
      };

      GrpcChannel channel = GrpcChannel.ForAddress("https://blablabla:PORT", channelOptions);
      
      grpcClient = new Blabla(channel);
            

Do I need to specify pem certificate as described in here?

I am using Unity2020.3.36.

@Filein
Copy link

Filein commented Jul 29, 2022

I have an error:

RpcException: Status(StatusCode="Unavailable", Detail="Error starting gRPC call. HttpRequestException: An error occurred while sending the request IOException: Unable to read data from the transport connection:

Grpc.Net.Client.Internal.HttpClientCallInvoker.BlockingUnaryCall[TRequest,TResponse] (Grpc.Core.Method`2[TRequest,TResponse] method, System.String host, Grpc.Core.CallOptions options, TRequest request) (at <dce327fa8bc5488094df24d8937bcf24>:0)
Grpc.Core.Interceptors.InterceptingCallInvoker.<BlockingUnaryCall>b__3_0[TRequest,TResponse] (TRequest req, Grpc.Core.Interceptors.ClientInterceptorContext`2[TRequest,TResponse] ctx) (at <4e0f892a1f5640f4a7bebeeb12f2d516>:0)
Grpc.Core.ClientBase+ClientBaseConfiguration+ClientBaseConfigurationInterceptor.BlockingUnaryCall[TRequest,TResponse] (TRequest request, Grpc.Core.Interceptors.ClientInterceptorContext`2[TRequest,TResponse] context, Grpc.Core.Interceptors.Interceptor+BlockingUnaryCallContinuation`2[TRequest,TResponse] continuation) (at <4e0f892a1f5640f4a7bebeeb12f2d516>:0)
Grpc.Core.Interceptors.InterceptingCallInvoker.BlockingUnaryCall[TRequest,TResponse] (Grpc.Core.Method`2[TRequest,TResponse] method, System.String host, Grpc.Core.CallOptions options, TRequest request) (at <4e0f892a1f5640f4a7bebeeb12f2d516>:0)
Helloworld.Greeter+GreeterClient.SayHello (Helloworld.HelloRequest request, Grpc.Core.CallOptions options) (at Assets/Scripts/Packets/HelloworldGrpc.cs:134)
Helloworld.Greeter+GreeterClient.SayHello (Helloworld.HelloRequest request, Grpc.Core.Metadata headers, System.Nullable`1[T] deadline, System.Threading.CancellationToken cancellationToken) (at Assets/Scripts/Packets/HelloworldGrpc.cs:123)
NetworkPeer.StartPeer (System.String serverAddress, InputHandler+InputType characterType) (at Assets/Scripts/NetworkPeer.cs:44)
GameManager.OnClickAddPeer () (at Assets/Scripts/GameManager.cs:21)
UnityEngine.Events.InvokableCall.Invoke () (at <6afd1274f120405096bc1ad9e2010ba6>:0)
UnityEngine.Events.UnityEvent.Invoke () (at <6afd1274f120405096bc1ad9e2010ba6>:0)
UnityEngine.UI.Button.Press () (at Library/PackageCache/[email protected]/Runtime/UI/Core/Button.cs:70)
UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at Library/PackageCache/[email protected]/Runtime/UI/Core/Button.cs:114)
UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at Library/PackageCache/[email protected]/Runtime/EventSystem/ExecuteEvents.cs:57)
UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) (at Library/PackageCache/[email protected]/Runtime/EventSystem/ExecuteEvents.cs:272)
UnityEngine.EventSystems.EventSystem:Update() (at Library/PackageCache/[email protected]/Runtime/EventSystem/EventSystem.cs:501)

I have the error using Grpc.Net.Client.Web. Here is a code :

    var channel = GrpcChannel.ForAddress("http://localhost:50051", new GrpcChannelOptions
    {
        Credentials = ChannelCredentials.Insecure,
        HttpHandler = new GrpcWebHandler(new HttpClientHandler()),
    });

    var client = new Helloworld.Greeter.GreeterClient(channel);
    var response = client.SayHello(new Helloworld.HelloRequest()
    {
        Name = "World"
    });

But, It works using Grpc.Core.

    var channel = new Channel("localhost:50051", ChannelCredentials.Insecure);

    var client = new Helloworld.Greeter.GreeterClient(channel);
    var response = client.SayHello(new Helloworld.HelloRequest()
    {
        Name = "World"
    });

I don't know what is difference... :(( I'm afraid of using Grpc.Core cause It'll be gone. How can I avoid an error using Grpc.Net.Client.*?

@JamesNK
Copy link
Member

JamesNK commented Jul 29, 2022

The problem might be that the server is using HTTP/2, which Unity doesn't support. See https://docs.microsoft.com/en-us/aspnet/core/grpc/browser?view=aspnetcore-6.0#http-protocol

@plucked
Copy link

plucked commented Oct 25, 2022

Some update from regarding http2 support

I have had a chat with our developers and they have clarified that there is unfortunately no roadmap to add http/2 support at the moment. It will likely be included when we eventually switch to .NET 6.0 and CoreCLR, which will likely be a few years.

@Olof-IL
Copy link

Olof-IL commented Nov 16, 2022

So I went down this rabbit hole too...

I seem to have got Grpc.Core working talking to a server in Go, with normal requests and bidirectional streaming. (bidirectional streaming is a must for me, so Grpc-Web is not an option)

It even works on my iOS device.

BUT! It only works with insecure channel... If I try to add TLS, the C# client refuse to connect. I even tried to instantiate the server in C#, but still get the same error. (Just get "Unavailable" like it fails to connect at all)

Rpc SignIn failed(Unavailable): Status(StatusCode="Unavailable", Detail="failed to connect to all addresses", DebugException="Grpc.Core.Internal.CoreErrorDetailException: {"created":"@1668609108.098833000","description":"Failed to pick subchannel","file":"/tmpfs/altsrc/github/grpc/workspace_csharp_ext_macos_x64/src/core/ext/filters/client_channel/client_channel.cc","file_line":3129,"referenced_errors":[{"created":"@1668609108.098833000","description":"failed to connect to all addresses","file":"/tmpfs/altsrc/github/grpc/workspace_csharp_ext_macos_x64/src/core/lib/transport/error_utils.cc","file_line":163,"grpc_status":14}]}")

Has anyone here had any luck getting Grpc.Core to work with TLS in Unity?

Very sad to hear Unity is not prioritising http/2 and grpc-dotnet support... Grpc.Core is only in maintenance for one year more, and they have already dropped the Unity/Xamarin support (grpc/grpc#24236), so right now there seems to be zero support for any gRPC in Unity.

Here's my test code... Using Insecure works fine both with C# Server and Golang server, and the certificates also work fine when I use a test client written in Golang to talk with my Golang server, so those should be fine too.

    var serverCert = File.ReadAllText(Application.dataPath + "/TGO/Netcode/Cert/server-cert.pem");
    var serverKey = File.ReadAllText(Application.dataPath + "/TGO/Netcode/Cert/server-key.pem");

    var serverCredentials = new SslServerCredentials(new[] { new KeyCertificatePair(serverCert, serverKey) });
    instance.serverImpl = new Server
    {
      Services = { GameServer.BindService(new GameServerImpl()) },
      Ports = { { instance.server, instance.port, serverCredentials } }
    };
    instance.serverImpl.Start();


    var rootCert = File.ReadAllText(Application.dataPath + "/TGO/Netcode/Cert/ca-cert.pem");
    instance.channel = new Channel(instance.server, instance.port, new SslCredentials(rootCert));

    // instance.channel = new Channel(instance.server, instance.port, ChannelCredentials.Insecure);

@MHDante
Copy link

MHDante commented Nov 16, 2022

One possible solution is to use a plugin like this:

https://assetstore.unity.com/packages/tools/network/best-http-2-155981

It has a built in handler that you could use as the HttpHandler on requests.

The primary issue with this plugin is that it is not redistribution-friendly

@doctorseus
Copy link

doctorseus commented Dec 3, 2022

I started to implement support for grpc-dotnet using the asset store package which @MHDante mentioned (BestHTTP/2), which is an HTTP2 client written in C# (so no native libraries involved). You can find my work here: https://github.com/doctorseus/grpc-dotnet-unity

There is support for all call types, but some behavior still needs to be finalized (cancel, timeout). However, at the moment the package above is for sale so in case someone wants to pick it up to give this a try I pushed the work which is already done.
Edit (05.02.2023): The code is feature complete now with respect to gRPC and much more stable across all different call types.

Another important note: BestHTTP does not support plaintext http2 aka "h2c with prior knowledge" (what is used if you use gRPC via an "Unsecured" channel). So you will need to use SSL on your gRPC server.

@StephenHodgson
Copy link

StephenHodgson commented Jan 23, 2023

Hopefully that new version of Unity with CoreCLR support drops this year...

IMAGE

https://www.youtube.com/watch?v=T6HhePbyAsg

@ytimenkov
Copy link

An alternative solution is to fallback to HTTP/1 (so-called grpc-web), if you are in control over server.

There is an amazing documentation from Microsoft which explicitly mentions Unity: https://learn.microsoft.com/en-us/aspnet/core/grpc/netstandard?view=aspnetcore-7.0 and more general overview https://learn.microsoft.com/en-us/aspnet/core/grpc/supported-platforms?view=aspnetcore-7.0

In Asp.net the support can be added with few lines of code, while other servers will require a proxy.

@mayuki
Copy link
Contributor

mayuki commented Jul 28, 2023

Today, we have released a preview of the HTTP/2 handler that enables the use of grpc-dotnet on Unity.
https://github.com/Cysharp/YetAnotherHttpHandler

This library provides an HttpMessageHandler for HTTP/2 communication, built on top of hyper.

Although this project is still in its early stages and rough around the edges, we believe it is an important step forward in the Unity ecosystem. Please try out our library, and we look forward to receiving your feedback.

@Blackclaws
Copy link
Contributor

Today, we have released a preview of the HTTP/2 handler that enables the use of grpc-dotnet on Unity. https://github.com/Cysharp/YetAnotherHttpHandler

This library provides an HttpMessageHandler for HTTP/2 communication, built on top of hyper.

Although this project is still in its early stages and rough around the edges, we believe it is an important step forward in the Unity ecosystem. Please try out our library, and we look forward to receiving your feedback.

Awesome, I love using your other libraries in Unity. As we are actively using GRPC in Unity using Grpc Web we'll try it out and report back if we find any issues!

@smumair06
Copy link

rpc-dotnet using the asset store package which @MHDante mentioned (BestHTTP/2), which is an HTTP2 client written in C# (so no native libraries involved). You can find my work here: https://github.com/doctorseus/grpc-dotnet-unity

There is support for all call types, but some behavior still needs to be finalized (cancel, timeout). However, at the moment the package above is for sale so in case someone wants to pick it up to give this a try I pushed the work which is already done. Edit (05.02.2023): The code is feature complete now with respect to gRPC and much more stable across all different call types.

Another important note: BestHTTP does not support plaintext http2 aka "h2c with prior knowledge" (what is used if you use gRPC via an "Unsecured" channel). So you will need to use SSL on your gRPC server.

Hi, have tried using your library in Unity 2022.3.38 but, I am getting following error when I try to invoke gRPC service:

IOException: client error (Connect)
Cysharp.Net.Http.ResponseContext.CompleteAsFailed (System.String errorMessage, System.UInt32 h2ErrorCode) (at Assets/YetAnotherHttpHandler/ResponseContext.cs:134)
--- End of stack trace from previous location where exception was thrown ---
Cysharp.Net.Http.ResponseContext.GetResponseAsync () (at Assets/YetAnotherHttpHandler/ResponseContext.cs:163)
Rethrow as HttpRequestException: client error (Connect)
Cysharp.Net.Http.ResponseContext.GetResponseAsync () (at Assets/YetAnotherHttpHandler/ResponseContext.cs:167)
Grpc.Net.Client.Internal.GrpcCall2[TRequest,TResponse].RunCall (System.Net.Http.HttpRequestMessage request, System.Nullable1[T] timeout) (at <94da598685444ce2bdee3187d22127cb>:0)
Rethrow as RpcException: Status(StatusCode="Unavailable", Detail="Error starting gRPC call. HttpRequestException: client error (Connect) IOException: client error (Connect)", DebugException="System.Net.Http.HttpRequestException: client error (Connect)")
Demo.Start () (at Assets/Scripts/Demo.cs:23)
System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.b__7_0 (System.Object state) (at :0)
UnityEngine.UnitySynchronizationContext+WorkRequest.Invoke () (at <2d8783c7af0442318483a199a473c55b>:0)
UnityEngine.UnitySynchronizationContext.Exec () (at <2d8783c7af0442318483a199a473c55b>:0)
UnityEngine.UnitySynchronizationContext.ExecuteTasks () (at <2d8783c7af0442318483a199a473c55b>:0)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests