Skip to main content

3 posts tagged with "Biomes"

View All Tags

Wei Ji

Galois

Galois is a collection of tools for asset generation. Galois introduces a custom DSL for defining assets (e.g. meshes, textures, game objects, configuration files, maps, worlds), a build system for generating binary asset data, and tools for viewing and editing assets through a UI.

以上是 README 的原始描述。

遊戲素材

src/galois/data 目錄下是該專案大部分透過 Git LFS (Large File Storage) 儲存的資料所在的路徑,內容如下:

tree -L 1 src/galois/data
src/galois/data
├── animations
├── audio
├── blocks
├── color_palettes
├── crops
├── editor
├── flora
├── gaia
├── glass
├── icons
├── item_meshes
├── mapping
├── maps
├── npcs
├── placeables
├── textures
└── wearables

並且當中的結構與 biomes_data_snapshot.tar.gz 的內容大致吻合:

biomes_data_snapshot.tar.gz 是什麼呢?它是 Biomes 透過腳本在本地運行時,理論上應該被下載的檔案,但是官方的伺服器已經不再提供該檔案,GitHub 上有一些 issue 有反應這件事:

換言之,這個 biomes_data_snapshot.tar.gz 是檔案想接手這個開源專案的人第一個會碰到的問題(在不理解程式直接使用官方提供的腳本試圖運行該專案的話),而這個 biomes_data_snapshot.tar.gz 檔案的祕密就藏在 Galois 組件內。

info

打包用的腳本其實存在於 scripts/b/data_snapshot.py,不過我認為理解資料比輸出資料還重要。

素材瀏覽器與編輯器

src/galois/js 中可以看到 editorviewer

tree -L 1 src/galois/js
src/galois/js
├── assets
├── components
├── editor
├── index.d.ts
├── interface
├── lang
├── publish
├── python
├── README.md
├── scripts
├── server
├── tsconfig.json
└── viewer

根據程式碼判斷,它們應該是 Electron + React 實現的 GUI 工具。

這個組件的定位使其不涉及遊戲本身的實做,並且提供一些必要的封裝給遊戲開發使用,因此作為我的下一個解析目標。

AQL (Asset Query Language) utilities

這個模組在 Biomes 的原始路徑是:src/galois/js/lang, 並且當中缺少一些的 Typescript 程式碼是由 src/galois/py/assets 下的 Python 腳本生成的, 因為涉及跨語言的操作,而且沒有其他仰賴,因此我將其抽出作為獨立的套件。

"Asset Query Language" 只是根據內部實現的邏輯跟被呼叫的方式先隨便取的暫時性名稱,它被呼叫的時候大致上長這樣:

import * as l from "@/galois/lang";

const skeleton = l.toSkeleton(animationInfo.skeleton);
const tPose = l.ExtractInitialPose(animationsGltf, skeleton);
const posedVoxJointMap = l.ToPosedVoxJointMapFromVoxLayers(vox, skeleton);

let gltf = l.ToGLTF(
meshJointMap,
tPose,
l.ExtractAllAnimations(animationsGltf, skeleton)
);

不然 lang 並不是一個方便理解的名稱。

當前挑戰

Galois 的外部仰賴

首先, Galois 對 shared 有仰賴,這個部份沒有什麼問題,畢竟 shared 職責上就是處理那些同時會被前端與後端使用的通用邏輯。

但是 Galois 對 server 仰賴就有問題了,因為 server 是應用程式的後端實做,在素材瀏覽或編輯階段不應該對後端邏輯有所仰賴。

仔細看發現跟發布有關,或許可以將該部份暫時從 Galois 中移除,剩下的部份一小部份如果實做不是很多可以視情況直接從 server 移過來。

shared 的循環仰賴

因為 ecs 組件大部分的程式碼用 Python 生成的,可以的話我想拉出來變成單獨的套件,但是因為它跟 shared 之間有密切的耦合(循環仰賴),無奈之下只能用 monorepo 處理。

shared 的測試仰賴

shared 也有對 server 的仰賴,另外一個則是 voxeloo,但是它使用的是不明的實做:

