Passed
Push — develop ( cc9ef0...90d515 )
by Nikita
06:50
created

ServerService::query()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 37
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 6.0359

Importance

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

158
            return $this->replaceShortCodes($server, /** @scrutinizer ignore-type */ $script, $extraData);
Loading history...
159
        }
160
161 3
        throw new InvalidCommandException();
162
    }
163
164
    /**
165
     * @param Server $server
166
     * @return string
167
     */
168 6
    public function getConsoleLog(Server $server)
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
    public function sendConsoleCommand(Server $server, string $command)
198
    {
199 9
        $this->checkServer($server);
200 6
        $this->configureGdaemon($server);
201
202
        try {
203 6
            $command = $this->getCommand($server, 'send_command', ['command' => $command]);
204 6
            $this->gdaemonCommands->exec($command, $exitCode);
205
        } catch (EmptyCommandException $e) {
206
            $this->registerDisk($server);
207
            
208
            if (Storage::disk('server')->put('input.txt', $command)) {
209
                // Success
210
                $exitCode = 0;
211
            } else {
212
                // Failure
213
                $exitCode = 1;
214
            }
215
        }
216
217 6
        return $exitCode === 0;
218
    }
219
220
    /**
221
     * Setting up gdaemon commands configuration
222
     *
223
     * @param Server $server
224
     */
225 9
    private function configureGdaemon(Server $server)
226
    {
227 9
        $this->gdaemonCommands->setConfig(
228 9
            $server->dedicatedServer->gdaemonSettings($this->storageDisk)
229
        );
230 9
    }
231
232
    /**
233
     * @param Server $server
234
     * @throws ServerInactiveException
235
     */
236 15
    private function checkServer(Server $server)
237
    {
238 15
        if ($server->processActive() === false) {
239 6
            throw new ServerInactiveException('Server is down');
240
        }
241
    }
242
}