first commit

This commit is contained in:
Erreur32 2021-02-03 11:36:24 +01:00
parent 27aa22a5a4
commit a0521eff19
274 changed files with 31486 additions and 85 deletions

12
GameQ/.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ["https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=VAU2KADATP5PU"]

4
GameQ/.github/issue_label_bot.yaml vendored Normal file
View File

@ -0,0 +1,4 @@
label-alias:
bug: 'Bug'
feature_request: 'Feature Request'
question: 'Support'

19
GameQ/.github/stale.yml vendored Normal file
View File

@ -0,0 +1,19 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 28
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 14
# Issues with these labels will never be considered stale
exemptLabels:
- Bug
- Feature Request
- Support
# Label to use when marking an issue as stale
staleLabel: Stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

60
GameQ/Autoloader.php Normal file
View File

@ -0,0 +1,60 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
/**
* A simple PSR-4 spec auto loader to allow GameQ to function the same as if it were loaded via Composer
*
* To use this just include this file in your script and the GameQ namespace will be made available
*
* i.e. require_once('/path/to/src/GameQ/Autoloader.php');
*
* See: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader-examples.md
*
* @codeCoverageIgnore
*/
spl_autoload_register(function ($class) {
// project-specific namespace prefix
$prefix = 'GameQ\\';
// base directory for the namespace prefix
$base_dir = __DIR__ . DIRECTORY_SEPARATOR;
// does the class use the namespace prefix?
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
// no, move to the next registered autoloader
return;
}
// get the relative class name
$relative_class = substr($class, $len);
// replace the namespace prefix with the base directory, replace namespace
// separators with directory separators in the relative class name, append
// with .php
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
// if the file exists, require it
if (file_exists($file)) {
require $file;
}
});

501
GameQ/Buffer.php Normal file
View File

@ -0,0 +1,501 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
namespace GameQ;
use GameQ\Exception\Protocol as Exception;
/**
* Class Buffer
*
* Read specific byte sequences from a provided string or Buffer
*
* @package GameQ
*
* @author Austin Bischoff <austin@codebeard.com>
* @author Aidan Lister <aidan@php.net>
* @author Tom Buskens <t.buskens@deviation.nl>
*/
class Buffer
{
/**
* Constants for the byte code types we need to read as
*/
const NUMBER_TYPE_BIGENDIAN = 'be',
NUMBER_TYPE_LITTLEENDIAN = 'le',
NUMBER_TYPE_MACHINE = 'm';
/**
* The number type we use for reading integers. Defaults to little endian
*
* @type string
*/
private $number_type = self::NUMBER_TYPE_LITTLEENDIAN;
/**
* The original data
*
* @type string
*/
private $data;
/**
* The original data
*
* @type int
*/
private $length;
/**
* Position of pointer
*
* @type int
*/
private $index = 0;
/**
* Constructor
*
* @param string $data
* @param string $number_type
*/
public function __construct($data, $number_type = self::NUMBER_TYPE_LITTLEENDIAN)
{
$this->number_type = $number_type;
$this->data = $data;
$this->length = strlen($data);
}
/**
* Return all the data
*
* @return string The data
*/
public function getData()
{
return $this->data;
}
/**
* Return data currently in the buffer
*
* @return string The data currently in the buffer
*/
public function getBuffer()
{
return substr($this->data, $this->index);
}
/**
* Returns the number of bytes in the buffer
*
* @return int Length of the buffer
*/
public function getLength()
{
return max($this->length - $this->index, 0);
}
/**
* Read from the buffer
*
* @param int $length
*
* @return string
* @throws \GameQ\Exception\Protocol
*/
public function read($length = 1)
{
if (($length + $this->index) > $this->length) {
throw new Exception("Unable to read length={$length} from buffer. Bad protocol format or return?");
}
$string = substr($this->data, $this->index, $length);
$this->index += $length;
return $string;
}
/**
* Read the last character from the buffer
*
* Unlike the other read functions, this function actually removes
* the character from the buffer.
*
* @return string
*/
public function readLast()
{
$len = strlen($this->data);
$string = $this->data[strlen($this->data) - 1];
$this->data = substr($this->data, 0, $len - 1);
$this->length -= 1;
return $string;
}
/**
* Look at the buffer, but don't remove
*
* @param int $length
*
* @return string
*/
public function lookAhead($length = 1)
{
return substr($this->data, $this->index, $length);
}
/**
* Skip forward in the buffer
*
* @param int $length
*/
public function skip($length = 1)
{
$this->index += $length;
}
/**
* Jump to a specific position in the buffer,
* will not jump past end of buffer
*
* @param $index
*/
public function jumpto($index)
{
$this->index = min($index, $this->length - 1);
}
/**
* Get the current pointer position
*
* @return int
*/
public function getPosition()
{
return $this->index;
}
/**
* Read from buffer until delimiter is reached
*
* If not found, return everything
*
* @param string $delim
*
* @return string
* @throws \GameQ\Exception\Protocol
*/
public function readString($delim = "\x00")
{
// Get position of delimiter
$len = strpos($this->data, $delim, min($this->index, $this->length));
// If it is not found then return whole buffer
if ($len === false) {
return $this->read(strlen($this->data) - $this->index);
}
// Read the string and remove the delimiter
$string = $this->read($len - $this->index);
++$this->index;
return $string;
}
/**
* Reads a pascal string from the buffer
*
* @param int $offset Number of bits to cut off the end
* @param bool $read_offset True if the data after the offset is to be read
*
* @return string
* @throws \GameQ\Exception\Protocol
*/
public function readPascalString($offset = 0, $read_offset = false)
{
// Get the proper offset
$len = $this->readInt8();
$offset = max($len - $offset, 0);
// Read the data
if ($read_offset) {
return $this->read($offset);
} else {
return substr($this->read($len), 0, $offset);
}
}
/**
* Read from buffer until any of the delimiters is reached
*
* If not found, return everything
*
* @param $delims
* @param null $delimfound
*
* @return string
* @throws \GameQ\Exception\Protocol
*
* @todo: Check to see if this is even used anymore
*/
public function readStringMulti($delims, &$delimfound = null)
{
// Get position of delimiters
$pos = [];
foreach ($delims as $delim) {
if ($p = strpos($this->data, $delim, min($this->index, $this->length))) {
$pos[] = $p;
}
}
// If none are found then return whole buffer
if (empty($pos)) {
return $this->read(strlen($this->data) - $this->index);
}
// Read the string and remove the delimiter
sort($pos);
$string = $this->read($pos[0] - $this->index);
$delimfound = $this->read();
return $string;
}
/**
* Read an 8-bit unsigned integer
*
* @return int
* @throws \GameQ\Exception\Protocol
*/
public function readInt8()
{
$int = unpack('Cint', $this->read(1));
return $int['int'];
}
/**
* Read and 8-bit signed integer
*
* @return int
* @throws \GameQ\Exception\Protocol
*/
public function readInt8Signed()
{
$int = unpack('cint', $this->read(1));
return $int['int'];
}
/**
* Read a 16-bit unsigned integer
*
* @return int
* @throws \GameQ\Exception\Protocol
*/
public function readInt16()
{
// Change the integer type we are looking up
switch ($this->number_type) {
case self::NUMBER_TYPE_BIGENDIAN:
$type = 'nint';
break;
case self::NUMBER_TYPE_LITTLEENDIAN:
$type = 'vint';
break;
default:
$type = 'Sint';
}
$int = unpack($type, $this->read(2));
return $int['int'];
}
/**
* Read a 16-bit signed integer
*
* @return int
* @throws \GameQ\Exception\Protocol
*/
public function readInt16Signed()
{
// Read the data into a string
$string = $this->read(2);
// For big endian we need to reverse the bytes
if ($this->number_type == self::NUMBER_TYPE_BIGENDIAN) {
$string = strrev($string);
}
$int = unpack('sint', $string);
unset($string);
return $int['int'];
}
/**
* Read a 32-bit unsigned integer
*
* @return int
* @throws \GameQ\Exception\Protocol
*/
public function readInt32()
{
// Change the integer type we are looking up
switch ($this->number_type) {
case self::NUMBER_TYPE_BIGENDIAN:
$type = 'Nint';
break;
case self::NUMBER_TYPE_LITTLEENDIAN:
$type = 'Vint';
break;
default:
$type = 'Lint';
}
// Unpack the number
$int = unpack($type, $this->read(4));
return $int['int'];
}
/**
* Read a 32-bit signed integer
*
* @return int
* @throws \GameQ\Exception\Protocol
*/
public function readInt32Signed()
{
// Read the data into a string
$string = $this->read(4);
// For big endian we need to reverse the bytes
if ($this->number_type == self::NUMBER_TYPE_BIGENDIAN) {
$string = strrev($string);
}
$int = unpack('lint', $string);
unset($string);
return $int['int'];
}
/**
* Read a 64-bit unsigned integer
*
* @return int
* @throws \GameQ\Exception\Protocol
*/
public function readInt64()
{
// We have the pack 64-bit codes available. See: http://php.net/manual/en/function.pack.php
if (version_compare(PHP_VERSION, '5.6.3') >= 0 && PHP_INT_SIZE == 8) {
// Change the integer type we are looking up
switch ($this->number_type) {
case self::NUMBER_TYPE_BIGENDIAN:
$type = 'Jint';
break;
case self::NUMBER_TYPE_LITTLEENDIAN:
$type = 'Pint';
break;
default:
$type = 'Qint';
}
$int64 = unpack($type, $this->read(8));
$int = $int64['int'];
unset($int64);
} else {
if ($this->number_type == self::NUMBER_TYPE_BIGENDIAN) {
$high = $this->readInt32();
$low = $this->readInt32();
} else {
$low = $this->readInt32();
$high = $this->readInt32();
}
// We have to determine the number via bitwise
$int = ($high << 32) | $low;
unset($low, $high);
}
return $int;
}
/**
* Read a 32-bit float
*
* @return float
* @throws \GameQ\Exception\Protocol
*/
public function readFloat32()
{
// Read the data into a string
$string = $this->read(4);
// For big endian we need to reverse the bytes
if ($this->number_type == self::NUMBER_TYPE_BIGENDIAN) {
$string = strrev($string);
}
$float = unpack('ffloat', $string);
unset($string);
return $float['float'];
}
}

39
GameQ/CONTRIBUTING.md Normal file
View File

