DEV Community

Masui Masanori
Masui Masanori

Posted on • Updated on

[Unity] Communicate with SignalR from Unity

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.

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.

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.

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

Copy dlls

After installing, I could get packages.
Alt Text

And there were some dlls for several platforms.
Alt Text

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

Communicate with SignalR

MainPage.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,
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

Message.cs

public class Message
{
    public string UserName { get; set; }
    public string Text { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

SignalRConnector.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}");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Alt Text

--- 2021-04-01 Update ---

IL2CPP

When I set "Scripting Backent" to "IL2CPP", I will get error.
Alt Text

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

link.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>
Enter fullscreen mode Exit fullscreen mode

Top comments (10)

Collapse
 
warriorwork profile image
WarriorWork • Edited

Thanks for great tutorial!
I get the error in picture..
dev-to-uploads.s3.amazonaws.com/up...
I have to use the newtonsoft.Json 8.0.0.0 because I use it for IBM plugin...

What can I do?

Thanks!

Collapse
 
masanori_msl profile image
Masui Masanori • Edited

Thank you for reading my post.

I have to use the newtonsoft.Json 8.0.0.0 because I use it for IBM plugin...
What can I do?

I think it's hard to resolve :(.
Because "Microsoft.AspNetCore.SignalR.Client" requires .NET Standard 2.0 compatibility from ver.1.0.0.

nuget.org/packages/Microsoft.AspNe...

But Newtonsoft.Json has supported .NET Standard 2.0 from ver.11.

github.com/JamesNK/Newtonsoft.Json...

So I think it's better to find out how to force to use Newtonsoft.Json version 11 or later.

Collapse
 
warriorwork profile image
WarriorWork

Hey I think I resolved the problem!
At the begining I was following this tutorial:
youtube.com/watch?v=ntqxSSWG66g
And he uses Newtonsoft... then I pay attenion to your post and you didnt use it.
So I was following your tutorial post and it works good I dont need to change my newtonsoft to any versions

Thank you!

Collapse
 
warriorwork profile image
WarriorWork

Hi!
In Mac OS it should work? Because when I run it on MAC I cannot connect to the Server by:
connection = new HubConnectionBuilder()
.WithUrl("localhost:5000/chatHub")
.Build();

The HubConnection.ConnectionID is empty. In others platforms it works but only in MacOS it doesn't work... Do you have any idea?

Collapse
 
rathsocheat profile image
Rath Socheat • Edited

I have problem when build for android.

  • Editor: Works
  • IOS: Works
  • Android: Errors

Environments

