TezSDK 0.0.2

dotnet add package TezSDK --version 0.0.2
                    
NuGet\Install-Package TezSDK -Version 0.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TezSDK" Version="0.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TezSDK" Version="0.0.2" />
                    
Directory.Packages.props
<PackageReference Include="TezSDK" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TezSDK --version 0.0.2
                    
#r "nuget: TezSDK, 0.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TezSDK@0.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TezSDK&version=0.0.2
                    
Install as a Cake Addin
#tool nuget:?package=TezSDK&version=0.0.2
                    
Install as a Cake Tool

TezSDK

.NET client SDK for the Tez realtime UDP engine. Pure managed C# — no native dependencies, no FFI. Works on .NET 10+ across Windows, macOS, Linux, iOS, Android, and any platform supporting .NET.

Features

  • Wire-protocol v2 — full encode/decode of every Tez message type
  • State machine driver — handshake, heartbeat, reconnect with exponential backoff, cluster redirect
  • Ack tracking — cumulative bitmask acks for the reliable channel
  • World mirror — local snapshot of all players from state deltas
  • Built-in UDP transport — background IO thread, event queue for main-thread polling
  • 2D + 3D support — position, velocity, facing, 3D position, 3D velocity, quaternion rotation, scale

Quick Start

1. Add the project reference

In your .csproj:

<ItemGroup>
  <ProjectReference Include="..\TezSDK\TezSDK.csproj" />
</ItemGroup>

2. Connect to a server

using TezSDK;

// Connect and auto-join a room
var client = TezClient.Connect("127.0.0.1:9000", room: "lobby");

3. Handle events

Subscribe to the OnEvent callback (fires from the IO thread) or poll from your game loop:

// Option A: callback (runs on IO thread — dispatch to main thread if needed)
client.OnEvent = ev =>
{
    switch (ev)
    {
        case ClientEvent.Connected(var peerId, var tickRate):
            Console.WriteLine($"Connected as peer {peerId}, tick rate: {tickRate}");
            break;

        case ClientEvent.RoomJoined(var room):
            Console.WriteLine($"Joined room {room}");
            break;

        case ClientEvent.Game(var gameEvent):
            HandleGameEvent(gameEvent);
            break;

        case ClientEvent.Disconnected(var reason):
            Console.WriteLine($"Disconnected: {reason}");
            break;

        case ClientEvent.RttMicros(var micros):
            Console.WriteLine($"RTT: {micros}µs");
            break;
    }
};

// Option B: poll from your game loop (recommended for game engines)
void Update()
{
    var events = client.PollEvents();
    foreach (var ev in events)
    {
        // process events on the main thread
    }
}

4. Send data

// Unreliable movement input (rate-limited by server)
client.SendInput(vx: 1.0f, vy: 0.5f, facing: 1.57f);

// Reliable action (target 0 = broadcast to room)
client.SendAction(kind: 1);

// Reliable chat message
client.SendChat("Hello from .NET!");

// Reliable custom event (max 256 bytes payload)
client.SendCustom(kind: 7, data: [0x01, 0x02, 0x03]);

5. Read the world mirror

foreach (var (peer, state) in client.World)
{
    Console.WriteLine($"Peer {peer}: pos={state.Pos}, facing={state.Facing:F2}");
    // 3D: state.Pos3D, state.Vel3D, state.Rotation, state.Scale
}

6. Disconnect

client.Dispose();

Room Management

// Join a different room at any time
client.JoinRoom("arena-2");

// Leave the current room
client.LeaveRoom();

Connection Lifecycle

The client goes through these phases (ClientPhase enum):

Phase Description
Disconnected Not connected, no IO running
Handshaking Sending Hello, waiting for Welcome
Joining Welcome received, join request sent
Connected Fully connected, can send/receive
Redirecting Cluster redirect in progress
Reconnecting Connection lost, exponential backoff

Automatic Reconnection

When the server stops responding (idle timeout, default 10s), the client automatically enters Reconnecting with exponential backoff (400ms → 800ms → 1.6s → ... capped at 8s). The remembered room is re-joined automatically on recovery.

Cluster Redirects

If the requested room lives on another node, the server sends a RoomRedirect. The SDK handles this transparently: rebinds the UDP socket to the new node and re-handshakes, keeping the requested room.

Authentication

Servers started with --auth-key <hex> require a 48-byte HMAC-SHA256 token:

nonce (u64 LE) | expires_at (u64 LE, unix secs) | HMAC-SHA256(nonce | expiry)

Your backend mints tokens; pass the bytes to TezClient.Connect:

var token = GetTokenFromBackend(); // 48-byte HMAC-SHA256
var client = TezClient.Connect("myserver:9000", room: "lobby", token: token);

Without an auth key (dev/LAN mode), any token — including none — is accepted.

Configuration

Pass a TezConfig for fine-grained control:

var cfg = new TezConfig
{
    Room = "lobby",
    Token = [],
    HelloRetry = TimeSpan.FromMilliseconds(400),
    IdleTimeout = TimeSpan.FromSeconds(10),
    Heartbeat = TimeSpan.FromSeconds(2),
    ReconnectMax = TimeSpan.FromSeconds(8),
    MaxRedirects = 4,
};

var client = TezClient.Connect("127.0.0.1:9000", cfg);
Property Default Description
Token [] Auth token bytes
Room null Room to auto-join after handshake
HelloRetry 400ms Interval between Hello retries
IdleTimeout 10s No server traffic → connection lost
Heartbeat 2s Ping interval while connected
ReconnectMax 8s Backoff cap for reconnect attempts
MaxRedirects 4 Max cluster redirect hops