import wasmLoader from "@/gen/shared/cpp_ext/voxeloo-simd/wasm";

return wasmLoader({
wasmBinary: await readFile(wasmFile),
wasmMemory: makeWasmMemory(1024),
});

之前我用 Emscripten 編譯出來的 Voxeloo WASM 本身並沒有符合上述 wasmLoader 界面的東西,Bazel 裡面我也看不出來這個東西是在哪裡實做的。

這個部份是寫在單元測試內的東西,不影響遊戲本體運作,因此我合理懷疑這個是專案經過重構後遺留的痕跡,某個時間點之後就不再維護單元測試而只注重遊戲運行正常與否,造成單元測試內的實作已經過期。

Wei Ji

voxeloo WASM 綁定

Biomes 對 voxeloo 實做了兩種綁定/交叉編譯,將其從 C++ 專案轉換成其他語言的函式庫,其中一個是 Python,並且透過 pybind11 實現;另外一個則是 WASM 並且透過 Emscripten 實現。要注意的是編譯結果有分成一般跟 SIMD (Single Instruction Multiple Data) 兩種。

考量 Biomes 有自己分別在 client 和 server 端實做自己的 WASM 載入函式:

src/server/shared/voxeloo.ts:

export async function loadVoxeloo(): Promise<VoxelooModule> {
//...
}

src/client/game/webasm.ts:

export async function loadVoxeloo(
clientConfig: WasmConfig
): Promise<VoxelooModule> {
//...
}

我就不在 WASM binding 這一層做其他處理了,直接將 Emscripten 編譯的結果發布到 NPN 上。

順道一提,我有試著讓編譯過程執行在 Docker 內,但是並沒有成功,換句話說當前編譯過程可能仰賴我的 host 安裝的某些東西,但是現階段我不打算處理這個問題,等有機會再回來看要怎麼解決,確保「能成功編譯的環境」不會失傳。

cayley

cayley 看起來是另外一個運算吃重的模組,以 Rust 撰寫。

WASM 綁定

這裡遇到一個小插曲:

  • cayley 專案使用 Rust 1.70.0
    • WORKSPACE.bazel 內有寫: RUST_VERSION = "1.70.0"
  • 專案的 wasm-bindgen0.2.83
    • Cargo.lock 內有寫。
  • wasm-bindgen 要求 wasm-bindgen-cli 0.2.83
  • wasm-bindgen-cli 0.2.83 要求 Rust 1.82+
    • wasm-bindgen-cli 0.2.83 有一個仰賴要求 1.82+

我不知道這個相依性地獄 (dependency hell) 是怎麼產生的,也不打算深究,直接包進 Docker 裡面分階段在不同的 Rust 版本編譯。

Biomes 原本只有發布 bundlernodejs 兩種版本,後者是給 CommonJS 用的,前者則是直接 import WASM 檔案,需要專案有使用 Webpack 或是 Loader 之類的自動翻譯模組,因此我額外加上了 web 當作 ESM 的預設包,顧名思義是給瀏覽器用的。

wasm-bindgen-cli 工具蠻完整的,除了 WASM 載入器的 Javascript 有準備好,用來給 Typescript 的 .d.ts 也有生成,該說不愧是網頁前端友善(?)的 Rust 嗎?

Python 綁定

Python 這邊是使用 pyo3/maturin 的工具完成,並且這次我試著發布到 PyPI 去,有點意外 Rust 的部份也一併被上傳,從 Python 安裝的時候似乎會在本地跑編譯。

Wei Ji

前一陣子在 GitHub 上找到有興趣的專案,放到口袋清單之後,終於在上週 (2025-10-22) 忍不住把它拿出來玩了,這裡紀錄一下到目前為止 (2025-11-02) 的進度。

來自 Global Illumination, Inc. 的 Biomes

Biomes 是一個(理論上)能夠在瀏覽器執行的開源方塊沙盒多人連線角色扮演遊戲 (MMORPG)。

為什麼我說「理論上」呢?因為這個專案已經葛屁兩年了,在親眼看到以前說不準它的實際情況。

