Need an API key? 7-day free trial on every plan, from $29/mo.
Sign Up Agent Quickstart

OddSockets Unity SDK

Official Unity SDK for OddSockets real-time messaging platform

Unity Ready C# / .NET Cross-Platform High Performance Coroutine Support

Overview & Features

The OddSockets Unity SDK provides a powerful, Unity-optimized interface for real-time messaging in games and interactive applications.

Unity Optimized

Designed specifically for Unity's threading model with main thread marshaling and coroutine support.

Cross-Platform

Works on all Unity-supported platforms: PC, Mac, Linux, iOS, Android, WebGL, and consoles.

Coroutine Native

All async operations return Unity coroutines for seamless integration with Unity's async patterns.

High Performance

Optimized for low latency gaming with efficient WebSocket connections and smart routing.

Cost Effective

No per-message pricing, industry-standard 32KB message limits, transparent pricing.

Automatic Failover

Built-in redundancy and intelligent error handling for 99.9% uptime in production games.

Installation

Add the package to your project's Packages/manifest.json. The Socket.IO transport is bundled, so no extra dependencies are required:

json
{
  "dependencies": {
    "com.oddsockets.unity": "https://github.com/jyswee/oddsockets-unity-sdk.git"
  }
}

Add via Unity Package Manager using Git URL:

unity
1. Open Unity Package Manager (Window → Package Manager)
2. Click the + button → Add package from git URL
3. Enter: https://github.com/jyswee/oddsockets-unity-sdk.git
4. Click Add

Manual installation by copying scripts:

bash
git clone https://github.com/jyswee/oddsockets-unity-sdk.git
# Copy the Runtime/ and ThirdParty/ folders into your Unity project's Assets/ directory
# (ThirdParty/ contains the bundled Socket.IO transport)

Quick Start

Basic Usage

csharp
using UnityEngine;
using OddSockets.Unity;

public class ChatManager : MonoBehaviour
{
    private OddSocketsClient client;
    private OddSocketsChannel chatChannel;

    async void Start()
    {
        // OddSocketsClient is a MonoBehaviour: add it as a component, then initialize.
        client = gameObject.AddComponent();
        client.Initialize(new OddSocketsUnityConfig
        {
            ApiKey = "ak_live_1234567890abcdef",
            UserId = "player123"
        });

        // Set up event handlers
        client.OnConnected += OnConnected;
        client.OnError += OnError;

        // Connect. A realtime worker is auto-provisioned for your account -
        // there is no server URL to configure.
        await client.ConnectAsync();
    }

    private async void OnConnected()
    {
        Debug.Log("Connected to OddSockets!");

        // Get a channel and subscribe
        chatChannel = client.Channel("game-chat");
        await chatChannel.SubscribeAsync(OnMessage);

        // Publish a message
        await chatChannel.PublishAsync(new { text = "gg wp", player = "Player1" });
    }

    private void OnMessage(ChannelMessageData message)
    {
        Debug.Log($"Received: {message.Data}");
    }

    private void OnError(System.Exception error)
    {
        Debug.LogError($"OddSockets error: {error.Message}");
    }

    void OnDestroy()
    {
        client?.Disconnect();
    }
}

Enhanced Features

Beyond core pub/sub, the SDK exposes a rich real-time feature set through client.Enhanced. Every action is an async Task and every server event is a C# event you can subscribe to. All events round-trip through the worker, so a second connected client sees the change live.

csharp
// All enhanced features are accessed through client.Enhanced
var enhanced = client.Enhanced;

// Reactions
enhanced.OnReactionAdded += data => Debug.Log($"Reaction: {data}");
await enhanced.AddReactionAsync(messageId, "game-chat", "\uD83D\uDD25", "player123");

// Typing indicator
enhanced.OnUserTyping += data => Debug.Log($"typing: {data}");
await enhanced.StartTypingAsync("player123", "game-chat");

// Direct messages (whispers)
enhanced.OnDmReceived += data => Debug.Log($"DM: {data}");
await enhanced.SendDmAsync(conversationId, "gg wp", "player123");

// Custom game events (raw escape hatch on the client itself)
client.On("objective_captured", payload => Debug.Log($"captured: {payload}"));
await client.EmitAsync("objective_captured", new { team = "red", point = "B" });
Feature Key methods (client.Enhanced.*) Events
ReactionsAddReactionAsync, RemoveReactionAsync, GetReactionsAsyncOnReactionAdded, OnReactionRemoved, OnReactionsData
Typing indicatorStartTypingAsync, StopTypingAsyncOnUserTyping, OnUserStoppedTyping
Read receipts & unread badgesMarkReadAsync, MarkAllReadAsync, GetUnreadCountsAsyncOnUserRead, OnUnreadCountUpdated, OnAllMarkedRead
Threads (sub-chats)ThreadReplyAsync, GetThreadAsync, SubscribeThreadAsync, FollowThreadAsyncOnThreadReply, OnThreadData, OnThreadSubscribed
Edit / delete / pinEditMessageAsync, DeleteMessageAsync, PinMessageAsync, GetPinnedMessagesAsyncOnMessageEdited, OnMessageDeleted, OnMessagePinned
Presence & statusSetStatusAsync, SetCustomStatusAsync, SetDndAsync, GetUserPresenceAsyncOnUserStatusChanged, OnCustomStatusUpdated, OnUserPresenceData
File uploadsStartFileUploadAsync, GetChannelFilesAsyncOnFileUploadStarted, OnUploadProgress, OnChannelFiles
Direct messagesCreateDmAsync, SendDmAsync, GetDmHistoryAsync, MuteDmAsyncOnDmReceived, OnDmCreated, OnDmHistory
NotificationsSubscribeNotificationsAsync, GetNotificationsAsync, MarkNotificationReadAsyncOnNotification, OnUnreadCount, OnNotificationsData
Channel managementCreateChannelAsync, InviteToChannelAsync, JoinChannelAsync, GetChannelMembersAsyncOnChannelCreated, OnUserJoinedChannel, OnChannelMembers
Custom / raw eventsclient.On(evt, handler), client.EmitAsync(evt, payload), client.Off(evt)any event name you define

