Skip to content

GameSpy4 Query Protocol ​

Pumpkin includes native support for the GameSpy4 Query protocol over UDP. External monitoring systems, Discord bots, and server listing platforms use Query to poll real-time player counts, MOTD, server versions, and active player lists with minimal network overhead.

Query wire encoding is implemented in crates/pumpkin-protocol/src/query.rs, and the asynchronous UDP listener is implemented in crates/pumpkin/src/net/query.rs.


Why Query? (UDP vs. Status Ping) ​

While standard Minecraft clients ping servers via the TCP Status state (SLP), Query operates over stateless UDP:

  • No TCP Handshake: Avoids TCP connection overhead and connection state tables.
  • Detailed Server State: Returns comprehensive key-value metadata and a full list of connected player usernames (not available via basic TCP ping).
  • Amplification Attack Protection: Enforces cryptographic challenge tokens so malicious actors cannot abuse the UDP port for denial-of-service reflection attacks.

The Handshake & Challenge Token Flow ​

To prevent UDP spoofing and reflection DDoS attacks, Query requires a two-step handshake:

  1. Handshake Challenge: The client transmits SHandshake (0xFEFD, type 0x09, and a 32-bit session ID). Pumpkin generates a cryptographic 32-bit integer challenge token tied to the client's SocketAddr and responds with CHandshake.
  2. Status Query: The client sends SStatusRequest containing the received challenge token:
    • Basic Status (no padding): Returns MOTD, game type (SMP), map name, online player count, and max player capacity.
    • Full Status (4-byte padding): Returns detailed server metadata key-value pairs (hostname, gametype, game_id, version, plugins, map, numplayers, maxplayers, hostport, hostip) followed by an array of online player usernames.

Challenge Token Invalidation ​

In crates/pumpkin/src/net/query.rs, challenge tokens are stored in a thread-safe map (Arc<RwLock<HashMap<SocketAddr, i32>>>). A background Tokio timer purges all challenge tokens every 30 seconds to prevent replay abuse:

rust
// Tokens are strictly bound to the client's IP and port
let valid_challenge_tokens = Arc::new(RwLock::new(HashMap::new()));

// Background expiration loop
tokio::spawn(async move {
    let mut interval = time::interval(Duration::from_secs(30));
    loop {
        interval.tick().await;
        valid_challenge_tokens.write().await.clear();
    }
});

Packet Formats ​

Every incoming Query packet starts with the 2-byte magic header 0xFEFD followed by a 1-byte packet type:

Type ByteNameDirectionDescription
0x09Handshake RequestClient → ServerClient asks for a challenge token.
0x09Handshake ResponseServer → ClientReturns the numeric token encoded as an ASCII string.
0x00Stat RequestClient → ServerRequests basic or full status with challenge token.
0x00Basic Stat ResponseServer → Client11-byte short summary.
0x00Full Stat ResponseServer → ClientKey-value dictionary + null-separated player list.

Response Payloads ​

1. Basic Status (CBasicStatus) ​

Contains essential server fields:

  • motd: Server message of the day (stripped of color codes).
  • gametype: Fixed string "SMP".
  • map: Active world/level name (e.g., "world").
  • numplayers: Online player count.
  • maxplayers: Maximum configured player capacity.
  • hostport: Port number (u16 Little Endian).
  • hostip: Server IP string.

2. Full Status (CFullStatus) ​

Returns two null-terminated sections:

  1. Key-Value Metadata Map:
    • hostname: Server description / MOTD.
    • gametype: "SMP".
    • game_id: "MINECRAFT".
    • version: Server protocol / Minecraft version string.
    • plugins: Installed server software and plugin names.
    • map: Level name.
    • numplayers / maxplayers: Capacity stats.
    • hostport / hostip: Network bindings.
  2. Player List:
    • An array of null-terminated UTF-8 strings containing the usernames of all currently connected players.
    • Concludes with a terminal null byte (\0).

Configuration in pumpkin.toml ​

toml
[networking.query]
enabled = true
address = "0.0.0.0:25565"

Released under the MIT License.