Repository navigation
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 configured HTTP client]
F --> G[Raw responses]
G --> D
D --> H[Protocol-native Result]
H --> I[Filters and gq metadata]
I --> A
The public facade:
- stores servers and global options;
- owns the configured HTTP client and propagates it to existing and future protocol instances;
- 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.
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.
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;
- injectable HTTP transport access through
setHttpClient()andgetHttpClient(); - 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.
HTTP-backed protocols can extend GameQ\Protocols\JsonHttp. This base queries a JSON status endpoint at the configured server and query port through GameQ's configured HTTP client. Protocols backed by an official public server list can instead extend GameQ\Protocols\OfficialDirectory, which downloads and caches a validated HTTPS directory before selecting the configured server. Purpose-built HTTP protocols use the same transport for authentication and multi-request workflows. See HTTP and directory protocols.
The HTTP layer added in GameQ 5.2 separates protocol parsing from the concrete HTTP implementation:
-
ClientInterfaceis the transport contract; -
Requestcarries the method, validated URL and headers, body, timeout, and decoded response-size limit; -
Responsecarries the status, body, and headers; -
CurlClientis the standalone default; -
Psr18Clientadapts PSR-18/PSR-17 implementations; -
GuzzleClientadds Guzzle-specific per-request limits and transport policy.
Applications can inject a transport with GameQ::setHttpClient(). Protocols should call sendHttpRequest() instead of using cURL, Guzzle, or another HTTP library directly.
The native query implementation opens UDP/TCP/TLS/SSL streams, writes non-HTTP protocol 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 native packet. They still return data through the same protocol/result pipeline and use the configured HTTP client. OfficialDirectory, JsonHttp, authenticated APIs, and legacy HTTP packet protocols all share this transport boundary.
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.
Result stores general values and assembles player/team rows. Protocol parsers return Result::fetch() or another array<string, mixed>.
Filters run after parsing. The default normalize filter adds cross-game fields; optional filters remove color codes or format duration values. See filters.
-
addServer()validates the definition and constructs the protocol. -
process()divides servers according tomax_servers_per_batch. - Challenge-capable protocols send and apply their initial challenge.
- Each protocol runs
beforeSend()and supplies its initial packets. - HTTP-backed protocols use the configured client; native packet responses are collected through the socket query implementation.
-
getFollowUpPackets()can request more data, bounded bymax_follow_up_rounds. -
processResponse()validates and parses all accumulated responses. - GameQ adds online/address/port/type metadata.
- Filters run and the result keys are sorted.
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-checkbefore finalizing public/protected changes.
Shared protocol changes must be validated against every child fixture, not only the game that motivated the change.
Getting started
Configuration
Guides
Reference
Development
Migration