lip 的工作原理
读完本页,你就知道你向 lip 提出要求时它对服务器文件夹做了什么、为什么一次更改可以放心取消、lip 的后台进程是做什么用的,以及为什么有些事命令允许而界面拒绝。使用 lip 并不需要了解这些。遇到意料之外的情况时,它们会有帮助。
工作区
工作区是一个存放一个服务器的文件夹。对命令来说,它是你当前所在的文件夹。对界面来说,它是当前工作区,也就是你在界面里选中的那个。
工作区里有两类东西:
- 服务器:游戏、加载器、你的模组、你的世界和设置。这些都是普通文件,放在游戏期望的位置。
.lip/:lip 对这个文件夹的了解。
.lip/ 里的 | 是什么 |
|---|---|
state.json | 已安装包的列表:每个包的版本、来源、是否由你安装,以及它安装的文件。见状态文件。 |
config.toml | 这个工作区的设置。见配置。 |
lock | lip 更改工作区期间占用的文件。 |
server.lock | lip 运行服务器期间占用的文件。 |
journal | 正在进行的更改的记录。只在更改期间存在,或者在更改被中断之后存在。 |
logs/ | 失败报告。 |
wine/ | 在 Linux 和 macOS 上,服务器的 Wine 前缀。 |
lip 对一个服务器的全部了解都在它的文件夹里。移动或复制文件夹,服务器就跟着走。工作区不依赖任何注册表或中央列表;唯一的例外是界面显示的文件夹列表,那只是为了方便。
每个文件的说明见文件和目录。
游戏、加载器、模组和依赖
lip 安装的一切都是包:一组文件,加上一份说明,也就是 lip.toml,其中写明哪些文件放到哪里、这个包还需要什么。包在工作区里扮演四种角色:
| 角色 | 是什么 | 数量 |
|---|---|---|
| 游戏 | 某个版本的 Minecraft 基岩版专用服务器,Windows 版或 Linux 版。lip 从 Minecraft 官方网站下载它。 | 最多一个 |
| 加载器 | LeviLamina,它改造游戏,使之能加载模组。只用于 Windows 服务器。 | 最多一个 |
| 模组 | 由 LeviLamina 加载的包。安装在 mods/<名字>/。 | 任意多个 |
| 依赖 | 别的包需要的包。 | 按需 |
由此得出两条规则。
游戏和加载器是模组立足的地基。 每个加载器版本都写明了它支持哪些游戏版本,每个模组也写明了自己需要什么。所以 lip install 和 lip update 不动游戏和加载器;它们寻找与之相配的模组版本。只有服务器安装会更换地基,并随之把每个模组换成与新地基相配的版本。服务器安装指的是 lip setup,或者点名 LeviLamina 或游戏本身的 lip install。
lip 记得你要求过什么。 你点名的包属于你:在你卸载之前,它一直都在。依赖之所以存在,只是因为有包需要它;没有包需要时,lip 就会移除它。
每个包拥有它安装的文件。两个包不能拥有同一个文件,lip 也绝不删除不属于任何包的文件,只有一个例外:包可以声明自己留下的一些东西,在它被移除时一并清理。游戏对它的某些文件夹就是这样做的。见保留什么删除什么。
操作
一次操作是对工作区的一次更改:一条 lip setup、lip install、lip update 或 lip uninstall 所做的一切,包括它添加或移除的每个依赖。一次操作要么完整地发生,要么完全不发生。
它经过这些步骤:
- 加锁。 lip 占用
.lip/lock。如果另一个 lip 占着它,操作立刻失败。所以两次更改绝不会混在一起。 - 恢复。 如果之前有操作被中断,lip 先把它理顺。
- 决定。 lip 读取已安装的内容,向网络查询有哪些版本,选出一组彼此相配的包,下载它们,并检查每个文件都能放置。到这里工作区里还什么都没变。大多数失败发生在这一步,工作区原封不动。
- 准备系统。 如果要运行 Windows 程序,lip 会确保系统能运行它们。
- 更改。 lip 删除和写入文件,并运行包要求运行的程序。每次更改之前,它先在
.lip/journal里记一笔。被替换或删除的文件,会先以一个隐藏的名字留在原处旁边。 - 提交。 lip 写入新的
state.json,把日志标记为完成,然后删除留在旁边的旧文件和日志。 - 解锁。
在界面里,你在“应用”之前看到的计划就是第 3 步的结果,它是在不做任何更改的情况下得出的。
撤销
因为每次更改都先记录再执行,每个旧文件都保留到最后,lip 总是能退回去:
- 更改过程中某一步失败:lip 按相反的顺序撤销更改。工作区和
state.json恢复原样。 - 你取消了,用 Ctrl+C 或界面里的“取消”:同上。
- lip 被杀掉,或者断电:日志还在。那个文件夹里的下一次操作,或者下一次
lip run,会读取它。如果那次操作已经走到了提交,lip 就把清理做完。否则就撤销那次操作。
有一样东西无法撤销:包的程序在运行期间做过的事。我们要求包的作者把这类程序写成重复运行也无害的样子。
失败会在终端或对话框里得到说明,并被完整地记录下来。见故障排查。
试运行
试运行是一次在第 5 步之前停下的操作。用 --dry-run 就能得到一次试运行;界面在每次更改之前也会做一次,好把计划展示给你。
试运行是一项保证:
- 它不更改任何文件:工作区里的、缓存里的、你的设置里的,都不改。
- 它不运行任何程序,也不往系统里安装任何东西。
- 它不提问。操作会提问的地方,它只说明会问。
- 它不加锁,所以既不会等待也不会阻塞另一个 lip。
- 它不修复被中断的操作。它只告诉你有这样一次操作。
它做的是第 3 步里的一切,包括联网和下载,所以它给出的计划,就是工作区在当前状态下的真实计划。
用法见试运行。
守护进程和客户端
你运行 lip install 时,干活的并不是你启动的那个程序。它把请求交给一个后台进程,也就是守护进程,再把守护进程的反馈显示给你。你启动的那个程序是客户端。
守护进程的存在有两个原因:
- 让服务器比终端活得久。 属于某个终端的进程会随终端一起结束。守护进程不属于任何终端,所以它运行的服务器会一直运行下去。
- 共享同一个视图。 你在任何终端里打开的每个界面,都与同一个守护进程通信。它们看到的是同样的服务器、同样的工作区列表。
你不需要管理守护进程:
- 它会自己启动。 没有守护进程在运行时,第一条 lip 命令会启动它。
- 每个用户一个。 只有它所属的用户才能连接它:在 Windows 上通过命名管道,在其他系统上通过只有你能打开的套接字文件。
- 命令的表现和 lip 直接执行时一样。 它在你的文件夹里工作,使用你终端的环境变量,读取你的键盘输入,输出到你的终端,并以同样的退出码结束。Ctrl+C 也能传到它那里。
- 命令之间互不干扰。 守护进程可以同时执行多个终端的命令,每条命令都有自己的文件夹和设置。
- 它会自己退出:没有客户端连接、也没有服务器在运行,持续 10 分钟之后。只要它在运行服务器,就绝不会退出。
- 更新之后它会被替换。 较新的 lip 会请较旧的守护进程退出,并启动自己的。
lip daemon status 显示它是否在运行,lip daemon stop 在它空闲时结束它。见 lip daemon。
没有守护进程时
如果客户端无法启动或连接守护进程,或者守护进程是个忙得走不开的旧版本,客户端会说明情况,然后自己完成工作。命令做的事是一样的。缺少的只是需要长期后台进程才能做到的东西:托管服务器。
见 lip 自己执行了命令。
托管服务器和服务器锁
服务器有两种运行方式。
普通:lip run | 托管:界面,或 lip run --managed | |
|---|---|---|
| 属于 | 启动它的终端 | 守护进程 |
| 终端关闭时 | 服务器被要求停止 | 服务器继续运行 |
| 它的控制台 | 那个终端 | 每个接入它的客户端,以及每个界面的服务器页面 |
| 谁能停止它 | 那个终端 | 任何已接入的客户端,以及任何界面 |
托管服务器是守护进程运行的、不专属于任何人的服务器。客户端接入它来查看输出、向它输入,之后再脱离,不会打扰它。守护进程在一个工作区里最多运行一个。
无论服务器以哪种方式运行,运行它的进程都占着服务器锁,也就是 .lip/server.lock。“一个工作区一个服务器”正是靠它实现的:第二次启动会发现锁被占用,于是失败。lip 也是靠它得知有服务器在运行,即使那不是它自己启动的。
服务器锁和工作区锁是分开的。运行服务器不会锁住工作区、阻止更改;更改工作区也不需要服务器锁。
见让服务器持续运行。
界面与命令
界面不是另一个有自己一套逻辑的程序。你在界面里做的每件事,都是在当前工作区里运行的一条命令,只是输出显示在对话框里而不是终端里。命令做的事两边完全一样。
界面只在一个地方更谨慎。当前工作区的服务器运行期间,界面:
- 不在那里安装、更新、卸载任何东西,也不在那里安装服务器;
- 不保存
server.properties、allowlist.json和permissions.json。
它会请你先停止服务器:
模组的设置它仍然会保存,这些设置在下次启动时生效。
不管服务器是怎么启动的,都是如此:由这个界面启动、由另一个界面启动,还是由某个终端里普通的 lip run 启动。界面是通过服务器锁得知的。
命令不会拒绝。 服务器运行时 lip install 照样工作,而且无论结果如何,更改都是安全的。如果某个文件因为被运行中的服务器打开而无法替换,操作会失败并被撤销。如果成功了,服务器会在下次启动时用上更改。
| 服务器运行期间 | 界面 | 命令 |
|---|---|---|
| 安装、更新、卸载、安装服务器 | 拒绝 | 允许。可能因文件被占用而失败,这时会被撤销。 |
| 编辑游戏的设置 | 显示但不保存 | lip 没有相应的命令。请自己编辑文件。 |
| 编辑模组的设置 | 允许 | 同上。 |
| 查看已安装的内容 | 允许 | 允许 |
还有一点不同:界面把服务器作为托管服务器启动,而不带 --managed 的 lip run 不是。
页面与命令的完整对应关系见界面参考。
用户目录
在工作区之外,lip 为每个用户保留四个目录。
| 目录 | 存放 | Windows | Linux 和 macOS |
|---|---|---|---|
| 配置 | 你的设置 config.toml | %APPDATA%\lip | $XDG_CONFIG_HOME/lip,默认 ~/.config/lip |
| 缓存 | 从网上下载的文件,留着以免重复下载 | %LOCALAPPDATA%\lip\Cache | $XDG_CACHE_HOME/lip,默认 ~/.cache/lip |
| 数据 | lip 为运行服务器而安装的东西:Linux 和 macOS 上是它的 Wine | %LOCALAPPDATA%\lip\Data | $XDG_DATA_HOME/lip,默认 ~/.local/share/lip |
| 状态 | 界面的工作区列表 workspaces.json | %LOCALAPPDATA%\lip\State | $XDG_STATE_HOME/lip,默认 ~/.local/state/lip |
哪些可以放心删除:
- 缓存,任何时候都可以:
lip cache clean。需要的东西 lip 会重新下载。 - 数据目录,在没有 lip 运行时。lip 需要时会重新安装 Wine。
- 状态目录,在没有 lip 运行时。丢失的只是界面里的文件夹列表,不是文件夹本身。
- 配置目录里只有你的设置。
在 Linux 和 macOS 上,守护进程的套接字在 $XDG_RUNTIME_DIR/lip 里;该变量未设置时在状态目录里。在 Windows 上,守护进程使用命名管道,它不是文件。
除了这些目录、套接字所在的目录、工作区以及你给出的路径,lip 还会写一个地方:它自己的安装目录,在 lip self-update 替换 lip 的时候。它不使用系统的临时目录。