@ -0,0 +1,39 @@
# Contributing
Contributions are **welcome** and will be fully **credited**.
## Pull Requests
- **Document any change in behavior** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
- **Create feature branches** - Don't ask us to pull from your master branch.
- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
## Coding Standard
- The **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** is to be used for all code.
- **[PHPMD](http://phpmd.org/)** is used to help keep the code clean but exceptions can exist.
- Use the following commands to check your code before committing it:
```sh
$ vendor/bin/phpcs src tests --extensions=php --ignore=bootstrap --report=checkstyle --report-file=build/logs/checkstyle.xml --standard=build/config/phpcs.xml -v
$ vendor/bin/phpmd src,tests xml build/config/phpmd.xml
```
## Tests
- **Add tests!** - Your patch won't be accepted if it doesn't have tests.
- Run tests by calling `phpunit`
```sh
$ vendor/bin/phpunit
```
The coding standard, mess detection and tests are validated by [Travis CI](.travis.yml).
# Can't Contribute?
If you do not feel comfortable writing your own changes feel free open up a [new issue](https://github.com/Austinb/GameQ/issues/new) for
to add a game or feature.

View File

@ -0,0 +1,30 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
namespace GameQ\Exception;
/**
* Exception
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class Protocol extends \Exception
{
}

30
GameQ/Exception/Query.php Normal file
View File

@ -0,0 +1,30 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
namespace GameQ\Exception;
/**
* Exception
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class Query extends \Exception
{
}

View File

@ -0,0 +1,30 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
namespace GameQ\Exception;
/**
* Exception
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class Server extends \Exception
{
}

58
GameQ/Filters/Base.php Normal file
View File

@ -0,0 +1,58 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Filters;
use GameQ\Server;
/**
* Abstract base class which all filters must inherit
*
* @author Austin Bischoff <austin@codebeard.com>
*/
abstract class Base
{
/**
* Holds the options for this instance of the filter
*
* @type array
*/
protected $options = [];
/**
* Construct
*
* @param array $options
*/
public function __construct(array $options = [])
{
$this->options = $options;
}
/**
* Apply the filter to the data
*
* @param array $result
* @param \GameQ\Server $server
*
* @return mixed
*/
abstract public function apply(array $result, Server $server);
}

133
GameQ/Filters/Normalize.php Normal file
View File

@ -0,0 +1,133 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Filters;
use GameQ\Server;
/**
* Class Normalize
*
* @package GameQ\Filters
*/
class Normalize extends Base
{
/**
* Holds the protocol specific normalize information
*
* @type array
*/
protected $normalize = [];
/**
* Apply this filter
*
* @param array $result
* @param \GameQ\Server $server
*
* @return array
*/
public function apply(array $result, Server $server)
{
// No result passed so just return
if (empty($result)) {
return $result;
}
//$data = [ ];
//$data['raw'][$server->id()] = $result;
// Grab the normalize for this protocol for the specific server
$this->normalize = $server->protocol()->getNormalize();
// Do general information
$result = array_merge($result, $this->check('general', $result));
// Do player information
if (isset($result['players']) && count($result['players']) > 0) {
// Iterate
foreach ($result['players'] as $key => $player) {
$result['players'][$key] = array_merge($player, $this->check('player', $player));
}
} else {
$result['players'] = [];
}
// Do team information
if (isset($result['teams']) && count($result['teams']) > 0) {
// Iterate
foreach ($result['teams'] as $key => $team) {
$result['teams'][$key] = array_merge($team, $this->check('team', $team));
}
} else {
$result['teams'] = [];
}
//$data['filtered'][$server->id()] = $result;
/*file_put_contents(
sprintf('%s/../../../tests/Filters/Providers/Normalize/%s_1.json', __DIR__, $server->protocol()->getProtocol()),
json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PARTIAL_OUTPUT_ON_ERROR)
);*/
// Return the normalized result
return $result;
}
/**
* Check a section for normalization
*
* @param $section
* @param $data
*
* @return array
*/
protected function check($section, $data)
{
// Normalized return array
$normalized = [];
if (isset($this->normalize[$section]) && !empty($this->normalize[$section])) {
foreach ($this->normalize[$section] as $property => $raw) {
// Default the value for the new key as null
$value = null;
if (is_array($raw)) {
// Iterate over the raw property we want to use
foreach ($raw as $check) {
if (array_key_exists($check, $data)) {
$value = $data[$check];
break;
}
}
} else {
// String
if (array_key_exists($raw, $data)) {
$value = $data[$raw];
}
}
$normalized['gq_' . $property] = $value;
}
}
return $normalized;
}
}

View File

@ -0,0 +1,121 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Filters;
use GameQ\Server;
/**
* Class Secondstohuman
*
* This class converts seconds into a human readable time string 'hh:mm:ss'. This is mainly for converting
* a player's connected time into a readable string. Note that most game servers DO NOT return a player's connected
* time. Source (A2S) based games generally do but not always. This class can also be used to convert other time
* responses into readable time
*
* @package GameQ\Filters
* @author Austin Bischoff <austin@codebeard.com>
*/
class Secondstohuman extends Base
{
/**
* The options key for setting the data key(s) to look for to convert
*/
const OPTION_TIMEKEYS = 'timekeys';
/**
* The result key added when applying this filter to a result
*/
const RESULT_KEY = 'gq_%s_human';
/**
* Holds the default 'time' keys from the response array. This is key is usually 'time' from A2S responses
*
* @var array
*/
protected $timeKeysDefault = ['time'];
/**
* Secondstohuman constructor.
*
* @param array $options
*/
public function __construct(array $options = [])
{
// Check for passed keys
if (!array_key_exists(self::OPTION_TIMEKEYS, $options)) {
// Use default
$options[self::OPTION_TIMEKEYS] = $this->timeKeysDefault;
} else {
// Used passed key(s) and make sure it is an array
$options[self::OPTION_TIMEKEYS] = (!is_array($options[self::OPTION_TIMEKEYS])) ?
[$options[self::OPTION_TIMEKEYS]] : $options[self::OPTION_TIMEKEYS];
}
parent::__construct($options);
}
/**
* Apply this filter to the result data
*
* @param array $result
* @param Server $server
*
* @return array
*/
public function apply(array $result, Server $server)
{
// Send the results off to be iterated and return the updated result
return $this->iterate($result);
}
/**
* Home grown iterate function. Would like to replace this with an internal PHP method(s) but could not find a way
* to make the iterate classes add new keys to the response. They all seemed to be read-only.
*
* @todo: See if there is a more internal way of handling this instead of foreach looping and recursive calling
*
* @param array $result
*
* @return array
*/
protected function iterate(array &$result)
{
// Iterate over the results
foreach ($result as $key => $value) {
// Offload to itself if we have another array
if (is_array($value)) {
// Iterate and update the result
$result[$key] = $this->iterate($value);
} elseif (in_array($key, $this->options[self::OPTION_TIMEKEYS])) {
// Make sure the value is a float (throws E_WARNING in PHP 7.1+)
$value = floatval($value);
// We match one of the keys we are wanting to convert so add it and move on
$result[sprintf(self::RESULT_KEY, $key)] = sprintf(
"%02d:%02d:%02d",
floor($value / 3600),
($value / 60) % 60,
$value % 60
);
}
}
return $result;
}
}

View File

@ -0,0 +1,115 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Filters;
use GameQ\Server;
/**
* Class Strip Colors
*
* Strip color codes from UT and Quake based games
*
* @package GameQ\Filters
*/
class Stripcolors extends Base
{
/**
* Apply this filter
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @param array $result
* @param \GameQ\Server $server
*
* @return array
*/
public function apply(array $result, Server $server)
{
// No result passed so just return
if (empty($result)) {
return $result;
}
//$data = [];
//$data['raw'][ $server->id() ] = $result;
// Switch based on the base (not game) protocol
switch ($server->protocol()->getProtocol()) {
case 'quake2':
case 'quake3':
case 'doom3':
array_walk_recursive($result, [$this, 'stripQuake']);
break;
case 'unreal2':
case 'ut3':
case 'gamespy3': //not sure if gamespy3 supports ut colors but won't hurt
case 'gamespy2':
array_walk_recursive($result, [$this, 'stripUnreal']);
break;
case 'source':
array_walk_recursive($result, [$this, 'stripSource']);
break;
}
/*$data['filtered'][ $server->id() ] = $result;
file_put_contents(
sprintf(
'%s/../../../tests/Filters/Providers/Stripcolors\%s_1.json',
__DIR__,
$server->protocol()->getProtocol()
),
json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PARTIAL_OUTPUT_ON_ERROR)
);*/
// Return the stripped result
return $result;
}
/**
* Strip color codes from quake based games
*
* @param string $string
*/
protected function stripQuake(&$string)
{
$string = preg_replace('#(\^.)#', '', $string);
}
/**
* Strip color codes from Unreal based games
*
* @param string $string
*/
protected function stripUnreal(&$string)
{
$string = preg_replace('/\x1b.../', '', $string);
}
/**
* Strip color codes from Source based games
*
* @param string $string
*/
protected function stripSource(&$string)
{
$string = strip_tags($string);
}
}

47
GameQ/Filters/Test.php Normal file
View File

@ -0,0 +1,47 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Filters;
use GameQ\Server;
/**
* Class Test
*
* This is a test filter to be used for testing purposes only.
*
* @package GameQ\Filters
*/
class Test extends Base
{
/**
* Apply the filter. For this we just return whatever is sent
*
* @SuppressWarnings(PHPMD)
*
* @param array $result
* @param \GameQ\Server $server
*
* @return array
*/
public function apply(array $result, Server $server)
{
return $result;
}
}

649
GameQ/GameQ.php Normal file
View File

@ -0,0 +1,649 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ;
use GameQ\Exception\Protocol as ProtocolException;
use GameQ\Exception\Query as QueryException;
/**
* Base GameQ Class
*
* This class should be the only one that is included when you use GameQ to query
* any games servers.
*
* Requirements: See wiki or README for more information on the requirements
* - PHP 5.4.14+
* * Bzip2 - http://www.php.net/manual/en/book.bzip2.php
*
* @author Austin Bischoff <austin@codebeard.com>
*
* @property bool $debug
* @property string $capture_packets_file
* @property int $stream_timeout
* @property int $timeout
* @property int $write_wait
*/
class GameQ
{
/*
* Constants
*/
const PROTOCOLS_DIRECTORY = __DIR__ . '/Protocols';
/* Static Section */
/**
* Holds the instance of itself
*
* @type self
*/
protected static $instance = null;
/**
* Create a new instance of this class
*
* @return \GameQ\GameQ
*/
public static function factory()
{
// Create a new instance
self::$instance = new self();
// Return this new instance
return self::$instance;
}
/* Dynamic Section */
/**
* Default options
*
* @type array
*/
protected $options = [
'debug' => false,
'timeout' => 3, // Seconds
'filters' => [
// Default normalize
'normalize_d751713988987e9331980363e24189ce' => [
'filter' => 'normalize',
'options' => [],
],
],
// Advanced settings
'stream_timeout' => 200000, // See http://www.php.net/manual/en/function.stream-select.php for more info
'write_wait' => 500,
// How long (in micro-seconds) to pause between writing to server sockets, helps cpu usage
// Used for generating protocol test data
'capture_packets_file' => null,
];
/**
* Array of servers being queried
*
* @type array
*/
protected $servers = [];
/**
* The query library to use. Default is Native
*
* @type string
*/
protected $queryLibrary = 'GameQ\\Query\\Native';
/**
* Holds the instance of the queryLibrary
*
* @type \GameQ\Query\Core|null
*/
protected $query = null;
/**
* GameQ constructor.
*
* Do some checks as needed so this will operate
*/
public function __construct()
{
// Check for missing utf8_encode function
if (!function_exists('utf8_encode')) {
throw new \Exception("PHP's utf8_encode() function is required - "
. "http://php.net/manual/en/function.utf8-encode.php. Check your php installation.");
}
}
/**
* Get an option's value
*
* @param mixed $option
*
* @return mixed|null
*/
public function __get($option)
{
return isset($this->options[$option]) ? $this->options[$option] : null;
}
/**
* Set an option's value
*
* @param mixed $option
* @param mixed $value
*
* @return bool
*/
public function __set($option, $value)
{
$this->options[$option] = $value;
return true;
}
/**
* Chainable call to __set, uses set as the actual setter
*
* @param mixed $var
* @param mixed $value
*
* @return $this
*/
public function setOption($var, $value)
{
// Use magic
$this->{$var} = $value;
return $this; // Make chainable
}
/**
* Add a single server
*
* @param array $server_info
*
* @return $this
*/
public function addServer(array $server_info = [])
{
// Add and validate the server
$this->servers[uniqid()] = new Server($server_info);
return $this; // Make calls chainable
}
/**
* Add multiple servers in a single call
*
* @param array $servers
*
* @return $this
*/
public function addServers(array $servers = [])
{
// Loop through all the servers and add them
foreach ($servers as $server_info) {
$this->addServer($server_info);
}
return $this; // Make calls chainable
}
/**
* Add a set of servers from a file or an array of files.
* Supported formats:
* JSON
*
* @param array $files
*
* @return $this
* @throws \Exception
*/
public function addServersFromFiles($files = [])
{
// Since we expect an array let us turn a string (i.e. single file) into an array
if (!is_array($files)) {
$files = [$files];
}
// Iterate over the file(s) and add them
foreach ($files as $file) {
// Check to make sure the file exists and we can read it
if (!file_exists($file) || !is_readable($file)) {
continue;
}
// See if this file is JSON
if (($servers = json_decode(file_get_contents($file), true)) === null
&& json_last_error() !== JSON_ERROR_NONE
) {
// Type not supported
continue;
}
// Add this list of servers
$this->addServers($servers);
}
return $this;
}
/**
* Clear all of the defined servers
*
* @return $this
*/
public function clearServers()
{
// Reset all the servers
$this->servers = [];
return $this; // Make Chainable
}
/**
* Add a filter to the processing list
*
* @param string $filterName
* @param array $options
*
* @return $this
*/
public function addFilter($filterName, $options = [])
{
// Create the filter hash so we can run multiple versions of the same filter
$filterHash = sprintf('%s_%s', strtolower($filterName), md5(json_encode($options)));
// Add the filter
$this->options['filters'][$filterHash] = [
'filter' => strtolower($filterName),
'options' => $options,
];
unset($filterHash);
return $this;
}
/**
* Remove an added filter
*
* @param string $filterHash
*
* @return $this
*/
public function removeFilter($filterHash)
{
// Make lower case
$filterHash = strtolower($filterHash);
// Remove this filter if it has been defined
if (array_key_exists($filterHash, $this->options['filters'])) {
unset($this->options['filters'][$filterHash]);
}
unset($filterHash);
return $this;
}
/**
* Return the list of applied filters
*
* @return array
*/
public function listFilters()
{
return $this->options['filters'];
}
/**
* Main method used to actually process all of the added servers and return the information
*
* @return array
* @throws \Exception
*/
public function process()
{
// Initialize the query library we are using
$class = new \ReflectionClass($this->queryLibrary);
// Set the query pointer to the new instance of the library
$this->query = $class->newInstance();
unset($class);
// Define the return
$results = [];
// @todo: Add break up into loop to split large arrays into smaller chunks
// Do server challenge(s) first, if any
$this->doChallenges();
// Do packets for server(s) and get query responses
$this->doQueries();
// Now we should have some information to process for each server
foreach ($this->servers as $server) {
/* @var $server \GameQ\Server */
// Parse the responses for this server
$result = $this->doParseResponse($server);
// Apply the filters
$result = array_merge($result, $this->doApplyFilters($result, $server));
// Sort the keys so they are alphabetical and nicer to look at
ksort($result);
// Add the result to the results array
$results[$server->id()] = $result;
}
return $results;
}
/**
* Do server challenges, where required
*/
protected function doChallenges()
{
// Initialize the sockets for reading
$sockets = [];
// By default we don't have any challenges to process
$server_challenge = false;
// Do challenge packets
foreach ($this->servers as $server_id => $server) {
/* @var $server \GameQ\Server */
// This protocol has a challenge packet that needs to be sent
if ($server->protocol()->hasChallenge()) {
// We have a challenge, set the flag
$server_challenge = true;
// Let's make a clone of the query class
$socket = clone $this->query;
// Set the information for this query socket
$socket->set(
$server->protocol()->transport(),
$server->ip,
$server->port_query,
$this->timeout
);
try {
// Now write the challenge packet to the socket.
$socket->write($server->protocol()->getPacket(Protocol::PACKET_CHALLENGE));
// Add the socket information so we can reference it easily
$sockets[(int)$socket->get()] = [
'server_id' => $server_id,
'socket' => $socket,
];
} catch (QueryException $e) {
// Check to see if we are in debug, if so bubble up the exception
if ($this->debug) {
throw new \Exception($e->getMessage(), $e->getCode(), $e);
}
}
unset($socket);
// Let's sleep shortly so we are not hammering out calls rapid fire style hogging cpu
usleep($this->write_wait);
}
}
// We have at least one server with a challenge, we need to listen for responses
if ($server_challenge) {
// Now we need to listen for and grab challenge response(s)
$responses = call_user_func_array(
[$this->query, 'getResponses'],
[$sockets, $this->timeout, $this->stream_timeout]
);
// Iterate over the challenge responses
foreach ($responses as $socket_id => $response) {
// Back out the server_id we need to update the challenge response for
$server_id = $sockets[$socket_id]['server_id'];
// Make this into a buffer so it is easier to manipulate
$challenge = new Buffer(implode('', $response));
// Grab the server instance
/* @var $server \GameQ\Server */
$server = $this->servers[$server_id];
// Apply the challenge
$server->protocol()->challengeParseAndApply($challenge);
// Add this socket to be reused, has to be reused in GameSpy3 for example
$server->socketAdd($sockets[$socket_id]['socket']);
// Clear
unset($server);
}
}
}
/**
* Run the actual queries and get the response(s)
*/
protected function doQueries()
{
// Initialize the array of sockets
$sockets = [];
// Iterate over the server list
foreach ($this->servers as $server_id => $server) {
/* @var $server \GameQ\Server */
// Invoke the beforeSend method
$server->protocol()->beforeSend($server);
// Get all the non-challenge packets we need to send
$packets = $server->protocol()->getPacket('!' . Protocol::PACKET_CHALLENGE);
if (count($packets) == 0) {
// Skip nothing else to do for some reason.
continue;
}
// Try to use an existing socket
if (($socket = $server->socketGet()) === null) {
// Let's make a clone of the query class
$socket = clone $this->query;
// Set the information for this query socket
$socket->set(
$server->protocol()->transport(),
$server->ip,
$server->port_query,
$this->timeout
);
}
try {
// Iterate over all the packets we need to send
foreach ($packets as $packet_data) {
// Now write the packet to the socket.
$socket->write($packet_data);
// Let's sleep shortly so we are not hammering out calls rapid fire style
usleep($this->write_wait);
}
unset($packets);
// Add the socket information so we can reference it easily
$sockets[(int)$socket->get()] = [
'server_id' => $server_id,
'socket' => $socket,
];
} catch (QueryException $e) {
// Check to see if we are in debug, if so bubble up the exception
if ($this->debug) {
throw new \Exception($e->getMessage(), $e->getCode(), $e);
}
break;
}
// Clean up the sockets, if any left over
$server->socketCleanse();
}
// Now we need to listen for and grab response(s)
$responses = call_user_func_array(
[$this->query, 'getResponses'],
[$sockets, $this->timeout, $this->stream_timeout]
);
// Iterate over the responses
foreach ($responses as $socket_id => $response) {
// Back out the server_id
$server_id = $sockets[$socket_id]['server_id'];
// Grab the server instance
/* @var $server \GameQ\Server */
$server = $this->servers[$server_id];
// Save the response from this packet
$server->protocol()->packetResponse($response);
unset($server);
}
// Now we need to close all of the sockets
foreach ($sockets as $socketInfo) {
/* @var $socket \GameQ\Query\Core */
$socket = $socketInfo['socket'];
// Close the socket
$socket->close();
unset($socket);
}
unset($sockets);
}
/**
* Parse the response for a specific server
*
* @param \GameQ\Server $server
*
* @return array
* @throws \Exception
*/
protected function doParseResponse(Server $server)
{
try {
// @codeCoverageIgnoreStart
// We want to save this server's response to a file (useful for unit testing)
if (!is_null($this->capture_packets_file)) {
file_put_contents(
$this->capture_packets_file,
implode(PHP_EOL . '||' . PHP_EOL, $server->protocol()->packetResponse())
);
}
// @codeCoverageIgnoreEnd
// Get the server response
$results = $server->protocol()->processResponse();
// Check for online before we do anything else
$results['gq_online'] = (count($results) > 0);
} catch (ProtocolException $e) {
// Check to see if we are in debug, if so bubble up the exception
if ($this->debug) {
throw new \Exception($e->getMessage(), $e->getCode(), $e);
}
// We ignore this server
$results = [
'gq_online' => false,
];
}
// Now add some default stuff
$results['gq_address'] = (isset($results['gq_address'])) ? $results['gq_address'] : $server->ip();
$results['gq_port_client'] = $server->portClient();
$results['gq_port_query'] = (isset($results['gq_port_query'])) ? $results['gq_port_query'] : $server->portQuery();
$results['gq_protocol'] = $server->protocol()->getProtocol();
$results['gq_type'] = (string)$server->protocol();
$results['gq_name'] = $server->protocol()->nameLong();
$results['gq_transport'] = $server->protocol()->transport();
// Process the join link
if (!isset($results['gq_joinlink']) || empty($results['gq_joinlink'])) {
$results['gq_joinlink'] = $server->getJoinLink();
}
return $results;
}
/**
* Apply any filters to the results
*
* @param array $results
* @param \GameQ\Server $server
*
* @return array
*/
protected function doApplyFilters(array $results, Server $server)
{
// Loop over the filters
foreach ($this->options['filters'] as $filterOptions) {
// Try to do this filter
try {
// Make a new reflection class
$class = new \ReflectionClass(sprintf('GameQ\\Filters\\%s', ucfirst($filterOptions['filter'])));
// Create a new instance of the filter class specified
$filter = $class->newInstanceArgs([$filterOptions['options']]);
// Apply the filter to the data
$results = $filter->apply($results, $server);
} catch (\ReflectionException $e) {
// Invalid, skip it
continue;
}
}
return $results;
}
}

165
GameQ/LICENSE.lgpl Normal file
View File

@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

500
GameQ/Protocol.php Normal file
View File

@ -0,0 +1,500 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
namespace GameQ;
/**
* Handles the core functionality for the protocols
*
* @SuppressWarnings(PHPMD.NumberOfChildren)
*
* @author Austin Bischoff <austin@codebeard.com>
*/
abstract class Protocol
{
/**
* Constants for class states
*/
const STATE_TESTING = 1;
const STATE_BETA = 2;
const STATE_STABLE = 3;
const STATE_DEPRECATED = 4;
/**
* Constants for packet keys
*/
const PACKET_ALL = 'all'; // Some protocols allow all data to be sent back in one call.
const PACKET_BASIC = 'basic';
const PACKET_CHALLENGE = 'challenge';
const PACKET_CHANNELS = 'channels'; // Voice servers
const PACKET_DETAILS = 'details';
const PACKET_INFO = 'info';
const PACKET_PLAYERS = 'players';
const PACKET_STATUS = 'status';
const PACKET_RULES = 'rules';
const PACKET_VERSION = 'version';
/**
* Transport constants
*/
const TRANSPORT_UDP = 'udp';
const TRANSPORT_TCP = 'tcp';
const TRANSPORT_SSL = 'ssl';
const TRANSPORT_TLS = 'tls';
/**
* Short name of the protocol
*
* @type string
*/
protected $name = 'unknown';
/**
* The longer, fancier name for the protocol
*
* @type string
*/
protected $name_long = 'unknown';
/**
* The difference between the client port and query port
*
* @type int
*/
protected $port_diff = 0;
/**
* The transport method to use to actually send the data
* Default is UDP
*
* @type string
*/
protected $transport = self::TRANSPORT_UDP;
/**
* The protocol type used when querying the server
*
* @type string
*/
protected $protocol = 'unknown';
/**
* Holds the valid packet types this protocol has available.
*
* @type array
*/
protected $packets = [];
/**
* Holds the response headers and the method to use to process them.
*
* @type array
*/
protected $responses = [];
/**
* Holds the list of methods to run when parsing the packet response(s) data. These
* methods should provide all the return information.
*
* @type array
*/
protected $process_methods = [];
/**
* The packet responses received
*
* @type array
*/
protected $packets_response = [];
/**
* Holds the instance of the result class
*
* @type null
*/
protected $result = null;
/**
* Options for this protocol
*
* @type array
*/
protected $options = [];
/**
* Define the state of this class
*
* @type int
*/
protected $state = self::STATE_STABLE;
/**
* Holds specific normalize settings
*
* @todo: Remove this ugly bulk by moving specific ones to their specific game(s)
*
* @type array
*/
protected $normalize = [
// General
'general' => [
// target => source
'dedicated' => [
'listenserver',
'dedic',
'bf2dedicated',
'netserverdedicated',
'bf2142dedicated',
'dedicated',
],
'gametype' => ['ggametype', 'sigametype', 'matchtype'],
'hostname' => ['svhostname', 'servername', 'siname', 'name'],
'mapname' => ['map', 'simap'],
'maxplayers' => ['svmaxclients', 'simaxplayers', 'maxclients', 'max_players'],
'mod' => ['game', 'gamedir', 'gamevariant'],
'numplayers' => ['clients', 'sinumplayers', 'num_players'],
'password' => ['protected', 'siusepass', 'sineedpass', 'pswrd', 'gneedpass', 'auth', 'passsord'],
],
// Indvidual
'player' => [
'name' => ['nick', 'player', 'playername', 'name'],
'kills' => ['kills'],
'deaths' => ['deaths'],
'score' => ['kills', 'frags', 'skill', 'score'],
'ping' => ['ping'],
],
// Team
'team' => [
'name' => ['name', 'teamname', 'team_t'],
'score' => ['score', 'score_t'],
],
];
/**
* Quick join link
*
* @type string
*/
protected $join_link = '';
/**
* @param array $options
*/
public function __construct(array $options = [])
{
// Set the options for this specific instance of the class
$this->options = $options;
}
/**
* String name of this class
*
* @return string
*/
public function __toString()
{
return $this->name;
}
/**
* Get the port difference between the server's client (game) and query ports
*
* @return int
*/
public function portDiff()
{
return $this->port_diff;
}
/**
* "Find" the query port based off of the client port and port_diff
*
* This method is meant to be overloaded for more complex maths or lookup tables
*
* @param int $clientPort
*
* @return int
*/
public function findQueryPort($clientPort)
{
return $clientPort + $this->port_diff;
}
/**
* Return the join_link as defined by the protocol class
*
* @return string
*/
public function joinLink()
{
return $this->join_link;
}
/**
* Short (callable) name of this class
*
* @return string
*/
public function name()
{
return $this->name;
}
/**
* Long name of this class
*
* @return string
*/
public function nameLong()
{
return $this->name_long;
}
/**
* Return the status of this Protocol Class
*
* @return int
*/
public function state()
{
return $this->state;
}
/**
* Return the protocol property
*
* @return string
*/
public function getProtocol()
{
return $this->protocol;
}
/**
* Get/set the transport type for this protocol
*
* @param string|null $type
*
* @return string
*/
public function transport($type = null)
{
// Act as setter
if (!is_null($type)) {
$this->transport = $type;
}
return $this->transport;
}
/**
* Set the options for the protocol call
*
* @param array $options
*
* @return array
*/
public function options($options = [])
{
// Act as setter
if (!empty($options)) {
$this->options = $options;
}
return $this->options;
}
/*
* Packet Section
*/
/**
* Return specific packet(s)
*
* @param array $type
*
* @return array
*/
public function getPacket($type = [])
{
$packets = [];
// We want an array of packets back
if (is_array($type) && !empty($type)) {
// Loop the packets
foreach ($this->packets as $packet_type => $packet_data) {
// We want this packet
if (in_array($packet_type, $type)) {
$packets[$packet_type] = $packet_data;
}
}
} elseif ($type == '!challenge') {
// Loop the packets
foreach ($this->packets as $packet_type => $packet_data) {
// Dont want challenge packets
if ($packet_type != self::PACKET_CHALLENGE) {
$packets[$packet_type] = $packet_data;
}
}
} elseif (is_string($type)) {
// Return specific packet type
$packets = $this->packets[$type];
} else {
// Return all packets
$packets = $this->packets;
}
// Return the packets
return $packets;
}
/**
* Get/set the packet response
*
* @param array|null $response
*
* @return array
*/
public function packetResponse(array $response = null)
{
// Act as setter
if (!empty($response)) {
$this->packets_response = $response;
}
return $this->packets_response;
}
/*
* Challenge section
*/
/**
* Determine whether or not this protocol has a challenge needed before querying
*
* @return bool
*/
public function hasChallenge()
{
return (isset($this->packets[self::PACKET_CHALLENGE]) && !empty($this->packets[self::PACKET_CHALLENGE]));
}
/**
* Parse the challenge response and add it to the buffer items that need it.
* This should be overloaded by extending class
*
* @codeCoverageIgnore
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param \GameQ\Buffer $challenge_buffer
*
* @return bool
*/
public function challengeParseAndApply(Buffer $challenge_buffer)
{
return true;
}
/**
* Apply the challenge string to all the packets that need it.
*
* @param string $challenge_string
*
* @return bool
*/
protected function challengeApply($challenge_string)
{
// Let's loop through all the packets and append the challenge where it is needed
foreach ($this->packets as $packet_type => $packet) {
$this->packets[$packet_type] = sprintf($packet, $challenge_string);
}
return true;
}
/**
* Get the normalize settings for the protocol
*
* @return array
*/
public function getNormalize()
{
return $this->normalize;
}
/*
* General
*/
/**
* Generic method to allow protocol classes to do work right before the query is sent
*
* @codeCoverageIgnore
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param \GameQ\Server $server
*/
public function beforeSend(Server $server)
{
}
/**
* Method called to process query response data. Each extending class has to have one of these functions.
*
* @return mixed
*/
abstract public function processResponse();
}

53
GameQ/Protocols/Aa3.php Normal file
View File

@ -0,0 +1,53 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Aa3
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Aa3 extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'aa3';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "America's Army 3";
/**
* Query port = client_port + 18243
*
* client_port default 8777
* query_port default 27020
*
* @type int
*/
protected $port_diff = 18243;
}

42
GameQ/Protocols/Aapg.php Normal file
View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Aapg
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Aapg extends Aa3
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'aapg';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "America's Army: Proving Grounds";
}

