package mcstatus import ( "encoding/json" "fmt" "strings" "time" mcnet "github.com/Tnze/go-mc/net" pk "github.com/Tnze/go-mc/net/packet" ) // ServerStatus 服务器状态信息 type ServerStatus struct { Host string Port int Latency int64 // 延迟(毫秒) Version Version Players Players Description string // MOTD Favicon string // Base64 编码的图标 } // Version 版本信息 type Version struct { Name string Protocol int } // Players 玩家信息 type Players struct { Online int Max int } // Ping 查询 Minecraft 服务器状态 func Ping(host string, port int, timeout time.Duration) (*ServerStatus, error) { // 连接服务器 startTime := time.Now() conn, err := mcnet.DialMC(fmt.Sprintf("%s:%d", host, port)) if err != nil { return nil, fmt.Errorf("failed to connect: %w", err) } defer conn.Close() latency := time.Since(startTime).Milliseconds() // 发送握手包 handshake := pk.Marshal( 0x00, // Handshake packet ID pk.VarInt(47), // Protocol version (1.8+) pk.String(host), pk.UnsignedShort(port), pk.VarInt(1), // Next state: Status ) if err := conn.WritePacket(handshake); err != nil { return nil, fmt.Errorf("failed to send handshake: %w", err) } // 发送状态请求 request := pk.Marshal(0x00) // Status request packet ID if err := conn.WritePacket(request); err != nil { return nil, fmt.Errorf("failed to send status request: %w", err) } // 读取响应 var response pk.Packet if err := conn.ReadPacket(&response); err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } // 解析响应 var statusJSON pk.String if err := response.Scan(&statusJSON); err != nil { return nil, fmt.Errorf("failed to parse response: %w", err) } // 解析 JSON 响应 var statusData map[string]interface{} if err := json.Unmarshal([]byte(statusJSON), &statusData); err != nil { return nil, fmt.Errorf("failed to parse JSON: %w", err) } status := &ServerStatus{ Host: host, Port: port, Latency: latency, } // 解析 description if desc, ok := statusData["description"].(map[string]interface{}); ok { if text, ok := desc["text"].(string); ok { status.Description = text } else if text, ok := desc["extra"].([]interface{}); ok && len(text) > 0 { // 处理 extra 数组 var descText strings.Builder for _, item := range text { if itemMap, ok := item.(map[string]interface{}); ok { if text, ok := itemMap["text"].(string); ok { descText.WriteString(text) } } } status.Description = descText.String() } } else if desc, ok := statusData["description"].(string); ok { status.Description = desc } // 解析 version if version, ok := statusData["version"].(map[string]interface{}); ok { if name, ok := version["name"].(string); ok { status.Version.Name = name } if protocol, ok := version["protocol"].(float64); ok { status.Version.Protocol = int(protocol) } } // 解析 players if players, ok := statusData["players"].(map[string]interface{}); ok { if online, ok := players["online"].(float64); ok { status.Players.Online = int(online) } if max, ok := players["max"].(float64); ok { status.Players.Max = int(max) } } // 解析 favicon if favicon, ok := statusData["favicon"].(string); ok { // 移除 data:image/png;base64, 前缀 if strings.HasPrefix(favicon, "data:image/png;base64,") { status.Favicon = strings.TrimPrefix(favicon, "data:image/png;base64,") } else { status.Favicon = favicon } } return status, nil }