-
-
Notifications
You must be signed in to change notification settings - Fork 1
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
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.
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;
- 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.
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.
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. - Native responses are collected and stored on the protocol.
-
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