51
GameQ/Protocols/Arkse.php Normal file
View File

@ -0,0 +1,51 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class ARK: Survival Evolved
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Arkse extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'arkse';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "ARK: Survival Evolved";
/**
* query_port = client_port + 19238
* 27015 = 7777 + 19238
*
* @type int
*/
protected $port_diff = 19238;
}

43
GameQ/Protocols/Arma.php Normal file
View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Arma
*
* @package GameQ\Protocols
*
* @author Wilson Jesus <>
*/
class Arma extends Gamespy2
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'arma';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "ArmA Armed Assault";
}

181
GameQ/Protocols/Arma3.php Normal file
View File

@ -0,0 +1,181 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Buffer;
use GameQ\Result;
/**
* Class Armed Assault 3
*
* Rules protocol reference: https://community.bistudio.com/wiki/Arma_3_ServerBrowserProtocol2
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
* @author Memphis017 <https://github.com/Memphis017>
*/
class Arma3 extends Source
{
/**
* Defines the names for the specific game DLCs
*
* @var array
*/
protected $dlcNames = [
'af82811b' => 'Karts',
'94f76a1a' => 'Marksmen',
'd0356eec' => 'Helicopters',
'19984a71' => 'Zeus',
'7fb4b1f3' => 'Apex',
'49c2c12b' => 'Jets',
'7e766e18' => 'Laws of War',
'99d71f90' => 'Malden',
'a8b10cdf' => 'Tac-Ops',
'37680ce8' => 'Tanks',
'43f9c377' => 'Contact',
'c4979557' => 'Enoch',
];
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'arma3';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Arma3";
/**
* Query port = client_port + 1
*
* @type int
*/
protected $port_diff = 1;
/**
* Process the rules since Arma3 changed their response for rules
*
* @param Buffer $buffer
*
* @return array
* @throws \GameQ\Exception\Protocol
*/
protected function processRules(Buffer $buffer)
{
// Total number of packets, burn it
$buffer->readInt16();
// Will hold the data string
$data = '';
// Loop until we run out of strings
while ($buffer->getLength()) {
// Burn the delimiters (i.e. \x01\x04\x00)
$buffer->readString();
// Add the data to the string, we are reassembling it
$data .= $buffer->readString();
}
// Restore escaped sequences
$data = str_replace(["\x01\x01", "\x01\x02", "\x01\x03"], ["\x01", "\x00", "\xFF"], $data);
// Make a new buffer with the reassembled data
$responseBuffer = new Buffer($data);
// Kill the old buffer, should be empty
unset($buffer, $data);
// Set the result to a new result instance
$result = new Result();
// Get results
$result->add('rules_protocol_version', $responseBuffer->readInt8());
$result->add('overflow', $responseBuffer->readInt8());
$dlcBit = decbin($responseBuffer->readInt8()); // Grab DLC bit 1 and use it later
$dlcBit2 = decbin($responseBuffer->readInt8()); // Grab DLC bit 2 and use it later
$dlcCount = substr_count($dlcBit, '1') + substr_count($dlcBit2, '1'); // Count the DLCs
// Grab difficulty so we can man handle it...
$difficulty = $responseBuffer->readInt8();
// Process difficulty
$result->add('3rd_person', $difficulty >> 7);
$result->add('advanced_flight_mode', ($difficulty >> 6) & 1);
$result->add('difficulty_ai', ($difficulty >> 3) & 3);
$result->add('difficulty_level', $difficulty & 3);
unset($difficulty);
// Crosshair
$result->add('crosshair', $responseBuffer->readInt8());
// Loop over the DLC bit so we can pull in the info for the DLC (if enabled)
for ($x = 0; $x < $dlcCount; $x++) {
$dlcHash = dechex($responseBuffer->readInt32());
isset($this->dlcNames[$dlcHash]) ?
$result->addSub('dlcs', 'name', $this->dlcNames[$dlcHash])
: $result->addSub('dlcs', 'name', 'Unknown');
$result->addSub('dlcs', 'hash', $dlcHash);
}
// No longer needed
unset($dlcBit, $dlcBit2, $dlcCount, $dlcHash);
// Grab the mod count
$modCount = $responseBuffer->readInt8();
// Add mod count
$result->add('mod_count', $modCount);
// Loop the mod count and add them
for ($x = 0; $x < $modCount; $x++) {
// Add the mod to the list
$result->addSub('mods', 'hash', dechex($responseBuffer->readInt32()));
$result->addSub('mods', 'steam_id', hexdec($responseBuffer->readPascalString(0, true)));
$result->addSub('mods', 'name', $responseBuffer->readPascalString(0, true));
}
unset($modCount, $x);
// Get the signatures count
$signatureCount = $responseBuffer->readInt8();
$result->add('signature_count', $signatureCount);
// Make signatures array
$signatures = [];
// Loop until we run out of signatures
for ($x = 0; $x < $signatureCount; $x++) {
$signatures[] = $responseBuffer->readPascalString(0, true);
}
// Add as a simple array
$result->add('signatures', $signatures);
unset($responseBuffer, $signatureCount, $signatures, $x);
return $result->fetch();
}
}

View File

@ -0,0 +1,50 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Armedassault2oa
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Armedassault2oa extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = "armedassault2oa";
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Armed Assault 2: Operation Arrowhead";
/**
* Query port = client_port + 1
*
* @type int
*/
protected $port_diff = 1;
}

View File

@ -0,0 +1,32 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Armed assault 3 dummy Protocol Class
*
* Added for backward compatibility, please update to class arma3
*
* @deprecated v3.0.10
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Armedassault3 extends Arma3
{
}

208
GameQ/Protocols/Ase.php Normal file
View File

@ -0,0 +1,208 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Protocol;
use GameQ\Buffer;
use GameQ\Result;
/**
* All-Seeing Eye Protocol class
*
* @author Marcel Bößendörfer <m.boessendoerfer@marbis.net>
* @author Austin Bischoff <austin@codebeard.com>
*/
class Ase extends Protocol
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @type array
*/
protected $packets = [
self::PACKET_ALL => "s",
];
/**
* The query protocol used to make the call
*
* @type string
*/
protected $protocol = 'ase';
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'ase';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "All-Seeing Eye";
/**
* The client join link
*
* @type string
*/
protected $join_link = null;
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
// General
'general' => [
// target => source
'dedicated' => 'dedicated',
'gametype' => 'gametype',
'hostname' => 'servername',
'mapname' => 'map',
'maxplayers' => 'max_players',
'mod' => 'game_dir',
'numplayers' => 'num_players',
'password' => 'password',
],
// Individual
'player' => [
'name' => 'name',
'score' => 'score',
'team' => 'team',
'ping' => 'ping',
'time' => 'time',
],
];
/**
* Process the response
*
* @return array
* @throws \GameQ\Exception\Protocol
*/
public function processResponse()
{
// Create a new buffer
$buffer = new Buffer(implode('', $this->packets_response));
// Burn the header
$buffer->skip(4);
// Create a new result
$result = new Result();
// Variables
$result->add('gamename', $buffer->readPascalString(1, true));
$result->add('port', $buffer->readPascalString(1, true));
$result->add('servername', $buffer->readPascalString(1, true));
$result->add('gametype', $buffer->readPascalString(1, true));
$result->add('map', $buffer->readPascalString(1, true));
$result->add('version', $buffer->readPascalString(1, true));
$result->add('password', $buffer->readPascalString(1, true));
$result->add('num_players', $buffer->readPascalString(1, true));
$result->add('max_players', $buffer->readPascalString(1, true));
$result->add('dedicated', 1);
// Offload the key/value pair processing
$this->processKeyValuePairs($buffer, $result);
// Offload processing player and team info
$this->processPlayersAndTeams($buffer, $result);
unset($buffer);
return $result->fetch();
}
/*
* Internal methods
*/
/**
* Handles processing the extra key/value pairs for server settings
*
* @param \GameQ\Buffer $buffer
* @param \GameQ\Result $result
*/
protected function processKeyValuePairs(Buffer &$buffer, Result &$result)
{
// Key / value pairs
while ($buffer->getLength()) {
$key = $buffer->readPascalString(1, true);
// If we have an empty key, we've reached the end
if (empty($key)) {
break;
}
// Otherwise, add the pair
$result->add(
$key,
$buffer->readPascalString(1, true)
);
}
unset($key);
}
/**
* Handles processing the player and team data into a usable format
*
* @param \GameQ\Buffer $buffer
* @param \GameQ\Result $result
*/
protected function processPlayersAndTeams(Buffer &$buffer, Result &$result)
{
// Players and team info
while ($buffer->getLength()) {
// Get the flags
$flags = $buffer->readInt8();
// Get data according to the flags
if ($flags & 1) {
$result->addPlayer('name', $buffer->readPascalString(1, true));
}
if ($flags & 2) {
$result->addPlayer('team', $buffer->readPascalString(1, true));
}
if ($flags & 4) {
$result->addPlayer('skin', $buffer->readPascalString(1, true));
}
if ($flags & 8) {
$result->addPlayer('score', $buffer->readPascalString(1, true));
}
if ($flags & 16) {
$result->addPlayer('ping', $buffer->readPascalString(1, true));
}
if ($flags & 32) {
$result->addPlayer('time', $buffer->readPascalString(1, true));
}
}
}
}

55
GameQ/Protocols/Atlas.php Normal file
View File

@ -0,0 +1,55 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Atlas
*
* @package GameQ\Protocols
* @author Wilson Jesus <>
*/
class Atlas extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'atlas';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Atlas";
/**
* query_port = client_port + 51800
* 57561 = 5761 + 51800
*
* this is the default value for the stock game server, both ports
* can be independently changed from the stock ones,
* making the port_diff logic useless.
*
* @type int
*/
protected $port_diff = 51800;
}

View File

@ -0,0 +1,68 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Battalion 1944
*
* @package GameQ\Protocols
* @author TacTicToe66 <https://github.com/TacTicToe66>
*/
class Batt1944 extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'batt1944';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Battalion 1944";
/**
* query_port = client_port + 3
*
* @type int
*/
protected $port_diff = 3;
/**
* Normalize main fields
*
* @var array
*/
protected $normalize = [
// General
'general' => [
// target => source
'gametype' => 'bat_gamemode_s',
'hostname' => 'bat_name_s',
'mapname' => 'bat_map_s',
'maxplayers' => 'bat_max_players_i',
'numplayers' => 'bat_player_count_s',
'password' => 'bat_has_password_s',
],
];
}

View File

@ -0,0 +1,88 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Battlefield 1942
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Bf1942 extends Gamespy
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'bf1942';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Battlefield 1942";
/**
* query_port = client_port + 8433
* 23000 = 14567 + 8433
*
* @type int
*/
protected $port_diff = 8433;
/**
* The client join link
*
* @type string
*/
protected $join_link = "bf1942://%s:%d";
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
// General
'general' => [
// target => source
'dedicated' => 'dedicated',
'gametype' => 'gametype',
'hostname' => 'hostname',
'mapname' => 'mapname',
'maxplayers' => 'maxplayers',
'numplayers' => 'numplayers',
'password' => 'password',
],
// Individual
'player' => [
'name' => 'playername',
'kills' => 'kills',
'deaths' => 'deaths',
'ping' => 'ping',
'score' => 'score',
],
'team' => [
'name' => 'teamname',
],
];
}

98
GameQ/Protocols/Bf2.php Normal file
View File

@ -0,0 +1,98 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Battlefield 2
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Bf2 extends Gamespy3
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'bf2';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Battlefield 2";
/**
* query_port = client_port + 8433
* 29900 = 16567 + 13333
*
* @type int
*/
protected $port_diff = 13333;
/**
* The client join link
*
* @type string
*/
protected $join_link = "bf2://%s:%d";
/**
* BF2 has a different query packet to send than "normal" Gamespy 3
*
* @var array
*/
protected $packets = [
self::PACKET_ALL => "\xFE\xFD\x00\x10\x20\x30\x40\xFF\xFF\xFF\x01",
];
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
// General
'general' => [
// target => source
'dedicated' => 'dedicated',
'gametype' => 'gametype',
'hostname' => 'hostname',
'mapname' => 'mapname',
'maxplayers' => 'maxplayers',
'numplayers' => 'numplayers',
'password' => 'password',
],
// Individual
'player' => [
'name' => 'player',
'kills' => 'score',
'deaths' => 'deaths',
'ping' => 'ping',
'score' => 'score',
],
'team' => [
'name' => 'team',
'score' => 'score',
],
];
}

348
GameQ/Protocols/Bf3.php Normal file
View File

