nats-io/nats.net.v2

Cant get this to compile in Unity 2022.3.14

Opened this issue ยท 27 comments

Observed behavior

Fails because of dependency on System.Text.Json.
The project is targeted at Android / .Net Standard 2.1. Mono.
I downloaded the .nuget package, unzipped it, and dropped the NATS.Client.Core DLL into the Plugins folder.

Expected behavior

It would be great to use the official library in Unity 3d. Perhaps I am doing something wrong.

Server and client version

I was using the latest Nats.Client.Core.2.0.2

Host environment

No response

Steps to reproduce

Add to unity project, try and use it.

mtmk commented

I've never used Unity before so I'm not sure. How would you even get net-6.0/8.0 targeted library to work with Unity? (I believe AlterNats was used in Unity projects and that was targeting net-6, so it must be possible)

Did you try adding System.Text.Json DLL as well?

https://www.nuget.org/packages/System.Text.Json

Yes, I did. That failed because of some weird incompatibility with Unity. I can't recall the error of the top of my head.

In the meantime I am using the v1 client, but I would prefer to use the latest and greatest here instead of doing all the threading / task management myself with v1.

I am wondering if someone else in the Unity community has managed to make this work.

mtmk commented

I did ask around but haven't heard back yet.

One thing I noticed is the target in Unity settings says netstandard 2.1. But we're targeting 6 and 8 at the moment. Having said that the AlterNats project (which we forked from) was targeting net6 as well and they used it for Unity.

When I tried I get a System.Runtime version mismatch error. How did you get pass that?

After doing a bunch of reading, and trying manually installing etc, i gave up and went back to the v1 library instead, and am using that in unity with no problems.

i also looked into installing all the code manually, and due to the way the basic code structures, I decided it was way too much work to refactor it all

just seems a shame not to be able to use the latest version.

not complaining :)

mtmk commented

Sorry about that @adamgoodrich it looks like we need to target netstandard-2.1. I gave it a go trying to see if it's easy enough but seemed like quite a bit of work. We quite consciously decided early on only to target .NET TLS releases maintenance overhead of other targets being one of the reasons.

Good news is there seems to be a way as Andrew Laing pointed out:
https://natsio.slack.com/archives/C1V81FKU6/p1704843867736829?thread_ts=1704450461.895889&cid=C1V81FKU6

Looks like AlterNats (parent fork) project was using MacigOnion RPC framework in between:
https://github.com/Cysharp/alternats?tab=readme-ov-file#architecture-guide

I believe this is the general idea:

                            gRPC                                NATS
[Unity + MagicOnion Client]------[MagicOnion Server + NATS.Net]------[NATS server]
      netstandard-2.1                   net6.0 / net8.0

Please let us know if this is a solution so we can suggest to other Unity devs.

Just a heads up - I've discussed briefly with @mtmk about Unity support. I'm going to push a PR to the .Net Client here soon that will unblock the latest version of the client to work with netstandard2.0 projects and that will allow us to use NATS client directly on Unity. Will also look into the possibility to make it more AoT friendly so it can work with scenarios like iOS and Android but this is just an attempt as I don't see NATS as being something publicly exposed to the internet for mobile clients but nonetheless, AoT support is good in general.

I'll link this issue to the PR when I get there in the following weeks.

mtmk commented

update: we've just released a preview with .NET Standard 2.0 and 2.1 support

https://github.com/nats-io/nats.net.v2/releases/tag/v2.3.0-preview.1

Thank you for your help @galvesribeiro ๐Ÿ’ฏ without your push we wouldn't've started this effort!

If you can check the new release out and give us feedback, that'd be awesome!

thanks again!

Hello!! That is awesome news @mtmk! Thank you very much for doing it!

I'm going to try this out with the existing Unity project and let you know if we have any problems.

Thank you!

mtmk commented

