import { createContext, useContext, useState, useEffect } from 'react' import type { ReactNode } from 'react' interface ServerInfo { name: string ip: string port: number category: 'Surf' | 'Kz' | 'Bhop' } interface A2SResponse { appID: number botCount: number environment: string gameDescription: string gameDirectory: string mapName: string maxPlayers: number playerCount: number serverName: string serverType: string vac: number version: string visibility: number } export interface ServerData extends ServerInfo { a2sData?: A2SResponse status: 'online' | 'offline' | 'loading' error?: string } interface ServerContextType { servers: ServerData[] loading: boolean lastUpdated: number hasInitialData: boolean } const ServerContext = createContext(undefined) // Server list from database export const serverList: ServerInfo[] = [ { name: 'Surf 66 Tick #1', ip: '14.103.233.1', port: 27015, category: 'Surf' }, { name: 'Surf 66 Tick #2', ip: '14.103.233.1', port: 27016, category: 'Surf' }, { name: 'Surf 66 Tick #3', ip: '14.103.233.1', port: 27017, category: 'Surf' }, { name: 'Surf 100 Tick #1', ip: '14.103.233.1', port: 28015, category: 'Surf' }, { name: 'Surf 100 Tick #2', ip: '14.103.233.1', port: 28016, category: 'Surf' }, { name: 'Kz #1', ip: '14.103.233.1', port: 29015, category: 'Kz' }, { name: 'Kz #2', ip: '14.103.233.1', port: 29016, category: 'Kz' }, { name: 'Bhop #1', ip: '14.103.233.1', port: 30015, category: 'Bhop' }, { name: 'Bhop #2', ip: '14.103.233.1', port: 30016, category: 'Bhop' } ] export function ServerProvider({ children }: { children: ReactNode }) { const [servers, setServers] = useState([]) const [loading, setLoading] = useState(true) const [lastUpdated, setLastUpdated] = useState(Date.now()) const [hasInitialData, setHasInitialData] = useState(false) const fetchServerData = async () => { try { // Batch query all servers in a single API call const response = await fetch('/api/server/statistics/a2s-query', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ servers: serverList.map(server => ({ serverIP: server.ip, serverPort: server.port, timeout: 3000 })) }) }) if (!response.ok) { throw new Error(`HTTP ${response.status}`) } const data = await response.json() // Update servers state with new data setServers(prevServers => { const results: ServerData[] = serverList.map((server, index) => { const a2sData = data.results?.[index] if (a2sData) { return { ...server, a2sData, status: 'online' as const } } else { // If no data for this server but we have previous data, keep the previous data const prevServer = prevServers.find(s => s.ip === server.ip && s.port === server.port) if (prevServer?.a2sData) { return prevServer // Keep previous successful data } return { ...server, status: 'offline' as const, error: 'No response from server' } } }) return results }) setLoading(false) setLastUpdated(Date.now()) setHasInitialData(true) } catch (error) { console.error('Failed to fetch server data:', error) // If we already have data from a previous successful fetch, keep using it // Only show error state if this is the very first fetch attempt setServers(prevServers => { // Check if we have any previous successful data const hasPreviousData = prevServers.some(s => s.a2sData) if (hasPreviousData) { // Keep previous data on polling errors return prevServers } else { // First fetch failed, show offline state const offlineServers: ServerData[] = serverList.map(server => ({ ...server, status: 'offline' as const, error: error instanceof Error ? error.message : 'Unknown error' })) return offlineServers } }) setLoading(false) // Don't update lastUpdated on error to keep showing previous successful fetch time } } // Initial fetch and polling every 2 seconds useEffect(() => { fetchServerData() const interval = setInterval(() => { fetchServerData() }, 2000) // Poll every 2 seconds return () => clearInterval(interval) }, []) // Empty dependency array is intentional - we want this to run once return ( {children} ) } export function useServers() { const context = useContext(ServerContext) if (context === undefined) { throw new Error('useServers must be used within a ServerProvider') } return context }