Skip to main content
The query package implements the UT3 query protocol for querying Minecraft Bedrock Edition server information. Note that not all servers support this protocol (e.g., Bedrock Dedicated Server does not).

Functions

Do

func Do(address string) (information map[string]string, err error)
Queries a server using the UT3 query protocol and returns server information.
address
string
The server address to query (e.g., “localhost:19132”)
map[string]string
A map containing server information if the query succeeds
error
Error if the query fails or times out (5 second timeout)

Information Keys

The returned map typically contains the following keys:
  • hostname - Server name/MOTD
  • gametype - Game type (e.g., “SMP”)
  • game_id - Game identifier (“MINECRAFTPE”)
  • version - Server version
  • plugins - Installed plugins
  • map - World/map name
  • numplayers - Current player count
  • maxplayers - Maximum player capacity
  • hostport - Server port
  • hostip - Server IP address
Additional keys may be present depending on the server implementation.

Example Usage

Basic Query

import "github.com/sandertv/gophertunnel/query"

info, err := query.Do("localhost:19132")
if err != nil {
    panic(err)
}

fmt.Println("Server Name:", info["hostname"])
fmt.Println("Players:", info["numplayers"], "/", info["maxplayers"])
fmt.Println("Version:", info["version"])
fmt.Println("Map:", info["map"])

Full Server Info

info, err := query.Do("play.example.com:19132")
if err != nil {
    log.Printf("Query failed: %v", err)
    return
}

fmt.Println("=== Server Information ===")
for key, value := range info {
    fmt.Printf("%s: %s\n", key, value)
}

With Timeout Handling

import (
    "context"
    "time"
    "github.com/sandertv/gophertunnel/query"
)

// Note: query.Do has a built-in 5 second timeout
info, err := query.Do("server.example.com:19132")
if err != nil {
    if strings.Contains(err.Error(), "timeout") {
        fmt.Println("Server does not support query protocol")
    } else {
        fmt.Println("Query error:", err)
    }
    return
}

fmt.Println("Query successful!")

Server Status Display

func displayServerStatus(address string) {
    info, err := query.Do(address)
    if err != nil {
        fmt.Printf("Could not query %s: %v\n", address, err)
        return
    }
    
    fmt.Printf(`
Server: %s
  Address: %s
  Players: %s/%s
  Version: %s
  Type: %s
  Map: %s
`,
        info["hostname"],
        address,
        info["numplayers"],
        info["maxplayers"],
        info["version"],
        info["gametype"],
        info["map"],
    )
    
    if plugins, ok := info["plugins"]; ok && plugins != "" {
        fmt.Println("  Plugins:", plugins)
    }
}

Periodic Monitoring

func monitorServer(address string, interval time.Duration) {
    ticker := time.NewTicker(interval)
    defer ticker.Stop()
    
    for range ticker.C {
        info, err := query.Do(address)
        if err != nil {
            log.Printf("Query failed: %v", err)
            continue
        }
        
        log.Printf("Server: %s - Players: %s/%s",
            info["hostname"],
            info["numplayers"],
            info["maxplayers"],
        )
    }
}

// Monitor every 30 seconds
go monitorServer("localhost:19132", 30*time.Second)

Limitations

Not all Minecraft servers support the query protocol:
  • Supported: PocketMine-MP and most third-party servers
  • Not Supported: Bedrock Dedicated Server (BDS)
For servers that don’t support query, use RakNet ping instead:
import "github.com/sandertv/go-raknet"

pong, err := raknet.Ping("localhost:19132")

Notes

  • The query has a built-in 5-second timeout
  • Uses UDP protocol
  • Requires the server to have query enabled in configuration
  • Some servers may return different or additional keys
  • Query may be blocked by firewalls