TezSDK 0.0.2
dotnet add package TezSDK --version 0.0.2
NuGet\Install-Package TezSDK -Version 0.0.2
<PackageReference Include="TezSDK" Version="0.0.2" />
<PackageVersion Include="TezSDK" Version="0.0.2" />
<PackageReference Include="TezSDK" />
paket add TezSDK --version 0.0.2
#r "nuget: TezSDK, 0.0.2"
#:package TezSDK@0.0.2
#addin nuget:?package=TezSDK&version=0.0.2
#tool nuget:?package=TezSDK&version=0.0.2
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=0x07E2version=2(v1 also accepted for backward compatibility)seq=0for unreliable messages; nonzero for the reliable channel (acknowledged withAck)
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
| Product | Versions 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. |
-
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.