Skip to content

Architecture

Sascha Greuel edited this page Aug 2, 2026 · 1 revision

Architecture

Home · Adding a protocol · Tests and fixtures

GameQ separates orchestration, server configuration, protocol parsing, network transport, and result filtering. Understanding those boundaries makes protocol changes safer.

flowchart LR
    A[Application] --> B[GameQ orchestrator]
    B --> C[Server definitions]
    C --> D[Protocol instances]
    D --> E[Query packets]
    E --> F[Native socket or protocol HTTP/master request]
    F --> G[Raw responses]
    G --> D
    D --> H[Protocol-native Result]
    H --> I[Filters and gq metadata]
    I --> A
Loading

Core classes

GameQ\GameQ

The public facade:

  • stores servers and global options;
  • chunks servers into bounded batches;
  • coordinates challenge, initial, and follow-up query rounds;
  • delegates native sockets to a query implementation;
  • parses each response and applies filters;
  • returns results keyed by server ID.

Use a normal new GameQ() instance in new code. GameQ::factory() remains for compatibility but global singleton-style access is unnecessary for most applications.

GameQ\Server

Server validates type, address, client port, query port, ID, and per-server options. It dynamically resolves an identifier such as css to GameQ\Protocols\Css and owns that protocol instance.

The selected class controls the default query-port calculation. Server::protocolInstance() is the non-null accessor; Server::protocol() is deprecated but retained for 4.x compatibility.

GameQ\Protocol

The protocol base class defines:

  • packet names and transport constants;
  • query-port offsets and join-link templates;
  • response storage;
  • challenge replacement;
  • response-driven follow-up packets;
  • common normalization helpers;
  • the processResponse() contract.

Named game classes often extend a shared family such as Source, Gamespy3, Quake3, or Unreal2 and override only metadata, port offsets, or normalization.

GameQ\Query\Native

The native query implementation opens UDP/TCP/TLS/SSL streams, writes packets, waits for responses, and returns them grouped by socket. It enforces response-size and packet-count limits and keeps TCP records until a complete response is available.

Some protocols perform HTTPS, REST, or master-list work in beforeSend() instead of sending a normal native packet. They still return data through the same protocol/result pipeline.

GameQ\Buffer

Buffer provides bounds-checked binary reads for strings, integers, and floats. Parser code should use it instead of manual unchecked offsets whenever the format is binary.

An out-of-bounds read is a protocol error, not a condition to suppress. It normally indicates a truncated response, the wrong type/port, or a changed wire format.

GameQ\Result

Result stores general values and assembles player/team rows. Protocol parsers return Result::fetch() or another array<string, mixed>.

Filters

Filters run after parsing. The default normalize filter adds cross-game fields; optional filters remove color codes or format duration values. See filters.

Query lifecycle

  1. addServer() validates the definition and constructs the protocol.
  2. process() divides servers according to max_servers_per_batch.
  3. Challenge-capable protocols send and apply their initial challenge.
  4. Each protocol runs beforeSend() and supplies its initial packets.
  5. Native responses are collected and stored on the protocol.
  6. getFollowUpPackets() can request more data, bounded by max_follow_up_rounds.
  7. processResponse() validates and parses all accumulated responses.
  8. GameQ adds online/address/port/type metadata.
  9. Filters run and the result keys are sorted.

Compatibility boundary

Public and protected APIs are compatibility-sensitive because third-party protocols extend GameQ classes. Prefer:

  • adding a new method over changing an existing signature;
  • a documented deprecation and replacement over removal;
  • private helpers for new implementation details;
  • tests for child protocols when changing a shared family;
  • composer bc-check before finalizing public/protected changes.

Shared protocol changes must be validated against every child fixture, not only the game that motivated the change.

Clone this wiki locally