OddSockets Unity SDK
Official Unity SDK for OddSockets real-time messaging platform
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:
{
"dependencies": {
"com.oddsockets.unity": "https://github.com/jyswee/oddsockets-unity-sdk.git"
}
}
Add via Unity Package Manager using Git URL:
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:
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
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.
// 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 |
|---|---|---|
| Reactions | AddReactionAsync, RemoveReactionAsync, GetReactionsAsync | OnReactionAdded, OnReactionRemoved, OnReactionsData |
| Typing indicator | StartTypingAsync, StopTypingAsync | OnUserTyping, OnUserStoppedTyping |
| Read receipts & unread badges | MarkReadAsync, MarkAllReadAsync, GetUnreadCountsAsync | OnUserRead, OnUnreadCountUpdated, OnAllMarkedRead |
| Threads (sub-chats) | ThreadReplyAsync, GetThreadAsync, SubscribeThreadAsync, FollowThreadAsync | OnThreadReply, OnThreadData, OnThreadSubscribed |
| Edit / delete / pin | EditMessageAsync, DeleteMessageAsync, PinMessageAsync, GetPinnedMessagesAsync | OnMessageEdited, OnMessageDeleted, OnMessagePinned |
| Presence & status | SetStatusAsync, SetCustomStatusAsync, SetDndAsync, GetUserPresenceAsync | OnUserStatusChanged, OnCustomStatusUpdated, OnUserPresenceData |
| File uploads | StartFileUploadAsync, GetChannelFilesAsync | OnFileUploadStarted, OnUploadProgress, OnChannelFiles |
| Direct messages | CreateDmAsync, SendDmAsync, GetDmHistoryAsync, MuteDmAsync | OnDmReceived, OnDmCreated, OnDmHistory |
| Notifications | SubscribeNotificationsAsync, GetNotificationsAsync, MarkNotificationReadAsync | OnNotification, OnUnreadCount, OnNotificationsData |
| Channel management | CreateChannelAsync, InviteToChannelAsync, JoinChannelAsync, GetChannelMembersAsync | OnChannelCreated, OnUserJoinedChannel, OnChannelMembers |
| Custom / raw events | client.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).
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. Ackchallenge_create_success.ReportProgressAsync(challengeId, value, ...)— fire-and-forget metric progress. No ack.CompleteChallengeAsync(challengeId, outcome, ...)— finalize with an outcome. Ackchallenge_complete_success.UnlockAchievementAsync(achievementId, ..., percentComplete, ...)— fire-and-forget; passpercentComplete(0–100). No ack.GetStandingsAsync(challengeId, limit, offset)— request top-N + caller rank. Ackchallenge_standings_success.GetAchievementsAsync(achievementId)— query achievement state. Ackachievement_state.SendChallengeInviteAsync(toUserId, ...)— directed invite to another user. Ackchallenge_invite_success.ReplyChallengeInviteAsync(inviteId, accept, ...)— accept / decline an invite. Ackchallenge_reply_success.CancelChallengeInviteAsync(inviteId)— cancel a sent invite. Ackchallenge_invite_cancel_success.GetChallengeInvitesAsync()— list pending invites. Ackchallenge_invites.
Completion outcomes
The outcome argument to CompleteChallengeAsync is one of:
completed— win (rank 1)failed— losstied— drawconceded— resign / concedeexpired— 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 broadcasts —
challenge_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.
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,
0–1
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.
OddSocketsClientis aMonoBehaviour; add it withgameObject.AddComponent<OddSocketsClient>(), callInitialize(config), thenawait ConnectAsync(). It cleans up onOnDestroyviaDisconnect(). - 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; useasync voidUnity 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.
OnWorkerAssignedtells you when it is ready. - Reconnect-safe. Subscriptions and enhanced-event listeners are restored on
automatic reconnect;
OnReconnectingandOnDisconnectedlet 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.