Builda Online
Builda Online 为 Builda 游戏提供托管联机能力。游戏保留自己的客户端表现和玩法规则,平台负责玩家身份、 匹配、房间调度与联机传输;房间规则由你的 TypeScript 或 Rust Room 源码定义。
复制给 AI
请为当前项目接入 Builda Online:读取 https://ai.builda.game/online/setup.md,并严格按照其中的安装与工作流说明执行。
选择玩家体验
先决定玩家进入游戏、开始一局和结束后的体验。一个游戏可以同时提供多个入口,但每个入口都应属于一种明确模式。
| 玩家看到的体验 | 适合场景 | 联机方式 |
|---|---|---|
| 凑齐固定人数开一局,结束后回大厅重新找人 | 1v1、多人对战、合作闯关 | 单局匹配 |
| 玩家留在同一个房间连续开局,等待时可以加入 | CF/CS 风格房间、桌游房 | 循环房 |
| 玩家进入一个已经运行的世界,新人可以随时加入 | MMORPG 分线、影子世界、长期服务器 | 长期房 |
| 玩家直接开房,通过 Builda App 邀请好友或分享 Room ID | 好友对战、私人合作 | 私有房 |
每种玩法用一个 matchKey 标识,对应 Room 程序、人数、队伍和运行配置。同一玩法可以提供两个独立入口:
- 公共入口:
publicMatchmaking=true,由公共mode(Room、Pool 或 RankPool)决定如何匹配。 - 私有入口:
privateRoomsEnabled=true,直接创建房间,其他玩家凭 Room ID 加入,不参与公共匹配或 Confirm。
两种入口可以单独开启、同时开启或同时关闭。私有房没有房主,创建者与成员平权;仍受队伍容量和房间生命周期 约束。公共 Pool/RankPool 对局已确定参与名单,知道 Room ID 也不能新增成员。 AI 会继续确认每个入口的人数、何时允许加入、断线后如何恢复,以及一局结束后玩家去哪里。
排位积分: 公共 mode 为 RankPool 时,平台按游戏提交的积分配对:入队时传 rankScore(Godot 为
rank_score,Unity 为 MatchJoinOptions.RankScore)。积分必须是 0 到 9007199254740991 的整数。平台只校验数值
范围,不核验积分是否真实;胜负、奖励或排名依赖该积分时,游戏需要自己设计防篡改方案。
支持范围
每个项目只安装一套客户端 SDK、一种 Room 语言和同语言的本地 Mock。
| 部分 | 当前支持 |
|---|---|
| 游戏客户端 | Godot、H5、Cocos Creator 3.8 Web、Unity |
| Room 语言 | TypeScript、Rust |
| 玩法数据面 | WebRTC DataChannel |
| 房间入口 | 公开自动匹配、私有房创建与 Room ID 加入 |
| 玩家数据与支付发货 | Room 内读写跨房间保留的玩家数据;grantBy 为 "room" 的内购订单由 Room 发货 |
| Builda App 联机社交 | Room 聊天群、房间邀请拉起(由 Builda App 提供,游戏不用自建) |
| 本地联调 | TypeScript 或 Rust 源码 Mock |
客户端负责输入、界面和表现;Room 负责校验玩家行为、推进权威状态、广播结果、断线处理和结算。不要把需要 防篡改的胜负、伤害、资源或排名只放在客户端决定。
玩家数据
玩家数据(Player Data) 按游戏 + 玩家保存、跨房间保留,只能在 Room 里读写。客户端没有这套 API
(privateKV 是另一套私域存档)。TypeScript Room 使用 cloudEnqueue / cloudRead* / cloudFlush /
cloudLoadSnapshot,Rust Room 使用对应的 cloud_state_api。
cloudEnqueue成功只表示写入已排队;需要确认这批写入已生效时,调用cloudFlush等待 applied。- 已经持有的快照不会自动刷新;applied 后重新加载快照,或玩家重新进房,才能读到新值。
builda.开头的键由平台保留,游戏不要读写。
语义或故障请反馈玩家数据维护方,不要当成客户端 SDK 问题。
支付发货
内购付费点在 builda.publish.json 的 payPoints 里用 grantBy 声明发货方:
"client"(省略或空串同义):客户端自己幂等发货后调用Builda.iap.claim(orderId),Room 不参与。"room":必须显式声明。客户端支付成功后把orderId通过 Match 消息发给 Room,由 Room 发货;客户端不发货,也不 claim。
Room 发货流程:
- 收到
orderId后调用doOrder/do_order,结果在后续 tick 进入回调,不要阻塞等待。 - 只在结果为
allowed时,在同一回调内用fulfill给该玩家写玩家数据(例如增加金币)。商品以回调结果里的payId为准,不要信任客户端上传的商品 ID;发货对象始终是发来消息的已鉴权玩家。 - 支付回调里不要调用
cloudEnqueue/cloudFlush;平台会等fulfill的写入生效后再完成订单。 already_delivered表示已经发过,不要当成拒单;未完成的订单可以用同一orderId再次doOrder补发。
Builda App 自带的聊天与邀请
联机房间里的聊天和邀请好友不需要你自己做服务器或界面,Builda App 已经提供,SDK 负责在正确的时机把游戏 和它们接起来。
房间聊天
- Builda App 为每个 Room 提供一个聊天群,群就是这个 Room,成员进房后即可在 App 的聊天界面里发言。
- 游戏在拿到 Room 之后让 SDK 入群,同时告诉 App 玩家属于哪个队伍(
team_id由游戏自己定义); 主动离房、Room 关闭、断线重连超时或关闭连接时,SDK 自动退群,游戏不用处理。 - 聊天内容的发送、展示和审核都在 Builda App 里完成。App 推送的群消息也会送到游戏,游戏可以用它做头顶 气泡或战斗记录,但不能通过 Online 发送消息。
- 当前环境没有聊天能力时(本地 Mock、浏览器直接打开、旧版 Builda App),SDK 返回不可用,游戏应隐藏聊天 入口,不要重试或自建替代。
房间邀请与加入
- 玩家进入任何 Room 后,SDK 会自动告知 Builda App 当前所在的房间,App 据此显示联机成员入口并提供邀请好友 的功能。发出邀请由 Builda App 承担,Online 不提供发送邀请的接口。
- 被邀请的玩家在 Builda App 里点击邀请,App 会拉起游戏并把邀请带给它。游戏在启动流程中一次性读取邀请, 取得 Room ID 后按 ID 加入;没有邀请时走正常大厅流程。
- 邀请只是提示,能否进房仍由平台判定:房间已满、已关闭,或公共 Pool/RankPool 对局已经确定名单时, 按稳定错误码提示玩家。
- 游戏也可以用自己的社交系统分享 Room ID,其他玩家凭 Room ID 加入,效果与 App 邀请一致。
聊天和邀请对公开房与私有房完全一样。AI 接入时会按你的玩法决定聊天入口放在哪里、team_id 如何划分,
以及启动时如何处理邀请。
从接入到上线
- 确认项目。 项目应已接入 Builda,并能识别唯一的客户端引擎。
- 交给 AI。 点击页面右上角的“复制给 AI”,把完整接入指令发给项目中的 AI。AI 会安装与项目匹配的 Skill、SDK 和 Mock。
- 确认方案。 用玩家语言确认匹配入口、房间人数、房间生命周期、Bot、断线和结算规则。
- 本地联调。 用两个不同的本地身份验证加入、状态、消息、断线和离开。
- 部署 Room。 只有你明确要求部署或上线时,AI 才会上传 Room 源码并激活线上版本。
- 真实验证。 使用两个真实身份验证匹配、进入 Room 和玩法消息,再决定是否公开客户端。
Room 部署可能立即影响相同兼容修订号下新创建的真实房间。纯客户端更新、兼容的 Room 更新和不兼容协议更新 有不同的发布顺序,交给安装后的 Skill 判断,不要用重复上传来试错。
本地与线上
| 本地 | 线上 | |
|---|---|---|
| 身份 | 合成本地身份 | Builda App 真实身份 |
| Room | 直接运行当前语言源码 | 平台重新构建并托管运行 |
| 传输 | 本地回环 WebSocket | WebRTC DataChannel |
| 聊天与邀请 | 不可用,SDK 返回不可用状态 | 由 Builda App 提供 |
| 玩家数据与支付发货 | Mock 仿真玩家数据和假订单发货 | 真实玩家数据与 Builda 支付 |
| 能验证 | Room 规则、消息、状态和基本生命周期 | 真实鉴权、匹配、WebRTC、聊天与邀请、托管运行 |
| 不能代表 | 生产容量、安全隔离、TURN、分布式恢复 | 本地快速迭代环境 |
TypeScript / Rust 本地 Mock 可以仿真玩家数据和假订单的 doOrder / fulfill,不上线就能跑通发货闭环;但它不代表
生产存储和真实支付,完整验收仍需在线上完成。
页面地址、localhost 或 127.0.0.1 不能用来判断游戏是否连接本地 Mock。Builda App 也可能从设备本地地址加载
真实游戏 Bundle;客户端只使用安装记录和 Builda 本地开发外壳提供的明确配置。
使用限制
下面是每个 Room 和每次部署的当前硬上限,不是建议填满的目标。超过结构或运行上限时,请求可能被拒绝, 严重超限的 Room 可能被保护性关闭;日志超限只会丢弃日志,不会阻塞玩法逻辑。
| Room 运行资源 | 当前上限 |
|---|---|
| 单个 Room 玩家数 | 50 人 |
| 单个 Room 内存 | 64 MiB |
| 单个完整玩法消息帧 | 65535 字节 |
| 单个恢复 checkpoint | 4 MiB |
| 单次生命周期回调提交给平台的命令 | 256 条 |
| 单次回调 / 每 Room 每秒输出 | 1 MiB / 4 MiB |
游戏数据带宽
线上所有 Room 都同时执行用户和房间两层带宽限制。上行指客户端发给 Room,下行指 Room 发给客户端,
1 KB = 1024 字节。额度包含完整玩法消息帧和协议开销,不只是业务正文。
| 范围 | 上行基础速率 | 下行基础速率 |
|---|---|---|
| 每个用户 | 10 KB/s | 30 KB/s |
| 每个 Room | 10 × 有效人数 KB/s | 30 × 有效人数 KB/s |
有效人数为 min(当前已连接的去重用户数 + 1, 房间最大人数)。例如最大 5 人的房间,1 人在线时房间按
2 人额度计算,满员时按 5 人计算;每个用户仍受个人额度限制。同一用户的多条连接共享个人额度,
线上传输方式切换或重连不会立即重置额度。广播按实际接收连接数累计,定向发送只计目标连接。
每个范围、每个方向都必须同时满足:过去 5 秒累计不超过基础速率 × 5 秒,过去 1 秒累计不超过基础速率 × 2 秒。例如单用户上行在额度充足时可集中发送 20 KB,但任意 5 秒最多累计 50 KB,不能持续每秒发送 20 KB。统计按 100ms 分桶,窗口边界最多保守延后 100ms;恢复取决于历史用量何时过期,不保证等一秒就恢复。
超限时拒绝相应玩法消息,并报告 ROOM_BANDWIDTH_LIMIT_EXCEEDED。带宽超限本身不关闭 Room,额度恢复后可继续
处理新消息,但已拒绝的消息不会自动补发。可靠信道也不能保证超额消息被接受;关键操作需要业务确认和有界、
幂等的重试,不能立即反复重发。超限通知、进离房确认和迁移通知不占玩法额度。
上表的 1 MiB / 4 MiB 是 Room 回调向平台提交输出的运行时预算,不是玩家可用网络带宽;一次广播在运行时 按一条输出计算,实际发送时还要按接收连接累计字节,并同时满足网络限额。单帧未超过 65535 字节也不意味着 当前带宽一定允许发送,分块不能绕过累计限制。
设计同步时,用“编码后单条大小 × 每秒发送次数 × 实际接收连接数”估算最坏输出。优先使用紧凑二进制、 增量同步、合并限频和按队伍或可见范围定向发送;完整快照与重连补发也应纳入预算。
源码、配置与日志
| 源码与配置 | 当前上限 |
|---|---|
matchKeys |
64 项 |
| TypeScript 单文件 / 源码压缩包 | 16 MiB |
| Workspace 文件数 / 展开后总大小 | 2048 个 / 64 MiB |
| 匹配确认超时 | 1..60 秒 |
| 快照结构版本 | 1..65535 |
| Room 日志 | 当前上限 |
|---|---|
| 单条正文 | 2048 字节 |
| 每 Room 每秒 | 20 条、16 KiB 正文 |
| 内存缓冲 / 单次查询 | 256 KiB / 200 条 |
| 最长保留 | 7 天 |
大状态应拆成摘要、增量或分页;不要保留无界集合、逐对象调用平台能力,或在每个 tick 广播完整世界状态。 业务状态、重试和结算也不能依赖日志成功写入。本地 Mock 只执行部分消息与日志限制,不模拟线上 WASM 内存、 checkpoint、输出预算、线上双窗口带宽限制或真实容量,因此本地通过不能证明线上容量满足要求。
当前交付信息
本文档更新于 2026-10-23。页面、Skill、SDK 和 Mock 会随同一 CDN 快照验收和切换。
| 组件 | 版本 |
|---|---|
| 联机开发 Skill | 0.36.0 |
| 日志诊断 Skill | 0.5.0 |
| Godot / H5 / Cocos / Unity 客户端 SDK | 0.20.0 / 0.18.0 / 0.11.0 / 0.19.0 |
| TypeScript / Rust Room SDK | 0.14.11 / 0.14.11 |
| TypeScript / Rust 本地 Mock | 0.6.18 / 0.5.19 |
| Room 公开合同 | 0.12.0 |
联机交付物随 Builda SDK 同版本发布,项目按 .builda-agent/sdk-version 锁定唯一版本,不会因为开始新的联机功能
而自动升级。只有你让 AI 运行 builda sdk upgrade 推进版本后,builda-online upgrade 才把联机 Skill、客户端 SDK、
Room SDK 与本地 Mock 一起更新到同一版本,而不是只替换单个文件。
常见问题
需要自己学习全部 API 吗?
不需要。先确认玩家体验和规则,把安装入口交给项目 AI。AI 应根据当前引擎和 Room 语言只读取相关公开资料, 完成客户端、Room、配置和本地验证的闭环。
本地测试通过就能上线吗?
不能。本地 Mock 适合验证业务规则,但不模拟真实鉴权、匹配算法、WebRTC/TURN、容量或安全隔离。上线前仍需用 真实身份创建新 Ticket 和 Room 验证当前线上 Artifact。
可以同时使用 TypeScript 和 Rust Room 吗?
一个项目应选择一种 Room 语言。已有源码时沿用项目证据;新项目无法判断时,由作者选择。客户端引擎和 Room 语言彼此独立,例如 Unity 客户端可以配 TypeScript Room。
聊天和邀请好友需要自己开发吗?
不需要。房间聊天群和房间邀请由 Builda App 提供,SDK 在进房、离房和启动时自动衔接。游戏只需决定聊天入口的 位置和队伍划分,并在启动时处理一次邀请。本地 Mock 不模拟这两项,需要在 Builda App 里用真实身份验证。
如何查看线上 Room 问题?
安装包包含独立的日志诊断 Skill。让项目 AI 查询 Room 日志、丢弃统计或退出原因时,它会使用只读诊断流程, 并避免输出玩家隐私、凭据或完整敏感负载。