fix!(lua.endpoints): buy/pack hang when Celestial pack contains Black Hole

オープン
#199 コメント 4 件 リアクション 0 件 担当者 1 名 GitHub で見る

@S1M0N38 がすでに取り組んでいます。

2026年6月13日 から。

評価

この issue はまだ評価されていません。

説明

completed-in-dev

Description

Buying or selecting from a Celestial booster pack causes the endpoint to hang indefinitely when the pack contains Black Hole (c_black_hole, a Spectral card) as its first card. The response is never sent and the request times out on the client side.

Both buy (opening the pack) and pack (selecting a card from an already-opened pack) are affected.

Root Cause

Both src/lua/endpoints/buy.lua (~line 260) and src/lua/endpoints/pack.lua (~line 305) infer whether hand cards need to be dealt by checking the first card's ability.set:

local pack_key = G.pack_cards.cards[1].ability and G.pack_cards.cards[1].ability.set
local needs_hand = pack_key == "Tarot" or pack_key == "Spectral"
  • Black Hole has ability.set = "Spectral" (it is a Spectral-type consumable)
  • But Black Hole can appear in Celestial packs via the soul mechanism: when create_card("Planet", ...) is called for a Celestial pack, there is a ~0.3% chance per card (pseudorandom > 0.997) that it becomes Black Hole
  • Celestial packs do NOT have draw_hand = true in SMODS's booster config (only Arcana and Spectral packs do)
  • So when Black Hole is the first card, the code sets needs_hand = true and waits for G.hand.cards to be populated
  • Hand cards are never dealt → the condition event spins forever → endpoint hangs

Steps to Reproduce

Seed S001250 naturally produces Black Hole in a Celestial pack via the soul mechanism after cycling 12 Celestial packs in the first shop.

  1. Start a run: start({"deck": "b_red", "stake": "stake_white", "seed": "S001250"})
  2. Select a blind: select()
  3. Set chips and money: set({"chips": 100000, "money": 999})
  4. Play a hand: play({"cards": [0,1,2,3,4]})
  5. Cash out: cash_out()
  6. Free a booster slot: buy({"pack": 0}) → pack({"skip": true})
  7. Cycle 12 Celestial packs: add({"key": "p_celestial_normal_1"}) → buy({"pack": 1}) → pack({"skip": true}) × 12
  8. Add the 13th Celestial pack: add({"key": "p_celestial_normal_1"})
  9. Buy it: buy({"pack": 1})
  10. The request hangs indefinitely — Black Hole (set=Spectral) is the first card

For the pack endpoint: load the attached fixture to reach SMODS_BOOSTER_OPENED state with Black Hole at index 0, then call pack({"card": 0}) — this also hangs.

Expected Behavior

The endpoints should return the gamestate response after the pack opens / selection completes, regardless of whether the first card is a Spectral type. Celestial packs never deal hand cards, so the endpoints should not wait for them.

Actual Behavior

The request never returns. The log shows Buying Booster 'Celestial Pack' for $4 but no corresponding OK or ERR response. The event loop spins on the hand_ready condition that never becomes true.

Why This Bug Is Difficult to Reproduce

  • Black Hole only appears in Celestial packs through the soul mechanism: a ~0.3% chance per card when creating Planet cards
  • The bug only triggers when Black Hole is the first card in the pack, because the code only checks G.pack_cards.cards[1]
  • Combined probability: roughly 0.05% per shop — automated bots hit it eventually, humans almost never
  • Found via parallel search across 8 headless instances testing 2000 seeds

Environment

  • OS: macOS
  • Lovely version: 0.8.0
  • SMODS version: v1.0.0~BETA-1221a
  • BalatroBot commit: 7a33403

Files

主要言語
Python
スター
80
フォーク
23
PR マージ指標
30日以内にマージされた PR はありません

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

coder/balatrobot のほかの issue

coder/balatrobot の issue をすべて見る

似ている issue

Python の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。