grpc / grpc-dotnet

gRPC for .NET
Apache License 2.0
4.22k stars 776 forks source link

Unity support #1309

Open ChristopheBougere opened 3 years ago

ChristopheBougere commented 3 years ago

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?

JamesNK commented 3 years ago

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?

ChristopheBougere commented 3 years ago

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 commented 3 years ago

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

JamesNK commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

Looks like http/2 is not supported.

https://forum.unity.com/threads/unity-future-net-development-status.1092205/page-6#post-7422071

Blackclaws commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

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:

optionally:

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 commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

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

Blackclaws commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

@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 commented 3 years ago

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 commented 3 years ago

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:

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

I used it to install the following Packages:

This provided the the following dlls

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 commented 3 years ago

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 commented 2 years ago

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

MHDante commented 2 years ago

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 commented 2 years ago

Looks interesting. Does it support streaming?

MHDante commented 2 years ago

Only Server-side streaming (as per the grpc-web spec)

You can use it as follows:

var grpcChannel = UnityGrpcWeb.MakeChannel(ServerAddress);

It will use the appropriate connections depending on the platform (Only tested on windows/mac/webgl)

achia commented 2 years ago

Only Server-side streaming (as per the grpc-web spec)

You can use it as follows:


var grpcChannel = UnityGrpcWeb.MakeChannel(ServerAddress);

It will use the appropriate connections depending on the platform (Only tested on windows/mac/webgl)

Thank you for creating this!

I got it working on unity in editor, but for some reason in a webgl build (after I solved the cors issues) it just hangs and nothing is written to the console when I make a call async or no from client to server. Looks like a blocking issue too as I get a spinning ball. I wonder if its because I'm on a mac...

m-yukio commented 2 years ago

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/com.unity.ugui@1.0.0/Runtime/UI/Core/Button.cs:70)
UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Button.cs:114)
UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at Library/PackageCache/com.unity.ugui@1.0.0/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/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:272)
UnityEngine.EventSystems.EventSystem:Update() (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/EventSystem.cs:501)

Was SerializeContext determined to be unimplemented?

m-yukio commented 2 years ago

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/com.unity.ugui@1.0.0/Runtime/UI/Core/Button.cs:70)
UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Button.cs:114)
UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at Library/PackageCache/com.unity.ugui@1.0.0/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/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:272)
UnityEngine.EventSystems.EventSystem:Update() (at Library/PackageCache/com.unity.ugui@1.0.0/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 commented 2 years ago

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 commented 2 years ago

Since it is an error related to Protobuf, I also reported it in Protobuf issues. https://github.com/protocolbuffers/protobuf/issues/9618

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

mandicLuka commented 2 years ago

@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 commented 2 years ago

@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 commented 2 years ago

@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 commented 2 years ago

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 commented 2 years ago

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 https://github.com/grpc/grpc/issues/29815

How to avoid Unity IL2CPP build errors on macOS #29817 https://github.com/grpc/grpc/issues/29817

davzoltan commented 2 years ago

@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 commented 2 years ago

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 commented 2 years ago

@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 commented 2 years ago

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 commented 2 years ago

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/com.unity.ugui@1.0.0/Runtime/UI/Core/Button.cs:70)
UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Button.cs:114)
UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at Library/PackageCache/com.unity.ugui@1.0.0/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/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:272)
UnityEngine.EventSystems.EventSystem:Update() (at Library/PackageCache/com.unity.ugui@1.0.0/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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 (https://github.com/grpc/grpc/issues/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 commented 2 years ago

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 commented 2 years ago

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 commented 1 year ago

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

IMAGE

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

ytimenkov commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 4 months ago

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)