03 — Godot C¶
← Table Of Contents | Previous: Unity
This document explains how to use Zlink.Stream.Connector from a Godot 4 C# project
(Mono/.NET build). There's no dedicated Godot connector package. It uses the general .NET
connector as-is, calling Dispatch.Async() on the Godot main thread to run the user callbacks.
A Godot project using C++ GDExtension should see C++ Guide 09 — Engine Adapters instead of this document. For a Web build, see the Node/TypeScript Connector Guide.
Basic Principle¶
Godot's Node and the scene tree must not be touched off the main thread. So the connector's
default dispatch mode is Manual. In this mode, the On(...) handler, error event, disconnect
event, and request callback all run on the thread that called Dispatch.Async().
In Godot, Dispatch.Async() is called in Node._Process(double). That runs the callbacks
accumulated for that frame on the Godot main thread.
Node Example¶
using System;
using System.Threading.Tasks;
using Godot;
using Systems.Zlink.Stream.Connector;
using Systems.Zlink.Stream.Connector.Contracts;
public partial class ZlinkStreamClientNode : Node
{
private IZlinkStreamConnector? _connector;
public override async void _Ready()
{
_connector = ZlinkStreamConnectorFactory.Create(new ZlinkStreamConnectorOptions
{
Endpoint = new Uri("wss://example.com/stream")
});
_connector.ConnectionStateChanged += (change, _) =>
{
GD.Print($"ZLink stream state: {change.Current}");
return ValueTask.CompletedTask;
};
_connector.On("game.update", (message, _) =>
{
GD.Print($"packet: {message.Name}, bytes: {message.Payload.Payload.Length}");
return ValueTask.CompletedTask;
});
await _connector.Connect.Async();
}
public override async void _Process(double delta)
{
if (_connector is not null)
{
await _connector.Dispatch.Async();
}
}
public override async void _ExitTree()
{
if (_connector is not null)
{
await _connector.Close.Async();
await _connector.DisposeAsync();
_connector = null;
}
}
}
If Dispatch.Async() isn't called in _Process(), the handler and events don't run. Check
PendingDispatchCount to see how many callbacks haven't been processed yet.
Wiring Up With A Godot Signal¶
To notify the whole scene of an incoming packet, emit a signal inside the handler. Since the handler already runs on the main thread, no additional marshalling is needed.
[Signal]
public delegate void GameUpdatedEventHandler(string packetName);
// Inside _Ready()
_connector.On("game.update", (message, _) =>
{
EmitSignal(SignalName.GameUpdated, message.Name);
return ValueTask.CompletedTask;
});
Pausing And Shutdown¶
Switching Godot's Node.ProcessMode to Disabled stops _Process(), so dispatch also stops. The
connection is kept, but callbacks pile up in the queue. To close the connection too, explicitly call
Close.Async().
If Close.Async() and DisposeAsync() aren't called in _ExitTree(), the background receive loop
is left behind.
What Is Not A Contract¶
The code in this document is Godot usage, not a connector contract. The connector itself doesn't depend on Godot types. The common meaning of asynchronous execution follows the Common Framework Policy.