Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 60
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Eco
0.00% covered (danger)
0.00%
0 / 60
0.00% covered (danger)
0.00%
0 / 7
506
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
12
 query
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 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
 query_server
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
30
 queryHTTP
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
90
 stripTags
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 file_get_contents;
16use function is_array;
17use function is_scalar;
18use function json_decode;
19use function preg_replace;
20use function stream_context_create;
21use Clansuite\Capture\Protocol\ProtocolInterface;
22use Clansuite\Capture\ServerAddress;
23use Clansuite\Capture\ServerInfo;
24use Clansuite\ServerQuery\CSQuery;
25use Override;
26
27/**
28 * Eco server protocol implementation.
29 *
30 * Eco uses HTTP API on the web port (usually game port +1).
31 * The /info endpoint returns JSON with server information.
32 *
33 * @see https://eco.gamepedia.com/Server
34 */
35class Eco extends CSQuery implements ProtocolInterface
36{
37    /**
38     * Protocol name.
39     */
40    public string $name = 'Eco';
41
42    /**
43     * Protocol identifier.
44     */
45    public string $protocol = 'eco';
46
47    /**
48     * Constructor.
49     */
50    public function __construct(mixed $address = null, mixed $queryport = null)
51    {
52        parent::__construct();
53        $this->address   = $address !== null ? (string) $address : null;
54        $this->queryport = $queryport !== null ? (int) $queryport : null;
55    }
56
57    /**
58     * query method.
59     */
60    #[Override]
61    public function query(ServerAddress $addr): ServerInfo
62    {
63        $info         = new ServerInfo;
64        $info->online = false;
65
66        if ($this->queryHTTP($addr, $info)) {
67            $info->online = true;
68        }
69
70        return $info;
71    }
72
73    /**
74     * getProtocolName method.
75     */
76    #[Override]
77    public function getProtocolName(): string
78    {
79        return $this->protocol;
80    }
81
82    /**
83     * getVersion method.
84     */
85    #[Override]
86    public function getVersion(ServerInfo $info): string
87    {
88        return $info->gameversion ?? 'unknown';
89    }
90
91    /**
92     * query_server method.
93     */
94    #[Override]
95    public function query_server(bool $getPlayers = true, bool $getRules = true): bool
96    {
97        // Use HTTP query
98        $addr = new ServerAddress($this->address ?? '', $this->queryport ?? 0);
99        $info = $this->query($addr);
100
101        $this->online      = $info->online;
102        $this->servertitle = $info->servertitle ?? '';
103        $this->mapname     = $info->mapname ?? '';
104        $this->numplayers  = $info->numplayers;
105        $this->maxplayers  = $info->maxplayers;
106        $this->gamename    = $info->gamename ?? '';
107        $this->gameversion = $info->gameversion ?? '';
108        $this->gametype    = $info->gametype ?? '';
109
110        if ($getPlayers && $info->players !== []) {
111            $this->players = $info->players;
112        }
113
114        if ($getRules && $info->rules !== []) {
115            $this->rules = $info->rules;
116        }
117
118        return $this->online;
119    }
120
121    private function queryHTTP(ServerAddress $addr, ServerInfo $info): bool
122    {
123        $host = $addr->ip;
124        $port = $addr->port;
125        $url  = "http://{$host}:{$port}/info";
126
127        $context = stream_context_create([
128            'http' => [
129                'timeout' => 5,
130            ],
131        ]);
132
133        $response = @file_get_contents($url, false, $context);
134
135        if ($response === false) {
136            return false;
137        }
138
139        $data = json_decode($response, true);
140
141        if ($data === null || !is_array($data)) {
142            return false;
143        }
144
145        // Parse the JSON data
146        $info->address     = $addr->ip . ':' . $addr->port;
147        $info->queryport   = $addr->port;
148        $info->gamename    = 'Eco';
149        $info->gameversion = (string) ($data['Version'] ?? '');
150        $info->servertitle = $this->stripTags((string) ($data['Description'] ?? ''));
151        $info->mapname     = (string) ($data['WorldSize'] ?? '');
152        $info->gametype    = (string) ($data['Category'] ?? '');
153        $info->numplayers  = (int) ($data['OnlinePlayers'] ?? 0);
154        $info->maxplayers  = (int) ($data['TotalPlayers'] ?? 0);
155
156        // Players
157        $players = [];
158
159        if (isset($data['OnlinePlayersNames']) && is_array($data['OnlinePlayersNames'])) {
160            foreach ($data['OnlinePlayersNames'] as $name) {
161                $players[] = ['name' => (string) $name, 'score' => 0, 'time' => 0];
162            }
163        }
164        $info->players = $players;
165
166        // Rules (server variables)
167        $rules = [];
168
169        foreach ($data as $key => $value) {
170            if (is_scalar($value)) {
171                $rules[(string) $key] = (string) $value;
172            }
173        }
174        $info->rules = $rules;
175
176        return true;
177    }
178
179    private function stripTags(string $html): null|string
180    {
181        // Simple HTML tag stripping
182        return preg_replace('/<[^>]*>/', '', $html);
183    }
184}