1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* This file is part of GameQ. |
4
|
|
|
* |
5
|
|
|
* GameQ is free software; you can redistribute it and/or modify |
6
|
|
|
* it under the terms of the GNU Lesser General Public License as published by |
7
|
|
|
* the Free Software Foundation; either version 3 of the License, or |
8
|
|
|
* (at your option) any later version. |
9
|
|
|
* |
10
|
|
|
* GameQ is distributed in the hope that it will be useful, |
11
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
12
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13
|
|
|
* GNU Lesser General Public License for more details. |
14
|
|
|
* |
15
|
|
|
* You should have received a copy of the GNU Lesser General Public License |
16
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
17
|
|
|
*/ |
18
|
|
|
namespace GameQ; |
19
|
|
|
|
20
|
|
|
use GameQ\Exception\Protocol as ProtocolException; |
21
|
|
|
use GameQ\Exception\Query as QueryException; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Base GameQ Class |
25
|
|
|
* |
26
|
|
|
* This class should be the only one that is included when you use GameQ to query |
27
|
|
|
* any games servers. |
28
|
|
|
* |
29
|
|
|
* Requirements: See wiki or README for more information on the requirements |
30
|
|
|
* - PHP 5.4.14+ |
31
|
|
|
* * Bzip2 - http://www.php.net/manual/en/book.bzip2.php |
32
|
|
|
* |
33
|
|
|
* @author Austin Bischoff <[email protected]> |
34
|
|
|
* |
35
|
|
|
* @property bool $debug |
36
|
|
|
* @property string $capture_packets_file |
37
|
|
|
* @property int $stream_timeout |
38
|
|
|
* @property int $timeout |
39
|
|
|
* @property int $write_wait |
40
|
|
|
*/ |
41
|
|
|
class GameQ |
42
|
|
|
{ |
43
|
|
|
/* |
44
|
|
|
* Constants |
45
|
|
|
*/ |
46
|
|
|
|
47
|
|
|
/* Static Section */ |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Holds the instance of itself |
51
|
|
|
* |
52
|
|
|
* @type self |
53
|
|
|
*/ |
54
|
|
|
protected static $instance = null; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* Create a new instance of this class |
58
|
|
|
* |
59
|
|
|
* @return \GameQ\GameQ |
60
|
|
|
*/ |
61
|
1 |
|
public static function factory() |
62
|
|
|
{ |
63
|
|
|
|
64
|
|
|
// Create a new instance |
65
|
1 |
|
self::$instance = new self(); |
66
|
|
|
|
67
|
|
|
// Return this new instance |
68
|
1 |
|
return self::$instance; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/* Dynamic Section */ |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Default options |
75
|
|
|
* |
76
|
|
|
* @type array |
77
|
|
|
*/ |
78
|
|
|
protected $options = [ |
79
|
|
|
'debug' => false, |
80
|
|
|
'timeout' => 3, // Seconds |
81
|
|
|
'filters' => [ |
82
|
|
|
// Default normalize |
83
|
|
|
'normalize_d751713988987e9331980363e24189ce' => [ |
84
|
|
|
'filter' => 'normalize', |
85
|
|
|
'options' => [], |
86
|
|
|
], |
87
|
|
|
], |
88
|
|
|
// Advanced settings |
89
|
|
|
'stream_timeout' => 200000, // See http://www.php.net/manual/en/function.stream-select.php for more info |
90
|
|
|
'write_wait' => 500, |
91
|
|
|
// How long (in micro-seconds) to pause between writing to server sockets, helps cpu usage |
92
|
|
|
|
93
|
|
|
// Used for generating protocol test data |
94
|
|
|
'capture_packets_file' => null, |
95
|
|
|
]; |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* Array of servers being queried |
99
|
|
|
* |
100
|
|
|
* @type array |
101
|
|
|
*/ |
102
|
|
|
protected $servers = []; |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* The query library to use. Default is Native |
106
|
|
|
* |
107
|
|
|
* @type string |
108
|
|
|
*/ |
109
|
|
|
protected $queryLibrary = 'GameQ\\Query\\Native'; |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* Holds the instance of the queryLibrary |
113
|
|
|
* |
114
|
|
|
* @type \GameQ\Query\Core|null |
115
|
|
|
*/ |
116
|
|
|
protected $query = null; |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* GameQ constructor. |
120
|
|
|
* |
121
|
|
|
* Do some checks as needed so this will operate |
122
|
|
|
*/ |
123
|
223 |
|
public function __construct() |
124
|
|
|
{ |
125
|
|
|
// Check for missing utf8_encode function |
126
|
223 |
|
if (!function_exists('utf8_encode')) { |
127
|
|
|
throw new \Exception("PHP's utf8_encode() function is required - " |
128
|
|
|
. "http://php.net/manual/en/function.utf8-encode.php. Check your php installation."); |
129
|
|
|
} |
130
|
223 |
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Get an option's value |
134
|
|
|
* |
135
|
|
|
* @param mixed $option |
136
|
|
|
* |
137
|
|
|
* @return mixed|null |
138
|
|
|
*/ |
139
|
216 |
|
public function __get($option) |
140
|
|
|
{ |
141
|
|
|
|
142
|
216 |
|
return isset($this->options[$option]) ? $this->options[$option] : null; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* Set an option's value |
147
|
|
|
* |
148
|
|
|
* @param mixed $option |
149
|
|
|
* @param mixed $value |
150
|
|
|
* |
151
|
|
|
* @return bool |
152
|
|
|
*/ |
153
|
218 |
|
public function __set($option, $value) |
154
|
|
|
{ |
155
|
|
|
|
156
|
218 |
|
$this->options[$option] = $value; |
157
|
|
|
|
158
|
218 |
|
return true; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Chainable call to __set, uses set as the actual setter |
163
|
|
|
* |
164
|
|
|
* @param mixed $var |
165
|
|
|
* @param mixed $value |
166
|
|
|
* |
167
|
|
|
* @return $this |
168
|
|
|
*/ |
169
|
218 |
|
public function setOption($var, $value) |
170
|
|
|
{ |
171
|
|
|
|
172
|
|
|
// Use magic |
173
|
218 |
|
$this->{$var} = $value; |
174
|
|
|
|
175
|
218 |
|
return $this; // Make chainable |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* Add a single server |
180
|
|
|
* |
181
|
|
|
* @param array $server_info |
182
|
|
|
* |
183
|
|
|
* @return $this |
184
|
|
|
*/ |
185
|
2 |
|
public function addServer(array $server_info = []) |
186
|
|
|
{ |
187
|
|
|
|
188
|
|
|
// Add and validate the server |
189
|
2 |
|
$this->servers[uniqid()] = new Server($server_info); |
190
|
|
|
|
191
|
2 |
|
return $this; // Make calls chainable |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* Add multiple servers in a single call |
196
|
|
|
* |
197
|
|
|
* @param array $servers |
198
|
|
|
* |
199
|
|
|
* @return $this |
200
|
|
|
*/ |
201
|
2 |
|
public function addServers(array $servers = []) |
202
|
|
|
{ |
203
|
|
|
|
204
|
|
|
// Loop through all the servers and add them |
205
|
2 |
|
foreach ($servers as $server_info) { |
206
|
2 |
|
$this->addServer($server_info); |
207
|
2 |
|
} |
208
|
|
|
|
209
|
2 |
|
return $this; // Make calls chainable |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* Add a set of servers from a file or an array of files. |
214
|
|
|
* Supported formats: |
215
|
|
|
* JSON |
216
|
|
|
* |
217
|
|
|
* @param array $files |
218
|
|
|
* |
219
|
|
|
* @return $this |
220
|
|
|
* @throws \Exception |
221
|
|
|
*/ |
222
|
1 |
|
public function addServersFromFiles($files = []) |
223
|
|
|
{ |
224
|
|
|
|
225
|
|
|
// Since we expect an array let us turn a string (i.e. single file) into an array |
226
|
1 |
|
if (!is_array($files)) { |
227
|
1 |
|
$files = [$files]; |
228
|
1 |
|
} |
229
|
|
|
|
230
|
|
|
// Iterate over the file(s) and add them |
231
|
1 |
|
foreach ($files as $file) { |
232
|
|
|
// Check to make sure the file exists and we can read it |
233
|
1 |
|
if (!file_exists($file) || !is_readable($file)) { |
234
|
1 |
|
continue; |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
// See if this file is JSON |
238
|
1 |
|
if (($servers = json_decode(file_get_contents($file), true)) === null |
239
|
1 |
|
&& json_last_error() !== JSON_ERROR_NONE |
240
|
1 |
|
) { |
241
|
|
|
// Type not supported |
242
|
1 |
|
continue; |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
// Add this list of servers |
246
|
1 |
|
$this->addServers($servers); |
247
|
1 |
|
} |
248
|
|
|
|
249
|
1 |
|
return $this; |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
/** |
253
|
|
|
* Clear all of the defined servers |
254
|
|
|
* |
255
|
|
|
* @return $this |
256
|
|
|
*/ |
257
|
2 |
|
public function clearServers() |
258
|
|
|
{ |
259
|
|
|
|
260
|
|
|
// Reset all the servers |
261
|
2 |
|
$this->servers = []; |
262
|
|
|
|
263
|
2 |
|
return $this; // Make Chainable |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* Add a filter to the processing list |
268
|
|
|
* |
269
|
|
|
* @param string $filterName |
270
|
|
|
* @param array $options |
271
|
|
|
* |
272
|
|
|
* @return $this |
273
|
|
|
*/ |
274
|
3 |
|
public function addFilter($filterName, $options = []) |
275
|
|
|
{ |
276
|
|
|
// Create the filter hash so we can run multiple versions of the same filter |
277
|
3 |
|
$filterHash = sprintf('%s_%s', strtolower($filterName), md5(json_encode($options))); |
278
|
|
|
|
279
|
|
|
// Add the filter |
280
|
3 |
|
$this->options['filters'][$filterHash] = [ |
281
|
3 |
|
'filter' => strtolower($filterName), |
282
|
3 |
|
'options' => $options, |
283
|
|
|
]; |
284
|
|
|
|
285
|
3 |
|
unset($filterHash); |
286
|
|
|
|
287
|
3 |
|
return $this; |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
/** |
291
|
|
|
* Remove an added filter |
292
|
|
|
* |
293
|
|
|
* @param string $filterHash |
294
|
|
|
* |
295
|
|
|
* @return $this |
296
|
|
|
*/ |
297
|
216 |
|
public function removeFilter($filterHash) |
298
|
|
|
{ |
299
|
|
|
// Make lower case |
300
|
216 |
|
$filterHash = strtolower($filterHash); |
301
|
|
|
|
302
|
|
|
// Remove this filter if it has been defined |
303
|
216 |
|
if (array_key_exists($filterHash, $this->options['filters'])) { |
304
|
3 |
|
unset($this->options['filters'][$filterHash]); |
305
|
3 |
|
} |
306
|
|
|
|
307
|
216 |
|
unset($filterHash); |
308
|
|
|
|
309
|
216 |
|
return $this; |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
/** |
313
|
|
|
* Return the list of applied filters |
314
|
|
|
* |
315
|
|
|
* @return array |
316
|
|
|
*/ |
317
|
|
|
public function listFilters() |
318
|
|
|
{ |
319
|
|
|
return $this->options['filters']; |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
/** |
323
|
|
|
* Main method used to actually process all of the added servers and return the information |
324
|
|
|
* |
325
|
|
|
* @return array |
326
|
|
|
* @throws \Exception |
327
|
|
|
*/ |
328
|
|
|
public function process() |
329
|
|
|
{ |
330
|
|
|
|
331
|
|
|
// Initialize the query library we are using |
332
|
|
|
$class = new \ReflectionClass($this->queryLibrary); |
333
|
|
|
|
334
|
|
|
// Set the query pointer to the new instance of the library |
335
|
|
|
$this->query = $class->newInstance(); |
336
|
|
|
|
337
|
|
|
unset($class); |
338
|
|
|
|
339
|
|
|
// Define the return |
340
|
|
|
$results = []; |
341
|
|
|
|
342
|
|
|
// @todo: Add break up into loop to split large arrays into smaller chunks |
343
|
|
|
|
344
|
|
|
// Do server challenge(s) first, if any |
345
|
|
|
$this->doChallenges(); |
346
|
|
|
|
347
|
|
|
// Do packets for server(s) and get query responses |
348
|
|
|
$this->doQueries(); |
349
|
|
|
|
350
|
|
|
// Now we should have some information to process for each server |
351
|
|
|
foreach ($this->servers as $server) { |
352
|
|
|
/* @var $server \GameQ\Server */ |
353
|
|
|
|
354
|
|
|
// Parse the responses for this server |
355
|
|
|
$result = $this->doParseResponse($server); |
356
|
|
|
|
357
|
|
|
// Apply the filters |
358
|
|
|
$result = array_merge($result, $this->doApplyFilters($result, $server)); |
359
|
|
|
|
360
|
|
|
// Sort the keys so they are alphabetical and nicer to look at |
361
|
|
|
ksort($result); |
362
|
|
|
|
363
|
|
|
// Add the result to the results array |
364
|
|
|
$results[$server->id()] = $result; |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
return $results; |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
/** |
371
|
|
|
* Do server challenges, where required |
372
|
|
|
*/ |
373
|
|
|
protected function doChallenges() |
374
|
|
|
{ |
375
|
|
|
|
376
|
|
|
// Initialize the sockets for reading |
377
|
|
|
$sockets = []; |
378
|
|
|
|
379
|
|
|
// By default we don't have any challenges to process |
380
|
|
|
$server_challenge = false; |
381
|
|
|
|
382
|
|
|
// Do challenge packets |
383
|
|
|
foreach ($this->servers as $server_id => $server) { |
384
|
|
|
/* @var $server \GameQ\Server */ |
385
|
|
|
|
386
|
|
|
// This protocol has a challenge packet that needs to be sent |
387
|
|
|
if ($server->protocol()->hasChallenge()) { |
388
|
|
|
// We have a challenge, set the flag |
389
|
|
|
$server_challenge = true; |
390
|
|
|
|
391
|
|
|
// Let's make a clone of the query class |
392
|
|
|
$socket = clone $this->query; |
393
|
|
|
|
394
|
|
|
// Set the information for this query socket |
395
|
|
|
$socket->set( |
396
|
|
|
$server->protocol()->transport(), |
397
|
|
|
$server->ip, |
398
|
|
|
$server->port_query, |
399
|
|
|
$this->timeout |
400
|
|
|
); |
401
|
|
|
|
402
|
|
|
try { |
403
|
|
|
// Now write the challenge packet to the socket. |
404
|
|
|
$socket->write($server->protocol()->getPacket(Protocol::PACKET_CHALLENGE)); |
405
|
|
|
|
406
|
|
|
// Add the socket information so we can reference it easily |
407
|
|
|
$sockets[(int)$socket->get()] = [ |
408
|
|
|
'server_id' => $server_id, |
409
|
|
|
'socket' => $socket, |
410
|
|
|
]; |
411
|
|
|
} catch (QueryException $e) { |
412
|
|
|
// Check to see if we are in debug, if so bubble up the exception |
413
|
|
|
if ($this->debug) { |
414
|
|
|
throw new \Exception($e->getMessage(), $e->getCode(), $e); |
415
|
|
|
} |
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
unset($socket); |
419
|
|
|
|
420
|
|
|
// Let's sleep shortly so we are not hammering out calls rapid fire style hogging cpu |
421
|
|
|
usleep($this->write_wait); |
422
|
|
|
} |
423
|
|
|
} |
424
|
|
|
|
425
|
|
|
// We have at least one server with a challenge, we need to listen for responses |
426
|
|
|
if ($server_challenge) { |
427
|
|
|
// Now we need to listen for and grab challenge response(s) |
428
|
|
|
$responses = call_user_func_array( |
429
|
|
|
[$this->query, 'getResponses'], |
430
|
|
|
[$sockets, $this->timeout, $this->stream_timeout] |
431
|
|
|
); |
432
|
|
|
|
433
|
|
|
// Iterate over the challenge responses |
434
|
|
|
foreach ($responses as $socket_id => $response) { |
435
|
|
|
// Back out the server_id we need to update the challenge response for |
436
|
|
|
$server_id = $sockets[$socket_id]['server_id']; |
437
|
|
|
|
438
|
|
|
// Make this into a buffer so it is easier to manipulate |
439
|
|
|
$challenge = new Buffer(implode('', $response)); |
440
|
|
|
|
441
|
|
|
// Grab the server instance |
442
|
|
|
/* @var $server \GameQ\Server */ |
443
|
|
|
$server = $this->servers[$server_id]; |
444
|
|
|
|
445
|
|
|
// Apply the challenge |
446
|
|
|
$server->protocol()->challengeParseAndApply($challenge); |
447
|
|
|
|
448
|
|
|
// Add this socket to be reused, has to be reused in GameSpy3 for example |
449
|
|
|
$server->socketAdd($sockets[$socket_id]['socket']); |
450
|
|
|
|
451
|
|
|
// Clear |
452
|
|
|
unset($server); |
453
|
|
|
} |
454
|
|
|
} |
455
|
|
|
} |
456
|
|
|
|
457
|
|
|
/** |
458
|
|
|
* Run the actual queries and get the response(s) |
459
|
|
|
*/ |
460
|
|
|
protected function doQueries() |
461
|
|
|
{ |
462
|
|
|
|
463
|
|
|
// Initialize the array of sockets |
464
|
|
|
$sockets = []; |
465
|
|
|
|
466
|
|
|
// Iterate over the server list |
467
|
|
|
foreach ($this->servers as $server_id => $server) { |
468
|
|
|
/* @var $server \GameQ\Server */ |
469
|
|
|
|
470
|
|
|
// Invoke the beforeSend method |
471
|
|
|
$server->protocol()->beforeSend($server); |
472
|
|
|
|
473
|
|
|
// Get all the non-challenge packets we need to send |
474
|
|
|
$packets = $server->protocol()->getPacket('!' . Protocol::PACKET_CHALLENGE); |
475
|
|
|
|
476
|
|
|
if (count($packets) == 0) { |
477
|
|
|
// Skip nothing else to do for some reason. |
478
|
|
|
continue; |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
// Try to use an existing socket |
482
|
|
|
if (($socket = $server->socketGet()) === null) { |
483
|
|
|
// Let's make a clone of the query class |
484
|
|
|
$socket = clone $this->query; |
485
|
|
|
|
486
|
|
|
// Set the information for this query socket |
487
|
|
|
$socket->set( |
488
|
|
|
$server->protocol()->transport(), |
489
|
|
|
$server->ip, |
490
|
|
|
$server->port_query, |
491
|
|
|
$this->timeout |
492
|
|
|
); |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
try { |
496
|
|
|
// Iterate over all the packets we need to send |
497
|
|
|
foreach ($packets as $packet_data) { |
498
|
|
|
// Now write the packet to the socket. |
499
|
|
|
$socket->write($packet_data); |
500
|
|
|
|
501
|
|
|
// Let's sleep shortly so we are not hammering out calls rapid fire style |
502
|
|
|
usleep($this->write_wait); |
503
|
|
|
} |
504
|
|
|
|
505
|
|
|
unset($packets); |
506
|
|
|
|
507
|
|
|
// Add the socket information so we can reference it easily |
508
|
|
|
$sockets[(int)$socket->get()] = [ |
509
|
|
|
'server_id' => $server_id, |
510
|
|
|
'socket' => $socket, |
511
|
|
|
]; |
512
|
|
|
} catch (QueryException $e) { |
513
|
|
|
// Check to see if we are in debug, if so bubble up the exception |
514
|
|
|
if ($this->debug) { |
515
|
|
|
throw new \Exception($e->getMessage(), $e->getCode(), $e); |
516
|
|
|
} |
517
|
|
|
|
518
|
|
|
break; |
519
|
|
|
} |
520
|
|
|
|
521
|
|
|
// Clean up the sockets, if any left over |
522
|
|
|
$server->socketCleanse(); |
523
|
|
|
} |
524
|
|
|
|
525
|
|
|
// Now we need to listen for and grab response(s) |
526
|
|
|
$responses = call_user_func_array( |
527
|
|
|
[$this->query, 'getResponses'], |
528
|
|
|
[$sockets, $this->timeout, $this->stream_timeout] |
529
|
|
|
); |
530
|
|
|
|
531
|
|
|
// Iterate over the responses |
532
|
|
|
foreach ($responses as $socket_id => $response) { |
533
|
|
|
// Back out the server_id |
534
|
|
|
$server_id = $sockets[$socket_id]['server_id']; |
535
|
|
|
|
536
|
|
|
// Grab the server instance |
537
|
|
|
/* @var $server \GameQ\Server */ |
538
|
|
|
$server = $this->servers[$server_id]; |
539
|
|
|
|
540
|
|
|
// Save the response from this packet |
541
|
|
|
$server->protocol()->packetResponse($response); |
542
|
|
|
|
543
|
|
|
unset($server); |
544
|
|
|
} |
545
|
|
|
|
546
|
|
|
// Now we need to close all of the sockets |
547
|
|
|
foreach ($sockets as $socketInfo) { |
548
|
|
|
/* @var $socket \GameQ\Query\Core */ |
549
|
|
|
$socket = $socketInfo['socket']; |
550
|
|
|
|
551
|
|
|
// Close the socket |
552
|
|
|
$socket->close(); |
553
|
|
|
|
554
|
|
|
unset($socket); |
555
|
|
|
} |
556
|
|
|
|
557
|
|
|
unset($sockets); |
558
|
|
|
} |
559
|
|
|
|
560
|
|
|
/** |
561
|
|
|
* Parse the response for a specific server |
562
|
|
|
* |
563
|
|
|
* @param \GameQ\Server $server |
564
|
|
|
* |
565
|
|
|
* @return array |
566
|
|
|
* @throws \Exception |
567
|
|
|
*/ |
568
|
215 |
|
protected function doParseResponse(Server $server) |
|
|
|
|
569
|
|
|
{ |
570
|
|
|
|
571
|
|
|
try { |
572
|
|
|
// @codeCoverageIgnoreStart |
573
|
|
|
// We want to save this server's response to a file (useful for unit testing) |
574
|
|
|
if (!is_null($this->capture_packets_file)) { |
575
|
|
|
file_put_contents( |
576
|
|
|
$this->capture_packets_file, |
577
|
|
|
implode(PHP_EOL . '||' . PHP_EOL, $server->protocol()->packetResponse()) |
578
|
|
|
); |
579
|
|
|
} |
580
|
|
|
// @codeCoverageIgnoreEnd |
581
|
|
|
|
582
|
|
|
// Get the server response |
583
|
215 |
|
$results = $server->protocol()->processResponse(); |
584
|
|
|
|
585
|
|
|
// Check for online before we do anything else |
586
|
182 |
|
$results['gq_online'] = (count($results) > 0); |
587
|
215 |
|
} catch (ProtocolException $e) { |
588
|
|
|
// Check to see if we are in debug, if so bubble up the exception |
589
|
33 |
|
if ($this->debug) { |
590
|
19 |
|
throw new \Exception($e->getMessage(), $e->getCode(), $e); |
591
|
|
|
} |
592
|
|
|
|
593
|
|
|
// We ignore this server |
594
|
|
|
$results = [ |
595
|
14 |
|
'gq_online' => false, |
596
|
14 |
|
]; |
597
|
|
|
} |
598
|
|
|
|
599
|
|
|
// Now add some default stuff |
600
|
196 |
|
$results['gq_address'] = (isset($results['gq_address'])) ? $results['gq_address'] : $server->ip(); |
601
|
196 |
|
$results['gq_port_client'] = $server->portClient(); |
602
|
196 |
|
$results['gq_port_query'] = (isset($results['gq_port_query'])) ? $results['gq_port_query'] : $server->portQuery(); |
603
|
196 |
|
$results['gq_protocol'] = $server->protocol()->getProtocol(); |
604
|
196 |
|
$results['gq_type'] = (string)$server->protocol(); |
605
|
196 |
|
$results['gq_name'] = $server->protocol()->nameLong(); |
606
|
196 |
|
$results['gq_transport'] = $server->protocol()->transport(); |
607
|
|
|
|
608
|
|
|
// Process the join link |
609
|
196 |
|
if (!isset($results['gq_joinlink']) || empty($results['gq_joinlink'])) { |
610
|
196 |
|
$results['gq_joinlink'] = $server->getJoinLink(); |
611
|
196 |
|
} |
612
|
|
|
|
613
|
196 |
|
return $results; |
614
|
|
|
} |
615
|
|
|
|
616
|
|
|
/** |
617
|
|
|
* Apply any filters to the results |
618
|
|
|
* |
619
|
|
|
* @param array $results |
620
|
|
|
* @param \GameQ\Server $server |
621
|
|
|
* |
622
|
|
|
* @return array |
623
|
|
|
*/ |
624
|
2 |
|
protected function doApplyFilters(array $results, Server $server) |
625
|
|
|
{ |
626
|
|
|
|
627
|
|
|
// Loop over the filters |
628
|
2 |
|
foreach ($this->options['filters'] as $filterOptions) { |
629
|
|
|
// Try to do this filter |
630
|
|
|
try { |
631
|
|
|
// Make a new reflection class |
632
|
2 |
|
$class = new \ReflectionClass(sprintf('GameQ\\Filters\\%s', ucfirst($filterOptions['filter']))); |
633
|
|
|
|
634
|
|
|
// Create a new instance of the filter class specified |
635
|
1 |
|
$filter = $class->newInstanceArgs([$filterOptions['options']]); |
636
|
|
|
|
637
|
|
|
// Apply the filter to the data |
638
|
1 |
|
$results = $filter->apply($results, $server); |
639
|
2 |
|
} catch (\ReflectionException $e) { |
640
|
|
|
// Invalid, skip it |
641
|
1 |
|
continue; |
642
|
|
|
} |
643
|
2 |
|
} |
644
|
|
|
|
645
|
2 |
|
return $results; |
646
|
|
|
} |
647
|
|
|
} |
648
|
|
|
|
A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.
You can also find more information in the “Code” section of your repository.