@ -0,0 +1,348 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Protocol;
use GameQ\Buffer;
use GameQ\Result;
use GameQ\Exception\Protocol as Exception;
/**
* Battlefield 3 Protocol Class
*
* Good place for doc status and info is http://www.fpsadmin.com/forum/showthread.php?t=24134
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Bf3 extends Protocol
{
/**
* Array of packets we want to query.
*
* @type array
*/
protected $packets = [
self::PACKET_STATUS => "\x00\x00\x00\x21\x1b\x00\x00\x00\x01\x00\x00\x00\x0a\x00\x00\x00serverInfo\x00",
self::PACKET_VERSION => "\x00\x00\x00\x22\x18\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00version\x00",
self::PACKET_PLAYERS =>
"\x00\x00\x00\x23\x24\x00\x00\x00\x02\x00\x00\x00\x0b\x00\x00\x00listPlayers\x00\x03\x00\x00\x00\x61ll\x00",
];
/**
* Use the response flag to figure out what method to run
*
* @type array
*/
protected $responses = [
1627389952 => "processDetails", // a
1644167168 => "processVersion", // b
1660944384 => "processPlayers", // c
];
/**
* The transport mode for this protocol is TCP
*
* @type string
*/
protected $transport = self::TRANSPORT_TCP;
/**
* The query protocol used to make the call
*
* @type string
*/
protected $protocol = 'bf3';
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'bf3';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Battlefield 3";
/**
* The client join link
*
* @type string
*/
protected $join_link = null;
/**
* query_port = client_port + 22000
* 47200 = 25200 + 22000
*
* @type int
*/
protected $port_diff = 22000;
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
// General
'general' => [
// target => source
'dedicated' => 'dedicated',
'hostname' => 'hostname',
'mapname' => 'map',
'maxplayers' => 'max_players',
'numplayers' => 'num_players',
'password' => 'password',
],
'player' => [
'name' => 'name',
'score' => 'score',
'ping' => 'ping',
],
'team' => [
'score' => 'tickets',
],
];
/**
* Process the response for the StarMade server
*
* @return array
* @throws \GameQ\Exception\Protocol
*/
public function processResponse()
{
// Holds the results sent back
$results = [];
// Holds the processed packets after having been reassembled
$processed = [];
// Start up the index for the processed
$sequence_id_last = 0;
foreach ($this->packets_response as $packet) {
// Create a new buffer
$buffer = new Buffer($packet);
// Each "good" packet begins with sequence_id (32-bit)
$sequence_id = $buffer->readInt32();
// Sequence id is a response
if (array_key_exists($sequence_id, $this->responses)) {
$processed[$sequence_id] = $buffer->getBuffer();
$sequence_id_last = $sequence_id;
} else {
// This is a continuation of the previous packet, reset the buffer and append
$buffer->jumpto(0);
// Append
$processed[$sequence_id_last] .= $buffer->getBuffer();
}
}
unset($buffer, $sequence_id_last, $sequence_id);
// Iterate over the combined packets and do some work
foreach ($processed as $sequence_id => $data) {
// Create a new buffer
$buffer = new Buffer($data);
// Get the length of the packet
$packetLength = $buffer->getLength();
// Check to make sure the expected length matches the real length
// Subtract 4 for the sequence_id pulled out earlier
if ($packetLength != ($buffer->readInt32() - 4)) {
throw new Exception(__METHOD__ . " packet length does not match expected length!");
}
// Now we need to call the proper method
$results = array_merge(
$results,
call_user_func_array([$this, $this->responses[$sequence_id]], [$buffer])
);
}
return $results;
}
/*
* Internal Methods
*/
/**
* Decode the buffer into a usable format
*
* @param \GameQ\Buffer $buffer
*
* @return array
*/
protected function decode(Buffer $buffer)
{
$items = [];
// Get the number of words in this buffer
$itemCount = $buffer->readInt32();
// Loop over the number of items
for ($i = 0; $i < $itemCount; $i++) {
// Length of the string
$buffer->readInt32();
// Just read the string
$items[$i] = $buffer->readString();
}
return $items;
}
/**
* Process the server details
*
* @param \GameQ\Buffer $buffer
*
* @return array
*/
protected function processDetails(Buffer $buffer)
{
// Decode into items
$items = $this->decode($buffer);
// Set the result to a new result instance
$result = new Result();
// Server is always dedicated
$result->add('dedicated', 1);
// These are the same no matter what mode the server is in
$result->add('hostname', $items[1]);
$result->add('num_players', (int)$items[2]);
$result->add('max_players', (int)$items[3]);
$result->add('gametype', $items[4]);
$result->add('map', $items[5]);
$result->add('roundsplayed', (int)$items[6]);
$result->add('roundstotal', (int)$items[7]);
$result->add('num_teams', (int)$items[8]);
// Set the current index
$index_current = 9;
// Pull the team count
$teamCount = $result->get('num_teams');
// Loop for the number of teams found, increment along the way
for ($id = 1; $id <= $teamCount; $id++, $index_current++) {
// Shows the tickets
$result->addTeam('tickets', $items[$index_current]);
// We add an id so we know which team this is
$result->addTeam('id', $id);
}
// Get and set the rest of the data points.
$result->add('targetscore', (int)$items[$index_current]);
$result->add('online', 1); // Forced true, it seems $words[$index_current + 1] is always empty
$result->add('ranked', (int)$items[$index_current + 2]);
$result->add('punkbuster', (int)$items[$index_current + 3]);
$result->add('password', (int)$items[$index_current + 4]);
$result->add('uptime', (int)$items[$index_current + 5]);
$result->add('roundtime', (int)$items[$index_current + 6]);
// Added in R9
$result->add('ip_port', $items[$index_current + 7]);
$result->add('punkbuster_version', $items[$index_current + 8]);
$result->add('join_queue', (int)$items[$index_current + 9]);
$result->add('region', $items[$index_current + 10]);
$result->add('pingsite', $items[$index_current + 11]);
$result->add('country', $items[$index_current + 12]);
// Added in R29, No docs as of yet
$result->add('quickmatch', (int)$items[$index_current + 13]); // Guessed from research
unset($items, $index_current, $teamCount, $buffer);
return $result->fetch();
}
/**
* Process the server version
*
* @param \GameQ\Buffer $buffer
*
* @return array
*/
protected function processVersion(Buffer $buffer)
{
// Decode into items
$items = $this->decode($buffer);
// Set the result to a new result instance
$result = new Result();
$result->add('version', $items[2]);
unset($buffer, $items);
return $result->fetch();
}
/**
* Process the players
*
* @param \GameQ\Buffer $buffer
*
* @return array
*/
protected function processPlayers(Buffer $buffer)
{
// Decode into items
$items = $this->decode($buffer);
// Set the result to a new result instance
$result = new Result();
// Number of data points per player
$numTags = $items[1];
// Grab the tags for each player
$tags = array_slice($items, 2, $numTags);
// Get the player count
$playerCount = $items[$numTags + 2];
// Iterate over the index until we run out of players
for ($i = 0, $x = $numTags + 3; $i < $playerCount; $i++, $x += $numTags) {
// Loop over the player tags and extract the info for that tag
foreach ($tags as $index => $tag) {
$result->addPlayer($tag, $items[($x + $index)]);
}
}
return $result->fetch();
}
}

114
GameQ/Protocols/Bf4.php Normal file
View File

@ -0,0 +1,114 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Buffer;
use GameQ\Result;
/**
* Battlefield 4 Protocol class
*
* Good place for doc status and info is http://battlelog.battlefield.com/bf4/forum/view/2955064768683911198/
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Bf4 extends Bf3
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'bf4';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Battlefield 4";
/**
* Handle processing details since they are different than BF3
*
* @param \GameQ\Buffer $buffer
*
* @return array
*/
protected function processDetails(Buffer $buffer)
{
// Decode into items
$items = $this->decode($buffer);
// Set the result to a new result instance
$result = new Result();
// Server is always dedicated
$result->add('dedicated', 1);
// These are the same no matter what mode the server is in
$result->add('hostname', $items[1]);
$result->add('num_players', (int) $items[2]);
$result->add('max_players', (int) $items[3]);
$result->add('gametype', $items[4]);
$result->add('map', $items[5]);
$result->add('roundsplayed', (int) $items[6]);
$result->add('roundstotal', (int) $items[7]);
$result->add('num_teams', (int) $items[8]);
// Set the current index
$index_current = 9;
// Pull the team count
$teamCount = $result->get('num_teams');
// Loop for the number of teams found, increment along the way
for ($id = 1; $id <= $teamCount; $id++, $index_current++) {
// Shows the tickets
$result->addTeam('tickets', $items[$index_current]);
// We add an id so we know which team this is
$result->addTeam('id', $id);
}
// Get and set the rest of the data points.
$result->add('targetscore', (int) $items[$index_current]);
$result->add('online', 1); // Forced true, it seems $words[$index_current + 1] is always empty
$result->add('ranked', (int) $items[$index_current + 2]);
$result->add('punkbuster', (int) $items[$index_current + 3]);
$result->add('password', (int) $items[$index_current + 4]);
$result->add('uptime', (int) $items[$index_current + 5]);
$result->add('roundtime', (int) $items[$index_current + 6]);
$result->add('ip_port', $items[$index_current + 7]);
$result->add('punkbuster_version', $items[$index_current + 8]);
$result->add('join_queue', (int) $items[$index_current + 9]);
$result->add('region', $items[$index_current + 10]);
$result->add('pingsite', $items[$index_current + 11]);
$result->add('country', $items[$index_current + 12]);
//$result->add('quickmatch', (int) $items[$index_current + 13]); Supposed to be here according to R42 but is not
$result->add('blaze_player_count', (int) $items[$index_current + 13]);
$result->add('blaze_game_state', (int) $items[$index_current + 14]);
unset($items, $index_current, $teamCount, $buffer);
return $result->fetch();
}
}

326
GameQ/Protocols/Bfbc2.php Normal file
View File

@ -0,0 +1,326 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Protocol;
use GameQ\Buffer;
use GameQ\Result;
use GameQ\Exception\Protocol as Exception;
/**
* Battlefield Bad Company 2 Protocol Class
*
* NOTE: There are no qualifiers to the response packets sent back from the server as to which response packet
* belongs to which query request. For now this class assumes the responses are in the same order as the order in
* which the packets were sent to the server. If this assumption turns out to be wrong there is easy way to tell which
* response belongs to which query. Hopefully this assumption will hold true as it has in my testing.
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Bfbc2 extends Protocol
{
/**
* Array of packets we want to query.
*
* @type array
*/
protected $packets = [
self::PACKET_VERSION => "\x00\x00\x00\x00\x18\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00version\x00",
self::PACKET_STATUS => "\x00\x00\x00\x00\x1b\x00\x00\x00\x01\x00\x00\x00\x0a\x00\x00\x00serverInfo\x00",
self::PACKET_PLAYERS => "\x00\x00\x00\x00\x24\x00\x00\x00\x02\x00\x00\x00\x0b\x00\x00\x00listPlayers\x00\x03\x00\x00\x00\x61ll\x00",
];
/**
* Use the response flag to figure out what method to run
*
* @type array
*/
protected $responses = [
"processVersion",
"processDetails",
"processPlayers",
];
/**
* The transport mode for this protocol is TCP
*
* @type string
*/
protected $transport = self::TRANSPORT_TCP;
/**
* The query protocol used to make the call
*
* @type string
*/
protected $protocol = 'bfbc2';
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'bfbc2';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Battlefield Bad Company 2";
/**
* The client join link
*
* @type string
*/
protected $join_link = null;
/**
* query_port = client_port + 29321
* 48888 = 19567 + 29321
*
* @type int
*/
protected $port_diff = 29321;
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
// General
'general' => [
// target => source
'dedicated' => 'dedicated',
'hostname' => 'hostname',
'mapname' => 'map',
'maxplayers' => 'max_players',
'numplayers' => 'num_players',
'password' => 'password',
],
'player' => [
'name' => 'name',
'score' => 'score',
'ping' => 'ping',
],
'team' => [
'score' => 'tickets',
],
];
/**
* Process the response for the StarMade server
*
* @return array
* @throws \GameQ\Exception\Protocol
*/
public function processResponse()
{
//print_r($this->packets_response);
// Holds the results sent back
$results = [];
// Iterate over the response packets
// @todo: This protocol has no packet ordering, ids or anyway to identify which packet coming back belongs to which initial call.
foreach ($this->packets_response as $i => $packet) {
// Create a new buffer
$buffer = new Buffer($packet);
// Burn first 4 bytes, same across all packets
$buffer->skip(4);
// Get the packet length
$packetLength = $buffer->getLength();
// Check to make sure the expected length matches the real length
// Subtract 4 for the header burn
if ($packetLength != ($buffer->readInt32() - 4)) {
throw new Exception(__METHOD__ . " packet length does not match expected length!");
}
// We assume the packets are coming back in the same order as sent, this maybe incorrect...
$results = array_merge(
$results,
call_user_func_array([$this, $this->responses[$i]], [$buffer])
);
}
unset($buffer, $packetLength);
return $results;
}
/*
* Internal Methods
*/
/**
* Decode the buffer into a usable format
*
* @param \GameQ\Buffer $buffer
*
* @return array
*/
protected function decode(Buffer $buffer)
{
$items = [];
// Get the number of words in this buffer
$itemCount = $buffer->readInt32();
// Loop over the number of items
for ($i = 0; $i < $itemCount; $i++) {
// Length of the string
$buffer->readInt32();
// Just read the string
$items[$i] = $buffer->readString();
}
return $items;
}
/**
* Process the server details
*
* @param \GameQ\Buffer $buffer
*
* @return array
*/
protected function processDetails(Buffer $buffer)
{
// Decode into items
$items = $this->decode($buffer);
// Set the result to a new result instance
$result = new Result();
// Server is always dedicated
$result->add('dedicated', 1);
// These are the same no matter what mode the server is in
$result->add('hostname', $items[1]);
$result->add('num_players', (int)$items[2]);
$result->add('max_players', (int)$items[3]);
$result->add('gametype', $items[4]);
$result->add('map', $items[5]);
$result->add('roundsplayed', (int)$items[6]);
$result->add('roundstotal', (int)$items[7]);
$result->add('num_teams', (int)$items[8]);
// Set the current index
$index_current = 9;
// Pull the team count
$teamCount = $result->get('num_teams');
// Loop for the number of teams found, increment along the way
for ($id = 1; $id <= $teamCount; $id++, $index_current++) {
// Shows the tickets
$result->addTeam('tickets', $items[$index_current]);
// We add an id so we know which team this is
$result->addTeam('id', $id);
}
// Get and set the rest of the data points.
$result->add('targetscore', (int)$items[$index_current]);
$result->add('online', 1); // Forced true, shows accepting players
$result->add('ranked', (($items[$index_current + 2] == 'true') ? 1 : 0));
$result->add('punkbuster', (($items[$index_current + 3] == 'true') ? 1 : 0));
$result->add('password', (($items[$index_current + 4] == 'true') ? 1 : 0));
$result->add('uptime', (int)$items[$index_current + 5]);
$result->add('roundtime', (int)$items[$index_current + 6]);
$result->add('mod', $items[$index_current + 7]);
$result->add('ip_port', $items[$index_current + 9]);
$result->add('punkbuster_version', $items[$index_current + 10]);
$result->add('join_queue', (($items[$index_current + 11] == 'true') ? 1 : 0));
$result->add('region', $items[$index_current + 12]);
unset($items, $index_current, $teamCount, $buffer);
return $result->fetch();
}
/**
* Process the server version
*
* @param \GameQ\Buffer $buffer
*
* @return array
*/
protected function processVersion(Buffer $buffer)
{
// Decode into items
$items = $this->decode($buffer);
// Set the result to a new result instance
$result = new Result();
$result->add('version', $items[2]);
unset($buffer, $items);
return $result->fetch();
}
/**
* Process the players
*
* @param \GameQ\Buffer $buffer
*
* @return array
*/
protected function processPlayers(Buffer $buffer)
{
// Decode into items
$items = $this->decode($buffer);
// Set the result to a new result instance
$result = new Result();
// Number of data points per player
$numTags = $items[1];
// Grab the tags for each player
$tags = array_slice($items, 2, $numTags);
// Get the player count
$playerCount = $items[$numTags + 2];
// Iterate over the index until we run out of players
for ($i = 0, $x = $numTags + 3; $i < $playerCount; $i++, $x += $numTags) {
// Loop over the player tags and extract the info for that tag
foreach ($tags as $index => $tag) {
$result->addPlayer($tag, $items[($x + $index)]);
}
}
return $result->fetch();
}
}

43
GameQ/Protocols/Bfh.php Normal file
View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Battlefield Hardline Protocol class
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Bfh extends Bf4
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'bfh';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Battlefield Hardline";
}

50
GameQ/Protocols/Brink.php Normal file
View File

@ -0,0 +1,50 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Brink
*
* @package GameQ\Protocols
*
* @author Wilson Jesus <>
*/
class Brink extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'brink';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Brink";
/**
* query_port = client_port + 1
*
* @type int
*/
protected $port_diff = 1;
}

43
GameQ/Protocols/Cod.php Normal file
View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Call of Duty Protocol Class
*
* @package GameQ\Protocols
*
* @author Wilson Jesus <>
*/
class Cod extends Quake3
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'cod';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Call of Duty";
}

42
GameQ/Protocols/Cod2.php Normal file
View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Call of Duty 2 Protocol Class
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Cod2 extends Quake3
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'cod2';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Call of Duty 2";
}

42
GameQ/Protocols/Cod4.php Normal file
View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Call of Duty 4 Protocol Class
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Cod4 extends Quake3
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'cod4';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Call of Duty 4";
}

View File

@ -0,0 +1,50 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Codmw3
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Codmw3 extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'codmw3';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Call of Duty: Modern Warfare 3";
/**
* query_port = client_port + 2
*
* @type int
*/
protected $port_diff = 2;
}

43
GameQ/Protocols/Coduo.php Normal file
View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Call of Duty United Offensive Class
*
* @package GameQ\Protocols
*
* @author Wilson Jesus <>
*/
class Coduo extends Quake3
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'coduo';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Call of Duty: United Offensive";
}