Events Reference

Client Events (ClientEvent)

Event Payload Description
Connected PeerId, TickRate Handshake accepted
RoomJoined Room Room membership confirmed
Redirected Addr (string) Cluster redirect to new node
Disconnected Reason enum Session ended (Stopped / Timeout / RedirectLoop)
Reconnecting Attempt (uint) Reconnect attempt started
Game GameEvent Room event (see below)
RttMicros Micros (ulong) Heartbeat round-trip time

Game Events (GameEvent)

Event Payload Description
Joined Peer, Room Player entered the room
Left Peer, Room, Reason Player left (Voluntary / Timeout)
Action Peer, Room, Kind, Target Gameplay action from another peer
Chat Peer, Room, Text Chat message from another peer
Custom Peer, Room, Kind, Data Host-defined event from another peer

Types Reference

PlayerState (world mirror entry)

Field Type Description
Pos Vec2 2D position
Vel Vec2 2D velocity
Facing float 2D facing angle (radians)
Pos3D Vec3 3D position
Vel3D Vec3 3D velocity
Rotation Quat 3D rotation (quaternion)
Scale Vec3 Scale (uniform or per-axis)

Vec2 / Vec3 / Quat

Simple value types with X, Y (and Z for Vec3) / X, Y, Z, W (for Quat) float fields.

Wire Protocol

The SDK implements Tez wire protocol v2. Each UDP datagram has an 8-byte header:

+--------+---------+----------+----------+-------------+
| magic  | version | msg_type |   seq    |   payload   |
| u16 LE | u8      | u8       | u32 LE   | variable    |
+--------+---------+----------+----------+-------------+
  • magic = 0x07E2
  • version = 2 (v1 also accepted for backward compatibility)
  • seq = 0 for unreliable messages; nonzero for the reliable channel (acknowledged with Ack)

All payload fields are little-endian. Strings are encoded as u16 length + UTF-8 bytes.

Message Types

ID Name Direction Description
0 Hello Client → Server Handshake with auth token
1 Welcome Server → Client Handshake accepted, peer id assigned
2 JoinRoom Client → Server Request to join a room by name
3 RoomJoined Server → Client Room membership confirmed
4 LeaveRoom Client → Server Leave current room
5 Input Client → Server Unreliable movement (vel + facing)
6 Action Client → Server Reliable gameplay action
7 StateDelta Server → Client Per-player state snapshot
8 Event Server → Client Game event (join/leave/action/chat/custom)
9 Ping Client → Server Heartbeat request
10 Pong Server → Client Heartbeat reply
11 Ack Both Reliable channel acknowledgement
12 Chat Client → Server Reliable chat message
13 Custom Client → Server Reliable host-defined event
14 RoomRedirect Server → Client Cluster redirect to another node

Architecture

┌──────────────────────────────────────────┐
│              TezClient                   │
│  ┌─────────┐   ┌───────────┐            │
│  │  Driver  │   │  UdpClient│            │
│  │ (state   │   │ (transport│            │
│  │ machine) │   │  + IO     │            │
│  │          │   │  thread)  │            │
│  └────┬─────┘   └─────┬─────┘            │
│       │               │                  │
│       │    ┌──────────┴──────┐           │
│       └────┤  Event Queue    ├───► Poll  │
│            │ (ConcurrentQ)   │   Events  │
│            └─────────────────┘           │
└──────────────────────────────────────────┘
  • Driver — pure state machine, no IO. Encodes/decodes protocol messages, manages connection phases, ack tracking, and the world mirror.
  • TezClient — wraps the Driver with a UDP transport running on a background thread. Events are delivered via callback or polled from a concurrent queue.
  • Protocol — static encode/decode of all wire messages.

Advanced: Using the Driver Directly

For custom transports (e.g. WebSocket tunnel, Unity's networking layer), use the Driver class directly:

var driver = new Driver(new TezConfig { Room = "lobby" });
var step = driver.Connect();

// In your transport's send loop:
var pollStep = driver.Poll();
foreach (var datagram in pollStep.Send)
    myTransport.Send(datagram);

// When a datagram arrives:
var recvStep = driver.Receive(receivedBytes);
foreach (var datagram in recvStep.Send)
    myTransport.Send(datagram);
foreach (var ev in recvStep.Events)
    HandleEvent(ev);

// Send gameplay:
var inputBytes = driver.SendInput(new Vec2(1, 0), 0.5f);
if (inputBytes != null) myTransport.Send(inputBytes);

MAUI / Xamarin Usage

The SDK works in .NET MAUI apps. The background IO thread and UDP socket work on all mobile platforms:

// In your MAUI page or service
private TezClient? _client;

protected override void OnAppearing()
{
    _client = TezClient.Connect("myserver:9000", room: "lobby");
    _client.OnEvent = ev =>
    {
        MainThread.BeginInvokeOnMainThread(() =>
        {
            // Update UI on main thread
            if (ev is ClientEvent.Connected c)
                StatusLabel.Text = $"Connected as {c.PeerId}";
        });
    };
}

protected override void OnDisappearing()
{
    _client?.Dispose();
    base.OnDisappearing();
}

License

Part of the Tez realtime engine by CloudFort.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.2 67 8/29/2026
0.0.1 66 8/29/2026