-
-
Notifications
You must be signed in to change notification settings - Fork 1
Adding a Protocol
Home · Architecture · Tests and fixtures
Start by identifying whether the game uses an existing family. A small named subclass is safer than copying a mature parser.
For a Source-compatible game, the class may need only metadata and a port offset:
<?php
namespace GameQ\Protocols;
/**
* Example Game protocol.
*
* @author Your Name <you@example.com>
*/
class Examplegame extends Source
{
protected string $name = 'examplegame';
protected string $name_long = 'Example Game';
protected int $port_diff = 1;
}The filename and class are Examplegame.php / Examplegame: only the first character is capitalized because Server resolves ucfirst(strtolower($type)) on case-sensitive systems.
Confirm protocol compatibility from actual wire data and official documentation where available. Marketing claims such as “uses Steam” do not prove that a server implements A2S or uses a particular port.
A full protocol class normally defines:
-
$name,$name_long,$protocol, and$transport; -
$state, initiallySTATE_BETAuntil representative fixtures exist; -
$port_diffor an overriddenfindQueryPort(); -
$packetsfor initial requests; - a join-link template where a stable URI scheme exists;
- a normalization map;
-
processResponse()with explicit validation; -
beforeSend(), challenge handling, orgetFollowUpPackets()only when required.
Return an empty array when a valid query establishes that the server is unavailable. Throw ProtocolException for malformed, contradictory, truncated, oversized, or unsupported responses. Do not turn malformed data into plausible partial fields silently.
- Check headers, declared lengths, counts, offsets, and terminators before consuming data.
- Use
Bufferfor binary parsing. - Bound response sizes, packet counts, decompression output, pagination, and loops.
- Use
JSON_THROW_ON_ERRORfor JSON. - Use
LIBXML_NONETand internal libxml error handling for untrusted XML. - Restrict cURL protocols, status codes, response sizes, redirects, and timeouts.
- Do not use repeated
array_merge()in parser loops; append values or assemble once. - Preserve ordering where a protocol uses sequence numbers or paginated offsets.
- Avoid suppressing warnings unless the external API cannot be called safely another way and every return value is checked.
A change to Source, GameSpy, Quake, Unreal, Doom 3, Frostbite, RakNet, or another parent affects many games. Before changing shared behavior:
- identify every subclass;
- preserve initial packets and legacy branches that already work;
- add a fixture for the new case;
- run all child protocol tests;
- run
composer bc-checkfor public/protected changes; - document the behavior and compatibility impact.
Do not make a child protocol “correct” by breaking the parent's established children.
Extend JsonHttp when the configured game server exposes a simple, unauthenticated JSON status document over plain HTTP. Define the path, query-port offset, metadata, normalization, and processJson(array $data): array. The base constructs the HTTP request, supports IPv4 and IPv6 host headers, validates the HTTP response, decodes JSON with exceptions, and rejects scalar JSON documents.
Extend OfficialDirectory only when the game project operates a stable official HTTPS JSON directory. Implement directoryUrl(), findServer(), and processResponse(). Matching must include the advertised port and account for IPv4, IPv6, DNS names, and directory_address where appropriate.
Do not use either shared base for authenticated REST APIs, redirects, arbitrary HTTP methods, or multi-request workflows. Those require a purpose-built protocol implementation. See HTTP and directory protocols for the complete contracts and existing implementations.
Retain useful native fields and map common values:
protected array $normalize = [
'general' => [
'hostname' => 'server_name',
'mapname' => 'map',
'numplayers' => 'player_count',
'maxplayers' => 'max_players',
],
'player' => [
'name' => 'name',
'score' => 'score',
],
];Do not invent a universal meaning for a field that the protocol does not provide. Use null/absence rather than misleading zeroes or empty strings.
Every new identifier needs a test class, representative fixtures, malformed-input coverage for a new parser, and an entry in the changelog. Update:
- supported identifiers;
- protocol options for non-obvious ports, tokens, REST/master requirements, or switches;
- the README only when the feature belongs in the project overview.
Run the complete workflow in tests and fixtures.
Getting started
Configuration
Guides
Reference
Development
Migration