UNet LLAPI WebGL message recieved has no data.

Hello.
Apologies if this is a newbie question, but I’ve been troubleshooting and scouring forums for the past few hours to no avail, so I figured that hopefully someone here can help.

I’m following this tutorial: Bounce Castle on Steam to get communications working between servers and clients.

This has been going well for the most part, and I can send and recieve messages between the server and client on windows builds, but on webgl I’m having one problem - The webgl client can send to the windows server and the message is received correctly. However the windows server cannot send to the webgl client. The message is sent with no errors, but the buffer is empty (All 0s.)

I was wondering if this is expected behaviour, and if not what I’m doing wrong.

I’ve attached the code used to get this result below:

using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
using UnityEngine.Networking;
using TMPro;

public class Manager : MonoBehaviour {

    public static Manager instance;

    private static bool ready;
    public static bool Ready { get { return ready; } }

    public int hostingPort;
    public int webhostingPort;
    public bool isServer;
    public string connectionIP;
    public TextMeshProUGUI infoText;

    int reliableId;
    int unreliableId;
    HostTopology topology;
    int hostId;
    int websocketId;
    int connectionId = -1;
    List<int> clientConnections = new List<int>();
    bool connected = false;

    void Start() {
        instance = this;
        // Initialize the Transport layer.
        NetworkTransport.Init();
        // Configure topology
        ConnectionConfig config = new ConnectionConfig();
        reliableId = config.AddChannel(QosType.Reliable);
        unreliableId = config.AddChannel(QosType.Unreliable);
        // At most 12 connected clients. Should be enough.
        topology = new HostTopology(config, 12);
        websocketId = NetworkTransport.AddWebsocketHost(topology, webhostingPort, null);
        hostId = NetworkTransport.AddHost(topology, hostingPort);
        infoText.text = "Socket Open. Socket ID is: " + hostId;
        Debug.Log("Socket Open. Socket ID is: " + hostId);
        Manager.ready = true;
    }

    public bool BecomeServer() {
        if (connectionId != -1) return false;
        Debug.Log("Becoming Server...");
        isServer = true;
        return true;
    }

    public bool ConnectToServer() {
        if (isServer) return false;
        Debug.Log("Attempting to Connect to Server...");
        infoText.text = "Attempting to Connect to Server...";
        isServer = false;
        byte error;
        #if UNITY_WEBGL
        connectionId = NetworkTransport.Connect(hostId, connectionIP, webhostingPort, 0, out error);
        #else
        connectionId = NetworkTransport.Connect(hostId, connectionIP, hostingPort, 0, out error);
        #endif
        if ((NetworkError)error == NetworkError.Ok) {
            Debug.Log("Connected to server. ConnectionId: " + connectionId);
            infoText.text = "Connected to server. ConnectionId: " + connectionId;
            connected = true;
        }
        else {
            Debug.Log("Failed to connect to server, with error: " + (NetworkError)error);
            infoText.text = "Failed to connect to server, with error: " + (NetworkError)error;
            connected = false;
        }
        return connected;
    }

    void Update() {
        // Handle messages from the queue.
        int recHostId;
        int recConId;
        int chanId;
        byte[] recBuffer = new byte[1024];
        int bufferSize = 1024;
        int dataSize;
        byte error;
        NetworkEventType recData = NetworkTransport.Receive(out recHostId, out recConId, out chanId, recBuffer, bufferSize, out dataSize, out error);
        switch (recData) {
            case NetworkEventType.Nothing: break;
            case NetworkEventType.ConnectEvent: {
                if (connectionId == recConId) {
                    connected = true;
                    Debug.Log("Connected!");
                } else {
                    if (isServer) {
                        Debug.Log("Recieved connection request from " + recConId);
                        clientConnections.Add(recConId);
                    }
                }
                break;
            }
            case NetworkEventType.DataEvent: {
                string res = "";
                for (int i=0; i<dataSize; i++)
                    res = res + recBuffer[i];
                infoText.text = dataSize + " Recieved Data: " + res;
                Stream stream = new MemoryStream(recBuffer);
                BinaryFormatter formatter = new BinaryFormatter();
                string message = formatter.Deserialize(stream) as string;
                Debug.Log("Recieved Data: " + message);
                if (isServer) {
                    Debug.Log("Sending confirmation...");
                    SendToClient(recHostId, recConId);
                }
                break;
            }
            case NetworkEventType.DisconnectEvent: {
                if (connectionId == recConId) {
                    connected = false;
                    Debug.Log("Connection Failed!");
                } else {
                    // One of the established connections has disconnected.
                    Debug.Log("Dead!");
                }
                break;
            }
            case NetworkEventType.BroadcastEvent: {
                Debug.Log("Broadcast recieved!");
                break;
            }
        }

        if (Input.GetKeyDown(KeyCode.S)) BecomeServer();
        if (Input.GetKeyDown(KeyCode.C)) ConnectToServer();
        if (Input.GetKeyDown(KeyCode.D)) SendCmd();
    }