  • Unity ver.2022.3.11f1
  • Framework .netstandard ver 2.1
  • Microsoft.AspNetCore.SignalR.Client ver 7.0.0 [I already tried same version as yours it doesn't work too]

Image description

Error 1:

C:\Program Files\Unity\Hub\Editor\2021.3.11f1\Editor\Data\il2cpp\build\deploy\il2cpp.exe --convert-to-cpp --data-folder=D:/Nimets/Projects/KhmerChess/KhmerChessClient/Library/Bee/artifacts/Android/il2cppOutput/data --generatedcppdir=D:/Nimets/Projects/KhmerChess/KhmerChessClient/Library/Bee/artifacts/Android/il2cppOutput/cpp --enable-analytics --emit-null-checks --enable-array-bounds-check --dotnetprofile=unityaot-linux --profiler-report --profiler-output-file=D:/Nimets/Projects/KhmerChess/KhmerChessClient/Library/Bee/artifacts/il2cpp_conv_oudx.traceevents --print-command-line
Error: IL2CPP error (no further information about what managed code was being converted is available)
System.InvalidOperationException: Sequence contains no elements
at System.Linq.ThrowHelper.ThrowNoElementsException()
at System.Linq.Enumerable.First[TSource](IEnumerable
1 source)
at Unity.IL2CPP.DataModel.BuildLogic.DataModelBuilder.Initialize(UnderConstruction2& systemAssembly)
at Unity.IL2CPP.DataModel.BuildLogic.DataModelBuilder.Build()
at Unity.IL2CPP.Contexts.Components.DataModelComponent.Load(LoadSettings loadSettings, Boolean ownsTypeContext, Boolean ownsBuilder, DataModelBuilder& builder)
at Unity.IL2CPP.AssemblyConversion.Phases.InitializePhase.Run(AssemblyConversionContext context)
at Unity.IL2CPP.AssemblyConversion.Classic.ClassicConverter.Run(AssemblyConversionContext context)
at Unity.IL2CPP.AssemblyConversion.AssemblyConverter.ConvertAssemblies(AssemblyConversionInputData data, AssemblyConversionParameters parameters, AssemblyConversionInputDataForTopLevelAccess dataForTopLevel)
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)

Error 2:
Building D:\Nimets\Projects\KhmerChess\KhmerChessClient\Library\Bee\artifacts\unitylinker_xy1a.traceevents failed with output:
C:\Program Files\Unity\Hub\Editor\2021.3.11f1\Editor\Data\il2cpp\build\deploy\UnityLinker.exe --search-directory=D:/Nimets/Projects/KhmerChess/KhmerChessClient/Temp/StagingArea/Data/Managed --out=Library/Bee/artifacts/Android/ManagedStripped --include-link-xml=D:/Nimets/Projects/KhmerChess/KhmerChessClient/Temp/StagingArea/Data/Managed\MethodsToPreserve.xml --include-link-xml=D:/Nimets/Projects/KhmerChess/KhmerChessClient/Temp/StagingArea/Data/Managed\TypesInScenes.xml --include-link-xml=D:/Nimets/Projects/KhmerChess/KhmerChessClient/Temp/StagingArea/Data/Managed\SerializedTypes.xml --include-link-xml=D:\Nimets\Projects\KhmerChess\KhmerChessClient\Temp\burst.link.xml --include-link-xml=D:\Nimets\Projects\KhmerChess\KhmerChessClient\Assets\link.xml --include-link-xml=D:\Nimets\Projects\KhmerChess\KhmerChessClient\Assets\Packages\FacebookSDK\link.xml --include-link-xml=C:/Program Files/Unity/Hub/Editor/2021.3.11f1/Editor/Data/PlaybackEngines/AndroidPlayer/Tools/AndroidNativeLink.xml --include-directory=D:/Nimets/Projects/KhmerChess/KhmerChessClient/Temp/StagingArea/Data/Managed --profiler-report --profiler-output-file=D:/Nimets/Projects/KhmerChess/KhmerChessClient/Library/Bee/artifacts/unitylinker_xy1a.traceevents --dotnetprofile=unityaot-linux --dotnetruntime=Il2Cpp --platform=Android --use-editor-options --enable-engine-module-stripping --engine-modules-asset-file=C:/Program Files/Unity/Hub/Editor/2021.3.11f1/Editor/Data/PlaybackEngines/AndroidPlayer/modules.asset --editor-data-file=D:/Nimets/Projects/KhmerChess/KhmerChessClient/Temp/StagingArea/Data/Managed/EditorToUnityLinkerData.json --include-unity-root-assembly=D:/Nimets/Projects/KhmerChess/KhmerChessClient/Temp/StagingArea/Data/Managed/Assembly-CSharp.dll --include-unity-root-assembly=D:/Nimets/Projects/KhmerChess/KhmerChessClient/Temp/StagingArea/Data/Managed/Unity.RenderPipelines.Universal.Runtime.dll --include-unity-root-assembly=D:/Nimets/Projects/KhmerChess/KhmerChessClient/Temp/StagingArea/Data/Managed/Unity.TextMeshPro.dll --include-unity-root-assembly=D:/Nimets/Projects/KhmerChess/KhmerChessClient/Temp/StagingArea/Data/Managed/UnityEngine.UI.dll --include-unity-root-assembly=D:/Nimets/Projects/KhmerChess/KhmerChessClient/Temp/StagingArea/Data/Managed/Unity.RenderPipelines.Core.Runtime.dll --include-unity-root-assembly=D:/Nimets/Projects/KhmerChess/KhmerChessClient/Temp/StagingArea/Data/Managed/Facebook.Unity.Settings.dll --print-command-line --enable-analytics
Fatal error in Unity CIL Linker
System.NullReferenceException: Object reference not set to an instance of an object.
at Unity.Linker.Analytics.AnalyticsService.RecognizedReflectionAccessPattern(IMemberDefinition source, Instruction sourceInstruction, IMetadataTokenProvider accessedItem)
at Mono.Linker.Dataflow.ReflectionMethodBodyScanner.MarkTypeForDynamicallyAccessedMembers(ReflectionPatternContext& reflectionContext, TypeDefinition typeDefinition, DynamicallyAccessedMemberTypes requiredMemberTypes)
at Mono.Linker.Dataflow.ReflectionMethodBodyScanner.RequireDynamicallyAccessedMembers(ReflectionPatternContext& reflectionContext, DynamicallyAccessedMemberTypes requiredMemberTypes, ValueNode value, IMetadataTokenProvider targetContext)
at Mono.Linker.Dataflow.ReflectionMethodBodyScanner.ProcessGenericArgumentDataFlow(GenericParameter genericParameter, TypeReference genericArgument, IMemberDefinition source)
at Mono.Linker.Steps.MarkStep.MarkGenericArgumentConstructors(IGenericInstance instance, IMemberDefinition sourceLocationMember)
at Mono.Linker.Steps.MarkStep.MarkGenericArguments(IGenericInstance instance, IMemberDefinition sourceLocationMember)
at Mono.Linker.Steps.MarkStep.GetOriginalType(TypeReference type, DependencyInfo reason, IMemberDefinition sourceLocationMember)
at Mono.Linker.Steps.MarkStep.MarkType(TypeReference reference, DependencyInfo reason, IMemberDefinition sourceLocationMember)
at Mono.Linker.Steps.MarkStep.MarkInterfaceImplementation(InterfaceImplementation iface, TypeDefinition type)
at Mono.Linker.Steps.MarkStep.MarkInterfaceImplementations(TypeDefinition type)
at Mono.Linker.Steps.MarkStep.MarkRequirementsForInstantiatedTypes(TypeDefinition type)
at Mono.Linker.Steps.MarkStep.MarkType(TypeReference reference, DependencyInfo reason, IMemberDefinition sourceLocationMember)
at Mono.Linker.Steps.MarkStep.InitializeType(TypeDefinition type)
at Mono.Linker.Steps.MarkStep.InitializeAssembly(AssemblyDefinition assembly)
at Unity.Linker.Steps.UnityMarkStep.InitializeAssembly(AssemblyDefinition assembly)
at Mono.Linker.Steps.MarkStep.Initialize()
at Mono.Linker.Steps.MarkStep.Process(LinkContext context)
at Unity.Linker.Steps.UnityMarkStep.Process(LinkContext context)
at Unity.Linker.UnityPipeline.ProcessStep(LinkContext context, IStep step)
at Mono.Linker.Pipeline.Process(LinkContext context)
at Unity.Linker.UnityDriver.UnityRun(Boolean noProfilerAllowed, ILogger customLogger)
at Unity.Linker.UnityDriver.RunDriverWithoutErrorHandling(ILogger customLogger, Boolean noProfilerAllowed)
at Unity.Linker.UnityDriver.RunDriver()
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)

