來源、匯出與推送
有三條寫入路徑,各自有不同的目標:
- 反映會將編寫暫存內容寫入來源。
- 匯出會將烘焙後的 SO 數值寫回試算表檔案。
- 推送則會逐一儲存格地將烘焙後的 SO 數值寫入正式運作中的 Google 試算表。
匯入來源
匯入來源是設定資源中的一項一等選項。每個來源都會宣告自己的編寫能力(CanAuthor):
| 來源 | 讀取的內容 | 編寫(寫回)能力 |
|---|---|---|
| LocalFile | 一個存放 .tsv / .csv / .xlsx 檔案的資料夾(僅限直屬子項目;一個檔案 = 一個分頁,xlsx 活頁簿則會貢獻其中的工作表) | 完整支援——反映、結構編輯、鍵值/分頁重新命名 |
| GoogleSheet · SheetsApi | 透過服務帳戶 JWT 驗證存取的私人/共享試算表(設定指南) | 完整支援——精準的儲存格寫入、結構改寫、推送 |
| GoogleSheet · ExportUrl | 透過連結分享的試算表匯出網址存取——不需驗證 | 唯讀(CanAuthor = false)——推送/反映/結構編輯/刪除皆會被停用,並附上說明 |
| 自訂提供者 | 任何由外掛註冊的內容(ISheetSourceProvider——資料庫、REST、自有格式) | 由提供者透過其 CanAuthor 旗標自行決定 |
備註:
- ExportUrl 需要 gid 對照表(分頁名稱 →
#gid=值)——沒有 gid 的匯出網址會靜默地只回傳第一個分頁,因此強制要求提供對照表(GoogleSheetGidMapMissing,重複的 gid 會被拒絕)。SheetsApi 模式會自動偵測分頁,不需要對照表。 - 內建的 xlsx 讀寫器是手寫的 OOXML 實作(僅使用
System.IO.Compression+System.Xml——不使用 NPOI/ClosedXML,零第三方程式碼),因此不會帶入任何可能與你專案中其他素材衝突的 DLL。這是同一套共用的編解碼器:同一個讀取器會在 Unity 編輯器中執行,並在編譯為 WebAssembly 後於網頁應用中執行,因此兩個主機絕不會對同一個儲存格產生分歧。它是刻意精簡且對此誠實以告的——只處理數值,不進行任何重新計算:- 公式儲存格會提供檔案中快取的數值。 沒有快取數值的公式,以及錯誤儲存格(
#REF!、#DIV/0!),都會被拒絕(UnsupportedXlsxCell)——請在 Excel 中儲存一次活頁簿以快取數值,或將公式實體化。 - 日期格式的儲存格會依其日期讀取,並呈現為
yyyy-MM-dd——無論是 ISO 儲存格型別,還是樣式為日期格式的一般數字,1900 制與 1904 制日期系統皆會被正確辨識——而不是讀取檔案所儲存的原始序列數字。其他數值格式、合併儲存格與圖表則不會被匯入。 - 這些判讀方式——公式採快取數值、日期以顯示文字呈現、格式被忽略——是讀取器在兩個主機中皆固定採用的政策,網頁應用的匯入對話框還會在一則**「本活頁簿的讀取方式」**說明中,具名列出實際發生過的判讀。
- 儲存格內含定位字元或換行字元會被拒絕(
UnsupportedCellCharacter)——請使用;來表示清單。 - 讀取器無法辨識的儲存格型別代碼,會以其原始儲存文字讀入,而不會被拒絕。
- 公式儲存格會提供檔案中快取的數值。 沒有快取數值的公式,以及錯誤儲存格(
- 本機檔案必須是 Unicode 編碼。 UTF-8 BOM 或 UTF-16 BOM(LE 或 BE)都會被承認;沒有 BOM 時,檔案會以嚴格的 UTF-8 解碼。像 CP949 或 Shift-JIS 這類舊式單位元組編碼會被拒絕(
UnsupportedEncoding),而不會被用來猜測——猜測會在不同機器上解碼出不同結果,並靜默損毀資料。請將檔案另存為 UTF-8。 - 一個來源可以回傳部分輸出——一個損壞的檔案不會連帶捨棄其他可讀取的分頁;問題會以診斷資訊的形式回報。
- 自訂來源提供者會被自動偵測,並顯示在同一個設定下拉選單中——請參閱外掛開發。
- 如果來源在你不知情的狀況下發生變化,Data Studio 會告訴你。 當視窗取得焦點時——或從 ⋯ 選單主動要求時——Data Studio 會重新讀取來源,並將它與你上一次匯入的快照進行比較,只有在資料確實不同時才會顯示一個徽章:僅僅重新儲存過、或只是重新排版過的試算表會保持安靜,因為比對依據的是內容,而不是時間戳記。點擊該徽章會提議執行一次匯入;沒有任何東西會按計時器輪詢,也沒有任何東西會自行匯入,處於離線狀態或未經授權只代表不會出現徽章。它對每一種來源類型都以相同方式運作——本機檔案、匯出網址試算表與 Sheets API 皆然。
匯出——往返流程的回程
Data Studio 工具列中的 ⋯ ▸ 執行匯出 會將烘焙後的 SO 數值寫回試算表檔案。
- 結構來自 baseline,數值來自 SO。 匯出會把目前的數值代入你試算表結構的 baseline 快照中——標記列、欄位順序、註解,以及人工撰寫的文字都會 100% 被保留。
- 語意上的數值往返:允許
1.0↔1的正規化(因為數值相同);浮點數會使用最短的往返格式;小數點永遠使用.。 - 格式:
Tsv/Csv/Xlsx/Json/MatchSource(每個分頁會回到其匯入時的原始格式;來源為 Google 或格式不明時,則回退為 Tsv)。Json是一種只出不進、面向機器而非試算表的格式:每個分頁一個檔案,記錄以物件形式呈現,int/float/bool是真正的 JSON 數字與布林值,而其餘每一種數值——參照、清單、顏色、曲線、自訂型別——都會以試算表所持有的那份精確標準儲存格文字原樣呈現,因此伺服器或外部工具不需解析試算表文字即可使用遊戲資料。JSON 並非匯入來源,一個 JSON 檔案也不帶有任何可供往返的試算表結構——試算表依然是唯一權威。TSV 與 CSV 會每個分頁各自寫成一個檔案;Xlsx會將所有匯出的分頁寫入同一本活頁簿(SheetForge.xlsx),每個分頁依分頁順序成為活頁簿中的一張工作表——活頁簿正是為了容納多張工作表而生的格式,而把它們放在一起,也正是讓參照下拉選單得以跨工作表指向彼此的原因(詳見下方)。在MatchSource下,來源為 xlsx 的分頁會匯聚進那同一本活頁簿,其餘分頁則各自回到自己的檔案。工作表命名規則無法承載的工作表名稱(過長,或含有禁用字元)會被調整,並具名回報——絕不會被靜默重新命名。 - 強制檢查新鮮度:結構描述變更後,若以過時的烘焙結果匯出,會因
ExportSchemaMismatch而失敗(烘焙的SchemaFingerprint必須與 baseline 的相符)——請先執行一次匯入。 - 資源參照會以試算表所使用的位址文字匯出回去——也就是鍵值,若為子資源則是
parent[sub];群組則沿用該欄自己的群組——絕不會以 GUID 形式匯出。型別化的欄(AssetRef@Group<Type>)也以相同方式往返。Color、AnimationCurve與Gradient數值會以其標準文字形式匯出回來(請參閱試算表語法);沒有任何關鍵影格的曲線會匯出為空白儲存格,顏色則會被鉗制在 0…1 之間(不支援 HDR)。
推送——寫回 Google 試算表的儲存格層級操作
Data Studio 工具列中的 ⋯ ▸ 推送至 Google 試算表 會將烘焙後的 SO 數值逐一儲存格地傳送到正式運作中的試算表。除非使用中的來源是 API 模式下的 Google 試算表,否則此項目會被停用,並明確說明原因。其設計目的是絕不破壞其他人正在編輯中的正式試算表。
這條鏈帶來三項保證:
- 沒有經過你核准的儲存格層級計畫,就不會傳送任何內容。
- 正式試算表上在你匯入之後發生變更的儲存格會被略過,絕不會被覆寫。
- 只有當正式試算表仍在那一確切的列上顯示那個鍵值時,列刪除才會被傳送——任何發生偏移的情況都會附上通知被略過,絕不會靠猜測處理。
依序排列的安全防護鏈:
- 需要 SheetsApi 憑證——在 ExportUrl 模式下,推送會在發出任何網路呼叫之前就被拒絕(
GooglePushRequiresSheetsApi)。 - 每個要推送的分頁都需要一個鍵值欄——推送會在正式試算表中依鍵值重新定位每一列,因此能偵測到位置已經移動的列,並安全地略過該筆寫入(絕不會送到錯誤的列)。若沒有鍵值欄的分頁存在變更,會被拒絕(
PushKeylessTabUnsupported)。 - 計畫+核准:系統會計算出一份儲存格層級的差異(baseline 與目前 SO 的比較)作為計畫——寫入、附加、列刪除——並在傳送任何內容之前,顯示出來供明確核准;刪除會獨立列在自己的區塊中,各自具名標示即將消失的那個鍵值。若拒絕 = 不會傳送任何儲存格。
- 傳送前重新擷取即時內容:在傳送之前,會立即重新擷取正式試算表並進行比較。發生衝突的儲存格會被略過,而不會被覆寫(並回報為警告):
PushConflictCellChanged——第三方編輯過該儲存格。PushConflictRowMoved——該鍵值被發現位於與你匯入時不同的列,因此該筆寫入一律會被略過(絕不會被送到錯誤的列)。請重新匯入以重新同步,然後再次推送。PushConflictRowMissing——該列已在外部被刪除。PushConflictDuplicateLiveKey/PushConflictAppendKeyExists——目標不明確。
- 列刪除會先依鍵值比對,才會被傳送。 你所刪除的一筆記錄,只有在傳送前的重新擷取確認其鍵值仍位於你匯入時所見的那一確切列之後,才會從正式試算表中移除:已經不存在的列會被視為已完成(再次推送不會把任何內容重複刪除兩次);若在另一列發現該鍵值——代表試算表已經產生偏移——就會附上通知被略過,絕不會依位置刪除。刪除操作會最後傳送,在每個分頁內由下往上進行,如此一來先前的刪除就不會挪動後續刪除項目的座標。無法刪除列的來源(不具備此能力的自訂提供者)會誠實地回退到舊有行為:刪除會被回報,正式試算表中的那一列則留給你自行處理。
推送之後,請檢查報告中已套用/已略過的數量;如果有儲存格被略過,請重新匯入以進行調解,然後再次推送。
對 Google 的結構變更
在 Google 來源上進行的結構編輯(欄、標記、重新排序、重新命名)會改寫整個目標分頁——會先進行即時差異檢查,並在覆寫你上次匯入後於試算表中發生的任何變更之前,要求明確核准。數值編輯則維持精準的逐儲存格方式;只有結構變更會使用改寫路徑。
反映時進行的 Addressables 註冊
在 Data Studio 中將一項資源拖放到 AssetRef@Group 儲存格上,或從專案中挑選一項資源,除了對試算表暫存變更之外,也可能對專案本身暫存一項變更:將資源加入該群組、將它從另一個群組移動過來,或建立該群組。這些註冊屬於反映的一部分,並在整條鏈路中固定的一個位置執行——無論是本機資料夾、Google 試算表,還是自訂來源提供者,都是同一個位置:
- 預檢會驗證整個預計呈現的狀態,並將暫存中的註冊視為已存在,因此一個指向尚未註冊資源的儲存格並不算是錯誤。
- 試算表會被寫入。 若寫入動作被取消或失敗,下面的步驟都不會執行:Addressables 設定不會被觸碰,這些註冊會繼續保持暫存狀態,等待下一次嘗試。一次因為所有被異動的分頁都被略過、而無法寫入任何分頁的反映(例如只有活頁簿型分頁被異動時),同樣不會執行這些步驟。而一次完全沒有任何內容需要寫入試算表的反映——唯一的暫存變更就是一項註冊——則會執行這些步驟並重新匯入;那一輪不會提交任何其他暫存編輯,因此它仍然可以復原。
- 這些註冊會依序執行:先建立群組(採用預設的
BundledAssetGroupSchema與ContentUpdateGroupSchema),接著新增或移動項目並賦予其位址,最後將設定儲存一次。每一個項目在執行前都會被立即重新檢查,並會選擇略過而非強行執行——當資源在此期間已被刪除時、當位址已被該群組中另一項資源占用時、當群組無法建立或找不到時,以及當再也沒有任何儲存格參照該位址時,皆是如此(一項註冊絕不會建立一個沒有任何內容指向的項目,一個所有項目皆被略過的群組也同樣不會被建立)。若專案尚未擁有 Addressables 設定資源,則會為此建立一個。 - 暫存清單會被清空——已套用與已略過的項目皆然——接著會進行自動重新匯入,讓烘焙流程能夠看見這些新項目。因此,一項被略過的註冊,會在那次重新匯入時,於需要它的儲存格上誠實地回報為
UnknownAssetKey。
Console 會針對每一個結果各印出一行——每個已套用的項目為 Addressables: 'address' → group 'Group',每個已略過的項目則以警告形式印出 Addressables: skipped 'address' (reason)——並附上一行摘要 Addressables: N registered, M skipped。若來源是本機資料夾,反映的完成對話框結尾也會顯示同一行摘要。
寫入試算表的下拉選單
選項數量有限的欄,會在試算表上附加一條資料驗證規則,讓在 Google 試算表或 Excel 中編輯的人可以從清單中選取,而不必記住確切拼法。你不需要開啟任何開關:這些規則會在每一次匯出、推送與編寫寫回時重新計算,並套用到任何能夠承載它們的目標上。
| 欄 | 規則 |
|---|---|
Enum<T> 純量 | 該 enum 成員的固定清單。 |
參照純量(RecordId@Tab,以及具有參照對等性的自訂型別——見 §4.4a) | 目標分頁鍵值欄上的一個範圍,並保持開放式端點,因此之後新增到目標分頁的記錄會自動加入清單。 |
List<>、wrapper 欄,以及鍵值欄本身 | 沒有規則——這類儲存格會存放多個值,或根本沒有目標可供列出。 |
- 僅供指引,絕非強制。 每一條規則都是非嚴格的(Google 為
strict:false,xlsx 為showErrorMessage="0"):清單之外的值會被標示警告記號,但依然會被接受。強制拒絕會破壞「先寫入參照、之後再定義記錄」這種常見工作流程,也會與匯入自身的候選建議機制互相牴觸。 - 這些規則屬於顯示用中繼資料,而非數值。 它們絕不會出現在儲存格中,因此不影響往返流程,而在此功能存在之前產生的匯出結果,與沒有任何規則的匯出結果會是逐位元組相同的。
- 獨立於數值套用。 附加規則是獨立的一個步驟,而非寫入儲存格的附帶效果——最常見的流程(新增一個 enum 成員、不變更任何資料)不會傳送任何儲存格,因此附帶效果絕不會被觸發。此動作具冪等性,重複執行也不會有任何變化。
- 失敗只是警告,不代表推送失敗。 如果數值已經送出,只有規則未能附加成功,推送依然算是成功;再次執行時只會重新套用規則。
各格式能夠承載的內容:
| 目標 | 機制 | 備註 |
|---|---|---|
| Google 試算表(推送/寫回) | setDataValidation,批次成一次請求 | 兩種規則皆支援。參照範圍不設定結束列,因此會隨著目標分頁的成長而跟進。 |
| xlsx(匯出) | 工作表資料之後的 dataValidations | 兩種規則皆支援。由於匯出結果是同一本活頁簿,參照範圍會指向同一份檔案內目標工作表的鍵值欄,並沿工作表向下開放式延伸——與 Google 範圍所代表的意義相同。以下三種誠實情況仍會使規則被略過,並在警告中具名列出:某個成員內含逗號(內嵌分隔符號會將其拆開)、內嵌清單超過格式規範的 255 字元上限(整份加上引號的清單,正是格式所限制的內容),以及規則屬於範圍型、但目標分頁不在該活頁簿中。 |
| TSV/CSV(匯出) | —— | 純文字沒有任何地方能夠承載它們。 |
| JSON(匯出) | —— | 是資料檔案,不是試算表——根本沒有儲存格可以附加下拉選單。 |
任何被省略的項目,都會以每次執行一則 DropdownNotSupportedByFormat 警告的方式誠實回報——並列出每個受影響的欄,因此「為什麼 Google 上有下拉選單,我的檔案裡卻沒有?」這個問題的答案就在報告中,而不是一個謎。這是警告而非錯誤,因為數值本身已完整匯出;缺少的只是編輯上的便利性。
gid 對照表
僅在 ExportUrl 模式下使用。每個項目會將一個分頁名稱對應到該試算表的 #gid= 值(在瀏覽器網址列中,選取該分頁時即可看到)。設定檢閱器只會在相關時才顯示此對照表。
你不需要一個一個把這些數字從瀏覽器中複製出來。設定資源的檢閱器有一個 Google Sheets 區段,可以幫你填好這份對照表。
- 在 ExportUrl 模式下,自動從正式試算表填入 gid 會讀取正式試算表目前的分頁清單,並依此重寫整份對照表,然後儲存設定資源。
- 在 SheetsApi 模式下,同一個面板則改為提供取得正式分頁清單,只會顯示試算表目前擁有的分頁。該模式本來就會自行偵測 gid,完全不需要對照表。
有一項但書:自動填入功能會呼叫 Sheets API,因此即使 ExportUrl 本身的匯入不需要,這裡仍需要設定好服務帳戶金鑰。如果沒有金鑰,它會停止動作並說明原因,而不會寫入一份填了一半的對照表。
建置新鮮度掛勾——過時的烘焙結果會讓建置失敗
每次建置之前,一個建置前掛勾都會針對每個已提交的產生 Database 型別,驗證三件事:
- (i) 烘焙後的 SO 是否存在;
- (ii) 其結構描述指紋是否與 baseline 相符;
- (iii) 其 Addressables 註冊是否存在。
任何一項失敗都會中止建置,並提供可據以行動的說明(例如「請開啟 Tools/SheetForge/Data Studio,按下 ↓ Pull from source,然後再建置」)。正因如此,「烘焙後的 SO 已加入 gitignore」才會是安全的:複製下來的機器或 CI 機器不可能發布一個空的快取。
相關頁面
- 快速上手——服務帳戶金鑰的設定與安全性
- 核心概念——baseline 與往返流程模型
- Data Studio——編寫寫入與推送的比較
- 功能與限制——完整的 Google/xlsx 限制清單