1. 程式人生 > >雲風的 BLOG: 遊戲資源倉庫及升級釋出

雲風的 BLOG: 遊戲資源倉庫及升級釋出

這次實現,我們去掉了目錄和檔案處理方式上的差異,一律都變成了以其內容的 hash 值為檔名(key)的資料塊。倉庫僅僅是一個 key-value 的資料倉庫,以內容 hash (選用了 sha1 演算法)為檔名放在倉庫目錄下。為了避免單個目錄檔案太多(對大多數檔案系統不友好),把內容雜湊到最多 256 個子目錄下。

普通檔案就是完全沒有修改過的檔案本身;而目錄是自己建立的索引資訊。我採用的是文字格式,一行一個檔案描述,記錄有檔案型別(檔案 f 或目錄 t )hash 值,檔名。最終一個目錄索引檔案看起來是這樣的:

d 10a34637ad661d98ba3344717656fcc76209c2f8 f0
f da39a3ee5e6b4b0d3255bfef95601890afd80709 f0_1.txt

這表示該目錄下有一個子目錄 f0 和一個檔案 f0_1.txt 。

這樣做會使得子目錄下任何一個檔案的改變,都會改變目錄索引本身的 hash 值,進而會影響父目錄的 hash 值。也就是說,整個倉庫中的任何一點修改,都最終會影響到根目錄的索引。

在介面上,我僅提供了從 hash 值 query 資料;修改根目錄指標(一個 hash 值);從文字路徑查詢最終物件的 hash 值等這麼幾個。

通過不同的根目錄指標,我們可以讓不同的歷史版本存在於同一個倉庫中,並且最大限度地重用版本間相同的資料。對於伺服器/ PC 編輯器端,我們僅需要提供一個重建索引的操作方法。我們從根索引遞迴遍歷下去,為每個目錄創建出索引檔案。

和 git 不同的是,我們可以不強制把本地檔案系統( git 的工作區)的檔案複製到倉庫中,而是在倉庫中建立一個引用檔案。引用檔案的檔名使用 hash.ref 的命名方式,表示這個 hash 值對應的實際資料並不在倉庫中,而是引用的外部檔案。其內容就是外部檔案的路徑、hash 值和時間戳。由於不同的檔案可能內容完全相同,因為一個引用檔案可以指向不同的路徑,我們會在引用資訊中用多行指名;這樣在一個檔案失效後,還可以依次索引到沒有變更的檔案。

在快速重建倉庫索引時,發現有引用檔案,就只比較時間戳。時間戳相同就不必重算 hash 值。

程式以 c/s 結構執行時,在移動裝置上先建立一個空的映象倉庫,同步 PC 端的資源倉庫。執行流程是這樣的:

首先在客戶端啟動的時候,向伺服器索取一個根索引的 hash ,在本地映象上設定根。

客戶端請求一個檔案路徑時,從根開始尋找對應的目錄索引檔案,逐級查詢。如果本地有所需的 hash 物件,就直接使用;否則向伺服器請求,直到最後獲得目標檔案。api 的設計上,open 一個資源路徑,要麼返回最終的檔案,要麼返回一個 hash ,表示當前還缺少這個 hash 物件;這樣,可以通過網路模組請求這個物件;獲得該物件後,無須理會這個物件是什麼,簡單寫入映象倉庫,然後重新前面的過程,再次請求未完成的路徑,最終就能開啟所需的資原始檔。

這個方式的巧妙之處在於,不需要傳統的版本號管理的方式,就可以完美處理多版本問題。客戶端和伺服器同步過程,只有第一步的根 hash 同步是強制的,且僅同步一個 hash 值。如果移動裝置上有多個不同的版本(比如在不同的開發人員的機器上切換),在一開始同步完根 hash 後,完全不用再有任何網路傳輸。這個過程比 git 切換版本還要快,因為我們在移動裝置上沒有工作區的概念,不需要在切換版本時拷貝檔案到工作區。

在最終版本釋出時,也可以做簡單的打包,避免小檔案過多。簡單的把倉庫檔案打包即可。倉庫中就是簡單的 key-value 結構,處理包檔案比本地檔案目錄更簡單。

同時,釋出版本的更新要比傳統版本號方式要簡單的多。

在釋出產品中,我們多半不提供開發期這種按需索要缺少的資源的工作方式,而是將當前版本的所有資料都提前下載後。在這種預下載模式下客戶端在啟動時從伺服器索取一個根 hash ,如果和本地的根相同,則說明版本沒有更新;否則,將自己的根 hash (代表了一個版本)傳送給伺服器。

伺服器理論上保留有所有歷史釋出過的版本,所以會找到這個歷史版本。這時伺服器可以自己對比這兩個版本,計算出客戶端缺失的物件,然後打包傳送給客戶端。

當然,伺服器可以快取一些常見的版本(因為大部分玩家都會停留在上個版本),省略這步比較操作,直接讓玩家從 CDN 下載打包好的更新包;對於不常見版本,可以計算出一個最接近的更新包(在 CDN 上),然後把更新包中沒有的檔案單獨打包出來。

這個流程其實和 git 的 fetch 操作非常類似,只不過我們可以針對網遊的特質,做一點優化罷了。

因為我們的引擎幾乎全部是用 lua 實現,包括這裡提到的資源倉庫的相關模組,所以理論上這塊程式碼本身也可以被這個倉庫管理。這樣,還涉及一些程式碼自舉和自更新的流程,這一塊的細節也有點意思,我打算過兩天另外寫一篇 blog 介紹。