Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 50
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
Starbound
0.00% covered (danger)
0.00%
0 / 50
0.00% covered (danger)
0.00%
0 / 5
132
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 query_server
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
56
 query
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
2
 getProtocolName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getVersion
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php declare(strict_types=1);
2
3/**
4 * Clansuite Server Query
5 *
6 * SPDX-FileCopyrightText: 2003-2025 Jens A. Koch
7 * SPDX-License-Identifier: MIT
8 *
9 * For the full copyright and license information, please view
10 * the LICENSE file that was distributed with this source code.
11 */
12
13namespace Clansuite\ServerQuery\ServerProtocols;
14
15use function count;
16use function explode;
17use function trim;
18use Clansuite\Capture\Protocol\ProtocolInterface;
19use Clansuite\Capture\ServerAddress;
20use Clansuite\Capture\ServerInfo;
21use Clansuite\ServerQuery\CSQuery;
22use Override;
23
24/**
25 * Starbound protocol implementation.
26 *
27 * Custom UDP protocol.
28 */
29class Starbound extends CSQuery implements ProtocolInterface
30{
31    /**
32     * Protocol name.
33     */
34    public string $name = 'Starbound';
35
36    /**
37     * List of supported games.
38     *
39     * @var array<string>
40     */
41    public array $supportedGames = ['Starbound'];
42
43    /**
44     * Protocol identifier.
45     */
46    public string $protocol = 'starbound';
47
48    /**
49     * Constructor.
50     */
51    public function __construct(?string $address = null, ?int $queryport = null)
52    {
53        parent::__construct();
54        $this->address   = $address;
55        $this->queryport = $queryport;
56    }
57
58    /**
59     * query_server method.
60     */
61    #[Override]
62    public function query_server(bool $getPlayers = true, bool $getRules = true): bool
63    {
64        if ($this->online) {
65            $this->reset();
66        }
67
68        // Starbound query packet
69        $command = "Starbound\x00query\x00";
70
71        $address = (string) $this->address;
72        $port    = (int) $this->queryport;
73
74        $result = $this->sendCommand($address, $port, $command);
75
76        if ($result === '' || $result === '0' || $result === false) {
77            $this->errstr = 'No reply received';
78
79            return false;
80        }
81
82        // Parse the response
83        // The response is a string like "Starbound <version> <servername> <players>/<maxplayers> <map>"
84        $data  = trim($result);
85        $parts = explode(' ', $data);
86
87        if (count($parts) < 5) {
88            $this->errstr = 'Invalid response format';
89
90            return false;
91        }
92
93        $this->gamename    = 'Starbound';
94        $this->gameversion = $parts[1] ?? '';
95        $this->servertitle = $parts[2] ?? '';
96        $this->mapname     = $parts[4] ?? '';
97
98        $playerPart  = $parts[3] ?? '';
99        $playerParts = explode('/', $playerPart);
100
101        if (count($playerParts) === 2) {
102            $this->numplayers = (int) $playerParts[0];
103            $this->maxplayers = (int) $playerParts[1];
104        }
105
106        // No player list or rules in this simple protocol
107        $this->players = [];
108        $this->rules   = [];
109
110        $this->online = true;
111
112        return true;
113    }
114
115    /**
116     * query method.
117     */
118    #[Override]
119    public function query(ServerAddress $addr): ServerInfo
120    {
121        $this->address   = $addr->ip;
122        $this->queryport = $addr->port;
123        $this->query_server(true, true);
124
125        return new ServerInfo(
126            address: $this->address,
127            queryport: $this->queryport,
128            online: $this->online,
129            gamename: $this->gamename,
130            gameversion: $this->gameversion,
131            servertitle: $this->servertitle,
132            mapname: $this->mapname,
133            gametype: $this->gametype,
134            numplayers: $this->numplayers,
135            maxplayers: $this->maxplayers,
136            rules: $this->rules,
137            players: $this->players,
138            errstr: $this->errstr,
139        );
140    }
141
142    /**
143     * getProtocolName method.
144     */
145    #[Override]
146    public function getProtocolName(): string
147    {
148        return $this->protocol;
149    }
150
151    /**
152     * getVersion method.
153     */
154    #[Override]
155    public function getVersion(ServerInfo $info): string
156    {
157        return $info->gameversion ?? 'unknown';
158    }
159}