1 Commits

Author SHA1 Message Date
4c5dd0c5f4 Improve source query diagnostics 2026-04-05 13:51:50 +03:00
3 changed files with 52 additions and 2 deletions

View File

@@ -21,3 +21,14 @@ func NewAll(timeout time.Duration) *Client {
Source: source.New(source.Options{Timeout: timeout}),
}
}
func (c *Client) SetSourceDebug(debug bool) {
if c == nil {
return
}
timeout := 2 * time.Second
if c.Source != nil {
timeout = c.Source.Timeout()
}
c.Source = source.New(source.Options{Timeout: timeout, Debug: debug})
}

View File

@@ -29,6 +29,7 @@ func main() {
}
client := gamestatus.NewAll(cfg.timeout)
client.SetSourceDebug(cfg.debug)
info, err := connect(client, cfg.edition, host, port, cfg.useQuery)
if err != nil {
@@ -54,6 +55,7 @@ type cliConfig struct {
useQuery bool
pretty bool
motdRaw bool
debug bool
}
func parseCLI(args []string) (cliConfig, string, error) {
@@ -70,6 +72,7 @@ func parseCLI(args []string) (cliConfig, string, error) {
useQuery := fs.Bool("query", false, "use full query when supported")
pretty := fs.Bool("pretty", false, "use compact human-friendly output")
motdRaw := fs.Bool("motd-raw", false, "print raw MOTD without cleanup")
debug := fs.Bool("debug", false, "print protocol-specific diagnostics")
if len(args) > 0 {
switch strings.ToLower(strings.TrimSpace(args[0])) {
@@ -96,6 +99,7 @@ func parseCLI(args []string) (cliConfig, string, error) {
cfg.useQuery = *useQuery
cfg.pretty = *pretty
cfg.motdRaw = *motdRaw
cfg.debug = *debug
if fs.NArg() != 1 {
return cfg, "", nil
@@ -126,6 +130,8 @@ func printUsage() {
fmt.Fprintln(os.Stderr, " use compact human-friendly output")
fmt.Fprintln(os.Stderr, " -motd-raw")
fmt.Fprintln(os.Stderr, " print raw MOTD without cleanup")
fmt.Fprintln(os.Stderr, " -debug")
fmt.Fprintln(os.Stderr, " print protocol-specific diagnostics")
}
func connect(client *gamestatus.Client, edition, host string, port int, useQuery bool) (*gamestatus.ServerInfo, error) {

View File

@@ -2,6 +2,8 @@ package source
import (
"encoding/json"
"errors"
"fmt"
"net"
"strconv"
"strings"
@@ -14,6 +16,7 @@ import (
type Options struct {
Timeout time.Duration
Debug bool
}
type Client struct {
@@ -27,6 +30,13 @@ func New(opt Options) *Client {
return &Client{opt: opt}
}
func (c *Client) Timeout() time.Duration {
if c == nil {
return 0
}
return c.opt.Timeout
}
func (c *Client) Connect(address string, port int) (*info.ServerInfo, error) {
return c.query(address, port, false)
}
@@ -93,14 +103,14 @@ func (c *Client) query(address string, port int, full bool) (*info.ServerInfo, e
addr := normalizeAddress(address, port)
client, err := a2s.NewClient(addr, a2s.TimeoutOption(c.opt.Timeout))
if err != nil {
return nil, err
return nil, wrapQueryError(addr, "connect", err, c.opt.Debug)
}
defer client.Close()
start := time.Now()
infoResp, err := client.QueryInfo()
if err != nil {
return nil, err
return nil, wrapQueryError(addr, "info", err, c.opt.Debug)
}
out := &info.ServerInfo{
@@ -133,10 +143,14 @@ func (c *Client) query(address string, port int, full bool) (*info.ServerInfo, e
}
}
out.Players = names
} else if err != nil && c.opt.Debug {
return nil, wrapQueryError(addr, "players", err, true)
}
rulesResp, err := client.QueryRules()
if err == nil && rulesResp != nil {
out.RawQuery = rulesResp.Rules
} else if err != nil && c.opt.Debug {
return nil, wrapQueryError(addr, "rules", err, true)
}
}
@@ -145,6 +159,25 @@ func (c *Client) query(address string, port int, full bool) (*info.ServerInfo, e
return out, nil
}
func wrapQueryError(addr, stage string, err error, debug bool) error {
if err == nil {
return nil
}
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
msg := fmt.Sprintf("no A2S response from %s", addr)
if debug && strings.TrimSpace(stage) != "" {
msg += fmt.Sprintf(" during %s", stage)
}
msg += " (server may have query disabled, blocked, or be using a different query port)"
return errors.New(msg)
}
if debug && strings.TrimSpace(stage) != "" {
return fmt.Errorf("source query %s failed for %s: %w", stage, addr, err)
}
return err
}
func normalizeAddress(host string, port int) string {
if port == 0 {
port = a2s.DefaultPort