ironie-nextjs/src/app/components/TelemetrySocket.tsx
2025-09-11 14:01:50 +02:00

96 lines
3.3 KiB
TypeScript

'use client'
import { useEffect, useMemo, useRef } from 'react'
import { usePresenceStore } from '@/app/lib/usePresenceStore'
import { useTelemetryStore } from '@/app/lib/useTelemetryStore'
type Status = 'idle'|'connecting'|'open'|'closed'|'error'
function makeWsUrl(host?: string, port?: string, path?: string, scheme?: string) {
const h = (host ?? '').trim() || '127.0.0.1'
const p = (port ?? '').trim() || '8081'
const pa = (path ?? '').trim() || '/telemetry'
const sch = (scheme ?? '').toLowerCase()
const pageHttps = typeof window !== 'undefined' && window.location.protocol === 'https:'
const useWss = sch === 'wss' || (sch !== 'ws' && (p === '443' || pageHttps))
const proto = useWss ? 'wss' : 'ws'
const portPart = (p === '80' || p === '443') ? '' : `:${p}`
return `${proto}://${h}${portPart}${pa}`
}
export default function TelemetrySocket() {
const url = useMemo(
() => makeWsUrl(
process.env.NEXT_PUBLIC_CS2_TELEMETRY_WS_HOST,
process.env.NEXT_PUBLIC_CS2_TELEMETRY_WS_PORT,
process.env.NEXT_PUBLIC_CS2_TELEMETRY_WS_PATH,
process.env.NEXT_PUBLIC_CS2_TELEMETRY_WS_SCHEME
),
[]
)
const setSnapshot = usePresenceStore(s => s.setSnapshot)
const setJoin = usePresenceStore(s => s.setJoin)
const setLeave = usePresenceStore(s => s.setLeave)
const setMapKey = useTelemetryStore(s => s.setMapKey)
// interne Refs für saubere Handler
const aliveRef = useRef(true)
const retryRef = useRef<number | null>(null)
const wsRef = useRef<WebSocket | null>(null)
useEffect(() => {
aliveRef.current = true
const connect = () => {
if (!aliveRef.current || !url) return
const ws = new WebSocket(url)
wsRef.current = ws
ws.onopen = () => {
if (process.env.NODE_ENV !== 'production') console.debug('[TelemetrySocket] open')
}
ws.onerror = () => {
if (process.env.NODE_ENV !== 'production') console.debug('[TelemetrySocket] error')
}
ws.onclose = () => {
if (process.env.NODE_ENV !== 'production') console.debug('[TelemetrySocket] closed')
if (aliveRef.current) retryRef.current = window.setTimeout(connect, 2000)
}
ws.onmessage = (ev) => {
let msg: any = null
try { msg = JSON.parse(String(ev.data ?? '')) } catch {}
if (!msg) return
if (msg.type === 'players' && Array.isArray(msg.players)) {
// kompletter Presence-Snapshot
setSnapshot(msg.players)
} else if (msg.type === 'player_join' && msg.player) {
setJoin(msg.player)
} else if (msg.type === 'player_leave') {
const sid = msg.steamId ?? msg.steam_id ?? msg.id
if (sid != null) setLeave(sid)
}
// ⬇️ Map-Event aus /telemetry
if (msg.type === 'map' && typeof msg.name === 'string') {
const key = msg.name.toLowerCase()
if (process.env.NODE_ENV!=='production') console.debug('[TelemetrySocket] map:', key)
setMapKey(key)
}
}
}
connect()
return () => {
aliveRef.current = false
if (retryRef.current) window.clearTimeout(retryRef.current)
try { wsRef.current?.close(1000, 'telemetry socket unmounted') } catch {}
}
}, [url, setSnapshot, setJoin, setLeave])
return null
}