Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions src/utility/utility-practice.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,10 @@ export class UtilityPracticeService {
[session.id, steamIds, user.steam_id],
);

if (invited.length > 0 && session.match_id) {
await this.matchAssistant.sendUtilityPracticeRefresh(session.match_id);
}

await this.notifyInvited(
session,
user.steam_id,
Expand Down Expand Up @@ -884,13 +888,20 @@ export class UtilityPracticeService {
return null;
}

// Invitees are on the door list before they join: a player who takes the
// invite by joining the host through Steam never touches joinUtilityPractice,
// and would otherwise be turned away with no idea why.
const players = await this.postgres.query<Array<{ steam_id: string }>>(
`SELECT mlp.steam_id::text AS steam_id
FROM public.match_lineup_players mlp
INNER JOIN public.match_lineups ml ON ml.id = mlp.match_lineup_id
WHERE ml.match_id = $1::uuid
AND mlp.steam_id IS NOT NULL`,
[row.match_id],
AND mlp.steam_id IS NOT NULL
UNION
SELECT i.steam_id::text AS steam_id
FROM public.utility_practice_invites i
WHERE i.utility_practice_session_id = $2::uuid`,
[row.match_id, row.session_id],
);

return {
Expand Down
27 changes: 27 additions & 0 deletions test/utility-practice.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1517,6 +1517,33 @@ describe("utility practice sessions (SQL-driven)", () => {
expect(session?.steam_ids.sort()).toEqual([host, mate].sort());
});

it("lets an invitee in before they have joined from the site", async () => {
const host = await fx.player();
const invited = await fx.player();
const { matchId } = await createPracticeMatch(host);
const sessionId = await insertSession(host, {
match_id: matchId,
status: "Ready",
});
const serverId = await reservedServer("practice-a", matchId, 27964);

const sendUtilityPracticeRefresh = jest.fn(
async (): Promise<void> => undefined,
);
const service = makeService({
matchAssistant: { sendUtilityPracticeRefresh },
});

await service.invite({ steam_id: host } as never, {
session_id: sessionId,
steam_ids: [invited],
});

expect(sendUtilityPracticeRefresh).toHaveBeenCalledWith(matchId);
const session = await service.sessionForServer(serverId);
expect(session?.steam_ids.sort()).toEqual([host, invited].sort());
});

// A compromised game server that could name a session id could read another
// session's match password, which is the credential for walking onto that
// server. The lookup starts from the authenticated server for that reason.
Expand Down
Loading