Tez · Unity

Unity SDK

Complete guide to integrating the Tez realtime engine into your Unity project. Native C# bindings, automatic reconnects, and cluster-aware room routing.

Overview

The Tez Unity SDK is a native C# package that wraps the high-performance Rust client library via FFI. It provides a thread-safe API with automatic main-thread event delivery, so you can focus on gameplay without worrying about networking internals.

The SDK handles the full lifecycle: UDP handshake, room joins, delta-compressed state synchronization, reliable actions, chat, custom events, automatic reconnection with exponential backoff, and transparent cluster redirects when your room lives on a different node.

2021.3+
Min Unity Version
IL2CPP
Scripting Backend
<1ms
SDK Overhead
3
Included Samples

Installation

Unity Package Manager
// Option 1: Add from local path
Window → Package Manager → + → Add package from disk...
Select: unity/ir.cloudfort.tez/package.json
// Option 2: Add from git URL
Window → Package Manager → + → Add package from git URL
https://github.com/Cloudfort-Tech/tez.git?path=/unity/ir.cloudfort.tez
// Requirements: Unity 2021.3+ (IL2CPP & Mono)

Quick Start

TezManager.cs
using Tez.Client;
// 1. Add TezEventDispatcher to a GameObject
// 2. Set Server Address (e.g. 127.0.0.1:9000) and Room
// 3. Subscribe to events
var dispatcher = GetComponent<TezEventDispatcher>();
dispatcher.Connected += (peer, tick) => Debug.Log($"Connected: {peer}");
dispatcher.RoomJoined += room => Debug.Log($"Room: {room}");
dispatcher.PlayerJoined += (peer, room) => Debug.Log($"Peer {peer} joined");
// Send movement every frame
dispatcher.Client.SendInput(vx, vy, facing);
// Reliable action (e.g. fire weapon)
dispatcher.Client.SendAction(kind: 1, target: 0);
// Read the snapshot mirror
var players = new List<TezNative.TezPlayer>();
dispatcher.Client.Players(players);
✓ All callbacks on Unity main thread

Architecture

TezEventDispatcher (MonoBehaviour)

Polls the native client once per frame in Update(). Raises C# events on the Unity main thread. Add one to any GameObject.

