Lines 100.00% 31 / 31
Functions and Methods 100.00% 2 / 2
Classes and Traits 100.00% 1 / 1
Name Lines Functions and Methods CRAP Classes and Traits
ServerListClient 100.00% 31 / 31 100.00% 2 / 2 4 100.00% 1 / 1
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 fetch 100.00% 30 / 30 100.00% 1 / 1 3
1<?php
2
3declare(strict_types=1);
4
5namespace Lorekeeper\Client\Login;
6
7use Lorekeeper\Client\Exception\ServerListUnavailableException;
8use Lorekeeper\Server\Server;
9use Psl\Network\Address;
10use Psl\Type;
11use Psl\Type\Exception\CoercionException;
12
13final readonly class ServerListClient
14{
15    /** @param non-empty-string $url */
16    public function __construct(
17        private string $url = 'https://game.aq.com/game/api/data/servers',
18    ) {}
19
20    /**
21     * @throws ServerListUnavailableException
22     *
23     * @return list<Server>
24     */
25    public function fetch(): array
26    {
27        $curl = curl_init($this->url);
28        curl_setopt_array($curl, [
29            CURLOPT_RETURNTRANSFER => true,
30            CURLOPT_FOLLOWLOCATION => true,
31            CURLOPT_TIMEOUT => 10,
32            CURLOPT_CONNECTTIMEOUT => 5,
33            CURLOPT_USERAGENT => 'Mozilla/5.0',
34        ]);
35
36        $response = curl_exec($curl);
37        if (!is_string($response)) {
38            throw new ServerListUnavailableException('Could not reach the server list API: ' . curl_error($curl));
39        }
40
41        try {
42            $servers = Type\json_decoded(Type\vec(Type\shape([
43                'sName' => Type\non_empty_string(),
44                'sIP' => Type\non_empty_string(),
45                'iPort' => Type\int_range(min: 1, max: 65_535),
46                'bOnline' => Type\int(),
47                'bUpg' => Type\int(),
48            ], allowUnknownFields: true)))
49                ->coerce($response);
50        } catch (CoercionException $exception) {
51            throw new ServerListUnavailableException('Unexpected server list response.', previous: $exception);
52        }
53
54        return array_map(
55            static fn(array $server): Server => new Server(
56                $server['sName'],
57                Address::tcp($server['sIP'], $server['iPort']),
58                $server['bOnline'] === 1,
59                $server['bUpg'] === 1,
60            ),
61            $servers,
62        );
63    }
64}