WPF messaging to Unity via M2MQTT - Error : Create can only be called from the main thread

Hi everyone, I rarely post in forums but this is something I can’t wrap my head around.
I’m trying to send a string path from a WPF application to Unity using MQTT. I’m using this library GitHub - vovacooper/Unity3d_MQTT: MQTT protocol running on Unity3d . So far I can get my path to Unity, that’s fine. However, when I try to call a method to open the path I receive, I get this error:

Here’s my code

using UnityEngine;
using System;
using System.Net;
using uPLibrary.Networking.M2Mqtt;
using uPLibrary.Networking.M2Mqtt.Messages;
using System.Text;

public class SimpleMQTT : MonoBehaviour
{

    // Host IP adress
    public string host;
    // Port number - 1883 by default for easy test
    public int port;
    //Is it secured or not
    public bool secured;
    //The topics you want to receive
    public string[] topicToSubscribe;

    //Reference to MQTT client
    private MqttClient client;

    //Debug variables
    private string publishTopic = "/Test";
    private string publishMessage = "This is a test from Unity";

    AudioFetch audioFetch;

    // Use this for initialization
    void Start()
    {
        audioFetch = GameObject.FindObjectOfType<AudioFetch>();
        //Connect
        Connect();
    }

    void Connect()
    {
        // create client instance
        client = new MqttClient(IPAddress.Parse(host), port, secured, null);

        //Register
        Register();
    }

    void Register()
    {
        // register to message received
        client.MqttMsgPublishReceived += Client_MqttMsgPublishReceived;
        string clientId = Guid.NewGuid().ToString();
        client.Connect(clientId);

        //Subscribe
        Subscribe();
    }


    private void Subscribe()
    {
        //Susbscribre to the topic specified // None by default
        for (int i = 0; i < topicToSubscribe.Length; i++)
        {
            client.Subscribe(new string[] { topicToSubscribe[i] }, new byte[] { MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });
        }
    }

    void Client_MqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e)
    {
        Debug.Log(Encoding.UTF8.GetString(e.Message));
        audioFetch.GetAudio(Encoding.UTF8.GetString(e.Message));
    }

    //for debug purpose // will send message

    void OnGUI()
    {
        if (GUI.Button(new Rect(20, 40, 80, 20), "Level 1"))
        {
            Debug.Log("Sending: " + publishMessage);
            Publish();
        }
    }

    void Publish()
    {
        client.Publish(publishTopic, Encoding.UTF8.GetBytes(publishMessage), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AudioFetch : MonoBehaviour {

    AudioSource audioSource;
    WWW www;

    // Use this for initialization
    void Start () {
        audioSource = gameObject.GetComponent<AudioSource>();
    }

    public void GetAudio(string path)
    {
        www = new WWW(path);
        audioSource.clip = www.GetAudioClip();
        audioSource.Play();
    }
}

The message says what happens.
Some Unity API call has been made on a different thread, probably due to the callback you’ve registered for the ‘MqttMsgPublishReceived’ event.

The library you’re using is not designed with Unity in mind, so you have to either avoid Unity API calls or dispatch / delay those in order to run that logic on the main thread.

So, in your case the following happens:
Data is received asynchronously and the ‘MqttMsgPublishReceived’ event will be raised on a seperate thread. Thus your subscribed method will be executed on this thread as well. That method attempts to access the audioFetch component by calling GetAudio, which is still okay… Technically you can access components and their members as long as no call to the Unity API is involved. There are some exceptions to this, such as Debug.Log, a few Utilities, some value types and a few other types.

However, WWW is apparently none of those, so that’s where it starts to fail when you create the WWW instance. This is part of the Unity API which is designed to only allow calls from the main thread.

1 Like

Yes, I mean I kind of understand the error really. I guess my question was more, how can I run my other method on the main thread, instead of on the same thread I receive the message.

You can write a dispatcher that’ll be triggered on the main thread, e.g. every n-th frame or every n seconds.
The dispatcher can either be event-based or interface based. You can then add to it from another thread and trigger the execution from the main thread and reset the list. All the operations should be implemented with thread-safety.

1 Like

Thank you for that pointer. I ended up using the dispatcher https://github.com/PimDeWitte/UnityMainThreadDispatcher and it works fine. In the mean time I also did a TCP communication system. It uses threads as well but it is possible to run a method on the main thread by using a bool in the update and switching it’s value when I receive data. I will still use the dispatcher as it is cleaner and MQTT gives me options to connect different kinds of devices.

1 Like