# @fabi-play/protocol 中文文档

规范地址：https://npmcn.com/package/%40fabi-play/protocol/

版本：0\.9\.0
npm 元数据更新时间：2026-11-13T21:54:57.658Z
中文译文更新时间：2026-11-14T10:03:42.915Z
软件包许可：SEE LICENSE IN LICENSE

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40fabi-play/protocol
原文对照：https://npmcn.com/package/%40fabi-play/protocol/?lang=original
项目主页：https://docs.fabiplay.app/
源码仓库：https://github.com/fabiplay/fabiplay-kit

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：135 kB，6 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=18
- 运行依赖：0 个
- 首次发布：2026-11-04T22:37:12.702Z
- 最新版本发布：2026-11-13T21:54:57.459Z
- 近 30 天下载量：2081（2026-10-14 至 2026-11-12，npm 官方下载统计）
- 已发布版本数：10（近 12 个月 10 个）

## 中文简介

手机、屏幕和游戏之间的信息 以及他们的支票.

## 安装

```sh
npm install @fabi-play/protocol
```

也可以使用：`pnpm add @fabi-play/protocol`、`yarn add @fabi-play/protocol`、`bun add @fabi-play/protocol`

## README

# @ fabi-play/ protocol (英语)

电话、屏幕和游戏之间的信息 [Fabi 游戏](https://fabiplay.app/),用他们的支票: 地标`P`, (中文(简体) ), `Body`),玩家,年龄乐队,游戏表 (`fabi-play.json`, (中文(简体) ), `parseManifest`,以及 JSON Schema),和剖析方 运行在其得到的.

```sh
npm install @fabi-play/protocol
```

```ts
import { parseManifest, AGE_RANGES, type GameManifest } from '@fabi-play/protocol'

const result = parseManifest(JSON.parse(text))
if ('errors' in result) console.error(result.errors)
```

名单的JSON斯克马: `@fabi-play/protocol/fabi-play.schema.json` (同样在SDK旁边服役) [https://cdn.fabiplay.app (英语)](https://cdn.fabiplay.app/)无害环境管理,包括类型,不依赖。 文档 : [https://docs.fabiplay.app (英语)](https://docs.fabiplay.app/)。 。 。 .

## 游戏介绍

清单的可选性 `intro` (单位:千美元)`GameIntro`: 1到4个步骤,每个句子在一幅相片或一幅相片上),就是法比所想的 校正前在电视上说,请看"Fabi解释你的游戏" [`@fabi-play/sdk` 读取](https://www.npmjs.com/package/@fabi-play/sdk)。 。 。 . `parseManifest` 报告有什么不对劲 (a) 与《公约》有关的规定; `parseIntro` 给予介绍或无效(电视跳过一个坏的,包括未知的字段), `introText` 页:1 语言和年龄.

-   **判决**:每个语言最多90个字符,计为NFC格式的Unicode代码点(一个emoji是 ; 两端没有空白; 没有控制字符、 单独代位或双向控制 (U+202A–U+202E,U+2066–U+2069). JSON Schema也这么说 除了孤独的代孕者和NFC计数 便携式JSON Schema regexes无法表达: `parseManifest` 这是参考文献.
-   **路径** (单位:千美元)`entry`,一个步骤的 `image`:相对于游戏地址,没有方案,没有领先 `/`没有空的, `.` 或者说 `..` 片段,最多200个字符; ASCII 字母的片段, 数字, `_` 财务报告和审定财务报表 `-` (口袋在里面,不是第一个); 小写扩展 : `.html` 页:1 `entry`, (中文(简体) ), `.jpg` 或者说 `.png` 图像( e)`intro/track.jpg`没有 `intro/Track.PNG`. 没有WebP:旧电视浏览器(WebKit基于Tizen 2.x,webOS 1–2)可能无法解码. JPG (质量) 大约80个),用于截图,PNG只用于平面艺术.
-   **语音短片**:一个句子的片断在 `voiceClipUrl(base, locale, voiceKeySync(text, locale))`,这是 `${base}/voice/${FABI_VOICE}/${locale}/${key}.mp3`,每个语言一个文件夹(`voiceKey` 与约同; `voiceClipUrl` 抛出在本地地址上,但 `it` 或者说 `en`) (中文(简体) ). 键是 UTF-8 字节中的 SHA-256 的第一个32个小写十六进制字符 `` `${FABI_VOICE}\n${locale}\n${t}` `` (单位:千美元)`FABI_VOICE` 实值 `fabi-v1`),在其中 `t` 是 Unicode NFC 中的文本,使用 Java 脚本 `trim()` 已应用; 有效句子周围没有空白, 因此对于任何语言的生成器 `t` 只是NFC的文本。 这么说吧 `voiceKeySync('Salta!', 'it')` 实值 `cbd16ba39816bdc5afd34a3e84082331` 及其剪辑 `https://cdn.fabiplay.app/voice/fabi-v1/it/cbd16ba39816bdc5afd34a3e84082331.mp3`。 。 。 . `voiceUrl(base, key)`旧的 没有本地文件夹的平面布局, 已贬值 。 SHA-256为平原JavaScript,, 同步: 无 `crypto.subtle`因此,它在电视网页上工作,服务于http、旧的电视浏览器和节点18 软件包 `engines`) (中文(简体) ).
-   **电话的按钮**: 在介绍期间,电话发送 `{ type: 'intro', action }`,有动作 `next`, (中文(简体) ), `repeat`, (中文(简体) ), `skip`,或 `replay` (从暂停菜单:再次解释);屏幕告诉它什么是法比 说 `{ type: 'intro', step, steps, text, voice }`。 。 `phase` 信件携带 `intro: true` 当 游戏有介绍:只有这样,电话才会提供 `replay`。 。 。 .

