import type { PlayerSession } from "./types"; import { onlineApiOrigin, requestOnlineJson } from "./onlineAccountClient"; import type { ArenaLoadout, ArenaQueueRequest, ArenaQueueState, ArenaStageManifest } from "../game/arenaTypes"; export interface ArenaStateResponse { characterId: string | null; loadouts: ArenaLoadout[]; ratings: Record<"2v2" | "3v3", { bracket: "2v2" | "3v3"; rating: number; title: string; updatedAt: number | null } | null>; queue: ArenaQueueState; stages: ArenaStageManifest[]; } function token(session: PlayerSession): string { if (session.kind !== "account" || !session.accessToken) throw new Error("Rated arenas require an online account."); return session.accessToken; } export function fetchArenaState(session: PlayerSession, characterId: string): Promise { return requestOnlineJson(`/api/arena/state?characterId=${encodeURIComponent(characterId)}`, {}, token(session)); } export function putArenaLoadout( session: PlayerSession, characterId: string, slot: number, loadout: Omit & { selected?: boolean }, ): Promise { return requestOnlineJson(`/api/arena/loadouts/${slot}`, { method: "PUT", body: JSON.stringify({ ...loadout, characterId }), }, token(session)); } export function deleteArenaLoadout(session: PlayerSession, characterId: string, slot: number) { return requestOnlineJson<{ ok: true }>(`/api/arena/loadouts/${slot}`, { method: "DELETE", body: JSON.stringify({ characterId }), }, token(session)); } export function enterArenaQueue(session: PlayerSession, request: ArenaQueueRequest): Promise { return requestOnlineJson("/api/arena/queue", { method: "POST", body: JSON.stringify(request) }, token(session)); } export function leaveArenaQueue(session: PlayerSession): Promise { return requestOnlineJson("/api/arena/queue", { method: "DELETE" }, token(session)); } export function acceptArenaReadyCheck(session: PlayerSession): Promise { return requestOnlineJson("/api/arena/ready", { method: "POST" }, token(session)); } interface ArenaSocketTicket { ticket: string; expiresAt: number; path: string } export async function openArenaSocket( session: PlayerSession, onMessage: (message: unknown) => void, onClose: () => void, ): Promise { const response = await requestOnlineJson("/api/arena/socket-ticket", { method: "POST" }, token(session)); const origin = onlineApiOrigin(); const base = origin || window.location.origin; const url = new URL(response.path, base); url.protocol = url.protocol === "https:" ? "wss:" : "ws:"; url.searchParams.set("ticket", response.ticket); const socket = new WebSocket(url); socket.addEventListener("message", (event) => { try { onMessage(JSON.parse(String(event.data))); } catch { /* Ignore malformed server messages. */ } }); socket.addEventListener("close", onClose); await new Promise((resolve, reject) => { socket.addEventListener("open", () => resolve(), { once: true }); socket.addEventListener("error", () => reject(new Error("Could not connect to the arena server.")), { once: true }); }); return socket; }