夜花櫻TINYYANA
今晚的顏色櫻粉

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

今晚的音樂

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

← 回筆記
技術實作

Lycohinya 是怎麼搭起來的(一):整體架構

這個系列寫 Lycohinya 整台伺服器是怎麼搭的。這篇先講全貌:有哪些機器、哪些插件、資料放哪、資源包怎麼給玩家。插件怎麼拆、副本怎麼做、商團經濟為什麼放到另一台機器,之後各寫一篇。

---
config:
  layout: elk
---
flowchart TB
  client["Minecraft 客戶端"]
  subgraph entry["入口"]
    direction LR
    proxy["Velocity 入口代理"]
    lobby["大廳(等待區)"]
  end
  subgraph server["生存伺服器(Lecithin 26.2):世界和所有插件都在這台"]
    direction LR
    plugins["自製插件"]
    third["第三方插件"]
    localdb[("本機資料庫")]
  end
  subgraph outside["遊戲外的服務"]
    direction LR
    brain["商團運算服務"]
    braindb[("商團的資料庫")]
    web["商團資訊網頁"]
    discord["Discord"]
  end
  client --> proxy
  proxy -. 連不上時 .-> lobby
  proxy --> plugins
  plugins --> third
  plugins -.- localdb
  plugins -- HTTPS --> brain
  brain -.- braindb
  plugins -. 唯讀資料 .-> web
  third -- DiscordSRV --> discord

箭頭從主動的一方指出去。實線是連線或呼叫,虛線是資料讀寫或備援路線。資源包怎麼下載,放在後面玩家連線那張圖。

入口和大廳

玩家不會直接連到生存伺服器,而是先連到 Velocity 入口代理,再由代理轉進去。現在後面只接一台生存伺服器,看起來多繞一層,但這是為了之後玩家變多時,可以在代理後面接更多伺服器,到時候玩家也不用換連線地址。

另一個用途是等待區。生存伺服器重開或連不上時,代理會把玩家送去大廳,不會直接斷線。大廳是一台很小的 Paper,什麼都不能做,純粹讓玩家等。入口代理和生存伺服器之間用我自己的 LycoNetwork 溝通,負責分流、生存伺服器不在時改送大廳,還有全網共用的封鎖名單。

生存伺服器

世界和所有插件都在這台。核心是 Lecithin 26.2,上游是 Lophine,再往上是 PaperMC 的 Folia。Folia 會把世界切成很多塊、分開跑;Lophine 在上面補了生電相關的內容;Lecithin 則是為了能跑原本 Paper 的插件而做的核心。我是 LophineLabs 的成員,也維護 Lecithin。

對架服的人來說,選 Folia 系核心最大的影響是挑插件。插件要在 plugin.yml 宣告支援 Folia 才會被載入,不少常見插件沒有宣告,有宣告的也最好先在測試服跑過。完整的插件清單寫在這篇。

即使這樣,我還是推薦用 Folia。它解決的是玩家之間的公平:世界切成很多塊各自跑,A 蓋了一台超大、超卡的機器,卡的是 A 那一塊,旁邊的 B 不會跟著卡。

插件分三層

---
config:
  layout: elk
---
flowchart TB
  play["Lyco 系列玩法插件<br/>核心插件、使魔、旅行、任務、物品、抽卡…"]
  alone["可以單獨安裝的插件<br/>HanaToki、Kyokalith、商團插件"]
  lib["LycoLib<br/>訊息、選單、資料庫連線、插件之間的介面"]
  third["第三方插件<br/>Vault、LuckPerms、GriefPrevention、Essentials、DiscordSRV 等"]
  srv["Lecithin 26.2"]
  play -- 必要依賴 --> lib
  alone -. 商團插件有裝就用 .-> lib
  lib -- 經濟走 Vault --> third
  alone --> third
  lib --> srv
  third --> srv

經濟、權限、領地、基本指令這些東西,我直接用 Vault、LuckPerms、GriefPrevention、Essentials 這類第三方插件,不自己重做。自製插件要扣錢,是透過 LycoLib 接到 Vault。

中間是我自己的共用函式庫 LycoLib:訊息格式、選單排版、資料庫連線,還有插件之間互相呼叫用的介面。Lyco 開頭的玩法插件都把它列成必要依賴,沒有它就不會啟動。

最上面是一個玩法一顆插件:核心插件管簽到、主選單、檢定這些共用的東西,使魔、旅行、任務、物品、抽卡各自一顆。HanaToki、Kyokalith 和商團插件比較特別,它們是設計成別的伺服器也能裝的,所以不硬綁 LycoLib,商團插件是有裝就用。

資料放哪

玩法插件的資料存在生存伺服器本機的資料庫裡,依插件不同用 SQLite 或 MariaDB。我的原則是一張表只讓一顆插件寫,這部分在拆插件那篇會細講。

商團經濟是例外。它的模擬放在另一台機器上的服務跑,資料也存在那台的資料庫,生存伺服器只用 HTTPS 去問它。商團資訊網頁也只收插件推過去的唯讀資料,不會反過來動遊戲。

玩家連進來時發生什麼

sequenceDiagram
  autonumber
  participant C as 玩家客戶端
  participant P as 入口代理
  participant S as 生存伺服器
  participant L as 大廳
  participant R as Cloudflare R2
  C->>P: 連線
  P->>S: 轉進生存伺服器
  P-->>L: 生存伺服器連不上時,改送大廳等待
  S->>C: 告知資源包網址和 SHA-1
  C->>R: 下載資源包
  C->>S: 回報資源包有沒有載入
  Note over C,S: 拒絕或下載失敗也照樣進服
  S->>C: 進入世界

虛線是生存伺服器連不上時才會走的路。

資源包不經過遊戲伺服器。我把資源包上傳到 Cloudflare R2,伺服器設定裡只放網址、SHA-1 和資源包 id,玩家的客戶端自己去 R2 下載。換新版時 SHA-1 和 id 要一起換,只換一個的話,客戶端可能因為快取繼續用舊的。

資源包不是強制的。拒絕或下載失敗的玩家照樣進得來,自訂物品會顯示成原版物品,選單也會改用文字版,操作完全一樣。我不想讓「沒裝資源包」變成進不了服的理由。

更新

插件更新大多直接熱換,不停服。只有換伺服器核心、動到資料庫結構,或碰到已知不能熱換的插件時,我才停服更新。能這樣熱換,靠的是插件拆得夠小,這是下一篇要講的。

這套適合誰

這套架構是照「一個世界、主要只有我在維護、內容一直加」長出來的,沒有處理規模問題:世界只有一台生存伺服器在跑,入口代理只是先把擴充的位置留好。核心我推薦 Folia 系;但如果你的自訂內容不多,用現成插件就夠了,不需要把功能拆成這麼多顆插件。