狀態檔
本頁說明 .lip/state.json,讀完後你能看懂 lip 為一個工作區記錄了什麼,以及每個成員的用途。
lip 在工作區的狀態檔中記錄工作區狀態:安裝了哪些套件、它們的確切版本、來源,以及它們寫入磁碟的檔案。
位置
狀態檔是工作區根目錄下的 .lip/state.json。在 Windows 上,lip 建立 .lip 目錄時會為其設定隱藏屬性;在其他平臺上,它的名稱本身就使其隱藏。
由於狀態檔位於工作區目錄之中,而不是與其路徑綁定,移動、重命名或複製工作區後,狀態檔會隨之保留,已安裝的套件也隨之保留。
.lip 為 lip 保留:安裝的套件若要把檔案寫入工作區根目錄下的 .lip 或其下任何位置,安裝會失敗;purge 模式也永遠不會符合 .lip 及其下的任何內容。
作用
狀態檔有以下幾個用途:
- 狀態追蹤:作為工作區安裝狀態的唯一真實來源。
- 可復現性:記錄安裝每個已安裝套件時所用的套件檔,對於來自 git 儲存庫的套件還記錄其所在的提交。當 lip 再次需要某個套件時,會用該套件檔安裝它,而不是重新解析該套件。
- 解除安裝:
lip追蹤套件寫入磁碟的每個檔案,使lip uninstall能幹淨地刪除與套件相關的所有檔案,避免工作區中殘留"腐爛檔案"。 - 相依圖:區分使用者顯式安裝的套件和作為相依安裝的套件。這樣
lip uninstall也能移除不再被任何顯式安裝的套件需要的相依,lip list也能把兩組分開顯示。 - 環境:一旦得知工作區的執行端和平臺,就將其記錄下來,使後續命令使用同一環境。
狀態檔在一次操作的所有套件都安裝或解除安裝完成後才取代一次。若操作失敗,lip 會復原其對工作區的修改,狀態檔保持原樣,因此兩者始終一致(參見 lip 的工作方式)。
lip 只會整體寫入狀態檔:先寫 .lip/state.json.tmp,再把它重命名為 .lip/state.json。因此,在 lip 工作期間讀取狀態檔的程式,看到的要麼是操作之前的檔案,要麼是操作之後的檔案,不會是寫了一半的。lip list 就是這樣讀取的,不取得工作區鎖。
結構
狀態檔是一個 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;遊戲github.com/LiteLDev/bds不來自任何儲存庫,也沒有。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 及更早版本的工作區見 lip v0.x 的工作區。
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 的套件會被丟棄。