最近我最喜歡的遊戲之一《Timberborn》發行了 1.0 版本,終於結束了搶先體驗階段。

對於不了解的人來說,《Timberborn》是一款城市建造策略遊戲。在遊戲中,玩家扮演一隻海狸的統治者——在人類(或遊戲中稱作「人族」)從地球上消失後,海狸接管了地球。遊戲內容包括資源收集、滿足海狸的各種需求、建造複雜的水力發電網路、工廠、交通運輸等等。

或者,就我而言,是一個資料庫。

遊戲中包含 3D 水體物理效果、不同的季節(例如乾旱,需要蓄水)以及惡劣的潮汐(新進入地圖的水受到污染,對海狸有害)。

自 2021 年首次發布以來,已經加入了許多功能。

最新更新讓我的遊戲體驗有點問題,因為他們加入了自動化功能。這些自動化功能包括各種感測器,用於檢測惡劣潮汐、水流等等。他們甚至還加入了邏輯閘和HTTP控制桿。

哦不

哦,不。我好好研究一下這些。這是它們在遊戲中的樣子:

Timberborn 的 HTTP 槓桿

如您所見,它們有一個名稱(在本例中為「HTTP Lever 1」)、一個「開啟 URL」和一個「關閉 URL」。

這些功能的概念是讓主播可以將它們與其他服務連接起來,例如 Twitch Webhook。如果有人按讚,就可以播放煙火之類的效果。

但是:誰說你只能使用其中一個呢?

Timberborn 大約有 1000 個 HTTP 權限

你明白我的意思嗎?

是的。遊戲可以同時執行大約 1000 個這樣的設備一段時間。我在測試的時候,Windows 兩次提示“系統出現問題”,所以從一開始,這都不太實用。不過,也並非一定要如此。

每個控制桿都有一個開啟端和一個關閉端。可惜不支援批量處理,但或許有相關的mod可以實現。另一個端點傳回目前地圖上每個控制桿的狀態。資料大致如下:

[
  {
    "name": "HTTP Lever 829",
    "state": false,
    "springReturn": false
  },
  {
    "name": "HTTP Lever 154",
    "state": true,
    "springReturn": false
  },
  {
    "name": "HTTP Lever 839",
    "state": false,
    "springReturn": false
  },
  {
    "name": "HTTP Lever 164",
    "state": true,
    "springReturn": false
  }
]

具有兩個狀態的 HTTP 槓桿可以被視為一個位元。

我們只需要一種方法來讀寫它們。

Timberborn 網頁介面:工作原理

我們的想法是,將使用者輸入轉換為 JSON 格式,然後使用 ASCII 編碼將其轉換為二進位格式,並逐位翻轉。讀取時,我們一次讀取所有控制桿狀態,將它們重新排列成比特序列,將其分割成 8 位元資料塊,解碼回字符,並解析生成的 JSON 資料。瞧!一個可讀寫的資料儲存就完成了。

我們先來學一點HTML:

<form method="POST" id="form">
  <div>
    <input type="text" id="title">
  </div>
  <div>
    <textarea id="text"></textarea>
  </div>
  <button type="submit">Store in Timberborn</button>
</form>

<button type="button" id="load">
  Load data from Timberborn
</button>

現在到了有趣的部分。我們先從一些 JavaScript 框架開始:

const title = document.querySelector('#title')
const text = document.querySelector('#text')
const form = document.querySelector('#form')
const load = document.querySelector('#load')
const chunkSize = 8 // We need this to later split the bits
const numberOfLevers = 1000 // This needs to be the exact number of levers in-game, otherwise it won't work reliably.

接下來,我們監聽表單提交事件,並根據這兩個欄位建立一個 JSON 字串:

form.addEventListener('submit', async (event) => {
  event.preventDefault();
  const data = {
    title: title.value,
    text: text.value,
  }

  const json = JSON.stringify(data)

  // ...
})

有了這個之後,我們就可以把它轉換成一系列二進位字串:

