From 9d5a046b4002c82d2f876defed6eb92ad1700c4d Mon Sep 17 00:00:00 2001 From: Stan Runge Date: Tue, 29 Sep 2026 02:58:48 +0000 Subject: [PATCH] fix: resume saved lobbies when host capacity is full --- src/lib/server/match/runtime.test.ts | 24 ++++++++++++++++++++++++ src/lib/server/match/runtime.ts | 3 ++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/lib/server/match/runtime.test.ts b/src/lib/server/match/runtime.test.ts index 01ff3d5..dffeb35 100644 --- a/src/lib/server/match/runtime.test.ts +++ b/src/lib/server/match/runtime.test.ts @@ -190,6 +190,30 @@ describe('match runtime with the simulator', () => { await runtime.stop(); }); + test('the due loop resumes a saved room even when all host slots are occupied', async () => { + const sim = new SimBancho(seeded(2)); + const saved = await sim.open('Already created'); + const initial = record( + { bestOf: 1, teamSize: 1, scoringType: 'score_v2' }, + slots(4, 0), + 1, + true + ); + initial.osuLobbyId = saved.id; + initial.ircAccount = saved.accountId; + const h = harness(initial, sim); + h.deps.dueMatchIds = async () => ['match-1']; + vi.spyOn(sim, 'capacity').mockReturnValue({ ...sim.capacity(), free: 0 }); + const make = vi.spyOn(sim, 'open'); + const runtime = new MatchRuntime(h.deps); + await runtime.boot(); + await vi.advanceTimersByTimeAsync(1); + expect(h.stored().procedure.phase).toBe('waiting'); + expect(h.stored().osuLobbyId).toBe(saved.id); + expect(make).not.toHaveBeenCalled(); + await runtime.stop(); + }); + test('a reload waits for an in-flight make and reuses its persisted room', async () => { const sim = new SimBancho(seeded(2)); const h = harness( diff --git a/src/lib/server/match/runtime.ts b/src/lib/server/match/runtime.ts index ef36d56..bea36d4 100644 --- a/src/lib/server/match/runtime.ts +++ b/src/lib/server/match/runtime.ts @@ -1096,7 +1096,8 @@ export class MatchRuntime { const ids = await this.deps.dueMatchIds().catch(() => []); for (const id of ids) { if (this.stopped) return; - if (this.deps.provider.capacity().free < 1) return; + // open() can reuse a saved room even when its host has no free slots. + // A new room checks capacity on this match's selected provider. try { await this.open(id); this.openFailures.delete(id);