Update: Downloaded old version somehow! This is only an issue I saw in 2021.3.29f1. When I tried on 2022.3.36f1 All worked fine. (still and issue with mono 6.12, but Unity folks must've patched it in later releases)

I'm probably missing something, but tried on a simple unity project, unfortunately netstandard2.1 isn't working. netstandard2.0 seems ok (only tired a ping so far). Unity seems to have an issue with socket.ReceiveAsync(Memory<byte>) system calls (which we use):

async void Start()
{
    // Does not work!
    {
        var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        var addresses = await Dns.GetHostAddressesAsync("google.com");
        await socket.ConnectAsync(addresses, 80);
        
        Memory<byte> sendBuffer = Encoding.ASCII.GetBytes("GET / HTTP/1.0\r\n\r\n");
        await socket.SendAsync(sendBuffer, SocketFlags.None);
        
        Memory<byte> memory = new byte[15];
        var read = await socket.ReceiveAsync(memory, SocketFlags.None); // <--- Fills memory with zeros
        
        if (read > 0)
        {
            Debug.Log($"Received Memory({read}): {Encoding.ASCII.GetString(memory.Span.Slice(0, read))}");
        }
    }

    // Works!
    {
        var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        var addresses = await Dns.GetHostAddressesAsync("google.com");
        await socket.ConnectAsync(addresses, 80);
        
        Memory<byte> sendBuffer = Encoding.ASCII.GetBytes("GET / HTTP/1.0\r\n\r\n");
        await socket.SendAsync(sendBuffer, SocketFlags.None);
        
        ArraySegment<byte> segment = new byte[15];
        var read = await socket.ReceiveAsync(segment, SocketFlags.None); <--- works
        
        if (read > 0)
        {
            Debug.Log($"Received ArraySegment({read}): {Encoding.ASCII.GetString(segment.Slice(0, read))}");
        }
    }
        

@mtmk what is the error you are seeing?

image image

It worked just fine for me o both netcore2.1 and net.

mtmk commented

@galvesribeiro ah, thank you this is great. it must be something I'm doing causing the issue. I'm not seeing any errors, socket read is returning the correct number of bytes read but memory is filled with zeros.

image

This is my first time using Unity. I have a feeling I might've done something wrong here putting all required DLLs (netstandard2.1 version if available 2.0 if not):
image

here is the list:

> ls *.dll

    Directory: C:\Users\mtmk\My project\Assets\Plugins

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---          31/10/2023    13:58          19104 Microsoft.Bcl.AsyncInterfaces.dll
-a---          14/02/2024    22:58          64176 Microsoft.Extensions.DependencyInjection.Abstractions.dll
-a---          06/06/2024    19:56          95408 Microsoft.Extensions.DependencyInjection.dll
-a---          14/02/2024    22:54          67760 Microsoft.Extensions.Logging.Abstractions.dll
-a---          31/10/2023    14:10          53008 Microsoft.Extensions.Logging.dll
-a---          18/01/2024    02:12          68880 Microsoft.Extensions.Options.dll
-a---          31/10/2023    14:01          49936 Microsoft.Extensions.Primitives.dll
-a---          01/05/2024    15:40         433680 Microsoft.VisualStudio.Threading.dll
-a---          06/09/2023    18:30          37904 Microsoft.VisualStudio.Validation.dll
-a---          04/07/2024    01:14         477696 NATS.Client.Core.dll
-a---          01/05/2024    18:12         228536 Nerdbank.Streams.dll
-a---          19/03/2024    18:38         175280 System.Diagnostics.DiagnosticSource.dll
-a---          31/10/2023    14:03          84128 System.IO.Pipelines.dll
-a---          22/10/2021    23:40          18024 System.Runtime.CompilerServices.Unsafe.dll
-a---          31/10/2023    14:01          79120 System.Text.Encodings.Web.dll
-a---          14/02/2024    22:57         607392 System.Text.Json.dll
-a---          31/10/2023    14:01          75952 System.Threading.Channels.dll

(I added Microsoft.VisualStudio.* and Nerdbank.Streams when I tried it with NATS.Client.Core netstandard2.0)

mtmk commented

I just tried it with mono, it doesn't work with that either :-(

steps to repro:

  • Download mono (x64 or x86, tried both didn't make difference)
  • Open mono cmd
  • Create Program.cs
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

// WORKS
var socket1 = await Connect();
ArraySegment<byte> segment = new byte[15];
var read1 = await socket1.ReceiveAsync(segment, SocketFlags.None); // <--- works
Console.WriteLine($"Received ArraySegment({read1}): {Encoding.ASCII.GetString(segment.Slice(0, read1))}");

// DOES NOT WORK
var socket2 = await Connect();
Memory<byte> memory = new byte[15];
var read2 = await socket2.ReceiveAsync(memory, SocketFlags.None); // <--- memory isn't filled
Console.WriteLine($"Received Memory({read2}): {Encoding.ASCII.GetString(memory.Span.Slice(0, read2))}");

async Task<Socket> Connect()
{
    var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    var addresses = await Dns.GetHostAddressesAsync("google.com");
    await socket.ConnectAsync(addresses, 80);
    Memory<byte> sendBuffer = Encoding.ASCII.GetBytes("GET / HTTP/1.0\r\n\r\n");
    await socket.SendAsync(sendBuffer, SocketFlags.None);
    return socket;
}
  • Compile and run:
> csc Program.cs
> mono Program.exe
Received ArraySegment(15): HTTP/1.0 200 OK
Received Memory(15):                         <-- NO OUTPUT!

if this is the mono code memory.ToArray() obv isn't going to work:
https://github.com/mono/mono/blob/0cbf0e290c31adb476f9de0fa44b1d8829affa40/mcs/class/System/System.Net.Sockets/SocketTaskExtensions.cs#L254-L267

@galvesribeiro are you targeting something other than mono / windows exe? I'm on windows 11 btw.

Edit: just found this issue: mono/mono#20902

Edit2: tested on Windows x64, x86 and macOS Intel 64 with same results

Interesting. I just created an empty Unity project which defaults to Mono which defaults to Mono with .NET Standard 2.1.

image

I'm on MacOS btw.

In regards to copy the DLLs, I would strongly advise to not do it. Use the NuGetForUnity extension. It adds a package explorer and the proper nuget dependency support. That way you just add it to the project using Unity Editor so the assemblies will be available to use in code.

mtmk commented

Interesting. I just created an empty Unity project which defaults to Mono which defaults to Mono with .NET Standard 2.1.

image I'm on MacOS btw.

just tried on macos worked for me as well. seems to be an issue on windows

@mtmk
just tried on macos worked for me as well. seems to be an issue on windows

Interesting. I wonder what may be different on Windows.

I tried now to add the package thru NuGet but weirdly I'm getting this:

image

It is adding dependencies on "Nullable":

image

That shouldn't be an explicit dependency. Weird...

I've manually deleted the restored folder for that and will move with some tests.

mtmk commented

@galvesribeiro
That [Nullable] shouldn't be an explicit dependency. Weird...

You're right! just had the same issue. I made a mistake in package-ref. just put a fix in a few minutes ago:

I've manually deleted the restored folder for that and will move with some tests.

That worked for me too

@mtmk Just added the nuget package (after delete the Nullable for now) and run this simple test:

image

Apparently everything is working just fine.

It is important to note that I don't believe the Nats client will work out of the box with game clients, like ones targeting iOS/Android or any AOT-based platform.

I mean, maybe it will, but I haven't had time to test it appropriately and idk how AOT-friendly Nats.Net package is right now.

This simple sample is running under Mono with .Net Core 2.1 (defaults) which mean Unity Servers should work anywhere.

mtmk commented

@galvesribeiro
It is important to note that I don't believe the Nats client will work out of the box with game clients, like ones targeting iOS/Android or any AOT-based platform.

I mean, maybe it will, but I haven't had time to test it appropriately and idk how AOT-friendly Nats.Net package is right now.

We've been quite strict developing and testing for AOT but done it on net8.0 only. To be fair I haven't heard any reports on AOT (positive or negative) so I can't be 100% sure.

This simple sample is running under Mono with .Net Core 2.1 (defaults) which mean Unity Servers should work anywhere.

that'd be great if we can confirm that at some stage.

The AOT build fail:

image

The normal (Mono) build on the server mode just work as expected:

image
mtmk commented

Good news with server mode ๐Ÿ‘

How can we test AOT? just using il2ccp in project settings?

Yeah.

image

Select "Dedicated Server" and switch platform. Then click on "Player Settings"

image

Then on the Dedicated Server settings, change the "Scripting Backend" to IL2CPP.

image

You can play with other settings as well but this should be enough to trigger the error.

I'm only suggesting to use the dedicated server mode because it will be easier for you to run. If you have the regular player it will probably require you to have an emulator installed for the target platform to make it work.

I'm interesting on see this working with IL2CPP. Would be great to be able to AOT this not because of limitation of the platform on the server OSes but because of the startup time improvement and specially the size of the final binary. Those are very important in gaming since we usually span hundreds or thousands of containers all the time and whatever we can save in startup time and size, would be great ๐Ÿ˜ƒ

Nonetheless, I'm happy to confirm that at least on Mono the Nats.Client package works perfectly ๐Ÿ˜„

mtmk commented

(updated to comment up top as well)

Re socket receive issue. It turns out it was an old Unity version 2021 I installed which was the cause of it all. it worked fine on 2022. Nothing to do with windows/mac!

Nice to see. Yeah, mono kinda got a bit stale after MSFT bought Xamarin and .Net became OSS, so all efforts are more or less dedicated on the main runtime. I'm pretty sure Unity uses their own fork of Mono as of Today.

There is a saying in the Unity world that eventually they are going to drop usage of Mono and adopt the regular .Net runtime. We are all waiting for it.

mtmk commented

update: using NATS .NET v2.3.1-preview.2 I was able to use NuGet without nay issues (don't forget to select 'Show preview releases'), run a simple core pub/sub and build with il2cpp on Unity 2022.3.36f1