@onyx-engine/client
v1.6.0
Published
Onyx Engine SDK — auth, multiplayer, economy, social, and more
Downloads
2,505
Readme
@onyx-engine/client
TypeScript SDK for the Onyx game platform. One client for everything: authentication, multiplayer, economy, social, leaderboards, achievements, and more.
Works with React Native, Expo, and browsers. No native dependencies.
Versioning
SDK major version = API version. This ensures backwards compatibility:
- SDK 1.x → always uses API v1. Safe to update within 1.x.
- When API v2 is released → SDK 2.0.0 will be published.
^1.0.0in your package.json will never auto-update to 2.x.
Installation
npm install @onyx-engine/clientQuick Start
import { OnyxClient } from "@onyx-engine/client";
const client = new OnyxClient({
apiKey: "YOUR_API_KEY", // Get from https://onyx.blessupgames.com/dashboard/api-keys
});
// Login
const session = await client.auth.guestLogin("unique-device-id", "ios");
console.log(session.playerId);
// Use any feature
const profile = await client.players.getProfile();
const balance = await client.economy.getBalance();
const friends = await client.friends.list();
const leaderboard = await client.leaderboard.get("daily", 10);
// Real-time multiplayer
await client.realtime.connect("default");
client.realtime.raiseEvent(1, { action: "move", x: 10 }, "others");
client.realtime.on("customEvent", (evt) => {
console.log(`Player ${evt.senderRef} did:`, evt.data);
});Authentication
All login methods return AuthResponse and automatically store the JWT for subsequent API calls.
// Guest (anonymous)
await client.auth.guestLogin("device-id", "ios");
// Google
await client.auth.googleLogin(googleIdToken, "device-id", "android");
// Apple
await client.auth.appleLogin(appleIdentityToken, "[email protected]", "Alice", "device-id", "ios");
// Email OTP
await client.auth.sendOtp("[email protected]");
await client.auth.verifyOtp("[email protected]", "123456");
// Link provider to guest account
await client.auth.linkAccount("google", { token: googleIdToken });
// Logout / delete
await client.auth.logout();
await client.auth.deleteAccount();Token Persistence
JWT tokens expire after 1 hour. The SDK refreshes them automatically on 401. Persist tokens so users stay logged in:
client.onTokenRefresh((jwt, refreshToken) => {
// Save to AsyncStorage, SecureStore, localStorage, etc.
storage.set("jwt", jwt);
storage.set("refreshToken", refreshToken);
});
// Restore session on app restart
const restored = await client.restoreSession(savedJwt, savedRefreshToken);
if (!restored) {
// Token expired, show login screen
}Player Profile
const profile = await client.players.getProfile();
// { playerId, username, avatar, stats: { totalScore, gamesPlayed, coins } }
await client.players.setUsername("NewName"); // 30-day cooldown
await client.players.setAvatar("preset", "emoji:🎮");Friends & Social
const friends = await client.friends.list();
const results = await client.friends.search("ali");
await client.friends.sendRequest("player_xyz...");
const requests = await client.friends.getRequests();
await client.friends.respond(requestId, "accept");
await client.friends.remove(friendshipId);Chat
const convos = await client.chat.getConversations();
const history = await client.chat.getHistory(friendId, { limit: 50 });
await client.chat.send(friendId, "Hello!");
await client.chat.markAsRead(friendId);
const unread = await client.chat.getUnreadCount();
const ban = await client.chat.getBanStatus();Blocks & Moderation
await client.blocks.block(playerId);
await client.blocks.unblock(playerId);
const list = await client.blocks.list();
const status = await client.blocks.isBlocked(playerId);
await client.moderation.report({
reportedPlayerId: 123,
category: "chat_abuse",
reason: "Abusive messages",
evidence: "msg:1234,msg:1235",
});Economy & IAP
const { coins } = await client.economy.getBalance();
const products = await client.economy.getProducts("chess");
const inventory = await client.economy.getInventory();
// Buy with coins
await client.economy.purchase("hint_pack", 50);
// Verify real-money IAP receipt
await client.economy.verifyPurchase({
productId: "coins_500",
platform: "ios",
transactionId: "apple-tx-id",
receipt: "base64-receipt",
});
const txns = await client.economy.getTransactions(20, 0);Leaderboard
const lb = await client.leaderboard.get("weekly", 50);
// { period, entries: [{ rank, playerId, username, score }], myRank, total }Periods: daily, weekly, monthly, alltime.
Achievements
const { achievements } = await client.achievements.list();
// [{ id, nameKey, progress, unlocked, rewardAmount, rewardClaimed }]
const reward = await client.achievements.claimReward("first_win");
// { coinsAwarded: 50, newBalance: 390 }Push Notifications
await client.notifications.register(fcmToken, "ios", "device-id");
await client.notifications.updateToken(oldToken, newToken);
const prefs = await client.notifications.getPreferences();
await client.notifications.setPreferences({ challenges: false });Sessions (Open Lobby)
Browse and create game sessions. Players join freely, leave anytime.
// List open sessions
const { sessions } = await client.sessions.list({ gameType: "chess", limit: 20 });
sessions.forEach(s => console.log(s.sessionRef, s.playerCount, "/", s.maxPlayers));
// Create a session
const session = await client.sessions.create({
sessionName: "Friday Game Night",
maxPlayers: 20,
isOpen: true,
properties: { gameMode: "casual", language: "tr" },
});
// Join the session
await client.realtime.connect(session.sessionRef);Matchmaking
Matchmaking runs over WebSocket. Connect to realtime first.
await client.realtime.connect("default");
// Join queue
client.matchmaking.join("chess", "en", 1200);
// Listen for match
client.realtime.onEvent("EvMatchFound", (evt) => {
console.log("Match found:", evt.sessionRef, evt.opponent);
});
// Cancel
client.matchmaking.cancel();
// Query queue stats
const stats = await client.matchmaking.getQueueStats("word_puzzle", "tr");
console.log(`${stats.playersQueued} players queued, avg wait ${stats.averageWaitSeconds}s`);
// Challenge a friend
client.matchmaking.challenge("player_abc", "chess", "en");
client.realtime.onEvent("EvChallengeResult", (evt) => {
if (evt.accepted) console.log("Game session:", evt.sessionRef);
});Real-time Multiplayer
await client.realtime.connect("session-name");
// Send events to other players
client.realtime.raiseEvent(1, { x: 10, y: 20 }, "others");
client.realtime.raiseEvent(2, { card: 42 }, "all");
client.realtime.raiseEvent(3, { msg: "hi" }, ["Player(2)"]);
// Shared room state
client.realtime.setRoomProperties({ phase: "playing", round: 1 });
client.realtime.setRoomProperties(
{ turn: "Player(3)" },
{ turn: "Player(2)" } // CAS: only if current value matches
);
// Your player state
client.realtime.setPlayerProperties({ score: 42, ready: true });
// Listen for events
client.realtime.on("customEvent", (evt) => console.log(evt.senderRef, evt.data));
client.realtime.on("propertiesChanged", (evt) => console.log(evt.properties));
client.realtime.on("playerJoined", (evt) => console.log(evt.displayName, "joined"));
client.realtime.on("playerLeft", (evt) => console.log(evt.playerRef, "left"));
client.realtime.on("snapshot", (snap) => console.log("Players:", snap.playerCount));
client.realtime.on("disconnected", () => console.log("Lost connection"));
// Time helpers
client.realtime.tickRate; // 20
client.realtime.serverTick; // current tick
client.realtime.serverTimeSeconds; // elapsed seconds
client.realtime.remainingSeconds(endTick); // countdown
// Use power-up
client.realtime.usePowerUp("freeze");
// Disconnect
client.realtime.disconnect();Live Video (WebRTC)
const creds = await client.video.getTurnCredentials();
// { urls, username, credential, ttl }
const pc = new RTCPeerConnection({
iceServers: [{ urls: creds.urls, username: creds.username, credential: creds.credential }],
});
const ban = await client.video.getBanStatus();Error Handling
import { OnyxApiError } from "@onyx-engine/client";
try {
await client.economy.purchase("item", 100);
} catch (err) {
if (err instanceof OnyxApiError) {
console.log(err.status); // 400
console.log(err.code); // "insufficient_coins"
console.log(err.message);
}
}The SDK automatically refreshes expired JWTs on 401 responses.
Configuration
const client = new OnyxClient({
// baseUrl defaults to "https://api.blessupgames.com"
// wsUrl defaults to "wss://api.blessupgames.com/ws"
ws: {
clientVersion: "1.0.0",
autoReconnect: true,
reconnectBaseDelayMs: 500,
reconnectMaxDelayMs: 30000,
reconnectMaxAttempts: 10,
debug: false,
},
});Low-level GameClient
For advanced use cases, you can use the low-level GameClient directly:
import { GameClient, registerCommand, registerEvent } from "@onyx-engine/client";
const gc = new GameClient("wss://server/ws", { autoReconnect: true });
await gc.connect();
await gc.join("session", "Alice");
gc.raiseEvent(1, { data: "test" }, "others");
gc.setRoomProperties({ key: "value" });
gc.sendCommand("CmdCustom", { field: 1 });Exports
// High-level client
export { OnyxClient, OnyxApiError };
export type {
OnyxClientOptions, AuthResponse, PlayerProfile, Friend,
ChatMessage, Product, Achievement, LeaderboardEntry,
MatchmakingStatus, BanStatus, BlockedPlayer, Transaction,
NotificationPreferences, TurnCredentials,
};
// Low-level client
export { GameClient };
export type { GameClientOptions };
// Protocol types
export { MessageType, ConnectionState, DisconnectCode, EventTarget, PropertyScope };
// Serialization
export { registerCommand, registerEvent };React Native
import { useEffect, useRef, useState } from "react";
import { OnyxClient } from "@onyx-engine/client";
import AsyncStorage from "@react-native-async-storage/async-storage";
const client = new OnyxClient({ apiKey: process.env.ONYX_API_KEY! });
client.onTokenRefresh(async (jwt, rt) => {
await AsyncStorage.multiSet([["@jwt", jwt], ["@rt", rt]]);
});
function useOnyx() {
const [loggedIn, setLoggedIn] = useState(false);
useEffect(() => {
AsyncStorage.multiGet(["@jwt", "@rt"]).then(async ([[, jwt], [, rt]]) => {
if (jwt && rt) {
const ok = await client.restoreSession(jwt, rt);
setLoggedIn(ok);
}
});
}, []);
return { client, loggedIn, setLoggedIn };
}