View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Call of Duty World at War Class
*
* @package GameQ\Protocols
* @author naXe <naxeify@gmail.com>
* @author Austin Bischoff <austin@codebeard.com>
*/
class Codwaw extends Quake3
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'codwaw';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Call of Duty: World at War";
}

View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Conanexiles
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Conanexiles extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'conanexiles';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Conan Exiles";
}

View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Contagion
*
* @package GameQ\Protocols
* @author Nikolay Ipanyuk <rostov114@gmail.com>
* @author Austin Bischoff <austin@codebeard.com>
*/
class Contagion extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'contagion';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Contagion";
}

View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Crysis
*
* @package GameQ\Protocols
*
* @author Wilson Jesus <>
*/
class Crysis extends Gamespy3
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'crysis';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Crysis";
}

View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Crysis2
*
* @package GameQ\Protocols
*
* @author Wilson Jesus <>
*/
class Crysis2 extends Gamespy3
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'crysis2';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Crysis 2";
}

View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Crysiswars
*
* @package GameQ\Protocols
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class Crysiswars extends Gamespy3
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'crysiswars';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Crysis Wars";
}

45
GameQ/Protocols/Cs15.php Normal file
View File

@ -0,0 +1,45 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Counter-Strike 1.5 Protocol Class
*
* @author Nikolay Ipanyuk <rostov114@gmail.com>
* @author Austin Bischoff <austin@codebeard.com>
*
* @package GameQ\Protocols
*/
class Cs15 extends Won
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'cs15';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Counter-Strike 1.5";
}

69
GameQ/Protocols/Cs16.php Normal file
View File

@ -0,0 +1,69 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Cs16
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Cs16 extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'cs16';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Counter-Strike 1.6";
/**
* In the case of cs 1.6 we offload split packets here because the split packet response for rules is in
* the old gold source format
*
* @param $packet_id
* @param array $packets
*
* @return string
* @throws \GameQ\Exception\Protocol
*/
protected function processPackets($packet_id, array $packets = [])
{
// The response is gold source if the packets are split
$this->source_engine = self::GOLDSOURCE_ENGINE;
// Offload to the parent
$packs = parent::processPackets($packet_id, $packets);
// Reset the engine
$this->source_engine = self::SOURCE_ENGINE;
// Return the result
return $packs;
}
}

263
GameQ/Protocols/Cs2d.php Normal file
View File

@ -0,0 +1,263 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Protocol;
use GameQ\Buffer;
use GameQ\Result;
use GameQ\Exception\Protocol as Exception;
/**
* Counter-Strike 2d Protocol Class
*
* Note:
* Unable to make player information calls work as the protocol does not like parallel requests
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class Cs2d extends Protocol
{
/**
* Array of packets we want to query.
*
* @type array
*/
protected $packets = [
self::PACKET_STATUS => "\x01\x00\xFB\x01",
//self::PACKET_STATUS => "\x01\x00\x03\x10\x21\xFB\x01\x75\x00",
self::PACKET_PLAYERS => "\x01\x00\xFB\x05",
];
/**
* Use the response flag to figure out what method to run
*
* @type array
*/
protected $responses = [
"\x01\x00\xFB\x01" => "processDetails",
"\x01\x00\xFB\x05" => "processPlayers",
];
/**
* The query protocol used to make the call
*
* @type string
*/
protected $protocol = 'cs2d';
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'cs2d';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Counter-Strike 2d";
/**
* The client join link
*
* @type string
*/
protected $join_link = "cs2d://%s:%d/";
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
// General
'general' => [
// target => source
'dedicated' => 'dedicated',
'gametype' => 'game_mode',
'hostname' => 'hostname',
'mapname' => 'mapname',
'maxplayers' => 'max_players',
'mod' => 'game_dir',
'numplayers' => 'num_players',
'password' => 'password',
],
// Individual
'player' => [
'name' => 'name',
'deaths' => 'deaths',
'score' => 'score',
],
];
/**
* Process the response for the Tibia server
*
* @return array
* @throws \GameQ\Exception\Protocol
*/
public function processResponse()
{
// We have a merged packet, try to split it back up
if (count($this->packets_response) == 1) {
// Temp buffer to make string manipulation easier
$buffer = new Buffer($this->packets_response[0]);
// Grab the header and set the packet we need to split with
$packet = (($buffer->lookAhead(4) === $this->packets[self::PACKET_PLAYERS]) ?
self::PACKET_STATUS : self::PACKET_PLAYERS);
// Explode the merged packet as the response
$responses = explode(substr($this->packets[$packet], 2), $buffer->getData());
// Try to rebuild the second packet to the same as if it was sent as two separate responses
$responses[1] = $this->packets[$packet] . ((count($responses) === 2) ? $responses[1] : "");
unset($buffer);
} else {
$responses = $this->packets_response;
}
// Will hold the packets after sorting
$packets = [];
// We need to pre-sort these for split packets so we can do extra work where needed
foreach ($responses as $response) {
$buffer = new Buffer($response);
// Pull out the header
$header = $buffer->read(4);
// Add the packet to the proper section, we will combine later
$packets[$header][] = $buffer->getBuffer();
}
unset($buffer);
$results = [];
// Now let's iterate and process
foreach ($packets as $header => $packetGroup) {
// Figure out which packet response this is
if (!array_key_exists($header, $this->responses)) {
throw new Exception(__METHOD__ . " response type '" . bin2hex($header) . "' is not valid");
}
// Now we need to call the proper method
$results = array_merge(
$results,
call_user_func_array([$this, $this->responses[$header]], [new Buffer(implode($packetGroup))])
);
}
unset($packets);
return $results;
}
/**
* Handles processing the details data into a usable format
*
* @param Buffer $buffer
*
* @return array
* @throws Exception
*/
protected function processDetails(Buffer $buffer)
{
// Set the result to a new result instance
$result = new Result();
// First int is the server flags
$serverFlags = $buffer->readInt8();
// Read server flags
$result->add('password', (int)$this->readFlag($serverFlags, 0));
$result->add('registered_only', (int)$this->readFlag($serverFlags, 1));
$result->add('fog_of_war', (int)$this->readFlag($serverFlags, 2));
$result->add('friendly_fire', (int)$this->readFlag($serverFlags, 3));
$result->add('bots_enabled', (int)$this->readFlag($serverFlags, 5));
$result->add('lua_scripts', (int)$this->readFlag($serverFlags, 6));
// Read the rest of the buffer data
$result->add('servername', utf8_encode($buffer->readPascalString(0)));
$result->add('mapname', utf8_encode($buffer->readPascalString(0)));
$result->add('num_players', $buffer->readInt8());
$result->add('max_players', $buffer->readInt8());
$result->add('game_mode', $buffer->readInt8());
$result->add('num_bots', (($this->readFlag($serverFlags, 5)) ? $buffer->readInt8() : 0));
$result->add('dedicated', 1);
unset($buffer);
return $result->fetch();
}
/**
* Handles processing the player data into a usable format
*
* @param Buffer $buffer
*
* @return array
* @throws Exception
*/
protected function processPlayers(Buffer $buffer)
{
// Set the result to a new result instance
$result = new Result();
// First entry is the number of players in this list. Don't care
$buffer->read();
// Parse players
while ($buffer->getLength()) {
// Player id
if (($id = $buffer->readInt8()) !== 0) {
// Add the results
$result->addPlayer('id', $id);
$result->addPlayer('name', utf8_encode($buffer->readPascalString(0)));
$result->addPlayer('team', $buffer->readInt8());
$result->addPlayer('score', $buffer->readInt32());
$result->addPlayer('deaths', $buffer->readInt32());
}
}
unset($buffer, $id);
return $result->fetch();
}
/**
* Read flags from stored value
*
* @param $flags
* @param $offset
*
* @return bool
*/
protected function readFlag($flags, $offset)
{
return !!($flags & (1 << $offset));
}
}

45
GameQ/Protocols/Cscz.php Normal file
View File

@ -0,0 +1,45 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Cscz
*
* Based off of CS 1.6
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Cscz extends Cs16
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'cscz';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Counter-Strike: Condition Zero";
}

43
GameQ/Protocols/Csgo.php Normal file
View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Csgo
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Csgo extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'csgo';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Counter-Strike: Global Offensive";
}

42
GameQ/Protocols/Css.php Normal file
View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Css
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Css extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'css';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Counter-Strike: Source";
}

43
GameQ/Protocols/Dal.php Normal file
View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Dark and Light
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Dal extends Arkse
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'dal';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Dark and Light";
}

66
GameQ/Protocols/Dayz.php Normal file
View File

@ -0,0 +1,66 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Dayz
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Dayz extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'dayz';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "DayZ Standalone";
/**
* Overload the math used to guess at the Query Port
*
* @param int $clientPort
*
* @return int
*/
public function findQueryPort($clientPort)
{
/*
* Port layout:
* 2302 - 27016
* 2402 - 27017
* 2502 - 27018
* 2602 - 27019
* 2702 - 27020
* ...
*/
return 27016 + (($clientPort - 2302) / 100);
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Dayzmod
*
* @package GameQ\Protocols
* @author Marcel Bößendörfer <m.boessendoerfer@marbis.net>
* @author Austin Bischoff <austin@codebeard.com>
*/
class Dayzmod extends Armedassault2oa
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'dayzmod';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "DayZ Mod";
}

45
GameQ/Protocols/Dod.php Normal file
View File

@ -0,0 +1,45 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Dod
*
* Based off of CS 1.6
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Dod extends Cs16
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'dod';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Day of Defeat";
}

42
GameQ/Protocols/Dods.php Normal file
View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Dods
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Dods extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'dods';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Day of Defeat: Source";
}

69
GameQ/Protocols/Dow.php Normal file
View File

@ -0,0 +1,69 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Buffer;
/**
* Class Dow
*
* Apparently the player response is incomplete as there is no information being returned for that packet
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Dow extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'dow';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Days of War";
/**
* Normalize main fields
*
* @var array
*/
protected $normalize = [
// General
'general' => [
// target => source
'gametype' => 'G_s',
'hostname' => 'ONM_s',
'mapname' => 'MPN_s',
'maxplayers' => 'P_i',
'numplayers' => 'N_i',
],
// Individual
'player' => [
'name' => 'name',
'score' => 'score',
'time' => 'time',
],
];
}

123
GameQ/Protocols/Eco.php Normal file
View File

@ -0,0 +1,123 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Exception\Protocol as Exception;
use GameQ\Result;
/**
* ECO Global Survival Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class Eco extends Http
{
/**
* Packets to send
*
* @var array
*/
protected $packets = [
self::PACKET_STATUS => "GET /frontpage HTTP/1.0\r\nAccept: */*\r\n\r\n",
];
/**
* Http protocol is SSL
*
* @var string
*/
protected $transport = self::TRANSPORT_TCP;
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'eco';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'eco';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "ECO Global Survival";
/**
* query_port = client_port + 1
*
* @type int
*/
protected $port_diff = 1;
/**
* Normalize some items
*
* @var array
*/
protected $normalize = [
// General
'general' => [
// target => source
'dedicated' => 'dedicated',
'hostname' => 'description',
'maxplayers' => 'totalplayers',
'numplayers' => 'onlineplayers',
'password' => 'haspassword',
],
];
/**
* Process the response
*
* @return array
* @throws Exception
*/
public function processResponse()
{
if (empty($this->packets_response)) {
return [];
}
// Implode and rip out the JSON
preg_match('/\{(.*)\}/ms', implode('', $this->packets_response), $matches);
// Return should be JSON, let's validate
if (!isset($matches[0]) || ($json = json_decode($matches[0])) === null) {
throw new Exception("JSON response from Eco server is invalid.");
}
$result = new Result();
// Server is always dedicated
$result->add('dedicated', 1);
foreach ($json->Info as $info => $setting) {
$result->add(strtolower($info), $setting);
}
return $result->fetch();
}
}

51
GameQ/Protocols/Egs.php Normal file
View File

@ -0,0 +1,51 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Empyrion - Galactic Survival
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
* @author TacTicToe66 <https://github.com/TacTicToe66>
*/
class EgS extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'egs';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Empyrion - Galactic Survival";
/**
* query_port = client_port + 1
*
* @type int
*/
protected $port_diff = 1;
}

43
GameQ/Protocols/Et.php Normal file
View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Wolfenstein Enemy Territory Protocol Class
*
* @package GameQ\Protocols
*
* @author Wilson Jesus <>
*/
class Et extends Quake3
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'et';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Wolfenstein Enemy Territory";
}

234
GameQ/Protocols/Etqw.php Normal file
View File

@ -0,0 +1,234 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Buffer;
use GameQ\Exception\Protocol as Exception;
use GameQ\Protocol;
use GameQ\Result;
/**
* Enemy Territory Quake Wars Protocol Class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class Etqw extends Protocol
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @type array
*/
protected $packets = [
self::PACKET_STATUS => "\xFF\xFFgetInfoEx\x00\x00\x00\x00",
//self::PACKET_STATUS => "\xFF\xFFgetInfo\x00\x00\x00\x00\x00",
];
/**
* Use the response flag to figure out what method to run
*
* @type array
*/
protected $responses = [
"\xFF\xFFinfoExResponse" => "processStatus",
];
/**
* The query protocol used to make the call
*
* @type string
*/
protected $protocol = 'etqw';
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'etqw';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Enemy Territory Quake Wars";
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
// General
'general' => [
// target => source
'gametype' => 'campaign',
'hostname' => 'name',
'mapname' => 'map',
'maxplayers' => 'maxPlayers',
'mod' => 'gamename',
'numplayers' => 'numplayers',
'password' => 'privateClients',
],
// Individual
'player' => [
'name' => 'name',
'score' => 'score',
'time' => 'time',
],
];
/**
* Process the response
*
* @return array
* @throws \GameQ\Exception\Protocol
*/
public function processResponse()
{
// In case it comes back as multiple packets (it shouldn't)
$buffer = new Buffer(implode('', $this->packets_response));
// Figure out what packet response this is for
$response_type = $buffer->readString();
// Figure out which packet response this is
if (!array_key_exists($response_type, $this->responses)) {
throw new Exception(__METHOD__ . " response type '{$response_type}' is not valid");
}
// Offload the call
$results = call_user_func_array([$this, $this->responses[$response_type]], [$buffer]);
return $results;
}
/*
* Internal methods
*/
/**
* Handle processing the status response
*
* @param Buffer $buffer
*
* @return array
*/
protected function processStatus(Buffer $buffer)
{
// Set the result to a new result instance
$result = new Result();
// Defaults
$result->add('dedicated', 1);
// Now burn the challenge, version and size
$buffer->skip(16);
// Key / value pairs
while ($buffer->getLength()) {
$var = str_replace('si_', '', $buffer->readString());
$val = $buffer->readString();
if (empty($var) && empty($val)) {
break;
}
// Add the server prop
$result->add($var, $val);
}
// Now let's do the basic player info
$this->parsePlayers($buffer, $result);
// Now grab the rest of the server info
$result->add('osmask', $buffer->readInt32());
$result->add('ranked', $buffer->readInt8());
$result->add('timeleft', $buffer->readInt32());
$result->add('gamestate', $buffer->readInt8());
$result->add('servertype', $buffer->readInt8());
// 0: regular server
if ($result->get('servertype') == 0) {
$result->add('interested_clients', $buffer->readInt8());
} else {
// 1: tv server
$result->add('connected_clients', $buffer->readInt32());
$result->add('max_clients', $buffer->readInt32());
}
// Now let's parse the extended player info
$this->parsePlayersExtra($buffer, $result);
unset($buffer);
return $result->fetch();
}
/**
* Parse players out of the status ex response
*
* @param Buffer $buffer
* @param Result $result
*/
protected function parsePlayers(Buffer &$buffer, Result &$result)
{
// By default there are 0 players
$players = 0;
// Iterate over the players until we run out
while (($id = $buffer->readInt8()) != 32) {
$result->addPlayer('id', $id);
$result->addPlayer('ping', $buffer->readInt16());
$result->addPlayer('name', $buffer->readString());
$result->addPlayer('clantag_pos', $buffer->readInt8());
$result->addPlayer('clantag', $buffer->readString());
$result->addPlayer('bot', $buffer->readInt8());
$players++;
}
// Let's add in the current players as a result
$result->add('numplayers', $players);
// Free some memory
unset($id);
}
/**
* Handle parsing extra player data
*
* @param Buffer $buffer
* @param Result $result
*/
protected function parsePlayersExtra(Buffer &$buffer, Result &$result)
{
// Iterate over the extra player info
while (($id = $buffer->readInt8()) != 32) {
$result->addPlayer('total_xp', $buffer->readFloat32());
$result->addPlayer('teamname', $buffer->readString());
$result->addPlayer('total_kills', $buffer->readInt32());
$result->addPlayer('total_deaths', $buffer->readInt32());
}
// @todo: Add team stuff
// Free some memory
unset($id);
}
}

43
GameQ/Protocols/Ffe.php Normal file
View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Ffe - Fortress Forever
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Ffe extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'ffe';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Fortress Forever";
}

243
GameQ/Protocols/Ffow.php Normal file
View File

