技術說明
本站如何構建、儲存了什麼,以及哪些數字是實測、哪些是本站推算的。
頁面渲染
Next.js 15 App Router、React 19 與嚴格模式的 TypeScript。樣式使用 Tailwind v4,調色盤以主題代幣宣告在單一樣式表中;沒有元件庫也沒有圖表庫。五種圖表全是手寫 SVG,因此能在伺服器端渲染並繼承主題變數——這也是淺色模式在執行期零成本的原因。
語系區段下的每一頁都是逐請求渲染,而非靜態生成。這是刻意的選擇:頁首會顯示各工作階段的狀態(誰登入了、他的書籤),所以語系版面設定 dynamic = "force-dynamic",整個子樹退出靜態生成。語系本身放在網址裡;middleware 先從 cookie、再從 Accept-Language 協商出語系,並把結果寫到請求標頭上,讓根版面能設定 <html lang>。
頁面永遠不直接呼叫官方 API,而是呼叫單一服務模組。它逐請求決定要回應即時 API、本地快取,或在未設定金鑰時回應固定種子的示範資料——並把這個決定連同資料一起回傳,讓 UI 能加以標示。
本地索引
持久化使用 node:sqlite,即 Node 22 起內建的 SQLite 綁定。沒有原生模組要編譯、沒有資料庫伺服器要運行、也沒有 ORM——整個資料層就是一支 SQL 檔加上幾個小型的型別化輔助函式。檔案位置由 DATABASE_PATH 決定,預設 ./data/brawlinsights.db,並以 WAL 日誌、外鍵啟用與五秒 busy timeout 開啟。
結構描述在每次啟動時以 CREATE TABLE IF NOT EXISTS 建立。由於這不會替既有資料表補欄位,新欄位由另一個步驟處理:檢查每張表並補上缺少的 ALTER TABLE,既有安裝因此能直接取得新欄位,而不必砍掉重來。
它保存官方 API 不提供的一切:
- 讓名稱搜尋成為可能的玩家與俱樂部目錄;
- 名稱紀錄與俱樂部紀錄,由輪詢之間的變化比對重建;
- 每小時的玩家進度快照,趨勢圖表的資料來源;
- 封存的對戰紀錄,以及由其彙總的每日統計;
- 裝備中的造型,與從觀察到的檔案學來的排位段位名稱表;
- 帳號、工作階段、書籤、瀏覽紀錄、佈告欄貼文與公告。
與官方 API 溝通
單一模組包裝 api.brawlstars.com/v1。每個回應同時放入 Next 的資料快取,帶有各端點的重新驗證視窗與快取標籤;玩家與俱樂部檔案還會在短暫 TTL 內直接重用 SQLite 副本,之後才發出新請求。除 404 以外的失敗一律改回最後儲存的副本,而不是讓頁面出錯;404 代表帳號已不存在,頁面回報找不到,更新佇列則把該標籤自索引移除。
| 請求 | 重新驗證間隔 |
|---|---|
| 玩家檔案 | 120 s |
| 對戰紀錄 | 60 s |
| 俱樂部、俱樂部成員 | 300 s |
| 排行榜 | 900 s |
| 活動輪替 | 600 s |
| 英雄名冊 | 86400 s |
API 金鑰綁定發出請求的對外 IP,因此 403 accessDenied.invalidIp 是生產環境最常見的失敗。它會被特別偵測,並在健康端點以白話提示呈現,而不是記成一則普通錯誤。
更新佇列
API 沒有推播管道,也沒有「自某時間起變更」的過濾器,保持新鮮的唯一辦法就是輪詢——而平等地輪詢一切既慢又浪費。因此每個已收錄的玩家都落在一個優先層級中,紀錄要老過該層級允許的年齡才成為更新候選。候選再依層級高者先、紀錄舊者先處理。
在這條個人檔案流程中,只有 hot 與 warm 層級會連同檔案一併抓取對戰紀錄——冷帳號自上次查看以來多半沒再打過,而每份紀錄都是額外一個請求。俱樂部以 warm 門檻另有一輪掃描、書籤俱樂部優先;更新玩家時也會順帶更新其俱樂部,讓俱樂部頁面不需要第三條佇列就能保持新鮮。
不過對戰紀錄並不受限於這些層級。另一條獨立的採集流程——通常佔每輪最大的份額——按各帳號紀錄距上次收集的時間走訪整個索引(冷帳號也在內),凡在 HARVEST_INTERVAL(30 分鐘)內未被採集的玩家都會抓取一次。沒有人查看的帳號仍然在打對戰,而那些對戰正是強度榜樣本成長的來源——建立語料庫的是這條流程,不是個人檔案佇列。
每個對外請求都要先從代幣桶取得代幣,桶以每秒 BS_API_RPS(預設 8)個代幣補充,容量上限 BS_API_BURST(預設 16)。桶是單一共享物件,無論多少循環或手動更新重疊,整體速率都守得住;取不到代幣的呼叫端會精確等待所需時間,而不是空轉。
循環有兩種驅動方式。其一是行程內定時器:每 SYNC_INTERVAL_MS(預設 30 秒)一輪,每輪最多花 SYNC_BUDGET 個請求(預設 100),正常情況下約一半給對戰紀錄採集、約三分之一給玩家檔案、其餘給俱樂部;當還有超過一千個新發現的帳號等待第一次檔案抓取時,分配會反轉為以檔案優先。其二是在無法維持背景定時器的平台上,由外部排程器呼叫 /api/cron。重疊的循環會立即返回而不是倍增請求速率;開機後第一輪隨機延遲數秒,避免重啟風暴同時打向 API;遇到 429 或 IP 不符的 403 會提前中斷循環,而不是猛敲一扇已經關上的門。每六小時另有一輪修剪:刪除 90 天前的封存對戰、120 天前的彙總與一年前的快照。
玩家或俱樂部頁上的「立即更新」按鈕透過獨立端點繞過所有 TTL,但每個標籤有 20 秒冷卻,按住按鈕也無法變成放大器。/api/health 回報即時或示範模式、索引大小、封存量、佇列深度與最後一次錯誤。
對戰封存庫
API 永遠只回傳玩家最近 25 場對戰,也沒有歷史端點。本站所有回看超過 25 場的功能,都建立在那些視窗被封存的基礎上:每次檔案瀏覽與每次佇列更新都會存入新內容,並以鍵值確保重複看到同一場對戰是無操作。
每場對戰封存時,也會滾入以日、模式、地圖、區間與英雄為鍵的每日彙總表,計算出場、勝場、平手與明星球員次數。每場對戰更新四列——實際地圖與合成的 all 地圖,各寫兩次:一次記在粗區間(ranked 或 trophies),一次記在精確區帶(如 ranked:masters、trophies:500-750)。all 地圖列讓全模式強度榜成為單次索引讀取而非掃描;區帶列則讓強度榜上的段位與獎盃範圍篩選是真實的,而不是裝飾。友誼賽與無法辨識玩家英雄的對戰會被跳過。
生存戰沒有勝負欄位,只有名次,因此前半名次計為勝場:單人 10 取 5、雙人 5 取 3、其他 3 取 2。這條規則在勝率、強度榜與各英雄統計中一體適用。
由於封存庫來自本站見過的帳號,它是樣本而非全體對戰母體——而且是偏向被查詢帳號的樣本。這對絕對數量的影響遠大於對相對形狀的影響。
強度榜的計分方式
按原始勝率排名,會把上週手感火熱、只打了 40 場的冷門英雄排上頂端;按出場率排名,只是替受歡迎程度換個標籤。分數混合兩者,且勝率按我們實際相信的程度打折。
rows = { b : picks(b) ≥ 200 }
globalWinRate = Σ wins / Σ picks
shrunk = (wins + globalWinRate * 1500) / (picks + 1500)
pickRate = picks / total picks
popularity = log10(1 + pickRate * rows * 3) / log10(4)
score = (shrunk - globalWinRate) * 100 + popularity * 1.81500 是以虛擬對戰數表示的先驗:200 場的英雄會被大幅拉回全體平均,30,000 場的則幾乎保有自己的勝率。這就是經驗貝氏收縮的想法,用得粗糙但透明。出場率項取對數,讓出場十倍的英雄只值一點加分,而不是十倍分數。
接著從分數分佈、而非固定門檻切出階級:先在入榜英雄上計算平均與標準差,再按每位英雄高出或低於平均幾個標準差歸位——S 在 +1.25、A 在 +0.5、B 在 −0.25、C 在 −1、更低則為 D。值得知道的推論:階級是相對於當前版本環境的,就算版本再平衡,榜上也永遠有 S 階。
只有所選視窗至少累積 5,000 次出場(預設視窗七天)時,強度榜才會以真實資料建立。低於門檻時頁面退回固定種子產生器並自我標示為示範資料,而不是拿幾百場擺出一副自信的排名。
直接來自 API
以下欄位直接讀自官方回應、原樣顯示。API 公開的比多數社群包裝器所建模的更多,因此其他網站用估的幾個數字,在這裡是實測值。
- 獎盃、最高獎盃、經驗等級、3v3/單人/雙人勝場
- 排位段位索引與顯示名稱、排位分數、賽季 id
- 賽季與歷來最高的排位段位與分數
- 總威望等級、聲望與聲望階級
- 各英雄:能力等級、段位、獎盃、威望、當前與最長連勝
- 各英雄:小工具、星徽能力、裝備、超級充能、裝備中造型
- 俱樂部成員資格、成員名單與職位、入團獎盃門檻
- 各國排行榜與即時活動輪替
- 每位玩家最近 25 場對戰,含模式、地圖、類型、結果與獎盃變化
- 英雄名冊本身,由同步腳本抓入生成檔——最後同步於 2026/09/15
本站推算
以下數字不以任何形式存在於 API 中。它們由本站觀察到的資料計算而來,且每一項在出現之處都有標示。
- 名稱紀錄與俱樂部紀錄——由前後輪詢的差異比對建立
- 獎盃、排位分數、威望與聲望趨勢——來自每小時快照表
- 強度榜、勝率、出場率與明星球員率——來自對戰封存庫
- 造型使用率排行——統計自已收錄檔案的裝備中造型欄位
- 排位段位名稱表——從觀察到的檔案學習,並非寫死
- 估計遊玩時間——由勝場數與經驗等級推得,誤差約 ±15%,因為 API 不提供計時
- 排位分數時間線——排位對戰不含分數變化,勝敗以約 ±30 分模擬,重建一季的形狀
- 各英雄的稀有度與定位——名冊中唯二 API 未提供的欄位,以名稱查內建對照表,查不到就標
unknown而不猜 - 計算機表格(升級成本、獎盃增減、星光寶箱機率、獎盃之路)——自遊戲內轉錄,快照 2026-09-16
仍屬模擬的部分
少數頁面尚未由真實資料支撐,且會在頁面上明說,而不是默默端出猜測:
- 排位段位分佈。 頁面已接上真實查詢,統計每個完整收錄檔案上記錄的排位段位;只有在帶段位的已收錄檔案少於 200 份時,才退回模擬的天梯形狀——樣本太小又偏斜,硬畫形狀反而誤導。退回時會明說,並標出目前的數量。
- 樣本太薄的強度榜。 封存出場少於 5,000 次的模式、地圖或區間會退回模擬分佈,而不是用寥寥數場排名。頁面會標出已有數量與所需數量。
- 徽章、對戰卡片與檔案稱號。 這些以任何形式都不存在於 API 中,因此對應的排行頁是版面示範,並帶有明確告示。
- 示範模式。 未設定 API 金鑰時,整站以你搜尋的標籤為種子的固定產生器運行,同一標籤永遠產生相同數字。受影響的每一頁都有警示橫幅。
索引對搜尋結果的意義,請見搜尋規格。