From d79c6b1b4901fdf6dc5efb7612ece6018e946533 Mon Sep 17 00:00:00 2001
From: cialloo
Date: Sat, 4 Oct 2025 22:48:57 +0800
Subject: [PATCH] Refactor server data structure and update localization for
server categories and status
---
src/locales/en.json | 6 +-
src/locales/zh.json | 6 +-
src/pages/Servers.tsx | 358 +++++++++++++++++-------------------------
3 files changed, 150 insertions(+), 220 deletions(-)
diff --git a/src/locales/en.json b/src/locales/en.json
index 7969f7b..7203932 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -86,15 +86,15 @@
"title": "Game Servers",
"subtitle": "Join active Counter-Strike servers and compete with players worldwide",
"players": "Players",
- "ping": "Ping",
+ "status": "Status",
"joinServer": "Join Server",
"maintenance": "Maintenance",
"offline": "Offline",
"serverStats": "Server Statistics",
"onlineServers": "Online Servers",
"totalPlayers": "Total Players",
- "regions": "Regions",
- "uptime": "Uptime"
+ "categories": "Categories",
+ "totalServers": "Total Servers"
},
"forum": {
"title": "Community Forum",
diff --git a/src/locales/zh.json b/src/locales/zh.json
index 1623309..297810b 100644
--- a/src/locales/zh.json
+++ b/src/locales/zh.json
@@ -85,15 +85,15 @@
"title": "游戏服务器",
"subtitle": "加入活跃的反恐精英服务器,与全球玩家竞技",
"players": "玩家",
- "ping": "延迟",
+ "status": "状态",
"joinServer": "加入服务器",
"maintenance": "维护中",
"offline": "离线",
"serverStats": "服务器统计",
"onlineServers": "在线服务器",
"totalPlayers": "总玩家数",
- "regions": "地区",
- "uptime": "正常运行时间"
+ "categories": "分类",
+ "totalServers": "总服务器数"
},
"forum": {
"title": "社区论坛",
diff --git a/src/pages/Servers.tsx b/src/pages/Servers.tsx
index 8b2e224..d346e91 100644
--- a/src/pages/Servers.tsx
+++ b/src/pages/Servers.tsx
@@ -1,184 +1,106 @@
import { useTranslation } from 'react-i18next'
+import { useState, useEffect } from 'react'
import Layout from '../components/Layout'
import '../App.css'
-interface Server {
- id: number
+interface ServerInfo {
name: string
- map: string
- gameMode: string
- players: number
+ ip: string
+ port: number
+ category: 'Surf' | 'Kz' | 'Bhop'
+}
+
+interface A2SResponse {
+ appID: number
+ botCount: number
+ environment: string
+ gameDescription: string
+ gameDirectory: string
+ mapName: string
maxPlayers: number
- ping: number
- region: string
- difficulty: 'Easy' | 'Normal' | 'Hard' | 'Expert'
- status: 'online' | 'offline' | 'maintenance'
- description: string
- tags: string[]
+ playerCount: number
+ serverName: string
+ serverType: string
+ vac: number
+ version: string
+ visibility: number
+}
+
+interface ServerData extends ServerInfo {
+ a2sData?: A2SResponse
+ status: 'online' | 'offline' | 'loading'
+ error?: string
}
function Servers() {
const { t } = useTranslation()
- // Mock server data
- const servers: Server[] = [
- {
- id: 1,
- name: "Dust2 Classic - Competitive",
- map: "de_dust2",
- gameMode: "Competitive",
- players: 8,
- maxPlayers: 10,
- ping: 23,
- region: "Asia-Pacific",
- difficulty: "Normal",
- status: "online",
- description: "Classic Dust2 competitive matches with skilled players",
- tags: ["Competitive", "Ranked", "Dust2"]
- },
- {
- id: 2,
- name: "Mirage Pro League",
- map: "de_mirage",
- gameMode: "Premier",
- players: 10,
- maxPlayers: 10,
- ping: 45,
- region: "Europe",
- difficulty: "Hard",
- status: "online",
- description: "Professional Mirage matches for experienced players",
- tags: ["Premier", "Mirage", "Pro"]
- },
- {
- id: 3,
- name: "Inferno Casual",
- map: "de_inferno",
- gameMode: "Casual",
- players: 6,
- maxPlayers: 12,
- ping: 67,
- region: "North America",
- difficulty: "Easy",
- status: "online",
- description: "Relaxed Inferno matches for all skill levels",
- tags: ["Casual", "Inferno", "Beginner"]
- },
- {
- id: 4,
- name: "Nuke Tactical",
- map: "de_nuke",
- gameMode: "Tactical",
- players: 9,
- maxPlayers: 10,
- ping: 34,
- region: "Asia-Pacific",
- difficulty: "Expert",
- status: "online",
- description: "Tactical Nuke gameplay with strategic objectives",
- tags: ["Tactical", "Nuke", "Strategy"]
- },
- {
- id: 5,
- name: "Vertigo Deathmatch",
- map: "de_vertigo",
- gameMode: "Deathmatch",
- players: 12,
- maxPlayers: 16,
- ping: 28,
- region: "Europe",
- difficulty: "Normal",
- status: "online",
- description: "Fast-paced Vertigo deathmatch action",
- tags: ["Deathmatch", "Vertigo", "FFA"]
- },
- {
- id: 6,
- name: "Ancient Wingman",
- map: "de_ancient",
- gameMode: "Wingman",
- players: 2,
- maxPlayers: 4,
- ping: 52,
- region: "North America",
- difficulty: "Normal",
- status: "online",
- description: "2v2 Wingman matches on Ancient",
- tags: ["Wingman", "Ancient", "2v2"]
- },
- {
- id: 7,
- name: "Overpass Scrim",
- map: "de_overpass",
- gameMode: "Scrimmage",
- players: 8,
- maxPlayers: 10,
- ping: 41,
- region: "South America",
- difficulty: "Hard",
- status: "online",
- description: "Team scrimmage matches for practice",
- tags: ["Scrimmage", "Overpass", "Practice"]
- },
- {
- id: 8,
- name: "Cache Community",
- map: "de_cache",
- gameMode: "Community",
- players: 0,
- maxPlayers: 10,
- ping: 89,
- region: "Europe",
- difficulty: "Easy",
- status: "maintenance",
- description: "Community server - currently under maintenance",
- tags: ["Community", "Cache", "Social"]
- },
- {
- id: 9,
- name: "Train Pro League",
- map: "de_train",
- gameMode: "Premier",
- players: 10,
- maxPlayers: 10,
- ping: 31,
- region: "Asia-Pacific",
- difficulty: "Expert",
- status: "online",
- description: "Elite Train matches for top players",
- tags: ["Premier", "Train", "Elite"]
- },
- {
- id: 10,
- name: "Cobblestone Casual",
- map: "de_cbble",
- gameMode: "Casual",
- players: 4,
- maxPlayers: 12,
- ping: 76,
- region: "North America",
- difficulty: "Easy",
- status: "online",
- description: "Casual gameplay on the classic Cobblestone",
- tags: ["Casual", "Cobblestone", "Classic"]
- }
+ // 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' }
]
+ const [servers, setServers] = useState([])
+ const [selectedCategory, setSelectedCategory] = useState('All')
+
+ // Fetch A2S data for all servers
+ useEffect(() => {
+ const fetchServerData = async () => {
+ const serverPromises = serverList.map(async (server) => {
+ try {
+ const response = await fetch('/api/server/statistics/a2s-query', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ serverIP: server.ip,
+ serverPort: server.port,
+ timeout: 3000
+ })
+ })
+
+ if (!response.ok) {
+ throw new Error(`HTTP ${response.status}`)
+ }
+
+ const a2sData: A2SResponse = await response.json()
+ return { ...server, a2sData, status: 'online' as const }
+ } catch (error) {
+ console.error(`Failed to fetch data for ${server.name}:`, error)
+ return {
+ ...server,
+ status: 'offline' as const,
+ error: error instanceof Error ? error.message : 'Unknown error'
+ }
+ }
+ })
+
+ const results = await Promise.all(serverPromises)
+ setServers(results)
+ }
+
+ fetchServerData()
+ }, [])
+
+ // Filter servers by category
+ const filteredServers = selectedCategory === 'All'
+ ? servers
+ : servers.filter(server => server.category === selectedCategory)
+
+ const categories = ['All', ...Array.from(new Set(serverList.map(s => s.category)))]
+
const getStatusColor = (status: string) => {
switch (status) {
case 'online': return '#10b981'
case 'offline': return '#ef4444'
- case 'maintenance': return '#f59e0b'
- default: return '#6b7280'
- }
- }
-
- const getDifficultyColor = (difficulty: string) => {
- switch (difficulty) {
- case 'Easy': return '#10b981'
- case 'Normal': return '#3b82f6'
- case 'Hard': return '#f59e0b'
- case 'Expert': return '#ef4444'
+ case 'loading': return '#f59e0b'
default: return '#6b7280'
}
}
@@ -210,6 +132,34 @@ function Servers() {
}}>
{t('servers.subtitle')}
+
+ {/* Category Filter */}
+
+ {categories.map(category => (
+
+ ))}
+
@@ -224,9 +174,9 @@ function Servers() {
gridTemplateColumns: 'repeat(auto-fit, minmax(350px, 1fr))',
gap: '2rem'
}}>
- {servers.map((server) => (
+ {filteredServers.map((server) => (
- 🗺️ {server.map}
+ 🗺️ {server.a2sData?.mapName || 'Loading...'}
•
- {server.gameMode}
+ {server.category}
@@ -301,7 +251,7 @@ function Servers() {
fontWeight: 'bold',
color: 'var(--text-primary)'
}}>
- {server.players}/{server.maxPlayers}
+ {server.a2sData ? `${server.a2sData.playerCount - server.a2sData.botCount}/${server.a2sData.maxPlayers}` : 'Loading...'}
@@ -310,14 +260,14 @@ function Servers() {
color: 'var(--text-secondary)',
marginBottom: '0.25rem'
}}>
- {t('servers.ping')}
+ {t('servers.status')}
- {server.ping}ms
+ {server.status === 'online' ? 'Online' : server.status === 'offline' ? 'Offline' : 'Loading'}
@@ -332,45 +282,27 @@ function Servers() {
color: 'var(--text-secondary)',
marginBottom: '0.5rem'
}}>
- 🌍 {server.region}
+ � {server.ip}:{server.port}
- {server.difficulty}
+ {server.category}
-
- {server.description}
-
-
-
- {/* Tags */}
-
- {server.tags.map((tag, index) => (
-
- {tag}
-
- ))}
+ {server.error && (
+
+ Error: {server.error}
+
+ )}
{/* Join Button */}
@@ -394,9 +326,7 @@ function Servers() {
}
}}
>
- {server.status === 'online' ? t('servers.joinServer') :
- server.status === 'maintenance' ? t('servers.maintenance') :
- t('servers.offline')}
+ {server.status === 'online' ? t('servers.joinServer') : t('servers.offline')}
))}
@@ -446,7 +376,7 @@ function Servers() {
color: 'var(--accent-primary)',
marginBottom: '0.5rem'
}}>
- {servers.reduce((sum, s) => sum + s.players, 0)}
+ {servers.reduce((sum, s) => sum + (s.a2sData ? (s.a2sData.playerCount - s.a2sData.botCount) : 0), 0)}
- 4
+ {Array.from(new Set(servers.map(s => s.category))).length}
- {t('servers.regions')}
+ {t('servers.categories')}
@@ -478,13 +408,13 @@ function Servers() {
color: 'var(--accent-primary)',
marginBottom: '0.5rem'
}}>
- 95%
+ {servers.length}
- {t('servers.uptime')}
+ {t('servers.totalServers')}