    public void SendCmd() {
        Debug.Log("Sending...");
        byte error;
        byte[] buffer = new byte[1024];
        Stream stream = new MemoryStream(buffer);
        BinaryFormatter formatter= new BinaryFormatter();
        formatter.Serialize(stream, "Hello from Client");
        int bufferSize = 1024;
        NetworkTransport.Send(hostId, connectionId, reliableId, buffer, bufferSize, out error);
        if ((NetworkError)error != NetworkError.Ok)
            Debug.Log("Failed to send command. Send failed with error: " + (NetworkError)error);
        else Debug.Log("Sent with no errors!");
    }

    public void SendToClient(int hid, int clientId) {
        byte error;
        byte[] buffer = new byte[1024];
        Stream stream = new MemoryStream(buffer);
        BinaryFormatter formatter= new BinaryFormatter();
        formatter.Serialize(stream, "Hello from Server");
        int bufferSize = 1024;
        NetworkTransport.Send(hid, clientId, reliableId, buffer, bufferSize, out error);
        if ((NetworkError)error != NetworkError.Ok)
            Debug.Log("Failed to send command. Send failed with error: " + (NetworkError)error);
        else Debug.Log("Sent with no errors!");
    }

}

If you wanna get it running:
On the server, press s.
On the client, press c.
On the client, press d. This sends a message to the server. The server then responds with it’s own message.

The result of info text on the webGL client after this is:
1024 Received Data: 000000000000000…
And on the windows server:
1024 Received Data: 0018238123… (You get the idea).

UNET’s built in WebGL networking has a couple of bugs that were never fixed. It’s also closed source, so there isn’t much you can really do about it.

Try Mirror, our open source UNET fork. We have an open source Websocket transport - and you can even play with WebGL and Desktop at the same time :slight_smile:

1 Like

Awesome, guess I’ll have to try that. Just so I’m understanding this correctly, the Telepathy module would be the same message passing thing I’m looking for? And there’d be no difference between the code for windows and the code for webgl?

@mischa2k it seems I’m having a similar problem with this tool, although I trust this is probably my fault this time.
I’m using the following variation on the sample code provided for telepathy, and while it builds fine in WebGL, I can’t even connect to the windows server instance:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using UnityEngine;
using TMPro;

public class Manager : MonoBehaviour {

    public static Manager instance;

    private static bool ready = false;
    public static bool Ready { get { return ready; } }

    public int hostingPort;
    public bool isServer;
    public string connectionIP;
    public TextMeshProUGUI infoText;

    Telepathy.Server server = new Telepathy.Server();
    Telepathy.Client client = new Telepathy.Client();

    int hostId;
    List<int> clientConnections = new List<int>();
    bool connected = false;

    void Start() {
        instance = this;
    }

    public bool BecomeServer() {
        if (Manager.ready && connected) return false;
        Debug.Log("Becoming Server...");
        server.Start(hostingPort);
        isServer = true;
        Manager.ready = true;
        connected = true;
        return true;
    }

    public bool ConnectToServer() {
        if (Manager.ready && connected) return false;
        Debug.Log("Attempting to Connect to Server...");
        infoText.text = "Attempting to Connect to Server...";
        client.Connect(connectionIP, hostingPort);
        Debug.Log("Connected to server.");
        infoText.text = "Connected to server.";
        isServer = false;
        Manager.ready = true;
        connected = true;
        return true;
    }

    void Awake() {
        Application.runInBackground = true;
        Telepathy.Logger.Log = Debug.Log;
        Telepathy.Logger.LogWarning = Debug.LogWarning;
        Telepathy.Logger.LogError = Debug.LogError;
    }

