Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 56
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
FarmingSimulator
0.00% covered (danger)
0.00%
0 / 56
0.00% covered (danger)
0.00%
0 / 6
342
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 / 14
0.00% covered (danger)
0.00%
0 / 1
30
 queryHTTP
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
42
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 simplexml_load_string;
17use function stream_context_create;
18use Clansuite\Capture\Protocol\ProtocolInterface;
19use Clansuite\Capture\ServerAddress;
20use Clansuite\Capture\ServerInfo;
21use Clansuite\ServerQuery\CSQuery;
22use Override;
23
24/**
25 * Farming Simulator server protocol implementation.
26 *
27 * Farming Simulator uses HTTP API with XML response.
28 * Requires a token from the server settings page.
29 *
30 * @see https://github.com/ich777/farming-simulator-dedicated-server
31 */
32class FarmingSimulator extends CSQuery implements ProtocolInterface
33{
34    /**
35     * Protocol name.
36     */
37    public string $name = 'FarmingSimulator';
38
39    /**
40     * Protocol identifier.
41     */
42    public string $protocol = 'farmingsimulator';
43
44    /**
45     * Constructor.
46     */
47    public function __construct(mixed $address = null, mixed $queryport = null)
48    {
49        parent::__construct();
50        $this->address   = $address !== null ? (string) $address : null;
51        $this->queryport = $queryport !== null ? (int) $queryport : null;
52    }
53
54    /**
55     * query method.
56     */
57    #[Override]
58    public function query(ServerAddress $addr): ServerInfo
59    {
60        $info         = new ServerInfo;
61        $info->online = false;
62
63        if ($this->queryHTTP($addr, $info)) {
64            $info->online = true;
65        }
66
67        return $info;
68    }
69
70    /**
71     * getProtocolName method.
72     */
73    #[Override]
74    public function getProtocolName(): string
75    {
76        return $this->protocol;
77    }
78
79    /**
80     * getVersion method.
81     */
82    #[Override]
83    public function getVersion(ServerInfo $info): string
84    {
85        return $info->gameversion ?? 'unknown';
86    }
87
88    /**
89     * query_server method.
90     */
91    #[Override]
92    public function query_server(bool $getPlayers = true, bool $getRules = true): bool
93    {
94        // Use HTTP query
95        $addr = new ServerAddress($this->address ?? '', $this->queryport ?? 0);
96        $info = $this->query($addr);
97
98        $this->online      = $info->online;
99        $this->servertitle = $info->servertitle ?? '';
100        $this->mapname     = $info->mapname ?? '';
101        $this->numplayers  = $info->numplayers;
102        $this->maxplayers  = $info->maxplayers;
103        $this->gamename    = $info->gamename ?? '';
104        $this->gameversion = $info->gameversion ?? '';
105
106        if ($getPlayers && $info->players !== []) {
107            $this->players = $info->players;
108        }
109
110        if ($getRules && $info->rules !== []) {
111            $this->rules = $info->rules;
112        }
113
114        return $this->online;
115    }
116
117    private function queryHTTP(ServerAddress $addr, ServerInfo $info): bool
118    {
119        $host = $addr->ip;
120        $port = $addr->port;
121        // Note: token is required, but for example, we assume it's provided or use default
122        $token = 'example_token'; // In real use, get from server settings
123        $url   = "http://{$host}:{$port}/feed/dedicated-server-stats.xml?code={$token}";
124
125        $context = stream_context_create([
126            'http' => [
127                'timeout' => 5,
128            ],
129        ]);
130
131        $response = @file_get_contents($url, false, $context);
132
133        if ($response === false) {
134            return false;
135        }
136
137        $xml = simplexml_load_string($response);
138
139        if ($xml === false) {
140            return false;
141        }
142
143        // Parse the XML data
144        $info->address     = $addr->ip . ':' . $addr->port;
145        $info->queryport   = $addr->port;
146        $info->gamename    = 'Farming Simulator';
147        $info->servertitle = (string) $xml->Server['name'];
148        $info->mapname     = (string) $xml->Server['mapName'];
149        $info->numplayers  = (int) $xml->Server->Slots['numUsed'];
150        $info->maxplayers  = (int) $xml->Server->Slots['capacity'];
151
152        // Players
153        $players = [];
154
155        foreach ($xml->Server->Slots->Player as $player) {
156            if ((string) $player['isUsed'] === 'true') {
157                $players[] = ['name' => (string) $player, 'score' => 0, 'time' => (int) $player['uptime']];
158            }
159        }
160        $info->players = $players;
161
162        // Rules (server variables)
163        $rules = [];
164
165        foreach ($xml->Server->attributes() as $key => $value) {
166            $rules[$key] = (string) $value;
167        }
168        $info->rules = $rules;
169
170        return true;
171    }
172}