跳到正文
本页目录

lip 的工作原理

读完本页,你就知道你向 lip 提出要求时它对服务器文件夹做了什么、为什么一次更改可以放心取消、lip 的后台进程是做什么用的,以及为什么有些事命令允许而界面拒绝。使用 lip 并不需要了解这些。遇到意料之外的情况时,它们会有帮助。

工作区

工作区是一个存放一个服务器的文件夹。对命令来说,它是你当前所在的文件夹。对界面来说,它是当前工作区,也就是你在界面里选中的那个。

工作区里有两类东西:

  • 服务器:游戏、加载器、你的模组、你的世界和设置。这些都是普通文件,放在游戏期望的位置。
  • .lip/:lip 对这个文件夹的了解。
.lip/ 里的是什么
state.json已安装包的列表:每个包的版本、来源、是否由你安装,以及它安装的文件。见状态文件。
config.toml这个工作区的设置。见配置。
locklip 更改工作区期间占用的文件。
server.locklip 运行服务器期间占用的文件。
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 所做的一切,包括它添加或移除的每个依赖。一次操作要么完整地发生,要么完全不发生。

它经过这些步骤:

  1. 加锁。 lip 占用 .lip/lock。如果另一个 lip 占着它,操作立刻失败。所以两次更改绝不会混在一起。
  2. 恢复。 如果之前有操作被中断,lip 先把它理顺。
  3. 决定。 lip 读取已安装的内容,向网络查询有哪些版本,选出一组彼此相配的包,下载它们,并检查每个文件都能放置。到这里工作区里还什么都没变。大多数失败发生在这一步,工作区原封不动。
  4. 准备系统。 如果要运行 Windows 程序,lip 会确保系统能运行它们。
  5. 更改。 lip 删除和写入文件,并运行包要求运行的程序。每次更改之前,它先在 .lip/journal 里记一笔。被替换或删除的文件,会先以一个隐藏的名字留在原处旁边。
  6. 提交。 lip 写入新的 state.json,把日志标记为完成,然后删除留在旁边的旧文件和日志。
  7. 解锁。

模组页面上有一个标题为“卸载 github.com/LiteLDev/LegacyMoney”的对话框。它列出将要发生的事:卸载 LegacyMoney 0.16.1 和 LegacyRemoteCall 0.13.0。标注 1 指向计划,标注 2 指向“应用”和“取消”按钮。

在界面里,你在“应用”之前看到的计划就是第 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 为每个用户保留四个目录。

目录存放WindowsLinux 和 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 的时候。它不使用系统的临时目录。

接下来