Error 3:
BuildFailedException: Incremental Player build failed!
UnityEditor.Modules.BeeBuildPostprocessor.PostProcess (UnityEditor.Modules.BuildPostProcessArgs args) (at <4e64905d831f4883a53259ef37fb023b>:0)
UnityEditor.Modules.DefaultBuildPostprocessor.PostProcess (UnityEditor.Modules.BuildPostProcessArgs args, UnityEditor.BuildProperties& outProperties) (at <4e64905d831f4883a53259ef37fb023b>:0)
UnityEditor.Android.AndroidBuildPostprocessor.PostProcess (UnityEditor.Modules.BuildPostProcessArgs args, UnityEditor.BuildProperties& outProperties) (at <5e58a3838afa4e88a08dc92f05003dcc>:0)
UnityEditor.PostprocessBuildPlayer.Postprocess (UnityEditor.BuildTargetGroup targetGroup, UnityEditor.BuildTarget target, System.Int32 subtarget, System.String installPath, System.String companyName, System.String productName, System.Int32 width, System.Int32 height, UnityEditor.BuildOptions options, UnityEditor.RuntimeClassRegistry usedClassRegistry, UnityEditor.Build.Reporting.BuildReport report) (at <4e64905d831f4883a53259ef37fb023b>:0)
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr, Boolean&)

Collapse
 
rathsocheat profile image
Rath Socheat

Problem solve by update editor to ver.2022.3.16f1

Collapse
 
garethjones99 profile image
garethjones99

Hey Masui,

Thanks for the tutorial,

I can get it working for the editor, however when I build for android I'm getting errors in the app when calling .Build on the HubConnectionBuilder. It's possibly related to the DependancyInjection lib

I've posted in more detail here on the unity forums and was wondering if you could share any insight as to what may be causing this or if you have experienced this error before.

Thanks

Collapse
 
garethjones99 profile image
garethjones99

For anyone else with this issue it was fixed using this work around github.com/dotnet/aspnetcore/issue...

Collapse
 
pereviader profile image
Pere Viader

One important problem is I can't use NuGet for Unity applications. So I must put dlls into the Assets/Plugins folder manually.

You might be interested in this project I did github.com/PereViader/CSharpProjec...
It might help you integrate any c# project as a unity package

Collapse
 
439_th_713063ff2bdf6d33bc profile image
439 th

This is cool, but I want to put in something that simply shows updating data from another website. Which one of these scripts does that? Do I need to use two of them?