## 盖面

清单的可选性 `cover` 是游戏目录中的游戏卡片: 游戏的16: 9 图像( 建议 1280x720) 拥有文件, `cover.jpg`。 。 。 . `parseManifest` 检查路径( e)`isCoverPath`: 相对、无计划、无领导 `/`,没有 倒斜,没有空的, `.` 或者说 `..` 部分, `.jpg`, (中文(简体) ), `.jpeg`, (中文(简体) ), `.png` 或者说 `.webp` 小写,最多 `COVER_MAX_PATH`, (中文(简体) ), (原始内容存档于2017-10-12). 128, phone. 文件本身由 Fabi Play 的审查检查, 常数导出在此 : `COVER_MAX_BYTES` (15万个), `COVER_ASPECT` (第16/9号决议) `COVER_ASPECT_TOLERANCE` (0.02,相对数) `isCoverAspect(width, height)`· ; `COVER_EXTENSIONS` 财务报告和审定财务报表 `COVER_RECOMMENDED` (1280×720) (中文(简体) ).

## 方向

清单的可选性 `orientations` 列出游戏可以玩的屏幕取向( e)`Orientation`数字 : `'landscape' | 'portrait'`,他们全部在 `ORIENTATIONS`:非空地,不重复;没有手段 `['landscape']`,哪个 `gameOrientations(manifest)` 适用。 一个电话(“Gioca qui / Play here”)是直立的 只提供列表的游戏 `'portrait'`电视和石碑都是景观 屏幕说方向a 游戏开始于 `init.orientation` (单位:千美元)`orientationOf(width, height)`: 宽度( h): `'landscape'`; 缺席 旧宿主,意思是 `'landscape'`。这是信息:一个游戏的外观大小.

**瞄准目标.** `aimPoint(body | frame, side, { orientation, view })` 一条规则是 手对准 屏幕(它碰到的),分数:地平面 `toScreen(view, wrist)`; 在肖像中, 相机图像所在之处 在电话底部很小,手腕被肩膀伸到整个屏幕上(`HAND_SPACE`,则 长度 `PlayerFrame.leftHand`,被夹到0–1;臀部看不见的躯干至少是 `TORSO_PER_SHOULDER_WIDTH` 肩宽。 当手腕(在肖像上,肩膀也一样)不见时,是无效的 (单位:千美元)`AIM_VISIBLE`) (中文(简体) ). `AimHold` {\\fn黑体\\fs22\\bord1\\shad0\\3ahbe\\4ah00\\fscx67\\fscy66\\2chffffff\\3ch808080}把一只失落的手藏起来 `AIM_HOLD_MS` (300) (英语). SDK的车 `play.aim` 编辑 发动机 `hands()` 应用它.

**安全区.** `init.safeArea` (可选的)是电话的出处, 圆角和家酒吧封面, 分数 在游戏的取景口: `{ top, bottom, left, right }` (单位:千美元)`SafeArea`) (中文(简体) ). `parseSafeArea` 每边夹到0-0.25 (单位:千美元)`MAX_SAFE_INSET`失踪时为0; `NO_SAFE_AREA` 是所有0, 什么主机不发送它的意思.

**局部姿势.** `init.localPoses` (可选)是 `true` 当画面中的尸体来自同一个相机 设备作为屏幕(单独播放的电话或平板电脑),而不是网络上的电话。 然后SDK滑动 默认情况下插出变相器较轻( 空闲度: 要吸收的不快), 除非游戏给出 (单位:千美元) `smooth` 数额。 缺席(老年人主机,电话发送到电视)是指 `false`。 。 。 .

## 两个屏幕上的旋转木马

电话可以显示电视机的旋转木马游戏,两者同时移动. 成年的刷子,电话发出 `{ type: 'focus', game }` (CaptainMessage) 对于它中心卡片: 仅一个预览,没有开始(`pick` 开始 (游戏) 屏幕将同一张牌集中,并用其声音(屏幕拥有声音 电话保持安静。 当屏幕的旋转木马因任何其他原因而移动时(遥控器、手光标、另一个) 电话,它发送 `{ type: 'focus', game }` (ScreenMessage)和电话跟随. 都一样 `pick` (单位:千美元)`isGameId`; 其目录中没有游戏的一面忽略了消息.

## 电线上的可能性

电话流 `{ type: 'pose', t, bodies }` 在一个无序、无转发的数据上每秒约30次 频道. 列表的屏幕 `POSE_CODEC_VERSION` 致欢迎词`binaryPose: [1]`,参见 `acceptsBinaryPose`获得 在二进制中, `encodePose` 电话和 `decodePose` 在屏幕上; 任何其他屏幕都得到 JSON 和屏幕 都读了 版本 1, 小- endian :

| 折减 | 大小 | 外地 |
| --- | --- | --- |
| 0 个 | 1个 | 魔术 `0xFB` |
| 1个 | 1个 | 编码版本, 1 |
| 2个 | 1个 | 类型, 1=姿势 |
| 3个 | 1个 | 尸体数,0-4 |
| 页:1 | 第8条 | `t`,浮点64 (按电话钟计) |
| 第12条 | 每人132人 | 33个地标 × 金特32:比特0-9 x-1000,10-19 y-1000,20-29能见度 1000,30-31 0 |

因此有12个+132×个活体字节(4个活体540个活体). 值为千分之一 `toPlayerSpace` 弹入,所以解码 姿势和JSON一样. `decodePose` 返回为空(长度错误,), 魔法,版本或类型,超过4个身体,值超过1000, `t` 永远不要扔.

大多数游戏只需要 [`@fabi-play/sdk`](https://www.npmjs.com/package/@fabi-play/sdk),它包括它使用的东西 从这个包裹.

## 许可证

Fabi Play Kit License:可自由使用,仅用于为Fabi Play制作游戏和工具. 见 [自由主义](https://npmcn.com/package/%40fabi-play/protocol/LICENSE)。 。 。 .

## 电视遥控器

清单的可选性 `inputs` (单位:千美元)`GameInput[]`从 `GAME_INPUTS`数字 : `body`, (中文(简体) ), `remote`; 缺席 `["body"]`,参见 `gameInputs`),表示在没有电话的情况下,游戏是否可以用电视遥控器来玩; `parseManifest` 需求 `body` 在其中,没有重复和未知值。 屏幕告诉游戏如何播放会话 `init.input` (缺席) `'body'`和,与 `'remote'`,是否远程点(`init.pointer`LG的魔法遥控器: 游戏页面中的普通指针和鼠标事件). 屏幕将密钥转换成手势( QQ 或 确定) `jump`, 举行 `squat`编号 :`stand`车道,好两次 `clap`,退后平台的暂停),一次扮演一个孩子:init's `players` 是孩子现在在玩(一个id)。 有几个孩子轮流玩游戏,他们的名单上写着 `"turns": true` (仅以 `remote` 内 `inputs`;这意味着游戏遵循回合合同;没有它,只有一个孩子: `init.turns` 按顺序列出每个儿童`parseTurnOrder`; 缺席 : `players`) (中文(简体) ). 游戏每个孩子玩一回合; 当 a 孩子们的圆末(也撞倒了) 转弯不动,它问 `break('turn')`,屏幕总是回答 `break-start` (它转动的屏幕), `{ type: 'turn', players: [2] }` (确切地说,一个id,下一个顺序; `parseTurnPlayers` 检查其形状)和 `break-done`下一个孩子的傀儡就在中道 之后 最后一轮比赛以每个孩子的分数结束. `level` 财务报告和审定财务报表 `gameover` 课间休息从不交换玩家; 主机 在转弯前回答 `turn` 作为未知的原因,一次。 游戏按玩家ID保持分数. 见"用电视遥控器播放" [`@fabi-play/sdk` 读取](https://www.npmjs.com/package/@fabi-play/sdk)。 。 。 .

### 箭头作为光标

清单的可选性 `"remoteCursor": true` (仅以 `remote` 内 `inputs`)以远程模式询问屏幕, 将箭头变成自由光标而不是手势。 屏幕保存所持箭头并运行 `KeyCursor` 每个动画框( e)`update(held, dtMs)` → `{ x, y }`, 0–1 游戏框: 初慢而快 箭头持有的时间越长,对面箭头取消,对角不快,被夹在边上,一个长相框计算 等于100毫秒; `reset()` 在游戏上方绘制光标,并发送 `{ type: 'cursor', x, y, down }` (单位:千美元)`parseCursor`当它移动,当OK走下或上。 里面写着 `pointer: true`以魔术遥控器发誓, 这个 SDK在游戏的页面中将信件变成指针和鼠标事件,因此游戏无论用哪种方式都可以看到鼠标. 回来 保留平台的暂停; OK是光标的按钮, 不是跳出 .

## 手机上的图片

游戏可以在家庭手机上保存一张照片. 路径和消息:

1.  屏幕告诉游戏是否可以: `init.pictures` 当它改变时, `{ type: 'capabilities', pictures }`。这是真实的,当一个电话 `hello` 说 `pictures: true` 链接( app; 网络电话) 不说).
2.  游戏发送 `{ type: 'picture', data, title?, id? }` (游戏): `data` a base64 数据 URL, `data:image/jpeg` 或者说 `data:image/png`最多 `MAX_PICTURE_BYTES` (3 MiB of image),其字节真正开始为 该类型(`checkPicture`· ; `parseGameMessage` 其他东西都丢掉); `title` 最多 `MAX_PICTURE_TITLE` (60个) 字符,由 `parsePictureTitle`。 。 。 .
3.  屏幕通过他们可靠 订购的数据频道发送到手机上 片段,因为数据频道 不要携带多兆字节消息( 16 KiB 是每个浏览器需要的) : `chunkPicture(dataUrl, id, title)` 给 `{ type: 'picture-start', id, mime, size, title? }` (单位:千美元)`size` 在图像的字节中),然后 `{ type: 'picture-chunk', id, seq, data }` (单位:千美元)`PICTURE_CHUNK_CHARS`16,000个基数64,每个但最后一个, `seq` 从 0 开始;那么, JSON 的每个消息都保持在 16 KiB 以内 `{ type: 'picture-end', id }` (史记.
4.  电话给他们喂的 `PictureAssembler` (按任意顺序划出;缺少块,超过极限的图片,a) 块过其大小, 字节不是该开始的类型, 全部失败有原因) 和答案 `{ type: 'picture-saved', id }` 或者说 `{ type: 'picture-failed', id }` (主计长发言).
5.  屏幕回答游戏 `{ type: 'picture-result', id, result }` (单位:千美元)`parsePictureResult`: `'sent'`, (中文(简体) ), `'unavailable'`, (中文(简体) ), `'too-big'` 或者说 `'error'`。 。 。 .

隐私:照片只通过直接链接(WebRTC,加密)通过电视 手机,从未通过或进入服务器, 其它地方也没有副本 Games只能这样发送一个JPEG或PNG图像.

## 常见问题

### @fabi-play/protocol 是什么？

手机、屏幕和游戏之间的信息 以及他们的支票\.

### 如何安装 @fabi-play/protocol？

在项目目录运行 npm install @fabi-play/protocol，或使用 pnpm add / yarn add / bun add。

### @fabi-play/protocol 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @fabi-play/protocol 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @fabi-play/protocol 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=18。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @fabi-play/protocol 可以商用吗？

@fabi-play/protocol 使用 SEE LICENSE IN LICENSE 许可。许可条款写在包内的独立文件中，请阅读该文件确认可否商用。以上为概括，不构成法律意见，请以许可原文为准。

### @fabi-play/protocol 的下载量有多少？

根据 npm 官方下载统计，@fabi-play/protocol 在 2026/10/14 – 2026/11/12 这 30 天内被下载 2,081 次，日均约 69 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @fabi-play/protocol 还在维护吗？

最新版本 0\.9\.0 发布于 2026/11/14（今天）；至今共发布 10 个版本，其中过去 12 个月发布了 10 个；首次发布于 2026/11/05。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

