- C# 70.5%
- HTML 29.5%
UdonMQTTManager U#.asset was stale against its source: the field table was still at 23 entries and missing _editorInjectedLine and the other fields the manager has grown. Pure compiler output, no source change. |
||
|---|---|---|
| .forgejo/workflows | ||
| Documentation~ | ||
| Editor | ||
| Examples | ||
| Plugins | ||
| Prefabs | ||
| Udon | ||
| Editor.meta | ||
| Examples.meta | ||
| LICENSE | ||
| LICENSE.meta | ||
| package.json | ||
| package.json.meta | ||
| Plugins.meta | ||
| Prefabs.meta | ||
| README.md | ||
| README.md.meta | ||
| Udon.meta | ||
UdonMQTT
MQTT integration for VRChat worlds via UdonSharp. Bridges a real MQTT broker into a VRChat world -- commands leave via the Unity log file, messages arrive via keyboard stream -- with a full in-Editor driver for testing without leaving Unity.
Architecture
VRChat World (Udon) --log file--> Helper App <-MQTT-> Broker
VRChat World (Udon) <-keyboard-- Helper App
VRChat does not allow outbound network connections from Udon. UdonMQTT works around this using two separate channels:
- Egress (Udon -> Broker):
UdonMQTTManagerwrites structured commands to the Unity log (Debug.Log) using the[UdonMQTT]prefix. The helper app tails the Unity log file and executes the corresponding MQTT operations. - Ingress (Broker -> Udon): The helper app types base64-encoded JSON into the VRChat window via keyboard simulation.
UdonMQTTManagerreadsInput.inputStringeach frame, accumulates characters until a newline, then decodes and dispatches the message.
The helper app must be running on the same machine as VRChat.
Components
| Component | Description |
|---|---|
UdonMQTTManager (Udon) |
Core runtime. Writes commands to the Unity log (egress), reads keyboard-streamed JSON (ingress), handles ping/pong keepalive, and dispatches messages to subscriber scripts. |
UdonMQTTEditorDriver (Editor) |
In-Editor MQTT client. Connects to a real broker during Play Mode, intercepts [UdonMQTT] log lines, and sends responses directly into UdonMQTTManager -- no helper app needed for testing. |
UdonMQTT Manager.prefab |
Pre-configured prefab with UdonMQTTManager ready to drop into a scene. |
Setup
1. Place the prefab
Drop UdonMQTT Manager.prefab into your scene. Configure the inspector:
| Field | Description |
|---|---|
managerTopicPrefix |
MQTT topic prefix for manager status messages. Online/offline presence is published to <prefix>/<managerWorldId>/<md5(display name)> with payload keys status, managerWorldId, time (the player is identified only by the md5 hash in the topic, never by plain name). Default: udonmqtt/manager |
managerWorldId |
Unique identifier for this world/deployment (e.g. ef2026). Must match the helper app config. No spaces or special characters. |
whitelistedUsers |
VRChat display names of players allowed to drive the MQTT connection. Only the first matching player to join will be active. |
debugMode |
Enables verbose logging. |
2. Run the helper app
The helper app must be configured with the same managerWorldId and MQTT broker credentials, and must be running on the same machine as VRChat.
3. Authorization
Only players listed in whitelistedUsers will have an active UdonMQTTManager. For all other players the GameObject is disabled on join. The authorized player's client is the sole bridge between VRChat and the MQTT broker.
In-Editor Testing
The Editor MQTT Driver (OTT Labs -> UdonMQTT -> Editor MQTT Settings...) connects to a real MQTT broker during Unity Play Mode, eliminating the need for the helper app during development.
Connection settings
Configure host, port, TLS, and credentials in the settings window. Settings are saved to EditorPrefs and persist between sessions. Changes take effect on the next Play Mode entry.
Scenario testing tools
| Tool | What it does |
|---|---|
| Connection Flap | Repeatedly force-drops and reconnects to stress-test reconnect handling in your subscriber scripts. Configurable drop count and connected duration. |
| Message Blackhole | Silently discards all incoming MQTT messages before they reach Udon, without disconnecting. Useful for testing stale-state handling. |
| Ping Timeout Simulation | Suppresses the editor's PING response so Udon never receives the session key. Simulates an unresponsive helper app or bridge timeout. |
| Manual JSON Send | Sends raw JSON directly into UdonMQTTManager, bypassing MQTT entirely. Quick-fill buttons for msg, key, sub-ok, and unsub-ok message types. |
| Test Publish | Publishes a message to any topic from the editor. "Publish (no connection)" sends directly into Udon without going through the broker. |
| Active Subscriptions | Live view of all topic filters and their connection IDs. |
The driver auto-reconnects with exponential backoff (2s-60s) on unexpected disconnection and resubscribes all active topics after reconnect.
Using UdonMQTT in your own scripts
Call the following methods on UdonMQTTManager from any UdonSharpBehaviour. All methods are no-ops if the local player is not authorized or the connection is not yet established.
Subscribe
bool ok = mqttManager._u_MQTTSubscribe(connectionID, topic, this);
connectionID-- an integer slot (0-254). Each subscription occupies one slot.topic-- MQTT topic string (broker-side wildcards+and#are supported).- Returns
trueif the subscribe command was sent.
Your script receives the following events from UdonMQTTManager:
| Event | When |
|---|---|
_u_MQTTSubscribedTopic |
Broker confirmed subscription. |
_u_MQTTDataReceived |
A message arrived. Read mqttData (string) from the program variable. |
_u_MQTTUnsubscribedTopic |
Broker confirmed unsubscription. |
_u_MQTTResubscribedTopic |
Resubscription confirmed after _u_MQTTResubscribe. |
_u_HelperReconnected |
Helper app reconnected after a lost connection. Use this to re-issue subscriptions if needed. |
Resubscribe (change topic on an existing slot)
bool ok = mqttManager._u_MQTTResubscribe(connectionID, newTopic);
Publish
bool ok = mqttManager._u_MQTTPublish(topic, data, retain);
retain-- iftrue, the broker stores the message and delivers it to future subscribers.- Returns
trueif the command was sent (does not confirm broker receipt).
Unsubscribe
bool ok = mqttManager._u_MQTTUnsubscribe(connectionID, topic);
Protocol reference
This section is for helper app authors.
Outbound commands (Udon -> helper, via Unity log)
All log lines start with [UdonMQTT]. The session key is a 16-character random alphanumeric string exchanged at startup.
| Log line | Meaning |
|---|---|
[UdonMQTT] PING |
Heartbeat -- respond with the session key. |
[UdonMQTT] RESET |
Session started or ended -- reset all state. |
[UdonMQTT] ONLINE |
Authorized player came online. |
[UdonMQTT] <key> PUB <topic_b64> <data_b64> |
Publish (no retain). |
[UdonMQTT] <key> PUBR <topic_b64> <data_b64> |
Publish with retain. |
[UdonMQTT] <key> SUB <id> <topic_b64> |
Subscribe slot id to topic. |
[UdonMQTT] <key> UNSUB <id> |
Unsubscribe slot id. |
[UdonMQTT] <key> RESUB <id> <topic_b64> |
Move slot id to a new topic. |
Topics and payloads are base64-encoded UTF-16 LE strings.
Inbound messages (helper -> Udon, via keyboard stream)
Each message is a base64-encoded JSON object terminated by a newline character, typed into the VRChat window.
T field |
Other fields | Meaning |
|---|---|---|
"k" |
d: session key |
Session key / PING response. Triggers online state. |
"s" |
i: slot id, d: topic |
Subscribe confirmed. |
"r" |
i: slot id, d: topic |
Resubscribe confirmed. |
"u" |
i: slot id |
Unsubscribe confirmed. |
"m" |
i: slot id, d: payload |
Message received on topic subscribed to slot i. |
The manager validates the session key on all commands except "k". Up to 255 simultaneous slots are supported.