@ -0,0 +1,243 @@
<?php
namespace GameQ\Protocols;
use GameQ\Protocol;
use GameQ\Buffer;
use GameQ\Result;
use GameQ\Exception\Protocol as Exception;
/**
* Frontlines Fuel of War Protocol Class
*
* Handles processing ffow servers
*
* Class is incomplete due to lack of players to test against.
* http://wiki.hlsw.net/index.php/FFOW_Protocol
*
* @package GameQ\Protocols
*/
class Ffow extends Protocol
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @type array
*/
protected $packets = [
self::PACKET_CHALLENGE => "\xFF\xFF\xFF\xFF\x57",
self::PACKET_RULES => "\xFF\xFF\xFF\xFF\x56%s",
self::PACKET_PLAYERS => "\xFF\xFF\xFF\xFF\x55%s",
self::PACKET_INFO => "\xFF\xFF\xFF\xFF\x46\x4C\x53\x51",
];
/**
* Use the response flag to figure out what method to run
*
* @type array
*/
protected $responses = [
"\xFF\xFF\xFF\xFF\x49\x02" => 'processInfo', // I
"\xFF\xFF\xFF\xFF\x45\x00" => 'processRules', // E
"\xFF\xFF\xFF\xFF\x44\x00" => 'processPlayers', // D
];
/**
* The query protocol used to make the call
*
* @type string
*/
protected $protocol = 'ffow';
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'ffow';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Frontlines Fuel of War";
/**
* The client join link
*
* @type string
*/
protected $join_link = null;
/**
* query_port = client_port + 2
*
* @type int
*/
protected $port_diff = 2;
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
// General
'general' => [
// target => source
'gametype' => 'gamemode',
'hostname' => 'servername',
'mapname' => 'mapname',
'maxplayers' => 'max_players',
'mod' => 'modname',
'numplayers' => 'num_players',
'password' => 'password',
],
// Individual
'player' => [
'name' => 'name',
'ping' => 'ping',
'score' => 'frags',
],
];
/**
* Parse the challenge response and apply it to all the packet types
*
* @param \GameQ\Buffer $challenge_buffer
*
* @return bool
* @throws \GameQ\Exception\Protocol
*/
public function challengeParseAndApply(Buffer $challenge_buffer)
{
// Burn padding
$challenge_buffer->skip(5);
// Apply the challenge and return
return $this->challengeApply($challenge_buffer->read(4));
}
/**
* Handle response from the server
*
* @return mixed
* @throws Exception
*/
public function processResponse()
{
// Init results
$results = [];
foreach ($this->packets_response as $response) {
$buffer = new Buffer($response);
// Figure out what packet response this is for
$response_type = $buffer->read(6);
// Figure out which packet response this is
if (!array_key_exists($response_type, $this->responses)) {
throw new Exception(__METHOD__ . " response type '" . bin2hex($response_type) . "' is not valid");
}
// Now we need to call the proper method
$results = array_merge(
$results,
call_user_func_array([$this, $this->responses[$response_type]], [$buffer])
);
unset($buffer);
}
return $results;
}
/**
* Handle processing the server information
*
* @param Buffer $buffer
*
* @return array
*/
protected function processInfo(Buffer $buffer)
{
// Set the result to a new result instance
$result = new Result();
$result->add('servername', $buffer->readString());
$result->add('mapname', $buffer->readString());
$result->add('modname', $buffer->readString());
$result->add('gamemode', $buffer->readString());
$result->add('description', $buffer->readString());
$result->add('version', $buffer->readString());
$result->add('port', $buffer->readInt16());
$result->add('num_players', $buffer->readInt8());
$result->add('max_players', $buffer->readInt8());
$result->add('dedicated', $buffer->readInt8());
$result->add('os', $buffer->readInt8());
$result->add('password', $buffer->readInt8());
$result->add('anticheat', $buffer->readInt8());
$result->add('average_fps', $buffer->readInt8());
$result->add('round', $buffer->readInt8());
$result->add('max_rounds', $buffer->readInt8());
$result->add('time_left', $buffer->readInt16());
unset($buffer);
return $result->fetch();
}
/**
* Handle processing the server rules
*
* @param Buffer $buffer
*
* @return array
*/
protected function processRules(Buffer $buffer)
{
// Set the result to a new result instance
$result = new Result();
// Burn extra header
$buffer->skip(1);
// Read rules until we run out of buffer
while ($buffer->getLength()) {
$key = $buffer->readString();
// Check for map
if (strstr($key, "Map:")) {
$result->addSub("maplist", "name", $buffer->readString());
} else // Regular rule
{
$result->add($key, $buffer->readString());
}
}
unset($buffer);
return $result->fetch();
}
/**
* Handle processing of player data
*
* @todo: Build this out when there is a server with players to test against
*
* @param Buffer $buffer
*
* @return array
*/
protected function processPlayers(Buffer $buffer)
{
// Set the result to a new result instance
$result = new Result();
unset($buffer);
return $result->fetch();
}
}

181
GameQ/Protocols/Gamespy.php Normal file
View File

@ -0,0 +1,181 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Protocol;
use GameQ\Buffer;
use GameQ\Result;
use \GameQ\Exception\Protocol as Exception;
/**
* GameSpy Protocol class
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class Gamespy extends Protocol
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @type array
*/
protected $packets = [
self::PACKET_STATUS => "\x5C\x73\x74\x61\x74\x75\x73\x5C",
];
/**
* The query protocol used to make the call
*
* @type string
*/
protected $protocol = 'gamespy';
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'gamespy';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "GameSpy Server";
/**
* The client join link
*
* @type string
*/
protected $join_link = null;
/**
* Process the response for this protocol
*
* @return array
* @throws Exception
*/
public function processResponse()
{
// Holds the processed packets so we can sort them in case they come in an unordered
$processed = [];
// Iterate over the packets
foreach ($this->packets_response as $response) {
// Check to see if we had a preg_match error
if (($match = preg_match("#^(.*)\\\\queryid\\\\([^\\\\]+)(\\\\|$)#", $response, $matches)) === false
|| $match != 1
) {
throw new Exception(__METHOD__ . " An error occurred while parsing the packets for 'queryid'");
}
// Multiply so we move the decimal point out of the way, if there is one
$key = (int)(floatval($matches[2]) * 1000);
// Add this packet to the processed
$processed[$key] = $matches[1];
}
// Sort the new array to make sure the keys (query ids) are in the proper order
ksort($processed, SORT_NUMERIC);
// Create buffer and offload processing
return $this->processStatus(new Buffer(implode('', $processed)));
}
/*
* Internal methods
*/
/**
* Handle processing the status buffer
*
* @param Buffer $buffer
*
* @return array
*/
protected function processStatus(Buffer $buffer)
{
// Set the result to a new result instance
$result = new Result();
// By default dedicted
$result->add('dedicated', 1);
// Lets peek and see if the data starts with a \
if ($buffer->lookAhead(1) == '\\') {
// Burn the first one
$buffer->skip(1);
}
// Explode the data
$data = explode('\\', $buffer->getBuffer());
// No longer needed
unset($buffer);
// Init some vars
$numPlayers = 0;
$numTeams = 0;
$itemCount = count($data);
// Check to make sure we have more than 1 item in the array before trying to loop
if (count($data) > 1) {
// Now lets loop the array since we have items
for ($x = 0; $x < $itemCount; $x += 2) {
// Set some local vars
$key = $data[$x];
$val = $data[$x + 1];
// Check for <variable>_<count> variable (i.e players)
if (($suffix = strrpos($key, '_')) !== false && is_numeric(substr($key, $suffix + 1))) {
// See if this is a team designation
if (substr($key, 0, $suffix) == 'teamname') {
$result->addTeam('teamname', $val);
$numTeams++;
} else {
// Its a player
if (substr($key, 0, $suffix) == 'playername') {
$numPlayers++;
}
$result->addPlayer(substr($key, 0, $suffix), utf8_encode($val));
}
} else {
// Regular variable so just add the value.
$result->add($key, $val);
}
}
}
// Add the player and team count
$result->add('num_players', $numPlayers);
$result->add('num_teams', $numTeams);
// Unset some stuff to free up memory
unset($data, $key, $val, $suffix, $x, $itemCount);
// Return the result
return $result->fetch();
}
}

View File

@ -0,0 +1,269 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Exception\Protocol as Exception;
use GameQ\Protocol;
use GameQ\Buffer;
use GameQ\Result;
/**
* GameSpy2 Protocol class
*
* Given the ability for non utf-8 characters to be used as hostnames, player names, etc... this
* version returns all strings utf-8 encoded (utf8_encode). To access the proper version of a
* string response you must use utf8_decode() on the specific response.
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class Gamespy2 extends Protocol
{
/**
* Define the state of this class
*
* @type int
*/
protected $state = self::STATE_BETA;
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @type array
*/
protected $packets = [
self::PACKET_DETAILS => "\xFE\xFD\x00\x43\x4F\x52\x59\xFF\x00\x00",
self::PACKET_PLAYERS => "\xFE\xFD\x00\x43\x4F\x52\x58\x00\xFF\xFF",
];
/**
* Use the response flag to figure out what method to run
*
* @type array
*/
protected $responses = [
"\x00\x43\x4F\x52\x59" => "processDetails",
"\x00\x43\x4F\x52\x58" => "processPlayers",
];
/**
* The query protocol used to make the call
*
* @type string
*/
protected $protocol = 'gamespy2';
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'gamespy2';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "GameSpy2 Server";
/**
* The client join link
*
* @type string
*/
protected $join_link = null;
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
// General
'general' => [
// target => source
'dedicated' => 'dedicated',
'gametype' => 'gametype',
'hostname' => 'hostname',
'mapname' => 'mapname',
'maxplayers' => 'maxplayers',
'mod' => 'mod',
'numplayers' => 'numplayers',
'password' => 'password',
],
];
/**
* Process the response
*
* @return array
* @throws Exception
*/
public function processResponse()
{
// Will hold the packets after sorting
$packets = [];
// We need to pre-sort these for split packets so we can do extra work where needed
foreach ($this->packets_response as $response) {
$buffer = new Buffer($response);
// Pull out the header
$header = $buffer->read(5);
// Add the packet to the proper section, we will combine later
$packets[$header][] = $buffer->getBuffer();
}
unset($buffer);
$results = [];
// Now let's iterate and process
foreach ($packets as $header => $packetGroup) {
// Figure out which packet response this is
if (!array_key_exists($header, $this->responses)) {
throw new Exception(__METHOD__ . " response type '" . bin2hex($header) . "' is not valid");
}
// Now we need to call the proper method
$results = array_merge(
$results,
call_user_func_array([$this, $this->responses[$header]], [new Buffer(implode($packetGroup))])
);
}
unset($packets);
return $results;
}
/*
* Internal methods
*/
/**
* Handles processing the details data into a usable format
*
* @param \GameQ\Buffer $buffer
*
* @return array
* @throws Exception
*/
protected function processDetails(Buffer $buffer)
{
// Set the result to a new result instance
$result = new Result();
// We go until we hit an empty key
while ($buffer->getLength()) {
$key = $buffer->readString();
if (strlen($key) == 0) {
break;
}
$result->add($key, utf8_encode($buffer->readString()));
}
unset($buffer);
return $result->fetch();
}
/**
* Handles processing the players data into a usable format
*
* @param \GameQ\Buffer $buffer
*
* @return array
* @throws Exception
*/
protected function processPlayers(Buffer $buffer)
{
// Set the result to a new result instance
$result = new Result();
// Skip the header
$buffer->skip(1);
// Players are first
$this->parsePlayerTeam('players', $buffer, $result);
// Teams are next
$this->parsePlayerTeam('teams', $buffer, $result);
unset($buffer);
return $result->fetch();
}
/**
* Parse the player/team info returned from the player call
*
* @param string $dataType
* @param \GameQ\Buffer $buffer
* @param \GameQ\Result $result
*
* @throws Exception
*/
protected function parsePlayerTeam($dataType, Buffer &$buffer, Result &$result)
{
// Do count
$result->add('num_' . $dataType, $buffer->readInt8());
// Variable names
$varNames = [];
// Loop until we run out of length
while ($buffer->getLength()) {
$varNames[] = str_replace('_', '', $buffer->readString());
if ($buffer->lookAhead() === "\x00") {
$buffer->skip();
break;
}
}
// Check if there are any value entries
if ($buffer->lookAhead() == "\x00") {
$buffer->skip();
return;
}
// Get the values
while ($buffer->getLength() > 4) {
foreach ($varNames as $varName) {
$result->addSub($dataType, utf8_encode($varName), utf8_encode($buffer->readString()));
}
if ($buffer->lookAhead() === "\x00") {
$buffer->skip();
break;
}
}
return;
}
}

View File

@ -0,0 +1,340 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Protocol;
use GameQ\Buffer;
use GameQ\Result;
/**
* GameSpy3 Protocol class
*
* Given the ability for non utf-8 characters to be used as hostnames, player names, etc... this
* version returns all strings utf-8 encoded (utf8_encode). To access the proper version of a
* string response you must use utf8_decode() on the specific response.
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class Gamespy3 extends Protocol
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @type array
*/
protected $packets = [
self::PACKET_CHALLENGE => "\xFE\xFD\x09\x10\x20\x30\x40",
self::PACKET_ALL => "\xFE\xFD\x00\x10\x20\x30\x40%s\xFF\xFF\xFF\x01",
];
/**
* The query protocol used to make the call
*
* @type string
*/
protected $protocol = 'gamespy3';
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'gamespy3';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "GameSpy3 Server";
/**
* The client join link
*
* @type string
*/
protected $join_link = null;
/**
* This defines the split between the server info and player/team info.
* This value can vary by game. This value is the default split.
*
* @var string
*/
protected $packetSplit = "/\\x00\\x00\\x01/m";
/**
* Parse the challenge response and apply it to all the packet types
*
* @param \GameQ\Buffer $challenge_buffer
*
* @return bool
* @throws \GameQ\Exception\Protocol
*/
public function challengeParseAndApply(Buffer $challenge_buffer)
{
// Pull out the challenge
$challenge = substr(preg_replace("/[^0-9\-]/si", "", $challenge_buffer->getBuffer()), 1);
// By default, no challenge result (see #197)
$challenge_result = '';
// Check for valid challenge (see #197)
if ($challenge) {
// Encode chellenge result
$challenge_result = sprintf(
"%c%c%c%c",
($challenge >> 24),
($challenge >> 16),
($challenge >> 8),
($challenge >> 0)
);
}
// Apply the challenge and return
return $this->challengeApply($challenge_result);
}
/**
* Process the response
*
* @return array
*/
public function processResponse()
{
// Holds the processed packets
$processed = [];
// Iterate over the packets
foreach ($this->packets_response as $response) {
// Make a buffer
$buffer = new Buffer($response, Buffer::NUMBER_TYPE_BIGENDIAN);
// Packet type = 0
$buffer->readInt8();
// Session Id
$buffer->readInt32();
// We need to burn the splitnum\0 because it is not used
$buffer->skip(9);
// Get the id
$id = $buffer->readInt8();
// Burn next byte not sure what it is used for
$buffer->skip(1);
// Add this packet to the processed
$processed[$id] = $buffer->getBuffer();
unset($buffer, $id);
}
// Sort packets, reset index
ksort($processed);
// Offload cleaning up the packets if they happen to be split
$packets = $this->cleanPackets(array_values($processed));
// Split the packets by type general and the rest (i.e. players & teams)
$split = preg_split($this->packetSplit, implode('', $packets));
// Create a new result
$result = new Result();
// Assign variable due to pass by reference in PHP 7+
$buffer = new Buffer($split[0], Buffer::NUMBER_TYPE_BIGENDIAN);
// First key should be server details and rules
$this->processDetails($buffer, $result);
// The rest should be the player and team information, if it exists
if (array_key_exists(1, $split)) {
$buffer = new Buffer($split[1], Buffer::NUMBER_TYPE_BIGENDIAN);
$this->processPlayersAndTeams($buffer, $result);
}
unset($buffer);
return $result->fetch();
}
/*
* Internal methods
*/
/**
* Handles cleaning up packets since the responses can be a bit "dirty"
*
* @param array $packets
*
* @return array
*/
protected function cleanPackets(array $packets = [])
{
// Get the number of packets
$packetCount = count($packets);
// Compare last var of current packet with first var of next packet
// On a partial match, remove last var from current packet,
// variable header from next packet
for ($i = 0, $x = $packetCount; $i < $x - 1; $i++) {
// First packet
$fst = substr($packets[$i], 0, -1);
// Second packet
$snd = $packets[$i + 1];
// Get last variable from first packet
$fstvar = substr($fst, strrpos($fst, "\x00") + 1);
// Get first variable from last packet
$snd = substr($snd, strpos($snd, "\x00") + 2);
$sndvar = substr($snd, 0, strpos($snd, "\x00"));
// Check if fstvar is a substring of sndvar
// If so, remove it from the first string
if (!empty($fstvar) && strpos($sndvar, $fstvar) !== false) {
$packets[$i] = preg_replace("#(\\x00[^\\x00]+\\x00)$#", "\x00", $packets[$i]);
}
}
// Now let's loop the return and remove any dupe prefixes
for ($x = 1; $x < $packetCount; $x++) {
$buffer = new Buffer($packets[$x], Buffer::NUMBER_TYPE_BIGENDIAN);
$prefix = $buffer->readString();
// Check to see if the return before has the same prefix present
if ($prefix != null && strstr($packets[($x - 1)], $prefix)) {
// Update the return by removing the prefix plus 2 chars
$packets[$x] = substr(str_replace($prefix, '', $packets[$x]), 2);
}
unset($buffer);
}
unset($x, $i, $snd, $sndvar, $fst, $fstvar);
// Return cleaned packets
return $packets;
}
/**
* Handles processing the details data into a usable format
*
* @param \GameQ\Buffer $buffer
* @param \GameQ\Result $result
*/
protected function processDetails(Buffer &$buffer, Result &$result)
{
// We go until we hit an empty key
while ($buffer->getLength()) {
$key = $buffer->readString();
if (strlen($key) == 0) {
break;
}
$result->add($key, utf8_encode($buffer->readString()));
}
}
/**
* Handles processing the player and team data into a usable format
*
* @param \GameQ\Buffer $buffer
* @param \GameQ\Result $result
*/
protected function processPlayersAndTeams(Buffer &$buffer, Result &$result)
{
/*
* Explode the data into groups. First is player, next is team (item_t)
* Each group should be as follows:
*
* [0] => item_
* [1] => information for item_
* ...
*/
$data = explode("\x00\x00", $buffer->getBuffer());
// By default item_group is blank, this will be set for each loop thru the data
$item_group = '';
// By default the item_type is blank, this will be set on each loop
$item_type = '';
// Save count as variable
$count = count($data);
// Loop through all of the $data for information and pull it out into the result
for ($x = 0; $x < $count - 1; $x++) {
// Pull out the item
$item = $data[$x];
// If this is an empty item, move on
if ($item == '' || $item == "\x00") {
continue;
}
/*
* Left as reference:
*
* Each block of player_ and team_t have preceding junk chars
*
* player_ is actually \x01player_
* team_t is actually \x00\x02team_t
*
* Probably a by-product of the change to exploding the data from the original.
*
* For now we just strip out these characters
*/
// Check to see if $item has a _ at the end, this is player info
if (substr($item, -1) == '_') {
// Set the item group
$item_group = 'players';
// Set the item type, rip off any trailing stuff and bad chars
$item_type = rtrim(str_replace("\x01", '', $item), '_');
} elseif (substr($item, -2) == '_t') {
// Check to see if $item has a _t at the end, this is team info
// Set the item group
$item_group = 'teams';
// Set the item type, rip off any trailing stuff and bad chars
$item_type = rtrim(str_replace(["\x00", "\x02"], '', $item), '_t');
} else {
// We can assume it is data belonging to a previously defined item
// Make a temp buffer so we have easier access to the data
$buf_temp = new Buffer($item, Buffer::NUMBER_TYPE_BIGENDIAN);
// Get the values
while ($buf_temp->getLength()) {
// No value so break the loop, end of string
if (($val = $buf_temp->readString()) === '') {
break;
}
// Add the value to the proper item in the correct group
$result->addSub($item_group, $item_type, utf8_encode(trim($val)));
}
// Unset our buffer
unset($buf_temp);
}
}
// Free up some memory
unset($count, $data, $item, $item_group, $item_type, $val);
}
}

