跳轉到

MatchRPG new_player_defaults fallback 未對齊常數表:staminaMax hardcode 問題排查、修復與無上限常數處理策略

文檔資訊

  • 分類: debugging
  • 難度: intermediate
  • 預估閱讀時間: 7 分鐘
  • 標籤: MatchRPG, new_player_defaults, FixedValueConfig, stamina, hardcode, fallback, DB migration, constants table, cap strategy, ExciteTarget, TODO governance

摘要

前端讀到 staminaMax=30 但常數表設定為 100。根因是 new_player_defaults fallback 的 staminaMax hardcode 而非讀取常數表。修復後延伸出重要架構模式:常數表填 99999999999 表示「遊戲設計上無上限」,但伺服器端必須 cap 在設計文件規定的安全上限(如 2000)以保護 DB。另發現 ExciteTarget 使用了不存在的常數 key 作為註解,已改為 TODO 待企劃確認。

關鍵學習

  • new_player_defaults 是新玩家初始化的 fallback,其數值必須與 FixedValueConfig.xlsx 保持一致,否則會產生靜默錯誤(數值錯但不報錯)

  • 「直接下 SQL 改」優於為一次性資料修正建立 migration 檔案,避免過度工程化

  • 常數表填 99999999999 是企劃表達「遊戲設計上無上限」的慣例,伺服器不能照單全收,必須 cap 在設計文件規定的 DB 保護上限

  • Cap 策略:讀取常數表值,但 cap 在伺服器安全上限,不取最小值(因為常數表值是設計意圖,不是真實數字)

  • hardcode 值如果對應的常數 key 尚未由企劃定義,應改為 TODO 而非隨意填寫不存在的 key

  • 系統性掃描 hardcode 值後,需區分:(a) 真正的錯誤 fallback、(b) 故意的 DB 保護上限、© 待企劃確認的 TODO

技術細節

問題現象

API player.v1.PlayerService/GetPlayerData 回傳:

{
  "stamina": 75,
  "staminaMax": 30
}

FixedValueConfig.xlsx 常數表設定的體力上限應為 100,不是 30。

根因分析

new_player_defaults 設定檔(用於新玩家初始化的 fallback)中,staminaMax 被 hardcode 為 30,沒有讀取常數表的值。

理論上 new_player_defaults 是**純 fallback**,所有數值都應該參照常數表的設定。任何常數表有定義的欄位,fallback 都不應該自行 hardcode 不同的值。

修復確認

修復後,線上玩家 API 回傳:

{
  "stamina": 763,
  "staminaMax": 100
}
確認 staminaMax 已正確為 100。

無上限常數的 Cap 處理策略

在系統性掃描 hardcode 時,發現部分欄位常數表填 99999999999,企劃用這個表示「遊戲設計上無上限」。

問題:伺服器不能照單全收 99999999999,否則可能炸 DB。

解法(依設計文件): - Cap 值(如 2000)是**設計文件明確規定的 DB 保護上限**,不是隨意的 magic number - 邏輯:讀取常數表,確認其為「無上限」設計意圖,但實際存取/回傳時 cap 在 2000 - 不採用 min(fallback_cap, constants_value) 的做法,因為 99999999999 不是真實數字而是設計語義

// 正確做法:讀常數表 + 伺服器端 cap
const MaxInventoryCapacity = 2000  // DB 保護上限,見容量設計文件

func getInventoryCapacity(cfg *FixedValueConfig) int {
    rawCap := cfg.GetInventoryCap()  // 可能讀到 99999999999
    if rawCap > MaxInventoryCapacity {
        return MaxInventoryCapacity  // cap 保護
    }
    return rawCap
}

ExciteTarget 錯誤註解問題

teach_models.go:41 中:

ExciteTarget = 1000  // key: 99960000027

問題:99960000027 這個 key 在企劃設計中從未定義,是開發者自行猜測的 key。

正確處理:改為 TODO,等企劃給出正確的常數 key:

// TODO: ExciteTarget 待企劃定義對應的常數表 key,目前暫時 hardcode 為 1000
ExciteTarget = 1000

