eros 86153894ac feat: add Vite TypeScript frontend for curling game
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-24 08:18:13 -07:00

83 lines
2.4 KiB
TypeScript

import type {
ServerGameStateMessage,
ServerMessageTyped as ServerMessage,
Team,
} from './protocol'
const WS_URL = import.meta.env.VITE_WS_URL
function resolveWsUrl(): string {
if (WS_URL) return WS_URL
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
return `${protocol}//${window.location.hostname}:3000/ws`
}
export interface NetCallbacks {
onJoined: (room: string, team: Team) => void
onWaiting: (message: string) => void
onGameState: (msg: ServerGameStateMessage) => void
onTrajectory: (path: [number, number, number][]) => void
onEndScored: (end: number, points: number, scoringTeam: Team | null) => void
onGameOver: (scores: number[], winner: Team | null) => void
onError: (message: string) => void
onClose: () => void
}
let socket: WebSocket | null = null
export function connect(room: string, team: Team | null, callbacks: NetCallbacks): void {
if (socket) return
const teamParam = team ? `&team=${encodeURIComponent(team)}` : ''
const url = `${resolveWsUrl()}?room=${encodeURIComponent(room)}${teamParam}`
const ws = new WebSocket(url)
ws.onopen = () => {
socket = ws
}
ws.onmessage = (event) => {
try {
const msg = JSON.parse(event.data) as ServerMessage
if (!msg || typeof msg.type !== 'string') return
switch (msg.type) {
case 'joined':
callbacks.onJoined(msg.room, msg.team)
break
case 'waiting':
callbacks.onWaiting(msg.message)
break
case 'game_state':
callbacks.onGameState(msg)
break
case 'trajectory':
callbacks.onTrajectory(msg.path)
break
case 'end_scored':
callbacks.onEndScored(msg.end, msg.points, msg.scoring_team ?? null)
break
case 'game_over':
callbacks.onGameOver(msg.scores, msg.winner)
break
case 'error':
callbacks.onError(msg.message)
break
}
} catch (err) {
console.error('Failed to parse server message', err)
}
}
ws.onclose = () => {
socket = null
callbacks.onClose()
}
ws.onerror = () => {}
}
export function sendThrow(broomX: number, broomY: number, weight: number, curl: number, friction: number): void {
if (!socket || socket.readyState !== WebSocket.OPEN) return
socket.send(JSON.stringify({ type: 'throw', broom_x: broomX, broom_y: broomY, weight, curl, friction }))
}