a2s

a2s
Login

a2s

Go client library for Valve's A2S UDP server query protocol.

The package can query Source Engine compatible game servers for:

It handles challenge responses and Source Engine split-packet responses internally.

Install

Requires Go 1.24 or newer.

go get fakehacker.cc/a2s@latest

For direct VCS downloads, Go may need permission to use Fossil for this module:

go env -w GOVCS=fakehacker.cc:fossil,public:git|hg,private:all

You can also set GOVCS only for one command:

GOVCS=fakehacker.cc:fossil go get fakehacker.cc/a2s@latest

Usage

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"fakehacker.cc/a2s"
)

func main() {
	client, err := a2s.NewClient(a2s.WithTimeout(5 * time.Second))
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()

	info, err := client.Info(ctx, "192.0.2.10:27016")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s on %s: %d/%d players\n",
		info.Name,
		info.Map,
		info.Players,
		info.MaxPlayers,
	)
}

Query rules:

rules, err := client.Rules(ctx, "192.0.2.10:27016")
if err != nil {
	log.Fatal(err)
}

fmt.Println(rules.Raw["sv_cheats"])

Query Arma 3 or DayZ server-browser data:

browser, err := client.ServerBrowserRules(ctx, "192.0.2.10:27016")
if err != nil {
	log.Fatal(err)
}

switch browser.Game {
case a2s.ServerBrowserGameArma3:
	fmt.Println("Arma 3 mods:", len(browser.Mods))
case a2s.ServerBrowserGameDayZ:
	fmt.Println("DayZ island:", browser.Island)
}

Use the game-specific helpers when you want a type check:

arma3, err := client.Arma3Rules(ctx, "192.0.2.10:27016")
if err != nil {
	log.Fatal(err)
}

dayz, err := client.DayZRules(ctx, "192.0.2.10:27016")
if err != nil {
	log.Fatal(err)
}

fmt.Println(len(arma3.Mods), dayz.Island)

Addresses must use the server query port, not necessarily the game port. For DayZ, the query port is usually the game port plus 24648; for example, game port 2302 uses query port 27016.

Client Options

client, err := a2s.NewClient(
	a2s.WithTimeout(5*time.Second),
	a2s.WithBufferSize(65536),
)
if err != nil {
	log.Fatal(err)
}

Defaults:

Invalid option values return OptionError.

Client is safe for concurrent use. Each query opens and closes its own UDP connection.

Errors

Errors are returned as package-specific types and support errors.Is and errors.As.

info, err := client.Info(ctx, addr)
if err != nil {
	var queryErr *a2s.QueryError
	if errors.As(err, &queryErr) {
		log.Printf("query=%s operation=%s", queryErr.Query, queryErr.Operation)
	}

	if errors.Is(err, a2s.ErrGoldSourceResponse) {
		log.Printf("server returned unsupported GoldSource response")
	}

	return
}
_ = info

Common error types include:

Supported Response Details

A2S_INFO parsing includes the optional EDF fields:

When the full game ID is present, the low 24 bits are used to correct AppIDs that do not fit in the 16-bit base response field, such as DayZ.

A2S_RULES parsing includes ordinary rule key-value pairs and the nested server-browser payload used by Arma 3 and DayZ. The nested payload is split into binary pages, escaped for transport through Steam rules, and decoded into ServerBrowserRules.

Arma 3 server-browser parsing includes:

DayZ server-browser parsing includes:

Development

Run the test suite:

go test ./...

Run race-enabled tests:

go test -race ./...

Format check:

gofmt -l .

Protocol reference notes live in doc/server_queries.md; DayZ-specific notes live in doc/dayz.md.