同類問題:MaxExciteValue 也需要確認是否有對應的常數表 key。

What Changed

staminaMax 問題修復new_player_defaults 中的 staminaMax 從 hardcode 30 修正為對齊常數表的 100,並直接對線上 DB 執行 SQL 更新既有非 bot 玩家記錄。修復後 API 回傳確認 staminaMax=100。

無上限常數處理策略確立:發現常數表中 99999999999 是企劃表達「遊戲設計上無上限」的慣例,但伺服器端已有設計文件規定的 DB 保護上限(如 2000)。實作為讀取常數表後 cap 在保護上限,而非直接使用常數表的大數值。

ExciteTarget 錯誤 key 修正teach_models.go:41ExciteTarget=1000 有一個不存在的 key 99960000027 作為註解,已移除錯誤 key 並改為 TODO,等企劃確認正式 key。

So What

這個 session 揭示了兩層問題:一是 fallback 設定的靜默錯誤(數值錯但不報錯),二是「無上限」的設計語義如何在伺服器端安全處理。

特別是「無上限 = 99999999999」這個企劃慣例,如果不知道這個規則,很容易直接把常數表值存入 DB,造成整數溢出或其他問題。建立 「常數表大數 = 設計無上限,伺服器端需 cap」 的規範非常重要。

另外,錯誤的常數 key 註解比沒有註解更危險,因為它給人一種「已接入常數表」的假象,實際上 key 根本不存在。

Trade-offs

  • 直接 SQL 更新 vs Migration 檔案:一次性資料修正直接下 SQL 更快,不需要建立 migration 檔;migration 適合 schema 變更,不適合純資料更新
  • Cap 固定值 vs 讀常數表 cap:2000 是設計文件規定的 DB 保護上限,hardcode 在伺服器端是合理的(有文件依據),不需要每次讀常數表來決定保護上限
  • 99999999999 的處理:不採用 min(fallback, constants_value) 是因為常數表值不是真實數字,是設計語義;正確做法是理解語義後在伺服器端 cap
  • 重啟 vs rebuild:只改 fallback 設定值不需要 rebuild,重啟即可載入新設定;改了 code 邏輯則需要重新 build

Try It Fast

# 確認線上玩家的 stamina_max 分布
SELECT stamina_max, COUNT(*) FROM players GROUP BY stamina_max;

# 將非 bot 玩家的 stamina_max 改為 100
UPDATE players SET stamina_max = 100 WHERE is_bot = false;

# 確認修改結果
SELECT stamina_max, COUNT(*) FROM players WHERE is_bot = false GROUP BY stamina_max;
# 掃描 codebase 中可能未接常數表的 hardcode 值
grep -rn 'ExciteTarget\|MaxExciteValue\|staminaMax\|StaminaMax' --include='*.go' .

# 確認哪些地方有用到 99999999999 這個大數
grep -rn '99999999999\|9999999999' --include='*.go' .

Recommendation

  1. 建立規範:new_player_defaults 中每個欄位都必須有對應的常數表來源,禁止 hardcode 與常數表衝突的值
  2. 建立「無上限常數」處理規範:常數表填 99999999999 = 設計無上限,伺服器端必須 cap 在設計文件規定的安全上限
  3. 禁止在 code 中填寫未經企劃確認的常數 key;如果 key 未定義,改為 // TODO: 待企劃定義對應常數表 key
  4. 系統性掃描 hardcode 值時,需區分三類:(a) 錯誤 fallback(需修正)、(b) 有設計依據的保護上限(需加文件引用)、© 待企劃確認的 TODO(需追蹤)
  5. 定期做靜態掃描:grep codebase 中的數字 literal,確認初始化相關的 hardcode 值都有對應常數表設定或明確的設計文件依據
  6. 發現數值異常時,優先追查初始化 fallback 路徑,而不只是看 API handler 邏輯

本文檔由 Semi-Brain 自動生成

Session ID: 45b7e5f8-50d6-4529-afa9-e872dd553db2

分析信心度: 88%