View File

@ -0,0 +1,34 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* GameSpy4 Protocol Class
*
* By all accounts GameSpy 4 seems to be GameSpy 3.
*
* References:
* http://www.deletedscreen.com/?p=951
* http://pastebin.com/2zZFDuTd
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class Gamespy4 extends Gamespy3
{
}

42
GameQ/Protocols/Gmod.php Normal file
View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Gmod
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Gmod extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'gmod';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Garry's Mod";
}

42
GameQ/Protocols/Grav.php Normal file
View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Grav Online Protocol Class
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Grav extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'grav';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "GRAV Online";
}

173
GameQ/Protocols/Gta5m.php Normal file
View File

@ -0,0 +1,173 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Buffer;
use GameQ\Exception\Protocol as Exception;
use GameQ\Protocol;
use GameQ\Result;
/**
* GTA Five M Protocol Class
*
* Server base can be found at https://fivem.net/
*
* Based on code found at https://github.com/LiquidObsidian/fivereborn-query
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class Gta5m extends Protocol
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @type array
*/
protected $packets = [
self::PACKET_STATUS => "\xFF\xFF\xFF\xFFgetinfo xxx",
];
/**
* Use the response flag to figure out what method to run
*
* @type array
*/
protected $responses = [
"\xFF\xFF\xFF\xFFinfoResponse" => "processStatus",
];
/**
* The query protocol used to make the call
*
* @type string
*/
protected $protocol = 'gta5m';
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'gta5m';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "GTA Five M";
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
// General
'general' => [
// target => source
'gametype' => 'gametype',
'hostname' => 'hostname',
'mapname' => 'mapname',
'maxplayers' => 'sv_maxclients',
'mod' => 'gamename',
'numplayers' => 'clients',
'password' => 'privateClients',
],
];
/**
* Process the response
*
* @return array
* @throws \GameQ\Exception\Protocol
*/
public function processResponse()
{
// In case it comes back as multiple packets (it shouldn't)
$buffer = new Buffer(implode('', $this->packets_response));
// Figure out what packet response this is for
$response_type = $buffer->readString(PHP_EOL);
// Figure out which packet response this is
if (empty($response_type) || !array_key_exists($response_type, $this->responses)) {
throw new Exception(__METHOD__ . " response type '{$response_type}' is not valid");
}
// Offload the call
$results = call_user_func_array([$this, $this->responses[$response_type]], [$buffer]);
return $results;
}
/*
* Internal methods
*/
/**
* Handle processing the status response
*
* @param Buffer $buffer
*
* @return array
*/
protected function processStatus(Buffer $buffer)
{
// Set the result to a new result instance
$result = new Result();
// Lets peek and see if the data starts with a \
if ($buffer->lookAhead(1) == '\\') {
// Burn the first one
$buffer->skip(1);
}
// Explode the data
$data = explode('\\', $buffer->getBuffer());
// No longer needed
unset($buffer);
$itemCount = count($data);
// Now lets loop the array
for ($x = 0; $x < $itemCount; $x += 2) {
// Set some local vars
$key = $data[$x];
$val = $data[$x + 1];
if (in_array($key, ['challenge'])) {
continue; // skip
}
// Regular variable so just add the value.
$result->add($key, $val);
}
/*var_dump($data);
var_dump($result->fetch());
exit;*/
return $result->fetch();
}
}

163
GameQ/Protocols/Gtan.php Normal file
View File

@ -0,0 +1,163 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Exception\Protocol as Exception;
use GameQ\Result;
use GameQ\Server;
/**
* Grand Theft Auto Network Protocol Class
* https://stats.gtanet.work/
*
* Result from this call should be a header + JSON response
*
* References:
* - https://master.gtanet.work/apiservers
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class Gtan extends Http
{
/**
* Packets to send
*
* @var array
*/
protected $packets = [
//self::PACKET_STATUS => "GET /apiservers HTTP/1.0\r\nHost: master.gtanet.work\r\nAccept: */*\r\n\r\n",
self::PACKET_STATUS => "GET /gtan/api.php?ip=%s&raw HTTP/1.0\r\nHost: multiplayerhosting.info\r\nAccept: */*\r\n\r\n",
];
/**
* Http protocol is SSL
*
* @var string
*/
protected $transport = self::TRANSPORT_SSL;
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'gtan';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'gtan';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Grand Theft Auto Network";
/**
* Holds the real ip so we can overwrite it back
*
* @var string
*/
protected $realIp = null;
protected $realPortQuery = null;
/**
* Normalize some items
*
* @var array
*/
protected $normalize = [
// General
'general' => [
// target => source
'dedicated' => 'dedicated',
'hostname' => 'hostname',
'mapname' => 'map',
'mod' => 'mod',
'maxplayers' => 'maxplayers',
'numplayers' => 'numplayers',
'password' => 'password',
],
];
public function beforeSend(Server $server)
{
// Loop over the packets and update them
foreach ($this->packets as $packetType => $packet) {
// Fill out the packet with the server info
$this->packets[$packetType] = sprintf($packet, $server->ip . ':' . $server->port_query);
}
$this->realIp = $server->ip;
$this->realPortQuery = $server->port_query;
// Override the existing settings
//$server->ip = 'master.gtanet.work';
$server->ip = 'multiplayerhosting.info';
$server->port_query = 443;
}
/**
* Process the response
*
* @return array
* @throws Exception
*/
public function processResponse()
{
// No response, assume offline
if (empty($this->packets_response)) {
return [
'gq_address' => $this->realIp,
'gq_port_query' => $this->realPortQuery,
];
}
// Implode and rip out the JSON
preg_match('/\{(.*)\}/ms', implode('', $this->packets_response), $matches);
// Return should be JSON, let's validate
if (!isset($matches[0]) || ($json = json_decode($matches[0])) === null) {
throw new Exception("JSON response from Gtan protocol is invalid.");
}
$result = new Result();
// Server is always dedicated
$result->add('dedicated', 1);
$result->add('gq_address', $this->realIp);
$result->add('gq_port_query', $this->realPortQuery);
// Add server items
$result->add('hostname', $json->ServerName);
$result->add('serverversion', $json->ServerVersion);
$result->add('map', ((!empty($json->Map)) ? $json->Map : 'Los Santos/Blaine Country'));
$result->add('mod', $json->Gamemode);
$result->add('password', (int)$json->Passworded);
$result->add('numplayers', $json->CurrentPlayers);
$result->add('maxplayers', $json->MaxPlayers);
return $result->fetch();
}
}

164
GameQ/Protocols/Gtar.php Normal file
View File

@ -0,0 +1,164 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Exception\Protocol as Exception;
use GameQ\Result;
use GameQ\Server;
/**
* Grand Theft Auto Rage Protocol Class
* https://rage.mp/masterlist/
*
* Result from this call should be a header + JSON response
*
* @author K700 <admin@fianna.ru>
* @author Austin Bischoff <austin@codebeard.com>
*/
class Gtar extends Http
{
/**
* Packets to send
*
* @var array
*/
protected $packets = [
self::PACKET_STATUS => "GET /master/ HTTP/1.0\r\nHost: cdn.rage.mp\r\nAccept: */*\r\n\r\n",
];
/**
* Http protocol is SSL
*
* @var string
*/
protected $transport = self::TRANSPORT_SSL;
/**
* The protocol being used
*
* @var string
*/
protected $protocol = 'gtar';
/**
* String name of this protocol class
*
* @var string
*/
protected $name = 'gtar';
/**
* Longer string name of this protocol class
*
* @var string
*/
protected $name_long = "Grand Theft Auto Rage";
/**
* Holds the real ip so we can overwrite it back
*
* @var string
*/
protected $realIp = null;
protected $realPortQuery = null;
/**
* Normalize some items
*
* @var array
*/
protected $normalize = [
// General
'general' => [
// target => source
'dedicated' => 'dedicated',
'hostname' => 'hostname',
'mod' => 'mod',
'maxplayers' => 'maxplayers',
'numplayers' => 'numplayers',
],
];
public function beforeSend(Server $server)
{
// Loop over the packets and update them
foreach ($this->packets as $packetType => $packet) {
// Fill out the packet with the server info
$this->packets[$packetType] = sprintf($packet, $server->ip . ':' . $server->port_query);
}
$this->realIp = $server->ip;
$this->realPortQuery = $server->port_query;
// Override the existing settings
$server->ip = 'cdn.rage.mp';
$server->port_query = 443;
}
/**
* Process the response
*
* @return array
* @throws Exception
*/
public function processResponse()
{
// No response, assume offline
if (empty($this->packets_response)) {
return [
'gq_address' => $this->realIp,
'gq_port_query' => $this->realPortQuery,
];
}
// Implode and rip out the JSON
preg_match('/\{(.*)\}/ms', implode('', $this->packets_response), $matches);
// Return should be JSON, let's validate
if (!isset($matches[0]) || ($json = json_decode($matches[0])) === null) {
throw new Exception("JSON response from Gtar protocol is invalid.");
}
$address = $this->realIp.':'.$this->realPortQuery;
$server = $json->$address;
if (empty($server)) {
return [
'gq_address' => $this->realIp,
'gq_port_query' => $this->realPortQuery,
];
}
$result = new Result();
// Server is always dedicated
$result->add('dedicated', 1);
$result->add('gq_address', $this->realIp);
$result->add('gq_port_query', $this->realPortQuery);
// Add server items
$result->add('hostname', $server->name);
$result->add('mod', $server->gamemode);
$result->add('numplayers', $server->players);
$result->add('maxplayers', $server->maxplayers);
return $result->fetch();
}
}

42
GameQ/Protocols/Hl2dm.php Normal file
View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Hl2dm
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Hl2dm extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'hl2dm';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Half Life 2: Deathmatch";
}

67
GameQ/Protocols/Http.php Normal file
View File

@ -0,0 +1,67 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Protocol;
/**
* Class Http
*
* Generic HTTP protocol class. Useful for making http based requests
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
abstract class Http extends Protocol
{
/**
* The query protocol used to make the call
*
* @type string
*/
protected $protocol = 'http';
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'http';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Generic HTTP protocol";
/**
* Http protocol is TCP
*
* @var string
*/
protected $transport = self::TRANSPORT_TCP;
/**
* The client join link
*
* @type string
*/
protected $join_link = null;
}

View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Hurtworld
*
* @package GameQ\Protocols
* @author Nikolay Ipanyuk <rostov114@gmail.com>
* @author Austin Bischoff <austin@codebeard.com>
*/
class Hurtworld extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'hurtworld';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Hurtworld";
}

View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Insurgency
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Insurgency extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'insurgency';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Insurgency";
}

View File

@ -0,0 +1,49 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Insurgency Sandstorm Class
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Insurgencysand extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'insurgencysand';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Insurgency: Sandstorm";
/**
* query_port = client_port + 29
*
* @type int
*/
protected $port_diff = 29;
}

View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Jedi Academy Protocol Class
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Jediacademy extends Quake3
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'jediacademy';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Star Wars Jedi Knight: Jedi Academy";
}

View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Jedi Outcast Protocol Class
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Jedioutcast extends Quake3
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'jedioutcast';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Star Wars Jedi Knight II: Jedi Outcast";
}

View File

@ -0,0 +1,127 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Buffer;
use GameQ\Result;
/**
* Just Cause 2 Multiplayer Protocol Class
*
* Special thanks to Woet for some insight on packing
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Justcause2 extends Gamespy4
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'justcause2';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Just Cause 2 Multiplayer";
/**
* The client join link
*
* @type string
*/
protected $join_link = "steam://connect/%s:%d/";
/**
* Change the packets used
*
* @var array
*/
protected $packets = [
self::PACKET_CHALLENGE => "\xFE\xFD\x09\x10\x20\x30\x40",
self::PACKET_ALL => "\xFE\xFD\x00\x10\x20\x30\x40%s\xFF\xFF\xFF\x02",
];
/**
* Override the packet split
*
* @var string
*/
protected $packetSplit = "/\\x00\\x00\\x00/m";
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
'general' => [
// target => source
'dedicated' => 'dedicated',
'gametype' => 'gametype',
'hostname' => 'hostname',
'mapname' => 'mapname',
'maxplayers' => 'maxplayers',
'numplayers' => 'numplayers',
'password' => 'password',
],
// Individual
'player' => [
'name' => 'name',
'ping' => 'ping',
],
];
/**
* Overload so we can add in some static data points
*
* @param Buffer $buffer
* @param Result $result
*/
protected function processDetails(Buffer &$buffer, Result &$result)
{
parent::processDetails($buffer, $result);
// Add in map
$result->add('mapname', 'Panau');
$result->add('dedicated', 'true');
}
/**
* Override the parent, this protocol is returned differently
*
* @param Buffer $buffer
* @param Result $result
*
* @see Gamespy3::processPlayersAndTeams()
*/
protected function processPlayersAndTeams(Buffer &$buffer, Result &$result)
{
// Loop until we run out of data
while ($buffer->getLength()) {
$result->addPlayer('name', $buffer->readString());
$result->addPlayer('steamid', $buffer->readString());
$result->addPlayer('ping', $buffer->readInt16());
}
}
}

View File

@ -0,0 +1,50 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Just Cause 3
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Justcause3 extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'justcause3';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Just Cause 3";
/**
* Query port = client_port + 1
*
* @type int
*/
protected $port_diff = 1;
}

View File

@ -0,0 +1,96 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Buffer;
use GameQ\Result;
/**
* Class Killing floor
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Killingfloor extends Unreal2
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'killing floor';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Killing Floor";
/**
* query_port = client_port + 1
*
* @type int
*/
protected $port_diff = 1;
/**
* The client join link
*
* @type string
*/
protected $join_link = "steam://connect/%s:%d/";
/**
* Overload the default detail process since this version is different
*
* @param \GameQ\Buffer $buffer
*
* @return array
*/
protected function processDetails(Buffer $buffer)
{
// Set the result to a new result instance
$result = new Result();
$result->add('serverid', $buffer->readInt32()); // 0
$result->add('serverip', $buffer->readPascalString(1)); // empty
$result->add('gameport', $buffer->readInt32());
$result->add('queryport', $buffer->readInt32()); // 0
// We burn the first char since it is not always correct with the hostname
$buffer->skip(1);
// Read as a regular string since the length is incorrect (what we skipped earlier)
$result->add('servername', utf8_encode($buffer->readString()));
// The rest is read as normal
$result->add('mapname', utf8_encode($buffer->readPascalString(1)));
$result->add('gametype', $buffer->readPascalString(1));
$result->add('numplayers', $buffer->readInt32());
$result->add('maxplayers', $buffer->readInt32());
$result->add('currentwave', $buffer->readInt32());
unset($buffer);
return $result->fetch();
}
}

View File

@ -0,0 +1,51 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Killing floor
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Killingfloor2 extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'killing floor 2';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Killing Floor 2";
/**
* query_port = client_port + 19238
* 27015 = 7777 + 19238
*
* @type int
*/
protected $port_diff = 19238;
}

42
GameQ/Protocols/L4d.php Normal file
View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class L4d
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class L4d extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'l4d';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Left 4 Dead";
}

42
GameQ/Protocols/L4d2.php Normal file
View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class L4d2
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class L4d2 extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'l4d2';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Left 4 Dead 2";
}

214
GameQ/Protocols/Lhmp.php Normal file
View File

