探索使用 Golang 和 Webassembly 構建一個多人遊戲伺服器

為少 發表於 2021-05-04

探索使用 Golang 和 Webassembly 構建一個多人遊戲伺服器

什麼是 WebAssembly?由 GoogleMicrosoftMozillaApple 等發起的 WebAssembly 是一種新的位元組碼格式,主流瀏覽器都已經支援 WebAssembly。 和 JS 需要解釋執行不同,WebAssembly 位元組碼和底層機器碼很相似可快速裝載執行,因此效能相對於 JS 解釋執行大大提升。WebAssembly 並不是一門程式語言,而是一份位元組碼標準,需要用高階程式語言編譯出位元組碼放到 WebAssembly 虛擬機器中才能執行。

Panzr.io 簡介

  • 基於開源技術的遊戲
  • 使用 Web 作為發行平臺
  • 輕巧快速
  • 探索基本的多人遊戲技術
  • 擴充套件 Go 技術知識

Panzr.io 架構

探索使用 Golang 和 Webassembly 構建一個多人遊戲伺服器

Panzr.io 部署架構

探索使用 Golang 和 Webassembly 構建一個多人遊戲伺服器

Triebwerk 簡介

專案原始碼:

專案 Status:

  • Triebwerk 是一個開源的多人遊戲伺服器
  • 使用 Go 語言編寫
  • 目前僅是基礎原型

遊戲是如何執行的?

探索使用 Golang 和 Webassembly 構建一個多人遊戲伺服器

伺服器權威架構

  • 僅通過伺服器進行通訊
  • 客戶端將所有輸入傳送到伺服器
  • 伺服器有權進行模擬
  • 防止作弊並引入延遲

探索使用 Golang 和 Webassembly 構建一個多人遊戲伺服器

客戶端預測和伺服器協調

  • 最早由 QuakeWorld 推廣
  • 本地模擬運動
  • 不斷與伺服器狀態同步
  • 根據伺服器狀態更正本地狀態

探索使用 Golang 和 Webassembly 構建一個多人遊戲伺服器

客戶端插值

  • 網路更新(Updates) < 每秒幀數(Frames)
  • 過去狀態之間的插值
  • 保守演算法
  • 沒有推斷

探索使用 Golang 和 Webassembly 構建一個多人遊戲伺服器

定義邊界

限制:

  • 所有遊戲邏輯僅在 2D 空間中
  • 均勻表面
  • 僅通過鍵盤進行輸入控制
  • 限制地圖尺寸
  • 緩慢移動的車輛
  • 沒有物理引擎

探索使用 Golang 和 Webassembly 構建一個多人遊戲伺服器

伺服器實現

探索使用 Golang 和 Webassembly 構建一個多人遊戲伺服器

玩家移動

探索使用 Golang 和 Webassembly 構建一個多人遊戲伺服器

碰撞檢測

探索使用 Golang 和 Webassembly 構建一個多人遊戲伺服器

二進位制資料傳輸

  • 最小化資源使用
  • 防止資料包分段
  • 最小化丟包的影響

探索使用 Golang 和 Webassembly 構建一個多人遊戲伺服器

WebAssembly 模組

遊戲邏輯(Game logic)Server -> Client

  • 檔案大小 > 2MB
  • 伺服器和客戶端根據相同的邏輯計算狀態
  • 通過二進位制型別進行資料傳輸

編譯:

GOOS=js GOARCH=wasm go build -o tanks.wasm cmd/wasm/tanks.go

Client:

<script src="/game/wass_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(featch("/game/tanks.wass"), go.importObject).then(result => {
    go.run(result.instance);
});
</script>

Server:

js.Global().Set("updateNetworkPlayer", js.FuncOf(updateNetworkPlayer))

在 Go 中編碼 state

posX := float32(30.457777)
posY := float32(10.336666)
buf := make([]byte, 8)

binary.LittleEndian.PutUint32(buf[0:], math.Float32bits(posX))
binary.LittleEndian.PutUint32(buf[4:], math.Float32bits(posY))

var uint8Array = js.Global().Get("Uint8Array")
dst := uint8Array.New(len(buf))
js.CopyBytesToJS(dst, buf)

在 Javascript 中解碼 state

let dv = new DataView(state.buffer)
let posX = dv.getFloat32(0, true)
let posY = dv.getFloat32(4, true)

線上試玩

http://panzr.io

Refs

我是為少
微信:uuhells123
公眾號:黑客下午茶
加我微信(互相學習交流),關注公眾號(獲取更多學習資料~)

相關文章