並且專案停滯的時間點剛好是 OpenAI 收購 Global Illumination (Biomes 背後的公司團隊) 的時間點1

動機

不熟悉我的讀者可能不理解我沒事幹麻去撿一個死兩年專案的屍體,這些是我曾經寫過相關的文章:

作為一個開源 Minecraft 愛好者(?),我一直都有在關注開源軟體圈中與 Minecraft 的發展,特別是物色一個適合的目標 fork 它改成我想要的樣子。

Biomes 乍看之下完成度很高:

而且是使用貼近 Web 的技術(Typescript, WASM...)實做,對我而言理解的門檻會低得多(比起 C++ 實做的 Minetest)。

專案的第一印象

該專案使用異構單體庫結構,也就是:

  • 單庫(Monorepo):使用一個 Git 庫存放所有模組與程式碼。
  • 異構(Polyglot):多個模組涉及不同的程式語言與技術棧(Techstack)。
  • 單體(Monolithic):所有程式碼會組裝成單一的 Web 服務。
info

「異構單體庫」並不是一個正式用語,但是我試著用中文組裝成一個最簡要能表達多個複雜的概念的詞彙。

並且透過 Bazel 這個工具來處理複雜的仰賴鏈,接著在外面套一層 Python 腳本,所有操作都需要透過 b.py 完成。

雖然試著用 Bazel 把專案跑起來,但是似乎是工具版本不符的原因,整個過程並不順利。加上個人不是很喜歡這種「全家桶」結構,因此下一步就是開始拆專案。

拆拆拆

一些比較大型模組會有自己獨立的 README.md

find . -name README.md
./README.md
./voxeloo/README.md
./docs/README.md
./src/client/README.md
./src/shared/README.md
./src/server/README.md
./src/server/bob/README.md
./src/server/shared/README.md
./src/benchmarks/README.md
./src/galois/README.md
./src/galois/js/README.md
./.githooks/README.md
./scripts/tsconfig-replace-paths/README.md
./.devcontainer/README.md

接著是找到外部仰賴最少,即仰賴鏈最上游的模組。

docs

第一個抽出的模組是網頁文件:

https://github.com/FlySkyPie/biomes-docs

它不仰賴別人的模組也沒有被仰賴,並且有自己獨立的 package.json,很容易抽出。

yarn 轉換成 pnpm、修復幽靈仰賴;並且處理的圖片都儲存在 git LFS 的問題之後就能正常運作了。

geometry.hpplight_kernel.hpp

接下來的兩個模組則是用 Python 生成 .hpp 函式庫:

其中一個還使用了 Jinja2 — 基於 Python 的樣板引擎/函式庫。

voxeloo

接著是名為 voxeloo 的組件,使用了前述程序化生成的 .hpp

在原本使用 Bazel 處理的仰賴鏈中,還包含了一些指向 GitHub 的外部函式庫,例如:catch2eigenrobin-hood-hashingzstd...。

在這方面我使用 CPM.cmake 來處理仰賴關係,只要目標有撰寫良好的 CMakeLists.txt,就可以把 C++ 專案當成套件拉進專案使用。

第三方函式庫

OpenSimplexNoise 原本的 Git Repo 沒有 CMakeLists.txt,所以我 fork 之後加上 CMakeLists.txt

opttritri.htribox3.h 這兩個檔案原本是直接放在專案內的,我就稍微朔源了一下然後一樣加上 CMakeLists.txt

voxeloo Python 綁定

Biomes 對 voxeloo 實做了兩種綁定/交叉編譯,將其從 C++ 專案轉換成其他語言的函式庫,其中一個是 Python,並且透過 pybind11 實現。

我使用 scikit-build 作為 Python 的建置後端 (Build Backend) 將奇打包成一個 Python 庫 (Python Wheel),並且補上型別資訊 (Stub)。

Footnotes

  1. OpenAI收購AI設計公司Global Illumination | iThome. Retrieved 2025-10-06 from https://www.ithome.com.tw/news/158307