# 展示服启动器兼容性说明

:::tip
本文内容已过期。

本文内容已过期。

本文内容已过期。

文中说法目前已不再适用于最新版整合包，保留本文仅出于保存历史之考虑。

:::


TeaCon 2023 的展示服整合包基于 MultiMC 格式，使用 [Packwiz](https://packwiz.infra.link/) 实现模组自动更新。


使用该架构的动机包括但不限于：

* 解决一部分「模组」（包括但不限于 kotliin-for-forge 之类的 Language Provider）无法通过原 RemoteSync 实现自动更新的问题
* 解决 TeaCon 2021、2022 展示服整合包为同时提供两种格式（MultiMC 导出格式及 MCBBS 格式）的支持，而被迫在同一压缩包内提供两份一模一样的模组及配置文件，进而导致压缩包整体体积过大的问题
* 解决客户端配置文件无法自动更新的问题
* 解决 RemoteSync 本身硬依赖 Forge 生态（具体来说，是 FML 的 `IModLocator` 接口），无法在 Fabric 等其他生态上复用的问题
* 解决 RemoteSync 默认缓存所有已下载模组，导致在试图使用二分法排查故障时，难以确定当前在使用哪个版本的模组的问题
* 解决 RemoteSync 无法提供「可选模组」的问题
* 解决 RemoteSync 没有可视化界面，玩家无从得知当前下载进度的问题
* 解决无法正确区分「只需要安装在客户端」、「只需要安装在服务器」上的模组的问题
* 解决前置模组列表需要手动维护的问题（实际上，这个问题更多是「碧螺春-龙井」系统的问题）
* ……

上述问题均基于 TeaCon 2021、TeaCon 2022 中收获的惨痛经验教训总结而来。


如果你是普通玩家，你可能是从游戏启动前的弹窗那来到这里的。

如果你是启动器开发者，你大概则是从你的用户那里拿到本文的链接的。

本文将简要阐述该整合包所需的 MultiMC 特性。

从这里开始，本文的预期读者是启动器开发者。若你是普通玩家，以下内容对你来说将基本等于天书。

你可以在寻求启动器开发者帮助时，附带上本文地址，这样可以帮助开发者快速厘清头绪。


## MultiMC 导出包格式

这是所有需求的起点。

关于 MultliMC 导出包格式的内容，可参考 MultiMC 自己的说明：[点我跳转](https://github.com/MultiMC/Launcher/wiki/Export-Instance)。

这里只简述最基础支持所需要考虑的内容：

* `instance.cfg`：整合包配置文件，包括但不限于下列信息：
  * 名称
  * 图标
  * 备注
  * 启动所需 Java 位置
  * **预启动命令**（重要。稍后会阐述其细节）
  * ……
* `mmc-pack.json`：整合包所需库（libraries）信息，包括但不限于下列信息：
  * Minecraft 版本
  * LWJGL 版本
  * Fabric 版本（可能不存在）
  * Forge 版本（可能不存在）
  * LiteLoader 版本（可能不存在）
  * Quilt 版本（可能不存在）


## 预启动命令（Pre-Launch Command）

这是一串会在游戏启动前执行的命令。

若你有类 Unix 操作系统的使用经验，这里所谓的「命令」可简单理解为交由 `exec(3)` 启动的程序。或者，如果你熟悉 Windows 平台开发，`Processthreadsapi.h` 里的 `CreateProcess`。

若你有 Java 开发相关经验，这里所谓的「命令」亦可简单理解为「通过 `java.lang.ProcessBuilder` 创建一新的进程（`java.lang.Process`）并等待其运行完毕」。

Packwiz 利用此功能，在游戏正式启动前，运行其 Packwiz Installer，自动完成整合包文件的下载与更新。

MultiMC 只会在预启动命令执行成功（即，退出时状态码为 0）时，继续游戏启动。

MultiMC 执行预启动命令时，使用游戏目录（即俗称的 `.minecraft` 目录）作为当前工作目录。因此，所有相对路径均会以此为基准解析或展开。


## 预启动命令中的环境变量展开

根据 MultiMC 的用户交互界面中的说明，你可以在 MultiMC 中使用下列变量：

* `$INST_NAME`：实例名
* `$INST_ID`：实例 ID（即，该实例所在目录的名字）
* `$INST_DIR`：实例目录的绝对路径
* `$INST_MC_DIR`：Minecraft 本体所在目录的绝对路径
* `$INST_JAVA`：运行该实例时所使用的 Java 的绝对路径
* `$INST_JAVA_ARGS`：启动该实例时，使用的全部命令行参数


不论操作系统，所有变量均以上述格式出现。举例：

* 预启动命令：`$INST_JAVA -version` 会输出当前 Java 版本，因为 `$INST_JAVA` 已展开为 Java 程序的绝对路径。
* 预启动命令：`%INST_JAVA% -version` 会报错，因为 `%INST_JAVA%` 不会展开，系统也找不到名为 `%INST_JAVA%` 的程序。


MultiMC 在将命令传入 `QtProcess` 构造器（下文中将提及）前，会先行展开这些变量，并将这些变量注入预启动命令所在进程的环境变量中。

Packwiz 导出的 MultiMC 格式整合包只用到了上述参数中的 `$INST_JAVA`。


## 预启动命令中对引号的处理

MultiMC 基于 Qt 编写。自然地，MultiMC 在实现预启动命令时，也使用了 Qt 的 `QtProcess`。

MultiMC 的开发环境目前要求 Qt 5.6 及以上版本。这里有一份 Qt 5 的 `QtProcess` 的文档可供参考：<https://doc.qt.io/qt-5/qprocess.html>


`QtProcess` 在处理命令时，对于引号有特殊的处理（由 [QtProcess::splitCommand](https://doc.qt.io/qt-5/qprocess.html#splitCommand) 负责）：


1. 通常按空格分词
2. 遇到双引号把中间内容视为一个词
3. 三个连续的双引号视为双引号本身

举例说明：假设我们使用位于 `/app/my custom java 17/bin/java`的 Java 启动本整合包，且并不存在名为 `/app/my` 的文件或文件夹，同时，在 `$PATH` 中可以找到名为 `echo` 的程序，作用是复读其全部参数，每个参数之间用一个空格隔开，那么

* 预启动命令 `$INST_JAVA -version` 将会报错，提示 `/app/my` 不是可执行程序
* 预启动命令 `"$INST_JAVA" -version` 将正确打印当前 Java 的版本信息
* 预启动命令 `echo hello   world`（注意 `hello` 和 `world` 之间有三个空格）将会打印 `hello world`，因为 `echo` 最终接受了两个参数，分别为 `hello` 和 `world`
* 预启动命令 `echo "hello   world"`（注意 `hello` 和 `world` 之间有三个空格）将会打印 `hello   world`，因为 `echo`最终接受了一个参数，即 `hello   world`
* 预启动命令 `echo """`将会打印 `"`


Packwiz 导出的 MultiMC 格式整合包中利用了这一特性，用于规避 Java 路径中存在空格的情况。

---

**Documents**

- [TeaCon 2023 最终整合包下载地址](https://wiki.teacon.cn/s/2023/doc/teacon-2023-YTTGpQfDVx)
- [TeaCon 2023 展示服务器游览指南](https://wiki.teacon.cn/s/2023/doc/teacon-2023-dUHyCo34sX)
- [「言传身教」奖项评选材料](https://wiki.teacon.cn/s/2023/doc/44cm6kia5lyg6lqr5pwz44cn5aww6ag56ke6ycj5p2q5paz-OdEmjmjsSp)
- [一些教程：2023 年版](https://wiki.teacon.cn/s/2023/doc/2023-yZ8Ifv1mfY)
- [评审/试玩员须知](https://wiki.teacon.cn/s/2023/doc/6ke5a6hlivleeoqewrmomhuefpq-ByPgkTGdq0)
- [选手公示栏](https://wiki.teacon.cn/s/2023/doc/6ycj5oml5yws56s65qcp-lAfinGn66d)
- [建筑服救火记录](https://wiki.teacon.cn/s/2023/doc/5bu6562r5pyn5pwr54gr6k6w5b2v-fkxYkA7PhG)
- [龙井 FAQ](https://wiki.teacon.cn/s/2023/doc/faq-RxvgUOkabT)
- [给选手看的架构图](https://wiki.teacon.cn/s/2023/doc/57uz6ycj5oml55yl55qe5p625p6e5zu-l1w4YmTjkB)
- [展示服启动器兼容性说明](https://wiki.teacon.cn/s/2023/doc/5bgv56s65pyn5zcv5yqo5zmo5yw85a655ocn6k05pio-JGDwifXOLu)