Challenges & Leaderboards

Challenges, leaderboards and achievements build on the same live socket. The send side lives on the enhanced surface (client.Enhanced); request/query methods resolve with the worker's reply, while progress and achievement calls are fire-and-forget. Inbound broadcasts arrive on the client event surface — subscribe with the typed C# events on client.Enhanced (or the raw client.On("<event>", ...) escape hatch).

csharp
var enhanced = client.Enhanced;

// Ack lands as a typed C# event
enhanced.OnStandingsData += data => Debug.Log($"standings: {data}");

await enhanced.CreateChallengeAsync("weekly-kills", "kills");
await enhanced.ReportProgressAsync("weekly-kills", 3);        // fire-and-forget
await enhanced.GetStandingsAsync("weekly-kills", 10);         // ack: challenge_standings_success
await enhanced.CompleteChallengeAsync("weekly-kills", "completed");

Methods

  • CreateChallengeAsync(challengeId, metric, ...) — create a challenge / leaderboard. Ack challenge_create_success.
  • ReportProgressAsync(challengeId, value, ...) — fire-and-forget metric progress. No ack.
  • CompleteChallengeAsync(challengeId, outcome, ...) — finalize with an outcome. Ack challenge_complete_success.
  • UnlockAchievementAsync(achievementId, ..., percentComplete, ...) — fire-and-forget; pass percentComplete (0–100). No ack.
  • GetStandingsAsync(challengeId, limit, offset) — request top-N + caller rank. Ack challenge_standings_success.
  • GetAchievementsAsync(achievementId) — query achievement state. Ack achievement_state.
  • SendChallengeInviteAsync(toUserId, ...) — directed invite to another user. Ack challenge_invite_success.
  • ReplyChallengeInviteAsync(inviteId, accept, ...) — accept / decline an invite. Ack challenge_reply_success.
  • CancelChallengeInviteAsync(inviteId) — cancel a sent invite. Ack challenge_invite_cancel_success.
  • GetChallengeInvitesAsync() — list pending invites. Ack challenge_invites.

Completion outcomes

The outcome argument to CompleteChallengeAsync is one of:

  • completed — win (rank 1)
  • failed — loss
  • tied — draw
  • conceded — resign / concede
  • expired — timed out

Progressive achievements

UnlockAchievementAsync always emits the wire event achievement_unlock; the worker is authoritative and derives the outbound broadcast from percentComplete. A value < 100 broadcasts achievement_progress (status in_progress); a value >= 100 or omitted broadcasts achievement_unlock (status unlocked). Just call UnlockAchievementAsync with the right percentComplete — the worker chooses the broadcast for you.

Inbound events

Subscribe to these as typed C# events on client.Enhanced (each wraps client.On("<event>", ...)).

  • Room broadcastschallenge_progress, leaderboard_rank_change, challenge_complete, achievement_unlock, achievement_progress
  • Directed (per-user)challenge_invited, challenge_reply_received, challenge_invite_cancelled

Usage Analytics

Pull your tenant's headline usage tiles — monthly active users, daily active users, total messages published, and error rate — straight from the SDK, without hand-rolling a REST call. GetUsageStatsAsync() resolves the same four tiles the developer dashboard renders.

csharp
var stats = await client.GetUsageStatsAsync();

// Nullable long?/double? tiles stay null when a leg is not live yet.
string Tile(object v) => v?.ToString() ?? "\u2014"; // em-dash for null
Debug.Log($"MAU:            {Tile(stats.Mau)}");
Debug.Log($"DAU:            {Tile(stats.Dau)}");
Debug.Log($"Total messages: {Tile(stats.TotalMessages)}");
Debug.Log($"Error rate:     {Tile(stats.ErrorRate)}");

The four tiles

  • Mau — monthly active users for your owner scope
  • Dau — daily active users
  • TotalMessages — total messages published
  • ErrorRate — publish error rate, 01

Honesty rule — null, never a fake zero

Each tile is a nullable number (long? / double?). A null means that leg of the analytics pipeline is not live yet for your tenant — the SDK returns it verbatim and never coerces it to 0. Render an em-dash () for a null tile so you never show a fabricated zero.

Requires an API key

GetUsageStatsAsync() reads your owner-scoped analytics, so it needs an ApiKey. Keyless / token-only clients have no owner scope to query and will throw GetUsageStatsAsync requires an ApiKey.

Unity Features

  • MonoBehaviour lifecycle. OddSocketsClient is a MonoBehaviour; add it with gameObject.AddComponent<OddSocketsClient>(), call Initialize(config), then await ConnectAsync(). It cleans up on OnDestroy via Disconnect().
  • Main-thread callbacks. All events are marshalled back onto Unity's main thread, so you can touch GameObjects and UI directly inside handlers.
  • async/await, not coroutines. The API is Task-based; use async void Unity messages (e.g. async void Start()) or await from your own async methods.
  • Auto-provisioned worker. There is no server URL to configure - the client discovers its assigned worker through the manager. OnWorkerAssigned tells you when it is ready.
  • Reconnect-safe. Subscriptions and enhanced-event listeners are restored on automatic reconnect; OnReconnecting and OnDisconnected let you surface connection state in-game.
  • Bundled transport. The Socket.IO transport ships inside the package under ThirdParty/, so there is no extra Git dependency to install.