狀態文件
lip 在工作區的狀態文件中記錄工作區狀態:安裝了哪些包、它們的確切版本、來源,以及它們寫入磁盤的文件。
位置
狀態文件是工作區根目錄下的 .lip/state.json。在 Windows 上,lip 創建 .lip 目錄時會為其設置隱藏屬性;在其他平臺上,它的名稱本身就使其隱藏。
由於狀態文件位於工作區目錄之中,而不是與其路徑綁定,移動、重命名或複製工作區後,狀態文件會隨之保留,已安裝的包也隨之保留。
.lip 為 lip 保留:安裝的包若要把文件寫入工作區根目錄下的 .lip 或其下任何位置,安裝會失敗;purge 模式也永遠不會匹配 .lip 及其下的任何內容。
作用
狀態文件有以下幾個用途:
- 狀態追蹤:作為工作區安裝狀態的唯一真實來源。
- 可復現性:記錄安裝每個已安裝包時所用的包文件,對於來自 git 倉庫的包還記錄其所在的提交。當 lip 再次需要某個包時,會用該包文件安裝它,而不是重新解析該包。
- 卸載:
lip追蹤包寫入磁盤的每個文件,使lip uninstall能幹淨地刪除與包相關的所有文件,避免工作區中殘留"腐爛文件"。 - 依賴圖:區分用戶顯式安裝的包和作為依賴安裝的包。這樣
lip uninstall也能移除不再被任何顯式安裝的包需要的依賴,lip list也能把兩組分開顯示。 - 環境:一旦得知工作區的運行端和平臺,就將其記錄下來,使後續命令使用同一環境。
狀態文件在一次操作的所有包都安裝或卸載完成後才替換一次。若操作失敗,lip 會撤銷其對工作區的修改,狀態文件保持原樣,因此兩者始終一致(參見內部機制)。
結構
狀態文件是一個 JSON 文件,由 JSON Schema state.schema.json 描述:
{
"format": 4,
"side": "server",
"platform": "win-x64",
"packages": [...]
}字段
format:狀態文件的格式,4。side:工作區的運行端,"server"或"client"。lip 寫入"server"。platform:工作區的平臺,一個 .NET RID。packages:已安裝的包,按安裝順序排列:一個包排在滿足其依賴的包之後。
side 和 platform 總是存在且從不為 null;缺少它們的狀態文件無效。沒有狀態文件的工作區從其第一次 setup、install、uninstall 或 update 獲得環境(參見 lip install)。
包條目
每個包恰好由以下成員記錄:
manifest:安裝該包時所用的完整包文件。對於早期格式的tooth.json,它是 lip 針對工作區環境從中派生的包文件。其id是包 ID,version是已安裝的版本。variant:安裝來源的變體,默認變體為""。它與manifest的id一起構成包的身份。explicit:true:該包由用戶顯式安裝(例如lip install <package>)。false:該包作為另一個包的依賴安裝。
ref:包的安裝來源:傳給lip install或lip update的參數(例如github.com/user/repo、github.com/user/repo#lua@branch:main或https://URL;本地路徑記錄為其絕對路徑的file:URI),lip setup安裝遊戲或加載器所用的來源(例如github.com/LiteLDev/bds@1.21.0),對於依賴則是該依賴的鍵。lip update會再次請求它。commit:僅用於來自 git 倉庫的包:其歸檔所在的提交,以小寫十六進制表示。來自 URL 或本地路徑的包沒有commit。files:包擁有的文件,相對於工作區根目錄,以/分隔。
已安裝的條目是 manifest.variants[variant] 中 side 和 platform 與工作區相同的那個條目。
lip 只按身份、版本和提交比較已安裝的包與它解析出的包:身份和版本相同、且都不來自 git 倉庫或來自同一提交的兩個包是同一個包,即使 URL 或本地路徑的文件此後已經改變。下載的 ZIP 歸檔或資源的校驗和既不記錄也不檢查。
當 lip 再次需要某個已安裝包的文件時,它用記錄的 manifest 安裝該包:對於來自 git 倉庫的包,從記錄的提交安裝(即使它來自一個此後已前進的分支);否則從其 ref 現在指向的歸檔安裝。卸載不需要兩者中的任何一個。
示例
{
"format": 4,
"side": "server",
"platform": "win-x64",
"packages": [
{
"manifest": {
"format": 4,
"id": "github.com/example/MyMod",
"version": "1.0.0",
"variants": {
"": [
{
"side": "server",
"platform": "win-x64",
"deps": { "github.com/LiteLDev/LeviLamina": ">=1.0.0 <2.0.0" },
"assets": [
{
"type": "mcmod",
"urls": ["https://github.com/example/MyMod/releases/download/v1.0.0/MyMod-server-win-x64.zip"]
}
]
}
]
}
},
"variant": "",
"explicit": true,
"ref": "github.com/example/MyMod",
"commit": "0123456789abcdef0123456789abcdef01234567",
"files": ["mods/MyMod/MyMod.dll", "mods/MyMod/manifest.json"]
}
]
}lip v0.39.x 的工作區
lip v0.39.x 的狀態文件同樣是格式 4,但在其根部以及每個記錄的 manifest 的條目中,把運行端寫作 platform、把平臺寫作 runtime。lip 不讀取這樣的狀態文件:含有 runtime、或 platform 為 "server" 或 "client" 的根部或條目會使命令失敗,並給出消息,提示把 platform 改名為 side、把 runtime 改名為 platform。lip 不會遷移它;請在根部以及每個 manifest 的每個條目中改名這兩個鍵。下一節介紹 lip v0.37.0 及更早版本的工作區。
linux-x64 的工作區
早期版本的 lip 曾通過 --platform 為 win-x64 以外的平臺安裝包。lip 把記錄平臺 linux-x64 的狀態文件當作 win-x64 的狀態文件讀取,其中的 github.com/LiteLDev/bds 是其變體 github.com/LiteLDev/bds#linux,即 Linux 版服務器,下一次 setup、install、uninstall 或 update 會如此記錄它。記錄了其他任何包的 linux-x64 狀態文件會使讀取它的每條命令失敗,並給出指明該包的消息:請在新工作區中重新安裝這些包。記錄其他平臺的狀態文件會使 setup、install、uninstall、update 和 run 失敗。
lip v0.x 的工作區
lip v0.37.0 及更早版本的工作區在其根目錄中有一個格式版本 3 的 tooth_lock.json,而沒有狀態文件。lip 會自動遷移它:
lip list把它讀作它所代表的狀態文件,不做修改。- 下一次
lip setup、lip install、lip uninstall或lip update把該狀態文件寫為.lip/state.json,並刪除tooth_lock.json。
每個包保留其變體、是否顯式安裝,以及它擁有的、位於工作區之下的文件;其 ref 為其包 ID(加上 #<variant>),其 commit 從其倉庫的版本標籤 v<version> 查得。無法查得該提交時,命令會失敗。lip 無法針對工作區環境讀取其 tooth.json 的包會被丟棄。