@ -0,0 +1,214 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Protocol;
use GameQ\Buffer;
use GameQ\Result;
use GameQ\Exception\Protocol as Exception;
/**
* Lost Heaven Protocol class
*
* Reference: http://lh-mp.eu/wiki/index.php/Query_System
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class Lhmp extends Protocol
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @type array
*/
protected $packets = [
self::PACKET_DETAILS => "LHMPo",
self::PACKET_PLAYERS => "LHMPp",
];
/**
* Use the response flag to figure out what method to run
*
* @type array
*/
protected $responses = [
"LHMPo" => "processDetails",
"LHMPp" => "processPlayers",
];
/**
* The query protocol used to make the call
*
* @type string
*/
protected $protocol = 'lhmp';
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'lhmp';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Lost Heaven";
/**
* query_port = client_port + 1
*
* @type int
*/
protected $port_diff = 1;
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
// General
'general' => [
// target => source
'gametype' => 'gamemode',
'hostname' => 'servername',
'mapname' => 'mapname',
'maxplayers' => 'maxplayers',
'numplayers' => 'numplayers',
'password' => 'password',
],
// Individual
'player' => [
'name' => 'name',
],
];
/**
* Process the response
*
* @return array
* @throws \GameQ\Exception\Protocol
*/
public function processResponse()
{
// Will hold the packets after sorting
$packets = [];
// We need to pre-sort these for split packets so we can do extra work where needed
foreach ($this->packets_response as $response) {
$buffer = new Buffer($response);
// Pull out the header
$header = $buffer->read(5);
// Add the packet to the proper section, we will combine later
$packets[$header][] = $buffer->getBuffer();
}
unset($buffer);
$results = [];
// Now let's iterate and process
foreach ($packets as $header => $packetGroup) {
// Figure out which packet response this is
if (!array_key_exists($header, $this->responses)) {
throw new Exception(__METHOD__ . " response type '{$header}' is not valid");
}
// Now we need to call the proper method
$results = array_merge(
$results,
call_user_func_array([$this, $this->responses[$header]], [new Buffer(implode($packetGroup))])
);
}
unset($packets);
return $results;
}
/*
* Internal methods
*/
/**
* Handles processing the details data into a usable format
*
* @param Buffer $buffer
*
* @return array
* @throws Exception
*/
protected function processDetails(Buffer $buffer)
{
// Set the result to a new result instance
$result = new Result();
$result->add('protocol', $buffer->readString());
$result->add('password', $buffer->readString());
$result->add('numplayers', $buffer->readInt16());
$result->add('maxplayers', $buffer->readInt16());
$result->add('servername', utf8_encode($buffer->readPascalString()));
$result->add('gamemode', $buffer->readPascalString());
$result->add('website', utf8_encode($buffer->readPascalString()));
$result->add('mapname', utf8_encode($buffer->readPascalString()));
unset($buffer);
return $result->fetch();
}
/**
* Handles processing the player data into a usable format
*
* @param Buffer $buffer
*
* @return array
*/
protected function processPlayers(Buffer $buffer)
{
// Set the result to a new result instance
$result = new Result();
// Get the number of players
$result->add('numplayers', $buffer->readInt16());
// Parse players
while ($buffer->getLength()) {
// Player id
if (($id = $buffer->readInt16()) !== 0) {
// Add the results
$result->addPlayer('id', $id);
$result->addPlayer('name', utf8_encode($buffer->readPascalString()));
}
}
unset($buffer, $id);
return $result->fetch();
}
}

View File

@ -0,0 +1,87 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Minecraft Protocol Class
*
* Thanks to https://github.com/xPaw/PHP-Minecraft-Query for helping me realize this is
* Gamespy 3 Protocol. Make sure you enable the items below for it to work.
*
* Information from original author:
* Instructions
*
* Before using this class, you need to make sure that your server is running GS4 status listener.
*
* Look for those settings in server.properties:
*
* enable-query=true
* query.port=25565
*
* @package GameQ\Protocols
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class Minecraft extends Gamespy3
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'minecraft';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Minecraft";
/**
* The client join link
*
* @type string
*/
protected $join_link = "minecraft://%s:%d/";
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
// General
'general' => [
// target => source
'dedicated' => 'dedicated',
'gametype' => 'game_id',
'hostname' => 'hostname',
'mapname' => 'map',
'maxplayers' => 'maxplayers',
'numplayers' => 'numplayers',
'password' => 'password',
],
// Individual
'player' => [
'name' => 'player',
],
];
}

View File

@ -0,0 +1,44 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Minecraft PE (BE) Protocol Class
*
* @package GameQ\Protocols
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class Minecraftpe extends Minecraft
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'minecraftpe';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "MinecraftPE";
}

79
GameQ/Protocols/Mohaa.php Normal file
View File

@ -0,0 +1,79 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Medal of honor: Allied Assault Protocol Class
*
* @package GameQ\Protocols
* @author Bram <https://github.com/Stormyy>
* @author Austin Bischoff <austin@codebeard.com>
*/
class Mohaa extends Gamespy
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'mohaa';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Medal of honor: Allied Assault";
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
'general' => [
// target => source
'dedicated' => 'dedicated',
'gametype' => 'gametype',
'hostname' => 'hostname',
'mapname' => 'mapname',
'maxplayers' => 'maxplayers',
'numplayers' => 'numplayers',
'password' => 'password',
],
// Individual
'player' => [
'name' => 'player',
'score' => 'frags',
'ping' => 'ping',
],
];
/**
* Query port is always the client port + 97 in MOHAA
*
* @param int $clientPort
*
* @return int
*/
public function findQueryPort($clientPort)
{
return $clientPort + 97;
}
}

View File

@ -0,0 +1,53 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class MORDHAU
*
* @package GameQ\Protocols
* @author Wilson Jesus <>
*/
class Mordhau extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'mordhau';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "MORDHAU";
#protected $port = 7777;
/**
* query_port = client_port + 19238
* 27015 = 7777 + 19238
*
* @type int
*/
#protected $port_diff = 19238;
}

59
GameQ/Protocols/Mta.php Normal file
View File

@ -0,0 +1,59 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Multi Theft Auto
*
* @package GameQ\Protocols
*
* @author Marcel Bößendörfer <m.boessendoerfer@marbis.net>
* @author Austin Bischoff <austin@codebeard.com>
*/
class Mta extends Ase
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'mta';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Multi Theft Auto";
/**
* query_port = client_port + 123
*
* @type int
*/
protected $port_diff = 123;
/**
* The client join link
*
* @type string
*/
protected $join_link = "mtasa://%s:%d/";
}

194
GameQ/Protocols/Mumble.php Normal file
View File

@ -0,0 +1,194 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
use GameQ\Protocol;
use GameQ\Result;
use GameQ\Exception\Protocol as Exception;
/**
* Mumble Protocol class
*
* References:
* https://github.com/edmundask/MurmurQuery - Thanks to skylord123
*
* @author Austin Bischoff <austin@codebeard.com>
*/
class Mumble extends Protocol
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @type array
*/
protected $packets = [
self::PACKET_ALL => "\x6A\x73\x6F\x6E", // JSON packet
];
/**
* The transport mode for this protocol is TCP
*
* @type string
*/
protected $transport = self::TRANSPORT_TCP;
/**
* The query protocol used to make the call
*
* @type string
*/
protected $protocol = 'mumble';
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'mumble';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Mumble Server";
/**
* The client join link
*
* @type string
*/
protected $join_link = "mumble://%s:%d/";
/**
* 27800 = 64738 - 36938
*
* @type int
*/
protected $port_diff = -36938;
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
// General
'general' => [
'dedicated' => 'dedicated',
'gametype' => 'gametype',
'hostname' => 'name',
'numplayers' => 'numplayers',
'maxplayers' => 'x_gtmurmur_max_users',
],
// Player
'player' => [
'name' => 'name',
'ping' => 'tcpPing',
'team' => 'channel',
'time' => 'onlinesecs',
],
// Team
'team' => [
'name' => 'name',
],
];
/**
* Process the response
*
* @return array
* @throws \GameQ\Exception\Protocol
*/
public function processResponse()
{
// Try to json_decode, make it into an array
if (($data = json_decode(implode('', $this->packets_response), true)) === null) {
throw new Exception(__METHOD__ . " Unable to decode JSON data.");
}
// Set the result to a new result instance
$result = new Result();
// Always dedicated
$result->add('dedicated', 1);
// Let's iterate over the response items, there are a lot
foreach ($data as $key => $value) {
// Ignore root for now, that is where all of the channel/player info is housed
if (in_array($key, ['root'])) {
continue;
}
// Add them as is
$result->add($key, $value);
}
// Offload the channel and user parsing
$this->processChannelsAndUsers($data['root'], $result);
unset($data);
// Manually set the number of players
$result->add('numplayers', count($result->get('players')));
return $result->fetch();
}
/*
* Internal methods
*/
/**
* Handles processing the the channels and user info
*
* @param array $data
* @param \GameQ\Result $result
*/
protected function processChannelsAndUsers(array $data, Result &$result)
{
// Let's add all of the channel information
foreach ($data as $key => $value) {
// We will handle these later
if (in_array($key, ['channels', 'users'])) {
// skip
continue;
}
// Add the channel property as a team
$result->addTeam($key, $value);
}
// Itereate over the users in this channel
foreach ($data['users'] as $user) {
foreach ($user as $key => $value) {
$result->addPlayer($key, $value);
}
}
// Offload more channels to parse
foreach ($data['channels'] as $channel) {
$this->processChannelsAndUsers($channel, $result);
}
}
}

49
GameQ/Protocols/Ns2.php Normal file
View File

@ -0,0 +1,49 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Ns2
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Ns2 extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'ns2';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Natural Selection 2";
/**
* query_port = client_port + 1
*
* @type int
*/
protected $port_diff = 1;
}

View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class PixARK
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Pixark extends Arkse
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'pixark';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "PixARK";
}

View File

@ -0,0 +1,45 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Projectrealitybf2
*
* Based off of BF2
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Projectrealitybf2 extends Bf2
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'projectrealitybf2';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Project Reality: Battlefield 2";
}

219
GameQ/Protocols/Quake2.php Normal file
View File

@ -0,0 +1,219 @@
<?php
namespace GameQ\Protocols;
use GameQ\Protocol;
use GameQ\Buffer;
use GameQ\Result;
use GameQ\Exception\Protocol as Exception;
/**
* Quake2 Protocol Class
*
* Handles processing Quake 3 servers
*
* @package GameQ\Protocols
*/
class Quake2 extends Protocol
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @type array
*/
protected $packets = [
self::PACKET_STATUS => "\xFF\xFF\xFF\xFFstatus\x00",
];
/**
* Use the response flag to figure out what method to run
*
* @type array
*/
protected $responses = [
"\xFF\xFF\xFF\xFF\x70\x72\x69\x6e\x74" => 'processStatus',
];
/**
* The query protocol used to make the call
*
* @type string
*/
protected $protocol = 'quake2';
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'quake2';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Quake 2 Server";
/**
* The client join link
*
* @type string
*/
protected $join_link = null;
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
// General
'general' => [
// target => source
'gametype' => 'gamename',
'hostname' => 'hostname',
'mapname' => 'mapname',
'maxplayers' => 'maxclients',
'mod' => 'g_gametype',
'numplayers' => 'clients',
'password' => 'password',
],
// Individual
'player' => [
'name' => 'name',
'ping' => 'ping',
'score' => 'frags',
],
];
/**
* Handle response from the server
*
* @return mixed
* @throws Exception
*/
public function processResponse()
{
// Make a buffer
$buffer = new Buffer(implode('', $this->packets_response));
// Grab the header
$header = $buffer->readString("\x0A");
// Figure out which packet response this is
if (empty($header) || !array_key_exists($header, $this->responses)) {
throw new Exception(__METHOD__ . " response type '" . bin2hex($header) . "' is not valid");
}
return call_user_func_array([$this, $this->responses[$header]], [$buffer]);
}
/**
* Process the status response
*
* @param Buffer $buffer
*
* @return array
*/
protected function processStatus(Buffer $buffer)
{
// We need to split the data and offload
$results = $this->processServerInfo(new Buffer($buffer->readString("\x0A")));
$results = array_merge_recursive(
$results,
$this->processPlayers(new Buffer($buffer->getBuffer()))
);
unset($buffer);
// Return results
return $results;
}
/**
* Handle processing the server information
*
* @param Buffer $buffer
*
* @return array
*/
protected function processServerInfo(Buffer $buffer)
{
// Set the result to a new result instance
$result = new Result();
// Burn leading \ if one exists
$buffer->readString('\\');
// Key / value pairs
while ($buffer->getLength()) {
// Add result
$result->add(
trim($buffer->readString('\\')),
utf8_encode(trim($buffer->readStringMulti(['\\', "\x0a"])))
);
}
$result->add('password', 0);
$result->add('mod', 0);
unset($buffer);
return $result->fetch();
}
/**
* Handle processing of player data
*
* @param Buffer $buffer
*
* @return array
*/
protected function processPlayers(Buffer $buffer)
{
// Some games do not have a number of current players
$playerCount = 0;
// Set the result to a new result instance
$result = new Result();
// Loop until we are out of data
while ($buffer->getLength()) {
// Make a new buffer with this block
$playerInfo = new Buffer($buffer->readString("\x0A"));
// Add player info
$result->addPlayer('frags', $playerInfo->readString("\x20"));
$result->addPlayer('ping', $playerInfo->readString("\x20"));
// Skip first "
$playerInfo->skip(1);
// Add player name, encoded
$result->addPlayer('name', utf8_encode(trim(($playerInfo->readString('"')))));
// Skip first "
$playerInfo->skip(2);
// Add address
$result->addPlayer('address', trim($playerInfo->readString('"')));
// Increment
$playerCount++;
// Clear
unset($playerInfo);
}
$result->add('clients', $playerCount);
// Clear
unset($buffer, $playerCount);
return $result->fetch();
}
}

214
GameQ/Protocols/Quake3.php Normal file
View File

@ -0,0 +1,214 @@
<?php
namespace GameQ\Protocols;
use GameQ\Protocol;
use GameQ\Buffer;
use GameQ\Result;
use GameQ\Exception\Protocol as Exception;
/**
* Quake3 Protocol Class
*
* Handles processing Quake 3 servers
*
* @package GameQ\Protocols
*/
class Quake3 extends Protocol
{
/**
* Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class
*
* @type array
*/
protected $packets = [
self::PACKET_STATUS => "\xFF\xFF\xFF\xFF\x67\x65\x74\x73\x74\x61\x74\x75\x73\x0A",
];
/**
* Use the response flag to figure out what method to run
*
* @type array
*/
protected $responses = [
"\xFF\xFF\xFF\xFFstatusResponse" => 'processStatus',
];
/**
* The query protocol used to make the call
*
* @type string
*/
protected $protocol = 'quake3';
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'quake3';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Quake 3 Server";
/**
* The client join link
*
* @type string
*/
protected $join_link = null;
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
// General
'general' => [
// target => source
'gametype' => 'gamename',
'hostname' => 'sv_hostname',
'mapname' => 'mapname',
'maxplayers' => 'sv_maxclients',
'mod' => 'g_gametype',
'numplayers' => 'clients',
'password' => ['g_needpass', 'pswrd'],
],
// Individual
'player' => [
'name' => 'name',
'ping' => 'ping',
'score' => 'frags',
],
];
/**
* Handle response from the server
*
* @return mixed
* @throws Exception
*/
public function processResponse()
{
// Make a buffer
$buffer = new Buffer(implode('', $this->packets_response));
// Grab the header
$header = $buffer->readString("\x0A");
// Figure out which packet response this is
if (empty($header) || !array_key_exists($header, $this->responses)) {
throw new Exception(__METHOD__ . " response type '" . bin2hex($header) . "' is not valid");
}
return call_user_func_array([$this, $this->responses[$header]], [$buffer]);
}
protected function processStatus(Buffer $buffer)
{
// We need to split the data and offload
$results = $this->processServerInfo(new Buffer($buffer->readString("\x0A")));
$results = array_merge_recursive(
$results,
$this->processPlayers(new Buffer($buffer->getBuffer()))
);
unset($buffer);
// Return results
return $results;
}
/**
* Handle processing the server information
*
* @param Buffer $buffer
*
* @return array
*/
protected function processServerInfo(Buffer $buffer)
{
// Set the result to a new result instance
$result = new Result();
// Burn leading \ if one exists
$buffer->readString('\\');
// Key / value pairs
while ($buffer->getLength()) {
// Add result
$result->add(
trim($buffer->readString('\\')),
utf8_encode(trim($buffer->readStringMulti(['\\', "\x0a"])))
);
}
unset($buffer);
return $result->fetch();
}
/**
* Handle processing of player data
*
* @param Buffer $buffer
*
* @return array
* @throws Exception
*/
protected function processPlayers(Buffer $buffer)
{
// Some games do not have a number of current players
$playerCount = 0;
// Set the result to a new result instance
$result = new Result();
// Loop until we are out of data
while ($buffer->getLength()) {
// Add player info
$result->addPlayer('frags', $buffer->readString("\x20"));
$result->addPlayer('ping', $buffer->readString("\x20"));
// Look ahead to see if we have a name or team
$checkTeam = $buffer->lookAhead(1);
// We have team info
if ($checkTeam != '' and $checkTeam != '"') {
$result->addPlayer('team', $buffer->readString("\x20"));
}
// Check to make sure we have player name
$checkPlayerName = $buffer->read();
// Bad response
if ($checkPlayerName !== '"') {
throw new Exception('Expected " but got ' . $checkPlayerName . ' for beginning of player name string!');
}
// Add player name, encoded
$result->addPlayer('name', utf8_encode(trim($buffer->readString('"'))));
// Burn ending delimiter
$buffer->read();
// Increment
$playerCount++;
}
$result->add('clients', $playerCount);
// Clear
unset($buffer, $playerCount);
return $result->fetch();
}
}

View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Quake Live
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Quakelive extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'quakelive';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Quake Live";
}

View File

@ -0,0 +1,50 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Class Redorchestra2
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Redorchestra2 extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'redorchestra2';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Red Orchestra 2";
/**
* query_port = client_port + 19238
* 27015 = 7777 + 19238
*
* @type int
*/
protected $port_diff = 19238;
}

View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of GameQ.
*
* GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GameQ\Protocols;
/**
* Red Orchestra: Ostfront 41-45 Class
*
* @package GameQ\Protocols
* @author naXe <naxeify@gmail.com>
* @author Austin Bischoff <austin@codebeard.com>
*/
class Redorchestraostfront extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'redorchestraostfront';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Red Orchestra: Ostfront 41-45";
}

Some files were not shown because too many files have changed in this diff Show More