forked from eros/curltastic
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
100 lines
2.4 KiB
TypeScript
100 lines
2.4 KiB
TypeScript
import type {
|
|
ServerGameStateMessage,
|
|
ServerStoneTrajectory,
|
|
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) => void
|
|
onWaiting: (message: string) => void
|
|
onGameState: (msg: ServerGameStateMessage) => void
|
|
onTrajectory: (paths: ServerStoneTrajectory[]) => 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, callbacks: NetCallbacks): void {
|
|
if (socket) return
|
|
const url = `${resolveWsUrl()}?room=${encodeURIComponent(room)}`
|
|
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)
|
|
break
|
|
case 'waiting':
|
|
callbacks.onWaiting(msg.message)
|
|
break
|
|
case 'game_state':
|
|
callbacks.onGameState(msg)
|
|
break
|
|
case 'trajectory':
|
|
callbacks.onTrajectory(msg.paths)
|
|
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(
|
|
team: Team,
|
|
broomX: number,
|
|
broomY: number,
|
|
weight: number,
|
|
curl: number,
|
|
friction: number,
|
|
): void {
|
|
if (!socket || socket.readyState !== WebSocket.OPEN) return
|
|
socket.send(
|
|
JSON.stringify({
|
|
type: 'throw',
|
|
team,
|
|
broom_x: broomX,
|
|
broom_y: broomY,
|
|
weight,
|
|
curl,
|
|
friction,
|
|
}),
|
|
)
|
|
}
|