本页尚未翻译,当前显示英文版本。
PlayServ SDK
**随玩法一起交付的游戏后端。**PlayServ 是面向长线运营游戏的后端即服务:工作室不用自己托管后端,就能运行游戏所需的数据、玩家、房间、匹配和商业功能。你的代码无论在服务器上还是在引擎里,都通过 SDK 与这个平台协作。
模拟不是你的代码
房间、碰撞、移动、预测和同步都在平台内部运行。你的游戏由四部分组成:声明(实体、地图、技能、掉落表、同步策略)、hook(你的规则,在具名步骤上调用)、事件(订阅而不是轮询)和操作(你请求或下达的指令)。每个模块页面都恰好围绕这四种使用方式(surface)组织。
省掉的东西很具体:游戏循环、快照组装、增量编码器、碰撞解算、移动积分、断线重连处理、带延迟补偿的命中校验。像 Tanks 这样的游戏 ~300 行就能写完,快速入门会从头搭一遍。
修改已声明的状态,就是网络调用
没有发送这一步。你在字段旁加一个特性,声明它如何同步(sync),修改这个字段就是网络操作。相对上次确认状态的增量、按 aspect 的策略、优先级和发送频率、保留窗口(retained window)以及变更 hook,都由这条声明推导出来,一行都不用你写。
其他所有声明也一样:事件、RPC、群组、排行榜的排序维度。一条声明会变成每个绑定里的类型化 API、管理面板里的一个界面、hook 可以替换的具名步骤、按主体划分的访问规则,以及每个绑定的生成代码。所以要做版本管理的是声明,而不是生成的代码。见 Data & Subscriptions 和 Schema 即代码。
接口取决于主体,而不是哪一端
没有客户端 SDK,也没有服务器 SDK。只发布一个 SDK,一次调用能做什么,由它背后的主体(actor)决定:玩家、服务、机器人大脑,或运营方。
它针对的典型场景是:玩家的机器创建一个房间(room),然后运行它。这就是主客户端(master-client),它持有房主(room owner)的接口,仅此而已。room-visitor 构建里没有踢人,也没有关闭房间:这些接口根本不存在,而不是被禁用。
权限由原子权限(permission atom)组合而成,所以没有内置的角色等级,一个角色(role)对数据的控制可以细到行和列。见由谁决策;授权怎么写,见 Access & Roles。
一套设计,一套技术栈
怎么写
SDK 是一套设计,有两种收窄方式;上一层能表达的东西,绝不下放到下一层。通用原则在每个绑定中都一样。语言层面的形态只承担该语言范式无法用通用方式表达的部分。引擎层面的形态只承担引擎在语言之上重塑的部分:在 Unreal 中,声明写在引擎自己的反射宏里。
怎么运行
你的游戏代码只和模块(module)打交道。模块由四种原语(primitive)组装而成:事件、RPC、数据与订阅、群组。原语之下是你从不直接调用的 hub:依赖注入、模块挂载、会话、状态恢复、消息服务质量。再往下是传输适配器(transport adapter),每种协议一个;某次调用走哪个适配器,你的代码既不决定,也察觉不到。
模块靠组合,不靠继承
没有可派生的基础模块,也没有可扩展的层级结构。模块构成一张图,因为真实功能总会跨越分支:匹配(matchmaking)在房间里预留席位,掉落通过地图放置物品,聊天存在于房间之中。
这里的“继承”涵盖四种不同的机制,值得分清:实体(entity)的 RPC 是实体的一部分;预设(preset)是具名的一组 aspect,不是基类;覆盖平台步骤,是在你的替代实现上加一个特性;一个模块借用另一个模块,要通过一个收窄被借用接口的装饰器。见继承与组合。
谁看到什么由声明决定,而不是在客户端过滤
四十名玩家时,整房间快照没有问题;两百名时就不行了。兴趣区域规则(interest rules)决定谁收到哪一部分;按主体数据包和广播是同一个声明模型的两种投递模式,在两者之间切换只需改配置,不用重写。远处的物体在消失之前,会按声明的细节层级(detail tier)逐级降低精度。
这也是一项安全特性:**不能泄露的状态从不发送。**战争迷雾和仅所有者可见的字段根本不在数据包里,而不是在客户端隐藏。观战者、管理员和回放要看到更多,就持有范围更大的授权(grant)。见 Visibility。
主机失联后还剩什么
主机宕机不会让比赛结束。对局进行时,房间状态不会在主机之间复制:只有一个所有者,排序就不需要共识。比赛能延续,是因为它的状态是声明过的,而声明过的状态保存在主机之外, 按声明的间隔生成快照。替代主机从最近一次快照恢复。
替代主机拿到的是截至那次快照的完整状态,之后的对局进度会丢失。部署也用同一套机制,但不会丢失进度:让主机排空下线(drain),就是主动走一遍故障转移(failover)流程。
只存在于引擎中的状态无法恢复
只保存在引擎 actor 里的状态没有声明,所以替代主机拿不到它。 ::: 见主机失联后还剩什么;玩家通过哪个宽限期(grace window)重新进入,见 Rooms。
平台的任何步骤都可以换成你的
每个平台场景都是一串已注册的函数,你可以替换其中一环,或者把它包起来。登录、入场校验、购买、提交、上传,每一个都是具名步骤;你的替代实现用一个特性声明,按条件选择版本,平台自带的步骤作为兜底。
你不用 fork 平台的源码,替换它的步骤即可。见 Extensibility。
一套接口,六种语言
一份契约,六种投影:服务器端是 C#、TypeScript、Python 和 Go;引擎端是生成的 Unreal C++ 和 Unity C#。本站每个代码示例都展示全部六种;如果某个绑定没有对应某一步的接口,标签页会说明原因:这一步在引擎之外运行,或者调用权属于另一个主体。
选择语言之前,有两点值得了解:RPC 按引用接收 SDK 对象,而不是展平的 DTO;异步原语是内置的,包括通道、流和群组寻址,所以你可以向整个群组(group)发消息并收集回复,也可以在文件还在上传时就按块读取。
此外还有一个确定性的内存主机:它不依赖任何后端就能运行你的游戏代码,时间由你控制,因此测试结果是确定的。见线程、生命周期与测试。
SDK 刻意不包含的部分,如部署、计费、组织和用户管理,都放在运营平面上。其余一切都能在侧边栏里找到;快速入门是最快的上手途径。