diff --git a/third_party/gameq/GameQ/Buffer.php b/third_party/gameq/GameQ/Buffer.php index e9e1552d..ca23eb6d 100644 --- a/third_party/gameq/GameQ/Buffer.php +++ b/third_party/gameq/GameQ/Buffer.php @@ -276,8 +276,8 @@ class Buffer // Get position of delimiters $pos = []; foreach ($delims as $delim) { - if ($p = strpos($this->data, $delim, min($this->index, $this->length))) { - $pos[] = $p; + if ($index = strpos($this->data, $delim, min($this->index, $this->length))) { + $pos[] = $index; } } diff --git a/third_party/gameq/GameQ/Filters/Secondstohuman.php b/third_party/gameq/GameQ/Filters/Secondstohuman.php index 5de72693..1b413f74 100644 --- a/third_party/gameq/GameQ/Filters/Secondstohuman.php +++ b/third_party/gameq/GameQ/Filters/Secondstohuman.php @@ -104,6 +104,8 @@ class Secondstohuman extends Base // 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", diff --git a/third_party/gameq/GameQ/Filters/Stripcolors.php b/third_party/gameq/GameQ/Filters/Stripcolors.php index 635061c8..58953042 100644 --- a/third_party/gameq/GameQ/Filters/Stripcolors.php +++ b/third_party/gameq/GameQ/Filters/Stripcolors.php @@ -33,6 +33,8 @@ class Stripcolors extends Base /** * Apply this filter * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * * @param array $result * @param \GameQ\Server $server * @@ -62,6 +64,9 @@ class Stripcolors extends Base case 'gamespy2': array_walk_recursive($result, [$this, 'stripUnreal']); break; + case 'source': + array_walk_recursive($result, [$this, 'stripSource']); + break; } /*$data['filtered'][ $server->id() ] = $result; @@ -97,4 +102,14 @@ class Stripcolors extends Base { $string = preg_replace('/\x1b.../', '', $string); } + + /** + * Strip color codes from Source based games + * + * @param string $string + */ + protected function stripSource(&$string) + { + $string = strip_tags($string); + } } diff --git a/third_party/gameq/GameQ/GameQ.php b/third_party/gameq/GameQ/GameQ.php index b0a8b60e..fdc8ef70 100644 --- a/third_party/gameq/GameQ/GameQ.php +++ b/third_party/gameq/GameQ/GameQ.php @@ -15,6 +15,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ + namespace GameQ; use GameQ\Exception\Protocol as ProtocolException; @@ -43,6 +44,7 @@ class GameQ /* * Constants */ + const PROTOCOLS_DIRECTORY = __DIR__ . '/Protocols'; /* Static Section */ @@ -408,10 +410,10 @@ class GameQ 'server_id' => $server_id, 'socket' => $socket, ]; - } catch (QueryException $e) { + } catch (QueryException $exception) { // 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); + throw new \Exception($exception->getMessage(), $exception->getCode(), $exception); } } @@ -509,13 +511,13 @@ class GameQ 'server_id' => $server_id, 'socket' => $socket, ]; - } catch (QueryException $e) { + } catch (QueryException $exception) { // 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); + throw new \Exception($exception->getMessage(), $exception->getCode(), $exception); } - break; + continue; } // Clean up the sockets, if any left over @@ -636,7 +638,7 @@ class GameQ // Apply the filter to the data $results = $filter->apply($results, $server); - } catch (\ReflectionException $e) { + } catch (\ReflectionException $exception) { // Invalid, skip it continue; } diff --git a/third_party/gameq/GameQ/Protocols/Aoc.php b/third_party/gameq/GameQ/Protocols/Arma.php similarity index 85% rename from third_party/gameq/GameQ/Protocols/Aoc.php rename to third_party/gameq/GameQ/Protocols/Arma.php index 581c52c2..2653872f 100644 --- a/third_party/gameq/GameQ/Protocols/Aoc.php +++ b/third_party/gameq/GameQ/Protocols/Arma.php @@ -19,24 +19,25 @@ namespace GameQ\Protocols; /** - * Class Aoc + * Class Arma * * @package GameQ\Protocols - * @author Austin Bischoff + * + * @author Wilson Jesus <> */ -class Aoc extends Source +class Arma extends Gamespy2 { /** * String name of this protocol class * * @type string */ - protected $name = 'aoc'; + protected $name = 'arma'; /** * Longer string name of this protocol class * * @type string */ - protected $name_long = "Age of Chivalry"; + protected $name_long = "ArmA Armed Assault"; } diff --git a/third_party/gameq/GameQ/Protocols/Arma3.php b/third_party/gameq/GameQ/Protocols/Arma3.php index 99ff9544..51d6e5da 100644 --- a/third_party/gameq/GameQ/Protocols/Arma3.php +++ b/third_party/gameq/GameQ/Protocols/Arma3.php @@ -1,4 +1,5 @@ * @author Austin Bischoff - * @author 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 @@ -61,127 +42,4 @@ class Arma3 extends Source * @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 = $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();*/ - } } diff --git a/third_party/gameq/GameQ/Protocols/Armedassault2oa.php b/third_party/gameq/GameQ/Protocols/Armedassault2oa.php index 34fbc845..e527a38d 100644 --- a/third_party/gameq/GameQ/Protocols/Armedassault2oa.php +++ b/third_party/gameq/GameQ/Protocols/Armedassault2oa.php @@ -42,9 +42,9 @@ class Armedassault2oa extends Source protected $name_long = "Armed Assault 2: Operation Arrowhead"; /** - * Query port = client_port - 1 + * Query port = client_port + 1 * * @type int */ - protected $port_diff = -1; + protected $port_diff = 1; } diff --git a/third_party/gameq/GameQ/Protocols/Ase.php b/third_party/gameq/GameQ/Protocols/Ase.php index cb5c6a42..abc47818 100644 --- a/third_party/gameq/GameQ/Protocols/Ase.php +++ b/third_party/gameq/GameQ/Protocols/Ase.php @@ -105,12 +105,21 @@ class Ase extends Protocol */ public function processResponse() { - // Create a new buffer $buffer = new Buffer(implode('', $this->packets_response)); - // Burn the header - $buffer->skip(4); + // Check for valid response + 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 $result = new Result(); diff --git a/third_party/gameq/GameQ/Protocols/Atlas.php b/third_party/gameq/GameQ/Protocols/Atlas.php new file mode 100644 index 00000000..83406bae --- /dev/null +++ b/third_party/gameq/GameQ/Protocols/Atlas.php @@ -0,0 +1,55 @@ +. + */ + +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; +} diff --git a/third_party/gameq/GameQ/Protocols/Brink.php b/third_party/gameq/GameQ/Protocols/Brink.php new file mode 100644 index 00000000..20226525 --- /dev/null +++ b/third_party/gameq/GameQ/Protocols/Brink.php @@ -0,0 +1,50 @@ +. + */ + +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; +} diff --git a/third_party/gameq/GameQ/Protocols/Fof.php b/third_party/gameq/GameQ/Protocols/Cod.php similarity index 85% rename from third_party/gameq/GameQ/Protocols/Fof.php rename to third_party/gameq/GameQ/Protocols/Cod.php index 2875fbf8..2425ea67 100644 --- a/third_party/gameq/GameQ/Protocols/Fof.php +++ b/third_party/gameq/GameQ/Protocols/Cod.php @@ -19,24 +19,25 @@ namespace GameQ\Protocols; /** - * Class Fof + * Call of Duty Protocol Class * * @package GameQ\Protocols - * @author Austin Bischoff + * + * @author Wilson Jesus <> */ -class Fof extends Source +class Cod extends Quake3 { /** * String name of this protocol class * * @type string */ - protected $name = 'fof'; + protected $name = 'cod'; /** * Longer string name of this protocol class * * @type string */ - protected $name_long = "Fistful of Frags"; + protected $name_long = "Call of Duty"; } diff --git a/third_party/gameq/GameQ/Protocols/Alienswarm.php b/third_party/gameq/GameQ/Protocols/Coduo.php similarity index 83% rename from third_party/gameq/GameQ/Protocols/Alienswarm.php rename to third_party/gameq/GameQ/Protocols/Coduo.php index 204f957f..2dd9a182 100644 --- a/third_party/gameq/GameQ/Protocols/Alienswarm.php +++ b/third_party/gameq/GameQ/Protocols/Coduo.php @@ -19,24 +19,25 @@ namespace GameQ\Protocols; /** - * Class Alienswarm + * Call of Duty United Offensive Class * * @package GameQ\Protocols - * @author Austin Bischoff + * + * @author Wilson Jesus <> */ -class Alienswarm extends Source +class Coduo extends Quake3 { /** * String name of this protocol class * * @type string */ - protected $name = 'alienswarm'; + protected $name = 'coduo'; /** * Longer string name of this protocol class * * @type string */ - protected $name_long = "Alien Swarm"; + protected $name_long = "Call of Duty: United Offensive"; } diff --git a/third_party/gameq/GameQ/Protocols/Codwaw.php b/third_party/gameq/GameQ/Protocols/Codwaw.php new file mode 100644 index 00000000..f730678e --- /dev/null +++ b/third_party/gameq/GameQ/Protocols/Codwaw.php @@ -0,0 +1,43 @@ +. + */ + +namespace GameQ\Protocols; + +/** + * Call of Duty World at War Class + * + * @package GameQ\Protocols + * @author naXe + * @author Austin Bischoff + */ +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"; +} diff --git a/third_party/gameq/GameQ/Protocols/Nmrih.php b/third_party/gameq/GameQ/Protocols/Contagion.php similarity index 85% rename from third_party/gameq/GameQ/Protocols/Nmrih.php rename to third_party/gameq/GameQ/Protocols/Contagion.php index c8f01aeb..64d0b76e 100644 --- a/third_party/gameq/GameQ/Protocols/Nmrih.php +++ b/third_party/gameq/GameQ/Protocols/Contagion.php @@ -19,25 +19,24 @@ namespace GameQ\Protocols; /** - * Class Nmrih + * Class Contagion * * @package GameQ\Protocols + * @author Nikolay Ipanyuk * @author Austin Bischoff */ -class Nmrih extends Source +class Contagion extends Source { - /** * String name of this protocol class * * @type string */ - protected $name = 'nmrih'; - + protected $name = 'contagion'; /** * Longer string name of this protocol class * * @type string */ - protected $name_long = "No More Room in Hell"; + protected $name_long = "Contagion"; } diff --git a/third_party/gameq/GameQ/Protocols/Zps.php b/third_party/gameq/GameQ/Protocols/Crysis.php similarity index 85% rename from third_party/gameq/GameQ/Protocols/Zps.php rename to third_party/gameq/GameQ/Protocols/Crysis.php index 4f60b229..e09a673d 100644 --- a/third_party/gameq/GameQ/Protocols/Zps.php +++ b/third_party/gameq/GameQ/Protocols/Crysis.php @@ -19,24 +19,25 @@ namespace GameQ\Protocols; /** - * Class Zps + * Class Crysis * * @package GameQ\Protocols - * @author Austin Bischoff + * + * @author Wilson Jesus <> */ -class Zps extends Source +class Crysis extends Gamespy3 { /** * String name of this protocol class * * @type string */ - protected $name = 'zps'; + protected $name = 'crysis'; /** * Longer string name of this protocol class * * @type string */ - protected $name_long = "Zombie Panic Source"; + protected $name_long = "Crysis"; } diff --git a/third_party/gameq/GameQ/Protocols/Crysis2.php b/third_party/gameq/GameQ/Protocols/Crysis2.php new file mode 100644 index 00000000..75c6614a --- /dev/null +++ b/third_party/gameq/GameQ/Protocols/Crysis2.php @@ -0,0 +1,43 @@ +. + */ + +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"; +} diff --git a/third_party/gameq/GameQ/Protocols/Cs2d.php b/third_party/gameq/GameQ/Protocols/Cs2d.php new file mode 100644 index 00000000..0f238fdd --- /dev/null +++ b/third_party/gameq/GameQ/Protocols/Cs2d.php @@ -0,0 +1,263 @@ +. + */ + +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 + */ +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)); + } +} diff --git a/third_party/gameq/GameQ/Protocols/Jc2.php b/third_party/gameq/GameQ/Protocols/Dal.php similarity index 89% rename from third_party/gameq/GameQ/Protocols/Jc2.php rename to third_party/gameq/GameQ/Protocols/Dal.php index e179f3ad..6b05037d 100644 --- a/third_party/gameq/GameQ/Protocols/Jc2.php +++ b/third_party/gameq/GameQ/Protocols/Dal.php @@ -19,12 +19,12 @@ namespace GameQ\Protocols; /** - * Class Jc2 + * Class Dark and Light * * @package GameQ\Protocols * @author Austin Bischoff */ -class Jc2 extends Source +class Dal extends Arkse { /** @@ -32,12 +32,12 @@ class Jc2 extends Source * * @type string */ - protected $name = 'jc2'; + protected $name = 'dal'; /** * Longer string name of this protocol class * * @type string */ - protected $name_long = "Just Cause 2 Multiplayer"; + protected $name_long = "Dark and Light"; } diff --git a/third_party/gameq/GameQ/Protocols/Dayzmod.php b/third_party/gameq/GameQ/Protocols/Dayzmod.php index 2ce1076d..80b798e3 100644 --- a/third_party/gameq/GameQ/Protocols/Dayzmod.php +++ b/third_party/gameq/GameQ/Protocols/Dayzmod.php @@ -1,4 +1,5 @@ . + */ + +namespace GameQ\Protocols; + +use GameQ\Exception\Protocol as Exception; +use GameQ\Result; + +/** + * ECO Global Survival Protocol Class + * + * @author Austin Bischoff + */ +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(); + } +} diff --git a/third_party/gameq/GameQ/Protocols/Egs.php b/third_party/gameq/GameQ/Protocols/Egs.php index 4f82aa8c..aab79aea 100644 --- a/third_party/gameq/GameQ/Protocols/Egs.php +++ b/third_party/gameq/GameQ/Protocols/Egs.php @@ -25,7 +25,7 @@ namespace GameQ\Protocols; * @author Austin Bischoff * @author TacTicToe66 */ -class EgS extends Source +class Egs extends Source { /** diff --git a/third_party/gameq/GameQ/Protocols/Aapg.php b/third_party/gameq/GameQ/Protocols/Et.php similarity index 83% rename from third_party/gameq/GameQ/Protocols/Aapg.php rename to third_party/gameq/GameQ/Protocols/Et.php index a207d4fe..63b5beb7 100644 --- a/third_party/gameq/GameQ/Protocols/Aapg.php +++ b/third_party/gameq/GameQ/Protocols/Et.php @@ -19,24 +19,25 @@ namespace GameQ\Protocols; /** - * Class Aapg + * Wolfenstein Enemy Territory Protocol Class * * @package GameQ\Protocols - * @author Austin Bischoff + * + * @author Wilson Jesus <> */ -class Aapg extends Aa3 +class Et extends Quake3 { /** * String name of this protocol class * * @type string */ - protected $name = 'aapg'; + protected $name = 'et'; /** * Longer string name of this protocol class * * @type string */ - protected $name_long = "America's Army: Proving Grounds"; + protected $name_long = "Wolfenstein Enemy Territory"; } diff --git a/third_party/gameq/GameQ/Protocols/Gamespy.php b/third_party/gameq/GameQ/Protocols/Gamespy.php index 37a951e3..b1a1e4fa 100644 --- a/third_party/gameq/GameQ/Protocols/Gamespy.php +++ b/third_party/gameq/GameQ/Protocols/Gamespy.php @@ -140,28 +140,31 @@ class Gamespy extends Protocol $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]; + // 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 (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++; + // Check for _ 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)); } - $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); } } diff --git a/third_party/gameq/GameQ/Protocols/Gamespy2.php b/third_party/gameq/GameQ/Protocols/Gamespy2.php new file mode 100644 index 00000000..c7788d9e --- /dev/null +++ b/third_party/gameq/GameQ/Protocols/Gamespy2.php @@ -0,0 +1,269 @@ +. + */ + +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 + */ +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; + } +} diff --git a/third_party/gameq/GameQ/Protocols/Gamespy3.php b/third_party/gameq/GameQ/Protocols/Gamespy3.php index ab742060..2df0a4bd 100644 --- a/third_party/gameq/GameQ/Protocols/Gamespy3.php +++ b/third_party/gameq/GameQ/Protocols/Gamespy3.php @@ -277,10 +277,15 @@ class Gamespy3 extends Protocol // 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($data) - 1; $x++) { + for ($x = 0; $x < $count - 1; $x++) { // Pull out the item $item = $data[$x]; // If this is an empty item, move on @@ -330,6 +335,6 @@ class Gamespy3 extends Protocol } } // Free up some memory - unset($data, $item, $item_group, $item_type, $val); + unset($count, $data, $item, $item_group, $item_type, $val); } } diff --git a/third_party/gameq/GameQ/Protocols/Gtar.php b/third_party/gameq/GameQ/Protocols/Gtar.php new file mode 100644 index 00000000..2121e07c --- /dev/null +++ b/third_party/gameq/GameQ/Protocols/Gtar.php @@ -0,0 +1,164 @@ +. + */ +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 + * @author Austin Bischoff + */ +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(); + } +} diff --git a/third_party/gameq/GameQ/Protocols/Projectcars.php b/third_party/gameq/GameQ/Protocols/Hurtworld.php similarity index 85% rename from third_party/gameq/GameQ/Protocols/Projectcars.php rename to third_party/gameq/GameQ/Protocols/Hurtworld.php index fc9baaac..fa54654a 100644 --- a/third_party/gameq/GameQ/Protocols/Projectcars.php +++ b/third_party/gameq/GameQ/Protocols/Hurtworld.php @@ -19,25 +19,24 @@ namespace GameQ\Protocols; /** - * Class Projectcars + * Class Hurtworld * * @package GameQ\Protocols + * @author Nikolay Ipanyuk * @author Austin Bischoff */ -class Projectcars extends Source +class Hurtworld extends Source { - /** * String name of this protocol class * * @type string */ - protected $name = 'projectcars'; - + protected $name = 'hurtworld'; /** * Longer string name of this protocol class * * @type string */ - protected $name_long = "Project Cars"; + protected $name_long = "Hurtworld"; } diff --git a/third_party/gameq/GameQ/Protocols/Insurgencysand.php b/third_party/gameq/GameQ/Protocols/Insurgencysand.php new file mode 100644 index 00000000..407d6e26 --- /dev/null +++ b/third_party/gameq/GameQ/Protocols/Insurgencysand.php @@ -0,0 +1,49 @@ +. + */ + +namespace GameQ\Protocols; + +/** + * Insurgency Sandstorm Class + * + * @package GameQ\Protocols + * @author Austin Bischoff + */ +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; +} diff --git a/third_party/gameq/GameQ/Protocols/Killingfloor.php b/third_party/gameq/GameQ/Protocols/Killingfloor.php index 9cc19643..0947b48a 100644 --- a/third_party/gameq/GameQ/Protocols/Killingfloor.php +++ b/third_party/gameq/GameQ/Protocols/Killingfloor.php @@ -35,7 +35,7 @@ class Killingfloor extends Unreal2 * * @type string */ - protected $name = 'killing floor'; + protected $name = 'killingfloor'; /** * Longer string name of this protocol class diff --git a/third_party/gameq/GameQ/Protocols/Killingfloor2.php b/third_party/gameq/GameQ/Protocols/Killingfloor2.php index a134f258..fd5432bf 100644 --- a/third_party/gameq/GameQ/Protocols/Killingfloor2.php +++ b/third_party/gameq/GameQ/Protocols/Killingfloor2.php @@ -32,7 +32,7 @@ class Killingfloor2 extends Source * * @type string */ - protected $name = 'killing floor 2'; + protected $name = 'killingfloor2'; /** * Longer string name of this protocol class diff --git a/third_party/gameq/GameQ/Protocols/Mohaa.php b/third_party/gameq/GameQ/Protocols/Mohaa.php index d4ebb5ad..66ddd7e7 100644 --- a/third_party/gameq/GameQ/Protocols/Mohaa.php +++ b/third_party/gameq/GameQ/Protocols/Mohaa.php @@ -74,6 +74,6 @@ class Mohaa extends Gamespy */ public function findQueryPort($clientPort) { - return $clientPort+97; + return $clientPort + 97; } } diff --git a/third_party/gameq/GameQ/Protocols/Aa3.php b/third_party/gameq/GameQ/Protocols/Mordhau.php similarity index 76% rename from third_party/gameq/GameQ/Protocols/Aa3.php rename to third_party/gameq/GameQ/Protocols/Mordhau.php index 6ffd412a..fa305ce1 100644 --- a/third_party/gameq/GameQ/Protocols/Aa3.php +++ b/third_party/gameq/GameQ/Protocols/Mordhau.php @@ -19,12 +19,12 @@ namespace GameQ\Protocols; /** - * Class Aa3 + * Class MORDHAU * * @package GameQ\Protocols - * @author Austin Bischoff + * @author Wilson Jesus <> */ -class Aa3 extends Source +class Mordhau extends Source { /** @@ -32,22 +32,22 @@ class Aa3 extends Source * * @type string */ - protected $name = 'aa3'; + protected $name = 'mordhau'; /** * Longer string name of this protocol class * * @type string */ - protected $name_long = "America's Army 3"; + protected $name_long = "MORDHAU"; + + #protected $port = 7777; /** - * Query port = client_port + 18243 - * - * client_port default 8777 - * query_port default 27020 + * query_port = client_port + 19238 + * 27015 = 7777 + 19238 * * @type int */ - protected $port_diff = 18243; + #protected $port_diff = 19238; } diff --git a/third_party/gameq/GameQ/Protocols/Tfc.php b/third_party/gameq/GameQ/Protocols/Pixark.php similarity index 89% rename from third_party/gameq/GameQ/Protocols/Tfc.php rename to third_party/gameq/GameQ/Protocols/Pixark.php index aed24027..2e67af04 100644 --- a/third_party/gameq/GameQ/Protocols/Tfc.php +++ b/third_party/gameq/GameQ/Protocols/Pixark.php @@ -19,12 +19,12 @@ namespace GameQ\Protocols; /** - * Class Tfc + * Class PixARK * * @package GameQ\Protocols * @author Austin Bischoff */ -class Tfc extends Source +class Pixark extends Arkse { /** @@ -32,12 +32,12 @@ class Tfc extends Source * * @type string */ - protected $name = 'tfc'; + protected $name = 'pixark'; /** * Longer string name of this protocol class * * @type string */ - protected $name_long = "Team Fortress Classic"; + protected $name_long = "PixARK"; } diff --git a/third_party/gameq/GameQ/Protocols/Quake3.php b/third_party/gameq/GameQ/Protocols/Quake3.php index 3ed177bb..6269b927 100644 --- a/third_party/gameq/GameQ/Protocols/Quake3.php +++ b/third_party/gameq/GameQ/Protocols/Quake3.php @@ -162,6 +162,7 @@ class Quake3 extends Protocol * @param Buffer $buffer * * @return array + * @throws Exception */ protected function processPlayers(Buffer $buffer) { @@ -173,24 +174,34 @@ class Quake3 extends Protocol // 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")); + $result->addPlayer('frags', $buffer->readString("\x20")); + $result->addPlayer('ping', $buffer->readString("\x20")); - // Skip first " - $playerInfo->skip(1); + // 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(($playerInfo->readString('"'))))); + $result->addPlayer('name', utf8_encode(trim($buffer->readString('"')))); + + // Burn ending delimiter + $buffer->read(); // Increment $playerCount++; - - // Clear - unset($playerInfo); } $result->add('clients', $playerCount); diff --git a/third_party/gameq/GameQ/Protocols/Redorchestraostfront.php b/third_party/gameq/GameQ/Protocols/Redorchestraostfront.php new file mode 100644 index 00000000..4c83b7eb --- /dev/null +++ b/third_party/gameq/GameQ/Protocols/Redorchestraostfront.php @@ -0,0 +1,43 @@ +. + */ + +namespace GameQ\Protocols; + +/** + * Red Orchestra: Ostfront 41-45 Class + * + * @package GameQ\Protocols + * @author naXe + * @author Austin Bischoff + */ +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"; +} diff --git a/third_party/gameq/GameQ/Protocols/Samp.php b/third_party/gameq/GameQ/Protocols/Samp.php index e96358a7..cf01f834 100644 --- a/third_party/gameq/GameQ/Protocols/Samp.php +++ b/third_party/gameq/GameQ/Protocols/Samp.php @@ -86,6 +86,13 @@ class Samp extends Protocol */ protected $server_code = null; + /** + * The client join link + * + * @type string + */ + protected $join_link = "samp://%s:%d/"; + /** * Normalize settings for this protocol * @@ -120,7 +127,7 @@ class Samp extends Protocol // Build the server code $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 foreach ($this->packets as $packetType => $packet) { @@ -139,7 +146,7 @@ class Samp extends Protocol { // Results that will be returned - $results = [ ]; + $results = []; // Get the length of the server code so we can figure out how much to read later $serverCodeLength = strlen($this->server_code); @@ -170,7 +177,7 @@ class Samp extends Protocol // Now we need to call the proper method $results = array_merge( $results, - call_user_func_array([ $this, $this->responses[$response_type] ], [ $buffer ]) + call_user_func_array([$this, $this->responses[$response_type]], [$buffer]) ); unset($buffer); @@ -206,7 +213,7 @@ class Samp extends Protocol $result->add('max_players', $buffer->readInt16()); // 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('language', $buffer->read($buffer->readInt32())); @@ -234,7 +241,7 @@ class Samp extends Protocol // Run until we run out of buffer while ($buffer->getLength()) { $result->addPlayer('id', $buffer->readInt8()); - $result->addPlayer('name', $buffer->readPascalString()); + $result->addPlayer('name', utf8_encode($buffer->readPascalString())); $result->addPlayer('score', $buffer->readInt32()); $result->addPlayer('ping', $buffer->readInt32()); } diff --git a/third_party/gameq/GameQ/Protocols/Serioussam.php b/third_party/gameq/GameQ/Protocols/Serioussam.php new file mode 100644 index 00000000..64a03cf7 --- /dev/null +++ b/third_party/gameq/GameQ/Protocols/Serioussam.php @@ -0,0 +1,75 @@ +. + */ + +namespace GameQ\Protocols; + +/** + * Serious Sam Protocol Class + * + * @author ZCaliptium + */ +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', + ], + ]; +} diff --git a/third_party/gameq/GameQ/Protocols/Sof2.php b/third_party/gameq/GameQ/Protocols/Sof2.php new file mode 100644 index 00000000..96a4db25 --- /dev/null +++ b/third_party/gameq/GameQ/Protocols/Sof2.php @@ -0,0 +1,49 @@ +. + */ + +namespace GameQ\Protocols; + +/** + * Soldier of Fortune 2 Class + * + * @package GameQ\Protocols + * @author Austin Bischoff + */ +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/"; +} diff --git a/third_party/gameq/GameQ/Protocols/Source.php b/third_party/gameq/GameQ/Protocols/Source.php index 9e4cb5dd..dbf9212a 100644 --- a/third_party/gameq/GameQ/Protocols/Source.php +++ b/third_party/gameq/GameQ/Protocols/Source.php @@ -50,7 +50,7 @@ class Source extends Protocol */ protected $packets = [ 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_RULES => "\xFF\xFF\xFF\xFF\x56%s", ]; diff --git a/third_party/gameq/GameQ/Protocols/Swat4.php b/third_party/gameq/GameQ/Protocols/Swat4.php new file mode 100644 index 00000000..7b8e1200 --- /dev/null +++ b/third_party/gameq/GameQ/Protocols/Swat4.php @@ -0,0 +1,50 @@ +. + */ + +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; +} diff --git a/third_party/gameq/GameQ/Protocols/Teamspeak3.php b/third_party/gameq/GameQ/Protocols/Teamspeak3.php index c66f6a44..24c0d722 100644 --- a/third_party/gameq/GameQ/Protocols/Teamspeak3.php +++ b/third_party/gameq/GameQ/Protocols/Teamspeak3.php @@ -1,4 +1,5 @@ options[Server::SERVER_OPTIONS_QUERY_PORT]) + if ( + !isset($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 . "'."); diff --git a/third_party/gameq/GameQ/Protocols/Urbanterror.php b/third_party/gameq/GameQ/Protocols/Urbanterror.php new file mode 100644 index 00000000..682f91e6 --- /dev/null +++ b/third_party/gameq/GameQ/Protocols/Urbanterror.php @@ -0,0 +1,49 @@ +. + */ + +namespace GameQ\Protocols; + +/** + * Urban Terror Class + * + * @package GameQ\Protocols + * @author Austin Bischoff + */ +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/"; +} diff --git a/third_party/gameq/GameQ/Protocols/Valheim.php b/third_party/gameq/GameQ/Protocols/Valheim.php new file mode 100644 index 00000000..106a66e8 --- /dev/null +++ b/third_party/gameq/GameQ/Protocols/Valheim.php @@ -0,0 +1,41 @@ +. + */ + +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"; +} diff --git a/third_party/gameq/GameQ/Protocols/Ventrilo.php b/third_party/gameq/GameQ/Protocols/Ventrilo.php index 8a1e0e8e..6986bedc 100644 --- a/third_party/gameq/GameQ/Protocols/Ventrilo.php +++ b/third_party/gameq/GameQ/Protocols/Ventrilo.php @@ -778,11 +778,11 @@ class Ventrilo extends Protocol $characterCount = count($chars); $key = 0; - for ($i = 1; $i <= $characterCount; $i++) { - $chars[$i] -= ($table[$a2] + (($i - 1) % 5)) & 0xFF; + for ($index = 1; $index <= $characterCount; $index++) { + $chars[$index] -= ($table[$a2] + (($index - 1) % 5)) & 0xFF; $a2 = ($a2 + $a1) & 0xFF; - if (($i % 2) == 0) { - $short_array = unpack("n1", pack("C2", $chars[$i - 1], $chars[$i])); + if (($index % 2) == 0) { + $short_array = unpack("n1", pack("C2", $chars[$index - 1], $chars[$index])); $header_items[$key] = $short_array[1]; ++$key; } @@ -818,10 +818,10 @@ class Ventrilo extends Protocol $data = ""; $characterCount = count($chars); - for ($i = 1; $i <= $characterCount; $i++) { - $chars[$i] -= ($table[$a2] + (($i - 1) % 72)) & 0xFF; + for ($index = 1; $index <= $characterCount; $index++) { + $chars[$index] -= ($table[$a2] + (($index - 1) % 72)) & 0xFF; $a2 = ($a2 + $a1) & 0xFF; - $data .= chr($chars[$i]); + $data .= chr($chars[$index]); } //@todo: Check CRC ??? $decrypted[$header_items['pck']] = $data; diff --git a/third_party/gameq/GameQ/Query/Native.php b/third_party/gameq/GameQ/Query/Native.php index b078c9c4..24200b0c 100644 --- a/third_party/gameq/GameQ/Query/Native.php +++ b/third_party/gameq/GameQ/Query/Native.php @@ -112,6 +112,12 @@ class Native extends Core // Set blocking mode 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 { // Reset socket $this->socket = null; @@ -193,7 +199,7 @@ class Native extends Core /* @var $socket resource */ // 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. } diff --git a/third_party/gameq/GameQ/Result.php b/third_party/gameq/GameQ/Result.php index 1fe33d89..7023f17a 100644 --- a/third_party/gameq/GameQ/Result.php +++ b/third_party/gameq/GameQ/Result.php @@ -37,8 +37,8 @@ class Result /** * Adds variable to results * - * @param string $name Variable name - * @param string $value Variable value + * @param string $name Variable name + * @param string|array $value Variable value */ public function add($name, $value) { @@ -49,8 +49,8 @@ class Result /** * Adds player variable to output * - * @param string $name Variable name - * @param string $value Variable value + * @param string $name Variable name + * @param string $value Variable value */ public function addPlayer($name, $value) { @@ -61,8 +61,8 @@ class Result /** * Adds player variable to output * - * @param string $name Variable name - * @param string $value Variable value + * @param string $name Variable name + * @param string $value Variable value */ public function addTeam($name, $value) { @@ -87,7 +87,8 @@ class Result // Find the first entry that doesn't have this variable $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])) { $this->result[$sub][$i][$key] = $value; $found = true; @@ -99,6 +100,8 @@ class Result if (!$found) { $this->result[$sub][][$key] = $value; } + + unset($count); } /** @@ -115,7 +118,7 @@ class Result /** * Return a single variable * - * @param string $var The variable name + * @param string $var The variable name * * @return mixed The variable value */