form.addEventListener('submit', async (event) => {
  // ...

  const json = JSON.stringify(data)

  const asciiEncoded = json.split('')
    .map(c => c.charCodeAt(0))

  const binary = asciiEncoded.map(
    num => num.toString(2).padStart(chunkSize, '0')
  )

  // ...
})

這樣我們就得到了一個由 0 和 1 組成的 8 位元長字串陣列:

瀏覽器視窗顯示 HTML 表單和二進位資料的 console.log 輸出。

接下來,我們需要將這些位元連接起來,建立最終的大型位元串。我們將這些位元轉換為布林值,然後轉換為 API URL,之後我們可以使用fetch呼叫這些 URL:

form.addEventListener('submit', async (event) => {
  // ...

  const bits = binary.join('')
    // So there's no leftover data in the registry 
    // at the end of the current data
    .padEnd(numberOfLevers, '0')
    .split('')
    .map(b => b === '1')

  const allUrls = bits.map((bit, key) => 
    `http://localhost:8080/api/switch-${bit ? 'on' : 'off'}/HTTP Lever ${key + 1}`
  )

  await Promise.all(allUrls.map(url => fetch(url)))

  console.log('done!')
})

儲存時,這將向遊戲伺服器端發出總共 1000 個 HTTP 請求。難怪遊戲不喜歡這樣。

但是:它真的有效!

一個 GIF 動畫,展示了 Timberborn HTTP 控制桿在保存文字時如何更新。

(這裡有個GIF動圖,可能要幾秒鐘才能載入…)

從Timberborn讀取資料

接下來,我們需要讀取資料。正如我們在範例中看到的,控制桿狀態可以一次全部獲取,但它們的順序完全打亂了。我們可以透過先載入所有資料,然後再進行排序來解決這個問題。之後,我們將所有資料轉換回位元串,將其分段,並再次解碼為 ASCII 碼:

load.addEventListener('click', async () => {
  const response = await fetch('http://localhost:8080/api/levers')
  const json = await response.json()

  const sorted = json.sort((a, b) => {
    const aNumber = Number(a.name.replace('HTTP Lever ', ''))
    const bNumber = Number(b.name.replace('HTTP Lever ', ''))

    return aNumber - bNumber
  })

  const bitString = sorted.map(l => l.state ? '1' : '0')

  const chunks = []

  for (let i = 0; i < bitString.length; i += chunkSize) {
    chunks.push(bitString.slice(i, i + chunkSize).join(''))
  }

  const numbers = chunks.map(c => Number.parseInt(c, 2))
    // We filter out everything that's only 0s, because that's likely garbage-data
    .filter(n => n > 0)
  const letters = numbers.map(n => String.fromCharCode(n))
  const data = JSON.parse(letters.join(''))

  title.value = data.title
  text.value = data.text
})

就這樣!

從技術上講,這現在可以被視為一種雲端儲存。由於 Steam 會將遊戲存檔上傳到雲端,而 Timberborn 將 HTTP 請求視為有狀態的(即在保存遊戲時保存其狀態),因此所有資料都是持久化的。

如果有人能成功地將超過 1 千比特的資料導入 Timberborn,請聯絡我,我非常肯定我們可以想辦法為此建立一個 Drupal 資料庫適配器。

(附註:抱歉,但我並不後悔使用了AI生成的圖像。我最終還是得嘗試一下!)

(附註:本文並非廣告。不過,Timberborn 的開發者們,如果你們看到了這條訊息,我非常期待你們使用自動化功能進行更多有趣的嘗試 :D)


希望您喜歡這篇文章,就像我喜歡寫這篇文章一樣!如果喜歡,請按讚❤️ !我會在空閒時間寫一些科技文章,偶爾也喜歡喝杯咖啡。

如果你想支持我的創作,可以請我喝杯咖啡 !你也可以直接透過PayPal支持我!或在Bluesky上關注我🦋

給我買杯咖啡按鈕


原文出處:https://dev.to/thormeier/how-to-use-timberborn-yes-the-beaver-city-building-game-as-a-database-489c


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝9   💬10   ❤️3
534
🥈
我愛JS
📝2   💬6   ❤️2
141
🥉
💬1  
4
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登