TezClient (C# wrapper)

High-level API wrapping the native FFI handle. Thread-safe command enqueue, event draining, and snapshot mirror queries.

TezNative (P/Invoke)

Raw C ABI bindings matching the Rust #[repr(C)] structs byte-for-byte. Fully blittable — no marshalling overhead.

tez_client.dll (Native)

Pre-compiled native library. One dedicated IO thread per client handles UDP, heartbeats, reconnection, and cluster redirects.

API Reference

TezClient

High-level client. Thread-safe; all methods are cheap lock-protected enqueues.

Connect(addr, token?, room?)Unreliable

Create a client and start connecting. Non-blocking. Progress arrives via events.

JoinRoom(name)Reliable

Join or switch to a named room. Survives reconnects and cluster redirects.

LeaveRoom()Reliable

Leave the current room. Stops re-joining after reconnects.

SendInput(vx, vy, facing)Unreliable

Unreliable movement input. Rate-limited by server tick rate (default 30Hz).

SendAction(kind, target)Reliable

Reliable gameplay action. Target 0 broadcasts to the room; non-zero targets a specific peer.

SendChat(text)Reliable

Reliable chat message delivered to all room members (sender excluded).

SendCustom(kind, data)Reliable

Reliable host-defined event with binary payload (max 256 bytes). Fully extensible.

Players(sink)Unreliable

Copy the locally mirrored room state. Each entry has peer, pos, vel, facing.

PeerIdUnreliable

Own peer ID assigned by the server (0 while not connected).

StateUnreliable

Current connection phase (0-5): Disconnected → Handshaking → Joining → Connected.

TezEventDispatcher

MonoBehaviour bridge. Polls the native client in Update() and raises C# events on the main thread.

Connected(peerId, tickRate)

Handshake accepted. You have a peer ID and know the server tick rate.

RoomJoined(roomId)

Room membership confirmed. You can now send input and receive state deltas.

Redirected(addr)

Cluster redirect in flight. Automatic — the client rebinds and re-handshakes.

Disconnected(reason)

Session ended. Reason: 0 = voluntary stop, 1 = timeout, 2 = redirect loop.

Reconnecting(attempt)

Reconnect attempt started with exponential backoff.

PlayerJoined(peer, room)

A new player entered your room.

PlayerLeft(peer, room, reason)

A player left your room. Reason: 0 = voluntary, 1 = timeout.

ActionReceived(peer, kind, target)

A reliable gameplay action from another player.

ChatReceived(peer, text)

A chat message from another room member.

CustomReceived(peer, kind, payload)

A host-defined custom event with binary data.

RttMeasured(microseconds)

Heartbeat round-trip time from the latest Pong.

TezPlayer (Snapshot Mirror)

One entry per player visible in the current room. Updated every server tick.

FieldTypeDescription
peeruintUnique peer ID assigned by the server
xfloatPosition X (authoritative, from server)
yfloatPosition Y (authoritative, from server)
vxfloatVelocity X (last submitted input)
vyfloatVelocity Y (last submitted input)
facingfloatFacing angle in radians

Connection Lifecycle

0

Disconnected

Phase 0

No active connection. The client is idle or has been stopped.

1

Handshaking

Phase 1

Hello packets are being sent. Waiting for the server to respond with Welcome.

2

Joining

Phase 2

Handshake complete. A JoinRoom request is pending.

3

Connected

Phase 3

Fully connected in a room. Input, actions, and state sync are active.

4

Redirecting

Phase 4

Cluster redirect received. Rebinding to the owning node.

5

Reconnecting

Phase 5

Connection lost. Exponential backoff reconnect in progress.

Authentication

Servers started with --auth-key <hex> require a 48-byte HMAC-SHA256 token for each connection. The token format is:

nonce u64 LE | expires_at u64 LE | HMAC-SHA256(nonce + expiry)

Your backend mints tokens and passes them to TezClient.Connect(addr, token, room). Without an auth key (dev/LAN mode), any token — including none — is accepted.

Code Examples

MovementController.cs
// Read WASD input and send to server
void Update() {
var vx = Input.GetAxis("Horizontal");
var vy = Input.GetAxis("Vertical");
var facing = Mathf.Atan2(vy, vx);
// Unreliable — rate-limited by server tick
dispatcher.Client.SendInput(vx * speed, vy * speed, facing);
// Sync remote players from snapshot mirror
dispatcher.Client.Players(players);
foreach (var p in players) {
if (p.peer == myPeerId) continue;
// Lerp remote player transforms
remote[p.peer].position = Vector3.Lerp(
remote[p.peer].position,
new Vector3(p.x, 0, p.y),
Time.deltaTime * lerpSpeed);
}
}
ActionsAndChat.cs
// Reliable action — broadcast to room (target = 0)
dispatcher.Client.SendAction(kind: 1, target: 0);
// Reliable action — targeted at a specific peer
dispatcher.Client.SendAction(kind: 2, target: targetPeerId);
// Chat message to the room
dispatcher.Client.SendChat("Hello everyone!");
// Custom event with binary payload (max 256 bytes)
dispatcher.Client.SendCustom(kind: 7, data: new byte[] { 0xAB, 0xCD });
// Handle incoming events
dispatcher.ActionReceived += (peer, kind, target) => {
Debug.Log($"Action {kind} from {peer} targeting {target}");
};
dispatcher.ChatReceived += (peer, text) => {
Debug.Log($"[{peer}] {text}");
};

Features

IL2CPP & Mono

Works with both scripting backends. Fully blittable structs — zero marshalling overhead.

Main-Thread Events

All callbacks fire on the Unity main thread via Update(). No thread-safety headaches.

Auto Reconnect

Exponential backoff on timeout. Room is remembered and re-joined after reconnect.

Cluster Redirects

Transparent room routing across nodes. The client follows redirects automatically.

Delta Compression

Only changed fields cross the wire. Position, velocity, and facing are diffed per tick.

3 Included Samples

Movement, Actions & Chat, and Snapshot Mirror samples to get started quickly.

Server Configuration

tez-server CLI
# Start a basic server (open dev mode, no auth)
tez-server --bind 0.0.0.0:9000 --tick-rate 30
# With authentication enabled
tez-server --auth-key "your-secret-hex-key"
# Cluster mode with Redis
tez-server \
--redis-url "redis://localhost:6379" \
--node-id "node-1" \
--advertise "10.0.0.1:9000"
# With DTLS encryption + Prometheus metrics
tez-server --dtls 0.0.0.0:9443 --metrics 0.0.0.0:9101
--bind

UDP address to listen on (default: 0.0.0.0:9000)

--tick-rate

Server simulation ticks per second (default: 30)

--room-capacity

Maximum players per room (default: 64)

--peer-timeout-secs

Evict peers after this much silence (default: 10s)

--auth-key

Shared secret for HMAC handshake tokens (empty = open dev mode)

--max-sessions

Hard cap on concurrent sessions (default: 100,000)

--dtls

Optional DTLS endpoint for encrypted connections

--metrics

Optional Prometheus /metrics endpoint for monitoring

Ready to build?

Tez is currently free. Get your server address and start building multiplayer features in Unity today.