Skip to content

feat(lobby): block create/join while in matchmaking#496

Merged
V-rtualized merged 1 commit into
Balatro-Multiplayer:mqttfrom
ChronoFinale:feat/lobby-guard-while-queued
Jul 21, 2026
Merged

feat(lobby): block create/join while in matchmaking#496
V-rtualized merged 1 commit into
Balatro-Multiplayer:mqttfrom
ChronoFinale:feat/lobby-guard-while-queued

Conversation

@ChronoFinale

@ChronoFinale ChronoFinale commented Jul 13, 2026

Copy link
Copy Markdown

Beta testers could create or join a private lobby while a matchmaking search was active, ending up searching and in a lobby at once. The search kept running server-side with no feedback. This PR blocks both lobby entry points while queued. Two BalatroMultiplayerAPI PRs must merge first (see Depends on).

The bug

MP.pvp_create_private_lobby and MP.pvp_join_lobby had no queue check. Nothing stopped a queued player from allocating or joining a lobby mid-search.

The fix

Both functions now run this guard before touching the API (create's closure re-enters MP.pvp_create_private_lobby instead):

if MPAPI.matchmaking.guard_queued(function() return MP.pvp_join_lobby(code) end) then
	return
end

guard_queued(replay) lives in the API (Balatro-Multiplayer/BalatroMultiplayerAPI#7). If the player is queued, it shows a "Leave Queue & Continue" overlay, stashes replay, and returns true; the caller returns early. If not queued, it returns false and the function proceeds. Clicking the overlay button leaves the queue and runs the stashed replay.

The replay re-enters this mod's own function, not MPAPI.create_lobby / MPAPI.join_lobby. Only the mod function runs MP.setup_lobby_mirror and the CONNECTED handler that moves the client into the lobby. Replaying the API primitive would join or create server-side (the other player sees you) but leave the client stuck on the PvP main menu. That is why the guard lives here and not inside the API primitives.

How to review

  • pvp_api/flow.lua: the core change. Two guard blocks, one per entry point. Nothing else changes.
  • tests/test_lobby_guard_while_queued.lua: new standalone test.

Tests

Run from the repo root: luajit tests/test_lobby_guard_while_queued.lua. No game install needed. Cases:

  • Blocked while searching: no API call, no mirror, overlay shown, replay stashed.
  • Replay after leaving the queue: MPAPI.join_lobby and MP.setup_lobby_mirror both run.
  • Not searching: join proceeds normally, no overlay.
  • Same blocked-then-replay pair for create.
  • Red control: a replay that calls the API primitive directly joins server-side but never runs setup_lobby_mirror, reproducing the original stranding bug.

Depends on

Merge both before this PR:

What it looks like

Clicking a lobby create/join button while searching shows the shared guard overlay (from the API's queue-guard PR) instead of silently stranding the queue — with the search visibly running in the status panel:

queue guard overlay

Full annotated gallery of the guard's behavior (every entry point, every button outcome): VERIFICATION.md

You could create or join a custom lobby while a matchmaking search was
active, ending up searching and in a lobby at once (search left running
server-side with no feedback).

Guard MP.pvp_create_private_lobby / MP.pvp_join_lobby with the API's
shared MPAPI.matchmaking.guard_queued: while searching it shows the
"Leave Queue & Continue" overlay and stashes a replay closure instead of
proceeding. Crucially the replay re-enters the consumer function, not the
API primitive -- MP.pvp_join_lobby / MP.pvp_create_private_lobby call
MP.setup_lobby_mirror(lobby) after obtaining the lobby, which builds the
lobby UI and wires the CONNECTED handler that transitions the client into
the lobby. Replaying MPAPI.join_lobby/create_lobby directly joined/created
server-side (the other player saw you) but skipped setup_lobby_mirror,
stranding the client on the PvP menu.

Requires MultiplayerAPI with the queue-guard helper.

Adds tests/test_lobby_guard_while_queued.lua: blocked while searching
(nothing allocated, overlay shown, replay stashed), Leave Queue & Continue
runs the full consumer setup (setup_lobby_mirror ran), proceeds normally
when not searching, plus a control that replays the API primitive and
proves setup_lobby_mirror never ran (the strand bug).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants