GameQ Fix & Update

Provided by https://steamcommunity.com/id/0null1works/
(ဪTheLupi#5961)
This commit is contained in:
AEon-Jan 2022-01-06 21:41:48 +01:00
parent 26054f601c
commit 4236d48ae1
46 changed files with 1566 additions and 273 deletions

View File

@ -276,8 +276,8 @@ class Buffer
// Get position of delimiters // Get position of delimiters
$pos = []; $pos = [];
foreach ($delims as $delim) { foreach ($delims as $delim) {
if ($p = strpos($this->data, $delim, min($this->index, $this->length))) { if ($index = strpos($this->data, $delim, min($this->index, $this->length))) {
$pos[] = $p; $pos[] = $index;
} }
} }

View File

@ -104,6 +104,8 @@ class Secondstohuman extends Base
// Iterate and update the result // Iterate and update the result
$result[$key] = $this->iterate($value); $result[$key] = $this->iterate($value);
} elseif (in_array($key, $this->options[self::OPTION_TIMEKEYS])) { } 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 // We match one of the keys we are wanting to convert so add it and move on
$result[sprintf(self::RESULT_KEY, $key)] = sprintf( $result[sprintf(self::RESULT_KEY, $key)] = sprintf(
"%02d:%02d:%02d", "%02d:%02d:%02d",

View File

@ -33,6 +33,8 @@ class Stripcolors extends Base
/** /**
* Apply this filter * Apply this filter
* *
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @param array $result * @param array $result
* @param \GameQ\Server $server * @param \GameQ\Server $server
* *
@ -62,6 +64,9 @@ class Stripcolors extends Base
case 'gamespy2': case 'gamespy2':
array_walk_recursive($result, [$this, 'stripUnreal']); array_walk_recursive($result, [$this, 'stripUnreal']);
break; break;
case 'source':
array_walk_recursive($result, [$this, 'stripSource']);
break;
} }
/*$data['filtered'][ $server->id() ] = $result; /*$data['filtered'][ $server->id() ] = $result;
@ -97,4 +102,14 @@ class Stripcolors extends Base
{ {
$string = preg_replace('/\x1b.../', '', $string); $string = preg_replace('/\x1b.../', '', $string);
} }
/**
* Strip color codes from Source based games
*
* @param string $string
*/
protected function stripSource(&$string)
{
$string = strip_tags($string);
}
} }

View File

@ -15,6 +15,7 @@
* You should have received a copy of the GNU Lesser General Public License * 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/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace GameQ; namespace GameQ;
use GameQ\Exception\Protocol as ProtocolException; use GameQ\Exception\Protocol as ProtocolException;
@ -43,6 +44,7 @@ class GameQ
/* /*
* Constants * Constants
*/ */
const PROTOCOLS_DIRECTORY = __DIR__ . '/Protocols';
/* Static Section */ /* Static Section */
@ -408,10 +410,10 @@ class GameQ
'server_id' => $server_id, 'server_id' => $server_id,
'socket' => $socket, 'socket' => $socket,
]; ];
} catch (QueryException $e) { } catch (QueryException $exception) {
// Check to see if we are in debug, if so bubble up the exception // Check to see if we are in debug, if so bubble up the exception
if ($this->debug) { if ($this->debug) {
throw new \Exception($e->getMessage(), $e->getCode(), $e); throw new \Exception($exception->getMessage(), $exception->getCode(), $exception);
} }
} }
@ -509,13 +511,13 @@ class GameQ
'server_id' => $server_id, 'server_id' => $server_id,
'socket' => $socket, 'socket' => $socket,
]; ];
} catch (QueryException $e) { } catch (QueryException $exception) {
// Check to see if we are in debug, if so bubble up the exception // Check to see if we are in debug, if so bubble up the exception
if ($this->debug) { if ($this->debug) {
throw new \Exception($e->getMessage(), $e->getCode(), $e); throw new \Exception($exception->getMessage(), $exception->getCode(), $exception);
} }
break; continue;
} }
// Clean up the sockets, if any left over // Clean up the sockets, if any left over
@ -636,7 +638,7 @@ class GameQ
// Apply the filter to the data // Apply the filter to the data
$results = $filter->apply($results, $server); $results = $filter->apply($results, $server);
} catch (\ReflectionException $e) { } catch (\ReflectionException $exception) {
// Invalid, skip it // Invalid, skip it
continue; continue;
} }

View File

@ -19,24 +19,25 @@
namespace GameQ\Protocols; namespace GameQ\Protocols;
/** /**
* Class Aoc * Class Arma
* *
* @package GameQ\Protocols * @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com> *
* @author Wilson Jesus <>
*/ */
class Aoc extends Source class Arma extends Gamespy2
{ {
/** /**
* String name of this protocol class * String name of this protocol class
* *
* @type string * @type string
*/ */
protected $name = 'aoc'; protected $name = 'arma';
/** /**
* Longer string name of this protocol class * Longer string name of this protocol class
* *
* @type string * @type string
*/ */
protected $name_long = "Age of Chivalry"; protected $name_long = "ArmA Armed Assault";
} }

View File

@ -1,4 +1,5 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
@ -18,35 +19,15 @@
namespace GameQ\Protocols; namespace GameQ\Protocols;
use GameQ\Buffer;
use GameQ\Result;
/** /**
* Class Armed Assault 3 * Class Dayzmod
*
* Rules protocol reference: https://community.bistudio.com/wiki/Arma_3_ServerBrowserProtocol2
* *
* @package GameQ\Protocols * @package GameQ\Protocols
* @author Marcel Bößendörfer <m.boessendoerfer@marbis.net>
* @author Austin Bischoff <austin@codebeard.com> * @author Austin Bischoff <austin@codebeard.com>
* @author Memphis017 <https://github.com/Memphis017>
*/ */
class Arma3 extends Source class Arma3 extends Armedassault2oa
{ {
/**
* Defines the names for the specific game DLCs
*
* @var array
*/
protected $dlcNames = [
'Karts',
'Marksmen',
'Helicopters',
'Apex',
'Jets',
'Laws of War',
'Tac-Ops',
'Tanks',
];
/** /**
* String name of this protocol class * String name of this protocol class
@ -61,127 +42,4 @@ class Arma3 extends Source
* @type string * @type string
*/ */
protected $name_long = "Arma3"; 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 = $responseBuffer->readInt8(); // Grab DLC bit and use it later
$responseBuffer->skip(); // Reserved, burn it
// 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());
/*
* Due to a bug in the DLC byte response from the ARMA servers we end here until the DLC byte bug can be fixed
* or can code around the issue.
* See https://github.com/Austinb/GameQ/issues/420
*/
unset($dlcBit);
return $result->fetch();
//$dlcBit = 255;
/*// Loop over the DLC bit so we can pull in the infor for the DLC (if enabled)
for ($x = 0; $x < 8; $x++) {
if (($dlcBit >> $x) & 1) {
$result->addSub('dlcs', 'name', $this->dlcNames[$x]);
$result->addSub('dlcs', 'hash', dechex($responseBuffer->readInt32()));
}
}
// No longer needed
unset($dlcBit);
// 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);
// Burn the signatures count, we will just loop until we run out of strings
$responseBuffer->read();
// Make signatures array
$signatures = [];
// Loop until we run out of strings
while ($responseBuffer->getLength()) {
//$result->addSub('signatures', 0, $responseBuffer->readPascalString(0, true));
$signatures[] = $responseBuffer->readPascalString(0, true);
}
// Add as a simple array
$result->add('signatures', $signatures);
// Add signatures count
$result->add('signature_count', count($signatures));
unset($responseBuffer, $signatures);
return $result->fetch();*/
}
} }

View File

@ -42,9 +42,9 @@ class Armedassault2oa extends Source
protected $name_long = "Armed Assault 2: Operation Arrowhead"; protected $name_long = "Armed Assault 2: Operation Arrowhead";
/** /**
* Query port = client_port - 1 * Query port = client_port + 1
* *
* @type int * @type int
*/ */
protected $port_diff = -1; protected $port_diff = 1;
} }

View File

@ -105,12 +105,21 @@ class Ase extends Protocol
*/ */
public function processResponse() public function processResponse()
{ {
// Create a new buffer // Create a new buffer
$buffer = new Buffer(implode('', $this->packets_response)); $buffer = new Buffer(implode('', $this->packets_response));
// Burn the header // Check for valid response
$buffer->skip(4); if ($buffer->getLength() < 4) {
throw new \GameQ\Exception\Protocol(sprintf('%s The response from the server was empty.', __METHOD__));
}
// Read the header
$header = $buffer->read(4);
// Verify header
if ($header !== 'EYE1') {
throw new \GameQ\Exception\Protocol(sprintf('%s The response header "%s" does not match expected "EYE1"', __METHOD__, $header));
}
// Create a new result // Create a new result
$result = new Result(); $result = new Result();

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,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;
}

View File

@ -19,24 +19,25 @@
namespace GameQ\Protocols; namespace GameQ\Protocols;
/** /**
* Class Fof * Call of Duty Protocol Class
* *
* @package GameQ\Protocols * @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com> *
* @author Wilson Jesus <>
*/ */
class Fof extends Source class Cod extends Quake3
{ {
/** /**
* String name of this protocol class * String name of this protocol class
* *
* @type string * @type string
*/ */
protected $name = 'fof'; protected $name = 'cod';
/** /**
* Longer string name of this protocol class * Longer string name of this protocol class
* *
* @type string * @type string
*/ */
protected $name_long = "Fistful of Frags"; protected $name_long = "Call of Duty";
} }

View File

@ -19,24 +19,25 @@
namespace GameQ\Protocols; namespace GameQ\Protocols;
/** /**
* Class Alienswarm * Call of Duty United Offensive Class
* *
* @package GameQ\Protocols * @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com> *
* @author Wilson Jesus <>
*/ */
class Alienswarm extends Source class Coduo extends Quake3
{ {
/** /**
* String name of this protocol class * String name of this protocol class
* *
* @type string * @type string
*/ */
protected $name = 'alienswarm'; protected $name = 'coduo';
/** /**
* Longer string name of this protocol class * Longer string name of this protocol class
* *
* @type string * @type string
*/ */
protected $name_long = "Alien Swarm"; 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

@ -19,25 +19,24 @@
namespace GameQ\Protocols; namespace GameQ\Protocols;
/** /**
* Class Nmrih * Class Contagion
* *
* @package GameQ\Protocols * @package GameQ\Protocols
* @author Nikolay Ipanyuk <rostov114@gmail.com>
* @author Austin Bischoff <austin@codebeard.com> * @author Austin Bischoff <austin@codebeard.com>
*/ */
class Nmrih extends Source class Contagion extends Source
{ {
/** /**
* String name of this protocol class * String name of this protocol class
* *
* @type string * @type string
*/ */
protected $name = 'nmrih'; protected $name = 'contagion';
/** /**
* Longer string name of this protocol class * Longer string name of this protocol class
* *
* @type string * @type string
*/ */
protected $name_long = "No More Room in Hell"; protected $name_long = "Contagion";
} }

View File

@ -19,24 +19,25 @@
namespace GameQ\Protocols; namespace GameQ\Protocols;
/** /**
* Class Zps * Class Crysis
* *
* @package GameQ\Protocols * @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com> *
* @author Wilson Jesus <>
*/ */
class Zps extends Source class Crysis extends Gamespy3
{ {
/** /**
* String name of this protocol class * String name of this protocol class
* *
* @type string * @type string
*/ */
protected $name = 'zps'; protected $name = 'crysis';
/** /**
* Longer string name of this protocol class * Longer string name of this protocol class
* *
* @type string * @type string
*/ */
protected $name_long = "Zombie Panic Source"; 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,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));
}
}

View File

@ -19,12 +19,12 @@
namespace GameQ\Protocols; namespace GameQ\Protocols;
/** /**
* Class Jc2 * Class Dark and Light
* *
* @package GameQ\Protocols * @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com> * @author Austin Bischoff <austin@codebeard.com>
*/ */
class Jc2 extends Source class Dal extends Arkse
{ {
/** /**
@ -32,12 +32,12 @@ class Jc2 extends Source
* *
* @type string * @type string
*/ */
protected $name = 'jc2'; protected $name = 'dal';
/** /**
* Longer string name of this protocol class * Longer string name of this protocol class
* *
* @type string * @type string
*/ */
protected $name_long = "Just Cause 2 Multiplayer"; protected $name_long = "Dark and Light";
} }

View File

@ -1,4 +1,5 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *

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();
}
}

View File

@ -25,7 +25,7 @@ namespace GameQ\Protocols;
* @author Austin Bischoff <austin@codebeard.com> * @author Austin Bischoff <austin@codebeard.com>
* @author TacTicToe66 <https://github.com/TacTicToe66> * @author TacTicToe66 <https://github.com/TacTicToe66>
*/ */
class EgS extends Source class Egs extends Source
{ {
/** /**

View File

@ -19,24 +19,25 @@
namespace GameQ\Protocols; namespace GameQ\Protocols;
/** /**
* Class Aapg * Wolfenstein Enemy Territory Protocol Class
* *
* @package GameQ\Protocols * @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com> *
* @author Wilson Jesus <>
*/ */
class Aapg extends Aa3 class Et extends Quake3
{ {
/** /**
* String name of this protocol class * String name of this protocol class
* *
* @type string * @type string
*/ */
protected $name = 'aapg'; protected $name = 'et';
/** /**
* Longer string name of this protocol class * Longer string name of this protocol class
* *
* @type string * @type string
*/ */
protected $name_long = "America's Army: Proving Grounds"; protected $name_long = "Wolfenstein Enemy Territory";
} }

View File

@ -140,28 +140,31 @@ class Gamespy extends Protocol
$itemCount = count($data); $itemCount = count($data);
// Now lets loop the array // Check to make sure we have more than 1 item in the array before trying to loop
for ($x = 0; $x < $itemCount; $x += 2) { if (count($data) > 1) {
// Set some local vars // Now lets loop the array since we have items
$key = $data[$x]; for ($x = 0; $x < $itemCount; $x += 2) {
$val = $data[$x + 1]; // Set some local vars
$key = $data[$x];
$val = $data[$x + 1];
// Check for <variable>_<count> variable (i.e players) // Check for <variable>_<count> variable (i.e players)
if (($suffix = strrpos($key, '_')) !== false && is_numeric(substr($key, $suffix + 1))) { if (($suffix = strrpos($key, '_')) !== false && is_numeric(substr($key, $suffix + 1))) {
// See if this is a team designation // See if this is a team designation
if (substr($key, 0, $suffix) == 'teamname') { if (substr($key, 0, $suffix) == 'teamname') {
$result->addTeam('teamname', $val); $result->addTeam('teamname', $val);
$numTeams++; $numTeams++;
} else { } else {
// Its a player // Its a player
if (substr($key, 0, $suffix) == 'playername') { if (substr($key, 0, $suffix) == 'playername') {
$numPlayers++; $numPlayers++;
}
$result->addPlayer(substr($key, 0, $suffix), utf8_encode($val));
} }
$result->addPlayer(substr($key, 0, $suffix), utf8_encode($val)); } else {
// Regular variable so just add the value.
$result->add($key, $val);
} }
} else {
// Regular variable so just add the value.
$result->add($key, $val);
} }
} }

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

@ -277,10 +277,15 @@ class Gamespy3 extends Protocol
// By default item_group is blank, this will be set for each loop thru the data // By default item_group is blank, this will be set for each loop thru the data
$item_group = ''; $item_group = '';
// By default the item_type is blank, this will be set on each loop // By default the item_type is blank, this will be set on each loop
$item_type = ''; $item_type = '';
// Save count as variable
$count = count($data);
// Loop through all of the $data for information and pull it out into the result // Loop through all of the $data for information and pull it out into the result
for ($x = 0; $x < count($data) - 1; $x++) { for ($x = 0; $x < $count - 1; $x++) {
// Pull out the item // Pull out the item
$item = $data[$x]; $item = $data[$x];
// If this is an empty item, move on // If this is an empty item, move on
@ -330,6 +335,6 @@ class Gamespy3 extends Protocol
} }
} }
// Free up some memory // Free up some memory
unset($data, $item, $item_group, $item_type, $val); unset($count, $data, $item, $item_group, $item_type, $val);
} }
} }

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();
}
}

View File

@ -19,25 +19,24 @@
namespace GameQ\Protocols; namespace GameQ\Protocols;
/** /**
* Class Projectcars * Class Hurtworld
* *
* @package GameQ\Protocols * @package GameQ\Protocols
* @author Nikolay Ipanyuk <rostov114@gmail.com>
* @author Austin Bischoff <austin@codebeard.com> * @author Austin Bischoff <austin@codebeard.com>
*/ */
class Projectcars extends Source class Hurtworld extends Source
{ {
/** /**
* String name of this protocol class * String name of this protocol class
* *
* @type string * @type string
*/ */
protected $name = 'projectcars'; protected $name = 'hurtworld';
/** /**
* Longer string name of this protocol class * Longer string name of this protocol class
* *
* @type string * @type string
*/ */
protected $name_long = "Project Cars"; protected $name_long = "Hurtworld";
} }

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

@ -35,7 +35,7 @@ class Killingfloor extends Unreal2
* *
* @type string * @type string
*/ */
protected $name = 'killing floor'; protected $name = 'killingfloor';
/** /**
* Longer string name of this protocol class * Longer string name of this protocol class

View File

@ -32,7 +32,7 @@ class Killingfloor2 extends Source
* *
* @type string * @type string
*/ */
protected $name = 'killing floor 2'; protected $name = 'killingfloor2';
/** /**
* Longer string name of this protocol class * Longer string name of this protocol class

View File

@ -74,6 +74,6 @@ class Mohaa extends Gamespy
*/ */
public function findQueryPort($clientPort) public function findQueryPort($clientPort)
{ {
return $clientPort+97; return $clientPort + 97;
} }
} }

View File

@ -19,12 +19,12 @@
namespace GameQ\Protocols; namespace GameQ\Protocols;
/** /**
* Class Aa3 * Class MORDHAU
* *
* @package GameQ\Protocols * @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com> * @author Wilson Jesus <>
*/ */
class Aa3 extends Source class Mordhau extends Source
{ {
/** /**
@ -32,22 +32,22 @@ class Aa3 extends Source
* *
* @type string * @type string
*/ */
protected $name = 'aa3'; protected $name = 'mordhau';
/** /**
* Longer string name of this protocol class * Longer string name of this protocol class
* *
* @type string * @type string
*/ */
protected $name_long = "America's Army 3"; protected $name_long = "MORDHAU";
#protected $port = 7777;
/** /**
* Query port = client_port + 18243 * query_port = client_port + 19238
* * 27015 = 7777 + 19238
* client_port default 8777
* query_port default 27020
* *
* @type int * @type int
*/ */
protected $port_diff = 18243; #protected $port_diff = 19238;
} }

View File

@ -19,12 +19,12 @@
namespace GameQ\Protocols; namespace GameQ\Protocols;
/** /**
* Class Tfc * Class PixARK
* *
* @package GameQ\Protocols * @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com> * @author Austin Bischoff <austin@codebeard.com>
*/ */
class Tfc extends Source class Pixark extends Arkse
{ {
/** /**
@ -32,12 +32,12 @@ class Tfc extends Source
* *
* @type string * @type string
*/ */
protected $name = 'tfc'; protected $name = 'pixark';
/** /**
* Longer string name of this protocol class * Longer string name of this protocol class
* *
* @type string * @type string
*/ */
protected $name_long = "Team Fortress Classic"; protected $name_long = "PixARK";
} }

View File

@ -162,6 +162,7 @@ class Quake3 extends Protocol
* @param Buffer $buffer * @param Buffer $buffer
* *
* @return array * @return array
* @throws Exception
*/ */
protected function processPlayers(Buffer $buffer) protected function processPlayers(Buffer $buffer)
{ {
@ -173,24 +174,34 @@ class Quake3 extends Protocol
// Loop until we are out of data // Loop until we are out of data
while ($buffer->getLength()) { while ($buffer->getLength()) {
// Make a new buffer with this block
$playerInfo = new Buffer($buffer->readString("\x0A"));
// Add player info // Add player info
$result->addPlayer('frags', $playerInfo->readString("\x20")); $result->addPlayer('frags', $buffer->readString("\x20"));
$result->addPlayer('ping', $playerInfo->readString("\x20")); $result->addPlayer('ping', $buffer->readString("\x20"));
// Skip first " // Look ahead to see if we have a name or team
$playerInfo->skip(1); $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 // Add player name, encoded
$result->addPlayer('name', utf8_encode(trim(($playerInfo->readString('"'))))); $result->addPlayer('name', utf8_encode(trim($buffer->readString('"'))));
// Burn ending delimiter
$buffer->read();
// Increment // Increment
$playerCount++; $playerCount++;
// Clear
unset($playerInfo);
} }
$result->add('clients', $playerCount); $result->add('clients', $playerCount);

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";
}

View File

@ -86,6 +86,13 @@ class Samp extends Protocol
*/ */
protected $server_code = null; protected $server_code = null;
/**
* The client join link
*
* @type string
*/
protected $join_link = "samp://%s:%d/";
/** /**
* Normalize settings for this protocol * Normalize settings for this protocol
* *
@ -120,7 +127,7 @@ class Samp extends Protocol
// Build the server code // Build the server code
$this->server_code = implode('', array_map('chr', explode('.', $server->ip()))) . $this->server_code = implode('', array_map('chr', explode('.', $server->ip()))) .
pack("S", $server->portClient()); pack("S", $server->portClient());
// Loop over the packets and update them // Loop over the packets and update them
foreach ($this->packets as $packetType => $packet) { foreach ($this->packets as $packetType => $packet) {
@ -139,7 +146,7 @@ class Samp extends Protocol
{ {
// Results that will be returned // Results that will be returned
$results = [ ]; $results = [];
// Get the length of the server code so we can figure out how much to read later // Get the length of the server code so we can figure out how much to read later
$serverCodeLength = strlen($this->server_code); $serverCodeLength = strlen($this->server_code);
@ -170,7 +177,7 @@ class Samp extends Protocol
// Now we need to call the proper method // Now we need to call the proper method
$results = array_merge( $results = array_merge(
$results, $results,
call_user_func_array([ $this, $this->responses[$response_type] ], [ $buffer ]) call_user_func_array([$this, $this->responses[$response_type]], [$buffer])
); );
unset($buffer); unset($buffer);
@ -206,7 +213,7 @@ class Samp extends Protocol
$result->add('max_players', $buffer->readInt16()); $result->add('max_players', $buffer->readInt16());
// These are read differently for these last 3 // These are read differently for these last 3
$result->add('servername', $buffer->read($buffer->readInt32())); $result->add('servername', utf8_encode($buffer->read($buffer->readInt32())));
$result->add('gametype', $buffer->read($buffer->readInt32())); $result->add('gametype', $buffer->read($buffer->readInt32()));
$result->add('language', $buffer->read($buffer->readInt32())); $result->add('language', $buffer->read($buffer->readInt32()));
@ -234,7 +241,7 @@ class Samp extends Protocol
// Run until we run out of buffer // Run until we run out of buffer
while ($buffer->getLength()) { while ($buffer->getLength()) {
$result->addPlayer('id', $buffer->readInt8()); $result->addPlayer('id', $buffer->readInt8());
$result->addPlayer('name', $buffer->readPascalString()); $result->addPlayer('name', utf8_encode($buffer->readPascalString()));
$result->addPlayer('score', $buffer->readInt32()); $result->addPlayer('score', $buffer->readInt32());
$result->addPlayer('ping', $buffer->readInt32()); $result->addPlayer('ping', $buffer->readInt32());
} }

View File

@ -0,0 +1,75 @@
<?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;
/**
* Serious Sam Protocol Class
*
* @author ZCaliptium <zcaliptium@gmail.com>
*/
class Serioussam extends Gamespy
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'serioussam';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Serious Sam";
/**
* query_port = client_port + 1
*
* @type int
*/
protected $port_diff = 1;
/**
* Normalize settings for this protocol
*
* @type array
*/
protected $normalize = [
// General
'general' => [
// target => source
'dedicated' => 'dedicated',
'gametype' => 'gametype',
'hostname' => 'hostname',
'mapname' => 'mapname',
'maxplayers' => 'maxplayers',
'mod' => 'activemod',
'numplayers' => 'numplayers',
'password' => 'password',
],
// Individual
'player' => [
'name' => 'player',
'ping' => 'ping',
'score' => 'frags',
],
];
}

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;
/**
* Soldier of Fortune 2 Class
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Sof2 extends Quake3
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'sof2';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Solder of Fortune II";
/**
* The client join link
*
* @type string
*/
protected $join_link = "sof2mp://%s:%d/";
}

View File

@ -50,7 +50,7 @@ class Source extends Protocol
*/ */
protected $packets = [ protected $packets = [
self::PACKET_CHALLENGE => "\xFF\xFF\xFF\xFF\x56\x00\x00\x00\x00", self::PACKET_CHALLENGE => "\xFF\xFF\xFF\xFF\x56\x00\x00\x00\x00",
self::PACKET_DETAILS => "\xFF\xFF\xFF\xFFTSource Engine Query\x00", self::PACKET_DETAILS => "\xFF\xFF\xFF\xFFTSource Engine Query\x00%s",
self::PACKET_PLAYERS => "\xFF\xFF\xFF\xFF\x55%s", self::PACKET_PLAYERS => "\xFF\xFF\xFF\xFF\x55%s",
self::PACKET_RULES => "\xFF\xFF\xFF\xFF\x56%s", self::PACKET_RULES => "\xFF\xFF\xFF\xFF\x56%s",
]; ];

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 Swat4
*
* @package GameQ\Protocols
*
* @author Wilson Jesus <>
*/
class Swat4 extends Gamespy2
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'swat4';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "SWAT 4";
/**
* query_port = client_port + 1
*
* @type int
*/
protected $port_diff = 1;
}

View File

@ -1,4 +1,5 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
@ -122,7 +123,8 @@ class Teamspeak3 extends Protocol
{ {
// Check to make sure we have a query_port because it is required // Check to make sure we have a query_port because it is required
if (!isset($this->options[Server::SERVER_OPTIONS_QUERY_PORT]) if (
!isset($this->options[Server::SERVER_OPTIONS_QUERY_PORT])
|| empty($this->options[Server::SERVER_OPTIONS_QUERY_PORT]) || empty($this->options[Server::SERVER_OPTIONS_QUERY_PORT])
) { ) {
throw new Exception(__METHOD__ . " Missing required setting '" . Server::SERVER_OPTIONS_QUERY_PORT . "'."); throw new Exception(__METHOD__ . " Missing required setting '" . Server::SERVER_OPTIONS_QUERY_PORT . "'.");

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;
/**
* Urban Terror Class
*
* @package GameQ\Protocols
* @author Austin Bischoff <austin@codebeard.com>
*/
class Urbanterror extends Quake3
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'urbanterror';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Urban Terror";
/**
* The client join link
*
* @type string
*/
protected $join_link = "urt://%s:%d/";
}

View File

@ -0,0 +1,41 @@
<?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 Valheim
*
* @package GameQ\Protocols
*
*/
class Valheim extends Source
{
/**
* String name of this protocol class
*
* @type string
*/
protected $name = 'valheim';
/**
* Longer string name of this protocol class
*
* @type string
*/
protected $name_long = "Valheim";
}

View File

@ -778,11 +778,11 @@ class Ventrilo extends Protocol
$characterCount = count($chars); $characterCount = count($chars);
$key = 0; $key = 0;
for ($i = 1; $i <= $characterCount; $i++) { for ($index = 1; $index <= $characterCount; $index++) {
$chars[$i] -= ($table[$a2] + (($i - 1) % 5)) & 0xFF; $chars[$index] -= ($table[$a2] + (($index - 1) % 5)) & 0xFF;
$a2 = ($a2 + $a1) & 0xFF; $a2 = ($a2 + $a1) & 0xFF;
if (($i % 2) == 0) { if (($index % 2) == 0) {
$short_array = unpack("n1", pack("C2", $chars[$i - 1], $chars[$i])); $short_array = unpack("n1", pack("C2", $chars[$index - 1], $chars[$index]));
$header_items[$key] = $short_array[1]; $header_items[$key] = $short_array[1];
++$key; ++$key;
} }
@ -818,10 +818,10 @@ class Ventrilo extends Protocol
$data = ""; $data = "";
$characterCount = count($chars); $characterCount = count($chars);
for ($i = 1; $i <= $characterCount; $i++) { for ($index = 1; $index <= $characterCount; $index++) {
$chars[$i] -= ($table[$a2] + (($i - 1) % 72)) & 0xFF; $chars[$index] -= ($table[$a2] + (($index - 1) % 72)) & 0xFF;
$a2 = ($a2 + $a1) & 0xFF; $a2 = ($a2 + $a1) & 0xFF;
$data .= chr($chars[$i]); $data .= chr($chars[$index]);
} }
//@todo: Check CRC ??? //@todo: Check CRC ???
$decrypted[$header_items['pck']] = $data; $decrypted[$header_items['pck']] = $data;

View File

@ -112,6 +112,12 @@ class Native extends Core
// Set blocking mode // Set blocking mode
stream_set_blocking($this->socket, $this->blocking); stream_set_blocking($this->socket, $this->blocking);
// Set the read buffer
stream_set_read_buffer($this->socket, 0);
// Set the write buffer
stream_set_write_buffer($this->socket, 0);
} else { } else {
// Reset socket // Reset socket
$this->socket = null; $this->socket = null;
@ -193,7 +199,7 @@ class Native extends Core
/* @var $socket resource */ /* @var $socket resource */
// See if we have a response // See if we have a response
if (($response = fread($socket, 8192)) === false) { if (($response = fread($socket, 32768)) === false) {
continue; // No response yet so lets continue. continue; // No response yet so lets continue.
} }

View File

@ -37,8 +37,8 @@ class Result
/** /**
* Adds variable to results * Adds variable to results
* *
* @param string $name Variable name * @param string $name Variable name
* @param string $value Variable value * @param string|array $value Variable value
*/ */
public function add($name, $value) public function add($name, $value)
{ {
@ -49,8 +49,8 @@ class Result
/** /**
* Adds player variable to output * Adds player variable to output
* *
* @param string $name Variable name * @param string $name Variable name
* @param string $value Variable value * @param string $value Variable value
*/ */
public function addPlayer($name, $value) public function addPlayer($name, $value)
{ {
@ -61,8 +61,8 @@ class Result
/** /**
* Adds player variable to output * Adds player variable to output
* *
* @param string $name Variable name * @param string $name Variable name
* @param string $value Variable value * @param string $value Variable value
*/ */
public function addTeam($name, $value) public function addTeam($name, $value)
{ {
@ -87,7 +87,8 @@ class Result
// Find the first entry that doesn't have this variable // Find the first entry that doesn't have this variable
$found = false; $found = false;
for ($i = 0; $i != count($this->result[$sub]); $i++) { $count = count($this->result[$sub]);
for ($i = 0; $i != $count; $i++) {
if (!isset($this->result[$sub][$i][$key])) { if (!isset($this->result[$sub][$i][$key])) {
$this->result[$sub][$i][$key] = $value; $this->result[$sub][$i][$key] = $value;
$found = true; $found = true;
@ -99,6 +100,8 @@ class Result
if (!$found) { if (!$found) {
$this->result[$sub][][$key] = $value; $this->result[$sub][][$key] = $value;
} }
unset($count);
} }
/** /**
@ -115,7 +118,7 @@ class Result
/** /**
* Return a single variable * Return a single variable
* *
* @param string $var The variable name * @param string $var The variable name
* *
* @return mixed The variable value * @return mixed The variable value
*/ */