npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@qingzhenghust/game-sdk

v0.4.0

Published

Riffle game backend HTTP/WebSocket and host capability SDK

Readme

@qingzhenghust/game-sdk

Riffle 游戏客户端 SDK。0.3 起,多人的业务规则和状态由每个游戏自己的 backend/ 实现;平台负责身份、HTTP/WS 路由、隔离的 SQL/Redis,以及语音视频的短期 TURN/SFU 授权。

初始化

import { createGameSdkFromHost } from '@qingzhenghust/game-sdk';

const sdk = createGameSdkFromHost({ app: riffleBridge.app });
await sdk.ready();

宿主注入 Runtime URL、Launch Token、平台用户和 game ID。SDK 自动刷新临近过期的 Launch Token,并为 HTTP 和 WebSocket 请求附加同一个可信身份。游戏不得自己保存平台 token、拼接 Runtime backend URL 或传入用户 ID 冒充其他玩家。

SDK 0.3 的默认入口只有这些能力:

  • sdk.backend():游戏 backend 的 HTTP/WS 客户端;
  • sdk.gameRooms():基于游戏 backend 约定的可选房间组件;
  • sdk.media():语音、视频、TURN/SFU 和网络探针;
  • sdk.llm()、sdk.uploads() 和宿主 sdk.app 能力。

旧 Runtime 的 room()、rooms()、matchmaking()、leaderboard()、db() 和实时模拟 capability 不在 0.3 的公开 API 中。匹配、排行榜、状态存储和服务端模拟应在游戏 backend 中实现。

HTTP 和 WebSocket

const backend = sdk.backend();

const snapshot = await backend.get('/matches/m1');
await backend.post('/matches/m1/input', {
  requestId: crypto.randomUUID(),
  sequence: 18,
  action: 'jump',
});

const socket = await backend.connect('/matches/m1/events');
const unsubscribe = socket.onMessage(event => applyServerEvent(event));
socket.send({ type: 'subscribe', protocol: 1, requestId: crypto.randomUUID() });

HTTP 适合命令、CRUD、权限检查和恢复快照。WebSocket 适合订阅、服务端推送和短小的实时输入。业务 ACK 必须说明请求是已接收、已持久化还是已应用;socket open 和 HTTP 200 本身不代表比赛动作已经执行。

游戏 backend 使用 @qingzhenghust/game-sdk/server 声明 HTTP/WS 入口:

import { defineGameBackend } from '@qingzhenghust/game-sdk/server';

export default defineGameBackend({
  async http(request, ctx) {
    // ctx.identity 是可信平台身份;ctx.sql/ctx.redis 已按部署隔离。
    return Response.json({ userId: ctx.identity.userId });
  },
  async websocket(socket, ctx) {
    socket.send({ type: 'ready', protocol: 1 });
  },
});

服务端入口放在 backend/index.ts,迁移放在 backend/migrations/。部署平台以稳定的 backendDeploymentId 更新该游戏唯一 Runner;发布新 artifact 不会多建一套长期 Runner。完整约定见 Backend API 和 多人快速开始。

房间组件

房间不是平台隐式状态。游戏需要房间时,由自己的 backend 使用 @qingzhenghust/game-backend-kit 定义规则,客户端再调用:

const rooms = sdk.gameRooms();
const room = await rooms.create({ maxPlayers: 4, metadata: { mode: 'casual' } });
await rooms.join(room.id, { team: 'blue' });
const current = await rooms.get(room.id);
await rooms.leave(room.id);

不需要房间的留言板、排行榜和异步协作可直接设计 HTTP/WS API。SDK 不会强迫这些玩法创建虚构房间。

语音和视频

先由游戏 backend 确认当前用户属于该房间。SDK 随后通过 backend 获取短期媒体授权,并管理采集、P2P/SFU、信令订阅、重连和资源释放。

const voice = await sdk.media().voice(room.id);
voice.onTrack((_peerId, stream) => attachAudio(stream));
await voice.join(); // 必须由用户操作触发权限请求
voice.setMuted(true);
await voice.leave();

const video = await sdk.media().video(room.id);
video.onLocalStream(stream => showPreview(stream));
video.onTrack((peerId, stream) => attachRemoteVideo(peerId, stream));
await video.join();
await video.setCameraEnabled(false);
await video.switchCamera();
await video.leave();

双人房默认可用 P2P + TURN,多人房使用 SFU。选择结果来自服务端授权,游戏不接触 TURN 密钥或 SFU 签名密钥。媒体帧不会进入 Runner、SQL 或 Redis;backend 只处理成员权限、房间业务和信令路由。

语音和视频都提供延迟与丢包探针:

const stop = video.observeNetwork(snapshot => {
  renderNetwork({
    rttMs: snapshot.summary.rttMs,
    loss: snapshot.summary.packetLossRate,
    quality: snapshot.summary.quality,
  });
}, { intervalMs: 1000 });

const diagnostic = await video.probeNetwork({ sampleWindowMs: 1000 });
stop();

探针读取浏览器 WebRTC 统计,不上传媒体内容、SDP、ICE 地址或凭据。

可靠性边界

  • 客户端必须显式区分连接状态、业务状态、消息状态和状态版本。
  • 写命令携带稳定 requestId,连续输入携带递增 sequence,持久状态使用版本或事务。
  • 普通位置帧允许丢弃和覆盖,不能等待 ACK 后才继续模拟。
  • 开局、计分和结算使用去重、有限重试和快照恢复。
  • 断线恢复先读取服务端快照,再恢复订阅;旧连接回调不能覆盖新一代状态。
  • SQL 保存持久事实;Redis 保存短期租约、限流和在线协调;高频帧不写入数据库。

更完整的状态机和测试要求见 多人状态机 与 验证规范。