RCC文档Core

底层与自研

不看也不影响你玩。想知道「服务器出问题的时候我们在改什么」的话,答案在这一页。

这一页不是玩法说明,是底下那层:插件之间怎么说话、数据存在哪、为什么我们宁可自己写一套。

不看也不影响你玩。想知道「服务器出问题的时候我们在改什么」的话,这里有答案。

一句话地图

第 0 层  引擎绑定      对 BDS 的安全封装
第 1 层  通用基建      存储层(SQLite)  网页服务
第 2 层  三个管理器    权限   世界   地皮
第 3 层  玩法与工具    RCC 核心   建筑工具   起床战争

箭头只能从上往下画。 第 3 层的玩法插件不允许直接碰第 2 层的内部实现,只能经过各管理器对外的 SDK。

这条约束不是风格问题。它换来的是:一个只想列世界的插件,不会被迫拖进整套权限类型;改权限的内部结构,不用重编玩法插件。

三个管理器每个都是三件套:

部分 是什么
SDK 契约类型、服务名。别的插件只依赖这一个
内核 纯逻辑,不依赖游戏引擎,能在没有服务器的机器上跑测试
胶水 把两边接起来,唯一必须在真服务器上验的一层

「内核不依赖引擎」是整套架构里最值钱的一条。权限判定、地皮几何、继承关系、准入规则,全部能离线测——不用开一台 BDS 就能验证「先给整包再挖洞」这种规则算得对不对。

插件之间怎么说话

两条通道。

通用通道是「名字 + JSON 进,JSON 出」,任何语言写的插件都能用。

快车道只服务一个特例:两边都是同一次编译出来的 Rust 插件。这时候可以直接递函数表过去,跳过序列化。

收益不在「快」

一次通用调用一两微秒,命令门禁那个频率下根本不是瓶颈。真正的收益是另外两件事:

批量。「这个玩家对这 40 个节点分别有没有权限」,走 JSON 是 40 次往返、或者一个 40 字段的载荷;走快车道是一次调用,而且上下文只算一次

类型。 权限判定的结果是个三态——允许、拒绝、没规则。走快车道它原样过去;走 JSON 它得被拆成字段名再拼回来。

一个不会被报上来的 bug

假设 JSON 那头字段名从 allowed 打成了 allow。读的一侧取不到值,退回默认的 false

于是表现是:「这个人没有权限」

没人会觉得这是 bug。玩家以为自己确实没开通,管理员看配置又看不出问题,最后大概率归档成「他记错了」。类型直接过去就没有这一整类问题。

指纹,不是版本号

Rust 没有稳定的二进制接口。所以快车道比的不是版本号,而是一整串指纹:编译器完整版本、目标平台、编译档位、优化等级、车道名、契约版本,加上函数表的内存布局。

任何一项对不上,一个指针都不会递出去,自动降回 JSON 通道,功能一条不少。

失败模式是「慢」,不是崩。这是这条快车道敢存在的全部理由。

有个真实的坑值得记一笔:早先几个插件分属不同的编译配置,优化等级一个写 2 一个写 3。指纹里含优化等级,于是跨插件调用静默降级成了 JSON——功能完全正常,所以谁也没发现,只是每次调用多一次序列化。

现在整棵树在一个工作区里一次编完,这个坑从根上没了。

数据存在哪

统一走一个共用的 SQLite 层,SQLite 本身编进二进制,服主不需要自己装

地皮归属、权限组、世界配置、在线时长,都在那里面。少数不适合关系型存储的(比如地图瓦片)单独落文件。

网页管理界面

权限插件带一个网页界面,用的是一个零依赖的小 HTTP 服务,前端直接内联在二进制里——没有 node_modules,没有构建步骤,没有第三方前端资产。

默认关着。开启需要同时满足两件事:显式打开,并且填一个非空 token。token 是空的就拒绝启动,日志里写明原因。默认只监听回环地址,绑到别的地址上会留一条警告。

三条自我约束

这三条是写给未来接手的人的,不是给玩家看的,但它们直接影响你遇到 bug 之后多久能修好:

单个文件不超过 400 行。 三百多个源文件,最长那个 394 行。

代码里没有注释。 所有「为什么这么写」搬到单独的说明文件里,路径和源文件一一对应。理由是注释会和代码一起被改乱,而独立的说明文件改起来必须是有意识的动作。

一条命令编完整棵树。 十几个包一次编完,不用切目录,也就不会再出现「各自编各自的、指纹对不上」那种事。

这些约束由脚本机器检查,不靠自觉。

为什么不用现成的插件

三条,按重要性排:

出了问题只能等。 现成插件的作者不欠我们什么。等不到就只能把功能去掉,而去掉的那个功能是玩家已经在用的。

我们的需求确实特殊。 这是一台以红石为主的服务器,对 tick、对区块、对实体的要求比一般服务器苛刻得多。通用插件的默认取舍常常不适用。

提出问题的人和改代码的人在同一个群里。 这是最实际的一条。你在群里说一句「这个开关没生效」,当天就能有人去看那段代码。

代价也很明确:所有的 bug 都是我们自己的 bug,没有人可以怪。

常见问题

服务器崩了是不是这套东西的问题?

有可能,也可能是引擎本身或者某个红石电路。崩溃日志我们会看,能定位到具体插件的话会在群里说明。

插件开源吗?

目前没有公开仓库。想了解具体某处怎么实现的可以在群里问。

为什么用 Rust 写基岩版插件?

主要是为了「越界了会编译不过」这件事。方块坐标、维度号、权限判定结果这些东西在类型上是分开的,写错了在编译阶段就被挡住,而不是在服务器上表现成一个奇怪的现象。

一个插件挂了会不会连累别的?

设计上不会。插件之间是可选依赖,连不上就走「离线」路径——比如权限插件不在,别的插件不会报错,只是所有判定退回各自的默认值。这条路径是正常路径,不是错误处理。