夜花櫻TINYYANA
今晚的顏色櫻粉

夜、花、櫻。名字裡的三個字就是上面那片天空,拉一下會跟著換。

今晚的音樂

月夜蝶~君ヲ想フ~,來自H/MIX GALLERY。 音量在這裡調,右上角能暫停。

← 回筆記
技術實作

Lycohinya 是怎麼搭起來的(四):把商團經濟搬出伺服器

商團(薇莉雅)是一套會自己運轉的經濟:生產、消耗、價格、掛單、NPC 商人、倉庫、物流、合約、聲望。這些跟 Minecraft 的世界沒有關係,卻要大量運算,帳也必須對得起來。2.0 我把整套模擬搬到另一台機器上的獨立服務,用 Rust 寫,帶著自己的 MariaDB。

哪些留在遊戲裡

生存伺服器上的商團插件只留跟世界有關的部分:NPC 實體、玩家背包和物品本身、選單、領地,以及透過 Vault 扣錢加錢。價格怎麼算、倉庫剩多少、帳本怎麼記,全部由遠端服務決定。

這樣切,模擬再重也是另一台機器在扛,不會跟世界搶同一台伺服器的資源。

兩邊怎麼溝通

一律由遊戲這邊發起,用 HTTPS。商團插件會定期送心跳、定期去拉通知,玩家操作時再送對應的請求。遠端服務不會反過來呼叫遊戲。

遠端靠心跳判斷遊戲還在不在。一段時間沒收到心跳,它就凍結模擬、拒絕所有請求,等心跳回來再繼續。這樣遊戲伺服器關機或斷線時,商團不會在沒人看著的情況下自己跑下去。

玩家買一筆東西時

sequenceDiagram
  autonumber
  actor P as 玩家
  participant G as 商團插件(生存伺服器)
  participant V as Vault 經濟
  participant B as 商團運算服務
  P->>G: 在倉庫選單按購買
  G->>B: 查報價(不改任何東西)
  G->>P: 顯示確認畫面
  P->>G: 確認
  Note over G: 先把這筆請求寫進本機日誌
  G->>B: 送出交易(附請求編號)
  Note over B: 先演練一遍再回滾,記下待扣款
  G->>V: 扣款,只扣一次
  Note over G: 收據寫進本機日誌
  G->>B: 交回扣款收據
  Note over B: 庫存、帳本、結果一起寫進資料庫
  G->>P: 回覆結果、重開選單
  G-->>P: 扣款後 Vault 出錯時,改顯示「處理中」
  Note over G,B: 不知道扣了沒:不自動重試、不自動退款,等人工對帳

虛線是扣款出錯時才會走的路。

送出去的請求都先寫進生存伺服器本機的日誌,而且每筆都有編號。回應要是在網路上掉了,插件可以拿同一個編號重送,遠端認得這個編號,會直接回之前存好的結果,不會成交兩次。

扣錢只發生在一個地方:商團插件在遊戲裡呼叫 Vault,扣一次。遠端第一次收到交易時只是演練、記下一筆待扣款,等收到扣款收據,才把庫存、帳本和結果一起寫進去。

最麻煩的是 Vault 被呼叫之後出錯,插件不知道錢到底扣了沒。這種情況我選擇停下來:這筆標成「未知」,不自動重試,也不自動退款,玩家看到「處理中」,等人工對帳。對完之前,遠端會擋下後續的寫入。猜錯的代價是多扣或少扣玩家的錢,停一下比較安全。

同一個玩家同時只能有一筆交易在跑,第二筆會直接被擋掉。連線請求也交給背景去跑,排隊滿了就拒絕,不會讓遊戲卡住等網路。

連不上的時候

商團插件連不上遠端時,不會偷偷切回本機的舊邏輯自己算。兩邊同時寫帳本,比暫停一下麻煩得多。所以遠端或它的資料庫出狀況時,商團功能會暫停,但世界的其他部分照常運作。

這就是搬出去的代價:商團綁在一條跨網路的連線上,多養一台機器,也多一個會斷的地方。

什麼時候值得這樣做

只有在那塊邏輯跟世界無關、又重到會影響遊戲時才值得。一般的商店、拍賣場用現成插件就好,沒必要另外開服務。商團是因為要模擬整個市場,帳又必須正確,我才把它搬出去。