    void Update() {
        // Handle messages from the queue.
        if (Input.GetKeyDown(KeyCode.S)) BecomeServer();
        if (Input.GetKeyDown(KeyCode.C)) ConnectToServer();
        Telepathy.Message msg;
        if (!Manager.ready) return;
        while (true) {
            bool res;
            if (isServer)
                res = server.GetNextMessage(out msg);
            else
                res = client.GetNextMessage(out msg);
            if (!res) break;
            switch(msg.eventType) {
                case Telepathy.EventType.Connected:
                    if (isServer) {
                        Debug.Log(msg.connectionId + "Connected!");
                        infoText.text = msg.connectionId + "Connected!";
                        clientConnections.Add(msg.connectionId);
                    } else {
                        Debug.Log("Connected!");
                        infoText.text = "Connected!";
                    }
                    break;
                case Telepathy.EventType.Data:
                    Debug.Log("Received Data :" + BitConverter.ToString(msg.data));
                    infoText.text = "Received Data :" + BitConverter.ToString(msg.data);
                    if (isServer) {
                        Debug.Log("Sending back...");
                        SendToClient(msg.connectionId);
                    }
                    break;
                case Telepathy.EventType.Disconnected:
                    Debug.Log("Disconeccted");
                    infoText.text = "Disconnected";
                    break;
            }
        }
        if (Input.GetKeyDown(KeyCode.D)) SendCmd();
    }

    public void SendCmd() {
        Debug.Log("Sending...");
        byte[] buffer = new byte[1024];
        Stream stream = new MemoryStream(buffer);
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, "Hello from Client");
        client.Send(buffer);
        infoText.text = "Sent!";
    }

    public void SendToClient(int clientId) {
        byte[] buffer = new byte[1024];
        Stream stream = new MemoryStream(buffer);
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, "Hello from Server");
        server.Send(clientId, buffer);
        infoText.text = "Sent to Client";
    }

    void OnApplicationQuit() {
        if (client != null)
            client.Disconnect();
        if (server != null)
            server.Stop();
    }

}

Produces the following error on client.Connect(…) (Only in WebGL version)

SocketException: Success
  at System.Net.Sockets.Socket..ctor (System.Net.Sockets.AddressFamily addressFamily, System.Net.Sockets.SocketType socketType, System.Net.Sockets.ProtocolType protocolType) [0x00000] in <00000000000000000000000000000000>:0
(Filename: currently not available on il2cpp Line: -1)

2f9efc2b-788c-499d-a096-25a7de0dbc97:3394:11
    _JS_Log_Dump blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97:3394
    __Z19WebGLPrintfConsolev7LogTypePKcPi blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97 line 1392 > WebAssembly.instantiate:1912004
    __ZL20InternalErrorConsolePKcz blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97 line 1392 > WebAssembly.instantiate:1918679
    __Z40DebugStringToFilePostprocessedStacktraceRK21DebugStringToFileData blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97 line 1392 > WebAssembly.instantiate:1917606
    __Z17DebugStringToFileRK21DebugStringToFileData blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97 line 1392 > WebAssembly.instantiate:1906986
    __ZN9Scripting23LogExceptionFromManagedE21ScriptingExceptionPtriPKcb blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97 line 1392 > WebAssembly.instantiate:1987143
    __ZN19ScriptingInvocation6InvokeEP21ScriptingExceptionPtrb blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97 line 1392 > WebAssembly.instantiate:1985646
    __ZN13MonoBehaviour16CallUpdateMethodEi blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97 line 1392 > WebAssembly.instantiate:4261511
    __ZN13MonoBehaviour6UpdateEv blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97 line 1392 > WebAssembly.instantiate:4256674
    __ZN20BaseBehaviourManager12CommonUpdateI16BehaviourManagerEEvv blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97 line 1392 > WebAssembly.instantiate:5372631
    __ZN16BehaviourManager6UpdateEv blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97 line 1392 > WebAssembly.instantiate:5374615
    __ZZ23InitPlayerLoopCallbacksvEN41UpdateScriptRunBehaviourUpdateRegistrator7ForwardEv blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97 line 1392 > WebAssembly.instantiate:4503678
    __Z17ExecutePlayerLoopP22NativePlayerLoopSystem blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97 line 1392 > WebAssembly.instantiate:4320024
    __Z17ExecutePlayerLoopP22NativePlayerLoopSystem blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97 line 1392 > WebAssembly.instantiate:4320045
    __Z10PlayerLoopv blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97 line 1392 > WebAssembly.instantiate:4316330
    __ZL8MainLoopv blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97 line 1392 > WebAssembly.instantiate:4308511
    dynCall_v blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97 line 1392 > WebAssembly.instantiate:16639170
    dynCall_v blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97:26169
    browserIterationFunc blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97:9504
    runIter blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97:9606
    Browser_mainLoop_runner blob:null/2f9efc2b-788c-499d-a096-25a7de0dbc97:9542

Any help would be much appreciated.

Telepathy is our TCP transport, which won’t work in WebGL.
You will need to try our Websocket transport. You can find it on Github and in our Discord (see my signature).
If you join our Discord then that’s probably easiest and you can get help from the Websocket guy there too :slight_smile:

2 Likes