[Unity] Communicate with SignalR from Unity
Tags: unity3d, csharp
Published: 2021-03-30 16:41:14

## Intro
This time, I will try communicating with SignalR from Unity applications.
For server side, I will use the application what I created last time.

* [[ASP.NET Core] Try SignalR 1](https://dev.to/masanori_msl/asp-net-core-try-signalr-1-4if)

### Environments
* Unity ver.2020.2.1f1
* Microsoft.AspNetCore.SignalR.Client ver.5.0.4
* nuget.exe ver.5.8.1 

## AspNetCore.SignalR.Client
I can connect SignalR hub by AspNetCore.SignalR.Client.
I used JavaScript(TypeScript) version last time.

This time, I use C# version.

* [ASP.NET Core SignalR .NET Client](https://docs.microsoft.com/en-us/aspnet/core/signalr/dotnet-client?view=aspnetcore-5.0&tabs=visual-studio)

Though this library is named "AspNetCore", it complies .NET Standard.
So I can use it from WPF, Unity, and so on like the article above.

## Use AspNetCore.SignalR.Client from Unity
### How to install?
One important problem is I can't use NuGet for Unity applications.
So I must put dlls into the Assets/Plugins folder manually.

First, I tried installing AspNetCore.SignalR.Client in a .NET 5 console application, and copying dlls.
But the dlls were complied .NET Standard 2.1.

Because Unity applications only could use .NET Standard 2.0.

So I used NuGet of commandline version.
* [NuGet Gallery | Downloads](https://www.nuget.org/downloads)

I just installed nuget.exe and executed "./nuget install Microsoft.AspNetCore.SignalR.Client".

### Copy dlls
After installing, I could get packages.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kh91e080f862d9r5umue.jpg)

And there were some dlls for several platforms.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/edrvl1dpv7vmgn0bvvfb.jpg)

So I copied netstandard2.0 one.

First, I copied "Microsoft.AspNetCore.SignalR.Client.Core.dll" and got errors.
It was because lacking of dependencies. So I copied them.

When I couldn't get .NET Standard2.0 packages, I copied older one.

### Copied dlls
* Microsoft.AspNetCore.Http.Connections.Client.dll
* Microsoft.AspNetCore.Http.Connections.Common.dll
* Microsoft.AspNetCore.Http.Features.dll
* Microsoft.AspNetCore.SignalR.Client.Core.dll
* Microsoft.AspNetCore.SignalR.Client.dll
* Microsoft.AspNetCore.SignalR.Common.dll
* Microsoft.AspNetCore.SignalR.Protocols.Json.dll
* Microsoft.Bcl.AsyncInterfaces.dll
* Microsoft.Extensions.DependencyInjection.Abstractions.dll
* Microsoft.Extensions.DependencyInjection.dll
* Microsoft.Extensions.Logging.Abstractions.dll
* Microsoft.Extensions.Logging.dll
* Microsoft.Extensions.Options.dll
* Microsoft.Extensions.Primitives.dll
* System.Buffers.dll
* System.Diagnostics.DiagnosticSource.dll
* System.IO.Pipelines.dll
* System.Memory.dll
* System.Runtime.CompilerServices.Unsafe.dll
* System.Text.Encodings.Web.dll
* System.Text.Json.dll
* System.Threading.Channels.dll
* System.Threading.Tasks.Extensions.dll

* [Unity で ASP.NET Core SignalR を利用する - xin9le.net](https://blog.xin9le.net/entry/2019/05/03/231001)

## Communicate with SignalR
#### MainPage.cs
```cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using Microsoft.AspNetCore.SignalR.Client;

public class MainPage : MonoBehaviour
{
    public Text ReceivedText;
    public InputField MessageInput;
    public Button SendButton;
    private SignalRConnector connector;
    public async Task Start()
    {
        connector = new SignalRConnector();
        connector.OnMessageReceived += UpdateReceivedMessages;

        await connector.InitAsync();
        SendButton.onClick.AddListener(SendMessage);
    }
    private void UpdateReceivedMessages(Message newMessage)
    {
        var lastMessages = this.ReceivedText.text;
        if(string.IsNullOrEmpty(lastMessages) == false)
        {
            lastMessages += "\n";
        }
        lastMessages += $"User:{newMessage.UserName} Message:{newMessage.Text}";
        this.ReceivedText.text = lastMessages;
    }
    private async void SendMessage()
    {
        await connector.SendMessageAsync(new Message
        {
            UserName = "Example",
            Text = MessageInput.text,
        });
    }
}
```
#### Message.cs
```cs
public class Message
{
    public string UserName { get; set; }
    public string Text { get; set; }
}
```

#### SignalRConnector.cs
```cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using Microsoft.AspNetCore.SignalR.Client;

public class SignalRConnector
{
    public Action<Message> OnMessageReceived;
    private HubConnection connection;
    public async Task InitAsync()
    {
        connection = new HubConnectionBuilder()
                .WithUrl("http://localhost:5000/chatHub")
                .Build();
        connection.On<string, string>("ReceiveMessage", (user, message) =>
        {
            OnMessageReceived?.Invoke(new Message
            {
                UserName = user,
                Text = message,
            });
        });
        await StartConnectionAsync();
    }
    public async Task SendMessageAsync(Message message)
    {
        try
        {
            await connection.InvokeAsync("SendMessage",
                message.UserName, message.Text);
        }
        catch (Exception ex)
        {
            UnityEngine.Debug.LogError($"Error {ex.Message}");
        }
    }
    private async Task StartConnectionAsync()
    {
        try
        {
            await connection.StartAsync();
        }
        catch (Exception ex)
        {
            UnityEngine.Debug.LogError($"Error {ex.Message}");
        }
    }
}
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/52gw6181cj3rgqupu2bf.jpg)

<font color="red"><b>--- 2021-04-01 Update ---</b></font>
## IL2CPP
When I set "Scripting Backent" to "IL2CPP", I will get error.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ei6a26ktrgstdo8xmve9.png)

Because some classes are removed.
For avoiding this problem, I add "link.xml" into the Assets folder.

#### link.xml
```xml
<linker>
    <assembly fullname="Microsoft.AspNetCore.Connections.Abstractions" preserve="all"/>
    <assembly fullname="Microsoft.AspNetCore.Http.Connections.Client" preserve="all"/>
     <assembly fullname="Microsoft.AspNetCore.Http.Connections.Common" preserve="all"/>
    <assembly fullname="Microsoft.AspNetCore.Http.Features" preserve="all"/>
    <assembly fullname="Microsoft.AspNetCore.SignalR.Client.Core" preserve="all"/>
    <assembly fullname="Microsoft.AspNetCore.SignalR.Client" preserve="all"/>
    <assembly fullname="Microsoft.AspNetCore.SignalR.Common" preserve="all"/>
    <assembly fullname="Microsoft.AspNetCore.SignalR.Protocols.Json" preserve="all"/>
    <assembly fullname="Microsoft.Bcl.AsyncInterfaces" preserve="all"/>
    <assembly fullname="Microsoft.Extensions.DependencyInjection.Abstractions" preserve="all"/>
    <assembly fullname="Microsoft.Extensions.DependencyInjection" preserve="all"/>
    <assembly fullname="Microsoft.Extensions.Logging.Abstractions" preserve="all"/>
    <assembly fullname="Microsoft.Extensions.Logging" preserve="all"/>
    <assembly fullname="Microsoft.Extensions.Options" preserve="all"/>
    <assembly fullname="Microsoft.Extensions.Primitives" preserve="all"/>
    <assembly fullname="System.Buffers" preserve="all"/>
    <assembly fullname="System.Diagnostics.DiagnosticSource" preserve="all"/>
    <assembly fullname="System.IO.Pipelines" preserve="all"/>
    <assembly fullname="System.Memory" preserve="all"/>
    <assembly fullname="System.Runtime.CompilerServices.Unsafe" preserve="all"/>
    <assembly fullname="System.Text.Encodings.Web" preserve="all"/>
    <assembly fullname="System.Text.Json" preserve="all"/>
    <assembly fullname="System.Threading.Channels" preserve="all"/>
    <assembly fullname="System.Threading.Tasks.Extensions" preserve="all"/>
</linker>
```

* [Unity - Manual: Managed code stripping](https://docs.unity3d.com/Manual/ManagedCodeStripping.html)