Passed
Branch develop (34699c)
by Nikita
10:40
created

ServerService::query()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 37
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 5.0291

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 24
c 2
b 0
f 0
dl 0
loc 37
ccs 17
cts 19
cp 0.8947
rs 9.2248
cc 5
nc 6
nop 1
crap 5.0291
1
<?php
2
3
namespace Gameap\Services;
4
5
use Gameap\Exceptions\Services\EmptyCommandException;
6
use Gameap\Exceptions\Services\InvalidCommandException;
7
use Gameap\Exceptions\Services\ServerInactiveException;
8
use Gameap\Models\Server;
9
use GameQ\Exception\Protocol as GameqProtocolException;
10
use GameQ\Exception\Query as GameqQueryException;
11
use GameQ\Exception\Server as GameqServerException;
12
use GameQ\GameQ;
13
use Knik\Gameap\GdaemonCommands;
14
use Storage;
15
16
class ServerService
17
{
18
    public const CONSOLE_MAX_SYMBOLS = 10000;
19
20
    /**
21
     * @var GameQ
22
     */
23
    protected $gameq;
24
25
    /**
26
     * @var GdaemonCommands
27
     */
28
    protected $gdaemonCommands;
29
30
    /**
31
     * @var string
32
     */
33
    protected $storageDisk = 'local';
34
35
    /**
36
     * ServerService constructor.
37
     *
38
     * @param GameQ $gameq
39
     * @param GdaemonCommands $gdaemonCommands
40
     */
41
    public function __construct(GameQ $gameq, GdaemonCommands $gdaemonCommands)
42 3
    {
43
        $this->gameq = $gameq;
44 3
        $this->gdaemonCommands = $gdaemonCommands;
45 3
    }
46 3
47
    /**
48
     * Add default server disk
49
     *
50
     * @param Server $server
51
     */
52
    public function registerDisk(Server $server): void
53
    {
54
        foreach ($server->file_manager_disks as $diskName => $diskConfig) {
55
            if (empty(config("filesystems.disks.{$diskName}"))) {
56
                config(["filesystems.disks.{$diskName}" => $diskConfig]);
57
            }
58
        }
59
    }
60
61
    /**
62
     * @param Server $server
63
     * @return array|string[]
64
     * @throws \Exception
65
     */
66
    public function query(Server $server): array
67 6
    {
68
        $host = "{$server->server_ip}:{$server->query_port}";
69 6
70
        try {
71
            $query = $this->gameq->setOption('timeout', 5)
72 6
                ->addServer([
73 6
                    'type' => $server->game->engine,
74 6
                    'host' => $host,
75 6
                ])
76
                ->process();
77 6
        } catch (GameqServerException | GameqQueryException | GameqProtocolException $exception) {
78
            return [
79
                'status' => 'query not supported for this game',
80
            ];
81
        }
82
83
84
        $serverResult = $query[$host] ?? null;
85 6
86
        if (!empty($serverResult['gq_online'])) {
87 6
            $result = [
88
                'status'   => $serverResult['gq_online'] ? 'online' : 'offline',
89 3
                'hostname' => $serverResult['gq_hostname'],
90 3
                'map'      => $serverResult['gq_mapname'],
91 3
                'players'  => $serverResult['gq_numplayers'] . '/' . $serverResult['gq_maxplayers'],
92 3
                'version'  => $serverResult['version'] ?? null,
93 3
                'password' => $serverResult['gq_password'] ? 'yes' : 'no',
94 3
                'joinlink' => $serverResult['gq_joinlink'],
95 3
            ];
96
        } else {
97
            $result = [
98
                'status' => 'offline',
99 6
            ];
100
        }
101
102
        return $result;
103 6
    }
104
105
    /**
106
     * @param Server $server
107
     * @param string $command
108
     * @param array $extraData
109
     * @return string
110
     */
111
    public function replaceShortCodes(Server $server, string $command, array $extraData = []): string
112 15
    {
113
        foreach ($extraData as $key => $value) {
114 15
            $command = str_replace('{' . $key . '}', $value, $command);
115 9
        }
116
117
        $replaceArray = [
118
            'host'       => $server->server_ip,
119 15
            'port'       => $server->server_port,
120 15
            'query_port' => $server->query_port,
121 15
            'rcon_port'  => $server->rcon_port,
122 15
            'dir'        => $server->full_path,
123 15
            'uuid'       => $server->uuid,
124 15
            'uuid_short' => $server->uuid_short,
125 15
            'game'       => $server->game_id,
126 15
            'user'       => $server->su_user,
127 15
        ];
128
129
        foreach ($replaceArray as $key => $value) {
130 15
            $command = str_replace('{' . $key . '}', $value, $command);
131 15
        }
132
133
        return $command;
134 15
    }
135
136
    /**
137
     * @param Server $server
138
     * @param string $command
139
     * @param array $extraData
140
     * @return string
141
     *
142
     * @throws InvalidCommandException
143
     * @throws EmptyCommandException
144
     */
145
    public function getCommand(Server $server, string $command, array $extraData = []): string
146 15
    {
147
        $property = 'script_' . $command;
148 15
        $attributes = $server->dedicatedServer->getAttributes();
149 15
150
        if (array_key_exists($property, $attributes)) {
151 15
            $script = $server->dedicatedServer->getAttribute($property);
152 12
153
            if (empty($script)) {
154 12
                throw new EmptyCommandException();
155
            }
156
157
            return $this->replaceShortCodes($server, $script, $extraData);
0 ignored issues
show
Bug introduced by
It seems like $script can also be of type boolean; however, parameter $command of Gameap\Services\ServerService::replaceShortCodes() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

157
            return $this->replaceShortCodes($server, /** @scrutinizer ignore-type */ $script, $extraData);
Loading history...
158 12
        }
159
160
        throw new InvalidCommandException();
161 3
    }
162
163
    /**
164
     * @param Server $server
165
     * @return string
166
     * @throws ServerInactiveException|InvalidCommandException
167
     */
168 6
    public function getConsoleLog(Server $server): string
169
    {
170 6
        $this->checkServer($server);
171 3
        $this->configureGdaemon($server);
172
173
        try {
174 3
            $command = $this->getCommand($server, 'get_console');
175 3
            $result = $this->gdaemonCommands->exec($command, $exitCode);
176
        } catch (EmptyCommandException $e) {
177
            $this->registerDisk($server);
178
            $result = Storage::disk('server')->get('output.txt');
179
        }
180
181 3
        if (mb_strlen($result) > self::CONSOLE_MAX_SYMBOLS) {
182
            $result = mb_substr($result, mb_strlen($result) - self::CONSOLE_MAX_SYMBOLS, self::CONSOLE_MAX_SYMBOLS);
183
        }
184
185
        // Fix
186
        // Malformed UTF-8 characters, possibly incorrectly encoded
187 3
        $result = mb_convert_encoding($result, 'UTF-8', 'UTF-8');
188
        
189 3
        return $result;
190
    }
191
192
    /**
193
     * @param Server $server
194
     * @param string $command
195
     * @return bool
196
     *
197 9
     * @throws InvalidCommandException
198
     * @throws ServerInactiveException
199 9
     */
200 6
    public function sendConsoleCommand(Server $server, string $command): bool
201
    {
202
        $this->checkServer($server);
203 6
        $this->configureGdaemon($server);
204 6
205
        try {
206
            $command = $this->getCommand($server, 'send_command', ['command' => $command]);
207
            $this->gdaemonCommands->exec($command, $exitCode);
208
        } catch (EmptyCommandException $e) {
209
            $this->registerDisk($server);
210
            
211
            if (Storage::disk('server')->put('input.txt', $command)) {
212
                // Success
213
                $exitCode = 0;
214
            } else {
215
                // Failure
216
                $exitCode = 1;
217 6
            }
218
        }
219
220
        return $exitCode === 0;
221
    }
222
223
    /**
224
     * Setting up gdaemon commands configuration
225 9
     *
226
     * @param Server $server
227 9
     */
228 9
    private function configureGdaemon(Server $server): void
229
    {
230 9
        $this->gdaemonCommands->setConfig(
231
            $server->dedicatedServer->gdaemonSettings($this->storageDisk)
232
        );
233
    }
234
235
    /**
236 15
     * @param Server $server
237
     * @throws ServerInactiveException
238 15
     */
239 6
    private function checkServer(Server $server): void
240
    {
241 9
        if ($server->processActive() === false) {
242
            throw new ServerInactiveException('Server is down');
243
        }
244
    }
245
}
246