Skip to content

联机架构 ​

联机这块我们做的是一个描述,不是一组接口实现。理由很实在:主流联机方案的本地控制面高度同形。

为什么是「描述」 ​

不管陶瓦联机还是各家第三方联机 SDK,剥掉包装之后都是同一套东西:写一个端口文件 → 本地 HTTP 查状态 / 改状态。

核心启动后把自己监听的本地 HTTP 端口写进一个文件(陶瓦联机叫 http.json),启动器读到这个端口,之后所有操作都是打这个端口的 HTTP 接口:查状态、建房、进房、回空闲、关进程、取日志。

既然形状一样,就没必要为每家写一套 IMultiplayerProvider 实现——每多一个实现,多一份要维护、要测试、跟上游版本同步的代码。所以我们把「怎么装、怎么起、怎么问」抽成一份数据结构描述 MultiplayerSdkProtocol,内置陶瓦联机就是它的一个常量实例:

csharp
Id = "terracotta",
DisplayName = "陶瓦联机",
Vendor = "Terracotta | 陶瓦联机",
VendorUrl = "https://github.com/burningtnt/Terracotta",
Version = "0.4.2",
DownloadUrls =
[
    "https://gitee.com/burningtnt/Terracotta/releases/download/v{version}/terracotta-{version}-{platform}-pkg.tar.gz",
    "https://github.com/burningtnt/Terracotta/releases/download/v{version}/terracotta-{version}-{platform}-pkg.tar.gz",
],
ArchiveKind = "tar.gz",
ExecutableName = "terracotta.exe",
LaunchArguments = ["--hmcl", "{port_file}"],
NodeListUrl = "https://terracotta.glavo.site/nodes",

第三方 SDK 则是同一份描述的 JSON 清单——字段同名,用户或厂商自己提供一份,放进 <数据目录>/multiplayer/providers/*.json 就能被识别,不需要改启动器代码、不需要启动器发版、不需要把 DLL 塞进进程。内置与第三方共用同一份 ProtocolMultiplayerProvider 实现,差别只在描述里的常量;清单里若把 id 写成 terracotta,直接拒绝。

描述里有哪些字段 ​

分四组:身份(id / displayName / vendor / vendorUrl);安装(version、downloadUrls、archiveKind、executableName、versionFile、legacyCoreDir);会话(launchArguments、portFile、portJsonKey、startTimeoutSeconds);本地控制面(apiBase、各 action 的 *Path、各 action 的查询参数名 *QueryKey)。

downloadUrls 支持 {version} / {platform} / {arch} 占位符,按顺序尝试;archiveKind 是 tar.gz / zip / raw;executableName 是解压后统一重命名的固定名;versionFile 的内容等于 Version 才算已安装。stateField / roomField / playersField 指明核心返回的 JSON 里字段叫什么,phaseMap 把原始状态串映射成统一阶段。

一次会话的生命周期 ​

下载官方平台包 → 解压 → 可执行文件统一重命名 → 写版本文件;再以 --hmcl <端口文件> 拉起核心,核心把本地 HTTP 端口写进该文件;之后全部通过本地 HTTP 驱动。

第 2 步有个坑:Windows 上核心进程会再委派一个无窗口子进程去监听,自己随即退出。所以「会话是否在跑」以端口文件 + /state 是否响应为准,不能看进程句柄。

本地控制面必须绕开系统代理

csharp
new(new SocketsHttpHandler { UseProxy = false }) { Timeout = TimeSpan.FromSeconds(10) };

很多玩家开着 Clash / mihomo 之类的系统代理,.NET 的 HttpClient 默认连访问本机回环地址也会丢给代理,代理对 localhost 目标通常直接回 404。现象是「核心明明起来了,却一直查不到联机状态」。

合规:AGPL 与聚合例外 ​

内置的 Terracotta 是 AGPL-3.0-or-later,官方另附聚合例外条款:允许启动器下载并原样运行官方发布包、通过它的本地接口驱动,而启动器本身保持原许可。我们据此保持 MIT,办法是只做三件事:下载官方包 + 解压 + 重命名可执行文件(不改二进制内容、不重链接、不静态链接)。重命名是必要的——官方包里叫 terracotta-0.4.2-windows-x86_64.exe,描述里写的是固定名 terracotta.exe。

配套一条硬约束:任何联机核心都不得进入便携版 zip / NSIS 安装器 / 仓库。核心只存在于数据目录,用户第一次点「安装联机核心」时才从厂商官方 Releases 下载。理由是聚合例外只覆盖「运行用户自己下载的官方二进制」,塞进安装包等于再分发,那就得连带承担 AGPL 的源码提供义务。

已知限制 ​

房主侧仍需在游戏内先「对局域网开放」,全自动接力暂不支持。Minecraft 自己的「开放到局域网」是游戏内菜单里的开关,核心去不去点它我们不保证。

相关链接:Nova 工具集 —— 联机相关工具(multiplayer_host / multiplayer_join / multiplayer_stop)

基于 MIT 许可证(有修改)开源