'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(null) const wsRef = useRef(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 }